diff --git a/packages/conform-dom/README.md b/packages/conform-dom/README.md index cea61b57..04bf94ae 100644 --- a/packages/conform-dom/README.md +++ b/packages/conform-dom/README.md @@ -4,7 +4,7 @@ ## API Reference -- [createControlButton](#createControlButton) +- [getControlButtonProps](#getControlButtonProps) - [getFieldProps](#getFieldProps) - [getFieldElements](#getFieldElements) - [getName](#getName) @@ -17,7 +17,7 @@ - [setFieldState](#setFieldState) - [transform](#transform) -### createControlButton +### getControlButtonProps ### getFieldProps diff --git a/packages/conform-dom/index.ts b/packages/conform-dom/index.ts index 60f93c27..f9367281 100644 --- a/packages/conform-dom/index.ts +++ b/packages/conform-dom/index.ts @@ -72,6 +72,21 @@ export type Submission> = form: FormState; }; +export interface ControlButtonProps { + type: 'submit'; + name: string; + value: string; + formNoValidate: boolean; +} + +export interface ControlAction { + prepend: { defaultValue: T }; + append: { defaultValue: T }; + replace: { defaultValue: T; index: number }; + remove: { index: number }; + reorder: { from: number; to: number }; +} + export function isFieldsetElement( element: unknown, ): element is FieldsetElement { @@ -239,24 +254,54 @@ export function transform( return result; } -export function createControlButton( - name: string, - action: 'prepend' | 'append' | 'remove', - data: any, -): { - type: 'submit'; - name: string; - value: string; - formNoValidate: boolean; -} { +export function getControlButtonProps< + Action extends keyof ControlAction, + Payload extends ControlAction[Action], +>(name: string, action: Action, payload: Payload): ControlButtonProps { return { type: 'submit', name: '__conform__', - value: [name, action, JSON.stringify(data)].join('::'), + value: [name, action, JSON.stringify(payload)].join('::'), formNoValidate: true, }; } +export function applyControlCommand< + Type, + Action extends keyof ControlAction, + Payload extends ControlAction[Action], +>(list: Array, action: Action | string, payload: Payload): Array { + switch (action) { + case 'prepend': { + const { defaultValue } = payload as ControlAction['prepend']; + list.unshift(defaultValue); + break; + } + case 'append': { + const { defaultValue } = payload as ControlAction['append']; + list.push(defaultValue); + break; + } + case 'replace': { + const { defaultValue, index } = payload as ControlAction['replace']; + list.splice(index, 1, defaultValue); + break; + } + case 'remove': + const { index } = payload as ControlAction['remove']; + list.splice(index, 1); + break; + case 'reorder': + const { from, to } = payload as ControlAction['reorder']; + list.splice(to, 0, ...list.splice(from, 1)); + break; + default: + throw new Error('Invalid action found'); + } + + return list; +} + export function parse( payload: FormData | URLSearchParams, ): Submission> { @@ -292,29 +337,7 @@ export function parse( throw new Error(''); } - switch (action) { - case 'prepend': { - const defaultValue = JSON.parse(json); - - list.unshift(defaultValue); - break; - } - case 'append': { - const defaultValue = JSON.parse(json); - - list.push(defaultValue); - break; - } - case 'remove': - const { index } = JSON.parse(json); - - list.splice(index, 1); - break; - default: - throw new Error( - 'Invalid action found; Only `prepend`, `append` and `remove` is accepted', - ); - } + applyControlCommand(list, action, JSON.parse(json)); } catch (e) { return { state: 'rejected', diff --git a/packages/conform-react/README.md b/packages/conform-react/README.md index ea631d86..729f76bb 100644 --- a/packages/conform-react/README.md +++ b/packages/conform-react/README.md @@ -121,6 +121,13 @@ const schema = /* function BookFieldset() { const [ fieldsetProps, + /** + * The variables `name` and `isbn` are FieldProps objects + * They are used to configure the field (input, select, textarea) + * + * Please check the docs of the `conform` helpers for how to + * use them together + */ { name, isbn, @@ -153,50 +160,43 @@ function BookFieldset() { }, }); - /** - * The variable `isbn` is a FieldProps object - * It is used to configure the field (input, select, textarea) - * - * Please check the docs of the `conform` helpers for how to - * use them together - */ - console.log(isbn); - - /** - * This would be `book.isbn` instead of `isbn` - * if the `name` option is provided - */ - console.log(isbn.name); + const { + /** + * This would be `book.isbn` instead of `isbn` + * if the `name` option is provided + */ + name, - /** - * This would be `random-form-id` - * because of the `form` option provided - */ - console.log(isbn.form); + /** + * This would be `random-form-id` + * because of the `form` option provided + */ + form, - /** - * This would be `0340013818` if specified - * on the `initalValue` option - */ - console.log(isbn.defaultValue); + /** + * This would be `0340013818` if specified + * on the `initalValue` option + */ + defaultValue, - /** - * Current error message - * This would be 'Invalid ISBN' initially if specified - * on the `error` option - */ - console.log(isbn.error); + /** + * Current error message + * This would be 'Invalid ISBN' initially if specified + * on the `error` option + */ + error, - /** - * Constraint of the field (required, minLength etc) - * - * For example, the constraint of the isbn field could be: - * { - * required: true, - * pattern: '[0-9]{10,13}' - * } - */ - console.log(isbn.constraint); + /** + * Constraint of the field (required, minLength etc) + * + * For example, the constraint of the isbn field could be: + * { + * required: true, + * pattern: '[0-9]{10,13}' + * } + */ + ...constraint, + } = isbn; return (
@@ -312,10 +312,10 @@ function CollectionForm() { return (
- {bookList.map(({ key, config }, index) => ( -
- {/* `config` is a FieldProps object similar to `books` */} - + {bookList.map((book, index) => ( +
+ {/* `book.props` is a FieldProps object similar to `books` */} + {/* To setup a delete button */} @@ -353,14 +353,20 @@ function BookFieldset({ name, form, defaultValue, error }) { What can I do with `controls`? ```tsx -// To append a new row -; +// To append a new row with optional defaultValue +; -// To prepend a new row -; +// To prepend a new row with optional defaultValue +; // To remove a row by index ; + +// To replace a row with another defaultValue +; + +// To reorder a particular row to an another index +; ``` @@ -445,7 +451,7 @@ function RandomForm() { max={cateogry.max} multiple={cateogry.multiple} pattern={cateogry.pattern} - >2 + >