feat(editor): Add wysiwyg editor
This commit is contained in:
98
ts_web/elements/wysiwyg/wysiwyg.blocks.ts
Normal file
98
ts_web/elements/wysiwyg/wysiwyg.blocks.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
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';
|
||||
return `<${listTag}>${items.map(item => `<li>${WysiwygConverters.escapeHtml(item)}</li>`).join('')}</${listTag}>`;
|
||||
}
|
||||
|
||||
static renderBlock(
|
||||
block: IBlock,
|
||||
isSelected: boolean,
|
||||
handlers: {
|
||||
onInput: (e: InputEvent) => void;
|
||||
onKeyDown: (e: KeyboardEvent) => void;
|
||||
onFocus: () => void;
|
||||
onBlur: () => void;
|
||||
onCompositionStart: () => void;
|
||||
onCompositionEnd: () => void;
|
||||
}
|
||||
): TemplateResult {
|
||||
if (block.type === 'divider') {
|
||||
return html`
|
||||
<div
|
||||
class="block divider"
|
||||
data-block-id="${block.id}"
|
||||
>
|
||||
<hr>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (block.type === 'list') {
|
||||
return html`
|
||||
<div
|
||||
class="block list ${isSelected ? 'selected' : ''}"
|
||||
data-block-id="${block.id}"
|
||||
contenteditable="true"
|
||||
@input="${handlers.onInput}"
|
||||
@keydown="${handlers.onKeyDown}"
|
||||
@focus="${handlers.onFocus}"
|
||||
@blur="${handlers.onBlur}"
|
||||
@compositionstart="${handlers.onCompositionStart}"
|
||||
@compositionend="${handlers.onCompositionEnd}"
|
||||
.innerHTML="${this.renderListContent(block.content, block.metadata)}"
|
||||
></div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="block ${block.type} ${isSelected ? 'selected' : ''}"
|
||||
data-block-id="${block.id}"
|
||||
contenteditable="true"
|
||||
@input="${handlers.onInput}"
|
||||
@keydown="${handlers.onKeyDown}"
|
||||
@focus="${handlers.onFocus}"
|
||||
@blur="${handlers.onBlur}"
|
||||
@compositionstart="${handlers.onCompositionStart}"
|
||||
@compositionend="${handlers.onCompositionEnd}"
|
||||
></div>
|
||||
`;
|
||||
}
|
||||
|
||||
static setCursorToEnd(element: HTMLElement): void {
|
||||
const range = document.createRange();
|
||||
const sel = window.getSelection();
|
||||
range.selectNodeContents(element);
|
||||
range.collapse(false);
|
||||
sel!.removeAllRanges();
|
||||
sel!.addRange(range);
|
||||
}
|
||||
|
||||
static setCursorToStart(element: HTMLElement): void {
|
||||
const range = document.createRange();
|
||||
const sel = window.getSelection();
|
||||
range.selectNodeContents(element);
|
||||
range.collapse(true);
|
||||
sel!.removeAllRanges();
|
||||
sel!.addRange(range);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user