import type { JSONSchemaObject } from "@vertesia/common"; import { Button, FormItem } from "@vertesia/ui/core"; import clsx from "clsx"; import { Plus, Trash2 } from "lucide-react"; import { ComponentType, ReactNode, SyntheticEvent, useState } from "react"; import { FormContext, FormContextProvider, InputComponentProps, useForm } from "./FormContext.js"; import { ManagedListProperty, ManagedObject, ManagedObjectBase, ManagedProperty, Node } from "./ManagedObject.js"; import { Input } from "./inputs.js"; interface FormProps { object: ManagedObject; components?: Record>; children?: ReactNode | ReactNode[]; onSubmit?: (data: JSONSchemaObject) => void; onChange?: (prop: Node) => void; disabled?: boolean; } export function Form({ object, components, onSubmit, children, onChange, disabled }: FormProps) { const _onSubmit = (evt: SyntheticEvent) => { evt.stopPropagation(); evt.preventDefault(); onSubmit && onSubmit(object.value); } object.observer = onChange; return (
{children}
) } function GeneratedFormFields() { const ctx = useForm(); return (
{ ctx.object.properties.map(renderProperty) }
) } export function GeneratedForm({ children, ...props }: FormProps) { return (
{children} ) } function renderProperty(prop: Node) { if (prop.isList) { return } else if (prop.isObject) { return } else { return } } function renderItemProperty(prop: Node, editor?: string) { if (prop.isList) { return } else if (prop.isObject) { return } else { return } } interface ScalarFieldProps { object: ManagedProperty; inline?: boolean; editor?: string; // if present overwrite the object schema editor } export function ScalarField({ object, editor, inline = false }: ScalarFieldProps) { if (!editor) { editor = object.schema.editor; } const { components, disabled } = useForm(); const Component = (editor && components[editor]) || Input; const inputType = object.getInputType(); if (inputType === 'checkbox') { inline = true; } const handleOnChange = (event: any) => { if (disabled) return; if (object.schema.isBoolean) { object.value = event.target.checked; } else if (object.schema.isNumber) { object.value = parseFloat(event.target.value); } else { object.value = event.target.value; } } if (object.isListItem) { // List items don't need the FormItem wrapper (no label, description, etc.) return ; } return ( ) } interface ObjectFormProps { object: ManagedObjectBase; } function CompositeField({ object }: ObjectFormProps) { return (
{!object.isListItem &&
{object.title}
} { object.properties.map(renderProperty) }
) } interface ListFieldProps { object: ManagedListProperty; } function ListField({ object }: ListFieldProps) { const [value, setValue] = useState(object.value || []); const { disabled } = useForm(); const addItem = () => { if (disabled) return; object.add(); setValue([...object.value]); }; const deleteItem = (index: number) => { if (disabled) return; object.remove(index); setValue([...object.value]); }; return (
{!object.isListItem &&
{object.title}
} { object.items.map((item, index) => { return deleteItem(index)} disabled={disabled} />; }) }
) } interface ListItemProps { list: ManagedListProperty; object: Node & { index: number }; onDelete: () => void; disabled?: boolean; } function ListItem({ list, object, onDelete, disabled }: ListItemProps) { return (
{ renderItemProperty(object, list.schema.arraySchema.editor) }
) }