@conform-to/dom 1.8.1 → 1.9.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/dist/form.js CHANGED
@@ -37,13 +37,13 @@ function createFormMeta(options, isResetting) {
37
37
  return result;
38
38
  }
39
39
  function getDefaultKey(defaultValue, prefix) {
40
- return Object.entries(formdata.flatten(defaultValue, {
40
+ return Object.entries(submission.flatten(defaultValue, {
41
41
  prefix
42
42
  })).reduce((result, _ref2) => {
43
43
  var [key, value] = _ref2;
44
44
  if (Array.isArray(value)) {
45
45
  for (var i = 0; i < value.length; i++) {
46
- result[formdata.formatName(key, i)] = util.generateId();
46
+ result[formdata.appendPathSegment(key, i)] = util.generateId();
47
47
  }
48
48
  }
49
49
  return result;
@@ -76,7 +76,7 @@ function handleIntent(meta, intent, fields, initialized) {
76
76
  validated,
77
77
  value
78
78
  } = intent.payload;
79
- var _name2 = formdata.formatName(intent.payload.name, intent.payload.index);
79
+ var _name2 = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
80
80
  if (typeof value !== 'undefined') {
81
81
  updateValue(meta, _name2 !== null && _name2 !== void 0 ? _name2 : '', value);
82
82
  }
@@ -88,8 +88,8 @@ function handleIntent(meta, intent, fields, initialized) {
88
88
  meta.validated = {};
89
89
  }
90
90
  if (validated) {
91
- if (formdata.isPlainObject(value) || Array.isArray(value)) {
92
- Object.assign(meta.validated, formdata.flatten(value, {
91
+ if (util.isPlainObject(value) || Array.isArray(value)) {
92
+ Object.assign(meta.validated, submission.flatten(value, {
93
93
  resolve() {
94
94
  return true;
95
95
  },
@@ -105,8 +105,8 @@ function handleIntent(meta, intent, fields, initialized) {
105
105
  }
106
106
  case 'reset':
107
107
  {
108
- var _name3 = formdata.formatName(intent.payload.name, intent.payload.index);
109
- var _value = formdata.getValue(meta.defaultValue, _name3);
108
+ var _name3 = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
109
+ var _value = formdata.getValueAtPath(meta.defaultValue, _name3);
110
110
  updateValue(meta, _name3, _value);
111
111
  if (_name3) {
112
112
  submission.setState(meta.validated, _name3, () => undefined);
@@ -124,7 +124,7 @@ function handleIntent(meta, intent, fields, initialized) {
124
124
  meta.initialValue = util.clone(meta.initialValue);
125
125
  meta.key = util.clone(meta.key);
126
126
  submission.setListState(meta.key, intent, defaultValue => {
127
- if (!Array.isArray(defaultValue) && !formdata.isPlainObject(defaultValue)) {
127
+ if (!Array.isArray(defaultValue) && !util.isPlainObject(defaultValue)) {
128
128
  return util.generateId();
129
129
  }
130
130
  return Object.assign(getDefaultKey(defaultValue), {
@@ -159,9 +159,9 @@ function updateValue(meta, name, value) {
159
159
  meta.initialValue = util.clone(meta.initialValue);
160
160
  meta.value = util.clone(meta.value);
161
161
  meta.key = util.clone(meta.key);
162
- formdata.setValue(meta.initialValue, name, () => value);
163
- formdata.setValue(meta.value, name, () => value);
164
- if (formdata.isPlainObject(value) || Array.isArray(value)) {
162
+ formdata.setValueAtPath(meta.initialValue, name, () => value);
163
+ formdata.setValueAtPath(meta.value, name, () => value);
164
+ if (util.isPlainObject(value) || Array.isArray(value)) {
165
165
  submission.setState(meta.key, name, () => undefined);
166
166
  Object.assign(meta.key, getDefaultKey(value, name));
167
167
  }
@@ -180,16 +180,16 @@ function createStateProxy(fn) {
180
180
  });
181
181
  }
182
182
  function createValueProxy(value) {
183
- var val = formdata.normalize(value);
183
+ var val = submission.normalize(value);
184
184
  return createStateProxy((name, proxy) => {
185
185
  if (name === '') {
186
186
  return val;
187
187
  }
188
- var paths = formdata.getPaths(name);
189
- var basename = formdata.formatPaths(paths.slice(0, -1));
190
- var key = formdata.formatPaths(paths.slice(-1));
188
+ var path = formdata.getPathSegments(name);
189
+ var basename = formdata.formatPathSegments(path.slice(0, -1));
190
+ var key = formdata.formatPathSegments(path.slice(-1));
191
191
  var parentValue = proxy[basename];
192
- return formdata.getValue(parentValue, key);
192
+ return formdata.getValueAtPath(parentValue, key);
193
193
  });
194
194
  }
195
195
  function createConstraintProxy(constraint) {
@@ -197,15 +197,20 @@ function createConstraintProxy(constraint) {
197
197
  var _result;
198
198
  var result = constraint[name];
199
199
  if (!result) {
200
- var paths = formdata.getPaths(name);
201
- for (var i = paths.length - 1; i >= 0; i--) {
202
- var path = paths[i];
203
- if (typeof path === 'number' && !Number.isNaN(path)) {
204
- paths[i] = Number.NaN;
200
+ var path = formdata.getPathSegments(name);
201
+ for (var i = path.length - 1; i >= 0; i--) {
202
+ var segment = path[i];
203
+
204
+ // Try searching a less specific path for the constraint
205
+ // e.g. `array[0].anotherArray[1].key` -> `array[0].anotherArray[].key` -> `array[].anotherArray[].key`
206
+ if (typeof segment === 'number') {
207
+ // This overrides the current number segment with an empty string
208
+ // which will be treated as an empty bracket
209
+ path[i] = '';
205
210
  break;
206
211
  }
207
212
  }
208
- var alternative = formdata.formatPaths(paths);
213
+ var alternative = formdata.formatPathSegments(path);
209
214
  if (name !== alternative) {
210
215
  result = proxy[alternative];
211
216
  }
@@ -216,15 +221,15 @@ function createConstraintProxy(constraint) {
216
221
  function createKeyProxy(key) {
217
222
  return createStateProxy((name, proxy) => {
218
223
  var currentKey = key[name];
219
- var paths = formdata.getPaths(name);
220
- if (paths.length === 0) {
224
+ var segments = formdata.getPathSegments(name);
225
+ if (segments.length === 0) {
221
226
  return currentKey;
222
227
  }
223
- var parentKey = proxy[formdata.formatPaths(paths.slice(0, -1))];
228
+ var parentKey = proxy[formdata.formatPathSegments(segments.slice(0, -1))];
224
229
  if (typeof parentKey === 'undefined') {
225
230
  return currentKey;
226
231
  }
227
- return "".concat(parentKey, "/").concat(currentKey !== null && currentKey !== void 0 ? currentKey : paths.at(-1));
232
+ return "".concat(parentKey, "/").concat(currentKey !== null && currentKey !== void 0 ? currentKey : segments.at(-1));
228
233
  });
229
234
  }
230
235
  function createValidProxy(error) {
@@ -575,19 +580,22 @@ function createFormContext(options) {
575
580
  switch (intent.type) {
576
581
  case 'update':
577
582
  {
578
- var _name5 = formdata.formatName(intent.payload.name, intent.payload.index);
579
- var parentPaths = formdata.getPaths(_name5);
583
+ var _name5 = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
584
+ var baseSegments = formdata.getPathSegments(_name5);
580
585
  for (var element of formElement.elements) {
581
586
  if (dom.isFieldElement(element)) {
582
- var paths = formdata.getChildPaths(parentPaths, element.name);
587
+ var paths = formdata.getRelativePath(element.name, baseSegments);
583
588
  if (paths) {
584
- var value = formdata.getValue(intent.payload.value, formdata.formatPaths(paths));
585
- dom.updateField(element, {
586
- value: typeof value === 'string' || Array.isArray(value) && value.every(item => typeof item === 'string') ? value : null
587
- });
589
+ var value = formdata.getValueAtPath(intent.payload.value, paths);
590
+ var inputValue = typeof value === 'string' || Array.isArray(value) && value.every(item => typeof item === 'string') ? value : undefined;
591
+ if (typeof inputValue !== 'undefined' || _name5 === '' && paths.length > 1) {
592
+ dom.updateField(element, {
593
+ value: inputValue !== null && inputValue !== void 0 ? inputValue : null
594
+ });
588
595
 
589
- // Update the element attribute to notify useControl / useInputControl hook
590
- element.dataset.conform = util.generateId();
596
+ // Update the element attribute to notify useControl / useInputControl hook
597
+ element.dataset.conform = util.generateId();
598
+ }
591
599
  }
592
600
  }
593
601
  }
@@ -595,14 +603,17 @@ function createFormContext(options) {
595
603
  }
596
604
  case 'reset':
597
605
  {
598
- var prefix = formdata.formatName(intent.payload.name, intent.payload.index);
606
+ var prefix = formdata.appendPathSegment(intent.payload.name, intent.payload.index);
599
607
  for (var _element of formElement.elements) {
600
608
  if (dom.isFieldElement(_element) && _element.name && formdata.isPrefix(_element.name, prefix)) {
601
- var _value2 = formdata.getValue(meta.defaultValue, _element.name);
602
- var defaultValue = typeof _value2 === 'string' || Array.isArray(_value2) && _value2.every(item => typeof item === 'string') ? _value2 : null;
609
+ var _value2 = formdata.getValueAtPath(meta.defaultValue, _element.name);
610
+ var defaultValue = typeof _value2 === 'string' || Array.isArray(_value2) && _value2.every(item => typeof item === 'string') ? _value2 : undefined;
611
+ if (typeof defaultValue === 'undefined' && !_element.dataset.conform && 'defaultValue' in _element && !dom.isDirtyInput(_element)) {
612
+ continue;
613
+ }
603
614
  dom.updateField(_element, {
604
- defaultValue,
605
- value: defaultValue
615
+ defaultValue: defaultValue,
616
+ value: defaultValue !== null && defaultValue !== void 0 ? defaultValue : null
606
617
  });
607
618
 
608
619
  // Update the element attribute to notify useControl / useInputControl hook
package/dist/form.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
2
- import { flatten, formatName, getValue, isPlainObject, isPrefix, setValue, normalize, getFormData, getPaths, getChildPaths, formatPaths } from './formdata.mjs';
3
- import { getFormAction, getFormEncType, getFormMethod, isFieldElement, requestSubmit, updateField } from './dom.mjs';
4
- import { generateId, clone, invariant } from './util.mjs';
5
- import { serialize, setListState, setListValue, setState, INTENT, serializeIntent, root, getSubmissionContext } from './submission.mjs';
2
+ import { appendPathSegment, getValueAtPath, isPrefix, setValueAtPath, getFormData, getPathSegments, getRelativePath, formatPathSegments } from './formdata.mjs';
3
+ import { getFormAction, getFormEncType, getFormMethod, isFieldElement, requestSubmit, isDirtyInput, updateField } from './dom.mjs';
4
+ import { generateId, clone, isPlainObject, invariant } from './util.mjs';
5
+ import { serialize, flatten, setListState, setListValue, setState, normalize, INTENT, serializeIntent, root, getSubmissionContext } from './submission.mjs';
6
6
 
7
7
  function createFormMeta(options, isResetting) {
8
8
  var _lastResult$initialVa, _options$constraint, _lastResult$state$val, _lastResult$state, _ref;
@@ -39,7 +39,7 @@ function getDefaultKey(defaultValue, prefix) {
39
39
  var [key, value] = _ref2;
40
40
  if (Array.isArray(value)) {
41
41
  for (var i = 0; i < value.length; i++) {
42
- result[formatName(key, i)] = generateId();
42
+ result[appendPathSegment(key, i)] = generateId();
43
43
  }
44
44
  }
45
45
  return result;
@@ -72,7 +72,7 @@ function handleIntent(meta, intent, fields, initialized) {
72
72
  validated,
73
73
  value
74
74
  } = intent.payload;
75
- var _name2 = formatName(intent.payload.name, intent.payload.index);
75
+ var _name2 = appendPathSegment(intent.payload.name, intent.payload.index);
76
76
  if (typeof value !== 'undefined') {
77
77
  updateValue(meta, _name2 !== null && _name2 !== void 0 ? _name2 : '', value);
78
78
  }
@@ -101,8 +101,8 @@ function handleIntent(meta, intent, fields, initialized) {
101
101
  }
102
102
  case 'reset':
103
103
  {
104
- var _name3 = formatName(intent.payload.name, intent.payload.index);
105
- var _value = getValue(meta.defaultValue, _name3);
104
+ var _name3 = appendPathSegment(intent.payload.name, intent.payload.index);
105
+ var _value = getValueAtPath(meta.defaultValue, _name3);
106
106
  updateValue(meta, _name3, _value);
107
107
  if (_name3) {
108
108
  setState(meta.validated, _name3, () => undefined);
@@ -155,8 +155,8 @@ function updateValue(meta, name, value) {
155
155
  meta.initialValue = clone(meta.initialValue);
156
156
  meta.value = clone(meta.value);
157
157
  meta.key = clone(meta.key);
158
- setValue(meta.initialValue, name, () => value);
159
- setValue(meta.value, name, () => value);
158
+ setValueAtPath(meta.initialValue, name, () => value);
159
+ setValueAtPath(meta.value, name, () => value);
160
160
  if (isPlainObject(value) || Array.isArray(value)) {
161
161
  setState(meta.key, name, () => undefined);
162
162
  Object.assign(meta.key, getDefaultKey(value, name));
@@ -181,11 +181,11 @@ function createValueProxy(value) {
181
181
  if (name === '') {
182
182
  return val;
183
183
  }
184
- var paths = getPaths(name);
185
- var basename = formatPaths(paths.slice(0, -1));
186
- var key = formatPaths(paths.slice(-1));
184
+ var path = getPathSegments(name);
185
+ var basename = formatPathSegments(path.slice(0, -1));
186
+ var key = formatPathSegments(path.slice(-1));
187
187
  var parentValue = proxy[basename];
188
- return getValue(parentValue, key);
188
+ return getValueAtPath(parentValue, key);
189
189
  });
190
190
  }
191
191
  function createConstraintProxy(constraint) {
@@ -193,15 +193,20 @@ function createConstraintProxy(constraint) {
193
193
  var _result;
194
194
  var result = constraint[name];
195
195
  if (!result) {
196
- var paths = getPaths(name);
197
- for (var i = paths.length - 1; i >= 0; i--) {
198
- var path = paths[i];
199
- if (typeof path === 'number' && !Number.isNaN(path)) {
200
- paths[i] = Number.NaN;
196
+ var path = getPathSegments(name);
197
+ for (var i = path.length - 1; i >= 0; i--) {
198
+ var segment = path[i];
199
+
200
+ // Try searching a less specific path for the constraint
201
+ // e.g. `array[0].anotherArray[1].key` -> `array[0].anotherArray[].key` -> `array[].anotherArray[].key`
202
+ if (typeof segment === 'number') {
203
+ // This overrides the current number segment with an empty string
204
+ // which will be treated as an empty bracket
205
+ path[i] = '';
201
206
  break;
202
207
  }
203
208
  }
204
- var alternative = formatPaths(paths);
209
+ var alternative = formatPathSegments(path);
205
210
  if (name !== alternative) {
206
211
  result = proxy[alternative];
207
212
  }
@@ -212,15 +217,15 @@ function createConstraintProxy(constraint) {
212
217
  function createKeyProxy(key) {
213
218
  return createStateProxy((name, proxy) => {
214
219
  var currentKey = key[name];
215
- var paths = getPaths(name);
216
- if (paths.length === 0) {
220
+ var segments = getPathSegments(name);
221
+ if (segments.length === 0) {
217
222
  return currentKey;
218
223
  }
219
- var parentKey = proxy[formatPaths(paths.slice(0, -1))];
224
+ var parentKey = proxy[formatPathSegments(segments.slice(0, -1))];
220
225
  if (typeof parentKey === 'undefined') {
221
226
  return currentKey;
222
227
  }
223
- return "".concat(parentKey, "/").concat(currentKey !== null && currentKey !== void 0 ? currentKey : paths.at(-1));
228
+ return "".concat(parentKey, "/").concat(currentKey !== null && currentKey !== void 0 ? currentKey : segments.at(-1));
224
229
  });
225
230
  }
226
231
  function createValidProxy(error) {
@@ -571,19 +576,22 @@ function createFormContext(options) {
571
576
  switch (intent.type) {
572
577
  case 'update':
573
578
  {
574
- var _name5 = formatName(intent.payload.name, intent.payload.index);
575
- var parentPaths = getPaths(_name5);
579
+ var _name5 = appendPathSegment(intent.payload.name, intent.payload.index);
580
+ var baseSegments = getPathSegments(_name5);
576
581
  for (var element of formElement.elements) {
577
582
  if (isFieldElement(element)) {
578
- var paths = getChildPaths(parentPaths, element.name);
583
+ var paths = getRelativePath(element.name, baseSegments);
579
584
  if (paths) {
580
- var value = getValue(intent.payload.value, formatPaths(paths));
581
- updateField(element, {
582
- value: typeof value === 'string' || Array.isArray(value) && value.every(item => typeof item === 'string') ? value : null
583
- });
585
+ var value = getValueAtPath(intent.payload.value, paths);
586
+ var inputValue = typeof value === 'string' || Array.isArray(value) && value.every(item => typeof item === 'string') ? value : undefined;
587
+ if (typeof inputValue !== 'undefined' || _name5 === '' && paths.length > 1) {
588
+ updateField(element, {
589
+ value: inputValue !== null && inputValue !== void 0 ? inputValue : null
590
+ });
584
591
 
585
- // Update the element attribute to notify useControl / useInputControl hook
586
- element.dataset.conform = generateId();
592
+ // Update the element attribute to notify useControl / useInputControl hook
593
+ element.dataset.conform = generateId();
594
+ }
587
595
  }
588
596
  }
589
597
  }
@@ -591,14 +599,17 @@ function createFormContext(options) {
591
599
  }
592
600
  case 'reset':
593
601
  {
594
- var prefix = formatName(intent.payload.name, intent.payload.index);
602
+ var prefix = appendPathSegment(intent.payload.name, intent.payload.index);
595
603
  for (var _element of formElement.elements) {
596
604
  if (isFieldElement(_element) && _element.name && isPrefix(_element.name, prefix)) {
597
- var _value2 = getValue(meta.defaultValue, _element.name);
598
- var defaultValue = typeof _value2 === 'string' || Array.isArray(_value2) && _value2.every(item => typeof item === 'string') ? _value2 : null;
605
+ var _value2 = getValueAtPath(meta.defaultValue, _element.name);
606
+ var defaultValue = typeof _value2 === 'string' || Array.isArray(_value2) && _value2.every(item => typeof item === 'string') ? _value2 : undefined;
607
+ if (typeof defaultValue === 'undefined' && !_element.dataset.conform && 'defaultValue' in _element && !isDirtyInput(_element)) {
608
+ continue;
609
+ }
599
610
  updateField(_element, {
600
- defaultValue,
601
- value: defaultValue
611
+ defaultValue: defaultValue,
612
+ value: defaultValue !== null && defaultValue !== void 0 ? defaultValue : null
602
613
  });
603
614
 
604
615
  // Update the element attribute to notify useControl / useInputControl hook
@@ -1,3 +1,5 @@
1
+ import type { FormError, FormValue, JsonPrimitive, Serialize, SerializedValue, Submission, SubmissionResult } from './types';
2
+ export declare const DEFAULT_INTENT_NAME = "__INTENT__";
1
3
  /**
2
4
  * Construct a form data with the submitter value.
3
5
  * It utilizes the submitter argument on the FormData constructor from modern browsers
@@ -5,96 +7,83 @@
5
7
  *
6
8
  * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters
7
9
  */
8
- export declare function getFormData(form: HTMLFormElement, submitter?: HTMLInputElement | HTMLButtonElement | null): FormData;
10
+ export declare function getFormData(form: HTMLFormElement, submitter?: HTMLElement | null): FormData;
9
11
  /**
10
- * Returns the paths from a name based on the JS syntax convention
12
+ * Convert a string path into an array of segments.
13
+ *
11
14
  * @example
12
15
  * ```js
13
- * const paths = getPaths('todos[0].content'); // ['todos', 0, 'content']
16
+ * getPathSegments("object.key"); // ['object', 'key']
17
+ * getPathSegments("array[0].content"); // → ['array', 0, 'content']
18
+ * getPathSegments("todos[]"); // → ['todos', '']
14
19
  * ```
15
20
  */
16
- export declare function getPaths(name: string | undefined): Array<string | number>;
21
+ export declare function getPathSegments(path: string | undefined): Array<string | number>;
17
22
  /**
18
- * Returns a formatted name from the paths based on the JS syntax convention
23
+ * Returns a formatted name from the path segments based on the dot and bracket notation.
24
+ *
19
25
  * @example
20
26
  * ```js
21
- * const name = formatPaths(['todos', 0, 'content']); // "todos[0].content"
27
+ * formatPathSegments(['object', 'key']); // "object.key"
28
+ * formatPathSegments(['array', 0, 'content']); // → "array[0].content"
29
+ * formatPathSegments(['todos', '']); // → "todos[]"
22
30
  * ```
23
31
  */
24
- export declare function formatPaths(paths: Array<string | number>): string;
32
+ export declare function formatPathSegments(segments: Readonly<Array<string | number>>): string;
25
33
  /**
26
- * Format based on a prefix and a path
34
+ * Append one more segment onto an existing path string.
35
+ *
36
+ * - segment = `undefined` ⇒ no-op
37
+ * - segment = `""` ⇒ empty brackets "[]"
38
+ * - segment = `number` ⇒ bracket notation "[n]"
39
+ * - segment = `string` ⇒ dot-notation ".prop"
27
40
  */
28
- export declare function formatName(prefix: string | undefined, path?: string | number): string;
41
+ export declare function appendPathSegment(path: string | undefined, segment: string | number | undefined): string;
29
42
  /**
30
- * Check if a name match the prefix paths
43
+ * Returns true if `prefix` is a valid leading path of `name`.
44
+ *
45
+ * @example
46
+ * ```js
47
+ * isPrefix("foo.bar.baz", "foo.bar") // → true
48
+ * isPrefix("foo.bar[3].baz", "foo.bar[3]") // → true
49
+ * isPrefix("foo.bar[3].baz", "foo.bar") // → true
50
+ * isPrefix("foo.bar[3].baz", "foo.baz") // → false
51
+ * isPrefix("foo", "foo.bar") // → false
52
+ * ```
31
53
  */
32
54
  export declare function isPrefix(name: string, prefix: string): boolean;
33
55
  /**
34
- * Compare the parent and child paths to get the relative paths
35
- * Returns null if the child paths do not start with the parent paths
36
- */
37
- export declare function getChildPaths(parentNameOrPaths: string | Array<string | number>, childName: string): (string | number)[] | null;
38
- /**
39
- * Assign a value to a target object by following the paths
40
- */
41
- export declare function setValue(target: Record<string, any>, name: string, valueFn: (currentValue?: unknown) => unknown): void;
42
- /**
43
- * Retrive the value from a target object by following the paths
44
- */
45
- export declare function getValue(target: unknown, name: string): unknown;
46
- /**
47
- * Check if the value is a plain object
48
- */
49
- export declare function isPlainObject(obj: unknown): obj is Record<string | number | symbol, unknown>;
50
- type GlobalConstructors = {
51
- [K in keyof typeof globalThis]: (typeof globalThis)[K] extends new (...args: any) => any ? K : never;
52
- }[keyof typeof globalThis];
53
- export declare function isGlobalInstance<ClassName extends GlobalConstructors>(obj: unknown, className: ClassName): obj is InstanceType<(typeof globalThis)[ClassName]>;
54
- /**
55
- * Normalize value by removing empty object or array, empty string and null values
56
- */
57
- export declare function normalize<Type extends Record<string, unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
58
- export declare function normalize<Type extends Array<unknown>>(value: Type, acceptFile?: boolean): Type | undefined;
59
- export declare function normalize(value: unknown, acceptFile?: boolean): unknown | undefined;
60
- /**
61
- * Flatten a tree into a dictionary
56
+ * Return the segments of `fullPathStr` that come after the `baseSegments` prefix.
57
+ *
58
+ * @param fullPathStr Full path as a dot/bracket string
59
+ * @param basePath Base path, already parsed into segments
60
+ * @returns The “tail” segments, or `null` if `fullPathStr` isn’t nested under `baseSegments`
61
+ *
62
+ * @example
63
+ * ```js
64
+ * getRelativePath("foo.bar[0].qux", ["foo","bar"]) // → [0, "qux"]
65
+ * getRelativePath("a.b.c.d", ["a","b"]) // ["c","d"]
66
+ * getRelativePath("foo", ["foo","bar"]) // → null
67
+ * ```
62
68
  */
63
- export declare function flatten(data: unknown, options?: {
64
- resolve?: (data: unknown) => unknown;
65
- prefix?: string;
66
- }): Record<string, unknown>;
67
- export declare function deepEqual(left: unknown, right: unknown): boolean;
68
- export type JsonPrimitive = string | number | boolean | null;
69
+ export declare function getRelativePath(name: string, basePath: Array<string | number>): Array<string | number> | null;
69
70
  /**
70
- * The form value of a submission. This is usually constructed from a FormData or URLSearchParams.
71
- * It may contains JSON primitives if the value is updated based on a form intent.
71
+ * Assign a value to a target object by following the path segments.
72
72
  */
73
- export type FormValue<Type extends JsonPrimitive | FormDataEntryValue = JsonPrimitive | FormDataEntryValue> = Type | FormValue<Type | null>[] | {
74
- [key: string]: FormValue<Type>;
75
- };
73
+ export declare function setValueAtPath<T extends Record<string, any>>(target: T, pathOrSegments: string | Array<string | number>, valueOrFn: unknown | ((current: unknown) => unknown), options?: {
74
+ clone?: boolean;
75
+ silent?: boolean;
76
+ }): T;
76
77
  /**
77
- * The data of a form submission.
78
+ * Retrive the value from a target object by following the path segments.
78
79
  */
79
- export type Submission<ValueType extends FormDataEntryValue = FormDataEntryValue> = {
80
- /**
81
- * The form value structured following the naming convention.
82
- */
83
- value: Record<string, FormValue<ValueType>>;
84
- /**
85
- * The field names that are included in the FormData or URLSearchParams.
86
- */
87
- fields: string[];
88
- /**
89
- * The intent of the submission. This is usally included by specifying a name and value on a submit button.
90
- */
91
- intent: string | null;
92
- };
80
+ export declare function getValueAtPath(target: unknown, pathOrSegments: string | Array<string | number>): unknown;
93
81
  /**
94
82
  * Parse `FormData` or `URLSearchParams` into a submission object.
95
83
  * This function structures the form values based on the naming convention.
96
- * It also includes all the field names and the intent if the `intentName` option is provided.
84
+ * It also includes all the field names and extracts the intent from the submission.
97
85
  *
86
+ * @see https://conform.guide/api/react/future/parseSubmission
98
87
  * @example
99
88
  * ```ts
100
89
  * const formData = new FormData();
@@ -104,7 +93,7 @@ export type Submission<ValueType extends FormDataEntryValue = FormDataEntryValue
104
93
  *
105
94
  * parseSubmission(formData)
106
95
  * // {
107
- * // value: { email: 'test@example.com', password: 'secret' },
96
+ * // payload: { email: 'test@example.com', password: 'secret' },
108
97
  * // fields: ['email', 'password'],
109
98
  * // intent: null,
110
99
  * // }
@@ -113,7 +102,7 @@ export type Submission<ValueType extends FormDataEntryValue = FormDataEntryValue
113
102
  * formData.append('intent', 'login');
114
103
  * parseSubmission(formData, { intentName: 'intent' })
115
104
  * // {
116
- * // value: { email: 'test@example.com', password: 'secret' },
105
+ * // payload: { email: 'test@example.com', password: 'secret' },
117
106
  * // fields: ['email', 'password'],
118
107
  * // intent: 'login',
119
108
  * // }
@@ -121,18 +110,61 @@ export type Submission<ValueType extends FormDataEntryValue = FormDataEntryValue
121
110
  */
122
111
  export declare function parseSubmission(formData: FormData | URLSearchParams, options?: {
123
112
  /**
124
- * The name of the submit button that triggered the form submission.
125
- * Used to extract the submission's intent.
113
+ * The name of the submit button field that indicates the submission intent.
114
+ * Defaults to `__INTENT__`.
126
115
  */
127
116
  intentName?: string;
128
117
  /**
129
- * A filter function that excludes specific entries from being parsed.
118
+ * A function to exclude specific form fields from being parsed.
130
119
  * Return `true` to skip the entry.
131
120
  */
132
121
  skipEntry?: (name: string) => boolean;
133
122
  }): Submission;
134
- export type ParseSubmissionOptions = Required<Parameters<typeof parseSubmission>>[1];
135
- export declare function defaultSerialize(value: unknown): FormDataEntryValue | undefined;
123
+ /**
124
+ * Creates a SubmissionResult object from a submission, adding validation results and intended values.
125
+ * This function will remove all files in the submission payload by default since
126
+ * file inputs cannot be initialized with files.
127
+ * You can specify `keepFiles: true` to keep the files if needed.
128
+ *
129
+ * @see https://conform.guide/api/react/future/report
130
+ * @example
131
+ * ```ts
132
+ * // Report the submission with the field errors
133
+ * report(submission, {
134
+ * error: {
135
+ * fieldErrors: {
136
+ * email: ['Invalid email format'],
137
+ * password: ['Password is required'],
138
+ * },
139
+ * })
140
+ *
141
+ * // Report the submission with a form error
142
+ * report(submission, {
143
+ * error: {
144
+ * formErrors: ['Invalid credentials'],
145
+ * },
146
+ * })
147
+ *
148
+ * // Reset the form
149
+ * report(submission, {
150
+ * reset: true,
151
+ * })
152
+ * ```
153
+ */
154
+ export declare function report<ErrorShape = string>(submission: Submission, options?: {
155
+ keepFiles?: false;
156
+ error?: Partial<FormError<ErrorShape>> | null;
157
+ intendedValue?: Record<string, FormValue> | null;
158
+ hideFields?: string[];
159
+ reset?: boolean;
160
+ }): SubmissionResult<ErrorShape, Exclude<JsonPrimitive | FormDataEntryValue, File>>;
161
+ export declare function report<ErrorShape = string>(submission: Submission, options: {
162
+ keepFiles: true;
163
+ error?: Partial<FormError<ErrorShape>> | null;
164
+ intendedValue?: Record<string, FormValue> | null;
165
+ hideFields?: string[];
166
+ reset?: boolean;
167
+ }): SubmissionResult<ErrorShape>;
136
168
  /**
137
169
  * A utility function that checks whether the current form data differs from the default values.
138
170
  *
@@ -160,7 +192,7 @@ export declare function isDirty(
160
192
  * - A `URLSearchParams` object
161
193
  * - A plain object that was parsed from form data (i.e. `submission.payload`)
162
194
  */
163
- formData: FormData | URLSearchParams | FormValue<FormDataEntryValue> | null, options?: {
195
+ formData: FormData | URLSearchParams | FormValue | null, options?: {
164
196
  /**
165
197
  * An object representing the default values of the form to compare against.
166
198
  * Defaults to an empty object if not provided.
@@ -185,7 +217,7 @@ formData: FormData | URLSearchParams | FormValue<FormDataEntryValue> | null, opt
185
217
  * - Date:
186
218
  * - Converted to ISO string using `.toISOString()`
187
219
  */
188
- serialize?: (value: unknown, defaultSerialize: (value: unknown) => FormDataEntryValue | undefined) => FormDataEntryValue | undefined;
220
+ serialize?: (value: unknown, defaultSerialize: Serialize) => SerializedValue | undefined;
189
221
  /**
190
222
  * A function to exclude specific fields from the comparison.
191
223
  * Useful for ignoring hidden inputs like CSRF tokens or internal fields added by frameworks
@@ -200,5 +232,20 @@ formData: FormData | URLSearchParams | FormValue<FormDataEntryValue> | null, opt
200
232
  */
201
233
  skipEntry?: (name: string) => boolean;
202
234
  }): boolean | undefined;
203
- export {};
235
+ /**
236
+ * Convert an unknown value into something acceptable for HTML form submission.
237
+ * Returns `undefined` when the value cannot be represented in form data.
238
+ *
239
+ * Input -> Output:
240
+ * - string -> string
241
+ * - null -> '' (empty string)
242
+ * - boolean -> 'on' | '' (checked semantics)
243
+ * - number | bigint -> value.toString()
244
+ * - Date -> value.toISOString()
245
+ * - File -> File
246
+ * - FileList -> File[]
247
+ * - Array -> string[] or File[] if all items serialize to the same kind; otherwise undefined
248
+ * - anything else -> undefined
249
+ */
250
+ export declare function serialize(value: unknown): SerializedValue | undefined;
204
251
  //# sourceMappingURL=formdata.d.ts.map