Skip to content

Commit

Permalink
refactor: extract message and message-list logic into mixins (#8241)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Nov 29, 2024
1 parent ea24ab2 commit 276cce9
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 339 deletions.
1 change: 1 addition & 0 deletions packages/message-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"polymer"
],
"dependencies": {
"@open-wc/dedupe-mixin": "^1.3.0",
"@polymer/polymer": "^3.0.0",
"@vaadin/a11y-base": "24.6.0-beta1",
"@vaadin/avatar": "24.6.0-beta1",
Expand Down
42 changes: 42 additions & 0 deletions packages/message-list/src/vaadin-message-list-mixin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @license
* Copyright (c) 2021 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { Constructor } from '@open-wc/dedupe-mixin';
import type { KeyboardDirectionMixinClass } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';

export interface MessageListItem {
text?: string;
time?: string;
userName?: string;
userAbbr?: string;
userImg?: string;
userColorIndex?: number;
theme?: string;
className?: string;
}

export declare function MessageListMixin<T extends Constructor<HTMLElement>>(
base: T,
): Constructor<KeyboardDirectionMixinClass> & Constructor<MessageListMixinClass> & T;

export declare class MessageListMixinClass {
/**
* An array of objects which will be rendered as messages.
* The message objects can have the following properties:
* ```js
* Array<{
* text: string,
* time: string,
* userName: string,
* userAbbr: string,
* userImg: string,
* userColorIndex: number,
* className: string,
* theme: string
* }>
* ```
*/
items: MessageListItem[] | null | undefined;
}
148 changes: 148 additions & 0 deletions packages/message-list/src/vaadin-message-list-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/**
* @license
* Copyright (c) 2021 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { html, render } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';

/**
* @polymerMixin
* @mixes KeyboardDirectionMixin
*/
export const MessageListMixin = (superClass) =>
class MessageListMixinClass extends KeyboardDirectionMixin(superClass) {
static get properties() {
return {
/**
* An array of objects which will be rendered as messages.
* The message objects can have the following properties:
* ```js
* Array<{
* text: string,
* time: string,
* userName: string,
* userAbbr: string,
* userImg: string,
* userColorIndex: number,
* className: string,
* theme: string
* }>
* ```
*/
items: {
type: Array,
value: () => [],
observer: '_itemsChanged',
},
};
}

/** @protected */
get _messages() {
return [...this.querySelectorAll('vaadin-message')];
}

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

// Make screen readers announce new messages
this.setAttribute('aria-relevant', 'additions');
this.setAttribute('role', 'log');
}

/**
* Override method inherited from `KeyboardDirectionMixin`
* to use the list of message elements as items.
*
* @return {Element[]}
* @protected
* @override
*/
_getItems() {
return this._messages;
}

/** @private */
_itemsChanged(newVal, oldVal) {
const items = newVal || [];
const oldItems = oldVal || [];

if (items.length || oldItems.length) {
const focusedIndex = this._getIndexOfFocusableElement();
const closeToBottom = this.scrollHeight < this.clientHeight + this.scrollTop + 50;

this._renderMessages(items);
this._setTabIndexesByIndex(focusedIndex);

requestAnimationFrame(() => {
if (items.length > oldItems.length && closeToBottom) {
this._scrollToLastMessage();
}
});
}
}

/** @private */
_renderMessages(items) {
render(
html`
${items.map(
(item) => html`
<vaadin-message
role="listitem"
.time="${item.time}"
.userAbbr="${item.userAbbr}"
.userName="${item.userName}"
.userImg="${item.userImg}"
.userColorIndex="${item.userColorIndex}"
theme="${ifDefined(item.theme)}"
class="${ifDefined(item.className)}"
@focusin="${this._onMessageFocusIn}"
>${item.text}<vaadin-avatar slot="avatar"></vaadin-avatar
></vaadin-message>
`,
)}
`,
this,
{ host: this },
);
}

/** @private */
_scrollToLastMessage() {
if (this.items.length > 0) {
this.scrollTop = this.scrollHeight - this.clientHeight;
}
}

/** @private */
_onMessageFocusIn(e) {
const target = e.composedPath().find((node) => node instanceof customElements.get('vaadin-message'));
this._setTabIndexesByMessage(target);
}

/**
* @param {number} idx
* @protected
*/
_setTabIndexesByIndex(index) {
const message = this._messages[index] || this._messages[0];
this._setTabIndexesByMessage(message);
}

/** @private */
_setTabIndexesByMessage(message) {
this._messages.forEach((e) => {
e.tabIndex = e === message ? 0 : -1;
});
}

/** @private */
_getIndexOfFocusableElement() {
const index = this._messages.findIndex((e) => e.tabIndex === 0);
return index !== -1 ? index : 0;
}
};
35 changes: 3 additions & 32 deletions packages/message-list/src/vaadin-message-list.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@
* Copyright (c) 2021 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { MessageListMixin } from './vaadin-message-list-mixin.js';

export interface MessageListItem {
text?: string;
time?: string;
userName?: string;
userAbbr?: string;
userImg?: string;
userColorIndex?: number;
theme?: string;
className?: string;
}
export { MessageListItem } from './vaadin-message-list-mixin.js';

/**
* `<vaadin-message-list>` is a Web Component for showing an ordered list of messages. The messages are rendered as <vaadin-message>
Expand Down Expand Up @@ -51,27 +42,7 @@ export interface MessageListItem {
*
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
*/
declare class MessageList extends KeyboardDirectionMixin(ThemableMixin(ElementMixin(HTMLElement))) {
/**
* An array of objects which will be rendered as messages.
* The message objects can have the following properties:
* ```js
* Array<{
* text: string,
* time: string,
* userName: string,
* userAbbr: string,
* userImg: string,
* userColorIndex: number,
* className: string,
* theme: string
* }>
* ```
*
* @type {!Array<!MessageListItem>}
*/
items: MessageListItem[] | null | undefined;
}
declare class MessageList extends MessageListMixin(ThemableMixin(ElementMixin(HTMLElement))) {}

declare global {
interface HTMLElementTagNameMap {
Expand Down
Loading

0 comments on commit 276cce9

Please sign in to comment.