@boxcustodia/library 2.0.0-alpha.32 → 2.0.0-alpha.34
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/dist/components/button/button.cjs.js +1 -1
- package/dist/components/button/button.es.js +5 -5
- package/dist/components/combobox/combobox.cjs.js +1 -1
- package/dist/components/combobox/combobox.es.js +198 -218
- package/dist/components/select/select.cjs.js +1 -1
- package/dist/components/select/select.es.js +139 -159
- package/dist/src/components/combobox/combobox.d.ts +4 -16
- package/dist/src/components/select/select.d.ts +4 -17
- package/dist/src/utils/internal/index.d.ts +2 -0
- package/dist/src/utils/internal/item.d.ts +27 -0
- package/dist/utils/internal/item.cjs.js +1 -0
- package/dist/utils/internal/item.es.js +25 -0
- package/package.json +1 -1
- package/src/components/button/button.tsx +2 -2
- package/src/components/combobox/combobox.test.tsx +25 -0
- package/src/components/combobox/combobox.tsx +9 -43
- package/src/components/select/select.test.tsx +18 -0
- package/src/components/select/select.tsx +12 -46
- package/src/utils/internal/index.ts +2 -0
- package/src/utils/internal/item.ts +51 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Select as SelectPrimitive } from '@base-ui/react/select';
|
|
2
2
|
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import { InferItemId, ItemId } from '../../utils/internal';
|
|
3
4
|
import * as React from "react";
|
|
4
5
|
export declare const selectTriggerClasses: string;
|
|
5
6
|
export declare const selectTriggerIconClassName = "-me-1 size-4.5 opacity-80 sm:size-4";
|
|
@@ -29,21 +30,7 @@ export declare function SelectSeparator({ className, ...props }: SelectPrimitive
|
|
|
29
30
|
export declare function SelectGroup(props: SelectPrimitive.Group.Props): React.ReactElement;
|
|
30
31
|
export declare function SelectGroupLabel(props: SelectPrimitive.GroupLabel.Props): React.ReactElement;
|
|
31
32
|
export declare function SelectLabel({ className, ...props }: SelectPrimitive.Label.Props): React.ReactElement;
|
|
32
|
-
|
|
33
|
-
type SelectId = string | number;
|
|
34
|
-
/**
|
|
35
|
-
* Infers the id type from the shape of an item, mirroring `defaultGetId`'s
|
|
36
|
-
* lookup order (`id` before `value`, then the primitive itself). Lets the
|
|
37
|
-
* common case — `{ id, name }`, `{ label, value }`, or plain primitives — drive
|
|
38
|
-
* `value` / `onValueChange` typing with no explicit `getId`. Falls back to
|
|
39
|
-
* `string` for shapes `defaultGetId` can't address.
|
|
40
|
-
*/
|
|
41
|
-
type InferId<TItem> = TItem extends {
|
|
42
|
-
id: infer I extends SelectId;
|
|
43
|
-
} ? I : TItem extends {
|
|
44
|
-
value: infer V extends SelectId;
|
|
45
|
-
} ? V : TItem extends SelectId ? TItem : string;
|
|
46
|
-
type SelectBaseProps<TItem, TId extends SelectId> = Omit<SelectPrimitive.Root.Props<string, false>, "children" | "onValueChange" | "value" | "defaultValue" | "items" | "multiple"> & {
|
|
33
|
+
type SelectBaseProps<TItem, TId extends ItemId> = Omit<SelectPrimitive.Root.Props<string, false>, "children" | "onValueChange" | "value" | "defaultValue" | "items" | "multiple"> & {
|
|
47
34
|
items: readonly TItem[];
|
|
48
35
|
/** Returns the display text for an item. Used for the trigger value and ARIA. Defaults to `item.label`, `item.name`, `item.title`, or the stringified primitive. */
|
|
49
36
|
getLabel?: (item: TItem) => string;
|
|
@@ -64,7 +51,7 @@ type SelectBaseProps<TItem, TId extends SelectId> = Omit<SelectPrimitive.Root.Pr
|
|
|
64
51
|
item?: string;
|
|
65
52
|
};
|
|
66
53
|
};
|
|
67
|
-
export type SelectProps<TItem = unknown, TId extends
|
|
54
|
+
export type SelectProps<TItem = unknown, TId extends ItemId = InferItemId<TItem>> = (SelectBaseProps<TItem, TId> & {
|
|
68
55
|
multiple?: false;
|
|
69
56
|
/** Controlled selection. The id (whatever `getId` returns — `string` or `number`), or `null` when cleared. */
|
|
70
57
|
value?: TId | null;
|
|
@@ -99,6 +86,6 @@ export type SelectProps<TItem = unknown, TId extends SelectId = InferId<TItem>>
|
|
|
99
86
|
* Use `SelectRoot` + primitives directly when you need full structural
|
|
100
87
|
* control beyond what `className` + `classNames` slots offer.
|
|
101
88
|
*/
|
|
102
|
-
export declare function Select<TItem = unknown, TId extends
|
|
89
|
+
export declare function Select<TItem = unknown, TId extends ItemId = InferItemId<TItem>>(allProps: SelectProps<TItem, TId>): React.ReactElement;
|
|
103
90
|
export { SelectPrimitive };
|
|
104
91
|
export { SelectPopup as SelectContent };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Identifier an item can be addressed by. */
|
|
2
|
+
export type ItemId = string | number;
|
|
3
|
+
/**
|
|
4
|
+
* Infers the id type from the shape of an item, mirroring `defaultGetId`'s
|
|
5
|
+
* lookup order (`id` before `value`, then the primitive itself). Lets the
|
|
6
|
+
* common case — `{ id, name }`, `{ label, value }`, or plain primitives — drive
|
|
7
|
+
* `value` / `onValueChange` typing with no explicit `getId`. Falls back to
|
|
8
|
+
* `string` for shapes `defaultGetId` can't address.
|
|
9
|
+
*/
|
|
10
|
+
export type InferItemId<TItem> = TItem extends {
|
|
11
|
+
id: infer I extends ItemId;
|
|
12
|
+
} ? I : TItem extends {
|
|
13
|
+
value: infer V extends ItemId;
|
|
14
|
+
} ? V : TItem extends ItemId ? TItem : string;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the display text for an item. Defaults to `item.label`, `item.name`,
|
|
17
|
+
* `item.title`, or the stringified primitive. Always returns a string — labels
|
|
18
|
+
* are display text.
|
|
19
|
+
*/
|
|
20
|
+
export declare function defaultGetLabel(item: unknown): string;
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the id for an item, preserving its native type. The internal Base UI
|
|
23
|
+
* layer keys options by string (callers wrap this in `String(...)`), so keeping
|
|
24
|
+
* the raw type here lets `onValueChange` hand back a numeric id as a number, not
|
|
25
|
+
* a string. Defaults to `item.id`, `item.value`, or the stringified primitive.
|
|
26
|
+
*/
|
|
27
|
+
export declare function defaultGetId(item: unknown): ItemId;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function t(r){if(r==null)return"";if(typeof r=="string"||typeof r=="number")return String(r);if(typeof r=="object"){const n=r;if("label"in n)return String(n.label);if("name"in n)return String(n.name);if("title"in n)return String(n.title)}return String(r)}function e(r){if(r==null)return"";if(typeof r=="string"||typeof r=="number")return r;if(typeof r=="object"){const n=r;if("id"in n)return n.id;if("value"in n)return n.value}return String(r)}exports.defaultGetId=e;exports.defaultGetLabel=t;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function t(r) {
|
|
2
|
+
if (r == null) return "";
|
|
3
|
+
if (typeof r == "string" || typeof r == "number") return String(r);
|
|
4
|
+
if (typeof r == "object") {
|
|
5
|
+
const n = r;
|
|
6
|
+
if ("label" in n) return String(n.label);
|
|
7
|
+
if ("name" in n) return String(n.name);
|
|
8
|
+
if ("title" in n) return String(n.title);
|
|
9
|
+
}
|
|
10
|
+
return String(r);
|
|
11
|
+
}
|
|
12
|
+
function e(r) {
|
|
13
|
+
if (r == null) return "";
|
|
14
|
+
if (typeof r == "string" || typeof r == "number") return r;
|
|
15
|
+
if (typeof r == "object") {
|
|
16
|
+
const n = r;
|
|
17
|
+
if ("id" in n) return n.id;
|
|
18
|
+
if ("value" in n) return n.value;
|
|
19
|
+
}
|
|
20
|
+
return String(r);
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
e as defaultGetId,
|
|
24
|
+
t as defaultGetLabel
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -8,11 +8,11 @@ import { BaseButton } from "./components/base-button";
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
9
|
[
|
|
10
10
|
"group/button relative inline-flex shrink-0 items-center justify-center gap-1 rounded-lg border border-transparent bg-clip-padding",
|
|
11
|
-
"text-sm font-medium whitespace-nowrap transition-
|
|
11
|
+
"text-sm font-medium whitespace-nowrap transition-[background-color,border-color,box-shadow,transform,opacity] outline-none select-none",
|
|
12
12
|
"focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
13
13
|
"active:not-aria-[haspopup]:translate-y-px",
|
|
14
14
|
"disabled:pointer-events-none disabled:opacity-50",
|
|
15
|
-
"aria-invalid:border-
|
|
15
|
+
"aria-invalid:border-error aria-invalid:ring-3 aria-invalid:ring-error/20 dark:aria-invalid:border-error/50 dark:aria-invalid:ring-error/40",
|
|
16
16
|
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
17
17
|
"[&[data-loading]]:text-transparent",
|
|
18
18
|
"[&[data-loading]>*:not([data-loader])]:invisible",
|
|
@@ -217,6 +217,31 @@ describe("Combobox getId / getLabel defaults", () => {
|
|
|
217
217
|
await userEvent.click(getInput());
|
|
218
218
|
expect(screen.getAllByRole("option").length).toBe(2);
|
|
219
219
|
});
|
|
220
|
+
|
|
221
|
+
it("returns a numeric id as a number without a custom getId", async () => {
|
|
222
|
+
const numeric = [
|
|
223
|
+
{ id: 1, name: "Apple" },
|
|
224
|
+
{ id: 2, name: "Banana" },
|
|
225
|
+
];
|
|
226
|
+
const onValueChange = vi.fn();
|
|
227
|
+
render(
|
|
228
|
+
<Combobox
|
|
229
|
+
items={numeric}
|
|
230
|
+
placeholder="P"
|
|
231
|
+
onValueChange={onValueChange}
|
|
232
|
+
/>,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const input = getInput();
|
|
236
|
+
await userEvent.click(input);
|
|
237
|
+
await userEvent.type(input, "Banana");
|
|
238
|
+
await userEvent.click(screen.getByRole("option"));
|
|
239
|
+
|
|
240
|
+
// defaultGetId preserves the native id type — number stays a number,
|
|
241
|
+
// not a stringified "2".
|
|
242
|
+
expect(onValueChange.mock.calls[0][0]).toBe(2);
|
|
243
|
+
expect(typeof onValueChange.mock.calls[0][0]).toBe("number");
|
|
244
|
+
});
|
|
220
245
|
});
|
|
221
246
|
|
|
222
247
|
describe("Combobox (multiple mode)", () => {
|
|
@@ -6,6 +6,12 @@ import { ChevronsUpDownIcon, XIcon } from "lucide-react";
|
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { useIsomorphicLayoutEffect } from "../../hooks/internal";
|
|
8
8
|
import { cn } from "../../lib";
|
|
9
|
+
import {
|
|
10
|
+
defaultGetId,
|
|
11
|
+
defaultGetLabel,
|
|
12
|
+
type InferItemId,
|
|
13
|
+
type ItemId,
|
|
14
|
+
} from "../../utils/internal";
|
|
9
15
|
import { useIsInsideFieldRoot } from "../field";
|
|
10
16
|
import { Input, inputBaseClasses } from "../input";
|
|
11
17
|
import { ScrollArea } from "../scroll-area";
|
|
@@ -702,29 +708,6 @@ function ComboboxMultipleChipsContent({
|
|
|
702
708
|
|
|
703
709
|
// ─── Composite ────────────────────────────────────────────────────────────────
|
|
704
710
|
|
|
705
|
-
function defaultGetLabel(item: unknown): string {
|
|
706
|
-
if (item == null) return "";
|
|
707
|
-
if (typeof item === "string" || typeof item === "number") return String(item);
|
|
708
|
-
if (typeof item === "object") {
|
|
709
|
-
const record = item as Record<string, unknown>;
|
|
710
|
-
if ("label" in record) return String(record.label);
|
|
711
|
-
if ("name" in record) return String(record.name);
|
|
712
|
-
if ("title" in record) return String(record.title);
|
|
713
|
-
}
|
|
714
|
-
return String(item);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
function defaultGetId(item: unknown): string {
|
|
718
|
-
if (item == null) return "";
|
|
719
|
-
if (typeof item === "string" || typeof item === "number") return String(item);
|
|
720
|
-
if (typeof item === "object") {
|
|
721
|
-
const record = item as Record<string, unknown>;
|
|
722
|
-
if ("id" in record) return String(record.id);
|
|
723
|
-
if ("value" in record) return String(record.value);
|
|
724
|
-
}
|
|
725
|
-
return String(item);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
711
|
function ComboboxSingleTrigger({
|
|
729
712
|
placeholder,
|
|
730
713
|
showClear,
|
|
@@ -777,26 +760,9 @@ function ComboboxMultipleTrigger({
|
|
|
777
760
|
);
|
|
778
761
|
}
|
|
779
762
|
|
|
780
|
-
type ComboboxId = string | number;
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
* Infers the id type from the shape of an item, mirroring `defaultGetId`'s
|
|
784
|
-
* lookup order (`id` before `value`, then the primitive itself). Lets the
|
|
785
|
-
* common case — `{ id, name }`, `{ label, value }`, or plain primitives — drive
|
|
786
|
-
* `value` / `onValueChange` typing with no explicit `getId`. Falls back to
|
|
787
|
-
* `string` for shapes `defaultGetId` can't address.
|
|
788
|
-
*/
|
|
789
|
-
type InferId<TItem> = TItem extends { id: infer I extends ComboboxId }
|
|
790
|
-
? I
|
|
791
|
-
: TItem extends { value: infer V extends ComboboxId }
|
|
792
|
-
? V
|
|
793
|
-
: TItem extends ComboboxId
|
|
794
|
-
? TItem
|
|
795
|
-
: string;
|
|
796
|
-
|
|
797
763
|
type ComboboxBaseProps<
|
|
798
764
|
TItem = unknown,
|
|
799
|
-
TId extends
|
|
765
|
+
TId extends ItemId = InferItemId<TItem>,
|
|
800
766
|
> = Omit<
|
|
801
767
|
ComboboxPrimitive.Root.Props<TItem, boolean>,
|
|
802
768
|
| "items"
|
|
@@ -843,7 +809,7 @@ type ComboboxBaseProps<
|
|
|
843
809
|
|
|
844
810
|
export type ComboboxProps<
|
|
845
811
|
TItem = unknown,
|
|
846
|
-
TId extends
|
|
812
|
+
TId extends ItemId = InferItemId<TItem>,
|
|
847
813
|
> =
|
|
848
814
|
| (ComboboxBaseProps<TItem, TId> & {
|
|
849
815
|
multiple?: false;
|
|
@@ -881,7 +847,7 @@ export type ComboboxProps<
|
|
|
881
847
|
*/
|
|
882
848
|
export function Combobox<
|
|
883
849
|
TItem = unknown,
|
|
884
|
-
TId extends
|
|
850
|
+
TId extends ItemId = InferItemId<TItem>,
|
|
885
851
|
>(allProps: ComboboxProps<TItem, TId>): React.ReactElement {
|
|
886
852
|
// Cast internally — ComboboxProps discriminated union enforces correctness for consumers.
|
|
887
853
|
// The union can't be spread directly into ComboboxRoot because TypeScript can't resolve
|
|
@@ -424,6 +424,24 @@ describe("Select controlled value (id-first)", () => {
|
|
|
424
424
|
expect(onChange.mock.calls[0][1]).toEqual(numeric[0]);
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
+
it("returns a numeric id as a number without a custom getId", async () => {
|
|
428
|
+
const numeric = [
|
|
429
|
+
{ id: 1, name: "Aumentar retención" },
|
|
430
|
+
{ id: 2, name: "Reducir churn" },
|
|
431
|
+
];
|
|
432
|
+
const onChange = vi.fn();
|
|
433
|
+
render(<Select items={numeric} defaultOpen onValueChange={onChange} />);
|
|
434
|
+
|
|
435
|
+
await userEvent.click(
|
|
436
|
+
screen.getByRole("option", { name: "Aumentar retención" }),
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
// defaultGetId preserves the native id type — number stays a number,
|
|
440
|
+
// not a stringified "1".
|
|
441
|
+
expect(onChange.mock.calls[0][0]).toBe(1);
|
|
442
|
+
expect(typeof onChange.mock.calls[0][0]).toBe("number");
|
|
443
|
+
});
|
|
444
|
+
|
|
427
445
|
it("rejects an item object as value at compile time", () => {
|
|
428
446
|
// @ts-expect-error — value is id-first; passing the item must not compile.
|
|
429
447
|
render(<Select items={fruits} value={fruits[1]} />);
|
|
@@ -10,6 +10,12 @@ import {
|
|
|
10
10
|
} from "lucide-react";
|
|
11
11
|
import * as React from "react";
|
|
12
12
|
import { cn } from "../../lib";
|
|
13
|
+
import {
|
|
14
|
+
defaultGetId,
|
|
15
|
+
defaultGetLabel,
|
|
16
|
+
type InferItemId,
|
|
17
|
+
type ItemId,
|
|
18
|
+
} from "../../utils/internal";
|
|
13
19
|
import { inputBaseClasses } from "../input";
|
|
14
20
|
|
|
15
21
|
// ─── Primitives ───────────────────────────────────────────────────────────────
|
|
@@ -266,54 +272,13 @@ export function SelectLabel({
|
|
|
266
272
|
|
|
267
273
|
// ─── Composite ────────────────────────────────────────────────────────────────
|
|
268
274
|
|
|
269
|
-
function defaultGetLabel(item: unknown): string {
|
|
270
|
-
if (item == null) return "";
|
|
271
|
-
if (typeof item === "string" || typeof item === "number") return String(item);
|
|
272
|
-
if (typeof item === "object") {
|
|
273
|
-
const record = item as Record<string, unknown>;
|
|
274
|
-
if ("label" in record) return String(record.label);
|
|
275
|
-
if ("name" in record) return String(record.name);
|
|
276
|
-
if ("title" in record) return String(record.title);
|
|
277
|
-
}
|
|
278
|
-
return String(item);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function defaultGetId(item: unknown): string {
|
|
282
|
-
if (item == null) return "";
|
|
283
|
-
if (typeof item === "string" || typeof item === "number") return String(item);
|
|
284
|
-
if (typeof item === "object") {
|
|
285
|
-
const record = item as Record<string, unknown>;
|
|
286
|
-
if ("id" in record) return String(record.id);
|
|
287
|
-
if ("value" in record) return String(record.value);
|
|
288
|
-
}
|
|
289
|
-
return String(item);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
275
|
function defaultGetDisabled(item: unknown): boolean {
|
|
293
276
|
if (item != null && typeof item === "object" && "disabled" in item)
|
|
294
277
|
return Boolean((item as Record<string, unknown>).disabled);
|
|
295
278
|
return false;
|
|
296
279
|
}
|
|
297
280
|
|
|
298
|
-
|
|
299
|
-
type SelectId = string | number;
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Infers the id type from the shape of an item, mirroring `defaultGetId`'s
|
|
303
|
-
* lookup order (`id` before `value`, then the primitive itself). Lets the
|
|
304
|
-
* common case — `{ id, name }`, `{ label, value }`, or plain primitives — drive
|
|
305
|
-
* `value` / `onValueChange` typing with no explicit `getId`. Falls back to
|
|
306
|
-
* `string` for shapes `defaultGetId` can't address.
|
|
307
|
-
*/
|
|
308
|
-
type InferId<TItem> = TItem extends { id: infer I extends SelectId }
|
|
309
|
-
? I
|
|
310
|
-
: TItem extends { value: infer V extends SelectId }
|
|
311
|
-
? V
|
|
312
|
-
: TItem extends SelectId
|
|
313
|
-
? TItem
|
|
314
|
-
: string;
|
|
315
|
-
|
|
316
|
-
type SelectBaseProps<TItem, TId extends SelectId> = Omit<
|
|
281
|
+
type SelectBaseProps<TItem, TId extends ItemId> = Omit<
|
|
317
282
|
SelectPrimitive.Root.Props<string, false>,
|
|
318
283
|
"children" | "onValueChange" | "value" | "defaultValue" | "items" | "multiple"
|
|
319
284
|
> & {
|
|
@@ -340,7 +305,7 @@ type SelectBaseProps<TItem, TId extends SelectId> = Omit<
|
|
|
340
305
|
|
|
341
306
|
export type SelectProps<
|
|
342
307
|
TItem = unknown,
|
|
343
|
-
TId extends
|
|
308
|
+
TId extends ItemId = InferItemId<TItem>,
|
|
344
309
|
> =
|
|
345
310
|
| (SelectBaseProps<TItem, TId> & {
|
|
346
311
|
multiple?: false;
|
|
@@ -379,9 +344,10 @@ export type SelectProps<
|
|
|
379
344
|
* Use `SelectRoot` + primitives directly when you need full structural
|
|
380
345
|
* control beyond what `className` + `classNames` slots offer.
|
|
381
346
|
*/
|
|
382
|
-
export function Select<
|
|
383
|
-
|
|
384
|
-
|
|
347
|
+
export function Select<
|
|
348
|
+
TItem = unknown,
|
|
349
|
+
TId extends ItemId = InferItemId<TItem>,
|
|
350
|
+
>(allProps: SelectProps<TItem, TId>): React.ReactElement {
|
|
385
351
|
const {
|
|
386
352
|
items,
|
|
387
353
|
getLabel: getLabelProp,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Identifier an item can be addressed by. */
|
|
2
|
+
export type ItemId = string | number;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Infers the id type from the shape of an item, mirroring `defaultGetId`'s
|
|
6
|
+
* lookup order (`id` before `value`, then the primitive itself). Lets the
|
|
7
|
+
* common case — `{ id, name }`, `{ label, value }`, or plain primitives — drive
|
|
8
|
+
* `value` / `onValueChange` typing with no explicit `getId`. Falls back to
|
|
9
|
+
* `string` for shapes `defaultGetId` can't address.
|
|
10
|
+
*/
|
|
11
|
+
export type InferItemId<TItem> = TItem extends { id: infer I extends ItemId }
|
|
12
|
+
? I
|
|
13
|
+
: TItem extends { value: infer V extends ItemId }
|
|
14
|
+
? V
|
|
15
|
+
: TItem extends ItemId
|
|
16
|
+
? TItem
|
|
17
|
+
: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the display text for an item. Defaults to `item.label`, `item.name`,
|
|
21
|
+
* `item.title`, or the stringified primitive. Always returns a string — labels
|
|
22
|
+
* are display text.
|
|
23
|
+
*/
|
|
24
|
+
export function defaultGetLabel(item: unknown): string {
|
|
25
|
+
if (item == null) return "";
|
|
26
|
+
if (typeof item === "string" || typeof item === "number") return String(item);
|
|
27
|
+
if (typeof item === "object") {
|
|
28
|
+
const record = item as Record<string, unknown>;
|
|
29
|
+
if ("label" in record) return String(record.label);
|
|
30
|
+
if ("name" in record) return String(record.name);
|
|
31
|
+
if ("title" in record) return String(record.title);
|
|
32
|
+
}
|
|
33
|
+
return String(item);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolves the id for an item, preserving its native type. The internal Base UI
|
|
38
|
+
* layer keys options by string (callers wrap this in `String(...)`), so keeping
|
|
39
|
+
* the raw type here lets `onValueChange` hand back a numeric id as a number, not
|
|
40
|
+
* a string. Defaults to `item.id`, `item.value`, or the stringified primitive.
|
|
41
|
+
*/
|
|
42
|
+
export function defaultGetId(item: unknown): ItemId {
|
|
43
|
+
if (item == null) return "";
|
|
44
|
+
if (typeof item === "string" || typeof item === "number") return item;
|
|
45
|
+
if (typeof item === "object") {
|
|
46
|
+
const record = item as Record<string, unknown>;
|
|
47
|
+
if ("id" in record) return record.id as ItemId;
|
|
48
|
+
if ("value" in record) return record.value as ItemId;
|
|
49
|
+
}
|
|
50
|
+
return String(item);
|
|
51
|
+
}
|