@jfrader/storybook-docs 0.4.4 → 0.6.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/build/story-sources.d.ts +15 -0
- package/dist/controls.d.ts +2 -1
- package/dist/index.css +7 -3
- package/dist/index.js +147 -70
- package/dist/rail.d.ts +1 -0
- package/dist/sections.d.ts +2 -1
- package/dist/styles.css +46 -9
- package/dist/toc.d.ts +0 -6
- package/dist/virtual.d.ts +4 -0
- package/dist/vite-plugin.js +234 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface HelperExpansion {
|
|
2
|
+
inlined: string[];
|
|
3
|
+
remaining: {
|
|
4
|
+
name: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}[];
|
|
7
|
+
}
|
|
8
|
+
export declare function extractStorySourceDetails(source: string): {
|
|
9
|
+
sources: Record<string, string>;
|
|
10
|
+
helpers: Record<string, HelperExpansion>;
|
|
11
|
+
};
|
|
12
|
+
export declare function extractStorySources(source: string): Record<string, string>;
|
|
13
|
+
export declare function storySourcesIndexCode(slugs: Iterable<string>): string;
|
|
14
|
+
export declare function storySourcesCode(sources: Record<string, string>): string;
|
|
15
|
+
export {};
|
package/dist/controls.d.ts
CHANGED
|
@@ -40,10 +40,11 @@ export declare function getStoryControls(meta: StoryShape, story: StoryShape, co
|
|
|
40
40
|
controls: StoryControl[];
|
|
41
41
|
unavailable: string | null;
|
|
42
42
|
};
|
|
43
|
-
export declare function ControlledStoryFrame({ module, story, componentName, componentDefaults }: {
|
|
43
|
+
export declare function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }: {
|
|
44
44
|
module: Record<string, unknown>;
|
|
45
45
|
story: string;
|
|
46
46
|
componentName: string;
|
|
47
47
|
/** Defaults read from the component source, shown until the reader changes them. */
|
|
48
48
|
componentDefaults?: Record<string, unknown>;
|
|
49
|
+
renderSource?: string;
|
|
49
50
|
}): import("react").JSX.Element;
|
package/dist/index.css
CHANGED
|
@@ -106,16 +106,20 @@
|
|
|
106
106
|
.toc {
|
|
107
107
|
display: none;
|
|
108
108
|
}
|
|
109
|
+
.toc-track {
|
|
110
|
+
display: none;
|
|
111
|
+
}
|
|
109
112
|
@media (min-width: 1180px) {
|
|
110
113
|
.app.has-toc {
|
|
111
114
|
grid-template-columns: 276px minmax(0, 1fr) 224px;
|
|
112
115
|
}
|
|
113
|
-
.toc {
|
|
116
|
+
.toc-track {
|
|
114
117
|
display: block;
|
|
115
118
|
grid-column: 3;
|
|
116
119
|
grid-row: 2;
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
}
|
|
121
|
+
.toc {
|
|
122
|
+
display: block;
|
|
119
123
|
padding: 34px 18px 40px 6px;
|
|
120
124
|
font-size: 12.5px;
|
|
121
125
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,12 @@ import { createRoot } from "react-dom/client";
|
|
|
5
5
|
import { site as site3, themeCss, theme as theme2 } from "virtual:storybook-docs/site";
|
|
6
6
|
|
|
7
7
|
// src/app.tsx
|
|
8
|
-
import { useEffect as
|
|
8
|
+
import { Children, useEffect as useEffect8, useRef as useRef5, useState as useState9 } from "react";
|
|
9
9
|
import { catalog as catalog3, groups as groups2 } from "virtual:storybook-docs/catalog";
|
|
10
10
|
import { pages, site as site2, theme } from "virtual:storybook-docs/site";
|
|
11
11
|
|
|
12
12
|
// src/pages/component-page.tsx
|
|
13
|
-
import { useMemo as useMemo3 } from "react";
|
|
13
|
+
import { useEffect as useEffect6, useMemo as useMemo3, useState as useState7 } from "react";
|
|
14
14
|
import { storySource as storySource2 } from "virtual:storybook-docs/catalog";
|
|
15
15
|
|
|
16
16
|
// src/guidance.tsx
|
|
@@ -374,7 +374,8 @@ function usageSnippet(componentName, args) {
|
|
|
374
374
|
const children = typeof args.children === "string" && !/[&<>{}]/.test(args.children) ? args.children : `{${JSON.stringify(args.children)}}`;
|
|
375
375
|
return `${opening}>${children}</${componentName}>`;
|
|
376
376
|
}
|
|
377
|
-
function buildUsageSnippet({ componentName, from, args, source, jsxName, component }) {
|
|
377
|
+
function buildUsageSnippet({ componentName, from, args, source, renderSource, jsxName, component }) {
|
|
378
|
+
if (!source?.trim() && renderSource?.trim()) return renderSource;
|
|
378
379
|
const rootName = componentName.split(".")[0];
|
|
379
380
|
const identity = component;
|
|
380
381
|
const tag = jsxName ?? (typeof identity?.displayName === "string" && identity.displayName.startsWith(`${rootName}.`) ? identity.displayName : componentName);
|
|
@@ -510,6 +511,77 @@ function LazySection({ children, placeholder = "\u2026" }) {
|
|
|
510
511
|
|
|
511
512
|
// src/toc.tsx
|
|
512
513
|
import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
514
|
+
|
|
515
|
+
// src/rail.ts
|
|
516
|
+
var RAIL_MEDIA = "(min-width: 1180px)";
|
|
517
|
+
var RAIL_GAP = 20;
|
|
518
|
+
function followRail(rail, header) {
|
|
519
|
+
const track = rail.parentElement;
|
|
520
|
+
if (!track) return () => {
|
|
521
|
+
};
|
|
522
|
+
const media = window.matchMedia(RAIL_MEDIA);
|
|
523
|
+
let lastY = window.scrollY;
|
|
524
|
+
let direction = "down";
|
|
525
|
+
let spacer = 0;
|
|
526
|
+
const geometry = () => {
|
|
527
|
+
const top = header.offsetHeight + RAIL_GAP;
|
|
528
|
+
const viewportBottom = window.innerHeight - RAIL_GAP;
|
|
529
|
+
const height = rail.offsetHeight;
|
|
530
|
+
return {
|
|
531
|
+
top,
|
|
532
|
+
viewportBottom,
|
|
533
|
+
height,
|
|
534
|
+
fits: top + height <= viewportBottom,
|
|
535
|
+
trackTop: track.getBoundingClientRect().top + window.scrollY,
|
|
536
|
+
maxOffset: Math.max(0, track.clientHeight - height)
|
|
537
|
+
};
|
|
538
|
+
};
|
|
539
|
+
const clear = () => {
|
|
540
|
+
rail.removeAttribute("style");
|
|
541
|
+
track.removeAttribute("style");
|
|
542
|
+
};
|
|
543
|
+
const arm = (g = geometry()) => {
|
|
544
|
+
if (!media.matches) {
|
|
545
|
+
clear();
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
spacer = g.fits ? 0 : Math.min(spacer, g.maxOffset);
|
|
549
|
+
track.style.setProperty("--sd-rail-offset", `${spacer}px`);
|
|
550
|
+
rail.style.position = "sticky";
|
|
551
|
+
rail.style.top = g.fits ? `${g.top}px` : direction === "down" ? `${g.viewportBottom - g.height}px` : "auto";
|
|
552
|
+
rail.style.bottom = g.fits || direction === "down" ? "auto" : `${window.innerHeight - g.top - g.height}px`;
|
|
553
|
+
};
|
|
554
|
+
const onScroll = () => {
|
|
555
|
+
const y = window.scrollY;
|
|
556
|
+
const next = y > lastY ? "down" : y < lastY ? "up" : direction;
|
|
557
|
+
if (next !== direction && media.matches) {
|
|
558
|
+
const g = geometry();
|
|
559
|
+
spacer = direction === "down" ? Math.min(Math.max(spacer, lastY + g.viewportBottom - g.height - g.trackTop), g.maxOffset) : Math.max(Math.min(spacer, lastY + g.top - g.trackTop), 0);
|
|
560
|
+
direction = next;
|
|
561
|
+
arm(g);
|
|
562
|
+
}
|
|
563
|
+
direction = next;
|
|
564
|
+
lastY = y;
|
|
565
|
+
};
|
|
566
|
+
const relayout = () => arm();
|
|
567
|
+
const observer = new ResizeObserver(relayout);
|
|
568
|
+
observer.observe(rail);
|
|
569
|
+
observer.observe(track);
|
|
570
|
+
observer.observe(header);
|
|
571
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
572
|
+
window.addEventListener("resize", relayout);
|
|
573
|
+
media.addEventListener("change", relayout);
|
|
574
|
+
arm();
|
|
575
|
+
return () => {
|
|
576
|
+
observer.disconnect();
|
|
577
|
+
window.removeEventListener("scroll", onScroll);
|
|
578
|
+
window.removeEventListener("resize", relayout);
|
|
579
|
+
media.removeEventListener("change", relayout);
|
|
580
|
+
clear();
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/toc.tsx
|
|
513
585
|
import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
514
586
|
var STORY_PARAM = "story";
|
|
515
587
|
var SCROLL_OFFSET_PX = 76;
|
|
@@ -534,34 +606,9 @@ var slugify = (text) => text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCa
|
|
|
534
606
|
function useRailFollow(ref, ready = true) {
|
|
535
607
|
useEffect3(() => {
|
|
536
608
|
const element = ref.current;
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const limit = () => window.innerHeight - (Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sd-header-height")) || 56) - 20;
|
|
541
|
-
const apply = () => {
|
|
542
|
-
frame = 0;
|
|
543
|
-
const bottomInDocument = element.getBoundingClientRect().bottom + window.scrollY - applied;
|
|
544
|
-
const crossing = bottomInDocument - limit();
|
|
545
|
-
const next = Math.max(0, window.scrollY - crossing);
|
|
546
|
-
if (next !== applied) {
|
|
547
|
-
applied = next;
|
|
548
|
-
element.style.setProperty("--sd-rail-follow", `${next}px`);
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
|
-
const schedule = () => {
|
|
552
|
-
if (!frame) frame = requestAnimationFrame(apply);
|
|
553
|
-
};
|
|
554
|
-
apply();
|
|
555
|
-
window.addEventListener("scroll", schedule, { passive: true });
|
|
556
|
-
window.addEventListener("resize", schedule);
|
|
557
|
-
const observer = new ResizeObserver(schedule);
|
|
558
|
-
observer.observe(element);
|
|
559
|
-
return () => {
|
|
560
|
-
window.removeEventListener("scroll", schedule);
|
|
561
|
-
window.removeEventListener("resize", schedule);
|
|
562
|
-
observer.disconnect();
|
|
563
|
-
if (frame) cancelAnimationFrame(frame);
|
|
564
|
-
};
|
|
609
|
+
const header = element?.closest(".app")?.querySelector(".top");
|
|
610
|
+
if (!element || !header) return;
|
|
611
|
+
return followRail(element, header);
|
|
565
612
|
}, [ready, ref]);
|
|
566
613
|
}
|
|
567
614
|
function scanPage(root, pageKey) {
|
|
@@ -705,7 +752,7 @@ function PageRail({ pageKey }) {
|
|
|
705
752
|
const active = useActiveIds(scan);
|
|
706
753
|
const go = useCallback((element) => scrollToElement(element), []);
|
|
707
754
|
if (!scan) return null;
|
|
708
|
-
return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
|
|
755
|
+
return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsx5("div", { className: "rail-track toc-track", children: /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
|
|
709
756
|
/* @__PURE__ */ jsx5("div", { className: "toc-title", children: "On this page" }),
|
|
710
757
|
/* @__PURE__ */ jsx5("ul", { children: scan.sections.map((section) => /* @__PURE__ */ jsxs3("li", { children: [
|
|
711
758
|
/* @__PURE__ */ jsx5(
|
|
@@ -730,7 +777,7 @@ function PageRail({ pageKey }) {
|
|
|
730
777
|
}
|
|
731
778
|
) }, story.id)) }) : null
|
|
732
779
|
] }, section.id)) })
|
|
733
|
-
] }) : null });
|
|
780
|
+
] }) }) : null });
|
|
734
781
|
}
|
|
735
782
|
|
|
736
783
|
// src/component-demo.tsx
|
|
@@ -980,7 +1027,7 @@ function getStoryControls(meta, story, componentDefaults = {}) {
|
|
|
980
1027
|
unavailable: controls.length === 0 ? "No controls." : null
|
|
981
1028
|
};
|
|
982
1029
|
}
|
|
983
|
-
function ControlledStoryFrame({ module, story, componentName, componentDefaults }) {
|
|
1030
|
+
function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }) {
|
|
984
1031
|
const meta = module.default ?? {};
|
|
985
1032
|
const definition = module[story] ?? {};
|
|
986
1033
|
const { args: defaults, controls, unavailable } = getStoryControls(meta, definition, componentDefaults);
|
|
@@ -996,12 +1043,13 @@ function ControlledStoryFrame({ module, story, componentName, componentDefaults
|
|
|
996
1043
|
const storyName = definition.name ?? story;
|
|
997
1044
|
const skipped = ["loaders", "beforeEach"].filter((key) => meta[key] || definition[key]);
|
|
998
1045
|
if (definition.parameters?.viewport ?? meta.parameters?.viewport) skipped.push("viewport");
|
|
999
|
-
const codeLanguage = docsOverride?.source?.language ?? docsMeta?.source?.language ?? "tsx";
|
|
1046
|
+
const codeLanguage = authoredCode?.trim() ? docsOverride?.source?.language ?? docsMeta?.source?.language ?? "tsx" : "tsx";
|
|
1000
1047
|
const snippet = buildUsageSnippet({
|
|
1001
1048
|
componentName,
|
|
1002
1049
|
from: storySource,
|
|
1003
1050
|
args: { ...meta.args, ...definition.args, ...edits },
|
|
1004
1051
|
source: authoredCode,
|
|
1052
|
+
renderSource,
|
|
1005
1053
|
// A story name is not a component name: only a part-qualified name (the rare `Dialog.Content`
|
|
1006
1054
|
// story) is worth using as the JSX tag, otherwise the component's own name is the honest one.
|
|
1007
1055
|
jsxName: storyName.includes(".") ? storyName : void 0,
|
|
@@ -1049,6 +1097,7 @@ function useProps(slug) {
|
|
|
1049
1097
|
}
|
|
1050
1098
|
|
|
1051
1099
|
// src/pages/component-page.tsx
|
|
1100
|
+
import { loadStorySources } from "virtual:storybook-docs/story-sources";
|
|
1052
1101
|
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1053
1102
|
function formatValue(value) {
|
|
1054
1103
|
if (value === void 0) return "\u2014";
|
|
@@ -1175,6 +1224,19 @@ function PropsTable({
|
|
|
1175
1224
|
}
|
|
1176
1225
|
function ComponentPage({ entry }) {
|
|
1177
1226
|
const { module, error } = useStoryModule(entry);
|
|
1227
|
+
const [storySources, setStorySources] = useState7(null);
|
|
1228
|
+
useEffect6(() => {
|
|
1229
|
+
let active = true;
|
|
1230
|
+
loadStorySources(entry.slug).then(({ sources: sources2 }) => {
|
|
1231
|
+
if (active) setStorySources({ slug: entry.slug, sources: sources2 });
|
|
1232
|
+
}).catch(() => {
|
|
1233
|
+
if (active) setStorySources(null);
|
|
1234
|
+
});
|
|
1235
|
+
return () => {
|
|
1236
|
+
active = false;
|
|
1237
|
+
};
|
|
1238
|
+
}, [entry.slug]);
|
|
1239
|
+
const sources = storySources?.slug === entry.slug ? storySources.sources : {};
|
|
1178
1240
|
const props = useProps(entry.slug);
|
|
1179
1241
|
const meta = module?.default ?? {};
|
|
1180
1242
|
const argTypes = meta.argTypes ?? {};
|
|
@@ -1246,7 +1308,7 @@ function ComponentPage({ entry }) {
|
|
|
1246
1308
|
/* @__PURE__ */ jsx8(Skeleton, { height: 150 })
|
|
1247
1309
|
] }) : null,
|
|
1248
1310
|
module ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1249
|
-
example ? /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story: example, componentName: entry.name, componentDefaults }, `${entry.slug}:${example}`) : null,
|
|
1311
|
+
example ? /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story: example, componentName: entry.name, componentDefaults, renderSource: sources[example] }, `${entry.slug}:${example}`) : null,
|
|
1250
1312
|
entry.hasComponent ? /* @__PURE__ */ jsx8("h2", { children: "Usage" }) : null,
|
|
1251
1313
|
snippet ? /* @__PURE__ */ jsx8(CodeBlock, { code: snippet, language: codeLanguage }) : entry.hasComponent ? /* @__PURE__ */ jsx8("p", { children: "No usage code is authored for this component." }) : null,
|
|
1252
1314
|
usage ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
@@ -1256,7 +1318,7 @@ function ComponentPage({ entry }) {
|
|
|
1256
1318
|
/* @__PURE__ */ jsx8(Guidance, { slug: entry.slug }),
|
|
1257
1319
|
sections.map((section) => /* @__PURE__ */ jsxs5("section", { children: [
|
|
1258
1320
|
/* @__PURE__ */ jsx8("h2", { children: section.title }),
|
|
1259
|
-
section.stories.map((story) => /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story, componentName: entry.name, componentDefaults }, `${entry.slug}:${story}`))
|
|
1321
|
+
section.stories.map((story) => /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story, componentName: entry.name, componentDefaults, renderSource: sources[story] }, `${entry.slug}:${story}`))
|
|
1260
1322
|
] }, section.title))
|
|
1261
1323
|
] }) : null,
|
|
1262
1324
|
/* @__PURE__ */ jsx8("h2", { children: "Props" }),
|
|
@@ -1577,7 +1639,7 @@ function ReleasesPage() {
|
|
|
1577
1639
|
}
|
|
1578
1640
|
|
|
1579
1641
|
// src/search.tsx
|
|
1580
|
-
import { useEffect as
|
|
1642
|
+
import { useEffect as useEffect7, useMemo as useMemo4, useRef as useRef4, useState as useState8 } from "react";
|
|
1581
1643
|
import { catalog as catalog2 } from "virtual:storybook-docs/catalog";
|
|
1582
1644
|
|
|
1583
1645
|
// src/search-match.ts
|
|
@@ -1628,19 +1690,19 @@ function renderHighlighted(text, query) {
|
|
|
1628
1690
|
);
|
|
1629
1691
|
}
|
|
1630
1692
|
function SearchBox() {
|
|
1631
|
-
const [query, setQuery] =
|
|
1632
|
-
const [open, setOpen] =
|
|
1633
|
-
const [active, setActive] =
|
|
1693
|
+
const [query, setQuery] = useState8("");
|
|
1694
|
+
const [open, setOpen] = useState8(false);
|
|
1695
|
+
const [active, setActive] = useState8(0);
|
|
1634
1696
|
const inputRef = useRef4(null);
|
|
1635
1697
|
const listId = "storybook-docs-search-results";
|
|
1636
1698
|
const optionId = (key) => `${listId}-${key}`;
|
|
1637
1699
|
const results = useMemo4(() => {
|
|
1638
1700
|
return searchCatalog(catalog2, query);
|
|
1639
1701
|
}, [query]);
|
|
1640
|
-
|
|
1702
|
+
useEffect7(() => {
|
|
1641
1703
|
setActive(0);
|
|
1642
1704
|
}, [query]);
|
|
1643
|
-
|
|
1705
|
+
useEffect7(() => {
|
|
1644
1706
|
const onGlobalKeyDown = (event) => {
|
|
1645
1707
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
1646
1708
|
event.preventDefault();
|
|
@@ -1663,7 +1725,7 @@ function SearchBox() {
|
|
|
1663
1725
|
const showDropdown = open && (hasResults || hasQuery);
|
|
1664
1726
|
const activeRow = hasResults && results[active] ? results[active] : void 0;
|
|
1665
1727
|
const activeOptionId = activeRow ? optionId(rowKey(activeRow)) : void 0;
|
|
1666
|
-
|
|
1728
|
+
useEffect7(() => {
|
|
1667
1729
|
if (showDropdown && activeOptionId) {
|
|
1668
1730
|
const el = document.getElementById(activeOptionId);
|
|
1669
1731
|
el?.scrollIntoView({ block: "nearest" });
|
|
@@ -1785,6 +1847,23 @@ function SearchBox() {
|
|
|
1785
1847
|
import { releases as releases2 } from "virtual:storybook-docs/releases";
|
|
1786
1848
|
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1787
1849
|
var routeHref = (path) => `#${hrefFor(path)}`;
|
|
1850
|
+
var NAV_GROUP_WINDOW = 12;
|
|
1851
|
+
function NavGroup({ id, title, active, activeIndex = -1, children }) {
|
|
1852
|
+
const [open, setOpen] = useState9(false);
|
|
1853
|
+
const [showAll, setShowAll] = useState9(false);
|
|
1854
|
+
const expanded = active || open;
|
|
1855
|
+
const links = Children.toArray(children);
|
|
1856
|
+
const capped = active && links.length > NAV_GROUP_WINDOW;
|
|
1857
|
+
const start2 = Math.min(Math.max(activeIndex - NAV_GROUP_WINDOW / 2, 0), Math.max(0, links.length - NAV_GROUP_WINDOW));
|
|
1858
|
+
const visibleLinks = capped && !showAll ? links.slice(start2, start2 + NAV_GROUP_WINDOW) : links;
|
|
1859
|
+
return /* @__PURE__ */ jsxs13("div", { role: "group", "aria-labelledby": id, children: [
|
|
1860
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "grp", id, "aria-expanded": expanded, "aria-controls": `${id}-links`, disabled: active, onClick: () => setOpen(!open), children: title }),
|
|
1861
|
+
/* @__PURE__ */ jsxs13("div", { id: `${id}-links`, hidden: !expanded, children: [
|
|
1862
|
+
/* @__PURE__ */ jsx16("div", { id: `${id}-window`, children: visibleLinks }),
|
|
1863
|
+
capped ? /* @__PURE__ */ jsx16("button", { type: "button", className: "nav-more", "aria-expanded": showAll, "aria-controls": `${id}-window`, onClick: () => setShowAll(!showAll), children: showAll ? "Show less" : `Show all ${links.length}` }) : null
|
|
1864
|
+
] })
|
|
1865
|
+
] });
|
|
1866
|
+
}
|
|
1788
1867
|
function parseRoute(hash) {
|
|
1789
1868
|
const path = hashPath(hash).replace(new RegExp(`^#?\\/?${STORYBOOK_DOCS_BASE}`), "").replace(/^\/+/, "");
|
|
1790
1869
|
if (path.startsWith("components/")) {
|
|
@@ -1799,14 +1878,14 @@ function parseRoute(hash) {
|
|
|
1799
1878
|
return { kind: "overview" };
|
|
1800
1879
|
}
|
|
1801
1880
|
function DocsApp() {
|
|
1802
|
-
const [route, setRoute] =
|
|
1803
|
-
const [mode, setMode] =
|
|
1804
|
-
const [navOpen, setNavOpen] =
|
|
1881
|
+
const [route, setRoute] = useState9(() => parseRoute(window.location.hash));
|
|
1882
|
+
const [mode, setMode] = useState9(theme.values?.[0] ?? "dark");
|
|
1883
|
+
const [navOpen, setNavOpen] = useState9(false);
|
|
1805
1884
|
const navRef = useRef5(null);
|
|
1806
1885
|
useRailFollow(navRef);
|
|
1807
1886
|
const headerRef = useRef5(null);
|
|
1808
1887
|
const mainRef = useRef5(null);
|
|
1809
|
-
|
|
1888
|
+
useEffect8(() => {
|
|
1810
1889
|
const header = headerRef.current;
|
|
1811
1890
|
if (!header) return;
|
|
1812
1891
|
const root = document.documentElement;
|
|
@@ -1821,7 +1900,7 @@ function DocsApp() {
|
|
|
1821
1900
|
else root.style.removeProperty("--sd-header-height");
|
|
1822
1901
|
};
|
|
1823
1902
|
}, []);
|
|
1824
|
-
|
|
1903
|
+
useEffect8(() => {
|
|
1825
1904
|
const onHash = () => {
|
|
1826
1905
|
setRoute(parseRoute(window.location.hash));
|
|
1827
1906
|
setNavOpen(false);
|
|
@@ -1831,7 +1910,7 @@ function DocsApp() {
|
|
|
1831
1910
|
window.addEventListener("hashchange", onHash);
|
|
1832
1911
|
return () => window.removeEventListener("hashchange", onHash);
|
|
1833
1912
|
}, []);
|
|
1834
|
-
|
|
1913
|
+
useEffect8(() => {
|
|
1835
1914
|
document.documentElement.setAttribute(theme.attribute ?? "data-theme", mode);
|
|
1836
1915
|
}, [mode]);
|
|
1837
1916
|
const [darkMode, lightMode] = theme.values ?? ["dark", "light"];
|
|
@@ -1893,7 +1972,7 @@ function DocsApp() {
|
|
|
1893
1972
|
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u2197" })
|
|
1894
1973
|
] }, link.href))
|
|
1895
1974
|
] }),
|
|
1896
|
-
/* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
|
|
1975
|
+
/* @__PURE__ */ jsx16("div", { className: "rail-track nav-track", children: /* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
|
|
1897
1976
|
/* @__PURE__ */ jsx16("a", { href: routeHref("/"), className: route.kind === "overview" ? "on" : void 0, "aria-current": route.kind === "overview" ? "page" : void 0, children: "Overview" }),
|
|
1898
1977
|
releases2.changelog ? /* @__PURE__ */ jsxs13(
|
|
1899
1978
|
"a",
|
|
@@ -1910,22 +1989,20 @@ function DocsApp() {
|
|
|
1910
1989
|
]
|
|
1911
1990
|
}
|
|
1912
1991
|
) : null,
|
|
1913
|
-
foundations.length > 0 ? /* @__PURE__ */
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
))
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
/* @__PURE__ */ jsx16("div", { className: "grp", id: `nav-grp-${index}`, children: group }),
|
|
1928
|
-
catalog3.filter((entry) => entry.group === group).map((entry) => /* @__PURE__ */ jsx16(
|
|
1992
|
+
foundations.length > 0 ? /* @__PURE__ */ jsx16(NavGroup, { id: "nav-grp-foundations", title: "Foundations", active: foundations.some((item) => route.kind === item.kind), children: foundations.map((item) => /* @__PURE__ */ jsx16(
|
|
1993
|
+
"a",
|
|
1994
|
+
{
|
|
1995
|
+
href: item.href,
|
|
1996
|
+
className: route.kind === item.kind ? "on" : void 0,
|
|
1997
|
+
"aria-current": route.kind === item.kind ? "page" : void 0,
|
|
1998
|
+
children: item.label
|
|
1999
|
+
},
|
|
2000
|
+
item.href
|
|
2001
|
+
)) }, `foundations-${activeSlug ?? route.kind}`) : null,
|
|
2002
|
+
groups2.map((group, index) => {
|
|
2003
|
+
const entries = catalog3.filter((entry) => entry.group === group);
|
|
2004
|
+
const activeIndex = entries.findIndex((entry) => entry.slug === activeSlug);
|
|
2005
|
+
return /* @__PURE__ */ jsx16(NavGroup, { id: `nav-grp-${index}`, title: group, active: activeIndex !== -1, activeIndex, children: entries.map((entry) => /* @__PURE__ */ jsx16(
|
|
1929
2006
|
"a",
|
|
1930
2007
|
{
|
|
1931
2008
|
href: routeHref(`/components/${entry.slug}`),
|
|
@@ -1936,9 +2013,9 @@ function DocsApp() {
|
|
|
1936
2013
|
children: entry.name
|
|
1937
2014
|
},
|
|
1938
2015
|
entry.slug
|
|
1939
|
-
))
|
|
1940
|
-
|
|
1941
|
-
] }),
|
|
2016
|
+
)) }, `${group}-${activeSlug ?? route.kind}`);
|
|
2017
|
+
})
|
|
2018
|
+
] }) }),
|
|
1942
2019
|
/* @__PURE__ */ jsx16("main", { id: "main", className: "main", tabIndex: -1, ref: mainRef, children: route.kind === "component" ? /* @__PURE__ */ jsx16(ComponentPage, { entry: catalog3.find((entry) => entry.slug === route.slug) }) : route.kind === "color" ? /* @__PURE__ */ jsx16(ColorPage, {}) : route.kind === "typography" ? /* @__PURE__ */ jsx16(TypePage, {}) : route.kind === "spacing" ? /* @__PURE__ */ jsx16(SpacingPage, {}) : route.kind === "effects" ? /* @__PURE__ */ jsx16(EffectsPage, {}) : route.kind === "releases" ? /* @__PURE__ */ jsx16(ReleasesPage, {}) : /* @__PURE__ */ jsx16(OverviewPage, {}) }),
|
|
1943
2020
|
route.kind !== "overview" ? /* @__PURE__ */ jsx16(PageRail, { pageKey: activeSlug ?? route.kind }) : null
|
|
1944
2021
|
] });
|
package/dist/rail.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function followRail(rail: HTMLElement, header: HTMLElement): () => void;
|
package/dist/sections.d.ts
CHANGED
|
@@ -42,11 +42,12 @@ export interface ExampleStory {
|
|
|
42
42
|
export declare function selectExample(stories: ExampleStory[], parts?: string[], defaults?: Record<string, unknown> | null): string | null;
|
|
43
43
|
/** Only story-authored values: never synthesize children or component defaults. */
|
|
44
44
|
export declare function usageSnippet(componentName: string, args: Record<string, unknown> | undefined): string | null;
|
|
45
|
-
export declare function buildUsageSnippet({ componentName, from, args, source, jsxName, component }: {
|
|
45
|
+
export declare function buildUsageSnippet({ componentName, from, args, source, renderSource, jsxName, component }: {
|
|
46
46
|
componentName: string;
|
|
47
47
|
from: string;
|
|
48
48
|
args?: Record<string, unknown>;
|
|
49
49
|
source?: string;
|
|
50
|
+
renderSource?: string;
|
|
50
51
|
jsxName?: string;
|
|
51
52
|
component?: unknown;
|
|
52
53
|
}): string | null;
|
package/dist/styles.css
CHANGED
|
@@ -186,23 +186,44 @@ body {
|
|
|
186
186
|
outline-offset: 2px;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
.rail-track {
|
|
190
|
+
overflow-anchor: none;
|
|
191
|
+
min-width: 0;
|
|
192
|
+
}
|
|
193
|
+
@media (min-width: 1180px) {
|
|
194
|
+
.rail-track::before {
|
|
195
|
+
content: '';
|
|
196
|
+
display: block;
|
|
197
|
+
height: var(--sd-rail-offset, 0px);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
189
200
|
.nav {
|
|
190
201
|
border-right: 0;
|
|
191
202
|
background: var(--sd-surface);
|
|
192
|
-
padding: 14px 12px
|
|
193
|
-
/* Follows the document like the right rail, parking when its own end is visible. */
|
|
194
|
-
align-self: start;
|
|
195
|
-
transform: translateY(var(--sd-rail-follow, 0px));
|
|
203
|
+
padding: 14px 12px 12px;
|
|
196
204
|
}
|
|
197
|
-
.nav a
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
.nav a,
|
|
206
|
+
.nav-more {
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
box-sizing: border-box;
|
|
210
|
+
min-height: 28px;
|
|
211
|
+
padding: 4px 10px;
|
|
200
212
|
border-radius: 8px;
|
|
201
213
|
color: var(--sd-text-muted);
|
|
202
214
|
font-size: 13.5px;
|
|
203
215
|
text-decoration: none;
|
|
204
216
|
}
|
|
205
|
-
.nav
|
|
217
|
+
.nav-more {
|
|
218
|
+
width: 100%;
|
|
219
|
+
background: none;
|
|
220
|
+
border: 0;
|
|
221
|
+
font-family: inherit;
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
text-align: left;
|
|
224
|
+
}
|
|
225
|
+
.nav a:hover,
|
|
226
|
+
.nav-more:hover {
|
|
206
227
|
background: var(--sd-surface-hover);
|
|
207
228
|
color: var(--sd-text);
|
|
208
229
|
}
|
|
@@ -212,13 +233,26 @@ body {
|
|
|
212
233
|
font-weight: 600;
|
|
213
234
|
}
|
|
214
235
|
.grp {
|
|
215
|
-
margin:
|
|
236
|
+
margin: 3px 0 2px;
|
|
216
237
|
padding: 0 10px;
|
|
217
238
|
font-size: 10.5px;
|
|
218
239
|
letter-spacing: 0.09em;
|
|
219
240
|
text-transform: uppercase;
|
|
220
241
|
color: var(--sd-text-muted);
|
|
221
242
|
font-weight: 700;
|
|
243
|
+
display: block;
|
|
244
|
+
width: 100%;
|
|
245
|
+
text-align: left;
|
|
246
|
+
font-family: inherit;
|
|
247
|
+
background: none;
|
|
248
|
+
border: 0;
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
min-height: 28px;
|
|
251
|
+
}
|
|
252
|
+
.grp:focus-visible,
|
|
253
|
+
.nav-more:focus-visible {
|
|
254
|
+
outline: 2px solid var(--sd-focus-ring);
|
|
255
|
+
outline-offset: 2px;
|
|
222
256
|
}
|
|
223
257
|
|
|
224
258
|
.main {
|
|
@@ -618,6 +652,9 @@ code.type {
|
|
|
618
652
|
.nav-toggle {
|
|
619
653
|
display: inline-block;
|
|
620
654
|
}
|
|
655
|
+
.nav-track {
|
|
656
|
+
display: contents;
|
|
657
|
+
}
|
|
621
658
|
.search {
|
|
622
659
|
order: 10;
|
|
623
660
|
flex: 1 1 100%;
|
package/dist/toc.d.ts
CHANGED
|
@@ -6,12 +6,6 @@ export declare function hashPath(hash: string): string;
|
|
|
6
6
|
/** The `?story=` deep-link target of a hash, if any. */
|
|
7
7
|
export declare function storyFromHash(hash: string): string | null;
|
|
8
8
|
export declare function storyLink(location: Location, story: string): string;
|
|
9
|
-
/**
|
|
10
|
-
* A rail travels with the document until its own end would pass the fold, then it parks there —
|
|
11
|
-
* the reader keeps seeing where it continues, and scrolling up lets it fall back into flow. This is
|
|
12
|
-
* the scroll-linked translation docs sites use, because `position: sticky` cannot move an element
|
|
13
|
-
* taller than the viewport.
|
|
14
|
-
*/
|
|
15
9
|
export declare function useRailFollow(ref: RefObject<HTMLElement | null>, ready?: boolean): void;
|
|
16
10
|
/** The "On this page" rail and the `?story=` deep-link landing. */
|
|
17
11
|
export declare function PageRail({ pageKey }: {
|
package/dist/virtual.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ declare module 'virtual:storybook-docs/guidance' {
|
|
|
28
28
|
export function loadGuidance(slug: string): Promise<{ markdown: string }>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
declare module 'virtual:storybook-docs/story-sources' {
|
|
32
|
+
export function loadStorySources(slug: string): Promise<{ sources: Record<string, string> }>;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
declare module 'virtual:storybook-docs/releases' {
|
|
32
36
|
export const releases: { version: string | null; changelog: string | null };
|
|
33
37
|
}
|
package/dist/vite-plugin.js
CHANGED
|
@@ -608,6 +608,222 @@ export const fireEvent = () => true;
|
|
|
608
608
|
export const cleanup = () => {};
|
|
609
609
|
`;
|
|
610
610
|
|
|
611
|
+
// src/build/story-sources.ts
|
|
612
|
+
import ts2 from "typescript";
|
|
613
|
+
function unwrap(node) {
|
|
614
|
+
while (ts2.isParenthesizedExpression(node) || ts2.isAsExpression(node) || ts2.isSatisfiesExpression(node) || ts2.isTypeAssertionExpression(node)) node = node.expression;
|
|
615
|
+
return node;
|
|
616
|
+
}
|
|
617
|
+
var MAX_HELPER_DEPTH = 3;
|
|
618
|
+
function extractStorySourceDetails(source) {
|
|
619
|
+
const file = ts2.createSourceFile("stories.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
620
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
621
|
+
const exports = /* @__PURE__ */ new Map();
|
|
622
|
+
for (const statement of file.statements) {
|
|
623
|
+
const exported = ts2.canHaveModifiers(statement) && ts2.getModifiers(statement)?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword);
|
|
624
|
+
if (ts2.isVariableStatement(statement)) {
|
|
625
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
626
|
+
if (!ts2.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
627
|
+
declarations.set(declaration.name.text, declaration.initializer);
|
|
628
|
+
if (exported) exports.set(declaration.name.text, declaration.name.text);
|
|
629
|
+
}
|
|
630
|
+
} else if (ts2.isFunctionDeclaration(statement) && statement.name) {
|
|
631
|
+
declarations.set(statement.name.text, statement);
|
|
632
|
+
if (exported) exports.set(statement.name.text, statement.name.text);
|
|
633
|
+
} else if (ts2.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
|
|
634
|
+
for (const element of statement.exportClause.elements) {
|
|
635
|
+
exports.set(element.name.text, (element.propertyName ?? element.name).text);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const renderFor = (node, seen = /* @__PURE__ */ new Set()) => {
|
|
640
|
+
if (ts2.isFunctionDeclaration(node)) return node;
|
|
641
|
+
node = unwrap(node);
|
|
642
|
+
if (ts2.isIdentifier(node)) {
|
|
643
|
+
if (seen.has(node.text)) return;
|
|
644
|
+
seen.add(node.text);
|
|
645
|
+
const declaration = declarations.get(node.text);
|
|
646
|
+
return declaration ? renderFor(declaration, seen) : void 0;
|
|
647
|
+
}
|
|
648
|
+
if (ts2.isPropertyAccessExpression(node) && node.name.text === "render" || ts2.isElementAccessExpression(node) && ts2.isStringLiteral(node.argumentExpression) && node.argumentExpression.text === "render") return renderFor(node.expression, seen);
|
|
649
|
+
if (ts2.isArrowFunction(node) || ts2.isFunctionExpression(node)) return node;
|
|
650
|
+
if (!ts2.isObjectLiteralExpression(node)) return;
|
|
651
|
+
for (const property of [...node.properties].reverse()) {
|
|
652
|
+
if (ts2.isSpreadAssignment(property)) {
|
|
653
|
+
const render = renderFor(property.expression, new Set(seen));
|
|
654
|
+
if (render !== void 0) return render;
|
|
655
|
+
} else if (property.name && (ts2.isIdentifier(property.name) || ts2.isStringLiteral(property.name)) && property.name.text === "render") {
|
|
656
|
+
if (ts2.isMethodDeclaration(property)) return property;
|
|
657
|
+
if (ts2.isPropertyAssignment(property)) return renderFor(property.initializer, seen) ?? null;
|
|
658
|
+
if (ts2.isShorthandPropertyAssignment(property)) return renderFor(property.name, seen) ?? null;
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const hasJsx = (node) => ts2.isJsxElement(node) || ts2.isJsxSelfClosingElement(node) || ts2.isJsxFragment(node) || (ts2.forEachChild(node, hasJsx) ?? false);
|
|
664
|
+
const returnsCache = /* @__PURE__ */ new Map();
|
|
665
|
+
const returnCounts = /* @__PURE__ */ new Map();
|
|
666
|
+
const returnedJsx = (render) => {
|
|
667
|
+
const cached = returnsCache.get(render);
|
|
668
|
+
if (cached) return cached;
|
|
669
|
+
const expressions = [];
|
|
670
|
+
if (!render.body) return expressions;
|
|
671
|
+
let returnCount = 0;
|
|
672
|
+
if (ts2.isBlock(render.body)) {
|
|
673
|
+
const visit = (node) => {
|
|
674
|
+
if (ts2.isFunctionLike(node)) return;
|
|
675
|
+
if (ts2.isReturnStatement(node)) {
|
|
676
|
+
returnCount += 1;
|
|
677
|
+
if (node.expression && hasJsx(node.expression)) expressions.push(unwrap(node.expression));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
ts2.forEachChild(node, visit);
|
|
681
|
+
};
|
|
682
|
+
ts2.forEachChild(render.body, visit);
|
|
683
|
+
} else {
|
|
684
|
+
returnCount = 1;
|
|
685
|
+
if (hasJsx(render.body)) expressions.push(unwrap(render.body));
|
|
686
|
+
}
|
|
687
|
+
returnsCache.set(render, expressions);
|
|
688
|
+
returnCounts.set(render, returnCount);
|
|
689
|
+
return expressions;
|
|
690
|
+
};
|
|
691
|
+
const helperFor = (name, seen = /* @__PURE__ */ new Set()) => {
|
|
692
|
+
if (seen.has(name)) return;
|
|
693
|
+
seen.add(name);
|
|
694
|
+
const declaration = declarations.get(name);
|
|
695
|
+
if (!declaration) return;
|
|
696
|
+
if (ts2.isFunctionDeclaration(declaration)) return declaration;
|
|
697
|
+
const expression = unwrap(declaration);
|
|
698
|
+
if (ts2.isIdentifier(expression)) return helperFor(expression.text, seen);
|
|
699
|
+
if (ts2.isArrowFunction(expression) || ts2.isFunctionExpression(expression)) return expression;
|
|
700
|
+
};
|
|
701
|
+
const bindingContains = (binding, name) => ts2.isIdentifier(binding) ? binding.text === name : binding.elements.some((element) => ts2.isBindingElement(element) && bindingContains(element.name, name));
|
|
702
|
+
const isShadowed = (expression, name) => {
|
|
703
|
+
for (let node = expression.parent; node; node = node.parent) {
|
|
704
|
+
if (ts2.isFunctionLike(node) && node.parameters.some((parameter) => bindingContains(parameter.name, name))) return true;
|
|
705
|
+
if (ts2.isBlock(node) && node.statements.some((statement) => ts2.isVariableStatement(statement) && statement.declarationList.declarations.some((declaration) => bindingContains(declaration.name, name)) || ts2.isFunctionDeclaration(statement) && statement.name?.text === name)) return true;
|
|
706
|
+
}
|
|
707
|
+
return false;
|
|
708
|
+
};
|
|
709
|
+
const mapCache = /* @__PURE__ */ new Map();
|
|
710
|
+
const hasUnknownMap = (helper, jsx) => {
|
|
711
|
+
const cached = mapCache.get(jsx);
|
|
712
|
+
if (cached !== void 0) return cached;
|
|
713
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
714
|
+
for (const [name, declaration] of declarations) {
|
|
715
|
+
bindings.set(name, ts2.isFunctionDeclaration(declaration) ? void 0 : declaration);
|
|
716
|
+
}
|
|
717
|
+
const bind = (scope2, name, initializer) => {
|
|
718
|
+
if (ts2.isIdentifier(name)) scope2.set(name.text, initializer);
|
|
719
|
+
else for (const element of name.elements) {
|
|
720
|
+
if (ts2.isBindingElement(element)) bind(scope2, element.name, element.initializer);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
const addScope = (node, parent) => {
|
|
724
|
+
const scope2 = new Map(parent);
|
|
725
|
+
if (ts2.isFunctionLike(node)) {
|
|
726
|
+
for (const parameter of node.parameters) bind(scope2, parameter.name, parameter.initializer);
|
|
727
|
+
}
|
|
728
|
+
if (ts2.isBlock(node)) {
|
|
729
|
+
for (const statement of node.statements) {
|
|
730
|
+
if (ts2.isVariableStatement(statement)) {
|
|
731
|
+
for (const declaration of statement.declarationList.declarations) bind(scope2, declaration.name, declaration.initializer);
|
|
732
|
+
} else if (ts2.isFunctionDeclaration(statement) && statement.name) scope2.set(statement.name.text, void 0);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return scope2;
|
|
736
|
+
};
|
|
737
|
+
const ancestors = [];
|
|
738
|
+
for (let node = jsx.parent; node !== helper; node = node.parent) ancestors.push(node);
|
|
739
|
+
let scope = addScope(helper, bindings);
|
|
740
|
+
for (const node of ancestors.reverse()) scope = addScope(node, scope);
|
|
741
|
+
const knownCollection = (expression, bindings2, seen = /* @__PURE__ */ new Set()) => {
|
|
742
|
+
expression = unwrap(expression);
|
|
743
|
+
if (ts2.isArrayLiteralExpression(expression)) {
|
|
744
|
+
return expression.elements.every((element) => !ts2.isSpreadElement(element) || knownCollection(element.expression, bindings2, new Set(seen)));
|
|
745
|
+
}
|
|
746
|
+
if (ts2.isIdentifier(expression)) {
|
|
747
|
+
if (seen.has(expression.text)) return false;
|
|
748
|
+
seen.add(expression.text);
|
|
749
|
+
const initializer = bindings2.get(expression.text);
|
|
750
|
+
return Boolean(initializer && knownCollection(initializer, bindings2, seen));
|
|
751
|
+
}
|
|
752
|
+
if (ts2.isCallExpression(expression) && ts2.isPropertyAccessExpression(expression.expression) && ["slice", "filter", "toSorted", "toReversed"].includes(expression.expression.name.text)) {
|
|
753
|
+
return knownCollection(expression.expression.expression, bindings2, seen);
|
|
754
|
+
}
|
|
755
|
+
return false;
|
|
756
|
+
};
|
|
757
|
+
const visit = (node, bindings2) => {
|
|
758
|
+
if (ts2.isFunctionLike(node) || ts2.isBlock(node)) bindings2 = addScope(node, bindings2);
|
|
759
|
+
if (ts2.isCallExpression(node)) {
|
|
760
|
+
const target = node.expression;
|
|
761
|
+
const map = ts2.isPropertyAccessExpression(target) && target.name.text === "map" || ts2.isElementAccessExpression(target) && ts2.isStringLiteral(target.argumentExpression) && target.argumentExpression.text === "map";
|
|
762
|
+
if (map && !knownCollection(target.expression, bindings2)) return true;
|
|
763
|
+
}
|
|
764
|
+
return ts2.forEachChild(node, (child) => visit(child, bindings2)) ?? false;
|
|
765
|
+
};
|
|
766
|
+
const unknown = visit(jsx, scope);
|
|
767
|
+
mapCache.set(jsx, unknown);
|
|
768
|
+
return unknown;
|
|
769
|
+
};
|
|
770
|
+
const inlineRoot = (expression, detail, trail = []) => {
|
|
771
|
+
const tag = ts2.isJsxElement(expression) ? expression.openingElement.tagName : ts2.isJsxSelfClosingElement(expression) ? expression.tagName : void 0;
|
|
772
|
+
const name = tag && ts2.isIdentifier(tag) && /^[A-Z]/.test(tag.text) ? tag.text : void 0;
|
|
773
|
+
const helper = name && helperFor(name);
|
|
774
|
+
if (!name || !declarations.has(name)) return expression.getText(file);
|
|
775
|
+
if (!helper) {
|
|
776
|
+
const reason2 = "local definition is not a direct function";
|
|
777
|
+
detail.remaining.push({ name, reason: reason2 });
|
|
778
|
+
return `// ${name} not expanded: ${reason2}.
|
|
779
|
+
${expression.getText(file)}`;
|
|
780
|
+
}
|
|
781
|
+
const returned = returnedJsx(helper);
|
|
782
|
+
const jsx = returned[0];
|
|
783
|
+
const reason = isShadowed(expression, name) ? "file helper is shadowed in this scope" : trail.includes(name) ? "recursive helper cycle" : trail.length >= MAX_HELPER_DEPTH ? `helper depth limit (${MAX_HELPER_DEPTH})` : returnCounts.get(helper) !== 1 || returned.length !== 1 || !jsx || !(ts2.isJsxElement(jsx) || ts2.isJsxSelfClosingElement(jsx) || ts2.isJsxFragment(jsx)) ? "no single literal JSX return" : hasUnknownMap(helper, jsx) ? "maps over a collection without a local array definition" : void 0;
|
|
784
|
+
if (reason) {
|
|
785
|
+
detail.remaining.push({ name, reason });
|
|
786
|
+
return `// ${name} not expanded: ${reason}.
|
|
787
|
+
${expression.getText(file)}`;
|
|
788
|
+
}
|
|
789
|
+
detail.inlined.push(name);
|
|
790
|
+
return `// JSX from ${name}, as authored.
|
|
791
|
+
${inlineRoot(jsx, detail, [...trail, name])}`;
|
|
792
|
+
};
|
|
793
|
+
const sources = {};
|
|
794
|
+
const helpers = {};
|
|
795
|
+
for (const [name, local] of exports) {
|
|
796
|
+
const declaration = declarations.get(local);
|
|
797
|
+
const render = declaration && renderFor(declaration);
|
|
798
|
+
if (!render?.body) continue;
|
|
799
|
+
const expressions = returnedJsx(render);
|
|
800
|
+
if (expressions.length) {
|
|
801
|
+
const detail = { inlined: [], remaining: [] };
|
|
802
|
+
const jsx = expressions.map((expression) => inlineRoot(expression, detail)).join("\n\n");
|
|
803
|
+
sources[name] = expressions.length > 1 ? `// JSX return branches as authored in the story.
|
|
804
|
+
${jsx}` : jsx;
|
|
805
|
+
if (detail.inlined.length || detail.remaining.length) helpers[name] = detail;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return { sources, helpers };
|
|
809
|
+
}
|
|
810
|
+
function extractStorySources(source) {
|
|
811
|
+
return extractStorySourceDetails(source).sources;
|
|
812
|
+
}
|
|
813
|
+
function storySourcesIndexCode(slugs) {
|
|
814
|
+
return `
|
|
815
|
+
export function loadStorySources(slug) {
|
|
816
|
+
switch (slug) {
|
|
817
|
+
${[...slugs].map((slug) => ` case ${JSON.stringify(slug)}: return import(${JSON.stringify(`${VIRTUAL_PREFIX}story-sources/${slug}`)});`).join("\n")}
|
|
818
|
+
default: return Promise.resolve({ sources: {} });
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
`;
|
|
822
|
+
}
|
|
823
|
+
function storySourcesCode(sources) {
|
|
824
|
+
return `export const sources = ${JSON.stringify(sources)};`;
|
|
825
|
+
}
|
|
826
|
+
|
|
611
827
|
// src/vite-plugin.ts
|
|
612
828
|
function storybookDocs(options) {
|
|
613
829
|
const { config } = options;
|
|
@@ -616,6 +832,7 @@ function storybookDocs(options) {
|
|
|
616
832
|
catalog.files.clear();
|
|
617
833
|
const engineStyles = path7.join(import.meta.dirname, "styles.css");
|
|
618
834
|
const propsCache = /* @__PURE__ */ new Map();
|
|
835
|
+
const storySourcesCache = /* @__PURE__ */ new Map();
|
|
619
836
|
const propsCollector = createPropsCollector();
|
|
620
837
|
let guidanceFiles = /* @__PURE__ */ new Map();
|
|
621
838
|
const loadProps = (slug) => {
|
|
@@ -636,6 +853,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
636
853
|
"stub-storybook-test": () => STORYBOOK_TEST_STUB,
|
|
637
854
|
props: () => propsIndexCode(catalog.entries.map((entry) => entry.slug)),
|
|
638
855
|
guidance: () => guidanceIndexCode(guidanceFiles.keys()),
|
|
856
|
+
"story-sources": () => storySourcesIndexCode(catalog.files.keys()),
|
|
639
857
|
releases: () => releasesCode(readReleases(config, projectRoot)),
|
|
640
858
|
code: () => codeModuleCode(resolveCodeModule(config.code?.module, projectRoot))
|
|
641
859
|
};
|
|
@@ -665,6 +883,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
665
883
|
},
|
|
666
884
|
buildStart() {
|
|
667
885
|
propsCache.clear();
|
|
886
|
+
storySourcesCache.clear();
|
|
668
887
|
propsCollector.clearPrograms();
|
|
669
888
|
},
|
|
670
889
|
closeBundle() {
|
|
@@ -682,11 +901,26 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
682
901
|
const module = Object.hasOwn(modules, name) ? modules[name] : void 0;
|
|
683
902
|
if (module) return module();
|
|
684
903
|
if (name.startsWith("props/")) return loadProps(name.slice("props/".length));
|
|
904
|
+
if (name.startsWith("story-sources/")) {
|
|
905
|
+
const storyFile = catalog.files.get(name.slice("story-sources/".length));
|
|
906
|
+
if (!storyFile) return storySourcesCode({});
|
|
907
|
+
const file = realpathSync(storyFile);
|
|
908
|
+
let sources = storySourcesCache.get(file);
|
|
909
|
+
if (!sources) {
|
|
910
|
+
sources = extractStorySources(readFileSync3(file, "utf8"));
|
|
911
|
+
storySourcesCache.set(file, sources);
|
|
912
|
+
}
|
|
913
|
+
this.addWatchFile(file);
|
|
914
|
+
return storySourcesCode(sources);
|
|
915
|
+
}
|
|
685
916
|
if (name.startsWith("guidance/")) {
|
|
686
917
|
const file = guidanceFiles.get(name.slice("guidance/".length));
|
|
687
918
|
return guidanceCode(file ? readFileSync3(file, "utf8") : "");
|
|
688
919
|
}
|
|
689
920
|
return null;
|
|
921
|
+
},
|
|
922
|
+
watchChange(file) {
|
|
923
|
+
storySourcesCache.delete(file);
|
|
690
924
|
}
|
|
691
925
|
};
|
|
692
926
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jfrader/storybook-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A documentation front for any design system that has Storybook: navigation, live examples and API tables are generated from the story files themselves.",
|
|
6
6
|
"exports": {
|