@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 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
- - [requestCommand](#requestcommand)
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
- ### requestCommand
450
+ ### requestIntent
503
451
 
504
- It lets you [trigger a command](/docs/commands.md#triggering-a-command) without requiring users to click on a button. It supports both [list](#list) and [validate](#validate) command.
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
- requestCommand,
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
- requestCommand(form.ref.current, list.reorder({ from, to }));
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 type and intent give us hint on what should be valdiated.
628
- * If the type is 'validate', only the field with name matching the metadata must be validated.
629
- * If the type is 'submit', everything should be validated (Default submission)
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 submission = {
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(submission, 'email'));
561
+ console.log(shouldValidate(intent, 'email'));
640
562
 
641
563
  // This will log 'false'
642
- console.log(shouldValidate(submission, 'password'));
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 { FieldConfig } from '@conform-to/dom';
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
- declare type InputOptions = {
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 Primitive, type Submission } from '@conform-to/dom';
2
- import { type InputHTMLAttributes, type FormEvent, type RefObject } from 'react';
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<Schema>;
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
- }) => Submission<Schema>;
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: Submission<Schema>;
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>>(config?: FormConfig<Schema>): [Form<Schema>, Fieldset<Schema>];
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 declare type Field<Schema> = {
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 declare type Fieldset<Schema extends Record<string, any>> = {
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?: Array<[string, string]>;
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: {