diff --git a/components.js b/components.js index 378a94c..be67247 100644 --- a/components.js +++ b/components.js @@ -2,91 +2,112 @@ const smButton = document.createElement('template') smButton.innerHTML = `
@@ -95,7 +116,9 @@ customElements.define('sm-button', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smButton.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smButton.content.cloneNode(true)) } get disabled() { @@ -107,8 +130,7 @@ customElements.define('sm-button', this.isDisabled = true this.setAttribute('disabled', '') this.button.removeAttribute('tabindex') - } - else if (!value && this.isDisabled) { + } else if (!value && this.isDisabled) { this.isDisabled = false this.removeAttribute('disabled') } @@ -120,8 +142,7 @@ customElements.define('sm-button', bubbles: true, composed: true })) - } - else { + } else { this.dispatchEvent(new CustomEvent('clicked', { bubbles: true, composed: true @@ -151,7 +172,8 @@ smInput.innerHTML = ` *{ padding: 0; margin: 0; - box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button, @@ -167,14 +189,21 @@ input[type=number]::-webkit-outer-spin-button { appearance: none; margin: 0; } +input::-ms-reveal, +input::-ms-clear { + display: none; +} input:invalid{ outline: none; - box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; } ::-moz-focus-inner{ border: none; } :host{ + display: -webkit-box; + display: -ms-flexbox; display: flex; } .hide{ @@ -202,41 +231,70 @@ border: none; border-radius: 10rem; } .input { + display: -webkit-box; + display: -ms-flexbox; display: flex; - align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-align: left; position: relative; gap: 1em; padding: 0.7em 1em; border-radius: 0.3em; + -webkit-transition: opacity 0.3s; + -o-transition: opacity 0.3s; transition: opacity 0.3s; - background: rgba(var(--text-color), 0.1); + background: rgba(var(--text-color), 0.06); + -webkit-box-shadow: 0 0 0.2rem rgba(var(--text-color), 0.2) inset; + box-shadow: 0 0 0.2rem rgba(var(--text-color), 0.2) inset; font-family: var(--font-family); - width: 100% + width: 100%; outline: none; min-width: 0; } - +.input.readonly .clear{ + opacity: 0 !important; + margin-right: -2em; + pointer-events: none !important; +} +.readonly{ + pointer-events: none; +} input:focus{ caret-color: var(--accent-color); } -.input:focus-within{ - box-shadow: 0 0 0 0.1em var(--accent-color) inset; +.input:focus-within:not(.readonly){ + -webkit-box-shadow: 0 0 0 0.1em var(--accent-color) inset; + box-shadow: 0 0 0 0.1em var(--accent-color) inset; +} +.disabled{ + pointer-events: none; + opacity: 0.6; } - .label { - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; opacity: .7; font-weight: 400; font-size: 1em; position: absolute; top: 0; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + -o-transition: transform 0.3s; transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; -webkit-transform-origin: left; - transform-origin: left; + -ms-transform-origin: left; + transform-origin: left; pointer-events: none; white-space: nowrap; overflow: hidden; - text-overflow: ellipsis; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; width: 100%; will-change: transform; } @@ -246,10 +304,16 @@ input:focus{ } .container{ width: 100%; + display: -webkit-box; + display: -ms-flexbox; display: flex; position: relative; - align-items: center; - flex: 1; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; } input{ font-size: 1em; @@ -261,12 +325,14 @@ input{ } .animate-label .container input { -webkit-transform: translateY(0.6em); - transform: translateY(0.6em); + -ms-transform: translateY(0.6em); + transform: translateY(0.6em); } .animate-label .container .label { -webkit-transform: translateY(-0.6em) scale(0.8); - transform: translateY(-0.6em) scale(0.8); + -ms-transform: translateY(-0.6em) scale(0.8); + transform: translateY(-0.6em) scale(0.8); opacity: 1; color: var(--accent-color) } @@ -281,6 +347,9 @@ input{ border: solid 1px rgba(var(--text-color), 0.2); padding: 0.6em 1em; } +.helper-text:empty{ + padding: 0; +} @media (any-hover: hover){ .icon:hover{ background: rgba(var(--text-color), 0.1); @@ -288,26 +357,28 @@ input{ }
- -
+ +
`; customElements.define('sm-input', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smInput.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smInput.content.cloneNode(true)) } static get observedAttributes() { return ['placeholder'] @@ -339,22 +410,31 @@ customElements.define('sm-input', return this.shadowRoot.querySelector('input').checkValidity() } - focusIn() { - this.shadowRoot.querySelector('input').focus() + set disabled(value) { + if (value) + this.shadowRoot.querySelector('.input').classList.add('disabled') + else + this.shadowRoot.querySelector('.input').classList.remove('disabled') } - - focusOut() { - this.shadowRoot.querySelector('input').blur() - } - - 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(); + set readOnly(value) { + if (value) { + this.shadowRoot.querySelector('input').setAttribute('readonly', '') + this.shadowRoot.querySelector('.input').classList.add('readonly') + } else { + this.shadowRoot.querySelector('input').removeAttribute('readonly') + this.shadowRoot.querySelector('.input').classList.remove('readonly') } } - fireEvent() { + focusIn = () => { + this.input.focus() + } + + focusOut = () => { + this.input.blur() + } + + fireEvent = () => { let event = new Event('input', { bubbles: true, cancelable: true, @@ -363,7 +443,7 @@ customElements.define('sm-input', this.dispatchEvent(event); } - checkInput() { + checkInput = (e) => { if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') return; if (this.input.value !== '') { @@ -373,8 +453,7 @@ customElements.define('sm-input', this.label.classList.add('hide') if (!this.readonly) this.clearBtn.classList.remove('hide') - } - else { + } else { if (this.animate) this.inputParent.classList.remove('animate-label') else @@ -392,6 +471,8 @@ customElements.define('sm-input', this.helperText = this.shadowRoot.querySelector('.helper-text') this.valueChanged = false; this.readonly = false + this.min + this.max this.animate = this.hasAttribute('animate') this.input = this.shadowRoot.querySelector('input') this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') @@ -402,26 +483,45 @@ customElements.define('sm-input', if (this.hasAttribute('required')) { this.input.setAttribute('required', '') } + if (this.hasAttribute('min')) { + let minValue = this.getAttribute('min') + this.input.setAttribute('min', minValue) + this.min = parseInt(minValue) + } + if (this.hasAttribute('max')) { + let maxValue = this.getAttribute('max') + this.input.setAttribute('max', maxValue) + this.max = parseInt(maxValue) + } + if (this.hasAttribute('minlength')) { + let minValue = this.getAttribute('minlength') + this.input.setAttribute('minlength', minValue) + } + if (this.hasAttribute('maxlength')) { + let maxValue = this.getAttribute('maxlength') + this.input.setAttribute('maxlength', maxValue) + } + if (this.hasAttribute('pattern')) { + this.input.setAttribute('pattern', this.getAttribute('pattern')) + } if (this.hasAttribute('readonly')) { this.input.setAttribute('readonly', '') this.readonly = true } + if (this.hasAttribute('disabled')) { + this.inputParent.classList.add('disabled') + } if (this.hasAttribute('helper-text')) { this.helperText.textContent = this.getAttribute('helper-text') } if (this.hasAttribute('type')) { if (this.getAttribute('type') === 'number') { this.input.setAttribute('inputmode', 'numeric') - } - else + this.input.setAttribute('type', 'number') + } else this.input.setAttribute('type', this.getAttribute('type')) - } - else + } else this.input.setAttribute('type', 'text') - this.input.addEventListener('keydown', e => { - if (this.getAttribute('type') === 'number') - this.preventNonNumericalInput(e); - }) this.input.addEventListener('input', e => { this.checkInput() }) @@ -432,8 +532,10 @@ customElements.define('sm-input', attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { - if (name === 'placeholder') + if (name === 'placeholder') { this.shadowRoot.querySelector('.label').textContent = newValue; + this.setAttribute('aria-label', newValue); + } } } }) @@ -445,12 +547,15 @@ smTextarea.innerHTML = ` *{ padding: 0; margin: 0; - box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; } ::-moz-focus-inner{ border: none; } :host{ + display: -webkit-box; + display: -ms-flexbox; display: flex; } .hide{ @@ -475,16 +580,24 @@ smTextarea.innerHTML = ` min-width: 0; } .input { - flex: 1; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + display: -webkit-box; + display: -ms-flexbox; display: flex; - align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; position: relative; padding: 0.7em 1em; border-radius: 0.3em; + -webkit-transition: opacity 0.3s; + -o-transition: opacity 0.3s; transition: opacity 0.3s; background: rgba(var(--text-color), 0.1); font-family: var(--font-family); - width: 100% + width: 100%; outline: none; min-width: 0; } @@ -493,23 +606,33 @@ textarea:focus{ caret-color: var(--accent-color); } .input:focus-within{ - box-shadow: 0 0 0 0.1em var(--accent-color) inset; + -webkit-box-shadow: 0 0 0 0.1em var(--accent-color) inset; + box-shadow: 0 0 0 0.1em var(--accent-color) inset; } .label { - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; opacity: .7; font-weight: 400; font-size: 1em; position: absolute; top: 0.9em; + -webkit-transition: -webkit-transform 0.3s; + transition: -webkit-transform 0.3s; + -o-transition: transform 0.3s; transition: transform 0.3s; + transition: transform 0.3s, -webkit-transform 0.3s; -webkit-transform-origin: left; - transform-origin: left; + -ms-transform-origin: left; + transform-origin: left; pointer-events: none; white-space: nowrap; overflow: hidden; - text-overflow: ellipsis; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; width: 100%; will-change: transform; } @@ -525,17 +648,20 @@ textarea{ } .animate-label textarea { -webkit-transform: translateY(0.6em); - transform: translateY(0.6em); + -ms-transform: translateY(0.6em); + transform: translateY(0.6em); } .animate-label .label { -webkit-transform: translateY(-0.6em) scale(0.8); - transform: translateY(-0.6em) scale(0.8); + -ms-transform: translateY(-0.6em) scale(0.8); + transform: translateY(-0.6em) scale(0.8); opacity: 1; color: var(--accent-color) } .clear{ - align-self: flex-start; + -ms-flex-item-align: start; + align-self: flex-start; } @media (any-hover: hover){ .icon:hover{ @@ -557,7 +683,9 @@ customElements.define('sm-textarea', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smTextarea.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smTextarea.content.cloneNode(true)) } static get observedAttributes() { return ['placeholder'] @@ -598,16 +726,11 @@ customElements.define('sm-textarea', this.inputParent.classList.add('animate-label') else this.label.classList.add('hide') - if (!this.readonly) - this.clearBtn.classList.remove('hide') - } - else { + } else { if (this.animate) this.inputParent.classList.remove('animate-label') else this.label.classList.remove('hide') - if (!this.readonly) - this.clearBtn.classList.add('hide') } this.input.style.height = 'auto' @@ -621,7 +744,6 @@ customElements.define('sm-textarea', this.label = this.shadowRoot.querySelector('.label') this.helperText = this.shadowRoot.querySelector('.helper-text') this.valueChanged = false; - this.readonly = false this.animate = this.hasAttribute('animate') this.input = this.shadowRoot.querySelector('textarea') this.shadowRoot.querySelector('.label').textContent = this.getAttribute('placeholder') @@ -637,15 +759,10 @@ customElements.define('sm-textarea', } if (this.hasAttribute('readonly')) { this.input.setAttribute('readonly', '') - this.readonly = true } if (this.hasAttribute('helper-text')) { this.helperText.textContent = this.getAttribute('helper-text') } - this.input.addEventListener('keydown', e => { - if (this.getAttribute('type') === 'number') - this.preventNonNumericalInput(e); - }) this.input.addEventListener('input', e => { this.checkInput() }) @@ -666,43 +783,52 @@ customElements.define('sm-textarea', const smTab = document.createElement('template') smTab.innerHTML = `
@@ -712,7 +838,9 @@ opacity: 1 customElements.define('sm-tab', class extends HTMLElement { constructor() { super() - this.shadow = this.attachShadow({ mode: 'open' }).append(smTab.content.cloneNode(true)) + this.shadow = this.attachShadow({ + mode: 'open' + }).append(smTab.content.cloneNode(true)) } }) @@ -721,88 +849,99 @@ customElements.define('sm-tab', class extends HTMLElement { 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)) + this.attachShadow({ + mode: 'open' + }).append(smCheckbox.content.cloneNode(true)) this.checkbox = this.shadowRoot.querySelector('.checkbox'); this.input = this.shadowRoot.querySelector('input') @@ -852,8 +991,7 @@ customElements.define('sm-checkbox', class extends HTMLElement { if (newValue === 'true') { this.checkbox.classList.add('disabled') this.isDisabled = true - } - else { + } else { this.checkbox.classList.remove('disabled') this.isDisabled = false } @@ -863,8 +1001,7 @@ customElements.define('sm-checkbox', class extends HTMLElement { this.isChecked = true this.input.checked = true this.dispatch() - } - else { + } else { this.isChecked = false this.input.checked = false this.dispatch() @@ -880,101 +1017,129 @@ customElements.define('sm-checkbox', class extends HTMLElement { 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)) + this.attachShadow({ + mode: 'open' + }).append(smSwitch.content.cloneNode(true)) this.switch = this.shadowRoot.querySelector('.switch'); this.input = this.shadowRoot.querySelector('input') this.isChecked = false @@ -990,8 +1155,7 @@ customElements.define('sm-switch', class extends HTMLElement { this.disabled = true this.setAttribute('disabled', '') this.switch.classList.add('disabled') - } - else { + } else { this.disabled = false this.removeAttribute('disabled') this.switch.classList.remove('disabled') @@ -1008,8 +1172,7 @@ customElements.define('sm-switch', class extends HTMLElement { this.setAttribute('checked', '') this.isChecked = true this.input.checked = true - } - else { + } else { this.removeAttribute('checked') this.isChecked = false this.input.checked = false @@ -1045,83 +1208,107 @@ customElements.define('sm-switch', class extends HTMLElement { const smSelect = document.createElement('template') smSelect.innerHTML = `
@@ -1137,7 +1324,9 @@ smSelect.innerHTML = ` customElements.define('sm-select', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smSelect.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smSelect.content.cloneNode(true)) } static get observedAttributes() { return ['value'] @@ -1163,13 +1352,19 @@ customElements.define('sm-select', class extends HTMLElement { selection = this.shadowRoot.querySelector('.selection'), previousOption this.open = false; - this.slideDown = [ - { transform: `translateY(-0.5rem)` }, - { transform: `translateY(0)` } - ], - this.slideUp = [ - { transform: `translateY(0)` }, - { transform: `translateY(-0.5rem)` } + this.slideDown = [{ + transform: `translateY(-0.5rem)` + }, + { + transform: `translateY(0)` + } + ], + this.slideUp = [{ + transform: `translateY(0)` + }, + { + transform: `translateY(-0.5rem)` + } ], this.animationOptions = { duration: 300, @@ -1258,54 +1453,61 @@ customElements.define('sm-select', class extends HTMLElement { const smOption = document.createElement('template') smOption.innerHTML = `
@@ -1316,7 +1518,9 @@ smOption.innerHTML = ` customElements.define('sm-option', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smOption.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smOption.content.cloneNode(true)) } sendDetails(switching) { @@ -1364,100 +1568,119 @@ customElements.define('sm-option', class extends HTMLElement { const smStripSelect = document.createElement('template') smStripSelect.innerHTML = `
@@ -1477,7 +1700,9 @@ smStripSelect.innerHTML = ` customElements.define('sm-strip-select', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smStripSelect.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smStripSelect.content.cloneNode(true)) } static get observedAttributes() { return ['value'] @@ -1488,7 +1713,7 @@ customElements.define('sm-strip-select', class extends HTMLElement { set value(val) { this.setAttribute('value', val) } - scrollLeft() { + scrollLeft = () => { this.select.scrollBy({ top: 0, left: -this.scrollDistance, @@ -1496,7 +1721,7 @@ customElements.define('sm-strip-select', class extends HTMLElement { }) } - scrollRight() { + scrollRight = () => { this.select.scrollBy({ top: 0, left: this.scrollDistance, @@ -1518,8 +1743,7 @@ customElements.define('sm-strip-select', class extends HTMLElement { if (entries[0].isIntersecting) { this.previousArrow.classList.add('hide') this.previousGradient.classList.add('hide') - } - else { + } else { this.previousArrow.classList.remove('hide') this.previousGradient.classList.remove('hide') } @@ -1531,8 +1755,7 @@ customElements.define('sm-strip-select', class extends HTMLElement { if (entries[0].isIntersecting) { this.nextArrow.classList.add('hide') this.nextGradient.classList.add('hide') - } - else { + } else { this.nextArrow.classList.remove('hide') this.nextGradient.classList.remove('hide') } @@ -1553,7 +1776,11 @@ customElements.define('sm-strip-select', class extends HTMLElement { if (previousOption) previousOption.classList.remove('active') e.target.classList.add('active') - e.target.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }) + e.target.scrollIntoView({ + behavior: 'smooth', + inline: 'center', + block: 'nearest' + }) this.setAttribute('value', e.detail.value) this.dispatchEvent(new CustomEvent('change', { bubbles: true, @@ -1572,13 +1799,13 @@ customElements.define('sm-strip-select', class extends HTMLElement { previousOption = firstElement; } }); - this.nextArrow.addEventListener('click', this.scrollRight.bind(this)) - this.previousArrow.addEventListener('click', this.scrollLeft.bind(this)) + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) } disconnectedCallback() { - this.nextArrow.removeEventListener('click', this.scrollRight.bind(this)) - this.previousArrow.removeEventListener('click', this.scrollLeft.bind(this)) + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) } }) @@ -1586,36 +1813,41 @@ customElements.define('sm-strip-select', class extends HTMLElement { const smStripOption = document.createElement('template') smStripOption.innerHTML = `
@@ -1623,7 +1855,9 @@ smStripOption.innerHTML = ` customElements.define('sm-strip-option', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smStripOption.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smStripOption.content.cloneNode(true)) } sendDetails() { let optionSelected = new CustomEvent('optionSelected', { @@ -1660,78 +1894,126 @@ const smPopup = document.createElement('template') smPopup.innerHTML = ` `; customElements.define('sm-popup', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smPopup.content.cloneNode(true)) + + this.allowClosing = false } - resumeScrolling() { + resumeScrolling = () => { const scrollY = document.body.style.top; window.scrollTo(0, parseInt(scrollY || '0') * -1); setTimeout(() => { @@ -1777,31 +2054,49 @@ customElements.define('sm-popup', class extends HTMLElement { }, 300); } - show(pinned, popupStack) { - this.setAttribute('open', '') - this.pinned = pinned - this.popupStack = popupStack - this.popupContainer.classList.remove('hide') - if (window.innerWidth < 648) - this.popup.style.transform = 'translateY(0)'; - else - this.popup.style.transform = 'scale(1)'; + show = (pinned, popupStack) => { + if (popupStack) + this.popupStack = popupStack + if (this.popupStack && !this.hasAttribute('open')) { + this.popupStack.push({ + popup: this, + permission: pinned + }) + if (this.popupStack.items.length > 1) { + this.popupStack.items[this.popupStack.items.length - 2].popup.classList.add('stacked') + } + this.dispatchEvent( + new CustomEvent("popupopened", { + bubbles: true, + detail: { + popup: this, + popupStack: this.popupStack + } + }) + ) + this.setAttribute('open', '') + this.pinned = pinned + this.popupContainer.classList.remove('hide') + } + this.popup.style.transform = 'translateY(0)'; document.body.setAttribute('style', `overflow: hidden; top: -${window.scrollY}px`) + return this.popupStack } - hide() { - this.removeAttribute('open') - if (window.innerWidth < 648) + hide = () => { + if (window.innerWidth < 640) this.popup.style.transform = 'translateY(100%)'; else - this.popup.style.transform = 'scale(0.9)'; + this.popup.style.transform = 'translateY(1rem)'; this.popupContainer.classList.add('hide') + this.removeAttribute('open') if (typeof this.popupStack !== 'undefined') { this.popupStack.pop() - if (this.popupStack.items.length === 0) { + if (this.popupStack.items.length) { + this.popupStack.items[this.popupStack.items.length - 1].popup.classList.remove('stacked') + } else { this.resumeScrolling() } - } - else { + } else { this.resumeScrolling() } @@ -1815,15 +2110,24 @@ customElements.define('sm-popup', class extends HTMLElement { }) }, 300); } + this.dispatchEvent( + new CustomEvent("popupclosed", { + bubbles: true, + detail: { + popup: this, + popupStack: this.popupStack + } + }) + ) } - handleTouchStart(e) { + handleTouchStart = (e) => { this.touchStartY = e.changedTouches[0].clientY - this.popup.style.transition = 'initial' + this.popup.style.transition = 'transform 0.1s' this.touchStartTime = e.timeStamp } - handleTouchMove(e) { + handleTouchMove = (e) => { e.preventDefault() if (this.touchStartY < e.changedTouches[0].clientY) { this.offset = e.changedTouches[0].clientY - this.touchStartY; @@ -1835,26 +2139,33 @@ customElements.define('sm-popup', class extends HTMLElement { }*/ } - handleTouchEnd(e) { + handleTouchEnd = (e) => { this.touchEndTime = e.timeStamp cancelAnimationFrame(this.touchEndAnimataion) this.touchEndY = e.changedTouches[0].clientY this.popup.style.transition = 'transform 0.3s' if (this.touchEndTime - this.touchStartTime > 200) { if (this.touchEndY - this.touchStartY > this.threshold) { - this.hide() - } - else { + if (this.pinned) { + this.show() + return + } else + this.hide() + } else { this.show() } - } - else { + } else { if (this.touchEndY > this.touchStartY) + if (this.pinned) { + this.show() + return + } + else this.hide() } } - movePopup() { + movePopup = () => { this.popup.style.transform = `translateY(${this.offset}px)` } @@ -1877,12 +2188,16 @@ customElements.define('sm-popup', class extends HTMLElement { this.show() this.popupContainer.addEventListener('mousedown', e => { if (e.target === this.popupContainer && !this.pinned) { - this.hide() + if (this.pinned) { + this.show() + return + } else + this.hide() } }) this.popupBodySlot.addEventListener('slotchange', () => { - this.inputFields = this.popupBodySlot.assignedElements().filter(element => element.tagName === 'SM-INPUT' || element.tagName === 'SM-CHECKBOX' || element.tagName === 'TEXTAREA' || element.type === 'radio') + this.inputFields = this.querySelectorAll('sm-input', 'sm-checkbox', 'textarea', 'radio') }) this.popupHeader.addEventListener('touchstart', (e) => { @@ -1896,9 +2211,9 @@ customElements.define('sm-popup', class extends HTMLElement { }) } disconnectedCallback() { - this.popupHeader.removeEventListener('touchstart', this.handleTouchStart.bind(this)) - this.popupHeader.removeEventListener('touchmove', this.handleTouchMove.bind(this)) - this.popupHeader.removeEventListener('touchend', this.handleTouchEnd.bind(this)) + this.popupHeader.removeEventListener('touchstart', this.handleTouchStart) + this.popupHeader.removeEventListener('touchmove', this.handleTouchMove) + this.popupHeader.removeEventListener('touchend', this.handleTouchEnd) } }) @@ -1908,82 +2223,114 @@ const smCarousel = document.createElement('template') smCarousel.innerHTML = ` `; customElements.define('sm-carousel', class extends HTMLElement { constructor() { super() - this.attachShadow({ mode: 'open' }).append(smCarousel.content.cloneNode(true)) + this.attachShadow({ + mode: 'open' + }).append(smCarousel.content.cloneNode(true)) } - scrollLeft() { + static get observedAttributes() { + return ['indicator'] + } + + scrollLeft = () => { this.carousel.scrollBy({ top: 0, left: -this.scrollDistance, @@ -2047,7 +2399,7 @@ customElements.define('sm-carousel', class extends HTMLElement { }) } - scrollRight() { + scrollRight = () => { this.carousel.scrollBy({ top: 0, left: this.scrollDistance, @@ -2061,32 +2413,50 @@ customElements.define('sm-carousel', class extends HTMLElement { this.carouselSlot = this.shadowRoot.querySelector('slot') this.nextArrow = this.shadowRoot.querySelector('.next-item') this.previousArrow = this.shadowRoot.querySelector('.previous-item') - this.nextGradient = this.shadowRoot.querySelector('.right') - this.previousGradient = this.shadowRoot.querySelector('.left') + this.indicatorsContainer = this.shadowRoot.querySelector('.indicators') this.carouselItems + this.indicators + this.showIndicator = false this.scrollDistance = this.carouselContainer.getBoundingClientRect().width / 3 - const firstElementObserver = new IntersectionObserver(entries => { - if (entries[0].isIntersecting) { - this.previousArrow.classList.remove('expand') - this.previousGradient.classList.add('hide') - } - else { - this.previousArrow.classList.add('expand') - this.previousGradient.classList.remove('hide') - } - }, { - root: this.carouselContainer, - threshold: 0.9 - }) - const lastElementObserver = new IntersectionObserver(entries => { - if (entries[0].isIntersecting) { - this.nextArrow.classList.remove('expand') - this.nextGradient.classList.add('hide') - } - else { - this.nextArrow.classList.add('expand') - this.nextGradient.classList.remove('hide') - } + let frag = document.createDocumentFragment(); + if (this.hasAttribute('indicator')) + this.showIndicator = true + + + let firstVisible = false, + lastVisible = false + const allElementsObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (this.showIndicator) + if (entry.isIntersecting) { + this.indicators[parseInt(entry.target.attributes.rank.textContent)].classList.add('active') + } + else + this.indicators[parseInt(entry.target.attributes.rank.textContent)].classList.remove('active') + if (!entry.target.previousElementSibling) + if (entry.isIntersecting) { + this.previousArrow.classList.remove('expand') + firstVisible = true + } + else { + this.previousArrow.classList.add('expand') + firstVisible = false + } + if (!entry.target.nextElementSibling) + if (entry.isIntersecting) { + this.nextArrow.classList.remove('expand') + lastVisible = true + } + else { + this.nextArrow.classList.add('expand') + + lastVisible = false + } + if (firstVisible && lastVisible) + this.indicatorsContainer.classList.add('hide') + else + this.indicatorsContainer.classList.remove('hide') + }) }, { root: this.carouselContainer, threshold: 0.9 @@ -2102,8 +2472,18 @@ customElements.define('sm-carousel', class extends HTMLElement { this.carouselSlot.addEventListener('slotchange', e => { this.carouselItems = this.carouselSlot.assignedElements() - firstElementObserver.observe(this.carouselItems[0]) - lastElementObserver.observe(this.carouselItems[this.carouselItems.length - 1]) + this.carouselItems.forEach(item => allElementsObserver.observe(item)) + if (this.showIndicator) { + this.indicatorsContainer.innerHTML = `` + this.carouselItems.forEach((item, index) => { + let dot = document.createElement('div') + dot.classList.add('dot') + frag.append(dot) + item.setAttribute('rank', index) + }) + this.indicatorsContainer.append(frag) + this.indicators = this.indicatorsContainer.children + } }) this.addEventListener('keyup', e => { @@ -2113,13 +2493,22 @@ customElements.define('sm-carousel', class extends HTMLElement { this.scrollRight() }) - this.nextArrow.addEventListener('click', this.scrollRight.bind(this)) - this.previousArrow.addEventListener('click', this.scrollLeft.bind(this)) + this.nextArrow.addEventListener('click', this.scrollRight) + this.previousArrow.addEventListener('click', this.scrollLeft) + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === 'indicator') { + if (this.hasAttribute('indicator')) + this.showIndicator = true + else + this.showIndicator = false + } } disconnectedCallback() { - this.nextArrow.removeEventListener('click', this.scrollRight.bind(this)) - this.previousArrow.removeEventListener('click', this.scrollLeft.bind(this)) + this.nextArrow.removeEventListener('click', this.scrollRight) + this.previousArrow.removeEventListener('click', this.scrollLeft) } }) @@ -2128,130 +2517,153 @@ customElements.define('sm-carousel', class extends HTMLElement { const smNotifications = document.createElement('template') smNotifications.innerHTML = `
@@ -2260,29 +2672,30 @@ stroke-width: 6; customElements.define('sm-notifications', class extends HTMLElement { constructor() { super() - this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) + this.shadow = this.attachShadow({ + mode: 'open' + }).append(smNotifications.content.cloneNode(true)) } - handleTouchStart(e) { + handleTouchStart = (e) => { this.notification = e.target.closest('.notification') this.touchStartX = e.changedTouches[0].clientX this.notification.style.transition = 'initial' this.touchStartTime = e.timeStamp } - handleTouchMove(e) { + handleTouchMove = (e) => { e.preventDefault() if (this.touchStartX < e.changedTouches[0].clientX) { this.offset = e.changedTouches[0].clientX - this.touchStartX; this.touchEndAnimataion = requestAnimationFrame(this.movePopup) - } - else { + } else { this.offset = -(this.touchStartX - e.changedTouches[0].clientX); this.touchEndAnimataion = requestAnimationFrame(this.movePopup) } } - handleTouchEnd(e) { + handleTouchEnd = (e) => { this.notification.style.transition = 'transform 0.3s, opacity 0.3s' this.touchEndTime = e.timeStamp cancelAnimationFrame(this.touchEndAnimataion) @@ -2290,19 +2703,15 @@ customElements.define('sm-notifications', class extends HTMLElement { if (this.touchEndTime - this.touchStartTime > 200) { if (this.touchEndX - this.touchStartX > this.threshold) { this.removeNotification(this.notification) - } - else if (this.touchStartX - this.touchEndX > this.threshold) { + } else if (this.touchStartX - this.touchEndX > this.threshold) { this.removeNotification(this.notification, true) - } - else { + } else { this.resetPosition() } - } - else { + } else { if (this.touchEndX > this.touchStartX) { this.removeNotification(this.notification) - } - else { + } else { this.removeNotification(this.notification, true) } } @@ -2312,11 +2721,11 @@ customElements.define('sm-notifications', class extends HTMLElement { this.notification.style.transform = `translateX(${this.offset}px)` } - resetPosition() { + resetPosition = () => { this.notification.style.transform = `translateX(0)` } - push(messageBody, type, pinned) { + push = (messageBody, type, pinned) => { let notification = document.createElement('div'), composition = `` notification.classList.add('notification') @@ -2329,8 +2738,7 @@ customElements.define('sm-notifications', class extends HTMLElement { ` - } - else if (type === 'success') { + } else if (type === 'success') { composition += ` @@ -2346,8 +2754,7 @@ customElements.define('sm-notifications', class extends HTMLElement { notification.innerHTML = composition this.notificationPanel.prepend(notification) if (window.innerWidth > 640) { - notification.animate([ - { + notification.animate([{ transform: `translateX(1rem)`, opacity: '0' }, @@ -2358,22 +2765,20 @@ customElements.define('sm-notifications', class extends HTMLElement { ], this.animationOptions).onfinish = () => { notification.setAttribute('style', `transform: none;`); } - } - else { + } else { notification.setAttribute('style', `transform: translateY(0); opacity: 1`) } - notification.addEventListener('touchstart', this.handleTouchStart.bind(this)) - notification.addEventListener('touchmove', this.handleTouchMove.bind(this)) - notification.addEventListener('touchend', this.handleTouchEnd.bind(this)) + notification.addEventListener('touchstart', this.handleTouchStart) + notification.addEventListener('touchmove', this.handleTouchMove) + notification.addEventListener('touchend', this.handleTouchEnd) } - removeNotification(notification, toLeft) { + removeNotification = (notification, toLeft) => { if (!this.offset) this.offset = 0; if (toLeft) - notification.animate([ - { + notification.animate([{ transform: `translateX(${this.offset}px)`, opacity: '1' }, @@ -2385,8 +2790,7 @@ customElements.define('sm-notifications', class extends HTMLElement { notification.remove() } else { - notification.animate([ - { + notification.animate([{ transform: `translateX(${this.offset}px)`, opacity: '1' }, @@ -2424,7 +2828,7 @@ customElements.define('sm-notifications', class extends HTMLElement { this.touchEndAnimataion; this.notificationPanel.addEventListener('click', e => { - if (e.target.closest('.close')) ( + if (e.target.closest('.close'))( this.removeNotification(e.target.closest('.notification')) ) }) @@ -2441,8 +2845,7 @@ customElements.define('sm-notifications', class extends HTMLElement { this.notificationPanel.style.padding = '1.5rem 0 3rem 1.5rem'; else this.notificationPanel.style.padding = '1rem 1rem 2rem 1rem'; - } - else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { + } else if (mutation.removedNodes.length && !this.notificationPanel.children.length) { this.notificationPanel.style.padding = 0; } } @@ -2461,92 +2864,121 @@ customElements.define('sm-notifications', class extends HTMLElement { const smMenu = document.createElement('template') smMenu.innerHTML = `