import { typeboxResolver } from "@hookform/resolvers/typebox"; import { Select, Switch, Tabs, TextInput, Textarea, Tooltip } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; import { Type } from "@sinclair/typebox"; import { StringEnum, StringIdentifier, transformObject } from "core/utils"; import { FieldClassMap } from "data"; import { omit } from "lodash-es"; import { type FieldArrayWithId, type FieldValues, type UseControllerProps, type UseFormReturn, useController, useFieldArray, useForm, } from "react-hook-form"; import { TbChevronDown, TbChevronUp, TbGripVertical, TbTrash } from "react-icons/tb"; import { Button } from "../../../components/buttons/Button"; import { IconButton } from "../../../components/buttons/IconButton"; import { MantineSelect } from "../../../components/form/hook-form-mantine/MantineSelect"; const fieldSchemas = transformObject(omit(FieldClassMap, ["primary"]), (value) => value.schema); const fieldSchema = Type.Union( Object.entries(fieldSchemas).map(([type, schema]) => Type.Object( { type: Type.Const(type), name: StringIdentifier, config: Type.Optional(schema), }, { additionalProperties: false, }, ), ), ); const schema = Type.Object({ fields: Type.Array(fieldSchema), }); const fieldSchema2 = Type.Object({ type: StringEnum(Object.keys(fieldSchemas)), name: StringIdentifier, }); function specificFieldSchema(type: keyof typeof fieldSchemas) { return Type.Omit(fieldSchemas[type], [ "label", "description", "required", "fillable", "hidden", "virtual", ]); } export default function EntityFieldsForm() { const { control, formState: { isValid, errors }, getValues, handleSubmit, watch, register, setValue, } = useForm({ mode: "onTouched", resolver: typeboxResolver(schema), defaultValues: { fields: [{ type: "text", name: "", config: {} }], sort: { by: "-1", dir: "asc" }, }, }); const defaultType = Object.keys(fieldSchemas)[0]; const { fields, append, prepend, remove, swap, move, insert, update } = useFieldArray({ control, // control props comes from useForm (optional: if you are using FormProvider) name: "fields", // unique name for your Field Array }); function handleAppend() { append({ type: "text", name: "", config: {} }); } return (
{/*{fields.map((field, index) => ( ))}*/} {fields.map((field, index) => ( ))}
{JSON.stringify(watch(), null, 2)}
); } function EntityFieldForm({ update, index, value }) { const { register, handleSubmit, control, formState: { errors }, } = useForm({ mode: "onBlur", resolver: typeboxResolver( Type.Object({ type: StringEnum(Object.keys(fieldSchemas)), name: Type.String({ minLength: 1, maxLength: 3 }), }), ), defaultValues: value, }); function handleUpdate({ id, ...data }) { console.log("data", data); update(index, data); } return (
); } function EntityFieldController({ name, control, defaultValue, rules, shouldUnregister, }: UseControllerProps & { index: number; }) { const { field: { value, onChange: fieldOnChange, ...field }, fieldState, } = useController({ name, control, defaultValue, rules, shouldUnregister, }); return
field
; } function EntityField({ field, index, form: { watch, register, setValue, getValues, control }, remove, defaultType, }: { field: FieldArrayWithId; index: number; form: Pick, "watch" | "register" | "setValue" | "getValues" | "control">; remove: (index: number) => void; defaultType: string; }) { const [opened, handlers] = useDisclosure(false); const prefix = `fields.${index}` as const; const name = watch(`${prefix}.name`); const enabled = name?.length > 0; const type = watch(`${prefix}.type`); //const config = watch(`${prefix}.config`); const selectFieldRegister = register(`${prefix}.type`); //console.log("type", type, specificFieldSchema(type as any)); function handleDelete(index: number) { return () => { if (name.length === 0) { remove(index); return; } window.confirm(`Sure to delete "${name}"?`) && remove(index); }; } return (