@vaadin/avatar-group 23.3.0-alpha3 → 24.0.0-alpha1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/avatar-group",
3
- "version": "23.3.0-alpha3",
3
+ "version": "24.0.0-alpha1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -37,14 +37,14 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@polymer/polymer": "^3.0.0",
40
- "@vaadin/avatar": "23.3.0-alpha3",
41
- "@vaadin/component-base": "23.3.0-alpha3",
42
- "@vaadin/item": "23.3.0-alpha3",
43
- "@vaadin/list-box": "23.3.0-alpha3",
44
- "@vaadin/vaadin-lumo-styles": "23.3.0-alpha3",
45
- "@vaadin/vaadin-material-styles": "23.3.0-alpha3",
46
- "@vaadin/vaadin-overlay": "23.3.0-alpha3",
47
- "@vaadin/vaadin-themable-mixin": "23.3.0-alpha3"
40
+ "@vaadin/avatar": "24.0.0-alpha1",
41
+ "@vaadin/component-base": "24.0.0-alpha1",
42
+ "@vaadin/item": "24.0.0-alpha1",
43
+ "@vaadin/list-box": "24.0.0-alpha1",
44
+ "@vaadin/overlay": "24.0.0-alpha1",
45
+ "@vaadin/vaadin-lumo-styles": "24.0.0-alpha1",
46
+ "@vaadin/vaadin-material-styles": "24.0.0-alpha1",
47
+ "@vaadin/vaadin-themable-mixin": "24.0.0-alpha1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@esm-bundle/chai": "^4.3.4",
@@ -55,5 +55,5 @@
55
55
  "web-types.json",
56
56
  "web-types.lit.json"
57
57
  ],
58
- "gitHead": "e86cd2abf3e28bade37711291331415d92c454ec"
58
+ "gitHead": "427527c27c4b27822d61fd41d38d7b170134770b"
59
59
  }
@@ -3,16 +3,16 @@
3
3
  * Copyright (c) 2020 - 2022 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { OverlayElement } from '@vaadin/vaadin-overlay/src/vaadin-overlay.js';
7
- import { PositionMixin } from '@vaadin/vaadin-overlay/src/vaadin-overlay-position-mixin.js';
6
+ import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
7
+ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
8
8
 
9
9
  /**
10
10
  * An element used internally by `<vaadin-avatar-group>`. Not intended to be used separately.
11
11
  *
12
- * @extends OverlayElement
12
+ * @extends Overlay
13
13
  * @private
14
14
  */
15
- class AvatarGroupOverlay extends PositionMixin(OverlayElement) {
15
+ class AvatarGroupOverlay extends PositionMixin(Overlay) {
16
16
  static get is() {
17
17
  return 'vaadin-avatar-group-overlay';
18
18
  }
@@ -4,6 +4,7 @@
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';
8
9
  import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
9
10
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
@@ -51,7 +52,9 @@ export interface AvatarGroupItem {
51
52
  * Part name | Description
52
53
  * ----------- | ---------------
53
54
  * `container` | The container element
54
- * `avatar` | Individual avatars
55
+ *
56
+ * See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
57
+ * state attributes and stylable shadow parts of avatar elements.
55
58
  *
56
59
  * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
57
60
  *
@@ -60,10 +63,9 @@ export interface AvatarGroupItem {
60
63
  * In addition to `<vaadin-avatar-group>` itself, the following internal
61
64
  * components are themable:
62
65
  *
63
- * - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
64
66
  * - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
65
67
  */
66
- declare class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(HTMLElement))) {
68
+ declare class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(ControllerMixin(HTMLElement)))) {
67
69
  readonly _avatars: HTMLElement[];
68
70
 
69
71
  /**
@@ -3,17 +3,18 @@
3
3
  * Copyright (c) 2020 - 2022 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 './vaadin-avatar-group-list-box.js';
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
12
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
14
13
  import { announce } from '@vaadin/component-base/src/a11y-announcer.js';
14
+ import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
15
15
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
16
16
  import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
17
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
17
18
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
18
19
 
19
20
  const MINIMUM_DISPLAYED_AVATARS = 2;
@@ -43,7 +44,9 @@ const MINIMUM_DISPLAYED_AVATARS = 2;
43
44
  * Part name | Description
44
45
  * ----------- | ---------------
45
46
  * `container` | The container element
46
- * `avatar` | Individual avatars
47
+ *
48
+ * See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
49
+ * state attributes and stylable shadow parts of avatar elements.
47
50
  *
48
51
  * See [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.
49
52
  *
@@ -52,15 +55,15 @@ const MINIMUM_DISPLAYED_AVATARS = 2;
52
55
  * In addition to `<vaadin-avatar-group>` itself, the following internal
53
56
  * components are themable:
54
57
  *
55
- * - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
56
58
  * - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
57
59
  *
58
60
  * @extends HTMLElement
61
+ * @mixes ControllerMixin
59
62
  * @mixes ElementMixin
60
63
  * @mixes ThemableMixin
61
64
  * @mixes ResizeMixin
62
65
  */
63
- class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement))) {
66
+ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(ControllerMixin(PolymerElement)))) {
64
67
  static get template() {
65
68
  return html`
66
69
  <style>
@@ -82,7 +85,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
82
85
  flex-wrap: nowrap;
83
86
  }
84
87
 
85
- [part='avatar']:not(:first-child) {
88
+ ::slotted(vaadin-avatar:not(:first-child)) {
86
89
  -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
90
  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
91
  -webkit-mask-size: calc(
@@ -93,13 +96,13 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
93
96
  );
94
97
  }
95
98
 
96
- [part='avatar']:not([dir='rtl']):not(:first-child) {
99
+ ::slotted(vaadin-avatar:not([dir='rtl']):not(:first-child)) {
97
100
  margin-left: calc(var(--vaadin-avatar-group-overlap) * -1 - var(--vaadin-avatar-outline-width));
98
101
  -webkit-mask-position: calc(50% - var(--vaadin-avatar-size) + var(--vaadin-avatar-group-overlap));
99
102
  mask-position: calc(50% - var(--vaadin-avatar-size) + var(--vaadin-avatar-group-overlap));
100
103
  }
101
104
 
102
- [part='avatar'][dir='rtl']:not(:first-child) {
105
+ ::slotted(vaadin-avatar[dir='rtl']:not(:first-child)) {
103
106
  margin-right: calc(var(--vaadin-avatar-group-overlap) * -1);
104
107
  -webkit-mask-position: calc(
105
108
  50% + var(--vaadin-avatar-size) - var(--vaadin-avatar-group-overlap) + var(--vaadin-avatar-outline-width)
@@ -110,58 +113,16 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
110
113
  }
111
114
  </style>
112
115
  <div id="container" part="container">
113
- <template id="items" is="dom-repeat" items="[[__computeItems(items.*, __itemsInView, maxItemsVisible)]]">
114
- <vaadin-avatar
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>
116
+ <slot></slot>
117
+ <slot name="overflow"></slot>
137
118
  </div>
138
119
  <vaadin-avatar-group-overlay
139
120
  id="overlay"
140
121
  opened="{{_opened}}"
122
+ position-target="[[_overflow]]"
141
123
  no-vertical-overlap
142
124
  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>
125
+ ></vaadin-avatar-group-overlay>
165
126
  `;
166
127
  }
167
128
 
@@ -254,35 +215,63 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
254
215
  },
255
216
  },
256
217
 
218
+ /** @private */
219
+ _avatars: {
220
+ type: Array,
221
+ value: () => [],
222
+ },
223
+
257
224
  /** @private */
258
225
  __maxReached: {
259
226
  type: Boolean,
260
227
  computed: '__computeMaxReached(items.length, maxItemsVisible)',
261
228
  },
262
229
 
230
+ /** @private */
231
+ __items: {
232
+ type: Array,
233
+ },
234
+
263
235
  /** @private */
264
236
  __itemsInView: {
265
237
  type: Number,
266
238
  value: null,
267
239
  },
268
240
 
241
+ /** @private */
242
+ _overflow: {
243
+ type: Object,
244
+ },
245
+
246
+ /** @private */
247
+ _overflowItems: {
248
+ type: Array,
249
+ observer: '__overflowItemsChanged',
250
+ computed: '__computeOverflowItems(items.*, __itemsInView, maxItemsVisible)',
251
+ },
252
+
253
+ /** @private */
254
+ _overflowTooltip: {
255
+ type: Object,
256
+ },
257
+
269
258
  /** @private */
270
259
  _opened: {
271
260
  type: Boolean,
272
261
  observer: '__openedChanged',
273
- value: false,
274
262
  },
275
-
276
- /** @private */
277
- __overflowTextGenerator: Object,
278
263
  };
279
264
  }
280
265
 
281
266
  static get observers() {
282
267
  return [
283
- '__computeMoreTooltip(items.length, __itemsInView, maxItemsVisible)',
284
268
  '__itemsChanged(items.splices, items.*)',
285
269
  '__i18nItemsChanged(i18n.*, items.length)',
270
+ '__updateAvatarsTheme(_overflow, _avatars, _theme)',
271
+ '__updateAvatars(items.*, __itemsInView, maxItemsVisible)',
272
+ '__updateOverflowAbbr(_overflow, items.length, __itemsInView, maxItemsVisible)',
273
+ '__updateOverflowHidden(_overflow, items.length, __itemsInView, __maxReached)',
274
+ '__updateOverflowTooltip(_overflowTooltip, items.length, __itemsInView, maxItemsVisible)',
286
275
  ];
287
276
  }
288
277
 
@@ -290,20 +279,38 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
290
279
  ready() {
291
280
  super.ready();
292
281
 
293
- this._overlayElement = this.shadowRoot.querySelector('vaadin-avatar-group-overlay');
294
- this._overlayElement.positionTarget = this.$.overflow;
282
+ this._overflowController = new SlotController(
283
+ this,
284
+ 'overflow',
285
+ () => document.createElement('vaadin-avatar'),
286
+ (_, 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
+ this.$.overlay.renderer = this.__overlayRenderer.bind(this);
295
303
 
296
304
  afterNextRender(this, () => {
297
305
  this.__setItemsInView();
298
306
  });
299
307
  }
300
308
 
301
- /**
302
- * @return {!Array<!HTMLElement>}
303
- * @protected
304
- */
305
- get _avatars() {
306
- return this.shadowRoot.querySelectorAll('vaadin-avatar');
309
+ /** @protected */
310
+ disconnectedCallback() {
311
+ super.disconnectedCallback();
312
+
313
+ this._opened = false;
307
314
  }
308
315
 
309
316
  /** @private */
@@ -311,6 +318,76 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
311
318
  return action.replace('{user}', user.name || user.abbr || this.i18n.anonymous);
312
319
  }
313
320
 
321
+ /**
322
+ * Renders items when they are provided by the `items` property and clears the content otherwise.
323
+ * @param {!HTMLElement} root
324
+ * @param {!Select} _select
325
+ * @private
326
+ */
327
+ __overlayRenderer(root) {
328
+ let listBox = root.firstElementChild;
329
+ if (!listBox) {
330
+ listBox = document.createElement('vaadin-list-box');
331
+ listBox.addEventListener('keydown', (event) => this._onListKeyDown(event));
332
+ root.appendChild(listBox);
333
+ }
334
+
335
+ listBox.textContent = '';
336
+
337
+ if (!this._overflowItems) {
338
+ return;
339
+ }
340
+
341
+ this._overflowItems.forEach((item) => {
342
+ listBox.appendChild(this.__createItemElement(item));
343
+ });
344
+ }
345
+
346
+ /** @private */
347
+ __createAvatar(item) {
348
+ const avatar = document.createElement('vaadin-avatar');
349
+ avatar.name = item.name;
350
+ avatar.abbr = item.abbr;
351
+ avatar.img = item.img;
352
+ avatar.colorIndex = item.colorIndex;
353
+
354
+ avatar.withTooltip = true;
355
+ avatar.i18n = this.i18n;
356
+ avatar._item = item;
357
+
358
+ return avatar;
359
+ }
360
+
361
+ /** @private */
362
+ __createItemElement(item) {
363
+ const itemElement = document.createElement('vaadin-item');
364
+ itemElement.setAttribute('theme', 'avatar-group-item');
365
+ itemElement.setAttribute('role', 'option');
366
+
367
+ const avatar = document.createElement('vaadin-avatar');
368
+ itemElement.appendChild(avatar);
369
+
370
+ avatar.setAttribute('aria-hidden', 'true');
371
+ avatar.setAttribute('tabindex', '-1');
372
+ avatar.i18n = this.i18n;
373
+
374
+ if (this._theme) {
375
+ avatar.setAttribute('theme', this._theme);
376
+ }
377
+
378
+ avatar.name = item.name;
379
+ avatar.abbr = item.abbr;
380
+ avatar.img = item.img;
381
+ avatar.colorIndex = item.colorIndex;
382
+
383
+ if (item.name) {
384
+ const text = document.createTextNode(item.name);
385
+ itemElement.appendChild(text);
386
+ }
387
+
388
+ return itemElement;
389
+ }
390
+
314
391
  /** @private */
315
392
  _onOverflowClick(e) {
316
393
  e.stopPropagation();
@@ -354,17 +431,48 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
354
431
  }
355
432
 
356
433
  /** @private */
357
- __computeItems(arr, itemsInView, maxItemsVisible) {
434
+ __updateAvatars(arr, itemsInView, maxItemsVisible) {
358
435
  const items = arr.base || [];
359
436
  const limit = this.__getLimit(items.length, itemsInView, maxItemsVisible);
360
- return limit ? items.slice(0, limit) : items;
437
+
438
+ const newItems = limit ? items.slice(0, limit) : items;
439
+ const oldItems = this.__oldAvatarItems || [];
440
+
441
+ if (newItems.length || oldItems.length) {
442
+ const removed = oldItems.filter((item) => !newItems.includes(item));
443
+ const added = [...newItems];
444
+
445
+ this._avatars.forEach((avatar) => {
446
+ const item = avatar._item;
447
+ if (removed.includes(item)) {
448
+ avatar.remove();
449
+ } else if (added.includes(item)) {
450
+ added.splice(added.indexOf(item), 1);
451
+ }
452
+ });
453
+
454
+ this.__addAvatars(added, newItems);
455
+ }
456
+
457
+ this._avatars = [...this.querySelectorAll('vaadin-avatar')];
458
+ this.__oldAvatarItems = newItems;
361
459
  }
362
460
 
363
461
  /** @private */
364
- __computeExtraItems(arr, itemsInView, maxItemsVisible) {
462
+ __addAvatars(itemsToAdd, allItems) {
463
+ itemsToAdd.forEach((item) => {
464
+ const avatar = this.__createAvatar(item);
465
+ const nextItem = allItems[allItems.indexOf(item) + 1];
466
+ const nextAvatar = this._avatars.find((el) => el._item === nextItem);
467
+ this.insertBefore(avatar, nextAvatar || this._overflow);
468
+ });
469
+ }
470
+
471
+ /** @private */
472
+ __computeOverflowItems(arr, itemsInView, maxItemsVisible) {
365
473
  const items = arr.base || [];
366
474
  const limit = this.__getLimit(items.length, itemsInView, maxItemsVisible);
367
- return limit ? items.slice(limit) : items;
475
+ return limit ? items.slice(limit) : [];
368
476
  }
369
477
 
370
478
  /** @private */
@@ -373,21 +481,43 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
373
481
  }
374
482
 
375
483
  /** @private */
376
- __computeMore(items, itemsInView, maxItemsVisible) {
377
- return `+${items - this.__getLimit(items, itemsInView, maxItemsVisible)}`;
484
+ __updateOverflowAbbr(overflow, items, itemsInView, maxItemsVisible) {
485
+ if (overflow) {
486
+ overflow.abbr = `+${items - this.__getLimit(items, itemsInView, maxItemsVisible)}`;
487
+ }
378
488
  }
379
489
 
380
490
  /** @private */
381
- __computeMoreHidden(items, itemsInView, maxReached) {
382
- return !maxReached && !(itemsInView && itemsInView < items);
491
+ __updateOverflowHidden(overflow, items, itemsInView, maxReached) {
492
+ if (overflow) {
493
+ overflow.toggleAttribute('hidden', !maxReached && !(itemsInView && itemsInView < items));
494
+ }
383
495
  }
384
496
 
385
497
  /** @private */
386
- __computeMoreTooltip(items, itemsInView, maxItemsVisible) {
498
+ __updateAvatarsTheme(overflow, avatars, theme) {
499
+ if (overflow) {
500
+ [overflow, ...avatars].forEach((avatar) => {
501
+ if (theme) {
502
+ avatar.setAttribute('theme', theme);
503
+ } else {
504
+ avatar.removeAttribute('theme');
505
+ }
506
+ });
507
+ }
508
+ }
509
+
510
+ /** @private */
511
+ __updateOverflowTooltip(tooltip, items, itemsInView, maxItemsVisible) {
512
+ if (!tooltip) {
513
+ return;
514
+ }
515
+
387
516
  const limit = this.__getLimit(items, itemsInView, maxItemsVisible);
388
517
  if (limit == null) {
389
518
  return;
390
519
  }
520
+
391
521
  const result = [];
392
522
  for (let i = limit; i < items; i++) {
393
523
  const item = this.items[i];
@@ -395,8 +525,8 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
395
525
  result.push(item.name || item.abbr || 'anonymous');
396
526
  }
397
527
  }
398
- // Override generated tooltip text
399
- this.__overflowTextGenerator = () => result.join('\n');
528
+
529
+ tooltip.text = result.join('\n');
400
530
  }
401
531
 
402
532
  /** @private */
@@ -421,7 +551,6 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
421
551
  /** @private */
422
552
  __itemsChanged(splices, itemsChange) {
423
553
  const items = itemsChange.base;
424
- this.$.items.render();
425
554
  this.__setItemsInView();
426
555
 
427
556
  // Mutation using group.splice('items')
@@ -469,6 +598,10 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
469
598
  if (base.activeUsers[field]) {
470
599
  this.setAttribute('aria-label', base.activeUsers[field].replace('{count}', items || 0));
471
600
  }
601
+
602
+ this._avatars.forEach((avatar) => {
603
+ avatar.i18n = base;
604
+ });
472
605
  }
473
606
  }
474
607
 
@@ -476,20 +609,27 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
476
609
  __openedChanged(opened, wasOpened) {
477
610
  if (opened) {
478
611
  if (!this._menuElement) {
479
- this._menuElement = this._overlayElement.content.querySelector('vaadin-avatar-group-list-box');
612
+ this._menuElement = this.$.overlay.querySelector('vaadin-list-box');
480
613
  this._menuElement.setAttribute('role', 'listbox');
481
614
  }
482
615
 
483
- this._openedWithFocusRing = this.$.overflow.hasAttribute('focus-ring');
616
+ this._openedWithFocusRing = this._overflow.hasAttribute('focus-ring');
484
617
 
485
618
  this._menuElement.focus();
486
619
  } else if (wasOpened) {
487
- this.$.overflow.focus();
620
+ this._overflow.focus();
488
621
  if (this._openedWithFocusRing) {
489
- this.$.overflow.setAttribute('focus-ring', '');
622
+ this._overflow.setAttribute('focus-ring', '');
490
623
  }
491
624
  }
492
- this.$.overflow.setAttribute('aria-expanded', opened === true);
625
+ this._overflow.setAttribute('aria-expanded', opened === true);
626
+ }
627
+
628
+ /** @private */
629
+ __overflowItemsChanged(items, oldItems) {
630
+ if (items || oldItems) {
631
+ this.$.overlay.requestContentUpdate();
632
+ }
493
633
  }
494
634
 
495
635
  /** @private */
@@ -55,24 +55,13 @@ registerStyles('vaadin-avatar-group-overlay', [overlay, menuOverlayCore, avatarG
55
55
  });
56
56
 
57
57
  registerStyles(
58
- 'vaadin-avatar-group-list-box',
58
+ 'vaadin-item',
59
59
  css`
60
- [part='items'] ::slotted(vaadin-item[theme='avatar-group-item']) {
60
+ :host([theme='avatar-group-item']) {
61
61
  padding: var(--lumo-space-xs);
62
- padding-right: var(--lumo-space-m);
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;
@@ -1,6 +1,6 @@
1
1
  import '@vaadin/avatar/theme/lumo/vaadin-avatar.js';
2
2
  import '@vaadin/item/theme/lumo/vaadin-item.js';
3
3
  import '@vaadin/list-box/theme/lumo/vaadin-list-box.js';
4
- import '@vaadin/vaadin-overlay/theme/lumo/vaadin-overlay.js';
4
+ import '@vaadin/overlay/theme/lumo/vaadin-overlay.js';
5
5
  import './vaadin-avatar-group-styles.js';
6
6
  import '../../src/vaadin-avatar-group.js';
@@ -47,24 +47,13 @@ registerStyles('vaadin-avatar-group-overlay', [menuOverlay, avatarGroupOverlay],
47
47
  });
48
48
 
49
49
  registerStyles(
50
- 'vaadin-avatar-group-list-box',
50
+ 'vaadin-item',
51
51
  css`
52
- [part='items'] ::slotted(vaadin-item[theme='avatar-group-item']) {
52
+ :host([theme='avatar-group-item']) {
53
53
  padding: 8px;
54
- padding-right: 24px;
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;
@@ -1,6 +1,6 @@
1
1
  import '@vaadin/avatar/theme/material/vaadin-avatar.js';
2
2
  import '@vaadin/item/theme/material/vaadin-item.js';
3
3
  import '@vaadin/list-box/theme/material/vaadin-list-box.js';
4
- import '@vaadin/vaadin-overlay/theme/material/vaadin-overlay.js';
4
+ import '@vaadin/overlay/theme/material/vaadin-overlay.js';
5
5
  import './vaadin-avatar-group-styles.js';
6
6
  import '../../src/vaadin-avatar-group.js';
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/avatar-group",
4
- "version": "23.3.0-alpha3",
4
+ "version": "24.0.0-alpha1",
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/23.3.0-alpha3/#/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`avatar` | Individual avatars\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-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-list-box).\n- `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-overlay).",
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-alpha1/#/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-alpha1/#/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-alpha1/#/elements/vaadin-overlay).",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "max-items-visible",
@@ -37,7 +37,7 @@
37
37
  "properties": [
38
38
  {
39
39
  "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/23.3.0-alpha3/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/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```",
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/24.0.0-alpha1/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha1/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha1/#/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
41
  "value": {
42
42
  "type": [
43
43
  "Array.<AvatarGroupItem>",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/avatar-group",
4
- "version": "23.3.0-alpha3",
4
+ "version": "24.0.0-alpha1",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,12 +16,12 @@
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/23.3.0-alpha3/#/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`avatar` | Individual avatars\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-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-list-box).\n- `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-overlay).",
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-alpha1/#/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-alpha1/#/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-alpha1/#/elements/vaadin-overlay).",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {
23
23
  "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/23.3.0-alpha3/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/23.3.0-alpha3/#/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```",
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/24.0.0-alpha1/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha1/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha1/#/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
25
  "value": {
26
26
  "kind": "expression"
27
27
  }
@@ -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);