@conform-to/react 1.8.2 → 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.
@@ -2,146 +2,228 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var dom = require('@conform-to/dom');
5
+ var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
6
+ var future = require('@conform-to/dom/future');
6
7
 
7
- function getFormElement(formRef) {
8
- var _element$form;
9
- if (typeof formRef === 'string') {
10
- return document.forms.namedItem(formRef);
11
- }
12
- var element = formRef === null || formRef === void 0 ? void 0 : formRef.current;
13
- if (element instanceof HTMLFormElement) {
14
- return element;
15
- }
16
- return (_element$form = element === null || element === void 0 ? void 0 : element.form) !== null && _element$form !== void 0 ? _element$form : null;
8
+ var _excluded = [""];
9
+ function isUndefined(value) {
10
+ return value === undefined;
17
11
  }
18
- function focusable(element) {
19
- if (!element.hidden && element.type !== 'hidden') {
20
- return;
21
- }
22
-
23
- // Style the element to be visually hidden
24
- element.style.position = 'absolute';
25
- element.style.width = '1px';
26
- element.style.height = '1px';
27
- element.style.padding = '0';
28
- element.style.margin = '-1px';
29
- element.style.overflow = 'hidden';
30
- element.style.clip = 'rect(0,0,0,0)';
31
- element.style.whiteSpace = 'nowrap';
32
- element.style.border = '0';
33
-
34
- // Hide the element from screen readers
35
- element.setAttribute('aria-hidden', 'true');
36
-
37
- // Make sure people won't tab to this element
38
- element.tabIndex = -1;
39
-
40
- // Set the element to be visible again so it can be focused
41
- if (element.hidden) {
42
- element.hidden = false;
43
- }
44
- if (element.type === 'hidden') {
45
- element.setAttribute('type', 'text');
46
- }
12
+ function isString(value) {
13
+ return typeof value === 'string';
14
+ }
15
+ function isNumber(value) {
16
+ return typeof value === 'number';
47
17
  }
48
- function initializeField(element, options) {
49
- var _options$value;
50
- if (element.dataset.conform) {
51
- return;
18
+ function isOptional(value, typeGuard) {
19
+ return isUndefined(value) || typeGuard(value);
20
+ }
21
+ function getArrayAtPath(formValue, name) {
22
+ var _getValueAtPath;
23
+ var value = (_getValueAtPath = future.getValueAtPath(formValue, name)) !== null && _getValueAtPath !== void 0 ? _getValueAtPath : [];
24
+ if (!Array.isArray(value)) {
25
+ throw new Error("The value of \"".concat(name, "\" is not an array"));
52
26
  }
53
- var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
54
-
55
- // Update the value of the element, including the default value
56
- dom.unstable_updateField(element, {
57
- value: defaultValue,
58
- defaultValue
59
- });
60
- element.dataset.conform = 'initialized';
27
+ return value;
61
28
  }
62
- function getRadioGroupValue(inputs) {
63
- for (var input of inputs) {
64
- if (input.type === 'radio' && input.checked) {
65
- return input.value;
29
+
30
+ /**
31
+ * Immutably updates a value at the specified path.
32
+ * Empty path replaces the entire object.
33
+ */
34
+ function updateValueAtPath(data, name, value) {
35
+ if (name === '') {
36
+ if (!future.isPlainObject(value)) {
37
+ throw new Error('The value must be an object');
66
38
  }
39
+ return value;
67
40
  }
41
+ return future.setValueAtPath(data, future.getPathSegments(name), value, {
42
+ clone: true
43
+ });
68
44
  }
69
- function getCheckboxGroupValue(inputs) {
70
- var values;
71
- for (var input of inputs) {
72
- if (input.type === 'checkbox') {
73
- var _values;
74
- (_values = values) !== null && _values !== void 0 ? _values : values = [];
75
- if (input.checked) {
76
- values.push(input.value);
45
+
46
+ /**
47
+ * Creates a function that updates array indices in field paths.
48
+ * Returns null to remove fields, or updated path with new index.
49
+ */
50
+ function createPathIndexUpdater(listName, update) {
51
+ var listPaths = future.getPathSegments(listName);
52
+ return name => {
53
+ var paths = future.getPathSegments(name);
54
+ if (paths.length > listPaths.length && listPaths.every((path, index) => paths[index] === path)) {
55
+ var currentIndex = paths[listPaths.length];
56
+ if (typeof currentIndex === 'number') {
57
+ var newIndex = update(currentIndex);
58
+ if (newIndex === null) {
59
+ // To remove the item instead of updating it
60
+ return null;
61
+ }
62
+ if (newIndex !== currentIndex) {
63
+ // Replace the index
64
+ paths.splice(listPaths.length, 1, newIndex);
65
+ return future.formatPathSegments(paths);
66
+ }
77
67
  }
78
68
  }
69
+ return name;
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Returns null if error object has no actual error messages,
75
+ * otherwise returns the error as-is.
76
+ */
77
+ function normalizeFormError(error) {
78
+ if (error && error.formErrors.length === 0 && Object.entries(error.fieldErrors).every(_ref => {
79
+ var [, messages] = _ref;
80
+ return Array.isArray(messages) ? messages.length === 0 : !messages;
81
+ })) {
82
+ return null;
79
83
  }
80
- return values;
81
- }
82
- function getInputSnapshot(input) {
83
- if (input instanceof HTMLInputElement) {
84
- switch (input.type) {
85
- case 'file':
86
- return {
87
- files: input.files ? Array.from(input.files) : undefined
88
- };
89
- case 'radio':
90
- case 'checkbox':
91
- return {
92
- value: input.value,
93
- checked: input.checked
94
- };
95
- }
96
- } else if (input instanceof HTMLSelectElement && input.multiple) {
84
+ return error;
85
+ }
86
+ function normalizeValidateResult(result) {
87
+ if (result !== null && 'error' in result) {
97
88
  return {
98
- options: Array.from(input.selectedOptions).map(option => option.value)
89
+ error: normalizeFormError(result.error),
90
+ value: result.value
99
91
  };
100
92
  }
101
93
  return {
102
- value: input.value
94
+ error: normalizeFormError(result)
103
95
  };
104
96
  }
105
- function getDefaultSnapshot(defaultValue, defaultChecked, value) {
106
- if (typeof value === 'string' || typeof defaultChecked === 'boolean') {
107
- return {
108
- value: value !== null && value !== void 0 ? value : 'on',
109
- checked: defaultChecked
110
- };
97
+
98
+ /**
99
+ * Handles different validation result formats:
100
+ * - Promise: async validation only
101
+ * - Array: [syncResult, asyncPromise]
102
+ * - Object: sync validation only
103
+ */
104
+ function resolveValidateResult(result) {
105
+ var syncResult;
106
+ var asyncResult;
107
+ if (result instanceof Promise) {
108
+ asyncResult = result;
109
+ } else if (Array.isArray(result)) {
110
+ syncResult = result[0];
111
+ asyncResult = result[1];
112
+ } else {
113
+ syncResult = result;
111
114
  }
112
- if (typeof defaultValue === 'string') {
115
+ return {
116
+ syncResult: syncResult ? normalizeValidateResult(syncResult) : undefined,
117
+ asyncResult: asyncResult ? asyncResult.then(normalizeValidateResult) : undefined
118
+ };
119
+ }
120
+ function resolveStandardSchemaPath(issue) {
121
+ if (!issue.path) {
122
+ return [];
123
+ }
124
+ var segments = issue.path.map(segment => typeof segment === 'object' && 'key' in segment ? segment.key : segment);
125
+ if (!segments.every(segment => typeof segment !== 'symbol')) {
126
+ throw new Error('Path segments must not contain symbols. Use strings or numbers instead.');
127
+ }
128
+ return segments;
129
+ }
130
+ function resolveStandardSchemaResult(result) {
131
+ if (!result.issues) {
113
132
  return {
114
- value: defaultValue
133
+ error: null,
134
+ value: result.value
115
135
  };
116
136
  }
117
- if (Array.isArray(defaultValue)) {
118
- if (defaultValue.every(item => typeof item === 'string')) {
119
- return {
120
- options: defaultValue
121
- };
122
- } else {
123
- return {
124
- files: defaultValue
125
- };
137
+ var errorByName = {};
138
+ for (var issue of result.issues) {
139
+ var _errorByName$_name;
140
+ var path = resolveStandardSchemaPath(issue);
141
+ var _name = future.formatPathSegments(path);
142
+ (_errorByName$_name = errorByName[_name]) !== null && _errorByName$_name !== void 0 ? _errorByName$_name : errorByName[_name] = [];
143
+ errorByName[_name].push(issue.message);
144
+ }
145
+ var {
146
+ '': formErrors = []
147
+ } = errorByName,
148
+ fieldErrors = _rollupPluginBabelHelpers.objectWithoutProperties(errorByName, _excluded);
149
+ return {
150
+ error: {
151
+ formErrors,
152
+ fieldErrors
126
153
  }
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Create a copy of the object with the updated properties if there is any change
159
+ */
160
+ function merge(obj, update) {
161
+ if (obj === update || Object.entries(update).every(_ref2 => {
162
+ var [key, value] = _ref2;
163
+ return obj[key] === value;
164
+ })) {
165
+ return obj;
127
166
  }
128
- if (dom.isGlobalInstance(defaultValue, 'File')) {
129
- return {
130
- files: [defaultValue]
131
- };
167
+ return Object.assign({}, obj, update);
168
+ }
169
+
170
+ /**
171
+ * Transforms object keys using a mapping function.
172
+ * Keys mapped to null are filtered out.
173
+ */
174
+ function transformKeys(obj, fn) {
175
+ var result = {};
176
+ for (var [_key, _value] of Object.entries(obj)) {
177
+ var _name2 = fn(_key);
178
+ if (_name2 !== null) {
179
+ result[_name2] = _value;
180
+ }
132
181
  }
133
- if (dom.isGlobalInstance(defaultValue, 'FileList')) {
134
- return {
135
- files: Array.from(defaultValue)
136
- };
182
+ return result;
183
+ }
184
+
185
+ /**
186
+ * Appends item to array only if not already present.
187
+ * Returns original array if item exists, new array if added.
188
+ */
189
+ function appendUniqueItem(list, item) {
190
+ if (list.includes(item)) {
191
+ return list;
192
+ }
193
+ return list.concat(item);
194
+ }
195
+
196
+ /**
197
+ * Maps over array and filters out null results.
198
+ */
199
+ function compactMap(list, fn) {
200
+ var result = [];
201
+ for (var item of list) {
202
+ var _value2 = fn(item);
203
+ if (_value2 !== null) {
204
+ result.push(_value2);
205
+ }
137
206
  }
138
- return {};
207
+ return result;
208
+ }
209
+ function generateUniqueKey() {
210
+ return Math.trunc(Date.now() * Math.random()).toString(36);
139
211
  }
140
212
 
141
- exports.focusable = focusable;
142
- exports.getCheckboxGroupValue = getCheckboxGroupValue;
143
- exports.getDefaultSnapshot = getDefaultSnapshot;
144
- exports.getFormElement = getFormElement;
145
- exports.getInputSnapshot = getInputSnapshot;
146
- exports.getRadioGroupValue = getRadioGroupValue;
147
- exports.initializeField = initializeField;
213
+ exports.appendUniqueItem = appendUniqueItem;
214
+ exports.compactMap = compactMap;
215
+ exports.createPathIndexUpdater = createPathIndexUpdater;
216
+ exports.generateUniqueKey = generateUniqueKey;
217
+ exports.getArrayAtPath = getArrayAtPath;
218
+ exports.isNumber = isNumber;
219
+ exports.isOptional = isOptional;
220
+ exports.isString = isString;
221
+ exports.isUndefined = isUndefined;
222
+ exports.merge = merge;
223
+ exports.normalizeFormError = normalizeFormError;
224
+ exports.normalizeValidateResult = normalizeValidateResult;
225
+ exports.resolveStandardSchemaPath = resolveStandardSchemaPath;
226
+ exports.resolveStandardSchemaResult = resolveStandardSchemaResult;
227
+ exports.resolveValidateResult = resolveValidateResult;
228
+ exports.transformKeys = transformKeys;
229
+ exports.updateValueAtPath = updateValueAtPath;
@@ -1,137 +1,209 @@
1
- import { unstable_updateField, isGlobalInstance } from '@conform-to/dom';
1
+ import { objectWithoutProperties as _objectWithoutProperties } from '../_virtual/_rollupPluginBabelHelpers.mjs';
2
+ import { formatPathSegments, getValueAtPath, isPlainObject, setValueAtPath, getPathSegments } from '@conform-to/dom/future';
2
3
 
3
- function getFormElement(formRef) {
4
- var _element$form;
5
- if (typeof formRef === 'string') {
6
- return document.forms.namedItem(formRef);
7
- }
8
- var element = formRef === null || formRef === void 0 ? void 0 : formRef.current;
9
- if (element instanceof HTMLFormElement) {
10
- return element;
11
- }
12
- return (_element$form = element === null || element === void 0 ? void 0 : element.form) !== null && _element$form !== void 0 ? _element$form : null;
4
+ var _excluded = [""];
5
+ function isUndefined(value) {
6
+ return value === undefined;
13
7
  }
14
- function focusable(element) {
15
- if (!element.hidden && element.type !== 'hidden') {
16
- return;
17
- }
18
-
19
- // Style the element to be visually hidden
20
- element.style.position = 'absolute';
21
- element.style.width = '1px';
22
- element.style.height = '1px';
23
- element.style.padding = '0';
24
- element.style.margin = '-1px';
25
- element.style.overflow = 'hidden';
26
- element.style.clip = 'rect(0,0,0,0)';
27
- element.style.whiteSpace = 'nowrap';
28
- element.style.border = '0';
29
-
30
- // Hide the element from screen readers
31
- element.setAttribute('aria-hidden', 'true');
32
-
33
- // Make sure people won't tab to this element
34
- element.tabIndex = -1;
35
-
36
- // Set the element to be visible again so it can be focused
37
- if (element.hidden) {
38
- element.hidden = false;
39
- }
40
- if (element.type === 'hidden') {
41
- element.setAttribute('type', 'text');
42
- }
8
+ function isString(value) {
9
+ return typeof value === 'string';
10
+ }
11
+ function isNumber(value) {
12
+ return typeof value === 'number';
43
13
  }
44
- function initializeField(element, options) {
45
- var _options$value;
46
- if (element.dataset.conform) {
47
- return;
14
+ function isOptional(value, typeGuard) {
15
+ return isUndefined(value) || typeGuard(value);
16
+ }
17
+ function getArrayAtPath(formValue, name) {
18
+ var _getValueAtPath;
19
+ var value = (_getValueAtPath = getValueAtPath(formValue, name)) !== null && _getValueAtPath !== void 0 ? _getValueAtPath : [];
20
+ if (!Array.isArray(value)) {
21
+ throw new Error("The value of \"".concat(name, "\" is not an array"));
48
22
  }
49
- var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
50
-
51
- // Update the value of the element, including the default value
52
- unstable_updateField(element, {
53
- value: defaultValue,
54
- defaultValue
55
- });
56
- element.dataset.conform = 'initialized';
23
+ return value;
57
24
  }
58
- function getRadioGroupValue(inputs) {
59
- for (var input of inputs) {
60
- if (input.type === 'radio' && input.checked) {
61
- return input.value;
25
+
26
+ /**
27
+ * Immutably updates a value at the specified path.
28
+ * Empty path replaces the entire object.
29
+ */
30
+ function updateValueAtPath(data, name, value) {
31
+ if (name === '') {
32
+ if (!isPlainObject(value)) {
33
+ throw new Error('The value must be an object');
62
34
  }
35
+ return value;
63
36
  }
37
+ return setValueAtPath(data, getPathSegments(name), value, {
38
+ clone: true
39
+ });
64
40
  }
65
- function getCheckboxGroupValue(inputs) {
66
- var values;
67
- for (var input of inputs) {
68
- if (input.type === 'checkbox') {
69
- var _values;
70
- (_values = values) !== null && _values !== void 0 ? _values : values = [];
71
- if (input.checked) {
72
- values.push(input.value);
41
+
42
+ /**
43
+ * Creates a function that updates array indices in field paths.
44
+ * Returns null to remove fields, or updated path with new index.
45
+ */
46
+ function createPathIndexUpdater(listName, update) {
47
+ var listPaths = getPathSegments(listName);
48
+ return name => {
49
+ var paths = getPathSegments(name);
50
+ if (paths.length > listPaths.length && listPaths.every((path, index) => paths[index] === path)) {
51
+ var currentIndex = paths[listPaths.length];
52
+ if (typeof currentIndex === 'number') {
53
+ var newIndex = update(currentIndex);
54
+ if (newIndex === null) {
55
+ // To remove the item instead of updating it
56
+ return null;
57
+ }
58
+ if (newIndex !== currentIndex) {
59
+ // Replace the index
60
+ paths.splice(listPaths.length, 1, newIndex);
61
+ return formatPathSegments(paths);
62
+ }
73
63
  }
74
64
  }
65
+ return name;
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Returns null if error object has no actual error messages,
71
+ * otherwise returns the error as-is.
72
+ */
73
+ function normalizeFormError(error) {
74
+ if (error && error.formErrors.length === 0 && Object.entries(error.fieldErrors).every(_ref => {
75
+ var [, messages] = _ref;
76
+ return Array.isArray(messages) ? messages.length === 0 : !messages;
77
+ })) {
78
+ return null;
75
79
  }
76
- return values;
77
- }
78
- function getInputSnapshot(input) {
79
- if (input instanceof HTMLInputElement) {
80
- switch (input.type) {
81
- case 'file':
82
- return {
83
- files: input.files ? Array.from(input.files) : undefined
84
- };
85
- case 'radio':
86
- case 'checkbox':
87
- return {
88
- value: input.value,
89
- checked: input.checked
90
- };
91
- }
92
- } else if (input instanceof HTMLSelectElement && input.multiple) {
80
+ return error;
81
+ }
82
+ function normalizeValidateResult(result) {
83
+ if (result !== null && 'error' in result) {
93
84
  return {
94
- options: Array.from(input.selectedOptions).map(option => option.value)
85
+ error: normalizeFormError(result.error),
86
+ value: result.value
95
87
  };
96
88
  }
97
89
  return {
98
- value: input.value
90
+ error: normalizeFormError(result)
99
91
  };
100
92
  }
101
- function getDefaultSnapshot(defaultValue, defaultChecked, value) {
102
- if (typeof value === 'string' || typeof defaultChecked === 'boolean') {
103
- return {
104
- value: value !== null && value !== void 0 ? value : 'on',
105
- checked: defaultChecked
106
- };
93
+
94
+ /**
95
+ * Handles different validation result formats:
96
+ * - Promise: async validation only
97
+ * - Array: [syncResult, asyncPromise]
98
+ * - Object: sync validation only
99
+ */
100
+ function resolveValidateResult(result) {
101
+ var syncResult;
102
+ var asyncResult;
103
+ if (result instanceof Promise) {
104
+ asyncResult = result;
105
+ } else if (Array.isArray(result)) {
106
+ syncResult = result[0];
107
+ asyncResult = result[1];
108
+ } else {
109
+ syncResult = result;
107
110
  }
108
- if (typeof defaultValue === 'string') {
111
+ return {
112
+ syncResult: syncResult ? normalizeValidateResult(syncResult) : undefined,
113
+ asyncResult: asyncResult ? asyncResult.then(normalizeValidateResult) : undefined
114
+ };
115
+ }
116
+ function resolveStandardSchemaPath(issue) {
117
+ if (!issue.path) {
118
+ return [];
119
+ }
120
+ var segments = issue.path.map(segment => typeof segment === 'object' && 'key' in segment ? segment.key : segment);
121
+ if (!segments.every(segment => typeof segment !== 'symbol')) {
122
+ throw new Error('Path segments must not contain symbols. Use strings or numbers instead.');
123
+ }
124
+ return segments;
125
+ }
126
+ function resolveStandardSchemaResult(result) {
127
+ if (!result.issues) {
109
128
  return {
110
- value: defaultValue
129
+ error: null,
130
+ value: result.value
111
131
  };
112
132
  }
113
- if (Array.isArray(defaultValue)) {
114
- if (defaultValue.every(item => typeof item === 'string')) {
115
- return {
116
- options: defaultValue
117
- };
118
- } else {
119
- return {
120
- files: defaultValue
121
- };
133
+ var errorByName = {};
134
+ for (var issue of result.issues) {
135
+ var _errorByName$_name;
136
+ var path = resolveStandardSchemaPath(issue);
137
+ var _name = formatPathSegments(path);
138
+ (_errorByName$_name = errorByName[_name]) !== null && _errorByName$_name !== void 0 ? _errorByName$_name : errorByName[_name] = [];
139
+ errorByName[_name].push(issue.message);
140
+ }
141
+ var {
142
+ '': formErrors = []
143
+ } = errorByName,
144
+ fieldErrors = _objectWithoutProperties(errorByName, _excluded);
145
+ return {
146
+ error: {
147
+ formErrors,
148
+ fieldErrors
122
149
  }
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Create a copy of the object with the updated properties if there is any change
155
+ */
156
+ function merge(obj, update) {
157
+ if (obj === update || Object.entries(update).every(_ref2 => {
158
+ var [key, value] = _ref2;
159
+ return obj[key] === value;
160
+ })) {
161
+ return obj;
123
162
  }
124
- if (isGlobalInstance(defaultValue, 'File')) {
125
- return {
126
- files: [defaultValue]
127
- };
163
+ return Object.assign({}, obj, update);
164
+ }
165
+
166
+ /**
167
+ * Transforms object keys using a mapping function.
168
+ * Keys mapped to null are filtered out.
169
+ */
170
+ function transformKeys(obj, fn) {
171
+ var result = {};
172
+ for (var [_key, _value] of Object.entries(obj)) {
173
+ var _name2 = fn(_key);
174
+ if (_name2 !== null) {
175
+ result[_name2] = _value;
176
+ }
128
177
  }
129
- if (isGlobalInstance(defaultValue, 'FileList')) {
130
- return {
131
- files: Array.from(defaultValue)
132
- };
178
+ return result;
179
+ }
180
+
181
+ /**
182
+ * Appends item to array only if not already present.
183
+ * Returns original array if item exists, new array if added.
184
+ */
185
+ function appendUniqueItem(list, item) {
186
+ if (list.includes(item)) {
187
+ return list;
188
+ }
189
+ return list.concat(item);
190
+ }
191
+
192
+ /**
193
+ * Maps over array and filters out null results.
194
+ */
195
+ function compactMap(list, fn) {
196
+ var result = [];
197
+ for (var item of list) {
198
+ var _value2 = fn(item);
199
+ if (_value2 !== null) {
200
+ result.push(_value2);
201
+ }
133
202
  }
134
- return {};
203
+ return result;
204
+ }
205
+ function generateUniqueKey() {
206
+ return Math.trunc(Date.now() * Math.random()).toString(36);
135
207
  }
136
208
 
137
- export { focusable, getCheckboxGroupValue, getDefaultSnapshot, getFormElement, getInputSnapshot, getRadioGroupValue, initializeField };
209
+ export { appendUniqueItem, compactMap, createPathIndexUpdater, generateUniqueKey, getArrayAtPath, isNumber, isOptional, isString, isUndefined, merge, normalizeFormError, normalizeValidateResult, resolveStandardSchemaPath, resolveStandardSchemaResult, resolveValidateResult, transformKeys, updateValueAtPath };