react-f0rm 0.1.0 → 0.2.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/dist/index.esm.js CHANGED
@@ -1,4 +1,5 @@
1
- import { createContext, useContext, useRef, useEffect, useReducer, useMemo, createElement } from 'react';
1
+ import * as React from 'react';
2
+ import { createContext, useContext, useRef, useEffect, useReducer, useMemo, useCallback } from 'react';
2
3
 
3
4
  function getSet(ee, key) {
4
5
  var set = ee.get(key);
@@ -7,14 +8,13 @@ function getSet(ee, key) {
7
8
  ee.set(key, newSet);
8
9
  return newSet;
9
10
  }
10
- function create() {
11
+ function create$2() {
11
12
  return new Map();
12
13
  }
13
- function emit(ee, key) {
14
+ function emit$1(ee, key) {
14
15
  for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
15
16
  args[_key - 2] = arguments[_key];
16
17
  }
17
-
18
18
  (ee.get(key) || []).forEach(function (h) {
19
19
  return h.apply(void 0, args);
20
20
  });
@@ -27,425 +27,567 @@ function on(ee, key, handler) {
27
27
  };
28
28
  }
29
29
 
30
- function get(values, paths) {
31
- try {
32
- return paths.reduce((p, values) => values[p], values);
33
- } catch (e) {
34
- return undefined;
35
- }
30
+ function normalizePath(path) {
31
+ if (Array.isArray(path)) return path;
32
+ return path.split(/\.|\[/).map((prop) => prop.endsWith("]") ? Number.parseInt(prop, 10) : prop);
36
33
  }
37
- function set(values, paths, value) {
38
- if (!paths.length) return values;
39
- const path = paths[0];
40
-
41
- if (Number.isNumber(value)) {
34
+ function get(values, path) {
35
+ return path.reduce((current, p) => {
36
+ if (current == null) return void 0;
37
+ return current[p];
38
+ }, values);
39
+ }
40
+ function set(values, path, value) {
41
+ if (!path.length) return value;
42
+ const [prop, ...props] = path;
43
+ if (typeof prop === "number") {
42
44
  const arr = Array.isArray(values) ? values.slice() : [];
43
- arr[path] = set(arr[path], paths.slice(1), value);
45
+ arr[prop] = set(arr[prop], props, value);
44
46
  return arr;
45
47
  }
46
-
47
- return { ...values,
48
- [paths[0]]: set(values[path], paths.slice(1), value)
49
- };
50
- }
51
-
52
- /** @typedef { import('../index').TaskCounter } TaskCounter */
53
-
54
- /**
55
- * @return {TaskCounter}
56
- */
57
-
58
- function create$1() {
59
- return {
60
- emitter: create(),
61
- count: 0
62
- };
48
+ return { ...values, [prop]: set(values && values[prop], props, value) };
63
49
  }
64
- /**
65
- * @param {TaskCounter} counter
66
- */
67
-
68
- function isRunning(counter) {
69
- return Boolean(counter.count);
50
+ function isPromise(value) {
51
+ return value && typeof value.then === "function";
70
52
  }
71
- /**
72
- * @param {TaskCounter} counter
73
- * @param {() => boolean} ifReject
74
- */
75
-
76
- function waitUntil(counter, ifReject) {
53
+ function waitUntil(emitter, event, isResolve, isReject) {
77
54
  return new Promise((resolve, reject) => {
78
- const off = on(counter.emitter, 'done', () => {
79
- if (ifReject()) {
55
+ if (isReject()) return void reject();
56
+ if (isResolve()) return void resolve();
57
+ const off = on(emitter, event, () => {
58
+ if (isReject()) {
80
59
  off();
81
60
  reject();
82
61
  return;
83
62
  }
84
-
85
- if (counter.count) return;
63
+ if (isResolve()) return;
86
64
  off();
87
65
  resolve();
88
66
  });
89
67
  });
90
68
  }
91
69
 
92
- /** @typedef { import('../index').Form } Form */
93
-
94
- /** @typedef { import('../index').Paths } Paths */
95
-
96
- /**
97
- * Create form instance
98
- * @param {Object} [defaultValues] default values
99
- * @return {Form}
100
- */
70
+ function create$1(name) {
71
+ const value = normalizePath(name);
72
+ return { value, key: JSON.stringify(value) };
73
+ }
101
74
 
102
- function create$2(defaultValues) {
103
- const emitter = create();
75
+ const emit = emit$1;
76
+ function create(options) {
77
+ const emitter = create$2();
104
78
  return {
105
79
  emitter,
106
- validatingCount: 0,
107
- defaultValues,
108
- values: new Map(),
109
- errors: new Map(),
110
- touched: new Set()
80
+ revalidateOnChange: true,
81
+ ...options,
82
+ initialValues: options?.initialValues ?? {},
83
+ values: /* @__PURE__ */ new Map(),
84
+ errors: /* @__PURE__ */ new Map(),
85
+ touched: /* @__PURE__ */ new Set(),
86
+ validators: /* @__PURE__ */ new Map(),
87
+ validating: /* @__PURE__ */ new Set(),
88
+ isSubmitting: false,
89
+ submitCount: 0,
90
+ isSubmitSuccessful: void 0
111
91
  };
112
92
  }
113
- /**
114
- * Get form values
115
- * @param {Form} form
116
- */
117
-
118
- function getValues({
119
- defaultValues,
120
- values
121
- }) {
122
- return values.keys().reduce((k, v) => set(v, JSON.parse(k), values.get(k)), defaultValues);
93
+ function getValues(form) {
94
+ return Array.from(form.values.keys()).reduce(
95
+ (v, k) => set(v, JSON.parse(k), form.values.get(k)),
96
+ form.initialValues
97
+ );
123
98
  }
124
- /**
125
- * Get field value
126
- * @param {Form} form
127
- * @param {Paths} paths
128
- */
129
-
130
- function getValue({
131
- defaultValues,
132
- values
133
- }, paths) {
134
- const key = JSON.stringify(paths);
135
- if (values.has(key)) return values.get(key);
136
- return get(defaultValues, paths);
137
- }
138
- /**
139
- * Set field value
140
- * @param {Form} form
141
- * @param {Paths} paths
142
- * @param {any} value
143
- */
144
-
145
- function setValue({
146
- emitter,
147
- values
148
- }, paths, value) {
149
- const key = JSON.stringify(paths);
150
- values.set(key, value);
151
- emit(emitter, 'change');
152
- }
153
- /**
154
- * Get field error
155
- * @param {Form} form
156
- * @param {Paths} paths
157
- */
158
-
159
- function getError({
160
- errors
161
- }, paths) {
162
- return errors.get(JSON.stringify(paths));
163
- }
164
- /**
165
- * Get all errors
166
- * @param {Form} form
167
- */
168
-
169
- function getErrors({
170
- errors
171
- }) {
99
+ function getValue(form, name) {
100
+ return getValueByPath(form, create$1(name));
101
+ }
102
+ function getValueByPath({ initialValues, values }, path) {
103
+ if (values.has(path.key)) return values.get(path.key);
104
+ return get(initialValues, path.value);
105
+ }
106
+ function setValue(form, name, value) {
107
+ setValueByPath(form, create$1(name), value);
108
+ }
109
+ function setValueByPath({ emitter, values }, path, value) {
110
+ values.set(path.key, value);
111
+ emit(emitter, "change", path);
112
+ }
113
+ function getError(form, name) {
114
+ return getErrorByPath(form, create$1(name));
115
+ }
116
+ function getErrorByPath({ errors }, path) {
117
+ return errors.get(path.key);
118
+ }
119
+ function getErrors({ errors }) {
172
120
  return Array.from(errors.values());
173
121
  }
174
- /**
175
- * Set field error
176
- * @param {Form} Form
177
- * @param {Paths} paths
178
- * @param {string | undefined} error
179
- */
180
-
181
- function setError({
182
- emitter,
183
- errors
184
- }, paths, error) {
185
- errors.set(JSON.stringify(paths), error);
186
- emit(emitter, 'errors');
187
- }
188
- /**
189
- * Clear errors
190
- * @param {Form} form
191
- */
192
-
193
- function clearErrors({
194
- emitter,
195
- errors
196
- }) {
122
+ function getFirstError({ errors }) {
123
+ return errors.values().next().value;
124
+ }
125
+ function unsetValidatingByPath({ emitter, validating }, { key }) {
126
+ validating.delete(key);
127
+ emit(emitter, "validating");
128
+ }
129
+ function setValidatingByPath({ emitter, validating }, { key }) {
130
+ validating.add(key);
131
+ emit(emitter, "validating");
132
+ }
133
+ function setError(form, name, error) {
134
+ setErrorByPath(form, create$1(name), error);
135
+ }
136
+ function setErrorByPath({ emitter, errors }, path, error) {
137
+ if (error) {
138
+ errors.set(path.key, error);
139
+ } else {
140
+ errors.delete(path.key);
141
+ }
142
+ emit(emitter, "errors");
143
+ }
144
+ function clearErrors({ emitter, errors }) {
197
145
  errors.clear();
198
- emit(emitter, 'errors');
146
+ emit(emitter, "errors");
199
147
  }
200
- /**
201
- * Set field touched state
202
- * @param {Form} form
203
- * @param {Paths} paths
204
- */
205
-
206
- function setTouched({
207
- emitter,
208
- touched
209
- }, paths) {
210
- touched.add(JSON.stringify(paths));
211
- emit(emitter, 'touched');
212
- }
213
- /**
214
- * Set field touched state
215
- * @param {Form} form
216
- * @param {Paths} paths
217
- */
218
-
219
- function hasTouched({
220
- touched
221
- }, paths) {
222
- return touched.has(JSON.stringify(paths));
223
- }
224
- /**
225
- * Is dirty
226
- * @param {Form} form
227
- */
228
-
229
- function isDirty({
230
- touched
231
- }) {
148
+ function setTouched(form, name) {
149
+ setTouchedByPath(form, create$1(name));
150
+ }
151
+ function setTouchedByPath({ emitter, touched }, { key }) {
152
+ if (touched.has(key)) return;
153
+ touched.add(key);
154
+ emit(emitter, "touched");
155
+ }
156
+ function hasTouched(form, name) {
157
+ return hasTouchedByPath(form, create$1(name));
158
+ }
159
+ function hasTouchedByPath({ touched }, path) {
160
+ return touched.has(path.key);
161
+ }
162
+ function isDirty({ initialValues, values }) {
163
+ for (const [key, value] of values) {
164
+ const path = JSON.parse(key);
165
+ if (get(initialValues, path) !== value) return true;
166
+ }
167
+ return false;
168
+ }
169
+ function isTouched({ touched }) {
232
170
  return touched.size > 0;
233
171
  }
234
- /**
235
- * Remove field
236
- * @param {Form} form
237
- * @param {Paths} paths
238
- */
239
-
240
- function removeField(form, paths) {
241
- const key = JSON.stringify(paths);
242
- const {
243
- emitter,
244
- values,
245
- touched,
246
- errors
247
- } = form;
172
+ function removeField(form, name) {
173
+ removeFieldByPath(form, create$1(name));
174
+ }
175
+ function removeFieldByPath(form, { key }) {
176
+ const { emitter, values, touched, errors, validating } = form;
248
177
  values.delete(key);
249
178
  touched.delete(key);
250
179
  errors.delete(key);
251
- emit(emitter, 'change');
252
- emit(emitter, 'touched');
253
- emit(emitter, 'errors');
254
- }
255
- /**
256
- * Reset form
257
- * @param {Form} form
258
- * @param {Object} [defaultValues]
259
- */
260
-
261
- function reset(form, defaultValues) {
262
- form.defaultValues = defaultValues;
180
+ validating.delete(key);
181
+ emit(emitter, "change");
182
+ emit(emitter, "touched");
183
+ emit(emitter, "errors");
184
+ emit(emitter, "validating");
185
+ }
186
+ function setInitialValues(form, initialValues) {
187
+ if (form.initialValues === initialValues) return;
188
+ form.initialValues = initialValues;
189
+ form.values.clear();
190
+ emit(form.emitter, "change");
191
+ }
192
+ function reset(form, initialValues) {
193
+ form.initialValues = initialValues;
263
194
  clearErrors(form);
264
- const {
265
- emitter,
266
- touched
267
- } = form;
195
+ const { emitter, touched, values } = form;
196
+ values.clear();
268
197
  touched.clear();
269
- emit(emitter, 'change');
270
- emit(emitter, 'touched');
271
- emit(emitter, 'reset');
198
+ emit(emitter, "change");
199
+ emit(emitter, "touched");
200
+ emit(emitter, "reset");
272
201
  }
273
- /**
274
- * @param {Form} form
275
- */
276
-
277
- function hasErrors({
278
- errors
279
- }) {
202
+ function hasErrors({ errors }) {
280
203
  return errors.size > 0;
281
204
  }
282
- /**
283
- * Validate all fields.
284
- * @param {Form} form
285
- * @return {Promise} resolve if no error; reject and stop validate if has an error;
286
- */
287
-
288
- function validate(form) {
289
- const token = create$1();
290
- emit(form.emitter, 'validate', token);
291
- if (hasErrors(form)) return Promise.reject();
292
- if (!isRunning(token)) return Promise.resolve();
293
- return waitUntil(token, () => hasErrors(form));
205
+ function trigger(form) {
206
+ form.validators.forEach((validator) => validator());
207
+ }
208
+ async function ensureValidate(form) {
209
+ form.validators.forEach((validator) => validator());
210
+ await waitUntil(
211
+ form.emitter,
212
+ "validating",
213
+ () => !form.validating.size,
214
+ () => hasErrors(form)
215
+ ).catch(() => {
216
+ throw new Error(getFirstError(form));
217
+ });
218
+ if (form.validate) {
219
+ const result = await form.validate(getValues(form));
220
+ const entries = result ? Object.entries(result) : [];
221
+ if (entries.length) {
222
+ entries.forEach(([field, error]) => {
223
+ setError(form, field, error);
224
+ });
225
+ throw new Error(getFirstError(form));
226
+ }
227
+ }
228
+ }
229
+ async function validate(form) {
230
+ return ensureValidate(form).catch((e) => e.message);
231
+ }
232
+ function setIsSubmitting(form, value) {
233
+ form.isSubmitting = value;
234
+ emit(form.emitter, "submitting");
235
+ }
236
+ function incrementSubmitCount(form) {
237
+ form.submitCount++;
238
+ emit(form.emitter, "submitCount");
239
+ }
240
+ function setSubmitSuccessful(form, value) {
241
+ form.isSubmitSuccessful = value;
242
+ emit(form.emitter, "submitSuccessful");
294
243
  }
295
244
 
296
- const FormContext = /*#__PURE__*/createContext();
245
+ const FormContext = createContext(null);
297
246
  const FormProvider = FormContext.Provider;
298
247
  function useFormContext() {
299
248
  const form = useContext(FormContext);
300
- if (!form) throw new Error('no form provided');
249
+ if (!form) throw new Error("no form provided");
301
250
  return form;
302
251
  }
252
+ const CheckboxGroupContext = createContext(null);
253
+ const CheckboxGroupProvider = CheckboxGroupContext.Provider;
254
+ function useCheckboxGroupContext() {
255
+ const group = useContext(CheckboxGroupContext);
256
+ if (!group) throw new Error("no group provided");
257
+ return group;
258
+ }
303
259
 
304
- function useForm(defaultValues) {
305
- const ref = useRef();
306
- const form = ref.current = ref.current || create$2();
260
+ function useForm(options) {
261
+ const ref = useRef(null);
262
+ const form = ref.current = ref.current || create(options);
263
+ const initialValues = options && options.initialValues;
307
264
  useEffect(() => {
308
- form.defaultValues = defaultValues;
309
- emit(form.emitter, 'change');
310
- }, [defaultValues]);
265
+ setInitialValues(form, initialValues);
266
+ }, [initialValues]);
311
267
  return form;
312
268
  }
313
- /**
314
- * @param {EventEmitter} emitter
315
- * @param {string} event
316
- * @param {() => any} getter
317
- */
318
-
319
269
  function useWatch(emitter, event, getter) {
320
- const [value, syncValue] = useReducer(getter, getter);
270
+ const [value, syncValue] = useReducer(getter, void 0, getter);
321
271
  useEffect(() => on(emitter, event, syncValue), [emitter, event]);
322
272
  return value;
323
273
  }
324
- /**
325
- * Get field value state
326
- * @param {Form} form
327
- * @param {Paths} paths
328
- */
329
-
330
- function useValue(form, paths) {
331
- return useWatch(form.emitter, 'change', getValue.bind(null, form, paths));
274
+ function useValue(form, name) {
275
+ return useValueByPath(form, create$1(name));
276
+ }
277
+ function useValueByPath(form, path) {
278
+ return useWatch(
279
+ form.emitter,
280
+ "change",
281
+ getValueByPath.bind(null, form, path)
282
+ );
283
+ }
284
+ function useTouched(form, name) {
285
+ return useTouchedByPath(form, create$1(name));
286
+ }
287
+ function useTouchedByPath(form, path) {
288
+ return useWatch(
289
+ form.emitter,
290
+ "touched",
291
+ hasTouchedByPath.bind(null, form, path)
292
+ );
332
293
  }
333
- function useTouched(form, paths) {
334
- return useWatch(form.emitter, 'touched', hasTouched.bind(null, form, paths));
294
+ function useError(form, name) {
295
+ return useErrorByPath(form, create$1(name));
335
296
  }
336
- function useError(form, paths) {
337
- return useWatch(form.emitter, 'errors', getError.bind(null, form, paths));
297
+ function useErrorByPath(form, path) {
298
+ return useWatch(
299
+ form.emitter,
300
+ "errors",
301
+ getErrorByPath.bind(null, form, path)
302
+ );
338
303
  }
339
304
  function useIsDirty(form) {
340
- return useWatch(form.emitter, 'touched', isDirty.bind(null, form));
305
+ return useWatch(form.emitter, "touched", isDirty.bind(null, form));
341
306
  }
342
307
  function useHasErrors(form) {
343
- return useWatch(form.emitter, 'errors', hasErrors.bind(null, form));
308
+ return useWatch(form.emitter, "errors", hasErrors.bind(null, form));
309
+ }
310
+ function useIsSubmitting(form) {
311
+ return useWatch(form.emitter, "submitting", () => form.isSubmitting);
312
+ }
313
+ function useSubmitCount(form) {
314
+ return useWatch(form.emitter, "submitCount", () => form.submitCount);
344
315
  }
345
316
 
346
- function useField({
347
- name,
348
- defaultValue,
349
- validate
350
- }) {
351
- const form = useFormContext();
352
- const lockRef = useRef();
353
- const validateRef = useRef();
354
- validateRef.current = validate;
355
- const nameKey = JSON.stringify(name);
356
- const handlers = useMemo(() => {
357
- setValue(form, name, defaultValue);
358
-
359
- const onChange = v => setValue(form, name, v);
317
+ function usePath(name) {
318
+ const path = useMemo(() => create$1(normalizePath(name)), [name]);
319
+ return useMemo(() => path, [path.key]);
320
+ }
360
321
 
361
- const onBlur = () => {
362
- setTouched(form, name);
363
- if (!validateRef.current) return;
364
- const result = validateRef.current(getValue(form, name));
365
- if (!result) return;
322
+ function useStage(value) {
323
+ const ref = useRef(value);
324
+ ref.current = value;
325
+ return ref;
326
+ }
327
+ function useStageFn(fn) {
328
+ const ref = useStage(fn);
329
+ return useCallback(
330
+ (...params) => ref.current(...params),
331
+ []
332
+ );
333
+ }
366
334
 
367
- if (typeof result === 'string') {
368
- setError(form, name, result);
335
+ function useValidate(validate, path) {
336
+ const form = useFormContext();
337
+ const lockRef = useRef(null);
338
+ const validateRef = useRef(validate);
339
+ validateRef.current = validate;
340
+ useEffect(() => {
341
+ const validator = () => {
342
+ const fn = validateRef.current;
343
+ if (!fn) return;
344
+ const result = fn(getValueByPath(form, path), { form, path });
345
+ if (!isPromise(result)) {
346
+ setErrorByPath(form, path, result);
369
347
  return;
370
348
  }
371
-
372
349
  const lock = lockRef.current = {};
373
- result.then(error => {
374
- if (lock === lockRef.current && error) setError(form, name, error);
375
- }).finally(() => lockRef.current = null);
350
+ setValidatingByPath(form, path);
351
+ result.then((error) => {
352
+ if (lock === lockRef.current) {
353
+ setErrorByPath(form, path, error);
354
+ }
355
+ }).finally(() => {
356
+ if (lock === lockRef.current) {
357
+ unsetValidatingByPath(form, path);
358
+ lockRef.current = null;
359
+ }
360
+ });
376
361
  };
377
-
378
- return {
379
- onChange,
380
- onBlur
362
+ form.validators.set(path.key, validator);
363
+ return () => {
364
+ form.validators.delete(path.key);
381
365
  };
382
- }, [nameKey, form]);
383
- useEffect(() => () => {
384
- removeField(form, name);
385
- }, [nameKey, form]);
386
- const value = useValue(form, name);
387
- return {
388
- value,
389
- ...handlers
390
- };
366
+ }, [form, path.key]);
367
+ return useStageFn(() => form.validators.get(path.key)?.());
391
368
  }
392
369
 
393
- function _extends() {
394
- _extends = Object.assign || function (target) {
395
- for (var i = 1; i < arguments.length; i++) {
396
- var source = arguments[i];
397
-
398
- for (var key in source) {
399
- if (Object.prototype.hasOwnProperty.call(source, key)) {
400
- target[key] = source[key];
401
- }
370
+ function useField({
371
+ form: f1,
372
+ name,
373
+ initialValue,
374
+ shouldUnregister,
375
+ validate,
376
+ ...rest
377
+ }) {
378
+ const f2 = useFormContext();
379
+ const form = f1 || f2;
380
+ const path = usePath(name);
381
+ const validator = useValidate(validate, path);
382
+ useMemo(() => {
383
+ if (initialValue !== void 0) setValueByPath(form, path, initialValue);
384
+ }, [form, path]);
385
+ const error = useErrorByPath(form, path);
386
+ const value = useValueByPath(form, path);
387
+ const onChange = useStageFn((v) => {
388
+ setValueByPath(form, path, v);
389
+ if (form.validateOnChange || form.revalidateOnChange && error && error !== void 0)
390
+ validator();
391
+ });
392
+ const onBlur = useStageFn(() => {
393
+ setTouchedByPath(form, path);
394
+ if (form.validateOnBlur || form.revalidateOnBlur && error !== void 0)
395
+ validator();
396
+ });
397
+ useEffect(
398
+ () => () => {
399
+ if (shouldUnregister !== false) {
400
+ removeFieldByPath(form, path);
402
401
  }
402
+ },
403
+ [path, form, shouldUnregister]
404
+ );
405
+ return { ...rest, value, error, onChange, onBlur, name: path.key };
406
+ }
407
+
408
+ let idCounter = 0;
409
+ function generateId() {
410
+ return `_${++idCounter}`;
411
+ }
412
+ function useFieldArray(options) {
413
+ const f2 = useFormContext();
414
+ const form = options.form || f2;
415
+ const path = usePath(options.name);
416
+ const idsRef = useRef([]);
417
+ const getArray = useCallback(
418
+ () => getValueByPath(form, path) || [],
419
+ [form, path]
420
+ );
421
+ const setArray = useCallback(
422
+ (arr) => {
423
+ setValueByPath(form, path, arr);
424
+ },
425
+ [form, path]
426
+ );
427
+ const computeFields = useCallback(() => {
428
+ const arr = getArray();
429
+ while (idsRef.current.length < arr.length) {
430
+ idsRef.current.push(generateId());
403
431
  }
404
-
405
- return target;
406
- };
407
-
408
- return _extends.apply(this, arguments);
432
+ while (idsRef.current.length > arr.length) {
433
+ idsRef.current.pop();
434
+ }
435
+ return idsRef.current.map((id, index) => ({ id, index }));
436
+ }, [getArray]);
437
+ const [fields, syncFields] = useReducer(
438
+ computeFields,
439
+ void 0,
440
+ computeFields
441
+ );
442
+ useEffect(() => on(form.emitter, "change", syncFields), [form.emitter]);
443
+ const append = useStageFn((value) => {
444
+ const arr = getArray();
445
+ idsRef.current.push(generateId());
446
+ setArray([...arr, value]);
447
+ });
448
+ const prepend = useStageFn((value) => {
449
+ const arr = getArray();
450
+ idsRef.current.unshift(generateId());
451
+ setArray([value, ...arr]);
452
+ });
453
+ const insert = useStageFn((index, value) => {
454
+ const arr = getArray();
455
+ idsRef.current.splice(index, 0, generateId());
456
+ const newArr = [...arr.slice(0, index), value, ...arr.slice(index)];
457
+ setArray(newArr);
458
+ });
459
+ const remove = useStageFn((index) => {
460
+ const arr = getArray();
461
+ idsRef.current.splice(index, 1);
462
+ const newArr = arr.filter((_, i) => i !== index);
463
+ setArray(newArr);
464
+ });
465
+ const swap = useStageFn((from, to) => {
466
+ const arr = getArray();
467
+ [idsRef.current[from], idsRef.current[to]] = [
468
+ idsRef.current[to],
469
+ idsRef.current[from]
470
+ ];
471
+ const newArr = [...arr];
472
+ [newArr[from], newArr[to]] = [newArr[to], newArr[from]];
473
+ setArray(newArr);
474
+ });
475
+ const move = useStageFn((from, to) => {
476
+ const arr = getArray();
477
+ const [id] = idsRef.current.splice(from, 1);
478
+ idsRef.current.splice(to, 0, id);
479
+ const newArr = [...arr];
480
+ const [item] = newArr.splice(from, 1);
481
+ newArr.splice(to, 0, item);
482
+ setArray(newArr);
483
+ });
484
+ return { fields, append, prepend, insert, remove, swap, move };
409
485
  }
410
486
 
411
- /** @typedef { import('../index').FormProps } FormProps */
412
-
413
- /**
414
- * Form
415
- * @param {FormProps} props
416
- */
417
-
418
487
  function Form({
419
- form: f,
488
+ form: f1,
489
+ initialValues,
420
490
  onSubmit,
421
491
  onValidSubmit,
422
492
  onInvalidSubmit,
423
493
  ...props
424
494
  }) {
425
- const form = useMemo(() => f || create$2(), [f]);
426
-
427
- function handleSubmit(e) {
428
- const task = validate(form);
429
- if (!(onSubmit || onValidSubmit || onInvalidSubmit)) return;
495
+ const f2 = useForm({ initialValues });
496
+ const form = f1 || f2;
497
+ async function handleSubmit(e) {
498
+ e.preventDefault();
499
+ setIsSubmitting(form, true);
500
+ incrementSubmitCount(form);
501
+ const error = await validate(form);
430
502
  const values = getValues(form);
431
- if (onSubmit) onSubmit(values, e);
432
- task.then(valid => valid ? onValidSubmit && onValidSubmit(values, e) : onInvalidSubmit && onInvalidSubmit(getErrors(form), values));
503
+ if (error) {
504
+ setIsSubmitting(form, false);
505
+ setSubmitSuccessful(form, false);
506
+ if (onInvalidSubmit) onInvalidSubmit(getErrors(form), values);
507
+ return;
508
+ }
509
+ try {
510
+ if (onSubmit) await onSubmit(values, e);
511
+ if (onValidSubmit) onValidSubmit(values, e);
512
+ setSubmitSuccessful(form, true);
513
+ } catch {
514
+ setSubmitSuccessful(form, false);
515
+ } finally {
516
+ setIsSubmitting(form, false);
517
+ }
433
518
  }
434
-
435
- return /*#__PURE__*/createElement(FormProvider, {
436
- value: form
437
- }, /*#__PURE__*/createElement("form", _extends({}, props, {
438
- onSubmit: handleSubmit
439
- })));
519
+ return /* @__PURE__ */ React.createElement(FormProvider, { value: form }, /* @__PURE__ */ React.createElement("form", { ...props, noValidate: true, onSubmit: handleSubmit }));
440
520
  }
441
521
 
442
- function Field({
443
- render,
444
- ...props
445
- }) {
446
- const field = useField(props);
447
- return render(field);
522
+ const buildInError = /* @__PURE__ */ Symbol("buildInError");
523
+ function setRef(ref, value) {
524
+ if (typeof ref === "function") {
525
+ ref(value);
526
+ } else if (ref) {
527
+ ref.current = value;
528
+ }
448
529
  }
530
+ const Field = React.forwardRef(
531
+ ({ validate, eventToValue, initialValue, name, asProps, ...props }, ref) => {
532
+ const innerRef = React.useRef(null);
533
+ const mergedRef = React.useCallback(
534
+ (node) => {
535
+ innerRef.current = node;
536
+ setRef(ref, node);
537
+ },
538
+ [ref]
539
+ );
540
+ const { as, value, valueToProps, onChange, error, ...rest } = useField({
541
+ ...props,
542
+ name,
543
+ initialValue,
544
+ validate: (...params) => {
545
+ if (innerRef.current?.checkValidity() === false)
546
+ return buildInError;
547
+ if (validate) return validate(...params);
548
+ }
549
+ });
550
+ const Component = as || "input";
551
+ React.useEffect(() => {
552
+ if (!innerRef.current) return;
553
+ if (error === buildInError) {
554
+ innerRef.current.setCustomValidity("");
555
+ innerRef.current.reportValidity();
556
+ return;
557
+ }
558
+ if (typeof error === "string") {
559
+ innerRef.current.setCustomValidity(error);
560
+ innerRef.current.reportValidity();
561
+ }
562
+ }, [error]);
563
+ const toValue = eventToValue ?? ((e) => e.target.value);
564
+ return /* @__PURE__ */ React.createElement(
565
+ Component,
566
+ {
567
+ ...rest,
568
+ ...asProps,
569
+ ...valueToProps ? valueToProps(value) : { value },
570
+ onChange: (e) => onChange(toValue(e)),
571
+ ref: mergedRef
572
+ }
573
+ );
574
+ }
575
+ );
576
+ const Checkbox = React.forwardRef(
577
+ ({ name, ...props }, ref) => {
578
+ const { value, onChange, error, ...rest } = useField({ ...props, name });
579
+ return /* @__PURE__ */ React.createElement(
580
+ "input",
581
+ {
582
+ ...rest,
583
+ type: "checkbox",
584
+ checked: !!value,
585
+ onChange: (e) => onChange(e.target.checked),
586
+ ref
587
+ }
588
+ );
589
+ }
590
+ );
449
591
 
450
- export { Field, Form, FormContext, FormProvider, clearErrors, create$2 as createForm, getError, getErrors, getValue, getValues, hasErrors, hasTouched, isDirty, removeField, reset, setError, setTouched, setValue, useError, useField, useForm, useFormContext, useHasErrors, useIsDirty, useTouched, useValue, useWatch, validate };
592
+ export { Checkbox, CheckboxGroupContext, CheckboxGroupProvider, Field, Form, FormContext, FormProvider, clearErrors, create as createForm, ensureValidate, getError, getErrorByPath, getErrors, getFirstError, getValue, getValueByPath, getValues, hasErrors, hasTouched, hasTouchedByPath, incrementSubmitCount, isDirty, isTouched, removeField, removeFieldByPath, reset, setError, setErrorByPath, setInitialValues, setIsSubmitting, setSubmitSuccessful, setTouched, setTouchedByPath, setValidatingByPath, setValue, setValueByPath, trigger, unsetValidatingByPath, useCheckboxGroupContext, useError, useErrorByPath, useField, useFieldArray, useForm, useFormContext, useHasErrors, useIsDirty, useIsSubmitting, useSubmitCount, useTouched, useTouchedByPath, useValue, useValueByPath, useWatch, validate };
451
593
  //# sourceMappingURL=index.esm.js.map