fix(wysiwyg): Fix text selection detection for formatting menu in Shadow DOM
- Update selection detection to properly handle Shadow DOM boundaries - Use getComposedRanges API correctly according to MDN documentation - Add direct selection detection within block components - Dispatch custom events from blocks when text is selected - Fix formatting menu positioning using selection rect from events
This commit is contained in:
@ -315,6 +315,13 @@ export class DeesWysiwygBlock extends DeesElement {
|
||||
if (pos !== null) {
|
||||
this.lastKnownCursorPosition = pos;
|
||||
}
|
||||
|
||||
// Check for selection after keyboard navigation
|
||||
if (e.shiftKey || ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
|
||||
setTimeout(() => {
|
||||
this.checkForTextSelection();
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
editableBlock.addEventListener('focus', () => {
|
||||
@ -341,6 +348,9 @@ export class DeesWysiwygBlock extends DeesElement {
|
||||
this.lastKnownCursorPosition = pos;
|
||||
console.log('Cursor position after mouseup:', pos);
|
||||
}
|
||||
|
||||
// Check for text selection
|
||||
this.checkForTextSelection();
|
||||
}, 0);
|
||||
|
||||
this.handleMouseUp(e);
|
||||
@ -778,32 +788,75 @@ export class DeesWysiwygBlock extends DeesElement {
|
||||
}
|
||||
|
||||
private handleMouseUp(_e: MouseEvent): void {
|
||||
// Check if we have a selection within this block
|
||||
setTimeout(() => {
|
||||
const selection = window.getSelection();
|
||||
if (selection && selection.rangeCount > 0) {
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
// Check if selection is within this block
|
||||
const editableElement = this.block?.type === 'code'
|
||||
? this.shadowRoot?.querySelector('.block.code') as HTMLDivElement
|
||||
: this.shadowRoot?.querySelector('.block') as HTMLDivElement;
|
||||
if (editableElement && editableElement.contains(range.commonAncestorContainer)) {
|
||||
const selectedText = selection.toString();
|
||||
if (selectedText.length > 0) {
|
||||
// Dispatch a custom event that can cross shadow DOM boundaries
|
||||
this.dispatchEvent(new CustomEvent('block-text-selected', {
|
||||
detail: {
|
||||
text: selectedText,
|
||||
blockId: this.block.id,
|
||||
range: range
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 10);
|
||||
// Selection check is now handled in the mouseup event listener
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there's text selected within this block
|
||||
*/
|
||||
private checkForTextSelection(): void {
|
||||
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
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedText = selection.toString().trim();
|
||||
|
||||
// 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
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the selection is within this block
|
||||
const range = selection.getRangeAt(0);
|
||||
|
||||
// Check if both start and end are within our editable element
|
||||
const startInBlock = editableElement.contains(range.startContainer);
|
||||
const endInBlock = editableElement.contains(range.endContainer);
|
||||
|
||||
if (startInBlock && endInBlock) {
|
||||
console.log('Block detected text selection:', selectedText);
|
||||
|
||||
// Get the bounding rect of the selection
|
||||
const rect = range.getBoundingClientRect();
|
||||
|
||||
// Dispatch event to parent with selection details
|
||||
this.dispatchEvent(new CustomEvent('block-text-selected', {
|
||||
detail: {
|
||||
text: selectedText,
|
||||
blockId: this.block.id,
|
||||
range: range,
|
||||
rect: rect,
|
||||
hasSelection: true
|
||||
},
|
||||
bubbles: true,
|
||||
composed: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user