diff --git a/packages/lexical-playground/src/Editor.tsx b/packages/lexical-playground/src/Editor.tsx
index 1b752ced18c..ec1d2c0d1fa 100644
--- a/packages/lexical-playground/src/Editor.tsx
+++ b/packages/lexical-playground/src/Editor.tsx
@@ -55,7 +55,6 @@ import InlineImagePlugin from './plugins/InlineImagePlugin';
import KeywordsPlugin from './plugins/KeywordsPlugin';
import {LayoutPlugin} from './plugins/LayoutPlugin/LayoutPlugin';
import LinkPlugin from './plugins/LinkPlugin';
-import ListMaxIndentLevelPlugin from './plugins/ListMaxIndentLevelPlugin';
import MarkdownShortcutPlugin from './plugins/MarkdownShortcutPlugin';
import {MaxLengthPlugin} from './plugins/MaxLengthPlugin';
import MentionsPlugin from './plugins/MentionsPlugin';
@@ -200,7 +199,6 @@ export default function Editor(): JSX.Element {
-
-
+
diff --git a/packages/lexical-playground/src/plugins/ListMaxIndentLevelPlugin/index.ts b/packages/lexical-playground/src/plugins/ListMaxIndentLevelPlugin/index.ts
deleted file mode 100644
index 198543784c1..00000000000
--- a/packages/lexical-playground/src/plugins/ListMaxIndentLevelPlugin/index.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * 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 {ElementNode, RangeSelection} from 'lexical';
-
-import {$getListDepth, $isListItemNode, $isListNode} from '@lexical/list';
-import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-import {
- $getSelection,
- $isElementNode,
- $isRangeSelection,
- COMMAND_PRIORITY_CRITICAL,
- INDENT_CONTENT_COMMAND,
-} from 'lexical';
-import {useEffect} from 'react';
-
-function getElementNodesInSelection(
- selection: RangeSelection,
-): Set {
- const nodesInSelection = selection.getNodes();
-
- if (nodesInSelection.length === 0) {
- return new Set([
- selection.anchor.getNode().getParentOrThrow(),
- selection.focus.getNode().getParentOrThrow(),
- ]);
- }
-
- return new Set(
- nodesInSelection.map((n) => ($isElementNode(n) ? n : n.getParentOrThrow())),
- );
-}
-
-function $shouldPreventIndent(maxDepth: number): boolean {
- const selection = $getSelection();
-
- if (!$isRangeSelection(selection)) {
- return false;
- }
-
- const elementNodesInSelection: Set =
- getElementNodesInSelection(selection);
-
- let totalDepth = 0;
-
- for (const elementNode of elementNodesInSelection) {
- if ($isListNode(elementNode)) {
- totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth);
- } else if ($isListItemNode(elementNode)) {
- const parent = elementNode.getParent();
-
- if (!$isListNode(parent)) {
- throw new Error(
- 'ListMaxIndentLevelPlugin: A ListItemNode must have a ListNode for a parent.',
- );
- }
-
- totalDepth = Math.max($getListDepth(parent) + 1, totalDepth);
- }
- }
-
- return totalDepth > maxDepth;
-}
-
-export default function ListMaxIndentLevelPlugin({
- maxDepth = 7,
-}: {
- maxDepth?: number;
-}): null {
- const [editor] = useLexicalComposerContext();
-
- useEffect(() => {
- return editor.registerCommand(
- INDENT_CONTENT_COMMAND,
- () => $shouldPreventIndent(maxDepth),
- COMMAND_PRIORITY_CRITICAL,
- );
- }, [editor, maxDepth]);
- return null;
-}
diff --git a/packages/lexical-react/flow/LexicalTabIndentationPlugin.js.flow b/packages/lexical-react/flow/LexicalTabIndentationPlugin.js.flow
index 44784b01131..64d9ff87caa 100644
--- a/packages/lexical-react/flow/LexicalTabIndentationPlugin.js.flow
+++ b/packages/lexical-react/flow/LexicalTabIndentationPlugin.js.flow
@@ -11,6 +11,11 @@ import type {LexicalEditor} from 'lexical';
declare export function registerTabIndentation(
editor: LexicalEditor,
+ maxIndent?: number,
): () => void;
-declare export function TabIndentationPlugin(): null;
+type Props = $ReadOnly<{
+ maxIndent?: number,
+}>;
+
+declare export function TabIndentationPlugin(props: Props): null;
diff --git a/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx b/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx
index db8c0e8a6e2..7a21a5b3279 100644
--- a/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx
+++ b/packages/lexical-react/src/LexicalTabIndentationPlugin.tsx
@@ -6,16 +6,22 @@
*
*/
-import type {LexicalCommand, LexicalEditor, RangeSelection} from 'lexical';
+import type {LexicalEditor, RangeSelection} from 'lexical';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-import {$filter, $getNearestBlockElementAncestorOrThrow} from '@lexical/utils';
+import {
+ $filter,
+ $getNearestBlockElementAncestorOrThrow,
+ mergeRegister,
+} from '@lexical/utils';
import {
$createRangeSelection,
$getSelection,
$isBlockElementNode,
+ $isElementNode,
$isRangeSelection,
$normalizeSelection__EXPERIMENTAL,
+ COMMAND_PRIORITY_CRITICAL,
COMMAND_PRIORITY_EDITOR,
INDENT_CONTENT_COMMAND,
INSERT_TAB_COMMAND,
@@ -57,24 +63,54 @@ function $indentOverTab(selection: RangeSelection): boolean {
return false;
}
-export function registerTabIndentation(editor: LexicalEditor) {
- return editor.registerCommand(
- KEY_TAB_COMMAND,
- (event) => {
- const selection = $getSelection();
- if (!$isRangeSelection(selection)) {
- return false;
- }
+export function registerTabIndentation(
+ editor: LexicalEditor,
+ maxIndent?: number,
+) {
+ return mergeRegister(
+ editor.registerCommand(
+ KEY_TAB_COMMAND,
+ (event) => {
+ const selection = $getSelection();
+ if (!$isRangeSelection(selection)) {
+ return false;
+ }
+
+ event.preventDefault();
+ return $indentOverTab(selection)
+ ? event.shiftKey
+ ? editor.dispatchCommand(OUTDENT_CONTENT_COMMAND, undefined)
+ : editor.dispatchCommand(INDENT_CONTENT_COMMAND, maxIndent)
+ : editor.dispatchCommand(INSERT_TAB_COMMAND, undefined);
+ },
+ COMMAND_PRIORITY_EDITOR,
+ ),
+
+ editor.registerCommand(
+ INDENT_CONTENT_COMMAND,
+ () => {
+ if (maxIndent == null) {
+ return false;
+ }
+
+ const selection = $getSelection();
+ if (!$isRangeSelection(selection)) {
+ return false;
+ }
+
+ const indents = selection
+ .getNodes()
+ .map((node) =>
+ ($isElementNode(node) && !node.isInline()
+ ? node
+ : node.getParentOrThrow()
+ ).getIndent(),
+ );
- event.preventDefault();
- const command: LexicalCommand = $indentOverTab(selection)
- ? event.shiftKey
- ? OUTDENT_CONTENT_COMMAND
- : INDENT_CONTENT_COMMAND
- : INSERT_TAB_COMMAND;
- return editor.dispatchCommand(command, undefined);
- },
- COMMAND_PRIORITY_EDITOR,
+ return Math.max(...indents) + 1 >= maxIndent;
+ },
+ COMMAND_PRIORITY_CRITICAL,
+ ),
);
}
@@ -83,11 +119,11 @@ export function registerTabIndentation(editor: LexicalEditor) {
* recommend using this plugin as it could negatively affect acessibility for keyboard
* users, causing focus to become trapped within the editor.
*/
-export function TabIndentationPlugin(): null {
+export function TabIndentationPlugin({maxIndent}: {maxIndent?: number}): null {
const [editor] = useLexicalComposerContext();
useEffect(() => {
- return registerTabIndentation(editor);
- }, [editor]);
+ return registerTabIndentation(editor, maxIndent);
+ }, [editor, maxIndent]);
return null;
}