Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

@conform-to/react

Package Overview
Dependencies
Maintainers
1
Versions
98
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
+12
validitystate.d.ts
import { type Submission } from '@conform-to/dom';
/**
* Validate the form with the Constraint Validation API
* @see https://conform.guide/api/react#validateconstraint
*/
export declare function validateConstraint(options: {
form: HTMLFormElement;
formData?: FormData;
}): Submission<Record<string, any>, {
validity: ValidityState;
validationMessage: string;
}>;
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
var dom = require('@conform-to/dom');
/**
* Validate the form with the Constraint Validation API
* @see https://conform.guide/api/react#validateconstraint
*/
function validateConstraint(options) {
var _options$formData;
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
return dom.parse(formData, {
resolve(value) {
var error = {};
for (var element of options.form.elements) {
if (dom.isFieldElement(element) && element.name !== '' && !element.validity.valid) {
error[element.name] = {
validity: _rollupPluginBabelHelpers.objectSpread2({}, element.validity),
validationMessage: element.validationMessage
};
}
}
if (Object.entries(error).length > 0) {
return {
error
};
}
return {
value
};
}
});
}
exports.validateConstraint = validateConstraint;
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { parse, isFieldElement } from '@conform-to/dom';
/**
* Validate the form with the Constraint Validation API
* @see https://conform.guide/api/react#validateconstraint
*/
function validateConstraint(options) {
var _options$formData;
var formData = (_options$formData = options === null || options === void 0 ? void 0 : options.formData) !== null && _options$formData !== void 0 ? _options$formData : new FormData(options.form);
return parse(formData, {
resolve(value) {
var error = {};
for (var element of options.form.elements) {
if (isFieldElement(element) && element.name !== '' && !element.validity.valid) {
error[element.name] = {
validity: _objectSpread2({}, element.validity),
validationMessage: element.validationMessage
};
}
}
if (Object.entries(error).length > 0) {
return {
error
};
}
return {
value
};
}
});
}
export { validateConstraint };
+31
-23

@@ -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;
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { STATE, isPrefix, formatPaths, getPaths } from '@conform-to/dom';
import { STATE, formatPaths, getPaths, isPrefix } from '@conform-to/dom';
import { useContext, useMemo, createContext, useCallback, useSyncExternalStore, useRef } from 'react';

@@ -7,3 +7,3 @@ import { jsx } from 'react/jsx-runtime';

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

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

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

@@ -34,3 +33,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__*/jsx("input", {

@@ -52,12 +51,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] = _objectSpread2(_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] = _objectSpread2(_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({

@@ -67,21 +65,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 (isPrefix(key, name) && !context.state.valid[key]) {
for (var key of Object.keys(state.error)) {
if (isPrefix(key, name) && !state.valid[key]) {
return false;

@@ -92,13 +90,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 (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);
}
});
}

@@ -109,3 +117,3 @@ }, {

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

@@ -115,9 +123,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;

@@ -129,9 +137,7 @@ }

}
function getFieldMetadata(formId, context, options) {
var _options$name2, _options$name3;
var name = typeof options.key !== 'undefined' ? formatPaths([...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 : formatPaths([...getPaths(prefix), key]);
var metadata = getMetadata(formId, state, subjectRef, name);
return new Proxy(metadata, {

@@ -145,3 +151,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));
};
}
}

@@ -152,3 +170,25 @@ 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);
}
});
}
export { FormProvider, FormStateInput, Registry, getBaseMetadata, getFieldMetadata, useFormContext, useFormStore, useSubjectRef };
export { FormProvider, FormStateInput, Registry, getFieldMetadata, getFormMetadata, getMetadata, updateSubjectRef, useFormState, useRegistry, 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 {};
+192
-98

@@ -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;
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { INTENT, serializeIntents } from '@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) {

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

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

@@ -21,115 +26,202 @@ 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(_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(_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(_objectSpread2(_objectSpread2({
id: metadata.id,
name: metadata.name,
form: metadata.formId,
return simplify(_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 = _objectSpread2(_objectSpread2({}, getFormControlProps(field, options)), {}, {
var props = _objectSpread2(_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(_objectSpread2(_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 = _objectSpread2(_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(_objectSpread2(_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 = _objectSpread2(_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(_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(_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(_objectSpread2(_objectSpread2({}, getFormControlProps(metadata, options)), {}, {
var _metadata$key, _metadata$constraint12;
return simplify(_objectSpread2(_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: INTENT,
value: serializeIntents(intents),
form: formId,
formNoValidate: true
};
}
export { collection, fieldset, form, hiddenProps, input, select, textarea };
export { getAriaAttributes, getCollectionProps, getControlButtonProps, getFieldsetProps, getFormControlProps, getFormProps, getInputProps, getSelectProps, 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>;
};
+53
-109

@@ -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;
+54
-109
import { createForm } from '@conform-to/dom';
import { useState, useRef, useEffect, useLayoutEffect, useId } from 'react';
import { useSubjectRef, useFormStore, useFormContext, getBaseMetadata, getFieldMetadata } from './context.mjs';
import { useSubjectRef, useFormState, getFormMetadata, useRegistry, getFieldMetadata } from './context.mjs';

@@ -14,23 +14,36 @@ /**

}
function useNoValidate() {
var defaultNoValidate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var [noValidate, setNoValidate] = 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 = () => createForm(formId, options);
var [form, setForm] = useState(initializeForm);
var initializeContext = () => createForm(formId, options);
var [form, setForm] = useState(initializeContext);
// If id changes, reinitialize the form immediately
if (formId !== form.id) {
setForm(initializeForm);
setForm(initializeContext);
}
var optionsRef = 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) {

@@ -51,106 +64,38 @@ // If there is no change, do nothing

});
var subjectRef = useSubjectRef();
var state = useFormState(form, subjectRef);
var noValidate = useNoValidate(options.defaultNoValidate);
var meta = getFormMetadata(formId, state, subjectRef, form, noValidate);
return {
context: form,
fields,
form: metadata
meta,
fields: meta.getFieldset()
};
}
function useFormMetadata(options) {
var _options$defaultNoVal;
var subjectRef = useSubjectRef();
var form = useFormStore(options.formId, options.context);
var context = useFormContext(form, subjectRef);
var metadata = getBaseMetadata(options.formId, context, {
subjectRef
});
var [noValidate, setNoValidate] = 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 = useRegistry(options.formId);
var state = useFormState(form, subjectRef);
var noValidate = useNoValidate(options.defaultNoValidate);
return getFormMetadata(options.formId, state, subjectRef, form, noValidate);
}
function useFieldset(options) {
var subjectRef = useSubjectRef();
var form = useFormStore(options.formId, options.context);
var context = useFormContext(form, subjectRef);
return new Proxy({}, {
get(target, prop, receiver) {
var getMetadata = key => getFieldMetadata(options.formId, context, {
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 = useSubjectRef({
initialValue: {
name: [options.name]
}
});
var form = useFormStore(options.formId, options.context);
var context = useFormContext(form, subjectRef);
var initialValue = (_context$initialValue = context.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) => getFieldMetadata(options.formId, context, {
name: options.name,
key: index,
subjectRef
}));
}
function useField(options) {
var subjectRef = useSubjectRef();
var form = useFormStore(options.formId, options.context);
var context = useFormContext(form, subjectRef);
var metadata = getFieldMetadata(options.formId, context, {
name: options.name,
subjectRef
});
return metadata;
var context = useRegistry(options.formId);
var state = useFormState(context, subjectRef);
var meta = getFieldMetadata(options.formId, state, subjectRef, options.name);
var form = getFormMetadata(options.formId, state, subjectRef, context, 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
};
}
export { useField, useFieldList, useFieldset, useForm, useFormId, useFormMetadata, useSafeLayoutEffect };
export { useField, useForm, useFormId, useFormMetadata, useNoValidate, 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,8 +0,6 @@

export { isFieldElement, requestIntent } from '@conform-to/dom';
export { intent, isFieldElement, requestIntent, requestSubmit } from '@conform-to/dom';
export { FormProvider, FormStateInput } from './context.mjs';
export { useField, useFieldList, useFieldset, useForm, useFormMetadata } from './hooks.mjs';
export { useInputEvent } from './integrations.mjs';
import * as helpers from './helpers.mjs';
export { helpers as conform };
import * as intent from './intent.mjs';
export { intent };
export { useField, useForm, useFormMetadata } from './hooks.mjs';
export { useInputControl } from './integrations.mjs';
export { validateConstraint } from './validitystate.mjs';
export { getCollectionProps, getControlButtonProps, getFieldsetProps, getFormProps, getInputProps, getSelectProps, getTextareaProps } from './helpers.mjs';

@@ -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;

@@ -0,85 +1,89 @@

import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { isFieldElement } from '@conform-to/dom';
import { useRef, useMemo } from 'react';
import { useSafeLayoutEffect } from './hooks.mjs';
import { useRef, useState, useEffect, useMemo } from 'react';
/**
* 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 = 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 (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 = useRef({
onInput: false,
onFocus: false,
onBlur: false
change: false,
focus: false,
blur: false
});
useSafeLayoutEffect(() => {
var [key, setKey] = useState(metadata.key);
var optionsRef = 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] = useState(() => initialize(metadata.initialValue));
if (key !== metadata.key) {
setValue(initialize(metadata.initialValue));
setKey(metadata.key);
}
useEffect(() => {
optionsRef.current = options;
});
useSafeLayoutEffect(() => {
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 (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 = 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 (!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 = 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) {
/**

@@ -93,4 +97,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 {

@@ -100,6 +104,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 {

@@ -110,35 +114,45 @@ 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 _objectSpread2(_objectSpread2({}, handlers), {}, {
value
});
}
export { useInputEvent };
export { getEventTarget, getFieldElement, 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": {

import { type ListIntentPayload } from '@conform-to/dom';
import type { Field, Pretty } from './context';
declare function createIntentButtonProps(value: string, form?: string): {
name: string;
value: string;
form: string | undefined;
formNoValidate: boolean;
};
export declare function validate(field: Field<unknown>): {
name: string;
value: string;
form: string | undefined;
formNoValidate: boolean;
};
type ExtractListIntentPayload<Operation, Schema = unknown> = Pretty<Omit<Extract<ListIntentPayload<Schema>, {
operation: Operation;
}>, 'name' | 'operation'>>;
type ListIntent<Operation> = {} extends ExtractListIntentPayload<Operation> ? <Item>(name: Field<Array<Item>>, payload?: ExtractListIntentPayload<Operation, Item>) => ReturnType<typeof createIntentButtonProps> : <Item>(field: Field<Array<Item>>, payload: ExtractListIntentPayload<Operation, Item>) => ReturnType<typeof createIntentButtonProps>;
/**
* Helpers to configure an intent button for modifying a list
*
* @see https://conform.guide/api/react#list
*/
export declare const list: {
/**
* @deprecated You can use `insert` without specifying an index instead
*/
append: ListIntent<'append'>;
/**
* @deprecated You can use `insert` with zero index instead
*/
prepend: ListIntent<'prepend'>;
insert: ListIntent<'insert'>;
replace: ListIntent<'replace'>;
remove: ListIntent<'remove'>;
reorder: ListIntent<'reorder'>;
};
export {};
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
var dom = require('@conform-to/dom');
function createIntentButtonProps(value, form) {
return {
name: dom.INTENT,
value,
form,
formNoValidate: true
};
}
function validate(field) {
return createIntentButtonProps(dom.validate.serialize(field.name), field.formId);
}
/**
* Helpers to configure an intent button for modifying a list
*
* @see https://conform.guide/api/react#list
*/
var list = new Proxy({}, {
get(_target, operation) {
return function (field) {
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return createIntentButtonProps(dom.list.serialize(_rollupPluginBabelHelpers.objectSpread2({
name: field.name,
operation
}, payload)), field.formId);
};
}
});
exports.list = list;
exports.validate = validate;
import { objectSpread2 as _objectSpread2 } from './_virtual/_rollupPluginBabelHelpers.mjs';
import { validate as validate$1, list as list$1, INTENT } from '@conform-to/dom';
function createIntentButtonProps(value, form) {
return {
name: INTENT,
value,
form,
formNoValidate: true
};
}
function validate(field) {
return createIntentButtonProps(validate$1.serialize(field.name), field.formId);
}
/**
* Helpers to configure an intent button for modifying a list
*
* @see https://conform.guide/api/react#list
*/
var list = new Proxy({}, {
get(_target, operation) {
return function (field) {
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return createIntentButtonProps(list$1.serialize(_objectSpread2({
name: field.name,
operation
}, payload)), field.formId);
};
}
});
export { list, validate };