@conform-to/react 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/README.md +1 -1
- 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 +56 -0
- package/dist/future/state.js +300 -0
- package/dist/future/state.mjs +283 -0
- package/dist/future/types.d.ts +360 -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
package/dist/future/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export type { Control } from './
|
|
1
|
+
export type { FormValue, FormError } from '@conform-to/dom/future';
|
|
2
|
+
export { parseSubmission, report, isDirty } from '@conform-to/dom/future';
|
|
3
|
+
export type { Control, FormOptions, Fieldset, DefaultValue } from './types';
|
|
4
|
+
export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent, } from './hooks';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/future/index.js
CHANGED
|
@@ -2,14 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var future = require('@conform-to/dom/future');
|
|
6
6
|
var hooks = require('./hooks.js');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
Object.defineProperty(exports, 'isDirty', {
|
|
11
11
|
enumerable: true,
|
|
12
|
-
get: function () { return
|
|
12
|
+
get: function () { return future.isDirty; }
|
|
13
13
|
});
|
|
14
|
+
Object.defineProperty(exports, 'parseSubmission', {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return future.parseSubmission; }
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, 'report', {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return future.report; }
|
|
21
|
+
});
|
|
22
|
+
exports.FormProvider = hooks.FormProvider;
|
|
14
23
|
exports.useControl = hooks.useControl;
|
|
24
|
+
exports.useField = hooks.useField;
|
|
25
|
+
exports.useForm = hooks.useForm;
|
|
15
26
|
exports.useFormData = hooks.useFormData;
|
|
27
|
+
exports.useFormMetadata = hooks.useFormMetadata;
|
|
28
|
+
exports.useIntent = hooks.useIntent;
|
package/dist/future/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { useControl, useFormData } from './hooks.mjs';
|
|
1
|
+
export { isDirty, parseSubmission, report } from '@conform-to/dom/future';
|
|
2
|
+
export { FormProvider, useControl, useField, useForm, useFormData, useFormMetadata, useIntent } from './hooks.mjs';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FormValue, Submission } from '@conform-to/dom/future';
|
|
2
|
+
import type { ActionHandler, IntentDispatcher, UnknownIntent } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Serializes intent to string format: "type" or "type(payload)".
|
|
5
|
+
*/
|
|
6
|
+
export declare function serializeIntent<Intent extends UnknownIntent = UnknownIntent>(intent: Intent): string;
|
|
7
|
+
/**
|
|
8
|
+
* Parses serialized intent string back to intent object.
|
|
9
|
+
*/
|
|
10
|
+
export declare function deserializeIntent(value: string): UnknownIntent;
|
|
11
|
+
/**
|
|
12
|
+
* Applies intent transformation to submission payload.
|
|
13
|
+
* Returns modified payload or null for reset intent.
|
|
14
|
+
*/
|
|
15
|
+
export declare function applyIntent(submission: Submission, options?: {
|
|
16
|
+
handlers?: Record<string, ActionHandler>;
|
|
17
|
+
}): Record<string, FormValue> | null;
|
|
18
|
+
export declare function insertItem<Item>(list: Array<Item>, item: Item, index: number): void;
|
|
19
|
+
export declare function removeItem(list: Array<unknown>, index: number): void;
|
|
20
|
+
export declare function reorderItems(list: Array<unknown>, fromIndex: number, toIndex: number): void;
|
|
21
|
+
/**
|
|
22
|
+
* Updates list keys by removing child keys and optionally transforming remaining keys.
|
|
23
|
+
*/
|
|
24
|
+
export declare function updateListKeys(keys: Record<string, string[]> | undefined, keyToBeRemoved: string, updateKey?: (key: string) => string | null): Record<string, string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Built-in action handlers for form intents:
|
|
27
|
+
* - reset: clears form data
|
|
28
|
+
* - validate: marks fields as touched for validation display
|
|
29
|
+
* - update: updates specific field values
|
|
30
|
+
* - insert/remove/reorder: manages array field operations
|
|
31
|
+
*/
|
|
32
|
+
export declare const actionHandlers: {
|
|
33
|
+
[Type in keyof IntentDispatcher]: IntentDispatcher[Type] extends (payload: any) => void ? ActionHandler<IntentDispatcher[Type]> : never;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=intent.d.ts.map
|
|
@@ -0,0 +1,305 @@
|
|
|
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
|
+
var state = require('./state.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Serializes intent to string format: "type" or "type(payload)".
|
|
12
|
+
*/
|
|
13
|
+
function serializeIntent(intent) {
|
|
14
|
+
if (!intent.payload) {
|
|
15
|
+
return intent.type;
|
|
16
|
+
}
|
|
17
|
+
return "".concat(intent.type, "(").concat(JSON.stringify(intent.payload), ")");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parses serialized intent string back to intent object.
|
|
22
|
+
*/
|
|
23
|
+
function deserializeIntent(value) {
|
|
24
|
+
var type = value;
|
|
25
|
+
var payload;
|
|
26
|
+
var serializedPayload;
|
|
27
|
+
var openParenIndex = value.indexOf('(');
|
|
28
|
+
if (openParenIndex > 0 && value[value.length - 1] === ')') {
|
|
29
|
+
type = value.slice(0, openParenIndex);
|
|
30
|
+
serializedPayload = value.slice(openParenIndex + 1, -1);
|
|
31
|
+
}
|
|
32
|
+
if (serializedPayload) {
|
|
33
|
+
try {
|
|
34
|
+
payload = JSON.parse(serializedPayload);
|
|
35
|
+
} catch (_unused) {
|
|
36
|
+
// Ignore the error
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
type,
|
|
41
|
+
payload
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Applies intent transformation to submission payload.
|
|
47
|
+
* Returns modified payload or null for reset intent.
|
|
48
|
+
*/
|
|
49
|
+
function applyIntent(submission, options) {
|
|
50
|
+
var _options$handlers, _handler$validatePayl, _handler$validatePayl2;
|
|
51
|
+
if (!submission.intent) {
|
|
52
|
+
return submission.payload;
|
|
53
|
+
}
|
|
54
|
+
var intent = deserializeIntent(submission.intent);
|
|
55
|
+
var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : actionHandlers;
|
|
56
|
+
var handler = handlers[intent.type];
|
|
57
|
+
if (handler && handler.onApply && ((_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)) {
|
|
58
|
+
return handler.onApply(submission.payload, intent.payload);
|
|
59
|
+
}
|
|
60
|
+
return submission.payload;
|
|
61
|
+
}
|
|
62
|
+
function insertItem(list, item, index) {
|
|
63
|
+
list.splice(index, 0, item);
|
|
64
|
+
}
|
|
65
|
+
function removeItem(list, index) {
|
|
66
|
+
list.splice(index, 1);
|
|
67
|
+
}
|
|
68
|
+
function reorderItems(list, fromIndex, toIndex) {
|
|
69
|
+
list.splice(toIndex, 0, ...list.splice(fromIndex, 1));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Updates list keys by removing child keys and optionally transforming remaining keys.
|
|
74
|
+
*/
|
|
75
|
+
function updateListKeys() {
|
|
76
|
+
var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
77
|
+
var keyToBeRemoved = arguments.length > 1 ? arguments[1] : undefined;
|
|
78
|
+
var updateKey = arguments.length > 2 ? arguments[2] : undefined;
|
|
79
|
+
var basePath = future.getPathSegments(keyToBeRemoved);
|
|
80
|
+
return util.transformKeys(keys, field => {
|
|
81
|
+
var _updateKey;
|
|
82
|
+
return future.getRelativePath(field, basePath) !== null ? null : (_updateKey = updateKey === null || updateKey === void 0 ? void 0 : updateKey(field)) !== null && _updateKey !== void 0 ? _updateKey : field;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Built-in action handlers for form intents:
|
|
88
|
+
* - reset: clears form data
|
|
89
|
+
* - validate: marks fields as touched for validation display
|
|
90
|
+
* - update: updates specific field values
|
|
91
|
+
* - insert/remove/reorder: manages array field operations
|
|
92
|
+
*/
|
|
93
|
+
var actionHandlers = {
|
|
94
|
+
reset: {
|
|
95
|
+
onApply() {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
validate: {
|
|
100
|
+
validatePayload(name) {
|
|
101
|
+
return util.isOptional(name, util.isString);
|
|
102
|
+
},
|
|
103
|
+
onUpdate(state, _ref) {
|
|
104
|
+
var _intent$payload;
|
|
105
|
+
var {
|
|
106
|
+
submission,
|
|
107
|
+
intent,
|
|
108
|
+
error
|
|
109
|
+
} = _ref;
|
|
110
|
+
var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : '';
|
|
111
|
+
var basePath = future.getPathSegments(name);
|
|
112
|
+
var touchedFields = util.appendUniqueItem(state.touchedFields, name);
|
|
113
|
+
for (var field of submission.fields) {
|
|
114
|
+
// Add all child fields to the touched fields too
|
|
115
|
+
if (future.getRelativePath(field, basePath) !== null) {
|
|
116
|
+
touchedFields = util.appendUniqueItem(touchedFields, field);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// We couldn't find out all the fields from the FormData, e.g. unchecked checkboxes.
|
|
121
|
+
// or fieldsets without any fields, but we can at least include missing
|
|
122
|
+
// required fields based on the form error
|
|
123
|
+
if (name === '' && error) {
|
|
124
|
+
for (var _name of Object.keys(error.fieldErrors)) {
|
|
125
|
+
touchedFields = util.appendUniqueItem(touchedFields, _name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return util.merge(state, {
|
|
129
|
+
touchedFields
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
update: {
|
|
134
|
+
validatePayload(options) {
|
|
135
|
+
return future.isPlainObject(options) && util.isOptional(options.name, util.isString) && util.isOptional(options.index, util.isNumber) && !util.isUndefined(options.value);
|
|
136
|
+
},
|
|
137
|
+
onApply(value, options) {
|
|
138
|
+
return util.updateValueAtPath(value, future.appendPathSegment(options.name, options.index), options.value);
|
|
139
|
+
},
|
|
140
|
+
onUpdate(state, _ref2) {
|
|
141
|
+
var {
|
|
142
|
+
type,
|
|
143
|
+
submission,
|
|
144
|
+
intent
|
|
145
|
+
} = _ref2;
|
|
146
|
+
var listKeys = state.listKeys;
|
|
147
|
+
|
|
148
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
149
|
+
if (type === 'client') {
|
|
150
|
+
// TODO: Do we really need to update the keys here?
|
|
151
|
+
var name = future.appendPathSegment(intent.payload.name, intent.payload.index);
|
|
152
|
+
// Remove all child keys
|
|
153
|
+
listKeys = name === '' ? {} : updateListKeys(state.listKeys, name);
|
|
154
|
+
}
|
|
155
|
+
var basePath = future.getPathSegments(intent.payload.name);
|
|
156
|
+
var touchedFields = state.touchedFields;
|
|
157
|
+
for (var field of submission.fields) {
|
|
158
|
+
if (basePath.length === 0 || future.getRelativePath(field, basePath) !== null) {
|
|
159
|
+
touchedFields = util.appendUniqueItem(touchedFields, field);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state), {}, {
|
|
163
|
+
listKeys,
|
|
164
|
+
touchedFields
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
insert: {
|
|
169
|
+
validatePayload(options) {
|
|
170
|
+
return future.isPlainObject(options) && util.isString(options.name) && util.isOptional(options.index, util.isNumber);
|
|
171
|
+
},
|
|
172
|
+
onApply(value, options) {
|
|
173
|
+
var _options$index;
|
|
174
|
+
var list = Array.from(util.getArrayAtPath(value, options.name));
|
|
175
|
+
insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
|
|
176
|
+
return util.updateValueAtPath(value, options.name, list);
|
|
177
|
+
},
|
|
178
|
+
onUpdate(state$1, _ref3) {
|
|
179
|
+
var _intent$payload$index;
|
|
180
|
+
var {
|
|
181
|
+
type,
|
|
182
|
+
submission,
|
|
183
|
+
intent
|
|
184
|
+
} = _ref3;
|
|
185
|
+
var currentValue = submission.payload;
|
|
186
|
+
var list = util.getArrayAtPath(currentValue, intent.payload.name);
|
|
187
|
+
var index = (_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : list.length;
|
|
188
|
+
var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => index <= currentIndex ? currentIndex + 1 : currentIndex);
|
|
189
|
+
var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name);
|
|
190
|
+
var keys = state$1.listKeys;
|
|
191
|
+
|
|
192
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
193
|
+
if (type === 'client') {
|
|
194
|
+
var _state$listKeys$inten;
|
|
195
|
+
var listKeys = Array.from((_state$listKeys$inten = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten !== void 0 ? _state$listKeys$inten : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name));
|
|
196
|
+
insertItem(listKeys, util.generateUniqueKey(), index);
|
|
197
|
+
keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, index), updateListIndex)), {}, {
|
|
198
|
+
// Update existing list keys
|
|
199
|
+
[intent.payload.name]: listKeys
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, {
|
|
203
|
+
listKeys: keys,
|
|
204
|
+
touchedFields
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
remove: {
|
|
209
|
+
validatePayload(options) {
|
|
210
|
+
return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.index);
|
|
211
|
+
},
|
|
212
|
+
onApply(value, options) {
|
|
213
|
+
var list = Array.from(util.getArrayAtPath(value, options.name));
|
|
214
|
+
removeItem(list, options.index);
|
|
215
|
+
return util.updateValueAtPath(value, options.name, list);
|
|
216
|
+
},
|
|
217
|
+
onUpdate(state$1, _ref4) {
|
|
218
|
+
var {
|
|
219
|
+
type,
|
|
220
|
+
submission,
|
|
221
|
+
intent
|
|
222
|
+
} = _ref4;
|
|
223
|
+
var currentValue = submission.payload;
|
|
224
|
+
var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => {
|
|
225
|
+
if (intent.payload.index === currentIndex) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return intent.payload.index < currentIndex ? currentIndex - 1 : currentIndex;
|
|
229
|
+
});
|
|
230
|
+
var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name);
|
|
231
|
+
var keys = state$1.listKeys;
|
|
232
|
+
|
|
233
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
234
|
+
if (type === 'client') {
|
|
235
|
+
var _state$listKeys$inten2;
|
|
236
|
+
var listKeys = Array.from((_state$listKeys$inten2 = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten2 !== void 0 ? _state$listKeys$inten2 : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name));
|
|
237
|
+
removeItem(listKeys, intent.payload.index);
|
|
238
|
+
keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.index), updateListIndex)), {}, {
|
|
239
|
+
// Update existing list keys
|
|
240
|
+
[intent.payload.name]: listKeys
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, {
|
|
244
|
+
listKeys: keys,
|
|
245
|
+
touchedFields
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
reorder: {
|
|
250
|
+
validatePayload(options) {
|
|
251
|
+
return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.from) && util.isNumber(options.to);
|
|
252
|
+
},
|
|
253
|
+
onApply(value, options) {
|
|
254
|
+
var list = Array.from(util.getArrayAtPath(value, options.name));
|
|
255
|
+
reorderItems(list, options.from, options.to);
|
|
256
|
+
return util.updateValueAtPath(value, options.name, list);
|
|
257
|
+
},
|
|
258
|
+
onUpdate(state$1, _ref5) {
|
|
259
|
+
var {
|
|
260
|
+
type,
|
|
261
|
+
submission,
|
|
262
|
+
intent
|
|
263
|
+
} = _ref5;
|
|
264
|
+
var currentValue = submission.payload;
|
|
265
|
+
var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => {
|
|
266
|
+
if (intent.payload.from === intent.payload.to) {
|
|
267
|
+
return currentIndex;
|
|
268
|
+
}
|
|
269
|
+
if (currentIndex === intent.payload.from) {
|
|
270
|
+
return intent.payload.to;
|
|
271
|
+
}
|
|
272
|
+
if (intent.payload.from < intent.payload.to) {
|
|
273
|
+
return currentIndex > intent.payload.from && currentIndex <= intent.payload.to ? currentIndex - 1 : currentIndex;
|
|
274
|
+
}
|
|
275
|
+
return currentIndex >= intent.payload.to && currentIndex < intent.payload.from ? currentIndex + 1 : currentIndex;
|
|
276
|
+
});
|
|
277
|
+
var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name);
|
|
278
|
+
var keys = state$1.listKeys;
|
|
279
|
+
|
|
280
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
281
|
+
if (type === 'client') {
|
|
282
|
+
var _state$listKeys$inten3;
|
|
283
|
+
var listKeys = Array.from((_state$listKeys$inten3 = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten3 !== void 0 ? _state$listKeys$inten3 : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name));
|
|
284
|
+
reorderItems(listKeys, intent.payload.from, intent.payload.to);
|
|
285
|
+
keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.from), updateListIndex)), {}, {
|
|
286
|
+
// Update existing list keys
|
|
287
|
+
[intent.payload.name]: listKeys
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, {
|
|
291
|
+
listKeys: keys,
|
|
292
|
+
touchedFields
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
exports.actionHandlers = actionHandlers;
|
|
299
|
+
exports.applyIntent = applyIntent;
|
|
300
|
+
exports.deserializeIntent = deserializeIntent;
|
|
301
|
+
exports.insertItem = insertItem;
|
|
302
|
+
exports.removeItem = removeItem;
|
|
303
|
+
exports.reorderItems = reorderItems;
|
|
304
|
+
exports.serializeIntent = serializeIntent;
|
|
305
|
+
exports.updateListKeys = updateListKeys;
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
|
+
import { getPathSegments, getRelativePath, isPlainObject, appendPathSegment } from '@conform-to/dom/future';
|
|
3
|
+
import { isOptional, appendUniqueItem, merge, isUndefined, updateValueAtPath, isString, getArrayAtPath, createPathIndexUpdater, compactMap, generateUniqueKey, isNumber, transformKeys } from './util.mjs';
|
|
4
|
+
import { getDefaultListKey } from './state.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Serializes intent to string format: "type" or "type(payload)".
|
|
8
|
+
*/
|
|
9
|
+
function serializeIntent(intent) {
|
|
10
|
+
if (!intent.payload) {
|
|
11
|
+
return intent.type;
|
|
12
|
+
}
|
|
13
|
+
return "".concat(intent.type, "(").concat(JSON.stringify(intent.payload), ")");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parses serialized intent string back to intent object.
|
|
18
|
+
*/
|
|
19
|
+
function deserializeIntent(value) {
|
|
20
|
+
var type = value;
|
|
21
|
+
var payload;
|
|
22
|
+
var serializedPayload;
|
|
23
|
+
var openParenIndex = value.indexOf('(');
|
|
24
|
+
if (openParenIndex > 0 && value[value.length - 1] === ')') {
|
|
25
|
+
type = value.slice(0, openParenIndex);
|
|
26
|
+
serializedPayload = value.slice(openParenIndex + 1, -1);
|
|
27
|
+
}
|
|
28
|
+
if (serializedPayload) {
|
|
29
|
+
try {
|
|
30
|
+
payload = JSON.parse(serializedPayload);
|
|
31
|
+
} catch (_unused) {
|
|
32
|
+
// Ignore the error
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
type,
|
|
37
|
+
payload
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Applies intent transformation to submission payload.
|
|
43
|
+
* Returns modified payload or null for reset intent.
|
|
44
|
+
*/
|
|
45
|
+
function applyIntent(submission, options) {
|
|
46
|
+
var _options$handlers, _handler$validatePayl, _handler$validatePayl2;
|
|
47
|
+
if (!submission.intent) {
|
|
48
|
+
return submission.payload;
|
|
49
|
+
}
|
|
50
|
+
var intent = deserializeIntent(submission.intent);
|
|
51
|
+
var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : actionHandlers;
|
|
52
|
+
var handler = handlers[intent.type];
|
|
53
|
+
if (handler && handler.onApply && ((_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)) {
|
|
54
|
+
return handler.onApply(submission.payload, intent.payload);
|
|
55
|
+
}
|
|
56
|
+
return submission.payload;
|
|
57
|
+
}
|
|
58
|
+
function insertItem(list, item, index) {
|
|
59
|
+
list.splice(index, 0, item);
|
|
60
|
+
}
|
|
61
|
+
function removeItem(list, index) {
|
|
62
|
+
list.splice(index, 1);
|
|
63
|
+
}
|
|
64
|
+
function reorderItems(list, fromIndex, toIndex) {
|
|
65
|
+
list.splice(toIndex, 0, ...list.splice(fromIndex, 1));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Updates list keys by removing child keys and optionally transforming remaining keys.
|
|
70
|
+
*/
|
|
71
|
+
function updateListKeys() {
|
|
72
|
+
var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
73
|
+
var keyToBeRemoved = arguments.length > 1 ? arguments[1] : undefined;
|
|
74
|
+
var updateKey = arguments.length > 2 ? arguments[2] : undefined;
|
|
75
|
+
var basePath = getPathSegments(keyToBeRemoved);
|
|
76
|
+
return transformKeys(keys, field => {
|
|
77
|
+
var _updateKey;
|
|
78
|
+
return getRelativePath(field, basePath) !== null ? null : (_updateKey = updateKey === null || updateKey === void 0 ? void 0 : updateKey(field)) !== null && _updateKey !== void 0 ? _updateKey : field;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Built-in action handlers for form intents:
|
|
84
|
+
* - reset: clears form data
|
|
85
|
+
* - validate: marks fields as touched for validation display
|
|
86
|
+
* - update: updates specific field values
|
|
87
|
+
* - insert/remove/reorder: manages array field operations
|
|
88
|
+
*/
|
|
89
|
+
var actionHandlers = {
|
|
90
|
+
reset: {
|
|
91
|
+
onApply() {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
validate: {
|
|
96
|
+
validatePayload(name) {
|
|
97
|
+
return isOptional(name, isString);
|
|
98
|
+
},
|
|
99
|
+
onUpdate(state, _ref) {
|
|
100
|
+
var _intent$payload;
|
|
101
|
+
var {
|
|
102
|
+
submission,
|
|
103
|
+
intent,
|
|
104
|
+
error
|
|
105
|
+
} = _ref;
|
|
106
|
+
var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : '';
|
|
107
|
+
var basePath = getPathSegments(name);
|
|
108
|
+
var touchedFields = appendUniqueItem(state.touchedFields, name);
|
|
109
|
+
for (var field of submission.fields) {
|
|
110
|
+
// Add all child fields to the touched fields too
|
|
111
|
+
if (getRelativePath(field, basePath) !== null) {
|
|
112
|
+
touchedFields = appendUniqueItem(touchedFields, field);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// We couldn't find out all the fields from the FormData, e.g. unchecked checkboxes.
|
|
117
|
+
// or fieldsets without any fields, but we can at least include missing
|
|
118
|
+
// required fields based on the form error
|
|
119
|
+
if (name === '' && error) {
|
|
120
|
+
for (var _name of Object.keys(error.fieldErrors)) {
|
|
121
|
+
touchedFields = appendUniqueItem(touchedFields, _name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return merge(state, {
|
|
125
|
+
touchedFields
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
update: {
|
|
130
|
+
validatePayload(options) {
|
|
131
|
+
return isPlainObject(options) && isOptional(options.name, isString) && isOptional(options.index, isNumber) && !isUndefined(options.value);
|
|
132
|
+
},
|
|
133
|
+
onApply(value, options) {
|
|
134
|
+
return updateValueAtPath(value, appendPathSegment(options.name, options.index), options.value);
|
|
135
|
+
},
|
|
136
|
+
onUpdate(state, _ref2) {
|
|
137
|
+
var {
|
|
138
|
+
type,
|
|
139
|
+
submission,
|
|
140
|
+
intent
|
|
141
|
+
} = _ref2;
|
|
142
|
+
var listKeys = state.listKeys;
|
|
143
|
+
|
|
144
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
145
|
+
if (type === 'client') {
|
|
146
|
+
// TODO: Do we really need to update the keys here?
|
|
147
|
+
var name = appendPathSegment(intent.payload.name, intent.payload.index);
|
|
148
|
+
// Remove all child keys
|
|
149
|
+
listKeys = name === '' ? {} : updateListKeys(state.listKeys, name);
|
|
150
|
+
}
|
|
151
|
+
var basePath = getPathSegments(intent.payload.name);
|
|
152
|
+
var touchedFields = state.touchedFields;
|
|
153
|
+
for (var field of submission.fields) {
|
|
154
|
+
if (basePath.length === 0 || getRelativePath(field, basePath) !== null) {
|
|
155
|
+
touchedFields = appendUniqueItem(touchedFields, field);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
159
|
+
listKeys,
|
|
160
|
+
touchedFields
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
insert: {
|
|
165
|
+
validatePayload(options) {
|
|
166
|
+
return isPlainObject(options) && isString(options.name) && isOptional(options.index, isNumber);
|
|
167
|
+
},
|
|
168
|
+
onApply(value, options) {
|
|
169
|
+
var _options$index;
|
|
170
|
+
var list = Array.from(getArrayAtPath(value, options.name));
|
|
171
|
+
insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
|
|
172
|
+
return updateValueAtPath(value, options.name, list);
|
|
173
|
+
},
|
|
174
|
+
onUpdate(state, _ref3) {
|
|
175
|
+
var _intent$payload$index;
|
|
176
|
+
var {
|
|
177
|
+
type,
|
|
178
|
+
submission,
|
|
179
|
+
intent
|
|
180
|
+
} = _ref3;
|
|
181
|
+
var currentValue = submission.payload;
|
|
182
|
+
var list = getArrayAtPath(currentValue, intent.payload.name);
|
|
183
|
+
var index = (_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : list.length;
|
|
184
|
+
var updateListIndex = createPathIndexUpdater(intent.payload.name, currentIndex => index <= currentIndex ? currentIndex + 1 : currentIndex);
|
|
185
|
+
var touchedFields = appendUniqueItem(compactMap(state.touchedFields, updateListIndex), intent.payload.name);
|
|
186
|
+
var keys = state.listKeys;
|
|
187
|
+
|
|
188
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
189
|
+
if (type === 'client') {
|
|
190
|
+
var _state$listKeys$inten;
|
|
191
|
+
var listKeys = Array.from((_state$listKeys$inten = state.listKeys[intent.payload.name]) !== null && _state$listKeys$inten !== void 0 ? _state$listKeys$inten : getDefaultListKey(state.resetKey, currentValue, intent.payload.name));
|
|
192
|
+
insertItem(listKeys, generateUniqueKey(), index);
|
|
193
|
+
keys = _objectSpread2(_objectSpread2({}, updateListKeys(state.listKeys, appendPathSegment(intent.payload.name, index), updateListIndex)), {}, {
|
|
194
|
+
// Update existing list keys
|
|
195
|
+
[intent.payload.name]: listKeys
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
199
|
+
listKeys: keys,
|
|
200
|
+
touchedFields
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
remove: {
|
|
205
|
+
validatePayload(options) {
|
|
206
|
+
return isPlainObject(options) && isString(options.name) && isNumber(options.index);
|
|
207
|
+
},
|
|
208
|
+
onApply(value, options) {
|
|
209
|
+
var list = Array.from(getArrayAtPath(value, options.name));
|
|
210
|
+
removeItem(list, options.index);
|
|
211
|
+
return updateValueAtPath(value, options.name, list);
|
|
212
|
+
},
|
|
213
|
+
onUpdate(state, _ref4) {
|
|
214
|
+
var {
|
|
215
|
+
type,
|
|
216
|
+
submission,
|
|
217
|
+
intent
|
|
218
|
+
} = _ref4;
|
|
219
|
+
var currentValue = submission.payload;
|
|
220
|
+
var updateListIndex = createPathIndexUpdater(intent.payload.name, currentIndex => {
|
|
221
|
+
if (intent.payload.index === currentIndex) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
return intent.payload.index < currentIndex ? currentIndex - 1 : currentIndex;
|
|
225
|
+
});
|
|
226
|
+
var touchedFields = appendUniqueItem(compactMap(state.touchedFields, updateListIndex), intent.payload.name);
|
|
227
|
+
var keys = state.listKeys;
|
|
228
|
+
|
|
229
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
230
|
+
if (type === 'client') {
|
|
231
|
+
var _state$listKeys$inten2;
|
|
232
|
+
var listKeys = Array.from((_state$listKeys$inten2 = state.listKeys[intent.payload.name]) !== null && _state$listKeys$inten2 !== void 0 ? _state$listKeys$inten2 : getDefaultListKey(state.resetKey, currentValue, intent.payload.name));
|
|
233
|
+
removeItem(listKeys, intent.payload.index);
|
|
234
|
+
keys = _objectSpread2(_objectSpread2({}, updateListKeys(state.listKeys, appendPathSegment(intent.payload.name, intent.payload.index), updateListIndex)), {}, {
|
|
235
|
+
// Update existing list keys
|
|
236
|
+
[intent.payload.name]: listKeys
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
240
|
+
listKeys: keys,
|
|
241
|
+
touchedFields
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
reorder: {
|
|
246
|
+
validatePayload(options) {
|
|
247
|
+
return isPlainObject(options) && isString(options.name) && isNumber(options.from) && isNumber(options.to);
|
|
248
|
+
},
|
|
249
|
+
onApply(value, options) {
|
|
250
|
+
var list = Array.from(getArrayAtPath(value, options.name));
|
|
251
|
+
reorderItems(list, options.from, options.to);
|
|
252
|
+
return updateValueAtPath(value, options.name, list);
|
|
253
|
+
},
|
|
254
|
+
onUpdate(state, _ref5) {
|
|
255
|
+
var {
|
|
256
|
+
type,
|
|
257
|
+
submission,
|
|
258
|
+
intent
|
|
259
|
+
} = _ref5;
|
|
260
|
+
var currentValue = submission.payload;
|
|
261
|
+
var updateListIndex = createPathIndexUpdater(intent.payload.name, currentIndex => {
|
|
262
|
+
if (intent.payload.from === intent.payload.to) {
|
|
263
|
+
return currentIndex;
|
|
264
|
+
}
|
|
265
|
+
if (currentIndex === intent.payload.from) {
|
|
266
|
+
return intent.payload.to;
|
|
267
|
+
}
|
|
268
|
+
if (intent.payload.from < intent.payload.to) {
|
|
269
|
+
return currentIndex > intent.payload.from && currentIndex <= intent.payload.to ? currentIndex - 1 : currentIndex;
|
|
270
|
+
}
|
|
271
|
+
return currentIndex >= intent.payload.to && currentIndex < intent.payload.from ? currentIndex + 1 : currentIndex;
|
|
272
|
+
});
|
|
273
|
+
var touchedFields = appendUniqueItem(compactMap(state.touchedFields, updateListIndex), intent.payload.name);
|
|
274
|
+
var keys = state.listKeys;
|
|
275
|
+
|
|
276
|
+
// Update the keys only for client updates to avoid double updates if there is no client validation
|
|
277
|
+
if (type === 'client') {
|
|
278
|
+
var _state$listKeys$inten3;
|
|
279
|
+
var listKeys = Array.from((_state$listKeys$inten3 = state.listKeys[intent.payload.name]) !== null && _state$listKeys$inten3 !== void 0 ? _state$listKeys$inten3 : getDefaultListKey(state.resetKey, currentValue, intent.payload.name));
|
|
280
|
+
reorderItems(listKeys, intent.payload.from, intent.payload.to);
|
|
281
|
+
keys = _objectSpread2(_objectSpread2({}, updateListKeys(state.listKeys, appendPathSegment(intent.payload.name, intent.payload.from), updateListIndex)), {}, {
|
|
282
|
+
// Update existing list keys
|
|
283
|
+
[intent.payload.name]: listKeys
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
287
|
+
listKeys: keys,
|
|
288
|
+
touchedFields
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export { actionHandlers, applyIntent, deserializeIntent, insertItem, removeItem, reorderItems, serializeIntent, updateListKeys };
|