"use client"; import { useDraggable, useDropLine } from "@platejs/dnd"; import { BlockSelectionPlugin, useBlockSelected } from "@platejs/selection/react"; import { TablePlugin, TableProvider, useTableCellElement, useTableCellElementResizable, useTableElement, useTableMergeState, } from "@platejs/table/react"; import { PopoverAnchor } from "@radix-ui/react-popover"; import { cva } from "class-variance-authority"; import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, CombineIcon, GripVertical, SquareSplitHorizontalIcon, Trash2Icon, XIcon, } from "lucide-react"; import { KEYS, PathApi, type TElement, type TTableCellElement, type TTableElement, type TTableRowElement, } from "platejs"; import { PlateElement, type PlateElementProps, useComposedRef, useEditorPlugin, useEditorRef, useEditorSelector, useElement, useElementSelector, useFocusedLast, usePluginOption, useReadOnly, useRemoveNodeButton, useSelected, withHOC, } from "platejs/react"; import type * as React from "react"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; import { blockSelectionVariants } from "./block-selection"; import { ResizeHandle } from "./resize-handle"; import { Toolbar, ToolbarButton, ToolbarGroup } from "./toolbar"; export const TableElement = withHOC( TableProvider, function TableElement({ children, ...props }: PlateElementProps) { const readOnly = useReadOnly(); const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible"); const hasControls = !readOnly && !isSelectionAreaVisible; const { isSelectingCell, marginLeft, props: tableProps } = useTableElement(); const isSelectingTable = useBlockSelected(props.element.id as string); const content = (
{children}
{isSelectingTable &&
}
); if (readOnly) { return content; } return {content}; } ); function TableFloatingToolbar({ children, ...props }: React.ComponentProps) { const { tf } = useEditorPlugin(TablePlugin); const selected = useSelected(); const element = useElement(); const { props: buttonProps } = useRemoveNodeButton({ element }); const collapsedInside = useEditorSelector( (editor) => selected && editor.api.isCollapsed(), [selected] ); const isFocusedLast = useFocusedLast(); const { canMerge, canSplit } = useTableMergeState(); return ( {children} e.preventDefault()} contentEditable={false} {...props} > {canMerge && ( tf.table.merge()} onMouseDown={(e) => e.preventDefault()} tooltip="Merge cells" > )} {canSplit && ( tf.table.split()} onMouseDown={(e) => e.preventDefault()} tooltip="Split cell" > )} {collapsedInside && ( )} {collapsedInside && ( { tf.insert.tableRow({ before: true }); }} onMouseDown={(e) => e.preventDefault()} tooltip="Insert row before" > { tf.insert.tableRow(); }} onMouseDown={(e) => e.preventDefault()} tooltip="Insert row after" > { tf.remove.tableRow(); }} onMouseDown={(e) => e.preventDefault()} tooltip="Delete row" > )} {collapsedInside && ( { tf.insert.tableColumn({ before: true }); }} onMouseDown={(e) => e.preventDefault()} tooltip="Insert column before" > { tf.insert.tableColumn(); }} onMouseDown={(e) => e.preventDefault()} tooltip="Insert column after" > { tf.remove.tableColumn(); }} onMouseDown={(e) => e.preventDefault()} tooltip="Delete column" > )} ); } export function TableRowElement({ children, ...props }: PlateElementProps) { const { element } = props; const readOnly = useReadOnly(); const selected = useSelected(); const editor = useEditorRef(); const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible"); const hasControls = !readOnly && !isSelectionAreaVisible; const { isDragging, nodeRef, previewRef, handleRef } = useDraggable({ element, type: element.type, canDropNode: ({ dragEntry, dropEntry }) => PathApi.equals(PathApi.parent(dragEntry[1]), PathApi.parent(dropEntry[1])), onDropHandler: (_, { dragItem }) => { const dragElement = (dragItem as { element: TElement }).element; if (dragElement) { editor.tf.select(dragElement); } }, }); return ( {hasControls && ( )} {children} ); } function RowDragHandle({ dragRef }: { dragRef: React.Ref }) { const editor = useEditorRef(); const element = useElement(); return ( ); } function RowDropLine() { const { dropLine } = useDropLine(); if (!dropLine) return null; return (
); } export function TableCellElement({ isHeader, ...props }: PlateElementProps & { isHeader?: boolean; }) { const { api } = useEditorPlugin(TablePlugin); const readOnly = useReadOnly(); const element = props.element; const tableId = useElementSelector(([node]) => node.id as string, [], { key: KEYS.table, }); const rowId = useElementSelector(([node]) => node.id as string, [], { key: KEYS.tr, }); const isSelectingTable = useBlockSelected(tableId); const isSelectingRow = useBlockSelected(rowId) || isSelectingTable; const isSelectionAreaVisible = usePluginOption(BlockSelectionPlugin, "isSelectionAreaVisible"); const { borders, colIndex, colSpan, minHeight, rowIndex, selected, width } = useTableCellElement(); const { bottomProps, hiddenLeft, leftProps, rightProps } = useTableCellElementResizable({ colIndex, colSpan, rowIndex, }); return (
{props.children}
{!isSelectionAreaVisible && (
{!readOnly && ( <> {!hiddenLeft && ( )}