selection manipulation
This commit is contained in:
@ -806,6 +806,9 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
|
||||
if (!targetBlock || !targetBlockComponent) return;
|
||||
|
||||
// Create a range from our selection info
|
||||
const range = WysiwygSelection.createRangeFromInfo(selectionInfo);
|
||||
|
||||
// Handle link command specially
|
||||
if (command === 'link') {
|
||||
const url = await this.showLinkDialog();
|
||||
@ -814,28 +817,28 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
targetBlockComponent.focus();
|
||||
return;
|
||||
}
|
||||
// Apply link format
|
||||
WysiwygFormatting.applyFormat(command, url);
|
||||
// Apply link format with Shadow DOM aware formatting
|
||||
this.applyFormatInShadowDOM(range, command, targetBlockComponent, url);
|
||||
} else {
|
||||
// Apply the format
|
||||
WysiwygFormatting.applyFormat(command);
|
||||
// Apply the format with Shadow DOM aware formatting
|
||||
this.applyFormatInShadowDOM(range, command, targetBlockComponent);
|
||||
}
|
||||
|
||||
// Update content after a microtask to ensure DOM is updated
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
// Force content update
|
||||
// Update content immediately
|
||||
targetBlock.content = targetBlockComponent.getContent();
|
||||
|
||||
// Update value to persist changes
|
||||
this.updateValue();
|
||||
|
||||
// Restore focus to the block
|
||||
targetBlockComponent.focus();
|
||||
|
||||
// For link command, close the formatting menu
|
||||
if (command === 'link') {
|
||||
this.hideFormattingMenu();
|
||||
} else if (this.formattingMenu.visible) {
|
||||
// Keep selection and update menu position
|
||||
this.updateFormattingMenuPosition();
|
||||
} else {
|
||||
// Let selection handler update menu position
|
||||
this.selectedText = '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -967,6 +970,97 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
||||
/**
|
||||
* Save current state to history with cursor position
|
||||
*/
|
||||
/**
|
||||
* Apply formatting within Shadow DOM context
|
||||
*/
|
||||
private applyFormatInShadowDOM(range: Range, command: string, blockComponent: any, value?: string): void {
|
||||
const editableElement = blockComponent.shadowRoot?.querySelector('.block') as HTMLElement;
|
||||
if (!editableElement) return;
|
||||
|
||||
// Apply format based on command
|
||||
switch (command) {
|
||||
case 'bold':
|
||||
this.wrapSelectionInShadowDOM(range, 'strong', editableElement);
|
||||
break;
|
||||
|
||||
case 'italic':
|
||||
this.wrapSelectionInShadowDOM(range, 'em', editableElement);
|
||||
break;
|
||||
|
||||
case 'underline':
|
||||
this.wrapSelectionInShadowDOM(range, 'u', editableElement);
|
||||
break;
|
||||
|
||||
case 'strikeThrough':
|
||||
this.wrapSelectionInShadowDOM(range, 's', editableElement);
|
||||
break;
|
||||
|
||||
case 'code':
|
||||
this.wrapSelectionInShadowDOM(range, 'code', editableElement);
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
if (value) {
|
||||
this.wrapSelectionWithLinkInShadowDOM(range, value, editableElement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap selection with a tag within Shadow DOM
|
||||
*/
|
||||
private wrapSelectionInShadowDOM(range: Range, tagName: string, editableElement: HTMLElement): void {
|
||||
try {
|
||||
// Check if we're already wrapped in this tag
|
||||
const parentElement = range.commonAncestorContainer.parentElement;
|
||||
if (parentElement && parentElement.tagName.toLowerCase() === tagName) {
|
||||
// Unwrap
|
||||
const parent = parentElement.parentNode;
|
||||
while (parentElement.firstChild) {
|
||||
parent?.insertBefore(parentElement.firstChild, parentElement);
|
||||
}
|
||||
parent?.removeChild(parentElement);
|
||||
} else {
|
||||
// Wrap selection
|
||||
const wrapper = editableElement.ownerDocument.createElement(tagName);
|
||||
const contents = range.extractContents();
|
||||
wrapper.appendChild(contents);
|
||||
range.insertNode(wrapper);
|
||||
|
||||
// Select the wrapped content
|
||||
range.selectNodeContents(wrapper);
|
||||
|
||||
// Update selection using our Shadow DOM utilities
|
||||
WysiwygSelection.setSelectionFromRange(range);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to apply format:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap selection with a link within Shadow DOM
|
||||
*/
|
||||
private wrapSelectionWithLinkInShadowDOM(range: Range, url: string, editableElement: HTMLElement): void {
|
||||
try {
|
||||
const link = editableElement.ownerDocument.createElement('a');
|
||||
link.href = url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
|
||||
const contents = range.extractContents();
|
||||
link.appendChild(contents);
|
||||
range.insertNode(link);
|
||||
|
||||
// Select the link
|
||||
range.selectNodeContents(link);
|
||||
WysiwygSelection.setSelectionFromRange(range);
|
||||
} catch (e) {
|
||||
console.error('Failed to create link:', e);
|
||||
}
|
||||
}
|
||||
|
||||
public saveToHistory(debounce: boolean = true): void {
|
||||
// Get current cursor position if a block is focused
|
||||
let cursorPosition: { blockId: string; offset: number } | undefined;
|
||||
|
@ -667,7 +667,10 @@ export class DeesWysiwygBlock extends DeesElement {
|
||||
} else if (this.block.type === 'code') {
|
||||
return editableElement.textContent || '';
|
||||
} else {
|
||||
return editableElement.innerHTML || '';
|
||||
// For regular blocks, get the innerHTML which includes formatting tags
|
||||
const content = editableElement.innerHTML || '';
|
||||
console.log('Getting content from block:', content);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user