Skip to content

Combobox

직접 입력할 수도, 목록에서 고를 수도 있는 필드입니다. 입력한 글자는 목록을 걸러내고, 끄지 않는 한 그 자체로 값이 될 수도 있습니다.

tsx
import { Combobox } from 'neba';

<Combobox
  label="프레임워크"
  placeholder="검색하거나 직접 입력하세요"
  items={[
    { value: 'react', label: 'React' },
    { value: 'vue', label: 'Vue' }
  ]}
/>;

Props

Prop타입기본값설명
variant공통'solid' | 'outline' | 'text''outline'표면의 무게. TextField와 같은 셸이므로 폼 안에서 두 컨트롤이 구분되지 않습니다
size공통'xs' | 'sm' | 'md' | 'lg' | 'xl''md'높이와 타입 스케일. multiple에서는 칩이 줄바꿈하는 만큼 자라므로 최소 높이가 됩니다
color공통'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'의미론적 색 역할. 표면은 흰색이므로 가장자리·포커스 링·칩에 나타납니다
density공통'default' | 'compact''default'여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통0 | 1 | 2 | 30필드의 그림자 깊이. 팝업은 자기 그림자를 따로 가집니다
items * readonly ComboboxOption[]옵션 목록. { value, label?, disabled? } 배열이고, label은 ReactNode가 아니라 string입니다
multiplebooleanfalse값을 여러 개 들 수 있는지. 고른 값은 필드 안의 칩이 됩니다
valuestring | number | (string | number)[] | null선택된 값. multiple이면 배열입니다. onValueChange와 함께 제어 컴포넌트로 씁니다
defaultValuestring | number | (string | number)[] | null초기 선택 값
onValueChange(value) => void선택이 바뀔 때
onInputValueChange(inputValue: string) => void입력란의 글자가 바뀔 때. 값이 아니라 필터 질의입니다
allowCustombooleantrue목록에 없는 값을 확정할 수 있는지. 입력한 글자가 목록 맨 끝에 자기 행으로 제안됩니다 — 검색되는 select와 combobox를 가르는 지점입니다
customLabel(query: string) => ReactNodeAdd “…”그 행이 하는 말
clearablebooleanfalse필드를 비우는 ×. 한 번에 비워지는 필드는 실수로도 비워지는 필드입니다
emptyMessageReactNode'No matches'일치하는 것이 없고 값을 추가할 수도 없을 때 팝업이 하는 말
limitnumber-1한 번에 보여 줄 최대 행 수. -1은 전부
placeholderstring아무것도 입력하지 않았을 때 보이는 내용
labelReactNode컨트롤과 연결되는 라벨. Base UI Field가 묶어 줍니다
descriptionReactNode보조 설명
errorReactNode오류 메시지. 값이 있으면 invalid 상태도 함께 켜지고 색 계열이 danger로 넘어갑니다
invalidboolean!!error메시지 없이 invalid만 켭니다. 외부 폼 라이브러리가 유효성을 가질 때
startIconReactNode입력란 앞에 놓이는 내용
fullWidthbooleanfalse컨테이너 너비만큼 확장
removeLabel(label: string) => stringRemove …칩 제거 버튼의 접근성 이름. 칩의 라벨을 받습니다
clearLabelstring'Clear'× 버튼의 접근성 이름
namestring폼 제출 시의 필드 이름
readOnlybooleanfalse값은 보이지만 바꿀 수 없음. 색과 가장자리는 유지한 채 채도만 빠집니다
disabledbooleanfalse사용 불가. 색 계열을 버리고 중립 회색이 됩니다

옵션은 데이터입니다

Select와 같은 모양이고, 한 가지만 다릅니다.

ts
interface ComboboxOption {
  value: string | number;
  label?: string; // ReactNode가 아니라 string입니다
  disabled?: boolean;
}

여기서 labelstring인 것은 필터가 이 값을 상대로 매칭하고 입력란이 이 값을 그대로 써 넣기 때문입니다 — 엘리먼트로는 둘 다 할 수 없습니다. 값은 여전히 문자열과 숫자입니다. combobox는 폼 컨트롤이고, 그 값은 제출되는 것입니다.

예시

여러 개 고르기

고른 값은 필드 안에서 Chip이 되고, 입력란은 그 뒤로도 계속 필터 역할을 합니다. 필드가 한 번도 닫히지 않은 채로 태그 묶음이 만들어집니다. 입력란이 비어 있을 때 Backspace를 누르면 마지막 칩으로 손이 갑니다.

목록에 없는 값

이것이 검색되는 select와 combobox를 가르는 지점이고, 기본으로 켜져 있습니다.

입력한 글자는 포커스가 빠질 때 조용히 확정되는 대신, 목록 맨 끝에 자기 행으로 제안됩니다. 의도된 것입니다 — 쓰다 만 단어를 포커스가 떠나는 순간 값으로 만들어 버리는 필드는 데이터를 지어내는 필드입니다. 행으로 만들면 Enter도, 클릭도, 방향키도 다른 모든 행과 똑같은 방식으로 그곳에 닿고, 스크린 리더도 하나의 옵션으로 읽습니다.

타이핑하는 동안 첫 번째 일치 항목에 불이 들어오므로, 필요한 것은 여전히 키 하나입니다. 목록에 있는 것을 치면 Enter가 그것을 고르고, 없는 것을 치면 Enter가 그것을 더합니다.

값이 닫힌 집합인 필드라면 allowCustom={false}로 끄세요. 그때는 아무것도 찾지 못한 검색이 하는 말이 emptyMessage입니다.

Variant

TextField와 같은 세 가지 무게를, 같은 셸 위에 그립니다.

크기

필드 안의 칩은 컨트롤 사다리에서 한 칸 아래에 앉고, 남는 만큼이 필드의 상하 여백이 됩니다 — 그래서 한 줄짜리 combobox는 옆에 선 필드와 정확히 같은 높이입니다. multiple에서는 칩이 줄바꿈하는 만큼 필드가 자라며, 고정 높이를 갖지 않습니다.

상태

팝업

Select의 것과 동일합니다. combobox의 목록과 select의 목록은 같은 목록이기 때문입니다 — 3단계 그림자를 단 떠 있는 표면이고, <body> 끝으로 포털되며, CSS 리셋을 서브트리에 한정해 둔 호스트를 위해 positioner에 neba-portal이 걸려 있습니다.

접근성

  • 필터링과 그 콜레이터, 팝업의 위치 계산과 뒤집힘, combobox/listbox 연결, 목록과 칩을 가로지르는 방향키 이동, 폼 제출을 가능하게 하는 숨은 input은 모두 Base UI가 담당합니다.
  • label이 접근성 이름이 됩니다.
  • 비활성 옵션은 목록에 남은 채 aria-disabled로 보고됩니다 — 옵션은 존재하고, 다만 고를 수 없을 뿐입니다.
  • 칩의 제거 버튼 이름은 removeLabel이 짓고, 칩의 라벨을 받습니다. 그래서 버튼은 그냥 _Remove_가 아니라 _Remove documentation_이라고 말합니다.

Released under the MIT License