Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@conform-to/react

Package Overview
Dependencies
Maintainers
1
Versions
66
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@conform-to/react - npm Package Compare versions

Comparing version 0.4.0-pre.2 to 0.4.0-pre.3

49

hooks.d.ts
import { type FieldConfig, type FieldElement, type FieldValue, type FieldsetConstraint, type ListCommand, type Primitive, type Submission } from '@conform-to/dom';
import { type InputHTMLAttributes, type FormEvent, type RefObject } from 'react';
interface FormContext<Schema extends Record<string, any>> {
form: HTMLFormElement;
formData: FormData;
submission: Submission<Schema>;
}
export interface FormConfig<Schema extends Record<string, any>> {

@@ -43,3 +38,6 @@ /**

*/
onValidate?: (context: FormContext<Schema>) => Array<[string, string]>;
onValidate?: ({ form, formData, }: {
form: HTMLFormElement;
formData: FormData;
}) => Submission<Schema>;
/**

@@ -49,3 +47,6 @@ * The submit event handler of the form. It will be called

*/
onSubmit?: (event: FormEvent<HTMLFormElement>, context: FormContext<Schema>) => void;
onSubmit?: (event: FormEvent<HTMLFormElement>, context: {
formData: FormData;
submission: Submission<Schema>;
}) => void;
}

@@ -70,3 +71,3 @@ /**

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#useform
*/

@@ -112,7 +113,7 @@ export declare function useForm<Schema extends Record<string, any>>(config?: FormConfig<Schema>): Form<Schema>;

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldset
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usefieldset
*/
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldsetConfig<Schema>): Fieldset<Schema>;
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config?: FieldConfig<Schema>): Fieldset<Schema>;
interface ControlButtonProps {
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldsetConfig<Schema>): Fieldset<Schema>;
export declare function useFieldset<Schema extends Record<string, any>>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldConfig<Schema>): Fieldset<Schema>;
interface CommandButtonProps {
name?: string;

@@ -123,20 +124,10 @@ value?: string;

}
declare type CommandPayload<Schema, Type extends ListCommand<FieldValue<Schema>>['type']> = Extract<ListCommand<FieldValue<Schema>>, {
declare type ListCommandPayload<Schema, Type extends ListCommand<FieldValue<Schema>>['type']> = Extract<ListCommand<FieldValue<Schema>>, {
type: Type;
}>['payload'];
/**
* A group of helpers for configuring a list control button
*/
interface ListControl<Schema> {
prepend(payload?: CommandPayload<Schema, 'prepend'>): ControlButtonProps;
append(payload?: CommandPayload<Schema, 'append'>): ControlButtonProps;
replace(payload: CommandPayload<Schema, 'replace'>): ControlButtonProps;
remove(payload: CommandPayload<Schema, 'remove'>): ControlButtonProps;
reorder(payload: CommandPayload<Schema, 'reorder'>): ControlButtonProps;
}
/**
* Returns a list of key and config, with a group of helpers
* configuring buttons for list manipulation
*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usefieldlist
*/

@@ -148,3 +139,9 @@ export declare function useFieldList<Payload = any>(ref: RefObject<HTMLFormElement | HTMLFieldSetElement>, config: FieldConfig<Array<Payload>>): [

}>,
ListControl<Payload>
{
prepend(payload?: ListCommandPayload<Payload, 'prepend'>): CommandButtonProps;
append(payload?: ListCommandPayload<Payload, 'append'>): CommandButtonProps;
replace(payload: ListCommandPayload<Payload, 'replace'>): CommandButtonProps;
remove(payload: ListCommandPayload<Payload, 'remove'>): CommandButtonProps;
reorder(payload: ListCommandPayload<Payload, 'reorder'>): CommandButtonProps;
}
];

@@ -172,3 +169,3 @@ interface ShadowInputProps extends InputHTMLAttributes<HTMLInputElement> {

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usecontrolledinput
*/

@@ -175,0 +172,0 @@ export declare function useControlledInput<Element extends {

@@ -14,3 +14,3 @@ 'use strict';

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#useform
*/

@@ -187,41 +187,46 @@ function useForm() {

try {
var submission;
var formData = dom.getFormData(form, submitter);
var submission = dom.parse(formData);
var _context = {
form,
formData,
submission
}; // Touch all fields only if the submitter is not a command button
if (submission.context === 'submit') {
for (var field of form.elements) {
if (dom.isFieldElement(field)) {
// Mark the field as touched
field.dataset.conformTouched = 'true';
}
}
}
var _error;
if (typeof config.onValidate === 'function') {
_error = config.onValidate(_context);
submission = config.onValidate({
form,
formData
});
} else {
submission = dom.parse(formData);
if (config.mode !== 'server-validation') {
// Clear previous result
/**
* As there is no custom logic defined,
* removing the custom validity state will allow us
* finding the latest validation message.
*
* This is mainly used to showcase the constraint validation API.
*/
dom.setFormError(form, {
context: 'submit',
type: 'submit',
value: {},
error: []
});
for (var _element of form.elements) {
if (dom.isFieldElement(_element) && _element.willValidate) {
submission.error.push([_element.name, _element.validationMessage]);
}
}
}
} // Touch all fields only if the submitter is not a command button
_error = dom.getFormError(form);
}
if (_error.length > 0) {
submission.error.push(..._error);
if (submission.type === 'submit') {
for (var field of form.elements) {
if (dom.isFieldElement(field)) {
// Mark the field as touched
field.dataset.conformTouched = 'true';
}
}
}
if (!config.noValidate && !submitter.formNoValidate && dom.hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
if (!config.noValidate && !submitter.formNoValidate && dom.hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
event.preventDefault();

@@ -231,3 +236,6 @@ } else {

(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
formData,
submission
});
}

@@ -307,7 +315,7 @@

var invalidHandler = event => {
var _configRef$current$na, _configRef$current;
var _configRef$current$na;
var form = dom.getFormElement(ref.current);
var field = event.target;
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
var fieldsetName = (_configRef$current$na = configRef.current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';

@@ -356,6 +364,6 @@ if (!form || !dom.isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {

setError(prev => {
var _configRef$current$na2, _configRef$current2;
var _configRef$current$na2;
var next = prev;
var fieldsetName = (_configRef$current$na2 = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
var fieldsetName = (_configRef$current$na2 = configRef.current.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';

@@ -445,3 +453,3 @@ for (var field of form.elements) {

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usefieldlist
*/

@@ -502,3 +510,3 @@ function useFieldList(ref, config) {

var control = new Proxy({}, {
var command = new Proxy({}, {
get(_target, type) {

@@ -583,3 +591,4 @@ return function () {

}, [ref]);
return [list, control];
return [list, // @ts-expect-error proxy type
command];
}

@@ -592,3 +601,3 @@

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usecontrolledinput
*/

@@ -595,0 +604,0 @@ function useControlledInput(config) {

@@ -1,3 +0,3 @@

export { type FieldsetConstraint, type Submission, getFormError, hasError, isFieldElement, parse, shouldValidate, } from '@conform-to/dom';
export { type FieldsetConstraint, type Submission, getFormElements, hasError, parse, shouldValidate, } from '@conform-to/dom';
export * from './hooks';
export * as conform from './helpers';

@@ -11,5 +11,5 @@ 'use strict';

Object.defineProperty(exports, 'getFormError', {
Object.defineProperty(exports, 'getFormElements', {
enumerable: true,
get: function () { return dom.getFormError; }
get: function () { return dom.getFormElements; }
});

@@ -20,6 +20,2 @@ Object.defineProperty(exports, 'hasError', {

});
Object.defineProperty(exports, 'isFieldElement', {
enumerable: true,
get: function () { return dom.isFieldElement; }
});
Object.defineProperty(exports, 'parse', {

@@ -26,0 +22,0 @@ enumerable: true,

import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.js';
import { getSubmissionType, setFormError, focusFirstInvalidField, requestSubmit, isFieldElement, getFormData, parse, getFormError, hasError, getPaths, getName, requestValidate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
import { getSubmissionType, setFormError, focusFirstInvalidField, requestSubmit, isFieldElement, getFormData, parse, hasError, getPaths, getName, requestValidate, getFormElement, parseListCommand, updateList } from '@conform-to/dom';
import { useRef, useState, useEffect } from 'react';

@@ -10,3 +10,3 @@ import { input } from './helpers.js';

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#useform
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#useform
*/

@@ -183,41 +183,46 @@ function useForm() {

try {
var submission;
var formData = getFormData(form, submitter);
var submission = parse(formData);
var _context = {
form,
formData,
submission
}; // Touch all fields only if the submitter is not a command button
if (submission.context === 'submit') {
for (var field of form.elements) {
if (isFieldElement(field)) {
// Mark the field as touched
field.dataset.conformTouched = 'true';
}
}
}
var _error;
if (typeof config.onValidate === 'function') {
_error = config.onValidate(_context);
submission = config.onValidate({
form,
formData
});
} else {
submission = parse(formData);
if (config.mode !== 'server-validation') {
// Clear previous result
/**
* As there is no custom logic defined,
* removing the custom validity state will allow us
* finding the latest validation message.
*
* This is mainly used to showcase the constraint validation API.
*/
setFormError(form, {
context: 'submit',
type: 'submit',
value: {},
error: []
});
for (var _element of form.elements) {
if (isFieldElement(_element) && _element.willValidate) {
submission.error.push([_element.name, _element.validationMessage]);
}
}
}
} // Touch all fields only if the submitter is not a command button
_error = getFormError(form);
}
if (_error.length > 0) {
submission.error.push(..._error);
if (submission.type === 'submit') {
for (var field of form.elements) {
if (isFieldElement(field)) {
// Mark the field as touched
field.dataset.conformTouched = 'true';
}
}
}
if (!config.noValidate && !submitter.formNoValidate && hasError(submission.error) || submission.context === 'validate' && config.mode !== 'server-validation') {
if (!config.noValidate && !submitter.formNoValidate && hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
event.preventDefault();

@@ -227,3 +232,6 @@ } else {

(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, _context);
(_config$onSubmit = config.onSubmit) === null || _config$onSubmit === void 0 ? void 0 : _config$onSubmit.call(config, event, {
formData,
submission
});
}

@@ -303,7 +311,7 @@

var invalidHandler = event => {
var _configRef$current$na, _configRef$current;
var _configRef$current$na;
var form = getFormElement(ref.current);
var field = event.target;
var fieldsetName = (_configRef$current$na = (_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';
var fieldsetName = (_configRef$current$na = configRef.current.name) !== null && _configRef$current$na !== void 0 ? _configRef$current$na : '';

@@ -352,6 +360,6 @@ if (!form || !isFieldElement(field) || field.form !== form || !field.name.startsWith(fieldsetName)) {

setError(prev => {
var _configRef$current$na2, _configRef$current2;
var _configRef$current$na2;
var next = prev;
var fieldsetName = (_configRef$current$na2 = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';
var fieldsetName = (_configRef$current$na2 = configRef.current.name) !== null && _configRef$current$na2 !== void 0 ? _configRef$current$na2 : '';

@@ -441,3 +449,3 @@ for (var field of form.elements) {

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usefieldlist
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usefieldlist
*/

@@ -498,3 +506,3 @@ function useFieldList(ref, config) {

var control = new Proxy({}, {
var command = new Proxy({}, {
get(_target, type) {

@@ -579,3 +587,4 @@ return function () {

}, [ref]);
return [list, control];
return [list, // @ts-expect-error proxy type
command];
}

@@ -588,3 +597,3 @@

*
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.2/packages/conform-react/README.md#usecontrolledinput
* @see https://github.com/edmundhung/conform/tree/v0.4.0-pre.3/packages/conform-react/README.md#usecontrolledinput
*/

@@ -591,0 +600,0 @@ function useControlledInput(config) {

@@ -1,4 +0,4 @@

export { getFormError, hasError, isFieldElement, parse, shouldValidate } from '@conform-to/dom';
export { getFormElements, hasError, parse, shouldValidate } from '@conform-to/dom';
export { useControlledInput, useFieldList, useFieldset, useForm } from './hooks.js';
import * as helpers from './helpers.js';
export { helpers as conform };

@@ -5,3 +5,3 @@ {

"license": "MIT",
"version": "0.4.0-pre.2",
"version": "0.4.0-pre.3",
"main": "index.js",

@@ -23,3 +23,3 @@ "module": "module/index.js",

"dependencies": {
"@conform-to/dom": "0.4.0-pre.2"
"@conform-to/dom": "0.4.0-pre.3"
},

@@ -26,0 +26,0 @@ "peerDependencies": {

@@ -14,6 +14,5 @@ # @conform-to/react

- [conform](#conform)
- [getFormElements](#getformelements)
- [hasError](#haserror)
- [isFieldElement](#isfieldelement)
- [parse](#parse)
- [setFormError](#setformerror)
- [shouldValidate](#shouldvalidate)

@@ -300,29 +299,2 @@

<details>
<summary>Is it required to provide the FieldsetConfig to `useFieldset`?</summary>
No. The only thing required is the ref object. All the config is optional. You can always pass them to each fields manually.
```tsx
import { useForm, useFieldset } from '@conform-to/react';
function SubscriptionForm() {
const formProps = useForm();
const { email } = useFieldset(formProps.ref);
return (
<form {...formProps}>
<input
type="email"
name={email.config.name}
defaultValue="support@conform.dev"
required
/>
</form>
);
}
```
</details>
<details>
<summary>Why does `useFieldset` require a ref object of the form or fieldset?</summary>

@@ -357,3 +329,3 @@

It returns a list of key and config, with a group of helpers configuring buttons for list manipulation
It returns a list of key and config, with helpers to configure command buttons with [list command](/docs/submission.md#list-command).

@@ -379,3 +351,3 @@ ```tsx

const { books } = useFieldset<Collection>(ref);
const [bookList, control] = useFieldList(ref, books.config);
const [bookList, command] = useFieldList(ref, books.config);

@@ -397,3 +369,3 @@ return (

{/* To setup a delete button */}
<button {...control.remove({ index })}>Delete</button>
<button {...command.remove({ index })}>Delete</button>
</div>

@@ -403,3 +375,3 @@ ))}

{/* To setup a button that can append a new row with optional default value */}
<button {...control.append({ defaultValue: { name: '', isbn: '' } })}>
<button {...command.append({ defaultValue: { name: '', isbn: '' } })}>
add

@@ -421,3 +393,3 @@ </button>

const { books } = useFieldset<Collection>(ref);
const [bookList, control] = useFieldList(ref, books.config);
const [bookList, command] = useFieldList(ref, books.config);

@@ -432,3 +404,3 @@ return (

{/* To setup a delete button */}
<button {...control.remove({ index })}>Delete</button>
<button {...command.remove({ index })}>Delete</button>
</div>

@@ -438,3 +410,3 @@ ))}

{/* To setup a button that can append a new row */}
<button {...control.append()}>add</button>
<button {...command.append()}>add</button>
</fieldset>

@@ -594,70 +566,39 @@ );

### hasError
This helper checks if there is any message defined in error array with the provided name.
```ts
import { hasError } from '@conform-to/react';
/**
* Assume the error looks like this:
*/
const error = [['email', 'Email is required']];
// This will log `true`
console.log(hasError(error, 'email'));
// This will log `false`
console.log(hasError(error, 'password'));
```
---
### isFieldElement
### getFormElements
This checks if the provided element is an `input` / `select` / `textarea` or `button` HTML element with type guard. Useful when you need to access the validityState of the fields and modify the validation message manually.
It returns all _input_ / _select_ / _textarea_ or _button_ in the forms. Useful when looping through the form elements to validate each field.
```tsx
import { isFieldElement } from '@conform-to/react';
import { useForm, parse, getFormElements } from '@conform-to/react';
export default function SignupForm() {
export default function LoginForm() {
const form = useForm({
onValidate({ form }) {
for (const element of form.elements) {
if (isFieldElement(element)) {
switch (field.name) {
case 'email':
if (field.validity.valueMissing) {
field.setCustomValidity('Email is required');
} else if (field.validity.typeMismatch) {
field.setCustomValidity('Please enter a valid email');
} else {
field.setCustomValidity('');
}
break;
case 'password':
if (field.validity.valueMissing) {
field.setCustomValidity('Password is required');
} else if (field.validity.tooShort) {
field.setCustomValidity(
'The password should be at least 10 characters long',
);
} else {
field.setCustomValidity('');
}
break;
case 'confirm-password': {
if (field.validity.valueMissing) {
field.setCustomValidity('Confirm Password is required');
} else if (field.value !== formData.get('password')) {
field.setCustomValidity('The password does not match');
} else {
field.setCustomValidity('');
}
break;
onValidate({ form, formData }) {
const submission = parse(formData);
for (const element of getFormElements(form)) {
switch (element.name) {
case 'email': {
if (element.validity.valueMissing) {
submission.error.push([element.name, 'Email is required']);
} else if (element.validity.typeMismatch) {
submission.error.push([element.name, 'Email is invalid']);
}
break;
}
case 'password': {
if (element.validity.valueMissing) {
submission.error.push([element.name, 'Password is required']);
}
break;
}
}
}
return submission;
},
// ....
});

@@ -671,13 +612,19 @@

### parse
### hasError
It parses the formData based on the [naming convention](/docs/submission).
This helper checks if there is any message defined in error array with the provided name.
```tsx
import { parse } from '@conform-to/react';
```ts
import { hasError } from '@conform-to/react';
const formData = new FormData(formElement);
const submission = parse(formData);
/**
* Assume the error looks like this:
*/
const error = [['email', 'Email is required']];
console.log(submission);
// This will log `true`
console.log(hasError(error, 'email'));
// This will log `false`
console.log(hasError(error, 'password'));
```

@@ -687,23 +634,13 @@

### setFormError
### parse
This helpers updates the form error based on the submission result by looping through all elements in the form and update the error with the [setCustomValidity](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setCustomValidity) API.
It parses the formData based on the [naming convention](/docs/submission).
```tsx
import { setFormError } from '@conform-to/react';
import { parse } from '@conform-to/react';
function ExampleForm() {
const form = useForm({
onValidate({ form, submission }) {
const error = validate(submission);
const formData = new FormData();
const submission = parse(formData);
setFormError(form, {
...submission,
error,
});
},
});
// ...
}
console.log(submission);
```

@@ -721,10 +658,9 @@

/**
* The submission type and metadata give us hint on what should be valdiated.
* The submission type and intent give us hint on what should be valdiated.
* If the type is 'validate', only the field with name matching the metadata must be validated.
*
* However, if the type is `undefined`, both will log true (Default submission)
* If the type is 'submit', everything should be validated (Default submission)
*/
const submission = {
type: 'validate',
metadata: 'email',
context: 'validate',
intent: 'email',
value: {},

@@ -731,0 +667,0 @@ error: [],

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc