Menubar
File, Edit, View처럼 애플리케이션 상단에 놓이는 단어의 띠이고, 각각이 메뉴를 엽니다. 하나가 열린 뒤에는 띠 위를 지나가면 방금 있던 메뉴가 닫히는 대신 옆 메뉴로 넘어갑니다.
tsx
import { Menubar, MenubarMenu, MenuItem, MenuSeparator } from 'neba';
<Menubar>
<MenubarMenu label="File">
<MenuItem shortcut="⌘N">New file</MenuItem>
<MenuSeparator />
<MenuItem>Open…</MenuItem>
</MenubarMenu>
</Menubar>;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 단어의 높이와 타입 스케일. 각 단계에서 컨트롤 높이보다 한 칸 아래인 자기만의 사다리입니다 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 열린 메뉴의 단어가 띠는 색 역할 |
| density공통 | 'default' | 'compact' | 'default' | 단어의 가로 여백만 바꿉니다 |
| orientation공통 | 'horizontal' | 'vertical' | 'horizontal' | 띠가 이어지는 방향. 방향키도 이를 따릅니다 |
| modal | boolean | true | 열린 메뉴가 뒤 페이지를 가져가는지 여부 |
| loopFocus | boolean | true | 방향키가 바 양 끝에서 순환할지 여부 |
| disabled | boolean | false | 바 위의 모든 메뉴를 한 번에 비활성화합니다 |
| children | ReactNode | — | MenubarMenu들 |
<div>의 모든 속성이 color를 제외하고 그대로 전달됩니다.
자기 표면은 그리지 않습니다. 메뉴 바는 Toolbar나 WindowPane의 타이틀 바, Header처럼 무언가 위에 놓입니다. 이미 시트 위에 있는 띠 아래에 시트를 한 장 더 깔면 시트가 둘이 됩니다.
MenubarMenu
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| label * | ReactNode | — | 바 위에 놓이는 단어 |
| startIcon | ReactNode | — | 단어 앞의 내용. em 단위라 단어를 따라갑니다 |
| disabled | boolean | false | 사용 불가. 단어는 바에 남고 아무것도 열지 않습니다 |
| children | ReactNode | — | 행들. Menu 안에 쓰는 것과 똑같이 씁니다 |
행은 Menu가 받는 것과 같은 MenuItem, MenuSeparator, MenuGroup, MenuCheckboxItem, MenuRadioGroup, MenuSubmenu입니다. 같은 메뉴이기 때문입니다. size, color, density는 바의 것이므로 여기에서 다시 지정하지 않습니다.
예시
중첩된 행과 선택되는 행
Menu가 담을 수 있는 것은 바 위의 메뉴도 담을 수 있습니다. 서브메뉴와 그룹, 체크박스 행, 라디오 행이 모두 들어갑니다.
size
각 단계에서 컨트롤 높이보다 한 칸 아래인 자기만의 사다리를 씁니다. 메뉴 바는 버튼의 행이 아니라 단어의 띠이고, 대개 이미 높이를 가진 무언가 안에 놓이기 때문입니다.
tsx
import { Menubar, MenubarMenu, MenuItem } from 'neba';
const sizes = ['xs', 'sm', 'md', 'lg'] as const;
export default function MenubarSizes() {
return (
<div className="flex flex-col items-start gap-4">
{sizes.map((size) => (
<Menubar key={size} size={size} aria-label={size}>
<MenubarMenu label="File">
<MenuItem>New file</MenuItem>
</MenubarMenu>
<MenubarMenu label="Edit">
<MenuItem>Undo</MenuItem>
</MenubarMenu>
<MenubarMenu label="View">
<MenuItem>Sidebar</MenuItem>
</MenubarMenu>
</Menubar>
))}
</div>
);
}orientation
vertical은 단어를 세로로 쌓고, 방향키도 그 방향을 따릅니다.
tsx
import { Menubar, MenubarMenu, MenuItem } from 'neba';
export default function MenubarOrientation() {
return (
<Menubar orientation="vertical" size="sm" aria-label="Sections" className="w-40">
<MenubarMenu label="Project">
<MenuItem>Settings</MenuItem>
<MenuItem>Members</MenuItem>
</MenubarMenu>
<MenubarMenu label="Deploys">
<MenuItem>Production</MenuItem>
<MenuItem>Preview</MenuItem>
</MenubarMenu>
<MenubarMenu label="Billing">
<MenuItem>Invoices</MenuItem>
</MenubarMenu>
</Menubar>
);
}창 위에서
tsx
import { Menubar, MenubarMenu, MenuItem, MenuSeparator, Typography, WindowPane } from 'neba';
export default function MenubarWindow() {
return (
<WindowPane os="windows11" title="notes.txt" className="w-full max-w-lg" height={220}>
<div className="flex h-full flex-col">
<Menubar size="xs" className="border-b border-(--neba-border) px-1 py-0.5">
<MenubarMenu label="File">
<MenuItem>New</MenuItem>
<MenuSeparator />
<MenuItem>Exit</MenuItem>
</MenubarMenu>
<MenubarMenu label="Edit">
<MenuItem>Find…</MenuItem>
</MenubarMenu>
<MenubarMenu label="Format">
<MenuItem>Word wrap</MenuItem>
</MenubarMenu>
</Menubar>
<div className="p-3">
<Typography level="body">
The strip sits on the window, not on a sheet of its own.
</Typography>
</div>
</div>
</WindowPane>
);
}접근성
role="menubar"로 렌더링되고, 각 단어는 메뉴를 소유한menuitem이 됩니다.- 바 전체가 하나의 tab stop입니다. 방향키로 단어 사이와 행 안쪽을 오갈 수 있고, typeahead도 양쪽에서 동작합니다.
- 한 페이지에 바가 둘 이상이면
aria-label을 주세요.