Tabs
한 화면의 콘텐츠를 탭 단위로 구분하고, 탭 선택이나 좌우 스와이프로 전환하는 컴포넌트입니다.
<viewpager><viewpager-item>Installation
의존성 설치
아래 코드를 복사 후 붙여넣고 사용하세요
Props
TabsRoot
Prop
Type
value?string | undefineddefaultValue?string | undefinedonValueChange?((value: string) => void) | undefinedstyle?CSSProperties | undefinedchildren?ReactNodeclassName?string | undefinedTabsList
Prop
Type
style?CSSProperties | undefinedchildren?ReactNodeclassName?string | undefinedmain-thread:bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefinedmain-thread:catchbgload?EventHandler<BaseImageLoadEvent<Element>> | undefinedmain-thread:capture-bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefinedmain-thread:capture-catchbgload?EventHandler<BaseImageLoadEvent<Element>> | undefinedmain-thread:global-bindbgload?EventHandler<BaseImageLoadEvent<Element>> | undefinedmain-thread:bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefinedmain-thread:catchbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefinedmain-thread:capture-bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefinedmain-thread:capture-catchbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefinedmain-thread:global-bindbgerror?EventHandler<BaseImageErrorEvent<Element>> | undefinedmain-thread:bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchtouchstart?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchtouchstart?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindtouchstart?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchtouchmove?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchtouchmove?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindtouchmove?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindtouchcancel?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchtouchend?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchtouchend?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindtouchend?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchlongpress?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchlongpress?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindlongpress?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:catchtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-catchtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:global-bindtransitionstart?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:catchtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-catchtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:global-bindtransitioncancel?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:catchtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:capture-catchtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:global-bindtransitionend?EventHandler<BaseTransitionEvent<Element>> | undefinedmain-thread:bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:catchanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-catchanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:global-bindanimationstart?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:catchanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-catchanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:global-bindanimationiteration?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:catchanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-catchanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:global-bindanimationcancel?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:catchanimationend?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:capture-catchanimationend?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:global-bindanimationend?EventHandler<BaseAnimationEvent<Element>> | undefinedmain-thread:bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmousedown?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmousedown?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmousedown?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmouseup?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmouseup?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmouseup?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmousemove?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmousemove?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmousemove?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmouseenter?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmouseenter?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmouseenter?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmouseleave?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmouseleave?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmouseleave?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmouseclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmouseclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmouseclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmousedblclick?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindmouselongpress?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindwheel?EventHandler<BaseWheelEvent<Element>> | undefinedmain-thread:catchwheel?EventHandler<BaseWheelEvent<Element>> | undefinedmain-thread:capture-bindwheel?EventHandler<BaseWheelEvent<Element>> | undefinedmain-thread:capture-catchwheel?EventHandler<BaseWheelEvent<Element>> | undefinedmain-thread:global-bindwheel?EventHandler<BaseWheelEvent<Element>> | undefinedmain-thread:bindzoom?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:catchzoom?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-bindzoom?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:capture-catchzoom?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:global-bindzoom?EventHandler<BaseMouseEvent<Element>> | undefinedmain-thread:bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:catchkeydown?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:capture-bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:capture-catchkeydown?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:global-bindkeydown?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:catchkeyup?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:capture-bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:capture-catchkeyup?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:global-bindkeyup?EventHandler<BaseKeyEvent<Element>> | undefinedmain-thread:bindfocus?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:catchfocus?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:capture-bindfocus?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:capture-catchfocus?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:global-bindfocus?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:bindblur?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:catchblur?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:capture-bindblur?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:capture-catchblur?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:global-bindblur?EventHandler<BaseCommonEvent<Element>> | undefinedmain-thread:bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefinedmain-thread:catchlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefinedmain-thread:capture-bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefinedmain-thread:capture-catchlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefinedmain-thread:global-bindlayoutchange?EventHandler<LayoutChangeDetailEvent<Element>> | undefinedmain-thread:binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:catchuiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:capture-binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:capture-catchuiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:global-binduiappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:catchuidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:capture-binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:capture-catchuidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:global-binduidisappear?EventHandler<UIAppearanceDetailEvent<Element>> | undefinedmain-thread:bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefinedmain-thread:catchaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefinedmain-thread:capture-bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefinedmain-thread:capture-catchaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefinedmain-thread:global-bindaccessibilityaction?EventHandler<AccessibilityActionDetailEvent<Element>> | undefinedmain-thread:bindtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:catchlongtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:capture-catchlongtap?EventHandler<BaseTouchEvent<Element>> | undefinedmain-thread:global-bindlongtap?EventHandler<BaseTouchEvent<Element>> | undefinedTabsTrigger
Prop
Type
notification?boolean | undefineddisabled?boolean | undefinedclassName?string | undefinedbindtap?EventHandler<BaseTouchEvent<Target>> | undefinedchildrenstring | numberstyle?CSSProperties | undefinedaccessibility-label?string | undefinedvaluestringTabsCarousel
Prop
Type
swipeable?boolean | undefinedonSettle?(() => void) | undefinedstyle?CSSProperties | undefinedchildren?ReactNodeclassName?string | undefinedTabsContent
Prop
Type
valuestringstyle?CSSProperties | undefinedchildren?ReactNodeclassName?string | undefinedUsage
Registry의 TabsList는 Tabs.Indicator를 자동으로 추가합니다. TabsCarousel도 내부에서 Tabs.CarouselCamera를 조립하므로 각 TabsContent만 자식으로 전달하면 됩니다.
TabsCarousel은 Lynx 네이티브 <viewpager>를 사용합니다. Carousel에 앱 레이아웃에 맞는 높이를 지정해야 하며, swipeable을 설정하면 좌우 스와이프로 탭을 바꿀 수 있습니다.
iOS에서는 화면 왼쪽 32px 안에서 시작하는 뒤로가기 제스처를 우선합니다. 앱의 제스처 영역과 맞춰야 한다면 iosBackGestureEdgeWidth로 너비를 조정하세요.
TabsList의 scrollAlign은 선택한 tab을 가로 목록에 맞추는 방식이며 "nearest", "start", "center", "end"를 사용할 수 있습니다. 기본값은 "start"로 선택한 tab을 목록 시작에 맞춥니다. Chip Tabs의 ChipTabsList는 같은 옵션을 지원하지만 기본값이 "nearest"입니다.
Examples
Layout Fill (Default)
Layout Hug
Size Medium
Size Small (Default)
Transition
Lynx의 TabsCarousel은 탭을 선택할 때 native viewpager의 selectTab을 smooth: false로 호출하므로 콘텐츠가 즉시 바뀝니다. 전환 효과가 필요하면 TabsCarousel 대신 선택 값을 기준으로 콘텐츠를 직접 렌더링하고 앱에서 애니메이션을 적용하세요.
Swipeable
Trigger를 탭하거나 콘텐츠를 좌우로 밀면 선택 값과 Indicator가 함께 이동합니다.
Carousel Prevent Drag
Lynx는 carouselPreventDrag를 지원하지 않습니다. Carousel 안에서 드래그와 충돌하는 조작 요소는 Carousel 밖에 배치하세요.
Disabled
disabled Trigger는 탭할 수 없습니다. Carousel이 비활성 탭에 도달하면 다음 활성 탭으로 이동합니다.
Notification
Sticky List
탭이 전체 화면을 차지하고 TabsList가 화면 위에 고정되어야 할 때 사용합니다.
Standalone
TabsContent를 사용하지 않고 콘텐츠 영역을 직접 구성할 때 사용합니다.
이 구성에서는 Tabs가 제공하는 스와이프 기능을 사용할 수 없습니다.
Dynamic Height
TabsCarousel의 autoHeight, lazyMount, unmountOnExit 없이도 TabsContent를 Carousel 밖에 배치하면 선택된 콘텐츠 높이에 맞춰 아래 콘텐츠의 위치를 바꿀 수 있습니다. 이 구성은 스와이프를 제공하지 않습니다.
Scroll to Top
TabsTrigger의 bindtap은 이미 선택된 Trigger를 다시 탭할 때도 호출됩니다. 아래 예시는 네 번째 항목부터 보이도록 시작하며, 선택된 탭을 다시 탭하면 현재 콘텐츠의 scroll-view가 맨 위로 이동합니다.
Web Version Differences
| 항목 | React Web | Lynx |
|---|---|---|
| 렌더링 요소 | HTML div, button | 네이티브 view, text, scroll-view, viewpager |
| 탭 선택 이벤트 | onClick 기반 | bindtap 기반, 공개 상태 이벤트는 onValueChange |
| Carousel | Embla Carousel | 네이티브 <viewpager> |
| 가로 List | CSS overflow | 네이티브 <scroll-view> |
| 제어 방식 | value, defaultValue | 동일 |
| 접근성 | ARIA tablist, tab, tabpanel | Lynx accessibility-* 속성 |
| Indicator | DOM 측정과 CSS 변수 | bindlayoutchange 측정과 viewpager offset 보간 |
스와이프 관성, 비활성 탭 건너뛰기, Indicator 이동은 QR 코드 탭에서 Lynx Explorer로 확인하세요. 문서의 브라우저 미리보기와 네이티브 기기의 제스처 결과가 다를 수 있습니다.
Unsupported Lynx Features
현재 Lynx 구현은 다음 Web 기능을 지원하지 않습니다.
- 세로
orientation - 키보드 방향키, Home/End, roving focus
- RTL 순서와 스와이프 방향 반전
asChildlazyMount,unmountOnExit- Carousel의
loop,autoHeight,dragThreshold carouselPreventDrag- 콘텐츠 전환 애니메이션
Lynx의 native viewpager는 page slot을 유지해야 하므로 Carousel 콘텐츠를 lazy unmount하지 않습니다.
Last updated on