upgrade hook form resolvers

This commit is contained in:
dswbx
2025-03-11 10:17:07 +01:00
parent 0922d90aff
commit d90f14c707
5 changed files with 6 additions and 302 deletions

View File

@@ -70,7 +70,7 @@ export function StepCreate() {
name: "",
trigger: "manual",
mode: "async",
},
} as Static<typeof schema>,
mode: "onSubmit",
});

View File

@@ -16,7 +16,6 @@ import ModalTest from "../../routes/test/tests/modal-test";
import QueryJsonFormTest from "../../routes/test/tests/query-jsonform";
import DropdownTest from "./tests/dropdown-test";
import DropzoneElementTest from "./tests/dropzone-element-test";
import EntityFieldsForm from "./tests/entity-fields-form";
import FlowsTest from "./tests/flows-test";
import JsonSchemaForm3 from "./tests/json-schema-form3";
import JsonFormTest from "./tests/jsonform-test";
@@ -42,7 +41,6 @@ const tests = {
SqlAiTest,
SortableTest,
ReactHookErrors,
EntityFieldsForm,
FlowsTest,
AppShellAccordionsTest,
SwaggerTest,

View File

@@ -1,296 +0,0 @@
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 (
<div className="flex flex-col gap-1 p-8">
{/*{fields.map((field, index) => (
<EntityField
key={field.id}
field={field}
index={index}
form={{ watch, register, setValue, getValues, control }}
defaultType={defaultType}
remove={remove}
/>
))}*/}
{fields.map((field, index) => (
<EntityFieldForm key={field.id} value={field} index={index} update={update} />
))}
<Button className="justify-center" onClick={handleAppend}>
Add Field
</Button>
<div>
<pre>{JSON.stringify(watch(), null, 2)}</pre>
</div>
</div>
);
}
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 (
<form>
<MantineSelect
control={control}
name="type"
data={[...Object.keys(fieldSchemas), "test"]}
/>
<TextInput
label="Name"
placeholder="name"
classNames={{ root: "w-full" }}
{...register("name")}
error={errors.name?.message as any}
/>
</form>
);
}
function EntityFieldController({
name,
control,
defaultValue,
rules,
shouldUnregister,
}: UseControllerProps & {
index: number;
}) {
const {
field: { value, onChange: fieldOnChange, ...field },
fieldState,
} = useController({
name,
control,
defaultValue,
rules,
shouldUnregister,
});
return <div>field</div>;
}
function EntityField({
field,
index,
form: { watch, register, setValue, getValues, control },
remove,
defaultType,
}: {
field: FieldArrayWithId;
index: number;
form: Pick<UseFormReturn<any>, "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 (
<div key={field.id} className="flex flex-col border border-muted rounded">
<div className="flex flex-row gap-2 px-2 pt-1 pb-2">
<div className="flex items-center">
<IconButton Icon={TbGripVertical} className="mt-1" />
</div>
<div className="flex flex-row flex-grow gap-1">
<div>
<Select
label="Type"
data={[...Object.keys(fieldSchemas), "test"]}
defaultValue={defaultType}
onBlur={selectFieldRegister.onBlur}
onChange={(value) => {
setValue(`${prefix}.type`, value as any);
setValue(`${prefix}.config`, {} as any);
}}
/>
</div>
<TextInput
label="Name"
placeholder="name"
classNames={{ root: "w-full" }}
{...register(`fields.${index}.name`)}
/>
</div>
<div className="flex items-end ">
<div className="flex flex-row gap-1">
<Tooltip label="Specify a property name to see options." disabled={enabled}>
<Button
IconRight={opened ? TbChevronUp : TbChevronDown}
onClick={handlers.toggle}
variant={opened ? "default" : "ghost"}
disabled={!enabled}
>
Options
</Button>
</Tooltip>
<IconButton Icon={TbTrash} onClick={handleDelete(index)} />
</div>
</div>
</div>
{/*{enabled && opened && (
<div className="flex flex-col border-t border-t-muted px-3 py-2">
<Tabs defaultValue="general">
<Tabs.List>
<Tabs.Tab value="general">General</Tabs.Tab>
<Tabs.Tab value="specific">Specific</Tabs.Tab>
<Tabs.Tab value="visibility">Visiblity</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="general">
<div className="flex flex-col gap-2 pt-3 pb-1" key={`${prefix}_${type}`}>
<Switch
label="Required"
{...register(`${prefix}.config.required` as any)}
/>
<TextInput
label="Label"
placeholder="Label"
{...register(`${prefix}.config.label` as any)}
/>
<Textarea
label="Description"
placeholder="Description"
{...register(`${prefix}.config.description` as any)}
/>
<Switch label="Virtual" {...register(`${prefix}.config.virtual` as any)} />
</div>
</Tabs.Panel>
<Tabs.Panel value="specific">
<div className="flex flex-col gap-2 pt-3 pb-1">
<JsonSchemaForm
key={type}
schema={specificFieldSchema(type as any)}
uiSchema={dataFieldsUiSchema.config}
className="legacy hide-required-mark fieldset-alternative mute-root"
onChange={(value) => {
setValue(`${prefix}.config`, {
...getValues([`fields.${index}.config`])[0],
...value
});
}}
/>
</div>
</Tabs.Panel>
</Tabs>
</div>
)}*/}
</div>
);
}