@sealcode/sealgen 0.18.10 → 0.19.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 (378) hide show
  1. package/.eslintrc.cjs +7 -2
  2. package/.xunit +74 -69
  3. package/@types/add-crud.d.sync-conflict-20250918-184311-TFDLWFG.ts +1 -0
  4. package/@types/builders/colors-builder.d.sync-conflict-20250918-184323-TFDLWFG.ts +10 -0
  5. package/@types/builders/colors-builder.d.ts +1 -1
  6. package/@types/builders/fonts-builder.d.sync-conflict-20250918-184322-TFDLWFG.ts +7 -0
  7. package/@types/builders/fonts-builder.d.ts +1 -1
  8. package/@types/controllers/table-add-button.d.ts +6 -0
  9. package/@types/controllers/table-add-button.stimulus.d.ts +6 -0
  10. package/@types/crud-field-snippets.d.sync-conflict-20250918-184323-TFDLWFG.ts +1 -0
  11. package/@types/crud-field-snippets.d.ts +1 -3
  12. package/@types/forms/controls/array.d.sync-conflict-20250918-184306-TFDLWFG.ts +33 -0
  13. package/@types/forms/controls/array.d.ts +1 -3
  14. package/@types/forms/controls/array.sync-conflict-20250918-222045-TFDLWFG.d.ts +33 -0
  15. package/@types/forms/controls/autocomplete.d.sync-conflict-20250918-222042-TFDLWFG.ts +22 -0
  16. package/@types/forms/controls/autocomplete.d.ts +1 -1
  17. package/@types/forms/controls/checkbox-group.d.sync-conflict-20250918-184319-TFDLWFG.ts +21 -0
  18. package/@types/forms/controls/checkbox-group.d.ts +3 -3
  19. package/@types/forms/controls/checkboxed-list-input.d.sync-conflict-20250918-184323-TFDLWFG.ts +21 -0
  20. package/@types/forms/controls/checkboxed-list-input.d.ts +3 -3
  21. package/@types/forms/controls/controls.d.ts +2 -2
  22. package/@types/forms/controls/derived-frame.d.sync-conflict-20250918-184314-TFDLWFG.ts +19 -0
  23. package/@types/forms/controls/derived-frame.d.ts +0 -2
  24. package/@types/forms/controls/dropdown.d.sync-conflict-20250918-222044-TFDLWFG.ts +30 -0
  25. package/@types/forms/controls/dropdown.d.ts +1 -1
  26. package/@types/forms/controls/file.d.sync-conflict-20250918-222045-TFDLWFG.ts +13 -0
  27. package/@types/forms/controls/file.d.ts +1 -2
  28. package/@types/forms/controls/form-control.d.sync-conflict-20250918-184322-TFDLWFG.ts +19 -0
  29. package/@types/forms/controls/form-control.d.sync-conflict-20250918-222046-TFDLWFG.ts +19 -0
  30. package/@types/forms/controls/form-control.d.ts +7 -4
  31. package/@types/forms/controls/image.d.ts +19 -0
  32. package/@types/forms/controls/image.test.d.ts +1 -0
  33. package/@types/forms/controls/multiple-files.d.sync-conflict-20250918-184318-TFDLWFG.ts +42 -0
  34. package/@types/forms/controls/multiple-files.d.ts +3 -1
  35. package/@types/forms/controls/multiple-images.d.ts +19 -0
  36. package/@types/forms/controls/number.d.sync-conflict-20250918-222046-TFDLWFG.ts +13 -0
  37. package/@types/forms/controls/number.d.ts +1 -1
  38. package/@types/forms/controls/photo.d.sync-conflict-20250918-184321-TFDLWFG.ts +19 -0
  39. package/@types/forms/controls/photo.d.ts +3 -5
  40. package/@types/forms/controls/simple-input.d.sync-conflict-20250918-184310-TFDLWFG.ts +33 -0
  41. package/@types/forms/controls/simple-input.d.sync-conflict-20250918-222042-TFDLWFG.ts +33 -0
  42. package/@types/forms/controls/simple-input.d.ts +5 -4
  43. package/@types/forms/controls/simple-input.sync-conflict-20250918-222045-TFDLWFG.d.ts +33 -0
  44. package/@types/forms/controls/single-reference.d.ts +3 -2
  45. package/@types/forms/controls/table.d.sync-conflict-20250918-184302-TFDLWFG.ts +39 -0
  46. package/@types/forms/controls/table.d.ts +8 -3
  47. package/@types/forms/controls/table.sync-conflict-20250918-222042-TFDLWFG.d.ts +39 -0
  48. package/@types/forms/controls/textarea.d.sync-conflict-20250918-222045-TFDLWFG.ts +25 -0
  49. package/@types/forms/controls/textarea.d.ts +3 -3
  50. package/@types/forms/fields/boolean.d.sync-conflict-20250918-184325-TFDLWFG.ts +22 -0
  51. package/@types/forms/fields/boolean.d.ts +6 -1
  52. package/@types/forms/fields/boolean.sync-conflict-20250918-222042-TFDLWFG.d.ts +22 -0
  53. package/@types/forms/fields/checkboxed-list.d.sync-conflict-20250918-184325-TFDLWFG.ts +23 -0
  54. package/@types/forms/fields/checkboxed-list.d.ts +3 -1
  55. package/@types/forms/fields/collection-field.d.sync-conflict-20250918-184319-TFDLWFG.ts +22 -0
  56. package/@types/forms/fields/collection-field.d.sync-conflict-20250918-222047-TFDLWFG.ts +22 -0
  57. package/@types/forms/fields/collection-field.d.ts +8 -1
  58. package/@types/forms/fields/color.d.ts +4 -0
  59. package/@types/forms/fields/coordinates.d.sync-conflict-20250918-222044-TFDLWFG.ts +14 -0
  60. package/@types/forms/fields/coordinates.d.ts +14 -0
  61. package/@types/forms/fields/date.d.ts +13 -0
  62. package/@types/forms/fields/datetime.d.sync-conflict-20250918-222050-TFDLWFG.ts +4 -0
  63. package/@types/forms/fields/datetime.d.ts +14 -0
  64. package/@types/forms/fields/deep-reverse-single-reference-list.d.ts +24 -0
  65. package/@types/forms/fields/deep-reverse-single-reference-table.d.ts +16 -0
  66. package/@types/forms/fields/email.d.sync-conflict-20250918-184303-TFDLWFG.ts +8 -0
  67. package/@types/forms/fields/email.d.ts +3 -0
  68. package/@types/forms/fields/enum-multiple-sealious.d.ts +16 -0
  69. package/@types/forms/fields/enum-multiple-sealious.sync-conflict-20250918-222047-TFDLWFG.d.ts +16 -0
  70. package/@types/forms/fields/field.d.sync-conflict-20250918-184306-TFDLWFG.ts +70 -0
  71. package/@types/forms/fields/field.d.ts +37 -5
  72. package/@types/forms/fields/fields.d.sync-conflict-20250918-184314-TFDLWFG.ts +29 -0
  73. package/@types/forms/fields/fields.d.ts +23 -8
  74. package/@types/forms/fields/file.d.sync-conflict-20250918-184310-TFDLWFG.ts +23 -0
  75. package/@types/forms/fields/file.d.sync-conflict-20250918-222046-TFDLWFG.ts +23 -0
  76. package/@types/forms/fields/file.d.ts +11 -5
  77. package/@types/forms/fields/file.sync-conflict-20250918-222049-TFDLWFG.d.ts +23 -0
  78. package/@types/forms/fields/float.d.ts +4 -0
  79. package/@types/forms/fields/get-field-for-sealious.d.sync-conflict-20250918-222046-TFDLWFG.ts +29 -0
  80. package/@types/forms/fields/get-field-for-sealious.d.ts +31 -0
  81. package/@types/forms/fields/get-field-for-sealious.sync-conflict-20250918-222042-TFDLWFG.d.ts +29 -0
  82. package/@types/forms/fields/html.d.ts +6 -0
  83. package/@types/forms/fields/image.d.sync-conflict-20250918-222042-TFDLWFG.ts +5 -0
  84. package/@types/forms/fields/image.d.ts +5 -0
  85. package/@types/forms/fields/int.d.ts +6 -0
  86. package/@types/forms/fields/json.d.ts +13 -0
  87. package/@types/forms/fields/markdown.d.ts +6 -0
  88. package/@types/forms/fields/multiple-files.d.sync-conflict-20250918-184304-TFDLWFG.ts +16 -0
  89. package/@types/forms/fields/multiple-files.d.ts +3 -1
  90. package/@types/forms/fields/number.d.sync-conflict-20250918-184317-TFDLWFG.ts +25 -0
  91. package/@types/forms/fields/number.d.sync-conflict-20250918-222047-TFDLWFG.ts +25 -0
  92. package/@types/forms/fields/number.d.ts +3 -1
  93. package/@types/forms/fields/password.d.ts +6 -0
  94. package/@types/forms/fields/pick-from-list-sealious.d.ts +11 -0
  95. package/@types/forms/fields/pick-from-list.d.sync-conflict-20250918-184322-TFDLWFG.ts +15 -0
  96. package/@types/forms/fields/pick-from-list.d.ts +4 -3
  97. package/@types/forms/fields/proxy-field.d.sync-conflict-20250918-184307-TFDLWFG.ts +15 -0
  98. package/@types/forms/fields/proxy-field.d.ts +3 -1
  99. package/@types/forms/fields/regexp.d.sync-conflict-20250918-184305-TFDLWFG.ts +21 -0
  100. package/@types/forms/fields/regexp.d.sync-conflict-20250918-222045-TFDLWFG.ts +21 -0
  101. package/@types/forms/fields/regexp.d.ts +6 -1
  102. package/@types/forms/fields/regexp.sync-conflict-20250918-222047-TFDLWFG.d.ts +21 -0
  103. package/@types/forms/fields/simple-form-field.d.sync-conflict-20250918-184309-TFDLWFG.ts +29 -0
  104. package/@types/forms/fields/simple-form-field.d.sync-conflict-20250918-222044-TFDLWFG.ts +29 -0
  105. package/@types/forms/fields/simple-form-field.d.ts +23 -3
  106. package/@types/forms/fields/simple-form-field.sync-conflict-20250918-222049-TFDLWFG.d.ts +29 -0
  107. package/@types/forms/fields/single-reference.d.ts +19 -0
  108. package/@types/forms/fields/single-reference.sync-conflict-20250918-222043-TFDLWFG.d.ts +10 -0
  109. package/@types/forms/fields/structured-array.d.sync-conflict-20250918-184302-TFDLWFG.ts +27 -0
  110. package/@types/forms/fields/structured-array.d.sync-conflict-20250918-222049-TFDLWFG.ts +27 -0
  111. package/@types/forms/fields/structured-array.d.ts +19 -10
  112. package/@types/forms/fields/structured-array.sync-conflict-20250918-222047-TFDLWFG.d.ts +27 -0
  113. package/@types/forms/fields/table.d.sync-conflict-20250918-184303-TFDLWFG.ts +16 -0
  114. package/@types/forms/fields/table.d.ts +3 -0
  115. package/@types/forms/fields/text.d.ts +6 -0
  116. package/@types/forms/fields/url.d.ts +6 -0
  117. package/@types/forms/form-types.d.sync-conflict-20250918-222045-TFDLWFG.ts +26 -0
  118. package/@types/forms/form-types.d.ts +2 -0
  119. package/@types/forms/form.d.sync-conflict-20250918-184315-TFDLWFG.ts +32 -0
  120. package/@types/forms/form.d.sync-conflict-20250918-222049-TFDLWFG.ts +32 -0
  121. package/@types/forms/form.d.ts +10 -8
  122. package/@types/forms/form.sync-conflict-20250918-222049-TFDLWFG.d.ts +33 -0
  123. package/@types/forms/form.test.d.sync-conflict-20250918-184318-TFDLWFG.ts +6 -0
  124. package/@types/forms/form.test.d.sync-conflict-20250918-222047-TFDLWFG.ts +6 -0
  125. package/@types/forms/form.test.d.ts +2 -2
  126. package/@types/forms/multiform.d.sync-conflict-20250918-184308-TFDLWFG.ts +23 -0
  127. package/@types/forms/multiform.d.ts +1 -2
  128. package/@types/forms/multiform.sync-conflict-20250918-222044-TFDLWFG.d.ts +23 -0
  129. package/@types/index.d.sync-conflict-20250918-184316-TFDLWFG.ts +46 -0
  130. package/@types/index.d.ts +2 -2
  131. package/@types/mount.d.sync-conflict-20250918-184320-TFDLWFG.ts +6 -0
  132. package/@types/mount.d.ts +0 -1
  133. package/@types/page/list.d.sync-conflict-20250918-184318-TFDLWFG.ts +48 -0
  134. package/@types/page/list.d.sync-conflict-20250918-222046-TFDLWFG.ts +48 -0
  135. package/@types/page/list.d.ts +2 -2
  136. package/@types/page/list.sync-conflict-20250918-222048-TFDLWFG.d.ts +48 -0
  137. package/@types/page/mountable-with-fields.d.sync-conflict-20250918-184321-TFDLWFG.ts +42 -0
  138. package/@types/page/mountable-with-fields.d.sync-conflict-20250918-222046-TFDLWFG.ts +42 -0
  139. package/@types/page/mountable-with-fields.d.ts +3 -3
  140. package/@types/page/mountable-with-fields.sync-conflict-20250918-222044-TFDLWFG.d.ts +42 -0
  141. package/@types/page/mountable.d.sync-conflict-20250918-184306-TFDLWFG.ts +10 -0
  142. package/@types/page/mountable.d.ts +0 -1
  143. package/@types/page/page.d.sync-conflict-20250918-184320-TFDLWFG.ts +18 -0
  144. package/@types/page/page.d.ts +0 -1
  145. package/@types/page/props-parser.d.sync-conflict-20250918-184305-TFDLWFG.ts +15 -0
  146. package/@types/page/props-parser.d.ts +0 -1
  147. package/@types/page/stateful-page.d.sync-conflict-20250918-184326-TFDLWFG.ts +62 -0
  148. package/@types/page/stateful-page.d.ts +0 -1
  149. package/@types/templates/shared/shared-crud-form-fields.d.sync-conflict-20250918-184306-TFDLWFG.ts +1 -0
  150. package/@types/templates/shared/shared-crud-form-fields.d.ts +0 -67
  151. package/@types/test_utils/test-on-real-app.d.sync-conflict-20250918-184320-TFDLWFG.ts +26 -0
  152. package/@types/test_utils/test-on-real-app.d.ts +0 -1
  153. package/@types/utils/extract-fields-from-collection.d.sync-conflict-20250918-184312-TFDLWFG.ts +18 -0
  154. package/@types/utils/extract-fields-from-collection.d.ts +5 -1
  155. package/@types/utils/extract-fields-from-collection.sync-conflict-20250918-222045-TFDLWFG.d.ts +18 -0
  156. package/@types/utils/wrap-attributes.d.ts +3 -0
  157. package/README.md +5 -0
  158. package/assets/file-icon.svg +5 -0
  159. package/lib/add-crud.js +80 -89
  160. package/lib/add-list-route.test.js +1 -1
  161. package/lib/add-route.js +0 -7
  162. package/lib/build.js +3 -1
  163. package/lib/cli.js +2 -5
  164. package/lib/controllers/table-add-button.stimulus.js +35 -0
  165. package/lib/forms/controls/array.js +20 -16
  166. package/lib/forms/controls/big-radio-group.js +2 -2
  167. package/lib/forms/controls/checkbox-group.js +7 -7
  168. package/lib/forms/controls/checkboxed-list-input.js +9 -2
  169. package/lib/forms/controls/controls.js +2 -2
  170. package/lib/forms/controls/derived-frame.js +5 -1
  171. package/lib/forms/controls/derived.js +1 -1
  172. package/lib/forms/controls/dropdown.js +4 -1
  173. package/lib/forms/controls/editable-collection-subset.js +2 -2
  174. package/lib/forms/controls/file.js +26 -2
  175. package/lib/forms/controls/file.test.js +2 -2
  176. package/lib/forms/controls/hidden-input.js +4 -1
  177. package/lib/forms/controls/{photo.js → image.js} +6 -7
  178. package/lib/forms/controls/{photo.test.js → image.test.js} +6 -21
  179. package/lib/forms/controls/multiple-files.js +10 -1
  180. package/lib/forms/controls/{multiple-photos.js → multiple-images.js} +3 -3
  181. package/lib/forms/controls/simple-input.js +13 -12
  182. package/lib/forms/controls/single-reference.js +4 -0
  183. package/lib/forms/controls/table.js +66 -24
  184. package/lib/forms/controls/textarea.js +4 -2
  185. package/lib/forms/controls/tickable.js +13 -5
  186. package/lib/forms/fields/boolean.js +14 -0
  187. package/lib/forms/fields/checkboxed-list.js +4 -0
  188. package/lib/forms/fields/collection-field.js +9 -0
  189. package/lib/forms/fields/color.js +10 -0
  190. package/lib/forms/fields/coordinates.js +49 -0
  191. package/lib/forms/fields/date.js +34 -0
  192. package/lib/forms/fields/datetime.js +46 -0
  193. package/lib/forms/fields/deep-reverse-single-reference-list.js +81 -0
  194. package/lib/forms/fields/deep-reverse-single-reference-table.js +122 -0
  195. package/lib/forms/fields/email.js +7 -0
  196. package/lib/forms/fields/enum-multiple-sealious.js +32 -0
  197. package/lib/forms/fields/field.js +32 -4
  198. package/lib/forms/fields/field.test.js +5 -5
  199. package/lib/forms/fields/fields.js +23 -8
  200. package/lib/forms/fields/file.js +33 -9
  201. package/lib/forms/fields/file.test.js +4 -16
  202. package/lib/forms/fields/float.js +10 -0
  203. package/lib/forms/fields/get-field-for-sealious.js +213 -0
  204. package/lib/forms/fields/html.js +14 -0
  205. package/lib/forms/fields/image.js +13 -0
  206. package/lib/forms/fields/int.js +23 -0
  207. package/lib/forms/fields/json.js +31 -0
  208. package/lib/forms/fields/markdown.js +14 -0
  209. package/lib/forms/fields/multiple-files.js +4 -0
  210. package/lib/forms/fields/number.js +6 -0
  211. package/lib/forms/fields/password.js +13 -0
  212. package/lib/forms/fields/pick-from-list-sealious.js +17 -0
  213. package/lib/forms/fields/pick-from-list.js +6 -2
  214. package/lib/forms/fields/proxy-field.js +3 -0
  215. package/lib/forms/fields/regexp.js +14 -0
  216. package/lib/forms/fields/simple-form-field.js +60 -2
  217. package/lib/forms/fields/single-reference.js +44 -0
  218. package/lib/forms/fields/structured-array.js +63 -12
  219. package/lib/forms/fields/table.js +28 -1
  220. package/lib/forms/fields/text.js +17 -0
  221. package/lib/forms/fields/url.js +17 -0
  222. package/lib/forms/form.js +30 -13
  223. package/lib/forms/form.test.js +3 -4
  224. package/lib/forms/multiform.js +4 -2
  225. package/lib/forms/multiform.test.js +3 -3
  226. package/lib/generate-stimulus.js +1 -0
  227. package/lib/index.js +1 -0
  228. package/lib/make-hidden-inputs.js +1 -1
  229. package/lib/mount.js +11 -0
  230. package/lib/notifier.js +0 -1
  231. package/lib/page/list.js +15 -8
  232. package/lib/page/mountable-with-fields.js +14 -6
  233. package/lib/page/mountable-with-fields.test.js +1 -1
  234. package/lib/templates/form.js +8 -9
  235. package/lib/templates/multiform.js +4 -4
  236. package/lib/templates/page.js +0 -2
  237. package/lib/templates/shared/collection-create-form.js +28 -40
  238. package/lib/templates/shared/collection-edit-form.js +40 -77
  239. package/lib/templates/shared/collection-list.js +12 -20
  240. package/lib/templates/shared/shared-crud-form-fields.js +27 -95
  241. package/lib/test_utils/test-on-real-app.js +3 -3
  242. package/lib/utils/extract-fields-from-collection.js +60 -20
  243. package/lib/utils/make-slug.js +1 -1
  244. package/lib/utils/wrap-attributes.js +20 -0
  245. package/package.json +4 -3
  246. package/src/add-crud.ts +86 -97
  247. package/src/add-list-route.test.ts +1 -1
  248. package/src/add-route.ts +0 -7
  249. package/src/build.ts +3 -1
  250. package/src/cli.ts +3 -5
  251. package/src/controllers/table-add-button.stimulus.ts +35 -0
  252. package/src/forms/controls/array.ts +23 -20
  253. package/src/forms/controls/autocomplete.ts +1 -1
  254. package/src/forms/controls/big-radio-group.ts +2 -2
  255. package/src/forms/controls/checkbox-group.ts +9 -9
  256. package/src/forms/controls/checkboxed-list-input.ts +27 -15
  257. package/src/forms/controls/controls.ts +2 -2
  258. package/src/forms/controls/derived-frame.ts +5 -1
  259. package/src/forms/controls/derived.ts +1 -1
  260. package/src/forms/controls/dropdown.ts +5 -2
  261. package/src/forms/controls/editable-collection-subset.ts +2 -2
  262. package/src/forms/controls/file.test.ts +4 -2
  263. package/src/forms/controls/file.ts +31 -5
  264. package/src/forms/controls/form-control.ts +7 -2
  265. package/src/forms/controls/hidden-input.ts +4 -1
  266. package/src/forms/controls/{photo.test.ts → image.test.ts} +5 -22
  267. package/src/forms/controls/{photo.ts → image.ts} +3 -5
  268. package/src/forms/controls/multiple-files.ts +18 -1
  269. package/src/forms/controls/{multiple-photos.ts → multiple-images.ts} +1 -1
  270. package/src/forms/controls/number.ts +1 -1
  271. package/src/forms/controls/simple-input.ts +18 -34
  272. package/src/forms/controls/single-reference.ts +10 -2
  273. package/src/forms/controls/table.ts +90 -31
  274. package/src/forms/controls/textarea.ts +6 -4
  275. package/src/forms/controls/tickable.ts +13 -5
  276. package/src/forms/fields/boolean.ts +23 -1
  277. package/src/forms/fields/checkboxed-list.ts +11 -2
  278. package/src/forms/fields/collection-field.ts +16 -1
  279. package/src/forms/fields/color.ts +9 -0
  280. package/src/forms/fields/coordinates.ts +66 -0
  281. package/src/forms/fields/date.ts +50 -0
  282. package/src/forms/fields/datetime.ts +66 -0
  283. package/src/forms/fields/deep-reverse-single-reference-list.ts +136 -0
  284. package/src/forms/fields/deep-reverse-single-reference-table.ts +213 -0
  285. package/src/forms/fields/email.ts +9 -0
  286. package/src/forms/fields/enum-multiple-sealious.ts +48 -0
  287. package/src/forms/fields/field.test.ts +5 -5
  288. package/src/forms/fields/field.ts +94 -8
  289. package/src/forms/fields/fields.ts +23 -8
  290. package/src/forms/fields/file.test.ts +4 -16
  291. package/src/forms/fields/file.ts +43 -15
  292. package/src/forms/fields/float.ts +9 -0
  293. package/src/forms/fields/get-field-for-sealious.ts +304 -0
  294. package/src/forms/fields/html.ts +15 -0
  295. package/src/forms/fields/image.ts +12 -0
  296. package/src/forms/fields/int.ts +25 -0
  297. package/src/forms/fields/json.ts +48 -0
  298. package/src/forms/fields/markdown.ts +15 -0
  299. package/src/forms/fields/multiple-files.ts +10 -1
  300. package/src/forms/fields/number.ts +8 -1
  301. package/src/forms/fields/password.ts +17 -0
  302. package/src/forms/fields/pick-from-list-sealious.ts +22 -0
  303. package/src/forms/fields/pick-from-list.ts +7 -3
  304. package/src/forms/fields/proxy-field.ts +6 -1
  305. package/src/forms/fields/regexp.ts +27 -1
  306. package/src/forms/fields/simple-form-field.ts +95 -7
  307. package/src/forms/fields/single-reference.ts +62 -0
  308. package/src/forms/fields/structured-array.ts +115 -18
  309. package/src/forms/fields/table.ts +32 -1
  310. package/src/forms/fields/text.ts +18 -0
  311. package/src/forms/fields/url.ts +20 -0
  312. package/src/forms/form-types.ts +2 -0
  313. package/src/forms/form.test.ts +3 -4
  314. package/src/forms/form.ts +37 -16
  315. package/src/forms/forms.css +0 -85
  316. package/src/forms/multiform.test.ts +3 -3
  317. package/src/forms/multiform.ts +2 -0
  318. package/src/generate-stimulus.ts +1 -0
  319. package/src/index.ts +2 -1
  320. package/src/make-hidden-inputs.ts +1 -1
  321. package/src/mount.ts +14 -0
  322. package/src/notifier.ts +0 -1
  323. package/src/page/list.ts +15 -8
  324. package/src/page/mountable-with-fields.test.ts +1 -1
  325. package/src/page/mountable-with-fields.ts +18 -9
  326. package/src/register-external.ts +1 -1
  327. package/src/templates/form.ts +8 -9
  328. package/src/templates/multiform.ts +4 -4
  329. package/src/templates/page.ts +0 -2
  330. package/src/templates/shared/collection-create-form.ts +30 -66
  331. package/src/templates/shared/collection-edit-form.ts +41 -102
  332. package/src/templates/shared/collection-list.ts +12 -20
  333. package/src/templates/shared/item-delete.ts +0 -1
  334. package/src/templates/shared/shared-crud-form-fields.ts +37 -171
  335. package/src/test_utils/test-on-real-app.ts +4 -3
  336. package/src/utils/extract-fields-from-collection.ts +76 -25
  337. package/src/utils/make-slug.ts +1 -1
  338. package/src/utils/wrap-attributes.ts +17 -0
  339. package/lib/add-verbose-crud.js +0 -119
  340. package/lib/crud-field-snippets.js +0 -52
  341. package/lib/find-css-includes.js +0 -3
  342. package/lib/forms/controls/reverse-single-reference.js +0 -1
  343. package/lib/forms/fields/enum-multiple.js +0 -47
  344. package/lib/forms/fields/enum-multiple.test.js +0 -27
  345. package/lib/forms/fields/enum.js +0 -41
  346. package/lib/forms/fields/enum.test.js +0 -21
  347. package/lib/forms/fields/reverse-single-reference.js +0 -17
  348. package/lib/templates/shared/handlers/boolean.js +0 -14
  349. package/lib/templates/shared/handlers/date.js +0 -8
  350. package/lib/templates/shared/handlers/deep-reverse-single-reference.js +0 -209
  351. package/lib/templates/shared/handlers/default.js +0 -49
  352. package/lib/templates/shared/handlers/enum.js +0 -19
  353. package/lib/templates/shared/handlers/file.js +0 -23
  354. package/lib/templates/shared/handlers/image.js +0 -32
  355. package/lib/templates/shared/handlers/int.js +0 -11
  356. package/lib/templates/shared/handlers/jdd.js +0 -10
  357. package/lib/templates/shared/handlers/single-reference.js +0 -12
  358. package/lib/templates/shared/handlers/structured-array.js +0 -33
  359. package/src/add-verbose-crud.ts +0 -138
  360. package/src/crud-field-snippets.ts +0 -57
  361. package/src/find-css-includes.ts +0 -3
  362. package/src/forms/controls/reverse-single-reference.ts +0 -50
  363. package/src/forms/fields/enum-multiple.test.ts +0 -31
  364. package/src/forms/fields/enum-multiple.ts +0 -62
  365. package/src/forms/fields/enum.test.ts +0 -25
  366. package/src/forms/fields/enum.ts +0 -56
  367. package/src/forms/fields/reverse-single-reference.ts +0 -20
  368. package/src/templates/shared/handlers/boolean.ts +0 -18
  369. package/src/templates/shared/handlers/date.ts +0 -12
  370. package/src/templates/shared/handlers/deep-reverse-single-reference.ts +0 -244
  371. package/src/templates/shared/handlers/default.ts +0 -64
  372. package/src/templates/shared/handlers/enum.ts +0 -23
  373. package/src/templates/shared/handlers/file.ts +0 -27
  374. package/src/templates/shared/handlers/image.ts +0 -45
  375. package/src/templates/shared/handlers/int.ts +0 -15
  376. package/src/templates/shared/handlers/jdd.ts +0 -14
  377. package/src/templates/shared/handlers/single-reference.ts +0 -16
  378. package/src/templates/shared/handlers/structured-array.ts +0 -42
@@ -22,9 +22,9 @@ export class EditableCollectionSubset extends FormFieldControl {
22
22
  }
23
23
 
24
24
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
25
- const { parsed: values } = await this.field.getValue(
25
+ const { parsed: values } = await this.field.getParsedValue(
26
26
  fctx.ctx,
27
- fctx.data
27
+ fctx.data.raw_values
28
28
  );
29
29
 
30
30
  return tempstream/* HTML */ `<div>
@@ -12,6 +12,7 @@ import _locreq from "locreq";
12
12
  import { module_dirname } from "../../utils/module-dirname.js";
13
13
  import { FileManager, PathFilePointer } from "@sealcode/file-manager";
14
14
  import assert from "node:assert";
15
+
15
16
  const locreq = _locreq(module_dirname(import.meta.url));
16
17
 
17
18
  describe("file-control", () => {
@@ -26,12 +27,12 @@ describe("file-control", () => {
26
27
 
27
28
  it("doesn't require image input if it already has old-value", async () => {
28
29
  const port = await getPort();
29
- console.log(`Using port ${port} for photo.test.ts`);
30
+ console.log(`Using port ${port} for image.test.ts`);
30
31
  const app = new Koa();
31
32
  const router = new Router();
32
33
 
33
34
  const fields = {
34
- file: new Fields.File(true, new FileManager("/tmp", "/uploaded_files")),
35
+ file: new Fields.File(true),
35
36
  };
36
37
 
37
38
  let submit_called = false;
@@ -63,6 +64,7 @@ describe("file-control", () => {
63
64
  })(),
64
65
  true
65
66
  );
67
+
66
68
  app.use(router.routes()).use(router.allowedMethods());
67
69
  server = app.listen(port);
68
70
 
@@ -1,13 +1,17 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
1
5
  import { Context } from "koa";
2
- import { FilePointer } from "@sealcode/file-manager";
6
+ import { FilePointer, PathFilePointer } from "@sealcode/file-manager";
3
7
  import { FlatTemplatable, tempstream } from "tempstream";
4
- import { FileContainer } from "../fields/file.js";
5
8
  import { FormDataValue } from "../form-types.js";
6
9
  import { FormControlContext } from "./form-control.js";
7
10
  import { SimpleInput } from "./simple-input.js";
8
11
  import { htmlEscape } from "escape-goat";
12
+ import { extname } from "node:path";
9
13
 
10
- export class File extends SimpleInput<FileContainer> {
14
+ export class File extends SimpleInput {
11
15
  getType() {
12
16
  return "file";
13
17
  }
@@ -21,6 +25,25 @@ export class File extends SimpleInput<FileContainer> {
21
25
  if (!file) {
22
26
  return empty_container;
23
27
  }
28
+ if (file instanceof PathFilePointer) {
29
+ const filename = await file.getOriginalFilename();
30
+ return tempstream/* HTML */ `<div class="${class_name}">
31
+ <div class="${class_name}__icon">
32
+ <picture>
33
+ <img
34
+ src="/dist/sealgen/file-icon.svg"
35
+ width="80"
36
+ height="80"
37
+ alt=""
38
+ />
39
+ <span class="${class_name}__icon__extension"
40
+ >${extname(filename)}</span
41
+ >
42
+ </picture>
43
+ <a href="${file.getURL()}" download>${filename}</a>
44
+ </div>
45
+ </div>`;
46
+ }
24
47
  return tempstream/* HTML */ `<div class="${class_name}">
25
48
  ${file.getOriginalFilename()}
26
49
  </div>`;
@@ -30,7 +53,7 @@ export class File extends SimpleInput<FileContainer> {
30
53
  fctx: FormControlContext,
31
54
  data: Record<string, FormDataValue>
32
55
  ) {
33
- let { parsed } = await this.field.getValue(fctx.ctx, data);
56
+ let { parsed } = await this.field.getParsedValue(fctx.ctx, data);
34
57
  if (parsed === null) {
35
58
  parsed = { old: null, new: null };
36
59
  }
@@ -52,7 +75,10 @@ export class File extends SimpleInput<FileContainer> {
52
75
  fctx: FormControlContext
53
76
  ): Promise<Record<string, string | boolean>> {
54
77
  const original = await super.getInputAttributes(fctx);
55
- const { parsed } = await this.field.getValue(fctx.ctx, fctx.data);
78
+ const { parsed } = await this.field.getParsedValue(
79
+ fctx.ctx,
80
+ fctx.data.raw_values
81
+ );
56
82
  return {
57
83
  ...original,
58
84
  name: this.field.name + "[new]",
@@ -1,13 +1,18 @@
1
1
  import Router from "@koa/router";
2
2
  import { Context } from "koa";
3
3
  import { FlatTemplatable } from "tempstream";
4
- import { FormDataValue, FormMessage } from "../form-types.js";
4
+ import { FormMessage, FormData } from "../form-types.js";
5
5
  import type { Form } from "../form.js";
6
6
 
7
+ export type FieldMessages<Fieldnames extends string = string> = Record<
8
+ Fieldnames,
9
+ { type: "error" | "info"; message: string }
10
+ >;
11
+
7
12
  export class FormControlContext {
8
13
  constructor(
9
14
  public ctx: Context,
10
- public data: Record<string, FormDataValue>,
15
+ public data: FormData,
11
16
  public messages: FormMessage[],
12
17
  public field_name_prefix: string,
13
18
  public form_id: string,
@@ -10,7 +10,10 @@ export class HiddenInput extends FormFieldControl {
10
10
  }
11
11
 
12
12
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
13
- const { raw } = await this.field.getValue(fctx.ctx, fctx.data);
13
+ const { raw } = await this.field.getParsedValue(
14
+ fctx.ctx,
15
+ fctx.data.raw_values
16
+ );
14
17
  return tempstream`<input type="hidden" name="${
15
18
  this.field.name
16
19
  }" value="${attribute((raw || "").toString())}" form="${
@@ -7,8 +7,7 @@ import { mount } from "../../mount.js";
7
7
  import { Form } from "../form.js";
8
8
  import { getBrowser } from "../../utils/browser-creator.js";
9
9
  import { Fields } from "../../index.js";
10
- import { Photo } from "./photo.js";
11
- import { KoaResponsiveImageRouter } from "koa-responsive-image-router";
10
+ import { Image } from "./image.js";
12
11
 
13
12
  import _locreq from "locreq";
14
13
  import { module_dirname } from "../../utils/module-dirname.js";
@@ -33,25 +32,17 @@ describe("photo-control", () => {
33
32
  const router = new Router();
34
33
 
35
34
  const fields = {
36
- photo: new Fields.File(false, new FileManager("/tmp", "/uploaded_files")),
35
+ photo: new Fields.File(false),
37
36
  };
38
37
 
39
38
  let received_photo: unknown;
40
39
 
41
- const imageRouter = new KoaResponsiveImageRouter({
42
- staticPath: "/images",
43
- thumbnailSize: 20,
44
- cacheManagerResolutionThreshold: 50,
45
- imageStoragePath: `/tmp/image-cache-${port}`,
46
- smartCropStoragePath: `/tmp/smartcrop-storage-${port}`,
47
- });
48
-
49
40
  mount(
50
41
  router,
51
42
  "/",
52
43
  new (class extends Form<typeof fields, unknown> {
53
44
  fields = fields;
54
- controls = [new Photo(fields.photo, imageRouter)];
45
+ controls = [new Image(fields.photo)];
55
46
 
56
47
  async onSubmit(ctx: Context) {
57
48
  const data = await this.getParsedValues(ctx);
@@ -92,19 +83,11 @@ describe("photo-control", () => {
92
83
  const router = new Router();
93
84
 
94
85
  const fields = {
95
- photo: new Fields.File(true, new FileManager("/tmp", "/uploaded_files")),
86
+ photo: new Fields.File(true),
96
87
  };
97
88
 
98
89
  let received_photo: unknown;
99
90
 
100
- const imageRouter = new KoaResponsiveImageRouter({
101
- staticPath: "/images",
102
- thumbnailSize: 20,
103
- cacheManagerResolutionThreshold: 50,
104
- imageStoragePath: `/tmp/image-cache-${port}`,
105
- smartCropStoragePath: `/tmp/smartcrop-storage-${port}`,
106
- });
107
-
108
91
  let submit_called = false;
109
92
 
110
93
  mount(
@@ -112,7 +95,7 @@ describe("photo-control", () => {
112
95
  "/",
113
96
  new (class extends Form<typeof fields, unknown> {
114
97
  fields = fields;
115
- controls = [new Photo(fields.photo, imageRouter)];
98
+ controls = [new Image(fields.photo)];
116
99
 
117
100
  async onSubmit(ctx: Context) {
118
101
  submit_called = true;
@@ -5,13 +5,11 @@ import { FormField } from "../fields/field.js";
5
5
  import { SimpleInputOptions } from "./simple-input.js";
6
6
  import { FormControlContext } from "./form-control.js";
7
7
  import { FileContainer } from "../fields/file.js";
8
- import { KoaResponsiveImageRouter } from "koa-responsive-image-router";
9
8
  import { FilePointer } from "@sealcode/file-manager";
10
9
 
11
- export class Photo extends File {
10
+ export class Image extends File {
12
11
  constructor(
13
12
  public field: FormField<boolean, FileContainer>,
14
- public imageRouter: KoaResponsiveImageRouter,
15
13
  options?: SimpleInputOptions
16
14
  ) {
17
15
  super(field, options);
@@ -22,7 +20,7 @@ export class Photo extends File {
22
20
  }
23
21
 
24
22
  async renderFilePreview(
25
- _ctx: Context,
23
+ ctx: Context,
26
24
  file: FilePointer | null
27
25
  ): Promise<FlatTemplatable> {
28
26
  const class_name = "file_preview";
@@ -31,7 +29,7 @@ export class Photo extends File {
31
29
  return empty_container;
32
30
  }
33
31
  return tempstream/* HTML */ `<div class="${class_name}">
34
- ${this.imageRouter.image(await file.getPath(), {
32
+ ${ctx.$app.imageRouter.image(await file.getPath(), {
35
33
  resolutions: [100, 200],
36
34
  sizesAttr: "100px",
37
35
  crop: { width: 100, height: 100 },
@@ -151,7 +151,7 @@ export class MultipleFiles extends FormFieldControl {
151
151
  if (!token) {
152
152
  return null;
153
153
  }
154
- return await item.collection.app.FileManager.fromToken(token);
154
+ return await item.collection.app.fileManager.fromToken(token);
155
155
  }
156
156
 
157
157
  async renderFileItemPreview(
@@ -255,4 +255,21 @@ export class MultipleFiles extends FormFieldControl {
255
255
  `
256
256
  );
257
257
  }
258
+
259
+ setLabel(label: string) {
260
+ this.options.label = label;
261
+ }
262
+
263
+ setUploadLabel(label: string) {
264
+ this.options.uploadLabel = label;
265
+ }
266
+
267
+ setAdditionalFieldsGetter(
268
+ fn: (
269
+ ctx: Context,
270
+ file: FilePointer
271
+ ) => Promise<Record<string, unknown>>
272
+ ) {
273
+ this.options.getAdditionalFields = fn;
274
+ }
258
275
  }
@@ -5,7 +5,7 @@ import { MultipleFiles, MultipleFilesOptions } from "./multiple-files.js";
5
5
  import { MultipleFiles as MultipleFilesField } from "../fields/multiple-files.js";
6
6
  import { KoaResponsiveImageRouter } from "koa-responsive-image-router";
7
7
 
8
- export class MultiplePhotos extends MultipleFiles {
8
+ export class MultipleImages extends MultipleFiles {
9
9
  constructor(
10
10
  public field: MultipleFilesField,
11
11
  public imageRouter: KoaResponsiveImageRouter,
@@ -4,7 +4,7 @@ import { SimpleInput, SimpleInputOptions } from "./simple-input.js";
4
4
 
5
5
  export type NumberOptions = SimpleInputOptions & Partial<{ step: number }>;
6
6
 
7
- export class Number extends SimpleInput<number | null> {
7
+ export class Number extends SimpleInput {
8
8
  field: NumberField;
9
9
  options: NumberOptions;
10
10
  constructor(field: NumberField, options: NumberOptions) {
@@ -12,36 +12,20 @@ export type SimpleInputOptions = {
12
12
  label?: string;
13
13
  autocomplete?: boolean;
14
14
  hide_errors?: boolean;
15
- type?:
16
- | "color"
17
- | "date"
18
- | "email"
19
- | "file"
20
- | "month"
21
- | "number"
22
- | "password"
23
- | "search"
24
- | "tel"
25
- | "text"
26
- | "time"
27
- | "url"
28
- | "week";
15
+ type?: string;
29
16
  value?: string;
30
17
  placeholder?: string;
31
18
  readonly?: boolean;
32
19
  step?: number;
33
20
  suffix?: string;
21
+ pattern?: string;
34
22
  };
35
23
 
36
24
  export class SimpleInput<
37
- ParsedValue,
38
25
  Options extends SimpleInputOptions = SimpleInputOptions
39
26
  > extends FormFieldControl {
40
27
  options: SimpleInputOptions;
41
- constructor(
42
- public field: FormField<boolean, ParsedValue>,
43
- options?: Options
44
- ) {
28
+ constructor(public field: FormField<boolean, any>, options?: Options) {
45
29
  super([field]);
46
30
  this.options = options || {};
47
31
  }
@@ -78,29 +62,23 @@ export class SimpleInput<
78
62
  ): Promise<Record<string, string | boolean>> {
79
63
  const readonly = this.options.readonly || false;
80
64
  const required = this.field.required;
81
- const { parsed, raw, valid } = await this.field.getValue(
65
+ const { raw, valid } = await this.field.getParsedValue(
82
66
  fctx.ctx,
83
- fctx.data,
67
+ fctx.data.raw_values,
84
68
  true
85
69
  );
86
70
  return Object.fromEntries([
87
71
  <const>["id", this.getID()],
88
72
  <const>["type", this.getType()],
89
73
  <const>["name", `${fctx.field_name_prefix}${this.field.name}`],
90
- <const>[
91
- "value",
92
- !valid && raw
93
- ? raw.toString()
94
- : parsed !== undefined && parsed !== null
95
- ? (parsed as string)?.toString() || ""
96
- : "",
97
- ],
74
+ <const>["value", raw],
98
75
  ...(fctx.form_id ? [["form", fctx.form_id]] : []),
99
76
  <const>["placeholder", this.getPlaceholder()],
100
77
  ...(readonly ? [["readonly", true]] : []),
101
78
  ...(required ? [["required", true]] : []),
102
79
  ...(!this.options.autocomplete ? [["autocomplete", "off"]] : []),
103
80
  ...(this.options.step ? [["step", this.options.step]] : []),
81
+ ["aria-invalid", String(!valid)],
104
82
  ]) as Record<string, string>;
105
83
  }
106
84
 
@@ -131,9 +109,9 @@ export class SimpleInput<
131
109
  const id = this.getID();
132
110
  const label = this.getLabel();
133
111
 
134
- const { valid, message } = await this.field.getValue(
112
+ const { valid, message } = await this.field.getParsedValue(
135
113
  fctx.ctx,
136
- fctx.data,
114
+ fctx.data.raw_values,
137
115
  true
138
116
  );
139
117
  return inputWrapper(
@@ -141,15 +119,21 @@ export class SimpleInput<
141
119
  this.getWrapperModifiers(),
142
120
  tempstream/* HTML */ `
143
121
  ${label ? `<label for="${id}">${label}</label>` : ""}
144
- ${await this.preInput(fctx, fctx.data)}
122
+ ${await this.preInput(fctx, fctx.data.raw_values)}
145
123
  ${this.renderInput(
146
124
  fctx.ctx,
147
125
  renderAttributes(await this.getInputAttributes(fctx)),
148
- fctx.data
126
+ fctx.data.raw_values
149
127
  )}
150
128
  ${this.postInput(fctx.ctx)}
151
129
  ${~valid && !this.options.hide_errors && fctx.validate
152
- ? `<div class="input__error">${message}</div>`
130
+ ? `<div class="input__error">${[
131
+ message,
132
+ fctx.data.field_messages?.[this.field.name]
133
+ ?.message,
134
+ ]
135
+ .filter((e) => !!e)
136
+ .join(" · ")}</div>`
153
137
  : ""}
154
138
  `
155
139
  );
@@ -1,10 +1,11 @@
1
- import SealiousSingleReference from "sealious/@types/app/base-chips/field-types/single-reference.js";
1
+ import { FieldTypes as SealiousFieldTypes } from "sealious";
2
2
  import { CollectionField } from "../fields/collection-field.js";
3
3
  import { DropdownOptions, FreeformDropdown } from "./dropdown.js";
4
4
  import { Collection, CollectionItem } from "sealious";
5
5
 
6
6
  export class SingleReferenceDropdown<
7
- F extends SealiousSingleReference,
7
+ TargetCollection extends Collection,
8
+ F extends SealiousFieldTypes.SingleReference,
8
9
  Required extends boolean
9
10
  > extends FreeformDropdown {
10
11
  constructor(
@@ -38,4 +39,11 @@ export class SingleReferenceDropdown<
38
39
  options
39
40
  );
40
41
  }
42
+
43
+ setOptionLabelGetter(
44
+ getter: (item: CollectionItem<TargetCollection>) => string
45
+ ): this {
46
+ this.options.getLabel = getter;
47
+ return this;
48
+ }
41
49
  }
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
4
5
  import Router from "@koa/router";
5
6
  import { hasShape, is, predicates } from "@sealcode/ts-predicates";
6
7
  import { Context } from "koa";
@@ -10,19 +11,14 @@ import { FormField } from "../fields/field.js";
10
11
  import {
11
12
  ExtractFormFieldParsed,
12
13
  Table as TableField,
13
- TableFieldParsed,
14
14
  } from "../fields/table.js";
15
15
  import { FormReaction } from "../form-types.js";
16
16
  import { Form } from "../form.js";
17
17
  import { FormControl, FormControlContext } from "./form-control.js";
18
18
 
19
19
  export type TableControlOptions<F extends Record<string, FormField>> = {
20
- render_fields: {
21
- [field_name in keyof F]: (
22
- fctx: FormControlContext,
23
- name: string,
24
- value: string
25
- ) => FlatTemplatable | Promise<FlatTemplatable>;
20
+ subfield_controls: {
21
+ [field_name in keyof F]: FormControl;
26
22
  };
27
23
  allow_removing?: boolean;
28
24
  label_add?: string;
@@ -38,7 +34,8 @@ export type TableControlOptions<F extends Record<string, FormField>> = {
38
34
  }
39
35
  );
40
36
 
41
- const TO_REPLACE = "____$$$$";
37
+ export const TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER =
38
+ "TABLE_ROW_INDEX________________";
42
39
 
43
40
  export class Table<F extends Record<string, FormField>> extends FormControl {
44
41
  role = <const>"input";
@@ -50,15 +47,41 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
50
47
  }
51
48
 
52
49
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
53
- let { parsed: rows } = await this.table_field.getValue(
54
- fctx.ctx,
55
- fctx.data
56
- );
50
+ let rows = fctx.data.raw_values?.[this.table_field.name] as any[];
57
51
  if (!rows) {
58
52
  rows = [];
59
53
  }
60
54
 
61
- const make_row = (
55
+ const get_row_fctx = (
56
+ row:
57
+ | {
58
+ [field_name in keyof F]: ExtractFormFieldParsed<
59
+ F[field_name]
60
+ >;
61
+ }
62
+ | null,
63
+ row_index: number | string
64
+ ) => {
65
+ return typeof row_index == "number"
66
+ ? {
67
+ data: {
68
+ raw_values:
69
+ (Object.fromEntries(
70
+ Object.entries(
71
+ row as Record<string, unknown>
72
+ ).map(([key, value]) => [
73
+ this.table_field.columns[key].name,
74
+ value,
75
+ ])
76
+ ) as any) || {},
77
+ messages: [],
78
+ field_messages: {},
79
+ },
80
+ }
81
+ : {};
82
+ };
83
+
84
+ const make_row = async (
62
85
  row:
63
86
  | {
64
87
  [field_name in keyof F]: ExtractFormFieldParsed<
@@ -69,14 +92,26 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
69
92
  row_index: number | string
70
93
  ) => {
71
94
  return /* HTML */ tempstream`<tr>
72
- ${Object.entries(this.table_field.columns).map(([key]) => {
73
- return tempstream`<td>${this.options.render_fields[key](
74
- fctx,
75
- `${this.table_field.name}[${row_index}][${key}]`,
76
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
- (row?.[key] as string) || ""
78
- )}</td>`;
79
- })}
95
+ ${(
96
+ await Promise.all(
97
+ Object.entries(this.table_field.columns).map(
98
+ ([column_name]) => {
99
+ const inner_fctx = get_row_fctx(
100
+ row,
101
+ row_index
102
+ );
103
+ return tempstream`<td>${this.options.subfield_controls[
104
+ column_name
105
+ ].render({ ...fctx, ...inner_fctx })}</td>`;
106
+ }
107
+ )
108
+ )
109
+ ).map((html) =>
110
+ html.replaceAll(
111
+ TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER,
112
+ row_index.toString()
113
+ )
114
+ )}
80
115
  ${
81
116
  this.options.allow_removing
82
117
  ? /* HTML */ `<td>
@@ -136,27 +171,25 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
136
171
  ${rows?.map((row, index) => make_row(row, index))}
137
172
  </tbody>
138
173
  </table>
139
- <template> ${make_row(null, TO_REPLACE)} </template>
174
+ <template>
175
+ ${make_row(null, TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER)}
176
+ </template>
140
177
 
141
178
  ${
142
179
  /* because of https://github.com/ljharb/qs/issues/252 we have to
143
180
  put the indexes in the field names */
144
181
  this.options.allow_adding
145
182
  ? /* HTML */ `<button
146
- onclick="const template = this.closest('turbo-frame').querySelector('template');
147
- const tbody = this.closest('turbo-frame').querySelector('tbody');
148
- const orig_template = template.innerHTML;
149
- template.innerHTML = orig_template.replaceAll('${TO_REPLACE}', tbody.querySelectorAll('tr').length);
150
- tbody.appendChild(template.cloneNode(true).content);
151
- template.innerHTML = orig_template;"
183
+ data-controller="table-add-button"
184
+ data-action="click->table-add-button#addRow"
185
+ data-table-placeholder="${TABLE_COLUMN_FIELD_INDEX_PLACEHOLDER}"
152
186
  >
153
- add
187
+ ${this.options.label_add || "add"}
154
188
  </button>
155
189
  <noscript>
156
190
  <input
157
191
  type="submit"
158
- value="${this.options.label_add ||
159
- "add"}"
192
+ value="add"
160
193
  form="${fctx.form_id}"
161
194
  formnovalidate
162
195
  formaction="${this.getActionURL(
@@ -249,6 +282,7 @@ template.innerHTML = orig_template;"
249
282
  {
250
283
  raw_values: await form.extractRawValues(ctx),
251
284
  messages: [],
285
+ field_messages: {},
252
286
  },
253
287
  false
254
288
  ),
@@ -256,4 +290,29 @@ template.innerHTML = orig_template;"
256
290
  await next();
257
291
  });
258
292
  }
293
+
294
+ setAllowRemoving(value: boolean) {
295
+ this.options.allow_removing = value;
296
+ return this;
297
+ }
298
+
299
+ setAllowAdding(value: boolean) {
300
+ this.options.allow_adding = value;
301
+ return this;
302
+ }
303
+
304
+ setLabel(value: string) {
305
+ this.options.label = value;
306
+ return this;
307
+ }
308
+
309
+ setRemoveLabel(value: string) {
310
+ this.options.label_remove = value;
311
+ return this;
312
+ }
313
+
314
+ setAddLabel(value: string) {
315
+ this.options.label_add = value;
316
+ return this;
317
+ }
259
318
  }
@@ -4,9 +4,9 @@ import { FormDataValue } from "../form-types.js";
4
4
  import { FormControlContext } from "./form-control.js";
5
5
  import { SimpleInput, SimpleInputOptions } from "./simple-input.js";
6
6
 
7
- export class Textarea extends SimpleInput<string | null> {
7
+ export class Textarea extends SimpleInput {
8
8
  constructor(
9
- public field: FormField<boolean, string | null>,
9
+ public field: FormField<boolean>,
10
10
  public options: SimpleInputOptions & {
11
11
  label?: string;
12
12
  rows?: number;
@@ -49,7 +49,9 @@ export class Textarea extends SimpleInput<string | null> {
49
49
  attributes: string,
50
50
  data: Record<string, FormDataValue>
51
51
  ) {
52
- const { parsed: value } = await this.field.getValue(ctx, data);
53
- return /* HTML */ `<textarea ${attributes}>${value || ""}</textarea>`;
52
+ const { parsed: value } = await this.field.getParsedValue(ctx, data);
53
+ return /* HTML */ `<textarea ${attributes}>
54
+ ${String(value) || ""}</textarea
55
+ >`;
54
56
  }
55
57
  }