@jhits/plugin-blog 0.0.18 → 0.0.20
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/api/categories.d.ts.map +1 -1
- package/dist/api/categories.js +42 -38
- package/dist/api/handler.d.ts +1 -26
- package/dist/api/handler.d.ts.map +1 -1
- package/dist/api/handler.js +81 -500
- package/dist/api/router.d.ts +0 -5
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +8 -35
- package/dist/api/service.d.ts +80 -0
- package/dist/api/service.d.ts.map +1 -0
- package/dist/api/service.js +219 -0
- package/dist/hooks/useAutoSave.d.ts +10 -0
- package/dist/hooks/useAutoSave.d.ts.map +1 -0
- package/dist/hooks/useAutoSave.js +57 -0
- package/dist/hooks/useCategories.d.ts +1 -1
- package/dist/hooks/useCategories.d.ts.map +1 -1
- package/dist/hooks/useCategories.js +15 -46
- package/dist/index.d.ts +24 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -201
- package/dist/init.d.ts +20 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +8 -7
- package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/ColumnsBlock.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/ColumnsBlock.js +30 -113
- package/dist/lib/layouts/blocks/SectionBlock.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/SectionBlock.js +9 -21
- package/dist/lib/layouts/index.d.ts +3 -3
- package/dist/lib/layouts/index.js +4 -4
- package/dist/lib/mappers/apiMapper.d.ts +10 -0
- package/dist/lib/mappers/apiMapper.d.ts.map +1 -1
- package/dist/lib/mappers/apiMapper.js +47 -32
- package/dist/lib/rich-text/RichTextEditor.d.ts +4 -2
- package/dist/lib/rich-text/RichTextEditor.d.ts.map +1 -1
- package/dist/lib/rich-text/RichTextEditor.js +12 -9
- package/dist/lib/utils/config-resolver.d.ts +28 -0
- package/dist/lib/utils/config-resolver.d.ts.map +1 -0
- package/dist/lib/utils/config-resolver.js +46 -0
- package/dist/lib/utils/tree.d.ts +29 -0
- package/dist/lib/utils/tree.d.ts.map +1 -0
- package/dist/lib/utils/tree.js +129 -0
- package/dist/state/EditorContext.d.ts +3 -25
- package/dist/state/EditorContext.d.ts.map +1 -1
- package/dist/state/EditorContext.js +124 -174
- package/dist/state/reducer.d.ts +1 -5
- package/dist/state/reducer.d.ts.map +1 -1
- package/dist/state/reducer.js +128 -521
- package/dist/state/types.d.ts +12 -1
- package/dist/state/types.d.ts.map +1 -1
- package/dist/types/block.d.ts +9 -0
- package/dist/types/block.d.ts.map +1 -1
- package/dist/types/post.d.ts +17 -1
- package/dist/types/post.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.d.ts +5 -6
- package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.js +56 -264
- package/dist/views/CanvasEditor/CanvasEditorView.d.ts +5 -3
- package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
- package/dist/views/CanvasEditor/CanvasEditorView.js +55 -315
- package/dist/views/CanvasEditor/EditorBody.d.ts +6 -8
- package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
- package/dist/views/CanvasEditor/EditorBody.js +34 -482
- package/dist/views/CanvasEditor/EditorHeader.d.ts.map +1 -1
- package/dist/views/CanvasEditor/EditorHeader.js +27 -63
- package/dist/views/CanvasEditor/LayoutContainer.d.ts.map +1 -1
- package/dist/views/CanvasEditor/LayoutContainer.js +49 -70
- package/dist/views/CanvasEditor/components/CustomBlockItem.js +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.d.ts +15 -3
- package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.js +40 -18
- package/dist/views/CanvasEditor/components/EditorLibrary.d.ts +5 -1
- package/dist/views/CanvasEditor/components/EditorLibrary.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorLibrary.js +11 -7
- package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorSidebar.js +32 -14
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -6
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.js +17 -128
- package/dist/views/CanvasEditor/components/JSONInspector.d.ts +9 -0
- package/dist/views/CanvasEditor/components/JSONInspector.d.ts.map +1 -0
- package/dist/views/CanvasEditor/components/JSONInspector.js +56 -0
- package/dist/views/CanvasEditor/components/LibraryItem.js +2 -2
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -4
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.js +6 -28
- package/dist/views/CanvasEditor/components/index.d.ts +2 -0
- package/dist/views/CanvasEditor/components/index.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/index.js +1 -0
- package/dist/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +1 -1
- package/dist/views/CanvasEditor/hooks/useHeroBlock.js +15 -18
- package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts +3 -0
- package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts.map +1 -1
- package/dist/views/CanvasEditor/hooks/usePostLoader.js +12 -13
- package/dist/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -4
- package/dist/views/PostManager/EmptyState.d.ts +1 -1
- package/dist/views/PostManager/EmptyState.js +4 -4
- package/dist/views/PostManager/FilterDropdown.d.ts +21 -0
- package/dist/views/PostManager/FilterDropdown.d.ts.map +1 -0
- package/dist/views/PostManager/FilterDropdown.js +28 -0
- package/dist/views/PostManager/LanguageFlags.d.ts.map +1 -1
- package/dist/views/PostManager/LanguageFlags.js +4 -1
- package/dist/views/PostManager/PostCards.d.ts.map +1 -1
- package/dist/views/PostManager/PostCards.js +23 -40
- package/dist/views/PostManager/PostFilters.d.ts.map +1 -1
- package/dist/views/PostManager/PostFilters.js +34 -3
- package/dist/views/PostManager/PostManagerView.d.ts +1 -2
- package/dist/views/PostManager/PostManagerView.d.ts.map +1 -1
- package/dist/views/PostManager/PostManagerView.js +30 -96
- package/dist/views/PostManager/PostStats.d.ts.map +1 -1
- package/dist/views/PostManager/PostStats.js +10 -10
- package/dist/views/PostManager/PostTable.d.ts.map +1 -1
- package/dist/views/PostManager/PostTable.js +23 -40
- package/dist/views/Settings/SettingsView.d.ts +1 -1
- package/dist/views/Settings/SettingsView.d.ts.map +1 -1
- package/dist/views/Settings/SettingsView.js +12 -39
- package/dist/views/SlugSEO/SlugSEOManagerView.d.ts.map +1 -1
- package/dist/views/SlugSEO/SlugSEOManagerView.js +2 -2
- package/package.json +42 -6
- package/src/api/categories.ts +48 -52
- package/src/api/handler.ts +87 -604
- package/src/api/router.ts +15 -65
- package/src/api/service.ts +241 -0
- package/src/hooks/useAutoSave.ts +64 -0
- package/src/hooks/useCategories.ts +19 -47
- package/src/index.tsx +79 -293
- package/src/init.tsx +24 -11
- package/src/lib/blocks/BlockRenderer.tsx +1 -0
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +60 -173
- package/src/lib/layouts/blocks/SectionBlock.tsx +22 -26
- package/src/lib/layouts/index.ts +4 -4
- package/src/lib/mappers/apiMapper.ts +63 -32
- package/src/lib/rich-text/RichTextEditor.tsx +16 -9
- package/src/lib/utils/config-resolver.ts +64 -0
- package/src/lib/utils/tree.ts +150 -0
- package/src/state/EditorContext.tsx +153 -232
- package/src/state/reducer.ts +141 -606
- package/src/state/types.ts +14 -1
- package/src/types/block.ts +10 -0
- package/src/types/post.ts +19 -1
- package/src/views/CanvasEditor/BlockWrapper.tsx +130 -460
- package/src/views/CanvasEditor/CanvasEditorView.tsx +145 -420
- package/src/views/CanvasEditor/EditorBody.tsx +98 -610
- package/src/views/CanvasEditor/EditorHeader.tsx +176 -196
- package/src/views/CanvasEditor/LayoutContainer.tsx +74 -89
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +7 -8
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +139 -84
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +25 -10
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +196 -127
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +78 -210
- package/src/views/CanvasEditor/components/JSONInspector.tsx +125 -0
- package/src/views/CanvasEditor/components/LibraryItem.tsx +5 -6
- package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +73 -124
- package/src/views/CanvasEditor/components/index.ts +2 -1
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +15 -18
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +21 -13
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +4 -4
- package/src/views/PostManager/EmptyState.tsx +9 -10
- package/src/views/PostManager/FilterDropdown.tsx +95 -0
- package/src/views/PostManager/LanguageFlags.tsx +6 -2
- package/src/views/PostManager/PostCards.tsx +127 -133
- package/src/views/PostManager/PostFilters.tsx +73 -68
- package/src/views/PostManager/PostManagerView.tsx +132 -179
- package/src/views/PostManager/PostStats.tsx +21 -20
- package/src/views/PostManager/PostTable.tsx +137 -165
- package/src/views/Settings/SettingsView.tsx +64 -180
- package/src/views/SlugSEO/SlugSEOManagerView.tsx +59 -44
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.d.ts.map +0 -1
- package/src/hooks/useBlog.d.ts +0 -31
- package/src/hooks/useBlog.d.ts.map +0 -1
- package/src/hooks/useBlogs.d.ts +0 -39
- package/src/hooks/useBlogs.d.ts.map +0 -1
- package/src/hooks/useCategories.d.ts +0 -9
- package/src/hooks/useCategories.d.ts.map +0 -1
- package/src/lib/blocks/BlockRenderer.d.ts +0 -54
- package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
- package/src/lib/config-storage.d.ts +0 -30
- package/src/lib/config-storage.d.ts.map +0 -1
- 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/SectionBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
- package/src/lib/layouts/index.d.ts +0 -23
- package/src/lib/layouts/index.d.ts.map +0 -1
- package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
- package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
- package/src/lib/mappers/apiMapper.d.ts +0 -66
- package/src/lib/mappers/apiMapper.d.ts.map +0 -1
- 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/RichTextPreview.d.ts +0 -16
- package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
- package/src/lib/rich-text/index.d.ts +0 -9
- package/src/lib/rich-text/index.d.ts.map +0 -1
- package/src/lib/utils/blockHelpers.d.ts +0 -23
- package/src/lib/utils/blockHelpers.d.ts.map +0 -1
- package/src/lib/utils/configValidation.d.ts +0 -23
- package/src/lib/utils/configValidation.d.ts.map +0 -1
- package/src/registry/BlockRegistry.d.ts +0 -62
- package/src/registry/BlockRegistry.d.ts.map +0 -1
- package/src/registry/index.d.ts +0 -6
- package/src/registry/index.d.ts.map +0 -1
- package/src/state/EditorContext.d.ts +0 -45
- package/src/state/EditorContext.d.ts.map +0 -1
- package/src/state/index.d.ts +0 -7
- package/src/state/index.d.ts.map +0 -1
- package/src/state/reducer.d.ts +0 -11
- package/src/state/reducer.d.ts.map +0 -1
- package/src/state/types.d.ts +0 -162
- package/src/state/types.d.ts.map +0 -1
- package/src/types/block.d.ts +0 -221
- package/src/types/block.d.ts.map +0 -1
- package/src/types/index.d.ts +0 -8
- package/src/types/index.d.ts.map +0 -1
- package/src/types/post.d.ts +0 -136
- package/src/types/post.d.ts.map +0 -1
- package/src/utils/client.d.ts +0 -48
- package/src/utils/client.d.ts.map +0 -1
- package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
- package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
- package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
- package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
- package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
- package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
- package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
- package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
- 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/EditorCanvas.d.ts +0 -29
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
- 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/EditorSidebar.d.ts +0 -13
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
- 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/FeaturedMediaSection.d.ts +0 -25
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
- 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/PrivacySettingsSection.d.ts +0 -15
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
- 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/hooks/index.d.ts +0 -10
- package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
- 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/useKeyboardShortcuts.d.ts +0 -3
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
- 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/useRegisteredBlocks.d.ts +0 -2
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
- 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/index.d.ts +0 -16
- package/src/views/CanvasEditor/index.d.ts.map +0 -1
- package/src/views/PostManager/EmptyState.d.ts +0 -10
- package/src/views/PostManager/EmptyState.d.ts.map +0 -1
- package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
- package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
- package/src/views/PostManager/PostCards.d.ts +0 -15
- package/src/views/PostManager/PostCards.d.ts.map +0 -1
- package/src/views/PostManager/PostFilters.d.ts +0 -16
- package/src/views/PostManager/PostFilters.d.ts.map +0 -1
- package/src/views/PostManager/PostManagerView.d.ts +0 -11
- package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
- package/src/views/PostManager/PostStats.d.ts +0 -11
- package/src/views/PostManager/PostStats.d.ts.map +0 -1
- package/src/views/PostManager/PostTable.d.ts +0 -15
- package/src/views/PostManager/PostTable.d.ts.map +0 -1
- package/src/views/PostManager/index.d.ts +0 -12
- package/src/views/PostManager/index.d.ts.map +0 -1
- package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
- package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
- package/src/views/Preview/index.d.ts +0 -6
- package/src/views/Preview/index.d.ts.map +0 -1
- package/src/views/Settings/SettingsView.d.ts +0 -10
- package/src/views/Settings/SettingsView.d.ts.map +0 -1
- package/src/views/Settings/index.d.ts +0 -6
- package/src/views/Settings/index.d.ts.map +0 -1
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
- package/src/views/SlugSEO/index.d.ts +0 -6
- package/src/views/SlugSEO/index.d.ts.map +0 -1
|
@@ -1,522 +1,192 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Block Wrapper Component
|
|
3
3
|
* Provides hover controls (Delete, Move, Settings) for each block
|
|
4
|
+
* Simplified to allow LayoutWrapper/Prose to control styling
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
'use client';
|
|
7
8
|
|
|
8
|
-
import React, { useState,
|
|
9
|
-
import { Plus, Trash2, ChevronUp, ChevronDown,
|
|
10
|
-
import { Block } from '../../types/block';
|
|
9
|
+
import React, { useState, useRef, useCallback } from 'react';
|
|
10
|
+
import { Plus, Trash2, ChevronUp, ChevronDown, Copy } from 'lucide-react';
|
|
11
|
+
import { Block, BlockEditProps } from '../../types/block';
|
|
11
12
|
import { blockRegistry } from '../../registry/BlockRegistry';
|
|
12
|
-
import { getChildBlocks, isContainerBlock } from '../../lib/utils/blockHelpers';
|
|
13
13
|
import { useEditor } from '../../state/EditorContext';
|
|
14
14
|
|
|
15
|
-
export interface BlockWrapperProps {
|
|
15
|
+
export interface BlockWrapperProps extends Partial<Omit<BlockEditProps, 'block' | 'onUpdate'>> {
|
|
16
16
|
block: Block;
|
|
17
17
|
onUpdate: (data: Partial<Block['data']>) => void;
|
|
18
|
-
onDelete: () => void;
|
|
19
18
|
onMoveUp?: () => void;
|
|
20
19
|
onMoveDown?: () => void;
|
|
21
|
-
|
|
22
|
-
allBlocks?: Block[];
|
|
20
|
+
onDuplicate?: () => void;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
export function BlockWrapper({
|
|
26
24
|
block,
|
|
27
25
|
onUpdate,
|
|
28
26
|
onDelete,
|
|
27
|
+
onDuplicate,
|
|
29
28
|
onMoveUp,
|
|
30
29
|
onMoveDown,
|
|
31
|
-
|
|
30
|
+
isSelected: isSelectedProp,
|
|
31
|
+
childBlocks,
|
|
32
|
+
onChildBlockAdd,
|
|
33
|
+
onChildBlockUpdate,
|
|
34
|
+
onChildBlockDelete,
|
|
35
|
+
onChildBlockMove,
|
|
32
36
|
}: BlockWrapperProps) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const [isSelectingText, setIsSelectingText] = useState(false);
|
|
37
|
-
const [showSettingsMenu, setShowSettingsMenu] = useState(false);
|
|
38
|
-
const hideTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
39
|
-
const settingsMenuRef = useRef<HTMLDivElement>(null);
|
|
40
|
-
const { helpers, state, dispatch } = useEditor();
|
|
41
|
-
const blockDefinition = blockRegistry.get(block.type);
|
|
37
|
+
const { state, dispatch, translations } = useEditor();
|
|
38
|
+
const hideTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
39
|
+
const definition = blockRegistry.get(block.type);
|
|
42
40
|
|
|
43
|
-
//
|
|
44
|
-
const
|
|
45
|
-
// Get child blocks - if children are Block objects, use them directly
|
|
46
|
-
const childBlocks = isContainer && block.children && Array.isArray(block.children) && block.children.length > 0
|
|
47
|
-
? (typeof block.children[0] === 'object'
|
|
48
|
-
? block.children as Block[]
|
|
49
|
-
: getChildBlocks(block, state.blocks))
|
|
50
|
-
: [];
|
|
41
|
+
// Hero block is permanent and cannot be deleted, moved, or duplicated
|
|
42
|
+
const isHero = block.type === 'hero';
|
|
51
43
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
const shouldShow = isHovered || isControlsHovered;
|
|
44
|
+
// Controls are visible if this block is "selected" (hovered or clicked) AND it's not a hero block
|
|
45
|
+
const isSelected = !isHero && (isSelectedProp || state.selectedBlockId === block.id);
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Show immediately
|
|
63
|
-
setShowControls(true);
|
|
64
|
-
} else {
|
|
65
|
-
// Delay hiding by 500ms
|
|
66
|
-
hideTimeoutRef.current = setTimeout(() => {
|
|
67
|
-
setShowControls(false);
|
|
68
|
-
}, 500);
|
|
47
|
+
const handleMouseEnter = useCallback(() => {
|
|
48
|
+
if (isHero) return; // No hover effects for hero
|
|
49
|
+
|
|
50
|
+
if (hideTimeoutRef.current) {
|
|
51
|
+
clearTimeout(hideTimeoutRef.current);
|
|
52
|
+
hideTimeoutRef.current = null;
|
|
69
53
|
}
|
|
54
|
+
// Immediately select this block on hover, which hides any previous selection
|
|
55
|
+
dispatch({ type: 'SELECT_BLOCK', payload: block.id });
|
|
56
|
+
}, [block.id, dispatch, isHero]);
|
|
70
57
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
clearTimeout(hideTimeoutRef.current);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}, [isHovered, isControlsHovered]);
|
|
58
|
+
const handleMouseLeave = useCallback(() => {
|
|
59
|
+
if (isHero) return;
|
|
77
60
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
61
|
+
// Delay hiding to allow the user to reach the buttons
|
|
62
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
63
|
+
// Only clear if we are still the selected block (handled by reducer now)
|
|
64
|
+
dispatch({ type: 'DESELECT_BLOCK', payload: block.id });
|
|
65
|
+
}, 400);
|
|
66
|
+
}, [block.id, dispatch, isHero]);
|
|
87
67
|
|
|
88
|
-
const
|
|
68
|
+
const [isDraggingLocal, setIsDraggingLocal] = useState(false);
|
|
89
69
|
|
|
90
70
|
const handleDragStart = (e: React.DragEvent) => {
|
|
91
|
-
|
|
92
|
-
const selection = window.getSelection();
|
|
93
|
-
if (selection && selection.toString().length > 0) {
|
|
94
|
-
e.preventDefault();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Prevent dragging if user was selecting text
|
|
99
|
-
if (isSelectingText) {
|
|
100
|
-
e.preventDefault();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Only allow dragging when the block is focused/selected (hovered)
|
|
105
|
-
if (!isHovered && !showControls) {
|
|
71
|
+
if (isHero) {
|
|
106
72
|
e.preventDefault();
|
|
107
73
|
return;
|
|
108
74
|
}
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
blockData: block.data,
|
|
117
|
-
isContainer,
|
|
118
|
-
hasChildren: isContainer && childBlocks.length > 0,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
e.dataTransfer.setData('block-id', block.id);
|
|
122
|
-
e.dataTransfer.setData('block-type', block.type);
|
|
75
|
+
|
|
76
|
+
e.stopPropagation(); // VERY IMPORTANT: Prevent parent containers from also dragging
|
|
77
|
+
|
|
78
|
+
e.dataTransfer.setData('blockId', block.id);
|
|
79
|
+
e.dataTransfer.setData('blockType', block.type);
|
|
80
|
+
// Also set standard text/plain for better cross-browser support
|
|
81
|
+
e.dataTransfer.setData('text/plain', block.id);
|
|
123
82
|
e.dataTransfer.effectAllowed = 'move';
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// Store in a way that persists across components
|
|
83
|
+
|
|
84
|
+
// Also set a global flag for easier access in LayoutContainer
|
|
127
85
|
if (typeof window !== 'undefined') {
|
|
128
86
|
(window as any).__DRAGGED_BLOCK_ID__ = block.id;
|
|
129
|
-
console.log('[BlockWrapper] Stored global dragged block ID:', block.id);
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
// Track text selection
|
|
134
|
-
const handleMouseDown = (e: React.MouseEvent) => {
|
|
135
|
-
// Check if clicking on an input, textarea, or contentEditable element
|
|
136
|
-
const target = e.target as HTMLElement;
|
|
137
|
-
const isEditableElement = target.tagName === 'INPUT' ||
|
|
138
|
-
target.tagName === 'TEXTAREA' ||
|
|
139
|
-
target.isContentEditable ||
|
|
140
|
-
target.closest('input, textarea, [contenteditable="true"]');
|
|
141
|
-
|
|
142
|
-
if (isEditableElement) {
|
|
143
|
-
setIsSelectingText(true);
|
|
144
|
-
// Reset after mouse up
|
|
145
|
-
const handleMouseUp = () => {
|
|
146
|
-
setTimeout(() => {
|
|
147
|
-
const selection = window.getSelection();
|
|
148
|
-
if (!selection || selection.toString().length === 0) {
|
|
149
|
-
setIsSelectingText(false);
|
|
150
|
-
}
|
|
151
|
-
}, 100);
|
|
152
|
-
document.removeEventListener('mouseup', handleMouseUp);
|
|
153
|
-
};
|
|
154
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
// For hero blocks, only show controls when hovering over the image container
|
|
159
|
-
const isHeroBlock = block.type === 'hero';
|
|
160
|
-
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
161
|
-
|
|
162
|
-
const handleMouseMove = (e: React.MouseEvent) => {
|
|
163
|
-
if (isHeroBlock) {
|
|
164
|
-
// For hero blocks, check if mouse is actually over the image container element
|
|
165
|
-
const target = e.target as HTMLElement;
|
|
166
|
-
// Check if we're over the image container (using data attribute for more reliable detection)
|
|
167
|
-
const imageContainer = target.closest('[data-hero-image-container]');
|
|
168
|
-
// Check if we're over an Image component (from plugin-images)
|
|
169
|
-
const imageElement = target.closest('[data-image-id]');
|
|
170
|
-
// Check if we're directly over an img tag
|
|
171
|
-
const isImgTag = target.tagName === 'IMG';
|
|
172
|
-
|
|
173
|
-
const isOverImage = !!(imageContainer || imageElement || isImgTag);
|
|
174
|
-
setIsHovered(isOverImage);
|
|
175
|
-
} else if (!isHeroBlock) {
|
|
176
|
-
setIsHovered(true);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const handleMouseEnter = (e: React.MouseEvent) => {
|
|
181
|
-
if (!isHeroBlock) {
|
|
182
|
-
setIsHovered(true);
|
|
183
|
-
} else {
|
|
184
|
-
// For hero blocks, check position on enter
|
|
185
|
-
handleMouseMove(e);
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const handleMouseLeave = () => {
|
|
190
|
-
setIsHovered(false);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// Close settings menu when clicking outside
|
|
194
|
-
useEffect(() => {
|
|
195
|
-
function handleClickOutside(event: MouseEvent) {
|
|
196
|
-
if (settingsMenuRef.current && !settingsMenuRef.current.contains(event.target as Node)) {
|
|
197
|
-
setShowSettingsMenu(false);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (showSettingsMenu) {
|
|
202
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return () => {
|
|
206
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
207
|
-
};
|
|
208
|
-
}, [showSettingsMenu]);
|
|
209
|
-
|
|
210
|
-
// Generate a unique block ID
|
|
211
|
-
const generateBlockId = (): string => {
|
|
212
|
-
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
213
|
-
return crypto.randomUUID();
|
|
214
87
|
}
|
|
215
|
-
|
|
88
|
+
|
|
89
|
+
// Visual feedback using state instead of direct DOM manipulation
|
|
90
|
+
setIsDraggingLocal(true);
|
|
216
91
|
};
|
|
217
92
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
...blockToClone,
|
|
222
|
-
id: generateBlockId(),
|
|
223
|
-
data: { ...blockToClone.data },
|
|
224
|
-
meta: blockToClone.meta ? { ...blockToClone.meta } : undefined,
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// Handle children if they exist
|
|
228
|
-
if (blockToClone.children) {
|
|
229
|
-
if (Array.isArray(blockToClone.children) && blockToClone.children.length > 0) {
|
|
230
|
-
// Check if children are Block objects or IDs
|
|
231
|
-
if (typeof blockToClone.children[0] === 'object') {
|
|
232
|
-
cloned.children = (blockToClone.children as Block[]).map(cloneBlock);
|
|
233
|
-
} else {
|
|
234
|
-
// If children are IDs, we need to find and clone the actual blocks
|
|
235
|
-
const childBlocks = getChildBlocks(blockToClone, allBlocks.length > 0 ? allBlocks : state.blocks);
|
|
236
|
-
cloned.children = childBlocks.map(cloneBlock);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return cloned;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const handleCopy = () => {
|
|
245
|
-
const clonedBlock = cloneBlock(block);
|
|
246
|
-
// Store in localStorage for persistence across components
|
|
93
|
+
const handleDragEnd = (e: React.DragEvent) => {
|
|
94
|
+
setIsDraggingLocal(false);
|
|
95
|
+
|
|
247
96
|
if (typeof window !== 'undefined') {
|
|
248
|
-
|
|
97
|
+
(window as any).__DRAGGED_BLOCK_ID__ = null;
|
|
249
98
|
}
|
|
250
|
-
setShowSettingsMenu(false);
|
|
251
99
|
};
|
|
252
100
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (typeof window !== 'undefined') {
|
|
257
|
-
(window as any).__BLOG_EDITOR_HOVERED_BLOCK_ID__ = block.id;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}, [isHovered, showControls, block.id]);
|
|
101
|
+
if (!definition) return null;
|
|
102
|
+
|
|
103
|
+
const EditComponent = definition.components.Edit;
|
|
261
104
|
|
|
262
105
|
return (
|
|
263
106
|
<div
|
|
264
|
-
|
|
265
|
-
|
|
107
|
+
className={`relative group/block transition-all duration-200 ${
|
|
108
|
+
isHero ? '' : 'mb-4 cursor-grab active:cursor-grabbing'
|
|
109
|
+
} ${isDraggingLocal ? 'opacity-20 scale-[0.98] grayscale-50 ring-2 ring-primary/20 rounded-2xl shadow-2xl' : ''}`}
|
|
266
110
|
onMouseEnter={handleMouseEnter}
|
|
267
|
-
onMouseMove={isHeroBlock ? handleMouseMove : undefined}
|
|
268
111
|
onMouseLeave={handleMouseLeave}
|
|
269
|
-
|
|
270
|
-
draggable={isHovered || showControls}
|
|
112
|
+
draggable={!isHero}
|
|
271
113
|
onDragStart={handleDragStart}
|
|
272
|
-
|
|
114
|
+
onDragEnd={handleDragEnd}
|
|
115
|
+
data-block-wrapper="true"
|
|
273
116
|
data-block-id={block.id}
|
|
117
|
+
data-block-type={block.type}
|
|
274
118
|
>
|
|
275
|
-
{/*
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}`}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
>
|
|
287
|
-
<Plus size={14} />
|
|
288
|
-
</button>
|
|
289
|
-
|
|
290
|
-
{/* Move Up */}
|
|
291
|
-
{onMoveUp && (
|
|
292
|
-
<button
|
|
293
|
-
onClick={onMoveUp}
|
|
294
|
-
className="p-2 text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white bg-white dark:bg-neutral-800 rounded-lg shadow-sm border border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600 transition-colors"
|
|
295
|
-
title="Move up"
|
|
296
|
-
>
|
|
297
|
-
<ChevronUp size={14} />
|
|
298
|
-
</button>
|
|
299
|
-
)}
|
|
300
|
-
|
|
301
|
-
{/* Move Down */}
|
|
302
|
-
{onMoveDown && (
|
|
303
|
-
<button
|
|
304
|
-
onClick={onMoveDown}
|
|
305
|
-
className="p-2 text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white bg-white dark:bg-neutral-800 rounded-lg shadow-sm border border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600 transition-colors"
|
|
306
|
-
title="Move down"
|
|
307
|
-
>
|
|
308
|
-
<ChevronDown size={14} />
|
|
309
|
-
</button>
|
|
310
|
-
)}
|
|
311
|
-
|
|
312
|
-
{/* Delete */}
|
|
313
|
-
<button
|
|
314
|
-
onClick={onDelete}
|
|
315
|
-
className="p-2 text-neutral-500 dark:text-neutral-400 hover:text-red-500 dark:hover:text-red-400 bg-white dark:bg-neutral-800 rounded-lg shadow-sm border border-neutral-200 dark:border-neutral-700 hover:border-red-500 dark:hover:border-red-500/50 transition-colors"
|
|
316
|
-
title="Delete block"
|
|
119
|
+
{/* Hover Controls - Hidden for Hero Block */}
|
|
120
|
+
{!isHero && (
|
|
121
|
+
<div
|
|
122
|
+
className={`absolute -left-20 top-0 flex flex-col gap-2 transition-all duration-300 z-30 ${isSelected ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}
|
|
123
|
+
onMouseEnter={() => {
|
|
124
|
+
// Prevent hiding when mouse is over the controls themselves
|
|
125
|
+
if (hideTimeoutRef.current) {
|
|
126
|
+
clearTimeout(hideTimeoutRef.current);
|
|
127
|
+
hideTimeoutRef.current = null;
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
317
130
|
>
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
<div
|
|
324
|
-
className={`mb-2 transition-all relative ${isHovered || showControls || showSettingsMenu
|
|
325
|
-
? 'opacity-100 translate-y-0'
|
|
326
|
-
: 'opacity-0 -translate-y-2 pointer-events-none'
|
|
327
|
-
}`}
|
|
328
|
-
onMouseEnter={() => setIsHovered(true)}
|
|
329
|
-
onMouseLeave={() => setIsHovered(false)}
|
|
330
|
-
>
|
|
331
|
-
<div className="flex items-center justify-between px-2 py-1.5 rounded-lg backdrop-blur-sm border bg-neutral-50/95 dark:bg-neutral-800/95 border-neutral-200 dark:border-neutral-800">
|
|
332
|
-
<div className="flex items-center gap-2 flex-1 min-w-0">
|
|
333
|
-
<GripVertical
|
|
334
|
-
size={12}
|
|
335
|
-
className="cursor-grab active:cursor-grabbing shrink-0 text-neutral-400 dark:text-neutral-500"
|
|
336
|
-
/>
|
|
337
|
-
<span className="text-[10px] font-black uppercase tracking-wider shrink-0 text-neutral-500 dark:text-neutral-400">
|
|
338
|
-
{blockDefinition.name}
|
|
339
|
-
</span>
|
|
340
|
-
|
|
341
|
-
{/* Layout Block Settings - Inline in header */}
|
|
342
|
-
{/* {block.type === 'section' && (
|
|
343
|
-
<div className="flex items-center gap-2 ml-4 flex-1 min-w-0">
|
|
344
|
-
<select
|
|
345
|
-
value={(block.data.padding as string) || 'md'}
|
|
346
|
-
onChange={(e) => onUpdate({ padding: e.target.value })}
|
|
347
|
-
onClick={(e) => e.stopPropagation()}
|
|
348
|
-
className="text-[10px] font-bold border px-2 py-1 rounded outline-none focus:border-primary transition-all bg-white dark:bg-neutral-900/50 border-neutral-300 dark:border-neutral-700 dark:text-neutral-100"
|
|
349
|
-
>
|
|
350
|
-
<option value="sm">Small</option>
|
|
351
|
-
<option value="md">Medium</option>
|
|
352
|
-
<option value="lg">Large</option>
|
|
353
|
-
</select>
|
|
354
|
-
<select
|
|
355
|
-
value={(block.data.background as string) || 'DEFAULT'}
|
|
356
|
-
onChange={(e) => onUpdate({ background: e.target.value })}
|
|
357
|
-
onClick={(e) => e.stopPropagation()}
|
|
358
|
-
className="text-[10px] font-bold border px-2 py-1 rounded outline-none focus:border-primary transition-all bg-white dark:bg-neutral-900/50 border-neutral-300 dark:border-neutral-700 dark:text-neutral-100"
|
|
359
|
-
>
|
|
360
|
-
<option value="DEFAULT">Default</option>
|
|
361
|
-
<option value="NEUTRAL">Neutral</option>
|
|
362
|
-
<option value="SAGE">Sage</option>
|
|
363
|
-
<option value="CREAM">Cream</option>
|
|
364
|
-
</select>
|
|
365
|
-
</div>
|
|
366
|
-
)} */}
|
|
367
|
-
|
|
368
|
-
{block.type === 'columns' && (() => {
|
|
369
|
-
const columnCount = block.data.columnCount as number | undefined;
|
|
370
|
-
const layout = block.data.layout as string | undefined;
|
|
371
|
-
|
|
372
|
-
// Determine number of columns
|
|
373
|
-
let numColumns: number;
|
|
374
|
-
if (columnCount !== undefined && columnCount > 0) {
|
|
375
|
-
numColumns = columnCount;
|
|
376
|
-
} else if (layout) {
|
|
377
|
-
// Legacy layout system
|
|
378
|
-
const layoutMap: Record<string, number> = {
|
|
379
|
-
'50-50': 2,
|
|
380
|
-
'33-66': 2,
|
|
381
|
-
'66-33': 2,
|
|
382
|
-
'25-25-25-25': 4,
|
|
383
|
-
'25-75': 2,
|
|
384
|
-
'75-25': 2,
|
|
385
|
-
};
|
|
386
|
-
numColumns = layoutMap[layout] || 2;
|
|
387
|
-
} else {
|
|
388
|
-
numColumns = 2;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Get column widths
|
|
392
|
-
const storedWidths = block.data.columnWidths as number[] | undefined;
|
|
393
|
-
const columnWidths = storedWidths && storedWidths.length === numColumns
|
|
394
|
-
? storedWidths
|
|
395
|
-
: Array(numColumns).fill(Math.floor(100 / numColumns));
|
|
396
|
-
|
|
397
|
-
return (
|
|
398
|
-
<div className="flex items-center gap-1.5 ml-4 flex-1 min-w-0">
|
|
399
|
-
{Array.from({ length: numColumns }).map((_, colIndex) => (
|
|
400
|
-
<div key={colIndex} className="flex items-center gap-1">
|
|
401
|
-
<input
|
|
402
|
-
type="number"
|
|
403
|
-
min="10"
|
|
404
|
-
max="90"
|
|
405
|
-
step="1"
|
|
406
|
-
value={columnWidths[colIndex] || 50}
|
|
407
|
-
onChange={(e) => {
|
|
408
|
-
const newWidth = parseInt(e.target.value) || 50;
|
|
409
|
-
const newWidths = [...columnWidths];
|
|
410
|
-
newWidths[colIndex] = Math.max(10, Math.min(90, newWidth));
|
|
411
|
-
|
|
412
|
-
// Normalize remaining columns to sum to 100
|
|
413
|
-
const remainingTotal = newWidths.reduce((sum, w, i) => i === colIndex ? sum : sum + w, 0);
|
|
414
|
-
const remainingTarget = 100 - newWidths[colIndex];
|
|
415
|
-
if (remainingTotal > 0 && remainingTarget > 0) {
|
|
416
|
-
const scale = remainingTarget / remainingTotal;
|
|
417
|
-
newWidths.forEach((w, i) => {
|
|
418
|
-
if (i !== colIndex) {
|
|
419
|
-
newWidths[i] = Math.round(w * scale);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Ensure sum is exactly 100
|
|
425
|
-
const finalTotal = newWidths.reduce((sum, w) => sum + w, 0);
|
|
426
|
-
if (finalTotal !== 100) {
|
|
427
|
-
const diff = 100 - finalTotal;
|
|
428
|
-
const lastIndex = newWidths.length - 1;
|
|
429
|
-
newWidths[lastIndex] = Math.max(10, newWidths[lastIndex] + diff);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
onUpdate({
|
|
433
|
-
...block.data,
|
|
434
|
-
columnWidths: newWidths,
|
|
435
|
-
layout: undefined, // Clear layout when using custom widths
|
|
436
|
-
});
|
|
437
|
-
}}
|
|
438
|
-
onClick={(e) => e.stopPropagation()}
|
|
439
|
-
className="w-12 text-[10px] font-bold bg-white dark:bg-neutral-900/50 border border-neutral-300 dark:border-neutral-700 px-1.5 py-0.5 rounded outline-none focus:border-primary transition-all dark:text-neutral-100 text-center"
|
|
440
|
-
/>
|
|
441
|
-
<span className="text-[9px] text-neutral-400 dark:text-neutral-500">%</span>
|
|
442
|
-
</div>
|
|
443
|
-
))}
|
|
444
|
-
</div>
|
|
445
|
-
);
|
|
446
|
-
})()}
|
|
447
|
-
</div>
|
|
448
|
-
<div className="relative shrink-0 z-20" ref={settingsMenuRef}>
|
|
449
|
-
<button
|
|
450
|
-
onClick={(e) => {
|
|
451
|
-
e.stopPropagation();
|
|
452
|
-
setShowSettingsMenu(!showSettingsMenu);
|
|
453
|
-
}}
|
|
454
|
-
className="p-1 rounded transition-colors text-neutral-400 dark:text-neutral-500 hover:text-neutral-950 dark:hover:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800 relative z-20"
|
|
455
|
-
title="Block settings"
|
|
131
|
+
{onMoveUp && (
|
|
132
|
+
<button
|
|
133
|
+
onClick={onMoveUp}
|
|
134
|
+
className="p-2.5 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-xl text-neutral-400 hover:text-primary hover:border-primary shadow-xl transition-all hover:scale-110 active:scale-95"
|
|
135
|
+
title="Move Up"
|
|
456
136
|
>
|
|
457
|
-
<
|
|
137
|
+
<ChevronUp size={18} strokeWidth={2.5} />
|
|
458
138
|
</button>
|
|
459
|
-
|
|
460
|
-
{/* Settings Dropdown Menu */}
|
|
461
|
-
{showSettingsMenu && (
|
|
462
|
-
<div className="absolute right-0 top-full mt-1 w-40 bg-white dark:bg-neutral-900 border border-neutral-300 dark:border-neutral-700 rounded-lg shadow-xl z-20 overflow-hidden">
|
|
463
|
-
<button
|
|
464
|
-
onClick={(e) => {
|
|
465
|
-
e.stopPropagation();
|
|
466
|
-
handleCopy();
|
|
467
|
-
}}
|
|
468
|
-
className="w-full flex items-center gap-2 px-3 py-2 text-xs font-bold text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
|
469
|
-
>
|
|
470
|
-
<Copy size={14} />
|
|
471
|
-
<span>Copy</span>
|
|
472
|
-
</button>
|
|
473
|
-
</div>
|
|
474
|
-
)}
|
|
475
|
-
</div>
|
|
476
|
-
</div>
|
|
477
|
-
</div>
|
|
478
|
-
|
|
479
|
-
{/* Block Content Wrapper - Always visible border, minimal padding */}
|
|
480
|
-
<div
|
|
481
|
-
className={`relative rounded-xl border-2 transition-all ${showControls
|
|
482
|
-
? 'border-primary/60 dark:border-primary/40 bg-primary/5 dark:bg-primary/10'
|
|
483
|
-
: 'border-neutral-200 dark:border-neutral-700 bg-transparent'
|
|
484
|
-
}`}
|
|
485
|
-
>
|
|
486
|
-
{/* Edit Component - No padding, let the component control its own spacing */}
|
|
487
|
-
<div className="relative" style={{ userSelect: 'text' }}>
|
|
488
|
-
{/* For container blocks, pass child blocks and handlers */}
|
|
489
|
-
{isContainer && (block.type === 'section' || block.type === 'columns') ? (
|
|
490
|
-
<EditComponent
|
|
491
|
-
block={block}
|
|
492
|
-
onUpdate={onUpdate}
|
|
493
|
-
onDelete={onDelete}
|
|
494
|
-
isSelected={isHovered}
|
|
495
|
-
childBlocks={childBlocks}
|
|
496
|
-
onChildBlockAdd={(type, index, containerId) => {
|
|
497
|
-
helpers.addBlock(type, index, containerId || block.id);
|
|
498
|
-
}}
|
|
499
|
-
onChildBlockUpdate={(id, data, containerId) => {
|
|
500
|
-
helpers.updateBlock(id, data);
|
|
501
|
-
}}
|
|
502
|
-
onChildBlockDelete={(id, containerId) => {
|
|
503
|
-
helpers.deleteBlock(id);
|
|
504
|
-
}}
|
|
505
|
-
onChildBlockMove={(id, newIndex, containerId) => {
|
|
506
|
-
helpers.moveBlock(id, newIndex, containerId || block.id);
|
|
507
|
-
}}
|
|
508
|
-
/>
|
|
509
|
-
) : (
|
|
510
|
-
<EditComponent
|
|
511
|
-
block={block}
|
|
512
|
-
onUpdate={onUpdate}
|
|
513
|
-
onDelete={onDelete}
|
|
514
|
-
isSelected={isHovered}
|
|
515
|
-
/>
|
|
516
139
|
)}
|
|
140
|
+
{onMoveDown && (
|
|
141
|
+
<button
|
|
142
|
+
onClick={onMoveDown}
|
|
143
|
+
className="p-2.5 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-xl text-neutral-400 hover:text-primary hover:border-primary shadow-xl transition-all hover:scale-110 active:scale-95"
|
|
144
|
+
title="Move Down"
|
|
145
|
+
>
|
|
146
|
+
<ChevronDown size={18} strokeWidth={2.5} />
|
|
147
|
+
</button>
|
|
148
|
+
)}
|
|
149
|
+
{onDuplicate && (
|
|
150
|
+
<button
|
|
151
|
+
onClick={onDuplicate}
|
|
152
|
+
className="p-2.5 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-xl text-neutral-400 hover:text-primary hover:border-primary shadow-xl transition-all hover:scale-110 active:scale-95"
|
|
153
|
+
title="Duplicate Block"
|
|
154
|
+
>
|
|
155
|
+
<Copy size={18} strokeWidth={2.5} />
|
|
156
|
+
</button>
|
|
157
|
+
)}
|
|
158
|
+
<button
|
|
159
|
+
onClick={onDelete}
|
|
160
|
+
className="p-2.5 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-xl text-neutral-400 hover:text-red-500 hover:border-red-500 shadow-xl transition-all hover:scale-110 active:scale-95"
|
|
161
|
+
title="Delete Block"
|
|
162
|
+
>
|
|
163
|
+
<Trash2 size={18} strokeWidth={2.5} />
|
|
164
|
+
</button>
|
|
517
165
|
</div>
|
|
166
|
+
)}
|
|
167
|
+
|
|
168
|
+
{/* The Actual Editable Block Content */}
|
|
169
|
+
<div className="relative">
|
|
170
|
+
<EditComponent
|
|
171
|
+
block={block}
|
|
172
|
+
onUpdate={onUpdate}
|
|
173
|
+
isSelected={isSelected}
|
|
174
|
+
context={{
|
|
175
|
+
locale: state.currentLanguage || undefined,
|
|
176
|
+
translations
|
|
177
|
+
}}
|
|
178
|
+
childBlocks={childBlocks}
|
|
179
|
+
onChildBlockAdd={onChildBlockAdd}
|
|
180
|
+
onChildBlockUpdate={onChildBlockUpdate}
|
|
181
|
+
onChildBlockDelete={onChildBlockDelete}
|
|
182
|
+
onChildBlockMove={onChildBlockMove}
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
{/* Visual indicator of the block bounds when hovered - also hidden for Hero */}
|
|
186
|
+
{!isHero && (
|
|
187
|
+
<div className={`absolute -inset-x-4 -inset-y-2 border-2 border-primary/0 rounded-2xl pointer-events-none transition-all duration-300 ${isSelected ? 'border-primary/10' : ''}`} />
|
|
188
|
+
)}
|
|
518
189
|
</div>
|
|
519
190
|
</div>
|
|
520
191
|
);
|
|
521
192
|
}
|
|
522
|
-
|