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