6.5 KiB
Context Menu
현재 HM 구현의 기준 문서. 공통 원칙은 docs/guidelines/프로젝트-지침.md(§6, §7), Token 구조 원칙은 테마-토큰-가이드.md를 따른다. 이 문서는 Context Menu 고유의 API·Token·상태·결정만 기록한다. Dropdown Menu와 같은 메뉴 계열이므로 상세 값은 dropdown-menu.md와 대응한다.
1. 상태
| 항목 | 내용 |
|---|---|
| 구현 상태 | 구현 완료 (현재 HM 규격) |
| 기준일 | 2026-10-03 |
| 기반 | shadcn/ui Context Menu (Radix ContextMenu, radix-ui 1.6.7 / @radix-ui/react-context-menu 2.3.7) |
| 대표 Story | Components/Overlay/Context Menu → All (이름 "Context Menu") |
| 보류 | 아래 §11 |
2. 적용 근거
- 참고 근거: ui-viz에는 Context Menu 규격이 없다. 최종 기준은 현재
@hm-design/ui구현과 Dropdown Menu 문서다. - 현재 HM 규격: 면·윤곽·radius·그림자는 Floating Surface Semantic(
--hm-sem-floating-*)을 source로 쓰고, item·text 값은 Dropdown Menu와 같은 현재 HM 값이다. - 구현 방식: shadcn / Radix Context Menu 구조 유지. 상태는 Radix 속성(
data-highlighted,data-disabled,data-state)만 쓴다.
3. Dropdown Menu와 역할 차이
| Dropdown Menu | Context Menu | |
|---|---|---|
| 여는 방법 | 명시적 버튼(Trigger) 클릭 / Enter / Space / 방향키 | 대상 영역 우클릭, context-menu key / Shift+F10, 터치 long press |
| 위치 | Trigger 기준 anchored (sideOffset 4) |
포인터 위치 기준 (sideOffset 없음) |
| 용도 | 버튼이 가진 Action 목록 | 대상 객체에 대한 보조 Action |
| 닫힐 때 focus | Trigger로 복귀 | Trigger가 포커스 가능한 요소일 때만 복귀(§7) |
브라우저 native context menu 대신 HM Context Menu가 열린다(Radix가 contextmenu 이벤트를 막음).
4. 기본 규격 / 상태 규격
dropdown-menu.md §3·§4와 값이 같다(Surface White, 1px pale border, radius 4px, 12px glow 그림자, item 36px·14/20, Highlighted Accent 45%(--hm-sem-surface-highlight), Disabled opacity 50, Destructive --hm-sem-action-destructive, Check 16px / Radio 8px). 값이 같아도 --hm-dropdown-menu-*는 참조하지 않는다.
5. API
Radix Context Menu props 그대로. export: ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuGroup, ContextMenuLabel, ContextMenuItem, ContextMenuCheckboxItem, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent, ContextMenuPortal.
ContextMenuItem: shadcnvariant="default" | "destructive",inset. Label·CheckboxItem·RadioItem·SubTrigger:inset.- 출력 속성:
data-slot="context-menu-*", Radixdata-state·data-highlighted·data-disabled·data-side. - HM 전용 prop 없음.
6. Token
Component Mapping 위치: packages/ui/src/theme/context-menu.css (--hm-context-menu-*).
- Surface 5개(
background,border-width,border-color,radius,shadow)는 Floating Surface Semantic을 source로 참조한다:--hm-sem-floating-background|border-width|border-color|radius|shadow. - 나머지(
text,padding,item-*,font-*,highlight-*,destructive-*,label-*,shortcut-*,separator-color,indicator-size,radio-dot-size)는 Dropdown Menu와 같은 현재 HM 값을 자체 정의한다. - 다른 컴포넌트의 Component Token을 쓰지 않는다. 신규 Primitive·Semantic 없음. 기존 Floating Semantic을 유지한다.
7. shadcn / Radix에서 유지한 항목
Radix 구조·ARIA(role=menu, menuitem*)·키보드(방향키, Home/End, Enter/Space, 오른쪽 방향키 sub menu, Escape, typeahead)·Portal·collision·data-slot·cn(..., className)·open/close animation class·z-50·lucide 아이콘.
접근성 주의: Radix ContextMenuTrigger는 기본적으로 포커스할 수 없는 span이다. 키보드 사용자가 여는 경로(Shift+F10 / context-menu key)를 제공하려면 소비자가 Trigger 영역에 tabIndex={0}을 주거나 asChild로 포커스 가능한 요소(버튼, 행 등)를 감싸야 한다. 우클릭만으로 접근 가능한 기능을 만들지 않는다. 대표 Story의 영역은 tabIndex={0}을 준다.
8. HM에서 변경한 항목
Dropdown Menu와 동일: shadcn 시각 class(rounded, shadow, p-1, focus:bg-accent 등)를 제거하고 theme CSS + data-highlighted로 대체. Radio는 CSS 크기 token을 쓰는 원(context-menu-radio-dot), Check / Radio indicator span에 data-slot="context-menu-item-indicator" 추가. Context Menu에는 sideOffset이 없다.
9. Storybook
- 대표 Story
All("Context Menu"): 점선 영역을 우클릭하면 전체 메뉴가 열린다(Label, Item + Shortcut, disabled, Separator, Checkbox, Radio Group, Sub menu, destructive). - verification:
Open(로드 시 합성contextmenu이벤트로 열린 상태, fullscreen).
10. 사용자 확정 결정
- 2026-10-03: Context Menu는 Dropdown Menu와 같은 floating menu 계열이므로 Floating Surface Semantic을 Component Token의 source로 쓴다. Component Token은 유지하고
--hm-dropdown-menu-*는 참조하지 않는다(작업 지시).
11. 미확정 / 보류
- 수치는 Dropdown Menu와 같은 현재 HM 규격이며 디자인 확정 시 함께 재검토.
- Home/End·typeahead·long press(터치)는 확인하지 못했다.
- Shift+F10 / context-menu key는 자동화 입력이 브라우저의 native
contextmenu이벤트를 만들지 못해 확인하지 못했다(Radix는 이 이벤트로 여는 구조이며 우클릭과 같은 경로). - 메뉴가 닫힌 뒤 Trigger가 포커스 불가면 focus는 body에 남는다(Radix 동작).
12. 관련 파일
packages/ui/src/components/ui/context-menu.tsx
packages/ui/src/theme/context-menu.css
packages/ui/src/theme.css (import)
packages/ui/src/index.ts (export)
src/components/context-menu.stories.tsx
13. 검증 상태
npm run typecheck,npm run build: 통과(2026-10-03).- 실제 interaction(Browser pane): 우클릭 시 native 메뉴 대신 HM 메뉴가 열림, 방향키 이동(disabled 항목 건너뜀), Enter로 Checkbox 토글(메뉴 유지), 방향키 아래 + 오른쪽으로 Sub menu 열림과 첫 항목 focus, Escape 두 번으로 sub → menu 순서 닫힘.
- computed style: Content·Sub Content 모두 white,
1px solid rgb(222,219,210), radius 4px,0 0 12px .2그림자, item 36px, Dropdown Menu(OpenStory) 값과 동일. rolemenu. - Dropdown Menu의 같은 값이 변하지 않음을 확인(회귀 없음).