UNPKG

@progress/kendo-angular-treeview

Version:
246 lines (245 loc) 8.3 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Injectable } from '@angular/core'; import { Keys } from '@progress/kendo-angular-common'; import { LocalizationService } from '@progress/kendo-angular-l10n'; import { Subject } from 'rxjs'; import { NavigationModel } from './navigation-model'; import { nodeIndex, isPresent } from '../utils'; import * as i0 from "@angular/core"; import * as i1 from "@progress/kendo-angular-l10n"; /** * @hidden */ export class NavigationService { localization; expands = new Subject(); moves = new Subject(); checks = new Subject(); selects = new Subject(); deselectAllButCurrentItem = new Subject(); loadMore = new Subject(); navigable = true; selection = 'single'; isTreeViewActive = false; get model() { return this._model; } set model(model) { this._model = model; } actions = { [Keys.ArrowUp]: () => this.activate(this.model.findVisiblePrev(this.focusableItem), true), [Keys.ArrowDown]: () => this.activate(this.model.findVisibleNext(this.focusableItem), true), [Keys.ArrowLeft]: () => !this.isLoadMoreButton && (this.expand({ expand: this.localization.rtl, intercept: this.localization.rtl ? this.moveToFirstVisibleChild : this.moveToParent })), [Keys.ArrowRight]: () => !this.isLoadMoreButton && (this.expand({ expand: !this.localization.rtl, intercept: this.localization.rtl ? this.moveToParent : this.moveToFirstVisibleChild })), [Keys.Home]: () => this.activate(this.model.firstVisibleNode(), true), [Keys.End]: () => this.activate(this.model.lastVisibleNode(), true), [Keys.Enter]: (e) => this.handleEnter(e), [Keys.Space]: () => this.handleSpace() }; activeItem; isFocused = false; shouldScroll = false; _model = new NavigationModel(); get activeIndex() { return nodeIndex(this.activeItem) || null; } get isActiveExpanded() { return this.activeItem && this.activeItem.children.length > 0; } get isLoadMoreButton() { return this.activeItem && this.activeItem.loadMoreButton; } get focusableItem() { return this.activeItem || this.model.firstFocusableNode(); } constructor(localization) { this.localization = localization; this.moveToFirstVisibleChild = this.moveToFirstVisibleChild.bind(this); this.moveToParent = this.moveToParent.bind(this); } activate(item, shouldScroll = false) { if (!this.navigable || !item || this.isActive(nodeIndex(item))) { return; } this.isFocused = true; this.activeItem = item || this.activeItem; this.shouldScroll = shouldScroll; this.notifyMove(); } activateParent(index) { this.activate(this.model.findParent(index)); } activateIndex(index) { if (!index) { return; } this.activate(this.model.findNode(index)); } activateClosest(index) { if (!index || nodeIndex(this.focusableItem) !== index) { return; } this.activeItem = this.model.closestNode(index); this.notifyMove(); } activateFocusable() { if (this.activeItem) { return; } this.activeItem = this.model.firstVisibleNode(); this.notifyMove(); } deactivate() { if (!this.navigable || !this.isFocused) { return; } this.isFocused = false; this.notifyMove(); } checkIndex(index) { if (!this.isDisabled(index)) { this.checks.next(index); } } selectIndex(index) { if (!this.isDisabled(index)) { this.selects.next(index); } } notifyLoadMore(index) { if (!isPresent(index)) { return; } this.loadMore.next(index); } isActive(index) { if (!index) { return false; } return this.isFocused && this.activeIndex === index; } isFocusable(index) { return nodeIndex(this.focusableItem) === index; } isDisabled(index) { if (!index) { return false; } return this.model.findNode(index).disabled; } registerItem(id, index, disabled, loadMoreButton = false, visible = true) { const itemAtIndex = this.model.findNode(index); if (isPresent(itemAtIndex)) { this.model.unregisterItem(itemAtIndex.id, itemAtIndex.index); if (this.isActive(index)) { this.deactivate(); } } this.model.registerItem(id, index, disabled, loadMoreButton, visible); } updateItem(index, disabled, visible = true) { const itemAtIndex = this.model.findNode(index); if (isPresent(itemAtIndex)) { if (this.isActive(index)) { this.deactivate(); } } itemAtIndex.disabled = disabled; itemAtIndex.visible = visible; } unregisterItem(id, index) { if (this.isActive(index)) { this.activateParent(index); } this.model.unregisterItem(id, index); } move(e) { if (!this.navigable) { return; } const moveAction = this.actions[e.keyCode]; if (!moveAction) { return; } moveAction(e); e.preventDefault(); } expand({ expand, intercept }) { const index = nodeIndex(this.activeItem); if (!index || intercept(index)) { return; } this.notifyExpand(expand); } moveToParent() { if (this.isActiveExpanded) { return false; } this.activate(this.model.findParent(nodeIndex(this.activeItem))); return true; } moveToFirstVisibleChild() { if (!this.isActiveExpanded) { return false; } this.activate(this.model.findVisibleChild(nodeIndex(this.activeItem))); return true; } notifyExpand(expand) { this.expands.next(this.navigationState(expand)); } notifyMove() { this.moves.next(this.navigationState()); } navigationState(expand = false) { return ({ expand, index: this.activeIndex, isFocused: this.isFocused, shouldScroll: this.shouldScroll }); } handleEnter(event) { if (!this.navigable) { return; } if (this.isLoadMoreButton) { this.notifyLoadMore(this.activeIndex); } else { const isCtrlPressed = event.ctrlKey || event.metaKey; if (isCtrlPressed) { this.selectIndex(this.activeIndex); } else { if (this.selection === 'multiple') { this.deselectAllButCurrentItem.next({ dataItem: this.activeItem, index: this.activeIndex }); } else { this.selectIndex(this.activeIndex); } } } } handleSpace() { if (!this.navigable) { return; } if (this.isLoadMoreButton) { this.notifyLoadMore(this.activeIndex); } else { this.checkIndex(this.activeIndex); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService, deps: [{ token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.LocalizationService }]; } });