@mantine/form 9.0.0-alpha.5 → 9.0.0-alpha.7

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.
Files changed (173) hide show
  1. package/cjs/FormProvider/FormProvider.cjs +26 -21
  2. package/cjs/FormProvider/FormProvider.cjs.map +1 -1
  3. package/cjs/_virtual/_rolldown/runtime.cjs +24 -0
  4. package/cjs/actions/actions.cjs +86 -117
  5. package/cjs/actions/actions.cjs.map +1 -1
  6. package/cjs/form-index.cjs +5 -5
  7. package/cjs/form-index.cjs.map +1 -1
  8. package/cjs/get-input-on-change/get-input-on-change.cjs +15 -25
  9. package/cjs/get-input-on-change/get-input-on-change.cjs.map +1 -1
  10. package/cjs/get-status/get-status.cjs +11 -11
  11. package/cjs/get-status/get-status.cjs.map +1 -1
  12. package/cjs/hooks/use-form-errors/filter-errors/filter-errors.cjs +11 -15
  13. package/cjs/hooks/use-form-errors/filter-errors/filter-errors.cjs.map +1 -1
  14. package/cjs/hooks/use-form-errors/use-form-errors.cjs +39 -48
  15. package/cjs/hooks/use-form-errors/use-form-errors.cjs.map +1 -1
  16. package/cjs/hooks/use-form-list/use-form-list.cjs +47 -51
  17. package/cjs/hooks/use-form-list/use-form-list.cjs.map +1 -1
  18. package/cjs/hooks/use-form-status/use-form-status.cjs +95 -128
  19. package/cjs/hooks/use-form-status/use-form-status.cjs.map +1 -1
  20. package/cjs/hooks/use-form-validating/use-form-validating.cjs +49 -51
  21. package/cjs/hooks/use-form-validating/use-form-validating.cjs.map +1 -1
  22. package/cjs/hooks/use-form-values/use-form-values.cjs +92 -107
  23. package/cjs/hooks/use-form-values/use-form-values.cjs.map +1 -1
  24. package/cjs/hooks/use-form-watch/use-form-watch.cjs +41 -57
  25. package/cjs/hooks/use-form-watch/use-form-watch.cjs.map +1 -1
  26. package/cjs/index.cjs +35 -38
  27. package/cjs/lists/change-error-indices.cjs +34 -41
  28. package/cjs/lists/change-error-indices.cjs.map +1 -1
  29. package/cjs/lists/clear-list-state.cjs +11 -15
  30. package/cjs/lists/clear-list-state.cjs.map +1 -1
  31. package/cjs/lists/reorder-errors.cjs +30 -32
  32. package/cjs/lists/reorder-errors.cjs.map +1 -1
  33. package/cjs/paths/get-data-path.cjs +6 -6
  34. package/cjs/paths/get-data-path.cjs.map +1 -1
  35. package/cjs/paths/get-path.cjs +14 -19
  36. package/cjs/paths/get-path.cjs.map +1 -1
  37. package/cjs/paths/get-splitted-path.cjs +7 -9
  38. package/cjs/paths/get-splitted-path.cjs.map +1 -1
  39. package/cjs/paths/insert-path.cjs +12 -15
  40. package/cjs/paths/insert-path.cjs.map +1 -1
  41. package/cjs/paths/remove-path.cjs +10 -17
  42. package/cjs/paths/remove-path.cjs.map +1 -1
  43. package/cjs/paths/reorder-path.cjs +14 -17
  44. package/cjs/paths/reorder-path.cjs.map +1 -1
  45. package/cjs/paths/replace-path.cjs +13 -18
  46. package/cjs/paths/replace-path.cjs.map +1 -1
  47. package/cjs/paths/set-path.cjs +22 -26
  48. package/cjs/paths/set-path.cjs.map +1 -1
  49. package/cjs/schema-resolver.cjs +24 -0
  50. package/cjs/schema-resolver.cjs.map +1 -0
  51. package/cjs/use-field.cjs +94 -131
  52. package/cjs/use-field.cjs.map +1 -1
  53. package/cjs/use-form.cjs +316 -379
  54. package/cjs/use-form.cjs.map +1 -1
  55. package/cjs/validate/get-rule-for-path.cjs +16 -24
  56. package/cjs/validate/get-rule-for-path.cjs.map +1 -1
  57. package/cjs/validate/should-validate-on-change.cjs +10 -17
  58. package/cjs/validate/should-validate-on-change.cjs.map +1 -1
  59. package/cjs/validate/validate-field-value.cjs +58 -54
  60. package/cjs/validate/validate-field-value.cjs.map +1 -1
  61. package/cjs/validate/validate-values.cjs +77 -108
  62. package/cjs/validate/validate-values.cjs.map +1 -1
  63. package/cjs/validators/has-length/has-length.cjs +17 -27
  64. package/cjs/validators/has-length/has-length.cjs.map +1 -1
  65. package/cjs/validators/is-email/is-email.cjs +7 -8
  66. package/cjs/validators/is-email/is-email.cjs.map +1 -1
  67. package/cjs/validators/is-in-range/is-in-range.cjs +13 -19
  68. package/cjs/validators/is-in-range/is-in-range.cjs.map +1 -1
  69. package/cjs/validators/is-json-string/is-json-string.cjs +15 -17
  70. package/cjs/validators/is-json-string/is-json-string.cjs.map +1 -1
  71. package/cjs/validators/is-not-empty/is-not-empty.cjs +13 -21
  72. package/cjs/validators/is-not-empty/is-not-empty.cjs.map +1 -1
  73. package/cjs/validators/is-not-empty-html/is-not-empty-html.cjs +11 -13
  74. package/cjs/validators/is-not-empty-html/is-not-empty-html.cjs.map +1 -1
  75. package/cjs/validators/is-one-of/is-one-of.cjs +9 -9
  76. package/cjs/validators/is-one-of/is-one-of.cjs.map +1 -1
  77. package/cjs/validators/is-url/is-url.cjs +25 -33
  78. package/cjs/validators/is-url/is-url.cjs.map +1 -1
  79. package/cjs/validators/matches/matches.cjs +10 -12
  80. package/cjs/validators/matches/matches.cjs.map +1 -1
  81. package/cjs/validators/matches-field/matches-field.cjs +10 -12
  82. package/cjs/validators/matches-field/matches-field.cjs.map +1 -1
  83. package/esm/FormProvider/FormProvider.mjs +25 -19
  84. package/esm/FormProvider/FormProvider.mjs.map +1 -1
  85. package/esm/actions/actions.mjs +85 -114
  86. package/esm/actions/actions.mjs.map +1 -1
  87. package/esm/form-index.mjs +5 -3
  88. package/esm/form-index.mjs.map +1 -1
  89. package/esm/get-input-on-change/get-input-on-change.mjs +15 -23
  90. package/esm/get-input-on-change/get-input-on-change.mjs.map +1 -1
  91. package/esm/get-status/get-status.mjs +11 -9
  92. package/esm/get-status/get-status.mjs.map +1 -1
  93. package/esm/hooks/use-form-errors/filter-errors/filter-errors.mjs +11 -13
  94. package/esm/hooks/use-form-errors/filter-errors/filter-errors.mjs.map +1 -1
  95. package/esm/hooks/use-form-errors/use-form-errors.mjs +38 -46
  96. package/esm/hooks/use-form-errors/use-form-errors.mjs.map +1 -1
  97. package/esm/hooks/use-form-list/use-form-list.mjs +46 -49
  98. package/esm/hooks/use-form-list/use-form-list.mjs.map +1 -1
  99. package/esm/hooks/use-form-status/use-form-status.mjs +93 -122
  100. package/esm/hooks/use-form-status/use-form-status.mjs.map +1 -1
  101. package/esm/hooks/use-form-validating/use-form-validating.mjs +48 -49
  102. package/esm/hooks/use-form-validating/use-form-validating.mjs.map +1 -1
  103. package/esm/hooks/use-form-values/use-form-values.mjs +91 -105
  104. package/esm/hooks/use-form-values/use-form-values.mjs.map +1 -1
  105. package/esm/hooks/use-form-watch/use-form-watch.mjs +40 -55
  106. package/esm/hooks/use-form-watch/use-form-watch.mjs.map +1 -1
  107. package/esm/index.mjs +18 -17
  108. package/esm/lists/change-error-indices.mjs +34 -39
  109. package/esm/lists/change-error-indices.mjs.map +1 -1
  110. package/esm/lists/clear-list-state.mjs +11 -13
  111. package/esm/lists/clear-list-state.mjs.map +1 -1
  112. package/esm/lists/reorder-errors.mjs +30 -30
  113. package/esm/lists/reorder-errors.mjs.map +1 -1
  114. package/esm/paths/get-data-path.mjs +6 -4
  115. package/esm/paths/get-data-path.mjs.map +1 -1
  116. package/esm/paths/get-path.mjs +14 -17
  117. package/esm/paths/get-path.mjs.map +1 -1
  118. package/esm/paths/get-splitted-path.mjs +7 -7
  119. package/esm/paths/get-splitted-path.mjs.map +1 -1
  120. package/esm/paths/insert-path.mjs +12 -13
  121. package/esm/paths/insert-path.mjs.map +1 -1
  122. package/esm/paths/remove-path.mjs +10 -15
  123. package/esm/paths/remove-path.mjs.map +1 -1
  124. package/esm/paths/reorder-path.mjs +14 -15
  125. package/esm/paths/reorder-path.mjs.map +1 -1
  126. package/esm/paths/replace-path.mjs +13 -16
  127. package/esm/paths/replace-path.mjs.map +1 -1
  128. package/esm/paths/set-path.mjs +21 -24
  129. package/esm/paths/set-path.mjs.map +1 -1
  130. package/esm/schema-resolver.mjs +24 -0
  131. package/esm/schema-resolver.mjs.map +1 -0
  132. package/esm/use-field.mjs +93 -129
  133. package/esm/use-field.mjs.map +1 -1
  134. package/esm/use-form.mjs +315 -377
  135. package/esm/use-form.mjs.map +1 -1
  136. package/esm/validate/get-rule-for-path.mjs +16 -22
  137. package/esm/validate/get-rule-for-path.mjs.map +1 -1
  138. package/esm/validate/should-validate-on-change.mjs +10 -15
  139. package/esm/validate/should-validate-on-change.mjs.map +1 -1
  140. package/esm/validate/validate-field-value.mjs +58 -52
  141. package/esm/validate/validate-field-value.mjs.map +1 -1
  142. package/esm/validate/validate-values.mjs +77 -106
  143. package/esm/validate/validate-values.mjs.map +1 -1
  144. package/esm/validators/has-length/has-length.mjs +17 -25
  145. package/esm/validators/has-length/has-length.mjs.map +1 -1
  146. package/esm/validators/is-email/is-email.mjs +7 -6
  147. package/esm/validators/is-email/is-email.mjs.map +1 -1
  148. package/esm/validators/is-in-range/is-in-range.mjs +13 -17
  149. package/esm/validators/is-in-range/is-in-range.mjs.map +1 -1
  150. package/esm/validators/is-json-string/is-json-string.mjs +15 -15
  151. package/esm/validators/is-json-string/is-json-string.mjs.map +1 -1
  152. package/esm/validators/is-not-empty/is-not-empty.mjs +13 -19
  153. package/esm/validators/is-not-empty/is-not-empty.mjs.map +1 -1
  154. package/esm/validators/is-not-empty-html/is-not-empty-html.mjs +11 -11
  155. package/esm/validators/is-not-empty-html/is-not-empty-html.mjs.map +1 -1
  156. package/esm/validators/is-one-of/is-one-of.mjs +9 -7
  157. package/esm/validators/is-one-of/is-one-of.mjs.map +1 -1
  158. package/esm/validators/is-url/is-url.mjs +25 -31
  159. package/esm/validators/is-url/is-url.mjs.map +1 -1
  160. package/esm/validators/matches/matches.mjs +10 -10
  161. package/esm/validators/matches/matches.mjs.map +1 -1
  162. package/esm/validators/matches-field/matches-field.mjs +10 -10
  163. package/esm/validators/matches-field/matches-field.mjs.map +1 -1
  164. package/lib/hooks/use-form-status/use-form-status.d.ts +2 -2
  165. package/lib/hooks/use-form-values/use-form-values.d.ts +9 -9
  166. package/lib/hooks/use-form-watch/use-form-watch.d.ts +5 -5
  167. package/lib/index.d.mts +1 -0
  168. package/lib/index.d.ts +1 -0
  169. package/lib/schema-resolver.d.ts +5 -0
  170. package/lib/types.d.ts +19 -18
  171. package/package.json +5 -1
  172. package/cjs/index.cjs.map +0 -1
  173. package/esm/index.mjs.map +0 -1
package/esm/use-form.mjs CHANGED
@@ -1,379 +1,317 @@
1
- 'use client';
2
- import { useState, useRef, useCallback, useMemo } from 'react';
3
- import { useFormActions } from './actions/actions.mjs';
4
- import { getInputOnChange } from './get-input-on-change/get-input-on-change.mjs';
5
- import { useFormErrors } from './hooks/use-form-errors/use-form-errors.mjs';
6
- import { useFormList } from './hooks/use-form-list/use-form-list.mjs';
7
- import { useFormStatus } from './hooks/use-form-status/use-form-status.mjs';
8
- import { useFormValidating } from './hooks/use-form-validating/use-form-validating.mjs';
9
- import { useFormValues } from './hooks/use-form-values/use-form-values.mjs';
10
- import { useFormWatch } from './hooks/use-form-watch/use-form-watch.mjs';
11
- import { getPath } from './paths/get-path.mjs';
12
- import 'klona/full';
13
- import { getDataPath } from './paths/get-data-path.mjs';
14
- import { validateValues } from './validate/validate-values.mjs';
15
- import { validateFieldValue } from './validate/validate-field-value.mjs';
16
- import { shouldValidateOnChange } from './validate/should-validate-on-change.mjs';
17
-
18
- function useForm({
19
- name,
20
- mode = "controlled",
21
- initialValues,
22
- initialErrors = {},
23
- initialDirty = {},
24
- initialTouched = {},
25
- clearInputErrorOnChange = true,
26
- validateInputOnChange = false,
27
- validateInputOnBlur = false,
28
- onValuesChange,
29
- transformValues = ((values) => values),
30
- enhanceGetInputProps,
31
- validate: rules,
32
- onSubmitPreventDefault = "always",
33
- touchTrigger = "change",
34
- cascadeUpdates = false,
35
- validateDebounce = 0,
36
- resolveValidationError = (err) => err instanceof Error ? err.message : String(err)
37
- } = {}) {
38
- const $errors = useFormErrors(initialErrors);
39
- const $values = useFormValues({ initialValues, onValuesChange, mode });
40
- const $status = useFormStatus({ initialDirty, initialTouched, $values, mode });
41
- const $list = useFormList({ $values, $errors, $status });
42
- const $watch = useFormWatch({ $status, cascadeUpdates });
43
- const $validating = useFormValidating();
44
- const [formKey, setFormKey] = useState(0);
45
- const [fieldKeys, setFieldKeys] = useState({});
46
- const [submitting, setSubmitting] = useState(false);
47
- const validateGeneration = useRef(0);
48
- const reset = useCallback(() => {
49
- $values.resetValues();
50
- $errors.clearErrors();
51
- $status.resetDirty();
52
- $status.resetTouched();
53
- $validating.clearValidating();
54
- mode === "uncontrolled" && setFormKey((key2) => key2 + 1);
55
- }, []);
56
- const handleValuesChanges = useCallback(
57
- (previousValues) => {
58
- clearInputErrorOnChange && $errors.clearErrors();
59
- mode === "uncontrolled" && setFormKey((key2) => key2 + 1);
60
- Object.keys($watch.subscribers.current).forEach((path) => {
61
- const value = getPath(path, $values.refValues.current);
62
- const previousValue = getPath(path, previousValues);
63
- if (value !== previousValue) {
64
- $watch.getFieldSubscribers(path).forEach((cb) => cb({ previousValues, updatedValues: $values.refValues.current }));
65
- }
66
- });
67
- },
68
- [clearInputErrorOnChange]
69
- );
70
- const initialize = useCallback(
71
- (values) => {
72
- const previousValues = $values.refValues.current;
73
- $values.initialize(values, () => mode === "uncontrolled" && setFormKey((key2) => key2 + 1));
74
- handleValuesChanges(previousValues);
75
- },
76
- [handleValuesChanges]
77
- );
78
- const debouncedValidateField = useMemo(() => {
79
- const timers = {};
80
- const handleValidation = (path) => {
81
- const signal = $validating.getAbortSignal(path);
82
- const result = validateFieldValue(
83
- path,
84
- rules,
85
- $values.refValues.current,
86
- resolveValidationError,
87
- signal
88
- );
89
- const applyResult = (results) => {
90
- if (signal.aborted) {
91
- return;
92
- }
93
- if (results.hasError) {
94
- $errors.setFieldError(path, results.error);
95
- } else {
96
- $errors.clearFieldError(path);
97
- }
98
- };
99
- const cleanup = () => {
100
- if (!signal.aborted) {
101
- $validating.setFieldValidating(path, false);
102
- }
103
- };
104
- if (result instanceof Promise) {
105
- $validating.setFieldValidating(path, true);
106
- result.then(applyResult).finally(cleanup);
107
- } else {
108
- applyResult(result);
109
- }
110
- };
111
- return (path) => {
112
- clearTimeout(timers[path]);
113
- if (validateDebounce > 0) {
114
- timers[path] = setTimeout(() => handleValidation(path), validateDebounce);
115
- } else {
116
- handleValidation(path);
117
- }
118
- };
119
- }, [validateDebounce, rules, resolveValidationError]);
120
- const setFieldValue = useCallback(
121
- (path, value, options) => {
122
- const shouldValidate = shouldValidateOnChange(path, validateInputOnChange);
123
- const resolvedValue = value instanceof Function ? value(getPath(path, $values.refValues.current)) : value;
124
- $status.setCalculatedFieldDirty(path, resolvedValue);
125
- touchTrigger === "change" && $status.setFieldTouched(path, true);
126
- !shouldValidate && clearInputErrorOnChange && $errors.clearFieldError(path);
127
- $values.setFieldValue({
128
- path,
129
- value,
130
- updateState: mode === "controlled",
131
- subscribers: [
132
- ...$watch.getFieldSubscribers(path),
133
- shouldValidate ? () => debouncedValidateField(String(path)) : null,
134
- options?.forceUpdate !== false && mode !== "controlled" ? () => setFieldKeys((keys) => ({
135
- ...keys,
136
- [path]: (keys[path] || 0) + 1
137
- })) : null
138
- ]
139
- });
140
- },
141
- [onValuesChange, rules, debouncedValidateField]
142
- );
143
- const setValues = useCallback(
144
- (values) => {
145
- const previousValues = $values.refValues.current;
146
- $values.setValues({ values, updateState: mode === "controlled" });
147
- handleValuesChanges(previousValues);
148
- },
149
- [onValuesChange, handleValuesChanges]
150
- );
151
- const validate = useCallback(() => {
152
- const generation = ++validateGeneration.current;
153
- const signal = $validating.getAbortSignal("__form__");
154
- const handleResult = (results) => {
155
- if (generation !== validateGeneration.current) {
156
- return { hasErrors: false, errors: {} };
157
- }
158
- $errors.setErrors(results.errors);
159
- return results;
160
- };
161
- const cleanup = () => {
162
- if (generation === validateGeneration.current) {
163
- $validating.setFormValidating(false);
164
- }
165
- };
166
- const result = validateValues(rules, $values.refValues.current, resolveValidationError, signal);
167
- if (result instanceof Promise) {
168
- $validating.setFormValidating(true);
169
- return result.then(handleResult).finally(cleanup);
170
- }
171
- return handleResult(result);
172
- }, [rules, resolveValidationError]);
173
- const validateField = useCallback(
174
- (path) => {
175
- const signal = $validating.getAbortSignal(String(path));
176
- const applyResult = (results) => {
177
- if (signal.aborted) {
178
- return { hasError: false, error: null };
179
- }
180
- if (results.hasError) {
181
- $errors.setFieldError(path, results.error);
182
- } else {
183
- $errors.clearFieldError(path);
184
- }
185
- return results;
186
- };
187
- const cleanup = () => {
188
- if (!signal.aborted) {
189
- $validating.setFieldValidating(String(path), false);
190
- }
191
- };
192
- const result = validateFieldValue(
193
- path,
194
- rules,
195
- $values.refValues.current,
196
- resolveValidationError,
197
- signal
198
- );
199
- if (result instanceof Promise) {
200
- $validating.setFieldValidating(String(path), true);
201
- return result.then(applyResult).finally(cleanup);
202
- }
203
- return applyResult(result);
204
- },
205
- [rules, resolveValidationError]
206
- );
207
- const getInputProps = (path, { type = "input", withError = true, withFocus, ...otherOptions } = {}) => {
208
- const _withFocus = withFocus ?? type !== "radio";
209
- const onChange = getInputOnChange(
210
- (value) => setFieldValue(path, value, { forceUpdate: false })
211
- );
212
- const payload = { onChange, "data-path": getDataPath(name, path) };
213
- if (withError) {
214
- payload.error = $errors.errorsState[path];
215
- }
216
- if (type === "checkbox") {
217
- payload[mode === "controlled" ? "checked" : "defaultChecked"] = getPath(
218
- path,
219
- $values.refValues.current
220
- );
221
- } else if (type === "radio") {
222
- payload[mode === "controlled" ? "checked" : "defaultChecked"] = getPath(path, $values.refValues.current) === otherOptions.value;
223
- payload.value = otherOptions.value;
224
- } else {
225
- payload[mode === "controlled" ? "value" : "defaultValue"] = getPath(
226
- path,
227
- $values.refValues.current
228
- );
229
- }
230
- if (_withFocus) {
231
- payload.onFocus = () => $status.setFieldTouched(path, true);
232
- payload.onBlur = () => {
233
- if (shouldValidateOnChange(path, validateInputOnBlur)) {
234
- debouncedValidateField(String(path));
235
- }
236
- };
237
- }
238
- return Object.assign(
239
- payload,
240
- enhanceGetInputProps?.({
241
- inputProps: payload,
242
- field: path,
243
- options: { type, withError, withFocus: _withFocus, ...otherOptions },
244
- form
245
- })
246
- );
247
- };
248
- const onSubmit = (handleSubmit, handleValidationFailure) => (event) => {
249
- if (onSubmitPreventDefault === "always") {
250
- event?.preventDefault();
251
- }
252
- setSubmitting(true);
253
- const handleValidation = (results) => {
254
- if (results.hasErrors) {
255
- if (onSubmitPreventDefault === "validation-failed") {
256
- event?.preventDefault();
257
- }
258
- handleValidationFailure?.(results.errors, $values.refValues.current, event);
259
- setSubmitting(false);
260
- } else {
261
- const submitResult = handleSubmit?.(
262
- transformValues($values.refValues.current),
263
- event
264
- );
265
- if (submitResult instanceof Promise) {
266
- submitResult.finally(() => setSubmitting(false));
267
- } else {
268
- setSubmitting(false);
269
- }
270
- }
271
- };
272
- const result = validate();
273
- if (result instanceof Promise) {
274
- result.then(handleValidation).catch(() => {
275
- setSubmitting(false);
276
- });
277
- } else {
278
- handleValidation(result);
279
- }
280
- };
281
- const getTransformedValues = (input) => transformValues(input || $values.refValues.current);
282
- const onReset = useCallback((event) => {
283
- event.preventDefault();
284
- reset();
285
- }, []);
286
- const isValid = useCallback(
287
- (path) => {
288
- const signal = new AbortController().signal;
289
- if (path) {
290
- const result2 = validateFieldValue(
291
- path,
292
- rules,
293
- $values.refValues.current,
294
- resolveValidationError,
295
- signal
296
- );
297
- if (result2 instanceof Promise) {
298
- return result2.then((r) => !r.hasError);
299
- }
300
- return !result2.hasError;
301
- }
302
- const result = validateValues(
303
- rules,
304
- $values.refValues.current,
305
- resolveValidationError,
306
- signal
307
- );
308
- if (result instanceof Promise) {
309
- return result.then((r) => !r.hasErrors);
310
- }
311
- return !result.hasErrors;
312
- },
313
- [rules, resolveValidationError]
314
- );
315
- const key = (path) => `${formKey}-${String(path)}-${fieldKeys[String(path)] || 0}`;
316
- const getInputNode = useCallback(
317
- (path) => document.querySelector(`[data-path="${getDataPath(name, path)}"]`),
318
- []
319
- );
320
- const resetField = useCallback(
321
- (path) => {
322
- $values.resetField(path, [
323
- mode !== "controlled" ? () => setFieldKeys((keys) => ({
324
- ...keys,
325
- [path]: (keys[path] || 0) + 1
326
- })) : null
327
- ]);
328
- },
329
- [$values.resetField, mode, setFieldKeys]
330
- );
331
- const form = {
332
- watch: $watch.watch,
333
- initialized: $values.initialized.current,
334
- values: mode === "uncontrolled" ? $values.refValues.current : $values.stateValues,
335
- getValues: $values.getValues,
336
- getInitialValues: $values.getValuesSnapshot,
337
- setInitialValues: $values.setValuesSnapshot,
338
- resetField,
339
- initialize,
340
- setValues,
341
- setFieldValue,
342
- submitting,
343
- setSubmitting,
344
- validating: $validating.validating,
345
- isValidating: $validating.isValidating,
346
- errors: $errors.errorsState,
347
- setErrors: $errors.setErrors,
348
- setFieldError: $errors.setFieldError,
349
- clearFieldError: $errors.clearFieldError,
350
- clearErrors: $errors.clearErrors,
351
- resetDirty: $status.resetDirty,
352
- setTouched: $status.setTouched,
353
- setDirty: $status.setDirty,
354
- isTouched: $status.isTouched,
355
- resetTouched: $status.resetTouched,
356
- isDirty: $status.isDirty,
357
- getTouched: $status.getTouched,
358
- getDirty: $status.getDirty,
359
- reorderListItem: $list.reorderListItem,
360
- insertListItem: $list.insertListItem,
361
- removeListItem: $list.removeListItem,
362
- replaceListItem: $list.replaceListItem,
363
- reset,
364
- validate,
365
- validateField,
366
- getInputProps,
367
- onSubmit,
368
- onReset,
369
- isValid,
370
- getTransformedValues,
371
- key,
372
- getInputNode
373
- };
374
- useFormActions(name, form);
375
- return form;
1
+ "use client";
2
+ import { useFormActions } from "./actions/actions.mjs";
3
+ import { getInputOnChange } from "./get-input-on-change/get-input-on-change.mjs";
4
+ import { useFormErrors } from "./hooks/use-form-errors/use-form-errors.mjs";
5
+ import { getPath } from "./paths/get-path.mjs";
6
+ import { getDataPath } from "./paths/get-data-path.mjs";
7
+ import { useFormList } from "./hooks/use-form-list/use-form-list.mjs";
8
+ import { useFormStatus } from "./hooks/use-form-status/use-form-status.mjs";
9
+ import { useFormValidating } from "./hooks/use-form-validating/use-form-validating.mjs";
10
+ import { useFormValues } from "./hooks/use-form-values/use-form-values.mjs";
11
+ import { useFormWatch } from "./hooks/use-form-watch/use-form-watch.mjs";
12
+ import { validateValues } from "./validate/validate-values.mjs";
13
+ import { validateFieldValue } from "./validate/validate-field-value.mjs";
14
+ import { shouldValidateOnChange } from "./validate/should-validate-on-change.mjs";
15
+ import { useCallback, useMemo, useRef, useState } from "react";
16
+ //#region packages/@mantine/form/src/use-form.ts
17
+ function useForm({ name, mode = "controlled", initialValues, initialErrors = {}, initialDirty = {}, initialTouched = {}, clearInputErrorOnChange = true, validateInputOnChange = false, validateInputOnBlur = false, onValuesChange, transformValues = ((values) => values), enhanceGetInputProps, validate: rules, onSubmitPreventDefault = "always", touchTrigger = "change", cascadeUpdates = false, validateDebounce = 0, resolveValidationError = (err) => err instanceof Error ? err.message : String(err) } = {}) {
18
+ const $errors = useFormErrors(initialErrors);
19
+ const $values = useFormValues({
20
+ initialValues,
21
+ onValuesChange,
22
+ mode
23
+ });
24
+ const $status = useFormStatus({
25
+ initialDirty,
26
+ initialTouched,
27
+ $values,
28
+ mode
29
+ });
30
+ const $list = useFormList({
31
+ $values,
32
+ $errors,
33
+ $status
34
+ });
35
+ const $watch = useFormWatch({
36
+ $status,
37
+ cascadeUpdates
38
+ });
39
+ const $validating = useFormValidating();
40
+ const [formKey, setFormKey] = useState(0);
41
+ const [fieldKeys, setFieldKeys] = useState({});
42
+ const [submitting, setSubmitting] = useState(false);
43
+ const validateGeneration = useRef(0);
44
+ const reset = useCallback(() => {
45
+ $values.resetValues();
46
+ $errors.clearErrors();
47
+ $status.resetDirty();
48
+ $status.resetTouched();
49
+ $validating.clearValidating();
50
+ mode === "uncontrolled" && setFormKey((key) => key + 1);
51
+ }, []);
52
+ const notifyWatchSubscribers = useCallback((previousValues) => {
53
+ Object.keys($watch.subscribers.current).forEach((path) => {
54
+ if (getPath(path, $values.refValues.current) !== getPath(path, previousValues)) $watch.subscribers.current[path]?.forEach((cb) => cb({
55
+ previousValue: getPath(path, previousValues),
56
+ value: getPath(path, $values.refValues.current),
57
+ touched: $status.isTouched(path),
58
+ dirty: $status.isDirty(path)
59
+ }));
60
+ });
61
+ }, []);
62
+ const handleValuesChanges = useCallback((previousValues) => {
63
+ clearInputErrorOnChange && $errors.clearErrors();
64
+ mode === "uncontrolled" && setFormKey((key) => key + 1);
65
+ notifyWatchSubscribers(previousValues);
66
+ }, [clearInputErrorOnChange, notifyWatchSubscribers]);
67
+ const initialize = useCallback((values) => {
68
+ const previousValues = $values.refValues.current;
69
+ $values.initialize(values, () => mode === "uncontrolled" && setFormKey((key) => key + 1));
70
+ handleValuesChanges(previousValues);
71
+ }, [handleValuesChanges]);
72
+ const debouncedValidateField = useMemo(() => {
73
+ const timers = {};
74
+ const handleValidation = (path) => {
75
+ const signal = $validating.getAbortSignal(path);
76
+ const result = validateFieldValue(path, rules, $values.refValues.current, resolveValidationError, signal);
77
+ const applyResult = (results) => {
78
+ if (signal.aborted) return;
79
+ if (results.hasError) $errors.setFieldError(path, results.error);
80
+ else $errors.clearFieldError(path);
81
+ };
82
+ const cleanup = () => {
83
+ if (!signal.aborted) $validating.setFieldValidating(path, false);
84
+ };
85
+ if (result instanceof Promise) {
86
+ $validating.setFieldValidating(path, true);
87
+ result.then(applyResult).finally(cleanup);
88
+ } else applyResult(result);
89
+ };
90
+ return (path) => {
91
+ clearTimeout(timers[path]);
92
+ if (validateDebounce > 0) timers[path] = setTimeout(() => handleValidation(path), validateDebounce);
93
+ else handleValidation(path);
94
+ };
95
+ }, [
96
+ validateDebounce,
97
+ rules,
98
+ resolveValidationError
99
+ ]);
100
+ const setFieldValue = useCallback((path, value, options) => {
101
+ const shouldValidate = shouldValidateOnChange(path, validateInputOnChange);
102
+ const resolvedValue = value instanceof Function ? value(getPath(path, $values.refValues.current)) : value;
103
+ $status.setCalculatedFieldDirty(path, resolvedValue);
104
+ touchTrigger === "change" && $status.setFieldTouched(path, true);
105
+ !shouldValidate && clearInputErrorOnChange && $errors.clearFieldError(path);
106
+ $values.setFieldValue({
107
+ path,
108
+ value,
109
+ updateState: mode === "controlled",
110
+ subscribers: [
111
+ ...$watch.getFieldSubscribers(path),
112
+ shouldValidate ? () => debouncedValidateField(String(path)) : null,
113
+ options?.forceUpdate !== false && mode !== "controlled" ? () => setFieldKeys((keys) => ({
114
+ ...keys,
115
+ [path]: (keys[path] || 0) + 1
116
+ })) : null
117
+ ]
118
+ });
119
+ }, [
120
+ onValuesChange,
121
+ rules,
122
+ debouncedValidateField
123
+ ]);
124
+ const setValues = useCallback((values) => {
125
+ const previousValues = $values.refValues.current;
126
+ $values.setValues({
127
+ values,
128
+ updateState: mode === "controlled"
129
+ });
130
+ handleValuesChanges(previousValues);
131
+ }, [onValuesChange, handleValuesChanges]);
132
+ const validate = useCallback(() => {
133
+ const generation = ++validateGeneration.current;
134
+ const signal = $validating.getAbortSignal("__form__");
135
+ const handleResult = (results) => {
136
+ if (generation !== validateGeneration.current) return {
137
+ hasErrors: false,
138
+ errors: {}
139
+ };
140
+ $errors.setErrors(results.errors);
141
+ return results;
142
+ };
143
+ const cleanup = () => {
144
+ if (generation === validateGeneration.current) $validating.setFormValidating(false);
145
+ };
146
+ const result = validateValues(rules, $values.refValues.current, resolveValidationError, signal);
147
+ if (result instanceof Promise) {
148
+ $validating.setFormValidating(true);
149
+ return result.then(handleResult).finally(cleanup);
150
+ }
151
+ return handleResult(result);
152
+ }, [rules, resolveValidationError]);
153
+ const validateField = useCallback((path) => {
154
+ const signal = $validating.getAbortSignal(String(path));
155
+ const applyResult = (results) => {
156
+ if (signal.aborted) return {
157
+ hasError: false,
158
+ error: null
159
+ };
160
+ if (results.hasError) $errors.setFieldError(path, results.error);
161
+ else $errors.clearFieldError(path);
162
+ return results;
163
+ };
164
+ const cleanup = () => {
165
+ if (!signal.aborted) $validating.setFieldValidating(String(path), false);
166
+ };
167
+ const result = validateFieldValue(path, rules, $values.refValues.current, resolveValidationError, signal);
168
+ if (result instanceof Promise) {
169
+ $validating.setFieldValidating(String(path), true);
170
+ return result.then(applyResult).finally(cleanup);
171
+ }
172
+ return applyResult(result);
173
+ }, [rules, resolveValidationError]);
174
+ const getInputProps = (path, { type = "input", withError = true, withFocus, ...otherOptions } = {}) => {
175
+ const _withFocus = withFocus ?? type !== "radio";
176
+ const payload = {
177
+ onChange: getInputOnChange((value) => setFieldValue(path, value, { forceUpdate: false })),
178
+ "data-path": getDataPath(name, path)
179
+ };
180
+ if (withError) payload.error = $errors.errorsState[path];
181
+ if (type === "checkbox") payload[mode === "controlled" ? "checked" : "defaultChecked"] = getPath(path, $values.refValues.current);
182
+ else if (type === "radio") {
183
+ payload[mode === "controlled" ? "checked" : "defaultChecked"] = getPath(path, $values.refValues.current) === otherOptions.value;
184
+ payload.value = otherOptions.value;
185
+ } else payload[mode === "controlled" ? "value" : "defaultValue"] = getPath(path, $values.refValues.current);
186
+ if (_withFocus) {
187
+ payload.onFocus = () => $status.setFieldTouched(path, true);
188
+ payload.onBlur = () => {
189
+ if (shouldValidateOnChange(path, validateInputOnBlur)) debouncedValidateField(String(path));
190
+ };
191
+ }
192
+ return Object.assign(payload, enhanceGetInputProps?.({
193
+ inputProps: payload,
194
+ field: path,
195
+ options: {
196
+ type,
197
+ withError,
198
+ withFocus: _withFocus,
199
+ ...otherOptions
200
+ },
201
+ form
202
+ }));
203
+ };
204
+ const onSubmit = (handleSubmit, handleValidationFailure) => (event) => {
205
+ if (onSubmitPreventDefault === "always") event?.preventDefault();
206
+ setSubmitting(true);
207
+ const handleValidation = (results) => {
208
+ if (results.hasErrors) {
209
+ if (onSubmitPreventDefault === "validation-failed") event?.preventDefault();
210
+ handleValidationFailure?.(results.errors, $values.refValues.current, event);
211
+ setSubmitting(false);
212
+ } else {
213
+ const submitResult = handleSubmit?.(transformValues($values.refValues.current), event);
214
+ if (submitResult instanceof Promise) submitResult.finally(() => setSubmitting(false));
215
+ else setSubmitting(false);
216
+ }
217
+ };
218
+ const result = validate();
219
+ if (result instanceof Promise) result.then(handleValidation).catch(() => {
220
+ setSubmitting(false);
221
+ });
222
+ else handleValidation(result);
223
+ };
224
+ const getTransformedValues = (input) => transformValues(input || $values.refValues.current);
225
+ const onReset = useCallback((event) => {
226
+ event.preventDefault();
227
+ reset();
228
+ }, []);
229
+ const isValid = useCallback((path) => {
230
+ const signal = new AbortController().signal;
231
+ if (path) {
232
+ const result = validateFieldValue(path, rules, $values.refValues.current, resolveValidationError, signal);
233
+ if (result instanceof Promise) return result.then((r) => !r.hasError);
234
+ return !result.hasError;
235
+ }
236
+ const result = validateValues(rules, $values.refValues.current, resolveValidationError, signal);
237
+ if (result instanceof Promise) return result.then((r) => !r.hasErrors);
238
+ return !result.hasErrors;
239
+ }, [rules, resolveValidationError]);
240
+ const key = (path) => `${formKey}-${String(path)}-${fieldKeys[String(path)] || 0}`;
241
+ const getInputNode = useCallback((path) => document.querySelector(`[data-path="${getDataPath(name, path)}"]`), []);
242
+ const resetField = useCallback((path) => {
243
+ $values.resetField(path, [mode !== "controlled" ? () => setFieldKeys((keys) => ({
244
+ ...keys,
245
+ [path]: (keys[path] || 0) + 1
246
+ })) : null]);
247
+ }, [
248
+ $values.resetField,
249
+ mode,
250
+ setFieldKeys
251
+ ]);
252
+ const form = {
253
+ watch: $watch.watch,
254
+ initialized: $values.initialized.current,
255
+ values: mode === "uncontrolled" ? $values.refValues.current : $values.stateValues,
256
+ getValues: $values.getValues,
257
+ getInitialValues: $values.getValuesSnapshot,
258
+ setInitialValues: $values.setValuesSnapshot,
259
+ resetField,
260
+ initialize,
261
+ setValues,
262
+ setFieldValue,
263
+ submitting,
264
+ setSubmitting,
265
+ validating: $validating.validating,
266
+ isValidating: $validating.isValidating,
267
+ errors: $errors.errorsState,
268
+ setErrors: $errors.setErrors,
269
+ setFieldError: $errors.setFieldError,
270
+ clearFieldError: $errors.clearFieldError,
271
+ clearErrors: $errors.clearErrors,
272
+ resetDirty: $status.resetDirty,
273
+ setTouched: $status.setTouched,
274
+ setDirty: $status.setDirty,
275
+ isTouched: $status.isTouched,
276
+ resetTouched: $status.resetTouched,
277
+ isDirty: $status.isDirty,
278
+ getTouched: $status.getTouched,
279
+ getDirty: $status.getDirty,
280
+ reorderListItem: ((path, payload) => {
281
+ const previousValues = $values.refValues.current;
282
+ $list.reorderListItem(path, payload);
283
+ notifyWatchSubscribers(previousValues);
284
+ }),
285
+ insertListItem: ((path, item, index) => {
286
+ const previousValues = $values.refValues.current;
287
+ $list.insertListItem(path, item, index);
288
+ notifyWatchSubscribers(previousValues);
289
+ }),
290
+ removeListItem: ((path, index) => {
291
+ const previousValues = $values.refValues.current;
292
+ $list.removeListItem(path, index);
293
+ notifyWatchSubscribers(previousValues);
294
+ }),
295
+ replaceListItem: ((path, index, item) => {
296
+ const previousValues = $values.refValues.current;
297
+ $list.replaceListItem(path, index, item);
298
+ notifyWatchSubscribers(previousValues);
299
+ }),
300
+ reset,
301
+ validate,
302
+ validateField,
303
+ getInputProps,
304
+ onSubmit,
305
+ onReset,
306
+ isValid,
307
+ getTransformedValues,
308
+ key,
309
+ getInputNode
310
+ };
311
+ useFormActions(name, form);
312
+ return form;
376
313
  }
377
-
314
+ //#endregion
378
315
  export { useForm };
379
- //# sourceMappingURL=use-form.mjs.map
316
+
317
+ //# sourceMappingURL=use-form.mjs.map