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:
		
							parent
							
								
									16518a4f89
								
							
						
					
					
						commit
						fd07aa0f05
					
				|  | @ -13,7 +13,7 @@ import {registerTaskListHandler} from "./ui/framework/helpers/task-list-handler" | |||
| import {registerTableSelectionHandler} from "./ui/framework/helpers/table-selection-handler"; | ||||
| import {el} from "./utils/dom"; | ||||
| 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 { | ||||
|     const config: CreateEditorArgs = { | ||||
|  |  | |||
|  | @ -4,18 +4,17 @@ import { | |||
|     ElementNode, | ||||
|     LexicalEditor, | ||||
|     LexicalNode, | ||||
|     SerializedElementNode, Spread | ||||
|     Spread | ||||
| } from 'lexical'; | ||||
| import type {EditorConfig} from "lexical/LexicalEditor"; | ||||
| 
 | ||||
| import {el, sizeToPixels} from "../utils/dom"; | ||||
| import {el, setOrRemoveAttribute, sizeToPixels} from "../utils/dom"; | ||||
| import { | ||||
|     CommonBlockAlignment, | ||||
|     SerializedCommonBlockNode, | ||||
|     setCommonBlockPropsFromElement, | ||||
|     updateElementWithCommonBlockProps | ||||
| } from "./_common"; | ||||
| import {elem} from "../../services/dom"; | ||||
| import {$selectSingleNode} from "../utils/selection"; | ||||
| 
 | ||||
| export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio'; | ||||
|  | @ -218,10 +217,39 @@ export class MediaNode extends ElementNode { | |||
|         return wrap; | ||||
|     } | ||||
| 
 | ||||
|     updateDOM(prevNode: unknown, dom: HTMLElement) { | ||||
|     updateDOM(prevNode: MediaNode, dom: HTMLElement): boolean { | ||||
|         if (prevNode.__tag !== this.__tag) { | ||||
|             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 { | ||||
| 
 | ||||
|         const buildConverter = (tag: MediaNodeTag) => { | ||||
|  |  | |||
|  | @ -103,6 +103,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo | |||
|         if (templateId) { | ||||
|             insertTemplateToEditor(editor, templateId, event); | ||||
|             event.preventDefault(); | ||||
|             event.stopPropagation(); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|  | @ -114,6 +115,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo | |||
|                 $insertNodesAtEvent(newNodes, event, editor); | ||||
|             }); | ||||
|             event.preventDefault(); | ||||
|             event.stopPropagation(); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|  | @ -121,6 +123,7 @@ function createDropListener(context: EditorUiContext): (event: DragEvent) => boo | |||
|             const handled = handleMediaInsert(event.dataTransfer, context); | ||||
|             if (handled) { | ||||
|                 event.preventDefault(); | ||||
|                 event.stopPropagation(); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | @ -150,9 +153,11 @@ export function registerDropPasteHandling(context: EditorUiContext): () => void | |||
| 
 | ||||
|     const unregisterDrop = context.editor.registerCommand(DROP_COMMAND, dropListener, COMMAND_PRIORITY_HIGH); | ||||
|     const unregisterPaste = context.editor.registerCommand(PASTE_COMMAND, pasteListener, COMMAND_PRIORITY_HIGH); | ||||
|     context.scrollDOM.addEventListener('drop', dropListener); | ||||
| 
 | ||||
|     return () => { | ||||
|         unregisterDrop(); | ||||
|         unregisterPaste(); | ||||
|         context.scrollDOM.removeEventListener('drop', dropListener); | ||||
|     }; | ||||
| } | ||||
|  | @ -16,4 +16,4 @@ | |||
| 
 | ||||
| ## Bugs | ||||
| 
 | ||||
| - Template drag/drop not handled when outside core editor area (ignored in margin area). | ||||
| // | ||||
|  | @ -73,11 +73,15 @@ class NodeResizer { | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const nodeDOMBounds = nodeDOM.getBoundingClientRect(); | ||||
|         this.dom.style.left = nodeDOM.offsetLeft + 'px'; | ||||
|         this.dom.style.top = nodeDOM.offsetTop + 'px'; | ||||
|         this.dom.style.width = nodeDOMBounds.width + 'px'; | ||||
|         this.dom.style.height = nodeDOMBounds.height + 'px'; | ||||
|         const scrollAreaRect = this.scrollContainer.getBoundingClientRect(); | ||||
|         const nodeRect = nodeDOM.getBoundingClientRect(); | ||||
|         const top = nodeRect.top - (scrollAreaRect.top - this.scrollContainer.scrollTop); | ||||
|         const left = nodeRect.left - scrollAreaRect.left; | ||||
| 
 | ||||
|         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 { | ||||
|  | @ -71,3 +71,11 @@ export function extractStyleMapFromElement(element: HTMLElement): StyleMap { | |||
| 
 | ||||
|     return map; | ||||
| } | ||||
| 
 | ||||
| export function setOrRemoveAttribute(element: HTMLElement, name: string, value: string|null|undefined) { | ||||
|     if (value) { | ||||
|         element.setAttribute(name, value); | ||||
|     } else { | ||||
|         element.removeAttribute(name); | ||||
|     } | ||||
| } | ||||
|  | @ -374,10 +374,21 @@ body.editor-is-fullscreen { | |||
| } | ||||
| 
 | ||||
| .editor-media-wrap { | ||||
|   display: inline-block; | ||||
|   cursor: not-allowed; | ||||
|   iframe { | ||||
|     pointer-events: none; | ||||
|   } | ||||
|   &.align-left { | ||||
|     float: left; | ||||
|   } | ||||
|   &.align-right { | ||||
|     float: right; | ||||
|   } | ||||
|   &.align-center { | ||||
|     display: block; | ||||
|     margin-inline: auto; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue