@evinvest/uikit 0.27.0 → 0.29.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.
package/README.md CHANGED
@@ -540,8 +540,11 @@ import { Table, TableBody, TableCard, TableCell, TableHead, TableHeader, TableRo
540
540
  ```
541
541
  ### Command
542
542
 
543
- By default `Command` filters its items on the client: a case-insensitive
544
- substring match of the typed query against each `CommandItem`'s `value`. When the
543
+ By default `Command` filters and ranks its items on the client, as fzf does:
544
+ each space-separated term of the query must appear in the item's `value` as a
545
+ case-insensitive subsequence, and runs and word starts rank above scattered
546
+ matches. Ties keep your order. Ranking is CSS `order` within `CommandList` /
547
+ `CommandGroup`, which the arrows follow. When the
545
548
  rows are already the answer to the query — a search endpoint, a ranking of your
546
549
  own — pass `shouldFilter={false}` and own the query state:
547
550
 
@@ -605,8 +608,7 @@ element across the two ports.
605
608
  - **Tier C — overlay (13):** tooltip, popover, hover-card, dropdown-menu,
606
609
  context-menu, menubar, navigation-menu, dialog, alert-dialog, sheet, drawer,
607
610
  select, command.
608
- - **Tier D — engines (6):** chart, calendar, date-time-picker, sonner (toaster),
609
- form, resizable.
611
+ - **Tier D — engines (4):** calendar, date-time-picker, sonner (toaster), form.
610
612
  - **Site chrome (7):** header (marketing / compact density, plus `hideNav`),
611
613
  footer, logo, and the shared status pages — `StatusScreen` with the `NotFound`
612
614
  / `Forbidden` / `ServerError` presets (404 / 403 / 500). The kit ships no
@@ -670,10 +672,6 @@ measuring needs host-only `web-sys`). Known gaps:
670
672
  `onkeydown` — and `AlertDialog`, `Sheet`, `Drawer`, `ContextMenu`, `Menubar`,
671
673
  `NavigationMenu`, `HoverCard`, `Tooltip`, `InfoTip`, `DateTimePicker`,
672
674
  `CommandDialog` and `DropdownMenuSub` are not on the stack yet.
673
- - **chart:** the recharts plotting engine is not bundled. `ChartContainer` is a
674
- themed SVG host (emits `--color-*` from its config); `ChartTooltipContent` /
675
- `ChartLegendContent` are presentational and take explicit items. Draw series
676
- yourself inside the container.
677
675
  - **terminal:** no plotting engine and no state. `TerminalChart` is a sized
678
676
  `relative` host that hands its element out (`ref` in TS, `id` in Rust) for a
679
677
  consumer-chosen charting library to mount into; `OrderForm` only prevents the
@@ -719,9 +717,8 @@ measuring needs host-only `web-sys`). Known gaps:
719
717
  onto an arbitrary child (no `Slot`), so it publishes them as a
720
718
  `FormControlContext` that `Input`/`Textarea` consume; wrap a bare element and
721
719
  you wire them yourself.
722
- - **resizable / carousel:** pointer-drag physics are TS-only (keyboard in Rust
723
- for resizable; prev/next + keyboard for carousel). Embla momentum is not
724
- reproduced.
720
+ - **carousel:** pointer-drag physics are TS-only (prev/next + keyboard in
721
+ Rust). Embla momentum is not reproduced.
725
722
  - **drawer:** the Vaul-style enter/exit motion is shared by both ports via
726
723
  `motion.css` (inlined into `tokens.css`), keyed on `data-slot` +
727
724
  `data-vaul-drawer-direction` + `data-state`; the panel stays mounted on close
@@ -0,0 +1,42 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import {
4
+ Empty,
5
+ EmptyContent,
6
+ EmptyDescription,
7
+ EmptyHeader,
8
+ EmptyMedia,
9
+ EmptyTitle
10
+ } from "./empty.js";
11
+ function AuthWall({ title, description, children, ...props }) {
12
+ return /* @__PURE__ */ jsxs(Empty, { ...props, children: [
13
+ /* @__PURE__ */ jsxs(EmptyHeader, { children: [
14
+ /* @__PURE__ */ jsx(EmptyMedia, { variant: "icon", children: /* @__PURE__ */ jsx(LockIcon, {}) }),
15
+ /* @__PURE__ */ jsx(EmptyTitle, { children: title }),
16
+ /* @__PURE__ */ jsx(EmptyDescription, { children: description })
17
+ ] }),
18
+ /* @__PURE__ */ jsx(EmptyContent, { children })
19
+ ] });
20
+ }
21
+ function LockIcon() {
22
+ return /* @__PURE__ */ jsxs(
23
+ "svg",
24
+ {
25
+ viewBox: "0 0 24 24",
26
+ fill: "none",
27
+ stroke: "currentColor",
28
+ strokeWidth: 2,
29
+ strokeLinecap: "round",
30
+ strokeLinejoin: "round",
31
+ "aria-hidden": true,
32
+ children: [
33
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
34
+ /* @__PURE__ */ jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
35
+ ]
36
+ }
37
+ );
38
+ }
39
+ export {
40
+ AuthWall
41
+ };
42
+ //# sourceMappingURL=auth-wall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/auth-wall.tsx"],"sourcesContent":["import * as React from \"react\";\nimport {\n Empty,\n EmptyContent,\n EmptyDescription,\n EmptyHeader,\n EmptyMedia,\n EmptyTitle,\n} from \"./empty\";\n\n/**\n * What a section shows a caller it is not for: an `Empty` with a lock, a title, why,\n * and an action slot. A guest gets the sign-in button there; a signed-in caller\n * without the permission gets no action, or the host's own (request access).\n */\nexport interface AuthWallProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n title: React.ReactNode;\n description: React.ReactNode;\n}\n\nexport function AuthWall({ title, description, children, ...props }: AuthWallProps) {\n return (\n <Empty {...props}>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <LockIcon />\n </EmptyMedia>\n <EmptyTitle>{title}</EmptyTitle>\n <EmptyDescription>{description}</EmptyDescription>\n </EmptyHeader>\n <EmptyContent>{children}</EmptyContent>\n </Empty>\n );\n}\n\n// lucide `lock`, inlined so the kit keeps its zero-icon-dep footprint.\nfunction LockIcon() {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n );\n}\n"],"mappings":"AAuBM,SAEI,KAFJ;AAvBN,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYA,SAAS,SAAS,EAAE,OAAO,aAAa,UAAU,GAAG,MAAM,GAAkB;AAClF,SACE,qBAAC,SAAO,GAAG,OACT;AAAA,yBAAC,eACC;AAAA,0BAAC,cAAW,SAAQ,QAClB,8BAAC,YAAS,GACZ;AAAA,MACA,oBAAC,cAAY,iBAAM;AAAA,MACnB,oBAAC,oBAAkB,uBAAY;AAAA,OACjC;AAAA,IACA,oBAAC,gBAAc,UAAS;AAAA,KAC1B;AAEJ;AAGA,SAAS,WAAW;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEX;AAAA,4BAAC,UAAK,GAAE,KAAI,GAAE,MAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,IAAG,KAAI;AAAA,QACxD,oBAAC,UAAK,GAAE,4BAA2B;AAAA;AAAA;AAAA,EACrC;AAEJ;","names":[]}
@@ -4,6 +4,7 @@ import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { CHECKBOX_BASE, CHECKBOX_INDICATOR } from "../generated/checkbox.js";
6
6
  import { useControllableState } from "../primitives/use-controllable-state.js";
7
+ import { markLabelable } from "../primitives/labelable.js";
7
8
  import { useFieldControlId } from "./field-context.js";
8
9
  function Checkbox({
9
10
  className,
@@ -62,6 +63,7 @@ function Checkbox({
62
63
  }
63
64
  );
64
65
  }
66
+ markLabelable(Checkbox);
65
67
  export {
66
68
  Checkbox
67
69
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { CHECKBOX_BASE, CHECKBOX_INDICATOR } from \"../generated/checkbox\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<\"button\">, \"onChange\"> {\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport function Checkbox({\n className,\n checked,\n defaultChecked = false,\n onCheckedChange,\n onClick,\n disabled,\n id,\n ...props\n}: CheckboxProps) {\n const [on, setOn] = useControllableState<boolean>({\n ...(checked !== undefined ? { value: checked } : {}),\n defaultValue: defaultChecked,\n ...(onCheckedChange ? { onChange: onCheckedChange } : {}),\n });\n const state = on ? \"checked\" : \"unchecked\";\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n data-slot=\"checkbox\"\n data-state={state}\n aria-checked={on}\n disabled={disabled}\n id={useFieldControlId(id)}\n className={cn(CHECKBOX_BASE, className)}\n onClick={(e) => {\n onClick?.(e);\n setOn(!on);\n }}\n {...props}\n >\n {on && (\n <span\n data-slot=\"checkbox-indicator\"\n className={CHECKBOX_INDICATOR}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n )}\n </button>\n );\n}\n"],"mappings":";AA+DY;AA7DZ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,eAAe,0BAA0B;AAClD,SAAS,4BAA4B;AACrC,SAAS,yBAAyB;AAS3B,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,CAAC,IAAI,KAAK,IAAI,qBAA8B;AAAA,IAChD,GAAI,YAAY,SAAY,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,IAClD,cAAc;AAAA,IACd,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,EACzD,CAAC;AACD,QAAM,QAAQ,KAAK,YAAY;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd;AAAA,MACA,IAAI,kBAAkB,EAAE;AAAA,MACxB,WAAW,GAAG,eAAe,SAAS;AAAA,MACtC,SAAS,CAAC,MAAM;AACd,kBAAU,CAAC;AACX,cAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACC,GAAG;AAAA,MAEH,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,UAEX;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,QAAO;AAAA,cACP,aAAY;AAAA,cACZ,eAAc;AAAA,cACd,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,eAAY;AAAA,cAEZ,8BAAC,UAAK,GAAE,mBAAkB;AAAA;AAAA,UAC5B;AAAA;AAAA,MACF;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { CHECKBOX_BASE, CHECKBOX_INDICATOR } from \"../generated/checkbox\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport interface CheckboxProps\n extends Omit<React.ComponentProps<\"button\">, \"onChange\"> {\n checked?: boolean;\n defaultChecked?: boolean;\n onCheckedChange?: (checked: boolean) => void;\n}\n\nexport function Checkbox({\n className,\n checked,\n defaultChecked = false,\n onCheckedChange,\n onClick,\n disabled,\n id,\n ...props\n}: CheckboxProps) {\n const [on, setOn] = useControllableState<boolean>({\n ...(checked !== undefined ? { value: checked } : {}),\n defaultValue: defaultChecked,\n ...(onCheckedChange ? { onChange: onCheckedChange } : {}),\n });\n const state = on ? \"checked\" : \"unchecked\";\n return (\n <button\n type=\"button\"\n role=\"checkbox\"\n data-slot=\"checkbox\"\n data-state={state}\n aria-checked={on}\n disabled={disabled}\n id={useFieldControlId(id)}\n className={cn(CHECKBOX_BASE, className)}\n onClick={(e) => {\n onClick?.(e);\n setOn(!on);\n }}\n {...props}\n >\n {on && (\n <span\n data-slot=\"checkbox-indicator\"\n className={CHECKBOX_INDICATOR}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-3.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </span>\n )}\n </button>\n );\n}\n\nmarkLabelable(Checkbox);\n"],"mappings":";AAgEY;AA9DZ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,eAAe,0BAA0B;AAClD,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAS3B,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkB;AAChB,QAAM,CAAC,IAAI,KAAK,IAAI,qBAA8B;AAAA,IAChD,GAAI,YAAY,SAAY,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,IAClD,cAAc;AAAA,IACd,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,EACzD,CAAC;AACD,QAAM,QAAQ,KAAK,YAAY;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd;AAAA,MACA,IAAI,kBAAkB,EAAE;AAAA,MACxB,WAAW,GAAG,eAAe,SAAS;AAAA,MACtC,SAAS,CAAC,MAAM;AACd,kBAAU,CAAC;AACX,cAAM,CAAC,EAAE;AAAA,MACX;AAAA,MACC,GAAG;AAAA,MAEH,gBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,UAEX;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,QAAO;AAAA,cACP,aAAY;AAAA,cACZ,eAAc;AAAA,cACd,gBAAe;AAAA,cACf,WAAU;AAAA,cACV,eAAY;AAAA,cAEZ,8BAAC,UAAK,GAAE,mBAAkB;AAAA;AAAA,UAC5B;AAAA;AAAA,MACF;AAAA;AAAA,EAEJ;AAEJ;AAEA,cAAc,QAAQ;","names":[]}
@@ -28,6 +28,39 @@ function useCommand() {
28
28
  return ctx;
29
29
  }
30
30
  const ITEM_SELECTOR = '[data-slot="command-item"]:not([aria-disabled="true"])';
31
+ function fuzzyScore(value, query) {
32
+ const hay = [...value.toLowerCase()];
33
+ let total = 0;
34
+ for (const term of query.split(/\s+/)) {
35
+ const needle = [...term];
36
+ let k = 0;
37
+ let end = -1;
38
+ for (let i = 0; i < hay.length; i++) {
39
+ if (hay[i] === needle[k] && ++k === needle.length) {
40
+ end = i;
41
+ break;
42
+ }
43
+ }
44
+ if (end < 0) return null;
45
+ let start = end;
46
+ for (let i = end, j = needle.length - 1; j >= 0; i--) {
47
+ if (hay[i] === needle[j]) {
48
+ start = i;
49
+ j--;
50
+ }
51
+ }
52
+ let prev = -1;
53
+ for (let i = start, j = 0; j < needle.length; i++) {
54
+ if (hay[i] !== needle[j]) continue;
55
+ total += 16;
56
+ if (i === 0 || !/[\p{L}\p{N}]/u.test(hay[i - 1])) total += 8;
57
+ if (j > 0) total += i === prev + 1 ? 4 : -(3 + (i - prev - 2));
58
+ prev = i;
59
+ j++;
60
+ }
61
+ }
62
+ return total;
63
+ }
31
64
  function useCommandNavigation(query, handlers) {
32
65
  const rootRef = React.useRef(null);
33
66
  const [activeId, setActiveId] = React.useState(null);
@@ -35,8 +68,15 @@ function useCommandNavigation(query, handlers) {
35
68
  const enabledItems = React.useCallback(() => {
36
69
  const root = rootRef.current;
37
70
  if (!root) return [];
38
- return [...root.querySelectorAll(ITEM_SELECTOR)].filter(
39
- (el) => el.closest('[data-slot="command"]') === root
71
+ const byParent = /* @__PURE__ */ new Map();
72
+ for (const el of root.querySelectorAll(ITEM_SELECTOR)) {
73
+ if (el.closest('[data-slot="command"]') !== root) continue;
74
+ const rows = byParent.get(el.parentElement) ?? [];
75
+ rows.push(el);
76
+ byParent.set(el.parentElement, rows);
77
+ }
78
+ return [...byParent.values()].flatMap(
79
+ (rows) => rows.sort((a, b) => Number(a.style.order) - Number(b.style.order))
40
80
  );
41
81
  }, []);
42
82
  React.useEffect(() => {
@@ -131,13 +171,17 @@ function Command({
131
171
  });
132
172
  }, []);
133
173
  const query = currentSearch.trim().toLowerCase();
134
- const matches = React.useCallback(
135
- (value) => !shouldFilter || query === "" || value.toLowerCase().includes(query),
174
+ const order = React.useCallback(
175
+ (value) => {
176
+ if (!shouldFilter || query === "") return 0;
177
+ const score = fuzzyScore(value, query);
178
+ return score === null ? null : -score;
179
+ },
136
180
  [query, shouldFilter]
137
181
  );
138
182
  const hasMatches = React.useMemo(
139
- () => [...items.values()].some(matches),
140
- [items, matches]
183
+ () => [...items.values()].some((value) => order(value) !== null),
184
+ [items, order]
141
185
  );
142
186
  const listId = React.useId();
143
187
  const nav = useCommandNavigation(query, handlers);
@@ -150,7 +194,7 @@ function Command({
150
194
  registerItem,
151
195
  unregisterItem,
152
196
  query,
153
- matches,
197
+ order,
154
198
  hasMatches,
155
199
  listId,
156
200
  activeId: nav.activeId,
@@ -312,7 +356,7 @@ function CommandItem({
312
356
  children,
313
357
  ...props
314
358
  }) {
315
- const { registerItem, unregisterItem, matches, activeId, activate } = useCommand();
359
+ const { registerItem, unregisterItem, order, activeId, activate } = useCommand();
316
360
  const reactId = React.useId();
317
361
  const id = idProp ?? reactId;
318
362
  const onSelectRef = React.useRef(onSelect);
@@ -323,7 +367,8 @@ function CommandItem({
323
367
  registerItem(id, value, () => onSelectRef.current?.(value));
324
368
  return () => unregisterItem(id);
325
369
  }, [id, value, registerItem, unregisterItem]);
326
- if (!matches(value)) return null;
370
+ const rank = order(value);
371
+ if (rank === null) return null;
327
372
  const selected = activeId === id;
328
373
  return /* @__PURE__ */ jsx(
329
374
  "div",
@@ -345,6 +390,7 @@ function CommandItem({
345
390
  },
346
391
  className: cn(COMMAND_ITEM, className),
347
392
  ...props,
393
+ style: { ...props.style, order: rank },
348
394
  children
349
395
  }
350
396
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport {\n COMMAND_DIALOG_COMMAND,\n COMMAND_DIALOG_CONTENT,\n COMMAND_DIALOG_OVERLAY,\n COMMAND_EMPTY,\n COMMAND_GROUP,\n COMMAND_INPUT,\n COMMAND_INPUT_WRAPPER,\n COMMAND_ITEM,\n COMMAND_LIST,\n COMMAND_ROOT,\n COMMAND_SEPARATOR,\n COMMAND_SHORTCUT,\n} from \"../generated/command\";\n\ninterface CommandContextValue {\n search: string;\n setSearch: (next: string) => void;\n /// Registers a `CommandItem`'s value so `CommandEmpty` can tell \"nothing\n /// matched\" from \"nothing is here\", and its select handler so Enter can fire\n /// it. Items register even while filtered out.\n registerItem: (id: string, value: string, select: () => void) => void;\n unregisterItem: (id: string) => void;\n /// The active query: trimmed, so blank input is not a search, and lowercased.\n /// Shared by the item filter and the empty-state gate so the two can never\n /// disagree.\n query: string;\n matches: (value: string) => boolean;\n hasMatches: boolean;\n listId: string;\n /// DOM id of the highlighted item. Focus stays in the input and points here\n /// through `aria-activedescendant` (the ARIA combobox pattern), so items\n /// never need to be tabbable.\n activeId: string | null;\n activate: (id: string) => void;\n}\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null);\n\nfunction useCommand(): CommandContextValue {\n const ctx = React.useContext(CommandContext);\n if (!ctx) throw new Error(\"Command parts must be used within <Command>\");\n return ctx;\n}\n\nexport interface CommandProps extends React.ComponentProps<\"div\"> {\n search?: string;\n defaultSearch?: string;\n onSearchChange?: (search: string) => void;\n /// `false` hands filtering to the caller (e.g. rows that are a server\n /// response to the query): every mounted item renders, in the caller's\n /// order, and `CommandEmpty` counts mounted items. Defaults to `true`.\n shouldFilter?: boolean;\n}\n\nconst ITEM_SELECTOR = '[data-slot=\"command-item\"]:not([aria-disabled=\"true\"])';\n\n/// Keyboard highlight over the items currently in the DOM, in DOM order, so it\n/// follows whatever the filter (or the caller) rendered rather than the order\n/// items happened to register in.\nfunction useCommandNavigation(\n query: string,\n handlers: React.RefObject<Map<string, () => void>>,\n) {\n const rootRef = React.useRef<HTMLDivElement>(null);\n const [activeId, setActiveId] = React.useState<string | null>(null);\n // Until the user moves the highlight it tracks the first row, so results\n // that arrive after the keystroke (or reorder under it) put Enter on the top\n // hit. Once moved, it sticks for as long as that row stays.\n const userMoved = React.useRef(false);\n\n const enabledItems = React.useCallback((): HTMLElement[] => {\n const root = rootRef.current;\n if (!root) return [];\n return [...root.querySelectorAll<HTMLElement>(ITEM_SELECTOR)].filter(\n (el) => el.closest('[data-slot=\"command\"]') === root,\n );\n }, []);\n\n React.useEffect(() => {\n userMoved.current = false;\n }, [query]);\n\n // No deps on purpose: the caller can change the rendered rows on any render;\n // setting an unchanged id bails out without re-rendering.\n React.useEffect(() => {\n const els = enabledItems();\n const moved = userMoved.current;\n setActiveId((prev) =>\n moved && prev !== null && els.some((el) => el.id === prev)\n ? prev\n : (els[0]?.id ?? null),\n );\n });\n\n const activate = React.useCallback((id: string) => {\n userMoved.current = true;\n setActiveId(id);\n }, []);\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.nativeEvent.isComposing) return;\n // Only the search field drives the list: Enter or an arrow on a button\n // inside Command (an action in `CommandEmpty`, a footer) stays its own.\n const field = event.target as HTMLElement;\n if (field.dataset[\"slot\"] !== \"command-input\") return;\n const els = enabledItems();\n const current = els.findIndex((el) => el.id === activeId);\n const last = els.length - 1;\n let next: number;\n switch (event.key) {\n case \"ArrowDown\":\n next = current < 0 ? 0 : Math.min(current + 1, last);\n break;\n case \"ArrowUp\":\n next = current < 0 ? last : Math.max(current - 1, 0);\n break;\n // Home / End are left to the caret: the field is editable (ARIA APG\n // editable combobox).\n case \"Enter\": {\n const select =\n activeId !== null && current >= 0\n ? handlers.current.get(activeId)\n : undefined;\n if (select) {\n event.preventDefault();\n select();\n }\n return;\n }\n default:\n return;\n }\n const target = els[next];\n if (!target) return;\n event.preventDefault();\n activate(target.id);\n // Optional call: jsdom implements no `scrollIntoView`.\n target.scrollIntoView?.({ block: \"nearest\" });\n };\n\n return { rootRef, activeId, activate, onKeyDown };\n}\n\nexport function Command({\n className,\n search,\n defaultSearch = \"\",\n onSearchChange,\n shouldFilter = true,\n children,\n ref,\n onKeyDown,\n ...props\n}: CommandProps) {\n const [currentSearch, setSearch] = useControllableState<string>({\n ...(search !== undefined ? { value: search } : {}),\n defaultValue: defaultSearch,\n ...(onSearchChange ? { onChange: onSearchChange } : {}),\n });\n\n const [items, setItems] = React.useState<ReadonlyMap<string, string>>(\n () => new Map(),\n );\n const handlers = React.useRef(new Map<string, () => void>());\n const registerItem = React.useCallback(\n (id: string, value: string, select: () => void) => {\n handlers.current.set(id, select);\n setItems((prev) => {\n if (prev.get(id) === value) return prev;\n const next = new Map(prev);\n next.set(id, value);\n return next;\n });\n },\n [],\n );\n const unregisterItem = React.useCallback((id: string) => {\n handlers.current.delete(id);\n setItems((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const query = currentSearch.trim().toLowerCase();\n const matches = React.useCallback(\n (value: string) =>\n !shouldFilter || query === \"\" || value.toLowerCase().includes(query),\n [query, shouldFilter],\n );\n const hasMatches = React.useMemo(\n () => [...items.values()].some(matches),\n [items, matches],\n );\n\n const listId = React.useId();\n const nav = useCommandNavigation(query, handlers);\n\n return (\n <CommandContext.Provider\n value={{\n search: currentSearch,\n setSearch,\n registerItem,\n unregisterItem,\n query,\n matches,\n hasMatches,\n listId,\n activeId: nav.activeId,\n activate: nav.activate,\n }}\n >\n <div\n data-slot=\"command\"\n ref={mergeRefs(nav.rootRef, ref)}\n className={cn(COMMAND_ROOT, className)}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (!e.defaultPrevented) nav.onKeyDown(e);\n }}\n {...props}\n >\n {children}\n </div>\n </CommandContext.Provider>\n );\n}\n\nexport interface CommandDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /// See `CommandProps.shouldFilter`.\n shouldFilter?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function CommandDialog({\n open,\n defaultOpen = false,\n onOpenChange,\n shouldFilter = true,\n className,\n children,\n}: CommandDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const scopeRef = useFocusScope(isOpen);\n // Outside clicks stay with the overlay, as in Drawer: it is what \"outside\" means.\n const dismissRef = useDismissableLayer({\n enabled: isOpen,\n onDismiss: () => setOpen(false),\n pointerOutside: false,\n modal: true,\n });\n if (!isOpen) return null;\n return (\n <Portal>\n <div\n data-slot=\"command-overlay\"\n className={COMMAND_DIALOG_OVERLAY}\n onClick={() => setOpen(false)}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-slot=\"command-dialog\"\n ref={mergeRefs(scopeRef, dismissRef)}\n className={cn(COMMAND_DIALOG_CONTENT, className)}\n >\n <Command className={COMMAND_DIALOG_COMMAND} shouldFilter={shouldFilter}>\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </Command>\n </div>\n </Portal>\n );\n}\n\nexport function CommandInput({\n className,\n ...props\n}: React.ComponentProps<\"input\">) {\n const { search, setSearch, listId, activeId } = useCommand();\n return (\n <div className={COMMAND_INPUT_WRAPPER} data-slot=\"command-input-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 shrink-0 opacity-50\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n <input\n type=\"text\"\n role=\"combobox\"\n data-slot=\"command-input\"\n aria-autocomplete=\"list\"\n aria-expanded={true}\n aria-controls={listId}\n aria-activedescendant={activeId ?? undefined}\n autoComplete=\"off\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className={cn(COMMAND_INPUT, className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function CommandList({ className, ...props }: React.ComponentProps<\"div\">) {\n const { listId } = useCommand();\n return (\n <div\n role=\"listbox\"\n id={listId}\n data-slot=\"command-list\"\n className={cn(COMMAND_LIST, className)}\n {...props}\n />\n );\n}\n\n/// Renders only when a search is under way and no item matched it — never next\n/// to results, and never before the user has typed. With `shouldFilter={false}`\n/// \"matched\" means \"is mounted\": the caller's rows are the results.\nexport function CommandEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { query, hasMatches } = useCommand();\n if (query === \"\" || hasMatches) return null;\n return (\n <div\n data-slot=\"command-empty\"\n className={cn(COMMAND_EMPTY, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport interface CommandGroupProps extends React.ComponentProps<\"div\"> {\n heading?: string;\n}\n\nexport function CommandGroup({ className, heading, children, ...props }: CommandGroupProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"command-group\"\n className={cn(COMMAND_GROUP, className)}\n {...props}\n >\n {heading ? <div data-slot=\"command-group-heading\">{heading}</div> : null}\n {children}\n </div>\n );\n}\n\nexport interface CommandItemProps\n extends Omit<React.ComponentProps<\"div\">, \"onSelect\"> {\n value: string;\n disabled?: boolean;\n onSelect?: (value: string) => void;\n}\n\nexport function CommandItem({\n className,\n value,\n disabled = false,\n onSelect,\n id: idProp,\n onPointerMove,\n children,\n ...props\n}: CommandItemProps) {\n const { registerItem, unregisterItem, matches, activeId, activate } = useCommand();\n const reactId = React.useId();\n // The DOM id is what `aria-activedescendant` points at, so a caller's own id\n // has to be the one registered.\n const id = idProp ?? reactId;\n // Read at select time, so an inline `onSelect` doesn't re-register every render.\n const onSelectRef = React.useRef(onSelect);\n React.useEffect(() => {\n onSelectRef.current = onSelect;\n });\n // Registered whether or not this item survives the filter below, so\n // `CommandEmpty` gates on the search, not on who happens to be mounted.\n React.useEffect(() => {\n registerItem(id, value, () => onSelectRef.current?.(value));\n return () => unregisterItem(id);\n }, [id, value, registerItem, unregisterItem]);\n\n if (!matches(value)) return null;\n const selected = activeId === id;\n return (\n <div\n role=\"option\"\n id={id}\n data-slot=\"command-item\"\n data-disabled={disabled}\n data-selected={selected}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n tabIndex={-1}\n onClick={() => {\n if (!disabled) onSelect?.(value);\n }}\n onPointerMove={(e) => {\n onPointerMove?.(e);\n if (!disabled && !selected) activate(id);\n }}\n className={cn(COMMAND_ITEM, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"command-separator\"\n className={cn(COMMAND_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(COMMAND_SHORTCUT, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/command.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { DismissableLayerScope, useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport {\n COMMAND_DIALOG_COMMAND,\n COMMAND_DIALOG_CONTENT,\n COMMAND_DIALOG_OVERLAY,\n COMMAND_EMPTY,\n COMMAND_GROUP,\n COMMAND_INPUT,\n COMMAND_INPUT_WRAPPER,\n COMMAND_ITEM,\n COMMAND_LIST,\n COMMAND_ROOT,\n COMMAND_SEPARATOR,\n COMMAND_SHORTCUT,\n} from \"../generated/command\";\n\ninterface CommandContextValue {\n search: string;\n setSearch: (next: string) => void;\n /// Registers a `CommandItem`'s value so `CommandEmpty` can tell \"nothing\n /// matched\" from \"nothing is here\", and its select handler so Enter can fire\n /// it. Items register even while filtered out.\n registerItem: (id: string, value: string, select: () => void) => void;\n unregisterItem: (id: string) => void;\n /// The active query: trimmed, so blank input is not a search, and lowercased.\n /// Shared by the item filter and the empty-state gate so the two can never\n /// disagree.\n query: string;\n /// `null` filters the item out; a number is its CSS `order`, `0` while\n /// nothing is being ranked.\n order: (value: string) => number | null;\n hasMatches: boolean;\n listId: string;\n /// DOM id of the highlighted item. Focus stays in the input and points here\n /// through `aria-activedescendant` (the ARIA combobox pattern), so items\n /// never need to be tabbable.\n activeId: string | null;\n activate: (id: string) => void;\n}\n\nconst CommandContext = React.createContext<CommandContextValue | null>(null);\n\nfunction useCommand(): CommandContextValue {\n const ctx = React.useContext(CommandContext);\n if (!ctx) throw new Error(\"Command parts must be used within <Command>\");\n return ctx;\n}\n\nexport interface CommandProps extends React.ComponentProps<\"div\"> {\n search?: string;\n defaultSearch?: string;\n onSearchChange?: (search: string) => void;\n /// `false` hands filtering to the caller (e.g. rows that are a server\n /// response to the query): every mounted item renders, in the caller's\n /// order, and `CommandEmpty` counts mounted items. Defaults to `true`.\n shouldFilter?: boolean;\n}\n\nconst ITEM_SELECTOR = '[data-slot=\"command-item\"]:not([aria-disabled=\"true\"])';\n\n/// fzf v1: per space-separated term, the shortest window ending at the first\n/// full subsequence match, scored for word starts and runs, charged for gaps.\n/// `null` when a term does not match.\nfunction fuzzyScore(value: string, query: string): number | null {\n const hay = [...value.toLowerCase()];\n let total = 0;\n for (const term of query.split(/\\s+/)) {\n const needle = [...term];\n let k = 0;\n let end = -1;\n for (let i = 0; i < hay.length; i++) {\n if (hay[i] === needle[k] && ++k === needle.length) {\n end = i;\n break;\n }\n }\n if (end < 0) return null;\n let start = end;\n for (let i = end, j = needle.length - 1; j >= 0; i--) {\n if (hay[i] === needle[j]) {\n start = i;\n j--;\n }\n }\n let prev = -1;\n for (let i = start, j = 0; j < needle.length; i++) {\n if (hay[i] !== needle[j]) continue;\n total += 16;\n if (i === 0 || !/[\\p{L}\\p{N}]/u.test(hay[i - 1]!)) total += 8;\n if (j > 0) total += i === prev + 1 ? 4 : -(3 + (i - prev - 2));\n prev = i;\n j++;\n }\n }\n return total;\n}\n\n/// Keyboard highlight over the items currently in the DOM, in the order on\n/// screen — each container's rows by their CSS `order`, then DOM order — so it\n/// follows whatever the filter (or the caller) rendered rather than the order\n/// items happened to register in.\nfunction useCommandNavigation(\n query: string,\n handlers: React.RefObject<Map<string, () => void>>,\n) {\n const rootRef = React.useRef<HTMLDivElement>(null);\n const [activeId, setActiveId] = React.useState<string | null>(null);\n // Until the user moves the highlight it tracks the first row, so results\n // that arrive after the keystroke (or reorder under it) put Enter on the top\n // hit. Once moved, it sticks for as long as that row stays.\n const userMoved = React.useRef(false);\n\n const enabledItems = React.useCallback((): HTMLElement[] => {\n const root = rootRef.current;\n if (!root) return [];\n const byParent = new Map<Element | null, HTMLElement[]>();\n for (const el of root.querySelectorAll<HTMLElement>(ITEM_SELECTOR)) {\n if (el.closest('[data-slot=\"command\"]') !== root) continue;\n const rows = byParent.get(el.parentElement) ?? [];\n rows.push(el);\n byParent.set(el.parentElement, rows);\n }\n return [...byParent.values()].flatMap((rows) =>\n rows.sort((a, b) => Number(a.style.order) - Number(b.style.order)),\n );\n }, []);\n\n React.useEffect(() => {\n userMoved.current = false;\n }, [query]);\n\n // No deps on purpose: the caller can change the rendered rows on any render;\n // setting an unchanged id bails out without re-rendering.\n React.useEffect(() => {\n const els = enabledItems();\n const moved = userMoved.current;\n setActiveId((prev) =>\n moved && prev !== null && els.some((el) => el.id === prev)\n ? prev\n : (els[0]?.id ?? null),\n );\n });\n\n const activate = React.useCallback((id: string) => {\n userMoved.current = true;\n setActiveId(id);\n }, []);\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.nativeEvent.isComposing) return;\n // Only the search field drives the list: Enter or an arrow on a button\n // inside Command (an action in `CommandEmpty`, a footer) stays its own.\n const field = event.target as HTMLElement;\n if (field.dataset[\"slot\"] !== \"command-input\") return;\n const els = enabledItems();\n const current = els.findIndex((el) => el.id === activeId);\n const last = els.length - 1;\n let next: number;\n switch (event.key) {\n case \"ArrowDown\":\n next = current < 0 ? 0 : Math.min(current + 1, last);\n break;\n case \"ArrowUp\":\n next = current < 0 ? last : Math.max(current - 1, 0);\n break;\n // Home / End are left to the caret: the field is editable (ARIA APG\n // editable combobox).\n case \"Enter\": {\n const select =\n activeId !== null && current >= 0\n ? handlers.current.get(activeId)\n : undefined;\n if (select) {\n event.preventDefault();\n select();\n }\n return;\n }\n default:\n return;\n }\n const target = els[next];\n if (!target) return;\n event.preventDefault();\n activate(target.id);\n // Optional call: jsdom implements no `scrollIntoView`.\n target.scrollIntoView?.({ block: \"nearest\" });\n };\n\n return { rootRef, activeId, activate, onKeyDown };\n}\n\nexport function Command({\n className,\n search,\n defaultSearch = \"\",\n onSearchChange,\n shouldFilter = true,\n children,\n ref,\n onKeyDown,\n ...props\n}: CommandProps) {\n const [currentSearch, setSearch] = useControllableState<string>({\n ...(search !== undefined ? { value: search } : {}),\n defaultValue: defaultSearch,\n ...(onSearchChange ? { onChange: onSearchChange } : {}),\n });\n\n const [items, setItems] = React.useState<ReadonlyMap<string, string>>(\n () => new Map(),\n );\n const handlers = React.useRef(new Map<string, () => void>());\n const registerItem = React.useCallback(\n (id: string, value: string, select: () => void) => {\n handlers.current.set(id, select);\n setItems((prev) => {\n if (prev.get(id) === value) return prev;\n const next = new Map(prev);\n next.set(id, value);\n return next;\n });\n },\n [],\n );\n const unregisterItem = React.useCallback((id: string) => {\n handlers.current.delete(id);\n setItems((prev) => {\n if (!prev.has(id)) return prev;\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n const query = currentSearch.trim().toLowerCase();\n const order = React.useCallback(\n (value: string) => {\n if (!shouldFilter || query === \"\") return 0;\n const score = fuzzyScore(value, query);\n return score === null ? null : -score;\n },\n [query, shouldFilter],\n );\n const hasMatches = React.useMemo(\n () => [...items.values()].some((value) => order(value) !== null),\n [items, order],\n );\n\n const listId = React.useId();\n const nav = useCommandNavigation(query, handlers);\n\n return (\n <CommandContext.Provider\n value={{\n search: currentSearch,\n setSearch,\n registerItem,\n unregisterItem,\n query,\n order,\n hasMatches,\n listId,\n activeId: nav.activeId,\n activate: nav.activate,\n }}\n >\n <div\n data-slot=\"command\"\n ref={mergeRefs(nav.rootRef, ref)}\n className={cn(COMMAND_ROOT, className)}\n onKeyDown={(e) => {\n onKeyDown?.(e);\n if (!e.defaultPrevented) nav.onKeyDown(e);\n }}\n {...props}\n >\n {children}\n </div>\n </CommandContext.Provider>\n );\n}\n\nexport interface CommandDialogProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /// See `CommandProps.shouldFilter`.\n shouldFilter?: boolean;\n className?: string;\n children?: React.ReactNode;\n}\n\nexport function CommandDialog({\n open,\n defaultOpen = false,\n onOpenChange,\n shouldFilter = true,\n className,\n children,\n}: CommandDialogProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const scopeRef = useFocusScope(isOpen);\n // Outside clicks stay with the overlay, as in Drawer: it is what \"outside\" means.\n const dismissRef = useDismissableLayer({\n enabled: isOpen,\n onDismiss: () => setOpen(false),\n pointerOutside: false,\n modal: true,\n });\n if (!isOpen) return null;\n return (\n <Portal>\n <div\n data-slot=\"command-overlay\"\n className={COMMAND_DIALOG_OVERLAY}\n onClick={() => setOpen(false)}\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n data-slot=\"command-dialog\"\n ref={mergeRefs(scopeRef, dismissRef)}\n className={cn(COMMAND_DIALOG_CONTENT, className)}\n >\n <Command className={COMMAND_DIALOG_COMMAND} shouldFilter={shouldFilter}>\n <DismissableLayerScope layer={dismissRef}>{children}</DismissableLayerScope>\n </Command>\n </div>\n </Portal>\n );\n}\n\nexport function CommandInput({\n className,\n ...props\n}: React.ComponentProps<\"input\">) {\n const { search, setSearch, listId, activeId } = useCommand();\n return (\n <div className={COMMAND_INPUT_WRAPPER} data-slot=\"command-input-wrapper\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 shrink-0 opacity-50\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n <input\n type=\"text\"\n role=\"combobox\"\n data-slot=\"command-input\"\n aria-autocomplete=\"list\"\n aria-expanded={true}\n aria-controls={listId}\n aria-activedescendant={activeId ?? undefined}\n autoComplete=\"off\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className={cn(COMMAND_INPUT, className)}\n {...props}\n />\n </div>\n );\n}\n\nexport function CommandList({ className, ...props }: React.ComponentProps<\"div\">) {\n const { listId } = useCommand();\n return (\n <div\n role=\"listbox\"\n id={listId}\n data-slot=\"command-list\"\n className={cn(COMMAND_LIST, className)}\n {...props}\n />\n );\n}\n\n/// Renders only when a search is under way and no item matched it — never next\n/// to results, and never before the user has typed. With `shouldFilter={false}`\n/// \"matched\" means \"is mounted\": the caller's rows are the results.\nexport function CommandEmpty({ className, children, ...props }: React.ComponentProps<\"div\">) {\n const { query, hasMatches } = useCommand();\n if (query === \"\" || hasMatches) return null;\n return (\n <div\n data-slot=\"command-empty\"\n className={cn(COMMAND_EMPTY, className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n\nexport interface CommandGroupProps extends React.ComponentProps<\"div\"> {\n heading?: string;\n}\n\nexport function CommandGroup({ className, heading, children, ...props }: CommandGroupProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"command-group\"\n className={cn(COMMAND_GROUP, className)}\n {...props}\n >\n {heading ? <div data-slot=\"command-group-heading\">{heading}</div> : null}\n {children}\n </div>\n );\n}\n\nexport interface CommandItemProps\n extends Omit<React.ComponentProps<\"div\">, \"onSelect\"> {\n value: string;\n disabled?: boolean;\n onSelect?: (value: string) => void;\n}\n\nexport function CommandItem({\n className,\n value,\n disabled = false,\n onSelect,\n id: idProp,\n onPointerMove,\n children,\n ...props\n}: CommandItemProps) {\n const { registerItem, unregisterItem, order, activeId, activate } = useCommand();\n const reactId = React.useId();\n // The DOM id is what `aria-activedescendant` points at, so a caller's own id\n // has to be the one registered.\n const id = idProp ?? reactId;\n // Read at select time, so an inline `onSelect` doesn't re-register every render.\n const onSelectRef = React.useRef(onSelect);\n React.useEffect(() => {\n onSelectRef.current = onSelect;\n });\n // Registered whether or not this item survives the filter below, so\n // `CommandEmpty` gates on the search, not on who happens to be mounted.\n React.useEffect(() => {\n registerItem(id, value, () => onSelectRef.current?.(value));\n return () => unregisterItem(id);\n }, [id, value, registerItem, unregisterItem]);\n\n const rank = order(value);\n if (rank === null) return null;\n const selected = activeId === id;\n return (\n <div\n role=\"option\"\n id={id}\n data-slot=\"command-item\"\n data-disabled={disabled}\n data-selected={selected}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n tabIndex={-1}\n onClick={() => {\n if (!disabled) onSelect?.(value);\n }}\n onPointerMove={(e) => {\n onPointerMove?.(e);\n if (!disabled && !selected) activate(id);\n }}\n className={cn(COMMAND_ITEM, className)}\n {...props}\n style={{ ...props.style, order: rank }}\n >\n {children}\n </div>\n );\n}\n\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"command-separator\"\n className={cn(COMMAND_SEPARATOR, className)}\n {...props}\n />\n );\n}\n\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(COMMAND_SHORTCUT, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -4,13 +4,8 @@ import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { Label } from "./label.js";
6
6
  import { walkElements } from "../primitives/walk-elements.js";
7
- import { Checkbox } from "./checkbox.js";
7
+ import { isLabelable, markLabelable } from "../primitives/labelable.js";
8
8
  import { FieldControlContext, useFieldClaims } from "./field-context.js";
9
- import { Input } from "./input.js";
10
- import { NativeSelect } from "./native-select.js";
11
- import { SelectTrigger } from "./select.js";
12
- import { Switch } from "./switch.js";
13
- import { Textarea } from "./textarea.js";
14
9
  import {
15
10
  FIELD_BASE,
16
11
  FIELD_SET,
@@ -82,6 +77,7 @@ function Field({
82
77
  }
83
78
  ) });
84
79
  }
80
+ markLabelable(Field);
85
81
  function FieldContent({ className, ...props }) {
86
82
  return /* @__PURE__ */ jsx(
87
83
  "div",
@@ -107,14 +103,13 @@ function FieldLabel({ className, htmlFor, children, ...props }) {
107
103
  );
108
104
  }
109
105
  const LABELABLE_TAGS = /* @__PURE__ */ new Set(["input", "select", "textarea", "button"]);
110
- const LABELABLE_KIT = /* @__PURE__ */ new Set([Input, Textarea, NativeSelect, Checkbox, Switch, SelectTrigger, Field]);
111
106
  function wrapsControl(children) {
112
107
  let found = false;
113
108
  walkElements(children, (element) => {
114
109
  if (found) return false;
115
110
  const { type } = element;
116
111
  const props = element.props;
117
- if (typeof type === "string" ? LABELABLE_TAGS.has(type) && props.type !== "hidden" : LABELABLE_KIT.has(type)) {
112
+ if (typeof type === "string" ? LABELABLE_TAGS.has(type) && props.type !== "hidden" : isLabelable(type)) {
118
113
  found = true;
119
114
  }
120
115
  return !found;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/field.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Label } from \"./label\";\nimport { walkElements } from \"../primitives/walk-elements\";\nimport { Checkbox } from \"./checkbox\";\nimport { FieldControlContext, useFieldClaims } from \"./field-context\";\nimport { Input } from \"./input\";\nimport { NativeSelect } from \"./native-select\";\nimport { SelectTrigger } from \"./select\";\nimport { Switch } from \"./switch\";\nimport { Textarea } from \"./textarea\";\nimport {\n FIELD_BASE,\n FIELD_SET,\n FIELD_LEGEND,\n FIELD_GROUP,\n FIELD_CONTENT,\n FIELD_LABEL,\n FIELD_TITLE,\n FIELD_DESCRIPTION,\n FIELD_SEPARATOR,\n FIELD_SEPARATOR_LINE,\n FIELD_SEPARATOR_CONTENT,\n FIELD_ERROR,\n fieldOrientation,\n type FieldOrientation,\n} from \"../generated/field\";\n\nexport type { FieldOrientation };\n\nexport function FieldSet({ className, ...props }: React.ComponentProps<\"fieldset\">) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(FIELD_SET, className)}\n {...props}\n />\n );\n}\n\nexport function FieldLegend({\n className,\n variant = \"legend\",\n ...props\n}: React.ComponentProps<\"legend\"> & { variant?: \"legend\" | \"label\" }) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(FIELD_LEGEND, className)}\n {...props}\n />\n );\n}\n\nexport function FieldGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(FIELD_GROUP, className)}\n {...props}\n />\n );\n}\n\n/**\n * A labelled control. `Field` mints one id and hands it to both ends: its\n * `FieldLabel` takes it as `for`, and the kit's `Input`, `Textarea`,\n * `NativeSelect`, `SelectTrigger`, `Checkbox` and `Switch` take it as `id` —\n * each only when the caller passed none. `controlId` names the id instead of\n * minting one. One control per `Field`: a field that holds two has to name\n * their ids by hand, or both would claim the one id (a development warning\n * says so).\n */\nexport function Field({\n className,\n orientation = \"vertical\",\n controlId,\n ...props\n}: React.ComponentProps<\"div\"> & { orientation?: FieldOrientation; controlId?: string }) {\n const minted = React.useId();\n const claim = useFieldClaims();\n const id = controlId ?? minted;\n const control = React.useMemo(() => ({ id, claim }), [id, claim]);\n return (\n <FieldControlContext.Provider value={control}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation={orientation}\n className={cn(FIELD_BASE, fieldOrientation[orientation], className)}\n {...props}\n />\n </FieldControlContext.Provider>\n );\n}\n\nexport function FieldContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(FIELD_CONTENT, className)}\n {...props}\n />\n );\n}\n\nexport interface FieldLabelProps extends Omit<React.ComponentProps<typeof Label>, \"htmlFor\"> {\n /** `null` opts out of the `Field`'s id: the label then has no `for` at all. */\n htmlFor?: string | null;\n}\n\n/**\n * Takes its `Field`'s id as `for` unless it wraps a control itself — a label\n * around an `<input>` already labels it, and a `for` pointing elsewhere would\n * steal the click. A control inside a component of yours is invisible to that\n * check; pass `htmlFor={null}` there.\n */\nexport function FieldLabel({ className, htmlFor, children, ...props }: FieldLabelProps) {\n const field = React.useContext(FieldControlContext);\n const target = htmlFor === null ? undefined : (htmlFor ?? (wrapsControl(children) ? undefined : field?.id));\n return (\n <Label\n data-slot=\"field-label\"\n htmlFor={target}\n className={cn(FIELD_LABEL, className)}\n {...props}\n >\n {children}\n </Label>\n );\n}\n\n// The labelable elements (HTML's list, less `output`/`meter`/`progress`, which a\n// field label never wraps) and the kit's controls that render one — plus a\n// nested `Field`, the choice-card shape, whose control is labelled by its own.\nconst LABELABLE_TAGS = new Set([\"input\", \"select\", \"textarea\", \"button\"]);\nconst LABELABLE_KIT: ReadonlySet<unknown> = new Set([Input, Textarea, NativeSelect, Checkbox, Switch, SelectTrigger, Field]);\n\nfunction wrapsControl(children: React.ReactNode): boolean {\n let found = false;\n walkElements(children, (element) => {\n if (found) return false;\n const { type } = element;\n const props = element.props as { type?: unknown };\n if (typeof type === \"string\" ? LABELABLE_TAGS.has(type) && props.type !== \"hidden\" : LABELABLE_KIT.has(type)) {\n found = true;\n }\n return !found;\n });\n return found;\n}\n\nexport function FieldTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-label\"\n className={cn(FIELD_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function FieldDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"field-description\"\n className={cn(FIELD_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n\nexport function FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { children?: React.ReactNode }) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn(FIELD_SEPARATOR, className)}\n {...props}\n >\n <div role=\"separator\" className={FIELD_SEPARATOR_LINE} />\n {children && (\n <span\n className={FIELD_SEPARATOR_CONTENT}\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n );\n}\n\nexport function FieldError({\n className,\n children,\n errors,\n ...props\n}: React.ComponentProps<\"div\"> & {\n errors?: Array<{ message?: string } | undefined>;\n}) {\n const content = React.useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors) {\n return null;\n }\n\n if (errors?.length === 1 && errors[0]?.message) {\n return errors[0].message;\n }\n\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {errors.map(\n (error, index) => error?.message && <li key={index}>{error.message}</li>,\n )}\n </ul>\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(FIELD_ERROR, className)}\n {...props}\n >\n {content}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/field.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Label } from \"./label\";\nimport { walkElements } from \"../primitives/walk-elements\";\nimport { isLabelable, markLabelable } from \"../primitives/labelable\";\nimport { FieldControlContext, useFieldClaims } from \"./field-context\";\nimport {\n FIELD_BASE,\n FIELD_SET,\n FIELD_LEGEND,\n FIELD_GROUP,\n FIELD_CONTENT,\n FIELD_LABEL,\n FIELD_TITLE,\n FIELD_DESCRIPTION,\n FIELD_SEPARATOR,\n FIELD_SEPARATOR_LINE,\n FIELD_SEPARATOR_CONTENT,\n FIELD_ERROR,\n fieldOrientation,\n type FieldOrientation,\n} from \"../generated/field\";\n\nexport type { FieldOrientation };\n\nexport function FieldSet({ className, ...props }: React.ComponentProps<\"fieldset\">) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(FIELD_SET, className)}\n {...props}\n />\n );\n}\n\nexport function FieldLegend({\n className,\n variant = \"legend\",\n ...props\n}: React.ComponentProps<\"legend\"> & { variant?: \"legend\" | \"label\" }) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(FIELD_LEGEND, className)}\n {...props}\n />\n );\n}\n\nexport function FieldGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(FIELD_GROUP, className)}\n {...props}\n />\n );\n}\n\n/**\n * A labelled control. `Field` mints one id and hands it to both ends: its\n * `FieldLabel` takes it as `for`, and the kit's `Input`, `Textarea`,\n * `NativeSelect`, `SelectTrigger`, `Checkbox` and `Switch` take it as `id` —\n * each only when the caller passed none. `controlId` names the id instead of\n * minting one. One control per `Field`: a field that holds two has to name\n * their ids by hand, or both would claim the one id (a development warning\n * says so).\n */\nexport function Field({\n className,\n orientation = \"vertical\",\n controlId,\n ...props\n}: React.ComponentProps<\"div\"> & { orientation?: FieldOrientation; controlId?: string }) {\n const minted = React.useId();\n const claim = useFieldClaims();\n const id = controlId ?? minted;\n const control = React.useMemo(() => ({ id, claim }), [id, claim]);\n return (\n <FieldControlContext.Provider value={control}>\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation={orientation}\n className={cn(FIELD_BASE, fieldOrientation[orientation], className)}\n {...props}\n />\n </FieldControlContext.Provider>\n );\n}\n\nmarkLabelable(Field);\n\nexport function FieldContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(FIELD_CONTENT, className)}\n {...props}\n />\n );\n}\n\nexport interface FieldLabelProps extends Omit<React.ComponentProps<typeof Label>, \"htmlFor\"> {\n /** `null` opts out of the `Field`'s id: the label then has no `for` at all. */\n htmlFor?: string | null;\n}\n\n/**\n * Takes its `Field`'s id as `for` unless it wraps a control itself — a label\n * around an `<input>` already labels it, and a `for` pointing elsewhere would\n * steal the click. A control inside a component of yours is invisible to that\n * check; pass `htmlFor={null}` there.\n */\nexport function FieldLabel({ className, htmlFor, children, ...props }: FieldLabelProps) {\n const field = React.useContext(FieldControlContext);\n const target = htmlFor === null ? undefined : (htmlFor ?? (wrapsControl(children) ? undefined : field?.id));\n return (\n <Label\n data-slot=\"field-label\"\n htmlFor={target}\n className={cn(FIELD_LABEL, className)}\n {...props}\n >\n {children}\n </Label>\n );\n}\n\n// The labelable elements (HTML's list, less `output`/`meter`/`progress`, which a\n// field label never wraps) and the kit's controls that render one — plus a\n// nested `Field`, the choice-card shape, whose control is labelled by its own.\n// The kit's controls carry a mark (`primitives/labelable`) rather than sitting\n// in a set here: a set would import every control into every page with a Field.\nconst LABELABLE_TAGS = new Set([\"input\", \"select\", \"textarea\", \"button\"]);\n\nfunction wrapsControl(children: React.ReactNode): boolean {\n let found = false;\n walkElements(children, (element) => {\n if (found) return false;\n const { type } = element;\n const props = element.props as { type?: unknown };\n if (typeof type === \"string\" ? LABELABLE_TAGS.has(type) && props.type !== \"hidden\" : isLabelable(type)) {\n found = true;\n }\n return !found;\n });\n return found;\n}\n\nexport function FieldTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-label\"\n className={cn(FIELD_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function FieldDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"field-description\"\n className={cn(FIELD_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n\nexport function FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { children?: React.ReactNode }) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn(FIELD_SEPARATOR, className)}\n {...props}\n >\n <div role=\"separator\" className={FIELD_SEPARATOR_LINE} />\n {children && (\n <span\n className={FIELD_SEPARATOR_CONTENT}\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n );\n}\n\nexport function FieldError({\n className,\n children,\n errors,\n ...props\n}: React.ComponentProps<\"div\"> & {\n errors?: Array<{ message?: string } | undefined>;\n}) {\n const content = React.useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors) {\n return null;\n }\n\n if (errors?.length === 1 && errors[0]?.message) {\n return errors[0].message;\n }\n\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {errors.map(\n (error, index) => error?.message && <li key={index}>{error.message}</li>,\n )}\n </ul>\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(FIELD_ERROR, className)}\n {...props}\n >\n {content}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -3,6 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { INPUT_BASE, inputSizeClasses } from "../generated/input.js";
6
+ import { markLabelable } from "../primitives/labelable.js";
6
7
  import { useFieldControlId } from "./field-context.js";
7
8
  const Input = React.forwardRef(
8
9
  function Input2({ className, type, id, size = "md", ...props }, ref) {
@@ -20,6 +21,7 @@ const Input = React.forwardRef(
20
21
  );
21
22
  }
22
23
  );
24
+ markLabelable(Input);
23
25
  export {
24
26
  Input
25
27
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/input.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { INPUT_BASE, inputSizeClasses, type InputSize } from \"../generated/input\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport type { InputSize };\n\n// The native `size` attribute (a width in characters) gives way to the kit's\n// scale, as it does on `NativeSelect`; a width belongs in `className`.\nexport interface InputProps extends Omit<React.ComponentPropsWithoutRef<\"input\">, \"size\"> {\n size?: InputSize;\n}\n\n// `forwardRef` rather than a `ref` prop so the handle also reaches the element\n// under React 18, where function components drop `ref`.\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n function Input({ className, type, id, size = \"md\", ...props }, ref) {\n return (\n <input\n ref={ref}\n type={type}\n id={useFieldControlId(id)}\n data-slot=\"input\"\n data-size={size}\n className={cn(INPUT_BASE, inputSizeClasses[size], className)}\n {...props}\n />\n );\n },\n);\n"],"mappings":";AAoBM;AAlBN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,YAAY,wBAAwC;AAC7D,SAAS,yBAAyB;AAY3B,MAAM,QAAQ,MAAM;AAAA,EACzB,SAASA,OAAM,EAAE,WAAW,MAAM,IAAI,OAAO,MAAM,GAAG,MAAM,GAAG,KAAK;AAClE,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,IAAI,kBAAkB,EAAE;AAAA,QACxB,aAAU;AAAA,QACV,aAAW;AAAA,QACX,WAAW,GAAG,YAAY,iBAAiB,IAAI,GAAG,SAAS;AAAA,QAC1D,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;","names":["Input"]}
1
+ {"version":3,"sources":["../../src/components/input.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { INPUT_BASE, inputSizeClasses, type InputSize } from \"../generated/input\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\n\nexport type { InputSize };\n\n// The native `size` attribute (a width in characters) gives way to the kit's\n// scale, as it does on `NativeSelect`; a width belongs in `className`.\nexport interface InputProps extends Omit<React.ComponentPropsWithoutRef<\"input\">, \"size\"> {\n size?: InputSize;\n}\n\n// `forwardRef` rather than a `ref` prop so the handle also reaches the element\n// under React 18, where function components drop `ref`.\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n function Input({ className, type, id, size = \"md\", ...props }, ref) {\n return (\n <input\n ref={ref}\n type={type}\n id={useFieldControlId(id)}\n data-slot=\"input\"\n data-size={size}\n className={cn(INPUT_BASE, inputSizeClasses[size], className)}\n {...props}\n />\n );\n },\n);\n\nmarkLabelable(Input);\n"],"mappings":";AAqBM;AAnBN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,YAAY,wBAAwC;AAC7D,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAY3B,MAAM,QAAQ,MAAM;AAAA,EACzB,SAASA,OAAM,EAAE,WAAW,MAAM,IAAI,OAAO,MAAM,GAAG,MAAM,GAAG,KAAK;AAClE,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,IAAI,kBAAkB,EAAE;AAAA,QACxB,aAAU;AAAA,QACV,aAAW;AAAA,QACX,WAAW,GAAG,YAAY,iBAAiB,IAAI,GAAG,SAAS;AAAA,QAC1D,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AACF;AAEA,cAAc,KAAK;","names":["Input"]}
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../lib/cn.js";
5
5
  import { nativeSelectSizeClasses } from "../generated/select.js";
6
+ import { markLabelable } from "../primitives/labelable.js";
6
7
  import { useFieldControlId } from "./field-context.js";
7
8
  import { SelectChevron } from "./select-chevron.js";
8
9
  const NATIVE_SELECT = "border-input text-ink h-9 w-full min-w-0 appearance-none rounded-[var(--control-radius)] border bg-transparent py-1 pr-9 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error has-[option[value='']:checked]:text-ink-soft [&_option]:bg-popover [&_option]:text-ink [&_optgroup]:bg-popover [&_optgroup]:text-ink-soft [color-scheme:var(--scheme,normal)]";
@@ -48,6 +49,7 @@ const NativeSelect = React.forwardRef(
48
49
  );
49
50
  }
50
51
  );
52
+ markLabelable(NativeSelect);
51
53
  function NativeSelectOption(props) {
52
54
  return /* @__PURE__ */ jsx("option", { "data-slot": "native-select-option", ...props });
53
55
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/native-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { nativeSelectSizeClasses, type NativeSelectSize } from \"../generated/select\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\n\nexport type { NativeSelectSize };\n\n/**\n * A real `<select>` wearing the kit's control: the form posts its `name` with no\n * script at all, and the popup is the platform's own (keyboard, type-ahead, the\n * mobile picker). `Select` is the scripted combobox for everything a native\n * popup cannot draw; this is for a form that has to submit before hydration.\n *\n * A component of its own rather than a `native` switch on `Select`: none of\n * `SelectTrigger` / `SelectValue` / `SelectContent` has a native counterpart, so\n * a flag would leave most of the compound API meaning nothing in one mode.\n *\n * `className` styles the `<select>`; `wrapperClassName` the box that holds it\n * and the arrow — that box is what a layout sizes. It is `w-full` like `Input`,\n * so the two line up in a form column; narrow it with `wrapperClassName`.\n */\nexport interface NativeSelectProps\n extends Omit<React.ComponentPropsWithoutRef<\"select\">, \"size\" | \"multiple\"> {\n size?: NativeSelectSize;\n /** An empty, unpickable first option shown until a value is chosen. */\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n// `appearance-none` drops the OS arrow. The popup is drawn by the platform:\n// the option colours reach it where the platform honours them, and `--scheme`\n// (a palette's polarity, `light` or `dark`) picks the platform's own dark\n// menu where it does not. The placeholder is the empty option being the\n// checked one — CSS can see that without script.\nconst NATIVE_SELECT =\n \"border-input text-ink h-9 w-full min-w-0 appearance-none rounded-[var(--control-radius)] border bg-transparent py-1 pr-9 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error has-[option[value='']:checked]:text-ink-soft [&_option]:bg-popover [&_option]:text-ink [&_optgroup]:bg-popover [&_optgroup]:text-ink-soft [color-scheme:var(--scheme,normal)]\";\n\nexport const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(\n function NativeSelect(\n { className, wrapperClassName, size = \"md\", placeholder, id, children, value, defaultValue, ...props },\n ref,\n ) {\n const controlId = useFieldControlId(id);\n // Without a checked option the browser picks the first enabled one, which\n // would skip a disabled placeholder; start on it unless told otherwise.\n const initial = value === undefined && defaultValue === undefined && placeholder !== undefined ? \"\" : defaultValue;\n return (\n <span\n data-slot=\"native-select-wrapper\"\n className={cn(\"relative inline-flex w-full has-[select:disabled]:opacity-50\", wrapperClassName)}\n >\n <select\n ref={ref}\n id={controlId}\n data-slot=\"native-select\"\n data-size={size}\n className={cn(NATIVE_SELECT, nativeSelectSizeClasses[size], className)}\n {...(value !== undefined ? { value } : {})}\n {...(initial !== undefined ? { defaultValue: initial } : {})}\n {...props}\n >\n {placeholder !== undefined && (\n <option value=\"\" disabled hidden>\n {placeholder}\n </option>\n )}\n {children}\n </select>\n <SelectChevron\n className={cn(\n \"text-ink-soft pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2\",\n // keeps the arrow as far in from the edge as the large inset keeps the text\n size === \"lg\" && \"right-4\",\n )}\n />\n </span>\n );\n },\n);\n\nexport function NativeSelectOption(props: React.ComponentProps<\"option\">) {\n return <option data-slot=\"native-select-option\" {...props} />;\n}\n\nexport function NativeSelectGroup(props: React.ComponentProps<\"optgroup\">) {\n return <optgroup data-slot=\"native-select-group\" {...props} />;\n}\n"],"mappings":";AAsDQ,SAWI,KAXJ;AApDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,+BAAsD;AAC/D,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AA+B9B,MAAM,gBACJ;AAEK,MAAM,eAAe,MAAM;AAAA,EAChC,SAASA,cACP,EAAE,WAAW,kBAAkB,OAAO,MAAM,aAAa,IAAI,UAAU,OAAO,cAAc,GAAG,MAAM,GACrG,KACA;AACA,UAAM,YAAY,kBAAkB,EAAE;AAGtC,UAAM,UAAU,UAAU,UAAa,iBAAiB,UAAa,gBAAgB,SAAY,KAAK;AACtG,WACE;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,WAAW,GAAG,gEAAgE,gBAAgB;AAAA,QAE9F;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,IAAI;AAAA,cACJ,aAAU;AAAA,cACV,aAAW;AAAA,cACX,WAAW,GAAG,eAAe,wBAAwB,IAAI,GAAG,SAAS;AAAA,cACpE,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,cACvC,GAAI,YAAY,SAAY,EAAE,cAAc,QAAQ,IAAI,CAAC;AAAA,cACzD,GAAG;AAAA,cAEH;AAAA,gCAAgB,UACf,oBAAC,YAAO,OAAM,IAAG,UAAQ,MAAC,QAAM,MAC7B,uBACH;AAAA,gBAED;AAAA;AAAA;AAAA,UACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA;AAAA,gBAEA,SAAS,QAAQ;AAAA,cACnB;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEO,SAAS,mBAAmB,OAAuC;AACxE,SAAO,oBAAC,YAAO,aAAU,wBAAwB,GAAG,OAAO;AAC7D;AAEO,SAAS,kBAAkB,OAAyC;AACzE,SAAO,oBAAC,cAAS,aAAU,uBAAuB,GAAG,OAAO;AAC9D;","names":["NativeSelect"]}
1
+ {"version":3,"sources":["../../src/components/native-select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { nativeSelectSizeClasses, type NativeSelectSize } from \"../generated/select\";\nimport { markLabelable } from \"../primitives/labelable\";\nimport { useFieldControlId } from \"./field-context\";\nimport { SelectChevron } from \"./select-chevron\";\n\nexport type { NativeSelectSize };\n\n/**\n * A real `<select>` wearing the kit's control: the form posts its `name` with no\n * script at all, and the popup is the platform's own (keyboard, type-ahead, the\n * mobile picker). `Select` is the scripted combobox for everything a native\n * popup cannot draw; this is for a form that has to submit before hydration.\n *\n * A component of its own rather than a `native` switch on `Select`: none of\n * `SelectTrigger` / `SelectValue` / `SelectContent` has a native counterpart, so\n * a flag would leave most of the compound API meaning nothing in one mode.\n *\n * `className` styles the `<select>`; `wrapperClassName` the box that holds it\n * and the arrow — that box is what a layout sizes. It is `w-full` like `Input`,\n * so the two line up in a form column; narrow it with `wrapperClassName`.\n */\nexport interface NativeSelectProps\n extends Omit<React.ComponentPropsWithoutRef<\"select\">, \"size\" | \"multiple\"> {\n size?: NativeSelectSize;\n /** An empty, unpickable first option shown until a value is chosen. */\n placeholder?: string;\n wrapperClassName?: string;\n}\n\n// `appearance-none` drops the OS arrow. The popup is drawn by the platform:\n// the option colours reach it where the platform honours them, and `--scheme`\n// (a palette's polarity, `light` or `dark`) picks the platform's own dark\n// menu where it does not. The placeholder is the empty option being the\n// checked one — CSS can see that without script.\nconst NATIVE_SELECT =\n \"border-input text-ink h-9 w-full min-w-0 appearance-none rounded-[var(--control-radius)] border bg-transparent py-1 pr-9 pl-3 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-accent-error/20 aria-invalid:border-accent-error has-[option[value='']:checked]:text-ink-soft [&_option]:bg-popover [&_option]:text-ink [&_optgroup]:bg-popover [&_optgroup]:text-ink-soft [color-scheme:var(--scheme,normal)]\";\n\nexport const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(\n function NativeSelect(\n { className, wrapperClassName, size = \"md\", placeholder, id, children, value, defaultValue, ...props },\n ref,\n ) {\n const controlId = useFieldControlId(id);\n // Without a checked option the browser picks the first enabled one, which\n // would skip a disabled placeholder; start on it unless told otherwise.\n const initial = value === undefined && defaultValue === undefined && placeholder !== undefined ? \"\" : defaultValue;\n return (\n <span\n data-slot=\"native-select-wrapper\"\n className={cn(\"relative inline-flex w-full has-[select:disabled]:opacity-50\", wrapperClassName)}\n >\n <select\n ref={ref}\n id={controlId}\n data-slot=\"native-select\"\n data-size={size}\n className={cn(NATIVE_SELECT, nativeSelectSizeClasses[size], className)}\n {...(value !== undefined ? { value } : {})}\n {...(initial !== undefined ? { defaultValue: initial } : {})}\n {...props}\n >\n {placeholder !== undefined && (\n <option value=\"\" disabled hidden>\n {placeholder}\n </option>\n )}\n {children}\n </select>\n <SelectChevron\n className={cn(\n \"text-ink-soft pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2\",\n // keeps the arrow as far in from the edge as the large inset keeps the text\n size === \"lg\" && \"right-4\",\n )}\n />\n </span>\n );\n },\n);\n\nmarkLabelable(NativeSelect);\n\nexport function NativeSelectOption(props: React.ComponentProps<\"option\">) {\n return <option data-slot=\"native-select-option\" {...props} />;\n}\n\nexport function NativeSelectGroup(props: React.ComponentProps<\"optgroup\">) {\n return <optgroup data-slot=\"native-select-group\" {...props} />;\n}\n"],"mappings":";AAuDQ,SAWI,KAXJ;AArDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,+BAAsD;AAC/D,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AA+B9B,MAAM,gBACJ;AAEK,MAAM,eAAe,MAAM;AAAA,EAChC,SAASA,cACP,EAAE,WAAW,kBAAkB,OAAO,MAAM,aAAa,IAAI,UAAU,OAAO,cAAc,GAAG,MAAM,GACrG,KACA;AACA,UAAM,YAAY,kBAAkB,EAAE;AAGtC,UAAM,UAAU,UAAU,UAAa,iBAAiB,UAAa,gBAAgB,SAAY,KAAK;AACtG,WACE;AAAA,MAAC;AAAA;AAAA,QACC,aAAU;AAAA,QACV,WAAW,GAAG,gEAAgE,gBAAgB;AAAA,QAE9F;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,IAAI;AAAA,cACJ,aAAU;AAAA,cACV,aAAW;AAAA,cACX,WAAW,GAAG,eAAe,wBAAwB,IAAI,GAAG,SAAS;AAAA,cACpE,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,cACvC,GAAI,YAAY,SAAY,EAAE,cAAc,QAAQ,IAAI,CAAC;AAAA,cACzD,GAAG;AAAA,cAEH;AAAA,gCAAgB,UACf,oBAAC,YAAO,OAAM,IAAG,UAAQ,MAAC,QAAM,MAC7B,uBACH;AAAA,gBAED;AAAA;AAAA;AAAA,UACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA;AAAA,gBAEA,SAAS,QAAQ;AAAA,cACnB;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,cAAc,YAAY;AAEnB,SAAS,mBAAmB,OAAuC;AACxE,SAAO,oBAAC,YAAO,aAAU,wBAAwB,GAAG,OAAO;AAC7D;AAEO,SAAS,kBAAkB,OAAyC;AACzE,SAAO,oBAAC,cAAS,aAAU,uBAAuB,GAAG,OAAO;AAC9D;","names":["NativeSelect"]}
@@ -10,6 +10,7 @@ import { usePresence } from "../primitives/presence.js";
10
10
  import { mergeRefs } from "../primitives/merge-refs.js";
11
11
  import { Portal } from "../primitives/portal.js";
12
12
  import { walkElements } from "../primitives/walk-elements.js";
13
+ import { markLabelable } from "../primitives/labelable.js";
13
14
  import { useFieldControlId } from "./field-context.js";
14
15
  import { SelectChevron } from "./select-chevron.js";
15
16
  import { landOnChosen, tabFromTrigger, useListboxKeys } from "./select-listbox.js";
@@ -147,6 +148,7 @@ function SelectTrigger({
147
148
  }
148
149
  );
149
150
  }
151
+ markLabelable(SelectTrigger);
150
152
  function SelectValue({ className, placeholder, children, ...props }) {
151
153
  const { value, labelOf } = useSelect();
152
154
  const isEmpty = value === "";