@seekalfred/primitives 0.7.1 → 0.7.3
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/README.md +195 -4
- package/dist/alert/alert.d.ts +49 -4
- package/dist/alert/alert.js +66 -38
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert/index.d.ts +2 -2
- package/dist/alert/index.js +2 -2
- package/dist/alert-dialog/alert-dialog.d.ts +17 -4
- package/dist/alert-dialog/alert-dialog.js +26 -2
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/badge/badge.d.ts +1 -1
- package/dist/button/button.d.ts +2 -2
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/card/card.d.ts +22 -1
- package/dist/card/card.js +60 -7
- package/dist/card/card.js.map +1 -1
- package/dist/carousel/carousel.d.ts +21 -0
- package/dist/carousel/carousel.js +34 -8
- package/dist/carousel/carousel.js.map +1 -1
- package/dist/copy-button/copy-button.d.ts +18 -2
- package/dist/copy-button/copy-button.js +29 -8
- package/dist/copy-button/copy-button.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +22 -0
- package/dist/donut-chart/donut-chart.js +35 -18
- package/dist/donut-chart/donut-chart.js.map +1 -1
- package/dist/donut-chart/index.d.ts +2 -2
- package/dist/empty-state/empty-state.d.ts +21 -0
- package/dist/empty-state/empty-state.js +16 -4
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/filter-bar/filter-bar.d.ts +4 -1
- package/dist/filter-bar/filter-bar.js +53 -9
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +7 -7
- package/dist/input/input.js +4 -4
- package/dist/input/input.js.map +1 -1
- package/dist/lib/field-shell.js +10 -3
- package/dist/lib/field-shell.js.map +1 -1
- package/dist/lib/field-surface.js +22 -1
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/use-copy-feedback.js +17 -3
- package/dist/lib/use-copy-feedback.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -2
- package/dist/line-chart/line-chart.d.ts +42 -2
- package/dist/line-chart/line-chart.js +143 -14
- package/dist/line-chart/line-chart.js.map +1 -1
- package/dist/money-input/money-input.d.ts +22 -1
- package/dist/money-input/money-input.js +13 -7
- package/dist/money-input/money-input.js.map +1 -1
- package/dist/morph-select/morph-select.d.ts +19 -1
- package/dist/morph-select/morph-select.js +21 -2
- package/dist/morph-select/morph-select.js.map +1 -1
- package/dist/multi-select/index.d.ts +2 -2
- package/dist/multi-select/index.js +2 -2
- package/dist/multi-select/multi-select.d.ts +43 -2
- package/dist/multi-select/multi-select.js +93 -36
- package/dist/multi-select/multi-select.js.map +1 -1
- package/dist/password-field/password-field.d.ts +16 -0
- package/dist/password-field/password-field.js +37 -8
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/popover/index.d.ts +2 -2
- package/dist/popover/index.js +2 -2
- package/dist/popover/popover.d.ts +39 -2
- package/dist/popover/popover.js +29 -2
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +16 -2
- package/dist/progress/progress.js +15 -8
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/index.d.ts +2 -2
- package/dist/radio-group/index.js +2 -2
- package/dist/radio-group/radio-group.d.ts +33 -3
- package/dist/radio-group/radio-group.js +109 -9
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.js +4 -3
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/search-field/search-field.d.ts +22 -1
- package/dist/search-field/search-field.js +22 -7
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +1 -1
- package/dist/select/select.js +8 -6
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +21 -9
- package/dist/separator/separator.js +33 -11
- package/dist/separator/separator.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +34 -1
- package/dist/stat-card/stat-card.js +51 -21
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/table/table.d.ts +1 -1
- package/dist/time-picker/time-picker.d.ts +36 -1
- package/dist/time-picker/time-picker.js +33 -5
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/toast.d.ts +2 -2
- package/dist/usage-meter/index.d.ts +2 -2
- package/dist/usage-meter/usage-meter.d.ts +30 -0
- package/dist/usage-meter/usage-meter.js +56 -38
- package/dist/usage-meter/usage-meter.js.map +1 -1
- package/package.json +3 -3
|
@@ -63,9 +63,27 @@ export interface MorphSelectProps {
|
|
|
63
63
|
/** Adds a hidden input for native form submission. */
|
|
64
64
|
name?: string;
|
|
65
65
|
disabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The trigger's `id`, so a `<label htmlFor>` outside the select (or a form
|
|
68
|
+
* library that focuses a field by id) reaches the real control. When such a
|
|
69
|
+
* label exists it NAMES the trigger: the trigger then drops its
|
|
70
|
+
* `aria-labelledby` to the inner `label`, which would otherwise outrank the
|
|
71
|
+
* outside label in the accessible-name computation.
|
|
72
|
+
*/
|
|
73
|
+
id?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Marks the choice as wrong: the trigger takes the invalid border and is
|
|
76
|
+
* `aria-invalid`. Pair it with `errorMessage`, or the state is shown visually in colour only.
|
|
77
|
+
*/
|
|
78
|
+
isInvalid?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Why the choice is wrong, under the control. It shows only while `isInvalid`
|
|
81
|
+
* is set, and is named in the trigger's and the search field's `aria-describedby`.
|
|
82
|
+
*/
|
|
83
|
+
errorMessage?: ReactNode;
|
|
66
84
|
/** Extra classes for the wrapper. Merged last. */
|
|
67
85
|
className?: string;
|
|
68
86
|
}
|
|
69
|
-
export declare function MorphSelect({ label, labelHidden, items, value, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, panelWidth, maxListHeight, align, size, name, disabled, className }: MorphSelectProps): import("react").JSX.Element;
|
|
87
|
+
export declare function MorphSelect({ label, labelHidden, items, value, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, panelWidth, maxListHeight, align, size, name, disabled, id, isInvalid, errorMessage, className }: MorphSelectProps): import("react").JSX.Element;
|
|
70
88
|
//#endregion
|
|
71
89
|
//# sourceMappingURL=morph-select.d.ts.map
|
|
@@ -72,9 +72,11 @@ function LeavingLayer({ id, rect, dir, fly, onDone, children }) {
|
|
|
72
72
|
children
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
function MorphSelect({ label, labelHidden = false, items, value, defaultValue = null, onValueChange, placeholder = "Select", searchable = "auto", searchPlaceholder, panelWidth = 272, maxListHeight = 296, align = "start", size = "md", name, disabled = false, className }) {
|
|
75
|
+
function MorphSelect({ label, labelHidden = false, items, value, defaultValue = null, onValueChange, placeholder = "Select", searchable = "auto", searchPlaceholder, panelWidth = 272, maxListHeight = 296, align = "start", size = "md", name, disabled = false, id, isInvalid = false, errorMessage, className }) {
|
|
76
76
|
const uid = useId();
|
|
77
77
|
const labelId = `${uid}-label`;
|
|
78
|
+
const errorId = `${uid}-error`;
|
|
79
|
+
const showError = isInvalid && errorMessage != null;
|
|
78
80
|
const listId = `${uid}-list`;
|
|
79
81
|
const optionId = (index) => `${uid}-option-${index}`;
|
|
80
82
|
const rootRef = useRef(null);
|
|
@@ -84,6 +86,7 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
84
86
|
const faceRef = useRef(null);
|
|
85
87
|
const scrollRef = useRef(null);
|
|
86
88
|
const triggerRef = useRef(null);
|
|
89
|
+
const [outsideLabel, setOutsideLabel] = useState(false);
|
|
87
90
|
const inputRef = useRef(null);
|
|
88
91
|
const slotRef = useRef(null);
|
|
89
92
|
const layerRef = useRef(null);
|
|
@@ -198,6 +201,9 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
198
201
|
open,
|
|
199
202
|
visibleSections
|
|
200
203
|
]);
|
|
204
|
+
useIsomorphicLayoutEffect(() => {
|
|
205
|
+
setOutsideLabel(id != null && (triggerRef.current?.labels?.length ?? 0) > 0);
|
|
206
|
+
}, [id]);
|
|
201
207
|
const showSearch = open && canSearch;
|
|
202
208
|
useIsomorphicLayoutEffect(() => {
|
|
203
209
|
triggerRef.current?.toggleAttribute("inert", showSearch);
|
|
@@ -413,6 +419,7 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
413
419
|
"data-align": align,
|
|
414
420
|
"data-size": size,
|
|
415
421
|
"data-disabled": disabled || void 0,
|
|
422
|
+
"data-invalid": isInvalid || void 0,
|
|
416
423
|
style: rootStyle,
|
|
417
424
|
onBlur,
|
|
418
425
|
className: cn("morph-select-root group/ms relative inline-grid min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base", className),
|
|
@@ -439,16 +446,20 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
439
446
|
"data-slot": "morph-select-shape",
|
|
440
447
|
"data-open": open || void 0,
|
|
441
448
|
"data-disabled": disabled || void 0,
|
|
449
|
+
"data-invalid": isInvalid || void 0,
|
|
442
450
|
className: "morph-select-shape",
|
|
443
451
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
444
452
|
className: "absolute inset-x-0 top-0 z-10 h-(--ms-h)",
|
|
445
453
|
children: [
|
|
446
454
|
/* @__PURE__ */ jsx("button", {
|
|
447
455
|
ref: triggerRef,
|
|
456
|
+
id,
|
|
448
457
|
type: "button",
|
|
449
458
|
role: "combobox",
|
|
450
459
|
"data-slot": "morph-select-trigger",
|
|
451
|
-
"aria-labelledby": labelId,
|
|
460
|
+
"aria-labelledby": outsideLabel ? void 0 : labelId,
|
|
461
|
+
"aria-invalid": isInvalid || void 0,
|
|
462
|
+
"aria-describedby": showError ? errorId : void 0,
|
|
452
463
|
"aria-haspopup": "listbox",
|
|
453
464
|
"aria-expanded": open,
|
|
454
465
|
"aria-controls": listId,
|
|
@@ -517,6 +528,8 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
517
528
|
role: "combobox",
|
|
518
529
|
"data-slot": "morph-select-search",
|
|
519
530
|
"aria-label": `Search ${label.toLowerCase()}`,
|
|
531
|
+
"aria-invalid": isInvalid || void 0,
|
|
532
|
+
"aria-describedby": showError ? errorId : void 0,
|
|
520
533
|
"aria-expanded": open,
|
|
521
534
|
"aria-controls": listId,
|
|
522
535
|
"aria-autocomplete": "list",
|
|
@@ -673,6 +686,12 @@ function MorphSelect({ label, labelHidden = false, items, value, defaultValue =
|
|
|
673
686
|
})]
|
|
674
687
|
})]
|
|
675
688
|
}),
|
|
689
|
+
showError ? /* @__PURE__ */ jsx("span", {
|
|
690
|
+
id: errorId,
|
|
691
|
+
"data-slot": "morph-select-error",
|
|
692
|
+
className: "text-sm leading-(--leading-normal) text-destructive-strong",
|
|
693
|
+
children: errorMessage
|
|
694
|
+
}) : null,
|
|
676
695
|
name ? /* @__PURE__ */ jsx("input", {
|
|
677
696
|
type: "hidden",
|
|
678
697
|
name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"morph-select.js","names":[],"sources":["../../src/morph-select/morph-select.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport {\n prefersReducedMotion,\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n type Rect,\n} from \"../lib/motion.js\";\n\n/** One choice. */\nexport interface MorphSelectOption {\n value: string;\n label: string;\n /** A 20px mark shown before the label, in the list and in the trigger. An avatar or a plain icon. */\n icon?: ReactNode;\n /** Short trailing detail such as an offset or a count. Rendered with tabular numerals. */\n meta?: string;\n /** Extra words that match this option when searching. */\n keywords?: string;\n disabled?: boolean;\n}\n\n/** A labelled run of options. */\nexport interface MorphSelectGroup {\n label: string;\n options: MorphSelectOption[];\n}\n\nexport type MorphSelectItem = MorphSelectOption | MorphSelectGroup;\n\n/**\n * MorphSelect: a select whose trigger is the list. Opening grows the trigger itself\n * into the options surface (its width, height and corners spring from the closed\n * control to the open panel), a highlight glides between options, and the chosen\n * option's label lifts out of the list and settles into the trigger while the trigger\n * springs to its new width. Supports groups, type-ahead, search for long lists, and the\n * full listbox keyboard model: arrows, Home, End, PageUp, PageDown, Enter, Space,\n * Escape and Alt+ArrowUp.\n *\n * Use it for compact property pickers and form fields with up to a few hundred options.\n * Prefer a combobox when people mostly type free text, and Select when the list should\n * float over the page instead of growing from the trigger.\n *\n * It is its own listbox rather than a React Aria Select: a Select's list lives in a\n * popover, a separate surface that cannot be the trigger.\n */\nexport interface MorphSelectProps {\n /** Visible label, also the accessible name of the trigger and the list. */\n label: string;\n /** Keeps the label for assistive technology only. */\n labelHidden?: boolean;\n items: MorphSelectItem[];\n /** The chosen value. Controlled when given; `null` is nothing chosen. */\n value?: string | null;\n defaultValue?: string | null;\n onValueChange?: (value: string, option: MorphSelectOption) => void;\n /** Shown while nothing is chosen. @default \"Select\" */\n placeholder?: string;\n /** Shows a search field in place of the trigger when open. \"auto\" turns it on above eight options. @default \"auto\" */\n searchable?: boolean | \"auto\";\n searchPlaceholder?: string;\n /** Width of the open surface in px. It never gets narrower than the trigger. @default 272 */\n panelWidth?: number;\n /** Tallest the option list gets before it scrolls, in px. @default 296 */\n maxListHeight?: number;\n /** Which edge of the trigger stays put while the surface grows. @default \"start\" */\n align?: \"start\" | \"end\";\n /**\n * Control scale: the same heights as Select and Input.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Adds a hidden input for native form submission. */\n name?: string;\n disabled?: boolean;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\ninterface Section {\n key: string;\n label?: string;\n options: MorphSelectOption[];\n}\ninterface Change {\n value: string | null;\n dir: number;\n fly: { x: number; y: number } | null;\n}\n\nconst TYPEAHEAD_RESET = 600;\nconst PAGE = 8;\n\nconst isGroup = (item: MorphSelectItem): item is MorphSelectGroup => \"options\" in item;\n\n/** Loose options between groups gather into unlabelled sections, so the list keeps the order it was given. */\nfunction normalize(items: MorphSelectItem[]): Section[] {\n const out: Section[] = [];\n items.forEach((item, index) => {\n if (isGroup(item)) {\n out.push({ key: `group-${index}`, label: item.label, options: item.options });\n return;\n }\n const last = out[out.length - 1];\n if (last && !last.label) last.options.push(item);\n else out.push({ key: `loose-${index}`, options: [item] });\n });\n return out;\n}\n\nconst matches = (option: MorphSelectOption, needle: string) =>\n `${option.label} ${option.meta ?? \"\"} ${option.keywords ?? \"\"}`.toLowerCase().includes(needle);\n\n/** The closed trigger's width, rounded up. offsetWidth rounds to the nearest pixel, and half a pixel short is enough to ellipsize the label. */\nfunction triggerWidth(node: HTMLElement) {\n const exact = node.getBoundingClientRect().width;\n const rounded = node.offsetWidth;\n // Under a scaled ancestor the rect is scaled too, so fall back to the layout width with a pixel of slack.\n return Math.abs(exact - rounded) <= 0.5 ? Math.ceil(exact - 0.01) : rounded + 1;\n}\n\n/** The layout the closed trigger and its invisible measuring copy share, so the copy's width is the trigger's. */\nconst triggerLayout =\n \"box-border flex h-(--ms-h) items-center gap-2 ps-(--ms-pad) pe-10 font-medium whitespace-nowrap\";\n\nconst iconClass =\n \"grid size-5 flex-none place-items-center text-text-secondary [&>svg]:size-4 [&>img]:size-5 [&>img]:rounded-full [&>img]:object-cover\";\n\n/** The label a layer shows: the icon, then the text, or the placeholder. */\nfunction Value({ option, placeholder }: { option: MorphSelectOption | undefined; placeholder: string }) {\n if (!option) return <span className=\"font-normal text-muted-foreground\">{placeholder}</span>;\n return (\n <>\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </>\n );\n}\n\n/** A replaced label on its way out, popped out of flow where it stood and lifting away. */\nfunction LeavingLayer({\n id,\n rect,\n dir,\n fly,\n onDone,\n children,\n}: {\n id: number;\n rect: Rect;\n dir: number;\n fly: boolean;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n data-leaving=\"\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n // field-swap-out lifts by 0.3em times --dir: a label that flew to the trigger lifts 0.3em, one that rolled lifts 0.45em the way the list ran.\n style={{ ...rect, \"--dir\": fly ? 1 : dir * 1.5 } as CSSProperties}\n className=\"absolute flex min-w-0 items-center gap-2 motion-safe:animate-field-swap-out motion-reduce:animate-fade-out\"\n >\n {children}\n </span>\n );\n}\n\nexport function MorphSelect({\n label,\n labelHidden = false,\n items,\n value,\n defaultValue = null,\n onValueChange,\n placeholder = \"Select\",\n searchable = \"auto\",\n searchPlaceholder,\n panelWidth = 272,\n maxListHeight = 296,\n align = \"start\",\n size = \"md\",\n name,\n disabled = false,\n className,\n}: MorphSelectProps) {\n const uid = useId();\n const labelId = `${uid}-label`;\n const listId = `${uid}-list`;\n const optionId = (index: number) => `${uid}-option-${index}`;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const shapeRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const faceRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const slotRef = useRef<HTMLSpanElement>(null);\n const layerRef = useRef<HTMLSpanElement>(null);\n const highlightRef = useRef<HTMLSpanElement>(null);\n const searchRowRef = useRef<HTMLDivElement>(null);\n const optionRefs = useRef(new Map<string, HTMLElement>());\n\n const sections = useMemo(() => normalize(items), [items]);\n const all = useMemo(() => sections.flatMap((section) => section.options), [sections]);\n const indexOf = useMemo(() => new Map(all.map((option, index) => [option.value, index])), [all]);\n const canSearch = searchable === \"auto\" ? all.length > 8 : searchable;\n\n const [inner, setInner] = useState<string | null>(defaultValue);\n const selected = value !== undefined ? value : inner;\n const selectedOption = selected === null ? undefined : all.find((option) => option.value === selected);\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [active, setActive] = useState<string | null>(null);\n const [change, setChange] = useState<Change>({ value: null, dir: 1, fly: null });\n\n const needle = query.trim().toLowerCase();\n const visibleSections = useMemo(\n () =>\n needle\n ? sections\n .map((section) => ({\n ...section,\n options: section.options.filter((option) => matches(option, needle)),\n }))\n .filter((section) => section.options.length)\n : sections,\n [needle, sections],\n );\n const enabled = useMemo(\n () => visibleSections.flatMap((section) => section.options).filter((option) => !option.disabled),\n [visibleSections],\n );\n const current =\n active !== null && enabled.some((option) => option.value === active)\n ? active\n : (enabled[0]?.value ?? null);\n const resultCount = visibleSections.reduce((sum, section) => sum + section.options.length, 0);\n\n /* ---------------------------------------------- The shape ---------------------------------------------- */\n\n // One surface whose width and height follow the trigger and the open list. The sizes are written straight to the\n // two boxes, and the transitions in CSS do the moving, so a new label never waits on a render. Nothing animates\n // until the first measurement has landed (data-ready).\n const sizes = useRef({ trigger: 0, lid: 0, listW: 0, listH: 0 });\n const live = useRef({ open: false, anchorW: 0 });\n\n const place = useCallback(() => {\n const { trigger, lid, listW, listH } = sizes.current;\n const shape = shapeRef.current;\n const anchor = anchorRef.current;\n if (!trigger || !lid || !shape || !anchor) return;\n const isOpen = live.current.open;\n // A trigger that widens springs; one that narrows never overshoots, so it never dips under its label.\n if (trigger !== live.current.anchorW) {\n anchor.toggleAttribute(\"data-widening\", trigger > live.current.anchorW);\n live.current.anchorW = trigger;\n }\n anchor.style.width = `${trigger}px`;\n shape.style.width = `${isOpen ? Math.max(trigger, listW) : trigger}px`;\n shape.style.height = `${isOpen ? lid + listH : lid}px`;\n }, []);\n\n const read = useCallback(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n // The list is never narrower than the trigger; the width goes to CSS directly so a new label never waits on a render.\n const trigger = triggerWidth(measure);\n root.style.setProperty(\"--ms-trigger-w\", `${trigger}px`);\n sizes.current = {\n trigger,\n lid: measure.offsetHeight,\n listW: face.offsetWidth,\n listH: face.offsetHeight,\n };\n place();\n }, [place]);\n\n useIsomorphicLayoutEffect(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n read();\n // The first sizes are in place: from here on a change of size moves.\n void shapeRef.current?.offsetWidth;\n root.setAttribute(\"data-ready\", \"\");\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(measure);\n observer.observe(face);\n return () => observer.disconnect();\n }, [read]);\n\n useIsomorphicLayoutEffect(() => {\n if (live.current.open === open) return;\n live.current.open = open;\n // A pick changes the label and closes in the same commit, so both faces are read fresh before the shape moves.\n read();\n }, [open, read]);\n\n /* ---------------------------------------------- The highlight ---------------------------------------------- */\n\n const scrollIntent = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const highlight = highlightRef.current;\n if (!highlight) return;\n const node = open && current !== null ? optionRefs.current.get(current) : undefined;\n if (!node) {\n highlight.removeAttribute(\"data-on\");\n return;\n }\n const top = node.offsetTop;\n const height = node.offsetHeight;\n // It appears where it is needed, and glides from there.\n highlight.toggleAttribute(\"data-jump\", !highlight.hasAttribute(\"data-on\") || prefersReducedMotion());\n highlight.style.translate = `0 ${top}px`;\n highlight.style.height = `${height}px`;\n highlight.setAttribute(\"data-on\", \"\");\n const scroller = scrollRef.current;\n if (scroller && scrollIntent.current) {\n scrollIntent.current = false;\n const pad = 6;\n if (top < scroller.scrollTop + pad) scroller.scrollTop = top - pad;\n else if (top + height > scroller.scrollTop + scroller.clientHeight - pad)\n scroller.scrollTop = top + height - scroller.clientHeight + pad;\n }\n }, [current, open, visibleSections]);\n\n /* ---------------------------------------------- Focus and keys ---------------------------------------------- */\n\n const showSearch = open && canSearch;\n // Written as attributes, since React 18 and 19 disagree about the `inert` prop and both agree about the attribute: the\n // closed list and the idle search row take no focus and are hidden from assistive technology; a search row in use replaces the trigger.\n useIsomorphicLayoutEffect(() => {\n triggerRef.current?.toggleAttribute(\"inert\", showSearch);\n searchRowRef.current?.toggleAttribute(\"inert\", !showSearch);\n const face = faceRef.current;\n if (!face) return;\n face.toggleAttribute(\"inert\", !open);\n if (open) face.removeAttribute(\"aria-hidden\");\n else face.setAttribute(\"aria-hidden\", \"true\");\n }, [open, showSearch]);\n\n // Focus lands in the lid: the search field when there is one, otherwise the trigger that keeps the active option.\n const pendingFocus = useRef<\"trigger\" | \"input\" | null>(null);\n useIsomorphicLayoutEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"input\") inputRef.current?.focus({ preventScroll: true });\n if (target === \"trigger\") triggerRef.current?.focus({ preventScroll: true });\n }, [open]);\n\n const typeahead = useRef({ buffer: \"\", at: 0 });\n /** Space opens and picks on keydown; the click a button fires on the matching keyup must not undo it. */\n const suppressClick = useRef(false);\n const [announcement, setAnnouncement] = useState(\"\");\n\n function openList(focus?: string | null, seed = \"\") {\n if (disabled || open) return;\n setQuery(seed);\n setActive(focus !== undefined ? focus : selected);\n scrollIntent.current = true;\n pendingFocus.current = canSearch ? \"input\" : \"trigger\";\n setAnnouncement(\"\");\n setOpen(true);\n }\n\n const close = useCallback((restoreFocus: boolean) => {\n typeahead.current.buffer = \"\";\n if (restoreFocus) pendingFocus.current = \"trigger\";\n setOpen(false);\n }, []);\n\n function commit(option: MorphSelectOption | undefined, source?: HTMLElement | null) {\n if (!option || option.disabled) return;\n if (option.value !== selected) {\n // The chosen label starts where it stood in the list and settles into the trigger.\n let fly: Change[\"fly\"] = null;\n const slot = slotRef.current;\n const part = source?.querySelector<HTMLElement>(\"[data-part='value']\");\n if (!prefersReducedMotion() && part && slot) {\n const from = part.getBoundingClientRect();\n const to = slot.getBoundingClientRect();\n fly = { x: from.left - to.left, y: from.top + from.height / 2 - (to.top + to.height / 2) };\n }\n const from = selected === null ? -1 : (indexOf.get(selected) ?? -1);\n const to = indexOf.get(option.value) ?? 0;\n setChange({ value: option.value, dir: from < 0 ? 1 : Math.sign(to - from) || 1, fly });\n if (value === undefined) setInner(option.value);\n onValueChange?.(option.value, option);\n }\n close(true);\n }\n\n function typeTo(char: string) {\n const now = performance.now();\n const state = typeahead.current;\n state.buffer = now - state.at > TYPEAHEAD_RESET ? char : state.buffer + char;\n state.at = now;\n const buffer = state.buffer.toLowerCase();\n const repeated = buffer.split(\"\").every((letter) => letter === buffer[0]);\n const search = repeated ? buffer[0]! : buffer;\n const pool = all.filter((option) => !option.disabled);\n const from = open ? current : selected;\n const at = pool.findIndex((option) => option.value === from);\n const offset = repeated || buffer.length === 1 ? 1 : 0;\n const ordered = [...pool.slice(at + offset), ...pool.slice(0, at + offset)];\n return ordered.find((option) => option.label.toLowerCase().startsWith(search));\n }\n\n function move(to: string | null | undefined) {\n if (to === undefined || to === null) return;\n scrollIntent.current = true;\n setActive(to);\n }\n\n function onKey(event: KeyboardEvent<HTMLElement>, fromInput: boolean) {\n const { key } = event;\n if (key === \" \" && !fromInput) suppressClick.current = true;\n const printable = key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;\n const typing =\n performance.now() - typeahead.current.at < TYPEAHEAD_RESET && typeahead.current.buffer.length > 0;\n\n if (!open) {\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Enter\" || (key === \" \" && !typing)) {\n event.preventDefault();\n openList();\n return;\n }\n if (key === \"Home\" || key === \"End\") {\n event.preventDefault();\n const pool = all.filter((option) => !option.disabled);\n openList((key === \"Home\" ? pool[0] : pool[pool.length - 1])?.value ?? null);\n return;\n }\n if (printable) {\n event.preventDefault();\n if (canSearch) openList(undefined, key);\n else {\n const hit = typeTo(key);\n openList(hit?.value ?? selected);\n }\n }\n return;\n }\n\n const at = enabled.findIndex((option) => option.value === current);\n const caretKeys = fromInput && query.length > 0;\n const pick = () => commit(enabled[at], optionRefs.current.get(current ?? \"\"));\n switch (key) {\n case \"ArrowDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + 1)]?.value);\n return;\n case \"ArrowUp\":\n event.preventDefault();\n if (event.altKey) {\n pick();\n return;\n }\n move(enabled[Math.max(0, at - 1)]?.value);\n return;\n case \"PageDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + PAGE)]?.value);\n return;\n case \"PageUp\":\n event.preventDefault();\n move(enabled[Math.max(0, at - PAGE)]?.value);\n return;\n case \"Home\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[0]?.value);\n return;\n case \"End\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[enabled.length - 1]?.value);\n return;\n case \"Enter\":\n event.preventDefault();\n pick();\n return;\n case \"Escape\":\n event.preventDefault();\n event.stopPropagation();\n close(true);\n return;\n case \"Tab\":\n close(false);\n return;\n }\n if (fromInput) return;\n if (key === \" \" && !typing) {\n event.preventDefault();\n pick();\n return;\n }\n if (printable) {\n event.preventDefault();\n move(typeTo(key)?.value);\n }\n }\n\n // A press outside or focus leaving the control folds the list back into the trigger.\n useEffect(() => {\n if (!open) return;\n const down = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) close(false);\n };\n document.addEventListener(\"pointerdown\", down);\n return () => document.removeEventListener(\"pointerdown\", down);\n }, [close, open]);\n function onBlur(event: FocusEvent<HTMLDivElement>) {\n const next = event.relatedTarget as Node | null;\n if (open && next && !event.currentTarget.contains(next)) close(false);\n }\n\n function onQuery(next: string) {\n setQuery(next);\n setActive(null);\n scrollIntent.current = true;\n if (scrollRef.current) scrollRef.current.scrollTop = 0;\n const text = next.trim().toLowerCase();\n const count = text ? all.filter((option) => matches(option, text)).length : all.length;\n setAnnouncement(text ? (count ? `${count} ${count === 1 ? \"option\" : \"options\"}` : \"No matches\") : \"\");\n }\n\n /* ---------------------------------------------- The chosen label ---------------------------------------------- */\n\n // The trigger's value is a layer: a new one flies in from the list (or rolls, when it cannot) while the old one lifts away.\n const layerKey = selected ?? \"__empty\";\n const flying = change.value === selected ? change.fly : null;\n const { leaving, changed, remove } = useLeavingItems(\n layerKey,\n { key: layerKey, node: <Value option={selectedOption} placeholder={placeholder} />, dir: change.dir, fly: Boolean(flying) },\n () => layerRef.current,\n );\n\n const rootStyle = {\n \"--ms-panel-w\": `${panelWidth}px`,\n \"--ms-list-max\": `${maxListHeight}px`,\n } as CSSProperties;\n const layerStyle = {\n \"--dir\": change.dir,\n ...(flying ? { \"--fly-x\": `${flying.x}px`, \"--fly-y\": `${flying.y}px` } : {}),\n } as CSSProperties;\n\n return (\n <div\n ref={rootRef}\n data-slot=\"morph-select\"\n data-open={open || undefined}\n data-align={align}\n data-size={size}\n data-disabled={disabled || undefined}\n style={rootStyle}\n onBlur={onBlur}\n className={cn(\n \"morph-select-root group/ms relative inline-grid min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base\",\n className,\n )}\n >\n <span id={labelId} data-slot=\"morph-select-label\" className={cnAll(labelVariants(), labelHidden && \"sr-only\")}>\n {label}\n </span>\n\n <div ref={anchorRef} className=\"morph-select-anchor\">\n {/* Sizes the closed trigger. It holds the chosen label with the trigger's own padding, so the width has a target before anything moves. */}\n <span ref={measureRef} aria-hidden=\"true\" className={cnAll(triggerLayout, \"morph-select-measure\")}>\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n\n <div\n ref={shapeRef}\n data-slot=\"morph-select-shape\"\n data-open={open || undefined}\n data-disabled={disabled || undefined}\n className=\"morph-select-shape\"\n >\n {/* The lid is the trigger row. It stays at the top of the surface in both states and sits above the list, so a lifted label passes over it. */}\n <div className=\"absolute inset-x-0 top-0 z-10 h-(--ms-h)\">\n <button\n ref={triggerRef}\n type=\"button\"\n role=\"combobox\"\n data-slot=\"morph-select-trigger\"\n aria-labelledby={labelId}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listId}\n aria-activedescendant={\n open && !canSearch && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n disabled={disabled}\n tabIndex={showSearch ? -1 : 0}\n onClick={() => {\n if (suppressClick.current) {\n suppressClick.current = false;\n return;\n }\n if (open) close(true);\n else openList();\n }}\n onKeyDown={(event) => onKey(event, false)}\n onKeyUp={(event) => {\n if (event.key === \" \")\n window.setTimeout(() => {\n suppressClick.current = false;\n });\n }}\n className={cnAll(\n triggerLayout,\n \"absolute inset-0 w-full cursor-pointer border-0 bg-transparent text-start text-foreground outline-none [font-family:inherit] [font-size:inherit] [line-height:inherit] [letter-spacing:inherit] [-webkit-tap-highlight-color:transparent] [touch-action:manipulation] disabled:cursor-not-allowed\",\n )}\n >\n <span\n ref={slotRef}\n data-hidden={showSearch || undefined}\n className=\"relative block h-full min-w-0 flex-1 data-[hidden]:opacity-0 motion-safe:transition-opacity motion-safe:duration-(--duration-instant) motion-safe:ease-settle\"\n >\n {leaving.map((item) => (\n <LeavingLayer\n key={item.id}\n id={item.id}\n rect={item.rect}\n dir={item.value.dir}\n fly={item.value.fly}\n onDone={remove}\n >\n {item.value.node}\n </LeavingLayer>\n ))}\n <span\n key={layerKey}\n ref={layerRef}\n style={layerStyle}\n className={cnAll(\n \"absolute inset-0 flex min-w-0 items-center gap-2\",\n changed &&\n (flying\n ? \"motion-safe:animate-morph-select-fly motion-reduce:animate-fade-in\"\n : \"motion-safe:animate-select-value-in motion-reduce:animate-fade-in\"),\n )}\n >\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n </span>\n </button>\n\n {canSearch ? (\n <div\n ref={searchRowRef}\n className={cnAll(\n \"absolute inset-0 flex items-center gap-2 ps-(--ms-pad) pe-1.5 motion-safe:transition-opacity motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n showSearch ? \"opacity-100\" : \"pointer-events-none opacity-0\",\n )}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.5 10.5 3 3\" />\n </svg>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n data-slot=\"morph-select-search\"\n aria-label={`Search ${label.toLowerCase()}`}\n aria-expanded={open}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n open && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n placeholder={searchPlaceholder ?? selectedOption?.label ?? \"Search\"}\n value={query}\n autoComplete=\"off\"\n spellCheck={false}\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => onKey(event, true)}\n className=\"h-full min-w-0 flex-1 border-0 bg-transparent p-0 [font:inherit] text-foreground outline-none placeholder:text-muted-foreground\"\n />\n {query ? (\n <button\n type=\"button\"\n aria-label=\"Clear search\"\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onQuery(\"\");\n inputRef.current?.focus();\n }}\n className=\"grid size-6 flex-none cursor-pointer place-items-center rounded-pill border-0 bg-muted p-0 text-text-secondary outline-none motion-safe:animate-field-clear-in motion-reduce:animate-fade-in hover:text-foreground\"\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </button>\n ) : null}\n {/* Sits exactly over the shared chevron, so the chevron closes the list in both lid states. */}\n <button\n type=\"button\"\n aria-label={`Close ${label.toLowerCase()}`}\n onClick={() => close(true)}\n className=\"size-9 flex-none cursor-pointer rounded-pill border-0 bg-transparent p-0 outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle hover:bg-muted\"\n />\n </div>\n ) : null}\n\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"pointer-events-none absolute end-4 top-1/2 -mt-2 size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-data-[open]/ms:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </div>\n\n <div\n ref={faceRef}\n data-open={open || undefined}\n className=\"morph-select-face\"\n >\n <div\n ref={scrollRef}\n className=\"max-h-(--ms-list-max) overflow-y-auto overscroll-contain pt-1.5 [scrollbar-width:thin]\"\n >\n <div id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"relative grid grid-cols-1 gap-0.5\">\n <span ref={highlightRef} aria-hidden=\"true\" className=\"morph-select-highlight\" />\n {visibleSections.map((section) => {\n const rows = section.options.map((option) => {\n const index = indexOf.get(option.value) ?? 0;\n const isSelected = option.value === selected;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n data-slot=\"morph-select-option\"\n data-active={option.value === current || undefined}\n ref={(node) => {\n if (node) optionRefs.current.set(option.value, node);\n else optionRefs.current.delete(option.value);\n }}\n onPointerMove={(event) => {\n if (event.pointerType === \"mouse\" && !option.disabled && option.value !== current)\n setActive(option.value);\n }}\n onPointerDown={(event) => event.preventDefault()}\n onClick={(event) => commit(option, event.currentTarget)}\n className=\"morph-select-option relative box-border flex min-h-9 cursor-pointer select-none items-center gap-2 rounded-lg text-text-secondary outline-none data-[active]:text-foreground aria-selected:text-foreground aria-disabled:cursor-not-allowed aria-disabled:opacity-(--opacity-disabled) motion-safe:transition-colors motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <span data-part=\"value\" className=\"flex min-w-0 flex-1 items-center gap-2 font-medium\">\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </span>\n {option.meta ? (\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n {option.meta}\n </span>\n ) : null}\n <span\n aria-hidden=\"true\"\n data-on={isSelected || undefined}\n className=\"grid size-4 flex-none scale-60 place-items-center text-foreground opacity-0 data-[on]:scale-100 data-[on]:opacity-100 motion-safe:transition-[opacity,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </span>\n </div>\n );\n });\n return section.label ? (\n <div\n key={section.key}\n role=\"group\"\n aria-labelledby={`${uid}-${section.key}`}\n className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\"\n >\n <div\n id={`${uid}-${section.key}`}\n role=\"presentation\"\n className=\"px-2.5 pt-2 pb-1 text-xs text-muted-foreground\"\n >\n {section.label}\n </div>\n {rows}\n </div>\n ) : (\n <div key={section.key} role=\"presentation\" className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\">\n {rows}\n </div>\n );\n })}\n {resultCount === 0 ? (\n <p className=\"m-0 px-2.5 pt-3 pb-3.5 text-sm text-muted-foreground\">\n No matches for “{query.trim()}”\n </p>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {name ? <input type=\"hidden\" name={name} value={selected ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"morph-select.js","names":[],"sources":["../../src/morph-select/morph-select.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { labelVariants } from \"../label/label.js\";\nimport { cn, cnAll } from \"../lib/cn.js\";\nimport {\n prefersReducedMotion,\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n type Rect,\n} from \"../lib/motion.js\";\n\n/** One choice. */\nexport interface MorphSelectOption {\n value: string;\n label: string;\n /** A 20px mark shown before the label, in the list and in the trigger. An avatar or a plain icon. */\n icon?: ReactNode;\n /** Short trailing detail such as an offset or a count. Rendered with tabular numerals. */\n meta?: string;\n /** Extra words that match this option when searching. */\n keywords?: string;\n disabled?: boolean;\n}\n\n/** A labelled run of options. */\nexport interface MorphSelectGroup {\n label: string;\n options: MorphSelectOption[];\n}\n\nexport type MorphSelectItem = MorphSelectOption | MorphSelectGroup;\n\n/**\n * MorphSelect: a select whose trigger is the list. Opening grows the trigger itself\n * into the options surface (its width, height and corners spring from the closed\n * control to the open panel), a highlight glides between options, and the chosen\n * option's label lifts out of the list and settles into the trigger while the trigger\n * springs to its new width. Supports groups, type-ahead, search for long lists, and the\n * full listbox keyboard model: arrows, Home, End, PageUp, PageDown, Enter, Space,\n * Escape and Alt+ArrowUp.\n *\n * Use it for compact property pickers and form fields with up to a few hundred options.\n * Prefer a combobox when people mostly type free text, and Select when the list should\n * float over the page instead of growing from the trigger.\n *\n * It is its own listbox rather than a React Aria Select: a Select's list lives in a\n * popover, a separate surface that cannot be the trigger.\n */\nexport interface MorphSelectProps {\n /** Visible label, also the accessible name of the trigger and the list. */\n label: string;\n /** Keeps the label for assistive technology only. */\n labelHidden?: boolean;\n items: MorphSelectItem[];\n /** The chosen value. Controlled when given; `null` is nothing chosen. */\n value?: string | null;\n defaultValue?: string | null;\n onValueChange?: (value: string, option: MorphSelectOption) => void;\n /** Shown while nothing is chosen. @default \"Select\" */\n placeholder?: string;\n /** Shows a search field in place of the trigger when open. \"auto\" turns it on above eight options. @default \"auto\" */\n searchable?: boolean | \"auto\";\n searchPlaceholder?: string;\n /** Width of the open surface in px. It never gets narrower than the trigger. @default 272 */\n panelWidth?: number;\n /** Tallest the option list gets before it scrolls, in px. @default 296 */\n maxListHeight?: number;\n /** Which edge of the trigger stays put while the surface grows. @default \"start\" */\n align?: \"start\" | \"end\";\n /**\n * Control scale: the same heights as Select and Input.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Adds a hidden input for native form submission. */\n name?: string;\n disabled?: boolean;\n /**\n * The trigger's `id`, so a `<label htmlFor>` outside the select (or a form\n * library that focuses a field by id) reaches the real control. When such a\n * label exists it NAMES the trigger: the trigger then drops its\n * `aria-labelledby` to the inner `label`, which would otherwise outrank the\n * outside label in the accessible-name computation.\n */\n id?: string;\n /**\n * Marks the choice as wrong: the trigger takes the invalid border and is\n * `aria-invalid`. Pair it with `errorMessage`, or the state is shown visually in colour only.\n */\n isInvalid?: boolean;\n /**\n * Why the choice is wrong, under the control. It shows only while `isInvalid`\n * is set, and is named in the trigger's and the search field's `aria-describedby`.\n */\n errorMessage?: ReactNode;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\ninterface Section {\n key: string;\n label?: string;\n options: MorphSelectOption[];\n}\ninterface Change {\n value: string | null;\n dir: number;\n fly: { x: number; y: number } | null;\n}\n\nconst TYPEAHEAD_RESET = 600;\nconst PAGE = 8;\n\nconst isGroup = (item: MorphSelectItem): item is MorphSelectGroup => \"options\" in item;\n\n/** Loose options between groups gather into unlabelled sections, so the list keeps the order it was given. */\nfunction normalize(items: MorphSelectItem[]): Section[] {\n const out: Section[] = [];\n items.forEach((item, index) => {\n if (isGroup(item)) {\n out.push({ key: `group-${index}`, label: item.label, options: item.options });\n return;\n }\n const last = out[out.length - 1];\n if (last && !last.label) last.options.push(item);\n else out.push({ key: `loose-${index}`, options: [item] });\n });\n return out;\n}\n\nconst matches = (option: MorphSelectOption, needle: string) =>\n `${option.label} ${option.meta ?? \"\"} ${option.keywords ?? \"\"}`.toLowerCase().includes(needle);\n\n/** The closed trigger's width, rounded up. offsetWidth rounds to the nearest pixel, and half a pixel short is enough to ellipsize the label. */\nfunction triggerWidth(node: HTMLElement) {\n const exact = node.getBoundingClientRect().width;\n const rounded = node.offsetWidth;\n // Under a scaled ancestor the rect is scaled too, so fall back to the layout width with a pixel of slack.\n return Math.abs(exact - rounded) <= 0.5 ? Math.ceil(exact - 0.01) : rounded + 1;\n}\n\n/** The layout the closed trigger and its invisible measuring copy share, so the copy's width is the trigger's. */\nconst triggerLayout =\n \"box-border flex h-(--ms-h) items-center gap-2 ps-(--ms-pad) pe-10 font-medium whitespace-nowrap\";\n\nconst iconClass =\n \"grid size-5 flex-none place-items-center text-text-secondary [&>svg]:size-4 [&>img]:size-5 [&>img]:rounded-full [&>img]:object-cover\";\n\n/** The label a layer shows: the icon, then the text, or the placeholder. */\nfunction Value({ option, placeholder }: { option: MorphSelectOption | undefined; placeholder: string }) {\n if (!option) return <span className=\"font-normal text-muted-foreground\">{placeholder}</span>;\n return (\n <>\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </>\n );\n}\n\n/** A replaced label on its way out, popped out of flow where it stood and lifting away. */\nfunction LeavingLayer({\n id,\n rect,\n dir,\n fly,\n onDone,\n children,\n}: {\n id: number;\n rect: Rect;\n dir: number;\n fly: boolean;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n data-leaving=\"\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n // field-swap-out lifts by 0.3em times --dir: a label that flew to the trigger lifts 0.3em, one that rolled lifts 0.45em the way the list ran.\n style={{ ...rect, \"--dir\": fly ? 1 : dir * 1.5 } as CSSProperties}\n className=\"absolute flex min-w-0 items-center gap-2 motion-safe:animate-field-swap-out motion-reduce:animate-fade-out\"\n >\n {children}\n </span>\n );\n}\n\nexport function MorphSelect({\n label,\n labelHidden = false,\n items,\n value,\n defaultValue = null,\n onValueChange,\n placeholder = \"Select\",\n searchable = \"auto\",\n searchPlaceholder,\n panelWidth = 272,\n maxListHeight = 296,\n align = \"start\",\n size = \"md\",\n name,\n disabled = false,\n id,\n isInvalid = false,\n errorMessage,\n className,\n}: MorphSelectProps) {\n const uid = useId();\n const labelId = `${uid}-label`;\n const errorId = `${uid}-error`;\n // As on Select: the message shows only while the field is invalid.\n const showError = isInvalid && errorMessage != null;\n const listId = `${uid}-list`;\n const optionId = (index: number) => `${uid}-option-${index}`;\n\n const rootRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const shapeRef = useRef<HTMLDivElement>(null);\n const measureRef = useRef<HTMLSpanElement>(null);\n const faceRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n // An outside <label htmlFor={id}> names the trigger natively. aria-labelledby\n // outranks it, so while one exists the trigger does not point at the inner label.\n const [outsideLabel, setOutsideLabel] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const slotRef = useRef<HTMLSpanElement>(null);\n const layerRef = useRef<HTMLSpanElement>(null);\n const highlightRef = useRef<HTMLSpanElement>(null);\n const searchRowRef = useRef<HTMLDivElement>(null);\n const optionRefs = useRef(new Map<string, HTMLElement>());\n\n const sections = useMemo(() => normalize(items), [items]);\n const all = useMemo(() => sections.flatMap((section) => section.options), [sections]);\n const indexOf = useMemo(() => new Map(all.map((option, index) => [option.value, index])), [all]);\n const canSearch = searchable === \"auto\" ? all.length > 8 : searchable;\n\n const [inner, setInner] = useState<string | null>(defaultValue);\n const selected = value !== undefined ? value : inner;\n const selectedOption = selected === null ? undefined : all.find((option) => option.value === selected);\n\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [active, setActive] = useState<string | null>(null);\n const [change, setChange] = useState<Change>({ value: null, dir: 1, fly: null });\n\n const needle = query.trim().toLowerCase();\n const visibleSections = useMemo(\n () =>\n needle\n ? sections\n .map((section) => ({\n ...section,\n options: section.options.filter((option) => matches(option, needle)),\n }))\n .filter((section) => section.options.length)\n : sections,\n [needle, sections],\n );\n const enabled = useMemo(\n () => visibleSections.flatMap((section) => section.options).filter((option) => !option.disabled),\n [visibleSections],\n );\n const current =\n active !== null && enabled.some((option) => option.value === active)\n ? active\n : (enabled[0]?.value ?? null);\n const resultCount = visibleSections.reduce((sum, section) => sum + section.options.length, 0);\n\n /* ---------------------------------------------- The shape ---------------------------------------------- */\n\n // One surface whose width and height follow the trigger and the open list. The sizes are written straight to the\n // two boxes, and the transitions in CSS do the moving, so a new label never waits on a render. Nothing animates\n // until the first measurement has landed (data-ready).\n const sizes = useRef({ trigger: 0, lid: 0, listW: 0, listH: 0 });\n const live = useRef({ open: false, anchorW: 0 });\n\n const place = useCallback(() => {\n const { trigger, lid, listW, listH } = sizes.current;\n const shape = shapeRef.current;\n const anchor = anchorRef.current;\n if (!trigger || !lid || !shape || !anchor) return;\n const isOpen = live.current.open;\n // A trigger that widens springs; one that narrows never overshoots, so it never dips under its label.\n if (trigger !== live.current.anchorW) {\n anchor.toggleAttribute(\"data-widening\", trigger > live.current.anchorW);\n live.current.anchorW = trigger;\n }\n anchor.style.width = `${trigger}px`;\n shape.style.width = `${isOpen ? Math.max(trigger, listW) : trigger}px`;\n shape.style.height = `${isOpen ? lid + listH : lid}px`;\n }, []);\n\n const read = useCallback(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n // The list is never narrower than the trigger; the width goes to CSS directly so a new label never waits on a render.\n const trigger = triggerWidth(measure);\n root.style.setProperty(\"--ms-trigger-w\", `${trigger}px`);\n sizes.current = {\n trigger,\n lid: measure.offsetHeight,\n listW: face.offsetWidth,\n listH: face.offsetHeight,\n };\n place();\n }, [place]);\n\n useIsomorphicLayoutEffect(() => {\n const measure = measureRef.current;\n const face = faceRef.current;\n const root = rootRef.current;\n if (!measure || !face || !root) return;\n read();\n // The first sizes are in place: from here on a change of size moves.\n void shapeRef.current?.offsetWidth;\n root.setAttribute(\"data-ready\", \"\");\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(read);\n observer.observe(measure);\n observer.observe(face);\n return () => observer.disconnect();\n }, [read]);\n\n useIsomorphicLayoutEffect(() => {\n if (live.current.open === open) return;\n live.current.open = open;\n // A pick changes the label and closes in the same commit, so both faces are read fresh before the shape moves.\n read();\n }, [open, read]);\n\n /* ---------------------------------------------- The highlight ---------------------------------------------- */\n\n const scrollIntent = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const highlight = highlightRef.current;\n if (!highlight) return;\n const node = open && current !== null ? optionRefs.current.get(current) : undefined;\n if (!node) {\n highlight.removeAttribute(\"data-on\");\n return;\n }\n const top = node.offsetTop;\n const height = node.offsetHeight;\n // It appears where it is needed, and glides from there.\n highlight.toggleAttribute(\"data-jump\", !highlight.hasAttribute(\"data-on\") || prefersReducedMotion());\n highlight.style.translate = `0 ${top}px`;\n highlight.style.height = `${height}px`;\n highlight.setAttribute(\"data-on\", \"\");\n const scroller = scrollRef.current;\n if (scroller && scrollIntent.current) {\n scrollIntent.current = false;\n const pad = 6;\n if (top < scroller.scrollTop + pad) scroller.scrollTop = top - pad;\n else if (top + height > scroller.scrollTop + scroller.clientHeight - pad)\n scroller.scrollTop = top + height - scroller.clientHeight + pad;\n }\n }, [current, open, visibleSections]);\n\n /* ---------------------------------------------- Focus and keys ---------------------------------------------- */\n\n useIsomorphicLayoutEffect(() => {\n setOutsideLabel(id != null && (triggerRef.current?.labels?.length ?? 0) > 0);\n }, [id]);\n\n const showSearch = open && canSearch;\n // Written as attributes, since React 18 and 19 disagree about the `inert` prop and both agree about the attribute: the\n // closed list and the idle search row take no focus and are hidden from assistive technology; a search row in use replaces the trigger.\n useIsomorphicLayoutEffect(() => {\n triggerRef.current?.toggleAttribute(\"inert\", showSearch);\n searchRowRef.current?.toggleAttribute(\"inert\", !showSearch);\n const face = faceRef.current;\n if (!face) return;\n face.toggleAttribute(\"inert\", !open);\n if (open) face.removeAttribute(\"aria-hidden\");\n else face.setAttribute(\"aria-hidden\", \"true\");\n }, [open, showSearch]);\n\n // Focus lands in the lid: the search field when there is one, otherwise the trigger that keeps the active option.\n const pendingFocus = useRef<\"trigger\" | \"input\" | null>(null);\n useIsomorphicLayoutEffect(() => {\n const target = pendingFocus.current;\n pendingFocus.current = null;\n if (target === \"input\") inputRef.current?.focus({ preventScroll: true });\n if (target === \"trigger\") triggerRef.current?.focus({ preventScroll: true });\n }, [open]);\n\n const typeahead = useRef({ buffer: \"\", at: 0 });\n /** Space opens and picks on keydown; the click a button fires on the matching keyup must not undo it. */\n const suppressClick = useRef(false);\n const [announcement, setAnnouncement] = useState(\"\");\n\n function openList(focus?: string | null, seed = \"\") {\n if (disabled || open) return;\n setQuery(seed);\n setActive(focus !== undefined ? focus : selected);\n scrollIntent.current = true;\n pendingFocus.current = canSearch ? \"input\" : \"trigger\";\n setAnnouncement(\"\");\n setOpen(true);\n }\n\n const close = useCallback((restoreFocus: boolean) => {\n typeahead.current.buffer = \"\";\n if (restoreFocus) pendingFocus.current = \"trigger\";\n setOpen(false);\n }, []);\n\n function commit(option: MorphSelectOption | undefined, source?: HTMLElement | null) {\n if (!option || option.disabled) return;\n if (option.value !== selected) {\n // The chosen label starts where it stood in the list and settles into the trigger.\n let fly: Change[\"fly\"] = null;\n const slot = slotRef.current;\n const part = source?.querySelector<HTMLElement>(\"[data-part='value']\");\n if (!prefersReducedMotion() && part && slot) {\n const from = part.getBoundingClientRect();\n const to = slot.getBoundingClientRect();\n fly = { x: from.left - to.left, y: from.top + from.height / 2 - (to.top + to.height / 2) };\n }\n const from = selected === null ? -1 : (indexOf.get(selected) ?? -1);\n const to = indexOf.get(option.value) ?? 0;\n setChange({ value: option.value, dir: from < 0 ? 1 : Math.sign(to - from) || 1, fly });\n if (value === undefined) setInner(option.value);\n onValueChange?.(option.value, option);\n }\n close(true);\n }\n\n function typeTo(char: string) {\n const now = performance.now();\n const state = typeahead.current;\n state.buffer = now - state.at > TYPEAHEAD_RESET ? char : state.buffer + char;\n state.at = now;\n const buffer = state.buffer.toLowerCase();\n const repeated = buffer.split(\"\").every((letter) => letter === buffer[0]);\n const search = repeated ? buffer[0]! : buffer;\n const pool = all.filter((option) => !option.disabled);\n const from = open ? current : selected;\n const at = pool.findIndex((option) => option.value === from);\n const offset = repeated || buffer.length === 1 ? 1 : 0;\n const ordered = [...pool.slice(at + offset), ...pool.slice(0, at + offset)];\n return ordered.find((option) => option.label.toLowerCase().startsWith(search));\n }\n\n function move(to: string | null | undefined) {\n if (to === undefined || to === null) return;\n scrollIntent.current = true;\n setActive(to);\n }\n\n function onKey(event: KeyboardEvent<HTMLElement>, fromInput: boolean) {\n const { key } = event;\n if (key === \" \" && !fromInput) suppressClick.current = true;\n const printable = key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey;\n const typing =\n performance.now() - typeahead.current.at < TYPEAHEAD_RESET && typeahead.current.buffer.length > 0;\n\n if (!open) {\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Enter\" || (key === \" \" && !typing)) {\n event.preventDefault();\n openList();\n return;\n }\n if (key === \"Home\" || key === \"End\") {\n event.preventDefault();\n const pool = all.filter((option) => !option.disabled);\n openList((key === \"Home\" ? pool[0] : pool[pool.length - 1])?.value ?? null);\n return;\n }\n if (printable) {\n event.preventDefault();\n if (canSearch) openList(undefined, key);\n else {\n const hit = typeTo(key);\n openList(hit?.value ?? selected);\n }\n }\n return;\n }\n\n const at = enabled.findIndex((option) => option.value === current);\n const caretKeys = fromInput && query.length > 0;\n const pick = () => commit(enabled[at], optionRefs.current.get(current ?? \"\"));\n switch (key) {\n case \"ArrowDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + 1)]?.value);\n return;\n case \"ArrowUp\":\n event.preventDefault();\n if (event.altKey) {\n pick();\n return;\n }\n move(enabled[Math.max(0, at - 1)]?.value);\n return;\n case \"PageDown\":\n event.preventDefault();\n move(enabled[Math.min(enabled.length - 1, at + PAGE)]?.value);\n return;\n case \"PageUp\":\n event.preventDefault();\n move(enabled[Math.max(0, at - PAGE)]?.value);\n return;\n case \"Home\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[0]?.value);\n return;\n case \"End\":\n if (caretKeys) return;\n event.preventDefault();\n move(enabled[enabled.length - 1]?.value);\n return;\n case \"Enter\":\n event.preventDefault();\n pick();\n return;\n case \"Escape\":\n event.preventDefault();\n event.stopPropagation();\n close(true);\n return;\n case \"Tab\":\n close(false);\n return;\n }\n if (fromInput) return;\n if (key === \" \" && !typing) {\n event.preventDefault();\n pick();\n return;\n }\n if (printable) {\n event.preventDefault();\n move(typeTo(key)?.value);\n }\n }\n\n // A press outside or focus leaving the control folds the list back into the trigger.\n useEffect(() => {\n if (!open) return;\n const down = (event: PointerEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) close(false);\n };\n document.addEventListener(\"pointerdown\", down);\n return () => document.removeEventListener(\"pointerdown\", down);\n }, [close, open]);\n function onBlur(event: FocusEvent<HTMLDivElement>) {\n const next = event.relatedTarget as Node | null;\n if (open && next && !event.currentTarget.contains(next)) close(false);\n }\n\n function onQuery(next: string) {\n setQuery(next);\n setActive(null);\n scrollIntent.current = true;\n if (scrollRef.current) scrollRef.current.scrollTop = 0;\n const text = next.trim().toLowerCase();\n const count = text ? all.filter((option) => matches(option, text)).length : all.length;\n setAnnouncement(text ? (count ? `${count} ${count === 1 ? \"option\" : \"options\"}` : \"No matches\") : \"\");\n }\n\n /* ---------------------------------------------- The chosen label ---------------------------------------------- */\n\n // The trigger's value is a layer: a new one flies in from the list (or rolls, when it cannot) while the old one lifts away.\n const layerKey = selected ?? \"__empty\";\n const flying = change.value === selected ? change.fly : null;\n const { leaving, changed, remove } = useLeavingItems(\n layerKey,\n { key: layerKey, node: <Value option={selectedOption} placeholder={placeholder} />, dir: change.dir, fly: Boolean(flying) },\n () => layerRef.current,\n );\n\n const rootStyle = {\n \"--ms-panel-w\": `${panelWidth}px`,\n \"--ms-list-max\": `${maxListHeight}px`,\n } as CSSProperties;\n const layerStyle = {\n \"--dir\": change.dir,\n ...(flying ? { \"--fly-x\": `${flying.x}px`, \"--fly-y\": `${flying.y}px` } : {}),\n } as CSSProperties;\n\n return (\n <div\n ref={rootRef}\n data-slot=\"morph-select\"\n data-open={open || undefined}\n data-align={align}\n data-size={size}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n style={rootStyle}\n onBlur={onBlur}\n className={cn(\n \"morph-select-root group/ms relative inline-grid min-w-0 justify-items-start gap-2 font-sans leading-(--leading-normal) text-foreground data-[align=end]:justify-items-end data-[open]:z-30 data-[size=sm]:text-sm data-[size=md]:text-base data-[size=lg]:text-base\",\n className,\n )}\n >\n <span id={labelId} data-slot=\"morph-select-label\" className={cnAll(labelVariants(), labelHidden && \"sr-only\")}>\n {label}\n </span>\n\n <div ref={anchorRef} className=\"morph-select-anchor\">\n {/* Sizes the closed trigger. It holds the chosen label with the trigger's own padding, so the width has a target before anything moves. */}\n <span ref={measureRef} aria-hidden=\"true\" className={cnAll(triggerLayout, \"morph-select-measure\")}>\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n\n <div\n ref={shapeRef}\n data-slot=\"morph-select-shape\"\n data-open={open || undefined}\n data-disabled={disabled || undefined}\n data-invalid={isInvalid || undefined}\n className=\"morph-select-shape\"\n >\n {/* The lid is the trigger row. It stays at the top of the surface in both states and sits above the list, so a lifted label passes over it. */}\n <div className=\"absolute inset-x-0 top-0 z-10 h-(--ms-h)\">\n <button\n ref={triggerRef}\n id={id}\n type=\"button\"\n role=\"combobox\"\n data-slot=\"morph-select-trigger\"\n aria-labelledby={outsideLabel ? undefined : labelId}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listId}\n aria-activedescendant={\n open && !canSearch && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n disabled={disabled}\n tabIndex={showSearch ? -1 : 0}\n onClick={() => {\n if (suppressClick.current) {\n suppressClick.current = false;\n return;\n }\n if (open) close(true);\n else openList();\n }}\n onKeyDown={(event) => onKey(event, false)}\n onKeyUp={(event) => {\n if (event.key === \" \")\n window.setTimeout(() => {\n suppressClick.current = false;\n });\n }}\n className={cnAll(\n triggerLayout,\n \"absolute inset-0 w-full cursor-pointer border-0 bg-transparent text-start text-foreground outline-none [font-family:inherit] [font-size:inherit] [line-height:inherit] [letter-spacing:inherit] [-webkit-tap-highlight-color:transparent] [touch-action:manipulation] disabled:cursor-not-allowed\",\n )}\n >\n <span\n ref={slotRef}\n data-hidden={showSearch || undefined}\n className=\"relative block h-full min-w-0 flex-1 data-[hidden]:opacity-0 motion-safe:transition-opacity motion-safe:duration-(--duration-instant) motion-safe:ease-settle\"\n >\n {leaving.map((item) => (\n <LeavingLayer\n key={item.id}\n id={item.id}\n rect={item.rect}\n dir={item.value.dir}\n fly={item.value.fly}\n onDone={remove}\n >\n {item.value.node}\n </LeavingLayer>\n ))}\n <span\n key={layerKey}\n ref={layerRef}\n style={layerStyle}\n className={cnAll(\n \"absolute inset-0 flex min-w-0 items-center gap-2\",\n changed &&\n (flying\n ? \"motion-safe:animate-morph-select-fly motion-reduce:animate-fade-in\"\n : \"motion-safe:animate-select-value-in motion-reduce:animate-fade-in\"),\n )}\n >\n <Value option={selectedOption} placeholder={placeholder} />\n </span>\n </span>\n </button>\n\n {canSearch ? (\n <div\n ref={searchRowRef}\n className={cnAll(\n \"absolute inset-0 flex items-center gap-2 ps-(--ms-pad) pe-1.5 motion-safe:transition-opacity motion-safe:duration-(--duration-quick) motion-safe:ease-settle\",\n showSearch ? \"opacity-100\" : \"pointer-events-none opacity-0\",\n )}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 flex-none text-muted-foreground\"\n >\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.5 10.5 3 3\" />\n </svg>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n data-slot=\"morph-select-search\"\n aria-label={`Search ${label.toLowerCase()}`}\n aria-invalid={isInvalid || undefined}\n aria-describedby={showError ? errorId : undefined}\n aria-expanded={open}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={\n open && current !== null ? optionId(indexOf.get(current) ?? 0) : undefined\n }\n placeholder={searchPlaceholder ?? selectedOption?.label ?? \"Search\"}\n value={query}\n autoComplete=\"off\"\n spellCheck={false}\n onChange={(event) => onQuery(event.target.value)}\n onKeyDown={(event) => onKey(event, true)}\n className=\"h-full min-w-0 flex-1 border-0 bg-transparent p-0 [font:inherit] text-foreground outline-none placeholder:text-muted-foreground\"\n />\n {query ? (\n <button\n type=\"button\"\n aria-label=\"Clear search\"\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onQuery(\"\");\n inputRef.current?.focus();\n }}\n className=\"grid size-6 flex-none cursor-pointer place-items-center rounded-pill border-0 bg-muted p-0 text-text-secondary outline-none motion-safe:animate-field-clear-in motion-reduce:animate-fade-in hover:text-foreground\"\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n className=\"size-3.5\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </button>\n ) : null}\n {/* Sits exactly over the shared chevron, so the chevron closes the list in both lid states. */}\n <button\n type=\"button\"\n aria-label={`Close ${label.toLowerCase()}`}\n onClick={() => close(true)}\n className=\"size-9 flex-none cursor-pointer rounded-pill border-0 bg-transparent p-0 outline-none motion-safe:transition-[background-color] motion-safe:duration-(--duration-quick) motion-safe:ease-settle hover:bg-muted\"\n />\n </div>\n ) : null}\n\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"pointer-events-none absolute end-4 top-1/2 -mt-2 size-4 text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-data-[open]/ms:rotate-180\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </div>\n\n <div\n ref={faceRef}\n data-open={open || undefined}\n className=\"morph-select-face\"\n >\n <div\n ref={scrollRef}\n className=\"max-h-(--ms-list-max) overflow-y-auto overscroll-contain pt-1.5 [scrollbar-width:thin]\"\n >\n <div id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"relative grid grid-cols-1 gap-0.5\">\n <span ref={highlightRef} aria-hidden=\"true\" className=\"morph-select-highlight\" />\n {visibleSections.map((section) => {\n const rows = section.options.map((option) => {\n const index = indexOf.get(option.value) ?? 0;\n const isSelected = option.value === selected;\n return (\n <div\n key={option.value}\n id={optionId(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n data-slot=\"morph-select-option\"\n data-active={option.value === current || undefined}\n ref={(node) => {\n if (node) optionRefs.current.set(option.value, node);\n else optionRefs.current.delete(option.value);\n }}\n onPointerMove={(event) => {\n if (event.pointerType === \"mouse\" && !option.disabled && option.value !== current)\n setActive(option.value);\n }}\n onPointerDown={(event) => event.preventDefault()}\n onClick={(event) => commit(option, event.currentTarget)}\n className=\"morph-select-option relative box-border flex min-h-9 cursor-pointer select-none items-center gap-2 rounded-lg text-text-secondary outline-none data-[active]:text-foreground aria-selected:text-foreground aria-disabled:cursor-not-allowed aria-disabled:opacity-(--opacity-disabled) motion-safe:transition-colors motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <span data-part=\"value\" className=\"flex min-w-0 flex-1 items-center gap-2 font-medium\">\n {option.icon ? (\n <span className={iconClass} aria-hidden=\"true\">\n {option.icon}\n </span>\n ) : null}\n <span className=\"min-w-0 truncate\">{option.label}</span>\n </span>\n {option.meta ? (\n <span className=\"flex-none text-xs tabular-nums text-muted-foreground\">\n {option.meta}\n </span>\n ) : null}\n <span\n aria-hidden=\"true\"\n data-on={isSelected || undefined}\n className=\"grid size-4 flex-none scale-60 place-items-center text-foreground opacity-0 data-[on]:scale-100 data-[on]:opacity-100 motion-safe:transition-[opacity,scale] motion-safe:duration-(--duration-quick) motion-safe:ease-settle\"\n >\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n </span>\n </div>\n );\n });\n return section.label ? (\n <div\n key={section.key}\n role=\"group\"\n aria-labelledby={`${uid}-${section.key}`}\n className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\"\n >\n <div\n id={`${uid}-${section.key}`}\n role=\"presentation\"\n className=\"px-2.5 pt-2 pb-1 text-xs text-muted-foreground\"\n >\n {section.label}\n </div>\n {rows}\n </div>\n ) : (\n <div key={section.key} role=\"presentation\" className=\"grid grid-cols-1 gap-0.5 not-first:mt-1\">\n {rows}\n </div>\n );\n })}\n {resultCount === 0 ? (\n <p className=\"m-0 px-2.5 pt-3 pb-3.5 text-sm text-muted-foreground\">\n No matches for “{query.trim()}”\n </p>\n ) : null}\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {showError ? (\n <span\n id={errorId}\n data-slot=\"morph-select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </span>\n ) : null}\n {name ? <input type=\"hidden\" name={name} value={selected ?? \"\"} /> : null}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { MultiSelect, MultiSelectProps } from "./multi-select.js";
|
|
2
|
-
export { MultiSelect, type MultiSelectProps };
|
|
1
|
+
import { MultiSelect, MultiSelectGroup, MultiSelectGroupProps, MultiSelectProps, multiSelectGroupHeaderVariants, multiSelectGroupVariants } from "./multi-select.js";
|
|
2
|
+
export { MultiSelect, MultiSelectGroup, type MultiSelectGroupProps, type MultiSelectProps, multiSelectGroupHeaderVariants, multiSelectGroupVariants };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { MultiSelect } from "./multi-select.js";
|
|
2
|
-
export { MultiSelect };
|
|
1
|
+
import { MultiSelect, MultiSelectGroup, multiSelectGroupHeaderVariants, multiSelectGroupVariants } from "./multi-select.js";
|
|
2
|
+
export { MultiSelect, MultiSelectGroup, multiSelectGroupHeaderVariants, multiSelectGroupVariants };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { SelectProps } from "react-aria-components";
|
|
2
|
+
import { Key, SelectProps } from "react-aria-components";
|
|
3
3
|
//#region src/multi-select/multi-select.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* MultiSelect: pick several options from a list. A Select in multiple mode
|
|
@@ -10,7 +10,8 @@ import { SelectProps } from "react-aria-components";
|
|
|
10
10
|
* fades up from a blur and grows in on the snappy spring while the chips beside
|
|
11
11
|
* it glide over; the count rolls up or down as it changes. A clear button scales
|
|
12
12
|
* in beside the chevron when anything is picked. Options are `SelectItem`s, whose
|
|
13
|
-
* tick pops in as an option is picked
|
|
13
|
+
* tick pops in as an option is picked; `MultiSelectGroup` gathers them under a
|
|
14
|
+
* heading.
|
|
14
15
|
*/
|
|
15
16
|
export interface MultiSelectProps extends Omit<SelectProps<object, "multiple">, "className" | "style" | "children" | "items" | "selectionMode"> {
|
|
16
17
|
/** `SelectItem` children. */
|
|
@@ -24,6 +25,18 @@ export interface MultiSelectProps extends Omit<SelectProps<object, "multiple">,
|
|
|
24
25
|
errorMessage?: ReactNode;
|
|
25
26
|
/** Chips shown in the trigger before the rest become "+N". @default 2 */
|
|
26
27
|
maxVisible?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Words after the overflow count, so `"more"` reads "+2 more". Off by default:
|
|
30
|
+
* the count alone, "+2".
|
|
31
|
+
*/
|
|
32
|
+
moreLabel?: string;
|
|
33
|
+
/**
|
|
34
|
+
* While some picks are folded into "+N", hovering or focusing the trigger shows
|
|
35
|
+
* a tooltip listing them. Off by default.
|
|
36
|
+
*
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
overflowTooltip?: boolean;
|
|
27
40
|
/** Accessible name of the clear button. @default "Clear selections" */
|
|
28
41
|
clearLabel?: string;
|
|
29
42
|
/** @default "md" */
|
|
@@ -32,5 +45,33 @@ export interface MultiSelectProps extends Omit<SelectProps<object, "multiple">,
|
|
|
32
45
|
className?: string;
|
|
33
46
|
}
|
|
34
47
|
export declare const MultiSelect: import("react").ForwardRefExoticComponent<MultiSelectProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
48
|
+
export declare const multiSelectGroupVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
49
|
+
export declare const multiSelectGroupHeaderVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
50
|
+
/**
|
|
51
|
+
* A run of options under a heading, such as "Countries" and "Regions". React
|
|
52
|
+
* Aria's ListBoxSection, so the heading names the group for assistive tech and
|
|
53
|
+
* the arrow keys move straight through from one group to the next.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <MultiSelect label="Markets">
|
|
57
|
+
* <MultiSelectGroup title="Countries">
|
|
58
|
+
* <SelectItem id="IN">India</SelectItem>
|
|
59
|
+
* </MultiSelectGroup>
|
|
60
|
+
* <MultiSelectGroup title="Regions">
|
|
61
|
+
* <SelectItem id="europe">Europe</SelectItem>
|
|
62
|
+
* </MultiSelectGroup>
|
|
63
|
+
* </MultiSelect>
|
|
64
|
+
*/
|
|
65
|
+
export interface MultiSelectGroupProps {
|
|
66
|
+
/** The heading drawn above the group, and the group's accessible name. */
|
|
67
|
+
title: ReactNode;
|
|
68
|
+
/** `SelectItem` children. */
|
|
69
|
+
children?: ReactNode;
|
|
70
|
+
/** Identifies the group in the collection. Defaults to a generated id. */
|
|
71
|
+
id?: Key;
|
|
72
|
+
/** Extra classes for the group. Merged last. */
|
|
73
|
+
className?: string;
|
|
74
|
+
}
|
|
75
|
+
export declare const MultiSelectGroup: import("react").ForwardRefExoticComponent<MultiSelectGroupProps & import("react").RefAttributes<HTMLElement>>;
|
|
35
76
|
//#endregion
|
|
36
77
|
//# sourceMappingURL=multi-select.d.ts.map
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
import { cn, cnAll } from "../lib/cn.js";
|
|
3
3
|
import { SwapText } from "../lib/swap-text.js";
|
|
4
4
|
import { useFlip } from "../lib/flip.js";
|
|
5
|
+
import { Tooltip as Tooltip$1, TooltipTrigger as TooltipTrigger$1 } from "../tooltip/tooltip.js";
|
|
5
6
|
import { labelVariants } from "../label/label.js";
|
|
6
7
|
import { selectPopoverVariants, selectTriggerVariants, selectVariants } from "../select/select.js";
|
|
8
|
+
import { cva } from "class-variance-authority";
|
|
7
9
|
import { forwardRef, useContext, useRef, useState } from "react";
|
|
8
|
-
import { Button, ButtonContext, FieldError, Label, ListBox, Popover, Select, SelectStateContext, SelectValue } from "react-aria-components";
|
|
10
|
+
import { Button, ButtonContext, FieldError, Header, Label, ListBox, ListBoxSection, Popover, Select, SelectStateContext, SelectValue } from "react-aria-components";
|
|
9
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
12
|
//#region src/multi-select/multi-select.tsx
|
|
11
13
|
/** The picked options as chips, then "+N", reading Select's own state. */
|
|
12
|
-
function Chips({ placeholder, maxVisible }) {
|
|
14
|
+
function Chips({ placeholder, maxVisible, moreLabel, moreRef }) {
|
|
13
15
|
const state = useContext(SelectStateContext);
|
|
14
16
|
const keys = state?.value ?? [];
|
|
15
17
|
const labelOf = (key) => state?.collection.getItem(key)?.textValue ?? String(key);
|
|
@@ -43,19 +45,50 @@ function Chips({ placeholder, maxVisible }) {
|
|
|
43
45
|
children: labelOf(key)
|
|
44
46
|
}, String(key))),
|
|
45
47
|
rest > 0 ? /* @__PURE__ */ jsxs("span", {
|
|
48
|
+
ref: moreRef,
|
|
46
49
|
"data-flip": "",
|
|
47
50
|
"data-slot": "multi-select-more",
|
|
48
51
|
className: "multi-select-chip text-text-secondary tabular-nums",
|
|
49
|
-
children: [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
children: [
|
|
53
|
+
"+",
|
|
54
|
+
/* @__PURE__ */ jsx(SwapText, {
|
|
55
|
+
swapKey: String(rest),
|
|
56
|
+
dir: count.dir,
|
|
57
|
+
motion: "rise",
|
|
58
|
+
children: rest
|
|
59
|
+
}),
|
|
60
|
+
moreLabel ? /* @__PURE__ */ jsx("span", {
|
|
61
|
+
"data-slot": "multi-select-more-label",
|
|
62
|
+
className: "whitespace-pre",
|
|
63
|
+
children: ` ${moreLabel}`
|
|
64
|
+
}) : null
|
|
65
|
+
]
|
|
55
66
|
}) : null
|
|
56
67
|
]
|
|
57
68
|
});
|
|
58
69
|
}
|
|
70
|
+
/** The labels of the picks folded into "+N", in the order they were picked. */
|
|
71
|
+
function useHiddenLabels(maxVisible) {
|
|
72
|
+
const state = useContext(SelectStateContext);
|
|
73
|
+
return (state?.value ?? []).slice(maxVisible).map((key) => state?.collection.getItem(key)?.textValue ?? String(key));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wraps the trigger in a tooltip naming the picks hidden behind "+N", placed
|
|
77
|
+
* against the +N chip rather than the whole field. Always rendered, so turning
|
|
78
|
+
* `overflowTooltip` on or off never swaps the trigger element (and drops its
|
|
79
|
+
* focus); disabled while off or while nothing is hidden, so it never opens empty.
|
|
80
|
+
*/
|
|
81
|
+
function OverflowTooltip({ enabled, maxVisible, anchorRef, children }) {
|
|
82
|
+
const hidden = useHiddenLabels(maxVisible);
|
|
83
|
+
return /* @__PURE__ */ jsxs(TooltipTrigger$1, {
|
|
84
|
+
isDisabled: !enabled || hidden.length === 0,
|
|
85
|
+
children: [children, /* @__PURE__ */ jsx(Tooltip$1, {
|
|
86
|
+
triggerRef: anchorRef,
|
|
87
|
+
"data-slot": "multi-select-overflow-tooltip",
|
|
88
|
+
children: hidden.join(", ")
|
|
89
|
+
})]
|
|
90
|
+
});
|
|
91
|
+
}
|
|
59
92
|
/** Clears every pick. It sits outside the trigger, so it is its own tab stop. */
|
|
60
93
|
function Clear({ label, disabled }) {
|
|
61
94
|
const state = useContext(SelectStateContext);
|
|
@@ -84,7 +117,37 @@ function Clear({ label, disabled }) {
|
|
|
84
117
|
})
|
|
85
118
|
});
|
|
86
119
|
}
|
|
87
|
-
const MultiSelect = forwardRef(function MultiSelect({ children, label, labelHidden, placeholder = "Select options", errorMessage, maxVisible = 2, clearLabel = "Clear selections", size = "md", className, ...props }, ref) {
|
|
120
|
+
const MultiSelect = forwardRef(function MultiSelect({ children, label, labelHidden, placeholder = "Select options", errorMessage, maxVisible = 2, moreLabel, overflowTooltip = false, clearLabel = "Clear selections", size = "md", className, ...props }, ref) {
|
|
121
|
+
const moreRef = useRef(null);
|
|
122
|
+
const trigger = /* @__PURE__ */ jsxs(Button, {
|
|
123
|
+
ref,
|
|
124
|
+
"data-slot": "multi-select-trigger",
|
|
125
|
+
className: cnAll(selectTriggerVariants({ size }), "pe-16 multi-select-trigger"),
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsx(SelectValue, {
|
|
128
|
+
"data-slot": "multi-select-value",
|
|
129
|
+
className: "sr-only",
|
|
130
|
+
children: ({ isPlaceholder, selectedText }) => isPlaceholder ? placeholder : selectedText
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ jsx(Chips, {
|
|
133
|
+
placeholder,
|
|
134
|
+
maxVisible,
|
|
135
|
+
moreLabel,
|
|
136
|
+
moreRef
|
|
137
|
+
}),
|
|
138
|
+
/* @__PURE__ */ jsx("svg", {
|
|
139
|
+
"aria-hidden": "true",
|
|
140
|
+
viewBox: "0 0 16 16",
|
|
141
|
+
fill: "none",
|
|
142
|
+
stroke: "currentColor",
|
|
143
|
+
strokeWidth: "1.75",
|
|
144
|
+
strokeLinecap: "round",
|
|
145
|
+
strokeLinejoin: "round",
|
|
146
|
+
className: "absolute end-(--density-control-pad-md) text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180",
|
|
147
|
+
children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4" })
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
});
|
|
88
151
|
return /* @__PURE__ */ jsxs(Select, {
|
|
89
152
|
...props,
|
|
90
153
|
selectionMode: "multiple",
|
|
@@ -98,32 +161,11 @@ const MultiSelect = forwardRef(function MultiSelect({ children, label, labelHidd
|
|
|
98
161
|
}),
|
|
99
162
|
/* @__PURE__ */ jsxs("div", {
|
|
100
163
|
className: "relative",
|
|
101
|
-
children: [/* @__PURE__ */
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
children:
|
|
106
|
-
/* @__PURE__ */ jsx(SelectValue, {
|
|
107
|
-
"data-slot": "multi-select-value",
|
|
108
|
-
className: "sr-only",
|
|
109
|
-
children: ({ isPlaceholder, selectedText }) => isPlaceholder ? placeholder : selectedText
|
|
110
|
-
}),
|
|
111
|
-
/* @__PURE__ */ jsx(Chips, {
|
|
112
|
-
placeholder,
|
|
113
|
-
maxVisible
|
|
114
|
-
}),
|
|
115
|
-
/* @__PURE__ */ jsx("svg", {
|
|
116
|
-
"aria-hidden": "true",
|
|
117
|
-
viewBox: "0 0 16 16",
|
|
118
|
-
fill: "none",
|
|
119
|
-
stroke: "currentColor",
|
|
120
|
-
strokeWidth: "1.75",
|
|
121
|
-
strokeLinecap: "round",
|
|
122
|
-
strokeLinejoin: "round",
|
|
123
|
-
className: "absolute end-(--density-control-pad-md) text-muted-foreground motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180",
|
|
124
|
-
children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4" })
|
|
125
|
-
})
|
|
126
|
-
]
|
|
164
|
+
children: [/* @__PURE__ */ jsx(OverflowTooltip, {
|
|
165
|
+
enabled: overflowTooltip,
|
|
166
|
+
maxVisible,
|
|
167
|
+
anchorRef: moreRef,
|
|
168
|
+
children: trigger
|
|
127
169
|
}), /* @__PURE__ */ jsx(Clear, {
|
|
128
170
|
label: clearLabel,
|
|
129
171
|
disabled: props.isDisabled ?? false
|
|
@@ -146,7 +188,22 @@ const MultiSelect = forwardRef(function MultiSelect({ children, label, labelHidd
|
|
|
146
188
|
]
|
|
147
189
|
});
|
|
148
190
|
});
|
|
191
|
+
const multiSelectGroupVariants = cva(["flex flex-col not-first:mt-1"]);
|
|
192
|
+
const multiSelectGroupHeaderVariants = cva(["px-2 pt-1.5 pb-1 text-xs leading-(--leading-normal) font-semibold text-muted-foreground"]);
|
|
193
|
+
const MultiSelectGroup = forwardRef(function MultiSelectGroup({ title, children, id, className }, ref) {
|
|
194
|
+
return /* @__PURE__ */ jsxs(ListBoxSection, {
|
|
195
|
+
ref,
|
|
196
|
+
id,
|
|
197
|
+
"data-slot": "multi-select-group",
|
|
198
|
+
className: cn(multiSelectGroupVariants(), className),
|
|
199
|
+
children: [/* @__PURE__ */ jsx(Header, {
|
|
200
|
+
"data-slot": "multi-select-group-header",
|
|
201
|
+
className: multiSelectGroupHeaderVariants(),
|
|
202
|
+
children: title
|
|
203
|
+
}), children]
|
|
204
|
+
});
|
|
205
|
+
});
|
|
149
206
|
//#endregion
|
|
150
|
-
export { MultiSelect };
|
|
207
|
+
export { MultiSelect, MultiSelectGroup, multiSelectGroupHeaderVariants, multiSelectGroupVariants };
|
|
151
208
|
|
|
152
209
|
//# sourceMappingURL=multi-select.js.map
|