0d817537be
Introduce @workspace/lexical with composable actions, toolbars, embeds, rich-text nodes, media uploads, selection utilities, HTML serialization, and theme styling.\n\nLocalize built-in controls through MessageDescriptor catalogs for English and Simplified Chinese, while providing an English I18nProvider fallback for standalone editor use. Include focused unit coverage for actions, controls, serialization, plugins, public API, and catalogs.
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()
|
|
},
|
|
}
|
|
}
|