UNPKG

@mui/x-data-grid-premium

Version:

The Premium plan edition of the Data Grid Components (MUI X).

476 lines (473 loc) 20.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useGridCellSelection = exports.cellSelectionStateInitializer = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var _utils = require("@mui/material/utils"); var _internals = require("@mui/x-data-grid-pro/internals"); var _xDataGridPro = require("@mui/x-data-grid-pro"); var _gridCellSelectionSelector = require("./gridCellSelectionSelector"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const cellSelectionStateInitializer = (state, props) => (0, _extends2.default)({}, state, { cellSelection: (0, _extends2.default)({}, props.cellSelectionModel ?? props.initialState?.cellSelection) }); exports.cellSelectionStateInitializer = cellSelectionStateInitializer; function isKeyboardEvent(event) { return !!event.key; } const AUTO_SCROLL_SENSITIVITY = 50; // The distance from the edge to start scrolling const AUTO_SCROLL_SPEED = 20; // The speed to scroll once the mouse enters the sensitivity area const useGridCellSelection = (apiRef, props) => { const visibleRows = (0, _internals.useGridVisibleRows)(apiRef, props); const cellWithVirtualFocus = React.useRef(); const lastMouseDownCell = React.useRef(); const mousePosition = React.useRef(null); const autoScrollRAF = React.useRef(); const sortedRowIds = (0, _xDataGridPro.useGridSelector)(apiRef, _xDataGridPro.gridSortedRowIdsSelector); const dimensions = (0, _xDataGridPro.useGridSelector)(apiRef, _xDataGridPro.gridDimensionsSelector); const totalHeaderHeight = (0, _internals.getTotalHeaderHeight)(apiRef, props); const ignoreValueFormatterProp = props.ignoreValueFormatterDuringExport; const ignoreValueFormatter = (typeof ignoreValueFormatterProp === 'object' ? ignoreValueFormatterProp?.clipboardExport : ignoreValueFormatterProp) || false; const clipboardCopyCellDelimiter = props.clipboardCopyCellDelimiter; apiRef.current.registerControlState({ stateId: 'cellSelection', propModel: props.cellSelectionModel, propOnChange: props.onCellSelectionModelChange, stateSelector: _gridCellSelectionSelector.gridCellSelectionStateSelector, changeEvent: 'cellSelectionChange' }); const runIfCellSelectionIsEnabled = callback => (...args) => { if (props.cellSelection) { callback(...args); } }; const isCellSelected = React.useCallback((id, field) => { if (!props.cellSelection) { return false; } const cellSelectionModel = (0, _gridCellSelectionSelector.gridCellSelectionStateSelector)(apiRef.current.state); return cellSelectionModel[id] ? !!cellSelectionModel[id][field] : false; }, [apiRef, props.cellSelection]); const getCellSelectionModel = React.useCallback(() => { return (0, _gridCellSelectionSelector.gridCellSelectionStateSelector)(apiRef.current.state); }, [apiRef]); const setCellSelectionModel = React.useCallback(newModel => { if (!props.cellSelection) { return; } apiRef.current.setState(prevState => (0, _extends2.default)({}, prevState, { cellSelection: newModel })); apiRef.current.forceUpdate(); }, [apiRef, props.cellSelection]); const selectCellRange = React.useCallback((start, end, keepOtherSelected = false) => { const startRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(start.id); const startColumnIndex = apiRef.current.getColumnIndex(start.field); const endRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(end.id); const endColumnIndex = apiRef.current.getColumnIndex(end.field); let finalStartRowIndex = startRowIndex; let finalStartColumnIndex = startColumnIndex; let finalEndRowIndex = endRowIndex; let finalEndColumnIndex = endColumnIndex; if (finalStartRowIndex > finalEndRowIndex) { finalStartRowIndex = endRowIndex; finalEndRowIndex = startRowIndex; } if (finalStartColumnIndex > finalEndColumnIndex) { finalStartColumnIndex = endColumnIndex; finalEndColumnIndex = startColumnIndex; } const visibleColumns = apiRef.current.getVisibleColumns(); const rowsInRange = visibleRows.rows.slice(finalStartRowIndex, finalEndRowIndex + 1); const columnsInRange = visibleColumns.slice(finalStartColumnIndex, finalEndColumnIndex + 1); const newModel = keepOtherSelected ? apiRef.current.getCellSelectionModel() : {}; rowsInRange.forEach(row => { if (!newModel[row.id]) { newModel[row.id] = {}; } columnsInRange.forEach(column => { newModel[row.id][column.field] = true; }, {}); }); apiRef.current.setCellSelectionModel(newModel); }, [apiRef, visibleRows.rows]); const getSelectedCellsAsArray = React.useCallback(() => { const model = apiRef.current.getCellSelectionModel(); const idToIdLookup = (0, _xDataGridPro.gridRowsDataRowIdToIdLookupSelector)(apiRef); return Object.entries(model).reduce((acc, [id, fields]) => [...acc, ...Object.entries(fields).reduce((acc2, [field, isSelected]) => { return isSelected ? [...acc2, { id: idToIdLookup[id], field }] : acc2; }, [])], []); }, [apiRef]); const cellSelectionApi = { isCellSelected, getCellSelectionModel, setCellSelectionModel, selectCellRange, getSelectedCellsAsArray }; (0, _xDataGridPro.useGridApiMethod)(apiRef, cellSelectionApi, 'public'); const hasClickedValidCellForRangeSelection = React.useCallback(params => { if (params.field === _xDataGridPro.GRID_CHECKBOX_SELECTION_COL_DEF.field) { return false; } if (params.field === _xDataGridPro.GRID_DETAIL_PANEL_TOGGLE_FIELD) { return false; } const column = apiRef.current.getColumn(params.field); if (column.type === _xDataGridPro.GRID_ACTIONS_COLUMN_TYPE) { return false; } return params.rowNode.type !== 'pinnedRow'; }, [apiRef]); const handleMouseUp = (0, _utils.useEventCallback)(() => { lastMouseDownCell.current = null; apiRef.current.rootElementRef?.current?.classList.remove(_xDataGridPro.gridClasses['root--disableUserSelection']); // eslint-disable-next-line @typescript-eslint/no-use-before-define stopAutoScroll(); }); const handleCellMouseDown = React.useCallback((params, event) => { // Skip if the click comes from the right-button or, only on macOS, Ctrl is pressed // Fix for https://github.com/mui/mui-x/pull/6567#issuecomment-1329155578 const isMacOs = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0; if (event.button !== 0 || event.ctrlKey && isMacOs) { return; } if (params.field === _xDataGridPro.GRID_REORDER_COL_DEF.field) { return; } const focusedCell = (0, _xDataGridPro.gridFocusCellSelector)(apiRef); if (hasClickedValidCellForRangeSelection(params) && event.shiftKey && focusedCell) { event.preventDefault(); } lastMouseDownCell.current = { id: params.id, field: params.field }; apiRef.current.rootElementRef?.current?.classList.add(_xDataGridPro.gridClasses['root--disableUserSelection']); const document = (0, _utils.ownerDocument)(apiRef.current.rootElementRef?.current); document.addEventListener('mouseup', handleMouseUp, { once: true }); }, [apiRef, handleMouseUp, hasClickedValidCellForRangeSelection]); const stopAutoScroll = React.useCallback(() => { if (autoScrollRAF.current) { cancelAnimationFrame(autoScrollRAF.current); autoScrollRAF.current = null; } }, []); const handleCellFocusIn = React.useCallback(params => { cellWithVirtualFocus.current = { id: params.id, field: params.field }; }, []); const startAutoScroll = React.useCallback(() => { if (autoScrollRAF.current) { return; } if (!apiRef.current.virtualScrollerRef?.current) { return; } function autoScroll() { if (!mousePosition.current || !apiRef.current.virtualScrollerRef?.current) { return; } const { x: mouseX, y: mouseY } = mousePosition.current; const { height, width } = dimensions.viewportInnerSize; let deltaX = 0; let deltaY = 0; let factor = 0; if (mouseY <= AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollY) { // When scrolling up, the multiplier increases going closer to the top edge factor = (AUTO_SCROLL_SENSITIVITY - mouseY) / -AUTO_SCROLL_SENSITIVITY; deltaY = AUTO_SCROLL_SPEED; } else if (mouseY >= height - AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollY) { // When scrolling down, the multiplier increases going closer to the bottom edge factor = (mouseY - (height - AUTO_SCROLL_SENSITIVITY)) / AUTO_SCROLL_SENSITIVITY; deltaY = AUTO_SCROLL_SPEED; } else if (mouseX <= AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollX) { // When scrolling left, the multiplier increases going closer to the left edge factor = (AUTO_SCROLL_SENSITIVITY - mouseX) / -AUTO_SCROLL_SENSITIVITY; deltaX = AUTO_SCROLL_SPEED; } else if (mouseX >= width - AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollX) { // When scrolling right, the multiplier increases going closer to the right edge factor = (mouseX - (width - AUTO_SCROLL_SENSITIVITY)) / AUTO_SCROLL_SENSITIVITY; deltaX = AUTO_SCROLL_SPEED; } if (deltaX !== 0 || deltaY !== 0) { const { scrollLeft, scrollTop } = apiRef.current.virtualScrollerRef.current; apiRef.current.scroll({ top: scrollTop + deltaY * factor, left: scrollLeft + deltaX * factor }); } autoScrollRAF.current = requestAnimationFrame(autoScroll); } autoScroll(); }, [apiRef, dimensions]); const handleCellMouseOver = React.useCallback((params, event) => { if (!lastMouseDownCell.current) { return; } const { id, field } = params; apiRef.current.selectCellRange(lastMouseDownCell.current, { id, field }, event.ctrlKey || event.metaKey); const virtualScrollerRect = apiRef.current.virtualScrollerRef?.current?.getBoundingClientRect(); if (!virtualScrollerRect) { return; } const { x, y } = virtualScrollerRect; const { height, width } = dimensions.viewportInnerSize; const mouseX = event.clientX - x; const mouseY = event.clientY - y - totalHeaderHeight; mousePosition.current = { x: mouseX, y: mouseY }; const hasEnteredVerticalSensitivityArea = mouseY <= AUTO_SCROLL_SENSITIVITY || mouseY >= height - AUTO_SCROLL_SENSITIVITY; const hasEnteredHorizontalSensitivityArea = mouseX <= AUTO_SCROLL_SENSITIVITY || mouseX >= width - AUTO_SCROLL_SENSITIVITY; const hasEnteredSensitivityArea = hasEnteredVerticalSensitivityArea || hasEnteredHorizontalSensitivityArea; if (hasEnteredSensitivityArea) { // Mouse has entered the sensitity area for the first time startAutoScroll(); } else { // Mouse has left the sensitivity area while auto scroll is on stopAutoScroll(); } }, [apiRef, startAutoScroll, stopAutoScroll, totalHeaderHeight, dimensions]); const handleCellClick = (0, _utils.useEventCallback)((params, event) => { const { id, field } = params; if (!hasClickedValidCellForRangeSelection(params)) { return; } const focusedCell = (0, _xDataGridPro.gridFocusCellSelector)(apiRef); if (event.shiftKey && focusedCell) { apiRef.current.selectCellRange(focusedCell, { id, field }); cellWithVirtualFocus.current = { id, field }; return; } if (event.ctrlKey || event.metaKey) { // Add the clicked cell to the selection const prevModel = apiRef.current.getCellSelectionModel(); apiRef.current.setCellSelectionModel((0, _extends2.default)({}, prevModel, { [id]: (0, _extends2.default)({}, prevModel[id], { [field]: !apiRef.current.isCellSelected(id, field) }) })); } else { // Clear the selection and keep only the clicked cell selected apiRef.current.setCellSelectionModel({ [id]: { [field]: true } }); } }); const handleCellKeyDown = (0, _utils.useEventCallback)((params, event) => { if (!(0, _internals.isNavigationKey)(event.key) || !cellWithVirtualFocus.current) { return; } if (!event.shiftKey) { apiRef.current.setCellSelectionModel({}); return; } const { current: otherCell } = cellWithVirtualFocus; let endRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(otherCell.id); let endColumnIndex = apiRef.current.getColumnIndex(otherCell.field); if (event.key === 'ArrowDown') { endRowIndex += 1; } else if (event.key === 'ArrowUp') { endRowIndex -= 1; } else if (event.key === 'ArrowRight') { endColumnIndex += 1; } else if (event.key === 'ArrowLeft') { endColumnIndex -= 1; } if (endRowIndex < 0 || endRowIndex >= visibleRows.rows.length) { return; } const visibleColumns = apiRef.current.getVisibleColumns(); if (endColumnIndex < 0 || endColumnIndex >= visibleColumns.length) { return; } cellWithVirtualFocus.current = { id: visibleRows.rows[endRowIndex].id, field: visibleColumns[endColumnIndex].field }; apiRef.current.scrollToIndexes({ rowIndex: endRowIndex, colIndex: endColumnIndex }); const { id, field } = params; apiRef.current.selectCellRange({ id, field }, cellWithVirtualFocus.current); }); (0, _xDataGridPro.useGridApiEventHandler)(apiRef, 'cellClick', runIfCellSelectionIsEnabled(handleCellClick)); (0, _xDataGridPro.useGridApiEventHandler)(apiRef, 'cellFocusIn', runIfCellSelectionIsEnabled(handleCellFocusIn)); (0, _xDataGridPro.useGridApiEventHandler)(apiRef, 'cellKeyDown', runIfCellSelectionIsEnabled(handleCellKeyDown)); (0, _xDataGridPro.useGridApiEventHandler)(apiRef, 'cellMouseDown', runIfCellSelectionIsEnabled(handleCellMouseDown)); (0, _xDataGridPro.useGridApiEventHandler)(apiRef, 'cellMouseOver', runIfCellSelectionIsEnabled(handleCellMouseOver)); React.useEffect(() => { if (props.cellSelectionModel) { apiRef.current.setCellSelectionModel(props.cellSelectionModel); } }, [apiRef, props.cellSelectionModel]); React.useEffect(() => { const rootRef = apiRef.current.rootElementRef?.current; return () => { stopAutoScroll(); const document = (0, _utils.ownerDocument)(rootRef); document.removeEventListener('mouseup', handleMouseUp); }; }, [apiRef, handleMouseUp, stopAutoScroll]); const checkIfCellIsSelected = React.useCallback((isSelected, { id, field }) => { return apiRef.current.isCellSelected(id, field); }, [apiRef]); const addClassesToCells = React.useCallback((classes, { id, field }) => { if (!visibleRows.range || !apiRef.current.isCellSelected(id, field)) { return classes; } const newClasses = [...classes]; const rowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(id); const columnIndex = apiRef.current.getColumnIndex(field); const visibleColumns = apiRef.current.getVisibleColumns(); if (rowIndex > 0) { const { id: previousRowId } = visibleRows.rows[rowIndex - 1]; if (!apiRef.current.isCellSelected(previousRowId, field)) { newClasses.push(_xDataGridPro.gridClasses['cell--rangeTop']); } } else { newClasses.push(_xDataGridPro.gridClasses['cell--rangeTop']); } if (rowIndex + visibleRows.range.firstRowIndex < visibleRows.range.lastRowIndex) { const { id: nextRowId } = visibleRows.rows[rowIndex + 1]; if (!apiRef.current.isCellSelected(nextRowId, field)) { newClasses.push(_xDataGridPro.gridClasses['cell--rangeBottom']); } } else { newClasses.push(_xDataGridPro.gridClasses['cell--rangeBottom']); } if (columnIndex > 0) { const { field: previousColumnField } = visibleColumns[columnIndex - 1]; if (!apiRef.current.isCellSelected(id, previousColumnField)) { newClasses.push(_xDataGridPro.gridClasses['cell--rangeLeft']); } } else { newClasses.push(_xDataGridPro.gridClasses['cell--rangeLeft']); } if (columnIndex < visibleColumns.length - 1) { const { field: nextColumnField } = visibleColumns[columnIndex + 1]; if (!apiRef.current.isCellSelected(id, nextColumnField)) { newClasses.push(_xDataGridPro.gridClasses['cell--rangeRight']); } } else { newClasses.push(_xDataGridPro.gridClasses['cell--rangeRight']); } return newClasses; }, [apiRef, visibleRows.range, visibleRows.rows]); const canUpdateFocus = React.useCallback((initialValue, { event, cell }) => { if (!cell || !props.cellSelection || !event.shiftKey) { return initialValue; } if (isKeyboardEvent(event)) { return (0, _internals.isNavigationKey)(event.key) ? false : initialValue; } const focusedCell = (0, _xDataGridPro.gridFocusCellSelector)(apiRef); if (hasClickedValidCellForRangeSelection(cell) && focusedCell) { return false; } return initialValue; }, [apiRef, props.cellSelection, hasClickedValidCellForRangeSelection]); const handleClipboardCopy = React.useCallback(value => { if (apiRef.current.getSelectedCellsAsArray().length <= 1) { return value; } const cellSelectionModel = apiRef.current.getCellSelectionModel(); const unsortedSelectedRowIds = Object.keys(cellSelectionModel); const sortedSelectedRowIds = sortedRowIds.filter(id => unsortedSelectedRowIds.includes(`${id}`)); const copyData = sortedSelectedRowIds.reduce((acc, rowId) => { const fieldsMap = cellSelectionModel[rowId]; const rowString = Object.keys(fieldsMap).reduce((acc2, field) => { let cellData; if (fieldsMap[field]) { const cellParams = apiRef.current.getCellParams(rowId, field); cellData = (0, _internals.serializeCellValue)(cellParams, { delimiterCharacter: clipboardCopyCellDelimiter, ignoreValueFormatter, shouldAppendQuotes: false }); } else { cellData = ''; } return acc2 === '' ? cellData : [acc2, cellData].join(clipboardCopyCellDelimiter); }, ''); return acc === '' ? rowString : [acc, rowString].join('\r\n'); }, ''); return copyData; }, [apiRef, ignoreValueFormatter, clipboardCopyCellDelimiter, sortedRowIds]); (0, _internals.useGridRegisterPipeProcessor)(apiRef, 'isCellSelected', checkIfCellIsSelected); (0, _internals.useGridRegisterPipeProcessor)(apiRef, 'cellClassName', addClassesToCells); (0, _internals.useGridRegisterPipeProcessor)(apiRef, 'canUpdateFocus', canUpdateFocus); (0, _internals.useGridRegisterPipeProcessor)(apiRef, 'clipboardCopy', handleClipboardCopy); }; exports.useGridCellSelection = useGridCellSelection;