From 211791461a4aceac76d737f9d5f04119812f1e7b Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 19:22:06 -0400 Subject: [PATCH 01/24] perf: dont do expensive allocations --- .../src/hooks/features/filter/gridFilterUtils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 3370358d60065..980c4f128e938 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { defaultMemoize } from 'reselect'; import { GridCellParams, GridFilterItem, @@ -295,15 +296,18 @@ export const buildAggregatedFilterApplier = ( }); }; +const filterModelItems = defaultMemoize( + (apiRef: React.MutableRefObject, items: GridFilterItem[]) => + items.filter((item) => getFilterCallbackFromItem(item, apiRef) !== null) +); + export const passFilterLogic = ( allFilterItemResults: (null | GridFilterItemResult)[], allQuickFilterResults: (null | GridQuickFilterValueResult)[], filterModel: GridFilterModel, apiRef: React.MutableRefObject, ): boolean => { - const cleanedFilterItems = filterModel.items.filter( - (item) => getFilterCallbackFromItem(item, apiRef) !== null, - ); + const cleanedFilterItems = filterModelItems(apiRef, filterModel.items); const cleanedAllFilterItemResults = allFilterItemResults.filter( (result): result is GridFilterItemResult => result != null, From 0b1d56e1544cd7438acf92d65b298708a4f9cb0d Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 19:48:59 -0400 Subject: [PATCH 02/24] perf: fast filters --- .../src/colDef/gridStringOperators.ts | 5 +- .../hooks/features/filter/gridFilterUtils.ts | 50 +++++++++++++++---- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts index 20ba2beed424c..8274a847547cb 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts @@ -4,6 +4,7 @@ import { GridFilterItem } from '../models/gridFilterItem'; import { GridFilterOperator } from '../models/gridFilterOperator'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; import { GridCellParams } from '../models'; +import { wrapFastFilterOperators } from '../hooks/features/filter/gridFilterUtils'; export const getGridStringQuickFilterFn = (value: any) => { if (!value) { @@ -17,7 +18,7 @@ export const getGridStringQuickFilterFn = (value: any) => { export const getGridStringOperators = ( disableTrim: boolean = false, -): GridFilterOperator[] => [ +): GridFilterOperator[] => wrapFastFilterOperators([ { value: 'contains', getApplyFilterFn: (filterItem: GridFilterItem) => { @@ -116,4 +117,4 @@ export const getGridStringOperators = ( }, InputComponent: GridFilterInputMultipleValue, }, -]; +]); diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 980c4f128e938..9a3734777e849 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -2,10 +2,13 @@ import * as React from 'react'; import { defaultMemoize } from 'reselect'; import { GridCellParams, + GridColDef, GridFilterItem, GridFilterModel, + GridFilterOperator, GridLogicOperator, GridRowId, + GridTreeNode, } from '../../../models'; import { GridApiCommunity } from '../../../models/api/gridApiCommunity'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; @@ -128,6 +131,27 @@ export const mergeStateWithFilterModel = filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef), }); + +export const isFastFilterFn = (fn: T) => { + return (fn as any).isFastFilter === true; +} + +export const wrapFastFilterFn = (fn: T) => { + if (fn) { + (fn as any).isFastFilter = true; + } + return fn; +} + +export const wrapFastFilterOperators = + []>(operators: T) => { + return operators.map(operator => ({ + ...operator, + getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => + wrapFastFilterFn(operator.getApplyFilterFn(filterItem, column)) + })) + } + const getFilterCallbackFromItem = ( filterItem: GridFilterItem, apiRef: React.MutableRefObject, @@ -171,11 +195,19 @@ const getFilterCallbackFromItem = ( return null; } - const fn = (rowId: GridRowId) => { - const cellParams = apiRef.current.getCellParams(rowId, newFilterItem.field!); + const isFastFilter = isFastFilterFn(applyFilterOnRow); - return applyFilterOnRow(cellParams); - }; + const fn = + isFastFilter ? + (rowId: GridRowId) => { + const value = apiRef.current.getCellValue(rowId, newFilterItem.field!); + const params = { value } as GridCellParams; + return applyFilterOnRow(params); + } : + (rowId: GridRowId) => { + const params = apiRef.current.getCellParams(rowId, newFilterItem.field!); + return applyFilterOnRow(params); + }; return { fn, item: newFilterItem }; }; @@ -203,13 +235,11 @@ export const buildAggregatedFilterItemsApplier = ( return (rowId, shouldApplyFilter) => { const resultPerItemId: GridFilterItemResult = {}; - const filteredAppliers = shouldApplyFilter - ? appliers.filter((applier) => shouldApplyFilter(applier.item.field)) - : appliers; - - filteredAppliers.forEach((applier) => { + for (const applier of appliers) { + if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) + continue; resultPerItemId[applier.item.id!] = applier.fn(rowId); - }); + } return resultPerItemId; }; From 5409eb799b83324d1225f9cff6d8f109e14047fe Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 21:48:12 -0400 Subject: [PATCH 03/24] perf: non-memoized selectors --- .../features/columns/gridColumnsSelector.ts | 4 ++-- .../hooks/features/rows/gridRowsSelector.ts | 4 ++-- .../x-data-grid/src/utils/createSelector.ts | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts index 31b471a1d392d..6338212898f69 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsSelector.ts @@ -1,4 +1,4 @@ -import { createSelector } from '../../../utils/createSelector'; +import { createSelector, createRawSelector } from '../../../utils/createSelector'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; import { GridColumnLookup } from './gridColumnsInterfaces'; @@ -21,7 +21,7 @@ export const gridColumnFieldsSelector = createSelector( * Get the columns as a lookup (an object containing the field for keys and the definition for values). * @category Columns */ -export const gridColumnLookupSelector = createSelector( +export const gridColumnLookupSelector = createRawSelector( gridColumnsStateSelector, (columnsState) => columnsState.lookup, ); diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts b/packages/grid/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts index d2cfd8754fffb..f4156c7e575c6 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/gridRowsSelector.ts @@ -1,4 +1,4 @@ -import { createSelector } from '../../../utils/createSelector'; +import { createSelector, createRawSelector } from '../../../utils/createSelector'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; const gridRowsStateSelector = (state: GridStateCommunity) => state.rows; @@ -19,7 +19,7 @@ export const gridTopLevelRowCountSelector = createSelector( ); // TODO rows v6: Rename -export const gridRowsLookupSelector = createSelector( +export const gridRowsLookupSelector = createRawSelector( gridRowsStateSelector, (rows) => rows.dataRowIdToModelLookup, ); diff --git a/packages/grid/x-data-grid/src/utils/createSelector.ts b/packages/grid/x-data-grid/src/utils/createSelector.ts index 91eaf54c67d19..d4c0af1a85530 100644 --- a/packages/grid/x-data-grid/src/utils/createSelector.ts +++ b/packages/grid/x-data-grid/src/utils/createSelector.ts @@ -86,6 +86,26 @@ export const createSelector: CreateSelectorFunction = (...args: any) => { return selector; }; +export const createRawSelector: CreateSelectorFunction = (...selectors: any) => { + const selector = (stateOrApiRef: any) => { + const isApiRef = !!stateOrApiRef.current; + const state = isApiRef ? stateOrApiRef.current.state : stateOrApiRef; + + let result = state; + for (const selector of selectors) { + result = selector(result); + } + + return result + }; + + // We use this property to detect if the selector was created with createSelector + // or it's only a simple function the receives the state and returns part of it. + selector.acceptsApiRef = true; + + return selector; +}; + // eslint-disable-next-line @typescript-eslint/naming-convention export const unstable_resetCreateSelectorCache = () => { cacheContainer.cache = new WeakMap(); From 32620fc2d370099bece933d9121a0a90e44e8de5 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 21:48:59 -0400 Subject: [PATCH 04/24] perf: reduced indirection in API methods --- .../x-data-grid/src/hooks/utils/useGridApiMethod.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/utils/useGridApiMethod.ts b/packages/grid/x-data-grid/src/hooks/utils/useGridApiMethod.ts index 14289014837de..61515867a300e 100644 --- a/packages/grid/x-data-grid/src/hooks/utils/useGridApiMethod.ts +++ b/packages/grid/x-data-grid/src/hooks/utils/useGridApiMethod.ts @@ -20,14 +20,10 @@ export function useGridApiMethod< return; } apiMethodsNames.forEach((methodName) => { - if (!privateApiRef.current.hasOwnProperty(methodName)) { - privateApiRef.current.register(visibility, { - [methodName]: (...args: any[]) => { - const fn = apiMethodsRef.current[methodName] as Function; - return fn(...args); - }, - }); - } + privateApiRef.current.register(visibility, { + [methodName]: apiMethodsRef.current[methodName], + }); + }); }, [apiMethodsNames, privateApiRef, visibility]); From 19902ee277c0192ae080c33d54b9195c25fb0556 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 21:49:39 -0400 Subject: [PATCH 05/24] perf: avoid dynamic object prop --- .../src/hooks/features/filter/gridFilterUtils.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 9a3734777e849..45a4a79c288c9 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -232,6 +232,21 @@ export const buildAggregatedFilterItemsApplier = ( return null; } + if (appliers.length === 1) { + const applier = appliers[0]; + const applierFn = applier.fn; + + const fn = eval(`(rowId, shouldApplyFilter) => { + if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) { + return {}; + } + // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away + return { '${applier.item.id!}': applierFn(rowId) }; + }`); + + return fn; + } + return (rowId, shouldApplyFilter) => { const resultPerItemId: GridFilterItemResult = {}; From a117b387b8d19a39f56a0330bb0b06e9419600b5 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 21:50:41 -0400 Subject: [PATCH 06/24] perf: direct state access --- .../grid/x-data-grid/src/hooks/features/rows/useGridRows.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts index 5592206f34c53..8aac0849d21fc 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts @@ -92,7 +92,7 @@ export const useGridRows = ( const getRow = React.useCallback( (id) => { - const model = gridRowsLookupSelector(apiRef)[id] as any; + const model = apiRef.current.state.rows.dataRowIdToModelLookup[id] as any; if (model) { return model; From e222e9afde5a099915eae0b02c0c7c7941985c79 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 25 May 2023 21:56:56 -0400 Subject: [PATCH 07/24] perf: use Set for rows lookup --- .../rowGrouping/gridRowGroupingUtils.ts | 14 +++-- .../features/treeData/gridTreeDataUtils.ts | 14 +++-- .../features/filter/gridFilterSelector.ts | 4 +- .../hooks/features/filter/gridFilterState.ts | 4 +- .../hooks/features/filter/useGridFilter.tsx | 58 ++++++++++--------- 5 files changed, 52 insertions(+), 42 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 7e5c6db8720a7..6f7079ff4ab43 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -83,8 +83,8 @@ export const filterRowTreeFromGroupingColumns = ( params: FilterRowTreeFromTreeDataParams, ): Omit => { const { rowTree, isRowMatchingFilters, filterModel } = params; - const visibleRowsLookup: Record = {}; - const filteredRowsLookup: Record = {}; + const visibleRowsLookup = new Set(); + const filteredRowsLookup = new Set(); const filteredDescendantCountLookup: Record = {}; const filterTreeNode = ( @@ -138,13 +138,15 @@ export const filterRowTreeFromGroupingColumns = ( } } - visibleRowsLookup[node.id] = isPassingFiltering && areAncestorsExpanded; - filteredRowsLookup[node.id] = isPassingFiltering; + if (isPassingFiltering && areAncestorsExpanded) + visibleRowsLookup.add(node.id); + if (isPassingFiltering) + filteredRowsLookup.add(node.id); // TODO rows v6: Should we keep storing the visibility status of footer independently or rely on the group visibility in the selector ? if (node.type === 'group' && node.footerId != null) { - visibleRowsLookup[node.footerId] = - isPassingFiltering && areAncestorsExpanded && !!node.childrenExpanded; + if (isPassingFiltering && areAncestorsExpanded && !!node.childrenExpanded) + visibleRowsLookup.add(node.footerId); } if (!isPassingFiltering) { diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts index 4127610c5fb13..f7765aec63194 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts @@ -30,8 +30,8 @@ export const filterRowTreeFromTreeData = ( params: FilterRowTreeFromTreeDataParams, ): Omit => { const { rowTree, disableChildrenFiltering, isRowMatchingFilters } = params; - const visibleRowsLookup: Record = {}; - const filteredRowsLookup: Record = {}; + const visibleRowsLookup = new Set(); + const filteredRowsLookup = new Set(); const filteredDescendantCountLookup: Record = {}; const filterTreeNode = ( @@ -86,13 +86,15 @@ export const filterRowTreeFromTreeData = ( } } - visibleRowsLookup[node.id] = shouldPassFilters && areAncestorsExpanded; - filteredRowsLookup[node.id] = shouldPassFilters; + if (shouldPassFilters && areAncestorsExpanded) + visibleRowsLookup.add(node.id); + if (shouldPassFilters) + filteredRowsLookup.add(node.id); // TODO: Should we keep storing the visibility status of footer independently or rely on the group visibility in the selector ? if (node.type === 'group' && node.footerId != null) { - visibleRowsLookup[node.footerId] = - shouldPassFilters && areAncestorsExpanded && !!node.childrenExpanded; + if (shouldPassFilters && areAncestorsExpanded && !!node.childrenExpanded) + visibleRowsLookup.add(node.footerId); } if (!shouldPassFilters) { diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts index 1b2fa88880faf..1db7bb5123888 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts @@ -64,7 +64,7 @@ export const gridExpandedSortedRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, (visibleRowsLookup, sortedRows) => - sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), + sortedRows.filter((row) => visibleRowsLookup.has(row.id)), ); /** @@ -86,7 +86,7 @@ export const gridFilteredSortedRowEntriesSelector = createSelector( gridFilteredRowsLookupSelector, gridSortedRowEntriesSelector, (filteredRowsLookup, sortedRows) => - sortedRows.filter((row) => filteredRowsLookup[row.id] !== false), + sortedRows.filter((row) => filteredRowsLookup.has(row.id)), ); /** diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts index 33953f0f3f317..d0b07129092e2 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts @@ -21,13 +21,13 @@ export interface GridFilterState { * If a row is not registered in this lookup, it is filtered. * This is the equivalent of the `visibleRowsLookup` if all the groups were expanded. */ - filteredRowsLookup: Record; + filteredRowsLookup: Set; /** * Visibility status for each row. * A row is visible if it is passing the filters AND if its parents are expanded. * If a row is not registered in this lookup, it is visible. */ - visibleRowsLookup: Record; + visibleRowsLookup: Set; /** * Amount of descendants that are passing the filters. * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...). diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx index 2cd45748ab185..7f96774c589cd 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx @@ -362,40 +362,46 @@ export const useGridFilter = ( const flatFilteringMethod = React.useCallback>( (params) => { - if (props.filterMode === 'client' && params.isRowMatchingFilters) { - const tree = gridRowTreeSelector(apiRef); - const rowIds = (tree[GRID_ROOT_GROUP_ID] as GridGroupNode).children; - const filteredRowsLookup: Record = {}; - for (let i = 0; i < rowIds.length; i += 1) { - const rowId = rowIds[i]; - let isRowPassing; - if (typeof rowId === 'string' && rowId.startsWith('auto-generated-group-footer')) { - isRowPassing = true; - } else { - const { passingFilterItems, passingQuickFilterValues } = - params.isRowMatchingFilters(rowId); - isRowPassing = passFilterLogic( - [passingFilterItems], - [passingQuickFilterValues], - params.filterModel, - apiRef, - ); - } - filteredRowsLookup[rowId] = isRowPassing; - } + if (props.filterMode !== 'client' || !params.isRowMatchingFilters) { return { - filteredRowsLookup, - // For flat tree, the `visibleRowsLookup` and the `filteredRowsLookup` are equals since no row is collapsed. - visibleRowsLookup: filteredRowsLookup, + visibleRowsLookup: new Set(), + filteredRowsLookup: new Set(), filteredDescendantCountLookup: {}, }; } + const tree = gridRowTreeSelector(apiRef); + const rowIds = (tree[GRID_ROOT_GROUP_ID] as GridGroupNode).children; + const filteredRowsLookup = new Set(); + + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; + + if (typeof rowId === 'string' && rowId.startsWith('auto-generated-group-footer')) { + filteredRowsLookup.add(rowId) + } else { + const { passingFilterItems, passingQuickFilterValues } = + params.isRowMatchingFilters(rowId); + + const isRowPassing = passFilterLogic( + [passingFilterItems], + [passingQuickFilterValues], + params.filterModel, + apiRef, + ); + + if (isRowPassing) + filteredRowsLookup.add(rowId) + } + } + return { - visibleRowsLookup: {}, - filteredRowsLookup: {}, + filteredRowsLookup, + // For flat tree, the `visibleRowsLookup` and the `filteredRowsLookup` are equals since no row is collapsed. + visibleRowsLookup: filteredRowsLookup, filteredDescendantCountLookup: {}, }; + }, [apiRef, props.filterMode], ); From efb90cfd12e4274d71dd32331c58ed4bdf034250 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:11:51 -0400 Subject: [PATCH 08/24] perf: remove allocations --- .../features/rowGrouping/gridRowGroupingUtils.ts | 11 ++++++----- .../hooks/features/treeData/gridTreeDataUtils.ts | 12 +++++++++--- .../src/hooks/features/filter/gridFilterState.ts | 13 ++++++++----- .../src/hooks/features/filter/gridFilterUtils.ts | 10 ++++------ .../src/hooks/features/filter/useGridFilter.tsx | 13 +++++++++---- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 6f7079ff4ab43..05dae89e4a119 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -13,6 +13,7 @@ import { import { passFilterLogic, GridAggregatedFilterItemApplier, + GridAggregatedFilterItemApplierResult, GridColumnRawLookup, GridApiCommunity, } from '@mui/x-data-grid-pro/internals'; @@ -90,10 +91,10 @@ export const filterRowTreeFromGroupingColumns = ( const filterTreeNode = ( node: GridTreeNode, areAncestorsExpanded: boolean, - ancestorsResults: ReturnType[], + ancestorsResults: GridAggregatedFilterItemApplierResult[], ): number => { let isPassingFiltering = false; - let filterResults: ReturnType = { + const filterResults: GridAggregatedFilterItemApplierResult = { passingFilterItems: null, passingQuickFilterValues: null, }; @@ -104,7 +105,7 @@ export const filterRowTreeFromGroupingColumns = ( ? (columnField: string) => shouldApplyFilterItemOnGroup(columnField, node) : undefined; - filterResults = isRowMatchingFilters(node.id, shouldApplyItem); + isRowMatchingFilters(node.id, shouldApplyItem, filterResults); } else { isPassingFiltering = true; } @@ -117,7 +118,7 @@ export const filterRowTreeFromGroupingColumns = ( childNode, areAncestorsExpanded && !!node.childrenExpanded, - [...ancestorsResults, filterResults], + [...ancestorsResults, Object.assign({}, filterResults)], ); filteredDescendantCount += childSubTreeSize; }); @@ -128,7 +129,7 @@ export const filterRowTreeFromGroupingColumns = ( // If node has children - it's passing if at least one child passes filters isPassingFiltering = filteredDescendantCount > 0; } else { - const allResults = [...ancestorsResults, filterResults]; + const allResults = [...ancestorsResults, Object.assign({}, filterResults)]; isPassingFiltering = passFilterLogic( allResults.map((result) => result.passingFilterItems), allResults.map((result) => result.passingQuickFilterValues), diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts index f7765aec63194..9f025b82fc9da 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts @@ -7,6 +7,7 @@ import { } from '@mui/x-data-grid'; import { GridAggregatedFilterItemApplier, + GridAggregatedFilterItemApplierResult, GridApiCommunity, passFilterLogic, } from '@mui/x-data-grid/internals'; @@ -34,6 +35,11 @@ export const filterRowTreeFromTreeData = ( const filteredRowsLookup = new Set(); const filteredDescendantCountLookup: Record = {}; + const filterResults: GridAggregatedFilterItemApplierResult = { + passingFilterItems: null, + passingQuickFilterValues: null, + }; + const filterTreeNode = ( node: GridTreeNode, isParentMatchingFilters: boolean, @@ -47,10 +53,10 @@ export const filterRowTreeFromTreeData = ( } else if (!isRowMatchingFilters || node.type === 'footer') { isMatchingFilters = true; } else { - const { passingFilterItems, passingQuickFilterValues } = isRowMatchingFilters(node.id); + isRowMatchingFilters(node.id, undefined, filterResults); isMatchingFilters = passFilterLogic( - [passingFilterItems], - [passingQuickFilterValues], + [filterResults.passingFilterItems], + [filterResults.passingQuickFilterValues], params.filterModel, params.apiRef, ); diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts index d0b07129092e2..d8862920e1efa 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts @@ -41,17 +41,20 @@ export interface GridFilterInitialState { filterModel?: GridFilterModel; } +export interface GridAggregatedFilterItemApplierResult { + passingFilterItems: null | GridFilterItemResult; + passingQuickFilterValues: null | GridQuickFilterValueResult; +}; + /** * @param {GridRowId} rowId The id of the row we want to filter. * @param {(filterItem: GridFilterItem) => boolean} shouldApplyItem An optional callback to allow the filtering engine to only apply some items. */ export type GridAggregatedFilterItemApplier = ( rowId: GridRowId, - shouldApplyItem?: (field: string) => boolean, -) => { - passingFilterItems: null | GridFilterItemResult; - passingQuickFilterValues: null | GridQuickFilterValueResult; -}; + shouldApplyItem: ((field: string) => boolean) | undefined, + result: GridAggregatedFilterItemApplierResult, +) => void; export interface GridFilteringMethodParams { isRowMatchingFilters: GridAggregatedFilterItemApplier | null; diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 45a4a79c288c9..2debfb4cd11d8 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -333,12 +333,10 @@ export const buildAggregatedFilterApplier = ( const isRowMatchingFilterItems = buildAggregatedFilterItemsApplier(filterModel, apiRef); const isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(filterModel, apiRef); - return (rowId, shouldApplyFilter) => ({ - passingFilterItems: - isRowMatchingFilterItems && isRowMatchingFilterItems(rowId, shouldApplyFilter), - passingQuickFilterValues: - isRowMatchingQuickFilter && isRowMatchingQuickFilter(rowId, shouldApplyFilter), - }); + return function isRowMatchingFilters(rowId, shouldApplyFilter, result) { + result.passingFilterItems = isRowMatchingFilterItems?.(rowId, shouldApplyFilter) ?? null; + result.passingQuickFilterValues = isRowMatchingQuickFilter?.(rowId, shouldApplyFilter) ?? null; + }; }; const filterModelItems = defaultMemoize( diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx index 7f96774c589cd..a66d9275c4542 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx @@ -373,6 +373,12 @@ export const useGridFilter = ( const tree = gridRowTreeSelector(apiRef); const rowIds = (tree[GRID_ROOT_GROUP_ID] as GridGroupNode).children; const filteredRowsLookup = new Set(); + const { isRowMatchingFilters } = params; + + const result = { + passingFilterItems: null, + passingQuickFilterValues: null, + }; for (let i = 0; i < rowIds.length; i += 1) { const rowId = rowIds[i]; @@ -380,12 +386,11 @@ export const useGridFilter = ( if (typeof rowId === 'string' && rowId.startsWith('auto-generated-group-footer')) { filteredRowsLookup.add(rowId) } else { - const { passingFilterItems, passingQuickFilterValues } = - params.isRowMatchingFilters(rowId); + isRowMatchingFilters(rowId, undefined, result); const isRowPassing = passFilterLogic( - [passingFilterItems], - [passingQuickFilterValues], + [result.passingFilterItems], + [result.passingQuickFilterValues], params.filterModel, apiRef, ); From bbe66fd290076fa7167b470c4801021c9dd1035d Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:15:33 -0400 Subject: [PATCH 09/24] perf: specialize createRawSelector --- .../x-data-grid/src/utils/createSelector.ts | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/grid/x-data-grid/src/utils/createSelector.ts b/packages/grid/x-data-grid/src/utils/createSelector.ts index d4c0af1a85530..f97c71c9701bd 100644 --- a/packages/grid/x-data-grid/src/utils/createSelector.ts +++ b/packages/grid/x-data-grid/src/utils/createSelector.ts @@ -86,25 +86,39 @@ export const createSelector: CreateSelectorFunction = (...args: any) => { return selector; }; -export const createRawSelector: CreateSelectorFunction = (...selectors: any) => { - const selector = (stateOrApiRef: any) => { - const isApiRef = !!stateOrApiRef.current; - const state = isApiRef ? stateOrApiRef.current.state : stateOrApiRef; - - let result = state; - for (const selector of selectors) { - result = selector(result); - } - - return result - }; +export const createRawSelector: CreateSelectorFunction = (( + a: Function, + b?: Function, + c?: Function, + d?: Function, + ...rest: any[] +) => { + if (rest.length > 0) throw new Error('Unsupported number of selectors'); + + let selector: any; + + if (a && b && c && d) { + selector = (stateOrApiRef: any) => { + return d(c(b(a(stateOrApiRef.current ? stateOrApiRef.current.state : stateOrApiRef)))); + }; + } else if (a && b && c) { + selector = (stateOrApiRef: any) => { + return c(b(a(stateOrApiRef.current ? stateOrApiRef.current.state : stateOrApiRef))); + }; + } else if (a && b) { + selector = (stateOrApiRef: any) => { + return b(a(stateOrApiRef.current ? stateOrApiRef.current.state : stateOrApiRef)); + }; + } else { + throw new Error('Missing arguments'); + } // We use this property to detect if the selector was created with createSelector // or it's only a simple function the receives the state and returns part of it. selector.acceptsApiRef = true; return selector; -}; +}) as unknown as CreateSelectorFunction; // eslint-disable-next-line @typescript-eslint/naming-convention export const unstable_resetCreateSelectorCache = () => { From 2fb95ff0a83f2eb412156f97ee8bc0fc285e1a8b Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:19:06 -0400 Subject: [PATCH 10/24] lint --- .../x-data-grid/src/hooks/features/filter/gridFilterState.ts | 2 +- packages/grid/x-data-grid/src/internals/index.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts index d8862920e1efa..b18e8ad3827a4 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts @@ -44,7 +44,7 @@ export interface GridFilterInitialState { export interface GridAggregatedFilterItemApplierResult { passingFilterItems: null | GridFilterItemResult; passingQuickFilterValues: null | GridQuickFilterValueResult; -}; +} /** * @param {GridRowId} rowId The id of the row we want to filter. diff --git a/packages/grid/x-data-grid/src/internals/index.ts b/packages/grid/x-data-grid/src/internals/index.ts index 061f8ea0ecd86..2d3c9b154d713 100644 --- a/packages/grid/x-data-grid/src/internals/index.ts +++ b/packages/grid/x-data-grid/src/internals/index.ts @@ -54,7 +54,10 @@ export { useGridPrintExport } from '../hooks/features/export/useGridPrintExport' export { useGridFilter, filterStateInitializer } from '../hooks/features/filter/useGridFilter'; export { passFilterLogic } from '../hooks/features/filter/gridFilterUtils'; export { isSingleSelectColDef } from '../components/panel/filterPanel/filterPanelUtils'; -export type { GridAggregatedFilterItemApplier } from '../hooks/features/filter/gridFilterState'; +export type { + GridAggregatedFilterItemApplier, + GridAggregatedFilterItemApplierResult, +} from '../hooks/features/filter/gridFilterState'; export { useGridFocus, focusStateInitializer } from '../hooks/features/focus/useGridFocus'; export { useGridKeyboardNavigation } from '../hooks/features/keyboardNavigation/useGridKeyboardNavigation'; export { From a674f207cbdd0501bdf30bf12d2c75b672cd4850 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:21:38 -0400 Subject: [PATCH 11/24] lint --- .../src/hooks/features/rowGrouping/gridRowGroupingUtils.ts | 6 ++---- .../src/hooks/features/treeData/gridTreeDataUtils.ts | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 05dae89e4a119..6838f8b16bd46 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -139,10 +139,8 @@ export const filterRowTreeFromGroupingColumns = ( } } - if (isPassingFiltering && areAncestorsExpanded) - visibleRowsLookup.add(node.id); - if (isPassingFiltering) - filteredRowsLookup.add(node.id); + if (isPassingFiltering && areAncestorsExpanded) visibleRowsLookup.add(node.id); + if (isPassingFiltering) filteredRowsLookup.add(node.id); // TODO rows v6: Should we keep storing the visibility status of footer independently or rely on the group visibility in the selector ? if (node.type === 'group' && node.footerId != null) { diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts index 9f025b82fc9da..97d6dfdbce563 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts @@ -92,10 +92,8 @@ export const filterRowTreeFromTreeData = ( } } - if (shouldPassFilters && areAncestorsExpanded) - visibleRowsLookup.add(node.id); - if (shouldPassFilters) - filteredRowsLookup.add(node.id); + if (shouldPassFilters && areAncestorsExpanded) visibleRowsLookup.add(node.id); + if (shouldPassFilters) filteredRowsLookup.add(node.id); // TODO: Should we keep storing the visibility status of footer independently or rely on the group visibility in the selector ? if (node.type === 'group' && node.footerId != null) { From e687e0d7cae14075856943d79fd13f266e67c9ce Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:37:09 -0400 Subject: [PATCH 12/24] perf: specialize passFilterLogic --- .../features/treeData/gridTreeDataUtils.ts | 8 +- .../hooks/features/filter/gridFilterUtils.ts | 102 ++++++++++++++---- .../hooks/features/filter/useGridFilter.tsx | 8 +- 3 files changed, 89 insertions(+), 29 deletions(-) diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts index 97d6dfdbce563..4262417b2ded1 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts @@ -9,7 +9,7 @@ import { GridAggregatedFilterItemApplier, GridAggregatedFilterItemApplierResult, GridApiCommunity, - passFilterLogic, + passFilterLogicSingle, } from '@mui/x-data-grid/internals'; interface FilterRowTreeFromTreeDataParams { @@ -54,9 +54,9 @@ export const filterRowTreeFromTreeData = ( isMatchingFilters = true; } else { isRowMatchingFilters(node.id, undefined, filterResults); - isMatchingFilters = passFilterLogic( - [filterResults.passingFilterItems], - [filterResults.passingQuickFilterValues], + isMatchingFilters = passFilterLogicSingle( + filterResults.passingFilterItems, + filterResults.passingQuickFilterValues, params.filterModel, params.apiRef, ); diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 2debfb4cd11d8..807b1b688425a 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -251,8 +251,7 @@ export const buildAggregatedFilterItemsApplier = ( const resultPerItemId: GridFilterItemResult = {}; for (const applier of appliers) { - if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) - continue; + if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) continue; resultPerItemId[applier.item.id!] = applier.fn(rowId); } @@ -341,9 +340,16 @@ export const buildAggregatedFilterApplier = ( const filterModelItems = defaultMemoize( (apiRef: React.MutableRefObject, items: GridFilterItem[]) => - items.filter((item) => getFilterCallbackFromItem(item, apiRef) !== null) + items.filter((item) => getFilterCallbackFromItem(item, apiRef) !== null), ); +const isValidFilterItemResult = ( + result: null | GridFilterItemResult, +): result is GridFilterItemResult => result != null; +const isValidQuickFilterResult = ( + result: null | GridQuickFilterValueResult, +): result is GridQuickFilterValueResult => result != null; + export const passFilterLogic = ( allFilterItemResults: (null | GridFilterItemResult)[], allQuickFilterResults: (null | GridQuickFilterValueResult)[], @@ -351,28 +357,81 @@ export const passFilterLogic = ( apiRef: React.MutableRefObject, ): boolean => { const cleanedFilterItems = filterModelItems(apiRef, filterModel.items); + const cleanedFilterItemResults = allFilterItemResults.filter(isValidFilterItemResult); + const cleanedQuickFilterResults = allQuickFilterResults.filter(isValidQuickFilterResult); - const cleanedAllFilterItemResults = allFilterItemResults.filter( - (result): result is GridFilterItemResult => result != null, - ); + // get result for filter items model + if (cleanedFilterItemResults.length > 0) { + // Return true if the item pass with one of the rows + const filterItemPredicate = (item: GridFilterItem) => { + return cleanedFilterItemResults.some((filterItemResult) => filterItemResult[item.id!]); + }; - const cleanedAllQuickFilterResults = allQuickFilterResults.filter( - (result): result is GridQuickFilterValueResult => result != null, - ); + const operator = filterModel.logicOperator ?? getDefaultGridFilterModel().logicOperator; + + if (operator === GridLogicOperator.And) { + const passesAllFilters = cleanedFilterItems.every(filterItemPredicate); + if (!passesAllFilters) { + return false; + } + } else { + const passesSomeFilters = cleanedFilterItems.some(filterItemPredicate); + if (!passesSomeFilters) { + return false; + } + } + } + + // get result for quick filter model + if (cleanedQuickFilterResults.length > 0 && filterModel.quickFilterValues != null) { + // Return true if the item pass with one of the rows + const quickFilterValuePredicate = (value: string) => { + return cleanedQuickFilterResults.some( + (quickFilterValueResult) => quickFilterValueResult[value], + ); + }; - // Defaultize operators - const quickFilterLogicOperator = - filterModel.quickFilterLogicOperator ?? getDefaultGridFilterModel().quickFilterLogicOperator; - const logicOperator = filterModel.logicOperator ?? getDefaultGridFilterModel().logicOperator; + const operator = + filterModel.quickFilterLogicOperator ?? getDefaultGridFilterModel().quickFilterLogicOperator; + + if (operator === GridLogicOperator.And) { + const passesAllQuickFilterValues = + filterModel.quickFilterValues.every(quickFilterValuePredicate); + if (!passesAllQuickFilterValues) { + return false; + } + } else { + const passesSomeQuickFilterValues = + filterModel.quickFilterValues.some(quickFilterValuePredicate); + if (!passesSomeQuickFilterValues) { + return false; + } + } + } + + return true; +}; + +// This function is a specialization of `passFilterLogic` for the case where there is a +// single result to process. +export const passFilterLogicSingle = ( + filterItemResult: null | GridFilterItemResult, + quickFilterResult: null | GridQuickFilterValueResult, + filterModel: GridFilterModel, + apiRef: React.MutableRefObject, +): boolean => { + const cleanedFilterItems = filterModelItems(apiRef, filterModel.items); // get result for filter items model - if (cleanedAllFilterItemResults.length > 0) { + if (filterItemResult) { // Return true if the item pass with one of the rows const filterItemPredicate = (item: GridFilterItem) => { - return cleanedAllFilterItemResults.some((filterItemResult) => filterItemResult[item.id!]); + return filterItemResult[item.id!]; }; - if (logicOperator === GridLogicOperator.And) { + const operator = filterModel.logicOperator ?? getDefaultGridFilterModel().logicOperator; + + if (operator === GridLogicOperator.And) { const passesAllFilters = cleanedFilterItems.every(filterItemPredicate); if (!passesAllFilters) { return false; @@ -386,15 +445,16 @@ export const passFilterLogic = ( } // get result for quick filter model - if (cleanedAllQuickFilterResults.length > 0 && filterModel.quickFilterValues != null) { + if (quickFilterResult && filterModel.quickFilterValues != null) { // Return true if the item pass with one of the rows const quickFilterValuePredicate = (value: string) => { - return cleanedAllQuickFilterResults.some( - (quickFilterValueResult) => quickFilterValueResult[value], - ); + return quickFilterResult[value]; }; - if (quickFilterLogicOperator === GridLogicOperator.And) { + const operator = + filterModel.quickFilterLogicOperator ?? getDefaultGridFilterModel().quickFilterLogicOperator; + + if (operator === GridLogicOperator.And) { const passesAllQuickFilterValues = filterModel.quickFilterValues.every(quickFilterValuePredicate); if (!passesAllQuickFilterValues) { diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx index a66d9275c4542..00a3bc380a15a 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx @@ -25,7 +25,7 @@ import { sanitizeFilterModel, mergeStateWithFilterModel, cleanFilterItem, - passFilterLogic, + passFilterLogicSingle, } from './gridFilterUtils'; import { GridStateInitializer } from '../../utils/useGridInitializeState'; import { isDeepEqual } from '../../../utils/utils'; @@ -388,9 +388,9 @@ export const useGridFilter = ( } else { isRowMatchingFilters(rowId, undefined, result); - const isRowPassing = passFilterLogic( - [result.passingFilterItems], - [result.passingQuickFilterValues], + const isRowPassing = passFilterLogicSingle( + result.passingFilterItems, + result.passingQuickFilterValues, params.filterModel, apiRef, ); From 02977be690658d5cc4ab798ffa2de0df698bf41c Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:37:42 -0400 Subject: [PATCH 13/24] lint --- .../x-data-grid/src/hooks/features/filter/gridFilterUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 807b1b688425a..ed867efbfd966 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -237,10 +237,10 @@ export const buildAggregatedFilterItemsApplier = ( const applierFn = applier.fn; const fn = eval(`(rowId, shouldApplyFilter) => { + // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) { - return {}; + return { '${applier.item.id!}': false }; } - // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away return { '${applier.item.id!}': applierFn(rowId) }; }`); From b917232fa2c134de1e1f2ea76a849909fa37696c Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:40:31 -0400 Subject: [PATCH 14/24] lint --- .../features/filter/gridFilterSelector.ts | 6 ++--- .../hooks/features/filter/gridFilterUtils.ts | 24 +++++++++---------- .../grid/x-data-grid/src/internals/index.ts | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts index 1db7bb5123888..fac353d56cecf 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterSelector.ts @@ -63,8 +63,7 @@ export const gridFilteredDescendantCountLookupSelector = createSelector( export const gridExpandedSortedRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, - (visibleRowsLookup, sortedRows) => - sortedRows.filter((row) => visibleRowsLookup.has(row.id)), + (visibleRowsLookup, sortedRows) => sortedRows.filter((row) => visibleRowsLookup.has(row.id)), ); /** @@ -85,8 +84,7 @@ export const gridExpandedSortedRowIdsSelector = createSelector( export const gridFilteredSortedRowEntriesSelector = createSelector( gridFilteredRowsLookupSelector, gridSortedRowEntriesSelector, - (filteredRowsLookup, sortedRows) => - sortedRows.filter((row) => filteredRowsLookup.has(row.id)), + (filteredRowsLookup, sortedRows) => sortedRows.filter((row) => filteredRowsLookup.has(row.id)), ); /** diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index ed867efbfd966..8c065b57b1f6f 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -131,26 +131,26 @@ export const mergeStateWithFilterModel = filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef), }); - export const isFastFilterFn = (fn: T) => { return (fn as any).isFastFilter === true; -} +}; export const wrapFastFilterFn = (fn: T) => { if (fn) { (fn as any).isFastFilter = true; } return fn; -} - -export const wrapFastFilterOperators = - []>(operators: T) => { - return operators.map(operator => ({ - ...operator, - getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => - wrapFastFilterFn(operator.getApplyFilterFn(filterItem, column)) - })) - } +}; + +export const wrapFastFilterOperators = []>( + operators: T, +) => { + return operators.map((operator) => ({ + ...operator, + getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => + wrapFastFilterFn(operator.getApplyFilterFn(filterItem, column)), + })); +}; const getFilterCallbackFromItem = ( filterItem: GridFilterItem, diff --git a/packages/grid/x-data-grid/src/internals/index.ts b/packages/grid/x-data-grid/src/internals/index.ts index 2d3c9b154d713..a4e9a97f5bf26 100644 --- a/packages/grid/x-data-grid/src/internals/index.ts +++ b/packages/grid/x-data-grid/src/internals/index.ts @@ -52,7 +52,7 @@ export { useGridDensity, densityStateInitializer } from '../hooks/features/densi export { useGridCsvExport } from '../hooks/features/export/useGridCsvExport'; export { useGridPrintExport } from '../hooks/features/export/useGridPrintExport'; export { useGridFilter, filterStateInitializer } from '../hooks/features/filter/useGridFilter'; -export { passFilterLogic } from '../hooks/features/filter/gridFilterUtils'; +export { passFilterLogic, passFilterLogicSingle } from '../hooks/features/filter/gridFilterUtils'; export { isSingleSelectColDef } from '../components/panel/filterPanel/filterPanelUtils'; export type { GridAggregatedFilterItemApplier, From 65694fb0c15c25605983fd52599059f220702d1d Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sun, 28 May 2023 19:41:34 -0400 Subject: [PATCH 15/24] chore: synchronous demo data --- .../src/hooks/useDemoData.ts | 32 ++++++++++++++- .../src/services/real-data-service.ts | 41 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts b/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts index 677ff89be441b..de4171e8c1254 100644 --- a/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts +++ b/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; import { GridColumnVisibilityModel } from '@mui/x-data-grid-premium'; -import { GridDemoData, getRealGridData } from '../services/real-data-service'; +import { GridDemoData, getRealGridData, getRealGridDataSync } from '../services/real-data-service'; import { getCommodityColumns } from '../columns/commodities.columns'; import { getEmployeeColumns } from '../columns/employees.columns'; import asyncWorker from '../services/asyncWorker'; @@ -215,3 +215,33 @@ export const useDemoData = (options: UseDemoDataOptions): DemoDataReturnType => }, }; }; + +export const useDemoDataSync = (options: UseDemoDataOptions): DemoDataReturnType => { + const [rowLength, setRowLength] = React.useState(options.rowLength); + const [, setIndex] = React.useState(0); + const [loading] = React.useState(true); + + const columns = React.useMemo(() => { + return getColumnsFromOptions({ + dataSet: options.dataSet, + editable: options.editable, + maxColumns: options.maxColumns, + visibleFields: options.visibleFields, + }); + }, [options.dataSet, options.editable, options.maxColumns, options.visibleFields]); + + const data = addTreeDataOptionsToDemoData(getRealGridDataSync(rowLength, columns), { + maxDepth: options.treeData?.maxDepth, + groupingField: options.treeData?.groupingField, + averageChildren: options.treeData?.averageChildren, + }); + + return { + data, + loading, + setRowLength, + loadNewData: () => { + setIndex((oldIndex) => oldIndex + 1); + }, + }; +}; diff --git a/packages/grid/x-data-grid-generator/src/services/real-data-service.ts b/packages/grid/x-data-grid-generator/src/services/real-data-service.ts index 734126b13f339..b290fff088cce 100644 --- a/packages/grid/x-data-grid-generator/src/services/real-data-service.ts +++ b/packages/grid/x-data-grid-generator/src/services/real-data-service.ts @@ -60,3 +60,44 @@ export function getRealGridData( }); }); } + +export function getRealGridDataSync( + rowLength: number, + columns: GridColDefGenerator[], +): GridDemoData { + const rows: GridDemoData['rows'] = []; + const indexedValues: { [field: string]: { [value: string]: number } } = {}; + + for (let i = 0; i < rowLength; i += 1) { + const row: any = {}; + + for (let j = 0; j < columns.length; j += 1) { + const column = columns[j]; + if (column.generateData) { + const context: GridDataGeneratorContext = {}; + if (column.dataGeneratorUniquenessEnabled) { + let fieldValues = indexedValues[column.field]; + if (!fieldValues) { + fieldValues = {}; + indexedValues[column.field] = fieldValues; + } + + context.values = fieldValues; + } + + row[column.field] = column.generateData(row, context); + } + } + + rows.push(row); + } + + const columnVisibilityModel: GridColumnVisibilityModel = {}; + columns.forEach((col) => { + if (col.hide) { + columnVisibilityModel[col.field] = false; + } + }); + + return { columns, rows, initialState: { columns: { columnVisibilityModel } } }; +} From 6296c812b065c41f00a5f09390d87aab04ac39a8 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 01:27:24 -0400 Subject: [PATCH 16/24] perf: new filtering API --- .../useDataGridPremiumComponent.tsx | 2 +- .../rowGrouping/gridRowGroupingUtils.ts | 3 +- .../DataGridPro/useDataGridProComponent.tsx | 2 +- .../src/DataGrid/useDataGridComponent.tsx | 2 +- .../src/colDef/gridBooleanOperators.ts | 4 +- .../src/colDef/gridDateOperators.ts | 27 ++-- .../src/colDef/gridNumericOperators.ts | 36 ++--- .../src/colDef/gridSingleSelectOperators.ts | 14 +- .../src/colDef/gridStringOperators.ts | 35 +++-- .../hooks/features/filter/gridFilterState.ts | 4 +- .../hooks/features/filter/gridFilterUtils.ts | 132 ++++++++++-------- .../hooks/features/filter/useGridFilter.tsx | 40 +++--- .../hooks/features/rows/useGridParamsApi.ts | 23 ++- .../src/hooks/utils/useGridApiMethod.ts | 1 - .../src/models/api/gridParamsApi.ts | 11 +- .../src/models/gridFilterOperator.ts | 53 +++++-- 16 files changed, 229 insertions(+), 160 deletions(-) diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx index 960bc5f166f23..4da70394a3142 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx @@ -145,7 +145,7 @@ export const useDataGridPremiumComponent = ( useGridRowPinning(privateApiRef, props); useGridColumns(privateApiRef, props); useGridRows(privateApiRef, props); - useGridParamsApi(privateApiRef); + useGridParamsApi(privateApiRef, props); useGridDetailPanel(privateApiRef, props); useGridColumnSpanning(privateApiRef); useGridColumnGrouping(privateApiRef, props); diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 6838f8b16bd46..ea1deaacd8a45 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -105,7 +105,8 @@ export const filterRowTreeFromGroupingColumns = ( ? (columnField: string) => shouldApplyFilterItemOnGroup(columnField, node) : undefined; - isRowMatchingFilters(node.id, shouldApplyItem, filterResults); + const row = params.apiRef.current.getRow(node.id); + isRowMatchingFilters(row, shouldApplyItem, filterResults); } else { isPassingFiltering = true; } diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index 36ff4a78c824b..e9077e1996b1d 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -128,7 +128,7 @@ export const useDataGridProComponent = ( useGridRowPinning(apiRef, props); useGridColumns(apiRef, props); useGridRows(apiRef, props); - useGridParamsApi(apiRef); + useGridParamsApi(apiRef, props); useGridDetailPanel(apiRef, props); useGridColumnSpanning(apiRef); useGridColumnGrouping(apiRef, props); diff --git a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx index a43203f3a9a64..2e0db8b05d205 100644 --- a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx +++ b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx @@ -79,7 +79,7 @@ export const useDataGridComponent = ( useGridRowSelection(privateApiRef, props); useGridColumns(privateApiRef, props); useGridRows(privateApiRef, props); - useGridParamsApi(privateApiRef); + useGridParamsApi(privateApiRef, props); useGridColumnSpanning(privateApiRef); useGridColumnGrouping(privateApiRef, props); useGridEditing(privateApiRef, props); diff --git a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts index e702a0c0ef2f7..4435251cf85f5 100644 --- a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts @@ -5,13 +5,13 @@ import { GridFilterOperator } from '../models/gridFilterOperator'; export const getGridBooleanOperators = (): GridFilterOperator[] => [ { value: 'is', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!filterItem.value) { return null; } const valueAsBoolean = filterItem.value === 'true'; - return ({ value }): boolean => { + return (value, _, __): boolean => { return Boolean(value) === valueAsBoolean; }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts index c3473599576ed..b8b5f4ab84a4d 100644 --- a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts @@ -1,7 +1,6 @@ import { GridFilterInputDate } from '../components/panel/filterPanel/GridFilterInputDate'; import { GridFilterItem } from '../models/gridFilterItem'; -import { GridFilterOperator } from '../models/gridFilterOperator'; -import { GridCellParams } from '../models/params/gridCellParams'; +import { GridFilterOperator, ApplyFilterV7 } from '../models/gridFilterOperator'; const dateRegex = /(\d+)-(\d+)-(\d+)/; const dateTimeRegex = /(\d+)-(\d+)-(\d+)T(\d+):(\d+)/; @@ -11,7 +10,7 @@ function buildApplyFilterFn( compareFn: (value1: number, value2: number) => boolean, showTime?: boolean, keepHours?: boolean, -) { +): ApplyFilterV7 | null { if (!filterItem.value) { return null; } @@ -23,7 +22,7 @@ function buildApplyFilterFn( const time = new Date(year, month - 1, day, hour || 0, minute || 0).getTime(); - return ({ value }: GridCellParams): boolean => { + return (value, _, __): boolean => { if (!value) { return false; } @@ -47,7 +46,7 @@ function buildApplyFilterFn( export const getGridDateOperators = (showTime?: boolean): GridFilterOperator[] => [ { value: 'is', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn(filterItem, (value1, value2) => value1 === value2, showTime); }, InputComponent: GridFilterInputDate, @@ -55,7 +54,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn(filterItem, (value1, value2) => value1 !== value2, showTime); }, InputComponent: GridFilterInputDate, @@ -63,7 +62,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn(filterItem, (value1, value2) => value1 > value2, showTime); }, InputComponent: GridFilterInputDate, @@ -71,7 +70,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn(filterItem, (value1, value2) => value1 >= value2, showTime); }, InputComponent: GridFilterInputDate, @@ -79,7 +78,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn( filterItem, (value1, value2) => value1 < value2, @@ -92,7 +91,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { + getApplyFilterFnV7: (filterItem) => { return buildApplyFilterFn(filterItem, (value1, value2) => value1 <= value2, showTime); }, InputComponent: GridFilterInputDate, @@ -100,8 +99,8 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value == null; }; }, @@ -109,8 +108,8 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value != null; }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts index 182b839e6f2c6..56629c50483b1 100644 --- a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts @@ -28,12 +28,12 @@ export const getGridNumericOperators = (): GridFilterOperator< >[] => [ { value: '=', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { return parseNumericValue(value) === filterItem.value; }; }, @@ -42,12 +42,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: '!=', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { return parseNumericValue(value) !== filterItem.value; }; }, @@ -56,12 +56,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: '>', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { if (value == null) { return false; } @@ -74,12 +74,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: '>=', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { if (value == null) { return false; } @@ -92,12 +92,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: '<', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { if (value == null) { return false; } @@ -110,12 +110,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: '<=', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || Number.isNaN(filterItem.value)) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { if (value == null) { return false; } @@ -128,8 +128,8 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: 'isEmpty', - getApplyFilterFn: () => { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value == null; }; }, @@ -137,8 +137,8 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: 'isNotEmpty', - getApplyFilterFn: () => { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value != null; }; }, @@ -146,12 +146,12 @@ export const getGridNumericOperators = (): GridFilterOperator< }, { value: 'isAnyOf', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { return null; } - return ({ value }): boolean => { + return (value, _, __): boolean => { return value != null && filterItem.value.includes(Number(value)); }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts index 48b6cda0a3713..3310a39735d06 100644 --- a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts @@ -13,32 +13,34 @@ const parseObjectValue = (value: unknown) => { export const getGridSingleSelectOperators = (): GridFilterOperator[] => [ { value: 'is', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || filterItem.value === '') { return null; } - return ({ value }): boolean => parseObjectValue(value) === parseObjectValue(filterItem.value); + return (value, _, __): boolean => + parseObjectValue(value) === parseObjectValue(filterItem.value); }, InputComponent: GridFilterInputSingleSelect, }, { value: 'not', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (filterItem.value == null || filterItem.value === '') { return null; } - return ({ value }): boolean => parseObjectValue(value) !== parseObjectValue(filterItem.value); + return (value, _, __): boolean => + parseObjectValue(value) !== parseObjectValue(filterItem.value); }, InputComponent: GridFilterInputSingleSelect, }, { value: 'isAnyOf', - getApplyFilterFn: (filterItem) => { + getApplyFilterFnV7: (filterItem) => { if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { return null; } const filterItemValues = filterItem.value.map(parseObjectValue); - return ({ value }): boolean => filterItemValues.includes(parseObjectValue(value)); + return (value, _, __): boolean => filterItemValues.includes(parseObjectValue(value)); }, InputComponent: GridFilterInputMultipleSingleSelect, }, diff --git a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts index 8274a847547cb..4c1f76bbfa83b 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts @@ -4,7 +4,6 @@ import { GridFilterItem } from '../models/gridFilterItem'; import { GridFilterOperator } from '../models/gridFilterOperator'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; import { GridCellParams } from '../models'; -import { wrapFastFilterOperators } from '../hooks/features/filter/gridFilterUtils'; export const getGridStringQuickFilterFn = (value: any) => { if (!value) { @@ -18,32 +17,32 @@ export const getGridStringQuickFilterFn = (value: any) => { export const getGridStringOperators = ( disableTrim: boolean = false, -): GridFilterOperator[] => wrapFastFilterOperators([ +): GridFilterOperator[] => [ { value: 'contains', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!filterItem.value) { return null; } const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(escapeRegExp(filterItemValue), 'i'); - return ({ value }): boolean => { - return value != null ? filterRegex.test(value.toString()) : false; + return (value, _, __): boolean => { + return value != null ? filterRegex.test(String(value)) : false; }; }, InputComponent: GridFilterInputValue, }, { value: 'equals', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!filterItem.value) { return null; } const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return ({ value }): boolean => { + return (value, _, __): boolean => { return value != null ? collator.compare(filterItemValue, value.toString()) === 0 : false; }; }, @@ -51,14 +50,14 @@ export const getGridStringOperators = ( }, { value: 'startsWith', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!filterItem.value) { return null; } const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(`^${escapeRegExp(filterItemValue)}.*$`, 'i'); - return ({ value }): boolean => { + return (value, _, __): boolean => { return value != null ? filterRegex.test(value.toString()) : false; }; }, @@ -66,14 +65,14 @@ export const getGridStringOperators = ( }, { value: 'endsWith', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!filterItem.value) { return null; } const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(`.*${escapeRegExp(filterItemValue)}$`, 'i'); - return ({ value }): boolean => { + return (value, _, __): boolean => { return value != null ? filterRegex.test(value.toString()) : false; }; }, @@ -81,8 +80,8 @@ export const getGridStringOperators = ( }, { value: 'isEmpty', - getApplyFilterFn: () => { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value === '' || value == null; }; }, @@ -90,8 +89,8 @@ export const getGridStringOperators = ( }, { value: 'isNotEmpty', - getApplyFilterFn: () => { - return ({ value }): boolean => { + getApplyFilterFnV7: () => { + return (value, _, __): boolean => { return value !== '' && value != null; }; }, @@ -99,7 +98,7 @@ export const getGridStringOperators = ( }, { value: 'isAnyOf', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFnV7: (filterItem: GridFilterItem) => { if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { return null; } @@ -108,7 +107,7 @@ export const getGridStringOperators = ( : filterItem.value.map((val) => val.trim()); const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return ({ value }): boolean => + return (value, _, __): boolean => value != null ? filterItemValue.some((filterValue: GridFilterItem['value']) => { return collator.compare(filterValue, value.toString() || '') === 0; @@ -117,4 +116,4 @@ export const getGridStringOperators = ( }, InputComponent: GridFilterInputMultipleValue, }, -]); +]; diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts index b18e8ad3827a4..ca3257013ec4e 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterState.ts @@ -1,6 +1,6 @@ import { GridFilterItem, GridLogicOperator } from '../../../models/gridFilterItem'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { GridRowId } from '../../../models/gridRows'; +import { GridRowId, GridValidRowModel } from '../../../models/gridRows'; export type GridFilterItemResult = { [key: Required['id']]: boolean }; export type GridQuickFilterValueResult = { [key: string]: boolean }; @@ -51,7 +51,7 @@ export interface GridAggregatedFilterItemApplierResult { * @param {(filterItem: GridFilterItem) => boolean} shouldApplyItem An optional callback to allow the filtering engine to only apply some items. */ export type GridAggregatedFilterItemApplier = ( - rowId: GridRowId, + row: GridValidRowModel, shouldApplyItem: ((field: string) => boolean) | undefined, result: GridAggregatedFilterItemApplierResult, ) => void; diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 8c065b57b1f6f..172eb031c524b 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -8,7 +8,9 @@ import { GridFilterOperator, GridLogicOperator, GridRowId, + GridRowIdGetter, GridTreeNode, + GridValidRowModel, } from '../../../models'; import { GridApiCommunity } from '../../../models/api/gridApiCommunity'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; @@ -21,13 +23,20 @@ import { import { buildWarning } from '../../../utils/warning'; import { gridColumnFieldsSelector, gridColumnLookupSelector } from '../columns'; -type GridFilterItemApplier = { - fn: (rowId: GridRowId) => boolean; - item: GridFilterItem; -}; +type GridFilterItemApplier = + | { + v7?: false; + fn: (rowId: GridRowId) => boolean; + item: GridFilterItem; + } + | { + v7: true; + fn: (row: GridValidRowModel) => boolean; + item: GridFilterItem; + }; type GridFilterItemApplierNotAggregated = ( - rowId: GridRowId, + row: GridValidRowModel, shouldApplyItem?: (field: string) => boolean, ) => GridFilterItemResult; @@ -131,27 +140,6 @@ export const mergeStateWithFilterModel = filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef), }); -export const isFastFilterFn = (fn: T) => { - return (fn as any).isFastFilter === true; -}; - -export const wrapFastFilterFn = (fn: T) => { - if (fn) { - (fn as any).isFastFilter = true; - } - return fn; -}; - -export const wrapFastFilterOperators = []>( - operators: T, -) => { - return operators.map((operator) => ({ - ...operator, - getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => - wrapFastFilterFn(operator.getApplyFilterFn(filterItem, column)), - })); -}; - const getFilterCallbackFromItem = ( filterItem: GridFilterItem, apiRef: React.MutableRefObject, @@ -190,35 +178,45 @@ const getFilterCallbackFromItem = ( ); } + if ('getApplyFilterFnV7' in filterOperator) { + const applyFilterOnRow = filterOperator.getApplyFilterFnV7(newFilterItem, column)!; + if (typeof applyFilterOnRow !== 'function') { + return null; + } + return { + v7: true, + item: newFilterItem, + fn: (row: GridValidRowModel) => { + const value = apiRef.current.getRowValue(row, column); + return applyFilterOnRow(value, row, column); + }, + }; + } + const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; if (typeof applyFilterOnRow !== 'function') { return null; } - const isFastFilter = isFastFilterFn(applyFilterOnRow); - - const fn = - isFastFilter ? - (rowId: GridRowId) => { - const value = apiRef.current.getCellValue(rowId, newFilterItem.field!); - const params = { value } as GridCellParams; - return applyFilterOnRow(params); - } : - (rowId: GridRowId) => { - const params = apiRef.current.getCellParams(rowId, newFilterItem.field!); - return applyFilterOnRow(params); - }; - - return { fn, item: newFilterItem }; + return { + v7: false, + item: newFilterItem, + fn: (rowId: GridRowId) => { + const params = apiRef.current.getCellParams(rowId, newFilterItem.field!); + return applyFilterOnRow(params); + }, + }; }; /** * Generates a method to easily check if a row is matching the current filter model. + * @param {GridRowIdGetter | undefined} getRowId The getter for row's id. * @param {GridFilterModel} filterModel The model with which we want to filter the rows. * @param {React.MutableRefObject} apiRef The API of the grid. * @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters. */ -export const buildAggregatedFilterItemsApplier = ( +const buildAggregatedFilterItemsApplier = ( + getRowId: GridRowIdGetter | undefined, filterModel: GridFilterModel, apiRef: React.MutableRefObject, ): GridFilterItemApplierNotAggregated | null => { @@ -236,23 +234,31 @@ export const buildAggregatedFilterItemsApplier = ( const applier = appliers[0]; const applierFn = applier.fn; - const fn = eval(`(rowId, shouldApplyFilter) => { - // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away - if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) { - return { '${applier.item.id!}': false }; + const fn = eval(` + (row, shouldApplyFilter) => { + // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away + + if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) { + return { '${applier.item.id!}': false }; + } + return { '${applier.item.id!}': ${ + applier.v7 ? 'applierFn(row)' : 'applierFn(getRowId ? getRowId(row) : row.id)' + } }; } - return { '${applier.item.id!}': applierFn(rowId) }; - }`); + `); return fn; } - return (rowId, shouldApplyFilter) => { + return (row, shouldApplyFilter) => { const resultPerItemId: GridFilterItemResult = {}; - for (const applier of appliers) { + for (let i = 0; i < appliers.length; i++) { + const applier = appliers[i]; if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) continue; - resultPerItemId[applier.item.id!] = applier.fn(rowId); + resultPerItemId[applier.item.id!] = applier.v7 + ? applier.fn(row) + : applier.fn(getRowId ? getRowId(row) : row.id); } return resultPerItemId; @@ -261,11 +267,13 @@ export const buildAggregatedFilterItemsApplier = ( /** * Generates a method to easily check if a row is matching the current quick filter. - * @param {any[]} values The model with which we want to filter the rows. + * @param {GridRowIdGetter | undefined} getRowId The getter for row's id. + * @param {GridFilterItem} filterModel The model with which we want to filter the rows. * @param {React.MutableRefObject} apiRef The API of the grid. * @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters. */ -export const buildAggregatedQuickFilterApplier = ( +const buildAggregatedQuickFilterApplier = ( + getRowId: GridRowIdGetter | undefined, filterModel: GridFilterModel, apiRef: React.MutableRefObject, ): GridFilterItemApplierNotAggregated | null => { @@ -299,13 +307,16 @@ export const buildAggregatedQuickFilterApplier = ( return null; } - return (rowId, shouldApplyFilter) => { + return (row, shouldApplyFilter) => { const usedCellParams: { [field: string]: GridCellParams } = {}; const fieldsToFilter: string[] = []; Object.keys(appliersPerField).forEach((field) => { if (!shouldApplyFilter || shouldApplyFilter(field)) { - usedCellParams[field] = apiRef.current.getCellParams(rowId, field); + usedCellParams[field] = apiRef.current.getCellParams( + getRowId ? getRowId(row) : row.id, + field, + ); fieldsToFilter.push(field); } }); @@ -326,15 +337,16 @@ export const buildAggregatedQuickFilterApplier = ( }; export const buildAggregatedFilterApplier = ( + getRowId: GridRowIdGetter | undefined, filterModel: GridFilterModel, apiRef: React.MutableRefObject, ): GridAggregatedFilterItemApplier => { - const isRowMatchingFilterItems = buildAggregatedFilterItemsApplier(filterModel, apiRef); - const isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(filterModel, apiRef); + const isRowMatchingFilterItems = buildAggregatedFilterItemsApplier(getRowId, filterModel, apiRef); + const isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(getRowId, filterModel, apiRef); - return function isRowMatchingFilters(rowId, shouldApplyFilter, result) { - result.passingFilterItems = isRowMatchingFilterItems?.(rowId, shouldApplyFilter) ?? null; - result.passingQuickFilterValues = isRowMatchingQuickFilter?.(rowId, shouldApplyFilter) ?? null; + return function isRowMatchingFilters(row, shouldApplyFilter, result) { + result.passingFilterItems = isRowMatchingFilterItems?.(row, shouldApplyFilter) ?? null; + result.passingQuickFilterValues = isRowMatchingQuickFilter?.(row, shouldApplyFilter) ?? null; }; }; diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx index 00a3bc380a15a..2c46c8358e47b 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx @@ -55,6 +55,8 @@ export const useGridFilter = ( apiRef: React.MutableRefObject, props: Pick< DataGridProcessedProps, + | 'rows' + | 'getRowId' | 'initialState' | 'filterModel' | 'onFilterModelChange' @@ -79,7 +81,9 @@ export const useGridFilter = ( apiRef.current.setState((state) => { const filterModel = gridFilterModelSelector(state, apiRef.current.instanceId); const isRowMatchingFilters = - props.filterMode === 'client' ? buildAggregatedFilterApplier(filterModel, apiRef) : null; + props.filterMode === 'client' + ? buildAggregatedFilterApplier(props.getRowId, filterModel, apiRef) + : null; const filteringResult = apiRef.current.applyStrategyProcessor('filtering', { isRowMatchingFilters, @@ -370,8 +374,7 @@ export const useGridFilter = ( }; } - const tree = gridRowTreeSelector(apiRef); - const rowIds = (tree[GRID_ROOT_GROUP_ID] as GridGroupNode).children; + const rows = props.rows; const filteredRowsLookup = new Set(); const { isRowMatchingFilters } = params; @@ -380,35 +383,32 @@ export const useGridFilter = ( passingQuickFilterValues: null, }; - for (let i = 0; i < rowIds.length; i += 1) { - const rowId = rowIds[i]; + for (let i = 0; i < rows.length; i += 1) { + const row = rows[i]; - if (typeof rowId === 'string' && rowId.startsWith('auto-generated-group-footer')) { - filteredRowsLookup.add(rowId) - } else { - isRowMatchingFilters(rowId, undefined, result); + isRowMatchingFilters(row, undefined, result); - const isRowPassing = passFilterLogicSingle( - result.passingFilterItems, - result.passingQuickFilterValues, - params.filterModel, - apiRef, - ); + const isRowPassing = passFilterLogicSingle( + result.passingFilterItems, + result.passingQuickFilterValues, + params.filterModel, + apiRef, + ); - if (isRowPassing) - filteredRowsLookup.add(rowId) - } + if (isRowPassing) filteredRowsLookup.add(row.id); } + // XXX: Is props.rows what we want? + // XXX: Handle footer rows + return { filteredRowsLookup, // For flat tree, the `visibleRowsLookup` and the `filteredRowsLookup` are equals since no row is collapsed. visibleRowsLookup: filteredRowsLookup, filteredDescendantCountLookup: {}, }; - }, - [apiRef, props.filterMode], + [apiRef, props.filterMode, props.rows], ); useGridRegisterPipeProcessor(apiRef, 'columnMenu', addColumnMenuItem); diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts index 67f86592f8354..0c8e0eab00724 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts @@ -4,6 +4,7 @@ import { GridParamsApi } from '../../../models/api/gridParamsApi'; import { GridRowId, GridTreeNodeWithRender } from '../../../models/gridRows'; import { GridCellParams, GridValueGetterParams } from '../../../models/params/gridCellParams'; import { GridRowParams } from '../../../models/params/gridRowParams'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { getGridCellElement, getGridColumnHeaderElement, @@ -31,7 +32,12 @@ function warnMissingColumn(field: string) { * TODO: Impossible priority - useGridEditing also needs to be after useGridParamsApi * TODO: Impossible priority - useGridFocus also needs to be after useGridParamsApi */ -export function useGridParamsApi(apiRef: React.MutableRefObject) { +export function useGridParamsApi( + apiRef: React.MutableRefObject, + props: Pick, +) { + const { getRowId } = props; + const getColumnHeaderParams = React.useCallback( (field) => ({ field, @@ -154,6 +160,20 @@ export function useGridParamsApi(apiRef: React.MutableRefObject( + (row, colDef) => { + const id = getRowId ? getRowId(row) : row.id; + const field = colDef.field; + + if (!colDef || !colDef.valueGetter) { + return row[field]; + } + + return colDef.valueGetter(getBaseCellParams(id, field)); + }, + [apiRef, getBaseCellParams, getRowId], + ); + const getColumnHeaderElement = React.useCallback( (field) => { if (!apiRef.current.rootElementRef!.current) { @@ -184,6 +204,7 @@ export function useGridParamsApi(apiRef: React.MutableRefObject(id: GridRowId, field: string) => V; + /** + * Gets the value of a row + * @template V + * @param {GridRowModel} row The row model. + * @param {GridColDef} field The column definition. + * @returns {v} The cell value. + */ + getRowValue: (row: GridRowModel, colDef: GridColDef) => V; /** * Gets the underlying DOM element for a cell at the given `id` and `field`. * @param {GridRowId} id The id of the row. diff --git a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts index df045f81fd18e..0f6464cf21e1d 100644 --- a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts +++ b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts @@ -4,12 +4,50 @@ import { GridCellParams } from './params/gridCellParams'; import type { GridColDef } from './colDef/gridColDef'; import type { GridValidRowModel } from './gridRows'; +export type ApplyFilterV7 = ( + value: V, + row: R, + column: GridColDef, +) => boolean; + +type ApplyFilterInterface = + | { + /** + * The callback that generates a filtering function for a given filter item and column. + * This function can return `null` to skip filtering for this item and column. + * @param {GridFilterItem} filterItem The filter item with which we want to filter the column. + * @param {GridColDef} column The column from which we want to filter the rows. + * @returns {null | ((params: GridCellParams) => boolean)} The function to call to check if a row pass this filter item or not. + */ + getApplyFilterFn: ( + filterItem: GridFilterItem, + column: GridColDef, + ) => null | ((params: GridCellParams) => boolean); + } + | { + /** + * The callback that generates a filtering function for a given filter item and column. + * This function can return `null` to skip filtering for this item and column. + * @param {GridFilterItem} filterItem The filter item with which we want to filter the column. + * @param {GridColDef} column The column from which we want to filter the rows. + * @returns {null | ((value: V, row: R, column: GridColDef) => boolean)} The function to call to check if a row pass this filter item or not. + */ + getApplyFilterFnV7: ( + filterItem: GridFilterItem, + column: GridColDef, + ) => null | ApplyFilterV7; + }; + /** * Filter operator definition interface. * @demos * - [Custom filter operator](/x/react-data-grid/filtering/#create-a-custom-operator) */ -export interface GridFilterOperator { +export type GridFilterOperator< + R extends GridValidRowModel = any, + V = any, + F = V, +> = ApplyFilterInterface & { /** * The label of the filter operator. */ @@ -23,17 +61,6 @@ export interface GridFilterOperator boolean)} The function to call to check if a row pass this filter item or not. - */ - getApplyFilterFn: ( - filterItem: GridFilterItem, - column: GridColDef, - ) => null | ((params: GridCellParams) => boolean); /** * The input component to render in the filter panel for this filter operator. */ @@ -54,4 +81,4 @@ export interface GridFilterOperator Date: Mon, 29 May 2023 11:08:00 -0400 Subject: [PATCH 17/24] perf: new filtering API --- .../src/colDef/gridBooleanOperators.ts | 2 +- .../src/colDef/gridDateOperators.ts | 10 +- .../src/colDef/gridNumericColDef.ts | 2 +- .../src/colDef/gridNumericOperators.ts | 24 ++-- .../src/colDef/gridSingleSelectOperators.ts | 6 +- .../src/colDef/gridStringColDef.ts | 2 +- .../src/colDef/gridStringOperators.ts | 21 ++-- .../hooks/features/filter/gridFilterUtils.ts | 110 ++++++++++++------ .../hooks/features/rows/useGridParamsApi.ts | 19 +++ .../src/models/api/gridParamsApi.ts | 8 ++ .../src/models/colDef/gridColDef.ts | 16 +++ .../src/models/gridFilterOperator.ts | 6 +- 12 files changed, 153 insertions(+), 73 deletions(-) diff --git a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts index 4435251cf85f5..ee45f856312bf 100644 --- a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts @@ -11,7 +11,7 @@ export const getGridBooleanOperators = (): GridFilterOperator { + return (value, _, __, ___): boolean => { return Boolean(value) === valueAsBoolean; }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts index b8b5f4ab84a4d..75d33f704afd0 100644 --- a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts @@ -1,6 +1,6 @@ import { GridFilterInputDate } from '../components/panel/filterPanel/GridFilterInputDate'; import { GridFilterItem } from '../models/gridFilterItem'; -import { GridFilterOperator, ApplyFilterV7 } from '../models/gridFilterOperator'; +import { GridFilterOperator, GridApplyFilterV7 } from '../models/gridFilterOperator'; const dateRegex = /(\d+)-(\d+)-(\d+)/; const dateTimeRegex = /(\d+)-(\d+)-(\d+)T(\d+):(\d+)/; @@ -10,7 +10,7 @@ function buildApplyFilterFn( compareFn: (value1: number, value2: number) => boolean, showTime?: boolean, keepHours?: boolean, -): ApplyFilterV7 | null { +): GridApplyFilterV7 | null { if (!filterItem.value) { return null; } @@ -22,7 +22,7 @@ function buildApplyFilterFn( const time = new Date(year, month - 1, day, hour || 0, minute || 0).getTime(); - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { if (!value) { return false; } @@ -100,7 +100,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value == null; }; }, @@ -109,7 +109,7 @@ export const getGridDateOperators = (showTime?: boolean): GridFilterOperator { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null; }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts b/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts index cca2a740ff04a..178f1ded8e5ad 100644 --- a/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts +++ b/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts @@ -13,5 +13,5 @@ export const GRID_NUMERIC_COL_DEF: GridColTypeDef (value === '' ? null : Number(value)), valueFormatter: ({ value }) => (isNumber(value) ? value.toLocaleString() : value || ''), filterOperators: getGridNumericOperators(), - getApplyQuickFilterFn: getGridNumericQuickFilterFn, + getApplyQuickFilterFnV7: getGridNumericQuickFilterFn, }; diff --git a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts index 56629c50483b1..edf3b024e95ea 100644 --- a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts @@ -1,7 +1,7 @@ import { GridFilterInputValue } from '../components/panel/filterPanel/GridFilterInputValue'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; import { GridFilterOperator } from '../models/gridFilterOperator'; -import { GridCellParams } from '../models'; +import type { GridApplyQuickFilterV7 } from '../models/colDef/gridColDef'; const parseNumericValue = (value: unknown) => { if (value == null) { @@ -11,12 +11,12 @@ const parseNumericValue = (value: unknown) => { return Number(value); }; -export const getGridNumericQuickFilterFn = (value: any) => { +export const getGridNumericQuickFilterFn = (value: any): GridApplyQuickFilterV7 | null => { if (value == null || Number.isNaN(value) || value === '') { return null; } - return ({ value: columnValue }: GridCellParams): boolean => { + return (columnValue, _, __, ___): boolean => { return parseNumericValue(columnValue) === parseNumericValue(value); }; }; @@ -33,7 +33,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return parseNumericValue(value) === filterItem.value; }; }, @@ -47,7 +47,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return parseNumericValue(value) !== filterItem.value; }; }, @@ -61,7 +61,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { if (value == null) { return false; } @@ -79,7 +79,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { if (value == null) { return false; } @@ -97,7 +97,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { if (value == null) { return false; } @@ -115,7 +115,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { if (value == null) { return false; } @@ -129,7 +129,7 @@ export const getGridNumericOperators = (): GridFilterOperator< { value: 'isEmpty', getApplyFilterFnV7: () => { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value == null; }; }, @@ -138,7 +138,7 @@ export const getGridNumericOperators = (): GridFilterOperator< { value: 'isNotEmpty', getApplyFilterFnV7: () => { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null; }; }, @@ -151,7 +151,7 @@ export const getGridNumericOperators = (): GridFilterOperator< return null; } - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null && filterItem.value.includes(Number(value)); }; }, diff --git a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts index 3310a39735d06..a488a2c9c90eb 100644 --- a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts @@ -17,7 +17,7 @@ export const getGridSingleSelectOperators = (): GridFilterOperator[] => [ if (filterItem.value == null || filterItem.value === '') { return null; } - return (value, _, __): boolean => + return (value, _, __, ___): boolean => parseObjectValue(value) === parseObjectValue(filterItem.value); }, InputComponent: GridFilterInputSingleSelect, @@ -28,7 +28,7 @@ export const getGridSingleSelectOperators = (): GridFilterOperator[] => [ if (filterItem.value == null || filterItem.value === '') { return null; } - return (value, _, __): boolean => + return (value, _, __, ___): boolean => parseObjectValue(value) !== parseObjectValue(filterItem.value); }, InputComponent: GridFilterInputSingleSelect, @@ -40,7 +40,7 @@ export const getGridSingleSelectOperators = (): GridFilterOperator[] => [ return null; } const filterItemValues = filterItem.value.map(parseObjectValue); - return (value, _, __): boolean => filterItemValues.includes(parseObjectValue(value)); + return (value, _, __, ___): boolean => filterItemValues.includes(parseObjectValue(value)); }, InputComponent: GridFilterInputMultipleSingleSelect, }, diff --git a/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts b/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts index e9f764da95441..6a0eb87d21efe 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts @@ -24,5 +24,5 @@ export const GRID_STRING_COL_DEF: GridColTypeDef = { align: 'left', filterOperators: getGridStringOperators(), renderEditCell: renderEditInputCell, - getApplyQuickFilterFn: getGridStringQuickFilterFn, + getApplyQuickFilterFnV7: getGridStringQuickFilterFn, }; diff --git a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts index 4c1f76bbfa83b..e5a82f62b380c 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts @@ -1,16 +1,17 @@ import { GridFilterInputValue } from '../components/panel/filterPanel/GridFilterInputValue'; import { escapeRegExp } from '../utils/utils'; +import type { GridApplyQuickFilterV7 } from '../models/colDef/gridColDef'; import { GridFilterItem } from '../models/gridFilterItem'; import { GridFilterOperator } from '../models/gridFilterOperator'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; -import { GridCellParams } from '../models'; -export const getGridStringQuickFilterFn = (value: any) => { +export const getGridStringQuickFilterFn = (value: any): GridApplyQuickFilterV7 | null => { if (!value) { return null; } const filterRegex = new RegExp(escapeRegExp(value), 'i'); - return ({ formattedValue: columnValue }: GridCellParams): boolean => { + return (_, row, column, apiRef): boolean => { + const columnValue = apiRef.current.getRowFormattedValue(row, column); return columnValue != null ? filterRegex.test(columnValue.toString()) : false; }; }; @@ -27,7 +28,7 @@ export const getGridStringOperators = ( const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(escapeRegExp(filterItemValue), 'i'); - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null ? filterRegex.test(String(value)) : false; }; }, @@ -42,7 +43,7 @@ export const getGridStringOperators = ( const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null ? collator.compare(filterItemValue, value.toString()) === 0 : false; }; }, @@ -57,7 +58,7 @@ export const getGridStringOperators = ( const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(`^${escapeRegExp(filterItemValue)}.*$`, 'i'); - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null ? filterRegex.test(value.toString()) : false; }; }, @@ -72,7 +73,7 @@ export const getGridStringOperators = ( const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); const filterRegex = new RegExp(`.*${escapeRegExp(filterItemValue)}$`, 'i'); - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value != null ? filterRegex.test(value.toString()) : false; }; }, @@ -81,7 +82,7 @@ export const getGridStringOperators = ( { value: 'isEmpty', getApplyFilterFnV7: () => { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value === '' || value == null; }; }, @@ -90,7 +91,7 @@ export const getGridStringOperators = ( { value: 'isNotEmpty', getApplyFilterFnV7: () => { - return (value, _, __): boolean => { + return (value, _, __, ___): boolean => { return value !== '' && value != null; }; }, @@ -107,7 +108,7 @@ export const getGridStringOperators = ( : filterItem.value.map((val) => val.trim()); const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return (value, _, __): boolean => + return (value, _, __, ___): boolean => value != null ? filterItemValue.some((filterValue: GridFilterItem['value']) => { return collator.compare(filterValue, value.toString() || '') === 0; diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 172eb031c524b..6f2b1ec521bf4 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -284,55 +284,89 @@ const buildAggregatedQuickFilterApplier = ( const columnsFields = gridColumnFieldsSelector(apiRef); - const appliersPerField: { - [field: string]: (null | ((params: GridCellParams) => boolean))[]; - } = {}; + const appliersPerField = [] as { + field: string; + column: GridColDef, + appliers: { + v7: boolean; + fn: (null | ((...args: any[]) => boolean)); + }[]; + }[]; + columnsFields.forEach((field) => { const column = apiRef.current.getColumn(field); const getApplyQuickFilterFn = column?.getApplyQuickFilterFn; - if (!getApplyQuickFilterFn) { - return; + const getApplyQuickFilterFnV7 = column?.getApplyQuickFilterFnV7; + + if (getApplyQuickFilterFnV7) { + appliersPerField.push({ + field, + column, + appliers: quickFilterValues.map((value) => ({ + v7: true, + fn: getApplyQuickFilterFnV7(value, column, apiRef), + })), + }); + } + else if (getApplyQuickFilterFn) { + appliersPerField.push({ + field, + column, + appliers: quickFilterValues.map((value) => ({ + v7: false, + fn: getApplyQuickFilterFn(value, column, apiRef), + })), + }); } - appliersPerField[field] = quickFilterValues.map((value) => - getApplyQuickFilterFn(value, column, apiRef), - ); }); - // If some value does not have an applier we ignore them - const sanitizedQuickFilterValues = quickFilterValues.filter((value, index) => - Object.keys(appliersPerField).some((field) => appliersPerField[field][index] != null), - ); + return (row, shouldApplyFilter) => { - if (sanitizedQuickFilterValues.length === 0) { - return null; - } + const result = {} as GridQuickFilterValueResult; + const usedCellParams = {} as { [field: string]: GridCellParams }; - return (row, shouldApplyFilter) => { - const usedCellParams: { [field: string]: GridCellParams } = {}; - const fieldsToFilter: string[] = []; - - Object.keys(appliersPerField).forEach((field) => { - if (!shouldApplyFilter || shouldApplyFilter(field)) { - usedCellParams[field] = apiRef.current.getCellParams( - getRowId ? getRowId(row) : row.id, - field, - ); - fieldsToFilter.push(field); - } - }); + outer: for (let v = 0; v < quickFilterValues.length; v += 1) { + const filterValue = quickFilterValues[v]; + + for (let i = 0; i < appliersPerField.length; i += 1) { + const { field, column, appliers } = appliersPerField[i]; - const quickFilterValueResult: GridQuickFilterValueResult = {}; - sanitizedQuickFilterValues.forEach((value, index) => { - const isPassing = fieldsToFilter.some((field) => { - if (appliersPerField[field][index] == null) { - return false; + if (shouldApplyFilter && !shouldApplyFilter(field)) { + continue; } - return appliersPerField[field][index]?.(usedCellParams[field]); - }); - quickFilterValueResult[value] = isPassing; - }); - return quickFilterValueResult; + const applier = appliers[v]; + const value = apiRef.current.getRowValue(row, column); + + if (applier.fn === null) { + continue; + } + + if (applier.v7) { + const isMatching = applier.fn(value, row, column, apiRef); + if (isMatching) { + result[filterValue] = true + continue outer; + } + } else { + const cellParams = usedCellParams[field] ?? apiRef.current.getCellParams( + getRowId ? getRowId(row) : row.id, + field, + ); + usedCellParams[field] = cellParams; + + const isMatching = applier.fn(cellParams); + if (isMatching) { + result[filterValue] = true + continue outer; + } + } + } + + result[filterValue] = false + } + + return result; }; }; diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts index 0c8e0eab00724..dc5ed3e7faa20 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts @@ -174,6 +174,24 @@ export function useGridParamsApi( [apiRef, getBaseCellParams, getRowId], ); + const getRowFormattedValue = React.useCallback( + (row, colDef) => { + const value = getRowValue(row, colDef); + + if (!colDef || !colDef.valueFormatter) { + return value; + } + + return colDef.valueFormatter({ + id: getRowId ? getRowId(row) : row.id, + field: colDef.field, + value: value, + api: apiRef.current, + }); + }, + [apiRef, getBaseCellParams, getRowId, getRowValue], + ); + const getColumnHeaderElement = React.useCallback( (field) => { if (!apiRef.current.rootElementRef!.current) { @@ -205,6 +223,7 @@ export function useGridParamsApi( const paramsApi: GridParamsApi = { getRowValue, + getRowFormattedValue, getCellValue, getCellParams, getCellElement, diff --git a/packages/grid/x-data-grid/src/models/api/gridParamsApi.ts b/packages/grid/x-data-grid/src/models/api/gridParamsApi.ts index dc99a854ef3b3..bdaf6bfde0800 100644 --- a/packages/grid/x-data-grid/src/models/api/gridParamsApi.ts +++ b/packages/grid/x-data-grid/src/models/api/gridParamsApi.ts @@ -21,6 +21,14 @@ export interface GridParamsApi { * @returns {v} The cell value. */ getRowValue: (row: GridRowModel, colDef: GridColDef) => V; + /** + * Gets the formatted value of a row + * @template V + * @param {GridRowModel} row The row model. + * @param {GridColDef} field The column definition. + * @returns {v} The cell value. + */ + getRowFormattedValue: (row: GridRowModel, colDef: GridColDef) => V; /** * Gets the underlying DOM element for a cell at the given `id` and `field`. * @param {GridRowId} id The id of the row. diff --git a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts index aebe9767a0458..e99ecc0d9d2c1 100644 --- a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts +++ b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts @@ -32,6 +32,9 @@ export type ValueOptions = string | number | { value: any; label: string } | Rec */ export type GridKeyValue = string | number | boolean; +export type GridApplyQuickFilterV7 = + (value: V, row: R, column: GridColDef, apiRef: React.MutableRefObject) => boolean; + /** * Column Definition base interface. */ @@ -221,6 +224,19 @@ export interface GridBaseColDef, ) => null | ((params: GridCellParams) => boolean); + /** + * The callback that generates a filtering function for a given quick filter value. + * This function can return `null` to skip filtering for this value and column. + * @param {any} value The value with which we want to filter the column. + * @param {GridStateColDef} colDef The column from which we want to filter the rows. + * @param {React.MutableRefObject} apiRef Deprecated: The API of the grid. + * @returns {null | GridApplyQuickFilterV7} The function to call to check if a row pass this filter value or not. + */ + getApplyQuickFilterFnV7?: ( + value: any, + colDef: GridStateColDef, + apiRef: React.MutableRefObject, + ) => null | GridApplyQuickFilterV7; /** * If `true`, this column cannot be reordered. * @default false diff --git a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts index 0f6464cf21e1d..3efe8903d158c 100644 --- a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts +++ b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts @@ -3,11 +3,13 @@ import { GridFilterItem } from './gridFilterItem'; import { GridCellParams } from './params/gridCellParams'; import type { GridColDef } from './colDef/gridColDef'; import type { GridValidRowModel } from './gridRows'; +import type { GridApiCommunity } from './api/gridApiCommunity'; -export type ApplyFilterV7 = ( +export type GridApplyFilterV7 = ( value: V, row: R, column: GridColDef, + apiRef: React.MutableRefObject, ) => boolean; type ApplyFilterInterface = @@ -35,7 +37,7 @@ type ApplyFilterInterface = getApplyFilterFnV7: ( filterItem: GridFilterItem, column: GridColDef, - ) => null | ApplyFilterV7; + ) => null | GridApplyFilterV7; }; /** From b6655730aef778459161f2762cb5f809b0acffa7 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 11:19:17 -0400 Subject: [PATCH 18/24] perf: remove API Proxy wrapper --- .../hooks/core/useGridApiInitialization.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts b/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts index c5086ff638db9..9a85cf8c63e06 100644 --- a/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts +++ b/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts @@ -26,28 +26,21 @@ const wrapPublicApi = { Object.keys(methods).forEach((methodName) => { - if (visibility === 'public') { - publicApi[methodName as keyof PublicApi] = (methods as any)[methodName]; - } else { + if (visibility === 'private') { privateOnlyApi[methodName as keyof PrivateOnlyApi] = (methods as any)[methodName]; + Object.defineProperty(publicApi, methodName, { + enumerable: false, + value: (methods as any)[methodName], + }); + } else { + publicApi[methodName as keyof PublicApi] = (methods as any)[methodName]; } }); }; - const handler: ProxyHandler = { - get: (obj, prop) => { - if (prop in obj) { - return obj[prop as keyof typeof obj]; - } - return privateOnlyApi[prop as keyof PrivateOnlyApi]; - }, - set: (obj, prop, value) => { - obj[prop as keyof typeof obj] = value; - return true; - }, - }; + Object.assign(publicApi, privateOnlyApi); - return new Proxy(publicApi, handler) as PrivateApi; + return publicApi as unknown as PrivateApi; }; export function useGridApiInitialization< From 4b5164c927f04fce4fdc33ff54793c8a6af276f1 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 11:21:34 -0400 Subject: [PATCH 19/24] lint --- .../hooks/features/filter/gridFilterUtils.ts | 28 +++++++++---------- .../src/models/colDef/gridColDef.ts | 8 ++++-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 6f2b1ec521bf4..7f33aff4dde03 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -234,6 +234,9 @@ const buildAggregatedFilterItemsApplier = ( const applier = appliers[0]; const applierFn = applier.fn; + const applierCall = applier.v7 + ? 'applierFn(row)' + : 'applierFn(getRowId ? getRowId(row) : row.id)'; const fn = eval(` (row, shouldApplyFilter) => { // ${applierFn.name} <- Keep a ref, prevent the bundler from optimizing away @@ -241,9 +244,7 @@ const buildAggregatedFilterItemsApplier = ( if (shouldApplyFilter && !shouldApplyFilter(applier.item.field)) { return { '${applier.item.id!}': false }; } - return { '${applier.item.id!}': ${ - applier.v7 ? 'applierFn(row)' : 'applierFn(getRowId ? getRowId(row) : row.id)' - } }; + return { '${applier.item.id!}': ${applierCall} }; } `); @@ -286,10 +287,10 @@ const buildAggregatedQuickFilterApplier = ( const appliersPerField = [] as { field: string; - column: GridColDef, + column: GridColDef; appliers: { v7: boolean; - fn: (null | ((...args: any[]) => boolean)); + fn: null | ((...args: any[]) => boolean); }[]; }[]; @@ -307,8 +308,7 @@ const buildAggregatedQuickFilterApplier = ( fn: getApplyQuickFilterFnV7(value, column, apiRef), })), }); - } - else if (getApplyQuickFilterFn) { + } else if (getApplyQuickFilterFn) { appliersPerField.push({ field, column, @@ -321,7 +321,6 @@ const buildAggregatedQuickFilterApplier = ( }); return (row, shouldApplyFilter) => { - const result = {} as GridQuickFilterValueResult; const usedCellParams = {} as { [field: string]: GridCellParams }; @@ -345,25 +344,24 @@ const buildAggregatedQuickFilterApplier = ( if (applier.v7) { const isMatching = applier.fn(value, row, column, apiRef); if (isMatching) { - result[filterValue] = true + result[filterValue] = true; continue outer; } } else { - const cellParams = usedCellParams[field] ?? apiRef.current.getCellParams( - getRowId ? getRowId(row) : row.id, - field, - ); + const cellParams = + usedCellParams[field] ?? + apiRef.current.getCellParams(getRowId ? getRowId(row) : row.id, field); usedCellParams[field] = cellParams; const isMatching = applier.fn(cellParams); if (isMatching) { - result[filterValue] = true + result[filterValue] = true; continue outer; } } } - result[filterValue] = false + result[filterValue] = false; } return result; diff --git a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts index e99ecc0d9d2c1..c6ede251e05a4 100644 --- a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts +++ b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts @@ -32,8 +32,12 @@ export type ValueOptions = string | number | { value: any; label: string } | Rec */ export type GridKeyValue = string | number | boolean; -export type GridApplyQuickFilterV7 = - (value: V, row: R, column: GridColDef, apiRef: React.MutableRefObject) => boolean; +export type GridApplyQuickFilterV7 = ( + value: V, + row: R, + column: GridColDef, + apiRef: React.MutableRefObject, +) => boolean; /** * Column Definition base interface. From 072c659667b44687ee1bba0c471158635e0cf75a Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 11:33:42 -0400 Subject: [PATCH 20/24] fix: minor issues --- .../grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts | 1 + .../x-data-grid/src/hooks/features/filter/gridFilterUtils.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts b/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts index 9a85cf8c63e06..91f2c835e616a 100644 --- a/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts +++ b/packages/grid/x-data-grid/src/hooks/core/useGridApiInitialization.ts @@ -29,6 +29,7 @@ const wrapPublicApi = { const value = apiRef.current.getRowValue(row, column); - return applyFilterOnRow(value, row, column); + return applyFilterOnRow(value, row, column, apiRef); }, }; } From 8c6cdecf10d629f393f27513eb71378951be4b9b Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 11:35:19 -0400 Subject: [PATCH 21/24] lint --- .../x-data-grid/src/hooks/features/filter/gridFilterUtils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index ef08fa246b550..ea88bb743dbec 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -5,11 +5,9 @@ import { GridColDef, GridFilterItem, GridFilterModel, - GridFilterOperator, GridLogicOperator, GridRowId, GridRowIdGetter, - GridTreeNode, GridValidRowModel, } from '../../../models'; import { GridApiCommunity } from '../../../models/api/gridApiCommunity'; From 8183aaac55e12c1521231b7ffcce4910eab293f5 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 11:37:19 -0400 Subject: [PATCH 22/24] Revert "chore: synchronous demo data" This reverts commit 65694fb0c15c25605983fd52599059f220702d1d. --- .../src/hooks/useDemoData.ts | 32 +-------------- .../src/services/real-data-service.ts | 41 ------------------- 2 files changed, 1 insertion(+), 72 deletions(-) diff --git a/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts b/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts index de4171e8c1254..677ff89be441b 100644 --- a/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts +++ b/packages/grid/x-data-grid-generator/src/hooks/useDemoData.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; import { GridColumnVisibilityModel } from '@mui/x-data-grid-premium'; -import { GridDemoData, getRealGridData, getRealGridDataSync } from '../services/real-data-service'; +import { GridDemoData, getRealGridData } from '../services/real-data-service'; import { getCommodityColumns } from '../columns/commodities.columns'; import { getEmployeeColumns } from '../columns/employees.columns'; import asyncWorker from '../services/asyncWorker'; @@ -215,33 +215,3 @@ export const useDemoData = (options: UseDemoDataOptions): DemoDataReturnType => }, }; }; - -export const useDemoDataSync = (options: UseDemoDataOptions): DemoDataReturnType => { - const [rowLength, setRowLength] = React.useState(options.rowLength); - const [, setIndex] = React.useState(0); - const [loading] = React.useState(true); - - const columns = React.useMemo(() => { - return getColumnsFromOptions({ - dataSet: options.dataSet, - editable: options.editable, - maxColumns: options.maxColumns, - visibleFields: options.visibleFields, - }); - }, [options.dataSet, options.editable, options.maxColumns, options.visibleFields]); - - const data = addTreeDataOptionsToDemoData(getRealGridDataSync(rowLength, columns), { - maxDepth: options.treeData?.maxDepth, - groupingField: options.treeData?.groupingField, - averageChildren: options.treeData?.averageChildren, - }); - - return { - data, - loading, - setRowLength, - loadNewData: () => { - setIndex((oldIndex) => oldIndex + 1); - }, - }; -}; diff --git a/packages/grid/x-data-grid-generator/src/services/real-data-service.ts b/packages/grid/x-data-grid-generator/src/services/real-data-service.ts index b290fff088cce..734126b13f339 100644 --- a/packages/grid/x-data-grid-generator/src/services/real-data-service.ts +++ b/packages/grid/x-data-grid-generator/src/services/real-data-service.ts @@ -60,44 +60,3 @@ export function getRealGridData( }); }); } - -export function getRealGridDataSync( - rowLength: number, - columns: GridColDefGenerator[], -): GridDemoData { - const rows: GridDemoData['rows'] = []; - const indexedValues: { [field: string]: { [value: string]: number } } = {}; - - for (let i = 0; i < rowLength; i += 1) { - const row: any = {}; - - for (let j = 0; j < columns.length; j += 1) { - const column = columns[j]; - if (column.generateData) { - const context: GridDataGeneratorContext = {}; - if (column.dataGeneratorUniquenessEnabled) { - let fieldValues = indexedValues[column.field]; - if (!fieldValues) { - fieldValues = {}; - indexedValues[column.field] = fieldValues; - } - - context.values = fieldValues; - } - - row[column.field] = column.generateData(row, context); - } - } - - rows.push(row); - } - - const columnVisibilityModel: GridColumnVisibilityModel = {}; - columns.forEach((col) => { - if (col.hide) { - columnVisibilityModel[col.field] = false; - } - }); - - return { columns, rows, initialState: { columns: { columnVisibilityModel } } }; -} From aa90c79efebae14935dbd4f137ea9c1c61ba1ac9 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Mon, 29 May 2023 17:45:59 -0400 Subject: [PATCH 23/24] lint --- .../src/hooks/features/filter/gridFilterUtils.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index ea88bb743dbec..32c5979805e3a 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -19,7 +19,7 @@ import { GridQuickFilterValueResult, } from './gridFilterState'; import { buildWarning } from '../../../utils/warning'; -import { gridColumnFieldsSelector, gridColumnLookupSelector } from '../columns'; +import { gridFilterableColumnLookupSelector, gridColumnLookupSelector } from '../columns'; type GridFilterItemApplier = | { @@ -281,10 +281,9 @@ const buildAggregatedQuickFilterApplier = ( return null; } - const columnsFields = gridColumnFieldsSelector(apiRef); + const columnsByField = gridFilterableColumnLookupSelector(apiRef); const appliersPerField = [] as { - field: string; column: GridColDef; appliers: { v7: boolean; @@ -292,14 +291,12 @@ const buildAggregatedQuickFilterApplier = ( }[]; }[]; - columnsFields.forEach((field) => { - const column = apiRef.current.getColumn(field); + Object.values(columnsByField).forEach((column) => { const getApplyQuickFilterFn = column?.getApplyQuickFilterFn; const getApplyQuickFilterFnV7 = column?.getApplyQuickFilterFnV7; if (getApplyQuickFilterFnV7) { appliersPerField.push({ - field, column, appliers: quickFilterValues.map((value) => ({ v7: true, @@ -308,7 +305,6 @@ const buildAggregatedQuickFilterApplier = ( }); } else if (getApplyQuickFilterFn) { appliersPerField.push({ - field, column, appliers: quickFilterValues.map((value) => ({ v7: false, @@ -318,7 +314,7 @@ const buildAggregatedQuickFilterApplier = ( } }); - return (row, shouldApplyFilter) => { + return function isRowMatchingQuickFilter(row, shouldApplyFilter) { const result = {} as GridQuickFilterValueResult; const usedCellParams = {} as { [field: string]: GridCellParams }; @@ -326,7 +322,8 @@ const buildAggregatedQuickFilterApplier = ( const filterValue = quickFilterValues[v]; for (let i = 0; i < appliersPerField.length; i += 1) { - const { field, column, appliers } = appliersPerField[i]; + const { column, appliers } = appliersPerField[i]; + const { field } = column; if (shouldApplyFilter && !shouldApplyFilter(field)) { continue; From 189acf0b8dd66b968dcdc4554edf9cd2899c1a04 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sat, 3 Jun 2023 10:07:52 -0400 Subject: [PATCH 24/24] lint --- .../components/headerFiltering/GridHeaderFilterAdornment.tsx | 2 +- .../src/components/headerFiltering/GridHeaderFilterCell.tsx | 2 +- packages/grid/x-data-grid/src/models/gridFilterOperator.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterAdornment.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterAdornment.tsx index 5851d274080b7..56371f4d1331e 100644 --- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterAdornment.tsx +++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterAdornment.tsx @@ -103,7 +103,7 @@ GridHeaderFilterAdornment.propTypes = { }).isRequired, operators: PropTypes.arrayOf( PropTypes.shape({ - getApplyFilterFn: PropTypes.func.isRequired, + getApplyFilterFn: PropTypes.func, getValueAsString: PropTypes.func, headerLabel: PropTypes.string, InputComponent: PropTypes.elementType, diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index cdb410304fb41..50b67b07896aa 100644 --- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -285,7 +285,7 @@ GridHeaderFilterCell.propTypes = { colIndex: PropTypes.number.isRequired, filterOperators: PropTypes.arrayOf( PropTypes.shape({ - getApplyFilterFn: PropTypes.func.isRequired, + getApplyFilterFn: PropTypes.func, getValueAsString: PropTypes.func, headerLabel: PropTypes.string, InputComponent: PropTypes.elementType, diff --git a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts index 3efe8903d158c..59bed3ea6afa5 100644 --- a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts +++ b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts @@ -21,7 +21,7 @@ type ApplyFilterInterface = * @param {GridColDef} column The column from which we want to filter the rows. * @returns {null | ((params: GridCellParams) => boolean)} The function to call to check if a row pass this filter item or not. */ - getApplyFilterFn: ( + getApplyFilterFn?: ( filterItem: GridFilterItem, column: GridColDef, ) => null | ((params: GridCellParams) => boolean); @@ -34,7 +34,7 @@ type ApplyFilterInterface = * @param {GridColDef} column The column from which we want to filter the rows. * @returns {null | ((value: V, row: R, column: GridColDef) => boolean)} The function to call to check if a row pass this filter item or not. */ - getApplyFilterFnV7: ( + getApplyFilterFnV7?: ( filterItem: GridFilterItem, column: GridColDef, ) => null | GridApplyFilterV7;