@actuate-media/cms-admin 0.8.0 → 0.8.2
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/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +44 -42
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/search.test.js +10 -10
- package/dist/__tests__/lib/search.test.js.map +1 -1
- package/dist/__tests__/lib/utils.test.js.map +1 -1
- package/dist/__tests__/router/match-route.test.js.map +1 -1
- package/dist/__tests__/router/strip-base.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.js +2 -4
- package/dist/components/Breadcrumbs.js.map +1 -1
- package/dist/components/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette.js +7 -3
- package/dist/components/CommandPalette.js.map +1 -1
- package/dist/components/ContentOverviewChart.d.ts.map +1 -1
- package/dist/components/ContentOverviewChart.js.map +1 -1
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/ErrorBoundary.js.map +1 -1
- package/dist/components/FocalPointPicker.d.ts.map +1 -1
- package/dist/components/FocalPointPicker.js +4 -2
- package/dist/components/FocalPointPicker.js.map +1 -1
- package/dist/components/FolderTree.d.ts.map +1 -1
- package/dist/components/FolderTree.js +18 -10
- package/dist/components/FolderTree.js.map +1 -1
- package/dist/components/LivePreview.d.ts +1 -1
- package/dist/components/LivePreview.d.ts.map +1 -1
- package/dist/components/LivePreview.js +6 -2
- package/dist/components/LivePreview.js.map +1 -1
- package/dist/components/LocaleProvider.d.ts.map +1 -1
- package/dist/components/LocaleProvider.js.map +1 -1
- package/dist/components/LocaleSwitcher.d.ts.map +1 -1
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/LocaleSwitcher.js.map +1 -1
- package/dist/components/MediaPickerModal.d.ts.map +1 -1
- package/dist/components/MediaPickerModal.js.map +1 -1
- package/dist/components/PresenceIndicator.d.ts.map +1 -1
- package/dist/components/PresenceIndicator.js +5 -2
- package/dist/components/PresenceIndicator.js.map +1 -1
- package/dist/components/SEOPanel.d.ts +1 -1
- package/dist/components/SEOPanel.d.ts.map +1 -1
- package/dist/components/SEOPanel.js +110 -24
- package/dist/components/SEOPanel.js.map +1 -1
- package/dist/components/SEOPerformance.d.ts.map +1 -1
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SEOPerformance.js.map +1 -1
- package/dist/components/ThemeProvider.d.ts.map +1 -1
- package/dist/components/ThemeProvider.js.map +1 -1
- package/dist/components/TipTapEditor.d.ts.map +1 -1
- package/dist/components/TipTapEditor.js +5 -1
- package/dist/components/TipTapEditor.js.map +1 -1
- package/dist/components/VersionHistory.d.ts +1 -1
- package/dist/components/VersionHistory.d.ts.map +1 -1
- package/dist/components/VersionHistory.js +1 -1
- package/dist/components/VersionHistory.js.map +1 -1
- package/dist/components/ui/Avatar.d.ts.map +1 -1
- package/dist/components/ui/Avatar.js.map +1 -1
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/CommandPalette.d.ts.map +1 -1
- package/dist/components/ui/CommandPalette.js +8 -2
- package/dist/components/ui/CommandPalette.js.map +1 -1
- package/dist/components/ui/ConfirmDialog.d.ts.map +1 -1
- package/dist/components/ui/ConfirmDialog.js.map +1 -1
- package/dist/components/ui/DataTable.d.ts.map +1 -1
- package/dist/components/ui/DataTable.js +1 -3
- package/dist/components/ui/DataTable.js.map +1 -1
- package/dist/components/ui/EmptyState.d.ts.map +1 -1
- package/dist/components/ui/EmptyState.js +1 -1
- package/dist/components/ui/EmptyState.js.map +1 -1
- package/dist/components/ui/Modal.d.ts.map +1 -1
- package/dist/components/ui/Modal.js.map +1 -1
- package/dist/components/ui/Pagination.d.ts +1 -1
- package/dist/components/ui/Pagination.d.ts.map +1 -1
- package/dist/components/ui/Pagination.js +7 -2
- package/dist/components/ui/Pagination.js.map +1 -1
- package/dist/components/ui/SearchInput.d.ts.map +1 -1
- package/dist/components/ui/SearchInput.js.map +1 -1
- package/dist/components/ui/Skeleton.d.ts.map +1 -1
- package/dist/components/ui/Skeleton.js.map +1 -1
- package/dist/components/ui/Toast.d.ts.map +1 -1
- package/dist/components/ui/Toast.js.map +1 -1
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/ArrayField.d.ts.map +1 -1
- package/dist/fields/ArrayField.js +1 -1
- package/dist/fields/ArrayField.js.map +1 -1
- package/dist/fields/BlockBuilderField.d.ts.map +1 -1
- package/dist/fields/BlockBuilderField.js +7 -7
- package/dist/fields/BlockBuilderField.js.map +1 -1
- package/dist/fields/DateField.d.ts.map +1 -1
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/DateField.js.map +1 -1
- package/dist/fields/FieldRenderer.d.ts.map +1 -1
- package/dist/fields/FieldRenderer.js.map +1 -1
- package/dist/fields/GroupField.d.ts.map +1 -1
- package/dist/fields/GroupField.js +1 -1
- package/dist/fields/GroupField.js.map +1 -1
- package/dist/fields/MediaField.d.ts.map +1 -1
- package/dist/fields/MediaField.js +1 -1
- package/dist/fields/MediaField.js.map +1 -1
- package/dist/fields/NavBuilderField.d.ts.map +1 -1
- package/dist/fields/NavBuilderField.js +2 -5
- package/dist/fields/NavBuilderField.js.map +1 -1
- package/dist/fields/NumberField.d.ts +1 -1
- package/dist/fields/NumberField.d.ts.map +1 -1
- package/dist/fields/NumberField.js +2 -2
- package/dist/fields/NumberField.js.map +1 -1
- package/dist/fields/RelationshipField.d.ts.map +1 -1
- package/dist/fields/RelationshipField.js +7 -3
- package/dist/fields/RelationshipField.js.map +1 -1
- package/dist/fields/RichTextField.d.ts +1 -1
- package/dist/fields/RichTextField.d.ts.map +1 -1
- package/dist/fields/RichTextField.js +2 -2
- package/dist/fields/RichTextField.js.map +1 -1
- package/dist/fields/SelectField.d.ts.map +1 -1
- package/dist/fields/SelectField.js +9 -7
- package/dist/fields/SelectField.js.map +1 -1
- package/dist/fields/SlugField.d.ts.map +1 -1
- package/dist/fields/SlugField.js +1 -1
- package/dist/fields/SlugField.js.map +1 -1
- package/dist/fields/TextField.d.ts +1 -1
- package/dist/fields/TextField.d.ts.map +1 -1
- package/dist/fields/TextField.js +2 -2
- package/dist/fields/TextField.js.map +1 -1
- package/dist/fields/ToggleField.d.ts.map +1 -1
- package/dist/fields/ToggleField.js +1 -1
- package/dist/fields/ToggleField.js.map +1 -1
- package/dist/fields/block-types.d.ts.map +1 -1
- package/dist/fields/block-types.js +28 -8
- package/dist/fields/block-types.js.map +1 -1
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js.map +1 -1
- package/dist/hooks/useBuilderState.d.ts.map +1 -1
- package/dist/hooks/useBuilderState.js.map +1 -1
- package/dist/hooks/useContentLock.d.ts.map +1 -1
- package/dist/hooks/useContentLock.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -1
- package/dist/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.d.ts.map +1 -1
- package/dist/layout/Header.js.map +1 -1
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +5 -8
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +3 -5
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/useApiData.d.ts.map +1 -1
- package/dist/lib/useApiData.js.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1 -3
- package/dist/router/index.js.map +1 -1
- package/dist/views/CollectionList.d.ts.map +1 -1
- package/dist/views/CollectionList.js +56 -17
- package/dist/views/CollectionList.js.map +1 -1
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +26 -13
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.d.ts +1 -1
- package/dist/views/DocumentEdit.d.ts.map +1 -1
- package/dist/views/DocumentEdit.js +33 -15
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/ForgotPassword.d.ts.map +1 -1
- package/dist/views/ForgotPassword.js.map +1 -1
- package/dist/views/FormEditor.d.ts.map +1 -1
- package/dist/views/FormEditor.js +8 -2
- package/dist/views/FormEditor.js.map +1 -1
- package/dist/views/FormSubmissions.d.ts.map +1 -1
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/FormSubmissions.js.map +1 -1
- package/dist/views/Forms.d.ts.map +1 -1
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +5 -2
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +39 -19
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/PageEditor.d.ts.map +1 -1
- package/dist/views/PageEditor.js.map +1 -1
- package/dist/views/Pages.d.ts.map +1 -1
- package/dist/views/Pages.js +20 -10
- package/dist/views/Pages.js.map +1 -1
- package/dist/views/PostEditor.d.ts.map +1 -1
- package/dist/views/PostEditor.js.map +1 -1
- package/dist/views/Posts.d.ts.map +1 -1
- package/dist/views/Posts.js +13 -7
- package/dist/views/Posts.js.map +1 -1
- package/dist/views/Redirects.d.ts.map +1 -1
- package/dist/views/Redirects.js +17 -5
- package/dist/views/Redirects.js.map +1 -1
- package/dist/views/ResetPassword.d.ts.map +1 -1
- package/dist/views/ResetPassword.js.map +1 -1
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +40 -17
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/ScriptTagEditor.d.ts.map +1 -1
- package/dist/views/ScriptTagEditor.js +2 -1
- package/dist/views/ScriptTagEditor.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +38 -11
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.d.ts.map +1 -1
- package/dist/views/SetupWizard.js.map +1 -1
- package/dist/views/Users.d.ts.map +1 -1
- package/dist/views/Users.js +5 -3
- package/dist/views/Users.js.map +1 -1
- package/dist/views/page-builder/AIBlockAssist.d.ts.map +1 -1
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIBlockAssist.js.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +4 -1
- package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
- package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
- package/dist/views/page-builder/BlockEditor.js +1 -1
- package/dist/views/page-builder/BlockEditor.js.map +1 -1
- package/dist/views/page-builder/BlockPicker.d.ts.map +1 -1
- package/dist/views/page-builder/BlockPicker.js.map +1 -1
- package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
- package/dist/views/page-builder/BottomBar.js.map +1 -1
- package/dist/views/page-builder/BuilderToolbar.d.ts.map +1 -1
- package/dist/views/page-builder/BuilderToolbar.js.map +1 -1
- package/dist/views/page-builder/ContextPanel.d.ts.map +1 -1
- package/dist/views/page-builder/ContextPanel.js +4 -1
- package/dist/views/page-builder/ContextPanel.js.map +1 -1
- package/dist/views/page-builder/DesignScore.d.ts.map +1 -1
- package/dist/views/page-builder/DesignScore.js.map +1 -1
- package/dist/views/page-builder/NodeSettings.d.ts.map +1 -1
- package/dist/views/page-builder/NodeSettings.js +1 -1
- package/dist/views/page-builder/NodeSettings.js.map +1 -1
- package/dist/views/page-builder/PageBuilder.d.ts +1 -1
- package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
- package/dist/views/page-builder/PageBuilder.js +4 -2
- package/dist/views/page-builder/PageBuilder.js.map +1 -1
- package/dist/views/page-builder/PageSettings.d.ts.map +1 -1
- package/dist/views/page-builder/PageSettings.js.map +1 -1
- package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
- package/dist/views/page-builder/PageTemplates.js.map +1 -1
- package/dist/views/page-builder/SEOPanel.d.ts.map +1 -1
- package/dist/views/page-builder/SEOPanel.js +1 -3
- package/dist/views/page-builder/SEOPanel.js.map +1 -1
- package/dist/views/page-builder/SavedSections.d.ts.map +1 -1
- package/dist/views/page-builder/SavedSections.js +3 -7
- package/dist/views/page-builder/SavedSections.js.map +1 -1
- package/dist/views/page-builder/TemplatePicker.d.ts.map +1 -1
- package/dist/views/page-builder/TemplatePicker.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -5
- package/dist/views/page-builder/block-renderers/CodePreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +4 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +2 -2
- package/dist/views/page-builder/block-renderers/FormPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +1 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/TextPreview.js +2 -6
- package/dist/views/page-builder/block-renderers/TextPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.js +2 -5
- package/dist/views/page-builder/block-renderers/VideoPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/index.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/index.js.map +1 -1
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -5
- package/dist/views/page-builder/canvas/BlockRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js.map +1 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.js +1 -5
- package/dist/views/page-builder/canvas/ColumnRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.js +1 -5
- package/dist/views/page-builder/canvas/ContainerRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/RowRenderer.js +1 -5
- package/dist/views/page-builder/canvas/RowRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/SectionRenderer.js +1 -5
- package/dist/views/page-builder/canvas/SectionRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/index.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/index.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +263 -191
- package/src/__tests__/lib/search.test.ts +60 -69
- package/src/__tests__/lib/utils.test.ts +12 -12
- package/src/__tests__/router/match-route.test.ts +24 -26
- package/src/__tests__/router/strip-base.test.ts +15 -15
- package/src/components/Breadcrumbs.tsx +27 -24
- package/src/components/CommandPalette.tsx +115 -99
- package/src/components/ContentOverviewChart.tsx +19 -14
- package/src/components/ErrorBoundary.tsx +13 -13
- package/src/components/FocalPointPicker.tsx +31 -20
- package/src/components/FolderTree.tsx +172 -139
- package/src/components/LivePreview.tsx +68 -41
- package/src/components/LocaleProvider.tsx +26 -20
- package/src/components/LocaleSwitcher.tsx +9 -11
- package/src/components/MediaPickerModal.tsx +46 -45
- package/src/components/PresenceIndicator.tsx +30 -27
- package/src/components/SEOPanel.tsx +378 -228
- package/src/components/SEOPerformance.tsx +52 -30
- package/src/components/ThemeProvider.tsx +46 -46
- package/src/components/TipTapEditor.tsx +60 -64
- package/src/components/VersionHistory.tsx +63 -52
- package/src/components/ui/Avatar.tsx +8 -8
- package/src/components/ui/Badge.tsx +7 -5
- package/src/components/ui/Button.tsx +24 -13
- package/src/components/ui/CommandPalette.tsx +56 -42
- package/src/components/ui/ConfirmDialog.tsx +14 -14
- package/src/components/ui/DataTable.tsx +37 -39
- package/src/components/ui/EmptyState.tsx +9 -11
- package/src/components/ui/Modal.tsx +21 -15
- package/src/components/ui/Pagination.tsx +34 -19
- package/src/components/ui/SearchInput.tsx +17 -7
- package/src/components/ui/Skeleton.tsx +7 -7
- package/src/components/ui/Toast.tsx +29 -22
- package/src/components/ui/index.ts +24 -24
- package/src/fields/ArrayField.tsx +43 -25
- package/src/fields/BlockBuilderField.tsx +80 -99
- package/src/fields/DateField.tsx +20 -12
- package/src/fields/FieldRenderer.tsx +34 -34
- package/src/fields/GroupField.tsx +8 -10
- package/src/fields/MediaField.tsx +8 -10
- package/src/fields/NavBuilderField.tsx +24 -25
- package/src/fields/NumberField.tsx +21 -14
- package/src/fields/RelationshipField.tsx +105 -91
- package/src/fields/RichTextField.tsx +16 -12
- package/src/fields/SelectField.tsx +42 -34
- package/src/fields/SlugField.tsx +29 -17
- package/src/fields/TextField.tsx +24 -16
- package/src/fields/ToggleField.tsx +7 -9
- package/src/fields/block-types.ts +50 -24
- package/src/fields/index.ts +17 -17
- package/src/hooks/useBuilderState.ts +260 -221
- package/src/hooks/useContentLock.ts +23 -20
- package/src/hooks/useDebounce.ts +7 -7
- package/src/hooks/useKeyboardShortcuts.ts +16 -16
- package/src/index.ts +69 -58
- package/src/layout/Header.tsx +21 -20
- package/src/layout/Layout.tsx +22 -24
- package/src/layout/Sidebar.tsx +107 -72
- package/src/lib/api.ts +34 -34
- package/src/lib/search.ts +30 -34
- package/src/lib/useApiData.ts +65 -62
- package/src/lib/utils.ts +3 -3
- package/src/router/index.ts +33 -35
- package/src/styles/build-input.css +2 -2
- package/src/styles/tailwind.css +1 -1
- package/src/styles/theme.css +26 -2
- package/src/views/CollectionList.tsx +275 -121
- package/src/views/Dashboard.tsx +164 -117
- package/src/views/DocumentEdit.tsx +298 -253
- package/src/views/ForgotPassword.tsx +27 -23
- package/src/views/FormEditor.tsx +165 -99
- package/src/views/FormSubmissions.tsx +261 -117
- package/src/views/Forms.tsx +56 -26
- package/src/views/Login.tsx +107 -84
- package/src/views/MediaBrowser.tsx +717 -523
- package/src/views/PageEditor.tsx +44 -46
- package/src/views/Pages.tsx +312 -149
- package/src/views/PostEditor.tsx +57 -51
- package/src/views/Posts.tsx +206 -74
- package/src/views/Redirects.tsx +173 -117
- package/src/views/ResetPassword.tsx +43 -32
- package/src/views/SEO.tsx +607 -160
- package/src/views/ScriptTagEditor.tsx +69 -69
- package/src/views/ScriptTags.tsx +54 -42
- package/src/views/Settings.tsx +430 -220
- package/src/views/SetupWizard.tsx +69 -46
- package/src/views/Users.tsx +154 -120
- package/src/views/page-builder/AIBlockAssist.tsx +21 -25
- package/src/views/page-builder/AIGenerateDialog.tsx +134 -127
- package/src/views/page-builder/BlockEditor.tsx +94 -96
- package/src/views/page-builder/BlockPicker.tsx +73 -88
- package/src/views/page-builder/BottomBar.tsx +15 -11
- package/src/views/page-builder/BuilderToolbar.tsx +32 -29
- package/src/views/page-builder/ContextPanel.tsx +57 -57
- package/src/views/page-builder/DesignScore.tsx +52 -59
- package/src/views/page-builder/NodeSettings.tsx +59 -59
- package/src/views/page-builder/PageBuilder.tsx +156 -155
- package/src/views/page-builder/PageSettings.tsx +16 -15
- package/src/views/page-builder/PageTemplates.tsx +23 -17
- package/src/views/page-builder/SEOPanel.tsx +90 -111
- package/src/views/page-builder/SavedSections.tsx +99 -105
- package/src/views/page-builder/TemplatePicker.tsx +44 -48
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +11 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +13 -15
- package/src/views/page-builder/block-renderers/CodePreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +20 -23
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/FormPreview.tsx +9 -13
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +22 -28
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +17 -30
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +12 -12
- package/src/views/page-builder/block-renderers/TextPreview.tsx +22 -22
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +13 -18
- package/src/views/page-builder/block-renderers/index.ts +17 -17
- package/src/views/page-builder/canvas/BlockRenderer.tsx +19 -23
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +17 -20
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +22 -26
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +20 -24
- package/src/views/page-builder/canvas/RowRenderer.tsx +19 -23
- package/src/views/page-builder/canvas/SectionRenderer.tsx +30 -34
- package/src/views/page-builder/canvas/index.ts +2 -2
package/src/lib/api.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
const DEFAULT_BASE = '/api/cms'
|
|
1
|
+
const DEFAULT_BASE = '/api/cms'
|
|
2
2
|
|
|
3
|
-
let basePath = DEFAULT_BASE
|
|
3
|
+
let basePath = DEFAULT_BASE
|
|
4
4
|
|
|
5
5
|
export function setApiBase(path: string) {
|
|
6
|
-
basePath = path
|
|
6
|
+
basePath = path
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function getCsrfToken(): string {
|
|
10
|
-
if (typeof document === 'undefined') return ''
|
|
11
|
-
const match = document.cookie.match(/(?:^|;\s*)actuate_csrf=([^;]*)/)
|
|
12
|
-
return match?.[1] ?? ''
|
|
10
|
+
if (typeof document === 'undefined') return ''
|
|
11
|
+
const match = document.cookie.match(/(?:^|;\s*)actuate_csrf=([^;]*)/)
|
|
12
|
+
return match?.[1] ?? ''
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -21,52 +21,52 @@ function getCsrfToken(): string {
|
|
|
21
21
|
* `pendingFetch` cache so the admin doesn't fire N parallel `/auth/csrf`
|
|
22
22
|
* requests when a page lights up after navigation.
|
|
23
23
|
*/
|
|
24
|
-
let pendingFetch: Promise<void> | null = null
|
|
24
|
+
let pendingFetch: Promise<void> | null = null
|
|
25
25
|
|
|
26
26
|
export async function ensureCsrfToken(): Promise<void> {
|
|
27
|
-
if (getCsrfToken()) return
|
|
28
|
-
if (pendingFetch) return pendingFetch
|
|
27
|
+
if (getCsrfToken()) return
|
|
28
|
+
if (pendingFetch) return pendingFetch
|
|
29
29
|
pendingFetch = (async () => {
|
|
30
30
|
try {
|
|
31
|
-
await fetch(`${basePath}/auth/csrf`, { credentials: 'include' })
|
|
31
|
+
await fetch(`${basePath}/auth/csrf`, { credentials: 'include' })
|
|
32
32
|
} catch {
|
|
33
33
|
// best-effort — caller will see the 403 from the actual mutation if
|
|
34
34
|
// the cookie still isn't set, instead of a silent network failure.
|
|
35
35
|
} finally {
|
|
36
|
-
pendingFetch = null
|
|
36
|
+
pendingFetch = null
|
|
37
37
|
}
|
|
38
|
-
})()
|
|
39
|
-
return pendingFetch
|
|
38
|
+
})()
|
|
39
|
+
return pendingFetch
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function getActiveLocale(): string | null {
|
|
43
|
-
if (typeof localStorage === 'undefined') return null
|
|
44
|
-
return localStorage.getItem('actuate-locale')
|
|
43
|
+
if (typeof localStorage === 'undefined') return null
|
|
44
|
+
return localStorage.getItem('actuate-locale')
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function appendLocaleParam(endpoint: string): string {
|
|
48
|
-
const locale = getActiveLocale()
|
|
49
|
-
if (!locale) return endpoint
|
|
50
|
-
const separator = endpoint.includes('?') ? '&' : '?'
|
|
51
|
-
return `${endpoint}${separator}locale=${encodeURIComponent(locale)}
|
|
48
|
+
const locale = getActiveLocale()
|
|
49
|
+
if (!locale) return endpoint
|
|
50
|
+
const separator = endpoint.includes('?') ? '&' : '?'
|
|
51
|
+
return `${endpoint}${separator}locale=${encodeURIComponent(locale)}`
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
export async function cmsApi<T = unknown>(
|
|
55
55
|
endpoint: string,
|
|
56
|
-
options: RequestInit = {}
|
|
56
|
+
options: RequestInit = {},
|
|
57
57
|
): Promise<{ data?: T; error?: string; status: number }> {
|
|
58
|
-
const url = `${basePath}${appendLocaleParam(endpoint)}
|
|
59
|
-
const isFormData = typeof FormData !== 'undefined' && options.body instanceof FormData
|
|
58
|
+
const url = `${basePath}${appendLocaleParam(endpoint)}`
|
|
59
|
+
const isFormData = typeof FormData !== 'undefined' && options.body instanceof FormData
|
|
60
60
|
|
|
61
61
|
const headers: Record<string, string> = {
|
|
62
|
-
...(options.headers as Record<string, string> || {}),
|
|
63
|
-
}
|
|
62
|
+
...((options.headers as Record<string, string>) || {}),
|
|
63
|
+
}
|
|
64
64
|
|
|
65
65
|
if (!isFormData) {
|
|
66
|
-
headers['Content-Type'] = 'application/json'
|
|
66
|
+
headers['Content-Type'] = 'application/json'
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const method = (options.method ?? 'GET').toUpperCase()
|
|
69
|
+
const method = (options.method ?? 'GET').toUpperCase()
|
|
70
70
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
71
71
|
// Bootstrap the CSRF cookie lazily — covers consumers that sign users
|
|
72
72
|
// in via a custom Server Action (the Next.js App Router pattern) where
|
|
@@ -74,22 +74,22 @@ export async function cmsApi<T = unknown>(
|
|
|
74
74
|
// cookie from the login response. Without this, the very first mutation
|
|
75
75
|
// after sign-in 403s.
|
|
76
76
|
if (!getCsrfToken()) {
|
|
77
|
-
await ensureCsrfToken()
|
|
77
|
+
await ensureCsrfToken()
|
|
78
78
|
}
|
|
79
|
-
const csrf = getCsrfToken()
|
|
80
|
-
if (csrf) headers['x-csrf-token'] = csrf
|
|
79
|
+
const csrf = getCsrfToken()
|
|
80
|
+
if (csrf) headers['x-csrf-token'] = csrf
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
try {
|
|
84
|
-
const res = await fetch(url, { ...options, headers, credentials: 'include' })
|
|
85
|
-
const json = await res.json().catch(() => ({}))
|
|
84
|
+
const res = await fetch(url, { ...options, headers, credentials: 'include' })
|
|
85
|
+
const json = await res.json().catch(() => ({}))
|
|
86
86
|
|
|
87
87
|
if (!res.ok) {
|
|
88
|
-
return { error: json.error || `Request failed (${res.status})`, status: res.status }
|
|
88
|
+
return { error: json.error || `Request failed (${res.status})`, status: res.status }
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
return { data: json.data ?? json, status: res.status }
|
|
91
|
+
return { data: json.data ?? json, status: res.status }
|
|
92
92
|
} catch (err) {
|
|
93
|
-
return { error: err instanceof Error ? err.message : 'Network error', status: 0 }
|
|
93
|
+
return { error: err instanceof Error ? err.message : 'Network error', status: 0 }
|
|
94
94
|
}
|
|
95
95
|
}
|
package/src/lib/search.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
type SortDirection = 'asc' | 'desc'
|
|
1
|
+
type SortDirection = 'asc' | 'desc'
|
|
2
2
|
|
|
3
3
|
export interface SortConfig<K extends string = string> {
|
|
4
|
-
key: K
|
|
5
|
-
direction: SortDirection
|
|
4
|
+
key: K
|
|
5
|
+
direction: SortDirection
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function toSearchText(value: unknown): string {
|
|
9
|
-
return String(value ?? '')
|
|
9
|
+
return String(value ?? '')
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function scoreRelevance(text: unknown, query: unknown): number {
|
|
13
|
-
if (!query) return 0
|
|
14
|
-
const lower = toSearchText(text).toLowerCase()
|
|
15
|
-
const q = toSearchText(query).toLowerCase()
|
|
16
|
-
if (lower === q) return 100
|
|
17
|
-
if (lower.startsWith(q)) return 80
|
|
18
|
-
const wordBoundary = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)
|
|
19
|
-
if (wordBoundary.test(lower)) return 60
|
|
20
|
-
if (lower.includes(q)) return 40
|
|
21
|
-
return 0
|
|
13
|
+
if (!query) return 0
|
|
14
|
+
const lower = toSearchText(text).toLowerCase()
|
|
15
|
+
const q = toSearchText(query).toLowerCase()
|
|
16
|
+
if (lower === q) return 100
|
|
17
|
+
if (lower.startsWith(q)) return 80
|
|
18
|
+
const wordBoundary = new RegExp(`\\b${q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)
|
|
19
|
+
if (wordBoundary.test(lower)) return 60
|
|
20
|
+
if (lower.includes(q)) return 40
|
|
21
|
+
return 0
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function sortByRelevance<T>(
|
|
@@ -26,12 +26,12 @@ export function sortByRelevance<T>(
|
|
|
26
26
|
query: string,
|
|
27
27
|
getSearchFields: (item: T) => unknown[],
|
|
28
28
|
): T[] {
|
|
29
|
-
if (!query.trim()) return items
|
|
29
|
+
if (!query.trim()) return items
|
|
30
30
|
return [...items].sort((a, b) => {
|
|
31
|
-
const scoreA = Math.max(...getSearchFields(a).map(f => scoreRelevance(f, query)))
|
|
32
|
-
const scoreB = Math.max(...getSearchFields(b).map(f => scoreRelevance(f, query)))
|
|
33
|
-
return scoreB - scoreA
|
|
34
|
-
})
|
|
31
|
+
const scoreA = Math.max(...getSearchFields(a).map((f) => scoreRelevance(f, query)))
|
|
32
|
+
const scoreB = Math.max(...getSearchFields(b).map((f) => scoreRelevance(f, query)))
|
|
33
|
+
return scoreB - scoreA
|
|
34
|
+
})
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export function sortByColumn<T>(
|
|
@@ -39,25 +39,21 @@ export function sortByColumn<T>(
|
|
|
39
39
|
sortConfig: SortConfig | null,
|
|
40
40
|
getValue: (item: T, key: string) => string | number,
|
|
41
41
|
): T[] {
|
|
42
|
-
if (!sortConfig) return items
|
|
42
|
+
if (!sortConfig) return items
|
|
43
43
|
return [...items].sort((a, b) => {
|
|
44
|
-
const aVal = getValue(a, sortConfig.key)
|
|
45
|
-
const bVal = getValue(b, sortConfig.key)
|
|
46
|
-
const cmp =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
const aVal = getValue(a, sortConfig.key)
|
|
45
|
+
const bVal = getValue(b, sortConfig.key)
|
|
46
|
+
const cmp =
|
|
47
|
+
typeof aVal === 'number' && typeof bVal === 'number'
|
|
48
|
+
? aVal - bVal
|
|
49
|
+
: String(aVal).localeCompare(String(bVal))
|
|
50
|
+
return sortConfig.direction === 'asc' ? cmp : -cmp
|
|
51
|
+
})
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export function toggleSort<K extends string>(
|
|
54
|
-
current: SortConfig<K> | null,
|
|
55
|
-
key: K,
|
|
56
|
-
): SortConfig<K> {
|
|
54
|
+
export function toggleSort<K extends string>(current: SortConfig<K> | null, key: K): SortConfig<K> {
|
|
57
55
|
if (current?.key === key) {
|
|
58
|
-
return current.direction === 'asc'
|
|
59
|
-
? { key, direction: 'desc' }
|
|
60
|
-
: { key, direction: 'asc' };
|
|
56
|
+
return current.direction === 'asc' ? { key, direction: 'desc' } : { key, direction: 'asc' }
|
|
61
57
|
}
|
|
62
|
-
return { key, direction: 'asc' }
|
|
58
|
+
return { key, direction: 'asc' }
|
|
63
59
|
}
|
package/src/lib/useApiData.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import { useState, useEffect, useCallback, useRef } from 'react'
|
|
3
|
-
import { cmsApi } from './api.js'
|
|
1
|
+
'use client'
|
|
2
|
+
import { useState, useEffect, useCallback, useRef } from 'react'
|
|
3
|
+
import { cmsApi } from './api.js'
|
|
4
4
|
|
|
5
5
|
export interface UseApiDataOptions {
|
|
6
|
-
maxRetries?: number
|
|
6
|
+
maxRetries?: number
|
|
7
7
|
/** Base delay in ms for exponential backoff (default 1000). Actual delay = base * 2^attempt. */
|
|
8
|
-
retryBaseMs?: number
|
|
8
|
+
retryBaseMs?: number
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface UseApiDataResult<T> {
|
|
12
|
-
data: T | null
|
|
13
|
-
loading: boolean
|
|
14
|
-
error: string | null
|
|
12
|
+
data: T | null
|
|
13
|
+
loading: boolean
|
|
14
|
+
error: string | null
|
|
15
15
|
/** True when all retries are exhausted and the request permanently failed. */
|
|
16
|
-
exhausted: boolean
|
|
17
|
-
refetch: () => void
|
|
16
|
+
exhausted: boolean
|
|
17
|
+
refetch: () => void
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export function useApiData<T>(
|
|
@@ -22,74 +22,77 @@ export function useApiData<T>(
|
|
|
22
22
|
defaultValue: T | null = null,
|
|
23
23
|
options: UseApiDataOptions = {},
|
|
24
24
|
): UseApiDataResult<T> {
|
|
25
|
-
const { maxRetries = 3, retryBaseMs = 1000 } = options
|
|
26
|
-
const [data, setData] = useState<T | null>(defaultValue)
|
|
27
|
-
const [loading, setLoading] = useState(endpoint !== null)
|
|
28
|
-
const [error, setError] = useState<string | null>(null)
|
|
29
|
-
const [exhausted, setExhausted] = useState(false)
|
|
30
|
-
const abortRef = useRef<AbortController | null>(null)
|
|
31
|
-
const retryCountRef = useRef(0)
|
|
32
|
-
const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
25
|
+
const { maxRetries = 3, retryBaseMs = 1000 } = options
|
|
26
|
+
const [data, setData] = useState<T | null>(defaultValue)
|
|
27
|
+
const [loading, setLoading] = useState(endpoint !== null)
|
|
28
|
+
const [error, setError] = useState<string | null>(null)
|
|
29
|
+
const [exhausted, setExhausted] = useState(false)
|
|
30
|
+
const abortRef = useRef<AbortController | null>(null)
|
|
31
|
+
const retryCountRef = useRef(0)
|
|
32
|
+
const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
33
33
|
|
|
34
|
-
const fetchData = useCallback(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const fetchData = useCallback(
|
|
35
|
+
async (isRetry = false) => {
|
|
36
|
+
if (!endpoint) {
|
|
37
|
+
setLoading(false)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
abortRef.current?.abort()
|
|
42
|
+
const controller = new AbortController()
|
|
43
|
+
abortRef.current = controller
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (!isRetry) {
|
|
46
|
+
retryCountRef.current = 0
|
|
47
|
+
setExhausted(false)
|
|
48
|
+
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
setLoading(true)
|
|
51
|
+
setError(null)
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
const res = await cmsApi<T>(endpoint, { signal: controller.signal })
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
if (controller.signal.aborted) return
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
if (res.error) {
|
|
58
|
+
if (retryCountRef.current < maxRetries) {
|
|
59
|
+
const delay = retryBaseMs * Math.pow(2, retryCountRef.current)
|
|
60
|
+
retryCountRef.current += 1
|
|
61
|
+
retryTimerRef.current = setTimeout(() => fetchData(true), delay)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
setError(res.error)
|
|
65
|
+
setExhausted(true)
|
|
66
|
+
setLoading(false)
|
|
67
|
+
} else {
|
|
68
|
+
setData(res.data ?? null)
|
|
69
|
+
setLoading(false)
|
|
70
|
+
retryCountRef.current = 0
|
|
62
71
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} else {
|
|
67
|
-
setData(res.data ?? null);
|
|
68
|
-
setLoading(false);
|
|
69
|
-
retryCountRef.current = 0;
|
|
70
|
-
}
|
|
71
|
-
}, [endpoint, maxRetries, retryBaseMs]);
|
|
72
|
+
},
|
|
73
|
+
[endpoint, maxRetries, retryBaseMs],
|
|
74
|
+
)
|
|
72
75
|
|
|
73
76
|
const manualRefetch = useCallback(() => {
|
|
74
77
|
if (retryTimerRef.current) {
|
|
75
|
-
clearTimeout(retryTimerRef.current)
|
|
76
|
-
retryTimerRef.current = null
|
|
78
|
+
clearTimeout(retryTimerRef.current)
|
|
79
|
+
retryTimerRef.current = null
|
|
77
80
|
}
|
|
78
|
-
retryCountRef.current = 0
|
|
79
|
-
setExhausted(false)
|
|
80
|
-
fetchData(false)
|
|
81
|
-
}, [fetchData])
|
|
81
|
+
retryCountRef.current = 0
|
|
82
|
+
setExhausted(false)
|
|
83
|
+
fetchData(false)
|
|
84
|
+
}, [fetchData])
|
|
82
85
|
|
|
83
86
|
useEffect(() => {
|
|
84
|
-
fetchData(false)
|
|
87
|
+
fetchData(false)
|
|
85
88
|
return () => {
|
|
86
|
-
abortRef.current?.abort()
|
|
89
|
+
abortRef.current?.abort()
|
|
87
90
|
if (retryTimerRef.current) {
|
|
88
|
-
clearTimeout(retryTimerRef.current)
|
|
89
|
-
retryTimerRef.current = null
|
|
91
|
+
clearTimeout(retryTimerRef.current)
|
|
92
|
+
retryTimerRef.current = null
|
|
90
93
|
}
|
|
91
|
-
}
|
|
92
|
-
}, [fetchData])
|
|
94
|
+
}
|
|
95
|
+
}, [fetchData])
|
|
93
96
|
|
|
94
|
-
return { data, loading, error, exhausted, refetch: manualRefetch }
|
|
97
|
+
return { data, loading, error, exhausted, refetch: manualRefetch }
|
|
95
98
|
}
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
-
import { twMerge } from 'tailwind-merge'
|
|
1
|
+
import { clsx, type ClassValue } from 'clsx'
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
3
|
|
|
4
4
|
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs))
|
|
5
|
+
return twMerge(clsx(inputs))
|
|
6
6
|
}
|
package/src/router/index.ts
CHANGED
|
@@ -1,81 +1,79 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useCallback, useEffect, useRef } from 'react'
|
|
3
|
+
import { useState, useCallback, useEffect, useRef } from 'react'
|
|
4
4
|
|
|
5
5
|
interface RouteParams {
|
|
6
|
-
[key: string]: string
|
|
6
|
+
[key: string]: string
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function stripBase(pathname: string, basePath: string): string {
|
|
10
10
|
if (pathname.startsWith(basePath)) {
|
|
11
|
-
const rest = pathname.slice(basePath.length)
|
|
12
|
-
return rest === '' || rest === '/' ? '/' : rest.startsWith('/') ? rest : `/${rest}
|
|
11
|
+
const rest = pathname.slice(basePath.length)
|
|
12
|
+
return rest === '' || rest === '/' ? '/' : rest.startsWith('/') ? rest : `/${rest}`
|
|
13
13
|
}
|
|
14
|
-
return '/'
|
|
14
|
+
return '/'
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function useAdminRouter(basePath = '/admin', serverPath = '/') {
|
|
18
|
-
const [currentPath, setCurrentPath] = useState(serverPath)
|
|
19
|
-
const baseRef = useRef(basePath)
|
|
20
|
-
baseRef.current = basePath
|
|
18
|
+
const [currentPath, setCurrentPath] = useState(serverPath)
|
|
19
|
+
const baseRef = useRef(basePath)
|
|
20
|
+
baseRef.current = basePath
|
|
21
21
|
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
if (typeof window !== 'undefined') {
|
|
24
|
-
const browserPath = stripBase(window.location.pathname, basePath)
|
|
24
|
+
const browserPath = stripBase(window.location.pathname, basePath)
|
|
25
25
|
if (browserPath !== currentPath) {
|
|
26
|
-
setCurrentPath(browserPath)
|
|
26
|
+
setCurrentPath(browserPath)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
}, [])
|
|
29
|
+
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
function onPopState() {
|
|
33
|
-
setCurrentPath(stripBase(window.location.pathname, baseRef.current))
|
|
33
|
+
setCurrentPath(stripBase(window.location.pathname, baseRef.current))
|
|
34
34
|
}
|
|
35
|
-
window.addEventListener('popstate', onPopState)
|
|
36
|
-
return () => window.removeEventListener('popstate', onPopState)
|
|
37
|
-
}, [])
|
|
35
|
+
window.addEventListener('popstate', onPopState)
|
|
36
|
+
return () => window.removeEventListener('popstate', onPopState)
|
|
37
|
+
}, [])
|
|
38
38
|
|
|
39
39
|
const navigate = useCallback((path: string) => {
|
|
40
|
-
const normalizedPath = path.startsWith('/') ? path : `/${path}
|
|
41
|
-
setCurrentPath(normalizedPath)
|
|
40
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
|
41
|
+
setCurrentPath(normalizedPath)
|
|
42
42
|
|
|
43
|
-
const fullUrl = normalizedPath === '/'
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
window.history.pushState({ adminPath: normalizedPath }, '', fullUrl);
|
|
47
|
-
}, []);
|
|
43
|
+
const fullUrl = normalizedPath === '/' ? baseRef.current : `${baseRef.current}${normalizedPath}`
|
|
44
|
+
window.history.pushState({ adminPath: normalizedPath }, '', fullUrl)
|
|
45
|
+
}, [])
|
|
48
46
|
|
|
49
47
|
const matchRoute = useCallback(
|
|
50
48
|
(pattern: string): RouteParams | null => {
|
|
51
|
-
const patternParts = pattern.split('/').filter(Boolean)
|
|
52
|
-
const pathParts = currentPath.split('/').filter(Boolean)
|
|
49
|
+
const patternParts = pattern.split('/').filter(Boolean)
|
|
50
|
+
const pathParts = currentPath.split('/').filter(Boolean)
|
|
53
51
|
|
|
54
52
|
if (pattern === '/' && pathParts.length === 0) {
|
|
55
|
-
return {}
|
|
53
|
+
return {}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
if (patternParts.length !== pathParts.length) {
|
|
59
|
-
return null
|
|
57
|
+
return null
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
const params: RouteParams = {}
|
|
60
|
+
const params: RouteParams = {}
|
|
63
61
|
|
|
64
62
|
for (let i = 0; i < patternParts.length; i++) {
|
|
65
|
-
const patternPart = patternParts[i]
|
|
66
|
-
const pathPart = pathParts[i]
|
|
63
|
+
const patternPart = patternParts[i]!
|
|
64
|
+
const pathPart = pathParts[i]!
|
|
67
65
|
|
|
68
66
|
if (patternPart.startsWith(':')) {
|
|
69
|
-
params[patternPart.slice(1)] = pathPart
|
|
67
|
+
params[patternPart.slice(1)] = pathPart
|
|
70
68
|
} else if (patternPart !== pathPart) {
|
|
71
|
-
return null
|
|
69
|
+
return null
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
return params
|
|
73
|
+
return params
|
|
76
74
|
},
|
|
77
75
|
[currentPath],
|
|
78
|
-
)
|
|
76
|
+
)
|
|
79
77
|
|
|
80
|
-
return { currentPath, navigate, matchRoute }
|
|
78
|
+
return { currentPath, navigate, matchRoute }
|
|
81
79
|
}
|
package/src/styles/tailwind.css
CHANGED
package/src/styles/theme.css
CHANGED
|
@@ -129,11 +129,35 @@
|
|
|
129
129
|
|
|
130
130
|
.actuate-admin {
|
|
131
131
|
@apply bg-background text-foreground;
|
|
132
|
-
font-family:
|
|
132
|
+
font-family:
|
|
133
|
+
system-ui,
|
|
134
|
+
-apple-system,
|
|
135
|
+
BlinkMacSystemFont,
|
|
136
|
+
'Segoe UI',
|
|
137
|
+
Roboto,
|
|
138
|
+
sans-serif !important;
|
|
133
139
|
font-size: var(--font-size);
|
|
134
140
|
line-height: 1.5;
|
|
135
141
|
isolation: isolate;
|
|
136
|
-
|
|
142
|
+
/*
|
|
143
|
+
* Use `contain: style` only — NOT `contain: layout`.
|
|
144
|
+
*
|
|
145
|
+
* `contain: layout` would make every `.actuate-admin` element a
|
|
146
|
+
* *containing block* for `position: fixed` descendants. That
|
|
147
|
+
* silently breaks <AdminRoot>'s own fixed overlay (`position: fixed;
|
|
148
|
+
* inset: 0`) whenever a consumer wraps AdminRoot in their own
|
|
149
|
+
* `<div className="actuate-admin">` for tailwind isolation — the
|
|
150
|
+
* inner fixed div resolves relative to the wrapper instead of the
|
|
151
|
+
* viewport, and if the wrapper is 0-height (because all its
|
|
152
|
+
* children are out-of-flow), every admin element renders offscreen
|
|
153
|
+
* while still reporting non-zero computed dimensions. The visible
|
|
154
|
+
* symptom is `<body> intercepts pointer events` on every click in
|
|
155
|
+
* Playwright; the user-visible symptom is a blank-looking admin.
|
|
156
|
+
*
|
|
157
|
+
* `contain: style` keeps the style-scoping benefit (e.g. counters
|
|
158
|
+
* don't leak out) without the positioning footgun.
|
|
159
|
+
*/
|
|
160
|
+
contain: style;
|
|
137
161
|
-webkit-font-smoothing: antialiased;
|
|
138
162
|
-moz-osx-font-smoothing: grayscale;
|
|
139
163
|
}
|