🌐 AI搜索 & 代理 主页
Skip to content
Merged
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
8 changes: 8 additions & 0 deletions src/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ $b-popover-bg-level: $alert-bg-level !default;
$b-popover-border-level: $alert-border-level !default;
$b-popover-color-level: $alert-color-level !default;

// --- Avatar ---

$b-avatar-size: 2.5rem !default;
$b-avatar-size-sm: 1.5rem !default;
$b-avatar-size-lg: 3.5rem !default;
$b-avatar-font-size-scale: 0.4 !default;
$b-avatar-badge-font-size-scale: $b-avatar-font-size-scale * 0.7 !default;

// --- Skeleton ---

$b-skeleton-background-color: rgba(0, 0, 0, 0.12) !default;
Expand Down
28 changes: 28 additions & 0 deletions src/components/avatar/_avatar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
align-items: center;
justify-content: center;
vertical-align: middle;
width: $b-avatar-size;
height: $b-avatar-size;
font-size: inherit;
font-weight: 400;
line-height: 1;
Expand Down Expand Up @@ -100,6 +102,32 @@
}
}

.b-avatar-sm {
width: $b-avatar-size-sm;
height: $b-avatar-size-sm;

.b-avatar-text {
font-size: calc(#{$b-avatar-size-sm * $b-avatar-font-size-scale});
}

.b-avatar-badge {
font-size: calc(#{$b-avatar-size-sm * $b-avatar-badge-font-size-scale});
}
}

.b-avatar-lg {
width: $b-avatar-size-lg;
height: $b-avatar-size-lg;

.b-avatar-text {
font-size: calc(#{$b-avatar-size-lg * $b-avatar-font-size-scale});
}

.b-avatar-badge {
font-size: calc(#{$b-avatar-size-lg * $b-avatar-badge-font-size-scale});
}
}

.b-avatar-group {
.b-avatar-group-inner {
display: flex;
Expand Down
16 changes: 16 additions & 0 deletions src/components/avatar/avatar-group.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,20 @@ describe('avatar-group', () => {

wrapper.destroy()
})

it('overlap props work', async () => {
const wrapper = mount(BAvatarGroup, {
propsData: {
overlap: 0.65
}
})

expect(wrapper.vm).toBeDefined()
await waitNT(wrapper.vm)

expect(wrapper.vm.overlap).toBe(0.65)
expect(wrapper.vm.overlapScale).toBe(0.325)

wrapper.destroy()
})
})
53 changes: 21 additions & 32 deletions src/components/avatar/avatar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from '../../utils/vue'
import { getComponentConfig } from '../../utils/config'
import { isNumber, isString, isUndefinedOrNull } from '../../utils/inspect'
import { isNumber, isString } from '../../utils/inspect'
import { toFloat } from '../../utils/number'
import { omit } from '../../utils/object'
import { pluckProps } from '../../utils/props'
Expand All @@ -15,17 +15,13 @@ import normalizeSlotMixin from '../../mixins/normalize-slot'
const NAME = 'BAvatar'
const CLASS_NAME = 'b-avatar'

const SIZES = ['sm', null, 'lg']

const RX_NUMBER = /^[0-9]*\.?[0-9]+$/

const FONT_SIZE_SCALE = 0.4
const BADGE_FONT_SIZE_SCALE = FONT_SIZE_SCALE * 0.7

const DEFAULT_SIZES = {
sm: '1.5em',
md: '2.5em',
lg: '3.5em'
}

// --- Props ---
const linkProps = omit(BLinkProps, ['active', 'event', 'routerTag'])

Expand Down Expand Up @@ -99,18 +95,10 @@ const props = {

// --- Utility methods ---
export const computeSize = value => {
// Default to `md` size when `null`, or parse to
// number when value is a float-like string
value =
isUndefinedOrNull(value) || value === ''
? 'md'
: isString(value) && RX_NUMBER.test(value)
? toFloat(value, 0)
: value
// Parse to number when value is a float-like string
value = isString(value) && RX_NUMBER.test(value) ? toFloat(value, 0) : value
// Convert all numbers to pixel values
// Handle default sizes when `sm`, `md` or `lg`
// Or use value as is
return isNumber(value) ? `${value}px` : DEFAULT_SIZES[value] || value
return isNumber(value) ? `${value}px` : value || null
}

// --- Main component ---
Expand All @@ -130,36 +118,35 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
computed: {
computedSize() {
// Always use the avatar group size
return computeSize(this.bvAvatarGroup ? this.bvAvatarGroup.size : this.size)
const { bvAvatarGroup } = this
return computeSize(bvAvatarGroup ? bvAvatarGroup.size : this.size)
},
computedVariant() {
// Prefer avatar-group variant if provided
const avatarGroup = this.bvAvatarGroup
return avatarGroup && avatarGroup.variant ? avatarGroup.variant : this.variant
const { bvAvatarGroup } = this
return bvAvatarGroup && bvAvatarGroup.variant ? bvAvatarGroup.variant : this.variant
},
computedRounded() {
const avatarGroup = this.bvAvatarGroup
const square = avatarGroup && avatarGroup.square ? true : this.square
const rounded = avatarGroup && avatarGroup.rounded ? avatarGroup.rounded : this.rounded
const { bvAvatarGroup } = this
const square = bvAvatarGroup && bvAvatarGroup.square ? true : this.square
const rounded = bvAvatarGroup && bvAvatarGroup.rounded ? bvAvatarGroup.rounded : this.rounded
return square ? '0' : rounded === '' ? true : rounded || 'circle'
},
fontStyle() {
let fontSize = this.computedSize
fontSize = fontSize ? `calc(${fontSize} * ${FONT_SIZE_SCALE})` : null
const { computedSize: size } = this
const fontSize = SIZES.indexOf(size) === -1 ? `calc(${size} * ${FONT_SIZE_SCALE})` : null
return fontSize ? { fontSize } : {}
},
marginStyle() {
const avatarGroup = this.bvAvatarGroup
const overlapScale = avatarGroup ? avatarGroup.overlapScale : 0
const size = this.computedSize
const { computedSize: size, bvAvatarGroup } = this
const overlapScale = bvAvatarGroup ? bvAvatarGroup.overlapScale : 0
const value = size && overlapScale ? `calc(${size} * -${overlapScale})` : null
return value ? { marginLeft: value, marginRight: value } : {}
},
badgeStyle() {
const { computedSize: size, badgeTop, badgeLeft, badgeOffset } = this
const offset = badgeOffset || '0px'
return {
fontSize: size ? `calc(${size} * ${BADGE_FONT_SIZE_SCALE} )` : null,
fontSize: SIZES.indexOf(size) === -1 ? `calc(${size} * ${BADGE_FONT_SIZE_SCALE} )` : null,
top: badgeTop ? offset : null,
bottom: badgeTop ? null : offset,
left: badgeLeft ? offset : null,
Expand Down Expand Up @@ -246,6 +233,8 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
const componentData = {
staticClass: CLASS_NAME,
class: {
// Apply size class
[`${CLASS_NAME}-${size}`]: size && SIZES.indexOf(size) !== -1,
// We use badge styles for theme variants when not rendering `BButton`
[`badge-${variant}`]: !button && variant,
// Rounding/Square
Expand All @@ -254,7 +243,7 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({
// Other classes
disabled
},
style: { width: size, height: size, ...marginStyle },
style: { ...marginStyle, width: size, height: size },
attrs: { 'aria-label': ariaLabel || null },
props: button ? { variant, disabled, type } : link ? pluckProps(linkProps, this) : {},
on: button || link ? { click: this.onClick } : {}
Expand Down
19 changes: 12 additions & 7 deletions src/components/avatar/avatar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,19 +180,22 @@ describe('avatar', () => {

it('`size` prop should work as expected', async () => {
const wrapper1 = mount(BAvatar)
expect(wrapper1.attributes('style')).toEqual('width: 2.5em; height: 2.5em;')
expect(wrapper1.attributes('style')).toEqual(undefined)
wrapper1.destroy()

const wrapper2 = mount(BAvatar, { propsData: { size: 'sm' } })
expect(wrapper2.attributes('style')).toEqual('width: 1.5em; height: 1.5em;')
expect(wrapper2.attributes('style')).toEqual(undefined)
expect(wrapper2.classes()).toContain('b-avatar-sm')
wrapper2.destroy()

const wrapper3 = mount(BAvatar, { propsData: { size: 'md' } })
expect(wrapper3.attributes('style')).toEqual('width: 2.5em; height: 2.5em;')
expect(wrapper3.attributes('style')).toEqual(undefined)
expect(wrapper3.classes()).not.toContain('b-avatar-md')
wrapper3.destroy()

const wrapper4 = mount(BAvatar, { propsData: { size: 'lg' } })
expect(wrapper4.attributes('style')).toEqual('width: 3.5em; height: 3.5em;')
expect(wrapper4.attributes('style')).toEqual(undefined)
expect(wrapper4.classes()).toContain('b-avatar-lg')
wrapper4.destroy()

const wrapper5 = mount(BAvatar, { propsData: { size: 20 } })
Expand Down Expand Up @@ -255,7 +258,8 @@ describe('avatar', () => {
expect(wrapper1.element.tagName).toBe('SPAN')
expect(wrapper1.classes()).toContain('b-avatar')
expect(wrapper1.classes()).toContain('badge-secondary')
expect(wrapper1.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
// Uses avatar group size (default)
expect(wrapper1.attributes('style')).toBe(undefined)

wrapper1.destroy()

Expand All @@ -272,7 +276,8 @@ describe('avatar', () => {
expect(wrapper2.classes()).toContain('b-avatar')
expect(wrapper2.classes()).toContain('badge-danger')
expect(wrapper2.classes()).not.toContain('badge-secondary')
expect(wrapper2.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
// Uses avatar group size (default)
expect(wrapper2.attributes('style')).toBe(undefined)

wrapper2.destroy()
})
Expand All @@ -293,7 +298,7 @@ describe('avatar', () => {
expect(wrapper1.classes()).toContain('b-avatar')
expect(wrapper1.classes()).toContain('badge-secondary')
// Uses avatar group size (default)
expect(wrapper1.attributes('style')).toContain('width: 2.5em; height: 2.5em;')
expect(wrapper1.attributes('style')).toBe(undefined)

wrapper1.destroy()

Expand Down