@roadlittledawn/docs-design-system-react 0.4.0 → 0.5.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.
@@ -37,6 +37,13 @@ export var Basic = {
37
37
  spacing: '0.5rem',
38
38
  allowMultiple: true,
39
39
  },
40
+ parameters: {
41
+ docs: {
42
+ source: {
43
+ code: "<CollapserGroup spacing=\"0.5rem\" allowMultiple>\n <Collapser title=\"What is this documentation system?\">\n <p>\n This is a comprehensive documentation design system that provides\n reusable components and guidelines for creating effective technical\n documentation.\n </p>\n </Collapser>\n <Collapser title=\"How do I get started?\">\n <p>\n Start by installing the component package, then explore the components\n in Storybook to understand their usage and configuration options.\n </p>\n </Collapser>\n <Collapser title=\"Can I customize the components?\">\n <p>\n Yes! All components accept a className prop for custom styling, and you\n can override the default styles using CSS.\n </p>\n </Collapser>\n</CollapserGroup>",
44
+ },
45
+ },
46
+ },
40
47
  render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "What is this documentation system?", children: _jsx("p", { children: "This is a comprehensive documentation design system that provides reusable components and guidelines for creating effective technical documentation." }) }), _jsx(Collapser, { title: "How do I get started?", children: _jsx("p", { children: "Start by installing the component package, then explore the components in Storybook to understand their usage and configuration options." }) }), _jsx(Collapser, { title: "Can I customize the components?", children: _jsx("p", { children: "Yes! All components accept a className prop for custom styling, and you can override the default styles using CSS." }) })] }))); },
41
48
  };
42
49
  /**
@@ -46,6 +53,13 @@ export var AccordionMode = {
46
53
  args: {
47
54
  allowMultiple: false,
48
55
  },
56
+ parameters: {
57
+ docs: {
58
+ source: {
59
+ code: "<CollapserGroup allowMultiple={false}>\n <Collapser title=\"Installation\">\n <p>Install the package using npm or yarn:</p>\n <pre>npm install @roadlittledawn/docs-design-system</pre>\n </Collapser>\n <Collapser title=\"Configuration\">\n <p>Import the CSS and components in your application:</p>\n <pre>import '@roadlittledawn/docs-design-system/dist/styles.css';</pre>\n </Collapser>\n <Collapser title=\"Usage\">\n <p>Use the components in your React application:</p>\n <pre>{'<Button variant=\"primary\">Click me</Button>'}</pre>\n </Collapser>\n</CollapserGroup>",
60
+ },
61
+ },
62
+ },
49
63
  render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsxs(Collapser, { title: "Installation", children: [_jsx("p", { children: "Install the package using npm or yarn:" }), _jsx("pre", { children: "npm install @roadlittledawn/docs-design-system" })] }), _jsxs(Collapser, { title: "Configuration", children: [_jsx("p", { children: "Import the CSS and components in your application:" }), _jsx("pre", { children: "import '@roadlittledawn/docs-design-system/dist/styles.css';" })] }), _jsxs(Collapser, { title: "Usage", children: [_jsx("p", { children: "Use the components in your React application:" }), _jsx("pre", { children: '<Button variant="primary">Click me</Button>' })] })] }))); },
50
64
  };
51
65
  /**
@@ -55,6 +69,13 @@ export var CustomSpacing = {
55
69
  args: {
56
70
  spacing: '1.5rem',
57
71
  },
72
+ parameters: {
73
+ docs: {
74
+ source: {
75
+ code: "<CollapserGroup spacing=\"1.5rem\">\n <Collapser title=\"Section 1\">\n <p>Content with larger spacing between sections.</p>\n </Collapser>\n <Collapser title=\"Section 2\">\n <p>This makes the layout more breathable.</p>\n </Collapser>\n <Collapser title=\"Section 3\">\n <p>Useful for prominent content sections.</p>\n </Collapser>\n</CollapserGroup>",
76
+ },
77
+ },
78
+ },
58
79
  render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Section 1", children: _jsx("p", { children: "Content with larger spacing between sections." }) }), _jsx(Collapser, { title: "Section 2", children: _jsx("p", { children: "This makes the layout more breathable." }) }), _jsx(Collapser, { title: "Section 3", children: _jsx("p", { children: "Useful for prominent content sections." }) })] }))); },
59
80
  };
60
81
  /**
@@ -64,6 +85,13 @@ export var DefaultOpen = {
64
85
  args: {
65
86
  defaultOpen: 0,
66
87
  },
88
+ parameters: {
89
+ docs: {
90
+ source: {
91
+ code: "<CollapserGroup defaultOpen={0}>\n <Collapser title=\"Getting Started\">\n <p>This section is open by default.</p>\n </Collapser>\n <Collapser title=\"Advanced Topics\">\n <p>This section starts closed.</p>\n </Collapser>\n <Collapser title=\"API Reference\">\n <p>This section also starts closed.</p>\n </Collapser>\n</CollapserGroup>",
92
+ },
93
+ },
94
+ },
67
95
  render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Getting Started", children: _jsx("p", { children: "This section is open by default." }) }), _jsx(Collapser, { title: "Advanced Topics", children: _jsx("p", { children: "This section starts closed." }) }), _jsx(Collapser, { title: "API Reference", children: _jsx("p", { children: "This section also starts closed." }) })] }))); },
68
96
  };
69
97
  /**
@@ -73,5 +101,12 @@ export var MultipleDefaultOpen = {
73
101
  args: {
74
102
  defaultOpen: [0, 2],
75
103
  },
104
+ parameters: {
105
+ docs: {
106
+ source: {
107
+ code: "<CollapserGroup defaultOpen={[0, 2]}>\n <Collapser title=\"Introduction\">\n <p>This section is open by default.</p>\n </Collapser>\n <Collapser title=\"Installation\">\n <p>This section starts closed.</p>\n </Collapser>\n <Collapser title=\"Quick Start\">\n <p>This section is also open by default.</p>\n </Collapser>\n</CollapserGroup>",
108
+ },
109
+ },
110
+ },
76
111
  render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Introduction", children: _jsx("p", { children: "This section is open by default." }) }), _jsx(Collapser, { title: "Installation", children: _jsx("p", { children: "This section starts closed." }) }), _jsx(Collapser, { title: "Quick Start", children: _jsx("p", { children: "This section is also open by default." }) })] }))); },
77
112
  };
@@ -24,6 +24,13 @@ export var Level1 = {
24
24
  level: 1,
25
25
  children: 'Page Title (H1)',
26
26
  },
27
+ parameters: {
28
+ docs: {
29
+ source: {
30
+ code: "<Heading level={1}>Page Title (H1)</Heading>",
31
+ },
32
+ },
33
+ },
27
34
  };
28
35
  /**
29
36
  * Level 2 heading (h2) - used for major sections.
@@ -33,6 +40,13 @@ export var Level2 = {
33
40
  level: 2,
34
41
  children: 'Section Title (H2)',
35
42
  },
43
+ parameters: {
44
+ docs: {
45
+ source: {
46
+ code: "<Heading level={2}>Section Title (H2)</Heading>",
47
+ },
48
+ },
49
+ },
36
50
  };
37
51
  /**
38
52
  * Level 3 heading (h3) - used for subsections.
@@ -42,6 +56,13 @@ export var Level3 = {
42
56
  level: 3,
43
57
  children: 'Subsection Title (H3)',
44
58
  },
59
+ parameters: {
60
+ docs: {
61
+ source: {
62
+ code: "<Heading level={3}>Subsection Title (H3)</Heading>",
63
+ },
64
+ },
65
+ },
45
66
  };
46
67
  /**
47
68
  * Level 4 heading (h4) - used for sub-subsections.
@@ -51,16 +72,37 @@ export var Level4 = {
51
72
  level: 4,
52
73
  children: 'Sub-subsection Title (H4)',
53
74
  },
75
+ parameters: {
76
+ docs: {
77
+ source: {
78
+ code: "<Heading level={4}>Sub-subsection Title (H4)</Heading>",
79
+ },
80
+ },
81
+ },
54
82
  };
55
83
  /**
56
84
  * All heading levels displayed together to show the hierarchy.
57
85
  */
58
86
  export var AllLevels = {
87
+ parameters: {
88
+ docs: {
89
+ source: {
90
+ code: "<div>\n <Heading level={1}>Level 1 - Main Page Title</Heading>\n <p>Use h1 for the main page title. There should only be one h1 per page.</p>\n\n <Heading level={2}>Level 2 - Major Section</Heading>\n <p>Use h2 for major sections of your page.</p>\n\n <Heading level={3}>Level 3 - Subsection</Heading>\n <p>Use h3 for subsections within major sections.</p>\n\n <Heading level={4}>Level 4 - Sub-subsection</Heading>\n <p>Use h4 for sub-subsections. If you need deeper levels, reconsider your document structure.</p>\n</div>",
91
+ },
92
+ },
93
+ },
59
94
  render: function () { return (_jsxs("div", { children: [_jsx(Heading, { level: 1, children: "Level 1 - Main Page Title" }), _jsx("p", { style: { marginBottom: '1rem', color: '#666' }, children: "Use h1 for the main page title. There should only be one h1 per page." }), _jsx(Heading, { level: 2, children: "Level 2 - Major Section" }), _jsx("p", { style: { marginBottom: '1rem', color: '#666' }, children: "Use h2 for major sections of your page." }), _jsx(Heading, { level: 3, children: "Level 3 - Subsection" }), _jsx("p", { style: { marginBottom: '1rem', color: '#666' }, children: "Use h3 for subsections within major sections." }), _jsx(Heading, { level: 4, children: "Level 4 - Sub-subsection" }), _jsx("p", { style: { color: '#666' }, children: "Use h4 for sub-subsections. If you need deeper levels, reconsider your document structure." })] })); },
60
95
  };
61
96
  /**
62
97
  * Example of a properly structured document hierarchy.
63
98
  */
64
99
  export var DocumentStructure = {
100
+ parameters: {
101
+ docs: {
102
+ source: {
103
+ code: "<div>\n <Heading level={1}>Getting Started Guide</Heading>\n <p>Introduction to the documentation system...</p>\n\n <Heading level={2}>Installation</Heading>\n <p>How to install the components...</p>\n\n <Heading level={3}>Prerequisites</Heading>\n <p>What you need before installing...</p>\n\n <Heading level={3}>Installation Steps</Heading>\n <p>Step-by-step installation instructions...</p>\n\n <Heading level={2}>Configuration</Heading>\n <p>How to configure the system...</p>\n\n <Heading level={3}>Basic Configuration</Heading>\n <p>Essential configuration options...</p>\n\n <Heading level={4}>Environment Variables</Heading>\n <p>Required environment variables...</p>\n</div>",
104
+ },
105
+ },
106
+ },
65
107
  render: function () { return (_jsxs("div", { children: [_jsx(Heading, { level: 1, children: "Getting Started Guide" }), _jsx("p", { children: "Introduction to the documentation system..." }), _jsx(Heading, { level: 2, children: "Installation" }), _jsx("p", { children: "How to install the components..." }), _jsx(Heading, { level: 3, children: "Prerequisites" }), _jsx("p", { children: "What you need before installing..." }), _jsx(Heading, { level: 3, children: "Installation Steps" }), _jsx("p", { children: "Step-by-step installation instructions..." }), _jsx(Heading, { level: 2, children: "Configuration" }), _jsx("p", { children: "How to configure the system..." }), _jsx(Heading, { level: 3, children: "Basic Configuration" }), _jsx("p", { children: "Essential configuration options..." }), _jsx(Heading, { level: 4, children: "Environment Variables" }), _jsx("p", { children: "Required environment variables..." })] })); },
66
108
  };
@@ -24,6 +24,13 @@ export var Internal = {
24
24
  href: '/docs/components',
25
25
  children: 'View Components Documentation',
26
26
  },
27
+ parameters: {
28
+ docs: {
29
+ source: {
30
+ code: "<Link href=\"/docs/components\">View Components Documentation</Link>",
31
+ },
32
+ },
33
+ },
27
34
  };
28
35
  /**
29
36
  * External link (absolute URL) - opens in new tab with icon.
@@ -33,6 +40,13 @@ export var External = {
33
40
  href: 'https://github.com/your-repo/docs-design-system',
34
41
  children: 'View on GitHub',
35
42
  },
43
+ parameters: {
44
+ docs: {
45
+ source: {
46
+ code: "<Link href=\"https://github.com/your-repo/docs-design-system\">View on GitHub</Link>",
47
+ },
48
+ },
49
+ },
36
50
  };
37
51
  /**
38
52
  * Anchor link to a section on the same page.
@@ -42,22 +56,50 @@ export var AnchorLink = {
42
56
  href: '#installation',
43
57
  children: 'Jump to Installation',
44
58
  },
59
+ parameters: {
60
+ docs: {
61
+ source: {
62
+ code: "<Link href=\"#installation\">Jump to Installation</Link>",
63
+ },
64
+ },
65
+ },
45
66
  };
46
67
  /**
47
68
  * Link within body text.
48
69
  */
49
70
  export var InlineLink = {
71
+ parameters: {
72
+ docs: {
73
+ source: {
74
+ code: "<p>\n For more information, check out the{' '}\n <Link href=\"/docs/getting-started\">Getting Started guide</Link>\n {' '}or visit our{' '}\n <Link href=\"https://github.com\">GitHub repository</Link>.\n</p>",
75
+ },
76
+ },
77
+ },
50
78
  render: function () { return (_jsxs("p", { children: ["For more information, check out the", ' ', _jsx(Link, { href: "/docs/getting-started", children: "Getting Started guide" }), ' ', "or visit our", ' ', _jsx(Link, { href: "https://github.com", children: "GitHub repository" }), "."] })); },
51
79
  };
52
80
  /**
53
81
  * Multiple links in a list.
54
82
  */
55
83
  export var LinkList = {
84
+ parameters: {
85
+ docs: {
86
+ source: {
87
+ code: "<ul style={{ listStyle: 'none', padding: 0, display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>\n <li><Link href=\"/docs/introduction\">Introduction</Link></li>\n <li><Link href=\"/docs/components\">Components</Link></li>\n <li><Link href=\"/docs/best-practices\">Best Practices</Link></li>\n <li><Link href=\"https://github.com\">GitHub</Link></li>\n <li><Link href=\"https://npmjs.com\">NPM</Link></li>\n</ul>",
88
+ },
89
+ },
90
+ },
56
91
  render: function () { return (_jsxs("ul", { style: { listStyle: 'none', padding: 0, display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsx("li", { children: _jsx(Link, { href: "/docs/introduction", children: "Introduction" }) }), _jsx("li", { children: _jsx(Link, { href: "/docs/components", children: "Components" }) }), _jsx("li", { children: _jsx(Link, { href: "/docs/best-practices", children: "Best Practices" }) }), _jsx("li", { children: _jsx(Link, { href: "https://github.com", children: "GitHub" }) }), _jsx("li", { children: _jsx(Link, { href: "https://npmjs.com", children: "NPM" }) })] })); },
57
92
  };
58
93
  /**
59
94
  * Comparison of internal and external links.
60
95
  */
61
96
  export var InternalVsExternal = {
97
+ parameters: {
98
+ docs: {
99
+ source: {
100
+ code: "<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>\n <div>\n <strong>Internal Link:</strong>\n <br />\n <Link href=\"/docs/components\">Components (internal)</Link>\n <p style={{ fontSize: '0.875rem', color: '#666', marginTop: '0.5rem' }}>\n Opens in the same tab, no icon\n </p>\n </div>\n <div>\n <strong>External Link:</strong>\n <br />\n <Link href=\"https://example.com\">Example.com (external)</Link>\n <p style={{ fontSize: '0.875rem', color: '#666', marginTop: '0.5rem' }}>\n Opens in new tab, includes external link icon\n </p>\n </div>\n</div>",
101
+ },
102
+ },
103
+ },
62
104
  render: function () { return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [_jsxs("div", { children: [_jsx("strong", { children: "Internal Link:" }), _jsx("br", {}), _jsx(Link, { href: "/docs/components", children: "Components (internal)" }), _jsx("p", { style: { fontSize: '0.875rem', color: '#666', marginTop: '0.5rem' }, children: "Opens in the same tab, no icon" })] }), _jsxs("div", { children: [_jsx("strong", { children: "External Link:" }), _jsx("br", {}), _jsx(Link, { href: "https://example.com", children: "Example.com (external)" }), _jsx("p", { style: { fontSize: '0.875rem', color: '#666', marginTop: '0.5rem' }, children: "Opens in new tab, includes external link icon" })] })] })); },
63
105
  };
@@ -0,0 +1,64 @@
1
+ import { ReactNode } from "react";
2
+ export interface GlossaryData {
3
+ /** The canonical term (used for semantic markup) */
4
+ term: string;
5
+ /** Display title shown in the popover header */
6
+ title: string;
7
+ /**
8
+ * Definition content. Accepts ReactNode so consumers can pre-render
9
+ * markdown (e.g. via react-markdown) or pass arbitrary JSX.
10
+ */
11
+ definition: ReactNode;
12
+ }
13
+ export interface PreviewData {
14
+ /** Page or article title */
15
+ title: string;
16
+ /** Short excerpt or summary */
17
+ excerpt?: ReactNode;
18
+ /** Optional featured image URL */
19
+ imageUrl?: string;
20
+ /** Optional URL to the full content */
21
+ href?: string;
22
+ /** Link text. Default: "Read more" */
23
+ linkText?: string;
24
+ }
25
+ export type PopoverPlacement = "auto" | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end";
26
+ export type PopoverSize = "sm" | "md" | "lg";
27
+ export interface PopoverProps {
28
+ /**
29
+ * Arbitrary React content to render inside the popover.
30
+ * Use when built-in templates don't fit your use case.
31
+ */
32
+ content?: ReactNode;
33
+ /** Render a styled glossary definition popover */
34
+ glossary?: GlossaryData;
35
+ /** Render a styled content preview popover (Wikipedia-style) */
36
+ preview?: PreviewData;
37
+ /**
38
+ * Preferred placement relative to the trigger.
39
+ * "auto" detects available viewport space.
40
+ * @default "auto"
41
+ */
42
+ placement?: PopoverPlacement;
43
+ /**
44
+ * Popover width.
45
+ * sm = 240px, md = 320px, lg = 480px
46
+ * @default "md"
47
+ */
48
+ size?: PopoverSize;
49
+ /**
50
+ * Milliseconds to wait before showing the popover on hover.
51
+ * @default 200
52
+ */
53
+ showDelay?: number;
54
+ /**
55
+ * Milliseconds to wait before hiding the popover on hover-out.
56
+ * @default 150
57
+ */
58
+ hideDelay?: number;
59
+ /** The trigger element — the text or content that reveals the popover on hover/tap */
60
+ children: ReactNode;
61
+ /** Additional CSS classes on the trigger wrapper */
62
+ className?: string;
63
+ }
64
+ export declare function Popover({ content, glossary, preview, placement, size, showDelay, hideDelay, children, className, }: PopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,202 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
+ import { useRef, useId, useCallback, useEffect, useState, } from "react";
14
+ function GlossaryTemplate(_a) {
15
+ var data = _a.data;
16
+ return (_jsxs("div", { className: "dds-popover-glossary", children: [_jsx("span", { className: "dds-popover-eyebrow", children: "Glossary" }), _jsx("p", { className: "dds-popover-title", children: _jsx("dfn", { title: data.term, children: data.title }) }), _jsx("div", { className: "dds-popover-body", children: data.definition })] }));
17
+ }
18
+ function PreviewTemplate(_a) {
19
+ var _b;
20
+ var data = _a.data;
21
+ var _c = useState(false), imageError = _c[0], setImageError = _c[1];
22
+ return (_jsxs("div", { className: "dds-popover-preview", children: [data.imageUrl && !imageError && (_jsx("div", { className: "dds-popover-preview-image-wrap", children: _jsx("img", { className: "dds-popover-preview-image", src: data.imageUrl, alt: "", "aria-hidden": "true", onError: function () { return setImageError(true); } }) })), _jsxs("div", { className: "dds-popover-preview-body", children: [_jsx("p", { className: "dds-popover-title", children: data.title }), data.excerpt && (_jsx("div", { className: "dds-popover-body", children: data.excerpt })), data.href && (_jsxs("a", { className: "dds-popover-preview-link", href: data.href, target: "_blank", rel: "noopener noreferrer", children: [(_b = data.linkText) !== null && _b !== void 0 ? _b : "Read more", " \u2192"] }))] })] }));
23
+ }
24
+ /** Resolve the actual placement given preferred placement and available space */
25
+ function resolvePlacement(triggerRect, preferred) {
26
+ if (preferred !== "auto")
27
+ return preferred;
28
+ var spaceAbove = triggerRect.top;
29
+ var spaceBelow = window.innerHeight - triggerRect.bottom;
30
+ // Prefer top if there's more space above (and at least 180px)
31
+ if (spaceAbove > spaceBelow && spaceAbove >= 180)
32
+ return "top";
33
+ return "bottom";
34
+ }
35
+ /** Compute pixel coordinates for the popover given trigger rect and placement */
36
+ function computePosition(triggerRect, popoverEl, placement) {
37
+ var popoverWidth = popoverEl.offsetWidth;
38
+ var popoverHeight = popoverEl.offsetHeight;
39
+ var gap = 8; // px gap between trigger and popover
40
+ var viewportPad = 8; // min distance from viewport edges
41
+ var top = 0;
42
+ var left = 0;
43
+ var triggerCenterX = triggerRect.left + triggerRect.width / 2;
44
+ // Vertical
45
+ var isTop = placement.startsWith("top");
46
+ if (isTop) {
47
+ top = triggerRect.top - popoverHeight - gap;
48
+ }
49
+ else {
50
+ top = triggerRect.bottom + gap;
51
+ }
52
+ // Horizontal
53
+ if (placement.endsWith("-start")) {
54
+ left = triggerRect.left;
55
+ }
56
+ else if (placement.endsWith("-end")) {
57
+ left = triggerRect.right - popoverWidth;
58
+ }
59
+ else {
60
+ // center-aligned
61
+ left = triggerCenterX - popoverWidth / 2;
62
+ }
63
+ // Clamp to viewport horizontally
64
+ left = Math.max(viewportPad, Math.min(left, window.innerWidth - popoverWidth - viewportPad));
65
+ // Flip vertically if overflowing
66
+ if (isTop && top < viewportPad) {
67
+ top = triggerRect.bottom + gap;
68
+ }
69
+ else if (!isTop && top + popoverHeight > window.innerHeight - viewportPad) {
70
+ top = triggerRect.top - popoverHeight - gap;
71
+ }
72
+ return { top: top, left: left };
73
+ }
74
+ export function Popover(_a) {
75
+ var content = _a.content, glossary = _a.glossary, preview = _a.preview, _b = _a.placement, placement = _b === void 0 ? "auto" : _b, _c = _a.size, size = _c === void 0 ? "md" : _c, _d = _a.showDelay, showDelay = _d === void 0 ? 200 : _d, _e = _a.hideDelay, hideDelay = _e === void 0 ? 150 : _e, children = _a.children, _f = _a.className, className = _f === void 0 ? "" : _f;
76
+ var id = useId();
77
+ var popoverId = "dds-popover-".concat(id.replace(/:/g, ""));
78
+ var triggerRef = useRef(null);
79
+ var popoverRef = useRef(null);
80
+ var showTimerRef = useRef(null);
81
+ var hideTimerRef = useRef(null);
82
+ var clearTimers = useCallback(function () {
83
+ if (showTimerRef.current)
84
+ clearTimeout(showTimerRef.current);
85
+ if (hideTimerRef.current)
86
+ clearTimeout(hideTimerRef.current);
87
+ }, []);
88
+ var positionPopover = useCallback(function () {
89
+ var trigger = triggerRef.current;
90
+ var popover = popoverRef.current;
91
+ if (!trigger || !popover)
92
+ return;
93
+ // The Popover API renders in the top layer, outside the normal DOM tree,
94
+ // so it doesn't inherit class-based dark mode from ancestor elements.
95
+ // Mirror the theme onto the popover element itself.
96
+ var isDark = document.documentElement.classList.contains("dds-dark") ||
97
+ document.documentElement.dataset.ddsTheme === "dark" ||
98
+ document.body.classList.contains("dds-dark") ||
99
+ document.body.dataset.ddsTheme === "dark" ||
100
+ !!trigger.closest(".dds-dark, [data-dds-theme='dark']");
101
+ if (isDark) {
102
+ popover.setAttribute("data-dds-theme", "dark");
103
+ }
104
+ else {
105
+ popover.removeAttribute("data-dds-theme");
106
+ }
107
+ var triggerRect = trigger.getBoundingClientRect();
108
+ var resolved = resolvePlacement(triggerRect, placement);
109
+ var _a = computePosition(triggerRect, popover, resolved), top = _a.top, left = _a.left;
110
+ popover.style.top = "".concat(top, "px");
111
+ popover.style.left = "".concat(left, "px");
112
+ // Set placement data attribute for CSS arrow/animation direction
113
+ var baseDir = resolved.startsWith("top") ? "top" : "bottom";
114
+ popover.setAttribute("data-placement", baseDir);
115
+ }, [placement]);
116
+ var showPopover = useCallback(function () {
117
+ clearTimers();
118
+ showTimerRef.current = setTimeout(function () {
119
+ var popover = popoverRef.current;
120
+ if (!popover)
121
+ return;
122
+ // Position before showing so it doesn't flash in the wrong spot
123
+ popover.style.visibility = "hidden";
124
+ try {
125
+ popover.showPopover();
126
+ }
127
+ catch (_a) {
128
+ // Popover API not supported — fall back to display toggle
129
+ popover.style.display = "block";
130
+ }
131
+ positionPopover();
132
+ popover.style.visibility = "";
133
+ }, showDelay);
134
+ }, [clearTimers, showDelay, positionPopover]);
135
+ var hidePopover = useCallback(function () {
136
+ clearTimers();
137
+ hideTimerRef.current = setTimeout(function () {
138
+ var popover = popoverRef.current;
139
+ if (!popover)
140
+ return;
141
+ try {
142
+ popover.hidePopover();
143
+ }
144
+ catch (_a) {
145
+ popover.style.display = "none";
146
+ }
147
+ }, hideDelay);
148
+ }, [clearTimers, hideDelay]);
149
+ // Reposition on scroll/resize while open
150
+ useEffect(function () {
151
+ var handleReposition = function () {
152
+ var popover = popoverRef.current;
153
+ if (!popover)
154
+ return;
155
+ // Only reposition if visible (Popover API or display fallback)
156
+ if (popover.matches(":popover-open") ||
157
+ popover.style.display === "block") {
158
+ positionPopover();
159
+ }
160
+ };
161
+ window.addEventListener("scroll", handleReposition, { passive: true });
162
+ window.addEventListener("resize", handleReposition, { passive: true });
163
+ return function () {
164
+ window.removeEventListener("scroll", handleReposition);
165
+ window.removeEventListener("resize", handleReposition);
166
+ };
167
+ }, [positionPopover]);
168
+ // Cleanup timers on unmount
169
+ useEffect(function () { return function () { return clearTimers(); }; }, [clearTimers]);
170
+ var triggerClasses = ["dds-popover-trigger", className]
171
+ .filter(Boolean)
172
+ .join(" ");
173
+ var popoverClasses = [
174
+ "dds-popover",
175
+ "dds-popover-".concat(size),
176
+ ]
177
+ .filter(Boolean)
178
+ .join(" ");
179
+ // Resolve which content to render
180
+ var popoverContent = content !== null && content !== void 0 ? content : (glossary ? (_jsx(GlossaryTemplate, { data: glossary })) : preview ? (_jsx(PreviewTemplate, { data: preview })) : null);
181
+ if (!popoverContent)
182
+ return _jsx(_Fragment, { children: children });
183
+ return (_jsxs(_Fragment, { children: [_jsx("span", { ref: triggerRef, className: triggerClasses, onMouseEnter: showPopover, onMouseLeave: hidePopover, onFocus: showPopover, onBlur: hidePopover,
184
+ // Touch: toggle on tap (clear any pending hover timers first)
185
+ onClick: function () {
186
+ clearTimers();
187
+ var popover = popoverRef.current;
188
+ if (!popover)
189
+ return;
190
+ try {
191
+ popover.togglePopover();
192
+ if (popover.matches(":popover-open"))
193
+ positionPopover();
194
+ }
195
+ catch (_a) {
196
+ var isVisible = popover.style.display === "block";
197
+ popover.style.display = isVisible ? "none" : "block";
198
+ if (!isVisible)
199
+ positionPopover();
200
+ }
201
+ }, "aria-describedby": popoverId, tabIndex: 0, children: children }), _jsx("div", __assign({ ref: popoverRef, id: popoverId }, { popover: "auto" }, { className: popoverClasses, onMouseEnter: clearTimers, onMouseLeave: hidePopover, children: popoverContent }))] }));
202
+ }
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Popover } from "./Popover";
3
+ declare const meta: Meta<typeof Popover>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Popover>;
6
+ /**
7
+ * The glossary template renders a structured definition popover.
8
+ * The `definition` field accepts `ReactNode` — pre-render any markdown
9
+ * using your preferred renderer before passing it in.
10
+ */
11
+ export declare const GlossaryDefinition: Story;
12
+ /**
13
+ * The preview template renders a Wikipedia-style content preview with an
14
+ * optional featured image, excerpt, and "Read more" link.
15
+ */
16
+ export declare const ContentPreview: Story;
17
+ /**
18
+ * Pass any React element as the `content` prop when the built-in templates
19
+ * don't fit your use case.
20
+ */
21
+ export declare const CustomContent: Story;
22
+ /**
23
+ * Size variants — sm (240px), md (320px), lg (480px).
24
+ */
25
+ export declare const Sizes: Story;
26
+ /**
27
+ * Placement variants control which side of the trigger the popover appears on.
28
+ * "auto" (default) detects available viewport space.
29
+ */
30
+ export declare const Placement: Story;
31
+ /**
32
+ * Multiple popovers in a paragraph — only one can be open at a time
33
+ * (native Popover API `popover="auto"` behaviour).
34
+ */
35
+ export declare const InlineParagraph: Story;