/** * This source code is derived from code from Meta Platforms, Inc. * and affiliates, licensed under the MIT license located in the * LICENSE file in the /app/soapbox/features/compose/editor directory. */ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $insertNodes, COMMAND_PRIORITY_EDITOR, createCommand, EditorThemeClasses, Klass, LexicalCommand, LexicalEditor, LexicalNode, } from 'lexical'; import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import * as React from 'react'; import { $createTableNodeWithDimensions, TableNode } from '../nodes/table-node'; export type InsertTableCommandPayload = Readonly<{ columns: number rows: number includeHeaders?: boolean }>; export type CellContextShape = { cellEditorConfig: null | CellEditorConfig cellEditorPlugins: null | JSX.Element | Array set: ( cellEditorConfig: null | CellEditorConfig, cellEditorPlugins: null | JSX.Element | Array, ) => void }; export type CellEditorConfig = Readonly<{ namespace: string nodes?: ReadonlyArray> onError: (error: Error, editor: LexicalEditor) => void readOnly?: boolean theme?: EditorThemeClasses }>; export const INSERT_NEW_TABLE_COMMAND: LexicalCommand = createCommand('INSERT_NEW_TABLE_COMMAND'); export const CellContext = createContext({ cellEditorConfig: null, cellEditorPlugins: null, set: () => { // Empty }, }); export function TableContext({ children }: {children: JSX.Element}) { const [contextValue, setContextValue] = useState<{ cellEditorConfig: null | CellEditorConfig cellEditorPlugins: null | JSX.Element | Array }>({ cellEditorConfig: null, cellEditorPlugins: null, }); return ( ({ cellEditorConfig: contextValue.cellEditorConfig, cellEditorPlugins: contextValue.cellEditorPlugins, set: (cellEditorConfig, cellEditorPlugins) => { setContextValue({ cellEditorConfig, cellEditorPlugins }); }, }), [contextValue.cellEditorConfig, contextValue.cellEditorPlugins], )} > {children} ); } export function TablePlugin({ cellEditorConfig, children, }: { cellEditorConfig: CellEditorConfig children: JSX.Element | Array }): JSX.Element | null { const [editor] = useLexicalComposerContext(); const cellContext = useContext(CellContext); useEffect(() => { if (!editor.hasNodes([TableNode])) { throw new Error('TablePlugin: TableNode is not registered on editor'); } cellContext.set(cellEditorConfig, children); return editor.registerCommand( INSERT_NEW_TABLE_COMMAND, ({ columns, rows, includeHeaders }) => { const tableNode = $createTableNodeWithDimensions( rows, columns, includeHeaders, ); $insertNodes([tableNode]); return true; }, COMMAND_PRIORITY_EDITOR, ); }, [cellContext, cellEditorConfig, children, editor]); return null; }