安裝
Terminal
pnpm dlx shadcn-vue add @admin/tabsimport { AdminTabs, AdminTabsContent, AdminTabsList, AdminTabsTrigger,} from '@/components/admin/tabs'用法
<script setup lang="ts">import { AdminTabs, AdminTabsContent, AdminTabsList, AdminTabsTrigger} from '@/components/admin/tabs'</script><template> <admin-tabs class="w-80" default-value="a"> <admin-tabs-list> <admin-tabs-trigger value="a">Account</admin-tabs-trigger> <admin-tabs-trigger value="b">Password</admin-tabs-trigger> </admin-tabs-list> <admin-tabs-content class="pt-3" value="a">Account panel</admin-tabs-content> <admin-tabs-content class="pt-3" value="b">Password panel</admin-tabs-content> </admin-tabs></template>場景
受控模式
<script setup lang="ts">import { ref } from 'vue'import { AdminButton } from '@/components/admin/button'import { AdminTabs, AdminTabsContent, AdminTabsList, AdminTabsTrigger} from '@/components/admin/tabs'const active = ref<string | number>('account')</script><template> <div class="flex flex-col gap-3"> <div class="flex items-center gap-2"> <span class="typo-content-medium">目前分頁:{{ active }}</span> <admin-button size="small" @click="active = active === 'account' ? 'password' : 'account'"> 外部切換 </admin-button> </div> <admin-tabs v-model="active" class="w-80"> <admin-tabs-list> <admin-tabs-trigger value="account">Account</admin-tabs-trigger> <admin-tabs-trigger value="password">Password</admin-tabs-trigger> </admin-tabs-list> <admin-tabs-content class="pt-3" value="account">Account panel</admin-tabs-content> <admin-tabs-content class="pt-3" value="password">Password panel</admin-tabs-content> </admin-tabs> </div></template>Type-safe 封裝
AdminTabs 已是 generic(T extends StringOrNumber),以較嚴的 ref 綁定 v-model 即可收斂為 tab value 的 literal union。若要讓 value 與清單對齊、並讓每個 tab 的內容仍保有完整彈性,可自行封裝:傳入 items(建議加 as const),由封裝元件以 items 為單一事實來源渲染各 trigger 與 content shell,content 由使用端以 #panel-<value> 具名 slot 填入。
如此 items 即聯集來源,各 trigger 的 value 結構上不可能越界;型別安全落在 v-model 與 defaultValue 必須對齊 items 的 value union(允許 undefined 表未選)。封裝再以 defineSlots 將 content slot 收斂為 panel-<value> 聯集,填入不在清單內的 slot 名亦會編譯期報錯。
此食譜刻意精簡,未透出 trigger 的
badge/badgeValue;需要時可自行 fork 擴充。封裝元件僅為文件示範,不屬 registry 安裝產物。
TypedAdminTabs.vue
<script setup lang="ts" generic=" TItems extends ReadonlyArray<{ value: string | number label: string disabled?: boolean }> ">import type { AdminTabsEmits, AdminTabsProps } from '@/components/admin/tabs'import { reactiveOmit } from '@vueuse/core'import { useForwardPropsEmits } from 'reka-ui'import { AdminTabs, AdminTabsContent, AdminTabsList, AdminTabsTrigger} from '@/components/admin/tabs'type TItem = TItems[number]// v-model / defaultValue 型別跟著 items 走:允許 undefined 表初始未選type TValue = TItem['value']defineOptions({ name: 'typed-admin-tabs'})const props = defineProps< AdminTabsProps<TValue> & { items: TItems }>()const emits = defineEmits< Omit<AdminTabsEmits<TValue>, 'update:modelValue'> & { 'update:modelValue': [value: TValue] }>()defineSlots<Partial<Record<`panel-${TValue}`, (props: Record<string, never>) => unknown>>>()const delegatedProps = reactiveOmit(props, 'items')const forwarded = useForwardPropsEmits(delegatedProps, emits)</script><template> <admin-tabs v-bind="forwarded"> <admin-tabs-list> <admin-tabs-trigger v-for="item in props.items" :key="item.value" :disabled="item.disabled" :value="item.value" > {{ item.label }} </admin-tabs-trigger> </admin-tabs-list> <admin-tabs-content v-for="item in props.items" :key="item.value" :value="item.value"> <slot :name="`panel-${item.value}` as `panel-${TValue}`"></slot> </admin-tabs-content> </admin-tabs></template>需開啟 strictVModel
型別檢查需開啟 strictVModel(若已開啟 strictTemplates 則已包含)。設定見 Type-safe。下列同時含合法綁定與刻意錯誤綁定;錯誤處以 @vue-expect-error 標示。
<script setup lang="ts">import { ref } from 'vue'import TypedAdminTabs from './typed-admin-tabs-demo.vue'const items = [ { value: 'overview', label: '總覽' }, { value: 'settings', label: '設定' }] as constconst active = ref<(typeof items)[number]['value']>('overview')const wrong = ref('unknown')const wrongDefault = 'unknown'</script><template> <div class="flex w-full max-w-md flex-col gap-6"> <typed-admin-tabs v-model="active" :items="items"> <template #panel-overview>總覽內容</template> <template #panel-settings>設定內容</template> </typed-admin-tabs> <!-- @vue-expect-error value 不在 items 的 value union 內 --> <typed-admin-tabs v-model="wrong" :items="items"> <template #panel-overview>總覽內容</template> <template #panel-settings>設定內容</template> </typed-admin-tabs> <!-- @vue-expect-error defaultValue 不在 items 的 value union 內 --> <typed-admin-tabs :default-value="wrongDefault" :items="items"> <template #panel-overview>總覽內容</template> <template #panel-settings>設定內容</template> </typed-admin-tabs> <typed-admin-tabs v-model="active" :items="items"> <template #panel-overview>總覽內容</template> <template #panel-settings>設定內容</template> <!-- @vue-expect-error slot 名不在 items 的 panel-<value> 聯集內 --> <template #panel-typo>不存在的 slot</template> </typed-admin-tabs> </div></template>API 參考
組成
<template> <admin-tabs default-value="overview"> <admin-tabs-list> <admin-tabs-trigger value="overview">概覽</admin-tabs-trigger> <admin-tabs-trigger value="details">明細</admin-tabs-trigger> </admin-tabs-list> <admin-tabs-content value="overview">概覽內容</admin-tabs-content> <admin-tabs-content value="details">明細內容</admin-tabs-content> </admin-tabs></template>admin-tabs
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | ClassValue | — | 根元素之自訂 class |
defaultValue | TModelValue | undefined | — | The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs |
orientation | DataOrientation | undefined | horizontal | The orientation the tabs are laid out. Mainly so arrow navigation is done accordingly (left & right vs. up & down) |
dir | Direction | undefined | — | The reading direction of the combobox when applicable. If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
activationMode | "automatic" | "manual" | undefined | automatic | Whether a tab is activated automatically (on focus) or manually (on click). |
modelValue | TModelValue | undefined | — | The controlled value of the tab to activate. Can be bind as `v-model`. |
unmountOnHide | boolean | undefined | `true` | When `true`, the element will be unmounted on closed state. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
size | "xs" | "sm" | undefined | "xs" | tabs trigger 尺寸(透過 context 傳遞給所有 trigger) |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
update:modelValue | TModelValue | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-tabs-list
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
loop | boolean | undefined | — | When `true`, keyboard navigation will loop from last tab to first, and vice versa. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | 根元素之自訂 class |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-tabs-trigger
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value* | StringOrNumber | — | A unique value that associates the trigger with a content. |
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with the tab. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | 根元素之自訂 class |
badge | boolean | undefined | false | 設為 `true` 時於 trigger 右側顯示 badge |
badgeValue | number | undefined | — | badge 顯示的數字(搭配 `badge: true` 使用) |
* 代表必填
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-tabs-content
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value* | StringOrNumber | — | A unique value that associates the content with a trigger. |
forceMount | boolean | undefined | — | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | 根元素之自訂 class |
* 代表必填
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |