import { Input, InputArea, Select, Switch } from "@cloudflare/kumo"; import * as React from "react"; import type { SubFieldDef } from "./types"; interface SubFieldProps { /** * Unique DOM id for this sub-field instance. Required because the same * sub-field key (e.g. "name") may render many times in a `list` widget, * so the id must be composed per-instance by the caller to keep label * and input association correct. */ id: string; def: SubFieldDef; value: unknown; onChange: (value: unknown) => void; } function normalizeSelectItems( options: SubFieldDef["options"], ): Array<{ label: string; value: string }> { if (!options || !Array.isArray(options)) return []; return options.map((opt) => (typeof opt === "string" ? { label: opt, value: opt } : opt)); } /** * Wrap a label with a required asterisk. Kumo's `Field` wrapper marks * non-required fields with "(optional)" but does not display `*` for * required ones, so we add it ourselves to make the requirement obvious. */ function labelWithRequired(label: string, required: boolean | undefined): React.ReactNode { if (!required) return label; return ( <> {label} * ); } /** * Renders a single sub-field input based on its type definition. * Used by object-form and list widgets. */ export function SubField({ id, def, value, onChange }: SubFieldProps) { const fieldId = id; switch (def.type) { case "text": return ( onChange(e.target.value)} /> ); case "url": return ( onChange(e.target.value)} /> ); case "number": { const prefixOrSuffix = def.prefix || def.suffix; const labelId = `${fieldId}-label`; const numberInput = ( { const v = e.target.value; onChange(v === "" ? undefined : Number(v)); }} /> ); if (!prefixOrSuffix) return numberInput; return (
{def.prefix && ( {def.prefix} )} {numberInput} {def.suffix && ( {def.suffix} )}
{def.helpText &&

{def.helpText}

}
); } case "boolean": return ( onChange(checked)} /> ); case "select": { const items = normalizeSelectItems(def.options); return ( onChange(e.target.value || undefined)} /> ); case "color": return (
onChange(e.target.value)} /> onChange(e.target.value)} />
{def.helpText &&

{def.helpText}

}
); default: return ( onChange(e.target.value)} /> ); } }