import type { LexicalEditor } from "lexical" export interface SelectionAnchor { contextElement: HTMLElement getBoundingClientRect: () => DOMRect } export function runWithEditorFocus( editor: LexicalEditor, callback: VoidFunction ) { const rootElement = editor.getRootElement() if (!rootElement) return const document = rootElement.ownerDocument const selection = document.defaultView?.getSelection() const selectionIsInside = selection?.focusNode != null && rootElement.contains(selection.focusNode) const focusIsInside = document.activeElement != null && rootElement.contains(document.activeElement) if (selectionIsInside && focusIsInside) { callback() return } editor.focus(callback) } export function createSelectionAnchor( rootElement: HTMLElement ): HTMLElement | SelectionAnchor { const document = rootElement.ownerDocument const domSelection = document.defaultView?.getSelection() const focusNode = domSelection?.focusNode let range: Range | null = null if (focusNode && rootElement.contains(focusNode)) { try { range = document.createRange() range.setStart(focusNode, domSelection.focusOffset) range.collapse(true) } catch { range = null } } if (!range) return rootElement const selectionRange = range return { contextElement: rootElement, getBoundingClientRect: () => { const rangeRect = typeof selectionRange.getBoundingClientRect === "function" ? selectionRange.getBoundingClientRect() : null if ( rangeRect && (rangeRect.width > 0 || rangeRect.height > 0 || rangeRect.x !== 0 || rangeRect.y !== 0) ) { return rangeRect } const clientRects = typeof selectionRange.getClientRects === "function" ? Array.from(selectionRange.getClientRects()) : [] return clientRects.at(-1) ?? rootElement.getBoundingClientRect() }, } }