diff --git a/components/dist/button.js b/components/dist/button.js index 4b50666..3926952 100644 --- a/components/dist/button.js +++ b/components/dist/button.js @@ -73,11 +73,13 @@ smButton.innerHTML = ` background-color: rgba(var(--text-color), 0.3); } @media (hover: hover){ - :host(:not([disabled])) .button:hover{ + :host(:not([disabled])) .button:hover, + :host(:focus-within:not([disabled])) .button{ -webkit-box-shadow: 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.8rem rgba(0, 0, 0, 0.12); - box-shadow: 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.8rem rgba(0, 0, 0, 0.12); + box-shadow: 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.8rem rgba(0, 0, 0, 0.12); } - :host([variant='outlined']) .button:hover{ + :host([variant='outlined']:not([disabled])) .button:hover, + :host(:focus-within[variant='outlined']:not([disabled])) .button:hover{ -webkit-box-shadow: 0 0 0 1px rgba(var(--text-color), 0.2) inset, 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1), 0 0.4rem 0.8rem rgba(0, 0, 0, 0.12); box-shadow: 0 0 0 1px rgba(var(--text-color), 0.2) inset, 0 0.1rem 0.1rem rgba(0, 0, 0, 0.1), 0 0.4rem 0.8rem rgba(0, 0, 0, 0.12); } @@ -99,49 +101,52 @@ smButton.innerHTML = ` customElements.define('sm-button', class extends HTMLElement { constructor() { - super() + super(); this.attachShadow({ mode: 'open' - }).append(smButton.content.cloneNode(true)) + }).append(smButton.content.cloneNode(true)); } static get observedAttributes() { return ['disabled']; } get disabled() { - return this.hasAttribute('disabled') + return this.hasAttribute('disabled'); } set disabled(value) { if (value) { - this.setAttribute('disabled', '') - }else { - this.removeAttribute('disabled') + this.setAttribute('disabled', ''); + } else { + this.removeAttribute('disabled'); } } + focusIn() { + this.focus(); + } handleKeyDown(e) { if (!this.hasAttribute('disabled') && (e.key === 'Enter' || e.code === 'Space')) { - e.preventDefault() - this.click() + e.preventDefault(); + this.click(); } } connectedCallback() { if (!this.hasAttribute('disabled')) { - this.setAttribute('tabindex', '0') + this.setAttribute('tabindex', '0'); } - this.setAttribute('role', 'button') - this.addEventListener('keydown', this.handleKeyDown) + this.setAttribute('role', 'button'); + this.addEventListener('keydown', this.handleKeyDown); } - attributeChangedCallback(name, oldVal, newVal) { + attributeChangedCallback(name) { if (name === 'disabled') { - this.removeAttribute('tabindex') - this.setAttribute('aria-disabled', 'true') - } - else { - this.setAttribute('tabindex', '0') - this.setAttribute('aria-disabled', 'false') + if (this.hasAttribute('disabled')) { + this.removeAttribute('tabindex'); + } else { + this.setAttribute('tabindex', '0'); + } + this.setAttribute('aria-disabled', this.hasAttribute('disabled')); } } }) \ No newline at end of file diff --git a/components/dist/button.min.js b/components/dist/button.min.js index 833b4b9..96fd0e7 100644 --- a/components/dist/button.min.js +++ b/components/dist/button.min.js @@ -1 +1 @@ -const smButton=document.createElement("template");smButton.innerHTML="\n\n
\n \n
",customElements.define("sm-button",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smButton.content.cloneNode(!0))}static get observedAttributes(){return["disabled"]}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}handleKeyDown(e){this.hasAttribute("disabled")||"Enter"!==e.key&&"Space"!==e.code||(e.preventDefault(),this.click())}connectedCallback(){this.hasAttribute("disabled")||this.setAttribute("tabindex","0"),this.setAttribute("role","button"),this.addEventListener("keydown",this.handleKeyDown)}attributeChangedCallback(e,n,t){"disabled"===e?(this.removeAttribute("tabindex"),this.setAttribute("aria-disabled","true")):(this.setAttribute("tabindex","0"),this.setAttribute("aria-disabled","false"))}}); \ No newline at end of file +const smButton=document.createElement("template");smButton.innerHTML="\n\n
\n \n
",customElements.define("sm-button",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smButton.content.cloneNode(!0))}static get observedAttributes(){return["disabled"]}get disabled(){return this.hasAttribute("disabled")}set disabled(t){t?this.setAttribute("disabled",""):this.removeAttribute("disabled")}focusIn(){this.focus()}handleKeyDown(t){this.hasAttribute("disabled")||"Enter"!==t.key&&"Space"!==t.code||(t.preventDefault(),this.click())}connectedCallback(){this.hasAttribute("disabled")||this.setAttribute("tabindex","0"),this.setAttribute("role","button"),this.addEventListener("keydown",this.handleKeyDown)}attributeChangedCallback(t){"disabled"===t&&(this.hasAttribute("disabled")?this.removeAttribute("tabindex"):this.setAttribute("tabindex","0"),this.setAttribute("aria-disabled",this.hasAttribute("disabled")))}}); \ No newline at end of file diff --git a/components/dist/checkbox.js b/components/dist/checkbox.js index 2dbb280..8b8a169 100644 --- a/components/dist/checkbox.js +++ b/components/dist/checkbox.js @@ -95,6 +95,7 @@ customElements.define('sm-checkbox', class extends HTMLElement { mode: 'open' }).append(smCheckbox.content.cloneNode(true)) + this.defaultState this.checkbox = this.shadowRoot.querySelector('.checkbox'); this.reset = this.reset.bind(this) @@ -140,31 +141,36 @@ customElements.define('sm-checkbox', class extends HTMLElement { return this.getAttribute('value') } - reset() { - this.removeAttribute('checked') + focusIn() { + this.focus() } - dispatch(){ + reset() { + this.value = this.defaultState + } + + dispatch() { this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })) } - handleKeyDown(e){ + handleKeyDown(e) { if (e.code === "Space") { e.preventDefault() this.click() } } - handleClick(e){ + handleClick(e) { this.toggleAttribute('checked') } - + connectedCallback() { if (!this.hasAttribute('disabled')) { this.setAttribute('tabindex', '0') } this.setAttribute('role', 'checkbox') + this.defaultState = this.hasAttribute('checked') if (!this.hasAttribute('checked')) { this.setAttribute('aria-checked', 'false') } diff --git a/components/dist/checkbox.min.js b/components/dist/checkbox.min.js index 9c742cb..5a70cd2 100644 --- a/components/dist/checkbox.min.js +++ b/components/dist/checkbox.min.js @@ -1 +1 @@ -const smCheckbox=document.createElement("template");smCheckbox.innerHTML='\n\n',customElements.define("sm-checkbox",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smCheckbox.content.cloneNode(!0)),this.checkbox=this.shadowRoot.querySelector(".checkbox"),this.reset=this.reset.bind(this),this.dispatch=this.dispatch.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleClick=this.handleClick.bind(this)}static get observedAttributes(){return["value","disabled","checked"]}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}get checked(){return this.hasAttribute("checked")}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}set value(e){this.setAttribute("value",e)}get value(){return this.getAttribute("value")}reset(){this.removeAttribute("checked")}dispatch(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0}))}handleKeyDown(e){"Space"===e.code&&(e.preventDefault(),this.click())}handleClick(e){this.toggleAttribute("checked")}connectedCallback(){this.hasAttribute("disabled")||this.setAttribute("tabindex","0"),this.setAttribute("role","checkbox"),this.hasAttribute("checked")||this.setAttribute("aria-checked","false"),this.addEventListener("keydown",this.handleKeyDown),this.addEventListener("click",this.handleClick)}attributeChangedCallback(e,t,n){t!==n&&("checked"===e?(this.setAttribute("aria-checked",this.hasAttribute("checked")),this.dispatch()):"disabled"===e&&(this.hasAttribute("disabled")?this.removeAttribute("tabindex"):this.setAttribute("tabindex","0")))}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown),this.removeEventListener("change",this.handleClick)}}); \ No newline at end of file +const smCheckbox=document.createElement("template");smCheckbox.innerHTML='\n\n',customElements.define("sm-checkbox",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smCheckbox.content.cloneNode(!0)),this.defaultState,this.checkbox=this.shadowRoot.querySelector(".checkbox"),this.reset=this.reset.bind(this),this.dispatch=this.dispatch.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleClick=this.handleClick.bind(this)}static get observedAttributes(){return["value","disabled","checked"]}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}get checked(){return this.hasAttribute("checked")}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}set value(e){this.setAttribute("value",e)}get value(){return this.getAttribute("value")}focusIn(){this.focus()}reset(){this.value=this.defaultState}dispatch(){this.dispatchEvent(new CustomEvent("change",{bubbles:!0,composed:!0}))}handleKeyDown(e){"Space"===e.code&&(e.preventDefault(),this.click())}handleClick(e){this.toggleAttribute("checked")}connectedCallback(){this.hasAttribute("disabled")||this.setAttribute("tabindex","0"),this.setAttribute("role","checkbox"),this.defaultState=this.hasAttribute("checked"),this.hasAttribute("checked")||this.setAttribute("aria-checked","false"),this.addEventListener("keydown",this.handleKeyDown),this.addEventListener("click",this.handleClick)}attributeChangedCallback(e,t,n){t!==n&&("checked"===e?(this.setAttribute("aria-checked",this.hasAttribute("checked")),this.dispatch()):"disabled"===e&&(this.hasAttribute("disabled")?this.removeAttribute("tabindex"):this.setAttribute("tabindex","0")))}disconnectedCallback(){this.removeEventListener("keydown",this.handleKeyDown),this.removeEventListener("change",this.handleClick)}}); \ No newline at end of file diff --git a/components/dist/copy.js b/components/dist/copy.js index 698d4a1..711aae5 100644 --- a/components/dist/copy.js +++ b/components/dist/copy.js @@ -1,4 +1,4 @@ -const smCopy = document.createElement('template') +const smCopy = document.createElement('template'); smCopy.innerHTML = ` -

\n
\n',customElements.define("sm-copy",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smCopy.content.cloneNode(!0)),this.copyContent=this.shadowRoot.querySelector(".copy-content"),this.copyButton=this.shadowRoot.querySelector(".copy-button"),this.copy=this.copy.bind(this)}static get observedAttributes(){return["value"]}set value(n){this.setAttribute("value",n)}get value(){return this.getAttribute("value")}fireEvent(){this.dispatchEvent(new CustomEvent("copy",{composed:!0,bubbles:!0,cancelable:!0}))}copy(){navigator.clipboard.writeText(this.copyContent.textContent).then(n=>this.fireEvent()).catch(n=>console.error(n))}connectedCallback(){this.copyButton.addEventListener("click",this.copy)}attributeChangedCallback(n,t,o){"value"===n&&(this.copyContent.textContent=o)}disconnectedCallback(){this.copyButton.removeEventListener("click",this.copy)}}); \ No newline at end of file +const smCopy=document.createElement("template");smCopy.innerHTML='\n\n
\n

\n \n
\n',customElements.define("sm-copy",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smCopy.content.cloneNode(!0)),this.copyContent=this.shadowRoot.querySelector(".copy-content"),this.copyButton=this.shadowRoot.querySelector(".copy-button"),this.copy=this.copy.bind(this)}static get observedAttributes(){return["value"]}set value(n){this.setAttribute("value",n)}get value(){return this.getAttribute("value")}fireEvent(){this.dispatchEvent(new CustomEvent("copy",{composed:!0,bubbles:!0,cancelable:!0}))}copy(){navigator.clipboard.writeText(this.copyContent.textContent).then(n=>this.fireEvent()).catch(n=>console.error(n))}connectedCallback(){this.copyButton.addEventListener("click",this.copy)}attributeChangedCallback(n,t,o){"value"===n&&(this.copyContent.textContent=o)}disconnectedCallback(){this.copyButton.removeEventListener("click",this.copy)}}); \ No newline at end of file diff --git a/components/dist/form.js b/components/dist/form.js index 248f976..616622b 100644 --- a/components/dist/form.js +++ b/components/dist/form.js @@ -1,4 +1,4 @@ -const smForm = document.createElement('template') +const smForm = document.createElement('template'); smForm.innerHTML = ` -
+
-` +`; customElements.define('sm-form', class extends HTMLElement { constructor() { @@ -29,17 +28,18 @@ customElements.define('sm-form', class extends HTMLElement { mode: 'open' }).append(smForm.content.cloneNode(true)) - this.form = this.shadowRoot.querySelector('form') + this.form = this.shadowRoot.querySelector('form'); this.formElements this.requiredElements this.submitButton this.resetButton - this.allRequiredValid = false + this.allRequiredValid = false; this.debounce = this.debounce.bind(this) - this.handleInput = this.handleInput.bind(this) + this._checkValidity = this._checkValidity.bind(this) this.handleKeydown = this.handleKeydown.bind(this) this.reset = this.reset.bind(this) + this.elementsChanged = this.elementsChanged.bind(this) } debounce(callback, wait) { let timeoutId = null; @@ -50,7 +50,7 @@ customElements.define('sm-form', class extends HTMLElement { }, wait); }; } - handleInput(e) { + _checkValidity() { this.allRequiredValid = this.requiredElements.every(elem => elem.isValid) if (!this.submitButton) return; if (this.allRequiredValid) { @@ -61,34 +61,42 @@ customElements.define('sm-form', class extends HTMLElement { } } handleKeydown(e) { - if (e.key === 'Enter' && e.target.tagName !== 'SM-TEXTAREA' ) { + if (e.key === 'Enter' && e.target.tagName !== 'SM-TEXTAREA') { if (this.allRequiredValid) { - this.submitButton.click() + if (this.submitButton && this.submitButton.tagName === 'SM-BUTTON') { + this.submitButton.click() + } + this.dispatchEvent(new CustomEvent('submit', { + bubbles: true, + composed: true, + })) } else { - this.requiredElements.find(elem => !elem.isValid).vibrate() + this.requiredElements.find(elem => !elem.isValid).vibrate() } } } reset() { this.formElements.forEach(elem => elem.reset()) } + elementsChanged() { + this.formElements = [...this.querySelectorAll('sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')] + this.requiredElements = this.formElements.filter(elem => elem.hasAttribute('required')); + this.submitButton = this.querySelector('[variant="primary"], [type="submit"]'); + this.resetButton = this.querySelector('[type="reset"]'); + if (this.resetButton) { + this.resetButton.addEventListener('click', this.reset); + } + this._checkValidity() + } connectedCallback() { const slot = this.shadowRoot.querySelector('slot') - slot.addEventListener('slotchange', e => { - this.formElements = [...this.querySelectorAll('sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')] - this.requiredElements = this.formElements.filter(elem => elem.hasAttribute('required')) - this.submitButton = e.target.assignedElements().find(elem => elem.getAttribute('variant') === 'primary' || elem.getAttribute('type') === 'submit'); - this.resetButton = e.target.assignedElements().find(elem => elem.getAttribute('type') === 'reset'); - if (this.resetButton) { - this.resetButton.addEventListener('click', this.reset) - } - }) - this.addEventListener('input', this.debounce(this.handleInput, 100)) - this.addEventListener('keydown', this.debounce(this.handleKeydown, 100)) + slot.addEventListener('slotchange', this.elementsChanged) + this.addEventListener('input', this.debounce(this._checkValidity, 100)); + this.addEventListener('keydown', this.debounce(this.handleKeydown, 100)); } disconnectedCallback() { - this.removeEventListener('input', this.debounce(this.handleInput, 100)) - this.removeEventListener('keydown', this.debounce(this.handleKeydown, 100)) + this.removeEventListener('input', this.debounce(this._checkValidity, 100)); + this.removeEventListener('keydown', this.debounce(this.handleKeydown, 100)); } -}) +}) \ No newline at end of file diff --git a/components/dist/form.min.js b/components/dist/form.min.js index 3ddfb65..8621611 100644 --- a/components/dist/form.min.js +++ b/components/dist/form.min.js @@ -1 +1 @@ -const smForm=document.createElement("template");smForm.innerHTML='\n \n\t
\n\t\t\n\t
\n',customElements.define("sm-form",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smForm.content.cloneNode(!0)),this.form=this.shadowRoot.querySelector("form"),this.formElements,this.requiredElements,this.submitButton,this.resetButton,this.allRequiredValid=!1,this.debounce=this.debounce.bind(this),this.handleInput=this.handleInput.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.reset=this.reset.bind(this)}debounce(t,e){let s=null;return(...n)=>{window.clearTimeout(s),s=window.setTimeout(()=>{t.apply(null,n)},e)}}handleInput(t){this.allRequiredValid=this.requiredElements.every(t=>t.isValid),this.submitButton&&(this.allRequiredValid?this.submitButton.disabled=!1:this.submitButton.disabled=!0)}handleKeydown(t){"Enter"===t.key&&"SM-TEXTAREA"!==t.target.tagName&&(this.allRequiredValid?this.submitButton.click():this.requiredElements.find(t=>!t.isValid).vibrate())}reset(){this.formElements.forEach(t=>t.reset())}connectedCallback(){const t=this.shadowRoot.querySelector("slot");t.addEventListener("slotchange",t=>{this.formElements=[...this.querySelectorAll("sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio")],this.requiredElements=this.formElements.filter(t=>t.hasAttribute("required")),this.submitButton=t.target.assignedElements().find(t=>"primary"===t.getAttribute("variant")||"submit"===t.getAttribute("type")),this.resetButton=t.target.assignedElements().find(t=>"reset"===t.getAttribute("type")),this.resetButton&&this.resetButton.addEventListener("click",this.reset)}),this.addEventListener("input",this.debounce(this.handleInput,100)),this.addEventListener("keydown",this.debounce(this.handleKeydown,100))}disconnectedCallback(){this.removeEventListener("input",this.debounce(this.handleInput,100)),this.removeEventListener("keydown",this.debounce(this.handleKeydown,100))}}); \ No newline at end of file +const smForm=document.createElement("template");smForm.innerHTML='\n \n\t
\n\t\t\n\t
\n',customElements.define("sm-form",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smForm.content.cloneNode(!0)),this.form=this.shadowRoot.querySelector("form"),this.formElements,this.requiredElements,this.submitButton,this.resetButton,this.allRequiredValid=!1,this.debounce=this.debounce.bind(this),this._checkValidity=this._checkValidity.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.reset=this.reset.bind(this),this.elementsChanged=this.elementsChanged.bind(this)}debounce(t,e){let i=null;return(...s)=>{window.clearTimeout(i),i=window.setTimeout(()=>{t.apply(null,s)},e)}}_checkValidity(){this.allRequiredValid=this.requiredElements.every(t=>t.isValid),this.submitButton&&(this.allRequiredValid?this.submitButton.disabled=!1:this.submitButton.disabled=!0)}handleKeydown(t){"Enter"===t.key&&"SM-TEXTAREA"!==t.target.tagName&&(this.allRequiredValid?(this.submitButton&&"SM-BUTTON"===this.submitButton.tagName&&this.submitButton.click(),this.dispatchEvent(new CustomEvent("submit",{bubbles:!0,composed:!0}))):this.requiredElements.find(t=>!t.isValid).vibrate())}reset(){this.formElements.forEach(t=>t.reset())}elementsChanged(){this.formElements=[...this.querySelectorAll("sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio")],this.requiredElements=this.formElements.filter(t=>t.hasAttribute("required")),this.submitButton=this.querySelector('[variant="primary"], [type="submit"]'),this.resetButton=this.querySelector('[type="reset"]'),this.resetButton&&this.resetButton.addEventListener("click",this.reset),this._checkValidity()}connectedCallback(){const t=this.shadowRoot.querySelector("slot");t.addEventListener("slotchange",this.elementsChanged),this.addEventListener("input",this.debounce(this._checkValidity,100)),this.addEventListener("keydown",this.debounce(this.handleKeydown,100))}disconnectedCallback(){this.removeEventListener("input",this.debounce(this._checkValidity,100)),this.removeEventListener("keydown",this.debounce(this.handleKeydown,100))}}); \ No newline at end of file diff --git a/components/dist/input.js b/components/dist/input.js index de087be..43cfa4d 100644 --- a/components/dist/input.js +++ b/components/dist/input.js @@ -43,7 +43,6 @@ border: none; --success-color: #00C853; --danger-color: red; --width: 100%; - --font-size: 1rem; --icon-gap: 0.5rem; --border-radius: 0.3rem; --padding: 0.7rem 1rem; @@ -105,9 +104,9 @@ border: none; opacity: 0.6; } .label { + font-size: inherit; opacity: .7; font-weight: 400; - font-size: var(--font-size); position: absolute; top: 0; -webkit-transition: -webkit-transform 0.3s; @@ -145,7 +144,7 @@ border: none; flex: 1; } input{ - font-size: var(--font-size); + font-size: inherit; border: none; background: transparent; outline: none; @@ -166,7 +165,7 @@ input{ color: var(--accent-color) } :host([variant="outlined"]) .input { - box-shadow: 0 0 0 0.1rem rgba(var(--text-color), 0.4) inset; + box-shadow: 0 0 0 0.1rem var(--border-color, rgba(var(--text-color), 0.4)) inset; background: rgba(var(--background-color), 1); } :host([variant="outlined"]) .label { @@ -230,125 +229,131 @@ customElements.define('sm-input', class extends HTMLElement { constructor() { - super() + super(); this.attachShadow({ mode: 'open' - }).append(smInput.content.cloneNode(true)) + }).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) + 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.hideRequired = false; + this.validationFunction = undefined; + 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'] + return ['value', 'placeholder', 'required', 'disabled', 'type', 'inputmode', 'readonly', 'min', 'max', 'pattern', 'minlength', 'maxlength', 'step', 'helper-text', 'error-text', 'hiderequired']; } get value() { - return this.input.value + return this.input.value; } set value(val) { this.input.value = val; - this.checkInput() - this.fireEvent() + this.checkInput(); + this.fireEvent(); } get placeholder() { - return this.getAttribute('placeholder') + return this.getAttribute('placeholder'); } set placeholder(val) { - this.setAttribute('placeholder', val) + this.setAttribute('placeholder', val); } get type() { - return this.getAttribute('type') + return this.getAttribute('type'); } set type(val) { - this.setAttribute('type', val) + this.setAttribute('type', val); } get validity() { - return this.input.validity + return this.input.validity; } + get disabled() { + return this.hasAttribute('disabled'); + } set disabled(value) { if (value) - this.inputParent.classList.add('disabled') + this.inputParent.classList.add('disabled'); else - this.inputParent.classList.remove('disabled') + this.inputParent.classList.remove('disabled'); + } + get readOnly() { + return this.hasAttribute('readonly'); } set readOnly(value) { if (value) { - this.setAttribute('readonly', '') + this.setAttribute('readonly', ''); } else { - this.removeAttribute('readonly') + this.removeAttribute('readonly'); } } set customValidation(val) { - - this.validationFunction = val + this.validationFunction = val; } set errorText(val) { - this._errorText = val + this._errorText = val; } set helperText(val) { - this._helperText = val + this._helperText = val; } get isValid() { if (this.input.value !== '') { - const _isValid = this.input.checkValidity() - let _customValid = true + const _isValid = this.input.checkValidity(); + let _customValid = true; if (this.validationFunction) { - _customValid = Boolean(this.validationFunction(this.input.value)) + _customValid = Boolean(this.validationFunction(this.input.value)); } if (_isValid && _customValid) { - this.feedbackText.classList.remove('error') - this.feedbackText.classList.add('success') - this.feedbackText.textContent = '' + 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.classList.add('error'); + this.feedbackText.classList.remove('success'); this.feedbackText.innerHTML = ` ${this._errorText} - ` + `; } } - return (_isValid && _customValid) + return (_isValid && _customValid); } } - reset(){ - this.value = '' + reset() { + this.value = ''; } - focusIn(){ - this.input.focus() + focusIn() { + this.input.focus(); } - focusOut(){ - this.input.blur() + focusOut() { + this.input.blur(); } - fireEvent(){ + fireEvent() { let event = new Event('input', { bubbles: true, cancelable: true, @@ -357,28 +362,28 @@ customElements.define('sm-input', this.dispatchEvent(event); } - checkInput(e){ + checkInput(e) { if (!this.hasAttribute('readonly')) { if (this.input.value.trim() !== '') { - this.clearBtn.classList.remove('hide') + this.clearBtn.classList.remove('hide'); } else { - this.clearBtn.classList.add('hide') - if (this.isRequired) { - this.feedbackText.textContent = '* required' + this.clearBtn.classList.add('hide'); + if (this.isRequired && !this.hideRequired) { + 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') + this.inputParent.classList.add('animate-label'); else - this.label.classList.add('hide') + this.label.classList.add('hide'); } else { if (this.animate) - this.inputParent.classList.remove('animate-label') + this.inputParent.classList.remove('animate-label'); else - this.label.classList.remove('hide') + this.label.classList.remove('hide'); } } vibrate() { @@ -391,25 +396,25 @@ customElements.define('sm-input', ], { 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) + 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) : '') + this.input.setAttribute(name, this.getAttribute(name) ? this.getAttribute(name) : ''); } else { - this.input.removeAttribute(name) + this.input.removeAttribute(name); } } if (name === 'placeholder') { @@ -417,49 +422,55 @@ customElements.define('sm-input', this.setAttribute('aria-label', newValue); } else if (this.hasAttribute('value')) { - this.checkInput() + this.checkInput(); } else if (name === 'type') { if (this.hasAttribute('type') && this.getAttribute('type') === 'number') { - this.input.setAttribute('inputmode', 'numeric') + this.input.setAttribute('inputmode', 'numeric'); } } else if (name === 'helper-text') { - this._helperText = this.getAttribute('helper-text') + this._helperText = this.getAttribute('helper-text'); } else if (name === 'error-text') { - this._errorText = this.getAttribute('error-text') + this._errorText = this.getAttribute('error-text'); } else if (name === 'required') { - this.isRequired = this.hasAttribute('required') + this.isRequired = this.hasAttribute('required'); + if (this.isRequired && !this.hideRequired) { + this.feedbackText.textContent = ''; + } else { + this.feedbackText.textContent = '*required'; + } if (this.isRequired) { - this.feedbackText.textContent = '* required' - this.setAttribute('aria-required', 'true') + this.setAttribute('aria-required', 'true'); } else { - this.feedbackText.textContent = '' - this.setAttribute('aria-required', 'false') + this.setAttribute('aria-required', 'false'); } } + else if (name === 'hiderequired') { + this.hideRequired = this.hasAttribute('hiderequired') + } else if (name === 'readonly') { if (this.hasAttribute('readonly')) { - this.inputParent.classList.add('readonly') + this.inputParent.classList.add('readonly'); } else { - this.inputParent.classList.remove('readonly') + this.inputParent.classList.remove('readonly'); } } else if (name === 'disabled') { if (this.hasAttribute('disabled')) { - this.inputParent.classList.add('disabled') + this.inputParent.classList.add('disabled'); } else { - this.inputParent.classList.remove('disabled') + this.inputParent.classList.remove('disabled'); } } } } disconnectedCallback() { - this.input.removeEventListener('input', this.checkInput) - this.clearBtn.removeEventListener('click', this.reset) + this.input.removeEventListener('input', this.checkInput); + this.clearBtn.removeEventListener('click', this.reset); } }) \ No newline at end of file diff --git a/components/dist/input.min.js b/components/dist/input.min.js index 04f32a9..2afa3ae 100644 --- a/components/dist/input.min.js +++ b/components/dist/input.min.js @@ -1 +1 @@ -const smInput=document.createElement("template");smInput.innerHTML='\n\n
\n \n

\n
\n',customElements.define("sm-input",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smInput.content.cloneNode(!0)),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=!1,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(t){this.input.value=t,this.checkInput(),this.fireEvent()}get placeholder(){return this.getAttribute("placeholder")}set placeholder(t){this.setAttribute("placeholder",t)}get type(){return this.getAttribute("type")}set type(t){this.setAttribute("type",t)}get validity(){return this.input.validity}set disabled(t){t?this.inputParent.classList.add("disabled"):this.inputParent.classList.remove("disabled")}set readOnly(t){t?this.setAttribute("readonly",""):this.removeAttribute("readonly")}set customValidation(t){this.validationFunction=t}set errorText(t){this._errorText=t}set helperText(t){this._helperText=t}get isValid(){if(""!==this.input.value){const t=this.input.checkValidity();let e=!0;return this.validationFunction&&(e=Boolean(this.validationFunction(this.input.value))),t&&e?(this.feedbackText.classList.remove("error"),this.feedbackText.classList.add("success"),this.feedbackText.textContent=""):this._errorText&&(this.feedbackText.classList.add("error"),this.feedbackText.classList.remove("success"),this.feedbackText.innerHTML=`\n \n ${this._errorText}\n `),t&&e}}reset(){this.value=""}focusIn(){this.input.focus()}focusOut(){this.input.blur()}fireEvent(){let t=new Event("input",{bubbles:!0,cancelable:!0,composed:!0});this.dispatchEvent(t)}checkInput(t){this.hasAttribute("readonly")||(""!==this.input.value.trim()?this.clearBtn.classList.remove("hide"):(this.clearBtn.classList.add("hide"),this.isRequired&&(this.feedbackText.textContent="* required"))),this.hasAttribute("placeholder")&&""!==this.getAttribute("placeholder").trim()&&(""!==this.input.value?this.animate?this.inputParent.classList.add("animate-label"):this.label.classList.add("hide"):this.animate?this.inputParent.classList.remove("animate-label"):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(t,e,n){e!==n&&(this.reflectedAttributes.includes(t)&&(this.hasAttribute(t)?this.input.setAttribute(t,this.getAttribute(t)?this.getAttribute(t):""):this.input.removeAttribute(t)),"placeholder"===t?(this.label.textContent=n,this.setAttribute("aria-label",n)):this.hasAttribute("value")?this.checkInput():"type"===t?this.hasAttribute("type")&&"number"===this.getAttribute("type")&&this.input.setAttribute("inputmode","numeric"):"helper-text"===t?this._helperText=this.getAttribute("helper-text"):"error-text"===t?this._errorText=this.getAttribute("error-text"):"required"===t?(this.isRequired=this.hasAttribute("required"),this.isRequired?(this.feedbackText.textContent="* required",this.setAttribute("aria-required","true")):(this.feedbackText.textContent="",this.setAttribute("aria-required","false"))):"readonly"===t?this.hasAttribute("readonly")?this.inputParent.classList.add("readonly"):this.inputParent.classList.remove("readonly"):"disabled"===t&&(this.hasAttribute("disabled")?this.inputParent.classList.add("disabled"):this.inputParent.classList.remove("disabled")))}disconnectedCallback(){this.input.removeEventListener("input",this.checkInput),this.clearBtn.removeEventListener("click",this.reset)}}); \ No newline at end of file +const smInput=document.createElement("template");smInput.innerHTML='\n\n
\n \n

\n
\n',customElements.define("sm-input",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smInput.content.cloneNode(!0)),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=!1,this.hideRequired=!1,this.validationFunction=void 0,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","hiderequired"]}get value(){return this.input.value}set value(t){this.input.value=t,this.checkInput(),this.fireEvent()}get placeholder(){return this.getAttribute("placeholder")}set placeholder(t){this.setAttribute("placeholder",t)}get type(){return this.getAttribute("type")}set type(t){this.setAttribute("type",t)}get validity(){return this.input.validity}get disabled(){return this.hasAttribute("disabled")}set disabled(t){t?this.inputParent.classList.add("disabled"):this.inputParent.classList.remove("disabled")}get readOnly(){return this.hasAttribute("readonly")}set readOnly(t){t?this.setAttribute("readonly",""):this.removeAttribute("readonly")}set customValidation(t){this.validationFunction=t}set errorText(t){this._errorText=t}set helperText(t){this._helperText=t}get isValid(){if(""!==this.input.value){const t=this.input.checkValidity();let e=!0;return this.validationFunction&&(e=Boolean(this.validationFunction(this.input.value))),t&&e?(this.feedbackText.classList.remove("error"),this.feedbackText.classList.add("success"),this.feedbackText.textContent=""):this._errorText&&(this.feedbackText.classList.add("error"),this.feedbackText.classList.remove("success"),this.feedbackText.innerHTML=`\n \n ${this._errorText}\n `),t&&e}}reset(){this.value=""}focusIn(){this.input.focus()}focusOut(){this.input.blur()}fireEvent(){let t=new Event("input",{bubbles:!0,cancelable:!0,composed:!0});this.dispatchEvent(t)}checkInput(t){this.hasAttribute("readonly")||(""!==this.input.value.trim()?this.clearBtn.classList.remove("hide"):(this.clearBtn.classList.add("hide"),this.isRequired&&!this.hideRequired&&(this.feedbackText.textContent="*required"))),this.hasAttribute("placeholder")&&""!==this.getAttribute("placeholder").trim()&&(""!==this.input.value?this.animate?this.inputParent.classList.add("animate-label"):this.label.classList.add("hide"):this.animate?this.inputParent.classList.remove("animate-label"):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(t,e,n){e!==n&&(this.reflectedAttributes.includes(t)&&(this.hasAttribute(t)?this.input.setAttribute(t,this.getAttribute(t)?this.getAttribute(t):""):this.input.removeAttribute(t)),"placeholder"===t?(this.label.textContent=n,this.setAttribute("aria-label",n)):this.hasAttribute("value")?this.checkInput():"type"===t?this.hasAttribute("type")&&"number"===this.getAttribute("type")&&this.input.setAttribute("inputmode","numeric"):"helper-text"===t?this._helperText=this.getAttribute("helper-text"):"error-text"===t?this._errorText=this.getAttribute("error-text"):"required"===t?(this.isRequired=this.hasAttribute("required"),this.isRequired&&!this.hideRequired?this.feedbackText.textContent="":this.feedbackText.textContent="*required",this.isRequired?this.setAttribute("aria-required","true"):this.setAttribute("aria-required","false")):"hiderequired"===t?this.hideRequired=this.hasAttribute("hiderequired"):"readonly"===t?this.hasAttribute("readonly")?this.inputParent.classList.add("readonly"):this.inputParent.classList.remove("readonly"):"disabled"===t&&(this.hasAttribute("disabled")?this.inputParent.classList.add("disabled"):this.inputParent.classList.remove("disabled")))}disconnectedCallback(){this.input.removeEventListener("input",this.checkInput),this.clearBtn.removeEventListener("click",this.reset)}}); \ No newline at end of file diff --git a/components/dist/menu.js b/components/dist/menu.js index fdbdfc8..5013cd6 100644 --- a/components/dist/menu.js +++ b/components/dist/menu.js @@ -11,6 +11,7 @@ smMenu.innerHTML = ` display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; + } .menu{ display: -ms-grid; @@ -55,7 +56,8 @@ smMenu.innerHTML = ` right: 0; } .options{ - padding: 0.5rem 0; + top: 100%; + padding: 0.3rem; overflow: hidden auto; position: absolute; display: -webkit-box; @@ -68,8 +70,8 @@ smMenu.innerHTML = ` -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; - background: rgba(var(--background-color), 1); - border-radius: 0.3rem; + background: var(--background, rgba(var(--background-color), 1)); + border-radius: var(--border-radius, 0.5rem); z-index: 1; -webkit-box-shadow: 0 0.5rem 1.5rem -0.5rem rgba(0,0,0,0.3); box-shadow: 0 0.5rem 1.5rem -0.5rem rgba(0,0,0,0.3); @@ -96,7 +98,7 @@ smMenu.innerHTML = `
@@ -125,7 +127,7 @@ customElements.define('sm-menu', class extends HTMLElement { this.collapse = this.collapse.bind(this) this.toggle = this.toggle.bind(this) this.handleKeyDown = this.handleKeyDown.bind(this) - this.handleClickoutSide = this.handleClickoutSide.bind(this) + this.handleClickOutside = this.handleClickOutside.bind(this) } static get observedAttributes() { @@ -193,7 +195,7 @@ customElements.define('sm-menu', class extends HTMLElement { e.preventDefault() this.toggle() } - } else { // If mey is pressed over menu options + } else { // If key is pressed over menu options if (e.code === 'ArrowUp') { e.preventDefault() if (document.activeElement.previousElementSibling) { @@ -216,7 +218,7 @@ customElements.define('sm-menu', class extends HTMLElement { } } } - handleClickoutSide(e) { + handleClickOutside(e) { if (!this.contains(e.target) && e.button !== 2) { this.collapse() } @@ -231,12 +233,12 @@ customElements.define('sm-menu', class extends HTMLElement { }); this.addEventListener('click', this.toggle) this.addEventListener('keydown', this.handleKeyDown) - document.addEventListener('mousedown', this.handleClickoutSide) + document.addEventListener('mousedown', this.handleClickOutside) } disconnectedCallback() { this.removeEventListener('click', this.toggle) this.removeEventListener('keydown', this.handleKeyDown) - document.removeEventListener('mousedown', this.handleClickoutSide) + document.removeEventListener('mousedown', this.handleClickOutside) } }) @@ -254,20 +256,19 @@ menuOption.innerHTML = ` display: -webkit-box; display: -ms-flexbox; display: flex; - --padding: 0.6rem 1.6rem; } .option{ display: -webkit-box; display: -ms-flexbox; display: flex; min-width: 100%; - padding: var(--padding); + padding: var(--padding, 0.6rem 1rem); cursor: pointer; overflow-wrap: break-word; white-space: nowrap; outline: none; - font-size: 1rem; user-select: none; + border-radius: 0.3rem; -webkit-box-align: center; -ms-flex-align: center; align-items: center; @@ -277,8 +278,8 @@ menuOption.innerHTML = ` background: rgba(var(--text-color), 0.1); } @media (any-hover: hover){ - :host{ - --padding: 0.8rem 1.6rem; + .option{ + transition: background-color 0.2s; } .option:hover{ background: rgba(var(--text-color), 0.1); @@ -298,6 +299,7 @@ customElements.define('menu-option', class extends HTMLElement { connectedCallback() { this.setAttribute('role', 'option') + this.setAttribute('tabindex', '0') this.addEventListener('keyup', e => { if (e.code === 'Enter' || e.code === 'Space') { e.preventDefault() diff --git a/components/dist/menu.min.js b/components/dist/menu.min.js index 606b807..b4a8f88 100644 --- a/components/dist/menu.min.js +++ b/components/dist/menu.min.js @@ -1 +1 @@ -const smMenu=document.createElement("template");smMenu.innerHTML='\n\n
\n \n
\n \n
\n
',customElements.define("sm-menu",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smMenu.content.cloneNode(!0)),this.isOpen=!1,this.availableOptions,this.containerDimensions,this.animOptions={duration:200,easing:"ease"},this.optionList=this.shadowRoot.querySelector(".options"),this.menu=this.shadowRoot.querySelector(".menu"),this.icon=this.shadowRoot.querySelector(".icon"),this.expand=this.expand.bind(this),this.collapse=this.collapse.bind(this),this.toggle=this.toggle.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleClickoutSide=this.handleClickoutSide.bind(this)}static get observedAttributes(){return["value"]}get value(){return this.getAttribute("value")}set value(n){this.setAttribute("value",n)}expand(){this.isOpen||(this.optionList.classList.remove("hide"),this.optionList.animate([{transform:window.innerWidth<640?"translateY(1.5rem)":"translateY(-1rem)",opacity:"0"},{transform:"none",opacity:"1"}],this.animOptions).onfinish=(()=>{this.isOpen=!0,this.icon.classList.add("focused")}))}collapse(){this.isOpen&&(this.optionList.animate([{transform:"none",opacity:"1"},{transform:window.innerWidth<640?"translateY(1.5rem)":"translateY(-1rem)",opacity:"0"}],this.animOptions).onfinish=(()=>{this.isOpen=!1,this.icon.classList.remove("focused"),this.optionList.classList.add("hide")}))}toggle(){this.isOpen?this.collapse():this.expand()}handleKeyDown(n){n.target===this?"ArrowDown"===n.code?(n.preventDefault(),this.availableOptions[0].focus()):"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),this.toggle()):"ArrowUp"===n.code?(n.preventDefault(),document.activeElement.previousElementSibling?document.activeElement.previousElementSibling.focus():this.availableOptions[this.availableOptions.length-1].focus()):"ArrowDown"===n.code?(n.preventDefault(),document.activeElement.nextElementSibling?document.activeElement.nextElementSibling.focus():this.availableOptions[0].focus()):"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),n.target.click())}handleClickoutSide(n){this.contains(n.target)||2===n.button||this.collapse()}connectedCallback(){this.setAttribute("role","listbox"),this.setAttribute("aria-label","dropdown menu");const n=this.shadowRoot.querySelector(".options slot");n.addEventListener("slotchange",n=>{this.availableOptions=n.target.assignedElements(),this.containerDimensions=this.optionList.getBoundingClientRect()}),this.addEventListener("click",this.toggle),this.addEventListener("keydown",this.handleKeyDown),document.addEventListener("mousedown",this.handleClickoutSide)}disconnectedCallback(){this.removeEventListener("click",this.toggle),this.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("mousedown",this.handleClickoutSide)}});const menuOption=document.createElement("template");menuOption.innerHTML='\n\n
\n \n
',customElements.define("menu-option",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(menuOption.content.cloneNode(!0))}connectedCallback(){this.setAttribute("role","option"),this.addEventListener("keyup",n=>{"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),this.click())})}}); \ No newline at end of file +const smMenu=document.createElement("template");smMenu.innerHTML='\n\n
\n \n
\n \n
\n
',customElements.define("sm-menu",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(smMenu.content.cloneNode(!0)),this.isOpen=!1,this.availableOptions,this.containerDimensions,this.animOptions={duration:200,easing:"ease"},this.optionList=this.shadowRoot.querySelector(".options"),this.menu=this.shadowRoot.querySelector(".menu"),this.icon=this.shadowRoot.querySelector(".icon"),this.expand=this.expand.bind(this),this.collapse=this.collapse.bind(this),this.toggle=this.toggle.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleClickOutside=this.handleClickOutside.bind(this)}static get observedAttributes(){return["value"]}get value(){return this.getAttribute("value")}set value(n){this.setAttribute("value",n)}expand(){this.isOpen||(this.optionList.classList.remove("hide"),this.optionList.animate([{transform:window.innerWidth<640?"translateY(1.5rem)":"translateY(-1rem)",opacity:"0"},{transform:"none",opacity:"1"}],this.animOptions).onfinish=(()=>{this.isOpen=!0,this.icon.classList.add("focused")}))}collapse(){this.isOpen&&(this.optionList.animate([{transform:"none",opacity:"1"},{transform:window.innerWidth<640?"translateY(1.5rem)":"translateY(-1rem)",opacity:"0"}],this.animOptions).onfinish=(()=>{this.isOpen=!1,this.icon.classList.remove("focused"),this.optionList.classList.add("hide")}))}toggle(){this.isOpen?this.collapse():this.expand()}handleKeyDown(n){n.target===this?"ArrowDown"===n.code?(n.preventDefault(),this.availableOptions[0].focus()):"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),this.toggle()):"ArrowUp"===n.code?(n.preventDefault(),document.activeElement.previousElementSibling?document.activeElement.previousElementSibling.focus():this.availableOptions[this.availableOptions.length-1].focus()):"ArrowDown"===n.code?(n.preventDefault(),document.activeElement.nextElementSibling?document.activeElement.nextElementSibling.focus():this.availableOptions[0].focus()):"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),n.target.click())}handleClickOutside(n){this.contains(n.target)||2===n.button||this.collapse()}connectedCallback(){this.setAttribute("role","listbox"),this.setAttribute("aria-label","dropdown menu");const n=this.shadowRoot.querySelector(".options slot");n.addEventListener("slotchange",n=>{this.availableOptions=n.target.assignedElements(),this.containerDimensions=this.optionList.getBoundingClientRect()}),this.addEventListener("click",this.toggle),this.addEventListener("keydown",this.handleKeyDown),document.addEventListener("mousedown",this.handleClickOutside)}disconnectedCallback(){this.removeEventListener("click",this.toggle),this.removeEventListener("keydown",this.handleKeyDown),document.removeEventListener("mousedown",this.handleClickOutside)}});const menuOption=document.createElement("template");menuOption.innerHTML='\n\n
\n \n
',customElements.define("menu-option",class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"}).append(menuOption.content.cloneNode(!0))}connectedCallback(){this.setAttribute("role","option"),this.setAttribute("tabindex","0"),this.addEventListener("keyup",n=>{"Enter"!==n.code&&"Space"!==n.code||(n.preventDefault(),this.click())})}}); \ No newline at end of file diff --git a/components/dist/notifications.js b/components/dist/notifications.js index 6d34332..66537d0 100644 --- a/components/dist/notifications.js +++ b/components/dist/notifications.js @@ -96,6 +96,13 @@ smNotifications.innerHTML = ` width: 100%; fill: rgba(var(--text-color), 0.7); } + .icon--success { + fill: var(--green); + } + .icon--failure, + .icon--error { + fill: var(--danger-color); + } .close{ height: 2rem; width: 2rem; @@ -142,7 +149,7 @@ smNotifications.innerHTML = ` customElements.define('sm-notifications', class extends HTMLElement { constructor() { - super() + super(); this.shadow = this.attachShadow({ mode: 'open' }).append(smNotifications.content.cloneNode(true)) @@ -169,31 +176,31 @@ customElements.define('sm-notifications', class extends HTMLElement { return result; } - createNotification(message, options) { - const { pinned = false, icon = '' } = options + createNotification(message, options = {}) { + const { pinned = false, icon = '' } = options; const notification = document.createElement('div') notification.id = this.randString(8) - notification.classList.add('notification') - let composition = `` + notification.classList.add('notification'); + let composition = ``; composition += `
${icon}

${message}

- ` + `; if (pinned) { - notification.classList.add('pinned') + notification.classList.add('pinned'); composition += ` - ` + `; } - notification.innerHTML = composition - return notification + notification.innerHTML = composition; + return notification; } push(message, options = {}) { - const notification = this.createNotification(message, options) - this.notificationPanel.append(notification) + const notification = this.createNotification(message, options); + this.notificationPanel.append(notification); notification.animate([ { transform: `translateY(1rem)`, @@ -203,8 +210,8 @@ customElements.define('sm-notifications', class extends HTMLElement { transform: `none`, opacity: '1' }, - ], this.animationOptions) - return notification.id + ], this.animationOptions); + return notification.id; } removeNotification(notification) { @@ -218,36 +225,36 @@ customElements.define('sm-notifications', class extends HTMLElement { opacity: '0' } ], this.animationOptions).onfinish = () => { - notification.remove() - } + notification.remove(); + }; } clearAll() { Array.from(this.notificationPanel.children).forEach(child => { - this.removeNotification(child) - }) + this.removeNotification(child); + }); } connectedCallback() { this.notificationPanel.addEventListener('click', e => { - if (e.target.closest('.close')) ( - this.removeNotification(e.target.closest('.notification')) - ) - }) + if (e.target.closest('.close')) { + this.removeNotification(e.target.closest('.notification')); + } + }); const observer = new MutationObserver(mutationList => { mutationList.forEach(mutation => { if (mutation.type === 'childList') { if (mutation.addedNodes.length && !mutation.addedNodes[0].classList.contains('pinned')) { setTimeout(() => { - this.removeNotification(mutation.addedNodes[0]) + this.removeNotification(mutation.addedNodes[0]); }, 5000); } } - }) - }) + }); + }); observer.observe(this.notificationPanel, { childList: true, - }) + }); } -}) \ No newline at end of file +}); diff --git a/components/dist/notifications.min.js b/components/dist/notifications.min.js index 0971a8c..4c1c716 100644 --- a/components/dist/notifications.min.js +++ b/components/dist/notifications.min.js @@ -1 +1 @@ -const smNotifications=document.createElement("template");smNotifications.innerHTML='\n\n
\n',customElements.define("sm-notifications",class extends HTMLElement{constructor(){super(),this.shadow=this.attachShadow({mode:"open"}).append(smNotifications.content.cloneNode(!0)),this.notificationPanel=this.shadowRoot.querySelector(".notification-panel"),this.animationOptions={duration:300,fill:"forwards",easing:"cubic-bezier(0.175, 0.885, 0.32, 1.275)"},this.push=this.push.bind(this),this.createNotification=this.createNotification.bind(this),this.removeNotification=this.removeNotification.bind(this),this.clearAll=this.clearAll.bind(this)}randString(n){let t="";const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let o=0;o${o}
\n

${n}

\n `,i&&(e.classList.add("pinned"),a+='\n \n '),e.innerHTML=a,e}push(n,t={}){const i=this.createNotification(n,t);return this.notificationPanel.append(i),i.animate([{transform:"translateY(1rem)",opacity:"0"},{transform:"none",opacity:"1"}],this.animationOptions),i.id}removeNotification(n){n.animate([{transform:"none",opacity:"1"},{transform:"translateY(0.5rem)",opacity:"0"}],this.animationOptions).onfinish=(()=>{n.remove()})}clearAll(){Array.from(this.notificationPanel.children).forEach(n=>{this.removeNotification(n)})}connectedCallback(){this.notificationPanel.addEventListener("click",n=>{n.target.closest(".close")&&this.removeNotification(n.target.closest(".notification"))});const n=new MutationObserver(n=>{n.forEach(n=>{"childList"===n.type&&n.addedNodes.length&&!n.addedNodes[0].classList.contains("pinned")&&setTimeout(()=>{this.removeNotification(n.addedNodes[0])},5e3)})});n.observe(this.notificationPanel,{childList:!0})}}); \ No newline at end of file +const smNotifications=document.createElement("template");smNotifications.innerHTML='\n\n
\n',customElements.define("sm-notifications",class extends HTMLElement{constructor(){super(),this.shadow=this.attachShadow({mode:"open"}).append(smNotifications.content.cloneNode(!0)),this.notificationPanel=this.shadowRoot.querySelector(".notification-panel"),this.animationOptions={duration:300,fill:"forwards",easing:"cubic-bezier(0.175, 0.885, 0.32, 1.275)"},this.push=this.push.bind(this),this.createNotification=this.createNotification.bind(this),this.removeNotification=this.removeNotification.bind(this),this.clearAll=this.clearAll.bind(this)}randString(n){let t="";const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let o=0;o${o}
\n

${n}

\n `,i&&(e.classList.add("pinned"),r+='\n \n '),e.innerHTML=r,e}push(n,t={}){const i=this.createNotification(n,t);return this.notificationPanel.append(i),i.animate([{transform:"translateY(1rem)",opacity:"0"},{transform:"none",opacity:"1"}],this.animationOptions),i.id}removeNotification(n){n.animate([{transform:"none",opacity:"1"},{transform:"translateY(0.5rem)",opacity:"0"}],this.animationOptions).onfinish=(()=>{n.remove()})}clearAll(){Array.from(this.notificationPanel.children).forEach(n=>{this.removeNotification(n)})}connectedCallback(){this.notificationPanel.addEventListener("click",n=>{n.target.closest(".close")&&this.removeNotification(n.target.closest(".notification"))});const n=new MutationObserver(n=>{n.forEach(n=>{"childList"===n.type&&n.addedNodes.length&&!n.addedNodes[0].classList.contains("pinned")&&setTimeout(()=>{this.removeNotification(n.addedNodes[0])},5e3)})});n.observe(this.notificationPanel,{childList:!0})}}); \ No newline at end of file diff --git a/components/dist/popup.js b/components/dist/popup.js index 790011e..63ce93e 100644 --- a/components/dist/popup.js +++ b/components/dist/popup.js @@ -1,4 +1,22 @@ -const smPopup = document.createElement('template') +class Stack { + constructor() { + this.items = []; + } + push(element) { + this.items.push(element); + } + pop() { + if (this.items.length == 0) + return "Underflow"; + return this.items.pop(); + } + peek() { + return this.items[this.items.length - 1]; + } +} +const popupStack = new Stack(); + +const smPopup = document.createElement('template'); smPopup.innerHTML = ` -