# List
URL: /lynx/components/list
Source: https://github.com/daangn/seed-design/blob/dev/docs/content/lynx/components/list.mdx
가로 행으로 구성된 콘텐츠를 표현하는 컴포넌트입니다.
사용 가능 버전: @seed-design/lynx-react@0.7.0, @seed-design/lynx-css@0.11.0
## Preview
```tsx
import "./styles";
import IconILowercaseSerifCircleLine from "@karrotmarket/lynx-monochrome-icon/IconILowercaseSerifCircleLine";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import { PrefixIcon, SuffixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { ListHeader } from "@/components/ui/list-header";
import { List, ListDivider, ListItem } from "@/components/ui/list";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
리스트 헤더
} />}
title="아이콘이 있는 리스트 아이템"
detail="부가 정보가 포함된 설명"
suffix={} />}
/>
);
}
```
## Installation
- npm: npx @seed-design/cli@latest add ui:list
- pnpm: pnpm dlx @seed-design/cli@latest add ui:list
- yarn: yarn dlx @seed-design/cli@latest add ui:list
- bun: bun x @seed-design/cli@latest add ui:list
## Usage
```tsx
import { ListHeader } from "@/components/ui/list-header";
import { List, ListDivider, ListItem } from "@/components/ui/list";
리스트 헤더
```
## Props
### `ListHeader`
### `List`
### List Items
Lynx에는 HTML anchor에 해당하는 기본 요소가 없습니다. 링크 이동은 `ListButtonItem`의 `bindtap`에서 앱 라우터를 호출해 처리합니다.
## Examples
### Using `ListHeader`
`ListHeader`는 `List` 밖에 위치합니다. Lynx `ListHeader`는 ``를 렌더링하므로 액션은 Header의 형제로 배치합니다.
```tsx
import "./styles";
import IconChevronRightLine from "@karrotmarket/lynx-monochrome-icon/IconChevronRightLine";
import IconLockLine from "@karrotmarket/lynx-monochrome-icon/IconLockLine";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import IconQuestionmarkCircleFill from "@karrotmarket/lynx-monochrome-icon/IconQuestionmarkCircleFill";
import { ActionButton, PrefixIcon, SuffixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListButtonItem, ListDivider } from "@/components/ui/list";
import { ListHeader } from "@/components/ui/list-header";
function AccountList() {
return (
} />}
suffix={} />}
/>
} />}
suffix={} />}
/>
);
}
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
variant="mediumWeak"
variant="boldSolid"
List Header with Action Button
} />
도움말
);
}
```
### Affixes (Prefix/Suffix)
```tsx
import "./styles";
import IconArrowUpBracketDownFill from "@karrotmarket/lynx-monochrome-icon/IconArrowUpBracketDownFill";
import IconILowercaseSerifCircleLine from "@karrotmarket/lynx-monochrome-icon/IconILowercaseSerifCircleLine";
import { useState } from "@lynx-js/react";
import { ActionButton, Icon, PrefixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListDivider, ListItem } from "@/components/ui/list";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
const [pressed, setPressed] = useState(false);
return (
A
}
title="Prefix에 Avatar 넣기"
detail="Amet elit ullamco magna."
/>
} />}
/>
액션 버튼
}
/>
} />
}
/>
{
"background only";
setPressed((value) => !value);
}}
>
{pressed ? "선택됨" : "토글 버튼"}
}
/>
);
}
```
### Clickable List Items
`ListButtonItem`을 사용하면 리스트 항목 전체를 탭할 수 있습니다. Lynx에는 `ListLinkItem`이 없으므로 화면 이동은 `bindtap`에서 앱 라우터를 호출합니다.
```tsx
import "./styles";
import IconArrowUpRightLine from "@karrotmarket/lynx-monochrome-icon/IconArrowUpRightLine";
import IconCheckmarkFill from "@karrotmarket/lynx-monochrome-icon/IconCheckmarkFill";
import IconChevronRightLine from "@karrotmarket/lynx-monochrome-icon/IconChevronRightLine";
import IconPenHorizlineFill from "@karrotmarket/lynx-monochrome-icon/IconPenHorizlineFill";
import IconPlusFill from "@karrotmarket/lynx-monochrome-icon/IconPlusFill";
import IconSquare2StackedFill from "@karrotmarket/lynx-monochrome-icon/IconSquare2StackedFill";
import { useState } from "@lynx-js/react";
import { ActionButton, PrefixIcon, SuffixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListButtonItem, ListDivider, ListItem } from "@/components/ui/list";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
const [status, setStatus] = useState("아직 탭하지 않았어요");
const [subscribed, setSubscribed] = useState(false);
const [copied, setCopied] = useState(false);
return (
{
"background only";
setStatus("편집 클릭됨");
}}
>
} />
편집
}
/>
{
"background only";
setStatus("리스트 아이템 클릭됨");
}}
suffix={
{}}>
{
"background only";
setSubscribed((value) => !value);
setStatus("모아보기 상태 변경됨");
}}
>
: } />
{subscribed ? "모아보는 중" : "모아보기"}
} />
}
/>
{
"background only";
setStatus("리스트 아이템 클릭됨");
}}
suffix={
{
"background only";
setStatus("커스텀 버튼 클릭됨");
}}
>
커스텀 버튼
} />
}
/>
{
"background only";
setStatus("링크 이동 요청됨");
}}
suffix={
{}}>
{
"background only";
setCopied(true);
setStatus("URL 복사됨");
}}
>
: } />
{copied ? "복사됨" : "URL 복사"}
} />
}
/>
{status}
);
}
```
### `input`s in List Items
`ListSwitchItem`, `ListCheckItem`, `ListRadioItem`을 사용하면 항목 전체를 탭해 값을 바꿀 수 있습니다. snippet은 각 컨트롤을 기본 suffix로 렌더링합니다. `suffix={null}`을 전달하면 기본 컨트롤을 숨길 수 있습니다.
```tsx
import "./styles";
import IconTrashcanLine from "@karrotmarket/lynx-monochrome-icon/IconTrashcanLine";
import { PrefixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListDivider, ListSwitchItem } from "@/components/ui/list";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
} />}
/>
);
}
```
```tsx
import "./styles";
import IconCheckmarkFatFill from "@karrotmarket/lynx-monochrome-icon/IconCheckmarkFatFill";
import { Badge, Checkbox, HStack, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListCheckItem, ListDivider } from "@/components/ui/list";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
알림 수신 동의
권장
}
detail="푸시 알림을 받으시겠습니까?"
defaultChecked
/>
} />
}
suffix={null}
defaultChecked
/>
}
checked={}
/>
}
suffix={null}
title="Ghost Variant"
/>
);
}
```
```tsx
import "./styles";
import { useSeedClassName } from "@seed-design/lynx-react";
import { List, ListDivider, ListRadioItem } from "@/components/ui/list";
import { RadioGroup, Radiomark } from "@/components/ui/radio-group";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
}
/>
}
suffix={null}
/>
}
suffix={null}
/>
);
}
```
### Accessibility
`ListButtonItem`은 기본적으로 button 역할과 문자열 `title` 기반의 접근성 이름을 제공합니다. 선택 항목은 현재 선택 여부를 `accessibility-value`로 전달합니다. `ListRadioItem`은 반드시 `RadioGroup.Root` 안에서 사용하고 그룹의 접근성 이름도 지정하세요.
```tsx
import { List, ListRadioItem } from "@/components/ui/list";
import { RadioGroup } from "@/components/ui/radio-group";
```
### Disabled
```tsx
import "./styles";
import IconChevronRightLine from "@karrotmarket/lynx-monochrome-icon/IconChevronRightLine";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import IconSlashCircleLine from "@karrotmarket/lynx-monochrome-icon/IconSlashCircleLine";
import { PrefixIcon, SuffixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListButtonItem, ListCheckItem, ListRadioItem } from "@/components/ui/list";
import { RadioGroup } from "@/components/ui/radio-group";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
return (
} />}
title="활성화된 ListButtonItem"
detail="Cupidatat et pariatur amet."
suffix={} />}
/>
} />}
title="활성화된 ListCheckItem"
/>
} />}
title="활성화된 ListRadioItem"
value="enabled"
/>
} />}
title="비활성화된 ListButtonItem"
detail="Cupidatat et pariatur amet."
suffix={} />}
/>
} />}
title="비활성화된 ListCheckItem"
/>
} />}
title="비활성화된 ListRadioItem"
value="disabled"
/>
);
}
```
### Variants
#### Highlighted
```tsx
import "./styles";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import { useState } from "@lynx-js/react";
import { PrefixIcon, useSeedClassName } from "@seed-design/lynx-react";
import { List, ListButtonItem, ListDivider, ListItem } from "@/components/ui/list";
import { Switch } from "@/components/ui/switch";
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
const [highlighted, setHighlighted] = useState(true);
return (
} />}
title="버튼"
detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt."
/>
} />}
title="하이라이트된 버튼"
detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt."
/>
} />}
title="하이라이트 및 비활성화된 버튼"
detail="Enim aute duis magna mollit aute sit aliquip duis ut tempor sunt."
/>
} />}
title="하이라이트"
highlighted={highlighted}
/>
highlight
);
}
```
### With Bottom Sheet
```tsx
import "./styles";
import IconArrowClockwiseCircularFill from "@karrotmarket/lynx-monochrome-icon/IconArrowClockwiseCircularFill";
import { useRef, useState } from "@lynx-js/react";
import {
ActionButton,
type BottomSheetRootRef,
PrefixIcon,
useSeedClassName,
} from "@seed-design/lynx-react";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "@/components/ui/bottom-sheet";
import { List, ListCheckItem } from "@/components/ui/list";
const TYPES = ["버스", "지하철", "택시", "자전거", "도보"] as const;
export default function Example() {
const seedClassName = useSeedClassName({ colorMode: "system" });
const sheetRef = useRef(null);
const [selectedTypes, setSelectedTypes] = useState<(typeof TYPES)[number][]>([]);
return (
BottomSheet 열기
{TYPES.map((type) => (
{
setSelectedTypes((previous) =>
previous.includes(type)
? previous.filter((item) => item !== type)
: [...previous, type],
);
}}
/>
))}
{
"background only";
sheetRef.current?.close();
}}
>
경로 찾기
{
"background only";
setSelectedTypes([]);
}}
>
} />
초기화
);
}
```
### Alignment
React의 `alignItems` prop은 Lynx List에서 지원하지 않습니다. Lynx에서는 Recipe가 prefix, content, suffix를 가운데에 정렬합니다. 상단 정렬이 필요하면 저수준 슬롯을 직접 배치한 별도 레이아웃을 사용하세요.
### Border Radius
React의 `itemBorderRadius` prop은 Lynx List에서 지원하지 않습니다. Lynx 항목의 눌림 반경은 Recipe가 정한 값을 사용합니다. 둥근 카드 안에서는 바깥 컨테이너에 `border-radius`와 `overflow: hidden`을 적용하세요.
## Customization and Composition
### Usage
`@seed-design/lynx-react`에서 제공하는 `List`와 `ListHeader` 컴포넌트는 다음 구조로 사용합니다.
```
ListHeader
List.Root
└── List.Item
├── List.Prefix (선택사항)
├── List.Content
│ ├── List.Title
│ └── List.Detail (선택사항)
└── List.Suffix (선택사항)
└── List.Item
├── ...
```
```tsx
import IconChevronRightLine from "@karrotmarket/lynx-monochrome-icon/IconChevronRightLine";
import IconPersonCircleLine from "@karrotmarket/lynx-monochrome-icon/IconPersonCircleLine";
import { List, ListHeader, PrefixIcon, SuffixIcon } from "@seed-design/lynx-react";
내 정보
} />
내 프로필
다른 사람들에게 보이는 내 정보를 관리합니다.
} />
```
- `ListHeader`: 리스트의 제목이나 설명을 표시하는 헤더
- `List.Root`: 모든 리스트 항목을 감싸는 컨테이너
- `List.Item`: 상호작용하지 않는 개별 항목
- `List.ButtonItem`: 탭할 수 있는 개별 항목
- `List.CheckboxItem`, `List.RadioItem`, `List.SwitchItem`: 선택 상태를 가진 개별 항목
- `List.Prefix`: 아이콘이나 Avatar 등을 표시하는 시작 영역
- `List.Content`: 제목과 설명이 들어가는 중앙 영역
- `List.Suffix`: 아이콘이나 Action Button 등을 표시하는 끝 영역
### `asChild` prop으로 적절한 시맨틱 요소와 조합하기
Lynx에는 DOM 기반 `asChild` prop이 없습니다. 역할에 맞는 상호작용 항목을 직접 사용합니다.
#### Using `List.ButtonItem` instead of `asChild` in `List.Content`
항목 전체를 탭할 때는 `List.ButtonItem`을 사용합니다.
```tsx
import { List } from "@seed-design/lynx-react";
사용자
항목 6개
```
snippet의 `ListButtonItem`은 같은 구조를 간단히 작성할 수 있게 돕습니다.
```tsx
import { ListButtonItem } from "@/components/ui/list";
```
#### Using selection items instead of `asChild` in `List.Item`
선택 상태가 있는 항목은 각각 `List.CheckboxItem`, `List.RadioItem`, `List.SwitchItem`을 사용합니다. snippet의 선택 항목은 접근성 값과 기본 컨트롤을 함께 구성합니다.
```tsx
import { ListCheckItem } from "@/components/ui/list";
```
## 웹 버전과의 차이
| 영역 | React Web | Lynx |
| ------ | ----------------------------- | -------------------------------- |
| 요소 | `ul`, `li`, `button`, `input` | `view`, `text` |
| 이벤트 | `onClick` | `bindtap`, `main-thread:bindtap` |
| 접근성 | HTML 시맨틱과 ARIA | `accessibility-*` |
| 링크 항목 | `ListLinkItem` | `ListButtonItem`에서 앱 라우터 호출 |
| 다형 렌더링 | `as`, `asChild` | 미지원 |
| 선택 컨트롤 | hidden input | Lynx 상태와 접근성 값 공유 |
## Lynx 미지원 기능
| 기능 | 웹 대응 | 대체 방법 |
| ------------------ | -------------------- | -------------------------------------- |
| `ListLinkItem` | `` 링크 항목 | `ListButtonItem`의 `bindtap`에서 앱 라우터 호출 |
| `itemBorderRadius` | 목록별 눌림 반경 | 바깥 컨테이너에 반경과 clipping 적용 |
| `alignItems` | prefix와 suffix 정렬 | 저수준 슬롯을 사용한 별도 레이아웃 구성 |
| Header 내부 복합 콘텐츠 | 제목과 액션을 한 Header에 배치 | Header와 액션을 형제 요소로 배치 |