@maxal_studio/kratosjs-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/README.md +44 -0
  2. package/dist/FieldRenderer.d.ts +13 -0
  3. package/dist/FieldRenderer.js +62 -0
  4. package/dist/FormRenderer.d.ts +7 -0
  5. package/dist/FormRenderer.js +78 -0
  6. package/dist/TableRenderer.d.ts +2 -0
  7. package/dist/TableRenderer.js +1 -0
  8. package/dist/api/actionsApi.d.ts +23 -0
  9. package/dist/api/actionsApi.js +46 -0
  10. package/dist/api/authenticatedFetch.d.ts +8 -0
  11. package/dist/api/authenticatedFetch.js +31 -0
  12. package/dist/api/exportApi.d.ts +18 -0
  13. package/dist/api/exportApi.js +50 -0
  14. package/dist/api/http.d.ts +24 -0
  15. package/dist/api/http.js +52 -0
  16. package/dist/api/resourceApi.d.ts +37 -0
  17. package/dist/api/resourceApi.js +52 -0
  18. package/dist/api/tableApi.d.ts +83 -0
  19. package/dist/api/tableApi.js +51 -0
  20. package/dist/api/urls.d.ts +19 -0
  21. package/dist/api/urls.js +46 -0
  22. package/dist/app.d.ts +101 -0
  23. package/dist/app.js +89 -0
  24. package/dist/auth/AuthContext.d.ts +22 -0
  25. package/dist/auth/AuthContext.js +147 -0
  26. package/dist/auth/LoginPage.d.ts +10 -0
  27. package/dist/auth/LoginPage.js +179 -0
  28. package/dist/auth/ProtectedRoute.d.ts +12 -0
  29. package/dist/auth/ProtectedRoute.js +22 -0
  30. package/dist/auth/authApiClient.d.ts +24 -0
  31. package/dist/auth/authApiClient.js +95 -0
  32. package/dist/auth/types.d.ts +103 -0
  33. package/dist/auth/types.js +1 -0
  34. package/dist/components/ActionFormModal.d.ts +22 -0
  35. package/dist/components/ActionFormModal.js +8 -0
  36. package/dist/components/AdminPanel.d.ts +11 -0
  37. package/dist/components/AdminPanel.js +194 -0
  38. package/dist/components/Checkbox.d.ts +10 -0
  39. package/dist/components/Checkbox.js +8 -0
  40. package/dist/components/CheckboxField.d.ts +7 -0
  41. package/dist/components/CheckboxField.js +26 -0
  42. package/dist/components/ColorPickerField.d.ts +7 -0
  43. package/dist/components/ColorPickerField.js +26 -0
  44. package/dist/components/DateTimePickerField.d.ts +7 -0
  45. package/dist/components/DateTimePickerField.js +64 -0
  46. package/dist/components/FileUploadField.d.ts +9 -0
  47. package/dist/components/FileUploadField.js +478 -0
  48. package/dist/components/GlobalSearch.d.ts +22 -0
  49. package/dist/components/GlobalSearch.js +181 -0
  50. package/dist/components/GroupField.d.ts +7 -0
  51. package/dist/components/GroupField.js +23 -0
  52. package/dist/components/HiddenField.d.ts +3 -0
  53. package/dist/components/HiddenField.js +10 -0
  54. package/dist/components/ModalBreadcrumb.d.ts +5 -0
  55. package/dist/components/ModalBreadcrumb.js +33 -0
  56. package/dist/components/ModalDrawer.d.ts +15 -0
  57. package/dist/components/ModalDrawer.js +40 -0
  58. package/dist/components/RadioField.d.ts +7 -0
  59. package/dist/components/RadioField.js +26 -0
  60. package/dist/components/RepeaterField.d.ts +3 -0
  61. package/dist/components/RepeaterField.js +191 -0
  62. package/dist/components/ResourceModalRenderer.d.ts +10 -0
  63. package/dist/components/ResourceModalRenderer.js +80 -0
  64. package/dist/components/RichEditorField.d.ts +3 -0
  65. package/dist/components/RichEditorField.js +655 -0
  66. package/dist/components/SectionField.d.ts +9 -0
  67. package/dist/components/SectionField.js +111 -0
  68. package/dist/components/SelectField.d.ts +8 -0
  69. package/dist/components/SelectField.js +523 -0
  70. package/dist/components/TabsField.d.ts +10 -0
  71. package/dist/components/TabsField.js +214 -0
  72. package/dist/components/TagsInputField.d.ts +7 -0
  73. package/dist/components/TagsInputField.js +172 -0
  74. package/dist/components/TextInputField.d.ts +7 -0
  75. package/dist/components/TextInputField.js +44 -0
  76. package/dist/components/TextareaField.d.ts +7 -0
  77. package/dist/components/TextareaField.js +31 -0
  78. package/dist/components/ToggleField.d.ts +7 -0
  79. package/dist/components/ToggleField.js +57 -0
  80. package/dist/components/ViewModal.d.ts +25 -0
  81. package/dist/components/ViewModal.js +159 -0
  82. package/dist/components/blocks/BlockRenderer.d.ts +7 -0
  83. package/dist/components/blocks/BlockRenderer.js +36 -0
  84. package/dist/components/blocks/FormBlockRenderer.d.ts +6 -0
  85. package/dist/components/blocks/FormBlockRenderer.js +110 -0
  86. package/dist/components/blocks/TableBlockRenderer.d.ts +6 -0
  87. package/dist/components/blocks/TableBlockRenderer.js +12 -0
  88. package/dist/components/blocks/TabsBlockRenderer.d.ts +7 -0
  89. package/dist/components/blocks/TabsBlockRenderer.js +11 -0
  90. package/dist/components/blocks/WidgetBlockRenderer.d.ts +6 -0
  91. package/dist/components/blocks/WidgetBlockRenderer.js +11 -0
  92. package/dist/components/columns/CheckboxColumnComponent.d.ts +6 -0
  93. package/dist/components/columns/CheckboxColumnComponent.js +21 -0
  94. package/dist/components/columns/ColorColumnComponent.d.ts +3 -0
  95. package/dist/components/columns/ColorColumnComponent.js +11 -0
  96. package/dist/components/columns/DeeplinkWrapper.d.ts +15 -0
  97. package/dist/components/columns/DeeplinkWrapper.js +85 -0
  98. package/dist/components/columns/IconColumnComponent.d.ts +3 -0
  99. package/dist/components/columns/IconColumnComponent.js +52 -0
  100. package/dist/components/columns/ImageColumnComponent.d.ts +3 -0
  101. package/dist/components/columns/ImageColumnComponent.js +98 -0
  102. package/dist/components/columns/MediaColumnComponent.d.ts +3 -0
  103. package/dist/components/columns/MediaColumnComponent.js +160 -0
  104. package/dist/components/columns/SelectColumnComponent.d.ts +6 -0
  105. package/dist/components/columns/SelectColumnComponent.js +26 -0
  106. package/dist/components/columns/TagsColumnComponent.d.ts +3 -0
  107. package/dist/components/columns/TagsColumnComponent.js +18 -0
  108. package/dist/components/columns/TextColumnComponent.d.ts +11 -0
  109. package/dist/components/columns/TextColumnComponent.js +107 -0
  110. package/dist/components/columns/TextInputColumnComponent.d.ts +6 -0
  111. package/dist/components/columns/TextInputColumnComponent.js +18 -0
  112. package/dist/components/columns/ToggleColumnComponent.d.ts +6 -0
  113. package/dist/components/columns/ToggleColumnComponent.js +25 -0
  114. package/dist/components/columns/VideoColumnComponent.d.ts +3 -0
  115. package/dist/components/columns/VideoColumnComponent.js +125 -0
  116. package/dist/components/columns/ViewColumnComponent.d.ts +3 -0
  117. package/dist/components/columns/ViewColumnComponent.js +7 -0
  118. package/dist/components/errors/ErrorBoundary.d.ts +23 -0
  119. package/dist/components/errors/ErrorBoundary.js +33 -0
  120. package/dist/components/filters/CustomFilterComponent.d.ts +10 -0
  121. package/dist/components/filters/CustomFilterComponent.js +33 -0
  122. package/dist/components/filters/DateFilterComponent.d.ts +15 -0
  123. package/dist/components/filters/DateFilterComponent.js +132 -0
  124. package/dist/components/filters/QueryBuilderFilterComponent.d.ts +11 -0
  125. package/dist/components/filters/QueryBuilderFilterComponent.js +200 -0
  126. package/dist/components/layout/Header.d.ts +10 -0
  127. package/dist/components/layout/Header.js +70 -0
  128. package/dist/components/layout/PanelBrandMark.d.ts +8 -0
  129. package/dist/components/layout/PanelBrandMark.js +28 -0
  130. package/dist/components/layout/Sidebar.d.ts +35 -0
  131. package/dist/components/layout/Sidebar.js +125 -0
  132. package/dist/components/modals/RelationCreateModal.d.ts +19 -0
  133. package/dist/components/modals/RelationCreateModal.js +57 -0
  134. package/dist/components/modals/ResourceFormModal.d.ts +37 -0
  135. package/dist/components/modals/ResourceFormModal.js +44 -0
  136. package/dist/components/modals/useResourceForm.d.ts +40 -0
  137. package/dist/components/modals/useResourceForm.js +138 -0
  138. package/dist/components/modals/view/RecordActions.d.ts +17 -0
  139. package/dist/components/modals/view/RecordActions.js +16 -0
  140. package/dist/components/modals/view/RecordDetails.d.ts +13 -0
  141. package/dist/components/modals/view/RecordDetails.js +29 -0
  142. package/dist/components/modals/view/RelationPanel.d.ts +18 -0
  143. package/dist/components/modals/view/RelationPanel.js +16 -0
  144. package/dist/components/modals/view/RelationTabs.d.ts +32 -0
  145. package/dist/components/modals/view/RelationTabs.js +42 -0
  146. package/dist/components/modals/view/useRecordView.d.ts +18 -0
  147. package/dist/components/modals/view/useRecordView.js +114 -0
  148. package/dist/components/pages/PageRenderer.d.ts +6 -0
  149. package/dist/components/pages/PageRenderer.js +107 -0
  150. package/dist/components/table/ColumnTogglePopup.d.ts +11 -0
  151. package/dist/components/table/ColumnTogglePopup.js +16 -0
  152. package/dist/components/table/GridCard.d.ts +21 -0
  153. package/dist/components/table/GridCard.js +30 -0
  154. package/dist/components/table/GridView.d.ts +23 -0
  155. package/dist/components/table/GridView.js +49 -0
  156. package/dist/components/table/LayoutToggle.d.ts +7 -0
  157. package/dist/components/table/LayoutToggle.js +9 -0
  158. package/dist/components/table/TableActionsDropdown.d.ts +13 -0
  159. package/dist/components/table/TableActionsDropdown.js +46 -0
  160. package/dist/components/table/TableBulkActions.d.ts +11 -0
  161. package/dist/components/table/TableBulkActions.js +21 -0
  162. package/dist/components/table/TableHeader.d.ts +14 -0
  163. package/dist/components/table/TableHeader.js +23 -0
  164. package/dist/components/table/TablePagination.d.ts +13 -0
  165. package/dist/components/table/TablePagination.js +55 -0
  166. package/dist/components/table/TableRow.d.ts +21 -0
  167. package/dist/components/table/TableRow.js +32 -0
  168. package/dist/components/table/TableSearchBar.d.ts +11 -0
  169. package/dist/components/table/TableSearchBar.js +12 -0
  170. package/dist/components/table/TableTabs.d.ts +14 -0
  171. package/dist/components/table/TableTabs.js +8 -0
  172. package/dist/components/ui/Badge.d.ts +6 -0
  173. package/dist/components/ui/Badge.js +12 -0
  174. package/dist/components/ui/Button.d.ts +22 -0
  175. package/dist/components/ui/Button.js +22 -0
  176. package/dist/components/ui/Card.d.ts +7 -0
  177. package/dist/components/ui/Card.js +5 -0
  178. package/dist/components/ui/ConfirmDialog.d.ts +19 -0
  179. package/dist/components/ui/ConfirmDialog.js +45 -0
  180. package/dist/components/ui/EmptyState.d.ts +9 -0
  181. package/dist/components/ui/EmptyState.js +6 -0
  182. package/dist/components/ui/ErrorAlert.d.ts +7 -0
  183. package/dist/components/ui/ErrorAlert.js +9 -0
  184. package/dist/components/ui/Input.d.ts +11 -0
  185. package/dist/components/ui/Input.js +10 -0
  186. package/dist/components/ui/Label.d.ts +5 -0
  187. package/dist/components/ui/Label.js +5 -0
  188. package/dist/components/ui/PillButton.d.ts +14 -0
  189. package/dist/components/ui/PillButton.js +19 -0
  190. package/dist/components/ui/Select.d.ts +7 -0
  191. package/dist/components/ui/Select.js +7 -0
  192. package/dist/components/ui/Spinner.d.ts +8 -0
  193. package/dist/components/ui/Spinner.js +14 -0
  194. package/dist/components/ui/Toast.d.ts +21 -0
  195. package/dist/components/ui/Toast.js +47 -0
  196. package/dist/components/ui/index.d.ts +24 -0
  197. package/dist/components/ui/index.js +12 -0
  198. package/dist/components/utils/HintDisplay.d.ts +11 -0
  199. package/dist/components/utils/HintDisplay.js +12 -0
  200. package/dist/components/utils/Icon.d.ts +22 -0
  201. package/dist/components/utils/Icon.js +22 -0
  202. package/dist/components/utils/MediaPreviewModal.d.ts +14 -0
  203. package/dist/components/utils/MediaPreviewModal.js +32 -0
  204. package/dist/components/utils/ViewFieldWrapper.d.ts +11 -0
  205. package/dist/components/utils/ViewFieldWrapper.js +9 -0
  206. package/dist/components/utils/layoutHelpers.d.ts +19 -0
  207. package/dist/components/utils/layoutHelpers.js +257 -0
  208. package/dist/components/widgets/ChartWidget.d.ts +16 -0
  209. package/dist/components/widgets/ChartWidget.js +192 -0
  210. package/dist/components/widgets/StatsWidget.d.ts +16 -0
  211. package/dist/components/widgets/StatsWidget.js +39 -0
  212. package/dist/components/widgets/WidgetRenderer.d.ts +10 -0
  213. package/dist/components/widgets/WidgetRenderer.js +50 -0
  214. package/dist/components/widgets/WidgetShell.d.ts +9 -0
  215. package/dist/components/widgets/WidgetShell.js +7 -0
  216. package/dist/contexts/AuthChallengeRegistryContext.d.ts +15 -0
  217. package/dist/contexts/AuthChallengeRegistryContext.js +15 -0
  218. package/dist/contexts/BlockRegistryContext.d.ts +18 -0
  219. package/dist/contexts/BlockRegistryContext.js +8 -0
  220. package/dist/contexts/ColumnRegistryContext.d.ts +8 -0
  221. package/dist/contexts/ColumnRegistryContext.js +30 -0
  222. package/dist/contexts/FieldRegistryContext.d.ts +13 -0
  223. package/dist/contexts/FieldRegistryContext.js +46 -0
  224. package/dist/contexts/PanelMetadataContext.d.ts +26 -0
  225. package/dist/contexts/PanelMetadataContext.js +26 -0
  226. package/dist/contexts/PanelProviders.d.ts +27 -0
  227. package/dist/contexts/PanelProviders.js +24 -0
  228. package/dist/contexts/ResourceModalContext.d.ts +26 -0
  229. package/dist/contexts/ResourceModalContext.js +76 -0
  230. package/dist/contexts/SlotRegistryContext.d.ts +19 -0
  231. package/dist/contexts/SlotRegistryContext.js +24 -0
  232. package/dist/contexts/TableRefreshContext.d.ts +10 -0
  233. package/dist/contexts/TableRefreshContext.js +30 -0
  234. package/dist/contexts/WidgetRegistryContext.d.ts +17 -0
  235. package/dist/contexts/WidgetRegistryContext.js +14 -0
  236. package/dist/contexts/createRegistryContext.d.ts +19 -0
  237. package/dist/contexts/createRegistryContext.js +20 -0
  238. package/dist/hooks/useAfterStateUpdated.d.ts +6 -0
  239. package/dist/hooks/useAfterStateUpdated.js +62 -0
  240. package/dist/hooks/useValidation.d.ts +26 -0
  241. package/dist/hooks/useValidation.js +76 -0
  242. package/dist/i18n/I18nProvider.d.ts +27 -0
  243. package/dist/i18n/I18nProvider.js +101 -0
  244. package/dist/i18n/LocaleSwitcher.d.ts +10 -0
  245. package/dist/i18n/LocaleSwitcher.js +30 -0
  246. package/dist/i18n/activeLocale.d.ts +11 -0
  247. package/dist/i18n/activeLocale.js +34 -0
  248. package/dist/i18n/buildClientI18n.d.ts +28 -0
  249. package/dist/i18n/buildClientI18n.js +67 -0
  250. package/dist/i18n/index.d.ts +11 -0
  251. package/dist/i18n/index.js +9 -0
  252. package/dist/i18n/locales/core/en.d.ts +225 -0
  253. package/dist/i18n/locales/core/en.js +252 -0
  254. package/dist/i18n/locales/core/index.d.ts +2 -0
  255. package/dist/i18n/locales/core/index.js +4 -0
  256. package/dist/i18n/locales/core/sq.d.ts +253 -0
  257. package/dist/i18n/locales/core/sq.js +255 -0
  258. package/dist/i18n/useFormatter.d.ts +18 -0
  259. package/dist/i18n/useFormatter.js +37 -0
  260. package/dist/i18n/useLocale.d.ts +11 -0
  261. package/dist/i18n/useLocale.js +11 -0
  262. package/dist/i18n/useTranslation.d.ts +12 -0
  263. package/dist/i18n/useTranslation.js +12 -0
  264. package/dist/index.d.ts +106 -0
  265. package/dist/index.js +101 -0
  266. package/dist/pages/ResourceListPage.d.ts +8 -0
  267. package/dist/pages/ResourceListPage.js +139 -0
  268. package/dist/plugin.d.ts +79 -0
  269. package/dist/plugin.js +34 -0
  270. package/dist/runtime/conditions.d.ts +35 -0
  271. package/dist/runtime/conditions.js +97 -0
  272. package/dist/runtime/formTraversal.d.ts +25 -0
  273. package/dist/runtime/formTraversal.js +37 -0
  274. package/dist/runtime/serializedFunctions.d.ts +41 -0
  275. package/dist/runtime/serializedFunctions.js +264 -0
  276. package/dist/slots/Slot.d.ts +24 -0
  277. package/dist/slots/Slot.js +29 -0
  278. package/dist/slots/SlotCluster.d.ts +22 -0
  279. package/dist/slots/SlotCluster.js +49 -0
  280. package/dist/slots/index.d.ts +7 -0
  281. package/dist/slots/index.js +4 -0
  282. package/dist/slots/mergeSlots.d.ts +18 -0
  283. package/dist/slots/mergeSlots.js +35 -0
  284. package/dist/slots/types.d.ts +87 -0
  285. package/dist/slots/types.js +30 -0
  286. package/dist/styles.css +1 -0
  287. package/dist/table/TableContext.d.ts +36 -0
  288. package/dist/table/TableContext.js +13 -0
  289. package/dist/table/TableRenderer.d.ts +29 -0
  290. package/dist/table/TableRenderer.js +159 -0
  291. package/dist/table/components/FiltersPanel.d.ts +11 -0
  292. package/dist/table/components/FiltersPanel.js +52 -0
  293. package/dist/table/components/TableToolbar.d.ts +28 -0
  294. package/dist/table/components/TableToolbar.js +27 -0
  295. package/dist/table/components/TableToolbarButton.d.ts +6 -0
  296. package/dist/table/components/TableToolbarButton.js +9 -0
  297. package/dist/table/components/TableView.d.ts +12 -0
  298. package/dist/table/components/TableView.js +21 -0
  299. package/dist/table/defaultRowActions.d.ts +21 -0
  300. package/dist/table/defaultRowActions.js +37 -0
  301. package/dist/table/hooks/useColumnVisibility.d.ts +13 -0
  302. package/dist/table/hooks/useColumnVisibility.js +59 -0
  303. package/dist/table/hooks/useEditableRows.d.ts +22 -0
  304. package/dist/table/hooks/useEditableRows.js +63 -0
  305. package/dist/table/hooks/useTableActions.d.ts +54 -0
  306. package/dist/table/hooks/useTableActions.js +313 -0
  307. package/dist/table/hooks/useTableData.d.ts +28 -0
  308. package/dist/table/hooks/useTableData.js +63 -0
  309. package/dist/table/hooks/useTableLayout.d.ts +12 -0
  310. package/dist/table/hooks/useTableLayout.js +31 -0
  311. package/dist/table/hooks/useTableQuery.d.ts +29 -0
  312. package/dist/table/hooks/useTableQuery.js +135 -0
  313. package/dist/types/index.d.ts +224 -0
  314. package/dist/types/index.js +6 -0
  315. package/dist/utils/classNames.d.ts +7 -0
  316. package/dist/utils/classNames.js +9 -0
  317. package/dist/utils/columnMediaDimensions.d.ts +13 -0
  318. package/dist/utils/columnMediaDimensions.js +29 -0
  319. package/dist/utils/columnVisibilityStorage.d.ts +22 -0
  320. package/dist/utils/columnVisibilityStorage.js +56 -0
  321. package/dist/utils/fieldErrors.d.ts +13 -0
  322. package/dist/utils/fieldErrors.js +25 -0
  323. package/dist/utils/formatValue.d.ts +28 -0
  324. package/dist/utils/formatValue.js +109 -0
  325. package/dist/utils/layoutStorage.d.ts +23 -0
  326. package/dist/utils/layoutStorage.js +53 -0
  327. package/dist/utils/redirectHandler.d.ts +7 -0
  328. package/dist/utils/redirectHandler.js +25 -0
  329. package/dist/utils/tableFormatters.d.ts +14 -0
  330. package/dist/utils/tableFormatters.js +93 -0
  331. package/dist/utils/widgetVisibilityStorage.d.ts +11 -0
  332. package/dist/utils/widgetVisibilityStorage.js +39 -0
  333. package/package.json +101 -0
  334. package/src/FieldRenderer.test.tsx +44 -0
  335. package/src/FieldRenderer.tsx +104 -0
  336. package/src/FormRenderer.containers.test.tsx +121 -0
  337. package/src/FormRenderer.test.tsx +174 -0
  338. package/src/FormRenderer.tsx +140 -0
  339. package/src/TableRenderer.tsx +2 -0
  340. package/src/api/actionsApi.ts +76 -0
  341. package/src/api/authenticatedFetch.ts +40 -0
  342. package/src/api/exportApi.ts +66 -0
  343. package/src/api/http.test.ts +58 -0
  344. package/src/api/http.ts +68 -0
  345. package/src/api/resourceApi.ts +88 -0
  346. package/src/api/tableApi.test.ts +108 -0
  347. package/src/api/tableApi.ts +107 -0
  348. package/src/api/urls.ts +50 -0
  349. package/src/app.test.tsx +67 -0
  350. package/src/app.tsx +181 -0
  351. package/src/auth/AuthContext.tsx +188 -0
  352. package/src/auth/LoginPage.tsx +380 -0
  353. package/src/auth/ProtectedRoute.tsx +39 -0
  354. package/src/auth/authApiClient.ts +109 -0
  355. package/src/auth/authFlow.test.tsx +168 -0
  356. package/src/auth/types.ts +104 -0
  357. package/src/components/ActionFormModal.tsx +45 -0
  358. package/src/components/AdminPanel.tsx +368 -0
  359. package/src/components/Checkbox.tsx +59 -0
  360. package/src/components/CheckboxField.tsx +88 -0
  361. package/src/components/ColorPickerField.tsx +93 -0
  362. package/src/components/DateTimePickerField.tsx +112 -0
  363. package/src/components/FileUploadField.tsx +841 -0
  364. package/src/components/GlobalSearch.tsx +436 -0
  365. package/src/components/GroupField.tsx +85 -0
  366. package/src/components/HiddenField.tsx +14 -0
  367. package/src/components/ModalBreadcrumb.tsx +74 -0
  368. package/src/components/ModalDrawer.tsx +137 -0
  369. package/src/components/RadioField.tsx +80 -0
  370. package/src/components/RepeaterField.tsx +546 -0
  371. package/src/components/ResourceModalRenderer.tsx +144 -0
  372. package/src/components/RichEditorField.tsx +942 -0
  373. package/src/components/SectionField.tsx +242 -0
  374. package/src/components/SelectField.tsx +843 -0
  375. package/src/components/TabsField.test.tsx +151 -0
  376. package/src/components/TabsField.tsx +386 -0
  377. package/src/components/TagsInputField.tsx +411 -0
  378. package/src/components/TextInputField.tsx +91 -0
  379. package/src/components/TextareaField.tsx +110 -0
  380. package/src/components/ToggleField.tsx +126 -0
  381. package/src/components/ViewModal.tsx +353 -0
  382. package/src/components/blocks/BlockRenderer.tsx +56 -0
  383. package/src/components/blocks/FormBlockRenderer.tsx +160 -0
  384. package/src/components/blocks/TableBlockRenderer.tsx +33 -0
  385. package/src/components/blocks/TabsBlockRenderer.tsx +49 -0
  386. package/src/components/blocks/WidgetBlockRenderer.tsx +19 -0
  387. package/src/components/columns/CheckboxColumnComponent.tsx +38 -0
  388. package/src/components/columns/ColorColumnComponent.tsx +23 -0
  389. package/src/components/columns/CustomColumn.test.tsx +55 -0
  390. package/src/components/columns/DeeplinkWrapper.tsx +103 -0
  391. package/src/components/columns/IconColumnComponent.tsx +55 -0
  392. package/src/components/columns/ImageColumnComponent.tsx +220 -0
  393. package/src/components/columns/MediaColumnComponent.tsx +294 -0
  394. package/src/components/columns/SelectColumnComponent.tsx +49 -0
  395. package/src/components/columns/TagsColumnComponent.tsx +46 -0
  396. package/src/components/columns/TextColumnComponent.tsx +191 -0
  397. package/src/components/columns/TextInputColumnComponent.tsx +35 -0
  398. package/src/components/columns/ToggleColumnComponent.tsx +56 -0
  399. package/src/components/columns/VideoColumnComponent.tsx +236 -0
  400. package/src/components/columns/ViewColumnComponent.tsx +9 -0
  401. package/src/components/errors/ErrorBoundary.tsx +58 -0
  402. package/src/components/filters/CustomFilterComponent.tsx +130 -0
  403. package/src/components/filters/DateFilterComponent.tsx +272 -0
  404. package/src/components/filters/QueryBuilderFilterComponent.tsx +502 -0
  405. package/src/components/layout/Header.tsx +212 -0
  406. package/src/components/layout/PanelBrandMark.tsx +61 -0
  407. package/src/components/layout/Sidebar.tsx +283 -0
  408. package/src/components/modals/RelationCreateModal.tsx +107 -0
  409. package/src/components/modals/ResourceFormModal.test.tsx +119 -0
  410. package/src/components/modals/ResourceFormModal.tsx +128 -0
  411. package/src/components/modals/useResourceForm.ts +207 -0
  412. package/src/components/modals/view/RecordActions.tsx +69 -0
  413. package/src/components/modals/view/RecordDetails.tsx +60 -0
  414. package/src/components/modals/view/RelationPanel.tsx +76 -0
  415. package/src/components/modals/view/RelationTabs.tsx +145 -0
  416. package/src/components/modals/view/useRecordView.ts +134 -0
  417. package/src/components/pages/PageRenderer.tsx +173 -0
  418. package/src/components/table/ColumnTogglePopup.tsx +85 -0
  419. package/src/components/table/GridCard.tsx +155 -0
  420. package/src/components/table/GridView.tsx +138 -0
  421. package/src/components/table/LayoutToggle.tsx +24 -0
  422. package/src/components/table/TableActionsDropdown.tsx +114 -0
  423. package/src/components/table/TableBulkActions.tsx +65 -0
  424. package/src/components/table/TableHeader.tsx +96 -0
  425. package/src/components/table/TablePagination.tsx +169 -0
  426. package/src/components/table/TableRow.tsx +155 -0
  427. package/src/components/table/TableSearchBar.tsx +66 -0
  428. package/src/components/table/TableTabs.tsx +49 -0
  429. package/src/components/ui/Badge.tsx +30 -0
  430. package/src/components/ui/Button.test.tsx +78 -0
  431. package/src/components/ui/Button.tsx +102 -0
  432. package/src/components/ui/Card.tsx +23 -0
  433. package/src/components/ui/ConfirmDialog.tsx +112 -0
  434. package/src/components/ui/EmptyState.tsx +24 -0
  435. package/src/components/ui/ErrorAlert.tsx +37 -0
  436. package/src/components/ui/Input.tsx +48 -0
  437. package/src/components/ui/Label.tsx +15 -0
  438. package/src/components/ui/PillButton.tsx +72 -0
  439. package/src/components/ui/Select.tsx +33 -0
  440. package/src/components/ui/Spinner.tsx +39 -0
  441. package/src/components/ui/Toast.tsx +105 -0
  442. package/src/components/ui/index.ts +24 -0
  443. package/src/components/utils/HintDisplay.tsx +26 -0
  444. package/src/components/utils/Icon.tsx +36 -0
  445. package/src/components/utils/MediaPreviewModal.tsx +114 -0
  446. package/src/components/utils/ViewFieldWrapper.tsx +23 -0
  447. package/src/components/utils/layoutHelpers.ts +267 -0
  448. package/src/components/widgets/ChartWidget.tsx +247 -0
  449. package/src/components/widgets/StatsWidget.tsx +72 -0
  450. package/src/components/widgets/WidgetRenderer.tsx +108 -0
  451. package/src/components/widgets/WidgetShell.tsx +37 -0
  452. package/src/contexts/AuthChallengeRegistryContext.tsx +29 -0
  453. package/src/contexts/BlockRegistryContext.tsx +28 -0
  454. package/src/contexts/ColumnRegistryContext.tsx +38 -0
  455. package/src/contexts/FieldRegistryContext.tsx +56 -0
  456. package/src/contexts/PanelMetadataContext.tsx +60 -0
  457. package/src/contexts/PanelProviders.tsx +85 -0
  458. package/src/contexts/ResourceModalContext.tsx +137 -0
  459. package/src/contexts/SlotRegistryContext.tsx +35 -0
  460. package/src/contexts/TableRefreshContext.tsx +44 -0
  461. package/src/contexts/WidgetRegistryContext.tsx +34 -0
  462. package/src/contexts/createRegistryContext.tsx +29 -0
  463. package/src/hooks/useAfterStateUpdated.ts +70 -0
  464. package/src/hooks/useValidation.test.ts +59 -0
  465. package/src/hooks/useValidation.ts +95 -0
  466. package/src/i18n/I18nProvider.tsx +128 -0
  467. package/src/i18n/LocaleSwitcher.tsx +50 -0
  468. package/src/i18n/activeLocale.ts +39 -0
  469. package/src/i18n/buildClientI18n.ts +101 -0
  470. package/src/i18n/i18n.test.tsx +140 -0
  471. package/src/i18n/index.ts +12 -0
  472. package/src/i18n/locales/core/en.ts +274 -0
  473. package/src/i18n/locales/core/index.ts +5 -0
  474. package/src/i18n/locales/core/sq.ts +275 -0
  475. package/src/i18n/useFormatter.ts +42 -0
  476. package/src/i18n/useLocale.ts +16 -0
  477. package/src/i18n/useTranslation.ts +17 -0
  478. package/src/index.ts +244 -0
  479. package/src/pages/ResourceListPage.tsx +205 -0
  480. package/src/plugin.ts +110 -0
  481. package/src/runtime/conditions.test.ts +99 -0
  482. package/src/runtime/conditions.ts +148 -0
  483. package/src/runtime/formTraversal.ts +41 -0
  484. package/src/runtime/serializedFunctions.test.ts +59 -0
  485. package/src/runtime/serializedFunctions.ts +284 -0
  486. package/src/slots/Slot.test.tsx +89 -0
  487. package/src/slots/Slot.tsx +47 -0
  488. package/src/slots/SlotCluster.test.tsx +95 -0
  489. package/src/slots/SlotCluster.tsx +107 -0
  490. package/src/slots/index.ts +15 -0
  491. package/src/slots/mergeSlots.test.ts +71 -0
  492. package/src/slots/mergeSlots.ts +40 -0
  493. package/src/slots/slotNames.test.ts +21 -0
  494. package/src/slots/types.ts +119 -0
  495. package/src/styles.css +437 -0
  496. package/src/table/TableContext.tsx +41 -0
  497. package/src/table/TableRenderer.test.tsx +197 -0
  498. package/src/table/TableRenderer.tsx +390 -0
  499. package/src/table/components/FiltersPanel.tsx +193 -0
  500. package/src/table/components/TableToolbar.tsx +153 -0
  501. package/src/table/components/TableToolbarButton.tsx +14 -0
  502. package/src/table/components/TableView.tsx +106 -0
  503. package/src/table/defaultRowActions.ts +43 -0
  504. package/src/table/hooks/useColumnVisibility.test.ts +51 -0
  505. package/src/table/hooks/useColumnVisibility.ts +71 -0
  506. package/src/table/hooks/useEditableRows.test.ts +69 -0
  507. package/src/table/hooks/useEditableRows.ts +89 -0
  508. package/src/table/hooks/useTableActions.ts +393 -0
  509. package/src/table/hooks/useTableData.ts +89 -0
  510. package/src/table/hooks/useTableLayout.ts +45 -0
  511. package/src/table/hooks/useTableQuery.test.ts +116 -0
  512. package/src/table/hooks/useTableQuery.ts +172 -0
  513. package/src/test/mockFetch.ts +67 -0
  514. package/src/test/setup.ts +25 -0
  515. package/src/types/index.ts +228 -0
  516. package/src/utils/classNames.ts +10 -0
  517. package/src/utils/columnMediaDimensions.ts +45 -0
  518. package/src/utils/columnVisibilityStorage.ts +55 -0
  519. package/src/utils/fieldErrors.test.ts +35 -0
  520. package/src/utils/fieldErrors.ts +27 -0
  521. package/src/utils/formatValue.test.tsx +65 -0
  522. package/src/utils/formatValue.tsx +117 -0
  523. package/src/utils/layoutStorage.ts +52 -0
  524. package/src/utils/redirectHandler.ts +29 -0
  525. package/src/utils/tableFormatters.test.ts +54 -0
  526. package/src/utils/tableFormatters.ts +104 -0
  527. package/src/utils/widgetVisibilityStorage.ts +38 -0
  528. package/tailwind.config.js +9 -0
  529. package/vite.config.ts +17 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Layout storage utilities for managing table/grid layout preferences in localStorage
3
+ */
4
+ export declare const layoutStorage: {
5
+ /**
6
+ * Get layout preference for a resource
7
+ * @param resourceKey Unique identifier for the resource (e.g., 'users', 'posts')
8
+ * @param defaultLayout Default layout to return if no preference is stored
9
+ * @returns 'table' or 'grid'
10
+ */
11
+ getLayout(resourceKey: string, defaultLayout?: "table" | "grid"): "table" | "grid";
12
+ /**
13
+ * Set layout preference for a resource
14
+ * @param resourceKey Unique identifier for the resource
15
+ * @param layout Layout mode - 'table' or 'grid'
16
+ */
17
+ setLayout(resourceKey: string, layout: "table" | "grid"): void;
18
+ /**
19
+ * Clear stored layout preference for a resource
20
+ * @param resourceKey Unique identifier for the resource
21
+ */
22
+ clearLayout(resourceKey: string): void;
23
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Layout storage utilities for managing table/grid layout preferences in localStorage
3
+ */
4
+ export const layoutStorage = {
5
+ /**
6
+ * Get layout preference for a resource
7
+ * @param resourceKey Unique identifier for the resource (e.g., 'users', 'posts')
8
+ * @param defaultLayout Default layout to return if no preference is stored
9
+ * @returns 'table' or 'grid'
10
+ */
11
+ getLayout(resourceKey, defaultLayout = 'table') {
12
+ try {
13
+ const key = `kratosjs_layout_${resourceKey}`;
14
+ const stored = localStorage.getItem(key);
15
+ if (stored === 'table' || stored === 'grid') {
16
+ return stored;
17
+ }
18
+ return defaultLayout;
19
+ }
20
+ catch (error) {
21
+ console.warn('Failed to read layout preference from localStorage:', error);
22
+ return defaultLayout;
23
+ }
24
+ },
25
+ /**
26
+ * Set layout preference for a resource
27
+ * @param resourceKey Unique identifier for the resource
28
+ * @param layout Layout mode - 'table' or 'grid'
29
+ */
30
+ setLayout(resourceKey, layout) {
31
+ try {
32
+ const key = `kratosjs_layout_${resourceKey}`;
33
+ localStorage.setItem(key, layout);
34
+ }
35
+ catch (error) {
36
+ // Handle quota exceeded or other localStorage errors
37
+ if (error instanceof Error && error.name === 'QuotaExceededError') {
38
+ console.warn('localStorage quota exceeded, layout preference not saved');
39
+ }
40
+ else {
41
+ console.warn('Failed to save layout preference to localStorage:', error);
42
+ }
43
+ }
44
+ },
45
+ /**
46
+ * Clear stored layout preference for a resource
47
+ * @param resourceKey Unique identifier for the resource
48
+ */
49
+ clearLayout(resourceKey) {
50
+ const key = `kratosjs_layout_${resourceKey}`;
51
+ localStorage.removeItem(key);
52
+ },
53
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Handle redirect from API response
3
+ * @param responseData - Response data from API
4
+ * @param navigate - React Router navigate function
5
+ * @returns true if redirect was handled, false otherwise
6
+ */
7
+ export declare function handleRedirect(responseData: any, navigate: (path: string) => void): boolean;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Handle redirect from API response
3
+ * @param responseData - Response data from API
4
+ * @param navigate - React Router navigate function
5
+ * @returns true if redirect was handled, false otherwise
6
+ */
7
+ export function handleRedirect(responseData, navigate) {
8
+ if (responseData?.redirect) {
9
+ const redirectPath = responseData.redirect;
10
+ // Handle relative paths (client-side navigation)
11
+ if (redirectPath.startsWith('/')) {
12
+ navigate(redirectPath);
13
+ return true;
14
+ }
15
+ // Handle absolute URLs (external redirects)
16
+ if (redirectPath.startsWith('http://') || redirectPath.startsWith('https://')) {
17
+ window.location.href = redirectPath;
18
+ return true;
19
+ }
20
+ // Invalid redirect path
21
+ console.warn('Invalid redirect path:', redirectPath);
22
+ return false;
23
+ }
24
+ return false;
25
+ }
@@ -0,0 +1,14 @@
1
+ import { SerializedColumn } from '@maxal_studio/kratosjs';
2
+ /**
3
+ * Format a value based on column configuration
4
+ * @param value - The cell value
5
+ * @param column - The column configuration
6
+ * @param row - The entire row data (optional, used by formatStateUsing function)
7
+ */
8
+ export declare function formatValue(value: any, column: SerializedColumn, row?: Record<string, any>): any;
9
+ /**
10
+ * Strip HTML tags from a string
11
+ * @param html - The HTML string to strip
12
+ * @returns Plain text with HTML tags removed
13
+ */
14
+ export declare function stripHtml(html: string): string;
@@ -0,0 +1,93 @@
1
+ import { getActiveLocale } from '../i18n/activeLocale';
2
+ /**
3
+ * Format a value based on column configuration
4
+ * @param value - The cell value
5
+ * @param column - The column configuration
6
+ * @param row - The entire row data (optional, used by formatStateUsing function)
7
+ */
8
+ export function formatValue(value, column, row) {
9
+ // Only apply placeholder for null/undefined when there's no formatter
10
+ if (value === null || value === undefined) {
11
+ return null;
12
+ }
13
+ // Date formatting
14
+ if (column.dateFormat) {
15
+ const date = new Date(value);
16
+ if (isNaN(date.getTime()))
17
+ return value;
18
+ const locale = getActiveLocale();
19
+ switch (column.dateFormat) {
20
+ case 'date':
21
+ return date.toLocaleDateString(locale);
22
+ case 'dateTime':
23
+ return date.toLocaleString(locale);
24
+ case 'time':
25
+ return date.toLocaleTimeString(locale);
26
+ case 'since':
27
+ case 'until':
28
+ return getRelativeTime(date);
29
+ default:
30
+ return date.toLocaleDateString(locale);
31
+ }
32
+ }
33
+ // Money formatting
34
+ if (column.moneyFormat) {
35
+ const numValue = typeof value === 'number' ? value : parseFloat(value);
36
+ if (isNaN(numValue))
37
+ return value;
38
+ return new Intl.NumberFormat(getActiveLocale(), {
39
+ style: 'currency',
40
+ currency: column.moneyFormat || 'USD',
41
+ }).format(numValue);
42
+ }
43
+ // Strip HTML by default; only skip when stripHtml is explicitly false.
44
+ // column.stripHtml is true (default), false (explicit), or undefined (treated as true).
45
+ const shouldStripHtml = column.stripHtml !== false;
46
+ if (shouldStripHtml) {
47
+ if (typeof value === 'string') {
48
+ return stripHtml(value);
49
+ }
50
+ // Handle arrays - strip HTML from each string item
51
+ if (Array.isArray(value)) {
52
+ return value.map(item => (typeof item === 'string' ? stripHtml(item) : item));
53
+ }
54
+ }
55
+ return value;
56
+ }
57
+ /**
58
+ * Strip HTML tags from a string
59
+ * @param html - The HTML string to strip
60
+ * @returns Plain text with HTML tags removed
61
+ */
62
+ export function stripHtml(html) {
63
+ if (typeof html !== 'string') {
64
+ return html;
65
+ }
66
+ // Create a temporary DOM element to parse HTML
67
+ const tmp = document.createElement('div');
68
+ tmp.innerHTML = html;
69
+ return tmp.textContent || tmp.innerText || '';
70
+ }
71
+ /**
72
+ * Locale-aware relative time (e.g. "2 hours ago", "in 3 days"), using the active
73
+ * locale via `Intl.RelativeTimeFormat`. The true signed delta (date − now) drives
74
+ * direction, so past dates read "ago" and future dates read "in …".
75
+ */
76
+ function getRelativeTime(date) {
77
+ const deltaSec = Math.round((date.getTime() - Date.now()) / 1000);
78
+ const abs = Math.abs(deltaSec);
79
+ const rtf = new Intl.RelativeTimeFormat(getActiveLocale(), { numeric: 'auto' });
80
+ const units = [
81
+ ['year', 31536000],
82
+ ['month', 2592000],
83
+ ['week', 604800],
84
+ ['day', 86400],
85
+ ['hour', 3600],
86
+ ['minute', 60],
87
+ ];
88
+ for (const [unit, secs] of units) {
89
+ if (abs >= secs)
90
+ return rtf.format(Math.round(deltaSec / secs), unit);
91
+ }
92
+ return rtf.format(0, 'second');
93
+ }
@@ -0,0 +1,11 @@
1
+ /** Default when no preference stored: desktop expanded, mobile collapsed */
2
+ export declare function getDefaultWidgetsExpanded(): boolean;
3
+ /**
4
+ * Storage for widgets expanded/collapsed state.
5
+ * When no value is stored, mobile should be collapsed and desktop expanded (handled in component via media query).
6
+ */
7
+ export declare const widgetVisibilityStorage: {
8
+ get(): boolean | null;
9
+ set(expanded: boolean): void;
10
+ clear(): void;
11
+ };
@@ -0,0 +1,39 @@
1
+ const WIDGET_VISIBILITY_KEY = 'kratosjs_widgets_expanded';
2
+ /** Default when no preference stored: desktop expanded, mobile collapsed */
3
+ export function getDefaultWidgetsExpanded() {
4
+ if (typeof window === 'undefined')
5
+ return true;
6
+ return window.matchMedia('(min-width: 768px)').matches;
7
+ }
8
+ /**
9
+ * Storage for widgets expanded/collapsed state.
10
+ * When no value is stored, mobile should be collapsed and desktop expanded (handled in component via media query).
11
+ */
12
+ export const widgetVisibilityStorage = {
13
+ get() {
14
+ try {
15
+ const stored = localStorage.getItem(WIDGET_VISIBILITY_KEY);
16
+ if (stored === 'true')
17
+ return true;
18
+ if (stored === 'false')
19
+ return false;
20
+ return null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ },
26
+ set(expanded) {
27
+ try {
28
+ localStorage.setItem(WIDGET_VISIBILITY_KEY, String(expanded));
29
+ }
30
+ catch (error) {
31
+ if (error instanceof Error && error.name === 'QuotaExceededError') {
32
+ console.warn('localStorage quota exceeded, widgets visibility not saved');
33
+ }
34
+ }
35
+ },
36
+ clear() {
37
+ localStorage.removeItem(WIDGET_VISIBILITY_KEY);
38
+ },
39
+ };
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@maxal_studio/kratosjs-react",
3
+ "version": "1.0.0",
4
+ "description": "React components for KratosJs form builder",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "sideEffects": [
10
+ "**/*.css"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./app": {
18
+ "types": "./dist/app.d.ts",
19
+ "import": "./dist/app.js"
20
+ },
21
+ "./plugin": {
22
+ "types": "./dist/plugin.d.ts",
23
+ "import": "./dist/plugin.js"
24
+ },
25
+ "./styles.css": "./dist/styles.css",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "src",
31
+ "vite.config.ts",
32
+ "tailwind.config.js",
33
+ "README.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "npm run clean && tsc && vite build",
37
+ "clean": "rm -rf dist",
38
+ "dev": "tsc --watch",
39
+ "lint": "eslint ./src",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest"
42
+ },
43
+ "keywords": [
44
+ "react",
45
+ "forms",
46
+ "form-builder",
47
+ "kratosjs",
48
+ "tailwind"
49
+ ],
50
+ "author": "MaxAl",
51
+ "email": "info@max.al",
52
+ "website": "https://max.al",
53
+ "license": "ISC",
54
+ "peerDependencies": {
55
+ "@maxal_studio/kratosjs": "^1.0.0",
56
+ "react": "^19.0.0",
57
+ "react-dom": "^19.0.0",
58
+ "react-hook-form": "^7.0.0"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@maxal_studio/kratosjs": {
62
+ "optional": true
63
+ }
64
+ },
65
+ "dependencies": {
66
+ "@tiptap/extension-color": "^3.26.1",
67
+ "@tiptap/extension-highlight": "^3.26.1",
68
+ "@tiptap/extension-image": "^3.26.1",
69
+ "@tiptap/extension-link": "^3.26.1",
70
+ "@tiptap/extension-placeholder": "^3.26.1",
71
+ "@tiptap/extension-subscript": "^3.26.1",
72
+ "@tiptap/extension-superscript": "^3.26.1",
73
+ "@tiptap/extension-text-align": "^3.26.1",
74
+ "@tiptap/extension-text-style": "^3.26.1",
75
+ "@tiptap/extension-underline": "^3.26.1",
76
+ "@tiptap/react": "^3.26.1",
77
+ "@tiptap/starter-kit": "^3.26.1",
78
+ "chart.js": "^4.5.1",
79
+ "clsx": "^2.1.1",
80
+ "lucide-react": "^1.18.0",
81
+ "react-chartjs-2": "^5.3.1",
82
+ "react-dropzone": "^15.0.0",
83
+ "react-router-dom": "^7.17.0"
84
+ },
85
+ "devDependencies": {
86
+ "@maxal_studio/kratosjs": "file:../..",
87
+ "@tailwindcss/vite": "^4.3.1",
88
+ "@testing-library/jest-dom": "^6.9.1",
89
+ "@testing-library/react": "^16.3.2",
90
+ "@testing-library/user-event": "^14.6.1",
91
+ "react": "^19.2.7",
92
+ "react-dom": "^19.2.7",
93
+ "react-hook-form": "^7.79.0",
94
+ "eslint": "^9.39.4",
95
+ "jsdom": "^29.1.1",
96
+ "typescript-eslint": "^8.61.0",
97
+ "typescript": "^5.9.3",
98
+ "vite": "^7.3.5",
99
+ "vitest": "^4.1.8"
100
+ }
101
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { FormRenderer } from './FormRenderer';
4
+ import { FieldRegistryProvider } from './contexts/FieldRegistryContext';
5
+ import type { FieldProps } from './types';
6
+
7
+ // A custom field component an app registers without a plugin. In production this
8
+ // is wired via mountAdminPanel({ fields: { 'star-rating': StarRatingField } });
9
+ // here we feed it straight into the registry provider, which is the same path.
10
+ function StarRatingField(props: FieldProps) {
11
+ return <div data-testid="star-rating">{props.label} stars</div>;
12
+ }
13
+
14
+ describe('FieldRenderer custom component registration', () => {
15
+ it('renders a custom field registered via the field registry', () => {
16
+ render(
17
+ <FieldRegistryProvider customFields={{ 'star-rating': StarRatingField }}>
18
+ <FormRenderer
19
+ schema={{
20
+ type: 'form',
21
+ components: [{ type: 'star-rating', name: 'rating', label: 'Rating' }],
22
+ }}
23
+ onSubmit={vi.fn()}
24
+ />
25
+ </FieldRegistryProvider>,
26
+ );
27
+
28
+ expect(screen.getByTestId('star-rating')).toBeInTheDocument();
29
+ expect(screen.getByText(/rating stars/i)).toBeInTheDocument();
30
+ });
31
+
32
+ it('still warns for a field type that is not registered', () => {
33
+ render(
34
+ <FieldRegistryProvider customFields={{ 'star-rating': StarRatingField }}>
35
+ <FormRenderer
36
+ schema={{ type: 'form', components: [{ type: 'mystery', name: 'x' }] }}
37
+ onSubmit={vi.fn()}
38
+ />
39
+ </FieldRegistryProvider>,
40
+ );
41
+
42
+ expect(screen.getByText(/unknown field type "mystery"/i)).toBeInTheDocument();
43
+ });
44
+ });
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import { useWatch, useFormContext } from 'react-hook-form';
3
+ import { FieldRendererProps } from './types';
4
+ import { useFieldRegistry } from './contexts/FieldRegistryContext';
5
+ import { evaluateCondition } from './runtime/conditions';
6
+ import { useAfterStateUpdated } from './hooks/useAfterStateUpdated';
7
+ import { getColumnClasses } from './components/utils/layoutHelpers';
8
+ import { translate } from './i18n/activeLocale';
9
+
10
+ /**
11
+ * FieldRenderer component
12
+ * Dynamically renders the correct field component based on type
13
+ */
14
+ export function FieldRenderer({
15
+ field,
16
+ apiBaseUrl,
17
+ resource,
18
+ operation,
19
+ mode = 'edit',
20
+ value,
21
+ }: FieldRendererProps & {
22
+ apiBaseUrl?: string;
23
+ resource?: string;
24
+ operation?: 'create' | 'edit' | 'view';
25
+ mode?: 'edit' | 'view';
26
+ value?: any;
27
+ }) {
28
+ const registry = useFieldRegistry();
29
+ const FieldComponent = registry[field.type];
30
+
31
+ // In view mode, we don't need form context
32
+ let formState: Record<string, any> = {};
33
+ if (mode === 'edit') {
34
+ const { control } = useFormContext();
35
+ // Watch form state for conditional visibility/disabled evaluation
36
+ formState = useWatch({ control }) || {};
37
+ // Execute afterStateUpdated callback when field value changes (if provided)
38
+ useAfterStateUpdated(field.name, (field as any).afterStateUpdatedFn);
39
+ } else {
40
+ // In view mode, create a simple form state from the value prop for conditional evaluation
41
+ // This allows conditional visibility to work based on field values
42
+ if (field.name && value !== undefined) {
43
+ formState = { [field.name]: value };
44
+ }
45
+ }
46
+
47
+ // Unknown field type
48
+ if (!FieldComponent) {
49
+ console.warn(`Unknown field type: ${field.type}`);
50
+ return (
51
+ <div className="mb-4 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
52
+ <p className="text-sm text-yellow-800">
53
+ <strong>{translate('core:common.warning')}:</strong>{' '}
54
+ {translate('core:state.unknown_field', { type: field.type, name: field.name })}
55
+ </p>
56
+ </div>
57
+ );
58
+ }
59
+
60
+ // Evaluate conditional hidden state — hiddenWhen is the structured-AST form
61
+ // (no code execution), hiddenFn the serialized-function form.
62
+ const isHidden = evaluateCondition(
63
+ (field as any).hiddenWhen ?? (field as any).hiddenFn ?? field.hidden,
64
+ formState,
65
+ operation,
66
+ );
67
+
68
+ // Evaluate conditional disabled state
69
+ const isDisabled = evaluateCondition(
70
+ (field as any).disabledWhen ?? (field as any).disabledFn ?? field.disabled,
71
+ formState,
72
+ operation,
73
+ );
74
+
75
+ // Hidden field
76
+ if (isHidden) {
77
+ return null;
78
+ }
79
+
80
+ // Update field props with evaluated disabled state, API base URL, resource, mode, operation, and value
81
+ const fieldWithEvaluatedProps = {
82
+ ...field,
83
+ disabled: isDisabled,
84
+ apiBaseUrl,
85
+ resource,
86
+ mode,
87
+ operation,
88
+ value: value !== undefined ? value : mode === 'view' ? undefined : undefined,
89
+ };
90
+
91
+ // Get column layout classes using shared helper
92
+ const columnClasses = getColumnClasses(fieldWithEvaluatedProps.columnSpan, fieldWithEvaluatedProps.columnStart);
93
+
94
+ // Render the field component
95
+ if (columnClasses) {
96
+ return (
97
+ <div className={columnClasses}>
98
+ <FieldComponent {...fieldWithEvaluatedProps} />
99
+ </div>
100
+ );
101
+ }
102
+
103
+ return <FieldComponent {...fieldWithEvaluatedProps} />;
104
+ }
@@ -0,0 +1,121 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import React from 'react';
3
+ import { render, screen, waitFor } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { FormBuilder, TextInput, Group, Tabs, Repeater } from '@maxal_studio/kratosjs';
6
+ import { FormRenderer } from './FormRenderer';
7
+ import { FieldRegistryProvider } from './contexts/FieldRegistryContext';
8
+
9
+ // Front-end counterpart to the backend container validation tests: rules on
10
+ // fields nested in containers (Group/Tabs and Repeater rows) must block submit,
11
+ // driven by the same shared engine the server uses.
12
+
13
+ function renderForm(schema: any, defaultValues?: Record<string, any>) {
14
+ const onSubmit = vi.fn();
15
+ render(
16
+ <FieldRegistryProvider>
17
+ <FormRenderer schema={schema} onSubmit={onSubmit} defaultValues={defaultValues} />
18
+ </FieldRegistryProvider>,
19
+ );
20
+ return onSubmit;
21
+ }
22
+
23
+ const submit = () => userEvent.setup().click(screen.getByRole('button', { name: /submit/i }));
24
+
25
+ describe('FormRenderer validation in layout containers', () => {
26
+ it('Group: a required nested field blocks submit and shows an error', async () => {
27
+ const schema = FormBuilder.make()
28
+ .schema([Group.make('g').schema([TextInput.make('email').label('Email').required().email()])])
29
+ .toJSON();
30
+ const onSubmit = renderForm(schema);
31
+
32
+ await submit();
33
+
34
+ await waitFor(() => expect(screen.getByText(/this field is required/i)).toBeInTheDocument());
35
+ expect(onSubmit).not.toHaveBeenCalled();
36
+ });
37
+
38
+ it('Group: an invalid value on a nested field blocks submit', async () => {
39
+ const schema = FormBuilder.make()
40
+ .schema([Group.make('g').schema([TextInput.make('email').label('Email').required().email()])])
41
+ .toJSON();
42
+ const onSubmit = renderForm(schema);
43
+
44
+ const user = userEvent.setup();
45
+ await user.type(screen.getByLabelText(/email/i), 'not-an-email');
46
+ await user.click(screen.getByRole('button', { name: /submit/i }));
47
+
48
+ await waitFor(() => expect(screen.getByText(/valid email/i)).toBeInTheDocument());
49
+ expect(onSubmit).not.toHaveBeenCalled();
50
+ });
51
+
52
+ it('Tabs: a rule on a field inside the active tab is enforced', async () => {
53
+ const schema = FormBuilder.make()
54
+ .schema([
55
+ Tabs.make('t').tabs([
56
+ { label: 'A', schema: [TextInput.make('title').label('Title').required().min(3)] },
57
+ ]),
58
+ ])
59
+ .toJSON();
60
+ const onSubmit = renderForm(schema);
61
+
62
+ const user = userEvent.setup();
63
+ await user.type(screen.getByLabelText(/title/i), 'ab'); // below min:3
64
+ await user.click(screen.getByRole('button', { name: /submit/i }));
65
+
66
+ await waitFor(() => expect(screen.getByText(/at least 3 characters/i)).toBeInTheDocument());
67
+ expect(onSubmit).not.toHaveBeenCalled();
68
+ });
69
+
70
+ it('Group: a fully valid nested field submits', async () => {
71
+ const schema = FormBuilder.make()
72
+ .schema([Group.make('g').schema([TextInput.make('email').label('Email').required().email()])])
73
+ .toJSON();
74
+ const onSubmit = renderForm(schema);
75
+
76
+ const user = userEvent.setup();
77
+ await user.type(screen.getByLabelText(/email/i), 'a@b.com');
78
+ await user.click(screen.getByRole('button', { name: /submit/i }));
79
+
80
+ await waitFor(() => expect(onSubmit).toHaveBeenCalled());
81
+ expect(onSubmit.mock.calls[0][0]).toMatchObject({ email: 'a@b.com' });
82
+ });
83
+ });
84
+
85
+ describe('FormRenderer validation in a Repeater row', () => {
86
+ const schema = FormBuilder.make()
87
+ .schema([Repeater.make('items').schema([TextInput.make('name').label('Name').required().min(3)])])
88
+ .toJSON();
89
+
90
+ it('an invalid row (empty required) blocks submit and shows the row error', async () => {
91
+ // Seed one empty row so the row's fields render and register.
92
+ const onSubmit = renderForm(schema, { items: [{ name: '' }] });
93
+
94
+ // Row actually rendered (so the assertion is meaningful).
95
+ expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
96
+
97
+ await submit();
98
+
99
+ // The nested-name (items.0.name) error message now resolves and renders.
100
+ await waitFor(() => expect(screen.getByText(/this field is required/i)).toBeInTheDocument());
101
+ expect(onSubmit).not.toHaveBeenCalled();
102
+ });
103
+
104
+ it('a too-short row value (min:3) blocks submit and shows the row error', async () => {
105
+ const onSubmit = renderForm(schema, { items: [{ name: 'ab' }] });
106
+
107
+ await submit();
108
+
109
+ await waitFor(() => expect(screen.getByText(/at least 3 characters/i)).toBeInTheDocument());
110
+ expect(onSubmit).not.toHaveBeenCalled();
111
+ });
112
+
113
+ it('a valid row submits', async () => {
114
+ const onSubmit = renderForm(schema, { items: [{ name: 'abc' }] });
115
+
116
+ await submit();
117
+
118
+ await waitFor(() => expect(onSubmit).toHaveBeenCalled());
119
+ expect(onSubmit.mock.calls[0][0]).toMatchObject({ items: [{ name: 'abc' }] });
120
+ });
121
+ });