import React, { useCallback } from 'react' import { BubbleMenu } from '@tiptap/react/menus' function TableButton({ onClick, active = false, disabled = false, title, children }) { return ( ) } export function TableInsertDialog({ open, rows, cols, withHeaderRow, withHeaderColumn, onRowsChange, onColsChange, onHeaderRowChange, onHeaderColumnChange, onClose, onInsert, }) { if (!open) return null return (
{ if (event.target === event.currentTarget) { onClose?.() } }} role="presentation" >
Table

Insert table

Create a table and edit rows and columns directly in the editor.

) } export default function RichTableControls({ editor }) { const isTableActive = Boolean(editor?.isActive('table')) const canRun = useCallback((commandName) => { if (!editor) return false try { const chain = editor.can().chain().focus() const next = typeof chain[commandName] === 'function' ? chain[commandName]() : null return Boolean(next?.run?.()) } catch { return false } }, [editor]) const runCommand = useCallback((commandName) => { if (!editor) return const chain = editor.chain().focus() if (typeof chain[commandName] !== 'function') return chain[commandName]().run() }, [editor]) const deleteTable = useCallback(() => { if (!editor) return editor.chain().focus().deleteTable().run() }, [editor]) const getActiveTable = useCallback(() => { if (!editor) return null const { state } = editor const { $from } = state.selection for (let depth = $from.depth; depth >= 0; depth -= 1) { const node = $from.node(depth) if (node?.type?.name !== 'table') { continue } return { node, depth, pos: $from.before(depth), } } return null }, [editor]) const moveTable = useCallback((direction) => { if (!editor) return const tableInfo = getActiveTable() if (!tableInfo) return const { state, view } = editor const { doc } = state const tableNode = tableInfo.node const tablePos = tableInfo.pos const tableSize = tableNode.nodeSize let childPos = 1 let previous = null let current = null let next = null for (let index = 0; index < doc.childCount; index += 1) { const child = doc.child(index) if (childPos === tablePos) { current = { node: child, pos: childPos } next = index + 1 < doc.childCount ? { node: doc.child(index + 1), pos: childPos + child.nodeSize } : null break } previous = { node: child, pos: childPos } childPos += child.nodeSize } if (!current) return const tr = state.tr.delete(tablePos, tablePos + tableSize) let insertPos = tablePos if (direction === 'up') { if (!previous) return insertPos = previous.pos } else if (direction === 'down') { if (!next) return insertPos = next.pos + next.node.nodeSize - tableSize } else { return } tr.insert(insertPos, tableNode.type.create(tableNode.attrs, tableNode.content, tableNode.marks)) view.dispatch(tr) editor.chain().focus().setNodeSelection(insertPos).run() }, [editor, getActiveTable]) if (!editor) return null return ( Boolean(bubbleEditor?.isActive('table'))} tippyOptions={{ placement: 'top-start', offset: [0, 12], duration: 100, }} className="rich-table-toolbar" >
Table tools runCommand('addRowBefore')} disabled={!canRun('addRowBefore')} title="Add row before">Row + runCommand('addRowAfter')} disabled={!canRun('addRowAfter')} title="Add row after">Row + runCommand('deleteRow')} disabled={!canRun('deleteRow')} title="Delete row">Del row runCommand('addColumnBefore')} disabled={!canRun('addColumnBefore')} title="Add column before">Col + runCommand('addColumnAfter')} disabled={!canRun('addColumnAfter')} title="Add column after">Col + runCommand('deleteColumn')} disabled={!canRun('deleteColumn')} title="Delete column">Del col runCommand('mergeCells')} disabled={!canRun('mergeCells')} title="Merge selected cells">Merge runCommand('splitCell')} disabled={!canRun('splitCell')} title="Split selected cell">Split runCommand('toggleHeaderRow')} disabled={!canRun('toggleHeaderRow')} active={isTableActive} title="Toggle header row">Header row runCommand('toggleHeaderColumn')} disabled={!canRun('toggleHeaderColumn')} active={isTableActive} title="Toggle header column">Header col moveTable('up')} disabled={!getActiveTable()} title="Move table up">Move up moveTable('down')} disabled={!getActiveTable()} title="Move table down">Move down Delete table
) }