import { type IBlock } from './wysiwyg.types.js'; import { type IWysiwygComponent } from './wysiwyg.interfaces.js'; import { WysiwygShortcuts } from './wysiwyg.shortcuts.js'; import { WysiwygBlocks } from './wysiwyg.blocks.js'; export class WysiwygBlockOperations { private component: IWysiwygComponent; constructor(component: IWysiwygComponent) { this.component = component; } /** * Creates a new block with the specified parameters */ createBlock(type: IBlock['type'] = 'paragraph', content: string = '', metadata?: any): IBlock { return { id: WysiwygShortcuts.generateBlockId(), type, content, ...(metadata && { metadata }) }; } /** * Inserts a block after the specified block */ async insertBlockAfter(afterBlock: IBlock, newBlock: IBlock, focusNewBlock: boolean = true): Promise { const blocks = this.component.blocks; const blockIndex = blocks.findIndex((b: IBlock) => b.id === afterBlock.id); this.component.blocks = [ ...blocks.slice(0, blockIndex + 1), newBlock, ...blocks.slice(blockIndex + 1) ]; // Insert the new block element programmatically if we have the editor if (this.component.editorContentRef) { const afterWrapper = this.component.editorContentRef.querySelector(`[data-block-id="${afterBlock.id}"]`); if (afterWrapper) { const newWrapper = this.component.createBlockElement(newBlock); afterWrapper.insertAdjacentElement('afterend', newWrapper); } } this.component.updateValue(); if (focusNewBlock && newBlock.type !== 'divider') { // Give DOM time to settle await new Promise(resolve => setTimeout(resolve, 0)); // Focus the new block await this.focusBlock(newBlock.id, 'start'); } } /** * Removes a block by its ID */ removeBlock(blockId: string): void { // Save checkpoint before deletion this.component.saveToHistory(false); this.component.blocks = this.component.blocks.filter((b: IBlock) => b.id !== blockId); // Remove the block element programmatically if we have the editor if (this.component.editorContentRef) { const wrapper = this.component.editorContentRef.querySelector(`[data-block-id="${blockId}"]`); if (wrapper) { wrapper.remove(); } } this.component.updateValue(); } /** * Finds a block by its ID */ findBlock(blockId: string): IBlock | undefined { return this.component.blocks.find((b: IBlock) => b.id === blockId); } /** * Gets the index of a block */ getBlockIndex(blockId: string): number { return this.component.blocks.findIndex((b: IBlock) => b.id === blockId); } /** * Focuses a specific block */ async focusBlock(blockId: string, cursorPosition: 'start' | 'end' | number = 'start'): Promise { const wrapperElement = this.component.shadowRoot!.querySelector(`[data-block-id="${blockId}"]`); if (wrapperElement) { const blockComponent = wrapperElement.querySelector('dees-wysiwyg-block') as any; if (blockComponent) { // Wait a frame to ensure the block is rendered await new Promise(resolve => requestAnimationFrame(resolve)); // Now focus with cursor position blockComponent.focusWithCursor(cursorPosition); } } } /** * Updates the content of a block */ updateBlockContent(blockId: string, content: string): void { const block = this.findBlock(blockId); if (block) { block.content = content; this.component.updateValue(); } } /** * Transforms a block to a different type */ transformBlock(blockId: string, newType: IBlock['type'], metadata?: any): void { const block = this.findBlock(blockId); if (block) { // Save checkpoint before transformation this.component.saveToHistory(false); block.type = newType; block.content = ''; if (metadata) { block.metadata = metadata; } // Update the block element programmatically if we have the editor if (this.component.editorContentRef) { this.component.updateBlockElement(blockId); } this.component.updateValue(); } } /** * Moves a block to a new position */ moveBlock(blockId: string, targetIndex: number): void { const blocks = [...this.component.blocks]; const currentIndex = this.getBlockIndex(blockId); if (currentIndex === -1 || targetIndex < 0 || targetIndex >= blocks.length) { return; } const [movedBlock] = blocks.splice(currentIndex, 1); blocks.splice(targetIndex, 0, movedBlock); this.component.blocks = blocks; this.component.updateValue(); } /** * Gets the previous block */ getPreviousBlock(blockId: string): IBlock | null { const index = this.getBlockIndex(blockId); return index > 0 ? this.component.blocks[index - 1] : null; } /** * Gets the next block */ getNextBlock(blockId: string): IBlock | null { const index = this.getBlockIndex(blockId); return index < this.component.blocks.length - 1 ? this.component.blocks[index + 1] : null; } }