@sealcode/sealgen 0.18.10 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/.xunit +74 -69
  2. package/@types/add-crud.d.sync-conflict-20250918-184311-TFDLWFG.ts +1 -0
  3. package/@types/builders/colors-builder.d.sync-conflict-20250918-184323-TFDLWFG.ts +10 -0
  4. package/@types/builders/colors-builder.d.ts +1 -1
  5. package/@types/builders/fonts-builder.d.sync-conflict-20250918-184322-TFDLWFG.ts +7 -0
  6. package/@types/builders/fonts-builder.d.ts +1 -1
  7. package/@types/controllers/table-add-button.d.ts +6 -0
  8. package/@types/controllers/table-add-button.stimulus.d.ts +6 -0
  9. package/@types/crud-field-snippets.d.sync-conflict-20250918-184323-TFDLWFG.ts +1 -0
  10. package/@types/crud-field-snippets.d.ts +1 -3
  11. package/@types/forms/controls/array.d.sync-conflict-20250918-184306-TFDLWFG.ts +33 -0
  12. package/@types/forms/controls/array.d.ts +1 -3
  13. package/@types/forms/controls/array.sync-conflict-20250918-222045-TFDLWFG.d.ts +33 -0
  14. package/@types/forms/controls/autocomplete.d.sync-conflict-20250918-222042-TFDLWFG.ts +22 -0
  15. package/@types/forms/controls/autocomplete.d.ts +1 -1
  16. package/@types/forms/controls/checkbox-group.d.sync-conflict-20250918-184319-TFDLWFG.ts +21 -0
  17. package/@types/forms/controls/checkbox-group.d.ts +3 -3
  18. package/@types/forms/controls/checkboxed-list-input.d.sync-conflict-20250918-184323-TFDLWFG.ts +21 -0
  19. package/@types/forms/controls/checkboxed-list-input.d.ts +3 -3
  20. package/@types/forms/controls/controls.d.ts +2 -2
  21. package/@types/forms/controls/derived-frame.d.sync-conflict-20250918-184314-TFDLWFG.ts +19 -0
  22. package/@types/forms/controls/derived-frame.d.ts +0 -2
  23. package/@types/forms/controls/dropdown.d.sync-conflict-20250918-222044-TFDLWFG.ts +30 -0
  24. package/@types/forms/controls/dropdown.d.ts +1 -1
  25. package/@types/forms/controls/file.d.sync-conflict-20250918-222045-TFDLWFG.ts +13 -0
  26. package/@types/forms/controls/file.d.ts +1 -2
  27. package/@types/forms/controls/form-control.d.sync-conflict-20250918-184322-TFDLWFG.ts +19 -0
  28. package/@types/forms/controls/form-control.d.sync-conflict-20250918-222046-TFDLWFG.ts +19 -0
  29. package/@types/forms/controls/form-control.d.ts +7 -4
  30. package/@types/forms/controls/image.d.ts +19 -0
  31. package/@types/forms/controls/image.test.d.ts +1 -0
  32. package/@types/forms/controls/multiple-files.d.sync-conflict-20250918-184318-TFDLWFG.ts +42 -0
  33. package/@types/forms/controls/multiple-files.d.ts +3 -1
  34. package/@types/forms/controls/multiple-images.d.ts +19 -0
  35. package/@types/forms/controls/number.d.sync-conflict-20250918-222046-TFDLWFG.ts +13 -0
  36. package/@types/forms/controls/number.d.ts +1 -1
  37. package/@types/forms/controls/photo.d.sync-conflict-20250918-184321-TFDLWFG.ts +19 -0
  38. package/@types/forms/controls/photo.d.ts +3 -5
  39. package/@types/forms/controls/simple-input.d.sync-conflict-20250918-184310-TFDLWFG.ts +33 -0
  40. package/@types/forms/controls/simple-input.d.sync-conflict-20250918-222042-TFDLWFG.ts +33 -0
  41. package/@types/forms/controls/simple-input.d.ts +5 -4
  42. package/@types/forms/controls/simple-input.sync-conflict-20250918-222045-TFDLWFG.d.ts +33 -0
  43. package/@types/forms/controls/single-reference.d.ts +3 -2
  44. package/@types/forms/controls/table.d.sync-conflict-20250918-184302-TFDLWFG.ts +39 -0
  45. package/@types/forms/controls/table.d.ts +8 -3
  46. package/@types/forms/controls/table.sync-conflict-20250918-222042-TFDLWFG.d.ts +39 -0
  47. package/@types/forms/controls/textarea.d.sync-conflict-20250918-222045-TFDLWFG.ts +25 -0
  48. package/@types/forms/controls/textarea.d.ts +3 -3
  49. package/@types/forms/fields/boolean.d.sync-conflict-20250918-184325-TFDLWFG.ts +22 -0
  50. package/@types/forms/fields/boolean.d.ts +6 -1
  51. package/@types/forms/fields/boolean.sync-conflict-20250918-222042-TFDLWFG.d.ts +22 -0
  52. package/@types/forms/fields/checkboxed-list.d.sync-conflict-20250918-184325-TFDLWFG.ts +23 -0
  53. package/@types/forms/fields/checkboxed-list.d.ts +3 -1
  54. package/@types/forms/fields/collection-field.d.sync-conflict-20250918-184319-TFDLWFG.ts +22 -0
  55. package/@types/forms/fields/collection-field.d.sync-conflict-20250918-222047-TFDLWFG.ts +22 -0
  56. package/@types/forms/fields/collection-field.d.ts +8 -1
  57. package/@types/forms/fields/color.d.ts +4 -0
  58. package/@types/forms/fields/coordinates.d.sync-conflict-20250918-222044-TFDLWFG.ts +14 -0
  59. package/@types/forms/fields/coordinates.d.ts +14 -0
  60. package/@types/forms/fields/date.d.ts +13 -0
  61. package/@types/forms/fields/datetime.d.sync-conflict-20250918-222050-TFDLWFG.ts +4 -0
  62. package/@types/forms/fields/datetime.d.ts +14 -0
  63. package/@types/forms/fields/deep-reverse-single-reference-list.d.ts +20 -0
  64. package/@types/forms/fields/deep-reverse-single-reference-table.d.ts +16 -0
  65. package/@types/forms/fields/email.d.sync-conflict-20250918-184303-TFDLWFG.ts +8 -0
  66. package/@types/forms/fields/email.d.ts +3 -0
  67. package/@types/forms/fields/enum-multiple-sealious.d.ts +16 -0
  68. package/@types/forms/fields/enum-multiple-sealious.sync-conflict-20250918-222047-TFDLWFG.d.ts +16 -0
  69. package/@types/forms/fields/field.d.sync-conflict-20250918-184306-TFDLWFG.ts +70 -0
  70. package/@types/forms/fields/field.d.ts +37 -5
  71. package/@types/forms/fields/fields.d.sync-conflict-20250918-184314-TFDLWFG.ts +29 -0
  72. package/@types/forms/fields/fields.d.ts +23 -8
  73. package/@types/forms/fields/file.d.sync-conflict-20250918-184310-TFDLWFG.ts +23 -0
  74. package/@types/forms/fields/file.d.sync-conflict-20250918-222046-TFDLWFG.ts +23 -0
  75. package/@types/forms/fields/file.d.ts +11 -5
  76. package/@types/forms/fields/file.sync-conflict-20250918-222049-TFDLWFG.d.ts +23 -0
  77. package/@types/forms/fields/float.d.ts +4 -0
  78. package/@types/forms/fields/get-field-for-sealious.d.sync-conflict-20250918-222046-TFDLWFG.ts +29 -0
  79. package/@types/forms/fields/get-field-for-sealious.d.ts +31 -0
  80. package/@types/forms/fields/get-field-for-sealious.sync-conflict-20250918-222042-TFDLWFG.d.ts +29 -0
  81. package/@types/forms/fields/html.d.ts +6 -0
  82. package/@types/forms/fields/image.d.sync-conflict-20250918-222042-TFDLWFG.ts +5 -0
  83. package/@types/forms/fields/image.d.ts +5 -0
  84. package/@types/forms/fields/int.d.ts +6 -0
  85. package/@types/forms/fields/json.d.ts +13 -0
  86. package/@types/forms/fields/markdown.d.ts +6 -0
  87. package/@types/forms/fields/multiple-files.d.sync-conflict-20250918-184304-TFDLWFG.ts +16 -0
  88. package/@types/forms/fields/multiple-files.d.ts +3 -1
  89. package/@types/forms/fields/number.d.sync-conflict-20250918-184317-TFDLWFG.ts +25 -0
  90. package/@types/forms/fields/number.d.sync-conflict-20250918-222047-TFDLWFG.ts +25 -0
  91. package/@types/forms/fields/number.d.ts +3 -1
  92. package/@types/forms/fields/password.d.ts +5 -0
  93. package/@types/forms/fields/pick-from-list-sealious.d.ts +11 -0
  94. package/@types/forms/fields/pick-from-list.d.sync-conflict-20250918-184322-TFDLWFG.ts +15 -0
  95. package/@types/forms/fields/pick-from-list.d.ts +4 -3
  96. package/@types/forms/fields/proxy-field.d.sync-conflict-20250918-184307-TFDLWFG.ts +15 -0
  97. package/@types/forms/fields/proxy-field.d.ts +3 -1
  98. package/@types/forms/fields/regexp.d.sync-conflict-20250918-184305-TFDLWFG.ts +21 -0
  99. package/@types/forms/fields/regexp.d.sync-conflict-20250918-222045-TFDLWFG.ts +21 -0
  100. package/@types/forms/fields/regexp.d.ts +6 -1
  101. package/@types/forms/fields/regexp.sync-conflict-20250918-222047-TFDLWFG.d.ts +21 -0
  102. package/@types/forms/fields/simple-form-field.d.sync-conflict-20250918-184309-TFDLWFG.ts +29 -0
  103. package/@types/forms/fields/simple-form-field.d.sync-conflict-20250918-222044-TFDLWFG.ts +29 -0
  104. package/@types/forms/fields/simple-form-field.d.ts +23 -3
  105. package/@types/forms/fields/simple-form-field.sync-conflict-20250918-222049-TFDLWFG.d.ts +29 -0
  106. package/@types/forms/fields/single-reference.d.ts +19 -0
  107. package/@types/forms/fields/single-reference.sync-conflict-20250918-222043-TFDLWFG.d.ts +10 -0
  108. package/@types/forms/fields/structured-array.d.sync-conflict-20250918-184302-TFDLWFG.ts +27 -0
  109. package/@types/forms/fields/structured-array.d.sync-conflict-20250918-222049-TFDLWFG.ts +27 -0
  110. package/@types/forms/fields/structured-array.d.ts +19 -10
  111. package/@types/forms/fields/structured-array.sync-conflict-20250918-222047-TFDLWFG.d.ts +27 -0
  112. package/@types/forms/fields/table.d.sync-conflict-20250918-184303-TFDLWFG.ts +16 -0
  113. package/@types/forms/fields/table.d.ts +3 -0
  114. package/@types/forms/fields/text.d.ts +6 -0
  115. package/@types/forms/fields/url.d.ts +6 -0
  116. package/@types/forms/form-types.d.sync-conflict-20250918-222045-TFDLWFG.ts +26 -0
  117. package/@types/forms/form-types.d.ts +2 -0
  118. package/@types/forms/form.d.sync-conflict-20250918-184315-TFDLWFG.ts +32 -0
  119. package/@types/forms/form.d.sync-conflict-20250918-222049-TFDLWFG.ts +32 -0
  120. package/@types/forms/form.d.ts +10 -8
  121. package/@types/forms/form.sync-conflict-20250918-222049-TFDLWFG.d.ts +33 -0
  122. package/@types/forms/form.test.d.sync-conflict-20250918-184318-TFDLWFG.ts +6 -0
  123. package/@types/forms/form.test.d.sync-conflict-20250918-222047-TFDLWFG.ts +6 -0
  124. package/@types/forms/form.test.d.ts +2 -2
  125. package/@types/forms/multiform.d.sync-conflict-20250918-184308-TFDLWFG.ts +23 -0
  126. package/@types/forms/multiform.d.ts +1 -2
  127. package/@types/forms/multiform.sync-conflict-20250918-222044-TFDLWFG.d.ts +23 -0
  128. package/@types/index.d.sync-conflict-20250918-184316-TFDLWFG.ts +46 -0
  129. package/@types/index.d.ts +2 -2
  130. package/@types/mount.d.sync-conflict-20250918-184320-TFDLWFG.ts +6 -0
  131. package/@types/mount.d.ts +0 -1
  132. package/@types/page/list.d.sync-conflict-20250918-184318-TFDLWFG.ts +48 -0
  133. package/@types/page/list.d.sync-conflict-20250918-222046-TFDLWFG.ts +48 -0
  134. package/@types/page/list.d.ts +2 -2
  135. package/@types/page/list.sync-conflict-20250918-222048-TFDLWFG.d.ts +48 -0
  136. package/@types/page/mountable-with-fields.d.sync-conflict-20250918-184321-TFDLWFG.ts +42 -0
  137. package/@types/page/mountable-with-fields.d.sync-conflict-20250918-222046-TFDLWFG.ts +42 -0
  138. package/@types/page/mountable-with-fields.d.ts +3 -3
  139. package/@types/page/mountable-with-fields.sync-conflict-20250918-222044-TFDLWFG.d.ts +42 -0
  140. package/@types/page/mountable.d.sync-conflict-20250918-184306-TFDLWFG.ts +10 -0
  141. package/@types/page/mountable.d.ts +0 -1
  142. package/@types/page/page.d.sync-conflict-20250918-184320-TFDLWFG.ts +18 -0
  143. package/@types/page/page.d.ts +0 -1
  144. package/@types/page/props-parser.d.sync-conflict-20250918-184305-TFDLWFG.ts +15 -0
  145. package/@types/page/props-parser.d.ts +0 -1
  146. package/@types/page/stateful-page.d.sync-conflict-20250918-184326-TFDLWFG.ts +62 -0
  147. package/@types/page/stateful-page.d.ts +0 -1
  148. package/@types/register-external.d.ts +2 -2
  149. package/@types/templates/form.d.ts +1 -3
  150. package/@types/templates/jdd-component.d.ts +1 -1
  151. package/@types/templates/page.d.ts +1 -1
  152. package/@types/templates/shared/shared-crud-form-fields.d.sync-conflict-20250918-184306-TFDLWFG.ts +1 -0
  153. package/@types/templates/shared/shared-crud-form-fields.d.ts +1 -68
  154. package/@types/templates/templates.d.ts +1 -21
  155. package/@types/test_utils/test-on-real-app.d.sync-conflict-20250918-184320-TFDLWFG.ts +26 -0
  156. package/@types/test_utils/test-on-real-app.d.ts +0 -1
  157. package/@types/utils/extract-fields-from-collection.d.sync-conflict-20250918-184312-TFDLWFG.ts +18 -0
  158. package/@types/utils/extract-fields-from-collection.d.ts +5 -1
  159. package/@types/utils/extract-fields-from-collection.sync-conflict-20250918-222045-TFDLWFG.d.ts +18 -0
  160. package/@types/utils/wrap-attributes.d.ts +3 -0
  161. package/README.md +5 -0
  162. package/assets/file-icon.svg +5 -0
  163. package/eslint.config.mjs +56 -0
  164. package/lib/add-crud.js +77 -89
  165. package/lib/add-jdd-component.js +3 -3
  166. package/lib/add-list-route.test.js +1 -1
  167. package/lib/add-route.js +2 -9
  168. package/lib/build.js +6 -4
  169. package/lib/builders/backend-ts-builder.js +0 -1
  170. package/lib/builders/colors-builder.js +11 -8
  171. package/lib/builders/fonts-builder.js +0 -1
  172. package/lib/builders/frontend-ts-builder.js +0 -1
  173. package/lib/cli.js +4 -7
  174. package/lib/components/tabs.js +36 -36
  175. package/lib/controllers/table-add-button.stimulus.js +35 -0
  176. package/lib/esbuild-plugins/rewrite-asset-imports.js +7 -10
  177. package/lib/forms/controls/array.js +25 -22
  178. package/lib/forms/controls/big-radio-group.js +2 -2
  179. package/lib/forms/controls/checkbox-group.js +7 -7
  180. package/lib/forms/controls/checkboxed-list-input.js +7 -1
  181. package/lib/forms/controls/checkboxed-list-input.test.js +6 -6
  182. package/lib/forms/controls/controls.js +2 -2
  183. package/lib/forms/controls/derived-frame.js +8 -4
  184. package/lib/forms/controls/derived.js +1 -1
  185. package/lib/forms/controls/dropdown.js +5 -3
  186. package/lib/forms/controls/editable-collection-subset.js +2 -2
  187. package/lib/forms/controls/file.js +26 -2
  188. package/lib/forms/controls/file.test.js +2 -2
  189. package/lib/forms/controls/form-control.js +1 -1
  190. package/lib/forms/controls/hidden-input.js +4 -1
  191. package/lib/forms/controls/{photo.js → image.js} +6 -7
  192. package/lib/forms/controls/{photo.test.js → image.test.js} +8 -23
  193. package/lib/forms/controls/multiple-files.js +10 -1
  194. package/lib/forms/controls/{multiple-photos.js → multiple-images.js} +3 -3
  195. package/lib/forms/controls/simple-input.js +13 -12
  196. package/lib/forms/controls/single-reference.js +4 -0
  197. package/lib/forms/controls/single-reference.test.js +1 -1
  198. package/lib/forms/controls/table.js +62 -25
  199. package/lib/forms/controls/textarea.js +5 -3
  200. package/lib/forms/controls/tickable.js +13 -5
  201. package/lib/forms/fields/boolean.js +14 -0
  202. package/lib/forms/fields/checkboxed-list.js +4 -0
  203. package/lib/forms/fields/collection-field.js +9 -0
  204. package/lib/forms/fields/color.js +10 -0
  205. package/lib/forms/fields/coordinates.js +49 -0
  206. package/lib/forms/fields/date.js +34 -0
  207. package/lib/forms/fields/datetime.js +46 -0
  208. package/lib/forms/fields/deep-reverse-single-reference-list.js +81 -0
  209. package/lib/forms/fields/deep-reverse-single-reference-table.js +122 -0
  210. package/lib/forms/fields/email.js +7 -0
  211. package/lib/forms/fields/enum-multiple-sealious.js +32 -0
  212. package/lib/forms/fields/field.js +32 -4
  213. package/lib/forms/fields/field.test.js +8 -8
  214. package/lib/forms/fields/fields.js +23 -8
  215. package/lib/forms/fields/file.js +33 -9
  216. package/lib/forms/fields/file.test.js +5 -17
  217. package/lib/forms/fields/float.js +10 -0
  218. package/lib/forms/fields/get-field-for-sealious.js +213 -0
  219. package/lib/forms/fields/html.js +14 -0
  220. package/lib/forms/fields/image.js +13 -0
  221. package/lib/forms/fields/int.js +23 -0
  222. package/lib/forms/fields/json.js +31 -0
  223. package/lib/forms/fields/markdown.js +14 -0
  224. package/lib/forms/fields/multiple-files.js +4 -0
  225. package/lib/forms/fields/number.js +6 -0
  226. package/lib/forms/fields/password.js +13 -0
  227. package/lib/forms/fields/pick-from-list-sealious.js +17 -0
  228. package/lib/forms/fields/pick-from-list.js +6 -2
  229. package/lib/forms/fields/proxy-field.js +3 -0
  230. package/lib/forms/fields/regexp.js +14 -0
  231. package/lib/forms/fields/simple-form-field.js +61 -2
  232. package/lib/forms/fields/single-reference.js +44 -0
  233. package/lib/forms/fields/structured-array.js +61 -12
  234. package/lib/forms/fields/table.js +29 -2
  235. package/lib/forms/fields/text.js +17 -0
  236. package/lib/forms/fields/url.js +17 -0
  237. package/lib/forms/form.js +30 -14
  238. package/lib/forms/form.test.js +5 -7
  239. package/lib/forms/multiform.js +10 -9
  240. package/lib/forms/multiform.test.js +5 -5
  241. package/lib/generate-collections.js +2 -3
  242. package/lib/generate-components.js +2 -3
  243. package/lib/generate-routes.js +3 -5
  244. package/lib/generate-routes.test.js +7 -16
  245. package/lib/generate-stimulus.js +4 -4
  246. package/lib/get-fonts.js +29 -27
  247. package/lib/index.js +1 -0
  248. package/lib/make-env.js +1 -1
  249. package/lib/make-hidden-inputs.js +4 -4
  250. package/lib/mount.js +11 -1
  251. package/lib/notifier.js +3 -4
  252. package/lib/page/list.js +20 -15
  253. package/lib/page/mountable-with-fields.js +14 -6
  254. package/lib/page/mountable-with-fields.test.js +1 -1
  255. package/lib/page/mountable.js +0 -1
  256. package/lib/page/page.js +0 -1
  257. package/lib/register-external.js +4 -4
  258. package/lib/templates/form.js +9 -10
  259. package/lib/templates/jdd-component.js +1 -1
  260. package/lib/templates/jdd-editor.js +1 -1
  261. package/lib/templates/multiform.js +4 -4
  262. package/lib/templates/page.js +1 -3
  263. package/lib/templates/shared/collection-create-form.js +31 -44
  264. package/lib/templates/shared/collection-edit-form.js +43 -81
  265. package/lib/templates/shared/collection-list.js +13 -21
  266. package/lib/templates/shared/shared-crud-form-fields.js +28 -98
  267. package/lib/test_utils/exec_async.js +3 -3
  268. package/lib/test_utils/test-on-real-app.js +12 -14
  269. package/lib/utils/extract-fields-from-collection.js +60 -20
  270. package/lib/utils/make-slug.js +1 -1
  271. package/lib/utils/wrap-attributes.js +20 -0
  272. package/lib/utils/write-file.js +1 -1
  273. package/package.json +11 -9
  274. package/src/add-collection.ts +1 -3
  275. package/src/add-crud.ts +84 -100
  276. package/src/add-jdd-component.ts +4 -5
  277. package/src/add-list-route.test.ts +1 -1
  278. package/src/add-route.ts +3 -13
  279. package/src/build.ts +6 -4
  280. package/src/builders/backend-ts-builder.ts +0 -1
  281. package/src/builders/builder.ts +4 -1
  282. package/src/builders/colors-builder.ts +11 -9
  283. package/src/builders/css-builder.ts +4 -1
  284. package/src/builders/fonts-builder.ts +0 -1
  285. package/src/builders/frontend-ts-builder.ts +0 -1
  286. package/src/cli.ts +5 -7
  287. package/src/components/tabs.ts +38 -37
  288. package/src/components/toggle-modal.ts +1 -1
  289. package/src/controllers/table-add-button.stimulus.ts +35 -0
  290. package/src/esbuild-plugins/rewrite-asset-imports.ts +7 -10
  291. package/src/forms/controls/array.ts +35 -36
  292. package/src/forms/controls/autocomplete.ts +2 -2
  293. package/src/forms/controls/big-radio-group.ts +4 -4
  294. package/src/forms/controls/checkbox-group.ts +10 -10
  295. package/src/forms/controls/checkboxed-list-input.test.ts +6 -6
  296. package/src/forms/controls/checkboxed-list-input.ts +16 -8
  297. package/src/forms/controls/controls.ts +2 -2
  298. package/src/forms/controls/derived-frame.ts +16 -11
  299. package/src/forms/controls/derived.ts +7 -6
  300. package/src/forms/controls/dropdown.ts +13 -13
  301. package/src/forms/controls/editable-collection-subset.ts +4 -3
  302. package/src/forms/controls/field-group.ts +1 -1
  303. package/src/forms/controls/file.test.ts +4 -2
  304. package/src/forms/controls/file.ts +30 -7
  305. package/src/forms/controls/form-control.ts +8 -3
  306. package/src/forms/controls/hidden-input.ts +5 -1
  307. package/src/forms/controls/html.ts +1 -0
  308. package/src/forms/controls/{photo.test.ts → image.test.ts} +7 -24
  309. package/src/forms/controls/{photo.ts → image.ts} +4 -6
  310. package/src/forms/controls/multiple-files.ts +23 -8
  311. package/src/forms/controls/{multiple-photos.ts → multiple-images.ts} +2 -2
  312. package/src/forms/controls/number.ts +1 -1
  313. package/src/forms/controls/simple-input.ts +21 -33
  314. package/src/forms/controls/single-reference.test.ts +1 -1
  315. package/src/forms/controls/single-reference.ts +11 -3
  316. package/src/forms/controls/table.ts +87 -33
  317. package/src/forms/controls/textarea.ts +7 -5
  318. package/src/forms/controls/tickable.ts +13 -7
  319. package/src/forms/fields/boolean.ts +27 -2
  320. package/src/forms/fields/checkboxed-list.ts +11 -2
  321. package/src/forms/fields/collection-field.ts +21 -3
  322. package/src/forms/fields/color.ts +9 -0
  323. package/src/forms/fields/coordinates.ts +66 -0
  324. package/src/forms/fields/date.ts +50 -0
  325. package/src/forms/fields/datetime.ts +66 -0
  326. package/src/forms/fields/deep-reverse-single-reference-list.ts +130 -0
  327. package/src/forms/fields/deep-reverse-single-reference-table.ts +213 -0
  328. package/src/forms/fields/email.ts +10 -1
  329. package/src/forms/fields/enum-multiple-sealious.ts +48 -0
  330. package/src/forms/fields/existing-sealious-value.ts +1 -1
  331. package/src/forms/fields/field.test.ts +8 -8
  332. package/src/forms/fields/field.ts +98 -11
  333. package/src/forms/fields/fields.ts +23 -8
  334. package/src/forms/fields/file.test.ts +5 -17
  335. package/src/forms/fields/file.ts +44 -16
  336. package/src/forms/fields/float.ts +9 -0
  337. package/src/forms/fields/get-field-for-sealious.ts +337 -0
  338. package/src/forms/fields/html.ts +15 -0
  339. package/src/forms/fields/image.ts +12 -0
  340. package/src/forms/fields/int.ts +25 -0
  341. package/src/forms/fields/json.ts +48 -0
  342. package/src/forms/fields/markdown.ts +15 -0
  343. package/src/forms/fields/multiple-files.ts +10 -1
  344. package/src/forms/fields/number.ts +10 -3
  345. package/src/forms/fields/password.ts +13 -0
  346. package/src/forms/fields/pick-from-list-sealious.ts +22 -0
  347. package/src/forms/fields/pick-from-list.ts +8 -4
  348. package/src/forms/fields/proxy-field.ts +7 -1
  349. package/src/forms/fields/regexp.ts +27 -1
  350. package/src/forms/fields/simple-form-field.ts +103 -7
  351. package/src/forms/fields/single-reference.ts +62 -0
  352. package/src/forms/fields/structured-array.ts +111 -18
  353. package/src/forms/fields/table.ts +39 -16
  354. package/src/forms/fields/text.ts +18 -0
  355. package/src/forms/fields/url.ts +20 -0
  356. package/src/forms/form-types.ts +5 -4
  357. package/src/forms/form.test.ts +5 -7
  358. package/src/forms/form.ts +42 -23
  359. package/src/forms/forms.css +0 -85
  360. package/src/forms/multiform.test.ts +9 -9
  361. package/src/forms/multiform.ts +12 -10
  362. package/src/generate-collections.ts +2 -3
  363. package/src/generate-components.ts +2 -3
  364. package/src/generate-routes.test.ts +7 -16
  365. package/src/generate-routes.ts +6 -10
  366. package/src/generate-stimulus.ts +4 -5
  367. package/src/get-fonts.ts +32 -30
  368. package/src/index.ts +2 -1
  369. package/src/make-env.ts +1 -1
  370. package/src/make-hidden-inputs.ts +8 -7
  371. package/src/mount.ts +16 -7
  372. package/src/notifier.ts +4 -7
  373. package/src/page/default-list-filters.ts +1 -0
  374. package/src/page/list.ts +33 -28
  375. package/src/page/mountable-with-fields.test.ts +1 -1
  376. package/src/page/mountable-with-fields.ts +21 -12
  377. package/src/page/mountable.ts +0 -1
  378. package/src/page/page.ts +2 -3
  379. package/src/page/props-parser.ts +1 -1
  380. package/src/page/sealious-list.ts +1 -1
  381. package/src/page/stateful-page.ts +6 -10
  382. package/src/register-external.ts +5 -5
  383. package/src/templates/form.ts +9 -11
  384. package/src/templates/jdd-component.ts +1 -1
  385. package/src/templates/jdd-editor.ts +2 -4
  386. package/src/templates/list.ts +0 -1
  387. package/src/templates/multiform.ts +4 -4
  388. package/src/templates/page.ts +1 -6
  389. package/src/templates/shared/collection-create-form.ts +34 -71
  390. package/src/templates/shared/collection-edit-form.ts +46 -108
  391. package/src/templates/shared/collection-list.ts +13 -21
  392. package/src/templates/shared/item-delete.ts +0 -1
  393. package/src/templates/shared/shared-crud-form-fields.ts +38 -177
  394. package/src/templates/templates.ts +5 -1
  395. package/src/test_utils/exec_async.ts +3 -4
  396. package/src/test_utils/test-on-real-app.ts +13 -14
  397. package/src/utils/browser-creator.js +0 -1
  398. package/src/utils/exec.ts +0 -1
  399. package/src/utils/extract-fields-from-collection.ts +76 -25
  400. package/src/utils/input-wrapper.ts +1 -1
  401. package/src/utils/make-slug.ts +1 -1
  402. package/src/utils/wrap-attributes.ts +17 -0
  403. package/src/utils/write-file.ts +1 -1
  404. package/.eslintrc.cjs +0 -39
  405. package/lib/add-verbose-crud.js +0 -119
  406. package/lib/crud-field-snippets.js +0 -52
  407. package/lib/find-css-includes.js +0 -3
  408. package/lib/forms/controls/reverse-single-reference.js +0 -1
  409. package/lib/forms/fields/enum-multiple.js +0 -47
  410. package/lib/forms/fields/enum-multiple.test.js +0 -27
  411. package/lib/forms/fields/enum.js +0 -41
  412. package/lib/forms/fields/enum.test.js +0 -21
  413. package/lib/forms/fields/reverse-single-reference.js +0 -17
  414. package/lib/templates/shared/handlers/boolean.js +0 -14
  415. package/lib/templates/shared/handlers/date.js +0 -8
  416. package/lib/templates/shared/handlers/deep-reverse-single-reference.js +0 -209
  417. package/lib/templates/shared/handlers/default.js +0 -49
  418. package/lib/templates/shared/handlers/enum.js +0 -19
  419. package/lib/templates/shared/handlers/file.js +0 -23
  420. package/lib/templates/shared/handlers/image.js +0 -32
  421. package/lib/templates/shared/handlers/int.js +0 -11
  422. package/lib/templates/shared/handlers/jdd.js +0 -10
  423. package/lib/templates/shared/handlers/single-reference.js +0 -12
  424. package/lib/templates/shared/handlers/structured-array.js +0 -33
  425. package/src/add-verbose-crud.ts +0 -138
  426. package/src/crud-field-snippets.ts +0 -57
  427. package/src/find-css-includes.ts +0 -3
  428. package/src/forms/controls/reverse-single-reference.ts +0 -50
  429. package/src/forms/fields/enum-multiple.test.ts +0 -31
  430. package/src/forms/fields/enum-multiple.ts +0 -62
  431. package/src/forms/fields/enum.test.ts +0 -25
  432. package/src/forms/fields/enum.ts +0 -56
  433. package/src/forms/fields/reverse-single-reference.ts +0 -20
  434. package/src/templates/shared/handlers/boolean.ts +0 -18
  435. package/src/templates/shared/handlers/date.ts +0 -12
  436. package/src/templates/shared/handlers/deep-reverse-single-reference.ts +0 -244
  437. package/src/templates/shared/handlers/default.ts +0 -64
  438. package/src/templates/shared/handlers/enum.ts +0 -23
  439. package/src/templates/shared/handlers/file.ts +0 -27
  440. package/src/templates/shared/handlers/image.ts +0 -45
  441. package/src/templates/shared/handlers/int.ts +0 -15
  442. package/src/templates/shared/handlers/jdd.ts +0 -14
  443. package/src/templates/shared/handlers/single-reference.ts +0 -16
  444. package/src/templates/shared/handlers/structured-array.ts +0 -42
@@ -25,15 +25,16 @@ export class DerivedFrame extends Derived<{
25
25
  ) {
26
26
  super(
27
27
  fields,
28
- async (values, consts) => /* HTML */ `<turbo-frame
29
- id="${consts.frame_id}"
30
- src="./${consts.frame_id}?${Object.entries({
31
- ...consts,
32
- ...(values || {}),
33
- })
34
- .map(([key, value]) => `${key}=${value}`)
35
- .join("&")}"
36
- ></turbo-frame>`,
28
+ async (values, consts) =>
29
+ /* HTML */ `<turbo-frame
30
+ id="${consts.frame_id}"
31
+ src="./${consts.frame_id}?${Object.entries({
32
+ ...consts,
33
+ ...(values || {}),
34
+ })
35
+ .map(([key, value]) => `${key}=${value}`)
36
+ .join("&")}"
37
+ ></turbo-frame>`,
37
38
  async (fctx) => ({
38
39
  form_id: fctx.form_id,
39
40
  field_name_prefix: fctx.field_name_prefix,
@@ -44,7 +45,7 @@ export class DerivedFrame extends Derived<{
44
45
  }
45
46
 
46
47
  renderBackendResponseFrame(_ctx: Context, content: FlatTemplatable) {
47
- return tempstream/* HTML */ `<turbo-frame
48
+ return tempstream /* HTML */ `<turbo-frame
48
49
  id="${this.frame_id}"
49
50
  target="_top"
50
51
  >
@@ -76,7 +77,11 @@ export class DerivedFrame extends Derived<{
76
77
  await this.generateFrameContent(
77
78
  new FormControlContext(
78
79
  ctx,
79
- ctx.query,
80
+ {
81
+ messages: [],
82
+ field_messages: {},
83
+ raw_values: ctx.query,
84
+ },
80
85
  [],
81
86
  query.field_name_prefix,
82
87
  query.form_id,
@@ -1,3 +1,4 @@
1
+ /* eslint @typescript-eslint/no-redundant-type-constituents: off */
1
2
  import { randomUUID } from "crypto";
2
3
  import { FlatTemplatable, tempstream } from "tempstream";
3
4
  import { FormField } from "../fields/field.js";
@@ -5,7 +6,7 @@ import { FormDataValue } from "../form-types.js";
5
6
  import { FormControl, FormControlContext } from "./form-control.js";
6
7
 
7
8
  export class Derived<
8
- Consts extends Record<string, unknown | undefined>
9
+ Consts extends Record<string, unknown | undefined>,
9
10
  > extends FormControl {
10
11
  role = <const>"decoration";
11
12
 
@@ -17,7 +18,7 @@ export class Derived<
17
18
  ) => Promise<FlatTemplatable>,
18
19
  public getConsts: (
19
20
  fctx: FormControlContext
20
- ) => Promise<Consts> = async () => ({} as Consts)
21
+ ) => Promise<Consts> = async () => ({}) as Consts
21
22
  ) {
22
23
  super();
23
24
  }
@@ -27,7 +28,7 @@ export class Derived<
27
28
  ): Promise<Record<string, FormDataValue>> {
28
29
  const fieldnames = this.fields.map((f) => f.name);
29
30
  const result = Object.fromEntries(
30
- Object.entries(fctx.data).filter(([key]) =>
31
+ Object.entries(fctx.data.raw_values).filter(([key]) =>
31
32
  fieldnames.includes(key)
32
33
  )
33
34
  );
@@ -44,7 +45,7 @@ export class Derived<
44
45
  `render(${values_code}, consts)
45
46
  .then(result=>document.querySelector("#${id}").innerHTML=result)`;
46
47
 
47
- return tempstream/* HTML */ `<div>
48
+ return tempstream /* HTML */ `<div>
48
49
  <script>
49
50
  (function () {
50
51
  const consts = ${"{" +
@@ -54,8 +55,8 @@ export class Derived<
54
55
  typeof value === "object"
55
56
  ? JSON.stringify(value)
56
57
  : typeof value == "string"
57
- ? '"' + value.toString() + '"'
58
- : (value as string).toString()
58
+ ? '"' + value.toString() + '"'
59
+ : (value as string).toString()
59
60
  }`;
60
61
  })
61
62
  .join(",") +
@@ -15,7 +15,7 @@ export type DropdownOptions = {
15
15
 
16
16
  export type DropdownOption = { value: string; label: string };
17
17
 
18
- export class FreeformDropdown extends SimpleInput<string | null> {
18
+ export class FreeformDropdown extends SimpleInput {
19
19
  constructor(
20
20
  public field: FormField<boolean, string | null>,
21
21
  public _generateOptions: (ctx: Context) => Promise<DropdownOption[]>,
@@ -46,7 +46,7 @@ export class FreeformDropdown extends SimpleInput<string | null> {
46
46
  ((original.onchange as string | undefined) ||
47
47
  "") + ";this.form.submit();",
48
48
  ],
49
- ]
49
+ ]
50
50
  : []),
51
51
  ]),
52
52
  } as Record<string, string>;
@@ -57,19 +57,19 @@ export class FreeformDropdown extends SimpleInput<string | null> {
57
57
  attributes_str: string,
58
58
  data: Record<string, FormDataValue>
59
59
  ): Promise<FlatTemplatable> {
60
- const { parsed: picked_value } = await this.field.getValue(ctx, data);
60
+ const { parsed: picked_value } = await this.field.getParsedValue(
61
+ ctx,
62
+ data
63
+ );
61
64
  return /* HTML */ tempstream`<select ${attributes_str}>
62
- ${
63
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
64
- Promise.resolve(this.generateOptions(ctx)).then((options) =>
65
- options.map(
66
- ({ value, label }) =>
67
- `<option value="${attribute(value)}" ${
68
- (value || "") == picked_value ? "selected" : ""
69
- }>${label}</option>`
70
- )
65
+ ${Promise.resolve(this.generateOptions(ctx)).then((options) =>
66
+ options.map(
67
+ ({ value, label }) =>
68
+ `<option value="${attribute(value)}" ${
69
+ (value || "") == picked_value ? "selected" : ""
70
+ }>${label}</option>`
71
71
  )
72
- }
72
+ )}
73
73
  </select>`;
74
74
  }
75
75
  }
@@ -1,3 +1,4 @@
1
+ /* eslint @typescript-eslint/no-base-to-string: off */
1
2
  import { FlatTemplatable, tempstream } from "tempstream";
2
3
  import { attribute } from "../../sanitize.js";
3
4
  import { PickFromListField } from "../fields/pick-from-list.js";
@@ -22,12 +23,12 @@ export class EditableCollectionSubset extends FormFieldControl {
22
23
  }
23
24
 
24
25
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
25
- const { parsed: values } = await this.field.getValue(
26
+ const { parsed: values } = await this.field.getParsedValue(
26
27
  fctx.ctx,
27
- fctx.data
28
+ fctx.data.raw_values
28
29
  );
29
30
 
30
- return tempstream/* HTML */ `<div>
31
+ return tempstream /* HTML */ `<div>
31
32
  <ul>
32
33
  ${Promise.resolve(this.field.generateOptions(fctx.ctx)).then(
33
34
  (options) =>
@@ -41,7 +41,7 @@ export class FieldGroup extends FormFieldControl {
41
41
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
42
42
  const { label, classes } = this.options;
43
43
 
44
- return tempstream/* HTML */ `
44
+ return tempstream /* HTML */ `
45
45
  <div
46
46
  class="field-group ${(classes || []).join(" ")}"
47
47
  ${this.options.containerID
@@ -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.info(`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,14 @@
1
+ /* eslint @typescript-eslint/no-unsafe-assignment: off, @typescript-eslint/no-unsafe-argument: off, @typescript-eslint/no-unsafe-member-access: off, @typescript-eslint/no-unsafe-call: off */
1
2
  import { Context } from "koa";
2
- import { FilePointer } from "@sealcode/file-manager";
3
+ import { FilePointer, PathFilePointer } from "@sealcode/file-manager";
3
4
  import { FlatTemplatable, tempstream } from "tempstream";
4
- import { FileContainer } from "../fields/file.js";
5
5
  import { FormDataValue } from "../form-types.js";
6
6
  import { FormControlContext } from "./form-control.js";
7
7
  import { SimpleInput } from "./simple-input.js";
8
8
  import { htmlEscape } from "escape-goat";
9
+ import { extname } from "node:path";
9
10
 
10
- export class File extends SimpleInput<FileContainer> {
11
+ export class File extends SimpleInput {
11
12
  getType() {
12
13
  return "file";
13
14
  }
@@ -21,7 +22,26 @@ export class File extends SimpleInput<FileContainer> {
21
22
  if (!file) {
22
23
  return empty_container;
23
24
  }
24
- return tempstream/* HTML */ `<div class="${class_name}">
25
+ if (file instanceof PathFilePointer) {
26
+ const filename = await file.getOriginalFilename();
27
+ return tempstream /* HTML */ `<div class="${class_name}">
28
+ <div class="${class_name}__icon">
29
+ <picture>
30
+ <img
31
+ src="/dist/sealgen/file-icon.svg"
32
+ width="80"
33
+ height="80"
34
+ alt=""
35
+ />
36
+ <span class="${class_name}__icon__extension"
37
+ >${extname(filename)}</span
38
+ >
39
+ </picture>
40
+ <a href="${file.getURL()}" download>${filename}</a>
41
+ </div>
42
+ </div>`;
43
+ }
44
+ return tempstream /* HTML */ `<div class="${class_name}">
25
45
  ${file.getOriginalFilename()}
26
46
  </div>`;
27
47
  }
@@ -30,11 +50,11 @@ export class File extends SimpleInput<FileContainer> {
30
50
  fctx: FormControlContext,
31
51
  data: Record<string, FormDataValue>
32
52
  ) {
33
- let { parsed } = await this.field.getValue(fctx.ctx, data);
53
+ let { parsed } = await this.field.getParsedValue(fctx.ctx, data);
34
54
  if (parsed === null) {
35
55
  parsed = { old: null, new: null };
36
56
  }
37
- return tempstream/* HTML */ `${this.renderFilePreview(
57
+ return tempstream /* HTML */ `${this.renderFilePreview(
38
58
  fctx.ctx,
39
59
  parsed.old
40
60
  )}
@@ -52,7 +72,10 @@ export class File extends SimpleInput<FileContainer> {
52
72
  fctx: FormControlContext
53
73
  ): Promise<Record<string, string | boolean>> {
54
74
  const original = await super.getInputAttributes(fctx);
55
- const { parsed } = await this.field.getValue(fctx.ctx, fctx.data);
75
+ const { parsed } = await this.field.getParsedValue(
76
+ fctx.ctx,
77
+ fctx.data.raw_values
78
+ );
56
79
  return {
57
80
  ...original,
58
81
  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,
@@ -21,6 +26,6 @@ export abstract class FormControl {
21
26
  ): FlatTemplatable | Promise<FlatTemplatable>;
22
27
  abstract role: "input" | "decoration" | "messages" | "submit";
23
28
 
24
- // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
30
  mount(_router: Router, _form: Form<any, any>) {}
26
31
  }
@@ -1,3 +1,4 @@
1
+ /* eslint @typescript-eslint/no-base-to-string: off */
1
2
  import { FlatTemplatable, tempstream } from "tempstream";
2
3
  import { attribute } from "../../sanitize.js";
3
4
  import { FormField } from "../fields/field.js";
@@ -10,7 +11,10 @@ export class HiddenInput extends FormFieldControl {
10
11
  }
11
12
 
12
13
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
13
- const { raw } = await this.field.getValue(fctx.ctx, fctx.data);
14
+ const { raw } = await this.field.getParsedValue(
15
+ fctx.ctx,
16
+ fctx.data.raw_values
17
+ );
14
18
  return tempstream`<input type="hidden" name="${
15
19
  this.field.name
16
20
  }" value="${attribute((raw || "").toString())}" form="${
@@ -12,6 +12,7 @@ export class HTML extends FormControl {
12
12
  constructor(role: "decoration" | "submit", renderer: Renderer);
13
13
  constructor(renderer: Renderer);
14
14
  constructor(
15
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
15
16
  role_or_renderer: "decoration" | "submit" | Renderer,
16
17
  renderer_or_undefined?: Renderer | FlatTemplatable
17
18
  ) {
@@ -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";
@@ -28,30 +27,22 @@ describe("photo-control", () => {
28
27
 
29
28
  it("sends the old photo if it wasn't changed and it has an initial value", async () => {
30
29
  const port = await getPort();
31
- console.log(`Using port ${port} for photo.test.ts`);
30
+ console.info(`Using port ${port} for photo.test.ts`);
32
31
  const app = new Koa();
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);
@@ -87,24 +78,16 @@ describe("photo-control", () => {
87
78
 
88
79
  it("doesn't require image input if it already has old-value", async () => {
89
80
  const port = await getPort();
90
- console.log(`Using port ${port} for photo.test.ts`);
81
+ console.info(`Using port ${port} for photo.test.ts`);
91
82
  const app = new Koa();
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";
@@ -30,8 +28,8 @@ export class Photo extends File {
30
28
  if (!file) {
31
29
  return empty_container;
32
30
  }
33
- return tempstream/* HTML */ `<div class="${class_name}">
34
- ${this.imageRouter.image(await file.getPath(), {
31
+ return tempstream /* HTML */ `<div class="${class_name}">
32
+ ${ctx.$app.imageRouter.image(await file.getPath(), {
35
33
  resolutions: [100, 200],
36
34
  sizesAttr: "100px",
37
35
  crop: { width: 100, height: 100 },
@@ -105,7 +105,7 @@ export class MultipleFiles extends FormFieldControl {
105
105
  }
106
106
 
107
107
  renderFrame(_ctx: Context, content?: FlatTemplatable) {
108
- return tempstream/* HTML */ `<turbo-frame
108
+ return tempstream /* HTML */ `<turbo-frame
109
109
  ${content ? "" : `src="./${this.getFrameRelativeURL()}"`}
110
110
  id="${this.getFrameID()}"
111
111
  target="_top"
@@ -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(
@@ -168,7 +168,7 @@ export class MultipleFiles extends FormFieldControl {
168
168
  if (!(file instanceof PathFilePointer)) {
169
169
  return "";
170
170
  }
171
- return tempstream/* HTML */ `<li class="file-list-item">
171
+ return tempstream /* HTML */ `<li class="file-list-item">
172
172
  <a
173
173
  class="file-list-item__preview"
174
174
  href="${file.getURL()}"
@@ -215,9 +215,7 @@ export class MultipleFiles extends FormFieldControl {
215
215
  async renderFrameContent(ctx: Context): Promise<FlatTemplatable> {
216
216
  const files = (
217
217
  await Promise.all(
218
- (
219
- await this.getFileItems(ctx)
220
- )
218
+ (await this.getFileItems(ctx))
221
219
  .filter((item) => item) // filter out undefineds
222
220
  .map(async (item: CollectionItem<Collection>) => [
223
221
  item,
@@ -226,13 +224,13 @@ export class MultipleFiles extends FormFieldControl {
226
224
  )
227
225
  ).filter(([_, f]) => f !== null) as [
228
226
  CollectionItem<Collection>,
229
- FilePointer
227
+ FilePointer,
230
228
  ][];
231
229
 
232
230
  return inputWrapper(
233
231
  "multiple-files",
234
232
  ["multiple-files", this.field.name, ...this.getClassModifiers()],
235
- tempstream/* HTML */ `
233
+ tempstream /* HTML */ `
236
234
  <label>${this.options.label || this.field.name}</label>
237
235
  <ul class="multiple-files__list">
238
236
  ${files.map(([item, file]) =>
@@ -255,4 +253,21 @@ export class MultipleFiles extends FormFieldControl {
255
253
  `
256
254
  );
257
255
  }
256
+
257
+ setLabel(label: string) {
258
+ this.options.label = label;
259
+ }
260
+
261
+ setUploadLabel(label: string) {
262
+ this.options.uploadLabel = label;
263
+ }
264
+
265
+ setAdditionalFieldsGetter(
266
+ fn: (
267
+ ctx: Context,
268
+ file: FilePointer
269
+ ) => Promise<Record<string, unknown>>
270
+ ) {
271
+ this.options.getAdditionalFields = fn;
272
+ }
258
273
  }
@@ -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,
@@ -33,7 +33,7 @@ export class MultiplePhotos extends MultipleFiles {
33
33
  if (!path) {
34
34
  return "missing file";
35
35
  }
36
- return tempstream/* HTML */ `<li>
36
+ return tempstream /* HTML */ `<li>
37
37
  <a href="${file.getURL()}" data-turbo="false">
38
38
  ${this.imageRouter.image(path, {
39
39
  resolutions: [150, 300],
@@ -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,34 +12,22 @@ 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
- Options extends SimpleInputOptions = SimpleInputOptions
25
+ Options extends SimpleInputOptions = SimpleInputOptions,
39
26
  > extends FormFieldControl {
40
27
  options: SimpleInputOptions;
41
28
  constructor(
42
- public field: FormField<boolean, ParsedValue>,
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ public field: FormField<boolean, any>,
43
31
  options?: Options
44
32
  ) {
45
33
  super([field]);
@@ -78,29 +66,23 @@ export class SimpleInput<
78
66
  ): Promise<Record<string, string | boolean>> {
79
67
  const readonly = this.options.readonly || false;
80
68
  const required = this.field.required;
81
- const { parsed, raw, valid } = await this.field.getValue(
69
+ const { raw, valid } = await this.field.getParsedValue(
82
70
  fctx.ctx,
83
- fctx.data,
71
+ fctx.data.raw_values,
84
72
  true
85
73
  );
86
74
  return Object.fromEntries([
87
75
  <const>["id", this.getID()],
88
76
  <const>["type", this.getType()],
89
77
  <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
- ],
78
+ <const>["value", raw],
98
79
  ...(fctx.form_id ? [["form", fctx.form_id]] : []),
99
80
  <const>["placeholder", this.getPlaceholder()],
100
81
  ...(readonly ? [["readonly", true]] : []),
101
82
  ...(required ? [["required", true]] : []),
102
83
  ...(!this.options.autocomplete ? [["autocomplete", "off"]] : []),
103
84
  ...(this.options.step ? [["step", this.options.step]] : []),
85
+ ["aria-invalid", String(!valid)],
104
86
  ]) as Record<string, string>;
105
87
  }
106
88
 
@@ -131,25 +113,31 @@ export class SimpleInput<
131
113
  const id = this.getID();
132
114
  const label = this.getLabel();
133
115
 
134
- const { valid, message } = await this.field.getValue(
116
+ const { valid, message } = await this.field.getParsedValue(
135
117
  fctx.ctx,
136
- fctx.data,
118
+ fctx.data.raw_values,
137
119
  true
138
120
  );
139
121
  return inputWrapper(
140
122
  "simple-input",
141
123
  this.getWrapperModifiers(),
142
- tempstream/* HTML */ `
124
+ tempstream /* HTML */ `
143
125
  ${label ? `<label for="${id}">${label}</label>` : ""}
144
- ${await this.preInput(fctx, fctx.data)}
126
+ ${await this.preInput(fctx, fctx.data.raw_values)}
145
127
  ${this.renderInput(
146
128
  fctx.ctx,
147
129
  renderAttributes(await this.getInputAttributes(fctx)),
148
- fctx.data
130
+ fctx.data.raw_values
149
131
  )}
150
132
  ${this.postInput(fctx.ctx)}
151
133
  ${~valid && !this.options.hide_errors && fctx.validate
152
- ? `<div class="input__error">${message}</div>`
134
+ ? `<div class="input__error">${[
135
+ message,
136
+ fctx.data.field_messages?.[this.field.name]
137
+ ?.message,
138
+ ]
139
+ .filter((e) => !!e)
140
+ .join(" · ")}</div>`
153
141
  : ""}
154
142
  `
155
143
  );