You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ctk/services/frontend/src/components/Record.tsx

131 lines
3.4 KiB
TypeScript

import {
Fragment,
FunctionComponent,
ReactElement,
useCallback,
useMemo
} from "react";
import { IconButton, styled } from "@mui/material";
import { MinusSmIcon } from "@heroicons/react/solid";
import TextField from "./global/FormElements/TextField";
import Toggle from "./global/FormElements/Toggle";
import Records, { IRecordsProps } from "./Records";
export interface IFieldType {
name: string;
placeholder?: string;
required?: boolean;
type: "text" | "toggle" | "records";
options?:
| {
text: string;
value: string;
}[]
| IRecordsProps;
}
export interface IRecordProps {
fields: IFieldType[];
index: number;
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")`
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
column-gap: ${({ theme }) => theme.spacing(1)};
width: 100%;
@media (max-width: 768px) {
column-gap: ${({ theme }) => theme.spacing(1)};
}
@media (max-width: 640px) {
flex-direction: column;
}
`;
const RemoveButton = styled(IconButton)`
border-radius: ${({ theme }) => theme.spacing(2)};
`;
const Record: FunctionComponent<IRecordProps> = (
props: IRecordProps
): ReactElement => {
const { fields, index, onRemove, renderLayout, renderField, renderRemove } =
props;
const handleRemove = useCallback(() => {
onRemove(index);
}, [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 (
<Root>
{renderLayoutWrapper(
fields.map((field) => (
<Fragment key={field.name}>
{renderFieldWrapper(
<>
{field.type === "text" && (
<TextField
id={field.name}
name={field.name}
placeholder={
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) || []}
/>
)}
{field.type === "records" && (
<Records {...(field.options as any)} />
)}
</>,
field
)}
</Fragment>
))
)}
{renderRemoveWrapper(
<div className="flex justify-end content-end">
<RemoveButton size="small" color="warning" onClick={handleRemove}>
<MinusSmIcon className="h-5 w-5" />
</RemoveButton>
</div>
)}
</Root>
);
};
export default Record;