@sproutsocial/seeds-react-menu 1.12.1 → 1.12.3
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 +10 -10
- package/CHANGELOG.md +19 -0
- package/dist/esm/v3/index.js +332 -92
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/v3/index.d.mts +14 -0
- package/dist/v3/index.d.ts +14 -0
- package/dist/v3/index.js +331 -91
- package/dist/v3/index.js.map +1 -1
- package/package.json +9 -9
- package/src/v3/Common/VirtualizedComboboxList.tsx +29 -2
- package/src/v3/Common/sentinels.ts +13 -0
- package/src/v3/MultiCombobox.stories.tsx +34 -0
- package/src/v3/MultiCombobox.tsx +223 -55
- package/src/v3/MultiSearchableSelect.tsx +217 -55
- package/src/v3/MultiSelect.tsx +3 -1
- package/src/v3/SingleCombobox.tsx +7 -1
- package/src/v3/SingleSearchableSelect.tsx +3 -1
- package/src/v3/SingleSelect.stories.tsx +19 -0
- package/src/v3/SingleSelect.tsx +3 -1
package/dist/esm/v3/index.js
CHANGED
|
@@ -291,6 +291,7 @@ var StyledValue = styled4(Select3.Value)`
|
|
|
291
291
|
`;
|
|
292
292
|
function SingleSelect(props) {
|
|
293
293
|
const { id, placeholder, includePlaceholderItem } = props;
|
|
294
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
294
295
|
const isChildrenMode = "children" in props && props.children != null;
|
|
295
296
|
const selectedItemId = props.selectedItemId;
|
|
296
297
|
const defaultSelectedItemId = props.defaultSelectedItemId;
|
|
@@ -348,7 +349,7 @@ function SingleSelect(props) {
|
|
|
348
349
|
onValueChange: handleValueChange,
|
|
349
350
|
id,
|
|
350
351
|
children: [
|
|
351
|
-
/* @__PURE__ */ jsxs2(SelectTrigger, { children: [
|
|
352
|
+
/* @__PURE__ */ jsxs2(SelectTrigger, { "aria-describedby": ariaDescribedBy, children: [
|
|
352
353
|
/* @__PURE__ */ jsx3(StyledValue, { placeholder, children: renderSelection && selectedItem ? renderSelection(selectedItem) : void 0 }),
|
|
353
354
|
/* @__PURE__ */ jsx3(SelectIcon, { children: /* @__PURE__ */ jsx3(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx3(ChevronIcon, {}) }) })
|
|
354
355
|
] }),
|
|
@@ -420,6 +421,7 @@ var Placeholder = styled5.div`
|
|
|
420
421
|
`;
|
|
421
422
|
function MultiSelect(props) {
|
|
422
423
|
const { id, placeholder = "Select..." } = props;
|
|
424
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
423
425
|
const isChildrenMode = "children" in props && props.children != null;
|
|
424
426
|
const data = isChildrenMode ? [] : props.data;
|
|
425
427
|
const itemToString = isChildrenMode ? () => "" : props.itemToString;
|
|
@@ -476,7 +478,7 @@ function MultiSelect(props) {
|
|
|
476
478
|
disabled: props.disabled,
|
|
477
479
|
id,
|
|
478
480
|
children: [
|
|
479
|
-
/* @__PURE__ */ jsxs3(SelectTrigger, { children: [
|
|
481
|
+
/* @__PURE__ */ jsxs3(SelectTrigger, { "aria-describedby": ariaDescribedBy, children: [
|
|
480
482
|
/* @__PURE__ */ jsx4(SelectValue, { children: () => {
|
|
481
483
|
if (renderSelection) return renderSelection(selectedItems);
|
|
482
484
|
if (selectedItems.length === 0)
|
|
@@ -1087,6 +1089,12 @@ function makePlaceholderSentinel(label) {
|
|
|
1087
1089
|
function isPlaceholderSentinel(v) {
|
|
1088
1090
|
return typeof v === "object" && v !== null && "__placeholder" in v;
|
|
1089
1091
|
}
|
|
1092
|
+
function makeCreatableSentinel(label) {
|
|
1093
|
+
return { __creatable: true, label };
|
|
1094
|
+
}
|
|
1095
|
+
function isCreatableSentinel(v) {
|
|
1096
|
+
return typeof v === "object" && v !== null && "__creatable" in v;
|
|
1097
|
+
}
|
|
1090
1098
|
|
|
1091
1099
|
// src/v3/Common/VirtualizedComboboxList.tsx
|
|
1092
1100
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
@@ -1106,7 +1114,8 @@ function VirtualizedComboboxList({
|
|
|
1106
1114
|
itemToString,
|
|
1107
1115
|
inputType,
|
|
1108
1116
|
renderItem,
|
|
1109
|
-
renderGroupHeading
|
|
1117
|
+
renderGroupHeading,
|
|
1118
|
+
renderCreatableItem
|
|
1110
1119
|
}) {
|
|
1111
1120
|
const filteredItems = Combobox2.useFilteredItems();
|
|
1112
1121
|
const scrollElementRef = React7.useRef(null);
|
|
@@ -1224,6 +1233,17 @@ function VirtualizedComboboxList({
|
|
|
1224
1233
|
virtualItem.key
|
|
1225
1234
|
);
|
|
1226
1235
|
}
|
|
1236
|
+
if (isCreatableSentinel(row)) {
|
|
1237
|
+
if (renderCreatableItem) {
|
|
1238
|
+
return renderCreatableItem(
|
|
1239
|
+
row,
|
|
1240
|
+
baseStyle,
|
|
1241
|
+
virtualItem.index,
|
|
1242
|
+
virtualizer.measureElement
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
return null;
|
|
1246
|
+
}
|
|
1227
1247
|
const item = row;
|
|
1228
1248
|
return /* @__PURE__ */ jsx7(
|
|
1229
1249
|
ComboboxItem_default,
|
|
@@ -1257,6 +1277,7 @@ function SingleCombobox(props) {
|
|
|
1257
1277
|
emptyText = "No results found.",
|
|
1258
1278
|
filter
|
|
1259
1279
|
} = props;
|
|
1280
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1260
1281
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1261
1282
|
const data = isChildrenMode ? [] : props.data;
|
|
1262
1283
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1327,7 +1348,14 @@ function SingleCombobox(props) {
|
|
|
1327
1348
|
} : void 0,
|
|
1328
1349
|
children: [
|
|
1329
1350
|
/* @__PURE__ */ jsxs6(ComboboxInputGroup, { children: [
|
|
1330
|
-
/* @__PURE__ */ jsx8(
|
|
1351
|
+
/* @__PURE__ */ jsx8(
|
|
1352
|
+
ComboboxInput,
|
|
1353
|
+
{
|
|
1354
|
+
placeholder,
|
|
1355
|
+
id,
|
|
1356
|
+
"aria-describedby": ariaDescribedBy
|
|
1357
|
+
}
|
|
1358
|
+
),
|
|
1331
1359
|
/* @__PURE__ */ jsxs6(ComboboxActionButtons, { children: [
|
|
1332
1360
|
/* @__PURE__ */ jsx8(ComboboxClear, { "aria-label": "Clear selection", children: /* @__PURE__ */ jsx8(ClearIcon, {}) }),
|
|
1333
1361
|
/* @__PURE__ */ jsx8(ComboboxTrigger, { "aria-label": "Open popup", children: /* @__PURE__ */ jsx8(StyledChevron, { $isOpen: open, children: /* @__PURE__ */ jsx8(ChevronIcon, {}) }) })
|
|
@@ -1429,6 +1457,7 @@ function MultiCombobox(props) {
|
|
|
1429
1457
|
inputValue,
|
|
1430
1458
|
onInputValueChange
|
|
1431
1459
|
} = props;
|
|
1460
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1432
1461
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1433
1462
|
const data = isChildrenMode ? [] : props.data;
|
|
1434
1463
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1445,12 +1474,16 @@ function MultiCombobox(props) {
|
|
|
1445
1474
|
const selectAll = Boolean(selectAllProp);
|
|
1446
1475
|
const selectAllLabel = typeof selectAllProp === "string" ? selectAllProp : "Select all";
|
|
1447
1476
|
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1477
|
+
const creatableProp = isChildrenMode ? false : props.creatable ?? false;
|
|
1478
|
+
const onCreateProp = isChildrenMode ? void 0 : props.onCreate;
|
|
1448
1479
|
const children = isChildrenMode ? props.children : void 0;
|
|
1449
1480
|
const selectedItemIds = props.selectedItemIds;
|
|
1450
1481
|
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
1451
1482
|
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
1452
1483
|
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1453
1484
|
const [open, setOpen] = React9.useState(false);
|
|
1485
|
+
const [internalInputValue, setInternalInputValue] = React9.useState("");
|
|
1486
|
+
const skipNextInputChangeRef = React9.useRef(false);
|
|
1454
1487
|
const virtualizerRef = React9.useRef(null);
|
|
1455
1488
|
const isControlled = selectedItemIds !== void 0;
|
|
1456
1489
|
const idsToItems = React9.useCallback(
|
|
@@ -1469,6 +1502,18 @@ function MultiCombobox(props) {
|
|
|
1469
1502
|
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
1470
1503
|
[visibleData, groupBy]
|
|
1471
1504
|
);
|
|
1505
|
+
const currentInputValue = inputValue ?? internalInputValue;
|
|
1506
|
+
const creatableSentinel = React9.useMemo(() => {
|
|
1507
|
+
if (!creatableProp) return null;
|
|
1508
|
+
const trimmed = currentInputValue.trim();
|
|
1509
|
+
if (!trimmed) return null;
|
|
1510
|
+
const lowered = trimmed.toLocaleLowerCase();
|
|
1511
|
+
const exactExists = data.some(
|
|
1512
|
+
(d) => itemToString(d).trim().toLocaleLowerCase() === lowered
|
|
1513
|
+
);
|
|
1514
|
+
if (exactExists) return null;
|
|
1515
|
+
return makeCreatableSentinel(trimmed);
|
|
1516
|
+
}, [creatableProp, currentInputValue, data, itemToString]);
|
|
1472
1517
|
const flatItems = React9.useMemo(() => {
|
|
1473
1518
|
if (!selectHeadings && !selectAll) return null;
|
|
1474
1519
|
if (!groups && !selectAll) return null;
|
|
@@ -1490,6 +1535,18 @@ function MultiCombobox(props) {
|
|
|
1490
1535
|
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
1491
1536
|
}
|
|
1492
1537
|
function handleValueChange(next) {
|
|
1538
|
+
const creatableClicked = next.find(isCreatableSentinel);
|
|
1539
|
+
if (creatableClicked) {
|
|
1540
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
1541
|
+
const realItems2 = next.filter(
|
|
1542
|
+
(v) => !isGroupHeaderSentinel2(v) && !isSelectAllSentinel2(v) && !isCreatableSentinel(v)
|
|
1543
|
+
);
|
|
1544
|
+
setSelectedItems(newItem ? [...realItems2, newItem] : realItems2);
|
|
1545
|
+
skipNextInputChangeRef.current = true;
|
|
1546
|
+
setInternalInputValue("");
|
|
1547
|
+
onInputValueChange?.("");
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1493
1550
|
const selectAllClicked = next.filter(isSelectAllSentinel2);
|
|
1494
1551
|
const groupSentinels = next.filter(isGroupHeaderSentinel2);
|
|
1495
1552
|
const realItems = next.filter(
|
|
@@ -1527,6 +1584,16 @@ function MultiCombobox(props) {
|
|
|
1527
1584
|
setSelectedItems(updated);
|
|
1528
1585
|
}
|
|
1529
1586
|
function handleSimpleValueChange(newItems) {
|
|
1587
|
+
const creatableClicked = newItems.find(isCreatableSentinel);
|
|
1588
|
+
if (creatableClicked) {
|
|
1589
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
1590
|
+
const realItems = newItems.filter((v) => !isCreatableSentinel(v));
|
|
1591
|
+
setSelectedItems(newItem ? [...realItems, newItem] : realItems);
|
|
1592
|
+
skipNextInputChangeRef.current = true;
|
|
1593
|
+
setInternalInputValue("");
|
|
1594
|
+
onInputValueChange?.("");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1530
1597
|
setSelectedItems(newItems);
|
|
1531
1598
|
}
|
|
1532
1599
|
function removeItem(item, e) {
|
|
@@ -1562,6 +1629,9 @@ function MultiCombobox(props) {
|
|
|
1562
1629
|
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
1563
1630
|
] }, `__header-${row.groupName}`);
|
|
1564
1631
|
}
|
|
1632
|
+
if (isCreatableSentinel(row)) {
|
|
1633
|
+
return renderCreatableItem(row);
|
|
1634
|
+
}
|
|
1565
1635
|
const item = row;
|
|
1566
1636
|
return /* @__PURE__ */ jsx9(
|
|
1567
1637
|
ComboboxItem_default,
|
|
@@ -1576,7 +1646,28 @@ function MultiCombobox(props) {
|
|
|
1576
1646
|
item.id
|
|
1577
1647
|
);
|
|
1578
1648
|
}
|
|
1579
|
-
|
|
1649
|
+
function renderCreatableItem(sentinel, style, index, measureRef) {
|
|
1650
|
+
return /* @__PURE__ */ jsx9(
|
|
1651
|
+
ComboboxItem_default,
|
|
1652
|
+
{
|
|
1653
|
+
value: sentinel,
|
|
1654
|
+
itemId: "__creatable",
|
|
1655
|
+
label: `Create "${sentinel.label}"`,
|
|
1656
|
+
inputType: "none",
|
|
1657
|
+
renderItem: () => /* @__PURE__ */ jsxs7(Fragment6, { children: [
|
|
1658
|
+
/* @__PURE__ */ jsx9(Icon3, { name: "plus-outline", size: "mini" }),
|
|
1659
|
+
`Create "${sentinel.label}"`
|
|
1660
|
+
] }),
|
|
1661
|
+
style,
|
|
1662
|
+
index,
|
|
1663
|
+
"data-index": index,
|
|
1664
|
+
ref: measureRef
|
|
1665
|
+
},
|
|
1666
|
+
"__creatable"
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
const baseItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
1670
|
+
const rootItems = creatableSentinel && Array.isArray(baseItems) ? [...baseItems, creatableSentinel] : baseItems;
|
|
1580
1671
|
const hasSentinels = selectAll || selectHeadings;
|
|
1581
1672
|
const rootValue = hasSentinels ? [...value] : value;
|
|
1582
1673
|
return /* @__PURE__ */ jsxs7(Field, { children: [
|
|
@@ -1593,9 +1684,31 @@ function MultiCombobox(props) {
|
|
|
1593
1684
|
if (!nextOpen && eventDetails.reason === "item-press") return;
|
|
1594
1685
|
setOpen(nextOpen);
|
|
1595
1686
|
},
|
|
1596
|
-
filter,
|
|
1597
|
-
|
|
1598
|
-
|
|
1687
|
+
filter: creatableProp ? (item, query) => {
|
|
1688
|
+
if (isCreatableSentinel(item)) return true;
|
|
1689
|
+
if (filter) return filter(item, query);
|
|
1690
|
+
if (isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item))
|
|
1691
|
+
return true;
|
|
1692
|
+
const label = itemToString(item);
|
|
1693
|
+
return label.toLowerCase().includes(query.toLowerCase());
|
|
1694
|
+
} : filter,
|
|
1695
|
+
inputValue: creatableProp ? currentInputValue : inputValue ?? internalInputValue,
|
|
1696
|
+
onInputValueChange: (v, eventDetails) => {
|
|
1697
|
+
if (eventDetails.reason === "input-clear" && open) return;
|
|
1698
|
+
if (creatableProp) {
|
|
1699
|
+
if (skipNextInputChangeRef.current) {
|
|
1700
|
+
skipNextInputChangeRef.current = false;
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
setInternalInputValue(v);
|
|
1704
|
+
onInputValueChange?.(v);
|
|
1705
|
+
} else {
|
|
1706
|
+
if (inputValue === void 0) {
|
|
1707
|
+
setInternalInputValue(v);
|
|
1708
|
+
}
|
|
1709
|
+
onInputValueChange?.(v);
|
|
1710
|
+
}
|
|
1711
|
+
},
|
|
1599
1712
|
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
1600
1713
|
const virt = virtualizerRef.current;
|
|
1601
1714
|
if (!item || !virt) return;
|
|
@@ -1615,15 +1728,17 @@ function MultiCombobox(props) {
|
|
|
1615
1728
|
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
1616
1729
|
),
|
|
1617
1730
|
itemToStringLabel: (item) => {
|
|
1618
|
-
if (!item || isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item))
|
|
1731
|
+
if (!item || isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item) || isCreatableSentinel(item))
|
|
1619
1732
|
return "";
|
|
1620
1733
|
return itemToString(item);
|
|
1621
1734
|
},
|
|
1622
1735
|
isItemEqualToValue: (a, b) => {
|
|
1736
|
+
if (isCreatableSentinel(a) && isCreatableSentinel(b))
|
|
1737
|
+
return a.label === b.label;
|
|
1623
1738
|
if (isGroupHeaderSentinel2(a) && isGroupHeaderSentinel2(b))
|
|
1624
1739
|
return a.groupName === b.groupName;
|
|
1625
1740
|
if (isSelectAllSentinel2(a) && isSelectAllSentinel2(b)) return true;
|
|
1626
|
-
if (!isGroupHeaderSentinel2(a) && !isSelectAllSentinel2(a) && !isGroupHeaderSentinel2(b) && !isSelectAllSentinel2(b))
|
|
1741
|
+
if (!isGroupHeaderSentinel2(a) && !isSelectAllSentinel2(a) && !isCreatableSentinel(a) && !isGroupHeaderSentinel2(b) && !isSelectAllSentinel2(b) && !isCreatableSentinel(b))
|
|
1627
1742
|
return a.id === b.id;
|
|
1628
1743
|
return false;
|
|
1629
1744
|
},
|
|
@@ -1656,7 +1771,8 @@ function MultiCombobox(props) {
|
|
|
1656
1771
|
ComboboxTokenInput,
|
|
1657
1772
|
{
|
|
1658
1773
|
id,
|
|
1659
|
-
placeholder: value.length > 0 ? "" : placeholder
|
|
1774
|
+
placeholder: value.length > 0 ? "" : placeholder,
|
|
1775
|
+
"aria-describedby": ariaDescribedBy
|
|
1660
1776
|
}
|
|
1661
1777
|
),
|
|
1662
1778
|
/* @__PURE__ */ jsxs7(ComboboxActionButtons, { children: [
|
|
@@ -1693,37 +1809,55 @@ function MultiCombobox(props) {
|
|
|
1693
1809
|
itemToString,
|
|
1694
1810
|
inputType,
|
|
1695
1811
|
renderItem,
|
|
1696
|
-
renderGroupHeading
|
|
1812
|
+
renderGroupHeading,
|
|
1813
|
+
renderCreatableItem: creatableProp ? (sentinel, style, index, measureRef) => renderCreatableItem(
|
|
1814
|
+
sentinel,
|
|
1815
|
+
style,
|
|
1816
|
+
index,
|
|
1817
|
+
measureRef
|
|
1818
|
+
) : void 0
|
|
1697
1819
|
}
|
|
1698
|
-
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) =>
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
}
|
|
1725
|
-
item
|
|
1726
|
-
|
|
1820
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => {
|
|
1821
|
+
if (isCreatableSentinel(group)) {
|
|
1822
|
+
return renderCreatableItem(group);
|
|
1823
|
+
}
|
|
1824
|
+
const g = group;
|
|
1825
|
+
return /* @__PURE__ */ jsxs7(React9.Fragment, { children: [
|
|
1826
|
+
/* @__PURE__ */ jsxs7(ComboboxGroup, { items: g.items, children: [
|
|
1827
|
+
/* @__PURE__ */ jsx9(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(g.value) : g.value }),
|
|
1828
|
+
/* @__PURE__ */ jsx9(Combobox4.Collection, { children: (item) => /* @__PURE__ */ jsx9(
|
|
1829
|
+
ComboboxItem_default,
|
|
1830
|
+
{
|
|
1831
|
+
value: item,
|
|
1832
|
+
itemId: String(item.id),
|
|
1833
|
+
label: itemToString(item),
|
|
1834
|
+
disabled: item.disabled,
|
|
1835
|
+
inputType,
|
|
1836
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1837
|
+
},
|
|
1838
|
+
item.id
|
|
1839
|
+
) })
|
|
1840
|
+
] }),
|
|
1841
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx9(ComboboxSeparator, {})
|
|
1842
|
+
] }, g.value);
|
|
1843
|
+
} : (item) => {
|
|
1844
|
+
if (isCreatableSentinel(item)) {
|
|
1845
|
+
return renderCreatableItem(item);
|
|
1846
|
+
}
|
|
1847
|
+
const dataItem = item;
|
|
1848
|
+
return /* @__PURE__ */ jsx9(
|
|
1849
|
+
ComboboxItem_default,
|
|
1850
|
+
{
|
|
1851
|
+
value: dataItem,
|
|
1852
|
+
itemId: String(dataItem.id),
|
|
1853
|
+
label: itemToString(dataItem),
|
|
1854
|
+
disabled: dataItem.disabled,
|
|
1855
|
+
inputType,
|
|
1856
|
+
renderItem: renderItem ? () => renderItem(dataItem) : () => itemToString(dataItem)
|
|
1857
|
+
},
|
|
1858
|
+
dataItem.id
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1727
1861
|
}
|
|
1728
1862
|
)
|
|
1729
1863
|
]
|
|
@@ -1751,6 +1885,7 @@ function SingleSearchableSelect(props) {
|
|
|
1751
1885
|
loadingText = "Loading...",
|
|
1752
1886
|
filter
|
|
1753
1887
|
} = props;
|
|
1888
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1754
1889
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1755
1890
|
const data = isChildrenMode ? [] : props.data;
|
|
1756
1891
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1825,7 +1960,7 @@ function SingleSearchableSelect(props) {
|
|
|
1825
1960
|
}
|
|
1826
1961
|
} : void 0,
|
|
1827
1962
|
children: [
|
|
1828
|
-
/* @__PURE__ */ jsxs8(SearchableSelectTrigger, { id, children: [
|
|
1963
|
+
/* @__PURE__ */ jsxs8(SearchableSelectTrigger, { id, "aria-describedby": ariaDescribedBy, children: [
|
|
1829
1964
|
/* @__PURE__ */ jsx10(SearchableSelectPlaceholder, { children: /* @__PURE__ */ jsx10(Combobox5.Value, { placeholder, children: value ? renderSelection ? renderSelection(value) : itemToString(value) : placeholder }) }),
|
|
1830
1965
|
/* @__PURE__ */ jsx10(SearchableSelectTriggerIcon, { children: /* @__PURE__ */ jsx10(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx10(ChevronIcon, {}) }) })
|
|
1831
1966
|
] }),
|
|
@@ -1922,8 +2057,9 @@ function SingleSearchableSelect(props) {
|
|
|
1922
2057
|
import * as React11 from "react";
|
|
1923
2058
|
import { Combobox as Combobox6 } from "@base-ui/react/combobox";
|
|
1924
2059
|
import "@tanstack/react-virtual";
|
|
2060
|
+
import Icon4 from "@sproutsocial/seeds-react-icon";
|
|
1925
2061
|
import styled7 from "styled-components";
|
|
1926
|
-
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2062
|
+
import { Fragment as Fragment10, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1927
2063
|
var SelectionText2 = styled7.span`
|
|
1928
2064
|
flex: 1;
|
|
1929
2065
|
overflow: hidden;
|
|
@@ -1950,6 +2086,7 @@ function MultiSearchableSelect(props) {
|
|
|
1950
2086
|
loadingText = "Loading...",
|
|
1951
2087
|
filter
|
|
1952
2088
|
} = props;
|
|
2089
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1953
2090
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1954
2091
|
const data = isChildrenMode ? [] : props.data;
|
|
1955
2092
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1964,12 +2101,16 @@ function MultiSearchableSelect(props) {
|
|
|
1964
2101
|
const selectHeadings = isChildrenMode ? false : props.selectHeadings ?? false;
|
|
1965
2102
|
const selectAll = isChildrenMode ? false : props.selectAll ?? false;
|
|
1966
2103
|
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
2104
|
+
const creatableProp = isChildrenMode ? false : props.creatable ?? false;
|
|
2105
|
+
const onCreateProp = isChildrenMode ? void 0 : props.onCreate;
|
|
1967
2106
|
const children = isChildrenMode ? props.children : void 0;
|
|
1968
2107
|
const selectedItemIds = props.selectedItemIds;
|
|
1969
2108
|
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
1970
2109
|
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
1971
2110
|
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1972
2111
|
const [open, setOpen] = React11.useState(false);
|
|
2112
|
+
const [internalInputValue, setInternalInputValue] = React11.useState("");
|
|
2113
|
+
const skipNextInputChangeRef = React11.useRef(false);
|
|
1973
2114
|
const virtualizerRef = React11.useRef(null);
|
|
1974
2115
|
const isControlled = selectedItemIds !== void 0;
|
|
1975
2116
|
const idsToItems = React11.useCallback(
|
|
@@ -1988,6 +2129,18 @@ function MultiSearchableSelect(props) {
|
|
|
1988
2129
|
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
1989
2130
|
[visibleData, groupBy]
|
|
1990
2131
|
);
|
|
2132
|
+
const currentInputValue = internalInputValue;
|
|
2133
|
+
const creatableSentinel = React11.useMemo(() => {
|
|
2134
|
+
if (!creatableProp) return null;
|
|
2135
|
+
const trimmed = currentInputValue.trim();
|
|
2136
|
+
if (!trimmed) return null;
|
|
2137
|
+
const lowered = trimmed.toLocaleLowerCase();
|
|
2138
|
+
const exactExists = data.some(
|
|
2139
|
+
(d) => itemToString(d).trim().toLocaleLowerCase() === lowered
|
|
2140
|
+
);
|
|
2141
|
+
if (exactExists) return null;
|
|
2142
|
+
return makeCreatableSentinel(trimmed);
|
|
2143
|
+
}, [creatableProp, currentInputValue, data, itemToString]);
|
|
1991
2144
|
const flatItems = React11.useMemo(() => {
|
|
1992
2145
|
if (!groups || !selectHeadings && !selectAll) return null;
|
|
1993
2146
|
const rows = [];
|
|
@@ -2004,10 +2157,21 @@ function MultiSearchableSelect(props) {
|
|
|
2004
2157
|
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
2005
2158
|
}
|
|
2006
2159
|
function handleValueChange(next) {
|
|
2160
|
+
const creatableClicked = next.find(isCreatableSentinel);
|
|
2161
|
+
if (creatableClicked) {
|
|
2162
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
2163
|
+
const realItems2 = next.filter(
|
|
2164
|
+
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v) && !isCreatableSentinel(v)
|
|
2165
|
+
);
|
|
2166
|
+
setSelectedItems(newItem ? [...realItems2, newItem] : realItems2);
|
|
2167
|
+
skipNextInputChangeRef.current = true;
|
|
2168
|
+
setInternalInputValue("");
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2007
2171
|
const selectAllClicked = next.filter(isSelectAllSentinel3);
|
|
2008
2172
|
const groupSentinels = next.filter(isGroupHeaderSentinel3);
|
|
2009
2173
|
const realItems = next.filter(
|
|
2010
|
-
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v)
|
|
2174
|
+
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v) && !isCreatableSentinel(v)
|
|
2011
2175
|
);
|
|
2012
2176
|
if (selectAllClicked.length > 0) {
|
|
2013
2177
|
const allSelected = data.every(
|
|
@@ -2041,6 +2205,15 @@ function MultiSearchableSelect(props) {
|
|
|
2041
2205
|
setSelectedItems(updated);
|
|
2042
2206
|
}
|
|
2043
2207
|
function handleSimpleValueChange(newItems) {
|
|
2208
|
+
const creatableClicked = newItems.find(isCreatableSentinel);
|
|
2209
|
+
if (creatableClicked) {
|
|
2210
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
2211
|
+
const realItems = newItems.filter((v) => !isCreatableSentinel(v));
|
|
2212
|
+
setSelectedItems(newItem ? [...realItems, newItem] : realItems);
|
|
2213
|
+
skipNextInputChangeRef.current = true;
|
|
2214
|
+
setInternalInputValue("");
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2044
2217
|
setSelectedItems(newItems);
|
|
2045
2218
|
}
|
|
2046
2219
|
function renderSentinelRow(row) {
|
|
@@ -2071,6 +2244,9 @@ function MultiSearchableSelect(props) {
|
|
|
2071
2244
|
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
2072
2245
|
] }, `__header-${row.groupName}`);
|
|
2073
2246
|
}
|
|
2247
|
+
if (isCreatableSentinel(row)) {
|
|
2248
|
+
return renderCreatableItem(row);
|
|
2249
|
+
}
|
|
2074
2250
|
const item = row;
|
|
2075
2251
|
return /* @__PURE__ */ jsx11(
|
|
2076
2252
|
ComboboxItem_default,
|
|
@@ -2085,7 +2261,28 @@ function MultiSearchableSelect(props) {
|
|
|
2085
2261
|
item.id
|
|
2086
2262
|
);
|
|
2087
2263
|
}
|
|
2088
|
-
|
|
2264
|
+
function renderCreatableItem(sentinel, style, index, measureRef) {
|
|
2265
|
+
return /* @__PURE__ */ jsx11(
|
|
2266
|
+
ComboboxItem_default,
|
|
2267
|
+
{
|
|
2268
|
+
value: sentinel,
|
|
2269
|
+
itemId: "__creatable",
|
|
2270
|
+
label: `Create "${sentinel.label}"`,
|
|
2271
|
+
inputType: "none",
|
|
2272
|
+
renderItem: () => /* @__PURE__ */ jsxs9(Fragment10, { children: [
|
|
2273
|
+
/* @__PURE__ */ jsx11(Icon4, { name: "plus-outline", size: "mini" }),
|
|
2274
|
+
`Create "${sentinel.label}"`
|
|
2275
|
+
] }),
|
|
2276
|
+
style,
|
|
2277
|
+
index,
|
|
2278
|
+
"data-index": index,
|
|
2279
|
+
ref: measureRef
|
|
2280
|
+
},
|
|
2281
|
+
"__creatable"
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
2284
|
+
const baseItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
2285
|
+
const rootItems = creatableSentinel && Array.isArray(baseItems) ? [...baseItems, creatableSentinel] : baseItems;
|
|
2089
2286
|
const hasSentinels = selectAll || selectHeadings;
|
|
2090
2287
|
const rootValue = hasSentinels ? [...value] : value;
|
|
2091
2288
|
return /* @__PURE__ */ jsxs9(Field, { children: [
|
|
@@ -2096,7 +2293,23 @@ function MultiSearchableSelect(props) {
|
|
|
2096
2293
|
multiple: true,
|
|
2097
2294
|
id,
|
|
2098
2295
|
virtualized: isVirtualized,
|
|
2099
|
-
filter,
|
|
2296
|
+
filter: creatableProp ? (item, query) => {
|
|
2297
|
+
if (isCreatableSentinel(item)) return true;
|
|
2298
|
+
if (filter) return filter(item, query);
|
|
2299
|
+
if (isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
|
|
2300
|
+
return true;
|
|
2301
|
+
const label = itemToString(item);
|
|
2302
|
+
return label.toLowerCase().includes(query.toLowerCase());
|
|
2303
|
+
} : filter,
|
|
2304
|
+
inputValue: internalInputValue,
|
|
2305
|
+
onInputValueChange: (v, eventDetails) => {
|
|
2306
|
+
if (eventDetails.reason === "input-clear" && open) return;
|
|
2307
|
+
if (skipNextInputChangeRef.current) {
|
|
2308
|
+
skipNextInputChangeRef.current = false;
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
setInternalInputValue(v);
|
|
2312
|
+
},
|
|
2100
2313
|
open,
|
|
2101
2314
|
disabled: props.disabled,
|
|
2102
2315
|
onOpenChange: setOpen,
|
|
@@ -2119,34 +2332,43 @@ function MultiSearchableSelect(props) {
|
|
|
2119
2332
|
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
2120
2333
|
),
|
|
2121
2334
|
itemToStringLabel: (item) => {
|
|
2122
|
-
if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
|
|
2335
|
+
if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item) || isCreatableSentinel(item))
|
|
2123
2336
|
return "";
|
|
2124
2337
|
return itemToString(item);
|
|
2125
2338
|
},
|
|
2126
2339
|
isItemEqualToValue: (a, b) => {
|
|
2340
|
+
if (isCreatableSentinel(a) && isCreatableSentinel(b))
|
|
2341
|
+
return a.label === b.label;
|
|
2127
2342
|
if (isGroupHeaderSentinel3(a) && isGroupHeaderSentinel3(b))
|
|
2128
2343
|
return a.groupName === b.groupName;
|
|
2129
2344
|
if (isSelectAllSentinel3(a) && isSelectAllSentinel3(b)) return true;
|
|
2130
|
-
if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b))
|
|
2345
|
+
if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isCreatableSentinel(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b) && !isCreatableSentinel(b))
|
|
2131
2346
|
return a.id === b.id;
|
|
2132
2347
|
return false;
|
|
2133
2348
|
},
|
|
2134
2349
|
items: rootItems,
|
|
2135
2350
|
children: [
|
|
2136
|
-
/* @__PURE__ */ jsxs9(
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2351
|
+
/* @__PURE__ */ jsxs9(
|
|
2352
|
+
SearchableSelectTokenTrigger,
|
|
2353
|
+
{
|
|
2354
|
+
id,
|
|
2355
|
+
"aria-describedby": ariaDescribedBy,
|
|
2356
|
+
children: [
|
|
2357
|
+
renderSelection ? renderSelection(value) : value.length > 0 ? (() => {
|
|
2358
|
+
const visibleItems = maxSelections != null ? value.slice(0, maxSelections) : value;
|
|
2359
|
+
const overflowCount = maxSelections != null && value.length > maxSelections ? value.length - maxSelections : 0;
|
|
2360
|
+
const text = visibleItems.map(
|
|
2361
|
+
(item) => customRenderSelections ? customRenderSelections(item) : itemToString(item)
|
|
2362
|
+
).join(", ");
|
|
2363
|
+
return /* @__PURE__ */ jsxs9(SelectionText2, { children: [
|
|
2364
|
+
text,
|
|
2365
|
+
overflowCount > 0 ? `, +${overflowCount}` : ""
|
|
2366
|
+
] });
|
|
2367
|
+
})() : /* @__PURE__ */ jsx11(SearchableSelectPlaceholder, { children: placeholder }),
|
|
2368
|
+
/* @__PURE__ */ jsx11(SearchableSelectTriggerIcon, { style: { marginLeft: "auto" }, children: /* @__PURE__ */ jsx11(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ jsx11(ChevronIcon, {}) }) })
|
|
2369
|
+
]
|
|
2370
|
+
}
|
|
2371
|
+
),
|
|
2150
2372
|
/* @__PURE__ */ jsx11(Combobox6.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx11(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ jsxs9(
|
|
2151
2373
|
SearchableSelectPopup,
|
|
2152
2374
|
{
|
|
@@ -2183,37 +2405,55 @@ function MultiSearchableSelect(props) {
|
|
|
2183
2405
|
itemToString,
|
|
2184
2406
|
inputType,
|
|
2185
2407
|
renderItem,
|
|
2186
|
-
renderGroupHeading
|
|
2408
|
+
renderGroupHeading,
|
|
2409
|
+
renderCreatableItem: creatableProp ? (sentinel, style, index, measureRef) => renderCreatableItem(
|
|
2410
|
+
sentinel,
|
|
2411
|
+
style,
|
|
2412
|
+
index,
|
|
2413
|
+
measureRef
|
|
2414
|
+
) : void 0
|
|
2187
2415
|
}
|
|
2188
|
-
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) =>
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
}
|
|
2215
|
-
item
|
|
2216
|
-
|
|
2416
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => {
|
|
2417
|
+
if (isCreatableSentinel(group)) {
|
|
2418
|
+
return renderCreatableItem(group);
|
|
2419
|
+
}
|
|
2420
|
+
const g = group;
|
|
2421
|
+
return /* @__PURE__ */ jsxs9(React11.Fragment, { children: [
|
|
2422
|
+
/* @__PURE__ */ jsxs9(ComboboxGroup, { items: g.items, children: [
|
|
2423
|
+
/* @__PURE__ */ jsx11(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(g.value) : g.value }),
|
|
2424
|
+
/* @__PURE__ */ jsx11(Combobox6.Collection, { children: (item) => /* @__PURE__ */ jsx11(
|
|
2425
|
+
ComboboxItem_default,
|
|
2426
|
+
{
|
|
2427
|
+
value: item,
|
|
2428
|
+
itemId: String(item.id),
|
|
2429
|
+
label: itemToString(item),
|
|
2430
|
+
disabled: item.disabled,
|
|
2431
|
+
inputType,
|
|
2432
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
2433
|
+
},
|
|
2434
|
+
item.id
|
|
2435
|
+
) })
|
|
2436
|
+
] }),
|
|
2437
|
+
index < groups.length - 1 && /* @__PURE__ */ jsx11(ComboboxSeparator, {})
|
|
2438
|
+
] }, g.value);
|
|
2439
|
+
} : (item) => {
|
|
2440
|
+
if (isCreatableSentinel(item)) {
|
|
2441
|
+
return renderCreatableItem(item);
|
|
2442
|
+
}
|
|
2443
|
+
const dataItem = item;
|
|
2444
|
+
return /* @__PURE__ */ jsx11(
|
|
2445
|
+
ComboboxItem_default,
|
|
2446
|
+
{
|
|
2447
|
+
value: dataItem,
|
|
2448
|
+
itemId: String(dataItem.id),
|
|
2449
|
+
label: itemToString(dataItem),
|
|
2450
|
+
disabled: dataItem.disabled,
|
|
2451
|
+
inputType,
|
|
2452
|
+
renderItem: renderItem ? () => renderItem(dataItem) : () => itemToString(dataItem)
|
|
2453
|
+
},
|
|
2454
|
+
dataItem.id
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2217
2457
|
}
|
|
2218
2458
|
)
|
|
2219
2459
|
]
|