Skip to content

Commit 1fbd4e1

Browse files
committed
feat(NeBadgeV2): add NeBadgeV2 component
1 parent 78e6612 commit 1fbd4e1

File tree

3 files changed

+210
-0
lines changed

3 files changed

+210
-0
lines changed

src/components/NeBadgeV2.vue

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<!--
2+
Copyright (C) 2025 Nethesis S.r.l.
3+
SPDX-License-Identifier: GPL-3.0-or-later
4+
-->
5+
6+
<script lang="ts" setup>
7+
import { computed } from 'vue'
8+
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
9+
import { faXmark } from '@fortawesome/free-solid-svg-icons'
10+
11+
const {
12+
size = 'sm',
13+
kind = 'indigo',
14+
pill = true,
15+
dismissable = false,
16+
customKindClasses = '',
17+
dismissAriaLabel = 'Dismiss'
18+
} = defineProps<{
19+
size?: 'xs' | 'sm' | 'md'
20+
kind?: 'primary' | 'indigo' | 'gray' | 'green' | 'amber' | 'rose' | 'blue' | 'custom'
21+
pill?: boolean
22+
dismissable?: boolean
23+
customKindClasses?: string
24+
dismissAriaLabel?: string
25+
}>()
26+
27+
const emit = defineEmits(['dismiss'])
28+
29+
const textClasses = computed(() => {
30+
switch (size) {
31+
case 'xs':
32+
return 'text-xs'
33+
case 'md':
34+
return 'text-base'
35+
case 'sm':
36+
default:
37+
return 'text-sm'
38+
}
39+
})
40+
41+
const paddingClasses = computed(() => {
42+
////
43+
return 'px-2.5'
44+
})
45+
46+
const spacingClasses = computed(() => {
47+
return 'gap-x-1'
48+
})
49+
50+
const kindClasses = computed(() => {
51+
switch (kind) {
52+
case 'primary':
53+
return 'bg-primary-100 text-primary-800 dark:bg-primary-700 dark:text-primary-100'
54+
case 'gray':
55+
return 'bg-gray-200 text-gray-800 dark:bg-gray-600 dark:text-gray-100'
56+
case 'green':
57+
return 'bg-green-100 text-green-800 dark:bg-green-700 dark:text-green-50'
58+
case 'amber':
59+
return 'bg-amber-100 text-amber-800 dark:bg-amber-700 dark:text-amber-50'
60+
case 'rose':
61+
return 'bg-rose-100 text-rose-800 dark:bg-rose-700 dark:text-rose-100'
62+
case 'blue':
63+
return 'bg-blue-100 text-blue-800 dark:bg-blue-700 dark:text-blue-100'
64+
case 'custom':
65+
return `${customKindClasses}`
66+
case 'indigo':
67+
default:
68+
return 'bg-indigo-100 text-indigo-800 dark:bg-indigo-700 dark:text-indigo-100'
69+
}
70+
})
71+
72+
const dismissButtonClasses = computed(() => {
73+
switch (kind) {
74+
case 'primary':
75+
return 'hover:bg-primary-200 hover:dark:bg-primary-600'
76+
case 'gray':
77+
return 'hover:bg-gray-300 hover:dark:bg-gray-500'
78+
case 'green':
79+
return 'hover:bg-green-200 hover:dark:bg-green-600'
80+
case 'amber':
81+
return 'hover:bg-amber-200 hover:dark:bg-amber-600'
82+
case 'rose':
83+
return 'hover:bg-rose-200 hover:dark:bg-rose-600'
84+
case 'blue':
85+
return 'hover:bg-blue-200 hover:dark:bg-blue-600'
86+
case 'indigo':
87+
default:
88+
return 'hover:bg-indigo-200 hover:dark:bg-indigo-500'
89+
}
90+
})
91+
</script>
92+
93+
<template>
94+
<span
95+
:class="[
96+
textClasses,
97+
spacingClasses,
98+
paddingClasses,
99+
kindClasses,
100+
pill ? 'rounded-full' : 'rounded'
101+
]"
102+
class="flex w-fit items-center py-0.5 font-medium"
103+
>
104+
<slot />
105+
<button
106+
v-if="dismissable"
107+
:class="`inline-flex rounded focus:ring-2 focus:ring-offset-2 focus:outline-hidden ${dismissButtonClasses}`"
108+
type="button"
109+
@click="emit('dismiss')"
110+
>
111+
<span class="sr-only">{{ dismissAriaLabel }}</span>
112+
<FontAwesomeIcon :icon="faXmark" class="size-4" aria-hidden="true" />
113+
</button>
114+
</span>
115+
</template>

src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export { default as NeListbox } from './components/NeListbox.vue'
3939
export { default as NeDropdownFilter } from './components/NeDropdownFilter.vue'
4040
export { default as NeSortDropdown } from './components/NeSortDropdown.vue'
4141
export { default as NeAvatar } from './components/NeAvatar.vue'
42+
export { default as NeBadgeV2 } from './components/NeBadgeV2.vue'
4243

4344
// types export
4445
export type { NeComboboxOption } from './components/NeCombobox.vue'

stories/NeBadgeV2.stories.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (C) 2025 Nethesis S.r.l.
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
import { Meta, StoryObj } from '@storybook/vue3-vite'
5+
import { NeBadgeV2 } from '../src/main'
6+
7+
const meta: Meta<typeof NeBadgeV2> = {
8+
title: 'NeBadgeV2',
9+
component: NeBadgeV2,
10+
tags: ['autodocs'],
11+
argTypes: {
12+
size: {
13+
options: ['xs', 'sm', 'md'],
14+
control: { type: 'inline-radio' }
15+
},
16+
kind: {
17+
options: ['primary', 'gray', 'indigo', 'green', 'amber', 'rose', 'blue', 'custom'],
18+
control: { type: 'inline-radio' }
19+
}
20+
},
21+
args: {
22+
size: 'sm',
23+
kind: 'indigo',
24+
pill: true,
25+
dismissable: false,
26+
customKindClasses: '',
27+
dismissAriaLabel: 'Dismiss'
28+
}
29+
}
30+
31+
export default meta
32+
type Story = StoryObj<typeof meta>
33+
34+
const defaultTemplate = `<NeBadgeV2 v-bind="args">Badge</NeBadgeV2>`
35+
36+
export const Default: Story = {
37+
render: (args) => ({
38+
components: { NeBadgeV2 },
39+
setup() {
40+
return { args }
41+
},
42+
template: defaultTemplate
43+
}),
44+
args: {}
45+
}
46+
47+
export const Kinds: Story = {
48+
render: (args) => ({
49+
components: { NeBadgeV2 },
50+
setup() {
51+
return { args }
52+
},
53+
template: `
54+
<div class="flex flex-wrap gap-8">
55+
<NeBadgeV2 v-bind="args" kind="indigo">indigo</NeBadgeV2>
56+
<NeBadgeV2 v-bind="args" kind="primary">primary</NeBadgeV2>
57+
<NeBadgeV2 v-bind="args" kind="gray">gray</NeBadgeV2>
58+
<NeBadgeV2 v-bind="args" kind="green">green</NeBadgeV2>
59+
<NeBadgeV2 v-bind="args" kind="amber">amber</NeBadgeV2>
60+
<NeBadgeV2 v-bind="args" kind="rose">rose</NeBadgeV2>
61+
<NeBadgeV2 v-bind="args" kind="blue">blue</NeBadgeV2>
62+
<NeBadgeV2 v-bind="args" kind="custom" customKindClasses="bg-fuchsia-100 text-fuchsia-700 dark:bg-fuchsia-700 dark:text-fuchsia-50">custom</NeBadgeV2>
63+
</div>
64+
`
65+
}),
66+
args: {}
67+
}
68+
69+
export const CustomKind: Story = {
70+
render: (args) => ({
71+
components: { NeBadgeV2 },
72+
setup() {
73+
return { args }
74+
},
75+
template: `<NeBadgeV2 v-bind="args">Custom badge</NeBadgeV2>`
76+
}),
77+
args: {
78+
kind: 'custom',
79+
customKindClasses: 'text-white bg-linear-to-br from-fuchsia-500 to-blue-500'
80+
}
81+
}
82+
83+
export const Dismissable: Story = {
84+
render: (args) => ({
85+
components: { NeBadgeV2 },
86+
setup() {
87+
return { args }
88+
},
89+
template: defaultTemplate
90+
}),
91+
args: {
92+
dismissable: true
93+
}
94+
}

0 commit comments

Comments
 (0)