|
|
|
@ -1,25 +1,38 @@
|
|
|
|
import { Fragment, FunctionComponent, ReactElement, useCallback } from "react";
|
|
|
|
import {
|
|
|
|
|
|
|
|
Fragment,
|
|
|
|
|
|
|
|
FunctionComponent,
|
|
|
|
|
|
|
|
ReactElement,
|
|
|
|
|
|
|
|
useCallback,
|
|
|
|
|
|
|
|
useMemo
|
|
|
|
|
|
|
|
} from "react";
|
|
|
|
import { styled } from "@mui/joy";
|
|
|
|
import { styled } from "@mui/joy";
|
|
|
|
import IconButton from "@mui/joy/IconButton";
|
|
|
|
import IconButton from "@mui/joy/IconButton";
|
|
|
|
import { MinusSmIcon } from "@heroicons/react/solid";
|
|
|
|
import { MinusSmIcon } from "@heroicons/react/solid";
|
|
|
|
import TextField from "./global/FormElements/TextField";
|
|
|
|
import TextField from "./global/FormElements/TextField";
|
|
|
|
import Toggle from "./global/FormElements/Toggle";
|
|
|
|
import Toggle from "./global/FormElements/Toggle";
|
|
|
|
|
|
|
|
import Records, { IRecordsProps } from "./Records";
|
|
|
|
|
|
|
|
|
|
|
|
export interface IFieldType {
|
|
|
|
export interface IFieldType {
|
|
|
|
name: string;
|
|
|
|
name: string;
|
|
|
|
placeholder: string;
|
|
|
|
placeholder?: string;
|
|
|
|
required?: boolean;
|
|
|
|
required?: boolean;
|
|
|
|
type: "text" | "toggle";
|
|
|
|
type: "text" | "toggle" | "records";
|
|
|
|
options?: {
|
|
|
|
options?:
|
|
|
|
|
|
|
|
| {
|
|
|
|
text: string;
|
|
|
|
text: string;
|
|
|
|
value: string;
|
|
|
|
value: string;
|
|
|
|
}[];
|
|
|
|
}[]
|
|
|
|
|
|
|
|
| IRecordsProps;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface IRecordProps {
|
|
|
|
export interface IRecordProps {
|
|
|
|
fields: IFieldType[];
|
|
|
|
fields: IFieldType[];
|
|
|
|
index: number;
|
|
|
|
index: number;
|
|
|
|
onRemove: (index: number) => void;
|
|
|
|
onRemove: (index: number) => void;
|
|
|
|
|
|
|
|
direction?: "column" | "row";
|
|
|
|
|
|
|
|
renderLayout?: (elements: ReactElement[]) => ReactElement;
|
|
|
|
|
|
|
|
renderField?: (element: ReactElement, field: IFieldType) => ReactElement;
|
|
|
|
|
|
|
|
renderRemove?: (element: ReactElement) => ReactElement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Root = styled("div")`
|
|
|
|
const Root = styled("div")`
|
|
|
|
@ -28,6 +41,8 @@ const Root = styled("div")`
|
|
|
|
justify-content: flex-start;
|
|
|
|
justify-content: flex-start;
|
|
|
|
align-items: flex-start;
|
|
|
|
align-items: flex-start;
|
|
|
|
column-gap: ${({ theme }) => theme.spacing(2)};
|
|
|
|
column-gap: ${({ theme }) => theme.spacing(2)};
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
column-gap: ${({ theme }) => theme.spacing(1)};
|
|
|
|
column-gap: ${({ theme }) => theme.spacing(1)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -38,29 +53,65 @@ const RemoveButton = styled(IconButton)``;
|
|
|
|
const Record: FunctionComponent<IRecordProps> = (
|
|
|
|
const Record: FunctionComponent<IRecordProps> = (
|
|
|
|
props: IRecordProps
|
|
|
|
props: IRecordProps
|
|
|
|
): ReactElement => {
|
|
|
|
): ReactElement => {
|
|
|
|
const { fields, index, onRemove } = props;
|
|
|
|
const { fields, index, onRemove, renderLayout, renderField, renderRemove } =
|
|
|
|
|
|
|
|
props;
|
|
|
|
|
|
|
|
|
|
|
|
const handleRemove = useCallback(() => {
|
|
|
|
const handleRemove = useCallback(() => {
|
|
|
|
onRemove(index);
|
|
|
|
onRemove(index);
|
|
|
|
}, [index, onRemove]);
|
|
|
|
}, [index, onRemove]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const renderLayoutWrapper = useMemo(
|
|
|
|
|
|
|
|
() => renderLayout || ((elements: ReactElement[]) => <>{elements}</>),
|
|
|
|
|
|
|
|
[renderLayout]
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const renderFieldWrapper = useMemo(
|
|
|
|
|
|
|
|
() => renderField || ((element: ReactElement) => element),
|
|
|
|
|
|
|
|
[renderField]
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const renderRemoveWrapper = useMemo(
|
|
|
|
|
|
|
|
() => renderRemove || ((element: ReactElement) => element),
|
|
|
|
|
|
|
|
[renderRemove]
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<Root>
|
|
|
|
<Root>
|
|
|
|
{fields.map(({ type, name, placeholder, required, options }) => (
|
|
|
|
{renderLayoutWrapper(
|
|
|
|
<Fragment key={name}>
|
|
|
|
fields.map((field) => (
|
|
|
|
{type === "text" && (
|
|
|
|
<Fragment key={field.name}>
|
|
|
|
|
|
|
|
{renderFieldWrapper(
|
|
|
|
|
|
|
|
<>
|
|
|
|
|
|
|
|
{field.type === "text" && (
|
|
|
|
<TextField
|
|
|
|
<TextField
|
|
|
|
id={name}
|
|
|
|
id={field.name}
|
|
|
|
name={name}
|
|
|
|
name={field.name}
|
|
|
|
placeholder={placeholder + (required ? "*" : "")}
|
|
|
|
placeholder={
|
|
|
|
required={required}
|
|
|
|
field.placeholder && !(field as any).label
|
|
|
|
|
|
|
|
? field.placeholder + (field.required ? "*" : "")
|
|
|
|
|
|
|
|
: ""
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
label={(field as any).label}
|
|
|
|
|
|
|
|
required={field.required}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
{field.type === "toggle" && (
|
|
|
|
|
|
|
|
<Toggle
|
|
|
|
|
|
|
|
name={field.name}
|
|
|
|
|
|
|
|
label={field.placeholder || ""}
|
|
|
|
|
|
|
|
options={(field.options as any) || []}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
{type === "toggle" && (
|
|
|
|
{field.type === "records" && (
|
|
|
|
<Toggle name={name} label={placeholder} options={options || []} />
|
|
|
|
<Records {...(field.options as any)} />
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</>,
|
|
|
|
|
|
|
|
field
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
</Fragment>
|
|
|
|
</Fragment>
|
|
|
|
))}
|
|
|
|
))
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
{renderRemoveWrapper(
|
|
|
|
<RemoveButton
|
|
|
|
<RemoveButton
|
|
|
|
variant="soft"
|
|
|
|
variant="soft"
|
|
|
|
size="sm"
|
|
|
|
size="sm"
|
|
|
|
@ -69,6 +120,7 @@ const Record: FunctionComponent<IRecordProps> = (
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<MinusSmIcon className="h-5 w-5" />
|
|
|
|
<MinusSmIcon className="h-5 w-5" />
|
|
|
|
</RemoveButton>
|
|
|
|
</RemoveButton>
|
|
|
|
|
|
|
|
)}
|
|
|
|
</Root>
|
|
|
|
</Root>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|