@conform-to/react 0.5.1 → 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/README.md +11 -89
- package/_virtual/_rollupPluginBabelHelpers.js +17 -0
- package/helpers.d.ts +4 -3
- package/helpers.js +15 -3
- package/hooks.d.ts +15 -41
- package/hooks.js +91 -172
- package/index.d.ts +1 -1
- package/index.js +6 -11
- package/module/_virtual/_rollupPluginBabelHelpers.js +16 -1
- package/module/helpers.js +7 -4
- package/module/hooks.js +93 -173
- package/module/index.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
- [useFieldset](#usefieldset)
|
|
11
11
|
- [useFieldList](#usefieldlist)
|
|
12
12
|
- [useInputEvent](#useinputevent)
|
|
13
|
-
- [useControlledInput](#usecontrolledinput)
|
|
14
13
|
- [conform](#conform)
|
|
15
14
|
- [list](#list)
|
|
16
15
|
- [validate](#validate)
|
|
17
|
-
- [
|
|
16
|
+
- [requestIntent](#requestintent)
|
|
18
17
|
- [getFormElements](#getformelements)
|
|
19
18
|
- [hasError](#haserror)
|
|
20
19
|
- [parse](#parse)
|
|
@@ -43,14 +42,6 @@ function LoginForm() {
|
|
|
43
42
|
*/
|
|
44
43
|
id: undefined,
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
* Validation mode.
|
|
48
|
-
* Support "client-only" or "server-validation".
|
|
49
|
-
*
|
|
50
|
-
* Default to `client-only`.
|
|
51
|
-
*/
|
|
52
|
-
mode: 'client-only',
|
|
53
|
-
|
|
54
45
|
/**
|
|
55
46
|
* Define when the error should be reported initially.
|
|
56
47
|
* Support "onSubmit", "onChange", "onBlur".
|
|
@@ -330,49 +321,6 @@ function MuiForm() {
|
|
|
330
321
|
|
|
331
322
|
---
|
|
332
323
|
|
|
333
|
-
### useControlledInput
|
|
334
|
-
|
|
335
|
-
> This API is deprecated and replaced with the [useInputEvent](#useinputevent) hook.
|
|
336
|
-
|
|
337
|
-
It returns the properties required to configure a shadow input for validation and helper to integrate it. This is particularly useful when [integrating custom input components](/docs/integrations.md#custom-input-component) like dropdown and datepicker.
|
|
338
|
-
|
|
339
|
-
```tsx
|
|
340
|
-
import { useForm, useControlledInput } from '@conform-to/react';
|
|
341
|
-
import { Select, MenuItem } from '@mui/material';
|
|
342
|
-
|
|
343
|
-
function MuiForm() {
|
|
344
|
-
const [form, { category }] = useForm();
|
|
345
|
-
const [inputProps, control] = useControlledInput(category.config);
|
|
346
|
-
|
|
347
|
-
return (
|
|
348
|
-
<form {...form.props}>
|
|
349
|
-
{/* Render a shadow input somewhere */}
|
|
350
|
-
<input {...inputProps} />
|
|
351
|
-
|
|
352
|
-
{/* MUI Select is a controlled component */}
|
|
353
|
-
<TextField
|
|
354
|
-
label="Category"
|
|
355
|
-
inputRef={control.ref}
|
|
356
|
-
value={control.value}
|
|
357
|
-
onChange={control.onChange}
|
|
358
|
-
onBlur={control.onBlur}
|
|
359
|
-
inputProps={{
|
|
360
|
-
onInvalid: control.onInvalid,
|
|
361
|
-
}}
|
|
362
|
-
select
|
|
363
|
-
>
|
|
364
|
-
<MenuItem value="">Please select</MenuItem>
|
|
365
|
-
<MenuItem value="a">Category A</MenuItem>
|
|
366
|
-
<MenuItem value="b">Category B</MenuItem>
|
|
367
|
-
<MenuItem value="c">Category C</MenuItem>
|
|
368
|
-
</TextField>
|
|
369
|
-
</form>
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
---
|
|
375
|
-
|
|
376
324
|
### conform
|
|
377
325
|
|
|
378
326
|
It provides several helpers to remove the boilerplate when configuring a form control.
|
|
@@ -499,9 +447,9 @@ function Example() {
|
|
|
499
447
|
|
|
500
448
|
---
|
|
501
449
|
|
|
502
|
-
###
|
|
450
|
+
### requestIntent
|
|
503
451
|
|
|
504
|
-
It lets you [trigger
|
|
452
|
+
It lets you [trigger an intent](/docs/commands.md#triggering-an-intent) without requiring users to click on a button. It supports both [list](#list) and [validate](#validate) intent.
|
|
505
453
|
|
|
506
454
|
```tsx
|
|
507
455
|
import {
|
|
@@ -509,7 +457,7 @@ import {
|
|
|
509
457
|
useFieldList,
|
|
510
458
|
conform,
|
|
511
459
|
list,
|
|
512
|
-
|
|
460
|
+
requestIntent,
|
|
513
461
|
} from '@conform-to/react';
|
|
514
462
|
import DragAndDrop from 'awesome-dnd-example';
|
|
515
463
|
|
|
@@ -518,7 +466,7 @@ export default function Todos() {
|
|
|
518
466
|
const taskList = useFieldList(form.ref, tasks.config);
|
|
519
467
|
|
|
520
468
|
const handleDrop = (from, to) =>
|
|
521
|
-
|
|
469
|
+
requestIntent(form.ref.current, list.reorder({ from, to }));
|
|
522
470
|
|
|
523
471
|
return (
|
|
524
472
|
<form {...form.props}>
|
|
@@ -580,27 +528,6 @@ export default function LoginForm() {
|
|
|
580
528
|
|
|
581
529
|
---
|
|
582
530
|
|
|
583
|
-
### hasError
|
|
584
|
-
|
|
585
|
-
This helper checks if there is any message defined in error array with the provided name.
|
|
586
|
-
|
|
587
|
-
```ts
|
|
588
|
-
import { hasError } from '@conform-to/react';
|
|
589
|
-
|
|
590
|
-
/**
|
|
591
|
-
* Assume the error looks like this:
|
|
592
|
-
*/
|
|
593
|
-
const error = [['email', 'Email is required']];
|
|
594
|
-
|
|
595
|
-
// This will log `true`
|
|
596
|
-
console.log(hasError(error, 'email'));
|
|
597
|
-
|
|
598
|
-
// This will log `false`
|
|
599
|
-
console.log(hasError(error, 'password'));
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
531
|
### parse
|
|
605
532
|
|
|
606
533
|
It parses the formData based on the [naming convention](/docs/submission).
|
|
@@ -624,20 +551,15 @@ This helper checks if the scope of validation includes a specific field by check
|
|
|
624
551
|
import { shouldValidate } from '@conform-to/react';
|
|
625
552
|
|
|
626
553
|
/**
|
|
627
|
-
* The submission
|
|
628
|
-
* If the
|
|
629
|
-
* If the
|
|
554
|
+
* The submission intent give us hint on what should be valdiated.
|
|
555
|
+
* If the intent is 'validate/:field', only the field with name matching must be validated.
|
|
556
|
+
* If the intent is undefined, everything should be validated (Default submission)
|
|
630
557
|
*/
|
|
631
|
-
const
|
|
632
|
-
context: 'validate',
|
|
633
|
-
intent: 'email',
|
|
634
|
-
value: {},
|
|
635
|
-
error: [],
|
|
636
|
-
};
|
|
558
|
+
const intent = 'validate/email';
|
|
637
559
|
|
|
638
560
|
// This will log 'true'
|
|
639
|
-
console.log(shouldValidate(
|
|
561
|
+
console.log(shouldValidate(intent, 'email'));
|
|
640
562
|
|
|
641
563
|
// This will log 'false'
|
|
642
|
-
console.log(shouldValidate(
|
|
564
|
+
console.log(shouldValidate(intent, 'password'));
|
|
643
565
|
```
|
|
@@ -24,6 +24,7 @@ function _objectSpread2(target) {
|
|
|
24
24
|
return target;
|
|
25
25
|
}
|
|
26
26
|
function _defineProperty(obj, key, value) {
|
|
27
|
+
key = _toPropertyKey(key);
|
|
27
28
|
if (key in obj) {
|
|
28
29
|
Object.defineProperty(obj, key, {
|
|
29
30
|
value: value,
|
|
@@ -36,6 +37,22 @@ function _defineProperty(obj, key, value) {
|
|
|
36
37
|
}
|
|
37
38
|
return obj;
|
|
38
39
|
}
|
|
40
|
+
function _toPrimitive(input, hint) {
|
|
41
|
+
if (typeof input !== "object" || input === null) return input;
|
|
42
|
+
var prim = input[Symbol.toPrimitive];
|
|
43
|
+
if (prim !== undefined) {
|
|
44
|
+
var res = prim.call(input, hint || "default");
|
|
45
|
+
if (typeof res !== "object") return res;
|
|
46
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
47
|
+
}
|
|
48
|
+
return (hint === "string" ? String : Number)(input);
|
|
49
|
+
}
|
|
50
|
+
function _toPropertyKey(arg) {
|
|
51
|
+
var key = _toPrimitive(arg, "string");
|
|
52
|
+
return typeof key === "symbol" ? key : String(key);
|
|
53
|
+
}
|
|
39
54
|
|
|
40
55
|
exports.defineProperty = _defineProperty;
|
|
41
56
|
exports.objectSpread2 = _objectSpread2;
|
|
57
|
+
exports.toPrimitive = _toPrimitive;
|
|
58
|
+
exports.toPropertyKey = _toPropertyKey;
|
package/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FieldConfig, VALIDATION_SKIPPED, VALIDATION_UNDEFINED } from '@conform-to/dom';
|
|
2
2
|
import type { CSSProperties, HTMLInputTypeAttribute } from 'react';
|
|
3
3
|
interface FieldProps {
|
|
4
4
|
id?: string;
|
|
@@ -34,7 +34,7 @@ interface TextareaProps extends FieldProps {
|
|
|
34
34
|
maxLength?: number;
|
|
35
35
|
defaultValue?: string;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
type InputOptions = {
|
|
38
38
|
type: 'checkbox' | 'radio';
|
|
39
39
|
hidden?: boolean;
|
|
40
40
|
value?: string;
|
|
@@ -53,4 +53,5 @@ export declare function select<Schema>(config: FieldConfig<Schema>, options?: {
|
|
|
53
53
|
export declare function textarea<Schema>(config: FieldConfig<Schema>, options?: {
|
|
54
54
|
hidden?: boolean;
|
|
55
55
|
}): TextareaProps;
|
|
56
|
-
export
|
|
56
|
+
export declare const intent = "__intent__";
|
|
57
|
+
export { VALIDATION_UNDEFINED, VALIDATION_SKIPPED };
|
package/helpers.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var dom = require('@conform-to/dom');
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Style to make the input element visually hidden
|
|
7
9
|
* Based on the `sr-only` class from tailwindcss
|
|
@@ -41,7 +43,7 @@ function input(config) {
|
|
|
41
43
|
attributes.tabIndex = -1;
|
|
42
44
|
attributes['aria-hidden'] = true;
|
|
43
45
|
}
|
|
44
|
-
if (config.initialError && config.initialError.length > 0) {
|
|
46
|
+
if (config.initialError && Object.entries(config.initialError).length > 0) {
|
|
45
47
|
attributes.autoFocus = true;
|
|
46
48
|
}
|
|
47
49
|
if (options.type === 'checkbox' || options.type === 'radio') {
|
|
@@ -70,7 +72,7 @@ function select(config, options) {
|
|
|
70
72
|
attributes.tabIndex = -1;
|
|
71
73
|
attributes['aria-hidden'] = true;
|
|
72
74
|
}
|
|
73
|
-
if (config.initialError && config.initialError.length > 0) {
|
|
75
|
+
if (config.initialError && Object.entries(config.initialError).length > 0) {
|
|
74
76
|
attributes.autoFocus = true;
|
|
75
77
|
}
|
|
76
78
|
return attributes;
|
|
@@ -94,12 +96,22 @@ function textarea(config, options) {
|
|
|
94
96
|
attributes.tabIndex = -1;
|
|
95
97
|
attributes['aria-hidden'] = true;
|
|
96
98
|
}
|
|
97
|
-
if (config.initialError && config.initialError.length > 0) {
|
|
99
|
+
if (config.initialError && Object.entries(config.initialError).length > 0) {
|
|
98
100
|
attributes.autoFocus = true;
|
|
99
101
|
}
|
|
100
102
|
return attributes;
|
|
101
103
|
}
|
|
104
|
+
var intent = '__intent__';
|
|
102
105
|
|
|
106
|
+
Object.defineProperty(exports, 'VALIDATION_SKIPPED', {
|
|
107
|
+
enumerable: true,
|
|
108
|
+
get: function () { return dom.VALIDATION_SKIPPED; }
|
|
109
|
+
});
|
|
110
|
+
Object.defineProperty(exports, 'VALIDATION_UNDEFINED', {
|
|
111
|
+
enumerable: true,
|
|
112
|
+
get: function () { return dom.VALIDATION_UNDEFINED; }
|
|
113
|
+
});
|
|
103
114
|
exports.input = input;
|
|
115
|
+
exports.intent = intent;
|
|
104
116
|
exports.select = select;
|
|
105
117
|
exports.textarea = textarea;
|
package/hooks.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type
|
|
2
|
-
import { type
|
|
3
|
-
export interface FormConfig<Schema extends Record<string, any
|
|
1
|
+
import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type FormMethod, type FormEncType, type Submission } from '@conform-to/dom';
|
|
2
|
+
import { type FormEvent, type RefObject } from 'react';
|
|
3
|
+
export interface FormConfig<Schema extends Record<string, any>, ClientSubmission extends Submission | Submission<Schema> = Submission> {
|
|
4
4
|
/**
|
|
5
5
|
* If the form id is provided, Id for label,
|
|
6
6
|
* input and error elements will be derived.
|
|
7
7
|
*/
|
|
8
8
|
id?: string;
|
|
9
|
-
/**
|
|
10
|
-
* Validation mode. Default to `client-only`.
|
|
11
|
-
*/
|
|
12
|
-
mode?: 'client-only' | 'server-validation';
|
|
13
9
|
/**
|
|
14
10
|
* Define when the error should be reported initially.
|
|
15
11
|
* Support "onSubmit", "onChange", "onBlur".
|
|
@@ -24,7 +20,7 @@ export interface FormConfig<Schema extends Record<string, any>> {
|
|
|
24
20
|
/**
|
|
25
21
|
* An object describing the state from the last submission
|
|
26
22
|
*/
|
|
27
|
-
state?: Submission
|
|
23
|
+
state?: Submission;
|
|
28
24
|
/**
|
|
29
25
|
* An object describing the constraint of each field
|
|
30
26
|
*/
|
|
@@ -47,14 +43,17 @@ export interface FormConfig<Schema extends Record<string, any>> {
|
|
|
47
43
|
onValidate?: ({ form, formData, }: {
|
|
48
44
|
form: HTMLFormElement;
|
|
49
45
|
formData: FormData;
|
|
50
|
-
}) =>
|
|
46
|
+
}) => ClientSubmission;
|
|
51
47
|
/**
|
|
52
48
|
* The submit event handler of the form. It will be called
|
|
53
49
|
* only when the form is considered valid.
|
|
54
50
|
*/
|
|
55
51
|
onSubmit?: (event: FormEvent<HTMLFormElement>, context: {
|
|
56
52
|
formData: FormData;
|
|
57
|
-
submission:
|
|
53
|
+
submission: ClientSubmission;
|
|
54
|
+
action: string;
|
|
55
|
+
encType: FormEncType;
|
|
56
|
+
method: FormMethod;
|
|
58
57
|
}) => void;
|
|
59
58
|
}
|
|
60
59
|
/**
|
|
@@ -79,18 +78,19 @@ interface Form<Schema extends Record<string, any>> {
|
|
|
79
78
|
*
|
|
80
79
|
* @see https://conform.guide/api/react#useform
|
|
81
80
|
*/
|
|
82
|
-
export declare function useForm<Schema extends Record<string, any
|
|
81
|
+
export declare function useForm<Schema extends Record<string, any>, ClientSubmission extends Submission | Submission<Schema> = Submission>(config?: FormConfig<Schema, ClientSubmission>): [Form<Schema>, Fieldset<Schema>];
|
|
83
82
|
/**
|
|
84
83
|
* All the information of the field, including state and config.
|
|
85
84
|
*/
|
|
86
|
-
export
|
|
85
|
+
export type Field<Schema> = {
|
|
87
86
|
config: FieldConfig<Schema>;
|
|
88
87
|
error?: string;
|
|
88
|
+
errors?: string[];
|
|
89
89
|
};
|
|
90
90
|
/**
|
|
91
91
|
* A set of field information.
|
|
92
92
|
*/
|
|
93
|
-
export
|
|
93
|
+
export type Fieldset<Schema extends Record<string, any>> = {
|
|
94
94
|
[Key in keyof Schema]-?: Field<Schema[Key]>;
|
|
95
95
|
};
|
|
96
96
|
export interface FieldsetConfig<Schema extends Record<string, any>> {
|
|
@@ -105,7 +105,7 @@ export interface FieldsetConfig<Schema extends Record<string, any>> {
|
|
|
105
105
|
/**
|
|
106
106
|
* An object describing the initial error of each field
|
|
107
107
|
*/
|
|
108
|
-
initialError?:
|
|
108
|
+
initialError?: Record<string, string | string[]>;
|
|
109
109
|
/**
|
|
110
110
|
* An object describing the constraint of each field
|
|
111
111
|
*/
|
|
@@ -131,35 +131,9 @@ export declare function useFieldset<Schema extends Record<string, any>>(ref: Ref
|
|
|
131
131
|
export declare function useFieldList<Payload = any>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldConfig<Array<Payload>>): Array<{
|
|
132
132
|
key: string;
|
|
133
133
|
error: string | undefined;
|
|
134
|
+
errors: string[] | undefined;
|
|
134
135
|
config: FieldConfig<Payload>;
|
|
135
136
|
}>;
|
|
136
|
-
interface ShadowInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
137
|
-
ref: RefObject<HTMLInputElement>;
|
|
138
|
-
}
|
|
139
|
-
interface LegacyInputControl<Element extends {
|
|
140
|
-
focus: () => void;
|
|
141
|
-
}> {
|
|
142
|
-
ref: RefObject<Element>;
|
|
143
|
-
value: string;
|
|
144
|
-
onChange: (eventOrValue: {
|
|
145
|
-
target: {
|
|
146
|
-
value: string;
|
|
147
|
-
};
|
|
148
|
-
} | string) => void;
|
|
149
|
-
onBlur: () => void;
|
|
150
|
-
onInvalid: (event: FormEvent<FieldElement>) => void;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Returns the properties required to configure a shadow input for validation.
|
|
154
|
-
* This is particular useful when integrating dropdown and datepicker whichs
|
|
155
|
-
* introduces custom input mode.
|
|
156
|
-
*
|
|
157
|
-
* @deprecated Please use the `useInputEvent` hook instead
|
|
158
|
-
* @see https://conform.guide/api/react#usecontrolledinput
|
|
159
|
-
*/
|
|
160
|
-
export declare function useControlledInput<Element extends {
|
|
161
|
-
focus: () => void;
|
|
162
|
-
} = HTMLInputElement, Schema extends Primitive = Primitive>(config: FieldConfig<Schema>): [ShadowInputProps, LegacyInputControl<Element>];
|
|
163
137
|
interface InputControl {
|
|
164
138
|
change: (eventOrValue: {
|
|
165
139
|
target: {
|