Combobox

패키지 네임스페이스가 아니라 레시피입니다. useCombobox가 열림 상태, 값, 입력, 하이라이트, id를 맡습니다. 외형은 컴포넌트가 맡습니다. Combobox.* export는 없습니다. 루트 하나에는 인터랙티브 역할 하나만 둡니다.

표시 방식

useCombobox({ presentation: 'auto' })는 useSheetPresentation을 통해 거친 포인터나 좁은 뷰포트에서는 시트를, 그 밖에는 팝오버를 고릅니다. 서버와 첫 페인트에서는 항상 팝오버입니다. 문서 스크린샷이 고정되어야 할 때는 popover나 sheet를 강제하세요.

목록이 열린 동안 표시 방식이 바뀌면 훅은 blur로 닫습니다.

배지는 useSheetPresentation이 여기서 고른 값을 보여 줍니다. isSheet는 isTouch가 아닙니다.

popover · isSheet=false

Popover · 단일

형제 Label + Field.Root id. 입력이 combobox입니다. 목록은 Popover.Root 안에 있습니다.

const cb = useCombobox({ options, presentation: 'popover' })

<div className="labelled-field">
  <Label htmlFor={cb.ids.input}>Framework</Label>
  <Field.Root {...cb.getAnchorProps()}>
    <Field.TextInput {...cb.getInputProps()} />
    <Field.Icon><Chevron /></Field.Icon>
  </Field.Root>
</div>
<Popover.Root {...cb.getPopoverProps()}>
  <OptionList.Root {...cb.getListboxProps()}>
    {cb.items.map((item) => (
      <OptionList.Option key={item.id} {...cb.getOptionProps(item)} />
    ))}
  </OptionList.Root>
</Popover.Root>
React
Redux
Svelte

Popover · 다중

getChipInputProps로 Field.ChipInput을 씁니다. 칩은 레이블입니다. 값은 중복이 제거됩니다.

design
design
tokens

시트

닫힌 컨트롤은 Button.Root입니다. 검색 Field는 시트 안에 있습니다. inputValue와 목록은 팝오버와 같습니다.

const cb = useCombobox({ options, presentation: 'sheet' })

<Button.Root {...cb.getTriggerProps()}>{selected}</Button.Root>
<BottomSheet.Root {...cb.getSheetProps()}>
  <Field.Root id={cb.ids.input} aria-label="Search">
    <Field.TextInput {...cb.getInputProps()} />
  </Field.Root>
  <OptionList.Root {...cb.getListboxProps()}>…</OptionList.Root>
</BottomSheet.Root>

자동

훅은 하나입니다. 트리는 표시 방식에 따라 바뀝니다. 고정된 화면이 필요하면 presentation을 강제하세요.

presentation: popover (popover)

React
Redux
Svelte

비동기

loadOptions(query, signal)이 옵션을 불러옵니다. 새 쿼리는 이전 컨트롤러를 중단하고 오래된 결과를 버립니다. 훅은 loading이 true인 동안 이전 목록을 유지합니다. 디바운스, 로딩, 다시 시도 외형은 레시피에 둡니다. 훅은 디바운스하지 않습니다.

fail을 입력하면 오류 행을 볼 수 있습니다.

빈 결과와 생성

일치하는 항목이 없으면 상태 행을 렌더링합니다. allowCreate는 쿼리와 대소문자 구분 없이 일치하는 레이블이 없을 때 같은 OptionList에 Create "{query}" 옵션 하나를 더합니다. 클릭하거나 Enter를 누르면 확정됩니다. 단일 선택은 선택하고 닫습니다. 다중 선택은 칩을 더하고 열린 채로 둡니다. onCreate는 정규화된 값을 반환할 수 있으며, 목록이 선택된 상태로 보여 줄 수 있도록 새 옵션을 options에 추가해야 합니다.

todae
tokens
todae
tokens
todae
tokens

일치 항목 강조

useHighlight는 옵션의 마크업을 바꾸지 않고 각 옵션에서 query를 표시합니다. 훅의 query(입력에 선택한 레이블이 보이는 동안은 비어 있음)를 넘기고, ref를 목록에 두고, ::highlight(search)로 그 이름에 스타일을 주세요.

const found = useHighlight({ name: 'search', query: cb.query })

<OptionList.Root ref={found.ref} {...cb.getListboxProps()}>…</OptionList.Root>
San Francisco
San Jose
Santa Clara

조합 규칙

  • 형제 Label + Field.Root id. Field.Label은 없습니다.
  • getAnchorProps 옆에 usePopover의 getTriggerProps를 펼치지 마세요. 이중 연결은 QA 실패입니다.
  • OptionList가 오버레이 목록입니다. 같은 값에 Radio나 Checkbox를 함께 렌더링하지 마세요.
  • combobox를 Field에 내장하지 마세요. Combobox.* 외형을 export하지 마세요.
  • 시트 트리거는 aria-haspopup="dialog"를 씁니다. 팝오버의 Field 외형에는 aria-haspopup이 없습니다.

훅

언제 쓰는지와 props는 useCombobox와 useSheetPresentation에 있습니다.