Skip to content
Permalink
d731acd5b2
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
230 lines (203 sloc) 5.61 KB
import {
Checkbox,
CheckboxGroup,
FormControl,
FormErrorMessage,
Heading,
HStack,
Input,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Radio,
RadioGroup,
Select,
Stack,
Textarea,
} from "@chakra-ui/react";
import { type Dispatch, type SetStateAction, useState } from "react";
interface component {
id: string;
max_length?: number;
options?: { default?: boolean; value: string }[];
required: boolean;
title: string;
type: string;
value?: number | string | string[];
}
export default function ({
components,
read_only = true,
}: {
components: { [k: number]: component[] };
read_only: boolean;
}) {
function isNumberElemInvalid(e: HTMLInputElement): boolean {
return !(
e.value ||
e.valueAsNumber <= Number.MAX_SAFE_INTEGER ||
e.valueAsNumber >= Number.MIN_SAFE_INTEGER
);
}
function updateState(
state: { [k: string]: string | string[] },
setState: Dispatch<SetStateAction<{}>>,
id: string,
value: string
) {
const newState = { ...state };
newState[id] = value;
setState(newState);
}
function renderCheckboxOptions(
c: component,
state: { [k: string]: string | string[] },
setState: Dispatch<SetStateAction<{}>>
) {
if (!c.options) throw new Error("Options for checkbox are undefined");
const boxes = [];
const checkedBoxes = [];
for (const option of c.options) {
if (
option.default ||
(read_only && Array.isArray(c.value) && c.value.includes(option.value))
)
checkedBoxes.push(option.value);
boxes.push(
<Checkbox
isReadOnly={read_only}
onChange={(e) => {
const newState = { ...state };
const groupValues = newState[c.id] ?? [];
if (!Array.isArray(groupValues))
throw new Error("Expected CheckboxGroup values to be an array");
e.target.checked
? groupValues.push(e.target.value)
: groupValues.splice(
groupValues.findIndex((v) => v === e.target.value),
1
);
newState[c.id] = groupValues;
setState(newState);
}}
value={option.value}
>
{option.value}
</Checkbox>
);
}
return (
<CheckboxGroup defaultValue={checkedBoxes}>
<HStack spacing={5}>{boxes}</HStack>
</CheckboxGroup>
);
}
function renderRadioElements(
c: component,
state: { [k: string]: string | string[] },
setState: Dispatch<SetStateAction<{}>>
) {
if (!c.options) throw new Error("Options for radio buttons are undefined!");
const buttons = [];
for (const option of c.options) {
buttons.push(
<Radio checked={option.default} value={option.value}>
{option.value}
</Radio>
);
}
return (
<RadioGroup
id={c.id}
onChange={(e) => {
const newState = { ...state };
newState[c.id] = e;
setState(newState);
}}
>
<Stack direction="row">{buttons}</Stack>
</RadioGroup>
);
}
function generateReactComponents(
components: component[],
state: { [k: string]: string | string[] },
setState: Dispatch<SetStateAction<{}>>
): JSX.Element[] {
const fragmentsList = [];
for (const component of components) {
fragmentsList.push(
<Heading size="md">{component.title}</Heading>,
<br />
);
switch (component.type) {
case "checkbox":
fragmentsList.push(renderCheckboxOptions(component, state, setState));
break;
case "input":
fragmentsList.push(
<FormControl
isInvalid={
!(document.getElementById(component.id) as HTMLInputElement)
.value.length
}
isReadOnly={read_only}
>
<Input
id={component.id}
maxLength={component.max_length}
onChange={(e) =>
updateState(state, setState, component.id, e.target.value)
}
placeholder="Your response"
value={component.value}
/>
<FormErrorMessage>Field is required</FormErrorMessage>
</FormControl>
);
break;
case "number":
fragmentsList.push(
<NumberInput
isInvalid={isNumberElemInvalid(
document.getElementById(component.id) as HTMLInputElement
)}
isReadOnly={read_only}
>
<NumberInputField
id={component.id}
onChange={(e) =>
updateState(state, setState, component.id, e.target.value)
}
value={component.value}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
);
break;
case "select":
fragmentsList.push();
}
fragmentsList.push(<br />, <br />, <br />);
}
return fragmentsList;
}
const pages = [];
const [responses, setResponses] = useState({});
for (const [page, componentList] of Object.entries(components)) {
pages.push(
<div
id={`form-page-${page}`}
style={{ display: page ? "none" : undefined }}
>
{generateReactComponents(componentList, responses, setResponses)}
</div>
);
}
return <></>;
}