@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,33 @@
1
+ import React from 'react';
2
+ import { TableRenderer } from '../../TableRenderer';
3
+ import { cn } from '../../utils/classNames';
4
+
5
+ export interface TableBlockRendererProps {
6
+ block: any;
7
+ apiBaseUrl?: string;
8
+ }
9
+
10
+ export function TableBlockRenderer({ block, apiBaseUrl }: TableBlockRendererProps) {
11
+ const dataUrl = block.dataUrl
12
+ ? apiBaseUrl
13
+ ? `${apiBaseUrl}/${block.dataUrl}`
14
+ : block.dataUrl
15
+ : apiBaseUrl
16
+ ? `${apiBaseUrl}/custom/list`
17
+ : undefined;
18
+
19
+ return (
20
+ <>
21
+ {dataUrl && (
22
+ <TableRenderer
23
+ isResource={false}
24
+ schema={block.table}
25
+ apiUrl={dataUrl}
26
+ apiBaseUrl={apiBaseUrl}
27
+ onCreateClick={undefined}
28
+ canCreate={false}
29
+ />
30
+ )}
31
+ </>
32
+ );
33
+ }
@@ -0,0 +1,49 @@
1
+ import React, { useState } from 'react';
2
+ import { BlockRenderer } from './BlockRenderer';
3
+ import { Icon } from '../utils/Icon';
4
+ import { cn } from '../../utils/classNames';
5
+
6
+ export interface TabsBlockRendererProps {
7
+ block: any;
8
+ blockData?: Record<string, any>;
9
+ apiBaseUrl?: string;
10
+ }
11
+
12
+ export function TabsBlockRenderer({ block, blockData, apiBaseUrl }: TabsBlockRendererProps) {
13
+ const [activeTab, setActiveTab] = useState(block.defaultTab || 0);
14
+
15
+ return (
16
+ <>
17
+ {/* Tab Headers */}
18
+ <div className="flex border-b border-gray-200 dark:border-gray-700 mb-4">
19
+ {block.tabs.map((tab: any, index: number) => (
20
+ <button
21
+ key={index}
22
+ onClick={() => setActiveTab(index)}
23
+ className={cn(
24
+ 'px-4 py-2 font-medium text-sm transition-colors',
25
+ 'border-b-2 -mb-px',
26
+ activeTab === index
27
+ ? 'border-accent text-accent'
28
+ : 'border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200',
29
+ )}>
30
+ {tab.icon && (
31
+ <span className="inline-flex items-center gap-2">
32
+ <Icon name={tab.icon} className="w-4 h-4" />
33
+ {tab.label}
34
+ </span>
35
+ )}
36
+ {!tab.icon && tab.label}
37
+ </button>
38
+ ))}
39
+ </div>
40
+
41
+ {/* Tab Content */}
42
+ <div>
43
+ {block.tabs[activeTab]?.blocks.map((nestedBlock: any, index: number) => (
44
+ <BlockRenderer key={index} block={nestedBlock} blockData={blockData} apiBaseUrl={apiBaseUrl} />
45
+ ))}
46
+ </div>
47
+ </>
48
+ );
49
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { useWidgetRegistry } from '../../contexts/WidgetRegistryContext';
3
+
4
+ export interface WidgetBlockRendererProps {
5
+ block: any;
6
+ data: any;
7
+ }
8
+
9
+ export function WidgetBlockRenderer({ block, data }: WidgetBlockRendererProps) {
10
+ const widgets = useWidgetRegistry();
11
+ const WidgetComponent = widgets[block.widget.type];
12
+
13
+ if (!WidgetComponent) {
14
+ console.warn(`Widget type "${block.widget.type}" not registered`);
15
+ return null;
16
+ }
17
+
18
+ return <WidgetComponent widget={block.widget} data={data} />;
19
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { ColumnProps } from './TextColumnComponent';
3
+
4
+ export interface EditableColumnProps extends ColumnProps {
5
+ onCellChange?: (value: any) => void;
6
+ }
7
+
8
+ export function CheckboxColumnComponent({ column, record, onCellChange }: EditableColumnProps) {
9
+ const [value, setValue] = useState(Boolean(record[column.name]));
10
+ const isDisabled = column.disabled;
11
+
12
+ // Update local value when record changes (e.g., after reset)
13
+ useEffect(() => {
14
+ setValue(Boolean(record[column.name]));
15
+ }, [record[column.name]]);
16
+
17
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
18
+ if (isDisabled) return;
19
+
20
+ const newValue = e.target.checked;
21
+ setValue(newValue);
22
+
23
+ // Notify parent of change
24
+ if (onCellChange) {
25
+ onCellChange(newValue);
26
+ }
27
+ };
28
+
29
+ return (
30
+ <input
31
+ type="checkbox"
32
+ checked={value}
33
+ onChange={handleChange}
34
+ disabled={isDisabled}
35
+ className="w-4 h-4 text-accent bg-gray-100 border-gray-300 rounded focus:ring-ring dark:focus:ring-ring dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 disabled:opacity-50 disabled:cursor-not-allowed"
36
+ />
37
+ );
38
+ }
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { ColumnProps } from './TextColumnComponent';
3
+ import { formatValue } from '../../utils/tableFormatters';
4
+
5
+ export function ColorColumnComponent({ column, record }: ColumnProps) {
6
+ const rawValue = record[column.name];
7
+ // Apply formatter if present
8
+ const value = formatValue(rawValue, column, record);
9
+
10
+ if (!value) {
11
+ return <span className="text-fg-secondary">-</span>;
12
+ }
13
+
14
+ return (
15
+ <div className="flex items-center gap-2">
16
+ <div
17
+ className="w-6 h-6 rounded border border-gray-300 dark:border-gray-600"
18
+ style={{ backgroundColor: value }}
19
+ />
20
+ <span className="text-sm text-fg-secondary font-mono">{value}</span>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { TableRow } from '../table/TableRow';
4
+ import { ColumnRegistryProvider } from '../../contexts/ColumnRegistryContext';
5
+ import type { ColumnProps } from './TextColumnComponent';
6
+
7
+ // A custom column an app registers without a plugin. In production this is wired
8
+ // via mountAdminPanel({ columns: { 'star-rating': StarRatingColumn } }); here we
9
+ // feed it straight into the registry provider, which is the same path.
10
+ function StarRatingColumn({ column, record }: ColumnProps) {
11
+ return <span data-testid="stars">{`${record[column.name]} / 5`}</span>;
12
+ }
13
+
14
+ const noop = () => {};
15
+
16
+ function renderRow(registry: Record<string, any>) {
17
+ const column = { type: 'star-rating', name: 'rating' } as any;
18
+ render(
19
+ <ColumnRegistryProvider registry={registry}>
20
+ <table>
21
+ <tbody>
22
+ <TableRow
23
+ schema={{ columns: [column] } as any}
24
+ row={{ rating: 4 }}
25
+ rowIndex={0}
26
+ totalRows={1}
27
+ rowId={1}
28
+ visibleColumns={[column]}
29
+ isSelected={false}
30
+ hasChanges={false}
31
+ openActionsRowId={null}
32
+ onRowSelect={noop}
33
+ onCellChange={noop}
34
+ onRowAction={noop}
35
+ onSaveRow={noop}
36
+ onResetRow={noop}
37
+ onToggleActions={noop}
38
+ />
39
+ </tbody>
40
+ </table>
41
+ </ColumnRegistryProvider>,
42
+ );
43
+ }
44
+
45
+ describe('TableRow custom column registration', () => {
46
+ it('renders a custom column registered via the column registry', () => {
47
+ renderRow({ 'star-rating': StarRatingColumn });
48
+ expect(screen.getByTestId('stars')).toHaveTextContent('4 / 5');
49
+ });
50
+
51
+ it('falls back to the unknown-column cell when the type is not registered', () => {
52
+ renderRow({});
53
+ expect(screen.getByText(/unknown column type: star-rating/i)).toBeInTheDocument();
54
+ });
55
+ });
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { useNavigate, useLocation } from 'react-router-dom';
3
+ import { SerializedColumn } from '@maxal_studio/kratosjs';
4
+ import { executeSerializedFunction } from '../../runtime/serializedFunctions';
5
+ import { useResourceModal } from '../../contexts/ResourceModalContext';
6
+
7
+ interface DeeplinkWrapperProps {
8
+ column: SerializedColumn;
9
+ record: Record<string, any>;
10
+ value: any;
11
+ children: React.ReactNode;
12
+ className?: string;
13
+ }
14
+
15
+ /**
16
+ * Reusable component that wraps content with deeplink navigation functionality
17
+ * Uses an anchor tag for proper HTML content support and semantic correctness
18
+ */
19
+ export function DeeplinkWrapper({ column, record, value, children, className = '' }: DeeplinkWrapperProps) {
20
+ const navigate = useNavigate();
21
+ const location = useLocation();
22
+ const { openModal } = useResourceModal();
23
+
24
+ // Determine current resource from URL
25
+ const getCurrentResource = () => {
26
+ const pathParts = location.pathname.split('/').filter(Boolean);
27
+ // First part after root is typically the resource slug
28
+ return pathParts[0] || null;
29
+ };
30
+
31
+ // Check if deeplink exists
32
+ if (!column.deeplink) {
33
+ return <>{children}</>;
34
+ }
35
+
36
+ const handleClick = (e: React.MouseEvent) => {
37
+ e.preventDefault();
38
+ e.stopPropagation();
39
+
40
+ const deeplink = column.deeplink;
41
+ if (!deeplink) return;
42
+
43
+ try {
44
+ // Get the record ID
45
+ let recordId: string | undefined;
46
+
47
+ if (deeplink.id) {
48
+ // Static ID string
49
+ recordId = deeplink.id;
50
+ } else if (deeplink.idFn) {
51
+ // Execute the serialized function to get the ID
52
+ recordId = executeSerializedFunction(deeplink.idFn, value, record);
53
+ }
54
+
55
+ // Handle resource deeplinks
56
+ if (deeplink.resource) {
57
+ // Check if we're currently on the target resource page
58
+ const currentPath = location.pathname;
59
+ const isOnTargetResource = currentPath.startsWith(`/${deeplink.resource}`);
60
+
61
+ if (recordId) {
62
+ const mode = deeplink.edit ? 'edit' : 'view';
63
+ const currentResource = getCurrentResource();
64
+ const isSameResource = currentResource === deeplink.resource;
65
+
66
+ if (isSameResource) {
67
+ // Same resource: navigate to update URL (for bookmarking)
68
+ // The URL listener in ResourceListPage will open the modal via context
69
+ const path = deeplink.edit
70
+ ? `/${deeplink.resource}/${recordId}/edit`
71
+ : `/${deeplink.resource}/${recordId}`;
72
+ navigate(path);
73
+ } else {
74
+ // Cross-resource: push the target URL into browser history so the
75
+ // address bar updates, but do NOT call navigate() — that would unmount
76
+ // the current page. Store the current URL as originUrl so AdminPanel
77
+ // can restore it with replaceState when the modal closes.
78
+ const originUrl = location.pathname + location.search;
79
+ const targetPath = deeplink.edit
80
+ ? `/${deeplink.resource}/${recordId}/edit`
81
+ : `/${deeplink.resource}/${recordId}`;
82
+ window.history.pushState(null, '', targetPath);
83
+ openModal(deeplink.resource, mode, recordId, undefined, undefined, originUrl);
84
+ }
85
+ } else {
86
+ // No ID provided, navigate to resource list page
87
+ navigate(`/${deeplink.resource}`);
88
+ }
89
+ } else if (deeplink.page) {
90
+ // Page navigation
91
+ navigate(`/page/${deeplink.page}`);
92
+ }
93
+ } catch (error) {
94
+ console.error('Error handling deeplink:', error);
95
+ }
96
+ };
97
+
98
+ return (
99
+ <a href="#" onClick={handleClick} className={`cursor-pointer text-accent hover:underline ${className}`.trim()}>
100
+ {children}
101
+ </a>
102
+ );
103
+ }
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import { ColumnProps } from './TextColumnComponent';
3
+ import { formatValue } from '../../utils/tableFormatters';
4
+ import { cn } from '../../utils/classNames';
5
+ import { Icon, IconName } from '../utils/Icon';
6
+ import { executeSerializedFunction } from '../../runtime/serializedFunctions';
7
+
8
+ export function IconColumnComponent({ column, record }: ColumnProps) {
9
+ const rawValue = record[column.name];
10
+ // Apply formatStateUsing if present
11
+ const value = formatValue(rawValue, column, record);
12
+
13
+ // Determine icon name
14
+ let iconName: string | null = null;
15
+ if (column.iconFn) {
16
+ // Execute serialized function
17
+ const result = executeSerializedFunction(column.iconFn, value, record);
18
+ iconName = result !== undefined ? result : null;
19
+ } else if (column.icon) {
20
+ if (typeof column.icon === 'string') {
21
+ // Static icon
22
+ iconName = column.icon;
23
+ } else if (typeof column.icon === 'object') {
24
+ // Object mapping
25
+ iconName = column.icon[value] || null;
26
+ }
27
+ }
28
+
29
+ // Determine color class
30
+ let colorClass = 'text-fg';
31
+ if (column.iconColorFn) {
32
+ // Execute serialized function
33
+ const result = executeSerializedFunction(column.iconColorFn, value, record);
34
+ colorClass = result !== undefined ? result : 'text-fg';
35
+ } else if (column.iconColor) {
36
+ if (typeof column.iconColor === 'string') {
37
+ // Static color
38
+ colorClass = column.iconColor;
39
+ } else if (typeof column.iconColor === 'object') {
40
+ // Object mapping
41
+ colorClass = column.iconColor[value] || 'text-fg';
42
+ }
43
+ }
44
+
45
+ // If no icon is determined, show placeholder
46
+ if (!iconName) {
47
+ return <span className="text-fg-secondary">-</span>;
48
+ }
49
+
50
+ // Get size from column (default to 20px)
51
+ const size = column.size || 20;
52
+
53
+ // Render Lucide icon
54
+ return <Icon name={iconName} size={size} className={cn(colorClass)} />;
55
+ }
@@ -0,0 +1,220 @@
1
+ import React, { useState } from 'react';
2
+ import { ColumnProps } from './TextColumnComponent';
3
+ import { formatValue } from '../../utils/tableFormatters';
4
+ import { getColumnMediaDimensions } from '../../utils/columnMediaDimensions';
5
+ import { Icon } from '../utils/Icon';
6
+ import { DeeplinkWrapper } from './DeeplinkWrapper';
7
+ import { translate } from '../../i18n/activeLocale';
8
+
9
+ interface ImagePreviewModalProps {
10
+ isOpen: boolean;
11
+ onClose: () => void;
12
+ imageUrl: string;
13
+ title?: string;
14
+ }
15
+
16
+ function ImagePreviewModal({ isOpen, onClose, imageUrl, title }: ImagePreviewModalProps) {
17
+ if (!isOpen) return null;
18
+
19
+ return (
20
+ <div
21
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
22
+ onClick={onClose}>
23
+ <div
24
+ className="relative max-w-4xl max-h-[90vh] p-2 bg-surface rounded-lg shadow-2xl"
25
+ onClick={e => e.stopPropagation()}>
26
+ {/* Header */}
27
+ {title && (
28
+ <div className="px-4 py-2 border-b border-border">
29
+ <h3 className="text-lg font-semibold text-fg">{title}</h3>
30
+ </div>
31
+ )}
32
+
33
+ {/* Close button */}
34
+ <button
35
+ onClick={onClose}
36
+ className="absolute top-2 right-2 p-2 rounded-full bg-hover hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors z-10"
37
+ title={translate('core:modal.close')}>
38
+ <Icon name="X" size={20} className="text-fg" />
39
+ </button>
40
+
41
+ {/* Image */}
42
+ <div className="p-4">
43
+ <img
44
+ src={imageUrl}
45
+ alt={title || translate('core:common.preview')}
46
+ className="max-w-full max-h-[80vh] object-contain rounded"
47
+ />
48
+ </div>
49
+ </div>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ export function ImageColumnComponent({ column, record }: ColumnProps) {
55
+ const [previewOpen, setPreviewOpen] = useState(false);
56
+ const [previewUrl, setPreviewUrl] = useState<string>('');
57
+
58
+ const rawValue = record[column.name];
59
+ const value = rawValue;
60
+ const hasDeeplink = !!column.deeplink;
61
+ // Apply formatter if present (e.g., to extract nested property or prepend URL)
62
+ const formattedValue = formatValue(rawValue, column, record);
63
+
64
+ // Extract URL from new format: { key, storage, url } or use formatted value
65
+ let imageUrl: string | string[] | null = null;
66
+ if (formattedValue) {
67
+ if (typeof formattedValue === 'object' && formattedValue !== null) {
68
+ // New format: { key, storage, url } or array of such objects
69
+ if (Array.isArray(formattedValue)) {
70
+ imageUrl = formattedValue.map((item: any) => (typeof item === 'object' && item?.url ? item.url : item));
71
+ } else if (formattedValue.url) {
72
+ imageUrl = formattedValue.url;
73
+ } else {
74
+ imageUrl = formattedValue;
75
+ }
76
+ } else {
77
+ imageUrl = formattedValue;
78
+ }
79
+ }
80
+
81
+ imageUrl = imageUrl || column.defaultImageUrl;
82
+
83
+ if (!imageUrl) {
84
+ return <span className="text-fg-secondary">{translate('core:file.no_image')}</span>;
85
+ }
86
+
87
+ const getImageClasses = (circular: boolean) => {
88
+ if (circular) {
89
+ return 'h-full w-full object-cover';
90
+ }
91
+
92
+ const classes = ['object-cover'];
93
+
94
+ if (column.square) {
95
+ classes.push('rounded-none');
96
+ } else {
97
+ classes.push('rounded');
98
+ }
99
+
100
+ if (column.clickable || hasDeeplink) {
101
+ classes.push('cursor-pointer hover:opacity-80 transition-opacity');
102
+ }
103
+
104
+ return classes.join(' ');
105
+ };
106
+
107
+ const handleImageClick = (url: string) => {
108
+ // If there's a deeplink, don't handle the click here - DeeplinkWrapper will handle it
109
+ if (hasDeeplink) {
110
+ return;
111
+ }
112
+
113
+ // Fall back to existing clickable behavior
114
+ if (!column.clickable) return;
115
+
116
+ if (column.clickAction === 'link') {
117
+ window.open(url, '_blank');
118
+ } else {
119
+ // Default to preview
120
+ setPreviewUrl(url);
121
+ setPreviewOpen(true);
122
+ }
123
+ };
124
+
125
+ const dimensions = getColumnMediaDimensions(column, 40);
126
+ const isCircular = !!column.circular;
127
+
128
+ // Render single image helper
129
+ const renderImage = (url: string, extraClasses: string = '') => {
130
+ const interactive = column.clickable || hasDeeplink;
131
+
132
+ const imgElement = isCircular ? (
133
+ <span
134
+ className={`inline-flex shrink-0 overflow-hidden rounded-full ${interactive ? 'cursor-pointer hover:opacity-80 transition-opacity' : ''} ${extraClasses}`.trim()}
135
+ style={dimensions}
136
+ onClick={() => handleImageClick(url)}>
137
+ <img src={url} alt="" className={getImageClasses(true)} />
138
+ </span>
139
+ ) : (
140
+ <img
141
+ src={url}
142
+ alt=""
143
+ className={`${getImageClasses(false)} ${extraClasses}`.trim()}
144
+ style={dimensions}
145
+ onClick={() => handleImageClick(url)}
146
+ />
147
+ );
148
+
149
+ // Wrap in deeplink if exists
150
+ if (hasDeeplink) {
151
+ return (
152
+ <DeeplinkWrapper column={column} record={record} value={value} className="inline-block">
153
+ {imgElement}
154
+ </DeeplinkWrapper>
155
+ );
156
+ }
157
+
158
+ return imgElement;
159
+ };
160
+
161
+ // Handle arrays (stacked images)
162
+ if (Array.isArray(imageUrl)) {
163
+ const images = column.limit ? imageUrl.slice(0, column.limit) : imageUrl;
164
+
165
+ if (column.stacked) {
166
+ return (
167
+ <>
168
+ <div className="flex -space-x-2">
169
+ {images.map((url, idx) => (
170
+ <React.Fragment key={idx}>
171
+ {renderImage(url, 'ring-2 ring-white dark:ring-gray-800')}
172
+ </React.Fragment>
173
+ ))}
174
+ {column.limit && imageUrl.length > column.limit && (
175
+ <div
176
+ className={`flex items-center justify-center bg-muted text-fg-secondary text-xs font-medium ring-2 ring-surface ${isCircular ? 'rounded-full' : 'rounded'}`.trim()}
177
+ style={dimensions}>
178
+ +{imageUrl.length - column.limit}
179
+ </div>
180
+ )}
181
+ </div>
182
+ <ImagePreviewModal
183
+ isOpen={previewOpen}
184
+ onClose={() => setPreviewOpen(false)}
185
+ imageUrl={previewUrl}
186
+ title={column.previewTitle as string | undefined}
187
+ />
188
+ </>
189
+ );
190
+ }
191
+
192
+ return (
193
+ <>
194
+ <div className="flex gap-2">
195
+ {images.map((url, idx) => (
196
+ <React.Fragment key={idx}>{renderImage(url)}</React.Fragment>
197
+ ))}
198
+ </div>
199
+ <ImagePreviewModal
200
+ isOpen={previewOpen}
201
+ onClose={() => setPreviewOpen(false)}
202
+ imageUrl={previewUrl}
203
+ title={column.previewTitle as string | undefined}
204
+ />
205
+ </>
206
+ );
207
+ }
208
+
209
+ return (
210
+ <>
211
+ {renderImage(imageUrl)}
212
+ <ImagePreviewModal
213
+ isOpen={previewOpen}
214
+ onClose={() => setPreviewOpen(false)}
215
+ imageUrl={previewUrl}
216
+ title={column.previewTitle as string | undefined}
217
+ />
218
+ </>
219
+ );
220
+ }