Menu with Combobox
Menu Content 안에 필터 입력을 배치해 긴 작업 목록을 즉시 좁힙니다.
<script lang="ts">
import * as Field from '@odbd/svelte/field'
import * as Menu from '@odbd/svelte/menu'
const actions = [
{ value: 'assign-owner', label: '담당자 지정', scope: '워크플로' },
{ value: 'request-review', label: '검토 요청', scope: '승인' },
{ value: 'copy-link', label: '공유 링크 복사', scope: '협업' },
{ value: 'export-report', label: '리포트 내보내기', scope: '문서' },
{ value: 'archive-project', label: '프로젝트 보관', scope: '정리' },
{ value: 'open-history', label: '변경 기록 보기', scope: '감사' },
]
let query = $state('')
const normalizedQuery = $derived(query.trim().toLowerCase())
const filteredActions = $derived(
normalizedQuery
? actions.filter((action) =>
`${action.label} ${action.scope}`.toLowerCase().includes(normalizedQuery),
)
: actions,
)
const updateQuery = (event: Event) => {
query = (event.currentTarget as HTMLInputElement).value
}
// 인쇄 가능한 키만 메뉴 typeahead로 새지 않게 막는다 — 방향키/Enter/Escape 등
// 탐색 키는 Menu 머신이 받아야 필터 결과를 키보드로 선택할 수 있다.
const NAV_KEYS = new Set([
'ArrowDown',
'ArrowUp',
'ArrowLeft',
'ArrowRight',
'Enter',
'Escape',
'Home',
'End',
'Tab',
'PageUp',
'PageDown',
])
const keepTypingInsideInput = (event: KeyboardEvent) => {
if (!NAV_KEYS.has(event.key)) event.stopPropagation()
}
</script>
<Menu.Root>
<Menu.Trigger class="odbd-button" data-variant="outline" data-size="md">
작업 찾기
<Menu.Indicator>▾</Menu.Indicator>
</Menu.Trigger>
<Menu.Positioner>
<Menu.Content style="width: min(18rem, calc(100vw - var(--odbd-space-8)));">
<div style="padding: var(--odbd-space-2);">
<Field.Root>
<Field.Label>작업 필터</Field.Label>
<Field.Input
value={query}
placeholder="작업 이름을 입력하세요"
aria-label="메뉴 작업 필터"
oninput={updateQuery}
onkeydown={keepTypingInsideInput}
/>
</Field.Root>
</div>
<Menu.Separator />
<Menu.ItemGroup>
<Menu.ItemGroupLabel>작업</Menu.ItemGroupLabel>
{#each filteredActions as action}
<Menu.Item value={action.value} class="examples-menu-item">
<span>{action.label}</span>
<small>{action.scope}</small>
</Menu.Item>
{:else}
<Menu.Item value="empty" disabled>일치하는 작업이 없어요.</Menu.Item>
{/each}
</Menu.ItemGroup>
</Menu.Content>
</Menu.Positioner>
</Menu.Root>