Featuring Design System
Legacy

CoreMultiSelectPrim

레거시 compound MultiSelect 컴포넌트. 다중 값 선택을 지원하며 신규 프로젝트에서는 Select(multiple)로 전환을 권장합니다.

개요

마이그레이션 안내 — 이 컴포넌트는 레거시입니다. 신규 프로젝트에서는 Select의 multiple 모드를 사용하세요.

MultiSelectPrim은 Radix UI Select를 기반으로 다중 선택을 지원하는 compound 컴포넌트입니다.

  • 다중 값 관리valuestring[]이며 선택/해제를 직접 핸들링해야 합니다
  • CoreTag 기본 표시 — 선택된 값은 기본적으로 CoreTag 목록으로 Trigger에 렌더됩니다
  • 함수형 childrenItem에서 (selected: boolean) => ReactNode 패턴으로 CoreCheckbox 등과 조합 가능
  • 커스텀 ValueValuechildren을 함수 (selectedList) => ReactNode로 작성해 커스텀 렌더 지원
  • valueWrap — 선택 값이 많을 때 Trigger 높이를 늘려 줄바꿈으로 표시하는 옵션

Usage

기본 사용법

() => {
const [selected, setSelected] = React.useState([]);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="240px">
    <CoreMultiSelectPrim.Label tooltip="도움말">Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value placeholder="옵션을 선택하세요" />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.HelperText>Helper Text</CoreMultiSelectPrim.HelperText>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        <CoreMultiSelectPrim.Item value="option1">
          {(sel) => <CoreCheckbox checked={sel} label="Option 1" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
        <CoreMultiSelectPrim.Item value="option2">
          {(sel) => <CoreCheckbox checked={sel} label="Option 2" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
        <CoreMultiSelectPrim.Item value="option3" disabled>
          {(sel) => <CoreCheckbox checked={sel} label="Option 3 (disabled)" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

Controlled

() => {
const [selected, setSelected] = React.useState(['option1', 'option2']);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="240px">
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        <CoreMultiSelectPrim.Item value="option1">
          {(sel) => <CoreCheckbox checked={sel} label="Option 1" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
        <CoreMultiSelectPrim.Item value="option2">
          {(sel) => <CoreCheckbox checked={sel} label="Option 2" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

valueWrap으로 줄바꿈 표시

선택 항목이 많을 때 valueWrap을 켜면 Trigger가 높이를 늘려 모든 태그를 표시합니다.

() => {
const items = ['option1', 'option2', 'option3', 'option4', 'option5'];
const [selected, setSelected] = React.useState(items);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="300px">
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value valueWrap />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        {items.map((v) => (
          <CoreMultiSelectPrim.Item key={v} value={v}>
            {(sel) => <CoreCheckbox checked={sel} label={v} onChange={() => {}} />}
          </CoreMultiSelectPrim.Item>
        ))}
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

그룹과 구분선

() => {
const [selected, setSelected] = React.useState([]);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="240px">
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        <CoreMultiSelectPrim.Group>
          <CoreMultiSelectPrim.GroupLabel>그룹 1</CoreMultiSelectPrim.GroupLabel>
          <CoreMultiSelectPrim.Item value="option1">
            {(sel) => <CoreCheckbox checked={sel} label="Option 1" onChange={() => {}} />}
          </CoreMultiSelectPrim.Item>
          <CoreMultiSelectPrim.Item value="option2">
            {(sel) => <CoreCheckbox checked={sel} label="Option 2" onChange={() => {}} />}
          </CoreMultiSelectPrim.Item>
        </CoreMultiSelectPrim.Group>
        <CoreMultiSelectPrim.Separator />
        <CoreMultiSelectPrim.Group>
          <CoreMultiSelectPrim.GroupLabel>그룹 2</CoreMultiSelectPrim.GroupLabel>
          <CoreMultiSelectPrim.Item value="option3" label="Option 3">
            {(sel) => <CoreCheckbox checked={sel} label="Option 3" onChange={() => {}} />}
          </CoreMultiSelectPrim.Item>
        </CoreMultiSelectPrim.Group>
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

label prop으로 Trigger 표시 텍스트 분리

Itemchildren과 Trigger에 표시되는 텍스트를 다르게 하려면 label prop을 사용합니다.

() => {
const [selected, setSelected] = React.useState([]);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="240px">
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        <CoreMultiSelectPrim.Item value="option1" label="Option 1">
          {(sel) => <CoreCheckbox checked={sel} label="커스텀 표시 텍스트" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
        <CoreMultiSelectPrim.Item value="option2" label="Option 2">
          {(sel) => <CoreCheckbox checked={sel} label="다른 커스텀 텍스트" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

커스텀 Value 렌더

Valuechildren(selectedList) => ReactNode 함수로 작성하면 선택 목록 전체를 받아 자유롭게 렌더할 수 있습니다.

() => {
const options = { option1: 'Option 1', option2: 'Option 2', option3: 'Option 3' };
const [selected, setSelected] = React.useState([]);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root value={selected} onValueChange={toggle} required width="240px">
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value>
        {(list) => (
          <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>
            {list.map(({ value, label }) => (
              <CoreStatusBadge key={value} text={label} type="tint" />
            ))}
          </div>
        )}
      </CoreMultiSelectPrim.Value>
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content>
        {Object.entries(options).map(([value, label]) => (
          <CoreMultiSelectPrim.Item key={value} value={value} label={label}>
            {(sel) => <CoreCheckbox checked={sel} label={label} onChange={() => {}} />}
          </CoreMultiSelectPrim.Item>
        ))}
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

상태(status)와 크기(size)

() => {
const [selected, setSelected] = React.useState([]);
const toggle = (value) =>
  setSelected((prev) =>
    prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
  );
return (
  <CoreMultiSelectPrim.Root
    value={selected}
    onValueChange={toggle}
    status="error"
    size="lg"
    width="300px"
    required
  >
    <CoreMultiSelectPrim.Label>Label</CoreMultiSelectPrim.Label>
    <CoreMultiSelectPrim.Trigger>
      <CoreMultiSelectPrim.Value />
    </CoreMultiSelectPrim.Trigger>
    <CoreMultiSelectPrim.HelperText status="error">에러 메시지가 표시됩니다</CoreMultiSelectPrim.HelperText>
    <CoreMultiSelectPrim.Portal>
      <CoreMultiSelectPrim.Content height={200}>
        <CoreMultiSelectPrim.Item value="option1">
          {(sel) => <CoreCheckbox checked={sel} label="Option 1" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
        <CoreMultiSelectPrim.Item value="option2">
          {(sel) => <CoreCheckbox checked={sel} label="Option 2" onChange={() => {}} />}
        </CoreMultiSelectPrim.Item>
      </CoreMultiSelectPrim.Content>
    </CoreMultiSelectPrim.Portal>
  </CoreMultiSelectPrim.Root>
);
}

Props

CoreMultiSelectPrim.Root

Root에 설정한 status / size / width / disabled / readonly / required가 모든 하위 서브 컴포넌트에 자동 전달됩니다.

Prop

Type

CoreMultiSelectPrim.Label

Prop

Type

CoreMultiSelectPrim.Trigger

선택된 값 목록을 보여주는 <button> 요소입니다.

Prop

Type

CoreMultiSelectPrim.Value

Trigger 내부에서 선택된 값 목록을 표시합니다.

Prop

Type

CoreMultiSelectPrim.HelperText

Prop

Type

CoreMultiSelectPrim.Portal

Select.SelectPortalProps를 그대로 수용합니다.

CoreMultiSelectPrim.Content

Prop

Type

CoreMultiSelectPrim.Item

Prop

Type

CoreMultiSelectPrim.Group

Select.SelectGroupProps를 수용합니다.

CoreMultiSelectPrim.GroupLabel

Select.SelectLabelProps를 수용합니다.

CoreMultiSelectPrim.Separator

Select.SelectSeparatorProps를 수용합니다.

CoreTooltipProps

CoreTooltip 컴포넌트의 props를 참조하세요.

스타일

SelectPrim과 동일한 디자인 토큰을 공유합니다. CoreSelectPrim 스타일을 참조하세요.

Value 추가 스타일

valueWrapflex-wrapoverflow
falsenowraphidden
truewrap없음

마이그레이션 가이드

신규 Select 컴포넌트의 multiple 모드로 전환합니다.

CoreMultiSelectPrimSelect (신규)
CoreMultiSelectPrim.RootSelect.Root multiple
CoreMultiSelectPrim.Trigger + ValueSelect.Trigger + Select.Value
CoreMultiSelectPrim.Portal + ContentSelect.Popup
CoreMultiSelectPrim.Item (함수형 children)Select.Item
onValueChange: (value: string) => voidonValueChange: (values: string[]) => void