Skip to content

Cell merge POC 2 #16024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@
@extend %igx-grid__td--edited !optional;
}

@include e(td, $m: merged) {
@extend %igx-grid__td--merged !optional;
}

@include e(td, $m: editing) {
@extend %igx-grid__td--editing !optional;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,20 @@
color: var-get($theme, 'row-selected-text-color');
background: var-get($theme, 'row-selected-background');

&%grid-row--mrl {
background: transparent;
%grid-mrl-block {
color: var-get($theme, 'row-selected-text-color');
background: var-get($theme, 'row-selected-background');
%igx-grid__td--merged {
color: var-get($theme, 'row-selected-text-color');
background: var-get($theme, 'row-selected-background');
}
}
}



%grid-cell--selected,
%grid-cell--pinned-selected {
color: var-get($theme, 'cell-selected-within-text-color');
Expand Down Expand Up @@ -1872,6 +1886,11 @@
}
}

%igx-grid__td--merged {
z-index: 1;
grid-row: 1 / -1;
}

%igx-grid__tr--deleted {
%grid-cell-text {
font-style: italic;
Expand Down Expand Up @@ -2084,7 +2103,7 @@

.sort-icon {
color: var-get($theme, 'header-selected-text-color');

::after {
background: var-get($theme, 'header-selected-background');
}
Expand Down Expand Up @@ -2112,15 +2131,15 @@
&%igx-grid-th--sorted {
.sort-icon {
color: var-get($theme, 'header-selected-text-color');

> igx-icon {
color: inherit;
}

&:focus,
&:hover {
color: var-get($theme, 'header-selected-text-color');

> igx-icon {
color: inherit;
}
Expand Down Expand Up @@ -2177,14 +2196,14 @@
.sort-icon {
opacity: 1;
color: var-get($theme, 'sorted-header-icon-color');

> igx-icon {
color: inherit;
}

&:hover {
color: var-get($theme, 'sortable-header-icon-hover-color');

> igx-icon {
color: inherit;
}
Expand Down
19 changes: 18 additions & 1 deletion projects/igniteui-angular/src/lib/data-operations/data-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IGroupingState } from './groupby-state.interface';
import { mergeObjects } from '../core/utils';
import { Transaction, TransactionType, HierarchicalTransaction } from '../services/transaction/transaction';
import { getHierarchy, isHierarchyMatch } from './operations';
import { GridType } from '../grids/common/grid.interface';
import { ColumnType, GridType } from '../grids/common/grid.interface';
import { ITreeGridRecord } from '../grids/tree-grid/tree-grid.interfaces';
import { ISortingExpression } from './sorting-strategy';
import {
Expand All @@ -20,6 +20,7 @@ import {
} from '../grids/common/strategy';
import { DefaultDataCloneStrategy, IDataCloneStrategy } from '../data-operations/data-clone-strategy';
import { IGroupingExpression } from './grouping-expression.interface';
import { DefaultMergeStrategy, IGridMergeStrategy } from './merge-strategy';

/**
* @hidden
Expand All @@ -37,6 +38,13 @@ import { IGroupingExpression } from './grouping-expression.interface';
} as const;
export type DataType = (typeof DataType)[keyof typeof DataType];


export interface IMergeByResult {
rowSpan: number;
root?: any;
prev?: any;
}

/**
* @hidden
*/
Expand Down Expand Up @@ -90,6 +98,15 @@ export class DataUtil {
return grouping.groupBy(data, state, grid, groupsRecords, fullResult);
}

public static merge<T>(data: T[], columns: ColumnType[], strategy: IGridMergeStrategy = new DefaultMergeStrategy(), activeRowIndex = -1, grid: GridType = null,
): any[] {
let result = [];
for (const col of columns) {
strategy.merge(data, col.field, col.mergingComparer, result, activeRowIndex, grid);
}
return result;
}

public static page<T>(data: T[], state: IPagingState, dataLength?: number): T[] {
if (!state) {
return data;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { GridCellMergeMode, IMergeByResult } from 'igniteui-angular';
import type { KeyOfOrString } from '../core/types';
import { IBaseEventArgs } from '../core/utils';
import { ColumnType, GridType } from '../grids/common/grid.interface';


export interface IGridMergeStrategy {
/* blazorSuppress */
merge: (
data: any[],
field: string,
comparer: (prevRecord: any, currentRecord: any, field: string) => boolean,
result: any[],
activeRowIndex? : number,
grid?: GridType
) => any[];
}

export class DefaultMergeStrategy implements IGridMergeStrategy {
protected static _instance: DefaultMergeStrategy = null;

public static instance(): DefaultMergeStrategy {
return this._instance || (this._instance = new this());
}

/* blazorSuppress */
public merge(
data: any[],
field: string,
comparer: (prevRecord: any, record: any, field: string) => boolean = this.comparer,
result: any[],
activeRowIndex?: number,
grid?: GridType
) {
let prev = null;
let index = 0;
for (const rec of data) {

const recData = result[index];
// if this is active row or some special record type - add and skip merging
if (activeRowIndex === index || (grid && grid.isDetailRecord(rec) || grid.isGhostRecord(rec) || grid.isGroupByRecord(rec))) {
if(!recData) {
result.push(rec);
}
prev = null;
index++;
continue;
}
let recToUpdateData = recData ?? { recordRef: rec, cellMergeMeta: new Map<string, IMergeByResult>() };
recToUpdateData.cellMergeMeta.set(field, { rowSpan: 1 });
if (prev && comparer(prev.recordRef, recToUpdateData.recordRef, field)) {
const root = prev.cellMergeMeta.get(field)?.root ?? prev;
root.cellMergeMeta.get(field).rowSpan += 1;
recToUpdateData.cellMergeMeta.get(field).root = root;
}
prev = recToUpdateData;
if (!recData) {
result.push(recToUpdateData);
}
index++;
}
return result;
}

/* blazorSuppress */
public comparer(prevRecord: any, record: any, field: string): boolean {
const a = prevRecord[field];
const b = record[field];
const an = (a === null || a === undefined);
const bn = (b === null || b === undefined);
if (an) {
if (bn) {
return true;
}
return false;
} else if (bn) {
return false;
}
return a === b;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
return this._field;
}

/**
* Sets/gets whether to merge cells in this column.
* ```html
* <igx-column [merge]="true"></igx-column>
* ```
*
*/
@Input()
public merge = false;

/**
* @hidden @internal
Expand Down Expand Up @@ -1200,6 +1209,30 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
this._sortStrategy = classRef;
}

/* blazorSuppress */
/**
* Gets the function that compares values for merging.
* ```typescript
* let mergingComparer = this.column.mergingComparer'
* ```
*/
@Input()
public get mergingComparer(): (prevRecord: any, record: any, field: string) => boolean {
return this._mergingComparer;
}

/* blazorSuppress */
/**
* Sets a custom function to compare values for merging.
* ```typescript
* this.column.mergingComparer = (prevRecord: any, record: any, field: string) => { return prevRecord[field] === record[field]; }
* ```
*/
public set mergingComparer(funcRef: (prevRecord: any, record: any, field: string) => boolean) {
this._mergingComparer = funcRef;
}


/* blazorSuppress */
/**
* Gets the function that compares values for grouping.
Expand Down Expand Up @@ -1840,6 +1873,8 @@ export class IgxColumnComponent implements AfterContentInit, OnDestroy, ColumnTy
* @hidden
*/
protected _groupingComparer: (a: any, b: any, currRec?: any, groupRec?: any) => number;

protected _mergingComparer: (prevRecord: any, record: any, field: string) => boolean;
/**
* @hidden
*/
Expand Down
13 changes: 13 additions & 0 deletions projects/igniteui-angular/src/lib/grids/common/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ export const GridSelectionMode = {
} as const;
export type GridSelectionMode = (typeof GridSelectionMode)[keyof typeof GridSelectionMode];


/**
* Enumeration representing different cell merging modes for the grid elements.
* - 'never': Never merge cells.
* - 'always': Always merge adjacent cells based on merge strategy.
* - 'onSort': Only merge cells in column that are sorted.
*/
export const GridCellMergeMode = {
always: 'always',
onSort: 'onSort'
} as const;
export type GridCellMergeMode = (typeof GridCellMergeMode)[keyof typeof GridCellMergeMode];

/** Enumeration representing different column display order options. */
export const ColumnDisplayOrder = {
Alphabetical: 'Alphabetical',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ColumnPinningPosition, FilterMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition, GridValidationTrigger, RowPinningPosition, Size } from './enums';
import { ColumnPinningPosition, FilterMode, GridCellMergeMode, GridPagingMode, GridSelectionMode, GridSummaryCalculationMode, GridSummaryPosition, GridValidationTrigger, RowPinningPosition, Size } from './enums';
import {
ISearchInfo, IGridCellEventArgs, IRowSelectionEventArgs, IColumnSelectionEventArgs,
IPinColumnCancellableEventArgs, IColumnVisibilityChangedEventArgs, IColumnVisibilityChangingEventArgs,
Expand Down Expand Up @@ -38,6 +38,7 @@ import { IDimensionsChange, IPivotConfiguration, IPivotDimension, IPivotKeys, IP
import { IDataCloneStrategy } from '../../data-operations/data-clone-strategy';
import { FormControl, FormGroup, ValidationErrors } from '@angular/forms';
import { IgxGridValidationService } from '../grid/grid-validation.service';
import { IGridMergeStrategy } from '../../data-operations/merge-strategy';

export const IGX_GRID_BASE = /*@__PURE__*/new InjectionToken<GridType>('IgxGridBaseToken');
export const IGX_GRID_SERVICE_BASE = /*@__PURE__*/new InjectionToken<GridServiceType>('IgxGridServiceBaseToken');
Expand Down Expand Up @@ -335,6 +336,7 @@ export interface ColumnType extends FieldType {
/** @hidden @internal */
headerCell: any;
validators: any[];
mergingComparer: (prevRecord: any, record: any, field: string) => boolean;

/**
* The template reference for the custom header of the column
Expand Down Expand Up @@ -453,6 +455,7 @@ export interface ColumnType extends FieldType {
pinned: boolean;
/** Indicates if the column is currently expanded or collapsed. If the value is true, the column is expanded */
expanded: boolean;
merge: boolean;
/** Indicates if the column is currently selected. If the value is true, the column is selected */
selected: boolean;
/** Indicates if the column can be selected. If the value is true, the column can be selected */
Expand Down Expand Up @@ -690,6 +693,8 @@ export interface GridServiceType {
export interface GridType extends IGridDataBindable {
/** Represents the locale of the grid: `USD`, `EUR`, `GBP`, `CNY`, `JPY`, etc. */
locale: string;
cellMergeMode: GridCellMergeMode;
mergeStrategy: IGridMergeStrategy;
resourceStrings: IGridResourceStrings;
/* blazorSuppress */
/** Represents the native HTML element itself */
Expand Down Expand Up @@ -1180,6 +1185,7 @@ export interface GridType extends IGridDataBindable {
getEmptyRecordObjectFor(inRow: RowType): any;
isSummaryRow(rec: any): boolean;
isRecordPinned(rec: any): boolean;
isRecordMerged(rec: any): boolean;
getInitialPinnedIndex(rec: any): number;
isRecordPinnedByViewIndex(rowIndex: number): boolean;
isColumnGrouped(fieldName: string): boolean;
Expand Down
3 changes: 2 additions & 1 deletion projects/igniteui-angular/src/lib/grids/common/pipes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class IgxGridRowClassesPipe implements PipeTransform {
dragging: boolean,
index: number,
mrl: boolean,
merged: boolean,
filteredOut: boolean,
_rowData: any,
_: number
Expand All @@ -126,7 +127,7 @@ export class IgxGridRowClassesPipe implements PipeTransform {
[dirty, 'igx-grid__tr--edited'],
[deleted, 'igx-grid__tr--deleted'],
[dragging, 'igx-grid__tr--drag'],
[mrl, 'igx-grid__tr--mrl'],
[mrl || merged, 'igx-grid__tr--mrl'],
// Tree grid only
[filteredOut, 'igx-grid__tr--filtered']
];
Expand Down
Loading
Loading