@vaadin/avatar-group 23.3.3 → 24.0.0-alpha10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -10
- package/src/vaadin-avatar-group-overlay.js +1 -1
- package/src/vaadin-avatar-group.d.ts +9 -4
- package/src/vaadin-avatar-group.js +211 -93
- package/theme/lumo/vaadin-avatar-group-styles.js +3 -14
- package/theme/material/vaadin-avatar-group-styles.js +3 -14
- package/web-types.json +25 -3
- package/web-types.lit.json +10 -3
- package/src/vaadin-avatar-group-list-box.js +0 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/avatar-group",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "24.0.0-alpha10",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,14 +37,15 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@polymer/polymer": "^3.0.0",
|
|
40
|
-
"@vaadin/avatar": "
|
|
41
|
-
"@vaadin/component-base": "
|
|
42
|
-
"@vaadin/item": "
|
|
43
|
-
"@vaadin/list-box": "
|
|
44
|
-
"@vaadin/overlay": "
|
|
45
|
-
"@vaadin/vaadin-lumo-styles": "
|
|
46
|
-
"@vaadin/vaadin-material-styles": "
|
|
47
|
-
"@vaadin/vaadin-themable-mixin": "
|
|
40
|
+
"@vaadin/avatar": "24.0.0-alpha10",
|
|
41
|
+
"@vaadin/component-base": "24.0.0-alpha10",
|
|
42
|
+
"@vaadin/item": "24.0.0-alpha10",
|
|
43
|
+
"@vaadin/list-box": "24.0.0-alpha10",
|
|
44
|
+
"@vaadin/overlay": "24.0.0-alpha10",
|
|
45
|
+
"@vaadin/vaadin-lumo-styles": "24.0.0-alpha10",
|
|
46
|
+
"@vaadin/vaadin-material-styles": "24.0.0-alpha10",
|
|
47
|
+
"@vaadin/vaadin-themable-mixin": "24.0.0-alpha10",
|
|
48
|
+
"lit": "^2.0.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@esm-bundle/chai": "^4.3.4",
|
|
@@ -55,5 +56,5 @@
|
|
|
55
56
|
"web-types.json",
|
|
56
57
|
"web-types.lit.json"
|
|
57
58
|
],
|
|
58
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "2e04534d8b47bcd216f89b5f849bafef1a73b174"
|
|
59
60
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2020 -
|
|
3
|
+
* Copyright (c) 2020 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2020 -
|
|
3
|
+
* Copyright (c) 2020 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { AvatarI18n } from '@vaadin/avatar/src/vaadin-avatar.js';
|
|
7
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
7
8
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
9
|
+
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
8
10
|
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
9
11
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
12
|
|
|
@@ -51,7 +53,9 @@ export interface AvatarGroupItem {
|
|
|
51
53
|
* Part name | Description
|
|
52
54
|
* ----------- | ---------------
|
|
53
55
|
* `container` | The container element
|
|
54
|
-
*
|
|
56
|
+
*
|
|
57
|
+
* See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
|
|
58
|
+
* state attributes and stylable shadow parts of avatar elements.
|
|
55
59
|
*
|
|
56
60
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
|
|
57
61
|
*
|
|
@@ -60,10 +64,11 @@ export interface AvatarGroupItem {
|
|
|
60
64
|
* In addition to `<vaadin-avatar-group>` itself, the following internal
|
|
61
65
|
* components are themable:
|
|
62
66
|
*
|
|
63
|
-
* - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
|
|
64
67
|
* - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
|
|
65
68
|
*/
|
|
66
|
-
declare class AvatarGroup extends ResizeMixin(
|
|
69
|
+
declare class AvatarGroup extends ResizeMixin(
|
|
70
|
+
OverlayClassMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement)))),
|
|
71
|
+
) {
|
|
67
72
|
readonly _avatars: HTMLElement[];
|
|
68
73
|
|
|
69
74
|
/**
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @license
|
|
3
|
-
* Copyright (c) 2020 -
|
|
3
|
+
* Copyright (c) 2020 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import '@polymer/polymer/lib/elements/dom-repeat.js';
|
|
7
6
|
import '@vaadin/avatar/src/vaadin-avatar.js';
|
|
8
7
|
import '@vaadin/item/src/vaadin-item.js';
|
|
9
|
-
import '
|
|
8
|
+
import '@vaadin/list-box/src/vaadin-list-box.js';
|
|
10
9
|
import './vaadin-avatar-group-overlay.js';
|
|
11
10
|
import { calculateSplices } from '@polymer/polymer/lib/utils/array-splice.js';
|
|
12
11
|
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
13
|
-
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
12
|
+
import { html as legacyHtml, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
13
|
+
import { html, render } from 'lit';
|
|
14
14
|
import { announce } from '@vaadin/component-base/src/a11y-announcer.js';
|
|
15
|
+
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
15
16
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
17
|
+
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
16
18
|
import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
|
|
19
|
+
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
17
20
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
18
21
|
|
|
19
22
|
const MINIMUM_DISPLAYED_AVATARS = 2;
|
|
@@ -43,7 +46,9 @@ const MINIMUM_DISPLAYED_AVATARS = 2;
|
|
|
43
46
|
* Part name | Description
|
|
44
47
|
* ----------- | ---------------
|
|
45
48
|
* `container` | The container element
|
|
46
|
-
*
|
|
49
|
+
*
|
|
50
|
+
* See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
|
|
51
|
+
* state attributes and stylable shadow parts of avatar elements.
|
|
47
52
|
*
|
|
48
53
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
|
|
49
54
|
*
|
|
@@ -52,17 +57,18 @@ const MINIMUM_DISPLAYED_AVATARS = 2;
|
|
|
52
57
|
* In addition to `<vaadin-avatar-group>` itself, the following internal
|
|
53
58
|
* components are themable:
|
|
54
59
|
*
|
|
55
|
-
* - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
|
|
56
60
|
* - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
|
|
57
61
|
*
|
|
58
62
|
* @extends HTMLElement
|
|
63
|
+
* @mixes ControllerMixin
|
|
59
64
|
* @mixes ElementMixin
|
|
65
|
+
* @mixes OverlayClassMixin
|
|
60
66
|
* @mixes ThemableMixin
|
|
61
67
|
* @mixes ResizeMixin
|
|
62
68
|
*/
|
|
63
|
-
class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
|
|
69
|
+
class AvatarGroup extends ResizeMixin(OverlayClassMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement))))) {
|
|
64
70
|
static get template() {
|
|
65
|
-
return
|
|
71
|
+
return legacyHtml`
|
|
66
72
|
<style>
|
|
67
73
|
:host {
|
|
68
74
|
display: block;
|
|
@@ -82,7 +88,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
82
88
|
flex-wrap: nowrap;
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
|
|
91
|
+
::slotted(vaadin-avatar:not(:first-child)) {
|
|
86
92
|
-webkit-mask-image: url('data:image/svg+xml;utf8,<svg viewBox=%220 0 300 300%22 fill=%22none%22 xmlns=%22http://www.w3.org/2000/svg%22><path fill-rule=%22evenodd%22 clip-rule=%22evenodd%22 d=%22M300 0H0V300H300V0ZM150 200C177.614 200 200 177.614 200 150C200 122.386 177.614 100 150 100C122.386 100 100 122.386 100 150C100 177.614 122.386 200 150 200Z%22 fill=%22black%22/></svg>');
|
|
87
93
|
mask-image: url('data:image/svg+xml;utf8,<svg viewBox=%220 0 300 300%22 fill=%22none%22 xmlns=%22http://www.w3.org/2000/svg%22><path fill-rule=%22evenodd%22 clip-rule=%22evenodd%22 d=%22M300 0H0V300H300V0ZM150 200C177.614 200 200 177.614 200 150C200 122.386 177.614 100 150 100C122.386 100 100 122.386 100 150C100 177.614 122.386 200 150 200Z%22 fill=%22black%22/></svg>');
|
|
88
94
|
-webkit-mask-size: calc(
|
|
@@ -93,13 +99,13 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
93
99
|
);
|
|
94
100
|
}
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
::slotted(vaadin-avatar:not([dir='rtl']):not(:first-child)) {
|
|
97
103
|
margin-left: calc(var(--vaadin-avatar-group-overlap) * -1 - var(--vaadin-avatar-outline-width));
|
|
98
104
|
-webkit-mask-position: calc(50% - var(--vaadin-avatar-size) + var(--vaadin-avatar-group-overlap));
|
|
99
105
|
mask-position: calc(50% - var(--vaadin-avatar-size) + var(--vaadin-avatar-group-overlap));
|
|
100
106
|
}
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
::slotted(vaadin-avatar[dir='rtl']:not(:first-child)) {
|
|
103
109
|
margin-right: calc(var(--vaadin-avatar-group-overlap) * -1);
|
|
104
110
|
-webkit-mask-position: calc(
|
|
105
111
|
50% + var(--vaadin-avatar-size) - var(--vaadin-avatar-group-overlap) + var(--vaadin-avatar-outline-width)
|
|
@@ -110,58 +116,16 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
110
116
|
}
|
|
111
117
|
</style>
|
|
112
118
|
<div id="container" part="container">
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
name="[[item.name]]"
|
|
116
|
-
abbr="[[item.abbr]]"
|
|
117
|
-
img="[[item.img]]"
|
|
118
|
-
part="avatar"
|
|
119
|
-
theme$="[[_theme]]"
|
|
120
|
-
i18n="[[i18n]]"
|
|
121
|
-
color-index="[[item.colorIndex]]"
|
|
122
|
-
with-tooltip
|
|
123
|
-
></vaadin-avatar>
|
|
124
|
-
</template>
|
|
125
|
-
<vaadin-avatar
|
|
126
|
-
id="overflow"
|
|
127
|
-
part="avatar"
|
|
128
|
-
hidden$="[[__computeMoreHidden(items.length, __itemsInView, __maxReached)]]"
|
|
129
|
-
abbr="[[__computeMore(items.length, __itemsInView, maxItemsVisible)]]"
|
|
130
|
-
theme$="[[_theme]]"
|
|
131
|
-
on-click="_onOverflowClick"
|
|
132
|
-
on-keydown="_onOverflowKeyDown"
|
|
133
|
-
aria-haspopup="listbox"
|
|
134
|
-
>
|
|
135
|
-
<vaadin-tooltip slot="tooltip" generator="[[__overflowTextGenerator]]"></vaadin-tooltip>
|
|
136
|
-
</vaadin-avatar>
|
|
119
|
+
<slot></slot>
|
|
120
|
+
<slot name="overflow"></slot>
|
|
137
121
|
</div>
|
|
138
122
|
<vaadin-avatar-group-overlay
|
|
139
123
|
id="overlay"
|
|
140
124
|
opened="{{_opened}}"
|
|
125
|
+
position-target="[[_overflow]]"
|
|
141
126
|
no-vertical-overlap
|
|
142
127
|
on-vaadin-overlay-close="_onVaadinOverlayClose"
|
|
143
|
-
>
|
|
144
|
-
<template>
|
|
145
|
-
<vaadin-avatar-group-list-box on-keydown="_onListKeyDown">
|
|
146
|
-
<template is="dom-repeat" items="[[__computeExtraItems(items.*, __itemsInView, maxItemsVisible)]]">
|
|
147
|
-
<vaadin-item theme="avatar-group-item" role="option">
|
|
148
|
-
<vaadin-avatar
|
|
149
|
-
name="[[item.name]]"
|
|
150
|
-
abbr="[[item.abbr]]"
|
|
151
|
-
img="[[item.img]]"
|
|
152
|
-
i18n="[[i18n]]"
|
|
153
|
-
part="avatar"
|
|
154
|
-
theme$="[[_theme]]"
|
|
155
|
-
color-index="[[item.colorIndex]]"
|
|
156
|
-
tabindex="-1"
|
|
157
|
-
aria-hidden="true"
|
|
158
|
-
></vaadin-avatar>
|
|
159
|
-
[[item.name]]
|
|
160
|
-
</vaadin-item>
|
|
161
|
-
</template>
|
|
162
|
-
</vaadin-avatar-group-list-box>
|
|
163
|
-
</template>
|
|
164
|
-
</vaadin-avatar-group-overlay>
|
|
128
|
+
></vaadin-avatar-group-overlay>
|
|
165
129
|
`;
|
|
166
130
|
}
|
|
167
131
|
|
|
@@ -254,35 +218,63 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
254
218
|
},
|
|
255
219
|
},
|
|
256
220
|
|
|
221
|
+
/** @private */
|
|
222
|
+
_avatars: {
|
|
223
|
+
type: Array,
|
|
224
|
+
value: () => [],
|
|
225
|
+
},
|
|
226
|
+
|
|
257
227
|
/** @private */
|
|
258
228
|
__maxReached: {
|
|
259
229
|
type: Boolean,
|
|
260
230
|
computed: '__computeMaxReached(items.length, maxItemsVisible)',
|
|
261
231
|
},
|
|
262
232
|
|
|
233
|
+
/** @private */
|
|
234
|
+
__items: {
|
|
235
|
+
type: Array,
|
|
236
|
+
},
|
|
237
|
+
|
|
263
238
|
/** @private */
|
|
264
239
|
__itemsInView: {
|
|
265
240
|
type: Number,
|
|
266
241
|
value: null,
|
|
267
242
|
},
|
|
268
243
|
|
|
244
|
+
/** @private */
|
|
245
|
+
_overflow: {
|
|
246
|
+
type: Object,
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/** @private */
|
|
250
|
+
_overflowItems: {
|
|
251
|
+
type: Array,
|
|
252
|
+
observer: '__overflowItemsChanged',
|
|
253
|
+
computed: '__computeOverflowItems(items.*, __itemsInView, maxItemsVisible)',
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
/** @private */
|
|
257
|
+
_overflowTooltip: {
|
|
258
|
+
type: Object,
|
|
259
|
+
},
|
|
260
|
+
|
|
269
261
|
/** @private */
|
|
270
262
|
_opened: {
|
|
271
263
|
type: Boolean,
|
|
272
264
|
observer: '__openedChanged',
|
|
273
|
-
value: false,
|
|
274
265
|
},
|
|
275
|
-
|
|
276
|
-
/** @private */
|
|
277
|
-
__overflowTextGenerator: Object,
|
|
278
266
|
};
|
|
279
267
|
}
|
|
280
268
|
|
|
281
269
|
static get observers() {
|
|
282
270
|
return [
|
|
283
|
-
'__computeMoreTooltip(items.length, __itemsInView, maxItemsVisible)',
|
|
284
271
|
'__itemsChanged(items.splices, items.*)',
|
|
285
272
|
'__i18nItemsChanged(i18n.*, items.length)',
|
|
273
|
+
'__updateAvatarsTheme(_overflow, _avatars, _theme)',
|
|
274
|
+
'__updateAvatars(items.*, __itemsInView, maxItemsVisible, _overflow, i18n)',
|
|
275
|
+
'__updateOverflowAbbr(_overflow, items.length, __itemsInView, maxItemsVisible)',
|
|
276
|
+
'__updateOverflowHidden(_overflow, items.length, __itemsInView, __maxReached)',
|
|
277
|
+
'__updateOverflowTooltip(_overflowTooltip, items.length, __itemsInView, maxItemsVisible)',
|
|
286
278
|
];
|
|
287
279
|
}
|
|
288
280
|
|
|
@@ -290,8 +282,26 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
290
282
|
ready() {
|
|
291
283
|
super.ready();
|
|
292
284
|
|
|
293
|
-
this.
|
|
294
|
-
|
|
285
|
+
this._overflowController = new SlotController(this, 'overflow', 'vaadin-avatar', {
|
|
286
|
+
initializer: (overflow) => {
|
|
287
|
+
overflow.setAttribute('aria-haspopup', 'listbox');
|
|
288
|
+
overflow.setAttribute('aria-expanded', 'false');
|
|
289
|
+
overflow.addEventListener('click', (e) => this._onOverflowClick(e));
|
|
290
|
+
overflow.addEventListener('keydown', (e) => this._onOverflowKeyDown(e));
|
|
291
|
+
|
|
292
|
+
const tooltip = document.createElement('vaadin-tooltip');
|
|
293
|
+
tooltip.setAttribute('slot', 'tooltip');
|
|
294
|
+
overflow.appendChild(tooltip);
|
|
295
|
+
|
|
296
|
+
this._overflow = overflow;
|
|
297
|
+
this._overflowTooltip = tooltip;
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
this.addController(this._overflowController);
|
|
301
|
+
|
|
302
|
+
const overlay = this.$.overlay;
|
|
303
|
+
overlay.renderer = this.__overlayRenderer.bind(this);
|
|
304
|
+
this._overlayElement = overlay;
|
|
295
305
|
|
|
296
306
|
afterNextRender(this, () => {
|
|
297
307
|
this.__setItemsInView();
|
|
@@ -305,17 +315,64 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
305
315
|
this._opened = false;
|
|
306
316
|
}
|
|
307
317
|
|
|
318
|
+
/** @private */
|
|
319
|
+
__getMessage(user, action) {
|
|
320
|
+
return action.replace('{user}', user.name || user.abbr || this.i18n.anonymous);
|
|
321
|
+
}
|
|
322
|
+
|
|
308
323
|
/**
|
|
309
|
-
*
|
|
310
|
-
* @
|
|
324
|
+
* Renders items when they are provided by the `items` property and clears the content otherwise.
|
|
325
|
+
* @param {!HTMLElement} root
|
|
326
|
+
* @param {!Select} _select
|
|
327
|
+
* @private
|
|
311
328
|
*/
|
|
312
|
-
|
|
313
|
-
|
|
329
|
+
__overlayRenderer(root) {
|
|
330
|
+
let listBox = root.firstElementChild;
|
|
331
|
+
if (!listBox) {
|
|
332
|
+
listBox = document.createElement('vaadin-list-box');
|
|
333
|
+
listBox.addEventListener('keydown', (event) => this._onListKeyDown(event));
|
|
334
|
+
root.appendChild(listBox);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
listBox.textContent = '';
|
|
338
|
+
|
|
339
|
+
if (!this._overflowItems) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this._overflowItems.forEach((item) => {
|
|
344
|
+
listBox.appendChild(this.__createItemElement(item));
|
|
345
|
+
});
|
|
314
346
|
}
|
|
315
347
|
|
|
316
348
|
/** @private */
|
|
317
|
-
|
|
318
|
-
|
|
349
|
+
__createItemElement(item) {
|
|
350
|
+
const itemElement = document.createElement('vaadin-item');
|
|
351
|
+
itemElement.setAttribute('theme', 'avatar-group-item');
|
|
352
|
+
itemElement.setAttribute('role', 'option');
|
|
353
|
+
|
|
354
|
+
const avatar = document.createElement('vaadin-avatar');
|
|
355
|
+
itemElement.appendChild(avatar);
|
|
356
|
+
|
|
357
|
+
avatar.setAttribute('aria-hidden', 'true');
|
|
358
|
+
avatar.setAttribute('tabindex', '-1');
|
|
359
|
+
avatar.i18n = this.i18n;
|
|
360
|
+
|
|
361
|
+
if (this._theme) {
|
|
362
|
+
avatar.setAttribute('theme', this._theme);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
avatar.name = item.name;
|
|
366
|
+
avatar.abbr = item.abbr;
|
|
367
|
+
avatar.img = item.img;
|
|
368
|
+
avatar.colorIndex = item.colorIndex;
|
|
369
|
+
|
|
370
|
+
if (item.name) {
|
|
371
|
+
const text = document.createTextNode(item.name);
|
|
372
|
+
itemElement.appendChild(text);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return itemElement;
|
|
319
376
|
}
|
|
320
377
|
|
|
321
378
|
/** @private */
|
|
@@ -331,7 +388,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
331
388
|
/** @private */
|
|
332
389
|
_onOverflowKeyDown(e) {
|
|
333
390
|
if (!this._opened) {
|
|
334
|
-
if (/^(Enter|SpaceBar|\s)
|
|
391
|
+
if (/^(Enter|SpaceBar|\s)$/u.test(e.key)) {
|
|
335
392
|
e.preventDefault();
|
|
336
393
|
this._opened = true;
|
|
337
394
|
}
|
|
@@ -340,7 +397,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
340
397
|
|
|
341
398
|
/** @private */
|
|
342
399
|
_onListKeyDown(event) {
|
|
343
|
-
if (event.key === 'Escape' || event.key === '
|
|
400
|
+
if (event.key === 'Escape' || event.key === 'Tab') {
|
|
344
401
|
this._opened = false;
|
|
345
402
|
}
|
|
346
403
|
}
|
|
@@ -361,17 +418,47 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
361
418
|
}
|
|
362
419
|
|
|
363
420
|
/** @private */
|
|
364
|
-
|
|
421
|
+
__renderAvatars(items) {
|
|
422
|
+
render(
|
|
423
|
+
html`
|
|
424
|
+
${items.map(
|
|
425
|
+
(item) =>
|
|
426
|
+
html`
|
|
427
|
+
<vaadin-avatar
|
|
428
|
+
.name="${item.name}"
|
|
429
|
+
.abbr="${item.abbr}"
|
|
430
|
+
.img="${item.img}"
|
|
431
|
+
.colorIndex="${item.colorIndex}"
|
|
432
|
+
.i18n="${this.i18n}"
|
|
433
|
+
with-tooltip
|
|
434
|
+
></vaadin-avatar>
|
|
435
|
+
`,
|
|
436
|
+
)}
|
|
437
|
+
`,
|
|
438
|
+
this,
|
|
439
|
+
{ renderBefore: this._overflow },
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** @private */
|
|
444
|
+
__updateAvatars(arr, itemsInView, maxItemsVisible, overflow) {
|
|
445
|
+
if (!overflow) {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
365
449
|
const items = arr.base || [];
|
|
366
450
|
const limit = this.__getLimit(items.length, itemsInView, maxItemsVisible);
|
|
367
|
-
|
|
451
|
+
|
|
452
|
+
this.__renderAvatars(limit ? items.slice(0, limit) : items);
|
|
453
|
+
|
|
454
|
+
this._avatars = [...this.querySelectorAll('vaadin-avatar')];
|
|
368
455
|
}
|
|
369
456
|
|
|
370
457
|
/** @private */
|
|
371
|
-
|
|
458
|
+
__computeOverflowItems(arr, itemsInView, maxItemsVisible) {
|
|
372
459
|
const items = arr.base || [];
|
|
373
460
|
const limit = this.__getLimit(items.length, itemsInView, maxItemsVisible);
|
|
374
|
-
return limit ? items.slice(limit) :
|
|
461
|
+
return limit ? items.slice(limit) : [];
|
|
375
462
|
}
|
|
376
463
|
|
|
377
464
|
/** @private */
|
|
@@ -380,21 +467,43 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
380
467
|
}
|
|
381
468
|
|
|
382
469
|
/** @private */
|
|
383
|
-
|
|
384
|
-
|
|
470
|
+
__updateOverflowAbbr(overflow, items, itemsInView, maxItemsVisible) {
|
|
471
|
+
if (overflow) {
|
|
472
|
+
overflow.abbr = `+${items - this.__getLimit(items, itemsInView, maxItemsVisible)}`;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** @private */
|
|
477
|
+
__updateOverflowHidden(overflow, items, itemsInView, maxReached) {
|
|
478
|
+
if (overflow) {
|
|
479
|
+
overflow.toggleAttribute('hidden', !maxReached && !(itemsInView && itemsInView < items));
|
|
480
|
+
}
|
|
385
481
|
}
|
|
386
482
|
|
|
387
483
|
/** @private */
|
|
388
|
-
|
|
389
|
-
|
|
484
|
+
__updateAvatarsTheme(overflow, avatars, theme) {
|
|
485
|
+
if (overflow) {
|
|
486
|
+
[overflow, ...avatars].forEach((avatar) => {
|
|
487
|
+
if (theme) {
|
|
488
|
+
avatar.setAttribute('theme', theme);
|
|
489
|
+
} else {
|
|
490
|
+
avatar.removeAttribute('theme');
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
390
494
|
}
|
|
391
495
|
|
|
392
496
|
/** @private */
|
|
393
|
-
|
|
497
|
+
__updateOverflowTooltip(tooltip, items, itemsInView, maxItemsVisible) {
|
|
498
|
+
if (!tooltip) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
394
502
|
const limit = this.__getLimit(items, itemsInView, maxItemsVisible);
|
|
395
503
|
if (limit == null) {
|
|
396
504
|
return;
|
|
397
505
|
}
|
|
506
|
+
|
|
398
507
|
const result = [];
|
|
399
508
|
for (let i = limit; i < items; i++) {
|
|
400
509
|
const item = this.items[i];
|
|
@@ -402,8 +511,8 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
402
511
|
result.push(item.name || item.abbr || 'anonymous');
|
|
403
512
|
}
|
|
404
513
|
}
|
|
405
|
-
|
|
406
|
-
|
|
514
|
+
|
|
515
|
+
tooltip.text = result.join('\n');
|
|
407
516
|
}
|
|
408
517
|
|
|
409
518
|
/** @private */
|
|
@@ -428,7 +537,6 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
428
537
|
/** @private */
|
|
429
538
|
__itemsChanged(splices, itemsChange) {
|
|
430
539
|
const items = itemsChange.base;
|
|
431
|
-
this.$.items.render();
|
|
432
540
|
this.__setItemsInView();
|
|
433
541
|
|
|
434
542
|
// Mutation using group.splice('items')
|
|
@@ -476,6 +584,10 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
476
584
|
if (base.activeUsers[field]) {
|
|
477
585
|
this.setAttribute('aria-label', base.activeUsers[field].replace('{count}', items || 0));
|
|
478
586
|
}
|
|
587
|
+
|
|
588
|
+
this._avatars.forEach((avatar) => {
|
|
589
|
+
avatar.i18n = base;
|
|
590
|
+
});
|
|
479
591
|
}
|
|
480
592
|
}
|
|
481
593
|
|
|
@@ -483,20 +595,27 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
483
595
|
__openedChanged(opened, wasOpened) {
|
|
484
596
|
if (opened) {
|
|
485
597
|
if (!this._menuElement) {
|
|
486
|
-
this._menuElement = this.
|
|
598
|
+
this._menuElement = this.$.overlay.querySelector('vaadin-list-box');
|
|
487
599
|
this._menuElement.setAttribute('role', 'listbox');
|
|
488
600
|
}
|
|
489
601
|
|
|
490
|
-
this._openedWithFocusRing = this
|
|
602
|
+
this._openedWithFocusRing = this._overflow.hasAttribute('focus-ring');
|
|
491
603
|
|
|
492
604
|
this._menuElement.focus();
|
|
493
605
|
} else if (wasOpened) {
|
|
494
|
-
this
|
|
606
|
+
this._overflow.focus();
|
|
495
607
|
if (this._openedWithFocusRing) {
|
|
496
|
-
this
|
|
608
|
+
this._overflow.setAttribute('focus-ring', '');
|
|
497
609
|
}
|
|
498
610
|
}
|
|
499
|
-
this
|
|
611
|
+
this._overflow.setAttribute('aria-expanded', opened === true);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** @private */
|
|
615
|
+
__overflowItemsChanged(items, oldItems) {
|
|
616
|
+
if (items || oldItems) {
|
|
617
|
+
this.$.overlay.requestContentUpdate();
|
|
618
|
+
}
|
|
500
619
|
}
|
|
501
620
|
|
|
502
621
|
/** @private */
|
|
@@ -541,10 +660,9 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
|
|
|
541
660
|
// Take negative margin into account
|
|
542
661
|
const { marginLeft, marginRight } = getComputedStyle(avatars[1]);
|
|
543
662
|
|
|
544
|
-
const offset =
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
: parseInt(marginLeft, 0) - parseInt(marginRight, 0);
|
|
663
|
+
const offset = this.__isRTL
|
|
664
|
+
? parseInt(marginRight, 0) - parseInt(marginLeft, 0)
|
|
665
|
+
: parseInt(marginLeft, 0) - parseInt(marginRight, 0);
|
|
548
666
|
|
|
549
667
|
return Math.floor((this.$.container.offsetWidth - avatarWidth) / (avatarWidth + offset));
|
|
550
668
|
}
|
|
@@ -55,24 +55,13 @@ registerStyles('vaadin-avatar-group-overlay', [overlay, menuOverlayCore, avatarG
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
registerStyles(
|
|
58
|
-
'vaadin-
|
|
58
|
+
'vaadin-item',
|
|
59
59
|
css`
|
|
60
|
-
|
|
60
|
+
:host([theme='avatar-group-item']) {
|
|
61
61
|
padding: var(--lumo-space-xs);
|
|
62
|
-
padding-
|
|
62
|
+
padding-inline-end: var(--lumo-space-m);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
:host([dir='rtl']) [part='items'] ::slotted(vaadin-item[theme='avatar-group-item']) {
|
|
66
|
-
padding: var(--lumo-space-xs);
|
|
67
|
-
padding-left: var(--lumo-space-m);
|
|
68
|
-
}
|
|
69
|
-
`,
|
|
70
|
-
{ moduleId: 'lumo-avatar-group-list-box' },
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
registerStyles(
|
|
74
|
-
'vaadin-item',
|
|
75
|
-
css`
|
|
76
65
|
:host([theme='avatar-group-item']) [part='content'] {
|
|
77
66
|
display: flex;
|
|
78
67
|
align-items: center;
|
|
@@ -47,24 +47,13 @@ registerStyles('vaadin-avatar-group-overlay', [menuOverlay, avatarGroupOverlay],
|
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
registerStyles(
|
|
50
|
-
'vaadin-
|
|
50
|
+
'vaadin-item',
|
|
51
51
|
css`
|
|
52
|
-
|
|
52
|
+
:host([theme='avatar-group-item']) {
|
|
53
53
|
padding: 8px;
|
|
54
|
-
padding-
|
|
54
|
+
padding-inline-end: 24px;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
:host([dir='rtl']) [part='items'] ::slotted(vaadin-item[theme='avatar-group-item']) {
|
|
58
|
-
padding: 8px;
|
|
59
|
-
padding-left: 24px;
|
|
60
|
-
}
|
|
61
|
-
`,
|
|
62
|
-
{ moduleId: 'material-avatar-group-list-box' },
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
registerStyles(
|
|
66
|
-
'vaadin-item',
|
|
67
|
-
css`
|
|
68
57
|
:host([theme='avatar-group-item']) [part='content'] {
|
|
69
58
|
display: flex;
|
|
70
59
|
align-items: center;
|
package/web-types.json
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/avatar-group",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "24.0.0-alpha10",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
8
8
|
"elements": [
|
|
9
9
|
{
|
|
10
10
|
"name": "vaadin-avatar-group",
|
|
11
|
-
"description": "`<vaadin-avatar-group>` is a Web Component providing avatar group displaying functionality.\n\nTo create the avatar group, first add the component to the page:\n\n```\n<vaadin-avatar-group></vaadin-avatar-group>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/
|
|
11
|
+
"description": "`<vaadin-avatar-group>` is a Web Component providing avatar group displaying functionality.\n\nTo create the avatar group, first add the component to the page:\n\n```\n<vaadin-avatar-group></vaadin-avatar-group>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar-group#property-items) property to initialize the structure:\n\n```\ndocument.querySelector('vaadin-avatar-group').items = [\n {name: 'John Doe'},\n {abbr: 'AB'}\n];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n----------- | ---------------\n`container` | The container element\n\nSee the [`<vaadin-avatar>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar) documentation for the available\nstate attributes and stylable shadow parts of avatar elements.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-avatar-group>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-overlay).",
|
|
12
12
|
"attributes": [
|
|
13
|
+
{
|
|
14
|
+
"name": "overlay-class",
|
|
15
|
+
"description": "A space-delimited list of CSS class names to set on the overlay element.\nThis property does not affect other CSS class names set manually via JS.\n\nNote, if the CSS class name was set with this property, clearing it will\nremove it from the overlay, even if the same class name was also added\nmanually, e.g. by using `classList.add()` in the `renderer` function.",
|
|
16
|
+
"value": {
|
|
17
|
+
"type": [
|
|
18
|
+
"string",
|
|
19
|
+
"null",
|
|
20
|
+
"undefined"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
13
24
|
{
|
|
14
25
|
"name": "max-items-visible",
|
|
15
26
|
"description": "The maximum number of avatars to display. By default, all the avatars are displayed.\nWhen _maxItemsVisible_ is set, the overflowing avatars are grouped into one avatar with\na dropdown. Setting 0 or 1 has no effect so there are always at least two avatars visible.",
|
|
@@ -35,9 +46,20 @@
|
|
|
35
46
|
],
|
|
36
47
|
"js": {
|
|
37
48
|
"properties": [
|
|
49
|
+
{
|
|
50
|
+
"name": "overlayClass",
|
|
51
|
+
"description": "A space-delimited list of CSS class names to set on the overlay element.\nThis property does not affect other CSS class names set manually via JS.\n\nNote, if the CSS class name was set with this property, clearing it will\nremove it from the overlay, even if the same class name was also added\nmanually, e.g. by using `classList.add()` in the `renderer` function.",
|
|
52
|
+
"value": {
|
|
53
|
+
"type": [
|
|
54
|
+
"string",
|
|
55
|
+
"null",
|
|
56
|
+
"undefined"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
38
60
|
{
|
|
39
61
|
"name": "items",
|
|
40
|
-
"description": "An array containing the items which will be stamped as avatars.\n\nThe items objects allow to configure [`name`](https://cdn.vaadin.com/vaadin-web-components/
|
|
62
|
+
"description": "An array containing the items which will be stamped as avatars.\n\nThe items objects allow to configure [`name`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-colorIndex) properties on the\nstamped avatars.\n\n#### Example\n\n```js\ngroup.items = [\n {\n name: 'User name',\n img: 'url-to-image.png'\n },\n {\n abbr: 'JD',\n colorIndex: 1\n },\n];\n```",
|
|
41
63
|
"value": {
|
|
42
64
|
"type": [
|
|
43
65
|
"Array.<AvatarGroupItem>",
|
package/web-types.lit.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/avatar-group",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "24.0.0-alpha10",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"framework": "lit",
|
|
7
7
|
"framework-config": {
|
|
@@ -16,12 +16,19 @@
|
|
|
16
16
|
"elements": [
|
|
17
17
|
{
|
|
18
18
|
"name": "vaadin-avatar-group",
|
|
19
|
-
"description": "`<vaadin-avatar-group>` is a Web Component providing avatar group displaying functionality.\n\nTo create the avatar group, first add the component to the page:\n\n```\n<vaadin-avatar-group></vaadin-avatar-group>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/
|
|
19
|
+
"description": "`<vaadin-avatar-group>` is a Web Component providing avatar group displaying functionality.\n\nTo create the avatar group, first add the component to the page:\n\n```\n<vaadin-avatar-group></vaadin-avatar-group>\n```\n\nAnd then use [`items`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar-group#property-items) property to initialize the structure:\n\n```\ndocument.querySelector('vaadin-avatar-group').items = [\n {name: 'John Doe'},\n {abbr: 'AB'}\n];\n```\n\n### Styling\n\nThe following shadow DOM parts are exposed for styling:\n\nPart name | Description\n----------- | ---------------\n`container` | The container element\n\nSee the [`<vaadin-avatar>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar) documentation for the available\nstate attributes and stylable shadow parts of avatar elements.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nIn addition to `<vaadin-avatar-group>` itself, the following internal\ncomponents are themable:\n\n- `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-overlay).",
|
|
20
20
|
"extension": true,
|
|
21
21
|
"attributes": [
|
|
22
|
+
{
|
|
23
|
+
"name": ".overlayClass",
|
|
24
|
+
"description": "A space-delimited list of CSS class names to set on the overlay element.\nThis property does not affect other CSS class names set manually via JS.\n\nNote, if the CSS class name was set with this property, clearing it will\nremove it from the overlay, even if the same class name was also added\nmanually, e.g. by using `classList.add()` in the `renderer` function.",
|
|
25
|
+
"value": {
|
|
26
|
+
"kind": "expression"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
22
29
|
{
|
|
23
30
|
"name": ".items",
|
|
24
|
-
"description": "An array containing the items which will be stamped as avatars.\n\nThe items objects allow to configure [`name`](https://cdn.vaadin.com/vaadin-web-components/
|
|
31
|
+
"description": "An array containing the items which will be stamped as avatars.\n\nThe items objects allow to configure [`name`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha10/#/elements/vaadin-avatar#property-colorIndex) properties on the\nstamped avatars.\n\n#### Example\n\n```js\ngroup.items = [\n {\n name: 'User name',\n img: 'url-to-image.png'\n },\n {\n abbr: 'JD',\n colorIndex: 1\n },\n];\n```",
|
|
25
32
|
"value": {
|
|
26
33
|
"kind": "expression"
|
|
27
34
|
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) 2020 - 2022 Vaadin Ltd.
|
|
4
|
-
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
-
*/
|
|
6
|
-
import { ListBox } from '@vaadin/list-box/src/vaadin-list-box.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* An element used internally by `<vaadin-avatar-group>`. Not intended to be used separately.
|
|
10
|
-
*
|
|
11
|
-
* @extends ListBox
|
|
12
|
-
* @private
|
|
13
|
-
*/
|
|
14
|
-
class AvatarGroupListBox extends ListBox {
|
|
15
|
-
static get is() {
|
|
16
|
-
return 'vaadin-avatar-group-list-box';
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
customElements.define(AvatarGroupListBox.is, AvatarGroupListBox);
|