79 lines
2.0 KiB
TypeScript
79 lines
2.0 KiB
TypeScript
|
|
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()
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|