Files
dees-catalog/ts_web/elements/wysiwyg/wysiwyg.inputhandler.ts

193 lines
5.6 KiB
TypeScript
Raw Normal View History

2025-06-24 08:19:53 +00:00
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<void> {
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}><li></li></${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);
}
}
}