Skip to content

Responsive Tabs

ResponsiveTabs 컴포넌트는 화면 너비나 컨테이너 공간이 부족할 때, 초과되는 탭을 드롭다운 메뉴로 자동 숨김 처리하는 반응형 탭 UI입니다.

Architecture

  • ResponsiveTabs.vue: 탭들을 감싸는 Wrapper 컴포넌트입니다. ResizeObserver를 활용하여 컴포넌트의 가로 폭을 실시간으로 감지하고, 화면에 표시할 수 있는 ResponsiveTab의 개수를 동적으로 계산합니다. 잘리는 탭들은 MenuBox를 이용해 더보기 메뉴 하위에 드롭다운 형태로 표시됩니다.
  • ResponsiveTab.vue: SecondaryTab.vue 컴포넌트와 동일한 속성과 스타일을 공유하지만, 가려진 상태를 처리할 수 있는 hidden 로직에 특화된 탭 컴포넌트입니다.

Features

  • 반응형 탭 (Responsive Layouts): ResizeObserver를 통해 창의 크기가 변할 때마다 탭의 크기를 계산하고 "더보기" 버튼 안에 숨기거나 다시 나타나도록 처리합니다.
  • 자동 포커싱 스크롤: "더보기" 드롭다운 메뉴 안에서 현재 선택된 탭이 활성화 상태일 경우, 해당 메뉴를 열면 자동으로 해당 탭으로 스크롤되는 기능을 포함합니다 (MenuBox와 MenuContainer 기본 동작 연동).
  • 접근성(Accessibility): 기존 키보드 내비게이션(Arrow Key 등)을 그대로 유지하며 탭 전환이 가능합니다.

Props

ResponsiveTabs

  • tabs (TabContraction<T>['item'][]): 화면에 표시될 대상 탭들의 배열 객체.
  • activeTab (TabKey): 현재 선택/활성화된 탭의 키 (Key).
  • tabIdentifier (string): 탭 요소들의 v-for key 부여를 위한 고유 접두사 (Optional).

ResponsiveTab

  • tabKey (TabKey): 탭을 고유하게 식별하기 위한 값 (필수).
  • label (string): 탭에 표시할 텍스트 라벨 (필수).
  • icon (string): 탭의 아이콘 종류 (Optional).
  • disabled (boolean): 탭 비활성화 여부 지정 (기본값: false, Optional).
  • badgeContent (number): 숫자 뱃지 표시 내용 (Optional).
  • hidden (boolean): Responsive 계산에 의해 현재 탭이 숨김 처리되어야 하는지 여부 (Optional).

Used Packages

  • @vueuse/core (ResizeObserver, useElementBounding)
  • @vueuse/components (vOnClickOutside)
  • MenuBox, MenuContainer (내부 드롭다운 처리)

Example

Selected Tab: tab1

:::demo

vue
<template>
    <div
        style="width: 100%; max-width: 400px; border: 1px solid var(--gray-300); padding: 8px; border-radius: 4px; resize: horizontal; overflow: auto;"
    >
        <responsive-tabs
            :tabs="tabsData"
            :active-tab="currentActiveTab"
            @select:tab="handleTabSelect"
        />
    </div>

    <div style="margin-top: 24px">Selected Tab: {{ currentActiveTab }}</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { ResponsiveTabs, ICON_TYPE } from '@jennifersoft/vue-components-v2';

const tabsData = [
    { tabKey: 'tab1', label: 'Dashboard' },
    { tabKey: 'tab2', label: 'Management' },
    { tabKey: 'tab3', label: 'Settings', disabled: true },
    { tabKey: 'tab4', label: 'Profile' },
    { tabKey: 'tab5', label: 'Notifications', badgeContent: 'C' },
    { tabKey: 'tab6', label: 'Billing' },
    { tabKey: 'tab7', label: 'API Keys', icon: ICON_TYPE.key },
    { tabKey: 'tab8', label: 'Security Logs' },
    { tabKey: 'tab9', label: 'Analytics' },
    { tabKey: 'tab10', label: 'Deployments' },
    { tabKey: 'tab11', label: 'Webhooks' },
    { tabKey: 'tab12', label: 'Integrations' },
    { tabKey: 'tab13', label: 'Advanced Settings' },
];

const currentActiveTab = ref('tab1');

const handleTabSelect = (key: string | number | symbol) => {
    currentActiveTab.value = key as string;
};
</script>

:::