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.
362 lines
10 KiB
362 lines
10 KiB
|
7 months ago
|
# 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
|
||
|
|
```typescript
|
||
|
|
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
|
||
|
|
1. Insert buttons appear on hover over containers
|
||
|
|
2. Clicking '+' shows a popup with allowed types
|
||
|
|
3. Selecting a type inserts at that exact position
|
||
|
|
4. 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
|
||
|
|
```typescript
|
||
|
|
interface Props {
|
||
|
|
nodeId: string
|
||
|
|
nodeType: NodeType
|
||
|
|
position: { x: number; y: number }
|
||
|
|
onsettings: () => void
|
||
|
|
onduplicate: () => void
|
||
|
|
ondelete: () => void
|
||
|
|
onmoveup: () => void
|
||
|
|
onmovedown: () => void
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Behavior
|
||
|
|
1. Menu appears on element hover (with slight delay)
|
||
|
|
2. Menu positioned at top-right of element
|
||
|
|
3. Actions are context-aware (some may be disabled)
|
||
|
|
4. 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
|
||
|
|
```typescript
|
||
|
|
interface Props {
|
||
|
|
nodeId: string
|
||
|
|
disabled?: boolean
|
||
|
|
ondragstart: (nodeId: string, event: DragEvent) => void
|
||
|
|
ondragend: (nodeId: string, event: DragEvent) => void
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### DropZone.svelte
|
||
|
|
```typescript
|
||
|
|
interface Props {
|
||
|
|
parentId: string
|
||
|
|
index: number
|
||
|
|
acceptedTypes: NodeType[]
|
||
|
|
ondrop: (draggedId: string, parentId: string, index: number) => void
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Behavior
|
||
|
|
1. Click and hold to start drag (with drag handle icon)
|
||
|
|
2. Element becomes semi-transparent ghost
|
||
|
|
3. Drop zones appear as blue indicator lines
|
||
|
|
4. Invalid drop zones shown in red
|
||
|
|
5. Release to drop at indicated position
|
||
|
|
6. 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)
|
||
|
|
```typescript
|
||
|
|
interface DragState {
|
||
|
|
isDragging: boolean
|
||
|
|
draggedNodeId: string | null
|
||
|
|
draggedNodeType: NodeType | null
|
||
|
|
dropTargetId: string | null
|
||
|
|
dropIndex: number | null
|
||
|
|
isValidDrop: boolean
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Event Flow Diagram
|
||
|
|
|
||
|
|
```mermaid
|
||
|
|
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
|
||
|
|
1. Create InsertButton.svelte component
|
||
|
|
2. Create InsertionPopup.svelte with element selector
|
||
|
|
3. Modify NodeRenderer to show insert buttons
|
||
|
|
4. Wire up insertion logic with correct positioning
|
||
|
|
|
||
|
|
### Phase 2: Hover Actions
|
||
|
|
1. Create ElementActions.svelte component
|
||
|
|
2. Add hover detection with delay
|
||
|
|
3. Implement duplicate, delete, settings actions
|
||
|
|
4. Add move up/down functionality
|
||
|
|
|
||
|
|
### Phase 3: Drag and Drop
|
||
|
|
1. Create drag store for state management
|
||
|
|
2. Create DraggableWrapper.svelte
|
||
|
|
3. Create DropZone.svelte
|
||
|
|
4. Implement drop validation logic
|
||
|
|
5. Add visual feedback during drag
|
||
|
|
|
||
|
|
### Phase 4: Polish
|
||
|
|
1. Add animations and transitions
|
||
|
|
2. Implement keyboard shortcuts
|
||
|
|
3. Add undo/redo support for all operations
|
||
|
|
4. 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
|
||
|
|
|
||
|
|
1. ✅ Users can insert elements at specific positions using inline buttons
|
||
|
|
2. ✅ Hover menus provide quick access to common actions
|
||
|
|
3. ✅ Drag and drop works smoothly with visual feedback
|
||
|
|
4. ✅ All operations respect hierarchy constraints
|
||
|
|
5. ✅ All operations are undoable
|
||
|
|
6. ✅ Keyboard users can perform all actions
|