Skip to content

Commit

Permalink
experiment: add LitElement based version of avatar-group (#8155)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Nov 15, 2024
1 parent f078f8e commit 2f388cf
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 11 deletions.
38 changes: 29 additions & 9 deletions packages/avatar-group/src/vaadin-avatar-group-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const AvatarGroupMixin = (superClass) =>
items: {
type: Array,
observer: '__itemsChanged',
sync: true,
},

/**
Expand All @@ -64,6 +65,7 @@ export const AvatarGroupMixin = (superClass) =>
*/
maxItemsVisible: {
type: Number,
sync: true,
},

/**
Expand Down Expand Up @@ -97,6 +99,7 @@ export const AvatarGroupMixin = (superClass) =>
*/
i18n: {
type: Object,
sync: true,
value: () => {
return {
anonymous: 'anonymous',
Expand All @@ -114,42 +117,48 @@ export const AvatarGroupMixin = (superClass) =>
_avatars: {
type: Array,
value: () => [],
sync: true,
},

/** @private */
__itemsInView: {
type: Number,
value: null,
sync: true,
},

/** @private */
_overflow: {
type: Object,
sync: true,
},

/** @private */
_overflowItems: {
type: Array,
observer: '__overflowItemsChanged',
computed: '__computeOverflowItems(items, __itemsInView, maxItemsVisible)',
sync: true,
},

/** @private */
_overflowTooltip: {
type: Object,
sync: true,
},

/** @private */
_opened: {
type: Boolean,
observer: '__openedChanged',
sync: true,
},
};
}

static get observers() {
return [
'__i18nItemsChanged(i18n, items)',
'__openedChanged(_opened, _overflow)',
'__updateAvatarsTheme(_overflow, _avatars, _theme)',
'__updateAvatars(items, __itemsInView, maxItemsVisible, _overflow, i18n)',
'__updateOverflowAvatar(_overflow, items, __itemsInView, maxItemsVisible)',
Expand Down Expand Up @@ -297,6 +306,13 @@ export const AvatarGroupMixin = (superClass) =>
}
}

/** @private */
_onVaadinOverlayOpen() {
if (this._menuElement) {
this._menuElement.focus();
}
}

/** @private */
__renderAvatars(items) {
render(
Expand Down Expand Up @@ -460,22 +476,26 @@ export const AvatarGroupMixin = (superClass) =>
}

/** @private */
__openedChanged(opened, wasOpened) {
__openedChanged(opened, overflow) {
if (!overflow) {
return;
}

if (opened) {
if (!this._menuElement) {
this._menuElement = this.$.overlay.querySelector('vaadin-avatar-group-menu');
}

this._openedWithFocusRing = this._overflow.hasAttribute('focus-ring');

this._menuElement.focus();
} else if (wasOpened) {
this._overflow.focus();
this._openedWithFocusRing = overflow.hasAttribute('focus-ring');
} else if (this.__oldOpened) {
overflow.focus();
if (this._openedWithFocusRing) {
this._overflow.setAttribute('focus-ring', '');
overflow.setAttribute('focus-ring', '');
}
}
this._overflow.setAttribute('aria-expanded', opened === true);

overflow.setAttribute('aria-expanded', opened === true);
this.__oldOpened = opened;
}

/** @private */
Expand Down
1 change: 1 addition & 0 deletions packages/avatar-group/src/vaadin-avatar-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class AvatarGroup extends AvatarGroupMixin(ElementMixin(ThemableMixin(Controller
position-target="[[_overflow]]"
no-vertical-overlap
on-vaadin-overlay-close="_onVaadinOverlayClose"
on-vaadin-overlay-open="_onVaadinOverlayOpen"
></vaadin-avatar-group-overlay>
`;
}
Expand Down
60 changes: 60 additions & 0 deletions packages/avatar-group/src/vaadin-lit-avatar-group-menu-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @license
* Copyright (c) 2020 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { css, html, LitElement } from 'lit';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { ItemMixin } from '@vaadin/item/src/vaadin-item-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

/**
* An element used internally by `<vaadin-avatar-group>`. Not intended to be used separately.
*
* @customElement
* @extends HTMLElement
* @mixes DirMixin
* @mixes ItemMixin
* @mixes ThemableMixin
* @protected
*/
class AvatarGroupMenuItem extends ItemMixin(ThemableMixin(DirMixin(PolylitMixin(LitElement)))) {
static get is() {
return 'vaadin-avatar-group-menu-item';
}

static get styles() {
return css`
:host {
display: inline-block;
}
:host([hidden]) {
display: none !important;
}
`;
}

/** @protected */
render() {
return html`
<span part="checkmark" aria-hidden="true"></span>
<div part="content">
<slot></slot>
</div>
`;
}

/** @protected */
ready() {
super.ready();

this.setAttribute('role', 'menuitem');
}
}

defineCustomElement(AvatarGroupMenuItem);

export { AvatarGroupMenuItem };
87 changes: 87 additions & 0 deletions packages/avatar-group/src/vaadin-lit-avatar-group-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* @license
* Copyright (c) 2020 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { css, html, LitElement } from 'lit';
import { ListMixin } from '@vaadin/a11y-base/src/list-mixin.js';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

/**
* An element used internally by `<vaadin-avatar-group>`. Not intended to be used separately.
*
* @customElement
* @extends HTMLElement
* @mixes ControllerMixin
* @mixes DirMixin
* @mixes ListMixin
* @mixes ThemableMixin
* @protected
*/
class AvatarGroupMenu extends ListMixin(ThemableMixin(DirMixin(PolylitMixin(LitElement)))) {
static get is() {
return 'vaadin-avatar-group-menu';
}

static get styles() {
return css`
:host {
display: flex;
}
:host([hidden]) {
display: none !important;
}
[part='items'] {
height: 100%;
width: 100%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
`;
}

static get properties() {
return {
// We don't need to define this property since super default is vertical,
// but we don't want it to be modified, or be shown in the API docs.
/** @private */
orientation: {
readOnly: true,
},
};
}

/**
* @return {!HTMLElement}
* @protected
* @override
*/
get _scrollerElement() {
return this.shadowRoot.querySelector('[part="items"]');
}

/** @protected */
render() {
return html`
<div part="items">
<slot></slot>
</div>
`;
}

/** @protected */
ready() {
super.ready();

this.setAttribute('role', 'menu');
}
}

defineCustomElement(AvatarGroupMenu);

export { AvatarGroupMenu };
64 changes: 64 additions & 0 deletions packages/avatar-group/src/vaadin-lit-avatar-group-overlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @license
* Copyright (c) 2020 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { html, LitElement } from 'lit';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';

/**
* An element used internally by `<vaadin-avatar-group>`. Not intended to be used separately.
*
* @customElement
* @extends HTMLElement
* @mixes PositionMixin
* @mixes OverlayMixin
* @mixes DirMixin
* @mixes ThemableMixin
* @private
*/
class AvatarGroupOverlay extends PositionMixin(OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LitElement))))) {
static get is() {
return 'vaadin-avatar-group-overlay';
}

static get styles() {
return overlayStyles;
}

static get properties() {
return {
/**
* When true, the overlay is visible and attached to body.
* This property config is overridden to set `sync: true`.
*/
opened: {
type: Boolean,
notify: true,
observer: '_openedChanged',
reflectToAttribute: true,
sync: true,
},
};
}

/** @protected */
render() {
return html`
<div id="backdrop" part="backdrop" ?hidden="${!this.withBackdrop}"></div>
<div part="overlay" id="overlay" tabindex="0">
<div part="content" id="content">
<slot></slot>
</div>
</div>
`;
}
}

defineCustomElement(AvatarGroupOverlay);
1 change: 1 addition & 0 deletions packages/avatar-group/src/vaadin-lit-avatar-group.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './vaadin-avatar-group.js';
Loading

0 comments on commit 2f388cf

Please sign in to comment.