@rtkelly-labs/design-system 0.0.0-bootstrap.0 → 0.16.0-dev.0.37dedac
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/CHANGELOG.md +347 -0
- package/LICENSE +21 -0
- package/README.md +317 -2
- package/dist/components/AlertDialog.js +95 -0
- package/dist/components/AlertDialog.js.map +1 -0
- package/dist/components/AppShell.js +261 -0
- package/dist/components/AppShell.js.map +1 -0
- package/dist/components/AsciiDivider.js +8 -0
- package/dist/components/AsciiDivider.js.map +1 -0
- package/dist/components/AsciiGauge.js +130 -0
- package/dist/components/AsciiGauge.js.map +1 -0
- package/dist/components/Avatar.js +87 -0
- package/dist/components/Avatar.js.map +1 -0
- package/dist/components/Badge.js +25 -0
- package/dist/components/Badge.js.map +1 -0
- package/dist/components/BarChart.js +288 -0
- package/dist/components/BarChart.js.map +1 -0
- package/dist/components/BracketText.js +23 -0
- package/dist/components/BracketText.js.map +1 -0
- package/dist/components/BulletChart.js +50 -0
- package/dist/components/BulletChart.js.map +1 -0
- package/dist/components/Button.js +130 -0
- package/dist/components/Button.js.map +1 -0
- package/dist/components/Card.js +181 -0
- package/dist/components/Card.js.map +1 -0
- package/dist/components/ChartTooltip.js +54 -0
- package/dist/components/ChartTooltip.js.map +1 -0
- package/dist/components/Checkbox.js +55 -0
- package/dist/components/Checkbox.js.map +1 -0
- package/dist/components/ColophonCard.js +122 -0
- package/dist/components/ColophonCard.js.map +1 -0
- package/dist/components/DataTable.js +299 -0
- package/dist/components/DataTable.js.map +1 -0
- package/dist/components/Divider.js +42 -0
- package/dist/components/Divider.js.map +1 -0
- package/dist/components/Drawer.js +81 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/EmptyState.js +39 -0
- package/dist/components/EmptyState.js.map +1 -0
- package/dist/components/ErrorSummary.js +122 -0
- package/dist/components/ErrorSummary.js.map +1 -0
- package/dist/components/Fieldset.js +37 -0
- package/dist/components/Fieldset.js.map +1 -0
- package/dist/components/Glyph.js +60 -0
- package/dist/components/Glyph.js.map +1 -0
- package/dist/components/Input.js +111 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/LinkProvider.js +76 -0
- package/dist/components/LinkProvider.js.map +1 -0
- package/dist/components/Menu.js +144 -0
- package/dist/components/Menu.js.map +1 -0
- package/dist/components/MetadataGrid.js +184 -0
- package/dist/components/MetadataGrid.js.map +1 -0
- package/dist/components/MobileNav.js +120 -0
- package/dist/components/MobileNav.js.map +1 -0
- package/dist/components/Modal.js +79 -0
- package/dist/components/Modal.js.map +1 -0
- package/dist/components/NerdIcon.js +177 -0
- package/dist/components/NerdIcon.js.map +1 -0
- package/dist/components/NoteBlock.js +84 -0
- package/dist/components/NoteBlock.js.map +1 -0
- package/dist/components/PageHeader.js +34 -0
- package/dist/components/PageHeader.js.map +1 -0
- package/dist/components/PageTitle.js +21 -0
- package/dist/components/PageTitle.js.map +1 -0
- package/dist/components/Pagination.js +96 -0
- package/dist/components/Pagination.js.map +1 -0
- package/dist/components/Popover.js +87 -0
- package/dist/components/Popover.js.map +1 -0
- package/dist/components/Progress.js +83 -0
- package/dist/components/Progress.js.map +1 -0
- package/dist/components/RadioGroup.js +74 -0
- package/dist/components/RadioGroup.js.map +1 -0
- package/dist/components/ReportDocument.js +75 -0
- package/dist/components/ReportDocument.js.map +1 -0
- package/dist/components/SectionContainer.js +18 -0
- package/dist/components/SectionContainer.js.map +1 -0
- package/dist/components/Select.js +199 -0
- package/dist/components/Select.js.map +1 -0
- package/dist/components/SiteFooter.js +29 -0
- package/dist/components/SiteFooter.js.map +1 -0
- package/dist/components/SiteHeader.js +59 -0
- package/dist/components/SiteHeader.js.map +1 -0
- package/dist/components/SiteNav.js +141 -0
- package/dist/components/SiteNav.js.map +1 -0
- package/dist/components/Skeleton.js +51 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/SocialIcon.js +77 -0
- package/dist/components/SocialIcon.js.map +1 -0
- package/dist/components/Sparkline.js +104 -0
- package/dist/components/Sparkline.js.map +1 -0
- package/dist/components/Spinner.js +65 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/StatCard.js +53 -0
- package/dist/components/StatCard.js.map +1 -0
- package/dist/components/StatusPage.js +99 -0
- package/dist/components/StatusPage.js.map +1 -0
- package/dist/components/Swatch.js +55 -0
- package/dist/components/Swatch.js.map +1 -0
- package/dist/components/Switch.js +53 -0
- package/dist/components/Switch.js.map +1 -0
- package/dist/components/TLDR.js +53 -0
- package/dist/components/TLDR.js.map +1 -0
- package/dist/components/Table.js +155 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Tabs.js +325 -0
- package/dist/components/Tabs.js.map +1 -0
- package/dist/components/Tag.js +19 -0
- package/dist/components/Tag.js.map +1 -0
- package/dist/components/TelemetryGauge.js +112 -0
- package/dist/components/TelemetryGauge.js.map +1 -0
- package/dist/components/ThemeProvider.js +87 -0
- package/dist/components/ThemeProvider.js.map +1 -0
- package/dist/components/TickerTape.js +196 -0
- package/dist/components/TickerTape.js.map +1 -0
- package/dist/components/Toast.js +200 -0
- package/dist/components/Toast.js.map +1 -0
- package/dist/components/Tooltip.js +69 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/components/TrackCard.js +206 -0
- package/dist/components/TrackCard.js.map +1 -0
- package/dist/components/blog/BlogPost.js +116 -0
- package/dist/components/blog/BlogPost.js.map +1 -0
- package/dist/components/blog/LoremIpsumPost.js +62 -0
- package/dist/components/blog/LoremIpsumPost.js.map +1 -0
- package/dist/components/blog/author.js +25 -0
- package/dist/components/blog/author.js.map +1 -0
- package/dist/components/booleanControl.js +50 -0
- package/dist/components/booleanControl.js.map +1 -0
- package/dist/components/dialogPopupRef.js +28 -0
- package/dist/components/dialogPopupRef.js.map +1 -0
- package/dist/components/dialogSurface.js +75 -0
- package/dist/components/dialogSurface.js.map +1 -0
- package/dist/components/docs/AnchorHeading.js +80 -0
- package/dist/components/docs/AnchorHeading.js.map +1 -0
- package/dist/components/docs/Breadcrumbs.js +26 -0
- package/dist/components/docs/Breadcrumbs.js.map +1 -0
- package/dist/components/docs/CodeBlock.js +63 -0
- package/dist/components/docs/CodeBlock.js.map +1 -0
- package/dist/components/docs/CodeTabs.js +71 -0
- package/dist/components/docs/CodeTabs.js.map +1 -0
- package/dist/components/docs/DocPager.js +28 -0
- package/dist/components/docs/DocPager.js.map +1 -0
- package/dist/components/docs/DocsHeader.js +124 -0
- package/dist/components/docs/DocsHeader.js.map +1 -0
- package/dist/components/docs/DocsLayout.js +61 -0
- package/dist/components/docs/DocsLayout.js.map +1 -0
- package/dist/components/docs/DocsLinkProvider.js +25 -0
- package/dist/components/docs/DocsLinkProvider.js.map +1 -0
- package/dist/components/docs/DocsSidebar.js +147 -0
- package/dist/components/docs/DocsSidebar.js.map +1 -0
- package/dist/components/docs/Prose.js +56 -0
- package/dist/components/docs/Prose.js.map +1 -0
- package/dist/components/docs/TableOfContents.js +71 -0
- package/dist/components/docs/TableOfContents.js.map +1 -0
- package/dist/components/docs/codeTabsStore.js +75 -0
- package/dist/components/docs/codeTabsStore.js.map +1 -0
- package/dist/components/docs/figures/ActivityGrid.js +116 -0
- package/dist/components/docs/figures/ActivityGrid.js.map +1 -0
- package/dist/components/docs/figures/BeforeAfter.js +64 -0
- package/dist/components/docs/figures/BeforeAfter.js.map +1 -0
- package/dist/components/docs/figures/ChangeSummary.js +74 -0
- package/dist/components/docs/figures/ChangeSummary.js.map +1 -0
- package/dist/components/docs/figures/Decision.js +34 -0
- package/dist/components/docs/figures/Decision.js.map +1 -0
- package/dist/components/docs/figures/DrawingSheet.js +37 -0
- package/dist/components/docs/figures/DrawingSheet.js.map +1 -0
- package/dist/components/docs/figures/FigureFrame.js +226 -0
- package/dist/components/docs/figures/FigureFrame.js.map +1 -0
- package/dist/components/docs/figures/FileChangeTree.js +134 -0
- package/dist/components/docs/figures/FileChangeTree.js.map +1 -0
- package/dist/components/docs/figures/FlowDiagram.js +82 -0
- package/dist/components/docs/figures/FlowDiagram.js.map +1 -0
- package/dist/components/docs/figures/GanttChart.js +79 -0
- package/dist/components/docs/figures/GanttChart.js.map +1 -0
- package/dist/components/docs/figures/IndexedSection.js +45 -0
- package/dist/components/docs/figures/IndexedSection.js.map +1 -0
- package/dist/components/docs/figures/MetricStrip.js +23 -0
- package/dist/components/docs/figures/MetricStrip.js.map +1 -0
- package/dist/components/docs/figures/Steps.js +58 -0
- package/dist/components/docs/figures/Steps.js.map +1 -0
- package/dist/components/docs/figures/SwatchCompare.js +42 -0
- package/dist/components/docs/figures/SwatchCompare.js.map +1 -0
- package/dist/components/docs/figures/TermGrid.js +22 -0
- package/dist/components/docs/figures/TermGrid.js.map +1 -0
- package/dist/components/docs/figures/Terminal.js +37 -0
- package/dist/components/docs/figures/Terminal.js.map +1 -0
- package/dist/components/docs/figures/Timeline.js +61 -0
- package/dist/components/docs/figures/Timeline.js.map +1 -0
- package/dist/components/docs/figures/TokenTable.js +42 -0
- package/dist/components/docs/figures/TokenTable.js.map +1 -0
- package/dist/components/docs/figures/TreeDiagram.js +76 -0
- package/dist/components/docs/figures/TreeDiagram.js.map +1 -0
- package/dist/components/docs/figures/UptimeStrip.js +68 -0
- package/dist/components/docs/figures/UptimeStrip.js.map +1 -0
- package/dist/components/docs/figures/index.js +24 -0
- package/dist/components/docs/figures/index.js.map +1 -0
- package/dist/components/docs/index.js +15 -0
- package/dist/components/docs/index.js.map +1 -0
- package/dist/components/docs/mdxComponents.js +108 -0
- package/dist/components/docs/mdxComponents.js.map +1 -0
- package/dist/components/experiments/DesignSandbox.js +206 -0
- package/dist/components/experiments/DesignSandbox.js.map +1 -0
- package/dist/components/experiments/ExperimentsView.js +138 -0
- package/dist/components/experiments/ExperimentsView.js.map +1 -0
- package/dist/components/fieldFrame.js +200 -0
- package/dist/components/fieldFrame.js.map +1 -0
- package/dist/components/floatingSurface.js +35 -0
- package/dist/components/floatingSurface.js.map +1 -0
- package/dist/components/marketing/CTASection.js +47 -0
- package/dist/components/marketing/CTASection.js.map +1 -0
- package/dist/components/marketing/FeatureGrid.js +49 -0
- package/dist/components/marketing/FeatureGrid.js.map +1 -0
- package/dist/components/marketing/Hero.js +44 -0
- package/dist/components/marketing/Hero.js.map +1 -0
- package/dist/components/marketing/PricingGrid.js +65 -0
- package/dist/components/marketing/PricingGrid.js.map +1 -0
- package/dist/components/marketing/SectionGrid.js +51 -0
- package/dist/components/marketing/SectionGrid.js.map +1 -0
- package/dist/components/paginationRange.js +28 -0
- package/dist/components/paginationRange.js.map +1 -0
- package/dist/components/portalTheme.js +14 -0
- package/dist/components/portalTheme.js.map +1 -0
- package/dist/components/saas/SaasLandingPage.js +201 -0
- package/dist/components/saas/SaasLandingPage.js.map +1 -0
- package/dist/components/siteNavContext.js +24 -0
- package/dist/components/siteNavContext.js.map +1 -0
- package/dist/components/slides/Slide.js +84 -0
- package/dist/components/slides/Slide.js.map +1 -0
- package/dist/components/slides/SlideDeck.js +228 -0
- package/dist/components/slides/SlideDeck.js.map +1 -0
- package/dist/components/themeInitScript.js +16 -0
- package/dist/components/themeInitScript.js.map +1 -0
- package/dist/components/themeScope.js +9 -0
- package/dist/components/themeScope.js.map +1 -0
- package/dist/components/toastLifetime.js +21 -0
- package/dist/components/toastLifetime.js.map +1 -0
- package/dist/fonts/DSSymbols-OFL.txt +98 -0
- package/dist/fonts/DSSymbols-Regular.woff2 +0 -0
- package/dist/fonts/SymbolsNerdFontMono-Regular.woff2 +0 -0
- package/dist/hooks/useActiveHeading.js +59 -0
- package/dist/hooks/useActiveHeading.js.map +1 -0
- package/dist/hooks/useCopyToClipboard.js +58 -0
- package/dist/hooks/useCopyToClipboard.js.map +1 -0
- package/dist/index.d.ts +6394 -0
- package/dist/index.js +117 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/accentClasses.js +87 -0
- package/dist/lib/accentClasses.js.map +1 -0
- package/dist/lib/recipe.js +33 -0
- package/dist/lib/recipe.js.map +1 -0
- package/dist/lib/slug.js +39 -0
- package/dist/lib/slug.js.map +1 -0
- package/dist/lib/theme.js +76 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/lib/tokenRules.js +51 -0
- package/dist/lib/tokenRules.js.map +1 -0
- package/dist/prose.css +766 -0
- package/dist/styles.css +203 -0
- package/dist/theme/ansi.js +77 -0
- package/dist/theme/ansi.js.map +1 -0
- package/dist/theme/contrast.js +342 -0
- package/dist/theme/contrast.js.map +1 -0
- package/dist/theme/levels.js +234 -0
- package/dist/theme/levels.js.map +1 -0
- package/dist/theme/media.js +115 -0
- package/dist/theme/media.js.map +1 -0
- package/dist/theme/recommended.js +105 -0
- package/dist/theme/recommended.js.map +1 -0
- package/dist/theme.css +538 -0
- package/dist/tokens.js +1 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +209 -6
- package/skills/design-system-usage/SKILL.md +75 -0
- package/terminal/rtk-midnight.alacritty.toml +33 -0
- package/terminal/rtk-midnight.ghostty.conf +24 -0
- package/terminal/rtk-midnight.itermcolors +167 -0
- package/terminal/rtk-midnight.windows-terminal.json +23 -0
- package/terminal/rtk-sketch.alacritty.toml +33 -0
- package/terminal/rtk-sketch.ghostty.conf +24 -0
- package/terminal/rtk-sketch.itermcolors +167 -0
- package/terminal/rtk-sketch.windows-terminal.json +23 -0
- package/tokens/palette.midnight.tokens.json +477 -0
- package/tokens/palette.sketch.tokens.json +477 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/CodeBlock.tsx
|
|
4
|
+
import { createContext, useCallback, useContext, useRef } from "react";
|
|
5
|
+
import { Check, Copy } from "lucide-react";
|
|
6
|
+
import { useCopyToClipboard } from "../../hooks/useCopyToClipboard.js";
|
|
7
|
+
import { cn } from "../../lib/recipe.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
var CodeBlockAttachment = createContext(false);
|
|
10
|
+
var ATTACHED_STYLE = { marginTop: 0, borderTopWidth: 0 };
|
|
11
|
+
function CodeBlock({
|
|
12
|
+
children,
|
|
13
|
+
title,
|
|
14
|
+
language,
|
|
15
|
+
copyable = true,
|
|
16
|
+
attached,
|
|
17
|
+
className = "",
|
|
18
|
+
...rest
|
|
19
|
+
}) {
|
|
20
|
+
const attachedByContext = useContext(CodeBlockAttachment);
|
|
21
|
+
const isAttached = attached ?? attachedByContext;
|
|
22
|
+
const preRef = useRef(null);
|
|
23
|
+
const { copied, copy } = useCopyToClipboard();
|
|
24
|
+
const onCopy = useCallback(() => {
|
|
25
|
+
const text = preRef.current?.innerText ?? "";
|
|
26
|
+
if (text) void copy(text);
|
|
27
|
+
}, [copy]);
|
|
28
|
+
return /* @__PURE__ */ jsxs(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
"data-slot": "code-block",
|
|
32
|
+
className: cn("docs-codeblock", className),
|
|
33
|
+
style: isAttached ? ATTACHED_STYLE : void 0,
|
|
34
|
+
"data-attached": isAttached ? "true" : void 0,
|
|
35
|
+
children: [
|
|
36
|
+
(title || language) && /* @__PURE__ */ jsxs("div", { className: "docs-codeblock-bar", children: [
|
|
37
|
+
title && /* @__PURE__ */ jsx("span", { className: "docs-codeblock-title", children: title }),
|
|
38
|
+
language && /* @__PURE__ */ jsx("span", { className: "docs-codeblock-lang", children: language })
|
|
39
|
+
] }),
|
|
40
|
+
/* @__PURE__ */ jsxs("div", { className: "docs-codeblock-body", children: [
|
|
41
|
+
copyable && /* @__PURE__ */ jsx(
|
|
42
|
+
"button",
|
|
43
|
+
{
|
|
44
|
+
type: "button",
|
|
45
|
+
onClick: onCopy,
|
|
46
|
+
className: "docs-codeblock-copy",
|
|
47
|
+
"data-copied": copied ? "true" : void 0,
|
|
48
|
+
"aria-label": copied ? "Copied" : "Copy code",
|
|
49
|
+
title: copied ? "Copied!" : "Copy code",
|
|
50
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { size: 14 }) : /* @__PURE__ */ jsx(Copy, { size: 14 })
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
/* @__PURE__ */ jsx("pre", { ref: preRef, tabIndex: 0, role: "region", "aria-label": title ?? "Code", ...rest, children })
|
|
54
|
+
] })
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
CodeBlock,
|
|
61
|
+
CodeBlockAttachment
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=CodeBlock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/CodeBlock.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useCallback, useContext, useRef } from 'react';\nimport type { CSSProperties, HTMLAttributes, ReactNode } from 'react';\nimport { Check, Copy } from 'lucide-react';\nimport { useCopyToClipboard } from '../../hooks/useCopyToClipboard';\nimport { cn } from '../../lib/recipe';\n\nexport interface CodeBlockProps extends HTMLAttributes<HTMLPreElement> {\n children: ReactNode;\n /** Filename or caption shown in the header bar. */\n title?: string;\n /** Language tag shown on the right of the header bar. */\n language?: string;\n /** Hide the copy button (e.g. for output-only samples). */\n copyable?: boolean;\n /**\n * Sit flush under a control that draws the top edge — a {@link CodeTabs}\n * strip. Drops the block's own top margin and top rule so the seam between\n * strip and block is one 2px line rather than two stacked ones. Defaults to\n * whatever the nearest {@link CodeBlockAttachment} says, so a fenced block\n * inside a tab panel attaches without the author knowing the prop exists.\n */\n attached?: boolean;\n}\n\n/**\n * Tells every `CodeBlock` underneath that it is sitting inside a control that\n * already draws its top edge. Provided by `CodeTabs`; read by `CodeBlock`.\n */\nexport const CodeBlockAttachment = createContext(false);\n\n/**\n * `.docs-codeblock` is still plain CSS in `prose.css`, and plain CSS is\n * unlayered — it beats every Tailwind utility regardless of source order, so\n * `mt-0 border-t-0` on the wrapper would do nothing. Until the block migrates\n * to a `recipe`, the override has to be inline. Delete this with the migration.\n */\nconst ATTACHED_STYLE: CSSProperties = { marginTop: 0, borderTopWidth: 0 };\n\n/**\n * Fenced code block with a copy button and an optional filename bar.\n *\n * The copy ref sits on the `<pre>` rather than the outer wrapper so the\n * button's own label never ends up in the clipboard — an easy thing to get\n * wrong when the control is rendered inside the measured element.\n *\n * The button is always in the DOM (revealed on hover/focus via CSS) instead of\n * being conditionally rendered on hover: a mount-on-hover button cannot be\n * reached by keyboard at all.\n */\nexport function CodeBlock({\n children,\n title,\n language,\n copyable = true,\n attached,\n className = '',\n ...rest\n}: CodeBlockProps) {\n const attachedByContext = useContext(CodeBlockAttachment);\n const isAttached = attached ?? attachedByContext;\n const preRef = useRef<HTMLPreElement>(null);\n const { copied, copy } = useCopyToClipboard();\n\n const onCopy = useCallback(() => {\n const text = preRef.current?.innerText ?? '';\n if (text) void copy(text);\n }, [copy]);\n\n return (\n <div data-slot=\"code-block\"\n className={cn('docs-codeblock', className)}\n style={isAttached ? ATTACHED_STYLE : undefined}\n data-attached={isAttached ? 'true' : undefined}\n >\n {(title || language) && (\n <div className=\"docs-codeblock-bar\">\n {title && <span className=\"docs-codeblock-title\">{title}</span>}\n {language && <span className=\"docs-codeblock-lang\">{language}</span>}\n </div>\n )}\n\n <div className=\"docs-codeblock-body\">\n {copyable && (\n <button\n type=\"button\"\n onClick={onCopy}\n className=\"docs-codeblock-copy\"\n data-copied={copied ? 'true' : undefined}\n aria-label={copied ? 'Copied' : 'Copy code'}\n title={copied ? 'Copied!' : 'Copy code'}\n >\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n )}\n {/*\n `tabIndex={0}` because the block scrolls horizontally at narrow widths\n (`prose.css` gives it `overflow-x: auto`). A scrollable region that\n cannot be focused cannot be scrolled by keyboard at all — axe's\n `scrollable-region-focusable`, and a real trap rather than a technicality:\n the content is simply unreachable without a pointer.\n */}\n <pre ref={preRef} tabIndex={0} role=\"region\" aria-label={title ?? 'Code'} {...rest}>\n {children}\n </pre>\n </div>\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAS,eAAe,aAAa,YAAY,cAAc;AAE/D,SAAS,OAAO,YAAY;AAC5B,SAAS,0BAA0B;AACnC,SAAS,UAAU;AAuEX,SACY,KADZ;AA/CD,IAAM,sBAAsB,cAAc,KAAK;AAQtD,IAAM,iBAAgC,EAAE,WAAW,GAAG,gBAAgB,EAAE;AAajE,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAmB;AACjB,QAAM,oBAAoB,WAAW,mBAAmB;AACxD,QAAM,aAAa,YAAY;AAC/B,QAAM,SAAS,OAAuB,IAAI;AAC1C,QAAM,EAAE,QAAQ,KAAK,IAAI,mBAAmB;AAE5C,QAAM,SAAS,YAAY,MAAM;AAC/B,UAAM,OAAO,OAAO,SAAS,aAAa;AAC1C,QAAI,KAAM,MAAK,KAAK,IAAI;AAAA,EAC1B,GAAG,CAAC,IAAI,CAAC;AAET,SACE;AAAA,IAAC;AAAA;AAAA,MAAI,aAAU;AAAA,MACb,WAAW,GAAG,kBAAkB,SAAS;AAAA,MACzC,OAAO,aAAa,iBAAiB;AAAA,MACrC,iBAAe,aAAa,SAAS;AAAA,MAEnC;AAAA,kBAAS,aACT,qBAAC,SAAI,WAAU,sBACZ;AAAA,mBAAS,oBAAC,UAAK,WAAU,wBAAwB,iBAAM;AAAA,UACvD,YAAY,oBAAC,UAAK,WAAU,uBAAuB,oBAAS;AAAA,WAC/D;AAAA,QAGF,qBAAC,SAAI,WAAU,uBACZ;AAAA,sBACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAU;AAAA,cACV,eAAa,SAAS,SAAS;AAAA,cAC/B,cAAY,SAAS,WAAW;AAAA,cAChC,OAAO,SAAS,YAAY;AAAA,cAE3B,mBAAS,oBAAC,SAAM,MAAM,IAAI,IAAK,oBAAC,QAAK,MAAM,IAAI;AAAA;AAAA,UAClD;AAAA,UASF,oBAAC,SAAI,KAAK,QAAQ,UAAU,GAAG,MAAK,UAAS,cAAY,SAAS,QAAS,GAAG,MAC3E,UACH;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/CodeTabs.tsx
|
|
4
|
+
import { Children, Fragment, isValidElement } from "react";
|
|
5
|
+
import { cn } from "../../lib/recipe.js";
|
|
6
|
+
import { Tabs, TabsList, TabsPanel, TabsTab } from "../Tabs.js";
|
|
7
|
+
import { CodeBlock, CodeBlockAttachment } from "./CodeBlock.js";
|
|
8
|
+
import { useTabGroup } from "./codeTabsStore.js";
|
|
9
|
+
import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
function flattenFragments(children) {
|
|
11
|
+
return Children.toArray(children).flatMap(
|
|
12
|
+
(child) => isValidElement(child) && child.type === Fragment ? flattenFragments(child.props.children) : [child]
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
function CodeTab({ children }) {
|
|
16
|
+
return /* @__PURE__ */ jsx(Fragment2, { children });
|
|
17
|
+
}
|
|
18
|
+
function CodeTabs({
|
|
19
|
+
children,
|
|
20
|
+
group,
|
|
21
|
+
variant = "merged",
|
|
22
|
+
accent = "primary",
|
|
23
|
+
label,
|
|
24
|
+
className = ""
|
|
25
|
+
}) {
|
|
26
|
+
const tabs = flattenFragments(children).filter(isValidElement).map((child, index) => ({
|
|
27
|
+
label: child.props.label || `Tab ${index + 1}`,
|
|
28
|
+
language: child.props.language,
|
|
29
|
+
body: child.props.children
|
|
30
|
+
}));
|
|
31
|
+
const [selected, select] = useTabGroup(group, tabs[0]?.label ?? "");
|
|
32
|
+
const activeLabel = tabs.some((tab) => tab.label === selected) ? selected : tabs[0]?.label ?? "";
|
|
33
|
+
const name = label ?? group;
|
|
34
|
+
return (
|
|
35
|
+
// Controlled, because the selection lives in the group store rather than
|
|
36
|
+
// in the strip: two blocks sharing a group have one selection between them.
|
|
37
|
+
/* @__PURE__ */ jsxs(
|
|
38
|
+
Tabs,
|
|
39
|
+
{
|
|
40
|
+
value: activeLabel,
|
|
41
|
+
onValueChange: select,
|
|
42
|
+
variant,
|
|
43
|
+
font: "mono",
|
|
44
|
+
accent,
|
|
45
|
+
className: cn("my-6", className),
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
TabsList,
|
|
49
|
+
{
|
|
50
|
+
label: name ?? "Code variants",
|
|
51
|
+
caption: variant === "segmented" ? name ?? "source" : void 0,
|
|
52
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsx(TabsTab, { value: tab.label, children: tab.label }, tab.label))
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
/* @__PURE__ */ jsx(CodeBlockAttachment, { value: true, children: tabs.map((tab) => (
|
|
56
|
+
// Hidden panels stay in the document rather than unmounting, so
|
|
57
|
+
// every variant of a snippet is in the HTML for a crawler and a
|
|
58
|
+
// switch costs no re-render of the code. That is what `keepMounted`
|
|
59
|
+
// is for, and it is why this did not become a Base UI tablist.
|
|
60
|
+
/* @__PURE__ */ jsx(TabsPanel, { value: tab.label, keepMounted: true, children: typeof tab.body === "string" ? /* @__PURE__ */ jsx(CodeBlock, { language: tab.language, children: tab.body }) : tab.body }, tab.label)
|
|
61
|
+
)) })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
CodeTab,
|
|
69
|
+
CodeTabs
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=CodeTabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/CodeTabs.tsx"],"sourcesContent":["'use client';\n\nimport { Children, Fragment, isValidElement } from 'react';\nimport type { ReactNode } from 'react';\nimport { cn } from '../../lib/recipe';\nimport type { AccentToken } from '../../lib/theme';\nimport { Tabs, TabsList, TabsPanel, TabsTab } from '../Tabs';\nimport type { TabsVariant } from '../Tabs';\nimport { CodeBlock, CodeBlockAttachment } from './CodeBlock';\nimport { useTabGroup } from './codeTabsStore';\n\n/**\n * The code-block language switcher every developer documentation portal has:\n * a strip of tabs standing on a code block, one panel of the same content in\n * another form per tab.\n *\n * This is a **content switcher, not navigation** — a real `tablist` with\n * roving focus and arrow-key traversal, whose panels hold the same snippet in\n * another language or tool. That is why it is not a sidebar rotated: the\n * contract is different.\n *\n * ## It is a `Tabs` now\n *\n * The tablist this component used to hand-roll is {@link Tabs}, the public\n * primitive, and it was extracted from here rather than rewritten — same\n * markup, same classes, same keyboard model, same tests. What stays here is\n * the part that is documentation-specific and does not belong in a primitive:\n * the group store, the `CodeBlock` attachment, and reading tabs out of\n * children.\n *\n * ## `group` is what earns it\n *\n * Blocks sharing a group switch together, and the choice persists across\n * pages: a reader picks a package manager once, not once per snippet. A block\n * whose tab set does not include the group's choice falls back to its own first\n * tab rather than showing nothing.\n *\n * ## The state rule\n *\n * The selected tab is marked with an accent **fill** or a 4px accent **edge**,\n * never by swapping one surface for another. `surface.raised` against\n * `surface.base` is a few percent of lightness on the light rungs of the\n * ladder — enough to layer a strip over a page, not enough to tell a reader\n * which tab they are on. `pnpm check:contrast` asserts both devices clear 3:1\n * on every level, and reports the surface pair so nobody has to rediscover\n * why it is not an option.\n *\n * ## Usage in MDX\n *\n * ```mdx\n * <CodeTabs group=\"pkg\">\n * <CodeTab label=\"pnpm\">\n *\n * ```bash\n * pnpm add @rtkelly-labs/design-system\n * ```\n *\n * </CodeTab>\n * <CodeTab label=\"npm\">\n *\n * ```bash\n * npm install @rtkelly-labs/design-system\n * ```\n *\n * </CodeTab>\n * </CodeTabs>\n * ```\n *\n * Blank lines around the fences are load-bearing — MDX only parses markdown\n * inside a JSX block that opens and closes on its own lines. The fences compile\n * to `CodeBlock`, which reads {@link CodeBlockAttachment} and drops its own top\n * rule so the seam between strip and block is one line. A `CodeTab` whose\n * children are a plain string is wrapped in a `CodeBlock` here, for `.tsx`\n * callers with no MDX pipeline.\n */\n\n/**\n * The strip's shape. The same three the primitive declares — this alias is\n * kept because it is what existing call sites import.\n */\nexport type CodeTabsVariant = TabsVariant;\n\n/**\n * `Children.toArray` stops at a `<>…</>`, so a set of tabs an author keeps in\n * a fragment — the natural way to reuse one set across several blocks — would\n * read as a single tab called \"Tab 1\". Unwrap fragments to any depth.\n */\nfunction flattenFragments(children: ReactNode): ReactNode[] {\n return Children.toArray(children).flatMap((child) =>\n isValidElement<{ children?: ReactNode }>(child) && child.type === Fragment\n ? flattenFragments(child.props.children)\n : [child],\n );\n}\n\nexport interface CodeTabProps {\n /** Tab text: the language, tool or filename. Read by {@link CodeTabs}. */\n label: string;\n /**\n * Language tag for the block, when `children` is a plain string and this\n * component wraps it in a `CodeBlock` itself. Fenced MDX carries its own.\n */\n language?: string;\n children: ReactNode;\n}\n\n/**\n * One tab inside a {@link CodeTabs}. A marker component: `CodeTabs` reads its\n * `label` and renders its children into a panel. Rendered on its own it is\n * just its children, so a tab that ends up outside a `CodeTabs` still shows\n * its code rather than vanishing.\n */\nexport function CodeTab({ children }: CodeTabProps) {\n return <>{children}</>;\n}\n\nexport interface CodeTabsProps {\n /** `CodeTab` children. Anything else is ignored. */\n children: ReactNode;\n /** Blocks sharing a group switch together, and the choice persists. */\n group?: string;\n variant?: CodeTabsVariant;\n /**\n * Semantic accent for the selected tab. Accepts an `Emphasis` or an\n * `Intent`; the legacy hue names still resolve to the same values.\n */\n accent?: AccentToken;\n /**\n * Caption for the `segmented` variant — a filename or a title. Also the\n * accessible name of the tab list, ahead of `group`.\n */\n label?: string;\n className?: string;\n}\n\nexport function CodeTabs({\n children,\n group,\n variant = 'merged',\n accent = 'primary',\n label,\n className = '',\n}: CodeTabsProps) {\n const tabs = flattenFragments(children)\n .filter(isValidElement<CodeTabProps>)\n .map((child, index) => ({\n label: child.props.label || `Tab ${index + 1}`,\n language: child.props.language,\n body: child.props.children,\n }));\n\n const [selected, select] = useTabGroup(group, tabs[0]?.label ?? '');\n\n // A group can be shared by blocks with different tab sets — `pnpm | npm` in\n // one and `pnpm | npm | yarn` in another. Fall back rather than showing an\n // empty block when the group's choice is not on offer here.\n const activeLabel = tabs.some((tab) => tab.label === selected)\n ? selected\n : (tabs[0]?.label ?? '');\n\n const name = label ?? group;\n\n return (\n // Controlled, because the selection lives in the group store rather than\n // in the strip: two blocks sharing a group have one selection between them.\n <Tabs\n value={activeLabel}\n onValueChange={select}\n variant={variant}\n font=\"mono\"\n accent={accent}\n className={cn('my-6', className)}\n >\n <TabsList\n label={name ?? 'Code variants'}\n caption={variant === 'segmented' ? (name ?? 'source') : undefined}\n >\n {tabs.map((tab) => (\n <TabsTab key={tab.label} value={tab.label}>\n {tab.label}\n </TabsTab>\n ))}\n </TabsList>\n\n <CodeBlockAttachment value={true}>\n {tabs.map((tab) => (\n // Hidden panels stay in the document rather than unmounting, so\n // every variant of a snippet is in the HTML for a crawler and a\n // switch costs no re-render of the code. That is what `keepMounted`\n // is for, and it is why this did not become a Base UI tablist.\n <TabsPanel key={tab.label} value={tab.label} keepMounted>\n {typeof tab.body === 'string' ? (\n <CodeBlock language={tab.language}>{tab.body}</CodeBlock>\n ) : (\n tab.body\n )}\n </TabsPanel>\n ))}\n </CodeBlockAttachment>\n </Tabs>\n );\n}\n"],"mappings":";;;AAEA,SAAS,UAAU,UAAU,sBAAsB;AAEnD,SAAS,UAAU;AAEnB,SAAS,MAAM,UAAU,WAAW,eAAe;AAEnD,SAAS,WAAW,2BAA2B;AAC/C,SAAS,mBAAmB;AAwGnB,qBAAAA,WAAA,KAoDL,YApDK;AA1BT,SAAS,iBAAiB,UAAkC;AAC1D,SAAO,SAAS,QAAQ,QAAQ,EAAE;AAAA,IAAQ,CAAC,UACzC,eAAyC,KAAK,KAAK,MAAM,SAAS,WAC9D,iBAAiB,MAAM,MAAM,QAAQ,IACrC,CAAC,KAAK;AAAA,EACZ;AACF;AAmBO,SAAS,QAAQ,EAAE,SAAS,GAAiB;AAClD,SAAO,oBAAAA,WAAA,EAAG,UAAS;AACrB;AAqBO,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,SAAS;AAAA,EACT;AAAA,EACA,YAAY;AACd,GAAkB;AAChB,QAAM,OAAO,iBAAiB,QAAQ,EACnC,OAAO,cAA4B,EACnC,IAAI,CAAC,OAAO,WAAW;AAAA,IACtB,OAAO,MAAM,MAAM,SAAS,OAAO,QAAQ,CAAC;AAAA,IAC5C,UAAU,MAAM,MAAM;AAAA,IACtB,MAAM,MAAM,MAAM;AAAA,EACpB,EAAE;AAEJ,QAAM,CAAC,UAAU,MAAM,IAAI,YAAY,OAAO,KAAK,CAAC,GAAG,SAAS,EAAE;AAKlE,QAAM,cAAc,KAAK,KAAK,CAAC,QAAQ,IAAI,UAAU,QAAQ,IACzD,WACC,KAAK,CAAC,GAAG,SAAS;AAEvB,QAAM,OAAO,SAAS;AAEtB;AAAA;AAAA;AAAA,IAGE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,eAAe;AAAA,QACf;AAAA,QACA,MAAK;AAAA,QACL;AAAA,QACA,WAAW,GAAG,QAAQ,SAAS;AAAA,QAE/B;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,QAAQ;AAAA,cACf,SAAS,YAAY,cAAe,QAAQ,WAAY;AAAA,cAEvD,eAAK,IAAI,CAAC,QACT,oBAAC,WAAwB,OAAO,IAAI,OACjC,cAAI,SADO,IAAI,KAElB,CACD;AAAA;AAAA,UACH;AAAA,UAEA,oBAAC,uBAAoB,OAAO,MACzB,eAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,YAKT,oBAAC,aAA0B,OAAO,IAAI,OAAO,aAAW,MACrD,iBAAO,IAAI,SAAS,WACnB,oBAAC,aAAU,UAAU,IAAI,UAAW,cAAI,MAAK,IAE7C,IAAI,QAJQ,IAAI,KAMpB;AAAA,WACD,GACH;AAAA;AAAA;AAAA,IACF;AAAA;AAEJ;","names":["Fragment"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/components/docs/DocPager.tsx
|
|
2
|
+
import { ArrowLeft, ArrowRight } from "lucide-react";
|
|
3
|
+
import { DocsLink } from "./DocsLinkProvider.js";
|
|
4
|
+
import { cn } from "../../lib/recipe.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
function DocPager({ prev, next, className = "" }) {
|
|
7
|
+
if (!prev && !next) return null;
|
|
8
|
+
return /* @__PURE__ */ jsxs("nav", { "data-slot": "doc-pager", className: cn("not-prose docs-pager print:hidden", className), "aria-label": "Page navigation", children: [
|
|
9
|
+
prev ? /* @__PURE__ */ jsxs(DocsLink, { href: prev.href, className: "docs-pager-link", rel: "prev", children: [
|
|
10
|
+
/* @__PURE__ */ jsxs("span", { className: "docs-pager-dir", children: [
|
|
11
|
+
/* @__PURE__ */ jsx(ArrowLeft, { size: 13, "aria-hidden": "true" }),
|
|
12
|
+
" PREVIOUS"
|
|
13
|
+
] }),
|
|
14
|
+
/* @__PURE__ */ jsx("span", { className: "docs-pager-label", children: prev.label })
|
|
15
|
+
] }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true" }),
|
|
16
|
+
next ? /* @__PURE__ */ jsxs(DocsLink, { href: next.href, className: "docs-pager-link docs-pager-next", rel: "next", children: [
|
|
17
|
+
/* @__PURE__ */ jsxs("span", { className: "docs-pager-dir", children: [
|
|
18
|
+
"NEXT ",
|
|
19
|
+
/* @__PURE__ */ jsx(ArrowRight, { size: 13, "aria-hidden": "true" })
|
|
20
|
+
] }),
|
|
21
|
+
/* @__PURE__ */ jsx("span", { className: "docs-pager-label", children: next.label })
|
|
22
|
+
] }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true" })
|
|
23
|
+
] });
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
DocPager
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=DocPager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/DocPager.tsx"],"sourcesContent":["import { ArrowLeft, ArrowRight } from 'lucide-react';\nimport { DocsLink } from './DocsLinkProvider';\nimport { cn } from '../../lib/recipe';\n\nexport interface DocPagerTarget {\n label: string;\n href: string;\n}\n\nexport interface DocPagerProps {\n prev?: DocPagerTarget;\n next?: DocPagerTarget;\n className?: string;\n}\n\n/**\n * Titled previous/next navigation for the foot of a documentation page.\n *\n * Distinct from {@link Pagination}, which counts numbered pages (`[ 3 / 12 ]`)\n * for list views. Readers moving through docs sequentially need the *name* of\n * the adjacent page, not its index.\n *\n * Renders an empty spacer when only one side exists so `next` stays\n * right-aligned on a page with no predecessor.\n */\nexport function DocPager({ prev, next, className = '' }: DocPagerProps) {\n if (!prev && !next) return null;\n\n return (\n <nav data-slot=\"doc-pager\" className={cn('not-prose docs-pager print:hidden', className)} aria-label=\"Page navigation\">\n {prev ? (\n <DocsLink href={prev.href} className=\"docs-pager-link\" rel=\"prev\">\n <span className=\"docs-pager-dir\">\n <ArrowLeft size={13} aria-hidden=\"true\" /> PREVIOUS\n </span>\n <span className=\"docs-pager-label\">{prev.label}</span>\n </DocsLink>\n ) : (\n <span aria-hidden=\"true\" />\n )}\n\n {next ? (\n <DocsLink href={next.href} className=\"docs-pager-link docs-pager-next\" rel=\"next\">\n <span className=\"docs-pager-dir\">\n NEXT <ArrowRight size={13} aria-hidden=\"true\" />\n </span>\n <span className=\"docs-pager-label\">{next.label}</span>\n </DocsLink>\n ) : (\n <span aria-hidden=\"true\" />\n )}\n </nav>\n );\n}\n"],"mappings":";AAAA,SAAS,WAAW,kBAAkB;AACtC,SAAS,gBAAgB;AACzB,SAAS,UAAU;AA8BT,SACE,KADF;AAPH,SAAS,SAAS,EAAE,MAAM,MAAM,YAAY,GAAG,GAAkB;AACtE,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAE3B,SACE,qBAAC,SAAI,aAAU,aAAY,WAAW,GAAG,qCAAqC,SAAS,GAAG,cAAW,mBAClG;AAAA,WACC,qBAAC,YAAS,MAAM,KAAK,MAAM,WAAU,mBAAkB,KAAI,QACzD;AAAA,2BAAC,UAAK,WAAU,kBACd;AAAA,4BAAC,aAAU,MAAM,IAAI,eAAY,QAAO;AAAA,QAAE;AAAA,SAC5C;AAAA,MACA,oBAAC,UAAK,WAAU,oBAAoB,eAAK,OAAM;AAAA,OACjD,IAEA,oBAAC,UAAK,eAAY,QAAO;AAAA,IAG1B,OACC,qBAAC,YAAS,MAAM,KAAK,MAAM,WAAU,mCAAkC,KAAI,QACzE;AAAA,2BAAC,UAAK,WAAU,kBAAiB;AAAA;AAAA,QAC1B,oBAAC,cAAW,MAAM,IAAI,eAAY,QAAO;AAAA,SAChD;AAAA,MACA,oBAAC,UAAK,WAAU,oBAAoB,eAAK,OAAM;AAAA,OACjD,IAEA,oBAAC,UAAK,eAAY,QAAO;AAAA,KAE7B;AAEJ;","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/DocsHeader.tsx
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { Menu as MenuIcon, Palette, Search, X } from "lucide-react";
|
|
6
|
+
import { DocsLink } from "./DocsLinkProvider.js";
|
|
7
|
+
import { useOptionalTheme } from "../ThemeProvider.js";
|
|
8
|
+
import { LEVELS, isThemeLevel } from "../../theme/levels.js";
|
|
9
|
+
import { cn } from "../../lib/recipe.js";
|
|
10
|
+
import { Menu, MenuRadioGroup, MenuRadioItem } from "../Menu.js";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
function DocsHeader({
|
|
13
|
+
title,
|
|
14
|
+
titleHref = "/",
|
|
15
|
+
icon: Icon,
|
|
16
|
+
nav = [],
|
|
17
|
+
onSearch,
|
|
18
|
+
searchShortcut = "\u2318K",
|
|
19
|
+
onToggleSidebar,
|
|
20
|
+
sidebarOpen = false,
|
|
21
|
+
children,
|
|
22
|
+
className = ""
|
|
23
|
+
}) {
|
|
24
|
+
const ref = useRef(null);
|
|
25
|
+
const theme = useOptionalTheme();
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const el = ref.current;
|
|
28
|
+
if (!el || typeof window === "undefined") return;
|
|
29
|
+
const publish = () => {
|
|
30
|
+
document.documentElement.style.setProperty(
|
|
31
|
+
"--docs-header-height",
|
|
32
|
+
`${Math.round(el.getBoundingClientRect().height)}px`
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
publish();
|
|
36
|
+
if (typeof ResizeObserver === "undefined") {
|
|
37
|
+
window.addEventListener("resize", publish);
|
|
38
|
+
return () => window.removeEventListener("resize", publish);
|
|
39
|
+
}
|
|
40
|
+
const observer = new ResizeObserver(publish);
|
|
41
|
+
observer.observe(el);
|
|
42
|
+
return () => observer.disconnect();
|
|
43
|
+
}, []);
|
|
44
|
+
return /* @__PURE__ */ jsxs("header", { "data-slot": "docs-header", ref, className: cn("not-prose docs-header print:hidden", className), children: [
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "docs-header-left", children: [
|
|
46
|
+
onToggleSidebar && /* @__PURE__ */ jsx(
|
|
47
|
+
"button",
|
|
48
|
+
{
|
|
49
|
+
type: "button",
|
|
50
|
+
onClick: onToggleSidebar,
|
|
51
|
+
className: "docs-header-icon-btn docs-header-sidebar-toggle",
|
|
52
|
+
"aria-label": sidebarOpen ? "Close navigation" : "Open navigation",
|
|
53
|
+
"aria-expanded": sidebarOpen,
|
|
54
|
+
children: sidebarOpen ? /* @__PURE__ */ jsx(X, { size: 18 }) : /* @__PURE__ */ jsx(MenuIcon, { size: 18 })
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsxs(DocsLink, { href: titleHref, className: "docs-header-brand", children: [
|
|
58
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "docs-header-brand-icon" }),
|
|
59
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
60
|
+
"[ ",
|
|
61
|
+
title.toUpperCase(),
|
|
62
|
+
" ]"
|
|
63
|
+
] })
|
|
64
|
+
] })
|
|
65
|
+
] }),
|
|
66
|
+
nav.length > 0 && /* @__PURE__ */ jsx("nav", { className: "docs-header-nav", "aria-label": "Main", children: nav.map((item) => /* @__PURE__ */ jsx(
|
|
67
|
+
DocsLink,
|
|
68
|
+
{
|
|
69
|
+
href: item.href,
|
|
70
|
+
className: "docs-header-nav-link",
|
|
71
|
+
...item.active ? { "aria-current": "page" } : {},
|
|
72
|
+
children: item.label
|
|
73
|
+
},
|
|
74
|
+
item.href
|
|
75
|
+
)) }),
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { className: "docs-header-right", children: [
|
|
77
|
+
children,
|
|
78
|
+
onSearch && /* @__PURE__ */ jsxs(
|
|
79
|
+
"button",
|
|
80
|
+
{
|
|
81
|
+
type: "button",
|
|
82
|
+
onClick: onSearch,
|
|
83
|
+
className: "docs-header-search",
|
|
84
|
+
"aria-label": "Search documentation",
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx(Search, { size: 15, "aria-hidden": "true" }),
|
|
87
|
+
/* @__PURE__ */ jsx("span", { className: "docs-header-search-label", children: "SEARCH" }),
|
|
88
|
+
searchShortcut && /* @__PURE__ */ jsx("kbd", { className: "docs-header-kbd", children: searchShortcut })
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
theme && /* @__PURE__ */ jsx(
|
|
93
|
+
Menu,
|
|
94
|
+
{
|
|
95
|
+
align: "end",
|
|
96
|
+
trigger: /* @__PURE__ */ jsx(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
className: "docs-header-icon-btn",
|
|
101
|
+
"aria-label": `Theme level (current: ${LEVELS[theme.level].label})`,
|
|
102
|
+
children: /* @__PURE__ */ jsx(Palette, { size: 18, "aria-hidden": "true" })
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
children: /* @__PURE__ */ jsx(
|
|
106
|
+
MenuRadioGroup,
|
|
107
|
+
{
|
|
108
|
+
label: "Level",
|
|
109
|
+
value: theme.level,
|
|
110
|
+
onValueChange: (next) => {
|
|
111
|
+
if (isThemeLevel(next)) theme.setLevel(next);
|
|
112
|
+
},
|
|
113
|
+
children: theme.levels.map((level) => /* @__PURE__ */ jsx(MenuRadioItem, { value: level, closeOnClick: true, children: LEVELS[level].label }, level))
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
] })
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
DocsHeader
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=DocsHeader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/DocsHeader.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport type { ElementType, ReactNode } from 'react';\nimport { Menu as MenuIcon, Palette, Search, X } from 'lucide-react';\nimport { DocsLink } from './DocsLinkProvider';\nimport { useOptionalTheme } from '../ThemeProvider';\nimport { LEVELS, isThemeLevel } from '../../theme/levels';\nimport { cn } from '../../lib/recipe';\nimport { Menu, MenuRadioGroup, MenuRadioItem } from '../Menu';\n\nexport interface DocsNavItem {\n label: string;\n href: string;\n /** Marks the item active. Callers own route matching. */\n active?: boolean;\n external?: boolean;\n}\n\nexport interface DocsHeaderProps {\n /** Brand text, rendered bracketed and uppercased. */\n title: string;\n /** Href the brand links to. Defaults to the site root. */\n titleHref?: string;\n /** Optional glyph before the brand — a lucide icon or any SVG component. */\n icon?: ElementType<{ className?: string }>;\n /** Top-level sections, shown inline on desktop only. */\n nav?: readonly DocsNavItem[];\n /** Invoked by the search button. Omit to hide the button entirely. */\n onSearch?: () => void;\n /** Keyboard hint rendered inside the search button. */\n searchShortcut?: string;\n /** Controls the mobile sidebar toggle. Omit to hide it. */\n onToggleSidebar?: () => void;\n sidebarOpen?: boolean;\n /** Extra trailing content — version switcher, repo link, and so on. */\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * The persistent top bar for a docs site.\n *\n * Beyond navigation it owns one piece of cross-cutting state: it measures its\n * own rendered height and publishes it as `--docs-header-height` on the root\n * element. A sticky header silently swallows anchor targets — jump to\n * `#some-section` and the heading lands underneath the bar — so the offset has\n * to reach `scroll-margin-top` in `prose.css` and the scroll-spy reading line\n * in `useActiveHeading`. Measuring rather than hard-coding keeps all three\n * correct when the bar wraps on narrow screens or a consumer restyles it.\n */\nexport function DocsHeader({\n title,\n titleHref = '/',\n icon: Icon,\n nav = [],\n onSearch,\n searchShortcut = '⌘K',\n onToggleSidebar,\n sidebarOpen = false,\n children,\n className = '',\n}: DocsHeaderProps) {\n const ref = useRef<HTMLElement | null>(null);\n /*\n * `useOptionalTheme`, not `useTheme`. A docs header is chrome, and ADR 0004\n * says a consumer may theme with the `data-theme` attribute alone and never\n * mount a provider — developer themes consume colour only, the host owns the\n * geometry and the clock. The strict hook throws, so requiring it here takes\n * the whole page down rather than degrading.\n *\n * This component *adapts* to the level rather than requiring it: with no\n * provider there is no level to choose, so the control is omitted and the rest\n * of the header renders.\n */\n const theme = useOptionalTheme();\n\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof window === 'undefined') return;\n\n const publish = () => {\n document.documentElement.style.setProperty(\n '--docs-header-height',\n `${Math.round(el.getBoundingClientRect().height)}px`,\n );\n };\n\n publish();\n\n // ResizeObserver is absent in older jsdom-based test environments; the\n // initial measurement above is still correct there.\n if (typeof ResizeObserver === 'undefined') {\n window.addEventListener('resize', publish);\n return () => window.removeEventListener('resize', publish);\n }\n\n const observer = new ResizeObserver(publish);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n return (\n <header data-slot=\"docs-header\" ref={ref} className={cn('not-prose docs-header print:hidden', className)}>\n <div className=\"docs-header-left\">\n {onToggleSidebar && (\n <button\n type=\"button\"\n onClick={onToggleSidebar}\n className=\"docs-header-icon-btn docs-header-sidebar-toggle\"\n aria-label={sidebarOpen ? 'Close navigation' : 'Open navigation'}\n aria-expanded={sidebarOpen}\n >\n {sidebarOpen ? <X size={18} /> : <MenuIcon size={18} />}\n </button>\n )}\n\n <DocsLink href={titleHref} className=\"docs-header-brand\">\n {Icon && <Icon className=\"docs-header-brand-icon\" />}\n <span>[ {title.toUpperCase()} ]</span>\n </DocsLink>\n </div>\n\n {nav.length > 0 && (\n <nav className=\"docs-header-nav\" aria-label=\"Main\">\n {nav.map((item) => (\n <DocsLink\n key={item.href}\n href={item.href}\n className=\"docs-header-nav-link\"\n {...(item.active ? { 'aria-current': 'page' as const } : {})}\n >\n {item.label}\n </DocsLink>\n ))}\n </nav>\n )}\n\n <div className=\"docs-header-right\">\n {children}\n\n {onSearch && (\n <button\n type=\"button\"\n onClick={onSearch}\n className=\"docs-header-search\"\n aria-label=\"Search documentation\"\n >\n <Search size={15} aria-hidden=\"true\" />\n <span className=\"docs-header-search-label\">SEARCH</span>\n {searchShortcut && (\n <kbd className=\"docs-header-kbd\">{searchShortcut}</kbd>\n )}\n </button>\n )}\n\n {/*\n * A menu of the levels, not a button that cycles through them (#166).\n * Cycling made the choice a guess: nothing said what the next press\n * would pick, and reaching the last of N levels took N-1 presses.\n * The menu names every level, marks the current one, and closes on\n * a choice — the choice is the whole point of opening it.\n */}\n {theme && (\n <Menu\n align=\"end\"\n trigger={\n <button\n type=\"button\"\n className=\"docs-header-icon-btn\"\n aria-label={`Theme level (current: ${LEVELS[theme.level].label})`}\n >\n <Palette size={18} aria-hidden=\"true\" />\n </button>\n }\n >\n <MenuRadioGroup\n label=\"Level\"\n value={theme.level}\n onValueChange={(next) => {\n if (isThemeLevel(next)) theme.setLevel(next);\n }}\n >\n {theme.levels.map((level) => (\n <MenuRadioItem key={level} value={level} closeOnClick>\n {LEVELS[level].label}\n </MenuRadioItem>\n ))}\n </MenuRadioGroup>\n </Menu>\n )}\n </div>\n </header>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/DocsLayout.tsx
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
import { useHotkey } from "@tanstack/react-hotkeys";
|
|
6
|
+
import { cn } from "../../lib/recipe.js";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function DocsLayout({
|
|
9
|
+
header,
|
|
10
|
+
sidebar,
|
|
11
|
+
toc,
|
|
12
|
+
children,
|
|
13
|
+
sidebarOpen = false,
|
|
14
|
+
onCloseSidebar,
|
|
15
|
+
className = ""
|
|
16
|
+
}) {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (typeof document === "undefined") return;
|
|
19
|
+
if (!sidebarOpen) return;
|
|
20
|
+
const previous = document.body.style.overflow;
|
|
21
|
+
document.body.style.overflow = "hidden";
|
|
22
|
+
return () => {
|
|
23
|
+
document.body.style.overflow = previous;
|
|
24
|
+
};
|
|
25
|
+
}, [sidebarOpen]);
|
|
26
|
+
useHotkey("Escape", () => onCloseSidebar?.(), {
|
|
27
|
+
enabled: sidebarOpen && Boolean(onCloseSidebar),
|
|
28
|
+
preventDefault: false,
|
|
29
|
+
stopPropagation: false,
|
|
30
|
+
meta: { name: "Close navigation", description: "Dismisses the open docs drawer" }
|
|
31
|
+
});
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "docs-layout", className: cn("docs-layout", className), children: [
|
|
33
|
+
header,
|
|
34
|
+
/* @__PURE__ */ jsxs("div", { className: "docs-layout-body", children: [
|
|
35
|
+
sidebar && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36
|
+
sidebarOpen && /* @__PURE__ */ jsx(
|
|
37
|
+
"div",
|
|
38
|
+
{
|
|
39
|
+
className: "docs-layout-scrim",
|
|
40
|
+
onClick: onCloseSidebar,
|
|
41
|
+
"aria-hidden": "true"
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsx(
|
|
45
|
+
"aside",
|
|
46
|
+
{
|
|
47
|
+
className: "docs-layout-sidebar",
|
|
48
|
+
"data-open": sidebarOpen ? "true" : void 0,
|
|
49
|
+
children: sidebar
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
] }),
|
|
53
|
+
/* @__PURE__ */ jsx("main", { className: "docs-layout-main", id: "docs-content", children }),
|
|
54
|
+
toc && /* @__PURE__ */ jsx("aside", { className: "docs-layout-toc", children: toc })
|
|
55
|
+
] })
|
|
56
|
+
] });
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
DocsLayout
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=DocsLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/DocsLayout.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport type { ReactNode } from 'react';\nimport { useHotkey } from '@tanstack/react-hotkeys';\nimport { cn } from '../../lib/recipe';\n\nexport interface DocsLayoutProps {\n /** Rendered above everything, sticky. Normally a `DocsHeader`. */\n header?: ReactNode;\n /** Left rail. Normally a `DocsSidebar`. */\n sidebar?: ReactNode;\n /** Right rail. Normally a `TableOfContents`. Hidden below `xl`. */\n toc?: ReactNode;\n children: ReactNode;\n /** Mobile drawer state. Ignored at `lg` and up, where the rail is always shown. */\n sidebarOpen?: boolean;\n onCloseSidebar?: () => void;\n className?: string;\n}\n\n/**\n * Three-column documentation shell: sidebar, content, contents rail.\n *\n * Below `lg` the sidebar becomes an overlay drawer. Scroll on `<body>` is\n * locked while it is open, otherwise the page behind scrolls under the drawer\n * on touch devices and the reader loses their place on close.\n */\nexport function DocsLayout({\n header,\n sidebar,\n toc,\n children,\n sidebarOpen = false,\n onCloseSidebar,\n className = '',\n}: DocsLayoutProps) {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n if (!sidebarOpen) return;\n\n const previous = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = previous;\n };\n }, [sidebarOpen]);\n\n // Escape belongs to the drawer only while the drawer is open; `enabled`\n // says so declaratively where the old code rebuilt a `window` listener\n // around that condition. `preventDefault` stays off: closing a drawer is\n // not the kind of default that should be swallowed.\n useHotkey('Escape', () => onCloseSidebar?.(), {\n enabled: sidebarOpen && Boolean(onCloseSidebar),\n preventDefault: false,\n stopPropagation: false,\n meta: { name: 'Close navigation', description: 'Dismisses the open docs drawer' },\n });\n\n return (\n <div data-slot=\"docs-layout\" className={cn('docs-layout', className)}>\n {header}\n\n <div className=\"docs-layout-body\">\n {sidebar && (\n <>\n {sidebarOpen && (\n <div\n className=\"docs-layout-scrim\"\n onClick={onCloseSidebar}\n aria-hidden=\"true\"\n />\n )}\n <aside\n className=\"docs-layout-sidebar\"\n data-open={sidebarOpen ? 'true' : undefined}\n >\n {sidebar}\n </aside>\n </>\n )}\n\n <main className=\"docs-layout-main\" id=\"docs-content\">\n {children}\n </main>\n\n {toc && <aside className=\"docs-layout-toc\">{toc}</aside>}\n </div>\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAS,iBAAiB;AAE1B,SAAS,iBAAiB;AAC1B,SAAS,UAAU;AA4DT,mBAEI,KAFJ;AArCH,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,YAAY;AACd,GAAoB;AAClB,YAAU,MAAM;AACd,QAAI,OAAO,aAAa,YAAa;AACrC,QAAI,CAAC,YAAa;AAElB,UAAM,WAAW,SAAS,KAAK,MAAM;AACrC,aAAS,KAAK,MAAM,WAAW;AAC/B,WAAO,MAAM;AACX,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAMhB,YAAU,UAAU,MAAM,iBAAiB,GAAG;AAAA,IAC5C,SAAS,eAAe,QAAQ,cAAc;AAAA,IAC9C,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,MAAM,EAAE,MAAM,oBAAoB,aAAa,iCAAiC;AAAA,EAClF,CAAC;AAED,SACE,qBAAC,SAAI,aAAU,eAAc,WAAW,GAAG,eAAe,SAAS,GAChE;AAAA;AAAA,IAED,qBAAC,SAAI,WAAU,oBACZ;AAAA,iBACC,iCACG;AAAA,uBACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,SAAS;AAAA,YACT,eAAY;AAAA;AAAA,QACd;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,aAAW,cAAc,SAAS;AAAA,YAEjC;AAAA;AAAA,QACH;AAAA,SACF;AAAA,MAGF,oBAAC,UAAK,WAAU,oBAAmB,IAAG,gBACnC,UACH;AAAA,MAEC,OAAO,oBAAC,WAAM,WAAU,mBAAmB,eAAI;AAAA,OAClD;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/DocsLinkProvider.tsx
|
|
4
|
+
import {
|
|
5
|
+
LinkProvider,
|
|
6
|
+
renderProvidedLink,
|
|
7
|
+
useLinkComponent
|
|
8
|
+
} from "../LinkProvider.js";
|
|
9
|
+
import { jsx } from "react/jsx-runtime";
|
|
10
|
+
function DocsLinkProvider({ component, children }) {
|
|
11
|
+
return /* @__PURE__ */ jsx(LinkProvider, { component, children });
|
|
12
|
+
}
|
|
13
|
+
function useDocsLinkComponent() {
|
|
14
|
+
return useLinkComponent();
|
|
15
|
+
}
|
|
16
|
+
function DocsLink(props) {
|
|
17
|
+
const Component = useLinkComponent();
|
|
18
|
+
return renderProvidedLink(Component, { "data-slot": "docs-link", ...props });
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
DocsLink,
|
|
22
|
+
DocsLinkProvider,
|
|
23
|
+
useDocsLinkComponent
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=DocsLinkProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/DocsLinkProvider.tsx"],"sourcesContent":["'use client';\n\nimport type { ElementType, ReactNode } from 'react';\nimport {\n LinkProvider,\n renderProvidedLink,\n useLinkComponent,\n} from '../LinkProvider';\nimport type { LinkComponentProps } from '../LinkProvider';\n\nexport type DocsLinkProps = LinkComponentProps;\n\n// The docs chrome's name for the package's one link adapter.\n//\n// This file used to own the context. Since issue 246 it lives in\n// `components/LinkProvider.tsx`, shared with the site chrome, and these are\n// the docs-family names for it: a `DocsLinkProvider` and a `LinkProvider`\n// write to the same context, so either one reaches `DocsHeader`, `SiteHeader`\n// and everything else that renders a link.\n\nexport interface DocsLinkProviderProps {\n /** Any component accepting `href` and the usual anchor props. */\n component: ElementType<DocsLinkProps>;\n children: ReactNode;\n}\n\n/**\n * Injects the component every docs chrome element uses to render an internal\n * link.\n *\n * Defaults to a plain `<a>`, which is correct for Storybook, MDX previews, and\n * statically-rendered output. Apps with a client-side router inject their own\n * (`react-router`'s `Link`, `next/link`, …) so sidebar, breadcrumb, pager, and\n * TOC navigation stops triggering full page loads. Without this the docs chrome\n * would hard-navigate on every click and lose scroll position, focus, and any\n * client state on each hop.\n *\n * The same context as {@link LinkProvider}, which also takes `isCurrent`.\n */\nexport function DocsLinkProvider({ component, children }: DocsLinkProviderProps) {\n return <LinkProvider component={component}>{children}</LinkProvider>;\n}\n\nexport function useDocsLinkComponent(): ElementType<DocsLinkProps> {\n return useLinkComponent();\n}\n\n/**\n * Renders an internal link through the injected component and an external one\n * through a plain `<a>` with `rel=\"noopener noreferrer\"`. Pure in-page hashes\n * stay plain too — routers tend to treat `#section` as a route change and\n * scroll to the top instead of to the anchor.\n */\nexport function DocsLink(props: DocsLinkProps) {\n const Component = useLinkComponent();\n return renderProvidedLink(Component, { 'data-slot': 'docs-link', ...props } as DocsLinkProps);\n}\n"],"mappings":";;;AAGA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAiCE;AADF,SAAS,iBAAiB,EAAE,WAAW,SAAS,GAA0B;AAC/E,SAAO,oBAAC,gBAAa,WAAuB,UAAS;AACvD;AAEO,SAAS,uBAAmD;AACjE,SAAO,iBAAiB;AAC1B;AAQO,SAAS,SAAS,OAAsB;AAC7C,QAAM,YAAY,iBAAiB;AACnC,SAAO,mBAAmB,WAAW,EAAE,aAAa,aAAa,GAAG,MAAM,CAAkB;AAC9F;","names":[]}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/docs/DocsSidebar.tsx
|
|
4
|
+
import { useCallback, useMemo, useState } from "react";
|
|
5
|
+
import { ChevronRight } from "lucide-react";
|
|
6
|
+
import { DocsLink } from "./DocsLinkProvider.js";
|
|
7
|
+
import { cn } from "../../lib/recipe.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
function containsPath(node, path) {
|
|
10
|
+
if (!path) return false;
|
|
11
|
+
if (node.href === path) return true;
|
|
12
|
+
return (node.items ?? []).some((child) => containsPath(child, path));
|
|
13
|
+
}
|
|
14
|
+
function DocsSidebar({
|
|
15
|
+
nav,
|
|
16
|
+
currentPath,
|
|
17
|
+
label = "DOCS",
|
|
18
|
+
onNavigate,
|
|
19
|
+
className = ""
|
|
20
|
+
}) {
|
|
21
|
+
const [overrides, setOverrides] = useState({});
|
|
22
|
+
const toggle = useCallback((key, next) => {
|
|
23
|
+
setOverrides((prev) => ({ ...prev, [key]: next }));
|
|
24
|
+
}, []);
|
|
25
|
+
return /* @__PURE__ */ jsxs("nav", { "data-slot": "docs-sidebar", className: cn("not-prose print:hidden", className), "aria-label": "Documentation", children: [
|
|
26
|
+
label && /* @__PURE__ */ jsxs("p", { className: "docs-sidebar-label", children: [
|
|
27
|
+
"[ ",
|
|
28
|
+
label,
|
|
29
|
+
" ]"
|
|
30
|
+
] }),
|
|
31
|
+
/* @__PURE__ */ jsx(
|
|
32
|
+
NodeList,
|
|
33
|
+
{
|
|
34
|
+
nodes: nav,
|
|
35
|
+
currentPath,
|
|
36
|
+
depth: 0,
|
|
37
|
+
path: "",
|
|
38
|
+
overrides,
|
|
39
|
+
onToggle: toggle,
|
|
40
|
+
onNavigate
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
] });
|
|
44
|
+
}
|
|
45
|
+
function NodeList({ nodes, depth, path, ...rest }) {
|
|
46
|
+
return /* @__PURE__ */ jsx("ul", { className: "docs-sidebar-list", "data-depth": depth, children: nodes.map((node, index) => /* @__PURE__ */ jsx(
|
|
47
|
+
NodeItem,
|
|
48
|
+
{
|
|
49
|
+
node,
|
|
50
|
+
depth,
|
|
51
|
+
path: `${path}/${node.href ?? node.label}`,
|
|
52
|
+
...rest
|
|
53
|
+
},
|
|
54
|
+
`${path}/${node.href ?? node.label}#${index}`
|
|
55
|
+
)) });
|
|
56
|
+
}
|
|
57
|
+
function NodeItem({
|
|
58
|
+
node,
|
|
59
|
+
currentPath,
|
|
60
|
+
depth,
|
|
61
|
+
path,
|
|
62
|
+
overrides,
|
|
63
|
+
onToggle,
|
|
64
|
+
onNavigate
|
|
65
|
+
}) {
|
|
66
|
+
const hasChildren = (node.items?.length ?? 0) > 0;
|
|
67
|
+
const isActive = Boolean(node.href && node.href === currentPath);
|
|
68
|
+
const onActiveBranch = useMemo(
|
|
69
|
+
() => containsPath(node, currentPath),
|
|
70
|
+
[node, currentPath]
|
|
71
|
+
);
|
|
72
|
+
const expanded = overrides[path] ?? (onActiveBranch || node.defaultCollapsed !== true);
|
|
73
|
+
if (!hasChildren) {
|
|
74
|
+
return /* @__PURE__ */ jsx("li", { children: node.href ? /* @__PURE__ */ jsx(
|
|
75
|
+
DocsLink,
|
|
76
|
+
{
|
|
77
|
+
href: node.href,
|
|
78
|
+
className: "docs-sidebar-link",
|
|
79
|
+
"data-active": isActive ? "true" : void 0,
|
|
80
|
+
onClick: onNavigate,
|
|
81
|
+
...isActive ? { "aria-current": "page" } : {},
|
|
82
|
+
children: node.label
|
|
83
|
+
}
|
|
84
|
+
) : /* @__PURE__ */ jsx("span", { className: "docs-sidebar-link docs-sidebar-link-static", children: node.label }) });
|
|
85
|
+
}
|
|
86
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "docs-sidebar-group", children: [
|
|
88
|
+
/* @__PURE__ */ jsx(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
className: "docs-sidebar-toggle",
|
|
93
|
+
onClick: () => onToggle(path, !expanded),
|
|
94
|
+
"aria-expanded": expanded,
|
|
95
|
+
"aria-label": `${expanded ? "Collapse" : "Expand"} ${node.label}`,
|
|
96
|
+
children: /* @__PURE__ */ jsx(
|
|
97
|
+
ChevronRight,
|
|
98
|
+
{
|
|
99
|
+
size: 14,
|
|
100
|
+
"aria-hidden": "true",
|
|
101
|
+
style: {
|
|
102
|
+
transform: expanded ? "rotate(90deg)" : "none",
|
|
103
|
+
transition: "transform 0.15s ease"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
node.href ? /* @__PURE__ */ jsx(
|
|
110
|
+
DocsLink,
|
|
111
|
+
{
|
|
112
|
+
href: node.href,
|
|
113
|
+
className: "docs-sidebar-link docs-sidebar-group-label",
|
|
114
|
+
"data-active": isActive ? "true" : void 0,
|
|
115
|
+
onClick: onNavigate,
|
|
116
|
+
...isActive ? { "aria-current": "page" } : {},
|
|
117
|
+
children: node.label
|
|
118
|
+
}
|
|
119
|
+
) : /* @__PURE__ */ jsx(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
type: "button",
|
|
123
|
+
className: "docs-sidebar-link docs-sidebar-group-label",
|
|
124
|
+
onClick: () => onToggle(path, !expanded),
|
|
125
|
+
"aria-expanded": expanded,
|
|
126
|
+
children: node.label
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
] }),
|
|
130
|
+
expanded && /* @__PURE__ */ jsx(
|
|
131
|
+
NodeList,
|
|
132
|
+
{
|
|
133
|
+
nodes: node.items ?? [],
|
|
134
|
+
currentPath,
|
|
135
|
+
depth: depth + 1,
|
|
136
|
+
path,
|
|
137
|
+
overrides,
|
|
138
|
+
onToggle,
|
|
139
|
+
onNavigate
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
] });
|
|
143
|
+
}
|
|
144
|
+
export {
|
|
145
|
+
DocsSidebar
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=DocsSidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/docs/DocsSidebar.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { ChevronRight } from 'lucide-react';\nimport { DocsLink } from './DocsLinkProvider';\nimport { cn } from '../../lib/recipe';\n\nexport interface DocsNavNode {\n label: string;\n /** Omit for a pure grouping node. */\n href?: string;\n items?: readonly DocsNavNode[];\n /** Start collapsed even when it does not contain the active page. */\n defaultCollapsed?: boolean;\n}\n\nexport interface DocsSidebarProps {\n nav: readonly DocsNavNode[];\n /** Current page path, matched against `href` to mark the active item. */\n currentPath?: string;\n /** Shown above the tree. Pass `null` to omit. */\n label?: string | null;\n /** Called after any nav link is clicked — close the mobile drawer here. */\n onNavigate?: () => void;\n className?: string;\n}\n\n/** Does this subtree contain `path`? Drives auto-expansion of the active branch. */\nfunction containsPath(node: DocsNavNode, path: string | undefined): boolean {\n if (!path) return false;\n if (node.href === path) return true;\n return (node.items ?? []).some((child) => containsPath(child, path));\n}\n\n/**\n * Collapsible documentation navigation tree.\n *\n * Groups containing the current page expand on mount and cannot be\n * force-collapsed by stale state — expansion is stored as an override keyed by\n * group path, so navigating into a collapsed section still reveals it.\n */\nexport function DocsSidebar({\n nav,\n currentPath,\n label = 'DOCS',\n onNavigate,\n className = '',\n}: DocsSidebarProps) {\n const [overrides, setOverrides] = useState<Record<string, boolean>>({});\n\n const toggle = useCallback((key: string, next: boolean) => {\n setOverrides((prev) => ({ ...prev, [key]: next }));\n }, []);\n\n return (\n <nav data-slot=\"docs-sidebar\" className={cn('not-prose print:hidden', className)} aria-label=\"Documentation\">\n {label && <p className=\"docs-sidebar-label\">[ {label} ]</p>}\n <NodeList\n nodes={nav}\n currentPath={currentPath}\n depth={0}\n path=\"\"\n overrides={overrides}\n onToggle={toggle}\n onNavigate={onNavigate}\n />\n </nav>\n );\n}\n\ninterface NodeListProps {\n nodes: readonly DocsNavNode[];\n currentPath: string | undefined;\n depth: number;\n path: string;\n overrides: Record<string, boolean>;\n onToggle: (key: string, next: boolean) => void;\n onNavigate: (() => void) | undefined;\n}\n\nfunction NodeList({ nodes, depth, path, ...rest }: NodeListProps) {\n return (\n <ul className=\"docs-sidebar-list\" data-depth={depth}>\n {nodes.map((node, index) => (\n <NodeItem\n key={`${path}/${node.href ?? node.label}#${index}`}\n node={node}\n depth={depth}\n path={`${path}/${node.href ?? node.label}`}\n {...rest}\n />\n ))}\n </ul>\n );\n}\n\ninterface NodeItemProps extends Omit<NodeListProps, 'nodes'> {\n node: DocsNavNode;\n}\n\nfunction NodeItem({\n node,\n currentPath,\n depth,\n path,\n overrides,\n onToggle,\n onNavigate,\n}: NodeItemProps) {\n const hasChildren = (node.items?.length ?? 0) > 0;\n const isActive = Boolean(node.href && node.href === currentPath);\n\n const onActiveBranch = useMemo(\n () => containsPath(node, currentPath),\n [node, currentPath],\n );\n\n // Precedence: an explicit user toggle wins; otherwise the branch holding the\n // current page is open, and `defaultCollapsed` decides the rest.\n const expanded =\n overrides[path] ?? (onActiveBranch || node.defaultCollapsed !== true);\n\n if (!hasChildren) {\n return (\n <li>\n {node.href ? (\n <DocsLink\n href={node.href}\n className=\"docs-sidebar-link\"\n data-active={isActive ? 'true' : undefined}\n onClick={onNavigate}\n {...(isActive ? { 'aria-current': 'page' as const } : {})}\n >\n {node.label}\n </DocsLink>\n ) : (\n <span className=\"docs-sidebar-link docs-sidebar-link-static\">{node.label}</span>\n )}\n </li>\n );\n }\n\n return (\n <li>\n <div className=\"docs-sidebar-group\">\n <button\n type=\"button\"\n className=\"docs-sidebar-toggle\"\n onClick={() => onToggle(path, !expanded)}\n aria-expanded={expanded}\n aria-label={`${expanded ? 'Collapse' : 'Expand'} ${node.label}`}\n >\n <ChevronRight\n size={14}\n aria-hidden=\"true\"\n style={{\n transform: expanded ? 'rotate(90deg)' : 'none',\n transition: 'transform 0.15s ease',\n }}\n />\n </button>\n\n {node.href ? (\n <DocsLink\n href={node.href}\n className=\"docs-sidebar-link docs-sidebar-group-label\"\n data-active={isActive ? 'true' : undefined}\n onClick={onNavigate}\n {...(isActive ? { 'aria-current': 'page' as const } : {})}\n >\n {node.label}\n </DocsLink>\n ) : (\n <button\n type=\"button\"\n className=\"docs-sidebar-link docs-sidebar-group-label\"\n onClick={() => onToggle(path, !expanded)}\n aria-expanded={expanded}\n >\n {node.label}\n </button>\n )}\n </div>\n\n {expanded && (\n <NodeList\n nodes={node.items ?? []}\n currentPath={currentPath}\n depth={depth + 1}\n path={path}\n overrides={overrides}\n onToggle={onToggle}\n onNavigate={onNavigate}\n />\n )}\n </li>\n );\n}\n"],"mappings":"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","names":[]}
|