264 lines
11 KiB
JavaScript
264 lines
11 KiB
JavaScript
"use strict";
|
|
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.GridActionsCell = GridActionsCell;
|
|
exports.renderActionsCell = void 0;
|
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
var React = _interopRequireWildcard(require("react"));
|
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
var _MenuList = _interopRequireDefault(require("@mui/material/MenuList"));
|
|
var _styles = require("@mui/material/styles");
|
|
var _utils = require("@mui/utils");
|
|
var _gridClasses = require("../../constants/gridClasses");
|
|
var _GridMenu = require("../menu/GridMenu");
|
|
var _useGridRootProps = require("../../hooks/utils/useGridRootProps");
|
|
var _useGridApiContext = require("../../hooks/utils/useGridApiContext");
|
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
const _excluded = ["api", "colDef", "id", "hasFocus", "isEditable", "field", "value", "formattedValue", "row", "rowNode", "cellMode", "tabIndex", "position", "focusElementRef"];
|
|
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 hasActions = colDef => typeof colDef.getActions === 'function';
|
|
function GridActionsCell(props) {
|
|
const {
|
|
colDef,
|
|
id,
|
|
hasFocus,
|
|
tabIndex,
|
|
position = 'bottom-end',
|
|
focusElementRef
|
|
} = props,
|
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
const [focusedButtonIndex, setFocusedButtonIndex] = React.useState(-1);
|
|
const [open, setOpen] = React.useState(false);
|
|
const apiRef = (0, _useGridApiContext.useGridApiContext)();
|
|
const rootRef = React.useRef(null);
|
|
const buttonRef = React.useRef(null);
|
|
const ignoreCallToFocus = React.useRef(false);
|
|
const touchRippleRefs = React.useRef({});
|
|
const theme = (0, _styles.useTheme)();
|
|
const menuId = (0, _utils.unstable_useId)();
|
|
const buttonId = (0, _utils.unstable_useId)();
|
|
const rootProps = (0, _useGridRootProps.useGridRootProps)();
|
|
if (!hasActions(colDef)) {
|
|
throw new Error('MUI: Missing the `getActions` property in the `GridColDef`.');
|
|
}
|
|
const options = colDef.getActions(apiRef.current.getRowParams(id));
|
|
const iconButtons = options.filter(option => !option.props.showInMenu);
|
|
const menuButtons = options.filter(option => option.props.showInMenu);
|
|
const numberOfButtons = iconButtons.length + (menuButtons.length ? 1 : 0);
|
|
React.useLayoutEffect(() => {
|
|
if (!hasFocus) {
|
|
Object.entries(touchRippleRefs.current).forEach(([index, ref]) => {
|
|
ref?.stop({}, () => {
|
|
delete touchRippleRefs.current[index];
|
|
});
|
|
});
|
|
}
|
|
}, [hasFocus]);
|
|
React.useEffect(() => {
|
|
if (focusedButtonIndex < 0 || !rootRef.current) {
|
|
return;
|
|
}
|
|
if (focusedButtonIndex >= rootRef.current.children.length) {
|
|
return;
|
|
}
|
|
const child = rootRef.current.children[focusedButtonIndex];
|
|
child.focus({
|
|
preventScroll: true
|
|
});
|
|
}, [focusedButtonIndex]);
|
|
React.useEffect(() => {
|
|
if (!hasFocus) {
|
|
setFocusedButtonIndex(-1);
|
|
ignoreCallToFocus.current = false;
|
|
}
|
|
}, [hasFocus]);
|
|
React.useImperativeHandle(focusElementRef, () => ({
|
|
focus() {
|
|
// If ignoreCallToFocus is true, then one of the buttons was clicked and the focus is already set
|
|
if (!ignoreCallToFocus.current) {
|
|
// find the first focusable button and pass the index to the state
|
|
const focusableButtonIndex = options.findIndex(o => !o.props.disabled);
|
|
setFocusedButtonIndex(focusableButtonIndex);
|
|
}
|
|
}
|
|
}), [options]);
|
|
React.useEffect(() => {
|
|
if (focusedButtonIndex >= numberOfButtons) {
|
|
setFocusedButtonIndex(numberOfButtons - 1);
|
|
}
|
|
}, [focusedButtonIndex, numberOfButtons]);
|
|
const showMenu = () => {
|
|
setOpen(true);
|
|
setFocusedButtonIndex(numberOfButtons - 1);
|
|
ignoreCallToFocus.current = true;
|
|
};
|
|
const hideMenu = () => {
|
|
setOpen(false);
|
|
};
|
|
const handleTouchRippleRef = index => instance => {
|
|
touchRippleRefs.current[index] = instance;
|
|
};
|
|
const handleButtonClick = (index, onClick) => event => {
|
|
setFocusedButtonIndex(index);
|
|
ignoreCallToFocus.current = true;
|
|
if (onClick) {
|
|
onClick(event);
|
|
}
|
|
};
|
|
const handleRootKeyDown = event => {
|
|
if (numberOfButtons <= 1) {
|
|
return;
|
|
}
|
|
const getNewIndex = (index, direction) => {
|
|
if (index < 0 || index > options.length) {
|
|
return index;
|
|
}
|
|
|
|
// for rtl mode we need to reverse the direction
|
|
const rtlMod = theme.direction === 'rtl' ? -1 : 1;
|
|
const indexMod = (direction === 'left' ? -1 : 1) * rtlMod;
|
|
|
|
// if the button that should receive focus is disabled go one more step
|
|
return options[index + indexMod]?.props.disabled ? getNewIndex(index + indexMod, direction) : index + indexMod;
|
|
};
|
|
let newIndex = focusedButtonIndex;
|
|
if (event.key === 'ArrowRight') {
|
|
newIndex = getNewIndex(focusedButtonIndex, 'right');
|
|
} else if (event.key === 'ArrowLeft') {
|
|
newIndex = getNewIndex(focusedButtonIndex, 'left');
|
|
}
|
|
if (newIndex < 0 || newIndex >= numberOfButtons) {
|
|
return; // We're already in the first or last item = do nothing and let the grid listen the event
|
|
}
|
|
if (newIndex !== focusedButtonIndex) {
|
|
event.preventDefault(); // Prevent scrolling
|
|
event.stopPropagation(); // Don't stop propagation for other keys, e.g. ArrowUp
|
|
setFocusedButtonIndex(newIndex);
|
|
}
|
|
};
|
|
const handleListKeyDown = event => {
|
|
if (event.key === 'Tab') {
|
|
event.preventDefault();
|
|
}
|
|
if (['Tab', 'Escape'].includes(event.key)) {
|
|
hideMenu();
|
|
}
|
|
};
|
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", (0, _extends2.default)({
|
|
role: "menu",
|
|
ref: rootRef,
|
|
tabIndex: -1,
|
|
className: _gridClasses.gridClasses.actionsCell,
|
|
onKeyDown: handleRootKeyDown
|
|
}, other, {
|
|
children: [iconButtons.map((button, index) => /*#__PURE__*/React.cloneElement(button, {
|
|
key: index,
|
|
touchRippleRef: handleTouchRippleRef(index),
|
|
onClick: handleButtonClick(index, button.props.onClick),
|
|
tabIndex: focusedButtonIndex === index ? tabIndex : -1
|
|
})), menuButtons.length > 0 && buttonId && /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseIconButton, (0, _extends2.default)({
|
|
ref: buttonRef,
|
|
id: buttonId,
|
|
"aria-label": apiRef.current.getLocaleText('actionsCellMore'),
|
|
"aria-haspopup": "menu",
|
|
"aria-expanded": open,
|
|
"aria-controls": open ? menuId : undefined,
|
|
role: "menuitem",
|
|
size: "small",
|
|
onClick: showMenu,
|
|
touchRippleRef: handleTouchRippleRef(buttonId),
|
|
tabIndex: focusedButtonIndex === iconButtons.length ? tabIndex : -1
|
|
}, rootProps.slotProps?.baseIconButton, {
|
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.moreActionsIcon, {
|
|
fontSize: "small"
|
|
})
|
|
})), menuButtons.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_GridMenu.GridMenu, {
|
|
open: open,
|
|
target: buttonRef.current,
|
|
position: position,
|
|
onClose: hideMenu,
|
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MenuList.default, {
|
|
id: menuId,
|
|
className: _gridClasses.gridClasses.menuList,
|
|
onKeyDown: handleListKeyDown,
|
|
"aria-labelledby": buttonId,
|
|
variant: "menu",
|
|
autoFocusItem: true,
|
|
children: menuButtons.map((button, index) => /*#__PURE__*/React.cloneElement(button, {
|
|
key: index,
|
|
closeMenu: hideMenu
|
|
}))
|
|
})
|
|
})]
|
|
}));
|
|
}
|
|
process.env.NODE_ENV !== "production" ? GridActionsCell.propTypes = {
|
|
// ----------------------------- Warning --------------------------------
|
|
// | These PropTypes are generated from the TypeScript type definitions |
|
|
// | To update them edit the TypeScript types and run "yarn proptypes" |
|
|
// ----------------------------------------------------------------------
|
|
api: _propTypes.default.object,
|
|
/**
|
|
* 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,
|
|
/**
|
|
* A ref allowing to set imperative focus.
|
|
* It can be passed to the element that should receive focus.
|
|
* @ignore - do not document.
|
|
*/
|
|
focusElementRef: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.shape({
|
|
current: _propTypes.default.shape({
|
|
focus: _propTypes.default.func.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,
|
|
position: _propTypes.default.oneOf(['bottom-end', 'bottom-start', 'bottom', 'left-end', 'left-start', 'left', 'right-end', 'right-start', 'right', 'top-end', 'top-start', 'top']),
|
|
/**
|
|
* 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,
|
|
/**
|
|
* 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 renderActionsCell = params => /*#__PURE__*/(0, _jsxRuntime.jsx)(GridActionsCell, (0, _extends2.default)({}, params));
|
|
exports.renderActionsCell = renderActionsCell; |