import { type IBlock } from './wysiwyg.types.js'; export class WysiwygDragDropHandler { private component: any; private draggedBlockId: string | null = null; private dragOverBlockId: string | null = null; private dragOverPosition: 'before' | 'after' | null = null; constructor(component: any) { this.component = component; } /** * Gets the current drag state */ get dragState() { return { draggedBlockId: this.draggedBlockId, dragOverBlockId: this.dragOverBlockId, dragOverPosition: this.dragOverPosition }; } /** * Handles drag start */ handleDragStart(e: DragEvent, block: IBlock): void { if (!e.dataTransfer) return; this.draggedBlockId = block.id; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', block.id); // Update UI state this.updateComponentState(); // Add slight delay to show dragging state setTimeout(() => { this.component.requestUpdate(); }, 10); } /** * Handles drag end */ handleDragEnd(): void { this.draggedBlockId = null; this.dragOverBlockId = null; this.dragOverPosition = null; this.updateComponentState(); this.component.requestUpdate(); } /** * Handles drag over */ handleDragOver(e: DragEvent, block: IBlock): void { e.preventDefault(); if (!e.dataTransfer || !this.draggedBlockId || this.draggedBlockId === block.id) return; e.dataTransfer.dropEffect = 'move'; const rect = (e.currentTarget as HTMLElement).getBoundingClientRect(); const midpoint = rect.top + rect.height / 2; this.dragOverBlockId = block.id; this.dragOverPosition = e.clientY < midpoint ? 'before' : 'after'; this.updateComponentState(); this.component.requestUpdate(); } /** * Handles drag leave */ handleDragLeave(block: IBlock): void { if (this.dragOverBlockId === block.id) { this.dragOverBlockId = null; this.dragOverPosition = null; this.updateComponentState(); this.component.requestUpdate(); } } /** * Handles drop */ handleDrop(e: DragEvent, targetBlock: IBlock): void { e.preventDefault(); if (!this.draggedBlockId || this.draggedBlockId === targetBlock.id) return; const blocks = [...this.component.blocks]; const draggedIndex = blocks.findIndex(b => b.id === this.draggedBlockId); const targetIndex = blocks.findIndex(b => b.id === targetBlock.id); if (draggedIndex === -1 || targetIndex === -1) return; // Remove the dragged block const [draggedBlock] = blocks.splice(draggedIndex, 1); // Calculate the new index let newIndex = targetIndex; if (this.dragOverPosition === 'after') { newIndex = draggedIndex < targetIndex ? targetIndex : targetIndex + 1; } else { newIndex = draggedIndex < targetIndex ? targetIndex - 1 : targetIndex; } // Insert at new position blocks.splice(newIndex, 0, draggedBlock); // Update blocks this.component.blocks = blocks; this.component.updateValue(); this.handleDragEnd(); // Focus the moved block setTimeout(() => { const blockOps = this.component.blockOperations; if (draggedBlock.type !== 'divider') { blockOps.focusBlock(draggedBlock.id); } }, 100); } /** * Updates component drag state */ private updateComponentState(): void { this.component.draggedBlockId = this.draggedBlockId; this.component.dragOverBlockId = this.dragOverBlockId; this.component.dragOverPosition = this.dragOverPosition; } /** * Checks if a block is being dragged */ isDragging(blockId: string): boolean { return this.draggedBlockId === blockId; } /** * Checks if a block has drag over state */ isDragOver(blockId: string): boolean { return this.dragOverBlockId === blockId; } /** * Gets drag over CSS classes for a block */ getDragOverClasses(blockId: string): string { if (!this.isDragOver(blockId)) return ''; return this.dragOverPosition === 'before' ? 'drag-over-before' : 'drag-over-after'; } }