diff --git a/src/components/LexicalEditor/Index.jsx b/src/components/LexicalEditor/Index.jsx index 501c976..7b2005c 100644 --- a/src/components/LexicalEditor/Index.jsx +++ b/src/components/LexicalEditor/Index.jsx @@ -7,6 +7,7 @@ import { ContentEditable } from "@lexical/react/LexicalContentEditable"; import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin"; import {LexicalErrorBoundary} from "@lexical/react/LexicalErrorBoundary"; +import {TabIndentationPlugin} from '@lexical/react/LexicalTabIndentationPlugin'; import TreeViewPlugin from "./plugins/TreeViewPlugin"; import ToolbarPlugin from "./plugins/ToolbarPlugin"; import { HeadingNode, QuoteNode } from "@lexical/rich-text"; @@ -24,6 +25,7 @@ import { TRANSFORMERS } from "@lexical/markdown"; import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin"; import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin"; import AutoLinkPlugin from "./plugins/AutoLinkPlugin"; +import TabFocusPlugin from './plugins/TabFocusPlugin'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $getRoot, $getSelection, } from 'lexical'; @@ -128,10 +130,12 @@ export default function Editor({ isRichText, onChange, initialValue, ...props }) + - + + diff --git a/src/components/LexicalEditor/LICENSE b/src/components/LexicalEditor/LICENSE new file mode 100644 index 0000000..b93be90 --- /dev/null +++ b/src/components/LexicalEditor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/components/LexicalEditor/plugins/TabFocusPlugin.jsx b/src/components/LexicalEditor/plugins/TabFocusPlugin.jsx new file mode 100644 index 0000000..53670b2 --- /dev/null +++ b/src/components/LexicalEditor/plugins/TabFocusPlugin.jsx @@ -0,0 +1,57 @@ +/** + * 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 { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { $getSelection, $isRangeSelection, $setSelection, FOCUS_COMMAND } from 'lexical'; +import { useEffect } from 'react'; + +const COMMAND_PRIORITY_LOW = 1; +const TAB_TO_FOCUS_INTERVAL = 100; + +let lastTabKeyDownTimestamp = 0; +let hasRegisteredKeyDownListener = false; + +function registerKeyTimeStampTracker() { + window.addEventListener( + 'keydown', + (event) => { + // Tab + if (event.key === 'Tab') { + lastTabKeyDownTimestamp = event.timeStamp; + } + }, + true + ); +} + +export default function TabFocusPlugin() { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + if (!hasRegisteredKeyDownListener) { + registerKeyTimeStampTracker(); + hasRegisteredKeyDownListener = true; + } + + return editor.registerCommand( + FOCUS_COMMAND, + (event) => { + const selection = $getSelection(); + if ($isRangeSelection(selection)) { + if (lastTabKeyDownTimestamp + TAB_TO_FOCUS_INTERVAL > event.timeStamp) { + $setSelection(selection.clone()); + } + } + return false; + }, + COMMAND_PRIORITY_LOW + ); + }, [editor]); + + return null; +}