//Button const smBtn = document.createElement('template') smBtn.innerHTML = `
`; customElements.define('sm-btn', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(smBtn.content.cloneNode(true)) } static get observedAttributes() { return ['disabled'] } get disabled() { return this.getAttribute('disabled') } set disabled(val) { this.setAttribute('disabled', val) } connectedCallback() { let disabledEvent = new CustomEvent('disabled', { bubbles: true, composed: true }) let clicked = new CustomEvent('clicked', { bubbles: true, composed: true }) this.shadowRoot.querySelector('button').textContent = this.getAttribute('value') this.addEventListener('click', (e) => { if (this.getAttribute('disabled') === 'true') { this.dispatchEvent(disabledEvent) } else this.dispatchEvent(clicked) }) } attributeChangedCallback(name, oldValue, newValue) { } }) //Input const smInput = document.createElement('template') smInput.innerHTML = ` `; customElements.define('sm-input', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(smInput.content.cloneNode(true)) } static get observedAttributes() { return ['placeholder'] } get value() { return this.shadowRoot.querySelector('input').value } set value(val) { this.shadowRoot.querySelector('input').value = val; } get placeholder() { return this.getAttribute('placeholder') } set placeholder(val) { this.setAttribute('placeholder', val) } get type() { return this.getAttribute('type') } get isValid() { return this.shadowRoot.querySelector('input').checkValidity() } preventNonNumericalInput(e) { let keyCode = e.keyCode; if (!((keyCode > 47 && keyCode < 56) || (keyCode > 36 && keyCode < 39) || (keyCode > 95 && keyCode < 104) || keyCode === 110 || (keyCode > 7 && keyCode < 19))) { e.preventDefault(); } } checkInput(input, label, inputParent, clear) { if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') return; if (input.value !== '') { if (this.animate) inputParent.classList.add('animate-label') else label.classList.add('hide') clear.classList.remove('hide') } else { if (this.animate) inputParent.classList.remove('animate-label') else label.classList.remove('hide') clear.classList.add('hide') } } connectedCallback() { let input = this.shadowRoot.querySelector('input'), inputParent = this.shadowRoot.querySelector('.input'), clearBtn = this.shadowRoot.querySelector('.clear'), label = this.shadowRoot.querySelector('.label') this.animate = this.hasAttribute('animate') this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') if (this.hasAttribute('value')) { input.value = this.getAttribute('value') this.checkInput(input, inputParent, clearBtn) } if (this.hasAttribute('type')) { if (this.getAttribute('type') === 'number') { input.setAttribute('inputmode', 'numeric') } else input.setAttribute('type', this.getAttribute('type')) } else input.setAttribute('type', 'text') input.addEventListener('keydown', e => { if (this.getAttribute('type') === 'number') this.preventNonNumericalInput(e); }) input.addEventListener('input', e => { this.checkInput(input, label, inputParent, clearBtn) }) clearBtn.addEventListener('click', e => { input.value = '' this.checkInput(input, label, inputParent, clearBtn) }) } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { if (name === 'placeholder') this.shadowRoot.querySelector('.label').textContent = newValue; } } }) // tab-header const smTabHeader = document.createElement('template') smTabHeader.innerHTML = `
Nothing to see here
`; customElements.define('sm-tab-header', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(smTabHeader.content.cloneNode(true)) this.indicator = this.shadowRoot.querySelector('.indicator'); } static get observedAttributes() { return ['type'] } connectedCallback() { this.prevTab = '' this.type = this.getAttribute('type') this.addEventListener('switchTab', e => { if (e.target === this.prevTab) return if (this.type === 'tab') { if (this.prevTab) this.prevTab.classList.remove('tab-active') setTimeout(() => { e.target.classList.add('tab-active') }, 200); } else { if (this.prevTab) this.prevTab.classList.remove('line-active') setTimeout(() => { e.target.classList.add('line-active') }, 200); } setTimeout(() => { this.indicator.classList.add('transition') }, 100); this.indicator.setAttribute('style', `width: ${e.detail.width}px; transform: translateX(${e.detail.left - 1}px)`) e.target.scrollIntoView({behavior: 'smooth', inline: 'center'}) this.prevTab = e.target; }) } }) // tab const smTab = document.createElement('template') smTab.innerHTML = `
`; customElements.define('sm-tab', class extends HTMLElement { constructor() { super() this.shadow = this.attachShadow({ mode: 'open' }).append(smTab.content.cloneNode(true)) } connectedCallback() { let width = 0, left = 0; if ('ResizeObserver' in window) { let resizeObserver = new ResizeObserver(entries => { entries.forEach(entry => { width = entry.contentRect.width; left = this.getBoundingClientRect().left - this.parentNode.offsetLeft }) }) resizeObserver.observe(this) } else { let observer = new IntersectionObserver((entries, observer) => { if (entries[0].isIntersecting) { width = entry.contentRect.width; left = this.getBoundingClientRect().left - this.parentNode.offsetLeft } }, { threshold: 1 }) observer.observe(this) } let switchTab = new CustomEvent('switchTab', { bubbles: true, composed: true, detail: { panel: this.getAttribute('panel'), } }) this.addEventListener('click', () => { switchTab.detail.width = width; switchTab.detail.left = left; this.dispatchEvent(switchTab) }) if (this.hasAttribute('active')) { setTimeout(() => { switchTab.detail.width = width; switchTab.detail.left = left; this.dispatchEvent(switchTab) }, 0); } } }) //chcekbox const smCheckbox = document.createElement('template') smCheckbox.innerHTML = ` ` customElements.define('sm-checkbox', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(smCheckbox.content.cloneNode(true)) } static get observedAttributes() { return ['disabled'] } get disabled() { return this.getAttribute('disabled') } set disabled(val) { this.setAttribute('disabled', val) } connectedCallback() { this.checkbox = this.shadowRoot.querySelector('.checkbox'); if (this.hasAttribute('disabled')) { this.checkbox.classList.add('disabled') } else { this.checkbox.classList.remove('disabled') } } attributeChangedCallback(name, oldValue, newValue) { this.checkbox = this.shadowRoot.querySelector('.checkbox'); if (oldValue !== newValue) { if (name === 'disabled') { if (newValue === 'true') { this.checkbox.classList.add('disabled') } else { this.checkbox.classList.remove('disabled') } } } } }) //sm-audio const smAudio = document.createElement('template') smAudio.innerHTML = `
play pause
/
`; customElements.define('sm-audio', class extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }).append(smAudio.content.cloneNode(true)) this.playing = false; } static get observedAttributes() { return ['src'] } play() { this.audio.play() this.playing = false; this.pauseBtn.classList.remove('hide') this.playBtn.classList.add('hide') } pause() { this.audio.pause() this.playing = true; this.pauseBtn.classList.add('hide') this.playBtn.classList.remove('hide') } get isPlaying() { return this.playing; } connectedCallback() { this.playBtn = this.shadowRoot.querySelector('.play'); this.pauseBtn = this.shadowRoot.querySelector('.pause'); this.audio = this.shadowRoot.querySelector('audio') this.playBtn.addEventListener('click', e => { this.play() }) this.pauseBtn.addEventListener('click', e => { this.pause() }) this.audio.addEventListener('ended', e => { this.pause() }) let width; if ('ResizeObserver' in window) { let resizeObserver = new ResizeObserver(entries => { entries.forEach(entry => { width = entry.contentRect.width; }) }) resizeObserver.observe(this) } else { let observer = new IntersectionObserver((entries, observer) => { if (entries[0].isIntersecting) width = this.shadowRoot.querySelector('.audio').offsetWidth; }, { threshold: 1 }) observer.observe(this) } this.audio.addEventListener('timeupdate', e => { let time = this.audio.currentTime, minutes = Math.floor(time / 60), seconds = Math.floor(time - minutes * 60), y = seconds < 10 ? "0" + seconds : seconds; this.shadowRoot.querySelector('.current-time').textContent = `${minutes}:${y}` this.shadowRoot.querySelector('.track').style.width = (width / this.audio.duration) * this.audio.currentTime + 'px' }) } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { if (name === 'src') { if (this.hasAttribute('src') && newValue.trim() !== '') { this.shadowRoot.querySelector('audio').src = newValue; this.shadowRoot.querySelector('audio').onloadedmetadata = () => { let duration = this.audio.duration, minutes = Math.floor(duration / 60), seconds = Math.floor(duration - minutes * 60), y = seconds < 10 ? "0" + seconds : seconds; this.shadowRoot.querySelector('.duration').textContent = `${minutes}:${y}`; } } else this.classList.add('disabled') } } } }) //sm-switch const smSwitch = document.createElement('template') smSwitch.innerHTML = ` ` customElements.define('sm-switch', class extends HTMLElement{ constructor() { super() this.attachShadow({mode: 'open'}).append(smSwitch.content.cloneNode(true)) } connectedCallback() { } })