@mui/x-data-grid
Version:
The community edition of the data grid component (MUI X).
469 lines (393 loc) • 17.8 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import { DEFAULT_GRID_COL_TYPE_KEY, getGridDefaultColumnTypes } from '../../../colDef';
import { gridColumnsSelector, gridColumnVisibilityModelSelector } from './gridColumnsSelector';
import { clamp } from '../../../utils/utils';
export var COLUMNS_DIMENSION_PROPERTIES = ['maxWidth', 'minWidth', 'width', 'flex'];
export var computeColumnTypes = function computeColumnTypes() {
var customColumnTypes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var mergedColumnTypes = _extends({}, getGridDefaultColumnTypes());
Object.entries(customColumnTypes).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
colType = _ref2[0],
colTypeDef = _ref2[1];
if (mergedColumnTypes[colType]) {
mergedColumnTypes[colType] = _extends({}, mergedColumnTypes[colType], colTypeDef);
} else {
mergedColumnTypes[colType] = _extends({}, mergedColumnTypes[colTypeDef.extendType || DEFAULT_GRID_COL_TYPE_KEY], colTypeDef);
}
});
return mergedColumnTypes;
};
/**
* Computes width for flex columns.
* Based on CSS Flexbox specification:
* https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
*/
export function computeFlexColumnsWidth(_ref3) {
var initialFreeSpace = _ref3.initialFreeSpace,
totalFlexUnits = _ref3.totalFlexUnits,
flexColumns = _ref3.flexColumns;
var flexColumnsLookup = {
all: {},
frozenFields: [],
freeze: function freeze(field) {
var value = flexColumnsLookup.all[field];
if (value && value.frozen !== true) {
flexColumnsLookup.all[field].frozen = true;
flexColumnsLookup.frozenFields.push(field);
}
}
}; // Step 5 of https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths
function loopOverFlexItems() {
// 5a: If all the flex items on the line are frozen, free space has been distributed.
if (flexColumnsLookup.frozenFields.length === flexColumns.length) {
return;
}
var violationsLookup = {
min: {},
max: {}
};
var remainingFreeSpace = initialFreeSpace;
var flexUnits = totalFlexUnits;
var totalViolation = 0; // 5b: Calculate the remaining free space
flexColumnsLookup.frozenFields.forEach(function (field) {
remainingFreeSpace -= flexColumnsLookup.all[field].computedWidth;
flexUnits -= flexColumnsLookup.all[field].flex;
});
for (var i = 0; i < flexColumns.length; i += 1) {
var column = flexColumns[i];
if (flexColumnsLookup.all[column.field] && flexColumnsLookup.all[column.field].frozen === true) {
// eslint-disable-next-line no-continue
continue;
} // 5c: Distribute remaining free space proportional to the flex factors
var widthPerFlexUnit = remainingFreeSpace / flexUnits;
var computedWidth = widthPerFlexUnit * column.flex; // 5d: Fix min/max violations
if (computedWidth < column.minWidth) {
totalViolation += column.minWidth - computedWidth;
computedWidth = column.minWidth;
violationsLookup.min[column.field] = true;
} else if (computedWidth > column.maxWidth) {
totalViolation += column.maxWidth - computedWidth;
computedWidth = column.maxWidth;
violationsLookup.max[column.field] = true;
}
flexColumnsLookup.all[column.field] = {
frozen: false,
computedWidth: computedWidth,
flex: column.flex
};
} // 5e: Freeze over-flexed items
if (totalViolation < 0) {
// Freeze all the items with max violations
Object.keys(violationsLookup.max).forEach(function (field) {
flexColumnsLookup.freeze(field);
});
} else if (totalViolation > 0) {
// Freeze all the items with min violations
Object.keys(violationsLookup.min).forEach(function (field) {
flexColumnsLookup.freeze(field);
});
} else {
// Freeze all items
flexColumns.forEach(function (_ref4) {
var field = _ref4.field;
flexColumnsLookup.freeze(field);
});
} // 5f: Return to the start of this loop
loopOverFlexItems();
}
loopOverFlexItems();
return flexColumnsLookup.all;
}
/**
* Compute the `computedWidth` (ie: the width the column should have during rendering) based on the `width` / `flex` / `minWidth` / `maxWidth` properties of `GridColDef`.
* The columns already have been merged with there `type` default values for `minWidth`, `maxWidth` and `width`, thus the `!` for those properties below.
* TODO: Unit test this function in depth and only keep basic cases for the whole grid testing.
* TODO: Improve the `GridColDef` typing to reflect the fact that `minWidth` / `maxWidth` and `width` can't be null after the merge with the `type` default values.
*/
export var hydrateColumnsWidth = function hydrateColumnsWidth(rawState, viewportInnerWidth) {
var columnsLookup = {};
var totalFlexUnits = 0;
var widthAllocatedBeforeFlex = 0;
var flexColumns = []; // For the non-flex columns, compute their width
// For the flex columns, compute there minimum width and how much width must be allocated during the flex allocation
rawState.all.forEach(function (columnField) {
var newColumn = _extends({}, rawState.lookup[columnField]);
if (rawState.columnVisibilityModel[columnField] === false) {
newColumn.computedWidth = 0;
} else {
var computedWidth;
if (newColumn.flex && newColumn.flex > 0) {
totalFlexUnits += newColumn.flex;
computedWidth = 0;
flexColumns.push(newColumn);
} else {
computedWidth = clamp(newColumn.width, newColumn.minWidth, newColumn.maxWidth);
}
widthAllocatedBeforeFlex += computedWidth;
newColumn.computedWidth = computedWidth;
}
columnsLookup[columnField] = newColumn;
});
var initialFreeSpace = Math.max(viewportInnerWidth - widthAllocatedBeforeFlex, 0); // Allocate the remaining space to the flex columns
if (totalFlexUnits > 0 && viewportInnerWidth > 0) {
var computedColumnWidths = computeFlexColumnsWidth({
initialFreeSpace: initialFreeSpace,
totalFlexUnits: totalFlexUnits,
flexColumns: flexColumns
});
Object.keys(computedColumnWidths).forEach(function (field) {
columnsLookup[field].computedWidth = computedColumnWidths[field].computedWidth;
});
}
return _extends({}, rawState, {
lookup: columnsLookup
});
};
var columnTypeWarnedOnce = false;
/**
* Apply the order and the dimensions of the initial state.
* The columns not registered in `orderedFields` will be placed after the imported columns.
*/
export var applyInitialState = function applyInitialState(columnsState, initialState) {
if (!initialState) {
return columnsState;
}
var _initialState$ordered = initialState.orderedFields,
orderedFields = _initialState$ordered === void 0 ? [] : _initialState$ordered,
_initialState$dimensi = initialState.dimensions,
dimensions = _initialState$dimensi === void 0 ? {} : _initialState$dimensi;
var columnsWithUpdatedDimensions = Object.keys(dimensions);
if (columnsWithUpdatedDimensions.length === 0 && orderedFields.length === 0) {
return columnsState;
}
var orderedFieldsLookup = {};
var cleanOrderedFields = [];
for (var i = 0; i < orderedFields.length; i += 1) {
var _field = orderedFields[i]; // Ignores the fields in the initialState that matches no field on the current column state
if (columnsState.lookup[_field]) {
orderedFieldsLookup[_field] = true;
cleanOrderedFields.push(_field);
}
}
var newOrderedFields = cleanOrderedFields.length === 0 ? columnsState.all : [].concat(cleanOrderedFields, _toConsumableArray(columnsState.all.filter(function (field) {
return !orderedFieldsLookup[field];
})));
var newColumnLookup = _extends({}, columnsState.lookup);
var _loop = function _loop(_i) {
var field = columnsWithUpdatedDimensions[_i];
var newColDef = _extends({}, newColumnLookup[field], {
hasBeenResized: true
});
Object.entries(dimensions[field]).forEach(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
key = _ref6[0],
value = _ref6[1];
newColDef[key] = value === -1 ? Infinity : value;
});
newColumnLookup[field] = newColDef;
};
for (var _i = 0; _i < columnsWithUpdatedDimensions.length; _i += 1) {
_loop(_i);
}
var newColumnsState = {
all: newOrderedFields,
lookup: newColumnLookup
};
return newColumnsState;
};
/**
* @deprecated Should have been internal only, you can inline the logic.
*/
export var getGridColDef = function getGridColDef(columnTypes, type) {
if (!type) {
return columnTypes[DEFAULT_GRID_COL_TYPE_KEY];
}
if (process.env.NODE_ENV !== 'production') {
if (!columnTypeWarnedOnce && !columnTypes[type]) {
console.warn(["MUI: The column type \"".concat(type, "\" you are using is not supported."), "Column type \"string\" is being used instead."].join('\n'));
columnTypeWarnedOnce = true;
}
}
if (!columnTypes[type]) {
return columnTypes[DEFAULT_GRID_COL_TYPE_KEY];
}
return columnTypes[type];
};
export var createColumnsState = function createColumnsState(_ref7) {
var _apiRef$current$getRo, _apiRef$current$getRo2, _apiRef$current, _apiRef$current$getRo3;
var apiRef = _ref7.apiRef,
columnsToUpsert = _ref7.columnsToUpsert,
initialState = _ref7.initialState,
columnTypes = _ref7.columnTypes,
_ref7$currentColumnVi = _ref7.currentColumnVisibilityModel,
currentColumnVisibilityModel = _ref7$currentColumnVi === void 0 ? gridColumnVisibilityModelSelector(apiRef) : _ref7$currentColumnVi,
shouldRegenColumnVisibilityModelFromColumns = _ref7.shouldRegenColumnVisibilityModelFromColumns,
_ref7$keepOnlyColumns = _ref7.keepOnlyColumnsToUpsert,
keepOnlyColumnsToUpsert = _ref7$keepOnlyColumns === void 0 ? false : _ref7$keepOnlyColumns;
var isInsideStateInitializer = !apiRef.current.state.columns;
var columnsStateWithoutColumnVisibilityModel;
if (isInsideStateInitializer) {
columnsStateWithoutColumnVisibilityModel = {
all: [],
lookup: {}
};
} else {
var currentState = gridColumnsSelector(apiRef.current.state);
columnsStateWithoutColumnVisibilityModel = {
all: keepOnlyColumnsToUpsert ? [] : _toConsumableArray(currentState.all),
lookup: _extends({}, currentState.lookup) // Will be cleaned later if keepOnlyColumnsToUpsert=true
};
}
var columnsToKeep = {};
if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) {
columnsToKeep = Object.keys(columnsStateWithoutColumnVisibilityModel.lookup).reduce(function (acc, key) {
return _extends({}, acc, _defineProperty({}, key, false));
}, {});
}
var columnsToUpsertLookup = {};
columnsToUpsert.forEach(function (newColumn) {
var field = newColumn.field;
columnsToUpsertLookup[field] = true;
columnsToKeep[field] = true;
var existingState = columnsStateWithoutColumnVisibilityModel.lookup[field];
if (existingState == null) {
// New Column
existingState = _extends({}, getGridColDef(columnTypes, newColumn.type), {
// TODO v6: Inline `getGridColDef`
field: field,
hasBeenResized: false
});
columnsStateWithoutColumnVisibilityModel.all.push(field);
} else if (keepOnlyColumnsToUpsert) {
columnsStateWithoutColumnVisibilityModel.all.push(field);
}
var hasBeenResized = existingState.hasBeenResized;
COLUMNS_DIMENSION_PROPERTIES.forEach(function (key) {
if (newColumn[key] !== undefined) {
hasBeenResized = true;
if (newColumn[key] === -1) {
newColumn[key] = Infinity;
}
}
});
columnsStateWithoutColumnVisibilityModel.lookup[field] = _extends({}, existingState, {
hide: newColumn.hide == null ? false : newColumn.hide
}, newColumn, {
hasBeenResized: hasBeenResized
});
});
if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) {
Object.keys(columnsStateWithoutColumnVisibilityModel.lookup).forEach(function (field) {
if (!columnsToKeep[field]) {
delete columnsStateWithoutColumnVisibilityModel.lookup[field];
}
});
}
var columnsLookupBeforePreProcessing = _extends({}, columnsStateWithoutColumnVisibilityModel.lookup);
var columnsStateWithPreProcessing = apiRef.current.unstable_applyPipeProcessors('hydrateColumns', columnsStateWithoutColumnVisibilityModel); // TODO v6: remove the sync between the columns `hide` option and the model.
var columnVisibilityModel = {};
if (shouldRegenColumnVisibilityModelFromColumns) {
var hasModelChanged = false;
var newColumnVisibilityModel = _extends({}, currentColumnVisibilityModel);
if (isInsideStateInitializer) {
columnsStateWithPreProcessing.all.forEach(function (field) {
newColumnVisibilityModel[field] = !columnsStateWithoutColumnVisibilityModel.lookup[field].hide;
});
} else if (keepOnlyColumnsToUpsert) {
// At this point, `keepOnlyColumnsToUpsert` has a new meaning: keep the columns
// passed via `columnToUpsert` + columns added by the pre-processors. We do the following
// cleanup because a given column may have been removed from the `columns` prop but it still
// exists in the state.
Object.keys(newColumnVisibilityModel).forEach(function (field) {
if (!columnsStateWithPreProcessing.lookup[field]) {
delete newColumnVisibilityModel[field];
hasModelChanged = true;
}
});
}
columnsStateWithPreProcessing.all.forEach(function (field) {
// If neither the `columnsToUpsert` nor the pre-processors updated the column,
// Then we don't want to update the visibility status of the column in the model.
if (!columnsToUpsertLookup[field] && columnsLookupBeforePreProcessing[field] === columnsStateWithPreProcessing.lookup[field]) {
return;
} // We always assume that a column not in the model is visible by default. However, there's an
// edge case where the column is not in the model but it also doesn't exist in the `columns`
// prop, meaning that the column is being added. In that case, we assume that the column was
// not visible before for it be added to the model.
var isVisibleBefore = currentColumnVisibilityModel[field];
if (isVisibleBefore === undefined) {
if (isInsideStateInitializer) {
isVisibleBefore = true;
} else {
var _currentState = gridColumnsSelector(apiRef.current.state);
isVisibleBefore = !!_currentState.lookup[field];
}
}
var isVisibleAfter = !columnsStateWithPreProcessing.lookup[field].hide;
if (isVisibleAfter !== isVisibleBefore) {
hasModelChanged = true;
newColumnVisibilityModel[field] = isVisibleAfter;
}
});
if (hasModelChanged || isInsideStateInitializer) {
columnVisibilityModel = newColumnVisibilityModel;
} else {
columnVisibilityModel = currentColumnVisibilityModel;
}
} else {
columnVisibilityModel = currentColumnVisibilityModel;
}
var columnsStateWithPortableColumns = applyInitialState(columnsStateWithPreProcessing, initialState);
var columnsState = _extends({}, columnsStateWithPortableColumns, {
columnVisibilityModel: columnVisibilityModel
});
return hydrateColumnsWidth(columnsState, (_apiRef$current$getRo = (_apiRef$current$getRo2 = (_apiRef$current = apiRef.current).getRootDimensions) == null ? void 0 : (_apiRef$current$getRo3 = _apiRef$current$getRo2.call(_apiRef$current)) == null ? void 0 : _apiRef$current$getRo3.viewportInnerSize.width) != null ? _apiRef$current$getRo : 0);
};
export var mergeColumnsState = function mergeColumnsState(columnsState) {
return function (state) {
return _extends({}, state, {
columns: columnsState
});
};
};
export function getFirstNonSpannedColumnToRender(_ref8) {
var firstColumnToRender = _ref8.firstColumnToRender,
apiRef = _ref8.apiRef,
firstRowToRender = _ref8.firstRowToRender,
lastRowToRender = _ref8.lastRowToRender,
visibleRows = _ref8.visibleRows;
var firstNonSpannedColumnToRender = firstColumnToRender;
for (var i = firstRowToRender; i < lastRowToRender; i += 1) {
var row = visibleRows[i];
if (row) {
var rowId = visibleRows[i].id;
var cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, firstColumnToRender);
if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) {
firstNonSpannedColumnToRender = cellColSpanInfo.leftVisibleCellIndex;
}
}
}
return firstNonSpannedColumnToRender;
}
export function getFirstColumnIndexToRender(_ref9) {
var firstColumnIndex = _ref9.firstColumnIndex,
minColumnIndex = _ref9.minColumnIndex,
columnBuffer = _ref9.columnBuffer,
firstRowToRender = _ref9.firstRowToRender,
lastRowToRender = _ref9.lastRowToRender,
apiRef = _ref9.apiRef,
visibleRows = _ref9.visibleRows;
var initialFirstColumnToRender = Math.max(firstColumnIndex - columnBuffer, minColumnIndex);
var firstColumnToRender = getFirstNonSpannedColumnToRender({
firstColumnToRender: initialFirstColumnToRender,
apiRef: apiRef,
firstRowToRender: firstRowToRender,
lastRowToRender: lastRowToRender,
visibleRows: visibleRows
});
return firstColumnToRender;
}