Skip to content

perf(list-base): convert list-base adapter to class object #19983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 82 additions & 41 deletions src/material-experimental/mdc-list/list-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,82 @@ function toggleClass(el: Element, className: string, on: boolean) {
}
}

/** @docs-private */
class ListAdapter implements MDCListAdapter {
constructor(private _delegate: MatInteractiveListBase) {}

getListItemCount() {
return this._delegate._items.length;
}

listItemAtIndexHasClass(index: number, className: string) {
return this._delegate._elementAtIndex(index).classList.contains(className);
}

addClassForElementIndex(index: number, className: string) {
return this._delegate._elementAtIndex(index).classList.add(className);
}

removeClassForElementIndex(index: number, className: string) {
return this._delegate._elementAtIndex(index).classList.remove(className);
}

getAttributeForElementIndex(index: number, attr: string) {
return this._delegate._elementAtIndex(index).getAttribute(attr);
}

setAttributeForElementIndex(index: number, attr: string, value: string) {
return this._delegate._elementAtIndex(index).setAttribute(attr, value);
}

getFocusedElementIndex() {
return this._delegate._indexForElement(this._delegate._document?.activeElement);
}

isFocusInsideList() {
return this._delegate._element.nativeElement.contains(
this._delegate._document?.activeElement);
}

isRootFocused() {
return this._delegate._element.nativeElement ===
this._delegate._document?.activeElement;
}

focusItemAtIndex(index: number) {
return this._delegate._elementAtIndex(index).focus();
}

// MDC uses this method to disable focusable children of list items. However, we believe that
// this is not an accessible pattern and should be avoided, therefore we intentionally do not
// implement this method. In addition, implementing this would require violating Angular
// Material's general principle of not having components modify DOM elements they do not own.
// A user who feels they really need this feature can simply listen to the `(focus)` and
// `(blur)` events on the list item and enable/disable focus on the children themselves as
// appropriate.
setTabIndexForListItemChildren() {}

// The following methods have a dummy implementation in the base class because they are only
// applicable to certain types of lists. They should be implemented for the concrete classes
// where they are applicable.
hasCheckboxAtIndex() {
return false;
}
hasRadioAtIndex() {
return false;
}
setCheckedCheckboxOrRadioAtIndex() {}
isCheckboxCheckedAtIndex() {
return false;
}

// TODO(mmalerba): Determine if we need to implement these.
getPrimaryTextAtIndex() {
return '';
}
notifyAction() {}
}

@Directive()
/** @docs-private */
export abstract class MatListItemBase implements AfterContentInit, OnDestroy, RippleTarget {
Expand Down Expand Up @@ -134,56 +210,21 @@ export abstract class MatInteractiveListBase extends MatListBase

@ContentChildren(MatListItemBase, {descendants: true}) _items: QueryList<MatListItemBase>;

protected _adapter: MDCListAdapter = {
getListItemCount: () => this._items.length,
listItemAtIndexHasClass:
(index, className) => this._elementAtIndex(index).classList.contains(className),
addClassForElementIndex:
(index, className) => this._elementAtIndex(index).classList.add(className),
removeClassForElementIndex:
(index, className) => this._elementAtIndex(index).classList.remove(className),
getAttributeForElementIndex: (index, attr) => this._elementAtIndex(index).getAttribute(attr),
setAttributeForElementIndex:
(index, attr, value) => this._elementAtIndex(index).setAttribute(attr, value),
getFocusedElementIndex: () => this._indexForElement(this._document?.activeElement),
isFocusInsideList: () => this._element.nativeElement.contains(this._document?.activeElement),
isRootFocused: () => this._element.nativeElement === this._document?.activeElement,
focusItemAtIndex: index => this._elementAtIndex(index).focus(),

// MDC uses this method to disable focusable children of list items. However, we believe that
// this is not an accessible pattern and should be avoided, therefore we intentionally do not
// implement this method. In addition, implementing this would require violating Angular
// Material's general principle of not having components modify DOM elements they do not own.
// A user who feels they really need this feature can simply listen to the `(focus)` and
// `(blur)` events on the list item and enable/disable focus on the children themselves as
// appropriate.
setTabIndexForListItemChildren: () => {},

// The following methods have a dummy implementation in the base class because they are only
// applicable to certain types of lists. They should be implemented for the concrete classes
// where they are applicable.
hasCheckboxAtIndex: () => false,
hasRadioAtIndex: () => false,
setCheckedCheckboxOrRadioAtIndex: () => {},
isCheckboxCheckedAtIndex: () => false,

// TODO(mmalerba): Determine if we need to implement these.
getPrimaryTextAtIndex: () => '',
notifyAction: () => {},
};
protected _adapter: MDCListAdapter;

protected _foundation: MDCListFoundation;

protected _document: Document;
_document: Document;

private _itemsArr: MatListItemBase[] = [];

private _subscriptions = new Subscription();

constructor(protected _element: ElementRef<HTMLElement>, @Inject(DOCUMENT) document: any) {
constructor(public _element: ElementRef<HTMLElement>, @Inject(DOCUMENT) document: any) {
super();
this._document = document;
this._isNonInteractive = false;
this._adapter = new ListAdapter(this);
this._foundation = new MDCListFoundation(this._adapter);
}

Expand Down Expand Up @@ -211,11 +252,11 @@ export abstract class MatInteractiveListBase extends MatListBase
return this._itemsArr[index];
}

private _elementAtIndex(index: number): HTMLElement {
_elementAtIndex(index: number): HTMLElement {
return this._itemAtIndex(index)._elementRef.nativeElement;
}

private _indexForElement(element: Element | null) {
_indexForElement(element: Element | null) {
return element ?
this._itemsArr.findIndex(i => i._elementRef.nativeElement.contains(element)) : -1;
}
Expand Down