diff --git a/components.js b/components.js index 8a1fc6b..b1c5e6d 100644 --- a/components.js +++ b/components.js @@ -2,100 +2,117 @@ const smButton = document.createElement('template') smButton.innerHTML = ` -
+
`; +`; 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() { @@ -105,23 +122,21 @@ customElements.define('sm-button', set disabled(value) { if (value && !this.isDisabled) { this.isDisabled = true - this.setAttribute('disabled', '') + this.setAttribute('disable', '') this.button.removeAttribute('tabindex') - } - else if (!value && this.isDisabled) { + } else if (!value && this.isDisabled) { this.isDisabled = false - this.removeAttribute('disabled') + this.removeAttribute('disable') } } dispatch() { if (this.isDisabled) { - this.dispatchEvent(new CustomEvent('disabled', { + this.dispatchEvent(new CustomEvent('disable', { bubbles: true, composed: true })) - } - else { + } else { this.dispatchEvent(new CustomEvent('clicked', { bubbles: true, composed: true @@ -132,15 +147,11 @@ customElements.define('sm-button', connectedCallback() { this.isDisabled = false this.button = this.shadowRoot.querySelector('.button') - if (this.hasAttribute('disabled') && !this.isDisabled) + if (this.hasAttribute('disable') && !this.isDisabled) this.isDisabled = true this.addEventListener('click', (e) => { this.dispatch() }) - this.addEventListener('keyup', (e) => { - if (e.code === "Enter" || e.code === "Space") - this.dispatch() - }) } }) @@ -151,7 +162,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,15 +179,26 @@ 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; + --font-size: 1rem; + --border-radius: 0.3rem; + --padding: 0.7rem 1rem; + --background: rgba(var(--text-color), 0.06); } .hide{ opacity: 0 !important; @@ -185,62 +208,74 @@ border: none; display: none; } .icon { - fill: none; - height: 1.6em; - width: 1.6em; - padding: 0.5em; - stroke: rgba(var(--text-color), 0.7); - stroke-width: 10; - overflow: visible; - stroke-linecap: round; - border-radius: 1em; - stroke-linejoin: round; + fill: rgba(var(--text-color), 0.6); + height: 1.4rem; + width: 1.4rem; + border-radius: 1rem; cursor: pointer; min-width: 0; } + :host(.round) .input{ border-radius: 10rem; } .input { + display: -webkit-box; + display: -ms-flexbox; display: flex; - align-items: center; - text-align: left; - position: relative; - gap: 1em; - padding: 0.7em 1em; - border-radius: 0.3em; - transition: opacity 0.3s; - background: rgba(var(--text-color), 0.06); - box-shadow: 0 0 0 0.1em rgba(var(--text-color), 0.2) inset; - font-family: var(--font-family); - width: 100% - outline: none; + cursor: text; min-width: 0; + text-align: left; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: relative; + gap: 0.5rem; + padding: var(--padding); + border-radius: var(--border-radius); + -webkit-transition: opacity 0.3s; + -o-transition: opacity 0.3s; + transition: opacity 0.3s; + background: var(--background); + width: 100%; + outline: none; } -input:focus{ - caret-color: var(--accent-color); +.input.readonly .clear{ + opacity: 0 !important; + margin-right: -2rem; + pointer-events: none !important; } -.input:focus-within{ - box-shadow: 0 0 0 0.1em var(--accent-color) inset; +.readonly{ + pointer-events: none; +} +.input:focus-within:not(.readonly){ + box-shadow: 0 0 0 0.1rem var(--accent-color) inset !important; } .disabled{ pointer-events: none; + opacity: 0.6; } .label { - user-select: none; opacity: .7; font-weight: 400; - font-size: 1em; + font-size: var(--font-size); 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%; + user-select: none; will-change: transform; } .outer-container{ @@ -249,42 +284,65 @@ 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; + font-size: var(--font-size); border: none; background: transparent; outline: none; color: rgba(var(--text-color), 1); width: 100%; } -.animate-label .container input { - -webkit-transform: translateY(0.6em); - transform: translateY(0.6em); +:host(:not(.outlined)) .animate-label .container input { + -webkit-transform: translateY(0.6rem); + -ms-transform: translateY(0.6rem); + transform: translateY(0.6rem); } -.animate-label .container .label { - -webkit-transform: translateY(-0.6em) scale(0.8); - transform: translateY(-0.6em) scale(0.8); +:host(:not(.outlined)) .animate-label .label { + -webkit-transform: translateY(-0.7em) scale(0.8); + -ms-transform: translateY(-0.7em) scale(0.8); + transform: translateY(-0.7em) scale(0.8); opacity: 1; color: var(--accent-color) } -.helper-text{ - top: 100%; - width: 100%; - position: absolute; - color: var(--error-color); +:host(.outlined) .input { + box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 0.4) inset; background: rgba(var(--foreground-color), 1); - margin-top: 0.5em; - border-radius: 0.2em; - border: solid 1px rgba(var(--text-color), 0.2); - padding: 0.6em 1em; } -.helper-text:empty{ +:host(.outlined) .label { + width: max-content; + margin-left: -0.5rem; + padding: 0 0.5rem; +} +:host(.outlined) .animate-label .label { + -webkit-transform: translate(0.1rem, -1.5rem) scale(0.8); + -ms-transform: translate(0.1rem, -1.5rem) scale(0.8); + transform: translate(0.1rem, -1.5rem) scale(0.8); + opacity: 1; + background: rgba(var(--foreground-color), 1); +} +.animate-label:focus-within:not(.readonly) .label{ + color: var(--accent-color) +} +.feedback-text{ + font-size: 0.9rem; + width: 100%; + color: var(--error-color); + padding: 0.6rem 1rem; + text-align: left; +} +.feedback-text:empty{ padding: 0; } @media (any-hover: hover){ @@ -300,21 +358,23 @@ input{
- - clear - - - + -
+
`; customElements.define('sm-input', class extends HTMLElement { + + static formAssociated = true; + 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'] } @@ -345,26 +405,44 @@ customElements.define('sm-input', return this.shadowRoot.querySelector('input').checkValidity() } + get validity() { + return this.shadowRoot.querySelector('input').validity + } + set disabled(value) { if (value) this.shadowRoot.querySelector('.input').classList.add('disabled') else this.shadowRoot.querySelector('.input').classList.remove('disabled') } + 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') + } + } + + setValidity = (message) => { + this.feedbackText.textContent = message + } + + showValidity = () => { + this.feedbackText.classList.remove('hide-completely') + } + + hideValidity = () => { + this.feedbackText.classList.add('hide-completely') + } focusIn = () => { - this.shadowRoot.querySelector('input').focus() + this.input.focus() } focusOut = () => { - this.shadowRoot.querySelector('input').blur() - } - - preventNonNumericalInput = (e) => { - let keyCode = e.keyCode; - if (!((keyCode > 47 && keyCode < 56) || (keyCode > 36 && keyCode < 39) || (keyCode > 95 && keyCode < 106) || keyCode === 110 || (keyCode > 7 && keyCode < 19))) { - e.preventDefault(); - } + this.input.blur() } fireEvent = () => { @@ -377,23 +455,24 @@ customElements.define('sm-input', } checkInput = (e) => { - if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') - return; + if (!this.readonly) { + if (this.input.value !== '') { + this.clearBtn.classList.remove('hide') + } else { + this.clearBtn.classList.add('hide') + } + } + if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder') === '') return; if (this.input.value !== '') { if (this.animate) 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') } } @@ -402,9 +481,10 @@ customElements.define('sm-input', this.inputParent = this.shadowRoot.querySelector('.input') this.clearBtn = this.shadowRoot.querySelector('.clear') this.label = this.shadowRoot.querySelector('.label') - this.helperText = this.shadowRoot.querySelector('.helper-text') + this.feedbackText = this.shadowRoot.querySelector('.feedback-text') this.valueChanged = false; this.readonly = false + this.isNumeric = false this.min this.max this.animate = this.hasAttribute('animate') @@ -427,35 +507,42 @@ customElements.define('sm-input', this.input.setAttribute('max', maxValue) this.max = parseInt(maxValue) } + if (this.hasAttribute('minlength')) { + const minValue = this.getAttribute('minlength') + this.input.setAttribute('minlength', minValue) + } + if (this.hasAttribute('maxlength')) { + const maxValue = this.getAttribute('maxlength') + this.input.setAttribute('maxlength', maxValue) + } + if (this.hasAttribute('step')) { + const steps = this.getAttribute('step') + this.input.setAttribute('step', steps) + } if (this.hasAttribute('pattern')) { this.input.setAttribute('pattern', this.getAttribute('pattern')) } if (this.hasAttribute('readonly')) { this.input.setAttribute('readonly', '') - this.readonly = true + this.readonly = true } - if (this.hasAttribute('readonly')) { + if (this.hasAttribute('disabled')) { this.inputParent.classList.add('disabled') } - if (this.hasAttribute('helper-text')) { - this.helperText.textContent = this.getAttribute('helper-text') + if (this.hasAttribute('error-text')) { + this.feedbackText.textContent = this.getAttribute('error-text') } if (this.hasAttribute('type')) { if (this.getAttribute('type') === 'number') { this.input.setAttribute('inputmode', 'numeric') this.input.setAttribute('type', 'number') - } - else + this.isNumeric = true + } 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() + this.checkInput(e) }) this.clearBtn.addEventListener('click', e => { this.value = '' @@ -464,9 +551,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); + } } } }) @@ -475,145 +563,119 @@ customElements.define('sm-input', const smTextarea = document.createElement('template') smTextarea.innerHTML = ` -