Select Box
명확한 테두리를 가진 컨테이너를 활용하여, 정의된 목록 중 하나 이상의 옵션을 선택하는 UI 요소입니다.
import "./styles";
import { root } from "@lynx-js/react";
import { HStack, VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
<page className={seedClassName}>
<HStack className="select-box-preview" gap="x6" align="flex-start">
<VStack className="select-box-preview__column">
<CheckSelectBoxGroup accessibility-label="Fruit">
<CheckSelectBox label="Apple" defaultChecked suffix={<CheckSelectBoxCheckmark />} />
<CheckSelectBox
label="Melon"
description="Elit cupidatat dolore fugiat enim veniam culpa."
suffix={<CheckSelectBoxCheckmark />}
/>
<CheckSelectBox label="Mango" suffix={<CheckSelectBoxCheckmark />} />
</CheckSelectBoxGroup>
</VStack>
<VStack className="select-box-preview__column">
<RadioSelectBoxRoot defaultValue="apple" accessibility-label="Fruit">
<RadioSelectBoxItem value="apple" label="Apple" suffix={<RadioSelectBoxRadiomark />} />
<RadioSelectBoxItem
value="melon"
label="Melon"
description="Elit cupidatat dolore fugiat enim veniam culpa."
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem value="mango" label="Mango" suffix={<RadioSelectBoxRadiomark />} />
</RadioSelectBoxRoot>
</VStack>
</HStack>
</page>
);
}
root.render(<Root />);문서 미리보기에서는 아이콘 색상이 적용되지 않아요. 아이콘의 실제 색상은 QR 코드 탭에서 Lynx Explorer를 실행해 확인할 수 있어요.
Installation
npx @seed-design/cli@latest add ui:select-boxpnpm dlx @seed-design/cli@latest add ui:select-boxyarn dlx @seed-design/cli@latest add ui:select-boxbun x @seed-design/cli@latest add ui:select-box의존성 설치
npm install @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactyarn add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactpnpm add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactbun add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react아래 코드를 복사 후 붙여넣고 사용하세요
/**
* @file ui:select-box
* @requires @seed-design/lynx-react@>=0.6.0 <1.0.0
* @requires @seed-design/lynx-css@>=0.10.0 <1.0.0
* @requires @karrotmarket/lynx-monochrome-icon@>=1.20.0 <2.0.0
**/
import IconCheckmarkFatFill from "@karrotmarket/lynx-monochrome-icon/IconCheckmarkFatFill";
import * as React from "@lynx-js/react";
import {
CheckSelectBox as SeedCheckSelectBox,
PrefixIcon,
RadioGroup as SeedRadioGroup,
RadioSelectBox as SeedRadioSelectBox,
} from "@seed-design/lynx-react";
import type { LynxIconElementProps } from "@seed-design/lynx-react";
export interface RadioSelectBoxRootProps
extends Omit<SeedRadioGroup.RootProps, "children" | "className" | "style">,
SeedRadioSelectBox.GroupProps {}
/**
* @see https://seed-design.io/lynx/components/select-box
*/
export const RadioSelectBoxRoot = React.forwardRef<unknown, RadioSelectBoxRootProps>(
({ children, columns = 1, className, style, ...rootProps }, ref) => {
return (
<SeedRadioGroup.Root ref={ref} {...rootProps}>
<SeedRadioSelectBox.Group columns={columns} className={className} style={style}>
{children}
</SeedRadioSelectBox.Group>
</SeedRadioGroup.Root>
);
},
);
RadioSelectBoxRoot.displayName = "RadioSelectBoxRoot";
export interface RadioSelectBoxItemProps extends Omit<SeedRadioSelectBox.ItemProps, "children"> {
label: React.ReactNode;
description?: React.ReactNode;
prefixIcon?: React.ReactElement<LynxIconElementProps>;
suffix?: React.ReactNode;
footer?: React.ReactNode;
}
export const RadioSelectBoxItem = React.forwardRef<unknown, RadioSelectBoxItemProps>(
(
{
label,
description,
prefixIcon,
suffix,
footer,
"accessibility-label": accessibilityLabel,
...otherProps
},
ref,
) => {
return (
<SeedRadioSelectBox.Item
ref={ref}
accessibility-label={accessibilityLabel ?? (typeof label === "string" ? label : undefined)}
{...otherProps}
>
<SeedRadioSelectBox.Trigger>
<SeedRadioSelectBox.Content>
{prefixIcon ? <PrefixIcon icon={prefixIcon} /> : null}
<SeedRadioSelectBox.Body>
<SeedRadioSelectBox.Label>{label}</SeedRadioSelectBox.Label>
{description != null ? (
<SeedRadioSelectBox.Description>{description}</SeedRadioSelectBox.Description>
) : null}
</SeedRadioSelectBox.Body>
</SeedRadioSelectBox.Content>
{suffix}
</SeedRadioSelectBox.Trigger>
{footer != null ? <SeedRadioSelectBox.Footer>{footer}</SeedRadioSelectBox.Footer> : null}
</SeedRadioSelectBox.Item>
);
},
);
RadioSelectBoxItem.displayName = "RadioSelectBoxItem";
export interface RadioSelectBoxRadiomarkProps extends SeedRadioGroup.ItemControlProps {}
export const RadioSelectBoxRadiomark = React.forwardRef<unknown, RadioSelectBoxRadiomarkProps>(
(props, ref) => {
return (
<SeedRadioGroup.ItemControl ref={ref} tone="neutral" {...props}>
<SeedRadioGroup.ItemIndicator />
</SeedRadioGroup.ItemControl>
);
},
);
RadioSelectBoxRadiomark.displayName = "RadioSelectBoxRadiomark";
export interface CheckSelectBoxGroupProps extends SeedCheckSelectBox.GroupProps {}
export const CheckSelectBoxGroup = SeedCheckSelectBox.Group;
export interface CheckSelectBoxProps extends Omit<SeedCheckSelectBox.RootProps, "children"> {
label: React.ReactNode;
description?: React.ReactNode;
prefixIcon?: React.ReactElement<LynxIconElementProps>;
suffix?: React.ReactNode;
footer?: React.ReactNode;
}
/**
* @see https://seed-design.io/lynx/components/select-box
*/
export const CheckSelectBox = React.forwardRef<unknown, CheckSelectBoxProps>(
(
{
label,
description,
prefixIcon,
suffix,
footer,
"accessibility-label": accessibilityLabel,
...otherProps
},
ref,
) => {
return (
<SeedCheckSelectBox.Root
ref={ref}
accessibility-label={accessibilityLabel ?? (typeof label === "string" ? label : undefined)}
{...otherProps}
>
<SeedCheckSelectBox.Trigger>
<SeedCheckSelectBox.Content>
{prefixIcon ? <PrefixIcon icon={prefixIcon} /> : null}
<SeedCheckSelectBox.Body>
<SeedCheckSelectBox.Label>{label}</SeedCheckSelectBox.Label>
{description != null ? (
<SeedCheckSelectBox.Description>{description}</SeedCheckSelectBox.Description>
) : null}
</SeedCheckSelectBox.Body>
</SeedCheckSelectBox.Content>
{suffix}
</SeedCheckSelectBox.Trigger>
{footer != null ? <SeedCheckSelectBox.Footer>{footer}</SeedCheckSelectBox.Footer> : null}
</SeedCheckSelectBox.Root>
);
},
);
CheckSelectBox.displayName = "CheckSelectBox";
export interface CheckSelectBoxCheckmarkProps extends SeedCheckSelectBox.CheckmarkControlProps {}
export const CheckSelectBoxCheckmark = React.forwardRef<unknown, CheckSelectBoxCheckmarkProps>(
(props, ref) => {
return (
<SeedCheckSelectBox.CheckmarkControl ref={ref} {...props}>
<SeedCheckSelectBox.CheckmarkIcon icon={<IconCheckmarkFatFill />} />
</SeedCheckSelectBox.CheckmarkControl>
);
},
);
CheckSelectBoxCheckmark.displayName = "CheckSelectBoxCheckmark";
/**
* This file is a snippet from SEED Design, helping you get started quickly with @seed-design/* packages.
* You can extend this snippet however you want.
*/
Props
Check Select Box
CheckSelectBoxGroup
Prop
Type
style?CSSProperties | undefinedchildren?React.ReactNodeclassName?string | undefinedCheckSelectBox
Prop
Type
labelReact.ReactNodedescription?React.ReactNodeprefixIcon?React.ReactElement<LynxIconElementProps, string | React.JSXElementConstructor<any>> | undefinedsuffix?React.ReactNodefooter?React.ReactNodeclassName?string | undefinedchecked?boolean | undefineddefaultChecked?boolean | undefinedindeterminate?boolean | undefinedonCheckedChange?((checked: boolean) => void) | undefinedstyle?CSSProperties | undefinedCheckSelectBoxCheckmark
Prop
Type
style?CSSProperties | undefinedchildren?React.ReactNodeclassName?string | undefinedRadio Select Box
RadioSelectBoxRoot
Prop
Type
value?string | undefineddefaultValue?string | undefinedonValueChange?((value: string) => void) | undefinedstyle?CSSProperties | undefinedchildren?React.ReactNodeclassName?string | undefinedRadioSelectBoxItem
Prop
Type
labelReact.ReactNodedescription?React.ReactNodeprefixIcon?React.ReactElement<LynxIconElementProps, string | React.JSXElementConstructor<any>> | undefinedsuffix?React.ReactNodefooter?React.ReactNodeclassName?string | undefineddisabled?boolean | undefinedstyle?CSSProperties | undefinedvaluestringRadioSelectBoxRadiomark
Prop
Type
style?CSSProperties | undefinedchildren?React.ReactNodeclassName?string | undefinedExamples
Customizing Label
label prop에 여러 요소를 조합한 사용자 정의 콘텐츠를 전달할 수 있습니다.
import "./styles";
import { root } from "@lynx-js/react";
import { Badge, HStack, VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function CustomizedLabel() {
return (
<>
<text>Melon</text>
<Badge tone="brand" variant="solid">
New
</Badge>
</>
);
}
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
<page className={seedClassName}>
<HStack className="select-box-preview" gap="x8" align="flex-start">
<VStack className="select-box-preview__column">
<CheckSelectBoxGroup accessibility-label="Fruit">
<CheckSelectBox label="Apple" defaultChecked suffix={<CheckSelectBoxCheckmark />} />
<CheckSelectBox
accessibility-label="Melon New"
label={<CustomizedLabel />}
description="Elit cupidatat dolore fugiat enim veniam culpa."
suffix={<CheckSelectBoxCheckmark />}
/>
<CheckSelectBox
label="Mango"
description="Aliqua ad aute eiusmod eiusmod nulla adipisicing proident ullamco in."
suffix={<CheckSelectBoxCheckmark />}
/>
</CheckSelectBoxGroup>
</VStack>
<VStack className="select-box-preview__column">
<RadioSelectBoxRoot defaultValue="apple" accessibility-label="Fruit">
<RadioSelectBoxItem value="apple" label="Apple" suffix={<RadioSelectBoxRadiomark />} />
<RadioSelectBoxItem
value="melon"
accessibility-label="Melon New"
label={<CustomizedLabel />}
description="Elit cupidatat dolore fugiat enim veniam culpa."
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="mango"
label="Mango"
description="Aliqua ad aute eiusmod eiusmod nulla adipisicing proident ullamco in."
suffix={<RadioSelectBoxRadiomark />}
/>
</RadioSelectBoxRoot>
</VStack>
</HStack>
</page>
);
}
root.render(<Root />);Listening to Value Changes
CheckSelectBox는 onCheckedChange를 사용하여 체크박스의 선택 상태 변경을 감지할 수 있습니다.
RadioSelectBoxRoot는 onValueChange를 사용하여 라디오 버튼의 선택 값 변경을 감지할 수 있습니다.
import "./styles";
import { root, useState } from "@lynx-js/react";
import { HStack, VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
const [checkCount, setCheckCount] = useState(0);
const [checkLastValue, setCheckLastValue] = useState<boolean | null>(null);
const [radioCount, setRadioCount] = useState(0);
const [radioLastValue, setRadioLastValue] = useState<string | null>(null);
return (
<page className={seedClassName}>
<HStack className="select-box-preview" gap="x8" align="center">
<VStack className="select-box-preview__column" gap="x4" align="center">
<CheckSelectBoxGroup accessibility-label="Fruit">
<CheckSelectBox
label="Apple"
suffix={<CheckSelectBoxCheckmark />}
onCheckedChange={(checked) => {
setCheckCount((previous) => previous + 1);
setCheckLastValue(checked);
}}
/>
</CheckSelectBoxGroup>
<text className="select-box-preview__status">
onCheckedChange called: {checkCount} times, last value:{" "}
{checkLastValue === null ? "-" : JSON.stringify(checkLastValue)}
</text>
</VStack>
<VStack className="select-box-preview__column" gap="x4" align="center">
<RadioSelectBoxRoot
defaultValue="apple"
accessibility-label="Fruit"
onValueChange={(value) => {
setRadioCount((previous) => previous + 1);
setRadioLastValue(value);
}}
>
<RadioSelectBoxItem value="apple" label="Apple" suffix={<RadioSelectBoxRadiomark />} />
<RadioSelectBoxItem
value="banana"
label="Banana"
suffix={<RadioSelectBoxRadiomark />}
/>
</RadioSelectBoxRoot>
<text className="select-box-preview__status">
onValueChange called: {radioCount} times, last value: {radioLastValue ?? "-"}
</text>
</VStack>
</HStack>
</page>
);
}
root.render(<Root />);Grid Layout (Columns)
columns prop을 사용하여 여러 열로 배치할 수 있습니다. columns가 1보다 크면 하위 요소의 layout이 자동으로 "vertical"로 설정됩니다.
필요한 경우 layout prop을 직접 설정하여 개별 항목의 레이아웃을 오버라이드할 수 있습니다.
import "./styles";
import IconDiamond from "@karrotmarket/lynx-multicolor-icon/IconDiamond";
import IconIcecreamcone from "@karrotmarket/lynx-multicolor-icon/IconIcecreamcone";
import { root } from "@lynx-js/react";
import { VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
<page className={seedClassName}>
<VStack className="select-box-preview" gap="x8">
<CheckSelectBoxGroup columns={2} accessibility-label="Grid 레이아웃 예제">
<CheckSelectBox
prefixIcon={<IconIcecreamcone />}
label="옵션 1"
description="layout=vertical"
suffix={<CheckSelectBoxCheckmark />}
/>
<CheckSelectBox
prefixIcon={<IconIcecreamcone />}
label="옵션 2"
description="layout=vertical"
suffix={<CheckSelectBoxCheckmark />}
/>
<CheckSelectBox
prefixIcon={<IconIcecreamcone />}
defaultChecked
layout="horizontal"
label="layout=horizontal"
description="layout을 horizontal로 오버라이드"
suffix={<CheckSelectBoxCheckmark />}
/>
<CheckSelectBox
prefixIcon={<IconIcecreamcone />}
label="옵션 4"
description="layout=vertical"
suffix={<CheckSelectBoxCheckmark />}
/>
</CheckSelectBoxGroup>
<RadioSelectBoxRoot
columns={3}
defaultValue="option3"
accessibility-label="Grid 레이아웃 예제"
>
<RadioSelectBoxItem
value="option1"
prefixIcon={<IconDiamond />}
label="옵션 1"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="option2"
prefixIcon={<IconDiamond />}
label="옵션 2"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="option3"
prefixIcon={<IconDiamond />}
label="layout=horizontal"
description="layout을 horizontal로 오버라이드"
layout="horizontal"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="option4"
prefixIcon={<IconDiamond />}
label="옵션 4"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="option5"
prefixIcon={<IconDiamond />}
label="옵션 5"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="option6"
prefixIcon={<IconDiamond />}
label="옵션 6"
suffix={<RadioSelectBoxRadiomark />}
/>
</RadioSelectBoxRoot>
</VStack>
</page>
);
}
root.render(<Root />);With Suffix
suffix prop을 사용하여 체크마크, 라디오 마크, 또는 커스텀 요소를 표시할 수 있습니다. CheckSelectBoxCheckmark와 RadioSelectBoxRadiomark를 사용하거나, 아이콘이나 텍스트 등 자유로운 요소를 전달할 수 있습니다.
import "./styles";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import { root } from "@lynx-js/react";
import { HStack, VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
<page className={seedClassName}>
<HStack className="select-box-preview" gap="x8" align="flex-start">
<VStack className="select-box-preview__column">
<CheckSelectBoxGroup accessibility-label="Suffix 예제">
<CheckSelectBox label="체크마크" suffix={<CheckSelectBoxCheckmark />} />
<CheckSelectBox
label="텍스트 suffix"
suffix={<text className="select-box-preview__suffix">+1,000원</text>}
/>
<CheckSelectBox
label="suffix 없음"
description="Commodo aliquip fugiat aute irure."
prefixIcon={<IconPersonCircleLine />}
/>
</CheckSelectBoxGroup>
</VStack>
<VStack className="select-box-preview__column">
<RadioSelectBoxRoot defaultValue="radiomark" accessibility-label="Radiomark 예제">
<RadioSelectBoxItem
value="radiomark"
label="라디오 마크"
suffix={<RadioSelectBoxRadiomark />}
/>
<RadioSelectBoxItem
value="text"
label="텍스트 suffix"
description="Commodo aliquip fugiat aute irure."
suffix={<text className="select-box-preview__suffix">+1,000원</text>}
/>
<RadioSelectBoxItem
value="none"
label="suffix 없음"
prefixIcon={<IconPersonCircleLine />}
/>
</RadioSelectBoxRoot>
</VStack>
</HStack>
</page>
);
}
root.render(<Root />);Collapsible Footer
footer prop으로 추가 콘텐츠를 표시할 수 있습니다. footerVisibility prop으로 footer의 표시 조건을 제어할 수 있습니다.
"when-selected"(기본값): 항목이 선택되었을 때만 표시"when-not-selected": 항목이 선택되지 않았을 때만 표시"always": 항상 표시
import "./styles";
import { root } from "@lynx-js/react";
import { HStack, VStack, useSeedClassName } from "@seed-design/lynx-react";
import {
CheckSelectBox,
CheckSelectBoxCheckmark,
CheckSelectBoxGroup,
RadioSelectBoxItem,
RadioSelectBoxRadiomark,
RadioSelectBoxRoot,
} from "@/components/ui/select-box";
function Root() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
<page className={seedClassName}>
<HStack className="select-box-preview" gap="x8" align="flex-start">
<VStack className="select-box-preview__column">
<CheckSelectBoxGroup accessibility-label="Footer 예제">
<CheckSelectBox
label="선택 시에만 표시 (기본값)"
description="footerVisibility='when-selected'"
suffix={<CheckSelectBoxCheckmark />}
footer={<text className="select-box-preview__footer">선택되었을 때만 보입니다.</text>}
/>
<CheckSelectBox
label="항상 표시"
description="footerVisibility='always'"
suffix={<CheckSelectBoxCheckmark />}
footerVisibility="always"
footer={<text className="select-box-preview__footer">항상 보입니다.</text>}
/>
<CheckSelectBox
label="미선택 시에만 표시"
description="footerVisibility='when-not-selected'"
suffix={<CheckSelectBoxCheckmark />}
footerVisibility="when-not-selected"
footer={
<text className="select-box-preview__footer">선택되지 않았을 때만 보입니다.</text>
}
/>
</CheckSelectBoxGroup>
</VStack>
<VStack className="select-box-preview__column">
<RadioSelectBoxRoot defaultValue="when-selected" accessibility-label="Footer 예제">
<RadioSelectBoxItem
value="when-selected"
label="선택 시에만 표시 (기본값)"
description="footerVisibility='when-selected'"
suffix={<RadioSelectBoxRadiomark />}
footer={<text className="select-box-preview__footer">선택되었을 때만 보입니다.</text>}
/>
<RadioSelectBoxItem
value="always"
label="항상 표시"
description="footerVisibility='always'"
suffix={<RadioSelectBoxRadiomark />}
footerVisibility="always"
footer={<text className="select-box-preview__footer">항상 보입니다.</text>}
/>
<RadioSelectBoxItem
value="when-not-selected"
label="미선택 시에만 표시"
description="footerVisibility='when-not-selected'"
suffix={<RadioSelectBoxRadiomark />}
footerVisibility="when-not-selected"
footer={
<text className="select-box-preview__footer">선택되지 않았을 때만 보입니다.</text>
}
/>
</RadioSelectBoxRoot>
</VStack>
</HStack>
</page>
);
}
root.render(<Root />);웹 버전과의 차이
Lynx Select Box는 React 버전과 다음 차이가 있습니다.
- React Hook Form 미지원: Lynx에는 native form 제출 모델이 없어 React Hook Form 예제를 제공하지 않습니다.
- Field wrapper 없음: React Registry의 Fieldset과 RadioGroupField 연동, field label, description, error message 연결을 제공하지 않습니다.
- form 제출 없음: Hidden input과
inputProps,name,required,invalid를 제공하지 않습니다. - 키보드 포커스 없음: 브라우저의 키보드 focus 대신 네이티브 tap과 접근성 탐색을 사용합니다.
- 접근성: HTML ARIA 대신 Lynx의 접근성 역할과 선택 값을 사용합니다.
Last updated on