@conform-to/react 0.5.0 → 0.6.0-pre.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/hooks.js CHANGED
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
6
6
  var dom = require('@conform-to/dom');
7
7
  var react = require('react');
8
- var helpers = require('./helpers.js');
9
8
 
10
9
  /**
11
10
  * Returns properties required to hook into form events.
@@ -14,16 +13,17 @@ var helpers = require('./helpers.js');
14
13
  * @see https://conform.guide/api/react#useform
15
14
  */
16
15
  function useForm() {
16
+ var _config$state;
17
17
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
18
18
  var configRef = react.useRef(config);
19
19
  var ref = react.useRef(null);
20
+ var [lastSubmission, setLastSubmission] = react.useState((_config$state = config.state) !== null && _config$state !== void 0 ? _config$state : null);
20
21
  var [error, setError] = react.useState(() => {
21
- var _config$state$error$f, _config$state, _config$state$error;
22
- var [, message] = (_config$state$error$f = (_config$state = config.state) === null || _config$state === void 0 ? void 0 : (_config$state$error = _config$state.error) === null || _config$state$error === void 0 ? void 0 : _config$state$error.find(_ref => {
23
- var [key] = _ref;
24
- return key === '';
25
- })) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
26
- return message !== null && message !== void 0 ? message : '';
22
+ if (!config.state) {
23
+ return '';
24
+ }
25
+ var message = config.state.error[''];
26
+ return dom.getValidationMessage(message);
27
27
  });
28
28
  var [uncontrolledState, setUncontrolledState] = react.useState(() => {
29
29
  var submission = config.state;
@@ -33,11 +33,14 @@ function useForm() {
33
33
  };
34
34
  }
35
35
  return {
36
- defaultValue: submission.value,
37
- initialError: submission.error.filter(_ref2 => {
38
- var [name] = _ref2;
39
- return name !== '' && dom.shouldValidate(submission, name);
40
- })
36
+ defaultValue: submission.payload,
37
+ initialError: Object.entries(submission.error).reduce((result, _ref) => {
38
+ var [name, message] = _ref;
39
+ if (name !== '' && dom.shouldValidate(submission.intent, name)) {
40
+ result[name] = message;
41
+ }
42
+ return result;
43
+ }, {})
41
44
  };
42
45
  });
43
46
  var fieldsetConfig = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, uncontrolledState), {}, {
@@ -54,11 +57,25 @@ function useForm() {
54
57
  }, []);
55
58
  react.useEffect(() => {
56
59
  var form = ref.current;
57
- if (!form || !config.state) {
60
+ var submission = config.state;
61
+ if (!form || !submission) {
58
62
  return;
59
63
  }
60
- dom.reportSubmission(form, config.state);
64
+ var listCommand = dom.parseListCommand(submission.intent);
65
+ if (listCommand) {
66
+ form.dispatchEvent(new CustomEvent('conform/list', {
67
+ detail: submission.intent
68
+ }));
69
+ }
70
+ setLastSubmission(submission);
61
71
  }, [config.state]);
72
+ react.useEffect(() => {
73
+ var form = ref.current;
74
+ if (!form || !lastSubmission) {
75
+ return;
76
+ }
77
+ dom.reportSubmission(ref.current, lastSubmission);
78
+ }, [lastSubmission]);
62
79
  react.useEffect(() => {
63
80
  // Revalidate the form when input value is changed
64
81
  var handleInput = event => {
@@ -69,7 +86,7 @@ function useForm() {
69
86
  return;
70
87
  }
71
88
  if (field.dataset.conformTouched || formConfig.initialReport === 'onChange') {
72
- dom.requestCommand(form, dom.validate(field.name));
89
+ dom.requestIntent(form, dom.validate(field.name));
73
90
  }
74
91
  };
75
92
  var handleBlur = event => {
@@ -80,7 +97,7 @@ function useForm() {
80
97
  return;
81
98
  }
82
99
  if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
83
- dom.requestCommand(form, dom.validate(field.name));
100
+ dom.requestIntent(form, dom.validate(field.name));
84
101
  }
85
102
  };
86
103
  var handleInvalid = event => {
@@ -111,8 +128,7 @@ function useForm() {
111
128
  }
112
129
  setError('');
113
130
  setUncontrolledState({
114
- defaultValue: formConfig.defaultValue,
115
- initialError: []
131
+ defaultValue: formConfig.defaultValue
116
132
  });
117
133
  };
118
134
 
@@ -145,53 +161,38 @@ function useForm() {
145
161
  var form = event.currentTarget;
146
162
  var nativeEvent = event.nativeEvent;
147
163
  var submitter = nativeEvent.submitter;
148
-
149
- /**
150
- * It checks defaultPrevented to confirm if the submission is intentional
151
- * This is utilized by `useFieldList` to modify the list state when the submit
152
- * event is captured and revalidate the form with new fields without triggering
153
- * a form submission at the same time.
154
- */
155
164
  if (event.defaultPrevented) {
156
165
  return;
157
166
  }
158
167
  try {
159
- var submission;
168
+ var _config$onValidate;
160
169
  var formData = dom.getFormData(form, submitter);
161
- if (typeof config.onValidate === 'function') {
162
- submission = config.onValidate({
163
- form,
164
- formData
165
- });
166
- } else {
167
- submission = dom.parse(formData);
168
- if (config.mode !== 'server-validation') {
169
- /**
170
- * As there is no custom logic defined,
171
- * removing the custom validity state will allow us
172
- * finding the latest validation message.
173
- *
174
- * This is mainly used to showcase the constraint validation API.
175
- */
176
- for (var element of form.elements) {
177
- if (dom.isFieldElement(element) && element.willValidate) {
178
- element.setCustomValidity('');
179
- submission.error.push([element.name, element.validationMessage]);
180
- }
181
- }
170
+ var getSubmission = (_config$onValidate = config.onValidate) !== null && _config$onValidate !== void 0 ? _config$onValidate : context => dom.parse(context.formData);
171
+ var submission = getSubmission({
172
+ form,
173
+ formData
174
+ });
175
+ if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && Object.entries(submission.error).some(_ref2 => {
176
+ var [, message] = _ref2;
177
+ return message !== '' && ![].concat(message).includes(dom.VALIDATION_UNDEFINED);
178
+ }) || typeof config.onValidate !== 'undefined' && (submission.intent.startsWith('validate') || submission.intent.startsWith('list')) && Object.entries(submission.error).every(_ref3 => {
179
+ var [, message] = _ref3;
180
+ return ![].concat(message).includes(dom.VALIDATION_UNDEFINED);
181
+ })) {
182
+ var listCommand = dom.parseListCommand(submission.intent);
183
+ if (listCommand) {
184
+ form.dispatchEvent(new CustomEvent('conform/list', {
185
+ detail: submission.intent
186
+ }));
182
187
  }
183
- }
184
- if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && dom.hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
188
+ setLastSubmission(submission);
185
189
  event.preventDefault();
186
190
  } else {
187
191
  var _config$onSubmit;
188
- (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
192
+ (_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _rollupPluginBabelHelpers.objectSpread2({
189
193
  formData,
190
194
  submission
191
- });
192
- }
193
- if (event.defaultPrevented) {
194
- dom.reportSubmission(form, submission);
195
+ }, dom.getFormAttributes(form, submitter)));
195
196
  }
196
197
  } catch (e) {
197
198
  console.warn(e);
@@ -214,18 +215,14 @@ function useFieldset(ref, config) {
214
215
  () => {
215
216
  var _config$defaultValue;
216
217
  var initialError = {};
217
- for (var [name, message] of (_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : []) {
218
+ for (var [name, message] of Object.entries((_config$initialError = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError !== void 0 ? _config$initialError : {})) {
218
219
  var _config$initialError;
219
220
  var [key, ...paths] = dom.getPaths(name);
220
221
  if (typeof key === 'string') {
221
- var _initialError$key;
222
222
  var scopedName = dom.getName(paths);
223
- var entries = (_initialError$key = initialError[key]) !== null && _initialError$key !== void 0 ? _initialError$key : [];
224
- if (scopedName === '' && entries.length > 0 && entries[0][0] !== '') {
225
- initialError[key] = [[scopedName, message], ...entries];
226
- } else {
227
- initialError[key] = [...entries, [scopedName, message]];
228
- }
223
+ initialError[key] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, initialError[key]), {}, {
224
+ [scopedName]: message
225
+ });
229
226
  }
230
227
  }
231
228
  return {
@@ -235,12 +232,8 @@ function useFieldset(ref, config) {
235
232
  });
236
233
  var [error, setError] = react.useState(() => {
237
234
  var result = {};
238
- for (var [key, entries] of Object.entries(uncontrolledState.initialError)) {
239
- var _entries$;
240
- var [name, message] = (_entries$ = entries === null || entries === void 0 ? void 0 : entries[0]) !== null && _entries$ !== void 0 ? _entries$ : [];
241
- if (name === '') {
242
- result[key] = message !== null && message !== void 0 ? message : '';
243
- }
235
+ for (var [key, _error] of Object.entries(uncontrolledState.initialError)) {
236
+ result[key] = dom.getErrors(dom.getValidationMessage(_error === null || _error === void 0 ? void 0 : _error['']));
244
237
  }
245
238
  return result;
246
239
  });
@@ -266,13 +259,12 @@ function useFieldset(ref, config) {
266
259
  field.setAttribute('aria-invalid', field.validationMessage !== '' ? 'true' : 'false');
267
260
  }
268
261
  setError(prev => {
269
- var _prev$key;
270
- var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
262
+ var prevMessage = dom.getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[key]);
271
263
  if (prevMessage === field.validationMessage) {
272
264
  return prev;
273
265
  }
274
266
  return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, prev), {}, {
275
- [key]: field.validationMessage
267
+ [key]: dom.getErrors(field.validationMessage)
276
268
  });
277
269
  });
278
270
  }
@@ -310,19 +302,21 @@ function useFieldset(ref, config) {
310
302
  */
311
303
  return new Proxy({}, {
312
304
  get(_target, key) {
313
- var _fieldsetConfig$const, _error$key;
305
+ var _fieldsetConfig$const;
314
306
  if (typeof key !== 'string') {
315
307
  return;
316
308
  }
317
309
  var fieldsetConfig = config !== null && config !== void 0 ? config : {};
318
310
  var constraint = (_fieldsetConfig$const = fieldsetConfig.constraint) === null || _fieldsetConfig$const === void 0 ? void 0 : _fieldsetConfig$const[key];
311
+ var errors = error === null || error === void 0 ? void 0 : error[key];
319
312
  var field = {
320
313
  config: _rollupPluginBabelHelpers.objectSpread2({
321
314
  name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
322
315
  defaultValue: uncontrolledState.defaultValue[key],
323
316
  initialError: uncontrolledState.initialError[key]
324
317
  }, constraint),
325
- error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
318
+ error: errors === null || errors === void 0 ? void 0 : errors[0],
319
+ errors
326
320
  };
327
321
  if (fieldsetConfig.form) {
328
322
  field.config.form = fieldsetConfig.form;
@@ -345,18 +339,14 @@ function useFieldList(ref, config) {
345
339
  var [uncontrolledState, setUncontrolledState] = react.useState(() => {
346
340
  var _config$defaultValue2;
347
341
  var initialError = [];
348
- for (var [name, message] of (_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : []) {
342
+ for (var [name, message] of Object.entries((_config$initialError2 = config === null || config === void 0 ? void 0 : config.initialError) !== null && _config$initialError2 !== void 0 ? _config$initialError2 : {})) {
349
343
  var _config$initialError2;
350
344
  var [index, ...paths] = dom.getPaths(name);
351
345
  if (typeof index === 'number') {
352
- var _initialError$index;
353
346
  var scopedName = dom.getName(paths);
354
- var _entries = (_initialError$index = initialError[index]) !== null && _initialError$index !== void 0 ? _initialError$index : [];
355
- if (scopedName === '' && _entries.length > 0 && _entries[0][0] !== '') {
356
- initialError[index] = [[scopedName, message], ..._entries];
357
- } else {
358
- initialError[index] = [..._entries, [scopedName, message]];
359
- }
347
+ initialError[index] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, initialError[index]), {}, {
348
+ [scopedName]: message
349
+ });
360
350
  }
361
351
  }
362
352
  return {
@@ -364,7 +354,7 @@ function useFieldList(ref, config) {
364
354
  initialError
365
355
  };
366
356
  });
367
- var [error, setError] = react.useState(() => uncontrolledState.initialError.map(error => error === null || error === void 0 ? void 0 : error[0][1]));
357
+ var [error, setError] = react.useState(() => uncontrolledState.initialError.map(error => dom.getErrors(dom.getValidationMessage(error === null || error === void 0 ? void 0 : error['']))));
368
358
  var [entries, setEntries] = react.useState(() => {
369
359
  var _config$defaultValue3;
370
360
  return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
@@ -387,24 +377,23 @@ function useFieldList(ref, config) {
387
377
  if (typeof index === 'number' && paths.length === 0) {
388
378
  if (field.dataset.conformTouched) {
389
379
  setError(prev => {
390
- var _prev$index;
391
- var prevMessage = (_prev$index = prev === null || prev === void 0 ? void 0 : prev[index]) !== null && _prev$index !== void 0 ? _prev$index : '';
380
+ var prevMessage = dom.getValidationMessage(prev === null || prev === void 0 ? void 0 : prev[index]);
392
381
  if (prevMessage === field.validationMessage) {
393
382
  return prev;
394
383
  }
395
- return [...prev.slice(0, index), field.validationMessage, ...prev.slice(index + 1)];
384
+ return [...prev.slice(0, index), dom.getErrors(field.validationMessage), ...prev.slice(index + 1)];
396
385
  });
397
386
  }
398
387
  event.preventDefault();
399
388
  }
400
389
  };
401
- var submitHandler = event => {
390
+ var listHandler = event => {
402
391
  var form = dom.getFormElement(ref.current);
403
- if (!form || event.target !== form || !(event.submitter instanceof HTMLButtonElement) || event.submitter.name !== 'conform/list') {
392
+ if (!form || event.target !== form) {
404
393
  return;
405
394
  }
406
- var command = dom.parseListCommand(event.submitter.value);
407
- if (command.scope !== configRef.current.name) {
395
+ var command = dom.parseListCommand(event.detail);
396
+ if ((command === null || command === void 0 ? void 0 : command.scope) !== configRef.current.name) {
408
397
  // Ensure the scope of the listener are limited to specific field name
409
398
  return;
410
399
  }
@@ -415,7 +404,9 @@ function useFieldList(ref, config) {
415
404
  case 'replace':
416
405
  return dom.updateList([...(entries !== null && entries !== void 0 ? entries : [])], _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command), {}, {
417
406
  payload: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, command.payload), {}, {
418
- defaultValue: ["".concat(Date.now()), command.payload.defaultValue]
407
+ defaultValue: ["".concat(Date.now()),
408
+ // @ts-expect-error unknown type as it is sent through network
409
+ command.payload.defaultValue]
419
410
  })
420
411
  }));
421
412
  default:
@@ -440,7 +431,6 @@ function useFieldList(ref, config) {
440
431
  }
441
432
  }
442
433
  });
443
- event.preventDefault();
444
434
  };
445
435
  var resetHandler = event => {
446
436
  var _fieldConfig$defaultV, _fieldConfig$defaultV2;
@@ -456,17 +446,21 @@ function useFieldList(ref, config) {
456
446
  setEntries(Object.entries((_fieldConfig$defaultV2 = fieldConfig.defaultValue) !== null && _fieldConfig$defaultV2 !== void 0 ? _fieldConfig$defaultV2 : [undefined]));
457
447
  setError([]);
458
448
  };
459
- document.addEventListener('submit', submitHandler, true);
449
+
450
+ // @ts-expect-error Custom event: conform/list
451
+ document.addEventListener('conform/list', listHandler, true);
460
452
  document.addEventListener('invalid', invalidHandler, true);
461
453
  document.addEventListener('reset', resetHandler);
462
454
  return () => {
463
- document.removeEventListener('submit', submitHandler, true);
455
+ // @ts-expect-error Custom event: conform/list
456
+ document.removeEventListener('conform/list', listHandler, true);
464
457
  document.removeEventListener('invalid', invalidHandler, true);
465
458
  document.removeEventListener('reset', resetHandler);
466
459
  };
467
460
  }, [ref]);
468
- return entries.map((_ref3, index) => {
469
- var [key, defaultValue] = _ref3;
461
+ return entries.map((_ref4, index) => {
462
+ var [key, defaultValue] = _ref4;
463
+ var errors = error[index];
470
464
  var fieldConfig = {
471
465
  name: "".concat(config.name, "[").concat(index, "]"),
472
466
  defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
@@ -479,98 +473,189 @@ function useFieldList(ref, config) {
479
473
  }
480
474
  return {
481
475
  key,
482
- error: error[index],
476
+ error: errors === null || errors === void 0 ? void 0 : errors[0],
477
+ errors,
483
478
  config: fieldConfig
484
479
  };
485
480
  });
486
481
  }
482
+
487
483
  /**
488
- * Returns the properties required to configure a shadow input for validation.
489
- * This is particular useful when integrating dropdown and datepicker whichs
490
- * introduces custom input mode.
491
- *
492
- * @see https://conform.guide/api/react#usecontrolledinput
484
+ * Triggering react custom change event
485
+ * Solution based on dom-testing-library
486
+ * @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
487
+ * @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
493
488
  */
494
- function useControlledInput(config) {
495
- var _config$defaultValue4;
496
- var ref = react.useRef(null);
497
- var inputRef = react.useRef(null);
498
- var configRef = react.useRef(config);
499
- var [uncontrolledState, setUncontrolledState] = react.useState({
500
- defaultValue: config.defaultValue,
501
- initialError: config.initialError
502
- });
503
- var [value, setValue] = react.useState("".concat((_config$defaultValue4 = config.defaultValue) !== null && _config$defaultValue4 !== void 0 ? _config$defaultValue4 : ''));
504
- var handleChange = eventOrValue => {
505
- if (!ref.current) {
506
- return;
489
+ function setNativeValue(element, value) {
490
+ if (element.value === value) {
491
+ // It will not trigger a change event if `element.value` is the same as the set value
492
+ return;
493
+ }
494
+ var {
495
+ set: valueSetter
496
+ } = Object.getOwnPropertyDescriptor(element, 'value') || {};
497
+ var prototype = Object.getPrototypeOf(element);
498
+ var {
499
+ set: prototypeValueSetter
500
+ } = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
501
+ if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
502
+ prototypeValueSetter.call(element, value);
503
+ } else {
504
+ if (valueSetter) {
505
+ valueSetter.call(element, value);
506
+ } else {
507
+ throw new Error('The given element does not have a value setter');
507
508
  }
508
- var newValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
509
- ref.current.value = newValue;
510
- ref.current.dispatchEvent(new InputEvent('input', {
511
- bubbles: true
512
- }));
513
- setValue(newValue);
514
- };
515
- var handleBlur = () => {
516
- var _ref$current;
517
- (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.dispatchEvent(new FocusEvent('blur', {
518
- bubbles: true
519
- }));
520
- };
521
- var handleInvalid = event => {
522
- event.preventDefault();
523
- };
524
- react.useEffect(() => {
525
- configRef.current = config;
509
+ }
510
+ }
511
+
512
+ /**
513
+ * useLayoutEffect is client-only.
514
+ * This basically makes it a no-op on server
515
+ */
516
+ var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
517
+ function useInputEvent(options) {
518
+ var ref = react.useRef(null);
519
+ var optionsRef = react.useRef(options);
520
+ var changeDispatched = react.useRef(false);
521
+ var focusDispatched = react.useRef(false);
522
+ var blurDispatched = react.useRef(false);
523
+ useSafeLayoutEffect(() => {
524
+ optionsRef.current = options;
526
525
  });
527
- react.useEffect(() => {
526
+ useSafeLayoutEffect(() => {
527
+ var getInputElement = () => {
528
+ var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
529
+ return (_optionsRef$current$g = (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : (_optionsRef$current$g2 = _optionsRef$current.getElement) === null || _optionsRef$current$g2 === void 0 ? void 0 : _optionsRef$current$g2.call(_optionsRef$current, ref.current)) !== null && _optionsRef$current$g !== void 0 ? _optionsRef$current$g : ref.current;
530
+ };
531
+ var inputHandler = event => {
532
+ var input = getInputElement();
533
+ if (input && event.target === input) {
534
+ changeDispatched.current = true;
535
+ }
536
+ };
537
+ var focusHandler = event => {
538
+ var input = getInputElement();
539
+ if (input && event.target === input) {
540
+ focusDispatched.current = true;
541
+ }
542
+ };
543
+ var blurHandler = event => {
544
+ var input = getInputElement();
545
+ if (input && event.target === input) {
546
+ blurDispatched.current = true;
547
+ }
548
+ };
549
+ var submitHandler = event => {
550
+ var input = getInputElement();
551
+ if (input !== null && input !== void 0 && input.form && event.target === input.form) {
552
+ var _optionsRef$current2, _optionsRef$current2$;
553
+ (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : (_optionsRef$current2$ = _optionsRef$current2.onSubmit) === null || _optionsRef$current2$ === void 0 ? void 0 : _optionsRef$current2$.call(_optionsRef$current2, event);
554
+ }
555
+ };
528
556
  var resetHandler = event => {
529
- var _configRef$current$de;
530
- var form = dom.getFormElement(ref.current);
531
- if (!form || event.target !== form) {
532
- return;
557
+ var input = getInputElement();
558
+ if (input !== null && input !== void 0 && input.form && event.target === input.form) {
559
+ var _optionsRef$current3, _optionsRef$current3$;
560
+ (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : (_optionsRef$current3$ = _optionsRef$current3.onReset) === null || _optionsRef$current3$ === void 0 ? void 0 : _optionsRef$current3$.call(_optionsRef$current3, event);
533
561
  }
534
- setUncontrolledState({
535
- defaultValue: configRef.current.defaultValue,
536
- initialError: configRef.current.initialError
537
- });
538
- setValue("".concat((_configRef$current$de = configRef.current.defaultValue) !== null && _configRef$current$de !== void 0 ? _configRef$current$de : ''));
539
562
  };
563
+ document.addEventListener('input', inputHandler, true);
564
+ document.addEventListener('focus', focusHandler, true);
565
+ document.addEventListener('blur', blurHandler, true);
566
+ document.addEventListener('submit', submitHandler);
540
567
  document.addEventListener('reset', resetHandler);
541
568
  return () => {
569
+ document.removeEventListener('input', inputHandler, true);
570
+ document.removeEventListener('focus', focusHandler, true);
571
+ document.removeEventListener('blur', blurHandler, true);
572
+ document.removeEventListener('submit', submitHandler);
542
573
  document.removeEventListener('reset', resetHandler);
543
574
  };
544
575
  }, []);
545
- return [_rollupPluginBabelHelpers.objectSpread2({
546
- ref,
547
- style: {
548
- position: 'absolute',
549
- width: '1px',
550
- height: '1px',
551
- padding: 0,
552
- margin: '-1px',
553
- overflow: 'hidden',
554
- clip: 'rect(0,0,0,0)',
555
- whiteSpace: 'nowrap',
556
- borderWidth: 0
557
- },
558
- tabIndex: -1,
559
- 'aria-hidden': true,
560
- onFocus() {
561
- var _inputRef$current;
562
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
563
- }
564
- }, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState))), {
565
- ref: inputRef,
566
- value,
567
- onChange: handleChange,
568
- onBlur: handleBlur,
569
- onInvalid: handleInvalid
570
- }];
576
+ var control = react.useMemo(() => {
577
+ var getInputElement = () => {
578
+ var _optionsRef$current$g3, _optionsRef$current4, _optionsRef$current4$;
579
+ return (_optionsRef$current$g3 = (_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 ? void 0 : (_optionsRef$current4$ = _optionsRef$current4.getElement) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, ref.current)) !== null && _optionsRef$current$g3 !== void 0 ? _optionsRef$current$g3 : ref.current;
580
+ };
581
+ return {
582
+ change(eventOrValue) {
583
+ var input = getInputElement();
584
+ if (!input) {
585
+ console.warn('Missing input ref; No change-related events will be dispatched');
586
+ return;
587
+ }
588
+ if (changeDispatched.current) {
589
+ changeDispatched.current = false;
590
+ return;
591
+ }
592
+ var previousValue = input.value;
593
+ var nextValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
594
+
595
+ // This make sure no event is dispatched on the first effect run
596
+ if (nextValue === previousValue) {
597
+ return;
598
+ }
599
+
600
+ // Dispatch beforeinput event before updating the input value
601
+ input.dispatchEvent(new Event('beforeinput', {
602
+ bubbles: true
603
+ }));
604
+ // Update the input value to trigger a change event
605
+ setNativeValue(input, nextValue);
606
+ // Dispatch input event with the updated input value
607
+ input.dispatchEvent(new InputEvent('input', {
608
+ bubbles: true
609
+ }));
610
+ // Reset the dispatched flag
611
+ changeDispatched.current = false;
612
+ },
613
+ focus() {
614
+ var input = getInputElement();
615
+ if (!input) {
616
+ console.warn('Missing input ref; No focus-related events will be dispatched');
617
+ return;
618
+ }
619
+ if (focusDispatched.current) {
620
+ focusDispatched.current = false;
621
+ return;
622
+ }
623
+ var focusinEvent = new FocusEvent('focusin', {
624
+ bubbles: true
625
+ });
626
+ var focusEvent = new FocusEvent('focus');
627
+ input.dispatchEvent(focusinEvent);
628
+ input.dispatchEvent(focusEvent);
629
+
630
+ // Reset the dispatched flag
631
+ focusDispatched.current = false;
632
+ },
633
+ blur() {
634
+ var input = getInputElement();
635
+ if (!input) {
636
+ console.warn('Missing input ref; No blur-related events will be dispatched');
637
+ return;
638
+ }
639
+ if (blurDispatched.current) {
640
+ blurDispatched.current = false;
641
+ return;
642
+ }
643
+ var focusoutEvent = new FocusEvent('focusout', {
644
+ bubbles: true
645
+ });
646
+ var blurEvent = new FocusEvent('blur');
647
+ input.dispatchEvent(focusoutEvent);
648
+ input.dispatchEvent(blurEvent);
649
+
650
+ // Reset the dispatched flag
651
+ blurDispatched.current = false;
652
+ }
653
+ };
654
+ }, []);
655
+ return [ref, control];
571
656
  }
572
657
 
573
- exports.useControlledInput = useControlledInput;
574
658
  exports.useFieldList = useFieldList;
575
659
  exports.useFieldset = useFieldset;
576
660
  exports.useForm = useForm;
661
+ exports.useInputEvent = useInputEvent;
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, hasError, list, validate, requestCommand, requestSubmit, parse, shouldValidate, } from '@conform-to/dom';
1
+ export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, list, validate, requestIntent, requestSubmit, parse, validateConstraint, } from '@conform-to/dom';
2
2
  export * from './hooks';
3
3
  export * as conform from './helpers';