@roadlittledawn/docs-design-system-react 0.9.1 → 0.11.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.
@@ -0,0 +1,41 @@
1
+ import { ReactNode } from "react";
2
+ export interface BreadcrumbItem {
3
+ /** Display text for the breadcrumb segment */
4
+ label: string;
5
+ /** Optional URL for the segment; omit for the current (last) page */
6
+ href?: string;
7
+ }
8
+ export interface BreadcrumbProps {
9
+ /** Ordered list of path segments, root first */
10
+ items: BreadcrumbItem[];
11
+ /**
12
+ * Separator between segments. Accepts a string or any ReactNode (e.g. an SVG chevron icon).
13
+ * @default "/"
14
+ */
15
+ delimiter?: ReactNode;
16
+ /**
17
+ * Size variant controlling font size.
18
+ * @default "md"
19
+ */
20
+ size?: "sm" | "md";
21
+ /**
22
+ * On narrow viewports, collapse middle segments behind an expandable ellipsis (…).
23
+ * The first and last segments are always visible. Click/tap the ellipsis to reveal all.
24
+ * @default false
25
+ */
26
+ collapseOnMobile?: boolean;
27
+ /**
28
+ * On narrow viewports, let the full breadcrumb trail scroll horizontally instead of
29
+ * wrapping to multiple lines. Can be combined with `collapseOnMobile`.
30
+ * @default false
31
+ */
32
+ scrollOnMobile?: boolean;
33
+ /** Additional CSS classes for consumer extensibility */
34
+ className?: string;
35
+ }
36
+ /**
37
+ * Breadcrumb navigation component. Renders a `<nav aria-label="Breadcrumb">` with an ordered
38
+ * list of segments. The last (current) item receives `aria-current="page"`. Delimiter characters
39
+ * are marked `aria-hidden` so screen readers skip them.
40
+ */
41
+ export declare function Breadcrumb({ items, delimiter, size, collapseOnMobile, scrollOnMobile, className, }: BreadcrumbProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import { useState } from "react";
15
+ /**
16
+ * Breadcrumb navigation component. Renders a `<nav aria-label="Breadcrumb">` with an ordered
17
+ * list of segments. The last (current) item receives `aria-current="page"`. Delimiter characters
18
+ * are marked `aria-hidden` so screen readers skip them.
19
+ */
20
+ export function Breadcrumb(_a) {
21
+ var items = _a.items, _b = _a.delimiter, delimiter = _b === void 0 ? "/" : _b, _c = _a.size, size = _c === void 0 ? "md" : _c, _d = _a.collapseOnMobile, collapseOnMobile = _d === void 0 ? false : _d, _e = _a.scrollOnMobile, scrollOnMobile = _e === void 0 ? false : _e, _f = _a.className, className = _f === void 0 ? "" : _f;
22
+ var _g = useState(false), isExpanded = _g[0], setIsExpanded = _g[1];
23
+ if (items.length === 0)
24
+ return null;
25
+ var isCollapsible = collapseOnMobile && items.length > 2;
26
+ var navClasses = [
27
+ "dds-breadcrumb",
28
+ "dds-breadcrumb--".concat(size),
29
+ isCollapsible ? "dds-breadcrumb--collapsible" : "",
30
+ scrollOnMobile ? "dds-breadcrumb--scroll" : "",
31
+ className,
32
+ ]
33
+ .filter(Boolean)
34
+ .join(" ");
35
+ var firstItem = items[0];
36
+ var lastItem = items[items.length - 1];
37
+ var middleItems = items.length > 1 ? items.slice(1, -1) : [];
38
+ var hasMiddle = middleItems.length > 0;
39
+ return (_jsx("nav", __assign({ className: navClasses, "aria-label": "Breadcrumb" }, (isCollapsible ? { "data-expanded": String(isExpanded) } : {}), { children: _jsxs("ol", { className: "dds-breadcrumb-list", children: [_jsxs("li", { className: "dds-breadcrumb-item", children: [items.length === 1 ? (_jsx("span", { className: "dds-breadcrumb-current", "aria-current": "page", children: firstItem.label })) : firstItem.href ? (_jsx("a", { href: firstItem.href, className: "dds-breadcrumb-link", children: firstItem.label })) : (_jsx("span", { className: "dds-breadcrumb-link", children: firstItem.label })), items.length > 1 && (_jsx("span", { className: "dds-breadcrumb-delimiter", "aria-hidden": "true", children: delimiter }))] }), isCollapsible && hasMiddle && (_jsxs("li", { className: "dds-breadcrumb-item dds-breadcrumb-item--ellipsis", children: [_jsx("button", { type: "button", className: "dds-breadcrumb-ellipsis", "aria-label": "Show all breadcrumb items", onClick: function () { return setIsExpanded(true); }, children: "\u2026" }), _jsx("span", { className: "dds-breadcrumb-delimiter", "aria-hidden": "true", children: delimiter })] })), hasMiddle &&
40
+ middleItems.map(function (item, relIndex) {
41
+ var _a;
42
+ return (_jsxs("li", { className: [
43
+ "dds-breadcrumb-item",
44
+ isCollapsible ? "dds-breadcrumb-item--middle" : "",
45
+ ]
46
+ .filter(Boolean)
47
+ .join(" "), children: [item.href ? (_jsx("a", { href: item.href, className: "dds-breadcrumb-link", children: item.label })) : (_jsx("span", { className: "dds-breadcrumb-link", children: item.label })), _jsx("span", { className: "dds-breadcrumb-delimiter", "aria-hidden": "true", children: delimiter })] }, "".concat((_a = item.href) !== null && _a !== void 0 ? _a : '', "-").concat(item.label, "-").concat(relIndex)));
48
+ }), items.length > 1 && (_jsx("li", { className: "dds-breadcrumb-item", children: lastItem.href ? (_jsx("a", { href: lastItem.href, className: "dds-breadcrumb-link dds-breadcrumb-current", "aria-current": "page", children: lastItem.label })) : (_jsx("span", { className: "dds-breadcrumb-current", "aria-current": "page", children: lastItem.label })) }))] }) })));
49
+ }
@@ -0,0 +1,40 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Breadcrumb } from "./Breadcrumb";
3
+ declare const meta: Meta<typeof Breadcrumb>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Breadcrumb>;
6
+ /**
7
+ * The default breadcrumb with a three-segment path and the "/" delimiter.
8
+ */
9
+ export declare const Default: Story;
10
+ /**
11
+ * A single-item breadcrumb — just the current page with no parent links.
12
+ */
13
+ export declare const SingleItem: Story;
14
+ /**
15
+ * Custom string delimiter using "›" instead of "/".
16
+ */
17
+ export declare const CustomStringDelimiter: Story;
18
+ /**
19
+ * Custom SVG chevron as the delimiter — demonstrates ReactNode delimiter support.
20
+ */
21
+ export declare const SvgChevronDelimiter: Story;
22
+ /**
23
+ * Small size variant — useful in compact page headers.
24
+ */
25
+ export declare const SmallSize: Story;
26
+ /**
27
+ * `collapseOnMobile` — middle segments collapse to an ellipsis on narrow viewports.
28
+ * Resize the browser window or use a mobile emulator to see this in action.
29
+ * Click the "…" to expand all items.
30
+ */
31
+ export declare const CollapseOnMobile: Story;
32
+ /**
33
+ * `scrollOnMobile` — the breadcrumb trail scrolls horizontally on narrow viewports
34
+ * rather than wrapping onto multiple lines.
35
+ */
36
+ export declare const ScrollOnMobile: Story;
37
+ /**
38
+ * All size and delimiter variants displayed together for visual comparison.
39
+ */
40
+ export declare const AllVariants: Story;
@@ -0,0 +1,216 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Breadcrumb } from "./Breadcrumb";
3
+ var meta = {
4
+ title: "Components/Breadcrumb",
5
+ component: Breadcrumb,
6
+ tags: ["autodocs"],
7
+ argTypes: {
8
+ items: {
9
+ description: "Ordered array of path segments (root first). Each item requires a `label`; `href` is optional and omitted for the current page.",
10
+ },
11
+ delimiter: {
12
+ control: "text",
13
+ description: "Separator between segments. Accepts a string or any ReactNode (e.g. an SVG icon). Rendered `aria-hidden`.",
14
+ table: { defaultValue: { summary: '"/"' } },
15
+ },
16
+ size: {
17
+ control: { type: "select" },
18
+ options: ["sm", "md"],
19
+ description: "Font-size variant.",
20
+ table: { defaultValue: { summary: '"md"' } },
21
+ },
22
+ collapseOnMobile: {
23
+ control: "boolean",
24
+ description: "Collapse middle segments behind an ellipsis on narrow viewports. Click the ellipsis to expand.",
25
+ table: { defaultValue: { summary: "false" } },
26
+ },
27
+ scrollOnMobile: {
28
+ control: "boolean",
29
+ description: "Let the full breadcrumb scroll horizontally on narrow viewports instead of wrapping. Can be combined with `collapseOnMobile`.",
30
+ table: { defaultValue: { summary: "false" } },
31
+ },
32
+ className: {
33
+ control: "text",
34
+ description: "Additional CSS classes.",
35
+ table: { defaultValue: { summary: '""' } },
36
+ },
37
+ },
38
+ parameters: {
39
+ docs: {
40
+ description: {
41
+ component: "\nThe Breadcrumb component renders a hierarchical navigation trail for documentation pages.\n\n## When to Use\n\n- To show the user's current location within a multi-level documentation hierarchy\n- To provide quick navigation back to parent sections\n- On pages nested 2 or more levels deep\n\n## When Not to Use\n\n- On top-level landing pages where hierarchy is obvious\n- For purely sequential step indicators (use a stepper/progress component instead)\n\n## Accessibility\n\n- Renders as `<nav aria-label=\"Breadcrumb\">` with an `<ol>` list so screen readers announce it correctly\n- The current (last) page item receives `aria-current=\"page\"`\n- Delimiter characters are marked `aria-hidden` and are not read aloud\n- The ellipsis expand button has an accessible `aria-label`\n ",
42
+ },
43
+ },
44
+ },
45
+ };
46
+ export default meta;
47
+ var docsItems = [
48
+ { label: "Docs", href: "/docs" },
49
+ { label: "Components", href: "/docs/components" },
50
+ { label: "Breadcrumb" },
51
+ ];
52
+ /**
53
+ * The default breadcrumb with a three-segment path and the "/" delimiter.
54
+ */
55
+ export var Default = {
56
+ args: {
57
+ items: docsItems,
58
+ },
59
+ parameters: {
60
+ docs: {
61
+ source: {
62
+ code: "<Breadcrumb\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n/>",
63
+ },
64
+ },
65
+ },
66
+ };
67
+ /**
68
+ * A single-item breadcrumb — just the current page with no parent links.
69
+ */
70
+ export var SingleItem = {
71
+ args: {
72
+ items: [{ label: "Getting Started" }],
73
+ },
74
+ parameters: {
75
+ docs: {
76
+ source: {
77
+ code: "<Breadcrumb items={[{ label: 'Getting Started' }]} />",
78
+ },
79
+ },
80
+ },
81
+ };
82
+ /**
83
+ * Custom string delimiter using "›" instead of "/".
84
+ */
85
+ export var CustomStringDelimiter = {
86
+ args: {
87
+ items: docsItems,
88
+ delimiter: "›",
89
+ },
90
+ parameters: {
91
+ docs: {
92
+ source: {
93
+ code: "<Breadcrumb\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n delimiter=\"\u203A\"\n/>",
94
+ },
95
+ },
96
+ },
97
+ };
98
+ /**
99
+ * Custom SVG chevron as the delimiter — demonstrates ReactNode delimiter support.
100
+ */
101
+ export var SvgChevronDelimiter = {
102
+ args: {
103
+ items: docsItems,
104
+ delimiter: (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", style: { flexShrink: 0 }, children: _jsx("polyline", { points: "9 18 15 12 9 6" }) })),
105
+ },
106
+ parameters: {
107
+ docs: {
108
+ source: {
109
+ code: "const ChevronRight = (\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\"\n strokeLinejoin=\"round\" aria-hidden=\"true\">\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n\n<Breadcrumb\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n delimiter={ChevronRight}\n/>",
110
+ },
111
+ },
112
+ },
113
+ };
114
+ /**
115
+ * Small size variant — useful in compact page headers.
116
+ */
117
+ export var SmallSize = {
118
+ args: {
119
+ items: docsItems,
120
+ size: "sm",
121
+ },
122
+ parameters: {
123
+ docs: {
124
+ source: {
125
+ code: "<Breadcrumb\n size=\"sm\"\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n/>",
126
+ },
127
+ },
128
+ },
129
+ };
130
+ /**
131
+ * `collapseOnMobile` — middle segments collapse to an ellipsis on narrow viewports.
132
+ * Resize the browser window or use a mobile emulator to see this in action.
133
+ * Click the "…" to expand all items.
134
+ */
135
+ export var CollapseOnMobile = {
136
+ args: {
137
+ collapseOnMobile: true,
138
+ items: [
139
+ { label: "Docs", href: "/docs" },
140
+ { label: "Guides", href: "/docs/guides" },
141
+ { label: "Components", href: "/docs/guides/components" },
142
+ { label: "Navigation", href: "/docs/guides/components/navigation" },
143
+ { label: "Breadcrumb" },
144
+ ],
145
+ },
146
+ parameters: {
147
+ docs: {
148
+ source: {
149
+ code: "<Breadcrumb\n collapseOnMobile\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Guides', href: '/docs/guides' },\n { label: 'Components', href: '/docs/guides/components' },\n { label: 'Navigation', href: '/docs/guides/components/navigation' },\n { label: 'Breadcrumb' },\n ]}\n/>",
150
+ },
151
+ },
152
+ },
153
+ };
154
+ /**
155
+ * `scrollOnMobile` — the breadcrumb trail scrolls horizontally on narrow viewports
156
+ * rather than wrapping onto multiple lines.
157
+ */
158
+ export var ScrollOnMobile = {
159
+ args: {
160
+ scrollOnMobile: true,
161
+ items: [
162
+ { label: "Docs", href: "/docs" },
163
+ { label: "Guides", href: "/docs/guides" },
164
+ { label: "Components", href: "/docs/guides/components" },
165
+ { label: "Navigation", href: "/docs/guides/components/navigation" },
166
+ { label: "Breadcrumb" },
167
+ ],
168
+ },
169
+ parameters: {
170
+ docs: {
171
+ source: {
172
+ code: "<Breadcrumb\n scrollOnMobile\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Guides', href: '/docs/guides' },\n { label: 'Components', href: '/docs/guides/components' },\n { label: 'Navigation', href: '/docs/guides/components/navigation' },\n { label: 'Breadcrumb' },\n ]}\n/>",
173
+ },
174
+ },
175
+ },
176
+ };
177
+ /**
178
+ * All size and delimiter variants displayed together for visual comparison.
179
+ */
180
+ export var AllVariants = {
181
+ parameters: {
182
+ docs: {
183
+ source: {
184
+ code: "<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>\n <Breadcrumb\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n />\n <Breadcrumb\n size=\"sm\"\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n />\n <Breadcrumb\n delimiter=\"\u203A\"\n items={[\n { label: 'Docs', href: '/docs' },\n { label: 'Components', href: '/docs/components' },\n { label: 'Breadcrumb' },\n ]}\n />\n</div>",
185
+ },
186
+ },
187
+ },
188
+ render: function () { return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" }, children: [_jsxs("div", { children: [_jsx("p", { style: {
189
+ color: "var(--dds-breadcrumb-current-color)",
190
+ fontFamily: "sans-serif",
191
+ fontSize: "0.75rem",
192
+ marginBottom: "0.25rem",
193
+ }, children: "Default (md)" }), _jsx(Breadcrumb, { items: [
194
+ { label: "Docs", href: "/docs" },
195
+ { label: "Components", href: "/docs/components" },
196
+ { label: "Breadcrumb" },
197
+ ] })] }), _jsxs("div", { children: [_jsx("p", { style: {
198
+ color: "var(--dds-breadcrumb-current-color)",
199
+ fontFamily: "sans-serif",
200
+ fontSize: "0.75rem",
201
+ marginBottom: "0.25rem",
202
+ }, children: "Small (sm)" }), _jsx(Breadcrumb, { size: "sm", items: [
203
+ { label: "Docs", href: "/docs" },
204
+ { label: "Components", href: "/docs/components" },
205
+ { label: "Breadcrumb" },
206
+ ] })] }), _jsxs("div", { children: [_jsx("p", { style: {
207
+ color: "var(--dds-breadcrumb-current-color)",
208
+ fontFamily: "sans-serif",
209
+ fontSize: "0.75rem",
210
+ marginBottom: "0.25rem",
211
+ }, children: "Custom delimiter \"\u203A\"" }), _jsx(Breadcrumb, { delimiter: "\u203A", items: [
212
+ { label: "Docs", href: "/docs" },
213
+ { label: "Components", href: "/docs/components" },
214
+ { label: "Breadcrumb" },
215
+ ] })] })] })); },
216
+ };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
2
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
3
  /**
4
4
  * Visual style variant of the button
5
5
  * @default 'primary'
@@ -14,4 +14,3 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
14
14
  children: React.ReactNode;
15
15
  }
16
16
  export declare const Button: React.FC<ButtonProps>;
17
- export {};
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  export type CalloutVariant = "caution" | "important" | "tip" | "course";
3
- interface CalloutProps {
3
+ export interface CalloutProps {
4
4
  /** Visual style variant of the callout (caution, important, tip, or course) */
5
5
  variant: CalloutVariant;
6
6
  /**
@@ -14,4 +14,3 @@ interface CalloutProps {
14
14
  className?: string;
15
15
  }
16
16
  export declare function Callout({ variant, title, children, className, }: CalloutProps): import("react/jsx-runtime").JSX.Element;
17
- export {};
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from "react";
2
- interface CardProps {
2
+ export interface CardProps {
3
3
  /** Optional title displayed at the top of the card */
4
4
  title?: string;
5
5
  /**
@@ -20,4 +20,3 @@ interface CardProps {
20
20
  className?: string;
21
21
  }
22
22
  export declare function Card({ title, titleColor, backgroundColor, href, children, className, }: CardProps): import("react/jsx-runtime").JSX.Element;
23
- export {};
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- interface CardGridProps {
2
+ export interface CardGridProps {
3
3
  /**
4
4
  * Number of columns in the grid
5
5
  * @default 3
@@ -17,4 +17,3 @@ interface CardGridProps {
17
17
  className?: string;
18
18
  }
19
19
  export declare function CardGrid({ columns, equalHeight, children, className }: CardGridProps): import("react/jsx-runtime").JSX.Element;
20
- export {};
@@ -25,7 +25,7 @@ export interface CodeSnippet {
25
25
  /** Optional line numbers to highlight (1-indexed) */
26
26
  highlightLines?: number[];
27
27
  }
28
- interface CodeBlockProps {
28
+ export interface CodeBlockProps {
29
29
  /** Single code snippet (for simple usage) */
30
30
  code?: string;
31
31
  /** Language for syntax highlighting */
@@ -55,4 +55,3 @@ interface CodeBlockProps {
55
55
  */
56
56
  export declare function registerLanguages(loader: () => Promise<void>): Promise<void>;
57
57
  export declare function CodeBlock({ code, language, filename, highlightLines, snippets, path, className, }: CodeBlockProps): import("react/jsx-runtime").JSX.Element | null;
58
- export {};
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  export interface CollapserGroupProps {
3
+ /** Collapser components to render inside the group */
3
4
  children: React.ReactNode;
4
5
  /** Spacing between collapsers (CSS gap value) */
5
6
  spacing?: string;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- interface HeadingProps {
2
+ export interface HeadingProps {
3
3
  /** Optionally override auto-generation of `id` attribute */
4
4
  id?: string;
5
5
  /** Heading level (h1, h2, h3, or h4) */
@@ -10,4 +10,3 @@ interface HeadingProps {
10
10
  className?: string;
11
11
  }
12
12
  export declare function Heading({ level, children, id, className }: HeadingProps): import("react/jsx-runtime").JSX.Element;
13
- export {};
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ export interface IconProps {
3
+ /**
4
+ * SVG to render. Accepts a React SVG component (e.g. imported with SVGR or
5
+ * defined inline) or a raw SVG string.
6
+ *
7
+ * **Accessibility note:** when passing a React SVG component, do not include
8
+ * `aria-hidden`, `role`, or `aria-label` attributes in the component itself.
9
+ * The `Icon` component applies the correct accessibility attributes based on
10
+ * whether an `aria-label` prop is provided.
11
+ *
12
+ * **Security note:** when passing a raw SVG string, ensure the content is
13
+ * from a trusted source. The string is injected via `dangerouslySetInnerHTML`
14
+ * without sanitization.
15
+ */
16
+ svg: React.ComponentType<React.SVGProps<SVGSVGElement>> | string;
17
+ /**
18
+ * Width and height in pixels.
19
+ * @default 16
20
+ */
21
+ size?: number;
22
+ /** Additional CSS class names to apply to the icon element. */
23
+ className?: string;
24
+ /**
25
+ * Accessible label for the icon. When provided the icon is announced by
26
+ * screen readers with `role="img"`. When omitted the icon is treated as
27
+ * decorative and hidden from assistive technology (`aria-hidden="true"`).
28
+ */
29
+ 'aria-label'?: string;
30
+ }
31
+ /**
32
+ * Renders an SVG icon. Accepts either a React SVG component (e.g. imported
33
+ * with SVGR) or a raw SVG string, making it easy to use your own icon library
34
+ * without being tied to a specific icon set.
35
+ *
36
+ * @example
37
+ * // React SVG component
38
+ * import { ReactComponent as ChevronIcon } from './chevron.svg';
39
+ * <Icon svg={ChevronIcon} size={20} aria-label="Expand" />
40
+ *
41
+ * @example
42
+ * // Inline SVG function
43
+ * const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (
44
+ * <svg viewBox="0 0 24 24" {...props}>...</svg>
45
+ * );
46
+ * <Icon svg={StarIcon} size={16} />
47
+ *
48
+ * @example
49
+ * // Raw SVG string
50
+ * <Icon svg='<svg viewBox="0 0 24 24">...</svg>' size={24} />
51
+ */
52
+ export declare function Icon({ svg, size, className, 'aria-label': ariaLabel, }: IconProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,49 @@
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 } from "react/jsx-runtime";
13
+ /**
14
+ * Renders an SVG icon. Accepts either a React SVG component (e.g. imported
15
+ * with SVGR) or a raw SVG string, making it easy to use your own icon library
16
+ * without being tied to a specific icon set.
17
+ *
18
+ * @example
19
+ * // React SVG component
20
+ * import { ReactComponent as ChevronIcon } from './chevron.svg';
21
+ * <Icon svg={ChevronIcon} size={20} aria-label="Expand" />
22
+ *
23
+ * @example
24
+ * // Inline SVG function
25
+ * const StarIcon = (props: React.SVGProps<SVGSVGElement>) => (
26
+ * <svg viewBox="0 0 24 24" {...props}>...</svg>
27
+ * );
28
+ * <Icon svg={StarIcon} size={16} />
29
+ *
30
+ * @example
31
+ * // Raw SVG string
32
+ * <Icon svg='<svg viewBox="0 0 24 24">...</svg>' size={24} />
33
+ */
34
+ export function Icon(_a) {
35
+ var svg = _a.svg, _b = _a.size, size = _b === void 0 ? 16 : _b, _c = _a.className, className = _c === void 0 ? '' : _c, ariaLabel = _a["aria-label"];
36
+ var isDecorative = !ariaLabel;
37
+ var classNames = ['dds-icon', className].filter(Boolean).join(' ');
38
+ if (typeof svg === 'string') {
39
+ return (_jsx("span", __assign({ className: classNames, style: { width: size, height: size },
40
+ // eslint-disable-next-line react/no-danger
41
+ dangerouslySetInnerHTML: { __html: svg } }, (isDecorative
42
+ ? { 'aria-hidden': true }
43
+ : { role: 'img', 'aria-label': ariaLabel }))));
44
+ }
45
+ var SvgComponent = svg;
46
+ return (_jsx(SvgComponent, __assign({ className: classNames, width: size, height: size }, (isDecorative
47
+ ? { 'aria-hidden': true }
48
+ : { role: 'img', 'aria-label': ariaLabel }))));
49
+ }
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Icon } from './Icon';
3
+ declare const meta: Meta<typeof Icon>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Icon>;
6
+ /**
7
+ * Pass any React SVG component to the `svg` prop. The component is rendered
8
+ * directly with the provided `size` applied as `width` and `height`.
9
+ */
10
+ export declare const WithReactComponent: Story;
11
+ /**
12
+ * Pass a raw SVG string to the `svg` prop. The string is injected using
13
+ * `dangerouslySetInnerHTML` — ensure the content is from a trusted source.
14
+ */
15
+ export declare const WithSvgString: Story;
16
+ /**
17
+ * When an `aria-label` is provided the icon receives `role="img"` and is
18
+ * announced by screen readers with the given label. Use this when the icon
19
+ * conveys meaning without surrounding text.
20
+ */
21
+ export declare const WithAriaLabel: Story;
22
+ /**
23
+ * Sizes from small (12 px) to large (48 px). The `size` prop sets both
24
+ * `width` and `height` so the icon is always square.
25
+ */
26
+ export declare const Sizes: Story;
27
+ /**
28
+ * Icons inherit `currentColor` for their stroke/fill, so they automatically
29
+ * adapt to the surrounding text color.
30
+ */
31
+ export declare const InheritedColor: Story;