@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/v3/index.js
CHANGED
|
@@ -364,6 +364,7 @@ var StyledValue = (0, import_styled_components4.default)(import_select3.Select.V
|
|
|
364
364
|
`;
|
|
365
365
|
function SingleSelect(props) {
|
|
366
366
|
const { id, placeholder, includePlaceholderItem } = props;
|
|
367
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
367
368
|
const isChildrenMode = "children" in props && props.children != null;
|
|
368
369
|
const selectedItemId = props.selectedItemId;
|
|
369
370
|
const defaultSelectedItemId = props.defaultSelectedItemId;
|
|
@@ -421,7 +422,7 @@ function SingleSelect(props) {
|
|
|
421
422
|
onValueChange: handleValueChange,
|
|
422
423
|
id,
|
|
423
424
|
children: [
|
|
424
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(SelectTrigger, { children: [
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(SelectTrigger, { "aria-describedby": ariaDescribedBy, children: [
|
|
425
426
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyledValue, { placeholder, children: renderSelection && selectedItem ? renderSelection(selectedItem) : void 0 }),
|
|
426
427
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SelectIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronIcon, {}) }) })
|
|
427
428
|
] }),
|
|
@@ -493,6 +494,7 @@ var Placeholder = import_styled_components5.default.div`
|
|
|
493
494
|
`;
|
|
494
495
|
function MultiSelect(props) {
|
|
495
496
|
const { id, placeholder = "Select..." } = props;
|
|
497
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
496
498
|
const isChildrenMode = "children" in props && props.children != null;
|
|
497
499
|
const data = isChildrenMode ? [] : props.data;
|
|
498
500
|
const itemToString = isChildrenMode ? () => "" : props.itemToString;
|
|
@@ -549,7 +551,7 @@ function MultiSelect(props) {
|
|
|
549
551
|
disabled: props.disabled,
|
|
550
552
|
id,
|
|
551
553
|
children: [
|
|
552
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(SelectTrigger, { children: [
|
|
554
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(SelectTrigger, { "aria-describedby": ariaDescribedBy, children: [
|
|
553
555
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectValue, { children: () => {
|
|
554
556
|
if (renderSelection) return renderSelection(selectedItems);
|
|
555
557
|
if (selectedItems.length === 0)
|
|
@@ -1160,6 +1162,12 @@ function makePlaceholderSentinel(label) {
|
|
|
1160
1162
|
function isPlaceholderSentinel(v) {
|
|
1161
1163
|
return typeof v === "object" && v !== null && "__placeholder" in v;
|
|
1162
1164
|
}
|
|
1165
|
+
function makeCreatableSentinel(label) {
|
|
1166
|
+
return { __creatable: true, label };
|
|
1167
|
+
}
|
|
1168
|
+
function isCreatableSentinel(v) {
|
|
1169
|
+
return typeof v === "object" && v !== null && "__creatable" in v;
|
|
1170
|
+
}
|
|
1163
1171
|
|
|
1164
1172
|
// src/v3/Common/VirtualizedComboboxList.tsx
|
|
1165
1173
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
@@ -1179,7 +1187,8 @@ function VirtualizedComboboxList({
|
|
|
1179
1187
|
itemToString,
|
|
1180
1188
|
inputType,
|
|
1181
1189
|
renderItem,
|
|
1182
|
-
renderGroupHeading
|
|
1190
|
+
renderGroupHeading,
|
|
1191
|
+
renderCreatableItem
|
|
1183
1192
|
}) {
|
|
1184
1193
|
const filteredItems = import_combobox2.Combobox.useFilteredItems();
|
|
1185
1194
|
const scrollElementRef = React7.useRef(null);
|
|
@@ -1297,6 +1306,17 @@ function VirtualizedComboboxList({
|
|
|
1297
1306
|
virtualItem.key
|
|
1298
1307
|
);
|
|
1299
1308
|
}
|
|
1309
|
+
if (isCreatableSentinel(row)) {
|
|
1310
|
+
if (renderCreatableItem) {
|
|
1311
|
+
return renderCreatableItem(
|
|
1312
|
+
row,
|
|
1313
|
+
baseStyle,
|
|
1314
|
+
virtualItem.index,
|
|
1315
|
+
virtualizer.measureElement
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
return null;
|
|
1319
|
+
}
|
|
1300
1320
|
const item = row;
|
|
1301
1321
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1302
1322
|
ComboboxItem_default,
|
|
@@ -1330,6 +1350,7 @@ function SingleCombobox(props) {
|
|
|
1330
1350
|
emptyText = "No results found.",
|
|
1331
1351
|
filter
|
|
1332
1352
|
} = props;
|
|
1353
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1333
1354
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1334
1355
|
const data = isChildrenMode ? [] : props.data;
|
|
1335
1356
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1400,7 +1421,14 @@ function SingleCombobox(props) {
|
|
|
1400
1421
|
} : void 0,
|
|
1401
1422
|
children: [
|
|
1402
1423
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ComboboxInputGroup, { children: [
|
|
1403
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1425
|
+
ComboboxInput,
|
|
1426
|
+
{
|
|
1427
|
+
placeholder,
|
|
1428
|
+
id,
|
|
1429
|
+
"aria-describedby": ariaDescribedBy
|
|
1430
|
+
}
|
|
1431
|
+
),
|
|
1404
1432
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ComboboxActionButtons, { children: [
|
|
1405
1433
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ComboboxClear, { "aria-label": "Clear selection", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ClearIcon, {}) }),
|
|
1406
1434
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ComboboxTrigger, { "aria-label": "Open popup", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StyledChevron, { $isOpen: open, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChevronIcon, {}) }) })
|
|
@@ -1502,6 +1530,7 @@ function MultiCombobox(props) {
|
|
|
1502
1530
|
inputValue,
|
|
1503
1531
|
onInputValueChange
|
|
1504
1532
|
} = props;
|
|
1533
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1505
1534
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1506
1535
|
const data = isChildrenMode ? [] : props.data;
|
|
1507
1536
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1518,12 +1547,16 @@ function MultiCombobox(props) {
|
|
|
1518
1547
|
const selectAll = Boolean(selectAllProp);
|
|
1519
1548
|
const selectAllLabel = typeof selectAllProp === "string" ? selectAllProp : "Select all";
|
|
1520
1549
|
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
1550
|
+
const creatableProp = isChildrenMode ? false : props.creatable ?? false;
|
|
1551
|
+
const onCreateProp = isChildrenMode ? void 0 : props.onCreate;
|
|
1521
1552
|
const children = isChildrenMode ? props.children : void 0;
|
|
1522
1553
|
const selectedItemIds = props.selectedItemIds;
|
|
1523
1554
|
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
1524
1555
|
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
1525
1556
|
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
1526
1557
|
const [open, setOpen] = React9.useState(false);
|
|
1558
|
+
const [internalInputValue, setInternalInputValue] = React9.useState("");
|
|
1559
|
+
const skipNextInputChangeRef = React9.useRef(false);
|
|
1527
1560
|
const virtualizerRef = React9.useRef(null);
|
|
1528
1561
|
const isControlled = selectedItemIds !== void 0;
|
|
1529
1562
|
const idsToItems = React9.useCallback(
|
|
@@ -1542,6 +1575,18 @@ function MultiCombobox(props) {
|
|
|
1542
1575
|
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
1543
1576
|
[visibleData, groupBy]
|
|
1544
1577
|
);
|
|
1578
|
+
const currentInputValue = inputValue ?? internalInputValue;
|
|
1579
|
+
const creatableSentinel = React9.useMemo(() => {
|
|
1580
|
+
if (!creatableProp) return null;
|
|
1581
|
+
const trimmed = currentInputValue.trim();
|
|
1582
|
+
if (!trimmed) return null;
|
|
1583
|
+
const lowered = trimmed.toLocaleLowerCase();
|
|
1584
|
+
const exactExists = data.some(
|
|
1585
|
+
(d) => itemToString(d).trim().toLocaleLowerCase() === lowered
|
|
1586
|
+
);
|
|
1587
|
+
if (exactExists) return null;
|
|
1588
|
+
return makeCreatableSentinel(trimmed);
|
|
1589
|
+
}, [creatableProp, currentInputValue, data, itemToString]);
|
|
1545
1590
|
const flatItems = React9.useMemo(() => {
|
|
1546
1591
|
if (!selectHeadings && !selectAll) return null;
|
|
1547
1592
|
if (!groups && !selectAll) return null;
|
|
@@ -1563,6 +1608,18 @@ function MultiCombobox(props) {
|
|
|
1563
1608
|
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
1564
1609
|
}
|
|
1565
1610
|
function handleValueChange(next) {
|
|
1611
|
+
const creatableClicked = next.find(isCreatableSentinel);
|
|
1612
|
+
if (creatableClicked) {
|
|
1613
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
1614
|
+
const realItems2 = next.filter(
|
|
1615
|
+
(v) => !isGroupHeaderSentinel2(v) && !isSelectAllSentinel2(v) && !isCreatableSentinel(v)
|
|
1616
|
+
);
|
|
1617
|
+
setSelectedItems(newItem ? [...realItems2, newItem] : realItems2);
|
|
1618
|
+
skipNextInputChangeRef.current = true;
|
|
1619
|
+
setInternalInputValue("");
|
|
1620
|
+
onInputValueChange?.("");
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1566
1623
|
const selectAllClicked = next.filter(isSelectAllSentinel2);
|
|
1567
1624
|
const groupSentinels = next.filter(isGroupHeaderSentinel2);
|
|
1568
1625
|
const realItems = next.filter(
|
|
@@ -1600,6 +1657,16 @@ function MultiCombobox(props) {
|
|
|
1600
1657
|
setSelectedItems(updated);
|
|
1601
1658
|
}
|
|
1602
1659
|
function handleSimpleValueChange(newItems) {
|
|
1660
|
+
const creatableClicked = newItems.find(isCreatableSentinel);
|
|
1661
|
+
if (creatableClicked) {
|
|
1662
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
1663
|
+
const realItems = newItems.filter((v) => !isCreatableSentinel(v));
|
|
1664
|
+
setSelectedItems(newItem ? [...realItems, newItem] : realItems);
|
|
1665
|
+
skipNextInputChangeRef.current = true;
|
|
1666
|
+
setInternalInputValue("");
|
|
1667
|
+
onInputValueChange?.("");
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1603
1670
|
setSelectedItems(newItems);
|
|
1604
1671
|
}
|
|
1605
1672
|
function removeItem(item, e) {
|
|
@@ -1635,6 +1702,9 @@ function MultiCombobox(props) {
|
|
|
1635
1702
|
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
1636
1703
|
] }, `__header-${row.groupName}`);
|
|
1637
1704
|
}
|
|
1705
|
+
if (isCreatableSentinel(row)) {
|
|
1706
|
+
return renderCreatableItem(row);
|
|
1707
|
+
}
|
|
1638
1708
|
const item = row;
|
|
1639
1709
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1640
1710
|
ComboboxItem_default,
|
|
@@ -1649,7 +1719,28 @@ function MultiCombobox(props) {
|
|
|
1649
1719
|
item.id
|
|
1650
1720
|
);
|
|
1651
1721
|
}
|
|
1652
|
-
|
|
1722
|
+
function renderCreatableItem(sentinel, style, index, measureRef) {
|
|
1723
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1724
|
+
ComboboxItem_default,
|
|
1725
|
+
{
|
|
1726
|
+
value: sentinel,
|
|
1727
|
+
itemId: "__creatable",
|
|
1728
|
+
label: `Create "${sentinel.label}"`,
|
|
1729
|
+
inputType: "none",
|
|
1730
|
+
renderItem: () => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_seeds_react_icon3.default, { name: "plus-outline", size: "mini" }),
|
|
1732
|
+
`Create "${sentinel.label}"`
|
|
1733
|
+
] }),
|
|
1734
|
+
style,
|
|
1735
|
+
index,
|
|
1736
|
+
"data-index": index,
|
|
1737
|
+
ref: measureRef
|
|
1738
|
+
},
|
|
1739
|
+
"__creatable"
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
const baseItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
1743
|
+
const rootItems = creatableSentinel && Array.isArray(baseItems) ? [...baseItems, creatableSentinel] : baseItems;
|
|
1653
1744
|
const hasSentinels = selectAll || selectHeadings;
|
|
1654
1745
|
const rootValue = hasSentinels ? [...value] : value;
|
|
1655
1746
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Field, { children: [
|
|
@@ -1666,9 +1757,31 @@ function MultiCombobox(props) {
|
|
|
1666
1757
|
if (!nextOpen && eventDetails.reason === "item-press") return;
|
|
1667
1758
|
setOpen(nextOpen);
|
|
1668
1759
|
},
|
|
1669
|
-
filter,
|
|
1670
|
-
|
|
1671
|
-
|
|
1760
|
+
filter: creatableProp ? (item, query) => {
|
|
1761
|
+
if (isCreatableSentinel(item)) return true;
|
|
1762
|
+
if (filter) return filter(item, query);
|
|
1763
|
+
if (isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item))
|
|
1764
|
+
return true;
|
|
1765
|
+
const label = itemToString(item);
|
|
1766
|
+
return label.toLowerCase().includes(query.toLowerCase());
|
|
1767
|
+
} : filter,
|
|
1768
|
+
inputValue: creatableProp ? currentInputValue : inputValue ?? internalInputValue,
|
|
1769
|
+
onInputValueChange: (v, eventDetails) => {
|
|
1770
|
+
if (eventDetails.reason === "input-clear" && open) return;
|
|
1771
|
+
if (creatableProp) {
|
|
1772
|
+
if (skipNextInputChangeRef.current) {
|
|
1773
|
+
skipNextInputChangeRef.current = false;
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
setInternalInputValue(v);
|
|
1777
|
+
onInputValueChange?.(v);
|
|
1778
|
+
} else {
|
|
1779
|
+
if (inputValue === void 0) {
|
|
1780
|
+
setInternalInputValue(v);
|
|
1781
|
+
}
|
|
1782
|
+
onInputValueChange?.(v);
|
|
1783
|
+
}
|
|
1784
|
+
},
|
|
1672
1785
|
onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
|
|
1673
1786
|
const virt = virtualizerRef.current;
|
|
1674
1787
|
if (!item || !virt) return;
|
|
@@ -1688,15 +1801,17 @@ function MultiCombobox(props) {
|
|
|
1688
1801
|
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
1689
1802
|
),
|
|
1690
1803
|
itemToStringLabel: (item) => {
|
|
1691
|
-
if (!item || isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item))
|
|
1804
|
+
if (!item || isGroupHeaderSentinel2(item) || isSelectAllSentinel2(item) || isCreatableSentinel(item))
|
|
1692
1805
|
return "";
|
|
1693
1806
|
return itemToString(item);
|
|
1694
1807
|
},
|
|
1695
1808
|
isItemEqualToValue: (a, b) => {
|
|
1809
|
+
if (isCreatableSentinel(a) && isCreatableSentinel(b))
|
|
1810
|
+
return a.label === b.label;
|
|
1696
1811
|
if (isGroupHeaderSentinel2(a) && isGroupHeaderSentinel2(b))
|
|
1697
1812
|
return a.groupName === b.groupName;
|
|
1698
1813
|
if (isSelectAllSentinel2(a) && isSelectAllSentinel2(b)) return true;
|
|
1699
|
-
if (!isGroupHeaderSentinel2(a) && !isSelectAllSentinel2(a) && !isGroupHeaderSentinel2(b) && !isSelectAllSentinel2(b))
|
|
1814
|
+
if (!isGroupHeaderSentinel2(a) && !isSelectAllSentinel2(a) && !isCreatableSentinel(a) && !isGroupHeaderSentinel2(b) && !isSelectAllSentinel2(b) && !isCreatableSentinel(b))
|
|
1700
1815
|
return a.id === b.id;
|
|
1701
1816
|
return false;
|
|
1702
1817
|
},
|
|
@@ -1729,7 +1844,8 @@ function MultiCombobox(props) {
|
|
|
1729
1844
|
ComboboxTokenInput,
|
|
1730
1845
|
{
|
|
1731
1846
|
id,
|
|
1732
|
-
placeholder: value.length > 0 ? "" : placeholder
|
|
1847
|
+
placeholder: value.length > 0 ? "" : placeholder,
|
|
1848
|
+
"aria-describedby": ariaDescribedBy
|
|
1733
1849
|
}
|
|
1734
1850
|
),
|
|
1735
1851
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(ComboboxActionButtons, { children: [
|
|
@@ -1766,37 +1882,55 @@ function MultiCombobox(props) {
|
|
|
1766
1882
|
itemToString,
|
|
1767
1883
|
inputType,
|
|
1768
1884
|
renderItem,
|
|
1769
|
-
renderGroupHeading
|
|
1885
|
+
renderGroupHeading,
|
|
1886
|
+
renderCreatableItem: creatableProp ? (sentinel, style, index, measureRef) => renderCreatableItem(
|
|
1887
|
+
sentinel,
|
|
1888
|
+
style,
|
|
1889
|
+
index,
|
|
1890
|
+
measureRef
|
|
1891
|
+
) : void 0
|
|
1770
1892
|
}
|
|
1771
|
-
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) =>
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
}
|
|
1798
|
-
item
|
|
1799
|
-
|
|
1893
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => {
|
|
1894
|
+
if (isCreatableSentinel(group)) {
|
|
1895
|
+
return renderCreatableItem(group);
|
|
1896
|
+
}
|
|
1897
|
+
const g = group;
|
|
1898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(React9.Fragment, { children: [
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(ComboboxGroup, { items: g.items, children: [
|
|
1900
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(g.value) : g.value }),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_combobox4.Combobox.Collection, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1902
|
+
ComboboxItem_default,
|
|
1903
|
+
{
|
|
1904
|
+
value: item,
|
|
1905
|
+
itemId: String(item.id),
|
|
1906
|
+
label: itemToString(item),
|
|
1907
|
+
disabled: item.disabled,
|
|
1908
|
+
inputType,
|
|
1909
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
1910
|
+
},
|
|
1911
|
+
item.id
|
|
1912
|
+
) })
|
|
1913
|
+
] }),
|
|
1914
|
+
index < groups.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComboboxSeparator, {})
|
|
1915
|
+
] }, g.value);
|
|
1916
|
+
} : (item) => {
|
|
1917
|
+
if (isCreatableSentinel(item)) {
|
|
1918
|
+
return renderCreatableItem(item);
|
|
1919
|
+
}
|
|
1920
|
+
const dataItem = item;
|
|
1921
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1922
|
+
ComboboxItem_default,
|
|
1923
|
+
{
|
|
1924
|
+
value: dataItem,
|
|
1925
|
+
itemId: String(dataItem.id),
|
|
1926
|
+
label: itemToString(dataItem),
|
|
1927
|
+
disabled: dataItem.disabled,
|
|
1928
|
+
inputType,
|
|
1929
|
+
renderItem: renderItem ? () => renderItem(dataItem) : () => itemToString(dataItem)
|
|
1930
|
+
},
|
|
1931
|
+
dataItem.id
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1800
1934
|
}
|
|
1801
1935
|
)
|
|
1802
1936
|
]
|
|
@@ -1824,6 +1958,7 @@ function SingleSearchableSelect(props) {
|
|
|
1824
1958
|
loadingText = "Loading...",
|
|
1825
1959
|
filter
|
|
1826
1960
|
} = props;
|
|
1961
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
1827
1962
|
const isChildrenMode = "children" in props && props.children != null;
|
|
1828
1963
|
const data = isChildrenMode ? [] : props.data;
|
|
1829
1964
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -1898,7 +2033,7 @@ function SingleSearchableSelect(props) {
|
|
|
1898
2033
|
}
|
|
1899
2034
|
} : void 0,
|
|
1900
2035
|
children: [
|
|
1901
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(SearchableSelectTrigger, { id, children: [
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(SearchableSelectTrigger, { id, "aria-describedby": ariaDescribedBy, children: [
|
|
1902
2037
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SearchableSelectPlaceholder, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_combobox5.Combobox.Value, { placeholder, children: value ? renderSelection ? renderSelection(value) : itemToString(value) : placeholder }) }),
|
|
1903
2038
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SearchableSelectTriggerIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChevronIcon, {}) }) })
|
|
1904
2039
|
] }),
|
|
@@ -1995,6 +2130,7 @@ function SingleSearchableSelect(props) {
|
|
|
1995
2130
|
var React11 = __toESM(require("react"));
|
|
1996
2131
|
var import_combobox6 = require("@base-ui/react/combobox");
|
|
1997
2132
|
var import_react_virtual5 = require("@tanstack/react-virtual");
|
|
2133
|
+
var import_seeds_react_icon4 = __toESM(require("@sproutsocial/seeds-react-icon"));
|
|
1998
2134
|
var import_styled_components7 = __toESM(require("styled-components"));
|
|
1999
2135
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2000
2136
|
var SelectionText2 = import_styled_components7.default.span`
|
|
@@ -2023,6 +2159,7 @@ function MultiSearchableSelect(props) {
|
|
|
2023
2159
|
loadingText = "Loading...",
|
|
2024
2160
|
filter
|
|
2025
2161
|
} = props;
|
|
2162
|
+
const ariaDescribedBy = props["aria-describedby"];
|
|
2026
2163
|
const isChildrenMode = "children" in props && props.children != null;
|
|
2027
2164
|
const data = isChildrenMode ? [] : props.data;
|
|
2028
2165
|
const itemToString = isChildrenMode ? (_item) => "" : props.itemToString;
|
|
@@ -2037,12 +2174,16 @@ function MultiSearchableSelect(props) {
|
|
|
2037
2174
|
const selectHeadings = isChildrenMode ? false : props.selectHeadings ?? false;
|
|
2038
2175
|
const selectAll = isChildrenMode ? false : props.selectAll ?? false;
|
|
2039
2176
|
const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
|
|
2177
|
+
const creatableProp = isChildrenMode ? false : props.creatable ?? false;
|
|
2178
|
+
const onCreateProp = isChildrenMode ? void 0 : props.onCreate;
|
|
2040
2179
|
const children = isChildrenMode ? props.children : void 0;
|
|
2041
2180
|
const selectedItemIds = props.selectedItemIds;
|
|
2042
2181
|
const defaultSelectedItemIds = props.defaultSelectedItemIds;
|
|
2043
2182
|
const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
|
|
2044
2183
|
const { containerRef, portalContainer } = useSeedsPortalContainer();
|
|
2045
2184
|
const [open, setOpen] = React11.useState(false);
|
|
2185
|
+
const [internalInputValue, setInternalInputValue] = React11.useState("");
|
|
2186
|
+
const skipNextInputChangeRef = React11.useRef(false);
|
|
2046
2187
|
const virtualizerRef = React11.useRef(null);
|
|
2047
2188
|
const isControlled = selectedItemIds !== void 0;
|
|
2048
2189
|
const idsToItems = React11.useCallback(
|
|
@@ -2061,6 +2202,18 @@ function MultiSearchableSelect(props) {
|
|
|
2061
2202
|
() => groupBy ? groupItems(visibleData, groupBy) : null,
|
|
2062
2203
|
[visibleData, groupBy]
|
|
2063
2204
|
);
|
|
2205
|
+
const currentInputValue = internalInputValue;
|
|
2206
|
+
const creatableSentinel = React11.useMemo(() => {
|
|
2207
|
+
if (!creatableProp) return null;
|
|
2208
|
+
const trimmed = currentInputValue.trim();
|
|
2209
|
+
if (!trimmed) return null;
|
|
2210
|
+
const lowered = trimmed.toLocaleLowerCase();
|
|
2211
|
+
const exactExists = data.some(
|
|
2212
|
+
(d) => itemToString(d).trim().toLocaleLowerCase() === lowered
|
|
2213
|
+
);
|
|
2214
|
+
if (exactExists) return null;
|
|
2215
|
+
return makeCreatableSentinel(trimmed);
|
|
2216
|
+
}, [creatableProp, currentInputValue, data, itemToString]);
|
|
2064
2217
|
const flatItems = React11.useMemo(() => {
|
|
2065
2218
|
if (!groups || !selectHeadings && !selectAll) return null;
|
|
2066
2219
|
const rows = [];
|
|
@@ -2077,10 +2230,21 @@ function MultiSearchableSelect(props) {
|
|
|
2077
2230
|
onSelectedItemIdsChange(items.map((item) => item.id));
|
|
2078
2231
|
}
|
|
2079
2232
|
function handleValueChange(next) {
|
|
2233
|
+
const creatableClicked = next.find(isCreatableSentinel);
|
|
2234
|
+
if (creatableClicked) {
|
|
2235
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
2236
|
+
const realItems2 = next.filter(
|
|
2237
|
+
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v) && !isCreatableSentinel(v)
|
|
2238
|
+
);
|
|
2239
|
+
setSelectedItems(newItem ? [...realItems2, newItem] : realItems2);
|
|
2240
|
+
skipNextInputChangeRef.current = true;
|
|
2241
|
+
setInternalInputValue("");
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2080
2244
|
const selectAllClicked = next.filter(isSelectAllSentinel3);
|
|
2081
2245
|
const groupSentinels = next.filter(isGroupHeaderSentinel3);
|
|
2082
2246
|
const realItems = next.filter(
|
|
2083
|
-
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v)
|
|
2247
|
+
(v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v) && !isCreatableSentinel(v)
|
|
2084
2248
|
);
|
|
2085
2249
|
if (selectAllClicked.length > 0) {
|
|
2086
2250
|
const allSelected = data.every(
|
|
@@ -2114,6 +2278,15 @@ function MultiSearchableSelect(props) {
|
|
|
2114
2278
|
setSelectedItems(updated);
|
|
2115
2279
|
}
|
|
2116
2280
|
function handleSimpleValueChange(newItems) {
|
|
2281
|
+
const creatableClicked = newItems.find(isCreatableSentinel);
|
|
2282
|
+
if (creatableClicked) {
|
|
2283
|
+
const newItem = onCreateProp?.(creatableClicked.label);
|
|
2284
|
+
const realItems = newItems.filter((v) => !isCreatableSentinel(v));
|
|
2285
|
+
setSelectedItems(newItem ? [...realItems, newItem] : realItems);
|
|
2286
|
+
skipNextInputChangeRef.current = true;
|
|
2287
|
+
setInternalInputValue("");
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2117
2290
|
setSelectedItems(newItems);
|
|
2118
2291
|
}
|
|
2119
2292
|
function renderSentinelRow(row) {
|
|
@@ -2144,6 +2317,9 @@ function MultiSearchableSelect(props) {
|
|
|
2144
2317
|
renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
|
|
2145
2318
|
] }, `__header-${row.groupName}`);
|
|
2146
2319
|
}
|
|
2320
|
+
if (isCreatableSentinel(row)) {
|
|
2321
|
+
return renderCreatableItem(row);
|
|
2322
|
+
}
|
|
2147
2323
|
const item = row;
|
|
2148
2324
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2149
2325
|
ComboboxItem_default,
|
|
@@ -2158,7 +2334,28 @@ function MultiSearchableSelect(props) {
|
|
|
2158
2334
|
item.id
|
|
2159
2335
|
);
|
|
2160
2336
|
}
|
|
2161
|
-
|
|
2337
|
+
function renderCreatableItem(sentinel, style, index, measureRef) {
|
|
2338
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2339
|
+
ComboboxItem_default,
|
|
2340
|
+
{
|
|
2341
|
+
value: sentinel,
|
|
2342
|
+
itemId: "__creatable",
|
|
2343
|
+
label: `Create "${sentinel.label}"`,
|
|
2344
|
+
inputType: "none",
|
|
2345
|
+
renderItem: () => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2346
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_seeds_react_icon4.default, { name: "plus-outline", size: "mini" }),
|
|
2347
|
+
`Create "${sentinel.label}"`
|
|
2348
|
+
] }),
|
|
2349
|
+
style,
|
|
2350
|
+
index,
|
|
2351
|
+
"data-index": index,
|
|
2352
|
+
ref: measureRef
|
|
2353
|
+
},
|
|
2354
|
+
"__creatable"
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
const baseItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
|
|
2358
|
+
const rootItems = creatableSentinel && Array.isArray(baseItems) ? [...baseItems, creatableSentinel] : baseItems;
|
|
2162
2359
|
const hasSentinels = selectAll || selectHeadings;
|
|
2163
2360
|
const rootValue = hasSentinels ? [...value] : value;
|
|
2164
2361
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Field, { children: [
|
|
@@ -2169,7 +2366,23 @@ function MultiSearchableSelect(props) {
|
|
|
2169
2366
|
multiple: true,
|
|
2170
2367
|
id,
|
|
2171
2368
|
virtualized: isVirtualized,
|
|
2172
|
-
filter,
|
|
2369
|
+
filter: creatableProp ? (item, query) => {
|
|
2370
|
+
if (isCreatableSentinel(item)) return true;
|
|
2371
|
+
if (filter) return filter(item, query);
|
|
2372
|
+
if (isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
|
|
2373
|
+
return true;
|
|
2374
|
+
const label = itemToString(item);
|
|
2375
|
+
return label.toLowerCase().includes(query.toLowerCase());
|
|
2376
|
+
} : filter,
|
|
2377
|
+
inputValue: internalInputValue,
|
|
2378
|
+
onInputValueChange: (v, eventDetails) => {
|
|
2379
|
+
if (eventDetails.reason === "input-clear" && open) return;
|
|
2380
|
+
if (skipNextInputChangeRef.current) {
|
|
2381
|
+
skipNextInputChangeRef.current = false;
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
setInternalInputValue(v);
|
|
2385
|
+
},
|
|
2173
2386
|
open,
|
|
2174
2387
|
disabled: props.disabled,
|
|
2175
2388
|
onOpenChange: setOpen,
|
|
@@ -2192,34 +2405,43 @@ function MultiSearchableSelect(props) {
|
|
|
2192
2405
|
hasSentinels ? handleValueChange : handleSimpleValueChange
|
|
2193
2406
|
),
|
|
2194
2407
|
itemToStringLabel: (item) => {
|
|
2195
|
-
if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
|
|
2408
|
+
if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item) || isCreatableSentinel(item))
|
|
2196
2409
|
return "";
|
|
2197
2410
|
return itemToString(item);
|
|
2198
2411
|
},
|
|
2199
2412
|
isItemEqualToValue: (a, b) => {
|
|
2413
|
+
if (isCreatableSentinel(a) && isCreatableSentinel(b))
|
|
2414
|
+
return a.label === b.label;
|
|
2200
2415
|
if (isGroupHeaderSentinel3(a) && isGroupHeaderSentinel3(b))
|
|
2201
2416
|
return a.groupName === b.groupName;
|
|
2202
2417
|
if (isSelectAllSentinel3(a) && isSelectAllSentinel3(b)) return true;
|
|
2203
|
-
if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b))
|
|
2418
|
+
if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isCreatableSentinel(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b) && !isCreatableSentinel(b))
|
|
2204
2419
|
return a.id === b.id;
|
|
2205
2420
|
return false;
|
|
2206
2421
|
},
|
|
2207
2422
|
items: rootItems,
|
|
2208
2423
|
children: [
|
|
2209
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2424
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2425
|
+
SearchableSelectTokenTrigger,
|
|
2426
|
+
{
|
|
2427
|
+
id,
|
|
2428
|
+
"aria-describedby": ariaDescribedBy,
|
|
2429
|
+
children: [
|
|
2430
|
+
renderSelection ? renderSelection(value) : value.length > 0 ? (() => {
|
|
2431
|
+
const visibleItems = maxSelections != null ? value.slice(0, maxSelections) : value;
|
|
2432
|
+
const overflowCount = maxSelections != null && value.length > maxSelections ? value.length - maxSelections : 0;
|
|
2433
|
+
const text = visibleItems.map(
|
|
2434
|
+
(item) => customRenderSelections ? customRenderSelections(item) : itemToString(item)
|
|
2435
|
+
).join(", ");
|
|
2436
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(SelectionText2, { children: [
|
|
2437
|
+
text,
|
|
2438
|
+
overflowCount > 0 ? `, +${overflowCount}` : ""
|
|
2439
|
+
] });
|
|
2440
|
+
})() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SearchableSelectPlaceholder, { children: placeholder }),
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SearchableSelectTriggerIcon, { style: { marginLeft: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StyledChevron, { "data-chevron": true, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChevronIcon, {}) }) })
|
|
2442
|
+
]
|
|
2443
|
+
}
|
|
2444
|
+
),
|
|
2223
2445
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_combobox6.Combobox.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComboboxPositioner, { sideOffset: 4, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2224
2446
|
SearchableSelectPopup,
|
|
2225
2447
|
{
|
|
@@ -2256,37 +2478,55 @@ function MultiSearchableSelect(props) {
|
|
|
2256
2478
|
itemToString,
|
|
2257
2479
|
inputType,
|
|
2258
2480
|
renderItem,
|
|
2259
|
-
renderGroupHeading
|
|
2481
|
+
renderGroupHeading,
|
|
2482
|
+
renderCreatableItem: creatableProp ? (sentinel, style, index, measureRef) => renderCreatableItem(
|
|
2483
|
+
sentinel,
|
|
2484
|
+
style,
|
|
2485
|
+
index,
|
|
2486
|
+
measureRef
|
|
2487
|
+
) : void 0
|
|
2260
2488
|
}
|
|
2261
|
-
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) =>
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
}
|
|
2288
|
-
item
|
|
2289
|
-
|
|
2489
|
+
) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => {
|
|
2490
|
+
if (isCreatableSentinel(group)) {
|
|
2491
|
+
return renderCreatableItem(group);
|
|
2492
|
+
}
|
|
2493
|
+
const g = group;
|
|
2494
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(React11.Fragment, { children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ComboboxGroup, { items: g.items, children: [
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(g.value) : g.value }),
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_combobox6.Combobox.Collection, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2498
|
+
ComboboxItem_default,
|
|
2499
|
+
{
|
|
2500
|
+
value: item,
|
|
2501
|
+
itemId: String(item.id),
|
|
2502
|
+
label: itemToString(item),
|
|
2503
|
+
disabled: item.disabled,
|
|
2504
|
+
inputType,
|
|
2505
|
+
renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
|
|
2506
|
+
},
|
|
2507
|
+
item.id
|
|
2508
|
+
) })
|
|
2509
|
+
] }),
|
|
2510
|
+
index < groups.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComboboxSeparator, {})
|
|
2511
|
+
] }, g.value);
|
|
2512
|
+
} : (item) => {
|
|
2513
|
+
if (isCreatableSentinel(item)) {
|
|
2514
|
+
return renderCreatableItem(item);
|
|
2515
|
+
}
|
|
2516
|
+
const dataItem = item;
|
|
2517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2518
|
+
ComboboxItem_default,
|
|
2519
|
+
{
|
|
2520
|
+
value: dataItem,
|
|
2521
|
+
itemId: String(dataItem.id),
|
|
2522
|
+
label: itemToString(dataItem),
|
|
2523
|
+
disabled: dataItem.disabled,
|
|
2524
|
+
inputType,
|
|
2525
|
+
renderItem: renderItem ? () => renderItem(dataItem) : () => itemToString(dataItem)
|
|
2526
|
+
},
|
|
2527
|
+
dataItem.id
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2290
2530
|
}
|
|
2291
2531
|
)
|
|
2292
2532
|
]
|