import { type IBlock } from './wysiwyg.types.js'; import { WysiwygShortcuts } from './wysiwyg.shortcuts.js'; import { WysiwygBlocks } from './wysiwyg.blocks.js'; import { WysiwygBlockOperations } from './wysiwyg.blockoperations.js'; export class WysiwygInputHandler { private component: any; private saveTimeout: any = null; constructor(component: any) { this.component = component; } /** * Handles input events for blocks */ handleBlockInput(e: InputEvent, block: IBlock): void { if (this.component.isComposing) return; const target = e.target as HTMLDivElement; const textContent = target.textContent || ''; // Update block content based on type this.updateBlockContent(block, target); // Check for block type transformations const detectedType = this.detectBlockTypeIntent(textContent); if (detectedType && detectedType.type !== block.type) { e.preventDefault(); this.handleBlockTransformation(block, detectedType, target); return; } // Handle slash commands this.handleSlashCommand(textContent, target); // Schedule auto-save this.scheduleAutoSave(); } /** * Updates block content based on its type */ private updateBlockContent(block: IBlock, target: HTMLDivElement): void { if (block.type === 'list') { const listItems = target.querySelectorAll('li'); block.content = Array.from(listItems).map(li => li.textContent || '').join('\n'); const listElement = target.querySelector('ol, ul'); if (listElement) { block.metadata = { listType: listElement.tagName.toLowerCase() === 'ol' ? 'ordered' : 'bullet' }; } } else if (block.type === 'code') { block.content = target.textContent || ''; } else { block.content = target.innerHTML || ''; } } /** * Detects if the user is trying to create a specific block type */ private detectBlockTypeIntent(content: string): { type: IBlock['type'], listType?: 'bullet' | 'ordered' } | null { // Check heading patterns const headingResult = WysiwygShortcuts.checkHeadingShortcut(content); if (headingResult) { return headingResult; } // Check list patterns const listResult = WysiwygShortcuts.checkListShortcut(content); if (listResult) { return listResult; } // Check quote pattern if (WysiwygShortcuts.checkQuoteShortcut(content)) { return { type: 'quote' }; } // Check code pattern if (WysiwygShortcuts.checkCodeShortcut(content)) { return { type: 'code' }; } // Check divider pattern if (WysiwygShortcuts.checkDividerShortcut(content)) { return { type: 'divider' }; } return null; } /** * Handles block type transformation */ private async handleBlockTransformation( block: IBlock, detectedType: { type: IBlock['type'], listType?: 'bullet' | 'ordered' }, target: HTMLDivElement ): Promise { const blockOps = this.component.blockOperations; if (detectedType.type === 'list') { block.type = 'list'; block.content = ''; block.metadata = { listType: detectedType.listType }; const listTag = detectedType.listType === 'ordered' ? 'ol' : 'ul'; target.innerHTML = `<${listTag}>
  • `; this.component.updateValue(); this.component.requestUpdate(); setTimeout(() => { WysiwygBlocks.focusListItem(target); }, 0); } else if (detectedType.type === 'divider') { block.type = 'divider'; block.content = ' '; const newBlock = blockOps.createBlock(); blockOps.insertBlockAfter(block, newBlock); this.component.updateValue(); this.component.requestUpdate(); } else if (detectedType.type === 'code') { const language = await this.component.showLanguageSelectionModal(); if (language) { block.type = 'code'; block.content = ''; block.metadata = { language }; target.textContent = ''; this.component.updateValue(); this.component.requestUpdate(); } } else { block.type = detectedType.type; block.content = ''; target.textContent = ''; this.component.updateValue(); this.component.requestUpdate(); } } /** * Handles slash command detection and menu display */ private handleSlashCommand(textContent: string, target: HTMLDivElement): void { if (textContent === '/' || (textContent.startsWith('/') && this.component.showSlashMenu)) { if (!this.component.showSlashMenu && textContent === '/') { this.component.showSlashMenu = true; this.component.slashMenuSelectedIndex = 0; const rect = target.getBoundingClientRect(); const containerRect = this.component.shadowRoot!.querySelector('.wysiwyg-container')!.getBoundingClientRect(); this.component.slashMenuPosition = { x: rect.left - containerRect.left, y: rect.bottom - containerRect.top + 4 }; } this.component.slashMenuFilter = textContent.slice(1); } else if (!textContent.startsWith('/')) { this.component.closeSlashMenu(); } } /** * Schedules auto-save after a delay */ private scheduleAutoSave(): void { if (this.saveTimeout) { clearTimeout(this.saveTimeout); } this.saveTimeout = setTimeout(() => { this.component.updateValue(); }, 1000); } /** * Cleans up resources */ destroy(): void { if (this.saveTimeout) { clearTimeout(this.saveTimeout); } } }