Lynx

Chip Tabs

Chip 형태로 표현된 탭 컴포넌트입니다. 카테고리나 필터를 선택하여 콘텐츠를 전환할 때 사용합니다.

Engine ≥ 3.9<viewpager><viewpager-item>
사용 가능 버전@seed-design/[email protected], @seed-design/[email protected]
Lynx 예제를 불러오는 중입니다.

Installation

npx @seed-design/cli add ui:chip-tabs

Props

ChipTabsRoot

Prop

Type

value?string | undefined
defaultValue?string | undefined
onValueChange?((value: string) => void) | undefined
style?CSSProperties | undefined
children?ReactNode
className?string | undefined

ChipTabsList

Prop

Type

style?CSSProperties | undefined
children?ReactNode
className?string | undefined
main-thread:bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefined
main-thread:catchbgload?EventHandler<BaseImageLoadEvent<Element>> | undefined
main-thread:capture-bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefined
main-thread:capture-catchbgload?EventHandler<BaseImageLoadEvent<Element>> | undefined
main-thread:global-bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefined
main-thread:bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefined
main-thread:catchbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefined
main-thread:capture-bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefined
main-thread:capture-catchbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefined
main-thread:global-bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefined
main-thread:bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchtouchstart?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchtouchstart?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchtouchmove?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchtouchmove?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchtouchend?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchtouchend?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchlongpress?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchlongpress?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:catchtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-catchtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:global-bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:catchtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-catchtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:global-bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:catchtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:capture-catchtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:global-bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefined
main-thread:bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:catchanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-catchanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:global-bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:catchanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-catchanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:global-bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:catchanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-catchanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:global-bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:catchanimationend?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:capture-catchanimationend?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:global-bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefined
main-thread:bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmousedown?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmousedown?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmouseup?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmouseup?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmousemove?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmousemove?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmouseenter?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmouseenter?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmouseleave?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmouseleave?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmouseclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmouseclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindwheel?EventHandler<BaseWheelEvent<Element>> | undefined
main-thread:catchwheel?EventHandler<BaseWheelEvent<Element>> | undefined
main-thread:capture-bindwheel?EventHandler<BaseWheelEvent<Element>> | undefined
main-thread:capture-catchwheel?EventHandler<BaseWheelEvent<Element>> | undefined
main-thread:global-bindwheel?EventHandler<BaseWheelEvent<Element>> | undefined
main-thread:bindzoom?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:catchzoom?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-bindzoom?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:capture-catchzoom?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:global-bindzoom?EventHandler<BaseMouseEvent<Element>> | undefined
main-thread:bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:catchkeydown?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:capture-bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:capture-catchkeydown?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:global-bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:catchkeyup?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:capture-bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:capture-catchkeyup?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:global-bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefined
main-thread:bindfocus?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:catchfocus?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:capture-bindfocus?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:capture-catchfocus?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:global-bindfocus?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:bindblur?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:catchblur?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:capture-bindblur?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:capture-catchblur?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:global-bindblur?EventHandler<BaseCommonEvent<Element>> | undefined
main-thread:bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefined
main-thread:catchlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefined
main-thread:capture-bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefined
main-thread:capture-catchlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefined
main-thread:global-bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefined
main-thread:binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:catchuiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:capture-binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:capture-catchuiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:global-binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:catchuidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:capture-binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:capture-catchuidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:global-binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefined
main-thread:bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefined
main-thread:catchaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefined
main-thread:capture-bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefined
main-thread:capture-catchaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefined
main-thread:global-bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefined
main-thread:bindtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:catchlongtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:capture-catchlongtap?EventHandler<BaseTouchEvent<Element>> | undefined
main-thread:global-bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefined

ChipTabsTrigger

Prop

Type

notification?boolean | undefined
disabled?boolean | undefined
valuestring
style?CSSProperties | undefined
childrenstring | number
className?string | undefined
bindtap?EventHandler<BaseTouchEvent<Target>> | undefined
accessibility-label?string | undefined

ChipTabsCarousel

Prop

Type

swipeable?boolean | undefined
onSettle?(() => void) | undefined
style?CSSProperties | undefined
children?ReactNode
className?string | undefined

ChipTabsContent

Prop

Type

valuestring
style?CSSProperties | undefined
children?ReactNode
className?string | undefined

Usage

import { useState } from "@lynx-js/react";
import {
  ChipTabsList,
  ChipTabsRoot,
  ChipTabsTrigger,
} from "@/components/ui/chip-tabs";

export function App() {
  const [value, setValue] = useState("1");

  function handleValueChange(nextValue: string) {
    "background only";
    setValue(nextValue);
  }

  return (
    <>
      <ChipTabsRoot 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>content 1</text>}
      {value === "2" && <text>content 2</text>}
      {value === "3" && <text>content 3</text>}
    </>
  );
}

ChipTabsRootvalueonValueChange로 제어하거나 defaultValue로 초기 선택 값을 지정합니다. ChipTabsTriggernotification boolean은 label 옆 6px 간격에 작은 NotificationBadge를 표시합니다.

ChipTabsCarousel은 Registry에서 ChipTabsCarouselCamera를 조립하므로 ChipTabsContent만 자식으로 전달합니다. Carousel은 native <viewpager>이므로 Carousel 또는 Camera에 앱 레이아웃에 맞는 높이를 명시하고, swipeable로 좌우 스와이프를 켭니다. iOS의 화면 왼쪽 가장자리 뒤로가기 제스처를 우선할 범위는 iosBackGestureEdgeWidth로 정합니다.

공개 package API에서 Camera의 native bindchange, bindwillchange, bindoffsetchange를 직접 사용해야 할 때는 ChipTabs.CarouselCamera를 조합합니다. onSwipeStart, onSwipeEnd, onSettle은 Carousel에 전달해 스와이프 시작·종료·정착을 처리할 수 있습니다.

import { useState } from "@lynx-js/react";
import { ChipTabs } from "@seed-design/lynx-react";

export function App() {
  const [lastEvent, setLastEvent] = useState("대기");

  function handleSwipeStart() {
    "background only";
    setLastEvent("스와이프 시작");
  }

  function handleSwipeEnd() {
    "background only";
    setLastEvent("스와이프 종료");
  }

  function handleSettle() {
    "background only";
    setLastEvent("정착");
  }

  return (
    <ChipTabs.Root defaultValue="1">
      <ChipTabs.List>
        <ChipTabs.Trigger value="1">라벨1</ChipTabs.Trigger>
        <ChipTabs.Trigger value="2">라벨2</ChipTabs.Trigger>
      </ChipTabs.List>
      <ChipTabs.Carousel
        style={{ height: "240px" }}
        swipeable
        iosBackGestureEdgeWidth={32}
        onSwipeStart={handleSwipeStart}
        onSwipeEnd={handleSwipeEnd}
        onSettle={handleSettle}
      >
        <ChipTabs.CarouselCamera>
          <ChipTabs.Content value="1"><text>content 1</text></ChipTabs.Content>
          <ChipTabs.Content value="2"><text>content 2</text></ChipTabs.Content>
        </ChipTabs.CarouselCamera>
      </ChipTabs.Carousel>
      <text>{lastEvent}</text>
    </ChipTabs.Root>
  );
}

Scroll Alignment

ChipTabsListscrollAlign은 선택한 chip을 가로 목록에 맞추는 방식입니다. "nearest", "start", "center", "end"를 사용할 수 있으며 기본값은 "nearest"입니다.

  • "start", "center", "end"는 선택한 chip을 각각 목록의 시작, 가운데, 끝에 맞춥니다.
  • "nearest"는 React Web의 최소 스크롤 동작과 같이 선택한 chip이 완전히 보이면 현재 위치를 유지합니다. 왼쪽이 잘렸을 때는 시작 경계까지, 오른쪽이 잘렸을 때는 끝 경계까지 필요한 만큼만 이동하므로 "end"처럼 항상 끝에 맞추지 않습니다.

실제 chip의 너비와 위치, viewport를 기준으로 계산하며, 가변 너비 chip도 그대로 유지합니다. 목록의 첫·마지막 항목을 가운데에 맞추기 위해 빈 공간을 추가하지 않고 스크롤 가능한 범위 안에서만 이동하며 항목 순서도 바꾸지 않습니다.

아래 예제에서 모드를 고르면 선택을 라벨1로 초기화합니다. 그 뒤 라벨6을 탭하거나 6번 선택을 탭해 controlled value 변경에서도 실제 scrollAlign 차이를 확인하세요. nearest를 고르면 scrollAlign prop을 생략하여 기본 동작도 함께 확인합니다.

Lynx 예제를 불러오는 중입니다.

nearest에서 선택한 chip이 이미 완전히 보인 상태로 다시 선택하면 이동하지 않습니다. 목록을 직접 스크롤한 뒤에도 다음 선택이 필요한 경우에만 이동합니다.

Examples

Size=Medium

medium의 최소 높이는 36px입니다.

Lynx 예제를 불러오는 중입니다.

Size=Large

large의 최소 높이는 40px입니다.

Lynx 예제를 불러오는 중입니다.

Variant=Neutral Solid

Lynx 예제를 불러오는 중입니다.

Variant=Neutral Outline

Lynx 예제를 불러오는 중입니다.

Notification

Lynx 예제를 불러오는 중입니다.

With Scroll Fog

ScrollFog는 Registry snippet이나 Recipe 없이 @seed-design/lynx-react 패키지에서 직접 제공되는 package-only 공개 컴포넌트입니다. ChipTabsListplacement={["left", "right"]}ScrollFog로 감싸면 기본 20px fog가 좌우 edge에 표시됩니다. List의 내부 좌우 padding 16px은 그대로 유지합니다.

Lynx 예제를 불러오는 중입니다.

Web Version Differences

항목React WebLynx
렌더링 요소HTML div, button네이티브 view, text, scroll-view, viewpager
탭 선택 이벤트onClick 기반bindtap 기반, 공개 상태 이벤트는 onValueChange
콘텐츠Root 바깥의 조건부 div도 사용 가능Root 바깥에서 value에 따른 조건부 native text를 렌더링할 수 있음
가로 ListCSS overflow네이티브 가로 scroll-view
Carousel웹 Carousel네이티브 <viewpager>

React 예제와 마찬가지로 Lynx에서도 Root 바깥에서 선택 값에 따라 조건부 native text 콘텐츠를 렌더링할 수 있습니다. 별도 AppBar, 화면 shell, CTA, asset은 제공하지 않습니다.

Unsupported Lynx Features

  • 키보드 포커스와 roving focus
  • RTL 순서와 스와이프 방향 반전
  • lazyMount, unmountOnExit: native viewpager는 모든 page slot을 유지합니다.
  • Carousel의 loop, autoHeight, dragThreshold, carouselPreventDrag
  • 콘텐츠 전환 애니메이션

Last updated on

목차