@ceed/cds 1.34.1 → 1.36.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/components/Accordions/Accordions.d.ts +1 -0
  2. package/dist/components/Alert/Alert.d.ts +5 -5
  3. package/dist/components/Autocomplete/Autocomplete.d.ts +2 -2
  4. package/dist/components/Avatar/Avatar.d.ts +7 -17
  5. package/dist/components/Box/Box.d.ts +1 -0
  6. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +6 -5
  7. package/dist/components/Button/Button.d.ts +3 -2
  8. package/dist/components/Calendar/Calendar.d.ts +1 -0
  9. package/dist/components/Card/Card.d.ts +1 -0
  10. package/dist/components/Checkbox/Checkbox.d.ts +1 -0
  11. package/dist/components/Chip/Chip.d.ts +1 -0
  12. package/dist/components/Container/Container.d.ts +6 -1
  13. package/dist/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  14. package/dist/components/DialogActions/DialogActions.d.ts +1 -0
  15. package/dist/components/DialogContent/DialogContent.d.ts +1 -0
  16. package/dist/components/DialogFrame/DialogFrame.d.ts +1 -1
  17. package/dist/components/DialogTitle/DialogTitle.d.ts +1 -0
  18. package/dist/components/Divider/Divider.d.ts +1 -0
  19. package/dist/components/Drawer/Drawer.d.ts +1 -0
  20. package/dist/components/Dropdown/Dropdown.d.ts +28 -1
  21. package/dist/components/FilterableCheckboxGroup/FilterableCheckboxGroup.d.ts +1 -1
  22. package/dist/components/FormControl/FormControl.d.ts +1 -0
  23. package/dist/components/FormHelperText/FormHelperText.d.ts +1 -0
  24. package/dist/components/FormLabel/FormLabel.d.ts +1 -0
  25. package/dist/components/Grid/Grid.d.ts +1 -0
  26. package/dist/components/IconButton/IconButton.d.ts +3 -2
  27. package/dist/components/IconMenuButton/IconMenuButton.d.ts +7 -6
  28. package/dist/components/InfoSign/InfoSign.d.ts +3 -2
  29. package/dist/components/Input/Input.d.ts +8 -22
  30. package/dist/components/InsetDrawer/InsetDrawer.d.ts +1 -0
  31. package/dist/components/Markdown/Markdown.d.ts +9 -24
  32. package/dist/components/Menu/Menu.d.ts +2 -1
  33. package/dist/components/MenuButton/MenuButton.d.ts +10 -8
  34. package/dist/components/Modal/Modal.d.ts +4 -2
  35. package/dist/components/NavigationGroup/NavigationGroup.d.ts +3 -2
  36. package/dist/components/NavigationItem/NavigationItem.d.ts +3 -2
  37. package/dist/components/Navigator/Navigator.d.ts +5 -4
  38. package/dist/components/Pagination/Pagination.d.ts +2 -2
  39. package/dist/components/Radio/Radio.d.ts +1 -0
  40. package/dist/components/RadioList/RadioList.d.ts +3 -2
  41. package/dist/components/Select/Select.d.ts +12 -10
  42. package/dist/components/Sheet/Sheet.d.ts +1 -0
  43. package/dist/components/Stack/Stack.d.ts +1 -0
  44. package/dist/components/Stepper/Stepper.d.ts +2 -1
  45. package/dist/components/Switch/Switch.d.ts +1 -0
  46. package/dist/components/Table/Table.d.ts +7 -5
  47. package/dist/components/Tabs/Tabs.d.ts +1 -0
  48. package/dist/components/Textarea/Textarea.d.ts +8 -20
  49. package/dist/components/ThemeProvider/ThemeProvider.d.ts +15 -0
  50. package/dist/components/Tooltip/Tooltip.d.ts +1 -0
  51. package/dist/components/Typography/Typography.d.ts +1 -0
  52. package/dist/components/Uploader/Uploader.d.ts +18 -17
  53. package/dist/components/data-display/Avatar.md +60 -72
  54. package/dist/components/data-display/Badge.md +197 -181
  55. package/dist/components/data-display/Chip.md +164 -142
  56. package/dist/components/data-display/DataTable.md +843 -338
  57. package/dist/components/data-display/InfoSign.md +1 -3
  58. package/dist/components/data-display/Markdown.md +93 -125
  59. package/dist/components/data-display/Table.md +1453 -1007
  60. package/dist/components/data-display/Typography.md +113 -116
  61. package/dist/components/feedback/Alert.md +80 -86
  62. package/dist/components/feedback/CircularProgress.md +32 -36
  63. package/dist/components/feedback/Dialog.md +25 -17
  64. package/dist/components/feedback/Modal.md +296 -264
  65. package/dist/components/feedback/Skeleton.md +125 -89
  66. package/dist/components/index.d.ts +62 -2
  67. package/dist/components/inputs/Autocomplete.md +191 -95
  68. package/dist/components/inputs/Button.md +83 -83
  69. package/dist/components/inputs/ButtonGroup.md +195 -185
  70. package/dist/components/inputs/Calendar.md +25 -28
  71. package/dist/components/inputs/Checkbox.md +11 -29
  72. package/dist/components/inputs/CurrencyInput.md +4 -4
  73. package/dist/components/inputs/DatePicker.md +229 -110
  74. package/dist/components/inputs/DateRangePicker.md +248 -137
  75. package/dist/components/inputs/FilterableCheckboxGroup.md +115 -55
  76. package/dist/components/inputs/FormControl.md +75 -69
  77. package/dist/components/inputs/IconButton.md +229 -205
  78. package/dist/components/inputs/Input.md +131 -98
  79. package/dist/components/inputs/MonthPicker.md +186 -84
  80. package/dist/components/inputs/MonthRangePicker.md +73 -49
  81. package/dist/components/inputs/PercentageInput.md +15 -31
  82. package/dist/components/inputs/RadioButton.md +320 -256
  83. package/dist/components/inputs/RadioList.md +66 -50
  84. package/dist/components/inputs/RadioTileGroup.md +287 -170
  85. package/dist/components/inputs/SearchBar.md +82 -60
  86. package/dist/components/inputs/Select.md +106 -95
  87. package/dist/components/inputs/Slider.md +153 -102
  88. package/dist/components/inputs/Switch.md +193 -138
  89. package/dist/components/inputs/Textarea.md +15 -20
  90. package/dist/components/inputs/Uploader/Uploader.md +68 -39
  91. package/dist/components/layout/Box.md +841 -662
  92. package/dist/components/layout/Container.md +3 -11
  93. package/dist/components/layout/Grid.md +480 -394
  94. package/dist/components/layout/Stack.md +739 -566
  95. package/dist/components/navigation/Breadcrumbs.md +4 -4
  96. package/dist/components/navigation/Drawer.md +34 -25
  97. package/dist/components/navigation/Dropdown.md +745 -408
  98. package/dist/components/navigation/IconMenuButton.md +14 -6
  99. package/dist/components/navigation/InsetDrawer.md +8 -13
  100. package/dist/components/navigation/Link.md +1 -2
  101. package/dist/components/navigation/Menu.md +623 -502
  102. package/dist/components/navigation/MenuButton.md +18 -10
  103. package/dist/components/navigation/NavigationGroup.md +19 -50
  104. package/dist/components/navigation/NavigationItem.md +6 -6
  105. package/dist/components/navigation/Navigator.md +26 -28
  106. package/dist/components/navigation/Pagination.md +86 -75
  107. package/dist/components/navigation/Stepper.md +2 -12
  108. package/dist/components/navigation/Tabs.md +48 -36
  109. package/dist/components/surfaces/Accordions.md +89 -172
  110. package/dist/components/surfaces/Card.md +1094 -709
  111. package/dist/components/surfaces/Divider.md +562 -412
  112. package/dist/components/surfaces/Sheet.md +700 -518
  113. package/dist/guides/ThemeProvider.md +8 -8
  114. package/dist/index.browser.js +5 -5
  115. package/dist/index.browser.js.map +4 -4
  116. package/dist/index.cjs +1082 -1036
  117. package/dist/index.d.ts +2 -1
  118. package/dist/index.js +749 -695
  119. package/framer/index.js +1 -1
  120. package/package.json +34 -36
@@ -1,20 +1,20 @@
1
1
  # Dropdown
2
2
 
3
- Dropdown is the stateful container that orchestrates the open/close behavior of a menu system. It acts as the invisible glue between a trigger element (such as MenuButton or IconMenuButton) and the Menu that appears when activated. Dropdown itself renders no visible UI -- it provides context so that its children can coordinate with each other.
3
+ Dropdown is the stateful container that orchestrates the open/close behavior of a menu system. It acts as the invisible glue between a trigger element (`MenuButtonTrigger`) and the Menu that appears when activated. Dropdown itself renders no visible UI -- it provides context so that its children can coordinate with each other.
4
4
 
5
- Dropdown is part of a **composition pattern** together with Menu, MenuItem, MenuButton, and IconMenuButton. While MenuButton and IconMenuButton are higher-level convenience components that bundle trigger + menu into a single prop-driven API, Dropdown gives you full control over the trigger element, menu content, and layout when you need custom compositions.
5
+ Dropdown is part of a **composition pattern** together with `MenuButtonTrigger`, `Menu`, and `MenuItem`. `MenuButtonTrigger` is the low-level trigger that connects to the Dropdown context; the standalone `MenuButton` and `IconMenuButton` are higher-level convenience components that bundle trigger + menu into a single prop-driven API. Use the Dropdown composition when you need full control over the trigger element, menu content, and layout. All of these symbols are imported from `@ceed/cds`.
6
6
 
7
7
  ```tsx
8
8
  <Dropdown>
9
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
10
- 메뉴 열기
11
- </MenuButton>
12
- <Menu>
13
- <MenuItem>옵션 1</MenuItem>
14
- <MenuItem>옵션 2</MenuItem>
15
- <MenuItem>옵션 3</MenuItem>
16
- </Menu>
17
- </Dropdown>
9
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
10
+ 메뉴 열기
11
+ </MenuButtonTrigger>
12
+ <Menu>
13
+ <MenuItem>옵션 1</MenuItem>
14
+ <MenuItem>옵션 2</MenuItem>
15
+ <MenuItem>옵션 3</MenuItem>
16
+ </Menu>
17
+ </Dropdown>
18
18
  ```
19
19
 
20
20
  | Field | Description | Default |
@@ -24,13 +24,12 @@ Dropdown is part of a **composition pattern** together with Menu, MenuItem, Menu
24
24
  ## Usage
25
25
 
26
26
  ```tsx
27
- import { Dropdown, Menu, MenuItem } from '@ceed/cds';
28
- import { MenuButton } from '@ceed/cds';
27
+ import { Dropdown, MenuButtonTrigger, Menu, MenuItem } from '@ceed/cds';
29
28
 
30
29
  function MyComponent() {
31
30
  return (
32
31
  <Dropdown>
33
- <MenuButton>Open Menu</MenuButton>
32
+ <MenuButtonTrigger>Open Menu</MenuButtonTrigger>
34
33
  <Menu>
35
34
  <MenuItem>Option 1</MenuItem>
36
35
  <MenuItem>Option 2</MenuItem>
@@ -41,537 +40,865 @@ function MyComponent() {
41
40
  }
42
41
  ```
43
42
 
43
+ > **`MenuButtonTrigger` vs `MenuButton`**: `MenuButtonTrigger` is the low-level trigger for the Dropdown composition (Joy UI's `MenuButton`, re-exported from `@ceed/cds` under this name). The separate `MenuButton` exported from `@ceed/cds` is a higher-level, self-contained component (driven by `buttonText` / `items` props) that renders its **own** Dropdown internally — it cannot be nested inside `<Dropdown>`.
44
+
44
45
  ## Composition Pattern
45
46
 
46
47
  Dropdown, Menu, MenuItem, MenuButton, and IconMenuButton form a **menu component family**. Choose the right level of abstraction for your use case:
47
48
 
48
- | Approach | When to use |
49
- | ------------------------------------ | ------------------------------------------------------- |
50
- | `MenuButton` (standalone) | Simple text-triggered menus with a list of items |
51
- | `IconMenuButton` (standalone) | Icon-only triggers in space-constrained areas |
52
- | `Dropdown` + `MenuButton` + `Menu` | Custom menu content (headers, dividers, mixed elements) |
53
- | `Dropdown` + custom trigger + `Menu` | Fully custom trigger elements |
49
+ | Approach | When to use |
50
+ | ----------------------------------------- | ------------------------------------------------------- |
51
+ | `MenuButton` (standalone) | Simple text-triggered menus with a list of items |
52
+ | `IconMenuButton` (standalone) | Icon-only triggers in space-constrained areas |
53
+ | `Dropdown` + `MenuButtonTrigger` + `Menu` | Custom menu content (headers, dividers, mixed elements) |
54
+ | `Dropdown` + custom trigger + `Menu` | Fully custom trigger elements |
55
+
56
+ ## Custom Trigger Components
54
57
 
55
- ## Features
58
+ The Dropdown trigger is **not limited to a plain button**. `MenuButtonTrigger` is simply the element that wires the open/close behavior — `onClick`, `aria-haspopup`, `aria-expanded`, focus management, and a `ref` — into the Dropdown context. You can render that behavior into a different component in two ways:
56
59
 
57
- ### Basic Dropdown
60
+ - **`slots={{ root: YourComponent }}`** — replace the trigger entirely with another component (e.g. `Button`, `IconButton`). `MenuButtonTrigger` injects all of the trigger behavior into your component as the root slot. Pass props to it via `slotProps={{ root: { ... } }}`.
61
+ - **`slotProps={{ root: { component: 'a' } }}`** (or the `component` prop) — keep `MenuButtonTrigger`'s styling and behavior but render a different HTML element.
58
62
 
59
- The simplest Dropdown pairs a MenuButton trigger with a Menu of items.
63
+ This is exactly how the higher-level `IconMenuButton` is built internally (`slots={{ root: IconButton }}`). The only requirement is that the chosen root is focusable and clickable — prefer button-like components (`Button`, `IconButton`). For non-interactive components like `Avatar`, wrap them in a button-like root rather than using them directly.
60
64
 
61
65
  ```tsx
62
- <Stack direction="row" spacing={2}>
63
- <Dropdown>
64
- <MenuButton>기본 메뉴</MenuButton>
65
- <Menu>
66
- <MenuItem>첫 번째 옵션</MenuItem>
67
- <MenuItem>두 번째 옵션</MenuItem>
68
- <MenuItem>세 번째 옵션</MenuItem>
69
- </Menu>
70
- </Dropdown>
66
+ import { Dropdown, MenuButtonTrigger, Menu, MenuItem, Button } from '@ceed/cds';
71
67
 
72
- <Dropdown>
73
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
74
- 아이콘 포함
75
- </MenuButton>
76
- <Menu>
77
- <MenuItem>옵션 A</MenuItem>
78
- <MenuItem>옵션 B</MenuItem>
79
- <MenuItem>옵션 C</MenuItem>
80
- </Menu>
81
- </Dropdown>
82
- </Stack>
68
+ // Any button-like component as the trigger
69
+ <Dropdown>
70
+ <MenuButtonTrigger slots={{ root: Button }} slotProps={{ root: { variant: 'soft', color: 'primary' } }}>
71
+ Open
72
+ </MenuButtonTrigger>
73
+ <Menu>
74
+ <MenuItem>Option 1</MenuItem>
75
+ <MenuItem>Option 2</MenuItem>
76
+ </Menu>
77
+ </Dropdown>;
83
78
  ```
84
79
 
85
- ### Button Variants
80
+ > **Note**: Every component above — including `MenuButtonTrigger` — is imported from `@ceed/cds`. No direct `@mui/joy` import is required.
86
81
 
87
- MenuButton supports `plain`, `outlined`, `soft`, and `solid` variants to match your design context.
82
+ ## Button as Dropdown Trigger
83
+
84
+ Replace the trigger with the HDS `Button` via `slots={{ root: Button }}` to match your button styling system.
88
85
 
89
86
  ```tsx
90
- <Stack direction="row" spacing={2} flexWrap="wrap">
91
- <Dropdown>
92
- <MenuButton variant="plain">Plain</MenuButton>
87
+ <Dropdown>
88
+ <MenuButtonTrigger
89
+ slots={{
90
+ root: Button
91
+ }}
92
+ slotProps={{
93
+ root: {
94
+ variant: "soft",
95
+ color: "primary",
96
+ endDecorator: <KeyboardArrowDownIcon />
97
+ }
98
+ }}
99
+ >
100
+ HDS Button 트리거
101
+ </MenuButtonTrigger>
93
102
  <Menu>
94
103
  <MenuItem>옵션 1</MenuItem>
95
104
  <MenuItem>옵션 2</MenuItem>
105
+ <MenuItem>옵션 3</MenuItem>
96
106
  </Menu>
97
107
  </Dropdown>
108
+ ```
98
109
 
99
- <Dropdown>
100
- <MenuButton variant="outlined">Outlined</MenuButton>
101
- <Menu>
102
- <MenuItem>옵션 1</MenuItem>
103
- <MenuItem>옵션 2</MenuItem>
104
- </Menu>
105
- </Dropdown>
110
+ ## IconButton Trigger
106
111
 
107
- <Dropdown>
108
- <MenuButton variant="soft">Soft</MenuButton>
109
- <Menu>
110
- <MenuItem>옵션 1</MenuItem>
111
- <MenuItem>옵션 2</MenuItem>
112
- </Menu>
113
- </Dropdown>
112
+ For space-constrained areas like table rows or card headers, use an `IconButton` as the trigger. Always provide an `aria-label`.
114
113
 
115
- <Dropdown>
116
- <MenuButton variant="solid">Solid</MenuButton>
117
- <Menu>
118
- <MenuItem>옵션 1</MenuItem>
119
- <MenuItem>옵션 2</MenuItem>
114
+ ```tsx
115
+ <Dropdown>
116
+ <MenuButtonTrigger
117
+ slots={{
118
+ root: IconButton
119
+ }}
120
+ slotProps={{
121
+ root: {
122
+ variant: "plain",
123
+ color: "neutral",
124
+ "aria-label": "추가 작업"
125
+ }
126
+ }}
127
+ >
128
+ <MoreVertIcon />
129
+ </MenuButtonTrigger>
130
+ <Menu placement="bottom-end">
131
+ <MenuItem>
132
+ <EditIcon
133
+ sx={{
134
+ mr: 1
135
+ }}
136
+ />
137
+ 편집
138
+ </MenuItem>
139
+ <MenuItem>
140
+ <DeleteIcon
141
+ sx={{
142
+ mr: 1,
143
+ color: "danger.500"
144
+ }}
145
+ />
146
+ <Typography color="danger">삭제</Typography>
147
+ </MenuItem>
120
148
  </Menu>
121
149
  </Dropdown>
122
- </Stack>
123
150
  ```
124
151
 
125
- ### Button Colors
152
+ ## Avatar Trigger
126
153
 
127
- Apply semantic colors to the trigger button for different emphasis levels.
154
+ Profile menus often use an avatar as the trigger. Wrap the `Avatar` in a button-like `IconButton` root so the trigger stays keyboard- and screen-reader-accessible.
128
155
 
129
156
  ```tsx
130
- <Stack direction="row" spacing={2} flexWrap="wrap">
131
- <Dropdown>
132
- <MenuButton color="primary" variant="soft">
133
- Primary
134
- </MenuButton>
135
- <Menu>
136
- <MenuItem>옵션 1</MenuItem>
137
- <MenuItem>옵션 2</MenuItem>
157
+ <Dropdown>
158
+ <MenuButtonTrigger
159
+ slots={{
160
+ root: IconButton
161
+ }}
162
+ slotProps={{
163
+ root: {
164
+ variant: "plain",
165
+ color: "neutral",
166
+ "aria-label": "프로필 메뉴",
167
+ sx: {
168
+ borderRadius: "50%",
169
+ p: 0
170
+ }
171
+ }
172
+ }}
173
+ >
174
+ <Avatar size="sm" variant="soft" color="primary">
175
+
176
+ </Avatar>
177
+ </MenuButtonTrigger>
178
+ <Menu
179
+ placement="bottom-end"
180
+ sx={{
181
+ minWidth: 180
182
+ }}
183
+ >
184
+ <Stack px={2} py={1} spacing={0.5}>
185
+ <Typography level="title-sm" fontWeight="bold">
186
+ 김철수
187
+ </Typography>
188
+ <Typography level="body-xs" color="neutral">
189
+ kim@example.com
190
+ </Typography>
191
+ </Stack>
192
+ <ListDivider />
193
+ <MenuItem>
194
+ <PersonIcon
195
+ sx={{
196
+ mr: 1
197
+ }}
198
+ />
199
+ 내 프로필
200
+ </MenuItem>
201
+ <MenuItem>
202
+ <LogoutIcon
203
+ sx={{
204
+ mr: 1
205
+ }}
206
+ />
207
+ 로그아웃
208
+ </MenuItem>
138
209
  </Menu>
139
210
  </Dropdown>
211
+ ```
140
212
 
141
- <Dropdown>
142
- <MenuButton color="neutral" variant="soft">
143
- Neutral
144
- </MenuButton>
145
- <Menu>
146
- <MenuItem>옵션 1</MenuItem>
147
- <MenuItem>옵션 2</MenuItem>
148
- </Menu>
149
- </Dropdown>
213
+ ## Custom Trigger Element
150
214
 
151
- <Dropdown>
152
- <MenuButton color="danger" variant="soft">
153
- Danger
154
- </MenuButton>
215
+ To keep `MenuButtonTrigger`'s behavior and styling but render a different HTML tag, set `slotProps={{ root: { component: 'a' } }}`.
216
+
217
+ ```tsx
218
+ <Dropdown>
219
+ <MenuButtonTrigger
220
+ variant="outlined"
221
+ slotProps={{
222
+ root: {
223
+ component: "a",
224
+ role: "button"
225
+ }
226
+ }}
227
+ endDecorator={<ExpandMoreIcon />}
228
+ sx={{
229
+ cursor: "pointer"
230
+ }}
231
+ >
232
+ 앵커(a) 엘리먼트 트리거
233
+ </MenuButtonTrigger>
155
234
  <Menu>
156
235
  <MenuItem>옵션 1</MenuItem>
157
236
  <MenuItem>옵션 2</MenuItem>
158
237
  </Menu>
159
238
  </Dropdown>
239
+ ```
160
240
 
161
- <Dropdown>
162
- <MenuButton color="success" variant="soft">
163
- Success
164
- </MenuButton>
241
+ ## useMenuButton Hook Trigger
242
+
243
+ When you want a fully custom trigger element without using `MenuButtonTrigger` at all, use the `useMenuButton` hook. Call it inside a child of `<Dropdown>` and spread `getRootProps()` onto any element — the hook wires `onClick`, `aria-*`, and the `ref` into the Dropdown context. Render a native `<button>` (or add keyboard handling) so the trigger stays accessible.
244
+
245
+ ```tsx
246
+ import { Dropdown, useMenuButton, Menu, MenuItem, Box } from '@ceed/cds';
247
+
248
+ function HookTrigger({ children }) {
249
+ const { getRootProps, open } = useMenuButton();
250
+ return (
251
+ <Box component="button" {...getRootProps()} sx={{ cursor: 'pointer' /* custom styles */ }}>
252
+ {children}
253
+ </Box>
254
+ );
255
+ }
256
+
257
+ <Dropdown>
258
+ <HookTrigger>Custom trigger</HookTrigger>
259
+ <Menu>
260
+ <MenuItem>Option 1</MenuItem>
261
+ <MenuItem>Option 2</MenuItem>
262
+ </Menu>
263
+ </Dropdown>;
264
+ ```
265
+
266
+ ```tsx
267
+ <Dropdown>
268
+ <HookTrigger>완전 커스텀 트리거 (useMenuButton)</HookTrigger>
165
269
  <Menu>
166
270
  <MenuItem>옵션 1</MenuItem>
167
271
  <MenuItem>옵션 2</MenuItem>
272
+ <MenuItem>옵션 3</MenuItem>
168
273
  </Menu>
169
274
  </Dropdown>
275
+ ```
170
276
 
171
- <Dropdown>
172
- <MenuButton color="warning" variant="soft">
173
- Warning
174
- </MenuButton>
277
+ > **Note**: A trigger is always required — Dropdown itself renders no UI and only provides open/close state plus the anchor context. The trigger can be `MenuButtonTrigger` (optionally with `slots` / `component`) or your own element wired via `useMenuButton`. Both read the same Dropdown context, and both are available from `@ceed/cds`.
278
+
279
+ ## useMenuButton with Button
280
+
281
+ You can also spread `getRootProps()` onto the HDS `Button`. Because `Button` renders a native `<button>`, it already provides focusability, Enter/Space activation, and the `button` role — so accessibility is preserved by adding only the `aria-*` / `onClick` / `ref` from the hook. `getRootProps()` does not include `onKeyDown`, which is why a native button (not a `div`) matters here.
282
+
283
+ ```tsx
284
+ import { Dropdown, useMenuButton, Menu, MenuItem, Button } from '@ceed/cds';
285
+
286
+ function HookButton({ children }) {
287
+ const { getRootProps, open } = useMenuButton();
288
+ return (
289
+ <Button {...getRootProps()} variant={open ? 'soft' : 'outlined'} color="neutral">
290
+ {children}
291
+ </Button>
292
+ );
293
+ }
294
+ ```
295
+
296
+ ```tsx
297
+ <Dropdown>
298
+ <HookButton>useMenuButton + Button</HookButton>
175
299
  <Menu>
176
300
  <MenuItem>옵션 1</MenuItem>
177
301
  <MenuItem>옵션 2</MenuItem>
302
+ <MenuItem>옵션 3</MenuItem>
178
303
  </Menu>
179
304
  </Dropdown>
180
- </Stack>
181
305
  ```
182
306
 
183
- ### Menu Placements
307
+ > **Tip**: For a plain button trigger, prefer `MenuButtonTrigger slots={{ root: Button }}` — it is more concise and wires the menu keyboard behavior for you. Reach for `useMenuButton` when you need a non-button or deeply custom trigger element.
308
+
309
+ ## Dropdown Basic
310
+
311
+ The simplest Dropdown pairs a `MenuButtonTrigger` with a Menu of items.
312
+
313
+ ```tsx
314
+ <Stack direction="row" spacing={2}>
315
+ <Dropdown>
316
+ <MenuButtonTrigger>기본 메뉴</MenuButtonTrigger>
317
+ <Menu>
318
+ <MenuItem>첫 번째 옵션</MenuItem>
319
+ <MenuItem>두 번째 옵션</MenuItem>
320
+ <MenuItem>세 번째 옵션</MenuItem>
321
+ </Menu>
322
+ </Dropdown>
323
+
324
+ <Dropdown>
325
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
326
+ 아이콘 포함
327
+ </MenuButtonTrigger>
328
+ <Menu>
329
+ <MenuItem>옵션 A</MenuItem>
330
+ <MenuItem>옵션 B</MenuItem>
331
+ <MenuItem>옵션 C</MenuItem>
332
+ </Menu>
333
+ </Dropdown>
334
+ </Stack>
335
+ ```
336
+
337
+ ## Dropdown Button Variants
338
+
339
+ `MenuButtonTrigger` supports `plain`, `outlined`, `soft`, and `solid` variants to match your design context.
340
+
341
+ ```tsx
342
+ <Stack direction="row" spacing={2} flexWrap="wrap">
343
+ <Dropdown>
344
+ <MenuButtonTrigger variant="plain">Plain</MenuButtonTrigger>
345
+ <Menu>
346
+ <MenuItem>옵션 1</MenuItem>
347
+ <MenuItem>옵션 2</MenuItem>
348
+ </Menu>
349
+ </Dropdown>
350
+
351
+ <Dropdown>
352
+ <MenuButtonTrigger variant="outlined">Outlined</MenuButtonTrigger>
353
+ <Menu>
354
+ <MenuItem>옵션 1</MenuItem>
355
+ <MenuItem>옵션 2</MenuItem>
356
+ </Menu>
357
+ </Dropdown>
358
+
359
+ <Dropdown>
360
+ <MenuButtonTrigger variant="soft">Soft</MenuButtonTrigger>
361
+ <Menu>
362
+ <MenuItem>옵션 1</MenuItem>
363
+ <MenuItem>옵션 2</MenuItem>
364
+ </Menu>
365
+ </Dropdown>
366
+
367
+ <Dropdown>
368
+ <MenuButtonTrigger variant="solid">Solid</MenuButtonTrigger>
369
+ <Menu>
370
+ <MenuItem>옵션 1</MenuItem>
371
+ <MenuItem>옵션 2</MenuItem>
372
+ </Menu>
373
+ </Dropdown>
374
+ </Stack>
375
+ ```
376
+
377
+ ## Dropdown Button Colors
378
+
379
+ Apply semantic colors to the trigger button for different emphasis levels.
380
+
381
+ ```tsx
382
+ <Stack direction="row" spacing={2} flexWrap="wrap">
383
+ <Dropdown>
384
+ <MenuButtonTrigger color="primary" variant="soft">
385
+ Primary
386
+ </MenuButtonTrigger>
387
+ <Menu>
388
+ <MenuItem>옵션 1</MenuItem>
389
+ <MenuItem>옵션 2</MenuItem>
390
+ </Menu>
391
+ </Dropdown>
392
+
393
+ <Dropdown>
394
+ <MenuButtonTrigger color="neutral" variant="soft">
395
+ Neutral
396
+ </MenuButtonTrigger>
397
+ <Menu>
398
+ <MenuItem>옵션 1</MenuItem>
399
+ <MenuItem>옵션 2</MenuItem>
400
+ </Menu>
401
+ </Dropdown>
402
+
403
+ <Dropdown>
404
+ <MenuButtonTrigger color="danger" variant="soft">
405
+ Danger
406
+ </MenuButtonTrigger>
407
+ <Menu>
408
+ <MenuItem>옵션 1</MenuItem>
409
+ <MenuItem>옵션 2</MenuItem>
410
+ </Menu>
411
+ </Dropdown>
412
+
413
+ <Dropdown>
414
+ <MenuButtonTrigger color="success" variant="soft">
415
+ Success
416
+ </MenuButtonTrigger>
417
+ <Menu>
418
+ <MenuItem>옵션 1</MenuItem>
419
+ <MenuItem>옵션 2</MenuItem>
420
+ </Menu>
421
+ </Dropdown>
422
+
423
+ <Dropdown>
424
+ <MenuButtonTrigger color="warning" variant="soft">
425
+ Warning
426
+ </MenuButtonTrigger>
427
+ <Menu>
428
+ <MenuItem>옵션 1</MenuItem>
429
+ <MenuItem>옵션 2</MenuItem>
430
+ </Menu>
431
+ </Dropdown>
432
+ </Stack>
433
+ ```
434
+
435
+ ## Dropdown Menu Placements
184
436
 
185
437
  Control where the menu appears relative to the trigger using the `placement` prop on Menu.
186
438
 
187
439
  ```tsx
188
- <Stack direction="row" spacing={2} sx={{
189
- mt: 4
190
- }}>
191
- <Dropdown>
192
- <MenuButton variant="outlined">Bottom Start</MenuButton>
193
- <Menu placement="bottom-start">
194
- <MenuItem>옵션 1</MenuItem>
195
- <MenuItem>옵션 2</MenuItem>
196
- <MenuItem>옵션 3</MenuItem>
197
- </Menu>
198
- </Dropdown>
440
+ <Stack
441
+ direction="row"
442
+ spacing={2}
443
+ sx={{
444
+ mt: 4
445
+ }}
446
+ >
447
+ <Dropdown>
448
+ <MenuButtonTrigger variant="outlined">Bottom Start</MenuButtonTrigger>
449
+ <Menu placement="bottom-start">
450
+ <MenuItem>옵션 1</MenuItem>
451
+ <MenuItem>옵션 2</MenuItem>
452
+ <MenuItem>옵션 3</MenuItem>
453
+ </Menu>
454
+ </Dropdown>
199
455
 
200
- <Dropdown>
201
- <MenuButton variant="outlined">Bottom</MenuButton>
202
- <Menu placement="bottom">
203
- <MenuItem>옵션 1</MenuItem>
204
- <MenuItem>옵션 2</MenuItem>
205
- <MenuItem>옵션 3</MenuItem>
206
- </Menu>
207
- </Dropdown>
456
+ <Dropdown>
457
+ <MenuButtonTrigger variant="outlined">Bottom</MenuButtonTrigger>
458
+ <Menu placement="bottom">
459
+ <MenuItem>옵션 1</MenuItem>
460
+ <MenuItem>옵션 2</MenuItem>
461
+ <MenuItem>옵션 3</MenuItem>
462
+ </Menu>
463
+ </Dropdown>
208
464
 
209
- <Dropdown>
210
- <MenuButton variant="outlined">Bottom End</MenuButton>
211
- <Menu placement="bottom-end">
212
- <MenuItem>옵션 1</MenuItem>
213
- <MenuItem>옵션 2</MenuItem>
214
- <MenuItem>옵션 3</MenuItem>
215
- </Menu>
216
- </Dropdown>
465
+ <Dropdown>
466
+ <MenuButtonTrigger variant="outlined">Bottom End</MenuButtonTrigger>
467
+ <Menu placement="bottom-end">
468
+ <MenuItem>옵션 1</MenuItem>
469
+ <MenuItem>옵션 2</MenuItem>
470
+ <MenuItem>옵션 3</MenuItem>
471
+ </Menu>
472
+ </Dropdown>
217
473
 
218
- <Dropdown>
219
- <MenuButton variant="outlined">Top Start</MenuButton>
220
- <Menu placement="top-start">
221
- <MenuItem>옵션 1</MenuItem>
222
- <MenuItem>옵션 2</MenuItem>
223
- <MenuItem>옵션 3</MenuItem>
224
- </Menu>
225
- </Dropdown>
226
- </Stack>
474
+ <Dropdown>
475
+ <MenuButtonTrigger variant="outlined">Top Start</MenuButtonTrigger>
476
+ <Menu placement="top-start">
477
+ <MenuItem>옵션 1</MenuItem>
478
+ <MenuItem>옵션 2</MenuItem>
479
+ <MenuItem>옵션 3</MenuItem>
480
+ </Menu>
481
+ </Dropdown>
482
+ </Stack>
227
483
  ```
228
484
 
229
- ### With Icons
485
+ ## Dropdown with Icons
230
486
 
231
487
  Add icons to menu items and decorators to the trigger button for richer visual communication.
232
488
 
233
489
  ```tsx
234
490
  <Dropdown>
235
- <MenuButton variant="outlined" startDecorator={<PersonIcon />} endDecorator={<ExpandMoreIcon />}>
236
- 사용자 메뉴
237
- </MenuButton>
238
- <Menu>
239
- <MenuItem>
240
- <PersonIcon sx={{
241
- mr: 1
242
- }} />
243
- 프로필 보기
244
- </MenuItem>
245
- <MenuItem>
246
- <SettingsIcon sx={{
247
- mr: 1
248
- }} />
249
- 설정
250
- </MenuItem>
251
- <ListDivider />
252
- <MenuItem>
253
- <LogoutIcon sx={{
254
- mr: 1
255
- }} />
256
- 로그아웃
257
- </MenuItem>
258
- </Menu>
259
- </Dropdown>
491
+ <MenuButtonTrigger
492
+ variant="outlined"
493
+ startDecorator={<PersonIcon />}
494
+ endDecorator={<ExpandMoreIcon />}
495
+ >
496
+ 사용자 메뉴
497
+ </MenuButtonTrigger>
498
+ <Menu>
499
+ <MenuItem>
500
+ <PersonIcon
501
+ sx={{
502
+ mr: 1
503
+ }}
504
+ />
505
+ 프로필 보기
506
+ </MenuItem>
507
+ <MenuItem>
508
+ <SettingsIcon
509
+ sx={{
510
+ mr: 1
511
+ }}
512
+ />
513
+ 설정
514
+ </MenuItem>
515
+ <ListDivider />
516
+ <MenuItem>
517
+ <LogoutIcon
518
+ sx={{
519
+ mr: 1
520
+ }}
521
+ />
522
+ 로그아웃
523
+ </MenuItem>
524
+ </Menu>
525
+ </Dropdown>
260
526
  ```
261
527
 
262
- ### Actions Menu
528
+ ## Dropdown Actions Menu
263
529
 
264
530
  Build action menus for edit, delete, and other operations. Use `color="danger"` on destructive items.
265
531
 
266
532
  ```tsx
267
533
  <Dropdown>
268
- <MenuButton variant="plain" color="neutral" endDecorator={<ExpandMoreIcon />} sx={{
269
- fontSize: 'sm'
270
- }}>
271
- 작업
272
- </MenuButton>
273
- <Menu>
274
- <MenuItem>
275
- <EditIcon sx={{
276
- mr: 1,
277
- fontSize: 'sm'
278
- }} />
279
- 편집
280
- </MenuItem>
281
- <MenuItem>
282
- <DeleteIcon sx={{
283
- mr: 1,
284
- fontSize: 'sm',
285
- color: 'danger.500'
286
- }} />
287
- <Typography color="danger">삭제</Typography>
288
- </MenuItem>
289
- </Menu>
290
- </Dropdown>
534
+ <MenuButtonTrigger
535
+ variant="plain"
536
+ color="neutral"
537
+ endDecorator={<ExpandMoreIcon />}
538
+ sx={{
539
+ fontSize: "sm"
540
+ }}
541
+ >
542
+ 작업
543
+ </MenuButtonTrigger>
544
+ <Menu>
545
+ <MenuItem>
546
+ <EditIcon
547
+ sx={{
548
+ mr: 1,
549
+ fontSize: "sm"
550
+ }}
551
+ />
552
+ 편집
553
+ </MenuItem>
554
+ <MenuItem>
555
+ <DeleteIcon
556
+ sx={{
557
+ mr: 1,
558
+ fontSize: "sm",
559
+ color: "danger.500"
560
+ }}
561
+ />
562
+ <Typography color="danger">삭제</Typography>
563
+ </MenuItem>
564
+ </Menu>
565
+ </Dropdown>
291
566
  ```
292
567
 
293
- ### Filter Menu
568
+ ## Dropdown Filter Menu
294
569
 
295
570
  Use Dropdown as an interactive filter selector with state management.
296
571
 
297
572
  ```tsx
298
573
  <Dropdown>
299
- <MenuButton variant="outlined" startDecorator={<FilterListIcon />} endDecorator={<ExpandMoreIcon />}>
300
- 필터: {selectedFilter}
301
- </MenuButton>
302
- <Menu>
303
- <MenuItem onClick={() => setSelectedFilter('전체')}>전체</MenuItem>
304
- <MenuItem onClick={() => setSelectedFilter('활성')}>활성</MenuItem>
305
- <MenuItem onClick={() => setSelectedFilter('비활성')}>비활성</MenuItem>
306
- <MenuItem onClick={() => setSelectedFilter('대기중')}>대기중</MenuItem>
307
- </Menu>
308
- </Dropdown>
574
+ <MenuButtonTrigger
575
+ variant="outlined"
576
+ startDecorator={<FilterListIcon />}
577
+ endDecorator={<ExpandMoreIcon />}
578
+ >
579
+ 필터: {selectedFilter}
580
+ </MenuButtonTrigger>
581
+ <Menu>
582
+ <MenuItem onClick={() => setSelectedFilter("전체")}>전체</MenuItem>
583
+ <MenuItem onClick={() => setSelectedFilter("활성")}>활성</MenuItem>
584
+ <MenuItem onClick={() => setSelectedFilter("비활성")}>비활성</MenuItem>
585
+ <MenuItem onClick={() => setSelectedFilter("대기중")}>대기중</MenuItem>
586
+ </Menu>
587
+ </Dropdown>
309
588
  ```
310
589
 
311
- ### Profile Dropdown
590
+ ## Dropdown Profile
312
591
 
313
592
  Combine custom content (user info header) with standard menu items for profile menus.
314
593
 
315
594
  ```tsx
316
595
  <Dropdown>
317
- <MenuButton variant="plain" color="neutral" startDecorator={<Avatar size="sm" variant="soft" color="primary">
318
-
319
- </Avatar>} endDecorator={<ExpandMoreIcon />} sx={{
320
- gap: 1,
321
- padding: 1
322
- }}>
323
- 김철수
324
- </MenuButton>
325
- <Menu sx={{
326
- minWidth: 200
327
- }}>
328
- <Stack px={2} py={1.5} spacing={0.5}>
329
- <Typography level="title-sm" fontWeight="bold">
330
- 김철수
331
- </Typography>
332
- <Typography level="body-xs" color="neutral">
333
- kim@example.com
334
- </Typography>
335
- </Stack>
336
-
337
- <ListDivider />
338
-
339
- <MenuItem>
340
- <PersonIcon sx={{
341
- mr: 1
342
- }} />내 프로필
343
- </MenuItem>
344
- <MenuItem>
345
- <SettingsIcon sx={{
346
- mr: 1
347
- }} />
348
- 계정 설정
349
- </MenuItem>
350
-
351
- <ListDivider />
352
-
353
- <MenuItem>
354
- <LogoutIcon sx={{
355
- mr: 1
356
- }} />
357
- 로그아웃
358
- </MenuItem>
359
- </Menu>
360
- </Dropdown>
596
+ <MenuButtonTrigger
597
+ variant="plain"
598
+ color="neutral"
599
+ startDecorator={
600
+ <Avatar size="sm" variant="soft" color="primary">
601
+
602
+ </Avatar>
603
+ }
604
+ endDecorator={<ExpandMoreIcon />}
605
+ sx={{
606
+ gap: 1,
607
+ padding: 1
608
+ }}
609
+ >
610
+ 김철수
611
+ </MenuButtonTrigger>
612
+ <Menu
613
+ sx={{
614
+ minWidth: 200
615
+ }}
616
+ >
617
+ <Stack px={2} py={1.5} spacing={0.5}>
618
+ <Typography level="title-sm" fontWeight="bold">
619
+ 김철수
620
+ </Typography>
621
+ <Typography level="body-xs" color="neutral">
622
+ kim@example.com
623
+ </Typography>
624
+ </Stack>
625
+
626
+ <ListDivider />
627
+
628
+ <MenuItem>
629
+ <PersonIcon
630
+ sx={{
631
+ mr: 1
632
+ }}
633
+ />
634
+ 프로필
635
+ </MenuItem>
636
+ <MenuItem>
637
+ <SettingsIcon
638
+ sx={{
639
+ mr: 1
640
+ }}
641
+ />
642
+ 계정 설정
643
+ </MenuItem>
644
+
645
+ <ListDivider />
646
+
647
+ <MenuItem>
648
+ <LogoutIcon
649
+ sx={{
650
+ mr: 1
651
+ }}
652
+ />
653
+ 로그아웃
654
+ </MenuItem>
655
+ </Menu>
656
+ </Dropdown>
361
657
  ```
362
658
 
363
- ### Nested Menus
659
+ ## Dropdown Nested Menus
364
660
 
365
661
  Place multiple Dropdown instances side by side to create menu bar patterns.
366
662
 
367
663
  ```tsx
368
664
  <Stack direction="row" spacing={3}>
369
- <Dropdown>
370
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
371
- 파일
372
- </MenuButton>
373
- <Menu>
374
- <MenuItem>새 파일</MenuItem>
375
- <MenuItem>열기</MenuItem>
376
- <MenuItem disabled>최근 파일</MenuItem>
377
- <ListDivider />
378
- <MenuItem>저장</MenuItem>
379
- <MenuItem>다른 이름으로 저장</MenuItem>
380
- <ListDivider />
381
- <MenuItem>인쇄</MenuItem>
382
- </Menu>
383
- </Dropdown>
665
+ <Dropdown>
666
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
667
+ 파일
668
+ </MenuButtonTrigger>
669
+ <Menu>
670
+ <MenuItem>새 파일</MenuItem>
671
+ <MenuItem>열기</MenuItem>
672
+ <MenuItem disabled>최근 파일</MenuItem>
673
+ <ListDivider />
674
+ <MenuItem>저장</MenuItem>
675
+ <MenuItem>다른 이름으로 저장</MenuItem>
676
+ <ListDivider />
677
+ <MenuItem>인쇄</MenuItem>
678
+ </Menu>
679
+ </Dropdown>
384
680
 
385
- <Dropdown>
386
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
387
- 편집
388
- </MenuButton>
389
- <Menu>
390
- <MenuItem>실행 취소</MenuItem>
391
- <MenuItem>다시 실행</MenuItem>
392
- <ListDivider />
393
- <MenuItem>잘라내기</MenuItem>
394
- <MenuItem>복사</MenuItem>
395
- <MenuItem>붙여넣기</MenuItem>
396
- <ListDivider />
397
- <MenuItem>모두 선택</MenuItem>
398
- <MenuItem>찾기</MenuItem>
399
- </Menu>
400
- </Dropdown>
401
- </Stack>
681
+ <Dropdown>
682
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
683
+ 편집
684
+ </MenuButtonTrigger>
685
+ <Menu>
686
+ <MenuItem>실행 취소</MenuItem>
687
+ <MenuItem>다시 실행</MenuItem>
688
+ <ListDivider />
689
+ <MenuItem>잘라내기</MenuItem>
690
+ <MenuItem>복사</MenuItem>
691
+ <MenuItem>붙여넣기</MenuItem>
692
+ <ListDivider />
693
+ <MenuItem>모두 선택</MenuItem>
694
+ <MenuItem>찾기</MenuItem>
695
+ </Menu>
696
+ </Dropdown>
697
+ </Stack>
402
698
  ```
403
699
 
404
- ### Controlled Dropdown
700
+ ## Dropdown Controlled State
405
701
 
406
702
  Use the `open` and `onOpenChange` props to programmatically control the menu state.
407
703
 
408
704
  ```tsx
409
705
  <Stack spacing={2}>
410
- <Typography level="body-sm">메뉴 상태: {open ? '열림' : '닫힘'}</Typography>
706
+ <Typography level="body-sm">메뉴 상태: {open ? "열림" : "닫힘"}</Typography>
411
707
 
412
- <Dropdown open={open} onOpenChange={(event, isOpen) => setOpen(isOpen)}>
413
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
414
- 제어된 메뉴
415
- </MenuButton>
416
- <Menu>
417
- <MenuItem onClick={() => setOpen(false)}>옵션 1</MenuItem>
418
- <MenuItem onClick={() => setOpen(false)}>옵션 2</MenuItem>
419
- <MenuItem onClick={() => setOpen(false)}>옵션 3</MenuItem>
420
- </Menu>
421
- </Dropdown>
708
+ <Dropdown open={open} onOpenChange={(event, isOpen) => setOpen(isOpen)}>
709
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
710
+ 제어된 메뉴
711
+ </MenuButtonTrigger>
712
+ <Menu>
713
+ <MenuItem onClick={() => setOpen(false)}>옵션 1</MenuItem>
714
+ <MenuItem onClick={() => setOpen(false)}>옵션 2</MenuItem>
715
+ <MenuItem onClick={() => setOpen(false)}>옵션 3</MenuItem>
716
+ </Menu>
717
+ </Dropdown>
422
718
 
423
- <Button variant="soft" size="sm" onClick={() => setOpen(!open)}>
424
- {open ? '메뉴 닫기' : '메뉴 열기'}
425
- </Button>
426
- </Stack>
719
+ <Button variant="soft" size="sm" onClick={() => setOpen(!open)}>
720
+ {open ? "메뉴 닫기" : "메뉴 열기"}
721
+ </Button>
722
+ </Stack>
427
723
  ```
428
724
 
429
- ### Disabled State
725
+ ## Dropdown Disabled State
430
726
 
431
727
  Disable the entire trigger button or individual menu items.
432
728
 
433
729
  ```tsx
434
730
  <Stack direction="row" spacing={2}>
435
- <Dropdown>
436
- <MenuButton disabled>비활성 메뉴</MenuButton>
437
- <Menu>
438
- <MenuItem>접근 불가</MenuItem>
439
- </Menu>
440
- </Dropdown>
731
+ <Dropdown>
732
+ <MenuButtonTrigger disabled>비활성 메뉴</MenuButtonTrigger>
733
+ <Menu>
734
+ <MenuItem>접근 불가</MenuItem>
735
+ </Menu>
736
+ </Dropdown>
441
737
 
442
- <Dropdown>
443
- <MenuButton variant="outlined">일부 옵션 비활성</MenuButton>
444
- <Menu>
445
- <MenuItem>활성 옵션</MenuItem>
446
- <MenuItem disabled>비활성 옵션</MenuItem>
447
- <MenuItem>활성 옵션 2</MenuItem>
448
- </Menu>
449
- </Dropdown>
450
- </Stack>
738
+ <Dropdown>
739
+ <MenuButtonTrigger variant="outlined">일부 옵션 비활성</MenuButtonTrigger>
740
+ <Menu>
741
+ <MenuItem>활성 옵션</MenuItem>
742
+ <MenuItem disabled>비활성 옵션</MenuItem>
743
+ <MenuItem>활성 옵션 2</MenuItem>
744
+ </Menu>
745
+ </Dropdown>
746
+ </Stack>
451
747
  ```
452
748
 
453
- ### Long Menu
749
+ ## Dropdown Long Menu
454
750
 
455
751
  Apply `maxHeight` and `overflow: 'auto'` to Menu for scrollable lists with many items.
456
752
 
457
753
  ```tsx
458
754
  <Dropdown>
459
- <MenuButton variant="outlined" endDecorator={<ExpandMoreIcon />}>
460
- 긴 목록 메뉴
461
- </MenuButton>
462
- <Menu sx={{
463
- maxHeight: 300,
464
- overflow: 'auto'
465
- }}>
466
- {Array.from({
467
- length: 20
468
- }, (_, index) => <MenuItem key={index}>옵션 {index + 1}</MenuItem>)}
469
- </Menu>
470
- </Dropdown>
755
+ <MenuButtonTrigger variant="outlined" endDecorator={<ExpandMoreIcon />}>
756
+ 긴 목록 메뉴
757
+ </MenuButtonTrigger>
758
+ <Menu
759
+ sx={{
760
+ maxHeight: 300,
761
+ overflow: "auto"
762
+ }}
763
+ >
764
+ {Array.from(
765
+ {
766
+ length: 20
767
+ },
768
+ (_, index) => (
769
+ <MenuItem key={index}>옵션 {index + 1}</MenuItem>
770
+ )
771
+ )}
772
+ </Menu>
773
+ </Dropdown>
471
774
  ```
472
775
 
473
- ### Custom Styling
776
+ ## Dropdown Custom Styling
474
777
 
475
778
  Customize borders, shadows, border-radius, and gradients through the `sx` prop.
476
779
 
477
780
  ```tsx
478
781
  <Stack direction="row" spacing={2}>
479
- <Dropdown>
480
- <MenuButton variant="soft" color="primary" endDecorator={<ExpandMoreIcon />} sx={{
481
- borderRadius: 'xl',
482
- fontWeight: 'bold'
483
- }}>
484
- 커스텀 버튼
485
- </MenuButton>
486
- <Menu sx={{
487
- borderRadius: 'md',
488
- boxShadow: 'lg',
489
- border: '2px solid',
490
- borderColor: 'primary.200'
491
- }}>
492
- <MenuItem sx={{
493
- borderRadius: 'sm',
494
- mx: 0.5,
495
- my: 0.25
496
- }}>둥근 아이템 1</MenuItem>
497
- <MenuItem sx={{
498
- borderRadius: 'sm',
499
- mx: 0.5,
500
- my: 0.25
501
- }}>둥근 아이템 2</MenuItem>
502
- <MenuItem sx={{
503
- borderRadius: 'sm',
504
- mx: 0.5,
505
- my: 0.25
506
- }}>둥근 아이템 3</MenuItem>
507
- </Menu>
508
- </Dropdown>
782
+ <Dropdown>
783
+ <MenuButtonTrigger
784
+ variant="soft"
785
+ color="primary"
786
+ endDecorator={<ExpandMoreIcon />}
787
+ sx={{
788
+ borderRadius: "xl",
789
+ fontWeight: "bold"
790
+ }}
791
+ >
792
+ 커스텀 버튼
793
+ </MenuButtonTrigger>
794
+ <Menu
795
+ sx={{
796
+ borderRadius: "md",
797
+ boxShadow: "lg",
798
+ border: "2px solid",
799
+ borderColor: "primary.200"
800
+ }}
801
+ >
802
+ <MenuItem
803
+ sx={{
804
+ borderRadius: "sm",
805
+ mx: 0.5,
806
+ my: 0.25
807
+ }}
808
+ >
809
+ 둥근 아이템 1
810
+ </MenuItem>
811
+ <MenuItem
812
+ sx={{
813
+ borderRadius: "sm",
814
+ mx: 0.5,
815
+ my: 0.25
816
+ }}
817
+ >
818
+ 둥근 아이템 2
819
+ </MenuItem>
820
+ <MenuItem
821
+ sx={{
822
+ borderRadius: "sm",
823
+ mx: 0.5,
824
+ my: 0.25
825
+ }}
826
+ >
827
+ 둥근 아이템 3
828
+ </MenuItem>
829
+ </Menu>
830
+ </Dropdown>
509
831
 
510
- <Dropdown>
511
- <MenuButton variant="outlined" sx={{
512
- background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
513
- border: 0,
514
- borderRadius: 3,
515
- color: 'white',
516
- '&:hover': {
517
- background: 'linear-gradient(45deg, #FE6B8B 60%, #FF8E53 100%)'
518
- }
519
- }}>
520
- 그라데이션 버튼
521
- </MenuButton>
522
- <Menu>
523
- <MenuItem>스타일링된 메뉴</MenuItem>
524
- <MenuItem>아이템 2</MenuItem>
525
- </Menu>
526
- </Dropdown>
527
- </Stack>
832
+ <Dropdown>
833
+ <MenuButtonTrigger
834
+ variant="outlined"
835
+ sx={{
836
+ background: "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)",
837
+ border: 0,
838
+ borderRadius: 3,
839
+ color: "white",
840
+ "&:hover": {
841
+ background: "linear-gradient(45deg, #FE6B8B 60%, #FF8E53 100%)"
842
+ }
843
+ }}
844
+ >
845
+ 그라데이션 버튼
846
+ </MenuButtonTrigger>
847
+ <Menu>
848
+ <MenuItem>스타일링된 메뉴</MenuItem>
849
+ <MenuItem>아이템 2</MenuItem>
850
+ </Menu>
851
+ </Dropdown>
852
+ </Stack>
528
853
  ```
529
854
 
530
- ## Common Use Cases
531
-
532
- ### Navigation Menu
855
+ ## Navigation Menu
533
856
 
534
857
  ```tsx
535
858
  <Dropdown>
536
- <MenuButton variant="plain">Menu</MenuButton>
859
+ <MenuButtonTrigger variant="plain">Menu</MenuButtonTrigger>
537
860
  <Menu>
538
- <MenuItem component="a" href="/home">Home</MenuItem>
539
- <MenuItem component="a" href="/about">About</MenuItem>
540
- <MenuItem component="a" href="/products">Products</MenuItem>
861
+ <MenuItem component="a" href="/home">
862
+ Home
863
+ </MenuItem>
864
+ <MenuItem component="a" href="/about">
865
+ About
866
+ </MenuItem>
867
+ <MenuItem component="a" href="/products">
868
+ Products
869
+ </MenuItem>
541
870
  <ListDivider />
542
- <MenuItem component="a" href="/contact">Contact</MenuItem>
871
+ <MenuItem component="a" href="/contact">
872
+ Contact
873
+ </MenuItem>
543
874
  </Menu>
544
875
  </Dropdown>
545
876
  ```
546
877
 
547
- ### Filter Selector
878
+ ## Filter Selector
548
879
 
549
880
  ```tsx
550
881
  const [filter, setFilter] = useState('All');
551
882
 
552
883
  <Dropdown>
553
- <MenuButton
554
- variant="outlined"
555
- startDecorator={<FilterListIcon />}
556
- endDecorator={<ExpandMoreIcon />}
557
- >
884
+ <MenuButtonTrigger variant="outlined" startDecorator={<FilterListIcon />} endDecorator={<ExpandMoreIcon />}>
558
885
  Filter: {filter}
559
- </MenuButton>
886
+ </MenuButtonTrigger>
560
887
  <Menu>
561
888
  <MenuItem onClick={() => setFilter('All')}>All</MenuItem>
562
889
  <MenuItem onClick={() => setFilter('Active')}>Active</MenuItem>
563
890
  <MenuItem onClick={() => setFilter('Inactive')}>Inactive</MenuItem>
564
891
  </Menu>
565
- </Dropdown>
892
+ </Dropdown>;
566
893
  ```
567
894
 
568
- ### Controlled State with External Toggle
895
+ ## Controlled State with External Toggle
569
896
 
570
897
  ```tsx
571
898
  const [open, setOpen] = useState(false);
572
899
 
573
900
  <Dropdown open={open} onOpenChange={(event, isOpen) => setOpen(isOpen)}>
574
- <MenuButton>Controlled Menu</MenuButton>
901
+ <MenuButtonTrigger>Controlled Menu</MenuButtonTrigger>
575
902
  <Menu>
576
903
  <MenuItem onClick={() => setOpen(false)}>Option A</MenuItem>
577
904
  <MenuItem onClick={() => setOpen(false)}>Option B</MenuItem>
@@ -587,26 +914,30 @@ const [open, setOpen] = useState(false);
587
914
 
588
915
  ### Key Props
589
916
 
590
- | Prop | Type | Default | Description |
591
- | -------------- | -------------------------------- | ------- | ------------------------------------ |
592
- | `children` | `ReactNode` | - | Dropdown content (MenuButton + Menu) |
593
- | `open` | `boolean` | - | Controlled open state |
594
- | `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled) |
595
- | `onOpenChange` | `(event, open: boolean) => void` | - | Callback when open state changes |
917
+ | Prop | Type | Default | Description |
918
+ | -------------- | -------------------------------- | ------- | ------------------------------------------- |
919
+ | `children` | `ReactNode` | - | Dropdown content (MenuButtonTrigger + Menu) |
920
+ | `open` | `boolean` | - | Controlled open state |
921
+ | `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled) |
922
+ | `onOpenChange` | `(event, open: boolean) => void` | - | Callback when open state changes |
596
923
 
597
- > **Note**: Dropdown accepts all Joy UI Dropdown props. It is a state management wrapper — style props go on Menu and MenuButton.
924
+ > **Note**: Dropdown accepts all Joy UI Dropdown props. It is a state management wrapper — style props go on Menu and `MenuButtonTrigger`.
598
925
 
599
926
  ## Best Practices
600
927
 
601
928
  - **Use the right abstraction level.** If you only need a simple list of text items, prefer `MenuButton` or `IconMenuButton` standalone components. Use `Dropdown` + `Menu` when you need dividers, custom headers, or mixed content inside the menu.
602
929
 
603
930
  ```tsx
604
- {/* ✅ Good: Simple list -- use MenuButton directly */}
605
- <MenuButton buttonText="Actions" items={[{ text: 'Edit' }, { text: 'Delete' }]} />
606
-
607
- {/* ✅ Good: Complex content -- use Dropdown composition */}
931
+ {
932
+ /* Good: Simple list -- use MenuButton directly */
933
+ }
934
+ <MenuButton buttonText="Actions" items={[{ text: 'Edit' }, { text: 'Delete' }]} />;
935
+
936
+ {
937
+ /* ✅ Good: Complex content -- use Dropdown composition */
938
+ }
608
939
  <Dropdown>
609
- <MenuButton>User</MenuButton>
940
+ <MenuButtonTrigger>User</MenuButtonTrigger>
610
941
  <Menu>
611
942
  <Box sx={{ px: 2, py: 1 }}>
612
943
  <Typography level="title-sm">User Name</Typography>
@@ -615,29 +946,35 @@ const [open, setOpen] = useState(false);
615
946
  <MenuItem>Settings</MenuItem>
616
947
  <MenuItem>Logout</MenuItem>
617
948
  </Menu>
618
- </Dropdown>
949
+ </Dropdown>;
619
950
  ```
620
951
 
621
952
  - **Group related items with ListDivider.** Visually separate logical groups of menu items so users can scan options quickly.
622
953
 
623
954
  ```tsx
624
- {/* ✅ Good: Grouped by function */}
955
+ {
956
+ /* ✅ Good: Grouped by function */
957
+ }
625
958
  <Menu>
626
959
  <MenuItem>Edit</MenuItem>
627
960
  <MenuItem>Duplicate</MenuItem>
628
961
  <ListDivider />
629
962
  <MenuItem color="danger">Delete</MenuItem>
630
- </Menu>
963
+ </Menu>;
631
964
  ```
632
965
 
633
966
  - **Indicate dropdown affordance.** Use `endDecorator={<ExpandMoreIcon />}` on the trigger button so users understand it opens a menu.
634
967
 
635
968
  ```tsx
636
- {/* ✅ Good: Clear dropdown indicator */}
637
- <MenuButton endDecorator={<ExpandMoreIcon />}>Options</MenuButton>
638
-
639
- {/* ❌ Avoid: No visual hint that a menu will appear */}
640
- <MenuButton>Options</MenuButton>
969
+ {
970
+ /* Good: Clear dropdown indicator */
971
+ }
972
+ <MenuButtonTrigger endDecorator={<ExpandMoreIcon />}>Options</MenuButtonTrigger>;
973
+
974
+ {
975
+ /* ❌ Avoid: No visual hint that a menu will appear */
976
+ }
977
+ <MenuButtonTrigger>Options</MenuButtonTrigger>;
641
978
  ```
642
979
 
643
980
  - **Keep menu item count reasonable.** Aim for 3-7 items. For longer lists, apply `maxHeight` with scrolling or consider a different UI pattern like a dialog or searchable list.