@bgroup/wise-form 1.0.2 → 1.0.5

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 (269) hide show
  1. package/README.md +1 -1
  2. package/package.json +23 -4
  3. package/postcss.config.js +6 -0
  4. package/src/form/styles.css +11 -37
  5. package/src/form/view/components/containers/index.tsx +33 -4
  6. package/src/form/view/components/error.tsx +0 -3
  7. package/src/form/view/components/field/container.tsx +1 -1
  8. package/src/form/view/components/field/index.tsx +42 -7
  9. package/src/form/view/components/rows/row-container.tsx +37 -9
  10. package/src/form/view/components/rows/wrapper.tsx +17 -6
  11. package/src/form/view/components/wrapped-form.tsx +38 -5
  12. package/src/form/view/hooks/use-model.ts +91 -25
  13. package/src/form/view/index.tsx +15 -3
  14. package/src/models/field.ts +504 -458
  15. package/tailwind.config.js +11 -0
  16. package/tsconfig.json +2 -0
  17. package/vite.config.ts +59 -0
  18. package/dist/components/ui/Checkbox.d.ts +0 -14
  19. package/dist/components/ui/Checkbox.d.ts.map +0 -1
  20. package/dist/components/ui/Checkbox.js +0 -43
  21. package/dist/components/ui/Checkbox.js.map +0 -1
  22. package/dist/components/ui/CheckboxGroup.d.ts +0 -15
  23. package/dist/components/ui/CheckboxGroup.d.ts.map +0 -1
  24. package/dist/components/ui/CheckboxGroup.js +0 -33
  25. package/dist/components/ui/CheckboxGroup.js.map +0 -1
  26. package/dist/components/ui/Input.d.ts +0 -14
  27. package/dist/components/ui/Input.d.ts.map +0 -1
  28. package/dist/components/ui/Input.js +0 -49
  29. package/dist/components/ui/Input.js.map +0 -1
  30. package/dist/components/ui/Radio.d.ts +0 -14
  31. package/dist/components/ui/Radio.d.ts.map +0 -1
  32. package/dist/components/ui/Radio.js +0 -43
  33. package/dist/components/ui/Radio.js.map +0 -1
  34. package/dist/components/ui/Select.d.ts +0 -18
  35. package/dist/components/ui/Select.d.ts.map +0 -1
  36. package/dist/components/ui/Select.js +0 -44
  37. package/dist/components/ui/Select.js.map +0 -1
  38. package/dist/components/ui/Textarea.d.ts +0 -13
  39. package/dist/components/ui/Textarea.d.ts.map +0 -1
  40. package/dist/components/ui/Textarea.js +0 -42
  41. package/dist/components/ui/Textarea.js.map +0 -1
  42. package/dist/components/ui/index.d.ts +0 -13
  43. package/dist/components/ui/index.d.ts.map +0 -1
  44. package/dist/components/ui/index.js +0 -7
  45. package/dist/components/ui/index.js.map +0 -1
  46. package/dist/form/index.d.ts +0 -10
  47. package/dist/form/index.d.ts.map +0 -1
  48. package/dist/form/index.js +0 -5
  49. package/dist/form/index.js.map +0 -1
  50. package/dist/form/interfaces/field-container.d.ts +0 -8
  51. package/dist/form/interfaces/field-container.d.ts.map +0 -1
  52. package/dist/form/interfaces/field-container.js +0 -2
  53. package/dist/form/interfaces/field-container.js.map +0 -1
  54. package/dist/form/interfaces/interfaces.d.ts +0 -8
  55. package/dist/form/interfaces/interfaces.d.ts.map +0 -1
  56. package/dist/form/interfaces/interfaces.js +0 -2
  57. package/dist/form/interfaces/interfaces.js.map +0 -1
  58. package/dist/form/interfaces/settings.d.ts +0 -10
  59. package/dist/form/interfaces/settings.d.ts.map +0 -1
  60. package/dist/form/interfaces/settings.js +0 -2
  61. package/dist/form/interfaces/settings.js.map +0 -1
  62. package/dist/form/interfaces/template.d.ts +0 -6
  63. package/dist/form/interfaces/template.d.ts.map +0 -1
  64. package/dist/form/interfaces/template.js +0 -2
  65. package/dist/form/interfaces/template.js.map +0 -1
  66. package/dist/form/interfaces/wise-form-specs.d.ts +0 -9
  67. package/dist/form/interfaces/wise-form-specs.d.ts.map +0 -1
  68. package/dist/form/interfaces/wise-form-specs.js +0 -2
  69. package/dist/form/interfaces/wise-form-specs.js.map +0 -1
  70. package/dist/form/view/components/containers/index.d.ts +0 -3
  71. package/dist/form/view/components/containers/index.d.ts.map +0 -1
  72. package/dist/form/view/components/containers/index.js +0 -12
  73. package/dist/form/view/components/containers/index.js.map +0 -1
  74. package/dist/form/view/components/error.d.ts +0 -5
  75. package/dist/form/view/components/error.d.ts.map +0 -1
  76. package/dist/form/view/components/error.js +0 -8
  77. package/dist/form/view/components/error.js.map +0 -1
  78. package/dist/form/view/components/field/container.d.ts +0 -5
  79. package/dist/form/view/components/field/container.d.ts.map +0 -1
  80. package/dist/form/view/components/field/container.js +0 -5
  81. package/dist/form/view/components/field/container.js.map +0 -1
  82. package/dist/form/view/components/field/index.d.ts +0 -18
  83. package/dist/form/view/components/field/index.d.ts.map +0 -1
  84. package/dist/form/view/components/field/index.js +0 -89
  85. package/dist/form/view/components/field/index.js.map +0 -1
  86. package/dist/form/view/components/field/selection.d.ts +0 -2
  87. package/dist/form/view/components/field/selection.d.ts.map +0 -1
  88. package/dist/form/view/components/field/selection.js +0 -35
  89. package/dist/form/view/components/field/selection.js.map +0 -1
  90. package/dist/form/view/components/field/use-field.d.ts +0 -4
  91. package/dist/form/view/components/field/use-field.d.ts.map +0 -1
  92. package/dist/form/view/components/field/use-field.js +0 -41
  93. package/dist/form/view/components/field/use-field.js.map +0 -1
  94. package/dist/form/view/components/rows/row-container.d.ts +0 -18
  95. package/dist/form/view/components/rows/row-container.d.ts.map +0 -1
  96. package/dist/form/view/components/rows/row-container.js +0 -89
  97. package/dist/form/view/components/rows/row-container.js.map +0 -1
  98. package/dist/form/view/components/rows/wrapper.d.ts +0 -12
  99. package/dist/form/view/components/rows/wrapper.d.ts.map +0 -1
  100. package/dist/form/view/components/rows/wrapper.js +0 -27
  101. package/dist/form/view/components/rows/wrapper.js.map +0 -1
  102. package/dist/form/view/components/wrapped-form.d.ts +0 -6
  103. package/dist/form/view/components/wrapped-form.d.ts.map +0 -1
  104. package/dist/form/view/components/wrapped-form.js +0 -26
  105. package/dist/form/view/components/wrapped-form.js.map +0 -1
  106. package/dist/form/view/context.d.ts +0 -23
  107. package/dist/form/view/context.d.ts.map +0 -1
  108. package/dist/form/view/context.js +0 -7
  109. package/dist/form/view/context.js.map +0 -1
  110. package/dist/form/view/hooks/use-model.d.ts +0 -10
  111. package/dist/form/view/hooks/use-model.d.ts.map +0 -1
  112. package/dist/form/view/hooks/use-model.js +0 -31
  113. package/dist/form/view/hooks/use-model.js.map +0 -1
  114. package/dist/form/view/hooks/use-template.d.ts +0 -14
  115. package/dist/form/view/hooks/use-template.d.ts.map +0 -1
  116. package/dist/form/view/hooks/use-template.js +0 -57
  117. package/dist/form/view/hooks/use-template.js.map +0 -1
  118. package/dist/form/view/hooks/use-types.d.ts +0 -2
  119. package/dist/form/view/hooks/use-types.d.ts.map +0 -1
  120. package/dist/form/view/hooks/use-types.js +0 -19
  121. package/dist/form/view/hooks/use-types.js.map +0 -1
  122. package/dist/form/view/index.d.ts +0 -3
  123. package/dist/form/view/index.d.ts.map +0 -1
  124. package/dist/form/view/index.js +0 -38
  125. package/dist/form/view/index.js.map +0 -1
  126. package/dist/formulas/helpers/condition-types.d.ts +0 -5
  127. package/dist/formulas/helpers/condition-types.d.ts.map +0 -1
  128. package/dist/formulas/helpers/condition-types.js +0 -5
  129. package/dist/formulas/helpers/condition-types.js.map +0 -1
  130. package/dist/formulas/helpers/evaluations.d.ts +0 -15
  131. package/dist/formulas/helpers/evaluations.d.ts.map +0 -1
  132. package/dist/formulas/helpers/evaluations.js +0 -44
  133. package/dist/formulas/helpers/evaluations.js.map +0 -1
  134. package/dist/formulas/helpers/formula.d.ts +0 -6
  135. package/dist/formulas/helpers/formula.d.ts.map +0 -1
  136. package/dist/formulas/helpers/formula.js +0 -26
  137. package/dist/formulas/helpers/formula.js.map +0 -1
  138. package/dist/formulas/helpers/lexer.d.ts +0 -10
  139. package/dist/formulas/helpers/lexer.d.ts.map +0 -1
  140. package/dist/formulas/helpers/lexer.js +0 -73
  141. package/dist/formulas/helpers/lexer.js.map +0 -1
  142. package/dist/formulas/helpers/parser.d.ts +0 -24
  143. package/dist/formulas/helpers/parser.d.ts.map +0 -1
  144. package/dist/formulas/helpers/parser.js +0 -48
  145. package/dist/formulas/helpers/parser.js.map +0 -1
  146. package/dist/formulas/helpers/token.d.ts +0 -14
  147. package/dist/formulas/helpers/token.d.ts.map +0 -1
  148. package/dist/formulas/helpers/token.js +0 -14
  149. package/dist/formulas/helpers/token.js.map +0 -1
  150. package/dist/formulas/index.d.ts +0 -59
  151. package/dist/formulas/index.d.ts.map +0 -1
  152. package/dist/formulas/index.js +0 -186
  153. package/dist/formulas/index.js.map +0 -1
  154. package/dist/formulas/types/formulas.d.ts +0 -68
  155. package/dist/formulas/types/formulas.d.ts.map +0 -1
  156. package/dist/formulas/types/formulas.js +0 -2
  157. package/dist/formulas/types/formulas.js.map +0 -1
  158. package/dist/formulas/types/index.d.ts +0 -5
  159. package/dist/formulas/types/index.d.ts.map +0 -1
  160. package/dist/formulas/types/index.js +0 -2
  161. package/dist/formulas/types/index.js.map +0 -1
  162. package/dist/formulas/variants/array-formula.d.ts +0 -24
  163. package/dist/formulas/variants/array-formula.d.ts.map +0 -1
  164. package/dist/formulas/variants/array-formula.js +0 -142
  165. package/dist/formulas/variants/array-formula.js.map +0 -1
  166. package/dist/formulas/variants/base.d.ts +0 -6
  167. package/dist/formulas/variants/base.d.ts.map +0 -1
  168. package/dist/formulas/variants/base.js +0 -3
  169. package/dist/formulas/variants/base.js.map +0 -1
  170. package/dist/formulas/variants/basic.d.ts +0 -18
  171. package/dist/formulas/variants/basic.d.ts.map +0 -1
  172. package/dist/formulas/variants/basic.js +0 -128
  173. package/dist/formulas/variants/basic.js.map +0 -1
  174. package/dist/formulas/variants/comparison.d.ts +0 -25
  175. package/dist/formulas/variants/comparison.d.ts.map +0 -1
  176. package/dist/formulas/variants/comparison.js +0 -153
  177. package/dist/formulas/variants/comparison.js.map +0 -1
  178. package/dist/formulas/variants/conditional.d.ts +0 -18
  179. package/dist/formulas/variants/conditional.d.ts.map +0 -1
  180. package/dist/formulas/variants/conditional.js +0 -183
  181. package/dist/formulas/variants/conditional.js.map +0 -1
  182. package/dist/formulas/variants/iterative-array.d.ts +0 -20
  183. package/dist/formulas/variants/iterative-array.d.ts.map +0 -1
  184. package/dist/formulas/variants/iterative-array.js +0 -155
  185. package/dist/formulas/variants/iterative-array.js.map +0 -1
  186. package/dist/formulas/variants/per-value.d.ts +0 -20
  187. package/dist/formulas/variants/per-value.d.ts.map +0 -1
  188. package/dist/formulas/variants/per-value.js +0 -154
  189. package/dist/formulas/variants/per-value.js.map +0 -1
  190. package/dist/index.d.ts +0 -5
  191. package/dist/index.d.ts.map +0 -1
  192. package/dist/index.js +0 -6
  193. package/dist/index.js.map +0 -1
  194. package/dist/models/base.d.ts +0 -55
  195. package/dist/models/base.d.ts.map +0 -1
  196. package/dist/models/base.js +0 -146
  197. package/dist/models/base.js.map +0 -1
  198. package/dist/models/callback-manager.d.ts +0 -7
  199. package/dist/models/callback-manager.d.ts.map +0 -1
  200. package/dist/models/callback-manager.js +0 -89
  201. package/dist/models/callback-manager.js.map +0 -1
  202. package/dist/models/field.d.ts +0 -121
  203. package/dist/models/field.d.ts.map +0 -1
  204. package/dist/models/field.js +0 -443
  205. package/dist/models/field.js.map +0 -1
  206. package/dist/models/index.d.ts +0 -13
  207. package/dist/models/index.d.ts.map +0 -1
  208. package/dist/models/index.js +0 -7
  209. package/dist/models/index.js.map +0 -1
  210. package/dist/models/model.d.ts +0 -37
  211. package/dist/models/model.d.ts.map +0 -1
  212. package/dist/models/model.js +0 -245
  213. package/dist/models/model.js.map +0 -1
  214. package/dist/models/plugins/base.d.ts +0 -9
  215. package/dist/models/plugins/base.d.ts.map +0 -1
  216. package/dist/models/plugins/base.js +0 -3
  217. package/dist/models/plugins/base.js.map +0 -1
  218. package/dist/models/plugins/formula.d.ts +0 -18
  219. package/dist/models/plugins/formula.d.ts.map +0 -1
  220. package/dist/models/plugins/formula.js +0 -82
  221. package/dist/models/plugins/formula.js.map +0 -1
  222. package/dist/models/plugins/index.d.ts +0 -11
  223. package/dist/models/plugins/index.d.ts.map +0 -1
  224. package/dist/models/plugins/index.js +0 -52
  225. package/dist/models/plugins/index.js.map +0 -1
  226. package/dist/models/plugins/plugins.d.ts +0 -7
  227. package/dist/models/plugins/plugins.d.ts.map +0 -1
  228. package/dist/models/plugins/plugins.js +0 -7
  229. package/dist/models/plugins/plugins.js.map +0 -1
  230. package/dist/models/types/base-wise-model.d.ts +0 -7
  231. package/dist/models/types/base-wise-model.d.ts.map +0 -1
  232. package/dist/models/types/base-wise-model.js +0 -2
  233. package/dist/models/types/base-wise-model.js.map +0 -1
  234. package/dist/models/types/callbacks.d.ts +0 -19
  235. package/dist/models/types/callbacks.d.ts.map +0 -1
  236. package/dist/models/types/callbacks.js +0 -2
  237. package/dist/models/types/callbacks.js.map +0 -1
  238. package/dist/models/types/disabled.d.ts +0 -8
  239. package/dist/models/types/disabled.d.ts.map +0 -1
  240. package/dist/models/types/disabled.js +0 -2
  241. package/dist/models/types/disabled.js.map +0 -1
  242. package/dist/models/types/form-field.d.ts +0 -25
  243. package/dist/models/types/form-field.d.ts.map +0 -1
  244. package/dist/models/types/form-field.js +0 -2
  245. package/dist/models/types/form-field.js.map +0 -1
  246. package/dist/models/types/model.d.ts +0 -13
  247. package/dist/models/types/model.d.ts.map +0 -1
  248. package/dist/models/types/model.js +0 -2
  249. package/dist/models/types/model.js.map +0 -1
  250. package/dist/models/types/plugins.d.ts +0 -13
  251. package/dist/models/types/plugins.d.ts.map +0 -1
  252. package/dist/models/types/plugins.js +0 -2
  253. package/dist/models/types/plugins.js.map +0 -1
  254. package/dist/models/types/wrapped-form-model-props.d.ts +0 -11
  255. package/dist/models/types/wrapped-form-model-props.d.ts.map +0 -1
  256. package/dist/models/types/wrapped-form-model-props.js +0 -2
  257. package/dist/models/types/wrapped-form-model-props.js.map +0 -1
  258. package/dist/models/wrapper.d.ts +0 -30
  259. package/dist/models/wrapper.d.ts.map +0 -1
  260. package/dist/models/wrapper.js +0 -213
  261. package/dist/models/wrapper.js.map +0 -1
  262. package/dist/settings/index.d.ts +0 -7
  263. package/dist/settings/index.d.ts.map +0 -1
  264. package/dist/settings/index.js +0 -26
  265. package/dist/settings/index.js.map +0 -1
  266. package/dist/utils/pending-promise.d.ts +0 -6
  267. package/dist/utils/pending-promise.d.ts.map +0 -1
  268. package/dist/utils/pending-promise.js +0 -24
  269. package/dist/utils/pending-promise.js.map +0 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@ A reactive form library for React applications.
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @bgroup/wise-form react @beyond-js/reactive
8
+ pnpm add @bgroup/wise-form react @beyond-js/reactive
9
9
  ```
10
10
 
11
11
  ## Usage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgroup/wise-form",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "description": "Wise Form - A reactive form library",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,8 +13,20 @@
13
13
  "./styles": "./dist/form/styles.css"
14
14
  },
15
15
  "scripts": {
16
- "build": "tsc",
17
- "prepublishOnly": "npm run build"
16
+ "dev": "pnpm run dev:all",
17
+ "dev:ts": "vite build --watch --mode development",
18
+ "dev:css": "pnpm exec postcss src/form/styles.css -o dist/form/styles.css --watch",
19
+ "dev:all": "pnpm run dev:ts & pnpm run dev:css",
20
+ "build": "pnpm run clean && pnpm run build:types && pnpm run build:vite && pnpm run build:css",
21
+ "build:types": "tsc --emitDeclarationOnly",
22
+ "build:vite": "vite build",
23
+ "build:css": "pnpm exec postcss src/form/styles.css -o dist/form/styles.css",
24
+ "watch": "pnpm run dev:all",
25
+ "watch:ts": "tsc --watch --emitDeclarationOnly",
26
+ "watch:css": "pnpm exec postcss src/form/styles.css -o dist/form/styles.css --watch",
27
+ "clean": "rimraf dist",
28
+ "prepublishOnly": "pnpm run build",
29
+ "publish:public": "npm publish --access public"
18
30
  },
19
31
  "keywords": [
20
32
  "form",
@@ -33,6 +45,13 @@
33
45
  },
34
46
  "devDependencies": {
35
47
  "@types/react": "^18.0.26",
36
- "typescript": "^5.0.0"
48
+ "@vitejs/plugin-react": "^4.2.1",
49
+ "autoprefixer": "^10.4.22",
50
+ "postcss": "^8.5.6",
51
+ "postcss-cli": "^11.0.1",
52
+ "rimraf": "^5.0.5",
53
+ "tailwindcss": "^3.4.18",
54
+ "typescript": "^5.0.0",
55
+ "vite": "^5.0.0"
37
56
  }
38
57
  }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -1,40 +1,14 @@
1
- .reactive-form-container {
2
- display: grid;
3
- grid-gap: 1rem;
4
- }
5
-
6
- .reactive-form-container .rf-fields-container {
7
- display: grid;
8
- grid-gap: 0.5rem;
9
- }
10
-
11
- .reactive-form-container .rf-fields-container.fr-1 {
12
- grid-template-columns: repeat(1, 1fr);
13
- }
14
-
15
- .reactive-form-container .rf-fields-container.fr-2 {
16
- grid-template-columns: repeat(2, 1fr);
17
- }
18
-
19
- .reactive-form-container .rf-fields-container.fr-3 {
20
- grid-template-columns: repeat(3, 1fr);
21
- }
22
-
23
- .reactive-form-container .rf-fields-container.fr-4 {
24
- grid-template-columns: repeat(4, 1fr);
25
- }
26
-
27
- .reactive-form-container .rf-fields-container.fr-5 {
28
- grid-template-columns: repeat(5, 1fr);
29
- }
30
-
31
- .reactive-form-container .rf-fields-container.fr-6 {
32
- grid-template-columns: repeat(6, 1fr);
33
- }
34
-
35
- .reactive-form-container :not(.not-responsive) > .rf-fields-container {
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer components {
6
+ /* Responsive: en mobile los contenedores sin .not-responsive se convierten en 1 columna */
7
+ /* Esto requiere un selector CSS complejo que no se puede hacer solo con clases de Tailwind */
8
+ /* Usamos !important para sobrescribir estilos inline dinámicos */
36
9
  @media (max-width: 768px) {
37
- grid-template-columns: 1fr !important;
10
+ .reactive-form-container :not(.not-responsive) > .rf-fields-container {
11
+ grid-template-columns: 1fr !important;
12
+ }
38
13
  }
39
14
  }
40
-
@@ -10,9 +10,38 @@ export function Containers() {
10
10
  } = useWiseFormContext();
11
11
 
12
12
  const fields = [...model.fields.values()];
13
- return rows.map((num, index) => {
14
- const items = fields.splice(0, num[0]) as any; // Type assertion needed due to TypeScript strictness
15
- return <RowFieldContainer model={model} template={num} items={items} key={`rf-row--${index}.${num}`} styles={styles} />;
16
- });
13
+
14
+ // Use slice instead of splice to avoid mutating the array
15
+ // Calculate indices safely using useMemo with stable dependencies
16
+ const rowsLength = rows?.length || 0;
17
+ const fieldsSize = model.fields?.size || 0;
18
+ const modelName = model.name;
19
+
20
+ const containers = React.useMemo(() => {
21
+ let currentIndex = 0;
22
+ const result = rows.map((num, index) => {
23
+ const items = fields.slice(currentIndex, currentIndex + num[0]) as any; // Type assertion needed due to TypeScript strictness
24
+ currentIndex += num[0];
25
+ return <RowFieldContainer model={model} template={num} items={items} key={`rf-row--${index}.${num}`} styles={styles} />;
26
+ });
27
+ return result;
28
+ // eslint-disable-next-line react-hooks/exhaustive-deps
29
+ }, [rowsLength, fieldsSize, modelName]);
30
+
31
+ React.useEffect(() => {
32
+ const formElement = document.querySelector('form.reactive-form-container') as HTMLElement;
33
+ const containerElements = formElement?.querySelectorAll('.rf-fields-container');
34
+
35
+ // Verificar si hay algún contenedor que retorna null
36
+ if (containers.length > 0 && (!containerElements || containerElements.length === 0)) {
37
+ // Silent check - no logging
38
+ }
39
+ }, [containers.length]);
40
+
41
+ if (containers.length === 0) {
42
+ return null;
43
+ }
44
+
45
+ return <>{containers}</>;
17
46
  }
18
47
 
@@ -1,9 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
3
  export function ErrorRenderer({ error }) {
4
- React.useEffect(() => {
5
- console.error(error);
6
- }, []);
7
4
  return <div className='alert alert--error pui-alert'>{error}</div>;
8
5
  }
9
6
 
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
3
  export function ControlFieldContainer({ children }) {
4
- return <div className='rf-field-container'>{children}</div>;
4
+ return <div className='rf-field-container '>{children}</div>;
5
5
  }
6
6
 
@@ -22,13 +22,12 @@ type WiseFormFieldControlProps = {
22
22
  export const Control = ({ field, index, model, hidden }: WiseFormFieldControlProps) => {
23
23
  const { formTypes } = useWiseFormContext();
24
24
  const fieldItem = field as any; // Using any to access dynamic properties
25
-
25
+
26
26
  // Early return if field doesn't have a name (except for wrapper types and special types)
27
27
  const specialTypes = ['wrapper', 'hr', 'button'];
28
- const fieldType = fieldItem?.type;
28
+ let fieldType = fieldItem?.type; // Use let instead of const to allow modification
29
29
  const fieldName = fieldItem?.name;
30
30
  if (!fieldName && !specialTypes.includes(fieldType)) {
31
- console.warn('You need to provide a name to get a field in form', (model as any).name || 'unknown');
32
31
  return null;
33
32
  }
34
33
 
@@ -77,18 +76,54 @@ export const Control = ({ field, index, model, hidden }: WiseFormFieldControlPro
77
76
  ...formTypes,
78
77
  };
79
78
 
79
+ // Handle undefined type fields - use default type instead of returning null
80
+ if (!fieldType && fieldName) {
81
+ // Check if field has className 'hide' - these are intentionally hidden fields
82
+ const className = (fieldItem as any).className || '';
83
+ if (className.includes('hide') || className === 'hide') {
84
+ return null;
85
+ }
86
+
87
+ // Check if it's a hidden field in the model
88
+ const fieldModel = model.getField(fieldName);
89
+ if (fieldModel) {
90
+ const properties = (fieldModel as FormField).getProperties();
91
+ if ((properties as any).hidden) {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ // If no type is defined, use default - don't skip the field
97
+ (fieldItem as any).type = 'default';
98
+ fieldType = 'default'; // Update fieldType variable to use default
99
+ }
100
+
80
101
  const ControlComponent = types[fieldType] ?? types.default;
81
-
102
+
103
+ if (!ControlComponent) {
104
+ return null;
105
+ }
106
+
107
+ // Validar que ControlComponent sea realmente un componente válido (función o clase)
108
+ if (typeof ControlComponent !== 'function' && typeof ControlComponent !== 'object') {
109
+ return null;
110
+ }
111
+
112
+ // Si es un objeto, puede ser un componente de React (tiene render o $$typeof)
113
+ if (typeof ControlComponent === 'object' && !ControlComponent.$$typeof && typeof ControlComponent !== 'function') {
114
+ return null;
115
+ }
116
+
82
117
  // Merge field properties with attrs to ensure custom components receive all necessary props
83
118
  const fieldModel = fieldName ? model.getField(fieldName) : null;
84
119
  const fieldProperties = fieldModel ? fieldModel.getProperties() : {};
85
-
120
+
86
121
  // Also include original field item properties (like options, label, etc.)
87
122
  const fieldItemProps = { ...fieldItem };
88
123
  // Remove internal properties that shouldn't be passed to components
89
124
  delete (fieldItemProps as any).name;
90
125
  delete (fieldItemProps as any).type;
91
-
126
+
92
127
  // Filter out non-HTML attributes from fieldProperties to prevent React warnings
93
128
  const invalidAttributes = ['processing', 'processed', 'properties', 'specs', 'hidden', 'identifier'];
94
129
  const filteredFieldProperties = Object.keys(fieldProperties).reduce((acc, key) => {
@@ -97,7 +132,7 @@ export const Control = ({ field, index, model, hidden }: WiseFormFieldControlPro
97
132
  }
98
133
  return acc;
99
134
  }, {} as any);
100
-
135
+
101
136
  return (
102
137
  <ControlFieldContainer>
103
138
  <ControlComponent {...attrs} {...fieldItemProps} {...filteredFieldProperties} />
@@ -39,6 +39,15 @@ export function RowFieldContainer({ template: [totalFields, gridStyle], items, s
39
39
  });
40
40
 
41
41
  // Suscribirse a los cambios de cada campo
42
+ // Use stable references for field names to avoid unnecessary re-renders
43
+ const fieldNamesRef = React.useRef<string[]>([]);
44
+ const currentFieldNames = items.map((field: any) => field?.name).filter(Boolean);
45
+ const fieldNamesChanged = JSON.stringify(fieldNamesRef.current) !== JSON.stringify(currentFieldNames);
46
+
47
+ if (fieldNamesChanged) {
48
+ fieldNamesRef.current = currentFieldNames;
49
+ }
50
+
42
51
  React.useEffect(() => {
43
52
  const listeners: Array<() => void> = [];
44
53
 
@@ -53,10 +62,14 @@ export function RowFieldContainer({ template: [totalFields, gridStyle], items, s
53
62
 
54
63
  const onChange = () => {
55
64
  const properties = (fieldModel as FormField).getProperties();
56
- setFieldHiddenStates((prev) => ({
57
- ...prev,
58
- [fieldName]: (properties as any).hidden ?? false,
59
- }));
65
+ setFieldHiddenStates((prev) => {
66
+ const newHidden = (properties as any).hidden ?? false;
67
+ if (prev[fieldName] === newHidden) return prev; // Avoid unnecessary state update
68
+ return {
69
+ ...prev,
70
+ [fieldName]: newHidden,
71
+ };
72
+ });
60
73
  };
61
74
 
62
75
  fieldModel.on('change', onChange);
@@ -66,12 +79,15 @@ export function RowFieldContainer({ template: [totalFields, gridStyle], items, s
66
79
  return () => {
67
80
  listeners.forEach((cleanup) => cleanup());
68
81
  };
69
- }, [items, model]);
82
+ // eslint-disable-next-line react-hooks/exhaustive-deps
83
+ }, [currentFieldNames.join(','), model.name]);
70
84
 
71
85
  let hidden = false;
72
86
  const output = items.reduce((acc: React.ReactElement[], field, index) => {
73
87
  const fieldItem = field as any; // Using any to access dynamic properties
74
88
  const fieldType = fieldItem?.type;
89
+ const fieldName = fieldItem?.name;
90
+
75
91
  if (fieldType === "wrapper") {
76
92
  const fieldModel = model.getField(fieldItem?.name);
77
93
  const wrapperHidden = fieldModel ? ((fieldModel as WrappedFormModel).getProperties() as any).hidden ?? false : false;
@@ -80,7 +96,6 @@ export function RowFieldContainer({ template: [totalFields, gridStyle], items, s
80
96
  return acc;
81
97
  }
82
98
 
83
- const fieldName = fieldItem?.name;
84
99
  const isHidden = fieldName ? (fieldHiddenStates[fieldName] ?? (fieldItem as any).hidden ?? false) : false;
85
100
  if (!isHidden) {
86
101
  acc.push(<Control index={index} model={model} field={fieldItem} key={`rf-row__item--${index}`} hidden={isHidden} />);
@@ -88,9 +103,22 @@ export function RowFieldContainer({ template: [totalFields, gridStyle], items, s
88
103
  return acc;
89
104
  }, []);
90
105
 
91
- const attrs = { className: `rf-fields-container`, style: {} };
92
- attrs.style = { gridTemplateColumns: `${gridStyle}`, ...styles };
93
- if (hidden) return null;
106
+ // Construir clases de Tailwind para el contenedor
107
+ // grid gap-2 para el layout base
108
+ // En mobile (< 768px), los contenedores sin .not-responsive se convierten en 1 columna
109
+ // Esto se maneja con CSS personalizado porque requiere el selector :not(.not-responsive) >
110
+ const attrs = {
111
+ className: `rf-fields-container grid gap-2 w-full`,
112
+ style: { gridTemplateColumns: `${gridStyle}`, ...styles }
113
+ };
114
+ if (hidden) {
115
+ return null;
116
+ }
117
+
118
+ if (output.length === 0) {
119
+ return null;
120
+ }
121
+
94
122
  return <div {...attrs}>{output}</div>;
95
123
  }
96
124
 
@@ -1,27 +1,38 @@
1
1
  import React from "react";
2
- import {useWiseFormContext} from "../../context";
2
+ import { useWiseFormContext } from "../../context";
3
3
  /**
4
4
  *
5
5
  * @param data {WrappedFormModel}
6
6
  * @param model {FormModel} parent.
7
7
  * @returns
8
8
  */
9
- export function FormSectionWrapper({data, model}) {
10
- const {formTypes} = useWiseFormContext();
9
+ export function FormSectionWrapper({ data, model }) {
10
+ const { formTypes } = useWiseFormContext();
11
11
 
12
12
  const types = {
13
13
  ...formTypes,
14
14
  };
15
15
 
16
- if (!data.control) throw new Error("Wrapper must have a control");
16
+ if (!data.control) {
17
+ throw new Error("Wrapper must have a control");
18
+ }
17
19
  if (!data.name) {
18
- console.error("Wrapper must have a name", data);
19
20
  return null;
20
21
  }
21
- if (data?.hidden) return null;
22
+ if (data?.hidden) {
23
+ return null;
24
+ }
22
25
 
23
26
  const wrapperModel = model?.getField(data.name);
27
+ if (!wrapperModel) {
28
+ return null;
29
+ }
30
+
24
31
  const Control = types[data.control];
32
+ if (!Control) {
33
+ return null;
34
+ }
35
+
25
36
  // data = wrapperModel ? { ...data, ...wrapperModel.getProperties() } : data;
26
37
  return <Control model={wrapperModel} />;
27
38
  }
@@ -6,13 +6,46 @@ import { useTemplate } from '../hooks/use-template';
6
6
  export function WrappedForm({ children, name, types }): JSX.Element {
7
7
  const { model: parent } = useWiseFormContext();
8
8
  const wrapper = parent.wrappers.get(name);
9
+
10
+ if (!wrapper) {
11
+ return null;
12
+ }
13
+
9
14
  const model = wrapper;
10
15
  const template = useTemplate(model.settings);
11
- const fields = [...model.settings.fields];
12
- const Containers = template.items.map((num, index) => {
13
- const items = fields.splice(0, num[0]);
14
- return <RowFieldContainer template={num} model={model} items={items} key={`rf-row--${index}.${num}`} />;
15
- });
16
+
17
+ // Memoizar fields usando una clave estable basada en el contenido
18
+ // Comparar por longitud y nombres de campos para evitar recrear el array innecesariamente
19
+ const fieldsKey = React.useMemo(() => {
20
+ const fieldsArray = model.settings.fields || [];
21
+ return `${fieldsArray.length}-${fieldsArray.slice(0, 5).map(f => f.name || f.type || '').join(',')}`;
22
+ }, [model.settings.fields]);
23
+
24
+ const fields = React.useMemo(() => {
25
+ return [...model.settings.fields];
26
+ }, [fieldsKey]);
27
+
28
+ // Use stable references for useMemo dependencies
29
+ // Usar una referencia estable del modelo y sus propiedades clave
30
+ const modelName = model.name;
31
+ const templateItemsLength = template.items?.length || 0;
32
+ const fieldsLength = fields.length;
33
+
34
+ // Crear una clave estable basada en las propiedades del modelo que realmente importan
35
+ const modelKey = React.useMemo(() => {
36
+ return `${modelName}-${templateItemsLength}-${fieldsLength}`;
37
+ }, [modelName, templateItemsLength, fieldsLength]);
38
+
39
+ const Containers = React.useMemo(() => {
40
+ let currentIndex = 0;
41
+ const result = template.items.map((num, index) => {
42
+ const items = fields.slice(currentIndex, currentIndex + num[0]);
43
+ currentIndex += num[0];
44
+ return <RowFieldContainer template={num} model={model} items={items} key={`rf-row--${index}.${num}`} />;
45
+ });
46
+ return result;
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, [modelKey]);
16
49
 
17
50
  const value = {
18
51
  model,
@@ -2,36 +2,102 @@ import React from 'react';
2
2
  import { FormModel } from '@bgroup/wise-form/models';
3
3
  import { useTemplate } from './use-template';
4
4
 
5
+ /**
6
+ * Hook para gestionar el modelo del formulario
7
+ *
8
+ * Principios SOLID aplicados:
9
+ * - Single Responsibility: Solo gestiona el estado del modelo del formulario
10
+ * - Open/Closed: Extensible mediante eventos del modelo sin modificar el hook
11
+ * - Dependency Inversion: Depende de la abstracción FormModel, no de implementaciones concretas
12
+ */
5
13
  export function useModel(settings, form?: FormModel) {
6
- const [model, setModel] = React.useState(form);
7
- const [ready, setReady] = React.useState(false);
8
- const [values, setValues] = React.useState(form?.values || {});
9
- const templateSpecs = settings ? settings : form;
10
- const { type, styles, items } = useTemplate(templateSpecs, templateSpecs.gap);
11
- const startup = () => {
12
- setReady(false);
13
- const onChange = () => {
14
- setReady(form.ready);
14
+ // Estado del modelo - usa el form pasado o null inicialmente
15
+ const [model, setModel] = React.useState<FormModel | null>(form || null);
16
+
17
+ // Determinar si el modelo está listo basándose en su estado real
18
+ const getModelReadyState = React.useCallback((currentModel: FormModel | null): boolean => {
19
+ if (!currentModel) return false;
20
+
21
+ // Si el modelo tiene la propiedad ready, usarla
22
+ if (currentModel.ready !== undefined) {
23
+ return currentModel.ready === true;
24
+ }
25
+
26
+ // Si no tiene ready pero tiene campos y wrappers, asumir que está listo
27
+ const hasFields = currentModel.fields?.size > 0;
28
+ const hasWrappers = currentModel.wrappers?.size > 0;
29
+ const hasName = !!currentModel.name;
30
+
31
+ // Está listo si tiene al menos campos o wrappers y un nombre
32
+ return hasName && (hasFields || hasWrappers);
33
+ }, []);
34
+
35
+ // Estado de ready basado en el modelo actual
36
+ const [ready, setReady] = React.useState(() => getModelReadyState(form || null));
37
+ const [values, setValues] = React.useState(() => form?.values || {});
38
+
39
+ // Template specs: usar settings si están disponibles, sino usar el form
40
+ const templateSpecs = settings || form;
41
+ const { type, styles, items } = useTemplate(templateSpecs, templateSpecs?.gap);
42
+
43
+ // Efecto para inicializar y suscribirse a cambios del modelo
44
+ React.useEffect(() => {
45
+ let currentModel: FormModel | null = null;
46
+ let cleanup: (() => void) | null = null;
47
+
48
+ // Si ya tenemos un modelo, usarlo directamente
49
+ if (form) {
50
+ currentModel = form;
51
+ setModel(form);
52
+ const initialReady = getModelReadyState(form);
53
+ setReady(initialReady);
15
54
  setValues({ ...form.values });
16
- };
17
-
18
- if (!form) {
19
- const properties = settings.fields.map(item => item.name);
20
- const values = settings.values || {};
21
- form = new FormModel(settings, { properties, ...values });
55
+ }
56
+ // Si no hay modelo pero hay settings, crear uno nuevo
57
+ else if (settings) {
58
+ try {
59
+ const properties = settings.fields?.map((item: any) => item.name) || [];
60
+ const values = settings.values || {};
61
+ currentModel = new FormModel(settings, { properties, ...values });
62
+ setModel(currentModel);
63
+
64
+ // Verificar estado inicial del modelo creado
65
+ const modelReady = getModelReadyState(currentModel);
66
+ setReady(modelReady);
67
+ setValues({ ...currentModel.values });
68
+ } catch (error) {
69
+ setReady(false);
70
+ return;
71
+ }
72
+ }
73
+ // No hay modelo ni settings
74
+ else {
75
+ setReady(false);
76
+ return;
22
77
  }
23
-
24
- setModel(form);
25
- form.on('change', onChange);
26
-
27
- onChange();
28
-
78
+
79
+ // Suscribirse a cambios del modelo para actualizar el estado
80
+ if (currentModel) {
81
+ const onChange = () => {
82
+ const isReady = getModelReadyState(currentModel);
83
+ setReady(isReady);
84
+ if (currentModel) {
85
+ setValues({ ...currentModel.values });
86
+ }
87
+ };
88
+
89
+ currentModel.on('change', onChange);
90
+ cleanup = () => {
91
+ currentModel?.off('change', onChange);
92
+ };
93
+ }
94
+
29
95
  return () => {
30
- form.off('change', onChange);
96
+ if (cleanup) {
97
+ cleanup();
98
+ }
31
99
  };
32
- };
33
-
34
- React.useEffect(startup, [form?.name]);
100
+ }, [form?.name, settings?.name, getModelReadyState]);
35
101
 
36
102
  return { ready, model, values, type, styles, items };
37
103
  }
@@ -10,10 +10,22 @@ export function WiseForm({ children, settings, types, model }: IWiseFormSpecs):
10
10
  const { ready, model: instance, type, styles, items } = useModel(settings, model);
11
11
  const formTypes = useTypes(types);
12
12
 
13
- if (!ready) return null;
13
+ // IMPORTANTE: useEffect debe estar ANTES de cualquier early return
14
+ // para cumplir con las reglas de hooks de React
15
+ React.useEffect(() => {
16
+ if (!ready || !instance) return;
17
+ }, [ready, instance?.name]);
18
+
19
+ if (!ready) {
20
+ return null;
21
+ }
14
22
 
15
23
  if (!settings && !model) {
16
- console.error('the form does not have settings or model defined', settings);
24
+ return null;
25
+ }
26
+
27
+ if (!instance) {
28
+ return null;
17
29
  }
18
30
 
19
31
  const onSubmit = (event: React.FormEvent) => {
@@ -38,7 +50,7 @@ export function WiseForm({ children, settings, types, model }: IWiseFormSpecs):
38
50
  <WiseFormContext.Provider value={value}>
39
51
  <form onKeyDown={(e) => {
40
52
  if (e.key === 'Enter') e.preventDefault();
41
- }} className="reactive-form-container" onSubmit={onSubmit}>
53
+ }} className="reactive-form-container grid gap-4" onSubmit={onSubmit}>
42
54
  <Containers />
43
55
  {children}
44
56
  </form>