'use strict'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; const _Cona = class _Cona extends HTMLElement { constructor() { super(); __publicField(this, "_op"); __publicField(this, "props"); __publicField(this, "_ef"); __publicField(this, "_ev"); __publicField(this, "_sr"); __publicField(this, "_t"); this._op = {}; this.props = {}; this._ef = /* @__PURE__ */ new Map(); this._ev = /* @__PURE__ */ new Map(); this.attachShadow({ mode: "open" }); } connectedCallback() { this._sr = this.shadowRoot; this._getAttributes(this._sr?.host.attributes || []); this.setup?.(); this._update(); this.onMounted?.(); } disconnectedCallback() { this.onUnmounted?.(); } /** * Updates the component by performing a diffing algorithm on the rendered HTML. * * @param shouldShallowCompareProps - Optional parameter indicating whether to perform a shallow comparison of props before updating. * @returns void */ _update(shouldShallowCompareProps = false) { if (shouldShallowCompareProps && this._shadowCompareObject(this._op, this.props)) return; const renderString = this.render?.(this._render.bind(this)); const { body } = new DOMParser().parseFromString( renderString || "", "text/html" ); const styleElement = document.createElement("style"); styleElement.innerHTML = _Cona.style; this._pathDomDiffing(this._sr, body, styleElement); this._event(); this.onUpdated?.(); for (const [valueFn, callback] of this._ef.entries()) { const valueBeforeUpdate = this._ev.get(valueFn); const valueAfterUpdate = valueFn.bind(this)(); if (valueBeforeUpdate !== valueAfterUpdate) { callback.bind(this)(valueBeforeUpdate, valueAfterUpdate); } this._ev.set(valueFn, valueAfterUpdate); } } /** * Performs a diffing operation between two DOM nodes and updates the current node accordingly. * @param current - The current DOM node. * @param next - The next DOM node. * @param styleNode - Optional style node to be inserted at the beginning of the next node's child nodes. */ _pathDomDiffing(current, next, styleNode) { const cNodes = this._nodeMap(current.childNodes); const nNodes = this._nodeMap(next.childNodes); if (styleNode) nNodes.unshift(styleNode); let gap = cNodes.length - nNodes.length; if (gap > 0) for (; gap > 0; gap--) current.lastChild.remove(); for (const [i] of nNodes.entries()) { const c = cNodes[i]; const n = nNodes[i]; const clonedNewNode = n.cloneNode(true); const replace = () => c.parentNode.replaceChild(clonedNewNode, c); if (!c) current.append(clonedNewNode); else if (c.nodeName !== n.nodeName) replace(); else if (n.childNodes.length > 0) this._pathDomDiffing(c, n); else if (c._render) { c._getAttributes(n?.attributes); c._update(true); } else if (c.textContent !== n.textContent) replace(); if (c?.attributes) { while (c.attributes.length > 0) c.removeAttribute(c.attributes[0].name); for (const { name, value } of this._nodeMap(n?.attributes)) { c.setAttribute(name, value); } } } } _shadowCompareObject(obj1, obj2) { return Object.keys(obj1).every((key) => key in obj2 && obj1[key] === obj2[key]); } /** * Renders a template string by replacing placeholders with corresponding values. * @param stringArray - The template string array. * @param valueArray - The values to be inserted into the template string. * @returns The rendered template string. */ _render(stringArray, ...valueArray) { return stringArray.map((s, index) => { const currentValue = valueArray[index] || ""; let valueString = currentValue; if (s.endsWith("=")) { if (/(p:|on|ref).*$/.test(s)) { const key = Math.random().toString(36); _Cona._c[key] = typeof currentValue === "function" ? currentValue.bind(this) : currentValue; valueString = key; } else valueString = JSON.stringify(currentValue); } else if (Array.isArray(currentValue)) { valueString = currentValue.join(""); } return s + valueString; }).join(""); } /** * Handles event binding and sets the ref property for Cona component. * @private */ _event() { if (!this._sr) return; for (const node of this._sr.querySelectorAll("*")) { for (const { name, value } of this._nodeMap(node.attributes)) { if (name.startsWith("on")) { node[name] = (e) => _Cona._c[value].call(this, e); } if (name === "ref") _Cona._c[value].current = node; } } } /** * Sets the effect function and callback for a given value function. * The effect function is called immediately and whenever the value function changes. * * @param valueFn - The value function to track for changes. * @param callback - The callback function to execute when the value function changes. */ effect(valueFn, callback) { this._ef.set(valueFn, callback); this._ev.set(valueFn, valueFn.bind(this)()); } /** * Creates a new reference object with the specified initial value. * @param initialValue - The initial value for the reference object. * @returns A reference object with the specified initial value. * @template T - The type of the initial value. */ ref(initialValue) { return { current: initialValue }; } /** * Creates a reactive proxy for the given state object. * @param state - The state object to make reactive. * @returns A reactive proxy object. */ reactive(state) { return new Proxy(state, { set: (target, key, value) => { if (!(key in target) || target[key] !== value) { target[key] = value; if (this._t) cancelAnimationFrame(this._t); this._t = requestAnimationFrame(() => this._update()); } return true; }, get: (target, key) => { return target[key]; } }); } /** * Watches for changes in the value returned by the getter function and invokes the callback function when a change occurs. * @param getter - A function that returns the value to watch for changes. * @param callback - A function that will be called when a change occurs, with the new value and the old value as arguments. * @typeparam T - The type of the value returned by the getter function. */ watch(getter, callback) { this.effect(getter, (oldValue, newValue) => { callback(newValue, oldValue); }); } computed(getter) { let cachedValue = getter(); this.effect(getter, (_, newValue) => { cachedValue = newValue; }); return () => cachedValue; } /** * Converts a NamedNodeMap or NodeListOf to an array. * * @param attributes - The NamedNodeMap or NodeListOf to convert. * @returns An array containing the converted elements. */ _nodeMap(attributes) { return [...attributes]; } _getAttributes(attributes) { this._op = this.props; const createAttributeObject = (acc, { nodeName, nodeValue }) => ({ ...acc, [nodeName.startsWith("p:") ? nodeName.slice(2) : nodeName]: _Cona._c[nodeValue] }); this.props = this._nodeMap(attributes).reduce( (accumulator, element) => createAttributeObject(accumulator, element), {} ); } }; __publicField(_Cona, "style", ""); __publicField(_Cona, "_c", {}); let Cona = _Cona; exports.Cona = Cona;