@jxsuite/studio 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{2020-5wtzce5g.js → 2020-dg097k3e.js} +3 -3
- package/dist/chunks/adapter-tck81zft.js +4 -0
- package/dist/chunks/{add-new-flow-psym14sn.js → add-new-flow-a1w0bnm6.js} +3 -3
- package/dist/chunks/{cf-account-picker-haqazq5r.js → cf-account-picker-ce3kp3x1.js} +3 -3
- package/dist/chunks/cf-settings-7ye6x7g2.js +4 -0
- package/dist/chunks/color-4gj49xq6.js +4 -0
- package/dist/chunks/{color-area-6rbra5dy.js → color-area-vh9cew51.js} +2 -2
- package/dist/chunks/{color-field-gy56c94g.js → color-field-e78f16bs.js} +3 -3
- package/dist/chunks/{color-slider-sqqdyf6c.js → color-slider-fg4yhvk6.js} +2 -2
- package/dist/chunks/{combobox-72h32ejk.js → combobox-6hs00996.js} +2 -2
- package/dist/chunks/components-qce0v93f.js +4 -0
- package/dist/chunks/content-source-jjx8kgzr.js +4 -0
- package/dist/chunks/{convert-to-repeater-he70fc2b.js → convert-to-repeater-grnmea6c.js} +3 -3
- package/dist/chunks/editor-bkja5dbv.js +4 -0
- package/dist/chunks/entry-commands-1wh0btvg.js +4 -0
- package/dist/chunks/{extension-sections-0egb88dm.js → extension-sections-rp7fkega.js} +3 -3
- package/dist/chunks/file-ops-6pe4z7an.js +4 -0
- package/dist/chunks/files-ybpp3j38.js +4 -0
- package/dist/chunks/format-host-3mtbhxk1.js +4 -0
- package/dist/chunks/fs-events-xfanfxds.js +4 -0
- package/dist/chunks/grid-open-dg5qmnaj.js +4 -0
- package/dist/chunks/{grid-view-qg1b5eqx.js → grid-view-x26rfpz1.js} +16 -15
- package/dist/chunks/{icons-63bjtbs7.js → icons-853n6qt3.js} +2 -2
- package/dist/chunks/{iframe-entry-2zggzajv.js → iframe-entry-02wzcyvy.js} +4 -4
- package/dist/chunks/{iframe-entry-99nr5zfb.js → iframe-entry-42hpmwz5.js} +4 -4
- package/dist/chunks/{iframe-entry-76j8vwwx.js → iframe-entry-b5qgggyp.js} +2 -2
- package/dist/chunks/iframe-host-j2bk2xkc.js +4 -0
- package/dist/chunks/{index-32eqzv1k.js → index-203h71wy.js} +3 -3
- package/dist/chunks/index-fkrm7pdk.js +4 -0
- package/dist/chunks/{javascript-pjp734q4.js → javascript-ww5pv5ae.js} +2 -2
- package/dist/chunks/{jsonMode-njkvsa02.js → jsonMode-vwbdwwq3.js} +6 -6
- package/dist/chunks/jx-validate-w7v32f6a.js +4 -0
- package/dist/chunks/layers-g7j732cm.js +4 -0
- package/dist/chunks/layers-panel-gjrsxcck.js +4 -0
- package/dist/chunks/{listbox-c421cf8c.js → listbox-7gka1h86.js} +2 -2
- package/dist/chunks/lit-html-jdfz9rhc.js +4 -0
- package/dist/chunks/media-library-9j0v4wmk.js +4 -0
- package/dist/chunks/media-picker-cpyyt2m0.js +4 -0
- package/dist/chunks/{menu-bdpzbfvs.js → menu-d2jxpgma.js} +2 -2
- package/dist/chunks/{monaco-binding-vq1hgnn0.js → monaco-binding-63tqze27.js} +3 -3
- package/dist/chunks/monaco-buffer-9wcme176.js +4 -0
- package/dist/chunks/{monaco-setup-xrd2cbkp.js → monaco-setup-zyyynnj8.js} +3 -3
- package/dist/chunks/notify-rahnnnv4.js +4 -0
- package/dist/chunks/platform-b2aqv7v5.js +4 -0
- package/dist/chunks/{popover-b89sfej7.js → popover-9tf4cjrk.js} +2 -2
- package/dist/chunks/publish-panel-4yk2461b.js +4 -0
- package/dist/chunks/{publish-quick-qx2455cj.js → publish-quick-ajzdhhs6.js} +3 -3
- package/dist/chunks/publish-workspace-mxestzcf.js +4 -0
- package/dist/chunks/quick-search-9g6z65b0.js +4 -0
- package/dist/chunks/references-c26enpxs.js +4 -0
- package/dist/chunks/right-panel-9d0e0yg7.js +4 -0
- package/dist/chunks/source-control-xyn3a8ds.js +4 -0
- package/dist/chunks/{split-c9zfdgxf.js → split-m5rwn31z.js} +2 -2
- package/dist/chunks/{studio-x9hsze61.js → studio-02phtmqn.js} +4 -4
- package/dist/chunks/{studio-4hp8pkyt.js → studio-0khd0xdw.js} +4 -4
- package/dist/chunks/studio-1fvzq9vs.js +5 -0
- package/dist/chunks/studio-1gdfsr49.js +12 -0
- package/dist/chunks/{studio-3q40e3rj.js → studio-1meqbxns.js} +4 -4
- package/dist/chunks/{studio-q0tccd43.js → studio-1yhz0s8p.js} +4 -4
- package/dist/chunks/studio-25a5by3h.js +5 -0
- package/dist/chunks/{studio-k3xxbagr.js → studio-2b8443ms.js} +3 -3
- package/dist/chunks/{studio-3qqjeep5.js → studio-2c2mjyzj.js} +5 -5
- package/dist/chunks/studio-2dtc90zs.js +10 -0
- package/dist/chunks/{studio-9tmebf0s.js → studio-332hesdk.js} +4 -4
- package/dist/chunks/{studio-03ct6yfv.js → studio-3pndhat0.js} +8 -8
- package/dist/chunks/{studio-s32578pn.js → studio-3q6n9qvq.js} +4 -4
- package/dist/chunks/studio-4dsdnan2.js +9 -0
- package/dist/chunks/{studio-ed1hp6zf.js → studio-4fjqdzrb.js} +5 -5
- package/dist/chunks/{studio-jcnsdaep.js → studio-533py3yy.js} +4 -4
- package/dist/chunks/studio-5abge3wy.js +5 -0
- package/dist/chunks/{studio-8tr6cfrc.js → studio-6vrxzkba.js} +4 -4
- package/dist/chunks/{studio-jaaz8ce9.js → studio-73wz63nn.js} +4 -4
- package/dist/chunks/{studio-bavb31r0.js → studio-74nzv280.js} +4 -4
- package/dist/chunks/{studio-q7rtx6zq.js → studio-7cg75gtj.js} +7 -7
- package/dist/chunks/studio-89cwmnae.js +5 -0
- package/dist/chunks/studio-8m1t83kn.js +7 -0
- package/dist/chunks/{studio-q0mvbaq6.js → studio-8nyw10dp.js} +4 -4
- package/dist/chunks/{studio-83mk4bgg.js → studio-94ppxeqy.js} +8 -8
- package/dist/chunks/{studio-qmn1xc24.js → studio-957jg8g3.js} +4 -4
- package/dist/chunks/{studio-r5qy30vx.js → studio-9wyqyfqy.js} +4 -4
- package/dist/chunks/studio-a3evp7f6.js +6 -0
- package/dist/chunks/{studio-n0c5h6mf.js → studio-a6x058ew.js} +4 -4
- package/dist/chunks/studio-ana67wax.js +5 -0
- package/dist/chunks/studio-b4xhpcwy.js +4 -0
- package/dist/chunks/studio-bf0rgyf7.js +5 -0
- package/dist/chunks/{studio-x1x167jh.js → studio-bqgs90r8.js} +4 -4
- package/dist/chunks/{studio-qwdqwkj0.js → studio-c4rcaftm.js} +4 -4
- package/dist/chunks/{studio-mn0476ag.js → studio-c5dj3dzk.js} +4 -4
- package/dist/chunks/studio-cgjt1zce.js +5 -0
- package/dist/chunks/{studio-bbe4c90a.js → studio-cpdzrdgp.js} +4 -4
- package/dist/chunks/studio-cq87hree.js +5 -0
- package/dist/chunks/studio-d140sy8e.js +5 -0
- package/dist/chunks/{studio-jsgf2q5k.js → studio-dmn7gpba.js} +5 -5
- package/dist/chunks/studio-e8pzjjhf.js +5 -0
- package/dist/chunks/{studio-dgk4dqtx.js → studio-f8kz3krj.js} +5 -5
- package/dist/chunks/{studio-qgh13jtz.js → studio-fqs1a22a.js} +4 -4
- package/dist/chunks/{studio-syreb644.js → studio-fw3jp9m7.js} +4 -4
- package/dist/chunks/{studio-ed3xx40h.js → studio-g6ysm05p.js} +6 -6
- package/dist/chunks/studio-gmhd0j3f.js +5 -0
- package/dist/chunks/{studio-p4nrkvqr.js → studio-gqpty626.js} +4 -4
- package/dist/chunks/{studio-kt3pjkwd.js → studio-grp684wx.js} +4 -4
- package/dist/chunks/{studio-df0baea9.js → studio-h98520qr.js} +4 -4
- package/dist/chunks/{studio-8m0y2qe7.js → studio-hnx1h1pr.js} +4 -4
- package/dist/chunks/studio-hvxt8z79.js +5 -0
- package/dist/chunks/{studio-43sd2mam.js → studio-hw60n5dh.js} +3 -3
- package/dist/chunks/{studio-wbcjbhy1.js → studio-hxq1ebp0.js} +6 -6
- package/dist/chunks/{studio-wys23mxz.js → studio-j4e627w1.js} +4 -4
- package/dist/chunks/{studio-ccv2svc6.js → studio-j5jqvyn3.js} +4 -4
- package/dist/chunks/{studio-881z1xrx.js → studio-jqrg9xxg.js} +4 -4
- package/dist/chunks/{studio-b7y5mr30.js → studio-kdseew9m.js} +4 -4
- package/dist/chunks/studio-kt2j51sa.js +5 -0
- package/dist/chunks/{studio-d5grtb9e.js → studio-m4w6sygj.js} +9 -9
- package/dist/chunks/{studio-zz9qe3rw.js → studio-mmqr9awv.js} +37 -37
- package/dist/chunks/{studio-5g0rprdt.js → studio-nm9qev4p.js} +5 -5
- package/dist/chunks/{studio-tft6r105.js → studio-nsv7g1qn.js} +4 -4
- package/dist/chunks/{studio-3e54qh39.js → studio-p89w12s2.js} +5 -5
- package/dist/chunks/{studio-y3ahhdpx.js → studio-pbagym4j.js} +4 -4
- package/dist/chunks/{studio-w4rz9hdk.js → studio-pr23qhrw.js} +4 -4
- package/dist/chunks/{studio-6m380wp7.js → studio-r4ya3sb2.js} +4 -4
- package/dist/chunks/studio-rdnezzkd.js +6 -0
- package/dist/chunks/{studio-f14e1zdj.js → studio-rm9m3z60.js} +4 -4
- package/dist/chunks/studio-tpmnbgmt.js +5 -0
- package/dist/chunks/studio-ttxhq5yt.js +5 -0
- package/dist/chunks/{studio-nh6d342n.js → studio-txkx6586.js} +6 -6
- package/dist/chunks/{studio-a2bvaptz.js → studio-v0gsjav0.js} +4 -4
- package/dist/chunks/{studio-fbhxxc5f.js → studio-v2ek69cb.js} +4 -4
- package/dist/chunks/{studio-y0n7yxea.js → studio-wcakp5bt.js} +7 -7
- package/dist/chunks/{studio-k7evxqh4.js → studio-x3ge4rts.js} +4 -4
- package/dist/chunks/{studio-pbxa5fpc.js → studio-xddgbwhe.js} +3 -3
- package/dist/chunks/{studio-c05wdnt3.js → studio-y1x65h5r.js} +4 -4
- package/dist/chunks/{studio-rthyg42z.js → studio-z42kysg7.js} +169 -169
- package/dist/chunks/studio-zf188hnd.js +5 -0
- package/dist/chunks/{studio-tfkznv39.js → studio-zht0x0v3.js} +4 -4
- package/dist/chunks/{studio-svb7yyve.js → studio-zm7dnr9g.js} +4 -4
- package/dist/chunks/{tooltip-yypz28gq.js → tooltip-dj1pvgz7.js} +2 -2
- package/dist/chunks/{tree-9xa4hm7n.js → tree-j0ebwr5s.js} +2 -2
- package/dist/chunks/{tsMode-epyfz1hv.js → tsMode-vra2hh7y.js} +5 -5
- package/dist/chunks/workspace-3nt93k48.js +4 -0
- package/dist/chunks/workspace-home-zbgyqqws.js +4 -0
- package/dist/chunks/{ws-client-bv552sfx.js → ws-client-72x5n99j.js} +3 -3
- package/dist/iframe-entry.js +14 -14
- package/dist/manifest.json +75 -75
- package/dist/studio.js +80 -80
- package/package.json +11 -11
- package/src/browse/component-examples.ts +15 -224
- package/src/browse/library-preview.ts +6 -13
- package/src/browse/preview-root.ts +7 -4
- package/src/canvas/canvas-utils.ts +18 -4
- package/src/canvas/iframe-editable-root.ts +13 -4
- package/src/canvas/iframe-entry.ts +3 -1
- package/src/canvas/iframe-interaction.ts +11 -2
- package/src/canvas/iframe-render.ts +25 -3
- package/src/canvas/preview-placeholder.ts +120 -0
- package/src/editor/shortcuts.ts +3 -0
- package/src/panels/component-preview.ts +5 -0
- package/src/panels/pane-context.ts +43 -18
- package/src/panels/site-model.ts +7 -19
- package/src/panels/site-panel.ts +2 -25
- package/src/panels/stylebook-doc.ts +2 -53
- package/src/services/references.ts +0 -0
- package/src/style/stylebook-specimens.ts +11 -15
- package/src/surfaces/pane-context.json +286 -265
- package/src/surfaces/pane-context.ts +17 -8
- package/src/tabs/tab.ts +5 -6
- package/src/utils/geometry.ts +49 -2
- package/dist/chunks/adapter-5tphg7xe.js +0 -4
- package/dist/chunks/cf-settings-h2wrh6hq.js +0 -4
- package/dist/chunks/color-m7r1vk54.js +0 -4
- package/dist/chunks/components-r22xca09.js +0 -4
- package/dist/chunks/content-source-mmppmcyg.js +0 -4
- package/dist/chunks/editor-ccd9jwsa.js +0 -4
- package/dist/chunks/entry-commands-6qxz9e8p.js +0 -4
- package/dist/chunks/file-ops-qnab36ek.js +0 -4
- package/dist/chunks/files-rcn56qvm.js +0 -4
- package/dist/chunks/format-host-yxz0kjjs.js +0 -4
- package/dist/chunks/fs-events-d1qxq4z4.js +0 -4
- package/dist/chunks/grid-open-4z1g062t.js +0 -4
- package/dist/chunks/iframe-host-ssv7kyj0.js +0 -4
- package/dist/chunks/index-f0jn7nnp.js +0 -4
- package/dist/chunks/jx-validate-ttasrbh7.js +0 -4
- package/dist/chunks/layers-bgyr11tr.js +0 -4
- package/dist/chunks/layers-panel-7ez66zeq.js +0 -4
- package/dist/chunks/lit-html-v9gh687x.js +0 -4
- package/dist/chunks/media-library-sq468wts.js +0 -4
- package/dist/chunks/media-picker-shfe59pt.js +0 -4
- package/dist/chunks/monaco-buffer-3afgwvbp.js +0 -4
- package/dist/chunks/notify-kzrjr3fv.js +0 -4
- package/dist/chunks/platform-7v4rtd7m.js +0 -4
- package/dist/chunks/publish-panel-2z9gw8ee.js +0 -4
- package/dist/chunks/publish-workspace-wrc1dgwx.js +0 -4
- package/dist/chunks/quick-search-n0t0eqxb.js +0 -4
- package/dist/chunks/references-9547rg5s.js +0 -4
- package/dist/chunks/right-panel-wsgnr9pn.js +0 -4
- package/dist/chunks/source-control-kwktr9y6.js +0 -4
- package/dist/chunks/studio-07rcfje3.js +0 -5
- package/dist/chunks/studio-3exxhz5g.js +0 -5
- package/dist/chunks/studio-3qeh9t0h.js +0 -6
- package/dist/chunks/studio-58xg6j2q.js +0 -9
- package/dist/chunks/studio-80jyh1xh.js +0 -4
- package/dist/chunks/studio-8vcvfs7r.js +0 -6
- package/dist/chunks/studio-fyh0jc96.js +0 -5
- package/dist/chunks/studio-gcxk20n7.js +0 -5
- package/dist/chunks/studio-m8g16bgz.js +0 -5
- package/dist/chunks/studio-mx1smyq0.js +0 -5
- package/dist/chunks/studio-pgmvdv7z.js +0 -7
- package/dist/chunks/studio-ph84avkf.js +0 -5
- package/dist/chunks/studio-rj9eamf8.js +0 -5
- package/dist/chunks/studio-rptgsk96.js +0 -5
- package/dist/chunks/studio-s19tp1xr.js +0 -5
- package/dist/chunks/studio-tf9jryb1.js +0 -10
- package/dist/chunks/studio-txf2kysx.js +0 -5
- package/dist/chunks/studio-v8867ddv.js +0 -12
- package/dist/chunks/studio-vxq7fcvv.js +0 -5
- package/dist/chunks/studio-wc649wss.js +0 -5
- package/dist/chunks/studio-z97c48fk.js +0 -5
- package/dist/chunks/studio-zat62m0c.js +0 -5
- package/dist/chunks/studio-zcd2e7yz.js +0 -5
- package/dist/chunks/workspace-dz7nxfhv.js +0 -4
- package/dist/chunks/workspace-home-xsd7hkjw.js +0 -4
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* A stand-in picture for an image a component preview has no source for.
|
|
4
|
+
*
|
|
5
|
+
* Component previews draw a definition with only its own declared `examples` (`browse/
|
|
6
|
+
* component-examples.ts`), never with data borrowed from the project. A card component whose image
|
|
7
|
+
* prop defaults to `""` therefore drew a broken-image glyph — or, worse, an `<img src="">`, which
|
|
8
|
+
* resolves to the document's own url and fails as an image. Previews show the component's SHAPE, so
|
|
9
|
+
* an empty image is drawn as a neutral placeholder of a typical photo's proportions instead.
|
|
10
|
+
*
|
|
11
|
+
* Used by the two surfaces that draw components on their own: the parent-realm previews
|
|
12
|
+
* (`panels/component-preview.ts`: the Library, the Site panel's component cards, the palette) and
|
|
13
|
+
* the Stylebook's specimen render in the canvas frame (`canvas/iframe-render.ts`). Never the page
|
|
14
|
+
* canvas: an empty image on a page is something its author should see.
|
|
15
|
+
*
|
|
16
|
+
* Plain DOM with no realm-specific imports, so the frame and the editor share one definition.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A neutral landscape glyph: the canvas's one structural gray (`#808080`, as `STYLEBOOK_CSS` uses
|
|
21
|
+
* it) at low opacity, so it reads as a quiet tint on a light card and a dark one alike. A `data:`
|
|
22
|
+
* image is a document of its own, which no theme token or custom property can reach into.
|
|
23
|
+
*/
|
|
24
|
+
export const PREVIEW_IMAGE_PLACEHOLDER = `data:image/svg+xml,${encodeURIComponent(
|
|
25
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" viewBox="0 0 320 180">' +
|
|
26
|
+
'<rect width="320" height="180" fill="#808080" fill-opacity="0.16"/>' +
|
|
27
|
+
'<g fill="#808080" fill-opacity="0.38">' +
|
|
28
|
+
'<circle cx="118" cy="64" r="14"/>' +
|
|
29
|
+
'<path d="M60 140l52-50 34 30 38-46 76 66z"/>' +
|
|
30
|
+
"</g></svg>",
|
|
31
|
+
)}`;
|
|
32
|
+
|
|
33
|
+
/** Marks an `<img>` whose source is the placeholder, so a later real source is told apart. */
|
|
34
|
+
export const PLACEHOLDER_ATTR = "data-jx-image-placeholder";
|
|
35
|
+
|
|
36
|
+
/** Whether an image names no source at all: a blank `src` (or none) and a blank `srcset`. */
|
|
37
|
+
export function hasNoImageSource(img: HTMLImageElement): boolean {
|
|
38
|
+
const src = img.getAttribute("src")?.trim() ?? "";
|
|
39
|
+
const srcset = img.getAttribute("srcset")?.trim() ?? "";
|
|
40
|
+
if (src !== "" || srcset !== "") {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// A <picture>'s <source srcset> picks the image even when the <img> itself names none.
|
|
44
|
+
const picture = img.parentElement?.localName === "picture" ? img.parentElement : null;
|
|
45
|
+
return !picture?.querySelector("source[srcset]:not([srcset=''])");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Give one image the placeholder when it has no source; clear the mark once it has a real one. */
|
|
49
|
+
function fill(img: HTMLImageElement): void {
|
|
50
|
+
if (hasNoImageSource(img)) {
|
|
51
|
+
img.setAttribute(PLACEHOLDER_ATTR, "");
|
|
52
|
+
img.setAttribute("src", PREVIEW_IMAGE_PLACEHOLDER);
|
|
53
|
+
} else if (
|
|
54
|
+
img.hasAttribute(PLACEHOLDER_ATTR) &&
|
|
55
|
+
img.getAttribute("src") !== PREVIEW_IMAGE_PLACEHOLDER
|
|
56
|
+
) {
|
|
57
|
+
img.removeAttribute(PLACEHOLDER_ATTR);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether an element is an `<img>`. By tag name rather than `instanceof`: the Stylebook's frame is
|
|
63
|
+
* another realm, whose `HTMLImageElement` is not this module's.
|
|
64
|
+
*/
|
|
65
|
+
function isImage(element: Element): element is HTMLImageElement {
|
|
66
|
+
return element.localName === "img";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Every `<img>` at or under `node`. */
|
|
70
|
+
function imagesIn(node: Node): HTMLImageElement[] {
|
|
71
|
+
if (node.nodeType !== node.ELEMENT_NODE || !("querySelectorAll" in node)) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
// SAFETY: an ELEMENT_NODE with `querySelectorAll` is an Element, in whichever realm it lives.
|
|
75
|
+
const element = node as Element;
|
|
76
|
+
const found = [...element.querySelectorAll("img")];
|
|
77
|
+
return isImage(element) ? [element, ...found] : found;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Fill every source-less image under `root` with {@link PREVIEW_IMAGE_PLACEHOLDER}, now and as the
|
|
82
|
+
* tree changes: a component draws its own children in its `connectedCallback`, after the preview
|
|
83
|
+
* element is created, and a binding can clear an image's source later. A real source always wins —
|
|
84
|
+
* the placeholder only ever replaces nothing. Returns a function that stops watching.
|
|
85
|
+
*/
|
|
86
|
+
export function fillPreviewImages(root: Element): () => void {
|
|
87
|
+
for (const img of imagesIn(root)) {
|
|
88
|
+
fill(img);
|
|
89
|
+
}
|
|
90
|
+
/* The root's own realm's observer where it has one (the Stylebook's frame); this realm's for a
|
|
91
|
+
document with no window, such as a parsed one — an observer watches any same-origin node. */
|
|
92
|
+
const Observer = root.ownerDocument.defaultView?.MutationObserver ?? MutationObserver;
|
|
93
|
+
const observer = new Observer((records) => {
|
|
94
|
+
for (const record of records) {
|
|
95
|
+
if (record.type === "attributes") {
|
|
96
|
+
const { target } = record;
|
|
97
|
+
if (target.nodeType === target.ELEMENT_NODE && "localName" in target) {
|
|
98
|
+
// SAFETY: an ELEMENT_NODE target with `localName` is an Element (attributes only land on elements).
|
|
99
|
+
const element = target as Element;
|
|
100
|
+
if (isImage(element)) {
|
|
101
|
+
fill(element);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
for (const added of record.addedNodes) {
|
|
107
|
+
for (const img of imagesIn(added)) {
|
|
108
|
+
fill(img);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
observer.observe(root, {
|
|
114
|
+
attributeFilter: ["src", "srcset"],
|
|
115
|
+
attributes: true,
|
|
116
|
+
childList: true,
|
|
117
|
+
subtree: true,
|
|
118
|
+
});
|
|
119
|
+
return () => observer.disconnect();
|
|
120
|
+
}
|
package/src/editor/shortcuts.ts
CHANGED
|
@@ -1010,6 +1010,9 @@ const SELF_SCROLLING_MODES: ReadonlySet<string> = new Set([
|
|
|
1010
1010
|
"media-home",
|
|
1011
1011
|
"preview",
|
|
1012
1012
|
"publish-home",
|
|
1013
|
+
/* The Styles workspace's Variables view — `style/variables-view.ts`. An `overflow-y: auto`
|
|
1014
|
+
column over the stage; without it here the wheel panned an artboard that does not exist. */
|
|
1015
|
+
"tokens",
|
|
1013
1016
|
]);
|
|
1014
1017
|
|
|
1015
1018
|
/**
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { defineElement, setSkipServerFunctions } from "@jxsuite/runtime";
|
|
10
10
|
import { projectState } from "../store";
|
|
11
|
+
import { fillPreviewImages } from "../canvas/preview-placeholder";
|
|
11
12
|
import type { ComponentEntry } from "../files/components";
|
|
12
13
|
import type { JsonValue } from "@jxsuite/schema/types";
|
|
13
14
|
|
|
@@ -43,6 +44,10 @@ export async function renderComponentPreview(
|
|
|
43
44
|
for (const [name, value] of props) {
|
|
44
45
|
properties[name] = value;
|
|
45
46
|
}
|
|
47
|
+
/* Watched from now on: the instance draws its children when it connects, after this returns.
|
|
48
|
+
A preview shows the component's shape, so an image it has no source for is a placeholder,
|
|
49
|
+
not a broken glyph (`canvas/preview-placeholder.ts`). */
|
|
50
|
+
fillPreviewImages(el);
|
|
46
51
|
return el;
|
|
47
52
|
} catch (error) {
|
|
48
53
|
console.warn("Component preview failed:", comp.tagName, error);
|
|
@@ -105,6 +105,7 @@ import { componentPropEntries, isComponentDoc } from "../component-props";
|
|
|
105
105
|
import { mediaDisplayName } from "./shared";
|
|
106
106
|
import type { CanvasView, FitMode } from "../canvas/canvas-utils";
|
|
107
107
|
import { EDITOR_KIND_LABELS } from "../commands/context";
|
|
108
|
+
import type { EditorKind } from "../commands/context";
|
|
108
109
|
import type { ParamValues } from "../page-params";
|
|
109
110
|
import type { CommandArgValues } from "../commands/command-args";
|
|
110
111
|
import type { Tab } from "../tabs/tab";
|
|
@@ -781,22 +782,40 @@ function lensSummary(paneId: string): string {
|
|
|
781
782
|
|
|
782
783
|
// ─── Axis 1 · Editor kind ────────────────────────────────────────────────────
|
|
783
784
|
|
|
785
|
+
/**
|
|
786
|
+
* Editor kinds that are entered by a gesture elsewhere and never offered as a choice.
|
|
787
|
+
*
|
|
788
|
+
* A comparison (`diff`) is opened from Publish's changed files or its version history, which says
|
|
789
|
+
* what it is compared against; a Diff segment on every document would compare whatever is open
|
|
790
|
+
* against whatever HEAD is, and was a third way into a view two browsers already own. While a tab
|
|
791
|
+
* is IN one, the axis names it and offers nothing: the way out is Back, beside it.
|
|
792
|
+
*/
|
|
793
|
+
const IMPLICIT_EDITOR_KINDS: ReadonlySet<EditorKind> = new Set(["diff"]);
|
|
794
|
+
|
|
784
795
|
function editorAxis(tab: Tab): Partial<PaneContextView> {
|
|
785
796
|
/* What the DOCUMENT declares, and nothing else. This read `hostableKindsOf` — the declared kinds
|
|
786
797
|
narrowed by what the tab's pane was allowed to host — so a page in the side pane was offered
|
|
787
798
|
Code and not Design. The pane cap is gone; a control that cannot go anywhere is still the
|
|
788
799
|
defect this axis exists to remove, and one declared kind still renders as text. */
|
|
789
|
-
const kinds = editorKindsOf(tab);
|
|
790
800
|
const current = editorKindOf(tab);
|
|
801
|
+
const kinds = IMPLICIT_EDITOR_KINDS.has(current)
|
|
802
|
+
? [current]
|
|
803
|
+
: editorKindsOf(tab).filter((kind) => !IMPLICIT_EDITOR_KINDS.has(kind));
|
|
791
804
|
if (kinds.length < 2) {
|
|
792
|
-
// One kind is not a choice. Rendering it as a
|
|
805
|
+
// One kind is not a choice. Rendering it as a segmented group would be a control that cannot move —
|
|
793
806
|
// Which is the defect this axis exists to remove, in miniature.
|
|
794
|
-
return { editorLabel: EDITOR_KIND_LABELS[current], editorMode: "static" };
|
|
807
|
+
return { editorLabel: EDITOR_KIND_LABELS[current], editorMode: "static", editorState: "shown" };
|
|
795
808
|
}
|
|
796
809
|
return {
|
|
797
810
|
editorMode: "picker",
|
|
798
|
-
|
|
799
|
-
|
|
811
|
+
editorState: "shown",
|
|
812
|
+
editors: kinds.map((kind) => ({
|
|
813
|
+
checked: kind === current ? "true" : "false",
|
|
814
|
+
hint: `Open this document in ${EDITOR_KIND_LABELS[kind]}`,
|
|
815
|
+
key: kind,
|
|
816
|
+
label: EDITOR_KIND_LABELS[kind],
|
|
817
|
+
value: kind,
|
|
818
|
+
})),
|
|
800
819
|
};
|
|
801
820
|
}
|
|
802
821
|
|
|
@@ -895,25 +914,26 @@ function contextAxis(tab: Tab, paneId: string, ctx: PaneContextCtx): Partial<Pan
|
|
|
895
914
|
const plainQueries = featureQueries.filter(({ query }) => !isSchemeQuery(query));
|
|
896
915
|
const scheme = ui.previewColorScheme;
|
|
897
916
|
const { activeMedia } = ui;
|
|
898
|
-
const sizeLabel = activeMedia ? mediaDisplayName(activeMedia) : "Base";
|
|
899
|
-
const schemeLabel = SCHEMES.find(([value]) => value === scheme)?.[1] ?? "Auto";
|
|
900
917
|
const locale = localeOf(tab);
|
|
901
|
-
/* The language joins the trigger ONLY when the pane is not drawing the document's own — a French
|
|
902
|
-
page open in a French pane is not a rendering context worth three more characters, and a bar
|
|
903
|
-
that grew a third term in every multilingual project would stop reading as a state. */
|
|
904
|
-
const contextSummary = [
|
|
905
|
-
sizeLabel,
|
|
906
|
-
schemeQueries.length > 0 ? schemeLabel : null,
|
|
907
|
-
locale?.overridden ? localeLabel(locale.effective) : null,
|
|
908
|
-
]
|
|
909
|
-
.filter((part) => part !== null)
|
|
910
|
-
.join(" · ");
|
|
911
918
|
/* An entry written on the canvas is wrapped in its template's layout like a page (§22.5), so it
|
|
912
919
|
offers the same "show layout" choice. Whether a layout resolves for it is read asynchronously
|
|
913
920
|
by the canvas; the project default is the answer this bar can give synchronously. */
|
|
914
921
|
const hasLayout =
|
|
915
922
|
(isPageDoc(tab) && Boolean(getEffectiveLayoutPath(tab.doc.document.$layout))) ||
|
|
916
923
|
(Boolean(projectState?.isSiteProject) && writesEntryOnCanvas(tab.documentPath));
|
|
924
|
+
/* The trigger names only what its popover holds and is NOT at rest. Size and scheme are on the bar
|
|
925
|
+
as groups now, so repeating them here would print the same state twice; and a trigger that read
|
|
926
|
+
"Base · Auto" over a popover that no longer offers either would be describing another control.
|
|
927
|
+
The language joins ONLY when the pane is not drawing the document's own — a French page open in
|
|
928
|
+
a French pane is not a rendering context worth three more characters. */
|
|
929
|
+
const featuresOn = plainQueries.filter(({ name }) => ui.featureToggles[name]).length;
|
|
930
|
+
const contextSummary = [
|
|
931
|
+
locale?.overridden ? localeLabel(locale.effective) : null,
|
|
932
|
+
featuresOn > 0 ? `${featuresOn} ${featuresOn === 1 ? "query" : "queries"}` : null,
|
|
933
|
+
hasLayout && ui.showLayout === false ? "No layout" : null,
|
|
934
|
+
]
|
|
935
|
+
.filter((part) => part !== null)
|
|
936
|
+
.join(" · ");
|
|
917
937
|
const page = isPageDoc(tab);
|
|
918
938
|
const params = page ? paramRows(tab) : [];
|
|
919
939
|
const props = page ? [] : propRows(tab, paneId);
|
|
@@ -952,6 +972,8 @@ function contextAxis(tab: Tab, paneId: string, ctx: PaneContextCtx): Partial<Pan
|
|
|
952
972
|
})) satisfies PaneContextChoice[],
|
|
953
973
|
schemeState: schemeQueries.length > 0 ? "shown" : "hidden",
|
|
954
974
|
sizes: sizeRows(sizeBreakpoints, activeMedia),
|
|
975
|
+
// Base alone is not a choice: a one-segment group would be a control that cannot move.
|
|
976
|
+
sizesState: sizeBreakpoints.length > 0 ? "shown" : "hidden",
|
|
955
977
|
};
|
|
956
978
|
}
|
|
957
979
|
|
|
@@ -1134,7 +1156,10 @@ function actionsFor(paneId: string, ctx: PaneContextCtx): PaneContextActions {
|
|
|
1134
1156
|
if (!tab) {
|
|
1135
1157
|
return;
|
|
1136
1158
|
}
|
|
1137
|
-
|
|
1159
|
+
// An implicit kind is refused like an undeclared one: the axis never offers it.
|
|
1160
|
+
const editorKind = editorKindsOf(tab).find(
|
|
1161
|
+
(candidate) => candidate === kind && !IMPLICIT_EDITOR_KINDS.has(candidate),
|
|
1162
|
+
);
|
|
1138
1163
|
const mode = editorKind && modeForEditorKind(tab, editorKind);
|
|
1139
1164
|
if (!mode) {
|
|
1140
1165
|
return;
|
package/src/panels/site-model.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The panel lists the project as an author thinks of it: pages by address, then the templates,
|
|
5
5
|
* components and media pages are made of. Everything here turns facts the flow has already gathered
|
|
6
|
-
* (paths, `$paths.contentType`, entry counts
|
|
7
|
-
*
|
|
6
|
+
* (paths, `$paths.contentType`, entry counts) into the rows the surface draws, so the ordering, the
|
|
7
|
+
* nesting, the labels and the filter are testable without a project.
|
|
8
8
|
*
|
|
9
9
|
* **Routes come from the router.** Rows are ordered and labelled with `fileToRoute` and
|
|
10
10
|
* `compareRoutes` from `@jxsuite/site/routes` — the functions the build's page discovery sorts with
|
|
@@ -64,7 +64,7 @@ export interface SiteTemplateRow {
|
|
|
64
64
|
key: string;
|
|
65
65
|
path: string;
|
|
66
66
|
label: string;
|
|
67
|
-
/** "
|
|
67
|
+
/** "default layout" for the project's `defaults.layout`, else empty. */
|
|
68
68
|
detail: string;
|
|
69
69
|
selected: SiteSelected;
|
|
70
70
|
}
|
|
@@ -320,28 +320,16 @@ export function filterPageRows(rows: readonly SitePageRow[], needle: string): Si
|
|
|
320
320
|
return kept;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
/** "used by 3 pages", "used by 1 page", "not used yet", or "" when the count is unknown. */
|
|
324
|
-
export function usedByLabel(pages: number | null): string {
|
|
325
|
-
if (pages === null) {
|
|
326
|
-
return "";
|
|
327
|
-
}
|
|
328
|
-
if (pages === 0) {
|
|
329
|
-
return "not used yet";
|
|
330
|
-
}
|
|
331
|
-
return `used by ${pages} ${pages === 1 ? "page" : "pages"}`;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
323
|
/**
|
|
335
324
|
* The Templates section's rows: every JSON layout under `layouts/`, by path. Layouts are JSON only
|
|
336
325
|
* (the layout resolver parses nothing else), so a non-JSON file there is not a template.
|
|
337
326
|
*
|
|
338
|
-
* The project's default layout (`defaults.layout`) says so
|
|
339
|
-
*
|
|
340
|
-
*
|
|
327
|
+
* The project's default layout (`defaults.layout`) says so. No row says how many pages use it:
|
|
328
|
+
* counting would ask the reference index once per template on every paint, which is a project-wide
|
|
329
|
+
* sweep per row (`specs/studio.md` §5.7).
|
|
341
330
|
*/
|
|
342
331
|
export function templateRows(
|
|
343
332
|
paths: readonly string[],
|
|
344
|
-
usedBy: (path: string) => number | null,
|
|
345
333
|
activePath: string | null,
|
|
346
334
|
needle: string,
|
|
347
335
|
defaultPath: string | null = null,
|
|
@@ -356,7 +344,7 @@ export function templateRows(
|
|
|
356
344
|
}
|
|
357
345
|
return [
|
|
358
346
|
{
|
|
359
|
-
detail: path === defaultPath ? "default layout" :
|
|
347
|
+
detail: path === defaultPath ? "default layout" : "",
|
|
360
348
|
key: path,
|
|
361
349
|
label,
|
|
362
350
|
path,
|
package/src/panels/site-panel.ts
CHANGED
|
@@ -48,7 +48,6 @@ import { previewFileSrc } from "../files/media-paths";
|
|
|
48
48
|
import { elementForAsset } from "../editor/file-drop-action";
|
|
49
49
|
import { readPageFacts } from "../content/collection-pages";
|
|
50
50
|
import { listCollectionEntryIds } from "../grid/sources/content-source";
|
|
51
|
-
import { loadUsages, onUsagesInvalidated, peekUsages, splitUsages } from "../services/references";
|
|
52
51
|
import { projectMediaFiles } from "../ui/media-picker";
|
|
53
52
|
import { runActiveReported } from "../commands/run-reported";
|
|
54
53
|
import { emptySitePanelValues, mountSitePanelSurface } from "../surfaces/site-panel";
|
|
@@ -312,16 +311,6 @@ function defaultLayoutPath(): string | null {
|
|
|
312
311
|
return isString(layout) ? layout.replace(/^\.\//, "") : null;
|
|
313
312
|
}
|
|
314
313
|
|
|
315
|
-
/** How many pages use a template, from the reference index — `null` where it cannot say yet. */
|
|
316
|
-
function templateUsage(path: string): number | null {
|
|
317
|
-
const state = peekUsages({ path });
|
|
318
|
-
if (state === null) {
|
|
319
|
-
void loadUsages({ path }).then(repaint);
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
return state.status === "ready" ? splitUsages(state.result).pages : null;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
314
|
// ─── The view ────────────────────────────────────────────────────────────────
|
|
326
315
|
|
|
327
316
|
/** The filter, as typed. One per window: the panel is project-level and there is one project. */
|
|
@@ -366,12 +355,8 @@ export function sitePanelValues(): SitePanelValues {
|
|
|
366
355
|
: [];
|
|
367
356
|
const pages = filterPageRows(allPages, needle);
|
|
368
357
|
const defaultLayout = defaultLayoutPath();
|
|
369
|
-
const allTemplates = index
|
|
370
|
-
|
|
371
|
-
: [];
|
|
372
|
-
const templates = index
|
|
373
|
-
? templateRows(index.layouts, templateUsage, activePath, needle, defaultLayout)
|
|
374
|
-
: [];
|
|
358
|
+
const allTemplates = index ? templateRows(index.layouts, activePath, "", defaultLayout) : [];
|
|
359
|
+
const templates = index ? templateRows(index.layouts, activePath, needle, defaultLayout) : [];
|
|
375
360
|
const registryLoading = !_componentRegistryLoaded;
|
|
376
361
|
const allComponents = componentRows("", activePath);
|
|
377
362
|
const components = componentRows(needle, activePath);
|
|
@@ -537,9 +522,6 @@ export function mediaDragData(path: string): BlockDragData | null {
|
|
|
537
522
|
|
|
538
523
|
/** Mount the surface into `host`, or bring the one already there up to date. */
|
|
539
524
|
function mountInto(host: HTMLElement): void {
|
|
540
|
-
/* A page created, saved or deleted changes how many pages use a template, and the usage cache is
|
|
541
|
-
dropped for it: the Templates rows ask again on the repaint this asks for. */
|
|
542
|
-
_usagesUnlisten ??= onUsagesInvalidated(repaint);
|
|
543
525
|
if (_standing && (_standing.host !== host || _standing.handle?.connected() === false)) {
|
|
544
526
|
dispose(_standing);
|
|
545
527
|
_standing = null;
|
|
@@ -585,9 +567,6 @@ function mountInto(host: HTMLElement): void {
|
|
|
585
567
|
});
|
|
586
568
|
}
|
|
587
569
|
|
|
588
|
-
/** The usage-invalidation subscription, taken once the panel first mounts. */
|
|
589
|
-
let _usagesUnlisten: (() => void) | null = null;
|
|
590
|
-
|
|
591
570
|
/** Repaint the Navigator, which repaints this panel when it is the one showing. */
|
|
592
571
|
function repaint(): void {
|
|
593
572
|
renderOnly("leftPanel");
|
|
@@ -625,8 +604,6 @@ export function resetSitePanel(): void {
|
|
|
625
604
|
dispose(_standing);
|
|
626
605
|
}
|
|
627
606
|
_standing = null;
|
|
628
|
-
_usagesUnlisten?.();
|
|
629
|
-
_usagesUnlisten = null;
|
|
630
607
|
_filter = "";
|
|
631
608
|
_epoch += 1;
|
|
632
609
|
_index = null;
|
|
@@ -21,15 +21,9 @@ import { isKeyframesAtRule } from "@jxsuite/runtime/css";
|
|
|
21
21
|
import type { StylebookEntry } from "./stylebook-panel";
|
|
22
22
|
import type { ComponentEntry } from "../files/components";
|
|
23
23
|
import type { JxPath } from "../state";
|
|
24
|
-
import { isNonNullObject
|
|
24
|
+
import { isNonNullObject } from "@jxsuite/schema/guards";
|
|
25
25
|
import type { StylebookSpecimens } from "../style/stylebook-specimens";
|
|
26
|
-
import type {
|
|
27
|
-
JxDocument,
|
|
28
|
-
JxHeadEntry,
|
|
29
|
-
JxMutableNode,
|
|
30
|
-
JxStateDefinition,
|
|
31
|
-
JxStyle,
|
|
32
|
-
} from "@jxsuite/schema/types";
|
|
26
|
+
import type { JxHeadEntry, JxMutableNode, JxStyle } from "@jxsuite/schema/types";
|
|
33
27
|
|
|
34
28
|
export interface StylebookDocResult {
|
|
35
29
|
/** The specimen document (plain JSON — safe to post over the bridge). */
|
|
@@ -210,40 +204,6 @@ export function catalogueRows(
|
|
|
210
204
|
return out;
|
|
211
205
|
}
|
|
212
206
|
|
|
213
|
-
/**
|
|
214
|
-
* A card's specimen for a component drawn as the first row of a list
|
|
215
|
-
* (`style/stylebook-specimens.ts`): the list's repeater, reading its row from `rootState[key]` so
|
|
216
|
-
* two cards' lists cannot collide, and the rest of the page state the row was drawn in, the first
|
|
217
|
-
* card's value winning a shared name (`$page`, `$site`). Null when the sample is not a repeater
|
|
218
|
-
* over a state list.
|
|
219
|
-
*/
|
|
220
|
-
export function sampleRowSpecimen(
|
|
221
|
-
sample: JxDocument,
|
|
222
|
-
key: string,
|
|
223
|
-
rootState: Record<string, JxStateDefinition>,
|
|
224
|
-
): JxMutableNode | null {
|
|
225
|
-
const [list] = Array.isArray(sample.children) ? sample.children : [];
|
|
226
|
-
if (!isNonNullObject(list) || Array.isArray(list) || !isRef(list.items)) {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
const { $ref } = list.items;
|
|
230
|
-
const listKey = $ref.startsWith("#/state/") ? $ref.slice("#/state/".length) : "";
|
|
231
|
-
const rows = sample.state?.[listKey];
|
|
232
|
-
if (!Array.isArray(rows)) {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
rootState[key] = rows;
|
|
236
|
-
for (const [name, value] of Object.entries(sample.state ?? {})) {
|
|
237
|
-
if (name !== listKey && !(name in rootState)) {
|
|
238
|
-
rootState[name] = value;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/* SAFETY: `list` is the repeater object a page's `children` held; only its `items` is replaced,
|
|
242
|
-
with a ref of the same shape. */
|
|
243
|
-
const repeater = { ...list, items: { $ref: `#/state/${key}` } } as JxMutableNode;
|
|
244
|
-
return { children: [repeater], tagName: "div" };
|
|
245
|
-
}
|
|
246
|
-
|
|
247
207
|
/** Options for {@link buildStylebookDoc}. */
|
|
248
208
|
export interface BuildStylebookDocOpts {
|
|
249
209
|
meta: { $sections: { label: string; elements: StylebookEntry[] }[] };
|
|
@@ -374,8 +334,6 @@ export function buildStylebookDoc(opts: BuildStylebookDocOpts): StylebookDocResu
|
|
|
374
334
|
|
|
375
335
|
// Custom components from the registry — live custom elements registered via $elements.
|
|
376
336
|
const $elements: (string | { $ref: string })[] = [];
|
|
377
|
-
// The data the sample-row cards read: each row's list, and the page context it was drawn in.
|
|
378
|
-
const rootState: Record<string, JxStateDefinition> = {};
|
|
379
337
|
{
|
|
380
338
|
let comps = opts.components;
|
|
381
339
|
if (filter) {
|
|
@@ -404,12 +362,6 @@ export function buildStylebookDoc(opts: BuildStylebookDocOpts): StylebookDocResu
|
|
|
404
362
|
const specimen: JxMutableNode = { $props: drawn.props, tagName: comp.tagName };
|
|
405
363
|
return cardNode(comp.tagName, `<${comp.tagName}>`, specimen, cardPath, null);
|
|
406
364
|
}
|
|
407
|
-
if (drawn?.kind === "row") {
|
|
408
|
-
const row = sampleRowSpecimen(drawn.sample.doc, `stylebookRow${ci}`, rootState);
|
|
409
|
-
if (row) {
|
|
410
|
-
return cardNode(comp.tagName, `<${comp.tagName}>`, row, cardPath, null);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
365
|
if (drawn?.kind !== "none") {
|
|
414
366
|
const attributes: Record<string, string> = {};
|
|
415
367
|
for (const p of comp.props ?? []) {
|
|
@@ -455,9 +407,6 @@ export function buildStylebookDoc(opts: BuildStylebookDocOpts): StylebookDocResu
|
|
|
455
407
|
doc.$head = opts.effectiveHead;
|
|
456
408
|
}
|
|
457
409
|
doc.$media = opts.effectiveMedia;
|
|
458
|
-
if (Object.keys(rootState).length > 0) {
|
|
459
|
-
doc.state = rootState;
|
|
460
|
-
}
|
|
461
410
|
doc.attributes = { class: "sb-root" };
|
|
462
411
|
doc.children = sections;
|
|
463
412
|
doc.style = transposeStylebookStyle(opts.effectiveStyle);
|
|
Binary file
|
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
* component is rarely drawable that way: the museum starter's `<mu-exhibition-card>` formats a
|
|
6
6
|
* `date` that defaults to `""`, so its card stayed empty and the canvas logged "was not rendered:
|
|
7
7
|
* RangeError: Invalid time value" for it on every paint (`spec.md` §16.4). The Site panel's
|
|
8
|
-
* previews already know where a component's sample props live (`browse/component-examples.ts
|
|
9
|
-
*
|
|
8
|
+
* previews already know where a component's sample props live (`browse/component-examples.ts`: the
|
|
9
|
+
* definition's own `examples`), so a card asks the same source and draws:
|
|
10
10
|
*
|
|
11
11
|
* - `props`: an instance given its sample props, when the definition renders with them;
|
|
12
|
-
* - `
|
|
13
|
-
*
|
|
14
|
-
* - `none`: no live instance at all (the catalogue's placeholder box), when it renders with
|
|
15
|
-
*
|
|
12
|
+
* - `bare`: the bare instance, when it renders with its defaults and has no sample props it renders
|
|
13
|
+
* with;
|
|
14
|
+
* - `none`: no live instance at all (the catalogue's placeholder box), when it renders with neither,
|
|
15
|
+
* because an instance that throws draws nothing and reports an error nobody can fix.
|
|
16
|
+
*
|
|
17
|
+
* Nothing here reads the rest of the project, so a catalogue of N components costs N definition
|
|
18
|
+
* reads and no usage queries.
|
|
16
19
|
*
|
|
17
20
|
* Answers are cached per component registry: the registry is replaced whenever the project's
|
|
18
21
|
* components are re-read, so a changed definition is asked again.
|
|
@@ -20,17 +23,14 @@
|
|
|
20
23
|
* @docs studio/design/stylebook
|
|
21
24
|
*/
|
|
22
25
|
|
|
23
|
-
import { componentExampleProps
|
|
24
|
-
import type { SampleRow } from "../browse/component-examples";
|
|
26
|
+
import { componentExampleProps } from "../browse/component-examples";
|
|
25
27
|
import { readDefinition, rendersWith } from "../browse/library-preview";
|
|
26
|
-
import { defineComponent } from "../panels/component-preview";
|
|
27
28
|
import type { ComponentEntry } from "../files/components";
|
|
28
29
|
import type { JsonValue } from "@jxsuite/schema/types";
|
|
29
30
|
|
|
30
31
|
/** How one component card is drawn; see the module comment. */
|
|
31
32
|
export type StylebookSpecimen =
|
|
32
33
|
| { kind: "props"; props: Record<string, JsonValue> }
|
|
33
|
-
| { kind: "row"; sample: SampleRow }
|
|
34
34
|
| { kind: "bare" }
|
|
35
35
|
| { kind: "none" };
|
|
36
36
|
|
|
@@ -49,14 +49,10 @@ export async function componentSpecimen(component: ComponentEntry): Promise<Styl
|
|
|
49
49
|
if (!definition) {
|
|
50
50
|
return { kind: "bare" };
|
|
51
51
|
}
|
|
52
|
-
const props =
|
|
52
|
+
const props = componentExampleProps(definition);
|
|
53
53
|
if (props.size > 0 && (await rendersWith(definition, path, props))) {
|
|
54
54
|
return { kind: "props", props: Object.fromEntries(props) };
|
|
55
55
|
}
|
|
56
|
-
const sample = await componentSampleRow(definition, path);
|
|
57
|
-
if (sample && (await defineComponent(component))) {
|
|
58
|
-
return { kind: "row", sample };
|
|
59
|
-
}
|
|
60
56
|
return (await rendersWith(definition, path, new Map())) ? { kind: "bare" } : { kind: "none" };
|
|
61
57
|
}
|
|
62
58
|
|