@particle-academy/react-fancy 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [6.3.0] - 2026-11-01
15
+
16
+ ### Added
17
+
18
+ - **`Stepper`: ordered steps with states (requested by Prism).** An `<ol>` of
19
+ steps, each with a title, optional description and rich content, in one of
20
+ `upcoming` / `current` / `complete` / `error`. Data-driven through a JSON-
21
+ friendly `steps` array, or compound with `Stepper.Step` (read as data, so both
22
+ forms render identically). Controlled (`current` + `onChange`) or
23
+ uncontrolled (`defaultCurrent`); a step's own `state` overrides the derived
24
+ one, which is how `error` is shown. **Pure content by default: no button,
25
+ input or form element at all.** `clickable` opts in to step buttons, with
26
+ arrow-key / Home / End focus movement. `aria-current="step"` on the current
27
+ step; each state is in visually hidden text and in the indicator's glyph,
28
+ never colour alone; `stateLabels` translates the words. Vertical by default,
29
+ `orientation="horizontal"` for short content; `showContent="current"` shows
30
+ only the current step's body. Server-renders without DOM globals. Handles:
31
+ `data-react-fancy-stepper`, `data-react-fancy-stepper-step` with `data-state`,
32
+ `data-index` and `data-step-id`. Subpath: `@particle-academy/react-fancy/stepper`.
33
+
34
+ ### Changed
35
+
36
+ - **`BreadcrumbsItemProps` now accepts the element's HTML attributes.**
37
+ `Breadcrumbs.Item` always forwarded `title`, `id`, `aria-*`, `style` and
38
+ handlers to the element it renders, but the type listed its keys by hand and
39
+ rejected them, so a consumer titling a truncated crumb had to cast. It now
40
+ extends `HTMLAttributes<HTMLElement>` (the attributes valid on every element a
41
+ crumb can be: `<a>`, the `as` component, `<button>`, `<span>`), with
42
+ `onClick` kept at its existing `() => void` signature. The collapsed (below
43
+ `md`) dropdown now carries a crumb's `title` onto its menu entry; other
44
+ attributes stay on the trail, because both trails are in the DOM at once and
45
+ an `id` or `data-*` handle would be duplicated. **No action needed:** this
46
+ only adds accepted props, and every existing usage type-checks unchanged.
47
+
14
48
  ## [6.2.0] - 2026-11-01
15
49
 
16
50
  ### Added
package/README.md CHANGED
@@ -130,6 +130,7 @@ npx vite build # Build demo app (verifies imports work)
130
130
  | Card | Container with Header, Body, Footer compound slots | [docs](docs/Card.md) |
131
131
  | Callout | Alert/info box with icon, color, and dismissible support | [docs](docs/Callout.md) |
132
132
  | Timeline | Stacked, alternating, and horizontal timeline with data-driven or compound API | [docs](docs/Timeline.md) |
133
+ | Stepper | Ordered steps with upcoming / current / complete / error states; pure content by default, opt-in clickable steps | [docs](docs/Stepper.md) |
133
134
  | Marquee | Auto-scrolling ticker strip with seamless wrap, px/s speed, fade edges, reduced-motion gate | [docs](docs/Marquee.md) |
134
135
 
135
136
  ### Overlay & Floating
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkEWE776WI_cjs = require('./chunk-EWE776WI.cjs');
3
+ var chunkSKMORSBO_cjs = require('./chunk-SKMORSBO.cjs');
4
4
  require('./chunk-57FQGQJK.cjs');
5
5
  require('./chunk-OUYC6EIL.cjs');
6
6
  require('./chunk-ZU5Q77OT.cjs');
@@ -9,7 +9,7 @@ require('./chunk-ZU5Q77OT.cjs');
9
9
 
10
10
  Object.defineProperty(exports, "Breadcrumbs", {
11
11
  enumerable: true,
12
- get: function () { return chunkEWE776WI_cjs.Breadcrumbs; }
12
+ get: function () { return chunkSKMORSBO_cjs.Breadcrumbs; }
13
13
  });
14
14
  //# sourceMappingURL=breadcrumbs.cjs.map
15
15
  //# sourceMappingURL=breadcrumbs.cjs.map
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ReactNode, HTMLAttributes } from 'react';
3
3
  import { N as NavigableProps } from './types-DJI492iY.cjs';
4
4
 
5
5
  interface BreadcrumbsProps {
@@ -8,7 +8,18 @@ interface BreadcrumbsProps {
8
8
  shrink?: boolean;
9
9
  className?: string;
10
10
  }
11
- interface BreadcrumbsItemProps extends NavigableProps {
11
+ /**
12
+ * A crumb accepts the HTML attributes common to every element it can render as
13
+ * (`<a>`, the `as` component, `<button>` or `<span>`), so `title`, `id`,
14
+ * `aria-*`, `style` and handlers are typed as well as forwarded. Typed against
15
+ * `HTMLElement` rather than one element because the crumb's element depends on
16
+ * its props; anchor-only attributes such as `target` are not offered, since the
17
+ * same crumb renders as a `<span>` once it is `active`.
18
+ *
19
+ * `onClick` is omitted from the element's attributes and redeclared below with
20
+ * its existing `() => void` signature.
21
+ */
22
+ interface BreadcrumbsItemProps extends NavigableProps, Omit<HTMLAttributes<HTMLElement>, "onClick" | "children"> {
12
23
  children: ReactNode;
13
24
  active?: boolean;
14
25
  /**
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
2
+ import { ReactNode, HTMLAttributes } from 'react';
3
3
  import { N as NavigableProps } from './types-DJI492iY.js';
4
4
 
5
5
  interface BreadcrumbsProps {
@@ -8,7 +8,18 @@ interface BreadcrumbsProps {
8
8
  shrink?: boolean;
9
9
  className?: string;
10
10
  }
11
- interface BreadcrumbsItemProps extends NavigableProps {
11
+ /**
12
+ * A crumb accepts the HTML attributes common to every element it can render as
13
+ * (`<a>`, the `as` component, `<button>` or `<span>`), so `title`, `id`,
14
+ * `aria-*`, `style` and handlers are typed as well as forwarded. Typed against
15
+ * `HTMLElement` rather than one element because the crumb's element depends on
16
+ * its props; anchor-only attributes such as `target` are not offered, since the
17
+ * same crumb renders as a `<span>` once it is `active`.
18
+ *
19
+ * `onClick` is omitted from the element's attributes and redeclared below with
20
+ * its existing `() => void` signature.
21
+ */
22
+ interface BreadcrumbsItemProps extends NavigableProps, Omit<HTMLAttributes<HTMLElement>, "onClick" | "children"> {
12
23
  children: ReactNode;
13
24
  active?: boolean;
14
25
  /**
@@ -1,4 +1,4 @@
1
- export { Breadcrumbs } from './chunk-WJHPZS3U.js';
1
+ export { Breadcrumbs } from './chunk-GTS3QA7M.js';
2
2
  import './chunk-RGANGU7X.js';
3
3
  import './chunk-YX6FTJ6N.js';
4
4
  import './chunk-MMW2JGKZ.js';
@@ -0,0 +1,194 @@
1
+ import { useControllableState } from './chunk-EDYAXGUM.js';
2
+ import { cn } from './chunk-MMW2JGKZ.js';
3
+ import { forwardRef, Children, isValidElement } from 'react';
4
+ import { Check } from 'lucide-react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ var DEFAULT_STATE_LABELS = {
8
+ upcoming: "Not started",
9
+ current: "Current",
10
+ complete: "Completed",
11
+ error: "Error"
12
+ };
13
+ function StepperStep(_props) {
14
+ return null;
15
+ }
16
+ StepperStep.displayName = "StepperStep";
17
+ function stepsFromChildren(children) {
18
+ const out = [];
19
+ for (const child of Children.toArray(children)) {
20
+ if (!isValidElement(child) || child.type !== StepperStep) continue;
21
+ const { children: content, ...rest } = child.props;
22
+ out.push({ ...rest, content });
23
+ }
24
+ return out;
25
+ }
26
+ var indicatorClass = {
27
+ complete: "border-transparent bg-zinc-900 text-white dark:bg-white dark:text-zinc-900",
28
+ current: "border-2 border-zinc-900 bg-[color:var(--fancy-surface,var(--color-white))] text-zinc-900 ring-4 ring-zinc-900/10 dark:border-white dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:text-white dark:ring-white/15",
29
+ upcoming: "border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]",
30
+ error: "border-transparent bg-red-600 text-white dark:bg-red-500"
31
+ };
32
+ var titleClass = {
33
+ complete: "font-medium text-zinc-900 dark:text-white",
34
+ current: "font-semibold text-zinc-900 dark:text-white",
35
+ upcoming: "font-medium text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]",
36
+ error: "font-semibold text-red-700 dark:text-red-400"
37
+ };
38
+ function Indicator({ state, index }) {
39
+ return /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ "aria-hidden": "true",
43
+ "data-react-fancy-stepper-indicator": "",
44
+ className: cn(
45
+ "relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border text-sm font-semibold",
46
+ indicatorClass[state]
47
+ ),
48
+ children: state === "complete" ? /* @__PURE__ */ jsx(Check, { size: 16, strokeWidth: 3 }) : state === "error" ? "!" : index + 1
49
+ }
50
+ );
51
+ }
52
+ var StepperRoot = forwardRef(
53
+ ({
54
+ steps: stepsProp,
55
+ children,
56
+ current: currentProp,
57
+ defaultCurrent = 0,
58
+ onChange,
59
+ orientation = "vertical",
60
+ clickable = false,
61
+ showContent = "all",
62
+ stateLabels,
63
+ className,
64
+ onKeyDown,
65
+ ...rest
66
+ }, ref) => {
67
+ const [current, setCurrent] = useControllableState(currentProp, defaultCurrent, onChange);
68
+ const steps = stepsProp ?? stepsFromChildren(children);
69
+ const labels = { ...DEFAULT_STATE_LABELS, ...stateLabels };
70
+ const horizontal = orientation === "horizontal";
71
+ const handleKeyDown = (e) => {
72
+ onKeyDown?.(e);
73
+ if (!clickable || e.defaultPrevented) return;
74
+ const prevKey = horizontal ? "ArrowLeft" : "ArrowUp";
75
+ const nextKey = horizontal ? "ArrowRight" : "ArrowDown";
76
+ if (![prevKey, nextKey, "Home", "End"].includes(e.key)) return;
77
+ const list = e.currentTarget;
78
+ const target = e.target.closest("[data-react-fancy-stepper-trigger]");
79
+ if (!target || target.closest("[data-react-fancy-stepper]") !== list) return;
80
+ const buttons = Array.from(
81
+ list.querySelectorAll("[data-react-fancy-stepper-trigger]")
82
+ ).filter((b) => b.closest("[data-react-fancy-stepper]") === list && !b.disabled);
83
+ const at = buttons.indexOf(target);
84
+ let next;
85
+ if (e.key === "Home") next = buttons[0];
86
+ else if (e.key === "End") next = buttons[buttons.length - 1];
87
+ else if (e.key === nextKey) next = buttons[at + 1] ?? target;
88
+ else next = at > 0 ? buttons[at - 1] : target;
89
+ e.preventDefault();
90
+ next?.focus();
91
+ };
92
+ return /* @__PURE__ */ jsx(
93
+ "ol",
94
+ {
95
+ ref,
96
+ "data-react-fancy-stepper": "",
97
+ "data-orientation": orientation,
98
+ "data-current": current,
99
+ className: cn(horizontal ? "flex items-start" : "flex flex-col", className),
100
+ onKeyDown: handleKeyDown,
101
+ ...rest,
102
+ children: steps.map((step, index) => {
103
+ const derived = index < current ? "complete" : index === current ? "current" : "upcoming";
104
+ const state = step.state ?? derived;
105
+ const isCurrent = index === current;
106
+ const isLast = index === steps.length - 1;
107
+ const showBody = step.content != null && (showContent === "all" || isCurrent);
108
+ const headerInner = /* @__PURE__ */ jsxs(Fragment, { children: [
109
+ /* @__PURE__ */ jsx(Indicator, { state, index }),
110
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", "data-react-fancy-stepper-state": "", children: [
111
+ labels[state],
112
+ ":",
113
+ " "
114
+ ] }),
115
+ /* @__PURE__ */ jsx("span", { "data-react-fancy-stepper-title": "", className: cn("text-sm", titleClass[state]), children: step.title })
116
+ ] });
117
+ const headerClass = horizontal ? "flex flex-col items-center gap-2 text-center" : "flex items-center gap-x-4 text-left";
118
+ const header = clickable ? /* @__PURE__ */ jsx(
119
+ "button",
120
+ {
121
+ type: "button",
122
+ "data-react-fancy-stepper-trigger": "",
123
+ disabled: step.disabled,
124
+ onClick: () => {
125
+ if (index !== current) setCurrent(index);
126
+ },
127
+ className: cn(
128
+ headerClass,
129
+ "cursor-pointer rounded-md border-0 bg-transparent p-0",
130
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
131
+ "focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))] focus-visible:ring-offset-[color:var(--fancy-surface,var(--color-white))]",
132
+ "dark:focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))] dark:focus-visible:ring-offset-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
133
+ "disabled:cursor-not-allowed disabled:opacity-50"
134
+ ),
135
+ children: headerInner
136
+ }
137
+ ) : /* @__PURE__ */ jsx("div", { className: headerClass, children: headerInner });
138
+ return /* @__PURE__ */ jsxs(
139
+ "li",
140
+ {
141
+ "data-react-fancy-stepper-step": "",
142
+ "data-state": state,
143
+ "data-index": index,
144
+ "data-step-id": step.id,
145
+ "data-disabled": step.disabled ? "true" : void 0,
146
+ "aria-current": isCurrent ? "step" : void 0,
147
+ className: cn("relative", horizontal ? "min-w-0 flex-1" : !isLast && "pb-8"),
148
+ children: [
149
+ !isLast && /* @__PURE__ */ jsx(
150
+ "span",
151
+ {
152
+ "aria-hidden": "true",
153
+ "data-react-fancy-stepper-connector": "",
154
+ className: cn(
155
+ "absolute",
156
+ horizontal ? "top-4 left-[calc(50%+1.5rem)] h-px w-[calc(100%-3rem)]" : "top-10 bottom-2 left-4 w-px -translate-x-1/2",
157
+ index < current ? "bg-zinc-900 dark:bg-white" : "bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]"
158
+ )
159
+ }
160
+ ),
161
+ header,
162
+ (step.description != null || showBody) && /* @__PURE__ */ jsxs("div", { className: horizontal ? "mt-1 px-2 text-center" : "pl-12", children: [
163
+ step.description != null && /* @__PURE__ */ jsx(
164
+ "div",
165
+ {
166
+ "data-react-fancy-stepper-description": "",
167
+ className: "text-sm text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]",
168
+ children: step.description
169
+ }
170
+ ),
171
+ showBody && /* @__PURE__ */ jsx(
172
+ "div",
173
+ {
174
+ "data-react-fancy-stepper-content": "",
175
+ className: "mt-2 text-sm text-[color:var(--fancy-text-subtle,var(--color-zinc-600))] dark:text-[color:var(--fancy-text-subtle-dark,var(--color-zinc-400))]",
176
+ children: step.content
177
+ }
178
+ )
179
+ ] })
180
+ ]
181
+ },
182
+ step.id ?? index
183
+ );
184
+ })
185
+ }
186
+ );
187
+ }
188
+ );
189
+ StepperRoot.displayName = "Stepper";
190
+ var Stepper = Object.assign(StepperRoot, { Step: StepperStep });
191
+
192
+ export { Stepper };
193
+ //# sourceMappingURL=chunk-5EW47IH7.js.map
194
+ //# sourceMappingURL=chunk-5EW47IH7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Stepper/Stepper.tsx"],"names":[],"mappings":"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","file":"chunk-5EW47IH7.js","sourcesContent":["import {\n Children,\n forwardRef,\n isValidElement,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useControllableState } from \"../../hooks/use-controllable-state\";\nimport type {\n StepperProps,\n StepperStep as StepperStepData,\n StepperStepProps,\n StepperStepState,\n} from \"./Stepper.types\";\n\nconst DEFAULT_STATE_LABELS: Record<StepperStepState, string> = {\n upcoming: \"Not started\",\n current: \"Current\",\n complete: \"Completed\",\n error: \"Error\",\n};\n\n/**\n * Declarative step. Renders nothing itself: `Stepper` reads its props as data\n * and renders it through the same path as an entry in `steps`, so the two forms\n * cannot drift apart in markup, states or handles. Its children are the step's\n * content. It must be a DIRECT child of `Stepper` (not wrapped in another\n * component) for the same reason `Breadcrumbs.Item` must.\n */\nfunction StepperStep(_props: StepperStepProps): null {\n return null;\n}\nStepperStep.displayName = \"StepperStep\";\n\nfunction stepsFromChildren(children: ReactNode): StepperStepData[] {\n const out: StepperStepData[] = [];\n for (const child of Children.toArray(children)) {\n if (!isValidElement<StepperStepProps>(child) || child.type !== StepperStep) continue;\n const { children: content, ...rest } = child.props;\n out.push({ ...rest, content });\n }\n return out;\n}\n\nconst indicatorClass: Record<StepperStepState, string> = {\n complete:\n \"border-transparent bg-zinc-900 text-white dark:bg-white dark:text-zinc-900\",\n current:\n \"border-2 border-zinc-900 bg-[color:var(--fancy-surface,var(--color-white))] text-zinc-900 ring-4 ring-zinc-900/10 dark:border-white dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:text-white dark:ring-white/15\",\n upcoming:\n \"border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]\",\n error: \"border-transparent bg-red-600 text-white dark:bg-red-500\",\n};\n\nconst titleClass: Record<StepperStepState, string> = {\n complete: \"font-medium text-zinc-900 dark:text-white\",\n current: \"font-semibold text-zinc-900 dark:text-white\",\n upcoming:\n \"font-medium text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]\",\n error: \"font-semibold text-red-700 dark:text-red-400\",\n};\n\n/** The glyph differs per state, so state reads without colour: number, tick, \"!\". */\nfunction Indicator({ state, index }: { state: StepperStepState; index: number }) {\n return (\n <span\n aria-hidden=\"true\"\n data-react-fancy-stepper-indicator=\"\"\n className={cn(\n \"relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full border text-sm font-semibold\",\n indicatorClass[state],\n )}\n >\n {state === \"complete\" ? <Check size={16} strokeWidth={3} /> : state === \"error\" ? \"!\" : index + 1}\n </span>\n );\n}\n\nconst StepperRoot = forwardRef<HTMLOListElement, StepperProps>(\n (\n {\n steps: stepsProp,\n children,\n current: currentProp,\n defaultCurrent = 0,\n onChange,\n orientation = \"vertical\",\n clickable = false,\n showContent = \"all\",\n stateLabels,\n className,\n onKeyDown,\n ...rest\n },\n ref,\n ) => {\n const [current, setCurrent] = useControllableState(currentProp, defaultCurrent, onChange);\n const steps = stepsProp ?? stepsFromChildren(children);\n const labels = { ...DEFAULT_STATE_LABELS, ...stateLabels };\n const horizontal = orientation === \"horizontal\";\n\n // Arrow keys move focus between step buttons; Enter / Space activate them\n // natively. Focus only -- the current step changes on activation, never on\n // arrowing past it. DOM is read in the handler, never during render.\n const handleKeyDown = (e: KeyboardEvent<HTMLOListElement>) => {\n onKeyDown?.(e);\n if (!clickable || e.defaultPrevented) return;\n\n const prevKey = horizontal ? \"ArrowLeft\" : \"ArrowUp\";\n const nextKey = horizontal ? \"ArrowRight\" : \"ArrowDown\";\n if (![prevKey, nextKey, \"Home\", \"End\"].includes(e.key)) return;\n\n const list = e.currentTarget;\n const target = (e.target as HTMLElement).closest<HTMLButtonElement>(\"[data-react-fancy-stepper-trigger]\");\n if (!target || target.closest(\"[data-react-fancy-stepper]\") !== list) return;\n\n // Only this stepper's own buttons: a stepper nested in step content is\n // its own list.\n const buttons = Array.from(\n list.querySelectorAll<HTMLButtonElement>(\"[data-react-fancy-stepper-trigger]\"),\n ).filter((b) => b.closest(\"[data-react-fancy-stepper]\") === list && !b.disabled);\n const at = buttons.indexOf(target);\n\n let next: HTMLButtonElement | undefined;\n if (e.key === \"Home\") next = buttons[0];\n else if (e.key === \"End\") next = buttons[buttons.length - 1];\n else if (e.key === nextKey) next = buttons[at + 1] ?? target;\n else next = at > 0 ? buttons[at - 1] : target;\n\n e.preventDefault();\n next?.focus();\n };\n\n return (\n <ol\n ref={ref}\n data-react-fancy-stepper=\"\"\n data-orientation={orientation}\n data-current={current}\n className={cn(horizontal ? \"flex items-start\" : \"flex flex-col\", className)}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n {steps.map((step, index) => {\n const derived: StepperStepState =\n index < current ? \"complete\" : index === current ? \"current\" : \"upcoming\";\n const state = step.state ?? derived;\n const isCurrent = index === current;\n const isLast = index === steps.length - 1;\n const showBody = step.content != null && (showContent === \"all\" || isCurrent);\n\n const headerInner = (\n <>\n <Indicator state={state} index={index} />\n {/* State in words, before the title so it is read first. A\n sibling of the title, so the title handle reads the title. */}\n <span className=\"sr-only\" data-react-fancy-stepper-state=\"\">\n {labels[state]}:{\" \"}\n </span>\n <span data-react-fancy-stepper-title=\"\" className={cn(\"text-sm\", titleClass[state])}>\n {step.title}\n </span>\n </>\n );\n\n const headerClass = horizontal\n ? \"flex flex-col items-center gap-2 text-center\"\n : \"flex items-center gap-x-4 text-left\";\n\n const header = clickable ? (\n <button\n type=\"button\"\n data-react-fancy-stepper-trigger=\"\"\n disabled={step.disabled}\n onClick={() => {\n if (index !== current) setCurrent(index);\n }}\n className={cn(\n headerClass,\n \"cursor-pointer rounded-md border-0 bg-transparent p-0\",\n \"focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2\",\n \"focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))] focus-visible:ring-offset-[color:var(--fancy-surface,var(--color-white))]\",\n \"dark:focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))] dark:focus-visible:ring-offset-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n )}\n >\n {headerInner}\n </button>\n ) : (\n <div className={headerClass}>{headerInner}</div>\n );\n\n return (\n <li\n key={step.id ?? index}\n data-react-fancy-stepper-step=\"\"\n data-state={state}\n data-index={index}\n data-step-id={step.id}\n data-disabled={step.disabled ? \"true\" : undefined}\n aria-current={isCurrent ? \"step\" : undefined}\n className={cn(\"relative\", horizontal ? \"min-w-0 flex-1\" : !isLast && \"pb-8\")}\n >\n {!isLast && (\n <span\n aria-hidden=\"true\"\n data-react-fancy-stepper-connector=\"\"\n className={cn(\n \"absolute\",\n horizontal\n ? \"top-4 left-[calc(50%+1.5rem)] h-px w-[calc(100%-3rem)]\"\n : \"top-10 bottom-2 left-4 w-px -translate-x-1/2\",\n index < current\n ? \"bg-zinc-900 dark:bg-white\"\n : \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n )}\n />\n )}\n {header}\n {(step.description != null || showBody) && (\n <div className={horizontal ? \"mt-1 px-2 text-center\" : \"pl-12\"}>\n {step.description != null && (\n <div\n data-react-fancy-stepper-description=\"\"\n className=\"text-sm text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]\"\n >\n {step.description}\n </div>\n )}\n {showBody && (\n <div\n data-react-fancy-stepper-content=\"\"\n className=\"mt-2 text-sm text-[color:var(--fancy-text-subtle,var(--color-zinc-600))] dark:text-[color:var(--fancy-text-subtle-dark,var(--color-zinc-400))]\"\n >\n {step.content}\n </div>\n )}\n </div>\n )}\n </li>\n );\n })}\n </ol>\n );\n },\n);\n\nStepperRoot.displayName = \"Stepper\";\n\nexport const Stepper = Object.assign(StepperRoot, { Step: StepperStep });\n"]}
@@ -78,6 +78,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
78
78
  Link,
79
79
  {
80
80
  ...item.href ? { href: item.href } : {},
81
+ title: item.title,
81
82
  className: cls,
82
83
  onClick: () => {
83
84
  item.onClick?.();
@@ -94,6 +95,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
94
95
  "button",
95
96
  {
96
97
  type: "button",
98
+ title: item.title,
97
99
  className: cn(cls, "cursor-pointer border-0 bg-transparent"),
98
100
  onClick: () => {
99
101
  onClick();
@@ -104,7 +106,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
104
106
  i
105
107
  );
106
108
  }
107
- return /* @__PURE__ */ jsx("span", { className: cls, children: item.label }, i);
109
+ return /* @__PURE__ */ jsx("span", { title: item.title, className: cls, children: item.label }, i);
108
110
  }) })
109
111
  ] });
110
112
  }
@@ -126,7 +128,12 @@ function BreadcrumbsRoot({
126
128
  href: el.props?.href,
127
129
  as: el.props?.as,
128
130
  onClick: el.props?.onClick,
129
- active: el.props?.active
131
+ active: el.props?.active,
132
+ // Only `title` crosses into the menu. It is the full text of a crumb the
133
+ // consumer truncated, and the narrow menu is where labels get cut. Other
134
+ // attributes stay on the trail: both trails are in the DOM at once (CSS
135
+ // hides one), so an `id` or a `data-*` handle would be duplicated.
136
+ title: el.props?.title
130
137
  };
131
138
  });
132
139
  const activeLabel = dropdownItems[dropdownItems.length - 1]?.label ?? "Menu";
@@ -166,5 +173,5 @@ var Breadcrumbs = Object.assign(BreadcrumbsRoot, {
166
173
  });
167
174
 
168
175
  export { Breadcrumbs };
169
- //# sourceMappingURL=chunk-WJHPZS3U.js.map
170
- //# sourceMappingURL=chunk-WJHPZS3U.js.map
176
+ //# sourceMappingURL=chunk-GTS3QA7M.js.map
177
+ //# sourceMappingURL=chunk-GTS3QA7M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Breadcrumbs/BreadcrumbsItem.tsx","../src/components/Breadcrumbs/BreadcrumbsDropdown.tsx","../src/components/Breadcrumbs/Breadcrumbs.tsx"],"names":["jsx","useState","jsxs","Fragment"],"mappings":"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","file":"chunk-GTS3QA7M.js","sourcesContent":["import type { ElementType } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { BreadcrumbsItemProps } from \"./Breadcrumbs.types\";\n\nexport function BreadcrumbsItem({\n children,\n href,\n as: As,\n active = false,\n onClick,\n className,\n ...rest\n}: BreadcrumbsItemProps) {\n const baseClass = cn(\n \"text-sm\",\n active\n ? \"font-medium text-zinc-900 dark:text-white\"\n : \"text-[color:var(--fancy-text-muted,var(--color-zinc-500))] hover:text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))] dark:hover:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\",\n className,\n );\n\n // The CURRENT crumb stays a <span> even with `as`: it is the page you are on,\n // so a link to it is a link to nowhere, and `aria-current=\"page\"` is the point.\n if ((href || As) && !active) {\n const Link = (As ?? \"a\") as ElementType;\n\n return (\n <Link\n data-react-fancy-breadcrumbs-item=\"\"\n {...(href ? { href } : {})}\n onClick={onClick}\n className={baseClass}\n {...rest}\n >\n {children}\n </Link>\n );\n }\n\n // A crumb that navigates through a callback rather than a URL. Controlled\n // components own their navigation and have no href to give — a repository\n // browser walking directories, a wizard stepping back — and until this\n // existed the only way to make such a crumb clickable was to nest a button\n // inside the span below, which is interactive content inside a non-\n // interactive element.\n if (onClick && !active) {\n return (\n <button\n type=\"button\"\n data-react-fancy-breadcrumbs-item=\"\"\n onClick={onClick}\n className={cn(baseClass, \"cursor-pointer border-0 bg-transparent p-0\")}\n {...rest}\n >\n {children}\n </button>\n );\n }\n\n return (\n <span data-react-fancy-breadcrumbs-item=\"\" className={baseClass} aria-current={active ? \"page\" : undefined} {...rest}>\n {children}\n </span>\n );\n}\n\nBreadcrumbsItem.displayName = \"BreadcrumbsItem\";\n","import { useState, useRef, type ElementType, type ReactNode } from \"react\";\nimport { ChevronDown } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\n\ninterface BreadcrumbsDropdownItem {\n label: ReactNode;\n href?: string;\n as?: ElementType;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n}\n\ninterface BreadcrumbsDropdownProps {\n items: BreadcrumbsDropdownItem[];\n activeLabel: ReactNode;\n}\n\nexport function BreadcrumbsDropdown({ items, activeLabel }: BreadcrumbsDropdownProps) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useOutsideClick(ref, () => setOpen(false), open);\n useEscapeKey(() => setOpen(false), open);\n\n return (\n <div ref={ref} data-react-fancy-breadcrumbs-dropdown=\"\" className=\"relative flex md:hidden\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex items-center gap-1 rounded-lg px-2 py-1 text-sm font-medium text-zinc-900 hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-white dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\"\n >\n {activeLabel}\n <ChevronDown size={14} className={cn(\"transition-transform\", open && \"rotate-180\")} />\n </button>\n {open && (\n <div className=\"absolute top-full left-0 z-50 mt-1 min-w-[180px] rounded-lg border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] py-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\">\n {items.map((item, i) => {\n const cls = cn(\n \"block w-full px-3 py-1.5 text-left text-sm\",\n item.active\n ? \"font-medium text-zinc-900 dark:text-white\"\n : \"text-[color:var(--fancy-text-muted,var(--color-zinc-500))] hover:bg-zinc-50 hover:text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))] dark:hover:bg-zinc-800 dark:hover:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\",\n );\n\n // Mirrors BreadcrumbsItem: href / `as` wins, then onClick, and the\n // active crumb stays inert whatever it carries.\n if ((item.href || item.as) && !item.active) {\n const Link = (item.as ?? \"a\") as ElementType;\n return (\n <Link\n key={i}\n {...(item.href ? { href: item.href } : {})}\n title={item.title}\n className={cls}\n onClick={() => {\n item.onClick?.();\n setOpen(false);\n }}\n >\n {item.label}\n </Link>\n );\n }\n\n if (item.onClick && !item.active) {\n const onClick = item.onClick;\n return (\n <button\n key={i}\n type=\"button\"\n title={item.title}\n className={cn(cls, \"cursor-pointer border-0 bg-transparent\")}\n onClick={() => {\n onClick();\n setOpen(false);\n }}\n >\n {item.label}\n </button>\n );\n }\n\n return (\n <span key={i} title={item.title} className={cls}>\n {item.label}\n </span>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nBreadcrumbsDropdown.displayName = \"BreadcrumbsDropdown\";\n","import { Children, Fragment, useState, type ReactElement } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { BreadcrumbsItem } from \"./BreadcrumbsItem\";\nimport { BreadcrumbsDropdown } from \"./BreadcrumbsDropdown\";\nimport type { BreadcrumbsProps, BreadcrumbsItemProps } from \"./Breadcrumbs.types\";\n\nfunction BreadcrumbsRoot({\n children,\n separator,\n shrink,\n className,\n}: BreadcrumbsProps) {\n const items = Children.toArray(children);\n const [expanded, setExpanded] = useState(false);\n\n const sep = separator ?? (\n <ChevronRight size={12} className=\"text-zinc-400\" />\n );\n\n const lastItem = items[items.length - 1];\n\n // Extract info from children for mobile dropdown\n const dropdownItems = items.map((child) => {\n const el = child as ReactElement<BreadcrumbsItemProps>;\n return {\n label: el.props?.children,\n href: el.props?.href,\n as: el.props?.as,\n onClick: el.props?.onClick,\n active: el.props?.active,\n // Only `title` crosses into the menu. It is the full text of a crumb the\n // consumer truncated, and the narrow menu is where labels get cut. Other\n // attributes stay on the trail: both trails are in the DOM at once (CSS\n // hides one), so an `id` or a `data-*` handle would be duplicated.\n title: el.props?.title,\n };\n });\n\n const activeLabel = dropdownItems[dropdownItems.length - 1]?.label ?? \"Menu\";\n\n const showShrunk = shrink && !expanded && items.length > 1;\n\n return (\n <>\n {/* Desktop trail */}\n <nav\n data-react-fancy-breadcrumbs=\"\"\n aria-label=\"Breadcrumb\"\n className={cn(\"hidden items-center gap-2 md:flex\", className)}\n onMouseLeave={() => shrink && setExpanded(false)}\n >\n {showShrunk ? (\n <>\n <button\n type=\"button\"\n onClick={() => setExpanded(true)}\n onMouseEnter={() => setExpanded(true)}\n className=\"text-sm text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300\"\n >\n ...\n </button>\n <span aria-hidden=\"true\">{sep}</span>\n {lastItem}\n </>\n ) : (\n items.map((child, i) => (\n <Fragment key={i}>\n {i > 0 && <span aria-hidden=\"true\">{sep}</span>}\n {child}\n </Fragment>\n ))\n )}\n </nav>\n\n {/* Mobile dropdown */}\n <BreadcrumbsDropdown items={dropdownItems} activeLabel={activeLabel} />\n </>\n );\n}\n\nexport const Breadcrumbs = Object.assign(BreadcrumbsRoot, {\n Item: BreadcrumbsItem,\n});\n"]}
@@ -80,6 +80,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
80
80
  Link,
81
81
  {
82
82
  ...item.href ? { href: item.href } : {},
83
+ title: item.title,
83
84
  className: cls,
84
85
  onClick: () => {
85
86
  item.onClick?.();
@@ -96,6 +97,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
96
97
  "button",
97
98
  {
98
99
  type: "button",
100
+ title: item.title,
99
101
  className: chunkZU5Q77OT_cjs.cn(cls, "cursor-pointer border-0 bg-transparent"),
100
102
  onClick: () => {
101
103
  onClick();
@@ -106,7 +108,7 @@ function BreadcrumbsDropdown({ items, activeLabel }) {
106
108
  i
107
109
  );
108
110
  }
109
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: cls, children: item.label }, i);
111
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { title: item.title, className: cls, children: item.label }, i);
110
112
  }) })
111
113
  ] });
112
114
  }
@@ -128,7 +130,12 @@ function BreadcrumbsRoot({
128
130
  href: el.props?.href,
129
131
  as: el.props?.as,
130
132
  onClick: el.props?.onClick,
131
- active: el.props?.active
133
+ active: el.props?.active,
134
+ // Only `title` crosses into the menu. It is the full text of a crumb the
135
+ // consumer truncated, and the narrow menu is where labels get cut. Other
136
+ // attributes stay on the trail: both trails are in the DOM at once (CSS
137
+ // hides one), so an `id` or a `data-*` handle would be duplicated.
138
+ title: el.props?.title
132
139
  };
133
140
  });
134
141
  const activeLabel = dropdownItems[dropdownItems.length - 1]?.label ?? "Menu";
@@ -168,5 +175,5 @@ var Breadcrumbs = Object.assign(BreadcrumbsRoot, {
168
175
  });
169
176
 
170
177
  exports.Breadcrumbs = Breadcrumbs;
171
- //# sourceMappingURL=chunk-EWE776WI.cjs.map
172
- //# sourceMappingURL=chunk-EWE776WI.cjs.map
178
+ //# sourceMappingURL=chunk-SKMORSBO.cjs.map
179
+ //# sourceMappingURL=chunk-SKMORSBO.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Breadcrumbs/BreadcrumbsItem.tsx","../src/components/Breadcrumbs/BreadcrumbsDropdown.tsx","../src/components/Breadcrumbs/Breadcrumbs.tsx"],"names":["cn","jsx","useState","useRef","useOutsideClick","useEscapeKey","jsxs","ChevronDown","Children","ChevronRight","Fragment"],"mappings":"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","file":"chunk-SKMORSBO.cjs","sourcesContent":["import type { ElementType } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { BreadcrumbsItemProps } from \"./Breadcrumbs.types\";\n\nexport function BreadcrumbsItem({\n children,\n href,\n as: As,\n active = false,\n onClick,\n className,\n ...rest\n}: BreadcrumbsItemProps) {\n const baseClass = cn(\n \"text-sm\",\n active\n ? \"font-medium text-zinc-900 dark:text-white\"\n : \"text-[color:var(--fancy-text-muted,var(--color-zinc-500))] hover:text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))] dark:hover:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\",\n className,\n );\n\n // The CURRENT crumb stays a <span> even with `as`: it is the page you are on,\n // so a link to it is a link to nowhere, and `aria-current=\"page\"` is the point.\n if ((href || As) && !active) {\n const Link = (As ?? \"a\") as ElementType;\n\n return (\n <Link\n data-react-fancy-breadcrumbs-item=\"\"\n {...(href ? { href } : {})}\n onClick={onClick}\n className={baseClass}\n {...rest}\n >\n {children}\n </Link>\n );\n }\n\n // A crumb that navigates through a callback rather than a URL. Controlled\n // components own their navigation and have no href to give — a repository\n // browser walking directories, a wizard stepping back — and until this\n // existed the only way to make such a crumb clickable was to nest a button\n // inside the span below, which is interactive content inside a non-\n // interactive element.\n if (onClick && !active) {\n return (\n <button\n type=\"button\"\n data-react-fancy-breadcrumbs-item=\"\"\n onClick={onClick}\n className={cn(baseClass, \"cursor-pointer border-0 bg-transparent p-0\")}\n {...rest}\n >\n {children}\n </button>\n );\n }\n\n return (\n <span data-react-fancy-breadcrumbs-item=\"\" className={baseClass} aria-current={active ? \"page\" : undefined} {...rest}>\n {children}\n </span>\n );\n}\n\nBreadcrumbsItem.displayName = \"BreadcrumbsItem\";\n","import { useState, useRef, type ElementType, type ReactNode } from \"react\";\nimport { ChevronDown } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useOutsideClick } from \"../../hooks/use-outside-click\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\n\ninterface BreadcrumbsDropdownItem {\n label: ReactNode;\n href?: string;\n as?: ElementType;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n}\n\ninterface BreadcrumbsDropdownProps {\n items: BreadcrumbsDropdownItem[];\n activeLabel: ReactNode;\n}\n\nexport function BreadcrumbsDropdown({ items, activeLabel }: BreadcrumbsDropdownProps) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useOutsideClick(ref, () => setOpen(false), open);\n useEscapeKey(() => setOpen(false), open);\n\n return (\n <div ref={ref} data-react-fancy-breadcrumbs-dropdown=\"\" className=\"relative flex md:hidden\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className=\"flex items-center gap-1 rounded-lg px-2 py-1 text-sm font-medium text-zinc-900 hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-white dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\"\n >\n {activeLabel}\n <ChevronDown size={14} className={cn(\"transition-transform\", open && \"rotate-180\")} />\n </button>\n {open && (\n <div className=\"absolute top-full left-0 z-50 mt-1 min-w-[180px] rounded-lg border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] py-1 shadow-lg dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\">\n {items.map((item, i) => {\n const cls = cn(\n \"block w-full px-3 py-1.5 text-left text-sm\",\n item.active\n ? \"font-medium text-zinc-900 dark:text-white\"\n : \"text-[color:var(--fancy-text-muted,var(--color-zinc-500))] hover:bg-zinc-50 hover:text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))] dark:hover:bg-zinc-800 dark:hover:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\",\n );\n\n // Mirrors BreadcrumbsItem: href / `as` wins, then onClick, and the\n // active crumb stays inert whatever it carries.\n if ((item.href || item.as) && !item.active) {\n const Link = (item.as ?? \"a\") as ElementType;\n return (\n <Link\n key={i}\n {...(item.href ? { href: item.href } : {})}\n title={item.title}\n className={cls}\n onClick={() => {\n item.onClick?.();\n setOpen(false);\n }}\n >\n {item.label}\n </Link>\n );\n }\n\n if (item.onClick && !item.active) {\n const onClick = item.onClick;\n return (\n <button\n key={i}\n type=\"button\"\n title={item.title}\n className={cn(cls, \"cursor-pointer border-0 bg-transparent\")}\n onClick={() => {\n onClick();\n setOpen(false);\n }}\n >\n {item.label}\n </button>\n );\n }\n\n return (\n <span key={i} title={item.title} className={cls}>\n {item.label}\n </span>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\nBreadcrumbsDropdown.displayName = \"BreadcrumbsDropdown\";\n","import { Children, Fragment, useState, type ReactElement } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { BreadcrumbsItem } from \"./BreadcrumbsItem\";\nimport { BreadcrumbsDropdown } from \"./BreadcrumbsDropdown\";\nimport type { BreadcrumbsProps, BreadcrumbsItemProps } from \"./Breadcrumbs.types\";\n\nfunction BreadcrumbsRoot({\n children,\n separator,\n shrink,\n className,\n}: BreadcrumbsProps) {\n const items = Children.toArray(children);\n const [expanded, setExpanded] = useState(false);\n\n const sep = separator ?? (\n <ChevronRight size={12} className=\"text-zinc-400\" />\n );\n\n const lastItem = items[items.length - 1];\n\n // Extract info from children for mobile dropdown\n const dropdownItems = items.map((child) => {\n const el = child as ReactElement<BreadcrumbsItemProps>;\n return {\n label: el.props?.children,\n href: el.props?.href,\n as: el.props?.as,\n onClick: el.props?.onClick,\n active: el.props?.active,\n // Only `title` crosses into the menu. It is the full text of a crumb the\n // consumer truncated, and the narrow menu is where labels get cut. Other\n // attributes stay on the trail: both trails are in the DOM at once (CSS\n // hides one), so an `id` or a `data-*` handle would be duplicated.\n title: el.props?.title,\n };\n });\n\n const activeLabel = dropdownItems[dropdownItems.length - 1]?.label ?? \"Menu\";\n\n const showShrunk = shrink && !expanded && items.length > 1;\n\n return (\n <>\n {/* Desktop trail */}\n <nav\n data-react-fancy-breadcrumbs=\"\"\n aria-label=\"Breadcrumb\"\n className={cn(\"hidden items-center gap-2 md:flex\", className)}\n onMouseLeave={() => shrink && setExpanded(false)}\n >\n {showShrunk ? (\n <>\n <button\n type=\"button\"\n onClick={() => setExpanded(true)}\n onMouseEnter={() => setExpanded(true)}\n className=\"text-sm text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300\"\n >\n ...\n </button>\n <span aria-hidden=\"true\">{sep}</span>\n {lastItem}\n </>\n ) : (\n items.map((child, i) => (\n <Fragment key={i}>\n {i > 0 && <span aria-hidden=\"true\">{sep}</span>}\n {child}\n </Fragment>\n ))\n )}\n </nav>\n\n {/* Mobile dropdown */}\n <BreadcrumbsDropdown items={dropdownItems} activeLabel={activeLabel} />\n </>\n );\n}\n\nexport const Breadcrumbs = Object.assign(BreadcrumbsRoot, {\n Item: BreadcrumbsItem,\n});\n"]}