From 62ab01655ca8494a2856477f9724ea27d541e9ff Mon Sep 17 00:00:00 2001 From: J-Michalek <71264422+J-Michalek@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:55:27 +0200 Subject: [PATCH] feat(Tabs): add badge on items (#4553) Co-authored-by: Benjamin Canac --- docs/content/3.components/tabs.md | 3 +- playground/app/pages/components/tabs.vue | 3 +- src/runtime/components/Tabs.vue | 22 ++- src/theme/tabs.ts | 6 +- test/components/Tabs.spec.ts | 3 +- .../__snapshots__/Tabs-vue.spec.ts.snap | 183 +++++++++++++++--- .../__snapshots__/Tabs.spec.ts.snap | 183 +++++++++++++++--- 7 files changed, 348 insertions(+), 55 deletions(-) diff --git a/docs/content/3.components/tabs.md b/docs/content/3.components/tabs.md index f555de40..026a5f9a 100644 --- a/docs/content/3.components/tabs.md +++ b/docs/content/3.components/tabs.md @@ -19,12 +19,13 @@ Use the `items` prop as an array of objects with the following properties: - `label?: string`{lang="ts-type"} - `icon?: string`{lang="ts-type"} - `avatar?: AvatarProps`{lang="ts-type"} +- `badge?: string | number | BadgeProps`{lang="ts-type"} - `content?: string`{lang="ts-type"} - `value?: string | number`{lang="ts-type"} - `disabled?: boolean`{lang="ts-type"} - [`slot?: string`{lang="ts-type"}](#with-custom-slot) - `class?: any`{lang="ts-type"} -- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, label?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"} +- `ui?: { trigger?: ClassNameValue, leadingIcon?: ClassNameValue, leadingAvatar?: ClassNameValue, leadingAvatarSize?: ClassNameValue, label?: ClassNameValue, trailingBadge?: ClassNameValue, trailingBadgeSize?: ClassNameValue, content?: ClassNameValue }`{lang="ts-type"} ::component-code --- diff --git a/playground/app/pages/components/tabs.vue b/playground/app/pages/components/tabs.vue index b5517f76..d3c21932 100644 --- a/playground/app/pages/components/tabs.vue +++ b/playground/app/pages/components/tabs.vue @@ -25,7 +25,8 @@ const items = [{ label: 'Tab3', icon: 'i-lucide-bell', content: 'Finally, this is the content for Tab3', - slot: 'custom' as const + slot: 'custom' as const, + badge: '300' }] diff --git a/src/runtime/components/Tabs.vue b/src/runtime/components/Tabs.vue index f7acf755..f2c8e976 100644 --- a/src/runtime/components/Tabs.vue +++ b/src/runtime/components/Tabs.vue @@ -3,7 +3,7 @@ import type { TabsRootProps, TabsRootEmits } from 'reka-ui' import type { AppConfig } from '@nuxt/schema' import theme from '#build/ui/tabs' -import type { AvatarProps } from '../types' +import type { AvatarProps, BadgeProps } from '../types' import type { DynamicSlots, ComponentConfig } from '../types/utils' type Tabs = ComponentConfig @@ -15,13 +15,18 @@ export interface TabsItem { */ icon?: string avatar?: AvatarProps + /** + * Display a badge on the item. + * `{ size: 'sm', color: 'neutral', variant: 'outline' }`{lang="ts-type"} + */ + badge?: string | number | BadgeProps slot?: string content?: string /** A unique value for the tab item. Defaults to the index. */ value?: string | number disabled?: boolean class?: any - ui?: Pick + ui?: Pick [key: string]: any } @@ -134,14 +139,23 @@ defineExpose({ > - + {{ get(item, props.labelKey as string) }} - + + + diff --git a/src/theme/tabs.ts b/src/theme/tabs.ts index 1bff5b47..e8b33f2e 100644 --- a/src/theme/tabs.ts +++ b/src/theme/tabs.ts @@ -6,11 +6,13 @@ export default (options: Required) => ({ 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', options.theme.transitions && 'transition-colors'], - content: 'focus:outline-none w-full', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', leadingAvatarSize: '', - label: 'truncate' + label: 'truncate', + trailingBadge: 'shrink-0', + trailingBadgeSize: 'sm', + content: 'focus:outline-none w-full' }, variants: { color: { diff --git a/test/components/Tabs.spec.ts b/test/components/Tabs.spec.ts index e72a98e9..f9eb18dc 100644 --- a/test/components/Tabs.spec.ts +++ b/test/components/Tabs.spec.ts @@ -22,7 +22,8 @@ describe('Tabs', () => { label: 'Tab3', icon: 'i-lucide-bell', content: 'Finally, this is the content for Tab3', - slot: 'custom' + slot: 'custom', + badge: 'badge' }] const props = { items } diff --git a/test/components/__snapshots__/Tabs-vue.spec.ts.snap b/test/components/__snapshots__/Tabs-vue.spec.ts.snap index 47d1d883..e8c804a9 100644 --- a/test/components/__snapshots__/Tabs-vue.spec.ts.snap +++ b/test/components/__snapshots__/Tabs-vue.spec.ts.snap @@ -3,7 +3,13 @@ exports[`Tabs > renders with as correctly 1`] = ` "
-
+
This is the content shown for Tab1