# 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와 액션을 형제 요소로 배치 |