refactoring filters

This commit is contained in:
shamsmosowi
2021-10-01 18:19:49 +10:00
parent 68bb3a72e3
commit aec3d45bbe
5 changed files with 85 additions and 272 deletions

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import _find from "lodash/find";
import _sortBy from "lodash/sortBy";
import _isEmpty from "lodash/isEmpty";
import { useForm } from "react-hook-form";
import { makeStyles, createStyles } from "@mui/styles";
import {
Popover,
@@ -11,78 +11,21 @@ import {
Grid,
MenuItem,
TextField,
FormControlLabel,
Switch,
Chip,
} from "@mui/material";
import FilterIcon from "@mui/icons-material/FilterList";
import CloseIcon from "@mui/icons-material/Close";
import MultiSelect from "@rowy/multiselect";
import ButtonWithStatus from "components/ButtonWithStatus";
import { FieldType } from "constants/fields";
import { TableFilter } from "hooks/useTable";
import { useProjectContext } from "contexts/ProjectContext";
import { useAppContext } from "contexts/AppContext";
import { DocActions } from "hooks/useDoc";
import { getFieldProp } from "@src/components/fields";
const getType = (column) =>
column.type === FieldType.derivative
? column.config.renderFieldType
: column.type;
const OPERATORS = [
{
value: "==",
label: "Equals",
compatibleTypes: [
FieldType.phone,
FieldType.color,
FieldType.date,
FieldType.dateTime,
FieldType.shortText,
FieldType.singleSelect,
FieldType.url,
FieldType.email,
FieldType.checkbox,
],
},
{
value: "in",
label: "matches any of",
compatibleTypes: [FieldType.singleSelect],
},
// {
// value: "array-contains",
// label: "includes",
// compatibleTypes: [FieldType.connectTable],
// },
// {
// value: "array-contains",
// label: "Has",
// compatibleTypes: [FieldType.multiSelect],
// },
{
value: "array-contains-any",
label: "Has any",
compatibleTypes: [FieldType.multiSelect, FieldType.connectTable],
},
{ value: "<", label: "<", compatibleTypes: [FieldType.number] },
{ value: "<=", label: "<=", compatibleTypes: [FieldType.number] },
{ value: "==", label: "==", compatibleTypes: [FieldType.number] },
{ value: ">=", label: ">=", compatibleTypes: [FieldType.number] },
{ value: ">", label: ">", compatibleTypes: [FieldType.number] },
{
value: "<",
label: "before",
compatibleTypes: [FieldType.date, FieldType.dateTime],
},
{
value: ">=",
label: "after",
compatibleTypes: [FieldType.date, FieldType.dateTime],
},
];
const useStyles = makeStyles((theme) =>
createStyles({
@@ -127,14 +70,6 @@ const useStyles = makeStyles((theme) =>
})
);
const UNFILTERABLES = [
FieldType.image,
FieldType.file,
FieldType.action,
FieldType.subTable,
FieldType.last,
FieldType.longText,
];
const Filters = () => {
const { tableState, tableActions } = useProjectContext();
const { userDoc } = useAppContext();
@@ -150,7 +85,7 @@ const Filters = () => {
}
}, [userDoc.state, tableState?.tablePath]);
const filterColumns = _sortBy(Object.values(tableState!.columns), "index")
.filter((c) => !UNFILTERABLES.includes(c.type))
.filter((c) => getFieldProp("filter", c.type))
.map((c) => ({
key: c.key,
label: c.name,
@@ -159,7 +94,6 @@ const Filters = () => {
...c,
}));
const classes = useStyles();
const filters = [];
const [selectedColumn, setSelectedColumn] = useState<any>();
@@ -169,52 +103,24 @@ const Filters = () => {
value: "",
});
const [operators, setOperators] = useState<any[]>([]);
const type = selectedColumn ? getType(selectedColumn) : null;
//const [filter, setFilter] = useState<any>();
const customFieldInput = type ? getFieldProp("SideDrawerField", type) : null;
useEffect(() => {
if (selectedColumn) {
const _filter = getFieldProp("filter", selectedColumn.type);
//setFilter(_filter)
setOperators(_filter?.operators ?? []);
let updatedQuery: TableFilter = {
key: selectedColumn.key,
operator: "",
value: "",
operator: _filter.operators[0].value,
value: _filter.defaultValue,
};
const type = getType(selectedColumn);
if (
[
FieldType.phone,
FieldType.shortText,
FieldType.url,
FieldType.email,
FieldType.checkbox,
].includes(type)
) {
updatedQuery = { ...updatedQuery, operator: "==" };
}
if (type === FieldType.checkbox) {
updatedQuery = { ...updatedQuery, value: false };
}
if (type === FieldType.connectTable) {
updatedQuery = {
key: `${selectedColumn.key}ID`,
operator: "array-contains-any",
value: [],
};
}
if (type === FieldType.multiSelect) {
updatedQuery = {
...updatedQuery,
operator: "array-contains-any",
value: [],
};
}
setQuery(updatedQuery);
}
}, [selectedColumn]);
const operators = selectedColumn
? OPERATORS.filter((operator) =>
operator.compatibleTypes.includes(getType(selectedColumn))
)
: [];
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const handleClose = () => setAnchorEl(null);
@@ -230,133 +136,6 @@ const Filters = () => {
const id = open ? "simple-popper" : undefined;
const renderInputField = (selectedColumn, operator) => {
const type = getType(selectedColumn);
switch (type) {
case FieldType.checkbox:
return (
<FormControlLabel
control={
<Switch
value={query.value}
onChange={(e) => {
setQuery((query) => ({ ...query, value: e.target.checked }));
}}
size="medium"
/>
}
label="Value"
labelPlacement="top"
componentsProps={{
typography: { variant: "button" },
}}
sx={{
mr: 0,
ml: -0.5,
position: "relative",
bottom: -2,
"& .MuiFormControlLabel-label": { mt: 0, mb: -1 / 8, ml: 0.75 },
}}
/>
);
case FieldType.email:
case FieldType.phone:
case FieldType.shortText:
case FieldType.longText:
case FieldType.url:
return (
<TextField
label="Value"
id="value"
onChange={(e) => {
const value = e.target.value;
if (value) setQuery((query) => ({ ...query, value: value }));
}}
placeholder="Text value"
/>
);
case FieldType.number:
return (
<TextField
label="Value"
id="value"
onChange={(e) => {
const value = e.target.value;
if (query.value || value)
setQuery((query) => ({
...query,
value: value !== "" ? parseFloat(value) : "",
}));
}}
value={typeof query.value === "number" ? query.value : ""}
type="number"
placeholder="Number value"
/>
);
case FieldType.singleSelect:
if (operator === "in")
return (
<MultiSelect
label="Value"
multiple
max={10}
freeText={true}
onChange={(value) => setQuery((query) => ({ ...query, value }))}
options={
selectedColumn.config.options
? selectedColumn.config.options.sort()
: []
}
value={Array.isArray(query?.value) ? query.value : []}
/>
);
return (
<MultiSelect
label="Value"
freeText={true}
multiple={false}
onChange={(value) => {
if (value !== null) setQuery((query) => ({ ...query, value }));
}}
options={
selectedColumn.config.options
? selectedColumn.config.options.sort()
: []
}
value={typeof query?.value === "string" ? query.value : null}
/>
);
case FieldType.multiSelect:
return (
<MultiSelect
label="Value"
multiple
onChange={(value) => setQuery((query) => ({ ...query, value }))}
value={query.value as string[]}
max={10}
options={
selectedColumn.config.options
? selectedColumn.config.options.sort()
: []
}
searchable={false}
freeText={true}
/>
);
case FieldType.date:
case FieldType.dateTime:
return <>//TODO:Date/Time picker</>;
default:
return <>Not available</>;
// return <TextField variant="filled" fullWidth disabled />;
break;
}
};
const handleUpdateFilters = (filters: TableFilter[]) => {
userDoc.dispatch({
action: DocActions.update,
@@ -365,6 +144,14 @@ const Filters = () => {
},
});
};
const { control } = useForm({
mode: "onBlur",
// defaultValues: {
// [fieldName]:
// config.defaultValue?.value ?? getFieldProp("initialValue", _type),
// },
});
return (
<>
<Grid container direction="row" wrap="nowrap" style={{ width: "auto" }}>
@@ -410,38 +197,7 @@ const Filters = () => {
<IconButton className={classes.closeButton} onClick={handleClose}>
<CloseIcon />
</IconButton>
<div className={classes.content}>
{/* <Grid
container
alignItems="center"
spacing={2}
className={classes.topRow}
>
<Grid item>
<Typography component="span">Results match</Typography>
</Grid>
<Grid item>
<TextField
select
variant="filled"
id="demo-simple-select-filled"
value={combineType}
hiddenLabel
// disabled
// onChange={handleChange}
>
<MenuItem value="all">all</MenuItem>
<MenuItem value="any">any</MenuItem>
</TextField>
</Grid>
<Grid item>
<Typography component="span">of the filter criteria.</Typography>
</Grid>
</Grid> */}
<Grid container spacing={2}>
<Grid item xs={4}>
<TextField
@@ -492,13 +248,25 @@ const Filters = () => {
))}
</TextField>
</Grid>
<Grid item xs={4}>
{query.operator &&
renderInputField(selectedColumn, query.operator)}
{/* {query.operator&&React.createElement(filter.input,{
handleChange: (value) => setQuery((query) => ({
...query,
value,
}))
,
key: query.key,
},null)} */}
{customFieldInput &&
React.createElement(customFieldInput, {
column: selectedColumn,
control,
docRef: {},
disabled: false,
})}
</Grid>
</Grid>
<Grid
container
className={classes.bottomButtons}
@@ -516,13 +284,11 @@ const Filters = () => {
value: "",
});
setSelectedColumn(null);
//handleClose();
}}
>
Clear
</Button>
</Grid>
<Grid item>
<Button
disabled={
@@ -547,5 +313,4 @@ const Filters = () => {
</>
);
};
export default Filters;

View File

@@ -5,6 +5,7 @@ import withBasicCell from "../_withTableCell/withBasicCell";
import LongTextIcon from "@mui/icons-material/Notes";
import BasicCell from "./BasicCell";
import withSideDrawerEditor from "components/Table/editors/withSideDrawerEditor";
import FilterField, { filterOperators } from "../ShortText/Filter";
const SideDrawerField = lazy(
() =>
@@ -25,5 +26,9 @@ export const config: IFieldConfig = {
TableCell: withBasicCell(BasicCell),
TableEditor: withSideDrawerEditor(BasicCell),
SideDrawerField,
filter: {
input: FilterField,
operators: filterOperators,
},
};
export default config;

View File

@@ -0,0 +1,28 @@
import { IFiltersProps, IFilterOperator } from "../types";
import { TextField } from "@mui/material";
export default function ShortText({ handleChange, key }: IFiltersProps) {
return (
<TextField
label="Value"
id={key}
onChange={(e) => {
const value = e.target.value;
handleChange(value);
}}
placeholder="Text value"
/>
);
}
export const filterOperators: IFilterOperator[] = [
{
label: "equals",
value: "==",
},
{
label: "not equals",
value: "!=",
},
];

View File

@@ -6,12 +6,14 @@ import ShortTextIcon from "@mui/icons-material/ShortText";
import BasicCell from "../_BasicCell/BasicCellValue";
import TextEditor from "components/Table/editors/TextEditor";
import FilterField, { filterOperators } from "./Filter";
const SideDrawerField = lazy(
() =>
import(
"./SideDrawerField" /* webpackChunkName: "SideDrawerField-ShortText" */
)
);
const Settings = lazy(
() => import("./Settings" /* webpackChunkName: "Settings-ShortText" */)
);
@@ -29,5 +31,9 @@ export const config: IFieldConfig = {
TableEditor: TextEditor,
SideDrawerField,
settings: Settings,
filter: {
operators: filterOperators,
input: FilterField,
},
};
export default config;

View File

@@ -21,7 +21,11 @@ export interface IFieldConfig {
TableEditor: React.ComponentType<EditorProps<any, any>>;
SideDrawerField: React.ComponentType<ISideDrawerFieldProps>;
settings?: React.ComponentType<ISettingsProps>;
filters?: React.ComponentType<IFiltersProps>;
filter?: {
operators: IFilterOperator[];
input: React.ComponentType<IFiltersProps>;
defaultValue?: any;
};
csvExportFormatter?: (value: any, config?: any) => string;
csvImportParser?: (value: string, config?: any) => any;
}
@@ -66,3 +70,8 @@ export interface IFiltersProps {
handleChange: (key: string) => (value: any) => void;
[key: string]: any;
}
export interface IFilterOperator {
value: firebase.default.firestore.WhereFilterOp;
label: string;
}