@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
JavaScript
/**-----------------------------------------------------------------------------------------
* 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']
}] } });