117 lines
3.2 KiB
TypeScript
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;
|
|
}
|