@stackshift-ui/dropdown-menu 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +24 -0
- package/dist/chunk-3EYN2AKU.mjs +1 -0
- package/dist/chunk-T7FMXF3I.mjs +1 -0
- package/dist/dropdown-menu.d.ts +70 -0
- package/dist/dropdown-menu.js +2 -0
- package/dist/dropdown-menu.mjs +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.mjs +2 -0
- package/dist/setupTests.d.ts +2 -0
- package/dist/setupTests.js +51 -0
- package/dist/setupTests.mjs +51 -0
- package/package.json +64 -0
- package/src/dropdown-menu.test.tsx +312 -0
- package/src/dropdown-menu.tsx +305 -0
- package/src/index.ts +4 -0
- package/src/setupTests.ts +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stackshift-ui/dropdown-menu",
|
|
3
|
+
"description": "Displays a menu to the user — such as a set of actions or functions — triggered by a button.",
|
|
4
|
+
"version": "1.0.0-beta.1",
|
|
5
|
+
"private": false,
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**",
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"author": "WebriQ <info@webriq.com>",
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@testing-library/jest-dom": "^6.5.0",
|
|
17
|
+
"@testing-library/react": "^16.0.1",
|
|
18
|
+
"@testing-library/user-event": "^14.6.1",
|
|
19
|
+
"@types/node": "^22.7.0",
|
|
20
|
+
"@types/react": "^18.3.9",
|
|
21
|
+
"@types/react-dom": "^18.3.0",
|
|
22
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
23
|
+
"@vitest/coverage-v8": "^2.1.1",
|
|
24
|
+
"esbuild-plugin-rdi": "^0.0.0",
|
|
25
|
+
"esbuild-plugin-react18": "^0.2.5",
|
|
26
|
+
"esbuild-plugin-react18-css": "^0.0.4",
|
|
27
|
+
"jsdom": "^25.0.1",
|
|
28
|
+
"react": "^18.3.1",
|
|
29
|
+
"react-dom": "^18.3.1",
|
|
30
|
+
"tsup": "^8.3.0",
|
|
31
|
+
"typescript": "^5.6.2",
|
|
32
|
+
"vite-tsconfig-paths": "^5.0.1",
|
|
33
|
+
"vitest": "^2.1.1",
|
|
34
|
+
"@stackshift-ui/eslint-config": "6.0.10",
|
|
35
|
+
"@stackshift-ui/typescript-config": "6.0.10"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
39
|
+
"classnames": "^2.5.1",
|
|
40
|
+
"lucide-react": "^0.468.0",
|
|
41
|
+
"@stackshift-ui/scripts": "6.1.0-beta.0",
|
|
42
|
+
"@stackshift-ui/system": "6.1.0-beta.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@stackshift-ui/system": ">=6.1.0-beta.0",
|
|
46
|
+
"@types/react": "16.8 - 19",
|
|
47
|
+
"next": "10 - 14",
|
|
48
|
+
"react": "16.8 - 19",
|
|
49
|
+
"react-dom": "16.8 - 19"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"next": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup && tsc -p tsconfig-build.json",
|
|
58
|
+
"clean": "rm -rf dist",
|
|
59
|
+
"dev": "tsup --watch && tsc -p tsconfig-build.json -w",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"lint": "eslint src/",
|
|
62
|
+
"test": "vitest run --coverage"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { afterEach, beforeEach, describe, test, vi } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuCheckboxItem,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuGroup,
|
|
10
|
+
DropdownMenuItem,
|
|
11
|
+
DropdownMenuLabel,
|
|
12
|
+
DropdownMenuRadioGroup,
|
|
13
|
+
DropdownMenuRadioItem,
|
|
14
|
+
DropdownMenuSeparator,
|
|
15
|
+
DropdownMenuShortcut,
|
|
16
|
+
DropdownMenuSub,
|
|
17
|
+
DropdownMenuSubContent,
|
|
18
|
+
DropdownMenuSubTrigger,
|
|
19
|
+
DropdownMenuTrigger,
|
|
20
|
+
} from "./dropdown-menu";
|
|
21
|
+
|
|
22
|
+
describe("dropdown-menu", () => {
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(cleanup);
|
|
28
|
+
|
|
29
|
+
test("Common: DropdownMenu - test if renders with proper structure and data attributes", ({
|
|
30
|
+
expect,
|
|
31
|
+
}) => {
|
|
32
|
+
const customClass = "custom-dropdown";
|
|
33
|
+
|
|
34
|
+
const { unmount } = render(
|
|
35
|
+
<DropdownMenu>
|
|
36
|
+
<DropdownMenuTrigger data-testid="dropdown-trigger" className={customClass}>
|
|
37
|
+
Open Menu
|
|
38
|
+
</DropdownMenuTrigger>
|
|
39
|
+
<DropdownMenuContent data-testid="dropdown-content">
|
|
40
|
+
<DropdownMenuItem data-testid="dropdown-item">Item 1</DropdownMenuItem>
|
|
41
|
+
</DropdownMenuContent>
|
|
42
|
+
</DropdownMenu>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const trigger = screen.getByTestId("dropdown-trigger");
|
|
46
|
+
|
|
47
|
+
expect(trigger).toBeInTheDocument();
|
|
48
|
+
expect(trigger).toHaveAttribute("data-slot", "dropdown-menu-trigger");
|
|
49
|
+
expect(trigger.classList).toContain(customClass);
|
|
50
|
+
unmount();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Common: DropdownMenu - test if trigger interactions and content display work correctly", async ({
|
|
54
|
+
expect,
|
|
55
|
+
}) => {
|
|
56
|
+
const user = userEvent.setup();
|
|
57
|
+
|
|
58
|
+
const { unmount } = render(
|
|
59
|
+
<DropdownMenu>
|
|
60
|
+
<DropdownMenuTrigger data-testid="trigger-test">Open Menu</DropdownMenuTrigger>
|
|
61
|
+
<DropdownMenuContent data-testid="content-test" sideOffset={8}>
|
|
62
|
+
<DropdownMenuItem data-testid="menu-item-1">First Item</DropdownMenuItem>
|
|
63
|
+
<DropdownMenuItem data-testid="menu-item-2">Second Item</DropdownMenuItem>
|
|
64
|
+
</DropdownMenuContent>
|
|
65
|
+
</DropdownMenu>,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const trigger = screen.getByTestId("trigger-test");
|
|
69
|
+
|
|
70
|
+
expect(screen.queryByTestId("content-test")).not.toBeInTheDocument();
|
|
71
|
+
|
|
72
|
+
await user.click(trigger);
|
|
73
|
+
|
|
74
|
+
// Wait for content to appear
|
|
75
|
+
const content = await screen.findByTestId("content-test");
|
|
76
|
+
expect(content).toBeInTheDocument();
|
|
77
|
+
expect(content).toHaveAttribute("data-slot", "dropdown-menu-content");
|
|
78
|
+
|
|
79
|
+
expect(screen.getByTestId("menu-item-1")).toBeInTheDocument();
|
|
80
|
+
expect(screen.getByTestId("menu-item-2")).toBeInTheDocument();
|
|
81
|
+
unmount();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("Common: DropdownMenu - test if menu items with variants and states work properly", async ({
|
|
85
|
+
expect,
|
|
86
|
+
}) => {
|
|
87
|
+
const handleClick = vi.fn();
|
|
88
|
+
|
|
89
|
+
const { unmount } = render(
|
|
90
|
+
<DropdownMenu defaultOpen>
|
|
91
|
+
<DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
|
|
92
|
+
<DropdownMenuContent data-testid="variants-content">
|
|
93
|
+
<DropdownMenuItem data-testid="default-item" onSelect={handleClick}>
|
|
94
|
+
Default Item
|
|
95
|
+
</DropdownMenuItem>
|
|
96
|
+
<DropdownMenuItem
|
|
97
|
+
data-testid="destructive-item"
|
|
98
|
+
variant="destructive"
|
|
99
|
+
onSelect={handleClick}>
|
|
100
|
+
Delete Item
|
|
101
|
+
</DropdownMenuItem>
|
|
102
|
+
<DropdownMenuItem data-testid="inset-item" inset>
|
|
103
|
+
Inset Item
|
|
104
|
+
</DropdownMenuItem>
|
|
105
|
+
<DropdownMenuItem data-testid="disabled-item" disabled>
|
|
106
|
+
Disabled Item
|
|
107
|
+
</DropdownMenuItem>
|
|
108
|
+
</DropdownMenuContent>
|
|
109
|
+
</DropdownMenu>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const content = await screen.findByTestId("variants-content");
|
|
113
|
+
expect(content).toBeInTheDocument();
|
|
114
|
+
|
|
115
|
+
const defaultItem = screen.getByTestId("default-item");
|
|
116
|
+
const destructiveItem = screen.getByTestId("destructive-item");
|
|
117
|
+
const insetItem = screen.getByTestId("inset-item");
|
|
118
|
+
const disabledItem = screen.getByTestId("disabled-item");
|
|
119
|
+
|
|
120
|
+
// Check data attributes and styling
|
|
121
|
+
expect(defaultItem).toHaveAttribute("data-slot", "dropdown-menu-item");
|
|
122
|
+
expect(defaultItem).toHaveAttribute("data-variant", "default");
|
|
123
|
+
expect(destructiveItem).toHaveAttribute("data-variant", "destructive");
|
|
124
|
+
expect(insetItem).toHaveAttribute("data-inset", "true");
|
|
125
|
+
expect(disabledItem).toHaveAttribute("data-disabled");
|
|
126
|
+
unmount();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("Common: DropdownMenu - test if checkbox and radio item functionality works correctly", async ({
|
|
130
|
+
expect,
|
|
131
|
+
}) => {
|
|
132
|
+
function TestComponent() {
|
|
133
|
+
const [checkboxChecked, setCheckboxChecked] = React.useState(false);
|
|
134
|
+
const [radioValue, setRadioValue] = React.useState("option1");
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<DropdownMenu defaultOpen>
|
|
138
|
+
<DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
|
|
139
|
+
<DropdownMenuContent data-testid="checkbox-radio-content">
|
|
140
|
+
<DropdownMenuCheckboxItem
|
|
141
|
+
data-testid="checkbox-item"
|
|
142
|
+
checked={checkboxChecked}
|
|
143
|
+
onCheckedChange={setCheckboxChecked}>
|
|
144
|
+
Checkbox Item
|
|
145
|
+
</DropdownMenuCheckboxItem>
|
|
146
|
+
<DropdownMenuRadioGroup value={radioValue} onValueChange={setRadioValue}>
|
|
147
|
+
<DropdownMenuRadioItem data-testid="radio-item-1" value="option1">
|
|
148
|
+
Radio Option 1
|
|
149
|
+
</DropdownMenuRadioItem>
|
|
150
|
+
<DropdownMenuRadioItem data-testid="radio-item-2" value="option2">
|
|
151
|
+
Radio Option 2
|
|
152
|
+
</DropdownMenuRadioItem>
|
|
153
|
+
</DropdownMenuRadioGroup>
|
|
154
|
+
</DropdownMenuContent>
|
|
155
|
+
</DropdownMenu>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const { unmount } = render(<TestComponent />);
|
|
160
|
+
|
|
161
|
+
const content = await screen.findByTestId("checkbox-radio-content");
|
|
162
|
+
expect(content).toBeInTheDocument();
|
|
163
|
+
|
|
164
|
+
const checkboxItem = screen.getByTestId("checkbox-item");
|
|
165
|
+
const radioItem1 = screen.getByTestId("radio-item-1");
|
|
166
|
+
const radioItem2 = screen.getByTestId("radio-item-2");
|
|
167
|
+
|
|
168
|
+
expect(checkboxItem).toHaveAttribute("data-slot", "dropdown-menu-checkbox-item");
|
|
169
|
+
expect(radioItem1).toHaveAttribute("data-slot", "dropdown-menu-radio-item");
|
|
170
|
+
expect(radioItem2).toHaveAttribute("data-slot", "dropdown-menu-radio-item");
|
|
171
|
+
|
|
172
|
+
expect(checkboxItem).toHaveAttribute("data-state", "unchecked");
|
|
173
|
+
expect(radioItem1).toHaveAttribute("data-state", "checked");
|
|
174
|
+
expect(radioItem2).toHaveAttribute("data-state", "unchecked");
|
|
175
|
+
unmount();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("Common: DropdownMenu - test if keyboard navigation and accessibility work properly", async ({
|
|
179
|
+
expect,
|
|
180
|
+
}) => {
|
|
181
|
+
const user = userEvent.setup();
|
|
182
|
+
|
|
183
|
+
const { unmount } = render(
|
|
184
|
+
<DropdownMenu>
|
|
185
|
+
<DropdownMenuTrigger data-testid="keyboard-trigger">Open Menu</DropdownMenuTrigger>
|
|
186
|
+
<DropdownMenuContent data-testid="keyboard-content">
|
|
187
|
+
<DropdownMenuItem data-testid="item-1">First Item</DropdownMenuItem>
|
|
188
|
+
<DropdownMenuItem data-testid="item-2">Second Item</DropdownMenuItem>
|
|
189
|
+
<DropdownMenuItem data-testid="item-3">Third Item</DropdownMenuItem>
|
|
190
|
+
</DropdownMenuContent>
|
|
191
|
+
</DropdownMenu>,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const trigger = screen.getByTestId("keyboard-trigger");
|
|
195
|
+
|
|
196
|
+
await user.click(trigger);
|
|
197
|
+
|
|
198
|
+
const content = await screen.findByTestId("keyboard-content");
|
|
199
|
+
expect(content).toBeInTheDocument();
|
|
200
|
+
expect(content).toHaveAttribute("role", "menu");
|
|
201
|
+
expect(content).toHaveAttribute("data-slot", "dropdown-menu-content");
|
|
202
|
+
|
|
203
|
+
expect(screen.getByTestId("item-1")).toBeInTheDocument();
|
|
204
|
+
expect(screen.getByTestId("item-2")).toBeInTheDocument();
|
|
205
|
+
expect(screen.getByTestId("item-3")).toBeInTheDocument();
|
|
206
|
+
unmount();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("Common: DropdownMenu - test if sub-menu functionality works correctly", async ({
|
|
210
|
+
expect,
|
|
211
|
+
}) => {
|
|
212
|
+
const { unmount } = render(
|
|
213
|
+
<DropdownMenu defaultOpen>
|
|
214
|
+
<DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
|
|
215
|
+
<DropdownMenuContent data-testid="main-content">
|
|
216
|
+
<DropdownMenuItem>Regular Item</DropdownMenuItem>
|
|
217
|
+
<DropdownMenuSub>
|
|
218
|
+
<DropdownMenuSubTrigger data-testid="sub-trigger" inset>
|
|
219
|
+
More Options
|
|
220
|
+
</DropdownMenuSubTrigger>
|
|
221
|
+
<DropdownMenuSubContent data-testid="sub-content">
|
|
222
|
+
<DropdownMenuItem data-testid="sub-item-1">Sub Item 1</DropdownMenuItem>
|
|
223
|
+
<DropdownMenuItem data-testid="sub-item-2">Sub Item 2</DropdownMenuItem>
|
|
224
|
+
</DropdownMenuSubContent>
|
|
225
|
+
</DropdownMenuSub>
|
|
226
|
+
</DropdownMenuContent>
|
|
227
|
+
</DropdownMenu>,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const mainContent = await screen.findByTestId("main-content");
|
|
231
|
+
expect(mainContent).toBeInTheDocument();
|
|
232
|
+
|
|
233
|
+
const subTrigger = screen.getByTestId("sub-trigger");
|
|
234
|
+
|
|
235
|
+
expect(subTrigger).toHaveAttribute("data-slot", "dropdown-menu-sub-trigger");
|
|
236
|
+
expect(subTrigger).toHaveAttribute("data-inset", "true");
|
|
237
|
+
|
|
238
|
+
expect(screen.queryByTestId("sub-content")).not.toBeInTheDocument();
|
|
239
|
+
unmount();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("Common: DropdownMenu - test if menu structure components render with proper styling", ({
|
|
243
|
+
expect,
|
|
244
|
+
}) => {
|
|
245
|
+
const { unmount } = render(
|
|
246
|
+
<DropdownMenu defaultOpen>
|
|
247
|
+
<DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
|
|
248
|
+
<DropdownMenuContent data-testid="structure-content">
|
|
249
|
+
<DropdownMenuLabel data-testid="menu-label" inset>
|
|
250
|
+
Menu Section
|
|
251
|
+
</DropdownMenuLabel>
|
|
252
|
+
<DropdownMenuGroup data-testid="menu-group">
|
|
253
|
+
<DropdownMenuItem>
|
|
254
|
+
Item with shortcut
|
|
255
|
+
<DropdownMenuShortcut data-testid="menu-shortcut">⌘K</DropdownMenuShortcut>
|
|
256
|
+
</DropdownMenuItem>
|
|
257
|
+
</DropdownMenuGroup>
|
|
258
|
+
<DropdownMenuSeparator data-testid="menu-separator" />
|
|
259
|
+
<DropdownMenuItem>Another Item</DropdownMenuItem>
|
|
260
|
+
</DropdownMenuContent>
|
|
261
|
+
</DropdownMenu>,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const label = screen.getByTestId("menu-label");
|
|
265
|
+
const group = screen.getByTestId("menu-group");
|
|
266
|
+
const shortcut = screen.getByTestId("menu-shortcut");
|
|
267
|
+
const separator = screen.getByTestId("menu-separator");
|
|
268
|
+
|
|
269
|
+
expect(label).toHaveAttribute("data-slot", "dropdown-menu-label");
|
|
270
|
+
expect(label).toHaveAttribute("data-inset", "true");
|
|
271
|
+
expect(group).toHaveAttribute("data-slot", "dropdown-menu-group");
|
|
272
|
+
expect(shortcut).toHaveAttribute("data-slot", "dropdown-menu-shortcut");
|
|
273
|
+
expect(separator).toHaveAttribute("data-slot", "dropdown-menu-separator");
|
|
274
|
+
|
|
275
|
+
expect(label).toHaveTextContent("Menu Section");
|
|
276
|
+
expect(shortcut).toHaveTextContent("⌘K");
|
|
277
|
+
unmount();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("Common: DropdownMenu - test if portal and positioning behavior work correctly", async ({
|
|
281
|
+
expect,
|
|
282
|
+
}) => {
|
|
283
|
+
const user = userEvent.setup();
|
|
284
|
+
|
|
285
|
+
const { unmount } = render(
|
|
286
|
+
<div data-testid="container">
|
|
287
|
+
<DropdownMenu>
|
|
288
|
+
<DropdownMenuTrigger data-testid="portal-trigger">Open Menu</DropdownMenuTrigger>
|
|
289
|
+
<DropdownMenuContent data-testid="portal-content" sideOffset={12}>
|
|
290
|
+
<DropdownMenuItem>Portal Item</DropdownMenuItem>
|
|
291
|
+
</DropdownMenuContent>
|
|
292
|
+
</DropdownMenu>
|
|
293
|
+
</div>,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const trigger = screen.getByTestId("portal-trigger");
|
|
297
|
+
const container = screen.getByTestId("container");
|
|
298
|
+
|
|
299
|
+
expect(screen.queryByTestId("portal-content")).not.toBeInTheDocument();
|
|
300
|
+
|
|
301
|
+
await user.click(trigger);
|
|
302
|
+
|
|
303
|
+
const content = await screen.findByTestId("portal-content");
|
|
304
|
+
expect(content).toBeInTheDocument();
|
|
305
|
+
|
|
306
|
+
expect(container).not.toContainElement(content);
|
|
307
|
+
|
|
308
|
+
expect(content).toHaveAttribute("data-side");
|
|
309
|
+
expect(content).toHaveAttribute("data-align");
|
|
310
|
+
unmount();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
4
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn, DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
|
|
8
|
+
|
|
9
|
+
const displayName = "DropdownMenu";
|
|
10
|
+
const displayNamePortal = "DropdownMenuPortal";
|
|
11
|
+
const displayNameTrigger = "DropdownMenuTrigger";
|
|
12
|
+
const displayNameContent = "DropdownMenuContent";
|
|
13
|
+
const displayNameGroup = "DropdownMenuGroup";
|
|
14
|
+
const displayNameItem = "DropdownMenuItem";
|
|
15
|
+
const displayNameCheckboxItem = "DropdownMenuCheckboxItem";
|
|
16
|
+
const displayNameRadioGroup = "DropdownMenuRadioGroup";
|
|
17
|
+
const displayNameRadioItem = "DropdownMenuRadioItem";
|
|
18
|
+
const displayNameLabel = "DropdownMenuLabel";
|
|
19
|
+
const displayNameSeparator = "DropdownMenuSeparator";
|
|
20
|
+
const displayNameShortcut = "DropdownMenuShortcut";
|
|
21
|
+
const displayNameSub = "DropdownMenuSub";
|
|
22
|
+
const displayNameSubTrigger = "DropdownMenuSubTrigger";
|
|
23
|
+
const displayNameSubContent = "DropdownMenuSubContent";
|
|
24
|
+
|
|
25
|
+
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
26
|
+
const { [displayName]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
27
|
+
|
|
28
|
+
return <Component as={DropdownMenuPrimitive.Root} data-slot="dropdown-menu" {...props} />;
|
|
29
|
+
}
|
|
30
|
+
DropdownMenu.displayName = displayName;
|
|
31
|
+
|
|
32
|
+
function DropdownMenuPortal({
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
|
35
|
+
const { [displayNamePortal]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Component as={DropdownMenuPrimitive.Portal} data-slot="dropdown-menu-portal" {...props} />
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
DropdownMenuPortal.displayName = displayNamePortal;
|
|
42
|
+
|
|
43
|
+
function DropdownMenuTrigger({
|
|
44
|
+
...props
|
|
45
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
46
|
+
const { [displayNameTrigger]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Component as={DropdownMenuPrimitive.Trigger} data-slot="dropdown-menu-trigger" {...props} />
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
DropdownMenuTrigger.displayName = displayNameTrigger;
|
|
53
|
+
|
|
54
|
+
function DropdownMenuContent({
|
|
55
|
+
className,
|
|
56
|
+
sideOffset = 4,
|
|
57
|
+
...props
|
|
58
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
59
|
+
const { [displayNameContent]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<DropdownMenuPortal>
|
|
63
|
+
<Component
|
|
64
|
+
as={DropdownMenuPrimitive.Content}
|
|
65
|
+
data-slot="dropdown-menu-content"
|
|
66
|
+
sideOffset={sideOffset}
|
|
67
|
+
className={cn(
|
|
68
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
</DropdownMenuPortal>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
DropdownMenuContent.displayName = displayNameContent;
|
|
77
|
+
|
|
78
|
+
function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
79
|
+
const { [displayNameGroup]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
80
|
+
|
|
81
|
+
return <Component as={DropdownMenuPrimitive.Group} data-slot="dropdown-menu-group" {...props} />;
|
|
82
|
+
}
|
|
83
|
+
DropdownMenuGroup.displayName = displayNameGroup;
|
|
84
|
+
|
|
85
|
+
function DropdownMenuItem({
|
|
86
|
+
className,
|
|
87
|
+
inset,
|
|
88
|
+
variant = "default",
|
|
89
|
+
...props
|
|
90
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
91
|
+
inset?: boolean;
|
|
92
|
+
variant?: "default" | "destructive";
|
|
93
|
+
}) {
|
|
94
|
+
const { [displayNameItem]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Component
|
|
98
|
+
as={DropdownMenuPrimitive.Item}
|
|
99
|
+
data-slot="dropdown-menu-item"
|
|
100
|
+
data-inset={inset}
|
|
101
|
+
data-variant={variant}
|
|
102
|
+
className={cn(
|
|
103
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
104
|
+
className,
|
|
105
|
+
)}
|
|
106
|
+
{...props}
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
DropdownMenuItem.displayName = displayNameItem;
|
|
111
|
+
|
|
112
|
+
function DropdownMenuCheckboxItem({
|
|
113
|
+
className,
|
|
114
|
+
children,
|
|
115
|
+
checked,
|
|
116
|
+
...props
|
|
117
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
118
|
+
const { [displayNameCheckboxItem]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Component
|
|
122
|
+
as={DropdownMenuPrimitive.CheckboxItem}
|
|
123
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
124
|
+
className={cn(
|
|
125
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
126
|
+
className,
|
|
127
|
+
)}
|
|
128
|
+
checked={checked}
|
|
129
|
+
{...props}>
|
|
130
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
131
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
132
|
+
<CheckIcon className="size-4" />
|
|
133
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
134
|
+
</span>
|
|
135
|
+
{children}
|
|
136
|
+
</Component>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
DropdownMenuCheckboxItem.displayName = displayNameCheckboxItem;
|
|
140
|
+
|
|
141
|
+
function DropdownMenuRadioGroup({
|
|
142
|
+
...props
|
|
143
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
|
144
|
+
const { [displayNameRadioGroup]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Component
|
|
148
|
+
as={DropdownMenuPrimitive.RadioGroup}
|
|
149
|
+
data-slot="dropdown-menu-radio-group"
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
DropdownMenuRadioGroup.displayName = displayNameRadioGroup;
|
|
155
|
+
|
|
156
|
+
function DropdownMenuRadioItem({
|
|
157
|
+
className,
|
|
158
|
+
children,
|
|
159
|
+
...props
|
|
160
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
161
|
+
const { [displayNameRadioItem]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<Component
|
|
165
|
+
as={DropdownMenuPrimitive.RadioItem}
|
|
166
|
+
data-slot="dropdown-menu-radio-item"
|
|
167
|
+
className={cn(
|
|
168
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
169
|
+
className,
|
|
170
|
+
)}
|
|
171
|
+
{...props}>
|
|
172
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
173
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
174
|
+
<CircleIcon className="size-2 fill-current" />
|
|
175
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
176
|
+
</span>
|
|
177
|
+
{children}
|
|
178
|
+
</Component>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
DropdownMenuRadioItem.displayName = displayNameRadioItem;
|
|
182
|
+
|
|
183
|
+
function DropdownMenuLabel({
|
|
184
|
+
className,
|
|
185
|
+
inset,
|
|
186
|
+
...props
|
|
187
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
188
|
+
inset?: boolean;
|
|
189
|
+
}) {
|
|
190
|
+
const { [displayNameLabel]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Component
|
|
194
|
+
as={DropdownMenuPrimitive.Label}
|
|
195
|
+
data-slot="dropdown-menu-label"
|
|
196
|
+
data-inset={inset}
|
|
197
|
+
className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
|
|
198
|
+
{...props}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
DropdownMenuLabel.displayName = displayNameLabel;
|
|
203
|
+
|
|
204
|
+
function DropdownMenuSeparator({
|
|
205
|
+
className,
|
|
206
|
+
...props
|
|
207
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
208
|
+
const { [displayNameSeparator]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Component
|
|
212
|
+
as={DropdownMenuPrimitive.Separator}
|
|
213
|
+
data-slot="dropdown-menu-separator"
|
|
214
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
215
|
+
{...props}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
DropdownMenuSeparator.displayName = displayNameSeparator;
|
|
220
|
+
|
|
221
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
|
222
|
+
const { [displayNameShortcut]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<Component
|
|
226
|
+
as="span"
|
|
227
|
+
data-slot="dropdown-menu-shortcut"
|
|
228
|
+
className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
|
|
229
|
+
{...props}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
DropdownMenuShortcut.displayName = displayNameShortcut;
|
|
234
|
+
|
|
235
|
+
function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
236
|
+
const { [displayNameSub]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
237
|
+
|
|
238
|
+
return <Component as={DropdownMenuPrimitive.Sub} data-slot="dropdown-menu-sub" {...props} />;
|
|
239
|
+
}
|
|
240
|
+
DropdownMenuSub.displayName = displayNameSub;
|
|
241
|
+
|
|
242
|
+
function DropdownMenuSubTrigger({
|
|
243
|
+
className,
|
|
244
|
+
inset,
|
|
245
|
+
children,
|
|
246
|
+
...props
|
|
247
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
248
|
+
inset?: boolean;
|
|
249
|
+
}) {
|
|
250
|
+
const { [displayNameSubTrigger]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<Component
|
|
254
|
+
as={DropdownMenuPrimitive.SubTrigger}
|
|
255
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
256
|
+
data-inset={inset}
|
|
257
|
+
className={cn(
|
|
258
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
|
259
|
+
className,
|
|
260
|
+
)}
|
|
261
|
+
{...props}>
|
|
262
|
+
{children}
|
|
263
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
264
|
+
</Component>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
DropdownMenuSubTrigger.displayName = displayNameSubTrigger;
|
|
268
|
+
|
|
269
|
+
function DropdownMenuSubContent({
|
|
270
|
+
className,
|
|
271
|
+
...props
|
|
272
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
273
|
+
const { [displayNameSubContent]: Component = DefaultComponent } = useStackShiftUIComponents();
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<Component
|
|
277
|
+
as={DropdownMenuPrimitive.SubContent}
|
|
278
|
+
data-slot="dropdown-menu-sub-content"
|
|
279
|
+
className={cn(
|
|
280
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
281
|
+
className,
|
|
282
|
+
)}
|
|
283
|
+
{...props}
|
|
284
|
+
/>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
DropdownMenuSubContent.displayName = displayNameSubContent;
|
|
288
|
+
|
|
289
|
+
export {
|
|
290
|
+
DropdownMenu,
|
|
291
|
+
DropdownMenuCheckboxItem,
|
|
292
|
+
DropdownMenuContent,
|
|
293
|
+
DropdownMenuGroup,
|
|
294
|
+
DropdownMenuItem,
|
|
295
|
+
DropdownMenuLabel,
|
|
296
|
+
DropdownMenuPortal,
|
|
297
|
+
DropdownMenuRadioGroup,
|
|
298
|
+
DropdownMenuRadioItem,
|
|
299
|
+
DropdownMenuSeparator,
|
|
300
|
+
DropdownMenuShortcut,
|
|
301
|
+
DropdownMenuSub,
|
|
302
|
+
DropdownMenuSubContent,
|
|
303
|
+
DropdownMenuSubTrigger,
|
|
304
|
+
DropdownMenuTrigger,
|
|
305
|
+
};
|
package/src/index.ts
ADDED