Refresh UI components and showcase; release updated framework packages
This commit is contained in:
1 parent
d800228585
commit
c77f5e1cec
293 files changed
+22544
-35778
No files matched your search
@@ -15,14 +15,29 @@
|
||||
let request;
|
||||
let timer;
|
||||
|
||||
// Preview props are refreshed by the server when a control changes. Avoid a
|
||||
// second parent-to-child prop write during hydration, which reconstructs
|
||||
// nested composite mounts without their server-rendered children.
|
||||
function preservePreviewProps(preview) {
|
||||
const root = Array.from(preview.children).find((node) => node.tagName !== "SCRIPT");
|
||||
for (const attribute of Array.from(root?.attributes ?? [])) {
|
||||
if (attribute.name.startsWith("data-wrn-prop-bind-")) root.removeAttribute(attribute.name);
|
||||
}
|
||||
}
|
||||
document.querySelectorAll("[data-playground-preview]").forEach(preservePreviewProps);
|
||||
|
||||
function storedDesign() {
|
||||
const root = document.documentElement.dataset;
|
||||
const preference = (name) => {
|
||||
const entry = document.cookie.split("; ").find((part) => part.startsWith(`wrn-ui-${name}=`));
|
||||
return entry ? decodeURIComponent(entry.slice(entry.indexOf("=") + 1)) : "";
|
||||
};
|
||||
return {
|
||||
style: root.uiStyle || DESIGN_DEFAULTS.style,
|
||||
palette: root.uiPalette || DESIGN_DEFAULTS.palette,
|
||||
mode: root.uiMode || DESIGN_DEFAULTS.mode,
|
||||
font: root.uiFont || DESIGN_DEFAULTS.font,
|
||||
scale: root.uiScale || DESIGN_DEFAULTS.scale,
|
||||
style: preference("style") || root.uiStyle || DESIGN_DEFAULTS.style,
|
||||
palette: preference("palette") || root.uiPalette || DESIGN_DEFAULTS.palette,
|
||||
mode: preference("mode") || root.uiMode || DESIGN_DEFAULTS.mode,
|
||||
font: preference("font") || root.uiFont || DESIGN_DEFAULTS.font,
|
||||
scale: preference("scale") || root.uiScale || DESIGN_DEFAULTS.scale,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,8 +56,24 @@
|
||||
const dark =
|
||||
settings.mode === "dark" ||
|
||||
(settings.mode === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches);
|
||||
root.dataset.theme = dark ? "dark" : "light";
|
||||
if (window.wrnTheme) window.wrnTheme.set(dark ? "dark" : "light");
|
||||
else {
|
||||
const theme = dark ? "dark" : "light";
|
||||
root.dataset.theme = theme;
|
||||
const stylesheet = document.querySelector("link[data-wrnexus-theme]");
|
||||
if (stylesheet) {
|
||||
const href = stylesheet.getAttribute("href");
|
||||
if (href)
|
||||
stylesheet.setAttribute("href", href.replace(/(\/theme\/)[^/]+\//, `$1${theme}/`));
|
||||
}
|
||||
}
|
||||
|
||||
for (const button of document.querySelectorAll("[data-design-mode]")) {
|
||||
button.setAttribute(
|
||||
"aria-pressed",
|
||||
String(button.dataset.designMode === (dark ? "dark" : "light")),
|
||||
);
|
||||
}
|
||||
for (const control of document.querySelectorAll("[data-design-setting]")) {
|
||||
control.value = settings[control.dataset.designSetting] || "";
|
||||
}
|
||||
@@ -58,10 +89,29 @@
|
||||
|
||||
let design = storedDesign();
|
||||
applyDesign(design, false);
|
||||
for (const link of document.querySelectorAll(".docs-directory a[href]")) {
|
||||
const target = new URL(link.href, window.location.origin);
|
||||
if (target.pathname === window.location.pathname) link.setAttribute("aria-current", "page");
|
||||
function syncDirectory() {
|
||||
const directory = document.querySelector(".docs-directory");
|
||||
if (!directory) return;
|
||||
let active;
|
||||
for (const link of directory.querySelectorAll("a[href]")) {
|
||||
const matches =
|
||||
new URL(link.href, window.location.origin).pathname.replace(/\/$/, "") ===
|
||||
window.location.pathname.replace(/\/$/, "");
|
||||
if (matches) {
|
||||
link.setAttribute("aria-current", "page");
|
||||
active = link;
|
||||
} else link.removeAttribute("aria-current");
|
||||
}
|
||||
if (active) {
|
||||
const bounds = directory.getBoundingClientRect();
|
||||
const item = active.getBoundingClientRect();
|
||||
if (item.top < bounds.top || item.bottom > bounds.bottom)
|
||||
directory.scrollTop += item.top - bounds.top - directory.clientHeight / 2;
|
||||
}
|
||||
}
|
||||
syncDirectory();
|
||||
window.addEventListener("wrnexus:navigated", syncDirectory);
|
||||
window.addEventListener("popstate", syncDirectory);
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||
if (design.mode === "system") applyDesign(design);
|
||||
});
|
||||
@@ -83,6 +133,7 @@
|
||||
const publicTag = playground.getAttribute("data-playground-public-tag") === "true";
|
||||
const hasSlot = playground.getAttribute("data-playground-has-slot") === "true";
|
||||
const attributes = [];
|
||||
const declarations = [];
|
||||
|
||||
for (const field of form.elements) {
|
||||
if (
|
||||
@@ -96,6 +147,14 @@
|
||||
continue;
|
||||
|
||||
const name = field.name.slice(3);
|
||||
if (field.hasAttribute("data-playground-json")) {
|
||||
try {
|
||||
const data = JSON.parse(field.value);
|
||||
declarations.push(` state ${name} = ${JSON.stringify(data, null, 2)}`);
|
||||
attributes.push(`${name}="{${name}}"`);
|
||||
continue;
|
||||
} catch { /* Keep invalid JSON visible for correction. */ }
|
||||
}
|
||||
if (field instanceof HTMLInputElement && field.type === "checkbox") {
|
||||
attributes.push(`${name}="${field.checked}"`);
|
||||
} else if (field.value !== "") {
|
||||
@@ -113,6 +172,8 @@
|
||||
code.textContent = hasSlot
|
||||
? `${opening}>\n <!-- Add slot content here -->\n</${tag}>`
|
||||
: `${opening}\n/>`;
|
||||
if (declarations.length) code.textContent = `page ${component.replace(/[^A-Za-z0-9]/g, "")}Example {\n${declarations.join("\n")}\n view {\n${code.textContent}\n }\n}`;
|
||||
|
||||
}
|
||||
|
||||
function valuesFrom(form) {
|
||||
@@ -187,8 +248,13 @@
|
||||
|
||||
window.__wrnexusDisposeBehaviors?.(preview);
|
||||
preview.replaceChildren(
|
||||
...Array.from(
|
||||
parsed.querySelectorAll('script[type="application/json"][data-wrn-controls]'),
|
||||
(node) => document.importNode(node, true),
|
||||
),
|
||||
...Array.from(next.childNodes, (node) => document.importNode(node, true)),
|
||||
);
|
||||
preservePreviewProps(preview);
|
||||
window.__wrnexusHydrateScopes?.(preview);
|
||||
window.__wrnexusHydrateCsrFetches?.(preview);
|
||||
window.wrnTheme?.bind?.(document);
|
||||
@@ -235,15 +301,19 @@
|
||||
}
|
||||
if (byName.has(outputName)) return;
|
||||
const registry = (host.__wrnexusOutputHandlers ??= new Map());
|
||||
const handlers = registry.get(outputName) ?? new Set();
|
||||
const handlers =
|
||||
(registry instanceof Map ? registry.get(outputName) : registry[outputName]) ?? new Set();
|
||||
const handler = (payload) => renderOutput(owner, outputName, payload);
|
||||
handlers.add(handler);
|
||||
registry.set(outputName, handlers);
|
||||
if (registry instanceof Map) registry.set(outputName, handlers);
|
||||
else registry[outputName] = handlers;
|
||||
byName.set(outputName, handler);
|
||||
}
|
||||
|
||||
const initializedCode = new WeakSet();
|
||||
function bindPlaygroundEvents(root = document) {
|
||||
for (const playground of root.querySelectorAll?.(SELECTOR) ?? []) {
|
||||
if (!initializedCode.has(playground)) { initializedCode.add(playground); updateCode(playground); }
|
||||
const outputNames = (playground.getAttribute("data-playground-events") || "")
|
||||
.split(",")
|
||||
.map((name) => name.trim())
|
||||
@@ -280,17 +350,19 @@
|
||||
}
|
||||
|
||||
document.addEventListener("input", (event) => {
|
||||
const playground = event.target.closest?.(SELECTOR);
|
||||
const form = event.target.closest?.("[data-playground-form]");
|
||||
const playground = form?.closest(SELECTOR);
|
||||
if (playground) schedule(playground);
|
||||
});
|
||||
|
||||
document.addEventListener("change", (event) => {
|
||||
const playground = event.target.closest?.(SELECTOR);
|
||||
const form = event.target.closest?.("[data-playground-form]");
|
||||
const playground = form?.closest(SELECTOR);
|
||||
if (playground) schedule(playground);
|
||||
});
|
||||
|
||||
document.addEventListener("reset", (event) => {
|
||||
const playground = event.target.closest?.(SELECTOR);
|
||||
const playground = event.target.closest?.("[data-playground-form]")?.closest(SELECTOR);
|
||||
if (playground)
|
||||
setTimeout(() => {
|
||||
updateCode(playground);
|
||||
@@ -299,6 +371,11 @@
|
||||
});
|
||||
|
||||
document.addEventListener("click", async (event) => {
|
||||
if (event.target.closest?.("[data-design-mode]")) {
|
||||
design.mode = event.target.closest("[data-design-mode]").dataset.designMode;
|
||||
applyDesign(design);
|
||||
return;
|
||||
}
|
||||
const designToggle = event.target.closest?.("[data-design-panel-toggle]");
|
||||
if (designToggle) {
|
||||
document.documentElement.toggleAttribute("data-design-panel-open");
|
||||
@@ -311,6 +388,12 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const copyTarget = event.target.closest?.("[data-copy-target]");
|
||||
if (copyTarget) {
|
||||
const target = document.querySelector(copyTarget.dataset.copyTarget);
|
||||
if (target) { try { await navigator.clipboard.writeText(target.textContent || ""); copyTarget.textContent = "Copied"; } catch { copyTarget.textContent = "Select code to copy"; } }
|
||||
return;
|
||||
}
|
||||
const copyText = event.target.closest?.("[data-copy-text]");
|
||||
if (copyText) {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user