@progress/kendo-angular-grid
Version:
Kendo UI Grid for Angular - high performance data grid with paging, filtering, virtualization, CRUD, and more.
764 lines (763 loc) • 33.6 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, EventEmitter, Injectable, NgZone, Optional } from '@angular/core';
import { from, interval, Subscription } from 'rxjs';
import { filter, switchMap, switchMapTo, take, takeUntil, map } from 'rxjs/operators';
import { FocusRoot } from './focus-root';
import { FocusableDirective } from './focusable.directive';
import { GridFocusableElement } from './grid-focusable-element';
import { NavigationCursor } from './navigation-cursor';
import { NavigationModel } from './navigation-model';
import { DomEventsService } from '../common/dom-events.service';
import { isDocumentAvailable, isPresent, Keys } from '@progress/kendo-angular-common';
import { EditService } from '../editing/edit.service';
import { GroupsService } from '../grouping/groups.service';
import { PagerContextService } from '@progress/kendo-angular-pager';
import { closest, contains, findFocusableChild, isVisible, matchesNodeName } from '../rendering/common/dom-queries';
import { DetailsService } from '../rendering/details/details.service';
import { ScrollRequestService } from '../scrolling/scroll-request.service';
import { ContextService } from '../common/provider.service';
import { ColumnResizingService } from '../column-resizing/column-resizing.service';
import * as i0 from "@angular/core";
import * as i1 from "../common/dom-events.service";
import * as i2 from "@progress/kendo-angular-pager";
import * as i3 from "../scrolling/scroll-request.service";
import * as i4 from "../grouping/groups.service";
import * as i5 from "../rendering/details/details.service";
import * as i6 from "./focus-root";
import * as i7 from "../editing/edit.service";
import * as i8 from "../common/provider.service";
import * as i9 from "../column-resizing/column-resizing.service";
import * as i10 from "./focusable.directive";
const isInSameGrid = (element, gridElement) => closest(element, matchesNodeName('kendo-grid')) === gridElement;
const matchHeaderCell = matchesNodeName('th');
const matchDataCell = matchesNodeName('td');
const matchFooterCell = matchesNodeName('.k-grid-footer td');
const matchCell = (element) => matchDataCell(element) || matchHeaderCell(element) || matchFooterCell(element);
const gridCell = (element, gridElement) => {
let target = closest(element, matchCell);
while (target && !isInSameGrid(target, gridElement)) {
target = closest(target.parentElement, matchCell);
}
return target;
};
const targetCell = (target, gridElement) => {
const cell = gridCell(target, gridElement);
const row = closest(cell, matchesNodeName('tr'));
if (cell && row) {
let rowIndex = row.getAttribute('aria-rowindex') || row.getAttribute('data-kendo-grid-row-index');
rowIndex = rowIndex ? parseInt(rowIndex, 10) - 1 : null;
let colIndex = cell.getAttribute('aria-colindex');
colIndex = colIndex ? parseInt(colIndex, 10) - 1 : null;
if (rowIndex !== null && colIndex !== null) {
return { colIndex, rowIndex, element: cell };
}
}
};
const isArrowKey = keyCode => keyCode === Keys.ArrowLeft || keyCode === Keys.ArrowRight ||
keyCode === Keys.ArrowUp || keyCode === Keys.ArrowDown;
const isNavigationKey = keyCode => isArrowKey(keyCode) ||
keyCode === Keys.PageUp || keyCode === Keys.PageDown ||
keyCode === Keys.Home || keyCode === Keys.End;
const isInput = matchesNodeName('input');
const isTextInput = element => element && isInput(element) && element.type.toLowerCase() === 'text';
const isPrintableCharacter = (str) => str.length === 1 && str.match(/\S/);
const resizeStep = 10;
/**
* @hidden
*/
export class NavigationViewport {
firstItemIndex;
lastItemIndex;
constructor(firstItemIndex, lastItemIndex) {
this.firstItemIndex = firstItemIndex;
this.lastItemIndex = lastItemIndex;
}
containsRow(dataRowIndex) {
const headerRow = dataRowIndex < 0;
return headerRow || (dataRowIndex >= this.firstItemIndex && dataRowIndex <= this.lastItemIndex);
}
intersects(start, end) {
return (start <= this.firstItemIndex && this.lastItemIndex <= end) ||
(this.firstItemIndex <= start && start <= this.lastItemIndex) ||
(this.firstItemIndex <= end && end <= this.lastItemIndex);
}
}
/**
* @hidden
*/
export class NavigationService {
zone;
domEvents;
pagerContextService;
scrollRequestService;
groupsService;
detailsService;
focusRoot;
editService;
cd;
ctx;
resizeService;
focusableParent;
changes;
cellKeydown = new EventEmitter();
set metadata(value) {
this.meta = value;
this.cursor.metadata = value;
}
get metadata() {
return this.meta;
}
get enabled() {
return this.alive;
}
get pagerEnabled() {
return this.alive && this.pagerIsNavigable;
}
get tableEnabled() {
return this.alive && this.tableIsNavigable;
}
get toolbarEnabled() {
return this.alive && this.toolbarIsNavigable;
}
get activeCell() {
if (this.mode !== 0 /* NavigationMode.Standby */) {
return this.cursor.cell;
}
}
get activeRow() {
if (this.mode !== 0 /* NavigationMode.Standby */) {
return Object.assign({}, this.cursor.row, {
cells: this.cursor.row?.cells.toArray()
});
}
}
get isColumnResizable() {
const allColumns = Array.from(this.ctx.grid.columnsContainer.allColumns);
const column = allColumns.find((col) => col.level === this.activeCell.rowIndex && col.leafIndex === this.activeCell.colIndex);
if (!column.parent) {
if (column.isColumnGroup) {
return this.activeCell.colIndex + this.activeCell.colSpan !== this.ctx.grid.columnsContainer.leafColumnsToRender.length;
}
else {
return this.activeCell.colIndex !== this.ctx.grid.columnsContainer.leafColumnsToRender.length - 1;
}
}
else {
const columnGroup = column.parent;
const columnGroupChildren = Array.from(columnGroup.children).sort((a, b) => a.orderIndex - b.orderIndex);
const columnIndexInsideGroup = columnGroupChildren.indexOf(column);
if (column.isReordered || column.orderIndex > 0 || (column.isReordered && column.orderIndex === 0)) {
return (column.orderIndex - columnGroupChildren[0]['orderIndex']) !== columnGroupChildren.length - 1;
}
return columnIndexInsideGroup !== columnGroupChildren.length - 1;
}
}
viewport;
columnViewport;
activeRowIndex = 0;
alive = false;
active = true;
mode = 0 /* NavigationMode.Standby */;
model = new NavigationModel();
cursor = new NavigationCursor(this.model);
meta;
subs;
pendingRowIndex;
virtualCell;
pagerIsNavigable = false;
tableIsNavigable = false;
toolbarIsNavigable = false;
lastCellRowIndex;
get activeDataRow() {
return Math.max(0, this.activeRowIndex - this.meta.headerRows);
}
constructor(zone, domEvents, pagerContextService, scrollRequestService, groupsService, detailsService, focusRoot, editService, cd, ctx, resizeService, focusableParent) {
this.zone = zone;
this.domEvents = domEvents;
this.pagerContextService = pagerContextService;
this.scrollRequestService = scrollRequestService;
this.groupsService = groupsService;
this.detailsService = detailsService;
this.focusRoot = focusRoot;
this.editService = editService;
this.cd = cd;
this.ctx = ctx;
this.resizeService = resizeService;
this.focusableParent = focusableParent;
this.changes = this.cursor.changes;
}
init(meta, navigableOptions) {
this.setActiveSections(navigableOptions);
this.alive = true;
this.focusRoot.active = true;
this.metadata = meta;
const onStableSubscriber = (...operators) => (args) => this.zone.isStable ?
from([true]).pipe(map(() => args)) :
this.zone.onStable.pipe(take(1), map(() => args), ...operators);
const onStable = onStableSubscriber();
this.subs = new Subscription();
this.subs.add(this.cursor.changes.subscribe(args => this.onCursorChanges(args)));
this.subs.add(this.domEvents.focus.pipe(switchMap(onStable))
.subscribe((args) => this.navigateTo(args.target)));
this.subs.add(this.domEvents.focusOut.pipe(filter(() => this.mode !== 0 /* NavigationMode.Standby */), switchMap(onStableSubscriber(takeUntil(this.domEvents.focus))))
.subscribe(args => this.onFocusOut(args)));
this.subs.add(this.domEvents.windowBlur.pipe(filter(() => this.mode !== 0 /* NavigationMode.Standby */))
.subscribe(() => this.onWindowBlur()));
this.subs.add(
// Closing the editor will not always trigger focusout in Firefox.
// To get around this, we ensure that the cell is closed after editing.
this.editService.changes.pipe(filter(e => e.action !== 'edit' && this.mode === 2 /* NavigationMode.Content */), filter((e) => e.action === 'cellClose' && !e.prevented), switchMap(onStable))
.subscribe(() => this.leaveCell()));
this.subs.add(this.pagerContextService.pageChange
.subscribe(() => this.cursor.reset(0, 0)));
this.subs.add(this.domEvents.keydown
.subscribe(args => this.onKeydown(args)));
this.subs.add(this.domEvents.keydown.pipe(filter(args => args.keyCode === Keys.Tab && this.mode === 2 /* NavigationMode.Content */), switchMapTo(this.domEvents.focusOut.pipe(takeUntil(
// Timeout if focusOut doesn't fire very soon
interval(0).pipe(take(1))))))
.subscribe(() => this.onTabout()));
if (this.focusableParent) {
const element = new GridFocusableElement(this);
this.focusableParent.registerElement(element);
}
this.deactivateElements();
}
ngOnDestroy() {
if (this.subs) {
this.subs.unsubscribe();
}
this.alive = false;
}
registerCell(cell) {
if (cell.logicalRowIndex !== this.pendingRowIndex) {
const modelCell = this.model.registerCell(cell);
if (this.virtualCell && this.cursor.activateVirtualCell(modelCell)) {
this.virtualCell = false;
}
}
}
registerCellOnCurrentRow(cell) {
if (cell.logicalRowIndex === this.pendingRowIndex) {
this.model.registerCell(cell);
}
}
unregisterCell(index, rowIndex, cell) {
this.model.unregisterCell(index, rowIndex, cell);
}
registerRow(row) {
this.model.registerRow(row);
this.pendingRowIndex = row.logicalRowIndex;
}
updateRow(row) {
this.model.updateRow(row);
}
unregisterRow(index, row) {
this.model.unregisterRow(index, row);
const lastRow = this.model.lastRow;
if (lastRow && this.mode === 0 /* NavigationMode.Standby */) {
const maxIndex = (this.needsViewport() && this.viewport) ? this.viewport.lastItemIndex : lastRow.index;
if (this.activeRowIndex > maxIndex) {
this.cursor.reset(0, 0);
}
}
}
isCellFocusable(cell) {
return this.alive &&
this.active &&
this.mode !== 2 /* NavigationMode.Content */ &&
this.cursor.isActive(cell.logicalRowIndex, cell.logicalColIndex);
}
isCellFocused(cell) {
return this.mode === 1 /* NavigationMode.Cursor */ && this.isCellFocusable(cell);
}
navigateTo(el) {
if (!this.alive || !isDocumentAvailable()) {
return;
}
const cell = targetCell(el, this.meta.gridElement.nativeElement);
if (!cell) {
return;
}
const oldMode = this.mode;
const focusInCell = contains(cell.element, document.activeElement);
const focusInActiveRowContent = this.mode === 2 /* NavigationMode.Content */ &&
this.activeRowIndex === cell.rowIndex &&
el !== cell.element;
if (focusInCell) {
this.mode = 2 /* NavigationMode.Content */;
this.cursor.reset(cell.rowIndex, cell.colIndex);
this.activateRow();
}
else if (!focusInActiveRowContent) {
this.mode = 1 /* NavigationMode.Cursor */;
this.deactivateElements();
const alreadyActive = this.cursor.isActive(cell.rowIndex, cell.colIndex);
const isCursor = oldMode === 1 /* NavigationMode.Cursor */ && alreadyActive;
if (!isCursor) {
this.cursor.reset(cell.rowIndex, cell.colIndex);
}
}
}
tryFocus(el) {
this.activateElements();
const focusable = findFocusableChild(el);
if (focusable) {
const cell = targetCell(focusable, this.meta.gridElement.nativeElement);
if (cell) {
this.cursor.reset(cell.rowIndex, cell.colIndex);
this.deactivateElements();
this.enterCell();
}
focusable.focus();
}
else {
this.deactivateElements();
}
return !!focusable;
}
needsViewport() {
return this.meta && this.meta.isVirtual;
}
setViewport(firstItemIndex, lastItemIndex) {
this.viewport = new NavigationViewport(firstItemIndex, lastItemIndex);
if (this.meta && this.meta.isVirtual && this.activeDataRow > -1) {
const dataRowIndex = this.activeDataRow;
const ahead = firstItemIndex - dataRowIndex;
const behind = dataRowIndex - lastItemIndex;
if (ahead > 0) {
this.cursor.reset(firstItemIndex + this.meta.headerRows);
}
else if (behind > 0) {
this.cursor.reset(lastItemIndex - this.meta.headerRows);
}
}
}
setColumnViewport(firstItemIndex, lastItemIndex) {
this.columnViewport = new NavigationViewport(firstItemIndex, lastItemIndex);
if (this.meta && this.meta.isVirtual && this.activeDataRow > -1) {
const activeColumnIndex = this.cursor.cell ? this.cursor.cell.colIndex : 0;
const ahead = firstItemIndex - activeColumnIndex;
const behind = activeColumnIndex - lastItemIndex;
if (ahead > 0) {
this.cursor.reset(undefined, firstItemIndex, false);
}
else if (behind > 0) {
this.cursor.reset(undefined, lastItemIndex, false);
}
}
}
focusCell(rowIndex = undefined, colIndex = undefined) {
this.mode = 1 /* NavigationMode.Cursor */;
this.cursor.reset(rowIndex, colIndex);
return this.activeCell;
}
focusCellByElement(el) {
const cell = targetCell(el, this.meta.gridElement.nativeElement);
if (cell) {
return this.focusCell(cell.rowIndex, cell.colIndex);
}
}
focusNextCell(wrap = true) {
return this.focusAdjacentCell(true, wrap);
}
focusPrevCell(wrap = true) {
return this.focusAdjacentCell(false, wrap);
}
toggle(active) {
this.active = active;
this.cursor.announce();
}
hasFocus() {
return this.mode === 1 /* NavigationMode.Cursor */ || this.mode === 2 /* NavigationMode.Content */;
}
autoFocusCell(start, end) {
return !this.meta.virtualColumns || end < this.meta.columns.lockedLeafColumns.length || this.columnViewport.intersects(start, end);
}
setActiveSections(navigableOptions) {
this.pagerIsNavigable = navigableOptions.includes('pager');
this.tableIsNavigable = navigableOptions.includes('table');
this.toolbarIsNavigable = navigableOptions.includes('toolbar');
}
focusAdjacentCell(fwd, wrap) {
this.focusCell();
let success = fwd ? this.moveCursorFwd() : this.moveCursorBwd();
if (wrap && !success) {
success = fwd ? this.cursor.moveDown(1) : this.cursor.moveUp(1);
if (success) {
const row = this.cursor.row;
const colIdx = fwd ? 0 : this.cursor.lastCellIndex(row);
this.cursor.reset(row.index, colIdx);
}
}
if (success) {
return this.activeCell;
}
else {
this.mode = 0 /* NavigationMode.Standby */;
this.cursor.announce();
}
return null;
}
enterCell() {
const cell = this.cursor.cell;
if (!cell) {
return;
}
const group = cell.focusGroup;
const focusable = group && group.canFocus();
this.mode = focusable ? 2 /* NavigationMode.Content */ : 1 /* NavigationMode.Cursor */;
this.cursor.announce();
if (focusable) {
this.activateRow();
group.focus();
}
}
leaveCell() {
const cell = this.cursor.cell;
if (!cell) {
return;
}
const group = cell.focusGroup;
const focusable = group && group.canFocus();
if (!focusable) {
this.deactivateElements();
}
this.mode = 1 /* NavigationMode.Cursor */;
this.cursor.announce();
}
activateElements() {
this.focusRoot.activate();
}
deactivateElements() {
this.focusRoot.deactivate();
}
activateRow() {
this.cursor.row.cells
.forEach(cell => cell.focusGroup && cell.focusGroup.activate());
}
moveCursorFwd() {
this.lastCellRowIndex = this.activeCell.rowIndex;
return this.ctx.localization.rtl ? this.cursor.moveLeft() : this.cursor.moveRight();
}
moveCursorBwd() {
this.lastCellRowIndex = this.activeCell.rowIndex;
return this.ctx.localization.rtl ? this.cursor.moveRight() : this.cursor.moveLeft();
}
onCursorKeydown(args) {
let preventDefault = false;
const modifier = args.ctrlKey || args.metaKey;
let step = modifier ? 5 : 1;
const rowspan = +args.target?.getAttribute('rowspan');
let rowspanOffset = 0;
if (!this.onCellKeydown(args)) {
return;
}
const row = this.cursor.row;
const dir = args.keyCode === Keys.ArrowDown ? 'Down' : 'Up';
const right = args.keyCode === Keys.ArrowRight;
switch (args.keyCode) {
case Keys.ArrowDown:
case Keys.ArrowUp:
if (rowspan > 1) {
rowspanOffset = this.calculateRowspanOffset(dir, rowspan);
step += rowspanOffset;
}
if (args.shiftKey) {
if (this.ctx.grid.blockArrowSelection) {
return;
}
preventDefault = this.cursor[`move${dir}`](step);
if (this.activeRow?.dataItem) {
const sign = dir === 'Down' ? 1 : -1;
this.handleVerticalArrowSelection(sign * step);
}
}
else {
preventDefault = this.cursor[`move${dir}`](step);
}
this.lastCellRowIndex = this.activeRowIndex;
break;
case Keys.ArrowRight:
case Keys.ArrowLeft:
if (args.altKey && this.ctx.grid.resizable && this.isColumnResizable) {
this.columnResize(right);
break;
}
if (args.shiftKey) {
if (this.ctx.grid.blockArrowSelection) {
return;
}
preventDefault = this[`moveCursor${right ? 'Fwd' : 'Bwd'}`]();
this.handleHorizontalArrowSelection(args);
}
else {
preventDefault = this[`moveCursor${right ? 'Fwd' : 'Bwd'}`]();
}
break;
case Keys.PageDown:
if (this.metadata.isVirtual && this.viewport) {
let nextItemIndex = this.meta.headerRows + this.viewport.lastItemIndex + 1;
if (this.metadata.hasDetailTemplate) {
nextItemIndex++;
}
nextItemIndex = Math.min(this.meta.maxLogicalRowIndex, nextItemIndex);
this.cursor.reset(nextItemIndex);
preventDefault = true;
}
else if (this.metadata.hasPager) {
this.zone.run(() => this.pagerContextService.nextPage());
preventDefault = true;
}
break;
case Keys.PageUp:
if (this.metadata.isVirtual && this.viewport) {
const viewportSize = this.viewport.lastItemIndex - this.viewport.firstItemIndex;
const firstItemIndex = this.viewport.firstItemIndex;
const nextItemIndex = Math.max(this.meta.headerRows, firstItemIndex - viewportSize - 1);
this.cursor.reset(nextItemIndex);
preventDefault = true;
}
else if (this.metadata.hasPager) {
this.zone.run(() => this.pagerContextService.prevPage());
preventDefault = true;
}
break;
case Keys.Home:
if (modifier) {
if (this.meta.isVirtual) {
this.cursor.reset(this.meta.headerRows, 0, false);
}
else {
this.cursor.reset(this.model.firstRow.index, 0, false);
}
}
else {
let firstColumnIndex = 0;
if (this.meta.hasDetailTemplate && row.index < this.meta.headerRows) {
firstColumnIndex = 1;
}
this.cursor.reset(row.index, firstColumnIndex, false);
}
preventDefault = true;
break;
case Keys.End:
if (modifier) {
if (this.meta.isVirtual) {
let lastRowIndex = this.meta.maxLogicalRowIndex;
if (this.meta.hasDetailTemplate) {
lastRowIndex--;
}
this.cursor.reset(lastRowIndex, this.cursor.lastCellIndex(), false);
}
else {
this.cursor.reset(this.model.lastRow.index, this.cursor.lastCellIndex(this.model.lastRow), false);
}
}
else {
const lastIndex = this.cursor.lastCellIndex(row);
const cell = this.model.findCell(lastIndex, row);
if (cell) {
this.cursor.reset(cell.rowIndex, cell.colIndex);
}
else {
this.cursor.reset(row.index, lastIndex);
}
}
preventDefault = true;
break;
case Keys.Enter:
case Keys.F2: {
const groupItem = row.groupItem;
if (groupItem) {
this.zone.run(() => this.groupsService.toggleRow(groupItem));
}
else if (this.cursor.cell.detailExpandCell) {
this.zone.run(() => this.detailsService.toggleRow(row.dataRowIndex, row.dataItem));
}
else {
this.enterCell();
if (!this.cursor.cell.focusGroup.isNavigable()) {
preventDefault = true;
}
}
break;
}
default:
if (!args.ctrlKey && !args.altKey && isPrintableCharacter(args.key)) {
this.enterCell();
}
}
if (preventDefault) {
args.preventDefault();
}
}
columnResize(onRightArrow) {
const column = this.ctx.grid.columnsContainer.allColumns.find((col) => col.level === this.activeCell.rowIndex && col.leafIndex === this.activeCell.colIndex);
this.resizeService.start(column);
this.resizeService.resizeColumns(onRightArrow ? resizeStep : -1 * resizeStep);
if (this.resizeService.resizeColumns.length > 0) {
this.resizeService.end();
}
}
onContentKeydown(args) {
if (!this.onCellKeydown(args)) {
return;
}
const confirm = !args.defaultPrevented && args.keyCode === Keys.Enter && isTextInput(args.srcElement);
if (args.keyCode === Keys.Escape || args.keyCode === Keys.F2 || confirm) {
this.leaveCell();
this.cursor.reset();
args.stopPropagation();
}
else if (isNavigationKey(args.keyCode) && this.cursor.cell.focusGroup.isNavigable()) {
this.onCursorKeydown(args);
if (args.defaultPrevented) {
this.leaveCell();
}
}
}
onCellKeydown(args) {
if (this.editService.isEditingCell()) {
const confirm = args.keyCode === Keys.Enter;
const cancel = args.keyCode === Keys.Escape;
const navigate = isNavigationKey(args.keyCode);
if (confirm) {
this.editService.closeCell(args);
}
else if (cancel) {
this.editService.closeCell(args);
this.cd.detectChanges();
}
else if (navigate) {
return false;
}
}
this.cellKeydown.emit(args);
return true;
}
onCursorChanges(args) {
this.activeRowIndex = args.rowIndex;
const dataRowIndex = this.activeDataRow;
if (this.meta && (this.meta.isVirtual &&
args.rowIndex >= this.meta.headerRows &&
this.viewport &&
!this.viewport.containsRow(dataRowIndex) && dataRowIndex > -1)) {
this.scrollRequestService.scrollTo({ row: dataRowIndex });
}
if (this.meta.virtualColumns && args.colIndex >= this.meta.columns.lockedLeafColumns.length) {
const cell = this.activeCell;
const { start, end } = this.model.cellRange(cell);
if (!cell) {
this.virtualCell = true;
}
if ((!cell && this.mode !== 0 /* NavigationMode.Standby */) || (cell && !this.columnViewport.intersects(start, end))) {
this.scrollRequestService.scrollTo({ column: args.colIndex - (this.metadata.hasDetailTemplate ? 1 : 0) });
}
}
}
onFocusOut(args) {
if (isVisible(args.target)) {
this.mode = 0 /* NavigationMode.Standby */;
}
else {
// Focused target is no longer visible,
// reset to cursor mode and recapture focus.
this.mode = 1 /* NavigationMode.Cursor */;
}
this.deactivateElements();
this.cursor.announce();
}
onWindowBlur() {
this.mode = 0 /* NavigationMode.Standby */;
this.deactivateElements();
this.cursor.announce();
}
onKeydown(args) {
if (this.mode === 1 /* NavigationMode.Cursor */) {
this.onCursorKeydown(args);
}
else if (this.mode === 2 /* NavigationMode.Content */) {
this.onContentKeydown(args);
}
}
onTabout() {
// Tabbed out of the last focusable content element
// reset to cursor mode and recapture focus.
if (this.cursor.cell.focusGroup.isNavigable()) {
// Unless the cell has a single focusable element,
// otherwise we'd return to Content mode and enter an endless loop
return;
}
this.leaveCell();
this.cursor.reset();
}
handleVerticalArrowSelection(args) {
const cellSelectionEnabled = this.ctx.grid.cellSelectionService.active;
const rowSelectionEnabled = this.ctx.grid.selectionService.active && !this.ctx.grid.selectableSettings.checkboxOnly;
if (cellSelectionEnabled || rowSelectionEnabled) {
const selectionService = this.ctx.grid[cellSelectionEnabled ? 'cellSelectionService' : 'selectionService'];
const colIdx = this.cursor.cell ? this.cursor.cell.colIndex : 0;
const rowIdx = this.activeRow.dataRowIndex - this.ctx.grid.skip;
const dataItem = selectionService.settings.view.at(rowIdx);
const item = { index: this.activeRow.dataRowIndex, data: dataItem, column: this.ctx.grid.columnsContainer.leafColumnsToRender[colIdx] };
if (selectionService.options.mode === 'multiple') {
cellSelectionEnabled ? this.handleMultipleArrowCellSelection(item) : this.handleMultipleArrowRowSelection(item);
}
else {
selectionService.handleClick(item, args);
}
}
}
handleHorizontalArrowSelection(args) {
const cellSelectionEnabled = this.ctx.grid.cellSelectionService.active;
if (cellSelectionEnabled) {
const selectionService = this.ctx.grid[cellSelectionEnabled ? 'cellSelectionService' : 'selectionService'];
const row = this.activeRow;
const colIdx = this.cursor.cell ? this.cursor.cell.colIndex : 0;
const dataItem = selectionService.settings.view.at(row.dataRowIndex - this.ctx.grid.skip);
const item = { index: row.dataRowIndex, data: dataItem, column: this.ctx.grid.columnsContainer.leafColumnsToRender[colIdx] };
if (!isPresent(dataItem) || !isPresent(item.column)) {
return;
}
if (selectionService.options.mode === 'multiple') {
this.handleMultipleArrowCellSelection(item);
}
else {
selectionService.handleClick(item, args);
}
}
}
handleMultipleArrowCellSelection(item) {
const cellSelectionService = this.ctx.grid.cellSelectionService;
const startRowIndex = Math.min(cellSelectionService.lastSelectionItemRowIndex, item.index);
const startColIndex = Math.min(cellSelectionService.lastSelectionItemColIndex, item.column.leafIndex);
const endRowIndex = Math.max(cellSelectionService.lastSelectionItemRowIndex, item.index);
const endColIndex = Math.max(cellSelectionService.lastSelectionItemColIndex, item.column.leafIndex);
const ev = cellSelectionService.selectRange(startRowIndex, startColIndex, endRowIndex, endColIndex);
cellSelectionService.changes.emit(ev);
}
handleMultipleArrowRowSelection(item) {
const rowSelectionService = this.ctx.grid.selectionService;
const startRowIndex = Math.min(rowSelectionService.lastSelectionStartIndex, item.index);
const endRowIndex = Math.max(rowSelectionService.lastSelectionStartIndex, item.index);
const ev = rowSelectionService.selectRange(startRowIndex, endRowIndex);
rowSelectionService.changes.emit(ev);
}
calculateRowspanOffset(direction, cellRowspan) {
if (!isPresent(this.lastCellRowIndex)) {
return 0;
}
const offset = direction === 'Up' ? Math.abs(this.lastCellRowIndex - this.activeRowIndex) : (this.activeRowIndex + cellRowspan - this.lastCellRowIndex - 1);
return offset;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService, deps: [{ token: i0.NgZone }, { token: i1.DomEventsService }, { token: i2.PagerContextService }, { token: i3.ScrollRequestService }, { token: i4.GroupsService }, { token: i5.DetailsService }, { token: i6.FocusRoot }, { token: i7.EditService }, { token: i0.ChangeDetectorRef }, { token: i8.ContextService }, { token: i9.ColumnResizingService }, { token: i10.FocusableDirective, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NavigationService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i1.DomEventsService }, { type: i2.PagerContextService }, { type: i3.ScrollRequestService }, { type: i4.GroupsService }, { type: i5.DetailsService }, { type: i6.FocusRoot }, { type: i7.EditService }, { type: i0.ChangeDetectorRef }, { type: i8.ContextService }, { type: i9.ColumnResizingService }, { type: i10.FocusableDirective, decorators: [{
type: Optional
}] }]; } });