@conform-to/react 1.12.0 → 1.13.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 +1 -1
- package/dist/future/dom.d.ts +1 -1
- package/dist/future/dom.js +9 -6
- package/dist/future/dom.mjs +10 -7
- package/dist/future/hooks.d.ts +3 -3
- package/dist/future/hooks.js +12 -1
- package/dist/future/hooks.mjs +12 -1
- package/dist/future/intent.js +34 -15
- package/dist/future/intent.mjs +36 -17
- package/dist/future/state.d.ts +2 -2
- package/dist/future/state.js +15 -2
- package/dist/future/state.mjs +15 -2
- package/dist/future/types.d.ts +57 -19
- package/dist/future/util.d.ts +1 -0
- package/dist/future/util.js +4 -0
- package/dist/future/util.mjs +4 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
╚══════╝ ╚═════╝ ╚═╝ ╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
Version 1.
|
|
10
|
+
Version 1.13.0 / License MIT / Copyright (c) 2025 Edmund Hung
|
|
11
11
|
|
|
12
12
|
Progressively enhance HTML forms with React. Build resilient, type-safe forms with no hassle using web standards.
|
|
13
13
|
|
package/dist/future/dom.d.ts
CHANGED
|
@@ -32,5 +32,5 @@ export declare function updateFormValue(form: HTMLFormElement, intendedValue: Re
|
|
|
32
32
|
* Creates a proxy that dynamically generates intent dispatch functions.
|
|
33
33
|
* Each property access returns a function that submits the intent to the form.
|
|
34
34
|
*/
|
|
35
|
-
export declare function createIntentDispatcher(formElement: HTMLFormElement | (() => HTMLFormElement | null), intentName: string): IntentDispatcher
|
|
35
|
+
export declare function createIntentDispatcher<FormShape extends Record<string, any>>(formElement: HTMLFormElement | (() => HTMLFormElement | null), intentName: string): IntentDispatcher<FormShape>;
|
|
36
36
|
//# sourceMappingURL=dom.d.ts.map
|
package/dist/future/dom.js
CHANGED
|
@@ -176,13 +176,16 @@ function focusFirstInvalidField(ctx) {
|
|
|
176
176
|
function updateFormValue(form, intendedValue, serialize) {
|
|
177
177
|
for (var element of form.elements) {
|
|
178
178
|
if (future.isFieldElement(element) && element.name) {
|
|
179
|
-
var
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
preventDefault: true
|
|
184
|
-
});
|
|
179
|
+
var fieldValue = future.getValueAtPath(intendedValue, element.name);
|
|
180
|
+
if (element.type === 'file' && typeof fieldValue === 'undefined') {
|
|
181
|
+
// Do not update file inputs unless there's an intended value
|
|
182
|
+
continue;
|
|
185
183
|
}
|
|
184
|
+
var serializedValue = serialize(fieldValue);
|
|
185
|
+
var value = typeof serializedValue !== 'undefined' ? serializedValue : future.getFieldDefaultValue(element);
|
|
186
|
+
future.change(element, value, {
|
|
187
|
+
preventDefault: true
|
|
188
|
+
});
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
}
|
package/dist/future/dom.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { updateField, isGlobalInstance, isFieldElement, requestIntent, getValueAtPath, change } from '@conform-to/dom/future';
|
|
1
|
+
import { updateField, isGlobalInstance, isFieldElement, requestIntent, getValueAtPath, getFieldDefaultValue, change } from '@conform-to/dom/future';
|
|
2
2
|
import { serializeIntent } from './intent.mjs';
|
|
3
3
|
|
|
4
4
|
function getFormElement(formRef) {
|
|
@@ -172,13 +172,16 @@ function focusFirstInvalidField(ctx) {
|
|
|
172
172
|
function updateFormValue(form, intendedValue, serialize) {
|
|
173
173
|
for (var element of form.elements) {
|
|
174
174
|
if (isFieldElement(element) && element.name) {
|
|
175
|
-
var
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
preventDefault: true
|
|
180
|
-
});
|
|
175
|
+
var fieldValue = getValueAtPath(intendedValue, element.name);
|
|
176
|
+
if (element.type === 'file' && typeof fieldValue === 'undefined') {
|
|
177
|
+
// Do not update file inputs unless there's an intended value
|
|
178
|
+
continue;
|
|
181
179
|
}
|
|
180
|
+
var serializedValue = serialize(fieldValue);
|
|
181
|
+
var value = typeof serializedValue !== 'undefined' ? serializedValue : getFieldDefaultValue(element);
|
|
182
|
+
change(element, value, {
|
|
183
|
+
preventDefault: true
|
|
184
|
+
});
|
|
182
185
|
}
|
|
183
186
|
}
|
|
184
187
|
}
|
package/dist/future/hooks.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export declare function useConform<ErrorShape, Value = undefined>(formRef: FormR
|
|
|
58
58
|
export declare function useForm<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = DefaultErrorShape, Value = undefined>(options: FormOptions<FormShape, ErrorShape, Value>): {
|
|
59
59
|
form: FormMetadata<ErrorShape>;
|
|
60
60
|
fields: Fieldset<FormShape, ErrorShape>;
|
|
61
|
-
intent: IntentDispatcher
|
|
61
|
+
intent: IntentDispatcher<FormShape>;
|
|
62
62
|
};
|
|
63
63
|
/**
|
|
64
64
|
* A React hook that provides access to form-level metadata and state.
|
|
@@ -124,7 +124,7 @@ export declare function useField<FieldShape = any>(name: FieldName<FieldShape>,
|
|
|
124
124
|
* }
|
|
125
125
|
* ```
|
|
126
126
|
*/
|
|
127
|
-
export declare function useIntent(formRef: FormRef): IntentDispatcher
|
|
127
|
+
export declare function useIntent<FormShape extends Record<string, any>>(formRef: FormRef): IntentDispatcher<FormShape>;
|
|
128
128
|
/**
|
|
129
129
|
* A React hook that lets you sync the state of an input and dispatch native form events from it.
|
|
130
130
|
* This is useful when emulating native input behavior — typically by rendering a hidden base input
|
|
@@ -164,7 +164,7 @@ export declare function useControl(options?: {
|
|
|
164
164
|
* @see https://conform.guide/api/react/future/useFormData
|
|
165
165
|
* @example
|
|
166
166
|
* ```ts
|
|
167
|
-
* const value = useFormData(formRef, formData => formData?.get('fieldName')
|
|
167
|
+
* const value = useFormData(formRef, formData => formData?.get('fieldName') ?? '');
|
|
168
168
|
* ```
|
|
169
169
|
*/
|
|
170
170
|
export declare function useFormData<Value = any>(formRef: FormRef, select: Selector<FormData, Value>, options: UseFormDataOptions & {
|
package/dist/future/hooks.js
CHANGED
|
@@ -534,6 +534,16 @@ function useControl(options) {
|
|
|
534
534
|
observer
|
|
535
535
|
} = react.useContext(GlobalFormOptionsContext);
|
|
536
536
|
var inputRef = react.useRef(null);
|
|
537
|
+
var formRef = react.useMemo(() => ({
|
|
538
|
+
get current() {
|
|
539
|
+
var _input$0$form, _input$;
|
|
540
|
+
var input = inputRef.current;
|
|
541
|
+
if (!input) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
return Array.isArray(input) ? (_input$0$form = (_input$ = input[0]) === null || _input$ === void 0 ? void 0 : _input$.form) !== null && _input$0$form !== void 0 ? _input$0$form : null : input.form;
|
|
545
|
+
}
|
|
546
|
+
}), []);
|
|
537
547
|
var eventDispatched = react.useRef({});
|
|
538
548
|
var defaultSnapshot = dom.createDefaultSnapshot(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);
|
|
539
549
|
var snapshotRef = react.useRef(defaultSnapshot);
|
|
@@ -598,6 +608,7 @@ function useControl(options) {
|
|
|
598
608
|
checked: snapshot.checked,
|
|
599
609
|
options: snapshot.options,
|
|
600
610
|
files: snapshot.files,
|
|
611
|
+
formRef,
|
|
601
612
|
register: react.useCallback(element => {
|
|
602
613
|
if (!element) {
|
|
603
614
|
inputRef.current = null;
|
|
@@ -697,7 +708,7 @@ function useControl(options) {
|
|
|
697
708
|
* @see https://conform.guide/api/react/future/useFormData
|
|
698
709
|
* @example
|
|
699
710
|
* ```ts
|
|
700
|
-
* const value = useFormData(formRef, formData => formData?.get('fieldName')
|
|
711
|
+
* const value = useFormData(formRef, formData => formData?.get('fieldName') ?? '');
|
|
701
712
|
* ```
|
|
702
713
|
*/
|
|
703
714
|
|
package/dist/future/hooks.mjs
CHANGED
|
@@ -530,6 +530,16 @@ function useControl(options) {
|
|
|
530
530
|
observer
|
|
531
531
|
} = useContext(GlobalFormOptionsContext);
|
|
532
532
|
var inputRef = useRef(null);
|
|
533
|
+
var formRef = useMemo(() => ({
|
|
534
|
+
get current() {
|
|
535
|
+
var _input$0$form, _input$;
|
|
536
|
+
var input = inputRef.current;
|
|
537
|
+
if (!input) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
return Array.isArray(input) ? (_input$0$form = (_input$ = input[0]) === null || _input$ === void 0 ? void 0 : _input$.form) !== null && _input$0$form !== void 0 ? _input$0$form : null : input.form;
|
|
541
|
+
}
|
|
542
|
+
}), []);
|
|
533
543
|
var eventDispatched = useRef({});
|
|
534
544
|
var defaultSnapshot = createDefaultSnapshot(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);
|
|
535
545
|
var snapshotRef = useRef(defaultSnapshot);
|
|
@@ -594,6 +604,7 @@ function useControl(options) {
|
|
|
594
604
|
checked: snapshot.checked,
|
|
595
605
|
options: snapshot.options,
|
|
596
606
|
files: snapshot.files,
|
|
607
|
+
formRef,
|
|
597
608
|
register: useCallback(element => {
|
|
598
609
|
if (!element) {
|
|
599
610
|
inputRef.current = null;
|
|
@@ -693,7 +704,7 @@ function useControl(options) {
|
|
|
693
704
|
* @see https://conform.guide/api/react/future/useFormData
|
|
694
705
|
* @example
|
|
695
706
|
* ```ts
|
|
696
|
-
* const value = useFormData(formRef, formData => formData?.get('fieldName')
|
|
707
|
+
* const value = useFormData(formRef, formData => formData?.get('fieldName') ?? '');
|
|
697
708
|
* ```
|
|
698
709
|
*/
|
|
699
710
|
|
package/dist/future/intent.js
CHANGED
|
@@ -92,22 +92,38 @@ function updateListKeys() {
|
|
|
92
92
|
*/
|
|
93
93
|
var actionHandlers = {
|
|
94
94
|
reset: {
|
|
95
|
-
|
|
96
|
-
return null;
|
|
95
|
+
validatePayload(options) {
|
|
96
|
+
return util.isOptional(options, future.isPlainObject) && (util.isUndefined(options === null || options === void 0 ? void 0 : options.defaultValue) || util.isNullable(options === null || options === void 0 ? void 0 : options.defaultValue, future.isPlainObject));
|
|
97
|
+
},
|
|
98
|
+
onApply(_, options) {
|
|
99
|
+
var {
|
|
100
|
+
defaultValue
|
|
101
|
+
} = options !== null && options !== void 0 ? options : {};
|
|
102
|
+
return defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null;
|
|
103
|
+
},
|
|
104
|
+
onUpdate(_, _ref) {
|
|
105
|
+
var _intent$payload;
|
|
106
|
+
var {
|
|
107
|
+
intent
|
|
108
|
+
} = _ref;
|
|
109
|
+
var defaultValue = (_intent$payload = intent.payload) === null || _intent$payload === void 0 ? void 0 : _intent$payload.defaultValue;
|
|
110
|
+
return util.merge(state.initializeState(), {
|
|
111
|
+
serverIntendedValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null
|
|
112
|
+
});
|
|
97
113
|
}
|
|
98
114
|
},
|
|
99
115
|
validate: {
|
|
100
116
|
validatePayload(name) {
|
|
101
117
|
return util.isOptional(name, util.isString);
|
|
102
118
|
},
|
|
103
|
-
onUpdate(state,
|
|
104
|
-
var _intent$
|
|
119
|
+
onUpdate(state, _ref2) {
|
|
120
|
+
var _intent$payload2;
|
|
105
121
|
var {
|
|
106
122
|
submission,
|
|
107
123
|
intent,
|
|
108
124
|
error
|
|
109
|
-
} =
|
|
110
|
-
var name = (_intent$
|
|
125
|
+
} = _ref2;
|
|
126
|
+
var name = (_intent$payload2 = intent.payload) !== null && _intent$payload2 !== void 0 ? _intent$payload2 : '';
|
|
111
127
|
var basePath = future.getPathSegments(name);
|
|
112
128
|
var allFields = error ?
|
|
113
129
|
// Consider fields / fieldset with errors as touched too
|
|
@@ -129,14 +145,17 @@ var actionHandlers = {
|
|
|
129
145
|
return future.isPlainObject(options) && util.isOptional(options.name, util.isString) && util.isOptional(options.index, util.isNumber) && !util.isUndefined(options.value);
|
|
130
146
|
},
|
|
131
147
|
onApply(value, options) {
|
|
132
|
-
|
|
148
|
+
var _options$value;
|
|
149
|
+
var name = future.appendPathSegment(options.name, options.index);
|
|
150
|
+
var newValue = (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : name === '' ? {} : null;
|
|
151
|
+
return util.updateValueAtPath(value, name, newValue);
|
|
133
152
|
},
|
|
134
|
-
onUpdate(state,
|
|
153
|
+
onUpdate(state, _ref3) {
|
|
135
154
|
var {
|
|
136
155
|
type,
|
|
137
156
|
submission,
|
|
138
157
|
intent
|
|
139
|
-
} =
|
|
158
|
+
} = _ref3;
|
|
140
159
|
if (type === 'server') {
|
|
141
160
|
return state;
|
|
142
161
|
}
|
|
@@ -172,13 +191,13 @@ var actionHandlers = {
|
|
|
172
191
|
insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
|
|
173
192
|
return util.updateValueAtPath(value, options.name, list);
|
|
174
193
|
},
|
|
175
|
-
onUpdate(state$1,
|
|
194
|
+
onUpdate(state$1, _ref4) {
|
|
176
195
|
var _intent$payload$index;
|
|
177
196
|
var {
|
|
178
197
|
type,
|
|
179
198
|
submission,
|
|
180
199
|
intent
|
|
181
|
-
} =
|
|
200
|
+
} = _ref4;
|
|
182
201
|
if (type === 'server') {
|
|
183
202
|
return state$1;
|
|
184
203
|
}
|
|
@@ -214,12 +233,12 @@ var actionHandlers = {
|
|
|
214
233
|
removeItem(list, options.index);
|
|
215
234
|
return util.updateValueAtPath(value, options.name, list);
|
|
216
235
|
},
|
|
217
|
-
onUpdate(state$1,
|
|
236
|
+
onUpdate(state$1, _ref5) {
|
|
218
237
|
var {
|
|
219
238
|
type,
|
|
220
239
|
submission,
|
|
221
240
|
intent
|
|
222
|
-
} =
|
|
241
|
+
} = _ref5;
|
|
223
242
|
if (type === 'server') {
|
|
224
243
|
return state$1;
|
|
225
244
|
}
|
|
@@ -258,12 +277,12 @@ var actionHandlers = {
|
|
|
258
277
|
reorderItems(list, options.from, options.to);
|
|
259
278
|
return util.updateValueAtPath(value, options.name, list);
|
|
260
279
|
},
|
|
261
|
-
onUpdate(state$1,
|
|
280
|
+
onUpdate(state$1, _ref6) {
|
|
262
281
|
var {
|
|
263
282
|
type,
|
|
264
283
|
submission,
|
|
265
284
|
intent
|
|
266
|
-
} =
|
|
285
|
+
} = _ref6;
|
|
267
286
|
if (type === 'server') {
|
|
268
287
|
return state$1;
|
|
269
288
|
}
|
package/dist/future/intent.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { objectSpread2 as _objectSpread2 } from '../_virtual/_rollupPluginBabelHelpers.mjs';
|
|
2
2
|
import { getPathSegments, getRelativePath, isPlainObject, appendPathSegment } from '@conform-to/dom/future';
|
|
3
|
-
import { isOptional,
|
|
4
|
-
import { getDefaultListKey } from './state.mjs';
|
|
3
|
+
import { isOptional, isUndefined, isNullable, merge, appendUniqueItem, updateValueAtPath, isString, getArrayAtPath, createPathIndexUpdater, compactMap, generateUniqueKey, isNumber, transformKeys } from './util.mjs';
|
|
4
|
+
import { initializeState, getDefaultListKey } from './state.mjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Serializes intent to string format: "type" or "type(payload)".
|
|
@@ -88,22 +88,38 @@ function updateListKeys() {
|
|
|
88
88
|
*/
|
|
89
89
|
var actionHandlers = {
|
|
90
90
|
reset: {
|
|
91
|
-
|
|
92
|
-
return null;
|
|
91
|
+
validatePayload(options) {
|
|
92
|
+
return isOptional(options, isPlainObject) && (isUndefined(options === null || options === void 0 ? void 0 : options.defaultValue) || isNullable(options === null || options === void 0 ? void 0 : options.defaultValue, isPlainObject));
|
|
93
|
+
},
|
|
94
|
+
onApply(_, options) {
|
|
95
|
+
var {
|
|
96
|
+
defaultValue
|
|
97
|
+
} = options !== null && options !== void 0 ? options : {};
|
|
98
|
+
return defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null;
|
|
99
|
+
},
|
|
100
|
+
onUpdate(_, _ref) {
|
|
101
|
+
var _intent$payload;
|
|
102
|
+
var {
|
|
103
|
+
intent
|
|
104
|
+
} = _ref;
|
|
105
|
+
var defaultValue = (_intent$payload = intent.payload) === null || _intent$payload === void 0 ? void 0 : _intent$payload.defaultValue;
|
|
106
|
+
return merge(initializeState(), {
|
|
107
|
+
serverIntendedValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : defaultValue === null ? {} : null
|
|
108
|
+
});
|
|
93
109
|
}
|
|
94
110
|
},
|
|
95
111
|
validate: {
|
|
96
112
|
validatePayload(name) {
|
|
97
113
|
return isOptional(name, isString);
|
|
98
114
|
},
|
|
99
|
-
onUpdate(state,
|
|
100
|
-
var _intent$
|
|
115
|
+
onUpdate(state, _ref2) {
|
|
116
|
+
var _intent$payload2;
|
|
101
117
|
var {
|
|
102
118
|
submission,
|
|
103
119
|
intent,
|
|
104
120
|
error
|
|
105
|
-
} =
|
|
106
|
-
var name = (_intent$
|
|
121
|
+
} = _ref2;
|
|
122
|
+
var name = (_intent$payload2 = intent.payload) !== null && _intent$payload2 !== void 0 ? _intent$payload2 : '';
|
|
107
123
|
var basePath = getPathSegments(name);
|
|
108
124
|
var allFields = error ?
|
|
109
125
|
// Consider fields / fieldset with errors as touched too
|
|
@@ -125,14 +141,17 @@ var actionHandlers = {
|
|
|
125
141
|
return isPlainObject(options) && isOptional(options.name, isString) && isOptional(options.index, isNumber) && !isUndefined(options.value);
|
|
126
142
|
},
|
|
127
143
|
onApply(value, options) {
|
|
128
|
-
|
|
144
|
+
var _options$value;
|
|
145
|
+
var name = appendPathSegment(options.name, options.index);
|
|
146
|
+
var newValue = (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : name === '' ? {} : null;
|
|
147
|
+
return updateValueAtPath(value, name, newValue);
|
|
129
148
|
},
|
|
130
|
-
onUpdate(state,
|
|
149
|
+
onUpdate(state, _ref3) {
|
|
131
150
|
var {
|
|
132
151
|
type,
|
|
133
152
|
submission,
|
|
134
153
|
intent
|
|
135
|
-
} =
|
|
154
|
+
} = _ref3;
|
|
136
155
|
if (type === 'server') {
|
|
137
156
|
return state;
|
|
138
157
|
}
|
|
@@ -168,13 +187,13 @@ var actionHandlers = {
|
|
|
168
187
|
insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length);
|
|
169
188
|
return updateValueAtPath(value, options.name, list);
|
|
170
189
|
},
|
|
171
|
-
onUpdate(state,
|
|
190
|
+
onUpdate(state, _ref4) {
|
|
172
191
|
var _intent$payload$index;
|
|
173
192
|
var {
|
|
174
193
|
type,
|
|
175
194
|
submission,
|
|
176
195
|
intent
|
|
177
|
-
} =
|
|
196
|
+
} = _ref4;
|
|
178
197
|
if (type === 'server') {
|
|
179
198
|
return state;
|
|
180
199
|
}
|
|
@@ -210,12 +229,12 @@ var actionHandlers = {
|
|
|
210
229
|
removeItem(list, options.index);
|
|
211
230
|
return updateValueAtPath(value, options.name, list);
|
|
212
231
|
},
|
|
213
|
-
onUpdate(state,
|
|
232
|
+
onUpdate(state, _ref5) {
|
|
214
233
|
var {
|
|
215
234
|
type,
|
|
216
235
|
submission,
|
|
217
236
|
intent
|
|
218
|
-
} =
|
|
237
|
+
} = _ref5;
|
|
219
238
|
if (type === 'server') {
|
|
220
239
|
return state;
|
|
221
240
|
}
|
|
@@ -254,12 +273,12 @@ var actionHandlers = {
|
|
|
254
273
|
reorderItems(list, options.from, options.to);
|
|
255
274
|
return updateValueAtPath(value, options.name, list);
|
|
256
275
|
},
|
|
257
|
-
onUpdate(state,
|
|
276
|
+
onUpdate(state, _ref6) {
|
|
258
277
|
var {
|
|
259
278
|
type,
|
|
260
279
|
submission,
|
|
261
280
|
intent
|
|
262
|
-
} =
|
|
281
|
+
} = _ref6;
|
|
263
282
|
if (type === 'server') {
|
|
264
283
|
return state;
|
|
265
284
|
}
|
package/dist/future/state.d.ts
CHANGED
|
@@ -11,8 +11,8 @@ export declare function updateState<ErrorShape>(state: FormState<ErrorShape>, ac
|
|
|
11
11
|
handlers: Record<string, ActionHandler>;
|
|
12
12
|
reset: () => FormState<ErrorShape>;
|
|
13
13
|
}>): FormState<ErrorShape>;
|
|
14
|
-
export declare function getDefaultValue(context: FormContext<any>, name: string, serialize?: Serialize): string
|
|
15
|
-
export declare function getDefaultOptions(context: FormContext<any>, name: string, serialize?: Serialize): string[]
|
|
14
|
+
export declare function getDefaultValue(context: FormContext<any>, name: string, serialize?: Serialize): string;
|
|
15
|
+
export declare function getDefaultOptions(context: FormContext<any>, name: string, serialize?: Serialize): string[];
|
|
16
16
|
export declare function isDefaultChecked(context: FormContext<any>, name: string, serialize?: Serialize): boolean;
|
|
17
17
|
/**
|
|
18
18
|
* Determine if the field is touched
|
package/dist/future/state.js
CHANGED
|
@@ -74,6 +74,7 @@ function getDefaultValue(context, name) {
|
|
|
74
74
|
if (typeof serializedValue === 'string') {
|
|
75
75
|
return serializedValue;
|
|
76
76
|
}
|
|
77
|
+
return '';
|
|
77
78
|
}
|
|
78
79
|
function getDefaultOptions(context, name) {
|
|
79
80
|
var _ref2, _context$state$server2;
|
|
@@ -86,13 +87,17 @@ function getDefaultOptions(context, name) {
|
|
|
86
87
|
if (typeof serializedValue === 'string') {
|
|
87
88
|
return [serializedValue];
|
|
88
89
|
}
|
|
90
|
+
return [];
|
|
89
91
|
}
|
|
90
92
|
function isDefaultChecked(context, name) {
|
|
91
93
|
var _ref3, _context$state$server3;
|
|
92
94
|
var serialize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : future.serialize;
|
|
93
95
|
var value = future.getValueAtPath((_ref3 = (_context$state$server3 = context.state.serverIntendedValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.clientIntendedValue) !== null && _ref3 !== void 0 ? _ref3 : context.defaultValue, name);
|
|
94
96
|
var serializedValue = serialize(value);
|
|
95
|
-
|
|
97
|
+
if (typeof serializedValue === 'string') {
|
|
98
|
+
return serializedValue === 'on';
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
/**
|
|
@@ -328,15 +333,23 @@ function getField(context, options) {
|
|
|
328
333
|
if (typeof customize !== 'function') {
|
|
329
334
|
return metadata;
|
|
330
335
|
}
|
|
336
|
+
var customMetadata = null;
|
|
331
337
|
return new Proxy(metadata, {
|
|
332
338
|
get(target, prop, receiver) {
|
|
339
|
+
var _customMetadata;
|
|
333
340
|
if (Reflect.has(target, prop)) {
|
|
334
341
|
return Reflect.get(target, prop, receiver);
|
|
335
342
|
}
|
|
336
|
-
|
|
343
|
+
(_customMetadata = customMetadata) !== null && _customMetadata !== void 0 ? _customMetadata : customMetadata = customize(metadata);
|
|
337
344
|
if (Reflect.has(customMetadata, prop)) {
|
|
338
345
|
return Reflect.get(customMetadata, prop, receiver);
|
|
339
346
|
}
|
|
347
|
+
|
|
348
|
+
// Allow React DevTools to inspect the object
|
|
349
|
+
// without throwing errors for internal properties
|
|
350
|
+
if (typeof prop === 'symbol' || prop === '$$typeof') {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
340
353
|
throw new Error("Property \"".concat(String(prop), "\" does not exist on field metadata. ") + "If you have defined the CustomMetadata interface to include \"".concat(String(prop), "\", make sure to also implement it through the \"defineCustomMetadata\" property on <FormOptionsProvider />."));
|
|
341
354
|
}
|
|
342
355
|
});
|
package/dist/future/state.mjs
CHANGED
|
@@ -70,6 +70,7 @@ function getDefaultValue(context, name) {
|
|
|
70
70
|
if (typeof serializedValue === 'string') {
|
|
71
71
|
return serializedValue;
|
|
72
72
|
}
|
|
73
|
+
return '';
|
|
73
74
|
}
|
|
74
75
|
function getDefaultOptions(context, name) {
|
|
75
76
|
var _ref2, _context$state$server2;
|
|
@@ -82,13 +83,17 @@ function getDefaultOptions(context, name) {
|
|
|
82
83
|
if (typeof serializedValue === 'string') {
|
|
83
84
|
return [serializedValue];
|
|
84
85
|
}
|
|
86
|
+
return [];
|
|
85
87
|
}
|
|
86
88
|
function isDefaultChecked(context, name) {
|
|
87
89
|
var _ref3, _context$state$server3;
|
|
88
90
|
var serialize$1 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : serialize;
|
|
89
91
|
var value = getValueAtPath((_ref3 = (_context$state$server3 = context.state.serverIntendedValue) !== null && _context$state$server3 !== void 0 ? _context$state$server3 : context.state.clientIntendedValue) !== null && _ref3 !== void 0 ? _ref3 : context.defaultValue, name);
|
|
90
92
|
var serializedValue = serialize$1(value);
|
|
91
|
-
|
|
93
|
+
if (typeof serializedValue === 'string') {
|
|
94
|
+
return serializedValue === 'on';
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/**
|
|
@@ -324,15 +329,23 @@ function getField(context, options) {
|
|
|
324
329
|
if (typeof customize !== 'function') {
|
|
325
330
|
return metadata;
|
|
326
331
|
}
|
|
332
|
+
var customMetadata = null;
|
|
327
333
|
return new Proxy(metadata, {
|
|
328
334
|
get(target, prop, receiver) {
|
|
335
|
+
var _customMetadata;
|
|
329
336
|
if (Reflect.has(target, prop)) {
|
|
330
337
|
return Reflect.get(target, prop, receiver);
|
|
331
338
|
}
|
|
332
|
-
|
|
339
|
+
(_customMetadata = customMetadata) !== null && _customMetadata !== void 0 ? _customMetadata : customMetadata = customize(metadata);
|
|
333
340
|
if (Reflect.has(customMetadata, prop)) {
|
|
334
341
|
return Reflect.get(customMetadata, prop, receiver);
|
|
335
342
|
}
|
|
343
|
+
|
|
344
|
+
// Allow React DevTools to inspect the object
|
|
345
|
+
// without throwing errors for internal properties
|
|
346
|
+
if (typeof prop === 'symbol' || prop === '$$typeof') {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
336
349
|
throw new Error("Property \"".concat(String(prop), "\" does not exist on field metadata. ") + "If you have defined the CustomMetadata interface to include \"".concat(String(prop), "\", make sure to also implement it through the \"defineCustomMetadata\" property on <FormOptionsProvider />."));
|
|
337
350
|
}
|
|
338
351
|
});
|
package/dist/future/types.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export type Control = {
|
|
|
36
36
|
* Registers the base input element(s). Accepts a single input or an array for groups.
|
|
37
37
|
*/
|
|
38
38
|
register: (element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLCollectionOf<HTMLInputElement> | NodeListOf<HTMLInputElement> | null | undefined) => void;
|
|
39
|
+
/**
|
|
40
|
+
* A ref object containing the form element associated with the registered input.
|
|
41
|
+
* Use this with hooks like useFormData() and useIntent().
|
|
42
|
+
*/
|
|
43
|
+
formRef: React.RefObject<HTMLFormElement | null>;
|
|
39
44
|
/**
|
|
40
45
|
* Programmatically updates the input value and emits
|
|
41
46
|
* both [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) and
|
|
@@ -64,9 +69,9 @@ export type UseFormDataOptions = {
|
|
|
64
69
|
*/
|
|
65
70
|
acceptFiles?: boolean;
|
|
66
71
|
};
|
|
67
|
-
export type DefaultValue<
|
|
68
|
-
[Key in keyof
|
|
69
|
-
} | null |
|
|
72
|
+
export type DefaultValue<Shape> = Shape extends Record<string, any> ? {
|
|
73
|
+
[Key in keyof Shape]?: DefaultValue<Shape[Key]>;
|
|
74
|
+
} | null : Shape extends Array<infer Item> ? Array<DefaultValue<Item>> | null : Shape extends File | File[] ? null : Shape | string | null;
|
|
70
75
|
export type FormState<ErrorShape extends BaseErrorShape = DefaultErrorShape> = {
|
|
71
76
|
/** Unique identifier that changes on form reset to trigger reset side effects */
|
|
72
77
|
resetKey: string;
|
|
@@ -117,7 +122,7 @@ export type GlobalFormOptions = {
|
|
|
117
122
|
*/
|
|
118
123
|
defineCustomMetadata?: CustomMetadataDefinition;
|
|
119
124
|
};
|
|
120
|
-
export type FormOptions<FormShape, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined> = {
|
|
125
|
+
export type FormOptions<FormShape extends Record<string, any> = Record<string, any>, ErrorShape extends BaseErrorShape = string extends BaseErrorShape ? string : BaseErrorShape, Value = undefined> = {
|
|
121
126
|
/** Optional form identifier. If not provided, a unique ID is automatically generated. */
|
|
122
127
|
id?: string;
|
|
123
128
|
/** Optional key for form state reset. When the key changes, the form resets to its initial state. */
|
|
@@ -186,20 +191,39 @@ export type UnknownIntent = {
|
|
|
186
191
|
export type UnknownArgs<Args extends any[]> = {
|
|
187
192
|
[Key in keyof Args]: unknown;
|
|
188
193
|
};
|
|
189
|
-
export interface IntentDispatcher {
|
|
194
|
+
export interface IntentDispatcher<FormShape extends Record<string, any> = Record<string, any>> {
|
|
190
195
|
/**
|
|
191
196
|
* Validate the whole form or a specific field?
|
|
192
197
|
*/
|
|
193
198
|
validate(name?: string): void;
|
|
194
199
|
/**
|
|
195
|
-
* Reset the form to
|
|
200
|
+
* Reset the form to a specific default value.
|
|
201
|
+
*
|
|
202
|
+
* @param options.defaultValue - The value to reset the form to. Pass `null` to clear all fields, or omit to reset to the initial default value from `useForm`.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```tsx
|
|
206
|
+
* // Reset to initial default value
|
|
207
|
+
* intent.reset()
|
|
208
|
+
*
|
|
209
|
+
* // Clear all fields
|
|
210
|
+
* intent.reset({ defaultValue: null })
|
|
211
|
+
*
|
|
212
|
+
* // Restore to a specific snapshot
|
|
213
|
+
* intent.reset({ defaultValue: snapshotValue })
|
|
214
|
+
* ```
|
|
196
215
|
*/
|
|
197
|
-
reset(
|
|
216
|
+
reset(options?: {
|
|
217
|
+
/**
|
|
218
|
+
* The value to reset the form to. If not provided, resets to the default value from `useForm`. Pass `null` to clear all fields instead.
|
|
219
|
+
*/
|
|
220
|
+
defaultValue?: DefaultValue<FormShape>;
|
|
221
|
+
}): void;
|
|
198
222
|
/**
|
|
199
223
|
* Update a field or a fieldset.
|
|
200
224
|
* If you provide a fieldset name, it will update all fields within that fieldset
|
|
201
225
|
*/
|
|
202
|
-
update<FieldShape>(options: {
|
|
226
|
+
update<FieldShape = FormShape>(options: {
|
|
203
227
|
/**
|
|
204
228
|
* The name of the field. If you provide a fieldset name, it will update all fields within that fieldset.
|
|
205
229
|
*/
|
|
@@ -207,11 +231,11 @@ export interface IntentDispatcher {
|
|
|
207
231
|
/**
|
|
208
232
|
* Specify the index of the item to update if the field is an array.
|
|
209
233
|
*/
|
|
210
|
-
index?:
|
|
234
|
+
index?: FieldShape extends Array<any> ? number : unknown extends FieldShape ? number : never;
|
|
211
235
|
/**
|
|
212
236
|
* The new value for the field or fieldset.
|
|
213
237
|
*/
|
|
214
|
-
value:
|
|
238
|
+
value: DefaultValue<FieldShape>;
|
|
215
239
|
}): void;
|
|
216
240
|
/**
|
|
217
241
|
* Insert a new item into an array field.
|
|
@@ -229,9 +253,7 @@ export interface IntentDispatcher {
|
|
|
229
253
|
/**
|
|
230
254
|
* The default value for the new item.
|
|
231
255
|
*/
|
|
232
|
-
defaultValue?:
|
|
233
|
-
Array<infer ItemShape> | null | undefined
|
|
234
|
-
] ? Partial<ItemShape> : never;
|
|
256
|
+
defaultValue?: FieldShape extends Array<infer ItemShape> ? DefaultValue<ItemShape> : never;
|
|
235
257
|
}): void;
|
|
236
258
|
/**
|
|
237
259
|
* Remove an item from an array field.
|
|
@@ -307,12 +329,28 @@ export type BaseMetadata<FieldShape, ErrorShape extends BaseErrorShape> = Valida
|
|
|
307
329
|
errorId: string;
|
|
308
330
|
/** The form's unique identifier for associating field via the `form` attribute. */
|
|
309
331
|
formId: string;
|
|
310
|
-
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
332
|
+
/**
|
|
333
|
+
* The field's default value as a string.
|
|
334
|
+
*
|
|
335
|
+
* Returns an empty string `''` when:
|
|
336
|
+
* - No default value is set (field value is `null` or `undefined`)
|
|
337
|
+
* - The field value cannot be serialized to a string (e.g., objects or arrays)
|
|
338
|
+
*/
|
|
339
|
+
defaultValue: string;
|
|
340
|
+
/**
|
|
341
|
+
* Default selected options for multi-select fields or checkbox group.
|
|
342
|
+
*
|
|
343
|
+
* Returns an empty array `[]` when:
|
|
344
|
+
* - No default options are set (field value is `null` or `undefined`)
|
|
345
|
+
* - The field value cannot be serialized to a string array (e.g., nested objects or arrays of objects)
|
|
346
|
+
*/
|
|
347
|
+
defaultOptions: string[];
|
|
348
|
+
/**
|
|
349
|
+
* Default checked state for checkbox inputs. Returns `true` if the field value is `'on'`.
|
|
350
|
+
*
|
|
351
|
+
* For radio buttons, compare the field's `defaultValue` with the radio button's value attribute instead.
|
|
352
|
+
*/
|
|
353
|
+
defaultChecked: boolean;
|
|
316
354
|
/** Whether this field has been touched (through intent.validate() or the shouldValidate option). */
|
|
317
355
|
touched: boolean;
|
|
318
356
|
/** Whether this field currently has no validation errors. */
|
package/dist/future/util.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ValidateHandler, ValidateResult } from './types';
|
|
|
4
4
|
export declare function isUndefined(value: unknown): value is undefined;
|
|
5
5
|
export declare function isString(value: unknown): value is string;
|
|
6
6
|
export declare function isNumber(value: unknown): value is number;
|
|
7
|
+
export declare function isNullable<T>(value: unknown, typeGuard: (value: unknown) => value is T): value is T | null;
|
|
7
8
|
export declare function isOptional<T>(value: unknown, typeGuard: (value: unknown) => value is T): value is T | undefined;
|
|
8
9
|
export declare function getArrayAtPath<Type>(formValue: Record<string, Type> | null, name: string): Array<Type>;
|
|
9
10
|
/**
|
package/dist/future/util.js
CHANGED
|
@@ -13,6 +13,9 @@ function isString(value) {
|
|
|
13
13
|
function isNumber(value) {
|
|
14
14
|
return typeof value === 'number';
|
|
15
15
|
}
|
|
16
|
+
function isNullable(value, typeGuard) {
|
|
17
|
+
return value === null || typeGuard(value);
|
|
18
|
+
}
|
|
16
19
|
function isOptional(value, typeGuard) {
|
|
17
20
|
return isUndefined(value) || typeGuard(value);
|
|
18
21
|
}
|
|
@@ -188,6 +191,7 @@ exports.compactMap = compactMap;
|
|
|
188
191
|
exports.createPathIndexUpdater = createPathIndexUpdater;
|
|
189
192
|
exports.generateUniqueKey = generateUniqueKey;
|
|
190
193
|
exports.getArrayAtPath = getArrayAtPath;
|
|
194
|
+
exports.isNullable = isNullable;
|
|
191
195
|
exports.isNumber = isNumber;
|
|
192
196
|
exports.isOptional = isOptional;
|
|
193
197
|
exports.isString = isString;
|
package/dist/future/util.mjs
CHANGED
|
@@ -9,6 +9,9 @@ function isString(value) {
|
|
|
9
9
|
function isNumber(value) {
|
|
10
10
|
return typeof value === 'number';
|
|
11
11
|
}
|
|
12
|
+
function isNullable(value, typeGuard) {
|
|
13
|
+
return value === null || typeGuard(value);
|
|
14
|
+
}
|
|
12
15
|
function isOptional(value, typeGuard) {
|
|
13
16
|
return isUndefined(value) || typeGuard(value);
|
|
14
17
|
}
|
|
@@ -179,4 +182,4 @@ function generateUniqueKey() {
|
|
|
179
182
|
return Math.trunc(Date.now() * Math.random()).toString(36);
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
export { appendUniqueItem, compactMap, createPathIndexUpdater, generateUniqueKey, getArrayAtPath, isNumber, isOptional, isString, isUndefined, merge, normalizeFormError, normalizeValidateResult, resolveStandardSchemaResult, resolveValidateResult, transformKeys, updateValueAtPath };
|
|
185
|
+
export { appendUniqueItem, compactMap, createPathIndexUpdater, generateUniqueKey, getArrayAtPath, isNullable, isNumber, isOptional, isString, isUndefined, merge, normalizeFormError, normalizeValidateResult, resolveStandardSchemaResult, resolveValidateResult, transformKeys, updateValueAtPath };
|
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
|
+
"version": "1.13.0",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"url": "https://github.com/edmundhung/conform/issues"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@conform-to/dom": "1.
|
|
44
|
+
"@conform-to/dom": "1.13.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@babel/core": "^7.17.8",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"react-dom": "^18.2.0",
|
|
57
57
|
"rollup-plugin-copy": "^3.4.0",
|
|
58
58
|
"rollup": "^2.79.1",
|
|
59
|
-
"
|
|
59
|
+
"typescript": "^5.8.3",
|
|
60
|
+
"vitest-browser-react": "^1.0.1"
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|
|
62
63
|
"react": ">=18"
|