@elenajs/core 1.0.0-rc.6 → 1.0.0-rc.8
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 +6 -2
- package/dist/common/props.d.ts.map +1 -1
- package/dist/common/render.d.ts.map +1 -1
- package/dist/common/utils.d.ts +3 -0
- package/dist/common/utils.d.ts.map +1 -1
- package/dist/elena.d.ts.map +1 -1
- package/dist/elena.js +6 -2
- package/dist/props.js +1 -2
- package/dist/render.js +1 -2
- package/dist/utils.js +1 -2
- package/package.json +2 -2
- package/src/common/props.js +8 -9
- package/src/common/render.js +40 -34
- package/src/common/utils.js +20 -16
- package/src/elena.js +23 -33
- package/dist/bundle.js.map +0 -1
- package/dist/elena.js.map +0 -1
- package/dist/props.js.map +0 -1
- package/dist/render.js.map +0 -1
- package/dist/utils.js.map +0 -1
package/dist/bundle.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @elenajs/core v1.0.0-rc.8
|
|
3
|
+
* (c) 2025-present Ariel Salminen and Elena contributors
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
const t="░█ [ELENA]: ",s=Array.isArray,i=s=>console.warn(t+s),e={"&":"&","<":"<",">":">",'"':""","'":"'"};function n(t){return s(t)?t.map(o).join(""):o(t)}function o(t){return t?.t?String(t):String(t??"").replace(/[&<>"']/g,t=>e[t])}function r(t,...s){let i;return{t:!0,strings:t,values:s,toString:()=>(null==i&&(i=t.reduce((t,i,e)=>t+i+n(s[e]),"")),i)}}function h(t){return{t:!0,toString:()=>t??""}}const c={t:!0,toString:()=>""},u=t=>s(t)?t.some(t=>t?.t):t?.t,f=t=>s(t)?t.join(""):String(t??"");function l(t){return t.replace(/(>)\n\s*|\n\s*(<)/g,"$1$2").replace(/\n\s*/g," ").replace(/>\s+</g,"><")}function a(t,s,e){if(s="boolean"===t&&"boolean"!=typeof s?null!==s:s,!e)return s;if("toAttribute"===e)switch(t){case"object":case"array":return s&&JSON.stringify(s);case"boolean":return s?"":null;default:return""===s?null:s}else switch(t){case"object":case"array":if(!s)return s;try{return JSON.parse(s)}catch{return i("Invalid JSON: "+s),null}case"number":return null!==s?+s:s;default:return s??""}}function d(t,s,e){t?null===e?t.removeAttribute(s):t.setAttribute(s,e):i("Cannot sync attrs.")}const p=new WeakMap,g="e"+(1e5*Math.random()|0),b=()=>document.createElement("template"),y=t=>document.createTreeWalker(t,128);function m(t,i,e){return!function(t,i,e){if(t.i!==i||!t.o)return!1;for(let i=0;i<e.length;i++){const n=e[i],o=s(n)?f(n):n;if(o!==t.h[i]){if(u(n)||!t.o[i])return!1;t.h[i]=o,t.o[i].textContent=f(n)}}return!0}(t,i,e)&&(function(t,i,e){let o=p.get(i);if(!o){const t=i.map(l);o={u:t,l:e.length>0?S(t,e.length):null},p.set(i,o)}if(o.l)t.o=function(t,s,i){const e=s.content.cloneNode(!0),o=y(e),r=Array(i.length),h=[];let c;for(;c=o.nextNode();)c.data===g&&h.push(c);for(let t=0;t<h.length;t++){const s=i[t];if(u(s)){const i=b();i.innerHTML=n(s),h[t].parentNode.replaceChild(i.content,h[t])}else{const i=document.createTextNode(f(s));h[t].parentNode.replaceChild(i,h[t]),r[t]=i}}return t.replaceChildren(e),r}(t,o.l,e);else{const s=e.map(n),i=o.u.reduce((t,i,e)=>t+i+(s[e]??""),"").replace(/>\s+</g,"><").trim(),r=b();r.innerHTML=i,_(t,r.content.childNodes),t.o=null}t.i=i,t.h=e.map(t=>s(t)?f(t):t)}(t,i,e),!0)}function S(t,s){const i=`\x3c!--${g}--\x3e`,e=t.reduce((t,e,n)=>t+e+(n<s?i:""),"").trim(),n=b();n.innerHTML=e;const o=y(n.content);let r=0;for(;o.nextNode();)o.currentNode.data===g&&r++;return r===s?n:null}function _(t,s){const i=Array.from(t.childNodes),e=Array.from(s),n=Math.max(i.length,e.length);for(let s=0;s<n;s++){const n=i[s],o=e[s];n?o?n.nodeType!==o.nodeType||1===n.nodeType&&n.tagName!==o.tagName?t.replaceChild(o,n):3===n.nodeType?n.textContent!==o.textContent&&(n.textContent=o.textContent):1===n.nodeType&&(w(n,o),_(n,o.childNodes)):t.removeChild(n):t.appendChild(o)}}function w(t,s){for(let i=t.attributes.length-1;i>=0;i--){const{name:e}=t.attributes[i];s.hasAttribute(e)||t.removeAttribute(e)}for(let i=0;i<s.attributes.length;i++){const{name:e,value:n}=s.attributes[i];t.getAttribute(e)!==n&&t.setAttribute(e,n)}}const v=new WeakSet,x=(t,s)=>Object.prototype.hasOwnProperty.call(t,s);function C(s){return class extends s{element=null;attributeChangedCallback(t,s,e){super.attributeChangedCallback?.(t,s,e),"text"!==t?(this.p=!0,function(t,s,e,n){if(e!==n){const e=typeof t[s];"undefined"===e&&i(`Prop "${s}" has no default.`);const o=a(e,n,"toProp");t[s]=o}}(this,t,s,e),this.p=!1,this.m&&s!==e&&!this.S&&this._()):this.text=e??""}static get observedAttributes(){if(this.v)return this.v;const t=(this.props||[]).map(t=>"string"==typeof t?t:t.name);return this.v=[...t,"text"],this.v}connectedCallback(){super.connectedCallback?.(),this.C(),this.A(),this.m||void 0!==this.k||(this.text=this.textContent.trim()),this.P(),this.willUpdate(),this.M(),this.N(),this.O(),this.m||(this.m=!0,this.setAttribute("hydrated",""),this.firstUpdated()),this.updated()}C(){const t=this.constructor;if(v.has(t))return;const s=new Set,e=[];if(t.props){for(const i of t.props)"string"==typeof i?e.push(i):(e.push(i.name),!1===i.reflect&&s.add(i.name));e.includes("text")&&i('"text" is reserved.'),function(t,s,i){for(const e of s){const s=!i||!i.has(e);Object.defineProperty(t,e,{configurable:!0,enumerable:!0,get(){return this.j?.get(e)},set(t){if(this.j||(this.j=new Map),t!==this.j.get(e)&&(this.j.set(e,t),this.isConnected))if(s){if(!this.p){const s=a(typeof t,t,"toAttribute");d(this,e,s)}}else this.m&&!this.S&&this._()}})}}(t.prototype,e,s)}if(t.U=e,t.$=s,t.q=t.events||null,t.q)for(const s of t.q)x(t.prototype,s)||(t.prototype[s]=function(...t){return this.element[s](...t)});var n;t.J=(n=t.element)?t=>t.querySelector(n):t=>t.firstElementChild,v.add(t)}A(){this.p=!0;for(const t of this.constructor.U)if(x(this,t)){const s=this[t];delete this[t],this[t]=s}this.p=!1}get R(){return this.W??this.shadowRoot??this}P(){const t=this.constructor;if(!t.shadow)return;this.W||this.shadowRoot||(this.W=this.attachShadow({mode:t.shadow}));const s=this.W??this.shadowRoot;if(t.styles){if(!t.D){const s=[t.styles].flat();t.D=s.map(t=>{if("string"==typeof t){const s=new CSSStyleSheet;return s.replaceSync(t),s}return t})}s.adoptedStyleSheets=t.D}}M(){const t=this.constructor,s=this.R,e=this.render();if(e&&e.strings&&m(s,e.strings,e.values)){const i=this.element;if(this.element=t.J(s),this.F&&i&&this.element!==i){const s=t.q;for(const t of s)i.removeEventListener(t,this),this.element.addEventListener(t,this)}}this.element||(this.element=t.J(s),this.element||(t.element&&i("Element not found."),this.element=s.firstElementChild))}N(){if(this.j){const t=this.constructor.$;for(const[s,i]of this.j){if(t.has(s))continue;const e=a(typeof i,i,"toAttribute");(null!==e||this.hasAttribute(s))&&d(this,s,e)}}}O(){const t=this.constructor.q;if(!this.F&&t?.length)if(this.element){this.F=!0;for(const s of t)this.element.addEventListener(s,this)}else i("Cannot add events.")}render(){}willUpdate(){}firstUpdated(){}updated(){}adoptedCallback(){super.adoptedCallback?.()}disconnectedCallback(){if(super.disconnectedCallback?.(),this.F){this.F=!1;for(const t of this.constructor.q)this.element?.removeEventListener(t,this)}}handleEvent(t){this.constructor.q?.includes(t.type)&&(t.bubbles&&(t.composed||this.R===this)||this.dispatchEvent(new Event(t.type,{bubbles:t.bubbles})))}get text(){return this.k??""}set text(t){const s=this.k;this.k=t,this.m&&s!==t&&!this.S&&this._()}static define(){const t=this.tagName;t?function(t,s){const i=globalThis.customElements;i?.get(t)||i?.define(t,s)}(t,this):i("define() without a tagName.")}_(){this.S||this.I||(this.I=!0,this.L=new Promise(t=>{this.T=t}),queueMicrotask(()=>{try{this.B()}catch(s){console.error(t,s)}}))}B(){this.I=!1;const t=this.T;this.T=null;try{try{this.willUpdate(),this.S=!0,this.M()}finally{this.S=!1}this.updated()}finally{this.L=null,t()}}get updateComplete(){return this.L||Promise.resolve()}requestUpdate(){this.m&&!this.S&&this._()}}}export{C as Elena,r as html,c as nothing,h as unsafeHTML};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/common/props.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../../src/common/props.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,mCAJW,MAAM,SACN,GAAG,cACH,aAAa,GAAG,QAAQ,OAqClC;AAED;;;;;;GAMG;AACH,uCAJW,OAAO,QACP,MAAM,SACN,MAAM,GAAG,IAAI,QAYvB;AAED;;;;;;;;GAQG;AACH,qDAHW,MAAM,EAAE,cACR,GAAG,CAAC,MAAM,CAAC,QAsCrB;AAED;;;;;;;;GAQG;AACH,kCALW,MAAM,QACN,MAAM,YACN,GAAG,YACH,GAAG,QAWb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/common/render.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/common/render.js"],"names":[],"mappings":"AAWA;;;;;;;;;GASG;AACH,wCALW,WAAW,WACX,oBAAoB,kBAElB,OAAO,CAQnB"}
|
package/dist/common/utils.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export function unsafeHTML(str: string): {
|
|
|
45
45
|
* @returns {string}
|
|
46
46
|
*/
|
|
47
47
|
export function collapseWhitespace(string: string): string;
|
|
48
|
+
export function warn(msg: string): void;
|
|
48
49
|
/**
|
|
49
50
|
* A placeholder you can return from a conditional expression
|
|
50
51
|
* inside a template to render nothing.
|
|
@@ -57,4 +58,6 @@ export const nothing: {
|
|
|
57
58
|
};
|
|
58
59
|
export function isRaw(value: any): boolean;
|
|
59
60
|
export function toPlainText(value: any): string;
|
|
61
|
+
export const prefix: "\u2591\u2588 [ELENA]: ";
|
|
62
|
+
export const isArray: (arg: any) => arg is any[];
|
|
60
63
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/common/utils.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,uCAHW,MAAM,2BAMhB;AASD,6CAEC;AAED;;;;;;GAMG;AACH,oCAHW,GAAC,GACC,MAAM,CAOlB;AAaD;;;;;;;GAOG;AACH,8BAJW,oBAAoB,aACjB,GAAC,EAAA,GACF;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAC;IAAC,MAAM,QAAQ;IAAC,QAAQ,IAAI,MAAM,CAAA;CAAE,CAiB7F;AAED;;;;;GAKG;AACH,gCAHW,MAAM,GACJ;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,IAAI,MAAM,CAAA;CAAE,CAI/C;AA0BD;;;;;GAKG;AACH,2CAHW,MAAM,GACJ,MAAM,CAOlB;AAxHM,0BAHI,MAAM,QAGoC;AAqFrD;;;;;GAKG;AACH,sBAFU;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,IAAI,MAAM,CAAA;CAAE,CAEc;AAQpD,6BAHI,GAAC,GACC,OAAO,CAE2E;AAQxF,mCAHI,GAAC,GACC,MAAM,CAEwE;AAlH3F,qBAAe,wBAAc,CAAC;AAC9B,iDAA8B"}
|
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":"AAkEA;;;;;;;;;GASG;AACH,kCAHW,gBAAgB,GACd,uBAAuB,CA6dnC;+BAjgBY,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,WAAW;mCAInC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAAC,MAAM,IAAI,IAAI,CAAC;IAAC,UAAU,IAAI,IAAI,CAAC;IAAC,YAAY,IAAI,IAAI,CAAC;IAAC,OAAO,IAAI,IAAI,CAAC;IAAC,iBAAiB,IAAI,IAAI,CAAC;IAAC,oBAAoB,IAAI,IAAI,CAAA;CAAE;8BAIjL;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;sCAInC,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,WAAW,GAAG,oBAAoB,CAAC,GAAG;IACvE,MAAM,IAAI,IAAI,CAAC;IACnB,QAAY,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,CAAC,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC;CAC9D;qBA5CmE,mBAAmB;2BAAnB,mBAAmB;wBAAnB,mBAAmB"}
|
package/dist/elena.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @elenajs/core v1.0.0-rc.8
|
|
3
|
+
* (c) 2025-present Ariel Salminen and Elena contributors
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
import{getProps as t,setProps as e,getPropValue as s,syncAttribute as i}from"./props.js";import{warn as n,defineElement as r,prefix as o}from"./utils.js";export{html,nothing,unsafeHTML}from"./utils.js";import{renderTemplate as h}from"./render.js";const a=new WeakSet,d=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);function l(l){return class extends l{element=null;attributeChangedCallback(e,s,i){super.attributeChangedCallback?.(e,s,i),"text"!==e?(this._syncing=!0,t(this,e,s,i),this._syncing=!1,this._hydrated&&s!==i&&!this._isRendering&&this._safeRender()):this.text=i??""}static get observedAttributes(){if(this._observedAttrs)return this._observedAttrs;const t=(this.props||[]).map(t=>"string"==typeof t?t:t.name);return this._observedAttrs=[...t,"text"],this._observedAttrs}connectedCallback(){super.connectedCallback?.(),this._setupStaticProps(),this._captureClassFieldDefaults(),this._hydrated||void 0!==this._text||(this.text=this.textContent.trim()),this._attachShadow(),this.willUpdate(),this._applyRender(),this._syncProps(),this._delegateEvents(),this._hydrated||(this._hydrated=!0,this.setAttribute("hydrated",""),this.firstUpdated()),this.updated()}_setupStaticProps(){const t=this.constructor;if(a.has(t))return;const s=new Set,i=[];if(t.props){for(const e of t.props)"string"==typeof e?i.push(e):(i.push(e.name),!1===e.reflect&&s.add(e.name));i.includes("text")&&n('"text" is reserved.'),e(t.prototype,i,s)}if(t._propNames=i,t._noReflect=s,t._elenaEvents=t.events||null,t._elenaEvents)for(const e of t._elenaEvents)d(t.prototype,e)||(t.prototype[e]=function(...t){return this.element[e](...t)});var r;t._resolver=(r=t.element)?t=>t.querySelector(r):t=>t.firstElementChild,a.add(t)}_captureClassFieldDefaults(){this._syncing=!0;for(const t of this.constructor._propNames)if(d(this,t)){const e=this[t];delete this[t],this[t]=e}this._syncing=!1}get _renderRoot(){return this._shadow??this.shadowRoot??this}_attachShadow(){const t=this.constructor;if(!t.shadow)return;this._shadow||this.shadowRoot||(this._shadow=this.attachShadow({mode:t.shadow}));const e=this._shadow??this.shadowRoot;if(t.styles){if(!t._adoptedSheets){const e=[t.styles].flat();t._adoptedSheets=e.map(t=>{if("string"==typeof t){const e=new CSSStyleSheet;return e.replaceSync(t),e}return t})}e.adoptedStyleSheets=t._adoptedSheets}}_applyRender(){const t=this.constructor,e=this._renderRoot,s=this.render();if(s&&s.strings&&h(e,s.strings,s.values)){const s=this.element;if(this.element=t._resolver(e),this._events&&s&&this.element!==s){const e=t._elenaEvents;for(const t of e)s.removeEventListener(t,this),this.element.addEventListener(t,this)}}this.element||(this.element=t._resolver(e),this.element||(t.element&&n("Element not found."),this.element=e.firstElementChild))}_syncProps(){if(this._props){const t=this.constructor._noReflect;for(const[e,n]of this._props){if(t.has(e))continue;const r=s(typeof n,n,"toAttribute");(null!==r||this.hasAttribute(e))&&i(this,e,r)}}}_delegateEvents(){const t=this.constructor._elenaEvents;if(!this._events&&t?.length)if(this.element){this._events=!0;for(const e of t)this.element.addEventListener(e,this)}else n("Cannot add events.")}render(){}willUpdate(){}firstUpdated(){}updated(){}adoptedCallback(){super.adoptedCallback?.()}disconnectedCallback(){if(super.disconnectedCallback?.(),this._events){this._events=!1;for(const t of this.constructor._elenaEvents)this.element?.removeEventListener(t,this)}}handleEvent(t){this.constructor._elenaEvents?.includes(t.type)&&(t.bubbles&&(t.composed||this._renderRoot===this)||this.dispatchEvent(new Event(t.type,{bubbles:t.bubbles})))}get text(){return this._text??""}set text(t){const e=this._text;this._text=t,this._hydrated&&e!==t&&!this._isRendering&&this._safeRender()}static define(){const t=this.tagName;t?r(t,this):n("define() without a tagName.")}_safeRender(){this._isRendering||this._renderPending||(this._renderPending=!0,this._updateComplete=new Promise(t=>{this._resolveUpdate=t}),queueMicrotask(()=>{try{this._performUpdate()}catch(t){console.error(o,t)}}))}_performUpdate(){this._renderPending=!1;const t=this._resolveUpdate;this._resolveUpdate=null;try{try{this.willUpdate(),this._isRendering=!0,this._applyRender()}finally{this._isRendering=!1}this.updated()}finally{this._updateComplete=null,t()}}get updateComplete(){return this._updateComplete||Promise.resolve()}requestUpdate(){this._hydrated&&!this._isRendering&&this._safeRender()}}}export{l as Elena};
|
package/dist/props.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
function
|
|
2
|
-
//# sourceMappingURL=props.js.map
|
|
1
|
+
import{warn as t}from"./utils.js";function e(e,n,r){if(n="boolean"===e&&"boolean"!=typeof n?null!==n:n,!r)return n;if("toAttribute"===r)switch(e){case"object":case"array":return n&&JSON.stringify(n);case"boolean":return n?"":null;default:return""===n?null:n}else switch(e){case"object":case"array":if(!n)return n;try{return JSON.parse(n)}catch{return t("Invalid JSON: "+n),null}case"number":return null!==n?+n:n;default:return n??""}}function n(e,n,r){e?null===r?e.removeAttribute(n):e.setAttribute(n,r):t("Cannot sync attrs.")}function r(t,r,s){for(const o of r){const r=!s||!s.has(o);Object.defineProperty(t,o,{configurable:!0,enumerable:!0,get(){return this._props?.get(o)},set(t){if(this._props||(this._props=new Map),t!==this._props.get(o)&&(this._props.set(o,t),this.isConnected))if(r){if(!this._syncing){const r=e(typeof t,t,"toAttribute");n(this,o,r)}}else this._hydrated&&!this._isRendering&&this._safeRender()}})}}function s(n,r,s,o){if(s!==o){const s=typeof n[r];"undefined"===s&&t(`Prop "${r}" has no default.`);const i=e(s,o,"toProp");n[r]=i}}export{e as getPropValue,s as getProps,r as setProps,n as syncAttribute};
|
package/dist/render.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import{toPlainText as e,isRaw as
|
|
2
|
-
//# sourceMappingURL=render.js.map
|
|
1
|
+
import{isArray as t,toPlainText as e,isRaw as n,collapseWhitespace as r,resolveValue as o}from"./utils.js";const a=new WeakMap,l="e"+(1e5*Math.random()|0),c=()=>document.createElement("template"),s=t=>document.createTreeWalker(t,128);function i(i,p,d){return!function(r,o,a){if(r._templateStrings!==o||!r._templateParts)return!1;for(let o=0;o<a.length;o++){const l=a[o],c=t(l)?e(l):l;if(c!==r._templateValues[o]){if(n(l)||!r._templateParts[o])return!1;r._templateValues[o]=c,r._templateParts[o].textContent=e(l)}}return!0}(i,p,d)&&(function(i,p,d){let f=a.get(p);if(!f){const t=p.map(r);f={_strings:t,_template:d.length>0?u(t,d.length):null},a.set(p,f)}if(f._template)i._templateParts=function(t,r,a){const i=r.content.cloneNode(!0),u=s(i),m=Array(a.length),p=[];let d;for(;d=u.nextNode();)d.data===l&&p.push(d);for(let t=0;t<p.length;t++){const r=a[t];if(n(r)){const e=c();e.innerHTML=o(r),p[t].parentNode.replaceChild(e.content,p[t])}else{const n=document.createTextNode(e(r));p[t].parentNode.replaceChild(n,p[t]),m[t]=n}}return t.replaceChildren(i),m}(i,f._template,d);else{const t=d.map(o),e=f._strings.reduce((e,n,r)=>e+n+(t[r]??""),"").replace(/>\s+</g,"><").trim(),n=c();n.innerHTML=e,m(i,n.content.childNodes),i._templateParts=null}i._templateStrings=p,i._templateValues=d.map(n=>t(n)?e(n):n)}(i,p,d),!0)}function u(t,e){const n=`\x3c!--${l}--\x3e`,r=t.reduce((t,r,o)=>t+r+(o<e?n:""),"").trim(),o=c();o.innerHTML=r;const a=s(o.content);let i=0;for(;a.nextNode();)a.currentNode.data===l&&i++;return i===e?o:null}function m(t,e){const n=Array.from(t.childNodes),r=Array.from(e),o=Math.max(n.length,r.length);for(let e=0;e<o;e++){const o=n[e],a=r[e];o?a?o.nodeType!==a.nodeType||1===o.nodeType&&o.tagName!==a.tagName?t.replaceChild(a,o):3===o.nodeType?o.textContent!==a.textContent&&(o.textContent=a.textContent):1===o.nodeType&&(p(o,a),m(o,a.childNodes)):t.removeChild(o):t.appendChild(a)}}function p(t,e){for(let n=t.attributes.length-1;n>=0;n--){const{name:r}=t.attributes[n];e.hasAttribute(r)||t.removeAttribute(r)}for(let n=0;n<e.attributes.length;n++){const{name:r,value:o}=e.attributes[n];t.getAttribute(r)!==o&&t.setAttribute(r,o)}}export{i as renderTemplate};
|
package/dist/utils.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//# sourceMappingURL=utils.js.map
|
|
1
|
+
const n="░█ [ELENA]: ",r=Array.isArray,t=r=>console.warn(n+r);function e(n,r){const t=globalThis.customElements;t?.get(n)||t?.define(n,r)}const o={"&":"&","<":"<",">":">",'"':""","'":"'"};function i(n){return String(n).replace(/[&<>"']/g,n=>o[n])}function c(n){return r(n)?n.map(u).join(""):u(n)}function u(n){return n?.__raw?String(n):i(n??"")}function a(n,...r){let t;return{__raw:!0,strings:n,values:r,toString:()=>(null==t&&(t=n.reduce((n,t,e)=>n+t+c(r[e]),"")),t)}}function s(n){return{__raw:!0,toString:()=>n??""}}const g={__raw:!0,toString:()=>""},l=n=>r(n)?n.some(n=>n?.__raw):n?.__raw,_=n=>r(n)?n.join(""):String(n??"");function f(n){return n.replace(/(>)\n\s*|\n\s*(<)/g,"$1$2").replace(/\n\s*/g," ").replace(/>\s+</g,"><")}export{f as collapseWhitespace,e as defineElement,i as escapeHtml,a as html,r as isArray,l as isRaw,g as nothing,n as prefix,c as resolveValue,_ as toPlainText,s as unsafeHTML,t as warn};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elenajs/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.8",
|
|
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/",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"typescript": "5.9.3",
|
|
57
57
|
"vitest": "4.1.0"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "1ed3381ee856296e0fa92ff863447843e8a51957"
|
|
60
60
|
}
|
package/src/common/props.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { warn } from "./utils.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Get the value of the Elena Element property.
|
|
3
5
|
*
|
|
@@ -14,11 +16,10 @@ export function getPropValue(type, value, transform) {
|
|
|
14
16
|
switch (type) {
|
|
15
17
|
case "object":
|
|
16
18
|
case "array":
|
|
17
|
-
return value
|
|
19
|
+
return value && JSON.stringify(value);
|
|
18
20
|
case "boolean":
|
|
19
21
|
return value ? "" : null;
|
|
20
|
-
|
|
21
|
-
return value === null ? null : value;
|
|
22
|
+
// number, string:
|
|
22
23
|
default:
|
|
23
24
|
return value === "" ? null : value;
|
|
24
25
|
}
|
|
@@ -32,11 +33,9 @@ export function getPropValue(type, value, transform) {
|
|
|
32
33
|
try {
|
|
33
34
|
return JSON.parse(value);
|
|
34
35
|
} catch {
|
|
35
|
-
|
|
36
|
+
warn("Invalid JSON: " + value);
|
|
36
37
|
return null;
|
|
37
38
|
}
|
|
38
|
-
case "boolean":
|
|
39
|
-
return value; // conversion already handled above
|
|
40
39
|
case "number":
|
|
41
40
|
return value !== null ? +value : value;
|
|
42
41
|
default:
|
|
@@ -54,7 +53,7 @@ export function getPropValue(type, value, transform) {
|
|
|
54
53
|
*/
|
|
55
54
|
export function syncAttribute(element, name, value) {
|
|
56
55
|
if (!element) {
|
|
57
|
-
|
|
56
|
+
warn("Cannot sync attrs.");
|
|
58
57
|
return;
|
|
59
58
|
}
|
|
60
59
|
if (value === null) {
|
|
@@ -80,7 +79,7 @@ export function setProps(proto, propNames, noReflect) {
|
|
|
80
79
|
configurable: true,
|
|
81
80
|
enumerable: true,
|
|
82
81
|
get() {
|
|
83
|
-
return this._props
|
|
82
|
+
return this._props?.get(prop);
|
|
84
83
|
},
|
|
85
84
|
set(value) {
|
|
86
85
|
if (!this._props) {
|
|
@@ -124,7 +123,7 @@ export function getProps(context, name, oldValue, newValue) {
|
|
|
124
123
|
if (oldValue !== newValue) {
|
|
125
124
|
const type = typeof context[name];
|
|
126
125
|
if (type === "undefined") {
|
|
127
|
-
|
|
126
|
+
warn(`Prop "${name}" has no default.`);
|
|
128
127
|
}
|
|
129
128
|
const newAttr = getPropValue(type, newValue, "toProp");
|
|
130
129
|
context[name] = newAttr;
|
package/src/common/render.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { collapseWhitespace, isRaw, resolveValue, toPlainText } from "./utils.js";
|
|
1
|
+
import { collapseWhitespace, isArray, isRaw, resolveValue, toPlainText } from "./utils.js";
|
|
2
2
|
|
|
3
3
|
const stringsCache = new WeakMap();
|
|
4
|
-
const markerKey = "e" + Math.random()
|
|
4
|
+
const markerKey = "e" + ((Math.random() * 1e5) | 0);
|
|
5
|
+
const SHOW_COMMENT = 128;
|
|
6
|
+
const ELEMENT_NODE = 1;
|
|
7
|
+
const TEXT_NODE = 3;
|
|
8
|
+
|
|
9
|
+
const newTemplate = () => document.createElement("template");
|
|
10
|
+
const treeWalker = node => document.createTreeWalker(node, SHOW_COMMENT);
|
|
5
11
|
|
|
6
12
|
/**
|
|
7
13
|
* Render a tagged template into an Elena Element with DOM diffing.
|
|
@@ -31,24 +37,24 @@ export function renderTemplate(element, strings, values) {
|
|
|
31
37
|
*/
|
|
32
38
|
function patchTextNodes(element, strings, values) {
|
|
33
39
|
// Only works when re-rendering the same template shape
|
|
34
|
-
if (element.
|
|
40
|
+
if (element._templateStrings !== strings || !element._templateParts) {
|
|
35
41
|
return false;
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
for (let i = 0; i < values.length; i++) {
|
|
39
45
|
const v = values[i];
|
|
40
|
-
const comparable =
|
|
46
|
+
const comparable = isArray(v) ? toPlainText(v) : v;
|
|
41
47
|
|
|
42
|
-
if (comparable === element.
|
|
48
|
+
if (comparable === element._templateValues[i]) {
|
|
43
49
|
continue;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
if (isRaw(v) || !element.
|
|
52
|
+
if (isRaw(v) || !element._templateParts[i]) {
|
|
47
53
|
return false;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
element.
|
|
51
|
-
element.
|
|
56
|
+
element._templateValues[i] = comparable;
|
|
57
|
+
element._templateParts[i].textContent = toPlainText(v);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
return true;
|
|
@@ -65,54 +71,54 @@ function fullRender(element, strings, values) {
|
|
|
65
71
|
let entry = stringsCache.get(strings);
|
|
66
72
|
|
|
67
73
|
if (!entry) {
|
|
68
|
-
const
|
|
74
|
+
const _strings = strings.map(collapseWhitespace);
|
|
69
75
|
entry = {
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
_strings,
|
|
77
|
+
_template: values.length > 0 ? createTemplate(_strings, values.length) : null,
|
|
72
78
|
};
|
|
73
79
|
stringsCache.set(strings, entry);
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
if (entry.
|
|
77
|
-
element.
|
|
82
|
+
if (entry._template) {
|
|
83
|
+
element._templateParts = cloneAndPatch(element, entry._template, values);
|
|
78
84
|
} else {
|
|
79
85
|
// Fallback for attribute-position values or static templates.
|
|
80
86
|
// White space collapsing here protects against Vue SSR mismatches.
|
|
81
|
-
const renderedValues = values.map(
|
|
82
|
-
const markup = entry.
|
|
87
|
+
const renderedValues = values.map(resolveValue);
|
|
88
|
+
const markup = entry._strings
|
|
83
89
|
.reduce((out, str, i) => out + str + (renderedValues[i] ?? ""), "")
|
|
84
90
|
.replace(/>\s+</g, "><")
|
|
85
91
|
.trim();
|
|
86
92
|
|
|
87
93
|
// Morph existing DOM to match new markup instead of replacing it.
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
morphContent(element,
|
|
91
|
-
element.
|
|
94
|
+
const template = newTemplate();
|
|
95
|
+
template.innerHTML = markup;
|
|
96
|
+
morphContent(element, template.content.childNodes);
|
|
97
|
+
element._templateParts = null;
|
|
92
98
|
}
|
|
93
99
|
|
|
94
|
-
element.
|
|
95
|
-
element.
|
|
100
|
+
element._templateStrings = strings;
|
|
101
|
+
element._templateValues = values.map(v => (isArray(v) ? toPlainText(v) : v));
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
/**
|
|
99
105
|
* Build a <template> element with comment markers.
|
|
100
106
|
*
|
|
101
|
-
* @param {string[]}
|
|
107
|
+
* @param {string[]} _strings - Whitespace-collapsed static parts
|
|
102
108
|
* @param {number} valueCount - Number of dynamic values
|
|
103
109
|
* @returns {HTMLTemplateElement | null}
|
|
104
110
|
*/
|
|
105
|
-
function createTemplate(
|
|
111
|
+
function createTemplate(_strings, valueCount) {
|
|
106
112
|
const marker = `<!--${markerKey}-->`;
|
|
107
|
-
const markup =
|
|
113
|
+
const markup = _strings
|
|
108
114
|
.reduce((out, str, i) => out + str + (i < valueCount ? marker : ""), "")
|
|
109
115
|
.trim();
|
|
110
116
|
|
|
111
|
-
const
|
|
112
|
-
|
|
117
|
+
const template = newTemplate();
|
|
118
|
+
template.innerHTML = markup;
|
|
113
119
|
|
|
114
120
|
// Mismatch means this template shape cannot use the clone path.
|
|
115
|
-
const walker =
|
|
121
|
+
const walker = treeWalker(template.content);
|
|
116
122
|
let count = 0;
|
|
117
123
|
|
|
118
124
|
while (walker.nextNode()) {
|
|
@@ -121,7 +127,7 @@ function createTemplate(processedStrings, valueCount) {
|
|
|
121
127
|
}
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
return count === valueCount ?
|
|
130
|
+
return count === valueCount ? template : null;
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
/**
|
|
@@ -135,8 +141,8 @@ function createTemplate(processedStrings, valueCount) {
|
|
|
135
141
|
*/
|
|
136
142
|
function cloneAndPatch(element, template, values) {
|
|
137
143
|
const clone = template.content.cloneNode(true);
|
|
138
|
-
const walker =
|
|
139
|
-
const parts =
|
|
144
|
+
const walker = treeWalker(clone);
|
|
145
|
+
const parts = Array(values.length);
|
|
140
146
|
const markers = [];
|
|
141
147
|
let node;
|
|
142
148
|
|
|
@@ -152,7 +158,7 @@ function cloneAndPatch(element, template, values) {
|
|
|
152
158
|
|
|
153
159
|
if (isRaw(value)) {
|
|
154
160
|
// Raw HTML: parse and insert as fragment
|
|
155
|
-
const tmp =
|
|
161
|
+
const tmp = newTemplate();
|
|
156
162
|
tmp.innerHTML = resolveValue(value);
|
|
157
163
|
markers[i].parentNode.replaceChild(tmp.content, markers[i]);
|
|
158
164
|
|
|
@@ -191,14 +197,14 @@ function morphContent(parent, nextNodes) {
|
|
|
191
197
|
parent.removeChild(cur);
|
|
192
198
|
} else if (
|
|
193
199
|
cur.nodeType !== nxt.nodeType ||
|
|
194
|
-
(cur.nodeType ===
|
|
200
|
+
(cur.nodeType === ELEMENT_NODE && cur.tagName !== nxt.tagName)
|
|
195
201
|
) {
|
|
196
202
|
parent.replaceChild(nxt, cur);
|
|
197
|
-
} else if (cur.nodeType ===
|
|
203
|
+
} else if (cur.nodeType === TEXT_NODE) {
|
|
198
204
|
if (cur.textContent !== nxt.textContent) {
|
|
199
205
|
cur.textContent = nxt.textContent;
|
|
200
206
|
}
|
|
201
|
-
} else if (cur.nodeType ===
|
|
207
|
+
} else if (cur.nodeType === ELEMENT_NODE) {
|
|
202
208
|
morphAttributes(cur, nxt);
|
|
203
209
|
morphContent(cur, nxt.childNodes);
|
|
204
210
|
}
|
package/src/common/utils.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
const prefix = "░█ [ELENA]: ";
|
|
2
|
+
const isArray = Array.isArray;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} msg
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export const warn = msg => console.warn(prefix + msg);
|
|
9
|
+
export { prefix, isArray };
|
|
10
|
+
|
|
1
11
|
/**
|
|
2
12
|
* Register the Elena Element if the browser supports it.
|
|
3
13
|
*
|
|
@@ -5,11 +15,8 @@
|
|
|
5
15
|
* @param {Function} Element
|
|
6
16
|
*/
|
|
7
17
|
export function defineElement(tagName, Element) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
window.customElements.define(tagName, Element);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
18
|
+
const customElements = globalThis.customElements;
|
|
19
|
+
customElements?.get(tagName) || customElements?.define(tagName, Element);
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
/**
|
|
@@ -31,8 +38,8 @@ export function escapeHtml(str) {
|
|
|
31
38
|
* @returns {string}
|
|
32
39
|
*/
|
|
33
40
|
export function resolveValue(value) {
|
|
34
|
-
if (
|
|
35
|
-
return value.map(
|
|
41
|
+
if (isArray(value)) {
|
|
42
|
+
return value.map(resolveItem).join("");
|
|
36
43
|
}
|
|
37
44
|
return resolveItem(value);
|
|
38
45
|
}
|
|
@@ -45,7 +52,7 @@ export function resolveValue(value) {
|
|
|
45
52
|
* @returns {string}
|
|
46
53
|
*/
|
|
47
54
|
function resolveItem(value) {
|
|
48
|
-
return value?.__raw ? String(value) : escapeHtml(
|
|
55
|
+
return value?.__raw ? String(value) : escapeHtml(value ?? "");
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
/**
|
|
@@ -63,7 +70,7 @@ export function html(strings, ...values) {
|
|
|
63
70
|
strings,
|
|
64
71
|
values,
|
|
65
72
|
toString: () => {
|
|
66
|
-
if (str
|
|
73
|
+
if (str == null) {
|
|
67
74
|
str = strings.reduce((acc, s, i) => {
|
|
68
75
|
return acc + s + resolveValue(values[i]);
|
|
69
76
|
}, "");
|
|
@@ -89,7 +96,7 @@ export function unsafeHTML(str) {
|
|
|
89
96
|
*
|
|
90
97
|
* @type {{ __raw: true, toString(): string }}
|
|
91
98
|
*/
|
|
92
|
-
export const nothing =
|
|
99
|
+
export const nothing = { __raw: true, toString: () => "" };
|
|
93
100
|
|
|
94
101
|
/**
|
|
95
102
|
* Check if a value contains trusted HTML fragments.
|
|
@@ -97,8 +104,7 @@ export const nothing = Object.freeze({ __raw: true, toString: () => "" });
|
|
|
97
104
|
* @param {*} value
|
|
98
105
|
* @returns {boolean}
|
|
99
106
|
*/
|
|
100
|
-
export const isRaw = value =>
|
|
101
|
-
Array.isArray(value) ? value.some(item => item?.__raw) : value?.__raw;
|
|
107
|
+
export const isRaw = value => (isArray(value) ? value.some(item => item?.__raw) : value?.__raw);
|
|
102
108
|
|
|
103
109
|
/**
|
|
104
110
|
* Convert a value to its plain text string.
|
|
@@ -106,8 +112,7 @@ export const isRaw = value =>
|
|
|
106
112
|
* @param {*} value
|
|
107
113
|
* @returns {string}
|
|
108
114
|
*/
|
|
109
|
-
export const toPlainText = value =>
|
|
110
|
-
Array.isArray(value) ? value.map(item => String(item ?? "")).join("") : String(value ?? "");
|
|
115
|
+
export const toPlainText = value => (isArray(value) ? value.join("") : String(value ?? ""));
|
|
111
116
|
|
|
112
117
|
/**
|
|
113
118
|
* Collapse whitespace from a static string part.
|
|
@@ -117,8 +122,7 @@ export const toPlainText = value =>
|
|
|
117
122
|
*/
|
|
118
123
|
export function collapseWhitespace(string) {
|
|
119
124
|
return string
|
|
120
|
-
.replace(
|
|
121
|
-
.replace(/\n\s*</g, "<") // newline before tag open
|
|
125
|
+
.replace(/(>)\n\s*|\n\s*(<)/g, "$1$2") // newlines adjacent to tags
|
|
122
126
|
.replace(/\n\s*/g, " ") // newline in text content, preserve word boundary
|
|
123
127
|
.replace(/>\s+</g, "><"); // whitespace between tags
|
|
124
128
|
}
|
package/src/elena.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { setProps, getProps, getPropValue, syncAttribute } from "./common/props.js";
|
|
16
|
-
import { defineElement, html, unsafeHTML, nothing } from "./common/utils.js";
|
|
16
|
+
import { defineElement, html, unsafeHTML, nothing, warn, prefix } from "./common/utils.js";
|
|
17
17
|
import { renderTemplate } from "./common/render.js";
|
|
18
18
|
|
|
19
19
|
export { html, unsafeHTML, nothing };
|
|
@@ -62,6 +62,7 @@ function elementResolver(selector) {
|
|
|
62
62
|
|
|
63
63
|
// Tracks which component classes have already been set up.
|
|
64
64
|
const setupRegistry = new WeakSet();
|
|
65
|
+
const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* Creates an Elena component class by extending `superClass`.
|
|
@@ -136,7 +137,9 @@ export function Elena(superClass) {
|
|
|
136
137
|
super.connectedCallback?.();
|
|
137
138
|
this._setupStaticProps();
|
|
138
139
|
this._captureClassFieldDefaults();
|
|
139
|
-
this.
|
|
140
|
+
if (!this._hydrated && this._text === undefined) {
|
|
141
|
+
this.text = this.textContent.trim();
|
|
142
|
+
}
|
|
140
143
|
this._attachShadow();
|
|
141
144
|
this.willUpdate();
|
|
142
145
|
this._applyRender();
|
|
@@ -181,7 +184,7 @@ export function Elena(superClass) {
|
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
if (names.includes("text")) {
|
|
184
|
-
|
|
187
|
+
warn('"text" is reserved.');
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
setProps(component.prototype, names, noRef);
|
|
@@ -193,7 +196,7 @@ export function Elena(superClass) {
|
|
|
193
196
|
|
|
194
197
|
if (component._elenaEvents) {
|
|
195
198
|
for (const e of component._elenaEvents) {
|
|
196
|
-
if (!
|
|
199
|
+
if (!hasOwn(component.prototype, e)) {
|
|
197
200
|
component.prototype[e] = function (...args) {
|
|
198
201
|
return this.element[e](...args);
|
|
199
202
|
};
|
|
@@ -215,7 +218,7 @@ export function Elena(superClass) {
|
|
|
215
218
|
this._syncing = true;
|
|
216
219
|
|
|
217
220
|
for (const name of this.constructor._propNames) {
|
|
218
|
-
if (
|
|
221
|
+
if (hasOwn(this, name)) {
|
|
219
222
|
const value = this[name];
|
|
220
223
|
delete this[name];
|
|
221
224
|
this[name] = value;
|
|
@@ -225,17 +228,6 @@ export function Elena(superClass) {
|
|
|
225
228
|
this._syncing = false;
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
/**
|
|
229
|
-
* Saves any text inside the element before the first render.
|
|
230
|
-
*
|
|
231
|
-
* @internal
|
|
232
|
-
*/
|
|
233
|
-
_captureText() {
|
|
234
|
-
if (!this._hydrated && this._text === undefined) {
|
|
235
|
-
this.text = this.textContent.trim();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
231
|
/**
|
|
240
232
|
* The root node to render into. Returns the shadow root when shadow mode
|
|
241
233
|
* is enabled, otherwise the host element itself.
|
|
@@ -275,7 +267,7 @@ export function Elena(superClass) {
|
|
|
275
267
|
// Normalize to array and cache converted CSSStyleSheet instances on the class.
|
|
276
268
|
// Avoids re-parsing CSS strings on every element instance.
|
|
277
269
|
if (!component._adoptedSheets) {
|
|
278
|
-
const stylesList =
|
|
270
|
+
const stylesList = [component.styles].flat();
|
|
279
271
|
|
|
280
272
|
component._adoptedSheets = stylesList.map(s => {
|
|
281
273
|
if (typeof s === "string") {
|
|
@@ -297,10 +289,11 @@ export function Elena(superClass) {
|
|
|
297
289
|
* @internal
|
|
298
290
|
*/
|
|
299
291
|
_applyRender() {
|
|
292
|
+
const constructor = this.constructor;
|
|
293
|
+
const root = this._renderRoot;
|
|
300
294
|
const result = this.render();
|
|
301
295
|
|
|
302
296
|
if (result && result.strings) {
|
|
303
|
-
const root = this._renderRoot;
|
|
304
297
|
const rebuilt = renderTemplate(root, result.strings, result.values);
|
|
305
298
|
|
|
306
299
|
// Re-resolve element ref when the DOM was fully rebuilt.
|
|
@@ -308,11 +301,11 @@ export function Elena(superClass) {
|
|
|
308
301
|
// so the existing ref is still valid.
|
|
309
302
|
if (rebuilt) {
|
|
310
303
|
const oldElement = this.element;
|
|
311
|
-
this.element =
|
|
304
|
+
this.element = constructor._resolver(root);
|
|
312
305
|
|
|
313
306
|
// Re-bind event listeners when the inner element was replaced.
|
|
314
307
|
if (this._events && oldElement && this.element !== oldElement) {
|
|
315
|
-
const events =
|
|
308
|
+
const events = constructor._elenaEvents;
|
|
316
309
|
|
|
317
310
|
for (const e of events) {
|
|
318
311
|
oldElement.removeEventListener(e, this);
|
|
@@ -324,12 +317,11 @@ export function Elena(superClass) {
|
|
|
324
317
|
|
|
325
318
|
// Resolve inner element on first render
|
|
326
319
|
if (!this.element) {
|
|
327
|
-
|
|
328
|
-
this.element = this.constructor._resolver(root);
|
|
320
|
+
this.element = constructor._resolver(root);
|
|
329
321
|
|
|
330
322
|
if (!this.element) {
|
|
331
|
-
if (
|
|
332
|
-
|
|
323
|
+
if (constructor.element) {
|
|
324
|
+
warn("Element not found.");
|
|
333
325
|
}
|
|
334
326
|
this.element = root.firstElementChild;
|
|
335
327
|
}
|
|
@@ -373,7 +365,7 @@ export function Elena(superClass) {
|
|
|
373
365
|
|
|
374
366
|
if (!this._events && events?.length) {
|
|
375
367
|
if (!this.element) {
|
|
376
|
-
|
|
368
|
+
warn("Cannot add events.");
|
|
377
369
|
} else {
|
|
378
370
|
this._events = true;
|
|
379
371
|
|
|
@@ -477,10 +469,11 @@ export function Elena(superClass) {
|
|
|
477
469
|
* not on the Elena mixin itself.
|
|
478
470
|
*/
|
|
479
471
|
static define() {
|
|
480
|
-
|
|
481
|
-
|
|
472
|
+
const tag = this.tagName;
|
|
473
|
+
if (tag) {
|
|
474
|
+
defineElement(tag, this);
|
|
482
475
|
} else {
|
|
483
|
-
|
|
476
|
+
warn("define() without a tagName.");
|
|
484
477
|
}
|
|
485
478
|
}
|
|
486
479
|
|
|
@@ -503,7 +496,7 @@ export function Elena(superClass) {
|
|
|
503
496
|
try {
|
|
504
497
|
this._performUpdate();
|
|
505
498
|
} catch (e) {
|
|
506
|
-
console.error(
|
|
499
|
+
console.error(prefix, e);
|
|
507
500
|
}
|
|
508
501
|
});
|
|
509
502
|
}
|
|
@@ -541,10 +534,7 @@ export function Elena(superClass) {
|
|
|
541
534
|
* @type {Promise<void>}
|
|
542
535
|
*/
|
|
543
536
|
get updateComplete() {
|
|
544
|
-
|
|
545
|
-
return this._updateComplete;
|
|
546
|
-
}
|
|
547
|
-
return Promise.resolve();
|
|
537
|
+
return this._updateComplete || Promise.resolve();
|
|
548
538
|
}
|
|
549
539
|
|
|
550
540
|
/**
|
package/dist/bundle.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sources":["../src/common/props.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 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 */\nconst Escape = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\nexport function escapeHtml(str) {\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 .replace(/>\\s+</g, \"><\"); // whitespace between tags\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 = null;\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) => out + str + (i < valueCount ? marker : \"\"), \"\")\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 * @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 * ██████████ ████\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\";\n\nexport { html, unsafeHTML, nothing };\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 = (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._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\n if (component._elenaEvents) {\n for (const e of component._elenaEvents) {\n if (!Object.prototype.hasOwnProperty.call(component.prototype, e)) {\n component.prototype[e] = function (...args) {\n return this.element[e](...args);\n };\n }\n }\n }\n\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 if (!this._shadow && !this.shadowRoot) {\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 * Also resolves the inner element reference.\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 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 (rebuilt) {\n const oldElement = this.element;\n this.element = this.constructor._resolver(root);\n\n // Re-bind event listeners when the inner element was replaced.\n if (this._events && oldElement && this.element !== oldElement) {\n const events = this.constructor._elenaEvents;\n\n for (const e of events) {\n oldElement.removeEventListener(e, this);\n this.element.addEventListener(e, this);\n }\n }\n }\n }\n\n // Resolve inner element on first render\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 }\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 * Forwards events that cannot reach the host naturally:\n * non-bubbling events (focus, blur) and non-composed\n * events in Shadow DOM (change, submit, reset).\n * Composed bubbling events (click, input) pass through on their own.\n *\n * @internal\n */\n handleEvent(event) {\n if (!this.constructor._elenaEvents?.includes(event.type)) {\n return;\n }\n\n if (!event.bubbles || (!event.composed && this._renderRoot !== this)) {\n /** @internal */\n this.dispatchEvent(new Event(event.type, { bubbles: event.bubbles }));\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","Escape","resolveValue","Array","isArray","map","item","resolveItem","join","__raw","String","str","replace","c","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","setupRegistry","WeakSet","Elena","superClass","attributeChangedCallback","prop","oldValue","newValue","super","this","_syncing","context","newAttr","getProps","_hydrated","_isRendering","_safeRender","text","observedAttributes","_observedAttrs","propNames","props","p","connectedCallback","_setupStaticProps","_captureClassFieldDefaults","_captureText","_attachShadow","willUpdate","_applyRender","_syncProps","_delegateEvents","firstUpdated","updated","component","constructor","has","noRef","Set","names","reflect","add","includes","proto","noReflect","reflects","defineProperty","configurable","enumerable","_props","Map","isConnected","attrValue","setProps","prototype","_propNames","_noReflect","_elenaEvents","events","e","hasOwnProperty","call","args","selector","_resolver","host","querySelector","firstElementChild","_text","_renderRoot","_shadow","shadowRoot","shadow","attachShadow","mode","styles","_adoptedSheets","stylesList","sheet","CSSStyleSheet","replaceSync","adoptedStyleSheets","result","render","root","oldElement","_events","removeEventListener","addEventListener","adoptedCallback","disconnectedCallback","handleEvent","event","bubbles","composed","dispatchEvent","Event","old","define","Element","window","customElements","_renderPending","_updateComplete","Promise","resolve","_resolveUpdate","queueMicrotask","_performUpdate","error","updateComplete","requestUpdate"],"mappings":"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"}
|
package/dist/elena.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"elena.js","sources":["../src/elena.js"],"sourcesContent":["/**\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\";\n\nexport { html, unsafeHTML, nothing };\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 = (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._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\n if (component._elenaEvents) {\n for (const e of component._elenaEvents) {\n if (!Object.prototype.hasOwnProperty.call(component.prototype, e)) {\n component.prototype[e] = function (...args) {\n return this.element[e](...args);\n };\n }\n }\n }\n\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 if (!this._shadow && !this.shadowRoot) {\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 * Also resolves the inner element reference.\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 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 (rebuilt) {\n const oldElement = this.element;\n this.element = this.constructor._resolver(root);\n\n // Re-bind event listeners when the inner element was replaced.\n if (this._events && oldElement && this.element !== oldElement) {\n const events = this.constructor._elenaEvents;\n\n for (const e of events) {\n oldElement.removeEventListener(e, this);\n this.element.addEventListener(e, this);\n }\n }\n }\n }\n\n // Resolve inner element on first render\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 }\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 * Forwards events that cannot reach the host naturally:\n * non-bubbling events (focus, blur) and non-composed\n * events in Shadow DOM (change, submit, reset).\n * Composed bubbling events (click, input) pass through on their own.\n *\n * @internal\n */\n handleEvent(event) {\n if (!this.constructor._elenaEvents?.includes(event.type)) {\n return;\n }\n\n if (!event.bubbles || (!event.composed && this._renderRoot !== this)) {\n /** @internal */\n this.dispatchEvent(new Event(event.type, { bubbles: event.bubbles }));\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":["setupRegistry","WeakSet","Elena","superClass","element","attributeChangedCallback","prop","oldValue","newValue","super","this","_syncing","getProps","_hydrated","_isRendering","_safeRender","text","observedAttributes","_observedAttrs","propNames","props","map","p","name","connectedCallback","_setupStaticProps","_captureClassFieldDefaults","_captureText","_attachShadow","willUpdate","_applyRender","_syncProps","_delegateEvents","setAttribute","firstUpdated","updated","component","constructor","has","noRef","Set","names","push","reflect","add","includes","console","warn","setProps","prototype","_propNames","_noReflect","_elenaEvents","events","e","Object","hasOwnProperty","call","args","selector","_resolver","host","querySelector","firstElementChild","value","undefined","_text","textContent","trim","_renderRoot","_shadow","shadowRoot","shadow","attachShadow","mode","styles","_adoptedSheets","stylesList","Array","isArray","s","sheet","CSSStyleSheet","replaceSync","adoptedStyleSheets","result","render","strings","root","renderTemplate","values","oldElement","_events","removeEventListener","addEventListener","_props","noReflect","attrValue","getPropValue","hasAttribute","syncAttribute","length","adoptedCallback","disconnectedCallback","handleEvent","event","type","bubbles","composed","dispatchEvent","Event","old","define","tagName","defineElement","_renderPending","_updateComplete","Promise","resolve","_resolveUpdate","queueMicrotask","_performUpdate","error","updateComplete","requestUpdate"],"mappings":"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"}
|
package/dist/props.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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 = null;\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) => out + str + (i < valueCount ? marker : \"\"), \"\")\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 * @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/dist/utils.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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 */\nconst Escape = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\nexport function escapeHtml(str) {\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 .replace(/>\\s+</g, \"><\"); // whitespace between tags\n}\n"],"names":["defineElement","tagName","Element","window","customElements","get","define","Escape","escapeHtml","str","String","replace","c","resolveValue","value","Array","isArray","map","item","resolveItem","join","__raw","html","strings","values","toString","undefined","reduce","acc","s","i","unsafeHTML","nothing","Object","freeze","isRaw","some","toPlainText","collapseWhitespace","string"],"mappings":"AAMO,SAASA,EAAcC,EAASC,GACf,oBAAXC,QAA0B,mBAAoBA,SAClDA,OAAOC,eAAeC,IAAIJ,IAC7BE,OAAOC,eAAeE,OAAOL,EAASC,GAG5C,CAQA,MAAMK,EAAS,CAAE,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,SACtE,SAASC,EAAWC,GACzB,OAAOC,OAAOD,GAAKE,QAAQ,WAAYC,GAAKL,EAAOK,GACrD,CASO,SAASC,EAAaC,GAC3B,OAAIC,MAAMC,QAAQF,GACTA,EAAMG,IAAIC,GAAQC,EAAYD,IAAOE,KAAK,IAE5CD,EAAYL,EACrB,CASA,SAASK,EAAYL,GACnB,OAAOA,GAAOO,MAAQX,OAAOI,GAASN,EAAWE,OAAOI,GAAS,IACnE,CAUO,SAASQ,EAAKC,KAAYC,GAC/B,IAAIf,EACJ,MAAO,CACLY,OAAO,EACPE,UACAC,SACAC,SAAU,UACIC,IAARjB,IACFA,EAAMc,EAAQI,OAAO,CAACC,EAAKC,EAAGC,IACrBF,EAAMC,EAAIhB,EAAaW,EAAOM,IACpC,KAEErB,GAGb,CAQO,SAASsB,EAAWtB,GACzB,MAAO,CAAEY,OAAO,EAAMI,SAAU,IAAMhB,GAAO,GAC/C,CAQY,MAACuB,EAAUC,OAAOC,OAAO,CAAEb,OAAO,EAAMI,SAAU,IAAM,KAQvDU,EAAQrB,GACnBC,MAAMC,QAAQF,GAASA,EAAMsB,KAAKlB,GAAQA,GAAMG,OAASP,GAAOO,MAQrDgB,EAAcvB,GACzBC,MAAMC,QAAQF,GAASA,EAAMG,IAAIC,GAAQR,OAAOQ,GAAQ,KAAKE,KAAK,IAAMV,OAAOI,GAAS,IAQnF,SAASwB,EAAmBC,GACjC,OAAOA,EACJ5B,QAAQ,UAAW,KACnBA,QAAQ,UAAW,KACnBA,QAAQ,SAAU,KAClBA,QAAQ,SAAU,KACvB"}
|