@conform-to/react 1.6.1 → 1.7.0

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.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
8
8
  ```
9
9
 
10
- Version 1.6.1 / License MIT / Copyright (c) 2024 Edmund Hung
10
+ Version 1.7.0 / License MIT / Copyright (c) 2024 Edmund Hung
11
11
 
12
12
  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.
13
13
 
package/dist/context.d.ts CHANGED
@@ -10,6 +10,9 @@ export type Metadata<Schema, FormSchema extends Record<string, unknown>, FormErr
10
10
  errorId: string;
11
11
  descriptionId: string;
12
12
  name: FieldName<Schema, FormSchema, FormError>;
13
+ defaultValue: string | undefined;
14
+ defaultOptions: string[] | undefined;
15
+ defaultChecked: boolean | undefined;
13
16
  initialValue: FormValue<Schema>;
14
17
  value: FormValue<Schema>;
15
18
  errors: FormError | undefined;
package/dist/context.js CHANGED
@@ -74,6 +74,29 @@ function getMetadata(context, subjectRef, stateSnapshot) {
74
74
  name,
75
75
  errorId: "".concat(id, "-error"),
76
76
  descriptionId: "".concat(id, "-description"),
77
+ get defaultValue() {
78
+ var initialValue = this.initialValue;
79
+ if (typeof initialValue === 'string') {
80
+ return initialValue;
81
+ }
82
+ if (Array.isArray(initialValue)) {
83
+ return initialValue[0];
84
+ }
85
+ },
86
+ get defaultOptions() {
87
+ var initialValue = this.initialValue;
88
+ if (typeof initialValue === 'string') {
89
+ return [initialValue];
90
+ }
91
+ if (Array.isArray(initialValue) && initialValue.every(item => typeof item === 'string')) {
92
+ return initialValue;
93
+ }
94
+ },
95
+ get defaultChecked() {
96
+ if (this.initialValue === 'on') {
97
+ return true;
98
+ }
99
+ },
77
100
  get initialValue() {
78
101
  return state.initialValue[name];
79
102
  },
package/dist/context.mjs CHANGED
@@ -70,6 +70,29 @@ function getMetadata(context, subjectRef, stateSnapshot) {
70
70
  name,
71
71
  errorId: "".concat(id, "-error"),
72
72
  descriptionId: "".concat(id, "-description"),
73
+ get defaultValue() {
74
+ var initialValue = this.initialValue;
75
+ if (typeof initialValue === 'string') {
76
+ return initialValue;
77
+ }
78
+ if (Array.isArray(initialValue)) {
79
+ return initialValue[0];
80
+ }
81
+ },
82
+ get defaultOptions() {
83
+ var initialValue = this.initialValue;
84
+ if (typeof initialValue === 'string') {
85
+ return [initialValue];
86
+ }
87
+ if (Array.isArray(initialValue) && initialValue.every(item => typeof item === 'string')) {
88
+ return initialValue;
89
+ }
90
+ },
91
+ get defaultChecked() {
92
+ if (this.initialValue === 'on') {
93
+ return true;
94
+ }
95
+ },
73
96
  get initialValue() {
74
97
  return state.initialValue[name];
75
98
  },
@@ -0,0 +1,90 @@
1
+ export declare const formObserver: {
2
+ onFieldUpdate(callback: (event: {
3
+ type: "input" | "reset" | "mutation";
4
+ target: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
5
+ }) => void): () => void;
6
+ onFormUpdate(callback: (event: {
7
+ type: "submit" | "input" | "reset" | "mutation";
8
+ target: HTMLFormElement;
9
+ submitter?: HTMLInputElement | HTMLButtonElement | null;
10
+ }) => void): () => void;
11
+ dispose(): void;
12
+ };
13
+ export type Control = {
14
+ /**
15
+ * Current value of the base input. Undefined if the registered input
16
+ * is a multi-select, file input, or checkbox group.
17
+ */
18
+ value: string | undefined;
19
+ /**
20
+ * Selected options of the base input. Defined only when the registered input
21
+ * is a multi-select or checkbox group.
22
+ */
23
+ checked: boolean | undefined;
24
+ /**
25
+ * Checked state of the base input. Defined only when the registered input
26
+ * is a single checkbox or radio input.
27
+ */
28
+ options: string[] | undefined;
29
+ /**
30
+ * Selected files of the base input. Defined only when the registered input
31
+ * is a file input.
32
+ */
33
+ files: File[] | undefined;
34
+ /**
35
+ * Registers the base input element(s). Accepts a single input or an array for groups.
36
+ */
37
+ register: (element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLCollectionOf<HTMLInputElement> | NodeListOf<HTMLInputElement> | null | undefined) => void;
38
+ /**
39
+ * Programmatically updates the input value and emits
40
+ * both [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) and
41
+ * [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) events.
42
+ */
43
+ change(value: string | string[] | boolean | File | File[] | FileList): void;
44
+ /**
45
+ * Emits [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) and
46
+ * [focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) events.
47
+ * Does not actually move focus.
48
+ */
49
+ focus(): void;
50
+ /**
51
+ * Emits [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) and
52
+ * [focusin](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event) events.
53
+ * This does not move the actual keyboard focus to the input. Use `element.focus()` instead
54
+ * if you want to move focus to the input.
55
+ */
56
+ blur(): void;
57
+ };
58
+ /**
59
+ * A React hook that lets you sync the state of an input and dispatch native form events from it.
60
+ * This is useful when emulating native input behavior — typically by rendering a hidden base input
61
+ * and syncing it with a custom input.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const control = useControl(options);
66
+ * ```
67
+ */
68
+ export declare function useControl(options?: {
69
+ /**
70
+ * The initial value of the base input. It will be used to set the value
71
+ * when the input is first registered.
72
+ */
73
+ defaultValue?: string | string[] | File | File[] | null | undefined;
74
+ /**
75
+ * Whether the base input should be checked by default. It will be applied
76
+ * when the input is first registered.
77
+ */
78
+ defaultChecked?: boolean | undefined;
79
+ /**
80
+ * The value of a checkbox or radio input when checked. This sets the
81
+ * value attribute of the base input.
82
+ */
83
+ value?: string;
84
+ /**
85
+ * A callback function that is triggered when the base input is focused.
86
+ * Use this to delegate focus to a custom input.
87
+ */
88
+ onFocus?: () => void;
89
+ }): Control;
90
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1,179 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var dom = require('@conform-to/dom');
6
+ var react = require('react');
7
+ var util = require('./util.js');
8
+
9
+ var formObserver = dom.unstable_createGlobalFormsObserver();
10
+ /**
11
+ * A React hook that lets you sync the state of an input and dispatch native form events from it.
12
+ * This is useful when emulating native input behavior — typically by rendering a hidden base input
13
+ * and syncing it with a custom input.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const control = useControl(options);
18
+ * ```
19
+ */
20
+ function useControl(options) {
21
+ var inputRef = react.useRef(null);
22
+ var eventDispatched = react.useRef({});
23
+ var defaultSnapshot = util.getDefaultSnapshot(options === null || options === void 0 ? void 0 : options.defaultValue, options === null || options === void 0 ? void 0 : options.defaultChecked, options === null || options === void 0 ? void 0 : options.value);
24
+ var snapshotRef = react.useRef(defaultSnapshot);
25
+ var optionsRef = react.useRef(options);
26
+ react.useEffect(() => {
27
+ optionsRef.current = options;
28
+ });
29
+
30
+ // This is necessary to ensure that input is re-registered
31
+ // if the onFocus handler changes
32
+ var shouldHandleFocus = typeof (options === null || options === void 0 ? void 0 : options.onFocus) === 'function';
33
+ var snapshot = react.useSyncExternalStore(react.useCallback(callback => formObserver.onFieldUpdate(event => {
34
+ var input = event.target;
35
+ if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === input) : inputRef.current === input) {
36
+ callback();
37
+ }
38
+ }), []), () => {
39
+ var input = inputRef.current;
40
+ var prev = snapshotRef.current;
41
+ var next = !input ? defaultSnapshot : Array.isArray(input) ? {
42
+ value: util.getRadioGroupValue(input),
43
+ options: util.getCheckboxGroupValue(input)
44
+ } : util.getInputSnapshot(input);
45
+ if (dom.unstable_deepEqual(prev, next)) {
46
+ return prev;
47
+ }
48
+ snapshotRef.current = next;
49
+ return next;
50
+ }, () => snapshotRef.current);
51
+ react.useEffect(() => {
52
+ var createEventListener = listener => {
53
+ return event => {
54
+ if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === event.target) : inputRef.current === event.target) {
55
+ var timer = eventDispatched.current[listener];
56
+ if (timer) {
57
+ clearTimeout(timer);
58
+ }
59
+ eventDispatched.current[listener] = window.setTimeout(() => {
60
+ eventDispatched.current[listener] = undefined;
61
+ });
62
+ if (listener === 'focus') {
63
+ var _optionsRef$current, _optionsRef$current$o;
64
+ (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 || (_optionsRef$current$o = _optionsRef$current.onFocus) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current);
65
+ }
66
+ }
67
+ };
68
+ };
69
+ var inputHandler = createEventListener('change');
70
+ var focusHandler = createEventListener('focus');
71
+ var blurHandler = createEventListener('blur');
72
+ document.addEventListener('input', inputHandler, true);
73
+ document.addEventListener('focusin', focusHandler, true);
74
+ document.addEventListener('focusout', blurHandler, true);
75
+ return () => {
76
+ document.removeEventListener('input', inputHandler, true);
77
+ document.removeEventListener('focusin', focusHandler, true);
78
+ document.removeEventListener('focusout', blurHandler, true);
79
+ };
80
+ }, []);
81
+ return {
82
+ value: snapshot.value,
83
+ checked: snapshot.checked,
84
+ options: snapshot.options,
85
+ files: snapshot.files,
86
+ register: react.useCallback(element => {
87
+ if (!element) {
88
+ inputRef.current = null;
89
+ } else if (dom.isFieldElement(element)) {
90
+ inputRef.current = element;
91
+ if (shouldHandleFocus) {
92
+ util.focusable(element);
93
+ }
94
+ if (element.type === 'checkbox' || element.type === 'radio') {
95
+ var _optionsRef$current$v, _optionsRef$current2;
96
+ // React set the value as empty string incorrectly when the value is undefined
97
+ // This make sure the checkbox value falls back to the default value "on" properly
98
+ // @see https://github.com/facebook/react/issues/17590
99
+ element.value = (_optionsRef$current$v = (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : _optionsRef$current2.value) !== null && _optionsRef$current$v !== void 0 ? _optionsRef$current$v : 'on';
100
+ }
101
+ util.initializeField(element, optionsRef.current);
102
+ } else {
103
+ var _inputs$0$name, _inputs$, _inputs$0$type, _inputs$2;
104
+ var inputs = Array.from(element);
105
+ var name = (_inputs$0$name = (_inputs$ = inputs[0]) === null || _inputs$ === void 0 ? void 0 : _inputs$.name) !== null && _inputs$0$name !== void 0 ? _inputs$0$name : '';
106
+ var type = (_inputs$0$type = (_inputs$2 = inputs[0]) === null || _inputs$2 === void 0 ? void 0 : _inputs$2.type) !== null && _inputs$0$type !== void 0 ? _inputs$0$type : '';
107
+ if (!name || !(type === 'checkbox' || type === 'radio') || !inputs.every(input => input.name === name && input.type === type)) {
108
+ throw new Error('You can only register a checkbox or radio group with the same name');
109
+ }
110
+ inputRef.current = inputs;
111
+ for (var input of inputs) {
112
+ var _optionsRef$current3;
113
+ if (shouldHandleFocus) {
114
+ util.focusable(input);
115
+ }
116
+ util.initializeField(input, {
117
+ // We will not be uitlizing defaultChecked / value on checkbox / radio group
118
+ defaultValue: (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : _optionsRef$current3.defaultValue
119
+ });
120
+ }
121
+ }
122
+ }, [shouldHandleFocus]),
123
+ change: react.useCallback(value => {
124
+ if (!eventDispatched.current.change) {
125
+ var _inputRef$current;
126
+ var _element = Array.isArray(inputRef.current) ? (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.find(input => {
127
+ var wasChecked = input.checked;
128
+ var isChecked = Array.isArray(value) ? value.some(item => item === input.value) : input.value === value;
129
+ switch (input.type) {
130
+ case 'checkbox':
131
+ // We assume that only one checkbox can be checked at a time
132
+ // So we will pick the first element with checked state changed
133
+ return wasChecked !== isChecked;
134
+ case 'radio':
135
+ // We cannot uncheck a radio button
136
+ // So we will pick the first element that should be checked
137
+ return isChecked;
138
+ default:
139
+ return false;
140
+ }
141
+ }) : inputRef.current;
142
+ if (_element) {
143
+ dom.unstable_change(_element, typeof value === 'boolean' ? value ? _element.value : null : value);
144
+ }
145
+ }
146
+ if (eventDispatched.current.change) {
147
+ clearTimeout(eventDispatched.current.change);
148
+ }
149
+ eventDispatched.current.change = undefined;
150
+ }, []),
151
+ focus: react.useCallback(() => {
152
+ if (!eventDispatched.current.focus) {
153
+ var _element2 = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
154
+ if (_element2) {
155
+ dom.unstable_focus(_element2);
156
+ }
157
+ }
158
+ if (eventDispatched.current.focus) {
159
+ clearTimeout(eventDispatched.current.focus);
160
+ }
161
+ eventDispatched.current.focus = undefined;
162
+ }, []),
163
+ blur: react.useCallback(() => {
164
+ if (!eventDispatched.current.blur) {
165
+ var _element3 = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
166
+ if (_element3) {
167
+ dom.unstable_blur(_element3);
168
+ }
169
+ }
170
+ if (eventDispatched.current.blur) {
171
+ clearTimeout(eventDispatched.current.blur);
172
+ }
173
+ eventDispatched.current.blur = undefined;
174
+ }, [])
175
+ };
176
+ }
177
+
178
+ exports.formObserver = formObserver;
179
+ exports.useControl = useControl;
@@ -0,0 +1,174 @@
1
+ import { unstable_createGlobalFormsObserver, unstable_deepEqual, isFieldElement, unstable_change, unstable_focus, unstable_blur } from '@conform-to/dom';
2
+ import { useRef, useEffect, useSyncExternalStore, useCallback } from 'react';
3
+ import { getDefaultSnapshot, getRadioGroupValue, getCheckboxGroupValue, getInputSnapshot, focusable, initializeField } from './util.mjs';
4
+
5
+ var formObserver = unstable_createGlobalFormsObserver();
6
+ /**
7
+ * A React hook that lets you sync the state of an input and dispatch native form events from it.
8
+ * This is useful when emulating native input behavior — typically by rendering a hidden base input
9
+ * and syncing it with a custom input.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const control = useControl(options);
14
+ * ```
15
+ */
16
+ function useControl(options) {
17
+ var inputRef = useRef(null);
18
+ var eventDispatched = useRef({});
19
+ var defaultSnapshot = getDefaultSnapshot(options === null || options === void 0 ? void 0 : options.defaultValue, options === null || options === void 0 ? void 0 : options.defaultChecked, options === null || options === void 0 ? void 0 : options.value);
20
+ var snapshotRef = useRef(defaultSnapshot);
21
+ var optionsRef = useRef(options);
22
+ useEffect(() => {
23
+ optionsRef.current = options;
24
+ });
25
+
26
+ // This is necessary to ensure that input is re-registered
27
+ // if the onFocus handler changes
28
+ var shouldHandleFocus = typeof (options === null || options === void 0 ? void 0 : options.onFocus) === 'function';
29
+ var snapshot = useSyncExternalStore(useCallback(callback => formObserver.onFieldUpdate(event => {
30
+ var input = event.target;
31
+ if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === input) : inputRef.current === input) {
32
+ callback();
33
+ }
34
+ }), []), () => {
35
+ var input = inputRef.current;
36
+ var prev = snapshotRef.current;
37
+ var next = !input ? defaultSnapshot : Array.isArray(input) ? {
38
+ value: getRadioGroupValue(input),
39
+ options: getCheckboxGroupValue(input)
40
+ } : getInputSnapshot(input);
41
+ if (unstable_deepEqual(prev, next)) {
42
+ return prev;
43
+ }
44
+ snapshotRef.current = next;
45
+ return next;
46
+ }, () => snapshotRef.current);
47
+ useEffect(() => {
48
+ var createEventListener = listener => {
49
+ return event => {
50
+ if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === event.target) : inputRef.current === event.target) {
51
+ var timer = eventDispatched.current[listener];
52
+ if (timer) {
53
+ clearTimeout(timer);
54
+ }
55
+ eventDispatched.current[listener] = window.setTimeout(() => {
56
+ eventDispatched.current[listener] = undefined;
57
+ });
58
+ if (listener === 'focus') {
59
+ var _optionsRef$current, _optionsRef$current$o;
60
+ (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 || (_optionsRef$current$o = _optionsRef$current.onFocus) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current);
61
+ }
62
+ }
63
+ };
64
+ };
65
+ var inputHandler = createEventListener('change');
66
+ var focusHandler = createEventListener('focus');
67
+ var blurHandler = createEventListener('blur');
68
+ document.addEventListener('input', inputHandler, true);
69
+ document.addEventListener('focusin', focusHandler, true);
70
+ document.addEventListener('focusout', blurHandler, true);
71
+ return () => {
72
+ document.removeEventListener('input', inputHandler, true);
73
+ document.removeEventListener('focusin', focusHandler, true);
74
+ document.removeEventListener('focusout', blurHandler, true);
75
+ };
76
+ }, []);
77
+ return {
78
+ value: snapshot.value,
79
+ checked: snapshot.checked,
80
+ options: snapshot.options,
81
+ files: snapshot.files,
82
+ register: useCallback(element => {
83
+ if (!element) {
84
+ inputRef.current = null;
85
+ } else if (isFieldElement(element)) {
86
+ inputRef.current = element;
87
+ if (shouldHandleFocus) {
88
+ focusable(element);
89
+ }
90
+ if (element.type === 'checkbox' || element.type === 'radio') {
91
+ var _optionsRef$current$v, _optionsRef$current2;
92
+ // React set the value as empty string incorrectly when the value is undefined
93
+ // This make sure the checkbox value falls back to the default value "on" properly
94
+ // @see https://github.com/facebook/react/issues/17590
95
+ element.value = (_optionsRef$current$v = (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : _optionsRef$current2.value) !== null && _optionsRef$current$v !== void 0 ? _optionsRef$current$v : 'on';
96
+ }
97
+ initializeField(element, optionsRef.current);
98
+ } else {
99
+ var _inputs$0$name, _inputs$, _inputs$0$type, _inputs$2;
100
+ var inputs = Array.from(element);
101
+ var name = (_inputs$0$name = (_inputs$ = inputs[0]) === null || _inputs$ === void 0 ? void 0 : _inputs$.name) !== null && _inputs$0$name !== void 0 ? _inputs$0$name : '';
102
+ var type = (_inputs$0$type = (_inputs$2 = inputs[0]) === null || _inputs$2 === void 0 ? void 0 : _inputs$2.type) !== null && _inputs$0$type !== void 0 ? _inputs$0$type : '';
103
+ if (!name || !(type === 'checkbox' || type === 'radio') || !inputs.every(input => input.name === name && input.type === type)) {
104
+ throw new Error('You can only register a checkbox or radio group with the same name');
105
+ }
106
+ inputRef.current = inputs;
107
+ for (var input of inputs) {
108
+ var _optionsRef$current3;
109
+ if (shouldHandleFocus) {
110
+ focusable(input);
111
+ }
112
+ initializeField(input, {
113
+ // We will not be uitlizing defaultChecked / value on checkbox / radio group
114
+ defaultValue: (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : _optionsRef$current3.defaultValue
115
+ });
116
+ }
117
+ }
118
+ }, [shouldHandleFocus]),
119
+ change: useCallback(value => {
120
+ if (!eventDispatched.current.change) {
121
+ var _inputRef$current;
122
+ var _element = Array.isArray(inputRef.current) ? (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.find(input => {
123
+ var wasChecked = input.checked;
124
+ var isChecked = Array.isArray(value) ? value.some(item => item === input.value) : input.value === value;
125
+ switch (input.type) {
126
+ case 'checkbox':
127
+ // We assume that only one checkbox can be checked at a time
128
+ // So we will pick the first element with checked state changed
129
+ return wasChecked !== isChecked;
130
+ case 'radio':
131
+ // We cannot uncheck a radio button
132
+ // So we will pick the first element that should be checked
133
+ return isChecked;
134
+ default:
135
+ return false;
136
+ }
137
+ }) : inputRef.current;
138
+ if (_element) {
139
+ unstable_change(_element, typeof value === 'boolean' ? value ? _element.value : null : value);
140
+ }
141
+ }
142
+ if (eventDispatched.current.change) {
143
+ clearTimeout(eventDispatched.current.change);
144
+ }
145
+ eventDispatched.current.change = undefined;
146
+ }, []),
147
+ focus: useCallback(() => {
148
+ if (!eventDispatched.current.focus) {
149
+ var _element2 = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
150
+ if (_element2) {
151
+ unstable_focus(_element2);
152
+ }
153
+ }
154
+ if (eventDispatched.current.focus) {
155
+ clearTimeout(eventDispatched.current.focus);
156
+ }
157
+ eventDispatched.current.focus = undefined;
158
+ }, []),
159
+ blur: useCallback(() => {
160
+ if (!eventDispatched.current.blur) {
161
+ var _element3 = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
162
+ if (_element3) {
163
+ unstable_blur(_element3);
164
+ }
165
+ }
166
+ if (eventDispatched.current.blur) {
167
+ clearTimeout(eventDispatched.current.blur);
168
+ }
169
+ eventDispatched.current.blur = undefined;
170
+ }, [])
171
+ };
172
+ }
173
+
174
+ export { formObserver, useControl };
@@ -0,0 +1,2 @@
1
+ export { useControl } from './hooks';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var hooks = require('./hooks.js');
6
+
7
+
8
+
9
+ exports.useControl = hooks.useControl;
@@ -0,0 +1 @@
1
+ export { useControl } from './hooks.mjs';
@@ -0,0 +1,37 @@
1
+ export declare function focusable(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement): void;
2
+ export declare function initializeField(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, options: {
3
+ defaultValue?: string | string[] | File | File[] | null;
4
+ defaultChecked?: boolean;
5
+ value?: string;
6
+ } | undefined): void;
7
+ export declare function getRadioGroupValue(inputs: Array<HTMLInputElement>): string | undefined;
8
+ export declare function getCheckboxGroupValue(inputs: Array<HTMLInputElement>): string[] | undefined;
9
+ export type InputSnapshot = {
10
+ value?: string;
11
+ options?: string[];
12
+ checked?: boolean;
13
+ files?: File[];
14
+ };
15
+ export declare function getInputSnapshot(input: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement): {
16
+ files: File[] | undefined;
17
+ value?: undefined;
18
+ checked?: undefined;
19
+ options?: undefined;
20
+ } | {
21
+ value: string;
22
+ checked: boolean;
23
+ files?: undefined;
24
+ options?: undefined;
25
+ } | {
26
+ options: string[];
27
+ files?: undefined;
28
+ value?: undefined;
29
+ checked?: undefined;
30
+ } | {
31
+ value: string;
32
+ files?: undefined;
33
+ checked?: undefined;
34
+ options?: undefined;
35
+ };
36
+ export declare function getDefaultSnapshot(defaultValue: string | string[] | File | File[] | FileList | null | undefined, defaultChecked: boolean | undefined, value: string | undefined): InputSnapshot;
37
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var dom = require('@conform-to/dom');
6
+
7
+ function focusable(element) {
8
+ if (!element.hidden && element.type !== 'hidden') {
9
+ return;
10
+ }
11
+
12
+ // Style the element to be visually hidden
13
+ element.style.position = 'absolute';
14
+ element.style.width = '1px';
15
+ element.style.height = '1px';
16
+ element.style.padding = '0';
17
+ element.style.margin = '-1px';
18
+ element.style.overflow = 'hidden';
19
+ element.style.clip = 'rect(0,0,0,0)';
20
+ element.style.whiteSpace = 'nowrap';
21
+ element.style.border = '0';
22
+
23
+ // Hide the element from screen readers
24
+ element.setAttribute('aria-hidden', 'true');
25
+
26
+ // Make sure people won't tab to this element
27
+ element.tabIndex = -1;
28
+
29
+ // Set the element to be visible again so it can be focused
30
+ if (element.hidden) {
31
+ element.hidden = false;
32
+ }
33
+ if (element.type === 'hidden') {
34
+ element.setAttribute('type', 'text');
35
+ }
36
+ }
37
+ function initializeField(element, options) {
38
+ var _options$value;
39
+ if (element.dataset.conform) {
40
+ return;
41
+ }
42
+ var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
43
+
44
+ // Update the value of the element, including the default value
45
+ dom.unstable_updateField(element, {
46
+ value: defaultValue,
47
+ defaultValue
48
+ });
49
+ element.dataset.conform = 'initialized';
50
+ }
51
+ function getRadioGroupValue(inputs) {
52
+ for (var input of inputs) {
53
+ if (input.type === 'radio' && input.checked) {
54
+ return input.value;
55
+ }
56
+ }
57
+ }
58
+ function getCheckboxGroupValue(inputs) {
59
+ var values;
60
+ for (var input of inputs) {
61
+ if (input.type === 'checkbox') {
62
+ var _values;
63
+ (_values = values) !== null && _values !== void 0 ? _values : values = [];
64
+ if (input.checked) {
65
+ values.push(input.value);
66
+ }
67
+ }
68
+ }
69
+ return values;
70
+ }
71
+ function getInputSnapshot(input) {
72
+ if (input instanceof HTMLInputElement) {
73
+ switch (input.type) {
74
+ case 'file':
75
+ return {
76
+ files: input.files ? Array.from(input.files) : undefined
77
+ };
78
+ case 'radio':
79
+ case 'checkbox':
80
+ return {
81
+ value: input.value,
82
+ checked: input.checked
83
+ };
84
+ }
85
+ } else if (input instanceof HTMLSelectElement && input.multiple) {
86
+ return {
87
+ options: Array.from(input.selectedOptions).map(option => option.value)
88
+ };
89
+ }
90
+ return {
91
+ value: input.value
92
+ };
93
+ }
94
+ function getDefaultSnapshot(defaultValue, defaultChecked, value) {
95
+ if (typeof value === 'string' || typeof defaultChecked === 'boolean') {
96
+ return {
97
+ value: value !== null && value !== void 0 ? value : 'on',
98
+ checked: defaultChecked
99
+ };
100
+ }
101
+ if (typeof defaultValue === 'string') {
102
+ return {
103
+ value: defaultValue
104
+ };
105
+ }
106
+ if (Array.isArray(defaultValue) && defaultValue.every(item => typeof item === 'string')) {
107
+ return {
108
+ options: defaultValue
109
+ };
110
+ }
111
+ if (defaultValue instanceof FileList) {
112
+ return {
113
+ files: Array.from(defaultValue)
114
+ };
115
+ }
116
+ if (Array.isArray(defaultValue)) {
117
+ return {
118
+ files: defaultValue
119
+ };
120
+ }
121
+ if (defaultValue) {
122
+ return {
123
+ files: [defaultValue]
124
+ };
125
+ }
126
+ return {};
127
+ }
128
+
129
+ exports.focusable = focusable;
130
+ exports.getCheckboxGroupValue = getCheckboxGroupValue;
131
+ exports.getDefaultSnapshot = getDefaultSnapshot;
132
+ exports.getInputSnapshot = getInputSnapshot;
133
+ exports.getRadioGroupValue = getRadioGroupValue;
134
+ exports.initializeField = initializeField;
@@ -0,0 +1,125 @@
1
+ import { unstable_updateField } from '@conform-to/dom';
2
+
3
+ function focusable(element) {
4
+ if (!element.hidden && element.type !== 'hidden') {
5
+ return;
6
+ }
7
+
8
+ // Style the element to be visually hidden
9
+ element.style.position = 'absolute';
10
+ element.style.width = '1px';
11
+ element.style.height = '1px';
12
+ element.style.padding = '0';
13
+ element.style.margin = '-1px';
14
+ element.style.overflow = 'hidden';
15
+ element.style.clip = 'rect(0,0,0,0)';
16
+ element.style.whiteSpace = 'nowrap';
17
+ element.style.border = '0';
18
+
19
+ // Hide the element from screen readers
20
+ element.setAttribute('aria-hidden', 'true');
21
+
22
+ // Make sure people won't tab to this element
23
+ element.tabIndex = -1;
24
+
25
+ // Set the element to be visible again so it can be focused
26
+ if (element.hidden) {
27
+ element.hidden = false;
28
+ }
29
+ if (element.type === 'hidden') {
30
+ element.setAttribute('type', 'text');
31
+ }
32
+ }
33
+ function initializeField(element, options) {
34
+ var _options$value;
35
+ if (element.dataset.conform) {
36
+ return;
37
+ }
38
+ var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
39
+
40
+ // Update the value of the element, including the default value
41
+ unstable_updateField(element, {
42
+ value: defaultValue,
43
+ defaultValue
44
+ });
45
+ element.dataset.conform = 'initialized';
46
+ }
47
+ function getRadioGroupValue(inputs) {
48
+ for (var input of inputs) {
49
+ if (input.type === 'radio' && input.checked) {
50
+ return input.value;
51
+ }
52
+ }
53
+ }
54
+ function getCheckboxGroupValue(inputs) {
55
+ var values;
56
+ for (var input of inputs) {
57
+ if (input.type === 'checkbox') {
58
+ var _values;
59
+ (_values = values) !== null && _values !== void 0 ? _values : values = [];
60
+ if (input.checked) {
61
+ values.push(input.value);
62
+ }
63
+ }
64
+ }
65
+ return values;
66
+ }
67
+ function getInputSnapshot(input) {
68
+ if (input instanceof HTMLInputElement) {
69
+ switch (input.type) {
70
+ case 'file':
71
+ return {
72
+ files: input.files ? Array.from(input.files) : undefined
73
+ };
74
+ case 'radio':
75
+ case 'checkbox':
76
+ return {
77
+ value: input.value,
78
+ checked: input.checked
79
+ };
80
+ }
81
+ } else if (input instanceof HTMLSelectElement && input.multiple) {
82
+ return {
83
+ options: Array.from(input.selectedOptions).map(option => option.value)
84
+ };
85
+ }
86
+ return {
87
+ value: input.value
88
+ };
89
+ }
90
+ function getDefaultSnapshot(defaultValue, defaultChecked, value) {
91
+ if (typeof value === 'string' || typeof defaultChecked === 'boolean') {
92
+ return {
93
+ value: value !== null && value !== void 0 ? value : 'on',
94
+ checked: defaultChecked
95
+ };
96
+ }
97
+ if (typeof defaultValue === 'string') {
98
+ return {
99
+ value: defaultValue
100
+ };
101
+ }
102
+ if (Array.isArray(defaultValue) && defaultValue.every(item => typeof item === 'string')) {
103
+ return {
104
+ options: defaultValue
105
+ };
106
+ }
107
+ if (defaultValue instanceof FileList) {
108
+ return {
109
+ files: Array.from(defaultValue)
110
+ };
111
+ }
112
+ if (Array.isArray(defaultValue)) {
113
+ return {
114
+ files: defaultValue
115
+ };
116
+ }
117
+ if (defaultValue) {
118
+ return {
119
+ files: [defaultValue]
120
+ };
121
+ }
122
+ return {};
123
+ }
124
+
125
+ export { focusable, getCheckboxGroupValue, getDefaultSnapshot, getInputSnapshot, getRadioGroupValue, initializeField };
@@ -102,7 +102,7 @@ function useInputEvent(onUpdate) {
102
102
  eventDispatched.current.change = true;
103
103
  var element = ref.current;
104
104
  if (element) {
105
- dom.unstable_updateFieldValue(element, {
105
+ dom.unstable_updateField(element, {
106
106
  value
107
107
  });
108
108
 
@@ -223,7 +223,7 @@ function useControl(meta) {
223
223
  // This is now handled mostly by the side effect
224
224
  // But we still need to set the initial value for backward compatibility
225
225
  if (!element.dataset.conform) {
226
- dom.unstable_updateFieldValue(element, {
226
+ dom.unstable_updateField(element, {
227
227
  value
228
228
  });
229
229
  }
@@ -1,4 +1,4 @@
1
- import { unstable_updateFieldValue } from '@conform-to/dom';
1
+ import { unstable_updateField } from '@conform-to/dom';
2
2
  import { useRef, useEffect, useMemo, useState } from 'react';
3
3
 
4
4
  function getFormElement(formId) {
@@ -98,7 +98,7 @@ function useInputEvent(onUpdate) {
98
98
  eventDispatched.current.change = true;
99
99
  var element = ref.current;
100
100
  if (element) {
101
- unstable_updateFieldValue(element, {
101
+ unstable_updateField(element, {
102
102
  value
103
103
  });
104
104
 
@@ -219,7 +219,7 @@ function useControl(meta) {
219
219
  // This is now handled mostly by the side effect
220
220
  // But we still need to set the initial value for backward compatibility
221
221
  if (!element.dataset.conform) {
222
- unstable_updateFieldValue(element, {
222
+ unstable_updateField(element, {
223
223
  value
224
224
  });
225
225
  }
@@ -0,0 +1,3 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
3
+ //# sourceMappingURL=vitest.config.d.ts.map
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.6.1",
6
+ "version": "1.7.0",
7
7
  "main": "./dist/index.js",
8
8
  "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
@@ -14,6 +14,13 @@
14
14
  "import": "./dist/index.mjs",
15
15
  "require": "./dist/index.js",
16
16
  "default": "./dist/index.mjs"
17
+ },
18
+ "./future": {
19
+ "types": "./dist/future/index.d.ts",
20
+ "module": "./dist/future/index.mjs",
21
+ "import": "./dist/future/index.mjs",
22
+ "require": "./dist/future/index.js",
23
+ "default": "./dist/future/index.mjs"
17
24
  }
18
25
  },
19
26
  "files": [
@@ -34,7 +41,7 @@
34
41
  "url": "https://github.com/edmundhung/conform/issues"
35
42
  },
36
43
  "dependencies": {
37
- "@conform-to/dom": "1.6.1"
44
+ "@conform-to/dom": "1.7.0"
38
45
  },
39
46
  "devDependencies": {
40
47
  "@babel/core": "^7.17.8",
@@ -46,7 +53,8 @@
46
53
  "@types/react": "^18.2.43",
47
54
  "react": "^18.2.0",
48
55
  "rollup-plugin-copy": "^3.4.0",
49
- "rollup": "^2.79.1"
56
+ "rollup": "^2.79.1",
57
+ "vitest-browser-react": "^0.1.1"
50
58
  },
51
59
  "peerDependencies": {
52
60
  "react": ">=18"