diff --git a/packages/conform-dom/form.ts b/packages/conform-dom/form.ts index e8df30b5..ec05dcb9 100644 --- a/packages/conform-dom/form.ts +++ b/packages/conform-dom/form.ts @@ -8,6 +8,7 @@ import { isPrefix, setValue, normalize, + formatName, } from './formdata'; import { type FieldElement, @@ -289,7 +290,7 @@ function getDefaultKey( >((result, [key, value]) => { if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { - result[formatPaths([...getPaths(key), i])] = generateId(); + result[formatName(key, i)] = generateId(); } } @@ -327,7 +328,8 @@ function handleIntent( break; } case 'update': { - const { name, validated, value } = intent.payload; + const { validated, value } = intent.payload; + const name = formatName(intent.payload.name, intent.payload.index); if (typeof value !== 'undefined') { updateValue(meta, name ?? '', serialize(value)); @@ -362,7 +364,7 @@ function handleIntent( break; } case 'reset': { - const name = intent.payload.name ?? ''; + const name = formatName(intent.payload.name, intent.payload.index); const value = getValue(meta.defaultValue, name); updateValue(meta, name, value); diff --git a/packages/conform-dom/formdata.ts b/packages/conform-dom/formdata.ts index 58860ec6..7492927d 100644 --- a/packages/conform-dom/formdata.ts +++ b/packages/conform-dom/formdata.ts @@ -30,7 +30,7 @@ export function getFormData( * const paths = getPaths('todos[0].content'); // ['todos', 0, 'content'] * ``` */ -export function getPaths(name: string): Array { +export function getPaths(name: string | undefined): Array { if (!name) { return []; } @@ -72,6 +72,15 @@ export function formatPaths(paths: Array): string { }, ''); } +/** + * Format based on a prefix and a path + */ +export function formatName(prefix: string | undefined, path?: string | number) { + return typeof path !== 'undefined' + ? formatPaths([...getPaths(prefix), path]) + : prefix ?? ''; +} + /** * Check if a name match the prefix paths */ diff --git a/packages/conform-dom/submission.ts b/packages/conform-dom/submission.ts index cba73ab7..c1e2b594 100644 --- a/packages/conform-dom/submission.ts +++ b/packages/conform-dom/submission.ts @@ -6,6 +6,7 @@ import { setValue, isPrefix, getValue, + formatName, } from './formdata'; import { invariant } from './util'; @@ -164,7 +165,7 @@ export function parse( if (intent) { switch (intent.type) { case 'update': { - const { name } = intent.payload; + const name = formatName(intent.payload.name, intent.payload.index); const value = serialize(intent.payload.value); if (typeof value !== 'undefined') { @@ -178,7 +179,7 @@ export function parse( break; } case 'reset': { - const { name } = intent.payload; + const name = formatName(intent.payload.name, intent.payload.index); if (name) { setValue(context.payload, name, () => undefined); @@ -308,18 +309,34 @@ export type ValidateIntent = { export type ResetIntent = { type: 'reset'; - payload: { - name?: FieldName; - }; + payload: + | { + name?: FieldName; + index?: never; + } + | { + name: FieldName; + index: Schema extends Array ? number : never; + }; }; export type UpdateIntent = { type: 'update'; - payload: { - name?: FieldName; - value?: NonNullable>; - validated?: boolean; - }; + payload: + | { + name?: FieldName; + index?: never; + value?: NonNullable>; + validated?: boolean; + } + | { + name: FieldName; + index: Schema extends Array ? number : never; + value?: NonNullable< + DefaultValue ? Item : unknown> + >; + validated?: boolean; + }; }; export type RemoveIntent = any> = { diff --git a/playground/app/routes/simple-list.tsx b/playground/app/routes/simple-list.tsx index d01cc745..3d2eedeb 100644 --- a/playground/app/routes/simple-list.tsx +++ b/playground/app/routes/simple-list.tsx @@ -89,7 +89,8 @@ export default function SimpleList() {