react-form-manage 1.0.6-beta.0 → 1.0.6-beta.2

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 (54) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/components/Form/FormCleanUp.js +46 -0
  3. package/dist/components/Form/FormItem.d.ts +11 -17
  4. package/dist/components/Form/FormItem.js +46 -0
  5. package/dist/components/Form/FormList.d.ts +23 -7
  6. package/dist/components/Form/FormList.js +11 -0
  7. package/dist/components/Form/InputWrapper.d.ts +10 -5
  8. package/dist/components/Form/InputWrapper.js +7 -0
  9. package/dist/components/Input.d.ts +4 -5
  10. package/dist/components/Input.js +8 -0
  11. package/dist/constants/validation.js +33 -0
  12. package/dist/contexts/FormContext.js +2 -0
  13. package/dist/hooks/useFormItemControl.js +390 -0
  14. package/dist/hooks/useFormListControl.js +280 -0
  15. package/dist/index.d.ts +8 -185
  16. package/dist/index.js +9 -0
  17. package/dist/providers/Form.d.ts +15 -10
  18. package/dist/providers/Form.js +322 -0
  19. package/dist/stores/formStore.js +304 -0
  20. package/dist/types/index.d.ts +1 -0
  21. package/dist/types/index.js +1 -0
  22. package/dist/types/public.d.ts +51 -0
  23. package/dist/utils/obj.util.js +24 -0
  24. package/package.json +30 -45
  25. package/src/App.css +49 -0
  26. package/src/App.tsx +36 -0
  27. package/src/assets/react.svg +1 -0
  28. package/src/components/Form/FormCleanUp.tsx +57 -0
  29. package/src/components/Form/FormItem.tsx +72 -0
  30. package/src/components/Form/FormList.tsx +22 -0
  31. package/src/components/Form/InputWrapper.tsx +24 -0
  32. package/src/components/Input.jsx +13 -0
  33. package/src/components/Input.tsx +21 -0
  34. package/src/constants/validation.ts +63 -0
  35. package/src/contexts/FormContext.ts +3 -0
  36. package/src/hooks/useFormItemControl.ts +558 -0
  37. package/src/hooks/useFormListControl.ts +378 -0
  38. package/src/index.css +68 -0
  39. package/src/index.ts +35 -0
  40. package/src/main.tsx +10 -0
  41. package/src/providers/Form.tsx +440 -0
  42. package/src/stores/formStore.ts +477 -0
  43. package/src/types/index.ts +1 -0
  44. package/src/types/public.ts +50 -0
  45. package/src/utils/obj.util.ts +42 -0
  46. package/dist/App.d.ts +0 -2
  47. package/dist/index.cjs +0 -2
  48. package/dist/index.cjs.d.ts +0 -1
  49. package/dist/index.cjs.map +0 -1
  50. package/dist/index.esm.d.ts +0 -1
  51. package/dist/index.esm.js +0 -2
  52. package/dist/index.esm.js.map +0 -1
  53. package/src/types/components.d.ts +0 -135
  54. /package/dist/{main.d.ts → types/public.js} +0 -0
@@ -0,0 +1,558 @@
1
+ import { get, isNil } from "lodash";
2
+ import { useTaskEffect } from "minh-custom-hooks-release";
3
+ import { useEffect, useMemo, type RefObject } from "react";
4
+ import { useShallow } from "zustand/react/shallow"; // Import useShallow
5
+ import {
6
+ IS_ALPHABET_STRING_AND_NUMBER_REGEX,
7
+ IS_EMAIL_REGEX,
8
+ IS_NAME_REGEX,
9
+ IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX,
10
+ IS_NO_SPACE_ALPHABET_STRING_REGEX,
11
+ IS_NOSPACE_STRING_AND_NUMBER_REGEX,
12
+ IS_PASSWORD_REGEX,
13
+ IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX,
14
+ IS_POSITIVE_STRING_NUMBER_REGEX,
15
+ IS_STRING_AND_NUMBER_REGEX,
16
+ IS_STRING_NUMBER_REGEX,
17
+ IS_USERNAME_REGEX,
18
+ IS_VIETNAMESE_PHONE_NUMBER_REGEX,
19
+ } from "../constants/validation";
20
+ import { useFormContext } from "../providers/Form";
21
+ import {
22
+ useFormCleanUp,
23
+ useFormListeners,
24
+ useFormStore,
25
+ } from "../stores/formStore";
26
+
27
+ import type { FormInstance } from "../stores/formStore";
28
+
29
+ type AnyObject = Record<string, any>;
30
+
31
+ interface UseFormItemControlProps {
32
+ formName?: string;
33
+ form?: FormInstance;
34
+ name?: string;
35
+ initialValue?: any;
36
+ formItemId?: string;
37
+ rules?: any[];
38
+ elementRef?: RefObject<any> | null;
39
+ }
40
+
41
+ interface UseFormItemControlReturn {
42
+ value: any;
43
+ onChange: (value: any, options?: AnyObject) => void;
44
+ state: any;
45
+ errors: any;
46
+ onFocus: () => void;
47
+ isDirty?: boolean;
48
+ }
49
+
50
+ const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
51
+
52
+ export default function useFormItemControl<T = any>({
53
+ formName,
54
+ form,
55
+ name,
56
+ initialValue,
57
+ formItemId,
58
+ rules,
59
+ elementRef,
60
+ }: UseFormItemControlProps): UseFormItemControlReturn {
61
+ const contextForm = useFormContext();
62
+ const { value, setData, getCacheData, getFormValues } = useFormStore(
63
+ useShallow((state) => {
64
+ return {
65
+ value: get(
66
+ state.forms,
67
+ `${formName || form?.formName || contextForm?.formName}.${name}`,
68
+ ),
69
+ setData: state.setData,
70
+ getCacheData: state.getCacheData,
71
+ getFormValues: state.getFormValues,
72
+ };
73
+ }),
74
+ );
75
+
76
+ const { setCleanUpStack } = useFormCleanUp(
77
+ useShallow((state) => ({
78
+ setCleanUpStack: state.setCleanUpStack,
79
+ })),
80
+ );
81
+
82
+ const {
83
+ initValue: internalInitValue,
84
+ setInitData,
85
+ getInitData,
86
+ } = useFormStore(
87
+ useShallow((state) => {
88
+ // console.log(
89
+ // "Shallow get initValue: ",
90
+ // formName || form?.formName || contextForm?.formName,
91
+ // name,
92
+ // get(
93
+ // state.initialValues,
94
+ // `${formName || form?.formName || contextForm?.formName}.${name}`
95
+ // )
96
+ // );
97
+ return {
98
+ initValue: get(
99
+ state.initialValues,
100
+ `${formName || form?.formName || contextForm?.formName}.${name}`,
101
+ ),
102
+ setInitData: state.setInitData,
103
+ getInitData: state.getInitData,
104
+ };
105
+ }),
106
+ );
107
+
108
+ const { listener, setListener } = useFormListeners(
109
+ useShallow((state) => {
110
+ // console.log(
111
+ // "Get listener from store: ",
112
+ // state.listeners.find((l) => l.formItemId === formItemId)
113
+ // );
114
+ return {
115
+ listener: state.listeners.find((l) => l.formItemId === formItemId),
116
+ setListener: state.setListener,
117
+ };
118
+ }),
119
+ );
120
+
121
+ const onInitData = (value: T) => {
122
+ // console.log(
123
+ // "Init:",
124
+ // formName || form?.formName || contextForm?.formName,
125
+ // name,
126
+ // value
127
+ // );
128
+ setInitData(
129
+ formName || form?.formName || contextForm?.formName,
130
+ name,
131
+ value,
132
+ );
133
+ };
134
+
135
+ const onFocus = () => {
136
+ setListener({
137
+ formItemId,
138
+ isTouched: true,
139
+ isDirty: listener?.isDirty,
140
+ });
141
+ };
142
+
143
+ const emitFocus = () => {
144
+ if (elementRef?.current) {
145
+ elementRef.current.focus();
146
+ }
147
+ };
148
+
149
+ const onChange = (value: T, options?: any) => {
150
+ if (options?.notTriggerDirty !== true) {
151
+ setListener({
152
+ formItemId,
153
+ isDirty: true,
154
+ isTouched: listener?.isTouched,
155
+ });
156
+ }
157
+ setData(formName || form?.formName || contextForm?.formName, name, value);
158
+ };
159
+
160
+ const onReset = (value?: any) => {
161
+ console.log(
162
+ "Reset",
163
+ value,
164
+ getInitData(formName || form?.formName || contextForm?.formName, name),
165
+ );
166
+ onChange(
167
+ isNil(value)
168
+ ? getInitData(formName || form?.formName || contextForm?.formName, name)
169
+ : value,
170
+ );
171
+ };
172
+
173
+ // validate field rules
174
+ const internalRules = useMemo(() => {
175
+ return rules || [];
176
+ }, [rules]);
177
+
178
+ const { data: errors, state } = useTaskEffect({
179
+ async task() {
180
+ const listErrors = [];
181
+
182
+ const formValues = getFormValues(
183
+ formName || form?.formName || contextForm?.formName,
184
+ );
185
+
186
+ const ruleToTask = internalRules?.map((r, index) => async () => {
187
+ const ruleName = r?.name || index;
188
+
189
+ const fieldValue = value?.toString?.();
190
+
191
+ if (typeof r.handler === "function") {
192
+ if (!(await r.handler(value, formValues))) {
193
+ listErrors.push({
194
+ ruleName,
195
+ message: r.message,
196
+ });
197
+
198
+ return;
199
+ }
200
+ }
201
+
202
+ if (VALID_PREMITIVE_TYPE.includes(typeof value)) {
203
+ // REQUIRE
204
+ if (r.required && !(fieldValue?.toString()?.trim()?.length > 0)) {
205
+ // tempMessage.push("This field is required!");
206
+ listErrors.push({
207
+ ruleName,
208
+ message: r.message,
209
+ });
210
+
211
+ return;
212
+ }
213
+
214
+ // Min Max
215
+ if (!isNil(r.min) || !isNil(r.max)) {
216
+ if (isNil(value)) {
217
+ listErrors.push({
218
+ ruleName,
219
+ message: r.message,
220
+ });
221
+
222
+ return;
223
+ }
224
+ if (
225
+ (r.min ?? Number.MIN_SAFE_INTEGER) <=
226
+ (r.max ?? Number.MAX_SAFE_INTEGER)
227
+ ) {
228
+ if (!isNil(r.min)) {
229
+ if (!isNaN(Number(fieldValue)) && Number(fieldValue) <= r.min) {
230
+ // tempMessage.push(MIN_INVALID_MESSAGE);
231
+ listErrors.push({
232
+ ruleName,
233
+ message: r.message,
234
+ });
235
+
236
+ return;
237
+ }
238
+ }
239
+
240
+ if (!isNil(r.max)) {
241
+ if (!isNaN(Number(fieldValue)) && Number(fieldValue) >= r.max) {
242
+ // tempMessage.push(MAX_INVALID_MESSAGE);
243
+ listErrors.push({
244
+ ruleName,
245
+ message: r.message,
246
+ });
247
+
248
+ return;
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ // MIN LENGTH MAX LENGTH
255
+ if (!isNil(r.minLength) || !isNil(r.maxLength)) {
256
+ if (
257
+ (r.minLength ?? Number.MIN_SAFE_INTEGER) <=
258
+ (r.maxLength ?? Number.MAX_SAFE_INTEGER)
259
+ ) {
260
+ if (!isNil(r.minLength)) {
261
+ if (fieldValue?.length <= r.minLength) {
262
+ // tempMessage.push(MIN_INVALID_LENGTH_MESSAGE);
263
+ listErrors.push({
264
+ ruleName,
265
+ message: r.message,
266
+ });
267
+
268
+ return;
269
+ }
270
+ }
271
+
272
+ if (!isNil(r.maxLength)) {
273
+ if (fieldValue?.length >= r.maxLength) {
274
+ // tempMessage.push(MAX_INVALID_LENGTH_MESSAGE);
275
+ listErrors.push({
276
+ ruleName,
277
+ message: r.message,
278
+ });
279
+
280
+ return;
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ // Password
287
+ if (r.isPassword && !IS_PASSWORD_REGEX.test(fieldValue)) {
288
+ // tempMessage.push(IS_PASSWORD_INVALID_MESSAGE);
289
+ listErrors.push({
290
+ ruleName,
291
+ message: r.message,
292
+ });
293
+
294
+ return;
295
+ }
296
+
297
+ // Username
298
+ if (r.isUsername && !IS_USERNAME_REGEX.test(fieldValue)) {
299
+ // tempMessage.push(IS_USERNAME_INVALID_MESSAGE);
300
+ listErrors.push({
301
+ ruleName,
302
+ message: r.message,
303
+ });
304
+
305
+ return;
306
+ }
307
+
308
+ // Is Vietnamese Phone number
309
+ if (
310
+ r.isPhoneNumber &&
311
+ !IS_VIETNAMESE_PHONE_NUMBER_REGEX.test(fieldValue)
312
+ ) {
313
+ // tempMessage.push(IS_VIETNAMESE_PHONE_NUMBER_INVALID_MESSAGE);
314
+ listErrors.push({
315
+ ruleName,
316
+ message: r.message,
317
+ });
318
+
319
+ return;
320
+ }
321
+
322
+ // String number
323
+ if (r.isStringNumber && !IS_STRING_NUMBER_REGEX.test(fieldValue)) {
324
+ // tempMessage.push(IS_STRING_NUMBER_INVALID_MESSAGE);
325
+ listErrors.push({
326
+ ruleName,
327
+ message: r.message,
328
+ });
329
+
330
+ return;
331
+ }
332
+
333
+ // String positive number
334
+ if (
335
+ r.isPositiveStringNumber &&
336
+ !IS_POSITIVE_STRING_NUMBER_REGEX.test(fieldValue)
337
+ ) {
338
+ // tempMessage.push(IS_POSITIVE_STRING_NUMBER_INVALID_MESSAGE);
339
+ listErrors.push({
340
+ ruleName,
341
+ message: r.message,
342
+ });
343
+
344
+ return;
345
+ }
346
+
347
+ // String positive integer number
348
+ if (
349
+ r.isPositiveIntegerStringNumber &&
350
+ !IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX.test(fieldValue)
351
+ ) {
352
+ // tempMessage.push(
353
+ listErrors.push({
354
+ ruleName,
355
+ message: r.message,
356
+ });
357
+
358
+ return;
359
+ // IS_POSITIVE_INTEGER_STRING_NUMBER_INVALID_MESSAGE
360
+ // );
361
+ }
362
+
363
+ // Email
364
+ if (r.isEmail && !IS_EMAIL_REGEX.test(fieldValue)) {
365
+ // tempMessage.push(IS_EMAIL_INVALID_MESSAGE);
366
+ listErrors.push({
367
+ ruleName,
368
+ message: r.message,
369
+ });
370
+
371
+ return;
372
+ }
373
+
374
+ // Vietnamese Name
375
+ if (r.isVietnameseName && !IS_NAME_REGEX.test(fieldValue)) {
376
+ // tempMessage.push(IS_NAME_INVALID_MESSAGE);
377
+ listErrors.push({
378
+ ruleName,
379
+ message: r.message,
380
+ });
381
+
382
+ return;
383
+ }
384
+
385
+ // String and number without whitespace
386
+ if (
387
+ r.isNoSpaceStringAndNumber &&
388
+ !IS_NOSPACE_STRING_AND_NUMBER_REGEX.test(fieldValue)
389
+ ) {
390
+ // tempMessage.push(IS_NOSPACE_STRING_AND_NUMBER_MESSAGE);
391
+ listErrors.push({
392
+ ruleName,
393
+ message: r.message,
394
+ });
395
+
396
+ return;
397
+ }
398
+
399
+ // String and number with
400
+ if (
401
+ r.isStringAndNumber &&
402
+ !IS_STRING_AND_NUMBER_REGEX.test(fieldValue)
403
+ ) {
404
+ // tempMessage.push(IS_STRING_AND_NUMBER_MESSAGE);
405
+ listErrors.push({
406
+ ruleName,
407
+ message: r.message,
408
+ });
409
+
410
+ return;
411
+ }
412
+
413
+ if (
414
+ r.isNoSpaceOnlyAlphabetStringAndNumber &&
415
+ !IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)
416
+ ) {
417
+ // tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_MESSAGE);
418
+ listErrors.push({
419
+ ruleName,
420
+ message: r.message,
421
+ });
422
+
423
+ return;
424
+ }
425
+ if (
426
+ r.isOnlyAlphabetStringAndNumber &&
427
+ !IS_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)
428
+ ) {
429
+ // tempMessage.push(IS_ALPHABET_STRING_AND_NUMBER_MESSAGE);
430
+ listErrors.push({
431
+ ruleName,
432
+ message: r.message,
433
+ });
434
+
435
+ return;
436
+ }
437
+ if (
438
+ r.isNoSpaceAlphabetString &&
439
+ !IS_NO_SPACE_ALPHABET_STRING_REGEX.test(fieldValue)
440
+ ) {
441
+ // tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_MESSAGE);
442
+ listErrors.push({
443
+ ruleName,
444
+ message: r.message,
445
+ });
446
+
447
+ return;
448
+ }
449
+ }
450
+ });
451
+
452
+ // console.log("Trigger validate: ", listErrors);
453
+
454
+ for (const rAsync of ruleToTask) {
455
+ await rAsync();
456
+ }
457
+
458
+ setListener({ formItemId, internalErrors: listErrors });
459
+
460
+ return listErrors;
461
+ },
462
+ deps: [internalRules, value],
463
+ onError(err) {
464
+ console.log(err);
465
+ },
466
+ });
467
+
468
+ // useEffect(() => {
469
+ // console.log({ name, errors });
470
+ // }, [errors]);
471
+
472
+ // useEffect(() => {
473
+ // console.log({ name, listener });
474
+ // }, [listener]);
475
+
476
+ useEffect(() => {
477
+ // console.log({ internalInitValue, value, initialValue });
478
+ // console.log("Init item value: ", name, value, internalInitValue);
479
+ // console.log("internalInitValue: ", internalInitValue);
480
+
481
+ if (!value) {
482
+ if (!internalInitValue) {
483
+ if (!isNil(initialValue)) {
484
+ console.log("On init data", initialValue);
485
+ onInitData(initialValue);
486
+ }
487
+ } else {
488
+ onChange(internalInitValue);
489
+ }
490
+ }
491
+ }, [internalInitValue]);
492
+
493
+ useEffect(() => {
494
+ if (!listener) {
495
+ setListener({
496
+ onChange,
497
+ emitFocus,
498
+ isTouched: false,
499
+ isDirty: false,
500
+ name,
501
+ formName: formName || form?.formName || contextForm?.formName,
502
+ formItemId,
503
+ onReset,
504
+ });
505
+ }
506
+
507
+ // return () => {
508
+ // console.log("Revoke listener", listener);
509
+
510
+ // };
511
+ }, []);
512
+
513
+ useEffect(() => {
514
+ if (listener) {
515
+ setListener({
516
+ formItemId,
517
+ onChange,
518
+ name,
519
+ formName: formName || form?.formName || contextForm?.formName,
520
+ onReset,
521
+ isTouched: listener?.isTouched,
522
+ isDirty: listener?.isDirty,
523
+ });
524
+ }
525
+ const cacheData = getCacheData(
526
+ formName || form?.formName || contextForm?.formName,
527
+ );
528
+
529
+ // console.log("Get cache Data after list change: ", cacheData);
530
+
531
+ if (cacheData) {
532
+ const getNewDataFromCache = get(cacheData, name);
533
+
534
+ // console.log("Init data when change form ite: ", name, cacheData);
535
+
536
+ if (!getNewDataFromCache) {
537
+ onChange(initialValue);
538
+ } else onChange(getNewDataFromCache);
539
+ }
540
+ }, [name, formName || form?.formName || contextForm?.formName]);
541
+
542
+ useEffect(() => {
543
+ return () => {
544
+ setCleanUpStack({
545
+ itemKey: formItemId,
546
+ });
547
+ };
548
+ }, []);
549
+
550
+ return {
551
+ value: value as T,
552
+ onChange,
553
+ state,
554
+ errors,
555
+ onFocus,
556
+ isDirty: listener?.isDirty,
557
+ };
558
+ }