Floating
ContextMenu
우클릭으로 커서 위치에 열리는 컨텍스트 메뉴. 화살표키 내비게이션, typeahead, ARIA menu 패턴을 지원합니다.
설치
npm install @woon-ui/context-menuimport { ContextMenu } from '@woon-ui/context-menu'
import '@woon-ui/context-menu/css'개요
import { ContextMenu } from '@woon-ui/context-menu'ContextMenu는 우클릭 시 커서 위치에 열리는 컨텍스트 메뉴입니다.
@floating-ui/react 기반으로 화면 경계를 자동 감지하며, 화살표키 내비게이션, typeahead, ARIA menu 패턴을 내장합니다.
기본 사용법
import { ContextMenu } from '@woon-ui/context-menu'
<ContextMenu.Root>
<ContextMenu.Trigger asChild>
<div>우클릭 하세요</div>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Label>편집</ContextMenu.Label>
<ContextMenu.Item onSelect={() => {}}>복사</ContextMenu.Item>
<ContextMenu.Item onSelect={() => {}}>붙여넣기</ContextMenu.Item>
<ContextMenu.Separator />
<ContextMenu.Item onSelect={() => {}}>삭제</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Root>Preview
우클릭 하세요
컴파운드 컴포넌트
ContextMenu.Root
상태를 관리합니다. 포지셔닝 props는 없습니다 — 우클릭 커서 위치에 항상 열립니다.
| Prop | Type | Default |
|---|---|---|
| open | boolean | — |
| 제어 모드: 열림 상태 | ||
| defaultOpen | boolean | false |
| 비제어 모드: 초기 열림 여부 | ||
| onOpenChange | (open: boolean) => void | — |
| 열림 상태 변경 콜백 | ||
ContextMenu.Trigger
우클릭 이벤트를 캡처하는 래퍼입니다. asChild를 사용하면 자식 요소에 Trigger 동작을 위임합니다. 기본적으로 자체 div를 렌더합니다.
ContextMenu.Content
메뉴 컨테이너. role="menu"로 렌더되며 자식 Item들을 포함합니다.
ContextMenu.Item
| Prop | Type | Default |
|---|---|---|
| onSelect | () => void | — |
| 항목 선택 시 호출되는 콜백. 호출 후 메뉴가 자동으로 닫힙니다. | ||
| disabled | boolean | false |
| 비활성화. 클릭·키보드 선택이 막히고 시각적으로 흐리게 표시됩니다. | ||
| asChild | boolean | false |
| true면 자식 요소에 Item 동작을 위임합니다. | ||
| textValue | string | — |
| typeahead에 사용할 텍스트. children이 문자열이면 자동 추출되므로 보통 불필요합니다. | ||
ContextMenu.Label
항목 그룹의 비인터랙티브 레이블.
ContextMenu.Separator
항목 사이 구분선. role="separator"로 렌더됩니다.
ContextMenu.Group
관련 항목을 시맨틱으로 묶는 컨테이너. role="group"으로 렌더됩니다.
Disabled 항목
disabled prop을 사용하면 항목이 비활성화됩니다. 클릭과 키보드 선택이 막힙니다.
<ContextMenu.Item disabled>저장</ContextMenu.Item>Preview
우클릭 하세요 (비활성 항목 포함)
Group과 Label
Group과 Label을 사용해 관련 항목을 묶고 레이블을 붙일 수 있습니다.
<ContextMenu.Group>
<ContextMenu.Label>클립보드</ContextMenu.Label>
<ContextMenu.Item onSelect={() => {}}>잘라내기</ContextMenu.Item>
<ContextMenu.Item onSelect={() => {}}>복사</ContextMenu.Item>
</ContextMenu.Group>Preview
우클릭 하세요 (그룹 메뉴)
스타일
@woon-ui/context-menu/css로 기본 스타일이 적용됩니다. 특정 값만 바꾸려면 선택자로 override하세요.
/* 하이라이트 색상 변경 */
[data-woon-context-menu-item][data-highlighted] {
background-color: #eff6ff;
color: #1d4ed8;
}완전히 교체하려면 @woon-ui/react/css import를 제거하고 아래 CSS를 복사해서 수정하세요.
[data-woon-context-menu-floating] { z-index: 510; }
[data-woon-context-menu-content],
[data-woon-context-menu-content] *,
[data-woon-context-menu-content] *::before,
[data-woon-context-menu-content] *::after {
box-sizing: border-box;
}
[data-woon-context-menu-content] {
min-width: 8rem;
padding: 0.25rem;
background: #fff;
border-radius: 8px;
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.06),
0 4px 16px rgba(0, 0, 0, 0.12);
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 0.875rem;
line-height: 1.5;
outline: none;
}
[data-woon-context-menu-content][data-state='open'] {
animation: woon-context-menu-in 160ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes woon-context-menu-in {
from { opacity: 0; scale: 0.97; }
to { opacity: 1; scale: 1; }
}
[data-woon-context-menu-item] {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 0.625rem;
border-radius: 5px;
color: #18181b;
cursor: default;
user-select: none;
outline: none;
transition: background-color 100ms;
}
[data-woon-context-menu-item][data-highlighted] {
background-color: #f4f4f5;
}
[data-woon-context-menu-item][data-disabled] {
opacity: 0.4;
pointer-events: none;
}
[data-woon-context-menu-separator] {
height: 1px;
margin: 0.25rem 0.625rem;
background-color: #e4e4e7;
}
[data-woon-context-menu-label] {
padding: 0.375rem 0.625rem 0.25rem;
font-size: 0.75rem;
font-weight: 600;
color: #71717a;
user-select: none;
}
[data-woon-context-menu-group] + [data-woon-context-menu-group] {
margin-top: 0.25rem;
}
@media (prefers-reduced-motion) {
[data-woon-context-menu-content] { animation: none !important; }
}접근성
- Content에
role="menu"적용 - 각 Item에
role="menuitem", disabled 시aria-disabled적용 - 화살표키 위/아래로 항목 간 이동
- 첫 글자 입력으로 해당 항목으로 이동 (typeahead)
- Enter / Space로 항목 선택
- ESC로 닫기 (Dialog와 escape-stack 공유)
- 외부 클릭으로 닫기
- 닫힐 때 Trigger로 포커스 복귀
data 속성
| 속성 | 대상 | 값 |
|---|---|---|
data-woon-context-menu-floating | 포지셔닝 래퍼 | — |
data-woon-context-menu-content | Content | — |
data-state | Content | "open" |
data-side | Content | "top" "right" "bottom" "left" |
data-woon-context-menu-item | Item | — |
data-highlighted | Item | 현재 포커스된 항목 |
data-disabled | Item | 비활성 항목 |
data-woon-context-menu-separator | Separator | — |
data-woon-context-menu-label | Label | — |
data-woon-context-menu-group | Group | — |