aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2023-12-28 10:16:31 +0000
committerHayleigh Thompson <me@hayleigh.dev>2023-12-28 10:16:31 +0000
commita6e6d07935db7af7ac706b07dfa6b8ccb5666b8a (patch)
tree0a7ec17d65af1e5415c63988455506958ab87046
parent3118042c9e402b8a113922569b0ec97cfa7aeedf (diff)
downloadlustre-a6e6d07935db7af7ac706b07dfa6b8ccb5666b8a.tar.gz
lustre-a6e6d07935db7af7ac706b07dfa6b8ccb5666b8a.zip
:bug: Fixed a bug where dom patching would always reconstruct the dom.
-rw-r--r--src/runtime.ffi.mjs93
1 files changed, 41 insertions, 52 deletions
diff --git a/src/runtime.ffi.mjs b/src/runtime.ffi.mjs
index 97aca0a..c3cec68 100644
--- a/src/runtime.ffi.mjs
+++ b/src/runtime.ffi.mjs
@@ -6,12 +6,12 @@ export function morph(prev, curr, dispatch, parent) {
// element.
if (curr?.tag && prev?.nodeType === 1) {
const nodeName = curr.tag.toUpperCase();
- const ns = curr.namespace || null;
+ const ns = curr.namespace || "http://www.w3.org/1999/xhtml";
// If the current node and the existing DOM node have the same tag and
// namespace, we can morph them together: keeping the DOM node intact and just
// updating its attributes and children.
- if (prev.nodeName === nodeName && prev.namespaceURI === ns) {
+ if (prev.nodeName === nodeName && prev.namespaceURI == ns) {
return morphElement(prev, curr, dispatch, parent);
}
// Otherwise, we need to replace the DOM node with a new one. The `createElement`
@@ -58,49 +58,43 @@ function createElement(prev, curr, dispatch, parent = null) {
__registered_events: new Set(),
};
- let attr = curr.attrs;
let dangerousUnescapedHtml = "";
- while (attr.head) {
- if (attr.head[0] === "class") {
- morphAttr(el, attr.head[0], `${el.className} ${attr.head[1]}`);
- } else if (attr.head[0] === "style") {
- morphAttr(el, attr.head[0], `${el.style.cssText} ${attr.head[1]}`);
- } else if (attr.head[0] === "dangerous-unescaped-html") {
- dangerousUnescapedHtml += attr.head[1];
- } else if (attr.head[0] !== "") {
- morphAttr(el, attr.head[0], attr.head[1], dispatch);
+ 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);
}
-
- attr = attr.tail;
}
if (customElements.get(curr.tag)) {
el._slot = curr.children;
} else if (curr.tag === "slot") {
- let child = new Empty();
+ let children = new Empty();
let parentWithSlot = parent;
while (parentWithSlot) {
if (parentWithSlot._slot) {
- child = parentWithSlot._slot;
+ children = parentWithSlot._slot;
break;
} else {
parentWithSlot = parentWithSlot.parentNode;
}
}
- while (child.head) {
- el.appendChild(morph(null, child.head, dispatch, el));
- child = child.tail;
+ for (const child of children) {
+ el.appendChild(morph(null, child, dispatch, el));
}
} else if (dangerousUnescapedHtml) {
el.innerHTML = dangerousUnescapedHtml;
} else {
- let child = curr.children;
- while (child.head) {
- el.appendChild(morph(null, child.head, dispatch, el));
- child = child.tail;
+ for (const child of curr.children) {
+ el.appendChild(morph(null, child, dispatch, el));
}
}
@@ -110,7 +104,7 @@ function createElement(prev, curr, dispatch, parent = null) {
}
function morphElement(prev, curr, dispatch, parent) {
- const prevAttrs = Object.fromEntries(prev.attributes);
+ const prevAttrs = prev.attributes;
const currAttrs = new Map();
// This can happen if we're morphing an existing DOM element that *wasn't*
@@ -119,31 +113,22 @@ function morphElement(prev, curr, dispatch, parent) {
// We're going to convert the Gleam List of attributes into a JavaScript Map
// so its easier to lookup specific attributes.
- let currAttr = curr.attrs;
- while (currAttr.head) {
- if (currAttr.head[0] === "class" && currAttrs.has("class")) {
- currAttrs.set(
- currAttr.head[0],
- `${currAttrs.get("class")} ${currAttr.head[1]}`
- );
- } else if (currAttr.head[0] === "style" && currAttrs.has("style")) {
- currAttrs.set(
- currAttr.head[0],
- `${currAttrs.get("style")} ${currAttr.head[1]}`
- );
+ 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.head[0] === "dangerous-unescaped-html" &&
+ currAttr[0] === "dangerous-unescaped-html" &&
currAttrs.has("dangerous-unescaped-html")
) {
currAttrs.set(
- currAttr.head[0],
- `${currAttrs.get("dangerous-unescaped-html")} ${currAttr.head[1]}`
+ currAttr[0],
+ `${currAttrs.get("dangerous-unescaped-html")} ${currAttr[1]}`
);
- } else if (currAttr.head[0] !== "") {
- currAttrs.set(currAttr.head[0], currAttr.head[1]);
+ } else if (currAttr[0] !== "") {
+ currAttrs.set(currAttr[0], currAttr[1]);
}
-
- currAttr = currAttr.tail;
}
// TODO: Event listeners aren't currently removed when they are removed from
@@ -194,7 +179,9 @@ function morphElement(prev, curr, dispatch, parent) {
}
while (prevChild) {
- if (currChild.head) {
+ 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;
}
@@ -202,9 +189,8 @@ function morphElement(prev, curr, dispatch, parent) {
prevChild = prevChild.nextSibling;
}
- while (currChild.head) {
- prev.appendChild(morph(null, currChild.head, dispatch, prev));
- currChild = currChild.tail;
+ 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");
@@ -213,7 +199,11 @@ function morphElement(prev, curr, dispatch, parent) {
let currChild = curr.children;
while (prevChild) {
- if (currChild.head) {
+ 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;
@@ -225,9 +215,8 @@ function morphElement(prev, curr, dispatch, parent) {
}
}
- while (currChild.head) {
- prev.appendChild(morph(null, currChild.head, dispatch, prev));
- currChild = currChild.tail;
+ for (const child of currChild) {
+ prev.appendChild(morph(null, child, dispatch, prev));
}
}
@@ -281,7 +270,7 @@ function createText(prev, curr) {
function morphText(prev, curr) {
const prevValue = prev.nodeValue;
- const currValue = curr.text;
+ const currValue = curr.content;
if (!currValue) {
prev?.remove();