UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

232 lines (199 loc) 8.94 kB
import { Injectable, NgZone } from '@angular/core'; import { ColumnType } from '../common/grid.interface'; /** * @hidden * @internal */ @Injectable() export class IgxColumnResizingService { /** * @hidden */ public startResizePos: number; /** * Indicates that a column is currently being resized. */ public isColumnResizing: boolean; /** * @hidden */ public resizeCursor: string = null; /** * @hidden */ public showResizer = false; /** * The column being resized. */ public column: ColumnType; constructor(private zone: NgZone) { } /** * @hidden */ public getColumnHeaderRenderedWidth() { return this.column.headerCell.nativeElement.getBoundingClientRect().width; } /** * @hidden */ public 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.columnLayoutChild) { columnHeightMultiplier = this.column.grid.multiRowLayoutRowSize - this.column.rowStart + 1; } if (this.column.level !== 0) { height -= this.column.topLevelParent.headerGroup.height - this.column.headerGroup.height * columnHeightMultiplier; } return height; } /** * Returns the minimal possible width to which the column can be resized. */ public get restrictResizeMin(): number { const actualWidth = this.getColumnHeaderRenderedWidth(); const minWidth = this.column.minWidthPx < actualWidth ? this.column.minWidthPx : actualWidth; return actualWidth - minWidth; } /** * Returns the maximal possible width to which the column can be resized. */ public get restrictResizeMax(): number { const actualWidth = this.getColumnHeaderRenderedWidth(); const maxWidth = this.column.maxWidthPx; if (this.column.maxWidth) { return maxWidth - actualWidth; } else { return Number.MAX_SAFE_INTEGER; } } /** * Autosizes the column to the longest currently visible cell value, including the header cell. * If the column has a predifined maxWidth and the autosized column width will become bigger than it, * then the column is sized to its maxWidth. */ public autosizeColumnOnDblClick() { const currentColWidth = this.getColumnHeaderRenderedWidth(); this.column.width = this.column.getAutoSize(); this.zone.run(() => { }); this.column.grid.columnResized.emit({ column: this.column, prevWidth: currentColWidth.toString(), newWidth: this.column.width }); } /** * Resizes the column regaridng to the column minWidth and maxWidth. */ public resizeColumn(event: MouseEvent) { this.showResizer = false; const diff = event.clientX - this.startResizePos; const colWidth = this.column.width; const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; let currentColWidth = parseFloat(colWidth); const actualWidth = this.getColumnHeaderRenderedWidth(); currentColWidth = Number.isNaN(currentColWidth) ? parseFloat(actualWidth as any) : currentColWidth; if (this.column.grid.hasColumnLayouts) { this.resizeColumnLayoutFor(this.column, diff); } else if (isPercentageWidth) { this._handlePercentageResize(diff, this.column); } else { this._handlePixelResize(diff, this.column); } this.zone.run(() => { }); if (currentColWidth !== parseFloat(this.column.width)) { this.column.grid.columnResized.emit({ column: this.column, prevWidth: isPercentageWidth ? currentColWidth + '%' : currentColWidth + 'px', newWidth: this.column.width }); } this.isColumnResizing = false; } protected _handlePixelResize(diff: number, column: ColumnType) { const currentColWidth = parseFloat(column.width); const colMinWidth = column.minWidthPx; const colMaxWidth = column.maxWidthPx; if (currentColWidth + diff < colMinWidth) { column.width = colMinWidth + 'px'; } else if (colMaxWidth && (currentColWidth + diff > colMaxWidth)) { column.width = colMaxWidth + 'px'; } else { column.width = (currentColWidth + diff) + 'px'; } } protected _handlePercentageResize(diff: number, column: ColumnType) { const currentPercentWidth = parseFloat(column.width); const gridAvailableSize = column.grid.calcWidth; const diffPercentage = (diff / gridAvailableSize) * 100; const colMinWidth = column.minWidthPercent; const colMaxWidth = column.maxWidthPercent; if (currentPercentWidth + diffPercentage < colMinWidth) { column.width = colMinWidth + '%'; } else if (colMaxWidth && (currentPercentWidth + diffPercentage > colMaxWidth)) { column.width = colMaxWidth + '%'; } else { column.width = (currentPercentWidth + diffPercentage) + '%'; } } protected getColMinWidth(column: ColumnType) { let currentColWidth = parseFloat(column.width); const actualWidth = column.headerCell.nativeElement.getBoundingClientRect().width; currentColWidth = Number.isNaN(currentColWidth) || (currentColWidth < actualWidth) ? actualWidth : currentColWidth; const actualMinWidth = parseFloat(column.minWidth); return actualMinWidth < currentColWidth ? actualMinWidth : currentColWidth; } protected resizeColumnLayoutFor(column: ColumnType, diff: number) { const relativeColumns = column.getResizableColUnderEnd(); const combinedSpan = relativeColumns.reduce((acc, col) => acc + col.spanUsed, 0); // 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 colWidth = col.target.width; const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; const minWidth = col.target.minWidthPx; const maxWidth = col.target.maxWidthPx; if (currentResizeWidth + resizeScaled < minWidth) { col.target.width = isPercentageWidth ? col.target.minWidthPercent + '%' : minWidth + 'px'; updatedDiff += (currentResizeWidth - minWidth); newCombinedSpan -= col.spanUsed; setMinMaxCols = true; } else if (maxWidth && (currentResizeWidth + resizeScaled > maxWidth)) { col.target.width = isPercentageWidth ? col.target.maxWidthPercent + '%' : col.target.maxWidthPx + '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 resizeScaled = (updatedDiff / updatedCombinedSpan) * col.target.gridColumnSpan; const colWidth = col.target.width; const isPercentageWidth = colWidth && typeof colWidth === 'string' && colWidth.indexOf('%') !== -1; if (isPercentageWidth) { this._handlePercentageResize(resizeScaled, col.target); } else { this._handlePixelResize(resizeScaled, col.target); } }); } }