@mui/x-data-grid-pro
Version:
The Pro plan edition of the MUI X Data Grid components.
327 lines (324 loc) • 11.9 kB
JavaScript
"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";