fix(dees-modal): theming

This commit is contained in:
Juergen Kunz
2025-06-24 10:45:06 +00:00
parent c82c407350
commit 8b02c5aea3
18 changed files with 2283 additions and 600 deletions

View File

@ -42,46 +42,104 @@ export class WysiwygFormatting {
`;
}
static applyFormat(command: string, value?: string): void {
static applyFormat(command: string, value?: string): boolean {
// Save current selection
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return;
if (!selection || selection.rangeCount === 0) return false;
const range = selection.getRangeAt(0);
// Apply format based on command
switch (command) {
case 'bold':
this.wrapSelection(range, 'strong');
break;
case 'italic':
this.wrapSelection(range, 'em');
break;
case 'underline':
this.wrapSelection(range, 'u');
break;
case 'strikeThrough':
document.execCommand(command, false);
this.wrapSelection(range, 's');
break;
case 'code':
// For inline code, wrap selection in <code> tags
const codeElement = document.createElement('code');
try {
codeElement.appendChild(range.extractContents());
range.insertNode(codeElement);
// Select the newly created code element
range.selectNodeContents(codeElement);
selection.removeAllRanges();
selection.addRange(range);
} catch (e) {
// Fallback to execCommand if range manipulation fails
document.execCommand('fontName', false, 'monospace');
}
this.wrapSelection(range, 'code');
break;
case 'link':
const url = value || prompt('Enter URL:');
if (url) {
document.execCommand('createLink', false, url);
// Don't use prompt - return false to indicate we need async input
if (!value) {
return false;
}
this.wrapSelectionWithLink(range, value);
break;
}
return true;
}
private static wrapSelection(range: Range, tagName: string): void {
const selection = window.getSelection();
if (!selection) return;
// 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);
// Restore selection
selection.removeAllRanges();
selection.addRange(range);
} else {
// Wrap selection
const wrapper = document.createElement(tagName);
try {
// Extract and wrap contents
const contents = range.extractContents();
wrapper.appendChild(contents);
range.insertNode(wrapper);
// Select the wrapped content
range.selectNodeContents(wrapper);
selection.removeAllRanges();
selection.addRange(range);
} catch (e) {
console.error('Failed to wrap selection:', e);
}
}
}
private static wrapSelectionWithLink(range: Range, url: string): void {
const selection = window.getSelection();
if (!selection) return;
const link = document.createElement('a');
link.href = url;
link.target = '_blank';
link.rel = 'noopener noreferrer';
try {
const contents = range.extractContents();
link.appendChild(contents);
range.insertNode(link);
// Select the link
range.selectNodeContents(link);
selection.removeAllRanges();
selection.addRange(range);
} catch (e) {
console.error('Failed to create link:', e);
}
}
static getSelectionCoordinates(shadowRoot?: ShadowRoot): { x: number, y: number } | null {
@ -118,10 +176,33 @@ export class WysiwygFormatting {
}
static isFormattingApplied(command: string): boolean {
try {
return document.queryCommandState(command);
} catch {
return false;
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return false;
const range = selection.getRangeAt(0);
const container = range.commonAncestorContainer;
const element = container.nodeType === Node.TEXT_NODE
? container.parentElement
: container as Element;
if (!element) return false;
// Check if formatting is applied by looking at parent elements
switch (command) {
case 'bold':
return !!element.closest('b, strong');
case 'italic':
return !!element.closest('i, em');
case 'underline':
return !!element.closest('u');
case 'strikeThrough':
return !!element.closest('s, strike');
case 'code':
return !!element.closest('code');
case 'link':
return !!element.closest('a');
default:
return false;
}
}