You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
Divi-Like Visual Builder Plan
Overview
Transform the current PDF editor into a Divi-inspired visual builder with intuitive inline controls, drag-and-drop functionality, and seamless element manipulation.
Current State vs Target State
Current State
- Toolbar-based insertion from sidebar
- Basic selection with outline highlighting
- No inline insertion controls
- No drag-and-drop
- No hover action menus
Target State (Divi-like)
- Inline '+' buttons between elements for contextual insertion
- Hover action menus with settings, duplicate, delete, move
- Full drag-and-drop support with visual feedback
- Ghost previews and drop zone indicators
Feature 1: Inline Insertion System
Description
Add '+' buttons that appear between elements and at the end of containers, allowing users to insert new elements at specific positions.
Visual Design
┌─────────────────────────────────────┐
│ Section │
│ ┌─────────────────────────────────┐ │
│ │ Row │ │
│ │ ┌───────────┐ ┌───────────┐ │ │
│ │ │ Column │ │ Column │ │ │
│ │ │ [Text] │ │ [Image] │ │ │
│ │ │ │ │ │ │ │
│ │ │ [+] │ │ [+] │ │ │
│ │ └───────────┘ └───────────┘ │ │
│ │ [+] │ │ ← Add column button
│ └─────────────────────────────────┘ │
│ [+] │ ← Add row button
└─────────────────────────────────────┘
Components Needed
InsertButton.svelte
interface Props {
position: 'before' | 'after' | 'inside'
parentId: string
index: number
allowedTypes: NodeType[]
oninsert: (type: NodeType, parentId: string, index: number) => void
}
InsertionPopup.svelte
- Appears when clicking '+'
- Shows allowed element types based on context
- Respects hierarchy rules
Behavior
- Insert buttons appear on hover over containers
- Clicking '+' shows a popup with allowed types
- Selecting a type inserts at that exact position
- Buttons are context-aware (only show valid options)
Feature 2: Hover Action Menu
Description
Display a floating toolbar when hovering over elements with quick actions.
Visual Design
┌─────────────────────────────────────┐
│ [⚙️] [📋] [🗑️] [⋮] ← Action menu │
├─────────────────────────────────────┤
│ │
│ Element Content │
│ │
└─────────────────────────────────────┘
Actions
| Icon | Action | Description |
|---|---|---|
| ⚙️ | Settings | Open properties panel for this element |
| 📋 | Duplicate | Clone the element |
| 🗑️ | Delete | Remove the element |
| ⋮ | More | Expand for additional options (move up/down, copy, paste) |
Components Needed
ElementActions.svelte
interface Props {
nodeId: string
nodeType: NodeType
position: { x: number; y: number }
onsettings: () => void
onduplicate: () => void
ondelete: () => void
onmoveup: () => void
onmovedown: () => void
}
Behavior
- Menu appears on element hover (with slight delay)
- Menu positioned at top-right of element
- Actions are context-aware (some may be disabled)
- Menu disappears when mouse leaves element
Feature 3: Drag and Drop System
Description
Allow elements to be reordered by dragging them to new positions.
Visual Design
Before Drop:
┌─────────────┐
│ Element A │ ← Being dragged (ghost)
└─────────────┘
┌─────────────┐
│ Element B │
└─────────────┘
┌─────────────┐
│ Element C │
└─────────────┘
↓
Drop zone indicator (blue line)
After Drop:
┌─────────────┐
│ Element B │
└─────────────┘
┌─────────────┐
│ Element A │ ← Dropped here
└─────────────┘
┌─────────────┐
│ Element C │
└─────────────┘
Components Needed
Draggable.svelte
interface Props {
nodeId: string
disabled?: boolean
ondragstart: (nodeId: string, event: DragEvent) => void
ondragend: (nodeId: string, event: DragEvent) => void
}
DropZone.svelte
interface Props {
parentId: string
index: number
acceptedTypes: NodeType[]
ondrop: (draggedId: string, parentId: string, index: number) => void
}
Behavior
- Click and hold to start drag (with drag handle icon)
- Element becomes semi-transparent ghost
- Drop zones appear as blue indicator lines
- Invalid drop zones shown in red
- Release to drop at indicated position
- Escape cancels the drag operation
Feature 4: Visual Feedback System
Description
Provide clear visual feedback during all interactions.
Feedback Types
Hover State
- Outline brightens
- Action menu appears
- Cursor changes to pointer
Selection State
- Solid border (current)
- Resize handles (future)
Drag State
- Original element becomes ghost (50% opacity)
- Drag preview follows cursor
- Drop zones highlighted
Drop Zone State
- Blue line indicator for valid drops
- Red indicator for invalid drops
- Pulsing animation for active zone
Insert Button State
- Fade in on container hover
- Scale up on button hover
- Tooltip showing 'Add [element type]'
Implementation Architecture
New File Structure
src/lib/editor/
├── canvas/
│ ├── Canvas.svelte
│ ├── NodeRenderer.svelte
│ ├── Page.svelte
│ ├── InsertButton.svelte # NEW
│ ├── InsertionPopup.svelte # NEW
│ ├── ElementActions.svelte # NEW
│ ├── DraggableWrapper.svelte # NEW
│ └── DropZone.svelte # NEW
├── toolbar/
│ ├── Toolbar.svelte
│ ├── BlockButton.svelte
│ └── ElementQuickMenu.svelte # NEW
├── stores/
│ ├── drag.ts # NEW - drag state store
│ └── ...
└── Editor.svelte
Store Additions
dragStore (new)
interface DragState {
isDragging: boolean
draggedNodeId: string | null
draggedNodeType: NodeType | null
dropTargetId: string | null
dropIndex: number | null
isValidDrop: boolean
}
Event Flow Diagram
sequenceDiagram
participant User
participant NodeRenderer
participant ElementActions
participant Editor
participant DocumentStore
User->>NodeRenderer: Hover over element
NodeRenderer->>ElementActions: Show action menu
User->>ElementActions: Click drag handle
ElementActions->>Editor: Start drag
Editor->>DocumentStore: Set drag state
User->>NodeRenderer: Drag over drop zone
NodeRenderer->>Editor: Check valid drop
Editor->>DocumentStore: Update drop target
User->>NodeRenderer: Drop
NodeRenderer->>Editor: Complete drop
Editor->>DocumentStore: Move node
DocumentStore->>NodeRenderer: Re-render
Implementation Phases
Phase 1: Inline Insertion
- Create InsertButton.svelte component
- Create InsertionPopup.svelte with element selector
- Modify NodeRenderer to show insert buttons
- Wire up insertion logic with correct positioning
Phase 2: Hover Actions
- Create ElementActions.svelte component
- Add hover detection with delay
- Implement duplicate, delete, settings actions
- Add move up/down functionality
Phase 3: Drag and Drop
- Create drag store for state management
- Create DraggableWrapper.svelte
- Create DropZone.svelte
- Implement drop validation logic
- Add visual feedback during drag
Phase 4: Polish
- Add animations and transitions
- Implement keyboard shortcuts
- Add undo/redo support for all operations
- Mobile touch support (future)
Technical Considerations
Performance
- Use CSS transforms for drag preview (GPU accelerated)
- Debounce hover events
- Virtualize insertion popups (only one open at a time)
Accessibility
- Keyboard navigation for action menus
- Focus management during drag
- ARIA labels for all interactive elements
Browser Compatibility
- Use HTML5 Drag and Drop API
- Fallback for touch devices
- Handle Safari quirks
Files to Modify
Existing Files
| File | Changes |
|---|---|
src/lib/editor/Editor.svelte |
Add drag state, coordinate drop logic |
src/lib/editor/canvas/NodeRenderer.svelte |
Add action menu, insert buttons, drag handlers |
src/lib/stores/index.ts |
Export new drag store |
New Files to Create
| File | Purpose |
|---|---|
src/lib/editor/canvas/InsertButton.svelte |
Inline insertion trigger |
src/lib/editor/canvas/InsertionPopup.svelte |
Element type selector |
src/lib/editor/canvas/ElementActions.svelte |
Hover action toolbar |
src/lib/editor/canvas/DropZone.svelte |
Drop target indicator |
src/lib/stores/drag.ts |
Drag state management |
Success Criteria
- ✅ Users can insert elements at specific positions using inline buttons
- ✅ Hover menus provide quick access to common actions
- ✅ Drag and drop works smoothly with visual feedback
- ✅ All operations respect hierarchy constraints
- ✅ All operations are undoable
- ✅ Keyboard users can perform all actions