@proyecto-viviana/solid-stately 0.4.0 → 0.5.0
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/calendar/createDateFieldState.d.ts +56 -18
- package/dist/calendar/createRangeCalendarState.d.ts +10 -0
- package/dist/calendar/createTimeFieldState.d.ts +38 -67
- package/dist/calendar/intl/index.d.ts +10 -0
- package/dist/collections/ListCollection.d.ts +8 -0
- package/dist/collections/createListState.d.ts +12 -0
- package/dist/collections/getChildNodes.d.ts +8 -0
- package/dist/collections/index.d.ts +2 -2
- package/dist/collections/types.d.ts +12 -0
- package/dist/color/intl/index.d.ts +14 -0
- package/dist/dnd/createDroppableCollectionState.d.ts +17 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3451 -1309
- package/dist/index.js.map +1 -1
- package/dist/selection/SelectionManager.d.ts +9 -0
- package/package.json +1 -1
- package/src/calendar/createCalendarState.ts +26 -4
- package/src/calendar/createDateFieldState.ts +614 -459
- package/src/calendar/createRangeCalendarState.ts +212 -11
- package/src/calendar/createTimeFieldState.ts +179 -467
- package/src/calendar/intl/index.ts +364 -0
- package/src/collections/ListCollection.ts +40 -1
- package/src/collections/createListState.ts +105 -2
- package/src/collections/getChildNodes.ts +31 -0
- package/src/collections/index.ts +3 -0
- package/src/collections/types.ts +13 -0
- package/src/color/Color.ts +104 -133
- package/src/color/intl/index.ts +1409 -0
- package/src/combobox/createComboBoxState.ts +48 -16
- package/src/dnd/createDroppableCollectionState.ts +125 -6
- package/src/index.ts +3 -0
- package/src/selection/SelectionManager.ts +15 -0
- package/src/steplist/createStepListState.ts +27 -4
- package/src/tabs/createTabListState.ts +9 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* either type to filter options or select from a list.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { createSignal, createMemo, createEffect, type Accessor } from "solid-js";
|
|
9
|
+
import { createSignal, createMemo, createEffect, untrack, type Accessor } from "solid-js";
|
|
10
10
|
import { access, type MaybeAccessor } from "../utils";
|
|
11
11
|
import { createListState } from "../collections/createListState";
|
|
12
12
|
import { createOverlayTriggerState } from "../overlays";
|
|
@@ -373,6 +373,28 @@ export function createComboBoxState<T = unknown>(
|
|
|
373
373
|
};
|
|
374
374
|
|
|
375
375
|
// ---- Open/Toggle Logic ----
|
|
376
|
+
// Auto-focus the menu on open the way upstream's listbox does via its
|
|
377
|
+
// `autoFocus: state.focusStrategy || true` prop (useComboBox.ts:490 → useSelectableCollection):
|
|
378
|
+
// the first selectable selected key, else the first/last key for an explicit
|
|
379
|
+
// strategy, else nothing. The port drives this from the state (createListBox
|
|
380
|
+
// reimplements nav inline and does not honor a listbox `autoFocus` prop), so
|
|
381
|
+
// BOTH open() and toggle() must apply it — a button/mouse open routes through
|
|
382
|
+
// toggle(), and omitting it there left the menu opened with no active option
|
|
383
|
+
// (no aria-activedescendant / no highlighted row) unlike upstream.
|
|
384
|
+
const applyOpenFocus = (strategy: FocusStrategy | null) => {
|
|
385
|
+
const key = selectedKey();
|
|
386
|
+
const nextFocusedKey =
|
|
387
|
+
key != null && !listState.isDisabled(key)
|
|
388
|
+
? key
|
|
389
|
+
: strategy === "first"
|
|
390
|
+
? displayedCollection().getFirstKey()
|
|
391
|
+
: strategy === "last"
|
|
392
|
+
? displayedCollection().getLastKey()
|
|
393
|
+
: null;
|
|
394
|
+
listState.setFocused(true);
|
|
395
|
+
listState.setFocusedKey(nextFocusedKey);
|
|
396
|
+
};
|
|
397
|
+
|
|
376
398
|
const open = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
|
|
377
399
|
const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
|
|
378
400
|
|
|
@@ -391,17 +413,7 @@ export function createComboBoxState<T = unknown>(
|
|
|
391
413
|
menuOpenTrigger = trigger ?? "focus";
|
|
392
414
|
setFocusStrategy(strategy);
|
|
393
415
|
if (!overlayState.isOpen()) {
|
|
394
|
-
|
|
395
|
-
const nextFocusedKey =
|
|
396
|
-
key != null && !listState.isDisabled(key)
|
|
397
|
-
? key
|
|
398
|
-
: strategy === "first"
|
|
399
|
-
? displayedCollection().getFirstKey()
|
|
400
|
-
: strategy === "last"
|
|
401
|
-
? displayedCollection().getLastKey()
|
|
402
|
-
: null;
|
|
403
|
-
listState.setFocused(true);
|
|
404
|
-
listState.setFocusedKey(nextFocusedKey);
|
|
416
|
+
applyOpenFocus(strategy);
|
|
405
417
|
}
|
|
406
418
|
overlayState.open();
|
|
407
419
|
};
|
|
@@ -417,15 +429,20 @@ export function createComboBoxState<T = unknown>(
|
|
|
417
429
|
|
|
418
430
|
if (!canOpen && !overlayState.isOpen()) return;
|
|
419
431
|
|
|
420
|
-
|
|
432
|
+
const willOpen = !overlayState.isOpen();
|
|
433
|
+
|
|
434
|
+
if (displayAll && willOpen) {
|
|
421
435
|
setShowAllItems(true);
|
|
422
436
|
}
|
|
423
437
|
|
|
424
|
-
if (
|
|
438
|
+
if (willOpen) {
|
|
425
439
|
menuOpenTrigger = trigger ?? "focus";
|
|
426
440
|
}
|
|
427
441
|
|
|
428
442
|
setFocusStrategy(strategy);
|
|
443
|
+
if (willOpen) {
|
|
444
|
+
applyOpenFocus(strategy);
|
|
445
|
+
}
|
|
429
446
|
overlayState.toggle();
|
|
430
447
|
};
|
|
431
448
|
|
|
@@ -552,7 +569,22 @@ export function createComboBoxState<T = unknown>(
|
|
|
552
569
|
}
|
|
553
570
|
});
|
|
554
571
|
|
|
555
|
-
//
|
|
572
|
+
// Keep the input text in sync with the selected item (single mode only).
|
|
573
|
+
//
|
|
574
|
+
// This effect must react to the SELECTION and the COLLECTION — when the
|
|
575
|
+
// selected key changes, or when its item (and thus textValue) resolves as
|
|
576
|
+
// items load — but it must NOT react to the input value itself. The previous
|
|
577
|
+
// port compared `textValue !== inputValue()`, reading `inputValue()` inside
|
|
578
|
+
// the effect; in Solid that subscribes the effect to the input signal, so it
|
|
579
|
+
// re-ran on every keystroke and — while any selection was active — immediately
|
|
580
|
+
// reset the input back to the selected item's text. The result: the user
|
|
581
|
+
// could neither type nor delete in the field.
|
|
582
|
+
//
|
|
583
|
+
// The comparison is unchanged, but `inputValue()` is now read via `untrack`,
|
|
584
|
+
// so it is used only to decide whether a reset is needed and never becomes a
|
|
585
|
+
// dependency. `originalCollection()` stays tracked (the unfiltered collection
|
|
586
|
+
// only changes when `items` change, never on filtering) so a preset selection
|
|
587
|
+
// still gets its text once async items arrive.
|
|
556
588
|
createEffect(() => {
|
|
557
589
|
if (isMultiple()) return;
|
|
558
590
|
const key = selectedKey();
|
|
@@ -561,7 +593,7 @@ export function createComboBoxState<T = unknown>(
|
|
|
561
593
|
|
|
562
594
|
// Only update if selection changed and not fully controlled
|
|
563
595
|
if (!isInputControlled() || !isSelectionControlled()) {
|
|
564
|
-
if (key != null && textValue !== inputValue
|
|
596
|
+
if (key != null && textValue !== untrack(inputValue)) {
|
|
565
597
|
setInputValue(textValue);
|
|
566
598
|
setLastValue(textValue);
|
|
567
599
|
}
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
DroppableCollectionReorderEvent,
|
|
22
22
|
ItemDropTarget,
|
|
23
23
|
} from "./types";
|
|
24
|
+
import type { Collection } from "../collections/types";
|
|
24
25
|
|
|
25
26
|
export interface DroppableCollectionStateOptions {
|
|
26
27
|
/**
|
|
@@ -56,6 +57,14 @@ export interface DroppableCollectionStateOptions {
|
|
|
56
57
|
onMove?: (e: DroppableCollectionReorderEvent) => void;
|
|
57
58
|
/** A function returning whether a given target is a valid "on" drop target. */
|
|
58
59
|
shouldAcceptItemDrop?: (target: ItemDropTarget, types: DragTypes) => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The host collection. Used by the default drop-operation feature detection to
|
|
62
|
+
* confirm a reorder stays within the same parent and to prevent dropping an
|
|
63
|
+
* item onto itself or a descendant. Optional: a flat collection (e.g. a plain
|
|
64
|
+
* ListBox) is always "within parent" and has no descendants, so omitting it is
|
|
65
|
+
* faithful for flat hosts.
|
|
66
|
+
*/
|
|
67
|
+
collection?: Collection<unknown>;
|
|
59
68
|
/** Whether the droppable collection is disabled. */
|
|
60
69
|
isDisabled?: boolean;
|
|
61
70
|
}
|
|
@@ -86,11 +95,19 @@ export interface DroppableCollectionState {
|
|
|
86
95
|
isInternal: boolean,
|
|
87
96
|
draggingKeys?: Set<string | number>,
|
|
88
97
|
): void;
|
|
89
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Get the drop operation for a target. `isInternal` is true when the active
|
|
100
|
+
* drag originated from this same collection; `draggingKeys` are the keys being
|
|
101
|
+
* dragged. Both feed the handler-aware feature detection that decides whether a
|
|
102
|
+
* given drop position (before/after/on/root) is a valid target — e.g. a
|
|
103
|
+
* reorder-only collection rejects "on" targets, matching React Aria.
|
|
104
|
+
*/
|
|
90
105
|
getDropOperation(
|
|
91
106
|
target: DropTarget,
|
|
92
107
|
types: DragTypes,
|
|
93
108
|
allowedOperations: DropOperation[],
|
|
109
|
+
isInternal?: boolean,
|
|
110
|
+
draggingKeys?: Set<string | number>,
|
|
94
111
|
): DropOperation;
|
|
95
112
|
/** Check if an item drop should be accepted. */
|
|
96
113
|
shouldAcceptItemDrop(target: ItemDropTarget, types: DragTypes): boolean;
|
|
@@ -287,22 +304,124 @@ export function createDroppableCollectionState(
|
|
|
287
304
|
}
|
|
288
305
|
};
|
|
289
306
|
|
|
290
|
-
|
|
291
|
-
|
|
307
|
+
// Faithful of react-stately's `isDraggingWithinParent`: every dragged item must
|
|
308
|
+
// share the target's parent for a reorder to be valid. A flat collection (no
|
|
309
|
+
// `collection` provided) has a single implicit parent, so this is always true.
|
|
310
|
+
const isDraggingWithinParent = (
|
|
311
|
+
target: ItemDropTarget,
|
|
312
|
+
draggingKeys: Set<string | number>,
|
|
313
|
+
): boolean => {
|
|
314
|
+
const collection = getProps().collection;
|
|
315
|
+
if (!collection) return true;
|
|
316
|
+
const targetNode = collection.getItem(target.key);
|
|
317
|
+
for (const key of draggingKeys) {
|
|
318
|
+
const node = collection.getItem(key);
|
|
319
|
+
if (node?.parentKey !== targetNode?.parentKey) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Faithful of react-stately's `defaultGetDropOperation`: decide whether a drop
|
|
327
|
+
// position is a valid target from the handlers that are actually wired. A
|
|
328
|
+
// reorder-only collection (only `onReorder`) accepts before/after but rejects
|
|
329
|
+
// "on"; an item-drop collection accepts "on"; and so on.
|
|
330
|
+
const defaultGetDropOperation = (
|
|
331
|
+
target: DropTarget,
|
|
292
332
|
types: DragTypes,
|
|
293
333
|
allowedOperations: DropOperation[],
|
|
334
|
+
isInternal: boolean,
|
|
335
|
+
draggingKeys: Set<string | number>,
|
|
294
336
|
): DropOperation => {
|
|
295
337
|
const p = getProps();
|
|
296
338
|
|
|
339
|
+
if (p.isDisabled || !target) {
|
|
340
|
+
return "cancel";
|
|
341
|
+
}
|
|
342
|
+
|
|
297
343
|
if (!isAccepted(types)) {
|
|
298
344
|
return "cancel";
|
|
299
345
|
}
|
|
300
346
|
|
|
301
|
-
|
|
302
|
-
|
|
347
|
+
const isValidInsert =
|
|
348
|
+
p.onInsert &&
|
|
349
|
+
target.type === "item" &&
|
|
350
|
+
!isInternal &&
|
|
351
|
+
(target.dropPosition === "before" || target.dropPosition === "after");
|
|
352
|
+
const isValidReorder =
|
|
353
|
+
p.onReorder &&
|
|
354
|
+
target.type === "item" &&
|
|
355
|
+
isInternal &&
|
|
356
|
+
(target.dropPosition === "before" || target.dropPosition === "after") &&
|
|
357
|
+
isDraggingWithinParent(target, draggingKeys);
|
|
358
|
+
|
|
359
|
+
const isItemDropAllowed =
|
|
360
|
+
target.type !== "item" ||
|
|
361
|
+
target.dropPosition !== "on" ||
|
|
362
|
+
!p.shouldAcceptItemDrop ||
|
|
363
|
+
p.shouldAcceptItemDrop(target, types);
|
|
364
|
+
|
|
365
|
+
const isValidMove = p.onMove && target.type === "item" && isInternal && isItemDropAllowed;
|
|
366
|
+
|
|
367
|
+
// Feedback was that internal root drop was weird, so it is prevented.
|
|
368
|
+
const isValidRootDrop = p.onRootDrop && target.type === "root" && !isInternal;
|
|
369
|
+
|
|
370
|
+
// Automatically prevent items (e.g. folders) from being dropped on themselves.
|
|
371
|
+
const isValidOnItemDrop =
|
|
372
|
+
p.onItemDrop &&
|
|
373
|
+
target.type === "item" &&
|
|
374
|
+
target.dropPosition === "on" &&
|
|
375
|
+
!(isInternal && target.key != null && draggingKeys.has(target.key)) &&
|
|
376
|
+
isItemDropAllowed;
|
|
377
|
+
|
|
378
|
+
if (
|
|
379
|
+
p.onDrop ||
|
|
380
|
+
isValidInsert ||
|
|
381
|
+
isValidReorder ||
|
|
382
|
+
isValidMove ||
|
|
383
|
+
isValidRootDrop ||
|
|
384
|
+
isValidOnItemDrop
|
|
385
|
+
) {
|
|
386
|
+
if (typeof p.getDropOperation === "function") {
|
|
387
|
+
return p.getDropOperation(target, types, allowedOperations);
|
|
388
|
+
}
|
|
389
|
+
return allowedOperations[0] ?? "cancel";
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return "cancel";
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const getDropOperation = (
|
|
396
|
+
dropTarget: DropTarget,
|
|
397
|
+
types: DragTypes,
|
|
398
|
+
allowedOperations: DropOperation[],
|
|
399
|
+
isInternal: boolean = false,
|
|
400
|
+
draggingKeys: Set<string | number> = new Set(),
|
|
401
|
+
): DropOperation => {
|
|
402
|
+
// Prevent dropping items onto themselves or their descendants (faithful of
|
|
403
|
+
// react-stately's `getDropOperation` guard). Only meaningful for a nested
|
|
404
|
+
// collection; a flat host has no descendants.
|
|
405
|
+
if (isInternal && dropTarget.type === "item" && draggingKeys.size > 0) {
|
|
406
|
+
if (draggingKeys.has(dropTarget.key) && dropTarget.dropPosition === "on") {
|
|
407
|
+
return "cancel";
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const collection = getProps().collection;
|
|
411
|
+
if (collection) {
|
|
412
|
+
let currentKey: string | number | null = dropTarget.key;
|
|
413
|
+
while (currentKey != null) {
|
|
414
|
+
const item = collection.getItem(currentKey);
|
|
415
|
+
const parentKey = item?.parentKey;
|
|
416
|
+
if (parentKey != null && draggingKeys.has(parentKey)) {
|
|
417
|
+
return "cancel";
|
|
418
|
+
}
|
|
419
|
+
currentKey = parentKey ?? null;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
303
422
|
}
|
|
304
423
|
|
|
305
|
-
return allowedOperations
|
|
424
|
+
return defaultGetDropOperation(dropTarget, types, allowedOperations, isInternal, draggingKeys);
|
|
306
425
|
};
|
|
307
426
|
|
|
308
427
|
const shouldAcceptItemDrop = (dropTarget: ItemDropTarget, types: DragTypes): boolean => {
|
package/src/index.ts
CHANGED
|
@@ -50,14 +50,17 @@ export {
|
|
|
50
50
|
type MultipleSelection,
|
|
51
51
|
ListCollection,
|
|
52
52
|
createListCollection,
|
|
53
|
+
getItemCount,
|
|
53
54
|
createSelectionState,
|
|
54
55
|
type SelectionStateProps,
|
|
55
56
|
type SelectionState,
|
|
56
57
|
type SelectionPressEvent,
|
|
57
58
|
createListState,
|
|
59
|
+
createFilteredListState,
|
|
58
60
|
createSingleSelectListState,
|
|
59
61
|
type ListStateProps,
|
|
60
62
|
type ListState,
|
|
63
|
+
type ListFilterFn,
|
|
61
64
|
type SingleSelectListStateProps,
|
|
62
65
|
type SingleSelectListState,
|
|
63
66
|
createMenuState,
|
|
@@ -523,4 +523,19 @@ export class SelectionManager<T = unknown> {
|
|
|
523
523
|
fullCollection: this.fullCollection ?? this.collection,
|
|
524
524
|
});
|
|
525
525
|
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Like {@link withCollection}, but the collection is supplied as a live
|
|
529
|
+
* accessor so the returned manager stays reactive as the collection recomputes
|
|
530
|
+
* (e.g. an Autocomplete filter narrowing the list as the user types). The
|
|
531
|
+
* shared underlying selection/focus state is preserved, and the original
|
|
532
|
+
* (unfiltered) collection is retained as `fullCollection` so a select-all
|
|
533
|
+
* still materializes the filtered-out keys.
|
|
534
|
+
*/
|
|
535
|
+
withCollectionAccessor(collection: () => Collection<T>): SelectionManager<T> {
|
|
536
|
+
return new SelectionManager<T>(collection, this.state, {
|
|
537
|
+
layoutDelegate: this.layoutDelegate ?? undefined,
|
|
538
|
+
fullCollection: this.fullCollection ?? this.collection,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
526
541
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Tracks selected step, completion status, and selectability.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createMemo, createSignal, type Accessor } from "solid-js";
|
|
6
|
+
import { createEffect, createMemo, createSignal, type Accessor } from "solid-js";
|
|
7
7
|
import type { Key } from "../collections/types";
|
|
8
8
|
|
|
9
9
|
export interface StepListStateProps {
|
|
@@ -112,13 +112,16 @@ export function createStepListState(props: StepListStateProps): StepListState {
|
|
|
112
112
|
// First step is always selectable
|
|
113
113
|
const keyIndex = indexMap().get(key);
|
|
114
114
|
if (keyIndex === 0) return true;
|
|
115
|
-
//
|
|
115
|
+
// Otherwise a step is selectable only if the PREVIOUS step is completed —
|
|
116
|
+
// mirrors react-stately `useStepListState.isSelectable`
|
|
117
|
+
// (`isCompleted(prevStep) || step === firstKey`). There is no "step after the
|
|
118
|
+
// currently selected step" clause upstream: a fresh list exposes only the
|
|
119
|
+
// first step, and the immediate-next step becomes selectable when its
|
|
120
|
+
// predecessor is *completed*, not merely selected.
|
|
116
121
|
const currentItems = items();
|
|
117
122
|
if (keyIndex !== undefined && keyIndex > 0) {
|
|
118
123
|
const prevKey = currentItems[keyIndex - 1].key;
|
|
119
124
|
if (isCompleted(prevKey)) return true;
|
|
120
|
-
// Allow advancing to the step immediately after the selected step
|
|
121
|
-
if (prevKey === selectedKey()) return true;
|
|
122
125
|
}
|
|
123
126
|
return false;
|
|
124
127
|
};
|
|
@@ -177,6 +180,26 @@ export function createStepListState(props: StepListStateProps): StepListState {
|
|
|
177
180
|
props.onSelectionChange?.(key);
|
|
178
181
|
};
|
|
179
182
|
|
|
183
|
+
// Mirror react-stately `useStepListState`'s effect: whenever the selected step
|
|
184
|
+
// sits more than one past the last completed step (e.g. mounted with a
|
|
185
|
+
// `defaultSelectedKey` ahead of progress), auto-complete its immediate
|
|
186
|
+
// predecessor. Because completion is cumulative (`isCompleted` = index ≤
|
|
187
|
+
// lastCompleted index), advancing `lastCompletedStep` to `selectedIdx - 1`
|
|
188
|
+
// marks every intermediate step complete. This runs regardless of
|
|
189
|
+
// `isDisabled` / `isReadOnly`, exactly as upstream (the effect is ungated).
|
|
190
|
+
createEffect(() => {
|
|
191
|
+
const selKey = selectedKey();
|
|
192
|
+
if (selKey === null) return;
|
|
193
|
+
const selIdx = indexMap().get(selKey);
|
|
194
|
+
if (selIdx === undefined || selIdx <= 0) return;
|
|
195
|
+
const completed = lastCompletedStep();
|
|
196
|
+
const lcs = completed !== null ? (indexMap().get(completed) ?? -1) : -1;
|
|
197
|
+
if (selIdx > lcs + 1) {
|
|
198
|
+
const prevKey = items()[selIdx - 1]?.key;
|
|
199
|
+
if (prevKey !== undefined) setLastCompletedStep(prevKey);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
180
203
|
return {
|
|
181
204
|
selectedKey,
|
|
182
205
|
lastCompletedStep,
|
|
@@ -88,7 +88,15 @@ export function createTabListState<T = unknown>(
|
|
|
88
88
|
const nodes: CollectionNode<T>[] = items.map((item, index) => {
|
|
89
89
|
const o = item as CollectionItemLike;
|
|
90
90
|
const key = p.getKey?.(item) ?? o.key ?? o.id ?? index;
|
|
91
|
-
|
|
91
|
+
// See createListState: `String(item)` yields "[object Object]" for a plain
|
|
92
|
+
// object, so widen to name/title and fall back to the key for object items.
|
|
93
|
+
const textValue =
|
|
94
|
+
p.getTextValue?.(item) ??
|
|
95
|
+
o.textValue ??
|
|
96
|
+
o.label ??
|
|
97
|
+
o.name ??
|
|
98
|
+
o.title ??
|
|
99
|
+
(item != null && typeof item === "object" ? String(key) : String(item));
|
|
92
100
|
const isDisabled = p.getDisabled?.(item) ?? o.isDisabled ?? false;
|
|
93
101
|
|
|
94
102
|
return {
|