@conform-to/react 1.15.1 → 1.17.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.
@@ -1,5 +1,5 @@
1
- import type { FormValue, Submission } from '@conform-to/dom/future';
2
- import type { ActionHandler, IntentDispatcher, UnknownIntent } from './types';
1
+ import type { FormValue, Submission, SubmissionResult } from '@conform-to/dom/future';
2
+ import type { IntentHandler, IntentDispatcher, UnknownIntent } from './types';
3
3
  /**
4
4
  * Serializes intent to string format: "type" or "type(payload)".
5
5
  */
@@ -12,9 +12,16 @@ export declare function deserializeIntent(value: string): UnknownIntent;
12
12
  * Applies intent transformation to submission payload.
13
13
  * Returns modified payload or null for reset intent.
14
14
  */
15
- export declare function applyIntent(submission: Submission, options?: {
16
- handlers?: Record<string, ActionHandler>;
15
+ export declare function resolveIntent(submission: Submission, options?: {
16
+ handlers?: Record<string, IntentHandler>;
17
17
  }): Record<string, FormValue> | undefined;
18
+ /**
19
+ * Resolves an intent after validation by calling the handler's onResolve.
20
+ * Mutates the result with updated value/error and returns whether the intent was cancelled.
21
+ */
22
+ export declare function applyIntent<ErrorShape>(result: SubmissionResult<ErrorShape>, intent: UnknownIntent | null, options?: {
23
+ handlers?: Record<string, IntentHandler>;
24
+ }): SubmissionResult<ErrorShape>;
18
25
  export declare function insertItem<Item>(list: Array<Item>, item: Item, index: number): void;
19
26
  export declare function removeItem(list: Array<unknown>, index: number): void;
20
27
  export declare function reorderItems(list: Array<unknown>, fromIndex: number, toIndex: number): void;
@@ -29,7 +36,7 @@ export declare function updateListKeys(keys: Record<string, string[]> | undefine
29
36
  * - update: updates specific field values
30
37
  * - insert/remove/reorder: manages array field operations
31
38
  */
32
- export declare const actionHandlers: {
33
- [Type in keyof IntentDispatcher]: IntentDispatcher[Type] extends (payload: any) => void ? ActionHandler<IntentDispatcher[Type]> : never;
39
+ export declare const intentHandlers: {
40
+ [Type in keyof IntentDispatcher]: IntentDispatcher[Type] extends (payload: any) => void ? IntentHandler<IntentDispatcher[Type]> : never;
34
41
  };
35
42
  //# sourceMappingURL=intent.d.ts.map
@@ -46,19 +46,35 @@ function deserializeIntent(value) {
46
46
  * Applies intent transformation to submission payload.
47
47
  * Returns modified payload or null for reset intent.
48
48
  */
49
- function applyIntent(submission, options) {
50
- var _options$handlers, _handler$validatePayl, _handler$validatePayl2;
49
+ function resolveIntent(submission, options) {
50
+ var _options$handlers, _handler$validate, _handler$validate2;
51
51
  if (!submission.intent) {
52
52
  return submission.payload;
53
53
  }
54
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;
55
+ var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : intentHandlers;
56
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);
57
+ if (handler !== null && handler !== void 0 && handler.resolve && ((_handler$validate = (_handler$validate2 = handler.validate) === null || _handler$validate2 === void 0 ? void 0 : _handler$validate2.call(handler, intent.payload)) !== null && _handler$validate !== void 0 ? _handler$validate : true)) {
58
+ return handler.resolve(submission.payload, intent.payload);
59
59
  }
60
60
  return submission.payload;
61
61
  }
62
+
63
+ /**
64
+ * Resolves an intent after validation by calling the handler's onResolve.
65
+ * Mutates the result with updated value/error and returns whether the intent was cancelled.
66
+ */
67
+ function applyIntent(result, intent, options) {
68
+ if (intent) {
69
+ var _options$handlers2, _handler$validate3, _handler$validate4;
70
+ var handlers = (_options$handlers2 = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers2 !== void 0 ? _options$handlers2 : intentHandlers;
71
+ var handler = handlers[intent.type];
72
+ if (handler !== null && handler !== void 0 && handler.apply && ((_handler$validate3 = (_handler$validate4 = handler.validate) === null || _handler$validate4 === void 0 ? void 0 : _handler$validate4.call(handler, intent.payload)) !== null && _handler$validate3 !== void 0 ? _handler$validate3 : true)) {
73
+ return handler.apply(result, intent.payload);
74
+ }
75
+ }
76
+ return result;
77
+ }
62
78
  function insertItem(list, item, index) {
63
79
  list.splice(index, 0, item);
64
80
  }
@@ -90,36 +106,34 @@ function updateListKeys() {
90
106
  * - update: updates specific field values
91
107
  * - insert/remove/reorder: manages array field operations
92
108
  */
93
- var actionHandlers = {
109
+ var intentHandlers = {
94
110
  reset: {
95
- validatePayload(options) {
111
+ validate(options) {
96
112
  return util.isOptional(options, future.isPlainObject) && (util.isUndefined(options === null || options === void 0 ? void 0 : options.defaultValue) || util.isNullable(options === null || options === void 0 ? void 0 : options.defaultValue, future.isPlainObject));
97
113
  },
98
- onApply(_, options) {
114
+ resolve(_, options) {
99
115
  if ((options === null || options === void 0 ? void 0 : options.defaultValue) === null) {
100
116
  return {};
101
117
  }
102
118
  return options === null || options === void 0 ? void 0 : options.defaultValue;
103
119
  },
104
- onUpdate(_, _ref) {
105
- var {
106
- targetValue,
107
- ctx
108
- } = _ref;
109
- return ctx.reset(targetValue);
120
+ apply(result) {
121
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
122
+ reset: true
123
+ });
110
124
  }
111
125
  },
112
126
  validate: {
113
- validatePayload(name) {
127
+ validate(name) {
114
128
  return util.isOptional(name, util.isString);
115
129
  },
116
- onUpdate(state, _ref2) {
130
+ update(state, _ref) {
117
131
  var _intent$payload;
118
132
  var {
119
133
  submission,
120
134
  intent,
121
135
  error
122
- } = _ref2;
136
+ } = _ref;
123
137
  var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : '';
124
138
  var basePath = future.getPathSegments(name);
125
139
  var allFields = error ?
@@ -138,20 +152,20 @@ var actionHandlers = {
138
152
  }
139
153
  },
140
154
  update: {
141
- validatePayload(options) {
155
+ validate(options) {
142
156
  return future.isPlainObject(options) && util.isOptional(options.name, util.isString) && util.isOptional(options.index, util.isNumber) && !util.isUndefined(options.value);
143
157
  },
144
- onApply(value, options) {
158
+ resolve(value, options) {
145
159
  var _options$value;
146
160
  var name = future.appendPathSegment(options.name, options.index);
147
161
  return util.updateValueAtPath(value, name, (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : name === '' ? {} : null);
148
162
  },
149
- onUpdate(state, _ref3) {
163
+ update(state, _ref2) {
150
164
  var {
151
165
  type,
152
166
  submission,
153
167
  intent
154
- } = _ref3;
168
+ } = _ref2;
155
169
  if (type === 'server') {
156
170
  return state;
157
171
  }
@@ -178,63 +192,141 @@ var actionHandlers = {
178
192
  }
179
193
  },
180
194
  insert: {
181
- validatePayload(options) {
182
- return future.isPlainObject(options) && util.isString(options.name) && util.isOptional(options.index, util.isNumber);
195
+ validate(options) {
196
+ return future.isPlainObject(options) && util.isString(options.name) && util.isOptional(options.index, util.isNumber) && util.isOptional(options.from, util.isString) && util.isOptional(options.onInvalid, mode => mode === 'revert');
183
197
  },
184
- onApply(value, options) {
198
+ resolve(value, options) {
185
199
  var _options$index;
186
- var list = Array.from(util.getArrayAtPath(value, options.name));
187
- insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
188
- return util.updateValueAtPath(value, options.name, list);
200
+ var result = value;
201
+ var itemValue = options.defaultValue;
202
+ if (options.from !== undefined) {
203
+ itemValue = future.getValueAtPath(result, options.from);
204
+ result = util.updateValueAtPath(result, options.from, '');
205
+ }
206
+ var list = Array.from(util.getArrayAtPath(result, options.name));
207
+ insertItem(list, itemValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
208
+ return util.updateValueAtPath(result, options.name, list);
209
+ },
210
+ apply(result, options) {
211
+ var _result$error;
212
+ // Warn if validation result is not yet available
213
+ if (typeof result.error === 'undefined' && (options.onInvalid || options.from)) {
214
+ // eslint-disable-next-line no-console
215
+ console.warn('intent.insert() with `onInvalid` or `from` requires the validation result to be available synchronously. ' + 'These options are ignored because the error is not yet known.');
216
+ return result;
217
+ }
218
+ var arrayErrors = (_result$error = result.error) === null || _result$error === void 0 ? void 0 : _result$error.fieldErrors[options.name];
219
+ if (options.onInvalid === 'revert' && arrayErrors !== null && arrayErrors !== void 0 && arrayErrors.length) {
220
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
221
+ targetValue: undefined
222
+ });
223
+ }
224
+ if (options.from !== undefined) {
225
+ var _options$index2, _result$error2;
226
+ var index = (_options$index2 = options.index) !== null && _options$index2 !== void 0 ? _options$index2 : util.getArrayAtPath(result.submission.payload, options.name).length;
227
+ var insertedItemPath = future.appendPathSegment(options.name, index);
228
+ var insertedItemErrors = (_result$error2 = result.error) === null || _result$error2 === void 0 ? void 0 : _result$error2.fieldErrors[insertedItemPath];
229
+ if (insertedItemErrors !== null && insertedItemErrors !== void 0 && insertedItemErrors.length) {
230
+ var _result$error$fieldEr, _result$error3, _result$error$formErr, _result$error4, _result$error5;
231
+ var fromErrors = (_result$error$fieldEr = (_result$error3 = result.error) === null || _result$error3 === void 0 ? void 0 : _result$error3.fieldErrors[options.from]) !== null && _result$error$fieldEr !== void 0 ? _result$error$fieldEr : [];
232
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
233
+ targetValue: undefined,
234
+ error: {
235
+ formErrors: (_result$error$formErr = (_result$error4 = result.error) === null || _result$error4 === void 0 ? void 0 : _result$error4.formErrors) !== null && _result$error$formErr !== void 0 ? _result$error$formErr : [],
236
+ fieldErrors: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, (_result$error5 = result.error) === null || _result$error5 === void 0 ? void 0 : _result$error5.fieldErrors), {}, {
237
+ [options.from]: [...fromErrors, ...insertedItemErrors],
238
+ [insertedItemPath]: []
239
+ })
240
+ }
241
+ });
242
+ }
243
+ }
244
+ return result;
189
245
  },
190
- onUpdate(state$1, _ref4) {
246
+ update(state$1, _ref3) {
191
247
  var _intent$payload$index;
192
248
  var {
193
249
  type,
194
250
  submission,
195
- intent
196
- } = _ref4;
251
+ intent,
252
+ ctx
253
+ } = _ref3;
197
254
  if (type === 'server') {
198
255
  return state$1;
199
256
  }
200
- var currentValue = submission.payload;
201
- var list = util.getArrayAtPath(currentValue, intent.payload.name);
202
- var index = (_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : list.length;
257
+ var from = intent.payload.from;
258
+ var index = (_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : util.getArrayAtPath(submission.payload, intent.payload.name).length;
203
259
  var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => index <= currentIndex ? currentIndex + 1 : currentIndex);
204
- var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name);
205
- var keys = state$1.listKeys;
260
+ var touchedFields = state$1.touchedFields;
261
+ var listKeys = state$1.listKeys;
262
+ if (!ctx.cancelled) {
263
+ touchedFields = util.compactMap(state$1.touchedFields, updateListIndex);
206
264
 
207
- // Update the keys only for client updates to avoid double updates if there is no client validation
208
- if (type === 'client') {
209
- var _state$listKeys$inten;
210
- 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));
211
- insertItem(listKeys, util.generateUniqueKey(), index);
212
- keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, index), updateListIndex)), {}, {
213
- // Update existing list keys
214
- [intent.payload.name]: listKeys
215
- });
265
+ // Update the keys only for client updates to avoid double updates if there is no client validation
266
+ if (type === 'client') {
267
+ var _state$listKeys$inten;
268
+ var selectedListKeys = 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, submission.payload, intent.payload.name));
269
+ insertItem(selectedListKeys, util.generateUniqueKey(), index);
270
+ listKeys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, index), updateListIndex)), {}, {
271
+ // Update existing list keys
272
+ [intent.payload.name]: selectedListKeys
273
+ });
274
+ }
275
+ }
276
+ touchedFields = util.appendUniqueItem(touchedFields, intent.payload.name);
277
+ if (from !== undefined) {
278
+ touchedFields = util.appendUniqueItem(touchedFields, from);
216
279
  }
217
280
  return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, {
218
- listKeys: keys,
281
+ listKeys,
219
282
  touchedFields
220
283
  });
221
284
  }
222
285
  },
223
286
  remove: {
224
- validatePayload(options) {
225
- return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.index);
287
+ validate(options) {
288
+ return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.index) && util.isOptional(options.onInvalid, v => v === 'revert' || v === 'insert');
226
289
  },
227
- onApply(value, options) {
290
+ resolve(value, options) {
228
291
  var list = Array.from(util.getArrayAtPath(value, options.name));
229
292
  removeItem(list, options.index);
230
293
  return util.updateValueAtPath(value, options.name, list);
231
294
  },
232
- onUpdate(state$1, _ref5) {
295
+ apply(result, options) {
296
+ var _result$error6;
297
+ // Warn if validation result is not yet available
298
+ if (typeof result.error === 'undefined' && options.onInvalid) {
299
+ if (process.env.NODE_ENV !== 'production') {
300
+ // eslint-disable-next-line no-console
301
+ console.warn('intent.remove() with `onInvalid` requires the validation result to be available synchronously. ' + 'This option is ignored because the error is not yet known.');
302
+ }
303
+ return result;
304
+ }
305
+ if (result.targetValue && (_result$error6 = result.error) !== null && _result$error6 !== void 0 && _result$error6.fieldErrors[options.name]) {
306
+ switch (options.onInvalid) {
307
+ case 'revert':
308
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
309
+ targetValue: undefined
310
+ });
311
+ case 'insert':
312
+ {
313
+ var list = Array.from(util.getArrayAtPath(result.targetValue, options.name));
314
+ insertItem(list, options.defaultValue, list.length);
315
+ return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
316
+ targetValue: util.updateValueAtPath(result.targetValue, options.name, list)
317
+ });
318
+ }
319
+ }
320
+ }
321
+ return result;
322
+ },
323
+ update(state$1, _ref4) {
233
324
  var {
234
325
  type,
235
326
  submission,
236
- intent
237
- } = _ref5;
327
+ intent,
328
+ ctx
329
+ } = _ref4;
238
330
  if (type === 'server') {
239
331
  return state$1;
240
332
  }
@@ -245,40 +337,54 @@ var actionHandlers = {
245
337
  }
246
338
  return intent.payload.index < currentIndex ? currentIndex - 1 : currentIndex;
247
339
  });
248
- var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name);
249
- var keys = state$1.listKeys;
340
+ var touchedFields = state$1.touchedFields;
341
+ var listKeys = state$1.listKeys;
250
342
 
251
- // Update the keys only for client updates to avoid double updates if there is no client validation
252
- if (type === 'client') {
253
- var _state$listKeys$inten2;
254
- 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));
255
- removeItem(listKeys, intent.payload.index);
256
- keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.index), updateListIndex)), {}, {
257
- // Update existing list keys
258
- [intent.payload.name]: listKeys
259
- });
343
+ // If onInvalid is 'insert', we still remove the item and then insert a new item at the end
344
+ if (!ctx.cancelled || intent.payload.onInvalid === 'insert') {
345
+ touchedFields = util.compactMap(touchedFields, updateListIndex);
346
+
347
+ // Update the keys only for client updates to avoid double updates if there is no client validation
348
+ if (type === 'client') {
349
+ var _state$listKeys$inten2;
350
+ var selectedListKeys = 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));
351
+ removeItem(selectedListKeys, intent.payload.index);
352
+ listKeys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.index), updateListIndex)), {}, {
353
+ // Update existing list keys
354
+ [intent.payload.name]: selectedListKeys
355
+ });
356
+ if (ctx.cancelled) {
357
+ var index = selectedListKeys.length;
358
+ insertItem(selectedListKeys, util.generateUniqueKey(), index);
359
+ listKeys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, index), updateListIndex)), {}, {
360
+ // Update existing list keys
361
+ [intent.payload.name]: selectedListKeys
362
+ });
363
+ }
364
+ }
260
365
  }
366
+ touchedFields = util.appendUniqueItem(touchedFields, intent.payload.name);
261
367
  return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, {
262
- listKeys: keys,
368
+ listKeys: listKeys,
263
369
  touchedFields
264
370
  });
265
371
  }
266
372
  },
267
373
  reorder: {
268
- validatePayload(options) {
374
+ validate(options) {
269
375
  return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.from) && util.isNumber(options.to);
270
376
  },
271
- onApply(value, options) {
377
+ resolve(value, options) {
272
378
  var list = Array.from(util.getArrayAtPath(value, options.name));
273
379
  reorderItems(list, options.from, options.to);
274
380
  return util.updateValueAtPath(value, options.name, list);
275
381
  },
276
- onUpdate(state$1, _ref6) {
382
+ update(state$1, _ref5) {
277
383
  var {
278
384
  type,
279
385
  submission,
280
386
  intent
281
- } = _ref6;
387
+ } = _ref5;
282
388
  if (type === 'server') {
283
389
  return state$1;
284
390
  }
@@ -316,11 +422,12 @@ var actionHandlers = {
316
422
  }
317
423
  };
318
424
 
319
- exports.actionHandlers = actionHandlers;
320
425
  exports.applyIntent = applyIntent;
321
426
  exports.deserializeIntent = deserializeIntent;
322
427
  exports.insertItem = insertItem;
428
+ exports.intentHandlers = intentHandlers;
323
429
  exports.removeItem = removeItem;
324
430
  exports.reorderItems = reorderItems;
431
+ exports.resolveIntent = resolveIntent;
325
432
  exports.serializeIntent = serializeIntent;
326
433
  exports.updateListKeys = updateListKeys;