UNPKG

@progress/kendo-angular-treelist

Version:

Kendo UI TreeList for Angular - Display hierarchical data in an Angular tree grid view that supports sorting, filtering, paging, and much more.

148 lines (147 loc) 6.22 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Directive, ElementRef, Input, NgZone, Renderer2 } from '@angular/core'; import { Subscription } from 'rxjs'; import { Keys } from '@progress/kendo-angular-common'; import { ColumnMenuChooserComponent } from './column-menu-chooser.component'; import { ColumnMenuFilterComponent } from './column-menu-filter.component'; import * as i0 from "@angular/core"; /** * Provides keyboard navigation support for TreeList column menu items. * Use this directive to manage focus and tab order for custom column menu items ([see example](slug:columnmenu_treelist#customizing-the-content)). * * @example * ```html * <ng-template kendoTreelistColumnMenuTemplate let-service="service" let-column="column"> * <kendo-treelist-columnmenu-sort #sortItem [kendoTreeListColumnMenuItem]="sortItem" [service]="service"> * </kendo-treelist-columnmenu-sort> * </ng-template> * ``` * @remarks * Applied to: * {@link ColumnMenuChooserComponent}, * {@link ColumnMenuFilterComponent}, * {@link ColumnMenuSortComponent}, * {@link ColumnMenuLockComponent}, * {@link ColumnMenuComponent}, * {@link ColumnMenuItemComponent}, * {@link ColumnMenuAutoSizeAllColumnsComponent}, * {@link ColumnMenuAutoSizeColumnComponent}. */ export class ColumnMenuItemDirective { hostElement; renderer; ngZone; /** * Provides a reference to the TreeList column menu item. Required for built-in keyboard navigation. */ menuItemComponent; firstFocusableElement; lastFocusableElement; /** * @hidden */ set isFirst(value) { if (value) { const focusableElement = this.columnMenuItems[0]; this.menuItemComponent.service.menuTabbingService.firstFocusable = focusableElement; this.ngZone.runOutsideAngular(() => { const firstItemKeydownSub = this.renderer.listen(focusableElement, 'keydown', this.onTab); this.subs.add(firstItemKeydownSub); }); } this._isFirst = value; } /** * @hidden */ get isFirst() { return this._isFirst; } /** * @hidden */ set isLast(value) { if (!this.columnMenuItems) { return; } if (value) { const lastFocusableElement = this.getLastColumnMenuItem(); this.menuItemComponent.service.menuTabbingService.lastFocusable = lastFocusableElement; this.ngZone.runOutsideAngular(() => { const lastItemKeydownSub = this.renderer.listen(lastFocusableElement, 'keydown', this.onTab); this.subs.add(lastItemKeydownSub); }); if (this.isExpandableItem()) { this.menuItemComponent.isLast = true; } } this._isLast = value; } /** * @hidden */ get isLast() { return this._isLast; } _isFirst = false; _isLast = false; columnMenuItems; subs = new Subscription(); constructor(hostElement, renderer, ngZone) { this.hostElement = hostElement; this.renderer = renderer; this.ngZone = ngZone; } ngAfterViewInit() { this.columnMenuItems = this.hostElement.nativeElement.querySelectorAll('.k-columnmenu-item'); [].slice.apply(this.columnMenuItems).forEach(el => this.renderer.setAttribute(el, 'tabindex', '0')); if (this.menuItemComponent instanceof ColumnMenuFilterComponent) { this.menuItemComponent.service.menuTabbingService.isColumnMenu = true; } this.menuItemComponent.service?.columnMenuContainer.templateMenuItems.push(this); } ngOnDestroy() { if (this.subs) { this.subs.unsubscribe(); } } onTab = (e) => { if (e.keyCode !== Keys.Tab) { return; } if (this.isFirst && e.shiftKey && e.target === this.columnMenuItems[0]) { e.preventDefault(); this.menuItemComponent.service.menuTabbingService.lastFocusable.focus(); } if (this.isLast && !e.shiftKey) { const lastColumnMenuItem = this.getLastColumnMenuItem(); const isExpanded = this.menuItemComponent.expanded; if (lastColumnMenuItem === e.target && !isExpanded) { e.preventDefault(); this.menuItemComponent.service.menuTabbingService.firstFocusable.focus(); } } }; getLastColumnMenuItem() { return (this.columnMenuItems.length === 1 ? this.columnMenuItems[0] : this.columnMenuItems[1]); } isExpandableItem() { return this.menuItemComponent instanceof ColumnMenuFilterComponent || this.menuItemComponent instanceof ColumnMenuChooserComponent; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ColumnMenuItemDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ColumnMenuItemDirective, isStandalone: true, selector: "[kendoTreeListColumnMenuItem]", inputs: { menuItemComponent: ["kendoTreeListColumnMenuItem", "menuItemComponent"] }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ColumnMenuItemDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoTreeListColumnMenuItem]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { menuItemComponent: [{ type: Input, args: ['kendoTreeListColumnMenuItem'] }] } });