# Multi Select 정해진 목록에서 여러 개를 고르게 하는 선택 컴포넌트입니다. 고른 뒤에도 목록은 열린 채로 남습니다. > 원본: `03_ASSET_GUIDE/catalog/multi-select.js` · CSS: `03_ASSET_GUIDE/styles/elements/multi-select.css` > kind `component` · category `selection` · status `stable` · specSource `measured` > [!NOTE] > **이번 31개 목록 밖에서 새로 추가한 항목입니다** > 요청 목록에는 없었고, seed · Spectrum · Codeit 에도 대응 항목이 없습니다. 참고한 것은 > Ant Design 의 `Select mode="multiple"` 하나뿐이고, 나머지는 이 저장소의 > [Select](./select.md) 와 [Checkbox](./checkbox.md) 규약을 이어붙인 > 것입니다. 수치는 `styles/elements/multi-select.css` 에서 실측했습니다. --- ## 1. 구성 닫혀 있을 때 보이는 **Trigger** 와, 열렸을 때 나타나는 **Listbox** 로 나뉩니다. Select 와 달리 Trigger 가 여러 개의 값을 동시에 보여줘야 하고, Option 은 체크박스를 가집니다. ``` ┌──────────────────────────────────────┐ │ (항목 A ×) (항목 B ×) +3 ⌄ │ ← Trigger ① Chip+Remove ② Overflow ③ Caret ④ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ [■] 전체 선택 선택 해제 │ ← Toolbar ⑤ │ 그룹 A │ ← Group label ⑥ │ [✓] 항목 A │ ← Option + Checkbox ⑦ │ [ ] 항목 D │ │ 3개 선택됨 · 최대 8개 │ ← Footer ⑧ └──────────────────────────────────────┘ ``` | # | 요소 | 필수 | 클래스 | 설명 | |---|---|---|---|---| | — | Root | 필수 | `atom-multi-select` | 상태(`data-state` · `data-empty` · `data-invalid` · `data-disabled`)를 가집니다. | | ① | Trigger | 필수 | `atom-multi-select__trigger` · `__value` · `__placeholder` | 지금 무엇이 선택되어 있는지와 Caret 을 보여주는 버튼입니다. | | ② | Chip | 선택 | `atom-multi-select__chip` · `__chip-label` · `__chip-remove` | 선택된 항목 하나입니다. Remove 를 눌러 목록을 열지 않고도 해제합니다. | | ③ | Overflow counter | 선택 | `atom-multi-select__overflow` | Trigger 폭에 들어가지 못한 나머지 개수입니다. | | — | Count | `display="count"` 일 때 | `atom-multi-select__count` | `n개 선택됨`. | | ④ | Caret | 필수 | `atom-multi-select__caret` | 열리면 180° 회전해 상태를 알립니다. | | — | Listbox | 필수 | `atom-multi-select__listbox` | 열렸을 때 나타나는 목록. | | ⑤ | Toolbar | 선택 | `atom-multi-select__toolbar` · `__clear` | 전체 선택과 선택 해제입니다. 선택지가 많을 때만 둡니다. | | ⑥ | Group label | 선택 | `atom-multi-select__group-label` | 선택지가 많을 때 묶음의 이름입니다. | | ⑦ | Option | 필수 | `atom-multi-select__option` · `__option-box` · `__option-check` · `__option-indeterminate` · `__option-icon` · `__option-text` · `__option-label` · `__option-description` | 체크박스와 라벨은 필수, 아이콘과 설명은 선택입니다. | | — | Empty | 선택지 0개일 때 | `atom-multi-select__empty` | 빈 목록 문구. | | ⑧ | Footer | 선택 | `atom-multi-select__footer` | 현재 선택 수와 상한입니다. 상한이 있을 때만 둡니다. | --- ## 2. 종류 / 크기 ### Display 선택된 값을 Trigger 에 어떻게 요약할지 정합니다. `chip` 은 고른 항목을 그대로 보여주고 개별 해제가 되지만 폭을 많이 씁니다. `count` 는 개수만 보여주며 폭에 관계없이 표시가 일정합니다. | Display | Trigger 내용 | 개별 해제 | 쓰는 곳 | | --- | --- | --- | --- | | `chip` | 선택된 항목 칩 + 넘침 카운트 | 칩의 × 로 가능 | 선택 3~5개, 무엇을 골랐는지가 중요할 때 | | `count` | n개 선택됨 | 목록을 열어야 함 | 선택이 많거나 Trigger 폭이 좁을 때 | > [!NOTE] > **마크업은 하나입니다** > Trigger 안에 Chip 과 Count 를 모두 두고, 모디파이어가 어느 쪽을 보일지 고릅니다. > Display 는 모디파이어만 바꿔 전환합니다. ### 펼친 상태 선택해도 Listbox 는 닫히지 않습니다. 매번 닫히면 여러 개를 고를 때 Trigger 를 반복해서 눌러야 하기 때문입니다. Esc 나 바깥 클릭으로만 닫습니다. ### 종류 (Variant) `weak` 은 배경만으로 입력 영역을 표시하고, `outline` 은 테두리를 씁니다. 한 화면 안에서는 다른 입력 컴포넌트와 같은 Variant 로 맞춥니다. ### 크기 (Size) | Size | Trigger 높이 | 모서리 | 폰트 | Chip 폰트 | | --- | --- | --- | --- | --- | | `small` | 40px | 8px | 14px | 12px | | `medium` | 48px | 8px | 15px | 13px | | `large` | 56px | 12px | 16px | 14px | Chip 은 Trigger 를 넘기지 않도록 사이즈마다 함께 줄어듭니다. ### 빈 목록 검색이나 필터로 선택지가 하나도 남지 않으면 Listbox 를 비워두지 말고 문구를 둡니다. ### Field 와 함께 라벨과 상한 안내는 Multi Select 가 아니라 [Field](./field.md) 가 담당합니다. 상한이 있으면 고르기 전에 알립니다. ### 클래스 계약 `styles/elements/multi-select.css` 헤더와 원본 `registry.json` `css.blocks` 기준입니다. 클래스가 없는 값(—)은 기본 스타일 그대로입니다. | Prop | 값 | 클래스 | |---|---|---| | `size` | `small` · `medium` · `large` | `--size_small` · — · `--size_large` | | `variant` | `weak` · `outline` | — · `--variant_outline` | | `display` | `chip` · `count` | — · `--display_count` | 상태 선택자: `[data-state="closed|open"]` · `[data-empty]` · `[data-invalid]` · `[data-disabled]` · `__trigger:hover` · `__trigger:focus-visible` · `__option[aria-selected="true"]` · `__option[aria-checked="mixed"]` · `__option[aria-disabled="true"]` --- ## 3. 상태 값이 없을 때는 `data-empty` 로 Placeholder 를 보여주고, 값이 생기면 Chip 이나 Count 가 그 자리를 대신합니다. `data-invalid` 와 `data-disabled` 는 Select 와 같습니다. | State | 표현 | 선택자 | |---|---|---| | `closed` | Listbox 숨김 | `[data-state="closed"]` | | `open` | Listbox 표시, Caret 180° 회전 | `[data-state="open"]` | | empty | Placeholder 표시, Chip · Count · Overflow 숨김 | `[data-empty]` | | hover | `filter: brightness(0.96)` | `__trigger:hover` | | focus | `--atom-control-border-focus` | `__trigger:focus-visible` | | `invalid` | `--atom-control-border-invalid` | `[data-invalid]` | | `disabled` | `opacity: 0.4`, 입력 불가 | `[data-disabled]` | --- ## 4. Props | Prop | Type | Default | 필수 | 설명 | |---|---|---|---|---| | `value` | `string[]` | `[]` | | 선택된 항목들의 값입니다. | | `options` | `Array<{ value, label, description?, icon?, disabled? }>` | | ● | 선택지 목록입니다. | | `placeholder` | `string` | | | 선택이 없을 때 Trigger 에 보이는 문구입니다. | | `display` | `"chip" \| "count"` | `"chip"` | | 선택된 값을 Trigger 에 요약하는 방식입니다. | | `maxVisibleChips` | `number` | `3` | | `display="chip"` 에서 칩으로 보여줄 최대 개수입니다. 나머지는 넘침 카운트가 됩니다. | | `maxSelected` | `number` | | | 고를 수 있는 최대 개수입니다. 도달하면 나머지 Option 이 비활성이 됩니다. | | `selectAll` | `boolean` | `false` | | Listbox 위에 전체 선택 / 선택 해제 Toolbar 를 둡니다. | | `size` | `"small" \| "medium" \| "large"` | `"medium"` | | Trigger 높이와 폰트 크기를 결정합니다. | | `variant` | `"weak" \| "outline"` | `"weak"` | | Trigger 의 배경과 테두리 처리입니다. | | `invalid` | `boolean` | `false` | | Trigger 테두리를 오류 색으로 바꿉니다. | | `disabled` | `boolean` | `false` | | 열 수 없게 만듭니다. | | `emptyText` | `string` | `"결과가 없어요"` | | 선택지가 비었을 때 Listbox 에 보일 문구입니다. | | `onValueChange` | `(value: string[]) => void` | | | 선택이 바뀔 때마다 호출됩니다. 목록이 닫힐 때가 아닙니다. | ### Parts (compound) Multi Select 는 파트 없이 하나의 컴포넌트입니다. 내부 DOM 은 원본 Markup 을 따릅니다. | Part | 요소 | 설명 | |---|---|---| | `MultiSelect` | `