Skip to content

Commit 013b515

Browse files
authored
feat(richtext-lexical): allow disabling TabNode (#11656)
Similar to #11631. IndentFeature causes pressing Tab in the middle of a block such as a paragraph or heading to insert a TabNode. This property allows you to disable this behavior, and indentation will occur instead if the block allows it. Usage: ```ts editor: lexicalEditor({ features: ({ defaultFeatures }) => [ ...defaultFeatures, IndentFeature({ disableTabNode: true, }), ], }), ```
1 parent ebfb0eb commit 013b515

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

packages/richtext-lexical/src/features/indent/client/IndentPlugin.tsx

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,62 @@ import {
99
$isRangeSelection,
1010
COMMAND_PRIORITY_LOW,
1111
INDENT_CONTENT_COMMAND,
12+
KEY_TAB_COMMAND,
13+
OUTDENT_CONTENT_COMMAND,
14+
TabNode,
1215
} from 'lexical'
1316
import { useEffect } from 'react'
1417

1518
import type { PluginComponent } from '../../typesClient.js'
1619
import type { IndentFeatureProps } from '../server/index.js'
1720

18-
export const IndentPlugin: PluginComponent<IndentFeatureProps> = (props) => {
21+
export const IndentPlugin: PluginComponent<IndentFeatureProps> = ({ clientProps }) => {
1922
const [editor] = useLexicalComposerContext()
20-
const { disabledNodes } = props.clientProps
23+
const { disabledNodes, disableTabNode } = clientProps
2124

2225
useEffect(() => {
2326
if (!editor || !disabledNodes?.length) {
2427
return
2528
}
29+
return editor.registerCommand(
30+
INDENT_CONTENT_COMMAND,
31+
() => {
32+
return $handleIndentAndOutdent((block) => {
33+
if (!disabledNodes.includes(block.getType())) {
34+
const indent = block.getIndent()
35+
block.setIndent(indent + 1)
36+
}
37+
})
38+
},
39+
COMMAND_PRIORITY_LOW,
40+
)
41+
}, [editor, disabledNodes])
42+
43+
useEffect(() => {
44+
if (!editor || !disableTabNode) {
45+
return
46+
}
2647
return mergeRegister(
27-
editor.registerCommand(
28-
INDENT_CONTENT_COMMAND,
29-
() => {
30-
return $handleIndentAndOutdent((block) => {
31-
if (!disabledNodes.includes(block.getType())) {
32-
const indent = block.getIndent()
33-
block.setIndent(indent + 1)
34-
}
35-
})
48+
// This is so that when you press Tab in the middle of a paragraph,
49+
// it indents the paragraph, instead of inserting a TabNode.
50+
editor.registerCommand<KeyboardEvent>(
51+
KEY_TAB_COMMAND,
52+
(event) => {
53+
event.preventDefault()
54+
return editor.dispatchCommand(
55+
event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND,
56+
undefined,
57+
)
3658
},
3759
COMMAND_PRIORITY_LOW,
3860
),
61+
// Tab isn't the only way to insert a TabNode. We have to make sure
62+
// it doesn't happen, for example, when pasting from the clipboard.
63+
editor.registerNodeTransform(TabNode, (node) => {
64+
node.remove()
65+
}),
3966
)
40-
}, [editor, disabledNodes])
67+
}, [editor, disableTabNode])
4168

4269
return <TabIndentationPlugin />
4370
}

packages/richtext-lexical/src/features/indent/server/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ export type IndentFeatureProps = {
77
* These can be: "paragraph", "heading", "listitem", "quote" or other indentable nodes if they exist.
88
*/
99
disabledNodes?: string[]
10+
/**
11+
* If true, pressing Tab in the middle of a block such as a paragraph or heading will not insert a tabNode.
12+
* Instead, Tab will only be used for block-level indentation.
13+
*
14+
* @default false
15+
*/
16+
disableTabNode?: boolean
1017
}
1118

1219
export const IndentFeature = createServerFeature<

0 commit comments

Comments
 (0)