@sproutsocial/seeds-react-menu 1.11.1 → 1.11.2
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/.turbo/turbo-build.log +16 -16
- package/CHANGELOG.md +6 -0
- package/dist/esm/v3/index.js +1385 -2
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/v3/index.d.mts +14 -2
- package/dist/v3/index.d.ts +14 -2
- package/dist/v3/index.js +1391 -2
- package/dist/v3/index.js.map +1 -1
- package/package.json +1 -1
- package/src/v3/Common/ComboboxItem.tsx +1 -0
- package/src/v3/Common/ComboboxStyles.tsx +28 -0
- package/src/v3/Common/SelectItem.tsx +5 -1
- package/src/v3/Common/SelectStyles.tsx +5 -0
- package/src/v3/ModalStories.stories.tsx +6 -6
- package/src/v3/MultiCombobox.stories.tsx +1 -1
- package/src/v3/MultiCombobox.tsx +3 -1
- package/src/v3/MultiSearchableSelect.stories.tsx +1 -1
- package/src/v3/MultiSearchableSelect.tsx +3 -1
- package/src/v3/MultiSelect.stories.tsx +53 -53
- package/src/v3/MultiSelect.tsx +3 -222
- package/src/v3/SingleCombobox.stories.tsx +1 -1
- package/src/v3/SingleCombobox.tsx +3 -1
- package/src/v3/SingleSearchableSelect.stories.tsx +1 -1
- package/src/v3/SingleSearchableSelect.tsx +3 -1
- package/src/v3/SingleSelect.stories.tsx +20 -1
- package/src/v3/SingleSelect.tsx +3 -1
package/dist/esm/v3/index.js
CHANGED
|
@@ -6,6 +6,18 @@ import styled4 from "styled-components";
|
|
|
6
6
|
// src/v3/SeedsPortal.tsx
|
|
7
7
|
import * as React from "react";
|
|
8
8
|
import { DisablePortalToBodyContext } from "@sproutsocial/seeds-react-portal";
|
|
9
|
+
function useSeedsPortalContainer() {
|
|
10
|
+
const disablePortalToBody = React.useContext(DisablePortalToBodyContext);
|
|
11
|
+
const containerRef = React.useRef(null);
|
|
12
|
+
const [portalContainer, setPortalContainer] = React.useState(void 0);
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
if (disablePortalToBody && containerRef.current) {
|
|
15
|
+
const dialogContent = containerRef.current.closest("[role='dialog']");
|
|
16
|
+
setPortalContainer(dialogContent ?? containerRef.current);
|
|
17
|
+
}
|
|
18
|
+
}, [disablePortalToBody]);
|
|
19
|
+
return { containerRef, portalContainer, disablePortalToBody };
|
|
20
|
+
}
|
|
9
21
|
|
|
10
22
|
// src/v3/Common/SelectItem.tsx
|
|
11
23
|
import "react";
|
|
@@ -51,6 +63,72 @@ var StyledItemIndicator = styled(Select.ItemIndicator)`
|
|
|
51
63
|
}
|
|
52
64
|
`;
|
|
53
65
|
var StyledItemText = styled(Select.ItemText)``;
|
|
66
|
+
function CheckIcon() {
|
|
67
|
+
return /* @__PURE__ */ jsx(Icon, { name: "check-outline", fixedWidth: true, "aria-hidden": true });
|
|
68
|
+
}
|
|
69
|
+
function SelectItem({
|
|
70
|
+
value,
|
|
71
|
+
label,
|
|
72
|
+
inputType = "icon",
|
|
73
|
+
renderItem,
|
|
74
|
+
hidden
|
|
75
|
+
}) {
|
|
76
|
+
return /* @__PURE__ */ jsxs(
|
|
77
|
+
StyledItem,
|
|
78
|
+
{
|
|
79
|
+
value,
|
|
80
|
+
style: hidden ? { display: "none" } : void 0,
|
|
81
|
+
"data-qa-menu-item": label,
|
|
82
|
+
children: [
|
|
83
|
+
inputType === "radio" ? /* @__PURE__ */ jsx(
|
|
84
|
+
StyledItemIndicator,
|
|
85
|
+
{
|
|
86
|
+
keepMounted: true,
|
|
87
|
+
render: (_props, state) => /* @__PURE__ */ jsx(
|
|
88
|
+
Radio,
|
|
89
|
+
{
|
|
90
|
+
checked: state.selected,
|
|
91
|
+
onChange: () => {
|
|
92
|
+
},
|
|
93
|
+
tabIndex: -1,
|
|
94
|
+
id: `radio-${value}`,
|
|
95
|
+
name: "select-item"
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
) : inputType === "checkbox" ? /* @__PURE__ */ jsx(
|
|
100
|
+
StyledItemIndicator,
|
|
101
|
+
{
|
|
102
|
+
keepMounted: true,
|
|
103
|
+
render: (_props, state) => /* @__PURE__ */ jsx(
|
|
104
|
+
Checkbox,
|
|
105
|
+
{
|
|
106
|
+
checked: state.selected,
|
|
107
|
+
onChange: () => {
|
|
108
|
+
},
|
|
109
|
+
tabIndex: -1,
|
|
110
|
+
id: `checkbox-${value}`,
|
|
111
|
+
name: "select-item"
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
) : inputType === "icon" ? /* @__PURE__ */ jsx(StyledItemIndicator, { keepMounted: true, children: /* @__PURE__ */ jsx(CheckIcon, {}) }) : null,
|
|
116
|
+
/* @__PURE__ */ jsx(StyledItemText, { children: renderItem ? renderItem() : label })
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/v3/Common/groupItems.ts
|
|
123
|
+
function groupItems(data, groupBy) {
|
|
124
|
+
const map = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const item of data) {
|
|
126
|
+
const key = groupBy(item);
|
|
127
|
+
if (!map.has(key)) map.set(key, []);
|
|
128
|
+
map.get(key).push(item);
|
|
129
|
+
}
|
|
130
|
+
return Array.from(map.entries()).map(([value, items]) => ({ value, items }));
|
|
131
|
+
}
|
|
54
132
|
|
|
55
133
|
// src/v3/Common/SelectStyles.tsx
|
|
56
134
|
import { Select as Select2 } from "@base-ui/react/select";
|
|
@@ -96,6 +174,11 @@ var SelectTrigger = styled2(Select2.Trigger)`
|
|
|
96
174
|
&[data-popup-open] [data-chevron] {
|
|
97
175
|
transform: rotate(-180deg);
|
|
98
176
|
}
|
|
177
|
+
|
|
178
|
+
&[data-disabled] {
|
|
179
|
+
opacity: 0.4;
|
|
180
|
+
cursor: not-allowed;
|
|
181
|
+
}
|
|
99
182
|
`;
|
|
100
183
|
var SelectValue = styled2(Select2.Value)`
|
|
101
184
|
min-width: 0;
|
|
@@ -180,7 +263,13 @@ var StyledChevron = styled3.div`
|
|
|
180
263
|
transition: transform 0.15s;
|
|
181
264
|
${({ $isOpen }) => $isOpen && "transform: rotate(-180deg);"}
|
|
182
265
|
`;
|
|
183
|
-
function
|
|
266
|
+
function ChevronIcon() {
|
|
267
|
+
return /* @__PURE__ */ jsx2(Icon2, { name: "chevron-down-outline", fixedWidth: true, "aria-hidden": true });
|
|
268
|
+
}
|
|
269
|
+
function ClearIcon() {
|
|
270
|
+
return /* @__PURE__ */ jsx2(Icon2, { name: "circle-x-outline", fixedWidth: true, "aria-hidden": true, size: "mini" });
|
|
271
|
+
}
|
|
272
|
+
function CheckIcon2() {
|
|
184
273
|
return /* @__PURE__ */ jsx2(Icon2, { name: "check-outline", fixedWidth: true, "aria-hidden": true });
|
|
185
274
|
}
|
|
186
275
|
|
|
@@ -197,6 +286,116 @@ var StyledValue = styled4(Select3.Value)`
|
|
|
197
286
|
color: ${({ theme }) => theme.colors.text.subtext};
|
|
198
287
|
}
|
|
199
288
|
`;
|
|
289
|
+
function SingleSelect(props) {
|
|
290
|
+
const { id, placeholder, includePlaceholderItem } = props;
|
|
291
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
292
|
+
const selectedItemId = props.selectedItemId;
|
|
293
|
+
const defaultSelectedItemId = props.defaultSelectedItemId;
|
|
294
|
+
const onSelectedItemIdChange = props.onSelectedItemIdChange;
|
|
295
|
+
const data = isChildrenMode ? [] : props.data;
|
|
296
|
+
const itemToString = isChildrenMode ? () => "" : props.itemToString;
|
|
297
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
298
|
+
const inputType = isChildrenMode ? "icon" : props.inputType ?? "icon";
|
|
299
|
+
const renderSelection = isChildrenMode ? void 0 : props.renderSelection;
|
|
300
|
+
const groupBy = isChildrenMode ? void 0 : props.groupBy;
|
|
301
|
+
const renderGroupHeading = isChildrenMode ? void 0 : props.renderGroupHeading;
|
|
302
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
303
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
304
|
+
const items = React4.useMemo(() => {
|
|
305
|
+
const dataItems = data.map((item) => ({
|
|
306
|
+
value: String(item.id),
|
|
307
|
+
label: itemToString(item)
|
|
308
|
+
}));
|
|
309
|
+
if (includePlaceholderItem) {
|
|
310
|
+
return [{ value: null, label: placeholder ?? "" }, ...dataItems];
|
|
311
|
+
}
|
|
312
|
+
return dataItems;
|
|
313
|
+
}, [data, itemToString, includePlaceholderItem, placeholder]);
|
|
314
|
+
const dataWithPlaceholder = includePlaceholderItem ? [{ id: null, value: null, label: placeholder ?? "" }, ...data] : data;
|
|
315
|
+
const isControlled = selectedItemId !== void 0;
|
|
316
|
+
const [internalValue, setInternalValue] = React4.useState(
|
|
317
|
+
defaultSelectedItemId != null ? String(defaultSelectedItemId) : null
|
|
318
|
+
);
|
|
319
|
+
const value = isControlled ? selectedItemId != null ? String(selectedItemId) : null : internalValue;
|
|
320
|
+
const selectedItem = React4.useMemo(
|
|
321
|
+
() => value != null ? data.find((d) => String(d.id) === value) ?? null : null,
|
|
322
|
+
[data, value]
|
|
323
|
+
);
|
|
324
|
+
function handleValueChange(newValue) {
|
|
325
|
+
if (!isControlled) {
|
|
326
|
+
setInternalValue(newValue);
|
|
327
|
+
}
|
|
328
|
+
if (onSelectedItemIdChange) {
|
|
329
|
+
if (newValue == null) {
|
|
330
|
+
onSelectedItemIdChange(null);
|
|
331
|
+
} else {
|
|
332
|
+
const item = data.find((d) => String(d.id) === newValue);
|
|
333
|
+
onSelectedItemIdChange(item ? item.id : null);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return /* @__PURE__ */ jsxs2(Field, { children: [
|
|
338
|
+
/* @__PURE__ */ jsx3("div", { ref: containerRef, style: { position: "relative" } }),
|
|
339
|
+
/* @__PURE__ */ jsxs2(
|
|
340
|
+
Select3.Root,
|
|
341
|
+
{
|
|
342
|
+
items,
|
|
343
|
+
value,
|
|
344
|
+
disabled: props.disabled,
|
|
345
|
+
onValueChange: handleValueChange,
|
|
346
|
+
id,
|
|
347
|
+
children: [
|
|
348
|
+
/* @__PURE__ */ jsxs2(SelectTrigger, { children: [
|
|
349
|
+
/* @__PURE__ */ jsx3(StyledValue, { placeholder, children: renderSelection && selectedItem ? renderSelection(selectedItem) : void 0 }),
|
|
350
|
+
/* @__PURE__ */ jsx3(SelectIcon, { children: /* @__PURE__ */ jsx3(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx3(ChevronIcon, {}) }) })
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ jsx3(Select3.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx3(SelectPositioner, { sideOffset: 8, alignItemWithTrigger: false, children: /* @__PURE__ */ jsxs2(SelectPopup, { children: [
|
|
353
|
+
/* @__PURE__ */ jsx3(Select3.ScrollUpArrow, {}),
|
|
354
|
+
/* @__PURE__ */ jsx3(Select3.List, { children: children ? children : groupBy ? groupItems(data, groupBy).map((group, index, arr) => /* @__PURE__ */ jsxs2(React4.Fragment, { children: [
|
|
355
|
+
/* @__PURE__ */ jsxs2(SelectGroup, { children: [
|
|
356
|
+
/* @__PURE__ */ jsx3(SelectGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
357
|
+
group.items.map((item) => /* @__PURE__ */ jsx3(
|
|
358
|
+
SelectItem,
|
|
359
|
+
{
|
|
360
|
+
value: String(item.id),
|
|
361
|
+
label: itemToString(item),
|
|
362
|
+
inputType,
|
|
363
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
364
|
+
},
|
|
365
|
+
item.id
|
|
366
|
+
))
|
|
367
|
+
] }),
|
|
368
|
+
index < arr.length - 1 && /* @__PURE__ */ jsx3(SelectSeparator, {})
|
|
369
|
+
] }, group.value)) : (
|
|
370
|
+
// TODO: need to add a placeholder item when includePlaceholderItem is true
|
|
371
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
372
|
+
includePlaceholderItem && /* @__PURE__ */ jsx3(
|
|
373
|
+
SelectItem,
|
|
374
|
+
{
|
|
375
|
+
value: null,
|
|
376
|
+
label: placeholder ?? ""
|
|
377
|
+
},
|
|
378
|
+
"placeholder"
|
|
379
|
+
),
|
|
380
|
+
data.map((item) => /* @__PURE__ */ jsx3(
|
|
381
|
+
SelectItem,
|
|
382
|
+
{
|
|
383
|
+
value: String(item.id),
|
|
384
|
+
label: itemToString(item),
|
|
385
|
+
inputType,
|
|
386
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
387
|
+
},
|
|
388
|
+
item.id
|
|
389
|
+
))
|
|
390
|
+
] })
|
|
391
|
+
) }),
|
|
392
|
+
/* @__PURE__ */ jsx3(Select3.ScrollDownArrow, {})
|
|
393
|
+
] }) }) })
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
] });
|
|
398
|
+
}
|
|
200
399
|
|
|
201
400
|
// src/v3/MultiSelect.tsx
|
|
202
401
|
import * as React5 from "react";
|
|
@@ -217,6 +416,117 @@ var Placeholder = styled5.div`
|
|
|
217
416
|
padding-bottom: 2px;
|
|
218
417
|
margin: 2px 8px 0px 0px;
|
|
219
418
|
`;
|
|
419
|
+
function MultiSelect(props) {
|
|
420
|
+
const { id, placeholder = "Select..." } = props;
|
|
421
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
422
|
+
const data = isChildrenMode ? [] : props.data;
|
|
423
|
+
const itemToString = isChildrenMode ? () => "" : props.itemToString;
|
|
424
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
425
|
+
const customRenderSelections = isChildrenMode ? void 0 : props.customRenderSelections;
|
|
426
|
+
const maxSelections = isChildrenMode ? void 0 : props.maxSelections;
|
|
427
|
+
const renderSelection = isChildrenMode ? void 0 : props.renderSelection;
|
|
428
|
+
const removeSelectedItems = isChildrenMode ? false : props.removeSelectedItems ?? false;
|
|
429
|
+
const inputType = isChildrenMode ? "checkbox" : removeSelectedItems ? "none" : props.inputType ?? "checkbox";
|
|
430
|
+
const selectedItemIds = props.selectedItemIds;
|
|
431
|
+
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
432
|
+
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
433
|
+
const groupBy = isChildrenMode ? void 0 : props.groupBy;
|
|
434
|
+
const renderGroupHeading = isChildrenMode ? void 0 : props.renderGroupHeading;
|
|
435
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
436
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
437
|
+
const isControlled = selectedItemIds !== void 0;
|
|
438
|
+
const [internalValue, setInternalValue] = React5.useState(
|
|
439
|
+
() => defaultSelectedItemIds?.map(String) ?? []
|
|
440
|
+
);
|
|
441
|
+
const value = isControlled ? selectedItemIds.map(String) : internalValue;
|
|
442
|
+
const items = React5.useMemo(
|
|
443
|
+
() => data.map((item) => ({
|
|
444
|
+
value: String(item.id),
|
|
445
|
+
label: itemToString(item)
|
|
446
|
+
})),
|
|
447
|
+
[data, itemToString]
|
|
448
|
+
);
|
|
449
|
+
const selectedItems = React5.useMemo(
|
|
450
|
+
() => value.map((v) => data.find((d) => String(d.id) === v)).filter((d) => d != null),
|
|
451
|
+
[data, value]
|
|
452
|
+
);
|
|
453
|
+
function handleValueChange(newValues) {
|
|
454
|
+
if (!isControlled) {
|
|
455
|
+
setInternalValue(newValues);
|
|
456
|
+
}
|
|
457
|
+
if (onSelectedItemIdsChange) {
|
|
458
|
+
const ids = newValues.map((v) => {
|
|
459
|
+
const item = data.find((d) => String(d.id) === v);
|
|
460
|
+
return item ? item.id : v;
|
|
461
|
+
});
|
|
462
|
+
onSelectedItemIdsChange(ids);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return /* @__PURE__ */ jsxs3(Field, { children: [
|
|
466
|
+
/* @__PURE__ */ jsx4("div", { ref: containerRef, style: { position: "relative" } }),
|
|
467
|
+
/* @__PURE__ */ jsxs3(
|
|
468
|
+
Select4.Root,
|
|
469
|
+
{
|
|
470
|
+
multiple: true,
|
|
471
|
+
items,
|
|
472
|
+
value,
|
|
473
|
+
onValueChange: handleValueChange,
|
|
474
|
+
disabled: props.disabled,
|
|
475
|
+
id,
|
|
476
|
+
children: [
|
|
477
|
+
/* @__PURE__ */ jsxs3(SelectTrigger, { children: [
|
|
478
|
+
/* @__PURE__ */ jsx4(SelectValue, { children: () => {
|
|
479
|
+
if (renderSelection) return renderSelection(selectedItems);
|
|
480
|
+
if (selectedItems.length === 0)
|
|
481
|
+
return /* @__PURE__ */ jsx4(Placeholder, { children: placeholder });
|
|
482
|
+
const visibleItems = maxSelections != null ? selectedItems.slice(0, maxSelections) : selectedItems;
|
|
483
|
+
const overflowCount = maxSelections != null && selectedItems.length > maxSelections ? selectedItems.length - maxSelections : 0;
|
|
484
|
+
const text = visibleItems.map(
|
|
485
|
+
(item) => customRenderSelections ? customRenderSelections(item) : itemToString(item)
|
|
486
|
+
).join(", ");
|
|
487
|
+
return /* @__PURE__ */ jsxs3(SelectionText, { children: [
|
|
488
|
+
text,
|
|
489
|
+
overflowCount > 0 ? `, +${overflowCount}` : ""
|
|
490
|
+
] });
|
|
491
|
+
} }),
|
|
492
|
+
/* @__PURE__ */ jsx4(SelectIcon, { children: /* @__PURE__ */ jsx4(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx4(ChevronIcon, {}) }) })
|
|
493
|
+
] }),
|
|
494
|
+
/* @__PURE__ */ jsx4(Select4.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx4(SelectPositioner, { sideOffset: 8, alignItemWithTrigger: false, children: /* @__PURE__ */ jsxs3(SelectPopup, { children: [
|
|
495
|
+
/* @__PURE__ */ jsx4(Select4.ScrollUpArrow, {}),
|
|
496
|
+
/* @__PURE__ */ jsx4(Select4.List, { children: children ? children : groupBy ? groupItems(data, groupBy).map((group, index, arr) => /* @__PURE__ */ jsxs3(React5.Fragment, { children: [
|
|
497
|
+
/* @__PURE__ */ jsxs3(SelectGroup, { children: [
|
|
498
|
+
/* @__PURE__ */ jsx4(SelectGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
499
|
+
group.items.map((item) => /* @__PURE__ */ jsx4(
|
|
500
|
+
SelectItem,
|
|
501
|
+
{
|
|
502
|
+
value: String(item.id),
|
|
503
|
+
label: itemToString(item),
|
|
504
|
+
inputType,
|
|
505
|
+
hidden: removeSelectedItems && value.includes(String(item.id)),
|
|
506
|
+
renderItem: renderItem ? () => renderItem(item) : void 0
|
|
507
|
+
},
|
|
508
|
+
item.id
|
|
509
|
+
))
|
|
510
|
+
] }),
|
|
511
|
+
index < arr.length - 1 && /* @__PURE__ */ jsx4(SelectSeparator, {})
|
|
512
|
+
] }, group.value)) : data.map((item) => /* @__PURE__ */ jsx4(
|
|
513
|
+
SelectItem,
|
|
514
|
+
{
|
|
515
|
+
value: String(item.id),
|
|
516
|
+
label: itemToString(item),
|
|
517
|
+
inputType,
|
|
518
|
+
hidden: removeSelectedItems && value.includes(String(item.id)),
|
|
519
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
520
|
+
},
|
|
521
|
+
item.id
|
|
522
|
+
)) }),
|
|
523
|
+
/* @__PURE__ */ jsx4(Select4.ScrollDownArrow, {})
|
|
524
|
+
] }) }) })
|
|
525
|
+
]
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
] });
|
|
529
|
+
}
|
|
220
530
|
|
|
221
531
|
// src/v3/SingleCombobox.tsx
|
|
222
532
|
import * as React8 from "react";
|
|
@@ -255,6 +565,11 @@ var ComboboxInputGroup = styled6(Combobox.InputGroup)`
|
|
|
255
565
|
&:focus-within {
|
|
256
566
|
${focusRing2}
|
|
257
567
|
}
|
|
568
|
+
|
|
569
|
+
&[data-disabled] {
|
|
570
|
+
opacity: 0.4;
|
|
571
|
+
cursor: not-allowed;
|
|
572
|
+
}
|
|
258
573
|
`;
|
|
259
574
|
var ComboboxInput = styled6(Combobox.Input)`
|
|
260
575
|
flex: 1;
|
|
@@ -270,6 +585,10 @@ var ComboboxInput = styled6(Combobox.Input)`
|
|
|
270
585
|
&::placeholder {
|
|
271
586
|
color: ${({ theme }) => theme.colors.text.subtext};
|
|
272
587
|
}
|
|
588
|
+
|
|
589
|
+
&:disabled {
|
|
590
|
+
cursor: not-allowed;
|
|
591
|
+
}
|
|
273
592
|
`;
|
|
274
593
|
var ComboboxTokenInput = styled6(Combobox.Input)`
|
|
275
594
|
flex: 1;
|
|
@@ -286,6 +605,10 @@ var ComboboxTokenInput = styled6(Combobox.Input)`
|
|
|
286
605
|
&::placeholder {
|
|
287
606
|
color: ${({ theme }) => theme.colors.text.subtext};
|
|
288
607
|
}
|
|
608
|
+
|
|
609
|
+
&:disabled {
|
|
610
|
+
cursor: not-allowed;
|
|
611
|
+
}
|
|
289
612
|
`;
|
|
290
613
|
var ComboboxActionButtons = styled6.div`
|
|
291
614
|
display: flex;
|
|
@@ -529,6 +852,11 @@ var ComboboxTokenInputGroup = styled6(Combobox.InputGroup)`
|
|
|
529
852
|
&:focus-within {
|
|
530
853
|
${focusRing2}
|
|
531
854
|
}
|
|
855
|
+
|
|
856
|
+
&[data-disabled] {
|
|
857
|
+
opacity: 0.4;
|
|
858
|
+
cursor: not-allowed;
|
|
859
|
+
}
|
|
532
860
|
`;
|
|
533
861
|
var Token = styled6.span`
|
|
534
862
|
display: inline-flex;
|
|
@@ -589,6 +917,11 @@ var SearchableSelectTrigger = styled6(Combobox.Trigger)`
|
|
|
589
917
|
&[data-popup-open] [data-chevron] {
|
|
590
918
|
transform: rotate(-180deg);
|
|
591
919
|
}
|
|
920
|
+
|
|
921
|
+
&[data-disabled] {
|
|
922
|
+
opacity: 0.4;
|
|
923
|
+
cursor: not-allowed;
|
|
924
|
+
}
|
|
592
925
|
`;
|
|
593
926
|
var SearchableSelectTriggerIcon = styled6(Combobox.Icon)`
|
|
594
927
|
display: flex;
|
|
@@ -666,6 +999,11 @@ var SearchableSelectTokenTrigger = styled6(Combobox.Trigger)`
|
|
|
666
999
|
&[data-popup-open] [data-chevron] {
|
|
667
1000
|
transform: rotate(-180deg);
|
|
668
1001
|
}
|
|
1002
|
+
|
|
1003
|
+
&[data-disabled] {
|
|
1004
|
+
opacity: 0.4;
|
|
1005
|
+
cursor: not-allowed;
|
|
1006
|
+
}
|
|
669
1007
|
`;
|
|
670
1008
|
|
|
671
1009
|
// src/v3/Common/ComboboxItem.tsx
|
|
@@ -688,6 +1026,7 @@ function ComboboxItemInner({
|
|
|
688
1026
|
"data-index": dataIndex,
|
|
689
1027
|
style,
|
|
690
1028
|
ref,
|
|
1029
|
+
"data-qa-menu-item": label,
|
|
691
1030
|
children: [
|
|
692
1031
|
inputType === "radio" ? /* @__PURE__ */ jsx6(
|
|
693
1032
|
ComboboxItemIndicator,
|
|
@@ -721,22 +1060,310 @@ function ComboboxItemInner({
|
|
|
721
1060
|
}
|
|
722
1061
|
)
|
|
723
1062
|
}
|
|
724
|
-
) : inputType === "icon" ? /* @__PURE__ */ jsx6(ComboboxItemIndicator, { keepMounted: true, children: /* @__PURE__ */ jsx6(
|
|
1063
|
+
) : inputType === "icon" ? /* @__PURE__ */ jsx6(ComboboxItemIndicator, { keepMounted: true, children: /* @__PURE__ */ jsx6(CheckIcon2, {}) }) : null,
|
|
725
1064
|
/* @__PURE__ */ jsx6("span", { children: renderItem ? renderItem() : label })
|
|
726
1065
|
]
|
|
727
1066
|
}
|
|
728
1067
|
);
|
|
729
1068
|
}
|
|
730
1069
|
var ComboboxItem2 = React6.forwardRef(ComboboxItemInner);
|
|
1070
|
+
var ComboboxItem_default = ComboboxItem2;
|
|
731
1071
|
|
|
732
1072
|
// src/v3/Common/VirtualizedComboboxList.tsx
|
|
733
1073
|
import * as React7 from "react";
|
|
734
1074
|
import { Combobox as Combobox2 } from "@base-ui/react/combobox";
|
|
735
1075
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
736
1076
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1077
|
+
var ITEM_HEIGHT = 32;
|
|
1078
|
+
function isGroupHeaderSentinel(v) {
|
|
1079
|
+
return typeof v === "object" && v !== null && "__groupHeader" in v;
|
|
1080
|
+
}
|
|
1081
|
+
function isSelectAllSentinel(v) {
|
|
1082
|
+
return typeof v === "object" && v !== null && "__selectAll" in v;
|
|
1083
|
+
}
|
|
1084
|
+
function VirtualizedComboboxList({
|
|
1085
|
+
open,
|
|
1086
|
+
virtualizerRef,
|
|
1087
|
+
value,
|
|
1088
|
+
groups,
|
|
1089
|
+
data,
|
|
1090
|
+
itemToString,
|
|
1091
|
+
inputType,
|
|
1092
|
+
renderItem,
|
|
1093
|
+
renderGroupHeading
|
|
1094
|
+
}) {
|
|
1095
|
+
const filteredItems = Combobox2.useFilteredItems();
|
|
1096
|
+
const scrollElementRef = React7.useRef(null);
|
|
1097
|
+
const virtualizer = useVirtualizer({
|
|
1098
|
+
enabled: open,
|
|
1099
|
+
count: filteredItems.length,
|
|
1100
|
+
getScrollElement: () => scrollElementRef.current,
|
|
1101
|
+
estimateSize: () => ITEM_HEIGHT,
|
|
1102
|
+
overscan: 20,
|
|
1103
|
+
paddingStart: 4,
|
|
1104
|
+
paddingEnd: 4,
|
|
1105
|
+
scrollPaddingStart: 4,
|
|
1106
|
+
scrollPaddingEnd: 4
|
|
1107
|
+
});
|
|
1108
|
+
React7.useImperativeHandle(virtualizerRef, () => virtualizer);
|
|
1109
|
+
const handleScrollRef = React7.useCallback(
|
|
1110
|
+
(el) => {
|
|
1111
|
+
scrollElementRef.current = el;
|
|
1112
|
+
if (el) virtualizer.measure();
|
|
1113
|
+
},
|
|
1114
|
+
[virtualizer]
|
|
1115
|
+
);
|
|
1116
|
+
if (!filteredItems.length) return null;
|
|
1117
|
+
const selectedArray = Array.isArray(value) ? value : value != null ? [value] : [];
|
|
1118
|
+
const totalSize = virtualizer.getTotalSize();
|
|
1119
|
+
return /* @__PURE__ */ jsx7(
|
|
1120
|
+
"div",
|
|
1121
|
+
{
|
|
1122
|
+
role: "presentation",
|
|
1123
|
+
ref: handleScrollRef,
|
|
1124
|
+
style: {
|
|
1125
|
+
flex: 1,
|
|
1126
|
+
minHeight: 0,
|
|
1127
|
+
overflowY: "auto",
|
|
1128
|
+
overscrollBehavior: "contain"
|
|
1129
|
+
},
|
|
1130
|
+
children: /* @__PURE__ */ jsx7(
|
|
1131
|
+
"div",
|
|
1132
|
+
{
|
|
1133
|
+
role: "presentation",
|
|
1134
|
+
style: { position: "relative", width: "100%", height: totalSize },
|
|
1135
|
+
children: virtualizer.getVirtualItems().map((virtualItem) => {
|
|
1136
|
+
const row = filteredItems[virtualItem.index];
|
|
1137
|
+
if (!row) return null;
|
|
1138
|
+
const baseStyle = {
|
|
1139
|
+
position: "absolute",
|
|
1140
|
+
top: 0,
|
|
1141
|
+
left: 0,
|
|
1142
|
+
width: "100%",
|
|
1143
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
1144
|
+
boxSizing: "border-box"
|
|
1145
|
+
};
|
|
1146
|
+
if (isSelectAllSentinel(row)) {
|
|
1147
|
+
const selectedCount = selectedArray.length;
|
|
1148
|
+
const totalCount = data.length;
|
|
1149
|
+
const state = selectedCount === 0 ? "none" : selectedCount === totalCount ? "all" : "partial";
|
|
1150
|
+
return /* @__PURE__ */ jsxs5(
|
|
1151
|
+
ComboboxSelectAllItem,
|
|
1152
|
+
{
|
|
1153
|
+
index: virtualItem.index,
|
|
1154
|
+
"data-index": virtualItem.index,
|
|
1155
|
+
value: row,
|
|
1156
|
+
style: baseStyle,
|
|
1157
|
+
ref: virtualizer.measureElement,
|
|
1158
|
+
children: [
|
|
1159
|
+
/* @__PURE__ */ jsx7(ComboboxGroupHeaderCheckbox, { $state: state, id: "__selectAll" }),
|
|
1160
|
+
"Select all"
|
|
1161
|
+
]
|
|
1162
|
+
},
|
|
1163
|
+
virtualItem.key
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (isGroupHeaderSentinel(row)) {
|
|
1167
|
+
const group = groups?.find((g) => g.value === row.groupName);
|
|
1168
|
+
const groupItems2 = group?.items ?? [];
|
|
1169
|
+
const selectedCount = groupItems2.filter(
|
|
1170
|
+
(item2) => selectedArray.some((s) => s.id === item2.id)
|
|
1171
|
+
).length;
|
|
1172
|
+
const state = selectedCount === 0 ? "none" : selectedCount === groupItems2.length ? "all" : "partial";
|
|
1173
|
+
return /* @__PURE__ */ jsxs5(
|
|
1174
|
+
ComboboxGroupHeaderItem,
|
|
1175
|
+
{
|
|
1176
|
+
index: virtualItem.index,
|
|
1177
|
+
"data-index": virtualItem.index,
|
|
1178
|
+
value: row,
|
|
1179
|
+
style: baseStyle,
|
|
1180
|
+
ref: virtualizer.measureElement,
|
|
1181
|
+
children: [
|
|
1182
|
+
/* @__PURE__ */ jsx7(
|
|
1183
|
+
ComboboxGroupHeaderCheckbox,
|
|
1184
|
+
{
|
|
1185
|
+
$state: state,
|
|
1186
|
+
id: `__header-${row.groupName}`
|
|
1187
|
+
}
|
|
1188
|
+
),
|
|
1189
|
+
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
1190
|
+
]
|
|
1191
|
+
},
|
|
1192
|
+
virtualItem.key
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
const item = row;
|
|
1196
|
+
return /* @__PURE__ */ jsx7(
|
|
1197
|
+
ComboboxItem_default,
|
|
1198
|
+
{
|
|
1199
|
+
index: virtualItem.index,
|
|
1200
|
+
"data-index": virtualItem.index,
|
|
1201
|
+
value: item,
|
|
1202
|
+
itemId: String(item.id),
|
|
1203
|
+
label: itemToString(item),
|
|
1204
|
+
inputType,
|
|
1205
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item),
|
|
1206
|
+
style: baseStyle,
|
|
1207
|
+
ref: virtualizer.measureElement
|
|
1208
|
+
},
|
|
1209
|
+
virtualItem.key
|
|
1210
|
+
);
|
|
1211
|
+
})
|
|
1212
|
+
}
|
|
1213
|
+
)
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
737
1217
|
|
|
738
1218
|
// src/v3/SingleCombobox.tsx
|
|
739
1219
|
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1220
|
+
function SingleCombobox(props) {
|
|
1221
|
+
const {
|
|
1222
|
+
id,
|
|
1223
|
+
placeholder = "Search...",
|
|
1224
|
+
emptyText = "No results found."
|
|
1225
|
+
} = props;
|
|
1226
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
1227
|
+
const data = isChildrenMode ? [] : props.data;
|
|
1228
|
+
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
1229
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
1230
|
+
const inputType = isChildrenMode ? "icon" : props.inputType ?? "icon";
|
|
1231
|
+
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1232
|
+
const groupBy = isChildrenMode ? void 0 : isVirtualized ? void 0 : props.groupBy;
|
|
1233
|
+
const renderGroupHeading = isChildrenMode ? void 0 : isVirtualized ? void 0 : props.renderGroupHeading;
|
|
1234
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
1235
|
+
const selectedItemId = props.selectedItemId;
|
|
1236
|
+
const defaultSelectedItemId = props.defaultSelectedItemId;
|
|
1237
|
+
const onSelectedItemIdChange = props.onSelectedItemIdChange;
|
|
1238
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1239
|
+
const [open, setOpen] = React8.useState(false);
|
|
1240
|
+
const virtualizerRef = React8.useRef(null);
|
|
1241
|
+
const isControlled = selectedItemId !== void 0;
|
|
1242
|
+
const idToItem = React8.useCallback(
|
|
1243
|
+
(id2) => {
|
|
1244
|
+
if (id2 == null) return null;
|
|
1245
|
+
return data.find((d) => String(d.id) === String(id2)) ?? null;
|
|
1246
|
+
},
|
|
1247
|
+
[data]
|
|
1248
|
+
);
|
|
1249
|
+
const [internalValue, setInternalValue] = React8.useState(
|
|
1250
|
+
() => idToItem(defaultSelectedItemId)
|
|
1251
|
+
);
|
|
1252
|
+
const value = isControlled ? idToItem(selectedItemId) : internalValue;
|
|
1253
|
+
function handleValueChange(newItem) {
|
|
1254
|
+
if (!isControlled) {
|
|
1255
|
+
setInternalValue(newItem);
|
|
1256
|
+
}
|
|
1257
|
+
if (onSelectedItemIdChange) {
|
|
1258
|
+
onSelectedItemIdChange(newItem ? newItem.id : null);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
const groups = React8.useMemo(
|
|
1262
|
+
() => groupBy ? groupItems(data, groupBy) : null,
|
|
1263
|
+
[data, groupBy]
|
|
1264
|
+
);
|
|
1265
|
+
return /* @__PURE__ */ jsxs6(Field, { children: [
|
|
1266
|
+
/* @__PURE__ */ jsx8("div", { ref: containerRef, style: { position: "relative" } }),
|
|
1267
|
+
/* @__PURE__ */ jsxs6(
|
|
1268
|
+
Combobox3.Root,
|
|
1269
|
+
{
|
|
1270
|
+
id,
|
|
1271
|
+
value,
|
|
1272
|
+
onValueChange: handleValueChange,
|
|
1273
|
+
itemToStringLabel: itemToString,
|
|
1274
|
+
isItemEqualToValue: (a, b) => a != null && b != null && a.id === b.id,
|
|
1275
|
+
items: isVirtualized ? data : groups ?? (isChildrenMode ? void 0 : data),
|
|
1276
|
+
virtualized: isVirtualized,
|
|
1277
|
+
disabled: props.disabled,
|
|
1278
|
+
open,
|
|
1279
|
+
onOpenChange: setOpen,
|
|
1280
|
+
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
1281
|
+
const virt = virtualizerRef.current;
|
|
1282
|
+
if (!item || !virt) return;
|
|
1283
|
+
const isStart = index === 0;
|
|
1284
|
+
const isEnd = index === virt.options.count - 1;
|
|
1285
|
+
if (reason === "none" || reason === "keyboard" && (isStart || isEnd)) {
|
|
1286
|
+
queueMicrotask(() => {
|
|
1287
|
+
virt.scrollToIndex(index, {
|
|
1288
|
+
align: isEnd ? "start" : "end"
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
} : void 0,
|
|
1293
|
+
children: [
|
|
1294
|
+
/* @__PURE__ */ jsxs6(ComboboxInputGroup, { children: [
|
|
1295
|
+
/* @__PURE__ */ jsx8(ComboboxInput, { placeholder, id }),
|
|
1296
|
+
/* @__PURE__ */ jsxs6(ComboboxActionButtons, { children: [
|
|
1297
|
+
/* @__PURE__ */ jsx8(ComboboxClear, { "aria-label": "Clear selection", children: /* @__PURE__ */ jsx8(ClearIcon, {}) }),
|
|
1298
|
+
/* @__PURE__ */ jsx8(ComboboxTrigger, { "aria-label": "Open popup", children: /* @__PURE__ */ jsx8(StyledChevron, { $isOpen: open, children: /* @__PURE__ */ jsx8(ChevronIcon, {}) }) })
|
|
1299
|
+
] })
|
|
1300
|
+
] }),
|
|
1301
|
+
/* @__PURE__ */ jsx8(Combobox3.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx8(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ jsxs6(
|
|
1302
|
+
ComboboxPopup,
|
|
1303
|
+
{
|
|
1304
|
+
style: isVirtualized ? { display: "flex", flexDirection: "column" } : void 0,
|
|
1305
|
+
children: [
|
|
1306
|
+
/* @__PURE__ */ jsx8(ComboboxEmpty, { children: emptyText }),
|
|
1307
|
+
/* @__PURE__ */ jsx8(
|
|
1308
|
+
ComboboxList,
|
|
1309
|
+
{
|
|
1310
|
+
style: isVirtualized ? {
|
|
1311
|
+
padding: 0,
|
|
1312
|
+
flex: 1,
|
|
1313
|
+
minHeight: 0,
|
|
1314
|
+
display: "flex",
|
|
1315
|
+
flexDirection: "column"
|
|
1316
|
+
} : void 0,
|
|
1317
|
+
children: isVirtualized ? /* @__PURE__ */ jsx8(
|
|
1318
|
+
VirtualizedComboboxList,
|
|
1319
|
+
{
|
|
1320
|
+
open,
|
|
1321
|
+
virtualizerRef,
|
|
1322
|
+
value,
|
|
1323
|
+
groups,
|
|
1324
|
+
data,
|
|
1325
|
+
itemToString,
|
|
1326
|
+
inputType,
|
|
1327
|
+
renderItem,
|
|
1328
|
+
renderGroupHeading
|
|
1329
|
+
}
|
|
1330
|
+
) : children ? children : groups ? (group, index) => /* @__PURE__ */ jsxs6(React8.Fragment, { children: [
|
|
1331
|
+
/* @__PURE__ */ jsxs6(ComboboxGroup, { items: group.items, children: [
|
|
1332
|
+
/* @__PURE__ */ jsx8(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
1333
|
+
/* @__PURE__ */ jsx8(Combobox3.Collection, { children: (item) => /* @__PURE__ */ jsx8(
|
|
1334
|
+
ComboboxItem_default,
|
|
1335
|
+
{
|
|
1336
|
+
value: item,
|
|
1337
|
+
itemId: String(item.id),
|
|
1338
|
+
label: itemToString(item),
|
|
1339
|
+
inputType,
|
|
1340
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1341
|
+
},
|
|
1342
|
+
item.id
|
|
1343
|
+
) })
|
|
1344
|
+
] }),
|
|
1345
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx8(ComboboxSeparator, {})
|
|
1346
|
+
] }, group.value) : (item) => /* @__PURE__ */ jsx8(
|
|
1347
|
+
ComboboxItem_default,
|
|
1348
|
+
{
|
|
1349
|
+
value: item,
|
|
1350
|
+
itemId: String(item.id),
|
|
1351
|
+
label: itemToString(item),
|
|
1352
|
+
inputType,
|
|
1353
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1354
|
+
},
|
|
1355
|
+
item.id
|
|
1356
|
+
)
|
|
1357
|
+
}
|
|
1358
|
+
)
|
|
1359
|
+
]
|
|
1360
|
+
}
|
|
1361
|
+
) }) })
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
)
|
|
1365
|
+
] });
|
|
1366
|
+
}
|
|
740
1367
|
|
|
741
1368
|
// src/v3/MultiCombobox.tsx
|
|
742
1369
|
import * as React9 from "react";
|
|
@@ -744,12 +1371,473 @@ import { Combobox as Combobox4 } from "@base-ui/react/combobox";
|
|
|
744
1371
|
import "@tanstack/react-virtual";
|
|
745
1372
|
import Icon3 from "@sproutsocial/seeds-react-icon";
|
|
746
1373
|
import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1374
|
+
function makeGroupHeaderSentinel(groupName) {
|
|
1375
|
+
return { __groupHeader: true, groupName };
|
|
1376
|
+
}
|
|
1377
|
+
function isGroupHeaderSentinel2(v) {
|
|
1378
|
+
return typeof v === "object" && v !== null && "__groupHeader" in v;
|
|
1379
|
+
}
|
|
1380
|
+
var SELECT_ALL_SENTINEL = { __selectAll: true };
|
|
1381
|
+
function isSelectAllSentinel2(v) {
|
|
1382
|
+
return typeof v === "object" && v !== null && "__selectAll" in v;
|
|
1383
|
+
}
|
|
1384
|
+
function MultiCombobox(props) {
|
|
1385
|
+
const {
|
|
1386
|
+
id,
|
|
1387
|
+
placeholder = "Search...",
|
|
1388
|
+
emptyText = "No results found.",
|
|
1389
|
+
isLoading = false,
|
|
1390
|
+
loadingText = "Loading..."
|
|
1391
|
+
} = props;
|
|
1392
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
1393
|
+
const data = isChildrenMode ? [] : props.data;
|
|
1394
|
+
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
1395
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
1396
|
+
const renderToken = isChildrenMode ? void 0 : props.renderToken;
|
|
1397
|
+
const renderSelection = isChildrenMode ? void 0 : props.renderSelection;
|
|
1398
|
+
const maxTokens = isChildrenMode ? void 0 : props.maxTokens;
|
|
1399
|
+
const removeSelectedItems = isChildrenMode ? false : props.removeSelectedItems ?? false;
|
|
1400
|
+
const inputType = isChildrenMode ? "checkbox" : removeSelectedItems ? "none" : props.inputType ?? "checkbox";
|
|
1401
|
+
const groupBy = isChildrenMode ? void 0 : props.groupBy;
|
|
1402
|
+
const renderGroupHeading = isChildrenMode ? void 0 : props.renderGroupHeading;
|
|
1403
|
+
const selectHeadings = isChildrenMode ? false : props.selectHeadings ?? false;
|
|
1404
|
+
const selectAll = isChildrenMode ? false : props.selectAll ?? false;
|
|
1405
|
+
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1406
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
1407
|
+
const selectedItemIds = props.selectedItemIds;
|
|
1408
|
+
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
1409
|
+
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
1410
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1411
|
+
const [open, setOpen] = React9.useState(false);
|
|
1412
|
+
const virtualizerRef = React9.useRef(null);
|
|
1413
|
+
const isControlled = selectedItemIds !== void 0;
|
|
1414
|
+
const idsToItems = React9.useCallback(
|
|
1415
|
+
(ids) => ids.map((id2) => data.find((d) => d.id === id2)).filter((d) => d != null),
|
|
1416
|
+
[data]
|
|
1417
|
+
);
|
|
1418
|
+
const [internalValue, setInternalValue] = React9.useState(
|
|
1419
|
+
() => defaultSelectedItemIds ? idsToItems(defaultSelectedItemIds) : []
|
|
1420
|
+
);
|
|
1421
|
+
const value = isControlled ? idsToItems(selectedItemIds) : internalValue;
|
|
1422
|
+
const visibleData = React9.useMemo(
|
|
1423
|
+
() => removeSelectedItems ? data.filter((item) => !value.some((v) => v.id === item.id)) : data,
|
|
1424
|
+
[data, value, removeSelectedItems]
|
|
1425
|
+
);
|
|
1426
|
+
const groups = React9.useMemo(
|
|
1427
|
+
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
1428
|
+
[visibleData, groupBy]
|
|
1429
|
+
);
|
|
1430
|
+
const flatItems = React9.useMemo(() => {
|
|
1431
|
+
if (!groups || !selectHeadings && !selectAll) return null;
|
|
1432
|
+
const rows = [];
|
|
1433
|
+
if (selectAll) rows.push(SELECT_ALL_SENTINEL);
|
|
1434
|
+
for (const group of groups) {
|
|
1435
|
+
rows.push(makeGroupHeaderSentinel(group.value));
|
|
1436
|
+
rows.push(...group.items);
|
|
1437
|
+
}
|
|
1438
|
+
return rows;
|
|
1439
|
+
}, [groups, selectHeadings, selectAll]);
|
|
1440
|
+
function setSelectedItems(items) {
|
|
1441
|
+
if (!isControlled) setInternalValue(items);
|
|
1442
|
+
if (onSelectedItemIdsChange)
|
|
1443
|
+
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
1444
|
+
}
|
|
1445
|
+
function handleValueChange(next) {
|
|
1446
|
+
const selectAllClicked = next.filter(isSelectAllSentinel2);
|
|
1447
|
+
const groupSentinels = next.filter(isGroupHeaderSentinel2);
|
|
1448
|
+
const realItems = next.filter(
|
|
1449
|
+
(v) => !isGroupHeaderSentinel2(v) && !isSelectAllSentinel2(v)
|
|
1450
|
+
);
|
|
1451
|
+
if (selectAllClicked.length > 0) {
|
|
1452
|
+
const allSelected = data.every(
|
|
1453
|
+
(d) => realItems.some((s) => s.id === d.id)
|
|
1454
|
+
);
|
|
1455
|
+
setSelectedItems(allSelected ? [] : [...data]);
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (groupSentinels.length === 0) {
|
|
1459
|
+
setSelectedItems(realItems);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
let updated = [...realItems];
|
|
1463
|
+
for (const sentinel of groupSentinels) {
|
|
1464
|
+
const group = groups?.find((g) => g.value === sentinel.groupName);
|
|
1465
|
+
if (!group) continue;
|
|
1466
|
+
const allSelected = group.items.every(
|
|
1467
|
+
(item) => updated.some((s) => s.id === item.id)
|
|
1468
|
+
);
|
|
1469
|
+
if (allSelected) {
|
|
1470
|
+
updated = updated.filter(
|
|
1471
|
+
(s) => !group.items.some((item) => item.id === s.id)
|
|
1472
|
+
);
|
|
1473
|
+
} else {
|
|
1474
|
+
const missing = group.items.filter(
|
|
1475
|
+
(item) => !updated.some((s) => s.id === item.id)
|
|
1476
|
+
);
|
|
1477
|
+
updated = [...updated, ...missing];
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
setSelectedItems(updated);
|
|
1481
|
+
}
|
|
1482
|
+
function handleSimpleValueChange(newItems) {
|
|
1483
|
+
setSelectedItems(newItems);
|
|
1484
|
+
}
|
|
1485
|
+
function removeItem(item, e) {
|
|
1486
|
+
e.preventDefault();
|
|
1487
|
+
e.stopPropagation();
|
|
1488
|
+
setSelectedItems(value.filter((v) => v.id !== item.id));
|
|
1489
|
+
}
|
|
1490
|
+
function renderSentinelRow(row) {
|
|
1491
|
+
if (isSelectAllSentinel2(row)) {
|
|
1492
|
+
const totalCount = data.length;
|
|
1493
|
+
const selectedCount = value.length;
|
|
1494
|
+
const state = selectedCount === 0 ? "none" : selectedCount === totalCount ? "all" : "partial";
|
|
1495
|
+
return /* @__PURE__ */ jsxs7(ComboboxSelectAllItem, { value: row, children: [
|
|
1496
|
+
/* @__PURE__ */ jsx9(ComboboxGroupHeaderCheckbox, { $state: state, id: "__selectAll" }),
|
|
1497
|
+
"Select all"
|
|
1498
|
+
] }, "__selectAll");
|
|
1499
|
+
}
|
|
1500
|
+
if (isGroupHeaderSentinel2(row)) {
|
|
1501
|
+
const group = groups?.find((g) => g.value === row.groupName);
|
|
1502
|
+
const groupItemsList = group?.items ?? [];
|
|
1503
|
+
const selectedCount = groupItemsList.filter(
|
|
1504
|
+
(item2) => value.some((s) => s.id === item2.id)
|
|
1505
|
+
).length;
|
|
1506
|
+
const state = selectedCount === 0 ? "none" : selectedCount === groupItemsList.length ? "all" : "partial";
|
|
1507
|
+
return /* @__PURE__ */ jsxs7(ComboboxGroupHeaderItem, { value: row, children: [
|
|
1508
|
+
/* @__PURE__ */ jsx9(
|
|
1509
|
+
ComboboxGroupHeaderCheckbox,
|
|
1510
|
+
{
|
|
1511
|
+
$state: state,
|
|
1512
|
+
id: `__header-${row.groupName}`
|
|
1513
|
+
}
|
|
1514
|
+
),
|
|
1515
|
+
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
1516
|
+
] }, `__header-${row.groupName}`);
|
|
1517
|
+
}
|
|
1518
|
+
const item = row;
|
|
1519
|
+
return /* @__PURE__ */ jsx9(
|
|
1520
|
+
ComboboxItem_default,
|
|
1521
|
+
{
|
|
1522
|
+
value: item,
|
|
1523
|
+
itemId: String(item.id),
|
|
1524
|
+
label: itemToString(item),
|
|
1525
|
+
inputType,
|
|
1526
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1527
|
+
},
|
|
1528
|
+
item.id
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
const rootItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
1532
|
+
const hasSentinels = selectAll || selectHeadings;
|
|
1533
|
+
const rootValue = hasSentinels ? [...value] : value;
|
|
1534
|
+
return /* @__PURE__ */ jsxs7(Field, { children: [
|
|
1535
|
+
/* @__PURE__ */ jsx9("div", { ref: containerRef, style: { position: "relative" } }),
|
|
1536
|
+
/* @__PURE__ */ jsxs7(
|
|
1537
|
+
Combobox4.Root,
|
|
1538
|
+
{
|
|
1539
|
+
multiple: true,
|
|
1540
|
+
id,
|
|
1541
|
+
virtualized: isVirtualized,
|
|
1542
|
+
open,
|
|
1543
|
+
disabled: props.disabled,
|
|
1544
|
+
onOpenChange: setOpen,
|
|
1545
|
+
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
1546
|
+
const virt = virtualizerRef.current;
|
|
1547
|
+
if (!item || !virt) return;
|
|
1548
|
+
const isStart = index === 0;
|
|
1549
|
+
const isEnd = index === virt.options.count - 1;
|
|
1550
|
+
if (reason === "none" || reason === "keyboard" && (isStart || isEnd)) {
|
|
1551
|
+
queueMicrotask(() => {
|
|
1552
|
+
virt.scrollToIndex(index, {
|
|
1553
|
+
align: isEnd ? "start" : "end"
|
|
1554
|
+
});
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
} : void 0,
|
|
1558
|
+
value: rootValue,
|
|
1559
|
+
onValueChange: (
|
|
1560
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1561
|
+
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
1562
|
+
),
|
|
1563
|
+
itemToStringLabel: (item) => {
|
|
1564
|
+
if (!item || isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item))
|
|
1565
|
+
return "";
|
|
1566
|
+
return itemToString(item);
|
|
1567
|
+
},
|
|
1568
|
+
isItemEqualToValue: (a, b) => {
|
|
1569
|
+
if (isGroupHeaderSentinel2(a) && isGroupHeaderSentinel2(b))
|
|
1570
|
+
return a.groupName === b.groupName;
|
|
1571
|
+
if (isSelectAllSentinel2(a) && isSelectAllSentinel2(b)) return true;
|
|
1572
|
+
if (!isGroupHeaderSentinel2(a) && !isSelectAllSentinel2(a) && !isGroupHeaderSentinel2(b) && !isSelectAllSentinel2(b))
|
|
1573
|
+
return a.id === b.id;
|
|
1574
|
+
return false;
|
|
1575
|
+
},
|
|
1576
|
+
items: rootItems,
|
|
1577
|
+
children: [
|
|
1578
|
+
/* @__PURE__ */ jsx9(ComboboxTokenInputGroup, { children: /* @__PURE__ */ jsxs7(ComboboxToken, { children: [
|
|
1579
|
+
renderSelection ? renderSelection(value) : (() => {
|
|
1580
|
+
const visibleTokens = maxTokens != null ? value.slice(0, maxTokens) : value;
|
|
1581
|
+
const overflow = maxTokens != null ? value.length - maxTokens : 0;
|
|
1582
|
+
return /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
1583
|
+
visibleTokens.map((item) => /* @__PURE__ */ jsxs7(Token, { children: [
|
|
1584
|
+
renderToken ? renderToken(item) : itemToString(item),
|
|
1585
|
+
/* @__PURE__ */ jsx9(
|
|
1586
|
+
TokenRemove,
|
|
1587
|
+
{
|
|
1588
|
+
"aria-label": `Remove ${itemToString(item)}`,
|
|
1589
|
+
onPointerDown: (e) => e.preventDefault(),
|
|
1590
|
+
onClick: (e) => removeItem(item, e),
|
|
1591
|
+
children: /* @__PURE__ */ jsx9(Icon3, { name: "x-outline", size: "mini" })
|
|
1592
|
+
}
|
|
1593
|
+
)
|
|
1594
|
+
] }, item.id)),
|
|
1595
|
+
overflow > 0 && /* @__PURE__ */ jsxs7(Token, { children: [
|
|
1596
|
+
"+",
|
|
1597
|
+
overflow
|
|
1598
|
+
] }, "__overflow")
|
|
1599
|
+
] });
|
|
1600
|
+
})(),
|
|
1601
|
+
/* @__PURE__ */ jsx9(
|
|
1602
|
+
ComboboxTokenInput,
|
|
1603
|
+
{
|
|
1604
|
+
id,
|
|
1605
|
+
placeholder: value.length > 0 ? "" : placeholder
|
|
1606
|
+
}
|
|
1607
|
+
),
|
|
1608
|
+
/* @__PURE__ */ jsxs7(ComboboxActionButtons, { children: [
|
|
1609
|
+
/* @__PURE__ */ jsx9(ComboboxClear, { "aria-label": "Clear all", children: /* @__PURE__ */ jsx9(ClearIcon, {}) }),
|
|
1610
|
+
/* @__PURE__ */ jsx9(ComboboxTrigger, { "aria-label": "Open popup", children: /* @__PURE__ */ jsx9(StyledChevron, { $isOpen: open, children: /* @__PURE__ */ jsx9(ChevronIcon, {}) }) })
|
|
1611
|
+
] })
|
|
1612
|
+
] }) }),
|
|
1613
|
+
/* @__PURE__ */ jsx9(Combobox4.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx9(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ jsxs7(
|
|
1614
|
+
ComboboxPopup,
|
|
1615
|
+
{
|
|
1616
|
+
"aria-busy": isLoading || void 0,
|
|
1617
|
+
style: isVirtualized ? { display: "flex", flexDirection: "column" } : void 0,
|
|
1618
|
+
children: [
|
|
1619
|
+
/* @__PURE__ */ jsx9(ComboboxStatus, { children: isLoading ? loadingText : null }),
|
|
1620
|
+
/* @__PURE__ */ jsx9(ComboboxEmpty, { children: isLoading ? null : emptyText }),
|
|
1621
|
+
/* @__PURE__ */ jsx9(
|
|
1622
|
+
ComboboxList,
|
|
1623
|
+
{
|
|
1624
|
+
style: isVirtualized ? {
|
|
1625
|
+
padding: 0,
|
|
1626
|
+
flex: 1,
|
|
1627
|
+
minHeight: 0,
|
|
1628
|
+
display: "flex",
|
|
1629
|
+
flexDirection: "column"
|
|
1630
|
+
} : void 0,
|
|
1631
|
+
children: isVirtualized ? /* @__PURE__ */ jsx9(
|
|
1632
|
+
VirtualizedComboboxList,
|
|
1633
|
+
{
|
|
1634
|
+
open,
|
|
1635
|
+
virtualizerRef,
|
|
1636
|
+
value,
|
|
1637
|
+
groups,
|
|
1638
|
+
data: visibleData,
|
|
1639
|
+
itemToString,
|
|
1640
|
+
inputType,
|
|
1641
|
+
renderItem,
|
|
1642
|
+
renderGroupHeading
|
|
1643
|
+
}
|
|
1644
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => /* @__PURE__ */ jsxs7(React9.Fragment, { children: [
|
|
1645
|
+
/* @__PURE__ */ jsxs7(ComboboxGroup, { items: group.items, children: [
|
|
1646
|
+
/* @__PURE__ */ jsx9(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
1647
|
+
/* @__PURE__ */ jsx9(Combobox4.Collection, { children: (item) => /* @__PURE__ */ jsx9(
|
|
1648
|
+
ComboboxItem_default,
|
|
1649
|
+
{
|
|
1650
|
+
value: item,
|
|
1651
|
+
itemId: String(item.id),
|
|
1652
|
+
label: itemToString(item),
|
|
1653
|
+
inputType,
|
|
1654
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1655
|
+
},
|
|
1656
|
+
item.id
|
|
1657
|
+
) })
|
|
1658
|
+
] }),
|
|
1659
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx9(ComboboxSeparator, {})
|
|
1660
|
+
] }, group.value) : (item) => /* @__PURE__ */ jsx9(
|
|
1661
|
+
ComboboxItem_default,
|
|
1662
|
+
{
|
|
1663
|
+
value: item,
|
|
1664
|
+
itemId: String(item.id),
|
|
1665
|
+
label: itemToString(item),
|
|
1666
|
+
inputType,
|
|
1667
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1668
|
+
},
|
|
1669
|
+
item.id
|
|
1670
|
+
)
|
|
1671
|
+
}
|
|
1672
|
+
)
|
|
1673
|
+
]
|
|
1674
|
+
}
|
|
1675
|
+
) }) })
|
|
1676
|
+
]
|
|
1677
|
+
}
|
|
1678
|
+
)
|
|
1679
|
+
] });
|
|
1680
|
+
}
|
|
747
1681
|
|
|
748
1682
|
// src/v3/SingleSearchableSelect.tsx
|
|
749
1683
|
import * as React10 from "react";
|
|
750
1684
|
import { Combobox as Combobox5 } from "@base-ui/react/combobox";
|
|
751
1685
|
import "@tanstack/react-virtual";
|
|
752
1686
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1687
|
+
function SingleSearchableSelect(props) {
|
|
1688
|
+
const {
|
|
1689
|
+
id,
|
|
1690
|
+
placeholder = "Select...",
|
|
1691
|
+
searchPlaceholder = "Search...",
|
|
1692
|
+
emptyText = "No results found.",
|
|
1693
|
+
isLoading = false,
|
|
1694
|
+
loadingText = "Loading..."
|
|
1695
|
+
} = props;
|
|
1696
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
1697
|
+
const data = isChildrenMode ? [] : props.data;
|
|
1698
|
+
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
1699
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
1700
|
+
const renderSelection = isChildrenMode ? void 0 : props.renderSelection;
|
|
1701
|
+
const inputType = isChildrenMode ? "icon" : props.inputType ?? "icon";
|
|
1702
|
+
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1703
|
+
const groupBy = isChildrenMode ? void 0 : isVirtualized ? void 0 : props.groupBy;
|
|
1704
|
+
const renderGroupHeading = isChildrenMode ? void 0 : isVirtualized ? void 0 : props.renderGroupHeading;
|
|
1705
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
1706
|
+
const selectedItemId = props.selectedItemId;
|
|
1707
|
+
const defaultSelectedItemId = props.defaultSelectedItemId;
|
|
1708
|
+
const onSelectedItemIdChange = props.onSelectedItemIdChange;
|
|
1709
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1710
|
+
const [open, setOpen] = React10.useState(false);
|
|
1711
|
+
const virtualizerRef = React10.useRef(null);
|
|
1712
|
+
const isControlled = selectedItemId !== void 0;
|
|
1713
|
+
const idToItem = React10.useCallback(
|
|
1714
|
+
(id2) => {
|
|
1715
|
+
if (id2 == null) return null;
|
|
1716
|
+
return data.find((d) => String(d.id) === String(id2)) ?? null;
|
|
1717
|
+
},
|
|
1718
|
+
[data]
|
|
1719
|
+
);
|
|
1720
|
+
const [internalValue, setInternalValue] = React10.useState(
|
|
1721
|
+
() => idToItem(defaultSelectedItemId)
|
|
1722
|
+
);
|
|
1723
|
+
const value = isControlled ? idToItem(selectedItemId) : internalValue;
|
|
1724
|
+
function handleValueChange(newItem) {
|
|
1725
|
+
if (!isControlled) setInternalValue(newItem);
|
|
1726
|
+
if (onSelectedItemIdChange) {
|
|
1727
|
+
onSelectedItemIdChange(newItem ? newItem.id : null);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
const groups = React10.useMemo(
|
|
1731
|
+
() => groupBy ? groupItems(data, groupBy) : null,
|
|
1732
|
+
[data, groupBy]
|
|
1733
|
+
);
|
|
1734
|
+
return /* @__PURE__ */ jsxs8(Field, { children: [
|
|
1735
|
+
/* @__PURE__ */ jsx10("div", { ref: containerRef, style: { position: "relative" } }),
|
|
1736
|
+
/* @__PURE__ */ jsxs8(
|
|
1737
|
+
Combobox5.Root,
|
|
1738
|
+
{
|
|
1739
|
+
id,
|
|
1740
|
+
value,
|
|
1741
|
+
disabled: props.disabled,
|
|
1742
|
+
onValueChange: handleValueChange,
|
|
1743
|
+
itemToStringLabel: itemToString,
|
|
1744
|
+
isItemEqualToValue: (a, b) => a != null && b != null && a.id === b.id,
|
|
1745
|
+
items: isVirtualized ? data : groups ?? (isChildrenMode ? void 0 : data),
|
|
1746
|
+
virtualized: isVirtualized,
|
|
1747
|
+
open,
|
|
1748
|
+
onOpenChange: setOpen,
|
|
1749
|
+
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
1750
|
+
const virt = virtualizerRef.current;
|
|
1751
|
+
if (!item || !virt) return;
|
|
1752
|
+
const isStart = index === 0;
|
|
1753
|
+
const isEnd = index === virt.options.count - 1;
|
|
1754
|
+
if (reason === "none" || reason === "keyboard" && (isStart || isEnd)) {
|
|
1755
|
+
queueMicrotask(() => {
|
|
1756
|
+
virt.scrollToIndex(index, {
|
|
1757
|
+
align: isEnd ? "start" : "end"
|
|
1758
|
+
});
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1761
|
+
} : void 0,
|
|
1762
|
+
children: [
|
|
1763
|
+
/* @__PURE__ */ jsxs8(SearchableSelectTrigger, { id, children: [
|
|
1764
|
+
/* @__PURE__ */ jsx10(SearchableSelectPlaceholder, { children: /* @__PURE__ */ jsx10(Combobox5.Value, { placeholder, children: value ? renderSelection ? renderSelection(value) : itemToString(value) : null }) }),
|
|
1765
|
+
/* @__PURE__ */ jsx10(SearchableSelectTriggerIcon, { children: /* @__PURE__ */ jsx10(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx10(ChevronIcon, {}) }) })
|
|
1766
|
+
] }),
|
|
1767
|
+
/* @__PURE__ */ jsx10(Combobox5.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx10(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ jsxs8(
|
|
1768
|
+
SearchableSelectPopup,
|
|
1769
|
+
{
|
|
1770
|
+
"aria-busy": isLoading || void 0,
|
|
1771
|
+
style: { display: "flex", flexDirection: "column" },
|
|
1772
|
+
children: [
|
|
1773
|
+
/* @__PURE__ */ jsx10(SearchableSelectSearchContainer, { children: /* @__PURE__ */ jsx10(
|
|
1774
|
+
SearchableSelectInput,
|
|
1775
|
+
{
|
|
1776
|
+
placeholder: searchPlaceholder,
|
|
1777
|
+
autoFocus: true
|
|
1778
|
+
}
|
|
1779
|
+
) }),
|
|
1780
|
+
/* @__PURE__ */ jsx10(ComboboxStatus, { children: isLoading ? loadingText : null }),
|
|
1781
|
+
/* @__PURE__ */ jsx10(ComboboxEmpty, { children: isLoading ? null : emptyText }),
|
|
1782
|
+
/* @__PURE__ */ jsx10(
|
|
1783
|
+
SearchableSelectList,
|
|
1784
|
+
{
|
|
1785
|
+
style: isVirtualized ? {
|
|
1786
|
+
padding: 0,
|
|
1787
|
+
flex: 1,
|
|
1788
|
+
minHeight: 0,
|
|
1789
|
+
display: "flex",
|
|
1790
|
+
flexDirection: "column"
|
|
1791
|
+
} : void 0,
|
|
1792
|
+
children: isVirtualized ? /* @__PURE__ */ jsx10(
|
|
1793
|
+
VirtualizedComboboxList,
|
|
1794
|
+
{
|
|
1795
|
+
open,
|
|
1796
|
+
virtualizerRef,
|
|
1797
|
+
value,
|
|
1798
|
+
groups: null,
|
|
1799
|
+
data,
|
|
1800
|
+
itemToString,
|
|
1801
|
+
inputType,
|
|
1802
|
+
renderItem
|
|
1803
|
+
}
|
|
1804
|
+
) : children ? children : groups ? (group, index) => /* @__PURE__ */ jsxs8(React10.Fragment, { children: [
|
|
1805
|
+
/* @__PURE__ */ jsxs8(ComboboxGroup, { items: group.items, children: [
|
|
1806
|
+
/* @__PURE__ */ jsx10(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
1807
|
+
/* @__PURE__ */ jsx10(Combobox5.Collection, { children: (item) => /* @__PURE__ */ jsx10(
|
|
1808
|
+
ComboboxItem_default,
|
|
1809
|
+
{
|
|
1810
|
+
value: item,
|
|
1811
|
+
itemId: String(item.id),
|
|
1812
|
+
label: itemToString(item),
|
|
1813
|
+
inputType,
|
|
1814
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1815
|
+
},
|
|
1816
|
+
item.id
|
|
1817
|
+
) })
|
|
1818
|
+
] }),
|
|
1819
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx10(ComboboxSeparator, {})
|
|
1820
|
+
] }, group.value) : (item) => /* @__PURE__ */ jsx10(
|
|
1821
|
+
ComboboxItem_default,
|
|
1822
|
+
{
|
|
1823
|
+
value: item,
|
|
1824
|
+
itemId: String(item.id),
|
|
1825
|
+
label: itemToString(item),
|
|
1826
|
+
inputType,
|
|
1827
|
+
renderItem: renderItem ? () => renderItem(item) : void 0
|
|
1828
|
+
},
|
|
1829
|
+
item.id
|
|
1830
|
+
)
|
|
1831
|
+
}
|
|
1832
|
+
)
|
|
1833
|
+
]
|
|
1834
|
+
}
|
|
1835
|
+
) }) })
|
|
1836
|
+
]
|
|
1837
|
+
}
|
|
1838
|
+
)
|
|
1839
|
+
] });
|
|
1840
|
+
}
|
|
753
1841
|
|
|
754
1842
|
// src/v3/MultiSearchableSelect.tsx
|
|
755
1843
|
import * as React11 from "react";
|
|
@@ -763,6 +1851,295 @@ var SelectionText2 = styled7.span`
|
|
|
763
1851
|
text-overflow: ellipsis;
|
|
764
1852
|
white-space: nowrap;
|
|
765
1853
|
`;
|
|
1854
|
+
function makeGroupHeaderSentinel2(groupName) {
|
|
1855
|
+
return { __groupHeader: true, groupName };
|
|
1856
|
+
}
|
|
1857
|
+
function isGroupHeaderSentinel3(v) {
|
|
1858
|
+
return typeof v === "object" && v !== null && "__groupHeader" in v;
|
|
1859
|
+
}
|
|
1860
|
+
var SELECT_ALL_SENTINEL2 = { __selectAll: true };
|
|
1861
|
+
function isSelectAllSentinel3(v) {
|
|
1862
|
+
return typeof v === "object" && v !== null && "__selectAll" in v;
|
|
1863
|
+
}
|
|
1864
|
+
function MultiSearchableSelect(props) {
|
|
1865
|
+
const {
|
|
1866
|
+
id,
|
|
1867
|
+
placeholder = "Select...",
|
|
1868
|
+
searchPlaceholder = "Search...",
|
|
1869
|
+
emptyText = "No results found.",
|
|
1870
|
+
isLoading = false,
|
|
1871
|
+
loadingText = "Loading..."
|
|
1872
|
+
} = props;
|
|
1873
|
+
const isChildrenMode = "children" in props && props.children != null;
|
|
1874
|
+
const data = isChildrenMode ? [] : props.data;
|
|
1875
|
+
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
1876
|
+
const renderItem = isChildrenMode ? void 0 : props.renderItem;
|
|
1877
|
+
const customRenderSelections = isChildrenMode ? void 0 : props.customRenderSelections;
|
|
1878
|
+
const maxSelections = isChildrenMode ? void 0 : props.maxSelections;
|
|
1879
|
+
const renderSelection = isChildrenMode ? void 0 : props.renderSelection;
|
|
1880
|
+
const removeSelectedItems = isChildrenMode ? false : props.removeSelectedItems ?? false;
|
|
1881
|
+
const inputType = isChildrenMode ? "checkbox" : removeSelectedItems ? "none" : props.inputType ?? "checkbox";
|
|
1882
|
+
const groupBy = isChildrenMode ? void 0 : props.groupBy;
|
|
1883
|
+
const renderGroupHeading = isChildrenMode ? void 0 : props.renderGroupHeading;
|
|
1884
|
+
const selectHeadings = isChildrenMode ? false : props.selectHeadings ?? false;
|
|
1885
|
+
const selectAll = isChildrenMode ? false : props.selectAll ?? false;
|
|
1886
|
+
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1887
|
+
const children = isChildrenMode ? props.children : void 0;
|
|
1888
|
+
const selectedItemIds = props.selectedItemIds;
|
|
1889
|
+
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
1890
|
+
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
1891
|
+
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1892
|
+
const [open, setOpen] = React11.useState(false);
|
|
1893
|
+
const virtualizerRef = React11.useRef(null);
|
|
1894
|
+
const isControlled = selectedItemIds !== void 0;
|
|
1895
|
+
const idsToItems = React11.useCallback(
|
|
1896
|
+
(ids) => ids.map((id2) => data.find((d) => d.id === id2)).filter((d) => d != null),
|
|
1897
|
+
[data]
|
|
1898
|
+
);
|
|
1899
|
+
const [internalValue, setInternalValue] = React11.useState(
|
|
1900
|
+
() => defaultSelectedItemIds ? idsToItems(defaultSelectedItemIds) : []
|
|
1901
|
+
);
|
|
1902
|
+
const value = isControlled ? idsToItems(selectedItemIds) : internalValue;
|
|
1903
|
+
const visibleData = React11.useMemo(
|
|
1904
|
+
() => removeSelectedItems ? data.filter((item) => !value.some((v) => v.id === item.id)) : data,
|
|
1905
|
+
[data, value, removeSelectedItems]
|
|
1906
|
+
);
|
|
1907
|
+
const groups = React11.useMemo(
|
|
1908
|
+
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
1909
|
+
[visibleData, groupBy]
|
|
1910
|
+
);
|
|
1911
|
+
const flatItems = React11.useMemo(() => {
|
|
1912
|
+
if (!groups || !selectHeadings && !selectAll) return null;
|
|
1913
|
+
const rows = [];
|
|
1914
|
+
if (selectAll) rows.push(SELECT_ALL_SENTINEL2);
|
|
1915
|
+
for (const group of groups) {
|
|
1916
|
+
rows.push(makeGroupHeaderSentinel2(group.value));
|
|
1917
|
+
rows.push(...group.items);
|
|
1918
|
+
}
|
|
1919
|
+
return rows;
|
|
1920
|
+
}, [groups, selectHeadings, selectAll]);
|
|
1921
|
+
function setSelectedItems(items) {
|
|
1922
|
+
if (!isControlled) setInternalValue(items);
|
|
1923
|
+
if (onSelectedItemIdsChange)
|
|
1924
|
+
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
1925
|
+
}
|
|
1926
|
+
function handleValueChange(next) {
|
|
1927
|
+
const selectAllClicked = next.filter(isSelectAllSentinel3);
|
|
1928
|
+
const groupSentinels = next.filter(isGroupHeaderSentinel3);
|
|
1929
|
+
const realItems = next.filter(
|
|
1930
|
+
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v)
|
|
1931
|
+
);
|
|
1932
|
+
if (selectAllClicked.length > 0) {
|
|
1933
|
+
const allSelected = data.every(
|
|
1934
|
+
(d) => realItems.some((s) => s.id === d.id)
|
|
1935
|
+
);
|
|
1936
|
+
setSelectedItems(allSelected ? [] : [...data]);
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
if (groupSentinels.length === 0) {
|
|
1940
|
+
setSelectedItems(realItems);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
let updated = [...realItems];
|
|
1944
|
+
for (const sentinel of groupSentinels) {
|
|
1945
|
+
const group = groups?.find((g) => g.value === sentinel.groupName);
|
|
1946
|
+
if (!group) continue;
|
|
1947
|
+
const allSelected = group.items.every(
|
|
1948
|
+
(item) => updated.some((s) => s.id === item.id)
|
|
1949
|
+
);
|
|
1950
|
+
if (allSelected) {
|
|
1951
|
+
updated = updated.filter(
|
|
1952
|
+
(s) => !group.items.some((item) => item.id === s.id)
|
|
1953
|
+
);
|
|
1954
|
+
} else {
|
|
1955
|
+
const missing = group.items.filter(
|
|
1956
|
+
(item) => !updated.some((s) => s.id === item.id)
|
|
1957
|
+
);
|
|
1958
|
+
updated = [...updated, ...missing];
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
setSelectedItems(updated);
|
|
1962
|
+
}
|
|
1963
|
+
function handleSimpleValueChange(newItems) {
|
|
1964
|
+
setSelectedItems(newItems);
|
|
1965
|
+
}
|
|
1966
|
+
function renderSentinelRow(row) {
|
|
1967
|
+
if (isSelectAllSentinel3(row)) {
|
|
1968
|
+
const totalCount = data.length;
|
|
1969
|
+
const selectedCount = value.length;
|
|
1970
|
+
const state = selectedCount === 0 ? "none" : selectedCount === totalCount ? "all" : "partial";
|
|
1971
|
+
return /* @__PURE__ */ jsxs9(ComboboxSelectAllItem, { value: row, children: [
|
|
1972
|
+
/* @__PURE__ */ jsx11(ComboboxGroupHeaderCheckbox, { $state: state, id: "__selectAll" }),
|
|
1973
|
+
"Select all"
|
|
1974
|
+
] }, "__selectAll");
|
|
1975
|
+
}
|
|
1976
|
+
if (isGroupHeaderSentinel3(row)) {
|
|
1977
|
+
const group = groups?.find((g) => g.value === row.groupName);
|
|
1978
|
+
const groupItemsList = group?.items ?? [];
|
|
1979
|
+
const selectedCount = groupItemsList.filter(
|
|
1980
|
+
(item2) => value.some((s) => s.id === item2.id)
|
|
1981
|
+
).length;
|
|
1982
|
+
const state = selectedCount === 0 ? "none" : selectedCount === groupItemsList.length ? "all" : "partial";
|
|
1983
|
+
return /* @__PURE__ */ jsxs9(ComboboxGroupHeaderItem, { value: row, children: [
|
|
1984
|
+
/* @__PURE__ */ jsx11(
|
|
1985
|
+
ComboboxGroupHeaderCheckbox,
|
|
1986
|
+
{
|
|
1987
|
+
$state: state,
|
|
1988
|
+
id: `__header-${row.groupName}`
|
|
1989
|
+
}
|
|
1990
|
+
),
|
|
1991
|
+
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
1992
|
+
] }, `__header-${row.groupName}`);
|
|
1993
|
+
}
|
|
1994
|
+
const item = row;
|
|
1995
|
+
return /* @__PURE__ */ jsx11(
|
|
1996
|
+
ComboboxItem_default,
|
|
1997
|
+
{
|
|
1998
|
+
value: item,
|
|
1999
|
+
itemId: String(item.id),
|
|
2000
|
+
label: itemToString(item),
|
|
2001
|
+
inputType,
|
|
2002
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
2003
|
+
},
|
|
2004
|
+
item.id
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
const rootItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
2008
|
+
const hasSentinels = selectAll || selectHeadings;
|
|
2009
|
+
const rootValue = hasSentinels ? [...value] : value;
|
|
2010
|
+
return /* @__PURE__ */ jsxs9(Field, { children: [
|
|
2011
|
+
/* @__PURE__ */ jsx11("div", { ref: containerRef, style: { position: "relative" } }),
|
|
2012
|
+
/* @__PURE__ */ jsxs9(
|
|
2013
|
+
Combobox6.Root,
|
|
2014
|
+
{
|
|
2015
|
+
multiple: true,
|
|
2016
|
+
id,
|
|
2017
|
+
virtualized: isVirtualized,
|
|
2018
|
+
open,
|
|
2019
|
+
disabled: props.disabled,
|
|
2020
|
+
onOpenChange: setOpen,
|
|
2021
|
+
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
2022
|
+
const virt = virtualizerRef.current;
|
|
2023
|
+
if (!item || !virt) return;
|
|
2024
|
+
const isStart = index === 0;
|
|
2025
|
+
const isEnd = index === virt.options.count - 1;
|
|
2026
|
+
if (reason === "none" || reason === "keyboard" && (isStart || isEnd)) {
|
|
2027
|
+
queueMicrotask(() => {
|
|
2028
|
+
virt.scrollToIndex(index, {
|
|
2029
|
+
align: isEnd ? "start" : "end"
|
|
2030
|
+
});
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
} : void 0,
|
|
2034
|
+
value: rootValue,
|
|
2035
|
+
onValueChange: (
|
|
2036
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2037
|
+
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
2038
|
+
),
|
|
2039
|
+
itemToStringLabel: (item) => {
|
|
2040
|
+
if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
|
|
2041
|
+
return "";
|
|
2042
|
+
return itemToString(item);
|
|
2043
|
+
},
|
|
2044
|
+
isItemEqualToValue: (a, b) => {
|
|
2045
|
+
if (isGroupHeaderSentinel3(a) && isGroupHeaderSentinel3(b))
|
|
2046
|
+
return a.groupName === b.groupName;
|
|
2047
|
+
if (isSelectAllSentinel3(a) && isSelectAllSentinel3(b)) return true;
|
|
2048
|
+
if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b))
|
|
2049
|
+
return a.id === b.id;
|
|
2050
|
+
return false;
|
|
2051
|
+
},
|
|
2052
|
+
items: rootItems,
|
|
2053
|
+
children: [
|
|
2054
|
+
/* @__PURE__ */ jsxs9(SearchableSelectTokenTrigger, { id, children: [
|
|
2055
|
+
renderSelection ? renderSelection(value) : value.length > 0 ? (() => {
|
|
2056
|
+
const visibleItems = maxSelections != null ? value.slice(0, maxSelections) : value;
|
|
2057
|
+
const overflowCount = maxSelections != null && value.length > maxSelections ? value.length - maxSelections : 0;
|
|
2058
|
+
const text = visibleItems.map(
|
|
2059
|
+
(item) => customRenderSelections ? customRenderSelections(item) : itemToString(item)
|
|
2060
|
+
).join(", ");
|
|
2061
|
+
return /* @__PURE__ */ jsxs9(SelectionText2, { children: [
|
|
2062
|
+
text,
|
|
2063
|
+
overflowCount > 0 ? `, +${overflowCount}` : ""
|
|
2064
|
+
] });
|
|
2065
|
+
})() : /* @__PURE__ */ jsx11(SearchableSelectPlaceholder, { children: placeholder }),
|
|
2066
|
+
/* @__PURE__ */ jsx11(SearchableSelectTriggerIcon, { style: { marginLeft: "auto" }, children: /* @__PURE__ */ jsx11(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx11(ChevronIcon, {}) }) })
|
|
2067
|
+
] }),
|
|
2068
|
+
/* @__PURE__ */ jsx11(Combobox6.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx11(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ jsxs9(
|
|
2069
|
+
SearchableSelectPopup,
|
|
2070
|
+
{
|
|
2071
|
+
"aria-busy": isLoading || void 0,
|
|
2072
|
+
style: { display: "flex", flexDirection: "column" },
|
|
2073
|
+
children: [
|
|
2074
|
+
/* @__PURE__ */ jsx11(SearchableSelectSearchContainer, { children: /* @__PURE__ */ jsx11(
|
|
2075
|
+
SearchableSelectInput,
|
|
2076
|
+
{
|
|
2077
|
+
placeholder: searchPlaceholder,
|
|
2078
|
+
autoFocus: true
|
|
2079
|
+
}
|
|
2080
|
+
) }),
|
|
2081
|
+
/* @__PURE__ */ jsx11(ComboboxStatus, { children: isLoading ? loadingText : null }),
|
|
2082
|
+
/* @__PURE__ */ jsx11(ComboboxEmpty, { children: isLoading ? null : emptyText }),
|
|
2083
|
+
/* @__PURE__ */ jsx11(
|
|
2084
|
+
SearchableSelectList,
|
|
2085
|
+
{
|
|
2086
|
+
style: isVirtualized ? {
|
|
2087
|
+
padding: 0,
|
|
2088
|
+
flex: 1,
|
|
2089
|
+
minHeight: 0,
|
|
2090
|
+
display: "flex",
|
|
2091
|
+
flexDirection: "column"
|
|
2092
|
+
} : void 0,
|
|
2093
|
+
children: isVirtualized ? /* @__PURE__ */ jsx11(
|
|
2094
|
+
VirtualizedComboboxList,
|
|
2095
|
+
{
|
|
2096
|
+
open,
|
|
2097
|
+
virtualizerRef,
|
|
2098
|
+
value,
|
|
2099
|
+
groups,
|
|
2100
|
+
data: visibleData,
|
|
2101
|
+
itemToString,
|
|
2102
|
+
inputType,
|
|
2103
|
+
renderItem,
|
|
2104
|
+
renderGroupHeading
|
|
2105
|
+
}
|
|
2106
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => /* @__PURE__ */ jsxs9(React11.Fragment, { children: [
|
|
2107
|
+
/* @__PURE__ */ jsxs9(ComboboxGroup, { items: group.items, children: [
|
|
2108
|
+
/* @__PURE__ */ jsx11(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
|
|
2109
|
+
/* @__PURE__ */ jsx11(Combobox6.Collection, { children: (item) => /* @__PURE__ */ jsx11(
|
|
2110
|
+
ComboboxItem_default,
|
|
2111
|
+
{
|
|
2112
|
+
value: item,
|
|
2113
|
+
itemId: String(item.id),
|
|
2114
|
+
label: itemToString(item),
|
|
2115
|
+
inputType,
|
|
2116
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
2117
|
+
},
|
|
2118
|
+
item.id
|
|
2119
|
+
) })
|
|
2120
|
+
] }),
|
|
2121
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx11(ComboboxSeparator, {})
|
|
2122
|
+
] }, group.value) : (item) => /* @__PURE__ */ jsx11(
|
|
2123
|
+
ComboboxItem_default,
|
|
2124
|
+
{
|
|
2125
|
+
value: item,
|
|
2126
|
+
itemId: String(item.id),
|
|
2127
|
+
label: itemToString(item),
|
|
2128
|
+
inputType,
|
|
2129
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
2130
|
+
},
|
|
2131
|
+
item.id
|
|
2132
|
+
)
|
|
2133
|
+
}
|
|
2134
|
+
)
|
|
2135
|
+
]
|
|
2136
|
+
}
|
|
2137
|
+
) }) })
|
|
2138
|
+
]
|
|
2139
|
+
}
|
|
2140
|
+
)
|
|
2141
|
+
] });
|
|
2142
|
+
}
|
|
766
2143
|
export {
|
|
767
2144
|
ComboboxActionButtons,
|
|
768
2145
|
ComboboxClear,
|
|
@@ -786,6 +2163,9 @@ export {
|
|
|
786
2163
|
ComboboxTokenInput,
|
|
787
2164
|
ComboboxTokenInputGroup,
|
|
788
2165
|
ComboboxTrigger,
|
|
2166
|
+
MultiCombobox,
|
|
2167
|
+
MultiSearchableSelect,
|
|
2168
|
+
MultiSelect,
|
|
789
2169
|
SearchableSelectInput,
|
|
790
2170
|
SearchableSelectList,
|
|
791
2171
|
SearchableSelectPlaceholder,
|
|
@@ -794,6 +2174,9 @@ export {
|
|
|
794
2174
|
SearchableSelectTokenTrigger,
|
|
795
2175
|
SearchableSelectTrigger,
|
|
796
2176
|
SearchableSelectTriggerIcon,
|
|
2177
|
+
SingleCombobox,
|
|
2178
|
+
SingleSearchableSelect,
|
|
2179
|
+
SingleSelect,
|
|
797
2180
|
Token,
|
|
798
2181
|
TokenRemove
|
|
799
2182
|
};
|