@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,95 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import { SlotCluster } from './SlotCluster';
|
|
4
|
+
import { SlotRegistryProvider } from '../contexts/SlotRegistryContext';
|
|
5
|
+
import type { ResolvedSlots } from './types';
|
|
6
|
+
|
|
7
|
+
function item(id: string, label: string) {
|
|
8
|
+
return { id, render: () => <button data-testid="cluster-item">{label}</button> };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function renderCluster(slots: ResolvedSlots, maxInline?: number) {
|
|
12
|
+
return render(
|
|
13
|
+
<SlotRegistryProvider slots={slots}>
|
|
14
|
+
<SlotCluster name="header.right" maxInline={maxInline} />
|
|
15
|
+
</SlotRegistryProvider>,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Trigger buttons are the overflow ("…") menu toggles, marked aria-haspopup="menu". */
|
|
20
|
+
function overflowTriggers(container: HTMLElement): HTMLButtonElement[] {
|
|
21
|
+
return Array.from(container.querySelectorAll<HTMLButtonElement>('button[aria-haspopup="menu"]'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('SlotCluster', () => {
|
|
25
|
+
it('renders nothing when the slot is empty', () => {
|
|
26
|
+
const { container } = renderCluster({});
|
|
27
|
+
expect(container).toBeEmptyDOMElement();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders the first maxInline contributions inline', () => {
|
|
31
|
+
renderCluster(
|
|
32
|
+
{
|
|
33
|
+
'header.right': [item('a', 'one'), item('b', 'two'), item('c', 'three')],
|
|
34
|
+
},
|
|
35
|
+
2,
|
|
36
|
+
);
|
|
37
|
+
// Only the two inline items are mounted up-front; overflow lives in closed menus.
|
|
38
|
+
const inline = screen.getAllByTestId('cluster-item');
|
|
39
|
+
expect(inline.map(el => el.textContent)).toEqual(['one', 'two']);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('collapses extras into a dropdown that opens and closes', () => {
|
|
43
|
+
const { container } = renderCluster(
|
|
44
|
+
{
|
|
45
|
+
'header.right': [item('a', 'one'), item('b', 'two'), item('c', 'three')],
|
|
46
|
+
},
|
|
47
|
+
2,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// The third item is not shown until an overflow menu opens.
|
|
51
|
+
expect(screen.queryByText('three')).not.toBeInTheDocument();
|
|
52
|
+
|
|
53
|
+
// The desktop overflow trigger is the first haspopup button.
|
|
54
|
+
const [desktopTrigger] = overflowTriggers(container);
|
|
55
|
+
expect(desktopTrigger).toBeTruthy();
|
|
56
|
+
|
|
57
|
+
fireEvent.click(desktopTrigger);
|
|
58
|
+
expect(screen.getByText('three')).toBeInTheDocument();
|
|
59
|
+
expect(desktopTrigger).toHaveAttribute('aria-expanded', 'true');
|
|
60
|
+
|
|
61
|
+
// Escape closes it.
|
|
62
|
+
fireEvent.keyDown(document, { key: 'Escape' });
|
|
63
|
+
expect(screen.queryByText('three')).not.toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('closes the menu on an outside click', () => {
|
|
67
|
+
const { container } = renderCluster(
|
|
68
|
+
{
|
|
69
|
+
'header.right': [item('a', 'one'), item('b', 'two'), item('c', 'three')],
|
|
70
|
+
},
|
|
71
|
+
2,
|
|
72
|
+
);
|
|
73
|
+
const [desktopTrigger] = overflowTriggers(container);
|
|
74
|
+
fireEvent.click(desktopTrigger);
|
|
75
|
+
expect(screen.getByText('three')).toBeInTheDocument();
|
|
76
|
+
|
|
77
|
+
fireEvent.mouseDown(document.body);
|
|
78
|
+
expect(screen.queryByText('three')).not.toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('still exposes a mobile overflow menu even when nothing overflows', () => {
|
|
82
|
+
// With maxInline >= count there are no desktop extras, but the < sm menu
|
|
83
|
+
// holds everything so a phone-width row never overflows.
|
|
84
|
+
const { container } = renderCluster({ 'header.right': [item('a', 'one')] }, 5);
|
|
85
|
+
const triggers = overflowTriggers(container);
|
|
86
|
+
// Desktop overflow has no items (renders null); only the mobile menu trigger remains.
|
|
87
|
+
expect(triggers.length).toBe(1);
|
|
88
|
+
|
|
89
|
+
// 'one' already renders inline for sm+; opening the mobile menu mounts a second copy.
|
|
90
|
+
expect(screen.getAllByText('one')).toHaveLength(1);
|
|
91
|
+
fireEvent.click(triggers[0]);
|
|
92
|
+
expect(triggers[0]).toHaveAttribute('aria-expanded', 'true');
|
|
93
|
+
expect(screen.getAllByText('one')).toHaveLength(2);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { MoreHorizontal } from 'lucide-react';
|
|
3
|
+
import { IconButton } from '../components/ui';
|
|
4
|
+
import { cn } from '../utils/classNames';
|
|
5
|
+
import { translate } from '../i18n/activeLocale';
|
|
6
|
+
import { useSlot } from '../contexts/SlotRegistryContext';
|
|
7
|
+
import { renderSlot } from './Slot';
|
|
8
|
+
import type { SlotContext, SlotContribution, SlotName } from './types';
|
|
9
|
+
|
|
10
|
+
/** A self-contained "…" dropdown holding overflow slot contributions. */
|
|
11
|
+
function OverflowMenu({
|
|
12
|
+
items,
|
|
13
|
+
context,
|
|
14
|
+
className,
|
|
15
|
+
}: {
|
|
16
|
+
items: SlotContribution[];
|
|
17
|
+
context: SlotContext;
|
|
18
|
+
className?: string;
|
|
19
|
+
}) {
|
|
20
|
+
const [open, setOpen] = useState(false);
|
|
21
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!open) return;
|
|
25
|
+
const onClick = (event: MouseEvent) => {
|
|
26
|
+
if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false);
|
|
27
|
+
};
|
|
28
|
+
const onEsc = (event: KeyboardEvent) => {
|
|
29
|
+
if (event.key === 'Escape') setOpen(false);
|
|
30
|
+
};
|
|
31
|
+
document.addEventListener('mousedown', onClick);
|
|
32
|
+
document.addEventListener('keydown', onEsc);
|
|
33
|
+
return () => {
|
|
34
|
+
document.removeEventListener('mousedown', onClick);
|
|
35
|
+
document.removeEventListener('keydown', onEsc);
|
|
36
|
+
};
|
|
37
|
+
}, [open]);
|
|
38
|
+
|
|
39
|
+
if (items.length === 0) return null;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={cn('relative', className)} ref={ref}>
|
|
43
|
+
<IconButton
|
|
44
|
+
variant="ghost"
|
|
45
|
+
size="sm"
|
|
46
|
+
aria-label={translate('core:common.more')}
|
|
47
|
+
aria-expanded={open}
|
|
48
|
+
aria-haspopup="menu"
|
|
49
|
+
className="h-9 w-9 shrink-0"
|
|
50
|
+
onClick={() => setOpen(o => !o)}>
|
|
51
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
52
|
+
</IconButton>
|
|
53
|
+
{open && (
|
|
54
|
+
<div
|
|
55
|
+
role="menu"
|
|
56
|
+
className="absolute right-0 z-50 mt-2 flex w-56 flex-col gap-1 rounded-xl border border-border bg-raised p-2 shadow-soft-lg">
|
|
57
|
+
{items.map(item => renderSlot(item, context))}
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface SlotClusterProps {
|
|
65
|
+
/** The slot to render. */
|
|
66
|
+
name: SlotName;
|
|
67
|
+
/** Context passed to each contribution. `slot` is filled in automatically. */
|
|
68
|
+
context?: Omit<SlotContext, 'slot'>;
|
|
69
|
+
/**
|
|
70
|
+
* How many contributions render inline on `sm`+ screens before the rest
|
|
71
|
+
* collapse into a "…" menu. Below `sm`, everything collapses. Default 2.
|
|
72
|
+
*/
|
|
73
|
+
maxInline?: number;
|
|
74
|
+
/** className for the inline flex row. */
|
|
75
|
+
className?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A responsive slot for tight horizontal areas (header right, table toolbar).
|
|
80
|
+
* On `sm`+ the first `maxInline` items render inline and any extras fall into a
|
|
81
|
+
* "…" dropdown; below `sm` every item collapses into the dropdown, so a crowded
|
|
82
|
+
* slot can never overflow a phone-width row regardless of contributor count.
|
|
83
|
+
*/
|
|
84
|
+
export function SlotCluster({ name, context, maxInline = 2, className }: SlotClusterProps) {
|
|
85
|
+
const contributions = useSlot(name);
|
|
86
|
+
if (contributions.length === 0) return null;
|
|
87
|
+
|
|
88
|
+
const fullContext: SlotContext = { ...context, slot: name };
|
|
89
|
+
const inline = contributions.slice(0, maxInline);
|
|
90
|
+
const overflow = contributions.slice(maxInline);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{/* sm+ : inline items + (optional) overflow menu */}
|
|
95
|
+
<div className={cn('hidden items-center gap-1 sm:flex sm:gap-2', className)}>
|
|
96
|
+
{inline.map(item => (
|
|
97
|
+
<div key={item.id} className="shrink-0">
|
|
98
|
+
{renderSlot(item, fullContext)}
|
|
99
|
+
</div>
|
|
100
|
+
))}
|
|
101
|
+
<OverflowMenu items={overflow} context={fullContext} />
|
|
102
|
+
</div>
|
|
103
|
+
{/* < sm : everything in the overflow menu */}
|
|
104
|
+
<OverflowMenu items={contributions} context={fullContext} className="sm:hidden" />
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { Slot, renderSlot } from './Slot';
|
|
2
|
+
export type { SlotProps } from './Slot';
|
|
3
|
+
export { SlotCluster } from './SlotCluster';
|
|
4
|
+
export type { SlotClusterProps } from './SlotCluster';
|
|
5
|
+
export { mergeSlots, appendSlots, sortSlots } from './mergeSlots';
|
|
6
|
+
export { SLOT_NAMES } from './types';
|
|
7
|
+
export type {
|
|
8
|
+
SlotName,
|
|
9
|
+
BuiltInSlotName,
|
|
10
|
+
SlotContext,
|
|
11
|
+
SlotRender,
|
|
12
|
+
SlotContribution,
|
|
13
|
+
SlotMap,
|
|
14
|
+
ResolvedSlots,
|
|
15
|
+
} from './types';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mergeSlots, appendSlots, sortSlots } from './mergeSlots';
|
|
3
|
+
import { mergePluginClients } from '../plugin';
|
|
4
|
+
import type { SlotContribution, SlotMap } from './types';
|
|
5
|
+
|
|
6
|
+
const make = (id: string, order?: number): SlotContribution => ({
|
|
7
|
+
id,
|
|
8
|
+
render: () => null,
|
|
9
|
+
order,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('mergeSlots', () => {
|
|
13
|
+
it('concatenates contributions from multiple maps targeting the same slot (no override)', () => {
|
|
14
|
+
const a: SlotMap = { 'header.right': make('a') };
|
|
15
|
+
const b: SlotMap = { 'header.right': make('b') };
|
|
16
|
+
|
|
17
|
+
const merged = mergeSlots([a, b]);
|
|
18
|
+
|
|
19
|
+
expect(merged['header.right'].map(c => c.id)).toEqual(['a', 'b']);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('accepts a single contribution or an array per slot', () => {
|
|
23
|
+
const merged = mergeSlots([{ 'sidebar.top': [make('x'), make('y')] }, { 'sidebar.top': make('z') }]);
|
|
24
|
+
expect(merged['sidebar.top'].map(c => c.id)).toEqual(['x', 'y', 'z']);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('sorts by order, keeping registration order on ties', () => {
|
|
28
|
+
const merged = mergeSlots([
|
|
29
|
+
{ 'table.toolbar': [make('first-default'), make('low', -10)] },
|
|
30
|
+
{ 'table.toolbar': [make('high', 10), make('second-default')] },
|
|
31
|
+
]);
|
|
32
|
+
expect(merged['table.toolbar'].map(c => c.id)).toEqual(['low', 'first-default', 'second-default', 'high']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('ignores undefined maps and empty values', () => {
|
|
36
|
+
const merged = mergeSlots([undefined, {}, { 'panel.footer': make('only') }]);
|
|
37
|
+
expect(Object.keys(merged)).toEqual(['panel.footer']);
|
|
38
|
+
expect(merged['panel.footer'].map(c => c.id)).toEqual(['only']);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('appendSlots / sortSlots', () => {
|
|
43
|
+
it('appendSlots mutates and returns the target', () => {
|
|
44
|
+
const target = {};
|
|
45
|
+
const result = appendSlots(target, { 'form.footer': make('a') });
|
|
46
|
+
expect(result).toBe(target);
|
|
47
|
+
expect(result['form.footer'].map(c => c.id)).toEqual(['a']);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('sortSlots is stable for equal orders', () => {
|
|
51
|
+
const resolved = { s: [make('a'), make('b'), make('c')] };
|
|
52
|
+
expect(sortSlots(resolved).s.map(c => c.id)).toEqual(['a', 'b', 'c']);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('mergePluginClients slot integration', () => {
|
|
57
|
+
it('stacks plugin slots in plugin order and appends the app manifest last', () => {
|
|
58
|
+
const pluginA = { name: 'a', slots: { 'header.right': make('from-a') } };
|
|
59
|
+
const pluginB = { name: 'b', slots: { 'header.right': make('from-b') } };
|
|
60
|
+
const appManifest = { name: 'app', slots: { 'header.right': make('from-app') } };
|
|
61
|
+
|
|
62
|
+
const merged = mergePluginClients([pluginA, pluginB, appManifest]);
|
|
63
|
+
|
|
64
|
+
expect(merged.slots['header.right'].map(c => c.id)).toEqual(['from-a', 'from-b', 'from-app']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('produces an empty slot map when no plugin contributes slots', () => {
|
|
68
|
+
const merged = mergePluginClients([{ name: 'a' }]);
|
|
69
|
+
expect(merged.slots).toEqual({});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SlotMap, ResolvedSlots, SlotContribution } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Append the contributions of one `SlotMap` onto an accumulator. Unlike the
|
|
5
|
+
* override-based component registries, slots are 1:many — every contribution is
|
|
6
|
+
* kept, preserving the order contributors were applied in.
|
|
7
|
+
*/
|
|
8
|
+
export function appendSlots(target: ResolvedSlots, slots: SlotMap = {}): ResolvedSlots {
|
|
9
|
+
for (const [name, value] of Object.entries(slots)) {
|
|
10
|
+
if (!value) continue;
|
|
11
|
+
const contributions = Array.isArray(value) ? value : [value];
|
|
12
|
+
(target[name] ??= []).push(...contributions);
|
|
13
|
+
}
|
|
14
|
+
return target;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stable-sort every slot's contributions by `order` (default 0). Equal orders
|
|
19
|
+
* keep registration order, since `Array.prototype.sort` is stable.
|
|
20
|
+
*/
|
|
21
|
+
export function sortSlots(resolved: ResolvedSlots): ResolvedSlots {
|
|
22
|
+
for (const name of Object.keys(resolved)) {
|
|
23
|
+
resolved[name] = [...resolved[name]].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Merge an ordered list of slot maps into a single resolved, sorted map.
|
|
30
|
+
* Earlier maps contribute first; the app map is appended last by the caller.
|
|
31
|
+
*/
|
|
32
|
+
export function mergeSlots(maps: Array<SlotMap | undefined>): ResolvedSlots {
|
|
33
|
+
const resolved: ResolvedSlots = {};
|
|
34
|
+
for (const map of maps) {
|
|
35
|
+
appendSlots(resolved, map);
|
|
36
|
+
}
|
|
37
|
+
return sortSlots(resolved);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type { SlotContribution };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { SLOT_NAMES } from './types';
|
|
3
|
+
|
|
4
|
+
describe('SLOT_NAMES', () => {
|
|
5
|
+
it('maps camelCase keys to dotted slot names with unique values', () => {
|
|
6
|
+
const values = Object.values(SLOT_NAMES);
|
|
7
|
+
expect(new Set(values).size).toBe(values.length);
|
|
8
|
+
expect(values.every(v => /^[a-z]+\.[A-Za-z]+$/.test(v))).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('includes the additional slots', () => {
|
|
12
|
+
expect(SLOT_NAMES.sidebarBrand).toBe('sidebar.brand');
|
|
13
|
+
expect(SLOT_NAMES.tableBulkActions).toBe('table.bulkActions');
|
|
14
|
+
expect(SLOT_NAMES.tableRowActions).toBe('table.rowActions');
|
|
15
|
+
expect(SLOT_NAMES.detailTabs).toBe('detail.tabs');
|
|
16
|
+
expect(SLOT_NAMES.widgetsAppend).toBe('widgets.append');
|
|
17
|
+
expect(SLOT_NAMES.modalHeaderActions).toBe('modal.headerActions');
|
|
18
|
+
expect(SLOT_NAMES.modalFooter).toBe('modal.footer');
|
|
19
|
+
expect(SLOT_NAMES.loginTop).toBe('login.top');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ReactNode, ComponentType } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in slot ids — the named injection points the framework renders in the
|
|
5
|
+
* panel chrome. Plugins may also use arbitrary strings to declare their own
|
|
6
|
+
* slots inside components they render.
|
|
7
|
+
*/
|
|
8
|
+
export type BuiltInSlotName =
|
|
9
|
+
| 'header.left'
|
|
10
|
+
| 'header.right'
|
|
11
|
+
| 'header.userMenu'
|
|
12
|
+
| 'sidebar.brand'
|
|
13
|
+
| 'sidebar.top'
|
|
14
|
+
| 'sidebar.bottom'
|
|
15
|
+
| 'panel.footer'
|
|
16
|
+
| 'table.toolbar'
|
|
17
|
+
| 'table.aboveTable'
|
|
18
|
+
| 'table.belowTable'
|
|
19
|
+
| 'table.bulkActions'
|
|
20
|
+
| 'table.rowActions'
|
|
21
|
+
| 'form.header'
|
|
22
|
+
| 'form.footer'
|
|
23
|
+
| 'detail.actions'
|
|
24
|
+
| 'detail.tabs'
|
|
25
|
+
| 'detail.afterDetails'
|
|
26
|
+
| 'page.top'
|
|
27
|
+
| 'page.bottom'
|
|
28
|
+
| 'widgets.append'
|
|
29
|
+
| 'modal.headerActions'
|
|
30
|
+
| 'modal.footer'
|
|
31
|
+
| 'login.top'
|
|
32
|
+
| 'login.belowForm';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A slot name. The built-in names give autocomplete; the `string & {}` arm keeps
|
|
36
|
+
* the union open so plugins can define and target their own slots.
|
|
37
|
+
*/
|
|
38
|
+
export type SlotName = BuiltInSlotName | (string & {});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Context passed to every slot render. The base shape is always present; each
|
|
42
|
+
* placement widens it with the data it can supply (e.g. the current record for
|
|
43
|
+
* detail/form slots, the resource slug for table slots).
|
|
44
|
+
*/
|
|
45
|
+
export interface SlotContext {
|
|
46
|
+
/** The slot being rendered. */
|
|
47
|
+
slot: SlotName;
|
|
48
|
+
/** Slug of the resource in scope, when the slot sits inside a resource view. */
|
|
49
|
+
resourceSlug?: string;
|
|
50
|
+
/** Resource/page schema in scope, when available. */
|
|
51
|
+
schema?: unknown;
|
|
52
|
+
/** The record in scope, for detail/form slots. */
|
|
53
|
+
record?: Record<string, unknown>;
|
|
54
|
+
/** Current router pathname. */
|
|
55
|
+
location?: string;
|
|
56
|
+
/** Authenticated user, for header/userMenu slots. Cast to your `AuthUser`. */
|
|
57
|
+
user?: unknown;
|
|
58
|
+
/** Slot-specific extras (e.g. `{ selectedIds }` for `table.bulkActions`, `{ rowId }` for `table.rowActions`). */
|
|
59
|
+
data?: Record<string, unknown>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A slot's renderable: either a React component that receives the slot context
|
|
64
|
+
* as props, or a plain function of the context returning a node.
|
|
65
|
+
*/
|
|
66
|
+
export type SlotRender<C extends SlotContext = SlotContext> = ComponentType<C> | ((ctx: C) => ReactNode);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A single contribution to a slot. Multiple contributions stack into one slot,
|
|
70
|
+
* unlike the override-based component registries.
|
|
71
|
+
*/
|
|
72
|
+
export interface SlotContribution<C extends SlotContext = SlotContext> {
|
|
73
|
+
/** Stable id — used as the React key and to dedupe across contributors. */
|
|
74
|
+
id: string;
|
|
75
|
+
/** Component or function that produces the slot content. */
|
|
76
|
+
render: SlotRender<C>;
|
|
77
|
+
/** Lower renders first. Defaults to 0; ties keep registration order. */
|
|
78
|
+
order?: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What a plugin/app contributes: a single contribution or an array per slot.
|
|
83
|
+
* Authored on `KratosPluginClient.slots` and `mountAdminPanel({ slots })`.
|
|
84
|
+
*/
|
|
85
|
+
export type SlotMap = Partial<Record<SlotName, SlotContribution | SlotContribution[]>>;
|
|
86
|
+
|
|
87
|
+
/** Internal resolved form: every slot maps to a sorted array of contributions. */
|
|
88
|
+
export type ResolvedSlots = Record<string, SlotContribution[]>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Built-in slot names as a const object, for autocomplete and refactor-safe
|
|
92
|
+
* references without losing the open `SlotName` string union.
|
|
93
|
+
*/
|
|
94
|
+
export const SLOT_NAMES = {
|
|
95
|
+
headerLeft: 'header.left',
|
|
96
|
+
headerRight: 'header.right',
|
|
97
|
+
headerUserMenu: 'header.userMenu',
|
|
98
|
+
sidebarBrand: 'sidebar.brand',
|
|
99
|
+
sidebarTop: 'sidebar.top',
|
|
100
|
+
sidebarBottom: 'sidebar.bottom',
|
|
101
|
+
panelFooter: 'panel.footer',
|
|
102
|
+
tableToolbar: 'table.toolbar',
|
|
103
|
+
tableAboveTable: 'table.aboveTable',
|
|
104
|
+
tableBelowTable: 'table.belowTable',
|
|
105
|
+
tableBulkActions: 'table.bulkActions',
|
|
106
|
+
tableRowActions: 'table.rowActions',
|
|
107
|
+
formHeader: 'form.header',
|
|
108
|
+
formFooter: 'form.footer',
|
|
109
|
+
detailActions: 'detail.actions',
|
|
110
|
+
detailTabs: 'detail.tabs',
|
|
111
|
+
detailAfterDetails: 'detail.afterDetails',
|
|
112
|
+
pageTop: 'page.top',
|
|
113
|
+
pageBottom: 'page.bottom',
|
|
114
|
+
widgetsAppend: 'widgets.append',
|
|
115
|
+
modalHeaderActions: 'modal.headerActions',
|
|
116
|
+
modalFooter: 'modal.footer',
|
|
117
|
+
loginTop: 'login.top',
|
|
118
|
+
loginBelowForm: 'login.belowForm',
|
|
119
|
+
} as const satisfies Record<string, BuiltInSlotName>;
|