Files
simple-react-app-kit/packages/lexical/src/plugins/selection-anchor.ts
T

79 lines
2.0 KiB
TypeScript
Raw Normal View History

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()
},
}
}