Skip to content

Commit 570c0e9

Browse files
authored
refactor!: update avatar-group overlay to use native popover (#9754)
1 parent 8f83dac commit 570c0e9

File tree

9 files changed

+143
-174
lines changed

9 files changed

+143
-174
lines changed

packages/avatar-group/src/styles/vaadin-avatar-group-base-styles.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const avatarGroupStyles = css`
4848
--_d: calc(var(--_dir) * -1);
4949
}
5050
51-
::slotted(vaadin-avatar:not(:first-child)) {
51+
::slotted(vaadin-avatar:not(:first-of-type)) {
5252
margin-inline-start: calc(
5353
var(--vaadin-avatar-group-overlap, 8px) * -1 - var(--vaadin-focus-ring-width) +
5454
var(--vaadin-avatar-group-gap, 2px)
@@ -57,7 +57,7 @@ export const avatarGroupStyles = css`
5757
5858
:host(:not([theme~='reverse'])) ::slotted(vaadin-avatar:last-child),
5959
:host(:not([theme~='reverse']):not([has-overflow])) ::slotted(vaadin-avatar:nth-last-child(2)),
60-
:host([theme~='reverse']) ::slotted(vaadin-avatar:first-child) {
60+
:host([theme~='reverse']) ::slotted(vaadin-avatar:first-of-type) {
6161
mask-image: none;
6262
}
6363
`;

packages/avatar-group/src/styles/vaadin-avatar-group-core-styles.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ export const avatarGroupStyles = css`
2525
flex-wrap: nowrap;
2626
}
2727
28-
::slotted(vaadin-avatar:not(:first-child)) {
28+
::slotted(vaadin-avatar:not(:first-of-type)) {
2929
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>');
3030
mask-size: calc(300% + var(--vaadin-avatar-group-overlap-border) * 6 - var(--vaadin-avatar-outline-width) * 6);
3131
}
3232
33-
::slotted(vaadin-avatar:not([dir='rtl']):not(:first-child)) {
33+
::slotted(vaadin-avatar:not([dir='rtl']):not(:first-of-type)) {
3434
margin-left: calc(var(--vaadin-avatar-group-overlap) * -1 - var(--vaadin-avatar-outline-width));
3535
mask-position: calc(50% - var(--vaadin-avatar-size) + var(--vaadin-avatar-group-overlap));
3636
}
3737
38-
::slotted(vaadin-avatar[dir='rtl']:not(:first-child)) {
38+
::slotted(vaadin-avatar[dir='rtl']:not(:first-of-type)) {
3939
margin-right: calc(var(--vaadin-avatar-group-overlap) * -1);
4040
mask-position: calc(
4141
50% + var(--vaadin-avatar-size) - var(--vaadin-avatar-group-overlap) + var(--vaadin-avatar-outline-width)

packages/avatar-group/src/vaadin-avatar-group-mixin.js

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,17 @@ export const AvatarGroupMixin = (superClass) =>
144144
return [...this.children].filter((node) => node.localName === 'vaadin-avatar');
145145
}
146146

147-
constructor() {
148-
super();
149-
150-
this.__overlayRenderer = this.__overlayRenderer.bind(this);
151-
}
152-
153147
/** @protected */
154148
ready() {
155149
super.ready();
156150

151+
this._menuController = new SlotController(this, 'overlay', 'vaadin-avatar-group-menu', {
152+
initializer: (menu) => {
153+
menu.addEventListener('keydown', this._onListKeyDown.bind(this));
154+
this._menuElement = menu;
155+
},
156+
});
157+
157158
this._overflowController = new SlotController(this, 'overflow', 'vaadin-avatar', {
158159
initializer: (overflow) => {
159160
overflow.setAttribute('role', 'button');
@@ -170,6 +171,8 @@ export const AvatarGroupMixin = (superClass) =>
170171
this._overflowTooltip = tooltip;
171172
},
172173
});
174+
175+
this.addController(this._menuController);
173176
this.addController(this._overflowController);
174177

175178
this._overlayElement = this.$.overlay;
@@ -229,8 +232,8 @@ export const AvatarGroupMixin = (superClass) =>
229232
}
230233
}
231234

232-
if (props.has('_overflowItems')) {
233-
this.__overflowItemsChanged(this._overflowItems, props.get('_overflowItems'));
235+
if (props.has('_overflowItems') || props.has('__effectiveI18n') || props.has('_theme')) {
236+
this.__renderMenu();
234237
}
235238
}
236239

@@ -241,34 +244,31 @@ export const AvatarGroupMixin = (superClass) =>
241244

242245
/**
243246
* Renders items when they are provided by the `items` property and clears the content otherwise.
244-
* @param {!HTMLElement} root
245247
* @private
246248
*/
247-
__overlayRenderer(root) {
249+
__renderMenu() {
248250
render(
249251
html`
250-
<vaadin-avatar-group-menu @keydown="${this._onListKeyDown}">
251-
${(this._overflowItems || []).map(
252-
(item) => html`
253-
<vaadin-avatar-group-menu-item>
254-
<vaadin-avatar
255-
.name="${item.name}"
256-
.abbr="${item.abbr}"
257-
.img="${item.img}"
258-
.colorIndex="${item.colorIndex}"
259-
.i18n="${this.__effectiveI18n}"
260-
class="${ifDefined(item.className)}"
261-
theme="${ifDefined(this._theme)}"
262-
aria-hidden="true"
263-
tabindex="-1"
264-
></vaadin-avatar>
265-
${item.name || ''}
266-
</vaadin-avatar-group-menu-item>
267-
`,
268-
)}
269-
</vaadin-avatar-group-menu>
252+
${(this._overflowItems || []).map(
253+
(item) => html`
254+
<vaadin-avatar-group-menu-item>
255+
<vaadin-avatar
256+
.name="${item.name}"
257+
.abbr="${item.abbr}"
258+
.img="${item.img}"
259+
.colorIndex="${item.colorIndex}"
260+
.i18n="${this.__effectiveI18n}"
261+
class="${ifDefined(item.className)}"
262+
theme="${ifDefined(this._theme)}"
263+
aria-hidden="true"
264+
tabindex="-1"
265+
></vaadin-avatar>
266+
${item.name || ''}
267+
</vaadin-avatar-group-menu-item>
268+
`,
269+
)}
270270
`,
271-
root,
271+
this._menuElement,
272272
{ host: this },
273273
);
274274
}
@@ -453,10 +453,6 @@ export const AvatarGroupMixin = (superClass) =>
453453
/** @private */
454454
__openedChanged(opened, oldOpened) {
455455
if (opened) {
456-
if (!this._menuElement) {
457-
this._menuElement = this.$.overlay.querySelector('vaadin-avatar-group-menu');
458-
}
459-
460456
this._openedWithFocusRing = this._overflow.hasAttribute('focus-ring');
461457
} else if (oldOpened) {
462458
this._overflow.focus();
@@ -468,18 +464,6 @@ export const AvatarGroupMixin = (superClass) =>
468464
this._overflow.setAttribute('aria-expanded', opened === true);
469465
}
470466

471-
/** @private */
472-
__overflowItemsChanged(items, oldItems) {
473-
// Prevent renderer from being called unnecessarily on initialization
474-
if (items && items.length === 0 && (!oldItems || oldItems.length === 0)) {
475-
return;
476-
}
477-
478-
if (items || oldItems) {
479-
this.$.overlay.requestContentUpdate();
480-
}
481-
}
482-
483467
/** @private */
484468
__setItemsInView() {
485469
const avatars = this._avatars;

packages/avatar-group/src/vaadin-avatar-group-overlay.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ class AvatarGroupOverlay extends PositionMixin(
4646
</div>
4747
`;
4848
}
49+
50+
/**
51+
* @protected
52+
* @override
53+
*/
54+
_attachOverlay() {
55+
this.showPopover();
56+
}
57+
58+
/**
59+
* @protected
60+
* @override
61+
*/
62+
_detachOverlay() {
63+
this.hidePopover();
64+
}
4965
}
5066

5167
defineCustomElement(AvatarGroupOverlay);

packages/avatar-group/src/vaadin-avatar-group.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export { AvatarGroupI18n, AvatarGroupItem, AvatarI18n };
3535
* Part name | Description
3636
* ----------- | ---------------
3737
* `container` | The container element
38+
* `overlay` | The overflow avatar menu overlay
39+
* `content` | The overflow avatar menu overlay content
3840
*
3941
* See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
4042
* state attributes and stylable shadow parts of avatar elements.

packages/avatar-group/src/vaadin-avatar-group.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import { AvatarGroupMixin } from './vaadin-avatar-group-mixin.js';
4242
* Part name | Description
4343
* ----------- | ---------------
4444
* `container` | The container element
45+
* `overlay` | The overflow avatar menu overlay
46+
* `content` | The overflow avatar menu overlay content
4547
*
4648
* See the [`<vaadin-avatar>`](#/elements/vaadin-avatar) documentation for the available
4749
* state attributes and stylable shadow parts of avatar elements.
@@ -81,15 +83,18 @@ class AvatarGroup extends AvatarGroupMixin(ElementMixin(ThemableMixin(PolylitMix
8183
</div>
8284
<vaadin-avatar-group-overlay
8385
id="overlay"
86+
popover="manual"
8487
.owner="${this}"
8588
.opened="${this._opened}"
8689
.positionTarget="${this._overflow}"
87-
.renderer="${this.__overlayRenderer}"
8890
no-vertical-overlap
91+
exportparts="overlay, content"
8992
@vaadin-overlay-close="${this._onVaadinOverlayClose}"
9093
@vaadin-overlay-open="${this._onVaadinOverlayOpen}"
9194
@opened-changed="${this._onOpenedChanged}"
92-
></vaadin-avatar-group-overlay>
95+
>
96+
<slot name="overlay"></slot>
97+
</vaadin-avatar-group-overlay>
9398
`;
9499
}
95100

0 commit comments

Comments
 (0)