@conform-to/dom 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  export declare type Primitive = null | undefined | string | number | boolean | Date;
2
2
  export declare type FieldElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement;
3
3
  export interface FieldConfig<Schema = unknown> extends FieldConstraint<Schema> {
4
+ id?: string;
4
5
  name: string;
5
6
  defaultValue?: FieldValue<Schema>;
6
7
  initialError?: Array<[string, string]>;
7
8
  form?: string;
9
+ errorId?: string;
8
10
  }
9
- export declare type FieldValue<Schema> = Schema extends Primitive | File ? string : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
11
+ export declare type FieldValue<Schema> = Schema extends Primitive ? string : Schema extends File ? File : Schema extends Array<infer InnerType> ? Array<FieldValue<InnerType>> : Schema extends Record<string, any> ? {
10
12
  [Key in keyof Schema]?: FieldValue<Schema[Key]>;
11
13
  } : unknown;
12
14
  export declare type FieldConstraint<Schema = any> = {
@@ -28,25 +30,40 @@ export declare type Submission<Schema = unknown> = {
28
30
  value: FieldValue<Schema>;
29
31
  error: Array<[string, string]>;
30
32
  };
33
+ export interface CommandButtonProps<Name extends string = string> {
34
+ name: `conform/${Name}`;
35
+ value: string;
36
+ formNoValidate?: boolean;
37
+ }
31
38
  export declare function isFieldElement(element: unknown): element is FieldElement;
32
39
  export declare function getFormElements(form: HTMLFormElement): FieldElement[];
33
40
  export declare function getPaths(name: string): Array<string | number>;
34
41
  export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
35
42
  export declare function getName(paths: Array<string | number>): string;
36
- export declare function shouldValidate(submission: Submission, name?: string): boolean;
43
+ export declare function shouldValidate(submission: Submission, name: string): boolean;
37
44
  export declare function hasError(error: Array<[string, string]>, name?: string): boolean;
38
- export declare function setFormError(form: HTMLFormElement, submission: Submission): void;
45
+ export declare function reportSubmission(form: HTMLFormElement, submission: Submission): void;
39
46
  export declare function setValue<T>(target: any, paths: Array<string | number>, valueFn: (prev?: T) => T): void;
47
+ /**
48
+ * The ponyfill of `HTMLFormElement.requestSubmit()`
49
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
50
+ * @see https://caniuse.com/?search=requestSubmit
51
+ */
40
52
  export declare function requestSubmit(form: HTMLFormElement, submitter?: HTMLButtonElement | HTMLInputElement): void;
41
- export declare function requestValidate(form: HTMLFormElement, field?: string): void;
53
+ /**
54
+ * Creates a command button on demand and trigger a form submit by clicking it.
55
+ */
56
+ export declare function requestCommand(form: HTMLFormElement | undefined, buttonProps: CommandButtonProps): void;
57
+ /**
58
+ * Returns the properties required to configure a command button for validation
59
+ *
60
+ * @see https://conform.guide/api/react#validate
61
+ */
62
+ export declare function validate(field?: string): CommandButtonProps<'validate'>;
42
63
  export declare function getFormElement(element: HTMLFormElement | HTMLFieldSetElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement | null): HTMLFormElement | null;
43
- export declare function focusFirstInvalidField(form: HTMLFormElement): void;
64
+ export declare function focus(field: FieldElement): void;
44
65
  export declare function getSubmissionType(name: string): string | null;
45
66
  export declare function parse<Schema extends Record<string, any>>(payload: FormData | URLSearchParams): Submission<Schema>;
46
- export declare type Command = {
47
- name: string;
48
- value: string;
49
- };
50
67
  export declare type ListCommand<Schema = unknown> = {
51
68
  type: 'prepend';
52
69
  scope: string;
@@ -83,3 +100,28 @@ export declare type ListCommand<Schema = unknown> = {
83
100
  export declare function parseListCommand<Schema = unknown>(data: string): ListCommand<Schema>;
84
101
  export declare function updateList<Schema>(list: Array<Schema>, command: ListCommand<Schema>): Array<Schema>;
85
102
  export declare function handleList<Schema>(submission: Submission<Schema>): Submission<Schema>;
103
+ export interface ListCommandButtonBuilder {
104
+ append<Schema>(name: string, payload?: {
105
+ defaultValue: Schema;
106
+ }): CommandButtonProps<'list'>;
107
+ prepend<Schema>(name: string, payload?: {
108
+ defaultValue: Schema;
109
+ }): CommandButtonProps<'list'>;
110
+ replace<Schema>(name: string, payload: {
111
+ defaultValue: Schema;
112
+ index: number;
113
+ }): CommandButtonProps<'list'>;
114
+ remove(name: string, payload: {
115
+ index: number;
116
+ }): CommandButtonProps<'list'>;
117
+ reorder(name: string, payload: {
118
+ from: number;
119
+ to: number;
120
+ }): CommandButtonProps<'list'>;
121
+ }
122
+ /**
123
+ * Helpers to configure a command button for modifying a list
124
+ *
125
+ * @see https://conform.guide/api/react#list
126
+ */
127
+ export declare const list: ListCommandButtonBuilder;
package/index.js CHANGED
@@ -45,7 +45,7 @@ function getName(paths) {
45
45
  }, '');
46
46
  }
47
47
  function shouldValidate(submission, name) {
48
- return submission.type === 'submit' || submission.type === 'validate' && (typeof name === 'undefined' || submission.intent === name);
48
+ return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
49
49
  }
50
50
  function hasError(error, name) {
51
51
  return typeof error.find(_ref => {
@@ -53,13 +53,52 @@ function hasError(error, name) {
53
53
  return (typeof name === 'undefined' || name === fieldName) && message !== '';
54
54
  }) !== 'undefined';
55
55
  }
56
- function setFormError(form, submission) {
57
- var firstErrorByName = Object.fromEntries([...submission.error].reverse());
56
+ function reportSubmission(form, submission) {
57
+ var messageByName = new Map();
58
+ for (var [_name, message] of submission.error) {
59
+ if (!messageByName.has(_name)) {
60
+ // Only keep the first error message (for now)
61
+ messageByName.set(_name, message);
62
+
63
+ // We can't use empty string as button name
64
+ // As `form.element.namedItem('')` will always returns null
65
+ var elementName = _name ? _name : '__form__';
66
+ var item = form.elements.namedItem(elementName);
67
+ if (item instanceof RadioNodeList) {
68
+ for (var field of item) {
69
+ if (field.type !== 'radio') {
70
+ throw new Error('Repeated field name is not supported');
71
+ }
72
+ }
73
+ }
74
+ if (item === null) {
75
+ // Create placeholder button to keep the error without contributing to the form data
76
+ var button = document.createElement('button');
77
+ button.name = elementName;
78
+ button.hidden = true;
79
+ button.dataset.conformTouched = 'true';
80
+ item = button;
81
+ form.appendChild(button);
82
+ }
83
+ }
84
+ }
58
85
  for (var element of form.elements) {
59
- if (isFieldElement(element)) {
60
- var error = firstErrorByName[element.name];
61
- if (typeof error !== 'undefined' || shouldValidate(submission, element.name)) {
62
- element.setCustomValidity(error !== null && error !== void 0 ? error : '');
86
+ if (isFieldElement(element) && element.willValidate) {
87
+ var _elementName = element.name !== '__form__' ? element.name : '';
88
+ var _message = messageByName.get(_elementName);
89
+ var elementShouldValidate = shouldValidate(submission, _elementName);
90
+ if (elementShouldValidate) {
91
+ element.dataset.conformTouched = 'true';
92
+ }
93
+ if (typeof _message !== 'undefined' || elementShouldValidate) {
94
+ var invalidEvent = new Event('invalid', {
95
+ cancelable: true
96
+ });
97
+ element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
98
+ element.dispatchEvent(invalidEvent);
99
+ }
100
+ if (elementShouldValidate && !element.validity.valid) {
101
+ focus(element);
63
102
  }
64
103
  }
65
104
  }
@@ -78,6 +117,12 @@ function setValue(target, paths, valueFn) {
78
117
  pointer = pointer[key];
79
118
  }
80
119
  }
120
+
121
+ /**
122
+ * The ponyfill of `HTMLFormElement.requestSubmit()`
123
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
124
+ * @see https://caniuse.com/?search=requestSubmit
125
+ */
81
126
  function requestSubmit(form, submitter) {
82
127
  var submitEvent = new SubmitEvent('submit', {
83
128
  bubbles: true,
@@ -86,16 +131,39 @@ function requestSubmit(form, submitter) {
86
131
  });
87
132
  form.dispatchEvent(submitEvent);
88
133
  }
89
- function requestValidate(form, field) {
134
+
135
+ /**
136
+ * Creates a command button on demand and trigger a form submit by clicking it.
137
+ */
138
+ function requestCommand(form, buttonProps) {
139
+ if (!form) {
140
+ console.warn('No form element is provided');
141
+ return;
142
+ }
90
143
  var button = document.createElement('button');
91
- button.name = 'conform/validate';
92
- button.value = field !== null && field !== void 0 ? field : '';
93
- button.formNoValidate = true;
144
+ button.name = buttonProps.name;
145
+ button.value = buttonProps.value;
94
146
  button.hidden = true;
147
+ if (buttonProps.formNoValidate) {
148
+ button.formNoValidate = true;
149
+ }
95
150
  form.appendChild(button);
96
- requestSubmit(form, button);
151
+ button.click();
97
152
  form.removeChild(button);
98
153
  }
154
+
155
+ /**
156
+ * Returns the properties required to configure a command button for validation
157
+ *
158
+ * @see https://conform.guide/api/react#validate
159
+ */
160
+ function validate(field) {
161
+ return {
162
+ name: 'conform/validate',
163
+ value: field !== null && field !== void 0 ? field : '',
164
+ formNoValidate: true
165
+ };
166
+ }
99
167
  function getFormElement(element) {
100
168
  var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
101
169
  if (!form) {
@@ -103,20 +171,12 @@ function getFormElement(element) {
103
171
  }
104
172
  return form;
105
173
  }
106
- function focusFirstInvalidField(form) {
174
+ function focus(field) {
107
175
  var currentFocus = document.activeElement;
108
- if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
176
+ if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) {
109
177
  return;
110
178
  }
111
- for (var field of form.elements) {
112
- if (isFieldElement(field)) {
113
- // Focus on the first non button field
114
- if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') {
115
- field.focus();
116
- break;
117
- }
118
- }
119
- }
179
+ field.focus();
120
180
  }
121
181
  function getSubmissionType(name) {
122
182
  var prefix = 'conform/';
@@ -133,8 +193,8 @@ function parse(payload) {
133
193
  error: []
134
194
  };
135
195
  try {
136
- var _loop = function _loop(name, value) {
137
- var submissionType = getSubmissionType(name);
196
+ var _loop = function _loop(value, _name2) {
197
+ var submissionType = getSubmissionType(_name2);
138
198
  if (submissionType) {
139
199
  if (typeof value !== 'string') {
140
200
  throw new Error('The conform command could not be used on a file input');
@@ -148,17 +208,20 @@ function parse(payload) {
148
208
  });
149
209
  hasCommand = true;
150
210
  } else {
151
- var paths = getPaths(name);
211
+ var paths = getPaths(_name2);
152
212
  setValue(submission.value, paths, prev => {
153
- if (prev) {
154
- throw new Error('Entry with the same name is not supported');
213
+ if (!prev) {
214
+ return value;
215
+ } else if (Array.isArray(prev)) {
216
+ return prev.concat(value);
217
+ } else {
218
+ return [prev, value];
155
219
  }
156
- return value;
157
220
  });
158
221
  }
159
222
  };
160
- for (var [name, value] of payload.entries()) {
161
- _loop(name, value);
223
+ for (var [_name2, value] of payload.entries()) {
224
+ _loop(value, _name2);
162
225
  }
163
226
  switch (submission.type) {
164
227
  case 'list':
@@ -173,8 +236,8 @@ function parse(payload) {
173
236
  function parseListCommand(data) {
174
237
  try {
175
238
  var command = JSON.parse(data);
176
- if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
177
- throw new Error('Unsupported list command type');
239
+ if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
240
+ throw new Error("Unknown list command received: ".concat(command.type));
178
241
  }
179
242
  return command;
180
243
  } catch (error) {
@@ -217,15 +280,43 @@ function handleList(submission) {
217
280
  var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
218
281
  var paths = getPaths(command.scope);
219
282
  setValue(submission.value, paths, list => {
220
- if (!Array.isArray(list)) {
283
+ if (typeof list !== 'undefined' && !Array.isArray(list)) {
221
284
  throw new Error('The list command can only be applied to a list');
222
285
  }
223
- return updateList(list, command);
286
+ return updateList(list !== null && list !== void 0 ? list : [], command);
224
287
  });
225
288
  return submission;
226
289
  }
290
+ /**
291
+ * Helpers to configure a command button for modifying a list
292
+ *
293
+ * @see https://conform.guide/api/react#list
294
+ */
295
+ var list = new Proxy({}, {
296
+ get(_target, type) {
297
+ switch (type) {
298
+ case 'append':
299
+ case 'prepend':
300
+ case 'replace':
301
+ case 'remove':
302
+ case 'reorder':
303
+ return function (scope) {
304
+ var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
305
+ return {
306
+ name: 'conform/list',
307
+ value: JSON.stringify({
308
+ type,
309
+ scope,
310
+ payload
311
+ }),
312
+ formNoValidate: true
313
+ };
314
+ };
315
+ }
316
+ }
317
+ });
227
318
 
228
- exports.focusFirstInvalidField = focusFirstInvalidField;
319
+ exports.focus = focus;
229
320
  exports.getFormData = getFormData;
230
321
  exports.getFormElement = getFormElement;
231
322
  exports.getFormElements = getFormElements;
@@ -235,11 +326,13 @@ exports.getSubmissionType = getSubmissionType;
235
326
  exports.handleList = handleList;
236
327
  exports.hasError = hasError;
237
328
  exports.isFieldElement = isFieldElement;
329
+ exports.list = list;
238
330
  exports.parse = parse;
239
331
  exports.parseListCommand = parseListCommand;
332
+ exports.reportSubmission = reportSubmission;
333
+ exports.requestCommand = requestCommand;
240
334
  exports.requestSubmit = requestSubmit;
241
- exports.requestValidate = requestValidate;
242
- exports.setFormError = setFormError;
243
335
  exports.setValue = setValue;
244
336
  exports.shouldValidate = shouldValidate;
245
337
  exports.updateList = updateList;
338
+ exports.validate = validate;
package/module/index.js CHANGED
@@ -41,7 +41,7 @@ function getName(paths) {
41
41
  }, '');
42
42
  }
43
43
  function shouldValidate(submission, name) {
44
- return submission.type === 'submit' || submission.type === 'validate' && (typeof name === 'undefined' || submission.intent === name);
44
+ return submission.type === 'submit' || submission.type === 'validate' && (submission.intent === '' || submission.intent === name);
45
45
  }
46
46
  function hasError(error, name) {
47
47
  return typeof error.find(_ref => {
@@ -49,13 +49,52 @@ function hasError(error, name) {
49
49
  return (typeof name === 'undefined' || name === fieldName) && message !== '';
50
50
  }) !== 'undefined';
51
51
  }
52
- function setFormError(form, submission) {
53
- var firstErrorByName = Object.fromEntries([...submission.error].reverse());
52
+ function reportSubmission(form, submission) {
53
+ var messageByName = new Map();
54
+ for (var [_name, message] of submission.error) {
55
+ if (!messageByName.has(_name)) {
56
+ // Only keep the first error message (for now)
57
+ messageByName.set(_name, message);
58
+
59
+ // We can't use empty string as button name
60
+ // As `form.element.namedItem('')` will always returns null
61
+ var elementName = _name ? _name : '__form__';
62
+ var item = form.elements.namedItem(elementName);
63
+ if (item instanceof RadioNodeList) {
64
+ for (var field of item) {
65
+ if (field.type !== 'radio') {
66
+ throw new Error('Repeated field name is not supported');
67
+ }
68
+ }
69
+ }
70
+ if (item === null) {
71
+ // Create placeholder button to keep the error without contributing to the form data
72
+ var button = document.createElement('button');
73
+ button.name = elementName;
74
+ button.hidden = true;
75
+ button.dataset.conformTouched = 'true';
76
+ item = button;
77
+ form.appendChild(button);
78
+ }
79
+ }
80
+ }
54
81
  for (var element of form.elements) {
55
- if (isFieldElement(element)) {
56
- var error = firstErrorByName[element.name];
57
- if (typeof error !== 'undefined' || shouldValidate(submission, element.name)) {
58
- element.setCustomValidity(error !== null && error !== void 0 ? error : '');
82
+ if (isFieldElement(element) && element.willValidate) {
83
+ var _elementName = element.name !== '__form__' ? element.name : '';
84
+ var _message = messageByName.get(_elementName);
85
+ var elementShouldValidate = shouldValidate(submission, _elementName);
86
+ if (elementShouldValidate) {
87
+ element.dataset.conformTouched = 'true';
88
+ }
89
+ if (typeof _message !== 'undefined' || elementShouldValidate) {
90
+ var invalidEvent = new Event('invalid', {
91
+ cancelable: true
92
+ });
93
+ element.setCustomValidity(_message !== null && _message !== void 0 ? _message : '');
94
+ element.dispatchEvent(invalidEvent);
95
+ }
96
+ if (elementShouldValidate && !element.validity.valid) {
97
+ focus(element);
59
98
  }
60
99
  }
61
100
  }
@@ -74,6 +113,12 @@ function setValue(target, paths, valueFn) {
74
113
  pointer = pointer[key];
75
114
  }
76
115
  }
116
+
117
+ /**
118
+ * The ponyfill of `HTMLFormElement.requestSubmit()`
119
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit
120
+ * @see https://caniuse.com/?search=requestSubmit
121
+ */
77
122
  function requestSubmit(form, submitter) {
78
123
  var submitEvent = new SubmitEvent('submit', {
79
124
  bubbles: true,
@@ -82,16 +127,39 @@ function requestSubmit(form, submitter) {
82
127
  });
83
128
  form.dispatchEvent(submitEvent);
84
129
  }
85
- function requestValidate(form, field) {
130
+
131
+ /**
132
+ * Creates a command button on demand and trigger a form submit by clicking it.
133
+ */
134
+ function requestCommand(form, buttonProps) {
135
+ if (!form) {
136
+ console.warn('No form element is provided');
137
+ return;
138
+ }
86
139
  var button = document.createElement('button');
87
- button.name = 'conform/validate';
88
- button.value = field !== null && field !== void 0 ? field : '';
89
- button.formNoValidate = true;
140
+ button.name = buttonProps.name;
141
+ button.value = buttonProps.value;
90
142
  button.hidden = true;
143
+ if (buttonProps.formNoValidate) {
144
+ button.formNoValidate = true;
145
+ }
91
146
  form.appendChild(button);
92
- requestSubmit(form, button);
147
+ button.click();
93
148
  form.removeChild(button);
94
149
  }
150
+
151
+ /**
152
+ * Returns the properties required to configure a command button for validation
153
+ *
154
+ * @see https://conform.guide/api/react#validate
155
+ */
156
+ function validate(field) {
157
+ return {
158
+ name: 'conform/validate',
159
+ value: field !== null && field !== void 0 ? field : '',
160
+ formNoValidate: true
161
+ };
162
+ }
95
163
  function getFormElement(element) {
96
164
  var form = element instanceof HTMLFormElement ? element : element === null || element === void 0 ? void 0 : element.form;
97
165
  if (!form) {
@@ -99,20 +167,12 @@ function getFormElement(element) {
99
167
  }
100
168
  return form;
101
169
  }
102
- function focusFirstInvalidField(form) {
170
+ function focus(field) {
103
171
  var currentFocus = document.activeElement;
104
- if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== form) {
172
+ if (!isFieldElement(currentFocus) || currentFocus.tagName !== 'BUTTON' || currentFocus.form !== field.form) {
105
173
  return;
106
174
  }
107
- for (var field of form.elements) {
108
- if (isFieldElement(field)) {
109
- // Focus on the first non button field
110
- if (!field.validity.valid && field.dataset.conformTouched && field.tagName !== 'BUTTON') {
111
- field.focus();
112
- break;
113
- }
114
- }
115
- }
175
+ field.focus();
116
176
  }
117
177
  function getSubmissionType(name) {
118
178
  var prefix = 'conform/';
@@ -129,8 +189,8 @@ function parse(payload) {
129
189
  error: []
130
190
  };
131
191
  try {
132
- var _loop = function _loop(name, value) {
133
- var submissionType = getSubmissionType(name);
192
+ var _loop = function _loop(value, _name2) {
193
+ var submissionType = getSubmissionType(_name2);
134
194
  if (submissionType) {
135
195
  if (typeof value !== 'string') {
136
196
  throw new Error('The conform command could not be used on a file input');
@@ -144,17 +204,20 @@ function parse(payload) {
144
204
  });
145
205
  hasCommand = true;
146
206
  } else {
147
- var paths = getPaths(name);
207
+ var paths = getPaths(_name2);
148
208
  setValue(submission.value, paths, prev => {
149
- if (prev) {
150
- throw new Error('Entry with the same name is not supported');
209
+ if (!prev) {
210
+ return value;
211
+ } else if (Array.isArray(prev)) {
212
+ return prev.concat(value);
213
+ } else {
214
+ return [prev, value];
151
215
  }
152
- return value;
153
216
  });
154
217
  }
155
218
  };
156
- for (var [name, value] of payload.entries()) {
157
- _loop(name, value);
219
+ for (var [_name2, value] of payload.entries()) {
220
+ _loop(value, _name2);
158
221
  }
159
222
  switch (submission.type) {
160
223
  case 'list':
@@ -169,8 +232,8 @@ function parse(payload) {
169
232
  function parseListCommand(data) {
170
233
  try {
171
234
  var command = JSON.parse(data);
172
- if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder'].includes(command.type)) {
173
- throw new Error('Unsupported list command type');
235
+ if (typeof command.type !== 'string' || !['prepend', 'append', 'replace', 'remove', 'reorder', 'combine'].includes(command.type)) {
236
+ throw new Error("Unknown list command received: ".concat(command.type));
174
237
  }
175
238
  return command;
176
239
  } catch (error) {
@@ -213,12 +276,40 @@ function handleList(submission) {
213
276
  var command = parseListCommand((_submission$intent = submission.intent) !== null && _submission$intent !== void 0 ? _submission$intent : '');
214
277
  var paths = getPaths(command.scope);
215
278
  setValue(submission.value, paths, list => {
216
- if (!Array.isArray(list)) {
279
+ if (typeof list !== 'undefined' && !Array.isArray(list)) {
217
280
  throw new Error('The list command can only be applied to a list');
218
281
  }
219
- return updateList(list, command);
282
+ return updateList(list !== null && list !== void 0 ? list : [], command);
220
283
  });
221
284
  return submission;
222
285
  }
286
+ /**
287
+ * Helpers to configure a command button for modifying a list
288
+ *
289
+ * @see https://conform.guide/api/react#list
290
+ */
291
+ var list = new Proxy({}, {
292
+ get(_target, type) {
293
+ switch (type) {
294
+ case 'append':
295
+ case 'prepend':
296
+ case 'replace':
297
+ case 'remove':
298
+ case 'reorder':
299
+ return function (scope) {
300
+ var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
301
+ return {
302
+ name: 'conform/list',
303
+ value: JSON.stringify({
304
+ type,
305
+ scope,
306
+ payload
307
+ }),
308
+ formNoValidate: true
309
+ };
310
+ };
311
+ }
312
+ }
313
+ });
223
314
 
224
- export { focusFirstInvalidField, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, parse, parseListCommand, requestSubmit, requestValidate, setFormError, setValue, shouldValidate, updateList };
315
+ export { focus, getFormData, getFormElement, getFormElements, getName, getPaths, getSubmissionType, handleList, hasError, isFieldElement, list, parse, parseListCommand, reportSubmission, requestCommand, requestSubmit, setValue, shouldValidate, updateList, validate };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@conform-to/dom",
3
3
  "description": "A set of opinionated helpers built on top of the Constraint Validation API",
4
4
  "license": "MIT",
5
- "version": "0.4.1",
5
+ "version": "0.5.0",
6
6
  "main": "index.js",
7
7
  "module": "module/index.js",
8
8
  "repository": {