List
List 컴포넌트는 정보를 구조화된 목록 형태로 표시하는 데 사용됩니다.
import { List, ListDivider, ListItem } from "@/registry/ui/list";
import {
IconILowercaseSerifCircleLine,
IconPersonCircleLine,
} from "@karrotmarket/react-monochrome-icon";
import { Icon } from "@seed-design/react";
export default function ListPreview() {
return (
<List width="full">
<ListItem title="기본 리스트 아이템" />
<ListDivider />
<ListItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="아이콘이 있는 리스트 아이템"
detail="부가 정보가 포함된 설명"
suffix={<Icon svg={<IconILowercaseSerifCircleLine />} />}
/>
</List>
);
}
Installation
npx @seed-design/cli@latest add list
Props
List
VStackProps
와 동일합니다.
ListItem
Prop | Type | Default |
---|---|---|
title? | ReactNode | - |
detail? | ReactNode | - |
prefix? | ReactNode | - |
suffix? | ReactNode | - |
alignItems? | "flex-start" | "flex-end" | "center" | "stretch" | "flexStart" | "flexEnd" | - |
highlighted? | boolean | false |
ListButtonItem
Prop | Type | Default |
---|---|---|
title? | ReactNode | - |
detail? | ReactNode | - |
prefix? | ReactNode | - |
suffix? | ReactNode | - |
rootRef? | Ref<HTMLLIElement> | - |
alignItems? | "flex-start" | "flex-end" | "center" | "stretch" | "flexStart" | "flexEnd" | - |
highlighted? | boolean | false |
ListLinkItem
Prop | Type | Default |
---|---|---|
title? | ReactNode | - |
detail? | ReactNode | - |
prefix? | ReactNode | - |
suffix? | ReactNode | - |
rootRef? | Ref<HTMLLIElement> | - |
alignItems? | "flex-start" | "flex-end" | "center" | "stretch" | "flexStart" | "flexEnd" | - |
highlighted? | boolean | false |
ListCheckItem
Prop | Type | Default |
---|---|---|
title? | ReactNode | - |
detail? | ReactNode | - |
prefix? | ReactNode | - |
suffix? | ReactNode | - |
inputProps? | InputHTMLAttributes<HTMLInputElement> | - |
rootRef? | Ref<HTMLLabelElement> | - |
defaultChecked? | boolean | - |
alignItems? | "flex-start" | "flex-end" | "center" | "stretch" | "flexStart" | "flexEnd" | - |
highlighted? | boolean | false |
disabled? | boolean | - |
invalid? | boolean | - |
required? | boolean | - |
checked? | boolean | - |
onCheckedChange? | ((checked: boolean) => void) | - |
indeterminate? | boolean | - |
ListRadioItem
Prop | Type | Default |
---|---|---|
title? | ReactNode | - |
detail? | ReactNode | - |
prefix? | ReactNode | - |
suffix? | ReactNode | - |
inputProps? | InputHTMLAttributes<HTMLInputElement> | - |
rootRef? | Ref<HTMLLabelElement> | - |
alignItems? | "flex-start" | "flex-end" | "center" | "stretch" | "flexStart" | "flexEnd" | - |
highlighted? | boolean | false |
value? | string | - |
disabled? | boolean | - |
invalid? | boolean | - |
ListDivider
DividerProps
와 동일하나, 기본적으로 <li aria-hidden></li>
로 렌더링됩니다. List
를 fieldset
등으로 바꿔 사용하는 경우 ListDivider
역시 적절한 태그로 교체해주세요.
Prop | Type | Default |
---|---|---|
as? | "hr" | "div" | "li" | "li" |
aria-hidden? | Booleanish | true |
color? | ScopedColorStroke | ScopedColorPalette | (string & {}) | "stroke.neutralMuted" |
thickness? | 0 | (string & {}) | 1 | 1 |
orientation? | "horizontal" | "vertical" | "horizontal" |
Examples
리스트 항목에 Prefix 또는 Suffix 추가하기
"use client";
import {
IconArrowUpBracketDownLine,
IconILowercaseSerifCircleLine,
} from "@karrotmarket/react-monochrome-icon";
import { Icon } from "@seed-design/react";
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import { Avatar } from "seed-design/ui/avatar";
import { IdentityPlaceholder } from "seed-design/ui/identity-placeholder";
import { List, ListDivider, ListItem } from "seed-design/ui/list";
import { ToggleButton } from "seed-design/ui/toggle-button";
export default function ListAffixes() {
const [isToggleButtonPressed, setIsToggleButtonPressed] = useState(false);
return (
<List width="full" py="x4">
<ListItem
prefix={
<Avatar
size="48"
src="https://avatars.githubusercontent.com/u/54893898?v=4"
fallback={<IdentityPlaceholder />}
/>
}
title="Prefix에 Avatar 넣기"
detail="Amet elit ullamco magna."
/>
<ListDivider />
<ListItem
prefix={<Avatar size="48" fallback={<IdentityPlaceholder />} />}
title="Prefix에 Avatar 넣고 상단으로 정렬하기. 일반적으로 `title`이 길어질 때 사용합니다. Veniam elit velit esse ea incididunt sunt sit aute."
detail="Et proident sit ullamco ut voluptate."
alignItems="flex-start"
/>
<ListDivider />
<ListItem
title="Prefix에 아이콘 넣기"
detail="Deserunt nulla elit est."
prefix={<Icon svg={<IconILowercaseSerifCircleLine />} />}
/>
<ListDivider />
<ListItem
title="Suffix에 Action Button 넣기"
detail="Veniam non est non ut consequat."
suffix={<ActionButton size="xsmall">액션 버튼</ActionButton>}
/>
<ListDivider />
<ListItem
title="Suffix에 Action Button (Ghost) 넣기"
detail="Deserunt nulla elit est."
suffix={
<ActionButton size="small" variant="ghost" layout="iconOnly" aria-label="공유">
<Icon svg={<IconArrowUpBracketDownLine />} />
</ActionButton>
}
/>
<ListDivider />
<ListItem
title="Suffix에 Toggle Button 넣기"
detail="Sit eu incididunt aute ea elit ex."
suffix={
<ToggleButton
size="xsmall"
pressed={isToggleButtonPressed}
onPressedChange={setIsToggleButtonPressed}
>
{isToggleButtonPressed ? "선택됨" : "토글 버튼"}
</ToggleButton>
}
/>
</List>
);
}
리스트 항목 전체를 클릭 가능하게 만들기
ListButtonItem
또는 ListLinkItem
를 사용해서 리스트 항목 전체를 클릭 가능하도록 만들 수 있습니다.
"use client";
import {
IconArrowUpRightLine,
IconCheckmarkFill,
IconChevronRightLine,
IconPlusFill,
IconSquare2StackedFill,
} from "@karrotmarket/react-monochrome-icon";
import { PrefixIcon, Icon } from "@seed-design/react";
import { useCallback, useState } from "react";
import { List, ListDivider, ListItem, ListButtonItem, ListLinkItem } from "seed-design/ui/list";
import { ActionButton } from "seed-design/ui/action-button";
import { ToggleButton } from "seed-design/ui/toggle-button";
const href = "https://www.daangn.com";
export default function ListClickable() {
const [isSubscribed, setIsSubscribed] = useState(false);
const [isCopied, setIsCopied] = useState(false);
const onCopyClick = useCallback(() => {
navigator.clipboard.writeText(href);
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
}, []);
return (
<List width="full">
<ListItem
title="ListItem은 클릭할 수 없어요. 눌러보세요."
detail="우측의 Action Button만 클릭할 수 있어요"
suffix={
<ActionButton size="xsmall" onClick={() => alert("편집 클릭됨")}>
편집
</ActionButton>
}
/>
<ListDivider />
<ListButtonItem
title="ListButtonItem은 클릭할 수 있어요. 눌러보세요."
detail="리스트 항목 전체와 우측의 Toggle Button 각각을 클릭할 수 있어요"
onClick={() => alert("리스트 아이템 클릭됨")}
suffix={
<>
<ToggleButton size="xsmall" pressed={isSubscribed} onPressedChange={setIsSubscribed}>
<PrefixIcon svg={isSubscribed ? <IconCheckmarkFill /> : <IconPlusFill />} />
{isSubscribed ? "모아보는 중" : "모아보기"}
</ToggleButton>
<Icon svg={<IconChevronRightLine />} />
</>
}
/>
<ListDivider />
<ListLinkItem
title="ListLinkItem도 클릭할 수 있어요. 눌러보세요."
detail="리스트 항목 전체와 우측의 Action Button 각각을 클릭할 수 있어요"
suffix={
<>
<ActionButton size="xsmall" onClick={onCopyClick}>
<PrefixIcon svg={isCopied ? <IconCheckmarkFill /> : <IconSquare2StackedFill />} />
{isCopied ? "복사됨" : "URL 복사"}
</ActionButton>
<Icon svg={<IconArrowUpRightLine />} />
</>
}
href={href}
target="_blank"
rel="noreferrer"
/>
</List>
);
}
리스트 항목 전체를 폼 요소로 만들기
ListCheckItem
와 ListRadioItem
를 사용해서 리스트 항목 전체를 폼 요소로 만들 수 있습니다. Checkmark 또는 RadioMark와 같은 컨트롤 요소를 prefix
나 suffix
영역에 넣어 사용합니다.
import { Badge, HStack } from "@seed-design/react";
import { List, ListDivider, ListCheckItem } from "seed-design/ui/list";
import { Checkmark } from "seed-design/ui/checkbox";
export default function ListCheckbox() {
return (
<List as="fieldset" width="full">
<ListCheckItem
title={
<HStack gap="x1_5">
<span>알림 수신 동의</span>
<Badge variant="weak">권장</Badge>
</HStack>
}
detail="푸시 알림을 받으시겠습니까?"
suffix={<Checkmark size="large" />}
defaultChecked
/>
<ListDivider as="div" />
<ListCheckItem
prefix={<Checkmark size="large" />}
title="마케팅 정보 수신 동의"
detail="마케팅 정보를 받으시겠습니까?"
defaultChecked
/>
<ListDivider as="div" />
<ListCheckItem prefix={<Checkmark size="large" variant="ghost" />} title="Ghost Variant" />
</List>
);
}
import { RadioGroup } from "@seed-design/react";
import { List, ListDivider, ListRadioItem } from "seed-design/ui/list";
import { RadioMark } from "seed-design/ui/radio-group";
export default function ListRadio() {
return (
<List width="full" asChild>
<RadioGroup.Root defaultValue="option1" aria-label="옵션 선택">
<ListRadioItem
value="option1"
title="옵션 1"
detail="첫 번째 선택지"
suffix={<RadioMark size="large" />}
/>
<ListDivider as="div" />
<ListRadioItem
prefix={<RadioMark size="large" />}
value="option2"
title="옵션 2"
detail="두 번째 선택지"
/>
<ListDivider as="div" />
<ListRadioItem
prefix={<RadioMark size="large" />}
value="option3"
title="옵션 3"
detail="세 번째 선택지"
/>
</RadioGroup.Root>
</List>
);
}
접근 가능하게 만들기
List
는 기본적으로 <ul>
입니다. ListCheckItem
와 ListRadioItem
를 사용하는 경우 List
에 적절한 role을 부여해야 합니다.
<List as="fieldset">
<ListCheckItem
suffix={<Checkmark size="large" />}
title="알림 수신 동의"
detail="푸시 알림을 받으시겠습니까?"
/>
<ListCheckItem
suffix={<Checkmark size="large" />}
title="마케팅 정보 수신 동의"
detail="마케팅 정보를 받으시겠습니까?"
/>
</List>
<List asChild>
<RadioGroup.Root defaultValue="짜장" aria-label="점심 메뉴"> {/* <div role="radiogroup"> */}
<ListRadioItem
suffix={<RadioMark size="large" />}
value="짜장"
title="짜장"
/>
<ListRadioItem
suffix={<RadioMark size="large" />}
value="짬뽕"
title="짬뽕"
/>
</RadioGroup.Root>
</List>
Disabled
import {
IconChevronRightLine,
IconPersonCircleLine,
IconSlashCircleLine,
} from "@karrotmarket/react-monochrome-icon";
import { Divider, Icon, RadioGroup, VStack } from "@seed-design/react";
import { List, ListButtonItem, ListCheckItem, ListRadioItem } from "seed-design/ui/list";
import { Checkmark } from "seed-design/ui/checkbox";
import { RadioMark } from "seed-design/ui/radio-group";
export default function ListDisabled() {
return (
<VStack width="full">
<List>
<ListButtonItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="활성화된 ListButtonItem"
suffix={<Icon svg={<IconChevronRightLine />} />}
/>
</List>
<List as="fieldset">
<ListCheckItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="활성화된 ListCheckItem"
suffix={<Checkmark size="large" />}
/>
</List>
<List asChild>
<RadioGroup.Root defaultValue="foo" aria-label="옵션 선택">
<ListRadioItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="활성화된 ListRadioItem"
suffix={<RadioMark size="large" />}
value="foo"
/>
</RadioGroup.Root>
</List>
<Divider />
<List>
<ListButtonItem
disabled
prefix={<Icon svg={<IconSlashCircleLine />} />}
title="비활성화된 ListButtonItem"
suffix={<Icon svg={<IconChevronRightLine />} />}
/>
</List>
<List as="fieldset">
<ListCheckItem
disabled
prefix={<Icon svg={<IconSlashCircleLine />} />}
title="비활성화된 ListCheckItem"
suffix={<Checkmark size="large" />}
/>
</List>
<List asChild>
<RadioGroup.Root defaultValue="foo" aria-label="옵션 선택">
<ListRadioItem
disabled
prefix={<Icon svg={<IconSlashCircleLine />} />}
title="비활성화된 ListRadioItem"
suffix={<RadioMark size="large" />}
value="foo"
/>
</RadioGroup.Root>
</List>
</VStack>
);
}
Variants
Highlighted
"use client";
import { IconPersonCircleLine } from "@karrotmarket/react-monochrome-icon";
import { Box, Icon, VStack } from "@seed-design/react";
import { useState } from "react";
import { List, ListDivider, ListItem } from "seed-design/ui/list";
import { Switch } from "seed-design/ui/switch";
export default function ListHighlighted() {
const [highlighted, setHighlighted] = useState(true);
return (
<VStack width="full" gap="x4">
<List>
<ListItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="하이라이트되지 않은 항목"
detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt."
/>
<ListDivider />
<ListItem
highlighted
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="하이라이트된 항목"
detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt."
/>
</List>
<List>
<ListItem
prefix={<Icon svg={<IconPersonCircleLine />} />}
title="하이라이트"
highlighted={highlighted}
/>
</List>
<Box alignSelf="center">
<Switch
size="24"
label="highlight"
checked={highlighted}
onCheckedChange={setHighlighted}
/>
</Box>
</VStack>
);
}
Snippet 커스텀 가이드
구조와 영역별 역할
@seed-design/react 패키지에서 제공하는 List
컴포넌트는 다음과 같은 구조로 구성됩니다.
List.Root
└── List.Item
├── List.Prefix (선택사항)
├── List.Content
│ ├── List.Title
│ └── List.Detail (선택사항)
└── List.Suffix (선택사항)
import { List, Icon } from "@seed-design/react";
<List.Root>
<List.Item>
<List.Prefix>
<Icon svg={<IconPersonCircleLine />} />
</List.Prefix>
<List.Content>
<List.Title>내 프로필</List.Title>
<List.Detail>다른 사람들에게 보이는 내 정보를 관리합니다.</List.Detail>
</List.Content>
<List.Suffix>
<Icon svg={<IconArrowRightLine />} />
</List.Suffix>
</List.Item>
<List.Item>
{/* ... */}
</List.Item>
</List.Root>
List.Root
: 모든 리스트 항목을 감싸는 컨테이너 역할List.Item
: 개별 리스트 항목. 클릭 가능한 영역을 정의List.Prefix
: 아이콘, Avatar, Checkmark 등을 표시할 수 있는 시작 영역List.Content
: 주요 콘텐츠가 들어가는 중앙 영역List.Title
: 리스트 항목의 제목List.Detail
: 부가 설명이나 세부 정보
List.Suffix
: 아이콘, Action Button, Toggle Button 등을 표시할 수 있는 끝 영역
asChild
prop으로 적절한 시맨틱 요소와 조합하기
Composition
asChild
prop에 대해 자세히 알아봅니다.
List.Content
에 asChild
prop을 사용하는 경우
리스트 항목 전체 영역을 클릭 가능한 버튼으로 만드는 경우 활용할 수 있는 패턴입니다. 이 경우 List.Item
에 asChild
prop을 사용하지 않도록 유의하세요. List.Prefix
또는 List.Suffix
에 버튼을 넣는 경우 button
이 중첩되는 등 유효하지 않은 HTML이 생성됩니다.
import { List as SeedList } from "@seed-design/react";
<SeedList.Item>
<SeedList.Content asChild>
<button type="button" onClick={() => alert("사용자 클릭됨")}>
<SeedList.Title>사용자</SeedList.Title>
</button>
</SeedList.Content>
<SeedList.Suffix>
<ActionButton
size="xsmall"
variant="brandSolid"
onClick={() => alert("보기 클릭됨")}
>
보기
</ActionButton>
</SeedList.Suffix>
</SeedList.Item>
Snippet으로 제공되는 ListButtonItem
및 ListLinkItem
는 이 패턴을 쉽게 구현할 수 있도록 돕습니다.
import { ListButtonItem } from "seed-design/ui/list";
<ListButtonItem
onClick={() => alert("사용자 클릭됨")}
title="사용자"
detail="항목 6개"
suffix={
<ActionButton
size="xsmall"
variant="brandSolid"
onClick={() => alert("보기 클릭됨")}
>
보기
</ActionButton>
}
/>
List.Item
에 asChild
prop을 사용하는 경우
리스트 항목 전체 영역을 label
로 만들고, List.Prefix
또는 List.Suffix
에 Checkmark 또는 RadioMark를 넣는 경우 활용할 수 있는 패턴입니다.
import { List as SeedList } from "@seed-design/react";
import { Checkbox } from "@seed-design/react/primitive";
<SeedList.Item asChild> {/* <label> */}
<Checkbox.Root defaultChecked>
<SeedList.Content>
<SeedList.Title>동의</SeedList.Title>
</SeedList.Content>
<SeedList.Suffix>
<Checkmark />
</SeedList.Suffix>
<Checkbox.HiddenInput />
</Checkbox.Root>
</SeedList.Item>
Snippet으로 제공되는 ListCheckItem
및 ListRadioItem
는 이 패턴을 쉽게 구현할 수 있도록 돕습니다.
import { ListCheckItem } from "seed-design/ui/list";
<ListCheckItem
defaultChecked
title="동의"
suffix={<Checkmark size="large" />}
/>
Last updated on