UNPKG

@mui/x-data-grid-pro

Version:

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

194 lines (191 loc) 7.78 kB
'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]); };