@conform-to/react 1.8.2 → 1.9.1
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/README.md +11 -11
- package/dist/future/dom.d.ts +36 -0
- package/dist/future/dom.js +226 -0
- package/dist/future/dom.mjs +212 -0
- package/dist/future/hooks.d.ts +145 -54
- package/dist/future/hooks.js +566 -38
- package/dist/future/hooks.mjs +550 -33
- package/dist/future/index.d.ts +4 -3
- package/dist/future/index.js +15 -2
- package/dist/future/index.mjs +2 -2
- package/dist/future/intent.d.ts +35 -0
- package/dist/future/intent.js +305 -0
- package/dist/future/intent.mjs +294 -0
- package/dist/future/state.d.ts +57 -0
- package/dist/future/state.js +318 -0
- package/dist/future/state.mjs +300 -0
- package/dist/future/types.d.ts +374 -0
- package/dist/future/util.d.ts +65 -36
- package/dist/future/util.js +198 -116
- package/dist/future/util.mjs +182 -110
- package/package.json +3 -2
- package/dist/future/context.d.ts +0 -15
- package/dist/future/context.js +0 -12
- package/dist/future/context.mjs +0 -8
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ValidationAttributes, type Serialize } from '@conform-to/dom/future';
|
|
2
|
+
import type { DefaultFieldMetadata, Field, FieldName, Fieldset, FormContext, FormMetadata, FormState, FormAction, UnknownIntent, ActionHandler } from './types';
|
|
3
|
+
export declare function initializeState<ErrorShape>(): FormState<ErrorShape>;
|
|
4
|
+
/**
|
|
5
|
+
* Updates form state based on action type:
|
|
6
|
+
* - Client actions: update intended value and client errors
|
|
7
|
+
* - Server actions: update server errors and clear client errors
|
|
8
|
+
* - Initialize: set initial intended value
|
|
9
|
+
*/
|
|
10
|
+
export declare function updateState<ErrorShape>(state: FormState<ErrorShape>, action: FormAction<ErrorShape, UnknownIntent | null, {
|
|
11
|
+
handlers: Record<string, ActionHandler>;
|
|
12
|
+
reset: () => FormState<ErrorShape>;
|
|
13
|
+
}>): FormState<ErrorShape>;
|
|
14
|
+
export declare function getDefaultValue(context: FormContext<any>, name: string, serialize?: Serialize): string | undefined;
|
|
15
|
+
export declare function getDefaultOptions(context: FormContext<any>, name: string, serialize?: Serialize): string[] | undefined;
|
|
16
|
+
export declare function isDefaultChecked(context: FormContext<any>, name: string, serialize?: Serialize): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Determine if the field is touched
|
|
19
|
+
*
|
|
20
|
+
* This checks if the field is in the list of touched fields,
|
|
21
|
+
* or if there is any child field that is touched, i.e. form / fieldset
|
|
22
|
+
*/
|
|
23
|
+
export declare function isTouched(state: FormState<any>, name?: string): boolean;
|
|
24
|
+
export declare function getDefaultListKey(prefix: string, initialValue: Record<string, unknown> | null, name: string): string[];
|
|
25
|
+
export declare function getListKey(context: FormContext<any>, name: string): string[];
|
|
26
|
+
export declare function getErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): ErrorShape[] | undefined;
|
|
27
|
+
export declare function getFieldErrors<ErrorShape>(state: FormState<ErrorShape>, name?: string): Record<string, ErrorShape[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
30
|
+
* e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getConstraint(context: FormContext<any>, name: string): ValidationAttributes | undefined;
|
|
33
|
+
export declare function getFormMetadata<ErrorShape>(context: FormContext<ErrorShape>, options: {
|
|
34
|
+
serialize: Serialize;
|
|
35
|
+
}): FormMetadata<ErrorShape, DefaultFieldMetadata<ErrorShape>>;
|
|
36
|
+
export declare function getField<FieldShape, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
37
|
+
name: FieldName<FieldShape>;
|
|
38
|
+
serialize: Serialize;
|
|
39
|
+
key?: string;
|
|
40
|
+
}): Field<FieldShape, DefaultFieldMetadata<ErrorShape>>;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a proxy that dynamically generates field objects when properties are accessed.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getFieldset<FieldShape = Record<string, any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
45
|
+
name?: FieldName<FieldShape>;
|
|
46
|
+
serialize: Serialize;
|
|
47
|
+
}): Fieldset<FieldShape, DefaultFieldMetadata<ErrorShape>>;
|
|
48
|
+
/**
|
|
49
|
+
* Creates an array of field objects for list/array inputs
|
|
50
|
+
*/
|
|
51
|
+
export declare function getFieldList<FieldShape = Array<any>, ErrorShape = string>(context: FormContext<ErrorShape>, options: {
|
|
52
|
+
name: FieldName<FieldShape>;
|
|
53
|
+
serialize: Serialize;
|
|
54
|
+
}): Field<[
|
|
55
|
+
FieldShape
|
|
56
|
+
] extends [Array<infer ItemShape> | null | undefined] ? ItemShape : unknown, DefaultFieldMetadata<ErrorShape>>[];
|
|
57
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
+
var future = require('@conform-to/dom/future');
|
|
7
|
+
var util = require('./util.js');
|
|
8
|
+
|
|
9
|
+
function initializeState() {
|
|
10
|
+
return {
|
|
11
|
+
resetKey: util.generateUniqueKey(),
|
|
12
|
+
listKeys: {},
|
|
13
|
+
intendedValue: null,
|
|
14
|
+
serverValidatedValue: null,
|
|
15
|
+
serverError: null,
|
|
16
|
+
clientError: null,
|
|
17
|
+
touchedFields: []
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Updates form state based on action type:
|
|
23
|
+
* - Client actions: update intended value and client errors
|
|
24
|
+
* - Server actions: update server errors and clear client errors
|
|
25
|
+
* - Initialize: set initial intended value
|
|
26
|
+
*/
|
|
27
|
+
function updateState(state, action) {
|
|
28
|
+
var _action$intendedValue, _action$intendedValue2;
|
|
29
|
+
if (action.intendedValue === null) {
|
|
30
|
+
return action.ctx.reset();
|
|
31
|
+
}
|
|
32
|
+
var value = (_action$intendedValue = action.intendedValue) !== null && _action$intendedValue !== void 0 ? _action$intendedValue : action.submission.payload;
|
|
33
|
+
|
|
34
|
+
// Apply the form error and intended value from the result first
|
|
35
|
+
state = action.type === 'client' ? util.merge(state, {
|
|
36
|
+
intendedValue: !action.intent ? value : (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.intendedValue,
|
|
37
|
+
// Update client error only if the error is different from the previous one to minimize unnecessary re-renders
|
|
38
|
+
clientError: typeof action.error !== 'undefined' && !future.deepEqual(state.clientError, action.error) ? action.error : state.clientError,
|
|
39
|
+
// Reset server error if form value is changed
|
|
40
|
+
serverError: typeof action.error !== 'undefined' && !future.deepEqual(state.serverValidatedValue, value) ? null : state.serverError
|
|
41
|
+
}) : util.merge(state, {
|
|
42
|
+
intendedValue: action.type === 'initialize' ? value : state.intendedValue,
|
|
43
|
+
// Clear client error to avoid showing stale errors
|
|
44
|
+
clientError: null,
|
|
45
|
+
// Update server error if the error is defined.
|
|
46
|
+
// There is no need to check if the error is different as we are updating other states as well
|
|
47
|
+
serverError: typeof action.error !== 'undefined' ? action.error : state.serverError,
|
|
48
|
+
// Keep track of the value that the serverError is based on
|
|
49
|
+
serverValidatedValue: typeof action.error !== 'undefined' ? value : state.serverValidatedValue
|
|
50
|
+
});
|
|
51
|
+
if (action.type !== 'server' && typeof action.intent !== 'undefined') {
|
|
52
|
+
var _action$intent, _action$ctx$handlers;
|
|
53
|
+
// Validate the whole form if no intent is provided (default submission)
|
|
54
|
+
var intent = (_action$intent = action.intent) !== null && _action$intent !== void 0 ? _action$intent : {
|
|
55
|
+
type: 'validate'
|
|
56
|
+
};
|
|
57
|
+
var handler = (_action$ctx$handlers = action.ctx.handlers) === null || _action$ctx$handlers === void 0 ? void 0 : _action$ctx$handlers[intent.type];
|
|
58
|
+
if (typeof (handler === null || handler === void 0 ? void 0 : handler.onUpdate) === 'function') {
|
|
59
|
+
var _handler$validatePayl, _handler$validatePayl2;
|
|
60
|
+
if ((_handler$validatePayl = (_handler$validatePayl2 = handler.validatePayload) === null || _handler$validatePayl2 === void 0 ? void 0 : _handler$validatePayl2.call(handler, intent.payload)) !== null && _handler$validatePayl !== void 0 ? _handler$validatePayl : true) {
|
|
61
|
+
return handler.onUpdate(state, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, action), {}, {
|
|
62
|
+
intent: {
|
|
63
|
+
type: intent.type,
|
|
64
|
+
payload: intent.payload
|
|
65
|
+
}
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return state;
|
|
71
|
+
}
|
|
72
|
+
function getDefaultValue(context, name) {
|
|
73
|
+
var _ref, _context$state$intend;
|
|
74
|
+
var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
|
|
75
|
+
var value = future.getValueAtPath((_ref = (_context$state$intend = context.state.intendedValue) !== null && _context$state$intend !== void 0 ? _context$state$intend : context.defaultValue) !== null && _ref !== void 0 ? _ref : {}, name);
|
|
76
|
+
var serializedValue = serialize(value);
|
|
77
|
+
if (typeof serializedValue === 'string') {
|
|
78
|
+
return serializedValue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function getDefaultOptions(context, name) {
|
|
82
|
+
var _ref2, _context$state$intend2;
|
|
83
|
+
var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
|
|
84
|
+
var value = future.getValueAtPath((_ref2 = (_context$state$intend2 = context.state.intendedValue) !== null && _context$state$intend2 !== void 0 ? _context$state$intend2 : context.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {}, name);
|
|
85
|
+
var serializedValue = typeof value !== 'undefined' ? serialize(value) : undefined;
|
|
86
|
+
if (Array.isArray(serializedValue) && serializedValue.every(item => typeof item === 'string')) {
|
|
87
|
+
return serializedValue;
|
|
88
|
+
}
|
|
89
|
+
if (typeof serializedValue === 'string') {
|
|
90
|
+
return [serializedValue];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isDefaultChecked(context, name) {
|
|
94
|
+
var _ref3, _context$state$intend3;
|
|
95
|
+
var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
|
|
96
|
+
var value = future.getValueAtPath((_ref3 = (_context$state$intend3 = context.state.intendedValue) !== null && _context$state$intend3 !== void 0 ? _context$state$intend3 : context.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : {}, name);
|
|
97
|
+
var serializedValue = typeof value !== 'undefined' ? serialize(value) : undefined;
|
|
98
|
+
return serializedValue === 'on';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Determine if the field is touched
|
|
103
|
+
*
|
|
104
|
+
* This checks if the field is in the list of touched fields,
|
|
105
|
+
* or if there is any child field that is touched, i.e. form / fieldset
|
|
106
|
+
*/
|
|
107
|
+
function isTouched(state) {
|
|
108
|
+
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
109
|
+
if (state.touchedFields.includes(name)) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
var paths = future.getPathSegments(name);
|
|
113
|
+
return state.touchedFields.some(field => field !== name && future.getRelativePath(field, paths) !== null);
|
|
114
|
+
}
|
|
115
|
+
function getDefaultListKey(prefix, initialValue, name) {
|
|
116
|
+
return util.getArrayAtPath(initialValue, name).map((_, index) => "".concat(prefix, "-").concat(future.appendPathSegment(name, index)));
|
|
117
|
+
}
|
|
118
|
+
function getListKey(context, name) {
|
|
119
|
+
var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
|
|
120
|
+
return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
|
|
121
|
+
}
|
|
122
|
+
function getErrors(state, name) {
|
|
123
|
+
var _state$serverError;
|
|
124
|
+
var error = (_state$serverError = state.serverError) !== null && _state$serverError !== void 0 ? _state$serverError : state.clientError;
|
|
125
|
+
if (!error || !isTouched(state, name)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
var errors = name ? error.fieldErrors[name] : error.formErrors;
|
|
129
|
+
if (errors && errors.length > 0) {
|
|
130
|
+
return errors;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function getFieldErrors(state, name) {
|
|
134
|
+
var result = {};
|
|
135
|
+
var basePath = future.getPathSegments(name);
|
|
136
|
+
for (var field of state.touchedFields) {
|
|
137
|
+
var relativePath = future.getRelativePath(field, basePath);
|
|
138
|
+
if (!relativePath || relativePath.length === 0) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
var error = getErrors(state, field);
|
|
142
|
+
if (typeof error !== 'undefined') {
|
|
143
|
+
result[future.formatPathSegments(relativePath)] = error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
151
|
+
* e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
|
|
152
|
+
*/
|
|
153
|
+
function getConstraint(context, name) {
|
|
154
|
+
var _context$constraint;
|
|
155
|
+
var constraint = (_context$constraint = context.constraint) === null || _context$constraint === void 0 ? void 0 : _context$constraint[name];
|
|
156
|
+
if (!constraint) {
|
|
157
|
+
var path = future.getPathSegments(name);
|
|
158
|
+
for (var i = path.length - 1; i >= 0; i--) {
|
|
159
|
+
var segment = path[i];
|
|
160
|
+
// Try searching a less specific path for the constraint
|
|
161
|
+
// e.g. `array[0].anotherArray[1].key` -> `array[0].anotherArray[].key` -> `array[].anotherArray[].key`
|
|
162
|
+
if (typeof segment === 'number') {
|
|
163
|
+
// This overrides the current number segment with an empty string
|
|
164
|
+
// which will be treated as an empty bracket
|
|
165
|
+
path[i] = '';
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
var alternative = future.formatPathSegments(path);
|
|
170
|
+
if (name !== alternative) {
|
|
171
|
+
constraint = getConstraint(context, alternative);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return constraint;
|
|
175
|
+
}
|
|
176
|
+
function getFormMetadata(context, options) {
|
|
177
|
+
return {
|
|
178
|
+
key: context.state.resetKey,
|
|
179
|
+
id: context.formId,
|
|
180
|
+
errorId: "".concat(context.formId, "-form-error"),
|
|
181
|
+
descriptionId: "".concat(context.formId, "-form-description"),
|
|
182
|
+
get errors() {
|
|
183
|
+
return getErrors(context.state);
|
|
184
|
+
},
|
|
185
|
+
get fieldErrors() {
|
|
186
|
+
return getFieldErrors(context.state);
|
|
187
|
+
},
|
|
188
|
+
get touched() {
|
|
189
|
+
return isTouched(context.state);
|
|
190
|
+
},
|
|
191
|
+
get valid() {
|
|
192
|
+
return typeof getErrors(context.state) === 'undefined';
|
|
193
|
+
},
|
|
194
|
+
get invalid() {
|
|
195
|
+
return !this.valid;
|
|
196
|
+
},
|
|
197
|
+
props: {
|
|
198
|
+
id: context.formId,
|
|
199
|
+
onSubmit: context.handleSubmit,
|
|
200
|
+
onInput: context.handleInput,
|
|
201
|
+
onBlur: context.handleBlur,
|
|
202
|
+
noValidate: true
|
|
203
|
+
},
|
|
204
|
+
context,
|
|
205
|
+
getField(name) {
|
|
206
|
+
return getField(context, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
207
|
+
name
|
|
208
|
+
}));
|
|
209
|
+
},
|
|
210
|
+
getFieldset(name) {
|
|
211
|
+
return getFieldset(context, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
212
|
+
name
|
|
213
|
+
}));
|
|
214
|
+
},
|
|
215
|
+
getFieldList(name) {
|
|
216
|
+
return getFieldList(context, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
217
|
+
name
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function getField(context, options) {
|
|
223
|
+
var id = "".concat(context.formId, "-field-").concat(options.name.replace(/[^a-zA-Z0-9._-]/g, '_'));
|
|
224
|
+
var constraint = getConstraint(context, options.name);
|
|
225
|
+
var metadata = {
|
|
226
|
+
id: id,
|
|
227
|
+
descriptionId: "".concat(id, "-description"),
|
|
228
|
+
errorId: "".concat(id, "-error"),
|
|
229
|
+
formId: context.formId,
|
|
230
|
+
required: constraint === null || constraint === void 0 ? void 0 : constraint.required,
|
|
231
|
+
minLength: constraint === null || constraint === void 0 ? void 0 : constraint.minLength,
|
|
232
|
+
maxLength: constraint === null || constraint === void 0 ? void 0 : constraint.maxLength,
|
|
233
|
+
pattern: constraint === null || constraint === void 0 ? void 0 : constraint.pattern,
|
|
234
|
+
min: constraint === null || constraint === void 0 ? void 0 : constraint.min,
|
|
235
|
+
max: constraint === null || constraint === void 0 ? void 0 : constraint.max,
|
|
236
|
+
step: constraint === null || constraint === void 0 ? void 0 : constraint.step,
|
|
237
|
+
multiple: constraint === null || constraint === void 0 ? void 0 : constraint.multiple,
|
|
238
|
+
get defaultValue() {
|
|
239
|
+
return getDefaultValue(context, options.name, options.serialize);
|
|
240
|
+
},
|
|
241
|
+
get defaultOptions() {
|
|
242
|
+
return getDefaultOptions(context, options.name, options.serialize);
|
|
243
|
+
},
|
|
244
|
+
get defaultChecked() {
|
|
245
|
+
return isDefaultChecked(context, options.name, options.serialize);
|
|
246
|
+
},
|
|
247
|
+
get touched() {
|
|
248
|
+
return isTouched(context.state, options.name);
|
|
249
|
+
},
|
|
250
|
+
get valid() {
|
|
251
|
+
return typeof getErrors(context.state, options.name) === 'undefined';
|
|
252
|
+
},
|
|
253
|
+
get invalid() {
|
|
254
|
+
return !this.valid;
|
|
255
|
+
},
|
|
256
|
+
get errors() {
|
|
257
|
+
return getErrors(context.state, options.name);
|
|
258
|
+
},
|
|
259
|
+
get fieldErrors() {
|
|
260
|
+
return getFieldErrors(context.state, options.name);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
return Object.assign(metadata, {
|
|
264
|
+
key: options.key,
|
|
265
|
+
name: options.name,
|
|
266
|
+
getFieldset() {
|
|
267
|
+
return getFieldset(context, options);
|
|
268
|
+
},
|
|
269
|
+
getFieldList() {
|
|
270
|
+
return getFieldList(context, options);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Creates a proxy that dynamically generates field objects when properties are accessed.
|
|
277
|
+
*/
|
|
278
|
+
function getFieldset(context, options) {
|
|
279
|
+
return new Proxy({}, {
|
|
280
|
+
get(target, name, receiver) {
|
|
281
|
+
if (typeof name === 'string') {
|
|
282
|
+
return getField(context, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
283
|
+
name: future.appendPathSegment(options === null || options === void 0 ? void 0 : options.name, name)
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
return Reflect.get(target, name, receiver);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Creates an array of field objects for list/array inputs
|
|
293
|
+
*/
|
|
294
|
+
function getFieldList(context, options) {
|
|
295
|
+
var keys = getListKey(context, options.name);
|
|
296
|
+
return keys.map((key, index) => {
|
|
297
|
+
return getField(context, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
|
|
298
|
+
name: future.appendPathSegment(options.name, index),
|
|
299
|
+
key
|
|
300
|
+
}));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
exports.getConstraint = getConstraint;
|
|
305
|
+
exports.getDefaultListKey = getDefaultListKey;
|
|
306
|
+
exports.getDefaultOptions = getDefaultOptions;
|
|
307
|
+
exports.getDefaultValue = getDefaultValue;
|
|
308
|
+
exports.getErrors = getErrors;
|
|
309
|
+
exports.getField = getField;
|
|
310
|
+
exports.getFieldErrors = getFieldErrors;
|
|
311
|
+
exports.getFieldList = getFieldList;
|
|
312
|
+
exports.getFieldset = getFieldset;
|
|
313
|
+
exports.getFormMetadata = getFormMetadata;
|
|
314
|
+
exports.getListKey = getListKey;
|
|
315
|
+
exports.initializeState = initializeState;
|
|
316
|
+
exports.isDefaultChecked = isDefaultChecked;
|
|
317
|
+
exports.isTouched = isTouched;
|
|
318
|
+
exports.updateState = updateState;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
+
import { getPathSegments, getRelativePath, appendPathSegment, deepEqual, getValueAtPath, formatPathSegments, serialize } from '@conform-to/dom/future';
|
|
3
|
+
import { generateUniqueKey, merge, getArrayAtPath } from './util.mjs';
|
|
4
|
+
|
|
5
|
+
function initializeState() {
|
|
6
|
+
return {
|
|
7
|
+
resetKey: generateUniqueKey(),
|
|
8
|
+
listKeys: {},
|
|
9
|
+
intendedValue: null,
|
|
10
|
+
serverValidatedValue: null,
|
|
11
|
+
serverError: null,
|
|
12
|
+
clientError: null,
|
|
13
|
+
touchedFields: []
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Updates form state based on action type:
|
|
19
|
+
* - Client actions: update intended value and client errors
|
|
20
|
+
* - Server actions: update server errors and clear client errors
|
|
21
|
+
* - Initialize: set initial intended value
|
|
22
|
+
*/
|
|
23
|
+
function updateState(state, action) {
|
|
24
|
+
var _action$intendedValue, _action$intendedValue2;
|
|
25
|
+
if (action.intendedValue === null) {
|
|
26
|
+
return action.ctx.reset();
|
|
27
|
+
}
|
|
28
|
+
var value = (_action$intendedValue = action.intendedValue) !== null && _action$intendedValue !== void 0 ? _action$intendedValue : action.submission.payload;
|
|
29
|
+
|
|
30
|
+
// Apply the form error and intended value from the result first
|
|
31
|
+
state = action.type === 'client' ? merge(state, {
|
|
32
|
+
intendedValue: !action.intent ? value : (_action$intendedValue2 = action.intendedValue) !== null && _action$intendedValue2 !== void 0 ? _action$intendedValue2 : state.intendedValue,
|
|
33
|
+
// Update client error only if the error is different from the previous one to minimize unnecessary re-renders
|
|
34
|
+
clientError: typeof action.error !== 'undefined' && !deepEqual(state.clientError, action.error) ? action.error : state.clientError,
|
|
35
|
+
// Reset server error if form value is changed
|
|
36
|
+
serverError: typeof action.error !== 'undefined' && !deepEqual(state.serverValidatedValue, value) ? null : state.serverError
|
|
37
|
+
}) : merge(state, {
|
|
38
|
+
intendedValue: action.type === 'initialize' ? value : state.intendedValue,
|
|
39
|
+
// Clear client error to avoid showing stale errors
|
|
40
|
+
clientError: null,
|
|
41
|
+
// Update server error if the error is defined.
|
|
42
|
+
// There is no need to check if the error is different as we are updating other states as well
|
|
43
|
+
serverError: typeof action.error !== 'undefined' ? action.error : state.serverError,
|
|
44
|
+
// Keep track of the value that the serverError is based on
|
|
45
|
+
serverValidatedValue: typeof action.error !== 'undefined' ? value : state.serverValidatedValue
|
|
46
|
+
});
|
|
47
|
+
if (action.type !== 'server' && typeof action.intent !== 'undefined') {
|
|
48
|
+
var _action$intent, _action$ctx$handlers;
|
|
49
|
+
// Validate the whole form if no intent is provided (default submission)
|
|
50
|
+
var intent = (_action$intent = action.intent) !== null && _action$intent !== void 0 ? _action$intent : {
|
|
51
|
+
type: 'validate'
|
|
52
|
+
};
|
|
53
|
+
var handler = (_action$ctx$handlers = action.ctx.handlers) === null || _action$ctx$handlers === void 0 ? void 0 : _action$ctx$handlers[intent.type];
|
|
54
|
+
if (typeof (handler === null || handler === void 0 ? void 0 : handler.onUpdate) === 'function') {
|
|
55
|
+
var _handler$validatePayl, _handler$validatePayl2;
|
|
56
|
+
if ((_handler$validatePayl = (_handler$validatePayl2 = handler.validatePayload) === null || _handler$validatePayl2 === void 0 ? void 0 : _handler$validatePayl2.call(handler, intent.payload)) !== null && _handler$validatePayl !== void 0 ? _handler$validatePayl : true) {
|
|
57
|
+
return handler.onUpdate(state, _objectSpread2(_objectSpread2({}, action), {}, {
|
|
58
|
+
intent: {
|
|
59
|
+
type: intent.type,
|
|
60
|
+
payload: intent.payload
|
|
61
|
+
}
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return state;
|
|
67
|
+
}
|
|
68
|
+
function getDefaultValue(context, name) {
|
|
69
|
+
var _ref, _context$state$intend;
|
|
70
|
+
var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
|
|
71
|
+
var value = getValueAtPath((_ref = (_context$state$intend = context.state.intendedValue) !== null && _context$state$intend !== void 0 ? _context$state$intend : context.defaultValue) !== null && _ref !== void 0 ? _ref : {}, name);
|
|
72
|
+
var serializedValue = serialize$1(value);
|
|
73
|
+
if (typeof serializedValue === 'string') {
|
|
74
|
+
return serializedValue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getDefaultOptions(context, name) {
|
|
78
|
+
var _ref2, _context$state$intend2;
|
|
79
|
+
var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
|
|
80
|
+
var value = getValueAtPath((_ref2 = (_context$state$intend2 = context.state.intendedValue) !== null && _context$state$intend2 !== void 0 ? _context$state$intend2 : context.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {}, name);
|
|
81
|
+
var serializedValue = typeof value !== 'undefined' ? serialize$1(value) : undefined;
|
|
82
|
+
if (Array.isArray(serializedValue) && serializedValue.every(item => typeof item === 'string')) {
|
|
83
|
+
return serializedValue;
|
|
84
|
+
}
|
|
85
|
+
if (typeof serializedValue === 'string') {
|
|
86
|
+
return [serializedValue];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function isDefaultChecked(context, name) {
|
|
90
|
+
var _ref3, _context$state$intend3;
|
|
91
|
+
var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
|
|
92
|
+
var value = getValueAtPath((_ref3 = (_context$state$intend3 = context.state.intendedValue) !== null && _context$state$intend3 !== void 0 ? _context$state$intend3 : context.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : {}, name);
|
|
93
|
+
var serializedValue = typeof value !== 'undefined' ? serialize$1(value) : undefined;
|
|
94
|
+
return serializedValue === 'on';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Determine if the field is touched
|
|
99
|
+
*
|
|
100
|
+
* This checks if the field is in the list of touched fields,
|
|
101
|
+
* or if there is any child field that is touched, i.e. form / fieldset
|
|
102
|
+
*/
|
|
103
|
+
function isTouched(state) {
|
|
104
|
+
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
|
|
105
|
+
if (state.touchedFields.includes(name)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
var paths = getPathSegments(name);
|
|
109
|
+
return state.touchedFields.some(field => field !== name && getRelativePath(field, paths) !== null);
|
|
110
|
+
}
|
|
111
|
+
function getDefaultListKey(prefix, initialValue, name) {
|
|
112
|
+
return getArrayAtPath(initialValue, name).map((_, index) => "".concat(prefix, "-").concat(appendPathSegment(name, index)));
|
|
113
|
+
}
|
|
114
|
+
function getListKey(context, name) {
|
|
115
|
+
var _context$state$listKe, _context$state$listKe2, _context$state$intend4;
|
|
116
|
+
return (_context$state$listKe = (_context$state$listKe2 = context.state.listKeys) === null || _context$state$listKe2 === void 0 ? void 0 : _context$state$listKe2[name]) !== null && _context$state$listKe !== void 0 ? _context$state$listKe : getDefaultListKey(context.state.resetKey, (_context$state$intend4 = context.state.intendedValue) !== null && _context$state$intend4 !== void 0 ? _context$state$intend4 : context.defaultValue, name);
|
|
117
|
+
}
|
|
118
|
+
function getErrors(state, name) {
|
|
119
|
+
var _state$serverError;
|
|
120
|
+
var error = (_state$serverError = state.serverError) !== null && _state$serverError !== void 0 ? _state$serverError : state.clientError;
|
|
121
|
+
if (!error || !isTouched(state, name)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
var errors = name ? error.fieldErrors[name] : error.formErrors;
|
|
125
|
+
if (errors && errors.length > 0) {
|
|
126
|
+
return errors;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function getFieldErrors(state, name) {
|
|
130
|
+
var result = {};
|
|
131
|
+
var basePath = getPathSegments(name);
|
|
132
|
+
for (var field of state.touchedFields) {
|
|
133
|
+
var relativePath = getRelativePath(field, basePath);
|
|
134
|
+
if (!relativePath || relativePath.length === 0) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
var error = getErrors(state, field);
|
|
138
|
+
if (typeof error !== 'undefined') {
|
|
139
|
+
result[formatPathSegments(relativePath)] = error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Gets validation constraint for a field, with fallback to parent array patterns.
|
|
147
|
+
* e.g. "array[0].key" falls back to "array[].key" if specific constraint not found.
|
|
148
|
+
*/
|
|
149
|
+
function getConstraint(context, name) {
|
|
150
|
+
var _context$constraint;
|
|
151
|
+
var constraint = (_context$constraint = context.constraint) === null || _context$constraint === void 0 ? void 0 : _context$constraint[name];
|
|
152
|
+
if (!constraint) {
|
|
153
|
+
var path = getPathSegments(name);
|
|
154
|
+
for (var i = path.length - 1; i >= 0; i--) {
|
|
155
|
+
var segment = path[i];
|
|
156
|
+
// Try searching a less specific path for the constraint
|
|
157
|
+
// e.g. `array[0].anotherArray[1].key` -> `array[0].anotherArray[].key` -> `array[].anotherArray[].key`
|
|
158
|
+
if (typeof segment === 'number') {
|
|
159
|
+
// This overrides the current number segment with an empty string
|
|
160
|
+
// which will be treated as an empty bracket
|
|
161
|
+
path[i] = '';
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
var alternative = formatPathSegments(path);
|
|
166
|
+
if (name !== alternative) {
|
|
167
|
+
constraint = getConstraint(context, alternative);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return constraint;
|
|
171
|
+
}
|
|
172
|
+
function getFormMetadata(context, options) {
|
|
173
|
+
return {
|
|
174
|
+
key: context.state.resetKey,
|
|
175
|
+
id: context.formId,
|
|
176
|
+
errorId: "".concat(context.formId, "-form-error"),
|
|
177
|
+
descriptionId: "".concat(context.formId, "-form-description"),
|
|
178
|
+
get errors() {
|
|
179
|
+
return getErrors(context.state);
|
|
180
|
+
},
|
|
181
|
+
get fieldErrors() {
|
|
182
|
+
return getFieldErrors(context.state);
|
|
183
|
+
},
|
|
184
|
+
get touched() {
|
|
185
|
+
return isTouched(context.state);
|
|
186
|
+
},
|
|
187
|
+
get valid() {
|
|
188
|
+
return typeof getErrors(context.state) === 'undefined';
|
|
189
|
+
},
|
|
190
|
+
get invalid() {
|
|
191
|
+
return !this.valid;
|
|
192
|
+
},
|
|
193
|
+
props: {
|
|
194
|
+
id: context.formId,
|
|
195
|
+
onSubmit: context.handleSubmit,
|
|
196
|
+
onInput: context.handleInput,
|
|
197
|
+
onBlur: context.handleBlur,
|
|
198
|
+
noValidate: true
|
|
199
|
+
},
|
|
200
|
+
context,
|
|
201
|
+
getField(name) {
|
|
202
|
+
return getField(context, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
203
|
+
name
|
|
204
|
+
}));
|
|
205
|
+
},
|
|
206
|
+
getFieldset(name) {
|
|
207
|
+
return getFieldset(context, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
208
|
+
name
|
|
209
|
+
}));
|
|
210
|
+
},
|
|
211
|
+
getFieldList(name) {
|
|
212
|
+
return getFieldList(context, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
213
|
+
name
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function getField(context, options) {
|
|
219
|
+
var id = "".concat(context.formId, "-field-").concat(options.name.replace(/[^a-zA-Z0-9._-]/g, '_'));
|
|
220
|
+
var constraint = getConstraint(context, options.name);
|
|
221
|
+
var metadata = {
|
|
222
|
+
id: id,
|
|
223
|
+
descriptionId: "".concat(id, "-description"),
|
|
224
|
+
errorId: "".concat(id, "-error"),
|
|
225
|
+
formId: context.formId,
|
|
226
|
+
required: constraint === null || constraint === void 0 ? void 0 : constraint.required,
|
|
227
|
+
minLength: constraint === null || constraint === void 0 ? void 0 : constraint.minLength,
|
|
228
|
+
maxLength: constraint === null || constraint === void 0 ? void 0 : constraint.maxLength,
|
|
229
|
+
pattern: constraint === null || constraint === void 0 ? void 0 : constraint.pattern,
|
|
230
|
+
min: constraint === null || constraint === void 0 ? void 0 : constraint.min,
|
|
231
|
+
max: constraint === null || constraint === void 0 ? void 0 : constraint.max,
|
|
232
|
+
step: constraint === null || constraint === void 0 ? void 0 : constraint.step,
|
|
233
|
+
multiple: constraint === null || constraint === void 0 ? void 0 : constraint.multiple,
|
|
234
|
+
get defaultValue() {
|
|
235
|
+
return getDefaultValue(context, options.name, options.serialize);
|
|
236
|
+
},
|
|
237
|
+
get defaultOptions() {
|
|
238
|
+
return getDefaultOptions(context, options.name, options.serialize);
|
|
239
|
+
},
|
|
240
|
+
get defaultChecked() {
|
|
241
|
+
return isDefaultChecked(context, options.name, options.serialize);
|
|
242
|
+
},
|
|
243
|
+
get touched() {
|
|
244
|
+
return isTouched(context.state, options.name);
|
|
245
|
+
},
|
|
246
|
+
get valid() {
|
|
247
|
+
return typeof getErrors(context.state, options.name) === 'undefined';
|
|
248
|
+
},
|
|
249
|
+
get invalid() {
|
|
250
|
+
return !this.valid;
|
|
251
|
+
},
|
|
252
|
+
get errors() {
|
|
253
|
+
return getErrors(context.state, options.name);
|
|
254
|
+
},
|
|
255
|
+
get fieldErrors() {
|
|
256
|
+
return getFieldErrors(context.state, options.name);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
return Object.assign(metadata, {
|
|
260
|
+
key: options.key,
|
|
261
|
+
name: options.name,
|
|
262
|
+
getFieldset() {
|
|
263
|
+
return getFieldset(context, options);
|
|
264
|
+
},
|
|
265
|
+
getFieldList() {
|
|
266
|
+
return getFieldList(context, options);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Creates a proxy that dynamically generates field objects when properties are accessed.
|
|
273
|
+
*/
|
|
274
|
+
function getFieldset(context, options) {
|
|
275
|
+
return new Proxy({}, {
|
|
276
|
+
get(target, name, receiver) {
|
|
277
|
+
if (typeof name === 'string') {
|
|
278
|
+
return getField(context, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
279
|
+
name: appendPathSegment(options === null || options === void 0 ? void 0 : options.name, name)
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
return Reflect.get(target, name, receiver);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Creates an array of field objects for list/array inputs
|
|
289
|
+
*/
|
|
290
|
+
function getFieldList(context, options) {
|
|
291
|
+
var keys = getListKey(context, options.name);
|
|
292
|
+
return keys.map((key, index) => {
|
|
293
|
+
return getField(context, _objectSpread2(_objectSpread2({}, options), {}, {
|
|
294
|
+
name: appendPathSegment(options.name, index),
|
|
295
|
+
key
|
|
296
|
+
}));
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { getConstraint, getDefaultListKey, getDefaultOptions, getDefaultValue, getErrors, getField, getFieldErrors, getFieldList, getFieldset, getFormMetadata, getListKey, initializeState, isDefaultChecked, isTouched, updateState };
|