@webiny/app-admin 0.0.0-unstable.78f581c1d2 → 0.0.0-unstable.7be00a75a9

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 (1580) hide show
  1. package/README.md +7 -17
  2. package/assets/icons/add-18px.js +20 -0
  3. package/assets/icons/add-18px.js.map +1 -0
  4. package/assets/icons/add-18px.svg +1 -1
  5. package/assets/icons/arrow_drop_down-24px.js +20 -0
  6. package/assets/icons/arrow_drop_down-24px.js.map +1 -0
  7. package/assets/icons/arrow_drop_down-24px.svg +1 -1
  8. package/assets/icons/attach_file_black_24dp.js +20 -0
  9. package/assets/icons/attach_file_black_24dp.js.map +1 -0
  10. package/assets/icons/baseline-menu-24px.js +20 -0
  11. package/assets/icons/baseline-menu-24px.js.map +1 -0
  12. package/assets/icons/baseline-notification_important-24px.js +19 -0
  13. package/assets/icons/baseline-notification_important-24px.js.map +1 -0
  14. package/assets/icons/baseline-security-24px.js +19 -0
  15. package/assets/icons/baseline-security-24px.js.map +1 -0
  16. package/assets/icons/file_download.js +19 -0
  17. package/assets/icons/file_download.js.map +1 -0
  18. package/assets/icons/file_download.svg +1 -0
  19. package/assets/icons/file_upload.js +19 -0
  20. package/assets/icons/file_upload.js.map +1 -0
  21. package/assets/icons/file_upload.svg +1 -0
  22. package/assets/icons/filter-24px.js +23 -0
  23. package/assets/icons/filter-24px.js.map +1 -0
  24. package/assets/icons/filter-24px.svg +1 -1
  25. package/assets/icons/github-brands.js +19 -0
  26. package/assets/icons/github-brands.js.map +1 -0
  27. package/assets/icons/highlight-24px.js +19 -0
  28. package/assets/icons/highlight-24px.js.map +1 -0
  29. package/assets/icons/highlight-24px.svg +1 -1
  30. package/assets/icons/icon-community.js +18 -0
  31. package/assets/icons/icon-community.js.map +1 -0
  32. package/assets/icons/icon-documentation.js +18 -0
  33. package/assets/icons/icon-documentation.js.map +1 -0
  34. package/assets/icons/info.js +19 -0
  35. package/assets/icons/info.js.map +1 -0
  36. package/assets/icons/info.svg +1 -1
  37. package/assets/icons/insert_drive_file-24px.js +19 -0
  38. package/assets/icons/insert_drive_file-24px.js.map +1 -0
  39. package/assets/icons/insert_drive_file-24px.svg +1 -1
  40. package/assets/icons/insert_photo-24px.js +19 -0
  41. package/assets/icons/insert_photo-24px.js.map +1 -0
  42. package/assets/icons/insert_photo-24px.svg +1 -1
  43. package/assets/icons/label-24px.js +19 -0
  44. package/assets/icons/label-24px.js.map +1 -0
  45. package/assets/icons/label-24px.svg +1 -1
  46. package/assets/icons/round-account_circle-24px.js +19 -0
  47. package/assets/icons/round-account_circle-24px.js.map +1 -0
  48. package/assets/icons/round-account_circle-24px.svg +1 -1
  49. package/assets/icons/round-add-24px.js +20 -0
  50. package/assets/icons/round-add-24px.js.map +1 -0
  51. package/assets/icons/round-arrow_drop_down-24px.js +19 -0
  52. package/assets/icons/round-arrow_drop_down-24px.js.map +1 -0
  53. package/assets/icons/round-arrow_drop_down-24px.svg +1 -1
  54. package/assets/icons/round-chevron_right-24px.js +20 -0
  55. package/assets/icons/round-chevron_right-24px.js.map +1 -0
  56. package/assets/icons/round-feedback-24px.js +20 -0
  57. package/assets/icons/round-feedback-24px.js.map +1 -0
  58. package/assets/icons/round-help-24px.js +19 -0
  59. package/assets/icons/round-help-24px.js.map +1 -0
  60. package/assets/icons/round-help-24px.svg +1 -1
  61. package/assets/icons/round-invert_colors-24px.js +20 -0
  62. package/assets/icons/round-invert_colors-24px.js.map +1 -0
  63. package/assets/icons/round-keyboard_arrow_down-24px.js +17 -0
  64. package/assets/icons/round-keyboard_arrow_down-24px.js.map +1 -0
  65. package/assets/icons/round-keyboard_arrow_up-24px.js +17 -0
  66. package/assets/icons/round-keyboard_arrow_up-24px.js.map +1 -0
  67. package/assets/icons/round-lock_open-24px.js +22 -0
  68. package/assets/icons/round-lock_open-24px.js.map +1 -0
  69. package/assets/icons/round-more_vert-24px.js +20 -0
  70. package/assets/icons/round-more_vert-24px.js.map +1 -0
  71. package/assets/icons/round-open_in_new-24px.js +20 -0
  72. package/assets/icons/round-open_in_new-24px.js.map +1 -0
  73. package/assets/icons/round-settings-24px.js +20 -0
  74. package/assets/icons/round-settings-24px.js.map +1 -0
  75. package/assets/icons/round-settings-24px.svg +1 -1
  76. package/assets/icons/search-24px.js +21 -0
  77. package/assets/icons/search-24px.js.map +1 -0
  78. package/assets/icons/slack-logo.js +14 -0
  79. package/assets/icons/slack-logo.js.map +1 -0
  80. package/assets/icons/today-24px.js +19 -0
  81. package/assets/icons/today-24px.js.map +1 -0
  82. package/assets/icons/today-24px.svg +1 -1
  83. package/assets/icons/touch_app.js +20 -0
  84. package/assets/icons/touch_app.js.map +1 -0
  85. package/assets/icons/touch_app.svg +1 -1
  86. package/assets/images/webiny-logo.js +34 -0
  87. package/assets/images/webiny-logo.js.map +1 -0
  88. package/assets/images/webiny-orange-logo.js +34 -0
  89. package/assets/images/webiny-orange-logo.js.map +1 -0
  90. package/base/Admin.d.ts +5 -2
  91. package/base/Admin.js +57 -53
  92. package/base/Admin.js.map +1 -1
  93. package/base/Base/DefaultFieldRenderers.d.ts +2 -0
  94. package/base/Base/DefaultFieldRenderers.js +102 -0
  95. package/base/Base/DefaultFieldRenderers.js.map +1 -0
  96. package/base/Base/DefaultLayoutRenderers.d.ts +2 -0
  97. package/base/Base/DefaultLayoutRenderers.js +14 -0
  98. package/base/Base/DefaultLayoutRenderers.js.map +1 -0
  99. package/base/Base/FieldRenderers/CheckboxesRenderer.d.ts +13 -0
  100. package/base/Base/FieldRenderers/CheckboxesRenderer.js +27 -0
  101. package/base/Base/FieldRenderers/CheckboxesRenderer.js.map +1 -0
  102. package/base/Base/FieldRenderers/CodeEditorRenderer.d.ts +15 -0
  103. package/base/Base/FieldRenderers/CodeEditorRenderer.js +14 -0
  104. package/base/Base/FieldRenderers/CodeEditorRenderer.js.map +1 -0
  105. package/base/Base/FieldRenderers/DateTimeInputsRenderer.d.ts +17 -0
  106. package/base/Base/FieldRenderers/DateTimeInputsRenderer.js +66 -0
  107. package/base/Base/FieldRenderers/DateTimeInputsRenderer.js.map +1 -0
  108. package/base/Base/FieldRenderers/DateTimeRenderer.d.ts +21 -0
  109. package/base/Base/FieldRenderers/DateTimeRenderer.js +45 -0
  110. package/base/Base/FieldRenderers/DateTimeRenderer.js.map +1 -0
  111. package/base/Base/FieldRenderers/FilePickerRenderer.d.ts +13 -0
  112. package/base/Base/FieldRenderers/FilePickerRenderer.js +44 -0
  113. package/base/Base/FieldRenderers/FilePickerRenderer.js.map +1 -0
  114. package/base/Base/FieldRenderers/FileUrlPickerRenderer.d.ts +12 -0
  115. package/base/Base/FieldRenderers/FileUrlPickerRenderer.js +20 -0
  116. package/base/Base/FieldRenderers/FileUrlPickerRenderer.js.map +1 -0
  117. package/base/Base/FieldRenderers/HiddenRenderer.d.ts +12 -0
  118. package/base/Base/FieldRenderers/HiddenRenderer.js +5 -0
  119. package/base/Base/FieldRenderers/HiddenRenderer.js.map +1 -0
  120. package/base/Base/FieldRenderers/HorizontalTabsRenderer.d.ts +5 -0
  121. package/base/Base/FieldRenderers/HorizontalTabsRenderer.js +26 -0
  122. package/base/Base/FieldRenderers/HorizontalTabsRenderer.js.map +1 -0
  123. package/base/Base/FieldRenderers/InputRenderer.d.ts +12 -0
  124. package/base/Base/FieldRenderers/InputRenderer.js +19 -0
  125. package/base/Base/FieldRenderers/InputRenderer.js.map +1 -0
  126. package/base/Base/FieldRenderers/LexicalRenderer.d.ts +12 -0
  127. package/base/Base/FieldRenderers/LexicalRenderer.js +48 -0
  128. package/base/Base/FieldRenderers/LexicalRenderer.js.map +1 -0
  129. package/base/Base/FieldRenderers/MultiFilePickerRenderer.d.ts +13 -0
  130. package/base/Base/FieldRenderers/MultiFilePickerRenderer.js +70 -0
  131. package/base/Base/FieldRenderers/MultiFilePickerRenderer.js.map +1 -0
  132. package/base/Base/FieldRenderers/NumberInputRenderer.d.ts +12 -0
  133. package/base/Base/FieldRenderers/NumberInputRenderer.js +20 -0
  134. package/base/Base/FieldRenderers/NumberInputRenderer.js.map +1 -0
  135. package/base/Base/FieldRenderers/NumberInputsRenderer.d.ts +14 -0
  136. package/base/Base/FieldRenderers/NumberInputsRenderer.js +50 -0
  137. package/base/Base/FieldRenderers/NumberInputsRenderer.js.map +1 -0
  138. package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.d.ts +14 -0
  139. package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js +17 -0
  140. package/base/Base/FieldRenderers/ObjectRenderer/DynamicZoneRenderer.js.map +1 -0
  141. package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.d.ts +14 -0
  142. package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js +59 -0
  143. package/base/Base/FieldRenderers/ObjectRenderer/KeyValueTagsRenderer.js.map +1 -0
  144. package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.d.ts +10 -0
  145. package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.js +97 -0
  146. package/base/Base/FieldRenderers/ObjectRenderer/MultiValueDynamicZone.js.map +1 -0
  147. package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.d.ts +17 -0
  148. package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js +48 -0
  149. package/base/Base/FieldRenderers/ObjectRenderer/ObjectAccordionMultipleRenderer.js.map +1 -0
  150. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.d.ts +28 -0
  151. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js +69 -0
  152. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js.map +1 -0
  153. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.d.ts +14 -0
  154. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js +17 -0
  155. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js.map +1 -0
  156. package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.d.ts +10 -0
  157. package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.js +58 -0
  158. package/base/Base/FieldRenderers/ObjectRenderer/SingleValueDynamicZone.js.map +1 -0
  159. package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.d.ts +10 -0
  160. package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.js +68 -0
  161. package/base/Base/FieldRenderers/ObjectRenderer/TemplatePicker.js.map +1 -0
  162. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.d.ts +4 -0
  163. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js +19 -0
  164. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js.map +1 -0
  165. package/base/Base/FieldRenderers/PassthroughRenderer.d.ts +12 -0
  166. package/base/Base/FieldRenderers/PassthroughRenderer.js +12 -0
  167. package/base/Base/FieldRenderers/PassthroughRenderer.js.map +1 -0
  168. package/base/Base/FieldRenderers/RadioButtonsRenderer.d.ts +13 -0
  169. package/base/Base/FieldRenderers/RadioButtonsRenderer.js +26 -0
  170. package/base/Base/FieldRenderers/RadioButtonsRenderer.js.map +1 -0
  171. package/base/Base/FieldRenderers/SelectRenderer.d.ts +13 -0
  172. package/base/Base/FieldRenderers/SelectRenderer.js +28 -0
  173. package/base/Base/FieldRenderers/SelectRenderer.js.map +1 -0
  174. package/base/Base/FieldRenderers/SwitchRenderer.d.ts +12 -0
  175. package/base/Base/FieldRenderers/SwitchRenderer.js +16 -0
  176. package/base/Base/FieldRenderers/SwitchRenderer.js.map +1 -0
  177. package/base/Base/FieldRenderers/TagsRenderer.d.ts +12 -0
  178. package/base/Base/FieldRenderers/TagsRenderer.js +20 -0
  179. package/base/Base/FieldRenderers/TagsRenderer.js.map +1 -0
  180. package/base/Base/FieldRenderers/TextInputsRenderer.d.ts +14 -0
  181. package/base/Base/FieldRenderers/TextInputsRenderer.js +49 -0
  182. package/base/Base/FieldRenderers/TextInputsRenderer.js.map +1 -0
  183. package/base/Base/FieldRenderers/TextareaRenderer.d.ts +14 -0
  184. package/base/Base/FieldRenderers/TextareaRenderer.js +20 -0
  185. package/base/Base/FieldRenderers/TextareaRenderer.js.map +1 -0
  186. package/base/Base/FieldRenderers/TextareasRenderer.d.ts +14 -0
  187. package/base/Base/FieldRenderers/TextareasRenderer.js +52 -0
  188. package/base/Base/FieldRenderers/TextareasRenderer.js.map +1 -0
  189. package/base/Base/FieldRenderers/VerticalTabsRenderer.d.ts +5 -0
  190. package/base/Base/FieldRenderers/VerticalTabsRenderer.js +40 -0
  191. package/base/Base/FieldRenderers/VerticalTabsRenderer.js.map +1 -0
  192. package/base/Base/LexicalPreset.d.ts +2 -0
  193. package/base/Base/LexicalPreset.js +63 -0
  194. package/base/Base/LexicalPreset.js.map +1 -0
  195. package/base/Base/Menus/SupportMenuItems.d.ts +2 -0
  196. package/base/Base/Menus/SupportMenuItems.js +14 -0
  197. package/base/Base/Menus/SupportMenuItems.js.map +1 -0
  198. package/base/Base/Menus/WebinyVersion.d.ts +2 -0
  199. package/base/Base/Menus/WebinyVersion.js +23 -0
  200. package/base/Base/Menus/WebinyVersion.js.map +1 -0
  201. package/base/Base/Menus.d.ts +2 -0
  202. package/base/Base/Menus.js +58 -0
  203. package/base/Base/Menus.js.map +1 -0
  204. package/base/Base/RoutesConfig.d.ts +2 -0
  205. package/base/Base/RoutesConfig.js +28 -0
  206. package/base/Base/RoutesConfig.js.map +1 -0
  207. package/base/Base/Tenant/wby-horizontal.js +33 -0
  208. package/base/Base/Tenant/wby-horizontal.js.map +1 -0
  209. package/base/Base/Tenant/wby-horizontal.svg +23 -0
  210. package/base/Base/Tenant/wby-square.js +20 -0
  211. package/base/Base/Tenant/wby-square.js.map +1 -0
  212. package/base/Base/Tenant/wby-square.svg +3 -0
  213. package/base/Base/Tenant.d.ts +2 -0
  214. package/base/Base/Tenant.js +22 -0
  215. package/base/Base/Tenant.js.map +1 -0
  216. package/base/Base/UserMenu/ExitTenant.d.ts +20 -0
  217. package/base/Base/UserMenu/ExitTenant.js +26 -0
  218. package/base/Base/UserMenu/ExitTenant.js.map +1 -0
  219. package/base/Base/UserMenu/SignOut.d.ts +2 -0
  220. package/base/Base/UserMenu/SignOut.js +18 -0
  221. package/base/Base/UserMenu/SignOut.js.map +1 -0
  222. package/base/Base/UserMenu/UserInfo.d.ts +6 -0
  223. package/base/Base/UserMenu/UserInfo.js +30 -0
  224. package/base/Base/UserMenu/UserInfo.js.map +1 -0
  225. package/base/Base/UserMenu.d.ts +2 -0
  226. package/base/Base/UserMenu.js +20 -0
  227. package/base/Base/UserMenu.js.map +1 -0
  228. package/base/Base.d.ts +1 -1
  229. package/base/Base.js +14 -110
  230. package/base/Base.js.map +1 -1
  231. package/base/TelemetryAdminAppStart.d.ts +1 -0
  232. package/base/TelemetryAdminAppStart.js +16 -0
  233. package/base/TelemetryAdminAppStart.js.map +1 -0
  234. package/base/WebinyVersion.d.ts +2 -0
  235. package/base/WebinyVersion.js +23 -0
  236. package/base/WebinyVersion.js.map +1 -0
  237. package/base/createRootContainer.d.ts +2 -0
  238. package/base/createRootContainer.js +63 -0
  239. package/base/createRootContainer.js.map +1 -0
  240. package/base/plugins/AddGraphQLQuerySelection.d.ts +2 -3
  241. package/base/plugins/AddGraphQLQuerySelection.js +17 -29
  242. package/base/plugins/AddGraphQLQuerySelection.js.map +1 -1
  243. package/base/providers/AdminUiStateProvider.d.ts +1 -0
  244. package/base/providers/AdminUiStateProvider.js +9 -0
  245. package/base/providers/AdminUiStateProvider.js.map +1 -0
  246. package/base/providers/ApolloProvider.d.ts +5 -2
  247. package/base/providers/ApolloProvider.js +10 -26
  248. package/base/providers/ApolloProvider.js.map +1 -1
  249. package/base/providers/UiProviders.d.ts +1 -0
  250. package/base/providers/UiProviders.js +29 -0
  251. package/base/providers/UiProviders.js.map +1 -0
  252. package/base/providers/UiStateProvider.d.ts +1 -2
  253. package/base/providers/UiStateProvider.js +9 -23
  254. package/base/providers/UiStateProvider.js.map +1 -1
  255. package/base/ui/Brand.d.ts +39 -2
  256. package/base/ui/Brand.js +6 -18
  257. package/base/ui/Brand.js.map +1 -1
  258. package/base/ui/CenteredView.d.ts +21 -1
  259. package/base/ui/CenteredView.js +11 -40
  260. package/base/ui/CenteredView.js.map +1 -1
  261. package/base/ui/Dashboard.d.ts +39 -2
  262. package/base/ui/Dashboard.js +6 -18
  263. package/base/ui/Dashboard.js.map +1 -1
  264. package/base/ui/FileManager.d.ts +75 -0
  265. package/base/ui/FileManager.js +47 -0
  266. package/base/ui/FileManager.js.map +1 -0
  267. package/base/ui/Layout.d.ts +40 -2
  268. package/base/ui/Layout.js +6 -23
  269. package/base/ui/Layout.js.map +1 -1
  270. package/base/ui/LoginScreen.d.ts +40 -2
  271. package/base/ui/LoginScreen.js +19 -25
  272. package/base/ui/LoginScreen.js.map +1 -1
  273. package/base/ui/Logo.d.ts +38 -7
  274. package/base/ui/Logo.js +6 -35
  275. package/base/ui/Logo.js.map +1 -1
  276. package/base/ui/Navigation.d.ts +39 -23
  277. package/base/ui/Navigation.js +13 -181
  278. package/base/ui/Navigation.js.map +1 -1
  279. package/base/ui/NotFound.d.ts +39 -2
  280. package/base/ui/NotFound.js +6 -18
  281. package/base/ui/NotFound.js.map +1 -1
  282. package/base/ui/Tags.d.ts +2 -2
  283. package/base/ui/Tags.js +13 -26
  284. package/base/ui/Tags.js.map +1 -1
  285. package/base/ui/TenantSelector.d.ts +39 -0
  286. package/base/ui/TenantSelector.js +7 -0
  287. package/base/ui/TenantSelector.js.map +1 -0
  288. package/base/ui/UserMenu/UserMenu.d.ts +39 -0
  289. package/base/ui/UserMenu/UserMenu.js +7 -0
  290. package/base/ui/UserMenu/UserMenu.js.map +1 -0
  291. package/base/ui/UserMenu/UserMenuHandle.d.ts +39 -0
  292. package/base/ui/UserMenu/UserMenuHandle.js +7 -0
  293. package/base/ui/UserMenu/UserMenuHandle.js.map +1 -0
  294. package/base/ui/UserMenu/UserMenuItem.d.ts +81 -0
  295. package/base/ui/UserMenu/UserMenuItem.js +9 -0
  296. package/base/ui/UserMenu/UserMenuItem.js.map +1 -0
  297. package/base/ui/UserMenu/UserMenuLink.d.ts +81 -0
  298. package/base/ui/UserMenu/UserMenuLink.js +9 -0
  299. package/base/ui/UserMenu/UserMenuLink.js.map +1 -0
  300. package/base/ui/UserMenu/UserMenuSeparator.d.ts +42 -0
  301. package/base/ui/UserMenu/UserMenuSeparator.js +7 -0
  302. package/base/ui/UserMenu/UserMenuSeparator.js.map +1 -0
  303. package/base/ui/UserMenu.d.ts +79 -29
  304. package/base/ui/UserMenu.js +12 -137
  305. package/base/ui/UserMenu.js.map +1 -1
  306. package/components/AdminLayout.d.ts +2 -1
  307. package/components/AdminLayout.js +8 -22
  308. package/components/AdminLayout.js.map +1 -1
  309. package/components/BulkActions/Worker.d.ts +82 -0
  310. package/components/BulkActions/Worker.js +63 -0
  311. package/components/BulkActions/Worker.js.map +1 -0
  312. package/components/BulkActions/index.d.ts +2 -0
  313. package/components/BulkActions/index.js +2 -0
  314. package/components/BulkActions/useDialogWithReport/DialogMessage.d.ts +5 -0
  315. package/components/BulkActions/useDialogWithReport/DialogMessage.js +19 -0
  316. package/components/BulkActions/useDialogWithReport/DialogMessage.js.map +1 -0
  317. package/components/BulkActions/useDialogWithReport/index.d.ts +1 -0
  318. package/components/BulkActions/useDialogWithReport/index.js +1 -0
  319. package/components/BulkActions/useDialogWithReport/useDialogWithReport.d.ts +19 -0
  320. package/components/BulkActions/useDialogWithReport/useDialogWithReport.js +40 -0
  321. package/components/BulkActions/useDialogWithReport/useDialogWithReport.js.map +1 -0
  322. package/components/Buttons/Buttons.d.ts +24 -0
  323. package/components/Buttons/Buttons.js +50 -0
  324. package/components/Buttons/Buttons.js.map +1 -0
  325. package/components/Buttons/Buttons.styles.d.ts +4 -0
  326. package/components/Buttons/Buttons.styles.js +11 -0
  327. package/components/Buttons/Buttons.styles.js.map +1 -0
  328. package/components/Buttons/index.d.ts +2 -0
  329. package/components/Buttons/index.js +2 -0
  330. package/components/Buttons/useButtons.d.ts +14 -0
  331. package/components/Buttons/useButtons.js +19 -0
  332. package/components/Buttons/useButtons.js.map +1 -0
  333. package/components/DeveloperMode/DeveloperMode.d.ts +11 -0
  334. package/components/DeveloperMode/DeveloperMode.js +8 -0
  335. package/components/DeveloperMode/DeveloperMode.js.map +1 -0
  336. package/components/DeveloperMode/index.d.ts +2 -0
  337. package/components/DeveloperMode/index.js +1 -0
  338. package/components/Dialogs/Dialog.d.ts +22 -0
  339. package/components/Dialogs/Dialog.js +50 -0
  340. package/components/Dialogs/Dialog.js.map +1 -0
  341. package/components/Dialogs/DialogParamsContext.d.ts +6 -0
  342. package/components/Dialogs/DialogParamsContext.js +10 -0
  343. package/components/Dialogs/DialogParamsContext.js.map +1 -0
  344. package/components/Dialogs/DialogsContext.d.ts +41 -0
  345. package/components/Dialogs/DialogsContext.js +142 -0
  346. package/components/Dialogs/DialogsContext.js.map +1 -0
  347. package/components/Dialogs/useDialogs.d.ts +2 -0
  348. package/components/Dialogs/useDialogs.js +10 -0
  349. package/components/Dialogs/useDialogs.js.map +1 -0
  350. package/components/EmptyView.d.ts +4 -4
  351. package/components/EmptyView.js +29 -70
  352. package/components/EmptyView.js.map +1 -1
  353. package/components/Filters/Filters.d.ts +16 -0
  354. package/components/Filters/Filters.js +20 -0
  355. package/components/Filters/Filters.js.map +1 -0
  356. package/components/Filters/FiltersToggle.d.ts +7 -0
  357. package/components/Filters/FiltersToggle.js +21 -0
  358. package/components/Filters/FiltersToggle.js.map +1 -0
  359. package/components/Filters/index.d.ts +2 -0
  360. package/components/Filters/index.js +2 -0
  361. package/components/FloatingActionButton.d.ts +4 -1
  362. package/components/FloatingActionButton.js +15 -28
  363. package/components/FloatingActionButton.js.map +1 -1
  364. package/components/IconPicker/IconPicker.d.ts +13 -0
  365. package/components/IconPicker/IconPicker.js +37 -0
  366. package/components/IconPicker/IconPicker.js.map +1 -0
  367. package/components/IconPicker/IconPickerComponent.d.ts +17 -0
  368. package/components/IconPicker/IconPickerComponent.js +65 -0
  369. package/components/IconPicker/IconPickerComponent.js.map +1 -0
  370. package/components/IconPicker/IconPickerPresenter.d.ts +53 -0
  371. package/components/IconPicker/IconPickerPresenter.js +71 -0
  372. package/components/IconPicker/IconPickerPresenter.js.map +1 -0
  373. package/components/IconPicker/IconPickerPresenter.test.d.ts +1 -0
  374. package/components/IconPicker/IconPickerPresenter.test.js +84 -0
  375. package/components/IconPicker/IconPickerPresenter.test.js.map +1 -0
  376. package/components/IconPicker/IconPickerPresenterProvider.d.ts +9 -0
  377. package/components/IconPicker/IconPickerPresenterProvider.js +13 -0
  378. package/components/IconPicker/IconPickerPresenterProvider.js.map +1 -0
  379. package/components/IconPicker/IconPickerTab.d.ts +33 -0
  380. package/components/IconPicker/IconPickerTab.js +116 -0
  381. package/components/IconPicker/IconPickerTab.js.map +1 -0
  382. package/components/IconPicker/IconRenderer.d.ts +34 -0
  383. package/components/IconPicker/IconRenderer.js +19 -0
  384. package/components/IconPicker/IconRenderer.js.map +1 -0
  385. package/components/IconPicker/IconRepository.d.ts +19 -0
  386. package/components/IconPicker/IconRepository.js +48 -0
  387. package/components/IconPicker/IconRepository.js.map +1 -0
  388. package/components/IconPicker/IconRepository.test.d.ts +1 -0
  389. package/components/IconPicker/IconRepository.test.js +60 -0
  390. package/components/IconPicker/IconRepository.test.js.map +1 -0
  391. package/components/IconPicker/IconRepositoryFactory.d.ts +9 -0
  392. package/components/IconPicker/IconRepositoryFactory.js +21 -0
  393. package/components/IconPicker/IconRepositoryFactory.js.map +1 -0
  394. package/components/IconPicker/Loading.d.ts +14 -0
  395. package/components/IconPicker/Loading.js +51 -0
  396. package/components/IconPicker/Loading.js.map +1 -0
  397. package/components/IconPicker/components/IconPickerCell.d.ts +12 -0
  398. package/components/IconPicker/components/IconPickerCell.js +25 -0
  399. package/components/IconPicker/components/IconPickerCell.js.map +1 -0
  400. package/components/IconPicker/components/IconPickerContent.d.ts +11 -0
  401. package/components/IconPicker/components/IconPickerContent.js +33 -0
  402. package/components/IconPicker/components/IconPickerContent.js.map +1 -0
  403. package/components/IconPicker/components/IconPickerRow.d.ts +4 -0
  404. package/components/IconPicker/components/IconPickerRow.js +9 -0
  405. package/components/IconPicker/components/IconPickerRow.js.map +1 -0
  406. package/components/IconPicker/components/IconPickerTrigger.d.ts +13 -0
  407. package/components/IconPicker/components/IconPickerTrigger.js +50 -0
  408. package/components/IconPicker/components/IconPickerTrigger.js.map +1 -0
  409. package/components/IconPicker/components/index.d.ts +4 -0
  410. package/components/IconPicker/components/index.js +4 -0
  411. package/components/IconPicker/config/Emojis.d.ts +2 -0
  412. package/components/IconPicker/config/Emojis.js +22 -0
  413. package/components/IconPicker/config/Emojis.js.map +1 -0
  414. package/components/IconPicker/config/FontAwesomeIcons.d.ts +2 -0
  415. package/components/IconPicker/config/FontAwesomeIcons.js +57 -0
  416. package/components/IconPicker/config/FontAwesomeIcons.js.map +1 -0
  417. package/components/IconPicker/config/IconPackProvider.d.ts +7 -0
  418. package/components/IconPicker/config/IconPackProvider.js +21 -0
  419. package/components/IconPicker/config/IconPackProvider.js.map +1 -0
  420. package/components/IconPicker/config/IconType.d.ts +32 -0
  421. package/components/IconPicker/config/IconType.js +57 -0
  422. package/components/IconPicker/config/IconType.js.map +1 -0
  423. package/components/IconPicker/config/index.d.ts +31 -0
  424. package/components/IconPicker/config/index.js +42 -0
  425. package/components/IconPicker/config/index.js.map +1 -0
  426. package/components/IconPicker/defaultIcon.d.ts +6 -0
  427. package/components/IconPicker/defaultIcon.js +9 -0
  428. package/components/IconPicker/defaultIcon.js.map +1 -0
  429. package/components/IconPicker/index.d.ts +7 -0
  430. package/components/IconPicker/index.js +6 -0
  431. package/components/IconPicker/plugins/customPlugin.d.ts +2 -0
  432. package/components/IconPicker/plugins/customPlugin.js +104 -0
  433. package/components/IconPicker/plugins/customPlugin.js.map +1 -0
  434. package/components/IconPicker/plugins/emojisPlugin.d.ts +2 -0
  435. package/components/IconPicker/plugins/emojisPlugin.js +102 -0
  436. package/components/IconPicker/plugins/emojisPlugin.js.map +1 -0
  437. package/components/IconPicker/plugins/graphql.d.ts +20 -0
  438. package/components/IconPicker/plugins/graphql.js +22 -0
  439. package/components/IconPicker/plugins/graphql.js.map +1 -0
  440. package/components/IconPicker/plugins/iconsPlugin.d.ts +2 -0
  441. package/components/IconPicker/plugins/iconsPlugin.js +84 -0
  442. package/components/IconPicker/plugins/iconsPlugin.js.map +1 -0
  443. package/components/IconPicker/types.d.ts +22 -0
  444. package/components/IconPicker/types.js +7 -0
  445. package/components/IconPicker/types.js.map +1 -0
  446. package/components/LexicalEditor/DefaultLexicalEditorConfig.d.ts +2 -0
  447. package/components/LexicalEditor/DefaultLexicalEditorConfig.js +81 -0
  448. package/components/LexicalEditor/DefaultLexicalEditorConfig.js.map +1 -0
  449. package/components/LexicalEditor/LexicalEditor.d.ts +12 -0
  450. package/components/LexicalEditor/LexicalEditor.js +48 -0
  451. package/components/LexicalEditor/LexicalEditor.js.map +1 -0
  452. package/components/LexicalEditor/LexicalLinkForm.d.ts +6 -0
  453. package/components/LexicalEditor/LexicalLinkForm.js +88 -0
  454. package/components/LexicalEditor/LexicalLinkForm.js.map +1 -0
  455. package/components/LexicalEditor/TypographyDropDown.d.ts +8 -0
  456. package/components/LexicalEditor/TypographyDropDown.js +60 -0
  457. package/components/LexicalEditor/TypographyDropDown.js.map +1 -0
  458. package/components/LexicalEditor/index.d.ts +3 -0
  459. package/components/LexicalEditor/index.js +3 -0
  460. package/components/LexicalEditor/lexicalValueFromHtml.d.ts +7 -0
  461. package/components/LexicalEditor/lexicalValueFromHtml.js +28 -0
  462. package/components/LexicalEditor/lexicalValueFromHtml.js.map +1 -0
  463. package/components/LexicalEditor/lexicalValueWithHtml.d.ts +7 -0
  464. package/components/LexicalEditor/lexicalValueWithHtml.js +30 -0
  465. package/components/LexicalEditor/lexicalValueWithHtml.js.map +1 -0
  466. package/components/MultiImageUpload.d.ts +1 -1
  467. package/components/MultiImageUpload.js +6 -42
  468. package/components/MultiImageUpload.js.map +1 -1
  469. package/components/NotAuthorizedError/NotAuthorizedError.d.ts +2 -0
  470. package/components/NotAuthorizedError/NotAuthorizedError.js +19 -0
  471. package/components/NotAuthorizedError/NotAuthorizedError.js.map +1 -0
  472. package/components/NotAuthorizedError/SecureRouteError.js +297 -0
  473. package/components/NotAuthorizedError/SecureRouteError.js.map +1 -0
  474. package/components/NotAuthorizedError/SecureRouteError.svg +1 -0
  475. package/components/NotAuthorizedError/index.d.ts +1 -0
  476. package/components/NotAuthorizedError/index.js +1 -0
  477. package/components/OptionsMenu/OptionsMenu.d.ts +16 -0
  478. package/components/OptionsMenu/OptionsMenu.js +24 -0
  479. package/components/OptionsMenu/OptionsMenu.js.map +1 -0
  480. package/components/OptionsMenu/OptionsMenuItem.d.ts +10 -0
  481. package/components/OptionsMenu/OptionsMenuItem.js +18 -0
  482. package/components/OptionsMenu/OptionsMenuItem.js.map +1 -0
  483. package/components/OptionsMenu/OptionsMenuLink.d.ts +12 -0
  484. package/components/OptionsMenu/OptionsMenuLink.js +16 -0
  485. package/components/OptionsMenu/OptionsMenuLink.js.map +1 -0
  486. package/components/OptionsMenu/index.d.ts +4 -0
  487. package/components/OptionsMenu/index.js +4 -0
  488. package/components/OptionsMenu/useOptionsMenuItem.d.ts +13 -0
  489. package/components/OptionsMenu/useOptionsMenuItem.js +18 -0
  490. package/components/OptionsMenu/useOptionsMenuItem.js.map +1 -0
  491. package/components/OverlayLayout/OverlayLayout.d.ts +6 -17
  492. package/components/OverlayLayout/OverlayLayout.js +39 -182
  493. package/components/OverlayLayout/OverlayLayout.js.map +1 -1
  494. package/components/OverlayLayout/components/OverlayBackdrop.d.ts +7 -0
  495. package/components/OverlayLayout/components/OverlayBackdrop.js +10 -0
  496. package/components/OverlayLayout/components/OverlayBackdrop.js.map +1 -0
  497. package/components/OverlayLayout/components/OverlayContent.d.ts +6 -0
  498. package/components/OverlayLayout/components/OverlayContent.js +22 -0
  499. package/components/OverlayLayout/components/OverlayContent.js.map +1 -0
  500. package/components/OverlayLayout/components/OverlayHeader.d.ts +13 -0
  501. package/components/OverlayLayout/components/OverlayHeader.js +37 -0
  502. package/components/OverlayLayout/components/OverlayHeader.js.map +1 -0
  503. package/components/OverlayLayout/components/OverlayRoot.d.ts +9 -0
  504. package/components/OverlayLayout/components/OverlayRoot.js +18 -0
  505. package/components/OverlayLayout/components/OverlayRoot.js.map +1 -0
  506. package/components/OverlayLayout/components/index.d.ts +4 -0
  507. package/components/OverlayLayout/components/index.js +4 -0
  508. package/components/OverlayLayout/index.d.ts +1 -1
  509. package/components/OverlayLayout/index.js +1 -18
  510. package/components/Permissions/CannotUseAaclAlert.d.ts +2 -0
  511. package/components/Permissions/CannotUseAaclAlert.js +13 -0
  512. package/components/Permissions/CannotUseAaclAlert.js.map +1 -0
  513. package/components/Permissions/Permissions.d.ts +2 -2
  514. package/components/Permissions/Permissions.js +37 -52
  515. package/components/Permissions/Permissions.js.map +1 -1
  516. package/components/Permissions/PermissionsGroup.d.ts +6 -0
  517. package/components/Permissions/PermissionsGroup.js +12 -0
  518. package/components/Permissions/PermissionsGroup.js.map +1 -0
  519. package/components/Permissions/StyledComponents.d.ts +2 -3
  520. package/components/Permissions/StyledComponents.js +13 -35
  521. package/components/Permissions/StyledComponents.js.map +1 -1
  522. package/components/Permissions/index.d.ts +4 -2
  523. package/components/Permissions/index.js +4 -30
  524. package/components/RegisterFeature.d.ts +11 -0
  525. package/components/RegisterFeature.js +19 -0
  526. package/components/RegisterFeature.js.map +1 -0
  527. package/components/ResizablePanels/index.d.ts +1 -0
  528. package/components/ResizablePanels/index.js +1 -0
  529. package/components/RoleAutocomplete/graphql.d.ts +1 -0
  530. package/components/RoleAutocomplete/graphql.js +19 -0
  531. package/components/RoleAutocomplete/graphql.js.map +1 -0
  532. package/components/RoleAutocomplete/index.d.ts +7 -0
  533. package/components/RoleAutocomplete/index.js +26 -0
  534. package/components/RoleAutocomplete/index.js.map +1 -0
  535. package/components/RolesMultiAutocomplete/graphql.d.ts +1 -0
  536. package/components/RolesMultiAutocomplete/graphql.js +24 -0
  537. package/components/RolesMultiAutocomplete/graphql.js.map +1 -0
  538. package/components/RolesMultiAutocomplete/index.d.ts +7 -0
  539. package/components/RolesMultiAutocomplete/index.js +26 -0
  540. package/components/RolesMultiAutocomplete/index.js.map +1 -0
  541. package/components/SearchUI.d.ts +3 -2
  542. package/components/SearchUI.js +21 -73
  543. package/components/SearchUI.js.map +1 -1
  544. package/components/SimpleForm/SimpleForm.d.ts +19 -8
  545. package/components/SimpleForm/SimpleForm.js +60 -104
  546. package/components/SimpleForm/SimpleForm.js.map +1 -1
  547. package/components/SimpleForm/index.d.ts +1 -1
  548. package/components/SimpleForm/index.js +1 -31
  549. package/components/SimpleUI/InputField.d.ts +3 -1
  550. package/components/SimpleUI/InputField.js +76 -113
  551. package/components/SimpleUI/InputField.js.map +1 -1
  552. package/components/SingleImageUpload.d.ts +21 -8
  553. package/components/SingleImageUpload.js +39 -112
  554. package/components/SingleImageUpload.js.map +1 -1
  555. package/components/SplitView/SplitView.d.ts +11 -9
  556. package/components/SplitView/SplitView.js +51 -91
  557. package/components/SplitView/SplitView.js.map +1 -1
  558. package/components/SplitView/index.d.ts +1 -1
  559. package/components/SplitView/index.js +1 -25
  560. package/components/TeamAutocomplete/graphql.d.ts +1 -0
  561. package/components/TeamAutocomplete/graphql.js +19 -0
  562. package/components/TeamAutocomplete/graphql.js.map +1 -0
  563. package/components/TeamAutocomplete/index.d.ts +7 -0
  564. package/components/TeamAutocomplete/index.js +26 -0
  565. package/components/TeamAutocomplete/index.js.map +1 -0
  566. package/components/TeamsMultiAutocomplete/graphql.d.ts +1 -0
  567. package/components/TeamsMultiAutocomplete/graphql.js +24 -0
  568. package/components/TeamsMultiAutocomplete/graphql.js.map +1 -0
  569. package/components/TeamsMultiAutocomplete/index.d.ts +7 -0
  570. package/components/TeamsMultiAutocomplete/index.js +26 -0
  571. package/components/TeamsMultiAutocomplete/index.js.map +1 -0
  572. package/components/Wcp.d.ts +19 -0
  573. package/components/Wcp.js +37 -0
  574. package/components/Wcp.js.map +1 -0
  575. package/components/index.d.ts +27 -1
  576. package/components/index.js +25 -13
  577. package/config/AdminConfig/Dashboard.d.ts +3 -0
  578. package/config/AdminConfig/Dashboard.js +7 -0
  579. package/config/AdminConfig/Dashboard.js.map +1 -0
  580. package/config/AdminConfig/Dialog.d.ts +10 -0
  581. package/config/AdminConfig/Dialog.js +19 -0
  582. package/config/AdminConfig/Dialog.js.map +1 -0
  583. package/config/AdminConfig/FieldRenderer.d.ts +11 -0
  584. package/config/AdminConfig/FieldRenderer.js +19 -0
  585. package/config/AdminConfig/FieldRenderer.js.map +1 -0
  586. package/config/AdminConfig/Form.d.ts +4 -0
  587. package/config/AdminConfig/Form.js +9 -0
  588. package/config/AdminConfig/Form.js.map +1 -0
  589. package/config/AdminConfig/LayoutRenderer.d.ts +10 -0
  590. package/config/AdminConfig/LayoutRenderer.js +19 -0
  591. package/config/AdminConfig/LayoutRenderer.js.map +1 -0
  592. package/config/AdminConfig/LexicalTheme/Color.d.ts +11 -0
  593. package/config/AdminConfig/LexicalTheme/Color.js +38 -0
  594. package/config/AdminConfig/LexicalTheme/Color.js.map +1 -0
  595. package/config/AdminConfig/LexicalTheme/Typography.d.ts +30 -0
  596. package/config/AdminConfig/LexicalTheme/Typography.js +66 -0
  597. package/config/AdminConfig/LexicalTheme/Typography.js.map +1 -0
  598. package/config/AdminConfig/LexicalTheme.d.ts +9 -0
  599. package/config/AdminConfig/LexicalTheme.js +9 -0
  600. package/config/AdminConfig/LexicalTheme.js.map +1 -0
  601. package/config/AdminConfig/Logo.d.ts +24 -0
  602. package/config/AdminConfig/Logo.js +19 -0
  603. package/config/AdminConfig/Logo.js.map +1 -0
  604. package/config/AdminConfig/Menu/MenuGroup.d.ts +62 -0
  605. package/config/AdminConfig/Menu/MenuGroup.js +12 -0
  606. package/config/AdminConfig/Menu/MenuGroup.js.map +1 -0
  607. package/config/AdminConfig/Menu/MenuItem.d.ts +43 -0
  608. package/config/AdminConfig/Menu/MenuItem.js +12 -0
  609. package/config/AdminConfig/Menu/MenuItem.js.map +1 -0
  610. package/config/AdminConfig/Menu/MenuLink.d.ts +43 -0
  611. package/config/AdminConfig/Menu/MenuLink.js +21 -0
  612. package/config/AdminConfig/Menu/MenuLink.js.map +1 -0
  613. package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.d.ts +43 -0
  614. package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.js +11 -0
  615. package/config/AdminConfig/Menu/SupportMenu/SupportMenuItem.js.map +1 -0
  616. package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.d.ts +24 -0
  617. package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.js +11 -0
  618. package/config/AdminConfig/Menu/SupportMenu/SupportMenuLink.js.map +1 -0
  619. package/config/AdminConfig/Menu/SupportMenu.d.ts +72 -0
  620. package/config/AdminConfig/Menu/SupportMenu.js +38 -0
  621. package/config/AdminConfig/Menu/SupportMenu.js.map +1 -0
  622. package/config/AdminConfig/Menu/UserMenu/UserMenuItem.d.ts +40 -0
  623. package/config/AdminConfig/Menu/UserMenu/UserMenuItem.js +7 -0
  624. package/config/AdminConfig/Menu/UserMenu/UserMenuItem.js.map +1 -0
  625. package/config/AdminConfig/Menu/UserMenu/UserMenuLink.d.ts +40 -0
  626. package/config/AdminConfig/Menu/UserMenu/UserMenuLink.js +7 -0
  627. package/config/AdminConfig/Menu/UserMenu/UserMenuLink.js.map +1 -0
  628. package/config/AdminConfig/Menu/UserMenu/UserMenuSeparator.d.ts +1 -0
  629. package/config/AdminConfig/Menu/UserMenu/UserMenuSeparator.js +1 -0
  630. package/config/AdminConfig/Menu/UserMenu/types.d.ts +7 -0
  631. package/config/AdminConfig/Menu/UserMenu/types.js +0 -0
  632. package/config/AdminConfig/Menu/UserMenu.d.ts +129 -0
  633. package/config/AdminConfig/Menu/UserMenu.js +48 -0
  634. package/config/AdminConfig/Menu/UserMenu.js.map +1 -0
  635. package/config/AdminConfig/Menu/types.d.ts +7 -0
  636. package/config/AdminConfig/Menu/types.js +0 -0
  637. package/config/AdminConfig/Menu.d.ts +352 -0
  638. package/config/AdminConfig/Menu.js +69 -0
  639. package/config/AdminConfig/Menu.js.map +1 -0
  640. package/config/AdminConfig/Route.d.ts +3 -0
  641. package/config/AdminConfig/Route.js +7 -0
  642. package/config/AdminConfig/Route.js.map +1 -0
  643. package/config/AdminConfig/Security.d.ts +3 -0
  644. package/config/AdminConfig/Security.js +7 -0
  645. package/config/AdminConfig/Security.js.map +1 -0
  646. package/config/AdminConfig/SecurityPermissions.d.ts +17 -0
  647. package/config/AdminConfig/SecurityPermissions.js +24 -0
  648. package/config/AdminConfig/SecurityPermissions.js.map +1 -0
  649. package/config/AdminConfig/Tenant/TenantLogo.d.ts +24 -0
  650. package/config/AdminConfig/Tenant/TenantLogo.js +22 -0
  651. package/config/AdminConfig/Tenant/TenantLogo.js.map +1 -0
  652. package/config/AdminConfig/Tenant/TenantName.d.ts +23 -0
  653. package/config/AdminConfig/Tenant/TenantName.js +18 -0
  654. package/config/AdminConfig/Tenant/TenantName.js.map +1 -0
  655. package/config/AdminConfig/Tenant.d.ts +49 -0
  656. package/config/AdminConfig/Tenant.js +11 -0
  657. package/config/AdminConfig/Tenant.js.map +1 -0
  658. package/config/AdminConfig/Theme/assignColor.d.ts +2 -0
  659. package/config/AdminConfig/Theme/assignColor.js +55 -0
  660. package/config/AdminConfig/Theme/assignColor.js.map +1 -0
  661. package/config/AdminConfig/Theme/consts.d.ts +2 -0
  662. package/config/AdminConfig/Theme/consts.js +22 -0
  663. package/config/AdminConfig/Theme/consts.js.map +1 -0
  664. package/config/AdminConfig/Theme/types.d.ts +3 -0
  665. package/config/AdminConfig/Theme/types.js +0 -0
  666. package/config/AdminConfig/Theme.d.ts +16 -0
  667. package/config/AdminConfig/Theme.js +15 -0
  668. package/config/AdminConfig/Theme.js.map +1 -0
  669. package/config/AdminConfig/Title.d.ts +23 -0
  670. package/config/AdminConfig/Title.js +15 -0
  671. package/config/AdminConfig/Title.js.map +1 -0
  672. package/config/AdminConfig/Widget.d.ts +15 -0
  673. package/config/AdminConfig/Widget.js +29 -0
  674. package/config/AdminConfig/Widget.js.map +1 -0
  675. package/config/AdminConfig.d.ts +500 -0
  676. package/config/AdminConfig.js +69 -0
  677. package/config/AdminConfig.js.map +1 -0
  678. package/config/createAdminConfig.d.ts +57 -0
  679. package/config/createAdminConfig.js +63 -0
  680. package/config/createAdminConfig.js.map +1 -0
  681. package/css/tokens.css +379 -0
  682. package/css/typography.css +70 -0
  683. package/domain/Identity.d.ts +59 -0
  684. package/domain/Identity.js +86 -0
  685. package/domain/Identity.js.map +1 -0
  686. package/errors/ErrorOverlayNetworkErrorHandler.d.ts +11 -0
  687. package/errors/ErrorOverlayNetworkErrorHandler.js +51 -0
  688. package/errors/ErrorOverlayNetworkErrorHandler.js.map +1 -0
  689. package/errors/TenantIsDisabled.d.ts +2 -0
  690. package/errors/TenantIsDisabled.js +22 -0
  691. package/errors/TenantIsDisabled.js.map +1 -0
  692. package/exports/admin/build-params.d.ts +4 -0
  693. package/exports/admin/build-params.js +2 -0
  694. package/exports/admin/configs.d.ts +1 -0
  695. package/exports/admin/configs.js +1 -0
  696. package/exports/admin/form.d.ts +16 -0
  697. package/exports/admin/form.js +5 -0
  698. package/exports/admin/security.d.ts +14 -0
  699. package/exports/admin/security.js +9 -0
  700. package/exports/admin/tenancy.d.ts +2 -0
  701. package/exports/admin/tenancy.js +2 -0
  702. package/exports/admin/ui/file-manager.d.ts +3 -0
  703. package/exports/admin/ui/file-manager.js +2 -0
  704. package/exports/admin/ui/lexical.d.ts +1 -0
  705. package/exports/admin/ui/lexical.js +1 -0
  706. package/exports/admin/ui.d.ts +7 -0
  707. package/exports/admin/ui.js +6 -0
  708. package/exports/admin.d.ts +9 -0
  709. package/exports/admin.js +8 -0
  710. package/features/apolloClient/abstraction.d.ts +5 -0
  711. package/features/apolloClient/abstraction.js +5 -0
  712. package/features/apolloClient/abstraction.js.map +1 -0
  713. package/features/apolloClient/feature.d.ts +2 -0
  714. package/features/apolloClient/feature.js +31 -0
  715. package/features/apolloClient/feature.js.map +1 -0
  716. package/features/buildParams/BuildParams.d.ts +9 -0
  717. package/features/buildParams/BuildParams.js +24 -0
  718. package/features/buildParams/BuildParams.js.map +1 -0
  719. package/features/buildParams/abstractions.d.ts +15 -0
  720. package/features/buildParams/abstractions.js +6 -0
  721. package/features/buildParams/abstractions.js.map +1 -0
  722. package/features/buildParams/feature.d.ts +1 -0
  723. package/features/buildParams/feature.js +15 -0
  724. package/features/buildParams/feature.js.map +1 -0
  725. package/features/buildParams/index.d.ts +2 -0
  726. package/features/buildParams/index.js +2 -0
  727. package/features/fileUrlFormatter/abstractions.d.ts +6 -0
  728. package/features/fileUrlFormatter/abstractions.js +5 -0
  729. package/features/fileUrlFormatter/abstractions.js.map +1 -0
  730. package/features/formModel/ConditionRuleEvaluator.d.ts +9 -0
  731. package/features/formModel/ConditionRuleEvaluator.js +49 -0
  732. package/features/formModel/ConditionRuleEvaluator.js.map +1 -0
  733. package/features/formModel/Field.d.ts +99 -0
  734. package/features/formModel/Field.js +347 -0
  735. package/features/formModel/Field.js.map +1 -0
  736. package/features/formModel/FieldBuilder.d.ts +44 -0
  737. package/features/formModel/FieldBuilder.js +152 -0
  738. package/features/formModel/FieldBuilder.js.map +1 -0
  739. package/features/formModel/FieldBuilder.test.d.ts +1 -0
  740. package/features/formModel/FieldBuilder.test.js +236 -0
  741. package/features/formModel/FieldBuilder.test.js.map +1 -0
  742. package/features/formModel/FieldBuilderRegistry.d.ts +4 -0
  743. package/features/formModel/FieldBuilderRegistry.js +29 -0
  744. package/features/formModel/FieldBuilderRegistry.js.map +1 -0
  745. package/features/formModel/FocusManager.d.ts +14 -0
  746. package/features/formModel/FocusManager.js +83 -0
  747. package/features/formModel/FocusManager.js.map +1 -0
  748. package/features/formModel/FormErrors.d.ts +9 -0
  749. package/features/formModel/FormErrors.js +35 -0
  750. package/features/formModel/FormErrors.js.map +1 -0
  751. package/features/formModel/FormModel.d.ts +59 -0
  752. package/features/formModel/FormModel.js +410 -0
  753. package/features/formModel/FormModel.js.map +1 -0
  754. package/features/formModel/FormModel.test.d.ts +1 -0
  755. package/features/formModel/FormModel.test.js +3811 -0
  756. package/features/formModel/FormModel.test.js.map +1 -0
  757. package/features/formModel/FormModelFactory.d.ts +11 -0
  758. package/features/formModel/FormModelFactory.js +33 -0
  759. package/features/formModel/FormModelFactory.js.map +1 -0
  760. package/features/formModel/FormView.d.ts +46 -0
  761. package/features/formModel/FormView.js +187 -0
  762. package/features/formModel/FormView.js.map +1 -0
  763. package/features/formModel/LayoutBuilderFactory.d.ts +61 -0
  764. package/features/formModel/LayoutBuilderFactory.js +316 -0
  765. package/features/formModel/LayoutBuilderFactory.js.map +1 -0
  766. package/features/formModel/LayoutMutator.d.ts +11 -0
  767. package/features/formModel/LayoutMutator.js +129 -0
  768. package/features/formModel/LayoutMutator.js.map +1 -0
  769. package/features/formModel/LayoutResolver.d.ts +26 -0
  770. package/features/formModel/LayoutResolver.js +199 -0
  771. package/features/formModel/LayoutResolver.js.map +1 -0
  772. package/features/formModel/ObjectField.d.ts +97 -0
  773. package/features/formModel/ObjectField.js +563 -0
  774. package/features/formModel/ObjectField.js.map +1 -0
  775. package/features/formModel/PresenterErrors.d.ts +9 -0
  776. package/features/formModel/PresenterErrors.js +32 -0
  777. package/features/formModel/PresenterErrors.js.map +1 -0
  778. package/features/formModel/Rules.test.d.ts +1 -0
  779. package/features/formModel/Rules.test.js +324 -0
  780. package/features/formModel/Rules.test.js.map +1 -0
  781. package/features/formModel/abstractions.d.ts +715 -0
  782. package/features/formModel/abstractions.js +8 -0
  783. package/features/formModel/abstractions.js.map +1 -0
  784. package/features/formModel/createFieldRenderer.d.ts +20 -0
  785. package/features/formModel/createFieldRenderer.js +38 -0
  786. package/features/formModel/createFieldRenderer.js.map +1 -0
  787. package/features/formModel/demo/FieldRenderersDemoPresenter.d.ts +18 -0
  788. package/features/formModel/demo/FieldRenderersDemoPresenter.js +299 -0
  789. package/features/formModel/demo/FieldRenderersDemoPresenter.js.map +1 -0
  790. package/features/formModel/demo/FormModelDemo.d.ts +4 -0
  791. package/features/formModel/demo/FormModelDemo.js +206 -0
  792. package/features/formModel/demo/FormModelDemo.js.map +1 -0
  793. package/features/formModel/demo/FormModelDemoPresenter.d.ts +22 -0
  794. package/features/formModel/demo/FormModelDemoPresenter.js +140 -0
  795. package/features/formModel/demo/FormModelDemoPresenter.js.map +1 -0
  796. package/features/formModel/demo/FormModelPhase11Presenter.d.ts +25 -0
  797. package/features/formModel/demo/FormModelPhase11Presenter.js +107 -0
  798. package/features/formModel/demo/FormModelPhase11Presenter.js.map +1 -0
  799. package/features/formModel/demo/FormModelPhase8c1Presenter.d.ts +23 -0
  800. package/features/formModel/demo/FormModelPhase8c1Presenter.js +69 -0
  801. package/features/formModel/demo/FormModelPhase8c1Presenter.js.map +1 -0
  802. package/features/formModel/feature.d.ts +3 -0
  803. package/features/formModel/feature.js +30 -0
  804. package/features/formModel/feature.js.map +1 -0
  805. package/features/formModel/fieldTypes/BooleanFieldType.d.ts +19 -0
  806. package/features/formModel/fieldTypes/BooleanFieldType.js +26 -0
  807. package/features/formModel/fieldTypes/BooleanFieldType.js.map +1 -0
  808. package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +173 -0
  809. package/features/formModel/fieldTypes/DateTimeFieldType.js +246 -0
  810. package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -0
  811. package/features/formModel/fieldTypes/FileFieldType.d.ts +29 -0
  812. package/features/formModel/fieldTypes/FileFieldType.js +56 -0
  813. package/features/formModel/fieldTypes/FileFieldType.js.map +1 -0
  814. package/features/formModel/fieldTypes/FileUrlFieldType.d.ts +18 -0
  815. package/features/formModel/fieldTypes/FileUrlFieldType.js +23 -0
  816. package/features/formModel/fieldTypes/FileUrlFieldType.js.map +1 -0
  817. package/features/formModel/fieldTypes/LexicalFieldType.d.ts +19 -0
  818. package/features/formModel/fieldTypes/LexicalFieldType.js +23 -0
  819. package/features/formModel/fieldTypes/LexicalFieldType.js.map +1 -0
  820. package/features/formModel/fieldTypes/NumberFieldType.d.ts +19 -0
  821. package/features/formModel/fieldTypes/NumberFieldType.js +28 -0
  822. package/features/formModel/fieldTypes/NumberFieldType.js.map +1 -0
  823. package/features/formModel/fieldTypes/ObjectFieldType.d.ts +34 -0
  824. package/features/formModel/fieldTypes/ObjectFieldType.js +95 -0
  825. package/features/formModel/fieldTypes/ObjectFieldType.js.map +1 -0
  826. package/features/formModel/fieldTypes/TextFieldType.d.ts +18 -0
  827. package/features/formModel/fieldTypes/TextFieldType.js +23 -0
  828. package/features/formModel/fieldTypes/TextFieldType.js.map +1 -0
  829. package/features/formModel/fieldTypes/index.d.ts +8 -0
  830. package/features/formModel/fieldTypes/index.js +8 -0
  831. package/features/formModel/index.d.ts +24 -0
  832. package/features/formModel/index.js +22 -0
  833. package/features/formModel/renderers.d.ts +21 -0
  834. package/features/formModel/renderers.js +21 -0
  835. package/features/formModel/useFieldRenderers.d.ts +2 -0
  836. package/features/formModel/useFieldRenderers.js +15 -0
  837. package/features/formModel/useFieldRenderers.js.map +1 -0
  838. package/features/formModel/useLayoutRenderers.d.ts +2 -0
  839. package/features/formModel/useLayoutRenderers.js +15 -0
  840. package/features/formModel/useLayoutRenderers.js.map +1 -0
  841. package/features/listCache/ListCache.d.ts +28 -0
  842. package/features/listCache/ListCache.js +48 -0
  843. package/features/listCache/ListCache.js.map +1 -0
  844. package/features/listCache/index.d.ts +2 -0
  845. package/features/listCache/index.js +1 -0
  846. package/features/security/AuthenticationContext/AuthenticationContext.d.ts +15 -0
  847. package/features/security/AuthenticationContext/AuthenticationContext.js +32 -0
  848. package/features/security/AuthenticationContext/AuthenticationContext.js.map +1 -0
  849. package/features/security/AuthenticationContext/GraphQLClientDecorator.d.ts +12 -0
  850. package/features/security/AuthenticationContext/GraphQLClientDecorator.js +31 -0
  851. package/features/security/AuthenticationContext/GraphQLClientDecorator.js.map +1 -0
  852. package/features/security/AuthenticationContext/InternalIdTokenProvider.d.ts +10 -0
  853. package/features/security/AuthenticationContext/InternalIdTokenProvider.js +20 -0
  854. package/features/security/AuthenticationContext/InternalIdTokenProvider.js.map +1 -0
  855. package/features/security/AuthenticationContext/abstractions.d.ts +26 -0
  856. package/features/security/AuthenticationContext/abstractions.js +6 -0
  857. package/features/security/AuthenticationContext/abstractions.js.map +1 -0
  858. package/features/security/AuthenticationContext/feature.d.ts +3 -0
  859. package/features/security/AuthenticationContext/feature.js +21 -0
  860. package/features/security/AuthenticationContext/feature.js.map +1 -0
  861. package/features/security/AuthenticationContext/index.d.ts +1 -0
  862. package/features/security/AuthenticationContext/index.js +1 -0
  863. package/features/security/AuthenticationContext/types.d.ts +11 -0
  864. package/features/security/AuthenticationContext/types.js +0 -0
  865. package/features/security/IdentityContext/IdentityContext.d.ts +13 -0
  866. package/features/security/IdentityContext/IdentityContext.js +25 -0
  867. package/features/security/IdentityContext/IdentityContext.js.map +1 -0
  868. package/features/security/IdentityContext/abstractions.d.ts +10 -0
  869. package/features/security/IdentityContext/abstractions.js +5 -0
  870. package/features/security/IdentityContext/abstractions.js.map +1 -0
  871. package/features/security/IdentityContext/feature.d.ts +3 -0
  872. package/features/security/IdentityContext/feature.js +17 -0
  873. package/features/security/IdentityContext/feature.js.map +1 -0
  874. package/features/security/IdentityContext/index.d.ts +2 -0
  875. package/features/security/IdentityContext/index.js +2 -0
  876. package/features/security/LogIn/IdentityMapper.d.ts +9 -0
  877. package/features/security/LogIn/IdentityMapper.js +14 -0
  878. package/features/security/LogIn/IdentityMapper.js.map +1 -0
  879. package/features/security/LogIn/LogInGateway.d.ts +11 -0
  880. package/features/security/LogIn/LogInGateway.js +25 -0
  881. package/features/security/LogIn/LogInGateway.js.map +1 -0
  882. package/features/security/LogIn/LogInRepository.d.ts +14 -0
  883. package/features/security/LogIn/LogInRepository.js +21 -0
  884. package/features/security/LogIn/LogInRepository.js.map +1 -0
  885. package/features/security/LogIn/LogInUseCase.d.ts +15 -0
  886. package/features/security/LogIn/LogInUseCase.js +32 -0
  887. package/features/security/LogIn/LogInUseCase.js.map +1 -0
  888. package/features/security/LogIn/abstractions.d.ts +48 -0
  889. package/features/security/LogIn/abstractions.js +8 -0
  890. package/features/security/LogIn/abstractions.js.map +1 -0
  891. package/features/security/LogIn/createLoginMutation.d.ts +1 -0
  892. package/features/security/LogIn/createLoginMutation.js +48 -0
  893. package/features/security/LogIn/createLoginMutation.js.map +1 -0
  894. package/features/security/LogIn/feature.d.ts +3 -0
  895. package/features/security/LogIn/feature.js +23 -0
  896. package/features/security/LogIn/feature.js.map +1 -0
  897. package/features/security/LogIn/index.d.ts +1 -0
  898. package/features/security/LogIn/index.js +1 -0
  899. package/features/security/LogOut/LogOutUseCase.d.ts +13 -0
  900. package/features/security/LogOut/LogOutUseCase.js +25 -0
  901. package/features/security/LogOut/LogOutUseCase.js.map +1 -0
  902. package/features/security/LogOut/abstractions.d.ts +7 -0
  903. package/features/security/LogOut/abstractions.js +5 -0
  904. package/features/security/LogOut/abstractions.js.map +1 -0
  905. package/features/security/LogOut/feature.d.ts +3 -0
  906. package/features/security/LogOut/feature.js +17 -0
  907. package/features/security/LogOut/feature.js.map +1 -0
  908. package/features/security/LogOut/index.d.ts +2 -0
  909. package/features/security/LogOut/index.js +2 -0
  910. package/features/security/SecurityFeature.d.ts +1 -0
  911. package/features/security/SecurityFeature.js +17 -0
  912. package/features/security/SecurityFeature.js.map +1 -0
  913. package/features/telemetry/TelemetryService.d.ts +6 -0
  914. package/features/telemetry/TelemetryService.js +19 -0
  915. package/features/telemetry/TelemetryService.js.map +1 -0
  916. package/features/telemetry/abstractions.d.ts +7 -0
  917. package/features/telemetry/abstractions.js +5 -0
  918. package/features/telemetry/abstractions.js.map +1 -0
  919. package/features/telemetry/feature.d.ts +3 -0
  920. package/features/telemetry/feature.js +17 -0
  921. package/features/telemetry/feature.js.map +1 -0
  922. package/features/telemetry/index.d.ts +2 -0
  923. package/features/telemetry/index.js +2 -0
  924. package/features/tenancy/GraphQLClientDecorator.d.ts +12 -0
  925. package/features/tenancy/GraphQLClientDecorator.js +30 -0
  926. package/features/tenancy/GraphQLClientDecorator.js.map +1 -0
  927. package/features/tenancy/TenantContext.d.ts +17 -0
  928. package/features/tenancy/TenantContext.js +57 -0
  929. package/features/tenancy/TenantContext.js.map +1 -0
  930. package/features/tenancy/abstractions.d.ts +14 -0
  931. package/features/tenancy/abstractions.js +5 -0
  932. package/features/tenancy/abstractions.js.map +1 -0
  933. package/features/tenancy/feature.d.ts +5 -0
  934. package/features/tenancy/feature.js +31 -0
  935. package/features/tenancy/feature.js.map +1 -0
  936. package/features/tenancy/types.d.ts +4 -0
  937. package/features/tenancy/types.js +0 -0
  938. package/features/tools/LexicalContext/LexicalContext.d.ts +14 -0
  939. package/features/tools/LexicalContext/LexicalContext.js +25 -0
  940. package/features/tools/LexicalContext/LexicalContext.js.map +1 -0
  941. package/features/tools/LexicalContext/abstractions.d.ts +11 -0
  942. package/features/tools/LexicalContext/abstractions.js +5 -0
  943. package/features/tools/LexicalContext/abstractions.js.map +1 -0
  944. package/features/tools/LexicalContext/index.d.ts +2 -0
  945. package/features/tools/LexicalContext/index.js +1 -0
  946. package/features/tools/ToolPipelineRunner.d.ts +10 -0
  947. package/features/tools/ToolPipelineRunner.js +35 -0
  948. package/features/tools/ToolPipelineRunner.js.map +1 -0
  949. package/features/tools/ToolRegistry.d.ts +12 -0
  950. package/features/tools/ToolRegistry.js +38 -0
  951. package/features/tools/ToolRegistry.js.map +1 -0
  952. package/features/tools/abstractions.d.ts +28 -0
  953. package/features/tools/abstractions.js +7 -0
  954. package/features/tools/abstractions.js.map +1 -0
  955. package/features/tools/feature.d.ts +4 -0
  956. package/features/tools/feature.js +22 -0
  957. package/features/tools/feature.js.map +1 -0
  958. package/features/tools/index.d.ts +3 -0
  959. package/features/tools/index.js +3 -0
  960. package/features/wcp/ReactLicense.d.ts +22 -0
  961. package/features/wcp/ReactLicense.js +56 -0
  962. package/features/wcp/ReactLicense.js.map +1 -0
  963. package/features/wcp/WcpGateway.d.ts +10 -0
  964. package/features/wcp/WcpGateway.js +78 -0
  965. package/features/wcp/WcpGateway.js.map +1 -0
  966. package/features/wcp/WcpService.d.ts +20 -0
  967. package/features/wcp/WcpService.js +74 -0
  968. package/features/wcp/WcpService.js.map +1 -0
  969. package/features/wcp/abstractions.d.ts +19 -0
  970. package/features/wcp/abstractions.js +6 -0
  971. package/features/wcp/abstractions.js.map +1 -0
  972. package/features/wcp/feature.d.ts +3 -0
  973. package/features/wcp/feature.js +19 -0
  974. package/features/wcp/feature.js.map +1 -0
  975. package/features/wcp/types.d.ts +21 -0
  976. package/features/wcp/types.js +0 -0
  977. package/features/webinySdk/WebinySdk.d.ts +11 -0
  978. package/features/webinySdk/WebinySdk.js +28 -0
  979. package/features/webinySdk/WebinySdk.js.map +1 -0
  980. package/features/webinySdk/abstractions.d.ts +6 -0
  981. package/features/webinySdk/abstractions.js +5 -0
  982. package/features/webinySdk/abstractions.js.map +1 -0
  983. package/features/webinySdk/feature.d.ts +3 -0
  984. package/features/webinySdk/feature.js +17 -0
  985. package/features/webinySdk/feature.js.map +1 -0
  986. package/features/webinySdk/index.d.ts +2 -0
  987. package/features/webinySdk/index.js +2 -0
  988. package/hooks/index.d.ts +13 -0
  989. package/hooks/index.js +13 -0
  990. package/hooks/useConfirmationDialog.d.ts +7 -3
  991. package/hooks/useConfirmationDialog.js +36 -57
  992. package/hooks/useConfirmationDialog.js.map +1 -1
  993. package/hooks/useDialog.d.ts +9 -10
  994. package/hooks/useDialog.js +18 -36
  995. package/hooks/useDialog.js.map +1 -1
  996. package/hooks/useHotkeys.d.ts +9 -0
  997. package/hooks/useHotkeys.js +66 -0
  998. package/hooks/useHotkeys.js.map +1 -0
  999. package/hooks/useIsMounted.d.ts +3 -0
  1000. package/hooks/useIsMounted.js +16 -0
  1001. package/hooks/useIsMounted.js.map +1 -0
  1002. package/hooks/useKeyHandler.d.ts +8 -0
  1003. package/hooks/useKeyHandler.js +56 -0
  1004. package/hooks/useKeyHandler.js.map +1 -0
  1005. package/hooks/useModKey.d.ts +1 -0
  1006. package/hooks/useModKey.js +24 -0
  1007. package/hooks/useModKey.js.map +1 -0
  1008. package/hooks/useOpenDialog.d.ts +7 -0
  1009. package/hooks/useOpenDialog.js +18 -0
  1010. package/hooks/useOpenDialog.js.map +1 -0
  1011. package/hooks/useShiftKey.d.ts +5 -0
  1012. package/hooks/useShiftKey.js +42 -0
  1013. package/hooks/useShiftKey.js.map +1 -0
  1014. package/hooks/useSnackbar.d.ts +4 -0
  1015. package/hooks/useSnackbar.js +28 -36
  1016. package/hooks/useSnackbar.js.map +1 -1
  1017. package/hooks/useStateIfMounted.d.ts +1 -0
  1018. package/hooks/useStateIfMounted.js +18 -0
  1019. package/hooks/useStateIfMounted.js.map +1 -0
  1020. package/hooks/useStateWithCallback.d.ts +5 -0
  1021. package/hooks/useStateWithCallback.js +22 -0
  1022. package/hooks/useStateWithCallback.js.map +1 -0
  1023. package/hooks/useToggler.d.ts +7 -0
  1024. package/hooks/useToggler.js +16 -0
  1025. package/hooks/useToggler.js.map +1 -0
  1026. package/index.d.ts +58 -35
  1027. package/index.js +47 -373
  1028. package/lexical.css +2 -0
  1029. package/package.json +78 -68
  1030. package/permissions/PermissionRenderer.d.ts +6 -0
  1031. package/permissions/PermissionRenderer.js +205 -0
  1032. package/permissions/PermissionRenderer.js.map +1 -0
  1033. package/permissions/PermissionValueContext.d.ts +11 -0
  1034. package/permissions/PermissionValueContext.js +16 -0
  1035. package/permissions/PermissionValueContext.js.map +1 -0
  1036. package/permissions/createHasPermission.d.ts +4 -0
  1037. package/permissions/createHasPermission.js +39 -0
  1038. package/permissions/createHasPermission.js.map +1 -0
  1039. package/permissions/createPermissionSchema.d.ts +2 -0
  1040. package/permissions/createPermissionSchema.js +9 -0
  1041. package/permissions/createPermissionSchema.js.map +1 -0
  1042. package/permissions/createPermissions.d.ts +6 -0
  1043. package/permissions/createPermissions.js +141 -0
  1044. package/permissions/createPermissions.js.map +1 -0
  1045. package/permissions/createPermissions.test.d.ts +1 -0
  1046. package/permissions/createPermissions.test.js +209 -0
  1047. package/permissions/createPermissions.test.js.map +1 -0
  1048. package/permissions/index.d.ts +9 -0
  1049. package/permissions/index.js +7 -0
  1050. package/permissions/types.d.ts +267 -0
  1051. package/permissions/types.js +0 -0
  1052. package/permissions/usePermissionForm.d.ts +17 -0
  1053. package/permissions/usePermissionForm.js +157 -0
  1054. package/permissions/usePermissionForm.js.map +1 -0
  1055. package/permissions/usePermissions.d.ts +4 -0
  1056. package/permissions/usePermissions.js +10 -0
  1057. package/permissions/usePermissions.js.map +1 -0
  1058. package/plugins/MenuPlugin.d.ts +1 -1
  1059. package/plugins/MenuPlugin.js +17 -55
  1060. package/plugins/MenuPlugin.js.map +1 -1
  1061. package/presentation/browserFilePicker/BrowserFilePicker.d.ts +3 -0
  1062. package/presentation/browserFilePicker/BrowserFilePicker.js +88 -0
  1063. package/presentation/browserFilePicker/BrowserFilePicker.js.map +1 -0
  1064. package/presentation/browserFilePicker/BrowserFilePickerPresenter.d.ts +10 -0
  1065. package/presentation/browserFilePicker/BrowserFilePickerPresenter.js +84 -0
  1066. package/presentation/browserFilePicker/BrowserFilePickerPresenter.js.map +1 -0
  1067. package/presentation/browserFilePicker/index.d.ts +3 -0
  1068. package/presentation/browserFilePicker/index.js +2 -0
  1069. package/presentation/browserFilePicker/types.d.ts +53 -0
  1070. package/presentation/browserFilePicker/types.js +0 -0
  1071. package/presentation/browserFilePicker/utils.d.ts +2 -0
  1072. package/presentation/browserFilePicker/utils.js +16 -0
  1073. package/presentation/browserFilePicker/utils.js.map +1 -0
  1074. package/presentation/buildParams/useBuildParams.d.ts +1 -0
  1075. package/presentation/buildParams/useBuildParams.js +6 -0
  1076. package/presentation/buildParams/useBuildParams.js.map +1 -0
  1077. package/presentation/installation/components/SystemInstaller/SystemInstaller.d.ts +8 -0
  1078. package/presentation/installation/components/SystemInstaller/SystemInstaller.js +63 -0
  1079. package/presentation/installation/components/SystemInstaller/SystemInstaller.js.map +1 -0
  1080. package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.d.ts +2 -0
  1081. package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.js +12 -0
  1082. package/presentation/installation/components/SystemInstaller/SystemInstallerProvider.js.map +1 -0
  1083. package/presentation/installation/components/SystemInstaller/index.d.ts +1 -0
  1084. package/presentation/installation/components/SystemInstaller/index.js +1 -0
  1085. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.d.ts +8 -0
  1086. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.js +17 -0
  1087. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/createPasswordValidator.js.map +1 -0
  1088. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.d.ts +1 -0
  1089. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.js +16 -0
  1090. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep/usePasswordValidator.js.map +1 -0
  1091. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.d.ts +7 -0
  1092. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.js +69 -0
  1093. package/presentation/installation/components/SystemInstaller/steps/AdminUserStep.js.map +1 -0
  1094. package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.d.ts +7 -0
  1095. package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.js +85 -0
  1096. package/presentation/installation/components/SystemInstaller/steps/BasicInfoStep.js.map +1 -0
  1097. package/presentation/installation/components/SystemInstaller/steps/Center.d.ts +4 -0
  1098. package/presentation/installation/components/SystemInstaller/steps/Center.js +7 -0
  1099. package/presentation/installation/components/SystemInstaller/steps/Center.js.map +1 -0
  1100. package/presentation/installation/components/SystemInstaller/steps/Container.d.ts +8 -0
  1101. package/presentation/installation/components/SystemInstaller/steps/Container.js +22 -0
  1102. package/presentation/installation/components/SystemInstaller/steps/Container.js.map +1 -0
  1103. package/presentation/installation/components/SystemInstaller/steps/FinishSetup.d.ts +11 -0
  1104. package/presentation/installation/components/SystemInstaller/steps/FinishSetup.js +53 -0
  1105. package/presentation/installation/components/SystemInstaller/steps/FinishSetup.js.map +1 -0
  1106. package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.d.ts +7 -0
  1107. package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.js +18 -0
  1108. package/presentation/installation/components/SystemInstaller/steps/IntroductionStep.js.map +1 -0
  1109. package/presentation/installation/components/SystemInstaller/steps/introduction.js +723 -0
  1110. package/presentation/installation/components/SystemInstaller/steps/introduction.js.map +1 -0
  1111. package/presentation/installation/components/SystemInstaller/steps/introduction.svg +154 -0
  1112. package/presentation/installation/components/SystemInstaller/steps/referralSources.d.ts +1 -0
  1113. package/presentation/installation/components/SystemInstaller/steps/referralSources.js +16 -0
  1114. package/presentation/installation/components/SystemInstaller/steps/referralSources.js.map +1 -0
  1115. package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.d.ts +10 -0
  1116. package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.js +62 -0
  1117. package/presentation/installation/presenters/SystemInstaller/SystemInstallerGateway.js.map +1 -0
  1118. package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.d.ts +24 -0
  1119. package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.js +131 -0
  1120. package/presentation/installation/presenters/SystemInstaller/SystemInstallerPresenter.js.map +1 -0
  1121. package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.d.ts +11 -0
  1122. package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.js +32 -0
  1123. package/presentation/installation/presenters/SystemInstaller/SystemInstallerRepository.js.map +1 -0
  1124. package/presentation/installation/presenters/SystemInstaller/abstractions.d.ts +59 -0
  1125. package/presentation/installation/presenters/SystemInstaller/abstractions.js +7 -0
  1126. package/presentation/installation/presenters/SystemInstaller/abstractions.js.map +1 -0
  1127. package/presentation/installation/presenters/SystemInstaller/feature.d.ts +3 -0
  1128. package/presentation/installation/presenters/SystemInstaller/feature.js +21 -0
  1129. package/presentation/installation/presenters/SystemInstaller/feature.js.map +1 -0
  1130. package/presentation/installation/presenters/SystemInstaller/index.d.ts +3 -0
  1131. package/presentation/installation/presenters/SystemInstaller/index.js +2 -0
  1132. package/presentation/lexicalContext/useLexicalContext.d.ts +3 -0
  1133. package/presentation/lexicalContext/useLexicalContext.js +17 -0
  1134. package/presentation/lexicalContext/useLexicalContext.js.map +1 -0
  1135. package/presentation/listPresenter/ListPresenter.d.ts +30 -0
  1136. package/presentation/listPresenter/ListPresenter.js +216 -0
  1137. package/presentation/listPresenter/ListPresenter.js.map +1 -0
  1138. package/presentation/listPresenter/ListPresenter.test.d.ts +1 -0
  1139. package/presentation/listPresenter/ListPresenter.test.js +816 -0
  1140. package/presentation/listPresenter/ListPresenter.test.js.map +1 -0
  1141. package/presentation/listPresenter/SelectionController.d.ts +21 -0
  1142. package/presentation/listPresenter/SelectionController.js +74 -0
  1143. package/presentation/listPresenter/SelectionController.js.map +1 -0
  1144. package/presentation/listPresenter/abstractions.d.ts +107 -0
  1145. package/presentation/listPresenter/abstractions.js +5 -0
  1146. package/presentation/listPresenter/abstractions.js.map +1 -0
  1147. package/presentation/listPresenter/feature.d.ts +3 -0
  1148. package/presentation/listPresenter/feature.js +17 -0
  1149. package/presentation/listPresenter/feature.js.map +1 -0
  1150. package/presentation/listPresenter/index.d.ts +3 -0
  1151. package/presentation/listPresenter/index.js +2 -0
  1152. package/presentation/security/components/HasPermission.d.ts +10 -0
  1153. package/presentation/security/components/HasPermission.js +21 -0
  1154. package/presentation/security/components/HasPermission.js.map +1 -0
  1155. package/presentation/security/components/SecureRoute.d.ts +7 -0
  1156. package/presentation/security/components/SecureRoute.js +10 -0
  1157. package/presentation/security/components/SecureRoute.js.map +1 -0
  1158. package/presentation/security/hooks/useAuthentication.d.ts +9 -0
  1159. package/presentation/security/hooks/useAuthentication.js +18 -0
  1160. package/presentation/security/hooks/useAuthentication.js.map +1 -0
  1161. package/presentation/security/hooks/useIdentity.d.ts +6 -0
  1162. package/presentation/security/hooks/useIdentity.js +26 -0
  1163. package/presentation/security/hooks/useIdentity.js.map +1 -0
  1164. package/presentation/security/hooks/useSecurity.d.ts +10 -0
  1165. package/presentation/security/hooks/useSecurity.js +18 -0
  1166. package/presentation/security/hooks/useSecurity.js.map +1 -0
  1167. package/presentation/tenancy/TenancyProvider.d.ts +6 -0
  1168. package/presentation/tenancy/TenancyProvider.js +30 -0
  1169. package/presentation/tenancy/TenancyProvider.js.map +1 -0
  1170. package/presentation/tenancy/createTenancyProvider.d.ts +1 -0
  1171. package/presentation/tenancy/createTenancyProvider.js +9 -0
  1172. package/presentation/tenancy/createTenancyProvider.js.map +1 -0
  1173. package/presentation/tenancy/useTenantContext.d.ts +5 -0
  1174. package/presentation/tenancy/useTenantContext.js +23 -0
  1175. package/presentation/tenancy/useTenantContext.js.map +1 -0
  1176. package/presentation/textToLexicalTool/TextToLexicalTool.d.ts +30 -0
  1177. package/presentation/textToLexicalTool/TextToLexicalTool.js +31 -0
  1178. package/presentation/textToLexicalTool/TextToLexicalTool.js.map +1 -0
  1179. package/presentation/textToLexicalTool/feature.d.ts +1 -0
  1180. package/presentation/textToLexicalTool/feature.js +11 -0
  1181. package/presentation/textToLexicalTool/feature.js.map +1 -0
  1182. package/presentation/textToLexicalTool/textToLexicalState.d.ts +6 -0
  1183. package/presentation/textToLexicalTool/textToLexicalState.js +31 -0
  1184. package/presentation/textToLexicalTool/textToLexicalState.js.map +1 -0
  1185. package/presentation/wcp/WcpProvider.d.ts +7 -0
  1186. package/presentation/wcp/WcpProvider.js +23 -0
  1187. package/presentation/wcp/WcpProvider.js.map +1 -0
  1188. package/presentation/wcp/useWcp.d.ts +2 -0
  1189. package/presentation/wcp/useWcp.js +17 -0
  1190. package/presentation/wcp/useWcp.js.map +1 -0
  1191. package/routes.d.ts +6 -0
  1192. package/routes.js +18 -0
  1193. package/routes.js.map +1 -0
  1194. package/static/svg/SecureRouteError.e8b15981.svg +1 -0
  1195. package/static/svg/add-18px.b3062af6.svg +3 -0
  1196. package/static/svg/arrow_drop_down-24px.da74d713.svg +4 -0
  1197. package/static/svg/attach_file_black_24dp.7fb13a7e.svg +1 -0
  1198. package/static/svg/baseline-menu-24px.071b179d.svg +4 -0
  1199. package/static/svg/baseline-notification_important-24px.29277e73.svg +4 -0
  1200. package/static/svg/baseline-security-24px.12e519cc.svg +4 -0
  1201. package/static/svg/file_download.afe3fbec.svg +1 -0
  1202. package/static/svg/file_upload.6865d820.svg +1 -0
  1203. package/static/svg/filter-24px.886d9ff0.svg +8 -0
  1204. package/static/svg/github-brands.6311f0fa.svg +1 -0
  1205. package/static/svg/highlight-24px.959a97f7.svg +1 -0
  1206. package/static/svg/icon-community.cd087355.svg +16 -0
  1207. package/static/svg/icon-documentation.a189d24c.svg +16 -0
  1208. package/static/svg/info.fe810b72.svg +1 -0
  1209. package/static/svg/insert_drive_file-24px.39d490eb.svg +1 -0
  1210. package/static/svg/insert_photo-24px.81a5f945.svg +1 -0
  1211. package/static/svg/introduction.108720aa.svg +154 -0
  1212. package/static/svg/label-24px.9deafa4f.svg +1 -0
  1213. package/static/svg/round-account_circle-24px.f0b48cb7.svg +1 -0
  1214. package/static/svg/round-add-24px.4a5219b4.svg +16 -0
  1215. package/static/svg/round-arrow_drop_down-24px.5fdf92d1.svg +1 -0
  1216. package/static/svg/round-chevron_right-24px.34936511.svg +12 -0
  1217. package/static/svg/round-feedback-24px.4dae7231.svg +54 -0
  1218. package/static/svg/round-help-24px.a61b1d66.svg +1 -0
  1219. package/static/svg/round-invert_colors-24px.a229a1dd.svg +52 -0
  1220. package/static/svg/round-keyboard_arrow_down-24px.6febe804.svg +16 -0
  1221. package/static/svg/round-keyboard_arrow_up-24px.b1f7db34.svg +16 -0
  1222. package/static/svg/round-lock_open-24px.d527f6a5.svg +60 -0
  1223. package/static/svg/round-more_vert-24px.68d41b07.svg +12 -0
  1224. package/static/svg/round-open_in_new-24px.be5b4ddd.svg +44 -0
  1225. package/static/svg/round-settings-24px.c0b3056f.svg +4 -0
  1226. package/static/svg/search-24px.3c0f88dd.svg +20 -0
  1227. package/static/svg/slack-logo.7ee35e7f.svg +1 -0
  1228. package/static/svg/today-24px.df206362.svg +1 -0
  1229. package/static/svg/touch_app.debf3744.svg +1 -0
  1230. package/static/svg/wby-horizontal.03327bf6.svg +23 -0
  1231. package/static/svg/wby-square.ff8f47c3.svg +3 -0
  1232. package/static/svg/webiny-logo.ef56725c.svg +20 -0
  1233. package/static/svg/webiny-orange-logo.4eccf0f2.svg +20 -0
  1234. package/styles.scss +7 -2
  1235. package/types.d.ts +8 -92
  1236. package/types.js +0 -5
  1237. package/assets/images/icons.png +0 -0
  1238. package/assets/images/icons_retina.png +0 -0
  1239. package/assets/images/swich.png +0 -0
  1240. package/base/providers/TelemetryProvider.d.ts +0 -2
  1241. package/base/providers/TelemetryProvider.js +0 -37
  1242. package/base/providers/TelemetryProvider.js.map +0 -1
  1243. package/base/providers/ViewCompositionProvider.d.ts +0 -17
  1244. package/base/providers/ViewCompositionProvider.js +0 -70
  1245. package/base/providers/ViewCompositionProvider.js.map +0 -1
  1246. package/base/ui/LocaleSelector.d.ts +0 -2
  1247. package/base/ui/LocaleSelector.js +0 -19
  1248. package/base/ui/LocaleSelector.js.map +0 -1
  1249. package/base/ui/Menu.d.ts +0 -31
  1250. package/base/ui/Menu.js +0 -166
  1251. package/base/ui/Menu.js.map +0 -1
  1252. package/base/ui/Search.d.ts +0 -19
  1253. package/base/ui/Search.js +0 -81
  1254. package/base/ui/Search.js.map +0 -1
  1255. package/components/AppInstaller/AppInstaller.d.ts +0 -7
  1256. package/components/AppInstaller/AppInstaller.js +0 -183
  1257. package/components/AppInstaller/AppInstaller.js.map +0 -1
  1258. package/components/AppInstaller/Sidebar.d.ts +0 -9
  1259. package/components/AppInstaller/Sidebar.js +0 -178
  1260. package/components/AppInstaller/Sidebar.js.map +0 -1
  1261. package/components/AppInstaller/assets/sign-in-divider.svg +0 -19
  1262. package/components/AppInstaller/index.d.ts +0 -2
  1263. package/components/AppInstaller/index.js +0 -29
  1264. package/components/AppInstaller/index.js.map +0 -1
  1265. package/components/AppInstaller/styled.d.ts +0 -9
  1266. package/components/AppInstaller/styled.js +0 -91
  1267. package/components/AppInstaller/styled.js.map +0 -1
  1268. package/components/AppInstaller/useInstaller.d.ts +0 -31
  1269. package/components/AppInstaller/useInstaller.js +0 -321
  1270. package/components/AppInstaller/useInstaller.js.map +0 -1
  1271. package/components/FileManager/BottomInfoBar/SupportedFileTypes.d.ts +0 -6
  1272. package/components/FileManager/BottomInfoBar/SupportedFileTypes.js +0 -55
  1273. package/components/FileManager/BottomInfoBar/SupportedFileTypes.js.map +0 -1
  1274. package/components/FileManager/BottomInfoBar/UploadStatus.d.ts +0 -6
  1275. package/components/FileManager/BottomInfoBar/UploadStatus.js +0 -60
  1276. package/components/FileManager/BottomInfoBar/UploadStatus.js.map +0 -1
  1277. package/components/FileManager/BottomInfoBar.d.ts +0 -5
  1278. package/components/FileManager/BottomInfoBar.js +0 -60
  1279. package/components/FileManager/BottomInfoBar.js.map +0 -1
  1280. package/components/FileManager/DropFilesHere.d.ts +0 -10
  1281. package/components/FileManager/DropFilesHere.js +0 -76
  1282. package/components/FileManager/DropFilesHere.js.map +0 -1
  1283. package/components/FileManager/File.d.ts +0 -17
  1284. package/components/FileManager/File.js +0 -155
  1285. package/components/FileManager/File.js.map +0 -1
  1286. package/components/FileManager/FileDetails/Name.d.ts +0 -8
  1287. package/components/FileManager/FileDetails/Name.js +0 -138
  1288. package/components/FileManager/FileDetails/Name.js.map +0 -1
  1289. package/components/FileManager/FileDetails/Tags.d.ts +0 -10
  1290. package/components/FileManager/FileDetails/Tags.js +0 -339
  1291. package/components/FileManager/FileDetails/Tags.js.map +0 -1
  1292. package/components/FileManager/FileDetails.d.ts +0 -24
  1293. package/components/FileManager/FileDetails.js +0 -474
  1294. package/components/FileManager/FileDetails.js.map +0 -1
  1295. package/components/FileManager/FileManagerContext.d.ts +0 -42
  1296. package/components/FileManager/FileManagerContext.js +0 -224
  1297. package/components/FileManager/FileManagerContext.js.map +0 -1
  1298. package/components/FileManager/FileManagerView.d.ts +0 -18
  1299. package/components/FileManager/FileManagerView.js +0 -728
  1300. package/components/FileManager/FileManagerView.js.map +0 -1
  1301. package/components/FileManager/LeftSidebar.d.ts +0 -10
  1302. package/components/FileManager/LeftSidebar.js +0 -127
  1303. package/components/FileManager/LeftSidebar.js.map +0 -1
  1304. package/components/FileManager/NoPermissionView.d.ts +0 -3
  1305. package/components/FileManager/NoPermissionView.js +0 -87
  1306. package/components/FileManager/NoPermissionView.js.map +0 -1
  1307. package/components/FileManager/NoResults.d.ts +0 -3
  1308. package/components/FileManager/NoResults.js +0 -28
  1309. package/components/FileManager/NoResults.js.map +0 -1
  1310. package/components/FileManager/getFileTypePlugin.d.ts +0 -4
  1311. package/components/FileManager/getFileTypePlugin.js +0 -57
  1312. package/components/FileManager/getFileTypePlugin.js.map +0 -1
  1313. package/components/FileManager/getFileUploader.d.ts +0 -3
  1314. package/components/FileManager/getFileUploader.js +0 -20
  1315. package/components/FileManager/getFileUploader.js.map +0 -1
  1316. package/components/FileManager/graphql.d.ts +0 -96
  1317. package/components/FileManager/graphql.js +0 -58
  1318. package/components/FileManager/graphql.js.map +0 -1
  1319. package/components/FileManager/icons/content_copy-black-24px.svg +0 -1
  1320. package/components/FileManager/icons/delete.svg +0 -12
  1321. package/components/FileManager/icons/privacy_tip-24px.svg +0 -10
  1322. package/components/FileManager/icons/round-check_box-24px.svg +0 -4
  1323. package/components/FileManager/icons/round-check_box_outline_blank-24px.svg +0 -4
  1324. package/components/FileManager/icons/round-cloud_download-24px.svg +0 -4
  1325. package/components/FileManager/icons/round-cloud_upload-24px.svg +0 -4
  1326. package/components/FileManager/icons/round-description-24px.svg +0 -4
  1327. package/components/FileManager/icons/round-edit-24px.svg +0 -4
  1328. package/components/FileManager/icons/round-info-24px.svg +0 -4
  1329. package/components/FileManager/icons/round-label-24px.svg +0 -4
  1330. package/components/FileManager/icons/round-more_vert-24px.svg +0 -4
  1331. package/components/FileManager/icons/round-search-24px.svg +0 -20
  1332. package/components/FileManager/outputFileSelectionError.d.ts +0 -7
  1333. package/components/FileManager/outputFileSelectionError.js +0 -54
  1334. package/components/FileManager/outputFileSelectionError.js.map +0 -1
  1335. package/components/FileManager/types.d.ts +0 -21
  1336. package/components/FileManager/types.js +0 -5
  1337. package/components/FileManager/types.js.map +0 -1
  1338. package/components/FileManager.d.ts +0 -67
  1339. package/components/FileManager.js +0 -144
  1340. package/components/FileManager.js.map +0 -1
  1341. package/components/OverlayLayout/icons/close.svg +0 -13
  1342. package/components/OverlayLayout/icons/navigate_before.svg +0 -16
  1343. package/components/OverlayLayout/index.js.map +0 -1
  1344. package/components/Permissions/index.js.map +0 -1
  1345. package/components/RichTextEditor/RichTextEditor.d.ts +0 -3
  1346. package/components/RichTextEditor/RichTextEditor.js +0 -30
  1347. package/components/RichTextEditor/RichTextEditor.js.map +0 -1
  1348. package/components/RichTextEditor/index.d.ts +0 -2
  1349. package/components/RichTextEditor/index.js +0 -21
  1350. package/components/RichTextEditor/index.js.map +0 -1
  1351. package/components/RichTextEditor/styles.scss +0 -96
  1352. package/components/RichTextEditor/tools/header/index.d.ts +0 -229
  1353. package/components/RichTextEditor/tools/header/index.js +0 -731
  1354. package/components/RichTextEditor/tools/header/index.js.map +0 -1
  1355. package/components/RichTextEditor/tools/header/styles.scss +0 -48
  1356. package/components/RichTextEditor/tools/image/index.d.ts +0 -124
  1357. package/components/RichTextEditor/tools/image/index.js +0 -265
  1358. package/components/RichTextEditor/tools/image/index.js.map +0 -1
  1359. package/components/RichTextEditor/tools/image/styles.scss +0 -90
  1360. package/components/RichTextEditor/tools/image/svgs.d.ts +0 -6
  1361. package/components/RichTextEditor/tools/image/svgs.js +0 -12
  1362. package/components/RichTextEditor/tools/image/svgs.js.map +0 -1
  1363. package/components/RichTextEditor/tools/image/tunes.d.ts +0 -57
  1364. package/components/RichTextEditor/tools/image/tunes.js +0 -146
  1365. package/components/RichTextEditor/tools/image/tunes.js.map +0 -1
  1366. package/components/RichTextEditor/tools/image/types.d.ts +0 -29
  1367. package/components/RichTextEditor/tools/image/types.js +0 -5
  1368. package/components/RichTextEditor/tools/image/types.js.map +0 -1
  1369. package/components/RichTextEditor/tools/image/ui.d.ts +0 -122
  1370. package/components/RichTextEditor/tools/image/ui.js +0 -301
  1371. package/components/RichTextEditor/tools/image/ui.js.map +0 -1
  1372. package/components/RichTextEditor/tools/paragraph/index.d.ts +0 -209
  1373. package/components/RichTextEditor/tools/paragraph/index.js +0 -514
  1374. package/components/RichTextEditor/tools/paragraph/index.js.map +0 -1
  1375. package/components/RichTextEditor/tools/paragraph/styles.scss +0 -29
  1376. package/components/RichTextEditor/tools/textColor/index.d.ts +0 -64
  1377. package/components/RichTextEditor/tools/textColor/index.js +0 -271
  1378. package/components/RichTextEditor/tools/textColor/index.js.map +0 -1
  1379. package/components/RichTextEditor/tools/textColor/styles.scss +0 -21
  1380. package/components/RichTextEditor/tools/utils.d.ts +0 -19
  1381. package/components/RichTextEditor/tools/utils.js +0 -33
  1382. package/components/RichTextEditor/tools/utils.js.map +0 -1
  1383. package/components/Routes.d.ts +0 -6
  1384. package/components/Routes.js +0 -49
  1385. package/components/Routes.js.map +0 -1
  1386. package/components/SimpleForm/index.js.map +0 -1
  1387. package/components/SplitView/index.js.map +0 -1
  1388. package/components/index.js.map +0 -1
  1389. package/index.js.map +0 -1
  1390. package/plugins/FileManagerFileTypePlugin.d.ts +0 -33
  1391. package/plugins/FileManagerFileTypePlugin.js +0 -63
  1392. package/plugins/FileManagerFileTypePlugin.js.map +0 -1
  1393. package/plugins/PermissionRendererPlugin.d.ts +0 -22
  1394. package/plugins/PermissionRendererPlugin.js +0 -58
  1395. package/plugins/PermissionRendererPlugin.js.map +0 -1
  1396. package/plugins/fileManager/fileDefault.d.ts +0 -2
  1397. package/plugins/fileManager/fileDefault.js +0 -34
  1398. package/plugins/fileManager/fileDefault.js.map +0 -1
  1399. package/plugins/fileManager/fileImage/DeleteAction.d.ts +0 -7
  1400. package/plugins/fileManager/fileImage/DeleteAction.js +0 -83
  1401. package/plugins/fileManager/fileImage/DeleteAction.js.map +0 -1
  1402. package/plugins/fileManager/fileImage/EditAction.d.ts +0 -10
  1403. package/plugins/fileManager/fileImage/EditAction.js +0 -179
  1404. package/plugins/fileManager/fileImage/EditAction.js.map +0 -1
  1405. package/plugins/fileManager/fileImage/index.d.ts +0 -2
  1406. package/plugins/fileManager/fileImage/index.js +0 -46
  1407. package/plugins/fileManager/fileImage/index.js.map +0 -1
  1408. package/plugins/fileManager/icons/edit.svg +0 -17
  1409. package/plugins/fileManager/icons/round-description-24px.svg +0 -1
  1410. package/plugins/fileManager/index.d.ts +0 -2
  1411. package/plugins/fileManager/index.js +0 -21
  1412. package/plugins/fileManager/index.js.map +0 -1
  1413. package/plugins/globalSearch/SearchBar.d.ts +0 -15
  1414. package/plugins/globalSearch/SearchBar.js +0 -273
  1415. package/plugins/globalSearch/SearchBar.js.map +0 -1
  1416. package/plugins/globalSearch/SearchBarDropdown.d.ts +0 -23
  1417. package/plugins/globalSearch/SearchBarDropdown.js +0 -101
  1418. package/plugins/globalSearch/SearchBarDropdown.js.map +0 -1
  1419. package/plugins/globalSearch/icons/round-search-24px.svg +0 -20
  1420. package/plugins/globalSearch/index.d.ts +0 -7
  1421. package/plugins/globalSearch/index.js +0 -16
  1422. package/plugins/globalSearch/index.js.map +0 -1
  1423. package/plugins/globalSearch/styled.d.ts +0 -11
  1424. package/plugins/globalSearch/styled.js +0 -135
  1425. package/plugins/globalSearch/styled.js.map +0 -1
  1426. package/plugins/uiLayoutRenderer/index.d.ts +0 -2
  1427. package/plugins/uiLayoutRenderer/index.js +0 -75
  1428. package/plugins/uiLayoutRenderer/index.js.map +0 -1
  1429. package/styles/material-theme-assignments.scss +0 -346
  1430. package/styles/material.scss +0 -43
  1431. package/styles/theme.scss +0 -46
  1432. package/types.js.map +0 -1
  1433. package/ui/UIElement.d.ts +0 -2
  1434. package/ui/UIElement.js +0 -19
  1435. package/ui/UIElement.js.map +0 -1
  1436. package/ui/UILayout.d.ts +0 -1
  1437. package/ui/UILayout.js +0 -19
  1438. package/ui/UILayout.js.map +0 -1
  1439. package/ui/UIRenderer.d.ts +0 -2
  1440. package/ui/UIRenderer.js +0 -13
  1441. package/ui/UIRenderer.js.map +0 -1
  1442. package/ui/UIView.d.ts +0 -2
  1443. package/ui/UIView.js +0 -25
  1444. package/ui/UIView.js.map +0 -1
  1445. package/ui/elements/AccordionElement.d.ts +0 -27
  1446. package/ui/elements/AccordionElement.js +0 -110
  1447. package/ui/elements/AccordionElement.js.map +0 -1
  1448. package/ui/elements/ButtonElement.d.ts +0 -24
  1449. package/ui/elements/ButtonElement.js +0 -105
  1450. package/ui/elements/ButtonElement.js.map +0 -1
  1451. package/ui/elements/ButtonGroupElement.d.ts +0 -6
  1452. package/ui/elements/ButtonGroupElement.js +0 -67
  1453. package/ui/elements/ButtonGroupElement.js.map +0 -1
  1454. package/ui/elements/GenericElement.d.ts +0 -1
  1455. package/ui/elements/GenericElement.js +0 -18
  1456. package/ui/elements/GenericElement.js.map +0 -1
  1457. package/ui/elements/LabelElement.d.ts +0 -16
  1458. package/ui/elements/LabelElement.js +0 -60
  1459. package/ui/elements/LabelElement.js.map +0 -1
  1460. package/ui/elements/NavigationMenuElement.d.ts +0 -33
  1461. package/ui/elements/NavigationMenuElement.js +0 -177
  1462. package/ui/elements/NavigationMenuElement.js.map +0 -1
  1463. package/ui/elements/PanelElement.d.ts +0 -3
  1464. package/ui/elements/PanelElement.js +0 -33
  1465. package/ui/elements/PanelElement.js.map +0 -1
  1466. package/ui/elements/PlaceholderElement.d.ts +0 -6
  1467. package/ui/elements/PlaceholderElement.js +0 -44
  1468. package/ui/elements/PlaceholderElement.js.map +0 -1
  1469. package/ui/elements/SmallButtonElement.d.ts +0 -6
  1470. package/ui/elements/SmallButtonElement.js +0 -64
  1471. package/ui/elements/SmallButtonElement.js.map +0 -1
  1472. package/ui/elements/TypographyElement.d.ts +0 -13
  1473. package/ui/elements/TypographyElement.js +0 -67
  1474. package/ui/elements/TypographyElement.js.map +0 -1
  1475. package/ui/elements/ViewElement.d.ts +0 -1
  1476. package/ui/elements/ViewElement.js +0 -18
  1477. package/ui/elements/ViewElement.js.map +0 -1
  1478. package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.d.ts +0 -6
  1479. package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.js +0 -60
  1480. package/ui/elements/form/DynamicFieldsetElement/DynamicFieldsetRowElement.js.map +0 -1
  1481. package/ui/elements/form/DynamicFieldsetElement.d.ts +0 -43
  1482. package/ui/elements/form/DynamicFieldsetElement.js +0 -158
  1483. package/ui/elements/form/DynamicFieldsetElement.js.map +0 -1
  1484. package/ui/elements/form/FileManagerElement/EmptyStateElement.d.ts +0 -4
  1485. package/ui/elements/form/FileManagerElement/EmptyStateElement.js +0 -41
  1486. package/ui/elements/form/FileManagerElement/EmptyStateElement.js.map +0 -1
  1487. package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.d.ts +0 -7
  1488. package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.js +0 -95
  1489. package/ui/elements/form/FileManagerElement/EmptyStateElementRenderer.js.map +0 -1
  1490. package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.d.ts +0 -13
  1491. package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.js +0 -123
  1492. package/ui/elements/form/FileManagerElement/FileManagerElementRenderer.js.map +0 -1
  1493. package/ui/elements/form/FileManagerElement/styled.d.ts +0 -20
  1494. package/ui/elements/form/FileManagerElement/styled.js +0 -120
  1495. package/ui/elements/form/FileManagerElement/styled.js.map +0 -1
  1496. package/ui/elements/form/FileManagerElement.d.ts +0 -24
  1497. package/ui/elements/form/FileManagerElement.js +0 -108
  1498. package/ui/elements/form/FileManagerElement.js.map +0 -1
  1499. package/ui/elements/form/FormElement.d.ts +0 -21
  1500. package/ui/elements/form/FormElement.js +0 -66
  1501. package/ui/elements/form/FormElement.js.map +0 -1
  1502. package/ui/elements/form/FormFieldElement.d.ts +0 -56
  1503. package/ui/elements/form/FormFieldElement.js +0 -206
  1504. package/ui/elements/form/FormFieldElement.js.map +0 -1
  1505. package/ui/elements/form/HiddenElement.d.ts +0 -6
  1506. package/ui/elements/form/HiddenElement.js +0 -64
  1507. package/ui/elements/form/HiddenElement.js.map +0 -1
  1508. package/ui/elements/form/InputElement.d.ts +0 -7
  1509. package/ui/elements/form/InputElement.js +0 -72
  1510. package/ui/elements/form/InputElement.js.map +0 -1
  1511. package/ui/elements/form/PasswordElement.d.ts +0 -6
  1512. package/ui/elements/form/PasswordElement.js +0 -66
  1513. package/ui/elements/form/PasswordElement.js.map +0 -1
  1514. package/ui/elements/form/README.md +0 -2
  1515. package/ui/elements/form/SelectElement.d.ts +0 -15
  1516. package/ui/elements/form/SelectElement.js +0 -87
  1517. package/ui/elements/form/SelectElement.js.map +0 -1
  1518. package/ui/elements/form/TextareaElement.d.ts +0 -15
  1519. package/ui/elements/form/TextareaElement.js +0 -72
  1520. package/ui/elements/form/TextareaElement.js.map +0 -1
  1521. package/ui/views/AdminView/ContentElement.d.ts +0 -4
  1522. package/ui/views/AdminView/ContentElement.js +0 -78
  1523. package/ui/views/AdminView/ContentElement.js.map +0 -1
  1524. package/ui/views/AdminView/HeaderElement.d.ts +0 -13
  1525. package/ui/views/AdminView/HeaderElement.js +0 -144
  1526. package/ui/views/AdminView/HeaderElement.js.map +0 -1
  1527. package/ui/views/AdminView/HeaderSectionCenterElement.d.ts +0 -6
  1528. package/ui/views/AdminView/HeaderSectionCenterElement.js +0 -62
  1529. package/ui/views/AdminView/HeaderSectionCenterElement.js.map +0 -1
  1530. package/ui/views/AdminView/HeaderSectionLeftElement.d.ts +0 -6
  1531. package/ui/views/AdminView/HeaderSectionLeftElement.js +0 -62
  1532. package/ui/views/AdminView/HeaderSectionLeftElement.js.map +0 -1
  1533. package/ui/views/AdminView/HeaderSectionRightElement.d.ts +0 -6
  1534. package/ui/views/AdminView/HeaderSectionRightElement.js +0 -62
  1535. package/ui/views/AdminView/HeaderSectionRightElement.js.map +0 -1
  1536. package/ui/views/AdminView/components/Dialog.d.ts +0 -2
  1537. package/ui/views/AdminView/components/Dialog.js +0 -57
  1538. package/ui/views/AdminView/components/Dialog.js.map +0 -1
  1539. package/ui/views/AdminView/components/Hamburger.d.ts +0 -3
  1540. package/ui/views/AdminView/components/Hamburger.js +0 -39
  1541. package/ui/views/AdminView/components/Hamburger.js.map +0 -1
  1542. package/ui/views/AdminView/components/Snackbar.d.ts +0 -3
  1543. package/ui/views/AdminView/components/Snackbar.js +0 -41
  1544. package/ui/views/AdminView/components/Snackbar.js.map +0 -1
  1545. package/ui/views/FormView/FormContainerElement.d.ts +0 -12
  1546. package/ui/views/FormView/FormContainerElement.js +0 -71
  1547. package/ui/views/FormView/FormContainerElement.js.map +0 -1
  1548. package/ui/views/FormView/FormContentElement.d.ts +0 -3
  1549. package/ui/views/FormView/FormContentElement.js +0 -33
  1550. package/ui/views/FormView/FormContentElement.js.map +0 -1
  1551. package/ui/views/FormView/FormFooterElement.d.ts +0 -6
  1552. package/ui/views/FormView/FormFooterElement.js +0 -68
  1553. package/ui/views/FormView/FormFooterElement.js.map +0 -1
  1554. package/ui/views/FormView/FormHeaderElement.d.ts +0 -14
  1555. package/ui/views/FormView/FormHeaderElement.js +0 -103
  1556. package/ui/views/FormView/FormHeaderElement.js.map +0 -1
  1557. package/ui/views/FormView.d.ts +0 -40
  1558. package/ui/views/FormView.js +0 -208
  1559. package/ui/views/FormView.js.map +0 -1
  1560. package/ui/views/OverlayView/ContentElement.d.ts +0 -6
  1561. package/ui/views/OverlayView/ContentElement.js +0 -61
  1562. package/ui/views/OverlayView/ContentElement.js.map +0 -1
  1563. package/ui/views/OverlayView/HeaderElement.d.ts +0 -24
  1564. package/ui/views/OverlayView/HeaderElement.js +0 -134
  1565. package/ui/views/OverlayView/HeaderElement.js.map +0 -1
  1566. package/ui/views/OverlayView/HeaderTitleElement.d.ts +0 -13
  1567. package/ui/views/OverlayView/HeaderTitleElement.js +0 -73
  1568. package/ui/views/OverlayView/HeaderTitleElement.js.map +0 -1
  1569. package/ui/views/OverlayView/useOverlayView.d.ts +0 -5
  1570. package/ui/views/OverlayView/useOverlayView.js +0 -54
  1571. package/ui/views/OverlayView/useOverlayView.js.map +0 -1
  1572. package/ui/views/OverlayView.d.ts +0 -29
  1573. package/ui/views/OverlayView.js +0 -199
  1574. package/ui/views/OverlayView.js.map +0 -1
  1575. package/ui/views/SplitView/SplitViewPanelElement.d.ts +0 -11
  1576. package/ui/views/SplitView/SplitViewPanelElement.js +0 -91
  1577. package/ui/views/SplitView/SplitViewPanelElement.js.map +0 -1
  1578. package/ui/views/SplitView.d.ts +0 -21
  1579. package/ui/views/SplitView.js +0 -162
  1580. package/ui/views/SplitView.js.map +0 -1
@@ -0,0 +1,3811 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { z } from "zod";
3
+ import { Container } from "@webiny/di";
4
+ import { FormModelFeature } from "./feature.js";
5
+ import { FormModelFactory } from "./abstractions.js";
6
+ function createForm(config) {
7
+ const container = new Container();
8
+ FormModelFeature.register(container);
9
+ return container.resolve(FormModelFactory).create(config);
10
+ }
11
+ function asRow(node) {
12
+ if ("row" !== node.type) throw new Error(`Expected row node, got "${node.type}"`);
13
+ return node;
14
+ }
15
+ function createBasicForm() {
16
+ return createForm({
17
+ fields: (fields)=>({
18
+ title: fields.text().label("Title").required("Title is required"),
19
+ path: fields.text().label("Path").required("Path is required")
20
+ })
21
+ });
22
+ }
23
+ describe("FormModel", ()=>{
24
+ describe("field creation", ()=>{
25
+ it("should create fields from builder definitions", ()=>{
26
+ const form = createBasicForm();
27
+ expect(form.field("title")).toBeDefined();
28
+ expect(form.field("path")).toBeDefined();
29
+ });
30
+ it("should throw on unknown field access", ()=>{
31
+ const form = createBasicForm();
32
+ expect(()=>form.field("unknown")).toThrow('Field "unknown" not found.');
33
+ });
34
+ });
35
+ describe("setValue / getValue", ()=>{
36
+ it("should set and get field values", ()=>{
37
+ const form = createBasicForm();
38
+ form.field("title").setValue("Hello");
39
+ expect(form.field("title").getValue()).toBe("Hello");
40
+ });
41
+ it("should default to null when no defaultValue is set", ()=>{
42
+ const form = createBasicForm();
43
+ expect(form.field("title").getValue()).toBeNull();
44
+ });
45
+ it("should use defaultValue when provided", ()=>{
46
+ const form = createForm({
47
+ fields: (fields)=>({
48
+ status: fields.text().defaultValue("draft")
49
+ })
50
+ });
51
+ expect(form.field("status").getValue()).toBe("draft");
52
+ });
53
+ });
54
+ describe("getData / setData", ()=>{
55
+ it("should return all field values including hidden", ()=>{
56
+ const form = createForm({
57
+ fields: (fields)=>({
58
+ title: fields.text().label("Title"),
59
+ pageType: fields.text().hidden().defaultValue("static")
60
+ })
61
+ });
62
+ form.field("title").setValue("Hello");
63
+ const data = form.getData();
64
+ expect(data).toEqual({
65
+ title: "Hello",
66
+ pageType: "static"
67
+ });
68
+ });
69
+ it("should hydrate fields from data object", ()=>{
70
+ const form = createBasicForm();
71
+ form.setData({
72
+ title: "My Page",
73
+ path: "/my-page"
74
+ });
75
+ expect(form.field("title").getValue()).toBe("My Page");
76
+ expect(form.field("path").getValue()).toBe("/my-page");
77
+ });
78
+ it("should ignore unknown fields in setData", ()=>{
79
+ const form = createBasicForm();
80
+ form.setData({
81
+ title: "Test",
82
+ unknown: "value"
83
+ });
84
+ expect(form.field("title").getValue()).toBe("Test");
85
+ expect(()=>form.field("unknown")).toThrow();
86
+ });
87
+ });
88
+ describe("isDirty", ()=>{
89
+ it("should not be dirty initially", ()=>{
90
+ const form = createBasicForm();
91
+ expect(form.isDirty).toBe(false);
92
+ });
93
+ it("should be dirty after setValue", ()=>{
94
+ const form = createBasicForm();
95
+ form.field("title").setValue("Changed");
96
+ expect(form.isDirty).toBe(true);
97
+ });
98
+ it("should not be dirty after setData", ()=>{
99
+ const form = createBasicForm();
100
+ form.setData({
101
+ title: "Loaded",
102
+ path: "/loaded"
103
+ });
104
+ expect(form.isDirty).toBe(false);
105
+ });
106
+ it("should not be dirty after reverting to baseline value", ()=>{
107
+ const form = createBasicForm();
108
+ form.setData({
109
+ title: "Original",
110
+ path: "/original"
111
+ });
112
+ form.field("title").setValue("Changed");
113
+ expect(form.isDirty).toBe(true);
114
+ form.field("title").setValue("Original");
115
+ expect(form.isDirty).toBe(false);
116
+ });
117
+ });
118
+ describe("reset", ()=>{
119
+ it("should revert values to setData baseline", ()=>{
120
+ const form = createBasicForm();
121
+ form.setData({
122
+ title: "Original",
123
+ path: "/original"
124
+ });
125
+ form.field("title").setValue("Changed");
126
+ form.reset();
127
+ expect(form.field("title").getValue()).toBe("Original");
128
+ expect(form.isDirty).toBe(false);
129
+ });
130
+ it("should clear validation state on reset", async ()=>{
131
+ const form = createBasicForm();
132
+ await form.validate();
133
+ expect(form.errors.length).toBeGreaterThan(0);
134
+ form.reset();
135
+ expect(form.errors).toEqual([]);
136
+ expect(form.isValid).toBeNull();
137
+ });
138
+ });
139
+ describe("validation", ()=>{
140
+ it("should fail validation for empty required fields", async ()=>{
141
+ const form = createBasicForm();
142
+ const valid = await form.validate();
143
+ expect(valid).toBe(false);
144
+ expect(form.isValid).toBe(false);
145
+ expect(form.errors).toHaveLength(2);
146
+ expect(form.errors[0].path).toBe("title");
147
+ expect(form.errors[0].message).toBe("Title is required");
148
+ expect(form.errors[1].path).toBe("path");
149
+ });
150
+ it("should pass validation when required fields have values", async ()=>{
151
+ const form = createBasicForm();
152
+ form.field("title").setValue("My Page");
153
+ form.field("path").setValue("/my-page");
154
+ const valid = await form.validate();
155
+ expect(valid).toBe(true);
156
+ expect(form.isValid).toBe(true);
157
+ expect(form.errors).toHaveLength(0);
158
+ });
159
+ it("should validate zod schemas", async ()=>{
160
+ const form = createForm({
161
+ fields: (fields)=>({
162
+ email: fields.text().label("Email").schema(z.string().email("Invalid email"))
163
+ })
164
+ });
165
+ form.field("email").setValue("not-an-email");
166
+ const valid = await form.validate();
167
+ expect(valid).toBe(false);
168
+ expect(form.errors[0].message).toBe("Invalid email");
169
+ });
170
+ it("should run required check before zod schema", async ()=>{
171
+ const form = createForm({
172
+ fields: (fields)=>({
173
+ email: fields.text().label("Email").required("Email is required").schema(z.string().email("Invalid email"))
174
+ })
175
+ });
176
+ const valid = await form.validate();
177
+ expect(valid).toBe(false);
178
+ expect(form.errors[0].message).toBe("Email is required");
179
+ });
180
+ it("should expose field-level validation in field.vm", async ()=>{
181
+ const form = createBasicForm();
182
+ await form.validate();
183
+ const titleVm = form.field("title").vm;
184
+ expect(titleVm.validation.isValid).toBe(false);
185
+ expect(titleVm.validation.message).toBe("Title is required");
186
+ });
187
+ it("isValid should be null before first validation", ()=>{
188
+ const form = createBasicForm();
189
+ expect(form.isValid).toBeNull();
190
+ });
191
+ });
192
+ describe("submit", ()=>{
193
+ it("should return data when valid", async ()=>{
194
+ const form = createBasicForm();
195
+ form.field("title").setValue("My Page");
196
+ form.field("path").setValue("/my-page");
197
+ const result = await form.submit();
198
+ expect(result).toEqual({
199
+ title: "My Page",
200
+ path: "/my-page"
201
+ });
202
+ });
203
+ it("should return false when invalid", async ()=>{
204
+ const form = createBasicForm();
205
+ const result = await form.submit();
206
+ expect(result).toBe(false);
207
+ });
208
+ });
209
+ describe("vm", ()=>{
210
+ it("should expose layout with field VMs", ()=>{
211
+ const form = createBasicForm();
212
+ const vm = form.vm;
213
+ expect(vm.layout).toHaveLength(2);
214
+ expect(vm.layout[0].type).toBe("row");
215
+ expect(asRow(vm.layout[0]).fields[0].name).toBe("title");
216
+ expect(asRow(vm.layout[1]).fields[0].name).toBe("path");
217
+ });
218
+ it("should exclude hidden fields from layout", ()=>{
219
+ const form = createForm({
220
+ fields: (fields)=>({
221
+ title: fields.text().label("Title"),
222
+ pageType: fields.text().hidden().defaultValue("static")
223
+ })
224
+ });
225
+ const vm = form.vm;
226
+ expect(vm.layout).toHaveLength(1);
227
+ expect(asRow(vm.layout[0]).fields[0].name).toBe("title");
228
+ });
229
+ it("should expose isDirty and isValid", ()=>{
230
+ const form = createBasicForm();
231
+ expect(form.vm.isDirty).toBe(false);
232
+ expect(form.vm.isValid).toBeNull();
233
+ });
234
+ it("should expose field onChange that calls setValue", ()=>{
235
+ const form = createBasicForm();
236
+ const fieldVM = asRow(form.vm.layout[0]).fields[0];
237
+ fieldVM.onChange("New Value");
238
+ expect(form.field("title").getValue()).toBe("New Value");
239
+ });
240
+ });
241
+ describe("layout", ()=>{
242
+ it("should generate default layout (one row per non-hidden field)", ()=>{
243
+ const form = createBasicForm();
244
+ expect(form.vm.layout).toHaveLength(2);
245
+ });
246
+ it("should use explicit layout when provided", ()=>{
247
+ const form = createForm({
248
+ fields: (fields)=>({
249
+ title: fields.text().label("Title"),
250
+ path: fields.text().label("Path")
251
+ }),
252
+ layout: (layout)=>[
253
+ layout.row("title", "path")
254
+ ]
255
+ });
256
+ expect(form.vm.layout).toHaveLength(1);
257
+ expect(asRow(form.vm.layout[0]).fields).toHaveLength(2);
258
+ });
259
+ it("should warn about orphan fields in explicit layout", ()=>{
260
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
261
+ createForm({
262
+ fields: (fields)=>({
263
+ title: fields.text().label("Title"),
264
+ path: fields.text().label("Path")
265
+ }),
266
+ layout: (layout)=>[
267
+ layout.row("title")
268
+ ]
269
+ });
270
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Field "path" is not in the layout'));
271
+ warnSpy.mockRestore();
272
+ });
273
+ it("should not warn about hidden orphan fields", ()=>{
274
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
275
+ createForm({
276
+ fields: (fields)=>({
277
+ title: fields.text().label("Title"),
278
+ pageType: fields.text().hidden().defaultValue("static")
279
+ }),
280
+ layout: (layout)=>[
281
+ layout.row("title")
282
+ ]
283
+ });
284
+ expect(warnSpy).not.toHaveBeenCalled();
285
+ warnSpy.mockRestore();
286
+ });
287
+ });
288
+ describe("beforeChange / afterChange", ()=>{
289
+ it("should run beforeChange pipeline in order, transforming value", ()=>{
290
+ const form = createForm({
291
+ fields: (fields)=>({
292
+ path: fields.text().label("Path").beforeChange((value)=>String(value).trim()).beforeChange((value)=>String(value).toLowerCase())
293
+ })
294
+ });
295
+ form.field("path").setValue(" Hello World ");
296
+ expect(form.field("path").getValue()).toBe("hello world");
297
+ });
298
+ it("should run afterChange after value is stored", ()=>{
299
+ const received = [];
300
+ const form = createForm({
301
+ fields: (fields)=>({
302
+ title: fields.text().label("Title").afterChange((value)=>{
303
+ received.push(value);
304
+ })
305
+ })
306
+ });
307
+ form.field("title").setValue("Hello");
308
+ expect(received).toEqual([
309
+ "Hello"
310
+ ]);
311
+ });
312
+ it("should pass transformed value to afterChange", ()=>{
313
+ const received = [];
314
+ const form = createForm({
315
+ fields: (fields)=>({
316
+ path: fields.text().label("Path").beforeChange((value)=>String(value).toLowerCase()).afterChange((value)=>{
317
+ received.push(value);
318
+ })
319
+ })
320
+ });
321
+ form.field("path").setValue("HELLO");
322
+ expect(form.field("path").getValue()).toBe("hello");
323
+ expect(received).toEqual([
324
+ "hello"
325
+ ]);
326
+ });
327
+ it("should not fire afterChange when value does not change (recursion guard)", ()=>{
328
+ const calls = [];
329
+ const form = createForm({
330
+ fields: (fields)=>({
331
+ title: fields.text().label("Title").beforeChange(()=>"constant").afterChange(()=>{
332
+ calls.push("afterChange");
333
+ })
334
+ })
335
+ });
336
+ form.field("title").setValue("anything");
337
+ expect(calls).toEqual([
338
+ "afterChange"
339
+ ]);
340
+ expect(form.field("title").getValue()).toBe("constant");
341
+ form.field("title").setValue("something else");
342
+ expect(calls).toEqual([
343
+ "afterChange"
344
+ ]);
345
+ });
346
+ it("should not trigger beforeChange or afterChange on setData", ()=>{
347
+ const calls = [];
348
+ const form = createForm({
349
+ fields: (fields)=>({
350
+ title: fields.text().label("Title").beforeChange((value)=>{
351
+ calls.push("before");
352
+ return value;
353
+ }).afterChange(()=>{
354
+ calls.push("after");
355
+ })
356
+ })
357
+ });
358
+ form.setData({
359
+ title: "Loaded"
360
+ });
361
+ expect(calls).toEqual([]);
362
+ expect(form.field("title").getValue()).toBe("Loaded");
363
+ });
364
+ it("should support cross-field afterChange triggering target field pipeline", ()=>{
365
+ const form = createForm({
366
+ fields: (fields)=>({
367
+ title: fields.text().label("Title").afterChange((value, f)=>{
368
+ const path = "/" + String(value).toLowerCase().replace(/\s+/g, "-");
369
+ f.field("path").setValue(path);
370
+ }),
371
+ path: fields.text().label("Path").beforeChange((value)=>{
372
+ const str = String(value);
373
+ return str.startsWith("/") ? str : "/" + str;
374
+ })
375
+ })
376
+ });
377
+ form.field("title").setValue("Hello World");
378
+ expect(form.field("path").getValue()).toBe("/hello-world");
379
+ });
380
+ it("should allow appending callbacks to existing fields at runtime", ()=>{
381
+ const form = createForm({
382
+ fields: (fields)=>({
383
+ title: fields.text().label("Title")
384
+ })
385
+ });
386
+ const field = form.field("title");
387
+ field.addBeforeChange((value)=>String(value).toUpperCase());
388
+ field.setValue("hello");
389
+ expect(field.getValue()).toBe("HELLO");
390
+ });
391
+ it("should chain builder callbacks with runtime-appended callbacks", ()=>{
392
+ const form = createForm({
393
+ fields: (fields)=>({
394
+ path: fields.text().label("Path").beforeChange((value)=>String(value).trim())
395
+ })
396
+ });
397
+ form.field("path").addBeforeChange((value)=>String(value).toLowerCase());
398
+ form.field("path").setValue(" HELLO ");
399
+ expect(form.field("path").getValue()).toBe("hello");
400
+ });
401
+ it("should demonstrate title→path with path-dirty tracking", ()=>{
402
+ const form = createForm({
403
+ fields: (fields)=>({
404
+ title: fields.text().label("Title").required("Title is required").afterChange((value, f)=>{
405
+ if (f.field("path").getValue()) return;
406
+ const slug = String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
407
+ f.field("path").setValue("/" + slug);
408
+ }),
409
+ path: fields.text().label("Path").required("Path is required").beforeChange((value)=>{
410
+ const str = String(value);
411
+ return "/" + str.replace(/^\//, "").toLowerCase().replace(/[^a-z0-9/-]+/g, "-").replace(/^-|-$/g, "");
412
+ })
413
+ }),
414
+ layout: (layout)=>[
415
+ layout.row("title"),
416
+ layout.row("path")
417
+ ]
418
+ });
419
+ form.field("title").setValue("Hello World");
420
+ expect(form.field("path").getValue()).toBe("/hello-world");
421
+ form.field("path").setValue("/custom-path");
422
+ expect(form.field("path").getValue()).toBe("/custom-path");
423
+ form.field("title").setValue("New Title");
424
+ expect(form.field("path").getValue()).toBe("/custom-path");
425
+ });
426
+ });
427
+ describe("field with options", ()=>{
428
+ it("should resolve static options in field VM", ()=>{
429
+ const form = createForm({
430
+ fields: (fields)=>({
431
+ lang: fields.text().label("Language").options([
432
+ {
433
+ label: "English",
434
+ value: "en"
435
+ },
436
+ {
437
+ label: "German",
438
+ value: "de"
439
+ }
440
+ ])
441
+ })
442
+ });
443
+ const fieldVM = asRow(form.vm.layout[0]).fields[0];
444
+ expect(fieldVM.options).toEqual([
445
+ {
446
+ label: "English",
447
+ value: "en"
448
+ },
449
+ {
450
+ label: "German",
451
+ value: "de"
452
+ }
453
+ ]);
454
+ });
455
+ it("should resolve reactive options function in field VM", ()=>{
456
+ const form = createForm({
457
+ fields: (fields)=>({
458
+ lang: fields.text().label("Language").options(()=>[
459
+ {
460
+ label: "Dynamic",
461
+ value: "dynamic"
462
+ }
463
+ ])
464
+ })
465
+ });
466
+ const fieldVM = asRow(form.vm.layout[0]).fields[0];
467
+ expect(fieldVM.options).toEqual([
468
+ {
469
+ label: "Dynamic",
470
+ value: "dynamic"
471
+ }
472
+ ]);
473
+ });
474
+ });
475
+ describe("modifiers (Phase 3)", ()=>{
476
+ describe("form.fields() — add / replace / remove", ()=>{
477
+ it("should add a new field via form.fields()", ()=>{
478
+ const form = createBasicForm();
479
+ form.fields((fields)=>({
480
+ language: fields.text().label("Language").options([
481
+ {
482
+ label: "English",
483
+ value: "en"
484
+ },
485
+ {
486
+ label: "German",
487
+ value: "de"
488
+ }
489
+ ])
490
+ }));
491
+ expect(form.field("language")).toBeDefined();
492
+ expect(form.field("language").type).toBe("text");
493
+ expect(form.getData()).toHaveProperty("language");
494
+ });
495
+ it("should add a field that appears in getData but not layout until positioned", ()=>{
496
+ const form = createForm({
497
+ fields: (fields)=>({
498
+ title: fields.text().label("Title")
499
+ })
500
+ });
501
+ form.fields((fields)=>({
502
+ description: fields.text().label("Description")
503
+ }));
504
+ expect(form.getData()).toHaveProperty("description");
505
+ const fieldNames = form.vm.layout.map((row)=>asRow(row).fields[0].name);
506
+ expect(fieldNames).not.toContain("description");
507
+ form.layout((layout)=>[
508
+ layout.row("description").after("title")
509
+ ]);
510
+ const updatedNames = form.vm.layout.map((row)=>asRow(row).fields[0].name);
511
+ expect(updatedNames).toEqual([
512
+ "title",
513
+ "description"
514
+ ]);
515
+ });
516
+ it("should replace an existing field when key matches", ()=>{
517
+ const form = createBasicForm();
518
+ form.field("title").setValue("Old Value");
519
+ form.fields((fields)=>({
520
+ title: fields.text().label("Replaced Title").placeholder("New placeholder")
521
+ }));
522
+ expect(form.field("title").config.label).toBe("Replaced Title");
523
+ expect(form.field("title").config.placeholder).toBe("New placeholder");
524
+ expect(form.field("title").getValue()).toBeNull();
525
+ });
526
+ it("should remove a field via undefined", ()=>{
527
+ const form = createBasicForm();
528
+ form.fields(()=>({
529
+ path: void 0
530
+ }));
531
+ expect(()=>form.field("path")).toThrow('Field "path" not found.');
532
+ expect(form.getData()).not.toHaveProperty("path");
533
+ });
534
+ it("should remove a field via field.remove()", ()=>{
535
+ const form = createBasicForm();
536
+ form.field("path").remove();
537
+ expect(()=>form.field("path")).toThrow('Field "path" not found.');
538
+ expect(form.getData()).not.toHaveProperty("path");
539
+ });
540
+ it("should remove field from layout when removed via field.remove()", ()=>{
541
+ const form = createBasicForm();
542
+ expect(form.vm.layout).toHaveLength(2);
543
+ form.field("path").remove();
544
+ expect(form.vm.layout).toHaveLength(1);
545
+ expect(asRow(form.vm.layout[0]).fields[0].name).toBe("title");
546
+ });
547
+ it("should handle add + remove in the same fields() call", ()=>{
548
+ const form = createBasicForm();
549
+ form.fields((fields)=>({
550
+ path: void 0,
551
+ slug: fields.text().label("Slug")
552
+ }));
553
+ expect(()=>form.field("path")).toThrow();
554
+ expect(form.field("slug")).toBeDefined();
555
+ });
556
+ });
557
+ describe("form.field().setDisabled()", ()=>{
558
+ it("should disable a field via setDisabled(true)", ()=>{
559
+ const form = createBasicForm();
560
+ form.field("title").setDisabled(true);
561
+ expect(form.field("title").vm.disabled).toBe(true);
562
+ });
563
+ it("should re-enable a field via setDisabled(false)", ()=>{
564
+ const form = createBasicForm();
565
+ form.field("title").setDisabled(true);
566
+ form.field("title").setDisabled(false);
567
+ expect(form.field("title").vm.disabled).toBe(false);
568
+ });
569
+ });
570
+ describe("form.field().as() — type narrowing", ()=>{
571
+ it("should return the field when type matches", ()=>{
572
+ const form = createForm({
573
+ fields: (fields)=>({
574
+ lang: fields.text().label("Language").options([
575
+ {
576
+ label: "English",
577
+ value: "en"
578
+ }
579
+ ])
580
+ })
581
+ });
582
+ const textField = form.field("lang").as("text");
583
+ expect(textField).toBe(form.field("lang"));
584
+ });
585
+ it("should throw when type does not match", ()=>{
586
+ const form = createBasicForm();
587
+ expect(()=>form.field("title").as("boolean")).toThrow('Field "title" is type "text", not "boolean".');
588
+ });
589
+ });
590
+ describe("modifier appends callbacks to existing fields", ()=>{
591
+ it("should append beforeChange to an existing field", ()=>{
592
+ const form = createForm({
593
+ fields: (fields)=>({
594
+ path: fields.text().label("Path").beforeChange((value)=>String(value).trim())
595
+ })
596
+ });
597
+ form.field("path").addBeforeChange((value)=>String(value).toLowerCase());
598
+ form.field("path").setValue(" HELLO ");
599
+ expect(form.field("path").getValue()).toBe("hello");
600
+ });
601
+ it("should append afterChange to an existing field", ()=>{
602
+ const received = [];
603
+ const form = createForm({
604
+ fields: (fields)=>({
605
+ title: fields.text().label("Title"),
606
+ path: fields.text().label("Path")
607
+ })
608
+ });
609
+ form.field("title").addAfterChange((value, f)=>{
610
+ received.push(value);
611
+ f.field("path").setValue("/" + String(value).toLowerCase());
612
+ });
613
+ form.field("title").setValue("Hello");
614
+ expect(received).toEqual([
615
+ "Hello"
616
+ ]);
617
+ expect(form.field("path").getValue()).toBe("/hello");
618
+ });
619
+ });
620
+ describe("layout positional modifiers", ()=>{
621
+ function createFormWithLayout() {
622
+ return createForm({
623
+ fields: (fields)=>({
624
+ title: fields.text().label("Title"),
625
+ path: fields.text().label("Path"),
626
+ description: fields.text().label("Description")
627
+ }),
628
+ layout: (layout)=>[
629
+ layout.row("title"),
630
+ layout.row("path"),
631
+ layout.row("description")
632
+ ]
633
+ });
634
+ }
635
+ it("should insert a row before a target", ()=>{
636
+ const form = createFormWithLayout();
637
+ form.fields((fields)=>({
638
+ language: fields.text().label("Language").options([])
639
+ }));
640
+ form.layout((layout)=>[
641
+ layout.row("language").before("path")
642
+ ]);
643
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
644
+ expect(names).toEqual([
645
+ "title",
646
+ "language",
647
+ "path",
648
+ "description"
649
+ ]);
650
+ });
651
+ it("should insert a row after a target", ()=>{
652
+ const form = createFormWithLayout();
653
+ form.fields((fields)=>({
654
+ language: fields.text().label("Language").options([])
655
+ }));
656
+ form.layout((layout)=>[
657
+ layout.row("language").after("path")
658
+ ]);
659
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
660
+ expect(names).toEqual([
661
+ "title",
662
+ "path",
663
+ "language",
664
+ "description"
665
+ ]);
666
+ });
667
+ it("should replace a target row", ()=>{
668
+ const form = createFormWithLayout();
669
+ form.fields((fields)=>({
670
+ slug: fields.text().label("Slug")
671
+ }));
672
+ form.layout((layout)=>[
673
+ layout.row("slug").replace("path")
674
+ ]);
675
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
676
+ expect(names).toEqual([
677
+ "title",
678
+ "slug",
679
+ "description"
680
+ ]);
681
+ });
682
+ it("should remove a field from layout", ()=>{
683
+ const form = createFormWithLayout();
684
+ form.layout((layout)=>{
685
+ layout.remove("path");
686
+ return [];
687
+ });
688
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
689
+ expect(names).toEqual([
690
+ "title",
691
+ "description"
692
+ ]);
693
+ });
694
+ it("should append when no position is specified", ()=>{
695
+ const form = createFormWithLayout();
696
+ form.fields((fields)=>({
697
+ language: fields.text().label("Language").options([])
698
+ }));
699
+ form.layout((layout)=>[
700
+ layout.row("language")
701
+ ]);
702
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
703
+ expect(names).toEqual([
704
+ "title",
705
+ "path",
706
+ "description",
707
+ "language"
708
+ ]);
709
+ });
710
+ it("should append when target is not found", ()=>{
711
+ const form = createFormWithLayout();
712
+ form.fields((fields)=>({
713
+ language: fields.text().label("Language").options([])
714
+ }));
715
+ form.layout((layout)=>[
716
+ layout.row("language").after("nonexistent")
717
+ ]);
718
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
719
+ expect(names).toEqual([
720
+ "title",
721
+ "path",
722
+ "description",
723
+ "language"
724
+ ]);
725
+ });
726
+ });
727
+ describe("IFormModifier integration", ()=>{
728
+ it("should support a full modifier workflow: add field + position in layout + append callbacks", ()=>{
729
+ const form = createForm({
730
+ fields: (fields)=>({
731
+ title: fields.text().label("Title").required("Title is required"),
732
+ path: fields.text().label("Path").required("Path is required").beforeChange((value)=>{
733
+ const str = String(value);
734
+ return "/" + str.replace(/^\//, "").toLowerCase().replace(/[^a-z0-9/-]+/g, "-").replace(/^-|-$/g, "");
735
+ })
736
+ }),
737
+ layout: (layout)=>[
738
+ layout.row("title"),
739
+ layout.row("path")
740
+ ]
741
+ });
742
+ const modifier = {
743
+ modify (form) {
744
+ form.fields((fields)=>({
745
+ language: fields.text().label("Language").options([
746
+ {
747
+ label: "English",
748
+ value: "en"
749
+ },
750
+ {
751
+ label: "German",
752
+ value: "de"
753
+ }
754
+ ]).afterChange((value, f)=>{
755
+ const current = String(f.field("path").getValue() || "");
756
+ const stripped = current.replace(/^\/[a-z]{2}\//, "/");
757
+ if (value && "en" !== value) f.field("path").setValue("/" + value + stripped);
758
+ else f.field("path").setValue(stripped);
759
+ })
760
+ }));
761
+ form.layout((layout)=>[
762
+ layout.row("language").after("path")
763
+ ]);
764
+ }
765
+ };
766
+ modifier.modify(form);
767
+ const names = form.vm.layout.map((row)=>asRow(row).fields[0].name);
768
+ expect(names).toEqual([
769
+ "title",
770
+ "path",
771
+ "language"
772
+ ]);
773
+ form.field("path").setValue("/demo");
774
+ form.field("language").setValue("de");
775
+ expect(form.field("path").getValue()).toBe("/de/demo");
776
+ const data = form.getData();
777
+ expect(data.language).toBe("de");
778
+ });
779
+ });
780
+ });
781
+ describe("layout system expansion (Phase 5)", ()=>{
782
+ describe("separator", ()=>{
783
+ it("should include separator nodes in the resolved layout", ()=>{
784
+ const form = createForm({
785
+ fields: (fields)=>({
786
+ title: fields.text().label("Title"),
787
+ description: fields.text().label("Description")
788
+ }),
789
+ layout: (layout)=>[
790
+ layout.row("title"),
791
+ layout.separator(),
792
+ layout.row("description")
793
+ ]
794
+ });
795
+ const vm = form.vm;
796
+ expect(vm.layout).toHaveLength(3);
797
+ expect(vm.layout[0].type).toBe("row");
798
+ expect(vm.layout[1].type).toBe("separator");
799
+ expect(vm.layout[2].type).toBe("row");
800
+ });
801
+ it("should support separator via modifier layout API", ()=>{
802
+ const form = createForm({
803
+ fields: (fields)=>({
804
+ title: fields.text().label("Title"),
805
+ description: fields.text().label("Description")
806
+ }),
807
+ layout: (layout)=>[
808
+ layout.row("title"),
809
+ layout.row("description")
810
+ ]
811
+ });
812
+ form.layout((layout)=>[
813
+ layout.separator().after("title")
814
+ ]);
815
+ expect(form.vm.layout).toHaveLength(3);
816
+ expect(form.vm.layout[1].type).toBe("separator");
817
+ });
818
+ });
819
+ describe("tabs", ()=>{
820
+ function createFormWithTabs() {
821
+ return createForm({
822
+ fields: (fields)=>({
823
+ title: fields.text().label("Title"),
824
+ slug: fields.text().label("Slug"),
825
+ description: fields.text().label("Description"),
826
+ metaTitle: fields.text().label("Meta Title"),
827
+ metaDescription: fields.text().label("Meta Description")
828
+ }),
829
+ layout: (layout)=>[
830
+ layout.row("title", "slug"),
831
+ layout.tabs("settings").tab("general", (tab)=>{
832
+ tab.label("General").layout((layout)=>[
833
+ layout.row("description")
834
+ ]);
835
+ }).tab("seo", (tab)=>{
836
+ tab.label("SEO").description("Optimize how this page appears in search").layout((layout)=>[
837
+ layout.row("metaTitle"),
838
+ layout.row("metaDescription")
839
+ ]);
840
+ })
841
+ ]
842
+ });
843
+ }
844
+ it("should resolve tabs layout node with tab definitions", ()=>{
845
+ const form = createFormWithTabs();
846
+ const vm = form.vm;
847
+ expect(vm.layout).toHaveLength(2);
848
+ expect(vm.layout[0].type).toBe("row");
849
+ expect(vm.layout[1].type).toBe("tabs");
850
+ const tabsNode = vm.layout[1];
851
+ expect(tabsNode.id).toBe("settings");
852
+ expect(tabsNode.tabs).toHaveLength(2);
853
+ expect(tabsNode.tabs[0].id).toBe("general");
854
+ expect(tabsNode.tabs[0].label).toBe("General");
855
+ expect(tabsNode.tabs[1].id).toBe("seo");
856
+ expect(tabsNode.tabs[1].label).toBe("SEO");
857
+ expect(tabsNode.tabs[1].description).toBe("Optimize how this page appears in search");
858
+ });
859
+ it("should resolve fields inside tab layouts", ()=>{
860
+ const form = createFormWithTabs();
861
+ const tabsNode = form.vm.layout[1];
862
+ const generalTab = tabsNode.tabs[0];
863
+ expect(generalTab.layout).toHaveLength(1);
864
+ expect(generalTab.layout[0].type).toBe("row");
865
+ const generalRow = generalTab.layout[0];
866
+ expect(generalRow.fields[0].name).toBe("description");
867
+ const seoTab = tabsNode.tabs[1];
868
+ expect(seoTab.layout).toHaveLength(2);
869
+ });
870
+ it("should default activeTabId to the first tab", ()=>{
871
+ const form = createFormWithTabs();
872
+ const tabsNode = form.vm.layout[1];
873
+ expect(tabsNode.activeTabId).toBe("general");
874
+ });
875
+ it("should switch active tab via setActiveTab", ()=>{
876
+ const form = createFormWithTabs();
877
+ let tabsNode = form.vm.layout[1];
878
+ tabsNode.setActiveTab("seo");
879
+ tabsNode = form.vm.layout[1];
880
+ expect(tabsNode.activeTabId).toBe("seo");
881
+ });
882
+ it("should fall back to first tab when active tab ID is invalid", ()=>{
883
+ const form = createFormWithTabs();
884
+ let tabsNode = form.vm.layout[1];
885
+ tabsNode.setActiveTab("nonexistent");
886
+ tabsNode = form.vm.layout[1];
887
+ expect(tabsNode.activeTabId).toBe("general");
888
+ });
889
+ it("should compute hasErrors for tabs based on referenced fields", async ()=>{
890
+ const form = createForm({
891
+ fields: (fields)=>({
892
+ title: fields.text().label("Title").required("Title is required"),
893
+ metaTitle: fields.text().label("Meta Title").required("Required")
894
+ }),
895
+ layout: (layout)=>[
896
+ layout.tabs("settings").tab("general", (tab)=>{
897
+ tab.label("General").layout((layout)=>[
898
+ layout.row("title")
899
+ ]);
900
+ }).tab("seo", (tab)=>{
901
+ tab.label("SEO").layout((layout)=>[
902
+ layout.row("metaTitle")
903
+ ]);
904
+ })
905
+ ]
906
+ });
907
+ let tabsNode = form.vm.layout[0];
908
+ expect(tabsNode.tabs[0].hasErrors).toBe(false);
909
+ expect(tabsNode.tabs[1].hasErrors).toBe(false);
910
+ form.field("title").setValue("Hello");
911
+ await form.validate();
912
+ tabsNode = form.vm.layout[0];
913
+ expect(tabsNode.tabs[0].hasErrors).toBe(false);
914
+ expect(tabsNode.tabs[1].hasErrors).toBe(true);
915
+ });
916
+ it("should not warn about fields inside tabs as orphans", ()=>{
917
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(()=>{});
918
+ createForm({
919
+ fields: (fields)=>({
920
+ title: fields.text().label("Title"),
921
+ description: fields.text().label("Description")
922
+ }),
923
+ layout: (layout)=>[
924
+ layout.row("title"),
925
+ layout.tabs("settings").tab("general", (tab)=>{
926
+ tab.label("General").layout((layout)=>[
927
+ layout.row("description")
928
+ ]);
929
+ })
930
+ ]
931
+ });
932
+ expect(warnSpy).not.toHaveBeenCalled();
933
+ warnSpy.mockRestore();
934
+ });
935
+ it("should return null for tabs with empty tabs array", ()=>{
936
+ const form = createForm({
937
+ fields: (fields)=>({
938
+ title: fields.text().label("Title")
939
+ }),
940
+ layout: (layout)=>[
941
+ layout.row("title"),
942
+ layout.tabs("empty")
943
+ ]
944
+ });
945
+ expect(form.vm.layout).toHaveLength(1);
946
+ expect(form.vm.layout[0].type).toBe("row");
947
+ });
948
+ });
949
+ describe("element", ()=>{
950
+ it("should include element nodes in the resolved layout", ()=>{
951
+ const form = createForm({
952
+ fields: (fields)=>({
953
+ title: fields.text().label("Title")
954
+ }),
955
+ layout: (layout)=>[
956
+ layout.row("title"),
957
+ layout.element("usage-stats", {
958
+ plan: "enterprise"
959
+ })
960
+ ]
961
+ });
962
+ const vm = form.vm;
963
+ expect(vm.layout).toHaveLength(2);
964
+ expect(vm.layout[1].type).toBe("element");
965
+ const elementNode = vm.layout[1];
966
+ expect(elementNode.renderer).toBe("usage-stats");
967
+ expect(elementNode.props).toEqual({
968
+ plan: "enterprise"
969
+ });
970
+ });
971
+ it("should support element without props", ()=>{
972
+ const form = createForm({
973
+ fields: (fields)=>({
974
+ title: fields.text().label("Title")
975
+ }),
976
+ layout: (layout)=>[
977
+ layout.row("title"),
978
+ layout.element("divider")
979
+ ]
980
+ });
981
+ const elementNode = form.vm.layout[1];
982
+ expect(elementNode.renderer).toBe("divider");
983
+ expect(elementNode.props).toBeUndefined();
984
+ });
985
+ });
986
+ describe("named layout node access — form.layout(nodeId)", ()=>{
987
+ function createFormWithTabs() {
988
+ return createForm({
989
+ fields: (fields)=>({
990
+ title: fields.text().label("Title"),
991
+ description: fields.text().label("Description"),
992
+ metaTitle: fields.text().label("Meta Title")
993
+ }),
994
+ layout: (layout)=>[
995
+ layout.row("title"),
996
+ layout.tabs("settings").tab("general", (tab)=>{
997
+ tab.label("General").layout((layout)=>[
998
+ layout.row("description")
999
+ ]);
1000
+ }).tab("seo", (tab)=>{
1001
+ tab.label("SEO").layout((layout)=>[
1002
+ layout.row("metaTitle")
1003
+ ]);
1004
+ })
1005
+ ]
1006
+ });
1007
+ }
1008
+ it("should access a tabs node by ID and add a new tab", ()=>{
1009
+ const form = createFormWithTabs();
1010
+ form.fields((fields)=>({
1011
+ trackingId: fields.text().label("Tracking ID")
1012
+ }));
1013
+ form.layout("settings").as("tabs").tab("analytics", (tab)=>{
1014
+ tab.label("Analytics").layout((layout)=>[
1015
+ layout.row("trackingId")
1016
+ ]);
1017
+ }).after("seo");
1018
+ const tabsNode = form.vm.layout[1];
1019
+ expect(tabsNode.tabs).toHaveLength(3);
1020
+ expect(tabsNode.tabs[2].id).toBe("analytics");
1021
+ expect(tabsNode.tabs[2].label).toBe("Analytics");
1022
+ });
1023
+ it("should add a tab before an existing tab", ()=>{
1024
+ const form = createFormWithTabs();
1025
+ form.fields((fields)=>({
1026
+ trackingId: fields.text().label("Tracking ID")
1027
+ }));
1028
+ form.layout("settings").as("tabs").tab("analytics", (tab)=>{
1029
+ tab.label("Analytics").layout((layout)=>[
1030
+ layout.row("trackingId")
1031
+ ]);
1032
+ }).before("seo");
1033
+ const tabsNode = form.vm.layout[1];
1034
+ expect(tabsNode.tabs).toHaveLength(3);
1035
+ expect(tabsNode.tabs[0].id).toBe("general");
1036
+ expect(tabsNode.tabs[1].id).toBe("analytics");
1037
+ expect(tabsNode.tabs[2].id).toBe("seo");
1038
+ });
1039
+ it("should throw when accessing a non-existent node ID", ()=>{
1040
+ const form = createFormWithTabs();
1041
+ expect(()=>form.layout("nonexistent").as("tabs")).toThrow('Layout node "nonexistent" not found.');
1042
+ });
1043
+ });
1044
+ describe("positional modifiers targeting tabs/element nodes", ()=>{
1045
+ it("should insert a row before a tabs node by ID", ()=>{
1046
+ const form = createForm({
1047
+ fields: (fields)=>({
1048
+ title: fields.text().label("Title"),
1049
+ subtitle: fields.text().label("Subtitle"),
1050
+ description: fields.text().label("Description")
1051
+ }),
1052
+ layout: (layout)=>[
1053
+ layout.row("title"),
1054
+ layout.tabs("settings").tab("general", (tab)=>{
1055
+ tab.label("General").layout((layout)=>[
1056
+ layout.row("description")
1057
+ ]);
1058
+ })
1059
+ ]
1060
+ });
1061
+ form.layout((layout)=>[
1062
+ layout.row("subtitle").before("settings")
1063
+ ]);
1064
+ expect(form.vm.layout).toHaveLength(3);
1065
+ expect(form.vm.layout[0].type).toBe("row");
1066
+ expect(form.vm.layout[1].type).toBe("row");
1067
+ expect(form.vm.layout[2].type).toBe("tabs");
1068
+ const row = form.vm.layout[1];
1069
+ expect(row.fields[0].name).toBe("subtitle");
1070
+ });
1071
+ it("should remove a tabs node by ID", ()=>{
1072
+ const form = createForm({
1073
+ fields: (fields)=>({
1074
+ title: fields.text().label("Title"),
1075
+ description: fields.text().label("Description")
1076
+ }),
1077
+ layout: (layout)=>[
1078
+ layout.row("title"),
1079
+ layout.tabs("settings").tab("general", (tab)=>{
1080
+ tab.label("General").layout((layout)=>[
1081
+ layout.row("description")
1082
+ ]);
1083
+ })
1084
+ ]
1085
+ });
1086
+ form.layout((layout)=>{
1087
+ layout.remove("settings");
1088
+ return [];
1089
+ });
1090
+ expect(form.vm.layout).toHaveLength(1);
1091
+ expect(form.vm.layout[0].type).toBe("row");
1092
+ });
1093
+ it("should replace a tabs node by ID", ()=>{
1094
+ const form = createForm({
1095
+ fields: (fields)=>({
1096
+ title: fields.text().label("Title"),
1097
+ description: fields.text().label("Description"),
1098
+ metaTitle: fields.text().label("Meta Title")
1099
+ }),
1100
+ layout: (layout)=>[
1101
+ layout.row("title"),
1102
+ layout.tabs("settings").tab("general", (tab)=>{
1103
+ tab.label("General").layout((layout)=>[
1104
+ layout.row("description")
1105
+ ]);
1106
+ })
1107
+ ]
1108
+ });
1109
+ form.layout((layout)=>[
1110
+ layout.row("metaTitle").replace("settings")
1111
+ ]);
1112
+ expect(form.vm.layout).toHaveLength(2);
1113
+ expect(form.vm.layout[0].type).toBe("row");
1114
+ expect(form.vm.layout[1].type).toBe("row");
1115
+ const row = form.vm.layout[1];
1116
+ expect(row.fields[0].name).toBe("metaTitle");
1117
+ });
1118
+ });
1119
+ describe("modifier integration with tabs", ()=>{
1120
+ it("should support a full modifier workflow: base form with tabs + modifier adds tab + modifier appends to existing tab", ()=>{
1121
+ const form = createForm({
1122
+ fields: (fields)=>({
1123
+ title: fields.text().label("Title"),
1124
+ description: fields.text().label("Description"),
1125
+ metaTitle: fields.text().label("Meta Title")
1126
+ }),
1127
+ layout: (layout)=>[
1128
+ layout.row("title"),
1129
+ layout.separator(),
1130
+ layout.tabs("settings").tab("general", (tab)=>{
1131
+ tab.label("General").layout((layout)=>[
1132
+ layout.row("description")
1133
+ ]);
1134
+ }).tab("seo", (tab)=>{
1135
+ tab.label("SEO").layout((layout)=>[
1136
+ layout.row("metaTitle")
1137
+ ]);
1138
+ })
1139
+ ]
1140
+ });
1141
+ form.fields((fields)=>({
1142
+ trackingId: fields.text().label("Tracking ID")
1143
+ }));
1144
+ form.layout("settings").as("tabs").tab("analytics", (tab)=>{
1145
+ tab.label("Analytics").layout((layout)=>[
1146
+ layout.row("trackingId")
1147
+ ]);
1148
+ }).after("seo");
1149
+ const vm = form.vm;
1150
+ expect(vm.layout).toHaveLength(3);
1151
+ expect(vm.layout[0].type).toBe("row");
1152
+ expect(vm.layout[1].type).toBe("separator");
1153
+ expect(vm.layout[2].type).toBe("tabs");
1154
+ const tabsNode = vm.layout[2];
1155
+ expect(tabsNode.tabs).toHaveLength(3);
1156
+ expect(tabsNode.tabs[0].id).toBe("general");
1157
+ expect(tabsNode.tabs[1].id).toBe("seo");
1158
+ expect(tabsNode.tabs[2].id).toBe("analytics");
1159
+ const seoTab = tabsNode.tabs[1];
1160
+ expect(seoTab.layout).toHaveLength(1);
1161
+ const data = form.getData();
1162
+ expect(data).toHaveProperty("trackingId");
1163
+ });
1164
+ });
1165
+ });
1166
+ describe("object fields (Phase 6)", ()=>{
1167
+ describe("non-list object field", ()=>{
1168
+ function createFormWithObject() {
1169
+ return createForm({
1170
+ fields: (fields)=>({
1171
+ title: fields.text().label("Title"),
1172
+ address: fields.object().label("Address").fields((f)=>({
1173
+ street: f.text().label("Street").required("Street is required"),
1174
+ city: f.text().label("City").required("City is required")
1175
+ }))
1176
+ }),
1177
+ layout: (layout)=>[
1178
+ layout.row("title"),
1179
+ layout.row("address")
1180
+ ]
1181
+ });
1182
+ }
1183
+ it("should create an object field with children", ()=>{
1184
+ const form = createFormWithObject();
1185
+ const field = form.field("address");
1186
+ expect(field).toBeDefined();
1187
+ expect(field.type).toBe("object");
1188
+ });
1189
+ it("should access child fields via dot-notation", ()=>{
1190
+ const form = createFormWithObject();
1191
+ const street = form.field("address.street");
1192
+ expect(street).toBeDefined();
1193
+ expect(street.type).toBe("text");
1194
+ });
1195
+ it("should throw on invalid dot-notation", ()=>{
1196
+ const form = createFormWithObject();
1197
+ expect(()=>form.field("address.zipcode")).toThrow('Field "address.zipcode" not found.');
1198
+ });
1199
+ it("should return nested object from getData", ()=>{
1200
+ const form = createFormWithObject();
1201
+ form.field("address.street").setValue("123 Main St");
1202
+ form.field("address.city").setValue("Springfield");
1203
+ const data = form.getData();
1204
+ expect(data.address).toEqual({
1205
+ street: "123 Main St",
1206
+ city: "Springfield"
1207
+ });
1208
+ });
1209
+ it("should hydrate children from setData", ()=>{
1210
+ const form = createFormWithObject();
1211
+ form.setData({
1212
+ title: "Home",
1213
+ address: {
1214
+ street: "456 Oak Ave",
1215
+ city: "Portland"
1216
+ }
1217
+ });
1218
+ expect(form.field("address.street").getValue()).toBe("456 Oak Ave");
1219
+ expect(form.field("address.city").getValue()).toBe("Portland");
1220
+ });
1221
+ it("should not be dirty after setData", ()=>{
1222
+ const form = createFormWithObject();
1223
+ form.setData({
1224
+ title: "Home",
1225
+ address: {
1226
+ street: "456 Oak Ave",
1227
+ city: "Portland"
1228
+ }
1229
+ });
1230
+ expect(form.isDirty).toBe(false);
1231
+ });
1232
+ it("should be dirty after changing a child field", ()=>{
1233
+ const form = createFormWithObject();
1234
+ form.setData({
1235
+ title: "Home",
1236
+ address: {
1237
+ street: "456 Oak Ave",
1238
+ city: "Portland"
1239
+ }
1240
+ });
1241
+ form.field("address.street").setValue("789 Pine Rd");
1242
+ expect(form.isDirty).toBe(true);
1243
+ });
1244
+ it("should validate children recursively", async ()=>{
1245
+ const form = createFormWithObject();
1246
+ const valid = await form.validate();
1247
+ expect(valid).toBe(false);
1248
+ expect(form.errors.length).toBeGreaterThan(0);
1249
+ expect(form.errors.some((e)=>"address" === e.path)).toBe(true);
1250
+ });
1251
+ it("should pass validation when children are valid", async ()=>{
1252
+ const form = createFormWithObject();
1253
+ form.field("title").setValue("Home");
1254
+ form.field("address.street").setValue("123 Main St");
1255
+ form.field("address.city").setValue("Springfield");
1256
+ const valid = await form.validate();
1257
+ expect(valid).toBe(true);
1258
+ });
1259
+ it("should expose IObjectFieldVM from field.vm", ()=>{
1260
+ const form = createFormWithObject();
1261
+ form.field("address.street").setValue("123 Main St");
1262
+ form.field("address.city").setValue("Springfield");
1263
+ const vm = form.field("address").vm;
1264
+ expect(vm.type).toBe("object");
1265
+ expect(vm.isList).toBe(false);
1266
+ expect(vm.fields).toHaveLength(2);
1267
+ expect(vm.fields[0].name).toBe("street");
1268
+ expect(vm.fields[0].value).toBe("123 Main St");
1269
+ expect(vm.fields[1].name).toBe("city");
1270
+ expect(vm.items).toHaveLength(0);
1271
+ });
1272
+ it("should render object field in a row via default layout", ()=>{
1273
+ const form = createForm({
1274
+ fields: (fields)=>({
1275
+ address: fields.object().label("Address").fields((f)=>({
1276
+ street: f.text().label("Street"),
1277
+ city: f.text().label("City")
1278
+ }))
1279
+ })
1280
+ });
1281
+ const vm = form.vm;
1282
+ expect(vm.layout).toHaveLength(1);
1283
+ expect(vm.layout[0].type).toBe("row");
1284
+ const row = asRow(vm.layout[0]);
1285
+ expect(row.fields[0].type).toBe("object");
1286
+ expect(row.fields[0].name).toBe("address");
1287
+ });
1288
+ it("should reset to baseline values", ()=>{
1289
+ const form = createFormWithObject();
1290
+ form.setData({
1291
+ title: "Home",
1292
+ address: {
1293
+ street: "Original St",
1294
+ city: "Original City"
1295
+ }
1296
+ });
1297
+ form.field("address.street").setValue("Changed St");
1298
+ expect(form.isDirty).toBe(true);
1299
+ form.reset();
1300
+ expect(form.field("address.street").getValue()).toBe("Original St");
1301
+ expect(form.isDirty).toBe(false);
1302
+ });
1303
+ it("should submit nested data when valid", async ()=>{
1304
+ const form = createFormWithObject();
1305
+ form.field("title").setValue("Home");
1306
+ form.field("address.street").setValue("123 Main St");
1307
+ form.field("address.city").setValue("Springfield");
1308
+ const result = await form.submit();
1309
+ expect(result).toEqual({
1310
+ title: "Home",
1311
+ address: {
1312
+ street: "123 Main St",
1313
+ city: "Springfield"
1314
+ }
1315
+ });
1316
+ });
1317
+ });
1318
+ describe("list object field", ()=>{
1319
+ function createFormWithList() {
1320
+ return createForm({
1321
+ fields: (fields)=>({
1322
+ presets: fields.object().label("Presets").fields((f)=>({
1323
+ name: f.text().label("Name").required("Name is required"),
1324
+ model: f.text().label("Model").required("Model is required")
1325
+ })).list()
1326
+ }),
1327
+ layout: (layout)=>[
1328
+ layout.row("presets")
1329
+ ]
1330
+ });
1331
+ }
1332
+ it("should create a list object field", ()=>{
1333
+ const form = createFormWithList();
1334
+ const field = form.field("presets");
1335
+ expect(field.type).toBe("object");
1336
+ });
1337
+ it("should return empty array from getData initially", ()=>{
1338
+ const form = createFormWithList();
1339
+ expect(form.getData().presets).toEqual([]);
1340
+ });
1341
+ it("should add items via the field VM", ()=>{
1342
+ const form = createFormWithList();
1343
+ const vm = form.field("presets").vm;
1344
+ expect(vm.isList).toBe(true);
1345
+ expect(vm.items).toHaveLength(0);
1346
+ vm.addItem();
1347
+ const updatedVm = form.field("presets").vm;
1348
+ expect(updatedVm.items).toHaveLength(1);
1349
+ expect(updatedVm.items[0].fields).toHaveLength(2);
1350
+ expect(updatedVm.items[0].fields[0].name).toBe("name");
1351
+ });
1352
+ it("should hydrate list from setData", ()=>{
1353
+ const form = createFormWithList();
1354
+ form.setData({
1355
+ presets: [
1356
+ {
1357
+ name: "Default",
1358
+ model: "claude-sonnet"
1359
+ },
1360
+ {
1361
+ name: "Fast",
1362
+ model: "claude-haiku"
1363
+ }
1364
+ ]
1365
+ });
1366
+ const data = form.getData();
1367
+ expect(data.presets).toEqual([
1368
+ {
1369
+ name: "Default",
1370
+ model: "claude-sonnet"
1371
+ },
1372
+ {
1373
+ name: "Fast",
1374
+ model: "claude-haiku"
1375
+ }
1376
+ ]);
1377
+ });
1378
+ it("should expose items via IObjectFieldVM", ()=>{
1379
+ const form = createFormWithList();
1380
+ form.setData({
1381
+ presets: [
1382
+ {
1383
+ name: "Default",
1384
+ model: "claude-sonnet"
1385
+ },
1386
+ {
1387
+ name: "Fast",
1388
+ model: "claude-haiku"
1389
+ }
1390
+ ]
1391
+ });
1392
+ const vm = form.field("presets").vm;
1393
+ expect(vm.items).toHaveLength(2);
1394
+ expect(vm.items[0].fields[0].value).toBe("Default");
1395
+ expect(vm.items[1].fields[1].value).toBe("claude-haiku");
1396
+ });
1397
+ it("should remove items via item.remove()", ()=>{
1398
+ const form = createFormWithList();
1399
+ form.setData({
1400
+ presets: [
1401
+ {
1402
+ name: "Default",
1403
+ model: "claude-sonnet"
1404
+ },
1405
+ {
1406
+ name: "Fast",
1407
+ model: "claude-haiku"
1408
+ }
1409
+ ]
1410
+ });
1411
+ const vm = form.field("presets").vm;
1412
+ vm.items[0].remove();
1413
+ expect(form.getData().presets).toEqual([
1414
+ {
1415
+ name: "Fast",
1416
+ model: "claude-haiku"
1417
+ }
1418
+ ]);
1419
+ });
1420
+ it("should remove items via removeItem()", ()=>{
1421
+ const form = createFormWithList();
1422
+ form.setData({
1423
+ presets: [
1424
+ {
1425
+ name: "A",
1426
+ model: "a"
1427
+ },
1428
+ {
1429
+ name: "B",
1430
+ model: "b"
1431
+ },
1432
+ {
1433
+ name: "C",
1434
+ model: "c"
1435
+ }
1436
+ ]
1437
+ });
1438
+ const vm = form.field("presets").vm;
1439
+ vm.removeItem(1);
1440
+ expect(form.getData().presets).toEqual([
1441
+ {
1442
+ name: "A",
1443
+ model: "a"
1444
+ },
1445
+ {
1446
+ name: "C",
1447
+ model: "c"
1448
+ }
1449
+ ]);
1450
+ });
1451
+ it("should validate all items", async ()=>{
1452
+ const form = createFormWithList();
1453
+ form.setData({
1454
+ presets: [
1455
+ {
1456
+ name: "Default",
1457
+ model: "claude-sonnet"
1458
+ },
1459
+ {
1460
+ name: "",
1461
+ model: ""
1462
+ }
1463
+ ]
1464
+ });
1465
+ const valid = await form.validate();
1466
+ expect(valid).toBe(false);
1467
+ });
1468
+ it("should pass validation when all items are valid", async ()=>{
1469
+ const form = createFormWithList();
1470
+ form.setData({
1471
+ presets: [
1472
+ {
1473
+ name: "Default",
1474
+ model: "claude-sonnet"
1475
+ },
1476
+ {
1477
+ name: "Fast",
1478
+ model: "claude-haiku"
1479
+ }
1480
+ ]
1481
+ });
1482
+ const valid = await form.validate();
1483
+ expect(valid).toBe(true);
1484
+ });
1485
+ it("should validate with listSchema", async ()=>{
1486
+ const form = createForm({
1487
+ fields: (fields)=>({
1488
+ presets: fields.object().label("Presets").fields((f)=>({
1489
+ name: f.text().label("Name")
1490
+ })).list().listSchema(z.array(z.any()).min(2, "At least 2 presets are required"))
1491
+ })
1492
+ });
1493
+ form.setData({
1494
+ presets: [
1495
+ {
1496
+ name: "Only one"
1497
+ }
1498
+ ]
1499
+ });
1500
+ const valid = await form.validate();
1501
+ expect(valid).toBe(false);
1502
+ expect(form.errors[0].message).toBe("At least 2 presets are required");
1503
+ });
1504
+ it("should be dirty after adding an item", ()=>{
1505
+ const form = createFormWithList();
1506
+ form.setData({
1507
+ presets: []
1508
+ });
1509
+ expect(form.isDirty).toBe(false);
1510
+ const vm = form.field("presets").vm;
1511
+ vm.addItem();
1512
+ expect(form.isDirty).toBe(true);
1513
+ });
1514
+ it("should have stable keys across re-renders", ()=>{
1515
+ const form = createFormWithList();
1516
+ form.setData({
1517
+ presets: [
1518
+ {
1519
+ name: "A",
1520
+ model: "a"
1521
+ },
1522
+ {
1523
+ name: "B",
1524
+ model: "b"
1525
+ }
1526
+ ]
1527
+ });
1528
+ const vm1 = form.field("presets").vm;
1529
+ const key0 = vm1.items[0].key;
1530
+ const key1 = vm1.items[1].key;
1531
+ const vm2 = form.field("presets").vm;
1532
+ expect(vm2.items[0].key).toBe(key0);
1533
+ expect(vm2.items[1].key).toBe(key1);
1534
+ });
1535
+ it("should pass empty list validation when not required", async ()=>{
1536
+ const form = createFormWithList();
1537
+ const valid = await form.validate();
1538
+ expect(valid).toBe(true);
1539
+ });
1540
+ it("should modify item field values via VM onChange", ()=>{
1541
+ const form = createFormWithList();
1542
+ form.setData({
1543
+ presets: [
1544
+ {
1545
+ name: "Default",
1546
+ model: "claude-sonnet"
1547
+ }
1548
+ ]
1549
+ });
1550
+ const vm = form.field("presets").vm;
1551
+ vm.items[0].fields[0].onChange("Updated");
1552
+ expect(form.getData().presets[0].name).toBe("Updated");
1553
+ });
1554
+ });
1555
+ describe("hasErrors rollup through tabs", ()=>{
1556
+ it("should report hasErrors in tabs containing object fields", async ()=>{
1557
+ const form = createForm({
1558
+ fields: (fields)=>({
1559
+ title: fields.text().label("Title"),
1560
+ address: fields.object().label("Address").fields((f)=>({
1561
+ street: f.text().label("Street").required("Required")
1562
+ }))
1563
+ }),
1564
+ layout: (layout)=>[
1565
+ layout.tabs("settings").tab("general", (tab)=>{
1566
+ tab.label("General").layout((layout)=>[
1567
+ layout.row("title")
1568
+ ]);
1569
+ }).tab("details", (tab)=>{
1570
+ tab.label("Details").layout((layout)=>[
1571
+ layout.row("address")
1572
+ ]);
1573
+ })
1574
+ ]
1575
+ });
1576
+ form.field("title").setValue("Hello");
1577
+ await form.validate();
1578
+ const tabsNode = form.vm.layout[0];
1579
+ expect(tabsNode.tabs[0].hasErrors).toBe(false);
1580
+ expect(tabsNode.tabs[1].hasErrors).toBe(true);
1581
+ });
1582
+ });
1583
+ });
1584
+ describe("templated object fields (Phase 8a)", ()=>{
1585
+ function createFormWithTemplatedObject() {
1586
+ return createForm({
1587
+ fields: (fields)=>({
1588
+ content: fields.object().label("Content").template("hero", (t)=>{
1589
+ t.label("Hero Banner").fields((f)=>({
1590
+ heading: f.text().label("Heading").required("Required"),
1591
+ image: f.text().label("Image")
1592
+ }));
1593
+ }).template("text", (t)=>{
1594
+ t.label("Rich Text").fields((f)=>({
1595
+ body: f.text().label("Body").required("Required")
1596
+ }));
1597
+ })
1598
+ })
1599
+ });
1600
+ }
1601
+ describe("shape", ()=>{
1602
+ it("starts with no active template and empty children", ()=>{
1603
+ const form = createFormWithTemplatedObject();
1604
+ const field = form.field("content");
1605
+ expect(field.isTemplated).toBe(true);
1606
+ expect(field.activeTemplateId).toBeNull();
1607
+ expect(field.children.size).toBe(0);
1608
+ });
1609
+ it("exposes available templates via VM", ()=>{
1610
+ const form = createFormWithTemplatedObject();
1611
+ const vm = form.field("content").vm;
1612
+ expect(vm.isTemplated).toBe(true);
1613
+ expect(vm.availableTemplates).toEqual([
1614
+ {
1615
+ id: "hero",
1616
+ label: "Hero Banner"
1617
+ },
1618
+ {
1619
+ id: "text",
1620
+ label: "Rich Text"
1621
+ }
1622
+ ]);
1623
+ expect(vm.activeTemplateId).toBeNull();
1624
+ expect(vm.fields).toEqual([]);
1625
+ });
1626
+ it("rejects .fields() alongside .template() at build time", ()=>{
1627
+ expect(()=>createForm({
1628
+ fields: (fields)=>({
1629
+ content: fields.object().fields((f)=>({
1630
+ x: f.text()
1631
+ })).template("a", (t)=>{
1632
+ t.label("A").fields((f)=>({
1633
+ y: f.text()
1634
+ }));
1635
+ })
1636
+ })
1637
+ })).toThrow(/both .fields\(\) and .template\(\)/);
1638
+ });
1639
+ it("rejects duplicate template ids", ()=>{
1640
+ expect(()=>createForm({
1641
+ fields: (fields)=>({
1642
+ content: fields.object().template("a", (t)=>{
1643
+ t.label("A1").fields((f)=>({
1644
+ x: f.text()
1645
+ }));
1646
+ }).template("a", (t)=>{
1647
+ t.label("A2").fields((f)=>({
1648
+ y: f.text()
1649
+ }));
1650
+ })
1651
+ })
1652
+ })).toThrow(/Duplicate template id "a"/);
1653
+ });
1654
+ it("rejects reserved _templateId as template id", ()=>{
1655
+ expect(()=>createForm({
1656
+ fields: (fields)=>({
1657
+ content: fields.object().template("_templateId", (t)=>{
1658
+ t.label("X").fields((f)=>({
1659
+ x: f.text()
1660
+ }));
1661
+ })
1662
+ })
1663
+ })).toThrow(/reserved/);
1664
+ });
1665
+ it("rejects _templateId as a child field name in a template", ()=>{
1666
+ expect(()=>createForm({
1667
+ fields: (fields)=>({
1668
+ content: fields.object().template("hero", (t)=>{
1669
+ t.label("Hero").fields((f)=>({
1670
+ _templateId: f.text()
1671
+ }));
1672
+ })
1673
+ })
1674
+ })).toThrow(/reserved field "_templateId"/);
1675
+ });
1676
+ it("allows combining .list() with .template() (Phase 8b)", ()=>{
1677
+ expect(()=>createForm({
1678
+ fields: (fields)=>({
1679
+ content: fields.object().list().template("a", (t)=>{
1680
+ t.label("A").fields((f)=>({
1681
+ x: f.text()
1682
+ }));
1683
+ })
1684
+ })
1685
+ })).not.toThrow();
1686
+ });
1687
+ });
1688
+ describe("setTemplate / switching", ()=>{
1689
+ it("builds children when a template is selected", ()=>{
1690
+ const form = createFormWithTemplatedObject();
1691
+ const field = form.field("content");
1692
+ field.setTemplate("hero");
1693
+ expect(field.activeTemplateId).toBe("hero");
1694
+ expect(field.children.size).toBe(2);
1695
+ expect(form.field("content.heading").type).toBe("text");
1696
+ expect(form.field("content.image").type).toBe("text");
1697
+ });
1698
+ it("discards data when switching to a different template", ()=>{
1699
+ const form = createFormWithTemplatedObject();
1700
+ const field = form.field("content");
1701
+ field.setTemplate("hero");
1702
+ form.field("content.heading").setValue("Hello");
1703
+ field.setTemplate("text");
1704
+ expect(field.activeTemplateId).toBe("text");
1705
+ expect(form.field("content.body")).toBeDefined();
1706
+ expect(()=>form.field("content.heading")).toThrow();
1707
+ });
1708
+ it("is a no-op when setting the currently active template", ()=>{
1709
+ const form = createFormWithTemplatedObject();
1710
+ const field = form.field("content");
1711
+ field.setTemplate("hero");
1712
+ form.field("content.heading").setValue("Preserved");
1713
+ field.setTemplate("hero");
1714
+ expect(form.field("content.heading").getValue()).toBe("Preserved");
1715
+ });
1716
+ it("throws when setting an unknown template id", ()=>{
1717
+ const form = createFormWithTemplatedObject();
1718
+ const field = form.field("content");
1719
+ expect(()=>field.setTemplate("missing")).toThrow(/Template "missing" not found/);
1720
+ });
1721
+ it("wires the form reference on newly created children", ()=>{
1722
+ const form = createFormWithTemplatedObject();
1723
+ const field = form.field("content");
1724
+ field.setTemplate("hero");
1725
+ expect(()=>form.field("content.heading").setValue("OK")).not.toThrow();
1726
+ });
1727
+ });
1728
+ describe("getData / setData", ()=>{
1729
+ it("returns null when no template is active", ()=>{
1730
+ const form = createFormWithTemplatedObject();
1731
+ expect(form.getData().content).toBeNull();
1732
+ });
1733
+ it("includes _templateId and child values when active", ()=>{
1734
+ const form = createFormWithTemplatedObject();
1735
+ const field = form.field("content");
1736
+ field.setTemplate("hero");
1737
+ form.field("content.heading").setValue("Welcome");
1738
+ form.field("content.image").setValue("cover.jpg");
1739
+ expect(form.getData().content).toEqual({
1740
+ _templateId: "hero",
1741
+ heading: "Welcome",
1742
+ image: "cover.jpg"
1743
+ });
1744
+ });
1745
+ it("hydrates via setData by reading _templateId", ()=>{
1746
+ const form = createFormWithTemplatedObject();
1747
+ form.setData({
1748
+ content: {
1749
+ _templateId: "text",
1750
+ body: "Lorem ipsum"
1751
+ }
1752
+ });
1753
+ const field = form.field("content");
1754
+ expect(field.activeTemplateId).toBe("text");
1755
+ expect(form.field("content.body").getValue()).toBe("Lorem ipsum");
1756
+ });
1757
+ it("setData with null clears the active template", ()=>{
1758
+ const form = createFormWithTemplatedObject();
1759
+ const field = form.field("content");
1760
+ field.setTemplate("hero");
1761
+ form.field("content.heading").setValue("X");
1762
+ form.setData({
1763
+ content: null
1764
+ });
1765
+ expect(field.activeTemplateId).toBeNull();
1766
+ expect(field.children.size).toBe(0);
1767
+ });
1768
+ it("setData ignores unknown template id silently", ()=>{
1769
+ const form = createFormWithTemplatedObject();
1770
+ form.setData({
1771
+ content: {
1772
+ _templateId: "nope",
1773
+ foo: "bar"
1774
+ }
1775
+ });
1776
+ const field = form.field("content");
1777
+ expect(field.activeTemplateId).toBeNull();
1778
+ });
1779
+ });
1780
+ describe("validation", ()=>{
1781
+ it("required templated object fails validation when no template active", async ()=>{
1782
+ const form = createForm({
1783
+ fields: (fields)=>({
1784
+ content: fields.object().required("Pick a template").template("hero", (t)=>{
1785
+ t.label("Hero").fields((f)=>({
1786
+ heading: f.text()
1787
+ }));
1788
+ })
1789
+ })
1790
+ });
1791
+ const valid = await form.validate();
1792
+ expect(valid).toBe(false);
1793
+ expect(form.errors.some((e)=>"content" === e.path)).toBe(true);
1794
+ });
1795
+ it("required templated object passes when template active with valid children", async ()=>{
1796
+ const form = createForm({
1797
+ fields: (fields)=>({
1798
+ content: fields.object().required("Pick a template").template("hero", (t)=>{
1799
+ t.label("Hero").fields((f)=>({
1800
+ heading: f.text().required("Required")
1801
+ }));
1802
+ })
1803
+ })
1804
+ });
1805
+ const field = form.field("content");
1806
+ field.setTemplate("hero");
1807
+ form.field("content.heading").setValue("Hi");
1808
+ const valid = await form.validate();
1809
+ expect(valid).toBe(true);
1810
+ });
1811
+ it("validates child fields inside active template", async ()=>{
1812
+ const form = createFormWithTemplatedObject();
1813
+ const field = form.field("content");
1814
+ field.setTemplate("hero");
1815
+ const valid = await form.validate();
1816
+ expect(valid).toBe(false);
1817
+ });
1818
+ it("passes validation when object is optional and no template selected", async ()=>{
1819
+ const form = createFormWithTemplatedObject();
1820
+ const valid = await form.validate();
1821
+ expect(valid).toBe(true);
1822
+ });
1823
+ });
1824
+ describe("template visibility", ()=>{
1825
+ it("filters availableTemplates by reactive visible callback", ()=>{
1826
+ const form = createForm({
1827
+ fields: (fields)=>({
1828
+ plan: fields.text().defaultValue("free"),
1829
+ content: fields.object().template("basic", (t)=>{
1830
+ t.label("Basic").fields((f)=>({
1831
+ x: f.text()
1832
+ }));
1833
+ }).template("premium", (t)=>{
1834
+ t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
1835
+ y: f.text()
1836
+ }));
1837
+ })
1838
+ })
1839
+ });
1840
+ const vm1 = form.field("content").vm;
1841
+ expect(vm1.availableTemplates.map((t)=>t.id)).toEqual([
1842
+ "basic"
1843
+ ]);
1844
+ form.field("plan").setValue("enterprise");
1845
+ const vm2 = form.field("content").vm;
1846
+ expect(vm2.availableTemplates.map((t)=>t.id)).toEqual([
1847
+ "basic",
1848
+ "premium"
1849
+ ]);
1850
+ });
1851
+ it("hiding a template does not clear an already-active selection", ()=>{
1852
+ const form = createForm({
1853
+ fields: (fields)=>({
1854
+ plan: fields.text().defaultValue("enterprise"),
1855
+ content: fields.object().template("premium", (t)=>{
1856
+ t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
1857
+ y: f.text()
1858
+ }));
1859
+ })
1860
+ })
1861
+ });
1862
+ const field = form.field("content");
1863
+ field.setTemplate("premium");
1864
+ form.field("content.y").setValue("set");
1865
+ form.field("plan").setValue("free");
1866
+ const vm = form.field("content").vm;
1867
+ expect(vm.availableTemplates).toEqual([]);
1868
+ expect(vm.activeTemplateId).toBe("premium");
1869
+ expect(form.field("content.y").getValue()).toBe("set");
1870
+ });
1871
+ });
1872
+ describe("isDirty", ()=>{
1873
+ it("is not dirty after setData with template", ()=>{
1874
+ const form = createFormWithTemplatedObject();
1875
+ form.setData({
1876
+ content: {
1877
+ _templateId: "hero",
1878
+ heading: "Hello",
1879
+ image: ""
1880
+ }
1881
+ });
1882
+ expect(form.isDirty).toBe(false);
1883
+ });
1884
+ it("becomes dirty after switching template", ()=>{
1885
+ const form = createFormWithTemplatedObject();
1886
+ form.setData({
1887
+ content: {
1888
+ _templateId: "hero",
1889
+ heading: "Hello",
1890
+ image: ""
1891
+ }
1892
+ });
1893
+ form.field("content").setTemplate("text");
1894
+ expect(form.isDirty).toBe(true);
1895
+ });
1896
+ });
1897
+ });
1898
+ describe("templated list fields (Phase 8b)", ()=>{
1899
+ function createFormWithTemplatedList() {
1900
+ return createForm({
1901
+ fields: (fields)=>({
1902
+ sections: fields.object().label("Sections").list().template("hero", (t)=>{
1903
+ t.label("Hero").fields((f)=>({
1904
+ heading: f.text().required("Required"),
1905
+ image: f.text()
1906
+ }));
1907
+ }).template("text", (t)=>{
1908
+ t.label("Text").fields((f)=>({
1909
+ body: f.text().required("Required")
1910
+ }));
1911
+ })
1912
+ })
1913
+ });
1914
+ }
1915
+ describe("addItem / templateId", ()=>{
1916
+ it("requires a template id when adding to a templated list", ()=>{
1917
+ const form = createFormWithTemplatedList();
1918
+ const field = form.field("sections");
1919
+ expect(()=>field.addItem()).toThrow(/require a template id/);
1920
+ });
1921
+ it("rejects unknown template ids", ()=>{
1922
+ const form = createFormWithTemplatedList();
1923
+ const field = form.field("sections");
1924
+ expect(()=>field.addItem("missing")).toThrow(/Template "missing" not found/);
1925
+ });
1926
+ it("adds an item with the picked template's children", ()=>{
1927
+ const form = createFormWithTemplatedList();
1928
+ const field = form.field("sections");
1929
+ field.addItem("hero");
1930
+ expect(field.items.length).toBe(1);
1931
+ expect(field.items[0].templateId).toBe("hero");
1932
+ expect(field.items[0].children.has("heading")).toBe(true);
1933
+ expect(field.items[0].children.has("image")).toBe(true);
1934
+ });
1935
+ it("allows mixing different templates across items", ()=>{
1936
+ const form = createFormWithTemplatedList();
1937
+ const field = form.field("sections");
1938
+ field.addItem("hero");
1939
+ field.addItem("text");
1940
+ field.addItem("hero");
1941
+ expect(field.items.map((i)=>i.templateId)).toEqual([
1942
+ "hero",
1943
+ "text",
1944
+ "hero"
1945
+ ]);
1946
+ });
1947
+ });
1948
+ describe("getData", ()=>{
1949
+ it("includes _templateId per item", ()=>{
1950
+ const form = createFormWithTemplatedList();
1951
+ const field = form.field("sections");
1952
+ field.addItem("hero");
1953
+ field.items[0].children.get("heading").setValue("Welcome");
1954
+ field.addItem("text");
1955
+ field.items[1].children.get("body").setValue("Lorem");
1956
+ expect(form.getData().sections).toEqual([
1957
+ {
1958
+ _templateId: "hero",
1959
+ heading: "Welcome",
1960
+ image: null
1961
+ },
1962
+ {
1963
+ _templateId: "text",
1964
+ body: "Lorem"
1965
+ }
1966
+ ]);
1967
+ });
1968
+ });
1969
+ describe("setData", ()=>{
1970
+ it("hydrates items by reading each item's _templateId", ()=>{
1971
+ const form = createFormWithTemplatedList();
1972
+ form.setData({
1973
+ sections: [
1974
+ {
1975
+ _templateId: "hero",
1976
+ heading: "H1",
1977
+ image: "img.jpg"
1978
+ },
1979
+ {
1980
+ _templateId: "text",
1981
+ body: "Body copy"
1982
+ }
1983
+ ]
1984
+ });
1985
+ const field = form.field("sections");
1986
+ expect(field.items.length).toBe(2);
1987
+ expect(field.items[0].templateId).toBe("hero");
1988
+ expect(field.items[0].children.get("heading").getValue()).toBe("H1");
1989
+ expect(field.items[1].templateId).toBe("text");
1990
+ expect(field.items[1].children.get("body").getValue()).toBe("Body copy");
1991
+ });
1992
+ it("silently drops items with invalid or missing _templateId", ()=>{
1993
+ const form = createFormWithTemplatedList();
1994
+ form.setData({
1995
+ sections: [
1996
+ {
1997
+ _templateId: "hero",
1998
+ heading: "Keep"
1999
+ },
2000
+ {
2001
+ _templateId: "nope",
2002
+ x: 1
2003
+ },
2004
+ {
2005
+ heading: "no-id"
2006
+ },
2007
+ null,
2008
+ {
2009
+ _templateId: "text",
2010
+ body: "Also keep"
2011
+ }
2012
+ ]
2013
+ });
2014
+ const field = form.field("sections");
2015
+ expect(field.items.length).toBe(2);
2016
+ expect(field.items.map((i)=>i.templateId)).toEqual([
2017
+ "hero",
2018
+ "text"
2019
+ ]);
2020
+ });
2021
+ });
2022
+ describe("duplicate / move / remove", ()=>{
2023
+ it("duplicates an item preserving templateId and values", ()=>{
2024
+ const form = createFormWithTemplatedList();
2025
+ const field = form.field("sections");
2026
+ field.addItem("hero");
2027
+ field.items[0].children.get("heading").setValue("Original");
2028
+ field.items[0].children.get("image").setValue("pic.jpg");
2029
+ field.duplicateItem(0);
2030
+ expect(field.items.length).toBe(2);
2031
+ expect(field.items[1].templateId).toBe("hero");
2032
+ expect(field.items[1].children.get("heading").getValue()).toBe("Original");
2033
+ expect(field.items[1].children.get("image").getValue()).toBe("pic.jpg");
2034
+ expect(field.items[0].key).not.toBe(field.items[1].key);
2035
+ });
2036
+ it("moves items while preserving templateId", ()=>{
2037
+ const form = createFormWithTemplatedList();
2038
+ const field = form.field("sections");
2039
+ field.addItem("hero");
2040
+ field.addItem("text");
2041
+ field.moveItem(0, 1);
2042
+ expect(field.items.map((i)=>i.templateId)).toEqual([
2043
+ "text",
2044
+ "hero"
2045
+ ]);
2046
+ });
2047
+ it("removes items by index", ()=>{
2048
+ const form = createFormWithTemplatedList();
2049
+ const field = form.field("sections");
2050
+ field.addItem("hero");
2051
+ field.addItem("text");
2052
+ field.removeItem(0);
2053
+ expect(field.items.length).toBe(1);
2054
+ expect(field.items[0].templateId).toBe("text");
2055
+ });
2056
+ });
2057
+ describe("VM", ()=>{
2058
+ it("exposes templateId and duplicate on each item VM", ()=>{
2059
+ const form = createFormWithTemplatedList();
2060
+ const field = form.field("sections");
2061
+ field.addItem("hero");
2062
+ const vm = form.field("sections").vm;
2063
+ expect(vm.items.length).toBe(1);
2064
+ expect(vm.items[0].templateId).toBe("hero");
2065
+ expect(typeof vm.items[0].duplicate).toBe("function");
2066
+ });
2067
+ it("VM addItem(templateId) appends an item", ()=>{
2068
+ const form = createFormWithTemplatedList();
2069
+ const vm = form.field("sections").vm;
2070
+ vm.addItem("hero");
2071
+ vm.addItem("text");
2072
+ const field = form.field("sections");
2073
+ expect(field.items.map((i)=>i.templateId)).toEqual([
2074
+ "hero",
2075
+ "text"
2076
+ ]);
2077
+ });
2078
+ it("availableTemplates respects reactive visible() on templated lists", ()=>{
2079
+ const form = createForm({
2080
+ fields: (fields)=>({
2081
+ plan: fields.text().defaultValue("free"),
2082
+ sections: fields.object().list().template("basic", (t)=>{
2083
+ t.label("Basic").fields((f)=>({
2084
+ x: f.text()
2085
+ }));
2086
+ }).template("premium", (t)=>{
2087
+ t.label("Premium").visible((f)=>"enterprise" === f.field("plan").getValue()).fields((f)=>({
2088
+ y: f.text()
2089
+ }));
2090
+ })
2091
+ })
2092
+ });
2093
+ const vm1 = form.field("sections").vm;
2094
+ expect(vm1.availableTemplates.map((t)=>t.id)).toEqual([
2095
+ "basic"
2096
+ ]);
2097
+ form.field("plan").setValue("enterprise");
2098
+ const vm2 = form.field("sections").vm;
2099
+ expect(vm2.availableTemplates.map((t)=>t.id)).toEqual([
2100
+ "basic",
2101
+ "premium"
2102
+ ]);
2103
+ });
2104
+ });
2105
+ describe("validation", ()=>{
2106
+ it("validates each item's children under its template", async ()=>{
2107
+ const form = createFormWithTemplatedList();
2108
+ const field = form.field("sections");
2109
+ field.addItem("hero");
2110
+ field.addItem("text");
2111
+ field.items[1].children.get("body").setValue("ok");
2112
+ const valid = await form.validate();
2113
+ expect(valid).toBe(false);
2114
+ expect(form.errors.some((e)=>e.path.startsWith("sections"))).toBe(true);
2115
+ });
2116
+ it("passes when every item's required fields are filled", async ()=>{
2117
+ const form = createFormWithTemplatedList();
2118
+ const field = form.field("sections");
2119
+ field.addItem("hero");
2120
+ field.items[0].children.get("heading").setValue("H");
2121
+ field.addItem("text");
2122
+ field.items[1].children.get("body").setValue("B");
2123
+ const valid = await form.validate();
2124
+ expect(valid).toBe(true);
2125
+ });
2126
+ });
2127
+ describe("isDirty", ()=>{
2128
+ it("is not dirty after setData with templated list", ()=>{
2129
+ const form = createFormWithTemplatedList();
2130
+ form.setData({
2131
+ sections: [
2132
+ {
2133
+ _templateId: "hero",
2134
+ heading: "H",
2135
+ image: ""
2136
+ },
2137
+ {
2138
+ _templateId: "text",
2139
+ body: "B"
2140
+ }
2141
+ ]
2142
+ });
2143
+ expect(form.isDirty).toBe(false);
2144
+ });
2145
+ it("becomes dirty after adding an item", ()=>{
2146
+ const form = createFormWithTemplatedList();
2147
+ form.setData({
2148
+ sections: [
2149
+ {
2150
+ _templateId: "hero",
2151
+ heading: "H",
2152
+ image: ""
2153
+ }
2154
+ ]
2155
+ });
2156
+ form.field("sections").addItem("text");
2157
+ expect(form.isDirty).toBe(true);
2158
+ });
2159
+ });
2160
+ });
2161
+ describe("per-template / inner object layouts (Phase 8c)", ()=>{
2162
+ describe("non-templated single object", ()=>{
2163
+ it("defaults to one row per visible child when no layout.object() is registered", ()=>{
2164
+ const form = createForm({
2165
+ fields: (fields)=>({
2166
+ meta: fields.object().fields((f)=>({
2167
+ a: f.text().label("A"),
2168
+ b: f.text().label("B")
2169
+ }))
2170
+ })
2171
+ });
2172
+ const vm = form.field("meta").vm;
2173
+ expect(vm.layout.length).toBe(2);
2174
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2175
+ "a"
2176
+ ]);
2177
+ expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
2178
+ "b"
2179
+ ]);
2180
+ });
2181
+ it("resolves the registered inner layout against the children", ()=>{
2182
+ const form = createForm({
2183
+ fields: (fields)=>({
2184
+ meta: fields.object().fields((f)=>({
2185
+ a: f.text().label("A"),
2186
+ b: f.text().label("B")
2187
+ }))
2188
+ }),
2189
+ layout: (layout)=>[
2190
+ layout.object("meta", (l)=>[
2191
+ l.row("a", "b")
2192
+ ])
2193
+ ]
2194
+ });
2195
+ const vm = form.field("meta").vm;
2196
+ expect(vm.layout.length).toBe(1);
2197
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2198
+ "a",
2199
+ "b"
2200
+ ]);
2201
+ });
2202
+ it("throws when a per-template map is passed to a non-templated field", ()=>{
2203
+ expect(()=>createForm({
2204
+ fields: (fields)=>({
2205
+ meta: fields.object().fields((f)=>({
2206
+ a: f.text()
2207
+ }))
2208
+ }),
2209
+ layout: (layout)=>[
2210
+ layout.object("meta", {
2211
+ tplA: (l)=>[
2212
+ l.row("a")
2213
+ ]
2214
+ })
2215
+ ]
2216
+ })).toThrow(/not templated/);
2217
+ });
2218
+ });
2219
+ describe("non-templated list", ()=>{
2220
+ it("applies the inner layout to every list item", ()=>{
2221
+ const form = createForm({
2222
+ fields: (fields)=>({
2223
+ rows: fields.object().list().fields((f)=>({
2224
+ a: f.text(),
2225
+ b: f.text()
2226
+ }))
2227
+ }),
2228
+ layout: (layout)=>[
2229
+ layout.object("rows", (l)=>[
2230
+ l.row("a", "b")
2231
+ ])
2232
+ ]
2233
+ });
2234
+ const field = form.field("rows");
2235
+ field.addItem();
2236
+ field.addItem();
2237
+ const vm = field.vm;
2238
+ expect(vm.items.length).toBe(2);
2239
+ for (const item of vm.items){
2240
+ expect(item.layout.length).toBe(1);
2241
+ expect(asRow(item.layout[0]).fields.map((f)=>f.name)).toEqual([
2242
+ "a",
2243
+ "b"
2244
+ ]);
2245
+ }
2246
+ });
2247
+ });
2248
+ describe("templated single object", ()=>{
2249
+ function buildTemplatedForm(layoutFactory) {
2250
+ return createForm({
2251
+ fields: (fields)=>({
2252
+ content: fields.object().template("hero", (t)=>{
2253
+ t.label("Hero").fields((f)=>({
2254
+ heading: f.text().label("Heading"),
2255
+ subheading: f.text().label("Subheading")
2256
+ }));
2257
+ }).template("cta", (t)=>{
2258
+ t.label("CTA").fields((f)=>({
2259
+ text: f.text().label("Text"),
2260
+ url: f.text().label("URL")
2261
+ }));
2262
+ })
2263
+ }),
2264
+ layout: layoutFactory
2265
+ });
2266
+ }
2267
+ it("uses the active template's per-template layout", ()=>{
2268
+ const form = buildTemplatedForm((layout)=>[
2269
+ layout.object("content", {
2270
+ hero: (l)=>[
2271
+ l.row("heading", "subheading")
2272
+ ],
2273
+ cta: (l)=>[
2274
+ l.row("text"),
2275
+ l.row("url")
2276
+ ]
2277
+ })
2278
+ ]);
2279
+ const field = form.field("content");
2280
+ field.setTemplate("hero");
2281
+ let vm = form.field("content").vm;
2282
+ expect(vm.layout.length).toBe(1);
2283
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2284
+ "heading",
2285
+ "subheading"
2286
+ ]);
2287
+ field.setTemplate("cta");
2288
+ vm = form.field("content").vm;
2289
+ expect(vm.layout.length).toBe(2);
2290
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2291
+ "text"
2292
+ ]);
2293
+ expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
2294
+ "url"
2295
+ ]);
2296
+ });
2297
+ it("falls back to default one-row-per-child when active template has no entry", ()=>{
2298
+ const form = buildTemplatedForm((layout)=>[
2299
+ layout.object("content", {
2300
+ hero: (l)=>[
2301
+ l.row("heading", "subheading")
2302
+ ]
2303
+ })
2304
+ ]);
2305
+ const field = form.field("content");
2306
+ field.setTemplate("cta");
2307
+ const vm = form.field("content").vm;
2308
+ expect(vm.layout.length).toBe(2);
2309
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2310
+ "text"
2311
+ ]);
2312
+ expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
2313
+ "url"
2314
+ ]);
2315
+ });
2316
+ it("returns an empty layout when no template is active", ()=>{
2317
+ const form = buildTemplatedForm((layout)=>[
2318
+ layout.object("content", {
2319
+ hero: (l)=>[
2320
+ l.row("heading", "subheading")
2321
+ ]
2322
+ })
2323
+ ]);
2324
+ const vm = form.field("content").vm;
2325
+ expect(vm.activeTemplateId).toBeNull();
2326
+ expect(vm.layout).toEqual([]);
2327
+ });
2328
+ it("silently ignores an unknown template id in the layout map", ()=>{
2329
+ const form = buildTemplatedForm((layout)=>[
2330
+ layout.object("content", {
2331
+ hero: (l)=>[
2332
+ l.row("heading", "subheading")
2333
+ ],
2334
+ unknown: (l)=>[
2335
+ l.row("xxx")
2336
+ ]
2337
+ })
2338
+ ]);
2339
+ const field = form.field("content");
2340
+ field.setTemplate("hero");
2341
+ const vm = form.field("content").vm;
2342
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2343
+ "heading",
2344
+ "subheading"
2345
+ ]);
2346
+ });
2347
+ it("throws when a single LayoutNode[] is passed to a templated field", ()=>{
2348
+ expect(()=>buildTemplatedForm((layout)=>[
2349
+ layout.object("content", (l)=>[
2350
+ l.row("x")
2351
+ ])
2352
+ ])).toThrow(/is templated/);
2353
+ });
2354
+ it("falls back to default when no layout.object() is registered", ()=>{
2355
+ const form = buildTemplatedForm();
2356
+ const field = form.field("content");
2357
+ field.setTemplate("hero");
2358
+ const vm = form.field("content").vm;
2359
+ expect(vm.layout.length).toBe(2);
2360
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2361
+ "heading"
2362
+ ]);
2363
+ expect(asRow(vm.layout[1]).fields.map((f)=>f.name)).toEqual([
2364
+ "subheading"
2365
+ ]);
2366
+ });
2367
+ });
2368
+ describe("templated list", ()=>{
2369
+ function buildTemplatedListForm() {
2370
+ return createForm({
2371
+ fields: (fields)=>({
2372
+ sections: fields.object().list().template("hero", (t)=>{
2373
+ t.label("Hero").fields((f)=>({
2374
+ heading: f.text().label("Heading"),
2375
+ subheading: f.text().label("Subheading")
2376
+ }));
2377
+ }).template("cta", (t)=>{
2378
+ t.label("CTA").fields((f)=>({
2379
+ text: f.text().label("Text"),
2380
+ url: f.text().label("URL")
2381
+ }));
2382
+ })
2383
+ }),
2384
+ layout: (layout)=>[
2385
+ layout.object("sections", {
2386
+ hero: (l)=>[
2387
+ l.row("heading", "subheading")
2388
+ ],
2389
+ cta: (l)=>[
2390
+ l.row("text"),
2391
+ l.row("url")
2392
+ ]
2393
+ })
2394
+ ]
2395
+ });
2396
+ }
2397
+ it("each item resolves layout against its own template", ()=>{
2398
+ const form = buildTemplatedListForm();
2399
+ const field = form.field("sections");
2400
+ field.addItem("hero");
2401
+ field.addItem("cta");
2402
+ const vm = form.field("sections").vm;
2403
+ expect(vm.items.length).toBe(2);
2404
+ expect(asRow(vm.items[0].layout[0]).fields.map((f)=>f.name)).toEqual([
2405
+ "heading",
2406
+ "subheading"
2407
+ ]);
2408
+ expect(asRow(vm.items[1].layout[0]).fields.map((f)=>f.name)).toEqual([
2409
+ "text"
2410
+ ]);
2411
+ expect(asRow(vm.items[1].layout[1]).fields.map((f)=>f.name)).toEqual([
2412
+ "url"
2413
+ ]);
2414
+ });
2415
+ it("hides hidden child fields from the resolved layout row", ()=>{
2416
+ const form = createForm({
2417
+ fields: (fields)=>({
2418
+ content: fields.object().template("hero", (t)=>{
2419
+ t.label("Hero").fields((f)=>({
2420
+ heading: f.text(),
2421
+ secret: f.text().hidden()
2422
+ }));
2423
+ })
2424
+ }),
2425
+ layout: (layout)=>[
2426
+ layout.object("content", {
2427
+ hero: (l)=>[
2428
+ l.row("heading", "secret")
2429
+ ]
2430
+ })
2431
+ ]
2432
+ });
2433
+ const field = form.field("content");
2434
+ field.setTemplate("hero");
2435
+ const vm = form.field("content").vm;
2436
+ expect(asRow(vm.layout[0]).fields.map((f)=>f.name)).toEqual([
2437
+ "heading"
2438
+ ]);
2439
+ });
2440
+ });
2441
+ describe("interaction with form.vm.layout", ()=>{
2442
+ it("the form layout exposes a single-field row for the object", ()=>{
2443
+ const form = createForm({
2444
+ fields: (fields)=>({
2445
+ content: fields.object().template("hero", (t)=>{
2446
+ t.label("Hero").fields((f)=>({
2447
+ heading: f.text()
2448
+ }));
2449
+ })
2450
+ }),
2451
+ layout: (layout)=>[
2452
+ layout.object("content", {
2453
+ hero: (l)=>[
2454
+ l.row("heading")
2455
+ ]
2456
+ })
2457
+ ]
2458
+ });
2459
+ const layout = form.vm.layout;
2460
+ expect(layout.length).toBe(1);
2461
+ const row = asRow(layout[0]);
2462
+ expect(row.fields.map((f)=>f.name)).toEqual([
2463
+ "content"
2464
+ ]);
2465
+ });
2466
+ it("hides the object node from form.vm.layout when the field is not visible", ()=>{
2467
+ const form = createForm({
2468
+ fields: (fields)=>({
2469
+ content: fields.object().hidden().template("hero", (t)=>{
2470
+ t.label("Hero").fields((f)=>({
2471
+ heading: f.text()
2472
+ }));
2473
+ })
2474
+ }),
2475
+ layout: (layout)=>[
2476
+ layout.object("content", {
2477
+ hero: (l)=>[
2478
+ l.row("heading")
2479
+ ]
2480
+ })
2481
+ ]
2482
+ });
2483
+ expect(form.vm.layout).toEqual([]);
2484
+ });
2485
+ });
2486
+ });
2487
+ describe("nested object layouts (Phase 8c.1)", ()=>{
2488
+ it("registers layout.object() nested inside another object's inner layout (non-templated)", ()=>{
2489
+ const form = createForm({
2490
+ fields: (fields)=>({
2491
+ page: fields.object().fields((f)=>({
2492
+ title: f.text(),
2493
+ seo: f.object().fields((g)=>({
2494
+ metaTitle: g.text(),
2495
+ metaDescription: g.text()
2496
+ }))
2497
+ }))
2498
+ }),
2499
+ layout: (layout)=>[
2500
+ layout.object("page", (l)=>[
2501
+ l.row("title"),
2502
+ l.object("seo", (inner)=>[
2503
+ inner.row("metaTitle", "metaDescription")
2504
+ ])
2505
+ ])
2506
+ ]
2507
+ });
2508
+ const pageVm = form.field("page").vm;
2509
+ const seoRow = asRow(pageVm.layout[1]);
2510
+ const seoVm = seoRow.fields[0];
2511
+ expect(seoVm.layout.length).toBe(1);
2512
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2513
+ "metaTitle",
2514
+ "metaDescription"
2515
+ ]);
2516
+ });
2517
+ it("supports three levels of nesting", ()=>{
2518
+ const form = createForm({
2519
+ fields: (fields)=>({
2520
+ a: fields.object().fields((f)=>({
2521
+ b: f.object().fields((g)=>({
2522
+ c: g.object().fields((h)=>({
2523
+ x: h.text(),
2524
+ y: h.text()
2525
+ }))
2526
+ }))
2527
+ }))
2528
+ }),
2529
+ layout: (layout)=>[
2530
+ layout.object("a", (l1)=>[
2531
+ l1.object("b", (l2)=>[
2532
+ l2.object("c", (l3)=>[
2533
+ l3.row("x", "y")
2534
+ ])
2535
+ ])
2536
+ ])
2537
+ ]
2538
+ });
2539
+ const aVm = form.field("a").vm;
2540
+ const bVm = asRow(aVm.layout[0]).fields[0];
2541
+ const cVm = asRow(bVm.layout[0]).fields[0];
2542
+ expect(cVm.layout.length).toBe(1);
2543
+ expect(asRow(cVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2544
+ "x",
2545
+ "y"
2546
+ ]);
2547
+ });
2548
+ it("registers nested layouts on a templated single object when its template activates", ()=>{
2549
+ const form = createForm({
2550
+ fields: (fields)=>({
2551
+ block: fields.object().template("hero", (t)=>{
2552
+ t.label("Hero").fields((f)=>({
2553
+ heading: f.text(),
2554
+ seo: f.object().fields((g)=>({
2555
+ metaTitle: g.text(),
2556
+ metaDescription: g.text()
2557
+ }))
2558
+ }));
2559
+ })
2560
+ }),
2561
+ layout: (layout)=>[
2562
+ layout.object("block", {
2563
+ hero: (l)=>[
2564
+ l.row("heading"),
2565
+ l.object("seo", (inner)=>[
2566
+ inner.row("metaTitle", "metaDescription")
2567
+ ])
2568
+ ]
2569
+ })
2570
+ ]
2571
+ });
2572
+ const field = form.field("block");
2573
+ field.setTemplate("hero");
2574
+ const vm = form.field("block").vm;
2575
+ const seoRow = asRow(vm.layout[1]);
2576
+ const seoVm = seoRow.fields[0];
2577
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2578
+ "metaTitle",
2579
+ "metaDescription"
2580
+ ]);
2581
+ });
2582
+ it("registers nested layouts on a templated list — each item gets its own", ()=>{
2583
+ const form = createForm({
2584
+ fields: (fields)=>({
2585
+ sections: fields.object().list().template("hero", (t)=>{
2586
+ t.label("Hero").fields((f)=>({
2587
+ heading: f.text(),
2588
+ seo: f.object().fields((g)=>({
2589
+ metaTitle: g.text(),
2590
+ metaDescription: g.text()
2591
+ }))
2592
+ }));
2593
+ })
2594
+ }),
2595
+ layout: (layout)=>[
2596
+ layout.object("sections", {
2597
+ hero: (l)=>[
2598
+ l.row("heading"),
2599
+ l.object("seo", (inner)=>[
2600
+ inner.row("metaTitle", "metaDescription")
2601
+ ])
2602
+ ]
2603
+ })
2604
+ ]
2605
+ });
2606
+ const field = form.field("sections");
2607
+ field.addItem("hero");
2608
+ field.addItem("hero");
2609
+ const vm = form.field("sections").vm;
2610
+ expect(vm.items.length).toBe(2);
2611
+ for (const item of vm.items){
2612
+ const seoVm = asRow(item.layout[1]).fields[0];
2613
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2614
+ "metaTitle",
2615
+ "metaDescription"
2616
+ ]);
2617
+ }
2618
+ });
2619
+ it("registers nested layouts on a non-templated list item upon creation", ()=>{
2620
+ const form = createForm({
2621
+ fields: (fields)=>({
2622
+ rows: fields.object().list().fields((f)=>({
2623
+ label: f.text(),
2624
+ seo: f.object().fields((g)=>({
2625
+ metaTitle: g.text(),
2626
+ metaDescription: g.text()
2627
+ }))
2628
+ }))
2629
+ }),
2630
+ layout: (layout)=>[
2631
+ layout.object("rows", (l)=>[
2632
+ l.row("label"),
2633
+ l.object("seo", (inner)=>[
2634
+ inner.row("metaTitle", "metaDescription")
2635
+ ])
2636
+ ])
2637
+ ]
2638
+ });
2639
+ const field = form.field("rows");
2640
+ field.addItem();
2641
+ const vm = form.field("rows").vm;
2642
+ const seoVm = asRow(vm.items[0].layout[1]).fields[0];
2643
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2644
+ "metaTitle",
2645
+ "metaDescription"
2646
+ ]);
2647
+ });
2648
+ it("registers nested layouts on children added via field.as('object').fields() at runtime", ()=>{
2649
+ const form = createForm({
2650
+ fields: (fields)=>({
2651
+ page: fields.object().fields((f)=>({
2652
+ title: f.text()
2653
+ }))
2654
+ }),
2655
+ layout: (layout)=>[
2656
+ layout.object("page", (l)=>[
2657
+ l.row("title"),
2658
+ l.object("seo", (inner)=>[
2659
+ inner.row("metaTitle", "metaDescription")
2660
+ ])
2661
+ ])
2662
+ ]
2663
+ });
2664
+ form.field("page").as("object").fields((f)=>({
2665
+ seo: f.object().fields((g)=>({
2666
+ metaTitle: g.text(),
2667
+ metaDescription: g.text()
2668
+ }))
2669
+ }));
2670
+ const pageVm = form.field("page").vm;
2671
+ const seoRow = asRow(pageVm.layout[1]);
2672
+ const seoVm = seoRow.fields[0];
2673
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2674
+ "metaTitle",
2675
+ "metaDescription"
2676
+ ]);
2677
+ });
2678
+ it("recurses through tabs nested inside an inner layout", ()=>{
2679
+ const form = createForm({
2680
+ fields: (fields)=>({
2681
+ page: fields.object().fields((f)=>({
2682
+ title: f.text(),
2683
+ seo: f.object().fields((g)=>({
2684
+ metaTitle: g.text(),
2685
+ metaDescription: g.text()
2686
+ }))
2687
+ }))
2688
+ }),
2689
+ layout: (layout)=>[
2690
+ layout.object("page", (l)=>[
2691
+ l.tabs().tab("main", (tab)=>{
2692
+ tab.label("Main").layout((l)=>[
2693
+ l.row("title")
2694
+ ]);
2695
+ }).tab("seoTab", (tab)=>{
2696
+ tab.label("SEO").layout((l)=>[
2697
+ l.object("seo", (inner)=>[
2698
+ inner.row("metaTitle", "metaDescription")
2699
+ ])
2700
+ ]);
2701
+ })
2702
+ ])
2703
+ ]
2704
+ });
2705
+ const seoVm = form.field("page.seo").vm;
2706
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2707
+ "metaTitle",
2708
+ "metaDescription"
2709
+ ]);
2710
+ });
2711
+ it("resolves tabs inside an inner layout against the children scope", ()=>{
2712
+ const form = createForm({
2713
+ fields: (fields)=>({
2714
+ page: fields.object().fields((f)=>({
2715
+ title: f.text(),
2716
+ body: f.text(),
2717
+ seo: f.object().fields((g)=>({
2718
+ metaTitle: g.text(),
2719
+ metaDescription: g.text()
2720
+ }))
2721
+ }))
2722
+ }),
2723
+ layout: (layout)=>[
2724
+ layout.object("page", (l)=>[
2725
+ l.tabs("pageTabs").tab("general", (tab)=>{
2726
+ tab.label("General").layout((l)=>[
2727
+ l.row("title"),
2728
+ l.row("body")
2729
+ ]);
2730
+ }).tab("seo", (tab)=>{
2731
+ tab.label("SEO").layout((l)=>[
2732
+ l.object("seo", (inner)=>[
2733
+ inner.row("metaTitle", "metaDescription")
2734
+ ])
2735
+ ]);
2736
+ })
2737
+ ])
2738
+ ]
2739
+ });
2740
+ const pageVm = form.field("page").vm;
2741
+ expect(pageVm.layout.length).toBe(1);
2742
+ const tabs = pageVm.layout[0];
2743
+ expect(tabs.type).toBe("tabs");
2744
+ expect(tabs.tabs.map((t)=>t.id)).toEqual([
2745
+ "general",
2746
+ "seo"
2747
+ ]);
2748
+ const generalTab = tabs.tabs[0];
2749
+ expect(asRow(generalTab.layout[0]).fields.map((f)=>f.name)).toEqual([
2750
+ "title"
2751
+ ]);
2752
+ expect(asRow(generalTab.layout[1]).fields.map((f)=>f.name)).toEqual([
2753
+ "body"
2754
+ ]);
2755
+ const seoTab = tabs.tabs[1];
2756
+ const seoVm = asRow(seoTab.layout[0]).fields[0];
2757
+ expect(asRow(seoVm.layout[0]).fields.map((f)=>f.name)).toEqual([
2758
+ "metaTitle",
2759
+ "metaDescription"
2760
+ ]);
2761
+ });
2762
+ });
2763
+ describe("runtime template modification (Phase 8d)", ()=>{
2764
+ function createSingleTemplatedForm() {
2765
+ return createForm({
2766
+ fields: (fields)=>({
2767
+ content: fields.object().template("hero", (t)=>{
2768
+ t.label("Hero").fields((f)=>({
2769
+ heading: f.text()
2770
+ }));
2771
+ })
2772
+ })
2773
+ });
2774
+ }
2775
+ function createListTemplatedForm() {
2776
+ return createForm({
2777
+ fields: (fields)=>({
2778
+ blocks: fields.object().list().template("hero", (t)=>{
2779
+ t.label("Hero").fields((f)=>({
2780
+ heading: f.text()
2781
+ }));
2782
+ }).template("text", (t)=>{
2783
+ t.label("Text").fields((f)=>({
2784
+ body: f.text()
2785
+ }));
2786
+ })
2787
+ })
2788
+ });
2789
+ }
2790
+ it("templates.add appends a template and the picker lists it", ()=>{
2791
+ const form = createSingleTemplatedForm();
2792
+ const field = form.field("content").as("object");
2793
+ field.templates.add("text", (t)=>{
2794
+ t.label("Text").fields((f)=>({
2795
+ body: f.text()
2796
+ }));
2797
+ });
2798
+ const vm = form.field("content").vm;
2799
+ expect(vm.availableTemplates.map((t)=>t.id)).toEqual([
2800
+ "hero",
2801
+ "text"
2802
+ ]);
2803
+ });
2804
+ it("templates.add throws on duplicate id", ()=>{
2805
+ const form = createSingleTemplatedForm();
2806
+ const field = form.field("content").as("object");
2807
+ expect(()=>field.templates.add("hero", (t)=>{
2808
+ t.label("Other").fields((f)=>({
2809
+ x: f.text()
2810
+ }));
2811
+ })).toThrow(/Duplicate template id "hero"/);
2812
+ });
2813
+ it("templates.add throws on reserved _templateId", ()=>{
2814
+ const form = createSingleTemplatedForm();
2815
+ const field = form.field("content").as("object");
2816
+ expect(()=>field.templates.add("_templateId", (t)=>{
2817
+ t.label("Reserved").fields((f)=>({
2818
+ x: f.text()
2819
+ }));
2820
+ })).toThrow(/reserved/);
2821
+ });
2822
+ it("templates.add throws when the template defines a reserved _templateId field", ()=>{
2823
+ const form = createSingleTemplatedForm();
2824
+ const field = form.field("content").as("object");
2825
+ expect(()=>field.templates.add("bad", (t)=>{
2826
+ t.label("Bad").fields((f)=>({
2827
+ _templateId: f.text()
2828
+ }));
2829
+ })).toThrow(/reserved field "_templateId"/);
2830
+ });
2831
+ it("templates.remove removes the template from the picker", ()=>{
2832
+ const form = createListTemplatedForm();
2833
+ const field = form.field("blocks").as("object");
2834
+ field.templates.remove("text");
2835
+ const vm = form.field("blocks").vm;
2836
+ expect(vm.availableTemplates.map((t)=>t.id)).toEqual([
2837
+ "hero"
2838
+ ]);
2839
+ });
2840
+ it("templates.remove silently no-ops on unknown id", ()=>{
2841
+ const form = createListTemplatedForm();
2842
+ const field = form.field("blocks").as("object");
2843
+ const before = form.field("blocks").vm.availableTemplates.length;
2844
+ expect(()=>field.templates.remove("nonExistent")).not.toThrow();
2845
+ const after = form.field("blocks").vm.availableTemplates.length;
2846
+ expect(after).toBe(before);
2847
+ });
2848
+ it("templates.remove clears active template on a single-object field via onChange(null) semantics", ()=>{
2849
+ const form = createSingleTemplatedForm();
2850
+ const field = form.field("content").as("object");
2851
+ field.setTemplate("hero");
2852
+ expect(field.activeTemplateId).toBe("hero");
2853
+ field.templates.remove("hero");
2854
+ expect(field.activeTemplateId).toBeNull();
2855
+ expect(field.children.size).toBe(0);
2856
+ expect(form.field("content").getValue()).toBeNull();
2857
+ });
2858
+ it("templates.remove drops list items whose _templateId matches", ()=>{
2859
+ const form = createListTemplatedForm();
2860
+ const field = form.field("blocks").as("object");
2861
+ field.addItem("hero", {
2862
+ heading: "H1"
2863
+ });
2864
+ field.addItem("text", {
2865
+ body: "B1"
2866
+ });
2867
+ field.addItem("hero", {
2868
+ heading: "H2"
2869
+ });
2870
+ expect(field.items.length).toBe(3);
2871
+ field.templates.remove("hero");
2872
+ expect(field.items.length).toBe(1);
2873
+ expect(field.items[0].templateId).toBe("text");
2874
+ });
2875
+ it("templates.add throws when called on a non-templated object field", ()=>{
2876
+ const form = createForm({
2877
+ fields: (fields)=>({
2878
+ plain: fields.object().fields((f)=>({
2879
+ x: f.text()
2880
+ }))
2881
+ })
2882
+ });
2883
+ const field = form.field("plain").as("object");
2884
+ expect(()=>field.templates.add("x", (t)=>{
2885
+ t.label("X").fields((f)=>({
2886
+ y: f.text()
2887
+ }));
2888
+ })).toThrow(/not templated/);
2889
+ });
2890
+ it("templates.remove throws when called on a non-templated object field", ()=>{
2891
+ const form = createForm({
2892
+ fields: (fields)=>({
2893
+ plain: fields.object().fields((f)=>({
2894
+ x: f.text()
2895
+ }))
2896
+ })
2897
+ });
2898
+ const field = form.field("plain").as("object");
2899
+ expect(()=>field.templates.remove("anything")).toThrow(/not templated/);
2900
+ });
2901
+ it("orphan layout entry persists silently and is reused when the same template id is re-added", ()=>{
2902
+ const form = createForm({
2903
+ fields: (fields)=>({
2904
+ content: fields.object().template("hero", (t)=>{
2905
+ t.label("Hero").fields((f)=>({
2906
+ heading: f.text(),
2907
+ subheading: f.text()
2908
+ }));
2909
+ })
2910
+ }),
2911
+ layout: (layout)=>[
2912
+ layout.object("content", {
2913
+ hero: (l)=>[
2914
+ l.row("heading", "subheading")
2915
+ ]
2916
+ })
2917
+ ]
2918
+ });
2919
+ const field = form.field("content").as("object");
2920
+ field.setTemplate("hero");
2921
+ const vmBefore = form.field("content").vm;
2922
+ const rowBefore = asRow(vmBefore.layout[0]);
2923
+ expect(rowBefore.fields.map((f)=>f.name)).toEqual([
2924
+ "heading",
2925
+ "subheading"
2926
+ ]);
2927
+ field.templates.remove("hero");
2928
+ field.templates.add("hero", (t)=>{
2929
+ t.label("Hero v2").fields((f)=>({
2930
+ heading: f.text(),
2931
+ subheading: f.text()
2932
+ }));
2933
+ });
2934
+ field.setTemplate("hero");
2935
+ const vmAfter = form.field("content").vm;
2936
+ const rowAfter = asRow(vmAfter.layout[0]);
2937
+ expect(rowAfter.fields.map((f)=>f.name)).toEqual([
2938
+ "heading",
2939
+ "subheading"
2940
+ ]);
2941
+ });
2942
+ it("removing all templates does not emit dev warnings (orphan suppression)", ()=>{
2943
+ const warn = vi.spyOn(console, "warn").mockImplementation(()=>{});
2944
+ const form = createSingleTemplatedForm();
2945
+ const field = form.field("content").as("object");
2946
+ field.templates.remove("hero");
2947
+ const vm = form.field("content").vm;
2948
+ expect(vm.availableTemplates).toEqual([]);
2949
+ expect(warn).not.toHaveBeenCalled();
2950
+ warn.mockRestore();
2951
+ });
2952
+ });
2953
+ describe("requiredWhen (Phase 11)", ()=>{
2954
+ it("makes a field required when the callback returns true", async ()=>{
2955
+ const form = createForm({
2956
+ fields: (fields)=>({
2957
+ plan: fields.text().defaultValue("free"),
2958
+ seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Seats required")
2959
+ })
2960
+ });
2961
+ expect(form.field("seats").vm.required).toBe(false);
2962
+ expect(await form.validate()).toBe(true);
2963
+ form.field("plan").setValue("pro");
2964
+ expect(form.field("seats").vm.required).toBe(true);
2965
+ expect(await form.validate()).toBe(false);
2966
+ expect(form.field("seats").vm.validation.message).toBe("Seats required");
2967
+ });
2968
+ it("chains requiredWhen callbacks — first truthy wins", async ()=>{
2969
+ const form = createForm({
2970
+ fields: (fields)=>({
2971
+ plan: fields.text().defaultValue("free"),
2972
+ flag: fields.text().defaultValue("off"),
2973
+ seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Pro requires it").requiredWhen((f)=>"on" === f.field("flag").getValue(), "Flag requires it")
2974
+ })
2975
+ });
2976
+ form.field("flag").setValue("on");
2977
+ await form.validate();
2978
+ expect(form.field("seats").vm.validation.message).toBe("Flag requires it");
2979
+ form.field("plan").setValue("pro");
2980
+ await form.validate();
2981
+ expect(form.field("seats").vm.validation.message).toBe("Pro requires it");
2982
+ });
2983
+ it("hard .required() always wins over requiredWhen messages", async ()=>{
2984
+ const form = createForm({
2985
+ fields: (fields)=>({
2986
+ seats: fields.text().required("Always required").requiredWhen(()=>true, "Conditional message")
2987
+ })
2988
+ });
2989
+ await form.validate();
2990
+ expect(form.field("seats").vm.required).toBe(true);
2991
+ expect(form.field("seats").vm.validation.message).toBe("Always required");
2992
+ });
2993
+ it("modifier-added requiredWhen chains with builder-defined ones", async ()=>{
2994
+ const form = createForm({
2995
+ fields: (fields)=>({
2996
+ plan: fields.text().defaultValue("free"),
2997
+ other: fields.text().defaultValue("off"),
2998
+ seats: fields.text().requiredWhen((f)=>"pro" === f.field("plan").getValue(), "Pro required")
2999
+ })
3000
+ });
3001
+ form.field("seats").addRequiredWhen((f)=>"on" === f.field("other").getValue(), "Other required");
3002
+ expect(form.field("seats").vm.required).toBe(false);
3003
+ form.field("other").setValue("on");
3004
+ await form.validate();
3005
+ expect(form.field("seats").vm.validation.message).toBe("Other required");
3006
+ });
3007
+ });
3008
+ describe("computed / computedUntilDirty (Phase 11)", ()=>{
3009
+ it("computed field exposes derived value reactively", ()=>{
3010
+ const form = createForm({
3011
+ fields: (fields)=>({
3012
+ first: fields.text().defaultValue("Ada"),
3013
+ last: fields.text().defaultValue("Lovelace"),
3014
+ full: fields.text().computed((f)=>`${f.field("first").getValue()} ${f.field("last").getValue()}`)
3015
+ })
3016
+ });
3017
+ expect(form.field("full").getValue()).toBe("Ada Lovelace");
3018
+ form.field("first").setValue("Grace");
3019
+ expect(form.field("full").getValue()).toBe("Grace Lovelace");
3020
+ });
3021
+ it("computed field stays editable but value remains derived", ()=>{
3022
+ const form = createForm({
3023
+ fields: (fields)=>({
3024
+ src: fields.text().defaultValue("A"),
3025
+ derived: fields.text().computed((f)=>f.field("src").getValue())
3026
+ })
3027
+ });
3028
+ expect(form.field("derived").vm.disabled).toBe(false);
3029
+ form.field("derived").vm.onChange("manual override");
3030
+ expect(form.field("derived").getValue()).toBe("A");
3031
+ });
3032
+ it("computedUntilDirty switches to manual after first UI edit", ()=>{
3033
+ const form = createForm({
3034
+ fields: (fields)=>({
3035
+ src: fields.text().defaultValue("A"),
3036
+ derived: fields.text().computedUntilDirty((f)=>`derived-${f.field("src").getValue()}`)
3037
+ })
3038
+ });
3039
+ expect(form.field("derived").getValue()).toBe("derived-A");
3040
+ form.field("derived").vm.onChange("manual");
3041
+ expect(form.field("derived").getValue()).toBe("manual");
3042
+ form.field("src").setValue("B");
3043
+ expect(form.field("derived").getValue()).toBe("manual");
3044
+ });
3045
+ it("computed field still participates in validation", async ()=>{
3046
+ const form = createForm({
3047
+ fields: (fields)=>({
3048
+ src: fields.text().defaultValue(""),
3049
+ derived: fields.text().required("Derived must not be empty").computed((f)=>f.field("src").getValue())
3050
+ })
3051
+ });
3052
+ const valid = await form.validate();
3053
+ expect(valid).toBe(false);
3054
+ expect(form.field("derived").vm.validation.message).toBe("Derived must not be empty");
3055
+ form.field("src").setValue("hello");
3056
+ expect(await form.validate()).toBe(true);
3057
+ });
3058
+ it("modifier setComputed converts a regular field into a computed one", ()=>{
3059
+ const form = createForm({
3060
+ fields: (fields)=>({
3061
+ src: fields.text().defaultValue("X"),
3062
+ derived: fields.text().defaultValue("initial")
3063
+ })
3064
+ });
3065
+ form.field("derived").setComputed((f)=>`from-${f.field("src").getValue()}`);
3066
+ expect(form.field("derived").getValue()).toBe("from-X");
3067
+ form.field("src").setValue("Y");
3068
+ expect(form.field("derived").getValue()).toBe("from-Y");
3069
+ });
3070
+ });
3071
+ describe('field("...").as("object").fields() (Phase 11)', ()=>{
3072
+ it("adds new children to an existing object field at runtime", ()=>{
3073
+ const form = createForm({
3074
+ fields: (fields)=>({
3075
+ profile: fields.object().fields((f)=>({
3076
+ firstName: f.text().label("First")
3077
+ }))
3078
+ })
3079
+ });
3080
+ form.field("profile").as("object").fields((f)=>({
3081
+ lastName: f.text().label("Last")
3082
+ }));
3083
+ form.field("profile.firstName").setValue("Ada");
3084
+ form.field("profile.lastName").setValue("Lovelace");
3085
+ expect(form.getData()).toEqual({
3086
+ profile: {
3087
+ firstName: "Ada",
3088
+ lastName: "Lovelace"
3089
+ }
3090
+ });
3091
+ });
3092
+ it("replaces existing children when keys collide", ()=>{
3093
+ const form = createForm({
3094
+ fields: (fields)=>({
3095
+ profile: fields.object().fields((f)=>({
3096
+ firstName: f.text().label("Old")
3097
+ }))
3098
+ })
3099
+ });
3100
+ form.field("profile").as("object").fields((f)=>({
3101
+ firstName: f.text().label("New")
3102
+ }));
3103
+ const profile = form.field("profile").as("object");
3104
+ expect(profile.children.get("firstName")?.config.label).toBe("New");
3105
+ });
3106
+ it("removes children when factory returns undefined", ()=>{
3107
+ const form = createForm({
3108
+ fields: (fields)=>({
3109
+ profile: fields.object().fields((f)=>({
3110
+ firstName: f.text(),
3111
+ lastName: f.text()
3112
+ }))
3113
+ })
3114
+ });
3115
+ form.field("profile").as("object").fields(()=>({
3116
+ lastName: void 0
3117
+ }));
3118
+ const profile = form.field("profile").as("object");
3119
+ expect(profile.children.has("lastName")).toBe(false);
3120
+ expect(profile.children.has("firstName")).toBe(true);
3121
+ });
3122
+ it("propagates added children to existing list items", ()=>{
3123
+ const form = createForm({
3124
+ fields: (fields)=>({
3125
+ contacts: fields.object().list().fields((f)=>({
3126
+ name: f.text()
3127
+ }))
3128
+ })
3129
+ });
3130
+ const contacts = form.field("contacts").as("object");
3131
+ contacts.addItem({
3132
+ name: "Ada"
3133
+ });
3134
+ contacts.addItem({
3135
+ name: "Grace"
3136
+ });
3137
+ contacts.fields((f)=>({
3138
+ email: f.text()
3139
+ }));
3140
+ expect(contacts.items[0].children.has("email")).toBe(true);
3141
+ expect(contacts.items[1].children.has("email")).toBe(true);
3142
+ contacts.addItem({
3143
+ name: "Linus",
3144
+ email: "linus@example.com"
3145
+ });
3146
+ expect(contacts.items[2].children.get("email")?.getValue()).toBe("linus@example.com");
3147
+ });
3148
+ it("throws when called on a templated object field", ()=>{
3149
+ const form = createForm({
3150
+ fields: (fields)=>({
3151
+ block: fields.object().template("a", (t)=>{
3152
+ t.label("A").fields((f)=>({
3153
+ x: f.text()
3154
+ }));
3155
+ })
3156
+ })
3157
+ });
3158
+ expect(()=>{
3159
+ form.field("block").as("object").fields((f)=>({
3160
+ y: f.text()
3161
+ }));
3162
+ }).toThrow(/templated/);
3163
+ });
3164
+ });
3165
+ describe("form.addRule() (Phase 11)", ()=>{
3166
+ it("runs a Zod schema against getData() and surfaces issues", async ()=>{
3167
+ const form = createForm({
3168
+ fields: (fields)=>({
3169
+ password: fields.text().defaultValue("a"),
3170
+ confirm: fields.text().defaultValue("b")
3171
+ })
3172
+ });
3173
+ form.addRule(z.object({
3174
+ password: z.string(),
3175
+ confirm: z.string()
3176
+ }).refine((d)=>d.password === d.confirm, {
3177
+ message: "Passwords must match",
3178
+ path: [
3179
+ "confirm"
3180
+ ]
3181
+ }));
3182
+ const valid = await form.validate();
3183
+ expect(valid).toBe(false);
3184
+ expect(form.errors.some((e)=>"Passwords must match" === e.message)).toBe(true);
3185
+ expect(form.field("confirm").vm.validation.isValid).toBe(false);
3186
+ expect(form.field("confirm").vm.validation.message).toBe("Passwords must match");
3187
+ });
3188
+ it("runs an imperative function and merges returned errors", async ()=>{
3189
+ const form = createForm({
3190
+ fields: (fields)=>({
3191
+ age: fields.text().defaultValue("17")
3192
+ })
3193
+ });
3194
+ form.addRule((f)=>{
3195
+ if (Number(f.field("age").getValue()) < 18) return [
3196
+ {
3197
+ path: "age",
3198
+ message: "Must be 18+"
3199
+ }
3200
+ ];
3201
+ return [];
3202
+ });
3203
+ expect(await form.validate()).toBe(false);
3204
+ expect(form.field("age").vm.validation.message).toBe("Must be 18+");
3205
+ form.field("age").setValue("21");
3206
+ expect(await form.validate()).toBe(true);
3207
+ });
3208
+ it("supports async imperative rules", async ()=>{
3209
+ const form = createForm({
3210
+ fields: (fields)=>({
3211
+ name: fields.text().defaultValue("taken")
3212
+ })
3213
+ });
3214
+ form.addRule(async (f)=>{
3215
+ await Promise.resolve();
3216
+ if ("taken" === f.field("name").getValue()) return [
3217
+ {
3218
+ path: "name",
3219
+ message: "Already taken"
3220
+ }
3221
+ ];
3222
+ return [];
3223
+ });
3224
+ expect(await form.validate()).toBe(false);
3225
+ expect(form.field("name").vm.validation.message).toBe("Already taken");
3226
+ });
3227
+ });
3228
+ describe("form.setLayout() (Phase 11)", ()=>{
3229
+ it("replaces the layout entirely", ()=>{
3230
+ const form = createForm({
3231
+ fields: (fields)=>({
3232
+ a: fields.text(),
3233
+ b: fields.text(),
3234
+ c: fields.text()
3235
+ }),
3236
+ layout: (layout)=>[
3237
+ layout.row("a"),
3238
+ layout.row("b"),
3239
+ layout.row("c")
3240
+ ]
3241
+ });
3242
+ form.setLayout((layout)=>[
3243
+ layout.row("c", "a")
3244
+ ]);
3245
+ const layout = form.vm.layout;
3246
+ expect(layout).toHaveLength(1);
3247
+ const row = asRow(layout[0]);
3248
+ expect(row.fields.map((f)=>f.name)).toEqual([
3249
+ "c",
3250
+ "a"
3251
+ ]);
3252
+ });
3253
+ it("emits orphan warnings for fields not in the new layout", ()=>{
3254
+ const warn = vi.spyOn(console, "warn").mockImplementation(()=>{});
3255
+ const form = createForm({
3256
+ fields: (fields)=>({
3257
+ a: fields.text(),
3258
+ b: fields.text()
3259
+ })
3260
+ });
3261
+ warn.mockClear();
3262
+ form.setLayout((layout)=>[
3263
+ layout.row("a")
3264
+ ]);
3265
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining('Field "b" is not in the layout'));
3266
+ warn.mockRestore();
3267
+ });
3268
+ });
3269
+ describe("Phase 10: Advanced Validation", ()=>{
3270
+ describe("field.vm.validating", ()=>{
3271
+ it("should be false initially", ()=>{
3272
+ const form = createBasicForm();
3273
+ expect(form.field("title").vm.validating).toBe(false);
3274
+ });
3275
+ it("should be true while async schema validates", async ()=>{
3276
+ let resolveValidation;
3277
+ const form = createForm({
3278
+ fields: (fields)=>({
3279
+ email: fields.text().schema(z.string().refine(async ()=>{
3280
+ await new Promise((r)=>{
3281
+ resolveValidation = r;
3282
+ });
3283
+ return true;
3284
+ }))
3285
+ })
3286
+ });
3287
+ form.field("email").setValue("test@test.com");
3288
+ const promise = form.validate();
3289
+ await new Promise((r)=>setTimeout(r, 0));
3290
+ expect(form.field("email").vm.validating).toBe(true);
3291
+ resolveValidation();
3292
+ await promise;
3293
+ expect(form.field("email").vm.validating).toBe(false);
3294
+ });
3295
+ it("should be false after sync-only validation", async ()=>{
3296
+ const form = createForm({
3297
+ fields: (fields)=>({
3298
+ name: fields.text().required()
3299
+ })
3300
+ });
3301
+ form.field("name").setValue("hello");
3302
+ await form.validate();
3303
+ expect(form.field("name").vm.validating).toBe(false);
3304
+ });
3305
+ });
3306
+ describe("form.submitted", ()=>{
3307
+ it("should be false initially", ()=>{
3308
+ const form = createBasicForm();
3309
+ expect(form.submitted).toBe(false);
3310
+ });
3311
+ it("should be true after validate()", async ()=>{
3312
+ const form = createBasicForm();
3313
+ form.field("title").setValue("t");
3314
+ form.field("path").setValue("/p");
3315
+ await form.validate();
3316
+ expect(form.submitted).toBe(true);
3317
+ });
3318
+ it("should be true after failed validate()", async ()=>{
3319
+ const form = createBasicForm();
3320
+ await form.validate();
3321
+ expect(form.submitted).toBe(true);
3322
+ });
3323
+ it("should reset to false on setData()", async ()=>{
3324
+ const form = createBasicForm();
3325
+ form.field("title").setValue("t");
3326
+ form.field("path").setValue("/p");
3327
+ await form.validate();
3328
+ expect(form.submitted).toBe(true);
3329
+ form.setData({
3330
+ title: "new",
3331
+ path: "/new"
3332
+ });
3333
+ expect(form.submitted).toBe(false);
3334
+ });
3335
+ it("should reset to false on reset()", async ()=>{
3336
+ const form = createBasicForm();
3337
+ form.field("title").setValue("t");
3338
+ form.field("path").setValue("/p");
3339
+ await form.validate();
3340
+ expect(form.submitted).toBe(true);
3341
+ form.reset();
3342
+ expect(form.submitted).toBe(false);
3343
+ });
3344
+ });
3345
+ describe("validate-on-blur after submit", ()=>{
3346
+ it("should not validate on blur before first submit", async ()=>{
3347
+ const form = createForm({
3348
+ fields: (fields)=>({
3349
+ email: fields.text().required("Required")
3350
+ })
3351
+ });
3352
+ form.field("email").vm.onBlur();
3353
+ await new Promise((r)=>setTimeout(r, 0));
3354
+ expect(form.field("email").vm.validation.isValid).toBeNull();
3355
+ });
3356
+ it("should validate on blur after first submit", async ()=>{
3357
+ const form = createForm({
3358
+ fields: (fields)=>({
3359
+ email: fields.text().required("Required").schema(z.string().email("Invalid email"))
3360
+ })
3361
+ });
3362
+ form.field("email").setValue("bad");
3363
+ await form.submit();
3364
+ expect(form.field("email").vm.validation.isValid).toBe(false);
3365
+ form.field("email").setValue("valid@email.com");
3366
+ form.field("email").vm.onBlur();
3367
+ await new Promise((r)=>setTimeout(r, 0));
3368
+ expect(form.field("email").vm.validation.isValid).toBe(true);
3369
+ });
3370
+ it("should show error on blur for invalid value after submit", async ()=>{
3371
+ const form = createForm({
3372
+ fields: (fields)=>({
3373
+ name: fields.text().required("Name is required")
3374
+ })
3375
+ });
3376
+ form.field("name").setValue("hello");
3377
+ await form.submit();
3378
+ expect(form.field("name").vm.validation.isValid).toBe(true);
3379
+ form.field("name").setValue("");
3380
+ form.field("name").vm.onBlur();
3381
+ await new Promise((r)=>setTimeout(r, 0));
3382
+ expect(form.field("name").vm.validation.isValid).toBe(false);
3383
+ expect(form.field("name").vm.validation.message).toBe("Name is required");
3384
+ });
3385
+ });
3386
+ describe("validation memoization", ()=>{
3387
+ it("should not re-run schema on blur when value unchanged", async ()=>{
3388
+ const schemaSpy = vi.fn().mockReturnValue(true);
3389
+ const form = createForm({
3390
+ fields: (fields)=>({
3391
+ slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
3392
+ })
3393
+ });
3394
+ form.field("slug").setValue("hello");
3395
+ await form.submit();
3396
+ expect(schemaSpy).toHaveBeenCalledTimes(1);
3397
+ form.field("slug").vm.onBlur();
3398
+ await new Promise((r)=>setTimeout(r, 0));
3399
+ expect(schemaSpy).toHaveBeenCalledTimes(1);
3400
+ });
3401
+ it("should re-run schema on blur when value changed", async ()=>{
3402
+ const schemaSpy = vi.fn().mockReturnValue(true);
3403
+ const form = createForm({
3404
+ fields: (fields)=>({
3405
+ slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
3406
+ })
3407
+ });
3408
+ form.field("slug").setValue("hello");
3409
+ await form.submit();
3410
+ expect(schemaSpy).toHaveBeenCalledTimes(1);
3411
+ form.field("slug").setValue("world");
3412
+ form.field("slug").vm.onBlur();
3413
+ await new Promise((r)=>setTimeout(r, 0));
3414
+ expect(schemaSpy).toHaveBeenCalledTimes(2);
3415
+ });
3416
+ it("should always re-run schema on form.validate()", async ()=>{
3417
+ const schemaSpy = vi.fn().mockReturnValue(true);
3418
+ const form = createForm({
3419
+ fields: (fields)=>({
3420
+ slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
3421
+ })
3422
+ });
3423
+ form.field("slug").setValue("hello");
3424
+ await form.validate();
3425
+ expect(schemaSpy).toHaveBeenCalledTimes(1);
3426
+ await form.validate();
3427
+ expect(schemaSpy).toHaveBeenCalledTimes(2);
3428
+ });
3429
+ it("should clear cache on resetValidation()", async ()=>{
3430
+ const schemaSpy = vi.fn().mockReturnValue(true);
3431
+ const form = createForm({
3432
+ fields: (fields)=>({
3433
+ slug: fields.text().schema(z.string().refine(schemaSpy, "fail"))
3434
+ })
3435
+ });
3436
+ form.field("slug").setValue("hello");
3437
+ await form.submit();
3438
+ expect(schemaSpy).toHaveBeenCalledTimes(1);
3439
+ form.field("slug").resetValidation();
3440
+ form.field("slug").vm.onBlur();
3441
+ await new Promise((r)=>setTimeout(r, 0));
3442
+ expect(schemaSpy).toHaveBeenCalledTimes(2);
3443
+ });
3444
+ });
3445
+ describe("async validation with z.refine", ()=>{
3446
+ it("should validate async refine on submit", async ()=>{
3447
+ const form = createForm({
3448
+ fields: (fields)=>({
3449
+ slug: fields.text().schema(z.string().refine(async (value)=>"taken" !== value, "This slug is already taken"))
3450
+ })
3451
+ });
3452
+ form.field("slug").setValue("taken");
3453
+ const result = await form.submit();
3454
+ expect(result).toBe(false);
3455
+ expect(form.field("slug").vm.validation.isValid).toBe(false);
3456
+ expect(form.field("slug").vm.validation.message).toBe("This slug is already taken");
3457
+ });
3458
+ it("should pass async refine with valid value", async ()=>{
3459
+ const form = createForm({
3460
+ fields: (fields)=>({
3461
+ slug: fields.text().schema(z.string().refine(async (value)=>"taken" !== value, "This slug is already taken"))
3462
+ })
3463
+ });
3464
+ form.field("slug").setValue("available");
3465
+ const result = await form.submit();
3466
+ expect(result).toEqual({
3467
+ slug: "available"
3468
+ });
3469
+ expect(form.field("slug").vm.validation.isValid).toBe(true);
3470
+ });
3471
+ });
3472
+ });
3473
+ describe("normalizeValue", ()=>{
3474
+ it("should coerce string to number on setValue for number fields", ()=>{
3475
+ const form = createForm({
3476
+ fields: (fields)=>({
3477
+ count: fields.number().label("Count")
3478
+ })
3479
+ });
3480
+ form.field("count").setValue("42");
3481
+ expect(form.field("count").getValue()).toBe(42);
3482
+ });
3483
+ it("should normalize invalid values to null for number fields", ()=>{
3484
+ const form = createForm({
3485
+ fields: (fields)=>({
3486
+ count: fields.number().label("Count")
3487
+ })
3488
+ });
3489
+ form.field("count").setValue("");
3490
+ expect(form.field("count").getValue()).toBe(null);
3491
+ form.field("count").setValue(null);
3492
+ expect(form.field("count").getValue()).toBe(null);
3493
+ form.field("count").setValue("abc");
3494
+ expect(form.field("count").getValue()).toBe(null);
3495
+ });
3496
+ it("should store number for number field with options", ()=>{
3497
+ const form = createForm({
3498
+ fields: (fields)=>({
3499
+ tier: fields.number().label("Tier").options([
3500
+ {
3501
+ label: "Tier 1",
3502
+ value: 100
3503
+ },
3504
+ {
3505
+ label: "Tier 2",
3506
+ value: 200
3507
+ }
3508
+ ])
3509
+ })
3510
+ });
3511
+ form.field("tier").setValue("100");
3512
+ expect(form.field("tier").getValue()).toBe(100);
3513
+ expect(typeof form.field("tier").getValue()).toBe("number");
3514
+ });
3515
+ it("should coerce to boolean on setValue for boolean fields", ()=>{
3516
+ const form = createForm({
3517
+ fields: (fields)=>({
3518
+ active: fields.boolean().label("Active")
3519
+ })
3520
+ });
3521
+ form.field("active").setValue(1);
3522
+ expect(form.field("active").getValue()).toBe(true);
3523
+ form.field("active").setValue(0);
3524
+ expect(form.field("active").getValue()).toBe(false);
3525
+ });
3526
+ it("should apply normalizeValue on setData (via setValueSilent)", ()=>{
3527
+ const form = createForm({
3528
+ fields: (fields)=>({
3529
+ count: fields.number().label("Count")
3530
+ })
3531
+ });
3532
+ form.setData({
3533
+ count: "42"
3534
+ });
3535
+ expect(form.field("count").getValue()).toBe(42);
3536
+ expect(typeof form.field("count").getValue()).toBe("number");
3537
+ });
3538
+ it("should not alter text field values", ()=>{
3539
+ const form = createForm({
3540
+ fields: (fields)=>({
3541
+ name: fields.text().label("Name")
3542
+ })
3543
+ });
3544
+ form.field("name").setValue("hello");
3545
+ expect(form.field("name").getValue()).toBe("hello");
3546
+ form.field("name").setValue(42);
3547
+ expect(form.field("name").getValue()).toBe(42);
3548
+ });
3549
+ it("should run normalizeValue before beforeChange", ()=>{
3550
+ const log = [];
3551
+ const form = createForm({
3552
+ fields: (fields)=>({
3553
+ count: fields.number().label("Count").beforeChange((value)=>{
3554
+ log.push(value);
3555
+ return value;
3556
+ })
3557
+ })
3558
+ });
3559
+ form.field("count").setValue("7");
3560
+ expect(log).toEqual([
3561
+ 7
3562
+ ]);
3563
+ });
3564
+ });
3565
+ describe("focusField", ()=>{
3566
+ it("should set focusRequested on the target field", ()=>{
3567
+ const form = createForm({
3568
+ fields: (fields)=>({
3569
+ title: fields.text().label("Title"),
3570
+ path: fields.text().label("Path")
3571
+ })
3572
+ });
3573
+ form.focusField("title");
3574
+ expect(form.field("title").vm.focusRequested).toBe(true);
3575
+ expect(form.field("path").vm.focusRequested).toBe(false);
3576
+ });
3577
+ it("should activate the correct tab", ()=>{
3578
+ const form = createForm({
3579
+ fields: (fields)=>({
3580
+ title: fields.text().label("Title"),
3581
+ slug: fields.text().label("Slug")
3582
+ }),
3583
+ layout: (layout)=>[
3584
+ layout.tabs("mainTabs").tab("general", (tab)=>{
3585
+ tab.label("General").layout((l)=>[
3586
+ l.row("title")
3587
+ ]);
3588
+ }).tab("seo", (tab)=>{
3589
+ tab.label("SEO").layout((l)=>[
3590
+ l.row("slug")
3591
+ ]);
3592
+ })
3593
+ ]
3594
+ });
3595
+ expect(form.vm.layout[0].activeTabId).toBe("general");
3596
+ form.focusField("slug");
3597
+ expect(form.field("slug").vm.focusRequested).toBe(true);
3598
+ expect(form.vm.layout[0].activeTabId).toBe("seo");
3599
+ });
3600
+ it("should activate nested tabs inside object fields", ()=>{
3601
+ const form = createForm({
3602
+ fields: (fields)=>({
3603
+ page: fields.object().fields((f)=>({
3604
+ title: f.text().label("Title"),
3605
+ metaTitle: f.text().label("Meta Title")
3606
+ }))
3607
+ }),
3608
+ layout: (layout)=>[
3609
+ layout.object("page", (l)=>[
3610
+ l.tabs("pageTabs").tab("general", (tab)=>{
3611
+ tab.label("General").layout((l)=>[
3612
+ l.row("title")
3613
+ ]);
3614
+ }).tab("seo", (tab)=>{
3615
+ tab.label("SEO").layout((l)=>[
3616
+ l.row("metaTitle")
3617
+ ]);
3618
+ })
3619
+ ])
3620
+ ]
3621
+ });
3622
+ form.focusField("page.metaTitle");
3623
+ expect(form.field("page.metaTitle").vm.focusRequested).toBe(true);
3624
+ });
3625
+ it("should clear previous focus when focusing a new field", ()=>{
3626
+ const form = createForm({
3627
+ fields: (fields)=>({
3628
+ title: fields.text().label("Title"),
3629
+ path: fields.text().label("Path")
3630
+ })
3631
+ });
3632
+ form.focusField("title");
3633
+ expect(form.field("title").vm.focusRequested).toBe(true);
3634
+ form.focusField("path");
3635
+ expect(form.field("title").vm.focusRequested).toBe(false);
3636
+ expect(form.field("path").vm.focusRequested).toBe(true);
3637
+ });
3638
+ it("should allow renderer to clear focus via clearFocusRequest", ()=>{
3639
+ const form = createForm({
3640
+ fields: (fields)=>({
3641
+ title: fields.text().label("Title")
3642
+ })
3643
+ });
3644
+ form.focusField("title");
3645
+ expect(form.field("title").vm.focusRequested).toBe(true);
3646
+ form.field("title").vm.clearFocusRequest();
3647
+ expect(form.field("title").vm.focusRequested).toBe(false);
3648
+ });
3649
+ it("should not throw on unknown field", ()=>{
3650
+ const form = createForm({
3651
+ fields: (fields)=>({
3652
+ title: fields.text().label("Title")
3653
+ })
3654
+ });
3655
+ expect(()=>form.focusField("nonexistent")).not.toThrow();
3656
+ });
3657
+ it("should propagate qualifiedName through nested objects", ()=>{
3658
+ const form = createForm({
3659
+ fields: (fields)=>({
3660
+ page: fields.object().fields((f)=>({
3661
+ seo: f.object().fields((g)=>({
3662
+ metaTitle: g.text().label("Meta Title")
3663
+ }))
3664
+ }))
3665
+ })
3666
+ });
3667
+ const meta = form.field("page.seo.metaTitle");
3668
+ expect(meta.qualifiedName).toBe("page.seo.metaTitle");
3669
+ });
3670
+ it("field.focus() should delegate to form.focusField", ()=>{
3671
+ const form = createForm({
3672
+ fields: (fields)=>({
3673
+ title: fields.text().label("Title"),
3674
+ path: fields.text().label("Path")
3675
+ }),
3676
+ layout: (layout)=>[
3677
+ layout.tabs("tabs").tab("t1", (tab)=>{
3678
+ tab.label("T1").layout((l)=>[
3679
+ l.row("title")
3680
+ ]);
3681
+ }).tab("t2", (tab)=>{
3682
+ tab.label("T2").layout((l)=>[
3683
+ l.row("path")
3684
+ ]);
3685
+ })
3686
+ ]
3687
+ });
3688
+ form.field("path").focus();
3689
+ expect(form.field("path").vm.focusRequested).toBe(true);
3690
+ expect(form.vm.layout[0].activeTabId).toBe("t2");
3691
+ });
3692
+ });
3693
+ describe("primitive list addItem / removeItem", ()=>{
3694
+ it("addItem appends to empty list", ()=>{
3695
+ const form = createForm({
3696
+ fields: (f)=>({
3697
+ tags: f.text().list()
3698
+ }),
3699
+ layout: (l)=>[
3700
+ l.row("tags")
3701
+ ]
3702
+ });
3703
+ const vm = form.field("tags").vm;
3704
+ expect(vm.value).toEqual([]);
3705
+ vm.addItem("hello");
3706
+ expect(form.field("tags").vm.value).toEqual([
3707
+ "hello"
3708
+ ]);
3709
+ });
3710
+ it("addItem appends with default null when no value given", ()=>{
3711
+ const form = createForm({
3712
+ fields: (f)=>({
3713
+ tags: f.text().list()
3714
+ }),
3715
+ layout: (l)=>[
3716
+ l.row("tags")
3717
+ ]
3718
+ });
3719
+ form.field("tags").vm.addItem();
3720
+ expect(form.field("tags").vm.value).toEqual([
3721
+ null
3722
+ ]);
3723
+ });
3724
+ it("addItem appends to existing values", ()=>{
3725
+ const form = createForm({
3726
+ fields: (f)=>({
3727
+ tags: f.text().list().defaultValue([
3728
+ "a",
3729
+ "b"
3730
+ ])
3731
+ }),
3732
+ layout: (l)=>[
3733
+ l.row("tags")
3734
+ ]
3735
+ });
3736
+ form.field("tags").vm.addItem("c");
3737
+ expect(form.field("tags").vm.value).toEqual([
3738
+ "a",
3739
+ "b",
3740
+ "c"
3741
+ ]);
3742
+ });
3743
+ it("removeItem removes at index", ()=>{
3744
+ const form = createForm({
3745
+ fields: (f)=>({
3746
+ tags: f.text().list().defaultValue([
3747
+ "a",
3748
+ "b",
3749
+ "c"
3750
+ ])
3751
+ }),
3752
+ layout: (l)=>[
3753
+ l.row("tags")
3754
+ ]
3755
+ });
3756
+ form.field("tags").vm.removeItem(1);
3757
+ expect(form.field("tags").vm.value).toEqual([
3758
+ "a",
3759
+ "c"
3760
+ ]);
3761
+ });
3762
+ it("removeItem from beginning preserves order", ()=>{
3763
+ const form = createForm({
3764
+ fields: (f)=>({
3765
+ tags: f.text().list().defaultValue([
3766
+ "a",
3767
+ "b",
3768
+ "c"
3769
+ ])
3770
+ }),
3771
+ layout: (l)=>[
3772
+ l.row("tags")
3773
+ ]
3774
+ });
3775
+ form.field("tags").vm.removeItem(0);
3776
+ expect(form.field("tags").vm.value).toEqual([
3777
+ "b",
3778
+ "c"
3779
+ ]);
3780
+ });
3781
+ it("operations go through beforeChange pipeline", ()=>{
3782
+ const log = [];
3783
+ const form = createForm({
3784
+ fields: (f)=>({
3785
+ tags: f.text().list().defaultValue([
3786
+ "a"
3787
+ ]).beforeChange((value, _form)=>{
3788
+ log.push(value);
3789
+ return value;
3790
+ })
3791
+ }),
3792
+ layout: (l)=>[
3793
+ l.row("tags")
3794
+ ]
3795
+ });
3796
+ form.field("tags").vm.addItem("b");
3797
+ form.field("tags").vm.removeItem(0);
3798
+ expect(log).toEqual([
3799
+ [
3800
+ "a",
3801
+ "b"
3802
+ ],
3803
+ [
3804
+ "b"
3805
+ ]
3806
+ ]);
3807
+ });
3808
+ });
3809
+ });
3810
+
3811
+ //# sourceMappingURL=FormModel.test.js.map