From ac403c60471c4d4676c657ac364429e96e8403a9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 6 Feb 2026 23:56:15 +0000 Subject: [PATCH] feat: refine table editor insert zones, square headers, pointer cursors Insert zone improvements: - Row insert: hover zone now only covers the row-number area (left edge) instead of spanning the full row width, matching the column insert pattern where the zone is between header cells. The + button is positioned at the intersection of the horizontal row border and the vertical first-column border (translate-x-1/2 on the right edge of the row-number cell). - Column insert: blue highlight line now extends through the entire table (header + all data rows) using bottom: -200rem with pointer-events-none so it doesn't block cell interactions. - Row insert: blue highlight line extends across the full table width using width: 200rem with pointer-events-none for the same reason. - Removed the spacer approach for row inserts; zones are now directly inside the row-number with absolute positioning. Visual changes: - Headers are now square (removed rounded-tl-md from header cells and rounded-bl-md from last-row cells). - All buttons have explicit cursor-pointer class (sort, delete column, delete row, add column, add row, insert column, insert row, cancel, confirm, and the + insert buttons). Co-authored-by: milvasic --- web/src/components/TableEditorDialog.tsx | 80 +++++++++++------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/web/src/components/TableEditorDialog.tsx b/web/src/components/TableEditorDialog.tsx index 4e05f2e23..630377245 100644 --- a/web/src/components/TableEditorDialog.tsx +++ b/web/src/components/TableEditorDialog.tsx @@ -1,6 +1,5 @@ import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon, PlusIcon, TrashIcon } from "lucide-react"; import React, { useCallback, useEffect, useRef, useState } from "react"; -import { cn } from "@/lib/utils"; import type { ColumnAlignment, TableData } from "@/utils/markdown-table"; import { createEmptyTable, serializeMarkdownTable } from "@/utils/markdown-table"; import { Button } from "./ui/button"; @@ -122,9 +121,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table const sortByColumn = (col: number) => { let newDir: "asc" | "desc" = "asc"; - if (sortState && sortState.col === col && sortState.dir === "asc") { - newDir = "desc"; - } + if (sortState && sortState.col === col && sortState.dir === "asc") newDir = "desc"; setSortState({ col, dir: newDir }); setRows((prev) => { const sorted = [...prev].sort((a, b) => { @@ -171,16 +168,12 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table inputRefs.current.get(`${row}:${col}`)?.focus(); }; - // ---- Confirm ---- - const handleConfirm = () => { const md = serializeMarkdownTable({ headers, rows, alignments }); onConfirm(md); onOpenChange(false); }; - // ---- Sort indicator ---- - const SortIndicator = ({ col }: { col: number }) => { if (sortState?.col === col) { return sortState.dir === "asc" ? : ; @@ -188,7 +181,6 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table return ; }; - // Total colSpan: row-number col + data cols + action col const totalColSpan = colCount + 2; return ( @@ -210,12 +202,12 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table {/* ============ STICKY HEADER ============ */} - {/* Mask row: solid background strip that hides content scrolling behind the header */} + {/* Mask row: solid background that hides content scrolling behind the header */} - {/* Actual header row */} + {/* Header row */} {/* Row-number spacer */} {rows.map((row, rowIdx) => ( - {/* ---- Insert-row zone (between row rowIdx-1 and rowIdx) ---- */} - {rowIdx > 0 && ( - - + {/* Row number โ€” with insert-row zone on top border */} + - - )} - - {/* ---- Actual data row ---- */} - - {/* Row number */} - @@ -348,10 +339,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table setInputRef(`${rowIdx}:${col}`, el)} style={{ fontFamily: MONO_FONT }} - className={cn( - "w-full px-2 py-1.5 text-sm bg-transparent border border-border focus:outline-none focus:ring-1 focus:ring-primary/40", - rowIdx === rowCount - 1 && "rounded-bl-md", - )} + className="w-full px-2 py-1.5 text-sm bg-transparent border border-border focus:outline-none focus:ring-1 focus:ring-primary/40" value={cell} onChange={(e) => updateCell(rowIdx, col, e.target.value)} onKeyDown={(e) => handleKeyDown(e, rowIdx, col)} @@ -359,14 +347,14 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table ))} - {/* Row delete button (end of row) */} + {/* Row delete button */}
@@ -228,14 +220,17 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table className="group/cins absolute -left-4 top-0 bottom-0 w-8 z-30 flex items-center justify-center cursor-pointer" onClick={() => insertColumnAt(col)} > - {/* Blue vertical highlight line */} -
+ {/* Blue vertical line through the entire table */} +
{/* + button */} @@ -245,8 +240,8 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table
)} - {/* Header cell content โ€” bg extends across input + action buttons */} -
+ {/* Header cell โ€” bg covers input + sort + delete */} +
setInputRef(`-1:${col}`, el)} style={{ fontFamily: MONO_FONT }} @@ -260,7 +255,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table
+
+ {rowIdx > 0 && (
insertRowAt(rowIdx)} > - {/* Blue horizontal highlight line */} -
- {/* + button */} + {/* Blue horizontal line extending across the full table */} +
+ {/* + button at intersection of row border and first-column border */} @@ -331,14 +329,7 @@ const TableEditorDialog = ({ open, onOpenChange, initialData, onConfirm }: Table Insert row
-
+ )} {rowIdx + 1} {rowCount > 1 && (
- - +