import type { FieldApi, ReactFormExtendedApi } from "@tanstack/react-form"; import type { JSX } from "react"; import { type Entity, type EntityData, EnumField, type Field, JsonField, JsonSchemaField, RelationField, } from "data"; import { useStore } from "@tanstack/react-store"; import { MediaField } from "media/MediaField"; import { type ComponentProps, Suspense } from "react"; import { JsonEditor } from "ui/components/code/JsonEditor"; import * as Formy from "ui/components/form/Formy"; import { FieldLabel } from "ui/components/form/Formy"; import { type FileState, Media } from "ui/elements"; import { useEvent } from "ui/hooks/use-event"; import { EntityJsonSchemaFormField } from "./fields/EntityJsonSchemaFormField"; import { EntityRelationalFormField } from "./fields/EntityRelationalFormField"; import ErrorBoundary from "ui/components/display/ErrorBoundary"; import { Alert } from "ui/components/display/Alert"; import { bkndModals } from "ui/modals"; import type { PrimaryFieldType } from "core"; // simplify react form types 🤦 export type FormApi = ReactFormExtendedApi; // biome-ignore format: ... export type TFieldApi = FieldApi; type EntityFormProps = { entity: Entity; entityId?: PrimaryFieldType; data?: EntityData; handleSubmit: (e: React.FormEvent) => void; fieldsDisabled: boolean; Form: FormApi; className?: string; action: "create" | "update"; }; export function EntityForm({ entity, entityId, handleSubmit, fieldsDisabled, Form, data, className, action, }: EntityFormProps) { const fields = entity.getFillableFields(action, true); return (
{ //console.log("state", state); return [state.canSubmit, state.isValid, state.errors]; }} children={([canSubmit, isValid, errors]) => { //console.log("form:state", { canSubmit, isValid, errors }); return ( !isValid && (

Form is invalid.

{Array.isArray(errors) && (
    {errors.map((error, key) => (
  • {error}
  • ))}
)}
) ); }} />
{fields.map((field, key) => { // @todo: tanstack form re-uses the state, causes issues navigating between entities w/ same fields // media field needs to render outside of the form // as its value is not stored in the form state if (field instanceof MediaField) { return ( ); } if (!field.isFillable(action)) { return; } const _key = `${entity.name}-${field.name}-${key}`; return ( Field error: {field.name} } > ( )} /> ); })}
); } type EntityFormFieldProps< T extends keyof JSX.IntrinsicElements = "input", F extends Field = Field, > = ComponentProps & { fieldApi: TFieldApi; field: F; action: "create" | "update"; data?: EntityData; }; type FormInputElement = HTMLInputElement | HTMLTextAreaElement; function EntityFormField({ fieldApi, field, action, data, ...props }: EntityFormFieldProps) { const handleUpdate = useEvent((e: React.ChangeEvent | any) => { if (typeof e === "object" && "target" in e) { //console.log("handleUpdate", e.target.value); fieldApi.handleChange(e.target.value); } else { //console.log("handleUpdate-", e); fieldApi.handleChange(e); } }); //const required = field.isRequired(); //const customFieldProps = { ...props, action, required }; if (field instanceof RelationField) { return ( ); } if (field instanceof JsonField) { return ; } if (field instanceof JsonSchemaField) { return ( ); } if (field instanceof EnumField) { return ; } const fieldElement = field.getHtmlConfig().element; const fieldProps = field.getHtmlConfig().props as any; const Element = Formy.formElementFactory(fieldElement ?? "input", fieldProps); return ( ); } function EntityMediaFormField({ formApi, field, entity, entityId, disabled, }: { formApi: FormApi; field: MediaField; entity: Entity; entityId?: PrimaryFieldType; disabled?: boolean; }) { if (!entityId) return; const value = useStore(formApi.store, (state) => { const val = state.values[field.name]; if (!val || typeof val === "undefined") return []; if (Array.isArray(val)) return val; return [val]; }); const key = JSON.stringify([entity, entityId, field.name, value.length]); const onClick = (file: FileState) => { bkndModals.open(bkndModals.ids.mediaInfo, { file, }); }; return ( ); } function EntityJsonFormField({ fieldApi, field, ...props }: { fieldApi: TFieldApi; field: JsonField }) { const handleUpdate = useEvent((value: any) => { fieldApi.handleChange(value); }); return ( {field.getLabel()} {/**/} ); } function EntityEnumFormField({ fieldApi, field, ...props }: { fieldApi: TFieldApi; field: EnumField }) { const handleUpdate = useEvent((e: React.ChangeEvent) => { fieldApi.handleChange(e.target.value); }); return ( {field.getLabel()} {!field.isRequired() && } {field.getOptions().map((option) => ( ))} ); }