UNPKG

@mui/x-data-grid-pro

Version:

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

327 lines (324 loc) 11.9 kB
"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.GridMultiSelectCell = GridMultiSelectCell; exports.renderMultiSelectCell = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var React = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _clsx = _interopRequireDefault(require("clsx")); var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses")); var _styles = require("@mui/material/styles"); var _xDataGrid = require("@mui/x-data-grid"); var _internals = require("@mui/x-data-grid/internals"); var _useGridPrivateApiContext = require("../../hooks/utils/useGridPrivateApiContext"); var _GridMultiSelectChips = require("./GridMultiSelectChips"); var _jsxRuntime = require("react/jsx-runtime"); const useUtilityClasses = ownerState => { const { classes } = ownerState; const slots = { root: ['multiSelectCell'], chip: ['multiSelectCellChip'], chipHidden: ['multiSelectCellChip--hidden'], overflow: ['multiSelectCellOverflow'], popup: ['multiSelectCellPopup'], popperContent: ['multiSelectCellPopperContent'] }; return (0, _composeClasses.default)(slots, _xDataGrid.getDataGridUtilityClass, classes); }; const GridMultiSelectCellPopperContent = (0, _styles.styled)('div', { name: 'MuiDataGrid', slot: 'MultiSelectCellPopperContent' })(({ theme }) => (0, _extends2.default)({}, theme.typography.body2, { letterSpacing: 'normal', padding: theme.spacing(1), maxHeight: 52 * 4, overflow: 'auto', width: 'var(--_width)', border: `1px solid ${(theme.vars || theme).palette.divider}`, boxSizing: 'border-box', display: 'flex', flexWrap: 'wrap', gap: theme.spacing(0.5) })); const GridMultiSelectCellPopper = (0, _styles.styled)(_internals.NotRendered, { name: 'MuiDataGrid', slot: 'MultiSelectCellPopper' })(({ theme }) => ({ zIndex: _internals.vars.zIndex.menu, background: (theme.vars || theme).palette.background.paper, '&[data-popper-reference-hidden]': { visibility: 'hidden', pointerEvents: 'none' } })); function GridMultiSelectCell(props) { const { id, value, colDef, hasFocus, row, slotProps } = props; const popupId = `${id}-${colDef.field}-multiselect-popup`; const rootProps = (0, _xDataGrid.useGridRootProps)(); const apiRef = (0, _xDataGrid.useGridApiContext)(); const privateApiRef = (0, _useGridPrivateApiContext.useGridPrivateApiContext)(); const classes = useUtilityClasses(rootProps); const rowHeight = (0, _xDataGrid.useGridSelector)(apiRef, _internals.gridRowHeightSelector); const filterModel = (0, _xDataGrid.useGridSelector)(apiRef, _internals.gridFilterModelSelector); const isAutoHeight = (0, _xDataGrid.useGridSelector)(privateApiRef, () => privateApiRef.current.rowHasAutoHeight(id)); const [popupOpen, setPopupOpen] = React.useState(false); const cellRef = React.useRef(null); const overflowChipRef = React.useRef(null); const getOptionValue = colDef.getOptionValue; const getOptionLabel = colDef.getOptionLabel; const valueOptions = (0, _internals.isMultiSelectColDef)(colDef) ? (0, _internals.getValueOptions)(colDef, { id, row }) : null; const optionByValue = React.useMemo(() => { const map = new Map(); if (valueOptions) { for (const opt of valueOptions) { map.set(getOptionValue(opt), opt); } } return map; }, [valueOptions, getOptionValue]); // Reorder array to show filtered value first (improves UX when filtering). const arrayValue = React.useMemo(() => { const rawArrayValue = Array.isArray(value) ? value : []; if (rawArrayValue.length === 0) { return rawArrayValue; } const activeFilter = filterModel.items.find(item => item.field === colDef.field && item.operator === 'contains' && Array.isArray(item.value) && item.value.length > 0); if (!activeFilter) { return rawArrayValue; } const filterValues = activeFilter.value; const index = rawArrayValue.findIndex(v => filterValues.includes(v)); if (index <= 0) { return rawArrayValue; } const reordered = [...rawArrayValue]; const [match] = reordered.splice(index, 1); reordered.unshift(match); return reordered; }, [value, filterModel.items, colDef.field]); // Focus the overflow chip when the cell is focused, and close the popup on focus loss. // Centralized in a single effect to avoid registering a `cellFocusOut` listener per // cell (EventManager warns past 20). Covers mount-with-focus (e.g. after exiting edit // mode via Escape, which doesn't publish a focus event) and popup-close-while-focused. React.useEffect(() => { if (isAutoHeight) { return; } if (!hasFocus) { setPopupOpen(false); return; } if (popupOpen) { return; } if (overflowChipRef.current && overflowChipRef.current !== document.activeElement) { overflowChipRef.current.focus(); } }, [hasFocus, popupOpen, isAutoHeight]); const handleOverflowClick = event => { // event.detail === 0 means keyboard-triggered click (Enter keyup on focused button). // Ignore these to prevent popup from opening when focus moves to this cell via Enter. if (event.detail === 0) { return; } event.stopPropagation(); setPopupOpen(true); }; const handleOverflowKeyDown = event => { if (event.key === ' ' && !event.shiftKey) { event.preventDefault(); event.stopPropagation(); setPopupOpen(prev => !prev); } if (event.key === 'Escape' && popupOpen) { event.stopPropagation(); setPopupOpen(false); } }; const handleClickAway = () => { setPopupOpen(false); }; if (arrayValue.length === 0) { return null; } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_GridMultiSelectChips.GridMultiSelectChips, { ref: cellRef, values: arrayValue, field: colDef.field, columnWidth: colDef.computedWidth, autoWrap: isAutoHeight, optionByValue: optionByValue, getOptionLabel: getOptionLabel, classes: { root: classes.root, chip: classes.chip, chipHidden: classes.chipHidden, overflow: classes.overflow }, slotProps: { root: (0, _extends2.default)({}, slotProps?.root, { className: (0, _clsx.default)(slotProps?.root?.className, hasFocus && 'Mui-focused') }), chip: slotProps?.chip, overflow: (0, _extends2.default)({ ref: overflowChipRef, onClick: handleOverflowClick, onKeyDown: handleOverflowKeyDown, 'aria-haspopup': 'dialog', 'aria-keyshortcuts': 'Space', 'aria-controls': popupOpen ? popupId : undefined, 'aria-expanded': popupOpen, material: { tabIndex: -1, component: 'button' } }, slotProps?.overflowChip) } }), popupOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(GridMultiSelectCellPopper, (0, _extends2.default)({ id: popupId, role: "dialog", "aria-label": colDef.headerName || colDef.field, as: rootProps.slots.basePopper, ownerState: rootProps, open: popupOpen, target: cellRef.current, placement: "bottom-start", onClickAway: handleClickAway, clickAwayMouseEvent: "onMouseDown", flip: true, material: { container: cellRef.current?.closest('[role="row"]'), modifiers: [{ name: 'offset', options: { offset: [-10, -rowHeight] } }] } }, slotProps?.popper, { className: (0, _clsx.default)(classes.popup, slotProps?.popper?.className), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(GridMultiSelectCellPopperContent, (0, _extends2.default)({ tabIndex: -1, onKeyDown: event => { if (event.key === 'Escape') { event.stopPropagation(); setPopupOpen(false); apiRef.current.getCellElement(id, colDef.field)?.focus(); } } }, slotProps?.popperContent, { className: (0, _clsx.default)(classes.popperContent, slotProps?.popperContent?.className), style: (0, _extends2.default)({ '--_width': `${colDef.computedWidth}px` }, slotProps?.popperContent?.style), children: arrayValue.map((v, index) => { const option = optionByValue.get(v) ?? v; const chipProps = typeof slotProps?.chip === 'function' ? slotProps.chip(option, index) : slotProps?.chip; return /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseChip, (0, _extends2.default)({ label: getOptionLabel(option), size: "small", variant: "outlined" }, chipProps, { className: (0, _clsx.default)(classes.chip, chipProps?.className) }), index); }) })) }))] }); } process.env.NODE_ENV !== "production" ? GridMultiSelectCell.propTypes /* remove-proptypes */ = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "pnpm proptypes" | // ---------------------------------------------------------------------- /** * GridApi that let you manipulate the grid. */ api: _propTypes.default.object.isRequired, /** * The mode of the cell. */ cellMode: _propTypes.default.oneOf(['edit', 'view']).isRequired, /** * The column of the row that the current cell belongs to. */ colDef: _propTypes.default.object.isRequired, /** * The column field of the cell that triggered the event. */ field: _propTypes.default.string.isRequired, /** * The cell value formatted with the column valueFormatter. */ formattedValue: _propTypes.default.any, /** * If true, the cell is the active element. */ hasFocus: _propTypes.default.bool.isRequired, /** * The grid row id. */ id: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]).isRequired, /** * If true, the cell is editable. */ isEditable: _propTypes.default.bool, /** * The row model of the row that the current cell belongs to. */ row: _propTypes.default.any.isRequired, /** * The node of the row that the current cell belongs to. */ rowNode: _propTypes.default.object.isRequired, /** * Props passed to internal components. */ slotProps: _propTypes.default.object, /** * the tabIndex value. */ tabIndex: _propTypes.default.oneOf([-1, 0]).isRequired, /** * The cell value. * If the column has `valueGetter`, use `params.row` to directly access the fields. */ value: _propTypes.default.any } : void 0; const renderMultiSelectCell = params => { if (params.aggregation) { if (params.aggregation.position === 'footer') { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_internals.GridFooterCell, (0, _extends2.default)({}, params)); } return params.formattedValue ?? params.value; } // On group rows, `value` is the grouping key (string) from `groupingValueGetter`, not the array. if (params.rowNode.type === 'group') { return params.formattedValue ?? params.value; } return /*#__PURE__*/(0, _jsxRuntime.jsx)(GridMultiSelectCell, (0, _extends2.default)({}, params)); }; exports.renderMultiSelectCell = renderMultiSelectCell; if (process.env.NODE_ENV !== "production") renderMultiSelectCell.displayName = "renderMultiSelectCell";