diff --git a/packages/signalizejs/src/plugins/bind.js b/packages/signalizejs/src/plugins/bind.js index 26db93f..ee15f06 100755 --- a/packages/signalizejs/src/plugins/bind.js +++ b/packages/signalizejs/src/plugins/bind.js @@ -35,137 +35,151 @@ export default ($) => { }; $.bind = (element, attributes) => { - /** @type {CallableFunction[]} */ - const unwatchSignalCallbacks = []; - const cleanups = []; - - for (let [attr, attrOptions] of Object.entries(attributes)) { - if (attrOptions.length === 1) { - attrOptions = attrOptions[0]; - } - - const attrOptionsAsArray = Array.isArray(attrOptions) ? attrOptions : [attrOptions]; - const isNumericInput = numericInputAttributes.includes(element.getAttribute('type') ?? ''); - const attributeBinder = attrOptionsAsArray.pop(); - const signalsToWatch = attrOptionsAsArray; - const attributeBinderType = typeof attributeBinder; - const attributeBinderIsSignal = attributeBinder instanceof Signal; - let attributeInited = false; - let previousSettedValue; - let previousValue; - - /** - * @param {string} attribute - * @param {string|number} value - * @returns {Promise} - */ - const setAttribute = async (attribute, value) => { - value = value instanceof Promise ? await value : value; - if (attributeInited && previousValue === value) { - return; + const bind = () => { + /** @type {CallableFunction[]} */ + const unwatchSignalCallbacks = []; + const cleanups = []; + + for (let [attr, attrOptions] of Object.entries(attributes)) { + if (attrOptions.length === 1) { + attrOptions = attrOptions[0]; } - previousValue = value; - attribute = attributesAliases[attribute] ?? attribute; - - if (textContentAttributes.includes(attribute)) { - element[attribute] = value; - } else if (booleanAttributes.includes(attribute)) { - element[attribute] = !!value; - } else if (attribute === 'class') { - if (attributeInited) { - if (previousSettedValue !== undefined) { - for (const className of previousSettedValue) { - element.classList.remove(className); + + const attrOptionsAsArray = Array.isArray(attrOptions) ? attrOptions : [attrOptions]; + const isNumericInput = numericInputAttributes.includes(element.getAttribute('type') ?? ''); + const attributeBinder = attrOptionsAsArray.pop(); + const signalsToWatch = attrOptionsAsArray; + const attributeBinderType = typeof attributeBinder; + const attributeBinderIsSignal = attributeBinder instanceof Signal; + let attributeInited = false; + let previousSettedValue; + let previousValue; + + /** + * @param {string} attribute + * @param {string|number} value + * @returns {Promise} + */ + const setAttribute = async (attribute, value) => { + value = value instanceof Promise ? await value : value; + if (attributeInited && previousValue === value) { + return; + } + previousValue = value; + attribute = attributesAliases[attribute] ?? attribute; + + if (textContentAttributes.includes(attribute)) { + element[attribute] = value; + } else if (booleanAttributes.includes(attribute)) { + element[attribute] = !!value; + } else if (attribute === 'class') { + if (attributeInited) { + if (previousSettedValue !== undefined) { + for (const className of previousSettedValue) { + element.classList.remove(className); + } } } - } - const valueToSet = value.trim().split(' ').filter((className) => className.trim().length > 0); - previousSettedValue = valueToSet; + const valueToSet = value.trim().split(' ').filter((className) => className.trim().length > 0); + previousSettedValue = valueToSet; - for (const className of valueToSet) { - element.classList.add(className); + for (const className of valueToSet) { + element.classList.add(className); + } + } else { + element.setAttribute(attribute, value); } - } else { - element.setAttribute(attribute, value); + attributeInited = true; + }; + + if (['string', 'number'].includes(attributeBinderType)) { + setAttribute(attr, attributeBinder); + continue; } - attributeInited = true; - }; - if (['string', 'number'].includes(attributeBinderType)) { - setAttribute(attr, attributeBinder); - continue; - } + if (attributeBinderIsSignal) { + signalsToWatch.push(attributeBinder); + } - if (attributeBinderIsSignal) { - signalsToWatch.push(attributeBinder); - } + /** @type {CallableFunction|null} */ + let getListener = null; + /** @type {CallableFunction|null} */ + let setListener = null; + /** @type {any} */ + let initValue = undefined; + if (attributeBinderIsSignal) { + getListener = () => attributeBinder(); + setListener = (value) => attributeBinder(value); + } else { + if (typeof attributeBinder?.get === 'function') { + getListener = () => attributeBinder.get(); + } - /** @type {CallableFunction|null} */ - let getListener = null; - /** @type {CallableFunction|null} */ - let setListener = null; - /** @type {any} */ - let initValue = undefined; - if (attributeBinderIsSignal) { - getListener = () => attributeBinder(); - setListener = (value) => attributeBinder(value); - } else { - if (typeof attributeBinder?.get === 'function') { - getListener = () => attributeBinder.get(); - } + if (typeof attributeBinder?.set === 'function') { + setListener = (value) => attributeBinder.set(value); + } - if (typeof attributeBinder?.set === 'function') { - setListener = (value) => attributeBinder.set(value); - } + if (typeof attributeBinder?.value !== 'undefined') { + initValue = attributeBinder?.value; + } - if (typeof attributeBinder?.value !== 'undefined') { - initValue = attributeBinder?.value; - } + if (getListener === null) { + if (typeof attributeBinder === 'function') { + getListener = () => attributeBinder(); + } else if (signalsToWatch.length === 1) { + getListener = () => signalsToWatch[0](); + } + } - if (getListener === null) { - if (typeof attributeBinder === 'function') { - getListener = () => attributeBinder(); - } else if (signalsToWatch.length === 1) { - getListener = () => signalsToWatch[0](); + if (setListener === null && signalsToWatch.length === 1) { + setListener = (value) => signalsToWatch[0](value); } } - if (setListener === null && signalsToWatch.length === 1) { - setListener = (value) => signalsToWatch[0](value); + if (getListener !== null || initValue !== undefined) { + setAttribute(attr, initValue !== undefined ? initValue : getListener()); } - } - if (getListener !== null || initValue !== undefined) { - setAttribute(attr, initValue !== undefined ? initValue : getListener()); - } + for (const signalToWatch of signalsToWatch) { + unwatchSignalCallbacks.push( + signalToWatch.watch(() => setAttribute(attr, getListener())) + ); + } - for (const signalToWatch of signalsToWatch) { - unwatchSignalCallbacks.push( - signalToWatch.watch(() => setAttribute(attr, getListener())) - ); + if (typeof setListener === 'function' && reactiveInputAttributes.includes(attr)) { + const inputListener = () => { + setListener(isNumericInput ? Number(element[attr].replace(',', '.')) : element[attr]); + }; + + on('input', element, inputListener, { passive: true }); + cleanups.push(() => $.off('input', element, inputListener)); + } } - if (typeof setListener === 'function' && reactiveInputAttributes.includes(attr)) { - const inputListener = () => { - setListener(isNumericInput ? Number(element[attr].replace(',', '.')) : element[attr]); - }; + $.scope(element, ({ $cleanup }) => { + $cleanup(() => { + for (const cleanup of cleanups) { + cleanup(); + } - on('input', element, inputListener, { passive: true }); - cleanups.push(() => $.off('input', element, inputListener)); - } - } + for (const unwatch of unwatchSignalCallbacks) { + unwatch(); + } + }); + }); + }; - $.scope(element, ({ $cleanup }) => { - $cleanup(() => { - for (const cleanup of cleanups) { - cleanup(); - } + const tagName = element.tagName; - for (const unwatch of unwatchSignalCallbacks) { - unwatch(); + if (tagName.split('-').length > 1 && customElements.get(tagName) === undefined) { + on('component:beforeSetuped', ({ target }) => { + if (target === element) { + bind(); } }); - }); + } else { + bind(); + } }; }; diff --git a/packages/signalizejs/src/plugins/component.js b/packages/signalizejs/src/plugins/component.js index 794804c..38796a9 100755 --- a/packages/signalizejs/src/plugins/component.js +++ b/packages/signalizejs/src/plugins/component.js @@ -120,41 +120,6 @@ export default ($) => { } }); - /** - * @param {string} name - */ - this.#scope.$children = async (name) => { - if (customElements.get(name) === undefined) { - await customElements.whenDefined(name); - } - - const childComponents = this.#scope.$el.querySelectorAll(name); - const initPromises = []; - for (const childComponent of childComponents) { - const componentScope = scope(childComponent); - initPromises.push( - componentScope?._setuped === true - ? componentScope - : new Promise((resolve) => { - $.on('component:setuped', ({ detail }) => { - if (detail.$el === childComponent) { - resolve(detail); - } - }); - }) - ); - } - - return await Promise.all(initPromises); - }; - - /** - * @param {string} name - */ - this.#scope.$child = async (name) => { - return (await this.#scope.$children(name))[0] ?? null; - }; - for (const attr of this.#scope.$el.attributes) { this.attributeChangedCallback(attr.name, undefined, this.#scope.$el.getAttribute(attr.name)); } @@ -206,9 +171,9 @@ root select('slot:not([name])', template.content)?.replaceWith(currentTemplate.content); } */ - - dispatch('component:setuped', this.#scope, { target: this.#scope.$el, bubbles: true }); + dispatch('component:beforeSetuped', this.#scope, { target: this.#scope.$el, bubbles: true }); this.#scope._setuped = true; + dispatch('component:setuped', this.#scope, { target: this.#scope.$el, bubbles: true }); } /** diff --git a/packages/signalizejs/src/plugins/scope.js b/packages/signalizejs/src/plugins/scope.js index 5cc3900..698255a 100755 --- a/packages/signalizejs/src/plugins/scope.js +++ b/packages/signalizejs/src/plugins/scope.js @@ -127,36 +127,32 @@ export default ($) => { cleanChildren(this.$el); }; - /** - * @param {string} name - * @returns {Element|null} - */ - $ref = (name) => { - return this.$refs(name)[0] ?? null; - }; - /** * @param {string} name * @returns {Element[]} */ - $refs = (name) => { - return [...this.$el.querySelectorAll(`[${refAttribute}=${name}]`)].filter((element) => { - const checkParentElement = (el) => { - const parentElement = el.parentNode; - if (parentElement === this.$el) { - return true; - } - - if (parentElement.tagName.toLowerCase().includes('-')) { - return false; - } - - return checkParentElement(parentElement); - }; - - return checkParentElement(element); - }); - }; + $refs = new Proxy({}, { + get: (target, key) => { + const refs = [...this.$el.querySelectorAll(`[${refAttribute}=${key}]`)].filter((element) => { + const checkParentElement = (el) => { + const parentElement = el.parentNode; + if (parentElement === this.$el) { + return true; + } + + if (parentElement.tagName.toLowerCase().includes('-')) { + return false; + } + + return checkParentElement(parentElement); + }; + + return checkParentElement(element); + }); + + return refs.length === 1 ? refs[0] : refs; + } + }); } /**