diff design_system/src/components/story.js @ 251:117c4d53c9a4

[ui] Add HTML-first Web Component system Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 09:14:57 -0700
parents
children 2b6e732087ff
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/design_system/src/components/story.js	Tue Aug 04 09:14:57 2026 -0700
@@ -0,0 +1,57 @@
+function normalizeSource(source) {
+  const lines = source.replace(/^\n|\n\s*$/g, "").split("\n");
+  const indentation = lines
+    .filter(line => line.trim())
+    .map(line => line.match(/^\s*/)[0].length);
+  const minimum = indentation.length
+    ? Math.min(...indentation)
+    : 0;
+  return lines.map(line => line.slice(minimum)).join("\n");
+}
+
+export class ZenStory extends HTMLElement {
+  connectedCallback() {
+    if (this.dataset.ready === "true") return;
+    const template = this.querySelector(":scope > template");
+    if (!template) return;
+    this.dataset.ready = "true";
+
+    const heading = document.createElement("header");
+    const title = document.createElement("h3");
+    title.textContent = this.getAttribute("name") || "Example";
+    heading.append(title);
+
+    const copy = document.createElement("button");
+    copy.type = "button";
+    copy.textContent = "Copy HTML";
+    copy.addEventListener("click", async () => {
+      try {
+        await navigator.clipboard.writeText(
+          normalizeSource(template.innerHTML),
+        );
+        copy.textContent = "Copied";
+      } catch {
+        copy.textContent = "Copy unavailable";
+      }
+      setTimeout(() => {
+        copy.textContent = "Copy HTML";
+      }, 1200);
+    });
+    heading.append(copy);
+
+    const canvas = document.createElement("div");
+    canvas.className = "story-canvas";
+    canvas.append(template.content.cloneNode(true));
+
+    const source = document.createElement("pre");
+    const code = document.createElement("code");
+    code.textContent = normalizeSource(template.innerHTML);
+    source.append(code);
+
+    this.prepend(heading, canvas, source);
+  }
+}
+
+if (!customElements.get("zen-story")) {
+  customElements.define("zen-story", ZenStory);
+}