@jhits/plugin-blog 0.0.9 → 0.0.11
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/package.json +58 -59
- package/src/api/categories.ts +0 -43
- package/src/api/check-title.ts +0 -60
- package/src/api/config-handler.ts +0 -76
- package/src/api/handler.ts +0 -418
- package/src/api/index.ts +0 -33
- package/src/api/route.ts +0 -116
- package/src/api/router.ts +0 -128
- package/src/api-server.ts +0 -11
- package/src/config.ts +0 -161
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.d.ts.map +0 -1
- package/src/hooks/index.js +0 -7
- package/src/hooks/index.ts +0 -9
- package/src/hooks/useBlog.d.ts +0 -31
- package/src/hooks/useBlog.d.ts.map +0 -1
- package/src/hooks/useBlog.js +0 -57
- package/src/hooks/useBlog.ts +0 -85
- package/src/hooks/useBlogs.d.ts +0 -39
- package/src/hooks/useBlogs.d.ts.map +0 -1
- package/src/hooks/useBlogs.js +0 -82
- package/src/hooks/useBlogs.ts +0 -123
- package/src/hooks/useCategories.d.ts +0 -9
- package/src/hooks/useCategories.d.ts.map +0 -1
- package/src/hooks/useCategories.js +0 -70
- package/src/hooks/useCategories.ts +0 -76
- package/src/index.d.ts +0 -55
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -228
- package/src/index.server.ts +0 -14
- package/src/index.tsx +0 -335
- package/src/init.d.ts +0 -40
- package/src/init.d.ts.map +0 -1
- package/src/init.js +0 -41
- package/src/init.tsx +0 -63
- package/src/lib/blocks/BlockRenderer.d.ts +0 -54
- package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
- package/src/lib/blocks/BlockRenderer.js +0 -54
- package/src/lib/blocks/BlockRenderer.tsx +0 -141
- package/src/lib/blocks/index.ts +0 -6
- package/src/lib/config-storage.d.ts +0 -30
- package/src/lib/config-storage.d.ts.map +0 -1
- package/src/lib/config-storage.js +0 -31
- package/src/lib/config-storage.ts +0 -65
- package/src/lib/index.ts +0 -9
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
- package/src/lib/layouts/blocks/ColumnsBlock.js +0 -182
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +0 -298
- package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +0 -81
- package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
- package/src/lib/layouts/blocks/SectionBlock.js +0 -44
- package/src/lib/layouts/blocks/SectionBlock.tsx +0 -104
- package/src/lib/layouts/blocks/index.ts +0 -8
- package/src/lib/layouts/index.d.ts +0 -23
- package/src/lib/layouts/index.d.ts.map +0 -1
- package/src/lib/layouts/index.js +0 -45
- package/src/lib/layouts/index.ts +0 -52
- package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
- package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
- package/src/lib/layouts/registerLayoutBlocks.js +0 -60
- package/src/lib/layouts/registerLayoutBlocks.ts +0 -64
- package/src/lib/mappers/apiMapper.d.ts +0 -66
- package/src/lib/mappers/apiMapper.d.ts.map +0 -1
- package/src/lib/mappers/apiMapper.js +0 -191
- package/src/lib/mappers/apiMapper.ts +0 -254
- package/src/lib/migration/index.ts +0 -6
- package/src/lib/migration/mapper.ts +0 -140
- package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
- package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextEditor.js +0 -564
- package/src/lib/rich-text/RichTextEditor.tsx +0 -826
- package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
- package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextPreview.js +0 -144
- package/src/lib/rich-text/RichTextPreview.tsx +0 -210
- package/src/lib/rich-text/index.d.ts +0 -9
- package/src/lib/rich-text/index.d.ts.map +0 -1
- package/src/lib/rich-text/index.js +0 -6
- package/src/lib/rich-text/index.ts +0 -10
- package/src/lib/utils/blockHelpers.d.ts +0 -23
- package/src/lib/utils/blockHelpers.d.ts.map +0 -1
- package/src/lib/utils/blockHelpers.js +0 -65
- package/src/lib/utils/blockHelpers.ts +0 -72
- package/src/lib/utils/configValidation.d.ts +0 -23
- package/src/lib/utils/configValidation.d.ts.map +0 -1
- package/src/lib/utils/configValidation.js +0 -113
- package/src/lib/utils/configValidation.ts +0 -137
- package/src/lib/utils/index.ts +0 -8
- package/src/lib/utils/slugify.ts +0 -79
- package/src/registry/BlockRegistry.d.ts +0 -62
- package/src/registry/BlockRegistry.d.ts.map +0 -1
- package/src/registry/BlockRegistry.js +0 -112
- package/src/registry/BlockRegistry.ts +0 -139
- package/src/registry/index.d.ts +0 -6
- package/src/registry/index.d.ts.map +0 -1
- package/src/registry/index.js +0 -4
- package/src/registry/index.ts +0 -11
- package/src/state/EditorContext.d.ts +0 -45
- package/src/state/EditorContext.d.ts.map +0 -1
- package/src/state/EditorContext.js +0 -215
- package/src/state/EditorContext.tsx +0 -283
- package/src/state/index.d.ts +0 -7
- package/src/state/index.d.ts.map +0 -1
- package/src/state/index.js +0 -6
- package/src/state/index.ts +0 -8
- package/src/state/reducer.d.ts +0 -11
- package/src/state/reducer.d.ts.map +0 -1
- package/src/state/reducer.js +0 -443
- package/src/state/reducer.ts +0 -694
- package/src/state/types.d.ts +0 -162
- package/src/state/types.d.ts.map +0 -1
- package/src/state/types.js +0 -27
- package/src/state/types.ts +0 -160
- package/src/types/block.d.ts +0 -221
- package/src/types/block.d.ts.map +0 -1
- package/src/types/block.js +0 -6
- package/src/types/block.ts +0 -269
- package/src/types/index.d.ts +0 -8
- package/src/types/index.d.ts.map +0 -1
- package/src/types/index.js +0 -5
- package/src/types/index.ts +0 -17
- package/src/types/post.d.ts +0 -136
- package/src/types/post.d.ts.map +0 -1
- package/src/types/post.js +0 -5
- package/src/types/post.ts +0 -169
- package/src/utils/client.d.ts +0 -48
- package/src/utils/client.d.ts.map +0 -1
- package/src/utils/client.js +0 -77
- package/src/utils/client.ts +0 -122
- package/src/utils/index.ts +0 -7
- package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
- package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
- package/src/views/CanvasEditor/BlockWrapper.js +0 -276
- package/src/views/CanvasEditor/BlockWrapper.tsx +0 -522
- package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
- package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
- package/src/views/CanvasEditor/CanvasEditorView.js +0 -209
- package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -337
- package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
- package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorBody.js +0 -505
- package/src/views/CanvasEditor/EditorBody.tsx +0 -665
- package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
- package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorHeader.js +0 -101
- package/src/views/CanvasEditor/EditorHeader.tsx +0 -268
- package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
- package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
- package/src/views/CanvasEditor/LayoutContainer.js +0 -222
- package/src/views/CanvasEditor/LayoutContainer.tsx +0 -322
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
- package/src/views/CanvasEditor/SaveConfirmationModal.js +0 -78
- package/src/views/CanvasEditor/SaveConfirmationModal.tsx +0 -233
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/CustomBlockItem.js +0 -44
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -92
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorCanvas.js +0 -32
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -160
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorLibrary.js +0 -25
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -122
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorSidebar.js +0 -20
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -181
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/ErrorBanner.js +0 -8
- package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/FeaturedMediaSection.js +0 -182
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +0 -341
- package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/LibraryItem.js +0 -43
- package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -80
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/PrivacySettingsSection.js +0 -63
- package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +0 -212
- package/src/views/CanvasEditor/components/index.d.ts +0 -21
- package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/index.js +0 -12
- package/src/views/CanvasEditor/components/index.ts +0 -28
- package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
- package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/index.js +0 -9
- package/src/views/CanvasEditor/hooks/index.ts +0 -10
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useHeroBlock.js +0 -79
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +0 -103
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.js +0 -114
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -142
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/usePostLoader.js +0 -32
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +0 -39
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.js +0 -47
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -55
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -285
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +0 -339
- package/src/views/CanvasEditor/index.d.ts +0 -16
- package/src/views/CanvasEditor/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/index.js +0 -9
- package/src/views/CanvasEditor/index.ts +0 -16
- package/src/views/PostManager/EmptyState.d.ts +0 -10
- package/src/views/PostManager/EmptyState.d.ts.map +0 -1
- package/src/views/PostManager/EmptyState.js +0 -12
- package/src/views/PostManager/EmptyState.tsx +0 -42
- package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
- package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
- package/src/views/PostManager/PostActionsMenu.js +0 -58
- package/src/views/PostManager/PostActionsMenu.tsx +0 -112
- package/src/views/PostManager/PostCards.d.ts +0 -15
- package/src/views/PostManager/PostCards.d.ts.map +0 -1
- package/src/views/PostManager/PostCards.js +0 -79
- package/src/views/PostManager/PostCards.tsx +0 -197
- package/src/views/PostManager/PostFilters.d.ts +0 -16
- package/src/views/PostManager/PostFilters.d.ts.map +0 -1
- package/src/views/PostManager/PostFilters.js +0 -10
- package/src/views/PostManager/PostFilters.tsx +0 -95
- package/src/views/PostManager/PostManagerView.d.ts +0 -11
- package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
- package/src/views/PostManager/PostManagerView.js +0 -174
- package/src/views/PostManager/PostManagerView.tsx +0 -289
- package/src/views/PostManager/PostStats.d.ts +0 -11
- package/src/views/PostManager/PostStats.d.ts.map +0 -1
- package/src/views/PostManager/PostStats.js +0 -46
- package/src/views/PostManager/PostStats.tsx +0 -81
- package/src/views/PostManager/PostTable.d.ts +0 -15
- package/src/views/PostManager/PostTable.d.ts.map +0 -1
- package/src/views/PostManager/PostTable.js +0 -79
- package/src/views/PostManager/PostTable.tsx +0 -230
- package/src/views/PostManager/index.d.ts +0 -12
- package/src/views/PostManager/index.d.ts.map +0 -1
- package/src/views/PostManager/index.js +0 -11
- package/src/views/PostManager/index.ts +0 -15
- package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
- package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
- package/src/views/Preview/PreviewBridgeView.js +0 -11
- package/src/views/Preview/PreviewBridgeView.tsx +0 -64
- package/src/views/Preview/index.d.ts +0 -6
- package/src/views/Preview/index.d.ts.map +0 -1
- package/src/views/Preview/index.js +0 -4
- package/src/views/Preview/index.ts +0 -7
- package/src/views/Settings/SettingsView.d.ts +0 -10
- package/src/views/Settings/SettingsView.d.ts.map +0 -1
- package/src/views/Settings/SettingsView.js +0 -111
- package/src/views/Settings/SettingsView.tsx +0 -298
- package/src/views/Settings/index.d.ts +0 -6
- package/src/views/Settings/index.d.ts.map +0 -1
- package/src/views/Settings/index.js +0 -4
- package/src/views/Settings/index.ts +0 -7
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
- package/src/views/SlugSEO/SlugSEOManagerView.js +0 -11
- package/src/views/SlugSEO/SlugSEOManagerView.tsx +0 -94
- package/src/views/SlugSEO/index.d.ts +0 -6
- package/src/views/SlugSEO/index.d.ts.map +0 -1
- package/src/views/SlugSEO/index.js +0 -4
- package/src/views/SlugSEO/index.ts +0 -7
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect, useRef } from 'react';
|
|
4
|
-
import { useEditor } from '../../state/EditorContext';
|
|
5
|
-
import { EditorHeader } from './EditorHeader';
|
|
6
|
-
import { ErrorBanner } from './components/ErrorBanner';
|
|
7
|
-
import { EditorLibrary } from './components/EditorLibrary';
|
|
8
|
-
import { EditorCanvas } from './components/EditorCanvas';
|
|
9
|
-
import { EditorSidebar } from './components/EditorSidebar';
|
|
10
|
-
import { usePostLoader, useHeroBlock, useRegisteredBlocks, useKeyboardShortcuts, useUnsavedChanges } from './hooks';
|
|
11
|
-
export function CanvasEditorView({ postId, darkMode, backgroundColors: propsBackgroundColors, siteId, locale }) {
|
|
12
|
-
const { state, helpers, dispatch, darkMode: contextDarkMode, backgroundColors: contextBackgroundColors, canUndo, canRedo } = useEditor();
|
|
13
|
-
const effectiveDarkMode = darkMode !== undefined ? darkMode : contextDarkMode;
|
|
14
|
-
const effectiveBackgroundColors = propsBackgroundColors || contextBackgroundColors;
|
|
15
|
-
const [isSidebarOpen, setSidebarOpen] = useState(true);
|
|
16
|
-
const [isLibraryOpen, setLibraryOpen] = useState(true);
|
|
17
|
-
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
|
18
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
19
|
-
const [saveError, setSaveError] = useState(null);
|
|
20
|
-
// Get registered blocks
|
|
21
|
-
const registeredBlocks = useRegisteredBlocks();
|
|
22
|
-
// Hero block management
|
|
23
|
-
const { heroBlock, setHeroBlock, heroBlockDefinition } = useHeroBlock(state, registeredBlocks);
|
|
24
|
-
// Post loading
|
|
25
|
-
const { isLoadingPost } = usePostLoader(postId, state.postId, (post) => {
|
|
26
|
-
helpers.loadPost(post);
|
|
27
|
-
// After loading, ensure we're marked as clean
|
|
28
|
-
// Use setTimeout to ensure this runs after the reducer has processed LOAD_POST
|
|
29
|
-
setTimeout(() => {
|
|
30
|
-
dispatch({ type: 'MARK_CLEAN' });
|
|
31
|
-
}, 0);
|
|
32
|
-
}, () => setHeroBlock(null));
|
|
33
|
-
// Track if we just loaded a post to prevent marking as dirty during cleanup
|
|
34
|
-
const justLoadedRef = useRef(false);
|
|
35
|
-
const previousIsLoadingRef = useRef(false);
|
|
36
|
-
const loadingCleanupTimerRef = useRef(null);
|
|
37
|
-
// Mark when post loading completes and ensure it stays clean after all effects
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
// Detect when loading just finished (was loading, now not loading, and we have a postId)
|
|
40
|
-
const loadingJustFinished = previousIsLoadingRef.current && !isLoadingPost && state.postId;
|
|
41
|
-
if (loadingJustFinished) {
|
|
42
|
-
justLoadedRef.current = true;
|
|
43
|
-
// Clear any existing cleanup timer
|
|
44
|
-
if (loadingCleanupTimerRef.current) {
|
|
45
|
-
clearTimeout(loadingCleanupTimerRef.current);
|
|
46
|
-
}
|
|
47
|
-
// Wait for all effects to complete, then ensure we're marked as clean
|
|
48
|
-
// Use multiple animation frames + setTimeout to ensure all effects have run
|
|
49
|
-
requestAnimationFrame(() => {
|
|
50
|
-
requestAnimationFrame(() => {
|
|
51
|
-
loadingCleanupTimerRef.current = setTimeout(() => {
|
|
52
|
-
// Force mark as clean after loading - this ensures cleanup effects don't leave us dirty
|
|
53
|
-
console.log('[CanvasEditorView] Post loading complete - ensuring clean state');
|
|
54
|
-
dispatch({ type: 'MARK_CLEAN' });
|
|
55
|
-
justLoadedRef.current = false;
|
|
56
|
-
loadingCleanupTimerRef.current = null;
|
|
57
|
-
}, 500); // Delay to ensure all effects complete
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
// Update ref
|
|
62
|
-
previousIsLoadingRef.current = isLoadingPost;
|
|
63
|
-
return () => {
|
|
64
|
-
if (loadingCleanupTimerRef.current) {
|
|
65
|
-
clearTimeout(loadingCleanupTimerRef.current);
|
|
66
|
-
loadingCleanupTimerRef.current = null;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
}, [isLoadingPost, state.postId, dispatch]);
|
|
70
|
-
// Keyboard shortcuts
|
|
71
|
-
useKeyboardShortcuts(state, dispatch, canUndo, canRedo, helpers.undo, helpers.redo);
|
|
72
|
-
// Unsaved changes warning and auto-save
|
|
73
|
-
const { autoSaveEnabled, setAutoSaveEnabled, countdown, saveStatus } = useUnsavedChanges({
|
|
74
|
-
state,
|
|
75
|
-
isDirty: state.isDirty,
|
|
76
|
-
onSave: async () => {
|
|
77
|
-
// Preserve current status: if already published, keep it published
|
|
78
|
-
// Otherwise save as draft
|
|
79
|
-
const shouldPublish = state.status === 'published';
|
|
80
|
-
await handleSave(shouldPublish);
|
|
81
|
-
},
|
|
82
|
-
heroBlock,
|
|
83
|
-
postId: state.postId,
|
|
84
|
-
});
|
|
85
|
-
// Listen for hero title updates from HeroBlock (if it dispatches events)
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
const handleHeroTitleUpdate = (e) => {
|
|
88
|
-
dispatch({ type: 'SET_TITLE', payload: e.detail });
|
|
89
|
-
};
|
|
90
|
-
window.addEventListener('hero-title-update', handleHeroTitleUpdate);
|
|
91
|
-
return () => window.removeEventListener('hero-title-update', handleHeroTitleUpdate);
|
|
92
|
-
}, [dispatch]);
|
|
93
|
-
// Remove any hero blocks from the content blocks array
|
|
94
|
-
// Note: This effect will mark as dirty, but the loading cleanup effect will fix it
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
const heroBlocksInContent = state.blocks.filter(b => b.type === 'hero');
|
|
97
|
-
if (heroBlocksInContent.length > 0) {
|
|
98
|
-
heroBlocksInContent.forEach(block => {
|
|
99
|
-
dispatch({ type: 'DELETE_BLOCK', payload: { id: block.id } });
|
|
100
|
-
});
|
|
101
|
-
// Don't mark as clean here - let the loading cleanup effect handle it
|
|
102
|
-
// This ensures we wait for all effects to complete before marking clean
|
|
103
|
-
}
|
|
104
|
-
}, [state.blocks, dispatch]);
|
|
105
|
-
// Filter out hero blocks from content blocks
|
|
106
|
-
const contentBlocks = state.blocks.filter(b => b.type !== 'hero');
|
|
107
|
-
// Handler to add block at the bottom when clicking (not dragging)
|
|
108
|
-
const handleAddBlockAtBottom = (blockType) => {
|
|
109
|
-
// Add at the end of content blocks (excluding hero)
|
|
110
|
-
helpers.addBlock(blockType, contentBlocks.length, undefined);
|
|
111
|
-
};
|
|
112
|
-
// Handle save
|
|
113
|
-
const handleSave = async (publish) => {
|
|
114
|
-
setIsSaving(true);
|
|
115
|
-
setSaveError(null);
|
|
116
|
-
try {
|
|
117
|
-
// Status should already be set in EditorHeader, but verify and log
|
|
118
|
-
console.log('[CanvasEditorView] onSave called with publish:', publish, 'current status:', state.status);
|
|
119
|
-
// Only change status if explicitly requested (publish is true or false)
|
|
120
|
-
// If publish is undefined, preserve the current status (used for autosave)
|
|
121
|
-
if (publish === true && state.status !== 'published') {
|
|
122
|
-
console.warn('[CanvasEditorView] Status mismatch! Setting to published...');
|
|
123
|
-
dispatch({ type: 'SET_STATUS', payload: 'published' });
|
|
124
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
125
|
-
}
|
|
126
|
-
else if (publish === false && state.status !== 'draft' && state.status !== 'published') {
|
|
127
|
-
// Only set to draft if not already published (preserve published status)
|
|
128
|
-
// This prevents autosave from changing published posts back to draft
|
|
129
|
-
console.warn('[CanvasEditorView] Status mismatch! Setting to draft...');
|
|
130
|
-
dispatch({ type: 'SET_STATUS', payload: 'draft' });
|
|
131
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
132
|
-
}
|
|
133
|
-
console.log('[CanvasEditorView] Final status before save:', state.status);
|
|
134
|
-
// Pass hero block to save function so it can be included in the saved data
|
|
135
|
-
await helpers.save(heroBlock);
|
|
136
|
-
setIsSaving(false);
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
console.error('[CanvasEditorView] Save error:', error);
|
|
140
|
-
// Extract and format user-friendly error message
|
|
141
|
-
let errorMessage = error.message || 'Failed to save post';
|
|
142
|
-
// Make error messages more user-friendly
|
|
143
|
-
if (errorMessage.includes('Missing required fields')) {
|
|
144
|
-
errorMessage = errorMessage.replace('Missing required fields for publishing:', 'To publish, please fill in:');
|
|
145
|
-
}
|
|
146
|
-
else if (errorMessage.includes('All required fields')) {
|
|
147
|
-
errorMessage = 'To publish, please fill in all required fields: summary, featured image, category, and content.';
|
|
148
|
-
}
|
|
149
|
-
else if (errorMessage.includes('Unauthorized')) {
|
|
150
|
-
errorMessage = 'You are not authorized to save this post. Please log in again.';
|
|
151
|
-
}
|
|
152
|
-
else if (errorMessage.includes('Failed to save')) {
|
|
153
|
-
errorMessage = 'Unable to save the post. Please check your connection and try again.';
|
|
154
|
-
}
|
|
155
|
-
setSaveError(errorMessage);
|
|
156
|
-
setIsSaving(false); // Always reset saving state on error
|
|
157
|
-
throw error; // Re-throw so EditorHeader can handle it
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
// Handle hero block update
|
|
161
|
-
const handleHeroBlockUpdate = (data) => {
|
|
162
|
-
if (!heroBlock)
|
|
163
|
-
return;
|
|
164
|
-
setHeroBlock(Object.assign(Object.assign({}, heroBlock), { data: Object.assign(Object.assign({}, heroBlock.data), data) }));
|
|
165
|
-
// Sync title to editor state
|
|
166
|
-
if (data.title !== undefined && typeof data.title === 'string') {
|
|
167
|
-
dispatch({ type: 'SET_TITLE', payload: data.title });
|
|
168
|
-
}
|
|
169
|
-
// Sync summary to editor state metadata
|
|
170
|
-
if (data.summary !== undefined && typeof data.summary === 'string') {
|
|
171
|
-
dispatch({
|
|
172
|
-
type: 'SET_METADATA',
|
|
173
|
-
payload: { excerpt: data.summary }
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
// Hero image and featured image are completely independent
|
|
177
|
-
// Do NOT sync hero image to featured image
|
|
178
|
-
// The featured image is a separate thumbnail that the client adjusts independently
|
|
179
|
-
// Sync category to editor state metadata
|
|
180
|
-
if (data.category !== undefined && typeof data.category === 'string') {
|
|
181
|
-
dispatch({
|
|
182
|
-
type: 'SET_METADATA',
|
|
183
|
-
payload: {
|
|
184
|
-
categories: data.category.trim() ? [data.category.trim()] : []
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
// Handle hero block delete/reset
|
|
190
|
-
const handleHeroBlockDelete = () => {
|
|
191
|
-
if (!heroBlock || !heroBlockDefinition)
|
|
192
|
-
return;
|
|
193
|
-
const defaultData = heroBlockDefinition.defaultData || {};
|
|
194
|
-
setHeroBlock(Object.assign(Object.assign({}, heroBlock), { data: Object.assign({}, defaultData) }));
|
|
195
|
-
};
|
|
196
|
-
return (_jsx("div", { className: "h-full rounded-[2.5rem] w-full bg-dashboard-card text-dashboard-text flex flex-col font-sans transition-colors duration-300 overflow-hidden relative", children: _jsxs("main", { className: "flex flex-1 flex-col relative min-h-0", children: [_jsx(ErrorBanner, { error: saveError, onDismiss: () => setSaveError(null) }), _jsx(EditorHeader, { isLibraryOpen: isLibraryOpen, onLibraryToggle: () => setLibraryOpen(!isLibraryOpen), isPreviewMode: isPreviewMode, onPreviewToggle: () => setIsPreviewMode(!isPreviewMode), isSidebarOpen: isSidebarOpen, onSidebarToggle: () => setSidebarOpen(!isSidebarOpen), isSaving: isSaving, onSave: handleSave, onSaveError: (error) => {
|
|
197
|
-
// Format error message for display
|
|
198
|
-
if (error) {
|
|
199
|
-
let formattedError = error;
|
|
200
|
-
if (formattedError.includes('Missing required fields')) {
|
|
201
|
-
formattedError = formattedError.replace('Missing required fields for publishing:', 'To publish, please fill in:');
|
|
202
|
-
}
|
|
203
|
-
setSaveError(formattedError);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
setSaveError(null);
|
|
207
|
-
}
|
|
208
|
-
}, autoSaveEnabled: autoSaveEnabled, onAutoSaveToggle: setAutoSaveEnabled, isDirty: state.isDirty, autoSaveCountdown: countdown, autoSaveStatus: saveStatus }), _jsxs("div", { className: "flex flex-1 relative overflow-hidden min-h-0 flex-nowrap", children: [!isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-r border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isLibraryOpen ? 'w-72' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorLibrary, { registeredBlocks: registeredBlocks, onAddBlock: handleAddBlockAtBottom }) })), _jsx(EditorCanvas, { isPreviewMode: isPreviewMode, heroBlock: heroBlock, heroBlockDefinition: heroBlockDefinition, contentBlocks: contentBlocks, title: state.title, siteId: siteId, locale: locale, darkMode: effectiveDarkMode, backgroundColors: effectiveBackgroundColors, featuredImage: state.metadata.featuredImage, onTitleChange: (title) => dispatch({ type: 'SET_TITLE', payload: title }), onHeroBlockUpdate: handleHeroBlockUpdate, onHeroBlockDelete: handleHeroBlockDelete, onBlockAdd: (type, index, containerId) => helpers.addBlock(type, index, containerId), onBlockUpdate: (id, data) => helpers.updateBlock(id, data), onBlockDelete: (id) => helpers.deleteBlock(id), onBlockMove: (id, newIndex, containerId) => helpers.moveBlock(id, newIndex, containerId) }), !isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-l border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isSidebarOpen ? 'w-80' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorSidebar, { slug: state.slug, seo: state.seo, metadata: state.metadata, heroBlock: heroBlock, status: state.status, onSEOUpdate: (seo) => dispatch({ type: 'SET_SEO', payload: seo }), onMetadataUpdate: (metadata) => dispatch({ type: 'SET_METADATA', payload: metadata }) }) }))] })] }) }));
|
|
209
|
-
}
|
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
4
|
-
import { useEditor } from '../../state/EditorContext';
|
|
5
|
-
import { EditorHeader } from './EditorHeader';
|
|
6
|
-
import { ErrorBanner } from './components/ErrorBanner';
|
|
7
|
-
import { EditorLibrary } from './components/EditorLibrary';
|
|
8
|
-
import { EditorCanvas } from './components/EditorCanvas';
|
|
9
|
-
import { EditorSidebar } from './components/EditorSidebar';
|
|
10
|
-
import { usePostLoader, useHeroBlock, useRegisteredBlocks, useKeyboardShortcuts, useUnsavedChanges } from './hooks';
|
|
11
|
-
import type { Block } from '../../types/block';
|
|
12
|
-
import type { SEOMetadata, PostMetadata } from '../../types/post';
|
|
13
|
-
|
|
14
|
-
export interface CanvasEditorViewProps {
|
|
15
|
-
postId?: string;
|
|
16
|
-
siteId: string;
|
|
17
|
-
locale: string;
|
|
18
|
-
/** Enable dark mode for content area and wrappers (default: true) */
|
|
19
|
-
darkMode?: boolean;
|
|
20
|
-
/** Background colors for the editor */
|
|
21
|
-
backgroundColors?: {
|
|
22
|
-
light: string;
|
|
23
|
-
dark?: string;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function CanvasEditorView({ postId, darkMode, backgroundColors: propsBackgroundColors, siteId, locale }: CanvasEditorViewProps) {
|
|
28
|
-
const { state, helpers, dispatch, darkMode: contextDarkMode, backgroundColors: contextBackgroundColors, canUndo, canRedo } = useEditor();
|
|
29
|
-
const effectiveDarkMode = darkMode !== undefined ? darkMode : contextDarkMode;
|
|
30
|
-
const effectiveBackgroundColors = propsBackgroundColors || contextBackgroundColors;
|
|
31
|
-
const [isSidebarOpen, setSidebarOpen] = useState(true);
|
|
32
|
-
const [isLibraryOpen, setLibraryOpen] = useState(true);
|
|
33
|
-
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
|
34
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
35
|
-
const [saveError, setSaveError] = useState<string | null>(null);
|
|
36
|
-
|
|
37
|
-
// Get registered blocks
|
|
38
|
-
const registeredBlocks = useRegisteredBlocks();
|
|
39
|
-
|
|
40
|
-
// Hero block management
|
|
41
|
-
const { heroBlock, setHeroBlock, heroBlockDefinition } = useHeroBlock(state, registeredBlocks);
|
|
42
|
-
|
|
43
|
-
// Post loading
|
|
44
|
-
const { isLoadingPost } = usePostLoader(
|
|
45
|
-
postId,
|
|
46
|
-
state.postId,
|
|
47
|
-
(post) => {
|
|
48
|
-
helpers.loadPost(post);
|
|
49
|
-
// After loading, ensure we're marked as clean
|
|
50
|
-
// Use setTimeout to ensure this runs after the reducer has processed LOAD_POST
|
|
51
|
-
setTimeout(() => {
|
|
52
|
-
dispatch({ type: 'MARK_CLEAN' });
|
|
53
|
-
}, 0);
|
|
54
|
-
},
|
|
55
|
-
() => setHeroBlock(null)
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Track if we just loaded a post to prevent marking as dirty during cleanup
|
|
59
|
-
const justLoadedRef = useRef(false);
|
|
60
|
-
const previousIsLoadingRef = useRef<boolean>(false);
|
|
61
|
-
const loadingCleanupTimerRef = useRef<NodeJS.Timeout | null>(null);
|
|
62
|
-
|
|
63
|
-
// Mark when post loading completes and ensure it stays clean after all effects
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
// Detect when loading just finished (was loading, now not loading, and we have a postId)
|
|
66
|
-
const loadingJustFinished = previousIsLoadingRef.current && !isLoadingPost && state.postId;
|
|
67
|
-
|
|
68
|
-
if (loadingJustFinished) {
|
|
69
|
-
justLoadedRef.current = true;
|
|
70
|
-
|
|
71
|
-
// Clear any existing cleanup timer
|
|
72
|
-
if (loadingCleanupTimerRef.current) {
|
|
73
|
-
clearTimeout(loadingCleanupTimerRef.current);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Wait for all effects to complete, then ensure we're marked as clean
|
|
77
|
-
// Use multiple animation frames + setTimeout to ensure all effects have run
|
|
78
|
-
requestAnimationFrame(() => {
|
|
79
|
-
requestAnimationFrame(() => {
|
|
80
|
-
loadingCleanupTimerRef.current = setTimeout(() => {
|
|
81
|
-
// Force mark as clean after loading - this ensures cleanup effects don't leave us dirty
|
|
82
|
-
console.log('[CanvasEditorView] Post loading complete - ensuring clean state');
|
|
83
|
-
dispatch({ type: 'MARK_CLEAN' });
|
|
84
|
-
justLoadedRef.current = false;
|
|
85
|
-
loadingCleanupTimerRef.current = null;
|
|
86
|
-
}, 500); // Delay to ensure all effects complete
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Update ref
|
|
92
|
-
previousIsLoadingRef.current = isLoadingPost;
|
|
93
|
-
|
|
94
|
-
return () => {
|
|
95
|
-
if (loadingCleanupTimerRef.current) {
|
|
96
|
-
clearTimeout(loadingCleanupTimerRef.current);
|
|
97
|
-
loadingCleanupTimerRef.current = null;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
}, [isLoadingPost, state.postId, dispatch]);
|
|
101
|
-
|
|
102
|
-
// Keyboard shortcuts
|
|
103
|
-
useKeyboardShortcuts(state, dispatch, canUndo, canRedo, helpers.undo, helpers.redo);
|
|
104
|
-
|
|
105
|
-
// Unsaved changes warning and auto-save
|
|
106
|
-
const { autoSaveEnabled, setAutoSaveEnabled, countdown, saveStatus } = useUnsavedChanges({
|
|
107
|
-
state,
|
|
108
|
-
isDirty: state.isDirty,
|
|
109
|
-
onSave: async () => {
|
|
110
|
-
// Preserve current status: if already published, keep it published
|
|
111
|
-
// Otherwise save as draft
|
|
112
|
-
const shouldPublish = state.status === 'published';
|
|
113
|
-
await handleSave(shouldPublish);
|
|
114
|
-
},
|
|
115
|
-
heroBlock,
|
|
116
|
-
postId: state.postId,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Listen for hero title updates from HeroBlock (if it dispatches events)
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
const handleHeroTitleUpdate = (e: CustomEvent) => {
|
|
122
|
-
dispatch({ type: 'SET_TITLE', payload: e.detail });
|
|
123
|
-
};
|
|
124
|
-
window.addEventListener('hero-title-update', handleHeroTitleUpdate as EventListener);
|
|
125
|
-
return () => window.removeEventListener('hero-title-update', handleHeroTitleUpdate as EventListener);
|
|
126
|
-
}, [dispatch]);
|
|
127
|
-
|
|
128
|
-
// Remove any hero blocks from the content blocks array
|
|
129
|
-
// Note: This effect will mark as dirty, but the loading cleanup effect will fix it
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
const heroBlocksInContent = state.blocks.filter(b => b.type === 'hero');
|
|
132
|
-
if (heroBlocksInContent.length > 0) {
|
|
133
|
-
heroBlocksInContent.forEach(block => {
|
|
134
|
-
dispatch({ type: 'DELETE_BLOCK', payload: { id: block.id } });
|
|
135
|
-
});
|
|
136
|
-
// Don't mark as clean here - let the loading cleanup effect handle it
|
|
137
|
-
// This ensures we wait for all effects to complete before marking clean
|
|
138
|
-
}
|
|
139
|
-
}, [state.blocks, dispatch]);
|
|
140
|
-
|
|
141
|
-
// Filter out hero blocks from content blocks
|
|
142
|
-
const contentBlocks = state.blocks.filter(b => b.type !== 'hero');
|
|
143
|
-
|
|
144
|
-
// Handler to add block at the bottom when clicking (not dragging)
|
|
145
|
-
const handleAddBlockAtBottom = (blockType: string) => {
|
|
146
|
-
// Add at the end of content blocks (excluding hero)
|
|
147
|
-
helpers.addBlock(blockType, contentBlocks.length, undefined);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
// Handle save
|
|
151
|
-
const handleSave = async (publish?: boolean) => {
|
|
152
|
-
setIsSaving(true);
|
|
153
|
-
setSaveError(null);
|
|
154
|
-
try {
|
|
155
|
-
// Status should already be set in EditorHeader, but verify and log
|
|
156
|
-
console.log('[CanvasEditorView] onSave called with publish:', publish, 'current status:', state.status);
|
|
157
|
-
|
|
158
|
-
// Only change status if explicitly requested (publish is true or false)
|
|
159
|
-
// If publish is undefined, preserve the current status (used for autosave)
|
|
160
|
-
if (publish === true && state.status !== 'published') {
|
|
161
|
-
console.warn('[CanvasEditorView] Status mismatch! Setting to published...');
|
|
162
|
-
dispatch({ type: 'SET_STATUS', payload: 'published' });
|
|
163
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
164
|
-
} else if (publish === false && state.status !== 'draft' && state.status !== 'published') {
|
|
165
|
-
// Only set to draft if not already published (preserve published status)
|
|
166
|
-
// This prevents autosave from changing published posts back to draft
|
|
167
|
-
console.warn('[CanvasEditorView] Status mismatch! Setting to draft...');
|
|
168
|
-
dispatch({ type: 'SET_STATUS', payload: 'draft' });
|
|
169
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
console.log('[CanvasEditorView] Final status before save:', state.status);
|
|
173
|
-
|
|
174
|
-
// Pass hero block to save function so it can be included in the saved data
|
|
175
|
-
await helpers.save(heroBlock);
|
|
176
|
-
setIsSaving(false);
|
|
177
|
-
} catch (error: any) {
|
|
178
|
-
console.error('[CanvasEditorView] Save error:', error);
|
|
179
|
-
// Extract and format user-friendly error message
|
|
180
|
-
let errorMessage = error.message || 'Failed to save post';
|
|
181
|
-
|
|
182
|
-
// Make error messages more user-friendly
|
|
183
|
-
if (errorMessage.includes('Missing required fields')) {
|
|
184
|
-
errorMessage = errorMessage.replace('Missing required fields for publishing:', 'To publish, please fill in:');
|
|
185
|
-
} else if (errorMessage.includes('All required fields')) {
|
|
186
|
-
errorMessage = 'To publish, please fill in all required fields: summary, featured image, category, and content.';
|
|
187
|
-
} else if (errorMessage.includes('Unauthorized')) {
|
|
188
|
-
errorMessage = 'You are not authorized to save this post. Please log in again.';
|
|
189
|
-
} else if (errorMessage.includes('Failed to save')) {
|
|
190
|
-
errorMessage = 'Unable to save the post. Please check your connection and try again.';
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
setSaveError(errorMessage);
|
|
194
|
-
setIsSaving(false); // Always reset saving state on error
|
|
195
|
-
throw error; // Re-throw so EditorHeader can handle it
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
// Handle hero block update
|
|
200
|
-
const handleHeroBlockUpdate = (data: Partial<Block['data']>) => {
|
|
201
|
-
if (!heroBlock) return;
|
|
202
|
-
|
|
203
|
-
setHeroBlock({
|
|
204
|
-
...heroBlock,
|
|
205
|
-
data: { ...heroBlock.data, ...data },
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// Sync title to editor state
|
|
209
|
-
if (data.title !== undefined && typeof data.title === 'string') {
|
|
210
|
-
dispatch({ type: 'SET_TITLE', payload: data.title });
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Sync summary to editor state metadata
|
|
214
|
-
if (data.summary !== undefined && typeof data.summary === 'string') {
|
|
215
|
-
dispatch({
|
|
216
|
-
type: 'SET_METADATA',
|
|
217
|
-
payload: { excerpt: data.summary }
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Hero image and featured image are completely independent
|
|
222
|
-
// Do NOT sync hero image to featured image
|
|
223
|
-
// The featured image is a separate thumbnail that the client adjusts independently
|
|
224
|
-
|
|
225
|
-
// Sync category to editor state metadata
|
|
226
|
-
if (data.category !== undefined && typeof data.category === 'string') {
|
|
227
|
-
dispatch({
|
|
228
|
-
type: 'SET_METADATA',
|
|
229
|
-
payload: {
|
|
230
|
-
categories: data.category.trim() ? [data.category.trim()] : []
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// Handle hero block delete/reset
|
|
237
|
-
const handleHeroBlockDelete = () => {
|
|
238
|
-
if (!heroBlock || !heroBlockDefinition) return;
|
|
239
|
-
const defaultData = heroBlockDefinition.defaultData || {};
|
|
240
|
-
setHeroBlock({
|
|
241
|
-
...heroBlock,
|
|
242
|
-
data: { ...defaultData },
|
|
243
|
-
});
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
return (
|
|
247
|
-
<div className="h-full rounded-[2.5rem] w-full bg-dashboard-card text-dashboard-text flex flex-col font-sans transition-colors duration-300 overflow-hidden relative">
|
|
248
|
-
<main className="flex flex-1 flex-col relative min-h-0">
|
|
249
|
-
{/* Error Banner */}
|
|
250
|
-
<ErrorBanner error={saveError} onDismiss={() => setSaveError(null)} />
|
|
251
|
-
|
|
252
|
-
<EditorHeader
|
|
253
|
-
isLibraryOpen={isLibraryOpen}
|
|
254
|
-
onLibraryToggle={() => setLibraryOpen(!isLibraryOpen)}
|
|
255
|
-
isPreviewMode={isPreviewMode}
|
|
256
|
-
onPreviewToggle={() => setIsPreviewMode(!isPreviewMode)}
|
|
257
|
-
isSidebarOpen={isSidebarOpen}
|
|
258
|
-
onSidebarToggle={() => setSidebarOpen(!isSidebarOpen)}
|
|
259
|
-
isSaving={isSaving}
|
|
260
|
-
onSave={handleSave}
|
|
261
|
-
onSaveError={(error) => {
|
|
262
|
-
// Format error message for display
|
|
263
|
-
if (error) {
|
|
264
|
-
let formattedError = error;
|
|
265
|
-
if (formattedError.includes('Missing required fields')) {
|
|
266
|
-
formattedError = formattedError.replace('Missing required fields for publishing:', 'To publish, please fill in:');
|
|
267
|
-
}
|
|
268
|
-
setSaveError(formattedError);
|
|
269
|
-
} else {
|
|
270
|
-
setSaveError(null);
|
|
271
|
-
}
|
|
272
|
-
}}
|
|
273
|
-
autoSaveEnabled={autoSaveEnabled}
|
|
274
|
-
onAutoSaveToggle={setAutoSaveEnabled}
|
|
275
|
-
isDirty={state.isDirty}
|
|
276
|
-
autoSaveCountdown={countdown}
|
|
277
|
-
autoSaveStatus={saveStatus}
|
|
278
|
-
/>
|
|
279
|
-
|
|
280
|
-
{/* Editor Content Wrapper */}
|
|
281
|
-
<div className="flex flex-1 relative overflow-hidden min-h-0 flex-nowrap">
|
|
282
|
-
{/* LEFT SIDEBAR: COMPONENT LIBRARY */}
|
|
283
|
-
{!isPreviewMode && (
|
|
284
|
-
<aside
|
|
285
|
-
className={`transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-r border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isLibraryOpen ? 'w-72' : 'w-0 opacity-0 pointer-events-none'
|
|
286
|
-
}`}
|
|
287
|
-
>
|
|
288
|
-
<EditorLibrary
|
|
289
|
-
registeredBlocks={registeredBlocks}
|
|
290
|
-
onAddBlock={handleAddBlockAtBottom}
|
|
291
|
-
/>
|
|
292
|
-
</aside>
|
|
293
|
-
)}
|
|
294
|
-
|
|
295
|
-
{/* CENTER: THE WRITING CANVAS */}
|
|
296
|
-
<EditorCanvas
|
|
297
|
-
isPreviewMode={isPreviewMode}
|
|
298
|
-
heroBlock={heroBlock}
|
|
299
|
-
heroBlockDefinition={heroBlockDefinition}
|
|
300
|
-
contentBlocks={contentBlocks}
|
|
301
|
-
title={state.title}
|
|
302
|
-
siteId={siteId}
|
|
303
|
-
locale={locale}
|
|
304
|
-
darkMode={effectiveDarkMode}
|
|
305
|
-
backgroundColors={effectiveBackgroundColors}
|
|
306
|
-
featuredImage={state.metadata.featuredImage}
|
|
307
|
-
onTitleChange={(title: string) => dispatch({ type: 'SET_TITLE', payload: title })}
|
|
308
|
-
onHeroBlockUpdate={handleHeroBlockUpdate}
|
|
309
|
-
onHeroBlockDelete={handleHeroBlockDelete}
|
|
310
|
-
onBlockAdd={(type: string, index: number, containerId?: string) => helpers.addBlock(type, index, containerId)}
|
|
311
|
-
onBlockUpdate={(id: string, data: Partial<Block['data']>) => helpers.updateBlock(id, data)}
|
|
312
|
-
onBlockDelete={(id: string) => helpers.deleteBlock(id)}
|
|
313
|
-
onBlockMove={(id: string, newIndex: number, containerId?: string) => helpers.moveBlock(id, newIndex, containerId)}
|
|
314
|
-
/>
|
|
315
|
-
|
|
316
|
-
{/* RIGHT SIDEBAR: THE "DESK" (SETTINGS) */}
|
|
317
|
-
{!isPreviewMode && (
|
|
318
|
-
<aside
|
|
319
|
-
className={`transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-l border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isSidebarOpen ? 'w-80' : 'w-0 opacity-0 pointer-events-none'
|
|
320
|
-
}`}
|
|
321
|
-
>
|
|
322
|
-
<EditorSidebar
|
|
323
|
-
slug={state.slug}
|
|
324
|
-
seo={state.seo}
|
|
325
|
-
metadata={state.metadata}
|
|
326
|
-
heroBlock={heroBlock}
|
|
327
|
-
status={state.status}
|
|
328
|
-
onSEOUpdate={(seo: Partial<SEOMetadata>) => dispatch({ type: 'SET_SEO', payload: seo })}
|
|
329
|
-
onMetadataUpdate={(metadata: Partial<PostMetadata>) => dispatch({ type: 'SET_METADATA', payload: metadata })}
|
|
330
|
-
/>
|
|
331
|
-
</aside>
|
|
332
|
-
)}
|
|
333
|
-
</div>
|
|
334
|
-
</main>
|
|
335
|
-
</div>
|
|
336
|
-
);
|
|
337
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Editor Body Component
|
|
3
|
-
* Primary root container for the editor canvas
|
|
4
|
-
* Acts as the main drop zone for blocks
|
|
5
|
-
*/
|
|
6
|
-
import { Block } from '../../types/block';
|
|
7
|
-
export interface EditorBodyProps {
|
|
8
|
-
blocks: Block[];
|
|
9
|
-
onBlockAdd: (type: string, index: number, containerId?: string) => void;
|
|
10
|
-
onBlockUpdate: (id: string, data: Partial<Block['data']>) => void;
|
|
11
|
-
onBlockDelete: (id: string) => void;
|
|
12
|
-
onBlockMove: (id: string, newIndex: number, containerId?: string) => void;
|
|
13
|
-
/** Enable dark mode for content area and wrappers (default: true) */
|
|
14
|
-
darkMode?: boolean;
|
|
15
|
-
/** Background colors for the editor */
|
|
16
|
-
backgroundColors?: {
|
|
17
|
-
light: string;
|
|
18
|
-
dark?: string;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export declare function EditorBody({ blocks, onBlockAdd, onBlockUpdate, onBlockDelete, onBlockMove, darkMode, backgroundColors, }: EditorBodyProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
-
//# sourceMappingURL=EditorBody.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EditorBody.d.ts","sourceRoot":"","sources":["EditorBody.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAG1C,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC;IAClE,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED,wBAAgB,UAAU,CAAC,EACvB,MAAM,EACN,UAAU,EACV,aAAa,EACb,aAAa,EACb,WAAW,EACX,QAAe,EACf,gBAAgB,GACnB,EAAE,eAAe,2CAuhBjB"}
|