# Combobox 현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Combobox 고유의 API·Token·상태·결정만 기록한다. ## 1. 상태와 성격 | 항목 | 내용 | |---|---| | 성격 | **Reusable component, Popover + Command 조합** (typed `options` wrapper) | | 구현 상태 | 구현 완료 (single-select, 현재 HM 규격) | | 기준일 | 2026-10-02 | | 기반 | `Popover`(public API) + `Command`(public API) 조합. shadcn 공식 Combobox는 예시 조합이다 | | 대표 Story | `Components/Search & Date/Combobox` → `All` (이름 "Combobox") | | 보류 | 아래 §11 | ## 2. 적용 근거 - **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Combobox가 없다(Select와 Search Field는 다른 컴포넌트). 참고할 수치가 없었다. - **Component로 만든 이유**: 검색 가능한 단일 선택은 서비스에서 반복되고(trigger 텍스트, 검색, 선택 표시, 닫힘, focus 복귀가 동일) 최소 API(`options`)로 충분해서 typed wrapper를 제공한다. 더 복잡한 항목(그룹, 아이콘 등)은 소비자가 `Command`를 직접 조합한다. - **구현 방식**: Popover(Portal, collision, Escape, focus 복귀)와 Command(검색, 필터링, 키보드, listbox ARIA)를 그대로 조합한다. Select의 구현이나 연결형 윤곽을 복제하지 않았다. - **보류**: §11. ## 3. Select와의 구분 | | Select | Combobox | |---|---|---| | 용도 | 정해진 짧은 목록에서 선택, 검색 불필요 | 검색/필터가 필요한 선택 | | 구현 | Radix Select | Popover + Command(cmdk) | | 목록 | trigger에 붙은 연결형 윤곽, 선택 항목은 면으로 표시 | 독립 floating 패널(검색 Input + 목록), 선택 항목은 Accent 45% 면 + check 아이콘 | | 접근성 모델 | Radix Select(listbox) | `role=combobox` trigger + 패널 안 cmdk combobox/listbox | ## 4. 기본 규격 (현재 HM 규격) | 항목 | 값 | |---|---| | Size | `large`(기본, 44px, 16 / 24), `medium`(34px, 14 / 20) — Select와 같은 두 크기 | | Trigger | 폭 100%(부모 폭), padding-x 12px, 1px `--hm-sem-stroke-input-default`, radius 4px, 흰 면, 오른쪽 chevron 18px | | 값 / placeholder | 선택된 option의 label, 없으면 placeholder("선택", `--hm-sem-fg-placeholder`), 길면 말줄임 | | Popup | padding 0, 폭 = trigger 폭(`--radix-popover-trigger-width`), 독립 4px gap. Focus Light 2 border, input radius 4px(네 모서리), shadow `0 2px 24px black 12%` | | 검색 | Command Input(placeholder 기본 "검색...") | | 목록 | Command List(최대 280px), Item 높이 36px·padding-x 12px·radius 0·weight 400. Large 16/24, Medium 14/20. 선택 check 16px, 긴 label은 말줄임 | | Empty | "결과가 없습니다."(`emptyText`) | ## 5. 상태 규격 | 상태 | 값 | |---|---| | Focus / Open | trigger 테두리 전체가 `--hm-sem-stroke-input-focus` (Input·Select와 같은 방식) | | Highlighted option | `--hm-sem-surface-highlight` (Accent 45%) | | Selected option | check 아이콘 + Accent 45% 면(`data-checked`). Command highlight가 다른 항목으로 옮겨져도 유지 | | Disabled (combobox) | opacity 0.6(이 컴포넌트 값), cursor not-allowed, 열리지 않음 | | Disabled option | Command의 disabled(opacity 0.6), 방향키 이동·선택 제외 | | 선택 후 | 값 갱신, Popover 닫힘, focus가 trigger로 복귀. 다시 열면 검색어가 비어 있다(Command가 닫을 때 unmount됨) | ## 6. API export: `Combobox`, types `ComboboxOption`(`{ value, label, disabled? }`), `ComboboxSize`. - `options`(필수), `value`/`defaultValue`/`onValueChange(value)`, `placeholder`, `searchPlaceholder`, `emptyText`, `size`, `disabled`, 그리고 `button` props(`id`, `aria-label`, `className`…). - 필터링: option의 `value`와 `label`(keywords)에 cmdk 기본 fuzzy 필터를 쓴다. - 이미 선택된 option을 다시 선택해도 값은 유지된다(해제 UI는 없다). - **접근성**: Trigger는 `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded`. 패널 안 Command Input이 `role=combobox`(`aria-controls` → `role=listbox`), option은 `role=option`. 이름은 `