@vaadin/vaadin-avatar
Advanced tools
Comparing version 1.0.0-alpha7 to 1.0.0-alpha8
@@ -13,3 +13,3 @@ { | ||
"name": "@vaadin/vaadin-avatar", | ||
"version": "1.0.0-alpha7", | ||
"version": "1.0.0-alpha8", | ||
"main": "vaadin-avatar-group.js", | ||
@@ -36,2 +36,3 @@ "author": "Vaadin Ltd", | ||
"@polymer/polymer": "^3.0.0", | ||
"@polymer/iron-a11y-announcer": "^3.0.0", | ||
"@polymer/iron-resizable-behavior": "^3.0.0", | ||
@@ -38,0 +39,0 @@ "@vaadin/vaadin-themable-mixin": "^1.6.1", |
@@ -20,4 +20,8 @@ /** | ||
import {IronA11yAnnouncer} from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js'; | ||
import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import {IronA11yAnnouncer as IronA11yAnnouncer$0} from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js'; | ||
import {IronResizableBehavior} from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js'; | ||
@@ -31,2 +35,4 @@ | ||
import {calculateSplices} from '@polymer/polymer/lib/utils/array-splice.js'; | ||
import {mixinBehaviors} from '@polymer/polymer/lib/legacy/class.js'; | ||
@@ -57,5 +63,5 @@ | ||
/** | ||
* The maximum number of avatars to display. | ||
* By default, all the avatars are displayed. | ||
* When max is set, the overflowing avatars are grouped into one avatar. | ||
* The maximum number of avatars to display. By default, all the avatars are displayed. | ||
* When max is set, the overflowing avatars are grouped into one avatar with a dropdown. | ||
* Setting 0 or 1 has no effect so there are always at least two avatars visible. | ||
*/ | ||
@@ -70,11 +76,22 @@ max: number|null|undefined; | ||
* The object has the following JSON structure and default values: | ||
* { | ||
* // Translation of the anonymous user avatar title. | ||
* anonymous: 'anonymous', | ||
* // Translation of the avatar group accessible label. | ||
* activeUsers: { | ||
* one: 'Currently one active user', | ||
* many: 'Currently {count} active users' | ||
* } | ||
* } | ||
* ``` | ||
* { | ||
* // Translation of the anonymous user avatar title. | ||
* anonymous: 'anonymous', | ||
* // Translation of the avatar group accessible label. | ||
* // {count} is replaced with the actual count of users. | ||
* activeUsers: { | ||
* one: 'Currently one active user', | ||
* many: 'Currently {count} active users' | ||
* }, | ||
* // Screen reader announcement when user joins group. | ||
* // {user} is replaced with the name or abbreviation. | ||
* // When neither is set, "anonymous" is used instead. | ||
* joined: '{user} joined', | ||
* // Screen reader announcement when user leaves group. | ||
* // {user} is replaced with the name or abbreviation. | ||
* // When neither is set, "anonymous" is used instead. | ||
* left: '{user} left' | ||
* } | ||
* ``` | ||
*/ | ||
@@ -81,0 +98,0 @@ i18n: AvatarGroupI18n; |
@@ -10,3 +10,5 @@ /** | ||
import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js'; | ||
import { IronA11yAnnouncer } from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js'; | ||
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { IronA11yAnnouncer as IronA11yAnnouncer$0 } from '@polymer/iron-a11y-announcer/iron-a11y-announcer.js'; | ||
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js'; | ||
@@ -20,2 +22,3 @@ import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js'; | ||
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js'; | ||
import { calculateSplices } from '@polymer/polymer/lib/utils/array-splice.js'; | ||
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js'; | ||
@@ -49,2 +52,3 @@ | ||
display: block; | ||
width: 100%; /* prevent collapsing inside non-stretching column flex */ | ||
--vaadin-avatar-group-overlap: 8px; | ||
@@ -86,3 +90,3 @@ --vaadin-avatar-group-overlap-border: 2px; | ||
<div id="container" part="container"> | ||
<template is="dom-repeat" items="[[__computeItems(items.*, __itemsInView, max)]]"> | ||
<template id="items" is="dom-repeat" items="[[__computeItems(items.*, __itemsInView, max)]]"> | ||
<vaadin-avatar name="[[item.name]]" abbr="[[item.abbr]]" img="[[item.img]]" part="avatar" theme\$="[[theme]]" i18n="[[i18n]]" color-index="[[item.colorIndex]]"></vaadin-avatar> | ||
@@ -112,3 +116,3 @@ </template> | ||
static get version() { | ||
return '1.0.0-alpha7'; | ||
return '1.0.0-alpha8'; | ||
} | ||
@@ -127,5 +131,5 @@ | ||
/** | ||
* The maximum number of avatars to display. | ||
* By default, all the avatars are displayed. | ||
* When max is set, the overflowing avatars are grouped into one avatar. | ||
* The maximum number of avatars to display. By default, all the avatars are displayed. | ||
* When max is set, the overflowing avatars are grouped into one avatar with a dropdown. | ||
* Setting 0 or 1 has no effect so there are always at least two avatars visible. | ||
*/ | ||
@@ -142,14 +146,25 @@ max: { | ||
* The object has the following JSON structure and default values: | ||
{ | ||
// Translation of the anonymous user avatar title. | ||
anonymous: 'anonymous', | ||
// Translation of the avatar group accessible label. | ||
activeUsers: { | ||
one: 'Currently one active user', | ||
many: 'Currently {count} active users' | ||
} | ||
} | ||
* @type {!AvatarGroupI18n} | ||
* @default {English/US} | ||
*/ | ||
* ``` | ||
* { | ||
* // Translation of the anonymous user avatar title. | ||
* anonymous: 'anonymous', | ||
* // Translation of the avatar group accessible label. | ||
* // {count} is replaced with the actual count of users. | ||
* activeUsers: { | ||
* one: 'Currently one active user', | ||
* many: 'Currently {count} active users' | ||
* }, | ||
* // Screen reader announcement when user joins group. | ||
* // {user} is replaced with the name or abbreviation. | ||
* // When neither is set, "anonymous" is used instead. | ||
* joined: '{user} joined', | ||
* // Screen reader announcement when user leaves group. | ||
* // {user} is replaced with the name or abbreviation. | ||
* // When neither is set, "anonymous" is used instead. | ||
* left: '{user} left' | ||
* } | ||
* ``` | ||
* @type {!AvatarGroupI18n} | ||
* @default {English/US} | ||
*/ | ||
i18n: { | ||
@@ -163,3 +178,5 @@ type: Object, | ||
many: 'Currently {count} active users' | ||
} | ||
}, | ||
joined: '{user} joined', | ||
left: '{user} left' | ||
}; | ||
@@ -193,3 +210,3 @@ } | ||
'__computeMoreTitle(items.length, __itemsInView, max)', | ||
'__itemsChanged(items.length)', | ||
'__itemsChanged(items.splices, items.*)', | ||
'__i18nItemsChanged(i18n.*, items.length)' | ||
@@ -203,2 +220,4 @@ ]; | ||
IronA11yAnnouncer$0.requestAvailability(); | ||
this.__boundSetPosition = this.__setPosition.bind(this); | ||
@@ -237,2 +256,20 @@ | ||
/** @private */ | ||
__announce(text) { | ||
this.dispatchEvent( | ||
new CustomEvent('iron-announce', { | ||
bubbles: true, | ||
composed: true, | ||
detail: { | ||
text | ||
} | ||
}) | ||
); | ||
} | ||
/** @private */ | ||
__getMessage(user, action) { | ||
return action.replace('{user}', user.name || user.abbr || this.i18n.anonymous); | ||
} | ||
/** @private */ | ||
_onOverflowClick(e) { | ||
@@ -299,3 +336,3 @@ e.stopPropagation(); | ||
__computeMaxReached(items, max) { | ||
return max != null && items > max; | ||
return max != null && items > this.__getMax(max); | ||
} | ||
@@ -305,3 +342,3 @@ | ||
__computeMore(items, itemsInView, max) { | ||
return max != null ? `+${items - max}` : `+${items - itemsInView}`; | ||
return max != null ? `+${items - this.__getMax(max) + 1}` : `+${items - itemsInView}`; | ||
} | ||
@@ -332,4 +369,6 @@ | ||
let limit = null; | ||
if (max != null) { | ||
limit = max; | ||
// handle max set to 0 or 1 | ||
const adjustedMax = this.__getMax(max); | ||
if (max != null && adjustedMax < items) { | ||
limit = adjustedMax - 1; | ||
} else if (itemsInView && itemsInView < items) { | ||
@@ -342,7 +381,50 @@ limit = itemsInView; | ||
/** @private */ | ||
__itemsChanged(items) { | ||
__getMax(max) { | ||
return Math.max(max, 2); | ||
} | ||
/** @private */ | ||
__itemsChanged(splices, itemsChange) { | ||
const items = itemsChange.base; | ||
this.$.items.render(); | ||
this.__setItemsInView(); | ||
// mutation using group.splice('items') | ||
if (splices && Array.isArray(splices.indexSplices)) { | ||
splices.indexSplices.forEach((mutation) => { | ||
this.__announceItemsChange(items, mutation); | ||
}); | ||
} else if (Array.isArray(items) && Array.isArray(this.__oldItems)) { | ||
// mutation using group.set('items') | ||
const diff = calculateSplices(items, this.__oldItems); | ||
diff.forEach((mutation) => { | ||
this.__announceItemsChange(items, mutation); | ||
}); | ||
} | ||
this.__oldItems = items; | ||
} | ||
/** @private */ | ||
__announceItemsChange(items, mutation) { | ||
const {addedCount, index, removed} = mutation; | ||
let addedMsg = []; | ||
let removedMsg = []; | ||
if (addedCount) { | ||
addedMsg = items | ||
.slice(index, index + addedCount) | ||
.map((user) => this.__getMessage(user, this.i18n.joined || '{user} joined')); | ||
} | ||
if (removed) { | ||
removedMsg = removed.map((user) => this.__getMessage(user, this.i18n.left || '{user} left')); | ||
} | ||
const messages = removedMsg.concat(addedMsg); | ||
if (messages.length > 0) { | ||
this.__announce(messages.join(', ')); | ||
} | ||
} | ||
/** @private */ | ||
__i18nItemsChanged(i18n, items) { | ||
@@ -353,3 +435,3 @@ const {base} = i18n; | ||
if (base.activeUsers[field]) { | ||
this.setAttribute('aria-label', base.activeUsers[field].replace('{count}', items)); | ||
this.setAttribute('aria-label', base.activeUsers[field].replace('{count}', items || 0)); | ||
} | ||
@@ -388,3 +470,6 @@ } | ||
const avatars = this._avatars; | ||
if (!avatars || avatars.length < 2) { | ||
const items = this.items; | ||
// always show at least two avatars | ||
if (!items || !avatars || avatars.length < 3) { | ||
return; | ||
@@ -403,6 +488,11 @@ } | ||
const result = Math.floor((this.$.container.offsetWidth - avatarWidth) / (avatarWidth + offset)); | ||
let result = Math.floor((this.$.container.offsetWidth - avatarWidth) / (avatarWidth + offset)); | ||
// only show overlay if two or more avatars don't fit | ||
if (result === items.length - 1) { | ||
result = items.length; | ||
} | ||
// close overlay if all avatars become visible | ||
if (result >= this.items.length && this._opened) { | ||
if (result >= items.length && this._opened) { | ||
this.$.overlay.close(); | ||
@@ -409,0 +499,0 @@ // FIXME: hack to avoid jump before closing |
@@ -121,3 +121,3 @@ /** | ||
static get version() { | ||
return '1.0.0-alpha7'; | ||
return '1.0.0-alpha8'; | ||
} | ||
@@ -124,0 +124,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
64420
1272
10
+ Added@polymer/iron-a11y-announcer@3.2.0(transitive)