/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ import type { EditorState } from './LexicalEditorState'; import type { RootNode } from './nodes/LexicalRootNode'; import { DecoratorNode, ElementNode, type LineBreakNode, type UpdateTag } from '.'; import { CONTROL_OR_OTHER_KEY } from './LexicalConstants'; import { type DOMSlot, ElementDOMSlot } from './LexicalDOMSlot'; import { type AnyLexicalCommand, type CommandPayloadArgs, type CommandPayloadType, type DOMSlotForNode, type EditorDOMRenderConfig, type EditorThemeClasses, type Klass, LexicalEditor, type MutatedNodes, type MutationListeners, type NodeMutation, type RegisteredNode, type RegisteredNodes } from './LexicalEditor'; import { LexicalNode, type LexicalPrivateDOM, type NodeKey, type NodeMap, type StaticNodeConfigValue } from './LexicalNode'; import { type BaseSelection, type PointType, type RangeSelection } from './LexicalSelection'; import { type ParagraphNode } from './nodes/LexicalParagraphNode'; import { type TextFormatType, TextNode } from './nodes/LexicalTextNode'; export declare const emptyFunction: () => void; export declare function setPendingNodeToClone(pendingNode: null | LexicalNode): void; export declare function getPendingNodeToClone(): null | LexicalNode; /** Resets the internal key counter, primarily for deterministic test output. */ export declare function resetRandomKey(): void; export declare function generateRandomKey(): string; /** * @internal */ export declare function getRegisteredNodeOrThrow(editor: LexicalEditor, nodeType: string): RegisteredNode; /** * @internal */ export declare function getRegisteredNode(editor: LexicalEditor, nodeType: string): undefined | RegisteredNode; export declare const isArray: (arg: any) => arg is any[]; /** @internal */ export declare const scheduleMicroTask: (fn: () => void) => void; /** Returns true if the active element (resolved from the anchor's root) is a decorator's own input (e.g. an input, textarea, or foreign contentEditable) rather than Lexical-managed content. */ export declare function $isSelectionCapturedInDecoratorInput(anchorDOM: Node, preResolvedActiveElement?: Element | null): boolean; /** @deprecated renamed to {@link $isSelectionCapturedInDecoratorInput} by @lexical/eslint-plugin rules-of-lexical */ export declare const isSelectionCapturedInDecoratorInput: typeof $isSelectionCapturedInDecoratorInput; /** Returns true if the given DOM anchor and focus nodes are inside the editor's root element and not captured by a decorator input. */ export declare function isSelectionWithinEditor(editor: LexicalEditor, anchorDOM: null | Node, focusDOM: null | Node): boolean; /** * @returns true if the given argument is a LexicalEditor instance from this build of Lexical */ export declare function isLexicalEditor(editor: unknown): editor is LexicalEditor; /** Returns the nearest LexicalEditor instance by walking up the DOM tree from the given node, or null if none is found. */ export declare function getNearestEditorFromDOMNode(node: Node | null): LexicalEditor | null; /** @internal */ export declare function getEditorPropertyFromDOMNode(node: Node | null): unknown; /** Returns the text direction ('ltr' or 'rtl') of the given string, or null if it contains no strong directional characters. */ export declare function getTextDirection(text: string): 'ltr' | 'rtl' | null; /** * Return true if the TextNode is a TabNode or is in token mode. */ export declare function $isTokenOrTab(node: TextNode): boolean; /** * Return true if the TextNode is a TabNode, or is in token or segmented mode. */ export declare function $isTokenOrSegmented(node: TextNode): boolean; /** * @param node - The element being tested * @returns Returns true if node is an DOM Text node, false otherwise. */ export declare function isDOMTextNode(node: unknown): node is Text; /** * @param node - The element being tested * @returns Returns true if node is an DOM Document node, false otherwise. */ export declare function isDOMDocumentNode(node: unknown): node is Document; /** Returns the first DOM Text node found by descending the firstChild chain from the given node, or null. */ export declare function getDOMTextNode(element: Node | null): Text | null; /** Toggles the given text format type on a format bitmask, clearing mutually exclusive formats (subscript/superscript, lowercase/uppercase/capitalize). */ export declare function toggleTextFormatType(format: number, type: TextFormatType, alignWithFormat: null | number): number; /** Returns true if the given node is a leaf (TextNode, LineBreakNode, or DecoratorNode). */ export declare function $isLeafNode(node: LexicalNode | null | undefined): node is TextNode | LineBreakNode | DecoratorNode; export declare function $setNodeKey(node: LexicalNode, existingKey: NodeKey | null | undefined): void; /** * @internal * * Latch the "this document uses slots" flag. The editor keeps it for its * lifetime, and the EditorState currently being built carries it so that a * state handed to another editor via `setEditorState` brings the flag with it. * * The state marked here is the *active* one. Inside `editor.update()` that is * `editor._pendingEditorState`, but `parseEditorState` builds a detached * EditorState and leaves `_pendingEditorState` untouched, so keying off * pending would miss the parsed state entirely (and could stamp the flag onto * an unrelated pending state). */ export declare function $markSlotsUsed(): void; /** * Removes a node from its parent, updating all necessary pointers and links. * @internal * * This function does not adjust the editor's current selection. Callers * that need element-anchored offsets in the old parent to track the child * count change must call `$updateElementSelectionOnCreateDeleteNode` (with * `times = -1`) after invoking this — see `$removeNode`, `replace`, * `insertBefore`, and `insertAfter` for the pattern. * * This function is for internal use of the library. * Please do not use it as it may change in the future. */ export declare function $removeFromParent(node: LexicalNode): void; /** @deprecated renamed to {@link $removeFromParent} by @lexical/eslint-plugin rules-of-lexical */ export declare const removeFromParent: typeof $removeFromParent; export declare function internalMarkNodeAsDirty(node: LexicalNode): void; export declare function internalMarkSiblingsAsDirty(node: LexicalNode): void; /** Sets the active composition key, marking the previous and new composition nodes as dirty for re-rendering. */ export declare function $setCompositionKey(compositionKey: null | NodeKey): void; export declare function $getCompositionKey(): null | NodeKey; /** * Returns the node with the given key from the active EditorState * (or the given EditorState), or null if it does not exist. */ export declare function $getNodeByKey(key: NodeKey, _editorState?: EditorState): LexicalNode | null; /** * @deprecated The type parameter is an unchecked and unsafe cast, * equivalent to `$getNodeByKey(key) as T | null`, and will be removed * in a future release. Call this function without a type argument and * narrow the result with a type guard instead. */ export declare function $getNodeByKey(key: NodeKey, _editorState?: EditorState): T | null; /** Returns the LexicalNode directly associated with the given DOM node, or null if the DOM node has no Lexical key. */ export declare function $getNodeFromDOMNode(dom: Node, editorState?: EditorState): LexicalNode | null; export declare function setNodeKeyOnDOMNode(dom: Node, editor: LexicalEditor, key: NodeKey): void; export declare function clearNodeKeyOnDOMNode(dom: Node, editor: LexicalEditor): void; export declare function getNodeKeyFromDOMNode(dom: Node, editor: LexicalEditor): NodeKey | undefined; /** Returns the nearest LexicalNode by walking up the DOM tree from the given node, or null if no Lexical node is found. */ export declare function $getNearestNodeFromDOMNode(startingDOM: Node, editorState?: EditorState): LexicalNode | null; export declare function cloneDecorators(editor: LexicalEditor): Record; export declare function getEditorStateTextContent(editorState: EditorState): string; export declare function markNodesWithTypesAsDirty(editor: LexicalEditor, types: string[]): void; /** Returns the RootNode of the active EditorState. */ export declare function $getRoot(): RootNode; /** * Restores the empty paragraph a root or shadow root needs to stay editable, * when a removal has left `container` with no children at all. Removing the * last node it held (a lone table or block decorator sitting beside a block * cursor, or a select-all over a document that is a single shadow root) * otherwise leaves nowhere to put a caret, and the next keystroke acts on the * container itself rather than on a block inside it. * * A ParagraphNode is only a valid child of a container that holds blocks. The * RootNode always does, but a shadow root may be structural instead — a * TableNode holds rows, a TableRowNode holds cells — so for anything but the * root, `removedChild` (a child the caller is removing, or has just removed, * from `container`) decides: a paragraph belongs where a block did. * * Call this only where a removal could have emptied `container`. It is a no-op * on a container that is already populated, but on one that was *already* * empty beforehand it would seed a paragraph nobody asked for. * * @returns the paragraph that was appended, or null when nothing was restored. * @internal */ export declare function $restoreEmptyContainerParagraph(container: null | LexicalNode, removedChild: null | LexicalNode): null | ParagraphNode; export declare function internalGetRoot(editorState: EditorState): RootNode; /** Sets the current selection in the active EditorState, marking it dirty and clamping to slot boundaries when applicable. */ export declare function $setSelection(selection: null | BaseSelection): void; export declare function $flushMutations(): void; export declare function $getNodeFromDOM(dom: Node): null | LexicalNode; /** * Return true if `str` contains any valid surrogate pair. * * See also $updateCaretSelectionForUnicodeCharacter for * a discussion on when and why this is useful. */ export declare function doesContainSurrogatePair(str: string): boolean; export declare function getEditorsToPropagate(editor: LexicalEditor): LexicalEditor[]; export declare function createUID(): string; export declare function getAnchorTextFromDOM(anchorNode: Node): null | string; export declare function $updateSelectedTextFromDOM(isCompositionEnd: boolean, editor: LexicalEditor, data?: string): void; export declare function $updateTextNodeFromDOMContent(textNode: TextNode, textContent: string, anchorOffset: null | number, focusOffset: null | number, compositionEnd: boolean): void; export declare function $shouldInsertTextAfterOrBeforeTextNode(selection: RangeSelection, node: TextNode): boolean; /** * A KeyboardEvent or structurally similar object with a string `key` as well * as `altKey`, `ctrlKey`, `metaKey`, and `shiftKey` boolean properties. */ export type KeyboardEventModifiers = Pick; /** * A record of keyboard modifiers that must be enabled. * If the value is `'any'` then the modifier key's state is ignored. * If the value is `true` then the modifier key must be pressed. * If the value is `false` or the property is omitted then the modifier key must * not be pressed. */ export type KeyboardEventModifierMask = { [K in Exclude]?: boolean | undefined | 'any'; }; export { CONTROL_OR_OTHER_KEY }; /** @internal */ export interface KeyboardEventControlOrOther { [CONTROL_OR_OTHER_KEY]?: 'metaKey' | 'altKey'; } /** @internal */ export declare function keyboardEventMaskForPlatform(mask: KeyboardEventModifierMask & KeyboardEventControlOrOther, isApple: boolean): KeyboardEventModifierMask; /** * Match a KeyboardEvent with its expected modifier state * * @param event A KeyboardEvent, or structurally similar object * @param mask An object specifying the expected state of the modifiers * @returns true if the event matches */ export declare function isModifierMatch(event: KeyboardEventModifiers, mask: KeyboardEventModifierMask): boolean; /** * Match a KeyboardEvent with its expected state * * @param event A KeyboardEvent, or structurally similar object * @param expectedKey The string to compare with event.key (case insensitive) * @param mask An object specifying the expected state of the modifiers * @returns true if the event matches */ export declare function isExactShortcutMatch(event: KeyboardEventModifiers, expectedKey: string, mask: KeyboardEventModifierMask): boolean; export declare function isModifier(event: KeyboardEventModifiers): boolean; export declare function isBackspace(event: KeyboardEventModifiers): boolean; export declare function isEscape(event: KeyboardEventModifiers): boolean; export declare function isDelete(event: KeyboardEventModifiers): boolean; export declare function isSelectAll(event: KeyboardEventModifiers): boolean; /** Selects all content within the root. If a selection is provided, scopes to the nearest root or shadow root; otherwise creates a new RangeSelection spanning the entire root. */ export declare function $selectAll(selection?: RangeSelection | null): RangeSelection; /** * Removes `class` or `style` from the element when the attribute is present * but has an empty value. * * `classList.remove(...)` and `style.setProperty(prop, '')` do not remove the * attribute once every token/declaration is gone, so clearing the last theme * class or the last inline declaration leaves `class=""` / `style=""` behind * in the editor DOM. */ export declare function removeEmptyDOMAttribute(dom: HTMLElement, attributeName: 'class' | 'style'): void; export declare function getCachedClassNameArray(classNamesTheme: EditorThemeClasses, classNameThemeType: string): string[]; export declare function setMutatedNode(mutatedNodes: MutatedNodes, registeredNodes: RegisteredNodes, mutationListeners: MutationListeners, node: LexicalNode, mutation: NodeMutation): void; /** * Returns all nodes of the given type in the active editor state. * * Consider {@link LexicalEditor.registerMutationListener} with * `skipInitialization: false` instead if you need to track these nodes over * time rather than read them once. */ export declare function $nodesOfType(klass: Klass): T[]; /** Returns the node adjacent to the given selection point in the specified direction, or null if at a boundary. */ export declare function $getAdjacentNode(focus: PointType, isBackward: boolean): null | LexicalNode; export declare function isFirefoxClipboardEvents(editor: LexicalEditor): boolean; export declare function dispatchCommand(editor: LexicalEditor, command: TCommand, ...args: CommandPayloadArgs>): boolean; export declare function getElementByKeyOrThrow(editor: LexicalEditor, key: NodeKey): HTMLElement; /** Returns the parent element of a DOM node, crossing shadow root boundaries and following slot assignments. */ export declare function getParentElement(node: Node): HTMLElement | null; /** Returns the owner Document of the given EventTarget, or the target itself if it is a Document. */ export declare function getDOMOwnerDocument(target: EventTarget | null): Document | null; export declare function scrollIntoViewIfNeeded(editor: LexicalEditor, selectionRect: DOMRect, rootElement: HTMLElement): void; /** Returns true if the given tag has been added to the current update via $addUpdateTag. */ export declare function $hasUpdateTag(tag: UpdateTag): boolean; /** Adds a tag to the current update, which can be read by update listeners and $hasUpdateTag. */ export declare function $addUpdateTag(tag: UpdateTag): void; /** * Add a function to run after the current update. This will run after any * `onUpdate` function already supplied to `editor.update()`, as well as any * functions added with previous calls to `$onUpdate`. * * @param updateFn The function to run after the current update. */ export declare function $onUpdate(updateFn: () => void): void; export declare function $maybeMoveChildrenSelectionToParent(parentNode: LexicalNode): BaseSelection | null; /** Returns true if targetNode is an ancestor of child by walking up the parent chain. */ export declare function $hasAncestor(child: LexicalNode, targetNode: LexicalNode): boolean; export declare function getDefaultView(domElem: EventTarget | null): Window | null; export declare function getWindow(editor: LexicalEditor): Window; declare const InlineNodeBrand: unique symbol; /** Returns true if the given node is an inline ElementNode or an inline DecoratorNode. */ export declare function $isInlineElementOrDecoratorNode(node: LexicalNode): node is (ElementNode | DecoratorNode) & { isInline(): true; [InlineNodeBrand]: never; }; /** Returns the given node itself (if it is a slot boundary) or its nearest ancestor that is a RootNode, ShadowRootNode, or slot boundary. */ export declare function $getNearestRootOrShadowRoot(node: LexicalNode): RootNode | ElementNode; declare const ShadowRootNodeBrand: unique symbol; export interface ShadowRootNode extends ElementNode { [ShadowRootNodeBrand]: never; isShadowRoot(): true; } /** Returns true if the given node is an ElementNode whose isShadowRoot() returns true. */ export declare function $isShadowRootNode(node: null | LexicalNode): node is ShadowRootNode; /** Returns true if the given node is a RootNode or a ShadowRootNode. */ export declare function $isRootOrShadowRoot(node: null | LexicalNode): node is RootNode | ShadowRootNode; /** * Returns a shallow clone of node with a new key. All properties of the node * will be copied to the new node (by `clone` and then `afterCloneFrom`), * except those related to parent/sibling/child * relationships in the `EditorState`. This means that the copy must be * separately added to the document, and it will not have any children. * * @param node - The node to be copied. * @param skipReset - If true (default false) skip the call to resetOnCopyNodeFrom * @returns The copy of the node. */ export declare function $copyNode(node: T, skipReset?: boolean): T; /** Applies any registered node replacement for the given node's type, returning the replacement node or the original if none is registered. */ export declare function $applyNodeReplacement(node: N): N; export declare function errorOnInsertTextNodeOnRoot(node: LexicalNode, insertNode: LexicalNode): void; /** * Returns the node with the given key from the active EditorState, * or throws if it does not exist. */ export declare function $getNodeByKeyOrThrow(key: NodeKey): LexicalNode; /** * @deprecated The type parameter is an unchecked and unsafe cast, * equivalent to `$getNodeByKeyOrThrow(key) as N`, and will be removed * in a future release. Call this function without a type argument and * narrow the result with a type guard instead. */ export declare function $getNodeByKeyOrThrow(key: NodeKey): N; /** * Returns true if the given node needs a block cursor given an adjacent selection, * the node must be non-inline and one of: * - DecoratorNode * - ShadowRootNode with a parent that is not also a ShadowRootNode * - An ElementNode that can't be empty */ export declare function $needsBlockCursorBeside(node: null | LexicalNode): boolean; export declare function removeDOMBlockCursorElement(blockCursorElement: HTMLElement, editor: LexicalEditor, rootElement: HTMLElement): void; export declare function $updateDOMBlockCursorElement(editor: LexicalEditor, rootElement: HTMLElement, nextSelection: null | BaseSelection): void; /** * Returns the selection for the given window, or the global window if null. * Will return null if {@link CAN_USE_DOM} is false. * * @param targetWindow The window to get the selection from * @returns a Selection or null */ export declare function getDOMSelection(targetWindow: null | Window): null | Selection; /** * Returns the selection for the defaultView of the ownerDocument of given EventTarget. * * @param eventTarget The node to get the selection from * @returns a Selection or null */ export declare function getDOMSelectionFromTarget(eventTarget: null | EventTarget): null | Selection; /** * @param node A value that may be a DOM ShadowRoot. * @returns True if node is a DOM ShadowRoot (an open or closed shadow tree * root), false otherwise. A ShadowRoot is a DocumentFragment with a host. * * @experimental Shape may change as shadow DOM support stabilizes. */ export declare function isDOMShadowRoot(node: unknown): node is ShadowRoot; export declare function getDOMShadowRoots(node: Node): ShadowRoot[]; /** * Walks `root` and every open shadow root nested inside it, yielding each * element that matches `selector`. `querySelectorAll` does not pierce * shadow boundaries on its own; this descent does. * * @internal */ export declare function findAllLexicalElementsDeep(initialRoot: Document | ShadowRoot): Generator; /** * Resolves the document that hosts an editor's root element, falling * back to the global `document` when the editor isn't mounted. Use this * over `editor.getRootElement()?.ownerDocument ?? document` so iframe / * shadow-mounted editors land in the right realm. * * @internal */ export declare function getRootOwnerDocument(rootElement: HTMLElement | null): Document; /** * Returns the {@link Document} that owns the active editor's root element. * Falls back to `globalThis.document` when there is no active editor (e.g. * a node method such as `createDOM` / `exportDOM` is invoked headlessly, * outside of `editor.update()` / `editor.read()`), or when the active * editor has no root element (e.g. headless mode with * {@link @lexical/headless!withDOM | withDOM}). * * Use this inside `createDOM`, `updateDOM`, and `exportDOM` instead of the * bare `document` global so the node works correctly when the editor lives * inside a Shadow DOM or a cross-origin `