UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

265 lines (262 loc) 8.21 kB
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