tutuca 0.9.113 → 0.9.114
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/chai.js +1180 -359
- package/dist/tutuca-cli.js +14827 -13802
- package/dist/tutuca-components.js +323 -354
- package/dist/tutuca-dev.ext.js +1955 -2033
- package/dist/tutuca-dev.js +3254 -2627
- package/dist/tutuca-dev.min.js +5 -5
- package/dist/tutuca-extra.ext.js +1044 -1222
- package/dist/tutuca-extra.js +1261 -1480
- package/dist/tutuca-extra.min.js +3 -3
- package/dist/tutuca-storybook.js +174 -123
- package/dist/tutuca.ext.js +1026 -1213
- package/dist/tutuca.js +1243 -1471
- package/dist/tutuca.min.js +3 -3
- package/package.json +24 -21
- package/skill/tutuca/cli.md +1 -1
- package/skill/tutuca/testing.md +5 -4
- package/skill/tutuca-source/tutuca.ext.js +1026 -1213
package/dist/tutuca-storybook.js
CHANGED
|
@@ -5,60 +5,88 @@ var Storybook = component({
|
|
|
5
5
|
fields: {
|
|
6
6
|
selectedSectionIndex: 0,
|
|
7
7
|
sections: [],
|
|
8
|
+
// Retained navigation tree (SidebarGroup → SidebarEntry), built once from
|
|
9
|
+
// `sections` and then mutated in place: selection flips entry `selected`, the
|
|
10
|
+
// filter walk sets entry/group `visible`, a header click flips group `collapsed`.
|
|
11
|
+
// It owns its own UI state — never rebuilt or projected from `sections`.
|
|
8
12
|
sidebar: [],
|
|
9
13
|
filter: "",
|
|
10
14
|
sectionId: null,
|
|
11
15
|
exampleId: null,
|
|
12
16
|
focusExample: null,
|
|
13
17
|
sidebarCollapsed: false,
|
|
18
|
+
// The active margaui palette, and the names the switcher offers. `themes` is
|
|
19
|
+
// seeded by mountStorybook from its `themes` option and left EMPTY when no
|
|
20
|
+
// margaui theme CSS is on the page (`--no-margaui`) — an empty list hides the
|
|
21
|
+
// switcher, so nothing offers a theme that can't load.
|
|
14
22
|
theme: "",
|
|
15
23
|
themes: []
|
|
16
24
|
},
|
|
17
25
|
statics: {
|
|
26
|
+
// Build a storybook whose sidebar tree is derived from `sections` once. Use this
|
|
27
|
+
// instead of a bare `make` anywhere a Storybook is constructed by hand (the
|
|
28
|
+
// engine itself, the getRoot/Inception demo) so the sidebar is never empty.
|
|
18
29
|
withSections(sections) {
|
|
19
30
|
return this.make({ sections, sidebar: buildSidebar(sections) });
|
|
20
31
|
}
|
|
21
32
|
},
|
|
22
33
|
methods: {
|
|
23
34
|
selectSectionAtIndex(index) {
|
|
24
|
-
if (this.sections.size === 0)
|
|
25
|
-
return this;
|
|
35
|
+
if (this.sections.size === 0) return this;
|
|
26
36
|
const safeIndex = index >= 0 && index < this.sections.size ? index : 0;
|
|
27
|
-
return this.setSelectedSectionIndex(safeIndex).markSidebarSelected(
|
|
37
|
+
return this.setSelectedSectionIndex(safeIndex).markSidebarSelected(
|
|
38
|
+
this.sections.get(safeIndex)?.id
|
|
39
|
+
);
|
|
28
40
|
},
|
|
41
|
+
// Flip the `selected` highlight to the entry for `id`, clearing the rest.
|
|
29
42
|
markSidebarSelected(id) {
|
|
30
|
-
return this.setSidebar(
|
|
43
|
+
return this.setSidebar(
|
|
44
|
+
this.sidebar.map((g) => g.setRows(g.rows.map((e) => e.setSelected(e.sectionId === id))))
|
|
45
|
+
);
|
|
31
46
|
},
|
|
47
|
+
// Walk the tree setting each row's `visible` from the filter AND the group's
|
|
48
|
+
// collapse: a row is visible iff it passes the filter (every row passes when the
|
|
49
|
+
// filter is empty) AND its group is open — collapse always wins, so a collapsed
|
|
50
|
+
// group shows no rows even while filtering. The group's own `visible` is set from
|
|
51
|
+
// whether any row MATCHES (ignoring collapse), so a collapsed group that still
|
|
52
|
+
// contains matches keeps its header and stays expandable; with no filter every
|
|
53
|
+
// group shows.
|
|
32
54
|
applyFilterToSidebar(filter) {
|
|
33
55
|
const active = (filter ?? "") !== "";
|
|
34
|
-
return this.setSidebar(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
anyMatch = true;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
return this.setSidebar(
|
|
57
|
+
this.sidebar.map((g) => {
|
|
58
|
+
let anyMatch = false;
|
|
59
|
+
const rows = g.rows.map((e) => {
|
|
60
|
+
const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
|
|
61
|
+
if (match) anyMatch = true;
|
|
62
|
+
return e.setVisible(match && !g.collapsed);
|
|
63
|
+
});
|
|
64
|
+
return g.setRows(rows).setVisible(active ? anyMatch : true);
|
|
65
|
+
})
|
|
66
|
+
);
|
|
44
67
|
},
|
|
68
|
+
// Flip a named group's `collapsed`, recomputing its rows' visibility for the new
|
|
69
|
+
// state against the current filter. Collapsing always hides the rows (even while a
|
|
70
|
+
// filter is active — the reported chevron-flips-but-content-stays case); expanding
|
|
71
|
+
// re-reveals the rows that pass the filter. The group header's own `visible` is left
|
|
72
|
+
// as-is, so a collapsed group that matched the filter stays visible to be reopened.
|
|
45
73
|
toggleSidebarGroup(name) {
|
|
46
74
|
const filter = this.filter;
|
|
47
75
|
const active = filter !== "";
|
|
48
|
-
return this.setSidebar(
|
|
49
|
-
|
|
50
|
-
return g;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
76
|
+
return this.setSidebar(
|
|
77
|
+
this.sidebar.map((g) => {
|
|
78
|
+
if (g.name !== name) return g;
|
|
79
|
+
const collapsed = !g.collapsed;
|
|
80
|
+
const rows = g.rows.map((e) => {
|
|
81
|
+
const match = !active || fuzzyMatch(filter, `${e.title} ${e.description}`);
|
|
82
|
+
return e.setVisible(match && !collapsed);
|
|
83
|
+
});
|
|
84
|
+
return g.setCollapsed(collapsed).setRows(rows);
|
|
85
|
+
})
|
|
86
|
+
);
|
|
58
87
|
},
|
|
59
88
|
selectSectionWithId(id) {
|
|
60
|
-
if (!id)
|
|
61
|
-
return this.selectSectionAtIndex(this.selectedSectionIndex);
|
|
89
|
+
if (!id) return this.selectSectionAtIndex(this.selectedSectionIndex);
|
|
62
90
|
const index = this.sections.findIndex((s) => s.id === id);
|
|
63
91
|
return this.selectSectionAtIndex(index);
|
|
64
92
|
},
|
|
@@ -74,12 +102,21 @@ var Storybook = component({
|
|
|
74
102
|
return this.setSectionId(sectionId).setExampleId(exampleId).setFocusExample(example.value);
|
|
75
103
|
},
|
|
76
104
|
setSelectedSectionFilter(value) {
|
|
77
|
-
if (this.sections.size === 0)
|
|
78
|
-
return this;
|
|
105
|
+
if (this.sections.size === 0) return this;
|
|
79
106
|
const i = this.selectedSectionIndex;
|
|
80
107
|
const sections = this.sections.map((s, idx) => idx === i ? s.setFilter(value ?? "") : s);
|
|
81
108
|
return this.setSections(sections);
|
|
82
109
|
},
|
|
110
|
+
// Assemble the full URL snapshot from current state. `overrides` carries the
|
|
111
|
+
// change the calling handler is about to make, since `this` is still the
|
|
112
|
+
// pre-change state when the persistState request is issued.
|
|
113
|
+
//
|
|
114
|
+
// `theme` is deliberately NOT part of the base snapshot: it is resolved on load
|
|
115
|
+
// (from the OS preference when the URL says nothing), and writing that resolved
|
|
116
|
+
// value back would stamp `?theme=light` onto the URL the first time anyone types
|
|
117
|
+
// in the filter. persistState only touches the keys it is handed, so omitting it
|
|
118
|
+
// here leaves an already-chosen `?theme=` alone — onSelectTheme passes it as an
|
|
119
|
+
// override, which is the only way it enters the URL.
|
|
83
120
|
toUrlState(overrides = {}) {
|
|
84
121
|
const section = this.sections.get(this.selectedSectionIndex);
|
|
85
122
|
return {
|
|
@@ -92,6 +129,8 @@ var Storybook = component({
|
|
|
92
129
|
}
|
|
93
130
|
},
|
|
94
131
|
alter: {
|
|
132
|
+
// The sidebar render-each shows only visible groups (kept positional via `when`
|
|
133
|
+
// so a click resolves to the right group — see SidebarGroup.rowVisible).
|
|
95
134
|
groupVisible(_key, group) {
|
|
96
135
|
return group.visible;
|
|
97
136
|
}
|
|
@@ -109,6 +148,10 @@ var Storybook = component({
|
|
|
109
148
|
ctx.request("persistState", [this.toUrlState({ example: "" }), this, true]);
|
|
110
149
|
return this.setSectionId(null).setExampleId(null).setFocusExample(null);
|
|
111
150
|
},
|
|
151
|
+
// Switching a palette is a DOM effect (set data-theme, load the stylesheet), so it
|
|
152
|
+
// goes out as a request — the host handler in mountStorybook owns the document.
|
|
153
|
+
// Unregistered (no `themes` option), the request no-ops via the 404 path and the
|
|
154
|
+
// switcher isn't rendered anyway.
|
|
112
155
|
onSelectTheme(value, ctx) {
|
|
113
156
|
ctx.request("applyTheme", [value, this]);
|
|
114
157
|
ctx.request("persistState", [this.toUrlState({ theme: value }), this, false]);
|
|
@@ -116,6 +159,8 @@ var Storybook = component({
|
|
|
116
159
|
}
|
|
117
160
|
},
|
|
118
161
|
bubble: {
|
|
162
|
+
// A sidebar entry was clicked; it bubbles its section id. Resolve to the content
|
|
163
|
+
// index and select. Sidebar highlight is updated by selectSectionAtIndex.
|
|
119
164
|
sectionSelected(sectionId, ctx) {
|
|
120
165
|
ctx.stopPropagation();
|
|
121
166
|
const section = this.sections.find((s) => s.id === sectionId);
|
|
@@ -129,6 +174,7 @@ var Storybook = component({
|
|
|
129
174
|
transitionSections(ctx, next, oldIndex, next.selectedSectionIndex);
|
|
130
175
|
return next;
|
|
131
176
|
},
|
|
177
|
+
// Toggle a group open/closed in place — the collapse lives on the SidebarGroup.
|
|
132
178
|
groupToggled(name, ctx) {
|
|
133
179
|
ctx.stopPropagation();
|
|
134
180
|
return this.toggleSidebarGroup(name);
|
|
@@ -154,8 +200,7 @@ var Storybook = component({
|
|
|
154
200
|
},
|
|
155
201
|
response: {
|
|
156
202
|
loadState(state, err, ctx) {
|
|
157
|
-
if (err || !state)
|
|
158
|
-
return this;
|
|
203
|
+
if (err || !state) return this;
|
|
159
204
|
const selected = this.selectSectionWithId(state.section).setTheme(state.theme ?? "").setFilter(state.sectionFilter ?? "").applyFilterToSidebar(state.sectionFilter ?? "").setSelectedSectionFilter(state.exampleFilter ?? "");
|
|
160
205
|
const next = state.example ? selected.focusExampleByIds(state.section, state.example) : selected.setSectionId(null).setExampleId(null).setFocusExample(null);
|
|
161
206
|
transitionSections(ctx, next, this.selectedSectionIndex, next.selectedSectionIndex);
|
|
@@ -228,6 +273,9 @@ var Section = component({
|
|
|
228
273
|
id: "?",
|
|
229
274
|
title: "No Title Section",
|
|
230
275
|
description: "",
|
|
276
|
+
// Optional 2-level grouping: sections sharing a `group` name cluster under one
|
|
277
|
+
// collapsible sidebar header. Empty string = ungrouped (top-level, flat — the
|
|
278
|
+
// default and the backward-compatible behavior).
|
|
231
279
|
group: "",
|
|
232
280
|
items: [],
|
|
233
281
|
filter: "",
|
|
@@ -236,13 +284,18 @@ var Section = component({
|
|
|
236
284
|
statics: {
|
|
237
285
|
fromData(raw) {
|
|
238
286
|
if (!raw || typeof raw !== "object" || Array.isArray(raw) || raw.title == null) {
|
|
239
|
-
throw new Error(
|
|
287
|
+
throw new Error(
|
|
288
|
+
`Section.fromData: expected a section object { title, items }, got ${JSON.stringify(raw)}. getExamples() must return a section object or an array of section objects.`
|
|
289
|
+
);
|
|
240
290
|
}
|
|
241
291
|
const { id, title, description = "", group, items = [] } = raw;
|
|
242
292
|
return this.make({
|
|
243
293
|
id: id ?? slugify(title),
|
|
244
294
|
title,
|
|
245
295
|
description,
|
|
296
|
+
// `group` is an optional string (single 2-level grouping). A non-string is
|
|
297
|
+
// ignored here (rendered ungrouped) rather than coerced; the normalize layer
|
|
298
|
+
// (tools/core/module.js) reports it as a shape error.
|
|
246
299
|
group: typeof group === "string" ? group : "",
|
|
247
300
|
items: items.map((v) => Example.Class.fromData(v))
|
|
248
301
|
});
|
|
@@ -264,6 +317,7 @@ var Section = component({
|
|
|
264
317
|
}
|
|
265
318
|
},
|
|
266
319
|
receive: {
|
|
320
|
+
// First display of this section: run each example's `on.init`, mark shown.
|
|
267
321
|
init(ctx) {
|
|
268
322
|
fanoutLifecycle(ctx, this.items, "init");
|
|
269
323
|
return this.setInitialized(true);
|
|
@@ -318,6 +372,10 @@ var SidebarEntry = component({
|
|
|
318
372
|
var SidebarGroup = component({
|
|
319
373
|
name: "SidebarGroup",
|
|
320
374
|
fields: { name: "", collapsed: false, visible: true, rows: [] },
|
|
375
|
+
// Visibility drives the render-each `when` predicates (not `@show` on the items):
|
|
376
|
+
// a `when` filter renders only visible rows while keeping each row's positional key,
|
|
377
|
+
// so an event's path resolves to the right row. `@show` on a render-each item would
|
|
378
|
+
// shift the rendered set and mis-map the click.
|
|
321
379
|
alter: {
|
|
322
380
|
rowVisible(_key, row) {
|
|
323
381
|
return row.visible;
|
|
@@ -358,6 +416,9 @@ var Example = component({
|
|
|
358
416
|
view: "main",
|
|
359
417
|
requestHandlers: null,
|
|
360
418
|
on: null,
|
|
419
|
+
// Inspector tabs. activeTab selects which body renders; the *View fields hold
|
|
420
|
+
// prebuilt inspector instances (attached by mountStorybook before start, via
|
|
421
|
+
// src/storybook/inspect.js); the has* flags gate which tabs appear.
|
|
361
422
|
activeTab: "preview",
|
|
362
423
|
hasInspect: false,
|
|
363
424
|
hasComponent: false,
|
|
@@ -367,9 +428,15 @@ var Example = component({
|
|
|
367
428
|
instanceView: null,
|
|
368
429
|
lintView: null,
|
|
369
430
|
testView: null,
|
|
431
|
+
// Live dispatch activity for this example's component (`.value`): an ActivityLog
|
|
432
|
+
// (baked empty by src/storybook/inspect.js) the core observer appends to via
|
|
433
|
+
// logActivity. `hasActivity` gates the Activity tab so it only appears once
|
|
434
|
+
// something has actually happened.
|
|
370
435
|
activityLog: null,
|
|
371
436
|
hasActivity: false
|
|
372
437
|
},
|
|
438
|
+
// Storybook-only convention (read in mountStorybook, never by core): names the
|
|
439
|
+
// field on an example instance that holds its per-example request-handler mocks.
|
|
373
440
|
requestOverridesField: "requestHandlers",
|
|
374
441
|
statics: {
|
|
375
442
|
fromData({
|
|
@@ -393,6 +460,8 @@ var Example = component({
|
|
|
393
460
|
});
|
|
394
461
|
}
|
|
395
462
|
},
|
|
463
|
+
// Lifecycle hooks: the section forwards init/resume/suspend here; each runs the
|
|
464
|
+
// matching `on` phase's actions against this example's component (`.value`).
|
|
396
465
|
receive: {
|
|
397
466
|
init(ctx) {
|
|
398
467
|
this.runPhase(ctx, "init", this.on?.init);
|
|
@@ -406,6 +475,9 @@ var Example = component({
|
|
|
406
475
|
this.runPhase(ctx, "suspend", this.on?.suspend);
|
|
407
476
|
return this;
|
|
408
477
|
},
|
|
478
|
+
// Append one activity row to the example's ActivityLog. Dispatched by the storybook
|
|
479
|
+
// observer at this example's node — a path OUTSIDE `.value`, so the observer's own
|
|
480
|
+
// filter ignores it and this append is never itself logged (no loop).
|
|
409
481
|
logActivity(entry) {
|
|
410
482
|
return this.setActivityLog(this.activityLog.appendEntry(entry)).setHasActivity(true);
|
|
411
483
|
}
|
|
@@ -413,7 +485,9 @@ var Example = component({
|
|
|
413
485
|
methods: {
|
|
414
486
|
runPhase(ctx, name, phase) {
|
|
415
487
|
if (phaseHasBubble(phase))
|
|
416
|
-
console.warn(
|
|
488
|
+
console.warn(
|
|
489
|
+
`storybook on.${name}: a \`bubble\` action leaves this example and is received by the storybook engine, so your component's bubble handler won't run. Use send/request/input to drive a preset state.`
|
|
490
|
+
);
|
|
417
491
|
dispatchPhase(ctx, ctx.at.field("value").buildPath(), phase, this.value);
|
|
418
492
|
}
|
|
419
493
|
},
|
|
@@ -529,12 +603,9 @@ function transitionSections(ctx, sb, oldIndex, newIndex) {
|
|
|
529
603
|
if (changed && sb.sections.get(oldIndex)?.initialized)
|
|
530
604
|
ctx.at.index("sections", oldIndex).send("suspend", []);
|
|
531
605
|
const target = sb.sections.get(newIndex);
|
|
532
|
-
if (!target)
|
|
533
|
-
|
|
534
|
-
if (
|
|
535
|
-
ctx.at.index("sections", newIndex).send("init", []);
|
|
536
|
-
else if (changed)
|
|
537
|
-
ctx.at.index("sections", newIndex).send("resume", []);
|
|
606
|
+
if (!target) return;
|
|
607
|
+
if (!target.initialized) ctx.at.index("sections", newIndex).send("init", []);
|
|
608
|
+
else if (changed) ctx.at.index("sections", newIndex).send("resume", []);
|
|
538
609
|
}
|
|
539
610
|
function fanoutLifecycle(ctx, items, name) {
|
|
540
611
|
items.forEach((_item, j) => {
|
|
@@ -543,23 +614,18 @@ function fanoutLifecycle(ctx, items, name) {
|
|
|
543
614
|
}
|
|
544
615
|
function fuzzyMatch(query, target) {
|
|
545
616
|
const q = query.toLowerCase().trim();
|
|
546
|
-
if (q === "")
|
|
547
|
-
return true;
|
|
617
|
+
if (q === "") return true;
|
|
548
618
|
const t = target.toLowerCase();
|
|
549
|
-
if (t.includes(q))
|
|
550
|
-
return true;
|
|
619
|
+
if (t.includes(q)) return true;
|
|
551
620
|
const budget = q.length + Math.max(1, Math.floor(q.length / 2));
|
|
552
|
-
for (let start = 0;start < t.length; start++) {
|
|
553
|
-
if (t[start] !== q[0])
|
|
554
|
-
continue;
|
|
621
|
+
for (let start = 0; start < t.length; start++) {
|
|
622
|
+
if (t[start] !== q[0]) continue;
|
|
555
623
|
let qi = 1;
|
|
556
624
|
let ti = start + 1;
|
|
557
|
-
for (;ti < t.length && qi < q.length; ti++) {
|
|
558
|
-
if (t[ti] === q[qi])
|
|
559
|
-
qi++;
|
|
625
|
+
for (; ti < t.length && qi < q.length; ti++) {
|
|
626
|
+
if (t[ti] === q[qi]) qi++;
|
|
560
627
|
}
|
|
561
|
-
if (qi === q.length && ti - start <= budget)
|
|
562
|
-
return true;
|
|
628
|
+
if (qi === q.length && ti - start <= budget) return true;
|
|
563
629
|
}
|
|
564
630
|
return false;
|
|
565
631
|
}
|
|
@@ -567,36 +633,37 @@ function slugify(str) {
|
|
|
567
633
|
return String(str).normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
568
634
|
}
|
|
569
635
|
function buildSidebar(sections) {
|
|
570
|
-
const groups = new Map;
|
|
636
|
+
const groups = /* @__PURE__ */ new Map();
|
|
571
637
|
const singles = [];
|
|
572
638
|
sections.forEach((s) => {
|
|
573
639
|
const name = s.group ?? "";
|
|
574
|
-
if (name === "")
|
|
575
|
-
singles.push(s);
|
|
640
|
+
if (name === "") singles.push(s);
|
|
576
641
|
else {
|
|
577
|
-
if (!groups.has(name))
|
|
578
|
-
groups.set(name, []);
|
|
642
|
+
if (!groups.has(name)) groups.set(name, []);
|
|
579
643
|
groups.get(name).push(s);
|
|
580
644
|
}
|
|
581
645
|
});
|
|
582
646
|
const buckets = [];
|
|
583
647
|
for (const [name, secs] of groups)
|
|
584
648
|
buckets.push({ key: name.toLowerCase(), name, sections: secs });
|
|
585
|
-
for (const s of singles)
|
|
586
|
-
buckets.push({ key: s.title.toLowerCase(), name: "", sections: [s] });
|
|
649
|
+
for (const s of singles) buckets.push({ key: s.title.toLowerCase(), name: "", sections: [s] });
|
|
587
650
|
buckets.sort((a, b) => a.key.localeCompare(b.key));
|
|
588
|
-
return buckets.map(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
651
|
+
return buckets.map(
|
|
652
|
+
(b) => SidebarGroup.make({
|
|
653
|
+
name: b.name,
|
|
654
|
+
collapsed: false,
|
|
655
|
+
visible: true,
|
|
656
|
+
rows: b.sections.map(
|
|
657
|
+
(s) => SidebarEntry.make({
|
|
658
|
+
sectionId: s.id,
|
|
659
|
+
title: s.title,
|
|
660
|
+
description: s.description,
|
|
661
|
+
selected: false,
|
|
662
|
+
visible: true
|
|
663
|
+
})
|
|
664
|
+
)
|
|
665
|
+
})
|
|
666
|
+
);
|
|
600
667
|
}
|
|
601
668
|
|
|
602
669
|
// src/storybook/build.js
|
|
@@ -604,8 +671,7 @@ import { getComponents as getInspectorComponents } from "tutuca/components";
|
|
|
604
671
|
function buildStorybook(modules) {
|
|
605
672
|
const rawSections = modules.flatMap((m) => {
|
|
606
673
|
const raw = m.getExamples?.();
|
|
607
|
-
if (raw == null)
|
|
608
|
-
return [];
|
|
674
|
+
if (raw == null) return [];
|
|
609
675
|
return Array.isArray(raw) ? raw : [raw];
|
|
610
676
|
});
|
|
611
677
|
const sections = rawSections.map((s) => Section.Class.fromData(s)).sort((a, b) => a.title.localeCompare(b.title));
|
|
@@ -615,15 +681,15 @@ function buildStorybook(modules) {
|
|
|
615
681
|
Example,
|
|
616
682
|
SidebarGroup,
|
|
617
683
|
SidebarEntry,
|
|
684
|
+
// ActivityLog + ActivityEntry arrive via getInspectorComponents() (tutuca/components).
|
|
618
685
|
...getInspectorComponents()
|
|
619
686
|
];
|
|
620
687
|
const engineSet = new Set(engineComponents);
|
|
621
688
|
const moduleComponents = modules.map((m) => {
|
|
622
|
-
const seen = new Set;
|
|
689
|
+
const seen = /* @__PURE__ */ new Set();
|
|
623
690
|
const out = [];
|
|
624
691
|
for (const c of m.getComponents?.() ?? []) {
|
|
625
|
-
if (!c || engineSet.has(c) || seen.has(c))
|
|
626
|
-
continue;
|
|
692
|
+
if (!c || engineSet.has(c) || seen.has(c)) continue;
|
|
627
693
|
seen.add(c);
|
|
628
694
|
out.push(c);
|
|
629
695
|
}
|
|
@@ -631,20 +697,17 @@ function buildStorybook(modules) {
|
|
|
631
697
|
});
|
|
632
698
|
const macros = {};
|
|
633
699
|
const requestHandlers = {};
|
|
634
|
-
const overrideNames = new Set;
|
|
700
|
+
const overrideNames = /* @__PURE__ */ new Set();
|
|
635
701
|
for (const s of rawSections) {
|
|
636
702
|
for (const it of s?.items ?? []) {
|
|
637
|
-
for (const name in it?.requestHandlers ?? {})
|
|
638
|
-
overrideNames.add(name);
|
|
703
|
+
for (const name in it?.requestHandlers ?? {}) overrideNames.add(name);
|
|
639
704
|
}
|
|
640
705
|
}
|
|
641
706
|
for (const m of modules) {
|
|
642
|
-
if (m.getMacros)
|
|
643
|
-
|
|
644
|
-
if (m.getRequestHandlers)
|
|
645
|
-
Object.assign(requestHandlers, m.getRequestHandlers());
|
|
707
|
+
if (m.getMacros) Object.assign(macros, m.getMacros());
|
|
708
|
+
if (m.getRequestHandlers) Object.assign(requestHandlers, m.getRequestHandlers());
|
|
646
709
|
}
|
|
647
|
-
const components = [
|
|
710
|
+
const components = [.../* @__PURE__ */ new Set([...engineComponents, ...moduleComponents.flat()])];
|
|
648
711
|
return {
|
|
649
712
|
root: Storybook.Class.withSections(sections),
|
|
650
713
|
components,
|
|
@@ -656,15 +719,14 @@ function buildStorybook(modules) {
|
|
|
656
719
|
};
|
|
657
720
|
}
|
|
658
721
|
function buildExampleRequestHandlers({ requestHandlers: reals, overrideNames }) {
|
|
659
|
-
const names = new Set([...Object.keys(reals), ...overrideNames]);
|
|
722
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(reals), ...overrideNames]);
|
|
660
723
|
const makeMeta = (name) => async (...rest) => {
|
|
661
724
|
const ctx = rest.at(-1);
|
|
662
725
|
const args = rest.slice(0, -1);
|
|
663
726
|
let override = null;
|
|
664
727
|
ctx.walkPath((Comp, inst) => {
|
|
665
728
|
const field = Comp.extra?.requestOverridesField;
|
|
666
|
-
if (!field)
|
|
667
|
-
return;
|
|
729
|
+
if (!field) return;
|
|
668
730
|
const map = inst.get(field, null);
|
|
669
731
|
if (map && name in map) {
|
|
670
732
|
override = map[name];
|
|
@@ -672,15 +734,14 @@ function buildExampleRequestHandlers({ requestHandlers: reals, overrideNames })
|
|
|
672
734
|
}
|
|
673
735
|
});
|
|
674
736
|
const fn = override ?? reals[name];
|
|
675
|
-
if (!fn)
|
|
676
|
-
throw new Error(`Request not found: ${name}`);
|
|
737
|
+
if (!fn) throw new Error(`Request not found: ${name}`);
|
|
677
738
|
return await fn(...args, ctx);
|
|
678
739
|
};
|
|
679
740
|
const handlers = {};
|
|
680
|
-
for (const name of names)
|
|
681
|
-
handlers[name] = makeMeta(name);
|
|
741
|
+
for (const name of names) handlers[name] = makeMeta(name);
|
|
682
742
|
return handlers;
|
|
683
743
|
}
|
|
744
|
+
|
|
684
745
|
// src/storybook/mount.js
|
|
685
746
|
import { injectCss, tutuca } from "tutuca";
|
|
686
747
|
|
|
@@ -698,8 +759,7 @@ function subscribeExampleActivity(app) {
|
|
|
698
759
|
return;
|
|
699
760
|
const si = keys[0].key;
|
|
700
761
|
const ii = keys[1].key;
|
|
701
|
-
if (si ===
|
|
702
|
-
return;
|
|
762
|
+
if (si === void 0 || ii === void 0) return;
|
|
703
763
|
const entry = recordToEntry(record, { inspect, pathLabel: storybookPathLabel });
|
|
704
764
|
rootDispatcher(app.transactor).at.index("sections", si).index("items", ii).send("logActivity", [entry]);
|
|
705
765
|
});
|
|
@@ -708,14 +768,12 @@ function subscribeExampleActivity(app) {
|
|
|
708
768
|
// src/storybook/inspect.js
|
|
709
769
|
import { ActivityLog, buildInspectorViews, isComponentInstance } from "tutuca/components";
|
|
710
770
|
function buildTestIndex(modules) {
|
|
711
|
-
const index = new Map;
|
|
771
|
+
const index = /* @__PURE__ */ new Map();
|
|
712
772
|
for (const m of modules) {
|
|
713
|
-
if (typeof m.getTests !== "function")
|
|
714
|
-
continue;
|
|
773
|
+
if (typeof m.getTests !== "function") continue;
|
|
715
774
|
const components = m.getComponents?.() ?? [];
|
|
716
775
|
for (const c of components) {
|
|
717
|
-
if (!index.has(c.name))
|
|
718
|
-
index.set(c.name, { getTests: m.getTests, components });
|
|
776
|
+
if (!index.has(c.name)) index.set(c.name, { getTests: m.getTests, components });
|
|
719
777
|
}
|
|
720
778
|
}
|
|
721
779
|
return index;
|
|
@@ -734,10 +792,10 @@ async function buildExampleInspectors(example, scope, testIndex, dev) {
|
|
|
734
792
|
async function attachInspectorViews(root, scope, modules, dev = null) {
|
|
735
793
|
const testIndex = buildTestIndex(modules);
|
|
736
794
|
let sections = root.sections;
|
|
737
|
-
for (let si = 0;si < sections.size; si++) {
|
|
795
|
+
for (let si = 0; si < sections.size; si++) {
|
|
738
796
|
const section = sections.get(si);
|
|
739
797
|
let items = section.items;
|
|
740
|
-
for (let ii = 0;ii < items.size; ii++) {
|
|
798
|
+
for (let ii = 0; ii < items.size; ii++) {
|
|
741
799
|
items = items.set(ii, await buildExampleInspectors(items.get(ii), scope, testIndex, dev));
|
|
742
800
|
}
|
|
743
801
|
sections = sections.set(si, section.setItems(items));
|
|
@@ -783,7 +841,7 @@ var MARGAUI_THEMES = [
|
|
|
783
841
|
"winter",
|
|
784
842
|
"wireframe"
|
|
785
843
|
];
|
|
786
|
-
var BUNDLED_THEMES = new Set(["light", "dark"]);
|
|
844
|
+
var BUNDLED_THEMES = /* @__PURE__ */ new Set(["light", "dark"]);
|
|
787
845
|
|
|
788
846
|
// src/storybook/mount.js
|
|
789
847
|
async function mountStorybook(selector, modules, { compileCss, root, persistUrl = true, dev = null, noCache = false, themes = null } = {}) {
|
|
@@ -814,8 +872,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
|
|
|
814
872
|
injectCss("tutuca-storybook", await compileCss(app));
|
|
815
873
|
}
|
|
816
874
|
app.start({ noCache });
|
|
817
|
-
if (dev)
|
|
818
|
-
subscribeExampleActivity(app);
|
|
875
|
+
if (dev) subscribeExampleActivity(app);
|
|
819
876
|
app.sendAtRoot("init", []);
|
|
820
877
|
if (persistUrl) {
|
|
821
878
|
window.addEventListener("popstate", () => app.sendAtRoot("init", []));
|
|
@@ -823,20 +880,16 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
|
|
|
823
880
|
app.scopes = { root: rootScope, modules: moduleScopes };
|
|
824
881
|
return app;
|
|
825
882
|
function persistState(state, instance, push) {
|
|
826
|
-
if (instance !== app.state.val)
|
|
827
|
-
return;
|
|
883
|
+
if (instance !== app.state.val) return;
|
|
828
884
|
const url = new URL(window.location.href);
|
|
829
885
|
for (const [k, v] of Object.entries(state)) {
|
|
830
|
-
if (v === "" || v == null)
|
|
831
|
-
|
|
832
|
-
else
|
|
833
|
-
url.searchParams.set(k, String(v));
|
|
886
|
+
if (v === "" || v == null) url.searchParams.delete(k);
|
|
887
|
+
else url.searchParams.set(k, String(v));
|
|
834
888
|
}
|
|
835
889
|
window.history[push ? "pushState" : "replaceState"](null, "", url);
|
|
836
890
|
}
|
|
837
891
|
function applyTheme(name, instance) {
|
|
838
|
-
if (instance !== app.state.val)
|
|
839
|
-
return;
|
|
892
|
+
if (instance !== app.state.val) return;
|
|
840
893
|
ensureThemeLink(name);
|
|
841
894
|
document.documentElement.dataset.theme = name;
|
|
842
895
|
}
|
|
@@ -860,8 +913,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
|
|
|
860
913
|
};
|
|
861
914
|
}
|
|
862
915
|
function resolveTheme(fromUrl) {
|
|
863
|
-
if (!themeBaseUrl)
|
|
864
|
-
return "";
|
|
916
|
+
if (!themeBaseUrl) return "";
|
|
865
917
|
const name = MARGAUI_THEMES.includes(fromUrl) ? fromUrl : osTheme();
|
|
866
918
|
ensureThemeLink(name);
|
|
867
919
|
document.documentElement.dataset.theme = name;
|
|
@@ -871,8 +923,7 @@ async function mountStorybook(selector, modules, { compileCss, root, persistUrl
|
|
|
871
923
|
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
872
924
|
}
|
|
873
925
|
function ensureThemeLink(name) {
|
|
874
|
-
if (BUNDLED_THEMES.has(name) || document.getElementById(themeLinkId(name)))
|
|
875
|
-
return;
|
|
926
|
+
if (BUNDLED_THEMES.has(name) || document.getElementById(themeLinkId(name))) return;
|
|
876
927
|
const link = document.createElement("link");
|
|
877
928
|
link.rel = "stylesheet";
|
|
878
929
|
link.id = themeLinkId(name);
|
|
@@ -889,15 +940,15 @@ function getComponents() {
|
|
|
889
940
|
return [Storybook, Section, Example, SidebarGroup, SidebarEntry];
|
|
890
941
|
}
|
|
891
942
|
export {
|
|
892
|
-
|
|
893
|
-
mountStorybook,
|
|
894
|
-
getComponents,
|
|
895
|
-
fuzzyMatch,
|
|
896
|
-
buildStorybook,
|
|
897
|
-
buildExampleRequestHandlers,
|
|
898
|
-
Storybook,
|
|
899
|
-
Section,
|
|
900
|
-
MARGAUI_THEMES,
|
|
943
|
+
BUNDLED_THEMES,
|
|
901
944
|
Example,
|
|
902
|
-
|
|
945
|
+
MARGAUI_THEMES,
|
|
946
|
+
Section,
|
|
947
|
+
Storybook,
|
|
948
|
+
buildExampleRequestHandlers,
|
|
949
|
+
buildStorybook,
|
|
950
|
+
fuzzyMatch,
|
|
951
|
+
getComponents,
|
|
952
|
+
mountStorybook,
|
|
953
|
+
slugify
|
|
903
954
|
};
|