selection manipulation
This commit is contained in:
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user