Skip to content

Commit 79a69e7

Browse files
committed
feat(pivot): Add column/row selection handling
1 parent cd7687f commit 79a69e7

File tree

6 files changed

+161
-127
lines changed

6 files changed

+161
-127
lines changed

projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.pipes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { cloneArray } from '../../core/utils';
33
import { DataUtil } from '../../data-operations/data-util';
44
import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
55
import { IFilteringStrategy } from '../../data-operations/filtering-strategy';
6-
import { IPivotConfiguration, IPivotDimension, IPivotKeys } from './pivot-grid.interface';
6+
import { IPivotConfiguration, IPivotKeys } from './pivot-grid.interface';
77
import { PivotColumnDimensionsStrategy, PivotRowDimensionsStrategy } from '../../data-operations/pivot-strategy';
88
import { PivotUtil } from './pivot-util';
99
import { FilteringLogic } from '../../data-operations/filtering-expression.interface';
Lines changed: 78 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,87 @@
1-
<!-- Row Dimension -->
2-
<div #rowDimensionContainer>
3-
<igx-pivot-header-row class="igx-grid-thead" tabindex="0"
4-
[grid]="grid"
5-
[hasMRL]="grid.hasColumnLayouts"
6-
[density]="grid.displayDensity"
7-
[activeDescendant]="grid.activeDescendant"
8-
[width]="grid.pivotRowWidths"
9-
[unpinnedColumnCollection]="rowDimension"
10-
(scroll)="grid.preventHeaderScroll($event)"
11-
[row]="this"
12-
>
13-
</igx-pivot-header-row>
14-
</div>
1+
<!-- Row Dimension -->
2+
<div #rowDimensionContainer>
3+
<igx-pivot-header-row class="igx-grid-thead" tabindex="0" [grid]="grid" [hasMRL]="grid.hasColumnLayouts"
4+
[density]="grid.displayDensity" [activeDescendant]="grid.activeDescendant" [width]="grid.pivotRowWidths"
5+
[unpinnedColumnCollection]="rowDimension" (scroll)="grid.preventHeaderScroll($event)" [row]="this">
6+
</igx-pivot-header-row>
7+
</div>
8+
159

10+
<ng-container *ngIf="pinnedColumns.length > 0 && grid.isPinningToStart">
11+
<ng-template *ngTemplateOutlet="pinnedCellsTemplate; context: this"></ng-template>
12+
</ng-container>
1613

17-
<ng-container *ngIf="pinnedColumns.length > 0 && grid.isPinningToStart">
18-
<ng-template *ngTemplateOutlet="pinnedCellsTemplate; context: this"></ng-template>
19-
</ng-container>
14+
<ng-template igxGridFor let-col [igxGridForOf]="unpinnedColumns | igxNotGrouped"
15+
[igxForScrollContainer]="grid.parentVirtDir" let-colIndex="index" [igxForSizePropName]='"calcPixelWidth"'
16+
[igxForScrollOrientation]="'horizontal'" [igxForContainerSize]='grid.unpinnedWidth'
17+
[igxForTrackBy]='grid.trackColumnChanges' #igxDirRef>
18+
<igx-grid-cell #cell class="igx-grid__td igx-grid__td--fw"
19+
[class.igx-grid__td--edited]="rowID | transactionState:col.field:grid.rowEditable:grid.transactions:grid.pipeTrigger:grid.gridAPI.crudService.cell:grid.gridAPI.crudService.row"
20+
[attr.aria-describedby]="gridID + '_' + col.field | igxStringReplace:'.':'_'"
21+
[class.igx-grid__td--number]="col.dataType === 'number' || col.dataType === 'percent' || col.dataType === 'currency'"
22+
[class.igx-grid__td--bool]="col.dataType === 'boolean'"
23+
[ngClass]="this.getCellClass(col) | igxCellStyleClasses:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
24+
[ngStyle]="col.cellStyles | igxCellStyles:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
25+
[editMode]="col.editable && this.grid.crudService.targetInEdit(index, col.index)" [column]="col"
26+
[formatter]="col.formatter" [intRow]="this" [active]="isCellActive(col.visibleIndex)"
27+
[style.min-height.px]="cellHeight" [rowData]="rowData" [style.min-width]="col.width"
28+
[style.max-width]="col.width" [style.flex-basis]="col.width" [width]="col.getCellWidth()"
29+
[visibleColumnIndex]="col.visibleIndex"
30+
[value]="rowData | dataMapper:col.field:grid.pipeTrigger:rowData[col.field]:col.hasNestedPath"
31+
[cellTemplate]="col.bodyTemplate" [lastSearchInfo]="grid.lastSearchInfo"
32+
[cellSelectionMode]="grid.cellSelection" [displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)">
33+
</igx-grid-cell>
34+
</ng-template>
2035

21-
<ng-template igxGridFor let-col [igxGridForOf]="unpinnedColumns | igxNotGrouped" [igxForScrollContainer]="grid.parentVirtDir" let-colIndex="index" [igxForSizePropName]='"calcPixelWidth"' [igxForScrollOrientation]="'horizontal'" [igxForContainerSize]='grid.unpinnedWidth' [igxForTrackBy]='grid.trackColumnChanges' #igxDirRef>
22-
<igx-grid-cell
23-
#cell
24-
class="igx-grid__td igx-grid__td--fw"
25-
[class.igx-grid__td--edited]="rowID | transactionState:col.field:grid.rowEditable:grid.transactions:grid.pipeTrigger:grid.gridAPI.crudService.cell:grid.gridAPI.crudService.row"
26-
[attr.aria-describedby]="gridID + '_' + col.field | igxStringReplace:'.':'_'"
27-
[class.igx-grid__td--number]="col.dataType === 'number' || col.dataType === 'percent' || col.dataType === 'currency'"
28-
[class.igx-grid__td--bool]="col.dataType === 'boolean'"
29-
[ngClass]="this.getCellClass(col) | igxCellStyleClasses:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
30-
[ngStyle]="col.cellStyles | igxCellStyles:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
31-
[editMode]="col.editable && this.grid.crudService.targetInEdit(index, col.index)"
32-
[column]="col"
33-
[formatter]="col.formatter"
34-
[intRow]="this"
35-
[active]="isCellActive(col.visibleIndex)"
36-
[style.min-height.px]="cellHeight"
37-
[rowData]="rowData"
38-
[style.min-width]="col.width"
39-
[style.max-width]="col.width"
40-
[style.flex-basis]="col.width"
41-
[width]="col.getCellWidth()"
42-
[visibleColumnIndex]="col.visibleIndex"
43-
[value]="rowData | dataMapper:col.field:grid.pipeTrigger:rowData[col.field]:col.hasNestedPath"
44-
[cellTemplate]="col.bodyTemplate"
45-
[lastSearchInfo]="grid.lastSearchInfo"
46-
[cellSelectionMode]="grid.cellSelection"
47-
[displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)">
48-
</igx-grid-cell>
49-
</ng-template>
36+
<ng-container *ngIf="pinnedColumns.length > 0 && !grid.isPinningToStart">
37+
<ng-template *ngTemplateOutlet="pinnedCellsTemplate; context: this"></ng-template>
38+
</ng-container>
5039

51-
<ng-container *ngIf="pinnedColumns.length > 0 && !grid.isPinningToStart">
52-
<ng-template *ngTemplateOutlet="pinnedCellsTemplate; context: this"></ng-template>
53-
</ng-container>
40+
<ng-template #rowSelectorBaseTemplate>
41+
<div class="igx-grid__cbx-padding">
42+
<igx-checkbox [tabindex]="-1" [readonly]="true" [checked]="selected" [disableRipple]="true" [disabled]="deleted"
43+
[disableTransitions]="grid.disableTransitions" [aria-label]="rowCheckboxAriaLabel">
44+
</igx-checkbox>
45+
</div>
46+
</ng-template>
5447

55-
<ng-template #rowSelectorBaseTemplate>
56-
<div class="igx-grid__cbx-padding">
57-
<igx-checkbox
58-
[tabindex]="-1"
59-
[readonly]="true"
60-
[checked]="selected"
61-
[disableRipple]="true"
62-
[disabled]="deleted"
63-
[disableTransitions]="grid.disableTransitions"
64-
[aria-label]="rowCheckboxAriaLabel">
65-
</igx-checkbox>
66-
</div>
67-
</ng-template>
48+
<ng-template #pinnedCellsTemplate let-col>
49+
<igx-grid-cell *ngFor="let col of pinnedColumns | igxNotGrouped"
50+
class="igx-grid__td igx-grid__td--fw igx-grid__td--pinned"
51+
[class.igx-grid__td--edited]="rowID | transactionState:col.field:grid.rowEditable:grid.transactions:grid.pipeTrigger:grid.gridAPI.crudService.cell:grid.gridAPI.crudService.row"
52+
[attr.aria-describedby]="gridID + '_' + col.field | igxStringReplace:'.':'_'"
53+
[class.igx-grid__td--number]="col.dataType === 'number' || col.dataType === 'percent' || col.dataType === 'currency'"
54+
[ngClass]="col.cellClasses | igxCellStyleClasses:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
55+
[ngStyle]="col.cellStyles | igxCellStyles:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
56+
[editMode]="col.editable && this.grid.crudService.targetInEdit(index, col.index)" [column]="col"
57+
[formatter]="col.formatter" [intRow]="this" [active]="isCellActive(col.visibleIndex)"
58+
[firstPinned]="col.isFirstPinned" [lastPinned]="col.isLastPinned" [style.min-height.px]="cellHeight"
59+
[rowData]="rowData" [style.min-width]="col.width" [style.max-width]="col.width" [style.flex-basis]="col.width"
60+
[style.left]="col.rightPinnedOffset" [width]="col.getCellWidth()" [visibleColumnIndex]="col.visibleIndex"
61+
[value]="rowData | dataMapper:col.field:grid.pipeTrigger:rowData[col.field]:col.hasNestedPath"
62+
[cellTemplate]="col.bodyTemplate" [lastSearchInfo]="grid.lastSearchInfo"
63+
[cellSelectionMode]="grid.cellSelection" [displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)">
64+
</igx-grid-cell>
65+
</ng-template>
6866

69-
<ng-template #pinnedCellsTemplate let-col>
70-
<igx-grid-cell *ngFor="let col of pinnedColumns | igxNotGrouped"
71-
class="igx-grid__td igx-grid__td--fw igx-grid__td--pinned"
72-
[class.igx-grid__td--edited]="rowID | transactionState:col.field:grid.rowEditable:grid.transactions:grid.pipeTrigger:grid.gridAPI.crudService.cell:grid.gridAPI.crudService.row"
73-
[attr.aria-describedby]="gridID + '_' + col.field | igxStringReplace:'.':'_'"
74-
[class.igx-grid__td--number]="col.dataType === 'number' || col.dataType === 'percent' || col.dataType === 'currency'"
75-
[ngClass]="col.cellClasses | igxCellStyleClasses:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
76-
[ngStyle]="col.cellStyles | igxCellStyles:rowData[col.field]:rowData:col.field:viewIndex:grid.pipeTrigger"
77-
[editMode]="col.editable && this.grid.crudService.targetInEdit(index, col.index)"
78-
[column]="col"
79-
[formatter]="col.formatter"
80-
[intRow]="this"
81-
[active]="isCellActive(col.visibleIndex)"
82-
[firstPinned]="col.isFirstPinned"
83-
[lastPinned]="col.isLastPinned"
84-
[style.min-height.px]="cellHeight"
85-
[rowData]="rowData"
86-
[style.min-width]="col.width"
87-
[style.max-width]="col.width"
88-
[style.flex-basis]="col.width"
89-
[style.left]="col.rightPinnedOffset"
90-
[width]="col.getCellWidth()"
91-
[visibleColumnIndex]="col.visibleIndex"
92-
[value]="rowData | dataMapper:col.field:grid.pipeTrigger:rowData[col.field]:col.hasNestedPath"
93-
[cellTemplate]="col.bodyTemplate"
94-
[lastSearchInfo]="grid.lastSearchInfo"
95-
[cellSelectionMode]="grid.cellSelection"
96-
[displayPinnedChip]="shouldDisplayPinnedChip(col.visibleIndex)">
97-
</igx-grid-cell>
98-
</ng-template>
9967

68+
<ng-template #headerTemplate let-column>
69+
<div class="igx-grid__tr--header" (click)="this.selectPivotRow(column, $event)">
70+
<igx-icon [attr.draggable]="false" (click)="grid.toggleRow(getRowDimensionKey(column))"
71+
style='cursor: pointer;'>
72+
{{ getExpandState(column) ? 'expand_more' : 'expand_less'}}</igx-icon>
73+
{{column.header}}
74+
</div>
75+
</ng-template>
10076

101-
<ng-template #headerTemplate let-column>
102-
<div class="igx-grid__tr--header">
103-
<igx-icon [attr.draggable]="false"
104-
(click)="grid.toggleRow(getRowDimensionKey(column))"
105-
style='cursor: pointer;'>
106-
{{ getExpandState(column) ? 'expand_more' : 'expand_less'}}</igx-icon>
107-
{{column.header}}
108-
</div>
109-
</ng-template>
77+
<ng-template #headerTemplateGray let-column>
78+
<div class="igx-grid__tr--header" style='color:gray' (click)="this.selectPivotRow(column, $event)">
79+
{{column.header}}
80+
</div>
81+
</ng-template>
11082

111-
<ng-template #headerTemplateGray let-column>
112-
<div class="igx-grid__tr--header" style='color:gray'>
113-
{{column.header}}
114-
</div>
115-
</ng-template>
83+
<ng-template #headerDefaultTemplate let-column>
84+
<div class="igx-grid__tr--header" (click)="this.selectPivotRow(column, $event)">
85+
{{column.header}}
86+
</div>
87+
</ng-template>

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

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
ComponentFactoryResolver,
66
ElementRef,
77
forwardRef,
8+
HostBinding,
9+
Input,
810
OnChanges,
911
SimpleChanges,
1012
TemplateRef,
@@ -31,36 +33,57 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
3133
/**
3234
* @hidden @internal
3335
*/
34-
@ViewChild('headerTemplate', { read: TemplateRef, static: true })
35-
public headerTemplate: TemplateRef<any>;
36+
@ViewChild('headerTemplate', { read: TemplateRef, static: true })
37+
public headerTemplate: TemplateRef<any>;
3638

3739
/**
3840
* @hidden @internal
3941
*/
40-
@ViewChild('headerTemplateGray', { read: TemplateRef, static: true })
41-
public headerTemplateGray: TemplateRef<any>;
42+
@ViewChild('headerTemplateGray', { read: TemplateRef, static: true })
43+
public headerTemplateGray: TemplateRef<any>;
44+
45+
/**
46+
* @hidden @internal
47+
*/
48+
@ViewChild('headerDefaultTemplate', { read: TemplateRef, static: true })
49+
public headerDefaultTemplate: TemplateRef<any>;
4250

4351
public rowDimensionData: IPivotDimensionData[] = [];
4452

4553
public get rowDimension() {
4654
return this.rowDimensionData?.map(x => x.column);
4755
}
4856

57+
/**
58+
* @hidden
59+
*/
60+
@Input()
61+
@HostBinding('attr.aria-selected')
62+
public get selected(): boolean {
63+
let isSelected = false;
64+
this.rowDimensionData.forEach(x => {
65+
if (this.selectionService.isPivotRowSelected(this.getRowDimensionKey(x.column))) {
66+
isSelected = true;
67+
}
68+
});
69+
return isSelected;
70+
}
71+
4972
constructor(
5073
public gridAPI: GridBaseAPIService<IgxPivotGridComponent>,
5174
public selectionService: IgxGridSelectionService,
5275
public element: ElementRef<HTMLElement>,
5376
public cdr: ChangeDetectorRef,
5477
protected resolver: ComponentFactoryResolver,
5578
protected viewRef: ViewContainerRef
56-
){
79+
) {
5780
super(gridAPI, selectionService, element, cdr);
5881
}
5982
/**
6083
* @hidden
6184
* @internal
6285
*/
63-
public get viewIndex(): number {
86+
public get viewIndex(): number {
6487
return this.index;
6588
}
6689

@@ -69,9 +92,9 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
6992
* @internal
7093
*/
7194
public getRowDimensionKey(col: IgxColumnComponent) {
72-
const dimData = this.rowDimensionData.find(x => x.column === col);
73-
const key = PivotUtil.getRecordKey(this.rowData, dimData.dimension, dimData.prevDimensions);
74-
return key;
95+
const dimData = this.rowDimensionData.find(x => x.column.field === col.field);
96+
const key = PivotUtil.getRecordKey(this.rowData, dimData.dimension, dimData.prevDimensions);
97+
return key;
7598
}
7699

77100
public getExpandState(col: IgxColumnComponent) {
@@ -102,14 +125,27 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
102125
return this.grid.pivotConfiguration.values.find(v => v.member === measureName)?.styles;
103126
}
104127

128+
/**
129+
* @hidden @internal
130+
*/
131+
public selectPivotRow(col: any, event?: any) {
132+
event.stopPropagation();
133+
const key = this.getRowDimensionKey(col);
134+
if (this.grid.selectionService.isRowSelected(key)) {
135+
this.grid.selectionService.deselectRow(key, event);
136+
} else {
137+
this.grid.selectionService.selectRowById(key, true, event);
138+
}
139+
}
140+
105141
protected extractFromDimensions(rowDimConfig: IPivotDimension[], level: number) {
106142
let dimIndex = 0;
107143
let currentLvl = 0;
108144
currentLvl += level;
109145
const prev = [];
110146
for (const dim of rowDimConfig) {
111147
const dimData = PivotUtil.getDimensionLevel(dim, this.rowData,
112-
{ aggregations: 'aggregations', records: 'records', children: 'children', level: 'level'});
148+
{ aggregations: 'aggregations', records: 'records', children: 'children', level: 'level' });
113149
dimIndex += dimData.level;
114150
currentLvl += dimData.level;
115151
const column = this.extractFromDimension(dimData.dimension, dimIndex, currentLvl);
@@ -140,6 +176,8 @@ export class IgxPivotRowComponent extends IgxRowDirective<IgxPivotGridComponent>
140176
ref.instance.headerTemplate = this.headerTemplate;
141177
} else if (lvl < PivotUtil.getTotalLvl(this.rowData)) {
142178
ref.instance.headerTemplate = this.headerTemplateGray;
179+
} else {
180+
ref.instance.headerTemplate = this.headerDefaultTemplate;
143181
}
144182
return ref.instance;
145183
}

projects/igniteui-angular/src/lib/grids/selection/selection.service.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ export class IgxGridSelectionService {
444444

445445
/** Select the specified row and emit event. */
446446
public selectRowById(rowID, clearPrevSelection?, event?): void {
447-
if (!this.grid.isRowSelectable || this.isRowDeleted(rowID)) {
447+
if (!(this.grid.isRowSelectable || this.grid.isPivot) || this.isRowDeleted(rowID)) {
448448
return;
449449
}
450450
clearPrevSelection = !this.grid.isMultiRowSelectionEnabled || clearPrevSelection;
@@ -489,6 +489,17 @@ export class IgxGridSelectionService {
489489
return this.rowSelection.size > 0 && this.rowSelection.has(rowID);
490490
}
491491

492+
public isPivotRowSelected(rowID): boolean {
493+
let contains = false;
494+
this.rowSelection.forEach(x => {
495+
if (rowID.includes(x)) {
496+
contains = true;
497+
return;
498+
}
499+
});
500+
return this.rowSelection.size > 0 && contains;
501+
}
502+
492503
public isRowInIndeterminateState(rowID): boolean {
493504
return this.indeterminateRows.size > 0 && this.indeterminateRows.has(rowID);
494505
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<div class="sample-column">
2-
<igx-pivot-grid #grid1 [data]="origData" [width]="'100%'" [height]="'100%'" [pivotConfiguration]="pivotConfigHierarchy">
2+
<button igxButton (click)="test()">TEST</button>
3+
<igx-pivot-grid #grid1 [data]="origData" [width]="'100%'" [height]="'100%'" [pivotConfiguration]="pivotConfigHierarchy"
4+
[rowSelection]="'single'"
5+
[columnSelection]="'single'"
6+
[cellSelection]="'single'">
37
</igx-pivot-grid>
48
</div>

0 commit comments

Comments
 (0)