@angular/cdk
Version:
Angular Material Component Development Kit
265 lines (262 loc) • 8.21 kB
JavaScript
import { QueryList, InjectionToken } from '@angular/core';
import { Subscription, isObservable, Subject, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { Typeahead } from './_typeahead-chunk.mjs';
import { coerceObservable } from './coercion-private.mjs';
class TreeKeyManager {
_activeItemIndex = -1;
_activeItem = null;
_shouldActivationFollowFocus = false;
_horizontalOrientation = 'ltr';
_skipPredicateFn = _item => false;
_trackByFn = item => item;
_items = [];
_typeahead;
_typeaheadSubscription = Subscription.EMPTY;
_hasInitialFocused = false;
_initializeFocus() {
if (this._hasInitialFocused || this._items.length === 0) {
return;
}
let activeIndex = 0;
for (let i = 0; i < this._items.length; i++) {
if (!this._skipPredicateFn(this._items[i]) && !this._isItemDisabled(this._items[i])) {
activeIndex = i;
break;
}
}
const activeItem = this._items[activeIndex];
if (activeItem.makeFocusable) {
this._activeItem?.unfocus();
this._activeItemIndex = activeIndex;
this._activeItem = activeItem;
this._typeahead?.setCurrentSelectedItemIndex(activeIndex);
activeItem.makeFocusable();
} else {
this.focusItem(activeIndex);
}
this._hasInitialFocused = true;
}
constructor(items, config) {
if (items instanceof QueryList) {
this._items = items.toArray();
items.changes.subscribe(newItems => {
this._items = newItems.toArray();
this._typeahead?.setItems(this._items);
this._updateActiveItemIndex(this._items);
this._initializeFocus();
});
} else if (isObservable(items)) {
items.subscribe(newItems => {
this._items = newItems;
this._typeahead?.setItems(newItems);
this._updateActiveItemIndex(newItems);
this._initializeFocus();
});
} else {
this._items = items;
this._initializeFocus();
}
if (typeof config.shouldActivationFollowFocus === 'boolean') {
this._shouldActivationFollowFocus = config.shouldActivationFollowFocus;
}
if (config.horizontalOrientation) {
this._horizontalOrientation = config.horizontalOrientation;
}
if (config.skipPredicate) {
this._skipPredicateFn = config.skipPredicate;
}
if (config.trackBy) {
this._trackByFn = config.trackBy;
}
if (typeof config.typeAheadDebounceInterval !== 'undefined') {
this._setTypeAhead(config.typeAheadDebounceInterval);
}
}
change = new Subject();
destroy() {
this._typeaheadSubscription.unsubscribe();
this._typeahead?.destroy();
this.change.complete();
}
onKeydown(event) {
const key = event.key;
switch (key) {
case 'Tab':
return;
case 'ArrowDown':
this._focusNextItem();
break;
case 'ArrowUp':
this._focusPreviousItem();
break;
case 'ArrowRight':
this._horizontalOrientation === 'rtl' ? this._collapseCurrentItem() : this._expandCurrentItem();
break;
case 'ArrowLeft':
this._horizontalOrientation === 'rtl' ? this._expandCurrentItem() : this._collapseCurrentItem();
break;
case 'Home':
this._focusFirstItem();
break;
case 'End':
this._focusLastItem();
break;
case 'Enter':
case ' ':
this._activateCurrentItem();
break;
default:
if (event.key === '*') {
this._expandAllItemsAtCurrentItemLevel();
break;
}
this._typeahead?.handleKey(event);
return;
}
this._typeahead?.reset();
event.preventDefault();
}
getActiveItemIndex() {
return this._activeItemIndex;
}
getActiveItem() {
return this._activeItem;
}
_focusFirstItem() {
this.focusItem(this._findNextAvailableItemIndex(-1));
}
_focusLastItem() {
this.focusItem(this._findPreviousAvailableItemIndex(this._items.length));
}
_focusNextItem() {
this.focusItem(this._findNextAvailableItemIndex(this._activeItemIndex));
}
_focusPreviousItem() {
this.focusItem(this._findPreviousAvailableItemIndex(this._activeItemIndex));
}
focusItem(itemOrIndex, options = {}) {
options.emitChangeEvent ??= true;
let index = typeof itemOrIndex === 'number' ? itemOrIndex : this._items.findIndex(item => this._trackByFn(item) === this._trackByFn(itemOrIndex));
if (index < 0 || index >= this._items.length) {
return;
}
const activeItem = this._items[index];
if (this._activeItem !== null && this._trackByFn(activeItem) === this._trackByFn(this._activeItem)) {
return;
}
const previousActiveItem = this._activeItem;
this._activeItem = activeItem ?? null;
this._activeItemIndex = index;
this._typeahead?.setCurrentSelectedItemIndex(index);
this._activeItem?.focus();
previousActiveItem?.unfocus();
if (options.emitChangeEvent) {
this.change.next(this._activeItem);
}
if (this._shouldActivationFollowFocus) {
this._activateCurrentItem();
}
}
_updateActiveItemIndex(newItems) {
const activeItem = this._activeItem;
if (!activeItem) {
return;
}
const newIndex = newItems.findIndex(item => this._trackByFn(item) === this._trackByFn(activeItem));
if (newIndex > -1 && newIndex !== this._activeItemIndex) {
this._activeItemIndex = newIndex;
this._typeahead?.setCurrentSelectedItemIndex(newIndex);
}
}
_setTypeAhead(debounceInterval) {
this._typeahead = new Typeahead(this._items, {
debounceInterval: typeof debounceInterval === 'number' ? debounceInterval : undefined,
skipPredicate: item => this._skipPredicateFn(item)
});
this._typeaheadSubscription = this._typeahead.selectedItem.subscribe(item => {
this.focusItem(item);
});
}
_findNextAvailableItemIndex(startingIndex) {
for (let i = startingIndex + 1; i < this._items.length; i++) {
if (!this._skipPredicateFn(this._items[i])) {
return i;
}
}
return startingIndex;
}
_findPreviousAvailableItemIndex(startingIndex) {
for (let i = startingIndex - 1; i >= 0; i--) {
if (!this._skipPredicateFn(this._items[i])) {
return i;
}
}
return startingIndex;
}
_collapseCurrentItem() {
if (!this._activeItem) {
return;
}
if (this._isCurrentItemExpanded()) {
this._activeItem.collapse();
} else {
const parent = this._activeItem.getParent();
if (!parent || this._skipPredicateFn(parent)) {
return;
}
this.focusItem(parent);
}
}
_expandCurrentItem() {
if (!this._activeItem) {
return;
}
if (!this._isCurrentItemExpanded()) {
this._activeItem.expand();
} else {
coerceObservable(this._activeItem.getChildren()).pipe(take(1)).subscribe(children => {
const firstChild = children.find(child => !this._skipPredicateFn(child));
if (!firstChild) {
return;
}
this.focusItem(firstChild);
});
}
}
_isCurrentItemExpanded() {
if (!this._activeItem) {
return false;
}
return typeof this._activeItem.isExpanded === 'boolean' ? this._activeItem.isExpanded : this._activeItem.isExpanded();
}
_isItemDisabled(item) {
return typeof item.isDisabled === 'boolean' ? item.isDisabled : item.isDisabled?.();
}
_expandAllItemsAtCurrentItemLevel() {
if (!this._activeItem) {
return;
}
const parent = this._activeItem.getParent();
let itemsToExpand;
if (!parent) {
itemsToExpand = of(this._items.filter(item => item.getParent() === null));
} else {
itemsToExpand = coerceObservable(parent.getChildren());
}
itemsToExpand.pipe(take(1)).subscribe(items => {
for (const item of items) {
item.expand();
}
});
}
_activateCurrentItem() {
this._activeItem?.activate();
}
}
const TREE_KEY_MANAGER = new InjectionToken('tree-key-manager', {
providedIn: 'root',
factory: () => (items, options) => new TreeKeyManager(items, options)
});
export { TREE_KEY_MANAGER, TreeKeyManager };
//# sourceMappingURL=_tree-key-manager-chunk.mjs.map