From a54c3e49fe782e329f9245e496c336143e3e4b23 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Thu, 17 Oct 2024 17:09:59 +0200 Subject: [PATCH] feat(Button): handle `avatar` prop --- docs/content/3.components/button.md | 34 ++++++++++++++++ playground/app/pages/components/button.vue | 40 ++++++++++++++++++- src/runtime/components/Button.vue | 2 + src/runtime/composables/useComponentIcons.ts | 3 ++ src/theme/button.ts | 7 ++++ test/components/Button.spec.ts | 6 +++ .../__snapshots__/Button.spec.ts.snap | 40 +++++++++++++++++++ 7 files changed, 131 insertions(+), 1 deletion(-) diff --git a/docs/content/3.components/button.md b/docs/content/3.components/button.md index 3b53e050..5de5abfa 100644 --- a/docs/content/3.components/button.md +++ b/docs/content/3.components/button.md @@ -125,6 +125,40 @@ props: --- :: +### Avatar + +Use the `avatar` prop to show an [Avatar](/components/avatar) inside the Button. + +::component-code +--- +prettier: true +props: + avatar: + src: 'https://github.com/nuxt.png' + size: md + color: neutral + variant: outline +slots: + default: | + + Button +--- +:: + +The `label` as prop or slot is optional so you can use the Button as an avatar-only button. + +::component-code +--- +prettier: true +props: + avatar: + src: 'https://github.com/nuxt.png' + size: md + color: neutral + variant: outline +--- +:: + ### Loading Use the `loading` prop to show a loading icon and disable the Button. diff --git a/playground/app/pages/components/button.vue b/playground/app/pages/components/button.vue index cc591736..3bc25774 100644 --- a/playground/app/pages/components/button.vue +++ b/playground/app/pages/components/button.vue @@ -32,10 +32,15 @@ function onClick() {
- + Loading
+
+ + Loading auto + +
Loading @@ -54,12 +59,25 @@ function onClick() { color="neutral" />
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/src/runtime/components/Button.vue b/src/runtime/components/Button.vue index 02bb2ff7..9149b2a6 100644 --- a/src/runtime/components/Button.vue +++ b/src/runtime/components/Button.vue @@ -5,6 +5,7 @@ import _appConfig from '#build/app.config' import theme from '#build/ui/button' import type { LinkProps } from './Link.vue' import type { UseComponentIconsProps } from '../composables/useComponentIcons' +import type { AvatarProps } from '../types' import type { PartialString } from '../types/utils' import { formLoadingInjectionKey } from '../composables/useFormField' @@ -99,6 +100,7 @@ const ui = computed(() => button({ > + diff --git a/src/runtime/composables/useComponentIcons.ts b/src/runtime/composables/useComponentIcons.ts index 4322b4c5..7a75ce76 100644 --- a/src/runtime/composables/useComponentIcons.ts +++ b/src/runtime/composables/useComponentIcons.ts @@ -1,9 +1,12 @@ import { computed, toValue, type MaybeRefOrGetter } from 'vue' import { useAppConfig } from '#imports' +import type { AvatarProps } from '../types' export interface UseComponentIconsProps { /** Display an icon based on the `leading` and `trailing` props. */ icon?: string + /** Display an avatar on the left side. */ + avatar?: AvatarProps /** When `true`, the icon will be displayed on the left side. */ leading?: boolean /** Display an icon on the left side. */ diff --git a/src/theme/button.ts b/src/theme/button.ts index 3f1293d8..ec127e0b 100644 --- a/src/theme/button.ts +++ b/src/theme/button.ts @@ -7,6 +7,7 @@ export default (options: Required) => ({ label: 'truncate', leadingIcon: 'shrink-0', leadingAvatar: 'shrink-0', + leadingAvatarSize: '', trailingIcon: 'shrink-0' }, variants: { @@ -27,32 +28,38 @@ export default (options: Required) => ({ xs: { base: 'px-2 py-1 text-xs gap-1', leadingIcon: 'size-4', + leadingAvatarSize: '3xs', trailingIcon: 'size-4' }, sm: { base: 'px-2.5 py-1.5 text-xs gap-1.5', leadingIcon: 'size-4', + leadingAvatarSize: '3xs', trailingIcon: 'size-4' }, md: { base: 'px-2.5 py-1.5 text-sm gap-1.5', leadingIcon: 'size-5', + leadingAvatarSize: '2xs', trailingIcon: 'size-5' }, lg: { base: 'px-3 py-2 text-sm gap-2', leadingIcon: 'size-5', + leadingAvatarSize: '2xs', trailingIcon: 'size-5' }, xl: { base: 'px-3 py-2 text-base gap-2', leadingIcon: 'size-6', + leadingAvatarSize: 'xs', trailingIcon: 'size-6' } }, block: { true: { base: 'w-full justify-center', + leadingAvatarSize: 'xs', trailingIcon: 'ms-auto' } }, diff --git a/test/components/Button.spec.ts b/test/components/Button.spec.ts index dbfe7e50..4bdda5d6 100644 --- a/test/components/Button.spec.ts +++ b/test/components/Button.spec.ts @@ -25,7 +25,13 @@ describe('Button', () => { ['with leadingIcon', { props: { leadingIcon: 'i-heroicons-arrow-left' } }], ['with trailing and icon', { props: { trailing: true, icon: 'i-heroicons-arrow-right' } }], ['with trailingIcon', { props: { trailingIcon: 'i-heroicons-arrow-right' } }], + ['with avatar', { props: { avatar: { src: 'https://github.com/benjamincanac.png' } } }], + ['with avatar and leadingIcon', { props: { avatar: { src: 'https://github.com/benjamincanac.png' }, leadingIcon: 'i-heroicons-arrow-left' } }], + ['with avatar and trailingIcon', { props: { avatar: { src: 'https://github.com/benjamincanac.png' }, trailingIcon: 'i-heroicons-arrow-right' } }], ['with loading', { props: { loading: true } }], + ['with loading and avatar', { props: { loading: true, avatar: { src: 'https://github.com/benjamincanac.png' } } }], + ['with loading trailing', { props: { loading: true, trailing: true } }], + ['with loading trailing and avatar', { props: { loading: true, trailing: true, avatar: { src: 'https://github.com/benjamincanac.png' } } }], ['with loadingIcon', { props: { loading: true, loadingIcon: 'i-heroicons-sparkles' } }], ['with disabled', { props: { label: 'Button', disabled: true } }], ['with disabled and with link', { props: { label: 'Button', disabled: true, to: '/link' } }], diff --git a/test/components/__snapshots__/Button.spec.ts.snap b/test/components/__snapshots__/Button.spec.ts.snap index 9bea54a2..afb8c76f 100644 --- a/test/components/__snapshots__/Button.spec.ts.snap +++ b/test/components/__snapshots__/Button.spec.ts.snap @@ -1,5 +1,25 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`Button > renders with avatar and leadingIcon correctly 1`] = ` +"" +`; + +exports[`Button > renders with avatar and trailingIcon correctly 1`] = ` +"" +`; + +exports[`Button > renders with avatar correctly 1`] = ` +"" +`; + exports[`Button > renders with block correctly 1`] = ` "" `; +exports[`Button > renders with loading and avatar correctly 1`] = ` +"" +`; + exports[`Button > renders with loading correctly 1`] = ` "" `; +exports[`Button > renders with loading trailing and avatar correctly 1`] = ` +"" +`; + +exports[`Button > renders with loading trailing correctly 1`] = ` +"" +`; + exports[`Button > renders with loadingIcon correctly 1`] = ` "