import { Checkbox, Input, Select } from "@cloudflare/kumo"; import * as React from "react"; import type { FieldWidgetProps, GridAxisDef } from "../shared/types"; import { normalizeGrid } from "../shared/utils"; type CellType = "toggle" | "text" | "number" | "select"; interface SelectOption { label: string; value: string; } /** * Grid widget — a two-dimensional matrix of rows × columns with configurable * cell types. Stores as a nested JSON object. * * Seed usage: * { * "slug": "availability", * "type": "json", * "widget": "field-kit:grid", * "options": { * "rows": [ * { "key": "mon", "label": "Monday" }, * { "key": "tue", "label": "Tuesday" } * ], * "columns": [ * { "key": "morning", "label": "Morning" }, * { "key": "afternoon", "label": "Afternoon" } * ], * "cell": "toggle" * } * } * * Stored value: { "mon": { "morning": true, "afternoon": false }, ... } */ export function Grid({ value, onChange, label, required, options, minimal }: FieldWidgetProps) { const rows = (options?.rows as GridAxisDef[] | undefined) ?? []; const columns = (options?.columns as GridAxisDef[] | undefined) ?? []; const cellType = ((options?.cell as string | undefined) ?? "toggle") as CellType; const cellOptions = (options?.cellOptions as SelectOption[] | string[] | undefined) ?? []; const helpText = options?.helpText as string | undefined; const data = normalizeGrid(value, rows, columns); const dataRef = React.useRef(data); dataRef.current = data; const normalizedCellOptions: SelectOption[] = React.useMemo( () => cellOptions.map((opt) => (typeof opt === "string" ? { label: opt, value: opt } : opt)), [cellOptions], ); const updateCell = React.useCallback( (rowKey: string, colKey: string, cellValue: unknown) => { const rowData = { ...dataRef.current[rowKey], [colKey]: cellValue }; onChange({ ...dataRef.current, [rowKey]: rowData }); }, [onChange], ); const toggleCell = React.useCallback( (rowKey: string, colKey: string, next: boolean) => { const rowData = { ...dataRef.current[rowKey], [colKey]: next }; onChange({ ...dataRef.current, [rowKey]: rowData }); }, [onChange], ); if (rows.length === 0 || columns.length === 0) { return (
Widget misconfigured
The field's options.rows and options.columns arrays are
required. Define them in your seed file to use this widget.
| {columns.map((col) => ( |
{col.image && (
|
))}
|---|---|
|
{row.image && (
|
{columns.map((col) => {
const cellValue = data[row.key]?.[col.key];
return (
|
);
})}
{helpText}
}