Skip to content

Commit b36b75e

Browse files
authored
Merge pull request #6848 from IgniteUI/mkirova/row-pinning-base
Row pinning base
2 parents 3ac5cdc + ee478a2 commit b36b75e

20 files changed

+738
-15
lines changed

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-component.scss

+24
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@
7878
@extend %grid-tbody-scrollbar !optional;
7979
}
8080

81+
@include e(tbody-scrollbar-main) {
82+
@extend %grid-tbody-scrollbar-main !optional;
83+
}
84+
85+
@include e(tbody-scrollbar-start) {
86+
@extend %grid-tbody-scrollbar-start !optional;
87+
}
88+
89+
@include e(tbody-scrollbar-end) {
90+
@extend %grid-tbody-scrollbar-end !optional;
91+
}
92+
8193
@include e(scroll) {
8294
@extend %grid-scroll !optional;
8395
}
@@ -301,6 +313,18 @@
301313
@extend %igx-grid__tr--expanded !optional;
302314
}
303315

316+
@include e(tr, $m: pinned) {
317+
@extend %igx-grid__tr--pinned !optional;
318+
}
319+
320+
@include e(tr, $m: pinned-top) {
321+
@extend %igx-grid__tr--pinned-top !optional;
322+
}
323+
324+
@include e(tr, $m: pinned-bottom) {
325+
@extend %igx-grid__tr--pinned-bottom !optional;
326+
}
327+
304328
@include e(tree-grouping-indicator) {
305329
@extend %igx-grid__tree-grouping-indicator !optional;
306330
}

projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss

+26
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,18 @@
943943
position: relative;
944944
}
945945

946+
%grid-tbody-scrollbar-start {
947+
background: --var($theme, 'header-background');
948+
}
949+
950+
%grid-tbody-scrollbar-main {
951+
position: relative;
952+
}
953+
954+
%grid-tbody-scrollbar-end {
955+
background: --var($theme, 'header-background');
956+
}
957+
946958
%grid-scroll-start {
947959
background: --var($theme, 'header-background');
948960
}
@@ -1062,6 +1074,20 @@
10621074
border-bottom: none;
10631075
}
10641076

1077+
%igx-grid__tr--pinned {
1078+
position: relative;
1079+
background: inherit;
1080+
z-index: 10000;
1081+
}
1082+
1083+
%igx-grid__tr--pinned-top {
1084+
border-bottom: map-get($cell-pin, 'style') map-get($cell-pin, 'color') !important;
1085+
}
1086+
1087+
%igx-grid__tr--pinned-bottom {
1088+
border-top: map-get($cell-pin, 'style') map-get($cell-pin, 'color') !important;
1089+
}
1090+
10651091
%igx-grid__tr--edit {
10661092
border-bottom: 1px solid --var($theme, 'edit-mode-color');
10671093
position: relative;

projects/igniteui-angular/src/lib/grids/common/events.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IBaseEventArgs, CancelableEventArgs } from '../../core/utils';
22
import { IgxBaseExporter, IgxExporterOptionsBase } from '../../services';
33
import { GridKeydownTargetType } from './enums';
44
import { IgxDragDirective } from '../../directives/drag-drop/drag-drop.directive';
5-
import { IGridDataBindable } from './grid.interface';
5+
import { IGridDataBindable, GridType } from './grid.interface';
66
import { IgxGridCellComponent } from '../cell.component';
77
import { IgxColumnComponent } from '../columns/column.component';
88
import { IgxGridBaseDirective } from '../grid-base.directive';
@@ -118,3 +118,21 @@ export interface IRowToggleEventArgs extends IBaseEventArgs {
118118
event?: Event;
119119
cancel: boolean;
120120
}
121+
122+
/**
123+
* Event emitted when a row's pin state changes.
124+
*/
125+
export interface IPinRowEventArgs extends IBaseEventArgs {
126+
/** The row component instance, that was pinned/unpinned.
127+
* May be undefined if row does not exist in the current visible data.
128+
*/
129+
readonly row?: IgxRowDirective<IgxGridBaseDirective & GridType>;
130+
/** The ID of the row, that was pinned/unpinned.
131+
* ID is either the primaryKey value or the data record instance.
132+
*/
133+
readonly rowID: any;
134+
/** The index at which to pin the row in the pinned rows collection. */
135+
insertAtIndex?: number;
136+
/** Whether or noy the row is pinned or unpinned. */
137+
readonly isPinned: boolean;
138+
}

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

+132-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ import {
109109
GridSummaryPosition,
110110
GridSummaryCalculationMode,
111111
FilterMode,
112-
ColumnPinningPosition
112+
ColumnPinningPosition,
113+
RowPinningPosition
113114
} from './common/enums';
114115
import {
115116
IGridCellEventArgs,
@@ -129,7 +130,8 @@ import {
129130
IGridToolbarExportEventArgs,
130131
ISearchInfo,
131132
ICellPosition,
132-
IRowToggleEventArgs
133+
IRowToggleEventArgs,
134+
IPinRowEventArgs
133135
} from './common/events';
134136
import { IgxAdvancedFilteringDialogComponent } from './filtering/advanced-filtering/advanced-filtering-dialog.component';
135137
import { GridType, IPinningConfig } from './common/grid.interface';
@@ -1374,6 +1376,16 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
13741376
@Output()
13751377
public onRowToggle = new EventEmitter<IRowToggleEventArgs>();
13761378

1379+
/**
1380+
* Emitted when the pinned state of a row is changed.
1381+
* @example
1382+
* ```html
1383+
* <igx-grid [data]="employeeData" (onRowPinning)="rowPin($event)" [autoGenerate]="true"></igx-grid>
1384+
* ```
1385+
*/
1386+
@Output()
1387+
public onRowPinning = new EventEmitter<IPinRowEventArgs>();
1388+
13771389
/**
13781390
* @hidden @internal
13791391
*/
@@ -1612,6 +1624,14 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
16121624
return this.pinning.columns !== ColumnPinningPosition.End;
16131625
}
16141626

1627+
/**
1628+
* @hidden
1629+
* @internal
1630+
*/
1631+
get isRowPinningToTop() {
1632+
return this.pinning.rows !== RowPinningPosition.Bottom;
1633+
}
1634+
16151635
/**
16161636
* @hidden
16171637
* @internal
@@ -1704,6 +1724,12 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
17041724
@ViewChild('tbody', { static: true })
17051725
public tbody: ElementRef;
17061726

1727+
/**
1728+
* @hidden @internal
1729+
*/
1730+
@ViewChild('pinContainer', { static: false })
1731+
public pinContainer: ElementRef;
1732+
17071733
/**
17081734
* @hidden @internal
17091735
*/
@@ -2434,6 +2460,15 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
24342460
protected _columnPinning = false;
24352461

24362462

2463+
/**
2464+
* @hidden
2465+
*/
2466+
public get pinnedRecords() {
2467+
return this._pinnedRecords;
2468+
}
2469+
2470+
protected _pinnedRecords = [];
2471+
24372472
/**
24382473
* @hidden
24392474
*/
@@ -3255,6 +3290,17 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
32553290
return this._pinnedVisible;
32563291
}
32573292

3293+
/**
3294+
* Gets an array of the pinned `IgxRowComponent`s.
3295+
* @example
3296+
* ```typescript
3297+
* const pinnedRow = this.grid.pinnedRows;
3298+
* ```
3299+
*/
3300+
get pinnedRows(): IgxGridRowComponent[] {
3301+
return this.rowList.filter(x => x.pinned);
3302+
}
3303+
32583304
/**
32593305
* Gets an array of unpinned `IgxColumnComponent`s.
32603306
* @example
@@ -3988,6 +4034,87 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
39884034
return col.unpin(index);
39894035
}
39904036

4037+
/**
4038+
* Pin the row by its id.
4039+
* @remarks
4040+
* ID is either the primaryKey value or the data record instance.
4041+
* @example
4042+
* ```typescript
4043+
* this.grid.pinRow(rowID);
4044+
* ```
4045+
* @param rowID The row id - primaryKey value or the data record instance.
4046+
* @param index The index at which to insert the row in the pinned collection.
4047+
*/
4048+
public pinRow(rowID: any, index?: number): boolean {
4049+
const rec = this.gridAPI.get_rec_by_id(rowID);
4050+
if (!rec || this.pinnedRecords.indexOf(rec) !== -1 || this.data.indexOf(rec) === -1) {
4051+
return false;
4052+
}
4053+
const row = this.gridAPI.get_row_by_key(rowID);
4054+
4055+
const eventArgs: IPinRowEventArgs = {
4056+
insertAtIndex: index,
4057+
isPinned: true,
4058+
rowID: rowID,
4059+
row: row
4060+
};
4061+
this.onRowPinning.emit(eventArgs);
4062+
4063+
this.pinnedRecords.splice(eventArgs.insertAtIndex || this.pinnedRecords.length, 0, rec);
4064+
this._pipeTrigger++;
4065+
if (this.gridAPI.grid) {
4066+
this.notifyChanges(true);
4067+
}
4068+
}
4069+
4070+
/**
4071+
* Unpin the row by its id.
4072+
* @remarks
4073+
* ID is either the primaryKey value or the data record instance.
4074+
* @example
4075+
* ```typescript
4076+
* this.grid.unpinRow(rowID);
4077+
* ```
4078+
* @param rowID The row id - primaryKey value or the data record instance.
4079+
*/
4080+
public unpinRow(rowID: any) {
4081+
const rec = this.gridAPI.get_rec_by_id(rowID);
4082+
const index = this.pinnedRecords.indexOf(rec);
4083+
if (index === -1 || !rec) {
4084+
return false;
4085+
}
4086+
const row = this.gridAPI.get_row_by_key(rowID);
4087+
const eventArgs: IPinRowEventArgs = {
4088+
isPinned: false,
4089+
rowID: rowID,
4090+
row: row
4091+
};
4092+
this.onRowPinning.emit(eventArgs);
4093+
this.pinnedRecords.splice(index, 1);
4094+
this._pipeTrigger++;
4095+
if (this.gridAPI.grid) {
4096+
this.cdr.detectChanges();
4097+
this.notifyChanges(true);
4098+
}
4099+
return true;
4100+
}
4101+
4102+
get pinnedRowHeight() {
4103+
const containerHeight = this.pinContainer ? this.pinContainer.nativeElement.offsetHeight : 0;
4104+
return this.pinnedRecords.length > 0 ? containerHeight : 0;
4105+
}
4106+
4107+
get totalHeight() {
4108+
return this.calcHeight ? this.calcHeight + this.pinnedRowHeight : this.calcHeight;
4109+
}
4110+
4111+
get pinnedBottom() {
4112+
const start = this.verticalScrollContainer.state.startIndex;
4113+
const end = this.verticalScrollContainer.state.startIndex + this.verticalScrollContainer.state.chunkSize - 1;
4114+
const bottom = this.verticalScrollContainer.getScrollForIndex(end, true) - this.verticalScrollContainer.getScrollForIndex(start);
4115+
return bottom;
4116+
}
4117+
39914118

39924119
/**
39934120
* Recalculates grid width/height dimensions.
@@ -4293,6 +4420,9 @@ export class IgxGridBaseDirective extends DisplayDensityBase implements
42934420
}
42944421

42954422
this.calcHeight = this._calculateGridBodyHeight();
4423+
if (this.pinnedRowHeight && this.calcHeight) {
4424+
this.calcHeight -= this.pinnedRowHeight;
4425+
}
42964426
}
42974427

42984428
/**

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

+26-4
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,31 @@
101101
<div igxGridBody (keydown.control.c)="copyHandlerIE()" (copy)="copyHandler($event)" class="igx-grid__tbody">
102102
<div class="igx-grid__tbody-content" role="rowgroup" (onDragStop)="selectionService.dragMode = $event" (scroll)='preventContainerScroll($event)'
103103
(onDragScroll)="dragScroll($event)" [igxGridDragSelect]="selectionService.dragMode"
104-
[style.height.px]='calcHeight' [style.width.px]='calcWidth || null' #tbody>
104+
[style.height.px]='totalHeight' [style.width.px]='calcWidth || null' #tbody>
105105
<span *ngIf="hasMovableColumns && draggedColumn && pinnedColumns.length <= 0"
106106
[igxColumnMovingDrop]="headerContainer" [attr.droppable]="true" id="left"
107107
class="igx-grid__scroll-on-drag-left"></span>
108108
<span *ngIf="hasMovableColumns && draggedColumn && pinnedColumns.length > 0"
109109
[igxColumnMovingDrop]="headerContainer" [attr.droppable]="true" id="left"
110110
class="igx-grid__scroll-on-drag-pinned" [style.left.px]="pinnedWidth"></span>
111+
<ng-template #pinnedRecordsTemplate>
112+
<ng-container *ngFor="let rowData of pinnedRecords
113+
| visibleColumns:hasVisibleColumns
114+
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger
115+
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
116+
| gridDetails:hasDetails:expansionStates:pipeTrigger; let rowIndex = index">
117+
<ng-container *ngTemplateOutlet="getRowTemplate(rowData); context: getContext(rowData, rowIndex, true)">
118+
</ng-container>
119+
</ng-container>
120+
</ng-template>
121+
<div #pinContainer *ngIf='pinnedRecords.length > 0 && isRowPinningToTop' class='igx-grid__tr--pinned igx-grid__tr--pinned-top'>
122+
<ng-container *ngTemplateOutlet="pinnedRecordsTemplate">
123+
</ng-container>
124+
</div>
111125
<ng-template igxGridFor let-rowData [igxGridForOf]="data
112126
| gridTransaction:id:pipeTrigger
113127
| visibleColumns:hasVisibleColumns
128+
| rowPinning:id:pipeTrigger
114129
| gridFiltering:filteringExpressionsTree:filterStrategy:advancedFilteringExpressionsTree:id:pipeTrigger:filteringPipeTrigger
115130
| gridSort:sortingExpressions:sortStrategy:id:pipeTrigger
116131
| gridGroupBy:groupingExpressions:groupingExpansionState:groupsExpanded:id:groupsRecords:pipeTrigger
@@ -131,6 +146,10 @@
131146
(onBeforeViewDetach)='viewDetachHandler($event)'>
132147
</ng-template>
133148
</ng-template>
149+
<div #pinContainer *ngIf='pinnedRecords.length > 0 && !isRowPinningToTop' class='igx-grid__tr--pinned igx-grid__tr--pinned-bottom' [style.bottom.px]='pinnedBottom'>
150+
<ng-container *ngTemplateOutlet="pinnedRecordsTemplate">
151+
</ng-container>
152+
</div>
134153
<ng-template #record_template let-rowIndex="index" let-rowData>
135154
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" #row>
136155
</igx-grid-row>
@@ -170,9 +189,12 @@
170189
</div>
171190
<span *ngIf="hasMovableColumns && draggedColumn" [igxColumnMovingDrop]="headerContainer" [attr.droppable]="true"
172191
id="right" class="igx-grid__scroll-on-drag-right"></span>
173-
<div [hidden]='!hasVerticalSroll()' class="igx-grid__tbody-scrollbar" [style.width.px]="scrollWidth"
174-
[style.height.px]='calcHeight'>
175-
<ng-template igxGridFor [igxGridForOf]='[]' #verticalScrollHolder></ng-template>
192+
<div [hidden]='!hasVerticalSroll()' class="igx-grid__tbody-scrollbar" [style.width.px]="scrollWidth">
193+
<div class="igx-grid__tbody-scrollbar-start" [style.height.px]=' isRowPinningToTop ? pinnedRowHeight : 0'></div>
194+
<div class="igx-grid__tbody-scrollbar-main" [style.height.px]='calcHeight'>
195+
<ng-template igxGridFor [igxGridForOf]='[]' #verticalScrollHolder></ng-template>
196+
</div>
197+
<div class="igx-grid__tbody-scrollbar-end" [style.height.px]='!isRowPinningToTop ? pinnedRowHeight : 0'></div>
176198
</div>
177199
</div>
178200

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

+5-1
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,11 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
826826
/**
827827
* @hidden @internal
828828
*/
829-
public getContext(rowData, rowIndex): any {
829+
public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
830+
if (pinned && !this.isRowPinningToTop) {
831+
rowIndex = rowIndex + this.dataView.length;
832+
}
833+
rowIndex = !pinned && this.isRowPinningToTop ? rowIndex + this.pinnedRecords.length : rowIndex;
830834
if (this.isDetailRecord(rowData)) {
831835
const cachedData = this.childDetailTemplates.get(rowData.detailsData);
832836
const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : this.data.indexOf(rowData.detailsData);

0 commit comments

Comments
 (0)