@rovula/ui 0.1.20 → 0.1.22

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 (99) hide show
  1. package/dist/cjs/bundle.css +316 -43
  2. package/dist/cjs/bundle.js +675 -675
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Badge/Badge.d.ts +40 -0
  5. package/dist/cjs/types/components/Badge/Badge.stories.d.ts +295 -0
  6. package/dist/cjs/types/components/Badge/Badge.styles.d.ts +7 -0
  7. package/dist/cjs/types/components/Badge/index.d.ts +2 -0
  8. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +4 -8
  9. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  10. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  11. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +45 -30
  12. package/dist/cjs/types/components/Form/Form.d.ts +2 -1
  13. package/dist/cjs/types/components/Form/Form.stories.d.ts +4 -0
  14. package/dist/cjs/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  15. package/dist/cjs/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  16. package/dist/cjs/types/index.d.ts +4 -1
  17. package/dist/cjs/types/patterns/menu/Menu.d.ts +70 -0
  18. package/dist/cjs/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  19. package/dist/cjs/types/utils/mergeRefs.d.ts +20 -0
  20. package/dist/components/Avatar/Avatar.styles.js +2 -2
  21. package/dist/components/Badge/Badge.js +36 -0
  22. package/dist/components/Badge/Badge.stories.js +51 -0
  23. package/dist/components/Badge/Badge.styles.js +62 -0
  24. package/dist/components/Badge/index.js +2 -0
  25. package/dist/components/Dropdown/Dropdown.js +54 -163
  26. package/dist/components/Dropdown/Dropdown.stories.js +29 -0
  27. package/dist/components/DropdownMenu/DropdownMenu.js +24 -13
  28. package/dist/components/DropdownMenu/DropdownMenu.stories.js +120 -88
  29. package/dist/components/Form/Form.js +11 -4
  30. package/dist/components/Form/Form.stories.js +27 -0
  31. package/dist/components/ScrollArea/ScrollArea.js +50 -0
  32. package/dist/components/ScrollArea/ScrollArea.stories.js +56 -0
  33. package/dist/components/TextInput/TextInput.js +6 -3
  34. package/dist/esm/bundle.css +316 -43
  35. package/dist/esm/bundle.js +1545 -1545
  36. package/dist/esm/bundle.js.map +1 -1
  37. package/dist/esm/types/components/Badge/Badge.d.ts +40 -0
  38. package/dist/esm/types/components/Badge/Badge.stories.d.ts +295 -0
  39. package/dist/esm/types/components/Badge/Badge.styles.d.ts +7 -0
  40. package/dist/esm/types/components/Badge/index.d.ts +2 -0
  41. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +4 -8
  42. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +1 -6
  43. package/dist/esm/types/components/DropdownMenu/DropdownMenu.d.ts +5 -1
  44. package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +45 -30
  45. package/dist/esm/types/components/Form/Form.d.ts +2 -1
  46. package/dist/esm/types/components/Form/Form.stories.d.ts +4 -0
  47. package/dist/esm/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  48. package/dist/esm/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  49. package/dist/esm/types/index.d.ts +4 -1
  50. package/dist/esm/types/patterns/menu/Menu.d.ts +70 -0
  51. package/dist/esm/types/{components/Menu → patterns/menu}/Menu.stories.d.ts +17 -10
  52. package/dist/esm/types/utils/mergeRefs.d.ts +20 -0
  53. package/dist/index.d.ts +156 -74
  54. package/dist/index.js +3 -1
  55. package/dist/patterns/menu/Menu.js +95 -0
  56. package/dist/patterns/menu/Menu.stories.js +611 -0
  57. package/dist/src/theme/global.css +485 -57
  58. package/dist/utils/mergeRefs.js +42 -0
  59. package/package.json +1 -1
  60. package/src/components/Avatar/Avatar.styles.ts +2 -2
  61. package/src/components/Badge/Badge.stories.tsx +128 -0
  62. package/src/components/Badge/Badge.styles.ts +70 -0
  63. package/src/components/Badge/Badge.tsx +103 -0
  64. package/src/components/Badge/index.ts +3 -0
  65. package/src/components/Dropdown/Dropdown.stories.tsx +170 -1
  66. package/src/components/Dropdown/Dropdown.tsx +186 -276
  67. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +1375 -253
  68. package/src/components/DropdownMenu/DropdownMenu.tsx +118 -55
  69. package/src/components/Form/Form.stories.tsx +70 -0
  70. package/src/components/Form/Form.tsx +23 -0
  71. package/src/components/ScrollArea/ScrollArea.stories.tsx +229 -0
  72. package/src/components/ScrollArea/ScrollArea.tsx +72 -0
  73. package/src/components/TextInput/TextInput.tsx +6 -3
  74. package/src/index.ts +4 -1
  75. package/src/patterns/menu/Menu.stories.tsx +1100 -0
  76. package/src/patterns/menu/Menu.tsx +282 -0
  77. package/src/theme/global.css +84 -11
  78. package/src/theme/themes/xspector/baseline.css +1 -1
  79. package/src/theme/themes/xspector/components/scrollbar.css +12 -0
  80. package/src/theme/tokens/baseline.css +3 -1
  81. package/src/theme/tokens/components/badge.css +54 -0
  82. package/src/theme/tokens/components/dropdown-menu.css +16 -5
  83. package/src/theme/tokens/components/scrollbar.css +18 -0
  84. package/src/utils/mergeRefs.ts +46 -0
  85. package/dist/cjs/types/components/Menu/Menu.d.ts +0 -65
  86. package/dist/cjs/types/components/Menu/helpers.d.ts +0 -19
  87. package/dist/cjs/types/components/Menu/index.d.ts +0 -4
  88. package/dist/components/Menu/Menu.js +0 -64
  89. package/dist/components/Menu/Menu.stories.js +0 -406
  90. package/dist/components/Menu/helpers.js +0 -28
  91. package/dist/components/Menu/index.js +0 -3
  92. package/dist/esm/types/components/Menu/Menu.d.ts +0 -65
  93. package/dist/esm/types/components/Menu/helpers.d.ts +0 -19
  94. package/dist/esm/types/components/Menu/index.d.ts +0 -4
  95. package/src/components/Menu/Menu.stories.tsx +0 -586
  96. package/src/components/Menu/Menu.tsx +0 -235
  97. package/src/components/Menu/helpers.ts +0 -45
  98. package/src/components/Menu/index.ts +0 -7
  99. package/src/theme/themes/xspector/components/dropdown-menu.css +0 -28
@@ -8,27 +8,29 @@ import {
8
8
  DropdownMenuItem,
9
9
  DropdownMenuLabel,
10
10
  DropdownMenuPortal,
11
+ DropdownMenuRadioGroup,
12
+ DropdownMenuRadioItem,
11
13
  DropdownMenuSeparator,
14
+ DropdownMenuShortcut,
12
15
  DropdownMenuSub,
13
16
  DropdownMenuSubContent,
14
17
  DropdownMenuSubTrigger,
15
18
  DropdownMenuTrigger,
16
19
  } from "./DropdownMenu";
17
20
  import Button from "../Button/Button";
18
- import { DropdownMenuCheckboxItemProps } from "@radix-ui/react-dropdown-menu";
19
- import Icon from "../Icon/Icon";
20
21
  import ActionButton from "../ActionButton/ActionButton";
22
+ import Icon from "../Icon/Icon";
23
+ import { Input } from "../Input/Input";
21
24
 
22
25
  const meta = {
23
26
  title: "Components/DropdownMenu",
24
27
  component: DropdownMenu,
25
- // tags: ["autodocs"],
26
28
  parameters: {
27
29
  layout: "fullscreen",
28
30
  },
29
31
  decorators: [
30
32
  (Story) => (
31
- <div className="p-5 flex w-full">
33
+ <div className="p-10 flex gap-8 flex-wrap bg-workspace-surface min-h-screen">
32
34
  <Story />
33
35
  </div>
34
36
  ),
@@ -37,193 +39,453 @@ const meta = {
37
39
 
38
40
  export default meta;
39
41
 
40
- export const Default = {
41
- args: {
42
- // DropdownMenu: "Lorem Ipsum",
43
- // value: "Lorem Ipsum",
44
- // fullwidth: true,
45
- },
46
- render: (args) => {
47
- console.log("args ", args);
48
- const props: typeof args = {
49
- ...args,
50
- };
51
- return (
52
- <div className="flex flex-row gap-4 w-full">
53
- <div className="flex flex-1 justify-center items-center space-x-2">
54
- <DropdownMenu>
55
- <DropdownMenuTrigger>Open</DropdownMenuTrigger>
56
- <DropdownMenuContent>
57
- <DropdownMenuItem>Option Description</DropdownMenuItem>
58
- <DropdownMenuItem>Option Description</DropdownMenuItem>
59
- <DropdownMenuItem disabled>Option Description</DropdownMenuItem>
60
- <DropdownMenuItem>
61
- <Icon type="heroicons" name="check" className="size-4" />
62
- Option Description
63
- </DropdownMenuItem>
64
- <DropdownMenuItem>Option Description</DropdownMenuItem>
65
- </DropdownMenuContent>
66
- </DropdownMenu>
67
- </div>
42
+ // ---------------------------------------------------------------------------
43
+ // Figma: Normal dropdown / action menu
44
+ // Shows all item states: Default, Hover, Selected, Disabled, Selected+Disabled
45
+ // ---------------------------------------------------------------------------
46
+ export const AllStates = {
47
+ name: "All States (Normal)",
48
+ render: () => (
49
+ <div className="flex gap-8 items-start flex-wrap">
50
+ {/* Static all states visible */}
51
+ <div>
52
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
53
+ All States
54
+ </p>
55
+ <DropdownMenu>
56
+ <DropdownMenuTrigger asChild>
57
+ <ActionButton variant="icon">
58
+ <Icon type="heroicons" name="ellipsis-vertical" />
59
+ </ActionButton>
60
+ </DropdownMenuTrigger>
61
+ <DropdownMenuContent
62
+ className="relative"
63
+ style={{ position: "static", transform: "none" }}
64
+ >
65
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
66
+ <DropdownMenuItem className="!bg-[var(--dropdown-menu-hover-bg)]">
67
+ Option Description (Hover)
68
+ </DropdownMenuItem>
69
+ <DropdownMenuItem selected>Option Description</DropdownMenuItem>
70
+ <DropdownMenuItem disabled>Option Description</DropdownMenuItem>
71
+ <DropdownMenuItem selected disabled>
72
+ Option Description
73
+ </DropdownMenuItem>
74
+ </DropdownMenuContent>
75
+ </DropdownMenu>
68
76
  </div>
69
- );
70
- },
77
+
78
+ {/* Interactive version */}
79
+ <div>
80
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
81
+ Interactive
82
+ </p>
83
+ <DropdownMenu>
84
+ <DropdownMenuTrigger asChild>
85
+ <Button variant="outline">Open Menu</Button>
86
+ </DropdownMenuTrigger>
87
+ <DropdownMenuContent>
88
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
89
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
90
+ <DropdownMenuItem selected>Option Description</DropdownMenuItem>
91
+ <DropdownMenuItem disabled>Option Description</DropdownMenuItem>
92
+ <DropdownMenuItem selected disabled>
93
+ Option Description
94
+ </DropdownMenuItem>
95
+ </DropdownMenuContent>
96
+ </DropdownMenu>
97
+ </div>
98
+ </div>
99
+ ),
71
100
  } satisfies StoryObj;
72
101
 
102
+ // ---------------------------------------------------------------------------
103
+ // Figma: Have icon
104
+ // Items with leading icon
105
+ // ---------------------------------------------------------------------------
73
106
  export const WithIcon = {
74
- args: {
75
- // DropdownMenu: "Lorem Ipsum",
76
- // value: "Lorem Ipsum",
77
- // fullwidth: true,
78
- },
79
- render: (args) => {
80
- console.log("args ", args);
81
- const props: typeof args = {
82
- ...args,
83
- };
84
- return (
85
- <div className="flex flex-row gap-4 w-full">
86
- <div className="flex flex-1 justify-center items-center space-x-2">
87
- <DropdownMenu>
88
- <DropdownMenuTrigger asChild>
89
- <Button variant="outline">Open</Button>
90
- </DropdownMenuTrigger>
91
- <DropdownMenuContent>
92
- <DropdownMenuItem>
107
+ name: "With Icon",
108
+ render: () => (
109
+ <div className="flex gap-8 items-start flex-wrap">
110
+ <div>
111
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
112
+ All States With Icon
113
+ </p>
114
+ <DropdownMenu>
115
+ <DropdownMenuTrigger asChild>
116
+ <ActionButton variant="icon">
117
+ <Icon type="heroicons" name="ellipsis-vertical" />
118
+ </ActionButton>
119
+ </DropdownMenuTrigger>
120
+ <DropdownMenuContent
121
+ className="relative"
122
+ style={{ position: "static", transform: "none" }}
123
+ >
124
+ <DropdownMenuItem
125
+ icon={
93
126
  <Icon
94
127
  type="heroicons"
95
128
  name="rocket-launch"
96
- className="size-4"
129
+ className="size-4 shrink-0"
97
130
  />
98
- <span>Option Description</span>
99
- </DropdownMenuItem>
100
- <DropdownMenuItem>
131
+ }
132
+ >
133
+ Option Description
134
+ </DropdownMenuItem>
135
+ <DropdownMenuItem
136
+ className="!bg-[var(--dropdown-menu-hover-bg)]"
137
+ icon={
101
138
  <Icon
102
139
  type="heroicons"
103
140
  name="rocket-launch"
104
- className="size-4"
141
+ className="size-4 shrink-0"
105
142
  />
106
- <span>Option Description</span>
107
- </DropdownMenuItem>
108
- <DropdownMenuItem disabled>
143
+ }
144
+ >
145
+ Option Description (Hover)
146
+ </DropdownMenuItem>
147
+ <DropdownMenuItem
148
+ selected
149
+ icon={
109
150
  <Icon
110
151
  type="heroicons"
111
152
  name="rocket-launch"
112
- className="size-4"
153
+ className="size-4 shrink-0"
113
154
  />
114
- <span>Option Description</span>
115
- </DropdownMenuItem>
116
- </DropdownMenuContent>
117
- </DropdownMenu>
118
- </div>
155
+ }
156
+ >
157
+ Option Description (Selected)
158
+ </DropdownMenuItem>
159
+ <DropdownMenuItem
160
+ disabled
161
+ icon={
162
+ <Icon
163
+ type="heroicons"
164
+ name="rocket-launch"
165
+ className="size-4 shrink-0"
166
+ />
167
+ }
168
+ >
169
+ Option Description (Disabled)
170
+ </DropdownMenuItem>
171
+ <DropdownMenuItem
172
+ selected
173
+ disabled
174
+ icon={
175
+ <Icon
176
+ type="heroicons"
177
+ name="rocket-launch"
178
+ className="size-4 shrink-0"
179
+ />
180
+ }
181
+ >
182
+ Option Description (Selected + Disabled)
183
+ </DropdownMenuItem>
184
+ </DropdownMenuContent>
185
+ </DropdownMenu>
119
186
  </div>
120
- );
121
- },
187
+
188
+ <div>
189
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
190
+ Interactive
191
+ </p>
192
+ <DropdownMenu>
193
+ <DropdownMenuTrigger asChild>
194
+ <Button variant="outline">Open Menu</Button>
195
+ </DropdownMenuTrigger>
196
+ <DropdownMenuContent>
197
+ <DropdownMenuItem
198
+ icon={
199
+ <Icon
200
+ type="heroicons"
201
+ name="rocket-launch"
202
+ className="size-4 shrink-0"
203
+ />
204
+ }
205
+ >
206
+ Launch
207
+ </DropdownMenuItem>
208
+ <DropdownMenuItem
209
+ icon={
210
+ <Icon
211
+ type="heroicons"
212
+ name="pencil"
213
+ className="size-4 shrink-0"
214
+ />
215
+ }
216
+ >
217
+ Edit
218
+ </DropdownMenuItem>
219
+ <DropdownMenuItem
220
+ icon={
221
+ <Icon
222
+ type="heroicons"
223
+ name="document-duplicate"
224
+ className="size-4 shrink-0"
225
+ />
226
+ }
227
+ >
228
+ Duplicate
229
+ </DropdownMenuItem>
230
+ <DropdownMenuItem
231
+ disabled
232
+ icon={
233
+ <Icon
234
+ type="heroicons"
235
+ name="trash"
236
+ className="size-4 shrink-0"
237
+ />
238
+ }
239
+ >
240
+ Delete
241
+ </DropdownMenuItem>
242
+ </DropdownMenuContent>
243
+ </DropdownMenu>
244
+ </div>
245
+ </div>
246
+ ),
122
247
  } satisfies StoryObj;
123
248
 
124
- export const WithLabel = {
125
- args: {
126
- // DropdownMenu: "Lorem Ipsum",
127
- // value: "Lorem Ipsum",
128
- // fullwidth: true,
129
- },
130
- render: (args) => {
131
- console.log("args ", args);
132
- const props: typeof args = {
133
- ...args,
134
- };
249
+ // ---------------------------------------------------------------------------
250
+ // Figma: Checkbox items (Have icon=No, Checkbox=Yes)
251
+ // ---------------------------------------------------------------------------
252
+ export const WithCheckbox = {
253
+ name: "With Checkbox",
254
+ render: () => {
255
+ const [checked1, setChecked1] = useState(false);
256
+ const [checked2, setChecked2] = useState(true);
257
+ const [checked3, setChecked3] = useState(false);
258
+
135
259
  return (
136
- <div className="flex flex-row gap-4 w-full">
137
- <div className="flex flex-1 justify-center items-center space-x-2">
260
+ <div className="flex gap-8 items-start flex-wrap">
261
+ <div>
262
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
263
+ All States — Checkbox
264
+ </p>
138
265
  <DropdownMenu>
139
266
  <DropdownMenuTrigger asChild>
140
- <Button variant="outline">Open</Button>
267
+ <ActionButton variant="icon">
268
+ <Icon type="heroicons" name="ellipsis-vertical" />
269
+ </ActionButton>
141
270
  </DropdownMenuTrigger>
142
- <DropdownMenuContent>
143
- <DropdownMenuItem>Option Description</DropdownMenuItem>
144
- <DropdownMenuItem>Option Description</DropdownMenuItem>
145
- <DropdownMenuItem>Option Description</DropdownMenuItem>
146
- <DropdownMenuItem>Option Description</DropdownMenuItem>
147
- <DropdownMenuItem>Option Description</DropdownMenuItem>
148
- <DropdownMenuSeparator />
149
- <DropdownMenuLabel>Sort by</DropdownMenuLabel>
150
- <DropdownMenuItem>Option Description</DropdownMenuItem>
151
- <DropdownMenuItem>Option Description</DropdownMenuItem>
152
- <DropdownMenuItem>Option Description</DropdownMenuItem>
153
- <DropdownMenuItem>Option Description</DropdownMenuItem>
154
- <DropdownMenuItem>Option Description</DropdownMenuItem>
271
+ <DropdownMenuContent
272
+ className="relative"
273
+ style={{ position: "static", transform: "none" }}
274
+ >
275
+ <DropdownMenuCheckboxItem>
276
+ Option Description
277
+ </DropdownMenuCheckboxItem>
278
+ <DropdownMenuCheckboxItem className="!bg-[var(--dropdown-menu-hover-bg)]">
279
+ Option Description (Hover)
280
+ </DropdownMenuCheckboxItem>
281
+ <DropdownMenuCheckboxItem checked>
282
+ Option Description
283
+ </DropdownMenuCheckboxItem>
284
+ <DropdownMenuCheckboxItem disabled>
285
+ Option Description
286
+ </DropdownMenuCheckboxItem>
287
+ <DropdownMenuCheckboxItem checked disabled>
288
+ Option Description
289
+ </DropdownMenuCheckboxItem>
155
290
  </DropdownMenuContent>
156
291
  </DropdownMenu>
157
292
  </div>
158
- </div>
159
- );
160
- },
161
- } satisfies StoryObj;
162
293
 
163
- export const Checkboxes = {
164
- args: {
165
- // DropdownMenu: "Lorem Ipsum",
166
- // value: "Lorem Ipsum",
167
- // fullwidth: true,
168
- },
169
- render: (args) => {
170
- type Checked = DropdownMenuCheckboxItemProps["checked"];
171
- console.log("args ", args);
172
- const props: typeof args = {
173
- ...args,
174
- };
175
- return (
176
- <div className="flex flex-row gap-4 w-full">
177
- <div className="flex flex-1 justify-center items-center space-x-2">
294
+ <div>
295
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
296
+ Interactive
297
+ </p>
178
298
  <DropdownMenu>
179
299
  <DropdownMenuTrigger asChild>
180
- <Button variant="outline">Open</Button>
300
+ <Button variant="outline">Open Menu</Button>
181
301
  </DropdownMenuTrigger>
182
302
  <DropdownMenuContent>
183
- <DropdownMenuCheckboxItem>
303
+ <DropdownMenuCheckboxItem
304
+ checked={checked1}
305
+ onCheckedChange={setChecked1}
306
+ >
184
307
  Option Description
185
308
  </DropdownMenuCheckboxItem>
186
- <DropdownMenuCheckboxItem checked>
309
+ <DropdownMenuCheckboxItem
310
+ checked={checked2}
311
+ onCheckedChange={setChecked2}
312
+ >
187
313
  Option Description
188
314
  </DropdownMenuCheckboxItem>
189
- <DropdownMenuCheckboxItem disabled>
315
+ <DropdownMenuCheckboxItem
316
+ checked={checked3}
317
+ onCheckedChange={setChecked3}
318
+ >
190
319
  Option Description
191
320
  </DropdownMenuCheckboxItem>
192
- <DropdownMenuCheckboxItem checked disabled>
321
+ <DropdownMenuCheckboxItem disabled>
193
322
  Option Description
194
323
  </DropdownMenuCheckboxItem>
195
- <DropdownMenuCheckboxItem>
324
+ </DropdownMenuContent>
325
+ </DropdownMenu>
326
+ </div>
327
+ </div>
328
+ );
329
+ },
330
+ } satisfies StoryObj;
331
+
332
+ // ---------------------------------------------------------------------------
333
+ // Figma: Have section (DropdownMenuLabel as section header)
334
+ // ---------------------------------------------------------------------------
335
+ export const WithSection = {
336
+ name: "Have Section",
337
+ render: () => (
338
+ <div className="flex gap-8 items-start flex-wrap">
339
+ <div>
340
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
341
+ With Section Header
342
+ </p>
343
+ <DropdownMenu>
344
+ <DropdownMenuTrigger asChild>
345
+ <ActionButton variant="icon">
346
+ <Icon type="heroicons" name="ellipsis-vertical" />
347
+ </ActionButton>
348
+ </DropdownMenuTrigger>
349
+ <DropdownMenuContent
350
+ className="relative"
351
+ style={{ position: "static", transform: "none" }}
352
+ >
353
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
354
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
355
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
356
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
357
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
358
+ <DropdownMenuSeparator />
359
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
360
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
361
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
362
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
363
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
364
+ </DropdownMenuContent>
365
+ </DropdownMenu>
366
+ </div>
367
+
368
+ <div>
369
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
370
+ Interactive
371
+ </p>
372
+ <DropdownMenu>
373
+ <DropdownMenuTrigger asChild>
374
+ <Button variant="outline">Open Menu</Button>
375
+ </DropdownMenuTrigger>
376
+ <DropdownMenuContent>
377
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
378
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
379
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
380
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
381
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
382
+ <DropdownMenuSeparator />
383
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
384
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
385
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
386
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
387
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
388
+ </DropdownMenuContent>
389
+ </DropdownMenu>
390
+ </div>
391
+ </div>
392
+ ),
393
+ } satisfies StoryObj;
394
+
395
+ // ---------------------------------------------------------------------------
396
+ // Figma: Have search
397
+ // Dropdown with search input at the top
398
+ // ---------------------------------------------------------------------------
399
+ export const WithSearch = {
400
+ name: "Have Search",
401
+ render: () => {
402
+ const [query, setQuery] = useState("");
403
+ const allOptions = [
404
+ "Option Description",
405
+ "Project Alpha",
406
+ "Project Beta",
407
+ "Project Gamma",
408
+ "Project Delta",
409
+ "Project Epsilon",
410
+ "Project Zeta",
411
+ "Project Eta",
412
+ "Project Theta",
413
+ ];
414
+ const filtered = allOptions.filter((o) =>
415
+ o.toLowerCase().includes(query.toLowerCase()),
416
+ );
417
+
418
+ return (
419
+ <div className="flex gap-8 items-start flex-wrap">
420
+ <div>
421
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
422
+ With Search Field
423
+ </p>
424
+ <DropdownMenu>
425
+ <DropdownMenuTrigger asChild>
426
+ <ActionButton variant="icon">
427
+ <Icon type="heroicons" name="ellipsis-vertical" />
428
+ </ActionButton>
429
+ </DropdownMenuTrigger>
430
+ <DropdownMenuContent
431
+ className="relative w-[230px]"
432
+ style={{ position: "static", transform: "none" }}
433
+ onInteractOutside={(e: Event) => e.preventDefault()}
434
+ >
435
+ <div className="flex items-center px-4 py-2 border-b border-[var(--dropdown-menu-seperator-bg)]">
196
436
  <Icon
197
437
  type="heroicons"
198
- name="rocket-launch"
199
- className="size-4"
438
+ name="magnifying-glass"
439
+ className="size-[14px] shrink-0 text-text-g-contrast-medium mr-1"
200
440
  />
201
- <span>Option Description</span>
202
- </DropdownMenuCheckboxItem>
203
- <DropdownMenuCheckboxItem checked>
204
- <Icon
205
- type="heroicons"
206
- name="rocket-launch"
207
- className="size-4"
441
+ <Input
442
+ variant="flat"
443
+ size="sm"
444
+ className="flex-1 !p-0 !ring-0 typography-small1 placeholder:text-text-g-contrast-medium"
445
+ placeholder="Search"
446
+ value={query}
447
+ onChange={(e) => setQuery(e.target.value)}
208
448
  />
209
- <span>Option Description</span>
210
- </DropdownMenuCheckboxItem>
211
- <DropdownMenuCheckboxItem disabled>
449
+ </div>
450
+ {filtered.map((label) => (
451
+ <DropdownMenuItem key={label}>{label}</DropdownMenuItem>
452
+ ))}
453
+ {filtered.length === 0 && (
454
+ <div className="px-4 py-3 typography-subtitle4 text-text-g-contrast-medium">
455
+ No results found
456
+ </div>
457
+ )}
458
+ </DropdownMenuContent>
459
+ </DropdownMenu>
460
+ </div>
461
+
462
+ <div>
463
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
464
+ Interactive
465
+ </p>
466
+ <DropdownMenu>
467
+ <DropdownMenuTrigger asChild>
468
+ <Button variant="outline">Open Menu</Button>
469
+ </DropdownMenuTrigger>
470
+ <DropdownMenuContent className="w-[230px]">
471
+ <div className="flex items-center px-4 py-2 border-b border-[var(--dropdown-menu-seperator-bg)]">
212
472
  <Icon
213
473
  type="heroicons"
214
- name="rocket-launch"
215
- className="size-4"
474
+ name="magnifying-glass"
475
+ className="size-[14px] shrink-0 text-text-g-contrast-medium mr-1"
216
476
  />
217
- <span>Option Description</span>
218
- </DropdownMenuCheckboxItem>
219
- <DropdownMenuCheckboxItem checked disabled>
220
- <Icon
221
- type="heroicons"
222
- name="rocket-launch"
223
- className="size-4"
477
+ <Input
478
+ variant="flat"
479
+ size="sm"
480
+ className="flex-1 !p-0 !ring-0 typography-small1 placeholder:text-text-g-contrast-medium"
481
+ placeholder="Search"
224
482
  />
225
- <span>Option Description</span>
226
- </DropdownMenuCheckboxItem>
483
+ </div>
484
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
485
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
486
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
487
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
488
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
227
489
  </DropdownMenuContent>
228
490
  </DropdownMenu>
229
491
  </div>
@@ -232,84 +494,282 @@ export const Checkboxes = {
232
494
  },
233
495
  } satisfies StoryObj;
234
496
 
497
+ // ---------------------------------------------------------------------------
498
+ // Figma: Can scroll — long list, overflow-y scrollable
499
+ // ---------------------------------------------------------------------------
500
+ export const CanScroll = {
501
+ name: "Can Scroll",
502
+ render: () => (
503
+ <div className="flex gap-8 items-start flex-wrap">
504
+ <div>
505
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
506
+ Scrollable List
507
+ </p>
508
+ <DropdownMenu>
509
+ <DropdownMenuTrigger asChild>
510
+ <ActionButton variant="icon">
511
+ <Icon type="heroicons" name="ellipsis-vertical" />
512
+ </ActionButton>
513
+ </DropdownMenuTrigger>
514
+ <DropdownMenuContent
515
+ className="relative max-h-[270px] overflow-y-auto"
516
+ style={{ position: "static", transform: "none" }}
517
+ >
518
+ {Array.from({ length: 10 }, (_, i) => (
519
+ <DropdownMenuItem key={i}>
520
+ Option Description {i + 1}
521
+ </DropdownMenuItem>
522
+ ))}
523
+ </DropdownMenuContent>
524
+ </DropdownMenu>
525
+ </div>
526
+
527
+ <div>
528
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
529
+ Interactive
530
+ </p>
531
+ <DropdownMenu>
532
+ <DropdownMenuTrigger asChild>
533
+ <Button variant="outline">Open Menu</Button>
534
+ </DropdownMenuTrigger>
535
+ <DropdownMenuContent className="max-h-[270px] overflow-y-auto">
536
+ {Array.from({ length: 10 }, (_, i) => (
537
+ <DropdownMenuItem key={i}>
538
+ Option Description {i + 1}
539
+ </DropdownMenuItem>
540
+ ))}
541
+ </DropdownMenuContent>
542
+ </DropdownMenu>
543
+ </div>
544
+ </div>
545
+ ),
546
+ } satisfies StoryObj;
547
+
548
+ // ---------------------------------------------------------------------------
549
+ // Figma: Double scroll — two separate scrollable sections with section headers
550
+ // ---------------------------------------------------------------------------
551
+ export const DoubleScroll = {
552
+ name: "Double Scroll",
553
+ render: () => (
554
+ <div className="flex gap-8 items-start flex-wrap">
555
+ <div>
556
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
557
+ Two Scrollable Sections
558
+ </p>
559
+ <DropdownMenu>
560
+ <DropdownMenuTrigger asChild>
561
+ <ActionButton variant="icon">
562
+ <Icon type="heroicons" name="ellipsis-vertical" />
563
+ </ActionButton>
564
+ </DropdownMenuTrigger>
565
+ <DropdownMenuContent
566
+ className="relative"
567
+ style={{ position: "static", transform: "none" }}
568
+ >
569
+ <DropdownMenuLabel>Section A</DropdownMenuLabel>
570
+ <div className="max-h-[270px] overflow-y-auto">
571
+ {Array.from({ length: 5 }, (_, i) => (
572
+ <DropdownMenuItem key={i}>
573
+ Option Description {i + 1}
574
+ </DropdownMenuItem>
575
+ ))}
576
+ </div>
577
+ <DropdownMenuSeparator />
578
+ <DropdownMenuLabel>Section B</DropdownMenuLabel>
579
+ <div className="max-h-[216px] overflow-y-auto">
580
+ {Array.from({ length: 4 }, (_, i) => (
581
+ <DropdownMenuItem key={i}>
582
+ Option Description {i + 1}
583
+ </DropdownMenuItem>
584
+ ))}
585
+ </div>
586
+ </DropdownMenuContent>
587
+ </DropdownMenu>
588
+ </div>
589
+
590
+ <div>
591
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
592
+ Interactive
593
+ </p>
594
+ <DropdownMenu>
595
+ <DropdownMenuTrigger asChild>
596
+ <Button variant="outline">Open Menu</Button>
597
+ </DropdownMenuTrigger>
598
+ <DropdownMenuContent>
599
+ <DropdownMenuLabel>Section A</DropdownMenuLabel>
600
+ <div className="max-h-[270px] overflow-y-auto">
601
+ {Array.from({ length: 5 }, (_, i) => (
602
+ <DropdownMenuItem key={i}>
603
+ Option Description {i + 1}
604
+ </DropdownMenuItem>
605
+ ))}
606
+ </div>
607
+ <DropdownMenuSeparator />
608
+ <DropdownMenuLabel>Section B</DropdownMenuLabel>
609
+ <div className="max-h-[216px] overflow-y-auto">
610
+ {Array.from({ length: 4 }, (_, i) => (
611
+ <DropdownMenuItem key={i}>
612
+ Option Description {i + 1}
613
+ </DropdownMenuItem>
614
+ ))}
615
+ </div>
616
+ </DropdownMenuContent>
617
+ </DropdownMenu>
618
+ </div>
619
+ </div>
620
+ ),
621
+ } satisfies StoryObj;
622
+
623
+ // ---------------------------------------------------------------------------
624
+ // Figma: Sub menu (existing, preserved)
625
+ // ---------------------------------------------------------------------------
235
626
  export const SubMenu = {
236
- args: {
237
- // DropdownMenu: "Lorem Ipsum",
238
- // value: "Lorem Ipsum",
239
- // fullwidth: true,
240
- },
241
- render: (args) => {
242
- console.log("args ", args);
243
- const props: typeof args = {
244
- ...args,
245
- };
627
+ name: "Sub Menu",
628
+ render: () => (
629
+ <div className="flex gap-8 items-center flex-wrap">
630
+ <DropdownMenu>
631
+ <DropdownMenuTrigger asChild>
632
+ <Button variant="outline">Open Menu</Button>
633
+ </DropdownMenuTrigger>
634
+ <DropdownMenuContent>
635
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
636
+ <DropdownMenuSeparator />
637
+ <DropdownMenuItem>Profile</DropdownMenuItem>
638
+ <DropdownMenuItem>Billing</DropdownMenuItem>
639
+ <DropdownMenuItem>Team</DropdownMenuItem>
640
+ <DropdownMenuSeparator />
641
+ <DropdownMenuGroup>
642
+ <DropdownMenuSub>
643
+ <DropdownMenuSubTrigger>
644
+ <Icon
645
+ type="heroicons"
646
+ name="user-group"
647
+ className="size-4 shrink-0"
648
+ />
649
+ Invite users
650
+ </DropdownMenuSubTrigger>
651
+ <DropdownMenuPortal>
652
+ <DropdownMenuSubContent>
653
+ <DropdownMenuItem>Email</DropdownMenuItem>
654
+ <DropdownMenuItem>Message</DropdownMenuItem>
655
+ <DropdownMenuSeparator />
656
+ <DropdownMenuItem>More...</DropdownMenuItem>
657
+ </DropdownMenuSubContent>
658
+ </DropdownMenuPortal>
659
+ </DropdownMenuSub>
660
+ <DropdownMenuCheckboxItem>Panel</DropdownMenuCheckboxItem>
661
+ </DropdownMenuGroup>
662
+ </DropdownMenuContent>
663
+ </DropdownMenu>
664
+ </div>
665
+ ),
666
+ } satisfies StoryObj;
667
+
668
+ // ---------------------------------------------------------------------------
669
+ // Figma: Radio items — single select via DropdownMenuRadioGroup
670
+ // ---------------------------------------------------------------------------
671
+ export const WithRadio = {
672
+ name: "With Radio",
673
+ render: () => {
674
+ const [theme, setTheme] = useState("light");
675
+ const [position, setPosition] = useState("top");
676
+
246
677
  return (
247
- <div className="flex flex-row gap-4 w-full">
248
- <div className="flex flex-1 justify-center items-center space-x-2">
678
+ <div className="flex gap-8 items-start flex-wrap">
679
+ <div>
680
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
681
+ All States — Radio
682
+ </p>
249
683
  <DropdownMenu>
250
- <DropdownMenuTrigger>Open</DropdownMenuTrigger>
684
+ <DropdownMenuTrigger asChild>
685
+ <ActionButton variant="icon">
686
+ <Icon type="heroicons" name="ellipsis-vertical" />
687
+ </ActionButton>
688
+ </DropdownMenuTrigger>
689
+ <DropdownMenuContent
690
+ className="relative"
691
+ style={{ position: "static", transform: "none" }}
692
+ >
693
+ <DropdownMenuRadioGroup value="light">
694
+ <DropdownMenuRadioItem value="unselected">
695
+ Option Description
696
+ </DropdownMenuRadioItem>
697
+ <DropdownMenuRadioItem
698
+ value="unselected2"
699
+ className="!bg-[var(--dropdown-menu-hover-bg)]"
700
+ >
701
+ Option Description (Hover)
702
+ </DropdownMenuRadioItem>
703
+ <DropdownMenuRadioItem value="light">
704
+ Option Description (Selected)
705
+ </DropdownMenuRadioItem>
706
+ <DropdownMenuRadioItem value="dis" disabled>
707
+ Option Description (Disabled)
708
+ </DropdownMenuRadioItem>
709
+ <DropdownMenuRadioItem value="light" disabled>
710
+ Option Description (Selected + Disabled)
711
+ </DropdownMenuRadioItem>
712
+ </DropdownMenuRadioGroup>
713
+ </DropdownMenuContent>
714
+ </DropdownMenu>
715
+ </div>
716
+
717
+ <div>
718
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
719
+ Interactive — Theme
720
+ </p>
721
+ <DropdownMenu>
722
+ <DropdownMenuTrigger asChild>
723
+ <Button variant="outline">Theme: {theme}</Button>
724
+ </DropdownMenuTrigger>
251
725
  <DropdownMenuContent>
252
- <DropdownMenuLabel>My Account</DropdownMenuLabel>
253
- <DropdownMenuSeparator />
254
- <DropdownMenuItem>Profile</DropdownMenuItem>
255
- <DropdownMenuItem>Billing</DropdownMenuItem>
256
- <DropdownMenuItem>Team</DropdownMenuItem>
257
- <DropdownMenuItem>Subscription</DropdownMenuItem>
258
- <DropdownMenuSeparator />
259
- <DropdownMenuGroup>
260
- <DropdownMenuSub>
261
- <DropdownMenuSubTrigger>
262
- <span>Invite users</span>
263
- </DropdownMenuSubTrigger>
264
- <DropdownMenuPortal>
265
- <DropdownMenuSubContent>
266
- <DropdownMenuItem>
267
- <span>Email</span>
268
- </DropdownMenuItem>
269
- <DropdownMenuItem>
270
- <span>Message</span>
271
- </DropdownMenuItem>
272
- <DropdownMenuSeparator />
273
- <DropdownMenuItem>
274
- <span>More...</span>
275
- </DropdownMenuItem>
276
- </DropdownMenuSubContent>
277
- </DropdownMenuPortal>
278
- </DropdownMenuSub>
279
- <DropdownMenuCheckboxItem>Panel</DropdownMenuCheckboxItem>
280
- </DropdownMenuGroup>
726
+ <DropdownMenuLabel>Theme</DropdownMenuLabel>
727
+ <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
728
+ <DropdownMenuRadioItem value="light">
729
+ Light
730
+ </DropdownMenuRadioItem>
731
+ <DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem>
732
+ <DropdownMenuRadioItem value="system">
733
+ System
734
+ </DropdownMenuRadioItem>
735
+ </DropdownMenuRadioGroup>
281
736
  </DropdownMenuContent>
282
737
  </DropdownMenu>
283
738
  </div>
284
- </div>
285
- );
286
- },
287
- } satisfies StoryObj;
288
739
 
289
- export const OnIcon = {
290
- args: {
291
- // DropdownMenu: "Lorem Ipsum",
292
- // value: "Lorem Ipsum",
293
- // fullwidth: true,
294
- },
295
- render: (args) => {
296
- console.log("args ", args);
297
- const props: typeof args = {
298
- ...args,
299
- };
300
- return (
301
- <div className="flex flex-row gap-4 w-full">
302
- <div className="flex flex-1 justify-center items-center space-x-2">
740
+ <div>
741
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
742
+ Interactive Multiple Groups
743
+ </p>
303
744
  <DropdownMenu>
304
- <DropdownMenuTrigger>
305
- <Icon type="heroicons" name="ellipsis-vertical" />
745
+ <DropdownMenuTrigger asChild>
746
+ <Button variant="outline">Open Menu</Button>
306
747
  </DropdownMenuTrigger>
307
748
  <DropdownMenuContent>
308
- <DropdownMenuItem>Option Description</DropdownMenuItem>
309
- <DropdownMenuItem>Option Description</DropdownMenuItem>
310
- <DropdownMenuItem>Option Description</DropdownMenuItem>
311
- <DropdownMenuItem>Option Description</DropdownMenuItem>
312
- <DropdownMenuItem>Option Description</DropdownMenuItem>
749
+ <DropdownMenuLabel>Theme</DropdownMenuLabel>
750
+ <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
751
+ <DropdownMenuRadioItem value="light">
752
+ Light
753
+ </DropdownMenuRadioItem>
754
+ <DropdownMenuRadioItem value="dark">Dark</DropdownMenuRadioItem>
755
+ <DropdownMenuRadioItem value="system">
756
+ System
757
+ </DropdownMenuRadioItem>
758
+ </DropdownMenuRadioGroup>
759
+ <DropdownMenuSeparator />
760
+ <DropdownMenuLabel>Position</DropdownMenuLabel>
761
+ <DropdownMenuRadioGroup
762
+ value={position}
763
+ onValueChange={setPosition}
764
+ >
765
+ <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
766
+ <DropdownMenuRadioItem value="bottom">
767
+ Bottom
768
+ </DropdownMenuRadioItem>
769
+ <DropdownMenuRadioItem value="right">
770
+ Right
771
+ </DropdownMenuRadioItem>
772
+ </DropdownMenuRadioGroup>
313
773
  </DropdownMenuContent>
314
774
  </DropdownMenu>
315
775
  </div>
@@ -318,32 +778,160 @@ export const OnIcon = {
318
778
  },
319
779
  } satisfies StoryObj;
320
780
 
321
- export const AsChild = {
322
- args: {
323
- // DropdownMenu: "Lorem Ipsum",
324
- // value: "Lorem Ipsum",
325
- // fullwidth: true,
326
- },
327
- render: (args) => {
328
- console.log("args ", args);
329
- const props: typeof args = {
330
- ...args,
331
- };
781
+ // ---------------------------------------------------------------------------
782
+ // Radio with Icon — single select with leading icon via icon prop
783
+ // ---------------------------------------------------------------------------
784
+ export const WithRadioAndIcon = {
785
+ name: "With Radio + Icon",
786
+ render: () => {
787
+ const [theme, setTheme] = useState("dark");
788
+
332
789
  return (
333
- <div className="flex flex-row gap-4 w-full">
334
- <div className="flex flex-1 justify-center items-center space-x-2">
790
+ <div className="flex gap-8 items-start flex-wrap">
791
+ <div>
792
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
793
+ All States — Radio + Icon
794
+ </p>
335
795
  <DropdownMenu>
336
796
  <DropdownMenuTrigger asChild>
337
797
  <ActionButton variant="icon">
338
798
  <Icon type="heroicons" name="ellipsis-vertical" />
339
799
  </ActionButton>
340
800
  </DropdownMenuTrigger>
801
+ <DropdownMenuContent
802
+ className="relative"
803
+ style={{ position: "static", transform: "none" }}
804
+ >
805
+ <DropdownMenuRadioGroup value="dark">
806
+ <DropdownMenuRadioItem
807
+ value="light"
808
+ icon={
809
+ <Icon
810
+ type="heroicons"
811
+ name="sun"
812
+ className="size-4 shrink-0"
813
+ />
814
+ }
815
+ >
816
+ Light
817
+ </DropdownMenuRadioItem>
818
+ <DropdownMenuRadioItem
819
+ value="system"
820
+ className="!bg-[var(--dropdown-menu-hover-bg)]"
821
+ icon={
822
+ <Icon
823
+ type="heroicons"
824
+ name="computer-desktop"
825
+ className="size-4 shrink-0"
826
+ />
827
+ }
828
+ >
829
+ System (Hover)
830
+ </DropdownMenuRadioItem>
831
+ <DropdownMenuRadioItem
832
+ value="dark"
833
+ icon={
834
+ <Icon
835
+ type="heroicons"
836
+ name="moon"
837
+ className="size-4 shrink-0"
838
+ />
839
+ }
840
+ >
841
+ Dark (Selected)
842
+ </DropdownMenuRadioItem>
843
+ <DropdownMenuRadioItem
844
+ value="custom"
845
+ disabled
846
+ icon={
847
+ <Icon
848
+ type="heroicons"
849
+ name="paint-brush"
850
+ className="size-4 shrink-0"
851
+ />
852
+ }
853
+ >
854
+ Custom (Disabled)
855
+ </DropdownMenuRadioItem>
856
+ <DropdownMenuRadioItem
857
+ value="dark"
858
+ disabled
859
+ icon={
860
+ <Icon
861
+ type="heroicons"
862
+ name="moon"
863
+ className="size-4 shrink-0"
864
+ />
865
+ }
866
+ >
867
+ Dark (Selected + Disabled)
868
+ </DropdownMenuRadioItem>
869
+ </DropdownMenuRadioGroup>
870
+ </DropdownMenuContent>
871
+ </DropdownMenu>
872
+ </div>
873
+
874
+ <div>
875
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
876
+ Interactive
877
+ </p>
878
+ <DropdownMenu>
879
+ <DropdownMenuTrigger asChild>
880
+ <Button variant="outline">Theme: {theme}</Button>
881
+ </DropdownMenuTrigger>
341
882
  <DropdownMenuContent>
342
- <DropdownMenuItem>Option Description</DropdownMenuItem>
343
- <DropdownMenuItem>Option Description</DropdownMenuItem>
344
- <DropdownMenuItem>Option Description</DropdownMenuItem>
345
- <DropdownMenuItem>Option Description</DropdownMenuItem>
346
- <DropdownMenuItem>Option Description</DropdownMenuItem>
883
+ <DropdownMenuLabel>Theme</DropdownMenuLabel>
884
+ <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
885
+ <DropdownMenuRadioItem
886
+ value="light"
887
+ icon={
888
+ <Icon
889
+ type="heroicons"
890
+ name="sun"
891
+ className="size-4 shrink-0"
892
+ />
893
+ }
894
+ >
895
+ Light
896
+ </DropdownMenuRadioItem>
897
+ <DropdownMenuRadioItem
898
+ value="dark"
899
+ icon={
900
+ <Icon
901
+ type="heroicons"
902
+ name="moon"
903
+ className="size-4 shrink-0"
904
+ />
905
+ }
906
+ >
907
+ Dark
908
+ </DropdownMenuRadioItem>
909
+ <DropdownMenuRadioItem
910
+ value="system"
911
+ icon={
912
+ <Icon
913
+ type="heroicons"
914
+ name="computer-desktop"
915
+ className="size-4 shrink-0"
916
+ />
917
+ }
918
+ >
919
+ System
920
+ </DropdownMenuRadioItem>
921
+ <DropdownMenuRadioItem
922
+ value="custom"
923
+ disabled
924
+ icon={
925
+ <Icon
926
+ type="heroicons"
927
+ name="paint-brush"
928
+ className="size-4"
929
+ />
930
+ }
931
+ >
932
+ Custom
933
+ </DropdownMenuRadioItem>
934
+ </DropdownMenuRadioGroup>
347
935
  </DropdownMenuContent>
348
936
  </DropdownMenu>
349
937
  </div>
@@ -352,21 +940,230 @@ export const AsChild = {
352
940
  },
353
941
  } satisfies StoryObj;
354
942
 
355
- export const Position = {
356
- args: {
357
- side: "bottom",
358
- align: "center",
359
- sideOffset: 5,
360
- alignOffset: 0,
361
- },
362
- render: (args) => {
363
- console.log("args ", args);
364
- const props: typeof args = {
365
- ...args,
366
- };
943
+ // ---------------------------------------------------------------------------
944
+ // DropdownMenuShortcut — keyboard shortcut label on the right side of item
945
+ // ---------------------------------------------------------------------------
946
+ export const WithShortcut = {
947
+ name: "With Shortcut",
948
+ render: () => (
949
+ <div className="flex gap-8 items-start flex-wrap">
950
+ <div>
951
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
952
+ All States Shortcut
953
+ </p>
954
+ <DropdownMenu>
955
+ <DropdownMenuTrigger asChild>
956
+ <ActionButton variant="icon">
957
+ <Icon type="heroicons" name="ellipsis-vertical" />
958
+ </ActionButton>
959
+ </DropdownMenuTrigger>
960
+ <DropdownMenuContent
961
+ className="relative"
962
+ style={{ position: "static", transform: "none" }}
963
+ >
964
+ <DropdownMenuItem>
965
+ New Tab
966
+ <DropdownMenuShortcut>⌘T</DropdownMenuShortcut>
967
+ </DropdownMenuItem>
968
+ <DropdownMenuItem className="!bg-[var(--dropdown-menu-hover-bg)]">
969
+ New Window (Hover)
970
+ <DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
971
+ </DropdownMenuItem>
972
+ <DropdownMenuSeparator />
973
+ <DropdownMenuItem>
974
+ <Icon
975
+ type="heroicons"
976
+ name="pencil"
977
+ className="size-4 shrink-0"
978
+ />
979
+ Edit
980
+ <DropdownMenuShortcut>⌘E</DropdownMenuShortcut>
981
+ </DropdownMenuItem>
982
+ <DropdownMenuItem>
983
+ <Icon
984
+ type="heroicons"
985
+ name="document-duplicate"
986
+ className="size-4 shrink-0"
987
+ />
988
+ Duplicate
989
+ <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
990
+ </DropdownMenuItem>
991
+ <DropdownMenuSeparator />
992
+ <DropdownMenuItem disabled>
993
+ <Icon type="heroicons" name="trash" className="size-4 shrink-0" />
994
+ Delete
995
+ <DropdownMenuShortcut>⌫</DropdownMenuShortcut>
996
+ </DropdownMenuItem>
997
+ </DropdownMenuContent>
998
+ </DropdownMenu>
999
+ </div>
1000
+
1001
+ <div>
1002
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1003
+ Interactive
1004
+ </p>
1005
+ <DropdownMenu>
1006
+ <DropdownMenuTrigger asChild>
1007
+ <Button variant="outline">Open Menu</Button>
1008
+ </DropdownMenuTrigger>
1009
+ <DropdownMenuContent className="w-52">
1010
+ <DropdownMenuLabel>Actions</DropdownMenuLabel>
1011
+ <DropdownMenuSeparator />
1012
+ <DropdownMenuItem>
1013
+ New Tab
1014
+ <DropdownMenuShortcut>⌘T</DropdownMenuShortcut>
1015
+ </DropdownMenuItem>
1016
+ <DropdownMenuItem>
1017
+ New Window
1018
+ <DropdownMenuShortcut>⌘N</DropdownMenuShortcut>
1019
+ </DropdownMenuItem>
1020
+ <DropdownMenuSeparator />
1021
+ <DropdownMenuItem>
1022
+ <Icon
1023
+ type="heroicons"
1024
+ name="pencil"
1025
+ className="size-4 shrink-0"
1026
+ />
1027
+ Edit
1028
+ <DropdownMenuShortcut>⌘E</DropdownMenuShortcut>
1029
+ </DropdownMenuItem>
1030
+ <DropdownMenuItem>
1031
+ <Icon
1032
+ type="heroicons"
1033
+ name="document-duplicate"
1034
+ className="size-4 shrink-0"
1035
+ />
1036
+ Duplicate
1037
+ <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
1038
+ </DropdownMenuItem>
1039
+ <DropdownMenuSeparator />
1040
+ <DropdownMenuItem disabled>
1041
+ <Icon type="heroicons" name="trash" className="size-4 shrink-0" />
1042
+ Delete
1043
+ <DropdownMenuShortcut>⌫</DropdownMenuShortcut>
1044
+ </DropdownMenuItem>
1045
+ </DropdownMenuContent>
1046
+ </DropdownMenu>
1047
+ </div>
1048
+ </div>
1049
+ ),
1050
+ } satisfies StoryObj;
1051
+
1052
+ // ---------------------------------------------------------------------------
1053
+ // Trigger variants (on icon button, etc.)
1054
+ // ---------------------------------------------------------------------------
1055
+ export const TriggerVariants = {
1056
+ name: "Trigger Variants",
1057
+ render: () => (
1058
+ <div className="flex gap-8 items-center flex-wrap">
1059
+ <div>
1060
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1061
+ Text Trigger
1062
+ </p>
1063
+ <DropdownMenu>
1064
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
1065
+ <DropdownMenuContent>
1066
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1067
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1068
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1069
+ </DropdownMenuContent>
1070
+ </DropdownMenu>
1071
+ </div>
1072
+
1073
+ <div>
1074
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1075
+ Button Trigger
1076
+ </p>
1077
+ <DropdownMenu>
1078
+ <DropdownMenuTrigger asChild>
1079
+ <Button variant="outline">Open Menu</Button>
1080
+ </DropdownMenuTrigger>
1081
+ <DropdownMenuContent>
1082
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1083
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1084
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1085
+ </DropdownMenuContent>
1086
+ </DropdownMenu>
1087
+ </div>
1088
+
1089
+ <div>
1090
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1091
+ Icon Button Trigger
1092
+ </p>
1093
+ <DropdownMenu>
1094
+ <DropdownMenuTrigger asChild>
1095
+ <ActionButton variant="icon">
1096
+ <Icon type="heroicons" name="ellipsis-vertical" />
1097
+ </ActionButton>
1098
+ </DropdownMenuTrigger>
1099
+ <DropdownMenuContent>
1100
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1101
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1102
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
1103
+ </DropdownMenuContent>
1104
+ </DropdownMenu>
1105
+ </div>
1106
+
1107
+ <div>
1108
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1109
+ Horizontal dots
1110
+ </p>
1111
+ <DropdownMenu>
1112
+ <DropdownMenuTrigger asChild>
1113
+ <ActionButton variant="icon">
1114
+ <Icon type="heroicons" name="ellipsis-horizontal" />
1115
+ </ActionButton>
1116
+ </DropdownMenuTrigger>
1117
+ <DropdownMenuContent>
1118
+ <DropdownMenuItem>
1119
+ <Icon
1120
+ type="heroicons"
1121
+ name="pencil"
1122
+ className="size-4 shrink-0"
1123
+ />
1124
+ Edit
1125
+ </DropdownMenuItem>
1126
+ <DropdownMenuItem>
1127
+ <Icon
1128
+ type="heroicons"
1129
+ name="document-duplicate"
1130
+ className="size-4 shrink-0"
1131
+ />
1132
+ Duplicate
1133
+ </DropdownMenuItem>
1134
+ <DropdownMenuSeparator />
1135
+ <DropdownMenuItem disabled>
1136
+ <Icon type="heroicons" name="trash" className="size-4 shrink-0" />
1137
+ Delete
1138
+ </DropdownMenuItem>
1139
+ </DropdownMenuContent>
1140
+ </DropdownMenu>
1141
+ </div>
1142
+ </div>
1143
+ ),
1144
+ } satisfies StoryObj;
1145
+
1146
+ // ---------------------------------------------------------------------------
1147
+ // Complex showcase — real-world patterns combining all item types
1148
+ // ---------------------------------------------------------------------------
1149
+ export const ComplexShowcase = {
1150
+ name: "Complex Showcase",
1151
+ render: () => {
1152
+ const [theme, setTheme] = useState("dark");
1153
+ const [density, setDensity] = useState("comfortable");
1154
+ const [sortBy, setSortBy] = useState("name");
1155
+ const [sortDir, setSortDir] = useState("asc");
1156
+ const [showHidden, setShowHidden] = useState(false);
1157
+ const [showExtensions, setShowExtensions] = useState(true);
1158
+ const [showPreview, setShowPreview] = useState(true);
1159
+
367
1160
  return (
368
- <div className="flex flex-row gap-4 w-full">
369
- <div className="flex flex-1 justify-center items-center space-x-2">
1161
+ <div className="flex gap-8 items-start flex-wrap">
1162
+ {/* Static: File manager MenuItem + RadioGroup + CheckboxItem */}
1163
+ <div>
1164
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1165
+ File Manager Actions
1166
+ </p>
370
1167
  <DropdownMenu>
371
1168
  <DropdownMenuTrigger asChild>
372
1169
  <ActionButton variant="icon">
@@ -374,17 +1171,342 @@ export const Position = {
374
1171
  </ActionButton>
375
1172
  </DropdownMenuTrigger>
376
1173
  <DropdownMenuContent
377
- {...args}
378
- side="bottom" // controls the side (top, bottom, left, right)
379
- align="start" // controls the alignment (start, center, end)
380
- sideOffset={5} // offset from the side
381
- alignOffset={0} // offset from the alignment
1174
+ className="relative w-[220px]"
1175
+ style={{ position: "static", transform: "none" }}
382
1176
  >
383
- <DropdownMenuItem>Option Description</DropdownMenuItem>
384
- <DropdownMenuItem>Option Description</DropdownMenuItem>
385
- <DropdownMenuItem>Option Description</DropdownMenuItem>
386
- <DropdownMenuItem>Option Description</DropdownMenuItem>
387
- <DropdownMenuItem>Option Description</DropdownMenuItem>
1177
+ <DropdownMenuItem
1178
+ icon={
1179
+ <Icon
1180
+ type="heroicons"
1181
+ name="pencil"
1182
+ className="size-4 shrink-0"
1183
+ />
1184
+ }
1185
+ >
1186
+ Rename
1187
+ </DropdownMenuItem>
1188
+ <DropdownMenuItem
1189
+ icon={
1190
+ <Icon
1191
+ type="heroicons"
1192
+ name="document-duplicate"
1193
+ className="size-4 shrink-0"
1194
+ />
1195
+ }
1196
+ >
1197
+ Duplicate
1198
+ </DropdownMenuItem>
1199
+ <DropdownMenuSeparator />
1200
+ <DropdownMenuLabel>Sort By</DropdownMenuLabel>
1201
+ <DropdownMenuRadioGroup value="name">
1202
+ <DropdownMenuRadioItem value="name">Name</DropdownMenuRadioItem>
1203
+ <DropdownMenuRadioItem value="date">
1204
+ Date Modified
1205
+ </DropdownMenuRadioItem>
1206
+ <DropdownMenuRadioItem value="size">Size</DropdownMenuRadioItem>
1207
+ </DropdownMenuRadioGroup>
1208
+ <DropdownMenuSeparator />
1209
+ <DropdownMenuLabel>Show</DropdownMenuLabel>
1210
+ <DropdownMenuCheckboxItem>Hidden Files</DropdownMenuCheckboxItem>
1211
+ <DropdownMenuCheckboxItem checked>
1212
+ File Extensions
1213
+ </DropdownMenuCheckboxItem>
1214
+ <DropdownMenuCheckboxItem checked>
1215
+ Preview Panel
1216
+ </DropdownMenuCheckboxItem>
1217
+ <DropdownMenuSeparator />
1218
+ <DropdownMenuItem
1219
+ disabled
1220
+ icon={
1221
+ <Icon
1222
+ type="heroicons"
1223
+ name="trash"
1224
+ className="size-4 shrink-0"
1225
+ />
1226
+ }
1227
+ >
1228
+ Delete
1229
+ </DropdownMenuItem>
1230
+ </DropdownMenuContent>
1231
+ </DropdownMenu>
1232
+ </div>
1233
+
1234
+ {/* Static: Editor settings — multiple RadioGroups per section */}
1235
+ <div>
1236
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1237
+ Editor Settings (Multiple RadioGroups)
1238
+ </p>
1239
+ <DropdownMenu>
1240
+ <DropdownMenuTrigger asChild>
1241
+ <ActionButton variant="icon">
1242
+ <Icon type="heroicons" name="cog-6-tooth" />
1243
+ </ActionButton>
1244
+ </DropdownMenuTrigger>
1245
+ <DropdownMenuContent
1246
+ className="relative w-[220px]"
1247
+ style={{ position: "static", transform: "none" }}
1248
+ >
1249
+ <DropdownMenuLabel>Theme</DropdownMenuLabel>
1250
+ <DropdownMenuRadioGroup value="dark">
1251
+ <DropdownMenuRadioItem
1252
+ value="light"
1253
+ icon={
1254
+ <Icon
1255
+ type="heroicons"
1256
+ name="sun"
1257
+ className="size-4 shrink-0"
1258
+ />
1259
+ }
1260
+ >
1261
+ Light
1262
+ </DropdownMenuRadioItem>
1263
+ <DropdownMenuRadioItem
1264
+ value="dark"
1265
+ icon={
1266
+ <Icon
1267
+ type="heroicons"
1268
+ name="moon"
1269
+ className="size-4 shrink-0"
1270
+ />
1271
+ }
1272
+ >
1273
+ Dark
1274
+ </DropdownMenuRadioItem>
1275
+ <DropdownMenuRadioItem
1276
+ value="system"
1277
+ icon={
1278
+ <Icon
1279
+ type="heroicons"
1280
+ name="computer-desktop"
1281
+ className="size-4 shrink-0"
1282
+ />
1283
+ }
1284
+ >
1285
+ System
1286
+ </DropdownMenuRadioItem>
1287
+ </DropdownMenuRadioGroup>
1288
+ <DropdownMenuSeparator />
1289
+ <DropdownMenuLabel>Density</DropdownMenuLabel>
1290
+ <DropdownMenuRadioGroup value="comfortable">
1291
+ <DropdownMenuRadioItem value="compact">
1292
+ Compact
1293
+ </DropdownMenuRadioItem>
1294
+ <DropdownMenuRadioItem value="comfortable">
1295
+ Comfortable
1296
+ </DropdownMenuRadioItem>
1297
+ <DropdownMenuRadioItem value="spacious">
1298
+ Spacious
1299
+ </DropdownMenuRadioItem>
1300
+ </DropdownMenuRadioGroup>
1301
+ <DropdownMenuSeparator />
1302
+ <DropdownMenuLabel>Sort Direction</DropdownMenuLabel>
1303
+ <DropdownMenuRadioGroup value="asc">
1304
+ <DropdownMenuRadioItem
1305
+ value="asc"
1306
+ icon={
1307
+ <Icon
1308
+ type="heroicons"
1309
+ name="bars-arrow-up"
1310
+ className="size-4 shrink-0"
1311
+ />
1312
+ }
1313
+ >
1314
+ Ascending
1315
+ </DropdownMenuRadioItem>
1316
+ <DropdownMenuRadioItem
1317
+ value="desc"
1318
+ icon={
1319
+ <Icon
1320
+ type="heroicons"
1321
+ name="bars-arrow-down"
1322
+ className="size-4 shrink-0"
1323
+ />
1324
+ }
1325
+ >
1326
+ Descending
1327
+ </DropdownMenuRadioItem>
1328
+ </DropdownMenuRadioGroup>
1329
+ </DropdownMenuContent>
1330
+ </DropdownMenu>
1331
+ </div>
1332
+
1333
+ {/* Interactive: File Manager */}
1334
+ <div>
1335
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1336
+ Interactive — File Manager
1337
+ </p>
1338
+ <DropdownMenu>
1339
+ <DropdownMenuTrigger asChild>
1340
+ <Button variant="outline">Options</Button>
1341
+ </DropdownMenuTrigger>
1342
+ <DropdownMenuContent className="w-[220px]">
1343
+ <DropdownMenuItem
1344
+ icon={
1345
+ <Icon
1346
+ type="heroicons"
1347
+ name="pencil"
1348
+ className="size-4 shrink-0"
1349
+ />
1350
+ }
1351
+ >
1352
+ Rename
1353
+ </DropdownMenuItem>
1354
+ <DropdownMenuItem
1355
+ icon={
1356
+ <Icon
1357
+ type="heroicons"
1358
+ name="document-duplicate"
1359
+ className="size-4 shrink-0"
1360
+ />
1361
+ }
1362
+ >
1363
+ Duplicate
1364
+ </DropdownMenuItem>
1365
+ <DropdownMenuSeparator />
1366
+ <DropdownMenuLabel>Sort By</DropdownMenuLabel>
1367
+ <DropdownMenuRadioGroup value={sortBy} onValueChange={setSortBy}>
1368
+ <DropdownMenuRadioItem value="name">Name</DropdownMenuRadioItem>
1369
+ <DropdownMenuRadioItem value="date">
1370
+ Date Modified
1371
+ </DropdownMenuRadioItem>
1372
+ <DropdownMenuRadioItem value="size">Size</DropdownMenuRadioItem>
1373
+ </DropdownMenuRadioGroup>
1374
+ <DropdownMenuSeparator />
1375
+ <DropdownMenuLabel>Show</DropdownMenuLabel>
1376
+ <DropdownMenuCheckboxItem
1377
+ checked={showHidden}
1378
+ onCheckedChange={setShowHidden}
1379
+ onSelect={(e) => e.preventDefault()}
1380
+ >
1381
+ Hidden Files
1382
+ </DropdownMenuCheckboxItem>
1383
+ <DropdownMenuCheckboxItem
1384
+ checked={showExtensions}
1385
+ onCheckedChange={setShowExtensions}
1386
+ onSelect={(e) => e.preventDefault()}
1387
+ >
1388
+ File Extensions
1389
+ </DropdownMenuCheckboxItem>
1390
+ <DropdownMenuCheckboxItem
1391
+ checked={showPreview}
1392
+ onCheckedChange={setShowPreview}
1393
+ onSelect={(e) => e.preventDefault()}
1394
+ >
1395
+ Preview Panel
1396
+ </DropdownMenuCheckboxItem>
1397
+ <DropdownMenuSeparator />
1398
+ <DropdownMenuItem
1399
+ disabled
1400
+ icon={
1401
+ <Icon
1402
+ type="heroicons"
1403
+ name="trash"
1404
+ className="size-4 shrink-0"
1405
+ />
1406
+ }
1407
+ >
1408
+ Delete
1409
+ </DropdownMenuItem>
1410
+ </DropdownMenuContent>
1411
+ </DropdownMenu>
1412
+ </div>
1413
+
1414
+ {/* Interactive: Editor Settings */}
1415
+ <div>
1416
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">
1417
+ Interactive — Editor Settings
1418
+ </p>
1419
+ <DropdownMenu>
1420
+ <DropdownMenuTrigger asChild>
1421
+ <Button variant="outline">Settings</Button>
1422
+ </DropdownMenuTrigger>
1423
+ <DropdownMenuContent className="w-[220px]">
1424
+ <DropdownMenuLabel>Theme</DropdownMenuLabel>
1425
+ <DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
1426
+ <DropdownMenuRadioItem
1427
+ value="light"
1428
+ icon={
1429
+ <Icon
1430
+ type="heroicons"
1431
+ name="sun"
1432
+ className="size-4 shrink-0"
1433
+ />
1434
+ }
1435
+ >
1436
+ Light
1437
+ </DropdownMenuRadioItem>
1438
+ <DropdownMenuRadioItem
1439
+ value="dark"
1440
+ icon={
1441
+ <Icon
1442
+ type="heroicons"
1443
+ name="moon"
1444
+ className="size-4 shrink-0"
1445
+ />
1446
+ }
1447
+ >
1448
+ Dark
1449
+ </DropdownMenuRadioItem>
1450
+ <DropdownMenuRadioItem
1451
+ value="system"
1452
+ icon={
1453
+ <Icon
1454
+ type="heroicons"
1455
+ name="computer-desktop"
1456
+ className="size-4 shrink-0"
1457
+ />
1458
+ }
1459
+ >
1460
+ System
1461
+ </DropdownMenuRadioItem>
1462
+ </DropdownMenuRadioGroup>
1463
+ <DropdownMenuSeparator />
1464
+ <DropdownMenuLabel>Density</DropdownMenuLabel>
1465
+ <DropdownMenuRadioGroup
1466
+ value={density}
1467
+ onValueChange={setDensity}
1468
+ >
1469
+ <DropdownMenuRadioItem value="compact">
1470
+ Compact
1471
+ </DropdownMenuRadioItem>
1472
+ <DropdownMenuRadioItem value="comfortable">
1473
+ Comfortable
1474
+ </DropdownMenuRadioItem>
1475
+ <DropdownMenuRadioItem value="spacious">
1476
+ Spacious
1477
+ </DropdownMenuRadioItem>
1478
+ </DropdownMenuRadioGroup>
1479
+ <DropdownMenuSeparator />
1480
+ <DropdownMenuLabel>Sort Direction</DropdownMenuLabel>
1481
+ <DropdownMenuRadioGroup
1482
+ value={sortDir}
1483
+ onValueChange={setSortDir}
1484
+ >
1485
+ <DropdownMenuRadioItem
1486
+ value="asc"
1487
+ icon={
1488
+ <Icon
1489
+ type="heroicons"
1490
+ name="bars-arrow-up"
1491
+ className="size-4 shrink-0"
1492
+ />
1493
+ }
1494
+ >
1495
+ Ascending
1496
+ </DropdownMenuRadioItem>
1497
+ <DropdownMenuRadioItem
1498
+ value="desc"
1499
+ icon={
1500
+ <Icon
1501
+ type="heroicons"
1502
+ name="bars-arrow-down"
1503
+ className="size-4 shrink-0"
1504
+ />
1505
+ }
1506
+ >
1507
+ Descending
1508
+ </DropdownMenuRadioItem>
1509
+ </DropdownMenuRadioGroup>
388
1510
  </DropdownMenuContent>
389
1511
  </DropdownMenu>
390
1512
  </div>