@jfrader/storybook-docs 0.4.3 → 0.4.4

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/css.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '*.css';
package/dist/index.css CHANGED
@@ -114,11 +114,8 @@
114
114
  display: block;
115
115
  grid-column: 3;
116
116
  grid-row: 2;
117
- position: sticky;
118
- top: var(--sd-header-height, 56px);
119
117
  align-self: start;
120
- max-height: calc(100dvh - var(--sd-header-height, 56px));
121
- overflow-y: auto;
118
+ transform: translateY(var(--sd-rail-follow, 0px));
122
119
  padding: 34px 18px 40px 6px;
123
120
  font-size: 12.5px;
124
121
  }
@@ -173,13 +170,12 @@
173
170
  margin: 0;
174
171
  padding: 0 20px 18px;
175
172
  min-width: 0;
176
- border: 0;
177
173
  background: none;
178
174
  color: var(--sd-text-muted);
179
175
  font-size: 13px;
180
176
  }
181
- .sd-controls legend {
182
- padding: 0;
177
+ .sd-controls-label {
178
+ margin: 16px 0 10px;
183
179
  font-size: 11px;
184
180
  font-weight: 600;
185
181
  letter-spacing: 0.06em;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference path="./virtual.d.ts" />
2
+ /// <reference path="./css.d.ts" />
2
3
  export { start } from './start.js';
3
4
  export { ComponentDemo, type ComponentDemoProps } from './component-demo.js';
4
5
  export type { StorybookDocsConfig, CatalogEntry, ThemeRole } from './config.js';
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { site as site3, themeCss, theme as theme2 } from "virtual:storybook-docs
6
6
 
7
7
  // src/app.tsx
8
8
  import { useEffect as useEffect7, useRef as useRef5, useState as useState8 } from "react";
9
- import { catalog as catalog3, groups as groups2, stats as stats2 } from "virtual:storybook-docs/catalog";
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
@@ -531,6 +531,39 @@ function storyLink(location, story) {
531
531
  return `${base}${hashPath(location.hash)}?${new URLSearchParams({ [STORY_PARAM]: story })}`;
532
532
  }
533
533
  var slugify = (text) => text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
534
+ function useRailFollow(ref, ready = true) {
535
+ useEffect3(() => {
536
+ const element = ref.current;
537
+ if (!element) return;
538
+ let frame = 0;
539
+ let applied = 0;
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
+ };
565
+ }, [ready, ref]);
566
+ }
534
567
  function scanPage(root, pageKey) {
535
568
  const used = /* @__PURE__ */ new Set();
536
569
  const unique = (prefix, text) => {
@@ -542,9 +575,12 @@ function scanPage(root, pageKey) {
542
575
  };
543
576
  const sections = [];
544
577
  const frames = [];
545
- const subHeadings = [...root.querySelectorAll("h3")].filter((heading) => heading.textContent);
578
+ const subHeadings = [...root.querySelectorAll("h3")].filter(
579
+ (heading) => heading.textContent && !heading.closest(".story-lane") && !heading.closest(".frame")
580
+ );
546
581
  root.querySelectorAll("h2").forEach((heading) => {
547
582
  if (!heading.textContent) return;
583
+ if (heading.closest(".story-lane") || heading.closest(".frame")) return;
548
584
  heading.id ||= unique("section", heading.textContent);
549
585
  heading.dataset.toc = "";
550
586
  const section = heading.parentElement?.tagName === "SECTION" ? heading.parentElement : null;
@@ -570,6 +606,13 @@ function scanPage(root, pageKey) {
570
606
  });
571
607
  sections.push({ id: heading.id, title: heading.textContent, element: section ?? heading, heading, stories, subs });
572
608
  });
609
+ const counts = /* @__PURE__ */ new Map();
610
+ for (const item of frames) counts.set(item.name, (counts.get(item.name) ?? 0) + 1);
611
+ for (const item of frames) {
612
+ if ((counts.get(item.name) ?? 0) > 1 && item.story && item.story !== item.name) {
613
+ item.name = `${item.name} \xB7 ${item.story}`;
614
+ }
615
+ }
573
616
  return { pageKey, sections, frames };
574
617
  }
575
618
  var signature = (scan) => `${scan.pageKey}|${scan.sections.map((s) => `${s.id}(${s.subs.map((sub) => sub.id).join("+")}):${s.stories.map((st) => `${st.id}:${st.name}`).join(",")}`).join(";")}`;
@@ -657,10 +700,12 @@ function PageRail({ pageKey }) {
657
700
  window.setTimeout(() => match.element.classList.remove("toc-flash"), FLASH_MS);
658
701
  setTarget(null);
659
702
  }, [target, scan, pageKey]);
703
+ const railRef = useRef2(null);
704
+ useRailFollow(railRef, scan !== null);
660
705
  const active = useActiveIds(scan);
661
706
  const go = useCallback((element) => scrollToElement(element), []);
662
707
  if (!scan) return null;
663
- return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", children: [
708
+ return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
664
709
  /* @__PURE__ */ jsx5("div", { className: "toc-title", children: "On this page" }),
665
710
  /* @__PURE__ */ jsx5("ul", { children: scan.sections.map((section) => /* @__PURE__ */ jsxs3("li", { children: [
666
711
  /* @__PURE__ */ jsx5(
@@ -799,6 +844,7 @@ function ComponentDemo({
799
844
  children
800
845
  }) {
801
846
  const [codeOpen, setCodeOpen] = useState4(false);
847
+ const controlsLabelId = useId();
802
848
  const canvasRef = useRef3(null);
803
849
  const [scrollable, setScrollable] = useState4(false);
804
850
  useEffect4(() => {
@@ -844,8 +890,8 @@ function ComponentDemo({
844
890
  skipped.join(", "),
845
891
  ". See it in Storybook."
846
892
  ] }) : null,
847
- unavailable ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: unavailable }) : /* @__PURE__ */ jsxs4("fieldset", { className: "sd-controls", children: [
848
- /* @__PURE__ */ jsxs4("legend", { children: [
893
+ unavailable ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: unavailable }) : /* @__PURE__ */ jsxs4("div", { className: "sd-controls", role: "group", "aria-labelledby": controlsLabelId, children: [
894
+ /* @__PURE__ */ jsxs4("p", { className: "sd-controls-label", id: controlsLabelId, children: [
849
895
  "Controls for ",
850
896
  storyName
851
897
  ] }),
@@ -1756,6 +1802,8 @@ function DocsApp() {
1756
1802
  const [route, setRoute] = useState8(() => parseRoute(window.location.hash));
1757
1803
  const [mode, setMode] = useState8(theme.values?.[0] ?? "dark");
1758
1804
  const [navOpen, setNavOpen] = useState8(false);
1805
+ const navRef = useRef5(null);
1806
+ useRailFollow(navRef);
1759
1807
  const headerRef = useRef5(null);
1760
1808
  const mainRef = useRef5(null);
1761
1809
  useEffect7(() => {
@@ -1845,7 +1893,7 @@ function DocsApp() {
1845
1893
  /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u2197" })
1846
1894
  ] }, link.href))
1847
1895
  ] }),
1848
- /* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", children: [
1896
+ /* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
1849
1897
  /* @__PURE__ */ jsx16("a", { href: routeHref("/"), className: route.kind === "overview" ? "on" : void 0, "aria-current": route.kind === "overview" ? "page" : void 0, children: "Overview" }),
1850
1898
  releases2.changelog ? /* @__PURE__ */ jsxs13(
1851
1899
  "a",
@@ -1889,14 +1937,7 @@ function DocsApp() {
1889
1937
  },
1890
1938
  entry.slug
1891
1939
  ))
1892
- ] }, group)),
1893
- /* @__PURE__ */ jsxs13("div", { className: "nav-note", children: [
1894
- "navigation, examples and API tables generated from ",
1895
- stats2.components,
1896
- " story files \xB7 ",
1897
- stats2.stories,
1898
- " stories"
1899
- ] })
1940
+ ] }, group))
1900
1941
  ] }),
1901
1942
  /* @__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, {}) }),
1902
1943
  route.kind !== "overview" ? /* @__PURE__ */ jsx16(PageRail, { pageKey: activeSlug ?? route.kind }) : null
package/dist/styles.css CHANGED
@@ -190,6 +190,9 @@ body {
190
190
  border-right: 0;
191
191
  background: var(--sd-surface);
192
192
  padding: 14px 12px 80px;
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));
193
196
  }
194
197
  .nav a {
195
198
  display: block;
@@ -217,12 +220,6 @@ body {
217
220
  color: var(--sd-text-muted);
218
221
  font-weight: 700;
219
222
  }
220
- .nav-note {
221
- margin: 22px 10px 0;
222
- font-size: 11.5px;
223
- line-height: 1.6;
224
- color: var(--sd-text-muted);
225
- }
226
223
 
227
224
  .main {
228
225
  padding: 48px clamp(28px, 4vw, 64px) 160px;
package/dist/toc.d.ts CHANGED
@@ -1,9 +1,18 @@
1
+ /** @jsxImportSource react */
2
+ import { type RefObject } from 'react';
1
3
  import './toc.css';
2
4
  /** The hash without its query: `#/components/button?story=Variants` → `#/components/button`. */
3
5
  export declare function hashPath(hash: string): string;
4
6
  /** The `?story=` deep-link target of a hash, if any. */
5
7
  export declare function storyFromHash(hash: string): string | null;
6
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
+ export declare function useRailFollow(ref: RefObject<HTMLElement | null>, ready?: boolean): void;
7
16
  /** The "On this page" rail and the `?story=` deep-link landing. */
8
17
  export declare function PageRail({ pageKey }: {
9
18
  pageKey: string;
package/docs/RELEASING.md CHANGED
@@ -125,3 +125,29 @@ Official npm references (check them when changing authentication):
125
125
  [trusted publishers](https://docs.npmjs.com/trusted-publishers/),
126
126
  [provenance](https://docs.npmjs.com/generating-provenance-statements/),
127
127
  [granular tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens/).
128
+
129
+ ## First publish of a new package name
130
+
131
+ Established procedure, after a morning spent rediscovering it:
132
+
133
+ 1. **`npm trust` works before the package exists.** `npm trust github <pkg> --repo <owner>/<repo> --file publish.yml --allow-publish`
134
+ returns **201** even for a name that has never been published, as long as you are authenticated. Do this first; it is
135
+ what lets every later `v*` tag publish itself through OIDC with provenance, no tokens.
136
+ 2. **Workflows that set `NODE_AUTH_TOKEN` cannot use trusted publishing.** An empty secret sends an
137
+ unauthenticated request and npm answers `404` on the `PUT`, which reads like a missing package. This workflow
138
+ therefore sets no token.
139
+ 3. **The first publish has to happen once from a machine** — the registry has no artifact yet:
140
+
141
+ ```bash
142
+ cd <the package checkout>
143
+ npm login --registry https://registry.npmjs.org # prints a browser URL; no OTP code needed
144
+ NPM_CONFIG_USERCONFIG=<config> npm publish # browser URL again, then "+ <pkg>@<version>"
145
+ ```
146
+
147
+ A `~/.npmrc` that maps the scope to another registry (e.g. `@scope:registry=https://npm.pkg.github.com/`) makes a
148
+ plain `npm publish` target that registry instead and fail with `403`. Point the scope at npmjs in the config used
149
+ for the publish.
150
+ 4. **Verify from the registry, not from the local output:** `curl -s https://registry.npmjs.org/<scope>%2f<pkg>`
151
+ must list the version. Until it does, nothing is published.
152
+ 5. After that, releases are tags: `git tag -a vX.Y.Z && git push origin vX.Y.Z`, and the publish workflow does the
153
+ rest. Do **not** push a tag for a version you already published by hand — npm rejects the duplicate.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfrader/storybook-docs",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
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": {
@@ -27,12 +27,6 @@
27
27
  "react-dom": "^19.3.0",
28
28
  "vite": "^8.3.4"
29
29
  },
30
- "packageManager": "pnpm@10.12.1",
31
- "pnpm": {
32
- "onlyBuiltDependencies": [
33
- "esbuild"
34
- ]
35
- },
36
30
  "dependencies": {
37
31
  "marked": "^18.1.1",
38
32
  "react-docgen-typescript": "^2.4.0",
@@ -60,15 +54,6 @@
60
54
  "props",
61
55
  "docs"
62
56
  ],
63
- "scripts": {
64
- "test": "node --test test/*.test.ts",
65
- "typecheck": "tsc --noEmit",
66
- "build": "pnpm build:package && pnpm --dir examples/jfrader-ui build",
67
- "build:package": "node scripts/build.mjs",
68
- "smoke:package": "node scripts/smoke-package.mjs",
69
- "check:frames": "node scripts/check-example-frames.mjs",
70
- "prepack": "pnpm build:package"
71
- },
72
57
  "engines": {
73
58
  "node": ">=22.12.0"
74
59
  },
@@ -81,5 +66,13 @@
81
66
  ],
82
67
  "sideEffects": [
83
68
  "**/*.css"
84
- ]
85
- }
69
+ ],
70
+ "scripts": {
71
+ "test": "node --test test/*.test.ts",
72
+ "typecheck": "tsc --noEmit",
73
+ "build": "pnpm build:package && pnpm --dir examples/jfrader-ui build",
74
+ "build:package": "node scripts/build.mjs",
75
+ "smoke:package": "node scripts/smoke-package.mjs",
76
+ "check:frames": "node scripts/check-example-frames.mjs"
77
+ }
78
+ }