CoreMultiSelectPrim
컴파운드 패턴으로 구성된 Select 컴포넌트입니다. 다중 값에 대한 선택을 지원합니다. 선택된 값은 CoreTag 컴포넌트로 표시됩니다.
Usage
기본 사용법
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
<MultiSelectPrim.Root required width="240px">
<MultiSelectPrim.Label tooltip="도움말">Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value placeholder="옵션을 선택하세요" />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.HelperText>Helper Text</MultiSelectPrim.HelperText>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
<MultiSelectPrim.Item value="option1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 1"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 2"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option3" disabled>
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 3 (disabled)"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>제어된 컴포넌트 (Controlled Component)
import { useState } from 'react';
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
const [selected, setSelected] = useState<string[]>(['option1', 'option2']);
const handleValueChange = (value: string) => {
if (selected.includes(value)) {
setSelected(selected.filter((item) => item !== value));
} else {
setSelected([...selected, value]);
}
};
<MultiSelectPrim.Root
value={selected}
onValueChange={handleValueChange}
required
width="240px"
>
<MultiSelectPrim.Label>Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
<MultiSelectPrim.Item value="option1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 1"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 2"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>valueWrap을 사용한 여러 줄 표시
import { useState } from 'react';
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
const [selected, setSelected] = useState<string[]>(['option1', 'option2', 'option3', 'option4', 'option5']);
const handleValueChange = (value: string) => {
if (selected.includes(value)) {
setSelected(selected.filter((item) => item !== value));
} else {
setSelected([...selected, value]);
}
};
<MultiSelectPrim.Root
value={selected}
onValueChange={handleValueChange}
required
width="300px"
>
<MultiSelectPrim.Label>Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value valueWrap={true} />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
<MultiSelectPrim.Item value="option1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 1"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 2"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>그룹과 구분선 사용
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
import { CoreTooltip } from '@featuring-corp/components';
import { IconWarningFilled, IconErrorFilled } from '@featuring-corp/icons';
import { flex } from '@styles/utils';
import { vars } from '@styles/theme.css';
<MultiSelectPrim.Root required width="240px">
<MultiSelectPrim.Label tooltip="도움말">Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.HelperText>Helper Text</MultiSelectPrim.HelperText>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
<MultiSelectPrim.Group>
<MultiSelectPrim.GroupLabel>옵션 그룹 1</MultiSelectPrim.GroupLabel>
<MultiSelectPrim.Item value="option1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 1"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 2"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Group>
<MultiSelectPrim.Separator />
<MultiSelectPrim.Group>
<MultiSelectPrim.GroupLabel>옵션 그룹 2</MultiSelectPrim.GroupLabel>
<MultiSelectPrim.Item value="option3" label="Option 3">
{(selected) => (
<CoreCheckbox
checked={selected}
label={
<div className={flex({ gap: 'spacing-100' })}>
<CoreTooltip text="경고!">
<IconWarningFilled size="12px" fill={vars.semantic.color.support.warning[3]} />
</CoreTooltip>
Option 3
</div>
}
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option4" label="Option 4">
{(selected) => (
<CoreCheckbox
checked={selected}
label={
<div className={flex({ gap: 'spacing-100' })}>
<CoreTooltip text="에러!">
<IconErrorFilled size="12px" fill={vars.semantic.color.support.error[3]} />
</CoreTooltip>
Option 4
</div>
}
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Group>
<MultiSelectPrim.Separator />
<MultiSelectPrim.Item value="option5">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 5"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>커스텀 Value 표시
import { useState } from 'react';
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
import { CoreStatusBadge } from '@featuring-corp/components';
import { flex } from '@styles/utils';
const [selected, setSelected] = useState<string[]>(['option1', 'option2']);
const handleValueChange = (value: string) => {
if (selected.includes(value)) {
setSelected(selected.filter((item) => item !== value));
} else {
setSelected([...selected, value]);
}
};
const options = {
option1: { label: 'Option 1' },
option2: { label: 'Option 2' },
option3: { label: 'Option 3' },
};
<MultiSelectPrim.Root
value={selected}
onValueChange={handleValueChange}
required
width="240px"
>
<MultiSelectPrim.Label tooltip="도움말">Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value className={flex({ gap: 'spacing-100' })}>
{(selectedList) =>
selectedList.map(({ value, label }) => (
<CoreStatusBadge key={value} text={label} type="tint" />
))
}
</MultiSelectPrim.Value>
</MultiSelectPrim.Trigger>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
{Object.entries(options).map(([value, { label }]) => (
<MultiSelectPrim.Item key={value} value={value}>
{(selected) => (
<CoreCheckbox
checked={selected}
label={label}
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
))}
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>label prop을 사용한 아이템
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
<MultiSelectPrim.Root required width="240px">
<MultiSelectPrim.Label>Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content>
<MultiSelectPrim.Item value="option1" label="Option 1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="커스텀 표시 텍스트"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2" label="Option 2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="다른 커스텀 텍스트"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>상태와 크기 설정
import { useState } from 'react';
import { MultiSelectPrim } from '@featuring-corp/components';
import { CoreCheckbox } from '@featuring-corp/components';
const [selected, setSelected] = useState<string[]>(['option1']);
const handleValueChange = (value: string) => {
if (selected.includes(value)) {
setSelected(selected.filter((item) => item !== value));
} else {
setSelected([...selected, value]);
}
};
const getStatus = () => {
if (selected.includes('option4')) return 'error';
else if (selected.includes('option3')) return 'warning';
return 'none';
};
<MultiSelectPrim.Root
value={selected}
onValueChange={handleValueChange}
status={getStatus()}
size="lg"
width="300px"
required
disabled={false}
readonly={false}
>
<MultiSelectPrim.Label tooltip="도움말">Label</MultiSelectPrim.Label>
<MultiSelectPrim.Trigger>
<MultiSelectPrim.Value />
</MultiSelectPrim.Trigger>
<MultiSelectPrim.HelperText status={getStatus()}>
상태에 따른 메시지가 표시됩니다
</MultiSelectPrim.HelperText>
<MultiSelectPrim.Portal>
<MultiSelectPrim.Content height={200}>
<MultiSelectPrim.Item value="option1">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 1"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
<MultiSelectPrim.Item value="option2">
{(selected) => (
<CoreCheckbox
checked={selected}
label="Option 2"
onChange={() => {}}
/>
)}
</MultiSelectPrim.Item>
</MultiSelectPrim.Content>
</MultiSelectPrim.Portal>
</MultiSelectPrim.Root>Props
MultiSelectPrim.Root
가장 상위 컴포넌트로, Select 전체를 감싸는 역할을 합니다.
해당 컴포넌트를 통해 내부적으로 많은 기능을 컨트롤하기 때문에, 반드시 포함되어야하는 컴포넌트입니다.
Root 컴포넌트에 주입된 status, size, width, readonly, required, disabled props는 하위 컴포넌트에 모두 전달됩니다.
Prop
Type
MultiSelectPrim.Label
Select의 라벨을 표시합니다.
Prop
Type
MultiSelectPrim.Trigger
Select의 선택된 값을 표시합니다. button element로 되어 있습니다.
Prop
Type
MultiSelectPrim.Value
선택된 값을 표시합니다. Trigger 내부에서 사용합니다.
Prop
Type
MultiSelectPrim.HelperText
Select의 헬퍼 텍스트를 표시합니다.
Prop
Type
MultiSelectPrim.Portal
Select의 옵션 목록을 렌더링할 포탈입니다.
Select.SelectPortalProps 타입을 확장한 컴포넌트입니다.
MultiSelectPrim.Content
Select의 옵션 목록을 표시합니다.
Prop
Type
MultiSelectPrim.Item
옵션 목록 내부의 아이템입니다.
children에 작성한 내용이 선택 시에도 자동으로 적용됩니다.
Prop
Type
MultiSelectPrim.Group
옵션 목록 내부의 그룹입니다.
Select.SelectGroupProps 타입을 확장한 컴포넌트입니다.
MultiSelectPrim.GroupLabel
옵션 목록 내부의 그룹 라벨입니다.
Select.SelectLabelProps 타입을 확장한 컴포넌트입니다.
MultiSelectPrim.Separator
옵션 목록 내부의 구분선입니다.
Select.SelectSeparatorProps 타입을 확장한 컴포넌트입니다.
SelectStatus
Prop
Type
SelectSize
Prop
Type
CoreTooltipProps
CoreTooltip 컴포넌트의 props를 참조하세요.스타일
기본 스타일 속성
Label:
display:flexjustify-content:flex-startalign-items:centermargin-bottom:spacing-150(6px)typography:heading[1]color:text-2(기본),text-4(disabled)
Label Required:
margin-left:spacing-50(2px)
Label Icon:
margin-left:spacing-100(4px)
Select Trigger:
display:flexjustify-content:space-betweengap:spacing-200(8px)padding-x:spacing-250(10px)border-radius:radius-100border:1px solid border[1]background-color:background-1(기본),background-2(hover)color:text-1(기본),text-5(placeholder)cursor:pointerwidth:auto(기본값)
Select Value:
padding:2px 0(sm),4px 0(md),8px 0(lg)overflow:hidden(valueWrap: off)flex-wrap:nowrap(valueWrap: off),wrap(valueWrap: on)
Content Container:
background-color:background-1padding:spacing-200(8px)border-radius:radius-100elevation:elevation-8width:auto(기본값)height:auto(기본값)overflow:auto
Select Item Box:
position:relative
Select Item Shadow Dom:
position:absolutetop:0left:0width:0height:0overflow:hiddenopacity:0
Select Item:
display:flexgap:spacing-150(6px)border-radius:radius-100min-height:28pxcursor:pointer(disabled: off),not-allowed(disabled: on)
Helper Text:
display:flexgap:spacing-100(4px)margin-top:spacing-100(4px)typography:body[1]
Group Label:
padding-x:spacing-200(8px)padding-y:spacing-100(4px)color:text-3typography:body[1]
Separator:
margin-y:spacing-300(12px)margin-x:spacing-200(8px)border-top:1px solid border-default
Size Variants (Trigger)
sm:typography: body[1],min-height: 28px,height: max-contentmd:typography: body[2],min-height: 32px,height: max-contentlg:typography: body[2],min-height: 40px,height: max-content
Size Variants (Item)
sm:typography: body[1],padding-y: spacing-50(2px)md:typography: body[2],padding-y: spacing-100(4px)lg:typography: body[2],padding-y: spacing-200(8px)
Status Variants (Trigger)
none:border-color: border[1]warning:border-color: support-warning[3]error:border-color: support-error[3]
Helper Text Status Variants
none:color: text-3warning:color: support-warning-3error:color: support-error-3
Select Item States
- Disabled (on):
background-color: toggle-disabled-bg,color: toggle-disabled-text,cursor: not-allowed - Disabled (off):
div[data-highlighted] + &:background-color: primary[10],color: primary[100],cursor: pointer
States (상태별 토큰)
Select Trigger:
- Default:
background-color: background-1,border: 1px solid border[1],color: text-1 - Hover:
background-color: background-2,border-color: border-2 - Focus (not readonly, not disabled):
border-color: primary[50] - Data-state="open":
border-color: primary[50] - Readonly:
border-color: border[2],background-color: background[4],color: text[1],cursor: auto - Disabled:
border-color: border[2],background-color: background[4],color: text[4],cursor: not-allowed - Placeholder:
color: text[5]