@rovula/ui 0.1.20 → 0.1.21

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 (42) hide show
  1. package/dist/cjs/bundle.css +141 -17
  2. package/dist/cjs/bundle.js +3 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/DropdownMenu/DropdownMenu.stories.d.ts +29 -30
  5. package/dist/cjs/types/components/Form/Form.d.ts +2 -1
  6. package/dist/cjs/types/components/Form/Form.stories.d.ts +4 -0
  7. package/dist/cjs/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  8. package/dist/cjs/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  9. package/dist/cjs/types/index.d.ts +1 -0
  10. package/dist/components/DropdownMenu/DropdownMenu.js +7 -9
  11. package/dist/components/DropdownMenu/DropdownMenu.stories.js +79 -91
  12. package/dist/components/Form/Form.js +11 -4
  13. package/dist/components/Form/Form.stories.js +27 -0
  14. package/dist/components/ScrollArea/ScrollArea.js +50 -0
  15. package/dist/components/ScrollArea/ScrollArea.stories.js +56 -0
  16. package/dist/esm/bundle.css +141 -17
  17. package/dist/esm/bundle.js +3 -3
  18. package/dist/esm/bundle.js.map +1 -1
  19. package/dist/esm/types/components/DropdownMenu/DropdownMenu.stories.d.ts +29 -30
  20. package/dist/esm/types/components/Form/Form.d.ts +2 -1
  21. package/dist/esm/types/components/Form/Form.stories.d.ts +4 -0
  22. package/dist/esm/types/components/ScrollArea/ScrollArea.d.ts +38 -0
  23. package/dist/esm/types/components/ScrollArea/ScrollArea.stories.d.ts +301 -0
  24. package/dist/esm/types/index.d.ts +1 -0
  25. package/dist/index.d.ts +41 -2
  26. package/dist/index.js +1 -0
  27. package/dist/src/theme/global.css +196 -20
  28. package/package.json +1 -1
  29. package/src/components/DropdownMenu/DropdownMenu.stories.tsx +482 -297
  30. package/src/components/DropdownMenu/DropdownMenu.tsx +7 -8
  31. package/src/components/Form/Form.stories.tsx +70 -0
  32. package/src/components/Form/Form.tsx +23 -0
  33. package/src/components/ScrollArea/ScrollArea.stories.tsx +229 -0
  34. package/src/components/ScrollArea/ScrollArea.tsx +72 -0
  35. package/src/index.ts +1 -0
  36. package/src/theme/global.css +84 -11
  37. package/src/theme/themes/xspector/baseline.css +1 -0
  38. package/src/theme/themes/xspector/components/dropdown-menu.css +2 -2
  39. package/src/theme/themes/xspector/components/scrollbar.css +12 -0
  40. package/src/theme/tokens/baseline.css +2 -1
  41. package/src/theme/tokens/components/dropdown-menu.css +1 -1
  42. package/src/theme/tokens/components/scrollbar.css +18 -0
@@ -15,20 +15,19 @@ import {
15
15
  DropdownMenuTrigger,
16
16
  } from "./DropdownMenu";
17
17
  import Button from "../Button/Button";
18
- import { DropdownMenuCheckboxItemProps } from "@radix-ui/react-dropdown-menu";
19
- import Icon from "../Icon/Icon";
20
18
  import ActionButton from "../ActionButton/ActionButton";
19
+ import Icon from "../Icon/Icon";
20
+ import { Input } from "../Input/Input";
21
21
 
22
22
  const meta = {
23
23
  title: "Components/DropdownMenu",
24
24
  component: DropdownMenu,
25
- // tags: ["autodocs"],
26
25
  parameters: {
27
26
  layout: "fullscreen",
28
27
  },
29
28
  decorators: [
30
29
  (Story) => (
31
- <div className="p-5 flex w-full">
30
+ <div className="p-10 flex gap-8 flex-wrap bg-workspace-surface min-h-screen">
32
31
  <Story />
33
32
  </div>
34
33
  ),
@@ -37,193 +36,171 @@ const meta = {
37
36
 
38
37
  export default meta;
39
38
 
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>
39
+ // ---------------------------------------------------------------------------
40
+ // Figma: Normal dropdown / action menu
41
+ // Shows all item states: Default, Hover (focus), Selected, Disabled, Selected+Disabled
42
+ // ---------------------------------------------------------------------------
43
+ export const AllStates = {
44
+ name: "All States (Normal)",
45
+ render: () => (
46
+ <div className="flex gap-8 items-start flex-wrap">
47
+ {/* Default open all states visible */}
48
+ <div>
49
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">All States</p>
50
+ <DropdownMenu open>
51
+ <DropdownMenuTrigger asChild>
52
+ <ActionButton variant="icon">
53
+ <Icon type="heroicons" name="ellipsis-vertical" />
54
+ </ActionButton>
55
+ </DropdownMenuTrigger>
56
+ <DropdownMenuContent className="relative" style={{ position: "static", transform: "none" }}>
57
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
58
+ <DropdownMenuItem className="!bg-[var(--dropdown-menu-hover-bg)]">Option Description (Hover)</DropdownMenuItem>
59
+ <DropdownMenuCheckboxItem checked>Option Description</DropdownMenuCheckboxItem>
60
+ <DropdownMenuItem disabled>Option Description</DropdownMenuItem>
61
+ <DropdownMenuCheckboxItem checked disabled>Option Description</DropdownMenuCheckboxItem>
62
+ </DropdownMenuContent>
63
+ </DropdownMenu>
68
64
  </div>
69
- );
70
- },
65
+
66
+ {/* Interactive version */}
67
+ <div>
68
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
69
+ <DropdownMenu>
70
+ <DropdownMenuTrigger asChild>
71
+ <Button variant="outline">Open Menu</Button>
72
+ </DropdownMenuTrigger>
73
+ <DropdownMenuContent>
74
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
75
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
76
+ <DropdownMenuCheckboxItem checked>Option Description</DropdownMenuCheckboxItem>
77
+ <DropdownMenuItem disabled>Option Description</DropdownMenuItem>
78
+ <DropdownMenuCheckboxItem checked disabled>Option Description</DropdownMenuCheckboxItem>
79
+ </DropdownMenuContent>
80
+ </DropdownMenu>
81
+ </div>
82
+ </div>
83
+ ),
71
84
  } satisfies StoryObj;
72
85
 
86
+ // ---------------------------------------------------------------------------
87
+ // Figma: Have icon
88
+ // Items with leading icon
89
+ // ---------------------------------------------------------------------------
73
90
  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>
93
- <Icon
94
- type="heroicons"
95
- name="rocket-launch"
96
- className="size-4"
97
- />
98
- <span>Option Description</span>
99
- </DropdownMenuItem>
100
- <DropdownMenuItem>
101
- <Icon
102
- type="heroicons"
103
- name="rocket-launch"
104
- className="size-4"
105
- />
106
- <span>Option Description</span>
107
- </DropdownMenuItem>
108
- <DropdownMenuItem disabled>
109
- <Icon
110
- type="heroicons"
111
- name="rocket-launch"
112
- className="size-4"
113
- />
114
- <span>Option Description</span>
115
- </DropdownMenuItem>
116
- </DropdownMenuContent>
117
- </DropdownMenu>
118
- </div>
91
+ name: "With Icon",
92
+ render: () => (
93
+ <div className="flex gap-8 items-start flex-wrap">
94
+ <div>
95
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">All States — With Icon</p>
96
+ <DropdownMenu open>
97
+ <DropdownMenuTrigger asChild>
98
+ <ActionButton variant="icon">
99
+ <Icon type="heroicons" name="ellipsis-vertical" />
100
+ </ActionButton>
101
+ </DropdownMenuTrigger>
102
+ <DropdownMenuContent className="relative" style={{ position: "static", transform: "none" }}>
103
+ <DropdownMenuItem>
104
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
105
+ Option Description
106
+ </DropdownMenuItem>
107
+ <DropdownMenuItem className="!bg-[var(--dropdown-menu-hover-bg)]">
108
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
109
+ Option Description (Hover)
110
+ </DropdownMenuItem>
111
+ <DropdownMenuCheckboxItem checked>
112
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
113
+ Option Description
114
+ </DropdownMenuCheckboxItem>
115
+ <DropdownMenuItem disabled>
116
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
117
+ Option Description
118
+ </DropdownMenuItem>
119
+ <DropdownMenuCheckboxItem checked disabled>
120
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
121
+ Option Description
122
+ </DropdownMenuCheckboxItem>
123
+ </DropdownMenuContent>
124
+ </DropdownMenu>
119
125
  </div>
120
- );
121
- },
126
+
127
+ <div>
128
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
129
+ <DropdownMenu>
130
+ <DropdownMenuTrigger asChild>
131
+ <Button variant="outline">Open Menu</Button>
132
+ </DropdownMenuTrigger>
133
+ <DropdownMenuContent>
134
+ <DropdownMenuItem>
135
+ <Icon type="heroicons" name="rocket-launch" className="size-4 shrink-0" />
136
+ Option Description
137
+ </DropdownMenuItem>
138
+ <DropdownMenuItem>
139
+ <Icon type="heroicons" name="pencil" className="size-4 shrink-0" />
140
+ Option Description
141
+ </DropdownMenuItem>
142
+ <DropdownMenuItem>
143
+ <Icon type="heroicons" name="trash" className="size-4 shrink-0" />
144
+ Option Description
145
+ </DropdownMenuItem>
146
+ <DropdownMenuItem disabled>
147
+ <Icon type="heroicons" name="arrow-path" className="size-4 shrink-0" />
148
+ Option Description
149
+ </DropdownMenuItem>
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ </div>
153
+ </div>
154
+ ),
122
155
  } satisfies StoryObj;
123
156
 
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
- };
157
+ // ---------------------------------------------------------------------------
158
+ // Figma: Checkbox items (Have icon=No, Checkbox=Yes)
159
+ // ---------------------------------------------------------------------------
160
+ export const WithCheckbox = {
161
+ name: "With Checkbox",
162
+ render: () => {
163
+ const [checked1, setChecked1] = useState(false);
164
+ const [checked2, setChecked2] = useState(true);
165
+ const [checked3, setChecked3] = useState(false);
166
+
135
167
  return (
136
- <div className="flex flex-row gap-4 w-full">
137
- <div className="flex flex-1 justify-center items-center space-x-2">
138
- <DropdownMenu>
168
+ <div className="flex gap-8 items-start flex-wrap">
169
+ <div>
170
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">All States — Checkbox</p>
171
+ <DropdownMenu open>
139
172
  <DropdownMenuTrigger asChild>
140
- <Button variant="outline">Open</Button>
173
+ <ActionButton variant="icon">
174
+ <Icon type="heroicons" name="ellipsis-vertical" />
175
+ </ActionButton>
141
176
  </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>
177
+ <DropdownMenuContent className="relative" style={{ position: "static", transform: "none" }}>
178
+ <DropdownMenuCheckboxItem>Option Description</DropdownMenuCheckboxItem>
179
+ <DropdownMenuCheckboxItem className="!bg-[var(--dropdown-menu-hover-bg)]">Option Description (Hover)</DropdownMenuCheckboxItem>
180
+ <DropdownMenuCheckboxItem checked>Option Description</DropdownMenuCheckboxItem>
181
+ <DropdownMenuCheckboxItem disabled>Option Description</DropdownMenuCheckboxItem>
182
+ <DropdownMenuCheckboxItem checked disabled>Option Description</DropdownMenuCheckboxItem>
155
183
  </DropdownMenuContent>
156
184
  </DropdownMenu>
157
185
  </div>
158
- </div>
159
- );
160
- },
161
- } satisfies StoryObj;
162
186
 
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">
187
+ <div>
188
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
178
189
  <DropdownMenu>
179
190
  <DropdownMenuTrigger asChild>
180
- <Button variant="outline">Open</Button>
191
+ <Button variant="outline">Open Menu</Button>
181
192
  </DropdownMenuTrigger>
182
193
  <DropdownMenuContent>
183
- <DropdownMenuCheckboxItem>
184
- Option Description
185
- </DropdownMenuCheckboxItem>
186
- <DropdownMenuCheckboxItem checked>
194
+ <DropdownMenuCheckboxItem checked={checked1} onCheckedChange={setChecked1}>
187
195
  Option Description
188
196
  </DropdownMenuCheckboxItem>
189
- <DropdownMenuCheckboxItem disabled>
197
+ <DropdownMenuCheckboxItem checked={checked2} onCheckedChange={setChecked2}>
190
198
  Option Description
191
199
  </DropdownMenuCheckboxItem>
192
- <DropdownMenuCheckboxItem checked disabled>
200
+ <DropdownMenuCheckboxItem checked={checked3} onCheckedChange={setChecked3}>
193
201
  Option Description
194
202
  </DropdownMenuCheckboxItem>
195
- <DropdownMenuCheckboxItem>
196
- <Icon
197
- type="heroicons"
198
- name="rocket-launch"
199
- className="size-4"
200
- />
201
- <span>Option Description</span>
202
- </DropdownMenuCheckboxItem>
203
- <DropdownMenuCheckboxItem checked>
204
- <Icon
205
- type="heroicons"
206
- name="rocket-launch"
207
- className="size-4"
208
- />
209
- <span>Option Description</span>
210
- </DropdownMenuCheckboxItem>
211
- <DropdownMenuCheckboxItem disabled>
212
- <Icon
213
- type="heroicons"
214
- name="rocket-launch"
215
- className="size-4"
216
- />
217
- <span>Option Description</span>
218
- </DropdownMenuCheckboxItem>
219
- <DropdownMenuCheckboxItem checked disabled>
220
- <Icon
221
- type="heroicons"
222
- name="rocket-launch"
223
- className="size-4"
224
- />
225
- <span>Option Description</span>
226
- </DropdownMenuCheckboxItem>
203
+ <DropdownMenuCheckboxItem disabled>Option Description</DropdownMenuCheckboxItem>
227
204
  </DropdownMenuContent>
228
205
  </DropdownMenu>
229
206
  </div>
@@ -232,154 +209,139 @@ export const Checkboxes = {
232
209
  },
233
210
  } satisfies StoryObj;
234
211
 
235
- 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
- };
246
- return (
247
- <div className="flex flex-row gap-4 w-full">
248
- <div className="flex flex-1 justify-center items-center space-x-2">
249
- <DropdownMenu>
250
- <DropdownMenuTrigger>Open</DropdownMenuTrigger>
251
- <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>
281
- </DropdownMenuContent>
282
- </DropdownMenu>
283
- </div>
212
+ // ---------------------------------------------------------------------------
213
+ // Figma: Have section (DropdownMenuLabel as section header)
214
+ // ---------------------------------------------------------------------------
215
+ export const WithSection = {
216
+ name: "Have Section",
217
+ render: () => (
218
+ <div className="flex gap-8 items-start flex-wrap">
219
+ <div>
220
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">With Section Header</p>
221
+ <DropdownMenu open>
222
+ <DropdownMenuTrigger asChild>
223
+ <ActionButton variant="icon">
224
+ <Icon type="heroicons" name="ellipsis-vertical" />
225
+ </ActionButton>
226
+ </DropdownMenuTrigger>
227
+ <DropdownMenuContent className="relative" style={{ position: "static", transform: "none" }}>
228
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
229
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
230
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
231
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
232
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
233
+ <DropdownMenuSeparator />
234
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
235
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
236
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
237
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
238
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
239
+ </DropdownMenuContent>
240
+ </DropdownMenu>
284
241
  </div>
285
- );
286
- },
287
- } satisfies StoryObj;
288
242
 
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">
303
- <DropdownMenu>
304
- <DropdownMenuTrigger>
305
- <Icon type="heroicons" name="ellipsis-vertical" />
306
- </DropdownMenuTrigger>
307
- <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>
313
- </DropdownMenuContent>
314
- </DropdownMenu>
315
- </div>
243
+ <div>
244
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
245
+ <DropdownMenu>
246
+ <DropdownMenuTrigger asChild>
247
+ <Button variant="outline">Open Menu</Button>
248
+ </DropdownMenuTrigger>
249
+ <DropdownMenuContent>
250
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
251
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
252
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
253
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
254
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
255
+ <DropdownMenuSeparator />
256
+ <DropdownMenuLabel>Section Name</DropdownMenuLabel>
257
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
258
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
259
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
260
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
261
+ </DropdownMenuContent>
262
+ </DropdownMenu>
316
263
  </div>
317
- );
318
- },
264
+ </div>
265
+ ),
319
266
  } satisfies StoryObj;
320
267
 
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
- };
268
+ // ---------------------------------------------------------------------------
269
+ // Figma: Have search
270
+ // Dropdown with search input at the top
271
+ // ---------------------------------------------------------------------------
272
+ export const WithSearch = {
273
+ name: "Have Search",
274
+ render: () => {
275
+ const [query, setQuery] = useState("");
276
+ const allOptions = [
277
+ "Option Description",
278
+ "Project Alpha",
279
+ "Project Beta",
280
+ "Project Gamma",
281
+ "Project Delta",
282
+ "Project Epsilon",
283
+ "Project Zeta",
284
+ "Project Eta",
285
+ "Project Theta",
286
+ ];
287
+ const filtered = allOptions.filter((o) =>
288
+ o.toLowerCase().includes(query.toLowerCase())
289
+ );
290
+
332
291
  return (
333
- <div className="flex flex-row gap-4 w-full">
334
- <div className="flex flex-1 justify-center items-center space-x-2">
335
- <DropdownMenu>
292
+ <div className="flex gap-8 items-start flex-wrap">
293
+ <div>
294
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">With Search Field</p>
295
+ <DropdownMenu open>
336
296
  <DropdownMenuTrigger asChild>
337
297
  <ActionButton variant="icon">
338
298
  <Icon type="heroicons" name="ellipsis-vertical" />
339
299
  </ActionButton>
340
300
  </DropdownMenuTrigger>
341
- <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>
301
+ <DropdownMenuContent
302
+ className="relative w-[230px]"
303
+ style={{ position: "static", transform: "none" }}
304
+ onInteractOutside={(e: Event) => e.preventDefault()}
305
+ >
306
+ <div className="flex items-center px-4 py-2 border-b border-[var(--dropdown-menu-seperator-bg)]">
307
+ <Icon type="heroicons" name="magnifying-glass" className="size-[14px] shrink-0 text-text-g-contrast-medium mr-1" />
308
+ <Input
309
+ variant="flat"
310
+ size="sm"
311
+ className="flex-1 !p-0 !ring-0 typography-small1 placeholder:text-text-g-contrast-medium"
312
+ placeholder="Search"
313
+ value={query}
314
+ onChange={(e) => setQuery(e.target.value)}
315
+ />
316
+ </div>
317
+ {filtered.map((label) => (
318
+ <DropdownMenuItem key={label}>{label}</DropdownMenuItem>
319
+ ))}
320
+ {filtered.length === 0 && (
321
+ <div className="px-4 py-3 typography-subtitle4 text-text-g-contrast-medium">
322
+ No results found
323
+ </div>
324
+ )}
347
325
  </DropdownMenuContent>
348
326
  </DropdownMenu>
349
327
  </div>
350
- </div>
351
- );
352
- },
353
- } satisfies StoryObj;
354
328
 
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
- };
367
- return (
368
- <div className="flex flex-row gap-4 w-full">
369
- <div className="flex flex-1 justify-center items-center space-x-2">
329
+ <div>
330
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
370
331
  <DropdownMenu>
371
332
  <DropdownMenuTrigger asChild>
372
- <ActionButton variant="icon">
373
- <Icon type="heroicons" name="ellipsis-vertical" />
374
- </ActionButton>
333
+ <Button variant="outline">Open Menu</Button>
375
334
  </DropdownMenuTrigger>
376
- <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
382
- >
335
+ <DropdownMenuContent className="w-[230px]">
336
+ <div className="flex items-center px-4 py-2 border-b border-[var(--dropdown-menu-seperator-bg)]">
337
+ <Icon type="heroicons" name="magnifying-glass" className="size-[14px] shrink-0 text-text-g-contrast-medium mr-1" />
338
+ <Input
339
+ variant="flat"
340
+ size="sm"
341
+ className="flex-1 !p-0 !ring-0 typography-small1 placeholder:text-text-g-contrast-medium"
342
+ placeholder="Search"
343
+ />
344
+ </div>
383
345
  <DropdownMenuItem>Option Description</DropdownMenuItem>
384
346
  <DropdownMenuItem>Option Description</DropdownMenuItem>
385
347
  <DropdownMenuItem>Option Description</DropdownMenuItem>
@@ -392,3 +354,226 @@ export const Position = {
392
354
  );
393
355
  },
394
356
  } satisfies StoryObj;
357
+
358
+ // ---------------------------------------------------------------------------
359
+ // Figma: Can scroll — long list, overflow-y scrollable
360
+ // ---------------------------------------------------------------------------
361
+ export const CanScroll = {
362
+ name: "Can Scroll",
363
+ render: () => (
364
+ <div className="flex gap-8 items-start flex-wrap">
365
+ <div>
366
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Scrollable List</p>
367
+ <DropdownMenu open>
368
+ <DropdownMenuTrigger asChild>
369
+ <ActionButton variant="icon">
370
+ <Icon type="heroicons" name="ellipsis-vertical" />
371
+ </ActionButton>
372
+ </DropdownMenuTrigger>
373
+ <DropdownMenuContent
374
+ className="relative max-h-[270px] overflow-y-auto"
375
+ style={{ position: "static", transform: "none" }}
376
+ >
377
+ {Array.from({ length: 10 }, (_, i) => (
378
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
379
+ ))}
380
+ </DropdownMenuContent>
381
+ </DropdownMenu>
382
+ </div>
383
+
384
+ <div>
385
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
386
+ <DropdownMenu>
387
+ <DropdownMenuTrigger asChild>
388
+ <Button variant="outline">Open Menu</Button>
389
+ </DropdownMenuTrigger>
390
+ <DropdownMenuContent className="max-h-[270px] overflow-y-auto">
391
+ {Array.from({ length: 10 }, (_, i) => (
392
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
393
+ ))}
394
+ </DropdownMenuContent>
395
+ </DropdownMenu>
396
+ </div>
397
+ </div>
398
+ ),
399
+ } satisfies StoryObj;
400
+
401
+ // ---------------------------------------------------------------------------
402
+ // Figma: Double scroll — two separate scrollable sections with section headers
403
+ // ---------------------------------------------------------------------------
404
+ export const DoubleScroll = {
405
+ name: "Double Scroll",
406
+ render: () => (
407
+ <div className="flex gap-8 items-start flex-wrap">
408
+ <div>
409
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Two Scrollable Sections</p>
410
+ <DropdownMenu open>
411
+ <DropdownMenuTrigger asChild>
412
+ <ActionButton variant="icon">
413
+ <Icon type="heroicons" name="ellipsis-vertical" />
414
+ </ActionButton>
415
+ </DropdownMenuTrigger>
416
+ <DropdownMenuContent
417
+ className="relative"
418
+ style={{ position: "static", transform: "none" }}
419
+ >
420
+ <DropdownMenuLabel>Section A</DropdownMenuLabel>
421
+ <div className="max-h-[270px] overflow-y-auto">
422
+ {Array.from({ length: 5 }, (_, i) => (
423
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
424
+ ))}
425
+ </div>
426
+ <DropdownMenuSeparator />
427
+ <DropdownMenuLabel>Section B</DropdownMenuLabel>
428
+ <div className="max-h-[216px] overflow-y-auto">
429
+ {Array.from({ length: 4 }, (_, i) => (
430
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
431
+ ))}
432
+ </div>
433
+ </DropdownMenuContent>
434
+ </DropdownMenu>
435
+ </div>
436
+
437
+ <div>
438
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Interactive</p>
439
+ <DropdownMenu>
440
+ <DropdownMenuTrigger asChild>
441
+ <Button variant="outline">Open Menu</Button>
442
+ </DropdownMenuTrigger>
443
+ <DropdownMenuContent>
444
+ <DropdownMenuLabel>Section A</DropdownMenuLabel>
445
+ <div className="max-h-[270px] overflow-y-auto">
446
+ {Array.from({ length: 5 }, (_, i) => (
447
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
448
+ ))}
449
+ </div>
450
+ <DropdownMenuSeparator />
451
+ <DropdownMenuLabel>Section B</DropdownMenuLabel>
452
+ <div className="max-h-[216px] overflow-y-auto">
453
+ {Array.from({ length: 4 }, (_, i) => (
454
+ <DropdownMenuItem key={i}>Option Description {i + 1}</DropdownMenuItem>
455
+ ))}
456
+ </div>
457
+ </DropdownMenuContent>
458
+ </DropdownMenu>
459
+ </div>
460
+ </div>
461
+ ),
462
+ } satisfies StoryObj;
463
+
464
+ // ---------------------------------------------------------------------------
465
+ // Figma: Sub menu (existing, preserved)
466
+ // ---------------------------------------------------------------------------
467
+ export const SubMenu = {
468
+ name: "Sub Menu",
469
+ render: () => (
470
+ <DropdownMenu>
471
+ <DropdownMenuTrigger asChild>
472
+ <Button variant="outline">Open Menu</Button>
473
+ </DropdownMenuTrigger>
474
+ <DropdownMenuContent>
475
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
476
+ <DropdownMenuSeparator />
477
+ <DropdownMenuItem>Profile</DropdownMenuItem>
478
+ <DropdownMenuItem>Billing</DropdownMenuItem>
479
+ <DropdownMenuItem>Team</DropdownMenuItem>
480
+ <DropdownMenuSeparator />
481
+ <DropdownMenuGroup>
482
+ <DropdownMenuSub>
483
+ <DropdownMenuSubTrigger>
484
+ <Icon type="heroicons" name="user-group" className="size-4 shrink-0" />
485
+ Invite users
486
+ </DropdownMenuSubTrigger>
487
+ <DropdownMenuPortal>
488
+ <DropdownMenuSubContent>
489
+ <DropdownMenuItem>Email</DropdownMenuItem>
490
+ <DropdownMenuItem>Message</DropdownMenuItem>
491
+ <DropdownMenuSeparator />
492
+ <DropdownMenuItem>More...</DropdownMenuItem>
493
+ </DropdownMenuSubContent>
494
+ </DropdownMenuPortal>
495
+ </DropdownMenuSub>
496
+ <DropdownMenuCheckboxItem>Panel</DropdownMenuCheckboxItem>
497
+ </DropdownMenuGroup>
498
+ </DropdownMenuContent>
499
+ </DropdownMenu>
500
+ ),
501
+ } satisfies StoryObj;
502
+
503
+ // ---------------------------------------------------------------------------
504
+ // Trigger variants (on icon button, etc.)
505
+ // ---------------------------------------------------------------------------
506
+ export const TriggerVariants = {
507
+ name: "Trigger Variants",
508
+ render: () => (
509
+ <div className="flex gap-8 items-center flex-wrap">
510
+ <div>
511
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Text Trigger</p>
512
+ <DropdownMenu>
513
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
514
+ <DropdownMenuContent>
515
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
516
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
517
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
518
+ </DropdownMenuContent>
519
+ </DropdownMenu>
520
+ </div>
521
+
522
+ <div>
523
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Button Trigger</p>
524
+ <DropdownMenu>
525
+ <DropdownMenuTrigger asChild>
526
+ <Button variant="outline">Open Menu</Button>
527
+ </DropdownMenuTrigger>
528
+ <DropdownMenuContent>
529
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
530
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
531
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
532
+ </DropdownMenuContent>
533
+ </DropdownMenu>
534
+ </div>
535
+
536
+ <div>
537
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Icon Button Trigger</p>
538
+ <DropdownMenu>
539
+ <DropdownMenuTrigger asChild>
540
+ <ActionButton variant="icon">
541
+ <Icon type="heroicons" name="ellipsis-vertical" />
542
+ </ActionButton>
543
+ </DropdownMenuTrigger>
544
+ <DropdownMenuContent>
545
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
546
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
547
+ <DropdownMenuItem>Option Description</DropdownMenuItem>
548
+ </DropdownMenuContent>
549
+ </DropdownMenu>
550
+ </div>
551
+
552
+ <div>
553
+ <p className="typography-small4 text-text-g-contrast-medium mb-2">Horizontal dots</p>
554
+ <DropdownMenu>
555
+ <DropdownMenuTrigger asChild>
556
+ <ActionButton variant="icon">
557
+ <Icon type="heroicons" name="ellipsis-horizontal" />
558
+ </ActionButton>
559
+ </DropdownMenuTrigger>
560
+ <DropdownMenuContent>
561
+ <DropdownMenuItem>
562
+ <Icon type="heroicons" name="pencil" className="size-4 shrink-0" />
563
+ Edit
564
+ </DropdownMenuItem>
565
+ <DropdownMenuItem>
566
+ <Icon type="heroicons" name="document-duplicate" className="size-4 shrink-0" />
567
+ Duplicate
568
+ </DropdownMenuItem>
569
+ <DropdownMenuSeparator />
570
+ <DropdownMenuItem disabled>
571
+ <Icon type="heroicons" name="trash" className="size-4 shrink-0" />
572
+ Delete
573
+ </DropdownMenuItem>
574
+ </DropdownMenuContent>
575
+ </DropdownMenu>
576
+ </div>
577
+ </div>
578
+ ),
579
+ } satisfies StoryObj;