@mui/x-data-grid
Version:
The Community plan edition of the MUI X Data Grid components.
479 lines (473 loc) • 20.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.GridFilterForm = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _propTypes = _interopRequireDefault(require("prop-types"));
var _clsx = _interopRequireDefault(require("clsx"));
var _composeClasses = _interopRequireDefault(require("@mui/utils/composeClasses"));
var _useId = _interopRequireDefault(require("@mui/utils/useId"));
var _capitalize = _interopRequireDefault(require("@mui/utils/capitalize"));
var _styles = require("@mui/material/styles");
var _forwardRef = require("@mui/x-internals/forwardRef");
var _cssVariables = require("../../../constants/cssVariables");
var _gridColumnsSelector = require("../../../hooks/features/columns/gridColumnsSelector");
var _gridFilterSelector = require("../../../hooks/features/filter/gridFilterSelector");
var _useGridSelector = require("../../../hooks/utils/useGridSelector");
var _gridFilterItem = require("../../../models/gridFilterItem");
var _useGridApiContext = require("../../../hooks/utils/useGridApiContext");
var _useGridRootProps = require("../../../hooks/utils/useGridRootProps");
var _gridClasses = require("../../../constants/gridClasses");
var _filterPanelUtils = require("./filterPanelUtils");
var _pivoting = require("../../../hooks/features/pivoting");
var _jsxRuntime = require("react/jsx-runtime");
const _excluded = ["item", "hasMultipleFilters", "deleteFilter", "applyFilterChanges", "showMultiFilterOperators", "disableMultiFilterOperator", "applyMultiFilterOperatorChanges", "focusElementRef", "logicOperators", "columnsSort", "filterColumns", "deleteIconProps", "logicOperatorInputProps", "operatorInputProps", "columnInputProps", "valueInputProps", "readOnly", "children"],
_excluded2 = ["InputComponentProps"];
const useUtilityClasses = ownerState => {
const {
classes
} = ownerState;
const slots = {
root: ['filterForm'],
deleteIcon: ['filterFormDeleteIcon'],
logicOperatorInput: ['filterFormLogicOperatorInput'],
columnInput: ['filterFormColumnInput'],
operatorInput: ['filterFormOperatorInput'],
valueInput: ['filterFormValueInput']
};
return (0, _composeClasses.default)(slots, _gridClasses.getDataGridUtilityClass, classes);
};
const GridFilterFormRoot = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterForm'
})({
display: 'flex',
gap: _cssVariables.vars.spacing(1.5)
});
const FilterFormDeleteIcon = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterFormDeleteIcon'
})({
flexShrink: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
});
const FilterFormLogicOperatorInput = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterFormLogicOperatorInput'
})({
minWidth: 75,
justifyContent: 'end'
});
const FilterFormColumnInput = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterFormColumnInput'
})({
width: 150
});
const FilterFormOperatorInput = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterFormOperatorInput'
})({
width: 150
});
const FilterFormValueInput = (0, _styles.styled)('div', {
name: 'MuiDataGrid',
slot: 'FilterFormValueInput'
})({
width: 190
});
const getLogicOperatorLocaleKey = logicOperator => {
switch (logicOperator) {
case _gridFilterItem.GridLogicOperator.And:
return 'filterPanelOperatorAnd';
case _gridFilterItem.GridLogicOperator.Or:
return 'filterPanelOperatorOr';
default:
throw new Error('MUI X: Invalid `logicOperator` property in the `GridFilterPanel`.');
}
};
const getColumnLabel = col => col.headerName || col.field;
const collator = new Intl.Collator();
const GridFilterForm = exports.GridFilterForm = (0, _forwardRef.forwardRef)(function GridFilterForm(props, ref) {
const {
item,
hasMultipleFilters,
deleteFilter,
applyFilterChanges,
showMultiFilterOperators,
disableMultiFilterOperator,
applyMultiFilterOperatorChanges,
focusElementRef,
logicOperators = [_gridFilterItem.GridLogicOperator.And, _gridFilterItem.GridLogicOperator.Or],
columnsSort,
filterColumns,
deleteIconProps = {},
logicOperatorInputProps = {},
operatorInputProps = {},
columnInputProps = {},
valueInputProps = {},
readOnly
} = props,
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
const apiRef = (0, _useGridApiContext.useGridApiContext)();
const columnLookup = (0, _useGridSelector.useGridSelector)(apiRef, _gridColumnsSelector.gridColumnLookupSelector);
const filterableColumns = (0, _useGridSelector.useGridSelector)(apiRef, _gridColumnsSelector.gridFilterableColumnDefinitionsSelector);
const filterModel = (0, _useGridSelector.useGridSelector)(apiRef, _gridFilterSelector.gridFilterModelSelector);
const columnSelectId = (0, _useId.default)();
const columnSelectLabelId = (0, _useId.default)();
const operatorSelectId = (0, _useId.default)();
const operatorSelectLabelId = (0, _useId.default)();
const rootProps = (0, _useGridRootProps.useGridRootProps)();
const classes = useUtilityClasses(rootProps);
const valueRef = React.useRef(null);
const filterSelectorRef = React.useRef(null);
const multiFilterOperator = filterModel.logicOperator ?? _gridFilterItem.GridLogicOperator.And;
const hasLogicOperatorColumn = hasMultipleFilters && logicOperators.length > 0;
const baseSelectProps = rootProps.slotProps?.baseSelect || {};
const isBaseSelectNative = baseSelectProps.native ?? false;
const baseSelectOptionProps = rootProps.slotProps?.baseSelectOption || {};
const {
InputComponentProps
} = valueInputProps,
valueInputPropsOther = (0, _objectWithoutPropertiesLoose2.default)(valueInputProps, _excluded2);
const pivotActive = (0, _useGridSelector.useGridSelector)(apiRef, _pivoting.gridPivotActiveSelector);
const initialColumns = (0, _useGridSelector.useGridSelector)(apiRef, _pivoting.gridPivotInitialColumnsSelector);
const {
filteredColumns,
selectedField
} = React.useMemo(() => {
let itemField = item.field;
// Yields a valid value if the current filter belongs to a column that is not filterable
const selectedNonFilterableColumn = columnLookup[item.field].filterable === false ? columnLookup[item.field] : null;
if (selectedNonFilterableColumn) {
return {
filteredColumns: [selectedNonFilterableColumn],
selectedField: itemField
};
}
if (pivotActive) {
return {
filteredColumns: filterableColumns.filter(column => initialColumns.get(column.field) !== undefined),
selectedField: itemField
};
}
if (filterColumns === undefined || typeof filterColumns !== 'function') {
return {
filteredColumns: filterableColumns,
selectedField: itemField
};
}
const filteredFields = filterColumns({
field: item.field,
columns: filterableColumns,
currentFilters: filterModel?.items || []
});
return {
filteredColumns: filterableColumns.filter(column => {
const isFieldIncluded = filteredFields.includes(column.field);
if (column.field === item.field && !isFieldIncluded) {
itemField = undefined;
}
return isFieldIncluded;
}),
selectedField: itemField
};
}, [item.field, columnLookup, pivotActive, filterColumns, filterableColumns, filterModel?.items, initialColumns]);
const sortedFilteredColumns = React.useMemo(() => {
switch (columnsSort) {
case 'asc':
return filteredColumns.sort((a, b) => collator.compare(getColumnLabel(a), getColumnLabel(b)));
case 'desc':
return filteredColumns.sort((a, b) => -collator.compare(getColumnLabel(a), getColumnLabel(b)));
default:
return filteredColumns;
}
}, [filteredColumns, columnsSort]);
const currentColumn = item.field ? apiRef.current.getColumn(item.field) : null;
const currentOperator = React.useMemo(() => {
if (!item.operator || !currentColumn) {
return null;
}
return currentColumn.filterOperators?.find(operator => operator.value === item.operator);
}, [item, currentColumn]);
const changeColumn = React.useCallback(event => {
const field = event.target.value;
const column = apiRef.current.getColumn(field);
if (column.field === currentColumn.field) {
// column did not change
return;
}
// try to keep the same operator when column change
const newOperator = column.filterOperators.find(operator => operator.value === item.operator) || column.filterOperators[0];
// Erase filter value if the input component or filtered column type is modified
const eraseFilterValue = !newOperator.InputComponent || newOperator.InputComponent !== currentOperator?.InputComponent || column.type !== currentColumn.type;
let filterValue = eraseFilterValue ? undefined : item.value;
// Check filter value against the new valueOptions
if (column.type === 'singleSelect' && filterValue !== undefined) {
const colDef = column;
const valueOptions = (0, _filterPanelUtils.getValueOptions)(colDef);
if (Array.isArray(filterValue)) {
filterValue = filterValue.filter(val => {
return (
// Only keep values that are in the new value options
(0, _filterPanelUtils.getValueFromValueOptions)(val, valueOptions, colDef?.getOptionValue) !== undefined
);
});
} else if ((0, _filterPanelUtils.getValueFromValueOptions)(item.value, valueOptions, colDef?.getOptionValue) === undefined) {
// Reset the filter value if it is not in the new value options
filterValue = undefined;
}
}
applyFilterChanges((0, _extends2.default)({}, item, {
field,
operator: newOperator.value,
value: filterValue
}));
}, [apiRef, applyFilterChanges, item, currentColumn, currentOperator]);
const changeOperator = React.useCallback(event => {
const operator = event.target.value;
const newOperator = currentColumn?.filterOperators.find(op => op.value === operator);
const eraseItemValue = !newOperator?.InputComponent || newOperator?.InputComponent !== currentOperator?.InputComponent;
applyFilterChanges((0, _extends2.default)({}, item, {
operator,
value: eraseItemValue ? undefined : item.value
}));
}, [applyFilterChanges, item, currentColumn, currentOperator]);
const changeLogicOperator = React.useCallback(event => {
const logicOperator = event.target.value === _gridFilterItem.GridLogicOperator.And.toString() ? _gridFilterItem.GridLogicOperator.And : _gridFilterItem.GridLogicOperator.Or;
applyMultiFilterOperatorChanges(logicOperator);
}, [applyMultiFilterOperatorChanges]);
const handleDeleteFilter = () => {
deleteFilter(item);
};
React.useImperativeHandle(focusElementRef, () => ({
focus: () => {
if (currentOperator?.InputComponent) {
valueRef?.current?.focus();
} else {
filterSelectorRef.current.focus();
}
}
}), [currentOperator]);
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(GridFilterFormRoot, (0, _extends2.default)({
className: classes.root,
"data-id": item.id,
ownerState: rootProps
}, other, {
ref: ref,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(FilterFormDeleteIcon, (0, _extends2.default)({}, deleteIconProps, {
className: (0, _clsx.default)(classes.deleteIcon, deleteIconProps.className),
ownerState: rootProps,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.baseIconButton, (0, _extends2.default)({
"aria-label": apiRef.current.getLocaleText('filterPanelDeleteIconLabel'),
title: apiRef.current.getLocaleText('filterPanelDeleteIconLabel'),
onClick: handleDeleteFilter,
size: "small",
disabled: readOnly
}, rootProps.slotProps?.baseIconButton, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(rootProps.slots.filterPanelDeleteIcon, {
fontSize: "small"
})
}))
})), /*#__PURE__*/(0, _jsxRuntime.jsx)(FilterFormLogicOperatorInput, (0, _extends2.default)({
as: rootProps.slots.baseSelect,
sx: [hasLogicOperatorColumn ? {
display: 'flex'
} : {
display: 'none'
}, showMultiFilterOperators ? {
visibility: 'visible'
} : {
visibility: 'hidden'
}, logicOperatorInputProps.sx],
className: (0, _clsx.default)(classes.logicOperatorInput, logicOperatorInputProps.className),
ownerState: rootProps
}, logicOperatorInputProps, {
size: "small",
slotProps: {
htmlInput: {
'aria-label': apiRef.current.getLocaleText('filterPanelLogicOperator')
}
},
value: multiFilterOperator ?? '',
onChange: changeLogicOperator,
disabled: !!disableMultiFilterOperator || logicOperators.length === 1,
native: isBaseSelectNative
}, rootProps.slotProps?.baseSelect, {
children: logicOperators.map(logicOperator => /*#__PURE__*/(0, _react.createElement)(rootProps.slots.baseSelectOption, (0, _extends2.default)({}, baseSelectOptionProps, {
native: isBaseSelectNative,
key: logicOperator.toString(),
value: logicOperator.toString()
}), apiRef.current.getLocaleText(getLogicOperatorLocaleKey(logicOperator))))
})), /*#__PURE__*/(0, _jsxRuntime.jsx)(FilterFormColumnInput, (0, _extends2.default)({
as: rootProps.slots.baseSelect
}, columnInputProps, {
className: (0, _clsx.default)(classes.columnInput, columnInputProps.className),
ownerState: rootProps,
size: "small",
labelId: columnSelectLabelId,
id: columnSelectId,
label: apiRef.current.getLocaleText('filterPanelColumns'),
value: selectedField ?? '',
onChange: changeColumn,
native: isBaseSelectNative,
disabled: readOnly
}, rootProps.slotProps?.baseSelect, {
children: sortedFilteredColumns.map(col => /*#__PURE__*/(0, _react.createElement)(rootProps.slots.baseSelectOption, (0, _extends2.default)({}, baseSelectOptionProps, {
native: isBaseSelectNative,
key: col.field,
value: col.field
}), getColumnLabel(col)))
})), /*#__PURE__*/(0, _jsxRuntime.jsx)(FilterFormOperatorInput, (0, _extends2.default)({
as: rootProps.slots.baseSelect,
size: "small"
}, operatorInputProps, {
className: (0, _clsx.default)(classes.operatorInput, operatorInputProps.className),
ownerState: rootProps,
labelId: operatorSelectLabelId,
label: apiRef.current.getLocaleText('filterPanelOperator'),
id: operatorSelectId,
value: item.operator,
onChange: changeOperator,
native: isBaseSelectNative,
inputRef: filterSelectorRef,
disabled: readOnly
}, rootProps.slotProps?.baseSelect, {
children: currentColumn?.filterOperators?.map(operator => /*#__PURE__*/(0, _react.createElement)(rootProps.slots.baseSelectOption, (0, _extends2.default)({}, baseSelectOptionProps, {
native: isBaseSelectNative,
key: operator.value,
value: operator.value
}), operator.label || apiRef.current.getLocaleText(`filterOperator${(0, _capitalize.default)(operator.value)}`)))
})), /*#__PURE__*/(0, _jsxRuntime.jsx)(FilterFormValueInput, (0, _extends2.default)({}, valueInputPropsOther, {
className: (0, _clsx.default)(classes.valueInput, valueInputPropsOther.className),
ownerState: rootProps,
children: currentOperator?.InputComponent ? /*#__PURE__*/(0, _jsxRuntime.jsx)(currentOperator.InputComponent, (0, _extends2.default)({
apiRef: apiRef,
item: item,
applyValue: applyFilterChanges,
focusElementRef: valueRef,
disabled: readOnly,
slotProps: {
root: {
size: 'small'
}
}
}, currentOperator.InputComponentProps, InputComponentProps), item.field) : null
}))]
}));
});
if (process.env.NODE_ENV !== "production") GridFilterForm.displayName = "GridFilterForm";
process.env.NODE_ENV !== "production" ? GridFilterForm.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
/**
* Callback called when the operator, column field or value is changed.
* @param {GridFilterItem} item The updated [[GridFilterItem]].
*/
applyFilterChanges: _propTypes.default.func.isRequired,
/**
* Callback called when the logic operator is changed.
* @param {GridLogicOperator} operator The new logic operator.
*/
applyMultiFilterOperatorChanges: _propTypes.default.func.isRequired,
/**
* @ignore - do not document.
*/
children: _propTypes.default.node,
/**
* Props passed to the column input component.
* @default {}
*/
columnInputProps: _propTypes.default.any,
/**
* Changes how the options in the columns selector should be ordered.
* If not specified, the order is derived from the `columns` prop.
*/
columnsSort: _propTypes.default.oneOf(['asc', 'desc']),
/**
* Callback called when the delete button is clicked.
* @param {GridFilterItem} item The deleted [[GridFilterItem]].
*/
deleteFilter: _propTypes.default.func.isRequired,
/**
* Props passed to the delete icon.
* @default {}
*/
deleteIconProps: _propTypes.default.any,
/**
* If `true`, disables the logic operator field but still renders it.
*/
disableMultiFilterOperator: _propTypes.default.bool,
/**
* Allows to filter the columns displayed in the filter form.
* @param {FilterColumnsArgs} args The columns of the grid and name of field.
* @returns {GridColDef['field'][]} The filtered fields array.
*/
filterColumns: _propTypes.default.func,
/**
* A ref allowing to set imperative focus.
* It can be passed to the el
*/
focusElementRef: _propTypes.default /* @typescript-to-proptypes-ignore */.oneOfType([_propTypes.default.func, _propTypes.default.object]),
/**
* If `true`, the logic operator field is rendered.
* The field will be invisible if `showMultiFilterOperators` is also `true`.
*/
hasMultipleFilters: _propTypes.default.bool.isRequired,
/**
* The [[GridFilterItem]] representing this form.
*/
item: _propTypes.default.shape({
field: _propTypes.default.string.isRequired,
id: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
operator: _propTypes.default.string.isRequired,
value: _propTypes.default.any
}).isRequired,
/**
* Props passed to the logic operator input component.
* @default {}
*/
logicOperatorInputProps: _propTypes.default.any,
/**
* Sets the available logic operators.
* @default [GridLogicOperator.And, GridLogicOperator.Or]
*/
logicOperators: _propTypes.default.arrayOf(_propTypes.default.oneOf(['and', 'or']).isRequired),
/**
* Props passed to the operator input component.
* @default {}
*/
operatorInputProps: _propTypes.default.any,
/**
* `true` if the filter is disabled/read only.
* i.e. `colDef.fiterable = false` but passed in `filterModel`
* @default false
*/
readOnly: _propTypes.default.bool,
/**
* If `true`, the logic operator field is visible.
*/
showMultiFilterOperators: _propTypes.default.bool,
/**
* Props passed to the value input component.
* @default {}
*/
valueInputProps: _propTypes.default.any
} : void 0;
/**
* Demos:
* - [Filtering - overview](https://mui.com/x/react-data-grid/filtering/)
*
* API:
* - [GridFilterForm API](https://mui.com/x/api/data-grid/grid-filter-form/)
*/