diff --git a/web/src/components/TableEditorDialog.tsx b/web/src/components/TableEditorDialog.tsx index 31af74205..d12394bd6 100644 --- a/web/src/components/TableEditorDialog.tsx +++ b/web/src/components/TableEditorDialog.tsx @@ -12,7 +12,6 @@ import { VisuallyHidden } from "./ui/visually-hidden"; // Constants // --------------------------------------------------------------------------- -/** Monospace font stack for the cell inputs. */ const MONO_FONT = "'Fira Code', 'Fira Mono', 'JetBrains Mono', 'Cascadia Code', 'Consolas', ui-monospace, monospace"; // --------------------------------------------------------------------------- @@ -22,9 +21,7 @@ const MONO_FONT = "'Fira Code', 'Fira Mono', 'JetBrains Mono', 'Cascadia Code', interface TableEditorDialogProps { open: boolean; onOpenChange: (open: boolean) => void; - /** Initial table data when editing an existing table. */ initialData?: TableData | null; - /** Called with the formatted markdown table string on confirm. */ onConfirm: (markdown: string) => void; } @@ -40,7 +37,6 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table const [alignments, setAlignments] = useState([]); const [sortState, setSortState] = useState(null); - // Ref grid for Tab navigation: inputRefs[row][col] (row -1 = headers). const inputRefs = useRef>(new Map()); const setInputRef = useCallback((key: string, el: HTMLInputElement | null) => { @@ -51,7 +47,6 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table } }, []); - // Initialize state when dialog opens. useEffect(() => { if (open) { if (initialData) { @@ -89,7 +84,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table }); }; - // ---- Add / Remove ---- + // ---- Add / Remove / Insert ---- const addColumn = () => { setHeaders((prev) => [...prev, ""]); @@ -98,6 +93,13 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table setSortState(null); }; + const insertColumnAt = (index: number) => { + setHeaders((prev) => [...prev.slice(0, index), "", ...prev.slice(index)]); + setRows((prev) => prev.map((r) => [...r.slice(0, index), "", ...r.slice(index)])); + setAlignments((prev) => [...prev.slice(0, index), "none" as ColumnAlignment, ...prev.slice(index)]); + setSortState(null); + }; + const removeColumn = (col: number) => { if (colCount <= 1) return; setHeaders((prev) => prev.filter((_, i) => i !== col)); @@ -110,6 +112,10 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table setRows((prev) => [...prev, Array.from({ length: colCount }, () => "")]); }; + const insertRowAt = (index: number) => { + setRows((prev) => [...prev.slice(0, index), Array.from({ length: colCount }, () => ""), ...prev.slice(index)]); + }; + const removeRow = (row: number) => { if (rowCount <= 1) return; setRows((prev) => prev.filter((_, i) => i !== row)); @@ -206,14 +212,44 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table Edit table headers, rows, columns and sort data
- {/* Scrollable table area — grows to fill */} + {/* Scrollable table area */}
+ {/* Insert-column buttons row (above the table) */} +
+ {/* We position "+" buttons at each column border using the same grid layout */} +
+ {/* Offset for row-number column */} +
+ {headers.map((_, col) => ( +
+ {/* "+" button on the left edge of each column (= between col-1 and col) */} + {col > 0 && ( +
+ + + + + Insert column + +
+ )} +
+ ))} +
+
+ - {/* Header row */} - + {/* Sticky header */} + {/* Row number column */} - ))} - {/* Add column button */} + {/* Add column at end */} + {/* Data rows */} {rows.map((row, rowIdx) => ( - - {/* Row number + remove */} - + {/* Row number */} + + + {/* Data cells */} {row.map((cell, col) => ( - ))} - ))}
+ {headers.map((header, col) => (
@@ -255,7 +291,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table
@@ -272,32 +308,19 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table
-
- {rowIdx + 1} - {rowCount > 1 && ( - - - - - Remove row - - )} -
+
+ {rowIdx + 1} + setInputRef(`${rowIdx}:${col}`, el)} style={{ fontFamily: MONO_FONT }} @@ -308,30 +331,61 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table value={cell} onChange={(e) => updateCell(rowIdx, col, e.target.value)} onKeyDown={(e) => handleKeyDown(e, rowIdx, col)} - placeholder="..." /> + {/* Insert-row button: shown on the top border between rows */} + {rowIdx > 0 && col === 0 && ( +
+ + + + + Insert row + +
+ )}
+ + {/* Row delete button (end of row) */} + + {rowCount > 1 && ( + + + + + Remove row + + )} +
+
- {/* Add row button */} -
+ {/* Footer */} +
+
+ + {colCount} {colCount === 1 ? "column" : "columns"} · {rowCount} {rowCount === 1 ? "row" : "rows"} +
-
- - {/* Footer */} -
- - {colCount} {colCount === 1 ? "column" : "columns"} · {rowCount} {rowCount === 1 ? "row" : "rows"} -