@conform-to/react 1.0.0 → 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 +16 -11
- package/context.d.ts +13 -10
- 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 -10
- package/integrations.js +224 -85
- package/integrations.mjs +217 -86
- package/package.json +2 -2
package/README
CHANGED
|
@@ -8,25 +8,30 @@
|
|
|
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
|
|
|
15
|
-
|
|
15
|
+
# Getting Started
|
|
16
16
|
|
|
17
17
|
Check out the overview and tutorial at our website https://conform.guide
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Features
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
- Progressive enhancement first APIs
|
|
22
|
+
- Type-safe field inference
|
|
23
|
+
- Fine-grained subscription
|
|
24
|
+
- Built-in accessibility helpers
|
|
25
|
+
- Automatic type coercion with Zod
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
* Examples: https://conform.guide/examples
|
|
25
|
-
* Complex structures: https://conform.guide/complex-structures
|
|
26
|
-
* UI Integrations: https://conform.guide/integrations
|
|
27
|
-
* Accessibility Guide: https://conform.guide/accessibility
|
|
28
|
-
* API Reference: https://conform.guide/references
|
|
27
|
+
# Documentation
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
- Validation: https://conform.guide/validation
|
|
30
|
+
- Nested object and Array: https://conform.guide/complex-structures
|
|
31
|
+
- UI Integrations: https://conform.guide/integration/ui-libraries
|
|
32
|
+
- Intent button: https://conform.guide/intent-button
|
|
33
|
+
- Accessibility Guide: https://conform.guide/accessibility
|
|
34
|
+
|
|
35
|
+
# Support
|
|
31
36
|
|
|
32
37
|
To report a bug, please open an issue on the repository at https://github.com/edmundhung/conform. For feature requests and questions, you can post them in the Discussions section.
|
package/context.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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];
|
|
5
5
|
} & {};
|
|
6
|
-
export type Primitive = string | number | boolean | Date | File | null | undefined;
|
|
6
|
+
export type Primitive = string | number | bigint | boolean | Date | File | null | undefined;
|
|
7
7
|
export type Metadata<Schema, FormSchema extends Record<string, unknown>, FormError = string[]> = {
|
|
8
8
|
key: string | undefined;
|
|
9
9
|
id: string;
|
|
@@ -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,17 +1,45 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
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;
|
|
5
|
+
export declare function createDummySelect(form: HTMLFormElement, name: string, value?: string | string[] | undefined): HTMLSelectElement;
|
|
6
|
+
export declare function isDummySelect(element: HTMLElement): element is HTMLSelectElement;
|
|
7
|
+
export declare function updateFieldValue(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, value: string | string[]): void;
|
|
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;
|
|
6
25
|
focus: () => void;
|
|
7
26
|
blur: () => void;
|
|
8
27
|
};
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function getEventTarget(formId: string, name: string): FieldElement;
|
|
11
|
-
export type InputControlOptions = {
|
|
28
|
+
export declare function useInputControl<Value extends string | string[] | Array<string | undefined>>(meta: {
|
|
12
29
|
key?: Key | null | undefined;
|
|
13
30
|
name: string;
|
|
14
31
|
formId: string;
|
|
15
|
-
initialValue?:
|
|
32
|
+
initialValue?: Value | undefined;
|
|
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;
|
|
16
38
|
};
|
|
17
|
-
export declare function
|
|
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,53 +2,120 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js');
|
|
6
|
-
var dom = require('@conform-to/dom');
|
|
7
5
|
var react = require('react');
|
|
8
6
|
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
function getFormElement(formId) {
|
|
8
|
+
return document.forms.namedItem(formId);
|
|
9
|
+
}
|
|
10
|
+
function getFieldElements(form, name) {
|
|
11
|
+
var field = form === null || form === void 0 ? void 0 : form.elements.namedItem(name);
|
|
12
|
+
var elements = !field ? [] : field instanceof Element ? [field] : Array.from(field.values());
|
|
13
|
+
return elements.filter(element => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement);
|
|
14
|
+
}
|
|
15
|
+
function getEventTarget(form, name, value) {
|
|
16
|
+
var _elements$;
|
|
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;
|
|
18
23
|
}
|
|
24
|
+
return element;
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
|
-
return null;
|
|
27
|
+
return (_elements$ = elements[0]) !== null && _elements$ !== void 0 ? _elements$ : null;
|
|
28
|
+
}
|
|
29
|
+
function createDummySelect(form, name, value) {
|
|
30
|
+
var select = document.createElement('select');
|
|
31
|
+
var options = typeof value === 'string' ? [value] : value !== null && value !== void 0 ? value : [];
|
|
32
|
+
select.name = name;
|
|
33
|
+
select.multiple = true;
|
|
34
|
+
select.dataset.conform = 'true';
|
|
35
|
+
|
|
36
|
+
// To make sure the input is hidden but still focusable
|
|
37
|
+
select.setAttribute('aria-hidden', 'true');
|
|
38
|
+
select.tabIndex = -1;
|
|
39
|
+
select.style.position = 'absolute';
|
|
40
|
+
select.style.width = '1px';
|
|
41
|
+
select.style.height = '1px';
|
|
42
|
+
select.style.padding = '0';
|
|
43
|
+
select.style.margin = '-1px';
|
|
44
|
+
select.style.overflow = 'hidden';
|
|
45
|
+
select.style.clip = 'rect(0,0,0,0)';
|
|
46
|
+
select.style.whiteSpace = 'nowrap';
|
|
47
|
+
select.style.border = '0';
|
|
48
|
+
for (var option of options) {
|
|
49
|
+
select.options.add(new Option(option, option, true, true));
|
|
50
|
+
}
|
|
51
|
+
form.appendChild(select);
|
|
52
|
+
return select;
|
|
22
53
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
54
|
+
function isDummySelect(element) {
|
|
55
|
+
return element.dataset.conform === 'true';
|
|
56
|
+
}
|
|
57
|
+
function updateFieldValue(element, value) {
|
|
58
|
+
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
|
|
59
|
+
element.checked = Array.isArray(value) ? value.includes(element.value) : element.value === value;
|
|
60
|
+
} else if (element instanceof HTMLSelectElement && element.multiple) {
|
|
61
|
+
var selectedValue = Array.isArray(value) ? [...value] : [value];
|
|
62
|
+
for (var option of element.options) {
|
|
63
|
+
var index = selectedValue.indexOf(option.value);
|
|
64
|
+
var selected = index > -1;
|
|
65
|
+
|
|
66
|
+
// Update the selected state of the option
|
|
67
|
+
option.selected = selected;
|
|
68
|
+
// Remove the option from the selected array
|
|
69
|
+
if (selected) {
|
|
70
|
+
selectedValue.splice(index, 1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add the remaining options to the select element only if it's a dummy element managed by conform
|
|
75
|
+
if (isDummySelect(element)) {
|
|
76
|
+
for (var _option of selectedValue) {
|
|
77
|
+
element.options.add(new Option(_option, _option, false, true));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} else if (element.value !== value) {
|
|
81
|
+
// No `change` event will be triggered on React if `element.value` is already updated
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Triggering react custom change event
|
|
85
|
+
* Solution based on dom-testing-library
|
|
86
|
+
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
87
|
+
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
88
|
+
*/
|
|
89
|
+
var {
|
|
90
|
+
set: valueSetter
|
|
91
|
+
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
|
|
92
|
+
var prototype = Object.getPrototypeOf(element);
|
|
93
|
+
var {
|
|
94
|
+
set: prototypeValueSetter
|
|
95
|
+
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
96
|
+
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
97
|
+
prototypeValueSetter.call(element, value);
|
|
98
|
+
} else {
|
|
99
|
+
if (valueSetter) {
|
|
100
|
+
valueSetter.call(element, value);
|
|
101
|
+
} else {
|
|
102
|
+
throw new Error('The given element does not have a value setter');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
27
105
|
}
|
|
28
|
-
var form = document.forms.namedItem(formId);
|
|
29
|
-
var input = document.createElement('input');
|
|
30
|
-
input.type = 'hidden';
|
|
31
|
-
input.name = name;
|
|
32
|
-
form === null || form === void 0 || form.appendChild(input);
|
|
33
|
-
return input;
|
|
34
106
|
}
|
|
35
|
-
function
|
|
107
|
+
function useInputEvent() {
|
|
108
|
+
var ref = react.useRef(null);
|
|
36
109
|
var eventDispatched = react.useRef({
|
|
37
110
|
change: false,
|
|
38
111
|
focus: false,
|
|
39
112
|
blur: false
|
|
40
113
|
});
|
|
41
|
-
var [key, setKey] = react.useState(metaOrOptions.key);
|
|
42
|
-
var [value, setValue] = react.useState(() => metaOrOptions.initialValue);
|
|
43
|
-
if (key !== metaOrOptions.key) {
|
|
44
|
-
setValue(metaOrOptions.initialValue);
|
|
45
|
-
setKey(metaOrOptions.key);
|
|
46
|
-
}
|
|
47
114
|
react.useEffect(() => {
|
|
48
115
|
var createEventListener = listener => {
|
|
49
116
|
return event => {
|
|
50
|
-
var element =
|
|
51
|
-
if (element) {
|
|
117
|
+
var element = ref.current;
|
|
118
|
+
if (element && event.target === element) {
|
|
52
119
|
eventDispatched.current[listener] = true;
|
|
53
120
|
}
|
|
54
121
|
};
|
|
@@ -64,83 +131,155 @@ function useInputControl(metaOrOptions) {
|
|
|
64
131
|
document.removeEventListener('focusin', focusHandler, true);
|
|
65
132
|
document.removeEventListener('focusout', blurHandler, true);
|
|
66
133
|
};
|
|
67
|
-
}, [
|
|
68
|
-
|
|
134
|
+
}, [ref]);
|
|
135
|
+
return react.useMemo(() => {
|
|
69
136
|
return {
|
|
70
137
|
change(value) {
|
|
71
138
|
if (!eventDispatched.current.change) {
|
|
72
|
-
var _element = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
73
139
|
eventDispatched.current.change = true;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// No change event will be triggered on React if `element.value` is already updated
|
|
140
|
+
var element = ref.current;
|
|
141
|
+
if (element) {
|
|
142
|
+
updateFieldValue(element, value);
|
|
78
143
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
} = Object.getOwnPropertyDescriptor(_element, 'value') || {};
|
|
88
|
-
var prototype = Object.getPrototypeOf(_element);
|
|
89
|
-
var {
|
|
90
|
-
set: prototypeValueSetter
|
|
91
|
-
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
92
|
-
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
93
|
-
prototypeValueSetter.call(_element, value);
|
|
94
|
-
} else {
|
|
95
|
-
if (valueSetter) {
|
|
96
|
-
valueSetter.call(_element, value);
|
|
97
|
-
} else {
|
|
98
|
-
throw new Error('The given element does not have a value setter');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
144
|
+
// Dispatch input event with the updated input value
|
|
145
|
+
element.dispatchEvent(new InputEvent('input', {
|
|
146
|
+
bubbles: true
|
|
147
|
+
}));
|
|
148
|
+
// Dispatch change event (necessary for select to update the selected option)
|
|
149
|
+
element.dispatchEvent(new Event('change', {
|
|
150
|
+
bubbles: true
|
|
151
|
+
}));
|
|
101
152
|
}
|
|
102
|
-
|
|
103
|
-
// Dispatch input event with the updated input value
|
|
104
|
-
_element.dispatchEvent(new InputEvent('input', {
|
|
105
|
-
bubbles: true
|
|
106
|
-
}));
|
|
107
|
-
// Dispatch change event (necessary for select to update the selected option)
|
|
108
|
-
_element.dispatchEvent(new Event('change', {
|
|
109
|
-
bubbles: true
|
|
110
|
-
}));
|
|
111
153
|
}
|
|
112
|
-
setValue(value);
|
|
113
154
|
eventDispatched.current.change = false;
|
|
114
155
|
},
|
|
115
156
|
focus() {
|
|
116
157
|
if (!eventDispatched.current.focus) {
|
|
117
|
-
var _element2 = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
118
158
|
eventDispatched.current.focus = true;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
159
|
+
var element = ref.current;
|
|
160
|
+
if (element) {
|
|
161
|
+
element.dispatchEvent(new FocusEvent('focusin', {
|
|
162
|
+
bubbles: true
|
|
163
|
+
}));
|
|
164
|
+
element.dispatchEvent(new FocusEvent('focus'));
|
|
165
|
+
}
|
|
123
166
|
}
|
|
124
167
|
eventDispatched.current.focus = false;
|
|
125
168
|
},
|
|
126
169
|
blur() {
|
|
127
170
|
if (!eventDispatched.current.blur) {
|
|
128
|
-
var _element3 = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
129
171
|
eventDispatched.current.blur = true;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
172
|
+
var element = ref.current;
|
|
173
|
+
if (element) {
|
|
174
|
+
element.dispatchEvent(new FocusEvent('focusout', {
|
|
175
|
+
bubbles: true
|
|
176
|
+
}));
|
|
177
|
+
element.dispatchEvent(new FocusEvent('blur'));
|
|
178
|
+
}
|
|
134
179
|
}
|
|
135
180
|
eventDispatched.current.blur = false;
|
|
181
|
+
},
|
|
182
|
+
register(element) {
|
|
183
|
+
ref.current = element;
|
|
136
184
|
}
|
|
137
185
|
};
|
|
138
|
-
}, [
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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);
|
|
142
273
|
}
|
|
143
274
|
|
|
275
|
+
exports.Control = Control;
|
|
276
|
+
exports.createDummySelect = createDummySelect;
|
|
144
277
|
exports.getEventTarget = getEventTarget;
|
|
145
|
-
exports.
|
|
278
|
+
exports.getFieldElements = getFieldElements;
|
|
279
|
+
exports.getFormElement = getFormElement;
|
|
280
|
+
exports.isDummySelect = isDummySelect;
|
|
281
|
+
exports.updateFieldValue = updateFieldValue;
|
|
282
|
+
exports.useControl = useControl;
|
|
146
283
|
exports.useInputControl = useInputControl;
|
|
284
|
+
exports.useInputEvent = useInputEvent;
|
|
285
|
+
exports.useInputValue = useInputValue;
|
package/integrations.mjs
CHANGED
|
@@ -1,50 +1,117 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isFieldElement } from '@conform-to/dom';
|
|
3
|
-
import { useRef, useState, useEffect, useMemo } from 'react';
|
|
1
|
+
import { useRef, useEffect, useMemo, useState } from 'react';
|
|
4
2
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
function getFormElement(formId) {
|
|
4
|
+
return document.forms.namedItem(formId);
|
|
5
|
+
}
|
|
6
|
+
function getFieldElements(form, name) {
|
|
7
|
+
var field = form === null || form === void 0 ? void 0 : form.elements.namedItem(name);
|
|
8
|
+
var elements = !field ? [] : field instanceof Element ? [field] : Array.from(field.values());
|
|
9
|
+
return elements.filter(element => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement);
|
|
10
|
+
}
|
|
11
|
+
function getEventTarget(form, name, value) {
|
|
12
|
+
var _elements$;
|
|
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;
|
|
14
19
|
}
|
|
20
|
+
return element;
|
|
15
21
|
}
|
|
16
22
|
}
|
|
17
|
-
return null;
|
|
23
|
+
return (_elements$ = elements[0]) !== null && _elements$ !== void 0 ? _elements$ : null;
|
|
24
|
+
}
|
|
25
|
+
function createDummySelect(form, name, value) {
|
|
26
|
+
var select = document.createElement('select');
|
|
27
|
+
var options = typeof value === 'string' ? [value] : value !== null && value !== void 0 ? value : [];
|
|
28
|
+
select.name = name;
|
|
29
|
+
select.multiple = true;
|
|
30
|
+
select.dataset.conform = 'true';
|
|
31
|
+
|
|
32
|
+
// To make sure the input is hidden but still focusable
|
|
33
|
+
select.setAttribute('aria-hidden', 'true');
|
|
34
|
+
select.tabIndex = -1;
|
|
35
|
+
select.style.position = 'absolute';
|
|
36
|
+
select.style.width = '1px';
|
|
37
|
+
select.style.height = '1px';
|
|
38
|
+
select.style.padding = '0';
|
|
39
|
+
select.style.margin = '-1px';
|
|
40
|
+
select.style.overflow = 'hidden';
|
|
41
|
+
select.style.clip = 'rect(0,0,0,0)';
|
|
42
|
+
select.style.whiteSpace = 'nowrap';
|
|
43
|
+
select.style.border = '0';
|
|
44
|
+
for (var option of options) {
|
|
45
|
+
select.options.add(new Option(option, option, true, true));
|
|
46
|
+
}
|
|
47
|
+
form.appendChild(select);
|
|
48
|
+
return select;
|
|
18
49
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
50
|
+
function isDummySelect(element) {
|
|
51
|
+
return element.dataset.conform === 'true';
|
|
52
|
+
}
|
|
53
|
+
function updateFieldValue(element, value) {
|
|
54
|
+
if (element instanceof HTMLInputElement && (element.type === 'checkbox' || element.type === 'radio')) {
|
|
55
|
+
element.checked = Array.isArray(value) ? value.includes(element.value) : element.value === value;
|
|
56
|
+
} else if (element instanceof HTMLSelectElement && element.multiple) {
|
|
57
|
+
var selectedValue = Array.isArray(value) ? [...value] : [value];
|
|
58
|
+
for (var option of element.options) {
|
|
59
|
+
var index = selectedValue.indexOf(option.value);
|
|
60
|
+
var selected = index > -1;
|
|
61
|
+
|
|
62
|
+
// Update the selected state of the option
|
|
63
|
+
option.selected = selected;
|
|
64
|
+
// Remove the option from the selected array
|
|
65
|
+
if (selected) {
|
|
66
|
+
selectedValue.splice(index, 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Add the remaining options to the select element only if it's a dummy element managed by conform
|
|
71
|
+
if (isDummySelect(element)) {
|
|
72
|
+
for (var _option of selectedValue) {
|
|
73
|
+
element.options.add(new Option(_option, _option, false, true));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} else if (element.value !== value) {
|
|
77
|
+
// No `change` event will be triggered on React if `element.value` is already updated
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Triggering react custom change event
|
|
81
|
+
* Solution based on dom-testing-library
|
|
82
|
+
* @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
|
|
83
|
+
* @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
|
|
84
|
+
*/
|
|
85
|
+
var {
|
|
86
|
+
set: valueSetter
|
|
87
|
+
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
|
|
88
|
+
var prototype = Object.getPrototypeOf(element);
|
|
89
|
+
var {
|
|
90
|
+
set: prototypeValueSetter
|
|
91
|
+
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
92
|
+
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
93
|
+
prototypeValueSetter.call(element, value);
|
|
94
|
+
} else {
|
|
95
|
+
if (valueSetter) {
|
|
96
|
+
valueSetter.call(element, value);
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error('The given element does not have a value setter');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
23
101
|
}
|
|
24
|
-
var form = document.forms.namedItem(formId);
|
|
25
|
-
var input = document.createElement('input');
|
|
26
|
-
input.type = 'hidden';
|
|
27
|
-
input.name = name;
|
|
28
|
-
form === null || form === void 0 || form.appendChild(input);
|
|
29
|
-
return input;
|
|
30
102
|
}
|
|
31
|
-
function
|
|
103
|
+
function useInputEvent() {
|
|
104
|
+
var ref = useRef(null);
|
|
32
105
|
var eventDispatched = useRef({
|
|
33
106
|
change: false,
|
|
34
107
|
focus: false,
|
|
35
108
|
blur: false
|
|
36
109
|
});
|
|
37
|
-
var [key, setKey] = useState(metaOrOptions.key);
|
|
38
|
-
var [value, setValue] = useState(() => metaOrOptions.initialValue);
|
|
39
|
-
if (key !== metaOrOptions.key) {
|
|
40
|
-
setValue(metaOrOptions.initialValue);
|
|
41
|
-
setKey(metaOrOptions.key);
|
|
42
|
-
}
|
|
43
110
|
useEffect(() => {
|
|
44
111
|
var createEventListener = listener => {
|
|
45
112
|
return event => {
|
|
46
|
-
var element =
|
|
47
|
-
if (element) {
|
|
113
|
+
var element = ref.current;
|
|
114
|
+
if (element && event.target === element) {
|
|
48
115
|
eventDispatched.current[listener] = true;
|
|
49
116
|
}
|
|
50
117
|
};
|
|
@@ -60,81 +127,145 @@ function useInputControl(metaOrOptions) {
|
|
|
60
127
|
document.removeEventListener('focusin', focusHandler, true);
|
|
61
128
|
document.removeEventListener('focusout', blurHandler, true);
|
|
62
129
|
};
|
|
63
|
-
}, [
|
|
64
|
-
|
|
130
|
+
}, [ref]);
|
|
131
|
+
return useMemo(() => {
|
|
65
132
|
return {
|
|
66
133
|
change(value) {
|
|
67
134
|
if (!eventDispatched.current.change) {
|
|
68
|
-
var _element = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
69
135
|
eventDispatched.current.change = true;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// No change event will be triggered on React if `element.value` is already updated
|
|
136
|
+
var element = ref.current;
|
|
137
|
+
if (element) {
|
|
138
|
+
updateFieldValue(element, value);
|
|
74
139
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
} = Object.getOwnPropertyDescriptor(_element, 'value') || {};
|
|
84
|
-
var prototype = Object.getPrototypeOf(_element);
|
|
85
|
-
var {
|
|
86
|
-
set: prototypeValueSetter
|
|
87
|
-
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
|
|
88
|
-
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
|
|
89
|
-
prototypeValueSetter.call(_element, value);
|
|
90
|
-
} else {
|
|
91
|
-
if (valueSetter) {
|
|
92
|
-
valueSetter.call(_element, value);
|
|
93
|
-
} else {
|
|
94
|
-
throw new Error('The given element does not have a value setter');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
140
|
+
// Dispatch input event with the updated input value
|
|
141
|
+
element.dispatchEvent(new InputEvent('input', {
|
|
142
|
+
bubbles: true
|
|
143
|
+
}));
|
|
144
|
+
// Dispatch change event (necessary for select to update the selected option)
|
|
145
|
+
element.dispatchEvent(new Event('change', {
|
|
146
|
+
bubbles: true
|
|
147
|
+
}));
|
|
97
148
|
}
|
|
98
|
-
|
|
99
|
-
// Dispatch input event with the updated input value
|
|
100
|
-
_element.dispatchEvent(new InputEvent('input', {
|
|
101
|
-
bubbles: true
|
|
102
|
-
}));
|
|
103
|
-
// Dispatch change event (necessary for select to update the selected option)
|
|
104
|
-
_element.dispatchEvent(new Event('change', {
|
|
105
|
-
bubbles: true
|
|
106
|
-
}));
|
|
107
149
|
}
|
|
108
|
-
setValue(value);
|
|
109
150
|
eventDispatched.current.change = false;
|
|
110
151
|
},
|
|
111
152
|
focus() {
|
|
112
153
|
if (!eventDispatched.current.focus) {
|
|
113
|
-
var _element2 = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
114
154
|
eventDispatched.current.focus = true;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
var element = ref.current;
|
|
156
|
+
if (element) {
|
|
157
|
+
element.dispatchEvent(new FocusEvent('focusin', {
|
|
158
|
+
bubbles: true
|
|
159
|
+
}));
|
|
160
|
+
element.dispatchEvent(new FocusEvent('focus'));
|
|
161
|
+
}
|
|
119
162
|
}
|
|
120
163
|
eventDispatched.current.focus = false;
|
|
121
164
|
},
|
|
122
165
|
blur() {
|
|
123
166
|
if (!eventDispatched.current.blur) {
|
|
124
|
-
var _element3 = getEventTarget(metaOrOptions.formId, metaOrOptions.name);
|
|
125
167
|
eventDispatched.current.blur = true;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
168
|
+
var element = ref.current;
|
|
169
|
+
if (element) {
|
|
170
|
+
element.dispatchEvent(new FocusEvent('focusout', {
|
|
171
|
+
bubbles: true
|
|
172
|
+
}));
|
|
173
|
+
element.dispatchEvent(new FocusEvent('blur'));
|
|
174
|
+
}
|
|
130
175
|
}
|
|
131
176
|
eventDispatched.current.blur = false;
|
|
177
|
+
},
|
|
178
|
+
register(element) {
|
|
179
|
+
ref.current = element;
|
|
132
180
|
}
|
|
133
181
|
};
|
|
134
|
-
}, [
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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);
|
|
138
269
|
}
|
|
139
270
|
|
|
140
|
-
export { getEventTarget,
|
|
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",
|