@conform-to/react 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README +1 -1
- package/context.d.ts +12 -9
- package/helpers.d.ts +4 -4
- package/helpers.js +4 -2
- package/helpers.mjs +4 -2
- package/index.d.ts +1 -1
- package/index.js +2 -0
- package/index.mjs +1 -1
- package/integrations.d.ts +38 -12
- package/integrations.js +116 -52
- package/integrations.mjs +114 -54
- package/package.json +2 -2
package/README
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
Version 1.0.
|
|
11
|
+
Version 1.0.2 / License MIT / Copyright (c) 2024 Edmund Hung
|
|
12
12
|
|
|
13
13
|
A type-safe form validation library utilizing web fundamentals to progressively enhance HTML Forms with full support for server frameworks like Remix and Next.js.
|
|
14
14
|
|
package/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Constraint, type FormId, type FieldName, type FormContext as BaseFormContext, type FormValue, type FormState, type Intent, type SubscriptionScope, type SubscriptionSubject, type
|
|
1
|
+
import { type Constraint, type Combine, type FormId, type FieldName, type FormContext as BaseFormContext, type FormValue, type FormState, type Intent, type SubscriptionScope, type SubscriptionSubject, type FormOptions as BaseFormOptions } from '@conform-to/dom';
|
|
2
2
|
import { type FormEvent, type ReactElement, type ReactNode, type MutableRefObject } from 'react';
|
|
3
3
|
export type Pretty<T> = {
|
|
4
4
|
[K in keyof T]: T[K];
|
|
@@ -21,19 +21,22 @@ export type FormMetadata<Schema extends Record<string, unknown> = Record<string,
|
|
|
21
21
|
id: FormId<Schema, FormError>;
|
|
22
22
|
context: Wrapped<FormContext<Schema, FormError>>;
|
|
23
23
|
status?: 'success' | 'error';
|
|
24
|
-
getFieldset: () => {
|
|
25
|
-
[Key in
|
|
26
|
-
}
|
|
24
|
+
getFieldset: () => Required<{
|
|
25
|
+
[Key in keyof Combine<Schema>]: FieldMetadata<Combine<Schema>[Key], Schema, FormError>;
|
|
26
|
+
}>;
|
|
27
27
|
onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
|
28
28
|
noValidate: boolean;
|
|
29
29
|
};
|
|
30
|
+
type SubfieldMetadata<Schema, FormSchema extends Record<string, any>, FormError, CombinedSchema = Combine<Schema>> = Exclude<Schema, undefined> extends Array<infer Item> ? {
|
|
31
|
+
getFieldList: () => Array<FieldMetadata<Item, FormSchema, FormError>>;
|
|
32
|
+
} : Exclude<Schema, undefined> extends Record<string, unknown> ? {
|
|
33
|
+
getFieldset: () => Required<{
|
|
34
|
+
[Key in keyof CombinedSchema]: FieldMetadata<CombinedSchema[Key], FormSchema, FormError>;
|
|
35
|
+
}>;
|
|
36
|
+
} : {};
|
|
30
37
|
export type FieldMetadata<Schema = unknown, FormSchema extends Record<string, any> = Record<string, unknown>, FormError = string[]> = Metadata<Schema, FormSchema, FormError> & Constraint & {
|
|
31
38
|
formId: FormId<FormSchema, FormError>;
|
|
32
|
-
|
|
33
|
-
[Key in UnionKeyof<Schema>]: FieldMetadata<UnionKeyType<Schema, Key>, FormSchema, FormError>;
|
|
34
|
-
};
|
|
35
|
-
getFieldList: unknown extends Schema ? () => unknown : Schema extends Array<infer Item> ? () => Array<FieldMetadata<Item, FormSchema, FormError>> : never;
|
|
36
|
-
};
|
|
39
|
+
} & SubfieldMetadata<Schema, FormSchema, FormError>;
|
|
37
40
|
export declare const Form: import("react").Context<FormContext<any, string[], any>[]>;
|
|
38
41
|
declare const wrappedSymbol: unique symbol;
|
|
39
42
|
export type Wrapped<Type> = {
|
package/helpers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { FormMetadata, FieldMetadata, Metadata, Pretty
|
|
2
|
+
import type { FormMetadata, FieldMetadata, Metadata, Pretty } from './context';
|
|
3
3
|
type FormControlProps = {
|
|
4
4
|
key: string | undefined;
|
|
5
5
|
id: string;
|
|
@@ -138,7 +138,7 @@ export declare function getFormControlProps<Schema>(metadata: FieldMetadata<Sche
|
|
|
138
138
|
* <input {...getInputProps(metadata, { type: 'radio', value: false })} />
|
|
139
139
|
* ```
|
|
140
140
|
*/
|
|
141
|
-
export declare function getInputProps<Schema
|
|
141
|
+
export declare function getInputProps<Schema>(metadata: FieldMetadata<Schema, any, any>, options: InputOptions): InputProps;
|
|
142
142
|
/**
|
|
143
143
|
* Derives the properties of a select element based on the field metadata,
|
|
144
144
|
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
|
|
@@ -154,7 +154,7 @@ export declare function getInputProps<Schema extends Primitive | File[]>(metadat
|
|
|
154
154
|
* <select {...getSelectProps(metadata, { value: false })} />
|
|
155
155
|
* ```
|
|
156
156
|
*/
|
|
157
|
-
export declare function getSelectProps<Schema
|
|
157
|
+
export declare function getSelectProps<Schema>(metadata: FieldMetadata<Schema, any, any>, options?: SelectOptions): SelectProps;
|
|
158
158
|
/**
|
|
159
159
|
* Derives the properties of a textarea element based on the field metadata,
|
|
160
160
|
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby`
|
|
@@ -170,7 +170,7 @@ export declare function getSelectProps<Schema extends Exclude<Primitive, File> |
|
|
|
170
170
|
* <textarea {...getTextareaProps(metadata, { value: false })} />
|
|
171
171
|
* ```
|
|
172
172
|
*/
|
|
173
|
-
export declare function getTextareaProps<Schema
|
|
173
|
+
export declare function getTextareaProps<Schema>(metadata: FieldMetadata<Schema, any, any>, options?: TextareaOptions): TextareaProps;
|
|
174
174
|
/**
|
|
175
175
|
* Derives the properties of a collection of checkboxes or radio buttons based on the field metadata,
|
|
176
176
|
* including common form control attributes like `key`, `id`, `name`, `form`, `autoFocus`, `aria-invalid`, `aria-describedby` and `required`.
|
package/helpers.js
CHANGED
|
@@ -141,7 +141,8 @@ function getSelectProps(metadata) {
|
|
|
141
141
|
multiple: metadata.multiple
|
|
142
142
|
});
|
|
143
143
|
if (typeof options.value === 'undefined' || options.value) {
|
|
144
|
-
|
|
144
|
+
var _metadata$initialValu;
|
|
145
|
+
props.defaultValue = Array.isArray(metadata.initialValue) ? metadata.initialValue.map(item => "".concat(item !== null && item !== void 0 ? item : '')) : (_metadata$initialValu = metadata.initialValue) === null || _metadata$initialValu === void 0 ? void 0 : _metadata$initialValu.toString();
|
|
145
146
|
}
|
|
146
147
|
return simplify(props);
|
|
147
148
|
}
|
|
@@ -168,7 +169,8 @@ function getTextareaProps(metadata) {
|
|
|
168
169
|
maxLength: metadata.maxLength
|
|
169
170
|
});
|
|
170
171
|
if (typeof options.value === 'undefined' || options.value) {
|
|
171
|
-
|
|
172
|
+
var _metadata$initialValu2;
|
|
173
|
+
props.defaultValue = (_metadata$initialValu2 = metadata.initialValue) === null || _metadata$initialValu2 === void 0 ? void 0 : _metadata$initialValu2.toString();
|
|
172
174
|
}
|
|
173
175
|
return simplify(props);
|
|
174
176
|
}
|
package/helpers.mjs
CHANGED
|
@@ -137,7 +137,8 @@ function getSelectProps(metadata) {
|
|
|
137
137
|
multiple: metadata.multiple
|
|
138
138
|
});
|
|
139
139
|
if (typeof options.value === 'undefined' || options.value) {
|
|
140
|
-
|
|
140
|
+
var _metadata$initialValu;
|
|
141
|
+
props.defaultValue = Array.isArray(metadata.initialValue) ? metadata.initialValue.map(item => "".concat(item !== null && item !== void 0 ? item : '')) : (_metadata$initialValu = metadata.initialValue) === null || _metadata$initialValu === void 0 ? void 0 : _metadata$initialValu.toString();
|
|
141
142
|
}
|
|
142
143
|
return simplify(props);
|
|
143
144
|
}
|
|
@@ -164,7 +165,8 @@ function getTextareaProps(metadata) {
|
|
|
164
165
|
maxLength: metadata.maxLength
|
|
165
166
|
});
|
|
166
167
|
if (typeof options.value === 'undefined' || options.value) {
|
|
167
|
-
|
|
168
|
+
var _metadata$initialValu2;
|
|
169
|
+
props.defaultValue = (_metadata$initialValu2 = metadata.initialValue) === null || _metadata$initialValu2 === void 0 ? void 0 : _metadata$initialValu2.toString();
|
|
168
170
|
}
|
|
169
171
|
return simplify(props);
|
|
170
172
|
}
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { type Submission, type SubmissionResult, type DefaultValue, type Intent, type FormId, type FieldName, parse, } from '@conform-to/dom';
|
|
2
2
|
export { type FieldMetadata, type FormMetadata, FormProvider, FormStateInput, } from './context';
|
|
3
3
|
export { useForm, useFormMetadata, useField } from './hooks';
|
|
4
|
-
export { useInputControl } from './integrations';
|
|
4
|
+
export { Control as unstable_Control, useControl as unstable_useControl, useInputControl, } from './integrations';
|
|
5
5
|
export { getFormProps, getFieldsetProps, getInputProps, getSelectProps, getTextareaProps, getCollectionProps, } from './helpers';
|
package/index.js
CHANGED
|
@@ -19,6 +19,8 @@ exports.FormStateInput = context.FormStateInput;
|
|
|
19
19
|
exports.useField = hooks.useField;
|
|
20
20
|
exports.useForm = hooks.useForm;
|
|
21
21
|
exports.useFormMetadata = hooks.useFormMetadata;
|
|
22
|
+
exports.unstable_Control = integrations.Control;
|
|
23
|
+
exports.unstable_useControl = integrations.useControl;
|
|
22
24
|
exports.useInputControl = integrations.useInputControl;
|
|
23
25
|
exports.getCollectionProps = helpers.getCollectionProps;
|
|
24
26
|
exports.getFieldsetProps = helpers.getFieldsetProps;
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { parse } from '@conform-to/dom';
|
|
2
2
|
export { FormProvider, FormStateInput } from './context.mjs';
|
|
3
3
|
export { useField, useForm, useFormMetadata } from './hooks.mjs';
|
|
4
|
-
export { useInputControl } from './integrations.mjs';
|
|
4
|
+
export { Control as unstable_Control, useControl as unstable_useControl, useInputControl } from './integrations.mjs';
|
|
5
5
|
export { getCollectionProps, getFieldsetProps, getFormProps, getInputProps, getSelectProps, getTextareaProps } from './helpers.mjs';
|
package/integrations.d.ts
CHANGED
|
@@ -1,19 +1,45 @@
|
|
|
1
|
-
import { type Key } from 'react';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
focus: () => void;
|
|
6
|
-
blur: () => void;
|
|
7
|
-
};
|
|
8
|
-
export declare function getFormElement(formId: string): HTMLFormElement;
|
|
9
|
-
export declare function getFieldElements(form: HTMLFormElement, name: string): Array<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>;
|
|
10
|
-
export declare function getEventTarget(form: HTMLFormElement, name: string): HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null;
|
|
1
|
+
import { type Key, type RefCallback } from 'react';
|
|
2
|
+
export declare function getFormElement(formId: string): HTMLFormElement | null;
|
|
3
|
+
export declare function getFieldElements(form: HTMLFormElement | null, name: string): Array<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>;
|
|
4
|
+
export declare function getEventTarget(form: HTMLFormElement | null, name: string, value?: string | string[]): HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | null;
|
|
11
5
|
export declare function createDummySelect(form: HTMLFormElement, name: string, value?: string | string[] | undefined): HTMLSelectElement;
|
|
12
6
|
export declare function isDummySelect(element: HTMLElement): element is HTMLSelectElement;
|
|
13
7
|
export declare function updateFieldValue(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, value: string | string[]): void;
|
|
14
|
-
export declare function
|
|
8
|
+
export declare function useInputEvent(): {
|
|
9
|
+
change(value: string | string[]): void;
|
|
10
|
+
focus(): void;
|
|
11
|
+
blur(): void;
|
|
12
|
+
register: RefCallback<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | undefined>;
|
|
13
|
+
};
|
|
14
|
+
export declare function useInputValue<Value extends string | string[] | Array<string | undefined>>(options: {
|
|
15
|
+
key?: Key | null | undefined;
|
|
16
|
+
initialValue?: Value | undefined;
|
|
17
|
+
}): readonly [(Value extends string ? Value : string | string[]) | undefined, import("react").Dispatch<import("react").SetStateAction<(Value extends string ? Value : string | string[]) | undefined>>];
|
|
18
|
+
export declare function useControl<Value extends string | string[] | Array<string | undefined>>(meta: {
|
|
19
|
+
key?: Key | null | undefined;
|
|
20
|
+
initialValue?: Value | undefined;
|
|
21
|
+
}): {
|
|
22
|
+
register: RefCallback<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | undefined>;
|
|
23
|
+
value: (Value extends string ? Value : string | string[]) | undefined;
|
|
24
|
+
change: (value: Value extends string ? Value : string | string[]) => void;
|
|
25
|
+
focus: () => void;
|
|
26
|
+
blur: () => void;
|
|
27
|
+
};
|
|
28
|
+
export declare function useInputControl<Value extends string | string[] | Array<string | undefined>>(meta: {
|
|
15
29
|
key?: Key | null | undefined;
|
|
16
30
|
name: string;
|
|
17
31
|
formId: string;
|
|
18
32
|
initialValue?: Value | undefined;
|
|
19
|
-
}):
|
|
33
|
+
}): {
|
|
34
|
+
value: (Value extends string ? Value : string | string[]) | undefined;
|
|
35
|
+
change: import("react").Dispatch<import("react").SetStateAction<(Value extends string ? Value : string | string[]) | undefined>>;
|
|
36
|
+
focus: () => void;
|
|
37
|
+
blur: () => void;
|
|
38
|
+
};
|
|
39
|
+
export declare function Control<Value extends string | string[] | Array<string | undefined>>(props: {
|
|
40
|
+
meta: {
|
|
41
|
+
key?: Key | null | undefined;
|
|
42
|
+
initialValue?: Value | undefined;
|
|
43
|
+
};
|
|
44
|
+
render: (control: ReturnType<typeof useControl<Value>>) => React.ReactNode;
|
|
45
|
+
}): import("react").ReactNode;
|
package/integrations.js
CHANGED
|
@@ -2,24 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
5
|
var react = require('react');
|
|
7
6
|
|
|
8
7
|
function getFormElement(formId) {
|
|
9
|
-
|
|
10
|
-
if (!element) {
|
|
11
|
-
throw new Error('Form not found');
|
|
12
|
-
}
|
|
13
|
-
return element;
|
|
8
|
+
return document.forms.namedItem(formId);
|
|
14
9
|
}
|
|
15
10
|
function getFieldElements(form, name) {
|
|
16
|
-
var field = form.elements.namedItem(name);
|
|
11
|
+
var field = form === null || form === void 0 ? void 0 : form.elements.namedItem(name);
|
|
17
12
|
var elements = !field ? [] : field instanceof Element ? [field] : Array.from(field.values());
|
|
18
13
|
return elements.filter(element => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement);
|
|
19
14
|
}
|
|
20
|
-
function getEventTarget(form, name) {
|
|
15
|
+
function getEventTarget(form, name, value) {
|
|
21
16
|
var _elements$;
|
|
22
17
|
var elements = getFieldElements(form, name);
|
|
18
|
+
if (elements.length > 1) {
|
|
19
|
+
var options = typeof value === 'string' ? [value] : value;
|
|
20
|
+
for (var element of elements) {
|
|
21
|
+
if (typeof options !== 'undefined' && element instanceof HTMLInputElement && element.type === 'checkbox' && (element.checked ? options.includes(element.value) : !options.includes(element.value))) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
return element;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
23
27
|
return (_elements$ = elements[0]) !== null && _elements$ !== void 0 ? _elements$ : null;
|
|
24
28
|
}
|
|
25
29
|
function createDummySelect(form, name, value) {
|
|
@@ -52,7 +56,7 @@ function isDummySelect(element) {
|
|
|
52
56
|
}
|
|
53
57
|
function updateFieldValue(element, value) {
|
|
54
58
|
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
|
|
55
|
-
element.checked = element.value === value;
|
|
59
|
+
element.checked = Array.isArray(value) ? value.includes(element.value) : element.value === value;
|
|
56
60
|
} else if (element instanceof HTMLSelectElement && element.multiple) {
|
|
57
61
|
var selectedValue = Array.isArray(value) ? [...value] : [value];
|
|
58
62
|
for (var option of element.options) {
|
|
@@ -100,44 +104,18 @@ function updateFieldValue(element, value) {
|
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
// as there is no way to skip an entry in a multiple select when they all share the same name
|
|
106
|
-
var inputInitialValue = metaOrOptions.initialValue;
|
|
107
|
+
function useInputEvent() {
|
|
108
|
+
var ref = react.useRef(null);
|
|
107
109
|
var eventDispatched = react.useRef({
|
|
108
110
|
change: false,
|
|
109
111
|
focus: false,
|
|
110
112
|
blur: false
|
|
111
113
|
});
|
|
112
|
-
var [key, setKey] = react.useState(metaOrOptions.key);
|
|
113
|
-
var [initialValue, setInitialValue] = react.useState(inputInitialValue);
|
|
114
|
-
var [value, setValue] = react.useState(inputInitialValue);
|
|
115
|
-
if (key !== metaOrOptions.key) {
|
|
116
|
-
setValue(inputInitialValue);
|
|
117
|
-
setInitialValue(inputInitialValue);
|
|
118
|
-
setKey(metaOrOptions.key);
|
|
119
|
-
}
|
|
120
|
-
react.useEffect(() => {
|
|
121
|
-
var form = getFormElement(metaOrOptions.formId);
|
|
122
|
-
if (getEventTarget(form, metaOrOptions.name)) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
createDummySelect(form, metaOrOptions.name, initialValue);
|
|
126
|
-
return () => {
|
|
127
|
-
var elements = getFieldElements(form, metaOrOptions.name);
|
|
128
|
-
for (var element of elements) {
|
|
129
|
-
if (isDummySelect(element)) {
|
|
130
|
-
element.remove();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
}, [metaOrOptions.formId, metaOrOptions.name, initialValue]);
|
|
135
114
|
react.useEffect(() => {
|
|
136
115
|
var createEventListener = listener => {
|
|
137
116
|
return event => {
|
|
138
|
-
var
|
|
139
|
-
|
|
140
|
-
if ((element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) && element.name === metaOrOptions.name && ((_element$form = element.form) === null || _element$form === void 0 ? void 0 : _element$form.id) === metaOrOptions.formId) {
|
|
117
|
+
var element = ref.current;
|
|
118
|
+
if (element && event.target === element) {
|
|
141
119
|
eventDispatched.current[listener] = true;
|
|
142
120
|
}
|
|
143
121
|
};
|
|
@@ -153,14 +131,13 @@ function useInputControl(metaOrOptions) {
|
|
|
153
131
|
document.removeEventListener('focusin', focusHandler, true);
|
|
154
132
|
document.removeEventListener('focusout', blurHandler, true);
|
|
155
133
|
};
|
|
156
|
-
}, [
|
|
157
|
-
|
|
134
|
+
}, [ref]);
|
|
135
|
+
return react.useMemo(() => {
|
|
158
136
|
return {
|
|
159
137
|
change(value) {
|
|
160
138
|
if (!eventDispatched.current.change) {
|
|
161
139
|
eventDispatched.current.change = true;
|
|
162
|
-
var
|
|
163
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
140
|
+
var element = ref.current;
|
|
164
141
|
if (element) {
|
|
165
142
|
updateFieldValue(element, value);
|
|
166
143
|
|
|
@@ -174,14 +151,12 @@ function useInputControl(metaOrOptions) {
|
|
|
174
151
|
}));
|
|
175
152
|
}
|
|
176
153
|
}
|
|
177
|
-
setValue(value);
|
|
178
154
|
eventDispatched.current.change = false;
|
|
179
155
|
},
|
|
180
156
|
focus() {
|
|
181
157
|
if (!eventDispatched.current.focus) {
|
|
182
158
|
eventDispatched.current.focus = true;
|
|
183
|
-
var
|
|
184
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
159
|
+
var element = ref.current;
|
|
185
160
|
if (element) {
|
|
186
161
|
element.dispatchEvent(new FocusEvent('focusin', {
|
|
187
162
|
bubbles: true
|
|
@@ -194,8 +169,7 @@ function useInputControl(metaOrOptions) {
|
|
|
194
169
|
blur() {
|
|
195
170
|
if (!eventDispatched.current.blur) {
|
|
196
171
|
eventDispatched.current.blur = true;
|
|
197
|
-
var
|
|
198
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
172
|
+
var element = ref.current;
|
|
199
173
|
if (element) {
|
|
200
174
|
element.dispatchEvent(new FocusEvent('focusout', {
|
|
201
175
|
bubbles: true
|
|
@@ -204,18 +178,108 @@ function useInputControl(metaOrOptions) {
|
|
|
204
178
|
}
|
|
205
179
|
}
|
|
206
180
|
eventDispatched.current.blur = false;
|
|
181
|
+
},
|
|
182
|
+
register(element) {
|
|
183
|
+
ref.current = element;
|
|
207
184
|
}
|
|
208
185
|
};
|
|
209
|
-
}, [
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
186
|
+
}, []);
|
|
187
|
+
}
|
|
188
|
+
function useInputValue(options) {
|
|
189
|
+
var initializeValue = () => {
|
|
190
|
+
var _options$initialValue;
|
|
191
|
+
if (typeof options.initialValue === 'string') {
|
|
192
|
+
// @ts-expect-error FIXME: To ensure that the type of value is also `string | undefined` if initialValue is not an array
|
|
193
|
+
return options.initialValue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// @ts-expect-error Same as above
|
|
197
|
+
return (_options$initialValue = options.initialValue) === null || _options$initialValue === void 0 ? void 0 : _options$initialValue.map(value => value !== null && value !== void 0 ? value : '');
|
|
198
|
+
};
|
|
199
|
+
var [key, setKey] = react.useState(options.key);
|
|
200
|
+
var [value, setValue] = react.useState(initializeValue);
|
|
201
|
+
if (key !== options.key) {
|
|
202
|
+
setValue(initializeValue);
|
|
203
|
+
setKey(options.key);
|
|
204
|
+
}
|
|
205
|
+
return [value, setValue];
|
|
206
|
+
}
|
|
207
|
+
function useControl(meta) {
|
|
208
|
+
var [value, setValue] = useInputValue(meta);
|
|
209
|
+
var {
|
|
210
|
+
register,
|
|
211
|
+
change,
|
|
212
|
+
focus,
|
|
213
|
+
blur
|
|
214
|
+
} = useInputEvent();
|
|
215
|
+
var handleChange = value => {
|
|
216
|
+
setValue(value);
|
|
217
|
+
change(value);
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
register,
|
|
221
|
+
value,
|
|
222
|
+
change: handleChange,
|
|
223
|
+
focus,
|
|
224
|
+
blur
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function useInputControl(meta) {
|
|
228
|
+
var [value, setValue] = useInputValue(meta);
|
|
229
|
+
var initializedRef = react.useRef(false);
|
|
230
|
+
var {
|
|
231
|
+
register,
|
|
232
|
+
change,
|
|
233
|
+
focus,
|
|
234
|
+
blur
|
|
235
|
+
} = useInputEvent();
|
|
236
|
+
react.useEffect(() => {
|
|
237
|
+
var form = getFormElement(meta.formId);
|
|
238
|
+
if (!form) {
|
|
239
|
+
// eslint-disable-next-line no-console
|
|
240
|
+
console.warn("useInputControl is unable to find form#".concat(meta.formId, " and identify if a dummy input is required"));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
var element = getEventTarget(form, meta.name);
|
|
244
|
+
if (!element && typeof value !== 'undefined' && (!Array.isArray(value) || value.length > 0)) {
|
|
245
|
+
element = createDummySelect(form, meta.name, value);
|
|
246
|
+
}
|
|
247
|
+
register(element);
|
|
248
|
+
if (!initializedRef.current) {
|
|
249
|
+
initializedRef.current = true;
|
|
250
|
+
} else {
|
|
251
|
+
change(value !== null && value !== void 0 ? value : '');
|
|
252
|
+
}
|
|
253
|
+
return () => {
|
|
254
|
+
register(null);
|
|
255
|
+
var elements = getFieldElements(form, meta.name);
|
|
256
|
+
for (var _element of elements) {
|
|
257
|
+
if (isDummySelect(_element)) {
|
|
258
|
+
_element.remove();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}, [meta.formId, meta.name, value, change, register]);
|
|
263
|
+
return {
|
|
264
|
+
value,
|
|
265
|
+
change: setValue,
|
|
266
|
+
focus,
|
|
267
|
+
blur
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function Control(props) {
|
|
271
|
+
var control = useControl(props.meta);
|
|
272
|
+
return props.render(control);
|
|
213
273
|
}
|
|
214
274
|
|
|
275
|
+
exports.Control = Control;
|
|
215
276
|
exports.createDummySelect = createDummySelect;
|
|
216
277
|
exports.getEventTarget = getEventTarget;
|
|
217
278
|
exports.getFieldElements = getFieldElements;
|
|
218
279
|
exports.getFormElement = getFormElement;
|
|
219
280
|
exports.isDummySelect = isDummySelect;
|
|
220
281
|
exports.updateFieldValue = updateFieldValue;
|
|
282
|
+
exports.useControl = useControl;
|
|
221
283
|
exports.useInputControl = useInputControl;
|
|
284
|
+
exports.useInputEvent = useInputEvent;
|
|
285
|
+
exports.useInputValue = useInputValue;
|
package/integrations.mjs
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useRef, useState, useEffect, useMemo } from 'react';
|
|
1
|
+
import { useRef, useEffect, useMemo, useState } from 'react';
|
|
3
2
|
|
|
4
3
|
function getFormElement(formId) {
|
|
5
|
-
|
|
6
|
-
if (!element) {
|
|
7
|
-
throw new Error('Form not found');
|
|
8
|
-
}
|
|
9
|
-
return element;
|
|
4
|
+
return document.forms.namedItem(formId);
|
|
10
5
|
}
|
|
11
6
|
function getFieldElements(form, name) {
|
|
12
|
-
var field = form.elements.namedItem(name);
|
|
7
|
+
var field = form === null || form === void 0 ? void 0 : form.elements.namedItem(name);
|
|
13
8
|
var elements = !field ? [] : field instanceof Element ? [field] : Array.from(field.values());
|
|
14
9
|
return elements.filter(element => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement);
|
|
15
10
|
}
|
|
16
|
-
function getEventTarget(form, name) {
|
|
11
|
+
function getEventTarget(form, name, value) {
|
|
17
12
|
var _elements$;
|
|
18
13
|
var elements = getFieldElements(form, name);
|
|
14
|
+
if (elements.length > 1) {
|
|
15
|
+
var options = typeof value === 'string' ? [value] : value;
|
|
16
|
+
for (var element of elements) {
|
|
17
|
+
if (typeof options !== 'undefined' && element instanceof HTMLInputElement && element.type === 'checkbox' && (element.checked ? options.includes(element.value) : !options.includes(element.value))) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
return element;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
19
23
|
return (_elements$ = elements[0]) !== null && _elements$ !== void 0 ? _elements$ : null;
|
|
20
24
|
}
|
|
21
25
|
function createDummySelect(form, name, value) {
|
|
@@ -48,7 +52,7 @@ function isDummySelect(element) {
|
|
|
48
52
|
}
|
|
49
53
|
function updateFieldValue(element, value) {
|
|
50
54
|
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
|
|
51
|
-
element.checked = element.value === value;
|
|
55
|
+
element.checked = Array.isArray(value) ? value.includes(element.value) : element.value === value;
|
|
52
56
|
} else if (element instanceof HTMLSelectElement && element.multiple) {
|
|
53
57
|
var selectedValue = Array.isArray(value) ? [...value] : [value];
|
|
54
58
|
for (var option of element.options) {
|
|
@@ -96,44 +100,18 @@ function updateFieldValue(element, value) {
|
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
// as there is no way to skip an entry in a multiple select when they all share the same name
|
|
102
|
-
var inputInitialValue = metaOrOptions.initialValue;
|
|
103
|
+
function useInputEvent() {
|
|
104
|
+
var ref = useRef(null);
|
|
103
105
|
var eventDispatched = useRef({
|
|
104
106
|
change: false,
|
|
105
107
|
focus: false,
|
|
106
108
|
blur: false
|
|
107
109
|
});
|
|
108
|
-
var [key, setKey] = useState(metaOrOptions.key);
|
|
109
|
-
var [initialValue, setInitialValue] = useState(inputInitialValue);
|
|
110
|
-
var [value, setValue] = useState(inputInitialValue);
|
|
111
|
-
if (key !== metaOrOptions.key) {
|
|
112
|
-
setValue(inputInitialValue);
|
|
113
|
-
setInitialValue(inputInitialValue);
|
|
114
|
-
setKey(metaOrOptions.key);
|
|
115
|
-
}
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
var form = getFormElement(metaOrOptions.formId);
|
|
118
|
-
if (getEventTarget(form, metaOrOptions.name)) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
createDummySelect(form, metaOrOptions.name, initialValue);
|
|
122
|
-
return () => {
|
|
123
|
-
var elements = getFieldElements(form, metaOrOptions.name);
|
|
124
|
-
for (var element of elements) {
|
|
125
|
-
if (isDummySelect(element)) {
|
|
126
|
-
element.remove();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}, [metaOrOptions.formId, metaOrOptions.name, initialValue]);
|
|
131
110
|
useEffect(() => {
|
|
132
111
|
var createEventListener = listener => {
|
|
133
112
|
return event => {
|
|
134
|
-
var
|
|
135
|
-
|
|
136
|
-
if ((element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) && element.name === metaOrOptions.name && ((_element$form = element.form) === null || _element$form === void 0 ? void 0 : _element$form.id) === metaOrOptions.formId) {
|
|
113
|
+
var element = ref.current;
|
|
114
|
+
if (element && event.target === element) {
|
|
137
115
|
eventDispatched.current[listener] = true;
|
|
138
116
|
}
|
|
139
117
|
};
|
|
@@ -149,14 +127,13 @@ function useInputControl(metaOrOptions) {
|
|
|
149
127
|
document.removeEventListener('focusin', focusHandler, true);
|
|
150
128
|
document.removeEventListener('focusout', blurHandler, true);
|
|
151
129
|
};
|
|
152
|
-
}, [
|
|
153
|
-
|
|
130
|
+
}, [ref]);
|
|
131
|
+
return useMemo(() => {
|
|
154
132
|
return {
|
|
155
133
|
change(value) {
|
|
156
134
|
if (!eventDispatched.current.change) {
|
|
157
135
|
eventDispatched.current.change = true;
|
|
158
|
-
var
|
|
159
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
136
|
+
var element = ref.current;
|
|
160
137
|
if (element) {
|
|
161
138
|
updateFieldValue(element, value);
|
|
162
139
|
|
|
@@ -170,14 +147,12 @@ function useInputControl(metaOrOptions) {
|
|
|
170
147
|
}));
|
|
171
148
|
}
|
|
172
149
|
}
|
|
173
|
-
setValue(value);
|
|
174
150
|
eventDispatched.current.change = false;
|
|
175
151
|
},
|
|
176
152
|
focus() {
|
|
177
153
|
if (!eventDispatched.current.focus) {
|
|
178
154
|
eventDispatched.current.focus = true;
|
|
179
|
-
var
|
|
180
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
155
|
+
var element = ref.current;
|
|
181
156
|
if (element) {
|
|
182
157
|
element.dispatchEvent(new FocusEvent('focusin', {
|
|
183
158
|
bubbles: true
|
|
@@ -190,8 +165,7 @@ function useInputControl(metaOrOptions) {
|
|
|
190
165
|
blur() {
|
|
191
166
|
if (!eventDispatched.current.blur) {
|
|
192
167
|
eventDispatched.current.blur = true;
|
|
193
|
-
var
|
|
194
|
-
var element = getEventTarget(form, metaOrOptions.name);
|
|
168
|
+
var element = ref.current;
|
|
195
169
|
if (element) {
|
|
196
170
|
element.dispatchEvent(new FocusEvent('focusout', {
|
|
197
171
|
bubbles: true
|
|
@@ -200,12 +174,98 @@ function useInputControl(metaOrOptions) {
|
|
|
200
174
|
}
|
|
201
175
|
}
|
|
202
176
|
eventDispatched.current.blur = false;
|
|
177
|
+
},
|
|
178
|
+
register(element) {
|
|
179
|
+
ref.current = element;
|
|
203
180
|
}
|
|
204
181
|
};
|
|
205
|
-
}, [
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
182
|
+
}, []);
|
|
183
|
+
}
|
|
184
|
+
function useInputValue(options) {
|
|
185
|
+
var initializeValue = () => {
|
|
186
|
+
var _options$initialValue;
|
|
187
|
+
if (typeof options.initialValue === 'string') {
|
|
188
|
+
// @ts-expect-error FIXME: To ensure that the type of value is also `string | undefined` if initialValue is not an array
|
|
189
|
+
return options.initialValue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// @ts-expect-error Same as above
|
|
193
|
+
return (_options$initialValue = options.initialValue) === null || _options$initialValue === void 0 ? void 0 : _options$initialValue.map(value => value !== null && value !== void 0 ? value : '');
|
|
194
|
+
};
|
|
195
|
+
var [key, setKey] = useState(options.key);
|
|
196
|
+
var [value, setValue] = useState(initializeValue);
|
|
197
|
+
if (key !== options.key) {
|
|
198
|
+
setValue(initializeValue);
|
|
199
|
+
setKey(options.key);
|
|
200
|
+
}
|
|
201
|
+
return [value, setValue];
|
|
202
|
+
}
|
|
203
|
+
function useControl(meta) {
|
|
204
|
+
var [value, setValue] = useInputValue(meta);
|
|
205
|
+
var {
|
|
206
|
+
register,
|
|
207
|
+
change,
|
|
208
|
+
focus,
|
|
209
|
+
blur
|
|
210
|
+
} = useInputEvent();
|
|
211
|
+
var handleChange = value => {
|
|
212
|
+
setValue(value);
|
|
213
|
+
change(value);
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
register,
|
|
217
|
+
value,
|
|
218
|
+
change: handleChange,
|
|
219
|
+
focus,
|
|
220
|
+
blur
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function useInputControl(meta) {
|
|
224
|
+
var [value, setValue] = useInputValue(meta);
|
|
225
|
+
var initializedRef = useRef(false);
|
|
226
|
+
var {
|
|
227
|
+
register,
|
|
228
|
+
change,
|
|
229
|
+
focus,
|
|
230
|
+
blur
|
|
231
|
+
} = useInputEvent();
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
var form = getFormElement(meta.formId);
|
|
234
|
+
if (!form) {
|
|
235
|
+
// eslint-disable-next-line no-console
|
|
236
|
+
console.warn("useInputControl is unable to find form#".concat(meta.formId, " and identify if a dummy input is required"));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
var element = getEventTarget(form, meta.name);
|
|
240
|
+
if (!element && typeof value !== 'undefined' && (!Array.isArray(value) || value.length > 0)) {
|
|
241
|
+
element = createDummySelect(form, meta.name, value);
|
|
242
|
+
}
|
|
243
|
+
register(element);
|
|
244
|
+
if (!initializedRef.current) {
|
|
245
|
+
initializedRef.current = true;
|
|
246
|
+
} else {
|
|
247
|
+
change(value !== null && value !== void 0 ? value : '');
|
|
248
|
+
}
|
|
249
|
+
return () => {
|
|
250
|
+
register(null);
|
|
251
|
+
var elements = getFieldElements(form, meta.name);
|
|
252
|
+
for (var _element of elements) {
|
|
253
|
+
if (isDummySelect(_element)) {
|
|
254
|
+
_element.remove();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}, [meta.formId, meta.name, value, change, register]);
|
|
259
|
+
return {
|
|
260
|
+
value,
|
|
261
|
+
change: setValue,
|
|
262
|
+
focus,
|
|
263
|
+
blur
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function Control(props) {
|
|
267
|
+
var control = useControl(props.meta);
|
|
268
|
+
return props.render(control);
|
|
209
269
|
}
|
|
210
270
|
|
|
211
|
-
export { createDummySelect, getEventTarget, getFieldElements, getFormElement, isDummySelect, updateFieldValue, useInputControl };
|
|
271
|
+
export { Control, createDummySelect, getEventTarget, getFieldElements, getFormElement, isDummySelect, updateFieldValue, useControl, useInputControl, useInputEvent, useInputValue };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Conform view adapter for react",
|
|
4
4
|
"homepage": "https://conform.guide",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.2",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"module": "index.mjs",
|
|
9
9
|
"types": "index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"url": "https://github.com/edmundhung/conform/issues"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@conform-to/dom": "1.0.
|
|
33
|
+
"@conform-to/dom": "1.0.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "^18.2.43",
|