@sproutsocial/seeds-react-menu 1.10.8 → 1.11.0

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 (50) hide show
  1. package/.turbo/turbo-build.log +21 -15
  2. package/CHANGELOG.md +22 -0
  3. package/dist/esm/index.js +45 -24
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/v2/index.js +1 -1
  6. package/dist/esm/v2/index.js.map +1 -1
  7. package/dist/esm/v3/index.js +800 -0
  8. package/dist/esm/v3/index.js.map +1 -0
  9. package/dist/index.d.mts +22 -22
  10. package/dist/index.d.ts +22 -22
  11. package/dist/index.js +35 -14
  12. package/dist/index.js.map +1 -1
  13. package/dist/v2/index.js +1 -1
  14. package/dist/v2/index.js.map +1 -1
  15. package/dist/v3/index.d.mts +295 -0
  16. package/dist/v3/index.d.ts +295 -0
  17. package/dist/v3/index.js +868 -0
  18. package/dist/v3/index.js.map +1 -0
  19. package/package.json +7 -1
  20. package/src/MenuGroup/MenuGroup.tsx +16 -3
  21. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +19 -0
  22. package/src/MenuItem/MenuItem.tsx +21 -10
  23. package/src/MenuItem/__tests__/MenuItem.test.tsx +19 -0
  24. package/src/v2/Common-V2/ComboboxContent.tsx +1 -1
  25. package/src/v3/Common/ComboboxItem.tsx +82 -0
  26. package/src/v3/Common/ComboboxStyles.tsx +475 -0
  27. package/src/v3/Common/SelectGroup.tsx +28 -0
  28. package/src/v3/Common/SelectIcons.tsx +26 -0
  29. package/src/v3/Common/SelectItem.tsx +108 -0
  30. package/src/v3/Common/SelectStyles.tsx +126 -0
  31. package/src/v3/Common/VirtualizedComboboxList.tsx +200 -0
  32. package/src/v3/Common/groupItems.ts +12 -0
  33. package/src/v3/Common/types.ts +3 -0
  34. package/src/v3/ModalStories.stories.tsx +181 -0
  35. package/src/v3/MultiCombobox.stories.tsx +322 -0
  36. package/src/v3/MultiCombobox.tsx +562 -0
  37. package/src/v3/MultiSearchableSelect.stories.tsx +389 -0
  38. package/src/v3/MultiSearchableSelect.tsx +545 -0
  39. package/src/v3/MultiSelect.stories.tsx +300 -0
  40. package/src/v3/MultiSelect.tsx +498 -0
  41. package/src/v3/SeedsPortal.tsx +35 -0
  42. package/src/v3/SingleCombobox.stories.tsx +169 -0
  43. package/src/v3/SingleCombobox.tsx +304 -0
  44. package/src/v3/SingleSearchableSelect.stories.tsx +239 -0
  45. package/src/v3/SingleSearchableSelect.tsx +319 -0
  46. package/src/v3/SingleSelect.stories.tsx +227 -0
  47. package/src/v3/SingleSelect.tsx +245 -0
  48. package/src/v3/index.ts +7 -0
  49. package/src/v3/storyData.tsx +117 -0
  50. package/tsup.config.ts +1 -0
@@ -0,0 +1,475 @@
1
+ import { Combobox } from "@base-ui/react/combobox";
2
+ import styled from "styled-components";
3
+ import { focusRing } from "@sproutsocial/seeds-react-mixins";
4
+ import Checkbox from "@sproutsocial/seeds-react-checkbox";
5
+
6
+ export const ComboboxLabel = styled.label`
7
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
8
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
9
+ color: ${({ theme }) => theme.colors.text.body};
10
+ `;
11
+
12
+ export const ComboboxInputGroup = styled(Combobox.InputGroup)`
13
+ display: flex;
14
+ align-items: center;
15
+ min-width: 160px;
16
+ border-radius: ${({ theme }) => theme.radii[500]};
17
+ border: 1px solid ${({ theme }) => theme.colors.form.border.base};
18
+ background: ${({ theme }) => theme.colors.form.background.base};
19
+ color: ${({ theme }) => theme.colors.text.body};
20
+ transition: border-color ${({ theme }) => theme.duration.fast}
21
+ ${({ theme }) => theme.easing.ease_in},
22
+ box-shadow ${({ theme }) => theme.duration.fast}
23
+ ${({ theme }) => theme.easing.ease_in};
24
+
25
+ &:focus-within {
26
+ ${focusRing}
27
+ }
28
+ `;
29
+
30
+ export const ComboboxInput = styled(Combobox.Input)`
31
+ flex: 1;
32
+ min-width: 30px;
33
+ padding: ${({ theme }) => theme.space[300]};
34
+ border: none;
35
+ background: transparent;
36
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
37
+ line-height: ${({ theme }) => theme.typography[200].lineHeight};
38
+ color: ${({ theme }) => theme.colors.text.body};
39
+ outline: none;
40
+
41
+ &::placeholder {
42
+ color: ${({ theme }) => theme.colors.text.subtext};
43
+ }
44
+ `;
45
+
46
+ export const ComboboxTokenInput = styled(Combobox.Input)`
47
+ flex: 1;
48
+ min-width: 60px;
49
+ padding: ${({ theme }) => theme.space[200]} ${({ theme }) => theme.space[300]}
50
+ ${({ theme }) => theme.space[200]} ${({ theme }) => theme.space[350]};
51
+ border: none;
52
+ background: transparent;
53
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
54
+ line-height: ${({ theme }) => theme.typography[200].lineHeight};
55
+ color: ${({ theme }) => theme.colors.text.body};
56
+ outline: none;
57
+
58
+ &::placeholder {
59
+ color: ${({ theme }) => theme.colors.text.subtext};
60
+ }
61
+ `;
62
+
63
+ export const ComboboxActionButtons = styled.div`
64
+ display: flex;
65
+ align-items: center;
66
+ flex-shrink: 0;
67
+ margin-left: auto;
68
+ gap: ${({ theme }) => theme.space[100]};
69
+ `;
70
+
71
+ export const ComboboxClear = styled(Combobox.Clear)`
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ width: 20px;
76
+ height: 20px;
77
+ border: none;
78
+ background: transparent;
79
+ color: ${({ theme }) => theme.colors.icon.base};
80
+ cursor: pointer;
81
+ border-radius: ${({ theme }) => theme.radii[300]};
82
+ padding: 0;
83
+
84
+ &:hover {
85
+ color: ${({ theme }) => theme.colors.text.body};
86
+ }
87
+
88
+ &[data-empty] {
89
+ display: none;
90
+ }
91
+ `;
92
+
93
+ export const ComboboxTrigger = styled(Combobox.Trigger)`
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ width: 20px;
98
+ height: 20px;
99
+ border: none;
100
+ background: transparent;
101
+ color: ${({ theme }) => theme.colors.icon.base};
102
+ cursor: default;
103
+ padding: 0;
104
+
105
+ &:hover {
106
+ color: ${({ theme }) => theme.colors.text.body};
107
+ }
108
+ `;
109
+
110
+ export const ComboboxPositioner = styled(Combobox.Positioner)`
111
+ width: var(--anchor-width);
112
+ z-index: 7;
113
+ `;
114
+
115
+ export const ComboboxPopup = styled(Combobox.Popup)`
116
+ font-family: ${({ theme }) => theme.fontFamily};
117
+ background: ${({ theme }) => theme.colors.container.background.base};
118
+ border: 1px solid ${({ theme }) => theme.colors.container.border.base};
119
+ border-radius: ${({ theme }) => theme.radii[500]};
120
+ box-shadow: ${({ theme }) => theme.shadows.medium};
121
+ outline: none;
122
+ width: 100%;
123
+ max-height: min(var(--base-ui-available-height, 400px), 400px);
124
+ overflow-y: auto;
125
+ padding: 0;
126
+
127
+ &[data-open] {
128
+ animation: combobox-popup-in 120ms ease-out;
129
+ }
130
+
131
+ &[data-closed] {
132
+ animation: combobox-popup-out 100ms ease-in;
133
+ }
134
+
135
+ @keyframes combobox-popup-in {
136
+ from {
137
+ opacity: 0;
138
+ transform: scale(0.95);
139
+ }
140
+ to {
141
+ opacity: 1;
142
+ transform: scale(1);
143
+ }
144
+ }
145
+
146
+ @keyframes combobox-popup-out {
147
+ from {
148
+ opacity: 1;
149
+ transform: scale(1);
150
+ }
151
+ to {
152
+ opacity: 0;
153
+ transform: scale(0.95);
154
+ }
155
+ }
156
+ `;
157
+
158
+ export const ComboboxList = styled(Combobox.List)`
159
+ padding: ${({ theme }) => theme.space[200]};
160
+
161
+ &:empty {
162
+ padding: 0;
163
+ }
164
+ `;
165
+
166
+ export const ComboboxStatus = styled(Combobox.Status)`
167
+ padding: ${({ theme }) => `${theme.space[300]} ${theme.space[350]}`};
168
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
169
+ color: ${({ theme }) => theme.colors.text.subtext};
170
+
171
+ &:empty {
172
+ padding: 0;
173
+ }
174
+ `;
175
+
176
+ export const ComboboxEmpty = styled(Combobox.Empty)`
177
+ padding: ${({ theme }) => `${theme.space[300]} ${theme.space[350]}`};
178
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
179
+ color: ${({ theme }) => theme.colors.text.subtext};
180
+
181
+ &:empty {
182
+ padding: 0;
183
+ }
184
+ `;
185
+
186
+ export const ComboboxItem = styled(Combobox.Item)`
187
+ display: flex;
188
+ align-items: center;
189
+ gap: ${({ theme }) => theme.space[300]};
190
+ padding: ${({ theme }) => `${theme.space[300]} ${theme.space[300]}`};
191
+ border-radius: ${({ theme }) => theme.radii[500]};
192
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
193
+ color: ${({ theme }) => theme.colors.text.body};
194
+ cursor: default;
195
+ outline: none;
196
+
197
+ &[data-highlighted] {
198
+ background: ${({ theme }) => theme.colors.listItem.background.hover};
199
+ }
200
+
201
+ &[data-selected] {
202
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
203
+ }
204
+
205
+ &[data-disabled] {
206
+ opacity: 0.5;
207
+ cursor: not-allowed;
208
+ }
209
+ `;
210
+
211
+ export const ComboboxItemIndicator = styled(Combobox.ItemIndicator)`
212
+ display: flex;
213
+ align-items: center;
214
+ width: ${({ theme }) => theme.space[400]};
215
+ color: ${({ theme }) => theme.colors.text.body};
216
+ visibility: hidden;
217
+
218
+ &[data-selected] {
219
+ visibility: visible;
220
+ }
221
+ `;
222
+
223
+ export const ComboboxGroup = styled(Combobox.Group)`
224
+ display: block;
225
+ `;
226
+
227
+ export const ComboboxGroupLabel = styled(Combobox.GroupLabel)`
228
+ padding: ${({ theme }) =>
229
+ `${theme.space[300]} ${theme.space[300]} ${theme.space[200]}`};
230
+ font-size: ${({ theme }) => theme.typography[100].fontSize};
231
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
232
+ text-transform: uppercase;
233
+ letter-spacing: 0.05em;
234
+ color: ${({ theme }) => theme.colors.text.subtext};
235
+ `;
236
+
237
+ export const ComboboxSeparator = styled.div`
238
+ height: 1px;
239
+ margin: ${({ theme }) => `${theme.space[200]} ${theme.space[300]}`};
240
+ background: ${({ theme }) => theme.colors.container.border.base};
241
+ `;
242
+
243
+ export const ComboboxGroupHeaderItem = styled(Combobox.Item)`
244
+ display: flex;
245
+ align-items: center;
246
+ gap: ${({ theme }) => theme.space[300]};
247
+ padding: ${({ theme }) => `${theme.space[300]} ${theme.space[300]}`};
248
+ border-radius: ${({ theme }) => theme.radii[500]};
249
+ font-size: ${({ theme }) => theme.typography[100].fontSize};
250
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
251
+ text-transform: uppercase;
252
+ letter-spacing: 0.05em;
253
+ color: ${({ theme }) => theme.colors.text.subtext};
254
+ cursor: default;
255
+ outline: none;
256
+
257
+ &[data-highlighted] {
258
+ background: ${({ theme }) => theme.colors.listItem.background.hover};
259
+ color: ${({ theme }) => theme.colors.text.body};
260
+ }
261
+ `;
262
+
263
+ export const ComboboxSelectAllItem = styled(Combobox.Item)`
264
+ display: flex;
265
+ align-items: center;
266
+ gap: ${({ theme }) => theme.space[300]};
267
+ padding: ${({ theme }) => `${theme.space[300]} ${theme.space[300]}`};
268
+ border-radius: ${({ theme }) => theme.radii[500]};
269
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
270
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
271
+ color: ${({ theme }) => theme.colors.text.body};
272
+ cursor: default;
273
+ outline: none;
274
+ border-bottom: 1px solid ${({ theme }) => theme.colors.container.border.base};
275
+ margin-bottom: ${({ theme }) => theme.space[200]};
276
+
277
+ &[data-highlighted] {
278
+ background: ${({ theme }) => theme.colors.listItem.background.hover};
279
+ }
280
+ `;
281
+
282
+ export function ComboboxGroupHeaderCheckbox({
283
+ $state,
284
+ id,
285
+ }: {
286
+ $state: "none" | "partial" | "all";
287
+ id: string;
288
+ }) {
289
+ return (
290
+ <Checkbox
291
+ id={id}
292
+ checked={$state === "all"}
293
+ indeterminate={$state === "partial"}
294
+ onChange={() => {}}
295
+ tabIndex={-1}
296
+ />
297
+ );
298
+ }
299
+
300
+ // Multi-select Token components
301
+ export const ComboboxToken = styled(Combobox.Chips)`
302
+ display: flex;
303
+ width: 100%;
304
+ flex-wrap: wrap;
305
+ `;
306
+
307
+ export const ComboboxTokenInputGroup = styled(Combobox.InputGroup)`
308
+ display: flex;
309
+ align-items: center;
310
+ flex-wrap: wrap;
311
+ border-radius: ${({ theme }) => theme.radii[500]};
312
+ border: 1px solid ${({ theme }) => theme.colors.form.border.base};
313
+ background: ${({ theme }) => theme.colors.form.background.base};
314
+ color: ${({ theme }) => theme.colors.text.body};
315
+ padding: ${({ theme }) =>
316
+ `${theme.space[200]} ${theme.space[300]} ${theme.space[200]} ${theme.space[200]}`};
317
+ gap: ${({ theme }) => theme.space[200]};
318
+ transition: border-color ${({ theme }) => theme.duration.fast}
319
+ ${({ theme }) => theme.easing.ease_in},
320
+ box-shadow ${({ theme }) => theme.duration.fast}
321
+ ${({ theme }) => theme.easing.ease_in};
322
+
323
+ &:focus-within {
324
+ ${focusRing}
325
+ }
326
+ `;
327
+
328
+ export const Token = styled.span`
329
+ display: inline-flex;
330
+ align-items: center;
331
+ gap: ${({ theme }) => theme.space[200]};
332
+ padding: ${({ theme }) => `${theme.space[200]} ${theme.space[300]}`};
333
+ border-radius: ${({ theme }) => theme.radii[500]};
334
+ border: 1px solid ${({ theme }) => theme.colors.container.border.base};
335
+ background: ${({ theme }) => theme.colors.container.background.base};
336
+ color: ${({ theme }) => theme.colors.text.body};
337
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
338
+ font-weight: ${({ theme }) => theme.fontWeights.normal};
339
+ line-height: 1;
340
+ white-space: nowrap;
341
+ transition: all ${({ theme }) => theme.duration.fast}
342
+ ${({ theme }) => theme.easing.ease_inout};
343
+
344
+ &:hover {
345
+ box-shadow: ${({ theme }) => theme.shadows.low};
346
+ border-color: ${({ theme }) => theme.colors.container.border.base};
347
+ }
348
+ `;
349
+
350
+ export const TokenRemove = styled.button`
351
+ display: flex;
352
+ align-items: center;
353
+ justify-content: center;
354
+ border: none;
355
+ background: transparent;
356
+ color: inherit;
357
+ cursor: pointer;
358
+ padding: 0;
359
+ line-height: 1;
360
+ `;
361
+
362
+ // Searchable select components
363
+
364
+ export const SearchableSelectTrigger = styled(Combobox.Trigger)`
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: space-between;
368
+ gap: ${({ theme }) => theme.space[300]};
369
+ padding: ${({ theme }) => theme.space[200]} ${({ theme }) => theme.space[350]};
370
+ min-width: 160px;
371
+ border-radius: ${({ theme }) => theme.radii[500]};
372
+ border: 1px solid ${({ theme }) => theme.colors.form.border.base};
373
+ background: ${({ theme }) => theme.colors.form.background.base};
374
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
375
+ color: ${({ theme }) => theme.colors.text.body};
376
+ cursor: default;
377
+ outline: none;
378
+ user-select: none;
379
+ transition: border-color ${({ theme }) => theme.duration.fast}
380
+ ${({ theme }) => theme.easing.ease_in},
381
+ box-shadow ${({ theme }) => theme.duration.fast}
382
+ ${({ theme }) => theme.easing.ease_in};
383
+
384
+ &:focus-visible {
385
+ ${focusRing}
386
+ }
387
+
388
+ &[data-popup-open] [data-chevron] {
389
+ transform: rotate(-180deg);
390
+ }
391
+ `;
392
+
393
+ export const SearchableSelectTriggerIcon = styled(Combobox.Icon)`
394
+ display: flex;
395
+ align-items: center;
396
+ color: ${({ theme }) => theme.colors.icon.base};
397
+ flex-shrink: 0;
398
+ `;
399
+
400
+ export const SearchableSelectPlaceholder = styled.span`
401
+ color: ${({ theme }) => theme.colors.text.subtext};
402
+ padding: ${({ theme }) => theme.space[200]} 0;
403
+ `;
404
+
405
+ export const SearchableSelectPopup = ComboboxPopup;
406
+
407
+ export const SearchableSelectSearchContainer = styled.div`
408
+ padding: ${({ theme }) => theme.space[300]};
409
+ border-bottom: 1px solid ${({ theme }) => theme.colors.container.border.base};
410
+ flex-shrink: 0;
411
+ `;
412
+
413
+ export const SearchableSelectInput = styled(Combobox.Input)`
414
+ width: 100%;
415
+ height: ${({ theme }) => theme.space[500]};
416
+ padding: 0 ${({ theme }) => theme.space[300]};
417
+ border-radius: ${({ theme }) => theme.radii[500]};
418
+ border: 1px solid ${({ theme }) => theme.colors.container.border.base};
419
+ background: ${({ theme }) => theme.colors.container.background.base};
420
+ font-size: ${({ theme }) => theme.typography[200].fontSize};
421
+ color: ${({ theme }) => theme.colors.text.body};
422
+ outline: none;
423
+ box-sizing: border-box;
424
+
425
+ &:focus {
426
+ ${focusRing}
427
+ }
428
+
429
+ &::placeholder {
430
+ color: ${({ theme }) => theme.colors.text.subtext};
431
+ }
432
+ `;
433
+
434
+ export const SearchableSelectList = styled(Combobox.List)`
435
+ overflow-y: auto;
436
+ padding: ${({ theme }) => theme.space[200]};
437
+ flex: 1;
438
+ min-height: 0;
439
+ &:empty {
440
+ padding: 0;
441
+ }
442
+ `;
443
+
444
+ export const SearchableSelectTokenTrigger = styled(Combobox.Trigger)`
445
+ display: flex;
446
+ align-items: center;
447
+ flex-wrap: wrap;
448
+ min-width: 160px;
449
+ border-radius: ${({ theme }) => theme.radii[500]};
450
+ border: 1px solid ${({ theme }) => theme.colors.form.border.base};
451
+ background: ${({ theme }) => theme.colors.form.background.base};
452
+ color: ${({ theme }) => theme.colors.text.body};
453
+ padding: ${({ theme }) => theme.space[200]} ${({ theme }) => theme.space[300]};
454
+ gap: ${({ theme }) => theme.space[200]};
455
+ cursor: default;
456
+ text-align: left;
457
+ outline: none;
458
+ user-select: none;
459
+ transition: border-color ${({ theme }) => theme.duration.fast}
460
+ ${({ theme }) => theme.easing.ease_in},
461
+ box-shadow ${({ theme }) => theme.duration.fast}
462
+ ${({ theme }) => theme.easing.ease_in};
463
+
464
+ &:focus-visible {
465
+ ${focusRing}
466
+ }
467
+
468
+ &[data-popup-open] {
469
+ border-color: ${({ theme }) => theme.colors.blue[600]};
470
+ }
471
+
472
+ &[data-popup-open] [data-chevron] {
473
+ transform: rotate(-180deg);
474
+ }
475
+ `;
@@ -0,0 +1,28 @@
1
+ import * as React from "react";
2
+ import {
3
+ SelectGroup as StyledGroup,
4
+ SelectGroupLabel,
5
+ SelectSeparator,
6
+ } from "./SelectStyles";
7
+
8
+ interface SelectGroupProps {
9
+ label: string;
10
+ children: React.ReactNode;
11
+ showSeparator?: boolean;
12
+ }
13
+
14
+ export default function SelectGroup({
15
+ label,
16
+ children,
17
+ showSeparator = false,
18
+ }: SelectGroupProps) {
19
+ return (
20
+ <>
21
+ <StyledGroup>
22
+ <SelectGroupLabel>{label}</SelectGroupLabel>
23
+ {children}
24
+ </StyledGroup>
25
+ {showSeparator && <SelectSeparator />}
26
+ </>
27
+ );
28
+ }
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ import styled from "styled-components";
3
+ import { Icon } from "@sproutsocial/seeds-react-icon";
4
+
5
+ export interface StyledChevronProps {
6
+ $isOpen?: boolean;
7
+ }
8
+
9
+ export const StyledChevron = styled.div<StyledChevronProps>`
10
+ display: flex;
11
+ align-items: center;
12
+ transition: transform 0.15s;
13
+ ${({ $isOpen }) => $isOpen && "transform: rotate(-180deg);"}
14
+ `;
15
+
16
+ export function ChevronIcon() {
17
+ return <Icon name="chevron-down-outline" fixedWidth aria-hidden />;
18
+ }
19
+
20
+ export function ClearIcon() {
21
+ return <Icon name="circle-x-outline" fixedWidth aria-hidden size="mini" />;
22
+ }
23
+
24
+ export function CheckIcon() {
25
+ return <Icon name="check-outline" fixedWidth aria-hidden />;
26
+ }
@@ -0,0 +1,108 @@
1
+ import * as React from "react";
2
+ import { Select } from "@base-ui/react/select";
3
+ import styled from "styled-components";
4
+ import Radio from "@sproutsocial/seeds-react-radio";
5
+ import Checkbox from "@sproutsocial/seeds-react-checkbox";
6
+ import { Icon } from "@sproutsocial/seeds-react-icon";
7
+
8
+ // ─── Styled components ────────────────────────────────────────────────────────
9
+
10
+ export const StyledItem = styled(Select.Item)`
11
+ display: flex;
12
+ align-items: center;
13
+ gap: 8px;
14
+ padding: 6px 8px;
15
+ border-radius: ${({ theme }) => theme.radii[500]};
16
+ font-size: 14px;
17
+ color: ${({ theme }) => theme.colors.text.body};
18
+ cursor: default;
19
+ outline: none;
20
+
21
+ &[data-highlighted] {
22
+ background: ${({ theme }) => theme.colors.listItem.background.hover};
23
+ }
24
+
25
+ &[data-selected] {
26
+ font-weight: ${({ theme }) => theme.fontWeights.semibold};
27
+ }
28
+
29
+ &[data-disabled] {
30
+ opacity: 0.5;
31
+ cursor: not-allowed;
32
+ }
33
+ `;
34
+
35
+ export const StyledItemIndicator = styled(Select.ItemIndicator)`
36
+ display: flex;
37
+ align-items: center;
38
+ width: 16px;
39
+ color: ${({ theme }) => theme.colors.text.body};
40
+ visibility: hidden;
41
+
42
+ &[data-selected] {
43
+ visibility: visible;
44
+ }
45
+ `;
46
+
47
+ export const StyledItemText = styled(Select.ItemText)``;
48
+
49
+ // ─── Icons ────────────────────────────────────────────────────────────────────
50
+
51
+ function CheckIcon() {
52
+ return <Icon name="check-outline" fixedWidth aria-hidden />;
53
+ }
54
+
55
+ // ─── Component ────────────────────────────────────────────────────────────────
56
+
57
+ interface SelectItemProps {
58
+ value: string | null;
59
+ label: string;
60
+ inputType?: "icon" | "radio" | "checkbox" | "none";
61
+ renderItem?: () => React.ReactNode;
62
+ hidden?: boolean;
63
+ }
64
+
65
+ export default function SelectItem({
66
+ value,
67
+ label,
68
+ inputType = "icon",
69
+ renderItem,
70
+ hidden,
71
+ }: SelectItemProps) {
72
+ return (
73
+ <StyledItem value={value} style={hidden ? { display: "none" } : undefined}>
74
+ {inputType === "radio" ? (
75
+ <StyledItemIndicator
76
+ keepMounted
77
+ render={(_props, state) => (
78
+ <Radio
79
+ checked={state.selected}
80
+ onChange={() => {}}
81
+ tabIndex={-1}
82
+ id={`radio-${value}`}
83
+ name="select-item"
84
+ />
85
+ )}
86
+ />
87
+ ) : inputType === "checkbox" ? (
88
+ <StyledItemIndicator
89
+ keepMounted
90
+ render={(_props, state) => (
91
+ <Checkbox
92
+ checked={state.selected}
93
+ onChange={() => {}}
94
+ tabIndex={-1}
95
+ id={`checkbox-${value}`}
96
+ name="select-item"
97
+ />
98
+ )}
99
+ />
100
+ ) : inputType === "icon" ? (
101
+ <StyledItemIndicator keepMounted>
102
+ <CheckIcon />
103
+ </StyledItemIndicator>
104
+ ) : null}
105
+ <StyledItemText>{renderItem ? renderItem() : label}</StyledItemText>
106
+ </StyledItem>
107
+ );
108
+ }