@mieweb/ui 0.11.0-dev.20 → 0.11.0-dev.21

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.
@@ -28,6 +28,18 @@ var itemVariants = cva("overflow-hidden", {
28
28
  variant: "separated"
29
29
  }
30
30
  });
31
+ var FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
32
+ function isLibraryIcon(icon) {
33
+ return typeof icon === "object" && icon !== null && icon.$$typeof === FORWARD_REF_TYPE && typeof icon.render === "function";
34
+ }
35
+ var didWarnInvalidIcon = false;
36
+ function warnInvalidIconOnce(id) {
37
+ if (process.env.NODE_ENV === "production" || didWarnInvalidIcon) return;
38
+ didWarnInvalidIcon = true;
39
+ console.warn(
40
+ `[@mieweb/ui] Accordion item "${id}": \`icon\` must be a library icon component (e.g. \`icon: HelpCircleIcon\`), not an element or other value. The icon was ignored.`
41
+ );
42
+ }
31
43
  var Accordion = React.forwardRef(
32
44
  function Accordion2({
33
45
  items,
@@ -94,6 +106,9 @@ var Accordion = React.forwardRef(
94
106
  const isOpen = openSet.has(item.id);
95
107
  const triggerId = `${baseId}-${item.id}-trigger`;
96
108
  const panelId = `${baseId}-${item.id}-panel`;
109
+ const hasIcon = item.icon !== void 0;
110
+ const LeadingIcon = hasIcon && isLibraryIcon(item.icon) ? item.icon : null;
111
+ if (hasIcon && !LeadingIcon) warnInvalidIconOnce(item.id);
97
112
  return /* @__PURE__ */ jsxs("div", { className: cn(itemVariants({ variant })), children: [
98
113
  /* @__PURE__ */ jsx(Heading, { className: "m-0", children: /* @__PURE__ */ jsxs(
99
114
  "button",
@@ -111,7 +126,15 @@ var Accordion = React.forwardRef(
111
126
  "disabled:pointer-events-none disabled:opacity-50"
112
127
  ),
113
128
  children: [
114
- /* @__PURE__ */ jsx("span", { className: "text-foreground min-w-0 text-sm font-semibold", children: item.title }),
129
+ LeadingIcon ? /* @__PURE__ */ jsx(
130
+ LeadingIcon,
131
+ {
132
+ "aria-hidden": "true",
133
+ "data-slot": "accordion-icon",
134
+ className: "text-muted-foreground/70 -me-2 h-4 w-4 shrink-0"
135
+ }
136
+ ) : null,
137
+ /* @__PURE__ */ jsx("span", { className: "text-foreground min-w-0 flex-1 text-sm font-semibold", children: item.title }),
115
138
  /* @__PURE__ */ jsx(
116
139
  ChevronDown,
117
140
  {
@@ -155,5 +178,5 @@ var Accordion = React.forwardRef(
155
178
  );
156
179
 
157
180
  export { Accordion };
158
- //# sourceMappingURL=chunk-JUJGWUAN.js.map
159
- //# sourceMappingURL=chunk-JUJGWUAN.js.map
181
+ //# sourceMappingURL=chunk-BHKGVFTY.js.map
182
+ //# sourceMappingURL=chunk-BHKGVFTY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Accordion/Accordion.tsx"],"names":["Accordion"],"mappings":"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","file":"chunk-BHKGVFTY.js","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ChevronDown, type LucideIcon } from 'lucide-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst accordionVariants = cva('', {\n variants: {\n variant: {\n // Each item is its own card with spacing between\n separated: 'flex flex-col gap-3',\n // One bordered list with dividers\n joined: 'border-border divide-border divide-y rounded-lg border',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\nconst itemVariants = cva('overflow-hidden', {\n variants: {\n variant: {\n separated: 'border-border bg-card rounded-lg border',\n joined: 'bg-card first:rounded-t-lg last:rounded-b-lg',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AccordionItem {\n /** Stable id used for open state and aria wiring. */\n id: string;\n /** Header label (e.g. an FAQ question). */\n title: React.ReactNode;\n /** Panel content (e.g. the answer). */\n content: React.ReactNode;\n /**\n * Optional leading icon shown before the title. Pass a library icon\n * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:\n * `icon: HelpCircleIcon`. Unsupported values are ignored.\n */\n icon?: LucideIcon;\n disabled?: boolean;\n}\n\nexport interface AccordionProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>,\n VariantProps<typeof accordionVariants> {\n items: AccordionItem[];\n /** `single` keeps at most one panel open; `multiple` allows any number. */\n type?: 'single' | 'multiple';\n /** Ids open initially (uncontrolled). */\n defaultOpenIds?: string[];\n /** Controlled open ids. Changes reported via `onOpenChange`. */\n openIds?: string[];\n onOpenChange?: (openIds: string[]) => void;\n /** In `single` mode, allow closing the open panel (default true). */\n collapsible?: boolean;\n /** Heading level wrapping each trigger for document outline (default h3). */\n headingLevel?: 'h2' | 'h3' | 'h4';\n /**\n * Persists the open ids in `localStorage` under this key (uncontrolled\n * only). Restored after mount, so server and hydration renders use\n * `defaultOpenIds`.\n */\n storageKey?: string;\n}\n\n// =============================================================================\n// Accordion\n// =============================================================================\n\n// Leading icons are restricted to library icon components. Every\n// lucide-react icon (and therefore the `@mieweb/ui` Icons barrel) is a\n// `forwardRef` exotic component, so the guard requires exactly that shape:\n// `$$typeof` of react.forward_ref with a callable `render`. Plain function\n// components, elements (`<Icon />`), strings, memo wrappers, and malformed\n// tagged objects are all rejected, so the slot cannot be abused as a\n// free-form content area and invalid values cannot crash the render.\n// A true identity allowlist would require bundling the entire icon set\n// (defeating lucide's tree-shaking) — the `LucideIcon` type remains the\n// compile-time library-membership contract, and the unit test rendering a\n// real library icon guards this shape against lucide upgrades.\nconst FORWARD_REF_TYPE = Symbol.for('react.forward_ref');\n\nfunction isLibraryIcon(icon: unknown): icon is LucideIcon {\n return (\n typeof icon === 'object' &&\n icon !== null &&\n (icon as { $$typeof?: unknown }).$$typeof === FORWARD_REF_TYPE &&\n typeof (icon as { render?: unknown }).render === 'function'\n );\n}\n\n// Warn once per session (dev builds only) about rejected icon values.\n// Bundlers replace `process.env.NODE_ENV` at build time, so this branch is\n// dead-code-eliminated from production bundles.\nlet didWarnInvalidIcon = false;\nfunction warnInvalidIconOnce(id: string) {\n if (process.env.NODE_ENV === 'production' || didWarnInvalidIcon) return;\n didWarnInvalidIcon = true;\n console.warn(\n `[@mieweb/ui] Accordion item \"${id}\": \\`icon\\` must be a library icon component (e.g. \\`icon: HelpCircleIcon\\`), not an element or other value. The icon was ignored.`\n );\n}\n\n/**\n * A vertically stacked set of expandable panels — FAQ lists, settings\n * groups, progressive disclosure. Panels animate open to their natural\n * height (CSS grid rows, no max-height clipping), and headers are real\n * buttons inside headings with full `aria-expanded`/`aria-controls` wiring.\n *\n * @example\n * ```tsx\n * <Accordion\n * type=\"single\"\n * items={[\n * { id: 'pricing', title: 'How is pricing calculated?', content: <p>…</p> },\n * { id: 'billing', title: 'When am I billed?', content: <p>…</p> },\n * ]}\n * />\n * ```\n */\nexport const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n function Accordion(\n {\n items,\n type = 'single',\n defaultOpenIds,\n openIds: controlledOpen,\n onOpenChange,\n collapsible = true,\n headingLevel: Heading = 'h3',\n storageKey,\n variant,\n className,\n ...props\n },\n ref\n ) {\n const baseId = React.useId();\n const [internalOpen, setInternalOpen] = React.useState<string[]>(\n () => defaultOpenIds ?? []\n );\n const persists = Boolean(storageKey) && controlledOpen === undefined;\n\n React.useEffect(() => {\n if (!persists || !storageKey) return;\n try {\n const stored: unknown = JSON.parse(\n window.localStorage.getItem(storageKey) ?? 'null'\n );\n if (Array.isArray(stored)) {\n setInternalOpen(\n stored.filter((id): id is string => typeof id === 'string')\n );\n }\n } catch {\n // Storage blocked or value corrupt: keep defaultOpenIds.\n }\n }, [persists, storageKey]);\n const rawOpen = controlledOpen ?? internalOpen;\n // Single mode keeps at most one panel open, even if defaultOpenIds or a\n // controlled openIds array hands us several. Stored ids for removed items\n // are dropped first so they can't crowd out a valid one.\n const open = React.useMemo(() => {\n const known = persists\n ? rawOpen.filter((id) => items.some((item) => item.id === id))\n : rawOpen;\n return type === 'single' ? known.slice(0, 1) : known;\n }, [type, rawOpen, persists, items]);\n const openSet = React.useMemo(() => new Set(open), [open]);\n\n const toggle = (id: string) => {\n let next: string[];\n if (openSet.has(id)) {\n if (type === 'single' && !collapsible) return;\n next = open.filter((o) => o !== id);\n } else {\n next = type === 'single' ? [id] : [...open, id];\n }\n if (controlledOpen === undefined) setInternalOpen(next);\n if (persists && storageKey) {\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(next));\n } catch {\n // Storage blocked or full; state still updates in memory.\n }\n }\n onOpenChange?.(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn(accordionVariants({ variant }), className)}\n {...props}\n >\n {items.map((item) => {\n const isOpen = openSet.has(item.id);\n const triggerId = `${baseId}-${item.id}-trigger`;\n const panelId = `${baseId}-${item.id}-panel`;\n const hasIcon = item.icon !== undefined;\n const LeadingIcon =\n hasIcon && isLibraryIcon(item.icon) ? item.icon : null;\n if (hasIcon && !LeadingIcon) warnInvalidIconOnce(item.id);\n return (\n <div key={item.id} className={cn(itemVariants({ variant }))}>\n <Heading className=\"m-0\">\n <button\n type=\"button\"\n id={triggerId}\n data-slot=\"accordion-trigger\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n disabled={item.disabled}\n onClick={() => toggle(item.id)}\n className={cn(\n 'hover:bg-muted/60 flex w-full items-center justify-between gap-4 px-5 py-4 text-start transition-colors',\n 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n {LeadingIcon ? (\n <LeadingIcon\n aria-hidden=\"true\"\n data-slot=\"accordion-icon\"\n className=\"text-muted-foreground/70 -me-2 h-4 w-4 shrink-0\"\n />\n ) : null}\n <span className=\"text-foreground min-w-0 flex-1 text-sm font-semibold\">\n {item.title}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n 'text-muted-foreground h-5 w-5 shrink-0 transition-transform duration-200',\n isOpen && 'rotate-180'\n )}\n />\n </button>\n </Heading>\n {/* grid-rows 0fr→1fr animates to natural height without a max-height clip;\n collapsed panels are hidden from AT and unfocusable via aria-hidden + inert */}\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n aria-hidden={!isOpen}\n inert={!isOpen || undefined}\n className={cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )}\n >\n <div className=\"overflow-hidden\">\n <div\n data-slot=\"accordion-content\"\n className=\"text-muted-foreground px-5 pb-4 text-sm leading-relaxed\"\n >\n {item.content}\n </div>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n);\n"]}
@@ -50,6 +50,18 @@ var itemVariants = classVarianceAuthority.cva("overflow-hidden", {
50
50
  variant: "separated"
51
51
  }
52
52
  });
53
+ var FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
54
+ function isLibraryIcon(icon) {
55
+ return typeof icon === "object" && icon !== null && icon.$$typeof === FORWARD_REF_TYPE && typeof icon.render === "function";
56
+ }
57
+ var didWarnInvalidIcon = false;
58
+ function warnInvalidIconOnce(id) {
59
+ if (process.env.NODE_ENV === "production" || didWarnInvalidIcon) return;
60
+ didWarnInvalidIcon = true;
61
+ console.warn(
62
+ `[@mieweb/ui] Accordion item "${id}": \`icon\` must be a library icon component (e.g. \`icon: HelpCircleIcon\`), not an element or other value. The icon was ignored.`
63
+ );
64
+ }
53
65
  var Accordion = React__namespace.forwardRef(
54
66
  function Accordion2({
55
67
  items,
@@ -116,6 +128,9 @@ var Accordion = React__namespace.forwardRef(
116
128
  const isOpen = openSet.has(item.id);
117
129
  const triggerId = `${baseId}-${item.id}-trigger`;
118
130
  const panelId = `${baseId}-${item.id}-panel`;
131
+ const hasIcon = item.icon !== void 0;
132
+ const LeadingIcon = hasIcon && isLibraryIcon(item.icon) ? item.icon : null;
133
+ if (hasIcon && !LeadingIcon) warnInvalidIconOnce(item.id);
119
134
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkS5RVSXDV_cjs.cn(itemVariants({ variant })), children: [
120
135
  /* @__PURE__ */ jsxRuntime.jsx(Heading, { className: "m-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
121
136
  "button",
@@ -133,7 +148,15 @@ var Accordion = React__namespace.forwardRef(
133
148
  "disabled:pointer-events-none disabled:opacity-50"
134
149
  ),
135
150
  children: [
136
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground min-w-0 text-sm font-semibold", children: item.title }),
151
+ LeadingIcon ? /* @__PURE__ */ jsxRuntime.jsx(
152
+ LeadingIcon,
153
+ {
154
+ "aria-hidden": "true",
155
+ "data-slot": "accordion-icon",
156
+ className: "text-muted-foreground/70 -me-2 h-4 w-4 shrink-0"
157
+ }
158
+ ) : null,
159
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground min-w-0 flex-1 text-sm font-semibold", children: item.title }),
137
160
  /* @__PURE__ */ jsxRuntime.jsx(
138
161
  lucideReact.ChevronDown,
139
162
  {
@@ -177,5 +200,5 @@ var Accordion = React__namespace.forwardRef(
177
200
  );
178
201
 
179
202
  exports.Accordion = Accordion;
180
- //# sourceMappingURL=chunk-SHBCUXFS.cjs.map
181
- //# sourceMappingURL=chunk-SHBCUXFS.cjs.map
203
+ //# sourceMappingURL=chunk-PRGQVUMU.cjs.map
204
+ //# sourceMappingURL=chunk-PRGQVUMU.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Accordion/Accordion.tsx"],"names":["cva","React","Accordion","jsx","cn","jsxs","ChevronDown"],"mappings":"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","file":"chunk-PRGQVUMU.cjs","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ChevronDown, type LucideIcon } from 'lucide-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst accordionVariants = cva('', {\n variants: {\n variant: {\n // Each item is its own card with spacing between\n separated: 'flex flex-col gap-3',\n // One bordered list with dividers\n joined: 'border-border divide-border divide-y rounded-lg border',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\nconst itemVariants = cva('overflow-hidden', {\n variants: {\n variant: {\n separated: 'border-border bg-card rounded-lg border',\n joined: 'bg-card first:rounded-t-lg last:rounded-b-lg',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AccordionItem {\n /** Stable id used for open state and aria wiring. */\n id: string;\n /** Header label (e.g. an FAQ question). */\n title: React.ReactNode;\n /** Panel content (e.g. the answer). */\n content: React.ReactNode;\n /**\n * Optional leading icon shown before the title. Pass a library icon\n * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:\n * `icon: HelpCircleIcon`. Unsupported values are ignored.\n */\n icon?: LucideIcon;\n disabled?: boolean;\n}\n\nexport interface AccordionProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>,\n VariantProps<typeof accordionVariants> {\n items: AccordionItem[];\n /** `single` keeps at most one panel open; `multiple` allows any number. */\n type?: 'single' | 'multiple';\n /** Ids open initially (uncontrolled). */\n defaultOpenIds?: string[];\n /** Controlled open ids. Changes reported via `onOpenChange`. */\n openIds?: string[];\n onOpenChange?: (openIds: string[]) => void;\n /** In `single` mode, allow closing the open panel (default true). */\n collapsible?: boolean;\n /** Heading level wrapping each trigger for document outline (default h3). */\n headingLevel?: 'h2' | 'h3' | 'h4';\n /**\n * Persists the open ids in `localStorage` under this key (uncontrolled\n * only). Restored after mount, so server and hydration renders use\n * `defaultOpenIds`.\n */\n storageKey?: string;\n}\n\n// =============================================================================\n// Accordion\n// =============================================================================\n\n// Leading icons are restricted to library icon components. Every\n// lucide-react icon (and therefore the `@mieweb/ui` Icons barrel) is a\n// `forwardRef` exotic component, so the guard requires exactly that shape:\n// `$$typeof` of react.forward_ref with a callable `render`. Plain function\n// components, elements (`<Icon />`), strings, memo wrappers, and malformed\n// tagged objects are all rejected, so the slot cannot be abused as a\n// free-form content area and invalid values cannot crash the render.\n// A true identity allowlist would require bundling the entire icon set\n// (defeating lucide's tree-shaking) — the `LucideIcon` type remains the\n// compile-time library-membership contract, and the unit test rendering a\n// real library icon guards this shape against lucide upgrades.\nconst FORWARD_REF_TYPE = Symbol.for('react.forward_ref');\n\nfunction isLibraryIcon(icon: unknown): icon is LucideIcon {\n return (\n typeof icon === 'object' &&\n icon !== null &&\n (icon as { $$typeof?: unknown }).$$typeof === FORWARD_REF_TYPE &&\n typeof (icon as { render?: unknown }).render === 'function'\n );\n}\n\n// Warn once per session (dev builds only) about rejected icon values.\n// Bundlers replace `process.env.NODE_ENV` at build time, so this branch is\n// dead-code-eliminated from production bundles.\nlet didWarnInvalidIcon = false;\nfunction warnInvalidIconOnce(id: string) {\n if (process.env.NODE_ENV === 'production' || didWarnInvalidIcon) return;\n didWarnInvalidIcon = true;\n console.warn(\n `[@mieweb/ui] Accordion item \"${id}\": \\`icon\\` must be a library icon component (e.g. \\`icon: HelpCircleIcon\\`), not an element or other value. The icon was ignored.`\n );\n}\n\n/**\n * A vertically stacked set of expandable panels — FAQ lists, settings\n * groups, progressive disclosure. Panels animate open to their natural\n * height (CSS grid rows, no max-height clipping), and headers are real\n * buttons inside headings with full `aria-expanded`/`aria-controls` wiring.\n *\n * @example\n * ```tsx\n * <Accordion\n * type=\"single\"\n * items={[\n * { id: 'pricing', title: 'How is pricing calculated?', content: <p>…</p> },\n * { id: 'billing', title: 'When am I billed?', content: <p>…</p> },\n * ]}\n * />\n * ```\n */\nexport const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n function Accordion(\n {\n items,\n type = 'single',\n defaultOpenIds,\n openIds: controlledOpen,\n onOpenChange,\n collapsible = true,\n headingLevel: Heading = 'h3',\n storageKey,\n variant,\n className,\n ...props\n },\n ref\n ) {\n const baseId = React.useId();\n const [internalOpen, setInternalOpen] = React.useState<string[]>(\n () => defaultOpenIds ?? []\n );\n const persists = Boolean(storageKey) && controlledOpen === undefined;\n\n React.useEffect(() => {\n if (!persists || !storageKey) return;\n try {\n const stored: unknown = JSON.parse(\n window.localStorage.getItem(storageKey) ?? 'null'\n );\n if (Array.isArray(stored)) {\n setInternalOpen(\n stored.filter((id): id is string => typeof id === 'string')\n );\n }\n } catch {\n // Storage blocked or value corrupt: keep defaultOpenIds.\n }\n }, [persists, storageKey]);\n const rawOpen = controlledOpen ?? internalOpen;\n // Single mode keeps at most one panel open, even if defaultOpenIds or a\n // controlled openIds array hands us several. Stored ids for removed items\n // are dropped first so they can't crowd out a valid one.\n const open = React.useMemo(() => {\n const known = persists\n ? rawOpen.filter((id) => items.some((item) => item.id === id))\n : rawOpen;\n return type === 'single' ? known.slice(0, 1) : known;\n }, [type, rawOpen, persists, items]);\n const openSet = React.useMemo(() => new Set(open), [open]);\n\n const toggle = (id: string) => {\n let next: string[];\n if (openSet.has(id)) {\n if (type === 'single' && !collapsible) return;\n next = open.filter((o) => o !== id);\n } else {\n next = type === 'single' ? [id] : [...open, id];\n }\n if (controlledOpen === undefined) setInternalOpen(next);\n if (persists && storageKey) {\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(next));\n } catch {\n // Storage blocked or full; state still updates in memory.\n }\n }\n onOpenChange?.(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn(accordionVariants({ variant }), className)}\n {...props}\n >\n {items.map((item) => {\n const isOpen = openSet.has(item.id);\n const triggerId = `${baseId}-${item.id}-trigger`;\n const panelId = `${baseId}-${item.id}-panel`;\n const hasIcon = item.icon !== undefined;\n const LeadingIcon =\n hasIcon && isLibraryIcon(item.icon) ? item.icon : null;\n if (hasIcon && !LeadingIcon) warnInvalidIconOnce(item.id);\n return (\n <div key={item.id} className={cn(itemVariants({ variant }))}>\n <Heading className=\"m-0\">\n <button\n type=\"button\"\n id={triggerId}\n data-slot=\"accordion-trigger\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n disabled={item.disabled}\n onClick={() => toggle(item.id)}\n className={cn(\n 'hover:bg-muted/60 flex w-full items-center justify-between gap-4 px-5 py-4 text-start transition-colors',\n 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n {LeadingIcon ? (\n <LeadingIcon\n aria-hidden=\"true\"\n data-slot=\"accordion-icon\"\n className=\"text-muted-foreground/70 -me-2 h-4 w-4 shrink-0\"\n />\n ) : null}\n <span className=\"text-foreground min-w-0 flex-1 text-sm font-semibold\">\n {item.title}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n 'text-muted-foreground h-5 w-5 shrink-0 transition-transform duration-200',\n isOpen && 'rotate-180'\n )}\n />\n </button>\n </Heading>\n {/* grid-rows 0fr→1fr animates to natural height without a max-height clip;\n collapsed panels are hidden from AT and unfocusable via aria-hidden + inert */}\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n aria-hidden={!isOpen}\n inert={!isOpen || undefined}\n className={cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )}\n >\n <div className=\"overflow-hidden\">\n <div\n data-slot=\"accordion-content\"\n className=\"text-muted-foreground px-5 pb-4 text-sm leading-relaxed\"\n >\n {item.content}\n </div>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n);\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkSHBCUXFS_cjs = require('../../chunk-SHBCUXFS.cjs');
3
+ var chunkPRGQVUMU_cjs = require('../../chunk-PRGQVUMU.cjs');
4
4
  require('../../chunk-S5RVSXDV.cjs');
5
5
  require('../../chunk-EUAMEVVI.cjs');
6
6
 
@@ -8,7 +8,7 @@ require('../../chunk-EUAMEVVI.cjs');
8
8
 
9
9
  Object.defineProperty(exports, "Accordion", {
10
10
  enumerable: true,
11
- get: function () { return chunkSHBCUXFS_cjs.Accordion; }
11
+ get: function () { return chunkPRGQVUMU_cjs.Accordion; }
12
12
  });
13
13
  //# sourceMappingURL=index.cjs.map
14
14
  //# sourceMappingURL=index.cjs.map
@@ -1,5 +1,6 @@
1
1
  import * as class_variance_authority_types from 'class-variance-authority/types';
2
2
  import * as React from 'react';
3
+ import { LucideIcon } from 'lucide-react';
3
4
  import { VariantProps } from 'class-variance-authority';
4
5
 
5
6
  declare const accordionVariants: (props?: ({
@@ -12,6 +13,12 @@ interface AccordionItem {
12
13
  title: React.ReactNode;
13
14
  /** Panel content (e.g. the answer). */
14
15
  content: React.ReactNode;
16
+ /**
17
+ * Optional leading icon shown before the title. Pass a library icon
18
+ * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:
19
+ * `icon: HelpCircleIcon`. Unsupported values are ignored.
20
+ */
21
+ icon?: LucideIcon;
15
22
  disabled?: boolean;
16
23
  }
17
24
  interface AccordionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>, VariantProps<typeof accordionVariants> {
@@ -1,5 +1,6 @@
1
1
  import * as class_variance_authority_types from 'class-variance-authority/types';
2
2
  import * as React from 'react';
3
+ import { LucideIcon } from 'lucide-react';
3
4
  import { VariantProps } from 'class-variance-authority';
4
5
 
5
6
  declare const accordionVariants: (props?: ({
@@ -12,6 +13,12 @@ interface AccordionItem {
12
13
  title: React.ReactNode;
13
14
  /** Panel content (e.g. the answer). */
14
15
  content: React.ReactNode;
16
+ /**
17
+ * Optional leading icon shown before the title. Pass a library icon
18
+ * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:
19
+ * `icon: HelpCircleIcon`. Unsupported values are ignored.
20
+ */
21
+ icon?: LucideIcon;
15
22
  disabled?: boolean;
16
23
  }
17
24
  interface AccordionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>, VariantProps<typeof accordionVariants> {
@@ -1,4 +1,4 @@
1
- export { Accordion } from '../../chunk-JUJGWUAN.js';
1
+ export { Accordion } from '../../chunk-BHKGVFTY.js';
2
2
  import '../../chunk-TQONQSJO.js';
3
3
  import '../../chunk-PX6F3LHL.js';
4
4
  //# sourceMappingURL=index.js.map
package/dist/index.cjs CHANGED
@@ -122,7 +122,7 @@ require('./chunk-AT732HC6.cjs');
122
122
  var chunkL7YQBSEL_cjs = require('./chunk-L7YQBSEL.cjs');
123
123
  var chunkC7RVKV25_cjs = require('./chunk-C7RVKV25.cjs');
124
124
  var chunk5K7PRPSV_cjs = require('./chunk-5K7PRPSV.cjs');
125
- var chunkSHBCUXFS_cjs = require('./chunk-SHBCUXFS.cjs');
125
+ var chunkPRGQVUMU_cjs = require('./chunk-PRGQVUMU.cjs');
126
126
  require('./chunk-P22YCUVM.cjs');
127
127
  var chunkQ3YVLO52_cjs = require('./chunk-Q3YVLO52.cjs');
128
128
  var chunkGKDHMNUA_cjs = require('./chunk-GKDHMNUA.cjs');
@@ -45839,7 +45839,7 @@ Object.defineProperty(exports, "miewebUISafelist", {
45839
45839
  });
45840
45840
  Object.defineProperty(exports, "Accordion", {
45841
45841
  enumerable: true,
45842
- get: function () { return chunkSHBCUXFS_cjs.Accordion; }
45842
+ get: function () { return chunkPRGQVUMU_cjs.Accordion; }
45843
45843
  });
45844
45844
  Object.defineProperty(exports, "AvatarGroup", {
45845
45845
  enumerable: true,
package/dist/index.d.cts CHANGED
@@ -2,10 +2,10 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as class_variance_authority_types from 'class-variance-authority/types';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { ReactNode, RefObject } from 'react';
5
- import { VariantProps } from 'class-variance-authority';
6
5
  import * as lucide_react from 'lucide-react';
7
6
  import { LucideIcon } from 'lucide-react';
8
7
  export { Accessibility as AccessibilityIcon, Activity as ActivityIcon, AlertCircle as AlertCircleIcon, AlertTriangle as AlertTriangleIcon, Wheat as AllergyIcon, WheatOff as AllergyOffIcon, Ambulance as AmbulanceIcon, ArrowLeft as ArrowLeftIcon, ArrowRight as ArrowRightIcon, ArrowUpDown as ArrowUpDownIcon, ArrowUp as ArrowUpIcon, AtSign as AtSignIcon, Baby as BabyIcon, BadgeAlert as BadgeAlertIcon, BadgeCheck as BadgeCheckIcon, BadgeMinus as BadgeMinusIcon, BadgePlus as BadgePlusIcon, BadgeX as BadgeXIcon, Ban as BanIcon, Bandage as BandageIcon, BedDouble as BedDoubleIcon, Bed as BedIcon, BedSingle as BedSingleIcon, Bell as BellIcon, BellOff as BellOffIcon, Biohazard as BiohazardIcon, Bone as BoneIcon, BookOpenCheck as BookOpenCheckIcon, BookOpen as BookOpenIcon, Bookmark as BookmarkIcon, Bot as BotIcon, Brain as BrainIcon, Briefcase as BriefcaseIcon, BriefcaseMedical as BriefcaseMedicalIcon, Building as BuildingIcon, CalendarCheck as CalendarCheckIcon, Calendar as CalendarIcon, Camera as CameraIcon, Cannabis as CannabisIcon, BarChart3 as ChartIcon, CheckCircle as CheckCircleIcon, Check as CheckIcon, ChevronDown as ChevronDownIcon, ChevronLeft as ChevronLeftIcon, ChevronRight as ChevronRightIcon, ChevronUp as ChevronUpIcon, Cigarette as CigaretteIcon, CigaretteOff as CigaretteOffIcon, CircleDotDashed as CircleDotDashedIcon, CircleDot as CircleDotIcon, CircleUser as CircleUserIcon, ClipboardCheck as ClipboardCheckIcon, Clipboard as ClipboardIcon, ClipboardList as ClipboardListIcon, ClipboardPlus as ClipboardPlusIcon, Clock as ClockIcon, Cog as CogIcon, Columns as ColumnsIcon, Copy as CopyIcon, CreditCard as CreditCardIcon, Cross as CrossIcon, Dna as DnaIcon, DollarSign as DollarSignIcon, Download as DownloadIcon, Droplet as DropletIcon, Droplets as DropletsIcon, Ear as EarIcon, ExternalLink as ExternalLinkIcon, Eye as EyeIcon, EyeOff as EyeOffIcon, FileCheck2 as FileCheck2Icon, FileCheck as FileCheckIcon, FileHeart as FileHeartIcon, File as FileIcon, FilePlus2 as FilePlus2Icon, FilePlus as FilePlusIcon, FileText as FileTextIcon, Filter as FilterIcon, Fingerprint as FingerprintIcon, Flag as FlagIcon, FlaskConical as FlaskIcon, FlaskRound as FlaskRoundIcon, Folder as FolderIcon, FolderOpen as FolderOpenIcon, Footprints as FootprintsIcon, Glasses as GlassesIcon, Globe as GlobeIcon, LayoutGrid as GridIcon, GripVertical as GripVerticalIcon, Hand as HandIcon, HandMetal as HandMetalIcon, Hash as HashIcon, Heart as HeartIcon, HeartOff as HeartOffIcon, HeartPulse as HeartPulseIcon, HelpCircle as HelpCircleIcon, History as HistoryIcon, Home as HomeIcon, Hospital as HospitalIcon, Image as ImageIcon, Info as InfoIcon, Key as KeyIcon, Link as LinkIcon, List as ListIcon, Loader2 as LoaderIcon, Lock as LockIcon, LogIn as LogInIcon, LogOut as LogOutIcon, LucideIcon, LucideProps, Mail as MailIcon, MapPin as MapPinIcon, Maximize as MaximizeIcon, Menu as MenuIcon, MessageSquare as MessageIcon, Mic as MicIcon, Microscope as MicroscopeIcon, Minimize as MinimizeIcon, Minus as MinusIcon, Monitor as MonitorIcon, Moon as MoonIcon, MoreHorizontal as MoreHorizontalIcon, MoreVertical as MoreVerticalIcon, NotebookPen as NotebookPenIcon, Palette as PaletteIcon, Paperclip as PaperclipIcon, CircleUserRound as PatientIcon, Pencil as PencilIcon, UserRound as PersonIcon, Phone as PhoneIcon, PieChart as PieChartIcon, Pill as PillIcon, Plus as PlusIcon, Printer as PrinterIcon, Radiation as RadiationIcon, Ruler as RulerIcon, Save as SaveIcon, Scale as ScaleIcon, Scaling as ScalingIcon, ScanEye as ScanEyeIcon, Scan as ScanIcon, ScanLine as ScanLineIcon, Search as SearchIcon, Settings as SettingsIcon, Share as ShareIcon, ShieldCheck as ShieldCheckIcon, Shield as ShieldIcon, ShieldPlus as ShieldPlusIcon, Sliders as SlidersIcon, Sparkle as SparkleIcon, Star as StarIcon, Stethoscope as StethoscopeIcon, StickyNote as StickyNoteIcon, Square as StopIcon, Sun as SunIcon, Syringe as SyringeIcon, Table as TableIcon, Tablets as TabletsIcon, Tag as TagIcon, TestTube2 as TestTubeIcon, TestTubes as TestTubesIcon, Thermometer as ThermometerIcon, ThermometerSun as ThermometerSunIcon, ThumbsDown as ThumbsDownIcon, ThumbsUp as ThumbsUpIcon, Timer as TimerIcon, Trash2 as TrashIcon, TrendingDown as TrendingDownIcon, TrendingUp as TrendingUpIcon, Truck as TruckIcon, Unlock as UnlockIcon, Upload as UploadIcon, UserCheck as UserCheckIcon, User as UserIcon, UserMinus as UserMinusIcon, UserPlus as UserPlusIcon, UserRoundCheck as UserRoundCheckIcon, UserRoundMinus as UserRoundMinusIcon, UserRoundPlus as UserRoundPlusIcon, UserRoundX as UserRoundXIcon, Users as UsersIcon, Weight as WeightIcon, Wrench as WrenchIcon, XCircle as XCircleIcon, X as XIcon, Zap as ZapIcon } from 'lucide-react';
8
+ import { VariantProps } from 'class-variance-authority';
9
9
  import { DateTime } from 'luxon';
10
10
  import DOMPurify from 'dompurify';
11
11
  import { ClassValue } from 'clsx';
@@ -73,6 +73,12 @@ interface AccordionItem {
73
73
  title: React$1.ReactNode;
74
74
  /** Panel content (e.g. the answer). */
75
75
  content: React$1.ReactNode;
76
+ /**
77
+ * Optional leading icon shown before the title. Pass a library icon
78
+ * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:
79
+ * `icon: HelpCircleIcon`. Unsupported values are ignored.
80
+ */
81
+ icon?: LucideIcon;
76
82
  disabled?: boolean;
77
83
  }
78
84
  interface AccordionProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, 'onChange'>, VariantProps<typeof accordionVariants$1> {
package/dist/index.d.ts CHANGED
@@ -2,10 +2,10 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as class_variance_authority_types from 'class-variance-authority/types';
3
3
  import * as React$1 from 'react';
4
4
  import React__default, { ReactNode, RefObject } from 'react';
5
- import { VariantProps } from 'class-variance-authority';
6
5
  import * as lucide_react from 'lucide-react';
7
6
  import { LucideIcon } from 'lucide-react';
8
7
  export { Accessibility as AccessibilityIcon, Activity as ActivityIcon, AlertCircle as AlertCircleIcon, AlertTriangle as AlertTriangleIcon, Wheat as AllergyIcon, WheatOff as AllergyOffIcon, Ambulance as AmbulanceIcon, ArrowLeft as ArrowLeftIcon, ArrowRight as ArrowRightIcon, ArrowUpDown as ArrowUpDownIcon, ArrowUp as ArrowUpIcon, AtSign as AtSignIcon, Baby as BabyIcon, BadgeAlert as BadgeAlertIcon, BadgeCheck as BadgeCheckIcon, BadgeMinus as BadgeMinusIcon, BadgePlus as BadgePlusIcon, BadgeX as BadgeXIcon, Ban as BanIcon, Bandage as BandageIcon, BedDouble as BedDoubleIcon, Bed as BedIcon, BedSingle as BedSingleIcon, Bell as BellIcon, BellOff as BellOffIcon, Biohazard as BiohazardIcon, Bone as BoneIcon, BookOpenCheck as BookOpenCheckIcon, BookOpen as BookOpenIcon, Bookmark as BookmarkIcon, Bot as BotIcon, Brain as BrainIcon, Briefcase as BriefcaseIcon, BriefcaseMedical as BriefcaseMedicalIcon, Building as BuildingIcon, CalendarCheck as CalendarCheckIcon, Calendar as CalendarIcon, Camera as CameraIcon, Cannabis as CannabisIcon, BarChart3 as ChartIcon, CheckCircle as CheckCircleIcon, Check as CheckIcon, ChevronDown as ChevronDownIcon, ChevronLeft as ChevronLeftIcon, ChevronRight as ChevronRightIcon, ChevronUp as ChevronUpIcon, Cigarette as CigaretteIcon, CigaretteOff as CigaretteOffIcon, CircleDotDashed as CircleDotDashedIcon, CircleDot as CircleDotIcon, CircleUser as CircleUserIcon, ClipboardCheck as ClipboardCheckIcon, Clipboard as ClipboardIcon, ClipboardList as ClipboardListIcon, ClipboardPlus as ClipboardPlusIcon, Clock as ClockIcon, Cog as CogIcon, Columns as ColumnsIcon, Copy as CopyIcon, CreditCard as CreditCardIcon, Cross as CrossIcon, Dna as DnaIcon, DollarSign as DollarSignIcon, Download as DownloadIcon, Droplet as DropletIcon, Droplets as DropletsIcon, Ear as EarIcon, ExternalLink as ExternalLinkIcon, Eye as EyeIcon, EyeOff as EyeOffIcon, FileCheck2 as FileCheck2Icon, FileCheck as FileCheckIcon, FileHeart as FileHeartIcon, File as FileIcon, FilePlus2 as FilePlus2Icon, FilePlus as FilePlusIcon, FileText as FileTextIcon, Filter as FilterIcon, Fingerprint as FingerprintIcon, Flag as FlagIcon, FlaskConical as FlaskIcon, FlaskRound as FlaskRoundIcon, Folder as FolderIcon, FolderOpen as FolderOpenIcon, Footprints as FootprintsIcon, Glasses as GlassesIcon, Globe as GlobeIcon, LayoutGrid as GridIcon, GripVertical as GripVerticalIcon, Hand as HandIcon, HandMetal as HandMetalIcon, Hash as HashIcon, Heart as HeartIcon, HeartOff as HeartOffIcon, HeartPulse as HeartPulseIcon, HelpCircle as HelpCircleIcon, History as HistoryIcon, Home as HomeIcon, Hospital as HospitalIcon, Image as ImageIcon, Info as InfoIcon, Key as KeyIcon, Link as LinkIcon, List as ListIcon, Loader2 as LoaderIcon, Lock as LockIcon, LogIn as LogInIcon, LogOut as LogOutIcon, LucideIcon, LucideProps, Mail as MailIcon, MapPin as MapPinIcon, Maximize as MaximizeIcon, Menu as MenuIcon, MessageSquare as MessageIcon, Mic as MicIcon, Microscope as MicroscopeIcon, Minimize as MinimizeIcon, Minus as MinusIcon, Monitor as MonitorIcon, Moon as MoonIcon, MoreHorizontal as MoreHorizontalIcon, MoreVertical as MoreVerticalIcon, NotebookPen as NotebookPenIcon, Palette as PaletteIcon, Paperclip as PaperclipIcon, CircleUserRound as PatientIcon, Pencil as PencilIcon, UserRound as PersonIcon, Phone as PhoneIcon, PieChart as PieChartIcon, Pill as PillIcon, Plus as PlusIcon, Printer as PrinterIcon, Radiation as RadiationIcon, Ruler as RulerIcon, Save as SaveIcon, Scale as ScaleIcon, Scaling as ScalingIcon, ScanEye as ScanEyeIcon, Scan as ScanIcon, ScanLine as ScanLineIcon, Search as SearchIcon, Settings as SettingsIcon, Share as ShareIcon, ShieldCheck as ShieldCheckIcon, Shield as ShieldIcon, ShieldPlus as ShieldPlusIcon, Sliders as SlidersIcon, Sparkle as SparkleIcon, Star as StarIcon, Stethoscope as StethoscopeIcon, StickyNote as StickyNoteIcon, Square as StopIcon, Sun as SunIcon, Syringe as SyringeIcon, Table as TableIcon, Tablets as TabletsIcon, Tag as TagIcon, TestTube2 as TestTubeIcon, TestTubes as TestTubesIcon, Thermometer as ThermometerIcon, ThermometerSun as ThermometerSunIcon, ThumbsDown as ThumbsDownIcon, ThumbsUp as ThumbsUpIcon, Timer as TimerIcon, Trash2 as TrashIcon, TrendingDown as TrendingDownIcon, TrendingUp as TrendingUpIcon, Truck as TruckIcon, Unlock as UnlockIcon, Upload as UploadIcon, UserCheck as UserCheckIcon, User as UserIcon, UserMinus as UserMinusIcon, UserPlus as UserPlusIcon, UserRoundCheck as UserRoundCheckIcon, UserRoundMinus as UserRoundMinusIcon, UserRoundPlus as UserRoundPlusIcon, UserRoundX as UserRoundXIcon, Users as UsersIcon, Weight as WeightIcon, Wrench as WrenchIcon, XCircle as XCircleIcon, X as XIcon, Zap as ZapIcon } from 'lucide-react';
8
+ import { VariantProps } from 'class-variance-authority';
9
9
  import { DateTime } from 'luxon';
10
10
  import DOMPurify from 'dompurify';
11
11
  import { ClassValue } from 'clsx';
@@ -73,6 +73,12 @@ interface AccordionItem {
73
73
  title: React$1.ReactNode;
74
74
  /** Panel content (e.g. the answer). */
75
75
  content: React$1.ReactNode;
76
+ /**
77
+ * Optional leading icon shown before the title. Pass a library icon
78
+ * component (lucide-react / `@mieweb/ui` Icons barrel), not an element:
79
+ * `icon: HelpCircleIcon`. Unsupported values are ignored.
80
+ */
81
+ icon?: LucideIcon;
76
82
  disabled?: boolean;
77
83
  }
78
84
  interface AccordionProps extends Omit<React$1.HTMLAttributes<HTMLDivElement>, 'onChange'>, VariantProps<typeof accordionVariants$1> {
package/dist/index.js CHANGED
@@ -141,7 +141,7 @@ import './chunk-7XUN3OQT.js';
141
141
  export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber } from './chunk-ZVPJ2MH6.js';
142
142
  export { isHtmlEmpty, stripHtmlTags } from './chunk-ERLPVXOV.js';
143
143
  export { miewebUIPreset, miewebUISafelist } from './chunk-EBQG3NGT.js';
144
- export { Accordion } from './chunk-JUJGWUAN.js';
144
+ export { Accordion } from './chunk-BHKGVFTY.js';
145
145
  import './chunk-H2Y7HZV5.js';
146
146
  export { AvatarGroup, defaultAvatarGroupLabels } from './chunk-DVDT7TX7.js';
147
147
  import { Avatar } from './chunk-TI3TQQ7F.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mieweb/ui",
3
- "version": "0.11.0-dev.20",
3
+ "version": "0.11.0-dev.21",
4
4
  "description": "A themeable, accessible React component library built with Tailwind CSS",
5
5
  "author": "Medical Informatics Engineering, Inc.",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Accordion/Accordion.tsx"],"names":["Accordion"],"mappings":"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","file":"chunk-JUJGWUAN.js","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ChevronDown } from 'lucide-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst accordionVariants = cva('', {\n variants: {\n variant: {\n // Each item is its own card with spacing between\n separated: 'flex flex-col gap-3',\n // One bordered list with dividers\n joined: 'border-border divide-border divide-y rounded-lg border',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\nconst itemVariants = cva('overflow-hidden', {\n variants: {\n variant: {\n separated: 'border-border bg-card rounded-lg border',\n joined: 'bg-card first:rounded-t-lg last:rounded-b-lg',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AccordionItem {\n /** Stable id used for open state and aria wiring. */\n id: string;\n /** Header label (e.g. an FAQ question). */\n title: React.ReactNode;\n /** Panel content (e.g. the answer). */\n content: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface AccordionProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>,\n VariantProps<typeof accordionVariants> {\n items: AccordionItem[];\n /** `single` keeps at most one panel open; `multiple` allows any number. */\n type?: 'single' | 'multiple';\n /** Ids open initially (uncontrolled). */\n defaultOpenIds?: string[];\n /** Controlled open ids. Changes reported via `onOpenChange`. */\n openIds?: string[];\n onOpenChange?: (openIds: string[]) => void;\n /** In `single` mode, allow closing the open panel (default true). */\n collapsible?: boolean;\n /** Heading level wrapping each trigger for document outline (default h3). */\n headingLevel?: 'h2' | 'h3' | 'h4';\n /**\n * Persists the open ids in `localStorage` under this key (uncontrolled\n * only). Restored after mount, so server and hydration renders use\n * `defaultOpenIds`.\n */\n storageKey?: string;\n}\n\n// =============================================================================\n// Accordion\n// =============================================================================\n\n/**\n * A vertically stacked set of expandable panels — FAQ lists, settings\n * groups, progressive disclosure. Panels animate open to their natural\n * height (CSS grid rows, no max-height clipping), and headers are real\n * buttons inside headings with full `aria-expanded`/`aria-controls` wiring.\n *\n * @example\n * ```tsx\n * <Accordion\n * type=\"single\"\n * items={[\n * { id: 'pricing', title: 'How is pricing calculated?', content: <p>…</p> },\n * { id: 'billing', title: 'When am I billed?', content: <p>…</p> },\n * ]}\n * />\n * ```\n */\nexport const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n function Accordion(\n {\n items,\n type = 'single',\n defaultOpenIds,\n openIds: controlledOpen,\n onOpenChange,\n collapsible = true,\n headingLevel: Heading = 'h3',\n storageKey,\n variant,\n className,\n ...props\n },\n ref\n ) {\n const baseId = React.useId();\n const [internalOpen, setInternalOpen] = React.useState<string[]>(\n () => defaultOpenIds ?? []\n );\n const persists = Boolean(storageKey) && controlledOpen === undefined;\n\n React.useEffect(() => {\n if (!persists || !storageKey) return;\n try {\n const stored: unknown = JSON.parse(\n window.localStorage.getItem(storageKey) ?? 'null'\n );\n if (Array.isArray(stored)) {\n setInternalOpen(\n stored.filter((id): id is string => typeof id === 'string')\n );\n }\n } catch {\n // Storage blocked or value corrupt: keep defaultOpenIds.\n }\n }, [persists, storageKey]);\n const rawOpen = controlledOpen ?? internalOpen;\n // Single mode keeps at most one panel open, even if defaultOpenIds or a\n // controlled openIds array hands us several. Stored ids for removed items\n // are dropped first so they can't crowd out a valid one.\n const open = React.useMemo(() => {\n const known = persists\n ? rawOpen.filter((id) => items.some((item) => item.id === id))\n : rawOpen;\n return type === 'single' ? known.slice(0, 1) : known;\n }, [type, rawOpen, persists, items]);\n const openSet = React.useMemo(() => new Set(open), [open]);\n\n const toggle = (id: string) => {\n let next: string[];\n if (openSet.has(id)) {\n if (type === 'single' && !collapsible) return;\n next = open.filter((o) => o !== id);\n } else {\n next = type === 'single' ? [id] : [...open, id];\n }\n if (controlledOpen === undefined) setInternalOpen(next);\n if (persists && storageKey) {\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(next));\n } catch {\n // Storage blocked or full; state still updates in memory.\n }\n }\n onOpenChange?.(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn(accordionVariants({ variant }), className)}\n {...props}\n >\n {items.map((item) => {\n const isOpen = openSet.has(item.id);\n const triggerId = `${baseId}-${item.id}-trigger`;\n const panelId = `${baseId}-${item.id}-panel`;\n return (\n <div key={item.id} className={cn(itemVariants({ variant }))}>\n <Heading className=\"m-0\">\n <button\n type=\"button\"\n id={triggerId}\n data-slot=\"accordion-trigger\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n disabled={item.disabled}\n onClick={() => toggle(item.id)}\n className={cn(\n 'hover:bg-muted/60 flex w-full items-center justify-between gap-4 px-5 py-4 text-start transition-colors',\n 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n <span className=\"text-foreground min-w-0 text-sm font-semibold\">\n {item.title}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n 'text-muted-foreground h-5 w-5 shrink-0 transition-transform duration-200',\n isOpen && 'rotate-180'\n )}\n />\n </button>\n </Heading>\n {/* grid-rows 0fr→1fr animates to natural height without a max-height clip;\n collapsed panels are hidden from AT and unfocusable via aria-hidden + inert */}\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n aria-hidden={!isOpen}\n inert={!isOpen || undefined}\n className={cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )}\n >\n <div className=\"overflow-hidden\">\n <div\n data-slot=\"accordion-content\"\n className=\"text-muted-foreground px-5 pb-4 text-sm leading-relaxed\"\n >\n {item.content}\n </div>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n);\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Accordion/Accordion.tsx"],"names":["cva","React","Accordion","jsx","cn","jsxs","ChevronDown"],"mappings":"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","file":"chunk-SHBCUXFS.cjs","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ChevronDown } from 'lucide-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst accordionVariants = cva('', {\n variants: {\n variant: {\n // Each item is its own card with spacing between\n separated: 'flex flex-col gap-3',\n // One bordered list with dividers\n joined: 'border-border divide-border divide-y rounded-lg border',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\nconst itemVariants = cva('overflow-hidden', {\n variants: {\n variant: {\n separated: 'border-border bg-card rounded-lg border',\n joined: 'bg-card first:rounded-t-lg last:rounded-b-lg',\n },\n },\n defaultVariants: {\n variant: 'separated',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AccordionItem {\n /** Stable id used for open state and aria wiring. */\n id: string;\n /** Header label (e.g. an FAQ question). */\n title: React.ReactNode;\n /** Panel content (e.g. the answer). */\n content: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface AccordionProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>,\n VariantProps<typeof accordionVariants> {\n items: AccordionItem[];\n /** `single` keeps at most one panel open; `multiple` allows any number. */\n type?: 'single' | 'multiple';\n /** Ids open initially (uncontrolled). */\n defaultOpenIds?: string[];\n /** Controlled open ids. Changes reported via `onOpenChange`. */\n openIds?: string[];\n onOpenChange?: (openIds: string[]) => void;\n /** In `single` mode, allow closing the open panel (default true). */\n collapsible?: boolean;\n /** Heading level wrapping each trigger for document outline (default h3). */\n headingLevel?: 'h2' | 'h3' | 'h4';\n /**\n * Persists the open ids in `localStorage` under this key (uncontrolled\n * only). Restored after mount, so server and hydration renders use\n * `defaultOpenIds`.\n */\n storageKey?: string;\n}\n\n// =============================================================================\n// Accordion\n// =============================================================================\n\n/**\n * A vertically stacked set of expandable panels — FAQ lists, settings\n * groups, progressive disclosure. Panels animate open to their natural\n * height (CSS grid rows, no max-height clipping), and headers are real\n * buttons inside headings with full `aria-expanded`/`aria-controls` wiring.\n *\n * @example\n * ```tsx\n * <Accordion\n * type=\"single\"\n * items={[\n * { id: 'pricing', title: 'How is pricing calculated?', content: <p>…</p> },\n * { id: 'billing', title: 'When am I billed?', content: <p>…</p> },\n * ]}\n * />\n * ```\n */\nexport const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n function Accordion(\n {\n items,\n type = 'single',\n defaultOpenIds,\n openIds: controlledOpen,\n onOpenChange,\n collapsible = true,\n headingLevel: Heading = 'h3',\n storageKey,\n variant,\n className,\n ...props\n },\n ref\n ) {\n const baseId = React.useId();\n const [internalOpen, setInternalOpen] = React.useState<string[]>(\n () => defaultOpenIds ?? []\n );\n const persists = Boolean(storageKey) && controlledOpen === undefined;\n\n React.useEffect(() => {\n if (!persists || !storageKey) return;\n try {\n const stored: unknown = JSON.parse(\n window.localStorage.getItem(storageKey) ?? 'null'\n );\n if (Array.isArray(stored)) {\n setInternalOpen(\n stored.filter((id): id is string => typeof id === 'string')\n );\n }\n } catch {\n // Storage blocked or value corrupt: keep defaultOpenIds.\n }\n }, [persists, storageKey]);\n const rawOpen = controlledOpen ?? internalOpen;\n // Single mode keeps at most one panel open, even if defaultOpenIds or a\n // controlled openIds array hands us several. Stored ids for removed items\n // are dropped first so they can't crowd out a valid one.\n const open = React.useMemo(() => {\n const known = persists\n ? rawOpen.filter((id) => items.some((item) => item.id === id))\n : rawOpen;\n return type === 'single' ? known.slice(0, 1) : known;\n }, [type, rawOpen, persists, items]);\n const openSet = React.useMemo(() => new Set(open), [open]);\n\n const toggle = (id: string) => {\n let next: string[];\n if (openSet.has(id)) {\n if (type === 'single' && !collapsible) return;\n next = open.filter((o) => o !== id);\n } else {\n next = type === 'single' ? [id] : [...open, id];\n }\n if (controlledOpen === undefined) setInternalOpen(next);\n if (persists && storageKey) {\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(next));\n } catch {\n // Storage blocked or full; state still updates in memory.\n }\n }\n onOpenChange?.(next);\n };\n\n return (\n <div\n ref={ref}\n className={cn(accordionVariants({ variant }), className)}\n {...props}\n >\n {items.map((item) => {\n const isOpen = openSet.has(item.id);\n const triggerId = `${baseId}-${item.id}-trigger`;\n const panelId = `${baseId}-${item.id}-panel`;\n return (\n <div key={item.id} className={cn(itemVariants({ variant }))}>\n <Heading className=\"m-0\">\n <button\n type=\"button\"\n id={triggerId}\n data-slot=\"accordion-trigger\"\n aria-expanded={isOpen}\n aria-controls={panelId}\n disabled={item.disabled}\n onClick={() => toggle(item.id)}\n className={cn(\n 'hover:bg-muted/60 flex w-full items-center justify-between gap-4 px-5 py-4 text-start transition-colors',\n 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-inset',\n 'disabled:pointer-events-none disabled:opacity-50'\n )}\n >\n <span className=\"text-foreground min-w-0 text-sm font-semibold\">\n {item.title}\n </span>\n <ChevronDown\n aria-hidden=\"true\"\n className={cn(\n 'text-muted-foreground h-5 w-5 shrink-0 transition-transform duration-200',\n isOpen && 'rotate-180'\n )}\n />\n </button>\n </Heading>\n {/* grid-rows 0fr→1fr animates to natural height without a max-height clip;\n collapsed panels are hidden from AT and unfocusable via aria-hidden + inert */}\n <div\n id={panelId}\n role=\"region\"\n aria-labelledby={triggerId}\n aria-hidden={!isOpen}\n inert={!isOpen || undefined}\n className={cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )}\n >\n <div className=\"overflow-hidden\">\n <div\n data-slot=\"accordion-content\"\n className=\"text-muted-foreground px-5 pb-4 text-sm leading-relaxed\"\n >\n {item.content}\n </div>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n );\n }\n);\n"]}