projects/swimlane/ngx-datatable/src/lib/utils/math.ts
import { columnsByPin, columnsTotalWidth } from './column';
/**
* Calculates the Total Flex Grow
*/
export function getTotalFlexGrow(columns: any[]) {
let totalFlexGrow = 0;
for (const c of columns) {
totalFlexGrow += c.flexGrow || 0;
}
return totalFlexGrow;
}
/**
* Adjusts the column widths.
* Inspired by: https://github.com/facebook/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js
*/
export function adjustColumnWidths(allColumns: any, expectedWidth: any) {
const columnsWidth = columnsTotalWidth(allColumns);
const totalFlexGrow = getTotalFlexGrow(allColumns);
const colsByGroup = columnsByPin(allColumns);
if (columnsWidth !== expectedWidth) {
scaleColumns(colsByGroup, expectedWidth, totalFlexGrow);
}
}
/**
* Resizes columns based on the flexGrow property, while respecting manually set widths
*/
function scaleColumns(colsByGroup: any, maxWidth: any, totalFlexGrow: any) {
// calculate total width and flexgrow points for coulumns that can be resized
for (const attr in colsByGroup) {
for (const column of colsByGroup[attr]) {
if (!column.canAutoResize) {
maxWidth -= column.width;
totalFlexGrow -= column.flexGrow ? column.flexGrow : 0;
} else {
column.width = 0;
}
}
}
const hasMinWidth = {};
let remainingWidth = maxWidth;
// resize columns until no width is left to be distributed
do {
const widthPerFlexPoint = remainingWidth / totalFlexGrow;
remainingWidth = 0;
for (const attr in colsByGroup) {
for (const column of colsByGroup[attr]) {
// if the column can be resize and it hasn't reached its minimum width yet
if (column.canAutoResize && !hasMinWidth[column.prop]) {
const newWidth = column.width + column.flexGrow * widthPerFlexPoint;
if (column.minWidth !== undefined && newWidth < column.minWidth) {
remainingWidth += newWidth - column.minWidth;
column.width = column.minWidth;
hasMinWidth[column.prop] = true;
} else {
column.width = newWidth;
}
}
}
}
} while (remainingWidth !== 0);
}
/**
* Forces the width of the columns to
* distribute equally but overflowing when necessary
*
* Rules:
*
* - If combined withs are less than the total width of the grid,
* proportion the widths given the min / max / normal widths to fill the width.
*
* - If the combined widths, exceed the total width of the grid,
* use the standard widths.
*
* - If a column is resized, it should always use that width
*
* - The proportional widths should never fall below min size if specified.
*
* - If the grid starts off small but then becomes greater than the size ( + / - )
* the width should use the original width; not the newly proportioned widths.
*/
export function forceFillColumnWidths(
allColumns: any[],
expectedWidth: number,
startIdx: number,
allowBleed: boolean,
defaultColWidth: number = 300
) {
const columnsToResize = allColumns.slice(startIdx + 1, allColumns.length).filter(c => {
return c.canAutoResize !== false;
});
for (const column of columnsToResize) {
if (!column.$$oldWidth) {
column.$$oldWidth = column.width;
}
}
let additionWidthPerColumn = 0;
let exceedsWindow = false;
let contentWidth = getContentWidth(allColumns, defaultColWidth);
let remainingWidth = expectedWidth - contentWidth;
const columnsProcessed: any[] = [];
const remainingWidthLimit = 1; // when to stop
// This loop takes care of the
do {
additionWidthPerColumn = remainingWidth / columnsToResize.length;
exceedsWindow = contentWidth >= expectedWidth;
for (const column of columnsToResize) {
if (exceedsWindow && allowBleed) {
column.width = column.$$oldWidth || column.width || defaultColWidth;
} else {
const newSize = (column.width || defaultColWidth) + additionWidthPerColumn;
if (column.minWidth && newSize < column.minWidth) {
column.width = column.minWidth;
columnsProcessed.push(column);
} else if (column.maxWidth && newSize > column.maxWidth) {
column.width = column.maxWidth;
columnsProcessed.push(column);
} else {
column.width = newSize;
}
}
column.width = Math.max(0, column.width);
}
contentWidth = getContentWidth(allColumns);
remainingWidth = expectedWidth - contentWidth;
removeProcessedColumns(columnsToResize, columnsProcessed);
} while (remainingWidth > remainingWidthLimit && columnsToResize.length !== 0);
}
/**
* Remove the processed columns from the current active columns.
*/
function removeProcessedColumns(columnsToResize: any[], columnsProcessed: any[]) {
for (const column of columnsProcessed) {
const index = columnsToResize.indexOf(column);
columnsToResize.splice(index, 1);
}
}
/**
* Gets the width of the columns
*/
function getContentWidth(allColumns: any, defaultColWidth: number = 300): number {
let contentWidth = 0;
for (const column of allColumns) {
contentWidth += column.width || defaultColWidth;
}
return contentWidth;
}