Files
ncd-fe/app/soapbox/features/compose/editor/plugins/table-plugin.tsx
marcin mikołajczak 700e7af19d Lexical: WIP port tables support
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2023-08-07 21:55:17 +02:00

117 lines
3.2 KiB
TypeScript

/**
* 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<JSX.Element>
set: (
cellEditorConfig: null | CellEditorConfig,
cellEditorPlugins: null | JSX.Element | Array<JSX.Element>,
) => void
};
export type CellEditorConfig = Readonly<{
namespace: string
nodes?: ReadonlyArray<Klass<LexicalNode>>
onError: (error: Error, editor: LexicalEditor) => void
readOnly?: boolean
theme?: EditorThemeClasses
}>;
export const INSERT_NEW_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload> =
createCommand('INSERT_NEW_TABLE_COMMAND');
export const CellContext = createContext<CellContextShape>({
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<JSX.Element>
}>({
cellEditorConfig: null,
cellEditorPlugins: null,
});
return (
<CellContext.Provider
value={useMemo(
() => ({
cellEditorConfig: contextValue.cellEditorConfig,
cellEditorPlugins: contextValue.cellEditorPlugins,
set: (cellEditorConfig, cellEditorPlugins) => {
setContextValue({ cellEditorConfig, cellEditorPlugins });
},
}),
[contextValue.cellEditorConfig, contextValue.cellEditorPlugins],
)}
>
{children}
</CellContext.Provider>
);
}
export function TablePlugin({
cellEditorConfig,
children,
}: {
cellEditorConfig: CellEditorConfig
children: JSX.Element | Array<JSX.Element>
}): 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<InsertTableCommandPayload>(
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;
}