consolidate all field props to IFieldProps

This commit is contained in:
Sidney Alcantara
2020-08-22 16:25:34 +10:00
parent d7bb345f9f
commit 8aef9b112c
23 changed files with 189 additions and 259 deletions

View File

@@ -7,7 +7,7 @@ import _isUndefined from "lodash/isUndefined";
import _reduce from "lodash/reduce";
import { Control, useWatch } from "react-hook-form";
import { Values } from ".";
import { Values } from "./utils";
import { useAppContext } from "contexts/appContext";
import { useFiretableContext, firetableUser } from "contexts/firetableContext";

View File

@@ -1,5 +1,6 @@
import React, { useContext, useState } from "react";
import { Control, Controller, useWatch } from "react-hook-form";
import { Controller, useWatch } from "react-hook-form";
import { IFieldProps } from "../utils";
import {
createStyles,
@@ -40,12 +41,8 @@ const useStyles = makeStyles(theme =>
})
);
export interface IActionProps {
control: Control;
name: string;
docRef: firebase.firestore.DocumentReference;
export interface IActionProps extends IFieldProps {
config: { callableName: string };
editable?: boolean;
}
function Action({ control, name, docRef, editable, config }: IActionProps) {

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import {
makeStyles,
@@ -38,15 +39,16 @@ const useStyles = makeStyles(theme =>
})
);
export interface ICheckboxProps extends MuiSwitchProps {
control: Control;
name: string;
export interface ICheckboxProps
extends IFieldProps,
Omit<MuiSwitchProps, "name"> {
label?: React.ReactNode;
editable?: boolean;
}
export default function Checkbox({
control,
docRef,
label,
name,
editable,

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import AceEditor from "react-ace";
import "ace-builds/src-noconflict/mode-javascript";
@@ -101,12 +102,7 @@ function ControlledCode({ onChange, onBlur, value }: IControlledCodeProps) {
);
}
export interface ICodeProps {
control: Control;
name: string;
}
export default function Code({ control, name, ...props }: ICodeProps) {
export default function Code({ control, docRef, name, ...props }: IFieldProps) {
return (
<Controller
control={control}

View File

@@ -1,5 +1,6 @@
import React, { useState } from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { ChromePicker } from "react-color";
import {
@@ -39,12 +40,7 @@ const useStyles = makeStyles(theme =>
})
);
export interface IColorProps {
control: Control;
name: string;
}
export default function Color({ control, name }: IColorProps) {
export default function Color({ control, name }: IFieldProps) {
const classes = useStyles();
const [showPicker, setShowPicker] = useState(false);

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Control, Controller } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useTheme, Grid, Chip } from "@material-ui/core";
@@ -7,13 +8,13 @@ import ConnectTableSelect, {
IConnectTableSelectProps,
} from "components/ConnectTableSelect";
export interface IConnectTableProps extends Partial<IConnectTableSelectProps> {
control: Control;
name: string;
}
export interface IConnectTableProps
extends IFieldProps,
Partial<IConnectTableSelectProps> {}
export default function ConnectTable({
control,
docRef,
name,
editable,
...props

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useTheme } from "@material-ui/core";
import {
@@ -12,13 +13,12 @@ import { MuiPickersUtilsProvider } from "@material-ui/pickers";
import DateFnsUtils from "@date-io/date-fns";
export interface IDatePickerProps
extends Omit<KeyboardDatePickerProps, "onChange" | "value"> {
control: Control;
name: string;
}
extends IFieldProps,
Omit<KeyboardDatePickerProps, "name" | "onChange" | "value"> {}
export default function DatePicker({
control,
docRef,
name,
...props
}: IDatePickerProps) {

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useTheme } from "@material-ui/core";
import {
@@ -14,13 +15,12 @@ import { MuiPickersUtilsProvider } from "@material-ui/pickers";
import DateFnsUtils from "@date-io/date-fns";
export interface IDateTimePickerProps
extends Omit<KeyboardDateTimePickerProps, "onChange" | "value"> {
control: Control;
name: string;
}
extends IFieldProps,
Omit<KeyboardDateTimePickerProps, "name" | "onChange" | "value"> {}
export default function DateTimePicker({
control,
docRef,
name,
...props
}: IDateTimePickerProps) {

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useState } from "react";
import clsx from "clsx";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useDropzone } from "react-dropzone";
import useUploader, { FileValue } from "hooks/useFiretable/useUploader";
@@ -55,7 +56,7 @@ const useStyles = makeStyles(theme =>
);
export interface IControlledFileUploaderProps
extends Pick<IFileUploaderProps, "editable" | "docRef" | "name"> {
extends Pick<IFieldProps, "editable" | "docRef" | "name"> {
onChange: (...event: any[]) => void;
onBlur: () => void;
value: any;
@@ -173,19 +174,7 @@ export function ControlledFileUploader({
);
}
export interface IFileUploaderProps {
control: Control;
name: string;
editable?: boolean;
docRef?: firebase.firestore.DocumentReference;
}
export default function FileUploader({
control,
name,
...props
}: IFileUploaderProps) {
export default function FileUploader({ control, name, ...props }: IFieldProps) {
return (
<Controller
control={control}

View File

@@ -1,6 +1,7 @@
import React, { useCallback, useState } from "react";
import clsx from "clsx";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useDropzone } from "react-dropzone";
import useUploader from "hooks/useFiretable/useUploader";
@@ -20,7 +21,6 @@ import AddIcon from "@material-ui/icons/AddAPhoto";
import DeleteIcon from "@material-ui/icons/Delete";
import OpenIcon from "@material-ui/icons/OpenInNewOutlined";
import ErrorMessage from "../ErrorMessage";
import Confirmation from "components/Confirmation";
import { IMAGE_MIME_TYPES } from "constants/fields";
@@ -92,7 +92,7 @@ const useStyles = makeStyles(theme =>
);
export interface IControlledImageUploaderProps
extends Pick<IImageUploaderProps, "docRef" | "editable" | "name"> {
extends Pick<IFieldProps, "docRef" | "editable" | "name"> {
onChange: (...event: any[]) => void;
onBlur: () => void;
value: any;
@@ -253,19 +253,11 @@ export function ControlledImageUploader({
</>
);
}
export interface IImageUploaderProps {
control: Control;
name: string;
docRef?: firebase.firestore.DocumentReference;
editable?: boolean;
}
export default function ImageUploader({
control,
name,
...props
}: IImageUploaderProps) {
}: IFieldProps) {
return (
<Controller
control={control}

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Control, Controller } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import ReactJson from "react-json-view";
import { makeStyles, createStyles, useTheme } from "@material-ui/core";
@@ -32,12 +33,7 @@ const isValidJson = (val: any) => {
return true;
};
export interface IJsonEditorProps {
control: Control;
name: string;
}
export default function JsonEditor({ control, name }: IJsonEditorProps) {
export default function JsonEditor({ control, name }: IFieldProps) {
const classes = useStyles();
const theme = useTheme();

View File

@@ -1,25 +1,24 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useTheme, Grid } from "@material-ui/core";
import MultiSelect_, { MultiSelectProps } from "@antlerengineering/multiselect";
import FormattedChip from "components/FormattedChip";
export type IMultiSelectProps = Omit<
MultiSelectProps<string>,
"multiple" | "value" | "onChange" | "options"
> & {
control: Control;
name: string;
config?: { options: string[] };
editable?: boolean;
};
export type IMultiSelectProps = IFieldProps &
Omit<
MultiSelectProps<string>,
"name" | "multiple" | "value" | "onChange" | "options"
> & {
config?: { options: string[] };
};
export default function MultiSelect({
control,
name,
docRef,
editable,
config,
...props

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { makeStyles, createStyles, Typography } from "@material-ui/core";
import { resultColorsScale } from "util/color";
@@ -41,15 +42,7 @@ const useStyles = makeStyles(theme =>
})
);
export interface IPercentageProps {
control: Control;
name: string;
}
/**
* TODO: Fix cell not updating properly when switching between rows
*/
export default function Percentage({ control, name }: IPercentageProps) {
export default function Percentage({ control, name }: IFieldProps) {
const classes = useStyles();
return (

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { makeStyles, createStyles, Grid } from "@material-ui/core";
import { Rating as MuiRating } from "@material-ui/lab";
@@ -25,13 +26,7 @@ const useStyles = makeStyles(theme =>
})
);
export interface IRatingProps {
control: Control;
name: string;
editable?: boolean;
}
export default function Rating({ control, name, editable }: IRatingProps) {
export default function Rating({ control, name, editable }: IFieldProps) {
const classes = useStyles();
return (

View File

@@ -1,14 +1,10 @@
import React from "react";
import { Control, Controller } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import _RichText from "components/RichText";
export interface IRichTextProps {
control: Control;
name: string;
}
export default function RichText({ control, name }: IRichTextProps) {
export default function RichText({ control, name }: IFieldProps) {
return (
<Controller
control={control}

View File

@@ -1,21 +1,19 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import { useTheme } from "@material-ui/core";
import MultiSelect, { MultiSelectProps } from "@antlerengineering/multiselect";
import FormattedChip from "components/FormattedChip";
export type ISingleSelectProps = Omit<
MultiSelectProps<string>,
"multiple" | "value" | "onChange" | "options"
> & {
control: Control;
name: string;
config?: { options: string[] };
editable?: boolean;
};
export type ISingleSelectProps = IFieldProps &
Omit<
MultiSelectProps<string>,
"name" | "multiple" | "value" | "onChange" | "options"
> & {
config?: { options: string[] };
};
/**
* Uses the MultiSelect UI, but writes values as a string,
@@ -23,6 +21,7 @@ export type ISingleSelectProps = Omit<
*/
export default function SingleSelect({
control,
docRef,
name,
editable,
config,

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import {
makeStyles,
@@ -43,9 +44,7 @@ const useStyles = makeStyles(theme =>
})
);
export interface ISliderProps extends SliderProps {
control: Control;
name: string;
export interface ISliderProps extends IFieldProps, Omit<SliderProps, "name"> {
units?: string;
minLabel?: React.ReactNode;
maxLabel?: React.ReactNode;
@@ -53,6 +52,7 @@ export interface ISliderProps extends SliderProps {
export default function Slider({
control,
docRef,
name,
units,
minLabel,

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Control, useWatch } from "react-hook-form";
import { Controller, useWatch } from "react-hook-form";
import { IFieldProps } from "../utils";
import { Link } from "react-router-dom";
import queryString from "query-string";
@@ -31,10 +32,7 @@ const useStyles = makeStyles(theme =>
})
);
export interface ISubTableProps {
control: Control;
name: string;
docRef: firebase.firestore.DocumentReference;
export interface ISubTableProps extends IFieldProps {
config: { parentLabel?: string[] };
label: string;
}

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import {
makeStyles,
@@ -14,13 +15,6 @@ const useStyles = makeStyles(theme =>
})
);
export interface IFieldProps {
control: Control;
name: string;
docRef: firebase.firestore.DocumentReference;
editable?: boolean;
}
export interface ITextProps
extends IFieldProps,
Omit<FilledTextFieldProps, "variant" | "name"> {
@@ -60,7 +54,7 @@ export default function Text({
break;
case "number":
variantProps = { inputmode: "numeric", pattern: "[0-9]*" };
variantProps = { inputMode: "numeric", pattern: "[0-9]*" };
break;
case "short":

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Controller, Control } from "react-hook-form";
import { Controller } from "react-hook-form";
import { IFieldProps } from "../utils";
import {
Grid,
@@ -9,12 +10,11 @@ import {
} from "@material-ui/core";
import LaunchIcon from "@material-ui/icons/Launch";
export interface IUrlProps extends Omit<FilledTextFieldProps, "variant"> {
control: Control;
name: string;
}
export interface IUrlProps
extends IFieldProps,
Omit<FilledTextFieldProps, "name" | "variant"> {}
export default function Url({ control, name, ...props }: IUrlProps) {
export default function Url({ control, name, docRef, ...props }: IUrlProps) {
return (
<Grid container wrap="nowrap">
<Controller

View File

@@ -2,17 +2,14 @@ import React, { lazy } from "react";
import { useForm } from "react-hook-form";
import _isFunction from "lodash/isFunction";
import _isEmpty from "lodash/isEmpty";
// import { useFiretableContext } from "contexts/firetableContext";
import { Grid } from "@material-ui/core";
import { Fields, Values, getInitialValues, Field } from "./utils";
import { FieldType } from "constants/fields";
import Autosave from "./Autosave";
import FieldWrapper from "./FieldWrapper";
import Text from "./Fields/Text";
import { FieldType } from "constants/fields";
const Url = lazy(() =>
import("./Fields/Url" /* webpackChunkName: "SideDrawer-Url" */)
);
@@ -80,60 +77,6 @@ const Action = lazy(() =>
import("./Fields/Action" /* webpackChunkName: "SideDrawer-Action" */)
);
export type Values = { [key: string]: any };
export type Field = {
type?: FieldType;
name: string;
label?: string;
[key: string]: any;
};
export type Fields = (Field | ((values: Values) => Field))[];
const initializeValue = type => {
switch (type) {
case FieldType.singleSelect:
case FieldType.multiSelect:
case FieldType.image:
case FieldType.file:
return [];
case FieldType.date:
case FieldType.dateTime:
return null;
case FieldType.checkbox:
return false;
case FieldType.number:
return 0;
case FieldType.json:
return {};
case FieldType.shortText:
case FieldType.longText:
case FieldType.email:
case FieldType.phone:
case FieldType.url:
case FieldType.code:
case FieldType.richText:
default:
break;
}
};
const getInitialValues = (fields: Fields): Values =>
fields.reduce((acc, _field) => {
const field = _isFunction(_field) ? _field({}) : _field;
if (!field.name) return acc;
let _type = field.type;
if (field.config && field.config.renderFieldType) {
_type = field.config.renderFieldType;
}
const value = initializeValue(_type);
return { ...acc, [field.name]: value };
}, {});
export interface IFormProps {
fields: Fields;
values: Values;
@@ -188,7 +131,7 @@ export default function Form({ fields, values }: IFormProps) {
_type = field.config.renderFieldType;
}
let renderedField: React.ReactNode = null;
let fieldComponent: React.ComponentType<any> | null = null;
switch (_type) {
case FieldType.shortText:
@@ -196,111 +139,79 @@ export default function Form({ fields, values }: IFormProps) {
case FieldType.email:
case FieldType.phone:
case FieldType.number:
renderedField = (
<Text {...fieldProps} control={control} docRef={docRef} />
);
fieldComponent = Text;
break;
case FieldType.url:
renderedField = <Url {...fieldProps} control={control} />;
fieldComponent = Url;
break;
case FieldType.singleSelect:
renderedField = (
<SingleSelect {...fieldProps} control={control} />
);
fieldComponent = SingleSelect;
break;
case FieldType.multiSelect:
renderedField = <MultiSelect {...fieldProps} control={control} />;
fieldComponent = MultiSelect;
break;
case FieldType.date:
renderedField = <DatePicker {...fieldProps} control={control} />;
fieldComponent = DatePicker;
break;
case FieldType.dateTime:
renderedField = (
<DateTimePicker {...fieldProps} control={control} />
);
fieldComponent = DateTimePicker;
break;
case FieldType.checkbox:
renderedField = <Checkbox {...fieldProps} control={control} />;
fieldComponent = Checkbox;
break;
case FieldType.color:
renderedField = <Color {...fieldProps} control={control} />;
fieldComponent = Color;
break;
case FieldType.slider:
renderedField = <Slider {...fieldProps} control={control} />;
fieldComponent = Slider;
break;
case FieldType.richText:
renderedField = <RichText {...fieldProps} control={control} />;
fieldComponent = RichText;
break;
case FieldType.image:
renderedField = (
<ImageUploader
{...fieldProps}
control={control}
docRef={values.ref}
/>
);
fieldComponent = ImageUploader;
break;
case FieldType.file:
renderedField = (
<FileUploader
{...fieldProps}
control={control}
docRef={values.ref}
/>
);
fieldComponent = FileUploader;
break;
case FieldType.rating:
renderedField = <Rating {...fieldProps} control={control} />;
fieldComponent = Rating;
break;
case FieldType.percentage:
renderedField = <Percentage {...fieldProps} control={control} />;
fieldComponent = Percentage;
break;
case FieldType.connectTable:
renderedField = (
<ConnectTable {...fieldProps} control={control} />
);
fieldComponent = ConnectTable;
break;
case FieldType.subTable:
renderedField = (
<SubTable
{...(fieldProps as any)}
control={control}
docRef={docRef}
/>
);
fieldComponent = SubTable;
break;
case FieldType.action:
renderedField = (
<Action
{...(fieldProps as any)}
control={control}
docRef={docRef}
/>
);
fieldComponent = Action;
break;
case FieldType.json:
renderedField = <JsonEditor {...fieldProps} control={control} />;
fieldComponent = JsonEditor;
break;
case FieldType.code:
renderedField = <Code {...fieldProps} control={control} />;
fieldComponent = Code;
break;
case undefined:
@@ -311,6 +222,12 @@ export default function Form({ fields, values }: IFormProps) {
break;
}
// Should not reach this state
if (fieldComponent === null) {
console.error("`fieldComponent` is null");
return null;
}
return (
<FieldWrapper
key={fieldProps.name ?? i}
@@ -318,7 +235,11 @@ export default function Form({ fields, values }: IFormProps) {
name={field.name}
label={field.label}
>
{renderedField}
{React.createElement(fieldComponent, {
...fieldProps,
control,
docRef,
})}
</FieldWrapper>
);
})}

View File

@@ -0,0 +1,65 @@
import { Control } from "react-hook-form";
import _isFunction from "lodash/isFunction";
import { FieldType } from "constants/fields";
export interface IFieldProps {
control: Control;
name: string;
docRef: firebase.firestore.DocumentReference;
editable?: boolean;
}
export type Values = { [key: string]: any };
export type Field = {
type?: FieldType;
name: string;
label?: string;
[key: string]: any;
};
export type Fields = (Field | ((values: Values) => Field))[];
export const initializeValue = type => {
switch (type) {
case FieldType.singleSelect:
case FieldType.multiSelect:
case FieldType.image:
case FieldType.file:
return [];
case FieldType.date:
case FieldType.dateTime:
return null;
case FieldType.checkbox:
return false;
case FieldType.number:
return 0;
case FieldType.json:
return {};
case FieldType.shortText:
case FieldType.longText:
case FieldType.email:
case FieldType.phone:
case FieldType.url:
case FieldType.code:
case FieldType.richText:
default:
break;
}
};
export const getInitialValues = (fields: Fields): Values =>
fields.reduce((acc, _field) => {
const field = _isFunction(_field) ? _field({}) : _field;
if (!field.name) return acc;
let _type = field.type;
if (field.config && field.config.renderFieldType) {
_type = field.config.renderFieldType;
}
const value = initializeValue(_type);
return { ...acc, [field.name]: value };
}, {});

View File

@@ -8,7 +8,8 @@ import ChevronIcon from "@material-ui/icons/KeyboardArrowLeft";
import ChevronUpIcon from "@material-ui/icons/KeyboardArrowUp";
import ChevronDownIcon from "@material-ui/icons/KeyboardArrowDown";
import Form, { Field } from "./Form";
import Form from "./Form";
import { Field } from "./Form/utils";
import ErrorBoundary from "components/ErrorBoundary";
import { useStyles } from "./useStyles";