Lynx

Tabs

한 화면의 콘텐츠를 탭 단위로 구분하고, 탭 선택이나 좌우 스와이프로 전환하는 컴포넌트입니다.

Engine ≥ 3.6<viewpager><viewpager-item>
사용 가능 버전@seed-design/lynx-react@0.5.0, @seed-design/lynx-css@0.9.0
Lynx 예제를 불러오는 중입니다.

Installation

npx @seed-design/cli add ui:tabs

Props

TabsRoot

Prop

Type

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

TabsList

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

TabsTrigger

Prop

Type

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

TabsCarousel

Prop

Type

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

TabsContent

Prop

Type

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

Usage

import {
  TabsCarousel,
  TabsContent,
  TabsList,
  TabsRoot,
  TabsTrigger,
} from "@/components/ui/tabs";

export function App() {
  return (
    <TabsRoot defaultValue="one" triggerLayout="fill">
      <TabsList>
        <TabsTrigger value="one">첫 번째</TabsTrigger>
        <TabsTrigger value="two">두 번째</TabsTrigger>
      </TabsList>
      <TabsCarousel swipeable className="tabs-carousel">
        <TabsContent value="one">
          <text>첫 번째 콘텐츠</text>
        </TabsContent>
        <TabsContent value="two">
          <text>두 번째 콘텐츠</text>
        </TabsContent>
      </TabsCarousel>
    </TabsRoot>
  );
}

Registry의 TabsListTabs.Indicator를 자동으로 추가합니다. TabsCarousel도 내부에서 Tabs.CarouselCamera를 조립하므로 각 TabsContent만 자식으로 전달하면 됩니다.

TabsCarousel은 Lynx 네이티브 <viewpager>를 사용합니다. Carousel에 앱 레이아웃에 맞는 높이를 지정해야 하며, swipeable을 설정하면 좌우 스와이프로 탭을 바꿀 수 있습니다.

iOS에서는 화면 왼쪽 32px 안에서 시작하는 뒤로가기 제스처를 우선합니다. 앱의 제스처 영역과 맞춰야 한다면 iosBackGestureEdgeWidth로 너비를 조정하세요.

TabsListscrollAlign은 선택한 tab을 가로 목록에 맞추는 방식이며 "nearest", "start", "center", "end"를 사용할 수 있습니다. 기본값은 "start"로 선택한 tab을 목록 시작에 맞춥니다. Chip Tabs의 ChipTabsList는 같은 옵션을 지원하지만 기본값이 "nearest"입니다.

Examples

Layout Fill (Default)

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

Layout Hug

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

Size Medium

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

Size Small (Default)

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

Transition

Lynx의 TabsCarousel은 탭을 선택할 때 native viewpager의 selectTabsmooth: false로 호출하므로 콘텐츠가 즉시 바뀝니다. 전환 효과가 필요하면 TabsCarousel 대신 선택 값을 기준으로 콘텐츠를 직접 렌더링하고 앱에서 애니메이션을 적용하세요.

Swipeable

Trigger를 탭하거나 콘텐츠를 좌우로 밀면 선택 값과 Indicator가 함께 이동합니다.

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

Lynx는 carouselPreventDrag를 지원하지 않습니다. Carousel 안에서 드래그와 충돌하는 조작 요소는 Carousel 밖에 배치하세요.

Disabled

disabled Trigger는 탭할 수 없습니다. Carousel이 비활성 탭에 도달하면 다음 활성 탭으로 이동합니다.

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

Notification

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

Sticky List

탭이 전체 화면을 차지하고 TabsList가 화면 위에 고정되어야 할 때 사용합니다.

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

Standalone

TabsContent를 사용하지 않고 콘텐츠 영역을 직접 구성할 때 사용합니다.

이 구성에서는 Tabs가 제공하는 스와이프 기능을 사용할 수 없습니다.

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

Dynamic Height

TabsCarouselautoHeight, lazyMount, unmountOnExit 없이도 TabsContent를 Carousel 밖에 배치하면 선택된 콘텐츠 높이에 맞춰 아래 콘텐츠의 위치를 바꿀 수 있습니다. 이 구성은 스와이프를 제공하지 않습니다.

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

Scroll to Top

TabsTriggerbindtap은 이미 선택된 Trigger를 다시 탭할 때도 호출됩니다. 아래 예시는 네 번째 항목부터 보이도록 시작하며, 선택된 탭을 다시 탭하면 현재 콘텐츠의 scroll-view가 맨 위로 이동합니다.

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

Web Version Differences

항목React WebLynx
렌더링 요소HTML div, button네이티브 view, text, scroll-view, viewpager
탭 선택 이벤트onClick 기반bindtap 기반, 공개 상태 이벤트는 onValueChange
CarouselEmbla Carousel네이티브 <viewpager>
가로 ListCSS overflow네이티브 <scroll-view>
제어 방식value, defaultValue동일
접근성ARIA tablist, tab, tabpanelLynx accessibility-* 속성
IndicatorDOM 측정과 CSS 변수bindlayoutchange 측정과 viewpager offset 보간

스와이프 관성, 비활성 탭 건너뛰기, Indicator 이동은 QR 코드 탭에서 Lynx Explorer로 확인하세요. 문서의 브라우저 미리보기와 네이티브 기기의 제스처 결과가 다를 수 있습니다.

Unsupported Lynx Features

현재 Lynx 구현은 다음 Web 기능을 지원하지 않습니다.

  • 세로 orientation
  • 키보드 방향키, Home/End, roving focus
  • RTL 순서와 스와이프 방향 반전
  • asChild
  • lazyMount, unmountOnExit
  • Carousel의 loop, autoHeight, dragThreshold
  • carouselPreventDrag
  • 콘텐츠 전환 애니메이션

Lynx의 native viewpager는 page slot을 유지해야 하므로 Carousel 콘텐츠를 lazy unmount하지 않습니다.

Last updated on

목차