add SingleSelect, MultiSelect

This commit is contained in:
Sidney Alcantara
2021-01-12 19:31:41 +11:00
parent 4bb8d572ee
commit 82b9785476
14 changed files with 484 additions and 9 deletions

View File

@@ -64,6 +64,13 @@ export default function withPopoverCell(
setLocalValue(value);
}, [value]);
const handleSubmit = (value: any) => {
if (updateCell && !options?.readOnly) {
updateCell(props.row.ref, props.column.key as string, value);
setLocalValue(value);
}
};
const basicCell = (
<BasicCell
value={localValue}
@@ -72,6 +79,7 @@ export default function withPopoverCell(
setShowComplexCell={setShowComplexCell}
ref={basicCellRef}
disabled={props.column.editable === false}
onSubmit={handleSubmit}
/>
);
@@ -79,13 +87,6 @@ export default function withPopoverCell(
const parentRef = basicCellRef.current?.parentElement;
const handleSubmit = (value: any) => {
if (updateCell && !options?.readOnly) {
updateCell(props.row.ref, props.column.key as string, value);
setLocalValue(value);
}
};
// Switch to heavy cell Component once basic cell is clicked
return (
<>
@@ -104,6 +105,8 @@ export default function withPopoverCell(
},
...popoverProps?.PaperProps,
}}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<Cell
{...props}
@@ -112,6 +115,7 @@ export default function withPopoverCell(
onSubmit={handleSubmit}
disabled={props.column.editable === false}
setShowComplexCell={setShowComplexCell}
parentRef={parentRef}
/>
</Popover>
</Suspense>

View File

@@ -0,0 +1,26 @@
import React from "react";
import { Grid, Tooltip, Button } from "@material-ui/core";
export const ConvertStringToArray = ({ value, onSubmit }) => (
<Grid container wrap="nowrap" alignItems="center">
<Grid item xs style={{ overflow: "hidden", textOverflow: "ellipsis" }}>
{value}
</Grid>
<Grid item>
<Tooltip title="It looks like this is a string. Click to convert to an array">
<Button
onClick={() => onSubmit([value])}
style={{
display: "flex",
minWidth: 0,
marginRight: -12,
paddingLeft: 12,
paddingRight: 12,
}}
>
Fix
</Button>
</Tooltip>
</Grid>
</Grid>
);

View File

@@ -0,0 +1,89 @@
import React from "react";
import clsx from "clsx";
import { IPopoverBasicCellProps } from "../types";
import { makeStyles, createStyles, ButtonBase, Grid } from "@material-ui/core";
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
import { sanitiseValue } from "./utils";
import FormattedChip from "components/FormattedChip";
import { ConvertStringToArray } from "./ConvertStringToArray";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
height: "100%",
padding: theme.spacing(0, 1, 0, 1.5),
font: "inherit",
color: "inherit !important",
letterSpacing: "inherit",
textAlign: "inherit",
justifyContent: "flex-start",
},
value: {
flex: 1,
maxWidth: `calc(100% - 24px + 4px)`,
overflow: "hidden",
marginRight: 0,
},
chip: {
display: "flex",
cursor: "inherit",
},
chipLabel: { whiteSpace: "nowrap" },
icon: {
display: "block",
color: theme.palette.action.active,
},
disabled: {
color: theme.palette.action.disabled,
},
})
);
export const SingleSelect = React.forwardRef(function SingleSelect(
{ value, setShowComplexCell, disabled, onSubmit }: IPopoverBasicCellProps,
ref: React.Ref<any>
) {
const classes = useStyles();
if (typeof value === "string" && value !== "")
return <ConvertStringToArray value={value} onSubmit={onSubmit} />;
return (
<ButtonBase
className={clsx("cell-collapse-padding", classes.root)}
onClick={() => setShowComplexCell(true)}
ref={ref}
disabled={disabled}
>
<Grid
container
wrap="nowrap"
alignItems="center"
spacing={1}
className={classes.value}
>
{sanitiseValue(value).map(
(item) =>
typeof item === "string" && (
<Grid item key={item}>
<FormattedChip
label={item}
classes={{ root: classes.chip, label: classes.chipLabel }}
/>
</Grid>
)
)}
</Grid>
<ArrowDropDownIcon
className={clsx(classes.icon, disabled && classes.disabled)}
/>
</ButtonBase>
);
});
export default SingleSelect;

View File

@@ -0,0 +1,43 @@
import React from "react";
import { IPopoverCellProps } from "../types";
import _get from "lodash/get";
import MultiSelect_ from "@antlerengineering/multiselect";
import { sanitiseValue } from "./utils";
export default function SingleSelect({
value,
onSubmit,
column,
parentRef,
setShowComplexCell,
disabled,
}: IPopoverCellProps) {
const config = column.config ?? {};
return (
<MultiSelect_
value={sanitiseValue(value)}
onChange={onSubmit}
options={config.options ?? []}
multiple
freeText={config.freeText}
disabled={disabled}
label={column.name}
labelPlural={column.name}
TextFieldProps={{
style: { display: "none" },
SelectProps: {
open: true,
MenuProps: {
anchorEl: parentRef,
anchorOrigin: { vertical: "bottom", horizontal: "left" },
transformOrigin: { vertical: "top", horizontal: "left" },
},
},
}}
onClose={() => setShowComplexCell(false)}
/>
);
}

View File

@@ -0,0 +1,76 @@
import React from "react";
import { Controller } from "react-hook-form";
import { ISideDrawerFieldProps } from "../types";
import { useTheme, Grid } from "@material-ui/core";
import MultiSelect_ from "@antlerengineering/multiselect";
import FormattedChip from "components/FormattedChip";
import { sanitiseValue } from "./utils";
import { ConvertStringToArray } from "./ConvertStringToArray";
export default function SingleSelect({
column,
control,
disabled,
}: ISideDrawerFieldProps) {
const theme = useTheme();
const config = column.config ?? {};
return (
<Controller
control={control}
name={column.key}
render={({ onChange, onBlur, value }) => {
const handleDelete = (index: number) => () => {
const newValues = [...value];
newValues.splice(index, 1);
onChange(newValues);
};
if (typeof value === "string" && value !== "")
return <ConvertStringToArray value={value} onSubmit={onChange} />;
return (
<>
<MultiSelect_
value={sanitiseValue(value)}
onChange={onChange}
options={config.options ?? []}
multiple={true}
freeText={config.freeText}
disabled={disabled}
TextFieldProps={{
label: "",
hiddenLabel: true,
onBlur,
}}
/>
{value && Array.isArray(value) && (
<Grid
container
spacing={1}
style={{ marginTop: theme.spacing(1) }}
>
{value.map(
(item, i) =>
item?.length > 0 && (
<Grid item key={item}>
<FormattedChip
size="medium"
label={item}
onDelete={disabled ? undefined : handleDelete(i)}
/>
</Grid>
)
)}
</Grid>
)}
</>
);
}}
/>
);
}

View File

@@ -0,0 +1,35 @@
import React, { lazy } from "react";
import { IFieldConfig, FieldType } from "components/fields/types";
import withPopoverCell from "components/Table/withPopoverCell";
import MultiSelectIcon from "assets/icons/MultiSelect";
import PopoverBasicCell from "./PopoverBasicCell";
import NullEditor from "components/Table/editors/NullEditor";
const PopoverCell = lazy(
() =>
import("./PopoverCell" /* webpackChunkName: "PopoverCell-MultiSelect" */)
);
const SideDrawerField = lazy(
() =>
import(
"./SideDrawerField" /* webpackChunkName: "SideDrawerField-MultiSelect" */
)
);
export const config: IFieldConfig = {
type: FieldType.multiSelect,
name: "MultiSelect",
dataType: "string[]",
initialValue: [],
icon: <MultiSelectIcon />,
description:
"Dropdown selector with searchable options and check box behavior. Optionally allows users to input custom values. Max selection: all options.",
TableCell: withPopoverCell(PopoverCell, PopoverBasicCell, {
anchorOrigin: { horizontal: "left", vertical: "bottom" },
transparent: true,
}),
TableEditor: NullEditor,
SideDrawerField,
};
export default config;

View File

@@ -0,0 +1,4 @@
export const sanitiseValue = (value: any) => {
if (value === undefined || value === null || value === "") return [];
else return value as string[];
};

View File

@@ -0,0 +1,61 @@
import React from "react";
import clsx from "clsx";
import { IPopoverBasicCellProps } from "../types";
import { makeStyles, createStyles, ButtonBase } from "@material-ui/core";
import ArrowDropDownIcon from "@material-ui/icons/ArrowDropDown";
import { sanitiseValue } from "./utils";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
height: "100%",
padding: theme.spacing(0, 1, 0, 1.5),
font: "inherit",
color: "inherit !important",
letterSpacing: "inherit",
textAlign: "inherit",
justifyContent: "flex-start",
},
value: {
flex: 1,
maxWidth: `calc(100% - 24px)`,
overflow: "hidden",
textOverflow: "ellipsis",
},
icon: {
display: "block",
color: theme.palette.action.active,
},
disabled: {
color: theme.palette.action.disabled,
},
})
);
export const SingleSelect = React.forwardRef(function SingleSelect(
{ value, setShowComplexCell, disabled }: IPopoverBasicCellProps,
ref: React.Ref<any>
) {
const classes = useStyles();
return (
<ButtonBase
className={clsx("cell-collapse-padding", classes.root)}
onClick={() => setShowComplexCell(true)}
ref={ref}
disabled={disabled}
>
<div className={classes.value}>{sanitiseValue(value)}</div>
<ArrowDropDownIcon
className={clsx(classes.icon, disabled && classes.disabled)}
/>
</ButtonBase>
);
});
export default SingleSelect;

View File

@@ -0,0 +1,43 @@
import React from "react";
import { IPopoverCellProps } from "../types";
import _get from "lodash/get";
import MultiSelect_ from "@antlerengineering/multiselect";
import { sanitiseValue } from "./utils";
export default function SingleSelect({
value,
onSubmit,
column,
parentRef,
setShowComplexCell,
disabled,
}: IPopoverCellProps) {
const config = column.config ?? {};
return (
<MultiSelect_
value={sanitiseValue(value)}
onChange={onSubmit}
options={config.options ?? []}
multiple={false}
freeText={config.freeText}
disabled={disabled}
label={column.name}
labelPlural={column.name}
TextFieldProps={{
style: { display: "none" },
SelectProps: {
open: true,
MenuProps: {
anchorEl: parentRef,
anchorOrigin: { vertical: "bottom", horizontal: "left" },
transformOrigin: { vertical: "top", horizontal: "left" },
},
},
}}
onClose={() => setShowComplexCell(false)}
/>
);
}

View File

@@ -0,0 +1,49 @@
import React from "react";
import { Controller } from "react-hook-form";
import { ISideDrawerFieldProps } from "../types";
import { useTheme } from "@material-ui/core";
import MultiSelect_ from "@antlerengineering/multiselect";
import FormattedChip from "components/FormattedChip";
import { sanitiseValue } from "./utils";
export default function SingleSelect({
column,
control,
disabled,
}: ISideDrawerFieldProps) {
const theme = useTheme();
const config = column.config ?? {};
return (
<Controller
control={control}
name={column.key}
render={({ onChange, onBlur, value }) => (
<>
<MultiSelect_
value={sanitiseValue(value)}
onChange={onChange}
options={config.options ?? []}
multiple={false}
freeText={config.freeText}
disabled={disabled}
TextFieldProps={{
label: "",
hiddenLabel: true,
onBlur,
}}
/>
{value?.length > 0 && (
<div style={{ marginTop: theme.spacing(1) }}>
<FormattedChip size="medium" label={value} />
</div>
)}
</>
)}
/>
);
}

View File

@@ -0,0 +1,35 @@
import React, { lazy } from "react";
import { IFieldConfig, FieldType } from "components/fields/types";
import withPopoverCell from "components/Table/withPopoverCell";
import SingleSelectIcon from "@material-ui/icons/FormatListBulleted";
import PopoverBasicCell from "./PopoverBasicCell";
import NullEditor from "components/Table/editors/NullEditor";
const PopoverCell = lazy(
() =>
import("./PopoverCell" /* webpackChunkName: "PopoverCell-SingleSelect" */)
);
const SideDrawerField = lazy(
() =>
import(
"./SideDrawerField" /* webpackChunkName: "SideDrawerField-SingleSelect" */
)
);
export const config: IFieldConfig = {
type: FieldType.singleSelect,
name: "SingleSelect",
dataType: "string | null",
initialValue: null,
icon: <SingleSelectIcon />,
description:
"Dropdown selector with searchable options and radio button behavior. Optionally allows users to input custom values. Max selection: 1 option.",
TableCell: withPopoverCell(PopoverCell, PopoverBasicCell, {
anchorOrigin: { horizontal: "left", vertical: "bottom" },
transparent: true,
}),
TableEditor: NullEditor,
SideDrawerField,
};
export default config;

View File

@@ -0,0 +1,5 @@
export const sanitiseValue = (value: any) => {
if (value === undefined || value === null || value === "") return null;
else if (Array.isArray(value)) return value[0] as string;
else return value as string;
};

View File

@@ -21,6 +21,8 @@ import DateTime from "./DateTime";
import Duration from "./Duration";
import Image_ from "./Image";
import File_ from "./File";
import SingleSelect from "./SingleSelect";
import MultiSelect from "./MultiSelect";
import SubTable from "./SubTable";
import Json from "./Json";
import Code from "./Code";
@@ -54,8 +56,8 @@ export const FIELDS: IFieldConfig[] = [
Image_,
File_,
// SELECT
// TODO: singleSelect,
// TODO: multiSelect,
SingleSelect,
MultiSelect,
// CONNECTION
SubTable,
// TODO: connectTable,

View File

@@ -2,6 +2,7 @@ import { FieldType } from "constants/fields";
import { FormatterProps, EditorProps } from "react-data-grid";
import { Control } from "react-hook-form";
import { PopoverProps } from "@material-ui/core";
export { FieldType };
@@ -36,10 +37,12 @@ export interface IBasicCellProps {
export interface IPopoverCellProps extends ICustomCellProps {
setShowComplexCell: React.Dispatch<React.SetStateAction<boolean>>;
parentRef: PopoverProps["anchorEl"];
}
export interface IPopoverBasicCellProps extends IBasicCellProps {
setShowComplexCell: React.Dispatch<React.SetStateAction<boolean>>;
disabled: boolean;
onSubmit: (value: any) => void;
}
export interface ISideDrawerFieldProps {