Files
dees-catalog/readme.plan.md
Juergen Kunz e9541da8ff refactor
2025-06-24 22:45:50 +00:00

302 lines
8.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<string, IBlockHandler>();
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 `
<div class="block image${selectedClass}" data-block-id="${block.id}" data-block-type="${block.type}" tabindex="0">
${isLoading ? `<div class="image-loading">Uploading image...</div>` : ''}
${imageUrl ? this.renderImage(imageUrl, block.content) : this.renderPlaceholder()}
</div>
`;
}
private renderImage(url: string, alt: string): string {
return `
<div class="image-container">
<img src="${url}" alt="${alt || 'Uploaded image'}" />
</div>
`;
}
private renderPlaceholder(): string {
return `
<div class="image-upload-placeholder">
<div class="upload-icon">=<3D></div>
<div class="upload-text">Click to upload an image</div>
<div class="upload-hint">or drag and drop</div>
</div>
<input type="file" accept="image/*" style="display: none;" />
`;
}
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