I have a working implementation now that prevents nested tables and wanted to share it here. I would be curious what others think and if you see room for improvement:
new Plugin({
key: new PluginKey('preventNestedTables'),
// Prevent nested table (low level protection): filter out transactions that lead to nested table
filterTransaction: (transaction) => {
if (!transaction.docChanged) {
return true
}
let hasNestedTable = false
transaction.doc.descendants((node, pos) => {
if (node.type.name.startsWith('table')) {
const $pos = transaction.doc.resolve(pos)
for (let depth = $pos.depth; depth >= 0; depth--) {
const ancestor = $pos.node(depth)
if (ancestor.type.name === 'tableCell') {
console.warn(
'Detected nested table, filtering out transaction',
)
hasNestedTable = true
}
}
}
})
return !hasNestedTable
},
props: {
// Prevent nested table when pasting to a table cell
transformPasted: (slice) => {
if (!this.editor.isActive(this.type.name)) {
return slice
}
let tablePaste = false
slice.content.descendants((node) => {
if (node.type.name.startsWith('table')) {
tablePaste = true
}
})
if (!tablePaste) {
return slice
}
if (
slice.content.childCount === 1
&& slice.content.firstChild?.type.name === 'table'
) {
const tableChild = slice.content.firstChild.firstChild
if (
(tableChild.childCount === 1
&& tableChild.type.name === 'tableRow')
|| tableChild.type.name === 'tableHeadRow'
) {
const rowChild = tableChild.firstChild
if (
(rowChild.childCount === 1
&& rowChild.type.name === 'tableCell')
|| rowChild.type.name === 'tableHeader'
) {
return new Slice(rowChild.content, 0, 0)
}
}
}
console.warn('Nested tables are not supported')
alert(
t(
'text',
'A table was pasted into a table. Nested tables are not supported.',
),
)
const newSlice = new Slice(Fragment.empty, 0, 0)
return newSlice
},
},
})