UNPKG

@mui/x-data-grid

Version:

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

326 lines (312 loc) 13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildRootGroup = exports.GRID_ROOT_GROUP_ID = exports.GRID_ID_AUTOGENERATED = void 0; exports.checkGridRowIdIsValid = checkGridRowIdIsValid; exports.computeRowsUpdates = computeRowsUpdates; exports.updateCacheWithNewRows = exports.rowHeightWarning = exports.minimalContentHeight = exports.isAutogeneratedRowNode = exports.isAutogeneratedRow = exports.getValidRowHeight = exports.getTreeNodeDescendants = exports.getTopLevelRowCount = exports.getRowsStateFromCache = exports.getRowValue = exports.getRowIdFromRowModel = exports.getRowHeightWarning = exports.createRowsInternalCache = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _gridRowsSelector = require("./gridRowsSelector"); const GRID_ROOT_GROUP_ID = exports.GRID_ROOT_GROUP_ID = `auto-generated-group-node-root`; const GRID_ID_AUTOGENERATED = exports.GRID_ID_AUTOGENERATED = Symbol('mui.id_autogenerated'); const buildRootGroup = () => ({ type: 'group', id: GRID_ROOT_GROUP_ID, depth: -1, groupingField: null, groupingKey: null, isAutoGenerated: true, children: [], childrenFromPath: {}, childrenExpanded: true, parent: null }); /** * A helper function to check if the id provided is valid. * @param {GridRowId} id Id as [[GridRowId]]. * @param {GridRowModel | Partial<GridRowModel>} row Row as [[GridRowModel]]. * @param {string} detailErrorMessage A custom error message to display for invalid IDs */ exports.buildRootGroup = buildRootGroup; function checkGridRowIdIsValid(id, row, detailErrorMessage = 'A row was provided without id in the rows prop:') { if (id == null) { throw new Error(['MUI X: The Data Grid component requires all rows to have a unique `id` property.', 'Alternatively, you can use the `getRowId` prop to specify a custom id for each row.', detailErrorMessage, JSON.stringify(row)].join('\n')); } } const getRowIdFromRowModel = (rowModel, getRowId, detailErrorMessage) => { const id = getRowId ? getRowId(rowModel) : rowModel.id; checkGridRowIdIsValid(id, rowModel, detailErrorMessage); return id; }; exports.getRowIdFromRowModel = getRowIdFromRowModel; const getRowValue = (row, colDef, apiRef) => { const field = colDef.field; if (!colDef || !colDef.valueGetter) { return row[field]; } const value = row[colDef.field]; return colDef.valueGetter(value, row, colDef, apiRef); }; exports.getRowValue = getRowValue; const createRowsInternalCache = ({ rows, getRowId, loading, rowCount }) => { const updates = { type: 'full', rows: [] }; const dataRowIdToModelLookup = {}; for (let i = 0; i < rows.length; i += 1) { const model = rows[i]; const id = getRowIdFromRowModel(model, getRowId); dataRowIdToModelLookup[id] = model; updates.rows.push(id); } return { rowsBeforePartialUpdates: rows, loadingPropBeforePartialUpdates: loading, rowCountPropBeforePartialUpdates: rowCount, updates, dataRowIdToModelLookup }; }; exports.createRowsInternalCache = createRowsInternalCache; const getTopLevelRowCount = ({ tree, rowCountProp = 0 }) => { const rootGroupNode = tree[GRID_ROOT_GROUP_ID]; return Math.max(rowCountProp, rootGroupNode.children.length + (rootGroupNode.footerId == null ? 0 : 1)); }; exports.getTopLevelRowCount = getTopLevelRowCount; const getRowsStateFromCache = ({ apiRef, rowCountProp = 0, loadingProp, previousTree, previousTreeDepths, previousGroupsToFetch }) => { const cache = apiRef.current.caches.rows; // 1. Apply the "rowTreeCreation" family processing. const { tree: unProcessedTree, treeDepths: unProcessedTreeDepths, dataRowIds: unProcessedDataRowIds, groupingName, groupsToFetch = [] } = apiRef.current.applyStrategyProcessor('rowTreeCreation', { previousTree, previousTreeDepths, updates: cache.updates, dataRowIdToModelLookup: cache.dataRowIdToModelLookup, previousGroupsToFetch }); // 2. Apply the "hydrateRows" pipe-processing. const groupingParamsWithHydrateRows = apiRef.current.unstable_applyPipeProcessors('hydrateRows', { tree: unProcessedTree, treeDepths: unProcessedTreeDepths, dataRowIds: unProcessedDataRowIds, dataRowIdToModelLookup: cache.dataRowIdToModelLookup }); // 3. Reset the cache updates apiRef.current.caches.rows.updates = { type: 'partial', actions: { insert: [], modify: [], remove: [] }, idToActionLookup: {} }; return (0, _extends2.default)({}, groupingParamsWithHydrateRows, { totalRowCount: Math.max(rowCountProp, groupingParamsWithHydrateRows.dataRowIds.length), totalTopLevelRowCount: getTopLevelRowCount({ tree: groupingParamsWithHydrateRows.tree, rowCountProp }), groupingName, loading: loadingProp, groupsToFetch }); }; exports.getRowsStateFromCache = getRowsStateFromCache; const isAutogeneratedRow = row => GRID_ID_AUTOGENERATED in row; exports.isAutogeneratedRow = isAutogeneratedRow; const isAutogeneratedRowNode = rowNode => rowNode.type === 'skeletonRow' || rowNode.type === 'footer' || rowNode.type === 'group' && rowNode.isAutoGenerated || rowNode.type === 'pinnedRow' && rowNode.isAutoGenerated; exports.isAutogeneratedRowNode = isAutogeneratedRowNode; const getTreeNodeDescendants = (tree, parentId, skipAutoGeneratedRows, directChildrenOnly) => { const node = tree[parentId]; if (node.type !== 'group') { return []; } const validDescendants = []; for (let i = 0; i < node.children.length; i += 1) { const child = node.children[i]; if (!skipAutoGeneratedRows || !isAutogeneratedRowNode(tree[child])) { validDescendants.push(child); } if (directChildrenOnly) { continue; } const childDescendants = getTreeNodeDescendants(tree, child, skipAutoGeneratedRows, directChildrenOnly); for (let j = 0; j < childDescendants.length; j += 1) { validDescendants.push(childDescendants[j]); } } if (!skipAutoGeneratedRows && node.footerId != null) { validDescendants.push(node.footerId); } return validDescendants; }; exports.getTreeNodeDescendants = getTreeNodeDescendants; const updateCacheWithNewRows = ({ previousCache, getRowId, updates, groupKeys }) => { if (previousCache.updates.type === 'full') { throw new Error('MUI X: Unable to prepare a partial update if a full update is not applied yet.'); } // Remove duplicate updates. // A server can batch updates, and send several updates for the same row in one fn call. const uniqueUpdates = new Map(); updates.forEach(update => { const id = getRowIdFromRowModel(update, getRowId, 'A row was provided without id when calling updateRows():'); if (uniqueUpdates.has(id)) { uniqueUpdates.set(id, (0, _extends2.default)({}, uniqueUpdates.get(id), update)); } else { uniqueUpdates.set(id, update); } }); const partialUpdates = { type: 'partial', actions: { insert: [...(previousCache.updates.actions.insert ?? [])], modify: [...(previousCache.updates.actions.modify ?? [])], remove: [...(previousCache.updates.actions.remove ?? [])] }, idToActionLookup: (0, _extends2.default)({}, previousCache.updates.idToActionLookup), groupKeys }; const dataRowIdToModelLookup = (0, _extends2.default)({}, previousCache.dataRowIdToModelLookup); const alreadyAppliedActionsToRemove = { insert: {}, modify: {}, remove: {} }; // Depending on the action already applied to the data row, // We might want drop the already-applied-update. // For instance: // - if you delete then insert, then you don't want to apply the deletion in the tree. // - if you insert, then modify, then you just want to apply the insertion in the tree. uniqueUpdates.forEach((partialRow, id) => { const actionAlreadyAppliedToRow = partialUpdates.idToActionLookup[id]; // Action === "delete" // eslint-disable-next-line no-underscore-dangle if (partialRow._action === 'delete') { // If the data row has been removed since the last state update, // Then do nothing. if (actionAlreadyAppliedToRow === 'remove' || !dataRowIdToModelLookup[id]) { return; } // If the data row has been inserted / modified since the last state update, // Then drop this "insert" / "modify" update. if (actionAlreadyAppliedToRow != null) { alreadyAppliedActionsToRemove[actionAlreadyAppliedToRow][id] = true; } // Remove the data row from the lookups and add it to the "delete" update. partialUpdates.actions.remove.push(id); delete dataRowIdToModelLookup[id]; return; } const oldRow = dataRowIdToModelLookup[id]; // Action === "modify" if (oldRow) { // If the data row has been removed since the last state update, // Then drop this "remove" update and add it to the "modify" update instead. if (actionAlreadyAppliedToRow === 'remove') { alreadyAppliedActionsToRemove.remove[id] = true; partialUpdates.actions.modify.push(id); } // If the date has not been inserted / modified since the last state update, // Then add it to the "modify" update (if it has been inserted it should just remain "inserted"). else if (actionAlreadyAppliedToRow == null) { partialUpdates.actions.modify.push(id); } // Update the data row lookups. dataRowIdToModelLookup[id] = (0, _extends2.default)({}, oldRow, partialRow); return; } // Action === "insert" // If the data row has been removed since the last state update, // Then drop the "remove" update and add it to the "insert" update instead. if (actionAlreadyAppliedToRow === 'remove') { alreadyAppliedActionsToRemove.remove[id] = true; partialUpdates.actions.insert.push(id); } // If the data row has not been inserted since the last state update, // Then add it to the "insert" update. // `actionAlreadyAppliedToRow` can't be equal to "modify", otherwise we would have an `oldRow` above. else if (actionAlreadyAppliedToRow == null) { partialUpdates.actions.insert.push(id); } // Update the data row lookups. dataRowIdToModelLookup[id] = partialRow; }); const actionTypeWithActionsToRemove = Object.keys(alreadyAppliedActionsToRemove); for (let i = 0; i < actionTypeWithActionsToRemove.length; i += 1) { const actionType = actionTypeWithActionsToRemove[i]; const idsToRemove = alreadyAppliedActionsToRemove[actionType]; if (Object.keys(idsToRemove).length > 0) { partialUpdates.actions[actionType] = partialUpdates.actions[actionType].filter(id => !idsToRemove[id]); } } return { dataRowIdToModelLookup, updates: partialUpdates, rowsBeforePartialUpdates: previousCache.rowsBeforePartialUpdates, loadingPropBeforePartialUpdates: previousCache.loadingPropBeforePartialUpdates, rowCountPropBeforePartialUpdates: previousCache.rowCountPropBeforePartialUpdates }; }; exports.updateCacheWithNewRows = updateCacheWithNewRows; const minimalContentHeight = exports.minimalContentHeight = 'var(--DataGrid-overlayHeight, calc(var(--height) * 2))'; function computeRowsUpdates(apiRef, updates, getRowId) { const nonPinnedRowsUpdates = []; updates.forEach(update => { const id = getRowIdFromRowModel(update, getRowId, 'A row was provided without id when calling updateRows():'); const rowNode = (0, _gridRowsSelector.gridRowNodeSelector)(apiRef, id); if (rowNode?.type === 'pinnedRow') { // @ts-ignore because otherwise `release:build` doesn't work const pinnedRowsCache = apiRef.current.caches.pinnedRows; const prevModel = pinnedRowsCache.idLookup[id]; if (prevModel) { pinnedRowsCache.idLookup[id] = (0, _extends2.default)({}, prevModel, update); } } else { nonPinnedRowsUpdates.push(update); } }); return nonPinnedRowsUpdates; } let warnedOnceInvalidRowHeight = false; const getValidRowHeight = (rowHeightProp, defaultRowHeight, warningMessage) => { if (typeof rowHeightProp === 'number' && rowHeightProp > 0) { return rowHeightProp; } if (process.env.NODE_ENV !== 'production' && !warnedOnceInvalidRowHeight && typeof rowHeightProp !== 'undefined' && rowHeightProp !== null) { console.warn(warningMessage); warnedOnceInvalidRowHeight = true; } return defaultRowHeight; }; exports.getValidRowHeight = getValidRowHeight; const rowHeightWarning = exports.rowHeightWarning = [`MUI X: The \`rowHeight\` prop should be a number greater than 0.`, `The default value will be used instead.`].join('\n'); const getRowHeightWarning = exports.getRowHeightWarning = [`MUI X: The \`getRowHeight\` prop should return a number greater than 0 or 'auto'.`, `The default value will be used instead.`].join('\n');