mirror of
https://github.com/rowyio/rowy.git
synced 2026-07-12 13:28:48 +02:00
add withPopoverCell, Color field
This commit is contained in:
122
www/src/components/Table/withPopoverCell.tsx
Normal file
122
www/src/components/Table/withPopoverCell.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
62
www/src/components/fields/Color/PopoverBasicCell.tsx
Normal file
62
www/src/components/fields/Color/PopoverBasicCell.tsx
Normal 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;
|
||||
12
www/src/components/fields/Color/PopoverCell.tsx
Normal file
12
www/src/components/fields/Color/PopoverCell.tsx
Normal 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} />
|
||||
);
|
||||
}
|
||||
99
www/src/components/fields/Color/SideDrawerField.tsx
Normal file
99
www/src/components/fields/Color/SideDrawerField.tsx
Normal 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>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
30
www/src/components/fields/Color/index.tsx
Normal file
30
www/src/components/fields/Color/index.tsx
Normal 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;
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user