From 006207b04b41536acd676c4c35047ca07a74b72b Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 21 Dec 2025 00:27:50 -0500 Subject: [PATCH] confs --- src/components/blog/TextEditor.tsx | 227 ++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 8 deletions(-) diff --git a/src/components/blog/TextEditor.tsx b/src/components/blog/TextEditor.tsx index 5ae8d1b..9d80ae1 100644 --- a/src/components/blog/TextEditor.tsx +++ b/src/components/blog/TextEditor.tsx @@ -354,15 +354,12 @@ export default function TextEditor(props: TextEditorProps) { const instance = editor(); if (!instance) return; - console.log("Inserting table:", rows, "x", cols); - instance .chain() .focus() .insertTable({ rows, cols, withHeaderRow: true }) .run(); - console.log("Table inserted, isActive:", instance.isActive("table")); setShowTableMenu(false); }; @@ -375,6 +372,120 @@ export default function TextEditor(props: TextEditorProps) { setShowTableMenu(!showTableMenu()); }; + const deleteTableWithConfirmation = () => { + const instance = editor(); + if (!instance) return; + + const confirmed = window.confirm( + "Are you sure you want to delete this table?" + ); + if (!confirmed) return; + + instance.chain().focus().deleteTable().run(); + }; + + const deleteRowWithConfirmation = () => { + const instance = editor(); + if (!instance) return; + + const { state } = instance; + const { selection } = state; + + // Find the row node + let rowNode = null; + let depth = 0; + for (let d = selection.$anchor.depth; d > 0; d--) { + const node = selection.$anchor.node(d); + if (node.type.name === "tableRow") { + rowNode = node; + depth = d; + break; + } + } + + if (rowNode) { + let hasContent = false; + rowNode.descendants((node) => { + if (node.textContent.trim().length > 0) { + hasContent = true; + return false; + } + }); + + if (hasContent) { + const confirmed = window.confirm( + "This row contains content. Are you sure you want to delete it?" + ); + if (!confirmed) return; + } + } + + instance.chain().focus().deleteRow().run(); + }; + + const deleteColumnWithConfirmation = () => { + const instance = editor(); + if (!instance) return; + + const { state } = instance; + const { selection } = state; + + // Get the current cell position + const cellPos = selection.$anchor; + + // Find table and column index + let tableNode = null; + let tableDepth = 0; + for (let d = cellPos.depth; d > 0; d--) { + const node = cellPos.node(d); + if (node.type.name === "table") { + tableNode = node; + tableDepth = d; + break; + } + } + + if (tableNode) { + // Find which column we're in + let colIndex = 0; + const cellNode = cellPos.node(cellPos.depth); + const rowNode = cellPos.node(cellPos.depth - 1); + + rowNode.forEach((node, offset, index) => { + if ( + cellPos.pos >= cellPos.start(cellPos.depth - 1) + offset && + cellPos.pos < + cellPos.start(cellPos.depth - 1) + offset + node.nodeSize + ) { + colIndex = index; + } + }); + + // Check if this column has content + let hasContent = false; + tableNode.descendants((node, pos, parent) => { + if (parent && parent.type.name === "tableRow") { + let currentCol = 0; + parent.forEach((cell, offset, index) => { + if (index === colIndex && cell.textContent.trim().length > 0) { + hasContent = true; + return false; + } + }); + } + }); + + if (hasContent) { + const confirmed = window.confirm( + "This column contains content. Are you sure you want to delete it?" + ); + if (!confirmed) return; + } + } + + instance.chain().focus().deleteColumn().run(); + }; + // Close language selector on outside click createEffect(() => { if (showLanguageSelector()) { @@ -600,6 +711,108 @@ export default function TextEditor(props: TextEditorProps) { > Code + + {/* Table controls in bubble menu */} + +
+ + + + + + + +
+ + + + + + + +
+ + + + + + +
@@ -858,9 +1071,7 @@ export default function TextEditor(props: TextEditorProps) {