@sproutsocial/seeds-react-menu 1.12.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +16 -16
- package/CHANGELOG.md +19 -0
- package/dist/esm/v3/index.js +212 -63
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/v3/index.d.mts +13 -1
- package/dist/v3/index.d.ts +13 -1
- package/dist/v3/index.js +212 -63
- 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 +85 -1
- package/src/v3/MultiCombobox.tsx +238 -62
- package/src/v3/MultiSearchableSelect.tsx +6 -1
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-menu",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "Seeds React Menu",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,17 +37,17 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@radix-ui/react-popover": "^1.1.2",
|
|
39
39
|
"@sproutsocial/seeds-react-box": "^1.1.16",
|
|
40
|
-
"@sproutsocial/seeds-react-button": "^2.0.
|
|
41
|
-
"@sproutsocial/seeds-react-checkbox": "^1.3.
|
|
42
|
-
"@sproutsocial/seeds-react-icon": "^2.3.
|
|
43
|
-
"@sproutsocial/seeds-react-input": "^1.5.
|
|
40
|
+
"@sproutsocial/seeds-react-button": "^2.0.5",
|
|
41
|
+
"@sproutsocial/seeds-react-checkbox": "^1.3.33",
|
|
42
|
+
"@sproutsocial/seeds-react-icon": "^2.3.7",
|
|
43
|
+
"@sproutsocial/seeds-react-input": "^1.5.16",
|
|
44
44
|
"@sproutsocial/seeds-react-label": "^1.0.16",
|
|
45
|
-
"@sproutsocial/seeds-react-popout": "^2.4.
|
|
45
|
+
"@sproutsocial/seeds-react-popout": "^2.4.38",
|
|
46
46
|
"@sproutsocial/seeds-react-radio": "^1.3.16",
|
|
47
|
-
"@sproutsocial/seeds-react-switch": "^1.2.
|
|
47
|
+
"@sproutsocial/seeds-react-switch": "^1.2.32",
|
|
48
48
|
"@sproutsocial/seeds-react-system-props": "^3.0.2",
|
|
49
49
|
"@sproutsocial/seeds-react-theme": "^3.6.2",
|
|
50
|
-
"@sproutsocial/seeds-react-token-input": "^1.4.
|
|
50
|
+
"@sproutsocial/seeds-react-token-input": "^1.4.38",
|
|
51
51
|
"@sproutsocial/seeds-react-utilities": "^4.3.0",
|
|
52
52
|
"@base-ui/react": "^1.3.0",
|
|
53
53
|
"@tanstack/react-virtual": "^3.13.12",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"styled-system": "^5.1.5"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@sproutsocial/seeds-react-tooltip": "^1.1.
|
|
60
|
+
"@sproutsocial/seeds-react-tooltip": "^1.1.20",
|
|
61
61
|
"@sproutsocial/eslint-config-seeds": "*",
|
|
62
62
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
63
63
|
"@sproutsocial/seeds-testing": "*",
|
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
ComboboxSelectAllItem,
|
|
9
9
|
} from "./ComboboxStyles";
|
|
10
10
|
import type { DataWithId } from "./types";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
isPlaceholderSentinel,
|
|
13
|
+
isCreatableSentinel,
|
|
14
|
+
type CreatableSentinel,
|
|
15
|
+
} from "./sentinels";
|
|
12
16
|
|
|
13
17
|
export const ITEM_HEIGHT = 32;
|
|
14
18
|
|
|
@@ -29,7 +33,11 @@ export function isSelectAllSentinel(v: unknown): v is SelectAllSentinel {
|
|
|
29
33
|
return typeof v === "object" && v !== null && "__selectAll" in v;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
export type FlatSentinelRow<T> =
|
|
36
|
+
export type FlatSentinelRow<T> =
|
|
37
|
+
| T
|
|
38
|
+
| GroupHeaderSentinel
|
|
39
|
+
| SelectAllSentinel
|
|
40
|
+
| CreatableSentinel;
|
|
33
41
|
|
|
34
42
|
interface VirtualizedComboboxListProps<T extends DataWithId> {
|
|
35
43
|
open: boolean;
|
|
@@ -47,6 +55,12 @@ interface VirtualizedComboboxListProps<T extends DataWithId> {
|
|
|
47
55
|
inputType: "icon" | "radio" | "checkbox" | "none";
|
|
48
56
|
renderItem?: (item: T) => React.ReactNode;
|
|
49
57
|
renderGroupHeading?: (label: string) => React.ReactNode;
|
|
58
|
+
renderCreatableItem?: (
|
|
59
|
+
sentinel: CreatableSentinel,
|
|
60
|
+
style: React.CSSProperties,
|
|
61
|
+
index: number,
|
|
62
|
+
measureRef: (el: Element | null) => void
|
|
63
|
+
) => React.ReactNode;
|
|
50
64
|
}
|
|
51
65
|
|
|
52
66
|
export default function VirtualizedComboboxList<T extends DataWithId>({
|
|
@@ -59,6 +73,7 @@ export default function VirtualizedComboboxList<T extends DataWithId>({
|
|
|
59
73
|
inputType,
|
|
60
74
|
renderItem,
|
|
61
75
|
renderGroupHeading,
|
|
76
|
+
renderCreatableItem,
|
|
62
77
|
}: VirtualizedComboboxListProps<T>) {
|
|
63
78
|
type Row = FlatSentinelRow<T>;
|
|
64
79
|
|
|
@@ -197,6 +212,18 @@ export default function VirtualizedComboboxList<T extends DataWithId>({
|
|
|
197
212
|
);
|
|
198
213
|
}
|
|
199
214
|
|
|
215
|
+
if (isCreatableSentinel(row)) {
|
|
216
|
+
if (renderCreatableItem) {
|
|
217
|
+
return renderCreatableItem(
|
|
218
|
+
row,
|
|
219
|
+
baseStyle,
|
|
220
|
+
virtualItem.index,
|
|
221
|
+
virtualizer.measureElement
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
200
227
|
const item = row as T;
|
|
201
228
|
return (
|
|
202
229
|
<ComboboxItem
|
|
@@ -15,3 +15,16 @@ export function makePlaceholderSentinel(label: string): PlaceholderSentinel {
|
|
|
15
15
|
export function isPlaceholderSentinel(v: unknown): v is PlaceholderSentinel {
|
|
16
16
|
return typeof v === "object" && v !== null && "__placeholder" in v;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export interface CreatableSentinel {
|
|
20
|
+
__creatable: true;
|
|
21
|
+
label: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function makeCreatableSentinel(label: string): CreatableSentinel {
|
|
25
|
+
return { __creatable: true, label };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isCreatableSentinel(v: unknown): v is CreatableSentinel {
|
|
29
|
+
return typeof v === "object" && v !== null && "__creatable" in v;
|
|
30
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
largeBookSet,
|
|
9
9
|
itemToString,
|
|
10
10
|
renderItem,
|
|
11
|
+
type Book,
|
|
11
12
|
} from "./storyData";
|
|
12
13
|
|
|
13
14
|
const meta: Meta<typeof MultiCombobox> = {
|
|
@@ -99,6 +100,7 @@ export const Controlled: Story = {
|
|
|
99
100
|
name: "Controlled",
|
|
100
101
|
render: () => {
|
|
101
102
|
const [selectedIds, setSelectedIds] = React.useState<string[]>(["book-2"]);
|
|
103
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
102
104
|
const selected = books.filter((b) => selectedIds.includes(b.id));
|
|
103
105
|
return (
|
|
104
106
|
<div
|
|
@@ -119,9 +121,14 @@ export const Controlled: Story = {
|
|
|
119
121
|
placeholder="Search books..."
|
|
120
122
|
selectedItemIds={selectedIds}
|
|
121
123
|
onSelectedItemIdsChange={(ids: string[]) => setSelectedIds(ids)}
|
|
124
|
+
inputValue={inputValue}
|
|
125
|
+
onInputValueChange={setInputValue}
|
|
122
126
|
/>
|
|
123
127
|
)}
|
|
124
128
|
</FormField>
|
|
129
|
+
<div style={{ fontSize: 13 }}>
|
|
130
|
+
Input: <strong>"{inputValue}"</strong>
|
|
131
|
+
</div>
|
|
125
132
|
<div style={{ fontSize: 13 }}>
|
|
126
133
|
Selected:{" "}
|
|
127
134
|
<strong>
|
|
@@ -130,11 +137,14 @@ export const Controlled: Story = {
|
|
|
130
137
|
: "none"}
|
|
131
138
|
</strong>
|
|
132
139
|
</div>
|
|
133
|
-
<div style={{ display: "flex", gap: 8 }}>
|
|
140
|
+
<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
|
|
134
141
|
<button onClick={() => setSelectedIds([])}>Clear all</button>
|
|
135
142
|
<button onClick={() => setSelectedIds(["book-5", "book-7"])}>
|
|
136
143
|
Select 1984 & Meditations
|
|
137
144
|
</button>
|
|
145
|
+
<button onClick={() => setInputValue("War")}>
|
|
146
|
+
Set input to "War"
|
|
147
|
+
</button>
|
|
138
148
|
</div>
|
|
139
149
|
</div>
|
|
140
150
|
);
|
|
@@ -182,6 +192,27 @@ export const SelectableHeadings: Story = {
|
|
|
182
192
|
|
|
183
193
|
export const SelectAll: Story = {
|
|
184
194
|
name: "Select all",
|
|
195
|
+
render: () => (
|
|
196
|
+
<div style={{ width: "600px" }}>
|
|
197
|
+
<FormField label="Pick books">
|
|
198
|
+
{({ id }) => (
|
|
199
|
+
<MultiCombobox
|
|
200
|
+
id={id}
|
|
201
|
+
data={books}
|
|
202
|
+
itemToString={itemToString}
|
|
203
|
+
renderItem={renderItem}
|
|
204
|
+
placeholder="Search books..."
|
|
205
|
+
selectHeadings
|
|
206
|
+
selectAll
|
|
207
|
+
/>
|
|
208
|
+
)}
|
|
209
|
+
</FormField>
|
|
210
|
+
</div>
|
|
211
|
+
),
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const SelectAllWithGrouping: Story = {
|
|
215
|
+
name: "Select all with grouping",
|
|
185
216
|
render: () => (
|
|
186
217
|
<div style={{ width: "600px" }}>
|
|
187
218
|
<FormField label="Pick books">
|
|
@@ -202,6 +233,26 @@ export const SelectAll: Story = {
|
|
|
202
233
|
),
|
|
203
234
|
};
|
|
204
235
|
|
|
236
|
+
export const SelectAllLocalized: Story = {
|
|
237
|
+
name: "Select all (localized label)",
|
|
238
|
+
render: () => (
|
|
239
|
+
<div style={{ width: "600px" }}>
|
|
240
|
+
<FormField label="Pick books">
|
|
241
|
+
{({ id }) => (
|
|
242
|
+
<MultiCombobox
|
|
243
|
+
id={id}
|
|
244
|
+
data={books}
|
|
245
|
+
itemToString={itemToString}
|
|
246
|
+
renderItem={renderItem}
|
|
247
|
+
placeholder="Search books..."
|
|
248
|
+
selectAll="Tout sélectionner"
|
|
249
|
+
/>
|
|
250
|
+
)}
|
|
251
|
+
</FormField>
|
|
252
|
+
</div>
|
|
253
|
+
),
|
|
254
|
+
};
|
|
255
|
+
|
|
205
256
|
export const Virtualized: Story = {
|
|
206
257
|
name: "Virtualized",
|
|
207
258
|
render: () => (
|
|
@@ -371,3 +422,36 @@ export const CustomSearch: Story = {
|
|
|
371
422
|
</div>
|
|
372
423
|
),
|
|
373
424
|
};
|
|
425
|
+
|
|
426
|
+
export const Creatable: Story = {
|
|
427
|
+
name: "Creatable",
|
|
428
|
+
render: () => {
|
|
429
|
+
const [bookList, setBookList] = React.useState<Book[]>(books);
|
|
430
|
+
|
|
431
|
+
return (
|
|
432
|
+
<div style={{ width: "300px" }}>
|
|
433
|
+
<FormField label="Pick or create books">
|
|
434
|
+
{({ id }) => (
|
|
435
|
+
<MultiCombobox
|
|
436
|
+
id={id}
|
|
437
|
+
data={bookList}
|
|
438
|
+
itemToString={itemToString}
|
|
439
|
+
placeholder="Search or create..."
|
|
440
|
+
isVirtualized
|
|
441
|
+
creatable
|
|
442
|
+
onCreate={(value) => {
|
|
443
|
+
const newBook: Book = {
|
|
444
|
+
id: `book-custom-${value.toLowerCase().replace(/\s+/g, "-")}`,
|
|
445
|
+
title: value,
|
|
446
|
+
author: "Unknown",
|
|
447
|
+
};
|
|
448
|
+
setBookList((prev) => [...prev, newBook]);
|
|
449
|
+
return newBook;
|
|
450
|
+
}}
|
|
451
|
+
/>
|
|
452
|
+
)}
|
|
453
|
+
</FormField>
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
},
|
|
457
|
+
};
|