Files
moreminimore-chat/app/javascript/dashboard/components-next/vertical-tabs/VerticalTabs.vue
2026-08-03 10:39:02 +05:30

56 lines
1.7 KiB
Vue

<script setup>
import Icon from 'dashboard/components-next/icon/Icon.vue';
defineProps({
tabs: {
type: Array,
required: true,
validator: value => value.every(tab => tab.id && tab.label),
},
contentClass: {
type: String,
default: '',
},
});
const activeTab = defineModel({ type: String, required: true });
</script>
<template>
<div class="flex flex-col w-full gap-4 md:flex-row md:items-start md:gap-8">
<!-- Horizontal scrollable tab row on small screens; vertical rail from md up. -->
<nav
class="flex flex-row w-full gap-1 pb-2 overflow-x-auto no-scrollbar border-b shrink-0 border-n-weak md:sticky md:top-0 md:flex-col md:w-48 md:gap-0.5 md:border-b-0 md:overflow-visible"
>
<button
v-for="tab in tabs"
:key="tab.id"
type="button"
class="flex items-center h-9 gap-2 px-2.5 text-sm transition-colors rounded-lg shrink-0 md:w-full"
:class="
activeTab === tab.id
? 'bg-n-alpha-2 text-n-slate-12 font-medium'
: 'text-n-slate-11 hover:bg-n-alpha-1 hover:text-n-slate-12'
"
:aria-current="activeTab === tab.id ? 'page' : undefined"
@click="activeTab = tab.id"
>
<Icon v-if="tab.icon" :icon="tab.icon" class="shrink-0 size-4" />
{{ tab.label }}
</button>
</nav>
<div class="flex flex-col flex-1 w-full min-w-0" :class="contentClass">
<!-- Keep every panel mounted and toggle visibility so unsaved drafts survive tab switches. -->
<div
v-for="tab in tabs"
v-show="activeTab === tab.id"
:key="tab.id"
class="flex flex-col w-full min-w-0"
>
<slot :name="tab.id" />
</div>
</div>
</div>
</template>