UNPKG

@progress/kendo-angular-grid

Version:

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

428 lines (423 loc) 19.4 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Component, ElementRef, EventEmitter, HostBinding, Output, Input, HostListener, ViewChildren, QueryList, NgZone } from '@angular/core'; import { NgFor, NgIf } from '@angular/common'; import { IconWrapperComponent } from '@progress/kendo-angular-icons'; import { chevronUpIcon, chevronDownIcon, xCircleIcon, plusCircleIcon, xIcon } from '@progress/kendo-svg-icons'; import { KENDO_BUTTON } from '@progress/kendo-angular-buttons'; import { take } from 'rxjs/operators'; import { isPresent, Keys, normalizeNumpadKeys } from '@progress/kendo-angular-common'; import * as i0 from "@angular/core"; import * as i1 from "@progress/kendo-angular-buttons"; /** * @hidden */ export class GroupToolbarToolComponent { element; ngZone; hostClass = true; get lgClass() { return this.adaptive; } get mdClass() { return !this.adaptive; } onEscKeyDown(event) { event.preventDefault(); this.hostButton?.focus(event); this.close.emit(); } set groupItems(items) { this._groupItems = items; if (items?.first && (!isPresent(this.currentFocusedItemIndex) || this.currentFocusedItemIndex >= items.length || this.currentFocusedItemIndex < 0)) { this.ngZone.onStable.pipe(take(1)).subscribe(() => { this.currentFocusedItemIndex = 0; this.groupItems.first.nativeElement.focus(); }); return; } if (items?.first) { items.get(this.currentFocusedItemIndex).nativeElement.focus(); } } get groupItems() { return this._groupItems; } _groupItems; adaptive = false; close = new EventEmitter(); groupClear = new EventEmitter(); currentFocusedItemIndex; group = new Array(); columns = []; iconSize = 'medium'; upIcon = chevronUpIcon; downIcon = chevronDownIcon; removeIcon = xCircleIcon; addIcon = plusCircleIcon; clearIcon = xIcon; _ctx; set ctx(ctx) { if (!ctx || !ctx.grid) { return; } this._ctx = ctx; this.group = ctx.grid.group; this.subscription = ctx.grid.groupChange.subscribe((group) => { this.group = group; this.updateGroupedColumns(); }); this.ngZone.onStable.pipe(take(1)).subscribe(() => { this.updateGroupedColumns(); }); } get ctx() { return this._ctx; } groupedColumns = []; ungroupedColumns = []; subscription; hostButton; constructor(element, ngZone) { this.element = element; this.ngZone = ngZone; } ngOnInit() { this.iconSize = this.adaptive ? 'large' : 'medium'; } ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } } addGroup(column, ev) { ev.stopImmediatePropagation(); const index = this.group.length; const groups = this.group.filter(x => x.field !== column?.field); if (groups.length || this.group.length === 0) { this.group = [...groups.slice(0, index), { field: column?.field }, ...groups.slice(index)]; this.ctx.grid.groupChange.emit(this.group); this.updateGroupedColumns(); this.ngZone.onStable.pipe(take(1)).subscribe(() => { const newIndex = this.groupedColumns.length - 1; const newItem = this.groupItems.get(newIndex); if (newItem) { this.currentFocusedItemIndex = (this.groupedColumns?.length || 0) + newIndex; newItem.nativeElement.focus(); } }); } } removeGroup(column, ev) { ev.stopImmediatePropagation(); this.group = this.group.filter(x => x.field !== column?.field); this.ctx.grid.groupChange.emit(this.group); this.updateGroupedColumns(); this.ngZone.onStable.pipe(take(1)).subscribe(() => { const newIndex = this.ungroupedColumns.findIndex(ungroupedColumn => ungroupedColumn?.field === column?.field); const newItem = this.groupItems.get(newIndex + this.groupedColumns.length); if (newItem) { newItem.nativeElement.focus(); } }); } moveGroupUp(column, ev) { ev.stopImmediatePropagation(); const index = this.group.findIndex(x => x.field === column?.field); if (index > 0) { const groupToMove = this.group[index]; this.group.splice(index, 1); this.group.splice(index - 1, 0, groupToMove); this.ctx.grid.groupChange.emit(this.group); this.updateGroupedColumns(); this.ngZone.onStable.pipe(take(1)).subscribe(() => { const newItem = this.groupItems.get(index - 1); if (newItem) { newItem.nativeElement.focus(); this.currentFocusedItemIndex = index - 1; } }); } } moveGroupDown(column, ev) { ev.stopImmediatePropagation(); const index = this.group.findIndex(x => x.field === column?.field); if (index < this.group.length - 1) { const groupToMove = this.group[index]; this.group.splice(index, 1); this.group.splice(index + 1, 0, groupToMove); this.ctx.grid.groupChange.emit(this.group); this.updateGroupedColumns(); this.ngZone.onStable.pipe(take(1)).subscribe(() => { const newItem = this.groupItems.get(index + 1); if (newItem) { newItem.nativeElement.focus(); this.currentFocusedItemIndex = index + 1; } }); } } clear() { this.group = []; this.ctx.grid.groupChange.emit(this.group); this.groupClear.emit(this.group); } getColumnComponent(column) { return column; } onItemFocus(groupIndex, index) { const currentIndex = (typeof groupIndex === 'number' ? groupIndex : this.groupedColumns?.length || 0) + index; this.currentFocusedItemIndex = currentIndex; } handleGroupedKeydown(column, index, ev) { const code = normalizeNumpadKeys(ev); if (code === Keys.Enter || code === Keys.Backspace || code === Keys.Delete) { this.removeGroup(column, ev); } else if (code === Keys.ArrowUp && ev.shiftKey) { this.moveGroupUp(column, ev); } else if (code === Keys.ArrowDown && ev.shiftKey) { this.moveGroupDown(column, ev); } else if (code === Keys.ArrowUp) { this.navigateToPreviousItem(); } else if (code === Keys.ArrowDown) { this.navigateToNextItem(); } } handleUngroupedKeydown(column, index, ev) { const code = normalizeNumpadKeys(ev); if (code === Keys.Enter) { this.addGroup(column, ev); } else if (code === Keys.ArrowUp) { this.navigateToPreviousItem(); } else if (code === Keys.ArrowDown) { this.navigateToNextItem(); } } updateGroupedColumns() { this.groupedColumns = this.group .map(group => this.ctx.grid.columns.find(column => column?.field === group.field)) .filter(column => !!column); this.ungroupedColumns = this.ctx.grid.columns.filter(column => column?.groupable && !this.groupedColumns.some(gc => gc?.field === column?.field)); } navigateToNextItem() { if (this.currentFocusedItemIndex < this.groupItems.length - 1) { this.currentFocusedItemIndex++; this.groupItems.get(this.currentFocusedItemIndex).nativeElement.focus(); } else if (this.currentFocusedItemIndex === this.groupItems.length - 1) { this.currentFocusedItemIndex = 0; this.groupItems.get(this.currentFocusedItemIndex).nativeElement.focus(); } } navigateToPreviousItem() { if (this.currentFocusedItemIndex > 0) { this.currentFocusedItemIndex--; this.groupItems.get(this.currentFocusedItemIndex).nativeElement.focus(); } else if (this.currentFocusedItemIndex === 0) { this.currentFocusedItemIndex = this.groupItems.length - 1; this.groupItems.get(this.currentFocusedItemIndex).nativeElement.focus(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupToolbarToolComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: GroupToolbarToolComponent, isStandalone: true, selector: "kendo-group-toolbar-tool", inputs: { adaptive: "adaptive" }, outputs: { close: "close", groupClear: "groupClear" }, host: { listeners: { "keydown.escape": "onEscKeyDown($event)" }, properties: { "class.k-group-menu": "this.hostClass", "class.k-group-menu-lg": "this.lgClass", "class.k-group-menu-md": "this.mdClass" } }, viewQueries: [{ propertyName: "groupItems", predicate: ["groupItem"], descendants: true, read: ElementRef }], ngImport: i0, template: ` <div *ngIf="groupedColumns.length" class="k-group-menu-item-wrap"> <div *ngFor="let column of groupedColumns; let i = index" #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleGroupedKeydown(column, i, $event)" (focus)="onItemFocus(i, 0)" > <span class="k-group-menu-item-actions" *ngIf="groupedColumns.length > 1"> <span class="k-group-menu-item-action k-group-menu-item-up-action" (click)="moveGroupUp(column, $event)" [attr.aria-disabled]="i === 0" [class.k-disabled]="i === 0" > <kendo-icon-wrapper name="arrow-chevron-up" [svgIcon]="upIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> <span class="k-group-menu-item-action k-group-menu-item-down-action" (click)="moveGroupDown(column, $event)" [attr.aria-disabled]="i === groupedColumns.length - 1" [class.k-disabled]="i === groupedColumns.length - 1" > <kendo-icon-wrapper name="arrow-chevron-down" [svgIcon]="downIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> <span class="k-group-item-text">{{column.title || getColumnComponent(column).field}}</span> <span class="k-spacer"></span> <span class="k-group-menu-item-actions"> <span class="k-group-menu-item-action k-group-menu-item-remove-action" (click)="removeGroup(column, $event)"> <kendo-icon-wrapper name="x-circle" [svgIcon]="removeIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> </div> </div> <div *ngIf="ungroupedColumns.length" class="k-group-menu-item-wrap"> <div *ngFor="let column of ungroupedColumns; let i = index" #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleUngroupedKeydown(column, i, $event)" (focus)="onItemFocus(null, i)" > <span class="k-group-item-text">{{column.title || getColumnComponent(column).field}}</span> <span class="k-spacer"></span> <span class="k-group-menu-item-actions"> <span class="k-group-menu-item-action k-group-menu-item-add-action" (click)="addGroup(column, $event)"> <kendo-icon-wrapper name="plus-circle" [svgIcon]="addIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> </div> </div> <div *ngIf="!adaptive" class="k-actions k-actions-stretched k-actions-horizontal k-column-menu-footer"> <button kendoButton [svgIcon]="clearIcon" (click)="clear()" icon="x" > {{ctx?.localization.get('groupClearButton')}} </button> </div> `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconWrapperComponent, selector: "kendo-icon-wrapper", inputs: ["name", "svgIcon", "innerCssClass", "customFontClass", "size"], exportAs: ["kendoIconWrapper"] }, { kind: "component", type: i1.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: GroupToolbarToolComponent, decorators: [{ type: Component, args: [{ selector: 'kendo-group-toolbar-tool', template: ` <div *ngIf="groupedColumns.length" class="k-group-menu-item-wrap"> <div *ngFor="let column of groupedColumns; let i = index" #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleGroupedKeydown(column, i, $event)" (focus)="onItemFocus(i, 0)" > <span class="k-group-menu-item-actions" *ngIf="groupedColumns.length > 1"> <span class="k-group-menu-item-action k-group-menu-item-up-action" (click)="moveGroupUp(column, $event)" [attr.aria-disabled]="i === 0" [class.k-disabled]="i === 0" > <kendo-icon-wrapper name="arrow-chevron-up" [svgIcon]="upIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> <span class="k-group-menu-item-action k-group-menu-item-down-action" (click)="moveGroupDown(column, $event)" [attr.aria-disabled]="i === groupedColumns.length - 1" [class.k-disabled]="i === groupedColumns.length - 1" > <kendo-icon-wrapper name="arrow-chevron-down" [svgIcon]="downIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> <span class="k-group-item-text">{{column.title || getColumnComponent(column).field}}</span> <span class="k-spacer"></span> <span class="k-group-menu-item-actions"> <span class="k-group-menu-item-action k-group-menu-item-remove-action" (click)="removeGroup(column, $event)"> <kendo-icon-wrapper name="x-circle" [svgIcon]="removeIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> </div> </div> <div *ngIf="ungroupedColumns.length" class="k-group-menu-item-wrap"> <div *ngFor="let column of ungroupedColumns; let i = index" #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleUngroupedKeydown(column, i, $event)" (focus)="onItemFocus(null, i)" > <span class="k-group-item-text">{{column.title || getColumnComponent(column).field}}</span> <span class="k-spacer"></span> <span class="k-group-menu-item-actions"> <span class="k-group-menu-item-action k-group-menu-item-add-action" (click)="addGroup(column, $event)"> <kendo-icon-wrapper name="plus-circle" [svgIcon]="addIcon" [size]="iconSize" ></kendo-icon-wrapper> </span> </span> </div> </div> <div *ngIf="!adaptive" class="k-actions k-actions-stretched k-actions-horizontal k-column-menu-footer"> <button kendoButton [svgIcon]="clearIcon" (click)="clear()" icon="x" > {{ctx?.localization.get('groupClearButton')}} </button> </div> `, standalone: true, imports: [NgFor, NgIf, IconWrapperComponent, KENDO_BUTTON] }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { hostClass: [{ type: HostBinding, args: ['class.k-group-menu'] }], lgClass: [{ type: HostBinding, args: ['class.k-group-menu-lg'] }], mdClass: [{ type: HostBinding, args: ['class.k-group-menu-md'] }], onEscKeyDown: [{ type: HostListener, args: ['keydown.escape', ['$event']] }], groupItems: [{ type: ViewChildren, args: ['groupItem', { read: ElementRef }] }], adaptive: [{ type: Input }], close: [{ type: Output }], groupClear: [{ type: Output }] } });