Chip Tabs
Chip 형태로 표현된 탭 컴포넌트입니다. 카테고리나 필터를 선택하여 콘텐츠를 전환할 때 사용합니다.
<viewpager><viewpager-item>import "./styles";
import {
ChipTabsContent,
ChipTabsList,
ChipTabsRoot,
ChipTabsTrigger,
} from "@/components/ui/chip-tabs";
export default function Example() {
return (
<ChipTabsRoot defaultValue="1" style={{ width: "100%", maxWidth: "360px" }}>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
<ChipTabsContent className="chip-tabs-preview__content" value="1">
<text className="chip-tabs-preview__content-text">Content 1</text>
</ChipTabsContent>
<ChipTabsContent className="chip-tabs-preview__content" value="2">
<text className="chip-tabs-preview__content-text">Content 2</text>
</ChipTabsContent>
<ChipTabsContent className="chip-tabs-preview__content" value="3">
<text className="chip-tabs-preview__content-text">Content 3</text>
</ChipTabsContent>
</ChipTabsRoot>
);
}Installation
npx @seed-design/cli add ui:chip-tabspnpm dlx @seed-design/cli add ui:chip-tabsyarn dlx @seed-design/cli add ui:chip-tabsbun x @seed-design/cli add ui:chip-tabs의존성 설치
npm install @seed-design/lynx-reactyarn add @seed-design/lynx-reactpnpm add @seed-design/lynx-reactbun add @seed-design/lynx-react아래 코드를 복사 후 붙여넣고 사용하세요
/**
* @file ui:chip-tabs
* @requires @seed-design/lynx-react@^1.0.0
* @requires @seed-design/lynx-css@^1.0.0
**/
import { NotificationBadge, ChipTabs as SeedChipTabs } from "@seed-design/lynx-react";
import { forwardRef } from "@lynx-js/react";
export interface ChipTabsRootProps extends SeedChipTabs.RootProps {}
/**
* @see https://seed-design.io/lynx/components/chip-tabs
*/
export const ChipTabsRoot = SeedChipTabs.Root;
export interface ChipTabsListProps extends SeedChipTabs.ListProps {}
export const ChipTabsList = SeedChipTabs.List;
export interface ChipTabsTriggerProps extends Omit<SeedChipTabs.TriggerProps, "notification"> {
notification?: boolean;
}
export const ChipTabsTrigger = forwardRef<unknown, ChipTabsTriggerProps>((props, ref) => {
const { children, notification, ...otherProps } = props;
return (
<SeedChipTabs.Trigger
ref={ref}
{...otherProps}
notification={
notification ? (
<NotificationBadge size="small" accessibility-elements-hidden={true} />
) : undefined
}
>
{children}
</SeedChipTabs.Trigger>
);
});
ChipTabsTrigger.displayName = "ChipTabsTrigger";
export interface ChipTabsCarouselProps extends SeedChipTabs.CarouselProps {}
export const ChipTabsCarousel = forwardRef<unknown, ChipTabsCarouselProps>((props, ref) => {
const { children, ...otherProps } = props;
return (
<SeedChipTabs.Carousel ref={ref} {...otherProps}>
<SeedChipTabs.CarouselCamera>{children}</SeedChipTabs.CarouselCamera>
</SeedChipTabs.Carousel>
);
});
ChipTabsCarousel.displayName = "ChipTabsCarousel";
export interface ChipTabsContentProps extends SeedChipTabs.ContentProps {}
export const ChipTabsContent = SeedChipTabs.Content;
/**
* 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
ChipTabsRoot
Prop
Type
value?string | undefineddefaultValue?string | undefinedonValueChange?((value: string) => void) | undefinedChipTabsList
Prop
Type
ChipTabsTrigger
Prop
Type
notification?boolean | undefinedchildrenstring | numberdisabled?boolean | undefinedvaluestringChipTabsCarousel
Prop
Type
swipeable?boolean | undefinedonSettle?(() => void) | undefinedChipTabsContent
Prop
Type
valuestringUsage
Content
탭 선택으로만 콘텐츠를 전환할 때는 ChipTabsContent를 ChipTabsRoot 안에 배치합니다. Trigger와 Content의 value를 맞추면 선택된 패널만 표시됩니다. 이 구성은 콘텐츠 높이에 맞춰 배치되며 좌우 스와이프는 제공하지 않습니다.
import {
ChipTabsContent,
ChipTabsList,
ChipTabsRoot,
ChipTabsTrigger,
} from "@/components/ui/chip-tabs";
export function App() {
return (
<ChipTabsRoot defaultValue="1">
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
<ChipTabsContent value="1">
<text>첫 번째 콘텐츠</text>
</ChipTabsContent>
<ChipTabsContent value="2">
<text>두 번째 콘텐츠</text>
</ChipTabsContent>
<ChipTabsContent value="3">
<text>세 번째 콘텐츠</text>
</ChipTabsContent>
</ChipTabsRoot>
);
}ChipTabsRoot는 value와 onValueChange로 제어하거나 defaultValue로 초기 선택 값을 지정합니다. notification을 설정한 Trigger에는 알림 배지가 표시됩니다.
Swipeable
콘텐츠를 좌우로 밀어 탭을 바꾸려면 ChipTabsContent를 ChipTabsCarousel 안에 배치하고 swipeable을 설정합니다. Carousel에 앱 레이아웃에 맞는 높이를 지정하세요. iOS의 뒤로 가기 제스처 범위는 iosBackGestureEdgeWidth로 조절할 수 있습니다.
import "./styles";
import {
ChipTabsCarousel,
ChipTabsContent,
ChipTabsList,
ChipTabsRoot,
ChipTabsTrigger,
} from "@/components/ui/chip-tabs";
export default function Example() {
return (
<ChipTabsRoot defaultValue="2" style={{ width: "100%", maxWidth: "360px" }}>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
<ChipTabsCarousel swipeable style={{ height: "180px" }}>
<ChipTabsContent className="chip-tabs-preview__content" value="1">
<text className="chip-tabs-preview__content-text">Content 1</text>
</ChipTabsContent>
<ChipTabsContent className="chip-tabs-preview__content" value="2">
<text className="chip-tabs-preview__content-text">Content 2</text>
</ChipTabsContent>
<ChipTabsContent className="chip-tabs-preview__content" value="3">
<text className="chip-tabs-preview__content-text">Content 3</text>
</ChipTabsContent>
</ChipTabsCarousel>
</ChipTabsRoot>
);
}Scroll Alignment
ChipTabsList의 scrollAlign은 선택한 chip을 가로 목록에 맞추는 방식입니다. "nearest", "start", "center", "end"를 지원하며 기본값은 "nearest"입니다.
"nearest"는 선택한 chip이 완전히 보이면 이동하지 않고, 일부가 가려졌을 때만 필요한 만큼 스크롤합니다.
import "./styles";
import { useState } from "@lynx-js/react";
import { ActionButton, Box, HStack, Text, VStack } from "@seed-design/lynx-react";
import {
ChipTabsList,
ChipTabsRoot,
ChipTabsTrigger,
type ChipTabsListProps,
} from "@/components/ui/chip-tabs";
type ScrollAlign = NonNullable<ChipTabsListProps["scrollAlign"]>;
const ALIGNMENTS: ScrollAlign[] = ["nearest", "start", "center", "end"];
const LABELS = Array.from({ length: 15 }, (_, index) => String(index + 1));
export default function Example() {
const [value, setValue] = useState("1");
const [scrollAlign, setScrollAlign] = useState<ScrollAlign>("nearest");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
function selectScrollAlign(nextScrollAlign: ScrollAlign) {
"background only";
setScrollAlign(nextScrollAlign);
setValue("1");
}
function selectSixthChip() {
"background only";
setValue("6");
}
return (
<VStack
className="chip-tabs-scroll-alignment"
width="full"
maxWidth="360px"
align="center"
gap="x3"
>
<HStack
className="chip-tabs-scroll-alignment__controls"
wrap="wrap"
justify="center"
gap="x2"
>
{ALIGNMENTS.map((nextScrollAlign) => (
<ActionButton
key={nextScrollAlign}
className={`chip-tabs-scroll-alignment__mode-button chip-tabs-scroll-alignment__mode-button--${nextScrollAlign}`}
variant={scrollAlign === nextScrollAlign ? "neutralSolid" : "neutralOutline"}
bindtap={() => {
"background only";
selectScrollAlign(nextScrollAlign);
}}
>
{nextScrollAlign}
</ActionButton>
))}
<ActionButton
className="chip-tabs-scroll-alignment__select-sixth-button"
variant="neutralOutline"
bindtap={selectSixthChip}
>
6번 선택
</ActionButton>
</HStack>
<Box>
<Text className="chip-tabs-scroll-alignment__selected-value" textStyle="t5Regular">
{`선택: 라벨${value}`}
</Text>
</Box>
<Box className="chip-tabs-scroll-alignment__list-frame" width="full" maxWidth="360px">
<ChipTabsRoot value={value} onValueChange={handleValueChange}>
<ChipTabsList scrollAlign={scrollAlign === "nearest" ? undefined : scrollAlign}>
{LABELS.map((label) => (
<ChipTabsTrigger key={label} value={label}>
{`라벨${label}`}
</ChipTabsTrigger>
))}
</ChipTabsList>
</ChipTabsRoot>
</Box>
</VStack>
);
}Examples
Size=Medium
medium의 최소 높이는 36px입니다.
import "./styles";
import { useState } from "@lynx-js/react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
const [value, setValue] = useState("1");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
return (
<view className="chip-tabs-preview">
<ChipTabsRoot
variant="neutralSolid"
size="medium"
defaultValue="1"
value={value}
onValueChange={handleValueChange}
>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
</ChipTabsRoot>
{value === "1" && <text className="chip-tabs-preview__content-text">content 1</text>}
{value === "2" && <text className="chip-tabs-preview__content-text">content 2</text>}
{value === "3" && <text className="chip-tabs-preview__content-text">content 3</text>}
</view>
);
}Size=Large
large의 최소 높이는 40px입니다.
import "./styles";
import { useState } from "@lynx-js/react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
const [value, setValue] = useState("1");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
return (
<view className="chip-tabs-preview">
<ChipTabsRoot
variant="neutralSolid"
size="large"
defaultValue="1"
value={value}
onValueChange={handleValueChange}
>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
</ChipTabsRoot>
{value === "1" && <text className="chip-tabs-preview__content-text">content 1</text>}
{value === "2" && <text className="chip-tabs-preview__content-text">content 2</text>}
{value === "3" && <text className="chip-tabs-preview__content-text">content 3</text>}
</view>
);
}Variant=Neutral Solid
import "./styles";
import { useState } from "@lynx-js/react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
const [value, setValue] = useState("1");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
return (
<view className="chip-tabs-preview">
<ChipTabsRoot
variant="neutralSolid"
defaultValue="1"
value={value}
onValueChange={handleValueChange}
>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
</ChipTabsRoot>
{value === "1" && <text className="chip-tabs-preview__content-text">content 1</text>}
{value === "2" && <text className="chip-tabs-preview__content-text">content 2</text>}
{value === "3" && <text className="chip-tabs-preview__content-text">content 3</text>}
</view>
);
}Variant=Neutral Outline
import "./styles";
import { useState } from "@lynx-js/react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
const [value, setValue] = useState("1");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
return (
<view className="chip-tabs-preview">
<ChipTabsRoot
variant="neutralOutline"
defaultValue="1"
value={value}
onValueChange={handleValueChange}
>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
</ChipTabsRoot>
{value === "1" && <text className="chip-tabs-preview__content-text">content 1</text>}
{value === "2" && <text className="chip-tabs-preview__content-text">content 2</text>}
{value === "3" && <text className="chip-tabs-preview__content-text">content 3</text>}
</view>
);
}Notification
import "./styles";
import { useState } from "@lynx-js/react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
const [value, setValue] = useState("1");
function handleValueChange(nextValue: string) {
"background only";
setValue(nextValue);
}
return (
<view className="chip-tabs-preview">
<ChipTabsRoot
variant="neutralOutline"
size="medium"
defaultValue="1"
value={value}
onValueChange={handleValueChange}
>
<ChipTabsList>
<ChipTabsTrigger notification value="1">
라벨1
</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
</ChipTabsList>
</ChipTabsRoot>
{value === "1" && <text className="chip-tabs-preview__content-text">content 1</text>}
{value === "2" && <text className="chip-tabs-preview__content-text">content 2</text>}
{value === "3" && <text className="chip-tabs-preview__content-text">content 3</text>}
</view>
);
}With Scroll Fog
ChipTabsList를 placement={["left", "right"]}인 ScrollFog로 감싸면 좌우에 fog를 표시할 수 있습니다.
import "./styles";
import { ScrollFog } from "@seed-design/lynx-react";
import { ChipTabsList, ChipTabsRoot, ChipTabsTrigger } from "@/components/ui/chip-tabs";
export default function Example() {
return (
<ChipTabsRoot defaultValue="1" style={{ width: "100%", maxWidth: "360px" }}>
<ScrollFog style={{ width: "100%", height: "36px" }} placement={["left", "right"]}>
<ChipTabsList>
<ChipTabsTrigger value="1">라벨1</ChipTabsTrigger>
<ChipTabsTrigger value="2">라벨2</ChipTabsTrigger>
<ChipTabsTrigger value="3">라벨3</ChipTabsTrigger>
<ChipTabsTrigger value="4">라벨4</ChipTabsTrigger>
<ChipTabsTrigger value="5">라벨5</ChipTabsTrigger>
<ChipTabsTrigger value="6">라벨6</ChipTabsTrigger>
<ChipTabsTrigger value="7">라벨7</ChipTabsTrigger>
<ChipTabsTrigger value="8">라벨8</ChipTabsTrigger>
<ChipTabsTrigger value="9">라벨9</ChipTabsTrigger>
<ChipTabsTrigger value="10">라벨10</ChipTabsTrigger>
<ChipTabsTrigger value="11">라벨11</ChipTabsTrigger>
<ChipTabsTrigger value="12">라벨12</ChipTabsTrigger>
<ChipTabsTrigger value="13">라벨13</ChipTabsTrigger>
<ChipTabsTrigger value="14">라벨14</ChipTabsTrigger>
<ChipTabsTrigger value="15">라벨15</ChipTabsTrigger>
</ChipTabsList>
</ScrollFog>
</ChipTabsRoot>
);
}Web Version Differences
- 탭 이벤트는
onClick대신bindtap으로 받습니다. - Carousel은 네이티브
<viewpager>, 가로 탭 목록은<scroll-view>를 사용합니다. - Root 밖에서 선택 값에 따라 네이티브 콘텐츠를 조건부로 렌더링할 수 있습니다.
Unsupported Lynx Features
- 키보드 포커스와 roving focus
- RTL 순서와 스와이프 방향 반전
lazyMount,unmountOnExit- Carousel의
loop,autoHeight,dragThreshold,carouselPreventDrag - 콘텐츠 전환 애니메이션
Last updated on