fix(wysiwyg): Improve text selection detection with block-level approach

- Remove global selectionchange listener in favor of block-level detection
- Add comprehensive debugging logs to track selection detection
- Add multiple event listeners (mouseup, keyup, selectstart) for better coverage
- Add debounced selection checking to avoid race conditions
- Add click-outside handler to hide formatting menu
- Simplify selection detection logic by removing complex shadow DOM traversal
This commit is contained in:
Juergen Kunz
2025-06-24 16:31:00 +00:00
parent 3b93bd63a7
commit 366544befc
2 changed files with 93 additions and 201 deletions

View File

@ -349,8 +349,10 @@ export class DeesWysiwygBlock extends DeesElement {
console.log('Cursor position after mouseup:', pos);
}
// Check for text selection
this.checkForTextSelection();
// Check for text selection with a longer delay
setTimeout(() => {
this.checkForTextSelection();
}, 50);
}, 0);
this.handleMouseUp(e);
@ -368,6 +370,31 @@ export class DeesWysiwygBlock extends DeesElement {
}, 0);
});
// Add select event listener
editableBlock.addEventListener('selectstart', () => {
console.log('Selection started in block');
});
// Listen for selection changes with a mutation observer
let selectionCheckTimeout: any = null;
const checkSelectionDebounced = () => {
if (selectionCheckTimeout) clearTimeout(selectionCheckTimeout);
selectionCheckTimeout = setTimeout(() => {
this.checkForTextSelection();
}, 100);
};
// Check selection on various events
editableBlock.addEventListener('mouseup', checkSelectionDebounced);
editableBlock.addEventListener('keyup', checkSelectionDebounced);
document.addEventListener('selectionchange', () => {
// Check if this block has focus
if (document.activeElement === editableBlock ||
this.shadowRoot?.activeElement === editableBlock) {
checkSelectionDebounced();
}
});
// Set initial content if needed
if (this.block.content) {
if (this.block.type === 'code') {
@ -798,57 +825,51 @@ export class DeesWysiwygBlock extends DeesElement {
const editableElement = this.block?.type === 'code'
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
if (!editableElement) return;
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) {
// Dispatch event to clear selection
this.dispatchEvent(new CustomEvent('block-text-selected', {
detail: {
text: '',
blockId: this.block.id,
hasSelection: false
},
bubbles: true,
composed: true
}));
if (!editableElement) {
console.log('checkForTextSelection: No editable element found');
return;
}
const selectedText = selection.toString().trim();
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) {
console.log('checkForTextSelection: No selection or range count is 0');
return;
}
const selectedText = selection.toString();
console.log('checkForTextSelection: Selected text raw:', selectedText, 'length:', selectedText.length);
// Only proceed if we have selected text
if (selectedText.length === 0) {
// Dispatch event to clear selection
this.dispatchEvent(new CustomEvent('block-text-selected', {
detail: {
text: '',
blockId: this.block.id,
hasSelection: false
},
bubbles: true,
composed: true
}));
if (selectedText.trim().length === 0) {
console.log('checkForTextSelection: Selected text is empty after trim');
return;
}
// Check if the selection is within this block
const range = selection.getRangeAt(0);
console.log('checkForTextSelection: Range:', {
startContainer: range.startContainer,
endContainer: range.endContainer,
collapsed: range.collapsed
});
// Check if both start and end are within our editable element
const startInBlock = editableElement.contains(range.startContainer);
const endInBlock = editableElement.contains(range.endContainer);
console.log('checkForTextSelection: Start in block:', startInBlock, 'End in block:', endInBlock);
if (startInBlock && endInBlock) {
console.log('Block detected text selection:', selectedText);
console.log('Block detected text selection:', selectedText.trim());
// Get the bounding rect of the selection
const rect = range.getBoundingClientRect();
console.log('Selection rect:', rect);
// Dispatch event to parent with selection details
this.dispatchEvent(new CustomEvent('block-text-selected', {
detail: {
text: selectedText,
text: selectedText.trim(),
blockId: this.block.id,
range: range,
rect: rect,
@ -857,6 +878,8 @@ export class DeesWysiwygBlock extends DeesElement {
bubbles: true,
composed: true
}));
} else {
console.log('checkForTextSelection: Selection not contained in block');
}
}
}