@ceed/ads 1.23.3 → 1.23.5
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/dist/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +310 -61
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
- package/dist/components/inputs/FormControl.md +361 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +150 -333
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Menu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Menu and MenuItem are the content layer of a dropdown menu system. Menu renders the floating panel that contains a list of selectable items, while MenuItem represents each individual option. Together with Dropdown (the state container) and a trigger component (MenuButton or IconMenuButton), they form a complete menu composition.
|
|
4
4
|
|
|
5
|
-
Menu
|
|
5
|
+
Menu is built on Joy UI's Menu component and supports sizes, placements, custom styling, and rich content inside menu items such as icons, links, and nested layouts.
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
<Box sx={{
|
|
@@ -26,27 +26,42 @@ Menu와 MenuItem 컴포넌트는 드롭다운 메뉴의 콘텐츠를 구성하
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
|
-
import { Dropdown,
|
|
29
|
+
import { Dropdown, Menu, MenuItem } from '@ceed/ads';
|
|
30
|
+
import { MenuButton } from '@ceed/ads';
|
|
30
31
|
|
|
31
32
|
function MyComponent() {
|
|
32
33
|
return (
|
|
33
34
|
<Dropdown>
|
|
34
|
-
<MenuButton
|
|
35
|
+
<MenuButton>Open Menu</MenuButton>
|
|
35
36
|
<Menu>
|
|
36
|
-
<MenuItem
|
|
37
|
-
<MenuItem
|
|
38
|
-
<MenuItem
|
|
37
|
+
<MenuItem>Option 1</MenuItem>
|
|
38
|
+
<MenuItem>Option 2</MenuItem>
|
|
39
|
+
<MenuItem>Option 3</MenuItem>
|
|
39
40
|
</Menu>
|
|
40
41
|
</Dropdown>
|
|
41
42
|
);
|
|
42
43
|
}
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
##
|
|
46
|
+
## Composition Pattern
|
|
47
|
+
|
|
48
|
+
Menu is always used inside a Dropdown together with a trigger element. The full composition looks like this:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Dropdown -- state container (open/close)
|
|
52
|
+
MenuButton -- trigger element (or IconMenuButton)
|
|
53
|
+
Menu -- floating panel
|
|
54
|
+
MenuItem -- individual option
|
|
55
|
+
ListDivider -- visual separator between groups
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See the [Dropdown](?path=/docs/components-navigation-dropdown--docs) documentation for guidance on when to use the composition pattern versus the standalone MenuButton/IconMenuButton components.
|
|
59
|
+
|
|
60
|
+
## Features
|
|
46
61
|
|
|
47
62
|
### Basic Menu
|
|
48
63
|
|
|
49
|
-
|
|
64
|
+
A standard menu with simple text items, optionally separated by dividers.
|
|
50
65
|
|
|
51
66
|
```tsx
|
|
52
67
|
<Stack direction="row" spacing={3}>
|
|
@@ -77,7 +92,7 @@ function MyComponent() {
|
|
|
77
92
|
|
|
78
93
|
### Menu Sizes
|
|
79
94
|
|
|
80
|
-
|
|
95
|
+
Menu supports `sm`, `md`, and `lg` sizes that affect the padding and font size of all items.
|
|
81
96
|
|
|
82
97
|
```tsx
|
|
83
98
|
<Stack direction="row" spacing={3}>
|
|
@@ -118,7 +133,7 @@ function MyComponent() {
|
|
|
118
133
|
|
|
119
134
|
### MenuItem States
|
|
120
135
|
|
|
121
|
-
|
|
136
|
+
MenuItems can be `selected`, `disabled`, or colored with semantic colors like `primary`, `danger`, `success`, and `warning`.
|
|
122
137
|
|
|
123
138
|
```tsx
|
|
124
139
|
<Dropdown>
|
|
@@ -137,7 +152,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
137
152
|
|
|
138
153
|
### MenuItem with Icons
|
|
139
154
|
|
|
140
|
-
|
|
155
|
+
Add icons before item text to improve scannability. Use consistent icon sizing with `sx={{ mr: 1.5, fontSize: 'lg' }}`.
|
|
141
156
|
|
|
142
157
|
```tsx
|
|
143
158
|
<Dropdown>
|
|
@@ -180,7 +195,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
180
195
|
|
|
181
196
|
### Actions Menu
|
|
182
197
|
|
|
183
|
-
|
|
198
|
+
Build action menus for CRUD operations. Use `color="danger"` to highlight destructive actions.
|
|
184
199
|
|
|
185
200
|
```tsx
|
|
186
201
|
<Dropdown>
|
|
@@ -223,7 +238,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
223
238
|
|
|
224
239
|
### Clickable Menu Items
|
|
225
240
|
|
|
226
|
-
|
|
241
|
+
Attach `onClick` handlers to menu items to respond to user selections.
|
|
227
242
|
|
|
228
243
|
```tsx
|
|
229
244
|
<Stack spacing={2}>
|
|
@@ -242,7 +257,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
242
257
|
|
|
243
258
|
### Menu as Links
|
|
244
259
|
|
|
245
|
-
|
|
260
|
+
Use the `component="a"` prop on MenuItem to render items as anchor links for navigation.
|
|
246
261
|
|
|
247
262
|
```tsx
|
|
248
263
|
<Dropdown>
|
|
@@ -266,7 +281,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
266
281
|
|
|
267
282
|
### Profile Menu
|
|
268
283
|
|
|
269
|
-
|
|
284
|
+
Combine custom Box content (user info header) with standard MenuItem entries for rich profile menus.
|
|
270
285
|
|
|
271
286
|
```tsx
|
|
272
287
|
<Dropdown>
|
|
@@ -336,7 +351,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
336
351
|
|
|
337
352
|
### Menu Placements
|
|
338
353
|
|
|
339
|
-
|
|
354
|
+
Control the position of the menu panel relative to the trigger using the `placement` prop. Supported values include `top-start`, `top`, `top-end`, `bottom-start`, `bottom`, and `bottom-end`.
|
|
340
355
|
|
|
341
356
|
```tsx
|
|
342
357
|
<Stack spacing={4} alignItems="center" sx={{
|
|
@@ -405,7 +420,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
405
420
|
|
|
406
421
|
### Long Scrollable Menu
|
|
407
422
|
|
|
408
|
-
|
|
423
|
+
For menus with many items, apply `maxHeight` and `overflow: 'auto'` through the `sx` prop.
|
|
409
424
|
|
|
410
425
|
```tsx
|
|
411
426
|
<Dropdown>
|
|
@@ -423,7 +438,7 @@ MenuItem의 다양한 상태를 표현할 수 있습니다.
|
|
|
423
438
|
|
|
424
439
|
### Custom Styled Menu
|
|
425
440
|
|
|
426
|
-
|
|
441
|
+
Customize the menu panel appearance with border-radius, shadows, borders, and custom item spacing.
|
|
427
442
|
|
|
428
443
|
```tsx
|
|
429
444
|
<Stack direction="row" spacing={3}>
|
|
@@ -487,7 +502,7 @@ CSS 스타일을 이용해 메뉴를 커스터마이징할 수 있습니다.
|
|
|
487
502
|
|
|
488
503
|
### Menu in Card
|
|
489
504
|
|
|
490
|
-
|
|
505
|
+
Embed a Dropdown menu inside cards or other container components for contextual actions.
|
|
491
506
|
|
|
492
507
|
```tsx
|
|
493
508
|
<Sheet variant="outlined" sx={{
|
|
@@ -547,7 +562,7 @@ CSS 스타일을 이용해 메뉴를 커스터마이징할 수 있습니다.
|
|
|
547
562
|
|
|
548
563
|
### Nested Content
|
|
549
564
|
|
|
550
|
-
|
|
565
|
+
Menu can contain non-MenuItem elements like Box, Typography, and progress indicators alongside standard items.
|
|
551
566
|
|
|
552
567
|
```tsx
|
|
553
568
|
<Dropdown>
|
|
@@ -631,93 +646,42 @@ CSS 스타일을 이용해 메뉴를 커스터마이징할 수 있습니다.
|
|
|
631
646
|
</Dropdown>
|
|
632
647
|
```
|
|
633
648
|
|
|
634
|
-
## Component Structure
|
|
635
|
-
|
|
636
|
-
Menu는 다음과 같은 구조로 구성됩니다:
|
|
637
|
-
|
|
638
|
-
```tsx
|
|
639
|
-
<Dropdown>
|
|
640
|
-
<MenuButton>트리거</MenuButton>
|
|
641
|
-
<Menu>
|
|
642
|
-
{/* 기본 메뉴 아이템 */}
|
|
643
|
-
<MenuItem>기본 아이템</MenuItem>
|
|
644
|
-
|
|
645
|
-
{/* 아이콘이 있는 메뉴 아이템 */}
|
|
646
|
-
<MenuItem>
|
|
647
|
-
<Icon sx={{ mr: 1 }} />
|
|
648
|
-
아이콘 아이템
|
|
649
|
-
</MenuItem>
|
|
650
|
-
|
|
651
|
-
{/* 구분선 */}
|
|
652
|
-
<ListDivider />
|
|
653
|
-
|
|
654
|
-
{/* 색상이 있는 메뉴 아이템 */}
|
|
655
|
-
<MenuItem color="danger">위험한 작업</MenuItem>
|
|
656
|
-
|
|
657
|
-
{/* 비활성 메뉴 아이템 */}
|
|
658
|
-
<MenuItem disabled>비활성 아이템</MenuItem>
|
|
659
|
-
|
|
660
|
-
{/* 링크 메뉴 아이템 */}
|
|
661
|
-
<MenuItem component="a" href="/link">
|
|
662
|
-
링크 아이템
|
|
663
|
-
</MenuItem>
|
|
664
|
-
|
|
665
|
-
{/* 클릭 핸들러가 있는 메뉴 아이템 */}
|
|
666
|
-
<MenuItem onClick={handleClick}>클릭 가능한 아이템</MenuItem>
|
|
667
|
-
</Menu>
|
|
668
|
-
</Dropdown>
|
|
669
|
-
```
|
|
670
|
-
|
|
671
649
|
## Common Use Cases
|
|
672
650
|
|
|
673
|
-
###
|
|
651
|
+
### Action Menu with Icons
|
|
674
652
|
|
|
675
653
|
```tsx
|
|
676
654
|
<Dropdown>
|
|
677
|
-
<MenuButton
|
|
655
|
+
<MenuButton variant="plain" size="sm">Actions</MenuButton>
|
|
678
656
|
<Menu>
|
|
679
|
-
<MenuItem
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
<MenuItem component="a" href="/products">
|
|
683
|
-
제품
|
|
657
|
+
<MenuItem onClick={handleEdit}>
|
|
658
|
+
<EditIcon sx={{ mr: 1 }} />
|
|
659
|
+
Edit
|
|
684
660
|
</MenuItem>
|
|
685
|
-
<MenuItem
|
|
686
|
-
|
|
661
|
+
<MenuItem onClick={handleCopy}>
|
|
662
|
+
<CopyIcon sx={{ mr: 1 }} />
|
|
663
|
+
Copy
|
|
687
664
|
</MenuItem>
|
|
688
665
|
<ListDivider />
|
|
689
|
-
<MenuItem
|
|
690
|
-
|
|
666
|
+
<MenuItem color="danger" onClick={handleDelete}>
|
|
667
|
+
<DeleteIcon sx={{ mr: 1 }} />
|
|
668
|
+
Delete
|
|
691
669
|
</MenuItem>
|
|
692
670
|
</Menu>
|
|
693
671
|
</Dropdown>
|
|
694
672
|
```
|
|
695
673
|
|
|
696
|
-
###
|
|
674
|
+
### Navigation Menu
|
|
697
675
|
|
|
698
676
|
```tsx
|
|
699
677
|
<Dropdown>
|
|
700
|
-
<MenuButton
|
|
701
|
-
작업
|
|
702
|
-
</MenuButton>
|
|
678
|
+
<MenuButton>Navigate</MenuButton>
|
|
703
679
|
<Menu>
|
|
704
|
-
<MenuItem
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
</MenuItem>
|
|
708
|
-
<MenuItem onClick={handleCopy}>
|
|
709
|
-
<CopyIcon sx={{ mr: 1 }} />
|
|
710
|
-
복사
|
|
711
|
-
</MenuItem>
|
|
712
|
-
<MenuItem onClick={handleShare}>
|
|
713
|
-
<ShareIcon sx={{ mr: 1 }} />
|
|
714
|
-
공유
|
|
715
|
-
</MenuItem>
|
|
680
|
+
<MenuItem component="a" href="/home">Home</MenuItem>
|
|
681
|
+
<MenuItem component="a" href="/products">Products</MenuItem>
|
|
682
|
+
<MenuItem component="a" href="/services">Services</MenuItem>
|
|
716
683
|
<ListDivider />
|
|
717
|
-
<MenuItem
|
|
718
|
-
<DeleteIcon sx={{ mr: 1 }} />
|
|
719
|
-
삭제
|
|
720
|
-
</MenuItem>
|
|
684
|
+
<MenuItem component="a" href="/contact">Contact</MenuItem>
|
|
721
685
|
</Menu>
|
|
722
686
|
</Dropdown>
|
|
723
687
|
```
|
|
@@ -726,232 +690,75 @@ Menu는 다음과 같은 구조로 구성됩니다:
|
|
|
726
690
|
|
|
727
691
|
```tsx
|
|
728
692
|
<Dropdown>
|
|
729
|
-
<MenuButton
|
|
730
|
-
|
|
693
|
+
<MenuButton
|
|
694
|
+
startDecorator={<Avatar size="sm" src="/avatar.jpg" />}
|
|
695
|
+
endDecorator={<ExpandMoreIcon />}
|
|
696
|
+
>
|
|
697
|
+
Username
|
|
731
698
|
</MenuButton>
|
|
732
699
|
<Menu>
|
|
733
|
-
{/* 프로필 헤더 */}
|
|
734
700
|
<Box sx={{ px: 2, py: 1.5 }}>
|
|
735
|
-
<Typography level="title-sm"
|
|
736
|
-
<Typography level="body-xs" color="neutral">
|
|
737
|
-
user@example.com
|
|
738
|
-
</Typography>
|
|
701
|
+
<Typography level="title-sm">Username</Typography>
|
|
702
|
+
<Typography level="body-xs" color="neutral">user@example.com</Typography>
|
|
739
703
|
</Box>
|
|
740
|
-
|
|
741
704
|
<ListDivider />
|
|
742
|
-
|
|
743
705
|
<MenuItem>
|
|
744
706
|
<PersonIcon sx={{ mr: 1 }} />
|
|
745
|
-
|
|
707
|
+
Profile
|
|
746
708
|
</MenuItem>
|
|
747
709
|
<MenuItem>
|
|
748
710
|
<SettingsIcon sx={{ mr: 1 }} />
|
|
749
|
-
|
|
711
|
+
Settings
|
|
750
712
|
</MenuItem>
|
|
751
|
-
|
|
752
713
|
<ListDivider />
|
|
753
|
-
|
|
754
714
|
<MenuItem>
|
|
755
715
|
<LogoutIcon sx={{ mr: 1 }} />
|
|
756
|
-
|
|
716
|
+
Logout
|
|
757
717
|
</MenuItem>
|
|
758
718
|
</Menu>
|
|
759
719
|
</Dropdown>
|
|
760
720
|
```
|
|
761
721
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
```tsx
|
|
765
|
-
const [filter, setFilter] = useState('전체');
|
|
766
|
-
|
|
767
|
-
<Dropdown>
|
|
768
|
-
<MenuButton startDecorator={<FilterListIcon />} endDecorator={<ExpandMoreIcon />}>
|
|
769
|
-
필터: {filter}
|
|
770
|
-
</MenuButton>
|
|
771
|
-
<Menu>
|
|
772
|
-
<MenuItem selected={filter === '전체'} onClick={() => setFilter('전체')}>
|
|
773
|
-
전체
|
|
774
|
-
</MenuItem>
|
|
775
|
-
<MenuItem selected={filter === '활성'} onClick={() => setFilter('활성')}>
|
|
776
|
-
활성
|
|
777
|
-
</MenuItem>
|
|
778
|
-
<MenuItem selected={filter === '비활성'} onClick={() => setFilter('비활성')}>
|
|
779
|
-
비활성
|
|
780
|
-
</MenuItem>
|
|
781
|
-
</Menu>
|
|
782
|
-
</Dropdown>;
|
|
783
|
-
```
|
|
784
|
-
|
|
785
|
-
### Context Menu (Right-click Menu)
|
|
786
|
-
|
|
787
|
-
```tsx
|
|
788
|
-
const [contextMenu, setContextMenu] = useState(null);
|
|
789
|
-
|
|
790
|
-
const handleContextMenu = (event) => {
|
|
791
|
-
event.preventDefault();
|
|
792
|
-
setContextMenu({
|
|
793
|
-
mouseX: event.clientX - 2,
|
|
794
|
-
mouseY: event.clientY - 4,
|
|
795
|
-
});
|
|
796
|
-
};
|
|
797
|
-
|
|
798
|
-
<div onContextMenu={handleContextMenu}>
|
|
799
|
-
우클릭 가능한 영역
|
|
800
|
-
{contextMenu && (
|
|
801
|
-
<Menu
|
|
802
|
-
open
|
|
803
|
-
anchorReference="anchorPosition"
|
|
804
|
-
anchorPosition={{
|
|
805
|
-
top: contextMenu.mouseY,
|
|
806
|
-
left: contextMenu.mouseX,
|
|
807
|
-
}}
|
|
808
|
-
onClose={() => setContextMenu(null)}
|
|
809
|
-
>
|
|
810
|
-
<MenuItem>복사</MenuItem>
|
|
811
|
-
<MenuItem>붙여넣기</MenuItem>
|
|
812
|
-
<MenuItem>삭제</MenuItem>
|
|
813
|
-
</Menu>
|
|
814
|
-
)}
|
|
815
|
-
</div>;
|
|
816
|
-
```
|
|
722
|
+
## Best Practices
|
|
817
723
|
|
|
818
|
-
|
|
724
|
+
- **Use ListDivider to group related items.** Logical grouping helps users scan the menu faster. Place destructive actions (like Delete) in their own group at the bottom.
|
|
819
725
|
|
|
820
|
-
```tsx
|
|
821
|
-
|
|
822
|
-
<MenuButton>파일</MenuButton>
|
|
726
|
+
```tsx
|
|
727
|
+
{/* ✅ Good: Grouped actions with divider */}
|
|
823
728
|
<Menu>
|
|
824
|
-
<MenuItem
|
|
825
|
-
<MenuItem
|
|
826
|
-
|
|
827
|
-
{/* 서브메뉴는 별도 Dropdown으로 구현 */}
|
|
828
|
-
<MenuItem>
|
|
829
|
-
최근 파일
|
|
830
|
-
<Dropdown>
|
|
831
|
-
<MenuButton variant="plain" size="sm" sx={{ ml: 'auto' }}>
|
|
832
|
-
▶
|
|
833
|
-
</MenuButton>
|
|
834
|
-
<Menu placement="right-start">
|
|
835
|
-
<MenuItem>파일1.txt</MenuItem>
|
|
836
|
-
<MenuItem>파일2.txt</MenuItem>
|
|
837
|
-
<MenuItem>파일3.txt</MenuItem>
|
|
838
|
-
</Menu>
|
|
839
|
-
</Dropdown>
|
|
840
|
-
</MenuItem>
|
|
841
|
-
|
|
729
|
+
<MenuItem>Edit</MenuItem>
|
|
730
|
+
<MenuItem>Duplicate</MenuItem>
|
|
842
731
|
<ListDivider />
|
|
843
|
-
<MenuItem
|
|
844
|
-
<MenuItem>종료</MenuItem>
|
|
732
|
+
<MenuItem color="danger">Delete</MenuItem>
|
|
845
733
|
</Menu>
|
|
846
|
-
|
|
847
|
-
```
|
|
734
|
+
```
|
|
848
735
|
|
|
849
|
-
|
|
736
|
+
- **Keep icon sizing consistent.** When using icons in menu items, apply the same `fontSize` and `mr` (margin-right) to all icons so items align cleanly.
|
|
850
737
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
<
|
|
855
|
-
<Menu placement="top"> {/* 위쪽 중앙 정렬 */}
|
|
856
|
-
<Menu placement="top-end"> {/* 위쪽 오른쪽 정렬 */}
|
|
857
|
-
<Menu placement="bottom-start"> {/* 아래쪽 왼쪽 정렬 (기본값) */}
|
|
858
|
-
<Menu placement="bottom"> {/* 아래쪽 중앙 정렬 */}
|
|
859
|
-
<Menu placement="bottom-end"> {/* 아래쪽 오른쪽 정렬 */}
|
|
860
|
-
```
|
|
861
|
-
|
|
862
|
-
## Accessibility
|
|
738
|
+
```tsx
|
|
739
|
+
{/* ✅ Good: Consistent icon styling */}
|
|
740
|
+
<MenuItem><EditIcon sx={{ mr: 1.5, fontSize: 'lg' }} />Edit</MenuItem>
|
|
741
|
+
<MenuItem><DeleteIcon sx={{ mr: 1.5, fontSize: 'lg' }} />Delete</MenuItem>
|
|
863
742
|
|
|
864
|
-
|
|
743
|
+
{/* ❌ Avoid: Inconsistent icon sizes */}
|
|
744
|
+
<MenuItem><EditIcon sx={{ mr: 1 }} />Edit</MenuItem>
|
|
745
|
+
<MenuItem><DeleteIcon sx={{ mr: 2, fontSize: 'xl' }} />Delete</MenuItem>
|
|
746
|
+
```
|
|
865
747
|
|
|
866
|
-
|
|
748
|
+
- **Limit menu length.** Aim for 3-7 visible items. For longer lists, set `maxHeight` on Menu to enable scrolling, or reconsider the UI pattern.
|
|
867
749
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
- **Enter**: 메뉴 아이템 선택
|
|
750
|
+
```tsx
|
|
751
|
+
{/* ✅ Good: Scrollable long menu */}
|
|
752
|
+
<Menu sx={{ maxHeight: 300, overflow: 'auto' }}>
|
|
753
|
+
```
|
|
873
754
|
|
|
874
|
-
|
|
755
|
+
- **Use semantic colors intentionally.** Reserve `color="danger"` for destructive actions. Do not use multiple colors in the same menu unless each color carries a distinct meaning.
|
|
875
756
|
|
|
876
|
-
-
|
|
877
|
-
- `role="menuitem"`: 메뉴 아이템 역할 정의
|
|
878
|
-
- `aria-expanded`: 메뉴 열림/닫힘 상태
|
|
879
|
-
- `aria-haspopup`: 팝업 메뉴 존재 표시
|
|
757
|
+
- **Provide clear labels.** Every MenuItem should have a concise, action-oriented label that makes its purpose obvious without needing additional context.
|
|
880
758
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
```tsx
|
|
884
|
-
<MenuItem aria-label="사용자 프로필 편집">
|
|
885
|
-
<EditIcon />
|
|
886
|
-
편집
|
|
887
|
-
</MenuItem>
|
|
888
|
-
|
|
889
|
-
<MenuItem disabled aria-label="현재 사용할 수 없는 기능">
|
|
890
|
-
사용 불가
|
|
891
|
-
</MenuItem>
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
## Best Practices
|
|
895
|
-
|
|
896
|
-
1. **명확한 라벨링**: 메뉴 아이템은 명확하고 이해하기 쉬운 텍스트를 사용하세요.
|
|
897
|
-
|
|
898
|
-
2. **아이콘 일관성**: 아이콘을 사용할 때는 일관된 크기와 스타일을 유지하세요.
|
|
899
|
-
|
|
900
|
-
```tsx
|
|
901
|
-
<MenuItem>
|
|
902
|
-
<EditIcon sx={{ mr: 1.5, fontSize: 'lg' }} />
|
|
903
|
-
편집
|
|
904
|
-
</MenuItem>
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
3. **논리적 그룹화**: 관련된 메뉴 아이템들은 ListDivider로 구분하세요.
|
|
908
|
-
|
|
909
|
-
4. **위험한 작업 구분**: 삭제와 같은 위험한 작업은 색상으로 구분하세요.
|
|
910
|
-
|
|
911
|
-
```tsx
|
|
912
|
-
<MenuItem color="danger">
|
|
913
|
-
<DeleteIcon sx={{ mr: 1 }} />
|
|
914
|
-
삭제
|
|
915
|
-
</MenuItem>
|
|
916
|
-
```
|
|
917
|
-
|
|
918
|
-
5. **적절한 메뉴 크기**: 너무 긴 메뉴는 스크롤을 적용하거나 서브메뉴로 나누세요.
|
|
919
|
-
|
|
920
|
-
```tsx
|
|
921
|
-
<Menu sx={{ maxHeight: 300, overflow: 'auto' }}>
|
|
922
|
-
```
|
|
923
|
-
|
|
924
|
-
6. **상태 피드백**: 선택된 아이템이나 현재 상태를 명확히 표시하세요.
|
|
925
|
-
|
|
926
|
-
7. **모바일 고려**: 터치 기기에서 사용하기 쉽도록 충분한 터치 영역을 제공하세요.
|
|
927
|
-
|
|
928
|
-
## Performance Considerations
|
|
929
|
-
|
|
930
|
-
1. **메뉴 지연 로딩**: 복잡한 메뉴는 필요할 때만 렌더링하세요.
|
|
931
|
-
|
|
932
|
-
2. **가상화**: 매우 긴 메뉴 목록에는 가상화를 고려하세요.
|
|
933
|
-
|
|
934
|
-
3. **메모이제이션**: 정적인 메뉴 아이템들은 메모이제이션을 활용하세요.
|
|
935
|
-
|
|
936
|
-
## Troubleshooting
|
|
937
|
-
|
|
938
|
-
### 흔한 문제들
|
|
939
|
-
|
|
940
|
-
1. **메뉴가 화면을 벗어남**
|
|
941
|
-
|
|
942
|
-
```tsx
|
|
943
|
-
<Menu placement="top-end"> {/* placement 조정 */}
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
2. **긴 메뉴 스크롤 문제**
|
|
947
|
-
|
|
948
|
-
```tsx
|
|
949
|
-
<Menu sx={{ maxHeight: 400, overflow: 'auto' }}>
|
|
950
|
-
```
|
|
951
|
-
|
|
952
|
-
3. **모바일에서 터치 영역 부족**
|
|
953
|
-
```tsx
|
|
954
|
-
<MenuItem sx={{ minHeight: 44 }}> {/* 최소 터치 영역 보장 */}
|
|
955
|
-
```
|
|
759
|
+
## Accessibility
|
|
956
760
|
|
|
957
|
-
Menu
|
|
761
|
+
- **ARIA roles**: Menu automatically receives `role="menu"` and each MenuItem receives `role="menuitem"`. These roles help assistive technologies announce the menu structure correctly.
|
|
762
|
+
- **Keyboard navigation**: When the menu is open, Arrow Up/Down moves between items, Enter selects the focused item, and Escape closes the menu and returns focus to the trigger.
|
|
763
|
+
- **Disabled items**: Use the `disabled` prop on MenuItem to prevent interaction. Disabled items remain in the tab order but cannot be activated, allowing screen readers to announce them as unavailable.
|
|
764
|
+
- **Icon-only items**: If a MenuItem contains only an icon without visible text, add an `aria-label` to provide an accessible name.
|