Skip to content

Commit

Permalink
Merge pull request #4706 from IgniteUI/SKrastev/mrl-resizing
Browse files Browse the repository at this point in the history
Multi-row layout - Resizing feature integration.
  • Loading branch information
mpavlinov authored May 10, 2019
2 parents d15ccb5 + fc132ec commit 004891f
Show file tree
Hide file tree
Showing 5 changed files with 439 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IgxColumnComponent } from '../grids';

export interface MRLColumnSizeInfo {
ref: IgxColumnComponent;
width: number;
colSpan: number;
colEnd: number;
widthSetByUser: boolean;
}

export interface MRLResizeColumnInfo {
target: IgxColumnComponent;
spanUsed: number;
}
59 changes: 44 additions & 15 deletions projects/igniteui-angular/src/lib/grids/column.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { FilteringExpressionsTree } from '../data-operations/filtering-expressio
import { IgxGridFilteringCellComponent } from './filtering/grid-filtering-cell.component';
import { IgxGridHeaderGroupComponent } from './grid-header-group.component';
import { DeprecateProperty } from '../core/deprecateDecorators';
import { MRLColumnSizeInfo, MRLResizeColumnInfo } from '../data-operations/multi-row-layout.interfaces';

/**
* **Ignite UI for Angular Column** -
Expand Down Expand Up @@ -1081,19 +1082,19 @@ export class IgxColumnComponent implements AfterContentInit {
/**
* @hidden
*/
getGridTemplate(isRow, isIE): string {
getGridTemplate(isRow: boolean, isIE: boolean): string {
const colSpanAccum = (acc, val) => Math.max(val.colStart + val.gridColumnSpan - 1, acc);
const templateItems = !isRow ?
this.children && this.children.reduce(colSpanAccum, 1) || 1 :
this.grid.multiRowLayoutRowSize;
const generatedSizes = !isRow ? this.getColumnSizesString(this.children.toArray()) : null;
const generatedSizes = !isRow ? this.getColumnSizesString(this.children) : null;
return isIE ?
generatedSizes || `(1fr)[${templateItems}]` :
generatedSizes || `repeat(${templateItems},1fr)`;
}

public getInitialChildColumnSizes(children: any[]): Array<any> {
const columnSizes = [];
public getInitialChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<MRLColumnSizeInfo> {
const columnSizes: MRLColumnSizeInfo[] = [];
// find the smallest col spans
children.forEach(col => {
if (!col.colStart) {
Expand All @@ -1108,7 +1109,7 @@ export class IgxColumnComponent implements AfterContentInit {
// If nothing is defined yet take any column at first
// We use colEnd to know where the column actually ends, because not always it starts where we have it set in columnSizes.
columnSizes[col.colStart - 1] = {
field: col.field,
ref: col,
width: col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
colSpan: col.gridColumnSpan,
colEnd: col.colStart + col.gridColumnSpan,
Expand Down Expand Up @@ -1136,7 +1137,7 @@ export class IgxColumnComponent implements AfterContentInit {

// Replace the old column with the new one.
columnSizes[col.colStart - 1] = {
field: col.field,
ref: col,
width: col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
colSpan: col.gridColumnSpan,
colEnd: col.colStart + col.gridColumnSpan,
Expand All @@ -1149,7 +1150,7 @@ export class IgxColumnComponent implements AfterContentInit {
for (let i = col.colStart - 1 + columnSizes[col.colStart - 1].colSpan; i < col.colStart - 1 + col.gridColumnSpan; i++) {
if (!columnSizes[i] || !columnSizes[i].widthSetByUser) {
columnSizes[i] = {
field: col.field,
ref: col,
width: col.widthSetByUser || this.grid.columnWidthSetByUser ? parseInt(col.calcWidth, 10) : null,
colSpan: col.gridColumnSpan,
colEnd: col.colStart + col.gridColumnSpan,
Expand All @@ -1171,7 +1172,8 @@ export class IgxColumnComponent implements AfterContentInit {
for (; j < columnSizes[i].colSpan && i + j + 1 < columnSizes[i].colEnd; j++) {
if (columnSizes[i + j] &&
((!columnSizes[i].width && columnSizes[i + j].width) ||
(!!columnSizes[i + j].width && columnSizes[i + j].colSpan < columnSizes[i].colSpan))) {
(!columnSizes[i].width && !columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan) ||
(!!columnSizes[i + j].width && columnSizes[i + j].colSpan <= columnSizes[i].colSpan))) {
// If we reach an already defined column that has width and the current doesn't have or
// if the reached column has bigger colSpan we stop.
break;
Expand All @@ -1180,11 +1182,11 @@ export class IgxColumnComponent implements AfterContentInit {
columnSizes[i].width / columnSizes[i].colSpan :
columnSizes[i].width;
columnSizes[i + j] = {
field: columnSizes[i].field,
ref: columnSizes[i].ref,
width: width,
colSpan: 1,
widthSetByUser: columnSizes[i].widthSetByUser,
hidden: columnSizes[i].hidden
colEnd: columnSizes[i].colEnd,
widthSetByUser: columnSizes[i].widthSetByUser
};
}
}
Expand All @@ -1203,11 +1205,11 @@ export class IgxColumnComponent implements AfterContentInit {
return columnSizes;
}

protected getFilledChildColumnSizes(children: any[]): Array<any> {
protected getFilledChildColumnSizes(children: QueryList<IgxColumnComponent>): Array<string> {
const columnSizes = this.getInitialChildColumnSizes(children);

// fill the gaps if there are any
const result = [];
const result: string[] = [];
for (let i = 0; i < columnSizes.length; i++) {
if (columnSizes[i] && !!columnSizes[i].width) {
result.push(columnSizes[i].width + 'px');
Expand All @@ -1218,11 +1220,38 @@ export class IgxColumnComponent implements AfterContentInit {
return result;
}

protected getColumnSizesString(children: any[]): string {
protected getColumnSizesString(children: QueryList<IgxColumnComponent>): string {
const res = this.getFilledChildColumnSizes(children);
return res.join(' ');
}

public getResizableColUnderEnd(): MRLResizeColumnInfo[] {
if (this.columnLayout || !this.parent.columnLayout || this.columnGroup) {
return [{ target: this, spanUsed: 1 }];
}

const columnSized = this.getInitialChildColumnSizes(this.parent.children);
const targets: MRLResizeColumnInfo[] = [];
const colEnd = this.colEnd ? this.colEnd : this.colStart + 1;

for (let i = 0; i < columnSized.length; i++) {
if (this.colStart <= i + 1 && i + 1 < colEnd) {
targets.push({ target: columnSized[i].ref, spanUsed: 1});
}
}

const targetsSquashed: MRLResizeColumnInfo[] = [];
for (let j = 0; j < targets.length; j++) {
if (targetsSquashed.length && targetsSquashed[targetsSquashed.length - 1].target.field === targets[j].target.field) {
targetsSquashed[targetsSquashed.length - 1].spanUsed++;
} else {
targetsSquashed.push(targets[j]);
}
}

return targetsSquashed;
}

/**
* Pins the column at the provided index in the pinned area. Defaults to index `0` if not provided.
* Returns `true` if the column is successfully pinned. Returns `false` if the column cannot be pinned.
Expand Down Expand Up @@ -1747,7 +1776,7 @@ export class IgxColumnLayoutComponent extends IgxColumnGroupComponent implements
* @memberof IgxColumnGroupComponent
*/
get width() {
const width = this.getFilledChildColumnSizes(this.children.toArray()).reduce((acc, val) => acc + parseInt(val, 10), 0);
const width = this.getFilledChildColumnSizes(this.children).reduce((acc, val) => acc + parseInt(val, 10), 0);
return width;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3957,7 +3957,12 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
if (!this.columnWidthSetByUser) {
this._columnWidth = this.getPossibleColumnWidth();
this.columnList.forEach((column: IgxColumnComponent) => {
column.defaultWidth = this._columnWidth;
if (this.hasColumnLayouts && parseInt(this._columnWidth, 10)) {
const columnWidthCombined = parseInt(this._columnWidth, 10) * (column.colEnd ? column.colEnd - column.colStart : 1);
column.defaultWidth = columnWidthCombined + 'px';
} else {
column.defaultWidth = this._columnWidth;
}
});
}
}
Expand Down Expand Up @@ -4101,7 +4106,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
// Column layouts related
let visibleCols = [];
const columnBlocks = this.visibleColumns.filter(c => c.columnGroup);
const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children.toArray()));
const colsPerBlock = columnBlocks.map(block => block.getInitialChildColumnSizes(block.children));
const combinedBlocksSize = colsPerBlock.reduce((acc, item) => acc + item.length, 0);
colsPerBlock.forEach(blockCols => visibleCols = visibleCols.concat(blockCols));
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ export class IgxColumnResizingService {
get resizerHeight(): number {
let height = this.column.grid.getVisibleContentHeight();

// Column height multiplier in case there are Column Layouts. The resizer height need to take into account rowStart.
let columnHeightMultiplier = 1;
if (this.column.parent && this.column.parent.columnLayout) {
columnHeightMultiplier = this.column.grid.multiRowLayoutRowSize - this.column.rowStart + 1;
}

if (this.column.level !== 0) {
height -= this.column.topLevelParent.headerGroup.height - this.column.headerGroup.height;
height -= this.column.topLevelParent.headerGroup.height - this.column.headerGroup.height * columnHeightMultiplier;
}

return height;
Expand All @@ -47,8 +53,9 @@ export class IgxColumnResizingService {
* Returns the minimal possible width to which the column can be resized.
*/
get restrictResizeMin(): number {
const columnLayoutMultiplier = this.column.grid.hasColumnLayouts ? this.column.gridColumnSpan : 1;
const actualMinWidth = parseFloat(this.column.minWidth);
const defaultMinWidth = parseFloat(this.column.defaultMinWidth);
const defaultMinWidth = parseFloat(this.column.defaultMinWidth) * columnLayoutMultiplier;

let minWidth = Number.isNaN(actualMinWidth) || actualMinWidth < defaultMinWidth ? defaultMinWidth : actualMinWidth;
minWidth = minWidth < parseFloat(this.column.width) ? minWidth : parseFloat(this.column.width);
Expand Down Expand Up @@ -126,24 +133,21 @@ export class IgxColumnResizingService {
const diff = event.clientX - this.startResizePos;

let currentColWidth = parseFloat(this.column.width);

const actualMinWidth = parseFloat(this.column.minWidth);
const defaultMinWidth = parseFloat(this.column.defaultMinWidth);

let colMinWidth = Number.isNaN(actualMinWidth) || actualMinWidth < defaultMinWidth ? defaultMinWidth : actualMinWidth;
const colMaxWidth = this.column.pinned ? parseFloat(this.pinnedMaxWidth) : parseFloat(this.column.maxWidth);

const actualWidth = this.column.headerCell.elementRef.nativeElement.getBoundingClientRect().width;

currentColWidth = Number.isNaN(currentColWidth) || (currentColWidth < actualWidth) ? actualWidth : currentColWidth;
colMinWidth = colMinWidth < currentColWidth ? colMinWidth : currentColWidth;

if (currentColWidth + diff < colMinWidth) {
this.column.width = colMinWidth + 'px';
} else if (colMaxWidth && (currentColWidth + diff > colMaxWidth)) {
this.column.width = colMaxWidth + 'px';
const colMinWidth = this.getColMinWidth(this.column);
const colMaxWidth = this.getColMaxWidth(this.column);
if (this.column.grid.hasColumnLayouts) {
this.resizeColumnLayoutFor(this.column, diff);
} else {
this.column.width = (currentColWidth + diff) + 'px';
if (currentColWidth + diff < colMinWidth) {
this.column.width = colMinWidth + 'px';
} else if (colMaxWidth && (currentColWidth + diff > colMaxWidth)) {
this.column.width = colMaxWidth + 'px';
} else {
this.column.width = (currentColWidth + diff) + 'px';
}
}

this.zone.run(() => {});
Expand All @@ -159,4 +163,79 @@ export class IgxColumnResizingService {

this.isColumnResizing = false;
}

protected getColMinWidth(column: IgxColumnComponent) {
let currentColWidth = parseFloat(column.width);
const actualWidth = column.headerCell.elementRef.nativeElement.getBoundingClientRect().width;
currentColWidth = Number.isNaN(currentColWidth) || (currentColWidth < actualWidth) ? actualWidth : currentColWidth;

const columnLayoutMultiplier = column.grid.hasColumnLayouts ? column.gridColumnSpan : 1;
const actualMinWidth = parseFloat(column.minWidth);
const defaultMinWidth = parseFloat(column.defaultMinWidth) * columnLayoutMultiplier;
const colMinWidth = Number.isNaN(actualMinWidth) || actualMinWidth < defaultMinWidth ? defaultMinWidth : actualMinWidth;
return colMinWidth < currentColWidth ? colMinWidth : currentColWidth;
}

protected getColMaxWidth(column: IgxColumnComponent) {
return column.pinned ? parseFloat(this.pinnedMaxWidth) : parseFloat(column.maxWidth);
}

protected resizeColumnLayoutFor(column: IgxColumnComponent, diff: number) {
const relativeColumns = column.getResizableColUnderEnd();
const combinedSpan = relativeColumns.reduce((acc, col) => acc + col.spanUsed, 0);

if (column.pinned) {
const pinnedWidth = this.column.grid.getPinnedWidth(true);
const maxPinnedWidth = this.column.grid.calcPinnedContainerMaxWidth;

if (pinnedWidth + diff > maxPinnedWidth) {
diff = maxPinnedWidth - pinnedWidth;
}
}

// Resize first those who might reach min/max width
let columnsToResize = [...relativeColumns];
let updatedDiff = diff;
let updatedCombinedSpan = combinedSpan;
let setMinMaxCols = false;
do {
// Cycle them until there are not ones that reach min/max size, because the diff accumulates after each cycle.
// This is because we can have at first 2 cols reaching min width and then after
// recalculating the diff there might be 1 more that reaches min width.
setMinMaxCols = false;
let newCombinedSpan = updatedCombinedSpan;
const newColsToResize = [];
columnsToResize.forEach((col) => {
const currentResizeWidth = parseFloat(col.target.calcWidth);
const resizeScaled = (diff / updatedCombinedSpan) * col.target.gridColumnSpan;

const minWidth = this.getColMinWidth(col.target);
const maxWidth = this.getColMaxWidth(col.target);
if (currentResizeWidth + resizeScaled < minWidth) {
col.target.width = minWidth + 'px';
updatedDiff += (currentResizeWidth - minWidth);
newCombinedSpan -= col.spanUsed;
setMinMaxCols = true;
} else if (maxWidth && (currentResizeWidth + resizeScaled > maxWidth)) {
col.target.width = maxWidth + 'px';
updatedDiff -= (maxWidth - currentResizeWidth);
newCombinedSpan -= col.spanUsed;
setMinMaxCols = true;
} else {
// Save new ones that can be resized
newColsToResize.push(col);
}
});

updatedCombinedSpan = newCombinedSpan;
columnsToResize = newColsToResize;
} while (setMinMaxCols);

// Those left that don't reach min/max size resize them normally.
columnsToResize.forEach((col) => {
const currentResizeWidth = parseFloat(col.target.calcWidth);
const resizeScaled = (updatedDiff / updatedCombinedSpan) * col.target.gridColumnSpan;
col.target.width = (currentResizeWidth + resizeScaled) + 'px';
});
}
}
Loading

0 comments on commit 004891f

Please sign in to comment.