Files
dees-catalog/readme.hints.md
Juergen Kunz 35a648d450 refactor(wysiwyg): Clean up code and ensure drag-drop works with programmatic rendering
- Update drag handler to work without requestUpdate calls
- Remove duplicate modal methods (using WysiwygModalManager)
- Clean up unused imports and methods
- Ensure all DOM updates are programmatic
- Add comprehensive test files for all features
- Follow separation of concerns principles
2025-06-24 11:12:56 +00:00

20 KiB

!!! Please pay attention to the following points when writing the readme: !!!

  • Give a short rundown of components and a few points abputspecific features on each.
  • Try to list all components in a summary.
  • Then list all components with a short description.

Chart Components

dees-chart-area

  • Fully functional area chart component using ApexCharts
  • Displays time-series data with gradient fills
  • Responsive with ResizeObserver (debounced to prevent flicker)
  • Fixed: Chart now properly respects container boundaries on initial render
  • Overflow prevention with proper CSS containment
  • Enhanced demo features:
    • Multiple dataset examples (System Usage, Network Traffic, Sales Analytics)
    • Real-time data simulation with automatic updates
    • Dynamic dataset switching
    • Customizable Y-axis formatters (percentages, currency, units)
    • Data randomization for testing
    • Manual data point addition
  • Properties:
    • label: Chart title
    • series: ApexAxisChartSeries data
    • yAxisFormatter: Custom Y-axis label formatter function
  • Methods:
    • updateSeries(): Update chart data
    • appendData(): Add new data points to existing series
  • Demo uses global reference to access chart element (window.__demoChartElement)

dees-chart-log

  • Server log viewer component (not a chart despite the name)
  • Terminal-style interface with monospace font
  • Supports log levels: debug, info, warn, error, success
  • Features:
    • Auto-scroll toggle
    • Clear logs button
    • Colored log levels
    • Timestamp with milliseconds
    • Source labels for log entries
    • Maximum 1000 entries (configurable)
    • Light/dark theme support
  • Demo includes realistic server log simulation
  • Note: In demos, buttons use @clicked event (not @click)
  • Demo uses global reference to access log element (window.__demoLogElement)

UI Components

dees-button-group

  • Groups multiple buttons together with a unified background
  • Properties:
    • label: Optional label text displayed before the buttons
    • direction: 'horizontal' | 'vertical' layout
  • Features:
    • Light/dark theme support
    • Flexible layout with proper spacing
    • Works with all button types (normal, highlighted, success, danger)
  • Use cases:
    • View mode selectors
    • Action grouping
    • Navigation options
    • Filter controls

Form Components

dees-input-radio

  • Radio button component with proper group behavior
  • Properties:
    • name: Group name for mutually exclusive selection
    • key: Unique identifier for the radio option
    • value: Boolean indicating selection state
    • label: Display label
  • Features:
    • Automatic group management (radios with same name are mutually exclusive)
    • Cannot be deselected by clicking (proper radio behavior)
    • Form integration: Radio groups are collected by name, value is the selected radio's key
    • Works both inside and outside forms
    • Supports disabled state
  • Fixed: Radio buttons now properly deselect others in the group on first click
  • Note: When using in forms, set both name (for grouping) and key (for the value)

WYSIWYG Editor Architecture

Recent Refactoring (2025-06-24)

The WYSIWYG editor has been refactored to improve maintainability and separation of concerns:

New Handler Classes

  1. WysiwygBlockOperations (wysiwyg.blockoperations.ts)

    • Manages all block-related operations
    • Methods: createBlock, insertBlockAfter, removeBlock, findBlock, focusBlock, etc.
    • Centralized block manipulation logic
  2. WysiwygInputHandler (wysiwyg.inputhandler.ts)

    • Handles all input events for blocks
    • Manages block content updates based on type
    • Detects block type transformations
    • Handles slash commands
    • Manages auto-save with debouncing
  3. WysiwygKeyboardHandler (wysiwyg.keyboardhandler.ts)

    • Handles all keyboard events
    • Manages formatting shortcuts (Cmd/Ctrl + B/I/U/K)
    • Handles special keys: Tab, Enter, Backspace
    • Manages slash menu navigation
  4. WysiwygDragDropHandler (wysiwyg.dragdrophandler.ts)

    • Manages drag and drop operations
    • Tracks drag state
    • Handles visual feedback during drag
    • Manages block reordering
  5. WysiwygModalManager (wysiwyg.modalmanager.ts)

    • Static methods for showing modals
    • Language selection for code blocks
    • Block settings modal
    • Reusable modal patterns

Main Component Updates

The main DeesInputWysiwyg component now:

  • Instantiates handler classes in connectedCallback
  • Delegates complex operations to appropriate handlers
  • Maintains cleaner, more focused code
  • Better separation of concerns

Benefits

  • Reduced main component size from 1100+ lines
  • Each handler class is focused on a single responsibility
  • Easier to test individual components
  • Better code organization
  • Improved maintainability

Fixed Issues

  • Enter key no longer duplicates content in new blocks
  • Removed problematic setBlockContents() method
  • Content is now managed directly through DOM properties
  • Better timing for block creation and focus
  • Slash menu no longer disappears immediately on first "/" press
  • Focus is properly maintained when slash menu opens
  • Removed duplicate event handling methods from main component
  • Simplified focus management throughout the editor

Additional Refactoring (2025-06-24 - Part 2)

  • Removed duplicate code: handleBlockInput and handleBlockKeyDown methods removed from main component
  • Simplified focus management: Removed complex lifecycle methods and timers
  • Fixed slash menu behavior: Changed to click events and proper event prevention
  • dees-wysiwyg-block component: Now uses static HTML rendering for better content control
  • Improved formatting preservation: HTML formatting (bold, italic, etc.) properly preserved in all block types

Notes

  • All input handling now goes through WysiwygInputHandler
  • All keyboard handling goes through WysiwygKeyboardHandler
  • The slash menu uses click events instead of mousedown for better UX
  • Focus is maintained using requestAnimationFrame for better timing
  • The refactoring maintains all existing functionality with improved reliability

Global Menu Architecture (2025-06-24 - Part 3)

The slash menu and formatting menu have been refactored to render globally instead of inside the wysiwyg component. This fixes focus loss issues that were occurring when the menus were re-rendered with the component.

Key Components:

  1. DeesSlashMenu (dees-slash-menu.ts)

    • Singleton component that renders globally in the document body
    • Accessed via DeesSlashMenu.getInstance()
    • Manages its own visibility, position, and filtering
    • Emits callbacks when items are selected
  2. DeesFormattingMenu (dees-formatting-menu.ts)

    • Singleton component that renders globally in the document body
    • Accessed via DeesFormattingMenu.getInstance()
    • Shows when text is selected
    • Applies formatting commands via callback
  3. Integration in DeesInputWysiwyg

    • Stores singleton instances: private slashMenu = DeesSlashMenu.getInstance()
    • Shows menus with absolute positioning
    • Menus handle their own rendering and state management

Benefits:

  • No focus loss when menus appear/disappear
  • Better performance (menus don't re-render with component)
  • Cleaner separation of concerns
  • Menus persist across component updates

Usage:

// Show slash menu
this.slashMenu.show(
  { x: cursorX, y: cursorY },
  (type: string) => this.insertBlock(type)
);

// Show formatting menu
this.formattingMenu.show(
  { x: selectionX, y: selectionY },
  (command: string) => this.applyFormat(command)
);

Previous Issues Fixed:

  • Slash menu was disappearing immediately on first "/" press
  • Focus was lost when menus appeared
  • Text selection was not working properly
  • Cursor position was lost after menu interactions

Arrow Key Navigation (2025-06-24 - Part 4)

Enhanced arrow key handling for seamless navigation between blocks:

Features:

  1. ArrowUp at block start: Automatically navigates to the end of the previous block
  2. ArrowDown at block end: Automatically navigates to the beginning of the next block
  3. Smart detection: Checks actual cursor position within the block content
  4. Slash menu integration: When slash menu is open, arrow keys navigate menu items instead
  5. No focus loss: Navigation maintains focus throughout

Implementation:

  • Added handleArrowUp() and handleArrowDown() methods to WysiwygKeyboardHandler
  • Smart cursor position detection for different block types (text, lists, etc.)
  • Helper method getLastTextNode() for finding the last text position in complex HTML
  • Prevents default behavior only when navigating between blocks
  • Skips divider blocks during navigation

Focus Management Improvements (2025-06-24 - Part 5)

Enhanced focus management to prevent focus loss during various operations:

Key Improvements:

  1. Formatting Without execCommand:

    • Replaced deprecated document.execCommand with modern DOM manipulation
    • Proper selection restoration after formatting
    • Async formatting operations to maintain focus
  2. Link Dialog:

    • Replaced prompt() with custom modal dialog
    • Maintains focus context during async operations
    • Auto-focuses input field in modal
  3. Robust Focus Methods:

    • Double requestAnimationFrame for DOM update timing
    • Fallback focus attempts with microtasks
    • Contenteditable attribute verification
  4. Cursor Positioning:

    • Enhanced setCursorToStart/End with edge case handling
    • Zero-width space insertion for empty elements
    • Recursive node traversal for complex HTML structures
  5. Async Keyboard Shortcuts:

    • Formatting shortcuts use Promise resolution
    • Prevents focus loss during rapid keyboard input

Implementation Details:

  • focusWithCursor() method now handles empty blocks and complex HTML
  • applyFormat() is async and properly restores selection
  • Link creation no longer uses blocking prompt() dialog
  • All focus operations use proper timing with RAF and microtasks

Focus Loss Prevention for Menus (2025-06-24 - Part 6)

Fixed focus loss issues when slash menu and formatting menu appear:

Key Fixes:

  1. Timeout Reduction:

    • Replaced 50ms setTimeout with requestAnimationFrame
    • Immediate focus attempt before falling back to RAF
    • Reduced delay when inserting blocks
  2. Menu Focus Prevention:

    • Added tabindex="-1" to prevent menus from taking focus
    • Added focus event prevention on menus
    • Menus now use mousedown prevention consistently
  3. Blur Event Handling:

    • Skip value updates when slash menu is visible
    • Prevent auto-save during slash menu interaction
    • Maintain focus after menu appears with RAF
  4. Block Focus Optimization:

    • Try immediate focus if block element exists
    • Fall back to RAF only when necessary
    • Consistent focus handling across all block types

Implementation:

  • handleBlockBlur() checks if slash menu is visible before updating
  • scheduleAutoSave() skips saving when slash menu is open
  • Slash menu show adds RAF to restore focus if lost
  • Reduced timing delays throughout the focus chain

Slash Command Cleanup (2025-06-24 - Part 7)

Fixed the issue where "/" remained in the editor after selecting a block type:

The Fix:

  1. In insertBlock():

    • Clear slash command before transforming block type
    • Use regex /^\/[^\s]*\s*/ to match slash + filter text
    • Trim the result to ensure clean content
    • Set content to empty for transformed blocks
  2. Improved Content Handling:

    • Wait for updateComplete before focusing
    • Ensure lists start with empty content
    • Consistent cleanup in both insertBlock and closeSlashMenu
  3. Edge Cases:

    • Handle filtered commands (e.g., "/hea" for heading)
    • Clear content even with partial matches
    • Proper content reset for all block types

Now when selecting a block type from the slash menu, the "/" and any filter text is properly removed before the block transformation occurs.

Enhanced Enter Key and Block Settings (2025-06-24 - Part 8)

Added two major improvements to the wysiwyg editor:

1. Smart Enter Key Behavior:

When pressing Enter, content after the cursor is now moved to the next block:

  • Content Splitting: Uses Range API to extract content after cursor
  • HTML Preservation: Maintains formatting when splitting blocks
  • Clean Split: Current block keeps content before cursor, new block gets content after
  • Empty Block: If cursor is at end, creates empty new block

Implementation in WysiwygKeyboardHandler.handleEnter():

// Clone the range to extract content after cursor
const afterRange = range.cloneRange();
afterRange.selectNodeContents(target);
afterRange.setStart(range.endContainer, range.endOffset);

// Extract content after cursor
const afterContent = afterRange.extractContents();

2. Block Type Changing via Settings Menu:

The block settings menu (three dots) now includes block type selection:

  • Type Selector Grid: Shows all available block types with icons
  • Smart Metadata Handling:
    • Clears code language when changing from code block
    • Clears list type when changing from list
    • Prompts for language when changing to code block
  • Visual Feedback: Currently selected type is highlighted
  • Instant Update: Block transforms immediately on selection

Features:

  • Works for all block types (not just code blocks)
  • Preserves content during type transformation
  • Handles special cases like code block language selection
  • Modal closes automatically after selection

Complete WYSIWYG Refactoring (2025-06-24 - Part 9)

Major architectural improvements to fix Enter key behavior and left arrow focus loss:

1. Async Operation Architecture:

  • All focus operations are now async with proper Promise handling
  • insertBlockAfter() waits for component updates before focusing
  • focusBlock() ensures DOM is ready with updateComplete
  • Eliminated arbitrary timeouts in favor of proper async/await

2. Enter Key Split Content Fix:

  • Added getSplitContent() method to block component
  • Properly extracts content before/after cursor using Range API
  • Updates current block and creates new block atomically
  • Content after cursor correctly moves to new block
// In block component
public getSplitContent(): { before: string; after: string } | null {
  const beforeRange = range.cloneRange();
  beforeRange.selectNodeContents(this.blockElement);
  beforeRange.setEnd(range.startContainer, range.startOffset);
  // ... extract and return split content
}

3. Arrow Key Navigation:

  • Added ArrowLeft/ArrowRight handlers for block boundaries
  • Prevents focus loss when navigating between blocks
  • Only intercepts at block boundaries, normal navigation otherwise
  • All arrow key operations are async for proper timing

4. Interface Architecture:

Created wysiwyg.interfaces.ts with proper typing:

  • IWysiwygComponent - Main component contract
  • IBlockOperations - Block operation methods
  • IWysiwygBlockComponent - Block component interface
  • IBlockEventHandlers - Event handler signatures

5. Focus Management Improvements:

  • Eliminated double RAF in favor of single async flow
  • Focus operations wait for DOM updates via updateComplete
  • Proper cursor positioning after all operations
  • No more focus loss during navigation

Key Changes:

  1. Keyboard handler methods are now async
  2. Block operations return Promises
  3. Enter key properly splits content at cursor
  4. Arrow keys handle block navigation without focus loss
  5. All timing is handled via proper async/await patterns

The refactoring eliminates race conditions and timing issues that were causing focus loss and content duplication problems.

Programmatic Rendering Solution (2025-06-24 - Part 10)

Fixed persistent focus loss issue by implementing fully programmatic rendering:

The Problem:

  • User would click in a block, type text, then press arrow keys and lose focus
  • Root cause: Lit was re-rendering components when block content was mutated
  • Even with shouldUpdate() preventing re-renders, parent re-evaluation caused focus loss

The Solution:

  1. Static Parent Rendering:

    • Parent component renders only once with empty editor content div
    • All blocks are created and managed programmatically via DOM manipulation
    • No Lit re-renders triggered by state changes
  2. Manual Block Management:

    • renderBlocksProgrammatically() creates all block elements manually
    • createBlockElement() builds block wrapper with all event handlers
    • updateBlockElement() replaces individual blocks when needed
    • No reactive properties trigger parent re-renders
  3. Content Update Strategy:

    • During typing, content is NOT immediately synced to data model
    • Auto-save delayed to 2 seconds to avoid interference
    • Content synced from DOM only on blur or before save
    • syncAllBlockContent() reads from DOM when needed
  4. Focus Preservation:

    • Block components prevent re-renders with shouldUpdate()
    • Parent never re-renders after initial load
    • Focus remains stable during all editing operations
    • Arrow key navigation works without focus loss
  5. Implementation Details:

    // Parent render method - static after first render
    render(): TemplateResult {
      return html`
        <div class="editor-content" id="editor-content">
          <!-- Blocks rendered programmatically -->
        </div>
      `;
    }
    
    // All block operations use DOM manipulation
    private renderBlocksProgrammatically() {
      this.editorContentRef.innerHTML = '';
      this.blocks.forEach(block => {
        const blockWrapper = this.createBlockElement(block);
        this.editorContentRef.appendChild(blockWrapper);
      });
    }
    

This approach completely eliminates focus loss by taking full control of the DOM and preventing any framework-induced re-renders during editing.

Code Refactoring and Cleanup (2025-06-24 - Part 11)

Completed comprehensive refactoring to ensure clean, maintainable code with separated concerns:

Refactoring Changes:

  1. Drag and Drop Handler Cleanup:

    • Removed all requestUpdate() calls from drag handler
    • Handler now only updates internal state
    • Parent component handles DOM updates programmatically
    • Simplified drag state management
  2. Unused Code Removal:

    • Removed duplicate showBlockSettingsModal method (using WysiwygModalManager)
    • Removed duplicate showLanguageSelectionModal method
    • Removed unused renderBlock method
    • Cleaned up unused imports (WysiwygBlocks, ISlashMenuItem)
  3. Import Cleanup:

    • Removed unused type imports
    • Organized imports logically
    • Kept only necessary dependencies
  4. Separated Concerns:

    • Modal management in WysiwygModalManager
    • Block operations in WysiwygBlockOperations
    • Input handling in WysiwygInputHandler
    • Keyboard handling in WysiwygKeyboardHandler
    • Drag/drop in WysiwygDragDropHandler
    • Each class has a single responsibility
  5. Programmatic DOM Management:

    • All DOM updates happen through explicit methods
    • No reactive re-renders during user interaction
    • Manual class management for drag states
    • Direct DOM manipulation for performance
  6. Test Files Created:

    • test-focus-fix.html - Verifies focus management
    • test-drag-drop.html - Tests drag and drop functionality
    • test-comprehensive.html - Tests all features together

The refactoring follows the principles in instructions.md:

  • Uses static templates with manual DOM operations
  • Maintains separated concerns in different classes
  • Results in clean, concise, and manageable code