Files
dees-catalog/readme.plan.md

302 lines
8.6 KiB
Markdown
Raw Permalink Normal View History

2025-06-24 22:45:50 +00:00
# WYSIWYG Editor Refactoring Plan
2025-06-24 22:45:50 +00:00
## Current Status
2025-06-24 22:45:50 +00:00
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
2025-06-24 22:45:50 +00:00
This makes the file difficult to maintain and extend.
2025-06-24 22:45:50 +00:00
## Refactoring Goals
2025-06-24 22:45:50 +00:00
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
2025-06-24 22:45:50 +00:00
## 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
2025-06-24 22:45:50 +00:00
// 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;
}
2025-06-24 22:45:50 +00:00
export interface IBlockEventHandlers {
onInput: (e: InputEvent) => void;
onKeyDown: (e: KeyboardEvent) => void;
onFocus: () => void;
onBlur: () => void;
onCompositionStart: () => void;
onCompositionEnd: () => void;
onMouseUp?: (e: MouseEvent) => void;
}
2025-06-24 22:45:50 +00:00
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 '';
}
}
2025-06-24 22:45:50 +00:00
```
### 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());
}
}
```
2025-06-24 22:45:50 +00:00
### 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 ... */
`;
}
}
```
2025-06-24 22:45:50 +00:00
## 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
2025-06-24 22:45:50 +00:00
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