@mui/x-data-grid-pro
Version:
The Pro plan edition of the MUI X Data Grid components.
194 lines (191 loc) • 7.78 kB
JavaScript
'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useGridSelector, useGridEvent, useGridApiMethod, gridDataRowIdsSelector } from '@mui/x-data-grid';
import { useGridRegisterPipeProcessor } from '@mui/x-data-grid/internals';
import { GRID_DETAIL_PANEL_TOGGLE_FIELD } from "./gridDetailPanelToggleColDef.js";
import { gridDetailPanelExpandedRowIdsSelector, gridDetailPanelExpandedRowsContentCacheSelector, gridDetailPanelRawHeightCacheSelector } from "./gridDetailPanelSelector.js";
const emptySet = new Set();
export const detailPanelStateInitializer = (state, props) => {
return _extends({}, state, {
detailPanel: {
heightCache: {},
expandedRowIds: props.detailPanelExpandedRowIds ?? props.initialState?.detailPanel?.expandedRowIds ?? emptySet
}
});
};
function cacheContentAndHeight(apiRef, getDetailPanelContent, getDetailPanelHeight, previousHeightCache) {
if (typeof getDetailPanelContent !== 'function') {
return {};
}
// TODO change to lazy approach using a Proxy
// only call getDetailPanelContent when asked for an id
const rowIds = gridDataRowIdsSelector(apiRef);
const contentCache = {};
const heightCache = {};
for (let i = 0; i < rowIds.length; i += 1) {
const id = rowIds[i];
const params = apiRef.current.getRowParams(id);
const content = getDetailPanelContent(params);
contentCache[id] = content;
if (content == null) {
continue;
}
const height = getDetailPanelHeight(params);
const autoHeight = height === 'auto';
heightCache[id] = {
autoHeight,
height: autoHeight ? previousHeightCache[id]?.height : height
};
}
return {
contentCache,
heightCache
};
}
export const useGridDetailPanel = (apiRef, props) => {
const contentCache = useGridSelector(apiRef, gridDetailPanelExpandedRowsContentCacheSelector);
const handleCellClick = React.useCallback((params, event) => {
if (params.field !== GRID_DETAIL_PANEL_TOGGLE_FIELD || props.getDetailPanelContent == null) {
return;
}
const content = contentCache[params.id];
if (! /*#__PURE__*/React.isValidElement(content)) {
return;
}
// Ignore if the user didn't click specifically in the "i" button
if (event.target === event.currentTarget) {
return;
}
apiRef.current.toggleDetailPanel(params.id);
}, [apiRef, contentCache, props.getDetailPanelContent]);
const handleCellKeyDown = React.useCallback((params, event) => {
if (props.getDetailPanelContent == null) {
return;
}
if (params.field === GRID_DETAIL_PANEL_TOGGLE_FIELD && event.key === ' ') {
apiRef.current.toggleDetailPanel(params.id);
}
}, [apiRef, props.getDetailPanelContent]);
useGridEvent(apiRef, 'cellClick', handleCellClick);
useGridEvent(apiRef, 'cellKeyDown', handleCellKeyDown);
apiRef.current.registerControlState({
stateId: 'detailPanels',
propModel: props.detailPanelExpandedRowIds,
propOnChange: props.onDetailPanelExpandedRowIdsChange,
stateSelector: gridDetailPanelExpandedRowIdsSelector,
changeEvent: 'detailPanelsExpandedRowIdsChange'
});
const toggleDetailPanel = React.useCallback(id => {
if (props.getDetailPanelContent == null) {
return;
}
const content = contentCache[id];
if (! /*#__PURE__*/React.isValidElement(content)) {
return;
}
const ids = apiRef.current.getExpandedDetailPanels();
const newIds = new Set(ids);
if (ids.has(id)) {
newIds.delete(id);
} else {
newIds.add(id);
}
apiRef.current.setExpandedDetailPanels(newIds);
}, [apiRef, contentCache, props.getDetailPanelContent]);
const getExpandedDetailPanels = React.useCallback(() => gridDetailPanelExpandedRowIdsSelector(apiRef), [apiRef]);
const setExpandedDetailPanels = React.useCallback(ids => {
apiRef.current.setState(state => {
return _extends({}, state, {
detailPanel: _extends({}, state.detailPanel, {
expandedRowIds: ids
})
});
});
apiRef.current.requestPipeProcessorsApplication('rowHeight');
}, [apiRef]);
const storeDetailPanelHeight = React.useCallback((id, height) => {
const heightCache = gridDetailPanelRawHeightCacheSelector(apiRef);
if (!heightCache[id] || heightCache[id].height === height) {
return;
}
apiRef.current.setState(state => {
return _extends({}, state, {
detailPanel: _extends({}, state.detailPanel, {
heightCache: _extends({}, heightCache, {
[id]: _extends({}, heightCache[id], {
height
})
})
})
});
});
apiRef.current.requestPipeProcessorsApplication('rowHeight');
}, [apiRef]);
const detailPanelPubicApi = {
toggleDetailPanel,
getExpandedDetailPanels,
setExpandedDetailPanels
};
const detailPanelPrivateApi = {
storeDetailPanelHeight
};
useGridApiMethod(apiRef, detailPanelPubicApi, 'public');
useGridApiMethod(apiRef, detailPanelPrivateApi, 'private');
React.useEffect(() => {
if (props.detailPanelExpandedRowIds) {
const currentModel = gridDetailPanelExpandedRowIdsSelector(apiRef);
if (currentModel !== props.detailPanelExpandedRowIds) {
apiRef.current.setExpandedDetailPanels(props.detailPanelExpandedRowIds);
}
}
}, [apiRef, props.detailPanelExpandedRowIds]);
const updateCaches = React.useCallback(() => {
if (!props.getDetailPanelContent) {
return;
}
apiRef.current.setState(state => {
return _extends({}, state, {
detailPanel: _extends({}, state.detailPanel, cacheContentAndHeight(apiRef, props.getDetailPanelContent, props.getDetailPanelHeight, state.detailPanel.heightCache))
});
});
}, [apiRef, props.getDetailPanelContent, props.getDetailPanelHeight]);
useGridEvent(apiRef, 'sortedRowsSet', updateCaches);
const previousGetDetailPanelContentProp = React.useRef(undefined);
const previousGetDetailPanelHeightProp = React.useRef(undefined);
const updateCachesIfNeeded = React.useCallback(() => {
if (props.getDetailPanelContent === previousGetDetailPanelContentProp.current && props.getDetailPanelHeight === previousGetDetailPanelHeightProp.current) {
return;
}
apiRef.current.setState(state => {
return _extends({}, state, {
detailPanel: _extends({}, state.detailPanel, cacheContentAndHeight(apiRef, props.getDetailPanelContent, props.getDetailPanelHeight, state.detailPanel.heightCache))
});
});
previousGetDetailPanelContentProp.current = props.getDetailPanelContent;
previousGetDetailPanelHeightProp.current = props.getDetailPanelHeight;
}, [apiRef, props.getDetailPanelContent, props.getDetailPanelHeight]);
const addDetailHeight = React.useCallback((initialValue, row) => {
const expandedRowIds = gridDetailPanelExpandedRowIdsSelector(apiRef);
if (!expandedRowIds || !expandedRowIds.has(row.id)) {
initialValue.detail = 0;
return initialValue;
}
updateCachesIfNeeded();
const heightCache = gridDetailPanelRawHeightCacheSelector(apiRef);
initialValue.detail = heightCache[row.id]?.height ?? 0; // Fallback to zero because the cache might not be ready yet (for example page was changed)
return initialValue;
}, [apiRef, updateCachesIfNeeded]);
const enabled = props.getDetailPanelContent !== undefined;
useGridRegisterPipeProcessor(apiRef, 'rowHeight', addDetailHeight, enabled);
const isFirstRender = React.useRef(true);
if (isFirstRender.current) {
updateCachesIfNeeded();
}
React.useEffect(() => {
if (!isFirstRender.current) {
updateCachesIfNeeded();
}
isFirstRender.current = false;
}, [apiRef, updateCachesIfNeeded]);
};