@graundtech/fluent2-react-kit 0.5.1

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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/components/ui/accordion.d.ts +133 -0
  4. package/dist/components/ui/accordion.js +80 -0
  5. package/dist/components/ui/accordion.js.map +1 -0
  6. package/dist/components/ui/alert.d.ts +106 -0
  7. package/dist/components/ui/alert.js +73 -0
  8. package/dist/components/ui/alert.js.map +1 -0
  9. package/dist/components/ui/avatar.d.ts +42 -0
  10. package/dist/components/ui/avatar.js +55 -0
  11. package/dist/components/ui/avatar.js.map +1 -0
  12. package/dist/components/ui/badge.d.ts +36 -0
  13. package/dist/components/ui/badge.js +81 -0
  14. package/dist/components/ui/badge.js.map +1 -0
  15. package/dist/components/ui/breadcrumb.d.ts +53 -0
  16. package/dist/components/ui/breadcrumb.js +149 -0
  17. package/dist/components/ui/breadcrumb.js.map +1 -0
  18. package/dist/components/ui/button.d.ts +24 -0
  19. package/dist/components/ui/button.js +86 -0
  20. package/dist/components/ui/button.js.map +1 -0
  21. package/dist/components/ui/card.d.ts +51 -0
  22. package/dist/components/ui/card.js +97 -0
  23. package/dist/components/ui/card.js.map +1 -0
  24. package/dist/components/ui/checkbox.d.ts +101 -0
  25. package/dist/components/ui/checkbox.js +57 -0
  26. package/dist/components/ui/checkbox.js.map +1 -0
  27. package/dist/components/ui/combobox.d.ts +212 -0
  28. package/dist/components/ui/combobox.js +236 -0
  29. package/dist/components/ui/combobox.js.map +1 -0
  30. package/dist/components/ui/command.d.ts +187 -0
  31. package/dist/components/ui/command.js +225 -0
  32. package/dist/components/ui/command.js.map +1 -0
  33. package/dist/components/ui/dialog.d.ts +117 -0
  34. package/dist/components/ui/dialog.js +165 -0
  35. package/dist/components/ui/dialog.js.map +1 -0
  36. package/dist/components/ui/dropdown-menu.d.ts +155 -0
  37. package/dist/components/ui/dropdown-menu.js +295 -0
  38. package/dist/components/ui/dropdown-menu.js.map +1 -0
  39. package/dist/components/ui/input.d.ts +44 -0
  40. package/dist/components/ui/input.js +43 -0
  41. package/dist/components/ui/input.js.map +1 -0
  42. package/dist/components/ui/label.d.ts +39 -0
  43. package/dist/components/ui/label.js +30 -0
  44. package/dist/components/ui/label.js.map +1 -0
  45. package/dist/components/ui/link.d.ts +62 -0
  46. package/dist/components/ui/link.js +61 -0
  47. package/dist/components/ui/link.js.map +1 -0
  48. package/dist/components/ui/multi-select.d.ts +115 -0
  49. package/dist/components/ui/multi-select.js +133 -0
  50. package/dist/components/ui/multi-select.js.map +1 -0
  51. package/dist/components/ui/pagination.d.ts +46 -0
  52. package/dist/components/ui/pagination.js +164 -0
  53. package/dist/components/ui/pagination.js.map +1 -0
  54. package/dist/components/ui/popover.d.ts +117 -0
  55. package/dist/components/ui/popover.js +62 -0
  56. package/dist/components/ui/popover.js.map +1 -0
  57. package/dist/components/ui/progress.d.ts +98 -0
  58. package/dist/components/ui/progress.js +67 -0
  59. package/dist/components/ui/progress.js.map +1 -0
  60. package/dist/components/ui/radio-group.d.ts +94 -0
  61. package/dist/components/ui/radio-group.js +65 -0
  62. package/dist/components/ui/radio-group.js.map +1 -0
  63. package/dist/components/ui/select.d.ts +145 -0
  64. package/dist/components/ui/select.js +240 -0
  65. package/dist/components/ui/select.js.map +1 -0
  66. package/dist/components/ui/separator.d.ts +43 -0
  67. package/dist/components/ui/separator.js +27 -0
  68. package/dist/components/ui/separator.js.map +1 -0
  69. package/dist/components/ui/skeleton.d.ts +81 -0
  70. package/dist/components/ui/skeleton.js +24 -0
  71. package/dist/components/ui/skeleton.js.map +1 -0
  72. package/dist/components/ui/spinner.d.ts +56 -0
  73. package/dist/components/ui/spinner.js +83 -0
  74. package/dist/components/ui/spinner.js.map +1 -0
  75. package/dist/components/ui/switch.d.ts +60 -0
  76. package/dist/components/ui/switch.js +63 -0
  77. package/dist/components/ui/switch.js.map +1 -0
  78. package/dist/components/ui/tabs.d.ts +107 -0
  79. package/dist/components/ui/tabs.js +102 -0
  80. package/dist/components/ui/tabs.js.map +1 -0
  81. package/dist/components/ui/textarea.d.ts +28 -0
  82. package/dist/components/ui/textarea.js +36 -0
  83. package/dist/components/ui/textarea.js.map +1 -0
  84. package/dist/components/ui/toast.d.ts +185 -0
  85. package/dist/components/ui/toast.js +181 -0
  86. package/dist/components/ui/toast.js.map +1 -0
  87. package/dist/components/ui/tooltip.d.ts +115 -0
  88. package/dist/components/ui/tooltip.js +72 -0
  89. package/dist/components/ui/tooltip.js.map +1 -0
  90. package/dist/index.d.ts +55 -0
  91. package/dist/index.js +284 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/lib/utils.d.ts +9 -0
  94. package/dist/lib/utils.js +9 -0
  95. package/dist/lib/utils.js.map +1 -0
  96. package/package.json +77 -0
  97. package/src/styles/tokens.css +596 -0
  98. package/src/styles/tokens.test.ts +216 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/combobox.tsx"],"sourcesContent":["\"use client\";\n\nimport { Combobox as ComboboxPrimitive } from \"@base-ui/react/combobox\";\nimport {\n CheckmarkRegular,\n ChevronDownRegular,\n DismissRegular,\n} from \"@fluentui/react-icons\";\nimport type { ComponentProps } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Combobox — Fluent 2-styled, composable filterable select built on Base UI.\n *\n * This is the REFERENCE for the kit's filter-list family: Multi Select and\n * Command Menu are built by later agents reading THIS file the way the overlay\n * batch read `select.tsx`. Read the divergence notes and extension-point notes\n * below before extending it.\n *\n * It is the typed-into sibling of `Select`: the field is the kit's Input recipe\n * (h-8, `border-input` + `border-b-stroke-accessible`, bottom brand-accent\n * focus) instead of a button trigger, and the flyout is `SelectContent`'s\n * validated popup recipe verbatim (`bg-popover`, `border`, `shadow-16`,\n * `rounded-md`, `min-w-[var(--anchor-width)]`, scale+fade motion on Base UI's\n * `data-starting-style`/`data-ending-style` hooks). Item rows are `SelectItem`'s\n * 32px `data-[highlighted]` rows with the right-side check.\n *\n * ## Why `@base-ui/react/combobox` and NOT `@base-ui/react/autocomplete`\n * Base UI ships BOTH. They share the same AriaCombobox engine but draw the line\n * at *selection*:\n * - **Combobox** has a selection model — `value`/`onValueChange` (the committed\n * selected value, single or `multiple`) that is SEPARATE from\n * `inputValue`/`onInputValueChange` (the transient filter text). Picking an\n * item commits a discrete value; the input filters the list. This is exactly a\n * \"filterable select\".\n * - **Autocomplete** is `selectionMode: 'none'` — it deliberately omits\n * `selectedValue`/`onValueChange`; the input value IS the only state. It's for\n * free-text-with-suggestions (search boxes), where there is no discrete\n * committed choice.\n * The whole filter-list family (this, Multi Select, Command Menu) selects\n * discrete items, so it wraps **combobox**. A future free-text search field is\n * the one case that would wrap autocomplete instead.\n *\n * ## Base UI mapping (conventions §9)\n * Namespace import of `@base-ui/react/combobox`, matching the export shape in\n * node_modules (like `select.tsx`/`avatar.tsx`). shadcn-style part names mapped\n * onto Base UI's model:\n *\n * | Exported (kit name) | Base UI primitive |\n * | ------------------- | ---------------------------------------------------------- |\n * | `Combobox` | `Combobox.Root` |\n * | `ComboboxInput` | field wrapper + `Combobox.Input` + `Combobox.Clear` + `Combobox.Trigger` (+ `Combobox.Icon`) |\n * | `ComboboxContent` | `Combobox.Portal` + `Combobox.Positioner` + `Combobox.Popup` |\n * | `ComboboxList` | `Combobox.List` ← see divergence 5 |\n * | `ComboboxEmpty` | `Combobox.Empty` |\n * | `ComboboxItem` | `Combobox.Item` (+ `Combobox.ItemIndicator`) |\n * | `ComboboxGroup` | `Combobox.Group` |\n * | `ComboboxLabel` | `Combobox.GroupLabel` ← see divergence 1 |\n * | `ComboboxSeparator` | `Combobox.Separator` |\n * | `ComboboxValue` | `Combobox.Value` ← see divergence 4 |\n *\n * ## Divergences vs Base UI naming (all deliberate)\n * 1. **`ComboboxLabel` maps to `Combobox.GroupLabel`, not `Combobox.Label`.**\n * Base UI has two label parts: `Combobox.Label` labels the whole control (a\n * field `<label>`), `Combobox.GroupLabel` is the heading inside a\n * `Combobox.Group`. Like Select, the kit exposes the *group* heading as\n * `ComboboxLabel` and does NOT re-export the control-level label — pair\n * `ComboboxInput` with the kit's own `Label` component (a separate registry\n * item; install it explicitly). Labeling recipe: give `Label` an `id` and set\n * `aria-labelledby` on the `ComboboxInput` — but unlike Select's button\n * trigger, the input IS a labelable form field, so a plain `htmlFor`/`id`\n * pairing works too (the preview uses `htmlFor` + `id`).\n * 2. **`ComboboxInput` is a composed field, not a bare `<input>`.** It renders a\n * field-chrome wrapper (`data-slot=\"combobox-input-wrapper\"`) around the real\n * `Combobox.Input` (`data-slot=\"combobox-input\"`), plus a `Combobox.Clear`\n * reset button (Base UI auto-unmounts it when the field is empty) and a\n * `Combobox.Trigger` chevron that toggles the popup. This is the sibling of\n * `SelectTrigger`, which likewise bakes its chevron in. `className`, `ref` and\n * all other props forward to the inner `<input>`; the wrapper is styled via\n * `wrapperClassName`. The chevron/clear are `tabIndex={-1}` (not extra tab\n * stops — the input already owns `role=\"combobox\"` + keyboard control).\n * 3. **No `Combobox.Arrow`, no scroll buttons.** The popup is a flyout beneath\n * the field (not an anchored caret-overlap like a native `<select>`), so the\n * positioner arrow and Select's ScrollUp/DownArrow parts are omitted; the\n * `List` scrolls with `overflow-y-auto`.\n * 4. **`ComboboxValue` renders the raw selected value unless `items` is given.**\n * Same model as Select's `SelectValue`: `Combobox.Value` shows the raw\n * `value` unless `<Combobox items={[{ value, label }]}>` is supplied or a\n * render child is used. It is exported for callers who display the selection\n * outside the input; the field itself shows the selection as input text (Base\n * UI fills the input on select), so most single-select UIs never need it.\n * 5. **`ComboboxList` is a separate part (Select auto-wraps its `List`).** Base\n * UI does the filtering, so the natural API passes `items` to `<Combobox>` and\n * renders the list with a render-function child:\n * `<ComboboxList>{(item) => <ComboboxItem value={item}>{item.label}</ComboboxItem>}</ComboboxList>`.\n * `ComboboxEmpty` is a sibling of `ComboboxList` INSIDE `ComboboxContent` (not\n * a `List` child) — it mirrors Base UI's own structure and lets the no-match\n * caption sit at the popup level. Static `ComboboxItem` children also work,\n * but the built-in filter only runs against the `items` prop, so filtering UIs\n * should pass `items` + the render-function form. Unlike `SelectContent`\n * (which owns its `List`), keeping `List` explicit is what makes correct\n * `Empty` placement and the render-function API possible.\n *\n * ## Divergences vs shadcn's Combobox recipe\n * shadcn has NO Combobox component — its docs \"Combobox\" is a hand-rolled recipe\n * composing `Popover` + `Command` (cmdk) with local `useState`, `open`, and a\n * manually-wired `CommandInput`/`CommandItem`/`onSelect`. This kit instead ships\n * a real `Combobox*` part family:\n * - **Filtering is built in.** Base UI's `Combobox.Root` filters `List` items\n * against the input value automatically (`mode=\"list\"` default); the shadcn\n * recipe leans on cmdk's `Command` to filter. No `cmdk` dependency here.\n * - **One selection model, not two hooks.** shadcn juggles Popover `open` +\n * Command `value` + an external `value` `useState`. Here `value`/`inputValue`\n * are one model on the Root — controlled or uncontrolled — exposed honestly.\n * - **Composable parts, not a monolith.** `Combobox`, `ComboboxInput`,\n * `ComboboxContent`, `ComboboxItem`, … read as the sibling of `Select`, not as\n * a bespoke Popover+Command assembly the consumer re-wires each time.\n *\n * ## Extension points for Multi Select (do NOT style chips here)\n * `multiple` is passed through UNBLOCKED (`Combobox`'s generic second type param,\n * exactly like `Select`). When `multiple` is set, Base UI:\n * - makes `value`/`onValueChange` arrays;\n * - keeps `Combobox.Clear` clearing to `[]`;\n * - exposes `Combobox.Chips` / `Combobox.Chip` / `Combobox.ChipRemove` for a\n * token field, and `Combobox.Row` for grid layouts.\n * The Multi Select agent should build its OWN field part (e.g. `MultiSelectInput`\n * / a chips field) that wraps `Combobox.Chips` around `Combobox.Input` inside the\n * same field-chrome wrapper this file uses — it must NOT reuse `ComboboxInput`,\n * which is the single-line surface. This file intentionally leaves chip styling\n * unspecified; the `combobox-input-wrapper` class string is the shared field\n * recipe to copy. Everything else (`ComboboxContent`, `ComboboxItem`,\n * `ComboboxGroup`, `ComboboxLabel`, `ComboboxEmpty`) is reusable as-is.\n *\n * ## `\"use client\"` — required\n * `@fluentui/react-icons` forces it: the package's shared icon-sizing module\n * (`createFluentIcon.styles.js`) calls `@griffel/react`'s `__styles()` at module\n * scope without its own `'use client'`, so importing an icon into a Server\n * Component makes `next build` (Turbopack) fail collecting page data. Same fix\n * and root cause as `select.tsx`/`checkbox.tsx` (conventions §9). Every Base UI\n * Combobox part module already carries its own `'use client'`.\n *\n * ## data-slot note\n * `Combobox` (Root) renders no DOM element and its Base UI props type is strict\n * (no `data-*` passthrough), so it carries no `data-slot`. Every part that\n * renders an element does (`combobox-input`, `combobox-content`, `combobox-item`,\n * …) — the styling/testing hook consumers rely on.\n */\n\nfunction Combobox<Value, Multiple extends boolean | undefined = false>(\n props: ComboboxPrimitive.Root.Props<Value, Multiple>\n) {\n return <ComboboxPrimitive.Root {...props} />;\n}\n\n/**\n * Value — displays the selected value (Base UI `Combobox.Value`). Renders the\n * raw value unless `items` is passed to `<Combobox>` or a render child is used\n * (divergence 4). Exported for showing the selection outside the input; the\n * field itself already reflects the selection as input text.\n */\nfunction ComboboxValue(props: ComponentProps<typeof ComboboxPrimitive.Value>) {\n return <ComboboxPrimitive.Value data-slot=\"combobox-value\" {...props} />;\n}\n\n/**\n * Input — the Fluent single-line combobox field (divergence 2). A field-chrome\n * wrapper holds the real `<input>` (transparent, fills the field), a `Clear`\n * reset button (auto-unmounts when empty) and a chevron `Trigger`. The wrapper\n * carries the kit Input recipe: h-8, `rounded-md`, `border-input` +\n * `border-b-stroke-accessible` at rest, and the Fluent bottom brand accent on\n * focus via an inset box-shadow (no reflow) — keyed off `has-[input:focus-visible]`\n * because focus lands on the inner input, not the wrapper. `aria-invalid` on the\n * input swaps the field to destructive, declared after focus so it wins.\n * `className`/`ref`/`...props` forward to the inner `<input>`; style the wrapper\n * with `wrapperClassName`.\n */\nfunction ComboboxInput({\n className,\n wrapperClassName,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Input> & {\n wrapperClassName?: string;\n}) {\n return (\n <div\n data-slot=\"combobox-input-wrapper\"\n className={cn(\n // layout — Fluent medium field, 32px, matches Input. Resting bottom edge\n // uses the darker NeutralStrokeAccessible (#616161) accent; the other\n // sides stay border-input. Focus (border-primary + inset underline) and\n // aria-invalid (border-destructive) both override it.\n \"flex h-8 w-full items-center rounded-md border border-input border-b-stroke-accessible bg-background text-sm\",\n // motion — color + box-shadow so the focus accent animates (§4)\n \"transition-[color,box-shadow] duration-fast ease-ease\",\n // focus — Fluent bottom brand accent via inset box-shadow, no reflow (§4).\n // has-[input:focus-visible] because focus is on the inner input; text\n // inputs match :focus-visible on pointer focus too, so this covers both.\n \"has-[input:focus-visible]:border-primary has-[input:focus-visible]:shadow-[inset_0_-2px_0_0_var(--brand-80)] dark:has-[input:focus-visible]:shadow-[inset_0_-2px_0_0_var(--brand-100)]\",\n // invalid — shadcn aria-invalid treatment; after focus so it wins the border\n \"has-[input[aria-invalid='true']]:border-destructive has-[input[aria-invalid='true']]:ring-destructive/20 dark:has-[input[aria-invalid='true']]:ring-destructive/40\",\n \"has-[input[aria-invalid='true']:focus-visible]:shadow-[inset_0_-2px_0_0_var(--destructive)]\",\n // disabled — opacity read when the whole control is disabled\n \"has-[input:disabled]:pointer-events-none has-[input:disabled]:opacity-50\",\n wrapperClassName\n )}\n >\n <ComboboxPrimitive.Input\n data-slot=\"combobox-input\"\n className={cn(\n \"h-full min-w-0 flex-1 bg-transparent px-3 py-1 outline-none\",\n \"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground\",\n className\n )}\n {...props}\n />\n <ComboboxPrimitive.Clear\n data-slot=\"combobox-clear\"\n aria-label=\"Clear\"\n tabIndex={-1}\n className={cn(\n \"mr-0.5 flex size-6 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none\",\n \"transition-colors duration-fast ease-ease hover:text-foreground\",\n \"focus-visible:ring-2 focus-visible:ring-ring\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4\"\n )}\n >\n <DismissRegular />\n </ComboboxPrimitive.Clear>\n <ComboboxPrimitive.Trigger\n data-slot=\"combobox-trigger\"\n aria-label=\"Show options\"\n tabIndex={-1}\n className={cn(\n \"mr-2 flex shrink-0 items-center text-muted-foreground outline-none\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4\"\n )}\n >\n <ComboboxPrimitive.Icon data-slot=\"combobox-icon\">\n <ChevronDownRegular />\n </ComboboxPrimitive.Icon>\n </ComboboxPrimitive.Trigger>\n </div>\n );\n}\n\n/**\n * Content — the floating flyout: `Portal` → `Positioner` → `Popup`. Surface is\n * `SelectContent`'s validated recipe verbatim: `bg-popover` + `border` +\n * `shadow-16` (Fluent flyout elevation), `rounded-md`, min-width matched to the\n * field via Base UI's `--anchor-width`, and the scale+fade open/close animation\n * on `data-starting-style`/`data-ending-style` with token durations/easings.\n * `transition-[opacity,scale]` (never transform-based — conventions §3.5).\n * Children are placed directly in the popup: a `ComboboxEmpty` (no-match\n * caption) and a `ComboboxList` (the scroll container). See divergence 5.\n */\nfunction ComboboxContent({\n className,\n children,\n sideOffset = 4,\n align = \"start\",\n side = \"bottom\",\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Popup> &\n Pick<\n ComponentProps<typeof ComboboxPrimitive.Positioner>,\n \"side\" | \"align\" | \"sideOffset\"\n >) {\n return (\n <ComboboxPrimitive.Portal>\n <ComboboxPrimitive.Positioner\n data-slot=\"combobox-positioner\"\n sideOffset={sideOffset}\n align={align}\n side={side}\n className=\"z-50 outline-none\"\n >\n <ComboboxPrimitive.Popup\n data-slot=\"combobox-content\"\n className={cn(\n \"relative min-w-[var(--anchor-width)] origin-[var(--transform-origin)] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-16 outline-none\",\n // motion — subtle scale + fade on open (enter) / close (exit)\n \"transition-[opacity,scale] duration-fast ease-decelerate-mid\",\n \"data-starting-style:scale-95 data-starting-style:opacity-0\",\n \"data-ending-style:scale-95 data-ending-style:opacity-0 data-ending-style:ease-accelerate-mid\",\n className\n )}\n {...props}\n >\n {children}\n </ComboboxPrimitive.Popup>\n </ComboboxPrimitive.Positioner>\n </ComboboxPrimitive.Portal>\n );\n}\n\n/**\n * List — the scrollable options container (Base UI `Combobox.List`, divergence\n * 5). `children` may be a render function `(item, index) => …` (the filtering\n * API: pass `items` to `<Combobox>` and map each) or static `ComboboxItem`\n * children. Scrolls at `overflow-y-auto`; capped at `--available-height` or 20rem.\n */\nfunction ComboboxList({\n className,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.List>) {\n return (\n <ComboboxPrimitive.List\n data-slot=\"combobox-list\"\n className={cn(\n \"max-h-[min(var(--available-height),20rem)] overflow-y-auto overscroll-contain p-1\",\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Empty — the no-match state (Base UI `Combobox.Empty`). Base UI renders its\n * children ONLY when the filtered list is empty, and its root element must stay\n * mounted to announce (it carries `role=\"presentation\"` + a live region), so\n * always place a `<ComboboxEmpty>` in the content rather than conditionally\n * rendering it. Muted, centered caption.\n */\nfunction ComboboxEmpty({\n className,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Empty>) {\n return (\n <ComboboxPrimitive.Empty\n data-slot=\"combobox-empty\"\n className={cn(\"py-6 text-center text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Item — one option (Base UI `Combobox.Item`, renders a `<div>` with\n * `role=\"option\"`). `value` selects it; the selected-state check\n * (`CheckmarkRegular`) is absolutely positioned on the right inside\n * `ItemIndicator`, which Base UI only mounts for the selected item. 32px row,\n * 4px list radius, highlight (keyboard focus or hover) via `data-highlighted` →\n * `bg-accent`, disabled items muted + inert — the `SelectItem` recipe.\n */\nfunction ComboboxItem({\n className,\n children,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Item>) {\n return (\n <ComboboxPrimitive.Item\n data-slot=\"combobox-item\"\n className={cn(\n // layout — Fluent 32px row, 4px list radius, room on the right for check\n \"relative flex h-8 w-full cursor-default items-center rounded-md pr-8 pl-2 text-sm outline-none select-none\",\n // rest text is NeutralForeground2, darkening to accent-foreground on\n // highlight (Figma validation: Fluent list rows rest at #424242)\n \"text-foreground-2 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n // disabled item — muted + non-interactive\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n // icons a consumer puts inside the item text\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"absolute right-2 flex size-4 items-center justify-center\">\n <ComboboxPrimitive.ItemIndicator>\n <CheckmarkRegular className=\"size-4\" />\n </ComboboxPrimitive.ItemIndicator>\n </span>\n {children}\n </ComboboxPrimitive.Item>\n );\n}\n\n/**\n * Group — wraps a set of items under a `ComboboxLabel` heading (Base UI\n * `Combobox.Group`). With Base UI's built-in filtering, pass grouped `items` to\n * `<Combobox>` so empty groups drop out automatically; see the preview.\n */\nfunction ComboboxGroup({\n className,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Group>) {\n return (\n <ComboboxPrimitive.Group\n data-slot=\"combobox-group\"\n className={cn(className)}\n {...props}\n />\n );\n}\n\n/**\n * Label — a heading for a `ComboboxGroup` (Base UI `Combobox.GroupLabel`), NOT a\n * control-level field label (divergence 1). Small muted caption, Fluent style.\n */\nfunction ComboboxLabel({\n className,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.GroupLabel>) {\n return (\n <ComboboxPrimitive.GroupLabel\n data-slot=\"combobox-label\"\n className={cn(\n \"px-2 py-1.5 text-xs font-medium text-muted-foreground select-none\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction ComboboxSeparator({\n className,\n ...props\n}: ComponentProps<typeof ComboboxPrimitive.Separator>) {\n return (\n <ComboboxPrimitive.Separator\n data-slot=\"combobox-separator\"\n className={cn(\"pointer-events-none -mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Combobox,\n ComboboxValue,\n ComboboxInput,\n ComboboxContent,\n ComboboxList,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxGroup,\n ComboboxLabel,\n ComboboxSeparator,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,187 @@
1
+ import * as react from 'react';
2
+ import { ReactElement, ComponentProps, ReactNode } from 'react';
3
+ import { Autocomplete } from '@base-ui/react/autocomplete';
4
+ import { Dialog } from './dialog.js';
5
+ import '@base-ui/react/dialog';
6
+
7
+ /**
8
+ * Command — Fluent 2-styled, shadcn-API command palette (cmdk surface) built on
9
+ * Base UI, with NO `cmdk` dependency.
10
+ *
11
+ * A command palette is an ALWAYS-VISIBLE filtered list whose items fire actions
12
+ * (open a file, run a command) rather than persist a selection. That single
13
+ * requirement drives the architecture decision below.
14
+ *
15
+ * ## Why `@base-ui/react/autocomplete` and NOT `@base-ui/react/combobox`
16
+ * `combobox.tsx` (the filter-list family reference) documents the boundary: Base
17
+ * UI's Combobox and Autocomplete share one `AriaCombobox` engine but differ on
18
+ * *selection*. Command items **perform actions — they don't hold a value** — which
19
+ * points squarely at Autocomplete, and two Autocomplete-only capabilities make it
20
+ * the honest, least-fought fit (verified against `@base-ui/react@1.6.0` in
21
+ * node_modules before committing):
22
+ *
23
+ * 1. **`selectionMode: 'none'`.** `Autocomplete.Root` is hard-wired to `'none'`
24
+ * (it Omits `selectedValue`/`onValueChange`/`isItemEqualToValue`/… from its
25
+ * props). There is no committed selection and no check indicator — exactly a
26
+ * command list. A Combobox would force a value model we'd have to fight.
27
+ * 2. **`inline` + `open` = a statically-open list with no popup.**
28
+ * `Autocomplete.Root` exposes `inline`: *"Whether the list is rendered inline
29
+ * without using the component's own popup. Specify `open` unconditionally in
30
+ * conjunction with this prop so the list is considered visible."* This is the
31
+ * always-visible palette panel — `Autocomplete.List` renders directly, with no
32
+ * `Portal`/`Positioner`/`Popup` and no floating-ui positioning to stub. Combobox
33
+ * has the same `inline` flag (shared engine), but its selection model is dead
34
+ * weight here, so Autocomplete is the cleaner primitive.
35
+ *
36
+ * Filtering rides the engine's built-in text filter (`mode="list"`, the default):
37
+ * pass `items` to `<Command>` and render the list with a render-function child,
38
+ * exactly like `Combobox` (the built-in filter runs against the `items` prop). A
39
+ * manual filtered-list fallback was NOT needed — the primitive supports the
40
+ * inline-open + per-item `onClick` model natively.
41
+ *
42
+ * Keyboard is fully wired by the engine over the inline list: ArrowUp/Down roving
43
+ * highlight (`data-highlighted`), Home/End to the ends, and Enter fires the
44
+ * highlighted item — because `Autocomplete.Item.onClick` *"fires when clicking the
45
+ * item with the pointer, as well as when pressing Enter with the keyboard if the
46
+ * item is highlighted when the Input or List element has focus."* That is this
47
+ * kit's `CommandItem` action handler.
48
+ *
49
+ * ## Base UI mapping (conventions §9)
50
+ * Namespace import of `@base-ui/react/autocomplete` (matching the export shape in
51
+ * node_modules, like `select.tsx`/`combobox.tsx`). shadcn/cmdk part names mapped
52
+ * onto Base UI's model:
53
+ *
54
+ * | Exported (cmdk name) | Base UI primitive |
55
+ * | -------------------- | ------------------------------------------------------- |
56
+ * | `Command` | `Autocomplete.Root` (`inline open`) + panel `<div>` |
57
+ * | `CommandInput` | search-row `<div>` + `SearchRegular` + `Autocomplete.Input` |
58
+ * | `CommandList` | `Autocomplete.List` (the scroll container) |
59
+ * | `CommandEmpty` | `Autocomplete.Empty` |
60
+ * | `CommandGroup` | `Autocomplete.Group` (+ `Autocomplete.GroupLabel` heading) |
61
+ * | `CommandItem` | `Autocomplete.Item` |
62
+ * | `CommandSeparator` | `Autocomplete.Separator` |
63
+ * | `CommandShortcut` | plain `<span>` (cmdk parity — no Base UI part) |
64
+ * | `CommandDialog` | kit `Dialog` + `DialogContent` wrapping a `Command` |
65
+ *
66
+ * ## Divergences vs cmdk / shadcn's Command (all deliberate)
67
+ * 1. **Items fire `onClick`, not `onSelect`.** cmdk's `CommandItem` has an
68
+ * `onSelect(value)` callback; Base UI's `Autocomplete.Item` exposes a native
69
+ * `onClick` that fires on pointer-click AND Enter-when-highlighted (same effect,
70
+ * honest primitive prop). No synthetic `onSelect`/`value`-string is faked.
71
+ * 2. **Filtering keys off the `items` prop, not item DOM text.** cmdk filters by
72
+ * scoring each item's rendered text; Base UI filters the `items` array with its
73
+ * built-in filter. So a *filtering* palette passes `items` + the render-function
74
+ * `CommandList` child (like `Combobox`). Static `CommandItem` children still
75
+ * render, but only the `items` prop is filtered.
76
+ * 3. **`CommandGroup` takes a `heading` prop** (rendered via
77
+ * `Autocomplete.GroupLabel`), matching shadcn's `<CommandGroup heading="…">`
78
+ * ergonomics. Base UI's control-level label is not exposed (there is none for a
79
+ * palette).
80
+ * 4. **`Command` renders the panel element; `Autocomplete.Root` renders none.**
81
+ * Root is a headless provider, so `Command` wraps a `data-slot="command"` panel
82
+ * `<div>` inside it. Standalone it carries `border`; `CommandDialog` drops the
83
+ * border (the Dialog surface owns the elevation).
84
+ * 5. **`CommandEmpty` is a SIBLING of `CommandList`, not a child.** cmdk nests
85
+ * `CommandEmpty` inside `CommandList`; Base UI's `Autocomplete.List` takes
86
+ * EITHER a render-function child OR nodes (never a mix), and `Autocomplete.Empty`
87
+ * is a peer of the list (same as `combobox.tsx`). So place `<CommandEmpty>` next
88
+ * to `<CommandList>` inside `<Command>`, and give `CommandList` only the
89
+ * render-function child.
90
+ * 6. **`CommandDialog` accepts a `trigger` element** (forwarded to the kit
91
+ * `DialogTrigger`'s `render` prop) so an uncontrolled palette works without a
92
+ * client handler — cmdk's `CommandDialog` is controlled-only. Pass `open`/
93
+ * `onOpenChange` for the controlled path instead. The `DialogTitle`/
94
+ * `DialogDescription` are present but `sr-only` (Dialog requires an accessible
95
+ * name; the palette shows no visible heading).
96
+ *
97
+ * ## `"use client"` — required
98
+ * Two reasons, same as `combobox.tsx`: every Base UI Autocomplete part module
99
+ * carries its own `'use client'` (they manage client state), and — decisively —
100
+ * `SearchRegular` from `@fluentui/react-icons` must stay inside a client boundary
101
+ * (its shared icon-sizing module calls `@griffel/react`'s client-only `__styles()`
102
+ * at module scope without a directive, breaking `next build` from an RSC). See
103
+ * conventions §9; `select.tsx`/`checkbox.tsx` are the precedents.
104
+ *
105
+ * ## data-slot note
106
+ * `Autocomplete.Root` renders no DOM element and its props type is strict (no
107
+ * `data-*` passthrough), so it carries no `data-slot`; the `Command` panel `<div>`
108
+ * this file adds does. Every part that renders an element does.
109
+ */
110
+ /**
111
+ * Root props, narrowed to a FLAT `items` array (grouping is done with static
112
+ * `CommandGroup` children, not the grouped-items form). Narrowing also sidesteps
113
+ * `Autocomplete.Root`'s two-overload signature, which a spread of the union
114
+ * `items` type can't resolve against.
115
+ */
116
+ type CommandProps<ItemValue> = Omit<Autocomplete.Root.Props<ItemValue>, "items"> & {
117
+ items?: readonly ItemValue[];
118
+ className?: string;
119
+ };
120
+ declare function Command<ItemValue>({ className, children, inline, open, ...props }: CommandProps<ItemValue>): react.JSX.Element;
121
+ /**
122
+ * Input — the palette search row (cmdk style): a leading `SearchRegular` icon and
123
+ * a borderless, full-width `Autocomplete.Input` sitting above a `border-b`
124
+ * separator. The input is transparent with no field-chrome (the panel is the
125
+ * surface). `className`/`ref`/`...props` forward to the inner `<input>`; style the
126
+ * row with `wrapperClassName`.
127
+ */
128
+ declare function CommandInput({ className, wrapperClassName, "aria-expanded": ariaExpanded, ...props }: ComponentProps<typeof Autocomplete.Input> & {
129
+ wrapperClassName?: string;
130
+ }): react.JSX.Element;
131
+ /**
132
+ * List — the scrollable items container (Base UI `Autocomplete.List`). `children`
133
+ * may be a render function `(item, index) => …` (the filtering API: pass `items`
134
+ * to `<Command>` and map each) or static `CommandItem` children. Scrolls at
135
+ * `overflow-y-auto`, capped at ~300px like cmdk.
136
+ */
137
+ declare function CommandList({ className, ...props }: ComponentProps<typeof Autocomplete.List>): react.JSX.Element;
138
+ /**
139
+ * Empty — the no-results state (Base UI `Autocomplete.Empty`). Base UI renders
140
+ * its children only when the filtered list is empty and keeps its root mounted to
141
+ * announce, so always place a `<CommandEmpty>` in the list rather than
142
+ * conditionally rendering it. Muted, centered caption.
143
+ */
144
+ declare function CommandEmpty({ className, ...props }: ComponentProps<typeof Autocomplete.Empty>): react.JSX.Element;
145
+ /**
146
+ * Group — a labelled set of items (Base UI `Autocomplete.Group`). Pass a
147
+ * `heading` for the muted caption (rendered via `Autocomplete.GroupLabel`),
148
+ * matching shadcn's `<CommandGroup heading="…">`. With built-in filtering, pass
149
+ * grouped `items` to `<Command>` so empty groups drop out automatically.
150
+ */
151
+ declare function CommandGroup({ className, heading, children, ...props }: ComponentProps<typeof Autocomplete.Group> & {
152
+ heading?: ReactNode;
153
+ }): react.JSX.Element;
154
+ /**
155
+ * Item — one command (Base UI `Autocomplete.Item`, a `<div role="option">`). Its
156
+ * `onClick` is the action handler: it fires on pointer-click AND on Enter when the
157
+ * item is highlighted (divergence 1). 32px row, 4px list radius, keyboard/hover
158
+ * highlight via `data-highlighted` → `bg-accent`, disabled items muted + inert.
159
+ */
160
+ declare function CommandItem({ className, ...props }: ComponentProps<typeof Autocomplete.Item>): react.JSX.Element;
161
+ /**
162
+ * Separator — a thin rule between groups (Base UI `Autocomplete.Separator`).
163
+ */
164
+ declare function CommandSeparator({ className, ...props }: ComponentProps<typeof Autocomplete.Separator>): react.JSX.Element;
165
+ /**
166
+ * Shortcut — a right-aligned keyboard-hint caption inside a `CommandItem` (cmdk
167
+ * parity; no Base UI part). `ml-auto` pushes it to the trailing edge.
168
+ */
169
+ declare function CommandShortcut({ className, ...props }: ComponentProps<"span">): react.JSX.Element;
170
+ /**
171
+ * Dialog — the palette in a modal (Base UI/kit `Dialog`). Composes the kit
172
+ * `Dialog` + `DialogContent` (`p-0 overflow-hidden`, no ✕) around a borderless
173
+ * `Command`. A `trigger` element opens it uncontrolled (forwarded to
174
+ * `DialogTrigger`'s `render`); pass `open`/`onOpenChange` for the controlled path
175
+ * (divergence 6). `title`/`description` are present for a11y but visually hidden.
176
+ */
177
+ declare function CommandDialog<ItemValue>({ title, description, trigger, className, children, open, defaultOpen, onOpenChange, modal, ...commandProps }: Omit<CommandProps<ItemValue>, "open" | "defaultOpen" | "onOpenChange"> & {
178
+ title?: string;
179
+ description?: string;
180
+ trigger?: ReactElement;
181
+ open?: ComponentProps<typeof Dialog>["open"];
182
+ defaultOpen?: ComponentProps<typeof Dialog>["defaultOpen"];
183
+ onOpenChange?: ComponentProps<typeof Dialog>["onOpenChange"];
184
+ modal?: ComponentProps<typeof Dialog>["modal"];
185
+ }): react.JSX.Element;
186
+
187
+ export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut };
@@ -0,0 +1,225 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete";
4
+ import { SearchRegular } from "@fluentui/react-icons";
5
+ import { cn } from "../../lib/utils.js";
6
+ import {
7
+ Dialog,
8
+ DialogContent,
9
+ DialogDescription,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ DialogTrigger
13
+ } from "./dialog.js";
14
+ function Command({
15
+ className,
16
+ children,
17
+ inline = true,
18
+ open = true,
19
+ ...props
20
+ }) {
21
+ return /* @__PURE__ */ jsx(AutocompletePrimitive.Root, { inline, open, ...props, children: /* @__PURE__ */ jsx(
22
+ "div",
23
+ {
24
+ "data-slot": "command",
25
+ className: cn(
26
+ "flex h-full w-full flex-col overflow-hidden rounded-md border bg-popover text-popover-foreground",
27
+ className
28
+ ),
29
+ children
30
+ }
31
+ ) });
32
+ }
33
+ function CommandInput({
34
+ className,
35
+ wrapperClassName,
36
+ "aria-expanded": ariaExpanded = true,
37
+ ...props
38
+ }) {
39
+ return /* @__PURE__ */ jsxs(
40
+ "div",
41
+ {
42
+ "data-slot": "command-input-wrapper",
43
+ className: cn(
44
+ "flex h-11 items-center gap-2 border-b px-3",
45
+ wrapperClassName
46
+ ),
47
+ children: [
48
+ /* @__PURE__ */ jsx(SearchRegular, { className: "size-4 shrink-0 text-muted-foreground" }),
49
+ /* @__PURE__ */ jsx(
50
+ AutocompletePrimitive.Input,
51
+ {
52
+ "data-slot": "command-input",
53
+ "aria-expanded": ariaExpanded,
54
+ className: cn(
55
+ "flex h-full w-full min-w-0 bg-transparent py-3 text-sm outline-none",
56
+ "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground",
57
+ "disabled:cursor-not-allowed disabled:opacity-50",
58
+ className
59
+ ),
60
+ ...props
61
+ }
62
+ )
63
+ ]
64
+ }
65
+ );
66
+ }
67
+ function CommandList({
68
+ className,
69
+ ...props
70
+ }) {
71
+ return /* @__PURE__ */ jsx(
72
+ AutocompletePrimitive.List,
73
+ {
74
+ "data-slot": "command-list",
75
+ className: cn(
76
+ "max-h-[300px] overflow-x-hidden overflow-y-auto overscroll-contain p-1",
77
+ className
78
+ ),
79
+ ...props
80
+ }
81
+ );
82
+ }
83
+ function CommandEmpty({
84
+ className,
85
+ ...props
86
+ }) {
87
+ return /* @__PURE__ */ jsx(
88
+ AutocompletePrimitive.Empty,
89
+ {
90
+ "data-slot": "command-empty",
91
+ className: cn("py-6 text-center text-sm text-muted-foreground", className),
92
+ ...props
93
+ }
94
+ );
95
+ }
96
+ function CommandGroup({
97
+ className,
98
+ heading,
99
+ children,
100
+ ...props
101
+ }) {
102
+ return /* @__PURE__ */ jsxs(
103
+ AutocompletePrimitive.Group,
104
+ {
105
+ "data-slot": "command-group",
106
+ className: cn("overflow-hidden p-1 text-foreground", className),
107
+ ...props,
108
+ children: [
109
+ heading != null && /* @__PURE__ */ jsx(
110
+ AutocompletePrimitive.GroupLabel,
111
+ {
112
+ "data-slot": "command-group-heading",
113
+ className: "px-2 py-1.5 text-xs font-bold text-foreground-2 select-none",
114
+ children: heading
115
+ }
116
+ ),
117
+ children
118
+ ]
119
+ }
120
+ );
121
+ }
122
+ function CommandItem({
123
+ className,
124
+ ...props
125
+ }) {
126
+ return /* @__PURE__ */ jsx(
127
+ AutocompletePrimitive.Item,
128
+ {
129
+ "data-slot": "command-item",
130
+ className: cn(
131
+ // layout — Fluent 32px row, 4px list radius, gap for a leading icon
132
+ "relative flex h-8 w-full cursor-default items-center gap-2 rounded-md px-2 text-sm outline-none select-none",
133
+ // rest text is NeutralForeground2, darkening to accent-foreground on
134
+ // highlight (Figma validation: Fluent list rows rest at #424242)
135
+ "text-foreground-2 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
136
+ // disabled item — muted + non-interactive
137
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
138
+ // icons a consumer puts inside the item
139
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg]:text-muted-foreground",
140
+ className
141
+ ),
142
+ ...props
143
+ }
144
+ );
145
+ }
146
+ function CommandSeparator({
147
+ className,
148
+ ...props
149
+ }) {
150
+ return /* @__PURE__ */ jsx(
151
+ AutocompletePrimitive.Separator,
152
+ {
153
+ "data-slot": "command-separator",
154
+ className: cn("pointer-events-none -mx-1 my-1 h-px bg-border", className),
155
+ ...props
156
+ }
157
+ );
158
+ }
159
+ function CommandShortcut({ className, ...props }) {
160
+ return /* @__PURE__ */ jsx(
161
+ "span",
162
+ {
163
+ "data-slot": "command-shortcut",
164
+ className: cn(
165
+ "ml-auto text-xs tracking-widest text-muted-foreground",
166
+ className
167
+ ),
168
+ ...props
169
+ }
170
+ );
171
+ }
172
+ function CommandDialog({
173
+ title = "Command Palette",
174
+ description = "Search for a command to run\u2026",
175
+ trigger,
176
+ className,
177
+ children,
178
+ // Dialog-level state. Both `Dialog` and `Autocomplete.Root` own `open`/
179
+ // `defaultOpen`/`onOpenChange`; here they mean the DIALOG. They are pulled out
180
+ // so they never reach the inner `Command`, whose list stays always-open inline.
181
+ open,
182
+ defaultOpen,
183
+ onOpenChange,
184
+ modal,
185
+ ...commandProps
186
+ }) {
187
+ return /* @__PURE__ */ jsxs(
188
+ Dialog,
189
+ {
190
+ open,
191
+ defaultOpen,
192
+ onOpenChange,
193
+ modal,
194
+ children: [
195
+ trigger != null && /* @__PURE__ */ jsx(DialogTrigger, { render: trigger }),
196
+ /* @__PURE__ */ jsxs(
197
+ DialogContent,
198
+ {
199
+ showCloseButton: false,
200
+ className: cn("overflow-hidden p-0", className),
201
+ children: [
202
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "sr-only", children: [
203
+ /* @__PURE__ */ jsx(DialogTitle, { children: title }),
204
+ /* @__PURE__ */ jsx(DialogDescription, { children: description })
205
+ ] }),
206
+ /* @__PURE__ */ jsx(Command, { className: "rounded-xl border-0", ...commandProps, children })
207
+ ]
208
+ }
209
+ )
210
+ ]
211
+ }
212
+ );
213
+ }
214
+ export {
215
+ Command,
216
+ CommandDialog,
217
+ CommandEmpty,
218
+ CommandGroup,
219
+ CommandInput,
220
+ CommandItem,
221
+ CommandList,
222
+ CommandSeparator,
223
+ CommandShortcut
224
+ };
225
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/command.tsx"],"sourcesContent":["\"use client\";\n\nimport { Autocomplete as AutocompletePrimitive } from \"@base-ui/react/autocomplete\";\nimport { SearchRegular } from \"@fluentui/react-icons\";\nimport type { ComponentProps, ReactElement, ReactNode } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"./dialog\";\n\n/**\n * Command — Fluent 2-styled, shadcn-API command palette (cmdk surface) built on\n * Base UI, with NO `cmdk` dependency.\n *\n * A command palette is an ALWAYS-VISIBLE filtered list whose items fire actions\n * (open a file, run a command) rather than persist a selection. That single\n * requirement drives the architecture decision below.\n *\n * ## Why `@base-ui/react/autocomplete` and NOT `@base-ui/react/combobox`\n * `combobox.tsx` (the filter-list family reference) documents the boundary: Base\n * UI's Combobox and Autocomplete share one `AriaCombobox` engine but differ on\n * *selection*. Command items **perform actions — they don't hold a value** — which\n * points squarely at Autocomplete, and two Autocomplete-only capabilities make it\n * the honest, least-fought fit (verified against `@base-ui/react@1.6.0` in\n * node_modules before committing):\n *\n * 1. **`selectionMode: 'none'`.** `Autocomplete.Root` is hard-wired to `'none'`\n * (it Omits `selectedValue`/`onValueChange`/`isItemEqualToValue`/… from its\n * props). There is no committed selection and no check indicator — exactly a\n * command list. A Combobox would force a value model we'd have to fight.\n * 2. **`inline` + `open` = a statically-open list with no popup.**\n * `Autocomplete.Root` exposes `inline`: *\"Whether the list is rendered inline\n * without using the component's own popup. Specify `open` unconditionally in\n * conjunction with this prop so the list is considered visible.\"* This is the\n * always-visible palette panel — `Autocomplete.List` renders directly, with no\n * `Portal`/`Positioner`/`Popup` and no floating-ui positioning to stub. Combobox\n * has the same `inline` flag (shared engine), but its selection model is dead\n * weight here, so Autocomplete is the cleaner primitive.\n *\n * Filtering rides the engine's built-in text filter (`mode=\"list\"`, the default):\n * pass `items` to `<Command>` and render the list with a render-function child,\n * exactly like `Combobox` (the built-in filter runs against the `items` prop). A\n * manual filtered-list fallback was NOT needed — the primitive supports the\n * inline-open + per-item `onClick` model natively.\n *\n * Keyboard is fully wired by the engine over the inline list: ArrowUp/Down roving\n * highlight (`data-highlighted`), Home/End to the ends, and Enter fires the\n * highlighted item — because `Autocomplete.Item.onClick` *\"fires when clicking the\n * item with the pointer, as well as when pressing Enter with the keyboard if the\n * item is highlighted when the Input or List element has focus.\"* That is this\n * kit's `CommandItem` action handler.\n *\n * ## Base UI mapping (conventions §9)\n * Namespace import of `@base-ui/react/autocomplete` (matching the export shape in\n * node_modules, like `select.tsx`/`combobox.tsx`). shadcn/cmdk part names mapped\n * onto Base UI's model:\n *\n * | Exported (cmdk name) | Base UI primitive |\n * | -------------------- | ------------------------------------------------------- |\n * | `Command` | `Autocomplete.Root` (`inline open`) + panel `<div>` |\n * | `CommandInput` | search-row `<div>` + `SearchRegular` + `Autocomplete.Input` |\n * | `CommandList` | `Autocomplete.List` (the scroll container) |\n * | `CommandEmpty` | `Autocomplete.Empty` |\n * | `CommandGroup` | `Autocomplete.Group` (+ `Autocomplete.GroupLabel` heading) |\n * | `CommandItem` | `Autocomplete.Item` |\n * | `CommandSeparator` | `Autocomplete.Separator` |\n * | `CommandShortcut` | plain `<span>` (cmdk parity — no Base UI part) |\n * | `CommandDialog` | kit `Dialog` + `DialogContent` wrapping a `Command` |\n *\n * ## Divergences vs cmdk / shadcn's Command (all deliberate)\n * 1. **Items fire `onClick`, not `onSelect`.** cmdk's `CommandItem` has an\n * `onSelect(value)` callback; Base UI's `Autocomplete.Item` exposes a native\n * `onClick` that fires on pointer-click AND Enter-when-highlighted (same effect,\n * honest primitive prop). No synthetic `onSelect`/`value`-string is faked.\n * 2. **Filtering keys off the `items` prop, not item DOM text.** cmdk filters by\n * scoring each item's rendered text; Base UI filters the `items` array with its\n * built-in filter. So a *filtering* palette passes `items` + the render-function\n * `CommandList` child (like `Combobox`). Static `CommandItem` children still\n * render, but only the `items` prop is filtered.\n * 3. **`CommandGroup` takes a `heading` prop** (rendered via\n * `Autocomplete.GroupLabel`), matching shadcn's `<CommandGroup heading=\"…\">`\n * ergonomics. Base UI's control-level label is not exposed (there is none for a\n * palette).\n * 4. **`Command` renders the panel element; `Autocomplete.Root` renders none.**\n * Root is a headless provider, so `Command` wraps a `data-slot=\"command\"` panel\n * `<div>` inside it. Standalone it carries `border`; `CommandDialog` drops the\n * border (the Dialog surface owns the elevation).\n * 5. **`CommandEmpty` is a SIBLING of `CommandList`, not a child.** cmdk nests\n * `CommandEmpty` inside `CommandList`; Base UI's `Autocomplete.List` takes\n * EITHER a render-function child OR nodes (never a mix), and `Autocomplete.Empty`\n * is a peer of the list (same as `combobox.tsx`). So place `<CommandEmpty>` next\n * to `<CommandList>` inside `<Command>`, and give `CommandList` only the\n * render-function child.\n * 6. **`CommandDialog` accepts a `trigger` element** (forwarded to the kit\n * `DialogTrigger`'s `render` prop) so an uncontrolled palette works without a\n * client handler — cmdk's `CommandDialog` is controlled-only. Pass `open`/\n * `onOpenChange` for the controlled path instead. The `DialogTitle`/\n * `DialogDescription` are present but `sr-only` (Dialog requires an accessible\n * name; the palette shows no visible heading).\n *\n * ## `\"use client\"` — required\n * Two reasons, same as `combobox.tsx`: every Base UI Autocomplete part module\n * carries its own `'use client'` (they manage client state), and — decisively —\n * `SearchRegular` from `@fluentui/react-icons` must stay inside a client boundary\n * (its shared icon-sizing module calls `@griffel/react`'s client-only `__styles()`\n * at module scope without a directive, breaking `next build` from an RSC). See\n * conventions §9; `select.tsx`/`checkbox.tsx` are the precedents.\n *\n * ## data-slot note\n * `Autocomplete.Root` renders no DOM element and its props type is strict (no\n * `data-*` passthrough), so it carries no `data-slot`; the `Command` panel `<div>`\n * this file adds does. Every part that renders an element does.\n */\n\n/**\n * Root props, narrowed to a FLAT `items` array (grouping is done with static\n * `CommandGroup` children, not the grouped-items form). Narrowing also sidesteps\n * `Autocomplete.Root`'s two-overload signature, which a spread of the union\n * `items` type can't resolve against.\n */\ntype CommandProps<ItemValue> = Omit<\n AutocompletePrimitive.Root.Props<ItemValue>,\n \"items\"\n> & {\n items?: readonly ItemValue[];\n className?: string;\n};\n\nfunction Command<ItemValue>({\n className,\n children,\n inline = true,\n open = true,\n ...props\n}: CommandProps<ItemValue>) {\n return (\n <AutocompletePrimitive.Root inline={inline} open={open} {...props}>\n <div\n data-slot=\"command\"\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded-md border bg-popover text-popover-foreground\",\n className\n )}\n >\n {children}\n </div>\n </AutocompletePrimitive.Root>\n );\n}\n\n/**\n * Input — the palette search row (cmdk style): a leading `SearchRegular` icon and\n * a borderless, full-width `Autocomplete.Input` sitting above a `border-b`\n * separator. The input is transparent with no field-chrome (the panel is the\n * surface). `className`/`ref`/`...props` forward to the inner `<input>`; style the\n * row with `wrapperClassName`.\n */\nfunction CommandInput({\n className,\n wrapperClassName,\n \"aria-expanded\": ariaExpanded = true,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.Input> & {\n wrapperClassName?: string;\n}) {\n return (\n <div\n data-slot=\"command-input-wrapper\"\n className={cn(\n \"flex h-11 items-center gap-2 border-b px-3\",\n wrapperClassName\n )}\n >\n <SearchRegular className=\"size-4 shrink-0 text-muted-foreground\" />\n <AutocompletePrimitive.Input\n data-slot=\"command-input\"\n // The list is always visible (inline open), so the combobox input is\n // permanently expanded. Base UI omits `aria-expanded` in inline mode, so\n // set it here to satisfy the WAI-ARIA combobox contract.\n aria-expanded={ariaExpanded}\n className={cn(\n \"flex h-full w-full min-w-0 bg-transparent py-3 text-sm outline-none\",\n \"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\n/**\n * List — the scrollable items container (Base UI `Autocomplete.List`). `children`\n * may be a render function `(item, index) => …` (the filtering API: pass `items`\n * to `<Command>` and map each) or static `CommandItem` children. Scrolls at\n * `overflow-y-auto`, capped at ~300px like cmdk.\n */\nfunction CommandList({\n className,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.List>) {\n return (\n <AutocompletePrimitive.List\n data-slot=\"command-list\"\n className={cn(\n \"max-h-[300px] overflow-x-hidden overflow-y-auto overscroll-contain p-1\",\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Empty — the no-results state (Base UI `Autocomplete.Empty`). Base UI renders\n * its children only when the filtered list is empty and keeps its root mounted to\n * announce, so always place a `<CommandEmpty>` in the list rather than\n * conditionally rendering it. Muted, centered caption.\n */\nfunction CommandEmpty({\n className,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.Empty>) {\n return (\n <AutocompletePrimitive.Empty\n data-slot=\"command-empty\"\n className={cn(\"py-6 text-center text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Group — a labelled set of items (Base UI `Autocomplete.Group`). Pass a\n * `heading` for the muted caption (rendered via `Autocomplete.GroupLabel`),\n * matching shadcn's `<CommandGroup heading=\"…\">`. With built-in filtering, pass\n * grouped `items` to `<Command>` so empty groups drop out automatically.\n */\nfunction CommandGroup({\n className,\n heading,\n children,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.Group> & {\n heading?: ReactNode;\n}) {\n return (\n <AutocompletePrimitive.Group\n data-slot=\"command-group\"\n className={cn(\"overflow-hidden p-1 text-foreground\", className)}\n {...props}\n >\n {heading != null && (\n <AutocompletePrimitive.GroupLabel\n data-slot=\"command-group-heading\"\n // Fluent Menu section header — Caption 1 Stronger (12px bold) in\n // NeutralForeground2, matching DropdownMenuLabel's pass-2 fix\n // (Figma validation pass 3 caught this as an intra-kit drift).\n className=\"px-2 py-1.5 text-xs font-bold text-foreground-2 select-none\"\n >\n {heading}\n </AutocompletePrimitive.GroupLabel>\n )}\n {children}\n </AutocompletePrimitive.Group>\n );\n}\n\n/**\n * Item — one command (Base UI `Autocomplete.Item`, a `<div role=\"option\">`). Its\n * `onClick` is the action handler: it fires on pointer-click AND on Enter when the\n * item is highlighted (divergence 1). 32px row, 4px list radius, keyboard/hover\n * highlight via `data-highlighted` → `bg-accent`, disabled items muted + inert.\n */\nfunction CommandItem({\n className,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.Item>) {\n return (\n <AutocompletePrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n // layout — Fluent 32px row, 4px list radius, gap for a leading icon\n \"relative flex h-8 w-full cursor-default items-center gap-2 rounded-md px-2 text-sm outline-none select-none\",\n // rest text is NeutralForeground2, darkening to accent-foreground on\n // highlight (Figma validation: Fluent list rows rest at #424242)\n \"text-foreground-2 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n // disabled item — muted + non-interactive\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n // icons a consumer puts inside the item\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg]:text-muted-foreground\",\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Separator — a thin rule between groups (Base UI `Autocomplete.Separator`).\n */\nfunction CommandSeparator({\n className,\n ...props\n}: ComponentProps<typeof AutocompletePrimitive.Separator>) {\n return (\n <AutocompletePrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"pointer-events-none -mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\n/**\n * Shortcut — a right-aligned keyboard-hint caption inside a `CommandItem` (cmdk\n * parity; no Base UI part). `ml-auto` pushes it to the trailing edge.\n */\nfunction CommandShortcut({ className, ...props }: ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\n \"ml-auto text-xs tracking-widest text-muted-foreground\",\n className\n )}\n {...props}\n />\n );\n}\n\n/**\n * Dialog — the palette in a modal (Base UI/kit `Dialog`). Composes the kit\n * `Dialog` + `DialogContent` (`p-0 overflow-hidden`, no ✕) around a borderless\n * `Command`. A `trigger` element opens it uncontrolled (forwarded to\n * `DialogTrigger`'s `render`); pass `open`/`onOpenChange` for the controlled path\n * (divergence 6). `title`/`description` are present for a11y but visually hidden.\n */\nfunction CommandDialog<ItemValue>({\n title = \"Command Palette\",\n description = \"Search for a command to run…\",\n trigger,\n className,\n children,\n // Dialog-level state. Both `Dialog` and `Autocomplete.Root` own `open`/\n // `defaultOpen`/`onOpenChange`; here they mean the DIALOG. They are pulled out\n // so they never reach the inner `Command`, whose list stays always-open inline.\n open,\n defaultOpen,\n onOpenChange,\n modal,\n ...commandProps\n}: Omit<CommandProps<ItemValue>, \"open\" | \"defaultOpen\" | \"onOpenChange\"> & {\n title?: string;\n description?: string;\n trigger?: ReactElement;\n open?: ComponentProps<typeof Dialog>[\"open\"];\n defaultOpen?: ComponentProps<typeof Dialog>[\"defaultOpen\"];\n onOpenChange?: ComponentProps<typeof Dialog>[\"onOpenChange\"];\n modal?: ComponentProps<typeof Dialog>[\"modal\"];\n}) {\n return (\n <Dialog\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n modal={modal}\n >\n {trigger != null && <DialogTrigger render={trigger} />}\n <DialogContent\n showCloseButton={false}\n className={cn(\"overflow-hidden p-0\", className)}\n >\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <Command className=\"rounded-xl border-0\" {...commandProps}>\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandSeparator,\n CommandShortcut,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,117 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps } from 'react';
3
+ import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
4
+
5
+ /**
6
+ * Dialog — Fluent 2-styled, shadcn-API modal dialog.
7
+ *
8
+ * Follows the overlay patterns set by `select.tsx` (portal → popup surface,
9
+ * `data-starting-style`/`data-ending-style` motion with token durations and
10
+ * easings) with dialog-specific elevation: `shadow-64` + `rounded-xl`
11
+ * (conventions §3.4/§3.6 — dialogs are the kit's highest floating surface).
12
+ *
13
+ * ## Base UI mapping (conventions §9)
14
+ * Behavior — focus trapping, portalling, open/close state, scroll locking,
15
+ * outside-press and Escape dismissal — genuinely needs a primitive, so the
16
+ * parts wrap `@base-ui/react/dialog` (namespace import, matching the actual
17
+ * export shape in node_modules, exactly like `select.tsx`). shadcn part names
18
+ * are mapped onto Base UI's model:
19
+ *
20
+ * | Exported (shadcn name) | Base UI primitive |
21
+ * | ---------------------- | --------------------------------------------------- |
22
+ * | `Dialog` | `Dialog.Root` |
23
+ * | `DialogTrigger` | `Dialog.Trigger` |
24
+ * | `DialogPortal` | `Dialog.Portal` |
25
+ * | `DialogOverlay` | `Dialog.Backdrop` ← see divergence 1 |
26
+ * | `DialogContent` | `Dialog.Portal` + `Dialog.Backdrop` + `Dialog.Popup` (+ built-in ✕ `Dialog.Close`) |
27
+ * | `DialogHeader` | plain `<div>` (shadcn parity — no Base UI part) |
28
+ * | `DialogFooter` | plain `<div>` (shadcn parity — no Base UI part) |
29
+ * | `DialogTitle` | `Dialog.Title` (renders `<h2>`) |
30
+ * | `DialogDescription` | `Dialog.Description` (renders `<p>`) |
31
+ * | `DialogClose` | `Dialog.Close` |
32
+ *
33
+ * Base UI wires the popup's `aria-labelledby` to `DialogTitle` and
34
+ * `aria-describedby` to `DialogDescription` automatically — no manual ids.
35
+ *
36
+ * ## Divergences from the shadcn/Radix Dialog API (all deliberate)
37
+ * 1. **`DialogOverlay` wraps Base UI `Dialog.Backdrop`, not a Radix `Overlay`.**
38
+ * Same role (the dimming layer under the popup), different primitive name.
39
+ * Note Base UI skips rendering the backdrop for *nested* dialogs unless
40
+ * `forceRender` is set — parent smoke already covers the viewport.
41
+ * 2. **`modal` defaults to `true`** (focus trap + page scroll lock + outside
42
+ * pointer interactions disabled), matching Radix. Base UI adds a third mode,
43
+ * `modal="trap-focus"` (trap focus without scroll lock), which passes
44
+ * straight through.
45
+ * 3. **`DialogContent` composes `Portal` + `Backdrop` + `Popup`** and appends a
46
+ * top-right ✕ close button (shadcn parity — shadcn composes the same three
47
+ * Radix parts). Pass `showCloseButton={false}` to omit the ✕ (matching
48
+ * current shadcn's `DialogContent` prop).
49
+ * 4. **No `asChild`.** Base UI composes via the `render` prop instead — e.g.
50
+ * `<DialogTrigger render={<Button variant="secondary" />}>Open</DialogTrigger>`.
51
+ * Every part forwards it.
52
+ * 5. **`onOpenChange(open, eventDetails)`** — Base UI passes a second
53
+ * `eventDetails` argument (reason + native event). The first argument is the
54
+ * boolean Radix passes, so existing shadcn call sites work unchanged.
55
+ * 6. **`Dialog.Viewport` is not re-exported.** It's a Base UI layout helper for
56
+ * scrollable full-viewport dialogs with no shadcn equivalent; the kit centers
57
+ * the popup with fixed positioning like shadcn. Compose it manually from
58
+ * `@base-ui/react/dialog` if you need it.
59
+ *
60
+ * ## Smoke layer (`bg-black/40`) — sanctioned hardcoded color
61
+ * The backdrop is black at 40% opacity in *both* themes, per Fluent's smoke
62
+ * layer (it dims light and dark surfaces alike). Conventions §3.7 bans
63
+ * hardcoded colors so a shade can't drift from the token set, but the smoke
64
+ * layer is intentionally theme-invariant — there is no token for it and
65
+ * inventing one would imply it re-points in dark mode, which it must not.
66
+ * `bg-black/40` is the sanctioned exception here.
67
+ *
68
+ * ## Motion note — the popup zoom animates `scale`, not `transform`
69
+ * Tailwind v4 emits `translate-*` and `scale-*` as the independent CSS
70
+ * `translate:`/`scale:` properties (not a shared `transform:`), so the
71
+ * -50%/-50% centering translate and the open/close zoom coexist without
72
+ * clobbering each other — but it also means the popup's transition must target
73
+ * `transition-[opacity,scale]` (a `transition-[transform,opacity]` list would
74
+ * leave the zoom snapping — every popup in the kit uses `[opacity,scale]` for
75
+ * this reason).
76
+ *
77
+ * ## `"use client"` — required
78
+ * Two reasons. The Base UI Dialog parts manage client state (each part module
79
+ * carries its own `'use client'`), and — decisively — `DismissRegular` from
80
+ * `@fluentui/react-icons` must stay inside a client boundary: the package's
81
+ * shared icon-sizing module calls `@griffel/react`'s client-only `__styles()`
82
+ * at module scope without its own directive, so importing an icon from a
83
+ * Server Component breaks `next build`. See conventions §9; `select.tsx` and
84
+ * `checkbox.tsx` are the precedents.
85
+ *
86
+ * ## data-slot note
87
+ * `Dialog` (Root) renders no DOM element of its own, so it carries no
88
+ * `data-slot`. Every part that renders an element does — `dialog-trigger`,
89
+ * `dialog-overlay`, `dialog-content`, etc.
90
+ */
91
+ declare function Dialog<Payload>(props: Dialog$1.Root.Props<Payload>): react.JSX.Element;
92
+ declare function DialogTrigger({ className, ...props }: ComponentProps<typeof Dialog$1.Trigger>): react.JSX.Element;
93
+ declare function DialogPortal(props: ComponentProps<typeof Dialog$1.Portal>): react.JSX.Element;
94
+ /**
95
+ * Overlay — Fluent's smoke layer: black at 40% opacity over the full viewport
96
+ * in both themes (see the smoke-layer note above), fading in/out via Base UI's
97
+ * `data-starting-style`/`data-ending-style` hooks with token durations/easings.
98
+ */
99
+ declare function DialogOverlay({ className, ...props }: ComponentProps<typeof Dialog$1.Backdrop>): react.JSX.Element;
100
+ /**
101
+ * Content — the modal surface: `Portal` → `Backdrop` → `Popup`, centered with
102
+ * fixed positioning, on `bg-background` with `rounded-xl` + `shadow-64`
103
+ * (dialog elevation, conventions §3.6). A fade + slight zoom rides the
104
+ * `data-starting-style`/`data-ending-style` hooks; the zoom animates the CSS
105
+ * `scale` property (see the motion note above). The built-in ✕ close button
106
+ * sits top-right (`showCloseButton={false}` omits it).
107
+ */
108
+ declare function DialogContent({ className, children, showCloseButton, ...props }: ComponentProps<typeof Dialog$1.Popup> & {
109
+ showCloseButton?: boolean;
110
+ }): react.JSX.Element;
111
+ declare function DialogHeader({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
112
+ declare function DialogFooter({ className, ...props }: ComponentProps<"div">): react.JSX.Element;
113
+ declare function DialogTitle({ className, ...props }: ComponentProps<typeof Dialog$1.Title>): react.JSX.Element;
114
+ declare function DialogDescription({ className, ...props }: ComponentProps<typeof Dialog$1.Description>): react.JSX.Element;
115
+ declare function DialogClose({ className, ...props }: ComponentProps<typeof Dialog$1.Close>): react.JSX.Element;
116
+
117
+ export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger };