diff --git a/components/dist/form.js b/components/dist/form.js index 176ffae..bc137b9 100644 --- a/components/dist/form.js +++ b/components/dist/form.js @@ -31,7 +31,7 @@ customElements.define('sm-form', class extends HTMLElement { this.form = this.shadowRoot.querySelector('form'); this.invalidFieldsCount; this.skipSubmit = false; - this.isFormValid = false; + this.isFormValid = undefined; this.supportedElements = 'input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio'; this._requiredElements = [] this.debounce = this.debounce.bind(this); @@ -62,12 +62,12 @@ customElements.define('sm-form', class extends HTMLElement { if (isWC && !elem.isValid || !isWC && !elem.checkValidity()) this.invalidFieldsCount++; }); - if (this.isFormValid === this.invalidFieldsCount === 0) return; + if (this.isFormValid === (this.invalidFieldsCount === 0)) return; + this.isFormValid = this.invalidFieldsCount === 0; this.dispatchEvent(new CustomEvent(this.isFormValid ? 'valid' : 'invalid', { bubbles: true, composed: true, })); - this.isFormValid = this.invalidFieldsCount === 0; if (!this.skipSubmit) this.submitButton.disabled = !this.isFormValid; } diff --git a/components/dist/form.min.js b/components/dist/form.min.js index 88ebbe2..23d3c54 100644 --- a/components/dist/form.min.js +++ b/components/dist/form.min.js @@ -1 +1 @@ -const smForm = document.createElement("template"); smForm.innerHTML = `
`, 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 = !1, this.supportedElements = "input, sm-input, sm-textarea, sm-checkbox, tags-input, file-input, sm-switch, sm-radio", 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(t, e) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { t.apply(null, s) }, e) } } _checkValidity() { this.submitButton && 0 !== this._requiredElements.length && (this.invalidFieldsCount = 0, this._requiredElements.forEach(([t, e]) => { (e && !t.isValid || !e && !t.checkValidity()) && this.invalidFieldsCount++ }), this.isFormValid, this.invalidFieldsCount, this.dispatchEvent(new CustomEvent(this.isFormValid ? "valid" : "invalid", { bubbles: !0, composed: !0 })), this.isFormValid = 0 === this.invalidFieldsCount, this.skipSubmit || (this.submitButton.disabled = !this.isFormValid)) } handleKeydown(t) { if ("Enter" === t.key && t.target.tagName.includes("INPUT")) { if (0 === this.invalidFieldsCount) this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })); else for (let [e, i] of this._requiredElements) { let s = i ? !e.isValid : !e.checkValidity(); if (s) { (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" }), i ? e.focusIn() : e.focus(); break } } } } reset() { this.formElements.forEach(([t, e]) => { e ? t.reset() : t.value = "" }) } elementsChanged() { this.formElements = [...this.querySelectorAll(this.supportedElements)].map(t => [t, t.tagName.includes("-")]), 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() { let t = 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", t), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { ("childList" === e.type && [...e.addedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements)) || [...e.removedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements))) && t() }) }), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } attributeChangedCallback(t, e, i) { "skip-submit" === t && (this.skipSubmit = null !== i) } disconnectedCallback() { this.removeEventListener("input", this.debounce(this._checkValidity, 100)), this.removeEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver.disconnect() } }); \ No newline at end of file +const smForm = document.createElement("template"); smForm.innerHTML = `
`, 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._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(t, e) { let i = null; return (...s) => { window.clearTimeout(i), i = window.setTimeout(() => { t.apply(null, s) }, e) } } _checkValidity() { this.submitButton && 0 !== this._requiredElements.length && (this.invalidFieldsCount = 0, this._requiredElements.forEach(([t, e]) => { (e && !t.isValid || !e && !t.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(t) { if ("Enter" === t.key && t.target.tagName.includes("INPUT")) { if (0 === this.invalidFieldsCount) this.submitButton && this.submitButton.click(), this.dispatchEvent(new CustomEvent("submit", { bubbles: !0, composed: !0 })); else for (let [e, i] of this._requiredElements) { let s = i ? !e.isValid : !e.checkValidity(); if (s) { (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" }), i ? e.focusIn() : e.focus(); break } } } } reset() { this.formElements.forEach(([t, e]) => { e ? t.reset() : t.value = "" }) } elementsChanged() { this.formElements = [...this.querySelectorAll(this.supportedElements)].map(t => [t, t.tagName.includes("-")]), 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() { let t = 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", t), this.mutationObserver = new MutationObserver(e => { e.forEach(e => { ("childList" === e.type && [...e.addedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements)) || [...e.removedNodes].some(t => 1 === t.nodeType && t.querySelector(this.supportedElements))) && t() }) }), this.mutationObserver.observe(this, { childList: !0, subtree: !0 }) } attributeChangedCallback(t, e, i) { "skip-submit" === t && (this.skipSubmit = null !== i) } disconnectedCallback() { this.removeEventListener("input", this.debounce(this._checkValidity, 100)), this.removeEventListener("keydown", this.debounce(this.handleKeydown, 100)), this.mutationObserver.disconnect() } }); \ No newline at end of file