fix(ts_web): resolve TypeScript nullability and event typing issues across web components

This commit is contained in:
2026-04-01 05:00:21 +00:00
parent b1c8a7446e
commit af1f660486
78 changed files with 429 additions and 399 deletions

View File

@@ -200,15 +200,16 @@ export class HeadingBlockHandler extends BaseBlockHandler {
const wysiwygBlock = (headingBlock.getRootNode() as ShadowRoot).host as any;
if (wysiwygBlock) {
const originalDisconnectedCallback = (wysiwygBlock as any).disconnectedCallback;
const self = this;
(wysiwygBlock as any).disconnectedCallback = async function() {
if (this.selectionHandler) {
document.removeEventListener('selectionchange', this.selectionHandler);
this.selectionHandler = null;
if (self.selectionHandler) {
document.removeEventListener('selectionchange', self.selectionHandler);
self.selectionHandler = null;
}
if (originalDisconnectedCallback) {
await originalDisconnectedCallback.call(wysiwygBlock);
}
}.bind(this);
};
}
}

View File

@@ -213,15 +213,16 @@ export class ListBlockHandler extends BaseBlockHandler {
const wysiwygBlock = (listBlock.getRootNode() as ShadowRoot).host as any;
if (wysiwygBlock) {
const originalDisconnectedCallback = (wysiwygBlock as any).disconnectedCallback;
const self = this;
(wysiwygBlock as any).disconnectedCallback = async function() {
if (this.selectionHandler) {
document.removeEventListener('selectionchange', this.selectionHandler);
this.selectionHandler = null;
if (self.selectionHandler) {
document.removeEventListener('selectionchange', self.selectionHandler);
self.selectionHandler = null;
}
if (originalDisconnectedCallback) {
await originalDisconnectedCallback.call(wysiwygBlock);
}
}.bind(this);
};
}
}

View File

@@ -193,15 +193,16 @@ export class ParagraphBlockHandler extends BaseBlockHandler {
const wysiwygBlock = element.closest('dees-wysiwyg-block');
if (wysiwygBlock) {
const originalDisconnectedCallback = (wysiwygBlock as any).disconnectedCallback;
const self = this;
(wysiwygBlock as any).disconnectedCallback = async function() {
if (this.selectionHandler) {
document.removeEventListener('selectionchange', this.selectionHandler);
this.selectionHandler = null;
if (self.selectionHandler) {
document.removeEventListener('selectionchange', self.selectionHandler);
self.selectionHandler = null;
}
if (originalDisconnectedCallback) {
await originalDisconnectedCallback.call(wysiwygBlock);
}
}.bind(this);
};
}
}

View File

@@ -192,15 +192,16 @@ export class QuoteBlockHandler extends BaseBlockHandler {
const wysiwygBlock = (quoteBlock.getRootNode() as ShadowRoot).host as any;
if (wysiwygBlock) {
const originalDisconnectedCallback = (wysiwygBlock as any).disconnectedCallback;
const self = this;
(wysiwygBlock as any).disconnectedCallback = async function() {
if (this.selectionHandler) {
document.removeEventListener('selectionchange', this.selectionHandler);
this.selectionHandler = null;
if (self.selectionHandler) {
document.removeEventListener('selectionchange', self.selectionHandler);
self.selectionHandler = null;
}
if (originalDisconnectedCallback) {
await originalDisconnectedCallback.call(wysiwygBlock);
}
}.bind(this);
};
}
}

View File

@@ -188,36 +188,39 @@ export class DeesFormattingMenu extends DeesElement {
public firstUpdated(): void {
// Set up event delegation for the menu
this.shadowRoot?.addEventListener('mousedown', (e: MouseEvent) => {
this.shadowRoot!.addEventListener('mousedown', (e: Event) => {
const mouseEvent = e as MouseEvent;
const menu = this.shadowRoot?.querySelector('.formatting-menu');
if (menu && menu.contains(e.target as Node)) {
if (menu && menu.contains(mouseEvent.target as Node)) {
// Prevent focus loss
e.preventDefault();
e.stopPropagation();
mouseEvent.preventDefault();
mouseEvent.stopPropagation();
}
});
this.shadowRoot?.addEventListener('click', (e: MouseEvent) => {
const target = e.target as HTMLElement;
this.shadowRoot!.addEventListener('click', (e: Event) => {
const mouseEvent = e as MouseEvent;
const target = mouseEvent.target as HTMLElement;
const button = target.closest('.format-button') as HTMLElement;
if (button) {
e.preventDefault();
e.stopPropagation();
mouseEvent.preventDefault();
mouseEvent.stopPropagation();
const command = button.getAttribute('data-command');
if (command) {
this.applyFormat(command);
}
}
});
this.shadowRoot?.addEventListener('focus', (e: FocusEvent) => {
this.shadowRoot!.addEventListener('focus', (e: Event) => {
const focusEvent = e as FocusEvent;
const menu = this.shadowRoot?.querySelector('.formatting-menu');
if (menu && menu.contains(e.target as Node)) {
if (menu && menu.contains(focusEvent.target as Node)) {
// Prevent menu from taking focus
e.preventDefault();
e.stopPropagation();
focusEvent.preventDefault();
focusEvent.stopPropagation();
}
}, true); // Use capture phase
}

View File

@@ -77,7 +77,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
@state()
accessor selectedText: string = '';
public editorContentRef: HTMLDivElement;
public editorContentRef!: HTMLDivElement;
public isComposing: boolean = false;
// Handler instances
@@ -144,7 +144,7 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
// No global selection listener needed
// Listen for custom selection events from blocks
this.addEventListener('block-text-selected', (e: CustomEvent) => {
this.addEventListener('block-text-selected', ((e: CustomEvent) => {
if (!this.slashMenu.visible && e.detail.hasSelection && e.detail.text.length > 0) {
this.selectedText = e.detail.text;
@@ -164,8 +164,8 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
);
}
}
});
}) as EventListener);
// Hide formatting menu when clicking outside
document.addEventListener('mousedown', (e) => {
// Check if click is on the formatting menu itself
@@ -896,14 +896,14 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
{
name: 'Cancel',
action: async (modal) => {
modal.destroy();
modal!.destroy();
resolve(null);
}
},
{
name: 'Add Link',
action: async (modal) => {
modal.destroy();
modal!.destroy();
resolve(linkUrl);
}
}

View File

@@ -216,46 +216,50 @@ export class DeesSlashMenu extends DeesElement {
public firstUpdated(): void {
// Set up event delegation
this.shadowRoot?.addEventListener('mousedown', (e: MouseEvent) => {
this.shadowRoot!.addEventListener('mousedown', (e: Event) => {
const mouseEvent = e as MouseEvent;
const menu = this.shadowRoot?.querySelector('.slash-menu');
if (menu && menu.contains(e.target as Node)) {
if (menu && menu.contains(mouseEvent.target as Node)) {
// Prevent focus loss
e.preventDefault();
e.stopPropagation();
mouseEvent.preventDefault();
mouseEvent.stopPropagation();
}
});
this.shadowRoot?.addEventListener('click', (e: MouseEvent) => {
const target = e.target as HTMLElement;
this.shadowRoot!.addEventListener('click', (e: Event) => {
const mouseEvent = e as MouseEvent;
const target = mouseEvent.target as HTMLElement;
const menuItem = target.closest('.slash-menu-item') as HTMLElement;
if (menuItem) {
e.preventDefault();
e.stopPropagation();
mouseEvent.preventDefault();
mouseEvent.stopPropagation();
const itemType = menuItem.getAttribute('data-item-type');
if (itemType) {
this.selectItem(itemType);
}
}
});
this.shadowRoot?.addEventListener('mouseenter', (e: MouseEvent) => {
const target = e.target as HTMLElement;
this.shadowRoot!.addEventListener('mouseenter', (e: Event) => {
const mouseEvent = e as MouseEvent;
const target = mouseEvent.target as HTMLElement;
const menuItem = target.closest('.slash-menu-item') as HTMLElement;
if (menuItem) {
const index = parseInt(menuItem.getAttribute('data-item-index') || '0', 10);
this.selectedIndex = index;
}
}, true); // Use capture phase
this.shadowRoot?.addEventListener('focus', (e: FocusEvent) => {
this.shadowRoot!.addEventListener('focus', (e: Event) => {
const focusEvent = e as FocusEvent;
const menu = this.shadowRoot?.querySelector('.slash-menu');
if (menu && menu.contains(e.target as Node)) {
if (menu && menu.contains(focusEvent.target as Node)) {
// Prevent menu from taking focus
e.preventDefault();
e.stopPropagation();
focusEvent.preventDefault();
focusEvent.stopPropagation();
}
}, true); // Use capture phase
}

View File

@@ -33,13 +33,13 @@ export class DeesWysiwygBlock extends DeesElement {
}
}
@property({ type: Object })
accessor block: IBlock;
accessor block!: IBlock;
@property({ type: Boolean })
accessor isSelected: boolean = false;
@property({ type: Object })
accessor handlers: IBlockEventHandlers;
accessor handlers!: IBlockEventHandlers;
@property({ type: Object })
accessor wysiwygComponent: any; // Reference to parent dees-input-wysiwyg

View File

@@ -105,10 +105,10 @@ export class WysiwygDragDropHandler {
handleDragEnd(): void {
// Clean up visual state
const allBlocks = this.component.editorContentRef.querySelectorAll('.block-wrapper');
allBlocks.forEach((block: HTMLElement) => {
block.classList.remove('dragging', 'move-up', 'move-down');
block.style.removeProperty('--drag-offset');
block.style.removeProperty('transform');
allBlocks.forEach((block) => {
(block as HTMLElement).classList.remove('dragging', 'move-up', 'move-down');
(block as HTMLElement).style.removeProperty('--drag-offset');
(block as HTMLElement).style.removeProperty('transform');
});
// Remove dragging class from editor

View File

@@ -704,17 +704,17 @@ export class WysiwygKeyboardHandler {
const rect = range.getBoundingClientRect();
// Get the container element
let container = range.commonAncestorContainer;
let container: Node = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) {
container = container.parentElement;
container = container.parentElement!;
}
// Get the top position of the container
const containerRect = (container as Element).getBoundingClientRect();
// Check if we're near the top (within 5px tolerance for line height variations)
const isNearTop = rect.top - containerRect.top < 5;
// For single-line content, also check if we're at the beginning
if (container.textContent && !container.textContent.includes('\n')) {
const cursorPos = WysiwygSelection.getCursorPositionInElement(container as Element, ...shadowRoots);
@@ -740,11 +740,11 @@ export class WysiwygKeyboardHandler {
const rect = range.getBoundingClientRect();
// Get the container element
let container = range.commonAncestorContainer;
let container: Node = range.commonAncestorContainer;
if (container.nodeType === Node.TEXT_NODE) {
container = container.parentElement;
container = container.parentElement!;
}
// Get the bottom position of the container
const containerRect = (container as Element).getBoundingClientRect();

View File

@@ -72,7 +72,7 @@ export class WysiwygModalManager {
{
name: 'Cancel',
action: async (modal) => {
modal.destroy();
modal!.destroy();
resolve(null);
}
}
@@ -158,7 +158,7 @@ export class WysiwygModalManager {
{
name: 'Done',
action: async (modal) => {
modal.destroy();
modal!.destroy();
}
}
]