Functionality improvements and bug fixes

-- opening a popup will now return two promises (opened & closed). which will resolve on respective actions.
 - Also it is now possible to send some data with prop name "payload" in show/hide methods
This commit is contained in:
sairaj mote 2022-11-14 01:55:40 +05:30
parent d6c4c6401a
commit 182c4403ad
7 changed files with 62 additions and 39 deletions

View File

@ -126,7 +126,7 @@ smChips.innerHTML = `
<button class="nav-button nav-button--left hide">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10.828 12l4.95 4.95-1.414 1.414L8 12l6.364-6.364 1.414 1.414z"/></svg>
</button>
<section class="sm-chips">
<section class="sm-chips" part="chips-wrapper">
<slot></slot>
</section>
<button class="nav-button nav-button--right hide">
@ -298,16 +298,6 @@ smChip.innerHTML = `
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.sm-chip{
display: flex;
flex-shrink: 0;
cursor: pointer;
white-space: nowrap;
padding: var(--padding, 0.4rem 0.6rem);
transition: background 0.3s;
border-radius: var(--border-radius, 2rem);
-webkit-tap-highlight-color: transparent;
}
:host([selected]) .sm-chip{
color: var(--selected-option-color, rgba(var(--background-color,white)));
background-color: var(--selected-background-color, var(--accent-color,teal));
@ -321,10 +311,21 @@ smChip.innerHTML = `
:host(:hover:not([selected])) .sm-chip{
background-color: rgba(var(--text-color,(17,17,17)), 0.06);
}
.sm-chip{
display: flex;
flex-shrink: 0;
cursor: pointer;
white-space: nowrap;
padding: var(--padding, 0.5rem 0.8rem);
transition: background 0.3s;
border-radius: var(--border-radius, 0.5rem);
-webkit-tap-highlight-color: transparent;
background: var(--background,inherit);
}
</style>
<label class="sm-chip">
<span class="sm-chip" part="chip">
<slot></slot>
</label>
</span>
`;
customElements.define('sm-chip', class extends HTMLElement {
constructor() {

File diff suppressed because one or more lines are too long

View File

@ -83,16 +83,16 @@ customElements.define('sm-form', class extends HTMLElement {
}
}
reset() {
this.formElements.forEach(elem => elem.reset());
this.formElements.forEach(([elem, isWC]) => {
if (isWC) elem.reset();
else elem.value = '';
});
}
elementsChanged() {
this._requiredElements = [];
this.formElements = [...this.querySelectorAll('input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')];
this.formElements.forEach(elem => {
if (elem.hasAttribute('required')) {
this._requiredElements.push([elem, elem.tagName.includes('-')]);
}
this.formElements = [...this.querySelectorAll('input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio')].map(elem => {
return [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"]');
if (this.resetButton) {

View File

@ -1 +1 @@
const smForm = document.createElement("template"); smForm.innerHTML = ` <style> *{ padding: 0; margin: 0; box-sizing: border-box; } :host{ display: grid; width: 100%; } form{ display: inherit; gap: var(--gap, 1.5rem); width: 100%; } </style> <form part="form" onsubmit="return false"> <slot></slot> </form> `, 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.invalidFields = !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(e, t) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { e.apply(null, s) }, t) } } _checkValidity() { this.submitButton && (this.invalidFields = this._requiredElements.filter(([e, t]) => t ? !e.isValid : !e.checkValidity()), this.submitButton.disabled = this.invalidFields.length > 0) } handleKeydown(e) { if ("Enter" === e.key && e.target.tagName.includes("INPUT")) { if (this.invalidFields.length) for (let [t, i] of this._requiredElements) { let s = i ? !t.isValid : !t.checkValidity(); if (s) { (t?.shadowRoot?.lastElementChild || t).animate([{ transform: "translateX(-1rem)" }, { transform: "translateX(1rem)" }, { transform: "translateX(-0.5rem)" }, { transform: "translateX(0.5rem)" }, { transform: "translateX(0)" },], { duration: 300, easing: "ease" }), i ? t.focusIn() : t.focus(); break } } else this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })) } } reset() { this.formElements.forEach(e => e.reset()) } elementsChanged() { this._requiredElements = [], this.formElements = [...this.querySelectorAll("input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio")], this.formElements.forEach(e => { e.hasAttribute("required") && this._requiredElements.push([e, e.tagName.includes("-")]) }), 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() { this.shadowRoot.querySelector("slot").addEventListener("slotchange", this.elementsChanged), this.addEventListener("input", this.debounce(this._checkValidity, 100)), this.addEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { "childList" === e.type && this.elementsChanged() }) }), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } 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 = ` <style> *{ padding: 0; margin: 0; box-sizing: border-box; } :host{ display: grid; width: 100%; } form{ display: inherit; gap: var(--gap, 1.5rem); width: 100%; } </style> <form part="form" onsubmit="return false"> <slot></slot> </form> `, 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.invalidFields = !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(e, t) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { e.apply(null, s) }, t) } } _checkValidity() { this.submitButton && (this.invalidFields = this._requiredElements.filter(([e, t]) => t ? !e.isValid : !e.checkValidity()), this.submitButton.disabled = this.invalidFields.length > 0) } handleKeydown(e) { if ("Enter" === e.key && e.target.tagName.includes("INPUT")) { if (this.invalidFields.length) for (let [t, i] of this._requiredElements) { let s = i ? !t.isValid : !t.checkValidity(); if (s) { (t?.shadowRoot?.lastElementChild || t).animate([{ transform: "translateX(-1rem)" }, { transform: "translateX(1rem)" }, { transform: "translateX(-0.5rem)" }, { transform: "translateX(0.5rem)" }, { transform: "translateX(0)" },], { duration: 300, easing: "ease" }), i ? t.focusIn() : t.focus(); break } } else this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })) } } reset() { this.formElements.forEach(([e, t]) => { t ? e.reset() : e.value = "" }) } elementsChanged() { this.formElements = [...this.querySelectorAll("input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio")].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() { this.shadowRoot.querySelector("slot").addEventListener("slotchange", this.elementsChanged), this.addEventListener("input", this.debounce(this._checkValidity, 100)), this.addEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { "childList" === e.type && this.elementsChanged() }) }), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } disconnectedCallback() { this.removeEventListener("input", this.debounce(this._checkValidity, 100)), this.removeEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver.disconnect() } });

View File

@ -258,12 +258,13 @@ customElements.define('sm-popup', class extends HTMLElement {
}
show(options = {}) {
const { pinned = false } = options;
const { pinned = false, payload } = options;
if (this.isOpen) return;
const animOptions = {
duration: 300,
easing: 'ease'
}
this.payload = payload;
popupStack.push({
popup: this,
permission: pinned
@ -279,10 +280,16 @@ customElements.define('sm-popup', class extends HTMLElement {
this.backdrop.animate([
{ opacity: 0 },
{ opacity: 1 },
], animOptions)
], animOptions).onfinish = () => {
this.resolveOpen(this.payload);
}
this.dispatchEvent(
new CustomEvent("popupopened", {
bubbles: true,
composed: true,
detail: {
payload: this.payload
}
})
);
document.body.style.overflow = 'hidden';
@ -304,8 +311,17 @@ customElements.define('sm-popup', class extends HTMLElement {
this.popupHeader.addEventListener('touchstart', this.handleTouchStart, { passive: true });
this.backdrop.addEventListener('mousedown', this.handleSoftDismiss);
}
return {
opened: new Promise((resolve) => {
this.resolveOpen = resolve;
}),
closed: new Promise((resolve) => {
this.resolveClose = resolve;
})
}
}
hide() {
hide(options = {}) {
const { payload } = options;
const animOptions = {
duration: 150,
easing: 'ease'
@ -335,11 +351,13 @@ customElements.define('sm-popup', class extends HTMLElement {
this.dispatchEvent(
new CustomEvent("popupclosed", {
bubbles: true,
composed: true,
detail: {
popup: this,
payload: payload || this.payload
}
})
);
this.resolveClose(payload || this.payload);
this.isOpen = false;
})
popupStack.pop();

File diff suppressed because one or more lines are too long

View File

@ -35,25 +35,29 @@
<body>
<button>Show popup</button>
<collapsed-text>
Loreetur adipisicing elit. Architecto minima maiores autem iusto porro, odit
iure
ea emus dolor itaque unde sequi, reprehenderit ex aperiam
dinfgw egweb gnw
slideInRightgsdgs
</collapsed-text>
<sm-popup>
<input type="text" placeholder="fds">
<button>dsfsd</button>
<collapsed-text>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto minima maiores autem iusto porro, odit
iure
ea eveniet enim soluta ex a nihil? Dicta ducimus dolor itaque unde sequi, reprehenderit ex aperiam
dignissimos
inventore? Totam aliquid repellendus nulla culpa nemo perspiciatis tempora. Vel obcaecati asperiores nam,
ratione sint itaque temporibus incidunt officiis iusto cumque reiciendis ab repellendus quaerat ducimus
quibusdam quia maxime nostrum atque ad sequi eveniet est error ipsam voluptatem. Architecto molestiae ex et
minima praesentium quasi ea, ad enim consequuntur at nisi nostrum. Quisquam nostrum, accusamus neque, fugiat
esse deleniti ex vitae totam asperiores odit quaerat sunt voluptates.
</collapsed-text>
</sm-popup>
</body>
<script>
const popup = document.querySelector('sm-popup');
popup.show();
const pPromise = popup.show({ payload: { opened: new Date() } })
pPromise.opened.then((data) => {
console.log('popup opened', data);
});
pPromise.closed.then((data) => {
console.log('popup closed', data);
});
document.querySelector('button').addEventListener('click', () => {
popup.show();
});