Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Improve filters #635

Merged
merged 15 commits into from
Nov 25, 2020
4 changes: 4 additions & 0 deletions packages/grid/_modules_/grid/components/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ export const LoadIcon = createSvgIcon(
<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />,
'Load',
);
export const DragIcon = createSvgIcon(
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />,
'Drag',
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ export const HideColMenuItem: React.FC<FilterItemProps> = ({ column, onClick })
[apiRef, column?.field, onClick],
);

if (!column) {
return null;
}

return <MenuItem onClick={toggleColumn}>Hide</MenuItem>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const useStyles = makeStyles(
alignItems: 'center',
minHeight: 35, // Match MUI Small Button height
backgroundColor: theme.palette.background.default,
border: `1px solid ${borderColor}`,
paddingLeft: 4,
},
'& .MuiDataGrid-columnsContainer': {
position: 'absolute',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconButton } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import * as React from 'react';
import { PreferencePanelsValue } from '../../hooks/features/preferencesPanel/preferencesPanelValue';
import { useIcons } from '../../hooks/utils/useIcons';
Expand All @@ -14,8 +14,13 @@ export const ColumnsToolbarButton: React.FC<{}> = () => {
}, [apiRef]);

return (
<IconButton onClick={showColumns} color="primary" aria-label="Show Column Selector">
{iconElement}
</IconButton>
<Button
onClick={showColumns}
color="primary"
aria-label="Show Column Selector"
startIcon={iconElement}
>
Columns
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
import { IconButton } from '@material-ui/core';
import Badge from '@material-ui/core/Badge';
import Button from '@material-ui/core/Button';
import Tooltip from '@material-ui/core/Tooltip';
import * as React from 'react';
import { columnLookupSelector } from '../../hooks/features/columns/columnsSelector';
import { useGridSelector } from '../../hooks/features/core/useGridSelector';
import {
activeFilterItemsSelector,
filterItemsCounterSelector,
} from '../../hooks/features/filter/filterSelector';
import { useIcons } from '../../hooks/utils/useIcons';
import { optionsSelector } from '../../hooks/utils/useOptionsProp';
import { ApiContext } from '../api-context';

export const FilterToolbarButton: React.FC<{}> = () => {
const apiRef = React.useContext(ApiContext);
const options = useGridSelector(apiRef, optionsSelector);
const counter = useGridSelector(apiRef, filterItemsCounterSelector);
const activeFilters = useGridSelector(apiRef, activeFilterItemsSelector);
const lookup = useGridSelector(apiRef, columnLookupSelector);
const tooltipContentNode = React.useMemo(() => {
if (counter === 0) {
return 'Show Filters';
}
return (
<div>
{counter} active filter(s)
<ul>
{activeFilters.map((item) => (
<li key={item.id}>
{lookup[item.columnField!].headerName || item.columnField} {item.operatorValue}{' '}
{item.value}
</li>
))}
</ul>
</div>
);
}, [counter, activeFilters, lookup]);

const icons = useIcons();
const filterIconElement = React.createElement(icons.ColumnFiltering!, {});
// const columnsIconElement = React.createElement(icons.ColumnSelector!, {});
const showFilter = React.useCallback(() => {
apiRef!.current.showFilterPanel();
}, [apiRef]);
Expand All @@ -21,8 +48,19 @@ export const FilterToolbarButton: React.FC<{}> = () => {
}

return (
<IconButton onClick={showFilter} color="primary" aria-label="Show Filters">
{filterIconElement}
</IconButton>
<Tooltip title={tooltipContentNode} enterDelay={1000}>
<Button
onClick={showFilter}
color="primary"
aria-label="Show Filters"
startIcon={
<Badge badgeContent={counter} color="primary">
{filterIconElement}
</Badge>
}
>
Filters
</Button>
</Tooltip>
);
};
77 changes: 66 additions & 11 deletions packages/grid/_modules_/grid/components/tools/ColumnsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { FormControl, IconButton, Switch } from '@material-ui/core';
import Button from '@material-ui/core/Button';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import GridList from '@material-ui/core/GridList';
import ListItem from '@material-ui/core/ListItem';
import TextField from '@material-ui/core/TextField';
import * as React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { allColumnsSelector } from '../../hooks/features/columns/columnsSelector';
import { useGridSelector } from '../../hooks/features/core/useGridSelector';
import { PREVENT_HIDE_PREFERENCES } from '../../constants/index';
import { ApiContext } from '../api-context';
import { DragIcon } from '../icons/index';

const useStyles = makeStyles(() => ({
gridListRoot: {
maxWidth: '100%',
columnsListContainer: {
paddingTop: 8,
paddingLeft: 12,
},
column: {
display: 'flex',
justifyContent: 'space-between',
padding: '2px 4px',
},
switch: {
marginRight: 4,
},
dragIconRoot: {
justifyContent: 'flex-end',
},
}));

export const ColumnsPanel: React.FC<{}> = () => {
const classes = useStyles();

const apiRef = React.useContext(ApiContext);
const searchTextRef = React.useRef<HTMLInputElement>(null);
const columns = useGridSelector(apiRef, allColumnsSelector);
const [searchValue, setSearchValue] = React.useState('');

const dontHidePreferences = React.useCallback(
(event: React.ChangeEvent<{}>) => {
Expand Down Expand Up @@ -52,26 +66,67 @@ export const ColumnsPanel: React.FC<{}> = () => {
const showAllColumns = React.useCallback(() => toggleAllColumns(false), [toggleAllColumns]);
const hideAllColumns = React.useCallback(() => toggleAllColumns(true), [toggleAllColumns]);

const onSearchColumnValueChange = React.useCallback((event) => {
setSearchValue(event.target.value.toLowerCase());
}, []);

const currentColumns = React.useMemo(
() =>
!searchValue
? columns
: columns.filter(
(column) =>
column.field.toLowerCase().indexOf(searchValue) > -1 ||
(column.headerName && column.headerName.toLowerCase().indexOf(searchValue) > -1),
),
[columns, searchValue],
);

React.useEffect(() => {
if (searchTextRef && searchTextRef.current) {
searchTextRef!.current!.focus();
}
});

return (
<React.Fragment>
<div className="panelHeader">
<TextField
ref={searchTextRef}
label={'Find column'}
placeholder={'Column Title'}
value={searchValue}
onChange={onSearchColumnValueChange}
type={'text'}
autoFocus
fullWidth
/>
</div>
<div className="panelMainContainer">
<GridList cellHeight={'auto'} className={classes.gridListRoot}>
{columns.map((column) => (
<ListItem key={column.field}>
<div className={classes.columnsListContainer}>
{currentColumns.map((column) => (
<div key={column.field} className={classes.column}>
<FormControlLabel
control={
<Checkbox
<Switch
className={classes.switch}
checked={!column.hide}
onChange={toggleColumn}
name={column.field}
color="primary"
size="small"
/>
}
label={column.headerName || column.field}
/>
</ListItem>
<FormControl className={classes.dragIconRoot}>
<IconButton aria-label="Drag to reorder column" title="Reorder Column" size="small">
<DragIcon />
</IconButton>
</FormControl>
</div>
))}
</GridList>
</div>
</div>
<div className="panelFooter">
<Button onClick={hideAllColumns} color="primary">
Expand Down
9 changes: 6 additions & 3 deletions packages/grid/_modules_/grid/components/tools/FilterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ const useStyles = makeStyles(() => ({
operatorSelect: {
width: 120,
},
FilterValueInput: {
filterValueInput: {
width: 190,
},
closeIconRoot: {
justifyContent: 'flex-end',
},
}));

export const FilterForm: React.FC<FilterFormProps> = ({
Expand Down Expand Up @@ -182,7 +185,7 @@ export const FilterForm: React.FC<FilterFormProps> = ({
))}
</Select>
</FormControl>
<FormControl className={classes.FilterValueInput}>
<FormControl className={classes.filterValueInput}>
{currentColumn &&
currentOperator &&
React.createElement(currentOperator.InputComponent, {
Expand All @@ -191,7 +194,7 @@ export const FilterForm: React.FC<FilterFormProps> = ({
...currentOperator.InputComponentProps,
})}
</FormControl>
<FormControl>
<FormControl className={classes.closeIconRoot}>
<IconButton aria-label="Delete" title="Delete" onClick={handleDeleteFilter} size="small">
<CloseIcon />
</IconButton>
Expand Down
25 changes: 12 additions & 13 deletions packages/grid/_modules_/grid/components/tools/FilterPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import * as React from 'react';
import { Button } from '@material-ui/core';
import { useGridSelector } from '../../hooks/features/core/useGridSelector';
import { useGridState } from '../../hooks/features/core/useGridState';
import { PREVENT_HIDE_PREFERENCES } from '../../constants/index';
import { optionsSelector } from '../../hooks/utils/useOptionsProp';
import { FilterItem, LinkOperator } from '../../models/filterItem';
import { ApiContext } from '../api-context';
import { AddIcon, CloseIcon } from '../icons/index';
import { AddIcon } from '../icons/index';
import { FilterForm } from './FilterForm';

export const FilterPanel: React.FC<{}> = () => {
const apiRef = React.useContext(ApiContext);
const [gridState] = useGridState(apiRef!);
const { disableMultipleColumnsFiltering } = useGridSelector(apiRef, optionsSelector);

const hasMultipleFilters = React.useMemo(() => gridState.filter.items.length > 1, [
gridState.filter.items.length,
]);
Expand Down Expand Up @@ -40,10 +44,6 @@ export const FilterPanel: React.FC<{}> = () => {
apiRef!.current.upsertFilter({});
}, [apiRef]);

const clearFilter = React.useCallback(() => {
apiRef!.current.clearFilters();
}, [apiRef]);

const deleteFilter = React.useCallback(
(item: FilterItem) => {
apiRef!.current.deleteFilter(item);
Expand Down Expand Up @@ -75,14 +75,13 @@ export const FilterPanel: React.FC<{}> = () => {
/>
))}
</div>
<div className="panelFooter">
<Button onClick={clearFilter} startIcon={<CloseIcon />} color="primary">
Clear
</Button>
<Button onClick={addNewFilter} startIcon={<AddIcon />} color="primary">
Filter
</Button>
</div>
{!disableMultipleColumnsFiltering && (
<div className="panelFooter">
<Button onClick={addNewFilter} startIcon={<AddIcon />} color="primary">
Add Filter
</Button>
</div>
)}
</React.Fragment>
);
};
Loading