From 7ec8ad76250400e75f863c157883d30f9d601433 Mon Sep 17 00:00:00 2001 From: Bob Ippolito Date: Thu, 18 Jun 2026 08:01:04 -0700 Subject: [PATCH] [lexical-react][lexical-link][lexical-history][lexical-extension] Docs: API doc coverage of react exports (#8714) Co-authored-by: Claude --- .../src/HorizontalRuleExtension.ts | 12 +++++ .../src/TabIndentationExtension.ts | 7 +++ packages/lexical-history/src/index.ts | 7 +++ .../src/LexicalAutoLinkExtension.ts | 17 +++++++ .../src/LexicalAutoEmbedPlugin.tsx | 25 ++++++++++ .../src/LexicalAutoFocusPlugin.ts | 10 ++++ .../src/LexicalAutoLinkPlugin.ts | 13 +++++ .../src/LexicalBlockWithAlignableContents.tsx | 9 ++++ .../src/LexicalCharacterLimitPlugin.tsx | 10 ++++ .../src/LexicalCheckListPlugin.tsx | 11 ++++ .../src/LexicalClearEditorPlugin.ts | 10 ++++ .../src/LexicalClickableLinkPlugin.tsx | 10 ++++ .../src/LexicalCollaborationContext.tsx | 25 ++++++++++ .../src/LexicalCollaborationPlugin.tsx | 18 +++++++ .../lexical-react/src/LexicalComposer.tsx | 22 ++++++++ .../src/LexicalComposerContext.ts | 35 +++++++++++++ .../src/LexicalContentEditable.tsx | 13 +++++ .../src/LexicalDecoratorBlockNode.ts | 14 ++++++ .../src/LexicalDraggableBlockPlugin.tsx | 10 ++++ .../src/LexicalErrorBoundary.tsx | 13 +++++ .../src/LexicalExtensionComposer.tsx | 3 ++ .../src/LexicalExtensionEditorComposer.tsx | 3 ++ .../lexical-react/src/LexicalHashtagPlugin.ts | 10 ++++ .../lexical-react/src/LexicalHistoryPlugin.ts | 11 ++++ .../src/LexicalHorizontalRulePlugin.ts | 6 ++- .../lexical-react/src/LexicalLinkPlugin.ts | 12 +++++ .../lexical-react/src/LexicalListPlugin.ts | 13 +++++ .../src/LexicalMarkdownShortcutPlugin.tsx | 13 +++++ .../src/LexicalNestedComposer.tsx | 17 +++++++ .../src/LexicalNodeContextMenuPlugin.tsx | 21 ++++++++ .../src/LexicalNodeEventPlugin.ts | 8 +++ .../src/LexicalNodeMenuPlugin.tsx | 12 +++++ .../src/LexicalOnChangePlugin.ts | 8 +++ .../src/LexicalPlainTextPlugin.tsx | 12 +++++ .../src/LexicalRichTextPlugin.tsx | 12 +++++ .../src/LexicalSelectionAlwaysOnDisplay.tsx | 11 ++++ .../src/LexicalTabIndentationPlugin.tsx | 3 ++ .../src/LexicalTableOfContentsPlugin.tsx | 13 +++++ .../lexical-react/src/LexicalTablePlugin.ts | 6 +++ .../lexical-react/src/LexicalTreeView.tsx | 4 ++ .../src/LexicalTypeaheadMenuPlugin.tsx | 37 ++++++++++++++ .../src/ReactPluginHostExtension.tsx | 50 +++++++++++++++++++ .../lexical-react/src/TreeViewExtension.tsx | 12 +++++ .../shared/LexicalContentEditableElement.tsx | 16 ++++++ .../lexical-react/src/shared/LexicalMenu.tsx | 36 +++++++++++++ packages/lexical-react/src/shared/types.ts | 21 ++++++++ .../src/useExtensionComponent.tsx | 22 ++++++++ .../src/useLexicalIsTextContentEmpty.ts | 7 +++ .../src/useLexicalSubscription.tsx | 5 ++ .../lexical-react/src/useLexicalTextEntity.ts | 7 +++ 50 files changed, 701 insertions(+), 1 deletion(-) diff --git a/packages/lexical-extension/src/HorizontalRuleExtension.ts b/packages/lexical-extension/src/HorizontalRuleExtension.ts index e02cb64d26d..047e178f232 100644 --- a/packages/lexical-extension/src/HorizontalRuleExtension.ts +++ b/packages/lexical-extension/src/HorizontalRuleExtension.ts @@ -43,8 +43,17 @@ import {EditorStateExtension} from './EditorStateExtension'; import {NodeSelectionExtension} from './NodeSelectionExtension'; import {batch, effect, ReadonlySignal, Signal, signal} from './signals'; +/** + * The serialized form of a {@link HorizontalRuleNode}. It has no extra fields + * beyond the base serialized node. + */ export type SerializedHorizontalRuleNode = SerializedLexicalNode; +/** + * Command that inserts a {@link HorizontalRuleNode} at the current selection. + * Dispatch it with + * `editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined)`. + */ export const INSERT_HORIZONTAL_RULE_COMMAND: LexicalCommand = /* @__PURE__ */ createCommand('INSERT_HORIZONTAL_RULE_COMMAND'); @@ -103,6 +112,9 @@ export function $createHorizontalRuleNode(): HorizontalRuleNode { return $create(HorizontalRuleNode); } +/** + * @returns `true` if `node` is a {@link HorizontalRuleNode}, narrowing its type. + */ export function $isHorizontalRuleNode( node: LexicalNode | null | undefined, ): node is HorizontalRuleNode { diff --git a/packages/lexical-extension/src/TabIndentationExtension.ts b/packages/lexical-extension/src/TabIndentationExtension.ts index fba31d8ba5b..90a809e3e06 100644 --- a/packages/lexical-extension/src/TabIndentationExtension.ts +++ b/packages/lexical-extension/src/TabIndentationExtension.ts @@ -73,6 +73,13 @@ function $defaultCanIndent(node: ElementNode) { return node.canIndent(); } +/** + * Registers a `KEY_TAB_COMMAND` handler that makes Tab and Shift+Tab indent and + * outdent block elements (and otherwise insert a tab). Pass `maxIndent` to cap + * the indent depth and `$canIndent` to control which elements may be indented. + * + * @returns A cleanup function that unregisters the handler. + */ export function registerTabIndentation( editor: LexicalEditor, maxIndent?: number | ReadonlySignal, diff --git a/packages/lexical-history/src/index.ts b/packages/lexical-history/src/index.ts index 42a30568f0e..c7931d6070b 100644 --- a/packages/lexical-history/src/index.ts +++ b/packages/lexical-history/src/index.ts @@ -57,6 +57,13 @@ export type HistoryStateEntry = { editor: LexicalEditor; editorState: EditorState; }; +/** + * The undo/redo history maintained by the history plugin: the `current` entry + * plus the `undoStack` and `redoStack` of previous and future + * {@link HistoryStateEntry}s. Create an empty one with + * {@link createEmptyHistoryState} and pass it to the history plugin to share + * history across editors. + */ export type HistoryState = { current: null | HistoryStateEntry; redoStack: HistoryStateEntry[]; diff --git a/packages/lexical-link/src/LexicalAutoLinkExtension.ts b/packages/lexical-link/src/LexicalAutoLinkExtension.ts index 4b1447d46c4..9820f3797f9 100644 --- a/packages/lexical-link/src/LexicalAutoLinkExtension.ts +++ b/packages/lexical-link/src/LexicalAutoLinkExtension.ts @@ -33,6 +33,11 @@ import { TOGGLE_LINK_COMMAND, } from './LexicalLinkNode'; +/** + * A callback invoked when the auto-link plugin creates, updates, or removes an + * automatic link. It receives the new `url` and the `prevUrl`; either may be + * `null` when a link is added or removed. + */ export type ChangeHandler = ( url: string | null, prevUrl: string | null, @@ -46,8 +51,20 @@ export interface LinkMatcherResult { url: string; } +/** + * A function that inspects a piece of `text` and returns a + * {@link LinkMatcherResult} for the first URL it recognizes, or `null` if none + * is found. Used by the auto-link plugin to detect links as the user types. + */ export type LinkMatcher = (text: string) => LinkMatcherResult | null; +/** + * Builds a {@link LinkMatcher} from a regular expression. The matched text is + * used as the link URL, optionally rewritten by `urlTransformer` (for example + * to prepend a protocol). Pass the result to the auto-link plugin's `matchers`. + * + * @returns A matcher that reports the first match of `regExp` in the text. + */ export function createLinkMatcherWithRegExp( regExp: RegExp, urlTransformer: (text: string) => string = text => text, diff --git a/packages/lexical-react/src/LexicalAutoEmbedPlugin.tsx b/packages/lexical-react/src/LexicalAutoEmbedPlugin.tsx index 8e126731f97..6aa962b7e11 100644 --- a/packages/lexical-react/src/LexicalAutoEmbedPlugin.tsx +++ b/packages/lexical-react/src/LexicalAutoEmbedPlugin.tsx @@ -34,12 +34,23 @@ import { } from 'lexical'; import {useCallback, useEffect, useMemo, useState} from 'react'; +/** + * The result of matching a URL for an embed: the matched `url`, an `id` + * identifying the embedded resource, and optional provider-specific `data`. + */ export type EmbedMatchResult = { url: string; id: string; data?: TEmbedMatchResult; }; +/** + * Describes a kind of embed (for example YouTube, a tweet, or Google Maps) that + * {@link LexicalAutoEmbedPlugin} can detect and insert. Each config has a `type` + * identifier, a `parseUrl` function that decides whether a URL matches and + * extracts its data, and an `insertNode` function that inserts the corresponding + * Lexical node. + */ export interface EmbedConfig< TEmbedMatchResultData = unknown, TEmbedMatchResult = EmbedMatchResult, @@ -54,12 +65,26 @@ export interface EmbedConfig< insertNode: (editor: LexicalEditor, result: TEmbedMatchResult) => void; } +/** + * A general-purpose regular expression for detecting URLs, provided as a + * convenience for implementing an {@link EmbedConfig}'s `parseUrl`. + */ export const URL_MATCHER = /((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/; +/** + * Command dispatched to start inserting an embed. Its payload is the `type` of + * the {@link EmbedConfig} to use; {@link LexicalAutoEmbedPlugin} listens for it + * and runs that config's URL detection flow. + */ export const INSERT_EMBED_COMMAND: LexicalCommand = /* @__PURE__ */ createCommand('INSERT_EMBED_COMMAND'); +/** + * A {@link MenuOption} for the auto-embed menu, pairing a display `title` with + * an `onSelect` callback invoked when the user chooses to embed the detected + * URL. + */ export class AutoEmbedOption extends MenuOption { title: string; onSelect: (targetNode: LexicalNode | null) => void; diff --git a/packages/lexical-react/src/LexicalAutoFocusPlugin.ts b/packages/lexical-react/src/LexicalAutoFocusPlugin.ts index d9b24c6fc39..80bb73f85f8 100644 --- a/packages/lexical-react/src/LexicalAutoFocusPlugin.ts +++ b/packages/lexical-react/src/LexicalAutoFocusPlugin.ts @@ -13,6 +13,16 @@ type Props = { defaultSelection?: 'rootStart' | 'rootEnd'; }; +/** + * Focuses the editor when the component is mounted. Pass `defaultSelection` + * to control whether the selection is placed at the start (`'rootStart'`) or + * end (`'rootEnd'`) of the root when there is no existing selection to restore. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link AutoFocusExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function AutoFocusPlugin({defaultSelection}: Props): null { const [editor] = useLexicalComposerContext(); diff --git a/packages/lexical-react/src/LexicalAutoLinkPlugin.ts b/packages/lexical-react/src/LexicalAutoLinkPlugin.ts index af1d2e83f67..1371fc71057 100644 --- a/packages/lexical-react/src/LexicalAutoLinkPlugin.ts +++ b/packages/lexical-react/src/LexicalAutoLinkPlugin.ts @@ -44,6 +44,19 @@ function useAutoLink( }, [editor, matchers, onChange, excludeParents]); } +/** + * Automatically converts text that matches one of the provided `matchers` into + * {@link AutoLinkNode}s as the user types, and reverts them back to plain text + * when they no longer match. Provide `onChange` to react to links being + * created, updated, or removed, and `excludeParents` to skip matching inside + * particular ancestor nodes. The editor must have the {@link AutoLinkNode} + * registered. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link AutoLinkExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function AutoLinkPlugin({ matchers, onChange, diff --git a/packages/lexical-react/src/LexicalBlockWithAlignableContents.tsx b/packages/lexical-react/src/LexicalBlockWithAlignableContents.tsx index 8fae7bcb5da..8b982165ee3 100644 --- a/packages/lexical-react/src/LexicalBlockWithAlignableContents.tsx +++ b/packages/lexical-react/src/LexicalBlockWithAlignableContents.tsx @@ -38,6 +38,15 @@ type Props = Readonly<{ }>; }>; +/** + * A wrapper component for the contents of a {@link DecoratorBlockNode} that + * keeps the block in sync with node selection and element alignment. It renders + * its `children` inside a container that reflects the node's `format` + * alignment, responds to `FORMAT_ELEMENT_COMMAND` to update that alignment, and + * toggles the node's selection when the container is clicked. + * + * @returns The element to render for the decorator block. + */ export function BlockWithAlignableContents({ children, format, diff --git a/packages/lexical-react/src/LexicalCharacterLimitPlugin.tsx b/packages/lexical-react/src/LexicalCharacterLimitPlugin.tsx index 125228adec3..26e4170c2c2 100644 --- a/packages/lexical-react/src/LexicalCharacterLimitPlugin.tsx +++ b/packages/lexical-react/src/LexicalCharacterLimitPlugin.tsx @@ -52,6 +52,16 @@ function DefaultRenderer({remainingCharacters}: {remainingCharacters: number}) { ); } +/** + * Tracks the length of the editor's text content against `maxLength` and + * renders the number of remaining characters, marking any overflowing text so + * it can be styled. Length is measured in either `'UTF-8'` or `'UTF-16'` + * (default) code units via the `charset` prop, and the display can be + * customized with the `renderer` prop. + * + * @returns The element produced by `renderer` (by default a `` showing + * the number of remaining characters). + */ export function CharacterLimitPlugin({ charset = 'UTF-16', maxLength = CHARACTER_LIMIT, diff --git a/packages/lexical-react/src/LexicalCheckListPlugin.tsx b/packages/lexical-react/src/LexicalCheckListPlugin.tsx index 647a1924834..bf73e439455 100644 --- a/packages/lexical-react/src/LexicalCheckListPlugin.tsx +++ b/packages/lexical-react/src/LexicalCheckListPlugin.tsx @@ -10,6 +10,17 @@ import {registerCheckList} from '@lexical/list'; import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; import {useEffect} from 'react'; +/** + * Enables check list support, wiring up the keyboard and pointer interactions + * that toggle the checked state of check list items. Pass + * `disableTakeFocusOnClick` to stop the editor from taking focus when a + * checkbox is clicked. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link CheckListExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function CheckListPlugin({ disableTakeFocusOnClick = false, }: { diff --git a/packages/lexical-react/src/LexicalClearEditorPlugin.ts b/packages/lexical-react/src/LexicalClearEditorPlugin.ts index 58dcf9f909b..0e871b8aa3c 100644 --- a/packages/lexical-react/src/LexicalClearEditorPlugin.ts +++ b/packages/lexical-react/src/LexicalClearEditorPlugin.ts @@ -17,6 +17,16 @@ type Props = Readonly<{ onClear?: () => void; }>; +/** + * Registers a handler for `CLEAR_EDITOR_COMMAND` that empties the editor and + * resets the selection. Provide `onClear` to run your own logic in place of the + * default clearing behavior. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link ClearEditorExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function ClearEditorPlugin({onClear}: Props): JSX.Element | null { const [editor] = useLexicalComposerContext(); useLayoutEffect( diff --git a/packages/lexical-react/src/LexicalClickableLinkPlugin.tsx b/packages/lexical-react/src/LexicalClickableLinkPlugin.tsx index cf8d23d632c..5d713db86ab 100644 --- a/packages/lexical-react/src/LexicalClickableLinkPlugin.tsx +++ b/packages/lexical-react/src/LexicalClickableLinkPlugin.tsx @@ -11,6 +11,16 @@ import {registerClickableLink} from '@lexical/link'; import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; import {useEffect} from 'react'; +/** + * Makes {@link LinkNode}s clickable, navigating to the link's URL when it is + * clicked (opening it in a new tab when `newTab` is `true`, the default). Set + * `disabled` to temporarily turn the behavior off, for example while editing. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link ClickableLinkExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function ClickableLinkPlugin({ newTab = true, disabled = false, diff --git a/packages/lexical-react/src/LexicalCollaborationContext.tsx b/packages/lexical-react/src/LexicalCollaborationContext.tsx index 56fb9ef1ccd..ae7dfc17bda 100644 --- a/packages/lexical-react/src/LexicalCollaborationContext.tsx +++ b/packages/lexical-react/src/LexicalCollaborationContext.tsx @@ -11,6 +11,11 @@ import type {Doc} from 'yjs'; import devInvariant from '@lexical/internal/devInvariant'; import {createContext, useContext, useMemo} from 'react'; +/** + * The value stored in the {@link CollaborationContext}: the local user's + * display `name` and cursor `color`, whether collaboration is currently active, + * and the map of Yjs documents shared by the editors under this provider. + */ export type CollaborationContextType = { color: string; isCollabActive: boolean; @@ -39,6 +44,11 @@ const entries = [ const randomEntry = entries[Math.floor(Math.random() * entries.length)]; +/** + * The React context that holds the shared {@link CollaborationContextType} for + * collaborative editors. Provide it with {@link LexicalCollaboration} and read + * it with {@link useCollaborationContext}. + */ export const CollaborationContext = createContext(null); @@ -55,6 +65,14 @@ function newContext() { // a shared context across editors is likely to lead to bugs. const UNSAFE_GLOBAL_CONTEXT = newContext(); +/** + * A provider component that creates a fresh {@link CollaborationContextType} + * and makes it available to descendant editors via {@link CollaborationContext}. + * Wrap a group of editors that should share collaboration state in this + * component. + * + * @returns A context provider wrapping `children`. + */ export function LexicalCollaboration({children}: {children: React.ReactNode}) { const collabContext = useMemo(() => newContext(), []); @@ -65,6 +83,13 @@ export function LexicalCollaboration({children}: {children: React.ReactNode}) { ); } +/** + * Reads the current {@link CollaborationContextType} from the nearest + * {@link LexicalCollaboration} provider. Optionally pass `username` and `color` + * to set the local user's display name and cursor color. + * + * @returns The active collaboration context. + */ export function useCollaborationContext( username?: string, color?: string, diff --git a/packages/lexical-react/src/LexicalCollaborationPlugin.tsx b/packages/lexical-react/src/LexicalCollaborationPlugin.tsx index e7a2a4ad001..39856316269 100644 --- a/packages/lexical-react/src/LexicalCollaborationPlugin.tsx +++ b/packages/lexical-react/src/LexicalCollaborationPlugin.tsx @@ -55,6 +55,15 @@ type CollaborationPluginProps = { selectionHighlight?: boolean; }; +/** + * Connects the editor to a Yjs document for real-time collaboration, syncing + * editor state and rendering remote users' cursors and selections. Provide a + * `providerFactory` that creates the Yjs {@link Provider} for the given + * document `id`. Must be used within a {@link LexicalCollaboration} provider. + * + * @returns The element that renders collaborators' cursors (or an empty + * fragment until the provider and binding are initialized). + */ export function CollaborationPlugin({ id, providerFactory, @@ -226,6 +235,15 @@ type CollaborationPluginV2Props = { selectionHighlight?: boolean; }; +/** + * A variant of {@link CollaborationPlugin} that takes an already-created Yjs + * `doc` and {@link Provider} directly instead of a provider factory, giving the + * application full control over their lifecycle. Must be used within a + * {@link LexicalCollaboration} provider. + * + * @experimental The API may change in a future release. + * @returns The element that renders collaborators' cursors. + */ export function CollaborationPluginV2__EXPERIMENTAL({ id, doc, diff --git a/packages/lexical-react/src/LexicalComposer.tsx b/packages/lexical-react/src/LexicalComposer.tsx index 8f9b99c8262..b762e08279a 100644 --- a/packages/lexical-react/src/LexicalComposer.tsx +++ b/packages/lexical-react/src/LexicalComposer.tsx @@ -64,6 +64,13 @@ export type InitialEditorStateType = | EditorState | ((editor: LexicalEditor) => void); +/** + * The configuration passed to {@link LexicalComposer} via its `initialConfig` + * prop. It is read once when the editor is created and describes the editor's + * `namespace`, registered `nodes`, `theme`, error handling, initial editable + * state, optional initial {@link InitialEditorStateType}, and HTML + * import/export configuration. + */ export type InitialConfigType = Readonly<{ namespace: string; nodes?: readonly (Klass | LexicalNodeReplacement)[]; @@ -97,6 +104,21 @@ type Props = React.PropsWithChildren<{ initialConfig: InitialConfigType; }>; +/** + * The root component for a Lexical editor in React. It creates a + * {@link LexicalEditor} from `initialConfig`, provides it (and its + * {@link LexicalComposerContextType}) to descendants through React context, and + * renders its `children`. Place plugins and UI such as {@link RichTextPlugin} + * and {@link ContentEditable} inside it, and read the editor from descendants + * with {@link useLexicalComposerContext}. + * + * `LexicalComposer` uses the legacy plugin pattern and does not support the + * extension API. To build an editor from extensions, use + * {@link LexicalExtensionComposer} instead; see the + * [React extensions guide](https://lexical.dev/docs/extensions/react). + * + * @returns A context provider wrapping `children`. + */ export function LexicalComposer({initialConfig, children}: Props): JSX.Element { const composerContext: [LexicalEditor, LexicalComposerContextType] = useMemo( () => { diff --git a/packages/lexical-react/src/LexicalComposerContext.ts b/packages/lexical-react/src/LexicalComposerContext.ts index 61da6d675e7..8fb347bdd68 100644 --- a/packages/lexical-react/src/LexicalComposerContext.ts +++ b/packages/lexical-react/src/LexicalComposerContext.ts @@ -11,21 +11,48 @@ import type {EditorThemeClasses, LexicalEditor} from 'lexical'; import invariant from '@lexical/internal/invariant'; import {createContext as createReactContext, useContext} from 'react'; +/** + * The context value provided alongside a {@link LexicalEditor} by a + * {@link LexicalComposer}. It exposes a `getTheme()` function that resolves the + * active {@link EditorThemeClasses}, falling back to any parent composer's + * theme. + */ export type LexicalComposerContextType = { getTheme: () => EditorThemeClasses | null | undefined; }; +/** + * A tuple of the {@link LexicalEditor} and its + * {@link LexicalComposerContextType}, as stored in {@link LexicalComposerContext} + * and returned by {@link useLexicalComposerContext}. + */ export type LexicalComposerContextWithEditor = [ LexicalEditor, LexicalComposerContextType, ]; +/** + * The React context used to share the {@link LexicalEditor} and its + * {@link LexicalComposerContextType} with descendant plugins and components. + * Most code should read it through {@link useLexicalComposerContext} rather than + * consuming the context directly. + */ export const LexicalComposerContext: React.Context< LexicalComposerContextWithEditor | null | undefined > = createReactContext( null, ); +/** + * Creates a {@link LexicalComposerContextType} for a composer. Theme resolution + * falls back to the optional `parent` context, so nested composers inherit the + * parent's theme unless they provide their own. + * + * @param parent - The parent composer context to inherit from, if any. + * @param theme - The theme classes for this composer, or `null`/`undefined` to + * inherit from `parent`. + * @returns The new composer context value. + */ export function createLexicalComposerContext( parent: LexicalComposerContextWithEditor | null | undefined, theme: EditorThemeClasses | null | undefined, @@ -49,6 +76,14 @@ export function createLexicalComposerContext( }; } +/** + * Returns the {@link LexicalEditor} and its {@link LexicalComposerContextType} + * from the nearest {@link LexicalComposer} (or nested composer). This is the + * primary way plugins and components access the editor instance. + * + * @returns The `[editor, context]` tuple for the current composer. + * @throws If called outside of a LexicalComposer. + */ export function useLexicalComposerContext(): LexicalComposerContextWithEditor { const composerContext = useContext(LexicalComposerContext); diff --git a/packages/lexical-react/src/LexicalContentEditable.tsx b/packages/lexical-react/src/LexicalContentEditable.tsx index bac8571adcb..44b9018e19b 100644 --- a/packages/lexical-react/src/LexicalContentEditable.tsx +++ b/packages/lexical-react/src/LexicalContentEditable.tsx @@ -20,6 +20,12 @@ import {useCanShowPlaceholder} from './shared/useCanShowPlaceholder'; export {ContentEditableElement, type ContentEditableElementProps}; +/** + * Props for the {@link ContentEditable} component. These are the + * {@link ContentEditableElementProps} (minus `editor`, which is read from + * context) plus an optional `placeholder`; when a `placeholder` is provided an + * `aria-placeholder` string is also required for accessibility. + */ export type ContentEditableProps = Omit & ( | { @@ -34,6 +40,13 @@ export type ContentEditableProps = Omit & } ); +/** + * The editable surface of a Lexical editor: the `contentEditable` element that + * users type into. Render it inside a {@link LexicalComposer} (it reads the + * editor from context) and pass it to {@link RichTextPlugin} or + * {@link PlainTextPlugin}. An optional `placeholder` is shown while the editor + * is empty. The `ref` is forwarded to the underlying `
`. + */ export const ContentEditable = forwardRef(ContentEditableImpl); function ContentEditableImpl( diff --git a/packages/lexical-react/src/LexicalDecoratorBlockNode.ts b/packages/lexical-react/src/LexicalDecoratorBlockNode.ts index babd20082c5..77613af971a 100644 --- a/packages/lexical-react/src/LexicalDecoratorBlockNode.ts +++ b/packages/lexical-react/src/LexicalDecoratorBlockNode.ts @@ -18,6 +18,10 @@ import type {JSX} from 'react'; import {DecoratorNode} from 'lexical'; +/** + * The serialized form of a {@link DecoratorBlockNode}: the base serialized node + * data plus the block's element `format` (alignment). + */ export type SerializedDecoratorBlockNode = Spread< { format: ElementFormatType; @@ -25,6 +29,13 @@ export type SerializedDecoratorBlockNode = Spread< SerializedLexicalNode >; +/** + * A base class for block-level {@link DecoratorNode}s (decorator nodes rendered + * on their own line rather than inline). It stores an {@link ElementFormatType} + * alignment, is not indentable, and renders into a `
`. Extend it for custom + * block embeds such as images, videos, or tweets, typically pairing it with + * {@link BlockWithAlignableContents} to handle selection and alignment. + */ export class DecoratorBlockNode extends DecoratorNode { __format: ElementFormatType; @@ -80,6 +91,9 @@ export class DecoratorBlockNode extends DecoratorNode { } } +/** + * @returns `true` if `node` is a {@link DecoratorBlockNode}, narrowing its type. + */ export function $isDecoratorBlockNode( node: LexicalNode | null | undefined, ): node is DecoratorBlockNode { diff --git a/packages/lexical-react/src/LexicalDraggableBlockPlugin.tsx b/packages/lexical-react/src/LexicalDraggableBlockPlugin.tsx index d80d0b31616..f0947debed7 100644 --- a/packages/lexical-react/src/LexicalDraggableBlockPlugin.tsx +++ b/packages/lexical-react/src/LexicalDraggableBlockPlugin.tsx @@ -569,6 +569,16 @@ function useDraggableBlockMenu( ); } +/** + * Renders a draggable handle and drop-target line that let users reorder + * top-level blocks by dragging. You supply the handle and target-line elements + * via `menuComponent`/`menuRef` and `targetLineComponent`/`targetLineRef`, an + * `anchorElem` to portal them into (defaults to `document.body`), and an + * `isOnMenu` predicate used to detect interactions with the handle. + * + * @experimental The API may change in a future release. + * @returns A portal containing the drag handle and target line. + */ export function DraggableBlockPlugin_EXPERIMENTAL({ anchorElem = document.body, menuRef, diff --git a/packages/lexical-react/src/LexicalErrorBoundary.tsx b/packages/lexical-react/src/LexicalErrorBoundary.tsx index c608a9e8071..67631740d50 100644 --- a/packages/lexical-react/src/LexicalErrorBoundary.tsx +++ b/packages/lexical-react/src/LexicalErrorBoundary.tsx @@ -9,11 +9,24 @@ import {type ErrorInfo, type JSX, useCallback} from 'react'; import {ErrorBoundary} from 'react-error-boundary'; +/** + * Props for the {@link LexicalErrorBoundary} component. + */ export type LexicalErrorBoundaryProps = { children: JSX.Element; onError: (error: Error, info: ErrorInfo) => void; }; +/** + * An error boundary used by {@link RichTextPlugin} and {@link PlainTextPlugin} + * to isolate failures thrown while rendering decorator nodes. It renders a + * small fallback in place of the failed subtree and forwards the error (coerced + * to an `Error`) along with the React {@link ErrorInfo} to the `onError` + * callback. + * + * @returns The wrapped `children`, or the fallback element if an error is + * caught. + */ export function LexicalErrorBoundary({ children, onError, diff --git a/packages/lexical-react/src/LexicalExtensionComposer.tsx b/packages/lexical-react/src/LexicalExtensionComposer.tsx index 92f45a22dec..c7cfcd5b2d9 100644 --- a/packages/lexical-react/src/LexicalExtensionComposer.tsx +++ b/packages/lexical-react/src/LexicalExtensionComposer.tsx @@ -14,6 +14,9 @@ import {ReactProviderExtension} from '@lexical/react/ReactProviderExtension'; import {type AnyLexicalExtensionArgument, configExtension} from 'lexical'; import {useEffect, useMemo} from 'react'; +/** + * Props for the {@link LexicalExtensionComposer} component. + */ export interface LexicalExtensionComposerProps { /** * Your root extension, typically defined with {@link defineExtension} diff --git a/packages/lexical-react/src/LexicalExtensionEditorComposer.tsx b/packages/lexical-react/src/LexicalExtensionEditorComposer.tsx index de03c04290c..26877de97ee 100644 --- a/packages/lexical-react/src/LexicalExtensionEditorComposer.tsx +++ b/packages/lexical-react/src/LexicalExtensionEditorComposer.tsx @@ -9,6 +9,9 @@ import {getExtensionDependencyFromEditor} from '@lexical/extension'; import {ReactExtension} from '@lexical/react/ReactExtension'; import {LexicalEditorWithDispose} from 'lexical'; +/** + * Props for the {@link LexicalExtensionEditorComposer} component. + */ export interface LexicalExtensionEditorComposerProps { /** * Your root extension, typically defined with {@link defineExtension}. diff --git a/packages/lexical-react/src/LexicalHashtagPlugin.ts b/packages/lexical-react/src/LexicalHashtagPlugin.ts index 9ae28a86a63..755331a2bcf 100644 --- a/packages/lexical-react/src/LexicalHashtagPlugin.ts +++ b/packages/lexical-react/src/LexicalHashtagPlugin.ts @@ -12,6 +12,16 @@ import {HashtagNode, registerLexicalHashtag} from '@lexical/hashtag'; import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; import {useEffect} from 'react'; +/** + * Enables hashtag support by transforming runs of text that begin with `#` + * into {@link HashtagNode}s. The editor must have the {@link HashtagNode} + * registered. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link HashtagExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function HashtagPlugin(): JSX.Element | null { const [editor] = useLexicalComposerContext(); diff --git a/packages/lexical-react/src/LexicalHistoryPlugin.ts b/packages/lexical-react/src/LexicalHistoryPlugin.ts index c51f247a8e7..92dd7a33126 100644 --- a/packages/lexical-react/src/LexicalHistoryPlugin.ts +++ b/packages/lexical-react/src/LexicalHistoryPlugin.ts @@ -16,6 +16,17 @@ export {createEmptyHistoryState} from '@lexical/history'; export type {HistoryState}; +/** + * Adds undo/redo support to the editor by tracking changes in a + * {@link HistoryState}. Pass `delay` to control how long (in milliseconds) + * consecutive changes are merged into a single history entry, or + * `externalHistoryState` to share a history stack across editors. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link HistoryExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function HistoryPlugin({ delay, externalHistoryState, diff --git a/packages/lexical-react/src/LexicalHorizontalRulePlugin.ts b/packages/lexical-react/src/LexicalHorizontalRulePlugin.ts index 95c126efbc7..44007651c64 100644 --- a/packages/lexical-react/src/LexicalHorizontalRulePlugin.ts +++ b/packages/lexical-react/src/LexicalHorizontalRulePlugin.ts @@ -20,7 +20,11 @@ import { import {useEffect} from 'react'; /** - * @deprecated A pure Lexical implementation is available in `@lexical/extension` as HorizontalRuleExtension + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link HorizontalRuleExtension} instead. + * + * @deprecated A pure Lexical implementation is available in `@lexical/extension` + * as {@link HorizontalRuleExtension} */ export function HorizontalRulePlugin(): null { const [editor] = useLexicalComposerContext(); diff --git a/packages/lexical-react/src/LexicalLinkPlugin.ts b/packages/lexical-react/src/LexicalLinkPlugin.ts index 0a63475b4ea..78086f8253d 100644 --- a/packages/lexical-react/src/LexicalLinkPlugin.ts +++ b/packages/lexical-react/src/LexicalLinkPlugin.ts @@ -16,6 +16,18 @@ type Props = { attributes?: LinkAttributes; }; +/** + * Enables {@link LinkNode} support, registering the commands and transforms + * that toggle and normalize links. Pass `validateUrl` to restrict which URLs + * may be applied (which also enables automatic link creation when pasting a + * matching URL) and `attributes` to set defaults such as `target` or `rel`. + * The editor must have the {@link LinkNode} registered. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link LinkExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function LinkPlugin({validateUrl, attributes}: Props): null { const [editor] = useLexicalComposerContext(); diff --git a/packages/lexical-react/src/LexicalListPlugin.ts b/packages/lexical-react/src/LexicalListPlugin.ts index e0a8722943e..e0609747e84 100644 --- a/packages/lexical-react/src/LexicalListPlugin.ts +++ b/packages/lexical-react/src/LexicalListPlugin.ts @@ -18,6 +18,9 @@ import {useEffect} from 'react'; import {useList} from './shared/useList'; +/** + * Props for the {@link ListPlugin} component. + */ export interface ListPluginProps { /** * When `true`, enforces strict indentation rules for list items, ensuring consistent structure. @@ -31,6 +34,16 @@ export interface ListPluginProps { shouldPreserveNumbering?: boolean; } +/** + * Enables ordered, unordered, and check list support, registering the commands + * and transforms that create and maintain {@link ListNode} and + * {@link ListItemNode} structures. The editor must have both nodes registered. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link ListExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function ListPlugin({ hasStrictIndent = false, shouldPreserveNumbering = false, diff --git a/packages/lexical-react/src/LexicalMarkdownShortcutPlugin.tsx b/packages/lexical-react/src/LexicalMarkdownShortcutPlugin.tsx index b9fdd0adf01..f5409e68206 100644 --- a/packages/lexical-react/src/LexicalMarkdownShortcutPlugin.tsx +++ b/packages/lexical-react/src/LexicalMarkdownShortcutPlugin.tsx @@ -39,8 +39,21 @@ const HR: ElementTransformer = { triggerOnEnter: true, type: 'element', }; +/** + * The default set of Markdown {@link Transformer}s used by + * {@link MarkdownShortcutPlugin}: the core `@lexical/markdown` `TRANSFORMERS` + * plus a transformer that turns `---`, `***`, or `___` into a horizontal rule. + */ export const DEFAULT_TRANSFORMERS = [HR, ...TRANSFORMERS]; +/** + * Registers Markdown shortcuts so that typing Markdown syntax (for example + * `# ` for a heading or `- ` for a list item) is automatically converted into + * the corresponding nodes as you type. Pass `transformers` to customize which + * shortcuts are active; it defaults to {@link DEFAULT_TRANSFORMERS}. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function MarkdownShortcutPlugin({ transformers = DEFAULT_TRANSFORMERS, }: Readonly<{ diff --git a/packages/lexical-react/src/LexicalNestedComposer.tsx b/packages/lexical-react/src/LexicalNestedComposer.tsx index fc521bf4734..ae393224a2a 100644 --- a/packages/lexical-react/src/LexicalNestedComposer.tsx +++ b/packages/lexical-react/src/LexicalNestedComposer.tsx @@ -30,6 +30,9 @@ import { import * as React from 'react'; import {ReactNode, useContext, useEffect, useMemo, useRef} from 'react'; +/** + * Props for the {@link LexicalNestedComposer} component. + */ export interface LexicalNestedComposerProps { /** * Any children (e.g. plug-ins) for this editor. Note that the nested editor @@ -84,6 +87,20 @@ const explicitNamespaceWarning = warnOnlyOnce( `LexicalNestedComposer initialEditor should explicitly initialize its namespace when the node configuration differs from the parentEditor. For backwards compatibility, the namespace will be initialized from parentEditor until v0.32.0, but this has always had incorrect copy/paste behavior when the configuration differed.\nYou can configure your editor's namespace with createEditor({namespace: 'nested-editor-namespace', nodes: [], parentEditor: $getEditor()}).`, ); +/** + * Provides a nested {@link LexicalEditor} (for example the editor that backs a + * node such as an image caption or table cell) to its `children` through React + * context, the same way {@link LexicalComposer} does for a top-level editor. + * The nested editor must be created ahead of time (typically inside the owning + * decorator node) and passed as `initialEditor`; by default it inherits the + * parent's theme, nodes, namespace, and editable state. + * + * `LexicalNestedComposer` uses the legacy plugin pattern. To build a nested + * editor from extensions, create it with {@link NestedEditorExtension} and + * render it with {@link LexicalExtensionEditorComposer} instead. + * + * @returns A context provider wrapping `children`. + */ export function LexicalNestedComposer({ initialEditor, children, diff --git a/packages/lexical-react/src/LexicalNodeContextMenuPlugin.tsx b/packages/lexical-react/src/LexicalNodeContextMenuPlugin.tsx index 5a350343706..a4937a60b99 100644 --- a/packages/lexical-react/src/LexicalNodeContextMenuPlugin.tsx +++ b/packages/lexical-react/src/LexicalNodeContextMenuPlugin.tsx @@ -40,6 +40,12 @@ class MenuOption { } } +/** + * A selectable item in a {@link NodeContextMenuPlugin}. It pairs a `title` (and + * optional `icon`/`disabled` state) with a `$onSelect` callback that runs in an + * editor update when chosen, and an optional `$showOn` predicate that decides, + * per node, whether the item is shown for the right-clicked node. + */ class NodeContextMenuOption extends MenuOption { type: string; title: string; @@ -69,6 +75,11 @@ class NodeContextMenuOption extends MenuOption { } } +/** + * A non-interactive divider between groups of {@link NodeContextMenuOption}s in + * a {@link NodeContextMenuPlugin}. Like menu options, it accepts an optional + * `$showOn` predicate to control when it is displayed. + */ class NodeContextMenuSeparator extends MenuOption { type: string; $showOn?: (node: LexicalNode) => boolean; @@ -137,6 +148,16 @@ interface Props { items: ContextMenuType[]; } +/** + * Renders a custom context menu (replacing the browser's) when the user + * right-clicks inside the editor. Pass the `items` to display as + * {@link NodeContextMenuOption}s and {@link NodeContextMenuSeparator}s; each + * item's optional `$showOn` predicate is evaluated against the node nearest the + * click so the menu can adapt to its target. Supports keyboard navigation and + * type-ahead. + * + * @returns A portal containing the floating context menu while it is open. + */ const NodeContextMenuPlugin = forwardRef< HTMLButtonElement, Props & React.HTMLProps diff --git a/packages/lexical-react/src/LexicalNodeEventPlugin.ts b/packages/lexical-react/src/LexicalNodeEventPlugin.ts index 3903128751b..a2c1d7b0a10 100644 --- a/packages/lexical-react/src/LexicalNodeEventPlugin.ts +++ b/packages/lexical-react/src/LexicalNodeEventPlugin.ts @@ -15,6 +15,14 @@ import {useEffect, useRef} from 'react'; const capturedEvents = new Set(['mouseenter', 'mouseleave']); +/** + * Attaches a DOM event listener to the editor's root element and invokes + * `eventListener` whenever an event of `eventType` targets a node that is (or + * is contained by) an instance of `nodeType`. The listener receives the DOM + * event, the editor, and the matching node's {@link NodeKey}. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function NodeEventPlugin({ nodeType, eventType, diff --git a/packages/lexical-react/src/LexicalNodeMenuPlugin.tsx b/packages/lexical-react/src/LexicalNodeMenuPlugin.tsx index 0c684b2de34..d384efc4394 100644 --- a/packages/lexical-react/src/LexicalNodeMenuPlugin.tsx +++ b/packages/lexical-react/src/LexicalNodeMenuPlugin.tsx @@ -23,6 +23,9 @@ import {useCallback, useEffect, useState} from 'react'; import {LexicalMenu, MenuOption, useMenuAnchorRef} from './shared/LexicalMenu'; import {startTransition} from './shared/reactPatches'; +/** + * Props for the {@link LexicalNodeMenuPlugin} component. + */ export type NodeMenuPluginProps = { onSelectOption: ( option: TOption, @@ -40,6 +43,15 @@ export type NodeMenuPluginProps = { parent?: HTMLElement; }; +/** + * Renders a floating menu anchored to a specific node (identified by + * `nodeKey`), for example to offer actions on a just-inserted node. It is the + * node-anchored counterpart to {@link LexicalTypeaheadMenuPlugin}: provide the + * `options` to show and an `onSelectOption` handler, and the menu opens while + * `nodeKey` refers to a node and closes when it becomes `null`. + * + * @returns The floating menu element, or `null` when the menu is closed. + */ export function LexicalNodeMenuPlugin({ options, nodeKey, diff --git a/packages/lexical-react/src/LexicalOnChangePlugin.ts b/packages/lexical-react/src/LexicalOnChangePlugin.ts index a96490b1ca5..a91502b3195 100644 --- a/packages/lexical-react/src/LexicalOnChangePlugin.ts +++ b/packages/lexical-react/src/LexicalOnChangePlugin.ts @@ -13,6 +13,14 @@ import {HISTORY_MERGE_TAG} from 'lexical'; import useLayoutEffect from './shared/useLayoutEffect'; +/** + * Calls `onChange` with the latest {@link EditorState} whenever the editor + * updates. By default, updates that only change the selection, and updates that + * are part of a history merge, are ignored; set `ignoreSelectionChange` or + * `ignoreHistoryMergeTagChange` to control that filtering. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function OnChangePlugin({ ignoreHistoryMergeTagChange = true, ignoreSelectionChange = false, diff --git a/packages/lexical-react/src/LexicalPlainTextPlugin.tsx b/packages/lexical-react/src/LexicalPlainTextPlugin.tsx index 129f8de6d77..1ff317912e9 100644 --- a/packages/lexical-react/src/LexicalPlainTextPlugin.tsx +++ b/packages/lexical-react/src/LexicalPlainTextPlugin.tsx @@ -18,6 +18,18 @@ import { import {useCanShowPlaceholder} from './shared/useCanShowPlaceholder'; import {usePlainTextSetup} from './shared/usePlainTextSetup'; +/** + * Sets up plain text editing by wiring up the core plain text commands and + * rendering the provided `contentEditable`, an optional `placeholder`, and any + * decorator nodes (wrapped in the given `ErrorBoundary`). Use this instead of + * {@link RichTextPlugin} when the editor should not support block-level rich + * text formatting. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link PlainTextExtension} instead. + * + * @returns The element tree to render inside your LexicalComposer. + */ export function PlainTextPlugin({ contentEditable, // TODO Remove. This property is now part of ContentEditable diff --git a/packages/lexical-react/src/LexicalRichTextPlugin.tsx b/packages/lexical-react/src/LexicalRichTextPlugin.tsx index 5afaa188634..6d000c8995e 100644 --- a/packages/lexical-react/src/LexicalRichTextPlugin.tsx +++ b/packages/lexical-react/src/LexicalRichTextPlugin.tsx @@ -18,6 +18,18 @@ import { import {useCanShowPlaceholder} from './shared/useCanShowPlaceholder'; import {useRichTextSetup} from './shared/useRichTextSetup'; +/** + * Sets up rich text editing by wiring up the core rich text commands and + * rendering the provided `contentEditable`, an optional `placeholder`, and any + * decorator nodes (wrapped in the given `ErrorBoundary`). This is the standard + * plugin for editors that support headings, lists, quotes, and other + * block-level formatting. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link RichTextExtension} instead. + * + * @returns The element tree to render inside your LexicalComposer. + */ export function RichTextPlugin({ contentEditable, // TODO Remove. This property is now part of ContentEditable diff --git a/packages/lexical-react/src/LexicalSelectionAlwaysOnDisplay.tsx b/packages/lexical-react/src/LexicalSelectionAlwaysOnDisplay.tsx index 706b55dcebe..b108e490530 100644 --- a/packages/lexical-react/src/LexicalSelectionAlwaysOnDisplay.tsx +++ b/packages/lexical-react/src/LexicalSelectionAlwaysOnDisplay.tsx @@ -14,6 +14,17 @@ type Props = Readonly<{ onReposition?: (node: readonly HTMLElement[]) => void; }>; +/** + * Keeps the visual selection highlight on display even when the editor loses + * focus, which is useful when interacting with toolbars or other controls + * outside the editor. Pass `onReposition` to be notified when the highlighted + * range elements are recomputed. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link SelectionAlwaysOnDisplayExtension} instead. + * + * @returns `null`, this plugin renders no DOM of its own. + */ export function SelectionAlwaysOnDisplay({onReposition}: Props): null { const [editor] = useLexicalComposerContext(); useEffect(() => { diff --git a/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx b/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx index 96ba1aba018..deffeec57fb 100644 --- a/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx +++ b/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx @@ -19,6 +19,9 @@ export {registerTabIndentation}; * This plugin adds the ability to indent content using the tab key. Generally, we don't * recommend using this plugin as it could negatively affect accessibility for keyboard * users, causing focus to become trapped within the editor. + * + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link TabIndentationExtension} instead. */ export function TabIndentationPlugin({ maxIndent, diff --git a/packages/lexical-react/src/LexicalTableOfContentsPlugin.tsx b/packages/lexical-react/src/LexicalTableOfContentsPlugin.tsx index df309605b48..4ada5a7b9d3 100644 --- a/packages/lexical-react/src/LexicalTableOfContentsPlugin.tsx +++ b/packages/lexical-react/src/LexicalTableOfContentsPlugin.tsx @@ -23,6 +23,10 @@ import { } from 'lexical'; import {useEffect, useState} from 'react'; +/** + * A single entry in the table of contents, as a tuple of the heading node's + * {@link NodeKey}, its text content, and its heading tag (for example `'h1'`). + */ export type TableOfContentsEntry = [ key: NodeKey, text: string, @@ -142,6 +146,15 @@ type Props = { ) => JSX.Element; }; +/** + * Tracks every {@link HeadingNode} in the editor and keeps an ordered list of + * {@link TableOfContentsEntry}s in sync as headings are added, removed, edited, + * or moved. It is a render-prop component: `children` receives the current + * entries and the editor and returns the element used to render the table of + * contents. + * + * @returns The element returned by the `children` render prop. + */ export function TableOfContentsPlugin({children}: Props): JSX.Element { const [tableOfContents, setTableOfContents] = useState< TableOfContentsEntry[] diff --git a/packages/lexical-react/src/LexicalTablePlugin.ts b/packages/lexical-react/src/LexicalTablePlugin.ts index a55baee53fd..47a70aea1f5 100644 --- a/packages/lexical-react/src/LexicalTablePlugin.ts +++ b/packages/lexical-react/src/LexicalTablePlugin.ts @@ -21,6 +21,9 @@ import { import {$fullReconcile} from 'lexical'; import {useEffect, useState} from 'react'; +/** + * Props for the {@link TablePlugin} component. + */ export interface TablePluginProps { /** * When `false` (default `true`), merged cell support (colspan and rowspan) will be disabled and all @@ -51,6 +54,9 @@ export interface TablePluginProps { * A plugin to enable all of the features of Lexical's TableNode. * * @param props - See type for documentation + * This is a legacy plugin. When building an editor with the extension API, + * configure {@link TableExtension} instead. + * * @returns An element to render in your LexicalComposer */ export function TablePlugin({ diff --git a/packages/lexical-react/src/LexicalTreeView.tsx b/packages/lexical-react/src/LexicalTreeView.tsx index 7ea240aec74..8723b1a88a3 100644 --- a/packages/lexical-react/src/LexicalTreeView.tsx +++ b/packages/lexical-react/src/LexicalTreeView.tsx @@ -24,6 +24,10 @@ import {useEffect, useState} from 'react'; * the Lexical editor's state and enables debugging features like time travel * and custom tree node rendering. * + * When building an editor with the extension API, configure + * {@link TreeViewExtension} to render this debugging view wired to the editor + * instead of rendering `TreeView` directly. + * * @param {Object} props - The properties passed to the TreeView component. * @param {LexicalEditor} props.editor - The Lexical editor instance to be visualized and debugged. * @param {string} [props.treeTypeButtonClassName] - Custom class name for the tree type toggle button. diff --git a/packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx b/packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx index 395f816c479..341069c97bb 100644 --- a/packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx +++ b/packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx @@ -33,6 +33,11 @@ import {useCallback, useEffect, useState} from 'react'; import {LexicalMenu, MenuOption, useMenuAnchorRef} from './shared/LexicalMenu'; import {startTransition} from './shared/reactPatches'; +/** + * The default set of punctuation characters (as a character-class fragment) + * that terminate a typeahead query. Used as the default `punctuation` option of + * {@link useBasicTypeaheadTriggerMatch}. + */ export const PUNCTUATION = '\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%\'"~=<>_:;'; @@ -108,6 +113,12 @@ function isSelectionOnEntityBoundary( } // Got from https://stackoverflow.com/a/42543908/2013580 +/** + * Walks up from `element` and returns the nearest scrollable ancestor (or + * `document.body` if none is found), used to keep the active typeahead option + * scrolled into view. Set `includeHidden` to also treat `overflow: hidden` + * ancestors as scroll parents. + */ export function getScrollParent( element: HTMLElement, includeHidden: boolean, @@ -139,11 +150,25 @@ export function getScrollParent( export {useDynamicPositioning} from './shared/LexicalMenu'; +/** + * Command dispatched while the typeahead menu is open to scroll the option at + * the given `index` into view. The default menu renderer listens for it; custom + * {@link MenuRenderFn}s can handle it to implement their own scrolling. + */ export const SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND: LexicalCommand<{ index: number; option: MenuOption; }> = /* @__PURE__ */ createCommand('SCROLL_TYPEAHEAD_OPTION_INTO_VIEW_COMMAND'); +/** + * Builds a {@link TriggerFn} for the common case of a single-character + * `trigger` (such as `@` or `#`) followed by a query. The returned function + * matches when the trigger is preceded by whitespace or the start of the line + * and is followed by between `minLength` and `maxLength` non-`punctuation` + * characters (optionally allowing whitespace). + * + * @returns A memoized trigger function for {@link LexicalTypeaheadMenuPlugin}. + */ export function useBasicTypeaheadTriggerMatch( trigger: string, { @@ -192,6 +217,9 @@ export function useBasicTypeaheadTriggerMatch( ); } +/** + * Props for the {@link LexicalTypeaheadMenuPlugin} component. + */ export type TypeaheadMenuPluginProps = { onQueryChange: (matchingString: string | null) => void; onSelectOption: ( @@ -212,6 +240,15 @@ export type TypeaheadMenuPluginProps = { ignoreEntityBoundary?: boolean; }; +/** + * Renders a floating menu (such as an `@`-mention or slash-command picker) while + * the text before the cursor matches `triggerFn`. As the user types, the + * current query is reported via `onQueryChange`; supply the `options` to show + * and an `onSelectOption` handler to apply the chosen option. Use + * {@link useBasicTypeaheadTriggerMatch} to build a simple `triggerFn`. + * + * @returns The floating menu element, or `null` when no query is active. + */ export function LexicalTypeaheadMenuPlugin({ options, onQueryChange, diff --git a/packages/lexical-react/src/ReactPluginHostExtension.tsx b/packages/lexical-react/src/ReactPluginHostExtension.tsx index a0ab53bd8c1..be67cc1bb08 100644 --- a/packages/lexical-react/src/ReactPluginHostExtension.tsx +++ b/packages/lexical-react/src/ReactPluginHostExtension.tsx @@ -35,16 +35,34 @@ import {type Container, createRoot, type Root} from 'react-dom/client'; export type {DecoratorComponentProps}; +/** + * Payload for {@link REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND}: the React DOM `root` + * that the plugin host renders into. + */ export interface HostMountCommandArg { root: Root; } +/** + * Payload for {@link REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND}, describing a piece + * of React content to mount into the plugin host: a unique `key`, the `element` + * to render (or `null` to unmount it), and an optional `domNode` to portal it + * into. + */ export interface MountPluginCommandArg { key: string; element: JSX.Element | null; domNode?: Element | DocumentFragment | null; } +/** + * Mounts the output `Component` of a {@link ReactExtension}-based extension into + * an editor's plugin host, rendering it with the given `props`. Use this to add + * UI for a specific extension to an editor that was not built with + * {@link LexicalExtensionComposer}. The editor must use + * {@link ReactPluginHostExtension} and its host must already be mounted with + * {@link mountReactPluginHost}. + */ export function mountReactExtensionComponent< Extension extends AnyLexicalExtension, >( @@ -72,6 +90,13 @@ export function mountReactExtensionComponent< }); } +/** + * Mounts an arbitrary React `Component` (rendered with `props`, or unmounted + * when `props` is `null`) into an editor's plugin host. Use this for legacy + * React plug-ins or any React content. The editor must use + * {@link ReactPluginHostExtension} with its host mounted via + * {@link mountReactPluginHost}. + */ export function mountReactPluginComponent< P extends Record = Record, >( @@ -88,6 +113,13 @@ export function mountReactPluginComponent< }); } +/** + * Mounts a React `element` (the lowest-level entry point) into an editor's + * plugin host. {@link mountReactExtensionComponent} and + * {@link mountReactPluginComponent} are built on top of this. The editor must + * use {@link ReactPluginHostExtension} with its host mounted via + * {@link mountReactPluginHost}. + */ export function mountReactPluginElement( editor: LexicalEditor, opts: MountPluginCommandArg, @@ -98,6 +130,13 @@ export function mountReactPluginElement( ).output.mountReactPlugin(opts); } +/** + * Creates a React root in `container` and mounts the editor's React plugin host + * into it. Call this once before mounting any React content with + * {@link mountReactExtensionComponent}, {@link mountReactPluginComponent}, or + * {@link mountReactPluginElement} on an editor using + * {@link ReactPluginHostExtension}. + */ export function mountReactPluginHost( editor: LexicalEditor, container: Container, @@ -108,10 +147,21 @@ export function mountReactPluginHost( ).output.mountReactPluginHost(container); } +/** + * Command dispatched by {@link mountReactPluginHost} to mount the React plugin + * host into a React root (see {@link HostMountCommandArg}). Handled by + * {@link ReactPluginHostExtension}. + */ export const REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND = /* @__PURE__ */ createCommand( 'REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND', ); +/** + * Command dispatched by the mount helpers to add, update, or remove a piece of + * React content in the plugin host. Its payload is a + * {@link MountPluginCommandArg}, and it is handled by + * {@link ReactPluginHostExtension}. + */ export const REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND = /* @__PURE__ */ createCommand( 'REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND', diff --git a/packages/lexical-react/src/TreeViewExtension.tsx b/packages/lexical-react/src/TreeViewExtension.tsx index e8f5e5f34c0..170bb4cc5f0 100644 --- a/packages/lexical-react/src/TreeViewExtension.tsx +++ b/packages/lexical-react/src/TreeViewExtension.tsx @@ -13,7 +13,19 @@ import {ReactExtension} from '@lexical/react/ReactExtension'; import {useExtensionDependency} from '@lexical/react/useExtensionComponent'; import {defineExtension} from 'lexical'; +/** + * Configuration for {@link TreeViewExtensionComponent}: the props of + * {@link TreeView} except `editor` (which is taken from the composer context), + * i.e. the set of CSS class names used by the debug view. + */ export type TreeViewConfig = Omit[0], 'editor'>; +/** + * Renders the {@link TreeView} debugging panel for the current editor, merging + * the {@link TreeViewExtension} configuration with any `props` you pass. Use it + * inside an extension-based editor to inspect the editor state tree. + * + * @returns The TreeView element. + */ export function TreeViewExtensionComponent( props: Partial, ): JSX.Element { diff --git a/packages/lexical-react/src/shared/LexicalContentEditableElement.tsx b/packages/lexical-react/src/shared/LexicalContentEditableElement.tsx index 8d0352e81fb..8c3a7f05e68 100644 --- a/packages/lexical-react/src/shared/LexicalContentEditableElement.tsx +++ b/packages/lexical-react/src/shared/LexicalContentEditableElement.tsx @@ -15,6 +15,14 @@ import {forwardRef, useCallback, useMemo, useState} from 'react'; import {mergeRefs} from './mergeRefs'; import useLayoutEffect from './useLayoutEffect'; +/** + * Props for the {@link ContentEditableElement} component. In addition to an + * `editor`, it accepts the standard `
` HTML attributes (except + * `placeholder`), including the hyphenated `aria-*` attributes, which are the + * preferred way to set ARIA properties. The camelCase `aria*` props (such as + * `ariaLabel`) are also accepted but are retained only for backwards + * compatibility. + */ export type ContentEditableElementProps = { editor: LexicalEditor; ariaActiveDescendant?: React.AriaAttributes['aria-activedescendant']; @@ -123,4 +131,12 @@ function ContentEditableElementImpl( ); } +/** + * A lower-level building block for the editor's editable `
`. It binds the + * given `editor` to the rendered element via + * {@link LexicalEditor.setRootElement}, reflects the editor's editable state on + * the `contentEditable` attribute, and applies the provided ARIA and HTML + * attributes. Prefer {@link ContentEditable}, which reads the editor from + * context and adds placeholder support, unless you need this extra control. + */ export const ContentEditableElement = forwardRef(ContentEditableElementImpl); diff --git a/packages/lexical-react/src/shared/LexicalMenu.tsx b/packages/lexical-react/src/shared/LexicalMenu.tsx index bb7024d247e..cb8bfeb6443 100644 --- a/packages/lexical-react/src/shared/LexicalMenu.tsx +++ b/packages/lexical-react/src/shared/LexicalMenu.tsx @@ -39,17 +39,34 @@ import ReactDOM from 'react-dom'; import useLayoutEffect from './useLayoutEffect'; +/** + * Describes where a typeahead trigger matched the text before the cursor: the + * `leadOffset` where the match starts, the captured `matchingString` (the query + * after the trigger), and the `replaceableString` (the full matched text, + * including the trigger, that should be replaced when an option is selected). + */ export type MenuTextMatch = { leadOffset: number; matchingString: string; replaceableString: string; }; +/** + * The position and match information for an open menu: a `getRect` function that + * returns the anchor rectangle the menu is positioned against, and the optional + * {@link MenuTextMatch} that opened it. + */ export type MenuResolution = { match?: MenuTextMatch; getRect: () => DOMRect; }; +/** + * The base class for an item shown in a {@link LexicalTypeaheadMenuPlugin} or + * {@link LexicalNodeMenuPlugin} menu. Each option has a unique `key` and a `ref` + * to its rendered element (used for scrolling and keyboard navigation). + * Subclass it to attach your own data such as a label or callback. + */ export class MenuOption { key: string; ref?: RefObject; @@ -67,6 +84,13 @@ export class MenuOption { } } +/** + * A render function for a menu's contents. It receives the anchor element ref, + * the current item props (selected index, options, and helpers to select or + * highlight an option), and the matching query string, and returns the menu + * element (or portal) to render, or `null` to render nothing. Provide one to + * fully customize a menu's appearance. + */ export type MenuRenderFn = ( anchorElementRef: RefObject, itemProps: { @@ -202,6 +226,12 @@ function isTriggerVisibleInNearestScrollContainer( ); } +/** + * Keeps an open menu aligned with its trigger by calling `onReposition` on + * scroll, window resize, and target element resize while `resolution` is set. + * Optionally calls `onVisibilityChange` when the trigger enters or leaves its + * nearest scroll container's viewport. + */ // Reposition the menu on scroll, window resize, and element resize. export function useDynamicPositioning( resolution: MenuResolution | null, @@ -732,6 +762,12 @@ export function useMenuAnchorRef( return anchorElementRef; } +/** + * Detects whether the text before the cursor should open a typeahead menu. + * Given the current `text` and `editor`, it returns a {@link MenuTextMatch} + * describing the match, or `null` if there is none. See + * {@link useBasicTypeaheadTriggerMatch} for a common implementation. + */ export type TriggerFn = ( text: string, editor: LexicalEditor, diff --git a/packages/lexical-react/src/shared/types.ts b/packages/lexical-react/src/shared/types.ts index ad5405ef1d4..80174c61199 100644 --- a/packages/lexical-react/src/shared/types.ts +++ b/packages/lexical-react/src/shared/types.ts @@ -8,6 +8,11 @@ import type {LexicalComposerContextWithEditor} from '@lexical/react/LexicalComposerContext'; import type {JSX} from 'react'; +/** + * Props for an `EditorChildrenComponent` (see {@link ReactConfig}): the composer + * `context`, the `contentEditable` element to render (or `null`), and any + * additional `children`. + */ export interface EditorChildrenComponentProps { context: LexicalComposerContextWithEditor; contentEditable: null | JSX.Element; @@ -18,6 +23,11 @@ export type EditorChildrenComponentType = ( props: EditorChildrenComponentProps, ) => JSX.Element | null; +/** + * Props passed to a decorator component registered through {@link ReactConfig}: + * the composer `context` (the `[editor, context]` tuple) for the editor the + * decorator is rendered in. + */ export interface DecoratorComponentProps { context: LexicalComposerContextWithEditor; } @@ -49,6 +59,12 @@ export type EditorComponentType = ( props: Partial, ) => JSX.Element; +/** + * Configuration for the {@link ReactExtension}, controlling how the editor is + * rendered in React: the root `contentEditable` element, the `ErrorBoundary` + * used for decorators, the `EditorChildrenComponent` that lays out the editor's + * children, and any additional `decorators` to render. + */ export interface ReactConfig { /** * The default root element of the editor as JSX. Uses `` @@ -80,6 +96,11 @@ export interface ReactConfig { decorators: readonly DecoratorComponentType[]; } +/** + * The output of the {@link ReactExtension}: the editor `Component` used to + * render the editor, and the composer `context` (equivalent to + * {@link useLexicalComposerContext}). + */ export interface ReactOutputs { /** * The editor component, this can be used by Extensions that depend on this to diff --git a/packages/lexical-react/src/useExtensionComponent.tsx b/packages/lexical-react/src/useExtensionComponent.tsx index c2670813e4b..206027aa66d 100644 --- a/packages/lexical-react/src/useExtensionComponent.tsx +++ b/packages/lexical-react/src/useExtensionComponent.tsx @@ -16,6 +16,13 @@ import { type OutputComponentExtension, } from 'lexical'; +/** + * Returns the resolved {@link LexicalExtensionDependency} for `extension` from + * the current editor (read from the composer context), giving access to its + * config and output. The editor must include this extension. + * + * @returns The extension's dependency record. + */ export function useExtensionDependency( extension: Extension, ): LexicalExtensionDependency { @@ -25,12 +32,27 @@ export function useExtensionDependency( ); } +/** + * Like {@link useExtensionDependency}, but returns `undefined` instead of + * throwing when `extension` is not present on the current editor. Useful for + * optionally integrating with an extension that may or may not be configured. + * + * @returns The extension's dependency record, or `undefined` if absent. + */ export function useOptionalExtensionDependency< Extension extends AnyLexicalExtension, >(extension: Extension): undefined | LexicalExtensionDependency { return usePeerExtensionDependency(extension.name); } +/** + * Returns the {@link LexicalExtensionDependency} for a peer extension looked up + * by `extensionName`, or `undefined` if no extension with that name is present + * on the current editor. Use this when you only have the peer extension's name + * (for example to avoid a hard dependency on its module). + * + * @returns The peer extension's dependency record, or `undefined` if absent. + */ export function usePeerExtensionDependency< Extension extends AnyLexicalExtension, >( diff --git a/packages/lexical-react/src/useLexicalIsTextContentEmpty.ts b/packages/lexical-react/src/useLexicalIsTextContentEmpty.ts index ee06735edfa..a32b53953ed 100644 --- a/packages/lexical-react/src/useLexicalIsTextContentEmpty.ts +++ b/packages/lexical-react/src/useLexicalIsTextContentEmpty.ts @@ -13,6 +13,13 @@ import {useState} from 'react'; import useLayoutEffect from './shared/useLayoutEffect'; +/** + * Tracks whether the editor's text content is empty, updating as the editor + * changes. Pass `trim` to ignore leading and trailing whitespace when deciding + * emptiness. + * + * @returns `true` while the editor's text content is empty. + */ export function useLexicalIsTextContentEmpty( editor: LexicalEditor, trim?: boolean, diff --git a/packages/lexical-react/src/useLexicalSubscription.tsx b/packages/lexical-react/src/useLexicalSubscription.tsx index 24c46517483..9c05d5a39b7 100644 --- a/packages/lexical-react/src/useLexicalSubscription.tsx +++ b/packages/lexical-react/src/useLexicalSubscription.tsx @@ -13,6 +13,11 @@ import {useMemo, useRef, useState} from 'react'; import useLayoutEffect from './shared/useLayoutEffect'; +/** + * Describes how {@link useLexicalSubscription} reads a value from the editor: an + * `initialValueFn` that computes the current value, and a `subscribe` function + * that registers a listener for changes and returns an unsubscribe callback. + */ export type LexicalSubscription = { initialValueFn: () => T; subscribe: (callback: (value: T) => void) => () => void; diff --git a/packages/lexical-react/src/useLexicalTextEntity.ts b/packages/lexical-react/src/useLexicalTextEntity.ts index 3548c56f958..54ca6cc2da3 100644 --- a/packages/lexical-react/src/useLexicalTextEntity.ts +++ b/packages/lexical-react/src/useLexicalTextEntity.ts @@ -14,6 +14,13 @@ import {registerLexicalTextEntity} from '@lexical/text'; import {mergeRegister} from '@lexical/utils'; import {useEffect} from 'react'; +/** + * Registers a text entity on the current editor: text matched by `getMatch` is + * automatically wrapped in instances of `targetNode` (created via `createNode`) + * and unwrapped when it no longer matches. This is the React wrapper around + * `registerLexicalTextEntity` and is the basis for features such as hashtags + * and mentions. + */ export function useLexicalTextEntity( getMatch: (text: string) => null | EntityMatch, targetNode: Klass,