Skip to content

Commit

Permalink
spike out initial twoPass option for additional state retention.
Browse files Browse the repository at this point in the history
  • Loading branch information
botandrose-machine committed Dec 12, 2024
1 parent 31617cd commit a8a45e7
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 2 deletions.
53 changes: 51 additions & 2 deletions src/idiomorph.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ var Idiomorph = (function () {

// innerHTML, so we are only updating the children
morphChildren(normalizedNewContent, oldNode, ctx);
if (ctx.twoPass) {
restoreFromPantry(oldNode, ctx);
}
return Array.from(oldNode.children);

} else if (ctx.morphStyle === "outerHTML" || ctx.morphStyle == null) {
Expand Down Expand Up @@ -713,11 +716,19 @@ var Idiomorph = (function () {
ignoreActiveValue: mergedConfig.ignoreActiveValue,
idMap: createIdMap(oldNode, newContent),
deadIds: new Set(),
pantry: mergedConfig.twoPass && createPantry(),
callbacks: mergedConfig.callbacks,
head: mergedConfig.head
}
}

function createPantry() {
const pantry = document.createElement("div");
pantry.hidden = true;
document.body.insertAdjacentHTML("afterend", pantry);
return pantry;
}

/**
*
* @param {Node | null} node1
Expand Down Expand Up @@ -1063,11 +1074,49 @@ var Idiomorph = (function () {
function removeNode(tempNode, ctx) {
removeIdsFromConsideration(ctx, tempNode)
if (ctx.callbacks.beforeNodeRemoved(tempNode) === false) return;

tempNode.parentNode?.removeChild(tempNode);
if (ctx.pantry) {
moveToPantry(tempNode.pantry, ctx);
} else {
tempNode.parentNode?.removeChild(tempNode);
}
ctx.callbacks.afterNodeRemoved(tempNode);
}

function moveToPantry(node, ctx) {
if (!node) return;

// If the node is a leaf (no children), process it, and then we're done
if (!node.children || node.children.length === 0) {
if (node.id) {
ctx.pantry.appendChild(node);
}

// otherwise we need to process the children first
} else {
Array.from(node.children).forEach(child => {
moveToPantry(child, ctx);
});

// After processing children, process the current node
if (node.id) {
ctx.pantry.appendChild(node);
}
}
}

function restoreFromPantry(root, ctx) {
Array.from(ctx.pantry.children).forEach(element => {
const matchElement = root.findElementById(element.id);
if (matchElement) {
matchElement.before(element);
element.replaceChildren(matchElement.childNodes);
matchElement.remove();
syncNodeFrom(newContent, oldNode, ctx);
}
});
ctx.pantry.remove();
}

//=============================================================================
// ID Set Functions
//=============================================================================
Expand Down
52 changes: 52 additions & 0 deletions test/two-pass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe("Two-pass option for retaining more state", function(){

beforeEach(function() {
clearWorkArea();
});

it('fails to preserve all non-attribute element state with single-pass option', function()
{
getWorkArea().append(make(`
<div>
<input type="checkbox" checked id="first">
<input type="checkbox" checked id="second">
</div>
`));
document.getElementById("first").indeterminate = true
document.getElementById("second").indeterminate = true

let finalSrc = `
<div>
<input type="checkbox" checked id="second">
<input type="checkbox" checked id="first">
</div>
`;
Idiomorph.morph(getWorkArea(), finalSrc, {morphStyle:'innerHTML'});

document.getElementById("first").indeterminate.should.be.false
document.getElementById("second").indeterminate.should.be.true
});

it('preserves all non-attribute element state with two-pass option', function()
{
getWorkArea().append(make(`
<div>
<input type="checkbox" checked id="first">
<input type="checkbox" checked id="second">
</div>
`));
document.getElementById("first").indeterminate = true
document.getElementById("second").indeterminate = true

let finalSrc = `
<div>
<input type="checkbox" checked id="second">
<input type="checkbox" checked id="first">
</div>
`;
Idiomorph.morph(getWorkArea(), finalSrc, {morphStyle:'innerHTML',twoPass:true});

document.getElementById("first").indeterminate.should.be.true
document.getElementById("second").indeterminate.should.be.true
});
});

0 comments on commit a8a45e7

Please sign in to comment.