mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(Button): handle avatar prop
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -32,10 +32,15 @@ function onClick() {
|
||||
</UButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton loading-auto @click="onClick">
|
||||
<UButton loading>
|
||||
Loading
|
||||
</UButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton loading-auto @click="onClick">
|
||||
Loading auto
|
||||
</UButton>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton loading trailing>
|
||||
Loading
|
||||
@@ -54,12 +59,25 @@ function onClick() {
|
||||
color="neutral"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton
|
||||
v-for="variant in variants"
|
||||
:key="variant"
|
||||
:avatar="{ src: 'https://github.com/benjamincanac.png' }"
|
||||
:label="upperFirst(variant)"
|
||||
color="neutral"
|
||||
:variant="variant"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-129px]">
|
||||
<UButton v-for="size in sizes" :key="size" label="Button" :size="size" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-171px]">
|
||||
<UButton v-for="size in sizes" :key="size" icon="i-heroicons-rocket-launch" label="Button" :size="size" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-171px]">
|
||||
<UButton v-for="size in sizes" :key="size" :avatar="{ src: 'https://github.com/benjamincanac.png' }" label="Button" :size="size" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-159px]">
|
||||
<UButton
|
||||
v-for="size in sizes"
|
||||
@@ -70,9 +88,29 @@ function onClick() {
|
||||
:size="size"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-159px]">
|
||||
<UButton
|
||||
v-for="size in sizes"
|
||||
:key="size"
|
||||
:avatar="{ src: 'https://github.com/benjamincanac.png' }"
|
||||
label="Square"
|
||||
square
|
||||
:size="size"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-68px]">
|
||||
<UButton v-for="size in sizes" :key="size" icon="i-heroicons-rocket-launch" :size="size" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-[-68px]">
|
||||
<UButton
|
||||
v-for="size in sizes"
|
||||
:key="size"
|
||||
:avatar="{ src: 'https://github.com/benjamincanac.png' }"
|
||||
:size="size"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<UButton icon="i-heroicons-rocket-launch" trailing-icon="i-heroicons-chevron-down-20-solid" label="Block" block />
|
||||
</div>
|
||||
|
||||
@@ -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({
|
||||
>
|
||||
<slot name="leading">
|
||||
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
||||
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
|
||||
</slot>
|
||||
|
||||
<slot>
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -7,6 +7,7 @@ export default (options: Required<ModuleOptions>) => ({
|
||||
label: 'truncate',
|
||||
leadingIcon: 'shrink-0',
|
||||
leadingAvatar: 'shrink-0',
|
||||
leadingAvatarSize: '',
|
||||
trailingIcon: 'shrink-0'
|
||||
},
|
||||
variants: {
|
||||
@@ -27,32 +28,38 @@ export default (options: Required<ModuleOptions>) => ({
|
||||
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'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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' } }],
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Button > renders with avatar and leadingIcon correctly 1`] = `
|
||||
"<button type="button" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="iconify i-heroicons:arrow-left shrink-0 size-5" aria-hidden="true"></span>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with avatar and trailingIcon correctly 1`] = `
|
||||
"<button type="button" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-[var(--ui-bg-elevated)] size-5 text-[10px] shrink-0"><img role="img" src="https://github.com/benjamincanac.png" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-[var(--ui-text-muted)] truncate"></span></span>
|
||||
<!--v-if--><span class="iconify i-heroicons:arrow-right shrink-0 size-5" aria-hidden="true"></span>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with avatar correctly 1`] = `
|
||||
"<button type="button" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-[var(--ui-bg-elevated)] size-5 text-[10px] shrink-0"><img role="img" src="https://github.com/benjamincanac.png" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-[var(--ui-text-muted)] truncate"></span></span>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with block correctly 1`] = `
|
||||
"<button type="button" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 w-full justify-center text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)]">
|
||||
<!--v-if--><span class="truncate">Button</span>
|
||||
@@ -65,6 +85,13 @@ exports[`Button > renders with leadingIcon correctly 1`] = `
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with loading and avatar correctly 1`] = `
|
||||
"<button type="button" disabled="" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="iconify i-heroicons:arrow-path-20-solid shrink-0 size-5 animate-spin" aria-hidden="true"></span>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with loading correctly 1`] = `
|
||||
"<button type="button" disabled="" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="iconify i-heroicons:arrow-path-20-solid shrink-0 size-5 animate-spin" aria-hidden="true"></span>
|
||||
<!--v-if-->
|
||||
@@ -72,6 +99,19 @@ exports[`Button > renders with loading correctly 1`] = `
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with loading trailing and avatar correctly 1`] = `
|
||||
"<button type="button" disabled="" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="inline-flex items-center justify-center select-none overflow-hidden rounded-full align-middle bg-[var(--ui-bg-elevated)] size-5 text-[10px] shrink-0"><img role="img" src="https://github.com/benjamincanac.png" class="h-full w-full rounded-[inherit] object-cover" style="display: none;"><span class="font-medium leading-none text-[var(--ui-text-muted)] truncate"></span></span>
|
||||
<!--v-if--><span class="iconify i-heroicons:arrow-path-20-solid shrink-0 size-5 animate-spin" aria-hidden="true"></span>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with loading trailing correctly 1`] = `
|
||||
"<button type="button" disabled="" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5">
|
||||
<!--v-if-->
|
||||
<!--v-if--><span class="iconify i-heroicons:arrow-path-20-solid shrink-0 size-5 animate-spin" aria-hidden="true"></span>
|
||||
</button>"
|
||||
`;
|
||||
|
||||
exports[`Button > renders with loadingIcon correctly 1`] = `
|
||||
"<button type="button" disabled="" class="rounded-[calc(var(--ui-radius)*1.5)] font-medium inline-flex items-center focus:outline-none disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-[var(--ui-bg)] bg-[var(--ui-primary)] hover:bg-[var(--ui-primary)]/75 disabled:bg-[var(--ui-primary)] aria-disabled:bg-[var(--ui-primary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--ui-primary)] p-1.5"><span class="iconify i-heroicons:sparkles shrink-0 size-5 animate-spin" aria-hidden="true"></span>
|
||||
<!--v-if-->
|
||||
|
||||
Reference in New Issue
Block a user