@rebasepro/formex 0.4.0 → 0.6.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 +87 -147
- package/dist/Formex.d.ts +1 -1
- package/dist/index.es.js +311 -475
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +365 -505
- package/dist/index.umd.js.map +1 -1
- package/package.json +10 -11
- package/src/utils.ts +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1,490 +1,326 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import React__default, { useContext, useRef, useState, useCallback, useEffect, useMemo } from "react";
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
3
|
import { jsx } from "react/jsx-runtime";
|
|
5
4
|
import { deepEqual } from "fast-equals";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return ctx;
|
|
5
|
+
//#region src/Formex.tsx
|
|
6
|
+
var FormexContext = React.createContext(null);
|
|
7
|
+
var useFormex = () => {
|
|
8
|
+
const ctx = useContext(FormexContext);
|
|
9
|
+
if (!ctx) throw new Error("useFormex must be used within a Formex provider");
|
|
10
|
+
return ctx;
|
|
13
11
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} = t0;
|
|
20
|
-
let t1;
|
|
21
|
-
if ($[0] !== children || $[1] !== value) {
|
|
22
|
-
t1 = /* @__PURE__ */ jsx(FormexContext.Provider, { value, children });
|
|
23
|
-
$[0] = children;
|
|
24
|
-
$[1] = value;
|
|
25
|
-
$[2] = t1;
|
|
26
|
-
} else {
|
|
27
|
-
t1 = $[2];
|
|
28
|
-
}
|
|
29
|
-
return t1;
|
|
12
|
+
var Formex = ({ value, children }) => {
|
|
13
|
+
return /* @__PURE__ */ jsx(FormexContext.Provider, {
|
|
14
|
+
value,
|
|
15
|
+
children
|
|
16
|
+
});
|
|
30
17
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/utils.ts
|
|
20
|
+
/** @private is the value an empty array? */
|
|
21
|
+
var isEmptyArray = (value) => Array.isArray(value) && value.length === 0;
|
|
22
|
+
/** @private is the given object a Function? */
|
|
23
|
+
var isFunction = (obj) => typeof obj === "function";
|
|
24
|
+
/** @private is the given object an Object? */
|
|
25
|
+
var isObject = (obj) => obj !== null && typeof obj === "object";
|
|
26
|
+
/** @private is the given object an integer? */
|
|
27
|
+
var isInteger = (obj) => String(Math.floor(Number(obj))) === obj;
|
|
28
|
+
/** @private is the given object a NaN? */
|
|
29
|
+
var isNaN = (obj) => obj !== obj;
|
|
30
|
+
/**
|
|
31
|
+
* Deeply get a value from an object via its path.
|
|
32
|
+
*/
|
|
36
33
|
function getIn(obj, key, def, p = 0) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (p !== path.length && !current) {
|
|
43
|
-
return def;
|
|
44
|
-
}
|
|
45
|
-
return current === void 0 ? def : current;
|
|
34
|
+
const path = toPath(key);
|
|
35
|
+
let current = obj;
|
|
36
|
+
while (current && p < path.length) current = current[path[p++]];
|
|
37
|
+
if (p !== path.length && !current) return def;
|
|
38
|
+
return current === void 0 ? def : current;
|
|
46
39
|
}
|
|
47
40
|
function setIn(obj, path, value) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
delete resVal[pathArray[i]];
|
|
67
|
-
} else {
|
|
68
|
-
resVal[pathArray[i]] = value;
|
|
69
|
-
}
|
|
70
|
-
if (i === 0 && value === void 0) {
|
|
71
|
-
delete res[pathArray[i]];
|
|
72
|
-
}
|
|
73
|
-
return res;
|
|
41
|
+
const res = clone(obj);
|
|
42
|
+
let resVal = res;
|
|
43
|
+
let i = 0;
|
|
44
|
+
const pathArray = toPath(path);
|
|
45
|
+
for (; i < pathArray.length - 1; i++) {
|
|
46
|
+
const currentPath = pathArray[i];
|
|
47
|
+
const currentObj = getIn(obj, pathArray.slice(0, i + 1));
|
|
48
|
+
if (currentObj && (isObject(currentObj) || Array.isArray(currentObj))) resVal = resVal[currentPath] = clone(currentObj);
|
|
49
|
+
else {
|
|
50
|
+
const nextPath = pathArray[i + 1];
|
|
51
|
+
resVal = resVal[currentPath] = isInteger(nextPath) && Number(nextPath) >= 0 ? [] : {};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if ((i === 0 ? obj : resVal)[pathArray[i]] === value) return obj;
|
|
55
|
+
if (value === void 0) delete resVal[pathArray[i]];
|
|
56
|
+
else resVal[pathArray[i]] = value;
|
|
57
|
+
if (i === 0 && value === void 0) delete res[pathArray[i]];
|
|
58
|
+
return res;
|
|
74
59
|
}
|
|
75
60
|
function clone(value) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
...value
|
|
84
|
-
};
|
|
85
|
-
} else {
|
|
86
|
-
return value;
|
|
87
|
-
}
|
|
61
|
+
if (Array.isArray(value)) return [...value];
|
|
62
|
+
else if (typeof value === "object" && value !== null) {
|
|
63
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) return value;
|
|
64
|
+
return { ...value };
|
|
65
|
+
} else return value;
|
|
88
66
|
}
|
|
89
67
|
function toPath(value) {
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
if (Array.isArray(value)) return value;
|
|
69
|
+
return value.replace(/\[(\d+)]/g, ".$1").replace(/^\./, "").replace(/\.$/, "").split(".");
|
|
92
70
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
} else {
|
|
121
|
-
children = $[1];
|
|
122
|
-
className = $[2];
|
|
123
|
-
is = $[3];
|
|
124
|
-
name = $[4];
|
|
125
|
-
props = $[5];
|
|
126
|
-
}
|
|
127
|
-
const formex = useFormex();
|
|
128
|
-
let field;
|
|
129
|
-
let t1;
|
|
130
|
-
if ($[6] !== children || $[7] !== formex || $[8] !== name || $[9] !== props) {
|
|
131
|
-
t1 = /* @__PURE__ */ Symbol.for("react.early_return_sentinel");
|
|
132
|
-
bb0: {
|
|
133
|
-
field = getFieldProps({
|
|
134
|
-
name,
|
|
135
|
-
...props
|
|
136
|
-
}, formex);
|
|
137
|
-
if (isFunction(children)) {
|
|
138
|
-
t1 = children({
|
|
139
|
-
field,
|
|
140
|
-
form: formex
|
|
141
|
-
});
|
|
142
|
-
break bb0;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
$[6] = children;
|
|
146
|
-
$[7] = formex;
|
|
147
|
-
$[8] = name;
|
|
148
|
-
$[9] = props;
|
|
149
|
-
$[10] = field;
|
|
150
|
-
$[11] = t1;
|
|
151
|
-
} else {
|
|
152
|
-
field = $[10];
|
|
153
|
-
t1 = $[11];
|
|
154
|
-
}
|
|
155
|
-
if (t1 !== /* @__PURE__ */ Symbol.for("react.early_return_sentinel")) {
|
|
156
|
-
return t1;
|
|
157
|
-
}
|
|
158
|
-
const asElement = is || "input";
|
|
159
|
-
if (typeof asElement === "string") {
|
|
160
|
-
let innerRef;
|
|
161
|
-
let rest;
|
|
162
|
-
if ($[12] !== props) {
|
|
163
|
-
({
|
|
164
|
-
innerRef,
|
|
165
|
-
...rest
|
|
166
|
-
} = props);
|
|
167
|
-
$[12] = props;
|
|
168
|
-
$[13] = innerRef;
|
|
169
|
-
$[14] = rest;
|
|
170
|
-
} else {
|
|
171
|
-
innerRef = $[13];
|
|
172
|
-
rest = $[14];
|
|
173
|
-
}
|
|
174
|
-
let t22;
|
|
175
|
-
if ($[15] !== asElement || $[16] !== children || $[17] !== className || $[18] !== field || $[19] !== innerRef || $[20] !== rest) {
|
|
176
|
-
let t3;
|
|
177
|
-
if ($[22] !== className || $[23] !== field || $[24] !== innerRef || $[25] !== rest) {
|
|
178
|
-
t3 = {
|
|
179
|
-
ref: innerRef,
|
|
180
|
-
...field,
|
|
181
|
-
...rest,
|
|
182
|
-
className
|
|
183
|
-
};
|
|
184
|
-
$[22] = className;
|
|
185
|
-
$[23] = field;
|
|
186
|
-
$[24] = innerRef;
|
|
187
|
-
$[25] = rest;
|
|
188
|
-
$[26] = t3;
|
|
189
|
-
} else {
|
|
190
|
-
t3 = $[26];
|
|
191
|
-
}
|
|
192
|
-
t22 = React.createElement(asElement, t3, children);
|
|
193
|
-
$[15] = asElement;
|
|
194
|
-
$[16] = children;
|
|
195
|
-
$[17] = className;
|
|
196
|
-
$[18] = field;
|
|
197
|
-
$[19] = innerRef;
|
|
198
|
-
$[20] = rest;
|
|
199
|
-
$[21] = t22;
|
|
200
|
-
} else {
|
|
201
|
-
t22 = $[21];
|
|
202
|
-
}
|
|
203
|
-
return t22;
|
|
204
|
-
}
|
|
205
|
-
let t2;
|
|
206
|
-
if ($[27] !== asElement || $[28] !== children || $[29] !== className || $[30] !== field || $[31] !== props) {
|
|
207
|
-
let t3;
|
|
208
|
-
if ($[33] !== className || $[34] !== field || $[35] !== props) {
|
|
209
|
-
t3 = {
|
|
210
|
-
...field,
|
|
211
|
-
...props,
|
|
212
|
-
className
|
|
213
|
-
};
|
|
214
|
-
$[33] = className;
|
|
215
|
-
$[34] = field;
|
|
216
|
-
$[35] = props;
|
|
217
|
-
$[36] = t3;
|
|
218
|
-
} else {
|
|
219
|
-
t3 = $[36];
|
|
220
|
-
}
|
|
221
|
-
t2 = React.createElement(asElement, t3, children);
|
|
222
|
-
$[27] = asElement;
|
|
223
|
-
$[28] = children;
|
|
224
|
-
$[29] = className;
|
|
225
|
-
$[30] = field;
|
|
226
|
-
$[31] = props;
|
|
227
|
-
$[32] = t2;
|
|
228
|
-
} else {
|
|
229
|
-
t2 = $[32];
|
|
230
|
-
}
|
|
231
|
-
return t2;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/Field.tsx
|
|
73
|
+
function Field({ validate, name, children, as: is, className, ...props }) {
|
|
74
|
+
const formex = useFormex();
|
|
75
|
+
const field = getFieldProps({
|
|
76
|
+
name,
|
|
77
|
+
...props
|
|
78
|
+
}, formex);
|
|
79
|
+
if (isFunction(children)) return children({
|
|
80
|
+
field,
|
|
81
|
+
form: formex
|
|
82
|
+
});
|
|
83
|
+
const asElement = is || "input";
|
|
84
|
+
if (typeof asElement === "string") {
|
|
85
|
+
const { innerRef, ...rest } = props;
|
|
86
|
+
return React$1.createElement(asElement, {
|
|
87
|
+
ref: innerRef,
|
|
88
|
+
...field,
|
|
89
|
+
...rest,
|
|
90
|
+
className
|
|
91
|
+
}, children);
|
|
92
|
+
}
|
|
93
|
+
return React$1.createElement(asElement, {
|
|
94
|
+
...field,
|
|
95
|
+
...props,
|
|
96
|
+
className
|
|
97
|
+
}, children);
|
|
232
98
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
field.checked = valueState === valueProp;
|
|
259
|
-
field.value = valueProp;
|
|
260
|
-
} else if (is === "select" && multiple) {
|
|
261
|
-
field.value = field.value || [];
|
|
262
|
-
field.multiple = true;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return field;
|
|
99
|
+
var getFieldProps = (nameOrOptions, formex) => {
|
|
100
|
+
const name = typeof nameOrOptions === "string" ? nameOrOptions : nameOrOptions.name;
|
|
101
|
+
const valueState = getIn(formex.values, name);
|
|
102
|
+
const field = {
|
|
103
|
+
name,
|
|
104
|
+
value: valueState,
|
|
105
|
+
onChange: formex.handleChange,
|
|
106
|
+
onBlur: formex.handleBlur
|
|
107
|
+
};
|
|
108
|
+
if (typeof nameOrOptions !== "string") {
|
|
109
|
+
const { type, value: valueProp, as: is, multiple } = nameOrOptions;
|
|
110
|
+
if (type === "checkbox") if (valueProp === void 0) field.checked = !!valueState;
|
|
111
|
+
else {
|
|
112
|
+
field.checked = !!(Array.isArray(valueState) && ~valueState.indexOf(valueProp));
|
|
113
|
+
field.value = valueProp;
|
|
114
|
+
}
|
|
115
|
+
else if (type === "radio") {
|
|
116
|
+
field.checked = valueState === valueProp;
|
|
117
|
+
field.value = valueProp;
|
|
118
|
+
} else if (is === "select" && multiple) {
|
|
119
|
+
field.value = field.value || [];
|
|
120
|
+
field.multiple = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return field;
|
|
266
124
|
};
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
version,
|
|
465
|
-
debugId: debugIdRef.current,
|
|
466
|
-
undo,
|
|
467
|
-
redo,
|
|
468
|
-
canUndo: historyIndexRef.current > 0,
|
|
469
|
-
canRedo: historyIndexRef.current < historyRef.current.length - 1
|
|
470
|
-
}), [values, errors, touchedState, dirty, isSubmitting, submitCount, isValidating, version, handleChange, handleBlur, setValues, setFieldValue, setFieldTouched, setTouchedState, setFieldError, validate, submit, resetForm, undo, redo]);
|
|
471
|
-
useEffect(() => {
|
|
472
|
-
controllerRef.current = controller;
|
|
473
|
-
}, [controller]);
|
|
474
|
-
return controller;
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/useCreateFormex.tsx
|
|
127
|
+
function useCreateFormex({ initialValues, initialErrors, initialDirty, initialTouched, validation, validateOnChange = false, validateOnInitialRender = false, onSubmit, onReset, onValuesChangeDeferred, debugId }) {
|
|
128
|
+
const initialValuesRef = useRef(initialValues);
|
|
129
|
+
const valuesRef = useRef(initialValues);
|
|
130
|
+
const debugIdRef = useRef(debugId);
|
|
131
|
+
const [values, setValuesInner] = useState(initialValues);
|
|
132
|
+
const [touchedState, setTouchedState] = useState(initialTouched ?? {});
|
|
133
|
+
const [errors, setErrors] = useState(initialErrors ?? {});
|
|
134
|
+
const [dirty, setDirty] = useState(initialDirty ?? false);
|
|
135
|
+
const [submitCount, setSubmitCount] = useState(0);
|
|
136
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
137
|
+
const [isValidating, setIsValidating] = useState(false);
|
|
138
|
+
const [version, setVersion] = useState(0);
|
|
139
|
+
const onValuesChangeRef = useRef(onValuesChangeDeferred);
|
|
140
|
+
onValuesChangeRef.current = onValuesChangeDeferred;
|
|
141
|
+
const debounceTimeoutRef = useRef(void 0);
|
|
142
|
+
const callDebouncedOnValuesChange = useCallback((values) => {
|
|
143
|
+
if (onValuesChangeRef.current) {
|
|
144
|
+
if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current);
|
|
145
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
146
|
+
onValuesChangeRef.current?.(values, controllerRef.current);
|
|
147
|
+
}, 300);
|
|
148
|
+
}
|
|
149
|
+
}, []);
|
|
150
|
+
const historyRef = useRef([initialValues]);
|
|
151
|
+
const historyIndexRef = useRef(0);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (validateOnInitialRender) validate();
|
|
154
|
+
}, []);
|
|
155
|
+
const setValues = useCallback((newValues) => {
|
|
156
|
+
valuesRef.current = newValues;
|
|
157
|
+
setValuesInner(newValues);
|
|
158
|
+
setDirty(!deepEqual(initialValuesRef.current, newValues));
|
|
159
|
+
const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);
|
|
160
|
+
newHistory.push(newValues);
|
|
161
|
+
historyRef.current = newHistory;
|
|
162
|
+
historyIndexRef.current = newHistory.length - 1;
|
|
163
|
+
callDebouncedOnValuesChange(newValues);
|
|
164
|
+
}, [callDebouncedOnValuesChange]);
|
|
165
|
+
const validate = useCallback(async () => {
|
|
166
|
+
setIsValidating(true);
|
|
167
|
+
const validationErrors = await validation?.(valuesRef.current);
|
|
168
|
+
setErrors(validationErrors ?? {});
|
|
169
|
+
setIsValidating(false);
|
|
170
|
+
return validationErrors;
|
|
171
|
+
}, [validation]);
|
|
172
|
+
const setFieldValue = useCallback((key, value, shouldValidate) => {
|
|
173
|
+
const newValues = setIn(valuesRef.current, key, value);
|
|
174
|
+
valuesRef.current = newValues;
|
|
175
|
+
setValuesInner(newValues);
|
|
176
|
+
if (!deepEqual(getIn(initialValuesRef.current, key), value)) setDirty(true);
|
|
177
|
+
if (shouldValidate) validate();
|
|
178
|
+
const newHistory = historyRef.current.slice(0, historyIndexRef.current + 1);
|
|
179
|
+
newHistory.push(newValues);
|
|
180
|
+
historyRef.current = newHistory;
|
|
181
|
+
historyIndexRef.current = newHistory.length - 1;
|
|
182
|
+
callDebouncedOnValuesChange(newValues);
|
|
183
|
+
}, [validate, callDebouncedOnValuesChange]);
|
|
184
|
+
const setFieldError = useCallback((key, error) => {
|
|
185
|
+
setErrors((prevErrors) => {
|
|
186
|
+
const newErrors = { ...prevErrors };
|
|
187
|
+
if (error) newErrors[key] = error;
|
|
188
|
+
else delete newErrors[key];
|
|
189
|
+
return newErrors;
|
|
190
|
+
});
|
|
191
|
+
}, []);
|
|
192
|
+
const setFieldTouched = useCallback((key, touched, shouldValidate) => {
|
|
193
|
+
setTouchedState((prev) => ({
|
|
194
|
+
...prev,
|
|
195
|
+
[key]: touched
|
|
196
|
+
}));
|
|
197
|
+
if (shouldValidate) validate();
|
|
198
|
+
}, [validate]);
|
|
199
|
+
const handleChange = useCallback((event) => {
|
|
200
|
+
const target = event.target;
|
|
201
|
+
let value;
|
|
202
|
+
if (target.type === "checkbox") value = target.checked;
|
|
203
|
+
else if (target.type === "number") value = target.valueAsNumber;
|
|
204
|
+
else value = target.value;
|
|
205
|
+
const name = target.name;
|
|
206
|
+
setFieldValue(name, value, validateOnChange);
|
|
207
|
+
setFieldTouched(name, true);
|
|
208
|
+
}, [
|
|
209
|
+
setFieldValue,
|
|
210
|
+
setFieldTouched,
|
|
211
|
+
validateOnChange
|
|
212
|
+
]);
|
|
213
|
+
const handleBlur = useCallback((event) => {
|
|
214
|
+
const name = event.target.name;
|
|
215
|
+
setFieldTouched(name, true);
|
|
216
|
+
}, [setFieldTouched]);
|
|
217
|
+
const submit = useCallback(async (e) => {
|
|
218
|
+
e?.preventDefault();
|
|
219
|
+
e?.stopPropagation();
|
|
220
|
+
setIsSubmitting(true);
|
|
221
|
+
setSubmitCount((prev) => prev + 1);
|
|
222
|
+
const validationErrors = await validation?.(valuesRef.current);
|
|
223
|
+
if (validationErrors && Object.keys(validationErrors).length > 0) setErrors(validationErrors);
|
|
224
|
+
else {
|
|
225
|
+
setErrors({});
|
|
226
|
+
await onSubmit?.(valuesRef.current, controllerRef.current);
|
|
227
|
+
}
|
|
228
|
+
setIsSubmitting(false);
|
|
229
|
+
setVersion((prev) => prev + 1);
|
|
230
|
+
}, [onSubmit, validation]);
|
|
231
|
+
const resetForm = useCallback((props) => {
|
|
232
|
+
const { submitCount: submitCountProp, values: valuesProp, errors: errorsProp, touched: touchedProp } = props ?? {};
|
|
233
|
+
valuesRef.current = valuesProp ?? initialValuesRef.current;
|
|
234
|
+
initialValuesRef.current = valuesProp ?? initialValuesRef.current;
|
|
235
|
+
setValuesInner(valuesProp ?? initialValuesRef.current);
|
|
236
|
+
setErrors(errorsProp ?? {});
|
|
237
|
+
setTouchedState(touchedProp ?? initialTouched ?? {});
|
|
238
|
+
setDirty(false);
|
|
239
|
+
setSubmitCount(submitCountProp ?? 0);
|
|
240
|
+
setVersion((prev) => prev + 1);
|
|
241
|
+
onReset?.(controllerRef.current);
|
|
242
|
+
historyRef.current = [valuesProp ?? initialValuesRef.current];
|
|
243
|
+
historyIndexRef.current = 0;
|
|
244
|
+
}, [onReset, initialTouched]);
|
|
245
|
+
const undo = useCallback(() => {
|
|
246
|
+
if (historyIndexRef.current > 0) {
|
|
247
|
+
const newIndex = historyIndexRef.current - 1;
|
|
248
|
+
const newValues = historyRef.current[newIndex];
|
|
249
|
+
setValuesInner(newValues);
|
|
250
|
+
valuesRef.current = newValues;
|
|
251
|
+
historyIndexRef.current = newIndex;
|
|
252
|
+
setDirty(!deepEqual(initialValuesRef.current, newValues));
|
|
253
|
+
callDebouncedOnValuesChange(newValues);
|
|
254
|
+
}
|
|
255
|
+
}, [callDebouncedOnValuesChange]);
|
|
256
|
+
const redo = useCallback(() => {
|
|
257
|
+
if (historyIndexRef.current < historyRef.current.length - 1) {
|
|
258
|
+
const newIndex = historyIndexRef.current + 1;
|
|
259
|
+
const newValues = historyRef.current[newIndex];
|
|
260
|
+
setValuesInner(newValues);
|
|
261
|
+
valuesRef.current = newValues;
|
|
262
|
+
historyIndexRef.current = newIndex;
|
|
263
|
+
setDirty(!deepEqual(initialValuesRef.current, newValues));
|
|
264
|
+
callDebouncedOnValuesChange(newValues);
|
|
265
|
+
}
|
|
266
|
+
}, [callDebouncedOnValuesChange]);
|
|
267
|
+
const controllerRef = useRef({});
|
|
268
|
+
const controller = useMemo(() => ({
|
|
269
|
+
values,
|
|
270
|
+
initialValues: initialValuesRef.current,
|
|
271
|
+
handleChange,
|
|
272
|
+
isSubmitting,
|
|
273
|
+
setSubmitting: setIsSubmitting,
|
|
274
|
+
setValues,
|
|
275
|
+
setFieldValue,
|
|
276
|
+
errors,
|
|
277
|
+
setFieldError,
|
|
278
|
+
touched: touchedState,
|
|
279
|
+
setFieldTouched,
|
|
280
|
+
setTouched: setTouchedState,
|
|
281
|
+
dirty,
|
|
282
|
+
setDirty,
|
|
283
|
+
handleSubmit: submit,
|
|
284
|
+
submitCount,
|
|
285
|
+
setSubmitCount,
|
|
286
|
+
handleBlur,
|
|
287
|
+
validate,
|
|
288
|
+
isValidating,
|
|
289
|
+
resetForm,
|
|
290
|
+
version,
|
|
291
|
+
debugId: debugIdRef.current,
|
|
292
|
+
undo,
|
|
293
|
+
redo,
|
|
294
|
+
canUndo: historyIndexRef.current > 0,
|
|
295
|
+
canRedo: historyIndexRef.current < historyRef.current.length - 1
|
|
296
|
+
}), [
|
|
297
|
+
values,
|
|
298
|
+
errors,
|
|
299
|
+
touchedState,
|
|
300
|
+
dirty,
|
|
301
|
+
isSubmitting,
|
|
302
|
+
submitCount,
|
|
303
|
+
isValidating,
|
|
304
|
+
version,
|
|
305
|
+
handleChange,
|
|
306
|
+
handleBlur,
|
|
307
|
+
setValues,
|
|
308
|
+
setFieldValue,
|
|
309
|
+
setFieldTouched,
|
|
310
|
+
setTouchedState,
|
|
311
|
+
setFieldError,
|
|
312
|
+
validate,
|
|
313
|
+
submit,
|
|
314
|
+
resetForm,
|
|
315
|
+
undo,
|
|
316
|
+
redo
|
|
317
|
+
]);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
controllerRef.current = controller;
|
|
320
|
+
}, [controller]);
|
|
321
|
+
return controller;
|
|
475
322
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
getIn,
|
|
481
|
-
isEmptyArray,
|
|
482
|
-
isFunction,
|
|
483
|
-
isInteger,
|
|
484
|
-
isNaN,
|
|
485
|
-
isObject,
|
|
486
|
-
setIn,
|
|
487
|
-
useCreateFormex,
|
|
488
|
-
useFormex
|
|
489
|
-
};
|
|
490
|
-
//# sourceMappingURL=index.es.js.map
|
|
323
|
+
//#endregion
|
|
324
|
+
export { Field, Formex, clone, getIn, isEmptyArray, isFunction, isInteger, isNaN, isObject, setIn, useCreateFormex, useFormex };
|
|
325
|
+
|
|
326
|
+
//# sourceMappingURL=index.es.js.map
|