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)) this.inputParent = this.shadowRoot.querySelector('.input') this.input = this.shadowRoot.querySelector('input') this.clearBtn = this.shadowRoot.querySelector('.clear') this.label = this.shadowRoot.querySelector('.label') this.feedbackText = this.shadowRoot.querySelector('.feedback-text') this.outerContainer = this.shadowRoot.querySelector('.outer-container') this._helperText this._errorText this.isRequired = false this.validationFunction this.reflectedAttributes = ['value', 'required', 'disabled', 'type', 'inputmode', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step'] this.reset = this.reset.bind(this) this.focusIn = this.focusIn.bind(this) this.focusOut = this.focusOut.bind(this) this.fireEvent = this.fireEvent.bind(this) this.checkInput = this.checkInput.bind(this) this.vibrate = this.vibrate.bind(this) } static get observedAttributes() { return ['value', 'placeholder', 'required', 'disabled', 'type', 'inputmode', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step', 'helper-text', 'error-text'] } get value() { return this.input.value } set value(val) { this.input.value = val; this.checkInput() this.fireEvent() } get placeholder() { return this.getAttribute('placeholder') } set placeholder(val) { this.setAttribute('placeholder', val) } get type() { return this.getAttribute('type') } set type(val) { this.setAttribute('type', val) } get validity() { return this.input.validity } set disabled(value) { if (value) this.inputParent.classList.add('disabled') else this.inputParent.classList.remove('disabled') } set readOnly(value) { if (value) { this.setAttribute('readonly', '') } else { this.removeAttribute('readonly') } } set customValidation(val) { this.validationFunction = val } set errorText(val) { this._errorText = val } set helperText(val) { this._helperText = val } get isValid() { if (this.input.value !== '') { const _isValid = this.input.checkValidity() let _customValid = true if (this.validationFunction) { _customValid = Boolean(this.validationFunction(this.input.value)) } if (_isValid && _customValid) { this.feedbackText.classList.remove('error') this.feedbackText.classList.add('success') this.feedbackText.textContent = '' } else { if (this._errorText) { this.feedbackText.classList.add('error') this.feedbackText.classList.remove('success') this.feedbackText.innerHTML = ` ${this._errorText} ` } } return (_isValid && _customValid) } } reset(){ this.value = '' } focusIn(){ this.input.focus() } focusOut(){ this.input.blur() } fireEvent(){ let event = new Event('input', { bubbles: true, cancelable: true, composed: true }); this.dispatchEvent(event); } checkInput(e){ if (!this.hasAttribute('readonly')) { if (this.input.value.trim() !== '') { this.clearBtn.classList.remove('hide') } else { this.clearBtn.classList.add('hide') if (this.isRequired) { this.feedbackText.textContent = '* required' } } } if (!this.hasAttribute('placeholder') || this.getAttribute('placeholder').trim() === '') return; if (this.input.value !== '') { if (this.animate) this.inputParent.classList.add('animate-label') else this.label.classList.add('hide') } else { if (this.animate) this.inputParent.classList.remove('animate-label') else this.label.classList.remove('hide') } } vibrate() { this.outerContainer.animate([ { transform: 'translateX(-1rem)' }, { transform: 'translateX(1rem)' }, { transform: 'translateX(-0.5rem)' }, { transform: 'translateX(0.5rem)' }, { transform: 'translateX(0)' }, ], { duration: 300, easing: 'ease' }) } connectedCallback() { this.animate = this.hasAttribute('animate') this.setAttribute('role', 'textbox') this.input.addEventListener('input', this.checkInput) this.clearBtn.addEventListener('click', this.reset) } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { if (this.reflectedAttributes.includes(name)) { if (this.hasAttribute(name)) { this.input.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : '') } else { this.input.removeAttribute(name) } } if (name === 'placeholder') { this.label.textContent = newValue; this.setAttribute('aria-label', newValue); } else if (this.hasAttribute('value')) { this.checkInput() } else if (name === 'type') { if (this.hasAttribute('type') && this.getAttribute('type') === 'number') { this.input.setAttribute('inputmode', 'numeric') } } else if (name === 'helper-text') { this._helperText = this.getAttribute('helper-text') } else if (name === 'error-text') { this._errorText = this.getAttribute('error-text') } else if (name === 'required') { this.isRequired = this.hasAttribute('required') if (this.isRequired) { this.feedbackText.textContent = '* required' this.setAttribute('aria-required', 'true') } else { this.feedbackText.textContent = '' this.setAttribute('aria-required', 'false') } } else if (name === 'readonly') { if (this.hasAttribute('readonly')) { this.inputParent.classList.add('readonly') } else { this.inputParent.classList.remove('readonly') } } else if (name === 'disabled') { if (this.hasAttribute('disabled')) { this.inputParent.classList.add('disabled') } else { this.inputParent.classList.remove('disabled') } } } } disconnectedCallback() { this.input.removeEventListener('input', this.checkInput) this.clearBtn.removeEventListener('click', this.reset) } })