@elenajs/core 0.0.2 → 0.0.4
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/common/events.d.ts +2 -2
- package/dist/common/props.d.ts +3 -3
- package/dist/common/render.d.ts +5 -5
- package/dist/common/utils.d.ts +1 -2
- package/dist/common/utils.d.ts.map +1 -1
- package/dist/elena.d.ts +16 -36
- package/dist/elena.d.ts.map +1 -1
- package/dist/elena.js +1 -1
- package/dist/elena.js.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/render.js.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/bundle.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e(e,t,n){if(t="boolean"===e&&"boolean"!=typeof t?null!==t:t,!n)return t;if("toAttribute"===n)switch(e){case"object":case"array":return null===t?null:JSON.stringify(t);case"boolean":return t?"":null;case"number":return null===t?null:t;default:return
|
|
1
|
+
function e(e,t,n){if(t="boolean"===e&&"boolean"!=typeof t?null!==t:t,!n)return t;if("toAttribute"===n)switch(e){case"object":case"array":return null===t?null:JSON.stringify(t);case"boolean":return t?"":null;case"number":return null===t?null:t;default:return""===t?null:t}else switch(e){case"object":case"array":return t&&JSON.parse(t);case"boolean":default:return t;case"number":return null!==t?+t:t}}function t(e,t,n){e?null===n?e.removeAttribute(t):e.setAttribute(t,n):console.warn("░█ [ELENA]: Cannot sync attributes to a null element.")}class n extends Event{constructor(e,t){super(e,{bubbles:!0,composed:!0,...t})}}function s(e,t){"undefined"!=typeof window&&"customElements"in window&&(window.customElements.get(e)||window.customElements.define(e,t))}function r(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,e=>t[e])}function i(e,...t){const n=e.reduce((e,n,s)=>e+n+r(String(t[s]??"")),"");return{__raw:!0,toString:()=>n}}function l(e,t,n){(function(e,t,n){if(e._tplStrings!==t||!e._tplParts)return!1;let s=!1;for(let t=0;t<n.length;t++){const i=n[t],l=i&&i.__raw,o=l?String(i):r(String(i??""));if(o!==e._tplValues[t])if(e._tplValues[t]=o,l)s=!0;else{const n=e._tplParts[t];n?n.textContent=String(i??""):s=!0}}return!s})(e,t,n)||function(e,t,n){const s=n.map(e=>e&&e.__raw?String(e):r(String(e??""))),i=t.reduce((e,t,n)=>e+t.replace(/\n\s*/g," ")+(s[n]??""),"").replace(/>\s+</g,"><").replace(/>\s+/g,">").replace(/\s+</g,"<").trim();(function(e,t){if(!e)return void console.warn("░█ [ELENA]: Cannot render to a null element.");e.replaceChildren((o??=document.createRange()).createContextualFragment(t))})(e,i),e._tplStrings=t,e._tplValues=s,e._tplParts=function(e,t){const n=new Array(t.length),s=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);let r,i=0;for(;(r=s.nextNode())&&i<t.length;)r.textContent===t[i]&&(n[i]=r,i++);return n}(e,s)}(e,t,n)}let o;function a(s,r){const i=r&&r.element?/^[a-z][a-z0-9-]*$/i.test(r.element)?e=>e.getElementsByClassName(r.element)[0]:e=>e.querySelector(r.element):e=>e.firstElementChild;class o extends s{element=null;attributeChangedCallback(t,n,s){!function(t,n,s,r){if(s!==r){const s=e(typeof t[n],r,"toProp");t[n]=s}}(this,t,n,s),this.element&&n!==s&&this.render()}static get observedAttributes(){return r&&r.props?r.props:[]}render(){}template(e,...t){l(this,e,t)}connectedCallback(){if(this.render(),this.element||(this.element=i(this),this.element||(console.warn("░█ [ELENA]: No element found, using firstElementChild as fallback."),this.element=this.firstElementChild)),this._props)for(const[n,s]of this._props){const r=e(typeof s,s,"toAttribute");t(this,n,r),t(this.element,n,r)}!this._events&&r&&r.events&&(this._events=!0,r.events?.forEach(e=>{this.element.addEventListener(e,this),this[e]=(...t)=>this.element[e](...t)})),this.updated()}updated(){this._hydrated||(this._hydrated=!0,this.classList.add("elena-hydrated"))}disconnectedCallback(){this._events&&(this._events=!1,r.events?.forEach(e=>{this.element?.removeEventListener(e,this)}))}handleEvent(e){r.events?.includes(e.type)&&(e.stopPropagation(),this.dispatchEvent(new n(e.type,{cancelable:!0})))}}return r&&r.props?.length&&function(n,s){for(const r of s)Object.defineProperty(n,r,{configurable:!0,enumerable:!0,get(){return this._props?this._props.get(r):void 0},set(n){if(this._props||(this._props=new Map),n===this._props.get(r))return;if(this._props.set(r,n),!this.isConnected)return;const s=e(typeof n,n,"toAttribute");t(this,r,s),this.element&&t(this.element,r,s)}})}(o.prototype,r.props),o}export{a as Elena,s as defineElement,i as html};
|
|
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/events.js","../src/common/utils.js","../src/common/render.js","../src/elena.js"],"sourcesContent":["/**\n * Get the value of the Elena Custom Element prop.\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;\n }\n } else {\n switch (type) {\n case \"object\":\n case \"array\":\n return value && JSON.parse(value);\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 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 attributes to a null element.\");\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[]} properties - Prop names to define\n */\nexport function setProps(proto, properties) {\n for (const prop of properties) {\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 const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n if (this.element) {\n syncAttribute(this.element, prop, attrValue);\n }\n },\n });\n }\n}\n\n/**\n * We need to update the internals of our component\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 const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n","/**\n * A base class for events which defaults to\n * 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 * Register the Elena custom element if the\n * 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 * Tagged template for trusted HTML fragments with auto-escaped interpolations.\n * Use inside `this.template` for conditional HTML blocks.\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n * @returns {{ __raw: true, toString(): string }}\n */\nexport function html(strings, ...values) {\n const result = strings.reduce(\n (acc, str, i) => acc + str + escapeHtml(String(values[i] ?? \"\")),\n \"\"\n );\n return { __raw: true, toString: () => result };\n}\n","import { escapeHtml } from \"./utils.js\";\n\n/**\n * Render a tagged template into an element with DOM diffing.\n *\n * On first render, builds the full HTML markup and render.\n * On re-renders, patches only the text nodes whose values changed,\n * avoiding a full DOM rebuild.\n *\n * Cache state is stored on the element instance:\n * _tplStrings — reference to the template's static strings array\n * _tplValues — array of escaped values from the last render\n * _tplParts — array mapping each value index to its DOM text node\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return;\n }\n fullRender(element, strings, values);\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n * Returns true if all changes were handled (no full render needed).\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 let needsFullRender = false;\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const isRaw = v && v.__raw;\n const newRendered = isRaw ? String(v) : escapeHtml(String(v ?? \"\"));\n const oldRendered = element._tplValues[i];\n\n if (newRendered === oldRendered) {\n continue;\n }\n\n element._tplValues[i] = newRendered;\n\n // Raw HTML values or attribute-position values require a full render\n if (isRaw) {\n needsFullRender = true;\n } else {\n const textNode = element._tplParts[i];\n if (textNode) {\n // Value is in a text position — update the DOM node directly\n textNode.textContent = String(v ?? \"\");\n } else {\n // Value is in an attribute position — can't patch, need full render\n needsFullRender = true;\n }\n }\n }\n\n return !needsFullRender;\n}\n\n/**\n * Cold path: build full HTML markup, render it via DocumentFragment,\n * and map each interpolated value to its corresponding DOM text node\n * for future fast-path patching.\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 const renderedValues = values.map(v => (v && v.__raw ? String(v) : escapeHtml(String(v ?? \"\"))));\n\n // Build the complete HTML string, stripping template indentation.\n // 1. Replace newlines + indentation with a single space (preserves attribute separation)\n // 2. Clean up: remove whitespace between/around tags and trim\n const markup = strings\n .reduce((out, str, i) => out + str.replace(/\\n\\s*/g, \" \") + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .replace(/>\\s+/g, \">\")\n .replace(/\\s+</g, \"<\")\n .trim();\n\n renderHtml(element, markup);\n\n // Cache template identity and rendered values\n element._tplStrings = strings;\n element._tplValues = renderedValues;\n\n // Walk text nodes to map each value index to its DOM node\n element._tplParts = mapTextNodes(element, renderedValues);\n}\n\n/**\n * Walk the element's text nodes and map each escaped value\n * to its corresponding DOM text node. Values in attribute\n * positions won't have a matching text node and will be null.\n *\n * @param {HTMLElement} element - The host element to walk\n * @param {string[]} escapedValues - HTML-escaped interpolated values\n * @returns {Array<Text | undefined>} Array mapping each value index to its text node\n */\nfunction mapTextNodes(element, escapedValues) {\n const parts = new Array(escapedValues.length);\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\n\n let valueIndex = 0;\n let node;\n\n while ((node = walker.nextNode()) && valueIndex < escapedValues.length) {\n if (node.textContent === escapedValues[valueIndex]) {\n parts[valueIndex] = node;\n valueIndex++;\n }\n }\n\n return parts;\n}\n\n/**\n * Lazily-created Range reused across renders as a createContextualFragment\n * factory. Deferred so the module can be imported in non-browser environments.\n *\n * @type {Range | undefined}\n */\nlet _range;\n\n/**\n * Render an HTML string into an element by parsing it into a\n * DocumentFragment via createContextualFragment and swapping\n * the element's children in a single replaceChildren call.\n *\n * @param {HTMLElement} element\n * @param {string} markup\n */\nexport function renderHtml(element, markup) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot render to a null element.\");\n return;\n }\n element.replaceChildren((_range ??= document.createRange()).createContextualFragment(markup));\n}\n","import { setProps, getProps, getPropValue, syncAttribute } from \"./common/props.js\";\nimport { ElenaEvent } from \"./common/events.js\";\nimport { defineElement, html } from \"./common/utils.js\";\nimport { renderTemplate } from \"./common/render.js\";\n\nexport { defineElement, html };\n\n/**\n * ██████████ ████\n * ░░███░░░░░█░░███\n * ░███ █ ░ ░███ ██████ ████████ ██████\n * ░██████ ░███ ███░░███░░███░░███ ░░░░░███\n * ░███░░█ ░███ ░███████ ░███ ░███ ███████\n * ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███\n * ██████████ █████░░██████ ████ █████░░████████\n * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░\n *\n * Elena Custom Elements\n * https://elenajs.com\n *\n * @constructor\n * @param {ElenaConstructor} superClass - Class constructor to extend.\n * @param {ElenaOptions} options - Configuration options for Elena.\n * @returns {CustomElementConstructor}\n */\nexport function Elena(superClass, options) {\n /**\n * Pre-compile element resolver once at class definition\n * time to improve performance:\n *\n * 1. no selector: firstElementChild (property access)\n * 2. className: getElementsByClassName (skips full selector parser)\n * 3. any other: querySelector (full parser, only when needed)\n */\n const resolveElement = !(options && options.element)\n ? host => host.firstElementChild\n : /^[a-z][a-z0-9-]*$/i.test(options.element)\n ? host => host.getElementsByClassName(options.element)[0]\n : host => host.querySelector(options.element);\n\n /**\n * Set up the initial state and default\n * values for the Elena Custom Element.\n */\n class ElenaElement extends superClass {\n /**\n * Reference to the base element in the provided template.\n *\n * @type {Object}\n */\n element = null;\n\n /**\n * This method is called when the Elena Element’s\n * props are changed, added, removed or replaced.\n *\n * @param {string} prop\n * @param {string} oldValue\n * @param {string} newValue\n */\n attributeChangedCallback(prop, oldValue, newValue) {\n getProps(this, prop, oldValue, newValue);\n\n // Re-render when attributes change (after initial render)\n if (this.element && oldValue !== newValue) {\n this.render();\n }\n }\n\n /**\n * This static method returns an array containing the\n * names of the props we want to observe.\n */\n static get observedAttributes() {\n return options && options.props ? options.props : [];\n }\n\n /**\n * Override in subclasses to define the element's HTML structure.\n * No-op by default — elements without a render method connect safely.\n */\n render() {}\n\n /**\n * Render a tagged template literal with auto-escaping and DOM diffing.\n * Usage: this.template`<span>${this.label}</span>`\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n */\n template(strings, ...values) {\n renderTemplate(this, strings, values);\n }\n\n /**\n * This method is called each time the Elena\n * Custom Element is added to the document.\n */\n connectedCallback() {\n this.render();\n\n if (!this.element) {\n this.element = resolveElement(this);\n\n if (!this.element) {\n console.warn(\"░█ [ELENA]: No element found, using firstElementChild as fallback.\");\n this.element = this.firstElementChild;\n }\n }\n\n // Flush props set before connection to host and inner element attributes\n if (this._props) {\n for (const [prop, value] of this._props) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n if (typeof value === \"string\" && value === \"\") {\n this.removeAttribute(prop);\n this.element.removeAttribute(prop);\n } else {\n syncAttribute(this, prop, attrValue);\n syncAttribute(this.element, prop, attrValue);\n }\n }\n }\n\n if (!this._events && options && options.events) {\n this._events = true;\n options.events?.forEach(e => {\n this.element.addEventListener(e, this);\n this[e] = (...args) => this.element[e](...args);\n });\n }\n\n this.updated();\n }\n\n /**\n * Perform post-update after each render().\n *\n * @internal\n */\n updated() {\n if (!this._hydrated) {\n this._hydrated = true;\n this.classList.add(\"elena-hydrated\");\n }\n }\n\n /**\n * This method is called each time the Elena Custom\n * Element is removed from the document.\n */\n disconnectedCallback() {\n if (this._events) {\n this._events = false;\n options.events?.forEach(e => {\n this.element?.removeEventListener(e, this);\n });\n }\n }\n\n /**\n * Handles events on the custom element.\n *\n * @internal\n */\n handleEvent(event) {\n if (options.events?.includes(event.type)) {\n event.stopPropagation();\n /** @internal */\n this.dispatchEvent(new ElenaEvent(event.type, { cancelable: true }));\n }\n }\n }\n\n if (options && options.props?.length) {\n setProps(ElenaElement.prototype, options.props);\n }\n\n return ElenaElement;\n}\n\n/**\n * @typedef {Object} ElenaOptions\n * @property {string[]} [props] - Props observed and synced as attributes.\n * @property {string[]} [events] - Events to delegate from the inner element.\n * @property {string} [element] - CSS selector for the inner element.\n */\n\n/**\n * @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor\n */\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","syncAttribute","element","name","removeAttribute","setAttribute","console","warn","ElenaEvent","Event","constructor","eventInitDict","super","bubbles","composed","defineElement","tagName","Element","window","customElements","get","define","escapeHtml","str","Escape","String","replace","c","html","strings","values","result","reduce","acc","i","__raw","toString","renderTemplate","_tplStrings","_tplParts","needsFullRender","length","v","isRaw","newRendered","_tplValues","textNode","textContent","patchTextNodes","renderedValues","map","markup","out","trim","replaceChildren","_range","document","createRange","createContextualFragment","renderHtml","escapedValues","parts","Array","walker","createTreeWalker","NodeFilter","SHOW_TEXT","node","valueIndex","nextNode","mapTextNodes","fullRender","Elena","superClass","options","resolveElement","test","host","getElementsByClassName","querySelector","firstElementChild","ElenaElement","attributeChangedCallback","prop","oldValue","newValue","context","newAttr","getProps","this","render","observedAttributes","props","template","connectedCallback","_props","attrValue","_events","events","forEach","e","addEventListener","args","updated","_hydrated","classList","add","disconnectedCallback","removeEventListener","handleEvent","event","includes","stopPropagation","dispatchEvent","cancelable","proto","properties","Object","defineProperty","configurable","enumerable","undefined","set","Map","isConnected","setProps","prototype"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle.js","sources":["../src/common/props.js","../src/common/events.js","../src/common/utils.js","../src/common/render.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 return value && JSON.parse(value);\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 attributes to a null element.\");\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[]} properties - Prop names to define\n */\nexport function setProps(proto, properties) {\n for (const prop of properties) {\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 const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n if (this.element) {\n syncAttribute(this.element, prop, attrValue);\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 const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\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 * 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 * Tagged template for trusted HTML fragments with auto-escaped interpolations.\n * Use inside `this.template` for conditional HTML blocks.\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n * @returns {{ __raw: true, toString(): string }}\n */\nexport function html(strings, ...values) {\n const result = strings.reduce(\n (acc, str, i) => acc + str + escapeHtml(String(values[i] ?? \"\")),\n \"\"\n );\n return { __raw: true, toString: () => result };\n}\n","import { escapeHtml } from \"./utils.js\";\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n *\n * On first render, builds the full HTML markup and render.\n * On re-renders, patches only the text nodes whose values changed,\n * avoiding a full DOM rebuild.\n *\n * Cache state is stored on the element instance:\n * - _tplStrings: reference to the template’s static strings array\n * - _tplValues: array of escaped values from the last render\n * - _tplParts: array mapping each value index to its DOM text node\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return;\n }\n fullRender(element, strings, values);\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n * Returns true if all changes were handled (no full render needed).\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 let needsFullRender = false;\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const isRaw = v && v.__raw;\n const newRendered = isRaw ? String(v) : escapeHtml(String(v ?? \"\"));\n const oldRendered = element._tplValues[i];\n\n if (newRendered === oldRendered) {\n continue;\n }\n\n element._tplValues[i] = newRendered;\n\n // Raw HTML values or attribute-position values require a full render\n if (isRaw) {\n needsFullRender = true;\n } else {\n const textNode = element._tplParts[i];\n if (textNode) {\n // Value is in a text position, update the DOM node directly\n textNode.textContent = String(v ?? \"\");\n } else {\n // Value is in an attribute position, can’t patch, need full render\n needsFullRender = true;\n }\n }\n }\n\n return !needsFullRender;\n}\n\n/**\n * Cold path: build full HTML markup, render it via DocumentFragment,\n * and map each interpolated value to its corresponding DOM text node\n * for future fast-path patching.\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 const renderedValues = values.map(v => (v && v.__raw ? String(v) : escapeHtml(String(v ?? \"\"))));\n\n // Build the complete HTML string, stripping template indentation.\n // 1. Replace newlines + indentation with a single space\n // 2. Clean up, remove whitespace between/around tags and trim\n const markup = strings\n .reduce((out, str, i) => out + str.replace(/\\n\\s*/g, \" \") + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .replace(/>\\s+/g, \">\")\n .replace(/\\s+</g, \"<\")\n .trim();\n\n renderHtml(element, markup);\n\n // Cache template identity and rendered values\n element._tplStrings = strings;\n element._tplValues = renderedValues;\n\n // Walk text nodes to map each value index to its DOM node\n element._tplParts = mapTextNodes(element, renderedValues);\n}\n\n/**\n * Walk the Elena Element’s text nodes and map each escaped value\n * to its corresponding DOM text node. Values in attribute\n * positions won't have a matching text node and will be null.\n *\n * @param {HTMLElement} element - The host element to walk\n * @param {string[]} escapedValues - HTML-escaped interpolated values\n * @returns {Array<Text | undefined>} Array mapping each value index to its text node\n */\nfunction mapTextNodes(element, escapedValues) {\n const parts = new Array(escapedValues.length);\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\n\n let valueIndex = 0;\n let node;\n\n while ((node = walker.nextNode()) && valueIndex < escapedValues.length) {\n if (node.textContent === escapedValues[valueIndex]) {\n parts[valueIndex] = node;\n valueIndex++;\n }\n }\n\n return parts;\n}\n\n/**\n * Lazily-created Range reused across renders as a createContextualFragment\n * factory. Deferred so the module can be imported in non-browser environments.\n *\n * @type {Range | undefined}\n */\nlet _range;\n\n/**\n * Render an HTML string into an element by parsing it into a\n * DocumentFragment via createContextualFragment and swapping\n * the element’s children in a single replaceChildren call.\n *\n * @param {HTMLElement} element\n * @param {string} markup\n */\nexport function renderHtml(element, markup) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot render to a null element.\");\n return;\n }\n element.replaceChildren((_range ??= document.createRange()).createContextualFragment(markup));\n}\n","/**\n * ██████████ ████\n * ░░███░░░░░█░░███\n * ░███ █ ░ ░███ ██████ ████████ ██████\n * ░██████ ░███ ███░░███░░███░░███ ░░░░░███\n * ░███░░█ ░███ ░███████ ░███ ░███ ███████\n * ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███\n * ██████████ █████░░██████ ████ █████░░████████\n * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░\n *\n * Elena Custom Elements\n * https://elenajs.com\n */\n\nimport { setProps, getProps, getPropValue, syncAttribute } from \"./common/props.js\";\nimport { ElenaEvent } from \"./common/events.js\";\nimport { defineElement, html } from \"./common/utils.js\";\nimport { renderTemplate } from \"./common/render.js\";\n\nexport { defineElement, html };\n\n/**\n * @typedef {Object} ElenaOptions\n * @property {string[]} [props] - Props observed and synced as attributes.\n * @property {string[]} [events] - Events to delegate from the inner element.\n * @property {string} [element] - CSS selector for the inner element.\n */\n\n/**\n * @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor\n */\n\n/**\n * Factory that creates Elena mixin class.\n *\n * Wraps `superClass` with Elena’s lifecycle, templating, props,\n * and events features. The `options` argument is optional.\n *\n * @param {ElenaConstructor} superClass - Base class to extend.\n * @param {ElenaOptions} [options] - Optional configuration options.\n * @returns {CustomElementConstructor} A class ready to be registered.\n */\nexport function Elena(superClass, options) {\n /**\n * Pre-compile element resolver once at class definition\n * time to improve performance:\n *\n * 1. no selector: firstElementChild (property access)\n * 2. className: getElementsByClassName (skips full selector parser)\n * 3. any other: querySelector (full parser, only when needed)\n */\n const resolveElement = !(options && options.element)\n ? host => host.firstElementChild\n : /^[a-z][a-z0-9-]*$/i.test(options.element)\n ? host => host.getElementsByClassName(options.element)[0]\n : host => host.querySelector(options.element);\n\n /**\n * Set up the initial state and default values for Elena Element.\n */\n class ElenaElement extends superClass {\n /**\n * Reference to the base element in the provided template.\n *\n * @type {Object}\n */\n element = null;\n\n /**\n * This method is called when the Elena Element’s\n * props are changed, added, removed or replaced.\n *\n * @param {string} prop\n * @param {string} oldValue\n * @param {string} newValue\n */\n attributeChangedCallback(prop, oldValue, newValue) {\n getProps(this, prop, oldValue, newValue);\n\n // Re-render when attributes change (after initial render)\n if (this.element && oldValue !== newValue) {\n this.render();\n }\n }\n\n /**\n * This static method returns an array containing the\n * names of the props we want to observe.\n */\n static get observedAttributes() {\n return options && options.props ? options.props : [];\n }\n\n /**\n * Override in a subclass to define the element's HTML structure.\n * No-op by default: elements without a render method connect safely.\n */\n render() {}\n\n /**\n * Render a tagged template literal with auto-escaping and DOM diffing.\n * Usage: this.template`<span>${this.label}</span>`\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n */\n template(strings, ...values) {\n renderTemplate(this, strings, values);\n }\n\n /**\n * This method is called each time the Elena Element\n * is added to the document.\n */\n connectedCallback() {\n this.render();\n\n if (!this.element) {\n this.element = resolveElement(this);\n\n if (!this.element) {\n console.warn(\"░█ [ELENA]: No element found, using firstElementChild as fallback.\");\n this.element = this.firstElementChild;\n }\n }\n\n // Flush props set before connection to host and inner element attributes\n if (this._props) {\n for (const [prop, value] of this._props) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n syncAttribute(this.element, prop, attrValue);\n }\n }\n\n if (!this._events && options && options.events) {\n this._events = true;\n options.events?.forEach(e => {\n this.element.addEventListener(e, this);\n this[e] = (...args) => this.element[e](...args);\n });\n }\n\n this.updated();\n }\n\n /**\n * Perform post-update after each render().\n *\n * @internal\n */\n updated() {\n if (!this._hydrated) {\n this._hydrated = true;\n this.classList.add(\"elena-hydrated\");\n }\n }\n\n /**\n * This method is called each time the Elena Element\n * is removed from the document.\n */\n disconnectedCallback() {\n if (this._events) {\n this._events = false;\n options.events?.forEach(e => {\n this.element?.removeEventListener(e, this);\n });\n }\n }\n\n /**\n * Handles events on the Elena Element.\n *\n * @internal\n */\n handleEvent(event) {\n if (options.events?.includes(event.type)) {\n event.stopPropagation();\n /** @internal */\n this.dispatchEvent(new ElenaEvent(event.type, { cancelable: true }));\n }\n }\n }\n\n if (options && options.props?.length) {\n setProps(ElenaElement.prototype, options.props);\n }\n\n return ElenaElement;\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","syncAttribute","element","name","removeAttribute","setAttribute","console","warn","ElenaEvent","Event","constructor","eventInitDict","super","bubbles","composed","defineElement","tagName","Element","window","customElements","get","define","escapeHtml","str","Escape","String","replace","c","html","strings","values","result","reduce","acc","i","__raw","toString","renderTemplate","_tplStrings","_tplParts","needsFullRender","length","v","isRaw","newRendered","_tplValues","textNode","textContent","patchTextNodes","renderedValues","map","markup","out","trim","replaceChildren","_range","document","createRange","createContextualFragment","renderHtml","escapedValues","parts","Array","walker","createTreeWalker","NodeFilter","SHOW_TEXT","node","valueIndex","nextNode","mapTextNodes","fullRender","Elena","superClass","options","resolveElement","test","host","getElementsByClassName","querySelector","firstElementChild","ElenaElement","attributeChangedCallback","prop","oldValue","newValue","context","newAttr","getProps","this","render","observedAttributes","props","template","connectedCallback","_props","attrValue","_events","events","forEach","e","addEventListener","args","updated","_hydrated","classList","add","disconnectedCallback","removeEventListener","handleEvent","event","includes","stopPropagation","dispatchEvent","cancelable","proto","properties","Object","defineProperty","configurable","enumerable","undefined","set","Map","isConnected","setProps","prototype"],"mappings":"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"}
|
package/dist/common/events.d.ts
CHANGED
package/dist/common/props.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Get the value of the Elena
|
|
2
|
+
* Get the value of the Elena Element property.
|
|
3
3
|
*
|
|
4
4
|
* @param {string} type
|
|
5
5
|
* @param {any} value
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export function getPropValue(type: string, value: any, transform?: "toAttribute" | "toProp"): any;
|
|
9
9
|
/**
|
|
10
|
-
* Set or remove an attribute on an
|
|
10
|
+
* Set or remove an attribute on an Elena Element.
|
|
11
11
|
*
|
|
12
12
|
* @param {Element} element - Target element
|
|
13
13
|
* @param {string} name - Attribute name
|
|
@@ -24,7 +24,7 @@ export function syncAttribute(element: Element, name: string, value: string | nu
|
|
|
24
24
|
*/
|
|
25
25
|
export function setProps(proto: Function, properties: string[]): void;
|
|
26
26
|
/**
|
|
27
|
-
* We need to update the internals of
|
|
27
|
+
* We need to update the internals of the Elena Element
|
|
28
28
|
* when props on the host element are changed.
|
|
29
29
|
*
|
|
30
30
|
* @param {object} context
|
package/dist/common/render.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Render a tagged template into an
|
|
2
|
+
* Render a tagged template into an Elena Element with DOM diffing.
|
|
3
3
|
*
|
|
4
4
|
* On first render, builds the full HTML markup and render.
|
|
5
5
|
* On re-renders, patches only the text nodes whose values changed,
|
|
6
6
|
* avoiding a full DOM rebuild.
|
|
7
7
|
*
|
|
8
8
|
* Cache state is stored on the element instance:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* - _tplStrings: reference to the template’s static strings array
|
|
10
|
+
* - _tplValues: array of escaped values from the last render
|
|
11
|
+
* - _tplParts: array mapping each value index to its DOM text node
|
|
12
12
|
*
|
|
13
13
|
* @param {HTMLElement} element
|
|
14
14
|
* @param {TemplateStringsArray} strings - Static parts of the tagged template
|
|
@@ -18,7 +18,7 @@ export function renderTemplate(element: HTMLElement, strings: TemplateStringsArr
|
|
|
18
18
|
/**
|
|
19
19
|
* Render an HTML string into an element by parsing it into a
|
|
20
20
|
* DocumentFragment via createContextualFragment and swapping
|
|
21
|
-
* the element
|
|
21
|
+
* the element’s children in a single replaceChildren call.
|
|
22
22
|
*
|
|
23
23
|
* @param {HTMLElement} element
|
|
24
24
|
* @param {string} markup
|
package/dist/common/utils.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.js"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,uCAHW,MAAM,2BAShB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,GACJ,MAAM,CAKlB;AAED;;;;;;;GAOG;AACH,8BAJW,oBAAoB,aACjB,GAAC,EAAA,GACF;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,IAAI,MAAM,CAAA;CAAE,CAQ/C"}
|
package/dist/elena.d.ts
CHANGED
|
@@ -1,43 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
|
|
2
|
+
* @typedef {Object} ElenaOptions
|
|
3
|
+
* @property {string[]} [props] - Props observed and synced as attributes.
|
|
4
|
+
* @property {string[]} [events] - Events to delegate from the inner element.
|
|
5
|
+
* @property {string} [element] - CSS selector for the inner element.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Factory that creates Elena mixin class.
|
|
10
12
|
*
|
|
11
|
-
* Elena
|
|
12
|
-
*
|
|
13
|
+
* Wraps `superClass` with Elena’s lifecycle, templating, props,
|
|
14
|
+
* and events features. The `options` argument is optional.
|
|
13
15
|
*
|
|
14
|
-
* @
|
|
15
|
-
* @param {
|
|
16
|
-
* @
|
|
17
|
-
* @returns {CustomElementConstructor}
|
|
16
|
+
* @param {ElenaConstructor} superClass - Base class to extend.
|
|
17
|
+
* @param {ElenaOptions} [options] - Optional configuration options.
|
|
18
|
+
* @returns {CustomElementConstructor} A class ready to be registered.
|
|
18
19
|
*/
|
|
19
|
-
export function Elena(superClass: ElenaConstructor, options
|
|
20
|
-
export class Elena {
|
|
21
|
-
/**
|
|
22
|
-
* ██████████ ████
|
|
23
|
-
* ░░███░░░░░█░░███
|
|
24
|
-
* ░███ █ ░ ░███ ██████ ████████ ██████
|
|
25
|
-
* ░██████ ░███ ███░░███░░███░░███ ░░░░░███
|
|
26
|
-
* ░███░░█ ░███ ░███████ ░███ ░███ ███████
|
|
27
|
-
* ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███
|
|
28
|
-
* ██████████ █████░░██████ ████ █████░░████████
|
|
29
|
-
* ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░
|
|
30
|
-
*
|
|
31
|
-
* Elena Custom Elements
|
|
32
|
-
* https://elenajs.com
|
|
33
|
-
*
|
|
34
|
-
* @constructor
|
|
35
|
-
* @param {ElenaConstructor} superClass - Class constructor to extend.
|
|
36
|
-
* @param {ElenaOptions} options - Configuration options for Elena.
|
|
37
|
-
* @returns {CustomElementConstructor}
|
|
38
|
-
*/
|
|
39
|
-
constructor(superClass: ElenaConstructor, options: ElenaOptions);
|
|
40
|
-
}
|
|
20
|
+
export function Elena(superClass: ElenaConstructor, options?: ElenaOptions): CustomElementConstructor;
|
|
41
21
|
export type ElenaOptions = {
|
|
42
22
|
/**
|
|
43
23
|
* - Props observed and synced as attributes.
|
package/dist/elena.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elena.d.ts","sourceRoot":"","sources":["../src/elena.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"elena.d.ts","sourceRoot":"","sources":["../src/elena.js"],"names":[],"mappings":"AAqBA;;;;;GAKG;AAEH;;GAEG;AAEH;;;;;;;;;GASG;AACH,kCAJW,gBAAgB,YAChB,YAAY,GACV,wBAAwB,CAsJpC;;;;;YAvKa,MAAM,EAAE;;;;aACR,MAAM,EAAE;;;;cACR,MAAM;;+BAIP,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,WAAW;8BAbZ,mBAAmB;qBAAnB,mBAAmB"}
|
package/dist/elena.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{setProps as e,getProps as t,getPropValue as s,syncAttribute as n}from"./props.js";import{ElenaEvent as i}from"./events.js";export{defineElement,html}from"./utils.js";import{renderTemplate as r}from"./render.js";function l(l,h){const o=h&&h.element?/^[a-z][a-z0-9-]*$/i.test(h.element)?e=>e.getElementsByClassName(h.element)[0]:e=>e.querySelector(h.element):e=>e.firstElementChild;class a extends l{element=null;attributeChangedCallback(e,s,n){t(this,e,s,n),this.element&&s!==n&&this.render()}static get observedAttributes(){return h&&h.props?h.props:[]}render(){}template(e,...t){r(this,e,t)}connectedCallback(){if(this.render(),this.element||(this.element=o(this),this.element||(console.warn("░█ [ELENA]: No element found, using firstElementChild as fallback."),this.element=this.firstElementChild)),this._props)for(const[e,t]of this._props){const i=s(typeof t,t,"toAttribute");
|
|
1
|
+
import{setProps as e,getProps as t,getPropValue as s,syncAttribute as n}from"./props.js";import{ElenaEvent as i}from"./events.js";export{defineElement,html}from"./utils.js";import{renderTemplate as r}from"./render.js";function l(l,h){const o=h&&h.element?/^[a-z][a-z0-9-]*$/i.test(h.element)?e=>e.getElementsByClassName(h.element)[0]:e=>e.querySelector(h.element):e=>e.firstElementChild;class a extends l{element=null;attributeChangedCallback(e,s,n){t(this,e,s,n),this.element&&s!==n&&this.render()}static get observedAttributes(){return h&&h.props?h.props:[]}render(){}template(e,...t){r(this,e,t)}connectedCallback(){if(this.render(),this.element||(this.element=o(this),this.element||(console.warn("░█ [ELENA]: No element found, using firstElementChild as fallback."),this.element=this.firstElementChild)),this._props)for(const[e,t]of this._props){const i=s(typeof t,t,"toAttribute");n(this,e,i),n(this.element,e,i)}!this._events&&h&&h.events&&(this._events=!0,h.events?.forEach(e=>{this.element.addEventListener(e,this),this[e]=(...t)=>this.element[e](...t)})),this.updated()}updated(){this._hydrated||(this._hydrated=!0,this.classList.add("elena-hydrated"))}disconnectedCallback(){this._events&&(this._events=!1,h.events?.forEach(e=>{this.element?.removeEventListener(e,this)}))}handleEvent(e){h.events?.includes(e.type)&&(e.stopPropagation(),this.dispatchEvent(new i(e.type,{cancelable:!0})))}}return h&&h.props?.length&&e(a.prototype,h.props),a}export{l as Elena};
|
|
2
2
|
//# sourceMappingURL=elena.js.map
|
package/dist/elena.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elena.js","sources":["../src/elena.js"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"elena.js","sources":["../src/elena.js"],"sourcesContent":["/**\n * ██████████ ████\n * ░░███░░░░░█░░███\n * ░███ █ ░ ░███ ██████ ████████ ██████\n * ░██████ ░███ ███░░███░░███░░███ ░░░░░███\n * ░███░░█ ░███ ░███████ ░███ ░███ ███████\n * ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███\n * ██████████ █████░░██████ ████ █████░░████████\n * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░\n *\n * Elena Custom Elements\n * https://elenajs.com\n */\n\nimport { setProps, getProps, getPropValue, syncAttribute } from \"./common/props.js\";\nimport { ElenaEvent } from \"./common/events.js\";\nimport { defineElement, html } from \"./common/utils.js\";\nimport { renderTemplate } from \"./common/render.js\";\n\nexport { defineElement, html };\n\n/**\n * @typedef {Object} ElenaOptions\n * @property {string[]} [props] - Props observed and synced as attributes.\n * @property {string[]} [events] - Events to delegate from the inner element.\n * @property {string} [element] - CSS selector for the inner element.\n */\n\n/**\n * @typedef {new (...args: any[]) => HTMLElement} ElenaConstructor\n */\n\n/**\n * Factory that creates Elena mixin class.\n *\n * Wraps `superClass` with Elena’s lifecycle, templating, props,\n * and events features. The `options` argument is optional.\n *\n * @param {ElenaConstructor} superClass - Base class to extend.\n * @param {ElenaOptions} [options] - Optional configuration options.\n * @returns {CustomElementConstructor} A class ready to be registered.\n */\nexport function Elena(superClass, options) {\n /**\n * Pre-compile element resolver once at class definition\n * time to improve performance:\n *\n * 1. no selector: firstElementChild (property access)\n * 2. className: getElementsByClassName (skips full selector parser)\n * 3. any other: querySelector (full parser, only when needed)\n */\n const resolveElement = !(options && options.element)\n ? host => host.firstElementChild\n : /^[a-z][a-z0-9-]*$/i.test(options.element)\n ? host => host.getElementsByClassName(options.element)[0]\n : host => host.querySelector(options.element);\n\n /**\n * Set up the initial state and default values for Elena Element.\n */\n class ElenaElement extends superClass {\n /**\n * Reference to the base element in the provided template.\n *\n * @type {Object}\n */\n element = null;\n\n /**\n * This method is called when the Elena Element’s\n * props are changed, added, removed or replaced.\n *\n * @param {string} prop\n * @param {string} oldValue\n * @param {string} newValue\n */\n attributeChangedCallback(prop, oldValue, newValue) {\n getProps(this, prop, oldValue, newValue);\n\n // Re-render when attributes change (after initial render)\n if (this.element && oldValue !== newValue) {\n this.render();\n }\n }\n\n /**\n * This static method returns an array containing the\n * names of the props we want to observe.\n */\n static get observedAttributes() {\n return options && options.props ? options.props : [];\n }\n\n /**\n * Override in a subclass to define the element's HTML structure.\n * No-op by default: elements without a render method connect safely.\n */\n render() {}\n\n /**\n * Render a tagged template literal with auto-escaping and DOM diffing.\n * Usage: this.template`<span>${this.label}</span>`\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n */\n template(strings, ...values) {\n renderTemplate(this, strings, values);\n }\n\n /**\n * This method is called each time the Elena Element\n * is added to the document.\n */\n connectedCallback() {\n this.render();\n\n if (!this.element) {\n this.element = resolveElement(this);\n\n if (!this.element) {\n console.warn(\"░█ [ELENA]: No element found, using firstElementChild as fallback.\");\n this.element = this.firstElementChild;\n }\n }\n\n // Flush props set before connection to host and inner element attributes\n if (this._props) {\n for (const [prop, value] of this._props) {\n const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n syncAttribute(this.element, prop, attrValue);\n }\n }\n\n if (!this._events && options && options.events) {\n this._events = true;\n options.events?.forEach(e => {\n this.element.addEventListener(e, this);\n this[e] = (...args) => this.element[e](...args);\n });\n }\n\n this.updated();\n }\n\n /**\n * Perform post-update after each render().\n *\n * @internal\n */\n updated() {\n if (!this._hydrated) {\n this._hydrated = true;\n this.classList.add(\"elena-hydrated\");\n }\n }\n\n /**\n * This method is called each time the Elena Element\n * is removed from the document.\n */\n disconnectedCallback() {\n if (this._events) {\n this._events = false;\n options.events?.forEach(e => {\n this.element?.removeEventListener(e, this);\n });\n }\n }\n\n /**\n * Handles events on the Elena Element.\n *\n * @internal\n */\n handleEvent(event) {\n if (options.events?.includes(event.type)) {\n event.stopPropagation();\n /** @internal */\n this.dispatchEvent(new ElenaEvent(event.type, { cancelable: true }));\n }\n }\n }\n\n if (options && options.props?.length) {\n setProps(ElenaElement.prototype, options.props);\n }\n\n return ElenaElement;\n}\n"],"names":["Elena","superClass","options","resolveElement","element","test","host","getElementsByClassName","querySelector","firstElementChild","ElenaElement","attributeChangedCallback","prop","oldValue","newValue","getProps","this","render","observedAttributes","props","template","strings","values","renderTemplate","connectedCallback","console","warn","_props","value","attrValue","getPropValue","syncAttribute","_events","events","forEach","e","addEventListener","args","updated","_hydrated","classList","add","disconnectedCallback","removeEventListener","handleEvent","event","includes","type","stopPropagation","dispatchEvent","ElenaEvent","cancelable","length","setProps","prototype"],"mappings":"0NA0CO,SAASA,EAAMC,EAAYC,GAShC,MAAMC,EAAmBD,GAAWA,EAAQE,QAExC,qBAAqBC,KAAKH,EAAQE,SAChCE,GAAQA,EAAKC,uBAAuBL,EAAQE,SAAS,GACrDE,GAAQA,EAAKE,cAAcN,EAAQE,SAHrCE,GAAQA,EAAKG,kBAQjB,MAAMC,UAAqBT,EAMzBG,QAAU,KAUV,wBAAAO,CAAyBC,EAAMC,EAAUC,GACvCC,EAASC,KAAMJ,EAAMC,EAAUC,GAG3BE,KAAKZ,SAAWS,IAAaC,GAC/BE,KAAKC,QAET,CAMA,6BAAWC,GACT,OAAOhB,GAAWA,EAAQiB,MAAQjB,EAAQiB,MAAQ,EACpD,CAMA,MAAAF,GAAU,CASV,QAAAG,CAASC,KAAYC,GACnBC,EAAeP,KAAMK,EAASC,EAChC,CAMA,iBAAAE,GAaE,GAZAR,KAAKC,SAEAD,KAAKZ,UACRY,KAAKZ,QAAUD,EAAea,MAEzBA,KAAKZ,UACRqB,QAAQC,KAAK,sEACbV,KAAKZ,QAAUY,KAAKP,oBAKpBO,KAAKW,OACP,IAAK,MAAOf,EAAMgB,KAAUZ,KAAKW,OAAQ,CACvC,MAAME,EAAYC,SAAoBF,EAAOA,EAAO,eACpDG,EAAcf,KAAMJ,EAAMiB,GAC1BE,EAAcf,KAAKZ,QAASQ,EAAMiB,EACpC,EAGGb,KAAKgB,SAAW9B,GAAWA,EAAQ+B,SACtCjB,KAAKgB,SAAU,EACf9B,EAAQ+B,QAAQC,QAAQC,IACtBnB,KAAKZ,QAAQgC,iBAAiBD,EAAGnB,MACjCA,KAAKmB,GAAK,IAAIE,IAASrB,KAAKZ,QAAQ+B,MAAME,MAI9CrB,KAAKsB,SACP,CAOA,OAAAA,GACOtB,KAAKuB,YACRvB,KAAKuB,WAAY,EACjBvB,KAAKwB,UAAUC,IAAI,kBAEvB,CAMA,oBAAAC,GACM1B,KAAKgB,UACPhB,KAAKgB,SAAU,EACf9B,EAAQ+B,QAAQC,QAAQC,IACtBnB,KAAKZ,SAASuC,oBAAoBR,EAAGnB,QAG3C,CAOA,WAAA4B,CAAYC,GACN3C,EAAQ+B,QAAQa,SAASD,EAAME,QACjCF,EAAMG,kBAENhC,KAAKiC,cAAc,IAAIC,EAAWL,EAAME,KAAM,CAAEI,YAAY,KAEhE,EAOF,OAJIjD,GAAWA,EAAQiB,OAAOiC,QAC5BC,EAAS3C,EAAa4C,UAAWpD,EAAQiB,OAGpCT,CACT"}
|
package/dist/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sources":["../src/common/events.js"],"sourcesContent":["/**\n * A base class for events which defaults to
|
|
1
|
+
{"version":3,"file":"events.js","sources":["../src/common/events.js"],"sourcesContent":["/**\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"],"names":["ElenaEvent","Event","constructor","type","eventInitDict","super","bubbles","composed"],"mappings":"AAIO,MAAMA,UAAmBC,MAC9B,WAAAC,CAAYC,EAAMC,GAChBC,MAAMF,EAAM,CACVG,SAAS,EACTC,UAAU,KACPH,GAEP"}
|
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
|
|
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":return e&&JSON.parse(e);case"boolean":default:return e;case"number":return null!==e?+e:e}}function e(t,e,n){t?null===n?t.removeAttribute(e):t.setAttribute(e,n):console.warn("░█ [ELENA]: Cannot sync attributes to a null element.")}function n(n,r){for(const o of r)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))return;if(this._props.set(o,n),!this.isConnected)return;const r=t(typeof n,n,"toAttribute");e(this,o,r),this.element&&e(this.element,o,r)}})}function r(e,n,r,o){if(r!==o){const r=t(typeof e[n],o,"toProp");e[n]=r}}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
|
|
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 return value && JSON.parse(value);\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 attributes to a null element.\");\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[]} properties - Prop names to define\n */\nexport function setProps(proto, properties) {\n for (const prop of properties) {\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 const attrValue = getPropValue(typeof value, value, \"toAttribute\");\n syncAttribute(this, prop, attrValue);\n if (this.element) {\n syncAttribute(this.element, prop, attrValue);\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 const newAttr = getPropValue(type, newValue, \"toProp\");\n context[name] = newAttr;\n }\n}\n"],"names":["getPropValue","type","value","transform","JSON","stringify","parse","syncAttribute","element","name","removeAttribute","setAttribute","console","warn","setProps","proto","properties","prop","Object","defineProperty","configurable","enumerable","get","this","_props","undefined","set","Map","isConnected","attrValue","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,OAAOC,GAASE,KAAKE,MAAMJ,GAC7B,IAAK,UAIL,QACE,OAAOA,EAHT,IAAK,SACH,OAAiB,OAAVA,GAAkBA,EAAQA,EAKzC,CASO,SAASK,EAAcC,EAASC,EAAMP,GACtCM,EAIS,OAAVN,EACFM,EAAQE,gBAAgBD,GAExBD,EAAQG,aAAaF,EAAMP,GAN3BU,QAAQC,KAAK,wDAQjB,CAUO,SAASC,EAASC,EAAOC,GAC9B,IAAK,MAAMC,KAAQD,EACjBE,OAAOC,eAAeJ,EAAOE,EAAM,CACjCG,cAAc,EACdC,YAAY,EACZ,GAAAC,GACE,OAAOC,KAAKC,OAASD,KAAKC,OAAOF,IAAIL,QAAQQ,CAC/C,EACA,GAAAC,CAAIxB,GAIF,GAHKqB,KAAKC,SACRD,KAAKC,OAAS,IAAIG,KAEhBzB,IAAUqB,KAAKC,OAAOF,IAAIL,GAC5B,OAIF,GADAM,KAAKC,OAAOE,IAAIT,EAAMf,IACjBqB,KAAKK,YACR,OAGF,MAAMC,EAAY7B,SAAoBE,EAAOA,EAAO,eACpDK,EAAcgB,KAAMN,EAAMY,GACtBN,KAAKf,SACPD,EAAcgB,KAAKf,QAASS,EAAMY,EAEtC,GAGN,CAWO,SAASC,EAASC,EAAStB,EAAMuB,EAAUC,GAChD,GAAID,IAAaC,EAAU,CACzB,MACMC,EAAUlC,SADI+B,EAAQtB,GACOwB,EAAU,UAC7CF,EAAQtB,GAAQyB,CAClB,CACF"}
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sources":["../src/common/render.js"],"sourcesContent":["import { escapeHtml } from \"./utils.js\";\n\n/**\n * Render a tagged template into an
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../src/common/render.js"],"sourcesContent":["import { escapeHtml } from \"./utils.js\";\n\n/**\n * Render a tagged template into an Elena Element with DOM diffing.\n *\n * On first render, builds the full HTML markup and render.\n * On re-renders, patches only the text nodes whose values changed,\n * avoiding a full DOM rebuild.\n *\n * Cache state is stored on the element instance:\n * - _tplStrings: reference to the template’s static strings array\n * - _tplValues: array of escaped values from the last render\n * - _tplParts: array mapping each value index to its DOM text node\n *\n * @param {HTMLElement} element\n * @param {TemplateStringsArray} strings - Static parts of the tagged template\n * @param {Array} values - Dynamic interpolated values\n */\nexport function renderTemplate(element, strings, values) {\n if (patchTextNodes(element, strings, values)) {\n return;\n }\n fullRender(element, strings, values);\n}\n\n/**\n * Fast path: patch only the text nodes whose values changed.\n * Returns true if all changes were handled (no full render needed).\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 let needsFullRender = false;\n\n for (let i = 0; i < values.length; i++) {\n const v = values[i];\n const isRaw = v && v.__raw;\n const newRendered = isRaw ? String(v) : escapeHtml(String(v ?? \"\"));\n const oldRendered = element._tplValues[i];\n\n if (newRendered === oldRendered) {\n continue;\n }\n\n element._tplValues[i] = newRendered;\n\n // Raw HTML values or attribute-position values require a full render\n if (isRaw) {\n needsFullRender = true;\n } else {\n const textNode = element._tplParts[i];\n if (textNode) {\n // Value is in a text position, update the DOM node directly\n textNode.textContent = String(v ?? \"\");\n } else {\n // Value is in an attribute position, can’t patch, need full render\n needsFullRender = true;\n }\n }\n }\n\n return !needsFullRender;\n}\n\n/**\n * Cold path: build full HTML markup, render it via DocumentFragment,\n * and map each interpolated value to its corresponding DOM text node\n * for future fast-path patching.\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 const renderedValues = values.map(v => (v && v.__raw ? String(v) : escapeHtml(String(v ?? \"\"))));\n\n // Build the complete HTML string, stripping template indentation.\n // 1. Replace newlines + indentation with a single space\n // 2. Clean up, remove whitespace between/around tags and trim\n const markup = strings\n .reduce((out, str, i) => out + str.replace(/\\n\\s*/g, \" \") + (renderedValues[i] ?? \"\"), \"\")\n .replace(/>\\s+</g, \"><\")\n .replace(/>\\s+/g, \">\")\n .replace(/\\s+</g, \"<\")\n .trim();\n\n renderHtml(element, markup);\n\n // Cache template identity and rendered values\n element._tplStrings = strings;\n element._tplValues = renderedValues;\n\n // Walk text nodes to map each value index to its DOM node\n element._tplParts = mapTextNodes(element, renderedValues);\n}\n\n/**\n * Walk the Elena Element’s text nodes and map each escaped value\n * to its corresponding DOM text node. Values in attribute\n * positions won't have a matching text node and will be null.\n *\n * @param {HTMLElement} element - The host element to walk\n * @param {string[]} escapedValues - HTML-escaped interpolated values\n * @returns {Array<Text | undefined>} Array mapping each value index to its text node\n */\nfunction mapTextNodes(element, escapedValues) {\n const parts = new Array(escapedValues.length);\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\n\n let valueIndex = 0;\n let node;\n\n while ((node = walker.nextNode()) && valueIndex < escapedValues.length) {\n if (node.textContent === escapedValues[valueIndex]) {\n parts[valueIndex] = node;\n valueIndex++;\n }\n }\n\n return parts;\n}\n\n/**\n * Lazily-created Range reused across renders as a createContextualFragment\n * factory. Deferred so the module can be imported in non-browser environments.\n *\n * @type {Range | undefined}\n */\nlet _range;\n\n/**\n * Render an HTML string into an element by parsing it into a\n * DocumentFragment via createContextualFragment and swapping\n * the element’s children in a single replaceChildren call.\n *\n * @param {HTMLElement} element\n * @param {string} markup\n */\nexport function renderHtml(element, markup) {\n if (!element) {\n console.warn(\"░█ [ELENA]: Cannot render to a null element.\");\n return;\n }\n element.replaceChildren((_range ??= document.createRange()).createContextualFragment(markup));\n}\n"],"names":["renderTemplate","element","strings","values","_tplStrings","_tplParts","needsFullRender","i","length","v","isRaw","__raw","newRendered","String","escapeHtml","_tplValues","textNode","textContent","patchTextNodes","renderedValues","map","markup","reduce","out","str","replace","trim","renderHtml","escapedValues","parts","Array","walker","document","createTreeWalker","NodeFilter","SHOW_TEXT","node","valueIndex","nextNode","mapTextNodes","fullRender","_range","replaceChildren","createRange","createContextualFragment","console","warn"],"mappings":"wCAkBO,SAASA,EAAeC,EAASC,EAASC,IAgBjD,SAAwBF,EAASC,EAASC,GAExC,GAAIF,EAAQG,cAAgBF,IAAYD,EAAQI,UAC9C,OAAO,EAGT,IAAIC,GAAkB,EAEtB,IAAK,IAAIC,EAAI,EAAGA,EAAIJ,EAAOK,OAAQD,IAAK,CACtC,MAAME,EAAIN,EAAOI,GACXG,EAAQD,GAAKA,EAAEE,MACfC,EAAcF,EAAQG,OAAOJ,GAAKK,EAAWD,OAAOJ,GAAK,KAG/D,GAAIG,IAFgBX,EAAQc,WAAWR,GASvC,GAHAN,EAAQc,WAAWR,GAAKK,EAGpBF,EACFJ,GAAkB,MACb,CACL,MAAMU,EAAWf,EAAQI,UAAUE,GAC/BS,EAEFA,EAASC,YAAcJ,OAAOJ,GAAK,IAGnCH,GAAkB,CAEtB,CACF,CAEA,OAAQA,CACV,EAnDMY,CAAejB,EAASC,EAASC,IA8DvC,SAAoBF,EAASC,EAASC,GACpC,MAAMgB,EAAiBhB,EAAOiB,IAAIX,GAAMA,GAAKA,EAAEE,MAAQE,OAAOJ,GAAKK,EAAWD,OAAOJ,GAAK,MAKpFY,EAASnB,EACZoB,OAAO,CAACC,EAAKC,EAAKjB,IAAMgB,EAAMC,EAAIC,QAAQ,SAAU,MAAQN,EAAeZ,IAAM,IAAK,IACtFkB,QAAQ,SAAU,MAClBA,QAAQ,QAAS,KACjBA,QAAQ,QAAS,KACjBC,OAEHC,EAAW1B,EAASoB,GAGpBpB,EAAQG,YAAcF,EACtBD,EAAQc,WAAaI,EAGrBlB,EAAQI,UAYV,SAAsBJ,EAAS2B,GAC7B,MAAMC,EAAQ,IAAIC,MAAMF,EAAcpB,QAChCuB,EAASC,SAASC,iBAAiBhC,EAASiC,WAAWC,WAE7D,IACIC,EADAC,EAAa,EAGjB,MAAQD,EAAOL,EAAOO,aAAeD,EAAaT,EAAcpB,QAC1D4B,EAAKnB,cAAgBW,EAAcS,KACrCR,EAAMQ,GAAcD,EACpBC,KAIJ,OAAOR,CACT,CA3BsBU,CAAatC,EAASkB,EAC5C,CAhFEqB,CAAWvC,EAASC,EAASC,EAC/B,CAiHA,IAAIsC,EAUG,SAASd,EAAW1B,EAASoB,GAC7BpB,EAILA,EAAQyC,iBAAiBD,IAAWT,SAASW,eAAeC,yBAAyBvB,IAHnFwB,QAAQC,KAAK,+CAIjB"}
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../src/common/utils.js"],"sourcesContent":["/**\n * Register the Elena
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../src/common/utils.js"],"sourcesContent":["/**\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 * Tagged template for trusted HTML fragments with auto-escaped interpolations.\n * Use inside `this.template` for conditional HTML blocks.\n *\n * @param {TemplateStringsArray} strings\n * @param {...*} values\n * @returns {{ __raw: true, toString(): string }}\n */\nexport function html(strings, ...values) {\n const result = strings.reduce(\n (acc, str, i) => acc + str + escapeHtml(String(values[i] ?? \"\")),\n \"\"\n );\n return { __raw: true, toString: () => result };\n}\n"],"names":["defineElement","tagName","Element","window","customElements","get","define","escapeHtml","str","Escape","String","replace","c","html","strings","values","result","reduce","acc","i","__raw","toString"],"mappings":"AAMO,SAASA,EAAcC,EAASC,GACf,oBAAXC,QAA0B,mBAAoBA,SAClDA,OAAOC,eAAeC,IAAIJ,IAC7BE,OAAOC,eAAeE,OAAOL,EAASC,GAG5C,CAQO,SAASK,EAAWC,GACzB,MAAMC,EAAS,CAAE,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,SAC7E,OAAOC,OAAOF,GAAKG,QAAQ,WAAYC,GAAKH,EAAOG,GACrD,CAUO,SAASC,EAAKC,KAAYC,GAC/B,MAAMC,EAASF,EAAQG,OACrB,CAACC,EAAKV,EAAKW,IAAMD,EAAMV,EAAMD,EAAWG,OAAOK,EAAOI,IAAM,KAC5D,IAEF,MAAO,CAAEC,OAAO,EAAMC,SAAU,IAAML,EACxC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elenajs/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Elena is a lightweight, opinionated library for building blazing fast progressive custom elements and web components.",
|
|
5
5
|
"author": "Ariel Salminen <info@arielsalminen.com>",
|
|
6
6
|
"publishConfig": {
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"typescript": "5.9.3",
|
|
31
31
|
"vitest": "4.0.18"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "1128441ee6620bec259f704ef351e64c1344d514"
|
|
34
34
|
}
|