@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/avatar-group",
3
- "version": "23.3.3",
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": "~23.3.3",
41
- "@vaadin/component-base": "~23.3.3",
42
- "@vaadin/item": "~23.3.3",
43
- "@vaadin/list-box": "~23.3.3",
44
- "@vaadin/overlay": "~23.3.3",
45
- "@vaadin/vaadin-lumo-styles": "~23.3.3",
46
- "@vaadin/vaadin-material-styles": "~23.3.3",
47
- "@vaadin/vaadin-themable-mixin": "~23.3.3"
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": "1529ed623e053d28a3c1c66af55ebe402743ddd0"
59
+ "gitHead": "2e04534d8b47bcd216f89b5f849bafef1a73b174"
59
60
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * Copyright (c) 2020 - 2022 Vaadin Ltd.
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 - 2022 Vaadin Ltd.
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
- * `avatar` | Individual avatars
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(ElementMixin(ThemableMixin(HTMLElement))) {
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 - 2022 Vaadin Ltd.
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 './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
- 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
- * `avatar` | Individual avatars
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 html`
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
- [part='avatar']:not(:first-child) {
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
- [part='avatar']:not([dir='rtl']):not(:first-child) {
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
- [part='avatar'][dir='rtl']:not(:first-child) {
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
- <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>
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._overlayElement = this.shadowRoot.querySelector('vaadin-avatar-group-overlay');
294
- this._overlayElement.positionTarget = this.$.overflow;
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
- * @return {!Array<!HTMLElement>}
310
- * @protected
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
- get _avatars() {
313
- return this.shadowRoot.querySelectorAll('vaadin-avatar');
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
- __getMessage(user, action) {
318
- return action.replace('{user}', user.name || user.abbr || this.i18n.anonymous);
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)$/.test(e.key)) {
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 === 'Esc' || /^(Tab)$/.test(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
- __computeItems(arr, itemsInView, maxItemsVisible) {
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
- return limit ? items.slice(0, limit) : items;
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
- __computeExtraItems(arr, itemsInView, maxItemsVisible) {
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) : items;
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
- __computeMore(items, itemsInView, maxItemsVisible) {
384
- return `+${items - this.__getLimit(items, itemsInView, maxItemsVisible)}`;
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
- __computeMoreHidden(items, itemsInView, maxReached) {
389
- return !maxReached && !(itemsInView && itemsInView < items);
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
- __computeMoreTooltip(items, itemsInView, maxItemsVisible) {
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
- // Override generated tooltip text
406
- this.__overflowTextGenerator = () => result.join('\n');
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._overlayElement.content.querySelector('vaadin-avatar-group-list-box');
598
+ this._menuElement = this.$.overlay.querySelector('vaadin-list-box');
487
599
  this._menuElement.setAttribute('role', 'listbox');
488
600
  }
489
601
 
490
- this._openedWithFocusRing = this.$.overflow.hasAttribute('focus-ring');
602
+ this._openedWithFocusRing = this._overflow.hasAttribute('focus-ring');
491
603
 
492
604
  this._menuElement.focus();
493
605
  } else if (wasOpened) {
494
- this.$.overflow.focus();
606
+ this._overflow.focus();
495
607
  if (this._openedWithFocusRing) {
496
- this.$.overflow.setAttribute('focus-ring', '');
608
+ this._overflow.setAttribute('focus-ring', '');
497
609
  }
498
610
  }
499
- this.$.overflow.setAttribute('aria-expanded', opened === true);
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
- this.getAttribute('dir') === 'rtl'
546
- ? parseInt(marginRight, 0) - parseInt(marginLeft, 0)
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-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;
@@ -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;
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": "23.3.3",
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/23.3.3/#/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.3/#/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.3/#/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-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/23.3.3/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/23.3.3/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/23.3.3/#/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```",
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>",
@@ -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.3",
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/23.3.3/#/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.3/#/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.3/#/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-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/23.3.3/#/elements/vaadin-avatar#property-name),\n[`abbr`](https://cdn.vaadin.com/vaadin-web-components/23.3.3/#/elements/vaadin-avatar#property-abbr), [`img`](#/elements/vaadin-avatar#property-img)\nand [`colorIndex`](https://cdn.vaadin.com/vaadin-web-components/23.3.3/#/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```",
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);