import { Button } from "@cloudflare/kumo"; import { CaretRight } from "@phosphor-icons/react"; import * as React from "react"; import { SubField } from "../shared/sub-field"; import type { FieldWidgetProps, SubFieldDef } from "../shared/types"; import { normalizeObject } from "../shared/utils"; /** * Object form widget — renders a group of typed sub-fields that store as a * single JSON object. * * Seed usage: * { * "slug": "nutrition", * "type": "json", * "widget": "field-kit:object-form", * "options": { * "fields": [ * { "key": "calories", "label": "Calories", "type": "number", "suffix": "kcal" }, * { "key": "protein", "label": "Protein", "type": "number", "suffix": "g" } * ] * } * } * * Stored value: { "calories": 250, "protein": 12.5 } */ export function ObjectForm({ value, onChange, label, id, required, options, minimal, }: FieldWidgetProps) { const fields = (options?.fields as SubFieldDef[] | undefined) ?? []; const collapsed = options?.collapsed as boolean | undefined; const helpText = options?.helpText as string | undefined; const [isOpen, setIsOpen] = React.useState(!collapsed); const data = normalizeObject(value, fields); const dataRef = React.useRef(data); dataRef.current = data; const handleFieldChange = React.useCallback( (key: string, fieldValue: unknown) => { onChange({ ...dataRef.current, [key]: fieldValue }); }, [onChange], ); if (fields.length === 0) { return (
{!minimal && ( )}

Widget misconfigured

The field's options.fields array is empty or missing. Define sub-fields in your seed file to use this widget.

); } return (
{!minimal && ( )} {isOpen && (
{fields.map((field) => ( handleFieldChange(field.key, v)} /> ))}
)} {helpText &&

{helpText}

}
); }