UNPKG

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