Lexical: Further fixes

- Improved node resizer positioning to be more accurate
- Fixed drop handling not running within editor margin space
- Made media dom update smarter to reduce reloads
- Fixed media alignment, broken due to added wrapper
This commit is contained in:
Dan Brown 2024-09-09 12:28:01 +01:00
parent 16518a4f89
commit fd07aa0f05
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
7 changed files with 68 additions and 12 deletions

View File

@ -13,7 +13,7 @@ import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler"
import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler"; import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler";
import {el} from "./utils/dom"; import {el} from "./utils/dom";
import {registerShortcuts} from "./services/shortcuts"; import {registerShortcuts} from "./services/shortcuts";
import {registerNodeResizer} from "./ui/framework/helpers/image-resizer"; import {registerNodeResizer} from "./ui/framework/helpers/node-resizer";
export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface { export function createPageEditorInstance(container: HTMLElement, htmlContent: string, options: Record<string, any> = {}): SimpleWysiwygEditorInterface {
const config: CreateEditorArgs = { const config: CreateEditorArgs = {

View File

@ -4,18 +4,17 @@ import {
ElementNode, ElementNode,
LexicalEditor, LexicalEditor,
LexicalNode, LexicalNode,
SerializedElementNode, Spread Spread
} from 'lexical'; } from 'lexical';
import type {EditorConfig} from "lexical/LexicalEditor"; import type {EditorConfig} from "lexical/LexicalEditor";
import {el, sizeToPixels} from "../utils/dom"; import {el, setOrRemoveAttribute, sizeToPixels} from "../utils/dom";
import { import {
CommonBlockAlignment, CommonBlockAlignment,
SerializedCommonBlockNode, SerializedCommonBlockNode,
setCommonBlockPropsFromElement, setCommonBlockPropsFromElement,
updateElementWithCommonBlockProps updateElementWithCommonBlockProps
} from "./_common"; } from "./_common";
import {elem} from "../../services/dom";
import {$selectSingleNode} from "../utils/selection"; import {$selectSingleNode} from "../utils/selection";
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio'; export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
@ -218,10 +217,39 @@ export class MediaNode extends ElementNode {
return wrap; return wrap;
} }
updateDOM(prevNode: unknown, dom: HTMLElement) { updateDOM(prevNode: MediaNode, dom: HTMLElement): boolean {
if (prevNode.__tag !== this.__tag) {
return true; return true;
} }
if (JSON.stringify(prevNode.__sources) !== JSON.stringify(this.__sources)) {
return true;
}
if (JSON.stringify(prevNode.__attributes) !== JSON.stringify(this.__attributes)) {
return true;
}
const mediaEl = dom.firstElementChild as HTMLElement;
if (prevNode.__id !== this.__id) {
setOrRemoveAttribute(mediaEl, 'id', this.__id);
}
if (prevNode.__alignment !== this.__alignment) {
if (prevNode.__alignment) {
dom.classList.remove(`align-${prevNode.__alignment}`);
mediaEl.classList.remove(`align-${prevNode.__alignment}`);
}
if (this.__alignment) {
dom.classList.add(`align-${this.__alignment}`);
mediaEl.classList.add(`align-${this.__alignment}`);
}
}
return false;
}
static importDOM(): DOMConversionMap|null { static importDOM(): DOMConversionMap|null {
const buildConverter = (tag: MediaNodeTag) => { const buildConverter = (tag: MediaNodeTag) => {

View File

@ -103,6 +103,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo
if (templateId) { if (templateId) {
insertTemplateToEditor(editor, templateId, event); insertTemplateToEditor(editor, templateId, event);
event.preventDefault(); event.preventDefault();
event.stopPropagation();
return true; return true;
} }
@ -114,6 +115,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo
$insertNodesAtEvent(newNodes, event, editor); $insertNodesAtEvent(newNodes, event, editor);
}); });
event.preventDefault(); event.preventDefault();
event.stopPropagation();
return true; return true;
} }
@ -121,6 +123,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo
const handled = handleMediaInsert(event.dataTransfer, context); const handled = handleMediaInsert(event.dataTransfer, context);
if (handled) { if (handled) {
event.preventDefault(); event.preventDefault();
event.stopPropagation();
return true; return true;
} }
} }
@ -150,9 +153,11 @@ export function registerDropPasteHandling(context: EditorUiContext): () => void
const unregisterDrop = context.editor.registerCommand(DROP_COMMAND, dropListener, COMMAND_PRIORITY_HIGH); const unregisterDrop = context.editor.registerCommand(DROP_COMMAND, dropListener, COMMAND_PRIORITY_HIGH);
const unregisterPaste = context.editor.registerCommand(PASTE_COMMAND, pasteListener, COMMAND_PRIORITY_HIGH); const unregisterPaste = context.editor.registerCommand(PASTE_COMMAND, pasteListener, COMMAND_PRIORITY_HIGH);
context.scrollDOM.addEventListener('drop', dropListener);
return () => { return () => {
unregisterDrop(); unregisterDrop();
unregisterPaste(); unregisterPaste();
context.scrollDOM.removeEventListener('drop', dropListener);
}; };
} }

View File

@ -16,4 +16,4 @@
## Bugs ## Bugs
- Template drag/drop not handled when outside core editor area (ignored in margin area). //

View File

@ -73,11 +73,15 @@ class NodeResizer {
return; return;
} }
const nodeDOMBounds = nodeDOM.getBoundingClientRect(); const scrollAreaRect = this.scrollContainer.getBoundingClientRect();
this.dom.style.left = nodeDOM.offsetLeft + 'px'; const nodeRect = nodeDOM.getBoundingClientRect();
this.dom.style.top = nodeDOM.offsetTop + 'px'; const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop);
this.dom.style.width = nodeDOMBounds.width + 'px'; const left = nodeRect.left - scrollAreaRect.left;
this.dom.style.height = nodeDOMBounds.height + 'px';
this.dom.style.top = `${top}px`;
this.dom.style.left = `${left}px`;
this.dom.style.width = nodeRect.width + 'px';
this.dom.style.height = nodeRect.height + 'px';
} }
protected updateDOMSize(width: number, height: number): void { protected updateDOMSize(width: number, height: number): void {

View File

@ -71,3 +71,11 @@ export function extractStyleMapFromElement(element: HTMLElement): StyleMap {
return map; return map;
} }
export function setOrRemoveAttribute(element: HTMLElement, name: string, value: string|null|undefined) {
if (value) {
element.setAttribute(name, value);
} else {
element.removeAttribute(name);
}
}

View File

@ -374,10 +374,21 @@ body.editor-is-fullscreen {
} }
.editor-media-wrap { .editor-media-wrap {
display: inline-block;
cursor: not-allowed; cursor: not-allowed;
iframe { iframe {
pointer-events: none; pointer-events: none;
} }
&.align-left {
float: left;
}
&.align-right {
float: right;
}
&.align-center {
display: block;
margin-inline: auto;
}
} }
/** /**