add withPopoverCell, Color field

This commit is contained in:
Sidney Alcantara
2021-01-12 12:14:48 +11:00
parent c417a0aa0b
commit 4bb8d572ee
7 changed files with 335 additions and 2 deletions

View File

@@ -0,0 +1,122 @@
import React, { Suspense, useState, useEffect, useRef } from "react";
import { FormatterProps } from "react-data-grid";
import {
IPopoverCellProps,
IPopoverBasicCellProps,
} from "components/fields/types";
import {
makeStyles,
createStyles,
Popover,
PopoverProps,
} from "@material-ui/core";
import ErrorBoundary from "components/ErrorBoundary";
import { useFiretableContext } from "contexts/FiretableContext";
import { FieldType } from "constants/fields";
import { getCellValue } from "utils/fns";
const useStyles = makeStyles((theme) =>
createStyles({
transparentPaper: {
backgroundColor: "transparent",
boxShadow: "none",
},
})
);
export interface IPopoverCellOptions extends Partial<PopoverProps> {
transparent?: boolean;
readOnly?: boolean;
}
/**
* HOC to wrap around custom cell formatters.
* Renders BasicCell while scrolling for better scroll performance.
* @param Cell The cell component to display
* @param BasicCell The lighter cell component to display while scrolling
* @param readOnly Prevent the formatter from updating the cell value
*/
export default function withPopoverCell(
Cell: React.ComponentType<IPopoverCellProps>,
BasicCell: React.ForwardRefExoticComponent<
IPopoverBasicCellProps & React.RefAttributes<any>
>,
options?: IPopoverCellOptions
) {
return function PopoverCell(props: FormatterProps<any>) {
const classes = useStyles();
const { transparent, readOnly, ...popoverProps } = options ?? {};
const [showComplexCell, setShowComplexCell] = useState(false);
const basicCellRef = useRef<any>(null);
// COPIED FROM withCustomCell
const { updateCell } = useFiretableContext();
// TODO: Investigate if this still needs to be a state
const value = getCellValue(props.row, props.column.key as string);
const [localValue, setLocalValue] = useState(value);
useEffect(() => {
setLocalValue(value);
}, [value]);
const basicCell = (
<BasicCell
value={localValue}
name={(props.column as any).name}
type={(props.column as any).type as FieldType}
setShowComplexCell={setShowComplexCell}
ref={basicCellRef}
disabled={props.column.editable === false}
/>
);
if (!showComplexCell) return basicCell;
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 (
<>
{basicCell}
<ErrorBoundary fullScreen={false} basic wrap="nowrap">
<Suspense fallback={null}>
<Popover
open
anchorEl={parentRef}
onClose={() => setShowComplexCell(false)}
{...popoverProps}
PaperProps={{
classes: {
root: transparent ? classes.transparentPaper : "",
},
...popoverProps?.PaperProps,
}}
>
<Cell
{...props}
docRef={props.row.ref}
value={localValue}
onSubmit={handleSubmit}
disabled={props.column.editable === false}
setShowComplexCell={setShowComplexCell}
/>
</Popover>
</Suspense>
</ErrorBoundary>
</>
);
};
}

View File

@@ -0,0 +1,62 @@
import React from "react";
import { IPopoverBasicCellProps } from "../types";
import { makeStyles, createStyles, Grid, ButtonBase } from "@material-ui/core";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
paddingLeft: theme.spacing(1.5),
font: "inherit",
color: "inherit !important",
letterSpacing: "inherit",
textAlign: "inherit",
},
colorIndicator: {
width: 20,
height: 20,
boxShadow: `0 0 0 1px ${theme.palette.text.disabled} inset`,
borderRadius: theme.shape.borderRadius / 2,
},
})
);
export const Color = React.forwardRef(function Color(
{ value, setShowComplexCell, disabled }: IPopoverBasicCellProps,
ref: React.Ref<any>
) {
const classes = useStyles();
return (
<Grid
container
alignItems="center"
spacing={1}
className={classes.root}
component={ButtonBase}
onClick={() => setShowComplexCell(true)}
ref={ref}
disabled={disabled}
>
<Grid item>
<div
className={classes.colorIndicator}
style={{ backgroundColor: value?.hex }}
/>
</Grid>
<Grid item xs>
{value?.hex}
</Grid>
</Grid>
);
});
export default Color;

View File

@@ -0,0 +1,12 @@
import React from "react";
import { IPopoverCellProps } from "../types";
import { ChromePicker } from "react-color";
import _get from "lodash/get";
export default function Color({ value, onSubmit }: IPopoverCellProps) {
const handleChangeComplete = (color) => onSubmit(color);
return (
<ChromePicker color={value?.rgb} onChangeComplete={handleChangeComplete} />
);
}

View File

@@ -0,0 +1,99 @@
import React, { useState } from "react";
import { Controller } from "react-hook-form";
import { ISideDrawerFieldProps } from "../types";
import { ChromePicker } from "react-color";
import {
makeStyles,
createStyles,
Grid,
ButtonBase,
Typography,
Collapse,
} from "@material-ui/core";
import { useFieldStyles } from "components/SideDrawer/Form/utils";
const useStyles = makeStyles((theme) =>
createStyles({
root: {
height: 56,
cursor: "pointer",
textAlign: "left",
borderRadius: theme.shape.borderRadius,
backgroundColor:
theme.palette.type === "light"
? "rgba(0, 0, 0, 0.09)"
: "rgba(255, 255, 255, 0.09)",
margin: 0,
width: "100%",
padding: theme.spacing(0, 0.75),
},
colorIndicator: {
width: 20,
height: 20,
marginLeft: 2,
boxShadow: `0 0 0 1px ${theme.palette.text.disabled} inset`,
borderRadius: theme.shape.borderRadius,
},
})
);
export default function Color({
column,
control,
disabled,
}: ISideDrawerFieldProps) {
const classes = useStyles();
const fieldClasses = useFieldStyles();
const [showPicker, setShowPicker] = useState(false);
const toggleOpen = () => setShowPicker((s) => !s);
return (
<Controller
control={control}
name={column.key}
render={({ onChange, onBlur, value }) => (
<>
<Grid
container
alignItems="center"
spacing={1}
className={classes.root}
onClick={() => {
toggleOpen();
onBlur();
}}
component={ButtonBase}
focusRipple
disabled={disabled}
>
<Grid item>
<div
className={classes.colorIndicator}
style={{ backgroundColor: value?.hex }}
/>
</Grid>
<Grid item xs>
<Typography
variant="body1"
color={value?.hex ? "textPrimary" : "textSecondary"}
>
{value?.hex ?? "Choose a color…"}
</Typography>
</Grid>
</Grid>
<Collapse in={showPicker}>
<ChromePicker color={value?.rgb} onChangeComplete={onChange} />
</Collapse>
</>
)}
/>
);
}

View File

@@ -0,0 +1,30 @@
import React, { lazy } from "react";
import { IFieldConfig, FieldType } from "components/fields/types";
import withPopoverCell from "components/Table/withPopoverCell";
import ColorIcon from "@material-ui/icons/Colorize";
import PopoverBasicCell from "./PopoverBasicCell";
import NullEditor from "components/Table/editors/NullEditor";
const PopoverCell = lazy(
() => import("./PopoverCell" /* webpackChunkName: "PopoverCell-Color" */)
);
const SideDrawerField = lazy(
() =>
import("./SideDrawerField" /* webpackChunkName: "SideDrawerField-Color" */)
);
export const config: IFieldConfig = {
type: FieldType.color,
name: "Color",
dataType: "Record<string, any>",
initialValue: undefined,
icon: <ColorIcon />,
description: "Visual color picker. Supports Hex, RGBA, HSLA.",
TableCell: withPopoverCell(PopoverCell, PopoverBasicCell, {
anchorOrigin: { horizontal: "left", vertical: "bottom" },
}),
TableEditor: NullEditor,
SideDrawerField,
};
export default config;

View File

@@ -15,6 +15,7 @@ import Number_ from "./Number";
import Percentage from "./Percentage";
import Rating from "./Rating";
import Slider from "./Slider";
import Color from "./Color";
import Date_ from "./Date";
import DateTime from "./DateTime";
import Duration from "./Duration";
@@ -44,7 +45,7 @@ export const FIELDS: IFieldConfig[] = [
Percentage,
Rating,
Slider,
// TODO: color,
Color,
// DATE & TIME
Date_,
DateTime,

View File

@@ -28,13 +28,20 @@ export interface ICustomCellProps extends FormatterProps<any> {
docRef: firebase.firestore.DocumentReference;
disabled: boolean;
}
export interface IBasicCellProps {
value: any;
type: FieldType;
name: string;
}
export interface IPopoverCellProps extends ICustomCellProps {
setShowComplexCell: React.Dispatch<React.SetStateAction<boolean>>;
}
export interface IPopoverBasicCellProps extends IBasicCellProps {
setShowComplexCell: React.Dispatch<React.SetStateAction<boolean>>;
disabled: boolean;
}
export interface ISideDrawerFieldProps {
column: FormatterProps<any>["column"] & { config?: Record<string, any> };
control: Control;