Skip to content

Commit 4d1b981

Browse files
MKirovaMKirova
authored andcommitted
chore(*): Merged cells navigation.
1 parent f6ffa42 commit 4d1b981

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ import { getCurrentResourceStrings } from '../core/i18n/resources';
186186
import { isTree, recreateTree, recreateTreeFromFields } from '../data-operations/expressions-tree-util';
187187
import { getUUID } from './common/random';
188188
import { IGridMergeStrategy } from '../data-operations/merge-strategy';
189+
import { IgxGridMergeNavigationService } from './grid-merge-navigation.service';
189190

190191
interface IMatchInfoCache {
191192
row: any;
@@ -3984,6 +3985,23 @@ export abstract class IgxGridBaseDirective implements GridType,
39843985
if (this.actionStrip) {
39853986
this.actionStrip.menuOverlaySettings.outlet = this.outlet;
39863987
}
3988+
this._setupNavigationService();
3989+
}
3990+
3991+
protected _setupNavigationService() {
3992+
if (this.hasCellsToMerge) {
3993+
this.navigation = new IgxGridMergeNavigationService(this.platform);
3994+
this.navigation.grid = this;
3995+
}
3996+
}
3997+
3998+
protected get hasCellsToMerge() {
3999+
const columnToMerge = this.visibleColumns.filter(
4000+
x => x.merge && (this.cellMergeMode ==='always' ||
4001+
(this.cellMergeMode === 'onSort' && !!this.sortingExpressions
4002+
.find( x=> x.fieldName === x.fieldName)))
4003+
);
4004+
return columnToMerge.length > 0;
39874005
}
39884006

39894007
/**
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { Injectable } from '@angular/core';
2+
import { IgxGridNavigationService } from './grid-navigation.service';
3+
import { first } from 'rxjs/operators';
4+
/** @hidden */
5+
@Injectable()
6+
export class IgxGridMergeNavigationService extends IgxGridNavigationService {
7+
public override shouldPerformVerticalScroll(targetRowIndex: number, visibleColIndex: number): boolean {
8+
const targetRec = this.grid.verticalScrollContainer.igxForOf[targetRowIndex];
9+
const field = this.grid.visibleColumns[visibleColIndex]?.field;
10+
const rowSpan = targetRec?.cellMergeMeta?.get(field)?.rowSpan;
11+
if (rowSpan > 1) {
12+
const targetRow = super.getRowElementByIndex(targetRowIndex);
13+
const containerHeight = this.grid.calcHeight ? Math.ceil(this.grid.calcHeight) : 0;
14+
const scrollPos = this.getVerticalScrollPositions(targetRowIndex, rowSpan);
15+
return (!targetRow || targetRow.offsetTop < Math.abs(this.containerTopOffset)
16+
|| containerHeight && containerHeight < scrollPos.rowBottom - Math.ceil(this.scrollTop));
17+
} else {
18+
return super.shouldPerformVerticalScroll(targetRowIndex, visibleColIndex);
19+
}
20+
}
21+
22+
public override performVerticalScrollToCell(rowIndex: number, visibleColIndex: number, cb?: () => void) {
23+
const targetRec = this.grid.verticalScrollContainer.igxForOf[rowIndex];
24+
const field = this.grid.visibleColumns[visibleColIndex]?.field;
25+
const rowSpan = targetRec?.cellMergeMeta?.get(field)?.rowSpan;
26+
if (rowSpan > 1) {
27+
const containerHeight = this.grid.calcHeight ? Math.ceil(this.grid.calcHeight) : 0;
28+
const pos = this.getVerticalScrollPositions(rowIndex, rowSpan);
29+
const row = super.getRowElementByIndex(rowIndex);
30+
if ((this.scrollTop > pos.rowTop) && (!row || row.offsetTop < Math.abs(this.containerTopOffset))) {
31+
if (pos.topOffset === 0) {
32+
this.grid.verticalScrollContainer.scrollTo(rowIndex);
33+
} else {
34+
this.grid.verticalScrollContainer.scrollPosition = pos.rowTop;
35+
}
36+
} else {
37+
this.grid.verticalScrollContainer.addScrollTop(Math.abs(pos.rowBottom - this.scrollTop - containerHeight));
38+
}
39+
this.grid.verticalScrollContainer.chunkLoad
40+
.pipe(first()).subscribe(() => {
41+
if (cb) {
42+
cb();
43+
}
44+
});
45+
} else {
46+
super.performVerticalScrollToCell(rowIndex, visibleColIndex, cb);
47+
}
48+
}
49+
50+
protected override getNextPosition(rowIndex: number, colIndex: number, key: string, shift: boolean, ctrl: boolean, event: KeyboardEvent) {
51+
52+
const field = this.grid.visibleColumns[colIndex]?.field;
53+
switch (key) {
54+
case 'tab':
55+
case ' ':
56+
case 'spacebar':
57+
case 'space':
58+
case 'escape':
59+
case 'esc':
60+
case 'enter':
61+
case 'f2':
62+
case 'left':
63+
case 'arrowleft':
64+
case 'arrowright':
65+
case 'right':
66+
// same as base for these keys
67+
return super.getNextPosition(rowIndex, colIndex, key, shift, ctrl, event);
68+
break;
69+
case 'end':
70+
rowIndex = ctrl ? this.findLastDataRowIndex() : this.activeNode.row;
71+
colIndex = this.lastColumnIndex;
72+
break;
73+
case 'home':
74+
rowIndex = ctrl ? this.findFirstDataRowIndex() : this.activeNode.row;
75+
colIndex = 0;
76+
break;
77+
case 'arrowup':
78+
case 'up':
79+
const prevRec = this.grid.verticalScrollContainer.igxForOf[this.activeNode.row - 1];
80+
const root = prevRec?.cellMergeMeta?.get(field)?.root;
81+
const prev = this.activeNode.row - (root?.cellMergeMeta?.get(field).rowSpan || 1);
82+
colIndex = this.activeNode.column !== undefined ? this.activeNode.column : 0;
83+
rowIndex = ctrl ? this.findFirstDataRowIndex() : prev;
84+
break;
85+
case 'arrowdown':
86+
case 'down':
87+
const rec = this.grid.verticalScrollContainer.igxForOf[this.activeNode.row];
88+
const next = this.activeNode.row + (rec?.cellMergeMeta?.get(field)?.rowSpan || 1);
89+
colIndex = this.activeNode.column !== undefined ? this.activeNode.column : 0;
90+
rowIndex = ctrl ? this.findLastDataRowIndex() : next;
91+
break;
92+
default:
93+
return;
94+
}
95+
return { rowIndex, colIndex };
96+
}
97+
98+
private getVerticalScrollPositions(rowIndex: number, rowSpan: number) {
99+
const rowTop = this.grid.verticalScrollContainer.sizesCache[rowIndex];
100+
const rowBottom = this.grid.verticalScrollContainer.sizesCache[rowIndex + rowSpan];
101+
const topOffset = rowBottom - rowTop;
102+
return { topOffset, rowTop, rowBottom };
103+
}
104+
105+
private get scrollTop(): number {
106+
return Math.abs(this.grid.verticalScrollContainer.getScroll().scrollTop);
107+
}
108+
}

projects/igniteui-angular/src/lib/grids/grid/grid.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1357,11 +1357,12 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
13571357
this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
13581358
}
13591359

1360-
private _setupNavigationService() {
1360+
protected override _setupNavigationService() {
13611361
if (this.hasColumnLayouts) {
13621362
this.navigation = new IgxGridMRLNavigationService(this.platform);
13631363
this.navigation.grid = this;
13641364
}
1365+
super._setupNavigationService();
13651366
}
13661367

13671368
private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {

projects/igniteui-angular/src/lib/grids/row.directive.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,12 @@ export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy {
522522

523523
public isCellActive(visibleColumnIndex) {
524524
const node = this.grid.navigation.activeNode;
525+
const field = this.grid.visibleColumns[visibleColumnIndex].field;
526+
const rowSpan = this.metaData?.cellMergeMeta?.get(field)?.rowSpan;
527+
if (rowSpan > 1) {
528+
return node ? (node.row >= this.index && node.row < this.index + rowSpan)
529+
&& node.column === visibleColumnIndex : false;
530+
}
525531
return node ? node.row === this.index && node.column === visibleColumnIndex : false;
526532
}
527533

0 commit comments

Comments
 (0)