@sproutsocial/seeds-react-menu 1.7.12 → 1.7.13
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 +6 -0
- package/dist/esm/v2/index.js +44 -26
- package/dist/esm/v2/index.js.map +1 -1
- package/dist/v2/index.js +44 -26
- package/dist/v2/index.js.map +1 -1
- package/package.json +1 -1
- package/src/v2/Autocomplete/MultiAutocomplete.tsx +16 -6
- package/src/v2/Autocomplete/SingleAutocomplete.tsx +23 -27
- package/src/v2/Common-V2/Popout.tsx +3 -1
- package/src/v2/Select/MultiSelect.tsx +7 -2
- package/src/v2/Select/SingleSelect.tsx +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useState, useMemo } from "react";
|
|
2
2
|
import { useCombobox, useMultipleSelection } from "downshift";
|
|
3
|
-
import { Label } from "@sproutsocial/seeds-react-label";
|
|
4
3
|
import { TokenInput } from "@sproutsocial/seeds-react-token-input";
|
|
5
4
|
// This will eventually be imported from seeds-react-popout
|
|
6
5
|
import { Popout } from "../Common-V2";
|
|
@@ -26,6 +25,14 @@ function defaultReducer(state, actionAndChanges) {
|
|
|
26
25
|
const { changes, type } = actionAndChanges;
|
|
27
26
|
|
|
28
27
|
switch (type) {
|
|
28
|
+
case useCombobox.stateChangeTypes.InputBlur:
|
|
29
|
+
// Do not close the menu on blur from this event
|
|
30
|
+
// There is a very brief blur when clicking an item, even though the input seemingly does not lose focus
|
|
31
|
+
// Closing the menu is handled in the onBlur handler instead, which does not have this issue
|
|
32
|
+
return {
|
|
33
|
+
...changes,
|
|
34
|
+
isOpen: true,
|
|
35
|
+
};
|
|
29
36
|
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
30
37
|
case useCombobox.stateChangeTypes.ItemClick:
|
|
31
38
|
// If the item is already selected, don't set highlighted index to 0
|
|
@@ -264,7 +271,6 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
264
271
|
// Select all items
|
|
265
272
|
updateSelectedItems([...data]);
|
|
266
273
|
}
|
|
267
|
-
updateInputValue("");
|
|
268
274
|
} else if (
|
|
269
275
|
selectHeadings &&
|
|
270
276
|
groupBy &&
|
|
@@ -292,7 +298,6 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
292
298
|
];
|
|
293
299
|
updateSelectedItems(newItems);
|
|
294
300
|
}
|
|
295
|
-
updateInputValue("");
|
|
296
301
|
} else if (
|
|
297
302
|
!isGroupHeading(newSelectedItem) &&
|
|
298
303
|
!isToggleItem(newSelectedItem)
|
|
@@ -308,7 +313,6 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
308
313
|
} else {
|
|
309
314
|
updateSelectedItems([...selectedItems, newSelectedItem as T]);
|
|
310
315
|
}
|
|
311
|
-
updateInputValue("");
|
|
312
316
|
}
|
|
313
317
|
}
|
|
314
318
|
break;
|
|
@@ -356,6 +360,8 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
356
360
|
if (inputProps.onBlur) {
|
|
357
361
|
inputProps.onBlur(e);
|
|
358
362
|
}
|
|
363
|
+
// Need to figure why onblur is getting called when selecting select all item
|
|
364
|
+
updateIsOpen(false);
|
|
359
365
|
};
|
|
360
366
|
|
|
361
367
|
const inputPropsToSpread = { ...inputProps };
|
|
@@ -399,7 +405,7 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
399
405
|
};
|
|
400
406
|
}
|
|
401
407
|
const dataItem = item as T;
|
|
402
|
-
// Find the index in itemsForCombobox
|
|
408
|
+
// Find the index in itemsForCombobox by comparing keys
|
|
403
409
|
const itemIndex = itemsForCombobox.indexOf(dataItem);
|
|
404
410
|
if (itemIndex === -1) {
|
|
405
411
|
return {};
|
|
@@ -424,7 +430,6 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
424
430
|
/>
|
|
425
431
|
}
|
|
426
432
|
>
|
|
427
|
-
{/* @ts-expect-error onClick types to not match tokeninput */}
|
|
428
433
|
<TokenInput
|
|
429
434
|
placeholder={placeholder}
|
|
430
435
|
inputProps={{
|
|
@@ -447,6 +452,11 @@ export function MultiAutocomplete<T extends DataWithId>({
|
|
|
447
452
|
e.stopPropagation();
|
|
448
453
|
}}
|
|
449
454
|
onBlur={handleBlur}
|
|
455
|
+
aria-controls={undefined}
|
|
456
|
+
aria-expanded={undefined}
|
|
457
|
+
aria-haspopup={undefined}
|
|
458
|
+
// @ts-expect-error Popout adds the type=button prop but TokenInput does not expect that
|
|
459
|
+
type={undefined}
|
|
450
460
|
/>
|
|
451
461
|
</Popout>
|
|
452
462
|
);
|
|
@@ -7,6 +7,7 @@ import { Input } from "@sproutsocial/seeds-react-input";
|
|
|
7
7
|
import { Popout } from "../Common-V2";
|
|
8
8
|
import {
|
|
9
9
|
isGroupHeading,
|
|
10
|
+
isToggleItem,
|
|
10
11
|
menuAnimationConfig,
|
|
11
12
|
getFilteredItems,
|
|
12
13
|
groupItemsWithHeadings,
|
|
@@ -96,19 +97,22 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
96
97
|
const isIsOpenControlled = controlledIsOpen !== undefined;
|
|
97
98
|
const isOpen = isIsOpenControlled ? controlledIsOpen : uncontrolledIsOpen;
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
-
// Sync inputValue with selectedItem when selectedItem changes externally and inputValue is not controlled
|
|
100
|
+
// Sync inputValue with selectedItem when selectedItem changes externally and menu is closed
|
|
101
101
|
useEffect(() => {
|
|
102
102
|
if (selectedItem && !isOpen) {
|
|
103
103
|
// Only update if menu is closed - when open, user might be typing
|
|
104
104
|
const selectedItemString = itemToString(selectedItem);
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
uncontrolledInputValue !== selectedItemString
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else
|
|
111
|
-
|
|
105
|
+
if (!isInputValueControlled) {
|
|
106
|
+
// For uncontrolled input, update internal state
|
|
107
|
+
if (uncontrolledInputValue !== selectedItemString) {
|
|
108
|
+
setUncontrolledInputValue(selectedItemString);
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
// For controlled input, notify parent if value doesn't match
|
|
112
|
+
// Parent is responsible for updating the controlled inputValue
|
|
113
|
+
if (inputValue !== selectedItemString) {
|
|
114
|
+
onInputValueChange?.(selectedItemString);
|
|
115
|
+
}
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
}, [
|
|
@@ -117,6 +121,8 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
117
121
|
isInputValueControlled,
|
|
118
122
|
itemToString,
|
|
119
123
|
uncontrolledInputValue,
|
|
124
|
+
inputValue,
|
|
125
|
+
onInputValueChange,
|
|
120
126
|
]);
|
|
121
127
|
|
|
122
128
|
const updateSelectedItem = (newSelectedItem: T | null) => {
|
|
@@ -218,10 +224,7 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
218
224
|
isOpen: newIsOpen,
|
|
219
225
|
}) {
|
|
220
226
|
// Handle isOpen changes for any state change type
|
|
221
|
-
if (
|
|
222
|
-
newIsOpen !== undefined &&
|
|
223
|
-
type !== useCombobox.stateChangeTypes.InputBlur
|
|
224
|
-
) {
|
|
227
|
+
if (newIsOpen !== undefined) {
|
|
225
228
|
updateIsOpen(newIsOpen);
|
|
226
229
|
}
|
|
227
230
|
|
|
@@ -284,15 +287,6 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
284
287
|
}
|
|
285
288
|
const { ref, id: inputId, ...restProps } = inputProps as any;
|
|
286
289
|
|
|
287
|
-
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
|
288
|
-
// Call any provided onBlur handler
|
|
289
|
-
if (inputProps.onBlur) {
|
|
290
|
-
inputProps.onBlur(e);
|
|
291
|
-
}
|
|
292
|
-
// Close the menu on blur
|
|
293
|
-
updateIsOpen(false);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
290
|
return (
|
|
297
291
|
<Popout
|
|
298
292
|
open={comboboxIsOpen}
|
|
@@ -314,8 +308,13 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
314
308
|
// Only apply getItemProps to actual items, not headings or ToggleItems
|
|
315
309
|
const item = items[props.index];
|
|
316
310
|
const dataItem = item as T;
|
|
317
|
-
// Find the index in itemsForCombobox
|
|
318
|
-
const itemIndex = itemsForCombobox.
|
|
311
|
+
// Find the index in itemsForCombobox using itemToKey for safer comparison
|
|
312
|
+
const itemIndex = itemsForCombobox.findIndex((comboboxItem) => {
|
|
313
|
+
if (isToggleItem(comboboxItem)) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
return itemToKey(comboboxItem as T) === itemToKey(dataItem);
|
|
317
|
+
});
|
|
319
318
|
if (itemIndex === -1) {
|
|
320
319
|
return {};
|
|
321
320
|
}
|
|
@@ -340,9 +339,7 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
340
339
|
<Input
|
|
341
340
|
id={id}
|
|
342
341
|
inputProps={{
|
|
343
|
-
...restProps,
|
|
344
342
|
autoComplete: "off",
|
|
345
|
-
onBlur: handleBlur,
|
|
346
343
|
}}
|
|
347
344
|
innerRef={ref}
|
|
348
345
|
placeholder={placeholder}
|
|
@@ -355,7 +352,6 @@ export function SingleAutocomplete<T extends DataWithId>({
|
|
|
355
352
|
updateInputValue("");
|
|
356
353
|
}}
|
|
357
354
|
{...restProps}
|
|
358
|
-
onBlur={handleBlur}
|
|
359
355
|
/>
|
|
360
356
|
</Popout>
|
|
361
357
|
);
|
|
@@ -78,7 +78,9 @@ export function Popout({
|
|
|
78
78
|
<Popover.Trigger ref={radixRef} asChild>
|
|
79
79
|
{children}
|
|
80
80
|
</Popover.Trigger>
|
|
81
|
-
<Popover.Anchor
|
|
81
|
+
<Popover.Anchor>
|
|
82
|
+
<div style={{ height: "1px" }}> </div>
|
|
83
|
+
</Popover.Anchor>
|
|
82
84
|
<Popover.Portal>
|
|
83
85
|
<StyledContent
|
|
84
86
|
side={side}
|
|
@@ -367,8 +367,13 @@ export function MultiSelect<T extends DataWithId>({
|
|
|
367
367
|
};
|
|
368
368
|
}
|
|
369
369
|
const dataItem = item as T;
|
|
370
|
-
// Find the index in itemsForSelect
|
|
371
|
-
const itemIndex = itemsForSelect.
|
|
370
|
+
// Find the index in itemsForSelect using itemToKey for safer comparison
|
|
371
|
+
const itemIndex = itemsForSelect.findIndex((selectItem) => {
|
|
372
|
+
if (isGroupHeading(selectItem) || isToggleItem(selectItem)) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
return itemToKey(selectItem as T) === itemToKey(dataItem);
|
|
376
|
+
});
|
|
372
377
|
if (itemIndex === -1) {
|
|
373
378
|
return {};
|
|
374
379
|
}
|
|
@@ -239,8 +239,13 @@ export function SingleSelect<T extends DataWithId>({
|
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
241
|
const dataItem = item as T;
|
|
242
|
-
// Find the index in itemsForSelect
|
|
243
|
-
const itemIndex = itemsForSelect.
|
|
242
|
+
// Find the index in itemsForSelect using itemToKey for safer comparison
|
|
243
|
+
const itemIndex = itemsForSelect.findIndex((selectItem) => {
|
|
244
|
+
if (isToggleItem(selectItem)) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return itemToKey(selectItem as T) === itemToKey(dataItem);
|
|
248
|
+
});
|
|
244
249
|
if (itemIndex === -1) {
|
|
245
250
|
return {};
|
|
246
251
|
}
|