mobx-formkit 1.0.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.
Files changed (256) hide show
  1. package/AGENTS.md +761 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/lib/ArrayMap.d.ts +50 -0
  5. package/lib/ArrayMap.d.ts.map +1 -0
  6. package/lib/ArrayMap.js +151 -0
  7. package/lib/ArrayMap.js.map +1 -0
  8. package/lib/Base.d.ts +237 -0
  9. package/lib/Base.d.ts.map +1 -0
  10. package/lib/Base.js +872 -0
  11. package/lib/Base.js.map +1 -0
  12. package/lib/Bindings.d.ts +28 -0
  13. package/lib/Bindings.d.ts.map +1 -0
  14. package/lib/Bindings.js +73 -0
  15. package/lib/Bindings.js.map +1 -0
  16. package/lib/Field.d.ts +195 -0
  17. package/lib/Field.d.ts.map +1 -0
  18. package/lib/Field.js +734 -0
  19. package/lib/Field.js.map +1 -0
  20. package/lib/Form.d.ts +75 -0
  21. package/lib/Form.d.ts.map +1 -0
  22. package/lib/Form.js +199 -0
  23. package/lib/Form.js.map +1 -0
  24. package/lib/Options.d.ts +9 -0
  25. package/lib/Options.d.ts.map +1 -0
  26. package/lib/Options.js +85 -0
  27. package/lib/Options.js.map +1 -0
  28. package/lib/State.d.ts +43 -0
  29. package/lib/State.d.ts.map +1 -0
  30. package/lib/State.js +147 -0
  31. package/lib/State.js.map +1 -0
  32. package/lib/Validator.d.ts +25 -0
  33. package/lib/Validator.d.ts.map +1 -0
  34. package/lib/Validator.js +143 -0
  35. package/lib/Validator.js.map +1 -0
  36. package/lib/compat.d.ts +29 -0
  37. package/lib/compat.d.ts.map +1 -0
  38. package/lib/compat.js +117 -0
  39. package/lib/compat.js.map +1 -0
  40. package/lib/composer.d.ts +26 -0
  41. package/lib/composer.d.ts.map +1 -0
  42. package/lib/composer.js +65 -0
  43. package/lib/composer.js.map +1 -0
  44. package/lib/esm/ArrayMap.js +146 -0
  45. package/lib/esm/ArrayMap.js.map +1 -0
  46. package/lib/esm/Base.js +868 -0
  47. package/lib/esm/Base.js.map +1 -0
  48. package/lib/esm/Bindings.js +69 -0
  49. package/lib/esm/Bindings.js.map +1 -0
  50. package/lib/esm/Field.js +730 -0
  51. package/lib/esm/Field.js.map +1 -0
  52. package/lib/esm/Form.js +195 -0
  53. package/lib/esm/Form.js.map +1 -0
  54. package/lib/esm/Options.js +81 -0
  55. package/lib/esm/Options.js.map +1 -0
  56. package/lib/esm/State.js +143 -0
  57. package/lib/esm/State.js.map +1 -0
  58. package/lib/esm/Validator.js +139 -0
  59. package/lib/esm/Validator.js.map +1 -0
  60. package/lib/esm/compat.js +96 -0
  61. package/lib/esm/compat.js.map +1 -0
  62. package/lib/esm/composer.js +63 -0
  63. package/lib/esm/composer.js.map +1 -0
  64. package/lib/esm/index.js +8 -0
  65. package/lib/esm/index.js.map +1 -0
  66. package/lib/esm/models/FieldProps.js +116 -0
  67. package/lib/esm/models/FieldProps.js.map +1 -0
  68. package/lib/esm/models/OptionsModel.js +51 -0
  69. package/lib/esm/models/OptionsModel.js.map +1 -0
  70. package/lib/esm/models/StateInterface.js +9 -0
  71. package/lib/esm/models/StateInterface.js.map +1 -0
  72. package/lib/esm/models/ValidatorInterface.js +8 -0
  73. package/lib/esm/models/ValidatorInterface.js.map +1 -0
  74. package/lib/esm/parser.js +235 -0
  75. package/lib/esm/parser.js.map +1 -0
  76. package/lib/esm/props.js +118 -0
  77. package/lib/esm/props.js.map +1 -0
  78. package/lib/esm/utils.js +156 -0
  79. package/lib/esm/utils.js.map +1 -0
  80. package/lib/esm/validators/AJV.js +105 -0
  81. package/lib/esm/validators/AJV.js.map +1 -0
  82. package/lib/esm/validators/DVR.js +104 -0
  83. package/lib/esm/validators/DVR.js.map +1 -0
  84. package/lib/esm/validators/JOI.js +54 -0
  85. package/lib/esm/validators/JOI.js.map +1 -0
  86. package/lib/esm/validators/VALIBOT.js +50 -0
  87. package/lib/esm/validators/VALIBOT.js.map +1 -0
  88. package/lib/esm/validators/VINEJS.js +60 -0
  89. package/lib/esm/validators/VINEJS.js.map +1 -0
  90. package/lib/esm/validators/VJF.js +88 -0
  91. package/lib/esm/validators/VJF.js.map +1 -0
  92. package/lib/esm/validators/YUP.js +69 -0
  93. package/lib/esm/validators/YUP.js.map +1 -0
  94. package/lib/esm/validators/ZOD.js +43 -0
  95. package/lib/esm/validators/ZOD.js.map +1 -0
  96. package/lib/index.d.ts +7 -0
  97. package/lib/index.d.ts.map +1 -0
  98. package/lib/index.js +15 -0
  99. package/lib/index.js.map +1 -0
  100. package/lib/models/BaseInterface.d.ts +40 -0
  101. package/lib/models/BaseInterface.d.ts.map +1 -0
  102. package/lib/models/BindingsInterface.d.ts +18 -0
  103. package/lib/models/BindingsInterface.d.ts.map +1 -0
  104. package/lib/models/FieldInterface.d.ts +88 -0
  105. package/lib/models/FieldInterface.d.ts.map +1 -0
  106. package/lib/models/FieldProps.d.ts +111 -0
  107. package/lib/models/FieldProps.d.ts.map +1 -0
  108. package/lib/models/FieldProps.js +116 -0
  109. package/lib/models/FieldProps.js.map +1 -0
  110. package/lib/models/FormInterface.d.ts +184 -0
  111. package/lib/models/FormInterface.d.ts.map +1 -0
  112. package/lib/models/OptionsInterface.d.ts +8 -0
  113. package/lib/models/OptionsInterface.d.ts.map +1 -0
  114. package/lib/models/OptionsModel.d.ts +99 -0
  115. package/lib/models/OptionsModel.d.ts.map +1 -0
  116. package/lib/models/OptionsModel.js +51 -0
  117. package/lib/models/OptionsModel.js.map +1 -0
  118. package/lib/models/SharedActionsInterface.d.ts +27 -0
  119. package/lib/models/SharedActionsInterface.d.ts.map +1 -0
  120. package/lib/models/SharedEventsInterface.d.ts +8 -0
  121. package/lib/models/SharedEventsInterface.d.ts.map +1 -0
  122. package/lib/models/SharedHelpersInterface.d.ts +26 -0
  123. package/lib/models/SharedHelpersInterface.d.ts.map +1 -0
  124. package/lib/models/SharedInitializerInterface.d.ts +6 -0
  125. package/lib/models/SharedInitializerInterface.d.ts.map +1 -0
  126. package/lib/models/SharedUtilsInterface.d.ts +10 -0
  127. package/lib/models/SharedUtilsInterface.d.ts.map +1 -0
  128. package/lib/models/StateInterface.d.ts +37 -0
  129. package/lib/models/StateInterface.d.ts.map +1 -0
  130. package/lib/models/StateInterface.js +9 -0
  131. package/lib/models/StateInterface.js.map +1 -0
  132. package/lib/models/ValidatorInterface.d.ts +74 -0
  133. package/lib/models/ValidatorInterface.d.ts.map +1 -0
  134. package/lib/models/ValidatorInterface.js +8 -0
  135. package/lib/models/ValidatorInterface.js.map +1 -0
  136. package/lib/parser.d.ts +13 -0
  137. package/lib/parser.d.ts.map +1 -0
  138. package/lib/parser.js +247 -0
  139. package/lib/parser.js.map +1 -0
  140. package/lib/props.d.ts +14 -0
  141. package/lib/props.d.ts.map +1 -0
  142. package/lib/props.js +120 -0
  143. package/lib/props.js.map +1 -0
  144. package/lib/utils.d.ts +34 -0
  145. package/lib/utils.d.ts.map +1 -0
  146. package/lib/utils.js +182 -0
  147. package/lib/utils.js.map +1 -0
  148. package/lib/validators/AJV.d.ts +4 -0
  149. package/lib/validators/AJV.d.ts.map +1 -0
  150. package/lib/validators/AJV.js +109 -0
  151. package/lib/validators/AJV.js.map +1 -0
  152. package/lib/validators/DVR.d.ts +4 -0
  153. package/lib/validators/DVR.d.ts.map +1 -0
  154. package/lib/validators/DVR.js +108 -0
  155. package/lib/validators/DVR.js.map +1 -0
  156. package/lib/validators/JOI.d.ts +4 -0
  157. package/lib/validators/JOI.d.ts.map +1 -0
  158. package/lib/validators/JOI.js +58 -0
  159. package/lib/validators/JOI.js.map +1 -0
  160. package/lib/validators/VALIBOT.d.ts +4 -0
  161. package/lib/validators/VALIBOT.d.ts.map +1 -0
  162. package/lib/validators/VALIBOT.js +73 -0
  163. package/lib/validators/VALIBOT.js.map +1 -0
  164. package/lib/validators/VINEJS.d.ts +4 -0
  165. package/lib/validators/VINEJS.d.ts.map +1 -0
  166. package/lib/validators/VINEJS.js +64 -0
  167. package/lib/validators/VINEJS.js.map +1 -0
  168. package/lib/validators/VJF.d.ts +4 -0
  169. package/lib/validators/VJF.d.ts.map +1 -0
  170. package/lib/validators/VJF.js +92 -0
  171. package/lib/validators/VJF.js.map +1 -0
  172. package/lib/validators/YUP.d.ts +4 -0
  173. package/lib/validators/YUP.d.ts.map +1 -0
  174. package/lib/validators/YUP.js +73 -0
  175. package/lib/validators/YUP.js.map +1 -0
  176. package/lib/validators/ZOD.d.ts +4 -0
  177. package/lib/validators/ZOD.d.ts.map +1 -0
  178. package/lib/validators/ZOD.js +47 -0
  179. package/lib/validators/ZOD.js.map +1 -0
  180. package/package.json +183 -0
  181. package/src/ArrayMap.ts +165 -0
  182. package/src/Base.ts +1229 -0
  183. package/src/Bindings.ts +85 -0
  184. package/src/Field.ts +1034 -0
  185. package/src/Form.ts +264 -0
  186. package/src/Options.ts +91 -0
  187. package/src/State.ts +183 -0
  188. package/src/Validator.ts +177 -0
  189. package/src/compat.ts +104 -0
  190. package/src/composer.ts +82 -0
  191. package/src/index.ts +7 -0
  192. package/src/models/BaseInterface.ts +45 -0
  193. package/src/models/BindingsInterface.ts +25 -0
  194. package/src/models/FieldInterface.ts +97 -0
  195. package/src/models/FieldProps.ts +116 -0
  196. package/src/models/FormInterface.ts +203 -0
  197. package/src/models/OptionsInterface.ts +9 -0
  198. package/src/models/OptionsModel.ts +100 -0
  199. package/src/models/SharedActionsInterface.ts +30 -0
  200. package/src/models/SharedEventsInterface.ts +8 -0
  201. package/src/models/SharedHelpersInterface.ts +12 -0
  202. package/src/models/SharedInitializerInterface.ts +6 -0
  203. package/src/models/SharedUtilsInterface.ts +10 -0
  204. package/src/models/StateInterface.ts +44 -0
  205. package/src/models/ValidatorInterface.ts +88 -0
  206. package/src/parser.ts +327 -0
  207. package/src/props.ts +127 -0
  208. package/src/utils.ts +218 -0
  209. package/src/validators/AJV.ts +140 -0
  210. package/src/validators/DVR.ts +145 -0
  211. package/src/validators/JOI.ts +79 -0
  212. package/src/validators/VALIBOT.ts +70 -0
  213. package/src/validators/VINEJS.ts +83 -0
  214. package/src/validators/VJF.ts +123 -0
  215. package/src/validators/YUP.ts +90 -0
  216. package/src/validators/ZOD.ts +66 -0
  217. package/umd/MobxFormkit.umd.js +3093 -0
  218. package/umd/MobxFormkit.umd.js.map +1 -0
  219. package/umd/MobxFormkit.umd.min.js +2 -0
  220. package/umd/MobxFormkit.umd.min.js.map +1 -0
  221. package/umd/MobxFormkitComposer.umd.js +183 -0
  222. package/umd/MobxFormkitComposer.umd.js.map +1 -0
  223. package/umd/MobxFormkitComposer.umd.min.js +2 -0
  224. package/umd/MobxFormkitComposer.umd.min.js.map +1 -0
  225. package/umd/MobxFormkitValidatorAJV.umd.js +113 -0
  226. package/umd/MobxFormkitValidatorAJV.umd.js.map +1 -0
  227. package/umd/MobxFormkitValidatorAJV.umd.min.js +2 -0
  228. package/umd/MobxFormkitValidatorAJV.umd.min.js.map +1 -0
  229. package/umd/MobxFormkitValidatorDVR.umd.js +110 -0
  230. package/umd/MobxFormkitValidatorDVR.umd.js.map +1 -0
  231. package/umd/MobxFormkitValidatorDVR.umd.min.js +2 -0
  232. package/umd/MobxFormkitValidatorDVR.umd.min.js.map +1 -0
  233. package/umd/MobxFormkitValidatorJOI.umd.js +62 -0
  234. package/umd/MobxFormkitValidatorJOI.umd.js.map +1 -0
  235. package/umd/MobxFormkitValidatorJOI.umd.min.js +2 -0
  236. package/umd/MobxFormkitValidatorJOI.umd.min.js.map +1 -0
  237. package/umd/MobxFormkitValidatorVALIBOT.umd.js +8032 -0
  238. package/umd/MobxFormkitValidatorVALIBOT.umd.js.map +1 -0
  239. package/umd/MobxFormkitValidatorVALIBOT.umd.min.js +2 -0
  240. package/umd/MobxFormkitValidatorVALIBOT.umd.min.js.map +1 -0
  241. package/umd/MobxFormkitValidatorVINEJS.umd.js +11737 -0
  242. package/umd/MobxFormkitValidatorVINEJS.umd.js.map +1 -0
  243. package/umd/MobxFormkitValidatorVINEJS.umd.min.js +2 -0
  244. package/umd/MobxFormkitValidatorVINEJS.umd.min.js.map +1 -0
  245. package/umd/MobxFormkitValidatorVJF.umd.js +96 -0
  246. package/umd/MobxFormkitValidatorVJF.umd.js.map +1 -0
  247. package/umd/MobxFormkitValidatorVJF.umd.min.js +2 -0
  248. package/umd/MobxFormkitValidatorVJF.umd.min.js.map +1 -0
  249. package/umd/MobxFormkitValidatorYUP.umd.js +77 -0
  250. package/umd/MobxFormkitValidatorYUP.umd.js.map +1 -0
  251. package/umd/MobxFormkitValidatorYUP.umd.min.js +2 -0
  252. package/umd/MobxFormkitValidatorYUP.umd.min.js.map +1 -0
  253. package/umd/MobxFormkitValidatorZOD.umd.js +49 -0
  254. package/umd/MobxFormkitValidatorZOD.umd.js.map +1 -0
  255. package/umd/MobxFormkitValidatorZOD.umd.min.js +2 -0
  256. package/umd/MobxFormkitValidatorZOD.umd.min.js.map +1 -0
package/src/Form.ts ADDED
@@ -0,0 +1,264 @@
1
+ import {
2
+ action,
3
+ computed,
4
+ observable,
5
+ autorun,
6
+ runInAction,
7
+ } from "mobx";
8
+ import { makeObservable } from "./compat";
9
+ import { debounce, each, merge } from "lodash";
10
+
11
+ import Base from "./Base";
12
+ import Validator from "./Validator";
13
+ import State from "./State";
14
+ import Field from "./Field";
15
+ import ValidatorInterface from "./models/ValidatorInterface";
16
+ import { FieldInterface, FieldConstructor } from "./models/FieldInterface";
17
+ import {
18
+ FormInterface,
19
+ FieldsDefinitions,
20
+ FormConfig,
21
+ PathsOf,
22
+ } from "./models/FormInterface";
23
+ import { FieldPropsEnum } from "./models/FieldProps";
24
+ import { OptionsEnum } from "./models/OptionsModel";
25
+
26
+ export default class Form<F extends Record<string, any> = Record<string, any>> extends Base<F> implements FormInterface<F> {
27
+ name: string;
28
+ override path: any = null;
29
+ extra: Record<string, any> = {};
30
+ validator: ValidatorInterface;
31
+
32
+ debouncedValidation: any = null;
33
+
34
+ constructor(
35
+ setup: FieldsDefinitions = {},
36
+ {
37
+ name = "",
38
+ options = {},
39
+ plugins = {},
40
+ bindings = {},
41
+ hooks = {},
42
+ handlers = {},
43
+ extra = {},
44
+ }: FormConfig = {}
45
+ ) {
46
+ super();
47
+ makeObservable(this, {
48
+ extra: observable,
49
+ fields: observable,
50
+ flatMapValues: computed,
51
+ error: computed,
52
+ hasError: computed,
53
+ isValid: computed,
54
+ isPristine: computed,
55
+ isDirty: computed,
56
+ isDefault: computed,
57
+ isEmpty: computed,
58
+ focused: computed,
59
+ touched: computed,
60
+ disabled: computed,
61
+ // init: action,
62
+ invalidate: action,
63
+ clear: action,
64
+ reset: action,
65
+ resetValidation: action,
66
+ });
67
+
68
+ this.name = name;
69
+ this.extra = extra;
70
+ runInAction(() => (this.$hooks = hooks));
71
+ runInAction(() => (this.$handlers = handlers));
72
+
73
+ // load data from initializers methods
74
+ const initial = each(
75
+ {
76
+ setup,
77
+ options,
78
+ plugins,
79
+ bindings,
80
+ },
81
+ (val, key) =>
82
+ typeof (this as any)[key] === "function"
83
+ ? merge(val, (this as any)[key].apply(this, [this]))
84
+ : val
85
+ );
86
+
87
+ // setup hooks & handlers from initialization methods
88
+ runInAction(() =>
89
+ Object.assign(this.$hooks, (this as any).hooks?.apply(this, [this]))
90
+ );
91
+ runInAction(() =>
92
+ Object.assign(this.$handlers, (this as any).handlers?.apply(this, [this]))
93
+ );
94
+
95
+ this.state = new State({
96
+ form: this as any,
97
+ initial: initial.setup,
98
+ options: initial.options,
99
+ bindings: initial.bindings,
100
+ });
101
+
102
+ this.validator = new Validator({
103
+ form: this as any,
104
+ plugins: initial.plugins,
105
+ });
106
+
107
+ this.initFields(initial.setup);
108
+
109
+ this.debouncedValidation = debounce(
110
+ this.validate,
111
+ this.state.options.get(OptionsEnum.validationDebounceWait),
112
+ this.state.options.get(OptionsEnum.validationDebounceOptions)
113
+ );
114
+
115
+ // execute validation on form initialization
116
+ this.state.options.get(OptionsEnum.validateOnInit) &&
117
+ this.validator.validate({
118
+ showErrors: this.state.options.get(OptionsEnum.showErrorsOnInit),
119
+ });
120
+
121
+ this.execHook(FieldPropsEnum.onInit);
122
+
123
+ // handle Form onChange Hook
124
+ autorun(() => this.$changed && this.execHook(FieldPropsEnum.onChange));
125
+ }
126
+
127
+ /* ------------------------------------------------------------------ */
128
+ /* COMPUTED */
129
+
130
+ get flatMapValues(): Record<string, any> {
131
+ const data: any = {};
132
+ this.each(($field: any) => (data[$field.path] = $field.validatedValue));
133
+ return data;
134
+ }
135
+
136
+ get error(): string | null {
137
+ return this.validator.error || this.firstError() || null;
138
+ }
139
+
140
+ get hasError(): boolean {
141
+ return !!this.validator.error || this.check(FieldPropsEnum.hasError, true);
142
+ }
143
+
144
+ get isValid(): boolean {
145
+ return !this.validator.error && this.check(FieldPropsEnum.isValid, true);
146
+ }
147
+
148
+ get isPristine(): boolean {
149
+ return this.check(FieldPropsEnum.isPristine, true);
150
+ }
151
+
152
+ get isDirty(): boolean {
153
+ return this.check(FieldPropsEnum.isDirty, true);
154
+ }
155
+
156
+ get isDefault(): boolean {
157
+ return this.check(FieldPropsEnum.isDefault, true);
158
+ }
159
+
160
+ get isEmpty(): boolean {
161
+ return this.check(FieldPropsEnum.isEmpty, true);
162
+ }
163
+
164
+ get focused(): boolean {
165
+ return this.check(FieldPropsEnum.focused, true);
166
+ }
167
+
168
+ get touched(): boolean {
169
+ return this.check(FieldPropsEnum.touched, true);
170
+ }
171
+
172
+ get disabled(): boolean {
173
+ return this.check(FieldPropsEnum.disabled, true);
174
+ }
175
+
176
+ makeField(data: FieldConstructor, FieldClass: typeof Field = Field) {
177
+ return new FieldClass(data);
178
+ }
179
+
180
+ /**
181
+ * Select a field by key with type inference.
182
+ * Provides transparent autocomplete for both top-level keys (`keyof F`)
183
+ * AND nested paths (`PathsOf<F>`) without any type annotations needed.
184
+ *
185
+ * @example
186
+ * // Top-level keys — autocomplete from keyof F:
187
+ * form.$('username'); // returns Field<string>
188
+ *
189
+ * @example
190
+ * // Nested paths — autocomplete from PathsOf<F>:
191
+ * form.$('club'); // "club"
192
+ * form.$('club.name'); // "club.name" ← autocomplete after dot!
193
+ * form.$('members[].firstname'); // "members[].firstname"
194
+ */
195
+ override $(key: keyof F | PathsOf<F>): Field<F[keyof F]> {
196
+ return super.$(key as string) as Field<F[keyof F]>;
197
+ }
198
+
199
+ /**
200
+ * Select a field by path.
201
+ * For typed autocomplete, use `$()` instead, which is typed with `keyof F`.
202
+ */
203
+ override select(path: string | number, fields: any = null, isStrict: boolean = true): Field<any> {
204
+ return super.select(path, fields, isStrict) as Field<any>;
205
+ }
206
+
207
+ override values(): { [K in keyof F]?: F[K] } {
208
+ return super.values() as { [K in keyof F]?: F[K] };
209
+ }
210
+
211
+ /** DEPRECATED
212
+ Init Form Fields and Nested Fields
213
+
214
+ init($fields: any = null): void {
215
+ _.set(this, "fields", observable.map({}));
216
+
217
+ this.state.initial.props.values = $fields; // eslint-disable-line
218
+ this.state.current.props.values = $fields; // eslint-disable-line
219
+
220
+ this.initFields({
221
+ fields: $fields || this.state.struct(),
222
+ });
223
+ }
224
+ */
225
+
226
+ invalidate(message: string | null = null, deep: boolean = true): void {
227
+ this.debouncedValidation.cancel();
228
+ this.validator.error =
229
+ message ||
230
+ this.state.options.get(OptionsEnum.defaultGenericError) ||
231
+ null;
232
+ deep &&
233
+ this.each((field: FieldInterface) => field.debouncedValidation.cancel());
234
+ }
235
+
236
+ showErrors(show: boolean = true): void {
237
+ this.each((field: FieldInterface) => field.showErrors(show));
238
+ }
239
+
240
+ resetValidation(deep: boolean = true): void {
241
+ this.validator.error = null;
242
+ deep && this.each((field: FieldInterface) => field.resetValidation(deep));
243
+ }
244
+
245
+ /**
246
+ Clear Form Fields
247
+ */
248
+ clear(deep: boolean = true, execHook: boolean = true): void {
249
+ execHook && this.execHook(FieldPropsEnum.onClear);
250
+ this.$touched = false;
251
+ this.$changed = 0;
252
+ deep && this.each((field: FieldInterface) => field.clear(deep));
253
+ }
254
+
255
+ /**
256
+ Reset Form Fields
257
+ */
258
+ reset(deep: boolean = true, execHook: boolean = true): void {
259
+ execHook && this.execHook(FieldPropsEnum.onReset);
260
+ this.$touched = false;
261
+ this.$changed = 0;
262
+ deep && this.each((field: FieldInterface) => field.reset(deep));
263
+ }
264
+ }
package/src/Options.ts ADDED
@@ -0,0 +1,91 @@
1
+ import {
2
+ observable,
3
+ action,
4
+ toJS,
5
+ set,
6
+ } from "mobx";
7
+ import { makeObservable } from "./compat";
8
+
9
+ import { get, has } from "lodash";
10
+
11
+ import { uniqueId } from "./utils";
12
+
13
+ import {OptionsModel} from "./models/OptionsModel";
14
+ import {OptionsInterface} from "./models/OptionsInterface";
15
+
16
+ export default class Options implements OptionsInterface {
17
+ options: OptionsModel = {
18
+ uniqueId,
19
+ fallback: true,
20
+ fallbackValue: "",
21
+ defaultGenericError: null,
22
+ submitThrowsError: true,
23
+ showErrorsOnInit: false,
24
+ showErrorsOnSubmit: true,
25
+ showErrorsOnBlur: true,
26
+ showErrorsOnChange: true,
27
+ showErrorsOnClear: false,
28
+ showErrorsOnReset: true,
29
+ validateOnInit: true,
30
+ validateOnSubmit: true,
31
+ validateOnBlur: true,
32
+ validateOnChange: false,
33
+ validateOnChangeAfterInitialBlur: false,
34
+ validateOnChangeAfterSubmit: false,
35
+ validateOnClear: false,
36
+ validateOnReset: false,
37
+ validateDisabledFields: false,
38
+ validateDeletedFields: false,
39
+ validatePristineFields: true,
40
+ strictSet: false,
41
+ strictUpdate: false,
42
+ strictDelete: true,
43
+ strictSelect: true,
44
+ softDelete: false,
45
+ retrieveOnlyDirtyFieldsValues: false,
46
+ retrieveOnlyEnabledFieldsValues: false,
47
+ retrieveOnlyEnabledFieldsErrors: false,
48
+ retrieveNullifiedEmptyStrings: false,
49
+ autoTrimValue: false,
50
+ autoParseNumbers: false,
51
+ removeNullishValuesInArrays: false,
52
+ preserveDeletedFieldsValues: false,
53
+ validationDebounceWait: 250,
54
+ validationDebounceOptions: {
55
+ leading: false,
56
+ trailing: true,
57
+ },
58
+ stopValidationOnError: false,
59
+ validationPluginsOrder: undefined,
60
+ resetValidationBeforeValidate: true,
61
+ validateTrimmedValue: false,
62
+ applyInputConverterOnInit: true,
63
+ applyInputConverterOnSet: true,
64
+ applyInputConverterOnUpdate: true,
65
+ bubbleUpErrorMessages: false,
66
+ };
67
+
68
+ constructor() {
69
+ makeObservable(this, {
70
+ options: observable,
71
+ set: action,
72
+ });
73
+ }
74
+
75
+ get(key: string = '', field: any = null): OptionsModel {
76
+ // handle field option
77
+ if (has(field, "path")) {
78
+ if (has(field.$options, key)) {
79
+ return field.$options[key];
80
+ }
81
+ }
82
+
83
+ // fallback on global form options
84
+ if (key) return get(this.options, key);
85
+ return toJS(this.options);
86
+ }
87
+
88
+ set(options: OptionsModel): void {
89
+ set(this.options, options);
90
+ }
91
+ }
package/src/State.ts ADDED
@@ -0,0 +1,183 @@
1
+ import { observe } from "mobx";
2
+ import { get } from "lodash";
3
+
4
+ import Options from "./Options";
5
+ import Bindings from "./Bindings";
6
+
7
+ import {
8
+ props,
9
+ hasUnifiedProps,
10
+ hasSeparatedProps,
11
+ checkObserve,
12
+ isArrayOfStrings,
13
+ $try,
14
+ } from "./utils";
15
+
16
+ import { RuntimeMode } from "./models/StateInterface";
17
+ import { StateInterface } from "./models/StateInterface";
18
+ import { OptionsInterface } from "./models/OptionsInterface";
19
+ import { BindingsInterface } from "./models/BindingsInterface";
20
+ import { FormInterface } from "./models/FormInterface";
21
+ import { OptionsEnum } from "./models/OptionsModel";
22
+ import { FieldInterface } from "./models/FieldInterface";
23
+
24
+ export default class State implements StateInterface {
25
+ mode = RuntimeMode.mixed;
26
+
27
+ strict = false;
28
+
29
+ form!: FormInterface;
30
+
31
+ options: OptionsInterface;
32
+
33
+ bindings: BindingsInterface;
34
+
35
+ $extra: any;
36
+ $struct: string[] = [];
37
+
38
+ disposers = {
39
+ interceptor: {},
40
+ observer: {},
41
+ };
42
+
43
+ initial = {
44
+ props: {},
45
+ fields: {},
46
+ };
47
+
48
+ current = {
49
+ props: {},
50
+ fields: {},
51
+ };
52
+
53
+ constructor({ form, initial, options, bindings }: any) {
54
+ this.set("form", form);
55
+ this.initProps(initial);
56
+ this.options = new Options();
57
+ this.options.set(options);
58
+ this.bindings = new Bindings();
59
+ this.bindings.register(bindings);
60
+ this.observeOptions();
61
+ }
62
+
63
+ initProps(initial: any = {}) {
64
+ const pickKeys = [
65
+ ...props.editable,
66
+ ...props.separated,
67
+ ...props.validation,
68
+ ...props.functions,
69
+ ...props.handlers,
70
+ ];
71
+ const initialProps: any = Object.fromEntries(
72
+ Object.entries(initial).filter(([k]) => pickKeys.includes(k))
73
+ );
74
+
75
+ this.set("initial", "props", initialProps);
76
+
77
+ const $unified: boolean = hasUnifiedProps(initial);
78
+ const $separated: boolean = hasSeparatedProps(initial);
79
+
80
+ if (($separated || isArrayOfStrings(initial.fields)) && !$unified) {
81
+ const struct: any = $try(initial.struct, initial.fields);
82
+ this.struct(struct);
83
+ this.strict = true;
84
+ this.mode = RuntimeMode.separated;
85
+ return;
86
+ }
87
+
88
+ this.struct(initial.struct);
89
+ this.mode = RuntimeMode.unified;
90
+ }
91
+
92
+ /**
93
+ Get/Set Fields Structure
94
+ */
95
+ struct(data: any = null): any {
96
+ if (data) this.$struct = data;
97
+ return this.$struct;
98
+ }
99
+
100
+ /**
101
+ Get Props/Fields
102
+ */
103
+ get(type: string, subtype: string) {
104
+ return (this as any)[type][subtype];
105
+ }
106
+
107
+ /**
108
+ Set Props/Fields
109
+ */
110
+ set(type: string, subtype: any, state: any = null) {
111
+ if (type === "form") {
112
+ // subtype is the form here
113
+ this.form = subtype;
114
+ }
115
+
116
+ if (type === "initial") {
117
+ Object.assign((this.initial as any)[subtype], state);
118
+ Object.assign((this.current as any)[subtype], state);
119
+ }
120
+
121
+ if (type === "current") {
122
+ Object.assign((this.current as any)[subtype], state);
123
+ }
124
+ }
125
+
126
+ extra(data: any | null = null): any | null {
127
+ if (typeof data === 'string') return get(this.$extra, data);
128
+ if (data === null) return this.$extra;
129
+ this.$extra = data;
130
+ return null;
131
+ }
132
+
133
+ observeOptions(): void {
134
+ // Fix Issue #201
135
+ observe(
136
+ this.options.options,
137
+ checkObserve([
138
+ {
139
+ // start observing fields validateOnChange
140
+ type: "update",
141
+ key: OptionsEnum.validateOnChange,
142
+ to: true,
143
+ exec: () =>
144
+ this.form.each((field: FieldInterface) =>
145
+ field.observeValidationOnChange()
146
+ ),
147
+ },
148
+ {
149
+ // stop observing fields validateOnChange
150
+ type: "update",
151
+ key: OptionsEnum.validateOnChange,
152
+ to: false,
153
+ exec: () =>
154
+ this.form.each((field: FieldInterface) => {
155
+ if (typeof field.disposeValidationOnChange !== "function") return;
156
+ field.disposeValidationOnChange();
157
+ }),
158
+ },
159
+ {
160
+ // start observing fields validateOnBlur
161
+ type: "update",
162
+ key: OptionsEnum.validateOnBlur,
163
+ to: true,
164
+ exec: () =>
165
+ this.form.each((field: FieldInterface) =>
166
+ field.observeValidationOnBlur()
167
+ ),
168
+ },
169
+ {
170
+ // stop observing fields validateOnBlur
171
+ type: "update",
172
+ key: OptionsEnum.validateOnBlur,
173
+ to: false,
174
+ exec: () =>
175
+ this.form.each((field: FieldInterface) => {
176
+ if (typeof field.disposeValidationOnBlur !== "function") return;
177
+ field.disposeValidationOnBlur();
178
+ }),
179
+ },
180
+ ])
181
+ );
182
+ }
183
+ }
@@ -0,0 +1,177 @@
1
+ import { action, observable } from "mobx";
2
+ import { makeObservable } from "./compat";
3
+ import { each } from "lodash";
4
+ import { $try } from "./utils";
5
+ import ValidatorInterface, {
6
+ DriversMap,
7
+ ValidateOptions,
8
+ ValidationPluginInterface,
9
+ ValidationPlugins,
10
+ ValidatorConstructor,
11
+ } from "./models/ValidatorInterface";
12
+ import { FormInterface } from "./models/FormInterface";
13
+ import { FieldInterface } from "./models/FieldInterface";
14
+ import { OptionsEnum } from "./models/OptionsModel";
15
+
16
+ export default class Validator implements ValidatorInterface {
17
+ promises: Promise<any>[] = [];
18
+
19
+ form: FormInterface | null = null;
20
+
21
+ drivers: DriversMap = {};
22
+
23
+ plugins: ValidationPlugins = {
24
+ vjf: undefined,
25
+ dvr: undefined,
26
+ ajv: undefined,
27
+ yup: undefined,
28
+ zod: undefined,
29
+ joi: undefined,
30
+ valibot: undefined,
31
+ vinejs: undefined,
32
+ };
33
+
34
+ error: string | null = null;
35
+
36
+ constructor(obj: ValidatorConstructor) {
37
+ makeObservable(this, {
38
+ error: observable,
39
+ validate: action,
40
+ validateField: action,
41
+ });
42
+
43
+ this.form = obj.form;
44
+ Object.assign(this.plugins, obj.plugins);
45
+
46
+ this.initDrivers();
47
+ }
48
+
49
+ initDrivers(): void {
50
+ Object.entries(this.plugins).forEach(([key, plugin]) => {
51
+ this.drivers[key] = (plugin && (plugin as any).class) &&
52
+ new ((plugin as any).class)({
53
+ config: (plugin as any).config,
54
+ state: this.form!.state,
55
+ promises: this.promises,
56
+ });
57
+ });
58
+ }
59
+
60
+ validate(opt: ValidateOptions, obj: ValidateOptions): Promise<any> {
61
+ const path: string = $try((opt as any)?.path, opt);
62
+ const instance = $try((opt as any)?.field, this.form!.select(path, null, false), this.form!);
63
+ const related: boolean = $try((opt as any)?.related, (obj as any)?.related, true);
64
+ const showErrors: boolean = $try((opt as any)?.showErrors, (obj as any)?.showErrors, false);
65
+ instance.$validating = true;
66
+ instance.$validated += 1;
67
+
68
+ this.error = null;
69
+
70
+ return new Promise((resolve) => {
71
+ // validate instance (form or filed)
72
+ if (instance.path || typeof path === 'string') {
73
+ this.validateField({
74
+ field: instance,
75
+ showErrors,
76
+ related,
77
+ path,
78
+ });
79
+ }
80
+
81
+ // validate nested fields
82
+ instance.each((field: any) =>
83
+ this.validateField({
84
+ path: field.path,
85
+ field: field,
86
+ showErrors,
87
+ related,
88
+ })
89
+ );
90
+
91
+ // wait all promises
92
+ resolve(Promise.all(this.promises.splice(0)));
93
+ })
94
+ .then(
95
+ action(() => {
96
+ instance.$validating = false;
97
+ instance.$clearing = false;
98
+ instance.$resetting = false;
99
+ })
100
+ )
101
+ .catch(
102
+ action((err) => {
103
+ instance.$validating = false;
104
+ instance.$clearing = false;
105
+ instance.$resetting = false;
106
+ throw err;
107
+ })
108
+ )
109
+ .then(() => instance);
110
+ }
111
+
112
+ validateField({
113
+ showErrors = false,
114
+ related = false,
115
+ field = null,
116
+ path,
117
+ }: { showErrors?: boolean; related?: boolean; field?: any; path?: any }): void {
118
+ const instance = field || this.form!.select(path);
119
+ const { options } = this.form!.state;
120
+ // check if the field is a valid instance
121
+ if (!instance.path) throw new Error("Validation Error: Invalid Field Instance");
122
+ // do not validate soft deleted fields
123
+ if (instance.deleted && !options.get(OptionsEnum.validateDeletedFields, instance)) return;
124
+ // do not validate disabled fields
125
+ if (instance.disabled && !options.get(OptionsEnum.validateDisabledFields, instance)) return;
126
+ // do not validate pristine fields
127
+ if (instance.isPristine && !options.get(OptionsEnum.validatePristineFields, instance)) return;
128
+ // reset validation before validate
129
+ if (options.get(OptionsEnum.resetValidationBeforeValidate, instance)) instance.resetValidation();
130
+ // trim string value before validation
131
+ if (options.get(OptionsEnum.validateTrimmedValue, instance)) instance.trim();
132
+
133
+ // get stop on error
134
+ const stopOnError: boolean = options.get(
135
+ OptionsEnum.stopValidationOnError,
136
+ instance
137
+ );
138
+
139
+ // get validation plugin order
140
+ const validationPluginOrder = options.get(
141
+ OptionsEnum.validationPluginsOrder,
142
+ instance
143
+ );
144
+
145
+ const drivers: DriversMap = validationPluginOrder
146
+ ? validationPluginOrder.map((n: string) => this.drivers[n])
147
+ : this.drivers;
148
+
149
+ // validate with all enabled drivers
150
+ each(drivers, (driver: ValidationPluginInterface) => {
151
+ driver && driver.validate(instance);
152
+ if (stopOnError && instance.hasError) return;
153
+ });
154
+
155
+ // send error to the view
156
+ instance.showErrors(showErrors, false);
157
+
158
+ // related validation
159
+ if (related) this.validateRelatedFields(instance, showErrors);
160
+ }
161
+
162
+ /**
163
+ Validate 'related' fields if specified
164
+ and related validation allowed (recursive)
165
+ */
166
+ validateRelatedFields(field: FieldInterface, showErrors: boolean): void {
167
+ if (!field.related || !field.related.length) return;
168
+
169
+ field.related.map((path) =>
170
+ this.validateField({
171
+ related: false,
172
+ showErrors,
173
+ path,
174
+ })
175
+ );
176
+ }
177
+ }