# WYSIWYG Editor Refactoring Plan ## Current Status The `dees-wysiwyg-block.ts` file has grown to over 2100 lines and contains: - Main component logic - CSS styles for all block types - Rendering logic for each block type - Setup methods for each block type - Helper methods for various functionality This makes the file difficult to maintain and extend. ## Refactoring Goals 1. **Modularity**: Each block type should be self-contained 2. **Extensibility**: Adding new block types should be straightforward 3. **Maintainability**: Code should be organized by responsibility 4. **Type Safety**: Strong interfaces to ensure consistent implementation 5. **Performance**: Enable potential lazy loading of block types ## Proposed File Structure ``` ts_web/elements/wysiwyg/  dees-wysiwyg-block.ts (main component - simplified)  blocks/   index.ts (exports all block types)   block.base.ts (base class/interface)   block.registry.ts (block type registry)   block.styles.ts (common block styles)     text/    paragraph.block.ts    heading.block.ts    quote.block.ts    code.block.ts    list.block.ts     media/    image.block.ts    youtube.block.ts    attachment.block.ts     content/    markdown.block.ts    html.block.ts    divider.block.ts     utils/   file.utils.ts   media.utils.ts   markdown.utils.ts ``` ## Implementation Details ### 1. Block Handler Interface ```typescript // block.base.ts export interface IBlockHandler { type: string; render(block: IBlock, isSelected: boolean): string; setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void; getStyles(): string; getPlaceholder?(): string; } export interface IBlockEventHandlers { onInput: (e: InputEvent) => void; onKeyDown: (e: KeyboardEvent) => void; onFocus: () => void; onBlur: () => void; onCompositionStart: () => void; onCompositionEnd: () => void; onMouseUp?: (e: MouseEvent) => void; } export abstract class BaseBlockHandler implements IBlockHandler { abstract type: string; abstract render(block: IBlock, isSelected: boolean): string; // Default implementation for common setup setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void { // Common setup logic } // Common styles can be defined here getStyles(): string { return ''; } } ``` ### 2. Block Registry Pattern ```typescript // block.registry.ts export class BlockRegistry { private static handlers = new Map(); static register(type: string, handler: IBlockHandler): void { this.handlers.set(type, handler); } static getHandler(type: string): IBlockHandler | undefined { return this.handlers.get(type); } static getAllTypes(): string[] { return Array.from(this.handlers.keys()); } } ``` ### 3. Example Block Implementation ```typescript // media/image.block.ts export class ImageBlockHandler extends BaseBlockHandler { type = 'image'; render(block: IBlock, isSelected: boolean): string { const selectedClass = isSelected ? ' selected' : ''; const imageUrl = block.metadata?.url || ''; const isLoading = block.metadata?.loading || false; return `
${isLoading ? `
Uploading image...
` : ''} ${imageUrl ? this.renderImage(imageUrl, block.content) : this.renderPlaceholder()}
`; } private renderImage(url: string, alt: string): string { return `
${alt || 'Uploaded image'}
`; } private renderPlaceholder(): string { return `
=�
Click to upload an image
or drag and drop
`; } setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void { const imageBlock = element.querySelector('.block.image') as HTMLDivElement; if (!imageBlock) return; // Setup click handlers imageBlock.addEventListener('click', (e) => { if ((e.target as HTMLElement).tagName !== 'INPUT') { e.stopPropagation(); imageBlock.focus(); handlers.onFocus(); } }); // Setup file upload this.setupFileUpload(imageBlock, block, handlers); // Setup drag and drop this.setupDragDrop(imageBlock, block, handlers); // Setup keyboard events this.setupKeyboardEvents(imageBlock, handlers); } getStyles(): string { return ` .block.image { min-height: 200px; padding: 0; margin: 16px 0; border-radius: 8px; overflow: hidden; position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.15s ease; } .block.image:focus { outline: none; } .block.image.selected { box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.3); } /* ... rest of image-specific styles ... */ `; } } ``` ## Migration Strategy ### Phase 1: Infrastructure (Week 1) - [x] Create `blocks/` directory structure - [x] Implement `IBlockHandler` interface and `BaseBlockHandler` class - [x] Implement `BlockRegistry` with registration mechanism - [x] Create `block.styles.ts` for common styles - [x] Set up exports in `blocks/index.ts` ### Phase 2: Proof of Concept (Week 1) ✅ - [x] Migrate `divider` block as the simplest example - [x] Update main component to use registry for divider blocks - [x] Test that divider blocks work identically to before - [x] Document any issues or adjustments needed ### Phase 3: Text Blocks Migration (Week 2) ✅ - [x] Migrate `paragraph` block - [x] Migrate `heading` blocks (h1, h2, h3) - [x] Migrate `quote` block - [x] Migrate `code` block - [x] Migrate `list` block - [x] Extract common text formatting utilities ### Phase 4: Media Blocks Migration (Week 2) - [ ] Migrate `image` block - [ ] Migrate `youtube` block - [ ] Migrate `attachment` block - [ ] Extract file handling utilities to `utils/file.utils.ts` - [ ] Extract media utilities to `utils/media.utils.ts` ### Phase 5: Content Blocks Migration (Week 3) - [ ] Migrate `markdown` block - [ ] Migrate `html` block - [ ] Extract markdown utilities to `utils/markdown.utils.ts` ### Phase 6: Cleanup (Week 3) - [ ] Remove old code from main component - [ ] Optimize imports and exports - [ ] Update documentation - [ ] Performance testing - [ ] Bundle size analysis ## Technical Considerations ### 1. Backwards Compatibility - Ensure all existing functionality remains intact - No breaking changes to the public API - Maintain existing event handling patterns ### 2. Performance - Monitor bundle size impact - Consider dynamic imports for heavy block types: ```typescript // Lazy load markdown handler const { MarkdownBlockHandler } = await import('./content/markdown.block.js'); ``` ### 3. Type Safety - All block handlers must implement `IBlockHandler` - Proper typing for block metadata - Type guards for block-specific operations ### 4. Testing Strategy - Unit tests for each block handler - Integration tests for the registry - E2E tests to ensure no regressions ### 5. Style Management - Common styles in `block.styles.ts` - Block-specific styles returned by `getStyles()` - Theme variables properly utilized ## Success Criteria 1. **Code Organization**: Each file under 300 lines 2. **Single Responsibility**: Each class handles one block type 3. **No Regressions**: All existing functionality works identically 4. **Improved Developer Experience**: Adding new blocks is straightforward 5. **Performance**: No significant increase in bundle size or runtime overhead ## Future Enhancements 1. **Plugin System**: Allow external block types to be registered 2. **Lazy Loading**: Dynamic imports for rarely used block types 3. **Block Templates**: Predefined configurations for common use cases 4. **Block Transformations**: Convert between compatible block types 5. **Custom Block API**: Allow users to define their own block types ## Notes - This refactoring should be done incrementally to minimize risk - Each phase should be fully tested before moving to the next - Regular code reviews to ensure consistency - Documentation should be updated as we go