This commit is contained in:
Juergen Kunz
2025-06-26 13:32:37 +00:00
parent 342bd7d7c2
commit 979877b3b0
2 changed files with 94 additions and 195 deletions

View File

@ -0,0 +1,65 @@
# WYSIWYG Block Cleanup Status
## Overview
This document tracks the cleanup of `dees-wysiwyg-block.ts` after migrating all block types to the new block handler architecture.
## Completed ✅
All cleanup tasks have been successfully completed on 2025-06-26.
## Cleanup Tasks
### 1. ✅ Remove Block-Specific Styles (lines 101-219)
- [x] Remove `.block.heading-1/2/3` styles → Now in `heading.block.ts`
- [x] Remove `.block.quote` styles → Now in `quote.block.ts`
- [x] Remove `.block.list` styles → Now in `list.block.ts`
- [x] Remove `.block.paragraph` styles → Now in `paragraph.block.ts`
### 2. ✅ Remove Code Block Specific Logic
- [x] Remove code block rendering in `renderBlockContent()` (lines 508-521)
- [x] Remove all `type === 'code'` conditional branches
- [x] Simplify element selection to not special-case code blocks
### 3. ✅ Remove List Block Specific Logic
- [x] Remove `focusListItem()` method (lines 814-821)
- [x] Remove list-specific handling in `getContent()` (lines 732-734)
- [x] Remove list-specific handling in `setContent()` (lines 764-765)
- [x] Remove list content rendering in `firstUpdated()` (line 479)
### 4. ✅ Remove getPlaceholder() Method
- [x] Remove entire method (lines 538-553)
- [x] Update renderBlockContent() to not use placeholders
### 5. ✅ Clean Up Excessive Empty Lines
- [x] Remove consecutive blank lines throughout the file
### 6. ✅ Centralize nonEditableTypes
- [x] Create a single source of truth for non-editable block types
- [x] Remove duplicate arrays
### 7. ✅ Simplify Handler Delegation
- [x] Keep handler delegation pattern but ensure consistency
### 8. ✅ Remove Unused Properties (if confirmed unused)
- [x] Keep `contentInitialized` - still used for tracking
- [x] Keep `blockElement` - used for caching
- [x] Keep cursor tracking properties - used for selection
## Implementation Notes
### Block Types Now Fully Handled by Handlers:
1. **Text blocks**: paragraph, heading-1/2/3, quote, code, list
2. **Media blocks**: image, youtube, attachment
3. **Content blocks**: divider, markdown, html
### Remaining Responsibilities of dees-wysiwyg-block.ts:
1. Shadow DOM container management
2. Handler delegation for all operations
3. Generic block wrapper styles
4. Selection/cursor tracking
5. Event listener setup (until fully delegated to handlers)
## Future Improvements
- Consider moving all event handling to block handlers
- Simplify the handler delegation pattern
- Move generic block styles to a shared location
- Consider removing the need for special-casing any block types

View File

@ -50,6 +50,9 @@ export class DeesWysiwygBlock extends DeesElement {
private handlerStylesInjected = false;
// Block types that don't support contenteditable
private static readonly NON_EDITABLE_TYPES = ['image', 'divider', 'youtube'];
private injectHandlerStyles(): void {
// Only inject once per instance
if (this.handlerStylesInjected) return;
@ -98,54 +101,7 @@ export class DeesWysiwygBlock extends DeesElement {
pointer-events: none;
}
.block.heading-1 {
font-size: 32px;
font-weight: 700;
line-height: 1.2;
margin: 24px 0 8px 0;
color: ${cssManager.bdTheme('#000000', '#ffffff')};
}
.block.heading-2 {
font-size: 24px;
font-weight: 600;
line-height: 1.3;
margin: 20px 0 6px 0;
color: ${cssManager.bdTheme('#000000', '#ffffff')};
}
.block.heading-3 {
font-size: 20px;
font-weight: 600;
line-height: 1.4;
margin: 16px 0 4px 0;
color: ${cssManager.bdTheme('#000000', '#ffffff')};
}
.block.quote {
border-left: 3px solid ${cssManager.bdTheme('#0066cc', '#4d94ff')};
padding-left: 20px;
color: ${cssManager.bdTheme('#555', '#b0b0b0')};
font-style: italic;
line-height: 1.6;
margin: 16px 0;
}
/* Code block styles moved to handler */
.block.list {
padding: 0;
}
.block.list ul,
.block.list ol {
margin: 0;
padding-left: 24px;
}
.block.list li {
margin: 4px 0;
}
/* Block-specific styles moved to handlers */
/* Formatting styles */
@ -195,12 +151,6 @@ export class DeesWysiwygBlock extends DeesElement {
color: inherit;
}
/* Paragraph specific styles */
.block.paragraph {
font-size: 16px;
line-height: 1.6;
font-weight: 400;
}
/* Strike through */
.block :is(s, strike) {
@ -208,15 +158,6 @@ export class DeesWysiwygBlock extends DeesElement {
opacity: 0.7;
}
/* List specific margin adjustments */
.block.list li {
margin-bottom: 8px;
line-height: 1.6;
}
.block.list li:last-child {
margin-bottom: 0;
}
/* Block margin adjustments based on type */
:host-context(.block-wrapper:first-child) .block {
@ -307,9 +248,7 @@ export class DeesWysiwygBlock extends DeesElement {
// Handle special block types
// Now find the actual editable block element
const editableBlock = this.block.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
const editableBlock = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
// Ensure the block element maintains its content
if (editableBlock) {
@ -364,7 +303,7 @@ export class DeesWysiwygBlock extends DeesElement {
this.handlers?.onMouseUp?.(e);
});
editableBlock.addEventListener('click', (e: MouseEvent) => {
editableBlock.addEventListener('click', () => {
// Small delay to let browser set cursor position
setTimeout(() => {
const pos = this.getCursorPosition(editableBlock);
@ -398,9 +337,7 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Get fresh reference to the editable block
const currentEditableBlock = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
const currentEditableBlock = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!currentEditableBlock) return;
@ -464,7 +401,7 @@ export class DeesWysiwygBlock extends DeesElement {
(this as any)._selectionHandler = checkSelection;
// Add keyup handler for cursor position tracking
editableBlock.addEventListener('keyup', (e) => {
editableBlock.addEventListener('keyup', () => {
// Track cursor position
const pos = this.getCursorPosition(editableBlock);
if (pos !== null) {
@ -474,13 +411,7 @@ export class DeesWysiwygBlock extends DeesElement {
// Set initial content if needed
if (this.block.content) {
if (this.block.type === 'code') {
editableBlock.textContent = this.block.content;
} else if (this.block.type === 'list') {
editableBlock.innerHTML = WysiwygBlocks.renderListContent(this.block.content, this.block.metadata);
} else {
editableBlock.innerHTML = this.block.content;
}
editableBlock.innerHTML = this.block.content;
}
}
@ -505,52 +436,16 @@ export class DeesWysiwygBlock extends DeesElement {
return handler.render(this.block, this.isSelected);
}
if (this.block.type === 'code') {
const language = this.block.metadata?.language || 'plain text';
const selectedClass = this.isSelected ? ' selected' : '';
return `
<div class="code-block-container">
<div class="code-language">${language}</div>
<div
class="block code${selectedClass}"
contenteditable="true"
data-block-type="${this.block.type}"
></div>
</div>
`;
}
const placeholder = this.getPlaceholder();
// Default rendering for blocks without handlers
const selectedClass = this.isSelected ? ' selected' : '';
return `
<div
class="block ${this.block.type}${selectedClass}"
contenteditable="true"
data-placeholder="${placeholder}"
></div>
`;
}
private getPlaceholder(): string {
switch (this.block.type) {
case 'paragraph':
return "Type '/' for commands...";
case 'heading-1':
return 'Heading 1';
case 'heading-2':
return 'Heading 2';
case 'heading-3':
return 'Heading 3';
case 'quote':
return 'Quote';
default:
return '';
}
}
public focus(): void {
@ -563,8 +458,7 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Handle non-editable blocks
const nonEditableTypes = ['image', 'divider', 'youtube'];
if (this.block && nonEditableTypes.includes(this.block.type)) {
if (this.block && DeesWysiwygBlock.NON_EDITABLE_TYPES.includes(this.block.type)) {
const blockElement = this.shadowRoot?.querySelector(`.block.${this.block.type}`) as HTMLDivElement;
if (blockElement) {
blockElement.focus();
@ -572,10 +466,8 @@ export class DeesWysiwygBlock extends DeesElement {
return;
}
// Get the actual editable element (might be nested for code blocks)
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
// Get the actual editable element
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) return;
@ -604,16 +496,13 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Non-editable blocks don't support cursor positioning
const nonEditableTypes = ['image', 'divider', 'youtube'];
if (this.block && nonEditableTypes.includes(this.block.type)) {
if (this.block && DeesWysiwygBlock.NON_EDITABLE_TYPES.includes(this.block.type)) {
this.focus();
return;
}
// Get the actual editable element (might be nested for code blocks)
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
// Get the actual editable element
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) return;
@ -722,24 +611,15 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Get the actual editable element (might be nested for code blocks)
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
// Get the actual editable element
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) return '';
if (this.block.type === 'list') {
const listItems = editableElement.querySelectorAll('li');
return Array.from(listItems).map(li => li.innerHTML || '').join('\n');
} else if (this.block.type === 'code') {
return editableElement.textContent || '';
} else {
// For regular blocks, get the innerHTML which includes formatting tags
const content = editableElement.innerHTML || '';
console.log('Getting content from block:', content);
return content;
}
// Get the innerHTML which includes formatting tags
const content = editableElement.innerHTML || '';
console.log('Getting content from block:', content);
return content;
}
public setContent(content: string): void {
@ -751,23 +631,15 @@ export class DeesWysiwygBlock extends DeesElement {
return handler.setContent(container, content, context);
}
// Get the actual editable element (might be nested for code blocks)
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
// Get the actual editable element
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) return;
// Store if we have focus
const hadFocus = document.activeElement === editableElement || this.shadowRoot?.activeElement === editableElement;
if (this.block.type === 'list') {
editableElement.innerHTML = WysiwygBlocks.renderListContent(content, this.block.metadata);
} else if (this.block.type === 'code') {
editableElement.textContent = content;
} else {
editableElement.innerHTML = content;
}
editableElement.innerHTML = content;
// Restore focus if we had it
if (hadFocus) {
@ -785,9 +657,7 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Always find the element fresh, don't rely on cached blockElement
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (editableElement) {
WysiwygBlocks.setCursorToStart(editableElement);
}
@ -803,28 +673,12 @@ export class DeesWysiwygBlock extends DeesElement {
}
// Always find the element fresh, don't rely on cached blockElement
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (editableElement) {
WysiwygBlocks.setCursorToEnd(editableElement);
}
}
public focusListItem(): void {
if (this.block.type === 'list') {
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (editableElement) {
WysiwygBlocks.focusListItem(editableElement);
}
}
}
@ -860,9 +714,7 @@ export class DeesWysiwygBlock extends DeesElement {
// Get the actual editable element first
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
const editableElement = this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) {
console.log('getSplitContent: No editable element found');
@ -932,24 +784,6 @@ export class DeesWysiwygBlock extends DeesElement {
return null;
}
// For code blocks, use simple text splitting
if (this.block.type === 'code') {
const cursorPos = this.getCursorPosition(editableElement) || 0;
const fullText = editableElement.textContent || '';
console.log('getSplitContent: Code block split:', {
cursorPos,
fullTextLength: fullText.length,
before: fullText.substring(0, cursorPos),
after: fullText.substring(cursorPos)
});
return {
before: fullText.substring(0, cursorPos),
after: fullText.substring(cursorPos)
};
}
// For HTML content, get cursor position first
const cursorPos = this.getCursorPosition(editableElement);
console.log('getSplitContent: Cursor position for HTML split:', cursorPos);
@ -1004,4 +838,4 @@ export class DeesWysiwygBlock extends DeesElement {
};
}
}
}