UNPKG

@mui/x-data-grid

Version:

The Community plan edition of the Data Grid components (MUI X).

330 lines (316 loc) 12.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.applyInitialState = exports.COLUMNS_DIMENSION_PROPERTIES = void 0; exports.computeFlexColumnsWidth = computeFlexColumnsWidth; exports.createColumnsState = void 0; exports.getFirstNonSpannedColumnToRender = getFirstNonSpannedColumnToRender; exports.getTotalHeaderHeight = getTotalHeaderHeight; exports.hydrateColumnsWidth = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _colDef = require("../../../colDef"); var _gridColumnsSelector = require("./gridColumnsSelector"); var _utils = require("../../../utils/utils"); var _densitySelector = require("../density/densitySelector"); var _gridHeaderFilteringSelectors = require("../headerFiltering/gridHeaderFilteringSelectors"); var _gridColumnGroupsSelector = require("../columnGrouping/gridColumnGroupsSelector"); const COLUMNS_DIMENSION_PROPERTIES = exports.COLUMNS_DIMENSION_PROPERTIES = ['maxWidth', 'minWidth', 'width', 'flex']; const COLUMN_TYPES = (0, _colDef.getGridDefaultColumnTypes)(); /** * Computes width for flex columns. * Based on CSS Flexbox specification: * https://drafts.csswg.org/css-flexbox-1/#resolve-flexible-lengths */ function computeFlexColumnsWidth({ initialFreeSpace, totalFlexUnits, flexColumns }) { const uniqueFlexColumns = new Set(flexColumns.map(col => col.field)); const flexColumnsLookup = { all: {}, frozenFields: [], freeze: field => { const 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 === uniqueFlexColumns.size) { return; } const violationsLookup = { min: {}, max: {} }; let remainingFreeSpace = initialFreeSpace; let flexUnits = totalFlexUnits; let totalViolation = 0; // 5b: Calculate the remaining free space flexColumnsLookup.frozenFields.forEach(field => { remainingFreeSpace -= flexColumnsLookup.all[field].computedWidth; flexUnits -= flexColumnsLookup.all[field].flex; }); for (let i = 0; i < flexColumns.length; i += 1) { const column = flexColumns[i]; if (flexColumnsLookup.all[column.field] && flexColumnsLookup.all[column.field].frozen === true) { continue; } // 5c: Distribute remaining free space proportional to the flex factors const widthPerFlexUnit = remainingFreeSpace / flexUnits; let 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, flex: column.flex }; } // 5e: Freeze over-flexed items if (totalViolation < 0) { // Freeze all the items with max violations Object.keys(violationsLookup.max).forEach(field => { flexColumnsLookup.freeze(field); }); } else if (totalViolation > 0) { // Freeze all the items with min violations Object.keys(violationsLookup.min).forEach(field => { flexColumnsLookup.freeze(field); }); } else { // Freeze all items flexColumns.forEach(({ 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. */ const hydrateColumnsWidth = (rawState, dimensions) => { const columnsLookup = {}; let totalFlexUnits = 0; let widthAllocatedBeforeFlex = 0; const 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.orderedFields.forEach(columnField => { const newColumn = (0, _extends2.default)({}, rawState.lookup[columnField]); if (rawState.columnVisibilityModel[columnField] === false) { newColumn.computedWidth = 0; } else { let computedWidth; if (newColumn.flex && newColumn.flex > 0) { totalFlexUnits += newColumn.flex; computedWidth = 0; flexColumns.push(newColumn); } else { computedWidth = (0, _utils.clamp)(newColumn.width || _colDef.GRID_STRING_COL_DEF.width, newColumn.minWidth || _colDef.GRID_STRING_COL_DEF.minWidth, newColumn.maxWidth || _colDef.GRID_STRING_COL_DEF.maxWidth); } widthAllocatedBeforeFlex += computedWidth; newColumn.computedWidth = computedWidth; } columnsLookup[columnField] = newColumn; }); const availableWidth = dimensions === undefined ? 0 : dimensions.viewportOuterSize.width - (dimensions.hasScrollY ? dimensions.scrollbarSize : 0); const initialFreeSpace = Math.max(availableWidth - widthAllocatedBeforeFlex, 0); // Allocate the remaining space to the flex columns if (totalFlexUnits > 0 && availableWidth > 0) { const computedColumnWidths = computeFlexColumnsWidth({ initialFreeSpace, totalFlexUnits, flexColumns }); Object.keys(computedColumnWidths).forEach(field => { columnsLookup[field].computedWidth = computedColumnWidths[field].computedWidth; }); } return (0, _extends2.default)({}, rawState, { lookup: columnsLookup }); }; /** * Apply the order and the dimensions of the initial state. * The columns not registered in `orderedFields` will be placed after the imported columns. */ exports.hydrateColumnsWidth = hydrateColumnsWidth; const applyInitialState = (columnsState, initialState) => { if (!initialState) { return columnsState; } const { orderedFields = [], dimensions = {} } = initialState; const columnsWithUpdatedDimensions = Object.keys(dimensions); if (columnsWithUpdatedDimensions.length === 0 && orderedFields.length === 0) { return columnsState; } const orderedFieldsLookup = {}; const cleanOrderedFields = []; for (let i = 0; i < orderedFields.length; i += 1) { const 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); } } const newOrderedFields = cleanOrderedFields.length === 0 ? columnsState.orderedFields : [...cleanOrderedFields, ...columnsState.orderedFields.filter(field => !orderedFieldsLookup[field])]; const newColumnLookup = (0, _extends2.default)({}, columnsState.lookup); for (let i = 0; i < columnsWithUpdatedDimensions.length; i += 1) { const field = columnsWithUpdatedDimensions[i]; const newColDef = (0, _extends2.default)({}, newColumnLookup[field], { hasBeenResized: true }); Object.entries(dimensions[field]).forEach(([key, value]) => { newColDef[key] = value === -1 ? Infinity : value; }); newColumnLookup[field] = newColDef; } const newColumnsState = (0, _extends2.default)({}, columnsState, { orderedFields: newOrderedFields, lookup: newColumnLookup }); return newColumnsState; }; exports.applyInitialState = applyInitialState; function getDefaultColTypeDef(type) { let colDef = COLUMN_TYPES[_colDef.DEFAULT_GRID_COL_TYPE_KEY]; if (type && COLUMN_TYPES[type]) { colDef = COLUMN_TYPES[type]; } return colDef; } const createColumnsState = ({ apiRef, columnsToUpsert, initialState, columnVisibilityModel = (0, _gridColumnsSelector.gridColumnVisibilityModelSelector)(apiRef), keepOnlyColumnsToUpsert = false }) => { const isInsideStateInitializer = !apiRef.current.state.columns; let columnsState; if (isInsideStateInitializer) { columnsState = { orderedFields: [], lookup: {}, columnVisibilityModel }; } else { const currentState = (0, _gridColumnsSelector.gridColumnsStateSelector)(apiRef.current.state); columnsState = { orderedFields: keepOnlyColumnsToUpsert ? [] : [...currentState.orderedFields], lookup: (0, _extends2.default)({}, currentState.lookup), // Will be cleaned later if keepOnlyColumnsToUpsert=true columnVisibilityModel }; } let columnsToKeep = {}; if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) { columnsToKeep = Object.keys(columnsState.lookup).reduce((acc, key) => (0, _extends2.default)({}, acc, { [key]: false }), {}); } const columnsToUpsertLookup = {}; columnsToUpsert.forEach(newColumn => { const { field } = newColumn; columnsToUpsertLookup[field] = true; columnsToKeep[field] = true; let existingState = columnsState.lookup[field]; if (existingState == null) { existingState = (0, _extends2.default)({}, getDefaultColTypeDef(newColumn.type), { field, hasBeenResized: false }); columnsState.orderedFields.push(field); } else if (keepOnlyColumnsToUpsert) { columnsState.orderedFields.push(field); } // If the column type has changed - merge the existing state with the default column type definition if (existingState && existingState.type !== newColumn.type) { existingState = (0, _extends2.default)({}, getDefaultColTypeDef(newColumn.type), { field }); } let hasBeenResized = existingState.hasBeenResized; COLUMNS_DIMENSION_PROPERTIES.forEach(key => { if (newColumn[key] !== undefined) { hasBeenResized = true; if (newColumn[key] === -1) { newColumn[key] = Infinity; } } }); columnsState.lookup[field] = (0, _extends2.default)({}, existingState, newColumn, { hasBeenResized }); }); if (keepOnlyColumnsToUpsert && !isInsideStateInitializer) { Object.keys(columnsState.lookup).forEach(field => { if (!columnsToKeep[field]) { delete columnsState.lookup[field]; } }); } const columnsStateWithPreProcessing = apiRef.current.unstable_applyPipeProcessors('hydrateColumns', columnsState); const columnsStateWithPortableColumns = applyInitialState(columnsStateWithPreProcessing, initialState); return hydrateColumnsWidth(columnsStateWithPortableColumns, apiRef.current.getRootDimensions?.() ?? undefined); }; exports.createColumnsState = createColumnsState; function getFirstNonSpannedColumnToRender({ firstColumnToRender, apiRef, firstRowToRender, lastRowToRender, visibleRows }) { let firstNonSpannedColumnToRender = firstColumnToRender; for (let i = firstRowToRender; i < lastRowToRender; i += 1) { const row = visibleRows[i]; if (row) { const rowId = visibleRows[i].id; const cellColSpanInfo = apiRef.current.unstable_getCellColSpanInfo(rowId, firstColumnToRender); if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) { firstNonSpannedColumnToRender = cellColSpanInfo.leftVisibleCellIndex; } } } return firstNonSpannedColumnToRender; } function getTotalHeaderHeight(apiRef, props) { const densityFactor = (0, _densitySelector.gridDensityFactorSelector)(apiRef); const maxDepth = (0, _gridColumnGroupsSelector.gridColumnGroupsHeaderMaxDepthSelector)(apiRef); const isHeaderFilteringEnabled = (0, _gridHeaderFilteringSelectors.gridHeaderFilteringEnabledSelector)(apiRef); const columnHeadersHeight = Math.floor(props.columnHeaderHeight * densityFactor); const filterHeadersHeight = isHeaderFilteringEnabled ? Math.floor((props.headerFilterHeight ?? props.columnHeaderHeight) * densityFactor) : 0; return columnHeadersHeight * (1 + (maxDepth ?? 0)) + filterHeadersHeight; }