UNPKG

@mui/x-data-grid

Version:

The community edition of the data grid component (MUI X).

446 lines (407 loc) 17.6 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; const _excluded = ["item", "hasMultipleFilters", "deleteFilter", "applyFilterChanges", "multiFilterOperator", "showMultiFilterOperators", "disableMultiFilterOperator", "applyMultiFilterOperatorChanges", "focusElementRef", "linkOperators", "columnsSort", "deleteIconProps", "linkOperatorInputProps", "operatorInputProps", "columnInputProps", "valueInputProps", "children"], _excluded2 = ["InputComponentProps"]; import * as React from 'react'; import PropTypes from 'prop-types'; import { unstable_composeClasses as composeClasses } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import MenuItem from '@mui/material/MenuItem'; import InputLabel from '@mui/material/InputLabel'; import FormControl from '@mui/material/FormControl'; import { capitalize, unstable_useId as useId } from '@mui/material/utils'; import { styled } from '@mui/material/styles'; import clsx from 'clsx'; import { gridFilterableColumnDefinitionsSelector } from '../../../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../../../hooks/utils/useGridSelector'; import { GridLinkOperator } from '../../../models/gridFilterItem'; import { useGridApiContext } from '../../../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; import { getDataGridUtilityClass } from '../../../constants/gridClasses'; import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; const useUtilityClasses = ownerState => { const { classes } = ownerState; const slots = { root: ['filterForm'], deleteIcon: ['filterFormDeleteIcon'], linkOperatorInput: ['filterFormLinkOperatorInput'], columnInput: ['filterFormColumnInput'], operatorInput: ['filterFormOperatorInput'], valueInput: ['filterFormValueInput'] }; return composeClasses(slots, getDataGridUtilityClass, classes); }; const GridFilterFormRoot = styled('div', { name: 'MuiDataGrid', slot: 'FilterForm', overridesResolver: (props, styles) => styles.filterForm })(({ theme }) => ({ display: 'flex', padding: theme.spacing(1) })); const FilterFormDeleteIcon = styled(FormControl, { name: 'MuiDataGrid', slot: 'FilterFormDeleteIcon', overridesResolver: (_, styles) => styles.filterFormDeleteIcon })(({ theme }) => ({ flexShrink: 0, justifyContent: 'flex-end', marginRight: theme.spacing(0.5), marginBottom: theme.spacing(0.2) })); const FilterFormLinkOperatorInput = styled(FormControl, { name: 'MuiDataGrid', slot: 'FilterFormLinkOperatorInput', overridesResolver: (_, styles) => styles.filterFormLinkOperatorInput })({ minWidth: 55, marginRight: 5, justifyContent: 'end' }); const FilterFormColumnInput = styled(FormControl, { name: 'MuiDataGrid', slot: 'FilterFormColumnInput', overridesResolver: (_, styles) => styles.filterFormColumnInput })({ width: 150 }); const FilterFormOperatorInput = styled(FormControl, { name: 'MuiDataGrid', slot: 'FilterFormOperatorInput', overridesResolver: (_, styles) => styles.filterFormOperatorInput })({ width: 120 }); const FilterFormValueInput = styled(FormControl, { name: 'MuiDataGrid', slot: 'FilterFormValueInput', overridesResolver: (_, styles) => styles.filterFormValueInput })({ width: 190 }); const getLinkOperatorLocaleKey = linkOperator => { switch (linkOperator) { case GridLinkOperator.And: return 'filterPanelOperatorAnd'; case GridLinkOperator.Or: return 'filterPanelOperatorOr'; default: throw new Error('MUI: Invalid `linkOperator` property in the `GridFilterPanel`.'); } }; const getColumnLabel = col => col.headerName || col.field; const collator = new Intl.Collator(); const GridFilterForm = /*#__PURE__*/React.forwardRef(function GridFilterForm(props, ref) { var _rootProps$components, _rootProps$components2, _baseSelectProps$nati, _rootProps$components3, _rootProps$components4, _rootProps$components5, _currentColumn$filter2; const { item, hasMultipleFilters, deleteFilter, applyFilterChanges, multiFilterOperator, showMultiFilterOperators, disableMultiFilterOperator, applyMultiFilterOperatorChanges, focusElementRef, linkOperators = [GridLinkOperator.And, GridLinkOperator.Or], columnsSort, deleteIconProps = {}, linkOperatorInputProps = {}, operatorInputProps = {}, columnInputProps = {}, valueInputProps = {} } = props, other = _objectWithoutPropertiesLoose(props, _excluded); const apiRef = useGridApiContext(); const filterableColumns = useGridSelector(apiRef, gridFilterableColumnDefinitionsSelector); const columnSelectId = useId(); const columnSelectLabelId = useId(); const operatorSelectId = useId(); const operatorSelectLabelId = useId(); const rootProps = useGridRootProps(); const ownerState = { classes: rootProps.classes }; const classes = useUtilityClasses(ownerState); const valueRef = React.useRef(null); const filterSelectorRef = React.useRef(null); const hasLinkOperatorColumn = hasMultipleFilters && linkOperators.length > 0; const baseFormControlProps = ((_rootProps$components = rootProps.componentsProps) == null ? void 0 : _rootProps$components.baseFormControl) || {}; const baseSelectProps = ((_rootProps$components2 = rootProps.componentsProps) == null ? void 0 : _rootProps$components2.baseSelect) || {}; const isBaseSelectNative = (_baseSelectProps$nati = baseSelectProps.native) != null ? _baseSelectProps$nati : true; const OptionComponent = isBaseSelectNative ? 'option' : MenuItem; const { InputComponentProps } = valueInputProps, valueInputPropsOther = _objectWithoutPropertiesLoose(valueInputProps, _excluded2); const sortedFilterableColumns = React.useMemo(() => { switch (columnsSort) { case 'asc': return filterableColumns.sort((a, b) => collator.compare(getColumnLabel(a), getColumnLabel(b))); case 'desc': return filterableColumns.sort((a, b) => -collator.compare(getColumnLabel(a), getColumnLabel(b))); default: return filterableColumns; } }, [filterableColumns, columnsSort]); const currentColumn = item.columnField ? apiRef.current.getColumn(item.columnField) : null; const currentOperator = React.useMemo(() => { var _currentColumn$filter; if (!item.operatorValue || !currentColumn) { return null; } return (_currentColumn$filter = currentColumn.filterOperators) == null ? void 0 : _currentColumn$filter.find(operator => operator.value === item.operatorValue); }, [item, currentColumn]); const changeColumn = React.useCallback(event => { const columnField = event.target.value; const column = apiRef.current.getColumn(columnField); 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.operatorValue) || column.filterOperators[0]; // Erase filter value if the input component is modified const eraseItemValue = !newOperator.InputComponent || newOperator.InputComponent !== (currentOperator == null ? void 0 : currentOperator.InputComponent); applyFilterChanges(_extends({}, item, { columnField, operatorValue: newOperator.value, value: eraseItemValue ? undefined : item.value })); }, [apiRef, applyFilterChanges, item, currentColumn, currentOperator]); const changeOperator = React.useCallback(event => { const operatorValue = event.target.value; const newOperator = currentColumn == null ? void 0 : currentColumn.filterOperators.find(operator => operator.value === operatorValue); const eraseItemValue = !(newOperator != null && newOperator.InputComponent) || (newOperator == null ? void 0 : newOperator.InputComponent) !== (currentOperator == null ? void 0 : currentOperator.InputComponent); applyFilterChanges(_extends({}, item, { operatorValue, value: eraseItemValue ? undefined : item.value })); }, [applyFilterChanges, item, currentColumn, currentOperator]); const changeLinkOperator = React.useCallback(event => { const linkOperator = event.target.value === GridLinkOperator.And.toString() ? GridLinkOperator.And : GridLinkOperator.Or; applyMultiFilterOperatorChanges(linkOperator); }, [applyMultiFilterOperatorChanges]); const handleDeleteFilter = () => { if (rootProps.disableMultipleColumnsFiltering) { if (item.value === undefined) { deleteFilter(item); } else { // TODO v6: simplify the behavior by always remove the filter form applyFilterChanges(_extends({}, item, { value: undefined })); } } else { deleteFilter(item); } }; React.useImperativeHandle(focusElementRef, () => ({ focus: () => { if (currentOperator != null && currentOperator.InputComponent) { var _valueRef$current; valueRef == null ? void 0 : (_valueRef$current = valueRef.current) == null ? void 0 : _valueRef$current.focus(); } else { filterSelectorRef.current.focus(); } } }), [currentOperator]); return /*#__PURE__*/_jsxs(GridFilterFormRoot, _extends({ ref: ref, className: classes.root }, other, { children: [/*#__PURE__*/_jsx(FilterFormDeleteIcon, _extends({ variant: "standard", as: rootProps.components.BaseFormControl }, baseFormControlProps, deleteIconProps, { className: clsx(classes.deleteIcon, baseFormControlProps.className, deleteIconProps.className), children: /*#__PURE__*/_jsx(IconButton, { "aria-label": apiRef.current.getLocaleText('filterPanelDeleteIconLabel'), title: apiRef.current.getLocaleText('filterPanelDeleteIconLabel'), onClick: handleDeleteFilter, size: "small", children: /*#__PURE__*/_jsx(rootProps.components.FilterPanelDeleteIcon, { fontSize: "small" }) }) })), /*#__PURE__*/_jsx(FilterFormLinkOperatorInput, _extends({ variant: "standard", as: rootProps.components.BaseFormControl }, baseFormControlProps, linkOperatorInputProps, { sx: _extends({ display: hasLinkOperatorColumn ? 'flex' : 'none', visibility: showMultiFilterOperators ? 'visible' : 'hidden' }, baseFormControlProps.sx || {}, linkOperatorInputProps.sx || {}), className: clsx(classes.linkOperatorInput, baseFormControlProps.className, linkOperatorInputProps.className), children: /*#__PURE__*/_jsx(rootProps.components.BaseSelect, _extends({ inputProps: { 'aria-label': apiRef.current.getLocaleText('filterPanelLinkOperator') }, value: multiFilterOperator, onChange: changeLinkOperator, disabled: !!disableMultiFilterOperator || linkOperators.length === 1, native: isBaseSelectNative }, (_rootProps$components3 = rootProps.componentsProps) == null ? void 0 : _rootProps$components3.baseSelect, { children: linkOperators.map(linkOperator => /*#__PURE__*/_jsx(OptionComponent, { value: linkOperator.toString(), children: apiRef.current.getLocaleText(getLinkOperatorLocaleKey(linkOperator)) }, linkOperator.toString())) })) })), /*#__PURE__*/_jsxs(FilterFormColumnInput, _extends({ variant: "standard", as: rootProps.components.BaseFormControl }, baseFormControlProps, columnInputProps, { className: clsx(classes.columnInput, baseFormControlProps.className, columnInputProps.className), children: [/*#__PURE__*/_jsx(InputLabel, { htmlFor: columnSelectId, id: columnSelectLabelId, children: apiRef.current.getLocaleText('filterPanelColumns') }), /*#__PURE__*/_jsx(rootProps.components.BaseSelect, _extends({ labelId: columnSelectLabelId, id: columnSelectId, label: apiRef.current.getLocaleText('filterPanelColumns'), value: item.columnField || '', onChange: changeColumn, native: isBaseSelectNative }, (_rootProps$components4 = rootProps.componentsProps) == null ? void 0 : _rootProps$components4.baseSelect, { children: sortedFilterableColumns.map(col => /*#__PURE__*/_jsx(OptionComponent, { value: col.field, children: getColumnLabel(col) }, col.field)) }))] })), /*#__PURE__*/_jsxs(FilterFormOperatorInput, _extends({ variant: "standard", as: rootProps.components.BaseFormControl }, baseFormControlProps, operatorInputProps, { className: clsx(classes.operatorInput, baseFormControlProps.className, operatorInputProps.className), children: [/*#__PURE__*/_jsx(InputLabel, { htmlFor: operatorSelectId, id: operatorSelectLabelId, children: apiRef.current.getLocaleText('filterPanelOperators') }), /*#__PURE__*/_jsx(rootProps.components.BaseSelect, _extends({ labelId: operatorSelectLabelId, label: apiRef.current.getLocaleText('filterPanelOperators'), id: operatorSelectId, value: item.operatorValue, onChange: changeOperator, native: isBaseSelectNative, inputRef: filterSelectorRef }, (_rootProps$components5 = rootProps.componentsProps) == null ? void 0 : _rootProps$components5.baseSelect, { children: currentColumn == null ? void 0 : (_currentColumn$filter2 = currentColumn.filterOperators) == null ? void 0 : _currentColumn$filter2.map(operator => /*#__PURE__*/_jsx(OptionComponent, { value: operator.value, children: operator.label || apiRef.current.getLocaleText(`filterOperator${capitalize(operator.value)}`) }, operator.value)) }))] })), /*#__PURE__*/_jsx(FilterFormValueInput, _extends({ variant: "standard", as: rootProps.components.BaseFormControl }, baseFormControlProps, valueInputPropsOther, { className: clsx(classes.valueInput, baseFormControlProps.className, valueInputPropsOther.className), children: currentOperator != null && currentOperator.InputComponent ? /*#__PURE__*/_jsx(currentOperator.InputComponent, _extends({ apiRef: apiRef, item: item, applyValue: applyFilterChanges, focusElementRef: valueRef }, currentOperator.InputComponentProps, InputComponentProps)) : null }))] })); }); 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 "yarn proptypes" | // ---------------------------------------------------------------------- /** * Callback called when the operator, column field or value is changed. * @param {GridFilterItem} item The updated [[GridFilterItem]]. */ applyFilterChanges: PropTypes.func.isRequired, /** * Callback called when the logic operator is changed. * @param {GridLinkOperator} operator The new logic operator. */ applyMultiFilterOperatorChanges: PropTypes.func.isRequired, /** * @ignore - do not document. */ children: PropTypes.node, /** * Props passed to the column input component. * @default {} */ columnInputProps: PropTypes.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.oneOf(['asc', 'desc']), /** * Callback called when the delete button is clicked. * @param {GridFilterItem} item The deleted [[GridFilterItem]]. */ deleteFilter: PropTypes.func.isRequired, /** * Props passed to the delete icon. * @default {} */ deleteIconProps: PropTypes.any, /** * If `true`, disables the logic operator field but still renders it. */ disableMultiFilterOperator: PropTypes.bool, /** * A ref allowing to set imperative focus. * It can be passed to the el */ focusElementRef: PropTypes /* @typescript-to-proptypes-ignore */ .oneOfType([PropTypes.func, PropTypes.object]), /** * If `true`, the logic operator field is rendered. * The field will be invisible if `showMultiFilterOperators` is also `true`. */ hasMultipleFilters: PropTypes.bool.isRequired, /** * The [[GridFilterItem]] representing this form. */ item: PropTypes.shape({ columnField: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), operatorValue: PropTypes.string, value: PropTypes.any }).isRequired, /** * Props passed to the logic operator input component. * @default {} */ linkOperatorInputProps: PropTypes.any, /** * Sets the available logic operators. * @default [GridLinkOperator.And, GridLinkOperator.Or] */ linkOperators: PropTypes.arrayOf(PropTypes.oneOf(['and', 'or']).isRequired), /** * The current logic operator applied. */ multiFilterOperator: PropTypes.oneOf(['and', 'or']), /** * Props passed to the operator input component. * @default {} */ operatorInputProps: PropTypes.any, /** * If `true`, the logic operator field is visible. */ showMultiFilterOperators: PropTypes.bool, /** * Props passed to the value input component. * @default {} */ valueInputProps: PropTypes.any } : void 0; export { GridFilterForm };