@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.
- package/README.md +44 -0
- package/dist/FieldRenderer.d.ts +13 -0
- package/dist/FieldRenderer.js +62 -0
- package/dist/FormRenderer.d.ts +7 -0
- package/dist/FormRenderer.js +78 -0
- package/dist/TableRenderer.d.ts +2 -0
- package/dist/TableRenderer.js +1 -0
- package/dist/api/actionsApi.d.ts +23 -0
- package/dist/api/actionsApi.js +46 -0
- package/dist/api/authenticatedFetch.d.ts +8 -0
- package/dist/api/authenticatedFetch.js +31 -0
- package/dist/api/exportApi.d.ts +18 -0
- package/dist/api/exportApi.js +50 -0
- package/dist/api/http.d.ts +24 -0
- package/dist/api/http.js +52 -0
- package/dist/api/resourceApi.d.ts +37 -0
- package/dist/api/resourceApi.js +52 -0
- package/dist/api/tableApi.d.ts +83 -0
- package/dist/api/tableApi.js +51 -0
- package/dist/api/urls.d.ts +19 -0
- package/dist/api/urls.js +46 -0
- package/dist/app.d.ts +101 -0
- package/dist/app.js +89 -0
- package/dist/auth/AuthContext.d.ts +22 -0
- package/dist/auth/AuthContext.js +147 -0
- package/dist/auth/LoginPage.d.ts +10 -0
- package/dist/auth/LoginPage.js +179 -0
- package/dist/auth/ProtectedRoute.d.ts +12 -0
- package/dist/auth/ProtectedRoute.js +22 -0
- package/dist/auth/authApiClient.d.ts +24 -0
- package/dist/auth/authApiClient.js +95 -0
- package/dist/auth/types.d.ts +103 -0
- package/dist/auth/types.js +1 -0
- package/dist/components/ActionFormModal.d.ts +22 -0
- package/dist/components/ActionFormModal.js +8 -0
- package/dist/components/AdminPanel.d.ts +11 -0
- package/dist/components/AdminPanel.js +194 -0
- package/dist/components/Checkbox.d.ts +10 -0
- package/dist/components/Checkbox.js +8 -0
- package/dist/components/CheckboxField.d.ts +7 -0
- package/dist/components/CheckboxField.js +26 -0
- package/dist/components/ColorPickerField.d.ts +7 -0
- package/dist/components/ColorPickerField.js +26 -0
- package/dist/components/DateTimePickerField.d.ts +7 -0
- package/dist/components/DateTimePickerField.js +64 -0
- package/dist/components/FileUploadField.d.ts +9 -0
- package/dist/components/FileUploadField.js +478 -0
- package/dist/components/GlobalSearch.d.ts +22 -0
- package/dist/components/GlobalSearch.js +181 -0
- package/dist/components/GroupField.d.ts +7 -0
- package/dist/components/GroupField.js +23 -0
- package/dist/components/HiddenField.d.ts +3 -0
- package/dist/components/HiddenField.js +10 -0
- package/dist/components/ModalBreadcrumb.d.ts +5 -0
- package/dist/components/ModalBreadcrumb.js +33 -0
- package/dist/components/ModalDrawer.d.ts +15 -0
- package/dist/components/ModalDrawer.js +40 -0
- package/dist/components/RadioField.d.ts +7 -0
- package/dist/components/RadioField.js +26 -0
- package/dist/components/RepeaterField.d.ts +3 -0
- package/dist/components/RepeaterField.js +191 -0
- package/dist/components/ResourceModalRenderer.d.ts +10 -0
- package/dist/components/ResourceModalRenderer.js +80 -0
- package/dist/components/RichEditorField.d.ts +3 -0
- package/dist/components/RichEditorField.js +655 -0
- package/dist/components/SectionField.d.ts +9 -0
- package/dist/components/SectionField.js +111 -0
- package/dist/components/SelectField.d.ts +8 -0
- package/dist/components/SelectField.js +523 -0
- package/dist/components/TabsField.d.ts +10 -0
- package/dist/components/TabsField.js +214 -0
- package/dist/components/TagsInputField.d.ts +7 -0
- package/dist/components/TagsInputField.js +172 -0
- package/dist/components/TextInputField.d.ts +7 -0
- package/dist/components/TextInputField.js +44 -0
- package/dist/components/TextareaField.d.ts +7 -0
- package/dist/components/TextareaField.js +31 -0
- package/dist/components/ToggleField.d.ts +7 -0
- package/dist/components/ToggleField.js +57 -0
- package/dist/components/ViewModal.d.ts +25 -0
- package/dist/components/ViewModal.js +159 -0
- package/dist/components/blocks/BlockRenderer.d.ts +7 -0
- package/dist/components/blocks/BlockRenderer.js +36 -0
- package/dist/components/blocks/FormBlockRenderer.d.ts +6 -0
- package/dist/components/blocks/FormBlockRenderer.js +110 -0
- package/dist/components/blocks/TableBlockRenderer.d.ts +6 -0
- package/dist/components/blocks/TableBlockRenderer.js +12 -0
- package/dist/components/blocks/TabsBlockRenderer.d.ts +7 -0
- package/dist/components/blocks/TabsBlockRenderer.js +11 -0
- package/dist/components/blocks/WidgetBlockRenderer.d.ts +6 -0
- package/dist/components/blocks/WidgetBlockRenderer.js +11 -0
- package/dist/components/columns/CheckboxColumnComponent.d.ts +6 -0
- package/dist/components/columns/CheckboxColumnComponent.js +21 -0
- package/dist/components/columns/ColorColumnComponent.d.ts +3 -0
- package/dist/components/columns/ColorColumnComponent.js +11 -0
- package/dist/components/columns/DeeplinkWrapper.d.ts +15 -0
- package/dist/components/columns/DeeplinkWrapper.js +85 -0
- package/dist/components/columns/IconColumnComponent.d.ts +3 -0
- package/dist/components/columns/IconColumnComponent.js +52 -0
- package/dist/components/columns/ImageColumnComponent.d.ts +3 -0
- package/dist/components/columns/ImageColumnComponent.js +98 -0
- package/dist/components/columns/MediaColumnComponent.d.ts +3 -0
- package/dist/components/columns/MediaColumnComponent.js +160 -0
- package/dist/components/columns/SelectColumnComponent.d.ts +6 -0
- package/dist/components/columns/SelectColumnComponent.js +26 -0
- package/dist/components/columns/TagsColumnComponent.d.ts +3 -0
- package/dist/components/columns/TagsColumnComponent.js +18 -0
- package/dist/components/columns/TextColumnComponent.d.ts +11 -0
- package/dist/components/columns/TextColumnComponent.js +107 -0
- package/dist/components/columns/TextInputColumnComponent.d.ts +6 -0
- package/dist/components/columns/TextInputColumnComponent.js +18 -0
- package/dist/components/columns/ToggleColumnComponent.d.ts +6 -0
- package/dist/components/columns/ToggleColumnComponent.js +25 -0
- package/dist/components/columns/VideoColumnComponent.d.ts +3 -0
- package/dist/components/columns/VideoColumnComponent.js +125 -0
- package/dist/components/columns/ViewColumnComponent.d.ts +3 -0
- package/dist/components/columns/ViewColumnComponent.js +7 -0
- package/dist/components/errors/ErrorBoundary.d.ts +23 -0
- package/dist/components/errors/ErrorBoundary.js +33 -0
- package/dist/components/filters/CustomFilterComponent.d.ts +10 -0
- package/dist/components/filters/CustomFilterComponent.js +33 -0
- package/dist/components/filters/DateFilterComponent.d.ts +15 -0
- package/dist/components/filters/DateFilterComponent.js +132 -0
- package/dist/components/filters/QueryBuilderFilterComponent.d.ts +11 -0
- package/dist/components/filters/QueryBuilderFilterComponent.js +200 -0
- package/dist/components/layout/Header.d.ts +10 -0
- package/dist/components/layout/Header.js +70 -0
- package/dist/components/layout/PanelBrandMark.d.ts +8 -0
- package/dist/components/layout/PanelBrandMark.js +28 -0
- package/dist/components/layout/Sidebar.d.ts +35 -0
- package/dist/components/layout/Sidebar.js +125 -0
- package/dist/components/modals/RelationCreateModal.d.ts +19 -0
- package/dist/components/modals/RelationCreateModal.js +57 -0
- package/dist/components/modals/ResourceFormModal.d.ts +37 -0
- package/dist/components/modals/ResourceFormModal.js +44 -0
- package/dist/components/modals/useResourceForm.d.ts +40 -0
- package/dist/components/modals/useResourceForm.js +138 -0
- package/dist/components/modals/view/RecordActions.d.ts +17 -0
- package/dist/components/modals/view/RecordActions.js +16 -0
- package/dist/components/modals/view/RecordDetails.d.ts +13 -0
- package/dist/components/modals/view/RecordDetails.js +29 -0
- package/dist/components/modals/view/RelationPanel.d.ts +18 -0
- package/dist/components/modals/view/RelationPanel.js +16 -0
- package/dist/components/modals/view/RelationTabs.d.ts +32 -0
- package/dist/components/modals/view/RelationTabs.js +42 -0
- package/dist/components/modals/view/useRecordView.d.ts +18 -0
- package/dist/components/modals/view/useRecordView.js +114 -0
- package/dist/components/pages/PageRenderer.d.ts +6 -0
- package/dist/components/pages/PageRenderer.js +107 -0
- package/dist/components/table/ColumnTogglePopup.d.ts +11 -0
- package/dist/components/table/ColumnTogglePopup.js +16 -0
- package/dist/components/table/GridCard.d.ts +21 -0
- package/dist/components/table/GridCard.js +30 -0
- package/dist/components/table/GridView.d.ts +23 -0
- package/dist/components/table/GridView.js +49 -0
- package/dist/components/table/LayoutToggle.d.ts +7 -0
- package/dist/components/table/LayoutToggle.js +9 -0
- package/dist/components/table/TableActionsDropdown.d.ts +13 -0
- package/dist/components/table/TableActionsDropdown.js +46 -0
- package/dist/components/table/TableBulkActions.d.ts +11 -0
- package/dist/components/table/TableBulkActions.js +21 -0
- package/dist/components/table/TableHeader.d.ts +14 -0
- package/dist/components/table/TableHeader.js +23 -0
- package/dist/components/table/TablePagination.d.ts +13 -0
- package/dist/components/table/TablePagination.js +55 -0
- package/dist/components/table/TableRow.d.ts +21 -0
- package/dist/components/table/TableRow.js +32 -0
- package/dist/components/table/TableSearchBar.d.ts +11 -0
- package/dist/components/table/TableSearchBar.js +12 -0
- package/dist/components/table/TableTabs.d.ts +14 -0
- package/dist/components/table/TableTabs.js +8 -0
- package/dist/components/ui/Badge.d.ts +6 -0
- package/dist/components/ui/Badge.js +12 -0
- package/dist/components/ui/Button.d.ts +22 -0
- package/dist/components/ui/Button.js +22 -0
- package/dist/components/ui/Card.d.ts +7 -0
- package/dist/components/ui/Card.js +5 -0
- package/dist/components/ui/ConfirmDialog.d.ts +19 -0
- package/dist/components/ui/ConfirmDialog.js +45 -0
- package/dist/components/ui/EmptyState.d.ts +9 -0
- package/dist/components/ui/EmptyState.js +6 -0
- package/dist/components/ui/ErrorAlert.d.ts +7 -0
- package/dist/components/ui/ErrorAlert.js +9 -0
- package/dist/components/ui/Input.d.ts +11 -0
- package/dist/components/ui/Input.js +10 -0
- package/dist/components/ui/Label.d.ts +5 -0
- package/dist/components/ui/Label.js +5 -0
- package/dist/components/ui/PillButton.d.ts +14 -0
- package/dist/components/ui/PillButton.js +19 -0
- package/dist/components/ui/Select.d.ts +7 -0
- package/dist/components/ui/Select.js +7 -0
- package/dist/components/ui/Spinner.d.ts +8 -0
- package/dist/components/ui/Spinner.js +14 -0
- package/dist/components/ui/Toast.d.ts +21 -0
- package/dist/components/ui/Toast.js +47 -0
- package/dist/components/ui/index.d.ts +24 -0
- package/dist/components/ui/index.js +12 -0
- package/dist/components/utils/HintDisplay.d.ts +11 -0
- package/dist/components/utils/HintDisplay.js +12 -0
- package/dist/components/utils/Icon.d.ts +22 -0
- package/dist/components/utils/Icon.js +22 -0
- package/dist/components/utils/MediaPreviewModal.d.ts +14 -0
- package/dist/components/utils/MediaPreviewModal.js +32 -0
- package/dist/components/utils/ViewFieldWrapper.d.ts +11 -0
- package/dist/components/utils/ViewFieldWrapper.js +9 -0
- package/dist/components/utils/layoutHelpers.d.ts +19 -0
- package/dist/components/utils/layoutHelpers.js +257 -0
- package/dist/components/widgets/ChartWidget.d.ts +16 -0
- package/dist/components/widgets/ChartWidget.js +192 -0
- package/dist/components/widgets/StatsWidget.d.ts +16 -0
- package/dist/components/widgets/StatsWidget.js +39 -0
- package/dist/components/widgets/WidgetRenderer.d.ts +10 -0
- package/dist/components/widgets/WidgetRenderer.js +50 -0
- package/dist/components/widgets/WidgetShell.d.ts +9 -0
- package/dist/components/widgets/WidgetShell.js +7 -0
- package/dist/contexts/AuthChallengeRegistryContext.d.ts +15 -0
- package/dist/contexts/AuthChallengeRegistryContext.js +15 -0
- package/dist/contexts/BlockRegistryContext.d.ts +18 -0
- package/dist/contexts/BlockRegistryContext.js +8 -0
- package/dist/contexts/ColumnRegistryContext.d.ts +8 -0
- package/dist/contexts/ColumnRegistryContext.js +30 -0
- package/dist/contexts/FieldRegistryContext.d.ts +13 -0
- package/dist/contexts/FieldRegistryContext.js +46 -0
- package/dist/contexts/PanelMetadataContext.d.ts +26 -0
- package/dist/contexts/PanelMetadataContext.js +26 -0
- package/dist/contexts/PanelProviders.d.ts +27 -0
- package/dist/contexts/PanelProviders.js +24 -0
- package/dist/contexts/ResourceModalContext.d.ts +26 -0
- package/dist/contexts/ResourceModalContext.js +76 -0
- package/dist/contexts/SlotRegistryContext.d.ts +19 -0
- package/dist/contexts/SlotRegistryContext.js +24 -0
- package/dist/contexts/TableRefreshContext.d.ts +10 -0
- package/dist/contexts/TableRefreshContext.js +30 -0
- package/dist/contexts/WidgetRegistryContext.d.ts +17 -0
- package/dist/contexts/WidgetRegistryContext.js +14 -0
- package/dist/contexts/createRegistryContext.d.ts +19 -0
- package/dist/contexts/createRegistryContext.js +20 -0
- package/dist/hooks/useAfterStateUpdated.d.ts +6 -0
- package/dist/hooks/useAfterStateUpdated.js +62 -0
- package/dist/hooks/useValidation.d.ts +26 -0
- package/dist/hooks/useValidation.js +76 -0
- package/dist/i18n/I18nProvider.d.ts +27 -0
- package/dist/i18n/I18nProvider.js +101 -0
- package/dist/i18n/LocaleSwitcher.d.ts +10 -0
- package/dist/i18n/LocaleSwitcher.js +30 -0
- package/dist/i18n/activeLocale.d.ts +11 -0
- package/dist/i18n/activeLocale.js +34 -0
- package/dist/i18n/buildClientI18n.d.ts +28 -0
- package/dist/i18n/buildClientI18n.js +67 -0
- package/dist/i18n/index.d.ts +11 -0
- package/dist/i18n/index.js +9 -0
- package/dist/i18n/locales/core/en.d.ts +225 -0
- package/dist/i18n/locales/core/en.js +252 -0
- package/dist/i18n/locales/core/index.d.ts +2 -0
- package/dist/i18n/locales/core/index.js +4 -0
- package/dist/i18n/locales/core/sq.d.ts +253 -0
- package/dist/i18n/locales/core/sq.js +255 -0
- package/dist/i18n/useFormatter.d.ts +18 -0
- package/dist/i18n/useFormatter.js +37 -0
- package/dist/i18n/useLocale.d.ts +11 -0
- package/dist/i18n/useLocale.js +11 -0
- package/dist/i18n/useTranslation.d.ts +12 -0
- package/dist/i18n/useTranslation.js +12 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +101 -0
- package/dist/pages/ResourceListPage.d.ts +8 -0
- package/dist/pages/ResourceListPage.js +139 -0
- package/dist/plugin.d.ts +79 -0
- package/dist/plugin.js +34 -0
- package/dist/runtime/conditions.d.ts +35 -0
- package/dist/runtime/conditions.js +97 -0
- package/dist/runtime/formTraversal.d.ts +25 -0
- package/dist/runtime/formTraversal.js +37 -0
- package/dist/runtime/serializedFunctions.d.ts +41 -0
- package/dist/runtime/serializedFunctions.js +264 -0
- package/dist/slots/Slot.d.ts +24 -0
- package/dist/slots/Slot.js +29 -0
- package/dist/slots/SlotCluster.d.ts +22 -0
- package/dist/slots/SlotCluster.js +49 -0
- package/dist/slots/index.d.ts +7 -0
- package/dist/slots/index.js +4 -0
- package/dist/slots/mergeSlots.d.ts +18 -0
- package/dist/slots/mergeSlots.js +35 -0
- package/dist/slots/types.d.ts +87 -0
- package/dist/slots/types.js +30 -0
- package/dist/styles.css +1 -0
- package/dist/table/TableContext.d.ts +36 -0
- package/dist/table/TableContext.js +13 -0
- package/dist/table/TableRenderer.d.ts +29 -0
- package/dist/table/TableRenderer.js +159 -0
- package/dist/table/components/FiltersPanel.d.ts +11 -0
- package/dist/table/components/FiltersPanel.js +52 -0
- package/dist/table/components/TableToolbar.d.ts +28 -0
- package/dist/table/components/TableToolbar.js +27 -0
- package/dist/table/components/TableToolbarButton.d.ts +6 -0
- package/dist/table/components/TableToolbarButton.js +9 -0
- package/dist/table/components/TableView.d.ts +12 -0
- package/dist/table/components/TableView.js +21 -0
- package/dist/table/defaultRowActions.d.ts +21 -0
- package/dist/table/defaultRowActions.js +37 -0
- package/dist/table/hooks/useColumnVisibility.d.ts +13 -0
- package/dist/table/hooks/useColumnVisibility.js +59 -0
- package/dist/table/hooks/useEditableRows.d.ts +22 -0
- package/dist/table/hooks/useEditableRows.js +63 -0
- package/dist/table/hooks/useTableActions.d.ts +54 -0
- package/dist/table/hooks/useTableActions.js +313 -0
- package/dist/table/hooks/useTableData.d.ts +28 -0
- package/dist/table/hooks/useTableData.js +63 -0
- package/dist/table/hooks/useTableLayout.d.ts +12 -0
- package/dist/table/hooks/useTableLayout.js +31 -0
- package/dist/table/hooks/useTableQuery.d.ts +29 -0
- package/dist/table/hooks/useTableQuery.js +135 -0
- package/dist/types/index.d.ts +224 -0
- package/dist/types/index.js +6 -0
- package/dist/utils/classNames.d.ts +7 -0
- package/dist/utils/classNames.js +9 -0
- package/dist/utils/columnMediaDimensions.d.ts +13 -0
- package/dist/utils/columnMediaDimensions.js +29 -0
- package/dist/utils/columnVisibilityStorage.d.ts +22 -0
- package/dist/utils/columnVisibilityStorage.js +56 -0
- package/dist/utils/fieldErrors.d.ts +13 -0
- package/dist/utils/fieldErrors.js +25 -0
- package/dist/utils/formatValue.d.ts +28 -0
- package/dist/utils/formatValue.js +109 -0
- package/dist/utils/layoutStorage.d.ts +23 -0
- package/dist/utils/layoutStorage.js +53 -0
- package/dist/utils/redirectHandler.d.ts +7 -0
- package/dist/utils/redirectHandler.js +25 -0
- package/dist/utils/tableFormatters.d.ts +14 -0
- package/dist/utils/tableFormatters.js +93 -0
- package/dist/utils/widgetVisibilityStorage.d.ts +11 -0
- package/dist/utils/widgetVisibilityStorage.js +39 -0
- package/package.json +101 -0
- package/src/FieldRenderer.test.tsx +44 -0
- package/src/FieldRenderer.tsx +104 -0
- package/src/FormRenderer.containers.test.tsx +121 -0
- package/src/FormRenderer.test.tsx +174 -0
- package/src/FormRenderer.tsx +140 -0
- package/src/TableRenderer.tsx +2 -0
- package/src/api/actionsApi.ts +76 -0
- package/src/api/authenticatedFetch.ts +40 -0
- package/src/api/exportApi.ts +66 -0
- package/src/api/http.test.ts +58 -0
- package/src/api/http.ts +68 -0
- package/src/api/resourceApi.ts +88 -0
- package/src/api/tableApi.test.ts +108 -0
- package/src/api/tableApi.ts +107 -0
- package/src/api/urls.ts +50 -0
- package/src/app.test.tsx +67 -0
- package/src/app.tsx +181 -0
- package/src/auth/AuthContext.tsx +188 -0
- package/src/auth/LoginPage.tsx +380 -0
- package/src/auth/ProtectedRoute.tsx +39 -0
- package/src/auth/authApiClient.ts +109 -0
- package/src/auth/authFlow.test.tsx +168 -0
- package/src/auth/types.ts +104 -0
- package/src/components/ActionFormModal.tsx +45 -0
- package/src/components/AdminPanel.tsx +368 -0
- package/src/components/Checkbox.tsx +59 -0
- package/src/components/CheckboxField.tsx +88 -0
- package/src/components/ColorPickerField.tsx +93 -0
- package/src/components/DateTimePickerField.tsx +112 -0
- package/src/components/FileUploadField.tsx +841 -0
- package/src/components/GlobalSearch.tsx +436 -0
- package/src/components/GroupField.tsx +85 -0
- package/src/components/HiddenField.tsx +14 -0
- package/src/components/ModalBreadcrumb.tsx +74 -0
- package/src/components/ModalDrawer.tsx +137 -0
- package/src/components/RadioField.tsx +80 -0
- package/src/components/RepeaterField.tsx +546 -0
- package/src/components/ResourceModalRenderer.tsx +144 -0
- package/src/components/RichEditorField.tsx +942 -0
- package/src/components/SectionField.tsx +242 -0
- package/src/components/SelectField.tsx +843 -0
- package/src/components/TabsField.test.tsx +151 -0
- package/src/components/TabsField.tsx +386 -0
- package/src/components/TagsInputField.tsx +411 -0
- package/src/components/TextInputField.tsx +91 -0
- package/src/components/TextareaField.tsx +110 -0
- package/src/components/ToggleField.tsx +126 -0
- package/src/components/ViewModal.tsx +353 -0
- package/src/components/blocks/BlockRenderer.tsx +56 -0
- package/src/components/blocks/FormBlockRenderer.tsx +160 -0
- package/src/components/blocks/TableBlockRenderer.tsx +33 -0
- package/src/components/blocks/TabsBlockRenderer.tsx +49 -0
- package/src/components/blocks/WidgetBlockRenderer.tsx +19 -0
- package/src/components/columns/CheckboxColumnComponent.tsx +38 -0
- package/src/components/columns/ColorColumnComponent.tsx +23 -0
- package/src/components/columns/CustomColumn.test.tsx +55 -0
- package/src/components/columns/DeeplinkWrapper.tsx +103 -0
- package/src/components/columns/IconColumnComponent.tsx +55 -0
- package/src/components/columns/ImageColumnComponent.tsx +220 -0
- package/src/components/columns/MediaColumnComponent.tsx +294 -0
- package/src/components/columns/SelectColumnComponent.tsx +49 -0
- package/src/components/columns/TagsColumnComponent.tsx +46 -0
- package/src/components/columns/TextColumnComponent.tsx +191 -0
- package/src/components/columns/TextInputColumnComponent.tsx +35 -0
- package/src/components/columns/ToggleColumnComponent.tsx +56 -0
- package/src/components/columns/VideoColumnComponent.tsx +236 -0
- package/src/components/columns/ViewColumnComponent.tsx +9 -0
- package/src/components/errors/ErrorBoundary.tsx +58 -0
- package/src/components/filters/CustomFilterComponent.tsx +130 -0
- package/src/components/filters/DateFilterComponent.tsx +272 -0
- package/src/components/filters/QueryBuilderFilterComponent.tsx +502 -0
- package/src/components/layout/Header.tsx +212 -0
- package/src/components/layout/PanelBrandMark.tsx +61 -0
- package/src/components/layout/Sidebar.tsx +283 -0
- package/src/components/modals/RelationCreateModal.tsx +107 -0
- package/src/components/modals/ResourceFormModal.test.tsx +119 -0
- package/src/components/modals/ResourceFormModal.tsx +128 -0
- package/src/components/modals/useResourceForm.ts +207 -0
- package/src/components/modals/view/RecordActions.tsx +69 -0
- package/src/components/modals/view/RecordDetails.tsx +60 -0
- package/src/components/modals/view/RelationPanel.tsx +76 -0
- package/src/components/modals/view/RelationTabs.tsx +145 -0
- package/src/components/modals/view/useRecordView.ts +134 -0
- package/src/components/pages/PageRenderer.tsx +173 -0
- package/src/components/table/ColumnTogglePopup.tsx +85 -0
- package/src/components/table/GridCard.tsx +155 -0
- package/src/components/table/GridView.tsx +138 -0
- package/src/components/table/LayoutToggle.tsx +24 -0
- package/src/components/table/TableActionsDropdown.tsx +114 -0
- package/src/components/table/TableBulkActions.tsx +65 -0
- package/src/components/table/TableHeader.tsx +96 -0
- package/src/components/table/TablePagination.tsx +169 -0
- package/src/components/table/TableRow.tsx +155 -0
- package/src/components/table/TableSearchBar.tsx +66 -0
- package/src/components/table/TableTabs.tsx +49 -0
- package/src/components/ui/Badge.tsx +30 -0
- package/src/components/ui/Button.test.tsx +78 -0
- package/src/components/ui/Button.tsx +102 -0
- package/src/components/ui/Card.tsx +23 -0
- package/src/components/ui/ConfirmDialog.tsx +112 -0
- package/src/components/ui/EmptyState.tsx +24 -0
- package/src/components/ui/ErrorAlert.tsx +37 -0
- package/src/components/ui/Input.tsx +48 -0
- package/src/components/ui/Label.tsx +15 -0
- package/src/components/ui/PillButton.tsx +72 -0
- package/src/components/ui/Select.tsx +33 -0
- package/src/components/ui/Spinner.tsx +39 -0
- package/src/components/ui/Toast.tsx +105 -0
- package/src/components/ui/index.ts +24 -0
- package/src/components/utils/HintDisplay.tsx +26 -0
- package/src/components/utils/Icon.tsx +36 -0
- package/src/components/utils/MediaPreviewModal.tsx +114 -0
- package/src/components/utils/ViewFieldWrapper.tsx +23 -0
- package/src/components/utils/layoutHelpers.ts +267 -0
- package/src/components/widgets/ChartWidget.tsx +247 -0
- package/src/components/widgets/StatsWidget.tsx +72 -0
- package/src/components/widgets/WidgetRenderer.tsx +108 -0
- package/src/components/widgets/WidgetShell.tsx +37 -0
- package/src/contexts/AuthChallengeRegistryContext.tsx +29 -0
- package/src/contexts/BlockRegistryContext.tsx +28 -0
- package/src/contexts/ColumnRegistryContext.tsx +38 -0
- package/src/contexts/FieldRegistryContext.tsx +56 -0
- package/src/contexts/PanelMetadataContext.tsx +60 -0
- package/src/contexts/PanelProviders.tsx +85 -0
- package/src/contexts/ResourceModalContext.tsx +137 -0
- package/src/contexts/SlotRegistryContext.tsx +35 -0
- package/src/contexts/TableRefreshContext.tsx +44 -0
- package/src/contexts/WidgetRegistryContext.tsx +34 -0
- package/src/contexts/createRegistryContext.tsx +29 -0
- package/src/hooks/useAfterStateUpdated.ts +70 -0
- package/src/hooks/useValidation.test.ts +59 -0
- package/src/hooks/useValidation.ts +95 -0
- package/src/i18n/I18nProvider.tsx +128 -0
- package/src/i18n/LocaleSwitcher.tsx +50 -0
- package/src/i18n/activeLocale.ts +39 -0
- package/src/i18n/buildClientI18n.ts +101 -0
- package/src/i18n/i18n.test.tsx +140 -0
- package/src/i18n/index.ts +12 -0
- package/src/i18n/locales/core/en.ts +274 -0
- package/src/i18n/locales/core/index.ts +5 -0
- package/src/i18n/locales/core/sq.ts +275 -0
- package/src/i18n/useFormatter.ts +42 -0
- package/src/i18n/useLocale.ts +16 -0
- package/src/i18n/useTranslation.ts +17 -0
- package/src/index.ts +244 -0
- package/src/pages/ResourceListPage.tsx +205 -0
- package/src/plugin.ts +110 -0
- package/src/runtime/conditions.test.ts +99 -0
- package/src/runtime/conditions.ts +148 -0
- package/src/runtime/formTraversal.ts +41 -0
- package/src/runtime/serializedFunctions.test.ts +59 -0
- package/src/runtime/serializedFunctions.ts +284 -0
- package/src/slots/Slot.test.tsx +89 -0
- package/src/slots/Slot.tsx +47 -0
- package/src/slots/SlotCluster.test.tsx +95 -0
- package/src/slots/SlotCluster.tsx +107 -0
- package/src/slots/index.ts +15 -0
- package/src/slots/mergeSlots.test.ts +71 -0
- package/src/slots/mergeSlots.ts +40 -0
- package/src/slots/slotNames.test.ts +21 -0
- package/src/slots/types.ts +119 -0
- package/src/styles.css +437 -0
- package/src/table/TableContext.tsx +41 -0
- package/src/table/TableRenderer.test.tsx +197 -0
- package/src/table/TableRenderer.tsx +390 -0
- package/src/table/components/FiltersPanel.tsx +193 -0
- package/src/table/components/TableToolbar.tsx +153 -0
- package/src/table/components/TableToolbarButton.tsx +14 -0
- package/src/table/components/TableView.tsx +106 -0
- package/src/table/defaultRowActions.ts +43 -0
- package/src/table/hooks/useColumnVisibility.test.ts +51 -0
- package/src/table/hooks/useColumnVisibility.ts +71 -0
- package/src/table/hooks/useEditableRows.test.ts +69 -0
- package/src/table/hooks/useEditableRows.ts +89 -0
- package/src/table/hooks/useTableActions.ts +393 -0
- package/src/table/hooks/useTableData.ts +89 -0
- package/src/table/hooks/useTableLayout.ts +45 -0
- package/src/table/hooks/useTableQuery.test.ts +116 -0
- package/src/table/hooks/useTableQuery.ts +172 -0
- package/src/test/mockFetch.ts +67 -0
- package/src/test/setup.ts +25 -0
- package/src/types/index.ts +228 -0
- package/src/utils/classNames.ts +10 -0
- package/src/utils/columnMediaDimensions.ts +45 -0
- package/src/utils/columnVisibilityStorage.ts +55 -0
- package/src/utils/fieldErrors.test.ts +35 -0
- package/src/utils/fieldErrors.ts +27 -0
- package/src/utils/formatValue.test.tsx +65 -0
- package/src/utils/formatValue.tsx +117 -0
- package/src/utils/layoutStorage.ts +52 -0
- package/src/utils/redirectHandler.ts +29 -0
- package/src/utils/tableFormatters.test.ts +54 -0
- package/src/utils/tableFormatters.ts +104 -0
- package/src/utils/widgetVisibilityStorage.ts +38 -0
- package/tailwind.config.js +9 -0
- 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
|
+
});
|