2025-06-24 08:19:53 +00:00
|
|
|
import { type IBlock } from './wysiwyg.types.js';
|
2025-06-24 13:41:12 +00:00
|
|
|
import { type IWysiwygComponent } from './wysiwyg.interfaces.js';
|
2025-06-24 08:19:53 +00:00
|
|
|
|
|
|
|
export class WysiwygDragDropHandler {
|
2025-06-24 13:41:12 +00:00
|
|
|
private component: IWysiwygComponent;
|
2025-06-24 08:19:53 +00:00
|
|
|
private draggedBlockId: string | null = null;
|
|
|
|
private dragOverBlockId: string | null = null;
|
|
|
|
private dragOverPosition: 'before' | 'after' | null = null;
|
2025-06-24 12:24:02 +00:00
|
|
|
private dropIndicator: HTMLElement | null = null;
|
|
|
|
private initialMouseY: number = 0;
|
|
|
|
private initialBlockY: number = 0;
|
|
|
|
private draggedBlockElement: HTMLElement | null = null;
|
|
|
|
private draggedBlockHeight: number = 0;
|
2025-06-29 14:00:55 +00:00
|
|
|
private draggedBlockContentHeight: number = 0;
|
|
|
|
private draggedBlockMarginTop: number = 0;
|
2025-06-24 12:24:02 +00:00
|
|
|
private lastUpdateTime: number = 0;
|
|
|
|
private updateThrottle: number = 80; // milliseconds
|
2025-06-24 08:19:53 +00:00
|
|
|
|
2025-06-24 13:41:12 +00:00
|
|
|
constructor(component: IWysiwygComponent) {
|
2025-06-24 08:19:53 +00:00
|
|
|
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);
|
|
|
|
|
2025-06-24 12:24:02 +00:00
|
|
|
// Hide the default drag image
|
|
|
|
const emptyImg = new Image();
|
|
|
|
emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=';
|
|
|
|
e.dataTransfer.setDragImage(emptyImg, 0, 0);
|
|
|
|
|
|
|
|
// Store initial mouse position and block element
|
|
|
|
this.initialMouseY = e.clientY;
|
|
|
|
this.draggedBlockElement = this.component.editorContentRef.querySelector(`[data-block-id="${block.id}"]`);
|
|
|
|
|
2025-06-29 14:00:55 +00:00
|
|
|
|
2025-06-24 12:24:02 +00:00
|
|
|
if (this.draggedBlockElement) {
|
2025-06-29 14:00:55 +00:00
|
|
|
// Get the wrapper rect for measurements
|
2025-06-24 12:24:02 +00:00
|
|
|
const rect = this.draggedBlockElement.getBoundingClientRect();
|
|
|
|
this.initialBlockY = rect.top;
|
|
|
|
|
2025-06-29 14:00:55 +00:00
|
|
|
// Get the inner block element for proper measurements
|
|
|
|
const innerBlock = this.draggedBlockElement.querySelector('.block');
|
|
|
|
if (innerBlock) {
|
|
|
|
const innerRect = innerBlock.getBoundingClientRect();
|
|
|
|
const computedStyle = window.getComputedStyle(innerBlock);
|
|
|
|
this.draggedBlockMarginTop = parseInt(computedStyle.marginTop) || 0;
|
|
|
|
this.draggedBlockContentHeight = innerRect.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The drop indicator should match the wrapper height exactly
|
|
|
|
// The wrapper already includes all the space the block occupies
|
|
|
|
this.draggedBlockHeight = rect.height;
|
|
|
|
|
|
|
|
console.log('Drag measurements:', {
|
|
|
|
wrapperHeight: rect.height,
|
|
|
|
marginTop: this.draggedBlockMarginTop,
|
|
|
|
dropIndicatorHeight: this.draggedBlockHeight,
|
|
|
|
contentHeight: this.draggedBlockContentHeight,
|
|
|
|
blockId: block.id
|
|
|
|
});
|
|
|
|
|
2025-06-24 12:24:02 +00:00
|
|
|
// Create drop indicator
|
|
|
|
this.createDropIndicator();
|
|
|
|
|
|
|
|
// Set up drag event listeners
|
|
|
|
document.addEventListener('dragover', this.handleGlobalDragOver);
|
|
|
|
document.addEventListener('dragend', this.handleGlobalDragEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update component state
|
2025-06-24 11:12:56 +00:00
|
|
|
this.component.draggedBlockId = this.draggedBlockId;
|
2025-06-24 08:19:53 +00:00
|
|
|
|
2025-06-24 12:24:02 +00:00
|
|
|
// Add dragging class after a small delay
|
|
|
|
setTimeout(() => {
|
|
|
|
if (this.draggedBlockElement) {
|
|
|
|
this.draggedBlockElement.classList.add('dragging');
|
|
|
|
}
|
|
|
|
if (this.component.editorContentRef) {
|
|
|
|
this.component.editorContentRef.classList.add('dragging');
|
|
|
|
}
|
|
|
|
}, 10);
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles drag end
|
|
|
|
*/
|
|
|
|
handleDragEnd(): void {
|
2025-06-24 12:24:02 +00:00
|
|
|
// Clean up visual state
|
|
|
|
const allBlocks = this.component.editorContentRef.querySelectorAll('.block-wrapper');
|
|
|
|
allBlocks.forEach((block: HTMLElement) => {
|
|
|
|
block.classList.remove('dragging', 'move-up', 'move-down');
|
|
|
|
block.style.removeProperty('--drag-offset');
|
|
|
|
block.style.removeProperty('transform');
|
|
|
|
});
|
|
|
|
|
|
|
|
// Remove dragging class from editor
|
|
|
|
if (this.component.editorContentRef) {
|
|
|
|
this.component.editorContentRef.classList.remove('dragging');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset drag state
|
2025-06-24 08:19:53 +00:00
|
|
|
this.draggedBlockId = null;
|
|
|
|
this.dragOverBlockId = null;
|
|
|
|
this.dragOverPosition = null;
|
2025-06-24 12:24:02 +00:00
|
|
|
this.draggedBlockElement = null;
|
|
|
|
this.draggedBlockHeight = 0;
|
2025-06-29 14:00:55 +00:00
|
|
|
this.draggedBlockContentHeight = 0;
|
|
|
|
this.draggedBlockMarginTop = 0;
|
2025-06-24 12:24:02 +00:00
|
|
|
this.initialBlockY = 0;
|
2025-06-24 11:12:56 +00:00
|
|
|
|
|
|
|
// Update component state
|
|
|
|
this.component.draggedBlockId = null;
|
|
|
|
this.component.dragOverBlockId = null;
|
|
|
|
this.component.dragOverPosition = null;
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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';
|
2025-06-24 11:12:56 +00:00
|
|
|
|
|
|
|
// Update component state
|
|
|
|
this.component.dragOverBlockId = this.dragOverBlockId;
|
|
|
|
this.component.dragOverPosition = this.dragOverPosition;
|
|
|
|
|
|
|
|
// The parent component already handles drag-over classes programmatically
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles drag leave
|
|
|
|
*/
|
|
|
|
handleDragLeave(block: IBlock): void {
|
|
|
|
if (this.dragOverBlockId === block.id) {
|
|
|
|
this.dragOverBlockId = null;
|
|
|
|
this.dragOverPosition = null;
|
2025-06-24 11:12:56 +00:00
|
|
|
|
|
|
|
// Update component state
|
|
|
|
this.component.dragOverBlockId = null;
|
|
|
|
this.component.dragOverPosition = null;
|
|
|
|
|
|
|
|
// The parent component already handles removing drag-over classes programmatically
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles drop
|
|
|
|
*/
|
|
|
|
handleDrop(e: DragEvent, targetBlock: IBlock): void {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
if (!this.draggedBlockId || this.draggedBlockId === targetBlock.id) return;
|
|
|
|
|
2025-06-24 11:12:56 +00:00
|
|
|
// The parent component already has a handleDrop method that handles this programmatically
|
|
|
|
// We'll delegate to that to ensure proper programmatic rendering
|
|
|
|
this.component.handleDrop(e, targetBlock);
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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';
|
|
|
|
}
|
2025-06-24 12:24:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the drop indicator element
|
|
|
|
*/
|
|
|
|
private createDropIndicator(): void {
|
|
|
|
this.dropIndicator = document.createElement('div');
|
|
|
|
this.dropIndicator.className = 'drop-indicator';
|
|
|
|
this.dropIndicator.style.display = 'none';
|
|
|
|
this.component.editorContentRef.appendChild(this.dropIndicator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles global dragover to update dragged block position and move other blocks
|
|
|
|
*/
|
|
|
|
private handleGlobalDragOver = (e: DragEvent): void => {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
if (!this.draggedBlockElement) return;
|
|
|
|
|
|
|
|
// Calculate vertical offset from initial position
|
|
|
|
const deltaY = e.clientY - this.initialMouseY;
|
|
|
|
|
|
|
|
// Apply transform to move the dragged block vertically
|
|
|
|
this.draggedBlockElement.style.transform = `translateY(${deltaY}px)`;
|
|
|
|
|
|
|
|
// Throttle position updates to reduce stuttering
|
|
|
|
const now = Date.now();
|
|
|
|
if (now - this.lastUpdateTime < this.updateThrottle) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.lastUpdateTime = now;
|
|
|
|
|
|
|
|
// Calculate which blocks should move
|
|
|
|
this.updateBlockPositions(e.clientY);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates block positions based on cursor position
|
|
|
|
*/
|
|
|
|
private updateBlockPositions(mouseY: number): void {
|
|
|
|
const blocks = Array.from(this.component.editorContentRef.querySelectorAll('.block-wrapper')) as HTMLElement[];
|
|
|
|
const draggedIndex = blocks.findIndex(b => b.getAttribute('data-block-id') === this.draggedBlockId);
|
|
|
|
|
|
|
|
if (draggedIndex === -1) return;
|
|
|
|
|
|
|
|
// Reset all transforms first (except the dragged block)
|
|
|
|
blocks.forEach(block => {
|
|
|
|
if (block.getAttribute('data-block-id') !== this.draggedBlockId) {
|
|
|
|
block.classList.remove('move-up', 'move-down');
|
|
|
|
block.style.removeProperty('--drag-offset');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Calculate where the dragged block should be inserted
|
|
|
|
let newIndex = blocks.length; // Default to end
|
|
|
|
|
|
|
|
for (let i = 0; i < blocks.length; i++) {
|
|
|
|
if (i === draggedIndex) continue;
|
|
|
|
|
|
|
|
const block = blocks[i];
|
|
|
|
const rect = block.getBoundingClientRect();
|
|
|
|
const blockTop = rect.top;
|
|
|
|
|
|
|
|
// Check if mouse is above this block's middle
|
|
|
|
if (mouseY < blockTop + (rect.height * 0.5)) {
|
|
|
|
newIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Apply transforms to move blocks out of the way
|
|
|
|
for (let i = 0; i < blocks.length; i++) {
|
|
|
|
if (i === draggedIndex) continue;
|
|
|
|
|
|
|
|
const block = blocks[i];
|
|
|
|
|
|
|
|
// Determine if this block needs to move
|
|
|
|
if (draggedIndex < newIndex) {
|
|
|
|
// Dragging down: blocks between original and new position move up
|
|
|
|
if (i > draggedIndex && i < newIndex) {
|
|
|
|
block.classList.add('move-up');
|
|
|
|
block.style.setProperty('--drag-offset', `${this.draggedBlockHeight}px`);
|
|
|
|
}
|
|
|
|
} else if (draggedIndex > newIndex) {
|
|
|
|
// Dragging up: blocks between new and original position move down
|
|
|
|
if (i >= newIndex && i < draggedIndex) {
|
|
|
|
block.classList.add('move-down');
|
|
|
|
block.style.setProperty('--drag-offset', `${this.draggedBlockHeight}px`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update drop indicator position
|
|
|
|
this.updateDropIndicator(blocks, newIndex, draggedIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates the drop indicator position
|
|
|
|
*/
|
|
|
|
private updateDropIndicator(blocks: HTMLElement[], targetIndex: number, draggedIndex: number): void {
|
|
|
|
if (!this.dropIndicator || !this.draggedBlockElement) return;
|
|
|
|
|
|
|
|
this.dropIndicator.style.display = 'block';
|
|
|
|
|
|
|
|
const containerRect = this.component.editorContentRef.getBoundingClientRect();
|
|
|
|
let topPosition = 0;
|
|
|
|
|
2025-06-29 14:00:55 +00:00
|
|
|
// Build array of visual block positions (excluding dragged block)
|
|
|
|
const visualBlocks: { index: number, top: number, bottom: number }[] = [];
|
|
|
|
|
|
|
|
for (let i = 0; i < blocks.length; i++) {
|
|
|
|
if (i === draggedIndex) continue; // Skip the dragged block
|
2025-06-24 12:24:02 +00:00
|
|
|
|
2025-06-29 14:00:55 +00:00
|
|
|
const block = blocks[i];
|
|
|
|
const rect = block.getBoundingClientRect();
|
|
|
|
let top = rect.top - containerRect.top;
|
|
|
|
let bottom = rect.bottom - containerRect.top;
|
|
|
|
|
|
|
|
// Account for any transforms
|
|
|
|
const transform = window.getComputedStyle(block).transform;
|
|
|
|
if (transform && transform !== 'none') {
|
|
|
|
const matrix = new DOMMatrix(transform);
|
|
|
|
const yOffset = matrix.m42;
|
|
|
|
top += yOffset;
|
|
|
|
bottom += yOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
visualBlocks.push({ index: i, top, bottom });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort by visual position
|
|
|
|
visualBlocks.sort((a, b) => a.top - b.top);
|
|
|
|
|
|
|
|
// Adjust targetIndex to account for excluded dragged block
|
|
|
|
let adjustedTargetIndex = targetIndex;
|
|
|
|
if (targetIndex > draggedIndex) {
|
|
|
|
adjustedTargetIndex--; // Reduce by 1 since dragged block is not in visualBlocks
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate drop position
|
|
|
|
// Get the margin that will be applied based on the dragged block type
|
|
|
|
let blockMargin = 16; // default margin
|
|
|
|
if (this.draggedBlockElement) {
|
|
|
|
const draggedBlock = this.component.blocks.find(b => b.id === this.draggedBlockId);
|
|
|
|
if (draggedBlock) {
|
|
|
|
const blockType = draggedBlock.type;
|
|
|
|
if (blockType === 'heading-1' || blockType === 'heading-2' || blockType === 'heading-3') {
|
|
|
|
blockMargin = 24;
|
|
|
|
} else if (blockType === 'code' || blockType === 'quote') {
|
|
|
|
blockMargin = 20;
|
2025-06-24 12:24:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-29 14:00:55 +00:00
|
|
|
if (adjustedTargetIndex === 0) {
|
|
|
|
// Insert at the very top - no margin needed for first block
|
|
|
|
topPosition = 0;
|
|
|
|
} else if (adjustedTargetIndex >= visualBlocks.length) {
|
|
|
|
// Insert at the end
|
|
|
|
const lastBlock = visualBlocks[visualBlocks.length - 1];
|
|
|
|
if (lastBlock) {
|
|
|
|
topPosition = lastBlock.bottom;
|
|
|
|
// Add margin that will be applied to the dropped block
|
|
|
|
topPosition += blockMargin;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Insert between blocks
|
|
|
|
const blockBefore = visualBlocks[adjustedTargetIndex - 1];
|
|
|
|
if (blockBefore) {
|
|
|
|
topPosition = blockBefore.bottom;
|
|
|
|
// Add margin that will be applied to the dropped block
|
|
|
|
topPosition += blockMargin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the indicator height to match the dragged block
|
|
|
|
this.dropIndicator.style.height = `${this.draggedBlockHeight}px`;
|
|
|
|
|
|
|
|
// Set position
|
|
|
|
this.dropIndicator.style.top = `${Math.max(0, topPosition)}px`;
|
|
|
|
|
|
|
|
console.log('Drop indicator update:', {
|
|
|
|
targetIndex,
|
|
|
|
adjustedTargetIndex,
|
|
|
|
draggedIndex,
|
|
|
|
topPosition,
|
|
|
|
height: this.draggedBlockHeight,
|
|
|
|
blockMargin,
|
|
|
|
visualBlocks: visualBlocks.map(b => ({ index: b.index, top: b.top, bottom: b.bottom }))
|
|
|
|
});
|
2025-06-24 12:24:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles global drag end
|
|
|
|
*/
|
|
|
|
private handleGlobalDragEnd = (): void => {
|
|
|
|
// Clean up event listeners
|
|
|
|
document.removeEventListener('dragover', this.handleGlobalDragOver);
|
|
|
|
document.removeEventListener('dragend', this.handleGlobalDragEnd);
|
|
|
|
|
|
|
|
// Remove drop indicator
|
|
|
|
if (this.dropIndicator) {
|
|
|
|
this.dropIndicator.remove();
|
|
|
|
this.dropIndicator = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger the actual drop if we have a dragged block
|
|
|
|
if (this.draggedBlockId) {
|
|
|
|
// Small delay to ensure transforms are applied
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
this.performDrop();
|
|
|
|
// Call the regular drag end handler after drop
|
|
|
|
this.handleDragEnd();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Call the regular drag end handler
|
|
|
|
this.handleDragEnd();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Performs the actual drop operation
|
|
|
|
*/
|
|
|
|
private performDrop(): void {
|
|
|
|
if (!this.draggedBlockId) return;
|
|
|
|
|
|
|
|
// Get the visual order of blocks based on their positions
|
|
|
|
const blockElements = Array.from(this.component.editorContentRef.querySelectorAll('.block-wrapper')) as HTMLElement[];
|
|
|
|
const draggedElement = blockElements.find(el => el.getAttribute('data-block-id') === this.draggedBlockId);
|
|
|
|
|
|
|
|
if (!draggedElement) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Create an array of blocks with their visual positions
|
|
|
|
const visualOrder = blockElements.map(el => {
|
|
|
|
const id = el.getAttribute('data-block-id');
|
|
|
|
const rect = el.getBoundingClientRect();
|
|
|
|
const centerY = rect.top + rect.height / 2;
|
|
|
|
return { id, centerY, element: el };
|
|
|
|
});
|
|
|
|
|
|
|
|
// Sort by visual Y position
|
|
|
|
visualOrder.sort((a, b) => a.centerY - b.centerY);
|
|
|
|
|
|
|
|
// Get the new order of block IDs
|
|
|
|
const newBlockIds = visualOrder.map(item => item.id).filter(id => id !== null);
|
|
|
|
|
|
|
|
// Find the original block data
|
|
|
|
const originalBlocks = [...this.component.blocks];
|
|
|
|
const draggedBlock = originalBlocks.find(b => b.id === this.draggedBlockId);
|
|
|
|
|
|
|
|
if (!draggedBlock) return;
|
|
|
|
|
|
|
|
// Check if order actually changed
|
|
|
|
const oldOrder = originalBlocks.map(b => b.id);
|
|
|
|
const orderChanged = !newBlockIds.every((id, index) => id === oldOrder[index]);
|
|
|
|
|
|
|
|
if (!orderChanged) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reorder blocks based on visual positions
|
|
|
|
const newBlocks = newBlockIds.map(id => originalBlocks.find(b => b.id === id)!).filter(Boolean);
|
|
|
|
|
|
|
|
// Update blocks
|
|
|
|
this.component.blocks = newBlocks;
|
|
|
|
|
|
|
|
// Re-render blocks programmatically
|
|
|
|
this.component.renderBlocksProgrammatically();
|
|
|
|
|
|
|
|
// Update value
|
|
|
|
this.component.updateValue();
|
|
|
|
|
|
|
|
// Focus the moved block after a delay
|
|
|
|
setTimeout(() => {
|
|
|
|
if (draggedBlock.type !== 'divider') {
|
|
|
|
this.component.blockOperations.focusBlock(draggedBlock.id);
|
|
|
|
}
|
|
|
|
}, 100);
|
|
|
|
}
|
2025-06-24 08:19:53 +00:00
|
|
|
}
|