@underverse-ui/underverse 1.0.136 → 1.0.137

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
@@ -820,6 +820,8 @@ var en_default = {
820
820
  deleteTable: "Delete Table",
821
821
  toggleHeaderRow: "Toggle Header Row",
822
822
  toggleHeaderColumn: "Toggle Header Column",
823
+ mergeCells: "Merge Cells",
824
+ splitCell: "Split Cell",
823
825
  openControls: "Open Table Controls",
824
826
  quickAddColumnAfter: "Quick Add Column After",
825
827
  quickAddRowAfter: "Quick Add Row After",
@@ -1090,6 +1092,8 @@ var vi_default = {
1090
1092
  deleteTable: "X\xF3a b\u1EA3ng",
1091
1093
  toggleHeaderRow: "B\u1EADt/t\u1EAFt h\xE0ng ti\xEAu \u0111\u1EC1",
1092
1094
  toggleHeaderColumn: "B\u1EADt/t\u1EAFt c\u1ED9t ti\xEAu \u0111\u1EC1",
1095
+ mergeCells: "G\u1ED9p \xF4",
1096
+ splitCell: "T\xE1ch \xF4",
1093
1097
  openControls: "M\u1EDF \u0111i\u1EC1u khi\u1EC3n b\u1EA3ng",
1094
1098
  quickAddColumnAfter: "Th\xEAm nhanh c\u1ED9t sau",
1095
1099
  quickAddRowAfter: "Th\xEAm nhanh h\xE0ng sau",
@@ -1359,6 +1363,8 @@ var ko_default = {
1359
1363
  deleteTable: "\uD45C \uC0AD\uC81C",
1360
1364
  toggleHeaderRow: "\uD5E4\uB354 \uD589 \uC804\uD658",
1361
1365
  toggleHeaderColumn: "\uD5E4\uB354 \uC5F4 \uC804\uD658",
1366
+ mergeCells: "\uC140 \uBCD1\uD569",
1367
+ splitCell: "\uC140 \uBD84\uD560",
1362
1368
  openControls: "\uD45C \uCEE8\uD2B8\uB864 \uC5F4\uAE30",
1363
1369
  quickAddColumnAfter: "\uC624\uB978\uCABD \uC5F4 \uBE60\uB974\uAC8C \uCD94\uAC00",
1364
1370
  quickAddRowAfter: "\uC544\uB798 \uD589 \uBE60\uB974\uAC8C \uCD94\uAC00",
@@ -1628,6 +1634,8 @@ var ja_default = {
1628
1634
  deleteTable: "\u8868\u3092\u524A\u9664",
1629
1635
  toggleHeaderRow: "\u30D8\u30C3\u30C0\u30FC\u884C\u3092\u5207\u308A\u66FF\u3048",
1630
1636
  toggleHeaderColumn: "\u30D8\u30C3\u30C0\u30FC\u5217\u3092\u5207\u308A\u66FF\u3048",
1637
+ mergeCells: "\u30BB\u30EB\u3092\u7D50\u5408",
1638
+ splitCell: "\u30BB\u30EB\u3092\u5206\u5272",
1631
1639
  openControls: "\u8868\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u3092\u958B\u304F",
1632
1640
  quickAddColumnAfter: "\u53F3\u5074\u306B\u5217\u3092\u3059\u3070\u3084\u304F\u8FFD\u52A0",
1633
1641
  quickAddRowAfter: "\u4E0B\u306B\u884C\u3092\u3059\u3070\u3084\u304F\u8FFD\u52A0",
@@ -25352,6 +25360,109 @@ var letter_spacing_default = LetterSpacing;
25352
25360
  import { Table as Table3 } from "@tiptap/extension-table";
25353
25361
  import { Plugin as Plugin3 } from "@tiptap/pm/state";
25354
25362
 
25363
+ // src/components/UEditor/table-dom-utils.ts
25364
+ var MIN_TABLE_ROW_HEIGHT = 36;
25365
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
25366
+ var ROW_RESIZE_LINE_THICKNESS = 2;
25367
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
25368
+ var TABLE_RESIZE_HIT_ZONE = 10;
25369
+ function findTableRowNodeInfo(view, rowElement) {
25370
+ const firstCell = rowElement.querySelector("th,td");
25371
+ if (!firstCell) return null;
25372
+ const cellPos = view.posAtDOM(firstCell, 0);
25373
+ const $pos = view.state.doc.resolve(cellPos);
25374
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
25375
+ const node = $pos.node(depth);
25376
+ if (node.type.name === "tableRow") {
25377
+ return {
25378
+ pos: $pos.before(depth),
25379
+ node
25380
+ };
25381
+ }
25382
+ }
25383
+ return null;
25384
+ }
25385
+ function resolveEventElement(target) {
25386
+ if (target instanceof Element) return target;
25387
+ if (target instanceof Node) return target.parentElement;
25388
+ return null;
25389
+ }
25390
+ function getSelectionTableCell(view) {
25391
+ const browserSelection = window.getSelection();
25392
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
25393
+ const anchorCell = anchorElement?.closest?.("th,td");
25394
+ if (anchorCell instanceof HTMLElement) {
25395
+ return anchorCell;
25396
+ }
25397
+ const { from } = view.state.selection;
25398
+ const domAtPos = view.domAtPos(from);
25399
+ const element = resolveEventElement(domAtPos.node);
25400
+ const cell = element?.closest?.("th,td");
25401
+ return cell instanceof HTMLElement ? cell : null;
25402
+ }
25403
+ function isRowResizeHotspot(cell, clientX, clientY) {
25404
+ const rect = cell.getBoundingClientRect();
25405
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
25406
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
25407
+ return nearBottom && !nearRight;
25408
+ }
25409
+ function isColumnResizeHotspot(cell, clientX, clientY) {
25410
+ const rect = cell.getBoundingClientRect();
25411
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
25412
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
25413
+ return nearRight && !nearBottom;
25414
+ }
25415
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
25416
+ const surfaceRect = surface.getBoundingClientRect();
25417
+ const tableRect = table.getBoundingClientRect();
25418
+ const rowRect = row.getBoundingClientRect();
25419
+ const cellRect = cell.getBoundingClientRect();
25420
+ return {
25421
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
25422
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
25423
+ width: tableRect.width,
25424
+ height: tableRect.height,
25425
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
25426
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
25427
+ };
25428
+ }
25429
+ function getRelativeCellMetrics(surface, cell) {
25430
+ const surfaceRect = surface.getBoundingClientRect();
25431
+ const cellRect = cell.getBoundingClientRect();
25432
+ return {
25433
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
25434
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
25435
+ width: cellRect.width,
25436
+ height: cellRect.height
25437
+ };
25438
+ }
25439
+ function getRelativeSelectedCellsMetrics(surface) {
25440
+ const selectedCells = Array.from(
25441
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
25442
+ );
25443
+ if (selectedCells.length === 0) {
25444
+ return null;
25445
+ }
25446
+ const surfaceRect = surface.getBoundingClientRect();
25447
+ let left = Number.POSITIVE_INFINITY;
25448
+ let top = Number.POSITIVE_INFINITY;
25449
+ let right = Number.NEGATIVE_INFINITY;
25450
+ let bottom = Number.NEGATIVE_INFINITY;
25451
+ selectedCells.forEach((cell) => {
25452
+ const rect = cell.getBoundingClientRect();
25453
+ left = Math.min(left, rect.left);
25454
+ top = Math.min(top, rect.top);
25455
+ right = Math.max(right, rect.right);
25456
+ bottom = Math.max(bottom, rect.bottom);
25457
+ });
25458
+ return {
25459
+ left: left - surfaceRect.left + surface.scrollLeft,
25460
+ top: top - surfaceRect.top + surface.scrollTop,
25461
+ width: right - left,
25462
+ height: bottom - top
25463
+ };
25464
+ }
25465
+
25355
25466
  // src/components/UEditor/table-align-utils.ts
25356
25467
  function findTableNodeInfoAtResolvedPos($pos) {
25357
25468
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
@@ -25379,11 +25490,13 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
25379
25490
  editor.view.dispatch(
25380
25491
  editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
25381
25492
  ...tableInfo.node.attrs,
25382
- tableAlign
25493
+ textAlign: tableAlign
25383
25494
  })
25384
25495
  );
25496
+ const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
25497
+ const domAtTableElement = resolveEventElement(domAtTable);
25385
25498
  const tableDom = editor.view.nodeDOM(tableInfo.pos);
25386
- const tableElement = tableDom instanceof HTMLTableElement ? tableDom : tableDom instanceof HTMLElement ? tableDom.querySelector("table") : null;
25499
+ const tableElement = domAtTableElement?.closest?.("table") ?? (tableDom instanceof HTMLTableElement ? tableDom : tableDom instanceof HTMLElement ? tableDom.querySelector("table") : null) ?? (editor.view.dom.querySelectorAll("table").length === 1 ? editor.view.dom.querySelector("table") : null);
25387
25500
  if (tableElement instanceof HTMLTableElement) {
25388
25501
  if (tableAlign) {
25389
25502
  tableElement.setAttribute("data-table-align", tableAlign);
@@ -25432,25 +25545,30 @@ function renderTableAlignStyle(tableAlign) {
25432
25545
  }
25433
25546
  }
25434
25547
  var UEditorTable = Table3.extend({
25435
- addAttributes() {
25436
- return {
25437
- ...this.parent?.(),
25438
- tableAlign: {
25439
- default: null,
25440
- parseHTML: (element) => {
25441
- if (!(element instanceof HTMLElement)) return null;
25442
- return parseTableAlign(element);
25443
- },
25444
- renderHTML: (attributes) => {
25445
- const tableAlign = normalizeTableAlign(attributes.tableAlign);
25446
- if (!tableAlign) return {};
25447
- return {
25448
- "data-table-align": tableAlign,
25449
- style: renderTableAlignStyle(tableAlign)
25450
- };
25548
+ addGlobalAttributes() {
25549
+ return [
25550
+ ...this.parent?.() ?? [],
25551
+ {
25552
+ types: ["table"],
25553
+ attributes: {
25554
+ textAlign: {
25555
+ default: null,
25556
+ parseHTML: (element) => {
25557
+ if (!(element instanceof HTMLElement)) return null;
25558
+ return parseTableAlign(element);
25559
+ },
25560
+ renderHTML: (attributes) => {
25561
+ const tableAlign = normalizeTableAlign(attributes.textAlign);
25562
+ if (!tableAlign) return {};
25563
+ return {
25564
+ "data-table-align": tableAlign,
25565
+ style: renderTableAlignStyle(tableAlign)
25566
+ };
25567
+ }
25568
+ }
25451
25569
  }
25452
25570
  }
25453
- };
25571
+ ];
25454
25572
  },
25455
25573
  addCommands() {
25456
25574
  return {
@@ -25461,7 +25579,7 @@ var UEditorTable = Table3.extend({
25461
25579
  dispatch?.(
25462
25580
  state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
25463
25581
  ...tableInfo.node.attrs,
25464
- tableAlign
25582
+ textAlign: tableAlign
25465
25583
  })
25466
25584
  );
25467
25585
  return true;
@@ -25472,7 +25590,7 @@ var UEditorTable = Table3.extend({
25472
25590
  dispatch?.(
25473
25591
  state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
25474
25592
  ...tableInfo.node.attrs,
25475
- tableAlign: null
25593
+ textAlign: null
25476
25594
  })
25477
25595
  );
25478
25596
  return true;
@@ -25481,6 +25599,7 @@ var UEditorTable = Table3.extend({
25481
25599
  },
25482
25600
  addProseMirrorPlugins() {
25483
25601
  return [
25602
+ ...this.parent?.() ?? [],
25484
25603
  new Plugin3({
25485
25604
  appendTransaction(_transactions, _oldState, newState) {
25486
25605
  const { doc, schema } = newState;
@@ -26216,6 +26335,24 @@ function getDefaultLetterSpacings() {
26216
26335
 
26217
26336
  // src/components/UEditor/toolbar.tsx
26218
26337
  import { Fragment as Fragment27, jsx as jsx81, jsxs as jsxs67 } from "react/jsx-runtime";
26338
+ function getTableAnchorPos(editor) {
26339
+ const tableInfo = findTableNodeInfoFromState(editor.state);
26340
+ if (tableInfo) return editor.state.selection.from;
26341
+ const selectionAnchor = resolveEventElement(window.getSelection()?.anchorNode ?? null);
26342
+ const selectionCell2 = selectionAnchor?.closest?.("th,td");
26343
+ if (selectionCell2 instanceof HTMLTableCellElement && editor.view.dom.contains(selectionCell2)) {
26344
+ return editor.view.posAtDOM(selectionCell2, 0) + 1;
26345
+ }
26346
+ const activeElement = document.activeElement instanceof Element ? document.activeElement : null;
26347
+ const activeCell = activeElement?.closest?.("th,td");
26348
+ if (activeCell instanceof HTMLTableCellElement && editor.view.dom.contains(activeCell)) {
26349
+ return editor.view.posAtDOM(activeCell, 0) + 1;
26350
+ }
26351
+ const tables = editor.view.dom.querySelectorAll("table");
26352
+ if (tables.length !== 1) return null;
26353
+ const firstCell = tables[0]?.querySelector("th,td");
26354
+ return firstCell instanceof HTMLTableCellElement ? editor.view.posAtDOM(firstCell, 0) + 1 : null;
26355
+ }
26219
26356
  function fileToDataUrl2(file) {
26220
26357
  return new Promise((resolve, reject) => {
26221
26358
  const reader = new FileReader();
@@ -26322,12 +26459,15 @@ var EditorToolbar = ({
26322
26459
  const [imageUploadError, setImageUploadError] = useState44(null);
26323
26460
  const isImageSelected = editor.isActive("image");
26324
26461
  const imageAttrs = editor.getAttributes("image");
26325
- const tableAttrs = editor.getAttributes("table");
26462
+ const tableAnchorPos = getTableAnchorPos(editor);
26463
+ const tableInfo = tableAnchorPos == null ? null : findTableNodeInfoFromState(editor.state, tableAnchorPos);
26464
+ const tableAttrs = tableInfo?.node.attrs ?? editor.getAttributes("table");
26326
26465
  const textStyleAttrs = editor.getAttributes("textStyle");
26327
26466
  const imageLayout = imageAttrs.imageLayout === "left" || imageAttrs.imageLayout === "right" ? imageAttrs.imageLayout : "block";
26328
26467
  const imageWidthPreset = imageAttrs.imageWidthPreset === "sm" || imageAttrs.imageWidthPreset === "md" || imageAttrs.imageWidthPreset === "lg" ? imageAttrs.imageWidthPreset : null;
26329
- const currentTableAlign = tableAttrs.tableAlign === "center" || tableAttrs.tableAlign === "right" ? tableAttrs.tableAlign : "left";
26330
- const isTableSelected = editor.isActive("table");
26468
+ const tableAlignAttr = tableAttrs.tableAlign ?? tableAttrs.textAlign;
26469
+ const currentTableAlign = tableAlignAttr === "center" || tableAlignAttr === "right" ? tableAlignAttr : "left";
26470
+ const isTableSelected = tableInfo !== null;
26331
26471
  const hasTableContext = isTableSelected || tableCommandAnchorPosRef.current !== null;
26332
26472
  const currentFontFamily = normalizeStyleValue(textStyleAttrs.fontFamily);
26333
26473
  const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
@@ -26345,6 +26485,7 @@ var EditorToolbar = ({
26345
26485
  const currentLineHeightLabel = availableLineHeights.find((option) => normalizeStyleValue(option.value) === currentLineHeight)?.label ?? t("toolbar.lineHeightDefault");
26346
26486
  const currentLetterSpacingLabel = availableLetterSpacings.find((option) => normalizeStyleValue(option.value) === currentLetterSpacing)?.label ?? t("toolbar.letterSpacingDefault");
26347
26487
  const displayedFontFamilyLabel = currentFontFamily ? currentFontFamilyLabel : availableFontFamilies[0]?.label ?? t("toolbar.fontDefault");
26488
+ const tableCommandAnchorPos = tableCommandAnchorPosRef.current ?? tableAnchorPos ?? void 0;
26348
26489
  const insertImageFiles = async (files) => {
26349
26490
  if (files.length === 0) return;
26350
26491
  setIsUploadingImage(true);
@@ -26947,7 +27088,7 @@ var EditorToolbar = ({
26947
27088
  isOpen: isTableMenuOpen,
26948
27089
  onOpenChange: (open) => {
26949
27090
  setIsTableMenuOpen(open);
26950
- tableCommandAnchorPosRef.current = open && editor.isActive("table") ? editor.state.selection.$from.pos : null;
27091
+ tableCommandAnchorPosRef.current = open ? getTableAnchorPos(editor) : null;
26951
27092
  },
26952
27093
  trigger: /* @__PURE__ */ jsxs67(ToolbarButton, { onClick: () => {
26953
27094
  }, title: t("toolbar.table"), children: [
@@ -26973,7 +27114,7 @@ var EditorToolbar = ({
26973
27114
  {
26974
27115
  icon: AlignLeft,
26975
27116
  label: t("tableMenu.alignLeft"),
26976
- onClick: () => applyTableAlignment(editor, "left", tableCommandAnchorPosRef.current ?? void 0),
27117
+ onClick: () => applyTableAlignment(editor, "left", tableCommandAnchorPos),
26977
27118
  active: hasTableContext && currentTableAlign === "left",
26978
27119
  disabled: !hasTableContext
26979
27120
  }
@@ -26983,7 +27124,7 @@ var EditorToolbar = ({
26983
27124
  {
26984
27125
  icon: AlignCenter,
26985
27126
  label: t("tableMenu.alignCenter"),
26986
- onClick: () => applyTableAlignment(editor, "center", tableCommandAnchorPosRef.current ?? void 0),
27127
+ onClick: () => applyTableAlignment(editor, "center", tableCommandAnchorPos),
26987
27128
  active: hasTableContext && currentTableAlign === "center",
26988
27129
  disabled: !hasTableContext
26989
27130
  }
@@ -26993,7 +27134,7 @@ var EditorToolbar = ({
26993
27134
  {
26994
27135
  icon: AlignRight,
26995
27136
  label: t("tableMenu.alignRight"),
26996
- onClick: () => applyTableAlignment(editor, "right", tableCommandAnchorPosRef.current ?? void 0),
27137
+ onClick: () => applyTableAlignment(editor, "right", tableCommandAnchorPos),
26997
27138
  active: hasTableContext && currentTableAlign === "right",
26998
27139
  disabled: !hasTableContext
26999
27140
  }
@@ -27109,12 +27250,238 @@ import {
27109
27250
  RotateCcw as RotateCcw3,
27110
27251
  Subscript as SubscriptIcon2,
27111
27252
  Superscript as SuperscriptIcon2,
27253
+ TableCellsMerge,
27112
27254
  Trash2 as Trash23,
27113
27255
  Type as Type3,
27114
27256
  Underline as UnderlineIcon2,
27115
27257
  Strikethrough as StrikethroughIcon2
27116
27258
  } from "lucide-react";
27117
- import { jsx as jsx82, jsxs as jsxs68 } from "react/jsx-runtime";
27259
+
27260
+ // src/components/UEditor/table-cell-commands.ts
27261
+ import { TextSelection as TextSelection2 } from "@tiptap/pm/state";
27262
+ import { selectedRect, TableMap } from "@tiptap/pm/tables";
27263
+ function getCellSelectionPositions(selection) {
27264
+ const value = selection;
27265
+ const anchor = value.$anchorCell?.pos;
27266
+ const head = value.$headCell?.pos;
27267
+ return typeof anchor === "number" && typeof head === "number" ? { anchor, head } : null;
27268
+ }
27269
+ function findTableInfoFromCellPos(editor, cellPos) {
27270
+ const $pos = editor.state.doc.resolve(cellPos);
27271
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
27272
+ const node = $pos.node(depth);
27273
+ if (node.type.name === "table") {
27274
+ return {
27275
+ table: node,
27276
+ tablePos: $pos.before(depth),
27277
+ tableStart: $pos.start(depth)
27278
+ };
27279
+ }
27280
+ }
27281
+ return null;
27282
+ }
27283
+ function getFocusableCellPos(editor, cellPos) {
27284
+ const cellNode = editor.state.doc.nodeAt(cellPos);
27285
+ if (!cellNode) return cellPos + 1;
27286
+ let offset = cellPos + 1;
27287
+ let node = cellNode.firstChild ?? null;
27288
+ while (node && !node.isTextblock) {
27289
+ offset += 1;
27290
+ node = node.firstChild ?? null;
27291
+ }
27292
+ return node?.isTextblock ? offset + 1 : cellPos + 1;
27293
+ }
27294
+ function focusCell(editor, cellPos) {
27295
+ const selection = TextSelection2.near(editor.state.doc.resolve(getFocusableCellPos(editor, cellPos)));
27296
+ editor.view.dispatch(editor.state.tr.setSelection(selection));
27297
+ editor.view.focus();
27298
+ }
27299
+ function collectChildren(node) {
27300
+ const children = [];
27301
+ node.forEach((child) => children.push(child));
27302
+ return children;
27303
+ }
27304
+ function createEmptyCellNode(cellNode) {
27305
+ return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
27306
+ }
27307
+ function getSelectedTableRect(editor) {
27308
+ const cellSelection = getCellSelectionPositions(editor.state.selection);
27309
+ if (cellSelection) {
27310
+ const tableInfo = findTableInfoFromCellPos(editor, cellSelection.anchor);
27311
+ if (tableInfo) {
27312
+ const map = TableMap.get(tableInfo.table);
27313
+ const rect = map.rectBetween(
27314
+ cellSelection.anchor - tableInfo.tableStart,
27315
+ cellSelection.head - tableInfo.tableStart
27316
+ );
27317
+ return {
27318
+ ...rect,
27319
+ map,
27320
+ table: tableInfo.table,
27321
+ tableStart: tableInfo.tableStart
27322
+ };
27323
+ }
27324
+ }
27325
+ return selectedRect(editor.state);
27326
+ }
27327
+ function parsePixelWidth(value) {
27328
+ if (!value) return null;
27329
+ const parsed = Number.parseFloat(value);
27330
+ return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
27331
+ }
27332
+ function getDomColumnWidths(editor, rect) {
27333
+ const tableDom = editor.view.nodeDOM(rect.tableStart - 1);
27334
+ if (!(tableDom instanceof HTMLTableElement)) return null;
27335
+ const cols = Array.from(tableDom.querySelectorAll("colgroup > col"));
27336
+ if (cols.length === 0) return null;
27337
+ const widths = [];
27338
+ for (let col = rect.left; col < rect.right; col += 1) {
27339
+ const colElement = cols[col];
27340
+ if (!colElement) return null;
27341
+ const width = parsePixelWidth(colElement.style.width) ?? parsePixelWidth(colElement.getAttribute("width")) ?? Math.round(colElement.getBoundingClientRect().width);
27342
+ if (!Number.isFinite(width) || width <= 0) return null;
27343
+ widths.push(width);
27344
+ }
27345
+ return widths.length > 0 ? widths : null;
27346
+ }
27347
+ function getNodeColumnWidths(rect) {
27348
+ const widths = [];
27349
+ for (let col = rect.left; col < rect.right; col += 1) {
27350
+ let width = null;
27351
+ const seen = /* @__PURE__ */ new Set();
27352
+ for (let row = 0; row < rect.map.height && width == null; row += 1) {
27353
+ const cellPos = rect.map.map[row * rect.map.width + col];
27354
+ if (seen.has(cellPos)) continue;
27355
+ seen.add(cellPos);
27356
+ const cell = rect.table.nodeAt(cellPos);
27357
+ const colwidth = cell?.attrs.colwidth;
27358
+ if (!Array.isArray(colwidth)) continue;
27359
+ const cellLeft = rect.map.colCount(cellPos);
27360
+ const widthIndex = col - cellLeft;
27361
+ const candidate = colwidth[widthIndex];
27362
+ if (typeof candidate === "number" && candidate > 0) {
27363
+ width = candidate;
27364
+ }
27365
+ }
27366
+ if (width == null) return null;
27367
+ widths.push(width);
27368
+ }
27369
+ return widths.length > 0 ? widths : null;
27370
+ }
27371
+ function getSelectedColumnWidths(editor, rect) {
27372
+ return getDomColumnWidths(editor, rect) ?? getNodeColumnWidths(rect);
27373
+ }
27374
+ function dispatchTableLayoutChange(editor) {
27375
+ editor.view.dom.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, { bubbles: true }));
27376
+ }
27377
+ function mergeTableCellsPreservingColumnWidths(editor) {
27378
+ const rect = getSelectedTableRect(editor);
27379
+ const widths = getSelectedColumnWidths(editor, rect);
27380
+ const merged = editor.chain().focus().mergeCells().run();
27381
+ if (!merged) return merged;
27382
+ if (!widths) {
27383
+ dispatchTableLayoutChange(editor);
27384
+ return merged;
27385
+ }
27386
+ const nextRect = getSelectedTableRect(editor);
27387
+ const cellPos = nextRect.map.map[nextRect.top * nextRect.map.width + nextRect.left];
27388
+ const absolutePos = nextRect.tableStart + cellPos;
27389
+ const node = editor.state.doc.nodeAt(absolutePos);
27390
+ if (!node) return merged;
27391
+ editor.view.dispatch(
27392
+ editor.state.tr.setNodeMarkup(absolutePos, node.type, {
27393
+ ...node.attrs,
27394
+ colwidth: widths
27395
+ })
27396
+ );
27397
+ dispatchTableLayoutChange(editor);
27398
+ return true;
27399
+ }
27400
+ function runTableCommandAtCellPos(editor, cellPos, command) {
27401
+ if (cellPos == null) return false;
27402
+ focusCell(editor, cellPos);
27403
+ return command(editor.chain().focus(null, { scrollIntoView: false })).run();
27404
+ }
27405
+ function getTableCornerCellPos(editor, activePos) {
27406
+ const tableInfo = findTableInfoFromCellPos(editor, activePos);
27407
+ if (!tableInfo) return null;
27408
+ const map = TableMap.get(tableInfo.table);
27409
+ return tableInfo.tableStart + map.positionAt(map.height - 1, map.width - 1, tableInfo.table);
27410
+ }
27411
+ function replaceTableAtCellPos(editor, cellPos, updateTable) {
27412
+ if (cellPos == null) return false;
27413
+ const tableInfo = findTableInfoFromCellPos(editor, cellPos);
27414
+ if (!tableInfo) return false;
27415
+ const nextTable = updateTable(tableInfo.table);
27416
+ if (!nextTable) return false;
27417
+ editor.view.dispatch(editor.state.tr.replaceWith(tableInfo.tablePos, tableInfo.tablePos + tableInfo.table.nodeSize, nextTable));
27418
+ dispatchTableLayoutChange(editor);
27419
+ return true;
27420
+ }
27421
+ function duplicateTableRowAt(editor, rowIndex, cellPos) {
27422
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
27423
+ const rows = collectChildren(tableNode);
27424
+ const rowNode = rows[rowIndex];
27425
+ if (!rowNode) return null;
27426
+ rows.splice(rowIndex + 1, 0, rowNode.copy(rowNode.content));
27427
+ return tableNode.type.create(tableNode.attrs, rows);
27428
+ });
27429
+ }
27430
+ function clearTableRowAt(editor, rowIndex, cellPos) {
27431
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
27432
+ const rows = collectChildren(tableNode);
27433
+ const rowNode = rows[rowIndex];
27434
+ if (!rowNode) return null;
27435
+ const cells = collectChildren(rowNode).map((cellNode) => createEmptyCellNode(cellNode));
27436
+ rows[rowIndex] = rowNode.type.create(rowNode.attrs, cells);
27437
+ return tableNode.type.create(tableNode.attrs, rows);
27438
+ });
27439
+ }
27440
+ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
27441
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
27442
+ const rows = collectChildren(tableNode).map((rowNode) => {
27443
+ const cells = collectChildren(rowNode);
27444
+ const cellNode = cells[columnIndex];
27445
+ if (!cellNode) return rowNode;
27446
+ cells.splice(columnIndex + 1, 0, cellNode.copy(cellNode.content));
27447
+ return rowNode.type.create(rowNode.attrs, cells);
27448
+ });
27449
+ return tableNode.type.create(tableNode.attrs, rows);
27450
+ });
27451
+ }
27452
+ function clearTableColumnAt(editor, columnIndex, cellPos) {
27453
+ return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
27454
+ const rows = collectChildren(tableNode).map((rowNode) => {
27455
+ const cells = collectChildren(rowNode);
27456
+ const cellNode = cells[columnIndex];
27457
+ if (!cellNode) return rowNode;
27458
+ cells[columnIndex] = createEmptyCellNode(cellNode);
27459
+ return rowNode.type.create(rowNode.attrs, cells);
27460
+ });
27461
+ return tableNode.type.create(tableNode.attrs, rows);
27462
+ });
27463
+ }
27464
+ function expandTableFromCell(editor, activeCellPos, rows, columns) {
27465
+ let cornerCellPos = getTableCornerCellPos(editor, activeCellPos);
27466
+ if (cornerCellPos == null) return false;
27467
+ for (let index = 0; index < rows; index += 1) {
27468
+ const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addRowAfter());
27469
+ if (!ok) return false;
27470
+ cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
27471
+ if (cornerCellPos == null) return false;
27472
+ }
27473
+ for (let index = 0; index < columns; index += 1) {
27474
+ const ok = runTableCommandAtCellPos(editor, cornerCellPos, (chain) => chain.addColumnAfter());
27475
+ if (!ok) return false;
27476
+ cornerCellPos = getTableCornerCellPos(editor, cornerCellPos);
27477
+ if (cornerCellPos == null) return false;
27478
+ }
27479
+ dispatchTableLayoutChange(editor);
27480
+ return true;
27481
+ }
27482
+
27483
+ // src/components/UEditor/menus.tsx
27484
+ import { Fragment as Fragment28, jsx as jsx82, jsxs as jsxs68 } from "react/jsx-runtime";
27118
27485
  var FloatingSlashCommandMenu = ({ editor, onClose }) => {
27119
27486
  const t = useSmartTranslations("UEditor");
27120
27487
  const messages = useMemo23(() => buildSlashCommandMessages(t), [t]);
@@ -27203,6 +27570,8 @@ var BubbleMenuContent = ({
27203
27570
  const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
27204
27571
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
27205
27572
  const isInTable2 = isSelectionInTable(editor.state);
27573
+ const canMergeCells = isInTable2 && editor.can().mergeCells();
27574
+ const canSplitCell = isInTable2 && editor.can().splitCell();
27206
27575
  const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
27207
27576
  const currentLineHeight = normalizeStyleValue(textStyleAttrs.lineHeight);
27208
27577
  const quickFontSizes = useMemo23(
@@ -27497,15 +27866,33 @@ var BubbleMenuContent = ({
27497
27866
  ),
27498
27867
  /* @__PURE__ */ jsx82(ToolbarButton, { onClick: () => setActiveColorPalette("text"), title: t("colors.textColor"), children: /* @__PURE__ */ jsx82(TextColorIcon, { color: currentTextColor }) }),
27499
27868
  /* @__PURE__ */ jsx82(ToolbarButton, { onClick: () => setActiveColorPalette("highlight"), active: editor.isActive("highlight"), title: t("colors.highlight"), children: /* @__PURE__ */ jsx82(HighlightColorIcon, { color: currentHighlightColor }) }),
27500
- isInTable2 && /* @__PURE__ */ jsx82(
27501
- ToolbarButton,
27502
- {
27503
- onClick: () => setActiveColorPalette("cell-bg"),
27504
- active: Boolean(currentCellBgColor),
27505
- title: t("tableMenu.cellBackground") || "Cell background",
27506
- children: /* @__PURE__ */ jsx82(CellBgColorIcon, { color: currentCellBgColor })
27507
- }
27508
- ),
27869
+ isInTable2 && /* @__PURE__ */ jsxs68(Fragment28, { children: [
27870
+ /* @__PURE__ */ jsx82(
27871
+ ToolbarButton,
27872
+ {
27873
+ onClick: () => setActiveColorPalette("cell-bg"),
27874
+ active: Boolean(currentCellBgColor),
27875
+ title: t("tableMenu.cellBackground") || "Cell background",
27876
+ children: /* @__PURE__ */ jsx82(CellBgColorIcon, { color: currentCellBgColor })
27877
+ }
27878
+ ),
27879
+ /* @__PURE__ */ jsx82(
27880
+ ToolbarButton,
27881
+ {
27882
+ onClick: () => {
27883
+ if (canSplitCell) {
27884
+ editor.chain().focus().splitCell().run();
27885
+ return;
27886
+ }
27887
+ mergeTableCellsPreservingColumnWidths(editor);
27888
+ },
27889
+ active: canSplitCell,
27890
+ disabled: !canMergeCells && !canSplitCell,
27891
+ title: canSplitCell ? t("tableMenu.splitCell") || "Split cell" : t("tableMenu.mergeCells") || "Merge cells",
27892
+ children: /* @__PURE__ */ jsx82(TableCellsMerge, { className: "w-4 h-4" })
27893
+ }
27894
+ )
27895
+ ] }),
27509
27896
  /* @__PURE__ */ jsx82(
27510
27897
  ToolbarButton,
27511
27898
  {
@@ -27997,7 +28384,7 @@ function findDiffEnd(a, b, posA, posB) {
27997
28384
  posB -= size;
27998
28385
  }
27999
28386
  }
28000
- var Fragment28 = class _Fragment {
28387
+ var Fragment29 = class _Fragment {
28001
28388
  /**
28002
28389
  @internal
28003
28390
  */
@@ -28289,7 +28676,7 @@ var Fragment28 = class _Fragment {
28289
28676
  throw new RangeError("Can not convert " + nodes + " to a Fragment" + (nodes.nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : ""));
28290
28677
  }
28291
28678
  };
28292
- Fragment28.empty = new Fragment28([], 0);
28679
+ Fragment29.empty = new Fragment29([], 0);
28293
28680
  var found = { index: 0, offset: 0 };
28294
28681
  function retIndex(index, offset) {
28295
28682
  found.index = index;
@@ -28514,7 +28901,7 @@ var Slice = class _Slice {
28514
28901
  let openStart = json.openStart || 0, openEnd = json.openEnd || 0;
28515
28902
  if (typeof openStart != "number" || typeof openEnd != "number")
28516
28903
  throw new RangeError("Invalid input for Slice.fromJSON");
28517
- return new _Slice(Fragment28.fromJSON(schema, json.content), openStart, openEnd);
28904
+ return new _Slice(Fragment29.fromJSON(schema, json.content), openStart, openEnd);
28518
28905
  }
28519
28906
  /**
28520
28907
  Create a slice from a fragment by taking the maximum possible
@@ -28529,7 +28916,7 @@ var Slice = class _Slice {
28529
28916
  return new _Slice(fragment, openStart, openEnd);
28530
28917
  }
28531
28918
  };
28532
- Slice.empty = new Slice(Fragment28.empty, 0, 0);
28919
+ Slice.empty = new Slice(Fragment29.empty, 0, 0);
28533
28920
  function removeRange(content, from, to) {
28534
28921
  let { index, offset } = content.findIndex(from), child = content.maybeChild(index);
28535
28922
  let { index: indexTo, offset: offsetTo } = content.findIndex(to);
@@ -28627,7 +29014,7 @@ function replaceThreeWay($from, $start, $end, $to, depth) {
28627
29014
  addNode(close(openEnd, replaceTwoWay($end, $to, depth + 1)), content);
28628
29015
  }
28629
29016
  addRange($to, null, depth, content);
28630
- return new Fragment28(content);
29017
+ return new Fragment29(content);
28631
29018
  }
28632
29019
  function replaceTwoWay($from, $to, depth) {
28633
29020
  let content = [];
@@ -28637,13 +29024,13 @@ function replaceTwoWay($from, $to, depth) {
28637
29024
  addNode(close(type, replaceTwoWay($from, $to, depth + 1)), content);
28638
29025
  }
28639
29026
  addRange($to, null, depth, content);
28640
- return new Fragment28(content);
29027
+ return new Fragment29(content);
28641
29028
  }
28642
29029
  function prepareSliceForReplace(slice, $along) {
28643
29030
  let extra = $along.depth - slice.openStart, parent = $along.node(extra);
28644
29031
  let node = parent.copy(slice.content);
28645
29032
  for (let i = extra - 1; i >= 0; i--)
28646
- node = $along.node(i).copy(Fragment28.from(node));
29033
+ node = $along.node(i).copy(Fragment29.from(node));
28647
29034
  return {
28648
29035
  start: node.resolveNoCache(slice.openStart + extra),
28649
29036
  end: node.resolveNoCache(node.content.size - slice.openEnd - extra)
@@ -28982,7 +29369,7 @@ var Node2 = class _Node {
28982
29369
  this.type = type;
28983
29370
  this.attrs = attrs;
28984
29371
  this.marks = marks;
28985
- this.content = content || Fragment28.empty;
29372
+ this.content = content || Fragment29.empty;
28986
29373
  }
28987
29374
  /**
28988
29375
  The array of this node's child nodes.
@@ -29287,7 +29674,7 @@ var Node2 = class _Node {
29287
29674
  can optionally pass `start` and `end` indices into the
29288
29675
  replacement fragment.
29289
29676
  */
29290
- canReplace(from, to, replacement = Fragment28.empty, start = 0, end = replacement.childCount) {
29677
+ canReplace(from, to, replacement = Fragment29.empty, start = 0, end = replacement.childCount) {
29291
29678
  let one = this.contentMatchAt(from).matchFragment(replacement, start, end);
29292
29679
  let two = one && one.matchFragment(this.content, to);
29293
29680
  if (!two || !two.validEnd)
@@ -29369,7 +29756,7 @@ var Node2 = class _Node {
29369
29756
  throw new RangeError("Invalid text node in JSON");
29370
29757
  return schema.text(json.text, marks);
29371
29758
  }
29372
- let content = Fragment28.fromJSON(schema, json.content);
29759
+ let content = Fragment29.fromJSON(schema, json.content);
29373
29760
  let node = schema.nodeType(json.type).create(json.attrs, content, marks);
29374
29761
  node.type.checkAttrs(node.attrs);
29375
29762
  return node;
@@ -29465,7 +29852,7 @@ var ContentMatch = class _ContentMatch {
29465
29852
  function search(match, types) {
29466
29853
  let finished = match.matchFragment(after, startIndex);
29467
29854
  if (finished && (!toEnd || finished.validEnd))
29468
- return Fragment28.from(types.map((tp) => tp.createAndFill()));
29855
+ return Fragment29.from(types.map((tp) => tp.createAndFill()));
29469
29856
  for (let i = 0; i < match.next.length; i++) {
29470
29857
  let { type, next } = match.next[i];
29471
29858
  if (!(type.isText || type.hasRequiredAttrs()) && seen.indexOf(next) == -1) {
@@ -30033,7 +30420,7 @@ function mapFragment(fragment, f, parent) {
30033
30420
  child = f(child, parent, i);
30034
30421
  mapped.push(child);
30035
30422
  }
30036
- return Fragment28.fromArray(mapped);
30423
+ return Fragment29.fromArray(mapped);
30037
30424
  }
30038
30425
  var AddMarkStep = class _AddMarkStep extends Step {
30039
30426
  /**
@@ -30150,7 +30537,7 @@ var AddNodeMarkStep = class _AddNodeMarkStep extends Step {
30150
30537
  if (!node)
30151
30538
  return StepResult.fail("No node at mark step's position");
30152
30539
  let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
30153
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment28.from(updated), 0, node.isLeaf ? 0 : 1));
30540
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment29.from(updated), 0, node.isLeaf ? 0 : 1));
30154
30541
  }
30155
30542
  invert(doc) {
30156
30543
  let node = doc.nodeAt(this.pos);
@@ -30196,7 +30583,7 @@ var RemoveNodeMarkStep = class _RemoveNodeMarkStep extends Step {
30196
30583
  if (!node)
30197
30584
  return StepResult.fail("No node at mark step's position");
30198
30585
  let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
30199
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment28.from(updated), 0, node.isLeaf ? 0 : 1));
30586
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment29.from(updated), 0, node.isLeaf ? 0 : 1));
30200
30587
  }
30201
30588
  invert(doc) {
30202
30589
  let node = doc.nodeAt(this.pos);
@@ -30397,7 +30784,7 @@ var AttrStep = class _AttrStep extends Step {
30397
30784
  attrs[name] = node.attrs[name];
30398
30785
  attrs[this.attr] = this.value;
30399
30786
  let updated = node.type.create(attrs, null, node.marks);
30400
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment28.from(updated), 0, node.isLeaf ? 0 : 1));
30787
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment29.from(updated), 0, node.isLeaf ? 0 : 1));
30401
30788
  }
30402
30789
  getMap() {
30403
30790
  return StepMap.empty;
@@ -30574,7 +30961,7 @@ var Selection = class {
30574
30961
  found.
30575
30962
  */
30576
30963
  static findFrom($pos, dir, textOnly = false) {
30577
- let inner = $pos.parent.inlineContent ? new TextSelection2($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
30964
+ let inner = $pos.parent.inlineContent ? new TextSelection3($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
30578
30965
  if (inner)
30579
30966
  return inner;
30580
30967
  for (let depth = $pos.depth - 1; depth >= 0; depth--) {
@@ -30643,7 +31030,7 @@ var Selection = class {
30643
31030
  returns the bookmark for that.
30644
31031
  */
30645
31032
  getBookmark() {
30646
- return TextSelection2.between(this.$anchor, this.$head).getBookmark();
31033
+ return TextSelection3.between(this.$anchor, this.$head).getBookmark();
30647
31034
  }
30648
31035
  };
30649
31036
  Selection.prototype.visible = true;
@@ -30663,7 +31050,7 @@ function checkTextSelection($pos) {
30663
31050
  console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
30664
31051
  }
30665
31052
  }
30666
- var TextSelection2 = class _TextSelection extends Selection {
31053
+ var TextSelection3 = class _TextSelection extends Selection {
30667
31054
  /**
30668
31055
  Construct a text selection between the given points.
30669
31056
  */
@@ -30749,7 +31136,7 @@ var TextSelection2 = class _TextSelection extends Selection {
30749
31136
  return new _TextSelection($anchor, $head);
30750
31137
  }
30751
31138
  };
30752
- Selection.jsonID("text", TextSelection2);
31139
+ Selection.jsonID("text", TextSelection3);
30753
31140
  var TextBookmark = class _TextBookmark {
30754
31141
  constructor(anchor, head) {
30755
31142
  this.anchor = anchor;
@@ -30759,7 +31146,7 @@ var TextBookmark = class _TextBookmark {
30759
31146
  return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
30760
31147
  }
30761
31148
  resolve(doc) {
30762
- return TextSelection2.between(doc.resolve(this.anchor), doc.resolve(this.head));
31149
+ return TextSelection3.between(doc.resolve(this.anchor), doc.resolve(this.head));
30763
31150
  }
30764
31151
  };
30765
31152
  var NodeSelection2 = class _NodeSelection extends Selection {
@@ -30781,7 +31168,7 @@ var NodeSelection2 = class _NodeSelection extends Selection {
30781
31168
  return new _NodeSelection($pos);
30782
31169
  }
30783
31170
  content() {
30784
- return new Slice(Fragment28.from(this.node), 0, 0);
31171
+ return new Slice(Fragment29.from(this.node), 0, 0);
30785
31172
  }
30786
31173
  eq(other) {
30787
31174
  return other instanceof _NodeSelection && other.anchor == this.anchor;
@@ -30878,7 +31265,7 @@ var AllBookmark = {
30878
31265
  };
30879
31266
  function findSelectionIn(doc, node, pos, index, dir, text = false) {
30880
31267
  if (node.inlineContent)
30881
- return TextSelection2.create(doc, pos);
31268
+ return TextSelection3.create(doc, pos);
30882
31269
  for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
30883
31270
  let child = node.child(i);
30884
31271
  if (!child.isAtom) {
@@ -31184,7 +31571,7 @@ if (typeof WeakMap != "undefined") {
31184
31571
  return cache[cachePos++] = value;
31185
31572
  };
31186
31573
  }
31187
- var TableMap = class {
31574
+ var TableMap2 = class {
31188
31575
  constructor(width, height, map, problems) {
31189
31576
  this.width = width;
31190
31577
  this.height = height;
@@ -31321,7 +31708,7 @@ function computeMap(table) {
31321
31708
  pos++;
31322
31709
  }
31323
31710
  if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
31324
- const tableMap = new TableMap(width, height, map, problems);
31711
+ const tableMap = new TableMap2(width, height, map, problems);
31325
31712
  let badWidths = false;
31326
31713
  for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
31327
31714
  if (badWidths) findBadColWidths(tableMap, colWidths, table);
@@ -31425,7 +31812,7 @@ function inSameTable($cellA, $cellB) {
31425
31812
  }
31426
31813
  function nextCell($pos, axis, dir) {
31427
31814
  const table = $pos.node(-1);
31428
- const map = TableMap.get(table);
31815
+ const map = TableMap2.get(table);
31429
31816
  const tableStart = $pos.start(-1);
31430
31817
  const moved = map.nextCell($pos.pos - tableStart, axis, dir);
31431
31818
  return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
@@ -31445,7 +31832,7 @@ function removeColSpan(attrs, pos, n = 1) {
31445
31832
  var CellSelection = class CellSelection2 extends Selection {
31446
31833
  constructor($anchorCell, $headCell = $anchorCell) {
31447
31834
  const table = $anchorCell.node(-1);
31448
- const map = TableMap.get(table);
31835
+ const map = TableMap2.get(table);
31449
31836
  const tableStart = $anchorCell.start(-1);
31450
31837
  const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
31451
31838
  const doc = $anchorCell.node(0);
@@ -31470,11 +31857,11 @@ var CellSelection = class CellSelection2 extends Selection {
31470
31857
  else if (tableChanged && this.isColSelection()) return CellSelection2.colSelection($anchorCell, $headCell);
31471
31858
  else return new CellSelection2($anchorCell, $headCell);
31472
31859
  }
31473
- return TextSelection2.between($anchorCell, $headCell);
31860
+ return TextSelection3.between($anchorCell, $headCell);
31474
31861
  }
31475
31862
  content() {
31476
31863
  const table = this.$anchorCell.node(-1);
31477
- const map = TableMap.get(table);
31864
+ const map = TableMap2.get(table);
31478
31865
  const tableStart = this.$anchorCell.start(-1);
31479
31866
  const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
31480
31867
  const seen = {};
@@ -31509,10 +31896,10 @@ var CellSelection = class CellSelection2 extends Selection {
31509
31896
  }
31510
31897
  rowContent.push(cell);
31511
31898
  }
31512
- rows.push(table.child(row).copy(Fragment28.from(rowContent)));
31899
+ rows.push(table.child(row).copy(Fragment29.from(rowContent)));
31513
31900
  }
31514
31901
  const fragment = this.isColSelection() && this.isRowSelection() ? table : rows;
31515
- return new Slice(Fragment28.from(fragment), 1, 1);
31902
+ return new Slice(Fragment29.from(fragment), 1, 1);
31516
31903
  }
31517
31904
  replace(tr, content = Slice.empty) {
31518
31905
  const mapFrom = tr.steps.length, ranges = this.ranges;
@@ -31524,11 +31911,11 @@ var CellSelection = class CellSelection2 extends Selection {
31524
31911
  if (sel) tr.setSelection(sel);
31525
31912
  }
31526
31913
  replaceWith(tr, node) {
31527
- this.replace(tr, new Slice(Fragment28.from(node), 0, 0));
31914
+ this.replace(tr, new Slice(Fragment29.from(node), 0, 0));
31528
31915
  }
31529
31916
  forEachCell(f) {
31530
31917
  const table = this.$anchorCell.node(-1);
31531
- const map = TableMap.get(table);
31918
+ const map = TableMap2.get(table);
31532
31919
  const tableStart = this.$anchorCell.start(-1);
31533
31920
  const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
31534
31921
  for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
@@ -31543,7 +31930,7 @@ var CellSelection = class CellSelection2 extends Selection {
31543
31930
  }
31544
31931
  static colSelection($anchorCell, $headCell = $anchorCell) {
31545
31932
  const table = $anchorCell.node(-1);
31546
- const map = TableMap.get(table);
31933
+ const map = TableMap2.get(table);
31547
31934
  const tableStart = $anchorCell.start(-1);
31548
31935
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
31549
31936
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -31559,7 +31946,7 @@ var CellSelection = class CellSelection2 extends Selection {
31559
31946
  }
31560
31947
  isRowSelection() {
31561
31948
  const table = this.$anchorCell.node(-1);
31562
- const map = TableMap.get(table);
31949
+ const map = TableMap2.get(table);
31563
31950
  const tableStart = this.$anchorCell.start(-1);
31564
31951
  const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
31565
31952
  const headLeft = map.colCount(this.$headCell.pos - tableStart);
@@ -31573,7 +31960,7 @@ var CellSelection = class CellSelection2 extends Selection {
31573
31960
  }
31574
31961
  static rowSelection($anchorCell, $headCell = $anchorCell) {
31575
31962
  const table = $anchorCell.node(-1);
31576
- const map = TableMap.get(table);
31963
+ const map = TableMap2.get(table);
31577
31964
  const tableStart = $anchorCell.start(-1);
31578
31965
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
31579
31966
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -31622,7 +32009,7 @@ var CellBookmark = class CellBookmark2 {
31622
32009
  };
31623
32010
  var fixTablesKey = new PluginKey3("fix-tables");
31624
32011
  function convertTableNodeToArrayOfRows(tableNode) {
31625
- const map = TableMap.get(tableNode);
32012
+ const map = TableMap2.get(tableNode);
31626
32013
  const rows = [];
31627
32014
  const rowCount = map.height;
31628
32015
  const colCount$1 = map.width;
@@ -31653,7 +32040,7 @@ function convertTableNodeToArrayOfRows(tableNode) {
31653
32040
  }
31654
32041
  function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
31655
32042
  const newRows = [];
31656
- const map = TableMap.get(tableNode);
32043
+ const map = TableMap2.get(tableNode);
31657
32044
  const rowCount = map.height;
31658
32045
  const colCount$1 = map.width;
31659
32046
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
@@ -31702,7 +32089,7 @@ function findParentNode(predicate, $pos) {
31702
32089
  function getCellsInColumn(columnIndex, selection) {
31703
32090
  const table = findTable(selection.$from);
31704
32091
  if (!table) return;
31705
- const map = TableMap.get(table.node);
32092
+ const map = TableMap2.get(table.node);
31706
32093
  if (columnIndex < 0 || columnIndex > map.width - 1) return;
31707
32094
  return map.cellsInRect({
31708
32095
  left: columnIndex,
@@ -31723,7 +32110,7 @@ function getCellsInColumn(columnIndex, selection) {
31723
32110
  function getCellsInRow(rowIndex, selection) {
31724
32111
  const table = findTable(selection.$from);
31725
32112
  if (!table) return;
31726
- const map = TableMap.get(table.node);
32113
+ const map = TableMap2.get(table.node);
31727
32114
  if (rowIndex < 0 || rowIndex > map.height - 1) return;
31728
32115
  return map.cellsInRect({
31729
32116
  left: 0,
@@ -31852,7 +32239,7 @@ function moveColumn(moveColParams) {
31852
32239
  const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
31853
32240
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
31854
32241
  if (!select) return true;
31855
- const map = TableMap.get(newTable);
32242
+ const map = TableMap2.get(newTable);
31856
32243
  const start = table.start;
31857
32244
  const index = targetIndex;
31858
32245
  const lastCell = map.positionAt(map.height - 1, index, newTable);
@@ -31880,7 +32267,7 @@ function moveRow(moveRowParams) {
31880
32267
  const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
31881
32268
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
31882
32269
  if (!select) return true;
31883
- const map = TableMap.get(newTable);
32270
+ const map = TableMap2.get(newTable);
31884
32271
  const start = table.start;
31885
32272
  const index = targetIndex;
31886
32273
  const lastCell = map.positionAt(index, map.width - 1, newTable);
@@ -31895,12 +32282,12 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
31895
32282
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
31896
32283
  return convertArrayOfRowsToTableNode(table, rows);
31897
32284
  }
31898
- function selectedRect(state) {
32285
+ function selectedRect2(state) {
31899
32286
  const sel = state.selection;
31900
32287
  const $pos = selectionCell(state);
31901
32288
  const table = $pos.node(-1);
31902
32289
  const tableStart = $pos.start(-1);
31903
- const map = TableMap.get(table);
32290
+ const map = TableMap2.get(table);
31904
32291
  return {
31905
32292
  ...sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart),
31906
32293
  tableStart,
@@ -31913,7 +32300,7 @@ function deprecated_toggleHeader(type) {
31913
32300
  if (!isInTable(state)) return false;
31914
32301
  if (dispatch) {
31915
32302
  const types = tableNodeTypes(state.schema);
31916
- const rect = selectedRect(state), tr = state.tr;
32303
+ const rect = selectedRect2(state), tr = state.tr;
31917
32304
  const cells = rect.map.cellsInRect(type == "column" ? {
31918
32305
  left: rect.left,
31919
32306
  top: 0,
@@ -31953,7 +32340,7 @@ function toggleHeader(type, options) {
31953
32340
  if (!isInTable(state)) return false;
31954
32341
  if (dispatch) {
31955
32342
  const types = tableNodeTypes(state.schema);
31956
- const rect = selectedRect(state), tr = state.tr;
32343
+ const rect = selectedRect2(state), tr = state.tr;
31957
32344
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
31958
32345
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
31959
32346
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -32085,7 +32472,7 @@ function shiftArrow(axis, dir) {
32085
32472
  };
32086
32473
  }
32087
32474
  function atEndOfCell(view, axis, dir) {
32088
- if (!(view.state.selection instanceof TextSelection2)) return null;
32475
+ if (!(view.state.selection instanceof TextSelection3)) return null;
32089
32476
  const { $head } = view.state.selection;
32090
32477
  for (let d = $head.depth - 1; d >= 0; d--) {
32091
32478
  const parent = $head.node(d);
@@ -32110,157 +32497,484 @@ import {
32110
32497
  ArrowRight as ArrowRight2,
32111
32498
  ArrowUp as ArrowUp2,
32112
32499
  Copy as Copy2,
32113
- GripHorizontal,
32114
- GripVertical as GripVertical3,
32115
- MoreHorizontal as MoreHorizontal2,
32116
32500
  Table as TableIcon2,
32117
32501
  Trash2 as Trash24
32118
32502
  } from "lucide-react";
32119
32503
 
32120
- // src/components/UEditor/table-dom-utils.ts
32121
- var MIN_TABLE_ROW_HEIGHT = 36;
32122
- var COLUMN_RESIZE_LINE_THICKNESS = 2;
32123
- var ROW_RESIZE_LINE_THICKNESS = 2;
32124
- var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
32125
- var TABLE_RESIZE_HIT_ZONE = 10;
32126
- function findTableRowNodeInfo(view, rowElement) {
32127
- const firstCell = rowElement.querySelector("th,td");
32128
- if (!firstCell) return null;
32129
- const cellPos = view.posAtDOM(firstCell, 0);
32130
- const $pos = view.state.doc.resolve(cellPos);
32131
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
32132
- const node = $pos.node(depth);
32133
- if (node.type.name === "tableRow") {
32134
- return {
32135
- pos: $pos.before(depth),
32136
- node
32137
- };
32138
- }
32139
- }
32140
- return null;
32141
- }
32142
- function resolveEventElement(target) {
32143
- if (target instanceof Element) return target;
32144
- if (target instanceof Node) return target.parentElement;
32145
- return null;
32146
- }
32147
- function getSelectionTableCell(view) {
32148
- const browserSelection = window.getSelection();
32149
- const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
32150
- const anchorCell = anchorElement?.closest?.("th,td");
32151
- if (anchorCell instanceof HTMLElement) {
32152
- return anchorCell;
32153
- }
32154
- const { from } = view.state.selection;
32155
- const domAtPos = view.domAtPos(from);
32156
- const element = resolveEventElement(domAtPos.node);
32157
- const cell = element?.closest?.("th,td");
32158
- return cell instanceof HTMLElement ? cell : null;
32159
- }
32160
- function isRowResizeHotspot(cell, clientX, clientY) {
32161
- const rect = cell.getBoundingClientRect();
32162
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
32163
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
32164
- return nearBottom && !nearRight;
32165
- }
32166
- function isColumnResizeHotspot(cell, clientX, clientY) {
32167
- const rect = cell.getBoundingClientRect();
32168
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
32169
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
32170
- return nearRight && !nearBottom;
32171
- }
32172
- function getRelativeBoundaryMetrics(surface, table, row, cell) {
32173
- const surfaceRect = surface.getBoundingClientRect();
32174
- const tableRect = table.getBoundingClientRect();
32175
- const rowRect = row.getBoundingClientRect();
32176
- const cellRect = cell.getBoundingClientRect();
32177
- return {
32178
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
32179
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
32180
- width: tableRect.width,
32181
- height: tableRect.height,
32182
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
32183
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
32184
- };
32185
- }
32186
- function getRelativeCellMetrics(surface, cell) {
32187
- const surfaceRect = surface.getBoundingClientRect();
32188
- const cellRect = cell.getBoundingClientRect();
32189
- return {
32190
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
32191
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
32192
- width: cellRect.width,
32193
- height: cellRect.height
32194
- };
32195
- }
32196
- function getRelativeSelectedCellsMetrics(surface) {
32197
- const selectedCells = Array.from(
32198
- surface.querySelectorAll("td.selectedCell, th.selectedCell")
32199
- );
32200
- if (selectedCells.length === 0) {
32201
- return null;
32202
- }
32203
- const surfaceRect = surface.getBoundingClientRect();
32204
- let left = Number.POSITIVE_INFINITY;
32205
- let top = Number.POSITIVE_INFINITY;
32206
- let right = Number.NEGATIVE_INFINITY;
32207
- let bottom = Number.NEGATIVE_INFINITY;
32208
- selectedCells.forEach((cell) => {
32209
- const rect = cell.getBoundingClientRect();
32210
- left = Math.min(left, rect.left);
32211
- top = Math.min(top, rect.top);
32212
- right = Math.max(right, rect.right);
32213
- bottom = Math.max(bottom, rect.bottom);
32214
- });
32215
- return {
32216
- left: left - surfaceRect.left + surface.scrollLeft,
32217
- top: top - surfaceRect.top + surface.scrollTop,
32218
- width: right - left,
32219
- height: bottom - top
32220
- };
32221
- }
32222
-
32223
- // src/components/UEditor/table-controls.tsx
32224
- import { Fragment as Fragment29, jsx as jsx83, jsxs as jsxs70 } from "react/jsx-runtime";
32225
- var FALLBACK_TABLE_ROW_HEIGHT = 44;
32226
- var FALLBACK_TABLE_COLUMN_WIDTH = 160;
32504
+ // src/components/UEditor/table-hover-state.ts
32227
32505
  var MENU_HOVER_PADDING = 18;
32228
32506
  var ROW_HANDLE_HOVER_WIDTH = 28;
32229
32507
  var COLUMN_HANDLE_HOVER_HEIGHT = 28;
32230
- var TABLE_MENU_TOP_OFFSET = 10;
32231
- var ADD_COLUMN_RAIL_GAP = 4;
32232
- var ADD_ROW_RAIL_GAP = 4;
32233
32508
  var ADD_COLUMN_HOVER_WIDTH = 24;
32234
32509
  var ADD_ROW_HOVER_HEIGHT = 24;
32235
32510
  var HANDLE_HOVER_RADIUS = 14;
32236
- var IDLE_HANDLE_SCALE = "0.78";
32237
- var DEFAULT_HOVER_STATE = {
32511
+ var DEFAULT_TABLE_HOVER_STATE = {
32238
32512
  menuVisible: false,
32239
32513
  addColumnVisible: false,
32240
32514
  addRowVisible: false,
32241
32515
  rowHandleIndex: null,
32242
32516
  columnHandleIndex: null
32243
32517
  };
32244
- function resolveElement(target) {
32245
- if (target instanceof Element) return target;
32246
- if (target instanceof Node) return target.parentElement;
32247
- return null;
32518
+ function areTableHoverStatesEqual(left, right) {
32519
+ return left.menuVisible === right.menuVisible && left.addColumnVisible === right.addColumnVisible && left.addRowVisible === right.addRowVisible && left.rowHandleIndex === right.rowHandleIndex && left.columnHandleIndex === right.columnHandleIndex;
32248
32520
  }
32249
- function nearestIndex(centers, position) {
32250
- let bestIndex = 0;
32251
- let bestDistance = Number.POSITIVE_INFINITY;
32252
- centers.forEach((center, index) => {
32253
- const distance = Math.abs(center - position);
32254
- if (distance < bestDistance) {
32255
- bestDistance = distance;
32256
- bestIndex = index;
32521
+ function buildTableHoverState({
32522
+ event,
32523
+ layout,
32524
+ surface
32525
+ }) {
32526
+ const surfaceRect = surface.getBoundingClientRect();
32527
+ const relativeX = event.clientX - surfaceRect.left + surface.scrollLeft;
32528
+ const relativeY = event.clientY - surfaceRect.top + surface.scrollTop;
32529
+ const targetElement = resolveEventElement(event.target);
32530
+ const directRowHandle = targetElement?.closest?.("[data-row-handle-index]");
32531
+ const directColumnHandle = targetElement?.closest?.("[data-column-handle-index]");
32532
+ const directTableMenu = targetElement?.closest?.("[data-table-control='table-menu']");
32533
+ const directAddColumn = targetElement?.closest?.("[data-table-control='add-column']");
32534
+ const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
32535
+ const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
32536
+ const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
32537
+ const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
32538
+ const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
32539
+ const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
32540
+ const rowHandleIndex = Number.isFinite(directRowHandleIndex) ? directRowHandleIndex : layout.rowHandles.find((rowHandle) => relativeX >= layout.tableLeft - ROW_HANDLE_HOVER_WIDTH && relativeX <= layout.tableLeft && Math.abs(relativeY - rowHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
32541
+ const columnHandleIndex = Number.isFinite(directColumnHandleIndex) ? directColumnHandleIndex : layout.columnHandles.find((columnHandle) => relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop && Math.abs(relativeX - columnHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
32542
+ const menuVisible = Boolean(directTableMenu) || isMouseInTable || relativeX >= layout.tableLeft - MENU_HOVER_PADDING && relativeX <= layout.tableLeft + 42 && relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop + MENU_HOVER_PADDING;
32543
+ const lastRow = layout.rowHandles[layout.rowHandles.length - 1];
32544
+ const lastCol = layout.columnHandles[layout.columnHandles.length - 1];
32545
+ const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight : false;
32546
+ const addColumnVisible = Boolean(directAddColumn) || relativeX >= layout.tableLeft + visibleTableWidth && relativeX <= layout.tableLeft + visibleTableWidth + ADD_COLUMN_HOVER_WIDTH && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight || isMouseInLastColumn;
32547
+ const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth : false;
32548
+ const addRowVisible = Boolean(directAddRow) || relativeY >= layout.tableTop + visibleTableHeight && relativeY <= layout.tableTop + visibleTableHeight + ADD_ROW_HOVER_HEIGHT && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth || isMouseInLastRow;
32549
+ return {
32550
+ menuVisible,
32551
+ addColumnVisible,
32552
+ addRowVisible,
32553
+ rowHandleIndex,
32554
+ columnHandleIndex
32555
+ };
32556
+ }
32557
+
32558
+ // src/components/UEditor/table-drag-preview.tsx
32559
+ import { Fragment as Fragment30, jsx as jsx83, jsxs as jsxs70 } from "react/jsx-runtime";
32560
+ function TableDragPreview({
32561
+ columnDragLabel,
32562
+ dragPreview,
32563
+ layout,
32564
+ rowDragLabel
32565
+ }) {
32566
+ if (!dragPreview) return null;
32567
+ const expandPreviewWidth = dragPreview.kind === "add-column" ? layout.tableWidth + dragPreview.previewCols * layout.avgColumnWidth : layout.tableWidth;
32568
+ const expandPreviewHeight = dragPreview.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
32569
+ const dragStatusText = dragPreview.kind === "row" ? `${rowDragLabel} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview.kind === "column" ? `${columnDragLabel} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview.kind === "add-row" ? `+${dragPreview.previewRows}R` : `+${dragPreview.previewCols}C`;
32570
+ return /* @__PURE__ */ jsxs70(Fragment30, { children: [
32571
+ dragPreview.kind === "row" && /* @__PURE__ */ jsxs70(Fragment30, { children: [
32572
+ /* @__PURE__ */ jsx83(
32573
+ "div",
32574
+ {
32575
+ "aria-hidden": "true",
32576
+ className: "pointer-events-none absolute z-20 rounded-lg border border-primary/20 bg-primary/10",
32577
+ style: {
32578
+ top: dragPreview.targetStart,
32579
+ left: layout.tableLeft,
32580
+ width: layout.tableWidth,
32581
+ height: dragPreview.targetSize
32582
+ }
32583
+ }
32584
+ ),
32585
+ /* @__PURE__ */ jsx83(
32586
+ "div",
32587
+ {
32588
+ "aria-hidden": "true",
32589
+ className: "pointer-events-none absolute z-20 rounded-full bg-primary/80",
32590
+ style: {
32591
+ top: dragPreview.targetStart + dragPreview.targetSize / 2 - 1,
32592
+ left: layout.tableLeft,
32593
+ width: layout.tableWidth,
32594
+ height: 2
32595
+ }
32596
+ }
32597
+ )
32598
+ ] }),
32599
+ dragPreview.kind === "column" && /* @__PURE__ */ jsxs70(Fragment30, { children: [
32600
+ /* @__PURE__ */ jsx83(
32601
+ "div",
32602
+ {
32603
+ "aria-hidden": "true",
32604
+ className: "pointer-events-none absolute z-20 rounded-lg border border-primary/20 bg-primary/10",
32605
+ style: {
32606
+ top: layout.tableTop,
32607
+ left: dragPreview.targetStart,
32608
+ width: dragPreview.targetSize,
32609
+ height: layout.tableHeight
32610
+ }
32611
+ }
32612
+ ),
32613
+ /* @__PURE__ */ jsx83(
32614
+ "div",
32615
+ {
32616
+ "aria-hidden": "true",
32617
+ className: "pointer-events-none absolute z-20 rounded-full bg-primary/80",
32618
+ style: {
32619
+ top: layout.tableTop,
32620
+ left: dragPreview.targetStart + dragPreview.targetSize / 2 - 1,
32621
+ width: 2,
32622
+ height: layout.tableHeight
32623
+ }
32624
+ }
32625
+ )
32626
+ ] }),
32627
+ (dragPreview.kind === "add-row" || dragPreview.kind === "add-column") && /* @__PURE__ */ jsx83(
32628
+ "div",
32629
+ {
32630
+ "aria-hidden": "true",
32631
+ className: "pointer-events-none absolute z-20 rounded-xl border border-dashed border-primary/70 bg-primary/5",
32632
+ style: {
32633
+ top: layout.tableTop,
32634
+ left: layout.tableLeft,
32635
+ width: expandPreviewWidth,
32636
+ height: expandPreviewHeight
32637
+ }
32638
+ }
32639
+ ),
32640
+ /* @__PURE__ */ jsx83(
32641
+ "div",
32642
+ {
32643
+ role: "status",
32644
+ className: "pointer-events-none absolute z-30 rounded-full border border-primary/20 bg-background/95 px-2 py-1 text-[11px] font-medium text-foreground shadow-sm backdrop-blur",
32645
+ style: {
32646
+ top: dragPreview.kind === "add-row" || dragPreview.kind === "add-column" ? layout.tableTop + expandPreviewHeight + 8 : layout.tableTop - 40,
32647
+ left: dragPreview.kind === "add-row" || dragPreview.kind === "add-column" ? layout.tableLeft + Math.max(0, expandPreviewWidth - 84) : layout.tableLeft + Math.max(0, layout.tableWidth - 108)
32648
+ },
32649
+ children: dragStatusText
32650
+ }
32651
+ )
32652
+ ] });
32653
+ }
32654
+
32655
+ // src/components/UEditor/table-add-rails.tsx
32656
+ import { Fragment as Fragment31, jsx as jsx84, jsxs as jsxs71 } from "react/jsx-runtime";
32657
+ var ADD_COLUMN_RAIL_GAP = 4;
32658
+ var ADD_ROW_RAIL_GAP = 4;
32659
+ function TableAddRails({
32660
+ addColumnVisible,
32661
+ addRowVisible,
32662
+ canExpandTable,
32663
+ controlsVisible,
32664
+ layout,
32665
+ onStartAddColumn,
32666
+ onStartAddRow,
32667
+ quickAddColumnLabel,
32668
+ quickAddRowLabel
32669
+ }) {
32670
+ const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
32671
+ const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
32672
+ const columnRailTop = layout.tableTop;
32673
+ const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
32674
+ const rowRailTop = layout.tableTop + visibleTableHeight + ADD_ROW_RAIL_GAP;
32675
+ const rowRailLeft = layout.tableLeft;
32676
+ const showColumnRail = controlsVisible || addColumnVisible;
32677
+ const showRowRail = controlsVisible || addRowVisible;
32678
+ return /* @__PURE__ */ jsxs71(Fragment31, { children: [
32679
+ /* @__PURE__ */ jsx84(
32680
+ Tooltip,
32681
+ {
32682
+ placement: "right",
32683
+ content: /* @__PURE__ */ jsx84("span", { className: "text-xs font-medium", children: quickAddColumnLabel }),
32684
+ children: /* @__PURE__ */ jsx84(
32685
+ "button",
32686
+ {
32687
+ type: "button",
32688
+ "data-table-control": "add-column",
32689
+ "aria-label": quickAddColumnLabel,
32690
+ onMouseDown: (event) => {
32691
+ event.preventDefault();
32692
+ event.stopPropagation();
32693
+ if (!canExpandTable) return;
32694
+ onStartAddColumn();
32695
+ },
32696
+ disabled: !canExpandTable,
32697
+ className: cn(
32698
+ "absolute z-30 inline-flex items-center justify-center rounded-md",
32699
+ "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
32700
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
32701
+ ),
32702
+ style: {
32703
+ top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleTableHeight / 2 - 24),
32704
+ left: columnRailLeft,
32705
+ width: showColumnRail ? 18 : 12,
32706
+ height: showColumnRail ? visibleTableHeight : 48,
32707
+ opacity: showColumnRail ? 1 : 0,
32708
+ transform: showColumnRail ? "scale(1)" : "scale(0.92)",
32709
+ pointerEvents: showColumnRail ? "auto" : "none"
32710
+ },
32711
+ children: /* @__PURE__ */ jsx84("span", { className: "text-sm font-medium leading-none", children: "+" })
32712
+ }
32713
+ )
32714
+ }
32715
+ ),
32716
+ /* @__PURE__ */ jsx84(
32717
+ Tooltip,
32718
+ {
32719
+ placement: "bottom",
32720
+ content: /* @__PURE__ */ jsx84("span", { className: "text-xs font-medium", children: quickAddRowLabel }),
32721
+ children: /* @__PURE__ */ jsx84(
32722
+ "button",
32723
+ {
32724
+ type: "button",
32725
+ "data-table-control": "add-row",
32726
+ "aria-label": quickAddRowLabel,
32727
+ onMouseDown: (event) => {
32728
+ event.preventDefault();
32729
+ event.stopPropagation();
32730
+ if (!canExpandTable) return;
32731
+ onStartAddRow();
32732
+ },
32733
+ disabled: !canExpandTable,
32734
+ className: cn(
32735
+ "absolute z-30 inline-flex items-center justify-center rounded-md",
32736
+ "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
32737
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
32738
+ ),
32739
+ style: {
32740
+ top: rowRailTop,
32741
+ left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleTableWidth / 2 - 24),
32742
+ width: showRowRail ? visibleTableWidth : 48,
32743
+ height: showRowRail ? 16 : 12,
32744
+ opacity: showRowRail ? 1 : 0,
32745
+ transform: showRowRail ? "scale(1)" : "scale(0.92)",
32746
+ pointerEvents: showRowRail ? "auto" : "none"
32747
+ },
32748
+ children: /* @__PURE__ */ jsx84("span", { className: "text-sm font-medium leading-none", children: "+" })
32749
+ }
32750
+ )
32751
+ }
32752
+ )
32753
+ ] });
32754
+ }
32755
+
32756
+ // src/components/UEditor/table-control-menu.tsx
32757
+ import { MoreHorizontal as MoreHorizontal2 } from "lucide-react";
32758
+ import { jsx as jsx85 } from "react/jsx-runtime";
32759
+ function TableControlMenu({
32760
+ controlsVisible,
32761
+ isOpen,
32762
+ items,
32763
+ label,
32764
+ left,
32765
+ menuVisible,
32766
+ onOpenChange,
32767
+ top
32768
+ }) {
32769
+ const shown = controlsVisible || menuVisible || isOpen;
32770
+ return /* @__PURE__ */ jsx85(
32771
+ "div",
32772
+ {
32773
+ className: "absolute z-30",
32774
+ "data-table-control": "table-menu",
32775
+ style: {
32776
+ top,
32777
+ left
32778
+ },
32779
+ children: /* @__PURE__ */ jsx85(
32780
+ Tooltip,
32781
+ {
32782
+ placement: "top",
32783
+ disabled: isOpen,
32784
+ content: /* @__PURE__ */ jsx85("span", { className: "text-xs font-medium", children: label }),
32785
+ children: /* @__PURE__ */ jsx85("span", { className: "inline-flex", children: /* @__PURE__ */ jsx85(
32786
+ DropdownMenu,
32787
+ {
32788
+ placement: "bottom-start",
32789
+ isOpen,
32790
+ onOpenChange,
32791
+ contentClassName: "p-2",
32792
+ items,
32793
+ trigger: /* @__PURE__ */ jsx85(
32794
+ "button",
32795
+ {
32796
+ type: "button",
32797
+ "aria-label": label,
32798
+ onMouseDown: (event) => event.preventDefault(),
32799
+ className: cn(
32800
+ "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-full",
32801
+ "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
32802
+ "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground"
32803
+ ),
32804
+ style: {
32805
+ opacity: shown ? 1 : 0,
32806
+ transform: shown ? "scale(1)" : "scale(0.82)",
32807
+ pointerEvents: shown ? "auto" : "none"
32808
+ },
32809
+ children: /* @__PURE__ */ jsx85(MoreHorizontal2, { className: "h-4 w-4" })
32810
+ }
32811
+ )
32812
+ }
32813
+ ) })
32814
+ }
32815
+ )
32257
32816
  }
32258
- });
32259
- return bestIndex;
32817
+ );
32818
+ }
32819
+
32820
+ // src/components/UEditor/table-axis-handles.tsx
32821
+ import { GripHorizontal, GripVertical as GripVertical3 } from "lucide-react";
32822
+ import { Fragment as Fragment32, jsx as jsx86 } from "react/jsx-runtime";
32823
+ var IDLE_HANDLE_SCALE = "0.78";
32824
+ function TableRowHandles({
32825
+ activeRowIndex,
32826
+ controlsVisible,
32827
+ getMenuItems,
32828
+ hoverRowHandleIndex,
32829
+ onOpenMenuChange,
32830
+ onStartDrag,
32831
+ openMenuKey,
32832
+ rowDragLabel,
32833
+ rowHandleLeft,
32834
+ rowHandles
32835
+ }) {
32836
+ return /* @__PURE__ */ jsx86(Fragment32, { children: rowHandles.map((rowHandle) => {
32837
+ const menuKey = `row:${rowHandle.index}`;
32838
+ const isActive = rowHandle.index === activeRowIndex;
32839
+ const visible = controlsVisible || hoverRowHandleIndex === rowHandle.index || openMenuKey === menuKey;
32840
+ const isShown = visible || isActive;
32841
+ return /* @__PURE__ */ jsx86(
32842
+ "div",
32843
+ {
32844
+ className: "absolute z-30",
32845
+ "data-row-handle-index": rowHandle.index,
32846
+ style: {
32847
+ top: Math.max(8, rowHandle.center - 12),
32848
+ left: rowHandleLeft
32849
+ },
32850
+ children: /* @__PURE__ */ jsx86(
32851
+ Tooltip,
32852
+ {
32853
+ placement: "right",
32854
+ disabled: openMenuKey === menuKey || !visible && isActive,
32855
+ content: /* @__PURE__ */ jsx86("span", { className: "text-xs font-medium", children: `${rowDragLabel} ${rowHandle.index + 1}` }),
32856
+ children: /* @__PURE__ */ jsx86("span", { className: "inline-flex", children: /* @__PURE__ */ jsx86(
32857
+ DropdownMenu,
32858
+ {
32859
+ placement: "right",
32860
+ isOpen: openMenuKey === menuKey,
32861
+ onOpenChange: (open) => onOpenMenuChange(menuKey, open),
32862
+ contentClassName: "p-2",
32863
+ items: getMenuItems(rowHandle),
32864
+ trigger: /* @__PURE__ */ jsx86(
32865
+ "button",
32866
+ {
32867
+ type: "button",
32868
+ "aria-label": `${rowDragLabel} ${rowHandle.index + 1}`,
32869
+ onMouseDown: (event) => {
32870
+ event.preventDefault();
32871
+ event.stopPropagation();
32872
+ onStartDrag(rowHandle);
32873
+ },
32874
+ className: cn(
32875
+ "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-150",
32876
+ visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
32877
+ ),
32878
+ style: {
32879
+ opacity: isShown ? 1 : 0,
32880
+ transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
32881
+ pointerEvents: isShown ? "auto" : "none"
32882
+ },
32883
+ children: visible ? /* @__PURE__ */ jsx86(GripVertical3, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx86("div", { className: "h-3 w-1 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
32884
+ }
32885
+ )
32886
+ }
32887
+ ) })
32888
+ }
32889
+ )
32890
+ },
32891
+ `row-handle-${rowHandle.index}`
32892
+ );
32893
+ }) });
32894
+ }
32895
+ function TableColumnHandles({
32896
+ activeColumnIndex,
32897
+ columnDragLabel,
32898
+ columnHandleTop,
32899
+ columnHandles,
32900
+ controlsVisible,
32901
+ getMenuItems,
32902
+ hoverColumnHandleIndex,
32903
+ onOpenMenuChange,
32904
+ onStartDrag,
32905
+ openMenuKey
32906
+ }) {
32907
+ return /* @__PURE__ */ jsx86(Fragment32, { children: columnHandles.map((columnHandle) => {
32908
+ const menuKey = `column:${columnHandle.index}`;
32909
+ const isActive = columnHandle.index === activeColumnIndex;
32910
+ const visible = controlsVisible || hoverColumnHandleIndex === columnHandle.index || openMenuKey === menuKey;
32911
+ const isShown = visible || isActive;
32912
+ return /* @__PURE__ */ jsx86(
32913
+ "div",
32914
+ {
32915
+ className: "absolute z-30",
32916
+ "data-column-handle-index": columnHandle.index,
32917
+ style: {
32918
+ top: columnHandleTop,
32919
+ left: Math.max(8, columnHandle.center - 12)
32920
+ },
32921
+ children: /* @__PURE__ */ jsx86(
32922
+ Tooltip,
32923
+ {
32924
+ placement: "top",
32925
+ disabled: openMenuKey === menuKey || !visible && isActive,
32926
+ content: /* @__PURE__ */ jsx86("span", { className: "text-xs font-medium", children: `${columnDragLabel} ${columnHandle.index + 1}` }),
32927
+ children: /* @__PURE__ */ jsx86("span", { className: "inline-flex", children: /* @__PURE__ */ jsx86(
32928
+ DropdownMenu,
32929
+ {
32930
+ placement: "bottom-start",
32931
+ isOpen: openMenuKey === menuKey,
32932
+ onOpenChange: (open) => onOpenMenuChange(menuKey, open),
32933
+ contentClassName: "p-2",
32934
+ items: getMenuItems(columnHandle),
32935
+ trigger: /* @__PURE__ */ jsx86(
32936
+ "button",
32937
+ {
32938
+ type: "button",
32939
+ "aria-label": `${columnDragLabel} ${columnHandle.index + 1}`,
32940
+ onMouseDown: (event) => {
32941
+ event.preventDefault();
32942
+ event.stopPropagation();
32943
+ onStartDrag(columnHandle);
32944
+ },
32945
+ className: cn(
32946
+ "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-150",
32947
+ visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
32948
+ ),
32949
+ style: {
32950
+ opacity: isShown ? 1 : 0,
32951
+ transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
32952
+ pointerEvents: isShown ? "auto" : "none"
32953
+ },
32954
+ children: visible ? /* @__PURE__ */ jsx86(GripHorizontal, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx86("div", { className: "h-1 w-3 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
32955
+ }
32956
+ )
32957
+ }
32958
+ ) })
32959
+ }
32960
+ )
32961
+ },
32962
+ `column-handle-${columnHandle.index}`
32963
+ );
32964
+ }) });
32260
32965
  }
32966
+
32967
+ // src/components/UEditor/table-layout-model.ts
32968
+ var FALLBACK_TABLE_ROW_HEIGHT = 44;
32969
+ var FALLBACK_TABLE_COLUMN_WIDTH = 160;
32261
32970
  function metricOrFallback(value, fallback) {
32262
32971
  return Number.isFinite(value) && value > 0 ? value : fallback;
32263
32972
  }
32973
+ function parsePixelMetric(value) {
32974
+ if (!value) return null;
32975
+ const parsed = Number.parseFloat(value);
32976
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
32977
+ }
32264
32978
  function getPrimaryCell(table) {
32265
32979
  const cell = table.querySelector("th,td");
32266
32980
  return cell instanceof HTMLTableCellElement ? cell : null;
@@ -32272,7 +32986,7 @@ function getLastCell(table) {
32272
32986
  return cell instanceof HTMLTableCellElement ? cell : null;
32273
32987
  }
32274
32988
  function getCellFromTarget(target) {
32275
- const element = resolveElement(target);
32989
+ const element = resolveEventElement(target);
32276
32990
  if (!element) return null;
32277
32991
  const directCell = element.closest("th,td");
32278
32992
  if (directCell instanceof HTMLTableCellElement) {
@@ -32298,25 +33012,135 @@ function findTableInfo(editor, pos) {
32298
33012
  }
32299
33013
  return null;
32300
33014
  }
32301
- function getLastCellPosFromState(editor, pos) {
32302
- const tableInfo = findTableInfo(editor, pos);
32303
- if (!tableInfo) return null;
32304
- const map = TableMap.get(tableInfo.node);
32305
- return tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node);
33015
+ function getCellRelativePosFromDomPos(map, tableStart, domPos) {
33016
+ const relativeDomPos = domPos - tableStart;
33017
+ const seen = /* @__PURE__ */ new Set();
33018
+ for (const relativeCellPos of map.map) {
33019
+ if (seen.has(relativeCellPos)) continue;
33020
+ seen.add(relativeCellPos);
33021
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
33022
+ return relativeCellPos;
33023
+ }
33024
+ }
33025
+ return null;
32306
33026
  }
32307
- function buildLayout(editor, surface, cell) {
33027
+ function buildLogicalColumnMetrics({
33028
+ editor,
33029
+ surface,
33030
+ surfaceRect,
33031
+ tableElement,
33032
+ tableInfo,
33033
+ tableLeft,
33034
+ tableWidth
33035
+ }) {
33036
+ const map = TableMap2.get(tableInfo.node);
33037
+ const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
33038
+ const firstRow = tableElement.rows.item(0);
33039
+ const visualColumns = [];
33040
+ if (firstRow) {
33041
+ for (const tableCell of Array.from(firstRow.cells)) {
33042
+ if (!(tableCell instanceof HTMLTableCellElement)) continue;
33043
+ const cellPos = editor.view.posAtDOM(tableCell, 0);
33044
+ const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
33045
+ if (relativeCellPos == null) continue;
33046
+ const cellMapRect = map.findCell(relativeCellPos);
33047
+ const cellRect = tableCell.getBoundingClientRect();
33048
+ const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
33049
+ const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
33050
+ visualColumns.push({
33051
+ index: cellMapRect.left,
33052
+ cellPos: tableInfo.start + relativeCellPos,
33053
+ start: cellStart,
33054
+ size,
33055
+ center: cellStart + size / 2
33056
+ });
33057
+ }
33058
+ }
33059
+ if (visualColumns.length > 0) {
33060
+ return visualColumns.sort((a, b) => a.index - b.index);
33061
+ }
33062
+ const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
33063
+ const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
33064
+ const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
33065
+ let cursor = tableLeft;
33066
+ return Array.from({ length: map.width }, (_, index) => {
33067
+ const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
33068
+ const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
33069
+ cursor += size;
33070
+ return {
33071
+ index,
33072
+ cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
33073
+ start,
33074
+ size,
33075
+ center: start + size / 2
33076
+ };
33077
+ });
33078
+ }
33079
+ function buildLogicalRowMetrics({
33080
+ editor,
33081
+ surface,
33082
+ surfaceRect,
33083
+ tableInfo,
33084
+ rows,
33085
+ tableTop,
33086
+ tableHeight,
33087
+ cornerCell
33088
+ }) {
33089
+ const map = TableMap2.get(tableInfo.node);
33090
+ const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
33091
+ const visualRows = [];
33092
+ const seenCellPositions = /* @__PURE__ */ new Set();
33093
+ for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
33094
+ const relativeCellPos = map.map[rowIndex * map.width];
33095
+ if (seenCellPositions.has(relativeCellPos)) continue;
33096
+ seenCellPositions.add(relativeCellPos);
33097
+ const cellMapRect = map.findCell(relativeCellPos);
33098
+ const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
33099
+ const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
33100
+ if (tableCell) {
33101
+ const cellRect = tableCell.getBoundingClientRect();
33102
+ const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
33103
+ const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
33104
+ visualRows.push({
33105
+ index: cellMapRect.top,
33106
+ cellPos: tableInfo.start + relativeCellPos,
33107
+ start,
33108
+ size,
33109
+ center: start + size / 2
33110
+ });
33111
+ }
33112
+ }
33113
+ if (visualRows.length > 0) {
33114
+ return visualRows.sort((a, b) => a.index - b.index);
33115
+ }
33116
+ return rows.map((tableRow, index) => {
33117
+ const rowRect = tableRow.getBoundingClientRect();
33118
+ const anchorCell = tableRow.cells.item(0) ?? cornerCell;
33119
+ const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
33120
+ const size = metricOrFallback(rowRect.height, fallbackHeight);
33121
+ return {
33122
+ index,
33123
+ cellPos: editor.view.posAtDOM(anchorCell, 0),
33124
+ start,
33125
+ size,
33126
+ center: start + size / 2
33127
+ };
33128
+ });
33129
+ }
33130
+ function buildTableControlLayout(editor, surface, cell) {
32308
33131
  const row = cell.closest("tr");
32309
33132
  const table = cell.closest("table");
32310
33133
  if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
32311
33134
  return null;
32312
33135
  }
32313
33136
  const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
32314
- const referenceRow = rows[0];
32315
- const referenceCells = Array.from(referenceRow?.cells ?? []).filter((item) => item instanceof HTMLTableCellElement);
32316
33137
  const cornerCell = getLastCell(table);
32317
- if (rows.length === 0 || referenceCells.length === 0 || !(cornerCell instanceof HTMLTableCellElement)) {
33138
+ const cellPos = editor.view.posAtDOM(cell, 0);
33139
+ const tableInfo = findTableInfo(editor, cellPos);
33140
+ if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
32318
33141
  return null;
32319
33142
  }
33143
+ const map = TableMap2.get(tableInfo.node);
32320
33144
  const surfaceRect = surface.getBoundingClientRect();
32321
33145
  const tableRect = table.getBoundingClientRect();
32322
33146
  const wrapperElement = table.closest(".tableWrapper");
@@ -32325,8 +33149,8 @@ function buildLayout(editor, surface, cell) {
32325
33149
  const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
32326
33150
  const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
32327
33151
  const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
32328
- const avgColumnWidth = metricOrFallback(tableRect.width / referenceCells.length, FALLBACK_TABLE_COLUMN_WIDTH);
32329
- const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * referenceCells.length);
33152
+ const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
33153
+ const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
32330
33154
  const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
32331
33155
  const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
32332
33156
  const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
@@ -32336,36 +33160,33 @@ function buildLayout(editor, surface, cell) {
32336
33160
  const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
32337
33161
  const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
32338
33162
  const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
32339
- const rowHandles = rows.map((tableRow, index) => {
32340
- const rowRect = tableRow.getBoundingClientRect();
32341
- const anchorCell = tableRow.cells.item(0) ?? cornerCell;
32342
- const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * avgRowHeight;
32343
- const size = metricOrFallback(rowRect.height, avgRowHeight);
32344
- return {
32345
- index,
32346
- cellPos: editor.view.posAtDOM(anchorCell, 0),
32347
- start,
32348
- size,
32349
- center: start + size / 2
32350
- };
33163
+ const rowHandles = buildLogicalRowMetrics({
33164
+ editor,
33165
+ surface,
33166
+ surfaceRect,
33167
+ tableInfo,
33168
+ rows,
33169
+ tableTop,
33170
+ tableHeight,
33171
+ cornerCell
32351
33172
  });
32352
- const columnHandles = referenceCells.map((tableCell, index) => {
32353
- const cellRect = tableCell.getBoundingClientRect();
32354
- const start = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + index * avgColumnWidth;
32355
- const size = metricOrFallback(cellRect.width, avgColumnWidth);
32356
- return {
32357
- index,
32358
- cellPos: editor.view.posAtDOM(tableCell, 0),
32359
- start,
32360
- size,
32361
- center: start + size / 2
32362
- };
33173
+ const columnHandles = buildLogicalColumnMetrics({
33174
+ editor,
33175
+ surface,
33176
+ surfaceRect,
33177
+ tableElement: table,
33178
+ tableInfo,
33179
+ tableLeft,
33180
+ tableWidth
32363
33181
  });
33182
+ const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
33183
+ const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
33184
+ const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
32364
33185
  return {
32365
- cellPos: editor.view.posAtDOM(cell, 0),
32366
- cornerCellPos: editor.view.posAtDOM(cornerCell, 0),
32367
- activeRowIndex: row.rowIndex,
32368
- activeColumnIndex: cell.cellIndex,
33186
+ cellPos: normalizedCellPos,
33187
+ cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
33188
+ activeRowIndex: activeCellRect.top,
33189
+ activeColumnIndex: activeCellRect.left,
32369
33190
  tableLeft,
32370
33191
  tableTop,
32371
33192
  tableWidth,
@@ -32384,9 +33205,25 @@ function buildLayout(editor, surface, cell) {
32384
33205
  columnHandles
32385
33206
  };
32386
33207
  }
33208
+
33209
+ // src/components/UEditor/table-controls.tsx
33210
+ import { Fragment as Fragment33, jsx as jsx87, jsxs as jsxs72 } from "react/jsx-runtime";
33211
+ var TABLE_MENU_TOP_OFFSET = 10;
33212
+ function nearestIndex(centers, position) {
33213
+ let bestIndex = 0;
33214
+ let bestDistance = Number.POSITIVE_INFINITY;
33215
+ centers.forEach((center, index) => {
33216
+ const distance = Math.abs(center - position);
33217
+ if (distance < bestDistance) {
33218
+ bestDistance = distance;
33219
+ bestIndex = index;
33220
+ }
33221
+ });
33222
+ return bestIndex;
33223
+ }
32387
33224
  function getSelectedCell(editor) {
32388
33225
  const browserSelection = window.getSelection();
32389
- const anchorElement = resolveElement(browserSelection?.anchorNode ?? null);
33226
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
32390
33227
  const anchorCell = anchorElement?.closest?.("th,td");
32391
33228
  if (anchorCell instanceof HTMLTableCellElement) {
32392
33229
  return anchorCell;
@@ -32394,35 +33231,15 @@ function getSelectedCell(editor) {
32394
33231
  const domAtPos = editor.view.domAtPos(editor.state.selection.from);
32395
33232
  return getCellFromTarget(domAtPos.node);
32396
33233
  }
32397
- function getFocusableCellPos(editor, cellPos) {
32398
- const cellNode = editor.state.doc.nodeAt(cellPos);
32399
- if (!cellNode) return cellPos + 1;
32400
- let offset = cellPos + 1;
32401
- let node = cellNode.firstChild ?? null;
32402
- while (node && !node.isTextblock) {
32403
- offset += 1;
32404
- node = node.firstChild ?? null;
32405
- }
32406
- return node?.isTextblock ? offset + 1 : cellPos + 1;
32407
- }
32408
- function focusCell(editor, cellPos) {
32409
- const selection = TextSelection2.near(editor.state.doc.resolve(getFocusableCellPos(editor, cellPos)));
32410
- editor.view.dispatch(editor.state.tr.setSelection(selection));
32411
- editor.view.focus();
32412
- }
32413
- function collectChildren(node) {
32414
- const children = [];
32415
- node.forEach((child) => children.push(child));
32416
- return children;
32417
- }
32418
33234
  function TableControls({ editor, containerRef }) {
32419
33235
  const t = useSmartTranslations("UEditor");
32420
33236
  const [layout, setLayout] = React75.useState(null);
32421
33237
  const [dragPreview, setDragPreview] = React75.useState(null);
32422
- const [hoverState, setHoverState] = React75.useState(DEFAULT_HOVER_STATE);
33238
+ const [hoverState, setHoverState] = React75.useState(DEFAULT_TABLE_HOVER_STATE);
32423
33239
  const [openMenuKey, setOpenMenuKey] = React75.useState(null);
32424
33240
  const layoutRef = React75.useRef(null);
32425
33241
  const dragStateRef = React75.useRef(null);
33242
+ const syncFrameRef = React75.useRef(null);
32426
33243
  React75.useEffect(() => {
32427
33244
  layoutRef.current = layout;
32428
33245
  }, [layout]);
@@ -32432,11 +33249,24 @@ function TableControls({ editor, containerRef }) {
32432
33249
  setLayout(null);
32433
33250
  return;
32434
33251
  }
32435
- setLayout(buildLayout(editor, surface, cell));
33252
+ setLayout(buildTableControlLayout(editor, surface, cell));
32436
33253
  }, [containerRef, editor]);
32437
33254
  const syncFromSelection = React75.useCallback(() => {
32438
33255
  syncFromCell(getSelectedCell(editor));
32439
33256
  }, [editor, syncFromCell]);
33257
+ const scheduleSyncFromSelection = React75.useCallback(() => {
33258
+ if (syncFrameRef.current !== null) return;
33259
+ syncFrameRef.current = window.requestAnimationFrame(() => {
33260
+ syncFrameRef.current = null;
33261
+ syncFromSelection();
33262
+ });
33263
+ }, [syncFromSelection]);
33264
+ React75.useEffect(() => () => {
33265
+ if (syncFrameRef.current !== null) {
33266
+ window.cancelAnimationFrame(syncFrameRef.current);
33267
+ syncFrameRef.current = null;
33268
+ }
33269
+ }, []);
32440
33270
  const refreshCurrentLayout = React75.useCallback(() => {
32441
33271
  setLayout((prev) => {
32442
33272
  if (!prev) return prev;
@@ -32444,7 +33274,7 @@ function TableControls({ editor, containerRef }) {
32444
33274
  if (!surface) return null;
32445
33275
  const node = editor.view.nodeDOM(prev.cellPos);
32446
33276
  const cell = getCellFromTarget(node) ?? getSelectedCell(editor);
32447
- return cell ? buildLayout(editor, surface, cell) : null;
33277
+ return cell ? buildTableControlLayout(editor, surface, cell) : null;
32448
33278
  });
32449
33279
  }, [containerRef, editor]);
32450
33280
  const clearDrag = React75.useCallback(() => {
@@ -32456,43 +33286,16 @@ function TableControls({ editor, containerRef }) {
32456
33286
  const activeLayout = layoutRef.current;
32457
33287
  const surface = containerRef.current;
32458
33288
  if (!activeLayout || !surface || dragStateRef.current) {
32459
- setHoverState(DEFAULT_HOVER_STATE);
33289
+ setHoverState(DEFAULT_TABLE_HOVER_STATE);
32460
33290
  return;
32461
33291
  }
32462
- const surfaceRect = surface.getBoundingClientRect();
32463
- const relativeX = event.clientX - surfaceRect.left + surface.scrollLeft;
32464
- const relativeY = event.clientY - surfaceRect.top + surface.scrollTop;
32465
- const targetElement = resolveElement(event.target);
32466
- const directRowHandle = targetElement?.closest?.("[data-row-handle-index]");
32467
- const directColumnHandle = targetElement?.closest?.("[data-column-handle-index]");
32468
- const directTableMenu = targetElement?.closest?.("[data-table-control='table-menu']");
32469
- const directAddColumn = targetElement?.closest?.("[data-table-control='add-column']");
32470
- const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
32471
- const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
32472
- const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
32473
- const visibleTableWidth2 = Math.min(activeLayout.tableWidth, activeLayout.viewportWidth);
32474
- const visibleTableHeight2 = Math.min(activeLayout.tableHeight, activeLayout.viewportHeight);
32475
- const isMouseInTable = relativeX >= activeLayout.tableLeft && relativeX <= activeLayout.tableLeft + activeLayout.tableWidth && relativeY >= activeLayout.tableTop && relativeY <= activeLayout.tableTop + activeLayout.tableHeight;
32476
- 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;
32477
- 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;
32478
- const menuVisible = Boolean(directTableMenu) || isMouseInTable || relativeX >= activeLayout.tableLeft - MENU_HOVER_PADDING && relativeX <= activeLayout.tableLeft + 42 && relativeY >= activeLayout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= activeLayout.tableTop + MENU_HOVER_PADDING;
32479
- const lastRow = activeLayout.rowHandles[activeLayout.rowHandles.length - 1];
32480
- const lastCol = activeLayout.columnHandles[activeLayout.columnHandles.length - 1];
32481
- const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= activeLayout.tableTop && relativeY <= activeLayout.tableTop + visibleTableHeight2 : false;
32482
- const addColumnVisible = Boolean(directAddColumn) || relativeX >= activeLayout.tableLeft + visibleTableWidth2 && relativeX <= activeLayout.tableLeft + visibleTableWidth2 + ADD_COLUMN_HOVER_WIDTH && relativeY >= activeLayout.tableTop && relativeY <= activeLayout.tableTop + visibleTableHeight2 || isMouseInLastColumn;
32483
- const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= activeLayout.tableLeft && relativeX <= activeLayout.tableLeft + visibleTableWidth2 : false;
32484
- const addRowVisible = Boolean(directAddRow) || relativeY >= activeLayout.tableTop + visibleTableHeight2 && relativeY <= activeLayout.tableTop + visibleTableHeight2 + ADD_ROW_HOVER_HEIGHT && relativeX >= activeLayout.tableLeft && relativeX <= activeLayout.tableLeft + visibleTableWidth2 || isMouseInLastRow;
33292
+ const nextState = buildTableHoverState({
33293
+ event,
33294
+ layout: activeLayout,
33295
+ surface
33296
+ });
32485
33297
  setHoverState((prev) => {
32486
- if (prev.menuVisible === menuVisible && prev.addColumnVisible === addColumnVisible && prev.addRowVisible === addRowVisible && prev.rowHandleIndex === rowHandleIndex && prev.columnHandleIndex === columnHandleIndex) {
32487
- return prev;
32488
- }
32489
- return {
32490
- menuVisible,
32491
- addColumnVisible,
32492
- addRowVisible,
32493
- rowHandleIndex,
32494
- columnHandleIndex
32495
- };
33298
+ return areTableHoverStatesEqual(prev, nextState) ? prev : nextState;
32496
33299
  });
32497
33300
  }, [containerRef]);
32498
33301
  React75.useEffect(() => {
@@ -32510,14 +33313,17 @@ function TableControls({ editor, containerRef }) {
32510
33313
  };
32511
33314
  const handleMouseLeave = () => {
32512
33315
  if (dragStateRef.current) return;
32513
- setHoverState(DEFAULT_HOVER_STATE);
33316
+ setHoverState(DEFAULT_TABLE_HOVER_STATE);
32514
33317
  };
32515
- const handleFocusIn = () => {
33318
+ const handleFocusIn = (event) => {
32516
33319
  if (dragStateRef.current) return;
32517
- syncFromSelection();
33320
+ const cell = event ? getCellFromTarget(event.target) : null;
33321
+ syncFromCell(cell ?? getSelectedCell(editor));
32518
33322
  };
32519
33323
  proseMirror.addEventListener("mouseover", handleMouseOver);
32520
33324
  proseMirror.addEventListener("mouseleave", handleMouseLeave);
33325
+ proseMirror.addEventListener("click", handleFocusIn);
33326
+ proseMirror.addEventListener("mouseup", handleFocusIn);
32521
33327
  proseMirror.addEventListener("focusin", handleFocusIn);
32522
33328
  surface.addEventListener("mouseover", handleSurfaceMouseMove);
32523
33329
  surface.addEventListener("mousemove", handleSurfaceMouseMove);
@@ -32530,6 +33336,8 @@ function TableControls({ editor, containerRef }) {
32530
33336
  return () => {
32531
33337
  proseMirror.removeEventListener("mouseover", handleMouseOver);
32532
33338
  proseMirror.removeEventListener("mouseleave", handleMouseLeave);
33339
+ proseMirror.removeEventListener("click", handleFocusIn);
33340
+ proseMirror.removeEventListener("mouseup", handleFocusIn);
32533
33341
  proseMirror.removeEventListener("focusin", handleFocusIn);
32534
33342
  surface.removeEventListener("mouseover", handleSurfaceMouseMove);
32535
33343
  surface.removeEventListener("mousemove", handleSurfaceMouseMove);
@@ -32541,98 +33349,90 @@ function TableControls({ editor, containerRef }) {
32541
33349
  };
32542
33350
  }, [clearDrag, containerRef, editor, refreshCurrentLayout, syncFromCell, syncFromSelection, updateHoverState]);
32543
33351
  const runAtCellPos = React75.useCallback((cellPos, command, options) => {
32544
- if (cellPos == null) return false;
32545
- focusCell(editor, cellPos);
32546
- const result = command(editor.chain().focus(null, { scrollIntoView: false })).run();
33352
+ const result = runTableCommandAtCellPos(editor, cellPos, command);
32547
33353
  if (options?.sync !== false) {
32548
- requestAnimationFrame(syncFromSelection);
33354
+ scheduleSyncFromSelection();
32549
33355
  }
32550
33356
  return result;
32551
- }, [editor, syncFromSelection]);
33357
+ }, [editor, scheduleSyncFromSelection]);
32552
33358
  const runAtActiveCell = React75.useCallback((command, options) => {
32553
33359
  return runAtCellPos(layoutRef.current?.cellPos ?? null, command, options);
32554
33360
  }, [runAtCellPos]);
32555
- const getCurrentCornerCellPos = React75.useCallback(() => {
32556
- const activePos = layoutRef.current?.cellPos ?? editor.state.selection.from;
32557
- return getLastCellPosFromState(editor, activePos);
32558
- }, [editor]);
32559
- const runAtCornerCell = React75.useCallback((command, options) => {
32560
- return runAtCellPos(getCurrentCornerCellPos(), command, options);
32561
- }, [getCurrentCornerCellPos, runAtCellPos]);
32562
- const replaceTableAtCellPos = React75.useCallback((cellPos, updateTable) => {
32563
- if (cellPos == null) return false;
32564
- const tableInfo = findTableInfo(editor, cellPos);
32565
- if (!tableInfo) return false;
32566
- const nextTable = updateTable(tableInfo.node);
32567
- if (!nextTable) return false;
32568
- editor.view.dispatch(editor.state.tr.replaceWith(tableInfo.pos, tableInfo.pos + tableInfo.node.nodeSize, nextTable));
32569
- requestAnimationFrame(syncFromSelection);
32570
- return true;
32571
- }, [editor, syncFromSelection]);
32572
- const createEmptyCellNode = React75.useCallback((cellNode) => {
32573
- return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
32574
- }, []);
32575
33361
  const duplicateRowAt = React75.useCallback((rowIndex, cellPos) => {
32576
- return replaceTableAtCellPos(cellPos, (tableNode) => {
32577
- const rows = collectChildren(tableNode);
32578
- const rowNode = rows[rowIndex];
32579
- if (!rowNode) return null;
32580
- rows.splice(rowIndex + 1, 0, rowNode.copy(rowNode.content));
32581
- return tableNode.type.create(tableNode.attrs, rows);
32582
- });
32583
- }, [replaceTableAtCellPos]);
33362
+ const result = duplicateTableRowAt(editor, rowIndex, cellPos);
33363
+ scheduleSyncFromSelection();
33364
+ return result;
33365
+ }, [editor, scheduleSyncFromSelection]);
32584
33366
  const clearRowAt = React75.useCallback((rowIndex, cellPos) => {
32585
- return replaceTableAtCellPos(cellPos, (tableNode) => {
32586
- const rows = collectChildren(tableNode);
32587
- const rowNode = rows[rowIndex];
32588
- if (!rowNode) return null;
32589
- const cells = collectChildren(rowNode).map((cellNode) => createEmptyCellNode(cellNode));
32590
- rows[rowIndex] = rowNode.type.create(rowNode.attrs, cells);
32591
- return tableNode.type.create(tableNode.attrs, rows);
32592
- });
32593
- }, [createEmptyCellNode, replaceTableAtCellPos]);
33367
+ const result = clearTableRowAt(editor, rowIndex, cellPos);
33368
+ scheduleSyncFromSelection();
33369
+ return result;
33370
+ }, [editor, scheduleSyncFromSelection]);
32594
33371
  const duplicateColumnAt = React75.useCallback((columnIndex, cellPos) => {
32595
- return replaceTableAtCellPos(cellPos, (tableNode) => {
32596
- const rows = collectChildren(tableNode).map((rowNode) => {
32597
- const cells = collectChildren(rowNode);
32598
- const cellNode = cells[columnIndex];
32599
- if (!cellNode) return rowNode;
32600
- cells.splice(columnIndex + 1, 0, cellNode.copy(cellNode.content));
32601
- return rowNode.type.create(rowNode.attrs, cells);
32602
- });
32603
- return tableNode.type.create(tableNode.attrs, rows);
32604
- });
32605
- }, [replaceTableAtCellPos]);
33372
+ const result = duplicateTableColumnAt(editor, columnIndex, cellPos);
33373
+ scheduleSyncFromSelection();
33374
+ return result;
33375
+ }, [editor, scheduleSyncFromSelection]);
32606
33376
  const clearColumnAt = React75.useCallback((columnIndex, cellPos) => {
32607
- return replaceTableAtCellPos(cellPos, (tableNode) => {
32608
- const rows = collectChildren(tableNode).map((rowNode) => {
32609
- const cells = collectChildren(rowNode);
32610
- const cellNode = cells[columnIndex];
32611
- if (!cellNode) return rowNode;
32612
- cells[columnIndex] = createEmptyCellNode(cellNode);
32613
- return rowNode.type.create(rowNode.attrs, cells);
32614
- });
32615
- return tableNode.type.create(tableNode.attrs, rows);
32616
- });
32617
- }, [createEmptyCellNode, replaceTableAtCellPos]);
33377
+ const result = clearTableColumnAt(editor, columnIndex, cellPos);
33378
+ scheduleSyncFromSelection();
33379
+ return result;
33380
+ }, [editor, scheduleSyncFromSelection]);
32618
33381
  const expandTableBy = React75.useCallback((rows, cols) => {
32619
- let ok = true;
32620
- for (let index = 0; index < rows; index += 1) {
32621
- ok = runAtCornerCell((chain) => chain.addRowAfter(), { sync: false });
32622
- if (!ok) return false;
32623
- }
32624
- for (let index = 0; index < cols; index += 1) {
32625
- ok = runAtCornerCell((chain) => chain.addColumnAfter(), { sync: false });
32626
- if (!ok) return false;
32627
- }
32628
- requestAnimationFrame(syncFromSelection);
32629
- return true;
32630
- }, [runAtCornerCell, syncFromSelection]);
33382
+ const activeCellPos = layoutRef.current?.cellPos ?? editor.state.selection.from;
33383
+ const result = expandTableFromCell(editor, activeCellPos, rows, cols);
33384
+ scheduleSyncFromSelection();
33385
+ return result;
33386
+ }, [editor, scheduleSyncFromSelection]);
32631
33387
  const canExpandTable = Boolean(layout);
32632
33388
  const controlsVisible = dragPreview !== null;
32633
33389
  const tableMenuOpen = openMenuKey === "table";
32634
- const getRowMenuKey = React75.useCallback((index) => `row:${index}`, []);
32635
- const getColumnMenuKey = React75.useCallback((index) => `column:${index}`, []);
33390
+ const startAddColumnDrag = React75.useCallback(() => {
33391
+ setOpenMenuKey(null);
33392
+ dragStateRef.current = { kind: "add-column", previewCols: 1 };
33393
+ setDragPreview({ kind: "add-column", previewCols: 1 });
33394
+ document.body.style.cursor = "ew-resize";
33395
+ }, []);
33396
+ const startAddRowDrag = React75.useCallback(() => {
33397
+ setOpenMenuKey(null);
33398
+ dragStateRef.current = { kind: "add-row", previewRows: 1 };
33399
+ setDragPreview({ kind: "add-row", previewRows: 1 });
33400
+ document.body.style.cursor = "ns-resize";
33401
+ }, []);
33402
+ const startRowDrag = React75.useCallback((rowHandle) => {
33403
+ setOpenMenuKey(null);
33404
+ dragStateRef.current = {
33405
+ kind: "row",
33406
+ originIndex: rowHandle.index,
33407
+ targetIndex: rowHandle.index,
33408
+ anchorPos: rowHandle.cellPos
33409
+ };
33410
+ setDragPreview({
33411
+ kind: "row",
33412
+ originIndex: rowHandle.index,
33413
+ targetIndex: rowHandle.index,
33414
+ targetStart: rowHandle.start,
33415
+ targetSize: rowHandle.size
33416
+ });
33417
+ document.body.style.cursor = "grabbing";
33418
+ }, []);
33419
+ const startColumnDrag = React75.useCallback((columnHandle) => {
33420
+ setOpenMenuKey(null);
33421
+ dragStateRef.current = {
33422
+ kind: "column",
33423
+ originIndex: columnHandle.index,
33424
+ targetIndex: columnHandle.index,
33425
+ anchorPos: columnHandle.cellPos
33426
+ };
33427
+ setDragPreview({
33428
+ kind: "column",
33429
+ originIndex: columnHandle.index,
33430
+ targetIndex: columnHandle.index,
33431
+ targetStart: columnHandle.start,
33432
+ targetSize: columnHandle.size
33433
+ });
33434
+ document.body.style.cursor = "grabbing";
33435
+ }, []);
32636
33436
  React75.useEffect(() => {
32637
33437
  const handleMouseMove = (event) => {
32638
33438
  const dragState = dragStateRef.current;
@@ -32643,9 +33443,10 @@ function TableControls({ editor, containerRef }) {
32643
33443
  const relativeX = event.clientX - surfaceRect.left + surface.scrollLeft;
32644
33444
  const relativeY = event.clientY - surfaceRect.top + surface.scrollTop;
32645
33445
  if (dragState.kind === "row") {
32646
- const targetIndex = nearestIndex(activeLayout.rowHandles.map((item) => item.center), relativeY);
33446
+ const targetHandleIndex = nearestIndex(activeLayout.rowHandles.map((item) => item.center), relativeY);
33447
+ const targetRow = activeLayout.rowHandles[targetHandleIndex];
33448
+ const targetIndex = targetRow.index;
32647
33449
  dragState.targetIndex = targetIndex;
32648
- const targetRow = activeLayout.rowHandles[targetIndex];
32649
33450
  setDragPreview({
32650
33451
  kind: "row",
32651
33452
  originIndex: dragState.originIndex,
@@ -32657,9 +33458,10 @@ function TableControls({ editor, containerRef }) {
32657
33458
  return;
32658
33459
  }
32659
33460
  if (dragState.kind === "column") {
32660
- const targetIndex = nearestIndex(activeLayout.columnHandles.map((item) => item.center), relativeX);
33461
+ const targetHandleIndex = nearestIndex(activeLayout.columnHandles.map((item) => item.center), relativeX);
33462
+ const targetColumn = activeLayout.columnHandles[targetHandleIndex];
33463
+ const targetIndex = targetColumn.index;
32661
33464
  dragState.targetIndex = targetIndex;
32662
- const targetColumn = activeLayout.columnHandles[targetIndex];
32663
33465
  setDragPreview({
32664
33466
  kind: "column",
32665
33467
  originIndex: dragState.originIndex,
@@ -32692,7 +33494,7 @@ function TableControls({ editor, containerRef }) {
32692
33494
  pos: dragState.anchorPos,
32693
33495
  select: true
32694
33496
  })(editor.state, editor.view.dispatch);
32695
- requestAnimationFrame(syncFromSelection);
33497
+ scheduleSyncFromSelection();
32696
33498
  }
32697
33499
  if (dragState.kind === "column" && dragState.originIndex !== dragState.targetIndex) {
32698
33500
  moveTableColumn({
@@ -32701,7 +33503,7 @@ function TableControls({ editor, containerRef }) {
32701
33503
  pos: dragState.anchorPos,
32702
33504
  select: true
32703
33505
  })(editor.state, editor.view.dispatch);
32704
- requestAnimationFrame(syncFromSelection);
33506
+ scheduleSyncFromSelection();
32705
33507
  }
32706
33508
  if (dragState.kind === "add-row") {
32707
33509
  expandTableBy(dragState.previewRows, 0);
@@ -32719,7 +33521,7 @@ function TableControls({ editor, containerRef }) {
32719
33521
  window.removeEventListener("mouseup", handleMouseUp);
32720
33522
  window.removeEventListener("blur", clearDrag);
32721
33523
  };
32722
- }, [clearDrag, containerRef, editor, expandTableBy, syncFromSelection]);
33524
+ }, [clearDrag, containerRef, editor, expandTableBy, scheduleSyncFromSelection]);
32723
33525
  const menuItems = React75.useMemo(() => {
32724
33526
  if (!layout) return [];
32725
33527
  return [
@@ -32851,373 +33653,77 @@ function TableControls({ editor, containerRef }) {
32851
33653
  const menuLeft = Math.max(8, layout.tableLeft);
32852
33654
  const rowHandleLeft = layout.tableLeft - 12;
32853
33655
  const columnHandleTop = layout.tableTop - 12;
32854
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
32855
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
32856
- const columnRailTop = layout.tableTop;
32857
- const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
32858
- const rowRailTop = layout.tableTop + visibleTableHeight + ADD_ROW_RAIL_GAP;
32859
- const rowRailLeft = layout.tableLeft;
32860
- const expandPreviewWidth = dragPreview?.kind === "add-column" ? layout.tableWidth + dragPreview.previewCols * layout.avgColumnWidth : layout.tableWidth;
32861
- const expandPreviewHeight = dragPreview?.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
32862
- 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;
32863
- return /* @__PURE__ */ jsxs70(Fragment29, { children: [
32864
- layout.rowHandles.map((rowHandle) => {
32865
- const menuKey = getRowMenuKey(rowHandle.index);
32866
- const isActive = rowHandle.index === layout.activeRowIndex;
32867
- const visible = controlsVisible || hoverState.rowHandleIndex === rowHandle.index || openMenuKey === menuKey;
32868
- const isShown = visible || isActive;
32869
- return /* @__PURE__ */ jsx83(
32870
- "div",
32871
- {
32872
- className: "absolute z-30",
32873
- "data-row-handle-index": rowHandle.index,
32874
- style: {
32875
- top: Math.max(8, rowHandle.center - 12),
32876
- left: rowHandleLeft
32877
- },
32878
- children: /* @__PURE__ */ jsx83(
32879
- Tooltip,
32880
- {
32881
- placement: "right",
32882
- disabled: openMenuKey === menuKey || !visible && isActive,
32883
- content: /* @__PURE__ */ jsx83("span", { className: "text-xs font-medium", children: `${t("tableMenu.dragRow")} ${rowHandle.index + 1}` }),
32884
- children: /* @__PURE__ */ jsx83("span", { className: "inline-flex", children: /* @__PURE__ */ jsx83(
32885
- DropdownMenu,
32886
- {
32887
- placement: "right",
32888
- isOpen: openMenuKey === menuKey,
32889
- onOpenChange: (open) => {
32890
- setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
32891
- },
32892
- contentClassName: "p-2",
32893
- items: getRowHandleMenuItems(rowHandle),
32894
- trigger: /* @__PURE__ */ jsx83(
32895
- "button",
32896
- {
32897
- type: "button",
32898
- "aria-label": `${t("tableMenu.dragRow")} ${rowHandle.index + 1}`,
32899
- onMouseDown: (event) => {
32900
- event.preventDefault();
32901
- event.stopPropagation();
32902
- setOpenMenuKey(null);
32903
- dragStateRef.current = {
32904
- kind: "row",
32905
- originIndex: rowHandle.index,
32906
- targetIndex: rowHandle.index,
32907
- anchorPos: rowHandle.cellPos
32908
- };
32909
- setDragPreview({
32910
- kind: "row",
32911
- originIndex: rowHandle.index,
32912
- targetIndex: rowHandle.index,
32913
- targetStart: rowHandle.start,
32914
- targetSize: rowHandle.size
32915
- });
32916
- document.body.style.cursor = "grabbing";
32917
- },
32918
- className: cn(
32919
- "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-150",
32920
- visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
32921
- ),
32922
- style: {
32923
- opacity: isShown ? 1 : 0,
32924
- transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
32925
- pointerEvents: isShown ? "auto" : "none"
32926
- },
32927
- children: visible ? /* @__PURE__ */ jsx83(GripVertical3, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx83("div", { className: "h-3 w-1 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
32928
- }
32929
- )
32930
- }
32931
- ) })
32932
- }
32933
- )
32934
- },
32935
- `row-handle-${rowHandle.index}`
32936
- );
32937
- }),
32938
- layout.columnHandles.map((columnHandle) => {
32939
- const menuKey = getColumnMenuKey(columnHandle.index);
32940
- const isActive = columnHandle.index === layout.activeColumnIndex;
32941
- const visible = controlsVisible || hoverState.columnHandleIndex === columnHandle.index || openMenuKey === menuKey;
32942
- const isShown = visible || isActive;
32943
- return /* @__PURE__ */ jsx83(
32944
- "div",
32945
- {
32946
- className: "absolute z-30",
32947
- "data-column-handle-index": columnHandle.index,
32948
- style: {
32949
- top: columnHandleTop,
32950
- left: Math.max(8, columnHandle.center - 12)
32951
- },
32952
- children: /* @__PURE__ */ jsx83(
32953
- Tooltip,
32954
- {
32955
- placement: "top",
32956
- disabled: openMenuKey === menuKey || !visible && isActive,
32957
- content: /* @__PURE__ */ jsx83("span", { className: "text-xs font-medium", children: `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}` }),
32958
- children: /* @__PURE__ */ jsx83("span", { className: "inline-flex", children: /* @__PURE__ */ jsx83(
32959
- DropdownMenu,
32960
- {
32961
- placement: "bottom-start",
32962
- isOpen: openMenuKey === menuKey,
32963
- onOpenChange: (open) => {
32964
- setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
32965
- },
32966
- contentClassName: "p-2",
32967
- items: getColumnHandleMenuItems(columnHandle),
32968
- trigger: /* @__PURE__ */ jsx83(
32969
- "button",
32970
- {
32971
- type: "button",
32972
- "aria-label": `${t("tableMenu.dragColumn")} ${columnHandle.index + 1}`,
32973
- onMouseDown: (event) => {
32974
- event.preventDefault();
32975
- event.stopPropagation();
32976
- setOpenMenuKey(null);
32977
- dragStateRef.current = {
32978
- kind: "column",
32979
- originIndex: columnHandle.index,
32980
- targetIndex: columnHandle.index,
32981
- anchorPos: columnHandle.cellPos
32982
- };
32983
- setDragPreview({
32984
- kind: "column",
32985
- originIndex: columnHandle.index,
32986
- targetIndex: columnHandle.index,
32987
- targetStart: columnHandle.start,
32988
- targetSize: columnHandle.size
32989
- });
32990
- document.body.style.cursor = "grabbing";
32991
- },
32992
- className: cn(
32993
- "inline-flex h-6 w-6 items-center justify-center rounded-full transition-[opacity,transform,colors,border,background-color] duration-150",
32994
- visible ? "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur hover:bg-accent hover:text-foreground cursor-grab active:cursor-grabbing" : "border-transparent bg-transparent cursor-pointer"
32995
- ),
32996
- style: {
32997
- opacity: isShown ? 1 : 0,
32998
- transform: isShown ? "scale(1)" : `scale(${IDLE_HANDLE_SCALE})`,
32999
- pointerEvents: isShown ? "auto" : "none"
33000
- },
33001
- children: visible ? /* @__PURE__ */ jsx83(GripHorizontal, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx83("div", { className: "h-1 w-3 rounded-full bg-muted-foreground/50 hover:bg-muted-foreground" })
33002
- }
33003
- )
33004
- }
33005
- ) })
33006
- }
33007
- )
33008
- },
33009
- `column-handle-${columnHandle.index}`
33010
- );
33011
- }),
33012
- /* @__PURE__ */ jsx83(
33013
- "div",
33656
+ return /* @__PURE__ */ jsxs72(Fragment33, { children: [
33657
+ /* @__PURE__ */ jsx87(
33658
+ TableRowHandles,
33014
33659
  {
33015
- className: "absolute z-30",
33016
- "data-table-control": "table-menu",
33017
- style: {
33018
- top: menuTop,
33019
- left: menuLeft
33660
+ activeRowIndex: layout.activeRowIndex,
33661
+ controlsVisible,
33662
+ getMenuItems: getRowHandleMenuItems,
33663
+ hoverRowHandleIndex: hoverState.rowHandleIndex,
33664
+ onOpenMenuChange: (menuKey, open) => {
33665
+ setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
33020
33666
  },
33021
- children: /* @__PURE__ */ jsx83(
33022
- Tooltip,
33023
- {
33024
- placement: "top",
33025
- disabled: tableMenuOpen,
33026
- content: /* @__PURE__ */ jsx83("span", { className: "text-xs font-medium", children: t("tableMenu.openControls") }),
33027
- children: /* @__PURE__ */ jsx83("span", { className: "inline-flex", children: /* @__PURE__ */ jsx83(
33028
- DropdownMenu,
33029
- {
33030
- placement: "bottom-start",
33031
- isOpen: tableMenuOpen,
33032
- onOpenChange: (open) => {
33033
- setOpenMenuKey((prev) => open ? "table" : prev === "table" ? null : prev);
33034
- },
33035
- contentClassName: "p-2",
33036
- items: menuItems,
33037
- trigger: /* @__PURE__ */ jsx83(
33038
- "button",
33039
- {
33040
- type: "button",
33041
- "aria-label": t("tableMenu.openControls"),
33042
- onMouseDown: (event) => event.preventDefault(),
33043
- className: cn(
33044
- "pointer-events-auto inline-flex h-7 w-7 items-center justify-center rounded-full",
33045
- "border border-border/70 bg-background/95 text-muted-foreground shadow-sm backdrop-blur",
33046
- "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground"
33047
- ),
33048
- style: {
33049
- opacity: controlsVisible || hoverState.menuVisible || tableMenuOpen ? 1 : 0,
33050
- transform: controlsVisible || hoverState.menuVisible || tableMenuOpen ? "scale(1)" : "scale(0.82)",
33051
- pointerEvents: controlsVisible || hoverState.menuVisible || tableMenuOpen ? "auto" : "none"
33052
- },
33053
- children: /* @__PURE__ */ jsx83(MoreHorizontal2, { className: "h-4 w-4" })
33054
- }
33055
- )
33056
- }
33057
- ) })
33058
- }
33059
- )
33667
+ onStartDrag: startRowDrag,
33668
+ openMenuKey,
33669
+ rowDragLabel: t("tableMenu.dragRow"),
33670
+ rowHandleLeft,
33671
+ rowHandles: layout.rowHandles
33060
33672
  }
33061
33673
  ),
33062
- /* @__PURE__ */ jsx83(
33063
- Tooltip,
33674
+ /* @__PURE__ */ jsx87(
33675
+ TableColumnHandles,
33064
33676
  {
33065
- placement: "right",
33066
- content: /* @__PURE__ */ jsx83("span", { className: "text-xs font-medium", children: t("tableMenu.quickAddColumnAfter") }),
33067
- children: /* @__PURE__ */ jsx83(
33068
- "button",
33069
- {
33070
- type: "button",
33071
- "data-table-control": "add-column",
33072
- "aria-label": t("tableMenu.quickAddColumnAfter"),
33073
- onMouseDown: (event) => {
33074
- event.preventDefault();
33075
- event.stopPropagation();
33076
- setOpenMenuKey(null);
33077
- if (!canExpandTable) return;
33078
- dragStateRef.current = { kind: "add-column", previewCols: 1 };
33079
- setDragPreview({ kind: "add-column", previewCols: 1 });
33080
- document.body.style.cursor = "ew-resize";
33081
- },
33082
- disabled: !canExpandTable,
33083
- className: cn(
33084
- "absolute z-30 inline-flex items-center justify-center rounded-md",
33085
- "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
33086
- "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
33087
- ),
33088
- style: {
33089
- top: controlsVisible || hoverState.addColumnVisible ? columnRailTop : columnRailTop + Math.max(0, visibleTableHeight / 2 - 24),
33090
- left: columnRailLeft,
33091
- width: controlsVisible || hoverState.addColumnVisible ? 18 : 12,
33092
- height: controlsVisible || hoverState.addColumnVisible ? visibleTableHeight : 48,
33093
- opacity: controlsVisible || hoverState.addColumnVisible ? 1 : 0,
33094
- transform: controlsVisible || hoverState.addColumnVisible ? "scale(1)" : "scale(0.92)",
33095
- pointerEvents: controlsVisible || hoverState.addColumnVisible ? "auto" : "none"
33096
- },
33097
- children: /* @__PURE__ */ jsx83("span", { className: "text-sm font-medium leading-none", children: "+" })
33098
- }
33099
- )
33677
+ activeColumnIndex: layout.activeColumnIndex,
33678
+ columnDragLabel: t("tableMenu.dragColumn"),
33679
+ columnHandleTop,
33680
+ columnHandles: layout.columnHandles,
33681
+ controlsVisible,
33682
+ getMenuItems: getColumnHandleMenuItems,
33683
+ hoverColumnHandleIndex: hoverState.columnHandleIndex,
33684
+ onOpenMenuChange: (menuKey, open) => {
33685
+ setOpenMenuKey((prev) => open ? menuKey : prev === menuKey ? null : prev);
33686
+ },
33687
+ onStartDrag: startColumnDrag,
33688
+ openMenuKey
33100
33689
  }
33101
33690
  ),
33102
- /* @__PURE__ */ jsx83(
33103
- Tooltip,
33691
+ /* @__PURE__ */ jsx87(
33692
+ TableControlMenu,
33104
33693
  {
33105
- placement: "bottom",
33106
- content: /* @__PURE__ */ jsx83("span", { className: "text-xs font-medium", children: t("tableMenu.quickAddRowAfter") }),
33107
- children: /* @__PURE__ */ jsx83(
33108
- "button",
33109
- {
33110
- type: "button",
33111
- "data-table-control": "add-row",
33112
- "aria-label": t("tableMenu.quickAddRowAfter"),
33113
- onMouseDown: (event) => {
33114
- event.preventDefault();
33115
- event.stopPropagation();
33116
- setOpenMenuKey(null);
33117
- if (!canExpandTable) return;
33118
- dragStateRef.current = { kind: "add-row", previewRows: 1 };
33119
- setDragPreview({ kind: "add-row", previewRows: 1 });
33120
- document.body.style.cursor = "ns-resize";
33121
- },
33122
- disabled: !canExpandTable,
33123
- className: cn(
33124
- "absolute z-30 inline-flex items-center justify-center rounded-md",
33125
- "border border-border/70 bg-muted/40 text-muted-foreground shadow-sm backdrop-blur",
33126
- "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
33127
- ),
33128
- style: {
33129
- top: rowRailTop,
33130
- left: controlsVisible || hoverState.addRowVisible ? rowRailLeft : rowRailLeft + Math.max(0, visibleTableWidth / 2 - 24),
33131
- width: controlsVisible || hoverState.addRowVisible ? visibleTableWidth : 48,
33132
- height: controlsVisible || hoverState.addRowVisible ? 16 : 12,
33133
- opacity: controlsVisible || hoverState.addRowVisible ? 1 : 0,
33134
- transform: controlsVisible || hoverState.addRowVisible ? "scale(1)" : "scale(0.92)",
33135
- pointerEvents: controlsVisible || hoverState.addRowVisible ? "auto" : "none"
33136
- },
33137
- children: /* @__PURE__ */ jsx83("span", { className: "text-sm font-medium leading-none", children: "+" })
33138
- }
33139
- )
33694
+ controlsVisible,
33695
+ isOpen: tableMenuOpen,
33696
+ items: menuItems,
33697
+ label: t("tableMenu.openControls"),
33698
+ left: menuLeft,
33699
+ menuVisible: hoverState.menuVisible,
33700
+ onOpenChange: (open) => {
33701
+ setOpenMenuKey((prev) => open ? "table" : prev === "table" ? null : prev);
33702
+ },
33703
+ top: menuTop
33140
33704
  }
33141
33705
  ),
33142
- dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment29, { children: [
33143
- /* @__PURE__ */ jsx83(
33144
- "div",
33145
- {
33146
- "aria-hidden": "true",
33147
- className: "pointer-events-none absolute z-20 rounded-lg border border-primary/20 bg-primary/10",
33148
- style: {
33149
- top: dragPreview.targetStart,
33150
- left: layout.tableLeft,
33151
- width: layout.tableWidth,
33152
- height: dragPreview.targetSize
33153
- }
33154
- }
33155
- ),
33156
- /* @__PURE__ */ jsx83(
33157
- "div",
33158
- {
33159
- "aria-hidden": "true",
33160
- className: "pointer-events-none absolute z-20 rounded-full bg-primary/80",
33161
- style: {
33162
- top: dragPreview.targetStart + dragPreview.targetSize / 2 - 1,
33163
- left: layout.tableLeft,
33164
- width: layout.tableWidth,
33165
- height: 2
33166
- }
33167
- }
33168
- )
33169
- ] }),
33170
- dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment29, { children: [
33171
- /* @__PURE__ */ jsx83(
33172
- "div",
33173
- {
33174
- "aria-hidden": "true",
33175
- className: "pointer-events-none absolute z-20 rounded-lg border border-primary/20 bg-primary/10",
33176
- style: {
33177
- top: layout.tableTop,
33178
- left: dragPreview.targetStart,
33179
- width: dragPreview.targetSize,
33180
- height: layout.tableHeight
33181
- }
33182
- }
33183
- ),
33184
- /* @__PURE__ */ jsx83(
33185
- "div",
33186
- {
33187
- "aria-hidden": "true",
33188
- className: "pointer-events-none absolute z-20 rounded-full bg-primary/80",
33189
- style: {
33190
- top: layout.tableTop,
33191
- left: dragPreview.targetStart + dragPreview.targetSize / 2 - 1,
33192
- width: 2,
33193
- height: layout.tableHeight
33194
- }
33195
- }
33196
- )
33197
- ] }),
33198
- (dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column") && /* @__PURE__ */ jsx83(Fragment29, { children: /* @__PURE__ */ jsx83(
33199
- "div",
33706
+ /* @__PURE__ */ jsx87(
33707
+ TableAddRails,
33200
33708
  {
33201
- "aria-hidden": "true",
33202
- className: "pointer-events-none absolute z-20 rounded-xl border border-dashed border-primary/70 bg-primary/5",
33203
- style: {
33204
- top: layout.tableTop,
33205
- left: layout.tableLeft,
33206
- width: expandPreviewWidth,
33207
- height: expandPreviewHeight
33208
- }
33709
+ addColumnVisible: hoverState.addColumnVisible,
33710
+ addRowVisible: hoverState.addRowVisible,
33711
+ canExpandTable,
33712
+ controlsVisible,
33713
+ layout,
33714
+ onStartAddColumn: startAddColumnDrag,
33715
+ onStartAddRow: startAddRowDrag,
33716
+ quickAddColumnLabel: t("tableMenu.quickAddColumnAfter"),
33717
+ quickAddRowLabel: t("tableMenu.quickAddRowAfter")
33209
33718
  }
33210
- ) }),
33211
- dragStatusText && /* @__PURE__ */ jsx83(
33212
- "div",
33719
+ ),
33720
+ /* @__PURE__ */ jsx87(
33721
+ TableDragPreview,
33213
33722
  {
33214
- role: "status",
33215
- className: "pointer-events-none absolute z-30 rounded-full border border-primary/20 bg-background/95 px-2 py-1 text-[11px] font-medium text-foreground shadow-sm backdrop-blur",
33216
- style: {
33217
- top: dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column" ? layout.tableTop + expandPreviewHeight + 8 : layout.tableTop - 40,
33218
- left: dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column" ? layout.tableLeft + Math.max(0, expandPreviewWidth - 84) : layout.tableLeft + Math.max(0, layout.tableWidth - 108)
33219
- },
33220
- children: dragStatusText
33723
+ columnDragLabel: t("tableMenu.dragColumn"),
33724
+ dragPreview,
33725
+ layout,
33726
+ rowDragLabel: t("tableMenu.dragRow")
33221
33727
  }
33222
33728
  )
33223
33729
  ] });
@@ -33816,7 +34322,7 @@ function useUEditorTableInteractions(editor, editable = true) {
33816
34322
  }
33817
34323
 
33818
34324
  // src/components/UEditor/UEditor.tsx
33819
- import { jsx as jsx84, jsxs as jsxs71 } from "react/jsx-runtime";
34325
+ import { jsx as jsx88, jsxs as jsxs73 } from "react/jsx-runtime";
33820
34326
  var UEditor = React78.forwardRef(({
33821
34327
  content = "",
33822
34328
  onChange,
@@ -33913,6 +34419,7 @@ var UEditor = React78.forwardRef(({
33913
34419
  useImperativeHandle3(
33914
34420
  ref,
33915
34421
  () => ({
34422
+ editor,
33916
34423
  prepareContentForSave: async ({ throwOnError = false } = {}) => {
33917
34424
  if (!inFlightPrepareRef.current) {
33918
34425
  const htmlSnapshot = editor?.getHTML() ?? content ?? "";
@@ -33943,7 +34450,7 @@ var UEditor = React78.forwardRef(({
33943
34450
  }
33944
34451
  }, [content, editor]);
33945
34452
  if (!editor) {
33946
- return /* @__PURE__ */ jsx84(
34453
+ return /* @__PURE__ */ jsx88(
33947
34454
  "div",
33948
34455
  {
33949
34456
  className: cn("w-full rounded-lg border bg-background flex items-center justify-center text-muted-foreground", className),
@@ -33952,7 +34459,7 @@ var UEditor = React78.forwardRef(({
33952
34459
  }
33953
34460
  );
33954
34461
  }
33955
- return /* @__PURE__ */ jsxs71(
34462
+ return /* @__PURE__ */ jsxs73(
33956
34463
  "div",
33957
34464
  {
33958
34465
  className: cn(
@@ -33967,7 +34474,7 @@ var UEditor = React78.forwardRef(({
33967
34474
  className
33968
34475
  ),
33969
34476
  children: [
33970
- editable && showToolbar && /* @__PURE__ */ jsx84(
34477
+ editable && showToolbar && /* @__PURE__ */ jsx88(
33971
34478
  EditorToolbar,
33972
34479
  {
33973
34480
  editor,
@@ -33982,7 +34489,7 @@ var UEditor = React78.forwardRef(({
33982
34489
  letterSpacings
33983
34490
  }
33984
34491
  ),
33985
- editable && showBubbleMenu && /* @__PURE__ */ jsx84(
34492
+ editable && showBubbleMenu && /* @__PURE__ */ jsx88(
33986
34493
  CustomBubbleMenu,
33987
34494
  {
33988
34495
  editor,
@@ -33990,8 +34497,8 @@ var UEditor = React78.forwardRef(({
33990
34497
  lineHeights
33991
34498
  }
33992
34499
  ),
33993
- editable && showFloatingMenu && /* @__PURE__ */ jsx84(CustomFloatingMenu, { editor }),
33994
- /* @__PURE__ */ jsxs71(
34500
+ editable && showFloatingMenu && /* @__PURE__ */ jsx88(CustomFloatingMenu, { editor }),
34501
+ /* @__PURE__ */ jsxs73(
33995
34502
  "div",
33996
34503
  {
33997
34504
  ref: editorContentRef,
@@ -34001,7 +34508,7 @@ var UEditor = React78.forwardRef(({
34001
34508
  maxHeight
34002
34509
  },
34003
34510
  children: [
34004
- /* @__PURE__ */ jsx84(
34511
+ /* @__PURE__ */ jsx88(
34005
34512
  "span",
34006
34513
  {
34007
34514
  ref: tableColumnGuideRef,
@@ -34009,7 +34516,7 @@ var UEditor = React78.forwardRef(({
34009
34516
  className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
34010
34517
  }
34011
34518
  ),
34012
- /* @__PURE__ */ jsx84(
34519
+ /* @__PURE__ */ jsx88(
34013
34520
  "span",
34014
34521
  {
34015
34522
  ref: tableRowGuideRef,
@@ -34017,7 +34524,7 @@ var UEditor = React78.forwardRef(({
34017
34524
  className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
34018
34525
  }
34019
34526
  ),
34020
- /* @__PURE__ */ jsx84(
34527
+ /* @__PURE__ */ jsx88(
34021
34528
  "span",
34022
34529
  {
34023
34530
  ref: activeTableCellHighlightRef,
@@ -34026,8 +34533,8 @@ var UEditor = React78.forwardRef(({
34026
34533
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10 transition-[left,top,width,height] duration-100"
34027
34534
  }
34028
34535
  ),
34029
- editable && /* @__PURE__ */ jsx84(TableControls, { editor, containerRef: editorContentRef }),
34030
- /* @__PURE__ */ jsx84(
34536
+ editable && /* @__PURE__ */ jsx88(TableControls, { editor, containerRef: editorContentRef }),
34537
+ /* @__PURE__ */ jsx88(
34031
34538
  EditorContent,
34032
34539
  {
34033
34540
  editor,
@@ -34037,7 +34544,7 @@ var UEditor = React78.forwardRef(({
34037
34544
  ]
34038
34545
  }
34039
34546
  ),
34040
- showCharacterCount && /* @__PURE__ */ jsx84(CharacterCountDisplay, { editor, maxCharacters })
34547
+ showCharacterCount && /* @__PURE__ */ jsx88(CharacterCountDisplay, { editor, maxCharacters })
34041
34548
  ]
34042
34549
  }
34043
34550
  );