Skip to content

Commit 88071bb

Browse files
DiyanDimitrovgedinakova
authored andcommitted
feat: Adding IgxTreeGridComponent (#2852)
1 parent a407f26 commit 88071bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+6894
-286
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ All notable changes for each version of this project will be documented in this
2424
- Added support for custom SVG icons. Register the SVG icons with the `IgxIconService` and use `IgxIconComponent`'s `name` and `fontSet` properties to visualize the icon.
2525
- Transaction Provider - `TransactionService` is an injectable middleware that a component can use to accumulate changes without affecting the underlying data. The provider exposes API to access, manipulate changes (undo and redo) and discard or commit all to the data.
2626
For more detailed information, see the [README](https://github.com/IgniteUI/igniteui-angular/blob/master/projects/igniteui-angular/src/lib/services/transaction/README.md).
27+
- `igxTreeGrid`:
28+
- New `IgxTreeGridComponent` added.
29+
- The `igxTreeGrid` is used to display and manipulate hierarchical data with consistent schema, formatted as a table and provides a line of advanced features such as sorting, filtering, editing, column pinning, column moving, column hiding, paging and others.
30+
- The `igxTreeGrid` provides two ways of defining the relations among our data objects - by using a **child collection** for every data object or by using **primary and foreign keys** for every data object.
31+
- For more details on using the `igxTreeGrid`, take a look at the [official documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/treegrid.html).
2732
- `igxGrid`:
2833
- **Breaking change** `onGroupingDone` - The array of `ISortingExpression` can now be accessed through the `expressions` event property. Two new properties have been added to the event arguments - `groupedColumns` and `ungroupedColumns`. They provide references to arrays of `IgxColumnComponent` that hold the columns which have changed their state because of the **last** grouping/ungrouping operation.
2934

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

+25
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,26 @@
151151
@extend %grid-cell-display !optional;
152152
}
153153

154+
@include e(td, $m: tree-cell ) {
155+
@extend %igx-grid__td--tree-cell !optional;
156+
}
157+
154158
@include e(td, $m: selected) {
155159
@extend %grid-cell--selected !optional;
156160
}
157161

162+
@include e(tr, $mods: (selected, filtered)) {
163+
@extend %grid-row--selected--filtered !optional;
164+
}
165+
166+
@include e(tr, $m: filtered) {
167+
@extend %igx-grid-row--filtered !optional;
168+
}
169+
170+
@include e(tree-grouping-indicator) {
171+
@extend %igx-grid__tree-grouping-indicator !optional;
172+
}
173+
158174
@include e(td, $m: edited) {
159175
@extend %igx-grid__td--edited !optional;
160176
}
@@ -283,6 +299,9 @@
283299
@include e(group-row, $m: padding-level-#{$i}) {
284300
@extend %igx-grid__group-row--padding-level-#{$i} !optional;
285301
}
302+
@include e(tree-cell, $m: padding-level-#{$i}) {
303+
@extend %igx-grid__tree-cell--padding-level-#{$i} !optional;
304+
}
286305
}
287306

288307
@include m(cosy) {
@@ -347,6 +366,9 @@
347366
@include e(group-row, $m: padding-level-#{$i}) {
348367
@extend %igx-grid__group-row--padding-cosy-level-#{$i} !optional;
349368
}
369+
@include e(tree-cell, $m: padding-level-#{$i}) {
370+
@extend %igx-grid__tree-cell-cosy--padding-level-#{$i} !optional;
371+
}
350372
}
351373
}
352374

@@ -412,6 +434,9 @@
412434
@include e(group-row, $m: padding-level-#{$i}) {
413435
@extend %igx-grid__group-row-compact--padding-level-#{$i} !optional;
414436
}
437+
@include e(tree-cell, $m: padding-level-#{$i}) {
438+
@extend %igx-grid__tree-cell-compact--padding-level-#{$i} !optional;
439+
}
415440
}
416441
}
417442
}

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

+82-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
/// @param {Color} $drop-area-icon-color [rgba(0, 0, 0, .38)] - The drop area icon color.
5353
/// @param {Color} $drop-area-background [rgba(0, 0, 0, .04)] - The drop area background color.
5454
/// @param {Color} $drop-area-on-drop-background [rgba(0, 0, 0, .08)] - The drop area background on drop color.
55+
/// @param {Color} $treegrid-filtered-text-color [rgba(0, 0, 0, .04)] - grouping row background color on focus.
5556
///
5657
/// @requires igx-color
5758
/// @requires igx-contrast-color
@@ -123,7 +124,9 @@
123124
$drop-area-text-color: null,
124125
$drop-area-icon-color: null,
125126
$drop-area-background: null,
126-
$drop-area-on-drop-background: null
127+
$drop-area-on-drop-background: null,
128+
129+
$treegrid-filtered-text-color: null
127130
) {
128131
$default-theme: (
129132
name: 'igx-grid',
@@ -190,6 +193,7 @@
190193

191194
group-row-background: hexrgba(igx-color($palette, 'grays', 100)),
192195
group-row-selected-background: hexrgba(igx-color($palette, 'grays', 200)),
196+
treegrid-filtered-text-color: igx-color($palette, grays, 500)
193197
);
194198

195199
@if $preset {
@@ -379,6 +383,7 @@
379383
drop-area-icon-color: $drop-area-icon-color,
380384
drop-area-on-drop-background: $drop-area-on-drop-background,
381385
drop-area-background: $drop-area-background,
386+
treegrid-filtered-text-color: $treegrid-filtered-text-color
382387
));
383388
}
384389

@@ -648,6 +653,14 @@
648653
background-color: --var($theme, 'row-selected-background');
649654
color: --var($theme, 'row-selected-text-color');
650655
}
656+
657+
%igx-grid__tree-grouping-indicator {
658+
color: rgba(text-contrast(--var($theme, 'row-selected-background')), .5);
659+
660+
&:hover {
661+
color: text-contrast(--var($theme, 'row-selected-background'));
662+
}
663+
}
651664
}
652665

653666
%igx-grid__tr--edit {
@@ -693,6 +706,50 @@
693706
background: --var($theme, 'header-background') !important;
694707
}
695708

709+
%igx-grid-row--filtered {
710+
%grid-cell-text {
711+
color: --var($theme, 'treegrid-filtered-text-color');
712+
}
713+
}
714+
715+
%grid-row--selected--filtered {
716+
%grid-cell-text {
717+
color: rgba(text-contrast(--var($theme, 'row-selected-background')), .5);
718+
}
719+
720+
%igx-grid__tree-grouping-indicator {
721+
color: rgba(text-contrast(--var($theme, 'row-selected-background')), .5);
722+
723+
&:hover {
724+
color: text-contrast(--var($theme, 'row-selected-background'));
725+
}
726+
}
727+
728+
%grid-cell--selected {
729+
%igx-grid__tree-grouping-indicator {
730+
color: rgba(text-contrast(--var($theme, 'cell-selected-background')), .5);
731+
732+
&:hover {
733+
color: text-contrast(--var($theme, 'cell-selected-background'));
734+
}
735+
}
736+
}
737+
}
738+
739+
%igx-grid__tree-grouping-indicator {
740+
display: flex;
741+
align-items: center;
742+
justify-content: center;
743+
margin-right: rem(8);
744+
cursor: pointer;
745+
746+
color: --var($theme, 'expand-icon-color');
747+
748+
&:hover {
749+
color: --var($theme, 'expand-icon-hover-color')
750+
}
751+
}
752+
696753
%grid-cell-display {
697754
position: relative;
698755
display: flex;
@@ -706,6 +763,10 @@
706763
text-align: left;
707764
}
708765

766+
%igx-grid__td--tree-cell {
767+
overflow: hidden;
768+
}
769+
709770
%grid-cell-text {
710771
@include ellipsis();
711772
}
@@ -728,6 +789,14 @@
728789
color: --var($theme, 'cell-selected-text-color');
729790
background-color: --var($theme, 'cell-selected-background') !important;
730791
border-bottom: 0;
792+
793+
%igx-grid__tree-grouping-indicator {
794+
color: rgba(text-contrast(--var($theme, 'cell-selected-background')), .5);
795+
796+
&:hover {
797+
color: text-contrast(--var($theme, 'cell-selected-background'));
798+
}
799+
}
731800
}
732801

733802
%igx-grid__td--edited {
@@ -1367,6 +1436,10 @@
13671436
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'comfortable')};
13681437
}
13691438

1439+
%igx-grid__tree-cell--padding-level-#{$i} {
1440+
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'comfortable')};
1441+
}
1442+
13701443
// COSY
13711444
%igx-grid__row-indentation-cosy--level-#{$i} {
13721445
padding-left: calc(#{$i*map-get($grid-grouping-indicator-padding, 'cosy')} + #{$indicator-icon-width});
@@ -1376,6 +1449,10 @@
13761449
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'cosy')};
13771450
}
13781451

1452+
%igx-grid__tree-cell-cosy--padding-level-#{$i} {
1453+
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'cosy')};
1454+
}
1455+
13791456
// COMPACT
13801457
%igx-grid__row-indentation-compact--level-#{$i} {
13811458
padding-left: calc(#{$i*map-get($grid-grouping-indicator-padding, 'compact')} + #{$indicator-icon-width});
@@ -1384,6 +1461,10 @@
13841461
%igx-grid__group-row-compact--padding-level-#{$i} {
13851462
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'compact')};
13861463
}
1464+
1465+
%igx-grid__tree-cell-compact--padding-level-#{$i} {
1466+
padding-left: #{$i*map-get($grid-grouping-indicator-padding, 'compact')};
1467+
}
13871468
}
13881469

13891470
%igx-grid__outlet {

projects/igniteui-angular/src/lib/data-operations/data-util.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { FilteringStrategy, IFilteringStrategy } from './filtering-strategy';
1111

1212
import { ISortingExpression, SortingDirection } from './sorting-expression.interface';
1313
import { ISortingState, SortingStateDefaults } from './sorting-state.interface';
14-
import { ISortingStrategy, SortingStrategy, IGroupByResult } from './sorting-strategy';
14+
import { ISortingStrategy, SortingStrategy, IGroupByResult, TreeGridSortingStrategy } from './sorting-strategy';
1515

1616
import { IPagingState, PagingError } from './paging-state.interface';
1717

@@ -20,6 +20,7 @@ import { IGroupByExpandState, IGroupByKey } from './groupby-expand-state.interfa
2020
import { IGroupByRecord } from './groupby-record.interface';
2121
import { IGroupingState } from './groupby-state.interface';
2222
import { Transaction, TransactionType } from '../services';
23+
import { ITreeGridRecord } from '../grids/tree-grid/tree-grid.interfaces';
2324

2425
export enum DataType {
2526
String = 'string',
@@ -52,6 +53,37 @@ export class DataUtil {
5253
// apply default settings for each sorting expression(if not set)
5354
return state.strategy.sort(data, state.expressions);
5455
}
56+
57+
public static hierarchicalSort(hierarchicalData: ITreeGridRecord[], state: ISortingState, parent: ITreeGridRecord): ITreeGridRecord[] {
58+
state.strategy = new TreeGridSortingStrategy();
59+
let res: ITreeGridRecord[] = [];
60+
61+
hierarchicalData.forEach((hr: ITreeGridRecord) => {
62+
const rec: ITreeGridRecord = DataUtil.cloneTreeGridRecord(hr);
63+
rec.parent = parent;
64+
if (rec.children) {
65+
rec.children = DataUtil.hierarchicalSort(rec.children, state, rec);
66+
}
67+
res.push(rec);
68+
});
69+
70+
res = DataUtil.sort(res, state);
71+
72+
return res;
73+
}
74+
75+
public static cloneTreeGridRecord(hierarchicalRecord: ITreeGridRecord) {
76+
const rec: ITreeGridRecord = {
77+
rowID: hierarchicalRecord.rowID,
78+
data: hierarchicalRecord.data,
79+
children: hierarchicalRecord.children,
80+
isFilteredOutParent: hierarchicalRecord.isFilteredOutParent,
81+
level: hierarchicalRecord.level,
82+
expanded: hierarchicalRecord.expanded
83+
};
84+
return rec;
85+
}
86+
5587
public static group<T>(data: T[], state: IGroupingState): IGroupByResult {
5688
// set defaults
5789
DataUtil.mergeDefaultProperties(state, SortingStateDefaults);

projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts

+28-18
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,14 @@ export interface IFilteringStrategy {
88
filter(data: any[], expressionsTree: IFilteringExpressionsTree): any[];
99
}
1010

11-
export class FilteringStrategy implements IFilteringStrategy {
12-
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree): T[] {
13-
let i;
14-
let rec;
15-
const len = data.length;
16-
const res: T[] = [];
17-
if (!expressionsTree || !expressionsTree.filteringOperands || expressionsTree.filteringOperands.length === 0 || !len) {
18-
return data;
19-
}
20-
for (i = 0; i < len; i++) {
21-
rec = data[i];
22-
if (this.matchRecord(rec, expressionsTree)) {
23-
res.push(rec);
24-
}
25-
}
26-
return res;
27-
}
11+
export abstract class BaseFilteringStrategy implements IFilteringStrategy {
12+
public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree): any[];
13+
14+
protected abstract getFieldValue(rec: object, fieldName: string): any;
2815

2916
public findMatchByExpression(rec: object, expr: IFilteringExpression): boolean {
3017
const cond = expr.condition;
31-
const val = rec[expr.fieldName];
18+
const val = this.getFieldValue(rec, expr.fieldName);
3219
return cond.logic(val, expr.searchVal, expr.ignoreCase);
3320
}
3421

@@ -64,3 +51,26 @@ export class FilteringStrategy implements IFilteringStrategy {
6451
return true;
6552
}
6653
}
54+
55+
export class FilteringStrategy extends BaseFilteringStrategy {
56+
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree): T[] {
57+
let i;
58+
let rec;
59+
const len = data.length;
60+
const res: T[] = [];
61+
if (!expressionsTree || !expressionsTree.filteringOperands || expressionsTree.filteringOperands.length === 0 || !len) {
62+
return data;
63+
}
64+
for (i = 0; i < len; i++) {
65+
rec = data[i];
66+
if (this.matchRecord(rec, expressionsTree)) {
67+
res.push(rec);
68+
}
69+
}
70+
return res;
71+
}
72+
73+
protected getFieldValue(rec: object, fieldName: string): any {
74+
return rec[fieldName];
75+
}
76+
}

0 commit comments

Comments
 (0)