import { html, type TemplateResult } from '@design.estate/dees-element'; import { type IBlock } from './wysiwyg.types.js'; import { WysiwygConverters } from './wysiwyg.converters.js'; export class WysiwygBlocks { static renderListContent(content: string, metadata?: any): string { const items = content.split('\n').filter(item => item.trim()); if (items.length === 0) return ''; const listTag = metadata?.listType === 'ordered' ? 'ol' : 'ul'; // Don't escape HTML to preserve formatting return `<${listTag}>${items.map(item => `
  • ${item}
  • `).join('')}`; } static renderBlock( block: IBlock, isSelected: boolean, handlers: { onInput: (e: InputEvent) => void; onKeyDown: (e: KeyboardEvent) => void; onFocus: () => void; onBlur: () => void; onCompositionStart: () => void; onCompositionEnd: () => void; onMouseUp?: (e: MouseEvent) => void; } ): TemplateResult { if (block.type === 'divider') { return html`

    `; } if (block.type === 'list') { return html`
    `; } // Special rendering for code blocks with language indicator if (block.type === 'code') { const language = block.metadata?.language || 'plain text'; return html`
    ${language}
    `; } const blockElement = html`
    `; return blockElement; } static setCursorToEnd(element: HTMLElement): void { const sel = window.getSelection(); if (!sel) return; const range = document.createRange(); // Handle different content types if (element.childNodes.length === 0) { // Empty element - add a zero-width space to enable cursor const textNode = document.createTextNode('\u200B'); element.appendChild(textNode); range.setStart(textNode, 1); range.collapse(true); } else { // Find the last text node or element const lastNode = this.getLastNode(element); if (lastNode.nodeType === Node.TEXT_NODE) { range.setStart(lastNode, lastNode.textContent?.length || 0); } else { range.setStartAfter(lastNode); } range.collapse(true); } sel.removeAllRanges(); sel.addRange(range); // Remove zero-width space if it was added if (element.textContent === '\u200B') { element.textContent = ''; } } static setCursorToStart(element: HTMLElement): void { const sel = window.getSelection(); if (!sel) return; const range = document.createRange(); // Handle different content types if (element.childNodes.length === 0) { // Empty element range.setStart(element, 0); range.collapse(true); } else { // Find the first text node or element const firstNode = this.getFirstNode(element); if (firstNode.nodeType === Node.TEXT_NODE) { range.setStart(firstNode, 0); } else { range.setStartBefore(firstNode); } range.collapse(true); } sel.removeAllRanges(); sel.addRange(range); } private static getLastNode(element: Node): Node { if (element.childNodes.length === 0) { return element; } const lastChild = element.childNodes[element.childNodes.length - 1]; if (lastChild.nodeType === Node.TEXT_NODE || lastChild.childNodes.length === 0) { return lastChild; } return this.getLastNode(lastChild); } private static getFirstNode(element: Node): Node { if (element.childNodes.length === 0) { return element; } const firstChild = element.childNodes[0]; if (firstChild.nodeType === Node.TEXT_NODE || firstChild.childNodes.length === 0) { return firstChild; } return this.getFirstNode(firstChild); } static focusListItem(listElement: HTMLElement): void { const firstLi = listElement.querySelector('li'); if (firstLi) { firstLi.focus(); const range = document.createRange(); const sel = window.getSelection(); range.selectNodeContents(firstLi); range.collapse(true); sel!.removeAllRanges(); sel!.addRange(range); } } }