@elenajs/core 1.0.0-alpha.1 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +1 -1
- package/dist/bundle.js.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/render.js +1 -1
- package/dist/render.js.map +1 -1
- package/package.json +13 -13
- package/LICENSE +0 -21
package/dist/bundle.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function t(t,s
|
|
1
|
+
function t(t,e,s){if(e="boolean"===t&&"boolean"!=typeof e?null!==e:e,!s)return e;if("toAttribute"===s)switch(t){case"object":case"array":return null===e?null:JSON.stringify(e);case"boolean":return e?"":null;case"number":return null===e?null:e;default:return""===e?null:e}else switch(t){case"object":case"array":if(!e)return e;try{return JSON.parse(e)}catch{return console.warn("░█ [ELENA]: Invalid JSON: "+e),null}case"boolean":return e;case"number":return null!==e?+e:e;default:return e??""}}function e(t,e,s){t?null===s?t.removeAttribute(e):t.setAttribute(e,s):console.warn("░█ [ELENA]: Cannot sync attrs.")}function s(t){return Array.isArray(t)?t.map(t=>n(t)).join(""):n(t)}function n(t){return t?.t?String(t):function(t){const e={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(t).replace(/[&<>"']/g,t=>e[t])}(String(t??""))}function i(t,...e){let n;return{t:!0,strings:t,values:e,toString:()=>(void 0===n&&(n=t.reduce((t,n,i)=>t+n+s(e[i]),"")),n)}}function r(t){return{t:!0,toString:()=>t??""}}const o=Object.freeze({t:!0,toString:()=>""}),h=t=>Array.isArray(t)?t.some(t=>t?.t):t?.t,c=t=>Array.isArray(t)?t.map(t=>String(t??"")).join(""):String(t??"");function u(t){return t.replace(/>\n\s*/g,">").replace(/\n\s*</g,"<").replace(/\n\s*/g," ")}const l=new WeakMap,a="e"+Math.random().toString(36).slice(2,6);function f(t,e,n){return!function(t,e,s){if(t.i!==e||!t.o)return!1;for(let e=0;e<s.length;e++){const n=s[e],i=Array.isArray(n)?c(n):n;if(i!==t.h[e]){if(h(n)||!t.o[e])return!1;t.h[e]=i,t.o[e].textContent=c(n)}}return!0}(t,e,n)&&(function(t,e,n){let i=l.get(e);if(!i){const t=Array.from(e,u);i={processedStrings:t,template:n.length>0?d(t,n.length):null},l.set(e,i)}if(i.template)t.o=function(t,e,n){const i=e.content.cloneNode(!0),r=document.createTreeWalker(i,NodeFilter.SHOW_COMMENT),o=new Array(n.length),u=[];let l;for(;l=r.nextNode();)l.data===a&&u.push(l);for(let t=0;t<u.length;t++){const e=n[t];if(h(e)){const n=document.createElement("template");n.innerHTML=s(e),u[t].parentNode.replaceChild(n.content,u[t])}else{const s=document.createTextNode(c(e));u[t].parentNode.replaceChild(s,u[t]),o[t]=s}}return t.replaceChildren(i),o}(t,i.template,n);else{const e=n.map(t=>s(t)),r=i.processedStrings.reduce((t,s,n)=>t+s+(e[n]??""),"").replace(/>\s+</g,"><").trim(),o=document.createElement("template");o.innerHTML=r,p(t,o.content.childNodes),t.o=new Array(n.length)}t.i=e,t.h=n.map(t=>Array.isArray(t)?c(t):t)}(t,e,n),!0)}function d(t,e){const s=`\x3c!--${a}--\x3e`,n=t.reduce((t,n,i)=>t+n.replace(/>\s+</g,"><")+(i<e?s:""),"").trim(),i=document.createElement("template");i.innerHTML=n;const r=document.createTreeWalker(i.content,NodeFilter.SHOW_COMMENT);let o=0;for(;r.nextNode();)r.currentNode.data===a&&o++;return o===e?i:null}function p(t,e){const s=Array.from(t.childNodes),n=Array.from(e),i=Math.max(s.length,n.length);for(let e=0;e<i;e++){const i=s[e],r=n[e];i?r?i.nodeType!==r.nodeType||i.nodeType===Node.ELEMENT_NODE&&i.tagName!==r.tagName?t.replaceChild(r,i):i.nodeType===Node.TEXT_NODE?i.textContent!==r.textContent&&(i.textContent=r.textContent):i.nodeType===Node.ELEMENT_NODE&&(y(i,r),p(i,r.childNodes)):t.removeChild(i):t.appendChild(r)}}function y(t,e){for(let s=t.attributes.length-1;s>=0;s--){const{name:n}=t.attributes[s];e.hasAttribute(n)||t.removeAttribute(n)}for(let s=0;s<e.attributes.length;s++){const{name:n,value:i}=e.attributes[s];t.getAttribute(n)!==i&&t.setAttribute(n,i)}}class g extends Event{constructor(t,e){super(t,{bubbles:!0,composed:!0,...e})}}const m=new WeakSet;function b(s){return class extends s{element=null;attributeChangedCallback(e,s,n){super.attributeChangedCallback?.(e,s,n),"text"!==e?(this.u=!0,function(e,s,n,i){if(n!==i){const n=typeof e[s];"undefined"===n&&console.warn(`░█ [ELENA]: Prop "${s}" has no default.`);const r=t(n,i,"toProp");e[s]=r}}(this,e,s,n),this.u=!1,this.l&&s!==n&&!this.p&&this.m()):this.text=n??""}static get observedAttributes(){if(this.A)return this.A;const t=this.S||(this.props||[]).map(t=>"string"==typeof t?t:t.name);return this.A=[...t,"text"],this.A}connectedCallback(){super.connectedCallback?.(),this.N(),this._(),this.v(),this.C(),this.willUpdate(),this.L(),this.k(),this.O(),this.P(),this.l||(this.l=!0,this.setAttribute("hydrated",""),this.firstUpdated()),this.updated()}N(){const s=this.constructor;if(m.has(s))return;const n=new Set,i=[];if(s.props){for(const t of s.props)"string"==typeof t?i.push(t):(i.push(t.name),!1===t.reflect&&n.add(t.name));i.includes("text")&&console.warn('░█ [ELENA]: "text" is reserved.'),function(s,n,i){for(const r of n){const n=!i||!i.has(r);Object.defineProperty(s,r,{configurable:!0,enumerable:!0,get(){return this.j?this.j.get(r):void 0},set(s){if(this.j||(this.j=new Map),s!==this.j.get(r)&&(this.j.set(r,s),this.isConnected))if(n){if(!this.u){const n=t(typeof s,s,"toAttribute");e(this,r,n)}}else this.l&&!this.p&&this.m()}})}}(s.prototype,i,n)}var r;s.S=i,s.M=n,s.U=s.events||null,s.q=(r=s.element)?t=>t.querySelector(r):t=>t.firstElementChild,m.add(s)}_(){this.u=!0;for(const t of this.constructor.S)if(Object.prototype.hasOwnProperty.call(this,t)){const e=this[t];delete this[t],this[t]=e}this.u=!1}v(){this.l||void 0!==this.F||(this.text=this.textContent.trim())}get J(){return this.R??this.shadowRoot??this}C(){const t=this.constructor;if(!t.shadow)return;(this.R??this.shadowRoot)||(this.R=this.attachShadow({mode:t.shadow}));const e=this.R??this.shadowRoot;if(t.styles){if(!t.I){const e=Array.isArray(t.styles)?t.styles:[t.styles];t.I=e.map(t=>{if("string"==typeof t){const e=new CSSStyleSheet;return e.replaceSync(t),e}return t})}e.adoptedStyleSheets=t.I}}L(){const t=this.render();if(t&&t.strings){const e=this.J,s=f(e,t.strings,t.values);this.l&&s&&(this.element=this.constructor.q(e))}}k(){if(!this.element){const t=this.J;this.element=this.constructor.q(t),this.element||(this.constructor.element&&console.warn("░█ [ELENA]: Element not found."),this.element=t.firstElementChild)}}O(){if(this.j){const s=this.constructor.M;for(const[n,i]of this.j){if(s.has(n))continue;const r=t(typeof i,i,"toAttribute");(null!==r||this.hasAttribute(n))&&e(this,n,r)}}}P(){const t=this.constructor.U;if(!this.W&&t?.length)if(this.element){this.W=!0;for(const e of t)this.element.addEventListener(e,this),this[e]=(...t)=>this.element[e](...t)}else console.warn("░█ [ELENA]: Cannot add events.")}render(){}willUpdate(){}firstUpdated(){}updated(){}adoptedCallback(){super.adoptedCallback?.()}disconnectedCallback(){if(super.disconnectedCallback?.(),this.W){this.W=!1;for(const t of this.constructor.U)this.element?.removeEventListener(t,this)}}handleEvent(t){this.constructor.U?.includes(t.type)&&(t.stopPropagation(),this.dispatchEvent(new g(t.type,{cancelable:!0})))}get text(){return this.F??""}set text(t){const e=this.F;this.F=t,this.l&&e!==t&&!this.p&&this.m()}static define(){var t,e;this.tagName?(t=this.tagName,e=this,"undefined"!=typeof window&&"customElements"in window&&(window.customElements.get(t)||window.customElements.define(t,e))):console.warn("░█ [ELENA]: define() without a tagName.")}m(){this.p||this.$||(this.$=!0,this.D=new Promise(t=>{this.T=t}),queueMicrotask(()=>{try{this.B()}catch(t){console.error("░█ [ELENA]:",t)}}))}B(){this.$=!1;const t=this.T;this.T=null;try{try{this.willUpdate(),this.p=!0,this.L()}finally{this.p=!1}this.updated()}finally{this.D=null,t()}}get updateComplete(){return this.D?this.D:Promise.resolve()}requestUpdate(){this.l&&!this.p&&this.m()}}}export{b as Elena,g as ElenaEvent,i as html,o as nothing,r as unsafeHTML};
|
|
2
2
|
//# sourceMappingURL=bundle.js.map
|
package/dist/bundle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sources":["../src/common/props.js","../src/common/utils.js","../src/common/render.js","../src/common/events.js","../src/elena.js"],"sourcesContent":["/**\n * Get the value of the Elena Element property.\n *\n * @param {string} type\n * @param {any} value\n * @param {\"toAttribute\" | \"toProp\"} [transform]\n */\nexport function getPropValue(type, value, transform) {\n value = type === \"boolean\" && typeof value !== \"boolean\" ? value !== null : value;\n\n if (!transform) {\n return value;\n } else if (transform === \"toAttribute\") {\n switch (type) {\n case \"object\":\n case \"array\":\n return value === null ? null : JSON.stringify(value);\n case \"boolean\":\n return value ? \"\" : null;\n case \"number\":\n return value === null ? null : value;\n default:\n return value === \"\" ? null : value;\n }\n } else {\n switch (type) {\n case \"object\":\n case \"array\":\n if (!value) {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n console.warn(\"░█ [ELENA]: Invalid JSON: \" + value);\n return null;\n }\n case \"boolean\":\n return value; // conversion already handled above\n case \"number\":\n return value !== null ? +value : value;\n default:\n return value;\n }\n }\n}\n\n/**\n * Set or remove an attribute on an Elena Element.\n *\n * @param {Element} element - Target element\n * @param {string} name - Attribute name\n * @param {string | null} value - Attribute value, or null to remove\n */\nexport function syncAttribute(element, name, value) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot sync attrs.\");\n return;\n }\n if (value === null) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/**\n * Define prop getters/setters on the prototype once\n * at class-creation time. Values are stored per-instance\n * via a `_props` Map that is lazily created.\n *\n * @param {Function} proto - The class prototype\n * @param {string[]} propNames - Prop names to define\n * @param {Set<string>} [noReflect] - Props that should not reflect to attributes\n */\nexport function setProps(proto, propNames, noReflect) {\n for (const prop of propNames) {\n const reflects = !noReflect || !noReflect.has(prop);\n Object.defineProperty(proto, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return this._props ? this._props.get(prop) : undefined;\n },\n set(value) {\n if (!this._props) {\n this._props = new Map();\n }\n if (value === this._props.get(prop)) {\n return;\n }\n\n this._props.set(prop, value);\n if (!this.isConnected) {\n return;\n }\n\n if (reflects) {\n // Skip reflection when called from attributeChangedCallback. The\n // attribute is already at the new value, setting it again is redundant\n // and would fire an extra attributeChangedCallback with identical values.\n if (!this._syncing) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n }\n } else if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n },\n });\n }\n}\n\n/**\n * We need to update the internals of the Elena Element\n * when props on the host element are changed.\n *\n * @param {object} context\n * @param {string} name\n * @param {any} oldValue\n * @param {any} newValue\n */\nexport function getProps(context, name, oldValue, newValue) {\n if (oldValue !== newValue) {\n const type = typeof context[name];\n if (type === \"undefined\") {\n console.warn(`░█ [ELENA]: Prop \"${name}\" has no default.`);\n }\n const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n","/**\n * Register the Elena Element if the browser supports it.\n *\n * @param {string} tagName\n * @param {Function} Element\n */\nexport function defineElement(tagName, Element) {\n if (typeof window !== \"undefined\" && \"customElements\" in window) {\n if (!window.customElements.get(tagName)) {\n window.customElements.define(tagName, Element);\n }\n }\n}\n\n/**\n * Escape a string for safe insertion into HTML.\n *\n * @param {string} str\n * @returns {string}\n */\nexport function escapeHtml(str) {\n const Escape = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\n return String(str).replace(/[&<>\"']/g, c => Escape[c]);\n}\n\n/**\n * Resolve an interpolated template value to its\n * HTML string representation.\n *\n * @param {*} value\n * @returns {string}\n */\nexport function resolveValue(value) {\n if (Array.isArray(value)) {\n return value.map(item => resolveItem(item)).join(\"\");\n }\n return resolveItem(value);\n}\n\n/**\n * Resolve a single value to its HTML string\n * representation.\n *\n * @param {*} value\n * @returns {string}\n */\nfunction resolveItem(value) {\n return value?.__raw ? String(value) : escapeHtml(String(value ?? \"\"));\n}\n\n/**\n * Tagged template for trusted HTML. Use as the return value\n * of render(), or for sub-fragments inside render methods.\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n * @returns {{ __raw: true, strings: TemplateStringsArray, values: Array, toString(): string }}\n */\nexport function html(strings, ...values) {\n let str;\n return {\n __raw: true,\n strings,\n values,\n toString: () => {\n if (str === undefined) {\n str = strings.reduce((acc, s, i) => {\n return acc + s + resolveValue(values[i]);\n }, \"\");\n }\n return str;\n },\n };\n}\n\n/**\n * Renders a string as HTML rather than text.\n *\n * @param {string} str - The raw HTML string to trust.\n * @returns {{ __raw: true, toString(): string }}\n */\nexport function unsafeHTML(str) {\n return { __raw: true, toString: () => str ?? \"\" };\n}\n\n/**\n * A placeholder you can return from a conditional expression\n * inside a template to render nothing.\n *\n * @type {{ __raw: true, toString(): string }}\n */\nexport const nothing = Object.freeze({ __raw: true, toString: () => \"\" });\n\n/**\n * Check if a value contains trusted HTML fragments.\n *\n * @param {*} value\n * @returns {boolean}\n */\nexport const isRaw = value =>\n Array.isArray(value) ? value.some(item => item?.__raw) : value?.__raw;\n\n/**\n * Convert a value to its plain text string.\n *\n * @param {*} value\n * @returns {string}\n */\nexport const toPlainText = value =>\n Array.isArray(value) ? value.map(item => String(item ?? \"\")).join(\"\") : String(value ?? \"\");\n\n/**\n * Collapse whitespace from a static string part.\n *\n * @param {string} string\n * @returns {string}\n */\nexport function collapseWhitespace(string) {\n return string\n .replace(/>\\n\\s*/g, \">\") // newline after tag close\n .replace(/\\n\\s*</g, \"<\") // newline before tag open\n .replace(/\\n\\s*/g, \" \"); // newline in text content, preserve word boundary\n}\n","import { collapseWhitespace, isRaw, resolveValue, toPlainText } from \"./utils.js\";\n\nconst stringsCache = new WeakMap();\nconst markerKey = \"e\" + Math.random().toString(36).slice(2, 6);\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n * Returns true if the DOM was fully rebuilt, false if only text\n * nodes were patched in place.\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean}\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return false;\n }\n fullRender(element, strings, values);\n return true;\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n *\n * @param {HTMLElement} element - The host element with cached template state\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean} Whether patching was sufficient (false = full render)\n */\nfunction patchTextNodes(element, strings, values) {\n // Only works when re-rendering the same template shape\n if (element._tplStrings !== strings || !element._tplParts) {\n return false;\n }\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const comparable = Array.isArray(v) ? toPlainText(v) : v;\n\n if (comparable === element._tplValues[i]) {\n continue;\n }\n\n if (isRaw(v) || !element._tplParts[i]) {\n return false;\n }\n\n element._tplValues[i] = comparable;\n element._tplParts[i].textContent = toPlainText(v);\n }\n\n return true;\n}\n\n/**\n * Cold path: clone a cached <template> and patch in values.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nfunction fullRender(element, strings, values) {\n let entry = stringsCache.get(strings);\n\n if (!entry) {\n const processedStrings = Array.from(strings, collapseWhitespace);\n entry = {\n processedStrings,\n template: values.length > 0 ? createTemplate(processedStrings, values.length) : null,\n };\n stringsCache.set(strings, entry);\n }\n\n if (entry.template) {\n element._tplParts = cloneAndPatch(element, entry.template, values);\n } else {\n // Fallback for attribute-position values or static templates.\n // White space collapsing here protects against Vue SSR mismatches.\n const renderedValues = values.map(value => resolveValue(value));\n const markup = entry.processedStrings\n .reduce((out, str, i) => out + str + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .trim();\n\n element.innerHTML = markup;\n element._tplParts = new Array(values.length);\n }\n\n element._tplStrings = strings;\n element._tplValues = values.map(v => (Array.isArray(v) ? toPlainText(v) : v));\n}\n\n/**\n * Build a <template> element with comment markers.\n *\n * @param {string[]} processedStrings - Whitespace-collapsed static parts\n * @param {number} valueCount - Number of dynamic values\n * @returns {HTMLTemplateElement | null}\n */\nfunction createTemplate(processedStrings, valueCount) {\n const marker = `<!--${markerKey}-->`;\n const markup = processedStrings\n .reduce((out, str, i) => {\n const collapsed = str.replace(/>\\s+</g, \"><\");\n return out + collapsed + (i < valueCount ? marker : \"\");\n }, \"\")\n .trim();\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n\n // Mismatch means this template shape cannot use the clone path.\n const walker = document.createTreeWalker(tpl.content, NodeFilter.SHOW_COMMENT);\n let count = 0;\n\n while (walker.nextNode()) {\n if (walker.currentNode.data === markerKey) {\n count++;\n }\n }\n\n return count === valueCount ? tpl : null;\n}\n\n/**\n * Clone a cached template and replace comment markers\n * with actual content.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {HTMLTemplateElement} template - Cached template with markers\n * @param {Array} values - Raw interpolated values\n * @param {string[]} renderedValues - HTML-escaped rendered values\n * @returns {Array<Text | undefined>} Text node map for fast-path patching\n */\nfunction cloneAndPatch(element, template, values) {\n const clone = template.content.cloneNode(true);\n const walker = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT);\n const parts = new Array(values.length);\n const markers = [];\n let node;\n\n // Collect markers before modifying the tree\n while ((node = walker.nextNode())) {\n if (node.data === markerKey) {\n markers.push(node);\n }\n }\n\n for (let i = 0; i < markers.length; i++) {\n const value = values[i];\n\n if (isRaw(value)) {\n // Raw HTML: parse and insert as fragment\n const tmp = document.createElement(\"template\");\n tmp.innerHTML = resolveValue(value);\n markers[i].parentNode.replaceChild(tmp.content, markers[i]);\n\n // Raw values can't be fast-patched; leave parts undefined\n } else {\n // Create text node with unescaped content\n const textNode = document.createTextNode(toPlainText(value));\n markers[i].parentNode.replaceChild(textNode, markers[i]);\n parts[i] = textNode;\n }\n }\n\n element.replaceChildren(clone);\n return parts;\n}\n","/**\n * A base class for Elena Element’s events which\n * defaults to bubbling and composed.\n */\nexport class ElenaEvent extends Event {\n constructor(type, eventInitDict) {\n super(type, {\n bubbles: true,\n composed: true,\n ...eventInitDict,\n });\n }\n}\n","/**\n * ██████████ ████\n * ░░███░░░░░█░░███\n * ░███ █ ░ ░███ ██████ ████████ ██████\n * ░██████ ░███ ███░░███░░███░░███ ░░░░░███\n * ░███░░█ ░███ ░███████ ░███ ░███ ███████\n * ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███\n * ██████████ █████░░██████ ████ █████░░████████\n * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░\n *\n * Elena Progressive Web Components\n * https://elenajs.com\n */\n\nimport { setProps, getProps, getPropValue, syncAttribute } from \"./common/props.js\";\nimport { defineElement, html, unsafeHTML, nothing } from \"./common/utils.js\";\nimport { renderTemplate } from \"./common/render.js\";\nimport { ElenaEvent } from \"./common/events.js\";\n\nexport { html, unsafeHTML, nothing, ElenaEvent };\n\n/**\n * Returns a function that finds the inner element using the given selector.\n * Built once per component class to avoid repeated work.\n *\n * - No selector: uses firstElementChild\n * - Any string: uses querySelector\n *\n * @param {string | undefined} selector\n * @returns {(host: HTMLElement) => HTMLElement | null}\n */\nfunction elementResolver(selector) {\n if (!selector) {\n return host => host.firstElementChild;\n }\n return host => host.querySelector(selector);\n}\n\n/**\n * @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor\n */\n\n/**\n * @typedef {{ text: string, element: HTMLElement | null, render(): void, willUpdate(): void, firstUpdated(): void, updated(): void, connectedCallback(): void, disconnectedCallback(): void }} ElenaInstanceMembers\n */\n\n/**\n * @typedef {{ name: string, reflect?: boolean }} ElenaPropObject\n */\n\n/**\n * @typedef {(new (...args: any[]) => HTMLElement & ElenaInstanceMembers) & {\n * define(): void,\n * readonly observedAttributes: string[],\n * tagName?: string,\n * props?: (string | ElenaPropObject)[],\n * events?: string[],\n * element?: string,\n * shadow?: \"open\" | \"closed\",\n * styles?: CSSStyleSheet | string | (CSSStyleSheet | string)[],\n * }} ElenaElementConstructor\n */\n\n// Tracks which component classes have already been set up.\nconst setupRegistry = new WeakSet();\n\n/**\n * Creates an Elena component class by extending `superClass`.\n *\n * Adds rendering, props, and event handling to your component.\n * Configure it using static class fields: `static tagName`,\n * `static props`, `static events`, and `static element`.\n *\n * @param {ElenaConstructor} superClass - The base class to extend (usually `HTMLElement`).\n * @returns {ElenaElementConstructor} A class ready to be defined as a custom element.\n */\nexport function Elena(superClass) {\n /**\n * The base Elena element class with all built-in behavior.\n */\n class ElenaElement extends superClass {\n /**\n * The inner element rendered by this component.\n *\n * @type {HTMLElement | null}\n */\n element = null;\n\n /**\n * Called by the browser when an observed attribute changes.\n * Updates the matching prop and re-renders if needed.\n *\n * @param {string} prop\n * @param {string} oldValue\n * @param {string} newValue\n */\n attributeChangedCallback(prop, oldValue, newValue) {\n super.attributeChangedCallback?.(prop, oldValue, newValue);\n\n if (prop === \"text\") {\n this.text = newValue ?? \"\";\n return;\n }\n\n // Set flag so the property setter skips redundant attribute reflection:\n // the attribute is already at the new value, no need to set it again.\n this._syncing = true;\n getProps(this, prop, oldValue, newValue);\n this._syncing = false;\n\n // Re-render when attributes change (after initial render).\n // Guard against re-entrant renders: if render() itself mutates an observed\n // attribute, skip the recursive call to prevent an infinite loop.\n if (this._hydrated && oldValue !== newValue && !this._isRendering) {\n this._safeRender();\n }\n }\n\n /**\n * Lists the attributes Elena watches for changes.\n * Reads from the subclass’s `static props` field.\n */\n static get observedAttributes() {\n if (this._observedAttrs) {\n return this._observedAttrs;\n }\n\n const propNames =\n this._propNames || (this.props || []).map(p => (typeof p === \"string\" ? p : p.name));\n this._observedAttrs = [...propNames, \"text\"];\n return this._observedAttrs;\n }\n\n /**\n * Called by the browser each time the element is added to the page.\n */\n connectedCallback() {\n super.connectedCallback?.();\n this._setupStaticProps();\n this._captureClassFieldDefaults();\n this._captureText();\n this._attachShadow();\n this.willUpdate();\n this._applyRender();\n this._resolveInnerElement();\n this._syncProps();\n this._delegateEvents();\n if (!this._hydrated) {\n this._hydrated = true;\n this.setAttribute(\"hydrated\", \"\");\n this.firstUpdated();\n }\n this.updated();\n }\n\n /**\n * Sets up props, events, and the element selector once per component class.\n * Runs the first time an instance of a given class connects to the page.\n *\n * @internal\n */\n _setupStaticProps() {\n const component = this.constructor;\n\n if (setupRegistry.has(component)) {\n return;\n }\n\n // Props with reflect: false\n const noRef = new Set();\n const names = [];\n\n if (component.props) {\n for (const p of component.props) {\n if (typeof p === \"string\") {\n names.push(p);\n } else {\n names.push(p.name);\n\n if (p.reflect === false) {\n noRef.add(p.name);\n }\n }\n }\n\n if (names.includes(\"text\")) {\n console.warn('░█ [ELENA]: \"text\" is reserved.');\n }\n\n setProps(component.prototype, names, noRef);\n }\n\n component._propNames = names;\n component._noReflect = noRef;\n component._elenaEvents = component.events || null;\n component._resolver = elementResolver(component.element);\n setupRegistry.add(component);\n }\n\n /**\n * Moves class field defaults into Elena’s internal props store\n * so that getters and setters work correctly.\n *\n * @internal\n */\n _captureClassFieldDefaults() {\n this._syncing = true;\n\n for (const name of this.constructor._propNames) {\n if (Object.prototype.hasOwnProperty.call(this, name)) {\n const value = this[name];\n delete this[name];\n this[name] = value;\n }\n }\n\n this._syncing = false;\n }\n\n /**\n * Saves any text inside the element before the first render.\n *\n * @internal\n */\n _captureText() {\n if (!this._hydrated && this._text === undefined) {\n this.text = this.textContent.trim();\n }\n }\n\n /**\n * The root node to render into. Returns the shadow root when shadow mode\n * is enabled, otherwise the host element itself.\n *\n * @type {ShadowRoot | HTMLElement}\n */\n get _renderRoot() {\n return this._shadow ?? this.shadowRoot ?? this;\n }\n\n /**\n * Attaches a shadow root and adopts styles on first connect.\n * Only runs when `static shadow` is set on the component class.\n *\n * @internal\n */\n _attachShadow() {\n const component = this.constructor;\n\n if (!component.shadow) {\n return;\n }\n\n // A shadow root may already exist if Declarative Shadow DOM was used.\n // In that case skip attachShadow() but still adopt styles below.\n // Store the reference so closed shadow roots remain accessible.\n const root = this._shadow ?? this.shadowRoot;\n\n if (!root) {\n this._shadow = this.attachShadow({ mode: component.shadow });\n }\n\n const shadowRoot = this._shadow ?? this.shadowRoot;\n\n if (!component.styles) {\n return;\n }\n\n // Normalize to array and cache converted CSSStyleSheet instances on the class.\n // Avoids re-parsing CSS strings on every element instance.\n if (!component._adoptedSheets) {\n const stylesList = Array.isArray(component.styles) ? component.styles : [component.styles];\n\n component._adoptedSheets = stylesList.map(s => {\n if (typeof s === \"string\") {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(s);\n return sheet;\n }\n return s;\n });\n }\n\n shadowRoot.adoptedStyleSheets = component._adoptedSheets;\n }\n\n /**\n * Calls render() and updates the DOM with the result.\n *\n * @internal\n */\n _applyRender() {\n const result = this.render();\n\n if (result && result.strings) {\n const root = this._renderRoot;\n const rebuilt = renderTemplate(root, result.strings, result.values);\n\n // Re-resolve element ref only when the DOM was fully rebuilt.\n // Fast-path text node patching leaves the DOM structure intact,\n // so the existing ref is still valid.\n if (this._hydrated && rebuilt) {\n this.element = this.constructor._resolver(root);\n }\n }\n }\n\n /**\n * Finds and stores a reference to the inner element.\n *\n * @internal\n */\n _resolveInnerElement() {\n if (!this.element) {\n const root = this._renderRoot;\n this.element = this.constructor._resolver(root);\n\n if (!this.element) {\n if (this.constructor.element) {\n console.warn(\"░█ [ELENA]: Element not found.\");\n }\n this.element = root.firstElementChild;\n }\n }\n }\n\n /**\n * Syncs any props that were set before the element\n * connected to the page.\n *\n * @internal\n */\n _syncProps() {\n if (this._props) {\n const noReflect = this.constructor._noReflect;\n\n for (const [prop, value] of this._props) {\n if (noReflect.has(prop)) {\n continue;\n }\n\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n\n if (attrValue === null && !this.hasAttribute(prop)) {\n continue;\n }\n\n syncAttribute(this, prop, attrValue);\n }\n }\n }\n\n /**\n * Forwards events from the inner element\n * up to the host element.\n *\n * @internal\n */\n _delegateEvents() {\n const events = this.constructor._elenaEvents;\n\n if (!this._events && events?.length) {\n if (!this.element) {\n console.warn(\"░█ [ELENA]: Cannot add events.\");\n } else {\n this._events = true;\n\n for (const e of events) {\n this.element.addEventListener(e, this);\n this[e] = (...args) => this.element[e](...args);\n }\n }\n }\n }\n\n /**\n * Define the element’s HTML here. Return an `html`\n * tagged template. If not overridden, the element connects\n * to the page without rendering anything.\n */\n render() {}\n\n /**\n * Called before every render.\n * Override to prepare state before the template runs.\n */\n willUpdate() {}\n\n /**\n * Called once after the element’s first render.\n * Override to run setup that needs the DOM.\n */\n firstUpdated() {}\n\n /**\n * Called after every render.\n * Override to react to changes.\n */\n updated() {}\n\n /**\n * Called by the browser when the element is moved\n * to a new document via `adoptNode()`.\n */\n adoptedCallback() {\n super.adoptedCallback?.();\n }\n\n /**\n * Called by the browser each time the element\n * is removed from the page.\n */\n disconnectedCallback() {\n super.disconnectedCallback?.();\n if (this._events) {\n this._events = false;\n\n for (const e of this.constructor._elenaEvents) {\n this.element?.removeEventListener(e, this);\n }\n }\n }\n\n /**\n * Receives events from the inner element and\n * re-fires them on the host.\n *\n * @internal\n */\n handleEvent(event) {\n if (this.constructor._elenaEvents?.includes(event.type)) {\n event.stopPropagation();\n\n /** @internal */\n this.dispatchEvent(new ElenaEvent(event.type, { cancelable: true }));\n }\n }\n\n /**\n * The text content of the element. Elena reads this\n * from the element’s children before the first render.\n * Updating it triggers a re-render.\n *\n * @type {string}\n */\n get text() {\n return this._text ?? \"\";\n }\n\n set text(value) {\n const old = this._text;\n this._text = value;\n\n if (this._hydrated && old !== value && !this._isRendering) {\n this._safeRender();\n }\n }\n\n /**\n * Registers the component as a custom element using `static tagName`.\n * Call this on your component class after the class body is defined,\n * not on the Elena mixin itself.\n */\n static define() {\n if (this.tagName) {\n defineElement(this.tagName, this);\n } else {\n console.warn(\"░█ [ELENA]: define() without a tagName.\");\n }\n }\n\n /**\n * Schedules a re-render via microtask. If called multiple times\n * before the microtask fires, only one render runs.\n *\n * @internal\n */\n _safeRender() {\n if (this._isRendering) {\n return;\n }\n if (!this._renderPending) {\n this._renderPending = true;\n this._updateComplete = new Promise(resolve => {\n this._resolveUpdate = resolve;\n });\n queueMicrotask(() => {\n try {\n this._performUpdate();\n } catch (e) {\n console.error(\"░█ [ELENA]:\", e);\n }\n });\n }\n }\n\n /**\n * Runs the batched update cycle.\n * Called by the microtask in _safeRender().\n *\n * @internal\n */\n _performUpdate() {\n this._renderPending = false;\n const resolve = this._resolveUpdate;\n this._resolveUpdate = null;\n try {\n try {\n this.willUpdate();\n this._isRendering = true;\n this._applyRender();\n } finally {\n this._isRendering = false;\n }\n this.updated();\n } finally {\n this._updateComplete = null;\n resolve();\n }\n }\n\n /**\n * A Promise that resolves after the render completes.\n * Resolves immediately if no render is scheduled.\n *\n * @type {Promise<void>}\n */\n get updateComplete() {\n if (this._updateComplete) {\n return this._updateComplete;\n }\n return Promise.resolve();\n }\n\n /**\n * Schedules a re-render. Use this to manually trigger an\n * update when Elena cannot detect the change automatically.\n */\n requestUpdate() {\n if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n }\n }\n\n return ElenaElement;\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","console","warn","syncAttribute","element","name","removeAttribute","setAttribute","resolveValue","Array","isArray","map","item","resolveItem","join","__raw","String","str","Escape","replace","c","escapeHtml","html","strings","values","toString","undefined","reduce","acc","s","i","unsafeHTML","nothing","Object","freeze","isRaw","some","toPlainText","collapseWhitespace","string","stringsCache","WeakMap","markerKey","Math","random","slice","renderTemplate","_tplStrings","_tplParts","length","v","comparable","_tplValues","textContent","patchTextNodes","entry","get","processedStrings","from","template","createTemplate","set","clone","content","cloneNode","walker","document","createTreeWalker","NodeFilter","SHOW_COMMENT","parts","markers","node","nextNode","data","push","tmp","createElement","innerHTML","parentNode","replaceChild","textNode","createTextNode","replaceChildren","cloneAndPatch","renderedValues","markup","out","trim","fullRender","valueCount","marker","tpl","count","currentNode","ElenaEvent","Event","constructor","eventInitDict","super","bubbles","composed","setupRegistry","WeakSet","Elena","superClass","attributeChangedCallback","prop","oldValue","newValue","this","_syncing","context","newAttr","getProps","_hydrated","_isRendering","_safeRender","text","observedAttributes","_observedAttrs","propNames","_propNames","props","p","connectedCallback","_setupStaticProps","_captureClassFieldDefaults","_captureText","_attachShadow","willUpdate","_applyRender","_resolveInnerElement","_syncProps","_delegateEvents","firstUpdated","updated","component","has","noRef","Set","names","reflect","add","includes","proto","noReflect","reflects","defineProperty","configurable","enumerable","_props","Map","isConnected","attrValue","setProps","prototype","selector","_noReflect","_elenaEvents","events","_resolver","host","querySelector","firstElementChild","hasOwnProperty","call","_text","_renderRoot","_shadow","shadowRoot","shadow","attachShadow","mode","styles","_adoptedSheets","stylesList","sheet","CSSStyleSheet","replaceSync","adoptedStyleSheets","result","render","root","rebuilt","hasAttribute","_events","e","addEventListener","args","adoptedCallback","disconnectedCallback","removeEventListener","handleEvent","event","stopPropagation","dispatchEvent","cancelable","old","define","tagName","Element","window","customElements","_renderPending","_updateComplete","Promise","resolve","_resolveUpdate","queueMicrotask","_performUpdate","error","updateComplete","requestUpdate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle.js","sources":["../src/common/props.js","../src/common/utils.js","../src/common/render.js","../src/common/events.js","../src/elena.js"],"sourcesContent":["/**\n * Get the value of the Elena Element property.\n *\n * @param {string} type\n * @param {any} value\n * @param {\"toAttribute\" | \"toProp\"} [transform]\n */\nexport function getPropValue(type, value, transform) {\n value = type === \"boolean\" && typeof value !== \"boolean\" ? value !== null : value;\n\n if (!transform) {\n return value;\n } else if (transform === \"toAttribute\") {\n switch (type) {\n case \"object\":\n case \"array\":\n return value === null ? null : JSON.stringify(value);\n case \"boolean\":\n return value ? \"\" : null;\n case \"number\":\n return value === null ? null : value;\n default:\n return value === \"\" ? null : value;\n }\n } else {\n switch (type) {\n case \"object\":\n case \"array\":\n if (!value) {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n console.warn(\"░█ [ELENA]: Invalid JSON: \" + value);\n return null;\n }\n case \"boolean\":\n return value; // conversion already handled above\n case \"number\":\n return value !== null ? +value : value;\n default:\n return value ?? \"\";\n }\n }\n}\n\n/**\n * Set or remove an attribute on an Elena Element.\n *\n * @param {Element} element - Target element\n * @param {string} name - Attribute name\n * @param {string | null} value - Attribute value, or null to remove\n */\nexport function syncAttribute(element, name, value) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot sync attrs.\");\n return;\n }\n if (value === null) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/**\n * Define prop getters/setters on the prototype once\n * at class-creation time. Values are stored per-instance\n * via a `_props` Map that is lazily created.\n *\n * @param {Function} proto - The class prototype\n * @param {string[]} propNames - Prop names to define\n * @param {Set<string>} [noReflect] - Props that should not reflect to attributes\n */\nexport function setProps(proto, propNames, noReflect) {\n for (const prop of propNames) {\n const reflects = !noReflect || !noReflect.has(prop);\n Object.defineProperty(proto, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return this._props ? this._props.get(prop) : undefined;\n },\n set(value) {\n if (!this._props) {\n this._props = new Map();\n }\n if (value === this._props.get(prop)) {\n return;\n }\n\n this._props.set(prop, value);\n if (!this.isConnected) {\n return;\n }\n\n if (reflects) {\n // Skip reflection when called from attributeChangedCallback. The\n // attribute is already at the new value, setting it again is redundant\n // and would fire an extra attributeChangedCallback with identical values.\n if (!this._syncing) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n }\n } else if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n },\n });\n }\n}\n\n/**\n * We need to update the internals of the Elena Element\n * when props on the host element are changed.\n *\n * @param {object} context\n * @param {string} name\n * @param {any} oldValue\n * @param {any} newValue\n */\nexport function getProps(context, name, oldValue, newValue) {\n if (oldValue !== newValue) {\n const type = typeof context[name];\n if (type === \"undefined\") {\n console.warn(`░█ [ELENA]: Prop \"${name}\" has no default.`);\n }\n const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n","/**\n * Register the Elena Element if the browser supports it.\n *\n * @param {string} tagName\n * @param {Function} Element\n */\nexport function defineElement(tagName, Element) {\n if (typeof window !== \"undefined\" && \"customElements\" in window) {\n if (!window.customElements.get(tagName)) {\n window.customElements.define(tagName, Element);\n }\n }\n}\n\n/**\n * Escape a string for safe insertion into HTML.\n *\n * @param {string} str\n * @returns {string}\n */\nexport function escapeHtml(str) {\n const Escape = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\n return String(str).replace(/[&<>\"']/g, c => Escape[c]);\n}\n\n/**\n * Resolve an interpolated template value to its\n * HTML string representation.\n *\n * @param {*} value\n * @returns {string}\n */\nexport function resolveValue(value) {\n if (Array.isArray(value)) {\n return value.map(item => resolveItem(item)).join(\"\");\n }\n return resolveItem(value);\n}\n\n/**\n * Resolve a single value to its HTML string\n * representation.\n *\n * @param {*} value\n * @returns {string}\n */\nfunction resolveItem(value) {\n return value?.__raw ? String(value) : escapeHtml(String(value ?? \"\"));\n}\n\n/**\n * Tagged template for trusted HTML. Use as the return value\n * of render(), or for sub-fragments inside render methods.\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n * @returns {{ __raw: true, strings: TemplateStringsArray, values: Array, toString(): string }}\n */\nexport function html(strings, ...values) {\n let str;\n return {\n __raw: true,\n strings,\n values,\n toString: () => {\n if (str === undefined) {\n str = strings.reduce((acc, s, i) => {\n return acc + s + resolveValue(values[i]);\n }, \"\");\n }\n return str;\n },\n };\n}\n\n/**\n * Renders a string as HTML rather than text.\n *\n * @param {string} str - The raw HTML string to trust.\n * @returns {{ __raw: true, toString(): string }}\n */\nexport function unsafeHTML(str) {\n return { __raw: true, toString: () => str ?? \"\" };\n}\n\n/**\n * A placeholder you can return from a conditional expression\n * inside a template to render nothing.\n *\n * @type {{ __raw: true, toString(): string }}\n */\nexport const nothing = Object.freeze({ __raw: true, toString: () => \"\" });\n\n/**\n * Check if a value contains trusted HTML fragments.\n *\n * @param {*} value\n * @returns {boolean}\n */\nexport const isRaw = value =>\n Array.isArray(value) ? value.some(item => item?.__raw) : value?.__raw;\n\n/**\n * Convert a value to its plain text string.\n *\n * @param {*} value\n * @returns {string}\n */\nexport const toPlainText = value =>\n Array.isArray(value) ? value.map(item => String(item ?? \"\")).join(\"\") : String(value ?? \"\");\n\n/**\n * Collapse whitespace from a static string part.\n *\n * @param {string} string\n * @returns {string}\n */\nexport function collapseWhitespace(string) {\n return string\n .replace(/>\\n\\s*/g, \">\") // newline after tag close\n .replace(/\\n\\s*</g, \"<\") // newline before tag open\n .replace(/\\n\\s*/g, \" \"); // newline in text content, preserve word boundary\n}\n","import { collapseWhitespace, isRaw, resolveValue, toPlainText } from \"./utils.js\";\n\nconst stringsCache = new WeakMap();\nconst markerKey = \"e\" + Math.random().toString(36).slice(2, 6);\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n * Returns true if the DOM was fully rebuilt, false if only text\n * nodes were patched in place.\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean}\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return false;\n }\n fullRender(element, strings, values);\n return true;\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n *\n * @param {HTMLElement} element - The host element with cached template state\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean} Whether patching was sufficient (false = full render)\n */\nfunction patchTextNodes(element, strings, values) {\n // Only works when re-rendering the same template shape\n if (element._tplStrings !== strings || !element._tplParts) {\n return false;\n }\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const comparable = Array.isArray(v) ? toPlainText(v) : v;\n\n if (comparable === element._tplValues[i]) {\n continue;\n }\n\n if (isRaw(v) || !element._tplParts[i]) {\n return false;\n }\n\n element._tplValues[i] = comparable;\n element._tplParts[i].textContent = toPlainText(v);\n }\n\n return true;\n}\n\n/**\n * Cold path: clone a cached <template> and patch in values.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nfunction fullRender(element, strings, values) {\n let entry = stringsCache.get(strings);\n\n if (!entry) {\n const processedStrings = Array.from(strings, collapseWhitespace);\n entry = {\n processedStrings,\n template: values.length > 0 ? createTemplate(processedStrings, values.length) : null,\n };\n stringsCache.set(strings, entry);\n }\n\n if (entry.template) {\n element._tplParts = cloneAndPatch(element, entry.template, values);\n } else {\n // Fallback for attribute-position values or static templates.\n // White space collapsing here protects against Vue SSR mismatches.\n const renderedValues = values.map(value => resolveValue(value));\n const markup = entry.processedStrings\n .reduce((out, str, i) => out + str + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .trim();\n\n // Morph existing DOM to match new markup instead of replacing it.\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n morphContent(element, tpl.content.childNodes);\n element._tplParts = new Array(values.length);\n }\n\n element._tplStrings = strings;\n element._tplValues = values.map(v => (Array.isArray(v) ? toPlainText(v) : v));\n}\n\n/**\n * Build a <template> element with comment markers.\n *\n * @param {string[]} processedStrings - Whitespace-collapsed static parts\n * @param {number} valueCount - Number of dynamic values\n * @returns {HTMLTemplateElement | null}\n */\nfunction createTemplate(processedStrings, valueCount) {\n const marker = `<!--${markerKey}-->`;\n const markup = processedStrings\n .reduce((out, str, i) => {\n const collapsed = str.replace(/>\\s+</g, \"><\");\n return out + collapsed + (i < valueCount ? marker : \"\");\n }, \"\")\n .trim();\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n\n // Mismatch means this template shape cannot use the clone path.\n const walker = document.createTreeWalker(tpl.content, NodeFilter.SHOW_COMMENT);\n let count = 0;\n\n while (walker.nextNode()) {\n if (walker.currentNode.data === markerKey) {\n count++;\n }\n }\n\n return count === valueCount ? tpl : null;\n}\n\n/**\n * Clone a cached template and replace comment markers\n * with actual content.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {HTMLTemplateElement} template - Cached template with markers\n * @param {Array} values - Raw interpolated values\n * @param {string[]} renderedValues - HTML-escaped rendered values\n * @returns {Array<Text | undefined>} Text node map for fast-path patching\n */\nfunction cloneAndPatch(element, template, values) {\n const clone = template.content.cloneNode(true);\n const walker = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT);\n const parts = new Array(values.length);\n const markers = [];\n let node;\n\n // Collect markers before modifying the tree\n while ((node = walker.nextNode())) {\n if (node.data === markerKey) {\n markers.push(node);\n }\n }\n\n for (let i = 0; i < markers.length; i++) {\n const value = values[i];\n\n if (isRaw(value)) {\n // Raw HTML: parse and insert as fragment\n const tmp = document.createElement(\"template\");\n tmp.innerHTML = resolveValue(value);\n markers[i].parentNode.replaceChild(tmp.content, markers[i]);\n\n // Raw values can't be fast-patched; leave parts undefined\n } else {\n // Create text node with unescaped content\n const textNode = document.createTextNode(toPlainText(value));\n markers[i].parentNode.replaceChild(textNode, markers[i]);\n parts[i] = textNode;\n }\n }\n\n element.replaceChildren(clone);\n return parts;\n}\n\n/**\n * Patches attributes and text content in-place when structure is stable,\n * preserving element identity and focus state across re-renders.\n *\n * @param {Node} parent\n * @param {NodeList} nextNodes - The desired child nodes from the new render\n */\nfunction morphContent(parent, nextNodes) {\n const current = Array.from(parent.childNodes);\n const next = Array.from(nextNodes);\n const len = Math.max(current.length, next.length);\n\n for (let i = 0; i < len; i++) {\n const cur = current[i];\n const nxt = next[i];\n\n if (!cur) {\n parent.appendChild(nxt);\n } else if (!nxt) {\n parent.removeChild(cur);\n } else if (\n cur.nodeType !== nxt.nodeType ||\n (cur.nodeType === Node.ELEMENT_NODE && cur.tagName !== nxt.tagName)\n ) {\n parent.replaceChild(nxt, cur);\n } else if (cur.nodeType === Node.TEXT_NODE) {\n if (cur.textContent !== nxt.textContent) {\n cur.textContent = nxt.textContent;\n }\n } else if (cur.nodeType === Node.ELEMENT_NODE) {\n morphAttributes(cur, nxt);\n morphContent(cur, nxt.childNodes);\n }\n }\n}\n\n/**\n * Morhp element’s attributes without rebuilding the DOM.\n *\n * @param {Element} current - The current existing DOM element\n * @param {Element} next - The desired element from the new render\n */\nfunction morphAttributes(current, next) {\n for (let i = current.attributes.length - 1; i >= 0; i--) {\n const { name } = current.attributes[i];\n\n if (!next.hasAttribute(name)) {\n current.removeAttribute(name);\n }\n }\n\n for (let i = 0; i < next.attributes.length; i++) {\n const { name, value } = next.attributes[i];\n\n if (current.getAttribute(name) !== value) {\n current.setAttribute(name, value);\n }\n }\n}\n","/**\n * A base class for Elena Element’s events which\n * defaults to bubbling and composed.\n */\nexport class ElenaEvent extends Event {\n constructor(type, eventInitDict) {\n super(type, {\n bubbles: true,\n composed: true,\n ...eventInitDict,\n });\n }\n}\n","/**\n * ██████████ ████\n * ░░███░░░░░█░░███\n * ░███ █ ░ ░███ ██████ ████████ ██████\n * ░██████ ░███ ███░░███░░███░░███ ░░░░░███\n * ░███░░█ ░███ ░███████ ░███ ░███ ███████\n * ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███\n * ██████████ █████░░██████ ████ █████░░████████\n * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░\n *\n * Elena Progressive Web Components\n * https://elenajs.com\n */\n\nimport { setProps, getProps, getPropValue, syncAttribute } from \"./common/props.js\";\nimport { defineElement, html, unsafeHTML, nothing } from \"./common/utils.js\";\nimport { renderTemplate } from \"./common/render.js\";\nimport { ElenaEvent } from \"./common/events.js\";\n\nexport { html, unsafeHTML, nothing, ElenaEvent };\n\n/**\n * Returns a function that finds the inner element using the given selector.\n * Built once per component class to avoid repeated work.\n *\n * - No selector: uses firstElementChild\n * - Any string: uses querySelector\n *\n * @param {string | undefined} selector\n * @returns {(host: HTMLElement) => HTMLElement | null}\n */\nfunction elementResolver(selector) {\n if (!selector) {\n return host => host.firstElementChild;\n }\n return host => host.querySelector(selector);\n}\n\n/**\n * @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor\n */\n\n/**\n * @typedef {{ text: string, element: HTMLElement | null, render(): void, willUpdate(): void, firstUpdated(): void, updated(): void, connectedCallback(): void, disconnectedCallback(): void }} ElenaInstanceMembers\n */\n\n/**\n * @typedef {{ name: string, reflect?: boolean }} ElenaPropObject\n */\n\n/**\n * @typedef {(new (...args: any[]) => HTMLElement & ElenaInstanceMembers) & {\n * define(): void,\n * readonly observedAttributes: string[],\n * tagName?: string,\n * props?: (string | ElenaPropObject)[],\n * events?: string[],\n * element?: string,\n * shadow?: \"open\" | \"closed\",\n * styles?: CSSStyleSheet | string | (CSSStyleSheet | string)[],\n * }} ElenaElementConstructor\n */\n\n// Tracks which component classes have already been set up.\nconst setupRegistry = new WeakSet();\n\n/**\n * Creates an Elena component class by extending `superClass`.\n *\n * Adds rendering, props, and event handling to your component.\n * Configure it using static class fields: `static tagName`,\n * `static props`, `static events`, and `static element`.\n *\n * @param {ElenaConstructor} superClass - The base class to extend (usually `HTMLElement`).\n * @returns {ElenaElementConstructor} A class ready to be defined as a custom element.\n */\nexport function Elena(superClass) {\n /**\n * The base Elena element class with all built-in behavior.\n */\n class ElenaElement extends superClass {\n /**\n * The inner element rendered by this component.\n *\n * @type {HTMLElement | null}\n */\n element = null;\n\n /**\n * Called by the browser when an observed attribute changes.\n * Updates the matching prop and re-renders if needed.\n *\n * @param {string} prop\n * @param {string} oldValue\n * @param {string} newValue\n */\n attributeChangedCallback(prop, oldValue, newValue) {\n super.attributeChangedCallback?.(prop, oldValue, newValue);\n\n if (prop === \"text\") {\n this.text = newValue ?? \"\";\n return;\n }\n\n // Set flag so the property setter skips redundant attribute reflection:\n // the attribute is already at the new value, no need to set it again.\n this._syncing = true;\n getProps(this, prop, oldValue, newValue);\n this._syncing = false;\n\n // Re-render when attributes change (after initial render).\n // Guard against re-entrant renders: if render() itself mutates an observed\n // attribute, skip the recursive call to prevent an infinite loop.\n if (this._hydrated && oldValue !== newValue && !this._isRendering) {\n this._safeRender();\n }\n }\n\n /**\n * Lists the attributes Elena watches for changes.\n * Reads from the subclass’s `static props` field.\n */\n static get observedAttributes() {\n if (this._observedAttrs) {\n return this._observedAttrs;\n }\n\n const propNames =\n this._propNames || (this.props || []).map(p => (typeof p === \"string\" ? p : p.name));\n this._observedAttrs = [...propNames, \"text\"];\n return this._observedAttrs;\n }\n\n /**\n * Called by the browser each time the element is added to the page.\n */\n connectedCallback() {\n super.connectedCallback?.();\n this._setupStaticProps();\n this._captureClassFieldDefaults();\n this._captureText();\n this._attachShadow();\n this.willUpdate();\n this._applyRender();\n this._resolveInnerElement();\n this._syncProps();\n this._delegateEvents();\n if (!this._hydrated) {\n this._hydrated = true;\n this.setAttribute(\"hydrated\", \"\");\n this.firstUpdated();\n }\n this.updated();\n }\n\n /**\n * Sets up props, events, and the element selector once per component class.\n * Runs the first time an instance of a given class connects to the page.\n *\n * @internal\n */\n _setupStaticProps() {\n const component = this.constructor;\n\n if (setupRegistry.has(component)) {\n return;\n }\n\n // Props with reflect: false\n const noRef = new Set();\n const names = [];\n\n if (component.props) {\n for (const p of component.props) {\n if (typeof p === \"string\") {\n names.push(p);\n } else {\n names.push(p.name);\n\n if (p.reflect === false) {\n noRef.add(p.name);\n }\n }\n }\n\n if (names.includes(\"text\")) {\n console.warn('░█ [ELENA]: \"text\" is reserved.');\n }\n\n setProps(component.prototype, names, noRef);\n }\n\n component._propNames = names;\n component._noReflect = noRef;\n component._elenaEvents = component.events || null;\n component._resolver = elementResolver(component.element);\n setupRegistry.add(component);\n }\n\n /**\n * Moves class field defaults into Elena’s internal props store\n * so that getters and setters work correctly.\n *\n * @internal\n */\n _captureClassFieldDefaults() {\n this._syncing = true;\n\n for (const name of this.constructor._propNames) {\n if (Object.prototype.hasOwnProperty.call(this, name)) {\n const value = this[name];\n delete this[name];\n this[name] = value;\n }\n }\n\n this._syncing = false;\n }\n\n /**\n * Saves any text inside the element before the first render.\n *\n * @internal\n */\n _captureText() {\n if (!this._hydrated && this._text === undefined) {\n this.text = this.textContent.trim();\n }\n }\n\n /**\n * The root node to render into. Returns the shadow root when shadow mode\n * is enabled, otherwise the host element itself.\n *\n * @type {ShadowRoot | HTMLElement}\n */\n get _renderRoot() {\n return this._shadow ?? this.shadowRoot ?? this;\n }\n\n /**\n * Attaches a shadow root and adopts styles on first connect.\n * Only runs when `static shadow` is set on the component class.\n *\n * @internal\n */\n _attachShadow() {\n const component = this.constructor;\n\n if (!component.shadow) {\n return;\n }\n\n // A shadow root may already exist if Declarative Shadow DOM was used.\n // In that case skip attachShadow() but still adopt styles below.\n // Store the reference so closed shadow roots remain accessible.\n const root = this._shadow ?? this.shadowRoot;\n\n if (!root) {\n this._shadow = this.attachShadow({ mode: component.shadow });\n }\n\n const shadowRoot = this._shadow ?? this.shadowRoot;\n\n if (!component.styles) {\n return;\n }\n\n // Normalize to array and cache converted CSSStyleSheet instances on the class.\n // Avoids re-parsing CSS strings on every element instance.\n if (!component._adoptedSheets) {\n const stylesList = Array.isArray(component.styles) ? component.styles : [component.styles];\n\n component._adoptedSheets = stylesList.map(s => {\n if (typeof s === \"string\") {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(s);\n return sheet;\n }\n return s;\n });\n }\n\n shadowRoot.adoptedStyleSheets = component._adoptedSheets;\n }\n\n /**\n * Calls render() and updates the DOM with the result.\n *\n * @internal\n */\n _applyRender() {\n const result = this.render();\n\n if (result && result.strings) {\n const root = this._renderRoot;\n const rebuilt = renderTemplate(root, result.strings, result.values);\n\n // Re-resolve element ref only when the DOM was fully rebuilt.\n // Fast-path text node patching leaves the DOM structure intact,\n // so the existing ref is still valid.\n if (this._hydrated && rebuilt) {\n this.element = this.constructor._resolver(root);\n }\n }\n }\n\n /**\n * Finds and stores a reference to the inner element.\n *\n * @internal\n */\n _resolveInnerElement() {\n if (!this.element) {\n const root = this._renderRoot;\n this.element = this.constructor._resolver(root);\n\n if (!this.element) {\n if (this.constructor.element) {\n console.warn(\"░█ [ELENA]: Element not found.\");\n }\n this.element = root.firstElementChild;\n }\n }\n }\n\n /**\n * Syncs any props that were set before the element\n * connected to the page.\n *\n * @internal\n */\n _syncProps() {\n if (this._props) {\n const noReflect = this.constructor._noReflect;\n\n for (const [prop, value] of this._props) {\n if (noReflect.has(prop)) {\n continue;\n }\n\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n\n if (attrValue === null && !this.hasAttribute(prop)) {\n continue;\n }\n\n syncAttribute(this, prop, attrValue);\n }\n }\n }\n\n /**\n * Forwards events from the inner element\n * up to the host element.\n *\n * @internal\n */\n _delegateEvents() {\n const events = this.constructor._elenaEvents;\n\n if (!this._events && events?.length) {\n if (!this.element) {\n console.warn(\"░█ [ELENA]: Cannot add events.\");\n } else {\n this._events = true;\n\n for (const e of events) {\n this.element.addEventListener(e, this);\n this[e] = (...args) => this.element[e](...args);\n }\n }\n }\n }\n\n /**\n * Define the element’s HTML here. Return an `html`\n * tagged template. If not overridden, the element connects\n * to the page without rendering anything.\n */\n render() {}\n\n /**\n * Called before every render.\n * Override to prepare state before the template runs.\n */\n willUpdate() {}\n\n /**\n * Called once after the element’s first render.\n * Override to run setup that needs the DOM.\n */\n firstUpdated() {}\n\n /**\n * Called after every render.\n * Override to react to changes.\n */\n updated() {}\n\n /**\n * Called by the browser when the element is moved\n * to a new document via `adoptNode()`.\n */\n adoptedCallback() {\n super.adoptedCallback?.();\n }\n\n /**\n * Called by the browser each time the element\n * is removed from the page.\n */\n disconnectedCallback() {\n super.disconnectedCallback?.();\n if (this._events) {\n this._events = false;\n\n for (const e of this.constructor._elenaEvents) {\n this.element?.removeEventListener(e, this);\n }\n }\n }\n\n /**\n * Receives events from the inner element and\n * re-fires them on the host.\n *\n * @internal\n */\n handleEvent(event) {\n if (this.constructor._elenaEvents?.includes(event.type)) {\n event.stopPropagation();\n\n /** @internal */\n this.dispatchEvent(new ElenaEvent(event.type, { cancelable: true }));\n }\n }\n\n /**\n * The text content of the element. Elena reads this\n * from the element’s children before the first render.\n * Updating it triggers a re-render.\n *\n * @type {string}\n */\n get text() {\n return this._text ?? \"\";\n }\n\n set text(value) {\n const old = this._text;\n this._text = value;\n\n if (this._hydrated && old !== value && !this._isRendering) {\n this._safeRender();\n }\n }\n\n /**\n * Registers the component as a custom element using `static tagName`.\n * Call this on your component class after the class body is defined,\n * not on the Elena mixin itself.\n */\n static define() {\n if (this.tagName) {\n defineElement(this.tagName, this);\n } else {\n console.warn(\"░█ [ELENA]: define() without a tagName.\");\n }\n }\n\n /**\n * Schedules a re-render via microtask. If called multiple times\n * before the microtask fires, only one render runs.\n *\n * @internal\n */\n _safeRender() {\n if (this._isRendering) {\n return;\n }\n if (!this._renderPending) {\n this._renderPending = true;\n this._updateComplete = new Promise(resolve => {\n this._resolveUpdate = resolve;\n });\n queueMicrotask(() => {\n try {\n this._performUpdate();\n } catch (e) {\n console.error(\"░█ [ELENA]:\", e);\n }\n });\n }\n }\n\n /**\n * Runs the batched update cycle.\n * Called by the microtask in _safeRender().\n *\n * @internal\n */\n _performUpdate() {\n this._renderPending = false;\n const resolve = this._resolveUpdate;\n this._resolveUpdate = null;\n try {\n try {\n this.willUpdate();\n this._isRendering = true;\n this._applyRender();\n } finally {\n this._isRendering = false;\n }\n this.updated();\n } finally {\n this._updateComplete = null;\n resolve();\n }\n }\n\n /**\n * A Promise that resolves after the render completes.\n * Resolves immediately if no render is scheduled.\n *\n * @type {Promise<void>}\n */\n get updateComplete() {\n if (this._updateComplete) {\n return this._updateComplete;\n }\n return Promise.resolve();\n }\n\n /**\n * Schedules a re-render. Use this to manually trigger an\n * update when Elena cannot detect the change automatically.\n */\n requestUpdate() {\n if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n }\n }\n\n return ElenaElement;\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","console","warn","syncAttribute","element","name","removeAttribute","setAttribute","resolveValue","Array","isArray","map","item","resolveItem","join","__raw","String","str","Escape","replace","c","escapeHtml","html","strings","values","toString","undefined","reduce","acc","s","i","unsafeHTML","nothing","Object","freeze","isRaw","some","toPlainText","collapseWhitespace","string","stringsCache","WeakMap","markerKey","Math","random","slice","renderTemplate","_tplStrings","_tplParts","length","v","comparable","_tplValues","textContent","patchTextNodes","entry","get","processedStrings","from","template","createTemplate","set","clone","content","cloneNode","walker","document","createTreeWalker","NodeFilter","SHOW_COMMENT","parts","markers","node","nextNode","data","push","tmp","createElement","innerHTML","parentNode","replaceChild","textNode","createTextNode","replaceChildren","cloneAndPatch","renderedValues","markup","out","trim","tpl","morphContent","childNodes","fullRender","valueCount","marker","count","currentNode","parent","nextNodes","current","next","len","max","cur","nxt","nodeType","Node","ELEMENT_NODE","tagName","TEXT_NODE","morphAttributes","removeChild","appendChild","attributes","hasAttribute","getAttribute","ElenaEvent","Event","constructor","eventInitDict","super","bubbles","composed","setupRegistry","WeakSet","Elena","superClass","attributeChangedCallback","prop","oldValue","newValue","this","_syncing","context","newAttr","getProps","_hydrated","_isRendering","_safeRender","text","observedAttributes","_observedAttrs","propNames","_propNames","props","p","connectedCallback","_setupStaticProps","_captureClassFieldDefaults","_captureText","_attachShadow","willUpdate","_applyRender","_resolveInnerElement","_syncProps","_delegateEvents","firstUpdated","updated","component","has","noRef","Set","names","reflect","add","includes","proto","noReflect","reflects","defineProperty","configurable","enumerable","_props","Map","isConnected","attrValue","setProps","prototype","selector","_noReflect","_elenaEvents","events","_resolver","host","querySelector","firstElementChild","hasOwnProperty","call","_text","_renderRoot","_shadow","shadowRoot","shadow","attachShadow","mode","styles","_adoptedSheets","stylesList","sheet","CSSStyleSheet","replaceSync","adoptedStyleSheets","result","render","root","rebuilt","_events","e","addEventListener","args","adoptedCallback","disconnectedCallback","removeEventListener","handleEvent","event","stopPropagation","dispatchEvent","cancelable","old","define","Element","window","customElements","_renderPending","_updateComplete","Promise","resolve","_resolveUpdate","queueMicrotask","_performUpdate","error","updateComplete","requestUpdate"],"mappings":"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"}
|
package/dist/props.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function t(t,e,n){if(e="boolean"===t&&"boolean"!=typeof e?null!==e:e,!n)return e;if("toAttribute"===n)switch(t){case"object":case"array":return null===e?null:JSON.stringify(e);case"boolean":return e?"":null;case"number":return null===e?null:e;default:return""===e?null:e}else switch(t){case"object":case"array":if(!e)return e;try{return JSON.parse(e)}catch{return console.warn("░█ [ELENA]: Invalid JSON: "+e),null}case"boolean":return e;case"number":return null!==e?+e:e;default:return e}}function e(t,e,n){t?null===n?t.removeAttribute(e):t.setAttribute(e,n):console.warn("░█ [ELENA]: Cannot sync attrs.")}function n(n,r,s){for(const o of r){const r=!s||!s.has(o);Object.defineProperty(n,o,{configurable:!0,enumerable:!0,get(){return this._props?this._props.get(o):void 0},set(n){if(this._props||(this._props=new Map),n!==this._props.get(o)&&(this._props.set(o,n),this.isConnected))if(r){if(!this._syncing){const r=t(typeof n,n,"toAttribute");e(this,o,r)}}else this._hydrated&&!this._isRendering&&this._safeRender()}})}}function r(e,n,r,s){if(r!==s){const r=typeof e[n];"undefined"===r&&console.warn(`░█ [ELENA]: Prop "${n}" has no default.`);const o=t(r,s,"toProp");e[n]=o}}export{t as getPropValue,r as getProps,n as setProps,e as syncAttribute};
|
|
1
|
+
function t(t,e,n){if(e="boolean"===t&&"boolean"!=typeof e?null!==e:e,!n)return e;if("toAttribute"===n)switch(t){case"object":case"array":return null===e?null:JSON.stringify(e);case"boolean":return e?"":null;case"number":return null===e?null:e;default:return""===e?null:e}else switch(t){case"object":case"array":if(!e)return e;try{return JSON.parse(e)}catch{return console.warn("░█ [ELENA]: Invalid JSON: "+e),null}case"boolean":return e;case"number":return null!==e?+e:e;default:return e??""}}function e(t,e,n){t?null===n?t.removeAttribute(e):t.setAttribute(e,n):console.warn("░█ [ELENA]: Cannot sync attrs.")}function n(n,r,s){for(const o of r){const r=!s||!s.has(o);Object.defineProperty(n,o,{configurable:!0,enumerable:!0,get(){return this._props?this._props.get(o):void 0},set(n){if(this._props||(this._props=new Map),n!==this._props.get(o)&&(this._props.set(o,n),this.isConnected))if(r){if(!this._syncing){const r=t(typeof n,n,"toAttribute");e(this,o,r)}}else this._hydrated&&!this._isRendering&&this._safeRender()}})}}function r(e,n,r,s){if(r!==s){const r=typeof e[n];"undefined"===r&&console.warn(`░█ [ELENA]: Prop "${n}" has no default.`);const o=t(r,s,"toProp");e[n]=o}}export{t as getPropValue,r as getProps,n as setProps,e as syncAttribute};
|
|
2
2
|
//# sourceMappingURL=props.js.map
|
package/dist/props.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"props.js","sources":["../src/common/props.js"],"sourcesContent":["/**\n * Get the value of the Elena Element property.\n *\n * @param {string} type\n * @param {any} value\n * @param {\"toAttribute\" | \"toProp\"} [transform]\n */\nexport function getPropValue(type, value, transform) {\n value = type === \"boolean\" && typeof value !== \"boolean\" ? value !== null : value;\n\n if (!transform) {\n return value;\n } else if (transform === \"toAttribute\") {\n switch (type) {\n case \"object\":\n case \"array\":\n return value === null ? null : JSON.stringify(value);\n case \"boolean\":\n return value ? \"\" : null;\n case \"number\":\n return value === null ? null : value;\n default:\n return value === \"\" ? null : value;\n }\n } else {\n switch (type) {\n case \"object\":\n case \"array\":\n if (!value) {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n console.warn(\"░█ [ELENA]: Invalid JSON: \" + value);\n return null;\n }\n case \"boolean\":\n return value; // conversion already handled above\n case \"number\":\n return value !== null ? +value : value;\n default:\n return value;\n }\n }\n}\n\n/**\n * Set or remove an attribute on an Elena Element.\n *\n * @param {Element} element - Target element\n * @param {string} name - Attribute name\n * @param {string | null} value - Attribute value, or null to remove\n */\nexport function syncAttribute(element, name, value) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot sync attrs.\");\n return;\n }\n if (value === null) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/**\n * Define prop getters/setters on the prototype once\n * at class-creation time. Values are stored per-instance\n * via a `_props` Map that is lazily created.\n *\n * @param {Function} proto - The class prototype\n * @param {string[]} propNames - Prop names to define\n * @param {Set<string>} [noReflect] - Props that should not reflect to attributes\n */\nexport function setProps(proto, propNames, noReflect) {\n for (const prop of propNames) {\n const reflects = !noReflect || !noReflect.has(prop);\n Object.defineProperty(proto, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return this._props ? this._props.get(prop) : undefined;\n },\n set(value) {\n if (!this._props) {\n this._props = new Map();\n }\n if (value === this._props.get(prop)) {\n return;\n }\n\n this._props.set(prop, value);\n if (!this.isConnected) {\n return;\n }\n\n if (reflects) {\n // Skip reflection when called from attributeChangedCallback. The\n // attribute is already at the new value, setting it again is redundant\n // and would fire an extra attributeChangedCallback with identical values.\n if (!this._syncing) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n }\n } else if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n },\n });\n }\n}\n\n/**\n * We need to update the internals of the Elena Element\n * when props on the host element are changed.\n *\n * @param {object} context\n * @param {string} name\n * @param {any} oldValue\n * @param {any} newValue\n */\nexport function getProps(context, name, oldValue, newValue) {\n if (oldValue !== newValue) {\n const type = typeof context[name];\n if (type === \"undefined\") {\n console.warn(`░█ [ELENA]: Prop \"${name}\" has no default.`);\n }\n const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","console","warn","syncAttribute","element","name","removeAttribute","setAttribute","setProps","proto","propNames","noReflect","prop","reflects","has","Object","defineProperty","configurable","enumerable","get","this","_props","undefined","set","Map","isConnected","_syncing","attrValue","_hydrated","_isRendering","_safeRender","getProps","context","oldValue","newValue","newAttr"],"mappings":"AAOO,SAASA,EAAaC,EAAMC,EAAOC,GAGxC,GAFAD,EAAiB,YAATD,GAAuC,kBAAVC,EAAgC,OAAVA,EAAiBA,GAEvEC,EACH,OAAOD,EACF,GAAkB,gBAAdC,EACT,OAAQF,GACN,IAAK,SACL,IAAK,QACH,OAAiB,OAAVC,EAAiB,KAAOE,KAAKC,UAAUH,GAChD,IAAK,UACH,OAAOA,EAAQ,GAAK,KACtB,IAAK,SACH,OAAiB,OAAVA,EAAiB,KAAOA,EACjC,QACE,MAAiB,KAAVA,EAAe,KAAOA,OAGjC,OAAQD,GACN,IAAK,SACL,IAAK,QACH,IAAKC,EACH,OAAOA,EAET,IACE,OAAOE,KAAKE,MAAMJ,EACpB,CAAE,MAEA,OADAK,QAAQC,KAAK,6BAA+BN,GACrC,IACT,CACF,IAAK,UACH,OAAOA,EACT,IAAK,SACH,OAAiB,OAAVA,GAAkBA,EAAQA,EACnC,QACE,OAAOA,
|
|
1
|
+
{"version":3,"file":"props.js","sources":["../src/common/props.js"],"sourcesContent":["/**\n * Get the value of the Elena Element property.\n *\n * @param {string} type\n * @param {any} value\n * @param {\"toAttribute\" | \"toProp\"} [transform]\n */\nexport function getPropValue(type, value, transform) {\n value = type === \"boolean\" && typeof value !== \"boolean\" ? value !== null : value;\n\n if (!transform) {\n return value;\n } else if (transform === \"toAttribute\") {\n switch (type) {\n case \"object\":\n case \"array\":\n return value === null ? null : JSON.stringify(value);\n case \"boolean\":\n return value ? \"\" : null;\n case \"number\":\n return value === null ? null : value;\n default:\n return value === \"\" ? null : value;\n }\n } else {\n switch (type) {\n case \"object\":\n case \"array\":\n if (!value) {\n return value;\n }\n try {\n return JSON.parse(value);\n } catch {\n console.warn(\"░█ [ELENA]: Invalid JSON: \" + value);\n return null;\n }\n case \"boolean\":\n return value; // conversion already handled above\n case \"number\":\n return value !== null ? +value : value;\n default:\n return value ?? \"\";\n }\n }\n}\n\n/**\n * Set or remove an attribute on an Elena Element.\n *\n * @param {Element} element - Target element\n * @param {string} name - Attribute name\n * @param {string | null} value - Attribute value, or null to remove\n */\nexport function syncAttribute(element, name, value) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot sync attrs.\");\n return;\n }\n if (value === null) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value);\n }\n}\n\n/**\n * Define prop getters/setters on the prototype once\n * at class-creation time. Values are stored per-instance\n * via a `_props` Map that is lazily created.\n *\n * @param {Function} proto - The class prototype\n * @param {string[]} propNames - Prop names to define\n * @param {Set<string>} [noReflect] - Props that should not reflect to attributes\n */\nexport function setProps(proto, propNames, noReflect) {\n for (const prop of propNames) {\n const reflects = !noReflect || !noReflect.has(prop);\n Object.defineProperty(proto, prop, {\n configurable: true,\n enumerable: true,\n get() {\n return this._props ? this._props.get(prop) : undefined;\n },\n set(value) {\n if (!this._props) {\n this._props = new Map();\n }\n if (value === this._props.get(prop)) {\n return;\n }\n\n this._props.set(prop, value);\n if (!this.isConnected) {\n return;\n }\n\n if (reflects) {\n // Skip reflection when called from attributeChangedCallback. The\n // attribute is already at the new value, setting it again is redundant\n // and would fire an extra attributeChangedCallback with identical values.\n if (!this._syncing) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n }\n } else if (this._hydrated && !this._isRendering) {\n this._safeRender();\n }\n },\n });\n }\n}\n\n/**\n * We need to update the internals of the Elena Element\n * when props on the host element are changed.\n *\n * @param {object} context\n * @param {string} name\n * @param {any} oldValue\n * @param {any} newValue\n */\nexport function getProps(context, name, oldValue, newValue) {\n if (oldValue !== newValue) {\n const type = typeof context[name];\n if (type === \"undefined\") {\n console.warn(`░█ [ELENA]: Prop \"${name}\" has no default.`);\n }\n const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","console","warn","syncAttribute","element","name","removeAttribute","setAttribute","setProps","proto","propNames","noReflect","prop","reflects","has","Object","defineProperty","configurable","enumerable","get","this","_props","undefined","set","Map","isConnected","_syncing","attrValue","_hydrated","_isRendering","_safeRender","getProps","context","oldValue","newValue","newAttr"],"mappings":"AAOO,SAASA,EAAaC,EAAMC,EAAOC,GAGxC,GAFAD,EAAiB,YAATD,GAAuC,kBAAVC,EAAgC,OAAVA,EAAiBA,GAEvEC,EACH,OAAOD,EACF,GAAkB,gBAAdC,EACT,OAAQF,GACN,IAAK,SACL,IAAK,QACH,OAAiB,OAAVC,EAAiB,KAAOE,KAAKC,UAAUH,GAChD,IAAK,UACH,OAAOA,EAAQ,GAAK,KACtB,IAAK,SACH,OAAiB,OAAVA,EAAiB,KAAOA,EACjC,QACE,MAAiB,KAAVA,EAAe,KAAOA,OAGjC,OAAQD,GACN,IAAK,SACL,IAAK,QACH,IAAKC,EACH,OAAOA,EAET,IACE,OAAOE,KAAKE,MAAMJ,EACpB,CAAE,MAEA,OADAK,QAAQC,KAAK,6BAA+BN,GACrC,IACT,CACF,IAAK,UACH,OAAOA,EACT,IAAK,SACH,OAAiB,OAAVA,GAAkBA,EAAQA,EACnC,QACE,OAAOA,GAAS,GAGxB,CASO,SAASO,EAAcC,EAASC,EAAMT,GACtCQ,EAIS,OAAVR,EACFQ,EAAQE,gBAAgBD,GAExBD,EAAQG,aAAaF,EAAMT,GAN3BK,QAAQC,KAAK,iCAQjB,CAWO,SAASM,EAASC,EAAOC,EAAWC,GACzC,IAAK,MAAMC,KAAQF,EAAW,CAC5B,MAAMG,GAAYF,IAAcA,EAAUG,IAAIF,GAC9CG,OAAOC,eAAeP,EAAOG,EAAM,CACjCK,cAAc,EACdC,YAAY,EACZ,GAAAC,GACE,OAAOC,KAAKC,OAASD,KAAKC,OAAOF,IAAIP,QAAQU,CAC/C,EACA,GAAAC,CAAI3B,GAIF,GAHKwB,KAAKC,SACRD,KAAKC,OAAS,IAAIG,KAEhB5B,IAAUwB,KAAKC,OAAOF,IAAIP,KAI9BQ,KAAKC,OAAOE,IAAIX,EAAMhB,GACjBwB,KAAKK,aAIV,GAAIZ,GAIF,IAAKO,KAAKM,SAAU,CAClB,MAAMC,EAAYjC,SAAoBE,EAAOA,EAAO,eACpDO,EAAciB,KAAMR,EAAMe,EAC5B,OACSP,KAAKQ,YAAcR,KAAKS,cACjCT,KAAKU,aAET,GAEJ,CACF,CAWO,SAASC,EAASC,EAAS3B,EAAM4B,EAAUC,GAChD,GAAID,IAAaC,EAAU,CACzB,MAAMvC,SAAcqC,EAAQ3B,GACf,cAATV,GACFM,QAAQC,KAAK,qBAAqBG,sBAEpC,MAAM8B,EAAUzC,EAAaC,EAAMuC,EAAU,UAC7CF,EAAQ3B,GAAQ8B,CAClB,CACF"}
|
package/dist/render.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toPlainText as e,isRaw as t,collapseWhitespace as
|
|
1
|
+
import{toPlainText as e,isRaw as t,collapseWhitespace as n,resolveValue as r}from"./utils.js";const o=new WeakMap,l="e"+Math.random().toString(36).slice(2,6);function a(a,i,d){return!function(n,r,o){if(n._tplStrings!==r||!n._tplParts)return!1;for(let r=0;r<o.length;r++){const l=o[r],a=Array.isArray(l)?e(l):l;if(a!==n._tplValues[r]){if(t(l)||!n._tplParts[r])return!1;n._tplValues[r]=a,n._tplParts[r].textContent=e(l)}}return!0}(a,i,d)&&(function(a,i,d){let p=o.get(i);if(!p){const e=Array.from(i,n);p={processedStrings:e,template:d.length>0?c(e,d.length):null},o.set(i,p)}if(p.template)a._tplParts=function(n,o,a){const c=o.content.cloneNode(!0),s=document.createTreeWalker(c,NodeFilter.SHOW_COMMENT),i=new Array(a.length),d=[];let p;for(;p=s.nextNode();)p.data===l&&d.push(p);for(let n=0;n<d.length;n++){const o=a[n];if(t(o)){const e=document.createElement("template");e.innerHTML=r(o),d[n].parentNode.replaceChild(e.content,d[n])}else{const t=document.createTextNode(e(o));d[n].parentNode.replaceChild(t,d[n]),i[n]=t}}return n.replaceChildren(c),i}(a,p.template,d);else{const e=d.map(e=>r(e)),t=p.processedStrings.reduce((t,n,r)=>t+n+(e[r]??""),"").replace(/>\s+</g,"><").trim(),n=document.createElement("template");n.innerHTML=t,s(a,n.content.childNodes),a._tplParts=new Array(d.length)}a._tplStrings=i,a._tplValues=d.map(t=>Array.isArray(t)?e(t):t)}(a,i,d),!0)}function c(e,t){const n=`\x3c!--${l}--\x3e`,r=e.reduce((e,r,o)=>e+r.replace(/>\s+</g,"><")+(o<t?n:""),"").trim(),o=document.createElement("template");o.innerHTML=r;const a=document.createTreeWalker(o.content,NodeFilter.SHOW_COMMENT);let c=0;for(;a.nextNode();)a.currentNode.data===l&&c++;return c===t?o:null}function s(e,t){const n=Array.from(e.childNodes),r=Array.from(t),o=Math.max(n.length,r.length);for(let t=0;t<o;t++){const o=n[t],l=r[t];o?l?o.nodeType!==l.nodeType||o.nodeType===Node.ELEMENT_NODE&&o.tagName!==l.tagName?e.replaceChild(l,o):o.nodeType===Node.TEXT_NODE?o.textContent!==l.textContent&&(o.textContent=l.textContent):o.nodeType===Node.ELEMENT_NODE&&(i(o,l),s(o,l.childNodes)):e.removeChild(o):e.appendChild(l)}}function i(e,t){for(let n=e.attributes.length-1;n>=0;n--){const{name:r}=e.attributes[n];t.hasAttribute(r)||e.removeAttribute(r)}for(let n=0;n<t.attributes.length;n++){const{name:r,value:o}=t.attributes[n];e.getAttribute(r)!==o&&e.setAttribute(r,o)}}export{a as renderTemplate};
|
|
2
2
|
//# sourceMappingURL=render.js.map
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sources":["../src/common/render.js"],"sourcesContent":["import { collapseWhitespace, isRaw, resolveValue, toPlainText } from \"./utils.js\";\n\nconst stringsCache = new WeakMap();\nconst markerKey = \"e\" + Math.random().toString(36).slice(2, 6);\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n * Returns true if the DOM was fully rebuilt, false if only text\n * nodes were patched in place.\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean}\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return false;\n }\n fullRender(element, strings, values);\n return true;\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n *\n * @param {HTMLElement} element - The host element with cached template state\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean} Whether patching was sufficient (false = full render)\n */\nfunction patchTextNodes(element, strings, values) {\n // Only works when re-rendering the same template shape\n if (element._tplStrings !== strings || !element._tplParts) {\n return false;\n }\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const comparable = Array.isArray(v) ? toPlainText(v) : v;\n\n if (comparable === element._tplValues[i]) {\n continue;\n }\n\n if (isRaw(v) || !element._tplParts[i]) {\n return false;\n }\n\n element._tplValues[i] = comparable;\n element._tplParts[i].textContent = toPlainText(v);\n }\n\n return true;\n}\n\n/**\n * Cold path: clone a cached <template> and patch in values.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nfunction fullRender(element, strings, values) {\n let entry = stringsCache.get(strings);\n\n if (!entry) {\n const processedStrings = Array.from(strings, collapseWhitespace);\n entry = {\n processedStrings,\n template: values.length > 0 ? createTemplate(processedStrings, values.length) : null,\n };\n stringsCache.set(strings, entry);\n }\n\n if (entry.template) {\n element._tplParts = cloneAndPatch(element, entry.template, values);\n } else {\n // Fallback for attribute-position values or static templates.\n // White space collapsing here protects against Vue SSR mismatches.\n const renderedValues = values.map(value => resolveValue(value));\n const markup = entry.processedStrings\n .reduce((out, str, i) => out + str + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .trim();\n\n element.innerHTML = markup;\n element._tplParts = new Array(values.length);\n }\n\n element._tplStrings = strings;\n element._tplValues = values.map(v => (Array.isArray(v) ? toPlainText(v) : v));\n}\n\n/**\n * Build a <template> element with comment markers.\n *\n * @param {string[]} processedStrings - Whitespace-collapsed static parts\n * @param {number} valueCount - Number of dynamic values\n * @returns {HTMLTemplateElement | null}\n */\nfunction createTemplate(processedStrings, valueCount) {\n const marker = `<!--${markerKey}-->`;\n const markup = processedStrings\n .reduce((out, str, i) => {\n const collapsed = str.replace(/>\\s+</g, \"><\");\n return out + collapsed + (i < valueCount ? marker : \"\");\n }, \"\")\n .trim();\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n\n // Mismatch means this template shape cannot use the clone path.\n const walker = document.createTreeWalker(tpl.content, NodeFilter.SHOW_COMMENT);\n let count = 0;\n\n while (walker.nextNode()) {\n if (walker.currentNode.data === markerKey) {\n count++;\n }\n }\n\n return count === valueCount ? tpl : null;\n}\n\n/**\n * Clone a cached template and replace comment markers\n * with actual content.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {HTMLTemplateElement} template - Cached template with markers\n * @param {Array} values - Raw interpolated values\n * @param {string[]} renderedValues - HTML-escaped rendered values\n * @returns {Array<Text | undefined>} Text node map for fast-path patching\n */\nfunction cloneAndPatch(element, template, values) {\n const clone = template.content.cloneNode(true);\n const walker = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT);\n const parts = new Array(values.length);\n const markers = [];\n let node;\n\n // Collect markers before modifying the tree\n while ((node = walker.nextNode())) {\n if (node.data === markerKey) {\n markers.push(node);\n }\n }\n\n for (let i = 0; i < markers.length; i++) {\n const value = values[i];\n\n if (isRaw(value)) {\n // Raw HTML: parse and insert as fragment\n const tmp = document.createElement(\"template\");\n tmp.innerHTML = resolveValue(value);\n markers[i].parentNode.replaceChild(tmp.content, markers[i]);\n\n // Raw values can't be fast-patched; leave parts undefined\n } else {\n // Create text node with unescaped content\n const textNode = document.createTextNode(toPlainText(value));\n markers[i].parentNode.replaceChild(textNode, markers[i]);\n parts[i] = textNode;\n }\n }\n\n element.replaceChildren(clone);\n return parts;\n}\n"],"names":["stringsCache","WeakMap","markerKey","Math","random","toString","slice","renderTemplate","element","strings","values","_tplStrings","_tplParts","i","length","v","comparable","Array","isArray","toPlainText","_tplValues","isRaw","textContent","patchTextNodes","entry","get","processedStrings","from","collapseWhitespace","template","createTemplate","set","clone","content","cloneNode","walker","document","createTreeWalker","NodeFilter","SHOW_COMMENT","parts","markers","node","nextNode","data","push","value","tmp","createElement","innerHTML","resolveValue","parentNode","replaceChild","textNode","createTextNode","replaceChildren","cloneAndPatch","renderedValues","map","markup","reduce","out","str","replace","trim","fullRender","valueCount","marker","tpl","count","currentNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../src/common/render.js"],"sourcesContent":["import { collapseWhitespace, isRaw, resolveValue, toPlainText } from \"./utils.js\";\n\nconst stringsCache = new WeakMap();\nconst markerKey = \"e\" + Math.random().toString(36).slice(2, 6);\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n * Returns true if the DOM was fully rebuilt, false if only text\n * nodes were patched in place.\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean}\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return false;\n }\n fullRender(element, strings, values);\n return true;\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n *\n * @param {HTMLElement} element - The host element with cached template state\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n * @returns {boolean} Whether patching was sufficient (false = full render)\n */\nfunction patchTextNodes(element, strings, values) {\n // Only works when re-rendering the same template shape\n if (element._tplStrings !== strings || !element._tplParts) {\n return false;\n }\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const comparable = Array.isArray(v) ? toPlainText(v) : v;\n\n if (comparable === element._tplValues[i]) {\n continue;\n }\n\n if (isRaw(v) || !element._tplParts[i]) {\n return false;\n }\n\n element._tplValues[i] = comparable;\n element._tplParts[i].textContent = toPlainText(v);\n }\n\n return true;\n}\n\n/**\n * Cold path: clone a cached <template> and patch in values.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nfunction fullRender(element, strings, values) {\n let entry = stringsCache.get(strings);\n\n if (!entry) {\n const processedStrings = Array.from(strings, collapseWhitespace);\n entry = {\n processedStrings,\n template: values.length > 0 ? createTemplate(processedStrings, values.length) : null,\n };\n stringsCache.set(strings, entry);\n }\n\n if (entry.template) {\n element._tplParts = cloneAndPatch(element, entry.template, values);\n } else {\n // Fallback for attribute-position values or static templates.\n // White space collapsing here protects against Vue SSR mismatches.\n const renderedValues = values.map(value => resolveValue(value));\n const markup = entry.processedStrings\n .reduce((out, str, i) => out + str + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .trim();\n\n // Morph existing DOM to match new markup instead of replacing it.\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n morphContent(element, tpl.content.childNodes);\n element._tplParts = new Array(values.length);\n }\n\n element._tplStrings = strings;\n element._tplValues = values.map(v => (Array.isArray(v) ? toPlainText(v) : v));\n}\n\n/**\n * Build a <template> element with comment markers.\n *\n * @param {string[]} processedStrings - Whitespace-collapsed static parts\n * @param {number} valueCount - Number of dynamic values\n * @returns {HTMLTemplateElement | null}\n */\nfunction createTemplate(processedStrings, valueCount) {\n const marker = `<!--${markerKey}-->`;\n const markup = processedStrings\n .reduce((out, str, i) => {\n const collapsed = str.replace(/>\\s+</g, \"><\");\n return out + collapsed + (i < valueCount ? marker : \"\");\n }, \"\")\n .trim();\n\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = markup;\n\n // Mismatch means this template shape cannot use the clone path.\n const walker = document.createTreeWalker(tpl.content, NodeFilter.SHOW_COMMENT);\n let count = 0;\n\n while (walker.nextNode()) {\n if (walker.currentNode.data === markerKey) {\n count++;\n }\n }\n\n return count === valueCount ? tpl : null;\n}\n\n/**\n * Clone a cached template and replace comment markers\n * with actual content.\n *\n * @param {HTMLElement} element - The host element to render into\n * @param {HTMLTemplateElement} template - Cached template with markers\n * @param {Array} values - Raw interpolated values\n * @param {string[]} renderedValues - HTML-escaped rendered values\n * @returns {Array<Text | undefined>} Text node map for fast-path patching\n */\nfunction cloneAndPatch(element, template, values) {\n const clone = template.content.cloneNode(true);\n const walker = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT);\n const parts = new Array(values.length);\n const markers = [];\n let node;\n\n // Collect markers before modifying the tree\n while ((node = walker.nextNode())) {\n if (node.data === markerKey) {\n markers.push(node);\n }\n }\n\n for (let i = 0; i < markers.length; i++) {\n const value = values[i];\n\n if (isRaw(value)) {\n // Raw HTML: parse and insert as fragment\n const tmp = document.createElement(\"template\");\n tmp.innerHTML = resolveValue(value);\n markers[i].parentNode.replaceChild(tmp.content, markers[i]);\n\n // Raw values can't be fast-patched; leave parts undefined\n } else {\n // Create text node with unescaped content\n const textNode = document.createTextNode(toPlainText(value));\n markers[i].parentNode.replaceChild(textNode, markers[i]);\n parts[i] = textNode;\n }\n }\n\n element.replaceChildren(clone);\n return parts;\n}\n\n/**\n * Patches attributes and text content in-place when structure is stable,\n * preserving element identity and focus state across re-renders.\n *\n * @param {Node} parent\n * @param {NodeList} nextNodes - The desired child nodes from the new render\n */\nfunction morphContent(parent, nextNodes) {\n const current = Array.from(parent.childNodes);\n const next = Array.from(nextNodes);\n const len = Math.max(current.length, next.length);\n\n for (let i = 0; i < len; i++) {\n const cur = current[i];\n const nxt = next[i];\n\n if (!cur) {\n parent.appendChild(nxt);\n } else if (!nxt) {\n parent.removeChild(cur);\n } else if (\n cur.nodeType !== nxt.nodeType ||\n (cur.nodeType === Node.ELEMENT_NODE && cur.tagName !== nxt.tagName)\n ) {\n parent.replaceChild(nxt, cur);\n } else if (cur.nodeType === Node.TEXT_NODE) {\n if (cur.textContent !== nxt.textContent) {\n cur.textContent = nxt.textContent;\n }\n } else if (cur.nodeType === Node.ELEMENT_NODE) {\n morphAttributes(cur, nxt);\n morphContent(cur, nxt.childNodes);\n }\n }\n}\n\n/**\n * Morhp element’s attributes without rebuilding the DOM.\n *\n * @param {Element} current - The current existing DOM element\n * @param {Element} next - The desired element from the new render\n */\nfunction morphAttributes(current, next) {\n for (let i = current.attributes.length - 1; i >= 0; i--) {\n const { name } = current.attributes[i];\n\n if (!next.hasAttribute(name)) {\n current.removeAttribute(name);\n }\n }\n\n for (let i = 0; i < next.attributes.length; i++) {\n const { name, value } = next.attributes[i];\n\n if (current.getAttribute(name) !== value) {\n current.setAttribute(name, value);\n }\n }\n}\n"],"names":["stringsCache","WeakMap","markerKey","Math","random","toString","slice","renderTemplate","element","strings","values","_tplStrings","_tplParts","i","length","v","comparable","Array","isArray","toPlainText","_tplValues","isRaw","textContent","patchTextNodes","entry","get","processedStrings","from","collapseWhitespace","template","createTemplate","set","clone","content","cloneNode","walker","document","createTreeWalker","NodeFilter","SHOW_COMMENT","parts","markers","node","nextNode","data","push","value","tmp","createElement","innerHTML","resolveValue","parentNode","replaceChild","textNode","createTextNode","replaceChildren","cloneAndPatch","renderedValues","map","markup","reduce","out","str","replace","trim","tpl","morphContent","childNodes","fullRender","valueCount","marker","count","currentNode","parent","nextNodes","current","next","len","max","cur","nxt","nodeType","Node","ELEMENT_NODE","tagName","TEXT_NODE","morphAttributes","removeChild","appendChild","attributes","name","hasAttribute","removeAttribute","getAttribute","setAttribute"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elenajs/core",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.2",
|
|
4
4
|
"description": "Elena is a simple, tiny library for building Progressive Web Components.",
|
|
5
5
|
"author": "Elena <hi@elenajs.com>",
|
|
6
6
|
"homepage": "https://elenajs.com/",
|
|
@@ -29,6 +29,17 @@
|
|
|
29
29
|
"files": [
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"prebuild": "npm run -s clean",
|
|
34
|
+
"start": "rollup -c --watch",
|
|
35
|
+
"build": "NODE_OPTIONS='--no-warnings' rollup -c",
|
|
36
|
+
"postbuild": "npx -p typescript tsc",
|
|
37
|
+
"test": "vitest run --coverage",
|
|
38
|
+
"test:visual": "npx playwright test",
|
|
39
|
+
"test:visual:update": "npx playwright test --update-snapshots",
|
|
40
|
+
"bench": "vitest bench --config vitest.bench.config.mjs",
|
|
41
|
+
"clean": "rm -rf dist/"
|
|
42
|
+
},
|
|
32
43
|
"devDependencies": {
|
|
33
44
|
"@vitest/browser": "4.0.18",
|
|
34
45
|
"@vitest/browser-playwright": "4.0.18",
|
|
@@ -42,16 +53,5 @@
|
|
|
42
53
|
"serve": "^14.2.6",
|
|
43
54
|
"typescript": "5.9.3",
|
|
44
55
|
"vitest": "4.0.18"
|
|
45
|
-
},
|
|
46
|
-
"scripts": {
|
|
47
|
-
"prebuild": "npm run -s clean",
|
|
48
|
-
"start": "rollup -c --watch",
|
|
49
|
-
"build": "NODE_OPTIONS='--no-warnings' rollup -c",
|
|
50
|
-
"postbuild": "npx -p typescript tsc",
|
|
51
|
-
"test": "vitest run --coverage",
|
|
52
|
-
"test:visual": "npx playwright test",
|
|
53
|
-
"test:visual:update": "npx playwright test --update-snapshots",
|
|
54
|
-
"bench": "vitest bench --config vitest.bench.config.mjs",
|
|
55
|
-
"clean": "rm -rf dist/"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Ariel Salminen https://arielsalminen.com
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|