From 0d1a76e3c69e08534abb295b96548e67cfbea00c Mon Sep 17 00:00:00 2001 From: Malik-Jouda <48517781+Malik-Jouda@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:16:20 +0200 Subject: [PATCH] feat(Badge): handle `icon` prop (#2594) Co-authored-by: malik jouda --- docs/content/2.components/badge.md | 68 ++++++++++++++++++++ src/runtime/components/elements/Badge.vue | 77 ++++++++++++++++++++++- src/runtime/ui.config/elements/badge.ts | 15 +++++ 3 files changed, 158 insertions(+), 2 deletions(-) diff --git a/docs/content/2.components/badge.md b/docs/content/2.components/badge.md index 27e2f490..c495b1c7 100644 --- a/docs/content/2.components/badge.md +++ b/docs/content/2.components/badge.md @@ -141,6 +141,74 @@ Badge You can customize the whole [preset](#preset) by using the `ui` prop. :: +### Icon + +Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`. + +Use the `leading` and `trailing` props to set the icon position or the `leading-icon` and `trailing-icon` props to set a different icon for each position. + +::component-card +--- +props: + icon: 'i-heroicons-rocket-launch' + size: 'sm' + color: 'primary' + variant: 'solid' + label: Badge + trailing: false +options: + - name: variant + restriction: only + values: + - solid +excludedProps: + - icon + - label +--- +:: + +## Slots + +### `leading` + +Use the `#leading` slot to set the content of the leading icon. + +::component-card +--- +slots: + leading: +baseProps: + color: 'gray' +props: + label: Badge + color: 'gray' +excludedProps: + - color +--- + +#leading + :u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"} +:: + +### `trailing` + +Use the `#trailing` slot to set the content of the trailing icon. + +::component-card +--- +slots: + trailing: +props: + label: Badge + color: 'gray' +excludedProps: + - color +--- + +#trailing + :u-icon{name="i-heroicons-rocket-launch" class="w-4 h-4"} +:: + ## Props :component-props diff --git a/src/runtime/components/elements/Badge.vue b/src/runtime/components/elements/Badge.vue index d24ffe22..833175c7 100644 --- a/src/runtime/components/elements/Badge.vue +++ b/src/runtime/components/elements/Badge.vue @@ -1,6 +1,18 @@ @@ -8,6 +20,7 @@ import { computed, toRef, defineComponent } from 'vue' import type { PropType } from 'vue' import { twMerge, twJoin } from 'tailwind-merge' +import UIcon from '../elements/Icon.vue' import { useUI } from '../../composables/useUI' import { mergeConfig } from '../../utils' import { useInjectButtonGroup } from '../../composables/useButtonGroup' @@ -19,6 +32,9 @@ import { badge } from '#ui/ui.config' const config = mergeConfig(appConfig.ui.strategy, appConfig.ui.badge, badge) export default defineComponent({ + components: { + UIcon + }, inheritAttrs: false, props: { size: { @@ -49,6 +65,26 @@ export default defineComponent({ type: [String, Number], default: null }, + icon: { + type: String, + default: null + }, + leadingIcon: { + type: String, + default: null + }, + trailingIcon: { + type: String, + default: null + }, + trailing: { + type: Boolean, + default: false + }, + leading: { + type: Boolean, + default: false + }, class: { type: [String, Object, Array] as PropType, default: () => '' @@ -63,6 +99,14 @@ export default defineComponent({ const { size, rounded } = useInjectButtonGroup({ ui, props }) + const isLeading = computed(() => { + return (props.icon && props.leading) || (props.icon && !props.trailing) || !props.trailing || props.leadingIcon + }) + + const isTrailing = computed(() => { + return (props.icon && props.trailing) || props.trailing || props.trailingIcon + }) + const badgeClass = computed(() => { const variant = ui.value.color?.[props.color as string]?.[props.variant as string] || ui.value.variant[props.variant] @@ -71,13 +115,42 @@ export default defineComponent({ ui.value.font, rounded.value, ui.value.size[size.value], + ui.value.gap[size.value], variant?.replaceAll('{color}', props.color) ), props.class) }) + const leadingIconName = computed(() => { + return props.leadingIcon || props.icon + }) + + const trailingIconName = computed(() => { + return props.trailingIcon || props.icon + }) + + const leadingIconClass = computed(() => { + return twJoin( + ui.value.icon.base, + ui.value.icon.size[size.value] + ) + }) + + const trailingIconClass = computed(() => { + return twJoin( + ui.value.icon.base, + ui.value.icon.size[size.value] + ) + }) + return { attrs, - badgeClass + isLeading, + isTrailing, + badgeClass, + leadingIconName, + trailingIconName, + leadingIconClass, + trailingIconClass } } }) diff --git a/src/runtime/ui.config/elements/badge.ts b/src/runtime/ui.config/elements/badge.ts index c89de515..3769fe4f 100644 --- a/src/runtime/ui.config/elements/badge.ts +++ b/src/runtime/ui.config/elements/badge.ts @@ -8,6 +8,12 @@ export default { md: 'text-sm px-2 py-1', lg: 'text-sm px-2.5 py-1.5' }, + gap: { + xs: 'gap-0.5', + sm: 'gap-1', + md: 'gap-1', + lg: 'gap-1.5' + }, color: { white: { solid: 'ring-1 ring-inset ring-gray-300 dark:ring-gray-700 text-gray-900 dark:text-white bg-white dark:bg-gray-900' @@ -25,6 +31,15 @@ export default { soft: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400', subtle: 'bg-{color}-50 dark:bg-{color}-400 dark:bg-opacity-10 text-{color}-500 dark:text-{color}-400 ring-1 ring-inset ring-{color}-500 dark:ring-{color}-400 ring-opacity-25 dark:ring-opacity-25' }, + icon: { + base: 'flex-shrink-0', + size: { + xs: 'h-4 w-4', + sm: 'h-4 w-4', + md: 'h-5 w-5', + lg: 'h-5 w-5' + } + }, default: { size: 'sm', variant: 'solid',