aboutsummaryrefslogtreecommitdiff
path: root/priv/static
diff options
context:
space:
mode:
Diffstat (limited to 'priv/static')
-rw-r--r--priv/static/lustre-server-component.min.mjs2
-rw-r--r--priv/static/lustre-server-component.mjs618
2 files changed, 256 insertions, 364 deletions
diff --git a/priv/static/lustre-server-component.min.mjs b/priv/static/lustre-server-component.min.mjs
index 979b991..2f790b8 100644
--- a/priv/static/lustre-server-component.min.mjs
+++ b/priv/static/lustre-server-component.min.mjs
@@ -1 +1 @@
-var c=class{withFields(e){let n=Object.keys(this).map(u=>u in e?e[u]:this[u]);return new this.constructor(...n)}},w=class{static fromArray(e,n){let u=n||new d;return e.reduceRight((i,s)=>new y(s,i),u)}[Symbol.iterator](){return new k(this)}toArray(){return[...this]}atLeastLength(e){for(let n of this){if(e<=0)return!0;e--}return e<=0}hasLength(e){for(let n of this){if(e<=0)return!1;e--}return e===0}countLength(){let e=0;for(let n of this)e++;return e}};var k=class{#t;constructor(e){this.#t=e}next(){if(this.#t instanceof d)return{done:!0};{let{head:e,tail:n}=this.#t;return this.#t=n,{value:e,done:!1}}}},d=class extends w{},y=class extends w{constructor(e,n){super(),this.head=e,this.tail=n}};var m=class t extends c{static isResult(e){return e instanceof t}},p=class extends m{constructor(e){super(),this[0]=e}isOk(){return!0}},a=class extends m{constructor(e){super(),this[0]=e}isOk(){return!1}};var Mt=new DataView(new ArrayBuffer(8));var X=5,E=Math.pow(2,X),qt=E-1,Bt=E/2,Dt=E/4;function A(t,e){if(t.isOk()){let n=t[0];return new p(e(n))}else{let n=t[0];return new a(n)}}function f(t,e,n,u){if(e?.tag&&t?.nodeType===1){let i=e.tag.toUpperCase(),s=e.namespace||"http://www.w3.org/1999/xhtml";return t.nodeName===i&&t.namespaceURI==s?Q(t,e,n,u):B(t,e,n,u)}return e?.tag?B(t,e,n,u):typeof e?.content=="string"?t?.nodeType===3?te(t,e):ee(t,e):document.createComment(["[internal lustre error] I couldn't work out how to render this element. This","function should only be called internally by lustre's runtime: if you think","this is an error, please open an issue at","https://github.com/hayleigh-dot-dev/gleam-lustre/issues/new"].join(" "))}function D(t,e,n){for(let u of e[0]){let i=u[0];if(i==="0")f(t,u[1],n,t.parentNode);else{let s=Array.from(i),r=s.slice(0,-1).join(""),l=s.slice(-1)[0],o=t.querySelector(`[data-lustre-key="${i}"]`)??t.querySelector(`[data-lustre-key="${r}"]`).childNodes[l];f(o,u[1],n,o.parentNode)}}for(let u of e[1]){let i=u[0],s=Array.from(i),r=s.slice(0,-1).join(""),l=s.slice(-1)[0];(t.querySelector(`[data-lustre-key="${i}"]`)??t.querySelector(`[data-lustre-key="${r}"]`).childNodes[l]).remove()}for(let u of e[2]){let i=u[0],s=i==="0"?t:t.querySelector(`[data-lustre-key="${i}"]`);s.$lustre??={__registered_events:new Set};for(let r of u[0])x(s,r.name,r.value,n);for(let r of u[1])if(s.$lustre.__registered_events.has(r)){let l=r.slice(2).toLowerCase();s.removeEventListener(l,s.$lustre[`${r}Handler`]),s.$lustre.__registered_events.delete(r),delete s.$lustre[r],delete s.$lustre[`${r}Handler`]}else s.removeAttribute(r)}return t}function B(t,e,n,u=null){let i=e.namespace?document.createElementNS(e.namespace,e.tag):document.createElement(e.tag);i.$lustre={__registered_events:new Set};let s="";for(let r of e.attrs)r[0]==="class"?x(i,r[0],`${i.className} ${r[1]}`):r[0]==="style"?x(i,r[0],`${i.style.cssText} ${r[1]}`):r[0]==="dangerous-unescaped-html"?s+=r[1]:r[0]!==""&&x(i,r[0],r[1],n);if(customElements.get(e.tag))i._slot=e.children;else if(e.tag==="slot"){let r=new d,l=u;for(;l;)if(l._slot){r=l._slot;break}else l=l.parentNode;for(let o of r)i.appendChild(f(null,o,n,i))}else if(s)i.innerHTML=s;else for(let r of e.children)i.appendChild(f(null,r,n,i));return t&&t.replaceWith(i),i}function Q(t,e,n,u){let i=t.attributes,s=new Map;t.$lustre??={__registered_events:new Set};for(let r of e.attrs)r[0]==="class"&&s.has("class")?s.set(r[0],`${s.get("class")} ${r[1]}`):r[0]==="style"&&s.has("style")?s.set(r[0],`${s.get("style")} ${r[1]}`):r[0]==="dangerous-unescaped-html"&&s.has("dangerous-unescaped-html")?s.set(r[0],`${s.get("dangerous-unescaped-html")} ${r[1]}`):r[0]!==""&&s.set(r[0],r[1]);for(let{name:r}of i)if(!s.has(r))t.removeAttribute(r);else{let l=s.get(r);x(t,r,l,n),s.delete(r)}for(let r of t.$lustre.__registered_events)if(!s.has(r)){let l=r.slice(2).toLowerCase();t.removeEventListener(l,t.$lustre[`${r}Handler`]),t.$lustre.__registered_events.delete(r),delete t.$lustre[r],delete t.$lustre[`${r}Handler`]}for(let[r,l]of s)x(t,r,l,n);if(customElements.get(e.tag))t._slot=e.children;else if(e.tag==="slot"){let r=t.firstChild,l=new d,o=u;for(;o;)if(o._slot){l=o._slot;break}else o=o.parentNode;for(;r;)Array.isArray(l)&&l.length?f(r,l.shift(),n,t):l.head&&(f(r,l.head,n,t),l=l.tail),r=r.nextSibling;for(let h of l)t.appendChild(f(null,h,n,t))}else if(s.has("dangerous-unescaped-html"))t.innerHTML=s.get("dangerous-unescaped-html");else{let r=t.firstChild,l=e.children;for(;r;)if(Array.isArray(l)&&l.length){let o=r.nextSibling;f(r,l.shift(),n,t),r=o}else if(l.head){let o=r.nextSibling;f(r,l.head,n,t),l=l.tail,r=o}else{let o=r.nextSibling;r.remove(),r=o}for(let o of l)t.appendChild(f(null,o,n,t))}return t}function x(t,e,n,u){switch(typeof n){case(e.startsWith("data-lustre-on-")&&"string"):{if(!n){t.removeAttribute(e),t.removeEventListener(i,t.$lustre[`${e}Handler`]);break}if(t.hasAttribute(e))break;let i=e.slice(15).toLowerCase(),s=r=>u(re(r));t.$lustre[`${e}Handler`]&&t.removeEventListener(i,t.$lustre[`${e}Handler`]),t.addEventListener(i,s),t.$lustre[e]=n,t.$lustre[`${e}Handler`]=s,t.$lustre.__registered_events.add(e),t.setAttribute(e,n);break}case"string":e==="value"&&(t.value=n),n===""?t.removeAttribute(e):t.setAttribute(e,n);break;case(e.startsWith("on")&&"function"):{if(t.$lustre[e]===n)break;let i=e.slice(2).toLowerCase(),s=r=>A(n(r),u);t.$lustre[`${e}Handler`]&&t.removeEventListener(i,t.$lustre[`${e}Handler`]),t.addEventListener(i,s),t.$lustre[e]=n,t.$lustre[`${e}Handler`]=s,t.$lustre.__registered_events.add(e);break}default:t[e]=n}}function ee(t,e){let n=document.createTextNode(e.content);return t&&t.replaceWith(n),n}function te(t,e){let n=t.nodeValue,u=e.content;return u?(n!==u&&(t.nodeValue=u),t):(t?.remove(),null)}function re(t){let e=t.target,n=e.getAttribute(`data-lustre-on-${t.type}`),u=JSON.parse(e.getAttribute("data-lustre-data")||"{}"),i=JSON.parse(e.getAttribute("data-lustre-include")||"[]");switch(t.type){case"input":case"change":i.push("target.value");break}return{tag:n,data:i.reduce((s,r)=>{let l=r.split(".");for(let o=0,h=s,b=t;o<l.length;o++)o===l.length-1?h[l[o]]=b[l[o]]:(h[l[o]]??={},b=b[l[o]],h=h[l[o]]);return s},u)}}var C=class extends HTMLElement{static get observedAttributes(){return["route"]}#t=null;#r=null;#e=null;constructor(){super(),this.#t=new MutationObserver(e=>{let n=[];for(let u of e)if(u.type==="attributes"){let{attributeName:i,oldValue:s}=u,r=this.getAttribute(i);if(s!==r)try{n.push([i,JSON.parse(r)])}catch{n.push([i,r])}}n.length&&this.#e?.send(JSON.stringify([5,n]))})}connectedCallback(){this.#r=document.createElement("div"),this.appendChild(this.#r)}attributeChangedCallback(e,n,u){switch(e){case"route":if(!u)this.#e?.close(),this.#e=null;else if(n!==u){let i=this.getAttribute("id"),s=u+(i?`?id=${i}`:"");this.#e?.close(),this.#e=new WebSocket(`ws://${window.location.host}${s}`),this.#e.addEventListener("message",r=>this.messageReceivedCallback(r))}}}messageReceivedCallback({data:e}){let[n,...u]=JSON.parse(e);switch(n){case 0:return this.diff(u);case 1:return this.emit(u);case 2:return this.init(u)}}init([e,n]){let u=[];for(let i of e)i in this?u.push([i,this[i]]):this.hasAttribute(i)&&u.push([i,this.getAttribute(i)]),Object.defineProperty(this,i,{get(){return this[`_${i}`]??this.getAttribute(i)},set(s){let r=this[i];typeof s=="string"?this.setAttribute(i,s):this[`_${i}`]=s,r!==s&&this.#e?.send(JSON.stringify([5,[[i,s]]]))}});this.#t.observe(this,{attributeFilter:e,attributeOldValue:!0,attributes:!0,characterData:!1,characterDataOldValue:!1,childList:!1,subtree:!1}),this.morph(n),u.length&&this.#e?.send(JSON.stringify([5,u]))}morph(e){this.#r=f(this.#r,e,n=>{this.#e?.send(JSON.stringify([4,n.tag,n.data]))})}diff([e]){this.#r=D(this.#r,e,n=>{this.#e?.send(JSON.stringify([4,n.tag,n.data]))})}emit([e,n]){this.dispatchEvent(new CustomEvent(e,{detail:n}))}disconnectedCallback(){this.#e?.close()}};window.customElements.define("lustre-server-component",C);export{C as LustreServerComponent};
+function k(i,n,r,s=!1){let t,l=[{prev:i,next:n,parent:i.parentNode}];for(;l.length;){let{prev:e,next:o,parent:u}=l.pop();if(o.subtree!==void 0&&(o=o.subtree()),o.content!==void 0)if(e)if(e.nodeType===Node.TEXT_NODE)e.textContent=o.content,t??=e;else{let a=document.createTextNode(o.content);u.replaceChild(a,e),t??=a}else{let a=document.createTextNode(o.content);u.appendChild(a),t??=a}else if(o.tag!==void 0){let a=$({prev:e,next:o,dispatch:r,stack:l,isComponent:s});e?e!==a&&u.replaceChild(a,e):u.appendChild(a),t??=a}}return t}function T(i,n,r){let s=i.parentNode;for(let t of n[0]){let l=t[0].split("-"),e=t[1],o=N(s,l),u;if(o!==null&&o!==s)u=k(o,e,r);else{let a=N(s,l.slice(0,-1)),f=document.createTextNode("");a.appendChild(f),u=k(f,e,r)}l==="0"&&(i=u)}for(let t of n[1]){let l=t[0].split("-");N(s,l).remove()}for(let t of n[2]){let l=t[0].split("-"),e=t[1],o=N(s,l),u=v.get(o);for(let a of e[0]){let f=a[0],m=a[1];if(f.startsWith("data-lustre-on-")){let b=f.slice(15),d=r(J);u.has(b)||el.addEventListener(b,y),u.set(b,d),el.setAttribute(f,m)}else o.setAttribute(f,m),o[f]=m}for(let a of e[1])if(a[0].startsWith("data-lustre-on-")){let f=a[0].slice(15);o.removeEventListener(f,y),u.delete(f)}else o.removeAttribute(a[0])}return i}function $({prev:i,next:n,dispatch:r,stack:s}){let t=n.namespace||"http://www.w3.org/1999/xhtml",l=i&&i.nodeType===Node.ELEMENT_NODE&&i.localName===n.tag&&i.namespaceURI===(n.namespace||"http://www.w3.org/1999/xhtml"),e=l?i:t?document.createElementNS(t,n.tag):document.createElement(n.tag),o;if(v.has(e))o=v.get(e);else{let c=new Map;v.set(e,c),o=c}let u=l?new Set(o.keys()):null,a=l?new Set(Array.from(i.attributes,c=>c.name)):null,f=null,m=null,b=null;for(let c of n.attrs){let h=c[0],p=c[1];if(c[2])e[h]=p;else if(h.startsWith("on")){let g=h.slice(2),A=r(p);o.has(g)||e.addEventListener(g,y),o.set(g,A),l&&u.delete(g)}else if(h.startsWith("data-lustre-on-")){let g=h.slice(15),A=r(J);o.has(g)||e.addEventListener(g,y),o.set(g,A),e.setAttribute(h,p)}else h==="class"?f=f===null?p:f+" "+p:h==="style"?m=m===null?p:m+p:h==="dangerous-unescaped-html"?b=p:(e.setAttribute(h,p),e[h]=p,l&&a.delete(h))}if(f!==null&&(e.setAttribute("class",f),l&&a.delete("class")),m!==null&&(e.setAttribute("style",m),l&&a.delete("style")),l){for(let c of a)e.removeAttribute(c);for(let c of u)e.removeEventListener(c,y)}if(n.key!==void 0&&n.key!=="")e.setAttribute("data-lustre-key",n.key);else if(b!==null)return e.innerHTML=b,e;let d=i?.firstChild,C=null,w=null,O=null,E=n.children[Symbol.iterator]().next().value;E!==void 0&&E.key!==void 0&&E.key!==""&&(C=new Set,w=L(i),O=L(n));for(let c of n.children)if(c.key!==void 0&&C!==null){for(;d&&!O.has(d.getAttribute("data-lustre-key"));){let p=d.nextSibling;e.removeChild(d),d=p}if(w.size===0){s.unshift({prev:d,next:c,parent:e}),d=d?.nextSibling;continue}if(C.has(c.key)){console.warn(`Duplicate key found in Lustre vnode: ${c.key}`),s.unshift({prev:null,next:c,parent:e});continue}C.add(c.key);let h=w.get(c.key);if(!h&&!d){s.unshift({prev:null,next:c,parent:e});continue}if(!h&&d!==null){let p=document.createTextNode("");e.insertBefore(p,d),s.unshift({prev:p,next:c,parent:e});continue}if(!h||h===d){s.unshift({prev:d,next:c,parent:e}),d=d?.nextSibling;continue}e.insertBefore(h,d),s.unshift({prev:h,next:c,parent:e})}else s.unshift({prev:d,next:c,parent:e}),d=d?.nextSibling;for(;d;){let c=d.nextSibling;e.removeChild(d),d=c}return e}var v=new WeakMap;function y(i){if(!v.has(i.target)){i.target.removeEventListener(i.type,y);return}let n=v.get(i.target);if(!n.has(i.type)){i.target.removeEventListener(i.type,y);return}n.get(i.type)(i)}function J(i){let n=i.target,r=n.getAttribute(`data-lustre-on-${i.type}`),s=JSON.parse(n.getAttribute("data-lustre-data")||"{}"),t=JSON.parse(n.getAttribute("data-lustre-include")||"[]");switch(i.type){case"input":case"change":t.push("target.value");break}return{tag:r,data:t.reduce((l,e)=>{let o=e.split(".");for(let u=0,a=l,f=i;u<o.length;u++)u===o.length-1?a[o[u]]=f[o[u]]:(a[o[u]]??={},f=f[o[u]],a=a[o[u]]);return l},{data:s})}}function L(i){let n=new Map;if(i)for(let r of i.children){let s=r.key||r?.getAttribute("data-lustre-key");s&&n.set(s,r)}return n}function N(i,n){let r,s,t=i;for(;[r,...s]=n,r!==void 0;)t=t.childNodes.item(r),n=s;return t}var S=class extends HTMLElement{static get observedAttributes(){return["route"]}#n=null;#t=null;#e=null;constructor(){super(),this.#n=new MutationObserver(n=>{let r=[];for(let s of n)if(s.type==="attributes"){let{attributeName:t,oldValue:l}=s,e=this.getAttribute(t);if(l!==e)try{r.push([t,JSON.parse(e)])}catch{r.push([t,e])}}r.length&&this.#e?.send(JSON.stringify([5,r]))})}connectedCallback(){this.#t=document.createElement("div"),this.appendChild(this.#t)}attributeChangedCallback(n,r,s){switch(n){case"route":if(!s)this.#e?.close(),this.#e=null;else if(r!==s){let t=this.getAttribute("id"),l=s+(t?`?id=${t}`:"");this.#e?.close(),this.#e=new WebSocket(`ws://${window.location.host}${l}`),this.#e.addEventListener("message",e=>this.messageReceivedCallback(e))}}}messageReceivedCallback({data:n}){let[r,...s]=JSON.parse(n);switch(r){case 0:return this.diff(s);case 1:return this.emit(s);case 2:return this.init(s)}}init([n,r]){let s=[];for(let t of n)t in this?s.push([t,this[t]]):this.hasAttribute(t)&&s.push([t,this.getAttribute(t)]),Object.defineProperty(this,t,{get(){return this[`_${t}`]??this.getAttribute(t)},set(l){let e=this[t];typeof l=="string"?this.setAttribute(t,l):this[`_${t}`]=l,e!==l&&this.#e?.send(JSON.stringify([5,[[t,l]]]))}});this.#n.observe(this,{attributeFilter:n,attributeOldValue:!0,attributes:!0,characterData:!1,characterDataOldValue:!1,childList:!1,subtree:!1}),this.morph(r),s.length&&this.#e?.send(JSON.stringify([5,s]))}morph(n){this.#t=k(this.#t,n,r=>s=>{let t=r(s);this.#e?.send(JSON.stringify([4,t.tag,t.data]))})}diff([n]){this.#t=T(this.#t,n,r=>s=>{let t=r(s);this.#e?.send(JSON.stringify([4,t.tag,t.data]))})}emit([n,r]){this.dispatchEvent(new CustomEvent(n,{detail:r}))}disconnectedCallback(){this.#e?.close()}};window.customElements.define("lustre-server-component",S);export{S as LustreServerComponent};
diff --git a/priv/static/lustre-server-component.mjs b/priv/static/lustre-server-component.mjs
index aeb7d63..f217b44 100644
--- a/priv/static/lustre-server-component.mjs
+++ b/priv/static/lustre-server-component.mjs
@@ -5,391 +5,257 @@ var init = 2;
var event = 4;
var attrs = 5;
-// build/dev/javascript/prelude.mjs
-var CustomType = class {
- withFields(fields) {
- let properties = Object.keys(this).map(
- (label) => label in fields ? fields[label] : this[label]
- );
- return new this.constructor(...properties);
- }
-};
-var List = class {
- static fromArray(array, tail) {
- let t = tail || new Empty();
- return array.reduceRight((xs, x) => new NonEmpty(x, xs), t);
- }
- [Symbol.iterator]() {
- return new ListIterator(this);
- }
- toArray() {
- return [...this];
- }
- atLeastLength(desired) {
- for (let _ of this) {
- if (desired <= 0)
- return true;
- desired--;
- }
- return desired <= 0;
- }
- hasLength(desired) {
- for (let _ of this) {
- if (desired <= 0)
- return false;
- desired--;
- }
- return desired === 0;
- }
- countLength() {
- let length2 = 0;
- for (let _ of this)
- length2++;
- return length2;
- }
-};
-var ListIterator = class {
- #current;
- constructor(current) {
- this.#current = current;
- }
- next() {
- if (this.#current instanceof Empty) {
- return { done: true };
- } else {
- let { head, tail } = this.#current;
- this.#current = tail;
- return { value: head, done: false };
- }
- }
-};
-var Empty = class extends List {
-};
-var NonEmpty = class extends List {
- constructor(head, tail) {
- super();
- this.head = head;
- this.tail = tail;
- }
-};
-var Result = class _Result extends CustomType {
- static isResult(data) {
- return data instanceof _Result;
- }
-};
-var Ok = class extends Result {
- constructor(value) {
- super();
- this[0] = value;
- }
- isOk() {
- return true;
- }
-};
-var Error = class extends Result {
- constructor(detail) {
- super();
- this[0] = detail;
- }
- isOk() {
- return false;
- }
-};
-
-// build/dev/javascript/gleam_stdlib/dict.mjs
-var tempDataView = new DataView(new ArrayBuffer(8));
-var SHIFT = 5;
-var BUCKET_SIZE = Math.pow(2, SHIFT);
-var MASK = BUCKET_SIZE - 1;
-var MAX_INDEX_NODE = BUCKET_SIZE / 2;
-var MIN_ARRAY_NODE = BUCKET_SIZE / 4;
-
-// build/dev/javascript/gleam_stdlib/gleam/result.mjs
-function map2(result, fun) {
- if (result.isOk()) {
- let x = result[0];
- return new Ok(fun(x));
- } else {
- let e = result[0];
- return new Error(e);
- }
-}
-
// build/dev/javascript/lustre/vdom.ffi.mjs
-function morph(prev, curr, dispatch, parent) {
- if (curr?.tag && prev?.nodeType === 1) {
- const nodeName = curr.tag.toUpperCase();
- const ns = curr.namespace || "http://www.w3.org/1999/xhtml";
- if (prev.nodeName === nodeName && prev.namespaceURI == ns) {
- return morphElement(prev, curr, dispatch, parent);
- } else {
- return createElement(prev, curr, dispatch, parent);
+function morph(prev, next, dispatch, isComponent = false) {
+ let out;
+ let stack = [{ prev, next, parent: prev.parentNode }];
+ while (stack.length) {
+ let { prev: prev2, next: next2, parent } = stack.pop();
+ if (next2.subtree !== void 0)
+ next2 = next2.subtree();
+ if (next2.content !== void 0) {
+ if (!prev2) {
+ const created = document.createTextNode(next2.content);
+ parent.appendChild(created);
+ out ??= created;
+ } else if (prev2.nodeType === Node.TEXT_NODE) {
+ prev2.textContent = next2.content;
+ out ??= prev2;
+ } else {
+ const created = document.createTextNode(next2.content);
+ parent.replaceChild(created, prev2);
+ out ??= created;
+ }
+ } else if (next2.tag !== void 0) {
+ const created = createElementNode({
+ prev: prev2,
+ next: next2,
+ dispatch,
+ stack,
+ isComponent
+ });
+ if (!prev2) {
+ parent.appendChild(created);
+ } else if (prev2 !== created) {
+ parent.replaceChild(created, prev2);
+ }
+ out ??= created;
}
}
- if (curr?.tag) {
- return createElement(prev, curr, dispatch, parent);
- }
- if (typeof curr?.content === "string") {
- return prev?.nodeType === 3 ? morphText(prev, curr) : createText(prev, curr);
- }
- return document.createComment(
- [
- "[internal lustre error] I couldn't work out how to render this element. This",
- "function should only be called internally by lustre's runtime: if you think",
- "this is an error, please open an issue at",
- "https://github.com/hayleigh-dot-dev/gleam-lustre/issues/new"
- ].join(" ")
- );
+ return out;
}
function patch(root, diff2, dispatch) {
+ const rootParent = root.parentNode;
for (const created of diff2[0]) {
- const key = created[0];
- if (key === "0") {
- morph(root, created[1], dispatch, root.parentNode);
+ const key = created[0].split("-");
+ const next = created[1];
+ const prev = getDeepChild(rootParent, key);
+ let result;
+ if (prev !== null && prev !== rootParent) {
+ result = morph(prev, next, dispatch);
} else {
- const segments = Array.from(key);
- const parentKey = segments.slice(0, -1).join("");
- const indexKey = segments.slice(-1)[0];
- const prev = root.querySelector(`[data-lustre-key="${key}"]`) ?? root.querySelector(`[data-lustre-key="${parentKey}"]`).childNodes[indexKey];
- morph(prev, created[1], dispatch, prev.parentNode);
+ const parent = getDeepChild(rootParent, key.slice(0, -1));
+ const temp = document.createTextNode("");
+ parent.appendChild(temp);
+ result = morph(temp, next, dispatch);
+ }
+ if (key === "0") {
+ root = result;
}
}
for (const removed of diff2[1]) {
- const key = removed[0];
- const segments = Array.from(key);
- const parentKey = segments.slice(0, -1).join("");
- const indexKey = segments.slice(-1)[0];
- const prev = root.querySelector(`[data-lustre-key="${key}"]`) ?? root.querySelector(`[data-lustre-key="${parentKey}"]`).childNodes[indexKey];
- prev.remove();
+ const key = removed[0].split("-");
+ const deletedNode = getDeepChild(rootParent, key);
+ deletedNode.remove();
}
for (const updated of diff2[2]) {
- const key = updated[0];
- const prev = key === "0" ? root : root.querySelector(`[data-lustre-key="${key}"]`);
- prev.$lustre ??= { __registered_events: /* @__PURE__ */ new Set() };
- for (const created of updated[0]) {
- morphAttr(prev, created.name, created.value, dispatch);
- }
- for (const removed of updated[1]) {
- if (prev.$lustre.__registered_events.has(removed)) {
- const event2 = removed.slice(2).toLowerCase();
- prev.removeEventListener(event2, prev.$lustre[`${removed}Handler`]);
- prev.$lustre.__registered_events.delete(removed);
- delete prev.$lustre[removed];
- delete prev.$lustre[`${removed}Handler`];
+ const key = updated[0].split("-");
+ const patches = updated[1];
+ const prev = getDeepChild(rootParent, key);
+ const handlersForEl = registeredHandlers.get(prev);
+ for (const created of patches[0]) {
+ const name = created[0];
+ const value = created[1];
+ if (name.startsWith("data-lustre-on-")) {
+ const eventName = name.slice(15);
+ const callback = dispatch(lustreServerEventHandler);
+ if (!handlersForEl.has(eventName)) {
+ el.addEventListener(eventName, lustreGenericEventHandler);
+ }
+ handlersForEl.set(eventName, callback);
+ el.setAttribute(name, value);
} else {
- prev.removeAttribute(removed);
+ prev.setAttribute(name, value);
+ prev[name] = value;
}
}
- }
- return root;
-}
-function createElement(prev, curr, dispatch, parent = null) {
- const el = curr.namespace ? document.createElementNS(curr.namespace, curr.tag) : document.createElement(curr.tag);
- el.$lustre = {
- __registered_events: /* @__PURE__ */ new Set()
- };
- let dangerousUnescapedHtml = "";
- for (const attr of curr.attrs) {
- if (attr[0] === "class") {
- morphAttr(el, attr[0], `${el.className} ${attr[1]}`);
- } else if (attr[0] === "style") {
- morphAttr(el, attr[0], `${el.style.cssText} ${attr[1]}`);
- } else if (attr[0] === "dangerous-unescaped-html") {
- dangerousUnescapedHtml += attr[1];
- } else if (attr[0] !== "") {
- morphAttr(el, attr[0], attr[1], dispatch);
- }
- }
- if (customElements.get(curr.tag)) {
- el._slot = curr.children;
- } else if (curr.tag === "slot") {
- let children = new Empty();
- let parentWithSlot = parent;
- while (parentWithSlot) {
- if (parentWithSlot._slot) {
- children = parentWithSlot._slot;
- break;
+ for (const removed of patches[1]) {
+ if (removed[0].startsWith("data-lustre-on-")) {
+ const eventName = removed[0].slice(15);
+ prev.removeEventListener(eventName, lustreGenericEventHandler);
+ handlersForEl.delete(eventName);
} else {
- parentWithSlot = parentWithSlot.parentNode;
+ prev.removeAttribute(removed[0]);
}
}
- for (const child of children) {
- el.appendChild(morph(null, child, dispatch, el));
- }
- } else if (dangerousUnescapedHtml) {
- el.innerHTML = dangerousUnescapedHtml;
- } else {
- for (const child of curr.children) {
- el.appendChild(morph(null, child, dispatch, el));
- }
}
- if (prev)
- prev.replaceWith(el);
- return el;
+ return root;
}
-function morphElement(prev, curr, dispatch, parent) {
- const prevAttrs = prev.attributes;
- const currAttrs = /* @__PURE__ */ new Map();
- prev.$lustre ??= { __registered_events: /* @__PURE__ */ new Set() };
- for (const currAttr of curr.attrs) {
- if (currAttr[0] === "class" && currAttrs.has("class")) {
- currAttrs.set(currAttr[0], `${currAttrs.get("class")} ${currAttr[1]}`);
- } else if (currAttr[0] === "style" && currAttrs.has("style")) {
- currAttrs.set(currAttr[0], `${currAttrs.get("style")} ${currAttr[1]}`);
- } else if (currAttr[0] === "dangerous-unescaped-html" && currAttrs.has("dangerous-unescaped-html")) {
- currAttrs.set(
- currAttr[0],
- `${currAttrs.get("dangerous-unescaped-html")} ${currAttr[1]}`
- );
- } else if (currAttr[0] !== "") {
- currAttrs.set(currAttr[0], currAttr[1]);
- }
- }
- for (const { name } of prevAttrs) {
- if (!currAttrs.has(name)) {
- prev.removeAttribute(name);
+function createElementNode({ prev, next, dispatch, stack }) {
+ const namespace = next.namespace || "http://www.w3.org/1999/xhtml";
+ const canMorph = prev && prev.nodeType === Node.ELEMENT_NODE && prev.localName === next.tag && prev.namespaceURI === (next.namespace || "http://www.w3.org/1999/xhtml");
+ const el2 = canMorph ? prev : namespace ? document.createElementNS(namespace, next.tag) : document.createElement(next.tag);
+ let handlersForEl;
+ if (!registeredHandlers.has(el2)) {
+ const emptyHandlers = /* @__PURE__ */ new Map();
+ registeredHandlers.set(el2, emptyHandlers);
+ handlersForEl = emptyHandlers;
+ } else {
+ handlersForEl = registeredHandlers.get(el2);
+ }
+ const prevHandlers = canMorph ? new Set(handlersForEl.keys()) : null;
+ const prevAttributes = canMorph ? new Set(Array.from(prev.attributes, (a) => a.name)) : null;
+ let className = null;
+ let style = null;
+ let innerHTML = null;
+ for (const attr of next.attrs) {
+ const name = attr[0];
+ const value = attr[1];
+ const isProperty = attr[2];
+ if (isProperty) {
+ el2[name] = value;
+ } else if (name.startsWith("on")) {
+ const eventName = name.slice(2);
+ const callback = dispatch(value);
+ if (!handlersForEl.has(eventName)) {
+ el2.addEventListener(eventName, lustreGenericEventHandler);
+ }
+ handlersForEl.set(eventName, callback);
+ if (canMorph)
+ prevHandlers.delete(eventName);
+ } else if (name.startsWith("data-lustre-on-")) {
+ const eventName = name.slice(15);
+ const callback = dispatch(lustreServerEventHandler);
+ if (!handlersForEl.has(eventName)) {
+ el2.addEventListener(eventName, lustreGenericEventHandler);
+ }
+ handlersForEl.set(eventName, callback);
+ el2.setAttribute(name, value);
+ } else if (name === "class") {
+ className = className === null ? value : className + " " + value;
+ } else if (name === "style") {
+ style = style === null ? value : style + value;
+ } else if (name === "dangerous-unescaped-html") {
+ innerHTML = value;
} else {
- const value = currAttrs.get(name);
- morphAttr(prev, name, value, dispatch);
- currAttrs.delete(name);
+ el2.setAttribute(name, value);
+ el2[name] = value;
+ if (canMorph)
+ prevAttributes.delete(name);
}
}
- for (const name of prev.$lustre.__registered_events) {
- if (!currAttrs.has(name)) {
- const event2 = name.slice(2).toLowerCase();
- prev.removeEventListener(event2, prev.$lustre[`${name}Handler`]);
- prev.$lustre.__registered_events.delete(name);
- delete prev.$lustre[name];
- delete prev.$lustre[`${name}Handler`];
- }
+ if (className !== null) {
+ el2.setAttribute("class", className);
+ if (canMorph)
+ prevAttributes.delete("class");
}
- for (const [name, value] of currAttrs) {
- morphAttr(prev, name, value, dispatch);
+ if (style !== null) {
+ el2.setAttribute("style", style);
+ if (canMorph)
+ prevAttributes.delete("style");
}
- if (customElements.get(curr.tag)) {
- prev._slot = curr.children;
- } else if (curr.tag === "slot") {
- let prevChild = prev.firstChild;
- let currChild = new Empty();
- let parentWithSlot = parent;
- while (parentWithSlot) {
- if (parentWithSlot._slot) {
- currChild = parentWithSlot._slot;
- break;
- } else {
- parentWithSlot = parentWithSlot.parentNode;
- }
- }
- while (prevChild) {
- if (Array.isArray(currChild) && currChild.length) {
- morph(prevChild, currChild.shift(), dispatch, prev);
- } else if (currChild.head) {
- morph(prevChild, currChild.head, dispatch, prev);
- currChild = currChild.tail;
- }
- prevChild = prevChild.nextSibling;
+ if (canMorph) {
+ for (const attr of prevAttributes) {
+ el2.removeAttribute(attr);
}
- for (const child of currChild) {
- prev.appendChild(morph(null, child, dispatch, prev));
- }
- } else if (currAttrs.has("dangerous-unescaped-html")) {
- prev.innerHTML = currAttrs.get("dangerous-unescaped-html");
- } else {
- let prevChild = prev.firstChild;
- let currChild = curr.children;
- while (prevChild) {
- if (Array.isArray(currChild) && currChild.length) {
- const next = prevChild.nextSibling;
- morph(prevChild, currChild.shift(), dispatch, prev);
- prevChild = next;
- } else if (currChild.head) {
- const next = prevChild.nextSibling;
- morph(prevChild, currChild.head, dispatch, prev);
- currChild = currChild.tail;
- prevChild = next;
- } else {
- const next = prevChild.nextSibling;
- prevChild.remove();
- prevChild = next;
- }
- }
- for (const child of currChild) {
- prev.appendChild(morph(null, child, dispatch, prev));
+ for (const eventName of prevHandlers) {
+ el2.removeEventListener(eventName, lustreGenericEventHandler);
}
}
- return prev;
-}
-function morphAttr(el, name, value, dispatch) {
- switch (typeof value) {
- case (name.startsWith("data-lustre-on-") && "string"): {
- if (!value) {
- el.removeAttribute(name);
- el.removeEventListener(event2, el.$lustre[`${name}Handler`]);
- break;
+ if (next.key !== void 0 && next.key !== "") {
+ el2.setAttribute("data-lustre-key", next.key);
+ } else if (innerHTML !== null) {
+ el2.innerHTML = innerHTML;
+ return el2;
+ }
+ let prevChild = prev?.firstChild;
+ let seenKeys = null;
+ let keyedChildren = null;
+ let incomingKeyedChildren = null;
+ let firstChild = next.children[Symbol.iterator]().next().value;
+ if (firstChild !== void 0 && // Explicit checks are more verbose but truthy checks force a bunch of comparisons
+ // we don't care about: it's never gonna be a number etc.
+ firstChild.key !== void 0 && firstChild.key !== "") {
+ seenKeys = /* @__PURE__ */ new Set();
+ keyedChildren = getKeyedChildren(prev);
+ incomingKeyedChildren = getKeyedChildren(next);
+ }
+ for (const child of next.children) {
+ if (child.key !== void 0 && seenKeys !== null) {
+ while (prevChild && !incomingKeyedChildren.has(prevChild.getAttribute("data-lustre-key"))) {
+ const nextChild = prevChild.nextSibling;
+ el2.removeChild(prevChild);
+ prevChild = nextChild;
}
- if (el.hasAttribute(name))
- break;
- const event2 = name.slice(15).toLowerCase();
- const handler = (e) => dispatch(serverEventHandler(e));
- if (el.$lustre[`${name}Handler`]) {
- el.removeEventListener(event2, el.$lustre[`${name}Handler`]);
+ if (keyedChildren.size === 0) {
+ stack.unshift({ prev: prevChild, next: child, parent: el2 });
+ prevChild = prevChild?.nextSibling;
+ continue;
}
- el.addEventListener(event2, handler);
- el.$lustre[name] = value;
- el.$lustre[`${name}Handler`] = handler;
- el.$lustre.__registered_events.add(name);
- el.setAttribute(name, value);
- break;
- }
- case "string":
- if (name === "value")
- el.value = value;
- if (value === "") {
- el.removeAttribute(name);
- } else {
- el.setAttribute(name, value);
+ if (seenKeys.has(child.key)) {
+ console.warn(`Duplicate key found in Lustre vnode: ${child.key}`);
+ stack.unshift({ prev: null, next: child, parent: el2 });
+ continue;
}
- break;
- case (name.startsWith("on") && "function"): {
- if (el.$lustre[name] === value)
- break;
- const event2 = name.slice(2).toLowerCase();
- const handler = (e) => map2(value(e), dispatch);
- if (el.$lustre[`${name}Handler`]) {
- el.removeEventListener(event2, el.$lustre[`${name}Handler`]);
+ seenKeys.add(child.key);
+ const keyedChild = keyedChildren.get(child.key);
+ if (!keyedChild && !prevChild) {
+ stack.unshift({ prev: null, next: child, parent: el2 });
+ continue;
}
- el.addEventListener(event2, handler);
- el.$lustre[name] = value;
- el.$lustre[`${name}Handler`] = handler;
- el.$lustre.__registered_events.add(name);
- break;
+ if (!keyedChild && prevChild !== null) {
+ const placeholder = document.createTextNode("");
+ el2.insertBefore(placeholder, prevChild);
+ stack.unshift({ prev: placeholder, next: child, parent: el2 });
+ continue;
+ }
+ if (!keyedChild || keyedChild === prevChild) {
+ stack.unshift({ prev: prevChild, next: child, parent: el2 });
+ prevChild = prevChild?.nextSibling;
+ continue;
+ }
+ el2.insertBefore(keyedChild, prevChild);
+ stack.unshift({ prev: keyedChild, next: child, parent: el2 });
+ } else {
+ stack.unshift({ prev: prevChild, next: child, parent: el2 });
+ prevChild = prevChild?.nextSibling;
}
- default:
- el[name] = value;
}
-}
-function createText(prev, curr) {
- const el = document.createTextNode(curr.content);
- if (prev)
- prev.replaceWith(el);
- return el;
-}
-function morphText(prev, curr) {
- const prevValue = prev.nodeValue;
- const currValue = curr.content;
- if (!currValue) {
- prev?.remove();
- return null;
+ while (prevChild) {
+ const next2 = prevChild.nextSibling;
+ el2.removeChild(prevChild);
+ prevChild = next2;
}
- if (prevValue !== currValue)
- prev.nodeValue = currValue;
- return prev;
+ return el2;
}
-function serverEventHandler(event2) {
- const el = event2.target;
- const tag = el.getAttribute(`data-lustre-on-${event2.type}`);
- const data = JSON.parse(el.getAttribute("data-lustre-data") || "{}");
- const include = JSON.parse(el.getAttribute("data-lustre-include") || "[]");
+var registeredHandlers = /* @__PURE__ */ new WeakMap();
+function lustreGenericEventHandler(event2) {
+ if (!registeredHandlers.has(event2.target)) {
+ event2.target.removeEventListener(event2.type, lustreGenericEventHandler);
+ return;
+ }
+ const handlersForEventTarget = registeredHandlers.get(event2.target);
+ if (!handlersForEventTarget.has(event2.type)) {
+ event2.target.removeEventListener(event2.type, lustreGenericEventHandler);
+ return;
+ }
+ handlersForEventTarget.get(event2.type)(event2);
+}
+function lustreServerEventHandler(event2) {
+ const el2 = event2.target;
+ const tag = el2.getAttribute(`data-lustre-on-${event2.type}`);
+ const data = JSON.parse(el2.getAttribute("data-lustre-data") || "{}");
+ const include = JSON.parse(el2.getAttribute("data-lustre-include") || "[]");
switch (event2.type) {
case "input":
case "change":
@@ -398,21 +264,45 @@ function serverEventHandler(event2) {
}
return {
tag,
- data: include.reduce((data2, property) => {
- const path = property.split(".");
- for (let i = 0, o = data2, e = event2; i < path.length; i++) {
- if (i === path.length - 1) {
- o[path[i]] = e[path[i]];
- } else {
- o[path[i]] ??= {};
- e = e[path[i]];
- o = o[path[i]];
+ data: include.reduce(
+ (data2, property) => {
+ const path = property.split(".");
+ for (let i = 0, o = data2, e = event2; i < path.length; i++) {
+ if (i === path.length - 1) {
+ o[path[i]] = e[path[i]];
+ } else {
+ o[path[i]] ??= {};
+ e = e[path[i]];
+ o = o[path[i]];
+ }
}
- }
- return data2;
- }, data)
+ return data2;
+ },
+ { data }
+ )
};
}
+function getKeyedChildren(el2) {
+ const keyedChildren = /* @__PURE__ */ new Map();
+ if (el2) {
+ for (const child of el2.children) {
+ const key = child.key || child?.getAttribute("data-lustre-key");
+ if (key)
+ keyedChildren.set(key, child);
+ }
+ }
+ return keyedChildren;
+}
+function getDeepChild(el2, path) {
+ let n;
+ let rest;
+ let child = el2;
+ while ([n, ...rest] = path, n !== void 0) {
+ child = child.childNodes.item(n);
+ path = rest;
+ }
+ return child;
+}
// src/server-component.mjs
var LustreServerComponent = class extends HTMLElement {
@@ -520,12 +410,14 @@ var LustreServerComponent = class extends HTMLElement {
}
}
morph(vdom) {
- this.#root = morph(this.#root, vdom, (msg) => {
+ this.#root = morph(this.#root, vdom, (handler) => (event2) => {
+ const msg = handler(event2);
this.#socket?.send(JSON.stringify([event, msg.tag, msg.data]));
});
}
diff([diff2]) {
- this.#root = patch(this.#root, diff2, (msg) => {
+ this.#root = patch(this.#root, diff2, (handler) => (event2) => {
+ const msg = handler(event2);
this.#socket?.send(JSON.stringify([event, msg.tag, msg.data]));
});
}