From f705a2d4276deeeeb9f0d33a874c378a1d4cf2f6 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Mon, 26 Feb 2024 01:42:50 -0600 Subject: [PATCH] rework filter tooltip label logic --- README.md | 51 +++++---- packages/material-react-table/.eslintrc | 24 ++-- .../src/components/head/MRT_TableHeadCell.tsx | 18 ++- .../head/MRT_TableHeadCellFilterContainer.tsx | 21 +++- .../head/MRT_TableHeadCellFilterLabel.tsx | 106 +++++++++--------- .../inputs/MRT_FilterRangeFields.tsx | 14 ++- .../components/inputs/MRT_FilterTextField.tsx | 65 +++++------ .../getMRT_RowActionsColumnDef.tsx | 2 +- .../getMRT_RowDragColumnDef.tsx | 2 +- .../getMRT_RowExpandColumnDef.tsx | 2 +- .../getMRT_RowNumbersColumnDef.tsx | 2 +- .../getMRT_RowPinningColumnDef.tsx | 2 +- .../getMRT_RowSelectColumnDef.tsx | 2 +- .../getMRT_RowSpacerColumnDef.tsx | 2 +- .../src/utils/column.utils.ts | 72 ++++++++++++ .../material-react-table/src/utils/utils.ts | 2 +- .../features/Virtualization.stories.tsx | 1 - 17 files changed, 246 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index d7bec35ec..9ed179712 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ View [Documentation](https://www.material-react-table.com/) - - - + + + ## About ### _Quickly Create React Data Tables with Material Design_ -### __Built with [Material UI V5](https://mui.com) and [TanStack Table V8](https://tanstack.com/table/v8)__ +### **Built with [Material UI V5](https://mui.com) and [TanStack Table V8](https://tanstack.com/table/v8)** -MRT +MRT > Want to use Mantine instead of Material UI? Check out [Mantine React Table](https://www.mantine-react-table.com) @@ -46,19 +50,19 @@ View [Documentation](https://www.material-react-table.com/) ### Quick Examples - - [Basic Table](https://www.material-react-table.com/docs/examples/basic/) (See Default Features) - - [Minimal Table](https://www.material-react-table.com/docs/examples/minimal/) (Turn off Features like Pagination, Sorting, Filtering, and Toolbars) - - [Advanced Table](https://www.material-react-table.com/docs/examples/advanced/) (See some of the Advanced Features) - - [Custom Headless Table](https://www.material-react-table.com/docs/examples/custom-headless/) (Build your own table markup) - - [Dragging / Ordering Examples](https://www.material-react-table.com/docs/examples/column-ordering/) (Drag and Drop) - - [Editing (CRUD) Examples](https://www.material-react-table.com/docs/examples/editing-crud/) (Create, Edit, and Delete Rows) - - [Expanding / Grouping Examples](https://www.material-react-table.com/docs/examples/aggregation-and-grouping/) (Sum, Average, Count, etc.) - - [Filtering Examples](https://www.material-react-table.com/docs/examples/filter-variants/) (Faceted Values, Switching Filters, etc.) - - [Sticky Pinning Examples](https://www.material-react-table.com/docs/examples/sticky-header/) (Sticky Headers, Sticky Columns, Sticky Rows, etc.) - - [Remote Data Fetching Examples](https://www.material-react-table.com/docs/examples/react-query/) (Server-side Pagination, Sorting, and Filtering) - - [Virtualized Examples](https://www.material-react-table.com/docs/examples/virtualized/) (10,000 rows at once!) - - [Infinite Scrolling](https://www.material-react-table.com/docs/examples/infinite-scrolling/) (Fetch data as you scroll) - - [Localization (i18n)](https://www.material-react-table.com/docs/guides/localization#built-in-locale-examples) (Over a dozen languages built-in) +- [Basic Table](https://www.material-react-table.com/docs/examples/basic/) (See Default Features) +- [Minimal Table](https://www.material-react-table.com/docs/examples/minimal/) (Turn off Features like Pagination, Sorting, Filtering, and Toolbars) +- [Advanced Table](https://www.material-react-table.com/docs/examples/advanced/) (See some of the Advanced Features) +- [Custom Headless Table](https://www.material-react-table.com/docs/examples/custom-headless/) (Build your own table markup) +- [Dragging / Ordering Examples](https://www.material-react-table.com/docs/examples/column-ordering/) (Drag and Drop) +- [Editing (CRUD) Examples](https://www.material-react-table.com/docs/examples/editing-crud/) (Create, Edit, and Delete Rows) +- [Expanding / Grouping Examples](https://www.material-react-table.com/docs/examples/aggregation-and-grouping/) (Sum, Average, Count, etc.) +- [Filtering Examples](https://www.material-react-table.com/docs/examples/filter-variants/) (Faceted Values, Switching Filters, etc.) +- [Sticky Pinning Examples](https://www.material-react-table.com/docs/examples/sticky-header/) (Sticky Headers, Sticky Columns, Sticky Rows, etc.) +- [Remote Data Fetching Examples](https://www.material-react-table.com/docs/examples/react-query/) (Server-side Pagination, Sorting, and Filtering) +- [Virtualized Examples](https://www.material-react-table.com/docs/examples/virtualized/) (10,000 rows at once!) +- [Infinite Scrolling](https://www.material-react-table.com/docs/examples/infinite-scrolling/) (Fetch data as you scroll) +- [Localization (i18n)](https://www.material-react-table.com/docs/guides/localization#built-in-locale-examples) (Over a dozen languages built-in) View additional [storybook examples](https://www.material-react-table.dev/) @@ -68,7 +72,7 @@ _All features can easily be enabled/disabled_ _**Fully Fleshed out [Docs](https://www.material-react-table.com/docs/guides#guides) are available for all features**_ -- [x] 30-54kb gzipped - [Bundlephobia](https://bundlephobia.com/package/material-react-table) +- [x] 30-56kb gzipped - [Bundlephobia](https://bundlephobia.com/package/material-react-table) - [x] Advanced TypeScript Generics Support (TypeScript Optional) - [x] Aggregation and Grouping (Sum, Average, Count, etc.) - [x] Cell Actions (Right-click Context Menu) @@ -132,7 +136,10 @@ npm install material-react-table ```jsx import { useMemo, useState, useEffect } from 'react'; -import { MaterialReactTable, useMaterialReactTable } from 'material-react-table'; +import { + MaterialReactTable, + useMaterialReactTable, +} from 'material-react-table'; //data must be stable reference (useState, useMemo, useQuery, defined outside of component, etc.) const data = [ @@ -144,7 +151,7 @@ const data = [ name: 'Sara', age: 25, }, -] +]; export default function App() { const columns = useMemo( @@ -185,7 +192,7 @@ export default function App() { const someEventHandler = () => { //read the table state during an event from the table instance console.log(table.getState().sorting); - } + }; return ( //other more lightweight MRT sub components also available diff --git a/packages/material-react-table/.eslintrc b/packages/material-react-table/.eslintrc index 4484041f4..9dc129dbb 100644 --- a/packages/material-react-table/.eslintrc +++ b/packages/material-react-table/.eslintrc @@ -2,7 +2,7 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:perfectionist/recommended-natural" + "plugin:perfectionist/recommended-natural", ], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint", "mui-path-imports", "perfectionist"], @@ -16,12 +16,12 @@ { "prefer": "type-imports", "disallowTypeAnnotations": true, - "fixStyle": "inline-type-imports" - } + "fixStyle": "inline-type-imports", + }, ], "mui-path-imports/mui-path-imports": "warn", "perfectionist/sort-imports": [ - "error", + "warn", { "type": "natural", "order": "asc", @@ -34,7 +34,7 @@ "sibling-type", "parent", "parent-type", - "style" + "style", ], "custom-groups": { "value": { @@ -43,16 +43,16 @@ "tanstack": "@tanstack/**", "mui": "@mui/**", "mrt": ["./MRT_**", "../**MRT_**", "../../src"], - "faker": "@faker/**" + "faker": "@faker/**", }, "type": { - "react": "react" - } + "react": "react", + }, }, - "newlines-between": "never" - } - ] + "newlines-between": "never", + }, + ], }, "root": true, - "ignorePatterns": ["dist/", "locales/", "node_modules/"] + "ignorePatterns": ["dist/", "locales/", "node_modules/"], } diff --git a/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx b/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx index 8e48e123d..0f83a756f 100644 --- a/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx +++ b/packages/material-react-table/src/components/head/MRT_TableHeadCell.tsx @@ -15,6 +15,7 @@ import { type MRT_RowData, type MRT_TableInstance, } from '../../types'; +import { useColumnFilterInfo } from '../../utils/column.utils'; import { getCommonMRTCellStyles } from '../../utils/style.utils'; import { parseFromValuesOrFunc } from '../../utils/utils'; @@ -74,6 +75,11 @@ export const MRT_TableHeadCell = ({ ...rest, }; + const columnFilterInfo = useColumnFilterInfo({ + header, + table, + }); + const isColumnPinned = enableColumnPinning && columnDef.columnDefType !== 'group' && @@ -276,7 +282,11 @@ export const MRT_TableHeadCell = ({ {HeaderElement} {column.getCanFilter() && ( - + )} {column.getCanSort() && ( @@ -312,7 +322,11 @@ export const MRT_TableHeadCell = ({ )} {columnFilterDisplayMode === 'subheader' && column.getCanFilter() && ( - + )} ); diff --git a/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterContainer.tsx b/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterContainer.tsx index 1070eed8b..b8d3c92af 100644 --- a/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterContainer.tsx +++ b/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterContainer.tsx @@ -4,6 +4,7 @@ import { type MRT_RowData, type MRT_TableInstance, } from '../../types'; +import { type ColumnFilterInfo } from '../../utils/column.utils'; import { MRT_FilterCheckbox } from '../inputs/MRT_FilterCheckbox'; import { MRT_FilterRangeFields } from '../inputs/MRT_FilterRangeFields'; import { MRT_FilterRangeSlider } from '../inputs/MRT_FilterRangeSlider'; @@ -12,11 +13,13 @@ import { MRT_FilterTextField } from '../inputs/MRT_FilterTextField'; export interface MRT_TableHeadCellFilterContainerProps< TData extends MRT_RowData, > extends CollapseProps { + columnFilterInfo: ColumnFilterInfo; header: MRT_Header; table: MRT_TableInstance; } export const MRT_TableHeadCellFilterContainer = ({ + columnFilterInfo, header, table, ...rest @@ -28,6 +31,7 @@ export const MRT_TableHeadCellFilterContainer = ({ const { showColumnFilters } = getState(); const { column } = header; const { columnDef } = column; + const { isRangeFilter } = columnFilterInfo; return ( ({ ) : columnDef.filterVariant === 'range-slider' ? ( - ) : columnDef.filterVariant?.includes('range') || - ['between', 'betweenInclusive', 'inNumberRange'].includes( - columnDef._filterFn, - ) ? ( - + ) : isRangeFilter ? ( + ) : ( - + )} ); diff --git a/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterLabel.tsx b/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterLabel.tsx index 35535773e..90a8b1bce 100644 --- a/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterLabel.tsx +++ b/packages/material-react-table/src/components/head/MRT_TableHeadCellFilterLabel.tsx @@ -1,4 +1,4 @@ -import { type MouseEvent, useState, useMemo } from 'react'; +import { type MouseEvent, useState } from 'react'; import Box from '@mui/material/Box'; import Grow from '@mui/material/Grow'; import IconButton, { type IconButtonProps } from '@mui/material/IconButton'; @@ -10,15 +10,18 @@ import { type MRT_RowData, type MRT_TableInstance, } from '../../types'; +import { type ColumnFilterInfo } from '../../utils/column.utils'; import { getValueAndLabel, parseFromValuesOrFunc } from '../../utils/utils'; export interface MRT_TableHeadCellFilterLabelProps extends IconButtonProps { + columnFilterInfo: ColumnFilterInfo; header: MRT_Header; table: MRT_TableInstance; } export const MRT_TableHeadCellFilterLabel = ({ + columnFilterInfo, header, table, ...rest @@ -35,34 +38,31 @@ export const MRT_TableHeadCellFilterLabel = ({ const { column } = header; const { columnDef } = column; - const filterValue = column.getFilterValue(); + const filterValue = column.getFilterValue() as [string, string] | string; const [anchorEl, setAnchorEl] = useState(null); - const filterSelectOptionsLookup = useMemo( - () => - (columnDef.filterSelectOptions?.map(getValueAndLabel) ?? []).reduce( - (acc, { value, label }) => ({ ...acc, [value]: label as string }), - {} as Record, - ), - [columnDef.filterSelectOptions], - ); + const { + currentFilterOption, + dropdownOptions, + isMultiSelectFilter, + isRangeFilter, + isSelectFilter, + } = columnFilterInfo; - const isFilterSelectOptions = - (columnDef.filterVariant === 'select' || - columnDef.filterVariant === 'multi-select') && - Boolean(Object.keys(filterSelectOptionsLookup).length); + const getSelectLabel = (index?: number) => + getValueAndLabel( + dropdownOptions?.find( + (option) => + getValueAndLabel(option).value === + (index !== undefined ? filterValue[index] : filterValue), + ), + ).label; const isFilterActive = (Array.isArray(filterValue) && filterValue.some(Boolean)) || (!!filterValue && !Array.isArray(filterValue)); - const isRangeFilter = - columnDef.filterVariant?.includes('range') || - ['between', 'betweenInclusive', 'inNumberRange'].includes( - columnDef._filterFn, - ); - const currentFilterOption = columnDef._filterFn; const filterTooltip = columnFilterDisplayMode === 'popover' && !isFilterActive ? localization.filterByColumn?.replace( @@ -88,16 +88,14 @@ export const MRT_TableHeadCellFilterLabel = ({ `"${ Array.isArray(filterValue) ? (filterValue as [string, string]) - .map((value) => - isFilterSelectOptions - ? filterSelectOptionsLookup[value] - : value, + .map((value, index) => + isMultiSelectFilter ? getSelectLabel(index) : value, ) .join( `" ${isRangeFilter ? localization.and : localization.or} "`, ) - : isFilterSelectOptions - ? filterSelectOptionsLookup[filterValue as string] + : isSelectFilter + ? getSelectLabel() : (filterValue as string) }"`, ) @@ -148,31 +146,37 @@ export const MRT_TableHeadCellFilterLabel = ({ - event.stopPropagation()} - onClose={(event) => { - //@ts-ignore - event.stopPropagation(); - setAnchorEl(null); - }} - onKeyDown={(event) => event.key === 'Enter' && setAnchorEl(null)} - open={!!anchorEl} - slotProps={{ paper: { sx: { overflow: 'visible' } } }} - transformOrigin={{ - horizontal: 'center', - vertical: 'bottom', - }} - > - - - - + {columnFilterDisplayMode === 'popover' && ( + event.stopPropagation()} + onClose={(event) => { + //@ts-ignore + event.stopPropagation(); + setAnchorEl(null); + }} + onKeyDown={(event) => event.key === 'Enter' && setAnchorEl(null)} + open={!!anchorEl} + slotProps={{ paper: { sx: { overflow: 'visible' } } }} + transformOrigin={{ + horizontal: 'center', + vertical: 'bottom', + }} + > + + + + + )} ); }; diff --git a/packages/material-react-table/src/components/inputs/MRT_FilterRangeFields.tsx b/packages/material-react-table/src/components/inputs/MRT_FilterRangeFields.tsx index e8f66080a..5279c2cc4 100644 --- a/packages/material-react-table/src/components/inputs/MRT_FilterRangeFields.tsx +++ b/packages/material-react-table/src/components/inputs/MRT_FilterRangeFields.tsx @@ -5,15 +5,18 @@ import { type MRT_RowData, type MRT_TableInstance, } from '../../types'; +import { type ColumnFilterInfo } from '../../utils/column.utils'; import { parseFromValuesOrFunc } from '../../utils/utils'; export interface MRT_FilterRangeFieldsProps extends BoxProps { + columnFilterInfo: ColumnFilterInfo; header: MRT_Header; table: MRT_TableInstance; } export const MRT_FilterRangeFields = ({ + columnFilterInfo, header, table, ...rest @@ -28,8 +31,15 @@ export const MRT_FilterRangeFields = ({ ...(parseFromValuesOrFunc(rest?.sx, theme) as any), })} > - - + {[0, 1].map((rangeFilterIndex) => ( + + ))} ); }; diff --git a/packages/material-react-table/src/components/inputs/MRT_FilterTextField.tsx b/packages/material-react-table/src/components/inputs/MRT_FilterTextField.tsx index 4cb644d19..9f2c857fb 100644 --- a/packages/material-react-table/src/components/inputs/MRT_FilterTextField.tsx +++ b/packages/material-react-table/src/components/inputs/MRT_FilterTextField.tsx @@ -3,7 +3,6 @@ import { type MouseEvent, useCallback, useEffect, - useMemo, useRef, useState, } from 'react'; @@ -35,17 +34,20 @@ import { type MRT_RowData, type MRT_TableInstance, } from '../../types'; +import { type ColumnFilterInfo } from '../../utils/column.utils'; import { getValueAndLabel, parseFromValuesOrFunc } from '../../utils/utils'; import { MRT_FilterOptionMenu } from '../menus/MRT_FilterOptionMenu'; export interface MRT_FilterTextFieldProps extends TextFieldProps<'standard'> { + columnFilterInfo: ColumnFilterInfo; header: MRT_Header; rangeFilterIndex?: number; table: MRT_TableInstance; } export const MRT_FilterTextField = ({ + columnFilterInfo, header, rangeFilterIndex, table, @@ -53,7 +55,6 @@ export const MRT_FilterTextField = ({ }: MRT_FilterTextFieldProps) => { const { options: { - columnFilterModeOptions, enableColumnFilterModes, icons: { CloseIcon, FilterListIcon }, localization, @@ -112,17 +113,19 @@ export const MRT_FilterTextField = ({ }), }; - const isDateFilter = - filterVariant?.startsWith('date') || filterVariant?.startsWith('time'); - const isAutocompleteFilter = filterVariant === 'autocomplete'; - const isRangeFilter = - filterVariant?.includes('range') || rangeFilterIndex !== undefined; - const isSelectFilter = filterVariant === 'select'; - const isMultiSelectFilter = filterVariant === 'multi-select'; - const isTextboxFilter = - ['autocomplete', 'text'].includes(filterVariant!) || - (!isSelectFilter && !isMultiSelectFilter); - const currentFilterOption = columnDef._filterFn; + const { + allowedColumnFilterOptions, + currentFilterOption, + dropdownOptions, + facetedUniqueValues, + isAutocompleteFilter, + isDateFilter, + isMultiSelectFilter, + isRangeFilter, + isSelectFilter, + isTextboxFilter, + } = columnFilterInfo; + const filterChipLabel = ['empty', 'notEmpty'].includes(currentFilterOption) ? //@ts-ignore localization[ @@ -132,6 +135,7 @@ export const MRT_FilterTextField = ({ }` ] : ''; + const filterPlaceholder = !isRangeFilter ? textFieldProps?.placeholder ?? localization.filterByColumn?.replace('{column}', String(columnDef.header)) @@ -140,16 +144,14 @@ export const MRT_FilterTextField = ({ : rangeFilterIndex === 1 ? localization.max : ''; - const allowedColumnFilterOptions = - columnDef?.columnFilterModeOptions ?? columnFilterModeOptions; - const showChangeModeButton = + + const showChangeModeButton = !!( enableColumnFilterModes && columnDef.enableColumnFilterModes !== false && !rangeFilterIndex && (allowedColumnFilterOptions === undefined || - !!allowedColumnFilterOptions?.length); - - const facetedUniqueValues = column.getFacetedUniqueValues(); + !!allowedColumnFilterOptions?.length) + ); const [anchorEl, setAnchorEl] = useState(null); const [filterValue, setFilterValue] = useState(() => @@ -257,23 +259,6 @@ export const MRT_FilterTextField = ({ ); } - const dropdownOptions = useMemo( - () => - columnDef.filterSelectOptions ?? - ((isSelectFilter || isMultiSelectFilter || isAutocompleteFilter) && - facetedUniqueValues - ? Array.from(facetedUniqueValues.keys()) - .filter((value) => value !== null && value !== undefined) - .sort((a, b) => a.localeCompare(b)) - : undefined), - [ - columnDef.filterSelectOptions, - facetedUniqueValues, - isMultiSelectFilter, - isSelectFilter, - ], - ); - const endAdornment = !isAutocompleteFilter && !isDateFilter && !filterChipLabel ? ( ({ ) : isAutocompleteFilter ? ( getValueAndLabel(option).label} - onChange={(_e, newValue) => handleAutocompleteChange(newValue)} + getOptionLabel={(option: DropdownOption) => + getValueAndLabel(option).label + } + onChange={(_e, newValue: DropdownOption) => + handleAutocompleteChange(newValue) + } options={ dropdownOptions?.map((option) => getValueAndLabel(option)) ?? [] } diff --git a/packages/material-react-table/src/hooks/display-columns/getMRT_RowActionsColumnDef.tsx b/packages/material-react-table/src/hooks/display-columns/getMRT_RowActionsColumnDef.tsx index fb03a5f43..48371d49a 100644 --- a/packages/material-react-table/src/hooks/display-columns/getMRT_RowActionsColumnDef.tsx +++ b/packages/material-react-table/src/hooks/display-columns/getMRT_RowActionsColumnDef.tsx @@ -8,7 +8,7 @@ import { defaultDisplayColumnProps } from '../../utils/displayColumn.utils'; export const getMRT_RowActionsColumnDef = ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { return { Cell: ({ cell, row, staticRowIndex, table }) => ( ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { return { Cell: ({ row, rowRef, table }) => ( ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { const { defaultColumn, enableExpandAll, diff --git a/packages/material-react-table/src/hooks/display-columns/getMRT_RowNumbersColumnDef.tsx b/packages/material-react-table/src/hooks/display-columns/getMRT_RowNumbersColumnDef.tsx index a1439bb80..bbc3c85dd 100644 --- a/packages/material-react-table/src/hooks/display-columns/getMRT_RowNumbersColumnDef.tsx +++ b/packages/material-react-table/src/hooks/display-columns/getMRT_RowNumbersColumnDef.tsx @@ -7,7 +7,7 @@ import { defaultDisplayColumnProps } from '../../utils/displayColumn.utils'; export const getMRT_RowNumbersColumnDef = ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { const { localization, rowNumberDisplayMode } = tableOptions; const { pagination: { pageIndex, pageSize }, diff --git a/packages/material-react-table/src/hooks/display-columns/getMRT_RowPinningColumnDef.tsx b/packages/material-react-table/src/hooks/display-columns/getMRT_RowPinningColumnDef.tsx index ac4f52742..eddf0f01e 100644 --- a/packages/material-react-table/src/hooks/display-columns/getMRT_RowPinningColumnDef.tsx +++ b/packages/material-react-table/src/hooks/display-columns/getMRT_RowPinningColumnDef.tsx @@ -8,7 +8,7 @@ import { defaultDisplayColumnProps } from '../../utils/displayColumn.utils'; export const getMRT_RowPinningColumnDef = ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { return { Cell: ({ row, table }) => ( diff --git a/packages/material-react-table/src/hooks/display-columns/getMRT_RowSelectColumnDef.tsx b/packages/material-react-table/src/hooks/display-columns/getMRT_RowSelectColumnDef.tsx index 14500c56c..b8660f773 100644 --- a/packages/material-react-table/src/hooks/display-columns/getMRT_RowSelectColumnDef.tsx +++ b/packages/material-react-table/src/hooks/display-columns/getMRT_RowSelectColumnDef.tsx @@ -8,7 +8,7 @@ import { defaultDisplayColumnProps } from '../../utils/displayColumn.utils'; export const getMRT_RowSelectColumnDef = ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { const { enableMultiRowSelection, enableSelectAll } = tableOptions; return { diff --git a/packages/material-react-table/src/hooks/display-columns/getMRT_RowSpacerColumnDef.tsx b/packages/material-react-table/src/hooks/display-columns/getMRT_RowSpacerColumnDef.tsx index e265a8206..b8bca0617 100644 --- a/packages/material-react-table/src/hooks/display-columns/getMRT_RowSpacerColumnDef.tsx +++ b/packages/material-react-table/src/hooks/display-columns/getMRT_RowSpacerColumnDef.tsx @@ -17,7 +17,7 @@ const blankColProps = { export const getMRT_RowSpacerColumnDef = ( tableOptions: MRT_StatefulTableOptions, -): MRT_ColumnDef | null => { +): MRT_ColumnDef => { return { ...defaultDisplayColumnProps({ id: 'mrt-row-spacer', diff --git a/packages/material-react-table/src/utils/column.utils.ts b/packages/material-react-table/src/utils/column.utils.ts index d8e9fdf49..42a0ba915 100644 --- a/packages/material-react-table/src/utils/column.utils.ts +++ b/packages/material-react-table/src/utils/column.utils.ts @@ -1,12 +1,16 @@ +import { useMemo } from 'react'; import { type Row } from '@tanstack/react-table'; import { + type DropdownOption, type MRT_Column, type MRT_ColumnDef, type MRT_ColumnOrderState, type MRT_DefinedColumnDef, type MRT_DefinedTableOptions, type MRT_FilterOption, + type MRT_Header, type MRT_RowData, + type MRT_TableInstance, } from '../types'; export const getColumnId = ( @@ -121,3 +125,71 @@ export const getDefaultColumnFilterFn = ( return 'equals'; return 'fuzzy'; }; + +export type ColumnFilterInfo = ReturnType; + +export const useColumnFilterInfo = ({ + header, + table, +}: { + header: MRT_Header; + table: MRT_TableInstance; +}) => { + const { + options: { columnFilterModeOptions }, + } = table; + const { column } = header; + const { columnDef } = column; + const { filterVariant } = columnDef; + + const isDateFilter = !!( + filterVariant?.startsWith('date') || filterVariant?.startsWith('time') + ); + const isAutocompleteFilter = filterVariant === 'autocomplete'; + const isRangeFilter = + filterVariant?.includes('range') || + ['between', 'betweenInclusive', 'inNumberRange'].includes( + columnDef._filterFn, + ); + const isSelectFilter = filterVariant === 'select'; + const isMultiSelectFilter = filterVariant === 'multi-select'; + const isTextboxFilter = + ['autocomplete', 'text'].includes(filterVariant!) || + (!isSelectFilter && !isMultiSelectFilter); + const currentFilterOption = columnDef._filterFn; + + const allowedColumnFilterOptions = + columnDef?.columnFilterModeOptions ?? columnFilterModeOptions; + + const facetedUniqueValues = column.getFacetedUniqueValues(); + + const dropdownOptions = useMemo( + () => + columnDef.filterSelectOptions ?? + ((isSelectFilter || isMultiSelectFilter || isAutocompleteFilter) && + facetedUniqueValues + ? Array.from(facetedUniqueValues.keys()) + .filter((value) => value !== null && value !== undefined) + .sort((a, b) => a.localeCompare(b)) + : undefined), + [ + columnDef.filterSelectOptions, + facetedUniqueValues, + isMultiSelectFilter, + isSelectFilter, + ], + ); + + return { + allowedColumnFilterOptions, + currentFilterOption, + dropdownOptions, + facetedUniqueValues, + isAutocompleteFilter, + isDateFilter, + isMultiSelectFilter, + isRangeFilter, + isSelectFilter, + isTextboxFilter, + } as const; +}; diff --git a/packages/material-react-table/src/utils/utils.ts b/packages/material-react-table/src/utils/utils.ts index a11b5b7a2..bebde3eb1 100644 --- a/packages/material-react-table/src/utils/utils.ts +++ b/packages/material-react-table/src/utils/utils.ts @@ -6,7 +6,7 @@ export const parseFromValuesOrFunc = ( ): T | undefined => (fn instanceof Function ? fn(arg) : fn); export const getValueAndLabel = ( - option: DropdownOption, + option?: DropdownOption, ): { label: string; value: string } => { let label: string = ''; let value: string = ''; diff --git a/packages/material-react-table/stories/features/Virtualization.stories.tsx b/packages/material-react-table/stories/features/Virtualization.stories.tsx index e34844b3c..740f55035 100644 --- a/packages/material-react-table/stories/features/Virtualization.stories.tsx +++ b/packages/material-react-table/stories/features/Virtualization.stories.tsx @@ -96,7 +96,6 @@ export const EnableRowVirtualizationDense = () => ( data={longData} enableBottomToolbar={false} enablePagination={false} - enableRowNumbers enableRowVirtualization initialState={{ density: 'compact' }} />