@conform-to/react 0.5.0-pre.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +260 -277
- package/helpers.d.ts +16 -7
- package/helpers.js +47 -7
- package/hooks.d.ts +48 -31
- package/hooks.js +235 -81
- package/index.d.ts +1 -1
- package/index.js +17 -0
- package/module/helpers.js +47 -7
- package/module/hooks.js +237 -84
- package/module/index.js +2 -2
- package/package.json +2 -2
package/hooks.js
CHANGED
|
@@ -11,7 +11,7 @@ var helpers = require('./helpers.js');
|
|
|
11
11
|
* Returns properties required to hook into form events.
|
|
12
12
|
* Applied custom validation and define when error should be reported.
|
|
13
13
|
*
|
|
14
|
-
* @see https://
|
|
14
|
+
* @see https://conform.guide/api/react#useform
|
|
15
15
|
*/
|
|
16
16
|
function useForm() {
|
|
17
17
|
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -25,17 +25,26 @@ function useForm() {
|
|
|
25
25
|
})) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
|
|
26
26
|
return message !== null && message !== void 0 ? message : '';
|
|
27
27
|
});
|
|
28
|
-
var [
|
|
29
|
-
var
|
|
30
|
-
|
|
28
|
+
var [uncontrolledState, setUncontrolledState] = react.useState(() => {
|
|
29
|
+
var submission = config.state;
|
|
30
|
+
if (!submission) {
|
|
31
|
+
return {
|
|
32
|
+
defaultValue: config.defaultValue
|
|
33
|
+
};
|
|
34
|
+
}
|
|
31
35
|
return {
|
|
32
|
-
defaultValue:
|
|
33
|
-
initialError: error.filter(_ref2 => {
|
|
36
|
+
defaultValue: submission.value,
|
|
37
|
+
initialError: submission.error.filter(_ref2 => {
|
|
34
38
|
var [name] = _ref2;
|
|
35
|
-
return name !== '' && dom.
|
|
39
|
+
return name !== '' && dom.shouldValidate(submission, name);
|
|
36
40
|
})
|
|
37
41
|
};
|
|
38
42
|
});
|
|
43
|
+
var fieldsetConfig = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, uncontrolledState), {}, {
|
|
44
|
+
constraint: config.constraint,
|
|
45
|
+
form: config.id
|
|
46
|
+
});
|
|
47
|
+
var fieldset = useFieldset(ref, fieldsetConfig);
|
|
39
48
|
var [noValidate, setNoValidate] = react.useState(config.noValidate || !config.fallbackNative);
|
|
40
49
|
react.useEffect(() => {
|
|
41
50
|
configRef.current = config;
|
|
@@ -59,11 +68,8 @@ function useForm() {
|
|
|
59
68
|
if (!form || !dom.isFieldElement(field) || field.form !== form) {
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
62
|
-
if (formConfig.initialReport === 'onChange') {
|
|
63
|
-
field.
|
|
64
|
-
}
|
|
65
|
-
if (field.dataset.conformTouched) {
|
|
66
|
-
dom.requestValidate(form, field.name);
|
|
71
|
+
if (field.dataset.conformTouched || formConfig.initialReport === 'onChange') {
|
|
72
|
+
dom.requestCommand(form, dom.validate(field.name));
|
|
67
73
|
}
|
|
68
74
|
};
|
|
69
75
|
var handleBlur = event => {
|
|
@@ -74,8 +80,7 @@ function useForm() {
|
|
|
74
80
|
return;
|
|
75
81
|
}
|
|
76
82
|
if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
|
|
77
|
-
field.
|
|
78
|
-
dom.requestValidate(form, field.name);
|
|
83
|
+
dom.requestCommand(form, dom.validate(field.name));
|
|
79
84
|
}
|
|
80
85
|
};
|
|
81
86
|
var handleInvalid = event => {
|
|
@@ -100,11 +105,12 @@ function useForm() {
|
|
|
100
105
|
for (var field of form.elements) {
|
|
101
106
|
if (dom.isFieldElement(field)) {
|
|
102
107
|
delete field.dataset.conformTouched;
|
|
108
|
+
field.setAttribute('aria-invalid', 'false');
|
|
103
109
|
field.setCustomValidity('');
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
setError('');
|
|
107
|
-
|
|
113
|
+
setUncontrolledState({
|
|
108
114
|
defaultValue: formConfig.defaultValue,
|
|
109
115
|
initialError: []
|
|
110
116
|
});
|
|
@@ -127,11 +133,13 @@ function useForm() {
|
|
|
127
133
|
document.removeEventListener('reset', handleReset);
|
|
128
134
|
};
|
|
129
135
|
}, []);
|
|
130
|
-
|
|
136
|
+
var form = {
|
|
137
|
+
id: config.id,
|
|
131
138
|
ref,
|
|
132
139
|
error,
|
|
133
140
|
props: {
|
|
134
141
|
ref,
|
|
142
|
+
id: config.id,
|
|
135
143
|
noValidate,
|
|
136
144
|
onSubmit(event) {
|
|
137
145
|
var form = event.currentTarget;
|
|
@@ -173,16 +181,6 @@ function useForm() {
|
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
}
|
|
176
|
-
|
|
177
|
-
// Touch all fields only if the submitter is not a command button
|
|
178
|
-
if (submission.type === 'submit') {
|
|
179
|
-
for (var field of form.elements) {
|
|
180
|
-
if (dom.isFieldElement(field)) {
|
|
181
|
-
// Mark the field as touched
|
|
182
|
-
field.dataset.conformTouched = 'true';
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
184
|
if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && dom.hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
|
|
187
185
|
event.preventDefault();
|
|
188
186
|
} else {
|
|
@@ -202,6 +200,7 @@ function useForm() {
|
|
|
202
200
|
},
|
|
203
201
|
config: fieldsetConfig
|
|
204
202
|
};
|
|
203
|
+
return [form, fieldset];
|
|
205
204
|
}
|
|
206
205
|
|
|
207
206
|
/**
|
|
@@ -262,6 +261,10 @@ function useFieldset(ref, config) {
|
|
|
262
261
|
// Update the error only if the field belongs to the fieldset
|
|
263
262
|
if (typeof key === 'string' && paths.length === 0) {
|
|
264
263
|
if (field.dataset.conformTouched) {
|
|
264
|
+
// Update the aria attribute only if it is set
|
|
265
|
+
if (field.getAttribute('aria-invalid')) {
|
|
266
|
+
field.setAttribute('aria-invalid', field.validationMessage !== '' ? 'true' : 'false');
|
|
267
|
+
}
|
|
265
268
|
setError(prev => {
|
|
266
269
|
var _prev$key;
|
|
267
270
|
var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
|
|
@@ -316,21 +319,26 @@ function useFieldset(ref, config) {
|
|
|
316
319
|
var field = {
|
|
317
320
|
config: _rollupPluginBabelHelpers.objectSpread2({
|
|
318
321
|
name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
|
|
319
|
-
form: fieldsetConfig.form,
|
|
320
322
|
defaultValue: uncontrolledState.defaultValue[key],
|
|
321
323
|
initialError: uncontrolledState.initialError[key]
|
|
322
324
|
}, constraint),
|
|
323
325
|
error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
|
|
324
326
|
};
|
|
327
|
+
if (fieldsetConfig.form) {
|
|
328
|
+
field.config.form = fieldsetConfig.form;
|
|
329
|
+
field.config.id = "".concat(fieldsetConfig.form, "-").concat(field.config.name);
|
|
330
|
+
field.config.errorId = "".concat(field.config.id, "-error");
|
|
331
|
+
}
|
|
325
332
|
return field;
|
|
326
333
|
}
|
|
327
334
|
});
|
|
328
335
|
}
|
|
336
|
+
|
|
329
337
|
/**
|
|
330
338
|
* Returns a list of key and config, with a group of helpers
|
|
331
339
|
* configuring buttons for list manipulation
|
|
332
340
|
*
|
|
333
|
-
* @see https://
|
|
341
|
+
* @see https://conform.guide/api/react#usefieldlist
|
|
334
342
|
*/
|
|
335
343
|
function useFieldList(ref, config) {
|
|
336
344
|
var configRef = react.useRef(config);
|
|
@@ -361,41 +369,6 @@ function useFieldList(ref, config) {
|
|
|
361
369
|
var _config$defaultValue3;
|
|
362
370
|
return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
|
|
363
371
|
});
|
|
364
|
-
var list = entries.map((_ref3, index) => {
|
|
365
|
-
var [key, defaultValue] = _ref3;
|
|
366
|
-
return {
|
|
367
|
-
key,
|
|
368
|
-
error: error[index],
|
|
369
|
-
config: {
|
|
370
|
-
name: "".concat(config.name, "[").concat(index, "]"),
|
|
371
|
-
form: config.form,
|
|
372
|
-
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
|
|
373
|
-
initialError: uncontrolledState.initialError[index]
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
/***
|
|
379
|
-
* This use proxy to capture all information about the command and
|
|
380
|
-
* have it encoded in the value.
|
|
381
|
-
*/
|
|
382
|
-
var command = new Proxy({}, {
|
|
383
|
-
get(_target, type) {
|
|
384
|
-
return function () {
|
|
385
|
-
var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
386
|
-
return {
|
|
387
|
-
name: 'conform/list',
|
|
388
|
-
value: JSON.stringify({
|
|
389
|
-
type,
|
|
390
|
-
scope: config.name,
|
|
391
|
-
payload
|
|
392
|
-
}),
|
|
393
|
-
form: config.form,
|
|
394
|
-
formNoValidate: true
|
|
395
|
-
};
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
372
|
react.useEffect(() => {
|
|
400
373
|
configRef.current = config;
|
|
401
374
|
});
|
|
@@ -492,16 +465,32 @@ function useFieldList(ref, config) {
|
|
|
492
465
|
document.removeEventListener('reset', resetHandler);
|
|
493
466
|
};
|
|
494
467
|
}, [ref]);
|
|
495
|
-
return
|
|
496
|
-
|
|
497
|
-
|
|
468
|
+
return entries.map((_ref3, index) => {
|
|
469
|
+
var [key, defaultValue] = _ref3;
|
|
470
|
+
var fieldConfig = {
|
|
471
|
+
name: "".concat(config.name, "[").concat(index, "]"),
|
|
472
|
+
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
|
|
473
|
+
initialError: uncontrolledState.initialError[index]
|
|
474
|
+
};
|
|
475
|
+
if (config.form) {
|
|
476
|
+
fieldConfig.form = config.form;
|
|
477
|
+
fieldConfig.id = "".concat(config.form, "-").concat(config.name);
|
|
478
|
+
fieldConfig.errorId = "".concat(fieldConfig.id, "-error");
|
|
479
|
+
}
|
|
480
|
+
return {
|
|
481
|
+
key,
|
|
482
|
+
error: error[index],
|
|
483
|
+
config: fieldConfig
|
|
484
|
+
};
|
|
485
|
+
});
|
|
498
486
|
}
|
|
499
487
|
/**
|
|
500
488
|
* Returns the properties required to configure a shadow input for validation.
|
|
501
489
|
* This is particular useful when integrating dropdown and datepicker whichs
|
|
502
490
|
* introduces custom input mode.
|
|
503
491
|
*
|
|
504
|
-
* @
|
|
492
|
+
* @deprecated Please use the `useInputEvent` hook instead
|
|
493
|
+
* @see https://conform.guide/api/react#usecontrolledinput
|
|
505
494
|
*/
|
|
506
495
|
function useControlledInput(config) {
|
|
507
496
|
var _config$defaultValue4;
|
|
@@ -556,24 +545,13 @@ function useControlledInput(config) {
|
|
|
556
545
|
}, []);
|
|
557
546
|
return [_rollupPluginBabelHelpers.objectSpread2({
|
|
558
547
|
ref,
|
|
559
|
-
style: {
|
|
560
|
-
position: 'absolute',
|
|
561
|
-
width: '1px',
|
|
562
|
-
height: '1px',
|
|
563
|
-
padding: 0,
|
|
564
|
-
margin: '-1px',
|
|
565
|
-
overflow: 'hidden',
|
|
566
|
-
clip: 'rect(0,0,0,0)',
|
|
567
|
-
whiteSpace: 'nowrap',
|
|
568
|
-
borderWidth: 0
|
|
569
|
-
},
|
|
570
|
-
tabIndex: -1,
|
|
571
|
-
'aria-hidden': true,
|
|
572
548
|
onFocus() {
|
|
573
549
|
var _inputRef$current;
|
|
574
550
|
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
|
|
575
551
|
}
|
|
576
|
-
}, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState)
|
|
552
|
+
}, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState), {
|
|
553
|
+
hidden: true
|
|
554
|
+
})), {
|
|
577
555
|
ref: inputRef,
|
|
578
556
|
value,
|
|
579
557
|
onChange: handleChange,
|
|
@@ -582,7 +560,183 @@ function useControlledInput(config) {
|
|
|
582
560
|
}];
|
|
583
561
|
}
|
|
584
562
|
|
|
563
|
+
/**
|
|
564
|
+
* Triggering react custom change event
|
|
565
|
+
* Solution based on dom-testing-library
|
|
566
|
+
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
567
|
+
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
568
|
+
*/
|
|
569
|
+
function setNativeValue(element, value) {
|
|
570
|
+
if (element.value === value) {
|
|
571
|
+
// It will not trigger a change event if `element.value` is the same as the set value
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
var {
|
|
575
|
+
set: valueSetter
|
|
576
|
+
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
|
|
577
|
+
var prototype = Object.getPrototypeOf(element);
|
|
578
|
+
var {
|
|
579
|
+
set: prototypeValueSetter
|
|
580
|
+
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
581
|
+
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
582
|
+
prototypeValueSetter.call(element, value);
|
|
583
|
+
} else {
|
|
584
|
+
if (valueSetter) {
|
|
585
|
+
valueSetter.call(element, value);
|
|
586
|
+
} else {
|
|
587
|
+
throw new Error('The given element does not have a value setter');
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* useLayoutEffect is client-only.
|
|
594
|
+
* This basically makes it a no-op on server
|
|
595
|
+
*/
|
|
596
|
+
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
|
|
597
|
+
function useInputEvent(options) {
|
|
598
|
+
var ref = react.useRef(null);
|
|
599
|
+
var optionsRef = react.useRef(options);
|
|
600
|
+
var changeDispatched = react.useRef(false);
|
|
601
|
+
var focusDispatched = react.useRef(false);
|
|
602
|
+
var blurDispatched = react.useRef(false);
|
|
603
|
+
useSafeLayoutEffect(() => {
|
|
604
|
+
optionsRef.current = options;
|
|
605
|
+
});
|
|
606
|
+
useSafeLayoutEffect(() => {
|
|
607
|
+
var getInputElement = () => {
|
|
608
|
+
var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
|
|
609
|
+
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;
|
|
610
|
+
};
|
|
611
|
+
var inputHandler = event => {
|
|
612
|
+
var input = getInputElement();
|
|
613
|
+
if (input && event.target === input) {
|
|
614
|
+
changeDispatched.current = true;
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
var focusHandler = event => {
|
|
618
|
+
var input = getInputElement();
|
|
619
|
+
if (input && event.target === input) {
|
|
620
|
+
focusDispatched.current = true;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
var blurHandler = event => {
|
|
624
|
+
var input = getInputElement();
|
|
625
|
+
if (input && event.target === input) {
|
|
626
|
+
blurDispatched.current = true;
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
var submitHandler = event => {
|
|
630
|
+
var input = getInputElement();
|
|
631
|
+
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
|
|
632
|
+
var _optionsRef$current2, _optionsRef$current2$;
|
|
633
|
+
(_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);
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
var resetHandler = event => {
|
|
637
|
+
var input = getInputElement();
|
|
638
|
+
if (input !== null && input !== void 0 && input.form && event.target === input.form) {
|
|
639
|
+
var _optionsRef$current3, _optionsRef$current3$;
|
|
640
|
+
(_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);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
document.addEventListener('input', inputHandler, true);
|
|
644
|
+
document.addEventListener('focus', focusHandler, true);
|
|
645
|
+
document.addEventListener('blur', blurHandler, true);
|
|
646
|
+
document.addEventListener('submit', submitHandler);
|
|
647
|
+
document.addEventListener('reset', resetHandler);
|
|
648
|
+
return () => {
|
|
649
|
+
document.removeEventListener('input', inputHandler, true);
|
|
650
|
+
document.removeEventListener('focus', focusHandler, true);
|
|
651
|
+
document.removeEventListener('blur', blurHandler, true);
|
|
652
|
+
document.removeEventListener('submit', submitHandler);
|
|
653
|
+
document.removeEventListener('reset', resetHandler);
|
|
654
|
+
};
|
|
655
|
+
}, []);
|
|
656
|
+
var control = react.useMemo(() => {
|
|
657
|
+
var getInputElement = () => {
|
|
658
|
+
var _optionsRef$current$g3, _optionsRef$current4, _optionsRef$current4$;
|
|
659
|
+
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;
|
|
660
|
+
};
|
|
661
|
+
return {
|
|
662
|
+
change(eventOrValue) {
|
|
663
|
+
var input = getInputElement();
|
|
664
|
+
if (!input) {
|
|
665
|
+
console.warn('Missing input ref; No change-related events will be dispatched');
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (changeDispatched.current) {
|
|
669
|
+
changeDispatched.current = false;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
var previousValue = input.value;
|
|
673
|
+
var nextValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
|
|
674
|
+
|
|
675
|
+
// This make sure no event is dispatched on the first effect run
|
|
676
|
+
if (nextValue === previousValue) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Dispatch beforeinput event before updating the input value
|
|
681
|
+
input.dispatchEvent(new Event('beforeinput', {
|
|
682
|
+
bubbles: true
|
|
683
|
+
}));
|
|
684
|
+
// Update the input value to trigger a change event
|
|
685
|
+
setNativeValue(input, nextValue);
|
|
686
|
+
// Dispatch input event with the updated input value
|
|
687
|
+
input.dispatchEvent(new InputEvent('input', {
|
|
688
|
+
bubbles: true
|
|
689
|
+
}));
|
|
690
|
+
// Reset the dispatched flag
|
|
691
|
+
changeDispatched.current = false;
|
|
692
|
+
},
|
|
693
|
+
focus() {
|
|
694
|
+
var input = getInputElement();
|
|
695
|
+
if (!input) {
|
|
696
|
+
console.warn('Missing input ref; No focus-related events will be dispatched');
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (focusDispatched.current) {
|
|
700
|
+
focusDispatched.current = false;
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
var focusinEvent = new FocusEvent('focusin', {
|
|
704
|
+
bubbles: true
|
|
705
|
+
});
|
|
706
|
+
var focusEvent = new FocusEvent('focus');
|
|
707
|
+
input.dispatchEvent(focusinEvent);
|
|
708
|
+
input.dispatchEvent(focusEvent);
|
|
709
|
+
|
|
710
|
+
// Reset the dispatched flag
|
|
711
|
+
focusDispatched.current = false;
|
|
712
|
+
},
|
|
713
|
+
blur() {
|
|
714
|
+
var input = getInputElement();
|
|
715
|
+
if (!input) {
|
|
716
|
+
console.warn('Missing input ref; No blur-related events will be dispatched');
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (blurDispatched.current) {
|
|
720
|
+
blurDispatched.current = false;
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
var focusoutEvent = new FocusEvent('focusout', {
|
|
724
|
+
bubbles: true
|
|
725
|
+
});
|
|
726
|
+
var blurEvent = new FocusEvent('blur');
|
|
727
|
+
input.dispatchEvent(focusoutEvent);
|
|
728
|
+
input.dispatchEvent(blurEvent);
|
|
729
|
+
|
|
730
|
+
// Reset the dispatched flag
|
|
731
|
+
blurDispatched.current = false;
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}, []);
|
|
735
|
+
return [ref, control];
|
|
736
|
+
}
|
|
737
|
+
|
|
585
738
|
exports.useControlledInput = useControlledInput;
|
|
586
739
|
exports.useFieldList = useFieldList;
|
|
587
740
|
exports.useFieldset = useFieldset;
|
|
588
741
|
exports.useForm = useForm;
|
|
742
|
+
exports.useInputEvent = useInputEvent;
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, hasError, parse, shouldValidate, } from '@conform-to/dom';
|
|
1
|
+
export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, hasError, list, validate, requestCommand, requestSubmit, parse, shouldValidate, } from '@conform-to/dom';
|
|
2
2
|
export * from './hooks';
|
|
3
3
|
export * as conform from './helpers';
|
package/index.js
CHANGED
|
@@ -16,16 +16,33 @@ Object.defineProperty(exports, 'hasError', {
|
|
|
16
16
|
enumerable: true,
|
|
17
17
|
get: function () { return dom.hasError; }
|
|
18
18
|
});
|
|
19
|
+
Object.defineProperty(exports, 'list', {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return dom.list; }
|
|
22
|
+
});
|
|
19
23
|
Object.defineProperty(exports, 'parse', {
|
|
20
24
|
enumerable: true,
|
|
21
25
|
get: function () { return dom.parse; }
|
|
22
26
|
});
|
|
27
|
+
Object.defineProperty(exports, 'requestCommand', {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return dom.requestCommand; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, 'requestSubmit', {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return dom.requestSubmit; }
|
|
34
|
+
});
|
|
23
35
|
Object.defineProperty(exports, 'shouldValidate', {
|
|
24
36
|
enumerable: true,
|
|
25
37
|
get: function () { return dom.shouldValidate; }
|
|
26
38
|
});
|
|
39
|
+
Object.defineProperty(exports, 'validate', {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () { return dom.validate; }
|
|
42
|
+
});
|
|
27
43
|
exports.useControlledInput = hooks.useControlledInput;
|
|
28
44
|
exports.useFieldList = hooks.useFieldList;
|
|
29
45
|
exports.useFieldset = hooks.useFieldset;
|
|
30
46
|
exports.useForm = hooks.useForm;
|
|
47
|
+
exports.useInputEvent = hooks.useInputEvent;
|
|
31
48
|
exports.conform = helpers;
|
package/module/helpers.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Style to make the input element visually hidden
|
|
3
|
+
* Based on the `sr-only` class from tailwindcss
|
|
4
|
+
*/
|
|
5
|
+
var hiddenStyle = {
|
|
6
|
+
position: 'absolute',
|
|
7
|
+
width: '1px',
|
|
8
|
+
height: '1px',
|
|
9
|
+
padding: 0,
|
|
10
|
+
margin: '-1px',
|
|
11
|
+
overflow: 'hidden',
|
|
12
|
+
clip: 'rect(0,0,0,0)',
|
|
13
|
+
whiteSpace: 'nowrap',
|
|
14
|
+
border: 0
|
|
15
|
+
};
|
|
1
16
|
function input(config) {
|
|
17
|
+
var _config$initialError;
|
|
2
18
|
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
3
19
|
var attributes = {
|
|
20
|
+
id: config.id,
|
|
4
21
|
type: options.type,
|
|
5
22
|
name: config.name,
|
|
6
23
|
form: config.form,
|
|
@@ -11,8 +28,15 @@ function input(config) {
|
|
|
11
28
|
max: config.max,
|
|
12
29
|
step: config.step,
|
|
13
30
|
pattern: config.pattern,
|
|
14
|
-
multiple: config.multiple
|
|
31
|
+
multiple: config.multiple,
|
|
32
|
+
'aria-invalid': Boolean((_config$initialError = config.initialError) === null || _config$initialError === void 0 ? void 0 : _config$initialError.length),
|
|
33
|
+
'aria-describedby': config.errorId
|
|
15
34
|
};
|
|
35
|
+
if (options !== null && options !== void 0 && options.hidden) {
|
|
36
|
+
attributes.style = hiddenStyle;
|
|
37
|
+
attributes.tabIndex = -1;
|
|
38
|
+
attributes['aria-hidden'] = true;
|
|
39
|
+
}
|
|
16
40
|
if (config.initialError && config.initialError.length > 0) {
|
|
17
41
|
attributes.autoFocus = true;
|
|
18
42
|
}
|
|
@@ -25,31 +49,47 @@ function input(config) {
|
|
|
25
49
|
}
|
|
26
50
|
return attributes;
|
|
27
51
|
}
|
|
28
|
-
function select(config) {
|
|
29
|
-
var _config$defaultValue;
|
|
52
|
+
function select(config, options) {
|
|
53
|
+
var _config$defaultValue, _config$initialError2;
|
|
30
54
|
var attributes = {
|
|
55
|
+
id: config.id,
|
|
31
56
|
name: config.name,
|
|
32
57
|
form: config.form,
|
|
33
58
|
defaultValue: config.multiple ? Array.isArray(config.defaultValue) ? config.defaultValue : [] : "".concat((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : ''),
|
|
34
59
|
required: config.required,
|
|
35
|
-
multiple: config.multiple
|
|
60
|
+
multiple: config.multiple,
|
|
61
|
+
'aria-invalid': Boolean((_config$initialError2 = config.initialError) === null || _config$initialError2 === void 0 ? void 0 : _config$initialError2.length),
|
|
62
|
+
'aria-describedby': config.errorId
|
|
36
63
|
};
|
|
64
|
+
if (options !== null && options !== void 0 && options.hidden) {
|
|
65
|
+
attributes.style = hiddenStyle;
|
|
66
|
+
attributes.tabIndex = -1;
|
|
67
|
+
attributes['aria-hidden'] = true;
|
|
68
|
+
}
|
|
37
69
|
if (config.initialError && config.initialError.length > 0) {
|
|
38
70
|
attributes.autoFocus = true;
|
|
39
71
|
}
|
|
40
72
|
return attributes;
|
|
41
73
|
}
|
|
42
|
-
function textarea(config) {
|
|
43
|
-
var _config$defaultValue2;
|
|
74
|
+
function textarea(config, options) {
|
|
75
|
+
var _config$defaultValue2, _config$initialError3;
|
|
44
76
|
var attributes = {
|
|
77
|
+
id: config.id,
|
|
45
78
|
name: config.name,
|
|
46
79
|
form: config.form,
|
|
47
80
|
defaultValue: "".concat((_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : ''),
|
|
48
81
|
required: config.required,
|
|
49
82
|
minLength: config.minLength,
|
|
50
83
|
maxLength: config.maxLength,
|
|
51
|
-
autoFocus: Boolean(config.initialError)
|
|
84
|
+
autoFocus: Boolean(config.initialError),
|
|
85
|
+
'aria-invalid': Boolean((_config$initialError3 = config.initialError) === null || _config$initialError3 === void 0 ? void 0 : _config$initialError3.length),
|
|
86
|
+
'aria-describedby': config.errorId
|
|
52
87
|
};
|
|
88
|
+
if (options !== null && options !== void 0 && options.hidden) {
|
|
89
|
+
attributes.style = hiddenStyle;
|
|
90
|
+
attributes.tabIndex = -1;
|
|
91
|
+
attributes['aria-hidden'] = true;
|
|
92
|
+
}
|
|
53
93
|
if (config.initialError && config.initialError.length > 0) {
|
|
54
94
|
attributes.autoFocus = true;
|
|
55
95
|
}
|