@sproutsocial/seeds-react-menu 1.12.2 → 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.
@@ -262,6 +262,8 @@ interface MultiSearchableSelectDataProps<T extends DataWithId> extends MultiSear
262
262
  selectAll?: boolean;
263
263
  isVirtualized?: boolean;
264
264
  removeSelectedItems?: boolean;
265
+ creatable?: boolean;
266
+ onCreate?: (value: string) => T;
265
267
  children?: never;
266
268
  }
267
269
  interface MultiSearchableSelectChildrenProps extends MultiSearchableSelectBaseProps {
@@ -282,6 +284,8 @@ interface MultiSearchableSelectChildrenProps extends MultiSearchableSelectBasePr
282
284
  selectAll?: never;
283
285
  isVirtualized?: never;
284
286
  removeSelectedItems?: never;
287
+ creatable?: never;
288
+ onCreate?: never;
285
289
  }
286
290
  type MultiSearchableSelectProps<T extends DataWithId> = MultiSearchableSelectDataProps<T> | MultiSearchableSelectChildrenProps;
287
291
  declare function MultiSearchableSelect<T extends DataWithId>(props: MultiSearchableSelectProps<T>): react_jsx_runtime.JSX.Element;
@@ -262,6 +262,8 @@ interface MultiSearchableSelectDataProps<T extends DataWithId> extends MultiSear
262
262
  selectAll?: boolean;
263
263
  isVirtualized?: boolean;
264
264
  removeSelectedItems?: boolean;
265
+ creatable?: boolean;
266
+ onCreate?: (value: string) => T;
265
267
  children?: never;
266
268
  }
267
269
  interface MultiSearchableSelectChildrenProps extends MultiSearchableSelectBaseProps {
@@ -282,6 +284,8 @@ interface MultiSearchableSelectChildrenProps extends MultiSearchableSelectBasePr
282
284
  selectAll?: never;
283
285
  isVirtualized?: never;
284
286
  removeSelectedItems?: never;
287
+ creatable?: never;
288
+ onCreate?: never;
285
289
  }
286
290
  type MultiSearchableSelectProps<T extends DataWithId> = MultiSearchableSelectDataProps<T> | MultiSearchableSelectChildrenProps;
287
291
  declare function MultiSearchableSelect<T extends DataWithId>(props: MultiSearchableSelectProps<T>): react_jsx_runtime.JSX.Element;
package/dist/v3/index.js CHANGED
@@ -1765,15 +1765,23 @@ function MultiCombobox(props) {
1765
1765
  const label = itemToString(item);
1766
1766
  return label.toLowerCase().includes(query.toLowerCase());
1767
1767
  } : filter,
1768
- inputValue: creatableProp ? currentInputValue : inputValue,
1769
- onInputValueChange: creatableProp ? (v) => {
1770
- if (skipNextInputChangeRef.current) {
1771
- skipNextInputChangeRef.current = false;
1772
- return;
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);
1773
1783
  }
1774
- setInternalInputValue(v);
1775
- onInputValueChange?.(v);
1776
- } : onInputValueChange,
1784
+ },
1777
1785
  onItemHighlighted: isVirtualized ? (item, { reason, index }) => {
1778
1786
  const virt = virtualizerRef.current;
1779
1787
  if (!item || !virt) return;
@@ -2122,6 +2130,7 @@ function SingleSearchableSelect(props) {
2122
2130
  var React11 = __toESM(require("react"));
2123
2131
  var import_combobox6 = require("@base-ui/react/combobox");
2124
2132
  var import_react_virtual5 = require("@tanstack/react-virtual");
2133
+ var import_seeds_react_icon4 = __toESM(require("@sproutsocial/seeds-react-icon"));
2125
2134
  var import_styled_components7 = __toESM(require("styled-components"));
2126
2135
  var import_jsx_runtime11 = require("react/jsx-runtime");
2127
2136
  var SelectionText2 = import_styled_components7.default.span`
@@ -2165,12 +2174,16 @@ function MultiSearchableSelect(props) {
2165
2174
  const selectHeadings = isChildrenMode ? false : props.selectHeadings ?? false;
2166
2175
  const selectAll = isChildrenMode ? false : props.selectAll ?? false;
2167
2176
  const isVirtualized = isChildrenMode ? false : props.isVirtualized ?? false;
2177
+ const creatableProp = isChildrenMode ? false : props.creatable ?? false;
2178
+ const onCreateProp = isChildrenMode ? void 0 : props.onCreate;
2168
2179
  const children = isChildrenMode ? props.children : void 0;
2169
2180
  const selectedItemIds = props.selectedItemIds;
2170
2181
  const defaultSelectedItemIds = props.defaultSelectedItemIds;
2171
2182
  const onSelectedItemIdsChange = props.onSelectedItemIdsChange;
2172
2183
  const { containerRef, portalContainer } = useSeedsPortalContainer();
2173
2184
  const [open, setOpen] = React11.useState(false);
2185
+ const [internalInputValue, setInternalInputValue] = React11.useState("");
2186
+ const skipNextInputChangeRef = React11.useRef(false);
2174
2187
  const virtualizerRef = React11.useRef(null);
2175
2188
  const isControlled = selectedItemIds !== void 0;
2176
2189
  const idsToItems = React11.useCallback(
@@ -2189,6 +2202,18 @@ function MultiSearchableSelect(props) {
2189
2202
  () => groupBy ? groupItems(visibleData, groupBy) : null,
2190
2203
  [visibleData, groupBy]
2191
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]);
2192
2217
  const flatItems = React11.useMemo(() => {
2193
2218
  if (!groups || !selectHeadings && !selectAll) return null;
2194
2219
  const rows = [];
@@ -2205,10 +2230,21 @@ function MultiSearchableSelect(props) {
2205
2230
  onSelectedItemIdsChange(items.map((item) => item.id));
2206
2231
  }
2207
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
+ }
2208
2244
  const selectAllClicked = next.filter(isSelectAllSentinel3);
2209
2245
  const groupSentinels = next.filter(isGroupHeaderSentinel3);
2210
2246
  const realItems = next.filter(
2211
- (v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v)
2247
+ (v) => !isGroupHeaderSentinel3(v) && !isSelectAllSentinel3(v) && !isCreatableSentinel(v)
2212
2248
  );
2213
2249
  if (selectAllClicked.length > 0) {
2214
2250
  const allSelected = data.every(
@@ -2242,6 +2278,15 @@ function MultiSearchableSelect(props) {
2242
2278
  setSelectedItems(updated);
2243
2279
  }
2244
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
+ }
2245
2290
  setSelectedItems(newItems);
2246
2291
  }
2247
2292
  function renderSentinelRow(row) {
@@ -2272,6 +2317,9 @@ function MultiSearchableSelect(props) {
2272
2317
  renderGroupHeading ? renderGroupHeading(row.groupName) : row.groupName
2273
2318
  ] }, `__header-${row.groupName}`);
2274
2319
  }
2320
+ if (isCreatableSentinel(row)) {
2321
+ return renderCreatableItem(row);
2322
+ }
2275
2323
  const item = row;
2276
2324
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2277
2325
  ComboboxItem_default,
@@ -2286,7 +2334,28 @@ function MultiSearchableSelect(props) {
2286
2334
  item.id
2287
2335
  );
2288
2336
  }
2289
- const rootItems = flatItems ?? groups ?? (isChildrenMode ? void 0 : visibleData);
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;
2290
2359
  const hasSentinels = selectAll || selectHeadings;
2291
2360
  const rootValue = hasSentinels ? [...value] : value;
2292
2361
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Field, { children: [
@@ -2297,7 +2366,23 @@ function MultiSearchableSelect(props) {
2297
2366
  multiple: true,
2298
2367
  id,
2299
2368
  virtualized: isVirtualized,
2300
- 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
+ },
2301
2386
  open,
2302
2387
  disabled: props.disabled,
2303
2388
  onOpenChange: setOpen,
@@ -2320,15 +2405,17 @@ function MultiSearchableSelect(props) {
2320
2405
  hasSentinels ? handleValueChange : handleSimpleValueChange
2321
2406
  ),
2322
2407
  itemToStringLabel: (item) => {
2323
- if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item))
2408
+ if (!item || isGroupHeaderSentinel3(item) || isSelectAllSentinel3(item) || isCreatableSentinel(item))
2324
2409
  return "";
2325
2410
  return itemToString(item);
2326
2411
  },
2327
2412
  isItemEqualToValue: (a, b) => {
2413
+ if (isCreatableSentinel(a) && isCreatableSentinel(b))
2414
+ return a.label === b.label;
2328
2415
  if (isGroupHeaderSentinel3(a) && isGroupHeaderSentinel3(b))
2329
2416
  return a.groupName === b.groupName;
2330
2417
  if (isSelectAllSentinel3(a) && isSelectAllSentinel3(b)) return true;
2331
- if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b))
2418
+ if (!isGroupHeaderSentinel3(a) && !isSelectAllSentinel3(a) && !isCreatableSentinel(a) && !isGroupHeaderSentinel3(b) && !isSelectAllSentinel3(b) && !isCreatableSentinel(b))
2332
2419
  return a.id === b.id;
2333
2420
  return false;
2334
2421
  },
@@ -2391,37 +2478,55 @@ function MultiSearchableSelect(props) {
2391
2478
  itemToString,
2392
2479
  inputType,
2393
2480
  renderItem,
2394
- renderGroupHeading
2481
+ renderGroupHeading,
2482
+ renderCreatableItem: creatableProp ? (sentinel, style, index, measureRef) => renderCreatableItem(
2483
+ sentinel,
2484
+ style,
2485
+ index,
2486
+ measureRef
2487
+ ) : void 0
2395
2488
  }
2396
- ) : children ? children : flatItems ? (row) => renderSentinelRow(row) : groups ? (group, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(React11.Fragment, { children: [
2397
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(ComboboxGroup, { items: group.items, children: [
2398
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComboboxGroupLabel, { children: renderGroupHeading ? renderGroupHeading(group.value) : group.value }),
2399
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_combobox6.Combobox.Collection, { children: (item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2400
- ComboboxItem_default,
2401
- {
2402
- value: item,
2403
- itemId: String(item.id),
2404
- label: itemToString(item),
2405
- disabled: item.disabled,
2406
- inputType,
2407
- renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
2408
- },
2409
- item.id
2410
- ) })
2411
- ] }),
2412
- index < groups.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ComboboxSeparator, {})
2413
- ] }, group.value) : (item) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2414
- ComboboxItem_default,
2415
- {
2416
- value: item,
2417
- itemId: String(item.id),
2418
- label: itemToString(item),
2419
- disabled: item.disabled,
2420
- inputType,
2421
- renderItem: renderItem ? () => renderItem(item) : () => itemToString(item)
2422
- },
2423
- item.id
2424
- )
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
+ }
2425
2530
  }
2426
2531
  )
2427
2532
  ]