选项卡

选项卡GitHub
一组标签面板,每次显示一个。

用法

items 属性用作具有以下属性的对象数组

  • label?: string
  • icon?: string
  • avatar?: AvatarProps
  • badge?: string | number | BadgeProps
  • content?: string
  • value?: string | number
  • disabled?: boolean
  • slot?: string
  • class?: any
  • ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, leadingAvatarSize?: ClassNameValue, label?: ClassNameValue, trailingBadge?: ClassNameValue, trailingBadgeSize?: ClassNameValue, content?: ClassNameValue }
这是账户内容。
<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account',
    icon: 'i-lucide-user',
    content: 'This is the account content.'
  },
  {
    label: 'Password',
    icon: 'i-lucide-lock',
    content: 'This is the password content.'
  }
])
</script>

<template>
  <UTabs :items="items" class="w-full" />
</template>

内容

content 属性设置为 false 可以将选项卡转换为仅切换的控件,而不显示任何内容。默认为 true

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account',
    icon: 'i-lucide-user',
    content: 'This is the account content.'
  },
  {
    label: 'Password',
    icon: 'i-lucide-lock',
    content: 'This is the password content.'
  }
])
</script>

<template>
  <UTabs :content="false" :items="items" class="w-full" />
</template>

卸载

使用 unmount-on-hide 属性可以防止选项卡折叠时内容被卸载。默认为 true

这是账户内容。
<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account',
    icon: 'i-lucide-user',
    content: 'This is the account content.'
  },
  {
    label: 'Password',
    icon: 'i-lucide-lock',
    content: 'This is the password content.'
  }
])
</script>

<template>
  <UTabs :unmount-on-hide="false" :items="items" class="w-full" />
</template>
您可以检查 DOM 以查看每个项目内容的渲染情况。

颜色

使用 color 属性更改选项卡的颜色。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account'
  },
  {
    label: 'Password'
  }
])
</script>

<template>
  <UTabs color="neutral" :content="false" :items="items" class="w-full" />
</template>

变体

使用 variant 属性更改选项卡的变体。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account'
  },
  {
    label: 'Password'
  }
])
</script>

<template>
  <UTabs color="neutral" variant="link" :content="false" :items="items" class="w-full" />
</template>

尺寸

使用 size 属性更改选项卡的大小。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account'
  },
  {
    label: 'Password'
  }
])
</script>

<template>
  <UTabs size="md" variant="pill" :content="false" :items="items" class="w-full" />
</template>

方向

使用 orientation 属性更改选项卡的方向。默认为 horizontal

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = ref<TabsItem[]>([
  {
    label: 'Account'
  },
  {
    label: 'Password'
  }
])
</script>

<template>
  <UTabs orientation="vertical" variant="pill" :content="false" :items="items" class="w-full" />
</template>

示例

控制活动项目

您可以通过使用 default-value 属性或 v-model 指令(带项目索引)来控制活动项。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const route = useRoute()
const router = useRouter()

const items: TabsItem[] = [
  {
    label: 'Account',
    value: 'account'
  },
  {
    label: 'Password',
    value: 'password'
  }
]

const active = computed({
  get() {
    return (route.query.tab as string) || 'account'
  },
  set(tab) {
    // Hash is specified here to prevent the page from scrolling to the top
    router.push({
      path: '/components/tabs',
      query: { tab },
      hash: '#control-active-item'
    })
  }
})
</script>

<template>
  <UTabs v-model="active" :content="false" :items="items" class="w-full" />
</template>

使用 `content` 插槽

使用 `#content` 插槽可自定义每个项目的内容。

这是账户选项卡。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items: TabsItem[] = [
  {
    label: 'Account',
    icon: 'i-lucide-user'
  },
  {
    label: 'Password',
    icon: 'i-lucide-lock'
  }
]
</script>

<template>
  <UTabs :items="items" class="w-full">
    <template #content="{ item }">
      <p>This is the {{ item.label }} tab.</p>
    </template>
  </UTabs>
</template>

带自定义插槽

使用 slot 属性自定义特定项。

您将可以使用以下插槽

  • #{{ item.slot }}

在此处更改您的账户信息。完成后点击保存。

<script setup lang="ts">
import type { TabsItem } from '@nuxt/ui'

const items = [
  {
    label: 'Account',
    description: 'Make changes to your account here. Click save when you\'re done.',
    icon: 'i-lucide-user',
    slot: 'account' as const
  },
  {
    label: 'Password',
    description: 'Change your password here. After saving, you\'ll be logged out.',
    icon: 'i-lucide-lock',
    slot: 'password' as const
  }
] satisfies TabsItem[]

const state = reactive({
  name: 'Benjamin Canac',
  username: 'benjamincanac',
  currentPassword: '',
  newPassword: '',
  confirmPassword: ''
})
</script>

<template>
  <UTabs :items="items" variant="link" :ui="{ trigger: 'grow' }" class="gap-4 w-full">
    <template #account="{ item }">
      <p class="text-muted mb-4">
        {{ item.description }}
      </p>

      <UForm :state="state" class="flex flex-col gap-4">
        <UFormField label="Name" name="name">
          <UInput v-model="state.name" class="w-full" />
        </UFormField>
        <UFormField label="Username" name="username">
          <UInput v-model="state.username" class="w-full" />
        </UFormField>

        <UButton label="Save changes" type="submit" variant="soft" class="self-end" />
      </UForm>
    </template>

    <template #password="{ item }">
      <p class="text-muted mb-4">
        {{ item.description }}
      </p>

      <UForm :state="state" class="flex flex-col gap-4">
        <UFormField label="Current Password" name="current" required>
          <UInput v-model="state.currentPassword" type="password" required class="w-full" />
        </UFormField>
        <UFormField label="New Password" name="new" required>
          <UInput v-model="state.newPassword" type="password" required class="w-full" />
        </UFormField>
        <UFormField label="Confirm Password" name="confirm" required>
          <UInput v-model="state.confirmPassword" type="password" required class="w-full" />
        </UFormField>

        <UButton label="Change password" type="submit" variant="soft" class="self-end" />
      </UForm>
    </template>
  </UTabs>
</template>

API

属性

属性默认值类型
as

'div'

any

此组件应渲染为的元素或组件。

items

TabsItem[]

color

'primary'

"error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"

variant

'pill'

"pill" | "link"

尺寸

'md'

"sm" | "xs | "md" | "lg" | "xl"

orientation

'horizontal'

"horizontal" | "vertical"

选项卡的方向。

内容

true

boolean

选项卡的内容,可以禁用以防止渲染内容。

labelKey

'label'

string

用于从项中获取标签的键。

defaultValue

'0'

string | number

选项卡初始渲染时应激活的值。当您不需要控制选项卡状态时使用。

modelValue

string | number

要激活的选项卡的受控值。可以绑定为 v-model

activationMode

automatic

"automatic" | "manual"

选项卡是自动激活(聚焦时)还是手动激活(点击时)。

unmountOnHide

true

boolean

当为 `true` 时,元素在关闭状态下将被卸载。

ui

{ root?: ClassNameValue; list?: ClassNameValue; indicator?: ClassNameValue; trigger?: ClassNameValue; leadingIcon?: ClassNameValue; leadingAvatar?: ClassNameValue; leadingAvatarSize?: ClassNameValue; label?: ClassNameValue; trailingBadge?: ClassNameValue; trailingBadgeSize?: ClassNameValue; content?: ClassNameValue; }

插槽

插槽类型
前置

{ item: TabsItem; index: number; }

默认

{ item: TabsItem; index: number; }

尾部

{ item: TabsItem; index: number; }

内容

{ item: TabsItem; index: number; }

列表前缀

{}

列表后缀

{}

事件

事件类型
update:modelValue

[payload: string | number]

可访问属性

通过模板引用访问组件时,您可以使用以下内容:

名称类型
triggersRefRef<ComponentPublicInstance[]>

主题

app.config.ts
export default defineAppConfig({
  ui: {
    tabs: {
      slots: {
        root: 'flex items-center gap-2',
        list: 'relative flex p-1 group',
        indicator: 'absolute transition-[translate,width] duration-200',
        trigger: [
          'group relative inline-flex items-center min-w-0 data-[state=inactive]:text-muted hover:data-[state=inactive]:not-disabled:text-default font-medium rounded-md disabled:cursor-not-allowed disabled:opacity-75',
          'transition-colors'
        ],
        leadingIcon: 'shrink-0',
        leadingAvatar: 'shrink-0',
        leadingAvatarSize: '',
        label: 'truncate',
        trailingBadge: 'shrink-0',
        trailingBadgeSize: 'sm',
        content: 'focus:outline-none w-full'
      },
      variants: {
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        variant: {
          pill: {
            list: 'bg-elevated rounded-lg',
            trigger: 'grow',
            indicator: 'rounded-md shadow-xs'
          },
          link: {
            list: 'border-default',
            indicator: 'rounded-full',
            trigger: 'focus:outline-none'
          }
        },
        orientation: {
          horizontal: {
            root: 'flex-col',
            list: 'w-full',
            indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)',
            trigger: 'justify-center'
          },
          vertical: {
            list: 'flex-col',
            indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)'
          }
        },
        size: {
          xs: {
            trigger: 'px-2 py-1 text-xs gap-1',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs'
          },
          sm: {
            trigger: 'px-2.5 py-1.5 text-xs gap-1.5',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs'
          },
          md: {
            trigger: 'px-3 py-1.5 text-sm gap-1.5',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs'
          },
          lg: {
            trigger: 'px-3 py-2 text-sm gap-2',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs'
          },
          xl: {
            trigger: 'px-3 py-2 text-base gap-2',
            leadingIcon: 'size-6',
            leadingAvatarSize: 'xs'
          }
        }
      },
      compoundVariants: [
        {
          orientation: 'horizontal',
          variant: 'pill',
          class: {
            indicator: 'inset-y-1'
          }
        },
        {
          orientation: 'horizontal',
          variant: 'link',
          class: {
            list: 'border-b -mb-px',
            indicator: '-bottom-px h-px'
          }
        },
        {
          orientation: 'vertical',
          variant: 'pill',
          class: {
            indicator: 'inset-x-1',
            list: 'items-center'
          }
        },
        {
          orientation: 'vertical',
          variant: 'link',
          class: {
            list: 'border-s -ms-px',
            indicator: '-start-px w-px'
          }
        },
        {
          color: 'primary',
          variant: 'pill',
          class: {
            indicator: 'bg-primary',
            trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
          }
        },
        {
          color: 'neutral',
          variant: 'pill',
          class: {
            indicator: 'bg-inverted',
            trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
          }
        },
        {
          color: 'primary',
          variant: 'link',
          class: {
            indicator: 'bg-primary',
            trigger: 'data-[state=active]:text-primary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary'
          }
        },
        {
          color: 'neutral',
          variant: 'link',
          class: {
            indicator: 'bg-inverted',
            trigger: 'data-[state=active]:text-highlighted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted'
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        variant: 'pill',
        size: 'md'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        tabs: {
          slots: {
            root: 'flex items-center gap-2',
            list: 'relative flex p-1 group',
            indicator: 'absolute transition-[translate,width] duration-200',
            trigger: [
              'group relative inline-flex items-center min-w-0 data-[state=inactive]:text-muted hover:data-[state=inactive]:not-disabled:text-default font-medium rounded-md disabled:cursor-not-allowed disabled:opacity-75',
              'transition-colors'
            ],
            leadingIcon: 'shrink-0',
            leadingAvatar: 'shrink-0',
            leadingAvatarSize: '',
            label: 'truncate',
            trailingBadge: 'shrink-0',
            trailingBadgeSize: 'sm',
            content: 'focus:outline-none w-full'
          },
          variants: {
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              pill: {
                list: 'bg-elevated rounded-lg',
                trigger: 'grow',
                indicator: 'rounded-md shadow-xs'
              },
              link: {
                list: 'border-default',
                indicator: 'rounded-full',
                trigger: 'focus:outline-none'
              }
            },
            orientation: {
              horizontal: {
                root: 'flex-col',
                list: 'w-full',
                indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)',
                trigger: 'justify-center'
              },
              vertical: {
                list: 'flex-col',
                indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)'
              }
            },
            size: {
              xs: {
                trigger: 'px-2 py-1 text-xs gap-1',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs'
              },
              sm: {
                trigger: 'px-2.5 py-1.5 text-xs gap-1.5',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs'
              },
              md: {
                trigger: 'px-3 py-1.5 text-sm gap-1.5',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs'
              },
              lg: {
                trigger: 'px-3 py-2 text-sm gap-2',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs'
              },
              xl: {
                trigger: 'px-3 py-2 text-base gap-2',
                leadingIcon: 'size-6',
                leadingAvatarSize: 'xs'
              }
            }
          },
          compoundVariants: [
            {
              orientation: 'horizontal',
              variant: 'pill',
              class: {
                indicator: 'inset-y-1'
              }
            },
            {
              orientation: 'horizontal',
              variant: 'link',
              class: {
                list: 'border-b -mb-px',
                indicator: '-bottom-px h-px'
              }
            },
            {
              orientation: 'vertical',
              variant: 'pill',
              class: {
                indicator: 'inset-x-1',
                list: 'items-center'
              }
            },
            {
              orientation: 'vertical',
              variant: 'link',
              class: {
                list: 'border-s -ms-px',
                indicator: '-start-px w-px'
              }
            },
            {
              color: 'primary',
              variant: 'pill',
              class: {
                indicator: 'bg-primary',
                trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
              }
            },
            {
              color: 'neutral',
              variant: 'pill',
              class: {
                indicator: 'bg-inverted',
                trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
              }
            },
            {
              color: 'primary',
              variant: 'link',
              class: {
                indicator: 'bg-primary',
                trigger: 'data-[state=active]:text-primary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary'
              }
            },
            {
              color: 'neutral',
              variant: 'link',
              class: {
                indicator: 'bg-inverted',
                trigger: 'data-[state=active]:text-highlighted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            variant: 'pill',
            size: 'md'
          }
        }
      }
    })
  ]
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      ui: {
        tabs: {
          slots: {
            root: 'flex items-center gap-2',
            list: 'relative flex p-1 group',
            indicator: 'absolute transition-[translate,width] duration-200',
            trigger: [
              'group relative inline-flex items-center min-w-0 data-[state=inactive]:text-muted hover:data-[state=inactive]:not-disabled:text-default font-medium rounded-md disabled:cursor-not-allowed disabled:opacity-75',
              'transition-colors'
            ],
            leadingIcon: 'shrink-0',
            leadingAvatar: 'shrink-0',
            leadingAvatarSize: '',
            label: 'truncate',
            trailingBadge: 'shrink-0',
            trailingBadgeSize: 'sm',
            content: 'focus:outline-none w-full'
          },
          variants: {
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            variant: {
              pill: {
                list: 'bg-elevated rounded-lg',
                trigger: 'grow',
                indicator: 'rounded-md shadow-xs'
              },
              link: {
                list: 'border-default',
                indicator: 'rounded-full',
                trigger: 'focus:outline-none'
              }
            },
            orientation: {
              horizontal: {
                root: 'flex-col',
                list: 'w-full',
                indicator: 'left-0 w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position)',
                trigger: 'justify-center'
              },
              vertical: {
                list: 'flex-col',
                indicator: 'top-0 h-(--reka-tabs-indicator-size) translate-y-(--reka-tabs-indicator-position)'
              }
            },
            size: {
              xs: {
                trigger: 'px-2 py-1 text-xs gap-1',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs'
              },
              sm: {
                trigger: 'px-2.5 py-1.5 text-xs gap-1.5',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs'
              },
              md: {
                trigger: 'px-3 py-1.5 text-sm gap-1.5',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs'
              },
              lg: {
                trigger: 'px-3 py-2 text-sm gap-2',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs'
              },
              xl: {
                trigger: 'px-3 py-2 text-base gap-2',
                leadingIcon: 'size-6',
                leadingAvatarSize: 'xs'
              }
            }
          },
          compoundVariants: [
            {
              orientation: 'horizontal',
              variant: 'pill',
              class: {
                indicator: 'inset-y-1'
              }
            },
            {
              orientation: 'horizontal',
              variant: 'link',
              class: {
                list: 'border-b -mb-px',
                indicator: '-bottom-px h-px'
              }
            },
            {
              orientation: 'vertical',
              variant: 'pill',
              class: {
                indicator: 'inset-x-1',
                list: 'items-center'
              }
            },
            {
              orientation: 'vertical',
              variant: 'link',
              class: {
                list: 'border-s -ms-px',
                indicator: '-start-px w-px'
              }
            },
            {
              color: 'primary',
              variant: 'pill',
              class: {
                indicator: 'bg-primary',
                trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
              }
            },
            {
              color: 'neutral',
              variant: 'pill',
              class: {
                indicator: 'bg-inverted',
                trigger: 'data-[state=active]:text-inverted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-inverted'
              }
            },
            {
              color: 'primary',
              variant: 'link',
              class: {
                indicator: 'bg-primary',
                trigger: 'data-[state=active]:text-primary focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary'
              }
            },
            {
              color: 'neutral',
              variant: 'link',
              class: {
                indicator: 'bg-inverted',
                trigger: 'data-[state=active]:text-highlighted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-inverted'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            variant: 'pill',
            size: 'md'
          }
        }
      }
    })
  ]
})
为便于阅读,compoundVariants 中的某些颜色已省略。请在 GitHub 上查看源代码。