UNPKG

@progress/kendo-angular-grid

Version:

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

452 lines (447 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 { 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, normalizeKeys } 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({ preventScroll: true }); }); return; } if (items?.first) { items.get(this.currentFocusedItemIndex).nativeElement.focus({ preventScroll: true }); } } 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 = normalizeKeys(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 = normalizeKeys(ev); if (code === Keys.Enter) { this.addGroup(column, ev); } else if (code === Keys.ArrowUp) { this.navigateToPreviousItem(); } else if (code === Keys.ArrowDown) { this.navigateToNextItem(); } } updateGroupedColumns() { const columns = this.ctx.grid['columnInfoService'].leafNamedColumns; const groupableColumns = columns.filter(column => column?.groupable); this.groupedColumns = this.group .map(group => columns.find(column => column?.field === group.field)) .filter(column => !!column); this.ungroupedColumns = groupableColumns.filter(column => !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: "18.2.14", ngImport: i0, type: GroupToolbarToolComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", 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: ` @if (groupedColumns.length) { <div class="k-group-menu-item-wrap"> @for (column of groupedColumns; track column; let i = $index) { <div #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleGroupedKeydown(column, i, $event)" (focus)="onItemFocus(i, 0)" > @if (groupedColumns.length > 1) { <span class="k-group-menu-item-actions"> <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> } @if (ungroupedColumns.length) { <div class="k-group-menu-item-wrap"> @for (column of ungroupedColumns; track column; let i = $index) { <div #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> } @if (!adaptive) { <div 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: "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: "18.2.14", ngImport: i0, type: GroupToolbarToolComponent, decorators: [{ type: Component, args: [{ selector: 'kendo-group-toolbar-tool', template: ` @if (groupedColumns.length) { <div class="k-group-menu-item-wrap"> @for (column of groupedColumns; track column; let i = $index) { <div #groupItem role="button" class="k-group-menu-item" tabindex="0" (keydown)="handleGroupedKeydown(column, i, $event)" (focus)="onItemFocus(i, 0)" > @if (groupedColumns.length > 1) { <span class="k-group-menu-item-actions"> <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> } @if (ungroupedColumns.length) { <div class="k-group-menu-item-wrap"> @for (column of ungroupedColumns; track column; let i = $index) { <div #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> } @if (!adaptive) { <div 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: [IconWrapperComponent, KENDO_BUTTON] }] }], ctorParameters: () => [{ 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 }] } });