(null);
-
- useEffect(() => {
- const dropdownElem = dropDownRef.current;
- if (dropdownElem !== null) {
- const rect = menuElem.getBoundingClientRect();
- dropdownElem.style.top = `${rect.y}px`;
- dropdownElem.style.left = `${rect.x}px`;
- }
- }, [menuElem]);
-
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- const dropdownElem = dropDownRef.current;
- if (
- dropdownElem !== null &&
- !dropdownElem.contains(event.target as Node)
- ) {
- event.stopPropagation();
- }
- };
-
- window.addEventListener('click', handleClickOutside);
- return () => window.removeEventListener('click', handleClickOutside);
- }, [onClose]);
- const coords = cellCoordMap.get(cell.id);
-
- if (coords === undefined) {
- return null;
- }
- const [x, y] = coords;
-
- return (
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
- {
- e.stopPropagation();
- }}
- onPointerDown={(e) => {
- e.stopPropagation();
- }}
- onPointerUp={(e) => {
- e.stopPropagation();
- }}
- onClick={(e) => {
- e.stopPropagation();
- }}>
-
-
-
- {cell.type === 'header' && y === 0 && (
- <>
- {sortingOptions !== null && sortingOptions.x === x && (
-
- )}
- {(sortingOptions === null ||
- sortingOptions.x !== x ||
- sortingOptions.type === 'descending') && (
-
- )}
- {(sortingOptions === null ||
- sortingOptions.x !== x ||
- sortingOptions.type === 'ascending') && (
-
- )}
-
- >
- )}
-
-
-
-
-
-
- {rows[0].cells.length !== 1 && (
-
- )}
- {rows.length !== 1 && (
-
- )}
-
-
- );
-}
-
-function TableCell({
- cell,
- cellCoordMap,
- cellEditor,
- isEditing,
- isSelected,
- isPrimarySelected,
- theme,
- updateCellsByID,
- updateTableNode,
- rows,
- setSortingOptions,
- sortingOptions,
-}: {
- cell: Cell;
- isEditing: boolean;
- isSelected: boolean;
- isPrimarySelected: boolean;
- theme: EditorThemeClasses;
- cellEditor: LexicalEditor;
- updateCellsByID: (ids: Array, fn: () => void) => void;
- updateTableNode: (fn2: (tableNode: TableNode) => void) => void;
- cellCoordMap: Map;
- rows: Rows;
- setSortingOptions: (options: null | SortOptions) => void;
- sortingOptions: null | SortOptions;
-}) {
- const [showMenu, setShowMenu] = useState(false);
- const menuRootRef = useRef(null);
- const isHeader = cell.type !== 'normal';
- const editorStateJSON = cell.json;
- const CellComponent = isHeader ? 'th' : 'td';
- const cellWidth = cell.width;
- const menuElem = menuRootRef.current;
- const coords = cellCoordMap.get(cell.id);
- const isSorted =
- sortingOptions !== null &&
- coords !== undefined &&
- coords[0] === sortingOptions.x &&
- coords[1] === 0;
-
- useEffect(() => {
- if (isEditing || !isPrimarySelected) {
- setShowMenu(false);
- }
- }, [isEditing, isPrimarySelected]);
-
- return (
-
- {isPrimarySelected && (
-
- )}
- {isPrimarySelected && isEditing ? (
-
- ) : (
- <>
-
-
- >
- )}
- {isPrimarySelected && !isEditing && (
-
-
-
- )}
- {showMenu &&
- menuElem !== null &&
- createPortal(
- setShowMenu(false)}
- updateTableNode={updateTableNode}
- cellCoordMap={cellCoordMap}
- rows={rows}
- setSortingOptions={setSortingOptions}
- sortingOptions={sortingOptions}
- />,
- document.body,
- )}
- {isSorted && }
-
- );
-}
-
-export default function TableComponent({
- nodeKey,
- rows: rawRows,
- theme,
-}: {
- nodeKey: NodeKey;
- rows: Rows;
- theme: EditorThemeClasses;
-}) {
- const [isSelected, setSelected, clearSelection] =
- useLexicalNodeSelection(nodeKey);
- const resizeMeasureRef = useRef<{size: number; point: number}>({
- point: 0,
- size: 0,
- });
- const [sortingOptions, setSortingOptions] = useState(
- null,
- );
- const addRowsRef = useRef(null);
- const lastCellIDRef = useRef(null);
- const tableResizerRulerRef = useRef(null);
- const {cellEditorConfig} = useContext(CellContext);
- const [isEditing, setIsEditing] = useState(false);
- const [showAddColumns, setShowAddColumns] = useState(false);
- const [showAddRows, setShowAddRows] = useState(false);
- const [editor] = useLexicalComposerContext();
- const mouseDownRef = useRef(false);
- const [resizingID, setResizingID] = useState(null);
- const tableRef = useRef(null);
- const cellCoordMap = useMemo(() => {
- const map = new Map();
-
- for (let y = 0; y < rawRows.length; y++) {
- const row = rawRows[y];
- const cells = row.cells;
- for (let x = 0; x < cells.length; x++) {
- const cell = cells[x];
- map.set(cell.id, [x, y]);
- }
- }
- return map;
- }, [rawRows]);
- const rows = useMemo(() => {
- if (sortingOptions === null) {
- return rawRows;
- }
- const _rows = rawRows.slice(1);
- _rows.sort((a, b) => {
- const aCells = a.cells;
- const bCells = b.cells;
- const x = sortingOptions.x;
- const aContent = cellTextContentCache.get(aCells[x].json) || '';
- const bContent = cellTextContentCache.get(bCells[x].json) || '';
- if (aContent === '' || bContent === '') {
- return 1;
- }
- if (sortingOptions.type === 'ascending') {
- return aContent.localeCompare(bContent);
- }
- return bContent.localeCompare(aContent);
- });
- _rows.unshift(rawRows[0]);
- return _rows;
- }, [rawRows, sortingOptions]);
- const [primarySelectedCellID, setPrimarySelectedCellID] = useState<
- null | string
- >(null);
- const cellEditor = useMemo(() => {
- if (cellEditorConfig === null) {
- return null;
- }
- const _cellEditor = createEditor({
- namespace: cellEditorConfig.namespace,
- nodes: cellEditorConfig.nodes,
- onError: (error) => cellEditorConfig.onError(error, _cellEditor),
- theme: cellEditorConfig.theme,
- });
- return _cellEditor;
- }, [cellEditorConfig]);
- const [selectedCellIDs, setSelectedCellIDs] = useState>([]);
- const selectedCellSet = useMemo>(
- () => new Set(selectedCellIDs),
- [selectedCellIDs],
- );
-
- useEffect(() => {
- const tableElem = tableRef.current;
- if (
- isSelected &&
- document.activeElement === document.body &&
- tableElem !== null
- ) {
- tableElem.focus();
- }
- }, [isSelected]);
-
- const updateTableNode = useCallback(
- (fn: (tableNode: TableNode) => void) => {
- editor.update(() => {
- const tableNode = $getNodeByKey(nodeKey);
- if ($isTableNode(tableNode)) {
- fn(tableNode);
- }
- });
- },
- [editor, nodeKey],
- );
-
- const addColumns = () => {
- updateTableNode((tableNode) => {
- $addUpdateTag('history-push');
- tableNode.addColumns(1);
- });
- };
-
- const addRows = () => {
- updateTableNode((tableNode) => {
- $addUpdateTag('history-push');
- tableNode.addRows(1);
- });
- };
-
- const modifySelectedCells = useCallback(
- (x: number, y: number, extend: boolean) => {
- const id = rows[y].cells[x].id;
- lastCellIDRef.current = id;
- if (extend) {
- const selectedIDs = getSelectedIDs(
- rows,
- primarySelectedCellID as string,
- id,
- cellCoordMap,
- );
- setSelectedCellIDs(selectedIDs);
- } else {
- setPrimarySelectedCellID(id);
- setSelectedCellIDs(NO_CELLS);
- focusCell(tableRef.current as HTMLElement, id);
- }
- },
- [cellCoordMap, primarySelectedCellID, rows],
- );
-
- const saveEditorToJSON = useCallback(() => {
- if (cellEditor !== null && primarySelectedCellID !== null) {
- const json = JSON.stringify(cellEditor.getEditorState());
- updateTableNode((tableNode) => {
- const coords = cellCoordMap.get(primarySelectedCellID);
- if (coords === undefined) {
- return;
- }
- $addUpdateTag('history-push');
- const [x, y] = coords;
- tableNode.updateCellJSON(x, y, json);
- });
- }
- }, [cellCoordMap, cellEditor, primarySelectedCellID, updateTableNode]);
-
- const selectTable = useCallback(() => {
- setTimeout(() => {
- const parentRootElement = editor.getRootElement();
- if (parentRootElement !== null) {
- parentRootElement.focus({preventScroll: true});
- window.getSelection()?.removeAllRanges();
- }
- }, 20);
- }, [editor]);
-
- useEffect(() => {
- const tableElem = tableRef.current;
- if (tableElem === null) {
- return;
- }
- const doc = getCurrentDocument(editor);
-
- const isAtEdgeOfTable = (event: PointerEvent) => {
- const x = event.clientX - tableRect.x;
- const y = event.clientY - tableRect.y;
- return x < 5 || y < 5;
- };
-
- const handlePointerDown = (event: PointerEvent) => {
- const possibleID = getCellID(event.target as HTMLElement);
- if (
- possibleID !== null &&
- editor.isEditable() &&
- tableElem.contains(event.target as HTMLElement)
- ) {
- if (isAtEdgeOfTable(event)) {
- setSelected(true);
- setPrimarySelectedCellID(null);
- selectTable();
- return;
- }
- setSelected(false);
- if (isStartingResize(event.target as HTMLElement)) {
- setResizingID(possibleID);
- tableElem.style.userSelect = 'none';
- resizeMeasureRef.current = {
- point: event.clientX,
- size: getTableCellWidth(event.target as HTMLElement),
- };
- return;
- }
- mouseDownRef.current = true;
- if (primarySelectedCellID !== possibleID) {
- if (isEditing) {
- saveEditorToJSON();
- }
- setPrimarySelectedCellID(possibleID);
- setIsEditing(false);
- lastCellIDRef.current = possibleID;
- } else {
- lastCellIDRef.current = null;
- }
- setSelectedCellIDs(NO_CELLS);
- } else if (
- primarySelectedCellID !== null &&
- !isTargetOnPossibleUIControl(event.target as HTMLElement)
- ) {
- setSelected(false);
- mouseDownRef.current = false;
- if (isEditing) {
- saveEditorToJSON();
- }
- setPrimarySelectedCellID(null);
- setSelectedCellIDs(NO_CELLS);
- setIsEditing(false);
- lastCellIDRef.current = null;
- }
- };
-
- const tableRect = tableElem.getBoundingClientRect();
-
- const handlePointerMove = (event: PointerEvent) => {
- if (resizingID !== null) {
- const tableResizerRulerElem = tableResizerRulerRef.current;
- if (tableResizerRulerElem !== null) {
- const {size, point} = resizeMeasureRef.current;
- const diff = event.clientX - point;
- const newWidth = size + diff;
- let x = event.clientX - tableRect.x;
- if (x < 10) {
- x = 10;
- } else if (x > tableRect.width - 10) {
- x = tableRect.width - 10;
- } else if (newWidth < 20) {
- x = point - size + 20 - tableRect.x;
- }
- tableResizerRulerElem.style.left = `${x}px`;
- }
- return;
- }
- if (!isEditing) {
- const {clientX, clientY} = event;
- const {width, x, y, height} = tableRect;
- const isOnRightEdge =
- clientX > x + width * 0.9 &&
- clientX < x + width + 40 &&
- !mouseDownRef.current;
- setShowAddColumns(isOnRightEdge);
- const isOnBottomEdge =
- event.target === addRowsRef.current ||
- (clientY > y + height * 0.85 &&
- clientY < y + height + 5 &&
- !mouseDownRef.current);
- setShowAddRows(isOnBottomEdge);
- }
- if (
- isEditing ||
- !mouseDownRef.current ||
- primarySelectedCellID === null
- ) {
- return;
- }
- const possibleID = getCellID(event.target as HTMLElement);
- if (possibleID !== null && possibleID !== lastCellIDRef.current) {
- if (selectedCellIDs.length === 0) {
- tableElem.style.userSelect = 'none';
- }
- const selectedIDs = getSelectedIDs(
- rows,
- primarySelectedCellID,
- possibleID,
- cellCoordMap,
- );
- if (selectedIDs.length === 1) {
- setSelectedCellIDs(NO_CELLS);
- } else {
- setSelectedCellIDs(selectedIDs);
- }
- lastCellIDRef.current = possibleID;
- }
- };
-
- const handlePointerUp = (event: PointerEvent) => {
- if (resizingID !== null) {
- const {size, point} = resizeMeasureRef.current;
- const diff = event.clientX - point;
- let newWidth = size + diff;
- if (newWidth < 10) {
- newWidth = 10;
- }
- updateTableNode((tableNode) => {
- const [x] = cellCoordMap.get(resizingID) as [number, number];
- $addUpdateTag('history-push');
- tableNode.updateColumnWidth(x, newWidth);
- });
- setResizingID(null);
- }
- if (
- tableElem !== null &&
- selectedCellIDs.length > 1 &&
- mouseDownRef.current
- ) {
- tableElem.style.userSelect = 'text';
- window.getSelection()?.removeAllRanges();
- }
- mouseDownRef.current = false;
- };
-
- doc.addEventListener('pointerdown', handlePointerDown);
- doc.addEventListener('pointermove', handlePointerMove);
- doc.addEventListener('pointerup', handlePointerUp);
-
- return () => {
- doc.removeEventListener('pointerdown', handlePointerDown);
- doc.removeEventListener('pointermove', handlePointerMove);
- doc.removeEventListener('pointerup', handlePointerUp);
- };
- }, [
- cellEditor,
- editor,
- isEditing,
- rows,
- saveEditorToJSON,
- primarySelectedCellID,
- selectedCellSet,
- selectedCellIDs,
- cellCoordMap,
- resizingID,
- updateTableNode,
- setSelected,
- selectTable,
- ]);
-
- useEffect(() => {
- if (!isEditing && primarySelectedCellID !== null) {
- const doc = getCurrentDocument(editor);
-
- const loadContentIntoCell = (cell: Cell | null) => {
- if (cell !== null && cellEditor !== null) {
- const editorStateJSON = cell.json;
- const editorState = cellEditor.parseEditorState(editorStateJSON);
- cellEditor.setEditorState(editorState);
- }
- };
-
- const handleDblClick = (event: MouseEvent) => {
- const possibleID = getCellID(event.target as HTMLElement);
- if (possibleID === primarySelectedCellID && editor.isEditable()) {
- const cell = getCell(rows, possibleID, cellCoordMap);
- loadContentIntoCell(cell);
- setIsEditing(true);
- setSelectedCellIDs(NO_CELLS);
- }
- };
-
- const handleKeyDown = (event: KeyboardEvent) => {
- // Ignore arrow keys, escape or tab
- const keyCode = event.keyCode;
- if (
- keyCode === 16 ||
- keyCode === 27 ||
- keyCode === 9 ||
- keyCode === 37 ||
- keyCode === 38 ||
- keyCode === 39 ||
- keyCode === 40 ||
- keyCode === 8 ||
- keyCode === 46 ||
- !editor.isEditable()
- ) {
- return;
- }
- if (keyCode === 13) {
- event.preventDefault();
- }
- if (
- !isEditing &&
- primarySelectedCellID !== null &&
- editor.getEditorState().read(() => $getSelection() === null) &&
- (event.target as HTMLElement).contentEditable !== 'true'
- ) {
- if (isCopy(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) {
- editor.dispatchCommand(COPY_COMMAND, event);
- return;
- }
- if (isCut(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) {
- editor.dispatchCommand(CUT_COMMAND, event);
- return;
- }
- if (isPaste(keyCode, event.shiftKey, event.metaKey, event.ctrlKey)) {
- editor.dispatchCommand(PASTE_COMMAND, event);
- return;
- }
- }
- if (event.metaKey || event.ctrlKey || event.altKey) {
- return;
- }
- const cell = getCell(rows, primarySelectedCellID, cellCoordMap);
- loadContentIntoCell(cell);
- setIsEditing(true);
- setSelectedCellIDs(NO_CELLS);
- };
-
- doc.addEventListener('dblclick', handleDblClick);
- doc.addEventListener('keydown', handleKeyDown);
-
- return () => {
- doc.removeEventListener('dblclick', handleDblClick);
- doc.removeEventListener('keydown', handleKeyDown);
- };
- }
- }, [
- cellEditor,
- editor,
- isEditing,
- rows,
- primarySelectedCellID,
- cellCoordMap,
- ]);
-
- const updateCellsByID = useCallback(
- (ids: Array, fn: () => void) => {
- $updateCells(rows, ids, cellCoordMap, cellEditor, updateTableNode, fn);
- },
- [cellCoordMap, cellEditor, rows, updateTableNode],
- );
-
- const clearCellsCommand = useCallback((): boolean => {
- if (primarySelectedCellID !== null && !isEditing) {
- updateCellsByID([primarySelectedCellID, ...selectedCellIDs], () => {
- const root = $getRoot();
- root.clear();
- root.append($createParagraphNode());
- });
- return true;
- } else if (isSelected) {
- updateTableNode((tableNode) => {
- $addUpdateTag('history-push');
- tableNode.selectNext();
- tableNode.remove();
- });
- }
- return false;
- }, [
- isEditing,
- isSelected,
- primarySelectedCellID,
- selectedCellIDs,
- updateCellsByID,
- updateTableNode,
- ]);
-
- useEffect(() => {
- const tableElem = tableRef.current;
- if (tableElem === null) {
- return;
- }
-
- const copyDataToClipboard = (
- event: ClipboardEvent,
- htmlString: string,
- lexicalString: string,
- plainTextString: string,
- ) => {
- const clipboardData =
- event instanceof KeyboardEvent ? null : event.clipboardData;
- event.preventDefault();
-
- if (clipboardData != null) {
- clipboardData.setData('text/html', htmlString);
- clipboardData.setData('text/plain', plainTextString);
- clipboardData.setData('application/x-lexical-editor', lexicalString);
- } else {
- const clipboard = navigator.clipboard;
- if (clipboard != null) {
- // Most browsers only support a single item in the clipboard at one time.
- // So we optimize by only putting in HTML.
- const data = [
- new ClipboardItem({
- 'text/html': new Blob([htmlString as BlobPart], {
- type: 'text/html',
- }),
- }),
- ];
- clipboard.write(data);
- }
- }
- };
-
- const getTypeFromObject = async (
- clipboardData: DataTransfer | ClipboardItem,
- type: string,
- ): Promise => {
- try {
- return clipboardData instanceof DataTransfer
- ? clipboardData.getData(type)
- : clipboardData instanceof ClipboardItem
- ? await (await clipboardData.getType(type)).text()
- : '';
- } catch {
- return '';
- }
- };
-
- const pasteContent = async (event: ClipboardEvent) => {
- let clipboardData: null | DataTransfer | ClipboardItem =
- (event instanceof InputEvent ? null : event.clipboardData) || null;
-
- if (primarySelectedCellID !== null && cellEditor !== null) {
- event.preventDefault();
-
- if (clipboardData === null) {
- try {
- const items = await navigator.clipboard.read();
- clipboardData = items[0];
- } catch {
- // NO-OP
- }
- }
- const lexicalString =
- clipboardData !== null
- ? await getTypeFromObject(
- clipboardData,
- 'application/x-lexical-editor',
- )
- : '';
-
- if (lexicalString) {
- try {
- const payload = JSON.parse(lexicalString);
- if (
- payload.namespace === editor._config.namespace &&
- Array.isArray(payload.nodes)
- ) {
- $updateCells(
- rows,
- [primarySelectedCellID],
- cellCoordMap,
- cellEditor,
- updateTableNode,
- () => {
- const root = $getRoot();
- root.clear();
- root.append($createParagraphNode());
- root.selectEnd();
- const nodes = $generateNodesFromSerializedNodes(
- payload.nodes,
- );
- const sel = $getSelection();
- if ($isRangeSelection(sel)) {
- $insertGeneratedNodes(cellEditor, nodes, sel);
- }
- },
- );
- return;
- }
- // eslint-disable-next-line no-empty
- } catch {}
- }
- const htmlString =
- clipboardData !== null
- ? await getTypeFromObject(clipboardData, 'text/html')
- : '';
-
- if (htmlString) {
- try {
- const parser = new DOMParser();
- const dom = parser.parseFromString(htmlString, 'text/html');
- const possibleTableElement = dom.querySelector('table');
-
- if (possibleTableElement != null) {
- const pasteRows = extractRowsFromHTML(possibleTableElement);
- updateTableNode((tableNode) => {
- const [x, y] = cellCoordMap.get(primarySelectedCellID) as [
- number,
- number,
- ];
- $addUpdateTag('history-push');
- tableNode.mergeRows(x, y, pasteRows);
- });
- return;
- }
- $updateCells(
- rows,
- [primarySelectedCellID],
- cellCoordMap,
- cellEditor,
- updateTableNode,
- () => {
- const root = $getRoot();
- root.clear();
- root.append($createParagraphNode());
- root.selectEnd();
- const nodes = $generateNodesFromDOM(editor, dom);
- const sel = $getSelection();
- if ($isRangeSelection(sel)) {
- $insertGeneratedNodes(cellEditor, nodes, sel);
- }
- },
- );
- return;
- // eslint-disable-next-line no-empty
- } catch {}
- }
-
- // Multi-line plain text in rich text mode pasted as separate paragraphs
- // instead of single paragraph with linebreaks.
- const text =
- clipboardData !== null
- ? await getTypeFromObject(clipboardData, 'text/plain')
- : '';
-
- if (text != null) {
- $updateCells(
- rows,
- [primarySelectedCellID],
- cellCoordMap,
- cellEditor,
- updateTableNode,
- () => {
- const root = $getRoot();
- root.clear();
- root.selectEnd();
- const sel = $getSelection();
- if (sel !== null) {
- sel.insertRawText(text);
- }
- },
- );
- }
- }
- };
-
- const copyPrimaryCell = (event: ClipboardEvent) => {
- if (primarySelectedCellID !== null && cellEditor !== null) {
- const cell = getCell(rows, primarySelectedCellID, cellCoordMap) as Cell;
- const json = cell.json;
- const htmlString = cellHTMLCache.get(json) || null;
- if (htmlString === null) {
- return;
- }
- const editorState = cellEditor.parseEditorState(json);
- const plainTextString = editorState.read(() =>
- $getRoot().getTextContent(),
- );
- const lexicalString = editorState.read(() => {
- return JSON.stringify(
- $generateJSONFromSelectedNodes(cellEditor, null),
- );
- });
-
- copyDataToClipboard(event, htmlString, lexicalString, plainTextString);
- }
- };
-
- const copyCellRange = (event: ClipboardEvent) => {
- const lastCellID = lastCellIDRef.current;
- if (
- primarySelectedCellID !== null &&
- cellEditor !== null &&
- lastCellID !== null
- ) {
- const rect = getSelectedRect(
- primarySelectedCellID,
- lastCellID,
- cellCoordMap,
- );
- if (rect === null) {
- return;
- }
- const dom = exportTableCellsToHTML(rows, rect);
- const htmlString = dom.outerHTML;
- const plainTextString = dom.outerText;
- const tableNodeJSON = editor.getEditorState().read(() => {
- const tableNode = $getNodeByKey(nodeKey) as TableNode;
- return tableNode.exportJSON();
- });
- tableNodeJSON.rows = extractCellsFromRows(rows, rect);
- const lexicalJSON = {
- namespace: cellEditor._config.namespace,
- nodes: [tableNodeJSON],
- };
- const lexicalString = JSON.stringify(lexicalJSON);
- copyDataToClipboard(event, htmlString, lexicalString, plainTextString);
- }
- };
-
- const handlePaste = (
- event: ClipboardEvent,
- activeEditor: LexicalEditor,
- ) => {
- const selection = $getSelection();
- if (
- primarySelectedCellID !== null &&
- !isEditing &&
- selection === null &&
- activeEditor === editor
- ) {
- pasteContent(event);
- mouseDownRef.current = false;
- setSelectedCellIDs(NO_CELLS);
- return true;
- }
- return false;
- };
-
- const handleCopy = (event: ClipboardEvent, activeEditor: LexicalEditor) => {
- const selection = $getSelection();
- if (
- primarySelectedCellID !== null &&
- !isEditing &&
- selection === null &&
- activeEditor === editor
- ) {
- if (selectedCellIDs.length === 0) {
- copyPrimaryCell(event);
- } else {
- copyCellRange(event);
- }
- return true;
- }
- return false;
- };
-
- return mergeRegister(
- editor.registerCommand(
- CLICK_COMMAND,
- (payload) => {
- const selection = $getSelection();
- if ($isNodeSelection(selection)) {
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- PASTE_COMMAND,
- handlePaste,
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- COPY_COMMAND,
- handleCopy,
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- CUT_COMMAND,
- (event: ClipboardEvent, activeEditor) => {
- if (handleCopy(event, activeEditor)) {
- clearCellsCommand();
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_BACKSPACE_COMMAND,
- clearCellsCommand,
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_DELETE_COMMAND,
- clearCellsCommand,
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- FORMAT_TEXT_COMMAND,
- (payload) => {
- if (primarySelectedCellID !== null && !isEditing) {
- $updateCells(
- rows,
- [primarySelectedCellID, ...selectedCellIDs],
- cellCoordMap,
- cellEditor,
- updateTableNode,
- () => {
- const sel = $createSelectAll();
- sel.formatText(payload);
- },
- );
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ENTER_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (
- primarySelectedCellID === null &&
- !isEditing &&
- $isNodeSelection(selection) &&
- selection.has(nodeKey) &&
- selection.getNodes().length === 1 &&
- targetEditor === editor
- ) {
- const firstCellID = rows[0].cells[0].id;
- setPrimarySelectedCellID(firstCellID);
- focusCell(tableElem, firstCellID);
- event.preventDefault();
- event.stopPropagation();
- clearSelection();
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_TAB_COMMAND,
- (event) => {
- const selection = $getSelection();
- if (
- !isEditing &&
- selection === null &&
- primarySelectedCellID !== null
- ) {
- const isBackward = event.shiftKey;
- const [x, y] = cellCoordMap.get(primarySelectedCellID) as [
- number,
- number,
- ];
- event.preventDefault();
- let nextX = null;
- let nextY = null;
- if (x === 0 && isBackward) {
- if (y !== 0) {
- nextY = y - 1;
- nextX = rows[nextY].cells.length - 1;
- }
- } else if (x === rows[y].cells.length - 1 && !isBackward) {
- if (y !== rows.length - 1) {
- nextY = y + 1;
- nextX = 0;
- }
- } else if (!isBackward) {
- nextX = x + 1;
- nextY = y;
- } else {
- nextX = x - 1;
- nextY = y;
- }
- if (nextX !== null && nextY !== null) {
- modifySelectedCells(nextX, nextY, false);
- return true;
- }
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ARROW_UP_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (!isEditing && selection === null) {
- const extend = event.shiftKey;
- const cellID = extend
- ? lastCellIDRef.current || primarySelectedCellID
- : primarySelectedCellID;
- if (cellID !== null) {
- const [x, y] = cellCoordMap.get(cellID) as [number, number];
- if (y !== 0) {
- modifySelectedCells(x, y - 1, extend);
- return true;
- }
- }
- }
- if (!$isRangeSelection(selection) || targetEditor !== cellEditor) {
- return false;
- }
- if (
- selection.isCollapsed() &&
- selection.anchor
- .getNode()
- .getTopLevelElementOrThrow()
- .getPreviousSibling() === null
- ) {
- event.preventDefault();
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ARROW_DOWN_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (!isEditing && selection === null) {
- const extend = event.shiftKey;
- const cellID = extend
- ? lastCellIDRef.current || primarySelectedCellID
- : primarySelectedCellID;
- if (cellID !== null) {
- const [x, y] = cellCoordMap.get(cellID) as [number, number];
- if (y !== rows.length - 1) {
- modifySelectedCells(x, y + 1, extend);
- return true;
- }
- }
- }
- if (!$isRangeSelection(selection) || targetEditor !== cellEditor) {
- return false;
- }
- if (
- selection.isCollapsed() &&
- selection.anchor
- .getNode()
- .getTopLevelElementOrThrow()
- .getNextSibling() === null
- ) {
- event.preventDefault();
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ARROW_LEFT_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (!isEditing && selection === null) {
- const extend = event.shiftKey;
- const cellID = extend
- ? lastCellIDRef.current || primarySelectedCellID
- : primarySelectedCellID;
- if (cellID !== null) {
- const [x, y] = cellCoordMap.get(cellID) as [number, number];
- if (x !== 0) {
- modifySelectedCells(x - 1, y, extend);
- return true;
- }
- }
- }
- if (!$isRangeSelection(selection) || targetEditor !== cellEditor) {
- return false;
- }
- if (selection.isCollapsed() && selection.anchor.offset === 0) {
- event.preventDefault();
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ARROW_RIGHT_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (!isEditing && selection === null) {
- const extend = event.shiftKey;
- const cellID = extend
- ? lastCellIDRef.current || primarySelectedCellID
- : primarySelectedCellID;
- if (cellID !== null) {
- const [x, y] = cellCoordMap.get(cellID) as [number, number];
- if (x !== rows[y].cells.length - 1) {
- modifySelectedCells(x + 1, y, extend);
- return true;
- }
- }
- }
- if (!$isRangeSelection(selection) || targetEditor !== cellEditor) {
- return false;
- }
- if (selection.isCollapsed()) {
- const anchor = selection.anchor;
- if (
- (anchor.type === 'text' &&
- anchor.offset === anchor.getNode().getTextContentSize()) ||
- (anchor.type === 'element' &&
- anchor.offset === anchor.getNode().getChildrenSize())
- ) {
- event.preventDefault();
- return true;
- }
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- editor.registerCommand(
- KEY_ESCAPE_COMMAND,
- (event, targetEditor) => {
- const selection = $getSelection();
- if (!isEditing && selection === null && targetEditor === editor) {
- setSelected(true);
- setPrimarySelectedCellID(null);
- selectTable();
- return true;
- }
- if (!$isRangeSelection(selection)) {
- return false;
- }
- if (isEditing) {
- saveEditorToJSON();
- setIsEditing(false);
- if (primarySelectedCellID !== null) {
- setTimeout(() => {
- focusCell(tableElem, primarySelectedCellID);
- }, 20);
- }
- return true;
- }
- return false;
- },
- COMMAND_PRIORITY_LOW,
- ),
- );
- }, [
- cellCoordMap,
- cellEditor,
- clearCellsCommand,
- clearSelection,
- editor,
- isEditing,
- modifySelectedCells,
- nodeKey,
- primarySelectedCellID,
- rows,
- saveEditorToJSON,
- selectTable,
- selectedCellIDs,
- setSelected,
- updateTableNode,
- ]);
-
- if (cellEditor === null) {
- return;
- }
-
- return (
-
-
-
- {rows.map((row) => (
-
- {row.cells.map((cell) => {
- const {id} = cell;
- return (
-
- );
- })}
-
- ))}
-
-
- {showAddColumns && (
-
- )}
- {showAddRows && (
-
- )}
- {resizingID !== null && (
-
- )}
-
- );
-}
diff --git a/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx b/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx
index c5044a49b78..19406089056 100644
--- a/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx
+++ b/packages/lexical-playground/src/plugins/ComponentPickerPlugin/index.tsx
@@ -45,7 +45,7 @@ import {INSERT_IMAGE_COMMAND, InsertImageDialog} from '../ImagesPlugin';
import InsertLayoutDialog from '../LayoutPlugin/InsertLayoutDialog';
import {INSERT_PAGE_BREAK} from '../PageBreakPlugin';
import {InsertPollDialog} from '../PollPlugin';
-import {InsertNewTableDialog, InsertTableDialog} from '../TablePlugin';
+import {InsertTableDialog} from '../TablePlugin';
class ComponentPickerOption extends MenuOption {
// What shows up in the editor
@@ -179,14 +179,6 @@ function getBaseOptions(editor: LexicalEditor, showModal: ShowModal) {
)),
}),
- new ComponentPickerOption('Table (Experimental)', {
- icon: ,
- keywords: ['table', 'grid', 'spreadsheet', 'rows', 'columns'],
- onSelect: () =>
- showModal('Insert Table', (onClose) => (
-
- )),
- }),
new ComponentPickerOption('Numbered List', {
icon: ,
keywords: ['numbered list', 'ordered list', 'ol'],
diff --git a/packages/lexical-playground/src/plugins/TablePlugin.tsx b/packages/lexical-playground/src/plugins/TablePlugin.tsx
index e2fbf600337..e53ef957546 100644
--- a/packages/lexical-playground/src/plugins/TablePlugin.tsx
+++ b/packages/lexical-playground/src/plugins/TablePlugin.tsx
@@ -7,7 +7,11 @@
*/
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
-import {INSERT_TABLE_COMMAND} from '@lexical/table';
+import {
+ $createTableNodeWithDimensions,
+ INSERT_TABLE_COMMAND,
+ TableNode,
+} from '@lexical/table';
import {
$insertNodes,
COMMAND_PRIORITY_EDITOR,
@@ -22,7 +26,6 @@ import {createContext, useContext, useEffect, useMemo, useState} from 'react';
import * as React from 'react';
import invariant from 'shared/invariant';
-import {$createTableNodeWithDimensions, TableNode} from '../nodes/TableNode';
import Button from '../ui/Button';
import {DialogActions} from '../ui/Dialog';
import TextInput from '../ui/TextInput';
@@ -143,59 +146,6 @@ export function InsertTableDialog({
);
}
-export function InsertNewTableDialog({
- activeEditor,
- onClose,
-}: {
- activeEditor: LexicalEditor;
- onClose: () => void;
-}): JSX.Element {
- const [rows, setRows] = useState('');
- const [columns, setColumns] = useState('');
- const [isDisabled, setIsDisabled] = useState(true);
-
- useEffect(() => {
- const row = Number(rows);
- const column = Number(columns);
- if (row && row > 0 && row <= 500 && column && column > 0 && column <= 50) {
- setIsDisabled(false);
- } else {
- setIsDisabled(true);
- }
- }, [rows, columns]);
-
- const onClick = () => {
- activeEditor.dispatchCommand(INSERT_NEW_TABLE_COMMAND, {columns, rows});
- onClose();
- };
-
- return (
- <>
-
-
-
-
-
- >
- );
-}
-
export function TablePlugin({
cellEditorConfig,
children,
diff --git a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx
index 91ae43f7206..11d767b7f16 100644
--- a/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx
+++ b/packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx
@@ -96,7 +96,7 @@ import {InsertInlineImageDialog} from '../InlineImagePlugin';
import InsertLayoutDialog from '../LayoutPlugin/InsertLayoutDialog';
import {INSERT_PAGE_BREAK} from '../PageBreakPlugin';
import {InsertPollDialog} from '../PollPlugin';
-import {InsertNewTableDialog, InsertTableDialog} from '../TablePlugin';
+import {InsertTableDialog} from '../TablePlugin';
const blockTypeToBlockName = {
bullet: 'Bulleted List',
@@ -1118,19 +1118,6 @@ export default function ToolbarPlugin({
Table
- {
- showModal('Insert Table', (onClose) => (
-
- ));
- }}
- className="item">
-
- Table (Experimental)
-
{
showModal('Insert Poll', (onClose) => (