/** * 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, LexicalEditor} from 'lexical'; import type {JSX} from 'react'; import { CustomPrintNodeFn, generateContent, TreeView as TreeViewCore, useLexicalCommandsLog, } from '@lexical/devtools-core'; import {mergeRegister} from '@lexical/utils'; import * as React from 'react'; import {useEffect, useState} from 'react'; /** * TreeView is a React component that provides a visual representation of * the Lexical editor's state and enables debugging features like time travel * and custom tree node rendering. * * @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. * @param {string} [props.timeTravelButtonClassName] - Custom class name for the time travel toggle button. * @param {string} [props.timeTravelPanelButtonClassName] - Custom class name for buttons inside the time travel panel. * @param {string} [props.timeTravelPanelClassName] - Custom class name for the overall time travel panel container. * @param {string} [props.timeTravelPanelSliderClassName] - Custom class name for the time travel slider in the panel. * @param {string} [props.viewClassName] - Custom class name for the tree view container. * @param {CustomPrintNodeFn} [props.customPrintNode] - A function for customizing the display of nodes in the tree. * * @returns {JSX.Element} - A React element that visualizes the editor's state and supports debugging interactions. */ export function TreeView({ treeTypeButtonClassName, timeTravelButtonClassName, timeTravelPanelSliderClassName, timeTravelPanelButtonClassName, timeTravelPanelClassName, viewClassName, editor, customPrintNode, }: { editor: LexicalEditor; treeTypeButtonClassName?: string; timeTravelButtonClassName?: string; timeTravelPanelButtonClassName?: string; timeTravelPanelClassName?: string; timeTravelPanelSliderClassName?: string; viewClassName?: string; customPrintNode?: CustomPrintNodeFn; }): JSX.Element { const treeElementRef = React.createRef(); const [editorCurrentState, setEditorCurrentState] = useState( editor.getEditorState(), ); const commandsLog = useLexicalCommandsLog(editor); useEffect(() => { // Registers listeners to update the tree view when the editor state changes return mergeRegister( editor.registerUpdateListener(({editorState}) => { setEditorCurrentState(editorState); }), editor.registerEditableListener(() => { setEditorCurrentState(editor.getEditorState()); }), ); }, [editor]); useEffect(() => { const element = treeElementRef.current; if (element !== null) { // Assigns the editor instance to the tree view DOM element for internal tracking // @ts-ignore Internal field used by Lexical element.__lexicalEditor = editor; return () => { // Cleans up the reference when the component is unmounted // @ts-ignore Internal field used by Lexical element.__lexicalEditor = null; }; } }, [editor, treeElementRef]); /** * Handles toggling the readonly state of the editor. * * @param {boolean} isReadonly - Whether the editor should be set to readonly. */ const handleEditorReadOnly = (isReadonly: boolean) => { const rootElement = editor.getRootElement(); if (rootElement == null) { return; } rootElement.contentEditable = isReadonly ? 'false' : 'true'; }; return ( editor.setEditorState(state)} generateContent={async function (exportDOM) { // Generates the content for the tree view, allowing customization with exportDOM and customPrintNode return generateContent(editor, commandsLog, exportDOM, customPrintNode); }} ref={treeElementRef} commandsLog={commandsLog} /> ); }