code refactoring

This commit is contained in:
sairaj mote 2023-10-01 05:15:53 +05:30
parent 7068afd2b7
commit a051e5a067
8 changed files with 86 additions and 39 deletions

View File

@ -181,7 +181,7 @@ customElements.define('sm-chips', class extends HTMLElement {
this._value = value;
this.assignedElements.forEach(elem => {
if (elem.value == value) {
elem.setAttribute('selected', 'true');
elem.setAttribute('selected', '');
elem.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
}
else
@ -213,7 +213,7 @@ customElements.define('sm-chips', class extends HTMLElement {
this.slotChangeTimeout = setTimeout(() => {
this.assignedElements = slot.assignedElements();
this.assignedElements.forEach(elem => {
if (elem.hasAttribute('selected') && elem.getAttribute('selected') === 'true') {
if (elem.hasAttribute('selected')) {
this._value = elem.value;
}
});
@ -304,7 +304,7 @@ smChip.innerHTML = `
:host(:focus-within) .sm-chip{
box-shadow: 0 0 0 0.1rem var(--accent-color,teal) inset;
}
:host(:hover:not([selected="true"])) .sm-chip{
:host(:hover:not([selected])) .sm-chip{
background-color: rgba(var(--text-color,(17,17,17)), 0.06);
}
.sm-chip{
@ -318,7 +318,7 @@ smChip.innerHTML = `
-webkit-tap-highlight-color: transparent;
background: var(--background,inherit);
}
:host([selected="true"]) .sm-chip{
:host([selected]) .sm-chip{
background-color: var(--accent-color, teal);
color: rgba(var(--background-color, (255,255,255)), 1);
}

File diff suppressed because one or more lines are too long

View File

@ -35,11 +35,6 @@ customElements.define('sm-form', class extends HTMLElement {
this.supportedElements = 'input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio';
this.formElements = [];
this._requiredElements = []
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);
}
static get observedAttributes() {
return ['skip-submit'];
@ -47,7 +42,7 @@ customElements.define('sm-form', class extends HTMLElement {
get validity() {
return this.isFormValid;
}
debounce(callback, wait) {
debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
window.clearTimeout(timeoutId);
@ -56,7 +51,7 @@ customElements.define('sm-form', class extends HTMLElement {
}, wait);
};
}
_checkValidity() {
_checkValidity = () => {
if (!this.submitButton || this._requiredElements.length === 0) return;
this.invalidFieldsCount = 0
this._requiredElements.forEach(([elem, isWC]) => {
@ -72,7 +67,7 @@ customElements.define('sm-form', class extends HTMLElement {
if (!this.skipSubmit)
this.submitButton.disabled = !this.isFormValid;
}
handleKeydown(e) {
handleKeydown = (e) => {
if (e.key === 'Enter' && e.target.tagName.includes('INPUT')) {
if (this.invalidFieldsCount === 0) {
if (this.submitButton) {
@ -96,15 +91,19 @@ customElements.define('sm-form', class extends HTMLElement {
duration: 300,
easing: 'ease'
});
if (isWC) elem.focusIn();
else elem.focus();
if (isWC) {
elem.focusIn();
if (elem.tagName === 'SM-INPUT' && elem.value.trim() === '') {
elem.showError()
}
} else elem.focus();
break;
}
}
}
}
}
reset() {
reset = () => {
this.formElements.forEach(([elem, isWC]) => {
if (isWC) elem.reset();
else {
@ -121,7 +120,7 @@ customElements.define('sm-form', class extends HTMLElement {
});
this._checkValidity();
}
elementsChanged() {
elementsChanged = () => {
this.formElements = [...this.querySelectorAll(this.supportedElements)].map(elem => {
return [elem, elem.tagName.includes('-')];
});

View File

@ -1 +1 @@
const smForm = document.createElement("template"); smForm.innerHTML = '\n <style>\n *{\n padding: 0;\n margin: 0;\n box-sizing: border-box;\n }\n :host{\n display: grid;\n width: 100%;\n }\n form{\n display: inherit;\n gap: var(--gap, 1.5rem);\n width: 100%;\n }\n </style>\n <form part="form" onsubmit="return false">\n <slot></slot>\n </form>\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.invalidFieldsCount, this.skipSubmit = !1, this.isFormValid = void 0, this.supportedElements = "input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio", this.formElements = [], this._requiredElements = [], 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) } static get observedAttributes() { return ["skip-submit"] } get validity() { return this.isFormValid } debounce(e, t) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout((() => { e.apply(null, s) }), t) } } _checkValidity() { this.submitButton && 0 !== this._requiredElements.length && (this.invalidFieldsCount = 0, this._requiredElements.forEach((([e, t]) => { (!e.disabled && t && !e.isValid || !t && !e.checkValidity()) && this.invalidFieldsCount++ })), this.isFormValid !== (0 === this.invalidFieldsCount) && (this.isFormValid = 0 === this.invalidFieldsCount, this.dispatchEvent(new CustomEvent(this.isFormValid ? "valid" : "invalid", { bubbles: !0, composed: !0 })), this.skipSubmit || (this.submitButton.disabled = !this.isFormValid))) } handleKeydown(e) { if ("Enter" === e.key && e.target.tagName.includes("INPUT")) if (0 === this.invalidFieldsCount) this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })); else for (const [e, t] of this._requiredElements) { if (t ? !e.isValid : !e.checkValidity()) { (e?.shadowRoot?.lastElementChild || e).animate([{ transform: "translateX(-1rem)" }, { transform: "translateX(1rem)" }, { transform: "translateX(-0.5rem)" }, { transform: "translateX(0.5rem)" }, { transform: "translateX(0)" }], { duration: 300, easing: "ease" }), t ? e.focusIn() : e.focus(); break } } } reset() { this.formElements.forEach((([e, t]) => { if (t) e.reset(); else switch (e.type) { case "checkbox": case "radio": e.checked = !1; break; default: e.value = "" } })), this._checkValidity() } elementsChanged() { this.formElements = [...this.querySelectorAll(this.supportedElements)].map((e => [e, e.tagName.includes("-")])), this._requiredElements = this.formElements.filter((([e]) => e.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 e = this.debounce(this.elementsChanged, 100); this.addEventListener("input", this.debounce(this._checkValidity, 100)), this.addEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.shadowRoot.querySelector("slot").addEventListener("slotchange", e), this.mutationObserver = new MutationObserver((t => { t.forEach((t => { ("childList" === t.type && [...t.addedNodes].some((e => 1 === e.nodeType && e.querySelector(this.supportedElements))) || [...t.removedNodes].some((e => 1 === e.nodeType && e.querySelector(this.supportedElements)))) && e() })) })), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } attributeChangedCallback(e, t, i) { "skip-submit" === e && (this.skipSubmit = null !== i) } disconnectedCallback() { this.removeEventListener("input", this.debounce(this._checkValidity, 100)), this.removeEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver.disconnect() } });
const smForm=document.createElement("template");smForm.innerHTML='\n <style>\n *{\n padding: 0;\n margin: 0;\n box-sizing: border-box;\n }\n :host{\n display: grid;\n width: 100%;\n }\n form{\n display: inherit;\n gap: var(--gap, 1.5rem);\n width: 100%;\n }\n </style>\n <form part="form" onsubmit="return false">\n <slot></slot>\n </form>\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.invalidFieldsCount,this.skipSubmit=!1,this.isFormValid=void 0,this.supportedElements="input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio",this.formElements=[],this._requiredElements=[]}static get observedAttributes(){return["skip-submit"]}get validity(){return this.isFormValid}debounce=(callback,wait)=>{let timeoutId=null;return(...args)=>{window.clearTimeout(timeoutId),timeoutId=window.setTimeout((()=>{callback.apply(null,args)}),wait)}};_checkValidity=()=>{this.submitButton&&0!==this._requiredElements.length&&(this.invalidFieldsCount=0,this._requiredElements.forEach((([elem,isWC])=>{(!elem.disabled&&isWC&&!elem.isValid||!isWC&&!elem.checkValidity())&&this.invalidFieldsCount++})),this.isFormValid!==(0===this.invalidFieldsCount)&&(this.isFormValid=0===this.invalidFieldsCount,this.dispatchEvent(new CustomEvent(this.isFormValid?"valid":"invalid",{bubbles:!0,composed:!0})),this.skipSubmit||(this.submitButton.disabled=!this.isFormValid)))};handleKeydown=e=>{if("Enter"===e.key&&e.target.tagName.includes("INPUT"))if(0===this.invalidFieldsCount)this.submitButton&&this.submitButton.click(),this.dispatchEvent(new CustomEvent("submit",{bubbles:!0,composed:!0}));else for(const[elem,isWC]of this._requiredElements){if(isWC?!elem.isValid:!elem.checkValidity()){(elem?.shadowRoot?.lastElementChild||elem).animate([{transform:"translateX(-1rem)"},{transform:"translateX(1rem)"},{transform:"translateX(-0.5rem)"},{transform:"translateX(0.5rem)"},{transform:"translateX(0)"}],{duration:300,easing:"ease"}),isWC?(elem.focusIn(),"SM-INPUT"===elem.tagName&&""===elem.value.trim()&&elem.showError()):elem.focus();break}}};reset=()=>{this.formElements.forEach((([elem,isWC])=>{if(isWC)elem.reset();else switch(elem.type){case"checkbox":case"radio":elem.checked=!1;break;default:elem.value=""}})),this._checkValidity()};elementsChanged=()=>{this.formElements=[...this.querySelectorAll(this.supportedElements)].map((elem=>[elem,elem.tagName.includes("-")])),this._requiredElements=this.formElements.filter((([elem])=>elem.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 updateFormDecedents=this.debounce(this.elementsChanged,100);this.addEventListener("input",this.debounce(this._checkValidity,100)),this.addEventListener("keydown",this.debounce(this.handleKeydown,100)),this.shadowRoot.querySelector("slot").addEventListener("slotchange",updateFormDecedents),this.mutationObserver=new MutationObserver((mutations=>{mutations.forEach((mutation=>{("childList"===mutation.type&&[...mutation.addedNodes].some((node=>1===node.nodeType&&node.querySelector(this.supportedElements)))||[...mutation.removedNodes].some((node=>1===node.nodeType&&node.querySelector(this.supportedElements))))&&updateFormDecedents()}))})),this.mutationObserver.observe(this,{childList:!0,subtree:!0})}attributeChangedCallback(name,oldValue,newValue){"skip-submit"===name&&(this.skipSubmit=null!==newValue)}disconnectedCallback(){this.removeEventListener("input",this.debounce(this._checkValidity,100)),this.removeEventListener("keydown",this.debounce(this.handleKeydown,100)),this.mutationObserver.disconnect()}});

View File

@ -188,7 +188,7 @@ customElements.define('sm-select', class extends HTMLElement {
reset(fire = true) {
if (this.availableOptions[0] && this.previousOption !== this.availableOptions[0]) {
const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0];
const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0];
this.value = selectedOption.getAttribute('value')
if (fire) {
this.fireEvent()
@ -197,9 +197,9 @@ customElements.define('sm-select', class extends HTMLElement {
}
selectOption(selectedOption) {
if (this.previousOption !== selectedOption) {
this.querySelectorAll('[selected="true"]').forEach(option => option.removeAttribute('selected'))
this.querySelectorAll('[selected').forEach(option => option.removeAttribute('selected'))
this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`;
selectedOption.setAttribute('selected', 'true')
selectedOption.setAttribute('selected', '')
this.previousOption = selectedOption
}
}
@ -229,7 +229,7 @@ customElements.define('sm-select', class extends HTMLElement {
], this.animationOptions)
this.setAttribute('open', '');
this.style.zIndex = 1000;
(this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]).focus()
(this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]).focus()
document.addEventListener('mousedown', this.handleClickOutside)
this.isOpen = true
}
@ -307,7 +307,7 @@ customElements.define('sm-select', class extends HTMLElement {
if (e.target === this) {
if (this.isOpen && e.key === 'ArrowDown') {
e.preventDefault();
(this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0]).focus()
(this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0]).focus()
this.handleOptionSelection(e)
} else if (e.key === ' ') {
e.preventDefault()
@ -355,7 +355,7 @@ customElements.define('sm-select', class extends HTMLElement {
}
});
if (attributesChanged) {
const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected') && option.getAttribute('selected') === 'true') || this.availableOptions[0];
const selectedOption = this.availableOptions.find(option => option.hasAttribute('selected')) || this.availableOptions[0];
this.selectedOptionText.textContent = `${this.label}${selectedOption.textContent}`;
this.setAttribute('value', selectedOption.getAttribute('value'));
}
@ -444,7 +444,7 @@ smOption.innerHTML = `
:host(:focus) .option::before{
opacity: 1
}
:host([selected="true"]) .option::before{
:host([selected]) .option::before{
opacity: 1;
background: var(--accent-color, teal);
}
@ -452,7 +452,7 @@ smOption.innerHTML = `
.option:hover{
background: rgba(var(--text-color,(17,17,17)), 0.1);
}
:host(:not([selected="true"]):hover) .option::before{
:host(:not([selected]):hover) .option::before{
opacity: 1
}
}

File diff suppressed because one or more lines are too long

View File

@ -36,22 +36,69 @@
</head>
<body>
<sm-notifications id="notification_drawer"></sm-notifications>
<button onclick="pushNotification()">
Send Notification
</button>
<sm-form>
<sm-input placeholder="fafh" value="" animate required>
<sm-button slot="right" type="submit">Submit</sm-button>
</sm-input>
<button type="submit">Submit</button>
</sm-form>
</body>
<script>
function pushNotification() {
document.getElementById('notification_drawer').push(`<h4>This is a notification.</h4><p>d snfkjsdn sdf sdfsd</p>`, {
timeout: 10000,
action: {
label: 'click me',
callback: () => {
console.log('clicked')
document.querySelector('sm-input').isValid
let currentSubscriber = null;
/**
* @param {any} initialValue - initial value for the signal
* @returns {array} - array containing getter and setter for the signal
* @example
* const [getCount, setCount] = $signal(0);
*/
function $signal(initialValue) {
let value = initialValue;
const subscribers = new Set();
function getter() {
if (currentSubscriber) {
const weakRef = new WeakRef({ func: currentSubscriber });
subscribers.add(weakRef);
}
return value;
}
function setter(newValue) {
if (newValue !== value) {
value = newValue;
for (const subscriber of subscribers) {
const ref = subscriber.deref();
if (ref) {
ref.func();
}
}
}
})
}
return [getter, setter];
}
/**
*
* @param {function} fn - function that will run if any of its dependent signals change
* @example
* $effect(() => {
* console.log(count());
* }
* @returns {void}
*/
async function $effect(fn) {
currentSubscriber = fn;
const result = fn();
try {
if (result instanceof Promise) {
await result;
}
} catch (e) {
console.error(e)
} finally {
currentSubscriber = null;
}
}
</script>

1
components/test.min.html Normal file
View File

@ -0,0 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=UTF-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>Document</title><script src=dist/select.js></script><script src=dist/form.js></script><script src=dist/popup.js></script><script src=dist/switch.js></script><script src=dist/checkbox.js></script><script src=dist/radio.js></script><script src=dist/input.js></script><script src=dist/textarea.js></script><script src=dist/text-field.js></script><script src=dist/button.js></script><script src=dist/menu.js></script><script src=dist/cube-loader.js></script><script src=dist/tags-input.js></script><script src=dist/strip-select.js></script><script src=dist/collapsed-text.js></script><script src=dist/notifications.js></script><link rel=stylesheet href=css/main.min.css><style>div{display:flex;padding:4vmax}body{overflow:auto}</style></head><body><sm-form><sm-input placeholder=fafh value="" animate required><sm-button slot=right type=submit>Submit</sm-button></sm-input><button type=submit>Submit</button></sm-form></body><script>document.querySelector("sm-input").isValid;let currentSubscriber=null;function $signal(r){let c=r;const t=new Set;return[function(){var r;return currentSubscriber&&(r=new WeakRef({func:currentSubscriber}),t.add(r)),c},function(r){if(r!==c){c=r;for(const n of t){var e=n.deref();e&&e.func()}}}]}async function $effect(r){r=(currentSubscriber=r)();try{r instanceof Promise&&await r}catch(r){console.error(r)}finally{currentSubscriber=null}}</script></html>