@bgroup/wise-form 1.0.1

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 (319) hide show
  1. package/README.md +34 -0
  2. package/dist/components/ui/Checkbox.d.ts +14 -0
  3. package/dist/components/ui/Checkbox.d.ts.map +1 -0
  4. package/dist/components/ui/Checkbox.js +43 -0
  5. package/dist/components/ui/Checkbox.js.map +1 -0
  6. package/dist/components/ui/CheckboxGroup.d.ts +15 -0
  7. package/dist/components/ui/CheckboxGroup.d.ts.map +1 -0
  8. package/dist/components/ui/CheckboxGroup.js +33 -0
  9. package/dist/components/ui/CheckboxGroup.js.map +1 -0
  10. package/dist/components/ui/Input.d.ts +14 -0
  11. package/dist/components/ui/Input.d.ts.map +1 -0
  12. package/dist/components/ui/Input.js +49 -0
  13. package/dist/components/ui/Input.js.map +1 -0
  14. package/dist/components/ui/Radio.d.ts +14 -0
  15. package/dist/components/ui/Radio.d.ts.map +1 -0
  16. package/dist/components/ui/Radio.js +43 -0
  17. package/dist/components/ui/Radio.js.map +1 -0
  18. package/dist/components/ui/Select.d.ts +18 -0
  19. package/dist/components/ui/Select.d.ts.map +1 -0
  20. package/dist/components/ui/Select.js +44 -0
  21. package/dist/components/ui/Select.js.map +1 -0
  22. package/dist/components/ui/Textarea.d.ts +13 -0
  23. package/dist/components/ui/Textarea.d.ts.map +1 -0
  24. package/dist/components/ui/Textarea.js +42 -0
  25. package/dist/components/ui/Textarea.js.map +1 -0
  26. package/dist/components/ui/index.d.ts +13 -0
  27. package/dist/components/ui/index.d.ts.map +1 -0
  28. package/dist/components/ui/index.js +7 -0
  29. package/dist/components/ui/index.js.map +1 -0
  30. package/dist/form/index.d.ts +10 -0
  31. package/dist/form/index.d.ts.map +1 -0
  32. package/dist/form/index.js +5 -0
  33. package/dist/form/index.js.map +1 -0
  34. package/dist/form/interfaces/field-container.d.ts +8 -0
  35. package/dist/form/interfaces/field-container.d.ts.map +1 -0
  36. package/dist/form/interfaces/field-container.js +2 -0
  37. package/dist/form/interfaces/field-container.js.map +1 -0
  38. package/dist/form/interfaces/interfaces.d.ts +8 -0
  39. package/dist/form/interfaces/interfaces.d.ts.map +1 -0
  40. package/dist/form/interfaces/interfaces.js +2 -0
  41. package/dist/form/interfaces/interfaces.js.map +1 -0
  42. package/dist/form/interfaces/settings.d.ts +10 -0
  43. package/dist/form/interfaces/settings.d.ts.map +1 -0
  44. package/dist/form/interfaces/settings.js +2 -0
  45. package/dist/form/interfaces/settings.js.map +1 -0
  46. package/dist/form/interfaces/template.d.ts +6 -0
  47. package/dist/form/interfaces/template.d.ts.map +1 -0
  48. package/dist/form/interfaces/template.js +2 -0
  49. package/dist/form/interfaces/template.js.map +1 -0
  50. package/dist/form/interfaces/wise-form-specs.d.ts +9 -0
  51. package/dist/form/interfaces/wise-form-specs.d.ts.map +1 -0
  52. package/dist/form/interfaces/wise-form-specs.js +2 -0
  53. package/dist/form/interfaces/wise-form-specs.js.map +1 -0
  54. package/dist/form/view/components/containers/index.d.ts +3 -0
  55. package/dist/form/view/components/containers/index.d.ts.map +1 -0
  56. package/dist/form/view/components/containers/index.js +12 -0
  57. package/dist/form/view/components/containers/index.js.map +1 -0
  58. package/dist/form/view/components/error.d.ts +5 -0
  59. package/dist/form/view/components/error.d.ts.map +1 -0
  60. package/dist/form/view/components/error.js +8 -0
  61. package/dist/form/view/components/error.js.map +1 -0
  62. package/dist/form/view/components/field/container.d.ts +5 -0
  63. package/dist/form/view/components/field/container.d.ts.map +1 -0
  64. package/dist/form/view/components/field/container.js +5 -0
  65. package/dist/form/view/components/field/container.js.map +1 -0
  66. package/dist/form/view/components/field/index.d.ts +18 -0
  67. package/dist/form/view/components/field/index.d.ts.map +1 -0
  68. package/dist/form/view/components/field/index.js +89 -0
  69. package/dist/form/view/components/field/index.js.map +1 -0
  70. package/dist/form/view/components/field/selection.d.ts +2 -0
  71. package/dist/form/view/components/field/selection.d.ts.map +1 -0
  72. package/dist/form/view/components/field/selection.js +35 -0
  73. package/dist/form/view/components/field/selection.js.map +1 -0
  74. package/dist/form/view/components/field/use-field.d.ts +4 -0
  75. package/dist/form/view/components/field/use-field.d.ts.map +1 -0
  76. package/dist/form/view/components/field/use-field.js +41 -0
  77. package/dist/form/view/components/field/use-field.js.map +1 -0
  78. package/dist/form/view/components/rows/row-container.d.ts +18 -0
  79. package/dist/form/view/components/rows/row-container.d.ts.map +1 -0
  80. package/dist/form/view/components/rows/row-container.js +89 -0
  81. package/dist/form/view/components/rows/row-container.js.map +1 -0
  82. package/dist/form/view/components/rows/wrapper.d.ts +12 -0
  83. package/dist/form/view/components/rows/wrapper.d.ts.map +1 -0
  84. package/dist/form/view/components/rows/wrapper.js +27 -0
  85. package/dist/form/view/components/rows/wrapper.js.map +1 -0
  86. package/dist/form/view/components/wrapped-form.d.ts +6 -0
  87. package/dist/form/view/components/wrapped-form.d.ts.map +1 -0
  88. package/dist/form/view/components/wrapped-form.js +26 -0
  89. package/dist/form/view/components/wrapped-form.js.map +1 -0
  90. package/dist/form/view/context.d.ts +23 -0
  91. package/dist/form/view/context.d.ts.map +1 -0
  92. package/dist/form/view/context.js +7 -0
  93. package/dist/form/view/context.js.map +1 -0
  94. package/dist/form/view/hooks/use-model.d.ts +10 -0
  95. package/dist/form/view/hooks/use-model.d.ts.map +1 -0
  96. package/dist/form/view/hooks/use-model.js +31 -0
  97. package/dist/form/view/hooks/use-model.js.map +1 -0
  98. package/dist/form/view/hooks/use-template.d.ts +14 -0
  99. package/dist/form/view/hooks/use-template.d.ts.map +1 -0
  100. package/dist/form/view/hooks/use-template.js +57 -0
  101. package/dist/form/view/hooks/use-template.js.map +1 -0
  102. package/dist/form/view/hooks/use-types.d.ts +2 -0
  103. package/dist/form/view/hooks/use-types.d.ts.map +1 -0
  104. package/dist/form/view/hooks/use-types.js +19 -0
  105. package/dist/form/view/hooks/use-types.js.map +1 -0
  106. package/dist/form/view/index.d.ts +3 -0
  107. package/dist/form/view/index.d.ts.map +1 -0
  108. package/dist/form/view/index.js +38 -0
  109. package/dist/form/view/index.js.map +1 -0
  110. package/dist/formulas/helpers/condition-types.d.ts +5 -0
  111. package/dist/formulas/helpers/condition-types.d.ts.map +1 -0
  112. package/dist/formulas/helpers/condition-types.js +5 -0
  113. package/dist/formulas/helpers/condition-types.js.map +1 -0
  114. package/dist/formulas/helpers/evaluations.d.ts +15 -0
  115. package/dist/formulas/helpers/evaluations.d.ts.map +1 -0
  116. package/dist/formulas/helpers/evaluations.js +44 -0
  117. package/dist/formulas/helpers/evaluations.js.map +1 -0
  118. package/dist/formulas/helpers/formula.d.ts +6 -0
  119. package/dist/formulas/helpers/formula.d.ts.map +1 -0
  120. package/dist/formulas/helpers/formula.js +26 -0
  121. package/dist/formulas/helpers/formula.js.map +1 -0
  122. package/dist/formulas/helpers/lexer.d.ts +10 -0
  123. package/dist/formulas/helpers/lexer.d.ts.map +1 -0
  124. package/dist/formulas/helpers/lexer.js +73 -0
  125. package/dist/formulas/helpers/lexer.js.map +1 -0
  126. package/dist/formulas/helpers/parser.d.ts +24 -0
  127. package/dist/formulas/helpers/parser.d.ts.map +1 -0
  128. package/dist/formulas/helpers/parser.js +48 -0
  129. package/dist/formulas/helpers/parser.js.map +1 -0
  130. package/dist/formulas/helpers/token.d.ts +14 -0
  131. package/dist/formulas/helpers/token.d.ts.map +1 -0
  132. package/dist/formulas/helpers/token.js +14 -0
  133. package/dist/formulas/helpers/token.js.map +1 -0
  134. package/dist/formulas/index.d.ts +59 -0
  135. package/dist/formulas/index.d.ts.map +1 -0
  136. package/dist/formulas/index.js +186 -0
  137. package/dist/formulas/index.js.map +1 -0
  138. package/dist/formulas/types/formulas.d.ts +68 -0
  139. package/dist/formulas/types/formulas.d.ts.map +1 -0
  140. package/dist/formulas/types/formulas.js +2 -0
  141. package/dist/formulas/types/formulas.js.map +1 -0
  142. package/dist/formulas/types/index.d.ts +5 -0
  143. package/dist/formulas/types/index.d.ts.map +1 -0
  144. package/dist/formulas/types/index.js +2 -0
  145. package/dist/formulas/types/index.js.map +1 -0
  146. package/dist/formulas/variants/array-formula.d.ts +24 -0
  147. package/dist/formulas/variants/array-formula.d.ts.map +1 -0
  148. package/dist/formulas/variants/array-formula.js +142 -0
  149. package/dist/formulas/variants/array-formula.js.map +1 -0
  150. package/dist/formulas/variants/base.d.ts +6 -0
  151. package/dist/formulas/variants/base.d.ts.map +1 -0
  152. package/dist/formulas/variants/base.js +3 -0
  153. package/dist/formulas/variants/base.js.map +1 -0
  154. package/dist/formulas/variants/basic.d.ts +18 -0
  155. package/dist/formulas/variants/basic.d.ts.map +1 -0
  156. package/dist/formulas/variants/basic.js +128 -0
  157. package/dist/formulas/variants/basic.js.map +1 -0
  158. package/dist/formulas/variants/comparison.d.ts +25 -0
  159. package/dist/formulas/variants/comparison.d.ts.map +1 -0
  160. package/dist/formulas/variants/comparison.js +153 -0
  161. package/dist/formulas/variants/comparison.js.map +1 -0
  162. package/dist/formulas/variants/conditional.d.ts +18 -0
  163. package/dist/formulas/variants/conditional.d.ts.map +1 -0
  164. package/dist/formulas/variants/conditional.js +183 -0
  165. package/dist/formulas/variants/conditional.js.map +1 -0
  166. package/dist/formulas/variants/iterative-array.d.ts +20 -0
  167. package/dist/formulas/variants/iterative-array.d.ts.map +1 -0
  168. package/dist/formulas/variants/iterative-array.js +155 -0
  169. package/dist/formulas/variants/iterative-array.js.map +1 -0
  170. package/dist/formulas/variants/per-value.d.ts +20 -0
  171. package/dist/formulas/variants/per-value.d.ts.map +1 -0
  172. package/dist/formulas/variants/per-value.js +154 -0
  173. package/dist/formulas/variants/per-value.js.map +1 -0
  174. package/dist/index.d.ts +5 -0
  175. package/dist/index.d.ts.map +1 -0
  176. package/dist/index.js +6 -0
  177. package/dist/index.js.map +1 -0
  178. package/dist/models/base.d.ts +55 -0
  179. package/dist/models/base.d.ts.map +1 -0
  180. package/dist/models/base.js +146 -0
  181. package/dist/models/base.js.map +1 -0
  182. package/dist/models/callback-manager.d.ts +7 -0
  183. package/dist/models/callback-manager.d.ts.map +1 -0
  184. package/dist/models/callback-manager.js +89 -0
  185. package/dist/models/callback-manager.js.map +1 -0
  186. package/dist/models/field.d.ts +121 -0
  187. package/dist/models/field.d.ts.map +1 -0
  188. package/dist/models/field.js +374 -0
  189. package/dist/models/field.js.map +1 -0
  190. package/dist/models/index.d.ts +13 -0
  191. package/dist/models/index.d.ts.map +1 -0
  192. package/dist/models/index.js +7 -0
  193. package/dist/models/index.js.map +1 -0
  194. package/dist/models/model.d.ts +37 -0
  195. package/dist/models/model.d.ts.map +1 -0
  196. package/dist/models/model.js +245 -0
  197. package/dist/models/model.js.map +1 -0
  198. package/dist/models/plugins/base.d.ts +9 -0
  199. package/dist/models/plugins/base.d.ts.map +1 -0
  200. package/dist/models/plugins/base.js +3 -0
  201. package/dist/models/plugins/base.js.map +1 -0
  202. package/dist/models/plugins/formula.d.ts +18 -0
  203. package/dist/models/plugins/formula.d.ts.map +1 -0
  204. package/dist/models/plugins/formula.js +82 -0
  205. package/dist/models/plugins/formula.js.map +1 -0
  206. package/dist/models/plugins/index.d.ts +11 -0
  207. package/dist/models/plugins/index.d.ts.map +1 -0
  208. package/dist/models/plugins/index.js +52 -0
  209. package/dist/models/plugins/index.js.map +1 -0
  210. package/dist/models/plugins/plugins.d.ts +7 -0
  211. package/dist/models/plugins/plugins.d.ts.map +1 -0
  212. package/dist/models/plugins/plugins.js +7 -0
  213. package/dist/models/plugins/plugins.js.map +1 -0
  214. package/dist/models/types/base-wise-model.d.ts +7 -0
  215. package/dist/models/types/base-wise-model.d.ts.map +1 -0
  216. package/dist/models/types/base-wise-model.js +2 -0
  217. package/dist/models/types/base-wise-model.js.map +1 -0
  218. package/dist/models/types/callbacks.d.ts +19 -0
  219. package/dist/models/types/callbacks.d.ts.map +1 -0
  220. package/dist/models/types/callbacks.js +2 -0
  221. package/dist/models/types/callbacks.js.map +1 -0
  222. package/dist/models/types/disabled.d.ts +8 -0
  223. package/dist/models/types/disabled.d.ts.map +1 -0
  224. package/dist/models/types/disabled.js +2 -0
  225. package/dist/models/types/disabled.js.map +1 -0
  226. package/dist/models/types/form-field.d.ts +25 -0
  227. package/dist/models/types/form-field.d.ts.map +1 -0
  228. package/dist/models/types/form-field.js +2 -0
  229. package/dist/models/types/form-field.js.map +1 -0
  230. package/dist/models/types/model.d.ts +13 -0
  231. package/dist/models/types/model.d.ts.map +1 -0
  232. package/dist/models/types/model.js +2 -0
  233. package/dist/models/types/model.js.map +1 -0
  234. package/dist/models/types/plugins.d.ts +13 -0
  235. package/dist/models/types/plugins.d.ts.map +1 -0
  236. package/dist/models/types/plugins.js +2 -0
  237. package/dist/models/types/plugins.js.map +1 -0
  238. package/dist/models/types/wrapped-form-model-props.d.ts +11 -0
  239. package/dist/models/types/wrapped-form-model-props.d.ts.map +1 -0
  240. package/dist/models/types/wrapped-form-model-props.js +2 -0
  241. package/dist/models/types/wrapped-form-model-props.js.map +1 -0
  242. package/dist/models/wrapper.d.ts +30 -0
  243. package/dist/models/wrapper.d.ts.map +1 -0
  244. package/dist/models/wrapper.js +213 -0
  245. package/dist/models/wrapper.js.map +1 -0
  246. package/dist/settings/index.d.ts +7 -0
  247. package/dist/settings/index.d.ts.map +1 -0
  248. package/dist/settings/index.js +26 -0
  249. package/dist/settings/index.js.map +1 -0
  250. package/dist/utils/pending-promise.d.ts +6 -0
  251. package/dist/utils/pending-promise.d.ts.map +1 -0
  252. package/dist/utils/pending-promise.js +24 -0
  253. package/dist/utils/pending-promise.js.map +1 -0
  254. package/package.json +38 -0
  255. package/src/components/ui/Checkbox.tsx +68 -0
  256. package/src/components/ui/CheckboxGroup.tsx +60 -0
  257. package/src/components/ui/Input.tsx +72 -0
  258. package/src/components/ui/Radio.tsx +68 -0
  259. package/src/components/ui/Select.tsx +73 -0
  260. package/src/components/ui/Textarea.tsx +63 -0
  261. package/src/components/ui/index.ts +14 -0
  262. package/src/form/index.ts +11 -0
  263. package/src/form/interfaces/field-container.ts +9 -0
  264. package/src/form/interfaces/interfaces.ts +12 -0
  265. package/src/form/interfaces/settings.ts +11 -0
  266. package/src/form/interfaces/template.ts +7 -0
  267. package/src/form/interfaces/wise-form-specs.ts +10 -0
  268. package/src/form/styles.css +40 -0
  269. package/src/form/view/components/containers/index.tsx +18 -0
  270. package/src/form/view/components/error.tsx +9 -0
  271. package/src/form/view/components/field/container.tsx +6 -0
  272. package/src/form/view/components/field/index.tsx +107 -0
  273. package/src/form/view/components/field/selection.tsx +39 -0
  274. package/src/form/view/components/field/use-field.tsx +49 -0
  275. package/src/form/view/components/rows/row-container.tsx +96 -0
  276. package/src/form/view/components/rows/wrapper.tsx +28 -0
  277. package/src/form/view/components/wrapped-form.tsx +32 -0
  278. package/src/form/view/context.tsx +26 -0
  279. package/src/form/view/hooks/use-model.ts +37 -0
  280. package/src/form/view/hooks/use-template.tsx +64 -0
  281. package/src/form/view/hooks/use-types.ts +21 -0
  282. package/src/form/view/index.tsx +48 -0
  283. package/src/formulas/helpers/condition-types.ts +5 -0
  284. package/src/formulas/helpers/evaluations.ts +48 -0
  285. package/src/formulas/helpers/formula.ts +27 -0
  286. package/src/formulas/helpers/lexer.ts +82 -0
  287. package/src/formulas/helpers/parser.ts +55 -0
  288. package/src/formulas/helpers/token.ts +22 -0
  289. package/src/formulas/index.ts +198 -0
  290. package/src/formulas/types/formulas.ts +72 -0
  291. package/src/formulas/types/index.ts +5 -0
  292. package/src/formulas/variants/array-formula.ts +138 -0
  293. package/src/formulas/variants/base.ts +6 -0
  294. package/src/formulas/variants/basic.ts +141 -0
  295. package/src/formulas/variants/comparison.ts +156 -0
  296. package/src/formulas/variants/conditional.ts +208 -0
  297. package/src/formulas/variants/iterative-array.ts +152 -0
  298. package/src/formulas/variants/per-value.ts +152 -0
  299. package/src/index.ts +6 -0
  300. package/src/models/base.ts +164 -0
  301. package/src/models/callback-manager.ts +102 -0
  302. package/src/models/field.ts +426 -0
  303. package/src/models/index.ts +14 -0
  304. package/src/models/model.ts +260 -0
  305. package/src/models/plugins/base.ts +11 -0
  306. package/src/models/plugins/formula.ts +80 -0
  307. package/src/models/plugins/index.ts +48 -0
  308. package/src/models/plugins/plugins.ts +8 -0
  309. package/src/models/types/base-wise-model.ts +7 -0
  310. package/src/models/types/callbacks.ts +23 -0
  311. package/src/models/types/disabled.ts +8 -0
  312. package/src/models/types/form-field.ts +22 -0
  313. package/src/models/types/model.ts +14 -0
  314. package/src/models/types/plugins.ts +15 -0
  315. package/src/models/types/wrapped-form-model-props.ts +9 -0
  316. package/src/models/wrapper.ts +242 -0
  317. package/src/settings/index.ts +11 -0
  318. package/src/utils/pending-promise.ts +29 -0
  319. package/tsconfig.json +39 -0
@@ -0,0 +1,164 @@
1
+ import { ReactiveModel } from '@beyond-js/reactive/model';
2
+ import type { WrappedFormModel } from './wrapper';
3
+ import type { FormField } from './field';
4
+ import { PendingPromise } from '../utils/pending-promise';
5
+ import { FieldOrAlias } from './types/callbacks';
6
+ import { IFormModelProps } from './types/model';
7
+ import { IBaseWiseModel } from './types/base-wise-model';
8
+
9
+ export class BaseWiseModel extends ReactiveModel<IBaseWiseModel> {
10
+ #settings;
11
+ get settings() {
12
+ return this.#settings;
13
+ }
14
+
15
+ set settings(value) {
16
+ this.#settings = value;
17
+ }
18
+
19
+ #callbacks: Record<string, (...args) => void> = {};
20
+ get callbacks() {
21
+ return this.#callbacks;
22
+ }
23
+
24
+ set callbacks(value) {
25
+ this.#callbacks = value;
26
+ }
27
+
28
+ #initialValues: Record<string, string> = {};
29
+ get originalValues() {
30
+ return this.#initialValues;
31
+ }
32
+
33
+ get name() {
34
+ return this.#settings.name;
35
+ }
36
+ get template() {
37
+ return this.#settings.template;
38
+ }
39
+
40
+ #wrappers: Map<string, WrappedFormModel> = new Map();
41
+ get wrappers() {
42
+ return this.#wrappers;
43
+ }
44
+
45
+ set wrappers(value) {
46
+ this.#wrappers = value;
47
+ }
48
+
49
+ #fields: Map<string, FormField | WrappedFormModel> = new Map();
50
+ get fields() {
51
+ return this.#fields;
52
+ }
53
+ get values() {
54
+ const data: Record<string, any> = {};
55
+ this.#fields.forEach((field, key) => {
56
+ // FormField has value, WrappedFormModel doesn't
57
+ if ('value' in field) {
58
+ data[key] = (field as FormField).value;
59
+ }
60
+ });
61
+ return data;
62
+ }
63
+
64
+ #specs;
65
+ get specs() {
66
+ return this.#specs;
67
+ }
68
+ set specs(value) {
69
+ this.#specs = value;
70
+ }
71
+
72
+ #params = {};
73
+
74
+ protected loadedPromise: PendingPromise<boolean> = new PendingPromise();
75
+ protected childWrappersReady: number = 0;
76
+
77
+ constructor(settings: IFormModelProps, reactiveProps?) {
78
+ super(settings);
79
+
80
+ this.#params = settings.params ?? {};
81
+ this.#settings = settings;
82
+ this.#callbacks = settings.callbacks ?? {};
83
+ }
84
+
85
+ /**
86
+ * Sets the value of a specified field within the wrapper. If the field exists, its value is updated.
87
+ * @param {string} name - The name of the field to update.
88
+ * @param {any} value - The new value for the field.
89
+ */
90
+ setField(name: string, value) {
91
+ if (!this.getField(name)) {
92
+ console.error('Field not found', name, this.settings.name, this.fields.keys());
93
+ return;
94
+ }
95
+
96
+ const field = this.getField(this.getFieldName(name));
97
+ field.setValue(value);
98
+ }
99
+
100
+ /**
101
+ * Retrieves a field or nested wrapper by name. Supports dot notation for accessing deeply nested fields.
102
+ * @param {string} name - The name of the field or nested wrapper to retrieve.
103
+ * @returns {FormField | WrappedFormModel | undefined} The requested instance, or undefined if not found.
104
+ */
105
+ getField(name: string) {
106
+ if (!name) return console.warn('You need to provide a name to get a field in form ', this.#settings.name);
107
+
108
+ if (!name.includes('.')) {
109
+ let field = this.#fields.get(name);
110
+
111
+ if (!field) {
112
+ this.#wrappers.forEach(item => {
113
+ const foundField = item.getField(name);
114
+ if (foundField) field = foundField;
115
+ });
116
+ }
117
+ return field;
118
+ }
119
+
120
+ const [wrapperName, ...others] = name.split('.');
121
+ const currentWrapper = this.#wrappers.get(wrapperName);
122
+
123
+ const otherWrapper = others.join('.');
124
+ return currentWrapper.getField(otherWrapper);
125
+ }
126
+
127
+ /**
128
+ * Extracts the field name from a FieldOrAlias type. The input can either be a string directly representing
129
+ * the field name or an object where the key is the field name and the value is an alias.
130
+ * This function returns the field name if it is a string, or the first key (field name) if it is an object,
131
+ * assuming the object contains exactly one key-value pair.
132
+ *
133
+ * @param {FieldOrAlias} field - The field identifier which could be a string or an object with one key-value pair.
134
+ * @returns {string} - The field name extracted from the input.
135
+ * @throws {Error} - Throws an error if the input is an object that does not contain exactly one key.
136
+ */
137
+ getFieldName(field: FieldOrAlias): string {
138
+ if (typeof field === 'object' && Object.keys(field).length !== 1) {
139
+ throw new Error('Field object must contain exactly one key.');
140
+ }
141
+
142
+ if (typeof field === 'string') {
143
+ return field;
144
+ }
145
+
146
+ return Object.keys(field)[0];
147
+ }
148
+
149
+ /**
150
+ * Clears all fields within the wrapper, resetting their values to their initial state.
151
+ */
152
+ clear = () => {
153
+ this.fields.forEach(field => field.clear());
154
+ this.triggerEvent();
155
+ this.triggerEvent('clear');
156
+ };
157
+
158
+ getParams(param) {
159
+ return this.#params[param];
160
+ }
161
+
162
+
163
+ }
164
+
@@ -0,0 +1,102 @@
1
+ import type { FormField } from './field';
2
+ import type { FormModel } from './model';
3
+ import { CallbackFunction, ICallbackProps } from './types/callbacks';
4
+
5
+ export class CallbackManager {
6
+ #field: FormField;
7
+ #model: FormModel;
8
+ #callbacks: CallbackFunction[] = [];
9
+ #listeners = [];
10
+ constructor(model, field) {
11
+ this.#field = field;
12
+ this.#model = model;
13
+ this.#callbacks = model.callbacks;
14
+ this.initialize();
15
+ }
16
+
17
+ initialize() {
18
+ const instance = this.#field;
19
+ const checkField = async (settings) => {
20
+ const dependency = this.#model.getField(
21
+ this.#model.getFieldName(settings.field)
22
+ );
23
+ await dependency.isReady;
24
+ const required = ['field', 'callback'];
25
+ required.forEach((prop) => {
26
+ if (!settings[prop])
27
+ throw new Error(`${settings?.field} is missing ${prop}`);
28
+ });
29
+
30
+ if (!dependency)
31
+ throw new Error(`${settings?.field} is not a registered field`);
32
+
33
+ if (!this.#callbacks[settings.callback]) {
34
+ throw new Error(
35
+ `${settings.callback} is not a registered callback ${settings.name}`
36
+ );
37
+ }
38
+
39
+ // saved in listener array to be able to remove the listener if is required.
40
+ const event = settings.event || 'value.change';
41
+ const caller = () => this.executeCallback(settings);
42
+ this.#listeners.push(caller);
43
+ dependency.on(event, caller);
44
+
45
+ //callback({ dependency, settings, field: instance, form: this });
46
+ };
47
+
48
+ instance?.specs?.dependentOn.forEach(checkField);
49
+ }
50
+
51
+ executeCallback = async (settings) => {
52
+ const params: ICallbackProps = {
53
+ ...settings,
54
+ form: this.#model,
55
+ field: this.#field,
56
+
57
+ settings,
58
+ };
59
+ if (!settings) {
60
+ console.warn('the field does not have dependentOn settings');
61
+ }
62
+ const callback: CallbackFunction = this.#callbacks[settings.callback];
63
+
64
+ const dependency = this.#model.getField(
65
+ this.#model.getFieldName(settings.field)
66
+ );
67
+ await dependency.isReady;
68
+ const fields = { [dependency.name]: dependency };
69
+ if (settings.hasOwnProperty('fields')) {
70
+ for (const field of settings.fields) {
71
+ const instance = this.#model.getField(
72
+ this.#model.getFieldName(field)
73
+ );
74
+ if (instance) await instance.isReady;
75
+ const propName =
76
+ typeof field === 'string' ? field : field.alias;
77
+ fields[propName] = instance;
78
+ }
79
+
80
+ params.fields = fields;
81
+ }
82
+ params.dependency = dependency;
83
+
84
+ //global wiseForm params
85
+ if (settings.hasOwnProperty('params')) {
86
+ const specs = {};
87
+ settings.params.forEach((param) => {
88
+ if (!this.#model?.getParams(param)) {
89
+ console.warn(
90
+ `param ${param} is not registered in the form`
91
+ );
92
+ return;
93
+ }
94
+
95
+ specs[param] = this.#model.getParams(param);
96
+ });
97
+ params.specs = specs;
98
+ }
99
+ callback(params);
100
+ };
101
+ }
102
+
@@ -0,0 +1,426 @@
1
+ import { ReactiveModel } from '@beyond-js/reactive/model';
2
+ import type { WrappedFormModel } from './wrapper';
3
+ import { FormModel } from './model';
4
+ import { IFormField, IFormFieldProps } from './types/form-field';
5
+ import { IDisabled } from './types/disabled';
6
+
7
+ /**
8
+ * Represents a single form field within a `FormModel` or `WrappedFormModel`, providing mechanisms for data binding, validation, and interaction.
9
+ * This class extends `ReactiveModel` to enable reactive updates and interactions within the form's lifecycle.
10
+ *
11
+ * @extends ReactiveModel<IFormField>
12
+ */
13
+ export class FormField extends ReactiveModel<IFormField> {
14
+ // The parent model, either FormModel or WrappedFormModel, containing this field.
15
+ #parent: WrappedFormModel | FormModel;
16
+ get parent() {
17
+ return this.#parent;
18
+ }
19
+
20
+ #NATIVE_ACTIONS = ['hide', 'disable', 'enable', 'show', 'reset'];
21
+ #EVENTS = ['onClick', 'onChange', 'onKeyup'];
22
+ setEvents(events: string[]) {
23
+ this.#EVENTS.concat(events);
24
+ }
25
+ #isReady: boolean = false;
26
+ // Can be a boolean or an object specifying dynamic disablingvas logic based on other fields' values.
27
+ #disabled: boolean | IDisabled = false;
28
+
29
+ /**
30
+ * Evaluates and returns the disabled state of the field. If `#disabled` is an object, it checks the specified fields' values to determine the disabled state dynamically.
31
+ * @returns {boolean} The disabled state of the field.
32
+ */
33
+ get disabled() {
34
+ if (typeof this.#disabled !== 'object' || !this.#disabled?.fields)
35
+ return this.#disabled;
36
+
37
+ const validate = (field) => {
38
+ if (typeof field !== 'object')
39
+ return !this.#parent.form.getField(field).value;
40
+ const { name, value } = field;
41
+ const fieldInstance = this.#parent.getField(name);
42
+ if (!fieldInstance) return false;
43
+ if (field.hasOwnProperty('condition')) {
44
+ const compare = this.evaluations[field.condition](
45
+ fieldInstance.value,
46
+ field.value
47
+ );
48
+ return compare;
49
+ }
50
+ const { value: fieldValue } = fieldInstance;
51
+ return value !== fieldValue;
52
+ };
53
+
54
+ return this.#disabled.fields.some(validate);
55
+ }
56
+
57
+ set disabled(value) {
58
+ if (value === this.#disabled) return;
59
+ this.#disabled = value;
60
+ this.triggerEvent();
61
+ }
62
+
63
+ // Field specifications including its type, validation rules, and other metadata.
64
+ #specs: Record<string, any>;
65
+ get specs() {
66
+ return this.#specs;
67
+ }
68
+
69
+ get attributes() {
70
+ const props = this.getProperties();
71
+ return {
72
+ ...props,
73
+ disabled: this.#disabled,
74
+ };
75
+ }
76
+
77
+ #value: string;
78
+ get value() {
79
+ return this.#value;
80
+ }
81
+
82
+ set value(value) {
83
+ this.setValue(value);
84
+ }
85
+
86
+ // Tracks other fields this field listens to for changes, enabling reactive behavior and allowing the cleanup of event listeners.
87
+ #listeningItems = new Map();
88
+
89
+ /**
90
+ * Constructs a FormField instance with specified properties and parent form model.
91
+ * @param {Object} params - Construction parameters including the parent form model and field specifications.
92
+ */
93
+ constructor({ parent, specs }: { parent; specs: IFormFieldProps }) {
94
+ let { properties, disabled, ...props } = specs;
95
+ super({
96
+ ...props,
97
+ properties: [
98
+ 'name',
99
+ 'type',
100
+ 'placeholder',
101
+ 'required',
102
+ 'label',
103
+ 'variant',
104
+ 'options',
105
+ 'className',
106
+ 'checked',
107
+ 'id',
108
+ 'icon',
109
+ 'hidden',
110
+
111
+ ...properties,
112
+ ],
113
+ });
114
+
115
+ (this as any).__instanceID = `${specs.name}.${this.generateRandomNumber()}`;
116
+
117
+ this.#specs = specs;
118
+ this.#parent = parent;
119
+ (this as any).__instance = Math.random();
120
+
121
+ const toSet: Record<string, any> = {};
122
+ /**
123
+ * @todo: review this code
124
+ */
125
+ Object.keys(props).forEach((key) => {
126
+ if (key === 'properties') return;
127
+
128
+ if (
129
+ typeof props[key] === 'string' &&
130
+ props[key]?.includes('state:')
131
+ ) {
132
+ const state = props[key].split('state:')[1];
133
+ if (state === 'create' && !this.#parent.form.update) {
134
+ props[key] = true;
135
+ }
136
+ }
137
+ toSet[key] = props[key];
138
+ });
139
+ // this.#disabled = disabled;
140
+ // this.set(toSet);
141
+
142
+ this.set(specs);
143
+ }
144
+
145
+ getProperties() {
146
+ const properties = super.getProperties();
147
+ return { ...properties, value: this.#value };
148
+ }
149
+
150
+ /**
151
+ * This method is used to set the value property of the field and fire the value.change event
152
+ *
153
+ * @param value
154
+ * @returns
155
+ */
156
+ setValue(value: string) {
157
+ if (value === this.value) return;
158
+ this.#value = value;
159
+ this.trigger('change');
160
+ this.trigger('value.change', this);
161
+ }
162
+
163
+ generateRandomNumber = () => {
164
+ return Math.floor(Math.random() * (1000000 - 10000 + 1)) + 10000;
165
+ };
166
+
167
+ /**
168
+ * Performs initial setup based on the field's specifications, setting up validation, default values, and any specified dynamic behavior.
169
+ */
170
+ initialize = () => {
171
+ this.checkSettings(this.#specs);
172
+ this.on('change', this.listenerEvents);
173
+ // this.on('value.change', this.listenerEvents);
174
+ };
175
+
176
+ /**
177
+ * Resets the field to its initial value and state, including resetting the disabled state if it's statically defined.
178
+ */
179
+ clear = () => {
180
+ // Get initial values from specs or use empty object
181
+ const initValues = this.#specs || {};
182
+ this.set(initValues);
183
+ if (initValues.hasOwnProperty('disabled'))
184
+ this.disabled = initValues.disabled;
185
+ this.triggerEvent('clear');
186
+ };
187
+
188
+ /**
189
+ * Listens to changes in sibling fields (specified in dynamic disabling logic) and updates its state accordingly.
190
+ */
191
+ #listenSiblings = () => {
192
+ this.triggerEvent('change');
193
+ this.triggerEvent();
194
+ this.triggerEvent('value.change');
195
+ };
196
+
197
+ /**
198
+ * Checks and applies the field's settings, particularly for dynamic disabling, establishing listeners on related fields as necessary.
199
+ * @param {Object} props - The field's properties and settings to check and apply.
200
+ */
201
+ checkSettings(props) {
202
+ if (props.hasOwnProperty('disabled')) {
203
+ if (typeof props.disabled === 'boolean') {
204
+ this.#disabled = props.disabled;
205
+ return;
206
+ }
207
+
208
+ if (typeof props.disabled !== 'object') {
209
+ throw new Error(
210
+ `The disabled property of the field ${props.name} must be a boolean or an object`
211
+ );
212
+ }
213
+ if (!props.disabled.fields && !props.disabled.mode) {
214
+ throw new Error(
215
+ `The disabled property of the field ${props.name} must have a fields property or a mode defined`
216
+ );
217
+ }
218
+
219
+ if (props.disabled.mode) {
220
+ // posible modes : create, update;
221
+ this.#disabled = this.#parent.form.mode === props.disabled.mode;
222
+ return;
223
+ }
224
+
225
+ let allValid;
226
+ props.disabled.fields.forEach((item) => {
227
+ const name = typeof item === 'string' ? item : item.name;
228
+
229
+ const instance = this.#parent.form.getField(name);
230
+ allValid = instance;
231
+ if (!allValid) return;
232
+ instance.on('change', this.#listenSiblings);
233
+ instance.on('value.change', this.#listenSiblings);
234
+ this.#listeningItems.set(name, {
235
+ item: instance,
236
+ listener: this.#listenSiblings,
237
+ });
238
+ });
239
+
240
+ if (!allValid) {
241
+ const fieldName = this.getProperties().name || 'unknown';
242
+ throw new Error(
243
+ `the field ${allValid} does not exist in the form ${
244
+ (this.#parent as any).name
245
+ }, field passed in invalid settings of field "${fieldName}"`
246
+ );
247
+ }
248
+ this.#disabled = props.disabled;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * En este metodo se recorre el objeto asociado al evento y ejecuta cada una de las acciones asociadas
254
+ * como las acciones nativas del FormModel (HIDE, SHOW, DISABLE, ENABLE), hace el seteo de propiedades
255
+ * en caso de recibir field y ejecuta las callbacks asociadas
256
+ * @param actions objeto con las acciones que se van a realizar al ejecutarse el evento asociado
257
+ * @returns
258
+ */
259
+ async #executeEvent(actions) {
260
+ if (typeof actions !== 'object' || Array.isArray(actions)) return;
261
+
262
+ const formModel = this.#parent.form;
263
+
264
+ const sortedKeys = Object.keys(actions).sort(
265
+ (a, b) => actions[a]?.__order - actions[b]?.__order
266
+ );
267
+
268
+ for (let action of sortedKeys) {
269
+ if (action === 'fields') {
270
+ for (let fieldName in actions[action]) {
271
+ const field = this.#parent.form.getField(fieldName);
272
+
273
+ if (!field) continue;
274
+ await field.isReady;
275
+ field.set(actions[action][fieldName]);
276
+ }
277
+ continue;
278
+ }
279
+ if (formModel.callbacks.hasOwnProperty(action)) {
280
+ formModel.callbacks[action]({
281
+ ...actions[action],
282
+ form: formModel,
283
+ });
284
+ continue;
285
+ }
286
+
287
+ if (
288
+ this.#NATIVE_ACTIONS.includes(action) &&
289
+ formModel.hasOwnProperty(action)
290
+ ) {
291
+ formModel[action](actions[action].target);
292
+ }
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Busca el evento configurado en el field
298
+ * @param item objeto que tiene el evento
299
+ * @returns
300
+ */
301
+ #getEvent(item) {
302
+ let event: string;
303
+ const keys = Object.keys(item);
304
+ keys.forEach((key) => {
305
+ if (event) return;
306
+ if (this.#EVENTS.includes(key)) event = key;
307
+ });
308
+ return event;
309
+ }
310
+
311
+ /**
312
+ * Metodo para identificar si es field con multiples eventos configurados o solo es un evento configurado
313
+ * hace la busqueda del evento lanzado al haber multiples
314
+ * @returns
315
+ */
316
+ listenerEvents = (event2) => {
317
+ if (!this.#isReady) {
318
+ this.#isReady = true;
319
+ return;
320
+ }
321
+
322
+ if (!this.specs?.events) {
323
+ const event = this.#getEvent(this.specs);
324
+ if (!event) return;
325
+ this.#executeEvent(this.specs[event]);
326
+ return;
327
+ }
328
+
329
+ const event = this.#getEvent(this.specs.events);
330
+ if (!event) return;
331
+ const item = this.specs.events[event].hasOwnProperty(this.value)
332
+ ? this.specs.events[event][this.value]
333
+ : null;
334
+ if (!item) return;
335
+ this.#executeEvent(item);
336
+ };
337
+
338
+ /**
339
+ * Cleans up any established listeners and internal state when the field is removed or the form is reset, ensuring no memory leaks or stale data.
340
+ */
341
+ cleanUp() {
342
+ this.#listeningItems.forEach(({ item, listener }) =>
343
+ item.off('change', listener)
344
+ );
345
+ // todo: remove all events
346
+ }
347
+
348
+ /**
349
+ * The `set` method sets one or more properties on the model.
350
+ *
351
+ *
352
+ * This method overwrites the original reactiveModel set to pass the object as param
353
+ * when the change event is fired.
354
+ * Eventually this method will be removed and the original set method will be used, but
355
+ * it requires an upgrade in the reactive model package.
356
+ * @param {keyof ReactiveModelPublic<T>} property - The name of the property to set.
357
+ * @param {*} value - The value to set the property to.
358
+ * @returns {void}
359
+ */
360
+ set(properties: Partial<IFormField>): any {
361
+ let updated = false;
362
+ try {
363
+ Object.keys(properties).forEach((prop) => {
364
+ const currentProperties = Object.keys(this.getProperties());
365
+ if (!currentProperties || !currentProperties.includes(prop))
366
+ return;
367
+
368
+ const sameObject =
369
+ typeof properties[prop] === 'object' &&
370
+ JSON.stringify(properties[prop]) ===
371
+ JSON.stringify(this[prop]);
372
+
373
+ if (this[prop] === properties[prop] || sameObject) return;
374
+ const descriptor = Object.getOwnPropertyDescriptor(this, prop);
375
+
376
+ if (descriptor?.set) return;
377
+ this[prop] = properties[prop];
378
+ updated = true;
379
+ });
380
+ } catch (e) {
381
+ console.error(`Error setting properties:`, e);
382
+ throw new Error(`Error setting properties: ${e}`);
383
+ } finally {
384
+ if (updated) this.trigger('change', this);
385
+ }
386
+ }
387
+
388
+ hide = () => {
389
+ const className = this.getProperties().className || '';
390
+ const isHidden = className.includes('hidden');
391
+ const cls = isHidden ? className : `${className} hidden`;
392
+ if (cls !== className) this.set({ className: cls });
393
+ };
394
+
395
+ show = () => {
396
+ const className = this.getProperties().className || '';
397
+ const isHidden = className.includes('hidden');
398
+ const cls = isHidden
399
+ ? className.replace(/\bhidden\b/g, '').trim()
400
+ : className;
401
+ if (cls !== className) this.set({ className: cls });
402
+ };
403
+
404
+ private evaluations: Record<
405
+ string,
406
+ (value: any, comparisonValue?: any) => boolean
407
+ > = {
408
+ equal: (value, comparisonValue) => value == comparisonValue,
409
+ lower: (value, comparisonValue) =>
410
+ Number(value) < Number(comparisonValue),
411
+ upper: (value, comparisonValue) =>
412
+ Number(value) > Number(comparisonValue),
413
+ between: (value, [min, max]) => {
414
+ const numValue = Number(value);
415
+ return numValue >= Number(min) && numValue <= Number(max);
416
+ },
417
+ different: (value, comparisonValue) => value != comparisonValue,
418
+ hasValue: (value) => ![undefined, null, '', false].includes(value),
419
+ empty: (value) => [undefined, null, ''].includes(value),
420
+ lessOrEqual: (value, comparisonValue) =>
421
+ Number(value) <= Number(comparisonValue),
422
+ greaterOrEqual: (value, comparisonValue) =>
423
+ Number(value) >= Number(comparisonValue),
424
+ };
425
+ }
426
+
@@ -0,0 +1,14 @@
1
+ export { FormModel } from './model';
2
+ export { FormField } from './field';
3
+ export { WrappedFormModel } from './wrapper';
4
+ export { BaseWiseModel } from './base';
5
+ export { CallbackManager } from './callback-manager';
6
+ export { PluginsManager } from './plugins';
7
+
8
+ export type { IFormField, IFormFieldProps } from './types/form-field';
9
+ export type { IFormModelProps } from './types/model';
10
+ export type { IWrapperFormModelProps } from './types/wrapped-form-model-props';
11
+ export type { IDisabled, TDisabledSettings } from './types/disabled';
12
+ export type { ICallbackProps, CallbackFunction, FieldOrAlias } from './types/callbacks';
13
+ export type { IPluginForm, IPluginFormSpecs } from './types/plugins';
14
+