@sproutsocial/seeds-react-menu 1.17.0 → 1.18.1

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.
Files changed (59) hide show
  1. package/.turbo/turbo-build.log +14 -22
  2. package/CHANGELOG.md +37 -0
  3. package/dist/esm/index.js +542 -229
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/v3/index.js +793 -1038
  6. package/dist/esm/v3/index.js.map +1 -1
  7. package/dist/v3/index.d.mts +64 -38
  8. package/dist/v3/index.d.ts +64 -38
  9. package/dist/v3/index.js +813 -1058
  10. package/dist/v3/index.js.map +1 -1
  11. package/dist/v3/menu.css +947 -0
  12. package/package.json +20 -21
  13. package/src/v3/ActionMenuStyles.tsx +169 -149
  14. package/src/v3/Common/ComboboxStyles.tsx +396 -511
  15. package/src/v3/Common/SelectIcons.tsx +17 -8
  16. package/src/v3/Common/SelectItem.tsx +25 -38
  17. package/src/v3/Common/SelectStyles.tsx +146 -144
  18. package/src/v3/Common/cn.ts +38 -0
  19. package/src/v3/MenuButtonStyles.tsx +55 -34
  20. package/src/v3/MultiSearchableSelect.tsx +8 -8
  21. package/src/v3/MultiSelect.tsx +11 -16
  22. package/src/v3/SingleSelect.tsx +13 -13
  23. package/src/v3/menu.css +947 -0
  24. package/tsup.config.ts +0 -1
  25. package/dist/esm/chunk-ROQATIB7.js +0 -357
  26. package/dist/esm/chunk-ROQATIB7.js.map +0 -1
  27. package/dist/esm/v2/index.js +0 -2089
  28. package/dist/esm/v2/index.js.map +0 -1
  29. package/dist/v2/index.d.mts +0 -108
  30. package/dist/v2/index.d.ts +0 -108
  31. package/dist/v2/index.js +0 -2280
  32. package/dist/v2/index.js.map +0 -1
  33. package/src/v2/Autocomplete/Autocomplete.stories.tsx +0 -645
  34. package/src/v2/Autocomplete/MultiAutocomplete.tsx +0 -487
  35. package/src/v2/Autocomplete/SingleAutocomplete.tsx +0 -366
  36. package/src/v2/Autocomplete/index.ts +0 -2
  37. package/src/v2/Common-V2/ComboboxContent.tsx +0 -457
  38. package/src/v2/Common-V2/MenuGroup.tsx +0 -60
  39. package/src/v2/Common-V2/MenuGroupTypes.ts +0 -30
  40. package/src/v2/Common-V2/MenuHeading.tsx +0 -83
  41. package/src/v2/Common-V2/MenuItem.tsx +0 -138
  42. package/src/v2/Common-V2/Popout.tsx +0 -124
  43. package/src/v2/Common-V2/PopoutTypes.tsx +0 -109
  44. package/src/v2/Common-V2/SelectToggleButton.tsx +0 -99
  45. package/src/v2/Common-V2/index.ts +0 -11
  46. package/src/v2/Common-V2/props.ts +0 -84
  47. package/src/v2/Common-V2/styles.tsx +0 -41
  48. package/src/v2/Common-V2/types.ts +0 -16
  49. package/src/v2/Common-V2/useHighlightedIndex.ts +0 -62
  50. package/src/v2/Common-V2/utils.ts +0 -238
  51. package/src/v2/SearchableSelect/AutocompleteContent.tsx +0 -325
  52. package/src/v2/SearchableSelect/SearchableMultiSelect.tsx +0 -527
  53. package/src/v2/SearchableSelect/SearchableSelect.tsx +0 -395
  54. package/src/v2/SearchableSelect/index.ts +0 -12
  55. package/src/v2/Select/MultiSelect.tsx +0 -417
  56. package/src/v2/Select/Select.stories.tsx +0 -593
  57. package/src/v2/Select/SingleSelect.tsx +0 -285
  58. package/src/v2/Select/index.ts +0 -2
  59. package/src/v2/index.ts +0 -2
@@ -1,593 +0,0 @@
1
- /* eslint-disable react-hooks/rules-of-hooks */
2
- import React from "react";
3
- import type { ReactNode } from "react";
4
- import type { Meta, StoryObj } from "@storybook/react";
5
- import { action } from "@storybook/addon-actions";
6
-
7
- import { Box } from "@sproutsocial/seeds-react-box";
8
- import { FormField } from "@sproutsocial/seeds-react-form-field";
9
- import { Icon } from "@sproutsocial/seeds-react-icon";
10
- import { Badge } from "@sproutsocial/seeds-react-badge";
11
-
12
- import { MultiSelect } from "./MultiSelect";
13
- import { SingleSelect } from "./SingleSelect";
14
- import Button from "@sproutsocial/seeds-react-button";
15
- import type { ToggleItem } from "../Common-V2";
16
-
17
- type CustomSelectArgs = React.ComponentProps<typeof MultiSelect>;
18
-
19
- const meta: Meta<CustomSelectArgs> = {
20
- title: "Components/Menus/v2/Select",
21
- component: MultiSelect,
22
- parameters: {
23
- controls: {
24
- expanded: true,
25
- },
26
- },
27
- decorators: [
28
- (Story) => (
29
- <Box display="flex" flexDirection="column" gap="16px" maxWidth={320}>
30
- <Story />
31
- </Box>
32
- ),
33
- ],
34
- };
35
-
36
- export default meta;
37
-
38
- type Story = StoryObj<CustomSelectArgs>;
39
-
40
- interface Book {
41
- id: string;
42
- author: string;
43
- title: string;
44
- }
45
-
46
- const books: Book[] = [
47
- { id: "book-1", author: "Harper Lee", title: "To Kill a Mockingbird" },
48
- { id: "book-2", author: "Lev Tolstoy", title: "War and Peace" },
49
- { id: "book-3", author: "Fyodor Dostoyevsy", title: "The Idiot" },
50
- { id: "book-4", author: "Oscar Wilde", title: "A Picture of Dorian Gray" },
51
- { id: "book-5", author: "George Orwell", title: "1984" },
52
- { id: "book-6", author: "Jane Austen", title: "Pride and Prejudice" },
53
- { id: "book-7", author: "Marcus Aurelius", title: "Meditations" },
54
- {
55
- id: "book-8",
56
- author: "Fyodor Dostoevsky",
57
- title: "The Brothers Karamazov",
58
- },
59
- { id: "book-9", author: "Lev Tolstoy", title: "Anna Karenina" },
60
- { id: "book-10", author: "Fyodor Dostoevsky", title: "Crime and Punishment" },
61
- ];
62
-
63
- // Generate 1000 books for virtualization testing
64
- const generateBooks = (count: number): Book[] => {
65
- const authors = [
66
- "Harper Lee",
67
- "Lev Tolstoy",
68
- "Fyodor Dostoevsky",
69
- "Oscar Wilde",
70
- "George Orwell",
71
- "Jane Austen",
72
- "Marcus Aurelius",
73
- "Ernest Hemingway",
74
- "Virginia Woolf",
75
- "James Joyce",
76
- "Charles Dickens",
77
- "Mark Twain",
78
- "J.R.R. Tolkien",
79
- "C.S. Lewis",
80
- "Agatha Christie",
81
- "Arthur Conan Doyle",
82
- "J.K. Rowling",
83
- "Stephen King",
84
- "Isaac Asimov",
85
- "Ray Bradbury",
86
- "Aldous Huxley",
87
- "Kurt Vonnegut",
88
- "J.D. Salinger",
89
- "Maya Angelou",
90
- "Toni Morrison",
91
- "Gabriel García Márquez",
92
- "Milan Kundera",
93
- "Umberto Eco",
94
- "Haruki Murakami",
95
- "Chimamanda Ngozi Adichie",
96
- ];
97
-
98
- const titleWords = [
99
- "Adventures",
100
- "Chronicles",
101
- "Memories",
102
- "Journey",
103
- "Quest",
104
- "Tales",
105
- "Stories",
106
- "Legends",
107
- "Myths",
108
- "Dreams",
109
- "Visions",
110
- "Revelations",
111
- "Secrets",
112
- "Mysteries",
113
- "Shadows",
114
- "Light",
115
- "Darkness",
116
- "Echoes",
117
- "Whispers",
118
- "Voices",
119
- "Songs",
120
- "Poems",
121
- ];
122
-
123
- const books: Book[] = [];
124
- for (let i = 0; i < count; i++) {
125
- const author = authors[i % authors.length];
126
- const titleWord1 =
127
- titleWords[Math.floor(Math.random() * titleWords.length)];
128
- const titleWord2 =
129
- titleWords[Math.floor(Math.random() * titleWords.length)];
130
- const number = Math.floor(i / authors.length) + 1;
131
- const title = `${titleWord1} of ${titleWord2} ${number}`;
132
-
133
- books.push({
134
- id: `book-${i + 1}`,
135
- author,
136
- title,
137
- });
138
- }
139
-
140
- return books;
141
- };
142
- const largeBookSet = generateBooks(1000);
143
-
144
- const renderItem = (item: Book) => (
145
- <>
146
- <span style={{ fontWeight: "bold" }}>{item.title}</span>
147
- {item.author && (
148
- <span style={{ fontSize: "12px", color: "#666", marginLeft: "8px" }}>
149
- by {item.author}
150
- </span>
151
- )}
152
- </>
153
- );
154
-
155
- const itemToString = (item: Book | null): string => {
156
- if (!item) return "";
157
- return item.title;
158
- };
159
-
160
- // 1. Single
161
- export const Single: Story = {
162
- name: "Single",
163
- render: () => {
164
- return (
165
- <FormField label="Pick a book">
166
- {({ id }) => (
167
- <SingleSelect
168
- id={id}
169
- data={books}
170
- itemToString={itemToString}
171
- renderItem={renderItem}
172
- onSelectedItemIdChange={(itemId) => console.log(itemId)}
173
- placeholder="Select a book"
174
- />
175
- )}
176
- </FormField>
177
- );
178
- },
179
- };
180
-
181
- // 3. Multi
182
- export const Multi: Story = {
183
- name: "Multi",
184
- render: () => {
185
- return (
186
- <div>
187
- <FormField label="Pick some books">
188
- {({ id }) => (
189
- <MultiSelect
190
- id={id}
191
- data={books}
192
- itemToString={itemToString}
193
- renderItem={renderItem}
194
- onSelectedItemIdsChange={(newitems) => console.log(newitems)}
195
- showSelectedItems={true}
196
- placeholder="Select books"
197
- />
198
- )}
199
- </FormField>
200
- <Button onClick={() => console.log("Button clicked")}>Submit</Button>
201
- </div>
202
- );
203
- },
204
- };
205
-
206
- // 4. Multi Grouped by Author
207
- const TestMultiGroupedByAuthor = () => {
208
- return (
209
- <FormField label="Pick some books">
210
- {({ id }) => (
211
- <SingleSelect
212
- id={id}
213
- data={books}
214
- itemToString={itemToString}
215
- renderItem={renderItem}
216
- groupBy={(book) => book.author}
217
- placeholder="Select books"
218
- isVirtualized={true}
219
- menuProps={{
220
- style: {
221
- maxHeight: "400px",
222
- overflow: "auto",
223
- },
224
- }}
225
- />
226
- )}
227
- </FormField>
228
- );
229
- };
230
-
231
- export const MultiGroupedByAuthor: Story = {
232
- name: "Multi Grouped by Author",
233
- render: () => {
234
- return <TestMultiGroupedByAuthor />;
235
- },
236
- };
237
-
238
- // 5. Multi Grouped by First Letter
239
- const TestMultiGroupedByFirstLetter = () => {
240
- return (
241
- <FormField label="Pick some books">
242
- {({ id }) => (
243
- <MultiSelect
244
- id={id}
245
- data={books}
246
- itemToString={itemToString}
247
- renderItem={renderItem}
248
- showSelectedItems={true}
249
- groupBy={(book) => {
250
- return book.title.charAt(0).toUpperCase();
251
- }}
252
- placeholder="Select books"
253
- />
254
- )}
255
- </FormField>
256
- );
257
- };
258
-
259
- export const MultiGroupedByFirstLetter: Story = {
260
- name: "Multi Grouped by First Letter",
261
- render: () => {
262
- return <TestMultiGroupedByFirstLetter />;
263
- },
264
- };
265
-
266
- // 6. Multi Grouped by Author with Custom Heading
267
- const TestMultiGroupedByAuthorWithCustomHeading = () => {
268
- return (
269
- <FormField label="Pick some books">
270
- {({ id }) => (
271
- <MultiSelect
272
- id={id}
273
- data={books}
274
- itemToString={itemToString}
275
- renderItem={renderItem}
276
- groupBy={(book) => book.author}
277
- renderGroupHeading={(label) => `📚 ${label.toUpperCase()}`}
278
- showSelectedItems={true}
279
- placeholder="Select books"
280
- />
281
- )}
282
- </FormField>
283
- );
284
- };
285
-
286
- export const MultiGroupedByAuthorWithCustomHeading: Story = {
287
- name: "Multi Grouped by Author with Custom Heading",
288
- render: () => {
289
- return <TestMultiGroupedByAuthorWithCustomHeading />;
290
- },
291
- };
292
-
293
- // 7. Multi with Select All
294
- const TestMultiWithSelectAll = () => {
295
- const selectAllItem: ToggleItem = {
296
- id: "select-all",
297
- label: "Select All",
298
- };
299
-
300
- return (
301
- <FormField label="Pick some books">
302
- {({ id }) => (
303
- <MultiSelect
304
- id={id}
305
- data={books}
306
- itemToString={itemToString}
307
- renderItem={renderItem}
308
- selectAllItem={selectAllItem}
309
- onSelectedItemIdsChange={(items) => console.log(items)}
310
- showSelectedItems={true}
311
- placeholder="Select books"
312
- />
313
- )}
314
- </FormField>
315
- );
316
- };
317
-
318
- export const MultiWithSelectAll: Story = {
319
- name: "Multi with Select All",
320
- render: () => {
321
- return <TestMultiWithSelectAll />;
322
- },
323
- };
324
-
325
- // 8. Virtualized Multi (with 1000 items)
326
- const TestVirtualizedMulti = () => {
327
- return (
328
- <FormField label="Pick some books">
329
- {({ id }) => (
330
- <MultiSelect
331
- id={id}
332
- data={largeBookSet}
333
- itemToString={itemToString}
334
- renderItem={renderItem}
335
- isVirtualized={true}
336
- placeholder="Select books"
337
- menuProps={{
338
- style: {
339
- maxHeight: "400px",
340
- overflow: "auto",
341
- },
342
- }}
343
- />
344
- )}
345
- </FormField>
346
- );
347
- };
348
-
349
- export const VirtualizedMulti: Story = {
350
- name: "Virtualized Multi (with 1000 items)",
351
- render: () => {
352
- return <TestVirtualizedMulti />;
353
- },
354
- };
355
-
356
- // 9. Virtualized Multi (with 1000 items), With Select All, Grouped by Author, with Selectable Headings
357
- const TestVirtualizedMultiWithSelectAllGrouped = () => {
358
- const selectAllItem: ToggleItem = {
359
- id: "select-all",
360
- label: "Select All",
361
- };
362
-
363
- return (
364
- <FormField label="Pick some books">
365
- {({ id }) => (
366
- <MultiSelect
367
- id={id}
368
- data={largeBookSet}
369
- itemToString={itemToString}
370
- renderItem={renderItem}
371
- groupBy={(book) => book.author}
372
- selectAllItem={selectAllItem}
373
- selectHeadings={true}
374
- isVirtualized={true}
375
- renderGroupHeading={(label) => `📚 ${label.toUpperCase()}`}
376
- onSelectedItemIdsChange={(items) => console.log(items)}
377
- showSelectedItems={true}
378
- placeholder="Select books"
379
- menuProps={{
380
- style: {
381
- maxHeight: "400px",
382
- overflow: "auto",
383
- },
384
- }}
385
- />
386
- )}
387
- </FormField>
388
- );
389
- };
390
-
391
- export const VirtualizedMultiWithSelectAllGrouped: Story = {
392
- name: "Virtualized Multi (with 1000 items), With Select All, Grouped by Author, with Selectable Headings",
393
- render: () => {
394
- return <TestVirtualizedMultiWithSelectAllGrouped />;
395
- },
396
- };
397
-
398
- // 10. Single with Selection rendering
399
- export const SingleWithSelectionRendering: Story = {
400
- name: "Single with Selection rendering",
401
- render: () => {
402
- return (
403
- <FormField label="Pick a book">
404
- {({ id }) => (
405
- <SingleSelect
406
- id={id}
407
- data={books}
408
- itemToString={itemToString}
409
- itemToSelection={renderItem}
410
- renderItem={renderItem}
411
- onSelectedItemIdChange={(itemId) => console.log(itemId)}
412
- placeholder="Select a book"
413
- />
414
- )}
415
- </FormField>
416
- );
417
- },
418
- };
419
-
420
- const customSelectionRenderer = (selectedItems: Book[]): ReactNode => {
421
- if (selectedItems.length === 0) {
422
- return "No books selected";
423
- }
424
- return selectedItems.map((item) => <Badge key={item.id}>{item.title}</Badge>);
425
- };
426
- // 11. Multi with Selection rendering
427
- export const MultiWithSelectionRendering: Story = {
428
- name: "Multi with Selection rendering",
429
- render: () => {
430
- return (
431
- <FormField label="Pick some books">
432
- {({ id }) => (
433
- <MultiSelect
434
- id={id}
435
- data={books}
436
- itemToString={itemToString}
437
- renderSelections={customSelectionRenderer}
438
- renderItem={renderItem}
439
- onSelectedItemIdsChange={(newitems) => console.log(newitems)}
440
- showSelectedItems={true}
441
- shouldOverflow
442
- placeholder="Select books"
443
- />
444
- )}
445
- </FormField>
446
- );
447
- },
448
- };
449
-
450
- // 12. FullyControlled Single
451
- const TestFullyControlledSingle = () => {
452
- const [selectedItemId, setSelectedItemId] = React.useState<string | null>(
453
- books[0].id
454
- );
455
- const [isOpen, setIsOpen] = React.useState(false);
456
-
457
- const selectedItem = books.find((book) => book.id === selectedItemId) ?? null;
458
-
459
- return (
460
- <>
461
- <FormField label="Pick a book">
462
- {({ id }) => (
463
- <SingleSelect
464
- id={id}
465
- data={books}
466
- itemToString={itemToString}
467
- selectedItemId={selectedItemId}
468
- onSelectedItemIdChange={(newItemId) => {
469
- console.log("Selected item changed:", newItemId);
470
- setSelectedItemId(newItemId);
471
- }}
472
- isOpen={isOpen}
473
- onIsOpenChange={(newIsOpen) => {
474
- console.log("isOpen changed:", newIsOpen);
475
- setIsOpen(newIsOpen);
476
- }}
477
- renderItem={renderItem}
478
- placeholder="Select a book"
479
- />
480
- )}
481
- </FormField>
482
- <div
483
- style={{
484
- marginTop: "16px",
485
- display: "flex",
486
- flexDirection: "column",
487
- gap: "8px",
488
- }}
489
- >
490
- <div>
491
- <Button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</Button>
492
- <Button
493
- onClick={() => {
494
- const randomBook =
495
- books[Math.floor(Math.random() * books.length)];
496
- setSelectedItemId(randomBook.id);
497
- }}
498
- style={{ marginLeft: "8px" }}
499
- >
500
- Select Random Book
501
- </Button>
502
- </div>
503
- <div style={{ fontSize: "12px", color: "#666" }}>
504
- Menu: {isOpen ? "OPEN" : "CLOSED"} | Selected:{" "}
505
- {selectedItem ? selectedItem.title : "None"}
506
- </div>
507
- </div>
508
- </>
509
- );
510
- };
511
-
512
- export const FullyControlledSingle: Story = {
513
- name: "FullyControlled Single",
514
- render: () => {
515
- return <TestFullyControlledSingle />;
516
- },
517
- };
518
-
519
- // 13. FullyControlled Multi
520
- const TestFullyControlledMulti = () => {
521
- const [selectedItemIds, setSelectedItemIds] = React.useState<string[]>([
522
- books[0].id,
523
- ]);
524
- const [isOpen, setIsOpen] = React.useState(false);
525
-
526
- const selectedItems = books.filter((book) =>
527
- selectedItemIds.includes(book.id)
528
- );
529
-
530
- return (
531
- <>
532
- <FormField label="Pick some books">
533
- {({ id }) => (
534
- <MultiSelect
535
- id={id}
536
- data={books}
537
- itemToString={itemToString}
538
- selectedItemIds={selectedItemIds}
539
- onSelectedItemIdsChange={(newItemIds) => {
540
- console.log("Selected items changed:", newItemIds);
541
- setSelectedItemIds(newItemIds);
542
- }}
543
- isOpen={isOpen}
544
- onIsOpenChange={(newIsOpen) => {
545
- console.log("isOpen changed:", newIsOpen);
546
- setIsOpen(newIsOpen);
547
- }}
548
- renderItem={renderItem}
549
- placeholder="Select books"
550
- />
551
- )}
552
- </FormField>
553
- <div
554
- style={{
555
- marginTop: "16px",
556
- display: "flex",
557
- flexDirection: "column",
558
- gap: "8px",
559
- }}
560
- >
561
- <div>
562
- <Button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</Button>
563
- <Button
564
- onClick={() => {
565
- const randomBook =
566
- books[Math.floor(Math.random() * books.length)];
567
- setSelectedItemIds((prev) => {
568
- if (!prev.includes(randomBook.id)) {
569
- return [...prev, randomBook.id];
570
- }
571
- return prev;
572
- });
573
- }}
574
- style={{ marginLeft: "8px" }}
575
- >
576
- Add Random Book
577
- </Button>
578
- </div>
579
- <div style={{ fontSize: "12px", color: "#666" }}>
580
- Menu: {isOpen ? "OPEN" : "CLOSED"} | Selected:{" "}
581
- {selectedItems.map((item) => item.title).join(", ") || "None"}
582
- </div>
583
- </div>
584
- </>
585
- );
586
- };
587
-
588
- export const FullyControlledMulti: Story = {
589
- name: "FullyControlled Multi",
590
- render: () => {
591
- return <TestFullyControlledMulti />;
592
- },
593
- };