UNPKG

@progress/kendo-angular-grid

Version:

Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.

165 lines (164 loc) 6.93 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { ChangeDetectorRef, Directive, ElementRef, Input, NgZone, Renderer2 } from '@angular/core'; import { Subscription } from 'rxjs'; import { Keys } from '@progress/kendo-angular-common'; import { ColumnMenuPositionComponent } from './column-menu-position.component'; import { ColumnMenuChooserComponent } from './column-menu-chooser.component'; import { ColumnMenuFilterComponent } from './column-menu-filter.component'; import { ContextService } from '../common/provider.service'; import * as i0 from "@angular/core"; import * as i1 from "../common/provider.service"; /** * Represents a directive that manages keyboard navigation for a column menu item ([see example](slug:columnmenu_grid#customizing-the-content)). * * @example * ```html * <ng-template kendoGridColumnMenuTemplate let-service="service" let-column="column"> * <kendo-grid-columnmenu-sort #sortItem [kendoGridColumnMenuItem]="sortItem" [service]="service"></kendo-grid-columnmenu-sort> * </ng-template> * ``` * @remarks * Applied to: * {@link ColumnMenuChooserComponent}, * {@link ColumnMenuFilterComponent}, * {@link ColumnMenuSortComponent}, * {@link ColumnMenuLockComponent}, * {@link ColumnMenuStickyComponent}, * {@link ColumnMenuComponent}, * {@link ColumnMenuItemComponent}, * {@link ColumnMenuPositionComponent}, * {@link ColumnMenuAutoSizeAllColumnsComponent}, * {@link ColumnMenuAutoSizeColumnComponent}. */ export class ColumnMenuItemDirective { hostElement; renderer; ngZone; cdr; ctx; /** * Reference to the Grid column menu item. Required when you want to include the item in the built-in keyboard navigation. */ menuItemComponent; /** * Holds the first focusable element. */ firstFocusableElement; /** * Holds the last focusable element. */ 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; if (this.ctx.grid.isActionSheetExpanded) { this.cdr.detectChanges(); } } } this._isLast = value; } /** * @hidden */ get isLast() { return this._isLast; } _isFirst = false; _isLast = false; columnMenuItems; subs = new Subscription(); constructor(hostElement, renderer, ngZone, cdr, ctx) { this.hostElement = hostElement; this.renderer = renderer; this.ngZone = ngZone; this.cdr = cdr; this.ctx = ctx; } 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.code !== 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 || this.menuItemComponent instanceof ColumnMenuPositionComponent; } 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 }, { token: i0.ChangeDetectorRef }, { token: i1.ContextService }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ColumnMenuItemDirective, isStandalone: true, selector: "[kendoGridColumnMenuItem]", inputs: { menuItemComponent: ["kendoGridColumnMenuItem", "menuItemComponent"] }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ColumnMenuItemDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoGridColumnMenuItem]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i1.ContextService }]; }, propDecorators: { menuItemComponent: [{ type: Input, args: ['kendoGridColumnMenuItem'] }] } });