selection manipulation

This commit is contained in:
Juergen Kunz
2025-06-24 16:53:54 +00:00
parent 1041814823
commit bd223f77d0
2 changed files with 109 additions and 12 deletions

View File

@ -806,6 +806,9 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
if (!targetBlock || !targetBlockComponent) return; if (!targetBlock || !targetBlockComponent) return;
// Create a range from our selection info
const range = WysiwygSelection.createRangeFromInfo(selectionInfo);
// Handle link command specially // Handle link command specially
if (command === 'link') { if (command === 'link') {
const url = await this.showLinkDialog(); const url = await this.showLinkDialog();
@ -814,28 +817,28 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
targetBlockComponent.focus(); targetBlockComponent.focus();
return; return;
} }
// Apply link format // Apply link format with Shadow DOM aware formatting
WysiwygFormatting.applyFormat(command, url); this.applyFormatInShadowDOM(range, command, targetBlockComponent, url);
} else { } else {
// Apply the format // Apply the format with Shadow DOM aware formatting
WysiwygFormatting.applyFormat(command); this.applyFormatInShadowDOM(range, command, targetBlockComponent);
} }
// Update content after a microtask to ensure DOM is updated // Update content immediately
await new Promise(resolve => setTimeout(resolve, 10));
// Force content update
targetBlock.content = targetBlockComponent.getContent(); targetBlock.content = targetBlockComponent.getContent();
// Update value to persist changes // Update value to persist changes
this.updateValue(); this.updateValue();
// Restore focus to the block
targetBlockComponent.focus();
// For link command, close the formatting menu // For link command, close the formatting menu
if (command === 'link') { if (command === 'link') {
this.hideFormattingMenu(); this.hideFormattingMenu();
} else if (this.formattingMenu.visible) { } else {
// Keep selection and update menu position // Let selection handler update menu position
this.updateFormattingMenuPosition(); this.selectedText = '';
} }
} }
@ -967,6 +970,97 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
/** /**
* Save current state to history with cursor position * 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 { public saveToHistory(debounce: boolean = true): void {
// Get current cursor position if a block is focused // Get current cursor position if a block is focused
let cursorPosition: { blockId: string; offset: number } | undefined; let cursorPosition: { blockId: string; offset: number } | undefined;

View File

@ -667,7 +667,10 @@ export class DeesWysiwygBlock extends DeesElement {
} else if (this.block.type === 'code') { } else if (this.block.type === 'code') {
return editableElement.textContent || ''; return editableElement.textContent || '';
} else { } 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;
} }
} }