Skip to content

Commit

Permalink
feat(b-avatar-group): new helper component b-avatar-group (#5272)
Browse files Browse the repository at this point in the history
* feat(b-avatar-group): new helper component b-avatar-group

* Update _avatar.scss

* Update _avatar.scss

* Update _avatar.scss

* Update _avatar.scss

* Create avatar-group.js

* Update index.js

* Update package.json

* Create avatar-group.spec.js

* Update avatar-group.spec.js

* Update avatar.spec.js

* Update avatar.js

* Update avatar-group.js

* Update avatar.js

* Update avatar.js

* Update avatar-group.js

* Update avatar.spec.js

* Update avatar.spec.js

* Update avatar.spec.js

* Update avatar.spec.js

* Update avatar-group.js

* Update avatar.js

* Update README.md

* Update package.json

* Update index.d.ts

* Update _avatar.scss

* Update avatar.js

* Update _avatar.scss

* Update avatar.js

* Update _avatar.scss

* Update avatar.js

* Update avatar-group.js

* Update _avatar.scss

* Update README.md

* Update _avatar.scss

* Update avatar.js

* Update avatar-group.js

* Update avatar-group.js

* Update avatar.js

* Update avatar-group.js

* Update avatar-group.js

* Update package.json

* Update README.md

* Update avatar.js

* Update README.md

* Update avatar-group.js

* Update _avatar.scss

* Update avatar-group.js

* Update README.md

* lint

* Update avatar.js

* Update avatar.spec.js

* Update avatar.spec.js

* Update README.md

* Update _avatar.scss

* Update avatar.js

* Update _avatar.scss

* Update _avatar.scss

* Update _avatar.scss

* Update README.md

* Update _avatar.scss

* Update avatar-group.js

* Update avatar-group.js

* Update avatar-group.js

* Update avatar.js

* Update package.json

* Update index.js

* Update _avatar.scss

* Update README.md

* Update avatar-group.js

* Update README.md

* Update README.md

* Prettify

* Fix typos

* Update README.md

* Update README.md

* Update package.json

* Update avatar-group.js

* Update avatar.spec.js

* Update avatar-group.spec.js

Co-authored-by: Jacob Müller <jacob.mueller.elz@gmail.com>
  • Loading branch information
tmorehouse and jacobmllr95 committed May 7, 2020
1 parent 755164f commit c84faae
Show file tree
Hide file tree
Showing 10 changed files with 464 additions and 35 deletions.
161 changes: 149 additions & 12 deletions src/components/avatar/README.md
Expand Up @@ -61,8 +61,7 @@ styling on the content.

Use the `src` prop to specify a URL of an image to use as the avatar content. The image should have
an aspect ratio of `1:1` (meaning the width and height should be equal), otherwise image aspect
distortion will occur. The image will be scaled up or down to fit within the avatar's bounding box,
and will be sized to show the avatar's [variant background](#variants) around the edge.
distortion will occur. The image will be scaled up or down to fit within the avatar's bounding box.

```html
<template>
Expand All @@ -84,8 +83,9 @@ and will be sized to show the avatar's [variant background](#variants) around th
fallback to the value of the `icon` or `text` props. If neither the `icon` or `text` props are
provided, then the default avatar icon will be shown. Also, when the image fails to load, the
`img-error` event will be emitted.
- <span class="badge badge-secondary">2.12.0+</span> Setting the [variant prop](#variants) to an
empty string will remove the visible background border around the image.
- [Variant colors](#variants) when using images not normally visible, unless the image fails load.
The variant will affect the focus styling when the image avatar is also an
[actionalble avatar](#actionalble-avatars).

### Icon content

Expand Down Expand Up @@ -270,6 +270,8 @@ Easily create avatars that respond to clicks, or avatars that change the URL/rou
Actionable avatars will appear in the document tab sequence, and are accessible for both screen
reader and keyboard-only users.

Image avatars, when actionalble, employ a basic scale transform on the image when hovered.

### Button

Want to trigger the opening of a modal or trigger an action? Set the `button` prop to instruct
Expand All @@ -278,10 +280,20 @@ the `click` event whenever clicked.

```html
<template>
<div>
<b-avatar button @click="onClick" variant="primary" text="FF" class="align-baseline"></b-avatar>
Button Avatar
</div>
<b-list-group>
<b-list-group-item>
<b-avatar button @click="onClick" variant="primary" text="FF" class="align-baseline"></b-avatar>
Button Text Avatar
</b-list-group-item>
<b-list-group-item>
<b-avatar button @click="onClick" src="https://placekitten.com/300/300"></b-avatar>
Button Image Avatar
</b-list-group-item>
<b-list-group-item>
<b-avatar button @click="onClick" icon="star-fill" class="align-center"></b-avatar>
Button Icon Avatar
</b-list-group-item>
</b-list-group>
</template>

<script>
Expand Down Expand Up @@ -315,10 +327,20 @@ The `to` prop can either be a string path, or a `Location` object. The `to` prop

```html
<template>
<div>
<b-avatar href="#foobar" variant="info" src="https://placekitten.com/300/300"></b-avatar>
Link Avatar
</div>
<b-list-group>
<b-list-group-item>
<b-avatar href="#foo" variant="primary" text="FF" class="align-baseline"></b-avatar>
Link Text Avatar
</b-list-group-item>
<b-list-group-item>
<b-avatar href="#bar" src="https://placekitten.com/300/300"></b-avatar>
Link Image Avatar
</b-list-group-item>
<b-list-group-item>
<b-avatar href="#baz" icon="star-fill" class="align-center"></b-avatar>
Link Icon Avatar
</b-list-group-item>
</b-list-group>
</template>

<!-- b-avatar-href.vue -->
Expand Down Expand Up @@ -411,6 +433,121 @@ inward, while negative values will move the badge outward.
<!-- b-avatar-badge-offset.vue -->
```

## Avatar groups

<span class="badge badge-info small">v2.14.0+</span>

Group multiple avatars together by wrapping them in a `<b-avatar-group>` component:

```html
<template>
<div>
<b-avatar-group size="60px">
<b-avatar></b-avatar>
<b-avatar text="BV" variant="primary"></b-avatar>
<b-avatar src="https://placekitten.com/300/300" variant="info"></b-avatar>
<b-avatar text="OK" variant="danger"></b-avatar>
<b-avatar variant="warning"></b-avatar>
<b-avatar src="https://placekitten.com/320/320" variant="dark"></b-avatar>
<b-avatar icon="music-note" variant="success"></b-avatar>
</b-avatar-group>
</div>
</template>

<!-- b-avatar-group.vue -->
```

**Notes:**

- The `variant`, `square` and `rounded` props on `<b-avatar-group>` will take precedence over the
respective props on individual avatars.

### Group size

To size the avatars, use the prop `size` on `<b-avatar-group>`. The `size` prop accepts the same
type of values as the `size` prop on `<b-avatar>`. Note that the `size` prop will be ignored on
individual avatars when they are placed inside a `<b-avatar-group>`.

```html
<template>
<div>
<b-avatar-group size="5rem">
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
</b-avatar-group>
</div>
</template>

<!-- b-avatar-group-size.vue -->
```

### Group variant

Use the `variant` prop to color all child avatars in the `<b-avatar-group>`. Note that the `variant`
prop, when set, will override the the `variant` specified on individual avatars.

```html
<template>
<div>
<b-avatar-group variant="success">
<b-avatar></b-avatar>
<b-avatar variant="info"></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
</b-avatar-group>
</div>
</template>

<!-- b-avatar-group-variant.vue -->
```

### Group rounding

Similar to the `variant` prop, the `<b-avatar-group>` props `square` and `rounded` take precedence
over the respective props on individual child avatars.

```html
<template>
<div>
<b-avatar-group rounded="lg">
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
</b-avatar-group>
</div>
</template>

<!-- b-avatar-group-rounded.vue -->
```

### Group overlap

By default child avatars inside a `<b-avatar-group>` will overlap by a factor of `0.3` (relative to
the size of the avatar). You can control the overlap amount by setting the `overlap` prop to a value
between `0` and `1`, where `0` means no overlap and `1` means 100% overlap.

```html
<template>
<div>
<b-avatar-group overlap="0.65">
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
<b-avatar></b-avatar>
</b-avatar-group>
</div>
</template>

<!-- b-avatar-group-overlap.vue -->
```

## Accessibility

Use the `aria-label` prop to provide an accessible, screen reader friendly, label for your avatar.
Expand Down
47 changes: 39 additions & 8 deletions src/components/avatar/_avatar.scss
Expand Up @@ -20,14 +20,23 @@
outline: 0;
}

&a,
&button,
&.btn {
&.btn,
&[href] {
padding: 0;
border: 0;

.b-avatar-img img {
transition: transform 0.15s ease-in-out;
}

&:not(:disabled):not(.disabled) {
cursor: if($enable-pointer-cursor-for-buttons, pointer, null);

&:hover {
.b-avatar-img img {
transform: scale(1.15);
}
}
}
}

Expand All @@ -39,7 +48,8 @@
}

.b-avatar-custom,
.b-avatar-text {
.b-avatar-text,
.b-avatar-img {
border-radius: inherit;
width: 100%;
height: 100%;
Expand All @@ -54,16 +64,19 @@
white-space: nowrap;
}

&[href] {
text-decoration: none;
}

> .b-icon {
width: 60%;
height: auto;
max-width: 100%;
}

img {
width: 90%;
height: 90%;
max-width: 100%;
.b-avatar-img img {
width: 100%;
height: 100%;
max-height: auto;
border-radius: inherit;
}
Expand All @@ -81,3 +94,21 @@
z-index: 5;
}
}

.b-avatar-group {
.b-avatar-group-inner {
display: flex;
flex-wrap: wrap;
}

.b-avatar {
border: $border-width solid $border-color;
}

a,
.btn {
&.b-avatar:hover:not(.disabled):not(disabled) {
z-index: 3;
}
}
}
68 changes: 68 additions & 0 deletions src/components/avatar/avatar-group.js
@@ -0,0 +1,68 @@
import Vue from '../../utils/vue'
import normalizeSlotMixin from '../../mixins/normalize-slot'
import { mathMax, mathMin } from '../../utils/math'
import { toFloat } from '../../utils/number'
import { computeSize } from './avatar'

// --- Constants ---
const NAME = 'BAvatarGroup'

// --- Main component ---
// @vue/component
export const BAvatarGroup = /*#__PURE__*/ Vue.extend({
name: NAME,
mixins: [normalizeSlotMixin],
provide() {
return { bvAvatarGroup: this }
},
props: {
variant: {
// Child avatars will prefer this variant over their own
type: String,
default: null
},
size: {
// Child avatars will always use this over their own size
type: String,
default: null
},
overlap: {
type: [Number, String],
default: 0.3
},
square: {
// Child avatars will prefer this prop (if set) over their own
type: Boolean,
default: false
},
rounded: {
// Child avatars will prefer this prop (if set) over their own
type: [Boolean, String],
default: false
},
tag: {
type: String,
default: 'div'
}
},
computed: {
computedSize() {
return computeSize(this.size)
},
overlapScale() {
return mathMin(mathMax(toFloat(this.overlap, 0), 0), 1) / 2
},
paddingStyle() {
let value = this.computedSize
value = value ? `calc(${value} * ${this.overlapScale})` : null
return value ? { paddingLeft: value, paddingRight: value } : {}
}
},
render(h) {
const $inner = h('div', { staticClass: 'b-avatar-group-inner', style: this.paddingStyle }, [
this.normalizeSlot('default')
])

return h(this.tag, { staticClass: 'b-avatar-group', attrs: { role: 'group' } }, [$inner])
}
})

0 comments on commit c84faae

Please sign in to comment.