@underverse-ui/underverse 1.0.71 → 1.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20337,46 +20337,52 @@ function DataTableHeader({
20337
20337
  const titleContent = /* @__PURE__ */ jsxs53("div", { className: "flex items-center gap-1", children: [
20338
20338
  /* @__PURE__ */ jsx63("span", { className: cn("font-medium whitespace-nowrap select-text", headerTitleClass), children: col.title }),
20339
20339
  col.sortable && /* @__PURE__ */ jsx63(
20340
- "button",
20340
+ Tooltip,
20341
20341
  {
20342
- className: cn(
20343
- "p-1 rounded-lg transition-all duration-200 hover:bg-accent",
20344
- sort?.key === col.key ? "opacity-100 bg-accent" : "opacity-60 hover:opacity-100"
20345
- ),
20346
- onClick: () => {
20347
- setCurPage(1);
20348
- setSort((current) => {
20349
- if (!current || current.key !== col.key) return { key: col.key, order: "asc" };
20350
- if (current.order === "asc") return { key: col.key, order: "desc" };
20351
- return null;
20352
- });
20353
- },
20354
- "aria-label": "Sort",
20355
- title: `Sort by ${String(col.title)}`,
20356
- children: /* @__PURE__ */ jsxs53("svg", { viewBox: "0 0 20 20", fill: "none", className: cn("inline-block", sortIconClass), children: [
20357
- /* @__PURE__ */ jsx63(
20358
- "path",
20359
- {
20360
- d: "M7 8l3-3 3 3",
20361
- stroke: "currentColor",
20362
- strokeWidth: "1.5",
20363
- strokeLinecap: "round",
20364
- strokeLinejoin: "round",
20365
- opacity: sort?.key === col.key && sort.order === "asc" ? 1 : 0.4
20366
- }
20367
- ),
20368
- /* @__PURE__ */ jsx63(
20369
- "path",
20370
- {
20371
- d: "M7 12l3 3 3-3",
20372
- stroke: "currentColor",
20373
- strokeWidth: "1.5",
20374
- strokeLinecap: "round",
20375
- strokeLinejoin: "round",
20376
- opacity: sort?.key === col.key && sort.order === "desc" ? 1 : 0.4
20377
- }
20378
- )
20379
- ] })
20342
+ placement: "top",
20343
+ content: /* @__PURE__ */ jsx63("span", { className: "text-xs font-medium", children: `Sort by ${String(col.title)}` }),
20344
+ children: /* @__PURE__ */ jsx63(
20345
+ "button",
20346
+ {
20347
+ className: cn(
20348
+ "p-1 rounded-lg transition-all duration-200 hover:bg-accent",
20349
+ sort?.key === col.key ? "opacity-100 bg-accent" : "opacity-60 hover:opacity-100"
20350
+ ),
20351
+ onClick: () => {
20352
+ setCurPage(1);
20353
+ setSort((current) => {
20354
+ if (!current || current.key !== col.key) return { key: col.key, order: "asc" };
20355
+ if (current.order === "asc") return { key: col.key, order: "desc" };
20356
+ return null;
20357
+ });
20358
+ },
20359
+ "aria-label": "Sort",
20360
+ children: /* @__PURE__ */ jsxs53("svg", { viewBox: "0 0 20 20", fill: "none", className: cn("inline-block", sortIconClass), children: [
20361
+ /* @__PURE__ */ jsx63(
20362
+ "path",
20363
+ {
20364
+ d: "M7 8l3-3 3 3",
20365
+ stroke: "currentColor",
20366
+ strokeWidth: "1.5",
20367
+ strokeLinecap: "round",
20368
+ strokeLinejoin: "round",
20369
+ opacity: sort?.key === col.key && sort.order === "asc" ? 1 : 0.4
20370
+ }
20371
+ ),
20372
+ /* @__PURE__ */ jsx63(
20373
+ "path",
20374
+ {
20375
+ d: "M7 12l3 3 3-3",
20376
+ stroke: "currentColor",
20377
+ strokeWidth: "1.5",
20378
+ strokeLinecap: "round",
20379
+ strokeLinejoin: "round",
20380
+ opacity: sort?.key === col.key && sort.order === "desc" ? 1 : 0.4
20381
+ }
20382
+ )
20383
+ ] })
20384
+ }
20385
+ )
20380
20386
  }
20381
20387
  )
20382
20388
  ] });
@@ -20384,18 +20390,24 @@ function DataTableHeader({
20384
20390
  Popover,
20385
20391
  {
20386
20392
  placement: "bottom-start",
20387
- trigger: /* @__PURE__ */ jsx63(
20388
- "button",
20393
+ trigger: /* @__PURE__ */ jsx63("span", { className: "inline-flex", children: /* @__PURE__ */ jsx63(
20394
+ Tooltip,
20389
20395
  {
20390
- className: cn(
20391
- "p-1.5 rounded-lg transition-all duration-200 hover:bg-accent",
20392
- filters[col.key] ? "bg-accent text-primary" : "text-muted-foreground"
20393
- ),
20394
- "aria-label": "Filter",
20395
- title: `Filter by ${String(col.title)}`,
20396
- children: /* @__PURE__ */ jsx63(FilterIcon, { className: "w-4 h-4" })
20396
+ placement: "top",
20397
+ content: /* @__PURE__ */ jsx63("span", { className: "text-xs font-medium", children: `Filter by ${String(col.title)}` }),
20398
+ children: /* @__PURE__ */ jsx63(
20399
+ "button",
20400
+ {
20401
+ className: cn(
20402
+ "p-1.5 rounded-lg transition-all duration-200 hover:bg-accent",
20403
+ filters[col.key] ? "bg-accent text-primary" : "text-muted-foreground"
20404
+ ),
20405
+ "aria-label": "Filter",
20406
+ children: /* @__PURE__ */ jsx63(FilterIcon, { className: "w-4 h-4" })
20407
+ }
20408
+ )
20397
20409
  }
20398
- ),
20410
+ ) }),
20399
20411
  children: /* @__PURE__ */ jsxs53("div", { className: "p-3 w-64 space-y-3", children: [
20400
20412
  /* @__PURE__ */ jsxs53("div", { className: "flex items-center justify-between", children: [
20401
20413
  /* @__PURE__ */ jsx63("div", { className: "text-sm font-medium", children: col.title }),
@@ -28946,6 +28958,19 @@ import {
28946
28958
  import { Fragment as Fragment27, jsx as jsx81, jsxs as jsxs71 } from "react/jsx-runtime";
28947
28959
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
28948
28960
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
28961
+ var MENU_HOVER_PADDING = 18;
28962
+ var ROW_HANDLE_HOVER_WIDTH = 28;
28963
+ var COLUMN_HANDLE_HOVER_HEIGHT = 28;
28964
+ var ADD_COLUMN_HOVER_WIDTH = 24;
28965
+ var ADD_ROW_HOVER_HEIGHT = 24;
28966
+ var HANDLE_HOVER_RADIUS = 14;
28967
+ var DEFAULT_HOVER_STATE = {
28968
+ menuVisible: false,
28969
+ addColumnVisible: false,
28970
+ addRowVisible: false,
28971
+ rowHandleIndex: null,
28972
+ columnHandleIndex: null
28973
+ };
28949
28974
  function resolveElement(target) {
28950
28975
  if (target instanceof Element) return target;
28951
28976
  if (target instanceof Node) return target.parentElement;
@@ -29071,6 +29096,12 @@ function buildLayout(editor, surface, cell) {
29071
29096
  };
29072
29097
  }
29073
29098
  function getSelectedCell(editor) {
29099
+ const browserSelection = window.getSelection();
29100
+ const anchorElement = resolveElement(browserSelection?.anchorNode ?? null);
29101
+ const anchorCell = anchorElement?.closest?.("th,td");
29102
+ if (anchorCell instanceof HTMLTableCellElement) {
29103
+ return anchorCell;
29104
+ }
29074
29105
  const domAtPos = editor.view.domAtPos(editor.state.selection.from);
29075
29106
  return getCellFromTarget(domAtPos.node);
29076
29107
  }
@@ -29099,6 +29130,8 @@ function TableControls({ editor, containerRef }) {
29099
29130
  const t = useSmartTranslations("UEditor");
29100
29131
  const [layout, setLayout] = React74.useState(null);
29101
29132
  const [dragPreview, setDragPreview] = React74.useState(null);
29133
+ const [hoverState, setHoverState] = React74.useState(DEFAULT_HOVER_STATE);
29134
+ const [openMenuKey, setOpenMenuKey] = React74.useState(null);
29102
29135
  const layoutRef = React74.useRef(null);
29103
29136
  const dragStateRef = React74.useRef(null);
29104
29137
  React74.useEffect(() => {
@@ -29130,17 +29163,58 @@ function TableControls({ editor, containerRef }) {
29130
29163
  setDragPreview(null);
29131
29164
  document.body.style.cursor = "";
29132
29165
  }, []);
29166
+ const updateHoverState = React74.useCallback((event) => {
29167
+ const activeLayout = layoutRef.current;
29168
+ const surface = containerRef.current;
29169
+ if (!activeLayout || !surface || dragStateRef.current) {
29170
+ setHoverState(DEFAULT_HOVER_STATE);
29171
+ return;
29172
+ }
29173
+ const surfaceRect = surface.getBoundingClientRect();
29174
+ const relativeX = event.clientX - surfaceRect.left + surface.scrollLeft;
29175
+ const relativeY = event.clientY - surfaceRect.top + surface.scrollTop;
29176
+ const targetElement = resolveElement(event.target);
29177
+ const directRowHandle = targetElement?.closest?.("[data-row-handle-index]");
29178
+ const directColumnHandle = targetElement?.closest?.("[data-column-handle-index]");
29179
+ const directTableMenu = targetElement?.closest?.("[data-table-control='table-menu']");
29180
+ const directAddColumn = targetElement?.closest?.("[data-table-control='add-column']");
29181
+ const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
29182
+ const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
29183
+ const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
29184
+ const rowHandleIndex = Number.isFinite(directRowHandleIndex) ? directRowHandleIndex : activeLayout.rowHandles.find((rowHandle) => relativeX >= activeLayout.tableLeft - ROW_HANDLE_HOVER_WIDTH && relativeX <= activeLayout.tableLeft && Math.abs(relativeY - rowHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
29185
+ const columnHandleIndex = Number.isFinite(directColumnHandleIndex) ? directColumnHandleIndex : activeLayout.columnHandles.find((columnHandle) => relativeY >= activeLayout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= activeLayout.tableTop && Math.abs(relativeX - columnHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
29186
+ const menuVisible = Boolean(directTableMenu) || relativeX >= activeLayout.tableLeft - MENU_HOVER_PADDING && relativeX <= activeLayout.tableLeft + 42 && relativeY >= activeLayout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= activeLayout.tableTop + MENU_HOVER_PADDING;
29187
+ const addColumnVisible = Boolean(directAddColumn) || relativeX >= activeLayout.tableLeft + activeLayout.tableWidth && relativeX <= activeLayout.tableLeft + activeLayout.tableWidth + ADD_COLUMN_HOVER_WIDTH && relativeY >= activeLayout.tableTop && relativeY <= activeLayout.tableTop + activeLayout.tableHeight;
29188
+ const addRowVisible = Boolean(directAddRow) || relativeY >= activeLayout.tableTop + activeLayout.tableHeight && relativeY <= activeLayout.tableTop + activeLayout.tableHeight + ADD_ROW_HOVER_HEIGHT && relativeX >= activeLayout.tableLeft && relativeX <= activeLayout.tableLeft + activeLayout.tableWidth;
29189
+ setHoverState((prev) => {
29190
+ if (prev.menuVisible === menuVisible && prev.addColumnVisible === addColumnVisible && prev.addRowVisible === addRowVisible && prev.rowHandleIndex === rowHandleIndex && prev.columnHandleIndex === columnHandleIndex) {
29191
+ return prev;
29192
+ }
29193
+ return {
29194
+ menuVisible,
29195
+ addColumnVisible,
29196
+ addRowVisible,
29197
+ rowHandleIndex,
29198
+ columnHandleIndex
29199
+ };
29200
+ });
29201
+ }, [containerRef]);
29133
29202
  React74.useEffect(() => {
29134
29203
  const proseMirror = editor.view.dom;
29135
29204
  const surface = containerRef.current;
29136
29205
  if (!surface) return void 0;
29137
29206
  const handleMouseOver = (event) => {
29138
29207
  if (dragStateRef.current) return;
29139
- syncFromCell(getCellFromTarget(event.target));
29208
+ const cell = getCellFromTarget(event.target);
29209
+ if (!cell) return;
29210
+ syncFromCell(cell);
29211
+ };
29212
+ const handleSurfaceMouseMove = (event) => {
29213
+ updateHoverState(event);
29140
29214
  };
29141
29215
  const handleMouseLeave = () => {
29142
29216
  if (dragStateRef.current) return;
29143
- syncFromSelection();
29217
+ setHoverState(DEFAULT_HOVER_STATE);
29144
29218
  };
29145
29219
  const handleFocusIn = () => {
29146
29220
  if (dragStateRef.current) return;
@@ -29149,6 +29223,8 @@ function TableControls({ editor, containerRef }) {
29149
29223
  proseMirror.addEventListener("mouseover", handleMouseOver);
29150
29224
  proseMirror.addEventListener("mouseleave", handleMouseLeave);
29151
29225
  proseMirror.addEventListener("focusin", handleFocusIn);
29226
+ surface.addEventListener("mouseover", handleSurfaceMouseMove);
29227
+ surface.addEventListener("mousemove", handleSurfaceMouseMove);
29152
29228
  surface.addEventListener("scroll", refreshCurrentLayout, { passive: true });
29153
29229
  window.addEventListener("resize", refreshCurrentLayout);
29154
29230
  editor.on("selectionUpdate", syncFromSelection);
@@ -29158,12 +29234,14 @@ function TableControls({ editor, containerRef }) {
29158
29234
  proseMirror.removeEventListener("mouseover", handleMouseOver);
29159
29235
  proseMirror.removeEventListener("mouseleave", handleMouseLeave);
29160
29236
  proseMirror.removeEventListener("focusin", handleFocusIn);
29237
+ surface.removeEventListener("mouseover", handleSurfaceMouseMove);
29238
+ surface.removeEventListener("mousemove", handleSurfaceMouseMove);
29161
29239
  surface.removeEventListener("scroll", refreshCurrentLayout);
29162
29240
  window.removeEventListener("resize", refreshCurrentLayout);
29163
29241
  editor.off("selectionUpdate", syncFromSelection);
29164
29242
  editor.off("update", refreshCurrentLayout);
29165
29243
  };
29166
- }, [clearDrag, containerRef, editor, refreshCurrentLayout, syncFromCell, syncFromSelection]);
29244
+ }, [clearDrag, containerRef, editor, refreshCurrentLayout, syncFromCell, syncFromSelection, updateHoverState]);
29167
29245
  const runAtCellPos = React74.useCallback((cellPos, command, options) => {
29168
29246
  if (cellPos == null) return false;
29169
29247
  focusCell(editor, cellPos);
@@ -29253,6 +29331,10 @@ function TableControls({ editor, containerRef }) {
29253
29331
  return true;
29254
29332
  }, [runAtCornerCell, syncFromSelection]);
29255
29333
  const canExpandTable = Boolean(layout);
29334
+ const controlsVisible = dragPreview !== null;
29335
+ const tableMenuOpen = openMenuKey === "table";
29336
+ const getRowMenuKey = React74.useCallback((index) => `row:${index}`, []);
29337
+ const getColumnMenuKey = React74.useCallback((index) => `column:${index}`, []);
29256
29338
  React74.useEffect(() => {
29257
29339
  const handleMouseMove = (event) => {
29258
29340
  const dragState = dragStateRef.current;
@@ -29464,115 +29546,176 @@ function TableControls({ editor, containerRef }) {
29464
29546
  const expandPreviewHeight = dragPreview?.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
29465
29547
  const dragStatusText = dragPreview?.kind === "row" ? `${t("tableMenu.dragRow")} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview?.kind === "column" ? `${t("tableMenu.dragColumn")} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview?.kind === "add-row" ? `+${dragPreview.previewRows}R` : dragPreview?.kind === "add-column" ? `+${dragPreview.previewCols}C` : null;
29466
29548
  return /* @__PURE__ */ jsxs71(Fragment27, { children: [
29467
- layout.rowHandles.map((rowHandle) => /* @__PURE__ */ jsx81("div", { className: "absolute z-30", style: { top: Math.max(8, rowHandle.center - 12), left: rowHandleLeft }, children: /* @__PURE__ */ jsx81(
29468
- DropdownMenu,
29469
- {
29470
- placement: "right",
29471
- items: getRowHandleMenuItems(rowHandle),
29472
- trigger: /* @__PURE__ */ jsx81(
29473
- "button",
29474
- {
29475
- type: "button",
29476
- "aria-label": `${t("tableMenu.dragRow")} ${rowHandle.index + 1}`,
29477
- title: `${t("tableMenu.dragRow")} ${rowHandle.index + 1}`,
29478
- onMouseDown: (event) => {
29479
- event.preventDefault();
29480
- event.stopPropagation();
29481
- dragStateRef.current = {
29482
- kind: "row",
29483
- originIndex: rowHandle.index,
29484
- targetIndex: rowHandle.index,
29485
- anchorPos: rowHandle.cellPos
29486
- };
29487
- setDragPreview({
29488
- kind: "row",
29489
- originIndex: rowHandle.index,
29490
- targetIndex: rowHandle.index,
29491
- targetStart: rowHandle.start,
29492
- targetSize: rowHandle.size
29493
- });
29494
- document.body.style.cursor = "grabbing";
29495
- },
29496
- className: cn(
29497
- "inline-flex h-6 w-6 items-center justify-center rounded-full",
29498
- "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29499
- "transition-colors hover:bg-accent hover:text-foreground"
29500
- ),
29501
- children: /* @__PURE__ */ jsx81(GripVertical3, { className: "h-3.5 w-3.5" })
29502
- }
29503
- )
29504
- }
29505
- ) }, `row-handle-${rowHandle.index}`)),
29506
- layout.columnHandles.map((columnHandle) => /* @__PURE__ */ jsx81("div", { className: "absolute z-30", style: { top: columnHandleTop, left: Math.max(8, columnHandle.center - 12) }, children: /* @__PURE__ */ jsx81(
29507
- DropdownMenu,
29549
+ layout.rowHandles.map((rowHandle) => {
29550
+ const menuKey = getRowMenuKey(rowHandle.index);
29551
+ const visible = controlsVisible || hoverState.rowHandleIndex === rowHandle.index || openMenuKey === menuKey;
29552
+ if (!visible) return null;
29553
+ return /* @__PURE__ */ jsx81(
29554
+ "div",
29555
+ {
29556
+ className: "absolute z-30",
29557
+ "data-row-handle-index": rowHandle.index,
29558
+ style: {
29559
+ top: Math.max(8, rowHandle.center - 12),
29560
+ left: rowHandleLeft
29561
+ },
29562
+ children: /* @__PURE__ */ jsx81(
29563
+ DropdownMenu,
29564
+ {
29565
+ placement: "right",
29566
+ isOpen: openMenuKey === menuKey,
29567
+ onOpenChange: (open) => {
29568
+ setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
29569
+ },
29570
+ items: getRowHandleMenuItems(rowHandle),
29571
+ trigger: /* @__PURE__ */ jsx81(
29572
+ "button",
29573
+ {
29574
+ type: "button",
29575
+ "aria-label": `${t("tableMenu.dragRow")} ${rowHandle.index + 1}`,
29576
+ title: `${t("tableMenu.dragRow")} ${rowHandle.index + 1}`,
29577
+ onMouseDown: (event) => {
29578
+ event.preventDefault();
29579
+ event.stopPropagation();
29580
+ setOpenMenuKey(null);
29581
+ dragStateRef.current = {
29582
+ kind: "row",
29583
+ originIndex: rowHandle.index,
29584
+ targetIndex: rowHandle.index,
29585
+ anchorPos: rowHandle.cellPos
29586
+ };
29587
+ setDragPreview({
29588
+ kind: "row",
29589
+ originIndex: rowHandle.index,
29590
+ targetIndex: rowHandle.index,
29591
+ targetStart: rowHandle.start,
29592
+ targetSize: rowHandle.size
29593
+ });
29594
+ document.body.style.cursor = "grabbing";
29595
+ },
29596
+ className: cn(
29597
+ "inline-flex h-6 w-6 items-center justify-center rounded-full",
29598
+ "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29599
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground"
29600
+ ),
29601
+ children: /* @__PURE__ */ jsx81(GripVertical3, { className: "h-3.5 w-3.5" })
29602
+ }
29603
+ )
29604
+ }
29605
+ )
29606
+ },
29607
+ `row-handle-${rowHandle.index}`
29608
+ );
29609
+ }),
29610
+ layout.columnHandles.map((columnHandle) => {
29611
+ const menuKey = getColumnMenuKey(columnHandle.index);
29612
+ const visible = controlsVisible || hoverState.columnHandleIndex === columnHandle.index || openMenuKey === menuKey;
29613
+ if (!visible) return null;
29614
+ return /* @__PURE__ */ jsx81(
29615
+ "div",
29616
+ {
29617
+ className: "absolute z-30",
29618
+ "data-column-handle-index": columnHandle.index,
29619
+ style: {
29620
+ top: columnHandleTop,
29621
+ left: Math.max(8, columnHandle.center - 12)
29622
+ },
29623
+ children: /* @__PURE__ */ jsx81(
29624
+ DropdownMenu,
29625
+ {
29626
+ placement: "bottom-start",
29627
+ isOpen: openMenuKey === menuKey,
29628
+ onOpenChange: (open) => {
29629
+ setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
29630
+ },
29631
+ items: getColumnHandleMenuItems(columnHandle),
29632
+ trigger: /* @__PURE__ */ jsx81(
29633
+ "button",
29634
+ {
29635
+ type: "button",
29636
+ "aria-label": `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}`,
29637
+ title: `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}`,
29638
+ onMouseDown: (event) => {
29639
+ event.preventDefault();
29640
+ event.stopPropagation();
29641
+ setOpenMenuKey(null);
29642
+ dragStateRef.current = {
29643
+ kind: "column",
29644
+ originIndex: columnHandle.index,
29645
+ targetIndex: columnHandle.index,
29646
+ anchorPos: columnHandle.cellPos
29647
+ };
29648
+ setDragPreview({
29649
+ kind: "column",
29650
+ originIndex: columnHandle.index,
29651
+ targetIndex: columnHandle.index,
29652
+ targetStart: columnHandle.start,
29653
+ targetSize: columnHandle.size
29654
+ });
29655
+ document.body.style.cursor = "grabbing";
29656
+ },
29657
+ className: cn(
29658
+ "inline-flex h-6 w-6 items-center justify-center rounded-full",
29659
+ "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29660
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground"
29661
+ ),
29662
+ children: /* @__PURE__ */ jsx81(GripHorizontal, { className: "h-3.5 w-3.5" })
29663
+ }
29664
+ )
29665
+ }
29666
+ )
29667
+ },
29668
+ `column-handle-${columnHandle.index}`
29669
+ );
29670
+ }),
29671
+ (controlsVisible || hoverState.menuVisible || tableMenuOpen) && /* @__PURE__ */ jsx81(
29672
+ "div",
29508
29673
  {
29509
- placement: "bottom-start",
29510
- items: getColumnHandleMenuItems(columnHandle),
29511
- trigger: /* @__PURE__ */ jsx81(
29512
- "button",
29674
+ className: "absolute z-30",
29675
+ "data-table-control": "table-menu",
29676
+ style: {
29677
+ top: menuTop,
29678
+ left: menuLeft
29679
+ },
29680
+ children: /* @__PURE__ */ jsx81(
29681
+ DropdownMenu,
29513
29682
  {
29514
- type: "button",
29515
- "aria-label": `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}`,
29516
- title: `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}`,
29517
- onMouseDown: (event) => {
29518
- event.preventDefault();
29519
- event.stopPropagation();
29520
- dragStateRef.current = {
29521
- kind: "column",
29522
- originIndex: columnHandle.index,
29523
- targetIndex: columnHandle.index,
29524
- anchorPos: columnHandle.cellPos
29525
- };
29526
- setDragPreview({
29527
- kind: "column",
29528
- originIndex: columnHandle.index,
29529
- targetIndex: columnHandle.index,
29530
- targetStart: columnHandle.start,
29531
- targetSize: columnHandle.size
29532
- });
29533
- document.body.style.cursor = "grabbing";
29683
+ placement: "bottom-start",
29684
+ isOpen: tableMenuOpen,
29685
+ onOpenChange: (open) => {
29686
+ setOpenMenuKey((prev) => open ? "table" : prev === "table" ? null : prev);
29534
29687
  },
29535
- className: cn(
29536
- "inline-flex h-6 w-6 items-center justify-center rounded-full",
29537
- "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29538
- "transition-colors hover:bg-accent hover:text-foreground"
29539
- ),
29540
- children: /* @__PURE__ */ jsx81(GripHorizontal, { className: "h-3.5 w-3.5" })
29541
- }
29542
- )
29543
- }
29544
- ) }, `column-handle-${columnHandle.index}`)),
29545
- /* @__PURE__ */ jsx81("div", { className: "pointer-events-none absolute z-30", style: { top: menuTop, left: menuLeft }, children: /* @__PURE__ */ jsx81(
29546
- DropdownMenu,
29547
- {
29548
- placement: "bottom-start",
29549
- items: menuItems,
29550
- trigger: /* @__PURE__ */ jsx81(
29551
- "button",
29552
- {
29553
- type: "button",
29554
- "aria-label": t("tableMenu.openControls"),
29555
- title: t("tableMenu.openControls"),
29556
- onMouseDown: (event) => event.preventDefault(),
29557
- className: cn(
29558
- "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-full",
29559
- "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29560
- "transition-colors hover:bg-accent hover:text-foreground"
29561
- ),
29562
- children: /* @__PURE__ */ jsx81(MoreHorizontal2, { className: "h-4 w-4" })
29688
+ items: menuItems,
29689
+ trigger: /* @__PURE__ */ jsx81(
29690
+ "button",
29691
+ {
29692
+ type: "button",
29693
+ "aria-label": t("tableMenu.openControls"),
29694
+ title: t("tableMenu.openControls"),
29695
+ onMouseDown: (event) => event.preventDefault(),
29696
+ className: cn(
29697
+ "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-full",
29698
+ "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
29699
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground"
29700
+ ),
29701
+ children: /* @__PURE__ */ jsx81(MoreHorizontal2, { className: "h-4 w-4" })
29702
+ }
29703
+ )
29563
29704
  }
29564
29705
  )
29565
29706
  }
29566
- ) }),
29567
- /* @__PURE__ */ jsx81(
29707
+ ),
29708
+ (controlsVisible || hoverState.addColumnVisible) && /* @__PURE__ */ jsx81(
29568
29709
  "button",
29569
29710
  {
29570
29711
  type: "button",
29712
+ "data-table-control": "add-column",
29571
29713
  "aria-label": t("tableMenu.quickAddColumnAfter"),
29572
29714
  title: t("tableMenu.quickAddColumnAfter"),
29573
29715
  onMouseDown: (event) => {
29574
29716
  event.preventDefault();
29575
29717
  event.stopPropagation();
29718
+ setOpenMenuKey(null);
29576
29719
  if (!canExpandTable) return;
29577
29720
  dragStateRef.current = { kind: "add-column", previewCols: 1 };
29578
29721
  setDragPreview({ kind: "add-column", previewCols: 1 });
@@ -29582,21 +29725,28 @@ function TableControls({ editor, containerRef }) {
29582
29725
  className: cn(
29583
29726
  "absolute z-30 inline-flex items-center justify-center rounded-md",
29584
29727
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
29585
- "transition-colors hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
29728
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
29586
29729
  ),
29587
- style: { top: columnRailTop, left: columnRailLeft, width: 18, height: layout.tableHeight },
29730
+ style: {
29731
+ top: columnRailTop,
29732
+ left: columnRailLeft,
29733
+ width: 18,
29734
+ height: layout.tableHeight
29735
+ },
29588
29736
  children: /* @__PURE__ */ jsx81("span", { className: "text-sm font-medium leading-none", children: "+" })
29589
29737
  }
29590
29738
  ),
29591
- /* @__PURE__ */ jsx81(
29739
+ (controlsVisible || hoverState.addRowVisible) && /* @__PURE__ */ jsx81(
29592
29740
  "button",
29593
29741
  {
29594
29742
  type: "button",
29743
+ "data-table-control": "add-row",
29595
29744
  "aria-label": t("tableMenu.quickAddRowAfter"),
29596
29745
  title: t("tableMenu.quickAddRowAfter"),
29597
29746
  onMouseDown: (event) => {
29598
29747
  event.preventDefault();
29599
29748
  event.stopPropagation();
29749
+ setOpenMenuKey(null);
29600
29750
  if (!canExpandTable) return;
29601
29751
  dragStateRef.current = { kind: "add-row", previewRows: 1 };
29602
29752
  setDragPreview({ kind: "add-row", previewRows: 1 });
@@ -29606,9 +29756,14 @@ function TableControls({ editor, containerRef }) {
29606
29756
  className: cn(
29607
29757
  "absolute z-30 inline-flex items-center justify-center rounded-md",
29608
29758
  "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
29609
- "transition-colors hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
29759
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
29610
29760
  ),
29611
- style: { top: rowRailTop, left: rowRailLeft, width: layout.tableWidth, height: 16 },
29761
+ style: {
29762
+ top: rowRailTop,
29763
+ left: rowRailLeft,
29764
+ width: layout.tableWidth,
29765
+ height: 16
29766
+ },
29612
29767
  children: /* @__PURE__ */ jsx81("span", { className: "text-sm font-medium leading-none", children: "+" })
29613
29768
  }
29614
29769
  ),
@@ -29743,6 +29898,19 @@ function resolveEventElement(target) {
29743
29898
  if (target instanceof Node) return target.parentElement;
29744
29899
  return null;
29745
29900
  }
29901
+ function getSelectionTableCell(view) {
29902
+ const browserSelection = window.getSelection();
29903
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
29904
+ const anchorCell = anchorElement?.closest?.("th,td");
29905
+ if (anchorCell instanceof HTMLElement) {
29906
+ return anchorCell;
29907
+ }
29908
+ const { from } = view.state.selection;
29909
+ const domAtPos = view.domAtPos(from);
29910
+ const element = resolveEventElement(domAtPos.node);
29911
+ const cell = element?.closest?.("th,td");
29912
+ return cell instanceof HTMLElement ? cell : null;
29913
+ }
29746
29914
  function isRowResizeHotspot(cell, clientX, clientY) {
29747
29915
  const rect = cell.getBoundingClientRect();
29748
29916
  const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
@@ -29769,6 +29937,16 @@ function getRelativeBoundaryMetrics(surface, table, row, cell) {
29769
29937
  columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
29770
29938
  };
29771
29939
  }
29940
+ function getRelativeCellMetrics(surface, cell) {
29941
+ const surfaceRect = surface.getBoundingClientRect();
29942
+ const cellRect = cell.getBoundingClientRect();
29943
+ return {
29944
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
29945
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
29946
+ width: cellRect.width,
29947
+ height: cellRect.height
29948
+ };
29949
+ }
29772
29950
  var UEditor = React75.forwardRef(({
29773
29951
  content = "",
29774
29952
  onChange,
@@ -29796,6 +29974,9 @@ var UEditor = React75.forwardRef(({
29796
29974
  const editorContentRef = useRef32(null);
29797
29975
  const tableColumnGuideRef = useRef32(null);
29798
29976
  const tableRowGuideRef = useRef32(null);
29977
+ const activeTableCellHighlightRef = useRef32(null);
29978
+ const hoveredTableCellRef = useRef32(null);
29979
+ const activeTableCellRef = useRef32(null);
29799
29980
  const rowResizeStateRef = useRef32(null);
29800
29981
  const setEditorResizeCursor = React75.useCallback((cursor) => {
29801
29982
  const proseMirror = editorContentRef.current?.querySelector(".ProseMirror");
@@ -29822,6 +30003,36 @@ var UEditor = React75.forwardRef(({
29822
30003
  hideColumnGuide();
29823
30004
  hideRowGuide();
29824
30005
  }, [hideColumnGuide, hideRowGuide, setEditorResizeCursor]);
30006
+ const updateActiveCellHighlight = React75.useCallback((cell) => {
30007
+ const surface = editorContentRef.current;
30008
+ const highlight = activeTableCellHighlightRef.current;
30009
+ if (!highlight) return;
30010
+ if (!surface || !cell) {
30011
+ highlight.style.display = "none";
30012
+ return;
30013
+ }
30014
+ const metrics = getRelativeCellMetrics(surface, cell);
30015
+ highlight.style.display = "block";
30016
+ highlight.style.left = `${metrics.left}px`;
30017
+ highlight.style.top = `${metrics.top}px`;
30018
+ highlight.style.width = `${metrics.width}px`;
30019
+ highlight.style.height = `${metrics.height}px`;
30020
+ }, []);
30021
+ const setActiveTableCell = React75.useCallback((cell) => {
30022
+ if (activeTableCellRef.current === cell) return;
30023
+ activeTableCellRef.current = cell;
30024
+ updateActiveCellHighlight(activeTableCellRef.current);
30025
+ }, [updateActiveCellHighlight]);
30026
+ const clearActiveTableCell = React75.useCallback(() => {
30027
+ activeTableCellRef.current = null;
30028
+ updateActiveCellHighlight(null);
30029
+ }, [updateActiveCellHighlight]);
30030
+ const setHoveredTableCell = React75.useCallback((cell) => {
30031
+ hoveredTableCellRef.current = cell;
30032
+ }, []);
30033
+ const clearHoveredTableCell = React75.useCallback(() => {
30034
+ hoveredTableCellRef.current = null;
30035
+ }, []);
29825
30036
  const showColumnGuide = React75.useCallback((table, row, cell) => {
29826
30037
  const surface = editorContentRef.current;
29827
30038
  const guide = tableColumnGuideRef.current;
@@ -29927,8 +30138,13 @@ var UEditor = React75.forwardRef(({
29927
30138
  "[&_.selectedCell]:after:pointer-events-none",
29928
30139
  "[&_.column-resize-handle]:pointer-events-auto",
29929
30140
  "[&_.column-resize-handle]:cursor-col-resize",
30141
+ "[&_.column-resize-handle]:absolute",
30142
+ "[&_.column-resize-handle]:top-[-1px]",
30143
+ "[&_.column-resize-handle]:bottom-[-1px]",
30144
+ "[&_.column-resize-handle]:right-[-5px]",
30145
+ "[&_.column-resize-handle]:z-10",
29930
30146
  "[&_.column-resize-handle]:bg-primary/65",
29931
- "[&_.column-resize-handle]:w-2",
30147
+ "[&_.column-resize-handle]:w-2.5",
29932
30148
  "[&_.column-resize-handle]:rounded-full",
29933
30149
  "[&_.column-resize-handle]:opacity-0",
29934
30150
  "[&_.column-resize-handle]:shadow-sm",
@@ -30003,6 +30219,10 @@ var UEditor = React75.forwardRef(({
30003
30219
  onJsonChange?.(editor2.getJSON());
30004
30220
  }
30005
30221
  });
30222
+ const syncActiveTableCellFromSelection = React75.useCallback(() => {
30223
+ if (!editor) return;
30224
+ setActiveTableCell(getSelectionTableCell(editor.view));
30225
+ }, [editor, setActiveTableCell]);
30006
30226
  useImperativeHandle3(
30007
30227
  ref,
30008
30228
  () => ({
@@ -30035,9 +30255,27 @@ var UEditor = React75.forwardRef(({
30035
30255
  useEffect35(() => {
30036
30256
  if (!editor) return void 0;
30037
30257
  const proseMirror = editor.view.dom;
30258
+ const surface = editorContentRef.current;
30259
+ let selectionSyncTimeoutId = 0;
30260
+ const scheduleActiveCellSync = (fallbackCell = null) => {
30261
+ requestAnimationFrame(() => {
30262
+ setActiveTableCell(getSelectionTableCell(editor.view) ?? fallbackCell);
30263
+ });
30264
+ window.clearTimeout(selectionSyncTimeoutId);
30265
+ selectionSyncTimeoutId = window.setTimeout(() => {
30266
+ setActiveTableCell(getSelectionTableCell(editor.view) ?? fallbackCell);
30267
+ }, 0);
30268
+ };
30269
+ const handleSelectionChange = () => {
30270
+ scheduleActiveCellSync();
30271
+ };
30272
+ const handleActiveCellLayoutChange = () => {
30273
+ updateActiveCellHighlight(activeTableCellRef.current);
30274
+ };
30038
30275
  const handleEditorMouseMove = (event) => {
30039
30276
  const activeRowResize = rowResizeStateRef.current;
30040
30277
  if (activeRowResize) {
30278
+ setHoveredTableCell(activeRowResize.cellElement);
30041
30279
  showRowGuide(activeRowResize.tableElement, activeRowResize.rowElement, activeRowResize.cellElement);
30042
30280
  return;
30043
30281
  }
@@ -30048,12 +30286,15 @@ var UEditor = React75.forwardRef(({
30048
30286
  }
30049
30287
  const cell = target.closest("th,td");
30050
30288
  if (!(cell instanceof HTMLElement)) {
30289
+ clearHoveredTableCell();
30051
30290
  clearAllTableResizeHover();
30052
30291
  return;
30053
30292
  }
30293
+ setHoveredTableCell(cell);
30054
30294
  const row = cell.closest("tr");
30055
30295
  const table = cell.closest("table");
30056
30296
  if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
30297
+ clearHoveredTableCell();
30057
30298
  clearAllTableResizeHover();
30058
30299
  return;
30059
30300
  }
@@ -30072,6 +30313,7 @@ var UEditor = React75.forwardRef(({
30072
30313
  clearAllTableResizeHover();
30073
30314
  };
30074
30315
  const handleEditorMouseLeave = () => {
30316
+ clearHoveredTableCell();
30075
30317
  if (!rowResizeStateRef.current) {
30076
30318
  clearAllTableResizeHover();
30077
30319
  }
@@ -30079,15 +30321,24 @@ var UEditor = React75.forwardRef(({
30079
30321
  const handleEditorMouseDown = (event) => {
30080
30322
  if (event.button !== 0) return;
30081
30323
  const target = resolveEventElement(event.target);
30082
- if (!(target instanceof Element)) return;
30324
+ if (!(target instanceof Element)) {
30325
+ clearActiveTableCell();
30326
+ return;
30327
+ }
30083
30328
  const cell = target.closest("th,td");
30084
- if (!(cell instanceof HTMLTableCellElement)) return;
30329
+ if (!(cell instanceof HTMLTableCellElement)) {
30330
+ clearActiveTableCell();
30331
+ return;
30332
+ }
30333
+ setActiveTableCell(cell);
30334
+ scheduleActiveCellSync(cell);
30085
30335
  const row = cell.closest("tr");
30086
30336
  const table = cell.closest("table");
30087
30337
  if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) return;
30088
30338
  if (!isRowResizeHotspot(cell, event.clientX, event.clientY)) {
30089
30339
  return;
30090
30340
  }
30341
+ setHoveredTableCell(cell);
30091
30342
  const rowInfo = findTableRowNodeInfo(editor.view, row);
30092
30343
  if (!rowInfo) return;
30093
30344
  rowResizeStateRef.current = {
@@ -30148,6 +30399,7 @@ var UEditor = React75.forwardRef(({
30148
30399
  clearPreviewRowHeight(state.rowElement);
30149
30400
  rowResizeStateRef.current = null;
30150
30401
  document.body.style.cursor = "";
30402
+ clearHoveredTableCell();
30151
30403
  clearAllTableResizeHover();
30152
30404
  };
30153
30405
  const handleWindowBlur = () => {
@@ -30156,30 +30408,53 @@ var UEditor = React75.forwardRef(({
30156
30408
  clearPreviewRowHeight(state.rowElement);
30157
30409
  rowResizeStateRef.current = null;
30158
30410
  document.body.style.cursor = "";
30411
+ clearHoveredTableCell();
30159
30412
  clearAllTableResizeHover();
30160
30413
  };
30161
30414
  proseMirror.addEventListener("mousemove", handleEditorMouseMove);
30162
30415
  proseMirror.addEventListener("mouseleave", handleEditorMouseLeave);
30163
30416
  proseMirror.addEventListener("mousedown", handleEditorMouseDown);
30417
+ proseMirror.addEventListener("click", handleSelectionChange);
30418
+ proseMirror.addEventListener("mouseup", handleSelectionChange);
30419
+ proseMirror.addEventListener("keyup", handleSelectionChange);
30420
+ proseMirror.addEventListener("focusin", handleSelectionChange);
30421
+ document.addEventListener("selectionchange", handleSelectionChange);
30422
+ surface?.addEventListener("scroll", handleActiveCellLayoutChange, { passive: true });
30423
+ window.addEventListener("resize", handleActiveCellLayoutChange);
30164
30424
  window.addEventListener("mousemove", handlePointerMove);
30165
30425
  document.addEventListener("pointermove", handlePointerMove);
30166
30426
  window.addEventListener("mouseup", handlePointerUp);
30167
30427
  document.addEventListener("pointerup", handlePointerUp);
30168
30428
  window.addEventListener("blur", handleWindowBlur);
30429
+ editor.on("selectionUpdate", syncActiveTableCellFromSelection);
30430
+ editor.on("focus", syncActiveTableCellFromSelection);
30431
+ syncActiveTableCellFromSelection();
30169
30432
  return () => {
30170
30433
  proseMirror.removeEventListener("mousemove", handleEditorMouseMove);
30171
30434
  proseMirror.removeEventListener("mouseleave", handleEditorMouseLeave);
30172
30435
  proseMirror.removeEventListener("mousedown", handleEditorMouseDown);
30436
+ proseMirror.removeEventListener("click", handleSelectionChange);
30437
+ proseMirror.removeEventListener("mouseup", handleSelectionChange);
30438
+ proseMirror.removeEventListener("keyup", handleSelectionChange);
30439
+ proseMirror.removeEventListener("focusin", handleSelectionChange);
30440
+ document.removeEventListener("selectionchange", handleSelectionChange);
30441
+ surface?.removeEventListener("scroll", handleActiveCellLayoutChange);
30442
+ window.removeEventListener("resize", handleActiveCellLayoutChange);
30173
30443
  window.removeEventListener("mousemove", handlePointerMove);
30174
30444
  document.removeEventListener("pointermove", handlePointerMove);
30175
30445
  window.removeEventListener("mouseup", handlePointerUp);
30176
30446
  document.removeEventListener("pointerup", handlePointerUp);
30177
30447
  window.removeEventListener("blur", handleWindowBlur);
30448
+ editor.off("selectionUpdate", syncActiveTableCellFromSelection);
30449
+ editor.off("focus", syncActiveTableCellFromSelection);
30450
+ window.clearTimeout(selectionSyncTimeoutId);
30178
30451
  document.body.style.cursor = "";
30452
+ clearActiveTableCell();
30453
+ clearHoveredTableCell();
30179
30454
  clearAllTableResizeHover();
30180
30455
  rowResizeStateRef.current = null;
30181
30456
  };
30182
- }, [clearAllTableResizeHover, editor, hideColumnGuide, hideRowGuide, showColumnGuide, showRowGuide]);
30457
+ }, [clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editor, hideColumnGuide, hideRowGuide, setHoveredTableCell, showColumnGuide, showRowGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
30183
30458
  if (!editor) {
30184
30459
  return /* @__PURE__ */ jsx82(
30185
30460
  "div",
@@ -30231,6 +30506,14 @@ var UEditor = React75.forwardRef(({
30231
30506
  className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
30232
30507
  }
30233
30508
  ),
30509
+ /* @__PURE__ */ jsx82(
30510
+ "span",
30511
+ {
30512
+ ref: activeTableCellHighlightRef,
30513
+ "aria-hidden": "true",
30514
+ className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-[#2383e2] bg-[#2383e2]/[0.06] transition-[left,top,width,height] duration-100"
30515
+ }
30516
+ ),
30234
30517
  editable && /* @__PURE__ */ jsx82(TableControls, { editor, containerRef: editorContentRef }),
30235
30518
  /* @__PURE__ */ jsx82(
30236
30519
  EditorContent,