Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/api-generator/src/locale/en/VBadge.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"bordered": "Applies a **2px** by default and **1.5px** border around the badge when using the **dot** property.",
"content": "Text content to show in the badge.",
"dot": "Reduce the size of the badge and hide its contents.",
"dotSize": "Sets the size of the **dot** variant (includes border when using with **bordered**)",
"floating": "Move the badge further away from the slotted content. Equivalent to an 8px offset.",
"inline": "Display as an inline block instead of absolute position. **location**, **floating**, and **offset** will have no effect.",
"label": "The **aria-label** used for the badge.",
Expand Down
5 changes: 5 additions & 0 deletions packages/docs/src/data/new-in.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"border": "3.7.0"
}
},
"VBadge": {
"props": {
"dotSize": "4.0.0"
}
},
"VBanner": {
"props": {
"bgColor": "3.4.0"
Expand Down
154 changes: 104 additions & 50 deletions packages/vuetify/src/components/VAvatar/VAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import './VAvatar.sass'

// Components
import { VBadge } from '@/components/VBadge/VBadge'
import { VDefaultsProvider } from '@/components/VDefaultsProvider'
import { VIcon } from '@/components/VIcon'
import { VImg } from '@/components/VImg'
Expand All @@ -18,9 +19,17 @@ import { makeThemeProps, provideTheme } from '@/composables/theme'
import { genOverlays, makeVariantProps, useVariant } from '@/composables/variant'

// Utilities
import { genericComponent, propsFactory, useRender } from '@/util'
import { computed } from 'vue'
import { genericComponent, isObject, propsFactory, useRender } from '@/util'

// Types
import type { PropType } from 'vue'

export const makeVAvatarProps = propsFactory({
badge: {
type: [Boolean, Object] as PropType<boolean | VBadge['$props']>,
default: false,
},
start: Boolean,
end: Boolean,
icon: IconValue,
Expand All @@ -37,7 +46,11 @@ export const makeVAvatarProps = propsFactory({
...makeVariantProps({ variant: 'flat' } as const),
}, 'VAvatar')

export const VAvatar = genericComponent()({
export type VAvatarSlots = {
badge: never
}

export const VAvatar = genericComponent<VAvatarSlots>()({
name: 'VAvatar',

props: makeVAvatarProps(),
Expand All @@ -50,55 +63,96 @@ export const VAvatar = genericComponent()({
const { roundedClasses } = useRounded(props)
const { sizeClasses, sizeStyles } = useSize(props)

useRender(() => (
<props.tag
class={[
'v-avatar',
{
'v-avatar--start': props.start,
'v-avatar--end': props.end,
},
themeClasses.value,
borderClasses.value,
colorClasses.value,
densityClasses.value,
roundedClasses.value,
sizeClasses.value,
variantClasses.value,
props.class,
]}
style={[
colorStyles.value,
sizeStyles.value,
props.style,
]}
>
{ !slots.default ? (
props.image
? (<VImg key="image" src={ props.image } alt="" cover />)
: props.icon
? (<VIcon key="icon" icon={ props.icon } />)
: props.text
) : (
<VDefaultsProvider
key="content-defaults"
defaults={{
VImg: {
cover: true,
src: props.image,
},
VIcon: {
icon: props.icon,
},
const badgeDotSize = computed(() => {
switch (props.size) {
case 'x-small': return 8
case 'small': return 10
case 'large': return 14
case 'x-large': return 16
default: return 12
}
})

const badgeOffset = computed(() => {
const { floating } = isObject(props.badge) ? props.badge : {}
return (floating ? badgeDotSize.value / 2 : 0) - 1.5
})

const badgeProps = computed(() => {
return {
bordered: true,
dot: !slots.badge,
dotSize: badgeDotSize.value,
offsetX: badgeOffset.value,
offsetY: badgeOffset.value,
color: typeof props.badge === 'string' ? props.badge : 'primary',
...isObject(props.badge) ? props.badge : {},
}
})

useRender(() => {
const avatar = (
<props.tag
class={[
'v-avatar',
{
'v-avatar--start': props.start,
'v-avatar--end': props.end,
},
themeClasses.value,
borderClasses.value,
colorClasses.value,
densityClasses.value,
roundedClasses.value,
sizeClasses.value,
variantClasses.value,
props.class,
]}
style={[
colorStyles.value,
sizeStyles.value,
props.style,
]}
>
{ !slots.default ? (
props.image
? (<VImg key="image" src={ props.image } alt="" cover />)
: props.icon
? (<VIcon key="icon" icon={ props.icon } />)
: props.text
) : (
<VDefaultsProvider
key="content-defaults"
defaults={{
VImg: {
cover: true,
src: props.image,
},
VIcon: {
icon: props.icon,
},
}}
>
{ slots.default() }
</VDefaultsProvider>
)}

{ genOverlays(false, 'v-avatar') }
</props.tag>
)

return props.badge
? (
<VBadge
{ ...badgeProps.value }
v-slots={{
default: () => avatar,
badge: slots.badge,
}}
>
{ slots.default() }
</VDefaultsProvider>
)}

{ genOverlays(false, 'v-avatar') }
</props.tag>
))
/>
)
: avatar
})

return {}
},
Expand Down
6 changes: 1 addition & 5 deletions packages/vuetify/src/components/VBadge/VBadge.sass
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,10 @@
border-radius: inherit
border-style: $badge-border-style
border-width: $badge-border-width
bottom: 0
color: $badge-border-color
content: ''
left: 0
position: absolute
right: 0
top: 0
inset: 0
transform: $badge-border-transform

.v-badge--dot &
Expand Down Expand Up @@ -83,4 +80,3 @@
forced-color-adjust: preserve-parent-color
background: highlight
color: highlighttext

9 changes: 7 additions & 2 deletions packages/vuetify/src/components/VBadge/VBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { makeThemeProps, useTheme } from '@/composables/theme'
import { makeTransitionProps, MaybeTransition } from '@/composables/transition'

// Utilities
import { genericComponent, pickWithRest, propsFactory, useRender } from '@/util'
import { convertToUnit, genericComponent, pickWithRest, propsFactory, useRender } from '@/util'

export type VBadgeSlots = {
default: never
Expand All @@ -29,6 +29,7 @@ export const makeVBadgeProps = propsFactory({
color: String,
content: [Number, String],
dot: Boolean,
dotSize: [Number, String],
floating: Boolean,
icon: IconValue,
inline: Boolean,
Expand Down Expand Up @@ -71,7 +72,7 @@ export const VBadge = genericComponent<VBadgeSlots>()({
const { locationStyles } = useLocation(props, true, side => {
const base = props.floating
? (props.dot ? 2 : 4)
: (props.dot ? 8 : 12)
: (props.dot ? (Number(props.dotSize) ?? 8) : 12)

return base + (
['top', 'bottom'].includes(side) ? Number(props.offsetY ?? 0)
Expand Down Expand Up @@ -129,6 +130,10 @@ export const VBadge = genericComponent<VBadgeSlots>()({
textColorStyles.value,
dimensionStyles.value,
props.inline ? {} : locationStyles.value,
props.dot && props.dotSize ? {
width: convertToUnit(props.dotSize),
height: convertToUnit(props.dotSize),
} : {},
]}
aria-atomic="true"
aria-label={ t(props.label, value) }
Expand Down
2 changes: 1 addition & 1 deletion packages/vuetify/src/components/VBadge/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ $badge-border-radius: 10px !default;
$badge-border-style: solid !default;
$badge-border-transform: scale(1.05) !default;
$badge-border-width: 2px !default;
$badge-dot-border-radius: 4.5px !default;
$badge-dot-border-radius: 50% !default;
$badge-dot-border-width: 1.5px !default;
$badge-dot-height: 9px !default;
$badge-dot-width: 9px !default;
Expand Down
Loading