@@ -16,6 +16,7 @@ const CLASS_NAME = 'b-avatar'
1616const RX_NUMBER = / ^ [ 0 - 9 ] * \. ? [ 0 - 9 ] + $ /
1717
1818const FONT_SIZE_SCALE = 0.4
19+ const BADGE_FONT_SIZE_SCALE = FONT_SIZE_SCALE * 0.7
1920
2021const DEFAULT_SIZES = {
2122 sm : '1.5em' ,
@@ -112,6 +113,26 @@ const props = {
112113 type : String ,
113114 default : 'button'
114115 } ,
116+ badge : {
117+ type : [ Boolean , String ] ,
118+ default : false
119+ } ,
120+ badgeVariant : {
121+ type : String ,
122+ default : ( ) => getComponentConfig ( NAME , 'badgeVariant' )
123+ } ,
124+ badgeTop : {
125+ type : Boolean ,
126+ default : false
127+ } ,
128+ badgeLeft : {
129+ type : Boolean ,
130+ default : false
131+ } ,
132+ badgeOffset : {
133+ type : String ,
134+ default : '0px'
135+ } ,
115136 ...linkProps ,
116137 ariaLabel : {
117138 type : String
@@ -149,6 +170,17 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
149170 fontSize ( ) {
150171 const size = this . computedSize
151172 return size ? `calc(${ size } * ${ FONT_SIZE_SCALE } )` : null
173+ } ,
174+ badgeStyle ( ) {
175+ const { computedSize : size , badgeTop, badgeLeft, badgeOffset } = this
176+ const offset = badgeOffset || '0px'
177+ return {
178+ fontSize : size ? `calc(${ size } * ${ BADGE_FONT_SIZE_SCALE } )` : null ,
179+ top : badgeTop ? offset : null ,
180+ bottom : badgeTop ? null : offset ,
181+ left : badgeLeft ? offset : null ,
182+ right : badgeLeft ? null : offset
183+ }
152184 }
153185 } ,
154186 watch : {
@@ -178,7 +210,10 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
178210 fontSize,
179211 computedSize : size ,
180212 button : isButton ,
181- buttonType : type
213+ buttonType : type ,
214+ badge,
215+ badgeVariant,
216+ badgeStyle
182217 } = this
183218 const isBLink = ! isButton && ( this . href || this . to )
184219 const tag = isButton ? BButton : isBLink ? BLink : 'span'
@@ -189,7 +224,7 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
189224 let $content = null
190225 if ( this . hasNormalizedSlot ( 'default' ) ) {
191226 // Default slot overrides props
192- $content = this . normalizeSlot ( 'default' )
227+ $content = h ( 'span' , { staticClass : 'b-avatar-custom' } , [ this . normalizeSlot ( 'default' ) ] )
193228 } else if ( src ) {
194229 $content = h ( 'img' , { attrs : { src, alt } , on : { error : this . onImgError } } )
195230 } else if ( icon ) {
@@ -198,12 +233,27 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
198233 attrs : { 'aria-hidden' : 'true' , alt }
199234 } )
200235 } else if ( text ) {
201- $content = h ( 'span' , { style : { fontSize } } , text )
236+ $content = h ( 'span' , { staticClass : 'b-avatar-text' , style : { fontSize } } , [ h ( 'span' , text ) ] )
202237 } else {
203238 // Fallback default avatar content
204239 $content = h ( BIconPersonFill , { attrs : { 'aria-hidden' : 'true' , alt } } )
205240 }
206241
242+ let $badge = h ( )
243+ const hasBadgeSlot = this . hasNormalizedSlot ( 'badge' )
244+ if ( badge || badge === '' || hasBadgeSlot ) {
245+ const badgeText = badge === true ? '' : badge
246+ $badge = h (
247+ 'span' ,
248+ {
249+ staticClass : 'b-avatar-badge' ,
250+ class : { [ `badge-${ badgeVariant } ` ] : ! ! badgeVariant } ,
251+ style : badgeStyle
252+ } ,
253+ [ hasBadgeSlot ? this . normalizeSlot ( 'badge' ) : badgeText ]
254+ )
255+ }
256+
207257 const componentData = {
208258 staticClass : CLASS_NAME ,
209259 class : {
@@ -217,11 +267,11 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
217267 disabled
218268 } ,
219269 style : { width : size , height : size } ,
220- attrs : { 'aria-label' : ariaLabel } ,
270+ attrs : { 'aria-label' : ariaLabel || null } ,
221271 props : isButton ? { variant, disabled, type } : isBLink ? pluckProps ( linkProps , this ) : { } ,
222272 on : isBLink || isButton ? { click : this . onClick } : { }
223273 }
224274
225- return h ( tag , componentData , [ $content ] )
275+ return h ( tag , componentData , [ $content , $badge ] )
226276 }
227277} )
0 commit comments