# Autocomplete 초점이 들어오면 목록을 펼치고, 입력한 글자를 포함하는 항목만 남겨 보여주는 검색형 선택 엘리먼트입니다. > 원본: `03_ASSET_GUIDE/catalog/autocomplete.js` · CSS: `03_ASSET_GUIDE/styles/elements/autocomplete.css` > kind `component` · category `selection` · status `stable` · specSource `measured` > [!NOTE] > **이번 31개 목록의 `combobox` 자리를 실제로 구현한 항목입니다** > `docs/components/combobox.md` 는 아직 `proposed` 상태의 제안 문서이고, 이 문서는 그중 > "타이핑으로 목록을 좁힌다" 하나에 집중해 실제 CSS 로 구현한 것입니다. 수치는 > `styles/elements/autocomplete.css` 에서 실측했습니다. 참고한 것은 Spectrum 의 > Combo Box 와 Ant Design 의 AutoComplete, 그리고 이 저장소의 > [Text Input](./text-input.md) · [Select](./select.md) 규약입니다. --- ## 1. 구성 값을 입력받는 **Field** 와, 초점이 들어오면 그 아래에 펼쳐지는 **Listbox** 로 나뉩니다. [Select](./select.md) 와 달리 Trigger 가 버튼이 아니라 실제 입력 필드이고, Option 은 입력과 일치한 구간을 강조합니다. ``` ┌──────────────────────────────────────┐ │ [🔍] 설| (×) │ ← Field ① Icon ② Input ③ Clear ④ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 기본 항목 │ ← Listbox ⑤ Group label ⑥ │ ▌[설]정 관리 │ ← Option + Match ⑦ (data-active) │ 상세 [설]정 ✓ │ ← Check ⑧ (aria-selected) │ 전체 항목 │ └──────────────────────────────────────┘ (2개 결과) ← Status ⑨ (aria-live, 화면에 안 보임) ``` | # | 요소 | 필수 | 클래스 | 설명 | |---|---|---|---|---| | — | Root | 필수 | `atom-autocomplete` | 상태(`data-state` · `data-invalid` · `data-disabled` · `data-loading`)를 가집니다. | | ① | Field | 필수 | `atom-autocomplete__field` | 실제 `` 을 감싸는 상자입니다. 상자가 테두리를 그리고 input 은 투명합니다. | | ② | Icon | 선택 | `atom-autocomplete__icon` | 검색이라는 성격을 알리는 앞쪽 아이콘입니다. 선택입니다. | | ③ | Input | 필수 | `atom-autocomplete__input` | `role="combobox"` 를 가진 입력입니다. 초점은 열려 있는 동안에도 여기 머뭅니다. | | ④ | Clear | 선택 | `atom-autocomplete__clear` | 입력을 한 번에 지웁니다. 값이 있을 때만 나타납니다. | | — | Spinner | `loading` 일 때 | `atom-autocomplete__spinner` | 원격 조회 중 Clear 자리에 들어갑니다. | | ⑤ | Listbox | 필수 | `atom-autocomplete__listbox` | 필터된 선택지 목록. Field 바로 아래에 붙습니다. | | ⑥ | Group label | 선택 | `atom-autocomplete__group-label` | 선택지가 많을 때 묶음의 이름입니다. | | ⑦ | Option | 필수 | `atom-autocomplete__option` · `__option-icon` · `__option-text` · `__option-label` · `__option-description` | 라벨은 필수, 아이콘과 설명은 선택입니다. | | ⑦ | Match | — | `atom-autocomplete__match` | Option 라벨 안에서 입력과 일치한 구간입니다. | | ⑧ | Check | — | `atom-autocomplete__option-check` | 이미 선택된 항목에만 나타납니다. | | — | Empty | 결과 0개일 때 | `atom-autocomplete__empty` · `__empty-title` · `__empty-hint` | 검색어와 다음 행동 안내. | | — | Loading | `loading` 일 때 | `atom-autocomplete__loading` | Listbox 안의 `불러오는 중` 한 줄. | | ⑨ | Status | 필수 | `atom-autocomplete__status` | 결과 개수를 읽어주는 `aria-live` 영역입니다. 화면에는 보이지 않습니다. | `__text` · `__placeholder` · `__caret` 은 카드 프리뷰처럼 초점이 없는 정적 데모가 입력된 글자와 커서를 그리기 위한 대역입니다. 실제 컴포넌트는 `__input` 을 씁니다. --- ## 2. 종류 / 크기 ### 초점과 열림 초점이 들어오면 입력 없이도 전체 목록이 펼쳐집니다. 고를 수 있는 값을 입력 전에 보여주기 위해서입니다. `Esc` 나 바깥 클릭, 초점 이탈로 닫히고 항목을 고르면 값이 채워지면서 함께 닫힙니다. | 동작 | 결과 | | --- | --- | | Field 에 초점 | `data-state` 가 `open` 이 되고 전체 목록이 펼쳐집니다. | | 입력 | 입력을 포함하는 항목만 남습니다. 목록은 열린 채입니다. | | 항목 선택 | 값이 Input 에 채워지고 목록이 닫힙니다. | | Esc | 목록만 닫힙니다. 입력한 글자는 그대로 남습니다. | | 초점 이탈 (blur) | 목록이 닫힙니다. | ### 필터링 매칭은 **앞자리 일치(prefix)가 아니라 부분 문자열 일치(contains)** 입니다. `정` 을 입력하면 `정보 수정` 뿐 아니라 `설정 관리` 와 `사용자 정보` 도 남습니다. 이름의 일부만 기억하는 경우에도 찾을 수 있게 하기 위해서입니다. - 대소문자는 구분하지 않습니다. - 일치한 구간은 `__match` 로 감싸 강조합니다. 한 라벨 안에 여러 번 걸리면 모두 강조합니다. - 필터는 입력할 때마다 즉시 반영합니다. 원격 조회라면 요청을 debounce 하고 그동안 `data-loading` 을 켭니다. > [!NOTE] > **강조는 굵기와 색으로 합니다** > 일치 구간은 줄바꿈을 만나면 두 줄에 걸쳐 나뉩니다. 배경을 칠하면 잘린 상자가 남으므로 > `--atom-fg-brand` 와 굵기만 씁니다. ### 활성 항목과 선택된 항목 `↑` `↓` 로 옮겨 다니는 **활성 항목**과 이미 고른 **선택된 항목**은 구분합니다. 활성 항목은 `data-active` 로 브랜드 톤 배경을 얻고, 선택된 항목은 체크와 굵은 라벨로 표시합니다. 둘을 같은 색으로 칠하면 화살표 키로 이동한 위치가 보이지 않습니다. | 표시 | 속성 | 의미 | | --- | --- | --- | | 브랜드 톤 배경 | `data-active` | 키보드 커서가 지금 여기 있습니다. 초점은 Input 에 있습니다. | | 회색 배경 | `:hover` | 포인터가 지금 여기 있습니다. | | 체크 + 굵은 라벨 | `aria-selected="true"` | 이미 고른 값입니다. | | 흐림 | `aria-disabled="true"` | 고를 수 없습니다. 목록에서 빼지 않고 남겨 이유를 설명합니다. | ### 결과 없음 필터 결과가 0개이면 검색어와 다음에 해볼 방법을 한 줄로 알려줍니다. 빈 Listbox 만 두면 오류로 보입니다. ### 로딩 선택지를 서버에서 가져오는 경우에만 씁니다. `data-loading` 은 Field 의 Clear 를 Spinner 로 바꾸고 Listbox 에 `불러오는 중` 한 줄을 남깁니다. 결과 없음과 로딩 중을 구분하기 위해 목록을 비우지 않습니다. ### 크기 (Size) 높이는 컨트롤 공통 토큰 `--atom-control-h-small` · `--atom-control-h` · `--atom-control-h-large`(40 · 48 · 56px)를 씁니다. 같은 폼 안의 [Text Input](./text-input.md) · [Select](./select.md) 와 높이를 맞춥니다. | Size | Field 높이 | 모서리 | 폰트 | Icon | | --- | --- | --- | --- | --- | | `small` | 40px | 8px | 14px | 16px | | `medium` | 48px | 8px | 15px | 18px | | `large` | 56px | 12px | 16px | 18px | ### 종류 (Variant) `outline` 이 기본이며 Text Input 과 같습니다. 툴바처럼 이미 회색 면 위에 놓일 때만 `weak` 로 바꿉니다. ### Field 와 함께 라벨과 필수 표시, 하단 설명은 [Field](./field.md) 가 담당합니다. ### 클래스 계약 `styles/elements/autocomplete.css` 헤더와 원본 `registry.json` `css.blocks` 기준입니다. 클래스가 없는 값(—)은 기본 스타일 그대로입니다. | Prop | 값 | 클래스 | |---|---|---| | `size` | `small` · `medium` · `large` | `--size_small` · — · `--size_large` | | `variant` | `outline` · `weak` | — · `--variant_weak` | 상태 선택자: `[data-state="closed|open"]` · `[data-invalid]` · `[data-disabled]` · `[data-loading]` · `__field:hover` · `__field:focus-within` · `__option[data-active]` · `__option[aria-selected="true"]` · `__option[aria-disabled="true"]` --- ## 3. 상태 `closed` 와 `open` 이 기본 상태이고 여기에 `data-invalid` 와 `data-disabled` 가 겹칩니다. 열려 있으면 초점이 들어와 있는 상태이므로 `open` 일 때 Field 는 초점 링을 유지합니다. | State | 표현 | 선택자 | |---|---|---| | `closed` | Listbox 숨김 | `[data-state="closed"]` | | `open` | Listbox 표시, Field 초점 링 유지 | `[data-state="open"]` | | hover | Field 테두리 `--atom-control-border-hover` | `__field:hover` | | focus | Field 테두리 `--atom-control-border-focus` | `__field:focus-within` | | `loading` | Clear 대신 Spinner | `[data-loading]` | | `invalid` | Field 테두리 `--atom-control-border-invalid` | `[data-invalid]` | | `disabled` | `opacity: 0.45`, 입력 불가, Field 배경 `--atom-bg-neutral-weak` | `[data-disabled]` | --- ## 4. Props | Prop | Type | Default | 필수 | 설명 | |---|---|---|---|---| | `value` | `string` | | | 선택이 확정된 항목의 값입니다. | | `inputValue` | `string` | | | Input 에 지금 적혀 있는 글자입니다. 필터의 기준이며 `value` 와 다를 수 있습니다. | | `options` | `Array<{ value, label, description?, icon?, disabled? }>` | | ● | 필터 전 전체 선택지입니다. | | `filter` | `(option, inputValue) => boolean` | 대소문자 무시 contains | | 필터 규칙입니다. 기본값은 라벨에 입력이 포함되는지 봅니다. | | `openOnFocus` | `boolean` | `true` | | 초점이 들어올 때 입력 없이도 전체 목록을 펼칩니다. | | `highlightMatch` | `boolean` | `true` | | Option 라벨에서 일치한 구간을 `__match` 로 감쌉니다. | | `placeholder` | `string` | | | 값이 없을 때 Input 에 보이는 문구입니다. | | `clearable` | `boolean` | `true` | | 값이 있을 때 Clear 버튼을 보여줍니다. | | `loading` | `boolean` | `false` | | 원격 조회 중임을 알립니다. Clear 자리에 Spinner 가 들어갑니다. | | `size` | `"small" \| "medium" \| "large"` | `"medium"` | | Field 높이와 폰트 크기를 결정합니다. | | `variant` | `"outline" \| "weak"` | `"outline"` | | Field 의 배경과 테두리 처리입니다. | | `invalid` | `boolean` | `false` | | Field 테두리를 오류 색으로 바꿉니다. | | `disabled` | `boolean` | `false` | | 입력도 열기도 할 수 없게 만듭니다. | | `emptyText` | `string` | `"검색 결과가 없어요"` | | 필터 결과가 0개일 때 Listbox 에 보일 문구입니다. | | `onInputValueChange` | `(inputValue: string) => void` | | | 입력이 바뀔 때마다 호출됩니다. 원격 조회는 여기에 debounce 를 겁니다. | | `onValueChange` | `(value: string) => void` | | | 항목을 골라 선택이 확정될 때 호출됩니다. | ### Parts (compound) Autocomplete 는 파트 없이 하나의 컴포넌트입니다. 내부 DOM 은 원본 Markup 을 그대로 따릅니다. | Part | 요소 | 설명 | |---|---|---| | `Autocomplete` | `
검색 결과가 없어요 다른 이름으로 검색해보세요