@mui/x-data-grid
Version:
The community edition of the data grid component (MUI X).
360 lines (314 loc) • 15 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { GridFeatureModeConstant } from '../../../models/gridFeatureMode';
import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { useGridApiMethod } from '../../utils/useGridApiMethod';
import { useGridLogger } from '../../utils/useGridLogger';
import { gridFilterableColumnLookupSelector } from '../columns/gridColumnsSelector';
import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue';
import { getDefaultGridFilterModel } from './gridFilterState';
import { gridFilterModelSelector, gridVisibleSortedRowEntriesSelector } from './gridFilterSelector';
import { useFirstRender } from '../../utils/useFirstRender';
import { gridRowIdsSelector } from '../rows';
import { useGridRegisterPipeProcessor } from '../../core/pipeProcessing';
import { GRID_DEFAULT_STRATEGY, useGridRegisterStrategyProcessor } from '../../core/strategyProcessing';
import { buildAggregatedFilterApplier, sanitizeFilterModel, mergeStateWithFilterModel, cleanFilterItem, passFilterLogic } from './gridFilterUtils';
import { isDeepEqual } from '../../../utils/utils';
import { jsx as _jsx } from "react/jsx-runtime";
export var filterStateInitializer = function filterStateInitializer(state, props, apiRef) {
var _ref, _props$filterModel, _props$initialState, _props$initialState$f;
var filterModel = (_ref = (_props$filterModel = props.filterModel) != null ? _props$filterModel : (_props$initialState = props.initialState) == null ? void 0 : (_props$initialState$f = _props$initialState.filter) == null ? void 0 : _props$initialState$f.filterModel) != null ? _ref : getDefaultGridFilterModel();
return _extends({}, state, {
filter: {
filterModel: sanitizeFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef),
visibleRowsLookup: {},
filteredDescendantCountLookup: {}
}
});
};
/**
* @requires useGridColumns (method, event)
* @requires useGridParamsApi (method)
* @requires useGridRows (event)
*/
export var useGridFilter = function useGridFilter(apiRef, props) {
var _props$initialState3, _props$initialState3$, _props$componentsProp2;
var logger = useGridLogger(apiRef, 'useGridFilter');
apiRef.current.unstable_registerControlState({
stateId: 'filter',
propModel: props.filterModel,
propOnChange: props.onFilterModelChange,
stateSelector: gridFilterModelSelector,
changeEvent: 'filterModelChange'
});
var updateFilteredRows = React.useCallback(function () {
apiRef.current.setState(function (state) {
var filterModel = gridFilterModelSelector(state, apiRef.current.instanceId);
var isRowMatchingFilters = props.filterMode === GridFeatureModeConstant.client ? buildAggregatedFilterApplier(filterModel, apiRef) : null;
var filteringResult = apiRef.current.unstable_applyStrategyProcessor('filtering', {
isRowMatchingFilters: isRowMatchingFilters,
filterModel: filterModel != null ? filterModel : getDefaultGridFilterModel()
});
return _extends({}, state, {
filter: _extends({}, state.filter, filteringResult)
});
});
apiRef.current.publishEvent('filteredRowsSet');
}, [props.filterMode, apiRef]);
/**
* API METHODS
*/
var applyFilters = React.useCallback(function () {
updateFilteredRows();
apiRef.current.forceUpdate();
}, [apiRef, updateFilteredRows]);
var upsertFilterItem = React.useCallback(function (item) {
var filterModel = gridFilterModelSelector(apiRef);
var items = _toConsumableArray(filterModel.items);
var itemIndex = items.findIndex(function (filterItem) {
return filterItem.id === item.id;
});
if (itemIndex === -1) {
items.push(item);
} else {
items[itemIndex] = item;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'upsertFilterItem');
}, [apiRef]);
var upsertFilterItems = React.useCallback(function (items) {
var filterModel = gridFilterModelSelector(apiRef);
var existingItems = _toConsumableArray(filterModel.items);
items.forEach(function (item) {
var itemIndex = items.findIndex(function (filterItem) {
return filterItem.id === item.id;
});
if (itemIndex === -1) {
existingItems.push(item);
} else {
existingItems[itemIndex] = item;
}
});
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'upsertFilterItems');
}, [apiRef]);
var deleteFilterItem = React.useCallback(function (itemToDelete) {
var filterModel = gridFilterModelSelector(apiRef);
var items = filterModel.items.filter(function (item) {
return item.id !== itemToDelete.id;
});
if (items.length === filterModel.items.length) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: items
}), 'deleteFilterItem');
}, [apiRef]);
var showFilterPanel = React.useCallback(function (targetColumnField) {
logger.debug('Displaying filter panel');
if (targetColumnField) {
var filterModel = gridFilterModelSelector(apiRef);
var filterItemsWithValue = filterModel.items.filter(function (item) {
var _column$filterOperato;
if (item.value !== undefined) {
return true;
}
var column = apiRef.current.getColumn(item.columnField);
var filterOperator = (_column$filterOperato = column.filterOperators) == null ? void 0 : _column$filterOperato.find(function (operator) {
return operator.value === item.operatorValue;
});
var requiresFilterValue = typeof (filterOperator == null ? void 0 : filterOperator.requiresFilterValue) === 'undefined' ? true : filterOperator == null ? void 0 : filterOperator.requiresFilterValue; // Operators like `isEmpty` don't have and don't require `item.value`.
// So we don't want to remove them from the filter model if `item.value === undefined`.
// See https://github.com/mui/mui-x/issues/5402
if (requiresFilterValue) {
return false;
}
return true;
});
var newFilterItems;
var filterItemOnTarget = filterItemsWithValue.find(function (item) {
return item.columnField === targetColumnField;
});
if (filterItemOnTarget) {
newFilterItems = filterItemsWithValue;
} else if (props.disableMultipleColumnsFiltering) {
newFilterItems = [cleanFilterItem({
columnField: targetColumnField
}, apiRef)];
} else {
newFilterItems = [].concat(_toConsumableArray(filterItemsWithValue), [cleanFilterItem({
columnField: targetColumnField
}, apiRef)]);
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: newFilterItems
}));
}
apiRef.current.showPreferences(GridPreferencePanelsValue.filters);
}, [apiRef, logger, props.disableMultipleColumnsFiltering]);
var hideFilterPanel = React.useCallback(function () {
logger.debug('Hiding filter panel');
apiRef.current.hidePreferences();
}, [apiRef, logger]);
var setFilterLinkOperator = React.useCallback(function (linkOperator) {
var filterModel = gridFilterModelSelector(apiRef);
if (filterModel.linkOperator === linkOperator) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
linkOperator: linkOperator
}), 'changeLogicOperator');
}, [apiRef]);
var setQuickFilterValues = React.useCallback(function (values) {
var filterModel = gridFilterModelSelector(apiRef);
if (isDeepEqual(filterModel.quickFilterValues, values)) {
return;
}
apiRef.current.setFilterModel(_extends({}, filterModel, {
quickFilterValues: _toConsumableArray(values)
}));
}, [apiRef]);
var setFilterModel = React.useCallback(function (model, reason) {
var currentModel = gridFilterModelSelector(apiRef);
if (currentModel !== model) {
logger.debug('Setting filter model');
apiRef.current.unstable_updateControlState('filter', mergeStateWithFilterModel(model, props.disableMultipleColumnsFiltering, apiRef), reason);
apiRef.current.unstable_applyFilters();
}
}, [apiRef, logger, props.disableMultipleColumnsFiltering]);
var getVisibleRowModels = React.useCallback(function () {
var visibleSortedRows = gridVisibleSortedRowEntriesSelector(apiRef);
return new Map(visibleSortedRows.map(function (row) {
return [row.id, row.model];
}));
}, [apiRef]);
var filterApi = {
setFilterLinkOperator: setFilterLinkOperator,
unstable_applyFilters: applyFilters,
deleteFilterItem: deleteFilterItem,
upsertFilterItem: upsertFilterItem,
upsertFilterItems: upsertFilterItems,
setFilterModel: setFilterModel,
showFilterPanel: showFilterPanel,
hideFilterPanel: hideFilterPanel,
getVisibleRowModels: getVisibleRowModels,
setQuickFilterValues: setQuickFilterValues
};
useGridApiMethod(apiRef, filterApi, 'GridFilterApi');
/**
* PRE-PROCESSING
*/
var stateExportPreProcessing = React.useCallback(function (prevState, context) {
var _props$initialState2, _props$initialState2$;
var filterModelToExport = gridFilterModelSelector(apiRef);
var shouldExportFilterModel = // Always export if the `exportOnlyDirtyModels` property is activated
!context.exportOnlyDirtyModels || // Always export if the model is controlled
props.filterModel != null || // Always export if the model has been initialized
((_props$initialState2 = props.initialState) == null ? void 0 : (_props$initialState2$ = _props$initialState2.filter) == null ? void 0 : _props$initialState2$.filterModel) != null || // Export if the model is not equal to the default value
!isDeepEqual(filterModelToExport, getDefaultGridFilterModel());
if (!shouldExportFilterModel) {
return prevState;
}
return _extends({}, prevState, {
filter: {
filterModel: filterModelToExport
}
});
}, [apiRef, props.filterModel, (_props$initialState3 = props.initialState) == null ? void 0 : (_props$initialState3$ = _props$initialState3.filter) == null ? void 0 : _props$initialState3$.filterModel]);
var stateRestorePreProcessing = React.useCallback(function (params, context) {
var _context$stateToResto;
var filterModel = (_context$stateToResto = context.stateToRestore.filter) == null ? void 0 : _context$stateToResto.filterModel;
if (filterModel == null) {
return params;
}
apiRef.current.unstable_updateControlState('filter', mergeStateWithFilterModel(filterModel, props.disableMultipleColumnsFiltering, apiRef), 'restoreState');
return _extends({}, params, {
callbacks: [].concat(_toConsumableArray(params.callbacks), [apiRef.current.unstable_applyFilters])
});
}, [apiRef, props.disableMultipleColumnsFiltering]);
var preferencePanelPreProcessing = React.useCallback(function (initialValue, value) {
if (value === GridPreferencePanelsValue.filters) {
var _props$componentsProp;
var FilterPanel = props.components.FilterPanel;
return /*#__PURE__*/_jsx(FilterPanel, _extends({}, (_props$componentsProp = props.componentsProps) == null ? void 0 : _props$componentsProp.filterPanel));
}
return initialValue;
}, [props.components.FilterPanel, (_props$componentsProp2 = props.componentsProps) == null ? void 0 : _props$componentsProp2.filterPanel]);
var flatFilteringMethod = React.useCallback(function (params) {
if (props.filterMode === GridFeatureModeConstant.client && params.isRowMatchingFilters) {
var rowIds = gridRowIdsSelector(apiRef);
var filteredRowsLookup = {};
for (var i = 0; i < rowIds.length; i += 1) {
var rowId = rowIds[i];
var isRowPassing = void 0;
if (typeof rowId === 'string' && rowId.startsWith('auto-generated-group-footer')) {
isRowPassing = true;
} else {
var _params$isRowMatching = params.isRowMatchingFilters(rowId),
passingFilterItems = _params$isRowMatching.passingFilterItems,
passingQuickFilterValues = _params$isRowMatching.passingQuickFilterValues;
isRowPassing = passFilterLogic([passingFilterItems], [passingQuickFilterValues], params.filterModel, apiRef);
}
filteredRowsLookup[rowId] = isRowPassing;
}
return {
filteredRowsLookup: filteredRowsLookup,
// For flat tree, the `visibleRowsLookup` and the `filteredRowsLookup` since no row is collapsed.
visibleRowsLookup: filteredRowsLookup,
filteredDescendantCountLookup: {}
};
}
return {
visibleRowsLookup: {},
filteredRowsLookup: {},
filteredDescendantCountLookup: {}
};
}, [apiRef, props.filterMode]);
useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing);
useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing);
useGridRegisterPipeProcessor(apiRef, 'preferencePanel', preferencePanelPreProcessing);
useGridRegisterStrategyProcessor(apiRef, GRID_DEFAULT_STRATEGY, 'filtering', flatFilteringMethod);
/**
* EVENTS
*/
var handleColumnsChange = React.useCallback(function () {
logger.debug('onColUpdated - GridColumns changed, applying filters');
var filterModel = gridFilterModelSelector(apiRef);
var filterableColumnsLookup = gridFilterableColumnLookupSelector(apiRef);
var newFilterItems = filterModel.items.filter(function (item) {
return item.columnField && filterableColumnsLookup[item.columnField];
});
if (newFilterItems.length < filterModel.items.length) {
apiRef.current.setFilterModel(_extends({}, filterModel, {
items: newFilterItems
}));
}
}, [apiRef, logger]);
var handleStrategyProcessorChange = React.useCallback(function (methodName) {
if (methodName === 'filtering') {
apiRef.current.unstable_applyFilters();
}
}, [apiRef]); // Do not call `apiRef.current.forceUpdate` to avoid re-render before updating the sorted rows.
// Otherwise, the state is not consistent during the render
useGridApiEventHandler(apiRef, 'rowsSet', updateFilteredRows);
useGridApiEventHandler(apiRef, 'rowExpansionChange', apiRef.current.unstable_applyFilters);
useGridApiEventHandler(apiRef, 'columnsChange', handleColumnsChange);
useGridApiEventHandler(apiRef, 'activeStrategyProcessorChange', handleStrategyProcessorChange);
/**
* 1ST RENDER
*/
useFirstRender(function () {
apiRef.current.unstable_applyFilters();
});
/**
* EFFECTS
*/
React.useEffect(function () {
if (props.filterModel !== undefined) {
apiRef.current.setFilterModel(props.filterModel);
}
}, [apiRef, logger, props.filterModel]);
};