Socket
Socket
Sign inDemoInstall

@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 1.0.0-pre.0 to 1.0.0-pre.1

validitystate.d.ts

54

context.d.ts

@@ -1,2 +0,2 @@

import { type Constraint, type FieldName, type Form, type FormValue, type FormContext, type SubscriptionSubject } from '@conform-to/dom';
import { type Constraint, type FormId, type FieldName, type Form, type FormValue, type FormState, type SubscriptionScope, type SubscriptionSubject, type UnionKeyof, type UnionKeyType } from '@conform-to/dom';
import { type ReactElement, type ReactNode, type MutableRefObject } from 'react';

@@ -6,3 +6,11 @@ export type Pretty<T> = {

} & {};
export type BaseMetadata<Schema> = {
export type Primitive = string | number | boolean | Date | File | null | undefined;
export type FieldProps<FieldSchema, Error = unknown, FormSchema extends Record<string, unknown> = Record<string, unknown>> = {
formId: FormId<FormSchema, Error>;
name: FieldName<FieldSchema>;
} | {
formId: FieldSchema extends Record<string, unknown> ? FormId<FieldSchema, Error> : never;
name?: undefined;
};
export type Metadata<Schema, Error> = {
key?: string;

@@ -14,4 +22,4 @@ id: string;

value: FormValue<Schema>;
errors: string[] | undefined;
allErrors: Record<string, string[]>;
error: Error | undefined;
allError: Record<string, Error>;
allValid: boolean;

@@ -21,7 +29,8 @@ valid: boolean;

};
export type Field<Schema> = {
name: FieldName<Schema>;
formId: string;
};
export type FormMetadata<Schema extends Record<string, any>> = BaseMetadata<Schema> & {
export type FormMetadata<Schema extends Record<string, unknown> = Record<string, unknown>, Error = unknown> = Omit<Metadata<Schema, Error>, 'id'> & {
id: FormId<Schema, Error>;
context: Form<Schema, Error>;
getFieldset: () => {
[Key in UnionKeyof<Schema>]: FieldMetadata<UnionKeyType<Schema, Key>, Error, Schema>;
};
onSubmit: (event: React.FormEvent<HTMLFormElement>) => ReturnType<Form<Schema>['submit']>;

@@ -31,12 +40,16 @@ onReset: (event: React.FormEvent<HTMLFormElement>) => void;

};
export type FieldMetadata<Schema> = BaseMetadata<Schema> & {
formId: string;
export type FieldMetadata<Schema = unknown, Error = unknown, FormSchema extends Record<string, any> = Record<string, unknown>> = Metadata<Schema, Error> & {
formId: FormId<FormSchema, Error>;
name: FieldName<Schema>;
constraint?: Constraint;
getFieldset: unknown extends Schema ? () => unknown : Schema extends Primitive | Array<any> ? never : () => {
[Key in UnionKeyof<Schema>]: FieldMetadata<UnionKeyType<Schema, Key>, Error>;
};
getFieldList: unknown extends Schema ? () => unknown : Schema extends Array<infer Item> ? () => Array<FieldMetadata<Item, Error>> : never;
};
export declare const Registry: import("react").Context<Record<string, Form>>;
export declare function useFormStore(formId: string, context?: Form): Form;
export declare function useFormContext(form: Form, subjectRef?: MutableRefObject<SubscriptionSubject>): FormContext;
export declare function useRegistry<Schema extends Record<string, any>, Error, Value = Schema>(formId: FormId<Schema, Error>, context?: Form<Schema, Error, Value>): Form<Schema, Error, Value>;
export declare function useFormState<Error>(form: Form<any, Error>, subjectRef?: MutableRefObject<SubscriptionSubject>): FormState<Error>;
export declare function FormProvider(props: {
context: Form;
context: Form<any, any, any>;
children: ReactNode;

@@ -52,10 +65,5 @@ }): ReactElement;

export declare function useSubjectRef(initialSubject?: SubscriptionSubject): MutableRefObject<SubscriptionSubject>;
export declare function getBaseMetadata<Schema>(formId: string, context: FormContext, options: {
name?: string;
subjectRef: MutableRefObject<SubscriptionSubject>;
}): BaseMetadata<Schema>;
export declare function getFieldMetadata<Schema>(formId: string, context: FormContext, options: {
name?: string;
key?: string | number;
subjectRef: MutableRefObject<SubscriptionSubject>;
}): FieldMetadata<Schema>;
export declare function updateSubjectRef(ref: MutableRefObject<SubscriptionSubject>, name: string, subject: keyof SubscriptionSubject, scope: keyof SubscriptionScope): void;
export declare function getMetadata<Schema, Error, FormSchema extends Record<string, any>>(formId: FormId<FormSchema, Error>, state: FormState<Error>, subjectRef: MutableRefObject<SubscriptionSubject>, name?: FieldName<Schema>): Metadata<Schema, Error>;
export declare function getFieldMetadata<Schema, Error, FormSchema extends Record<string, any>>(formId: FormId<FormSchema, Error>, state: FormState<Error>, subjectRef: MutableRefObject<SubscriptionSubject>, prefix?: string, key?: string | number): FieldMetadata<Schema, Error, FormSchema>;
export declare function getFormMetadata<Schema extends Record<string, any>, Error>(formId: FormId<Schema, Error>, state: FormState<Error>, subjectRef: MutableRefObject<SubscriptionSubject>, form: Form<Schema, Error, any>, noValidate: boolean): FormMetadata<Schema, Error>;

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

var Registry = /*#__PURE__*/react.createContext({});
function useFormStore(formId, context) {
function useRegistry(formId, context) {
var registry = react.useContext(Registry);

@@ -20,6 +20,5 @@ var form = context !== null && context !== void 0 ? context : registry[formId];

}
function useFormContext(form, subjectRef) {
function useFormState(form, subjectRef) {
var subscribe = react.useCallback(callback => form.subscribe(callback, () => subjectRef === null || subjectRef === void 0 ? void 0 : subjectRef.current), [form, subjectRef]);
var result = react.useSyncExternalStore(subscribe, form.getContext, form.getContext);
return result;
return react.useSyncExternalStore(subscribe, form.getState, form.getState);
}

@@ -38,3 +37,3 @@ function FormProvider(props) {

var _props$formId;
var form = useFormStore((_props$formId = props.formId) !== null && _props$formId !== void 0 ? _props$formId : props.context.id, props.context);
var form = useRegistry((_props$formId = props.formId) !== null && _props$formId !== void 0 ? _props$formId : props.context.id, props.context);
return /*#__PURE__*/jsxRuntime.jsx("input", {

@@ -56,12 +55,11 @@ type: "hidden",

}
function getBaseMetadata(formId, context, options) {
var _options$name;
var name = (_options$name = options.name) !== null && _options$name !== void 0 ? _options$name : '';
function updateSubjectRef(ref, name, subject, scope) {
var _ref$current$subject$, _ref$current$subject;
ref.current[subject] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, ref.current[subject]), {}, {
[scope]: ((_ref$current$subject$ = (_ref$current$subject = ref.current[subject]) === null || _ref$current$subject === void 0 ? void 0 : _ref$current$subject[scope]) !== null && _ref$current$subject$ !== void 0 ? _ref$current$subject$ : []).concat(name)
});
}
function getMetadata(formId, state, subjectRef) {
var name = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
var id = name ? "".concat(formId, "-").concat(name) : formId;
var updateSubject = (subject, scope) => {
var _options$subjectRef$c, _options$subjectRef$c2;
options.subjectRef.current[subject] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options.subjectRef.current[subject]), {}, {
[scope]: ((_options$subjectRef$c = (_options$subjectRef$c2 = options.subjectRef.current[subject]) === null || _options$subjectRef$c2 === void 0 ? void 0 : _options$subjectRef$c2[scope]) !== null && _options$subjectRef$c !== void 0 ? _options$subjectRef$c : []).concat(name)
});
};
return new Proxy({

@@ -71,21 +69,21 @@ id,

descriptionId: "".concat(id, "-description"),
initialValue: context.initialValue[name],
value: context.value[name],
errors: context.error[name],
initialValue: state.initialValue[name],
value: state.value[name],
error: state.error[name],
get key() {
return context.state.key[name];
return state.key[name];
},
get valid() {
return context.state.valid[name];
return state.valid[name];
},
get dirty() {
return context.state.dirty[name];
return state.dirty[name];
},
get allValid() {
var keys = Object.keys(context.error);
var keys = Object.keys(state.error);
if (name === '') {
return keys.length === 0;
}
for (var key of Object.keys(context.error)) {
if (dom.isPrefix(key, name) && !context.state.valid[key]) {
for (var key of Object.keys(state.error)) {
if (dom.isPrefix(key, name) && !state.valid[key]) {
return false;

@@ -96,13 +94,23 @@ }

},
get allErrors() {
get allError() {
if (name === '') {
return context.error;
return state.error;
}
var result = {};
for (var [key, errors] of Object.entries(context.error)) {
for (var [key, error] of Object.entries(state.error)) {
if (dom.isPrefix(key, name)) {
result[key] = errors;
result[key] = error;
}
}
return result;
},
get getFieldset() {
return () => new Proxy({}, {
get(target, key, receiver) {
if (typeof key === 'string') {
return getFieldMetadata(formId, state, subjectRef, name, key);
}
return Reflect.get(target, key, receiver);
}
});
}

@@ -113,3 +121,3 @@ }, {

case 'key':
case 'errors':
case 'error':
case 'initialValue':

@@ -119,9 +127,9 @@ case 'value':

case 'dirty':
updateSubject(key === 'errors' ? 'error' : key, 'name');
updateSubjectRef(subjectRef, name, key, 'name');
break;
case 'allErrors':
updateSubject('error', 'prefix');
case 'allError':
updateSubjectRef(subjectRef, name, 'error', 'prefix');
break;
case 'allValid':
updateSubject('valid', 'prefix');
updateSubjectRef(subjectRef, name, 'valid', 'prefix');
break;

@@ -133,9 +141,7 @@ }

}
function getFieldMetadata(formId, context, options) {
var _options$name2, _options$name3;
var name = typeof options.key !== 'undefined' ? dom.formatPaths([...dom.getPaths((_options$name2 = options.name) !== null && _options$name2 !== void 0 ? _options$name2 : ''), options.key]) : (_options$name3 = options.name) !== null && _options$name3 !== void 0 ? _options$name3 : '';
var metadata = getBaseMetadata(formId, context, {
subjectRef: options.subjectRef,
name
});
function getFieldMetadata(formId, state, subjectRef) {
var prefix = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
var key = arguments.length > 4 ? arguments[4] : undefined;
var name = typeof key === 'undefined' ? prefix : dom.formatPaths([...dom.getPaths(prefix), key]);
var metadata = getMetadata(formId, state, subjectRef, name);
return new Proxy(metadata, {

@@ -149,3 +155,15 @@ get(target, key, receiver) {

case 'constraint':
return context.constraint[name];
return state.constraint[name];
case 'getFieldList':
{
return () => {
var _state$initialValue$n;
var initialValue = (_state$initialValue$n = state.initialValue[name]) !== null && _state$initialValue$n !== void 0 ? _state$initialValue$n : [];
updateSubjectRef(subjectRef, name, 'initialValue', 'name');
if (!Array.isArray(initialValue)) {
throw new Error('The initial value at the given name is not a list');
}
return Array(initialValue.length).fill(0).map((_, index) => getFieldMetadata(formId, state, subjectRef, name, index));
};
}
}

@@ -156,2 +174,24 @@ return Reflect.get(target, key, receiver);

}
function getFormMetadata(formId, state, subjectRef, form, noValidate) {
var metadata = getMetadata(formId, state, subjectRef);
return new Proxy(metadata, {
get(target, key, receiver) {
switch (key) {
case 'context':
return form;
case 'onSubmit':
return event => {
var submitEvent = event.nativeEvent;
form.submit(submitEvent);
if (submitEvent.defaultPrevented) {
event.preventDefault();
}
};
case 'noValidate':
return noValidate;
}
return Reflect.get(target, key, receiver);
}
});
}

@@ -161,6 +201,8 @@ exports.FormProvider = FormProvider;

exports.Registry = Registry;
exports.getBaseMetadata = getBaseMetadata;
exports.getFieldMetadata = getFieldMetadata;
exports.useFormContext = useFormContext;
exports.useFormStore = useFormStore;
exports.getFormMetadata = getFormMetadata;
exports.getMetadata = getMetadata;
exports.updateSubjectRef = updateSubjectRef;
exports.useFormState = useFormState;
exports.useRegistry = useRegistry;
exports.useSubjectRef = useSubjectRef;

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

import type { CSSProperties, HTMLInputTypeAttribute } from 'react';
import type { FormMetadata, FieldMetadata, Pretty } from './context';
/// <reference types="react" />
import { type Intent } from '@conform-to/dom';
import type { FormMetadata, FieldMetadata, Metadata, Pretty, Primitive } from './context';
type FormControlProps = {

@@ -10,9 +11,24 @@ id: string;

tabIndex?: number;
style?: CSSProperties;
'aria-describedby'?: string;
'aria-invalid'?: boolean;
'aria-hidden'?: boolean;
};
type FormControlOptions = {
/**
* Decide whether to include `aria-invalid` and `aria-describedby` in the result.
*/
ariaAttributes?: true;
/**
* Decide whether the aria attributes should be based on `field.valid` or `field.allValid`.
* @default 'field'
*/
ariaInvalid?: 'all' | 'field';
/**
* Assign additional `id` to the `aria-describedby` attribute. If `true`, it will use the `descriptionId` from the metadata.
*/
ariaDescribedBy?: boolean | string;
} | {
ariaAttributes: false;
};
type InputProps = Pretty<FormControlProps & {
type?: Exclude<HTMLInputTypeAttribute, 'submit' | 'reset' | 'button'>;
type?: 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'image' | 'month' | 'number' | 'password' | 'radio' | 'range' | 'search' | 'tel' | 'text' | 'time' | 'url' | 'week';
minLength?: number;

@@ -29,2 +45,16 @@ maxLength?: number;

}>;
type InputOptions = Pretty<FormControlOptions & ({
type: 'checkbox' | 'radio';
/**
* The value of the checkbox or radio button. Pass `false` if you want to mange the checked state yourself.
* @default 'on'
*/
value?: string | boolean;
} | {
type?: Exclude<InputProps['type'], 'checkbox' | 'radio'>;
/**
* Decide whether defaultValue should be returned. Pass `false` if you want to mange the value yourself.
*/
value?: boolean;
})>;
type SelectProps = Pretty<FormControlProps & {

@@ -34,2 +64,8 @@ defaultValue?: string | number | readonly string[] | undefined;

}>;
type SelectOptions = Pretty<FormControlOptions & {
/**
* Decide whether defaultValue should be returned. Pass `false` if you want to mange the value yourself.
*/
value?: boolean;
}>;
type TextareaProps = Pretty<FormControlProps & {

@@ -40,49 +76,157 @@ minLength?: number;

}>;
type Primitive = string | number | boolean | Date | null | undefined;
type BaseOptions = {
ariaAttributes?: true;
description?: boolean;
} | {
ariaAttributes: false;
type TextareaOptions = Pretty<FormControlOptions & {
/**
* Decide whether defaultValue should be returned. Pass `false` if you want to mange the value yourself.
*/
value?: true;
}>;
/**
* Derives aria attributes of a form control based on the field metadata.
*/
export declare function getAriaAttributes<Schema, Error>(metadata: Metadata<Schema, Error>, options?: FormControlOptions): {
'aria-invalid'?: boolean;
'aria-describedby'?: string;
};
type ControlOptions = BaseOptions & {
hidden?: boolean;
/**
* Derives the properties of a form element based on the form metadata,
* including `id`, `onSubmit`, `noValidate`, `aria-invalid` and `aria-describedby`.
*
* @example
* ```tsx
* <form {...getFormProps(metadata)} />
* ```
*/
export declare function getFormProps<Schema extends Record<string, any>, Error>(metadata: FormMetadata<Schema, Error>, options?: FormControlOptions): {
'aria-invalid'?: boolean | undefined;
'aria-describedby'?: string | undefined;
id: import("@conform-to/dom").FormId<Schema, Error>;
onSubmit: (event: import("react").FormEvent<HTMLFormElement>) => void;
noValidate: boolean;
};
type FormOptions<Schema extends Record<string, any>> = BaseOptions & {
onSubmit?: (event: React.FormEvent<HTMLFormElement>, context: ReturnType<FormMetadata<Schema>['onSubmit']>) => void;
onReset?: (event: React.FormEvent<HTMLFormElement>) => void;
};
type InputOptions = ControlOptions & ({
type: 'checkbox' | 'radio';
value?: string;
} | {
type?: Exclude<HTMLInputTypeAttribute, 'button' | 'submit' | 'hidden'>;
value?: never;
});
export declare const hiddenProps: {
style: CSSProperties;
tabIndex: number;
'aria-hidden': boolean;
};
export declare function input<Schema extends Primitive | unknown>(field: FieldMetadata<Schema>, options?: InputOptions): InputProps;
export declare function input<Schema extends File | File[]>(field: FieldMetadata<Schema>, options: InputOptions & {
type: 'file';
}): InputProps;
export declare function select<Schema extends Primitive | Primitive[] | undefined | unknown>(metadata: FieldMetadata<Schema>, options?: ControlOptions): SelectProps;
export declare function textarea<Schema extends Primitive | undefined | unknown>(metadata: FieldMetadata<Schema>, options?: ControlOptions): TextareaProps;
export declare function form<Schema extends Record<string, any>>(metadata: FormMetadata<Schema>, options?: FormOptions<Schema>): {
/**
* Derives the properties of a fieldset element based on the field metadata,
* including `id`, `name`, `form`, `aria-invalid` and `aria-describedby`.
*
* @example
* ```tsx
* <fieldset {...getFieldsetProps(metadata)} />
* ```
*/
export declare function getFieldsetProps<Schema extends Record<string, any> | undefined | unknown, Error>(metadata: FieldMetadata<Schema, Error, any>, options?: FormControlOptions): {
'aria-invalid'?: boolean | undefined;
'aria-describedby'?: string | undefined;
id: string;
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
onReset: (event: import("react").FormEvent<HTMLFormElement>) => void;
noValidate: boolean;
name: import("@conform-to/dom").FieldName<Schema>;
form: import("@conform-to/dom").FormId<any, Error>;
};
export declare function fieldset<Schema extends Record<string, any> | undefined | unknown>(metadata: FieldMetadata<Schema>, options?: BaseOptions): {
/**
* Derives common properties of a form control based on the field metadata,
* including `key`, `id`, `name`, `form`, `required`, `autoFocus`, `aria-invalid` and `aria-describedby`.
*/
export declare function getFormControlProps<Schema, Error>(metadata: FieldMetadata<Schema, Error, any>, options?: FormControlOptions): {
'aria-invalid'?: boolean | undefined;
'aria-describedby'?: string | undefined;
id: string;
name: import("@conform-to/dom").FieldName<Schema>;
form: string;
form: import("@conform-to/dom").FormId<any, Error>;
key: string | undefined;
required: true | undefined;
autoFocus: true | undefined;
};
export declare function collection<Schema extends Array<string | boolean> | string | boolean | undefined | unknown>(metadata: FieldMetadata<Schema>, options: BaseOptions & {
/**
* Derives the properties of an input element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `type`, `required`, `minLength`, `maxLength`, `min`, `max`, `step`, `pattern`, `multiple`.
* Depends on the provided options, it will also set `defaultChecked` / `checked` if it is a checkbox or radio button,
* or otherwise `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-input-props
* @example
* ```tsx
* // To setup an uncontrolled input
* <input {...getInputProps(metadata)} />
* // To setup an uncontrolled checkbox
* <input {...getInputProps(metadata, { type: 'checkbox' })} />
* // To setup an input without defaultValue
* <input {...getInputProps(metadata, { value: false })} />
* // To setup a radio button without defaultChecked state
* <input {...getInputProps(metadata, { type: 'radio', value: false })} />
* ```
*/
export declare function getInputProps<Schema extends Exclude<Primitive, File>>(metadata: FieldMetadata<Schema, any, any>, options?: InputOptions): InputProps;
export declare function getInputProps<Schema extends File | File[]>(metadata: FieldMetadata<Schema, any, any>, options: InputOptions & {
type: 'file';
}): InputProps;
/**
* Derives the properties of a select element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `required` or `multiple`.
* Depends on the provided options, it will also set `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-select-props
* @example
* ```tsx
* // To setup an uncontrolled select
* <select {...getSelectProps(metadata)} />
* // To setup a select without defaultValue
* <select {...getSelectProps(metadata, { value: false })} />
* ```
*/
export declare function getSelectProps<Schema extends Exclude<Primitive, File> | Array<Exclude<Primitive, File>> | undefined>(metadata: FieldMetadata<Schema, any, any>, options?: SelectOptions): SelectProps;
/**
* Derives the properties of a textarea element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `required`, `minLength` or `maxLength`.
* Depends on the provided options, it will also set `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-textarea-props
* @example
* ```tsx
* // To setup an uncontrolled textarea
* <textarea {...getTextareaProps(metadata)} />
* // To setup a textarea without defaultValue
* <textarea {...getTextareaProps(metadata, { value: false })} />
* ```
*/
export declare function getTextareaProps<Schema extends Exclude<Primitive, File> | undefined>(metadata: FieldMetadata<Schema, any, any>, options?: TextareaOptions): TextareaProps;
/**
* Derives the properties of a collection of checkboxes or radio buttons based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby` and `required`.
*
* @see https://conform.guide/api/react/get-textarea-props
* @example
* ```tsx
* <fieldset>
* {getCollectionProps(metadata, {
* type: 'checkbox',
* options: ['a', 'b', 'c']
* }).map((props) => (
* <div key={props.key}>
* <label htmlFor={props.id}>{props.value}</label>
* <input {...props} />
* </div>
* ))}
* </fieldset>
* ```
*/
export declare function getCollectionProps<Schema extends Array<string | boolean> | string | boolean | undefined | unknown>(metadata: FieldMetadata<Schema, any, any>, options: Pretty<FormControlOptions & {
/**
* The input type. Use `checkbox` for multiple selection or `radio` for single selection.
*/
type: 'checkbox' | 'radio';
/**
* The `value` assigned to each input element.
*/
options: string[];
}): Array<InputProps & Pick<Required<InputProps>, 'type' | 'value'>>;
/**
* Decide whether defaultValue should be returned. Pass `false` if you want to mange the value yourself.
*/
value?: boolean;
}>): Array<InputProps & Pick<Required<InputProps>, 'type' | 'value'>>;
export declare function getControlButtonProps(formId: string, intents: Array<Intent>): {
name: string;
value: string;
form: string;
formNoValidate: boolean;
};
export {};

@@ -6,8 +6,9 @@ 'use strict';

var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
var dom = require('@conform-to/dom');
/**
* Cleanup `undefined` from the dervied props
* Cleanup `undefined` from the result.
* To minimize conflicts when merging with user defined props
*/
function cleanup(props) {
function simplify(props) {
for (var key in props) {

@@ -20,2 +21,6 @@ if (props[key] === undefined) {

}
/**
* Derives aria attributes of a form control based on the field metadata.
*/
function getAriaAttributes(metadata) {

@@ -26,121 +31,210 @@ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

}
return cleanup({
'aria-invalid': !metadata.valid || undefined,
'aria-describedby': metadata.valid ? options.description ? metadata.descriptionId : undefined : "".concat(metadata.errorId, " ").concat(options.description ? metadata.descriptionId : '').trim()
var invalid = options.ariaInvalid === 'all' ? !metadata.allValid : !metadata.valid;
var ariaDescribedBy = typeof options.ariaDescribedBy === 'string' ? options.ariaDescribedBy : options.ariaDescribedBy ? metadata.descriptionId : undefined;
return simplify({
'aria-invalid': invalid || undefined,
'aria-describedby': invalid ? "".concat(metadata.errorId, " ").concat(ariaDescribedBy !== null && ariaDescribedBy !== void 0 ? ariaDescribedBy : '').trim() : ariaDescribedBy
});
}
/**
* Derives the properties of a form element based on the form metadata,
* including `id`, `onSubmit`, `noValidate`, `aria-invalid` and `aria-describedby`.
*
* @example
* ```tsx
* <form {...getFormProps(metadata)} />
* ```
*/
function getFormProps(metadata, options) {
return simplify(_rollupPluginBabelHelpers.objectSpread2({
id: metadata.id,
onSubmit: metadata.onSubmit,
noValidate: metadata.noValidate
}, getAriaAttributes(metadata, options)));
}
/**
* Derives the properties of a fieldset element based on the field metadata,
* including `id`, `name`, `form`, `aria-invalid` and `aria-describedby`.
*
* @example
* ```tsx
* <fieldset {...getFieldsetProps(metadata)} />
* ```
*/
function getFieldsetProps(metadata, options) {
return simplify(_rollupPluginBabelHelpers.objectSpread2({
id: metadata.id,
name: metadata.name,
form: metadata.formId
}, getAriaAttributes(metadata, options)));
}
/**
* Derives common properties of a form control based on the field metadata,
* including `key`, `id`, `name`, `form`, `required`, `autoFocus`, `aria-invalid` and `aria-describedby`.
*/
function getFormControlProps(metadata, options) {
var _metadata$constraint;
return cleanup(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({
id: metadata.id,
name: metadata.name,
form: metadata.formId,
return simplify(_rollupPluginBabelHelpers.objectSpread2({
key: metadata.key,
required: ((_metadata$constraint = metadata.constraint) === null || _metadata$constraint === void 0 ? void 0 : _metadata$constraint.required) || undefined,
autoFocus: !metadata.valid || undefined
}, options !== null && options !== void 0 && options.hidden ? hiddenProps : undefined), getAriaAttributes(metadata, options)));
}, getFieldsetProps(metadata, options)));
}
var hiddenProps = {
/**
* Style to make the input element visually hidden
* Based on the `sr-only` class from tailwindcss
*/
style: {
position: 'absolute',
width: '1px',
height: '1px',
padding: 0,
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
border: 0
},
tabIndex: -1,
'aria-hidden': true
};
function input(field) {
var _field$constraint, _field$constraint2, _field$constraint3, _field$constraint4, _field$constraint5, _field$constraint6, _field$constraint7;
/**
* Derives the properties of an input element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `type`, `required`, `minLength`, `maxLength`, `min`, `max`, `step`, `pattern`, `multiple`.
* Depends on the provided options, it will also set `defaultChecked` / `checked` if it is a checkbox or radio button,
* or otherwise `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-input-props
* @example
* ```tsx
* // To setup an uncontrolled input
* <input {...getInputProps(metadata)} />
* // To setup an uncontrolled checkbox
* <input {...getInputProps(metadata, { type: 'checkbox' })} />
* // To setup an input without defaultValue
* <input {...getInputProps(metadata, { value: false })} />
* // To setup a radio button without defaultChecked state
* <input {...getInputProps(metadata, { type: 'radio', value: false })} />
* ```
*/
function getInputProps(metadata) {
var _metadata$constraint2, _metadata$constraint3, _metadata$constraint4, _metadata$constraint5, _metadata$constraint6, _metadata$constraint7, _metadata$constraint8;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var props = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(field, options)), {}, {
var props = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
type: options.type,
minLength: (_field$constraint = field.constraint) === null || _field$constraint === void 0 ? void 0 : _field$constraint.minLength,
maxLength: (_field$constraint2 = field.constraint) === null || _field$constraint2 === void 0 ? void 0 : _field$constraint2.maxLength,
min: (_field$constraint3 = field.constraint) === null || _field$constraint3 === void 0 ? void 0 : _field$constraint3.min,
max: (_field$constraint4 = field.constraint) === null || _field$constraint4 === void 0 ? void 0 : _field$constraint4.max,
step: (_field$constraint5 = field.constraint) === null || _field$constraint5 === void 0 ? void 0 : _field$constraint5.step,
pattern: (_field$constraint6 = field.constraint) === null || _field$constraint6 === void 0 ? void 0 : _field$constraint6.pattern,
multiple: (_field$constraint7 = field.constraint) === null || _field$constraint7 === void 0 ? void 0 : _field$constraint7.multiple
minLength: (_metadata$constraint2 = metadata.constraint) === null || _metadata$constraint2 === void 0 ? void 0 : _metadata$constraint2.minLength,
maxLength: (_metadata$constraint3 = metadata.constraint) === null || _metadata$constraint3 === void 0 ? void 0 : _metadata$constraint3.maxLength,
min: (_metadata$constraint4 = metadata.constraint) === null || _metadata$constraint4 === void 0 ? void 0 : _metadata$constraint4.min,
max: (_metadata$constraint5 = metadata.constraint) === null || _metadata$constraint5 === void 0 ? void 0 : _metadata$constraint5.max,
step: (_metadata$constraint6 = metadata.constraint) === null || _metadata$constraint6 === void 0 ? void 0 : _metadata$constraint6.step,
pattern: (_metadata$constraint7 = metadata.constraint) === null || _metadata$constraint7 === void 0 ? void 0 : _metadata$constraint7.pattern,
multiple: (_metadata$constraint8 = metadata.constraint) === null || _metadata$constraint8 === void 0 ? void 0 : _metadata$constraint8.multiple
});
if (options.type === 'checkbox' || options.type === 'radio') {
var _options$value;
props.value = (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on';
props.defaultChecked = typeof field.initialValue === 'boolean' ? field.initialValue : field.initialValue === props.value;
} else if (options.type !== 'file') {
var _field$initialValue;
props.defaultValue = (_field$initialValue = field.initialValue) === null || _field$initialValue === void 0 ? void 0 : _field$initialValue.toString();
if (typeof options.value === 'undefined' || options.value) {
if (options.type === 'checkbox' || options.type === 'radio') {
props.value = typeof options.value === 'string' ? options.value : 'on';
props.defaultChecked = typeof metadata.initialValue === 'boolean' ? metadata.initialValue : metadata.initialValue === props.value;
} else if (typeof metadata.initialValue === 'string') {
props.defaultValue = metadata.initialValue;
}
}
return cleanup(props);
return simplify(props);
}
function select(metadata, options) {
var _metadata$initialValu, _metadata$constraint2;
return cleanup(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
defaultValue: (_metadata$initialValu = metadata.initialValue) === null || _metadata$initialValu === void 0 ? void 0 : _metadata$initialValu.toString(),
multiple: (_metadata$constraint2 = metadata.constraint) === null || _metadata$constraint2 === void 0 ? void 0 : _metadata$constraint2.multiple
}));
/**
* Derives the properties of a select element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `required` or `multiple`.
* Depends on the provided options, it will also set `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-select-props
* @example
* ```tsx
* // To setup an uncontrolled select
* <select {...getSelectProps(metadata)} />
* // To setup a select without defaultValue
* <select {...getSelectProps(metadata, { value: false })} />
* ```
*/
function getSelectProps(metadata) {
var _metadata$constraint9;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var props = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
multiple: (_metadata$constraint9 = metadata.constraint) === null || _metadata$constraint9 === void 0 ? void 0 : _metadata$constraint9.multiple
});
if (typeof options.value === 'undefined' || options.value) {
props.defaultValue = Array.isArray(metadata.initialValue) ? metadata.initialValue.map(item => "".concat(item !== null && item !== void 0 ? item : '')) : metadata.initialValue;
}
return simplify(props);
}
function textarea(metadata, options) {
var _metadata$initialValu2, _metadata$constraint3, _metadata$constraint4;
return cleanup(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
defaultValue: (_metadata$initialValu2 = metadata.initialValue) === null || _metadata$initialValu2 === void 0 ? void 0 : _metadata$initialValu2.toString(),
minLength: (_metadata$constraint3 = metadata.constraint) === null || _metadata$constraint3 === void 0 ? void 0 : _metadata$constraint3.minLength,
maxLength: (_metadata$constraint4 = metadata.constraint) === null || _metadata$constraint4 === void 0 ? void 0 : _metadata$constraint4.maxLength
}));
/**
* Derives the properties of a textarea element based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
* and constraint attributes such as `required`, `minLength` or `maxLength`.
* Depends on the provided options, it will also set `defaultValue` / `value`.
*
* @see https://conform.guide/api/react/get-textarea-props
* @example
* ```tsx
* // To setup an uncontrolled textarea
* <textarea {...getTextareaProps(metadata)} />
* // To setup a textarea without defaultValue
* <textarea {...getTextareaProps(metadata, { value: false })} />
* ```
*/
function getTextareaProps(metadata) {
var _metadata$constraint10, _metadata$constraint11;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var props = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
minLength: (_metadata$constraint10 = metadata.constraint) === null || _metadata$constraint10 === void 0 ? void 0 : _metadata$constraint10.minLength,
maxLength: (_metadata$constraint11 = metadata.constraint) === null || _metadata$constraint11 === void 0 ? void 0 : _metadata$constraint11.maxLength
});
if (typeof options.value === 'undefined' || options.value) {
props.defaultValue = metadata.initialValue;
}
return simplify(props);
}
function form(metadata, options) {
var onSubmit = options === null || options === void 0 ? void 0 : options.onSubmit;
var onReset = options === null || options === void 0 ? void 0 : options.onReset;
return cleanup(_rollupPluginBabelHelpers.objectSpread2({
id: metadata.id,
onSubmit: typeof onSubmit !== 'function' ? metadata.onSubmit : event => {
var context = metadata.onSubmit(event);
if (!event.defaultPrevented) {
onSubmit(event, context);
}
},
onReset: typeof onReset !== 'function' ? metadata.onReset : event => {
metadata.onReset(event);
onReset(event);
},
noValidate: metadata.noValidate
}, getAriaAttributes(metadata, options)));
}
function fieldset(metadata, options) {
return cleanup(_rollupPluginBabelHelpers.objectSpread2({
id: metadata.id,
name: metadata.name,
form: metadata.formId
}, getAriaAttributes(metadata, options)));
}
function collection(metadata, options) {
/**
* Derives the properties of a collection of checkboxes or radio buttons based on the field metadata,
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby` and `required`.
*
* @see https://conform.guide/api/react/get-textarea-props
* @example
* ```tsx
* <fieldset>
* {getCollectionProps(metadata, {
* type: 'checkbox',
* options: ['a', 'b', 'c']
* }).map((props) => (
* <div key={props.key}>
* <label htmlFor={props.id}>{props.value}</label>
* <input {...props} />
* </div>
* ))}
* </fieldset>
* ```
*/
function getCollectionProps(metadata, options) {
return options.options.map(value => {
var _metadata$constraint5;
return cleanup(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
var _metadata$key, _metadata$constraint12;
return simplify(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, getFormControlProps(metadata, options)), {}, {
key: "".concat((_metadata$key = metadata.key) !== null && _metadata$key !== void 0 ? _metadata$key : '').concat(value),
id: "".concat(metadata.id, "-").concat(value),
type: options.type,
value,
defaultChecked: options.type === 'checkbox' && Array.isArray(metadata.initialValue) ? metadata.initialValue.includes(value) : metadata.initialValue === value,
defaultChecked: typeof options.value === 'undefined' || options.value ? options.type === 'checkbox' && Array.isArray(metadata.initialValue) ? metadata.initialValue.includes(value) : metadata.initialValue === value : undefined,
// The required attribute doesn't make sense for checkbox group
// As it would require all checkboxes to be checked instead of at least one
// It is overriden with `undefiend` so it could be cleaned upW properly
required: options.type === 'checkbox' ? undefined : (_metadata$constraint5 = metadata.constraint) === null || _metadata$constraint5 === void 0 ? void 0 : _metadata$constraint5.required
// It is overriden with `undefiend` so it could be cleaned up properly
required: options.type === 'checkbox' ? undefined : (_metadata$constraint12 = metadata.constraint) === null || _metadata$constraint12 === void 0 ? void 0 : _metadata$constraint12.required
}));
});
}
function getControlButtonProps(formId, intents) {
return {
name: dom.INTENT,
value: dom.serializeIntents(intents),
form: formId,
formNoValidate: true
};
}
exports.collection = collection;
exports.fieldset = fieldset;
exports.form = form;
exports.hiddenProps = hiddenProps;
exports.input = input;
exports.select = select;
exports.textarea = textarea;
exports.getAriaAttributes = getAriaAttributes;
exports.getCollectionProps = getCollectionProps;
exports.getControlButtonProps = getControlButtonProps;
exports.getFieldsetProps = getFieldsetProps;
exports.getFormControlProps = getFormControlProps;
exports.getFormProps = getFormProps;
exports.getInputProps = getInputProps;
exports.getSelectProps = getSelectProps;
exports.getTextareaProps = getTextareaProps;

@@ -1,2 +0,2 @@

import { type UnionKeyof, type UnionKeyType, type FieldName, type Form, type FormOptions } from '@conform-to/dom';
import { type FormId, type FieldName, type FormOptions } from '@conform-to/dom';
import { useEffect } from 'react';

@@ -9,4 +9,5 @@ import { type FormMetadata, type FieldMetadata, type Pretty } from './context';

export declare const useSafeLayoutEffect: typeof useEffect;
export declare function useFormId(preferredId?: string): string;
export declare function useForm<Schema extends Record<string, any>>(options: Pretty<FormOptions<Schema> & {
export declare function useFormId<Schema extends Record<string, unknown>, Error>(preferredId?: string): FormId<Schema, Error>;
export declare function useNoValidate(defaultNoValidate?: boolean): boolean;
export declare function useForm<Schema extends Record<string, any>, Error = string[], Value = Schema>(options: Pretty<FormOptions<Schema, Error, Value> & {
/**

@@ -24,33 +25,20 @@ * If the form id is provided, Id for label,

}>): {
form: FormMetadata<Schema>;
context: Form<Schema>;
fields: Pretty<FieldsetMetadata<Schema>>;
meta: FormMetadata<Schema, Error>;
fields: ReturnType<FormMetadata<Schema, Error>['getFieldset']>;
};
export declare function useFormMetadata<Schema extends Record<string, any>>(options: {
formId: string;
context?: Form<Schema>;
export declare function useFormMetadata<Schema extends Record<string, any>, Error>(options: {
formId: FormId<Schema, Error>;
defaultNoValidate?: boolean;
}): FormMetadata<Schema>;
export type FieldsetMetadata<Schema> = Schema extends Array<any> ? {
[Key in keyof Schema]: FieldMetadata<Schema[Key]>;
} : Schema extends {
[key in string]?: any;
} ? {
[Key in UnionKeyof<Schema>]: FieldMetadata<UnionKeyType<Schema, Key>>;
} : Record<string | number, FieldMetadata<any>>;
export declare function useFieldset<Schema>(options: {
formId: string;
name?: FieldName<Schema>;
context?: Form;
}): Pretty<FieldsetMetadata<Schema>>;
export type Item<List> = List extends Array<infer Item> ? Item : any;
export declare function useFieldList<Schema>(options: {
formId: string;
name: FieldName<Schema>;
context?: Form;
}): Array<FieldMetadata<Item<Schema>>>;
export declare function useField<Schema>(options: {
formId: string;
name: FieldName<Schema>;
context?: Form;
}): FieldMetadata<Schema>;
}): FormMetadata<Schema, Error>;
export declare function useField<FormSchema extends Record<string, unknown>, FieldSchema = FormSchema, Error = unknown>(options: {
formId: FormId<FormSchema, Error>;
name: FieldName<FieldSchema>;
} | {
formId: FormId<FormSchema, Error>;
name?: undefined;
}): {
meta: FieldMetadata<FieldSchema, Error, FormSchema>;
fields: FieldMetadata<FieldSchema, Error, FormSchema>['getFieldset'] extends Function ? ReturnType<FieldMetadata<FieldSchema, Error, FormSchema>['getFieldset']> : never;
list: FieldMetadata<FieldSchema, Error, FormSchema>['getFieldList'] extends Function ? ReturnType<FieldMetadata<FieldSchema, Error, FormSchema>['getFieldList']> : never;
form: FormMetadata<FormSchema, Error>;
};

@@ -18,23 +18,36 @@ 'use strict';

}
function useNoValidate() {
var defaultNoValidate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var [noValidate, setNoValidate] = react.useState(defaultNoValidate);
useSafeLayoutEffect(() => {
// This is necessary to fix an issue in strict mode with related to our proxy setup
// It avoids the component from being rerendered without re-rendering the child
// Which reset the proxy but failed to capture its usage within child component
if (!noValidate) {
setNoValidate(true);
}
}, [noValidate]);
return noValidate;
}
function useForm(options) {
var formId = useFormId(options.id);
var initializeForm = () => dom.createForm(formId, options);
var [form, setForm] = react.useState(initializeForm);
var initializeContext = () => dom.createForm(formId, options);
var [form, setForm] = react.useState(initializeContext);
// If id changes, reinitialize the form immediately
if (formId !== form.id) {
setForm(initializeForm);
setForm(initializeContext);
}
var optionsRef = react.useRef(options);
var metadata = useFormMetadata({
formId,
context: form,
defaultNoValidate: options.defaultNoValidate
});
var fields = useFieldset({
formId,
context: form
});
useSafeLayoutEffect(() => form.initialize(), [form]);
useSafeLayoutEffect(() => {
document.addEventListener('input', form.input);
document.addEventListener('focusout', form.blur);
document.addEventListener('reset', form.reset);
return () => {
document.removeEventListener('input', form.input);
document.removeEventListener('focusout', form.blur);
document.removeEventListener('reset', form.reset);
};
}, [form]);
useSafeLayoutEffect(() => {
if (options.lastResult === optionsRef.current.lastResult) {

@@ -55,112 +68,43 @@ // If there is no change, do nothing

});
var subjectRef = context.useSubjectRef();
var state = context.useFormState(form, subjectRef);
var noValidate = useNoValidate(options.defaultNoValidate);
var meta = context.getFormMetadata(formId, state, subjectRef, form, noValidate);
return {
context: form,
fields,
form: metadata
meta,
fields: meta.getFieldset()
};
}
function useFormMetadata(options) {
var _options$defaultNoVal;
var subjectRef = context.useSubjectRef();
var form = context.useFormStore(options.formId, options.context);
var context$1 = context.useFormContext(form, subjectRef);
var metadata = context.getBaseMetadata(options.formId, context$1, {
subjectRef
});
var [noValidate, setNoValidate] = react.useState((_options$defaultNoVal = options.defaultNoValidate) !== null && _options$defaultNoVal !== void 0 ? _options$defaultNoVal : true);
useSafeLayoutEffect(() => {
// This is necessary to fix an issue in strict mode with related to our proxy setup
// It avoids the component from being rerendered without re-rendering the child
// Which reset the proxy but failed to capture its usage within child component
if (!noValidate) {
setNoValidate(true);
}
}, [noValidate]);
return new Proxy(metadata, {
get(target, key, receiver) {
switch (key) {
case 'onSubmit':
return event => {
var submitEvent = event.nativeEvent;
var result = form.submit(submitEvent);
if (submitEvent.defaultPrevented) {
event.preventDefault();
}
return result;
};
case 'onReset':
return event => form.reset(event.nativeEvent);
case 'noValidate':
return noValidate;
}
return Reflect.get(target, key, receiver);
}
});
var form = context.useRegistry(options.formId);
var state = context.useFormState(form, subjectRef);
var noValidate = useNoValidate(options.defaultNoValidate);
return context.getFormMetadata(options.formId, state, subjectRef, form, noValidate);
}
function useFieldset(options) {
var subjectRef = context.useSubjectRef();
var form = context.useFormStore(options.formId, options.context);
var context$1 = context.useFormContext(form, subjectRef);
return new Proxy({}, {
get(target, prop, receiver) {
var getMetadata = key => context.getFieldMetadata(options.formId, context$1, {
name: options.name,
key: key,
subjectRef
});
// To support array destructuring
if (prop === Symbol.iterator) {
var _index = 0;
return () => ({
next: () => ({
value: getMetadata(_index++),
done: false
})
});
}
var index = Number(prop);
if (typeof prop === 'string') {
return getMetadata(Number.isNaN(index) ? prop : index);
}
return Reflect.get(target, prop, receiver);
}
});
}
function useFieldList(options) {
var _context$initialValue;
var subjectRef = context.useSubjectRef({
initialValue: {
name: [options.name]
}
});
var form = context.useFormStore(options.formId, options.context);
var context$1 = context.useFormContext(form, subjectRef);
var initialValue = (_context$initialValue = context$1.initialValue[options.name]) !== null && _context$initialValue !== void 0 ? _context$initialValue : [];
if (!Array.isArray(initialValue)) {
throw new Error('The initial value at the given name is not a list');
}
return Array(initialValue.length).fill(0).map((_, index) => context.getFieldMetadata(options.formId, context$1, {
name: options.name,
key: index,
subjectRef
}));
}
function useField(options) {
var subjectRef = context.useSubjectRef();
var form = context.useFormStore(options.formId, options.context);
var context$1 = context.useFormContext(form, subjectRef);
var metadata = context.getFieldMetadata(options.formId, context$1, {
name: options.name,
subjectRef
});
return metadata;
var context$1 = context.useRegistry(options.formId);
var state = context.useFormState(context$1, subjectRef);
var meta = context.getFieldMetadata(options.formId, state, subjectRef, options.name);
var form = context.getFormMetadata(options.formId, state, subjectRef, context$1, false);
return {
meta,
// @ts-expect-error The types is used as a hint only
get fields() {
return meta.getFieldset();
},
// @ts-expect-error The types is used as a hint only
get list() {
return meta.getFieldList();
},
form
};
}
exports.useField = useField;
exports.useFieldList = useFieldList;
exports.useFieldset = useFieldset;
exports.useForm = useForm;
exports.useFormId = useFormId;
exports.useFormMetadata = useFormMetadata;
exports.useNoValidate = useNoValidate;
exports.useSafeLayoutEffect = useSafeLayoutEffect;

@@ -1,6 +0,6 @@

export { type Submission, type FieldName, requestIntent, isFieldElement, } from '@conform-to/dom';
export { type Field, type FieldMetadata as FieldConfig, FormProvider, FormStateInput, } from './context';
export { useForm, useFormMetadata, useFieldset, useFieldList, useField, } from './hooks';
export { useInputEvent } from './integrations';
export * as conform from './helpers';
export * as intent from './intent';
export { type Submission, type SubmissionResult, type Intent, type FormId, type FieldName, requestIntent, requestSubmit, isFieldElement, intent, } from '@conform-to/dom';
export { type FieldProps, type FieldMetadata, type FormMetadata, FormProvider, FormStateInput, } from './context';
export { useForm, useFormMetadata, useField } from './hooks';
export { useInputControl } from './integrations';
export { validateConstraint } from './validitystate';
export { getFormProps, getFieldsetProps, getInputProps, getSelectProps, getTextareaProps, getCollectionProps, getControlButtonProps, } from './helpers';

@@ -9,7 +9,11 @@ 'use strict';

var integrations = require('./integrations.js');
var validitystate = require('./validitystate.js');
var helpers = require('./helpers.js');
var intent = require('./intent.js');
Object.defineProperty(exports, 'intent', {
enumerable: true,
get: function () { return dom.intent; }
});
Object.defineProperty(exports, 'isFieldElement', {

@@ -23,11 +27,19 @@ enumerable: true,

});
Object.defineProperty(exports, 'requestSubmit', {
enumerable: true,
get: function () { return dom.requestSubmit; }
});
exports.FormProvider = context.FormProvider;
exports.FormStateInput = context.FormStateInput;
exports.useField = hooks.useField;
exports.useFieldList = hooks.useFieldList;
exports.useFieldset = hooks.useFieldset;
exports.useForm = hooks.useForm;
exports.useFormMetadata = hooks.useFormMetadata;
exports.useInputEvent = integrations.useInputEvent;
exports.conform = helpers;
exports.intent = intent;
exports.useInputControl = integrations.useInputControl;
exports.validateConstraint = validitystate.validateConstraint;
exports.getCollectionProps = helpers.getCollectionProps;
exports.getControlButtonProps = helpers.getControlButtonProps;
exports.getFieldsetProps = helpers.getFieldsetProps;
exports.getFormProps = helpers.getFormProps;
exports.getInputProps = helpers.getInputProps;
exports.getSelectProps = helpers.getSelectProps;
exports.getTextareaProps = helpers.getTextareaProps;

@@ -1,23 +0,18 @@

import { type FieldElement } from '@conform-to/dom';
import { type RefObject } from 'react';
export type InputControl = {
change: (eventOrValue: {
target: {
value: string;
};
} | string | boolean) => void;
import { type FieldElement, type FormValue } from '@conform-to/dom';
import { type FieldMetadata } from './context';
export type InputControl<Value> = {
value: Value;
change: (value: Value) => void;
focus: () => void;
blur: () => void;
};
/**
* Returns a ref object and a set of helpers that dispatch corresponding dom event.
*
* @see https://conform.guide/api/react#useinputevent
*/
export declare function useInputEvent(options: {
ref: RefObject<FieldElement> | (() => Element | RadioNodeList | FieldElement | null | undefined);
onInput?: (event: Event) => void;
onFocus?: (event: FocusEvent) => void;
onBlur?: (event: FocusEvent) => void;
onReset?: (event: Event) => void;
}): InputControl;
export declare function getFieldElement(formId: string, name: string, match?: (element: FieldElement) => boolean): FieldElement | null;
export declare function getEventTarget(formId: string, name: string): FieldElement;
export declare function useInputControl<Schema>(metadata: FieldMetadata<Schema, any, any>, options?: {
onFocus?: (event: Event) => void;
}): InputControl<string | undefined>;
export declare function useInputControl<Schema, Value>(metadata: FieldMetadata<Schema, any, any>, options: {
initialize: (value: FormValue<Schema> | undefined) => Value;
serialize?: (value: Value) => string;
onFocus?: (event: Event) => void;
}): InputControl<Value>;

@@ -5,86 +5,90 @@ 'use strict';

var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
var dom = require('@conform-to/dom');
var react = require('react');
var hooks = require('./hooks.js');
/**
* Returns a ref object and a set of helpers that dispatch corresponding dom event.
*
* @see https://conform.guide/api/react#useinputevent
*/
function useInputEvent(options) {
var optionsRef = react.useRef(options);
function getFieldElement(formId, name) {
var _document$forms$named;
var match = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : () => true;
var element = (_document$forms$named = document.forms.namedItem(formId)) === null || _document$forms$named === void 0 ? void 0 : _document$forms$named.elements.namedItem(name);
if (element) {
var items = element instanceof Element ? [element] : Array.from(element.values());
for (var item of items) {
if (dom.isFieldElement(item) && match(item)) {
return item;
}
}
}
return null;
}
function getEventTarget(formId, name) {
var element = getFieldElement(formId, name);
if (element) {
return element;
}
var form = document.forms.namedItem(formId);
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form === null || form === void 0 ? void 0 : form.appendChild(input);
return input;
}
function useInputControl(metadata, options) {
var _options$initialize;
var eventDispatched = react.useRef({
onInput: false,
onFocus: false,
onBlur: false
change: false,
focus: false,
blur: false
});
hooks.useSafeLayoutEffect(() => {
var [key, setKey] = react.useState(metadata.key);
var optionsRef = react.useRef(options);
var initialize = (_options$initialize = options === null || options === void 0 ? void 0 : options.initialize) !== null && _options$initialize !== void 0 ? _options$initialize : value => value === null || value === void 0 ? void 0 : value.toString();
var [value, setValue] = react.useState(() => initialize(metadata.initialValue));
if (key !== metadata.key) {
setValue(initialize(metadata.initialValue));
setKey(metadata.key);
}
react.useEffect(() => {
optionsRef.current = options;
});
hooks.useSafeLayoutEffect(() => {
react.useEffect(() => {
var createEventListener = listener => {
return event => {
var _optionsRef$current, _optionsRef$current2, _optionsRef$current3;
var element = typeof ((_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : _optionsRef$current.ref) === 'function' ? (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : _optionsRef$current2.ref() : (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : _optionsRef$current3.ref.current;
if (dom.isFieldElement(element) && (listener === 'onReset' ? event.target === element.form : event.target === element)) {
var _optionsRef$current4, _optionsRef$current4$;
if (listener !== 'onReset') {
eventDispatched.current[listener] = true;
var element = getFieldElement(metadata.formId, metadata.name, element => element === event.target);
if (element) {
if (listener === 'focus') {
var _optionsRef$current, _optionsRef$current$o;
(_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 || (_optionsRef$current$o = _optionsRef$current.onFocus) === null || _optionsRef$current$o === void 0 ? void 0 : _optionsRef$current$o.call(_optionsRef$current, event);
}
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4[listener]) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, event);
eventDispatched.current[listener] = true;
}
};
};
var inputHandler = createEventListener('onInput');
var focusHandler = createEventListener('onFocus');
var blurHandler = createEventListener('onBlur');
var resetHandler = createEventListener('onReset');
// focus/blur event does not bubble
var inputHandler = createEventListener('change');
var focusHandler = createEventListener('focus');
var blurHandler = createEventListener('blur');
document.addEventListener('input', inputHandler, true);
document.addEventListener('focus', focusHandler, true);
document.addEventListener('blur', blurHandler, true);
document.addEventListener('reset', resetHandler);
document.addEventListener('focusin', focusHandler, true);
document.addEventListener('focusout', blurHandler, true);
return () => {
document.removeEventListener('input', inputHandler, true);
document.removeEventListener('focus', focusHandler, true);
document.removeEventListener('blur', blurHandler, true);
document.removeEventListener('reset', resetHandler);
document.removeEventListener('focusin', focusHandler, true);
document.removeEventListener('focusout', blurHandler, true);
};
}, []);
var control = react.useMemo(() => {
var dispatch = (listener, fn) => {
if (!eventDispatched.current[listener]) {
var _optionsRef$current5, _optionsRef$current6, _optionsRef$current7;
var _element = typeof ((_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.ref) === 'function' ? (_optionsRef$current6 = optionsRef.current) === null || _optionsRef$current6 === void 0 ? void 0 : _optionsRef$current6.ref() : (_optionsRef$current7 = optionsRef.current) === null || _optionsRef$current7 === void 0 ? void 0 : _optionsRef$current7.ref.current;
if (!dom.isFieldElement(_element)) {
// eslint-disable-next-line no-console
console.warn('Failed to dispatch event; is the input mounted?');
return;
}
// To avoid recursion
eventDispatched.current[listener] = true;
fn(_element);
}
eventDispatched.current[listener] = false;
};
}, [metadata.formId, metadata.name]);
var handlers = react.useMemo(() => {
return {
change(eventOrValue) {
dispatch('onInput', element => {
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
if (typeof eventOrValue !== 'boolean') {
throw new Error('You should pass a boolean when changing a checkbox or radio input');
change(value) {
if (!eventDispatched.current.change) {
var _ref, _optionsRef$current$s, _optionsRef$current2, _optionsRef$current2$;
var _element = getEventTarget(metadata.formId, metadata.name);
var serializedValue = (_ref = (_optionsRef$current$s = (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 || (_optionsRef$current2$ = _optionsRef$current2.serialize) === null || _optionsRef$current2$ === void 0 ? void 0 : _optionsRef$current2$.call(_optionsRef$current2, value)) !== null && _optionsRef$current$s !== void 0 ? _optionsRef$current$s : value === null || value === void 0 ? void 0 : value.toString()) !== null && _ref !== void 0 ? _ref : '';
eventDispatched.current.change = true;
if (_element instanceof HTMLInputElement && (_element.type === 'checkbox' || _element.type === 'radio')) {
if (_element.checked ? _element.value !== serializedValue : _element.value === serializedValue) {
_element.click();
}
element.checked = eventOrValue;
} else {
if (typeof eventOrValue === 'boolean') {
throw new Error('You can pass a boolean only when changing a checkbox or radio input');
}
var value = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
// No change event will triggered on React if `element.value` is updated
// before dispatching the event
if (element.value !== value) {
// No change event will be triggered on React if `element.value` is already updated
if (_element.value !== serializedValue) {
/**

@@ -98,4 +102,4 @@ * Triggering react custom change event

set: valueSetter
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
var prototype = Object.getPrototypeOf(element);
} = Object.getOwnPropertyDescriptor(_element, 'value') || {};
var prototype = Object.getPrototypeOf(_element);
var {

@@ -105,6 +109,6 @@ set: prototypeValueSetter

if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
prototypeValueSetter.call(_element, value);
} else {
if (valueSetter) {
valueSetter.call(element, value);
valueSetter.call(_element, value);
} else {

@@ -115,35 +119,47 @@ throw new Error('The given element does not have a value setter');

}
// Dispatch input event with the updated input value
_element.dispatchEvent(new InputEvent('input', {
bubbles: true
}));
// Dispatch change event (necessary for select to update the selected option)
_element.dispatchEvent(new Event('change', {
bubbles: true
}));
}
// Dispatch input event with the updated input value
element.dispatchEvent(new InputEvent('input', {
bubbles: true
}));
// Dispatch change event (necessary for select to update the selected option)
element.dispatchEvent(new Event('change', {
bubbles: true
}));
});
}
setValue(value);
eventDispatched.current.change = false;
},
focus() {
dispatch('onFocus', element => {
element.dispatchEvent(new FocusEvent('focusin', {
if (!eventDispatched.current.focus) {
var _element2 = getEventTarget(metadata.formId, metadata.name);
eventDispatched.current.focus = true;
_element2.dispatchEvent(new FocusEvent('focusin', {
bubbles: true
}));
element.dispatchEvent(new FocusEvent('focus'));
});
_element2.dispatchEvent(new FocusEvent('focus'));
}
eventDispatched.current.focus = false;
},
blur() {
dispatch('onBlur', element => {
element.dispatchEvent(new FocusEvent('focusout', {
if (!eventDispatched.current.blur) {
var _element3 = getEventTarget(metadata.formId, metadata.name);
eventDispatched.current.blur = true;
_element3.dispatchEvent(new FocusEvent('focusout', {
bubbles: true
}));
element.dispatchEvent(new FocusEvent('blur'));
});
_element3.dispatchEvent(new FocusEvent('blur'));
}
eventDispatched.current.blur = false;
}
};
}, [optionsRef]);
return control;
}, [metadata.formId, metadata.name]);
return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, handlers), {}, {
value
});
}
exports.useInputEvent = useInputEvent;
exports.getEventTarget = getEventTarget;
exports.getFieldElement = getFieldElement;
exports.useInputControl = useInputControl;

@@ -6,3 +6,3 @@ {

"license": "MIT",
"version": "1.0.0-pre.0",
"version": "1.0.0-pre.1",
"main": "index.js",

@@ -34,3 +34,3 @@ "module": "index.mjs",

"dependencies": {
"@conform-to/dom": "1.0.0-pre.0"
"@conform-to/dom": "1.0.0-pre.1"
},

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

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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