Refresh UI components and showcase; release updated framework packages

This commit is contained in:
Clintchiz committed 2026-09-20 18:13:24 +05:30
1 parent d800228585
commit c77f5e1cec
293 files changed
+22544 -35778

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.8.40",
"version": "0.8.41",
"type": "module",
"main": "src/index.ts",
"exports": {
+5
View File
@@ -557,6 +557,11 @@ export const NAV_RUNTIME = String.raw`
syncWrnStyles(doc);
document.querySelectorAll('script[data-wrn-controls]').forEach(function (node) { node.remove(); });
doc.querySelectorAll('script[data-wrn-controls]').forEach(function (node) {
document.body.appendChild(document.importNode(node, true));
});
var importedNodes = [];
for (
+22 -7
View File
@@ -875,6 +875,15 @@ export const REACTIVE_RUNTIME = String.raw`
}
function setupScope(el) {
// Capture the document's registry nodes for retained scopes across navigation.
var controlRegistries = Array.prototype.slice.call(document.querySelectorAll('script[data-wrn-controls]'));
for (var registryParent = el.parentElement; registryParent; registryParent = registryParent.parentElement) {
if (registryParent.__wrnControlRegistries) {
controlRegistries = controlRegistries.concat(registryParent.__wrnControlRegistries);
break;
}
}
el.__wrnControlRegistries = controlRegistries;
if (el.__wrnexusScope) {
el.removeAttribute("data-scope");
el.removeAttribute("data-wrn-scope");
@@ -1989,6 +1998,14 @@ export const REACTIVE_RUNTIME = String.raw`
// materialize the appropriate branch/rows and hydrate their bindings.
function decodeControlDefinition(value) {
try {
if (value && value.charAt(0) === "@") {
var split = value.lastIndexOf(":");
var registryId = value.slice(1, split);
var registry = controlRegistries.find(function (node) { return node.id === registryId; });
if (!registry) throw new Error("Missing control template registry");
if (!registry.__wrnDefinitions) registry.__wrnDefinitions = JSON.parse(registry.textContent);
return registry.__wrnDefinitions[Number(value.slice(split + 1))];
}
var binary = window.atob(value || "");
var bytes = new Uint8Array(binary.length);
for (var index = 0; index < binary.length; index++) {
@@ -2389,16 +2406,14 @@ export const REACTIVE_RUNTIME = String.raw`
}
});
var keyedFragment = document.createDocumentFragment();
// Leave already-positioned keyed nodes attached. Detaching every row
// restarts CSS animations (notably existing toast progress timers).
var keyedCursor = marker.nextSibling;
orderedNodes.forEach(function (node) {
keyedFragment.appendChild(node);
if (node !== keyedCursor) parent.insertBefore(node, keyedCursor);
keyedCursor = node.nextSibling;
});
parent.insertBefore(
keyedFragment,
marker.nextSibling,
);
keyedRecords = nextRecords;
clones = orderedNodes;
});
+9
View File
@@ -71,6 +71,15 @@ test("intercepts an internal link click and swaps #app in place", async () => {
expect(win.document.title).toBe("About");
});
test("navigation replaces document control registries before hydration", async () => {
install('<div id="app"><a href="/next" id="lnk">Next</a></div><script type="application/json" data-wrn-controls id="old">[]</script>');
nextHtml = '<html><body><div id="app">Next</div><script type="application/json" data-wrn-controls id="new">[[{"cond":null,"body":"Ready"}]]</script></body></html>';
win.document.getElementById("lnk").click();
await flush();
expect(win.document.getElementById("old")).toBeNull();
expect(win.document.getElementById("new")?.textContent).toContain("Ready");
});
test("prefetches focused routes once and reuses the document during navigation", async () => {
install(`<div id="app"><a href="/about" id="prefetch">About</a></div>`);
nextHtml =
+28
View File
@@ -149,6 +149,21 @@ test("compiled if blocks switch branches after hydration", () => {
expect(win.document.querySelector(".open")?.textContent).toBe("Open 3");
});
test("registry controls retain nested markup and work after the registry leaves the document", () => {
const definitions = [
[{ cond: "open", body: '<template data-wrn-if="@registry:1"></template><b>Nested</b><template data-wrn-control-end></template>' }, { cond: null, body: '<i>Closed</i>' }],
[{ cond: null, body: '<b>Nested</b>' }],
];
const win = mount(`<script type="application/json" data-wrn-controls id="registry">${JSON.stringify(definitions)}</script>` +
'<div data-scope="open: false"><button data-on-click="open = !open">Toggle</button><template data-wrn-if="@registry:0"></template><i>Closed</i><template data-wrn-control-end></template></div>');
win.document.getElementById("registry")!.remove();
win.document.querySelector("button")!.click();
expect(win.document.querySelector("b")?.textContent).toBe("Nested");
expect(win.document.querySelector("i")).toBeNull();
win.document.querySelector("button")!.click();
expect(win.document.querySelector("i")?.textContent).toBe("Closed");
});
test("compiled each blocks rerender rows and their empty branch", () => {
const definition = Buffer.from(
JSON.stringify({
@@ -1890,3 +1905,16 @@ test("a @change binding still fires for a bubbling event inside a component", ()
expect(win.document.querySelector("#out")?.textContent).toBe("changed");
});
test("appending keyed notifications does not detach existing rows", () => {
const win = mount(`<div data-scope="rows: [{id: 1, name: 'first'}]"><ul><li data-for="row in rows key row.id">{row.name}</li></ul><button data-on-click="rows = [...rows, {id: 2, name: 'second'}]">add</button></div>`);
const list = win.document.querySelector("ul")!;
const first = list.querySelector("li");
const observer = new win.MutationObserver(() => {});
observer.observe(list, {childList: true});
(win.document.querySelector("button") as unknown as HTMLElement).click();
expect(list.querySelectorAll("li").length).toBe(2);
expect(observer.takeRecords().flatMap(record => Array.from(record.removedNodes))).not.toContain(first);
observer.disconnect();
});