@underverse-ui/underverse 1.0.156 → 1.0.158

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.cjs CHANGED
@@ -6042,7 +6042,7 @@ var Modal = ({
6042
6042
  const modalContent = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6043
6043
  "div",
6044
6044
  {
6045
- className: cn("fixed inset-0 z-9999 flex items-center justify-center p-4 md:p-6", overlayClassName),
6045
+ className: cn("fixed inset-0 z-[9999] flex items-center justify-center p-4 md:p-6", overlayClassName),
6046
6046
  style: { overscrollBehavior: "contain" },
6047
6047
  onMouseDown: handleOverlayMouseDown,
6048
6048
  onMouseUp: handleOverlayMouseUp,
@@ -27217,6 +27217,111 @@ var import_react55 = require("react");
27217
27217
  var import_extension_image = __toESM(require("@tiptap/extension-image"), 1);
27218
27218
  var import_core9 = require("@tiptap/core");
27219
27219
  var import_react56 = require("@tiptap/react");
27220
+
27221
+ // src/components/UEditor/table-dom-utils.ts
27222
+ var MIN_TABLE_ROW_HEIGHT = 36;
27223
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
27224
+ var ROW_RESIZE_LINE_THICKNESS = 2;
27225
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
27226
+ var TABLE_RESIZE_HIT_ZONE = 10;
27227
+ function findTableRowNodeInfo(view, rowElement) {
27228
+ const firstCell = rowElement.querySelector("th,td");
27229
+ if (!firstCell) return null;
27230
+ const cellPos = view.posAtDOM(firstCell, 0);
27231
+ const $pos = view.state.doc.resolve(cellPos);
27232
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
27233
+ const node = $pos.node(depth);
27234
+ if (node.type.name === "tableRow") {
27235
+ return {
27236
+ pos: $pos.before(depth),
27237
+ node
27238
+ };
27239
+ }
27240
+ }
27241
+ return null;
27242
+ }
27243
+ function resolveEventElement(target) {
27244
+ if (target instanceof Element) return target;
27245
+ if (target instanceof Node) return target.parentElement;
27246
+ return null;
27247
+ }
27248
+ function getSelectionTableCell(view) {
27249
+ const browserSelection = window.getSelection();
27250
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
27251
+ const anchorCell = anchorElement?.closest?.("th,td");
27252
+ if (anchorCell instanceof HTMLElement) {
27253
+ return anchorCell;
27254
+ }
27255
+ const { from } = view.state.selection;
27256
+ const domAtPos = view.domAtPos(from);
27257
+ const element = resolveEventElement(domAtPos.node);
27258
+ const cell = element?.closest?.("th,td");
27259
+ return cell instanceof HTMLElement ? cell : null;
27260
+ }
27261
+ function isRowResizeHotspot(cell, clientX, clientY) {
27262
+ const rect = cell.getBoundingClientRect();
27263
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27264
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27265
+ return nearBottom && !nearRight;
27266
+ }
27267
+ function isColumnResizeHotspot(cell, clientX, clientY) {
27268
+ const rect = cell.getBoundingClientRect();
27269
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27270
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27271
+ return nearRight && !nearBottom;
27272
+ }
27273
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
27274
+ const surfaceRect = surface.getBoundingClientRect();
27275
+ const tableRect = table.getBoundingClientRect();
27276
+ const rowRect = row.getBoundingClientRect();
27277
+ const cellRect = cell.getBoundingClientRect();
27278
+ return {
27279
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
27280
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
27281
+ width: tableRect.width,
27282
+ height: tableRect.height,
27283
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
27284
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
27285
+ };
27286
+ }
27287
+ function getRelativeCellMetrics(surface, cell) {
27288
+ const surfaceRect = surface.getBoundingClientRect();
27289
+ const cellRect = cell.getBoundingClientRect();
27290
+ return {
27291
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
27292
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
27293
+ width: cellRect.width,
27294
+ height: cellRect.height
27295
+ };
27296
+ }
27297
+ function getRelativeSelectedCellsMetrics(surface) {
27298
+ const selectedCells = Array.from(
27299
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
27300
+ );
27301
+ if (selectedCells.length === 0) {
27302
+ return null;
27303
+ }
27304
+ const surfaceRect = surface.getBoundingClientRect();
27305
+ let left = Number.POSITIVE_INFINITY;
27306
+ let top = Number.POSITIVE_INFINITY;
27307
+ let right = Number.NEGATIVE_INFINITY;
27308
+ let bottom = Number.NEGATIVE_INFINITY;
27309
+ selectedCells.forEach((cell) => {
27310
+ const rect = cell.getBoundingClientRect();
27311
+ left = Math.min(left, rect.left);
27312
+ top = Math.min(top, rect.top);
27313
+ right = Math.max(right, rect.right);
27314
+ bottom = Math.max(bottom, rect.bottom);
27315
+ });
27316
+ return {
27317
+ left: left - surfaceRect.left + surface.scrollLeft,
27318
+ top: top - surfaceRect.top + surface.scrollTop,
27319
+ width: right - left,
27320
+ height: bottom - top
27321
+ };
27322
+ }
27323
+
27324
+ // src/components/UEditor/resizable-image.tsx
27220
27325
  var import_jsx_runtime82 = require("react/jsx-runtime");
27221
27326
  var MIN_IMAGE_SIZE_PX = 40;
27222
27327
  var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
@@ -27298,6 +27403,7 @@ function ResizableImageNodeView(props) {
27298
27403
  const { node, selected, updateAttributes, editor, getPos } = props;
27299
27404
  const wrapperRef = (0, import_react55.useRef)(null);
27300
27405
  const imgRef = (0, import_react55.useRef)(null);
27406
+ const resizePreviewRef = (0, import_react55.useRef)(null);
27301
27407
  const [isHovered, setIsHovered] = (0, import_react55.useState)(false);
27302
27408
  const [isResizing, setIsResizing] = (0, import_react55.useState)(false);
27303
27409
  const widthAttr = toNullableNumber(node.attrs["width"]);
@@ -27305,11 +27411,59 @@ function ResizableImageNodeView(props) {
27305
27411
  const textAlign = String(node.attrs["textAlign"] ?? "");
27306
27412
  const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
27307
27413
  const dragStateRef = (0, import_react55.useRef)(null);
27414
+ const dispatchTableLayoutChange2 = () => {
27415
+ editor.view.dom.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, { bubbles: true }));
27416
+ };
27417
+ const getImageDisplayStyle = (width, height) => ({
27418
+ width: width ? `${width}px` : void 0,
27419
+ height: width && height ? "auto" : height ? `${height}px` : void 0,
27420
+ aspectRatio: width && height ? `${width} / ${height}` : void 0
27421
+ });
27422
+ const setResizePreviewStyle = (width, height, visible, resetBase = false) => {
27423
+ const preview = resizePreviewRef.current;
27424
+ if (!preview) return;
27425
+ const drag = dragStateRef.current;
27426
+ const baseW = resetBase || !drag ? Math.max(MIN_IMAGE_SIZE_PX, Math.round(width)) : drag.previewBaseW;
27427
+ const baseH = resetBase || !drag ? Math.max(MIN_IMAGE_SIZE_PX, Math.round(height)) : drag.previewBaseH;
27428
+ const scaleX = width / baseW;
27429
+ const scaleY = height / baseH;
27430
+ if (resetBase) {
27431
+ preview.style.width = `${baseW}px`;
27432
+ preview.style.height = `${baseH}px`;
27433
+ }
27434
+ preview.style.maxWidth = "none";
27435
+ preview.style.maxHeight = "none";
27436
+ preview.style.transform = visible ? `translateZ(0) scale(${scaleX}, ${scaleY})` : "translateZ(0) scale(1)";
27437
+ preview.style.display = visible ? "block" : "none";
27438
+ };
27439
+ const applyPendingResizeFrame = () => {
27440
+ const drag = dragStateRef.current;
27441
+ if (!drag) return;
27442
+ drag.frameId = null;
27443
+ setResizePreviewStyle(drag.pendingW, drag.pendingH, true);
27444
+ };
27445
+ const scheduleResizeFrame = () => {
27446
+ const drag = dragStateRef.current;
27447
+ if (!drag || drag.frameId !== null) return;
27448
+ drag.frameId = window.requestAnimationFrame(applyPendingResizeFrame);
27449
+ };
27450
+ (0, import_react55.useEffect)(() => {
27451
+ return () => {
27452
+ const drag = dragStateRef.current;
27453
+ if (drag && drag.frameId !== null) {
27454
+ window.cancelAnimationFrame(drag.frameId);
27455
+ }
27456
+ dragStateRef.current = null;
27457
+ document.body.style.cursor = "";
27458
+ };
27459
+ }, []);
27308
27460
  (0, import_react55.useEffect)(() => {
27309
27461
  const img = imgRef.current;
27310
27462
  if (!img) return;
27311
- img.style.width = widthAttr ? `${widthAttr}px` : "";
27312
- img.style.height = heightAttr ? `${heightAttr}px` : "";
27463
+ const displayStyle = getImageDisplayStyle(widthAttr, heightAttr);
27464
+ img.style.width = typeof displayStyle.width === "string" ? displayStyle.width : "";
27465
+ img.style.height = typeof displayStyle.height === "string" ? displayStyle.height : "";
27466
+ img.style.aspectRatio = typeof displayStyle.aspectRatio === "string" ? displayStyle.aspectRatio : "";
27313
27467
  }, [widthAttr, heightAttr]);
27314
27468
  const selectNode = () => {
27315
27469
  const pos = typeof getPos === "function" ? getPos() : null;
@@ -27340,12 +27494,17 @@ function ResizableImageNodeView(props) {
27340
27494
  startY: event.clientY,
27341
27495
  startW,
27342
27496
  startH,
27343
- lastW: startW,
27344
- lastH: startH,
27345
27497
  aspect,
27346
- maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW)
27498
+ maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW),
27499
+ previewBaseW: Math.max(MIN_IMAGE_SIZE_PX, Math.round(startW)),
27500
+ previewBaseH: Math.max(MIN_IMAGE_SIZE_PX, Math.round(startH)),
27501
+ pendingW: startW,
27502
+ pendingH: startH,
27503
+ frameId: null
27347
27504
  };
27348
27505
  setIsResizing(true);
27506
+ setResizePreviewStyle(startW, startH, true, true);
27507
+ document.body.style.cursor = "nwse-resize";
27349
27508
  event.currentTarget.setPointerCapture(event.pointerId);
27350
27509
  };
27351
27510
  const onResizePointerMove = (event) => {
@@ -27358,19 +27517,33 @@ function ResizableImageNodeView(props) {
27358
27517
  const nextSize = Math.abs(dx) >= Math.abs(dy) ? sizeFromWidth(drag.startW + dx, drag.aspect, drag.maxW) : sizeFromHeight(drag.startH + dy, drag.aspect, drag.maxW);
27359
27518
  const nextW = nextSize.width;
27360
27519
  const nextH = nextSize.height;
27361
- drag.lastW = nextW;
27362
- drag.lastH = nextH;
27363
- img.style.width = `${Math.round(nextW)}px`;
27364
- img.style.height = `${Math.round(nextH)}px`;
27520
+ drag.pendingW = nextW;
27521
+ drag.pendingH = nextH;
27522
+ scheduleResizeFrame();
27365
27523
  };
27366
27524
  const finishResize = () => {
27367
27525
  const drag = dragStateRef.current;
27368
27526
  dragStateRef.current = null;
27369
27527
  setIsResizing(false);
27528
+ document.body.style.cursor = "";
27370
27529
  if (!drag) return;
27530
+ if (drag.frameId !== null) {
27531
+ window.cancelAnimationFrame(drag.frameId);
27532
+ drag.frameId = null;
27533
+ }
27534
+ const img = imgRef.current;
27535
+ const nextW = Math.round(drag.pendingW);
27536
+ const nextH = Math.round(drag.pendingH);
27537
+ setResizePreviewStyle(nextW, nextH, false);
27538
+ if (img) {
27539
+ img.style.width = `${nextW}px`;
27540
+ img.style.height = "auto";
27541
+ img.style.aspectRatio = `${nextW} / ${nextH}`;
27542
+ }
27543
+ dispatchTableLayoutChange2();
27371
27544
  updateAttributes({
27372
- width: Math.round(drag.lastW),
27373
- height: Math.round(drag.lastH),
27545
+ width: nextW,
27546
+ height: nextH,
27374
27547
  imageWidthPreset: null
27375
27548
  });
27376
27549
  };
@@ -27419,9 +27592,30 @@ function ResizableImageNodeView(props) {
27419
27592
  selected ? "ring-2 ring-primary/60 ring-offset-2 ring-offset-background" : "",
27420
27593
  isResizing ? "select-none" : ""
27421
27594
  ].join(" "),
27595
+ style: getImageDisplayStyle(widthAttr, heightAttr)
27596
+ }
27597
+ ),
27598
+ /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
27599
+ "div",
27600
+ {
27601
+ ref: resizePreviewRef,
27602
+ "aria-hidden": "true",
27603
+ "data-ueditor-image-resize-preview": "",
27604
+ className: [
27605
+ "pointer-events-none absolute left-0 top-0 z-20 hidden rounded-lg",
27606
+ "border border-primary/70 bg-background/30 bg-center bg-no-repeat bg-[length:100%_100%]",
27607
+ "opacity-45 shadow-md ring-2 ring-primary/25 will-change-transform",
27608
+ "select-none"
27609
+ ].join(" "),
27422
27610
  style: {
27423
27611
  width: widthAttr ? `${widthAttr}px` : void 0,
27424
- height: heightAttr ? `${heightAttr}px` : void 0
27612
+ height: heightAttr ? `${heightAttr}px` : void 0,
27613
+ maxWidth: "none",
27614
+ maxHeight: "none",
27615
+ backgroundImage: `url("${String(node.attrs["src"] ?? "").replace(/"/g, "%22")}")`,
27616
+ transform: "translateZ(0) scale(1)",
27617
+ transformOrigin: "top left",
27618
+ display: isResizing ? "block" : "none"
27425
27619
  }
27426
27620
  }
27427
27621
  ),
@@ -27429,6 +27623,7 @@ function ResizableImageNodeView(props) {
27429
27623
  "div",
27430
27624
  {
27431
27625
  "aria-hidden": "true",
27626
+ "data-ueditor-image-resize-handle": "",
27432
27627
  onPointerDown: onResizePointerDown,
27433
27628
  onPointerMove: onResizePointerMove,
27434
27629
  onPointerUp: onResizePointerUp,
@@ -27685,109 +27880,6 @@ var letter_spacing_default = LetterSpacing;
27685
27880
  var import_extension_table = require("@tiptap/extension-table");
27686
27881
  var import_state6 = require("@tiptap/pm/state");
27687
27882
 
27688
- // src/components/UEditor/table-dom-utils.ts
27689
- var MIN_TABLE_ROW_HEIGHT = 36;
27690
- var COLUMN_RESIZE_LINE_THICKNESS = 2;
27691
- var ROW_RESIZE_LINE_THICKNESS = 2;
27692
- var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
27693
- var TABLE_RESIZE_HIT_ZONE = 10;
27694
- function findTableRowNodeInfo(view, rowElement) {
27695
- const firstCell = rowElement.querySelector("th,td");
27696
- if (!firstCell) return null;
27697
- const cellPos = view.posAtDOM(firstCell, 0);
27698
- const $pos = view.state.doc.resolve(cellPos);
27699
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
27700
- const node = $pos.node(depth);
27701
- if (node.type.name === "tableRow") {
27702
- return {
27703
- pos: $pos.before(depth),
27704
- node
27705
- };
27706
- }
27707
- }
27708
- return null;
27709
- }
27710
- function resolveEventElement(target) {
27711
- if (target instanceof Element) return target;
27712
- if (target instanceof Node) return target.parentElement;
27713
- return null;
27714
- }
27715
- function getSelectionTableCell(view) {
27716
- const browserSelection = window.getSelection();
27717
- const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
27718
- const anchorCell = anchorElement?.closest?.("th,td");
27719
- if (anchorCell instanceof HTMLElement) {
27720
- return anchorCell;
27721
- }
27722
- const { from } = view.state.selection;
27723
- const domAtPos = view.domAtPos(from);
27724
- const element = resolveEventElement(domAtPos.node);
27725
- const cell = element?.closest?.("th,td");
27726
- return cell instanceof HTMLElement ? cell : null;
27727
- }
27728
- function isRowResizeHotspot(cell, clientX, clientY) {
27729
- const rect = cell.getBoundingClientRect();
27730
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27731
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27732
- return nearBottom && !nearRight;
27733
- }
27734
- function isColumnResizeHotspot(cell, clientX, clientY) {
27735
- const rect = cell.getBoundingClientRect();
27736
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27737
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27738
- return nearRight && !nearBottom;
27739
- }
27740
- function getRelativeBoundaryMetrics(surface, table, row, cell) {
27741
- const surfaceRect = surface.getBoundingClientRect();
27742
- const tableRect = table.getBoundingClientRect();
27743
- const rowRect = row.getBoundingClientRect();
27744
- const cellRect = cell.getBoundingClientRect();
27745
- return {
27746
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
27747
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
27748
- width: tableRect.width,
27749
- height: tableRect.height,
27750
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
27751
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
27752
- };
27753
- }
27754
- function getRelativeCellMetrics(surface, cell) {
27755
- const surfaceRect = surface.getBoundingClientRect();
27756
- const cellRect = cell.getBoundingClientRect();
27757
- return {
27758
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
27759
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
27760
- width: cellRect.width,
27761
- height: cellRect.height
27762
- };
27763
- }
27764
- function getRelativeSelectedCellsMetrics(surface) {
27765
- const selectedCells = Array.from(
27766
- surface.querySelectorAll("td.selectedCell, th.selectedCell")
27767
- );
27768
- if (selectedCells.length === 0) {
27769
- return null;
27770
- }
27771
- const surfaceRect = surface.getBoundingClientRect();
27772
- let left = Number.POSITIVE_INFINITY;
27773
- let top = Number.POSITIVE_INFINITY;
27774
- let right = Number.NEGATIVE_INFINITY;
27775
- let bottom = Number.NEGATIVE_INFINITY;
27776
- selectedCells.forEach((cell) => {
27777
- const rect = cell.getBoundingClientRect();
27778
- left = Math.min(left, rect.left);
27779
- top = Math.min(top, rect.top);
27780
- right = Math.max(right, rect.right);
27781
- bottom = Math.max(bottom, rect.bottom);
27782
- });
27783
- return {
27784
- left: left - surfaceRect.left + surface.scrollLeft,
27785
- top: top - surfaceRect.top + surface.scrollTop,
27786
- width: right - left,
27787
- height: bottom - top
27788
- };
27789
- }
27790
-
27791
27883
  // src/components/UEditor/table-align-utils.ts
27792
27884
  function findTableNodeInfoAtResolvedPos($pos) {
27793
27885
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
@@ -28589,6 +28681,9 @@ var HIGHLIGHT_COLOR_SWATCHES = [
28589
28681
  function buildColorOptions(colors, prefix) {
28590
28682
  return colors.map((color, index) => ({ name: `${prefix} ${index + 1}`, color }));
28591
28683
  }
28684
+ function getSwatchCheckClass(color) {
28685
+ return /^#(?:fff|ffffff)$/i.test(color) ? "text-foreground" : "text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]";
28686
+ }
28592
28687
  var useEditorColors = () => {
28593
28688
  const t = useSmartTranslations("UEditor");
28594
28689
  const textColors = (0, import_react60.useMemo)(
@@ -28663,7 +28758,7 @@ var EditorColorPalette = ({
28663
28758
  currentColor === c.color ? "border-primary ring-2 ring-primary/25" : "border-border/70"
28664
28759
  ),
28665
28760
  style: { backgroundColor: c.color || "transparent" },
28666
- children: currentColor === c.color && /* @__PURE__ */ (0, import_jsx_runtime84.jsx)("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(import_lucide_react47.Check, { className: "h-3.5 w-3.5 text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]" }) })
28761
+ children: currentColor === c.color && /* @__PURE__ */ (0, import_jsx_runtime84.jsx)("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(import_lucide_react47.Check, { className: cn("h-3.5 w-3.5", getSwatchCheckClass(c.color)) }) })
28667
28762
  }
28668
28763
  ) }, `${c.name}-${c.color}`)) }),
28669
28764
  /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(
@@ -28978,6 +29073,20 @@ function createEmptyCellNode(cellNode) {
28978
29073
  function createCellCopyForColumnDuplicate(cellNode) {
28979
29074
  return cellNode.type.create(cellNode.attrs, cellNode.content);
28980
29075
  }
29076
+ function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
29077
+ const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
29078
+ let nextColwidth = null;
29079
+ if (Array.isArray(cellNode.attrs.colwidth)) {
29080
+ nextColwidth = [...cellNode.attrs.colwidth];
29081
+ const duplicateWidth = nextColwidth[widthIndex];
29082
+ nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
29083
+ }
29084
+ return cellNode.type.create({
29085
+ ...cellNode.attrs,
29086
+ colspan: colspan + 1,
29087
+ ...nextColwidth ? { colwidth: nextColwidth } : null
29088
+ }, cellNode.content);
29089
+ }
28981
29090
  function getTableRows(tableNode) {
28982
29091
  const rows = [];
28983
29092
  tableNode.forEach((rowNode, rowOffset) => {
@@ -29153,6 +29262,11 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
29153
29262
  return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
29154
29263
  });
29155
29264
  if (!sourceCell) return rowInfo.node;
29265
+ const sourceRect = safeFindCell(map, sourceCell.relativePos);
29266
+ if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
29267
+ cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
29268
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29269
+ }
29156
29270
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
29157
29271
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29158
29272
  });
@@ -30452,6 +30566,25 @@ function getTableFormulaRecalculationOrder(graph) {
30452
30566
  }
30453
30567
  return { order, circular };
30454
30568
  }
30569
+ function getAffectedTableFormulaLabels(graph, changedLabels) {
30570
+ const affected = /* @__PURE__ */ new Set();
30571
+ const queue = Array.from(changedLabels, (label) => label.toUpperCase());
30572
+ for (const label of queue) {
30573
+ if (graph.formulas.has(label)) {
30574
+ affected.add(label);
30575
+ }
30576
+ }
30577
+ for (let index = 0; index < queue.length; index += 1) {
30578
+ const label = queue[index];
30579
+ if (!label) continue;
30580
+ for (const dependent of graph.dependents.get(label) ?? []) {
30581
+ if (affected.has(dependent)) continue;
30582
+ affected.add(dependent);
30583
+ queue.push(dependent);
30584
+ }
30585
+ }
30586
+ return affected;
30587
+ }
30455
30588
  function evaluateBasicTableFormula(formula, getCellValue) {
30456
30589
  const normalized = normalizeTableFormula(formula);
30457
30590
  if (!normalized) {
@@ -30599,10 +30732,19 @@ var FormulaParser = class {
30599
30732
  const range = parseTableCellRange(token.value);
30600
30733
  if (!range) return { value: null, error: "invalid-reference" };
30601
30734
  for (const label of getTableCellRangeLabels(range)) {
30735
+ if (name === "COUNT") {
30736
+ const cellValue2 = this.readOptionalCellNumber(label);
30737
+ if (cellValue2 != null) values.push(cellValue2);
30738
+ continue;
30739
+ }
30602
30740
  const cellValue = this.readCellNumber(label);
30603
30741
  if (cellValue.error) return cellValue;
30604
30742
  values.push(cellValue.value);
30605
30743
  }
30744
+ } else if (name === "COUNT" && token.type === "cell") {
30745
+ this.index += 1;
30746
+ const cellValue = this.readOptionalCellNumber(token.value);
30747
+ if (cellValue != null) values.push(cellValue);
30606
30748
  } else {
30607
30749
  const value = this.parseExpression();
30608
30750
  if (value.error) return value;
@@ -30616,7 +30758,7 @@ var FormulaParser = class {
30616
30758
  }
30617
30759
  return { value: null, error: "invalid-formula" };
30618
30760
  }
30619
- if (values.length === 0) {
30761
+ if (values.length === 0 && name !== "COUNT") {
30620
30762
  return { value: null, error: "invalid-formula" };
30621
30763
  }
30622
30764
  if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
@@ -30634,6 +30776,11 @@ var FormulaParser = class {
30634
30776
  }
30635
30777
  return { value: parsed, error: null };
30636
30778
  }
30779
+ readOptionalCellNumber(label) {
30780
+ const value = this.getCellValue(label);
30781
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30782
+ return Number.isFinite(parsed) ? parsed : null;
30783
+ }
30637
30784
  peekOperator(operators) {
30638
30785
  const token = this.tokens[this.index];
30639
30786
  return token?.type === "operator" && operators.includes(token.value) ? token : null;
@@ -30697,6 +30844,41 @@ function getSelectionTableCellNode(editor) {
30697
30844
  }
30698
30845
  return null;
30699
30846
  }
30847
+ function getSelectionTableInfo(editor) {
30848
+ const { $from } = editor.state.selection;
30849
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30850
+ const node = $from.node(depth);
30851
+ if (node.type.name === "table") {
30852
+ return {
30853
+ node,
30854
+ pos: $from.before(depth)
30855
+ };
30856
+ }
30857
+ }
30858
+ return null;
30859
+ }
30860
+ function getSelectionTableCellLabel(editor) {
30861
+ const { $from } = editor.state.selection;
30862
+ let cellDepth = -1;
30863
+ let tableDepth = -1;
30864
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30865
+ const node = $from.node(depth);
30866
+ if (cellDepth < 0 && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
30867
+ cellDepth = depth;
30868
+ }
30869
+ if (node.type.name === "table") {
30870
+ tableDepth = depth;
30871
+ break;
30872
+ }
30873
+ }
30874
+ if (cellDepth < 0 || tableDepth < 0) return null;
30875
+ const tableNode = $from.node(tableDepth);
30876
+ const tableStart = $from.start(tableDepth);
30877
+ const relativeCellPos = $from.before(cellDepth) - tableStart;
30878
+ const rect = safeFindCell2(import_tables2.TableMap.get(tableNode), relativeCellPos);
30879
+ if (!rect) return null;
30880
+ return `${indexToColumnName(rect.left)}${rect.top + 1}`;
30881
+ }
30700
30882
  function isEditingTableFormulaText(editor) {
30701
30883
  const cellNode = getSelectionTableCellNode(editor);
30702
30884
  return Boolean(cellNode && getCellText(cellNode).startsWith("="));
@@ -30708,20 +30890,6 @@ function createCellDisplayContent(cellNode, displayValue) {
30708
30890
  }
30709
30891
  return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30710
30892
  }
30711
- function buildTableValueGetter(tableNode) {
30712
- const map = import_tables2.TableMap.get(tableNode);
30713
- const values = /* @__PURE__ */ new Map();
30714
- for (const rowInfo of getTableRows2(tableNode)) {
30715
- for (const entry of rowInfo.cells) {
30716
- const rect = safeFindCell2(map, entry.relativePos);
30717
- if (!rect) continue;
30718
- const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30719
- const computedValue = entry.node.attrs.computedValue;
30720
- values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30721
- }
30722
- }
30723
- return (label) => values.get(label.toUpperCase());
30724
- }
30725
30893
  function buildTableValueMap(tableNode) {
30726
30894
  const map = import_tables2.TableMap.get(tableNode);
30727
30895
  const values = /* @__PURE__ */ new Map();
@@ -30736,10 +30904,6 @@ function buildTableValueMap(tableNode) {
30736
30904
  }
30737
30905
  return values;
30738
30906
  }
30739
- function getFormulaComputedValue(formula, tableNode) {
30740
- const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30741
- return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30742
- }
30743
30907
  function normalizeFormulaInput(formula) {
30744
30908
  const trimmed = formula.trim();
30745
30909
  if (!trimmed) return "";
@@ -30750,19 +30914,19 @@ function setSelectedTableCellFormula(editor, formula) {
30750
30914
  const { state, view } = editor;
30751
30915
  if (!normalized) {
30752
30916
  const clearedFormula = (0, import_tables2.setCellAttr)("formula", null)(state, view.dispatch.bind(view));
30753
- const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30754
- if (clearedFormula || clearedValue) {
30917
+ const clearedValue2 = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30918
+ if (clearedFormula || clearedValue2) {
30919
+ recalculateActiveTableFormulas(editor);
30755
30920
  view.focus();
30756
30921
  dispatchTableLayoutChange(editor);
30757
30922
  return true;
30758
30923
  }
30759
30924
  return false;
30760
30925
  }
30761
- const rect = (0, import_tables2.selectedRect)(state);
30762
- const computedValue = getFormulaComputedValue(normalized, rect.table);
30763
30926
  const appliedFormula = (0, import_tables2.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
30764
- const appliedValue = (0, import_tables2.setCellAttr)("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30765
- if (appliedFormula || appliedValue) {
30927
+ const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30928
+ if (appliedFormula || clearedValue) {
30929
+ recalculateActiveTableFormulas(editor);
30766
30930
  view.focus();
30767
30931
  dispatchTableLayoutChange(editor);
30768
30932
  return true;
@@ -30844,7 +31008,7 @@ function promoteFormulaTextInTableNode(tableNode) {
30844
31008
  changed
30845
31009
  };
30846
31010
  }
30847
- function recalculateTableNode(tableNode) {
31011
+ function recalculateTableNode(tableNode, options) {
30848
31012
  const promoted = promoteFormulaTextInTableNode(tableNode);
30849
31013
  tableNode = promoted.tableNode;
30850
31014
  const map = import_tables2.TableMap.get(tableNode);
@@ -30874,13 +31038,16 @@ function recalculateTableNode(tableNode) {
30874
31038
  }))
30875
31039
  );
30876
31040
  const { order, circular } = getTableFormulaRecalculationOrder(graph);
31041
+ const affectedLabels = options?.changedLabels ? getAffectedTableFormulaLabels(graph, options.changedLabels) : null;
30877
31042
  const computedValues = /* @__PURE__ */ new Map();
30878
31043
  const getCellValue = (label) => values.get(label.toUpperCase());
30879
31044
  for (const label of circular) {
31045
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30880
31046
  computedValues.set(label, formatFormulaError("circular-reference"));
30881
31047
  values.set(label, formatFormulaError("circular-reference"));
30882
31048
  }
30883
31049
  for (const label of order) {
31050
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30884
31051
  const entry = formulaEntries.get(label);
30885
31052
  if (!entry) continue;
30886
31053
  const result = evaluateBasicTableFormula(entry.formula, getCellValue);
@@ -30925,6 +31092,22 @@ function recalculateSelectedTable(editor) {
30925
31092
  dispatchTableLayoutChange(editor);
30926
31093
  return true;
30927
31094
  }
31095
+ function recalculateActiveTableFormulas(editor) {
31096
+ const tableInfo = getSelectionTableInfo(editor);
31097
+ if (!tableInfo) {
31098
+ return recalculateAllTableFormulas(editor);
31099
+ }
31100
+ const activeCellLabel = getSelectionTableCellLabel(editor);
31101
+ const nextTable = recalculateTableNode(tableInfo.node, {
31102
+ changedLabels: activeCellLabel ? [activeCellLabel] : null
31103
+ });
31104
+ if (!nextTable) return false;
31105
+ editor.view.dispatch(
31106
+ editor.state.tr.replaceWith(tableInfo.pos, tableInfo.pos + tableInfo.node.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
31107
+ );
31108
+ dispatchTableLayoutChange(editor);
31109
+ return true;
31110
+ }
30928
31111
  function recalculateAllTableFormulas(editor) {
30929
31112
  const replacements = [];
30930
31113
  editor.state.doc.descendants((node, pos) => {
@@ -30961,9 +31144,41 @@ function applyTableCellBackground(editor, color) {
30961
31144
  }
30962
31145
  editor.chain().focus().setCellAttribute("backgroundColor", value).run();
30963
31146
  }
31147
+ function applyTableCellAttribute(editor, name, value, options = {}) {
31148
+ const shouldFocus = options.focus ?? true;
31149
+ const { state, view } = editor;
31150
+ const applied = (0, import_tables3.setCellAttr)(name, value)(state, view.dispatch.bind(view));
31151
+ if (applied) {
31152
+ if (shouldFocus) view.focus();
31153
+ return;
31154
+ }
31155
+ const chain = editor.chain();
31156
+ if (shouldFocus) chain.focus();
31157
+ chain.setCellAttribute(name, value).run();
31158
+ }
31159
+ function BorderStylePreviewIcon({ style }) {
31160
+ const isNone = style === "none";
31161
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31162
+ "span",
31163
+ {
31164
+ "aria-hidden": "true",
31165
+ className: cn(
31166
+ "relative inline-flex h-4 w-4 shrink-0 rounded-[2px]",
31167
+ isNone ? "border border-border/70 bg-muted/30" : "border text-current"
31168
+ ),
31169
+ style: isNone ? void 0 : {
31170
+ borderStyle: style,
31171
+ borderWidth: style === "double" ? 3 : 2
31172
+ },
31173
+ children: isNone && /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "absolute left-1/2 top-0 h-full w-px -translate-x-1/2 rotate-45 bg-muted-foreground/70" })
31174
+ }
31175
+ );
31176
+ }
30964
31177
  var BubbleMenuContent = ({
30965
31178
  editor,
30966
31179
  onKeepOpenChange,
31180
+ onLinkInputOpenChange,
31181
+ onRequestClose,
30967
31182
  fontSizes,
30968
31183
  lineHeights,
30969
31184
  initialShowLinkInput = false
@@ -30994,6 +31209,8 @@ var BubbleMenuContent = ({
30994
31209
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
30995
31210
  const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
30996
31211
  const currentCellNumberFormat = normalizeStyleValue(editor.getAttributes("tableCell").numberFormat || editor.getAttributes("tableHeader").numberFormat) || "text";
31212
+ const currentCellBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
31213
+ const currentCellBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
30997
31214
  const isInTable2 = (0, import_tables3.isInTable)(editor.state);
30998
31215
  const canMergeCells = isInTable2 && editor.can().mergeCells();
30999
31216
  const canSplitCell = isInTable2 && editor.can().splitCell();
@@ -31007,6 +31224,15 @@ var BubbleMenuContent = ({
31007
31224
  () => (lineHeights ?? getDefaultLineHeights()).filter((option) => ["1.2", "1.5", "1.75"].includes(option.value)),
31008
31225
  [lineHeights]
31009
31226
  );
31227
+ const borderColors = (0, import_react64.useMemo)(
31228
+ () => [
31229
+ highlightColors[0] ?? { name: t("colors.default"), color: "" },
31230
+ { name: "Black", color: "#000000" },
31231
+ { name: "White", color: "#ffffff" },
31232
+ ...highlightColors.slice(1)
31233
+ ],
31234
+ [highlightColors, t]
31235
+ );
31010
31236
  (0, import_react64.useEffect)(() => {
31011
31237
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
31012
31238
  }, [currentFontSize]);
@@ -31027,7 +31253,55 @@ var BubbleMenuContent = ({
31027
31253
  };
31028
31254
  (0, import_react64.useEffect)(() => {
31029
31255
  onKeepOpenChange?.(showLinkInput);
31030
- }, [onKeepOpenChange, showLinkInput]);
31256
+ onLinkInputOpenChange?.(showLinkInput);
31257
+ }, [onKeepOpenChange, onLinkInputOpenChange, showLinkInput]);
31258
+ (0, import_react64.useEffect)(() => {
31259
+ onKeepOpenChange?.(Boolean(activeColorPalette));
31260
+ }, [activeColorPalette, onKeepOpenChange]);
31261
+ const closeTransientPanels = (0, import_react64.useCallback)(() => {
31262
+ setActiveColorPalette(null);
31263
+ setShowLinkInput(false);
31264
+ onKeepOpenChange?.(false);
31265
+ onLinkInputOpenChange?.(false);
31266
+ onRequestClose?.();
31267
+ }, [onKeepOpenChange, onLinkInputOpenChange, onRequestClose]);
31268
+ const applyTableCellAttributeAndClose = (0, import_react64.useCallback)((name, value) => {
31269
+ applyTableCellAttribute(editor, name, value, { focus: false });
31270
+ closeTransientPanels();
31271
+ }, [closeTransientPanels, editor]);
31272
+ const clearTableCellBorderAndClose = (0, import_react64.useCallback)(() => {
31273
+ applyTableCellAttribute(editor, "borderColor", null, { focus: false });
31274
+ applyTableCellAttribute(editor, "borderStyle", null, { focus: false });
31275
+ applyTableCellAttribute(editor, "borderWidth", null, { focus: false });
31276
+ closeTransientPanels();
31277
+ }, [closeTransientPanels, editor]);
31278
+ const applyTableCellBorderColorAndClose = (0, import_react64.useCallback)((color) => {
31279
+ const value = color || null;
31280
+ applyTableCellAttribute(editor, "borderColor", value, { focus: false });
31281
+ closeTransientPanels();
31282
+ }, [closeTransientPanels, editor]);
31283
+ const closeColorPalette = (0, import_react64.useCallback)(() => {
31284
+ setActiveColorPalette(null);
31285
+ onKeepOpenChange?.(false);
31286
+ }, [onKeepOpenChange]);
31287
+ const applyInlineColorAndClose = (0, import_react64.useCallback)((color) => {
31288
+ if (activeColorPalette === "text") {
31289
+ if (color === "inherit") {
31290
+ editor.chain().focus().unsetColor().run();
31291
+ } else {
31292
+ editor.chain().focus().setColor(color).run();
31293
+ }
31294
+ } else if (activeColorPalette === "highlight") {
31295
+ if (color === "") {
31296
+ editor.chain().focus().unsetHighlight().run();
31297
+ } else {
31298
+ editor.chain().focus().toggleHighlight({ color }).run();
31299
+ }
31300
+ } else {
31301
+ applyTableCellBackground(editor, color);
31302
+ }
31303
+ closeColorPalette();
31304
+ }, [activeColorPalette, closeColorPalette, editor]);
31031
31305
  (0, import_react64.useEffect)(() => {
31032
31306
  if (!showLinkInput) return;
31033
31307
  const close2 = () => setShowLinkInput(false);
@@ -31059,51 +31333,53 @@ var BubbleMenuContent = ({
31059
31333
  const isTextPalette = activeColorPalette === "text";
31060
31334
  const isHighlightPalette = activeColorPalette === "highlight";
31061
31335
  if (activeColorPalette === "cell-border") {
31062
- const currentBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
31063
- const currentBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
31064
31336
  const currentBorderColor = editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || "currentColor";
31065
- return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2 p-2 w-56 text-sm", children: [
31337
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-2 p-2 w-56 text-sm", "data-ueditor-keep-open": true, children: [
31066
31338
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "font-semibold text-xs text-muted-foreground uppercase tracking-wider mb-1", children: t("tableMenu.cellBorder") || "Cell Borders" }),
31067
31339
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-1", children: [
31068
31340
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderStyle") || "Border Style" }),
31069
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31070
- "select",
31341
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "grid grid-cols-3 gap-1", role: "group", "aria-label": t("tableMenu.borderStyle") || "Border Style", children: [
31342
+ ["solid", "Solid"],
31343
+ ["dashed", "Dashed"],
31344
+ ["dotted", "Dotted"],
31345
+ ["double", "Double"],
31346
+ ["none", "None"]
31347
+ ].map(([style, label]) => /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31348
+ "button",
31071
31349
  {
31072
- className: "flex h-8 w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
31073
- value: currentBorderStyle,
31074
- onChange: (e) => {
31075
- const style = e.target.value;
31076
- editor.chain().focus().setCellAttribute("borderStyle", style).run();
31077
- },
31350
+ type: "button",
31351
+ "data-ueditor-close-on-select": true,
31352
+ onMouseDown: (event) => event.preventDefault(),
31353
+ onClick: () => applyTableCellAttributeAndClose("borderStyle", style),
31354
+ className: cn(
31355
+ "inline-flex h-8 items-center justify-center gap-1.5 rounded-md px-2 text-xs font-medium transition-colors hover:bg-muted",
31356
+ currentCellBorderStyle === style ? "bg-primary/10 text-primary" : "bg-muted/40 text-foreground"
31357
+ ),
31078
31358
  children: [
31079
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "solid", children: "Solid" }),
31080
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "dashed", children: "Dashed" }),
31081
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "dotted", children: "Dotted" }),
31082
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "double", children: "Double" }),
31083
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "none", children: "None" })
31359
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(BorderStylePreviewIcon, { style }),
31360
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { children: label })
31084
31361
  ]
31085
- }
31086
- )
31362
+ },
31363
+ style
31364
+ )) })
31087
31365
  ] }),
31088
31366
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-1", children: [
31089
31367
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderWidth") || "Border Width" }),
31090
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31091
- "select",
31368
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "grid grid-cols-4 gap-1", role: "group", "aria-label": t("tableMenu.borderWidth") || "Border Width", children: ["1px", "2px", "3px", "4px"].map((width) => /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31369
+ "button",
31092
31370
  {
31093
- className: "flex h-8 w-full rounded-md border border-input bg-background px-2 py-1 text-xs ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
31094
- value: currentBorderWidth,
31095
- onChange: (e) => {
31096
- const width = e.target.value;
31097
- editor.chain().focus().setCellAttribute("borderWidth", width).run();
31098
- },
31099
- children: [
31100
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "1px", children: "1px" }),
31101
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "2px", children: "2px" }),
31102
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "3px", children: "3px" }),
31103
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "4px", children: "4px" })
31104
- ]
31105
- }
31106
- )
31371
+ type: "button",
31372
+ "data-ueditor-close-on-select": true,
31373
+ onMouseDown: (event) => event.preventDefault(),
31374
+ onClick: () => applyTableCellAttributeAndClose("borderWidth", width),
31375
+ className: cn(
31376
+ "h-8 rounded-md px-2 text-xs font-medium transition-colors hover:bg-muted",
31377
+ currentCellBorderWidth === width ? "bg-primary/10 text-primary" : "bg-muted/40 text-foreground"
31378
+ ),
31379
+ children: width
31380
+ },
31381
+ width
31382
+ )) })
31107
31383
  ] }),
31108
31384
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31109
31385
  "button",
@@ -31125,70 +31401,36 @@ var BubbleMenuContent = ({
31125
31401
  ]
31126
31402
  }
31127
31403
  ),
31128
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: [
31129
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31130
- "button",
31131
- {
31132
- type: "button",
31133
- onClick: () => {
31134
- editor.chain().focus().setCellAttribute("borderColor", null).setCellAttribute("borderStyle", null).setCellAttribute("borderWidth", null).run();
31135
- setActiveColorPalette(null);
31136
- },
31137
- className: "text-xs text-destructive hover:underline",
31138
- children: t("tableMenu.clearBorder") || "Clear Border"
31139
- }
31140
- ),
31141
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31142
- "button",
31143
- {
31144
- type: "button",
31145
- onClick: () => setActiveColorPalette(null),
31146
- className: "text-xs font-medium text-primary hover:underline",
31147
- children: t("tableMenu.done") || "Done"
31148
- }
31149
- )
31150
- ] })
31404
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31405
+ "button",
31406
+ {
31407
+ type: "button",
31408
+ "data-ueditor-close-on-select": true,
31409
+ onClick: clearTableCellBorderAndClose,
31410
+ className: "text-xs text-destructive hover:underline",
31411
+ children: t("tableMenu.clearBorder") || "Clear Border"
31412
+ }
31413
+ ) })
31151
31414
  ] });
31152
31415
  }
31153
31416
  if (activeColorPalette === "cell-border-color") {
31154
31417
  const currentBorderColor = normalizeStyleValue(editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor) || "";
31155
- return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-56", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31418
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-56", "data-ueditor-keep-open": true, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31156
31419
  EditorColorPalette,
31157
31420
  {
31158
- colors: highlightColors,
31421
+ colors: borderColors,
31159
31422
  currentColor: currentBorderColor,
31160
- onSelect: (color) => {
31161
- const value = color || null;
31162
- editor.chain().focus().setCellAttribute("borderColor", value).run();
31163
- setActiveColorPalette("cell-border");
31164
- },
31423
+ onSelect: applyTableCellBorderColorAndClose,
31165
31424
  label: t("tableMenu.borderColor") || "Border Color"
31166
31425
  }
31167
31426
  ) });
31168
31427
  }
31169
- return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-56", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31428
+ return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-56", "data-ueditor-keep-open": true, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31170
31429
  EditorColorPalette,
31171
31430
  {
31172
31431
  colors: isTextPalette ? textColors : highlightColors,
31173
31432
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
31174
- onSelect: (color) => {
31175
- if (isTextPalette) {
31176
- if (color === "inherit") {
31177
- editor.chain().focus().unsetColor().run();
31178
- } else {
31179
- editor.chain().focus().setColor(color).run();
31180
- }
31181
- } else if (isHighlightPalette) {
31182
- if (color === "") {
31183
- editor.chain().focus().unsetHighlight().run();
31184
- } else {
31185
- editor.chain().focus().toggleHighlight({ color }).run();
31186
- }
31187
- } else {
31188
- applyTableCellBackground(editor, color);
31189
- }
31190
- setActiveColorPalette(null);
31191
- },
31433
+ onSelect: applyInlineColorAndClose,
31192
31434
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
31193
31435
  }
31194
31436
  ) });
@@ -31513,6 +31755,9 @@ var BubbleMenuContent = ({
31513
31755
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31514
31756
  ToolbarButton,
31515
31757
  {
31758
+ onMouseDown: () => {
31759
+ onKeepOpenChange?.(true);
31760
+ },
31516
31761
  onClick: () => setActiveColorPalette("cell-border"),
31517
31762
  active: Boolean(
31518
31763
  editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth
@@ -31594,14 +31839,27 @@ var CustomBubbleMenu = ({
31594
31839
  const SHOW_DELAY_MS = 180;
31595
31840
  const BUBBLE_MENU_OFFSET = 16;
31596
31841
  const [isVisible, setIsVisible] = (0, import_react64.useState)(false);
31842
+ const [linkInputOpen, setLinkInputOpen] = (0, import_react64.useState)(false);
31597
31843
  const [position, setPosition] = (0, import_react64.useState)({ top: 0, left: 0 });
31598
31844
  const menuRef = (0, import_react64.useRef)(null);
31599
31845
  const keepOpenRef = (0, import_react64.useRef)(false);
31600
31846
  const showTimeoutRef = (0, import_react64.useRef)(null);
31847
+ const suppressShowUntilRef = (0, import_react64.useRef)(0);
31601
31848
  const setKeepOpen = (0, import_react64.useCallback)((next) => {
31602
31849
  keepOpenRef.current = next;
31850
+ if (!next) setLinkInputOpen(false);
31603
31851
  if (next) setIsVisible(true);
31604
31852
  }, []);
31853
+ const closeBubbleMenu = (0, import_react64.useCallback)(() => {
31854
+ suppressShowUntilRef.current = Date.now() + 1e3;
31855
+ keepOpenRef.current = false;
31856
+ setLinkInputOpen(false);
31857
+ setIsVisible(false);
31858
+ if (showTimeoutRef.current) {
31859
+ clearTimeout(showTimeoutRef.current);
31860
+ showTimeoutRef.current = null;
31861
+ }
31862
+ }, []);
31605
31863
  (0, import_react64.useEffect)(() => {
31606
31864
  const clearShowTimeout = () => {
31607
31865
  if (showTimeoutRef.current) {
@@ -31613,6 +31871,11 @@ var CustomBubbleMenu = ({
31613
31871
  const { state, view } = editor;
31614
31872
  const { from, to, empty } = state.selection;
31615
31873
  const isLinkActive = editor.isActive("link");
31874
+ if (Date.now() < suppressShowUntilRef.current) {
31875
+ clearShowTimeout();
31876
+ setIsVisible(false);
31877
+ return;
31878
+ }
31616
31879
  if (!keepOpenRef.current && (empty && !isLinkActive || !view.hasFocus())) {
31617
31880
  clearShowTimeout();
31618
31881
  setIsVisible(false);
@@ -31624,6 +31887,11 @@ var CustomBubbleMenu = ({
31624
31887
  start = view.coordsAtPos(from);
31625
31888
  end = view.coordsAtPos(to);
31626
31889
  } catch {
31890
+ if (keepOpenRef.current) {
31891
+ clearShowTimeout();
31892
+ setIsVisible(true);
31893
+ return;
31894
+ }
31627
31895
  clearShowTimeout();
31628
31896
  setIsVisible(false);
31629
31897
  return;
@@ -31681,15 +31949,34 @@ var CustomBubbleMenu = ({
31681
31949
  left: `${position.left}px`,
31682
31950
  transform: "translate(-50%, -100%)"
31683
31951
  },
31684
- onMouseDown: (e) => e.preventDefault(),
31685
- children: editor.isActive("link") && !keepOpenRef.current ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(LinkPreviewContent, { editor, onEdit: () => setKeepOpen(true) }) : /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31952
+ onMouseDown: (e) => {
31953
+ const target = e.target;
31954
+ if (target?.closest?.("[data-ueditor-close-on-select]")) {
31955
+ keepOpenRef.current = false;
31956
+ } else if (target?.closest?.("[data-ueditor-keep-open]")) {
31957
+ keepOpenRef.current = true;
31958
+ }
31959
+ e.preventDefault();
31960
+ },
31961
+ children: editor.isActive("link") && !keepOpenRef.current && !linkInputOpen ? /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31962
+ LinkPreviewContent,
31963
+ {
31964
+ editor,
31965
+ onEdit: () => {
31966
+ setLinkInputOpen(true);
31967
+ setKeepOpen(true);
31968
+ }
31969
+ }
31970
+ ) : /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31686
31971
  BubbleMenuContent,
31687
31972
  {
31688
31973
  editor,
31689
31974
  onKeepOpenChange: setKeepOpen,
31975
+ onLinkInputOpenChange: setLinkInputOpen,
31976
+ onRequestClose: closeBubbleMenu,
31690
31977
  fontSizes,
31691
31978
  lineHeights,
31692
- initialShowLinkInput: keepOpenRef.current
31979
+ initialShowLinkInput: linkInputOpen
31693
31980
  }
31694
31981
  )
31695
31982
  }
@@ -38125,6 +38412,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38125
38412
  proseMirror.addEventListener("keyup", handleSelectionChange);
38126
38413
  proseMirror.addEventListener("focusin", handleSelectionChange);
38127
38414
  document.addEventListener("selectionchange", handleSelectionChange);
38415
+ surface?.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38128
38416
  surface?.addEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38129
38417
  window.addEventListener("resize", handleActiveCellLayoutChange);
38130
38418
  document.addEventListener("pointermove", handlePointerMove);
@@ -38144,6 +38432,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38144
38432
  proseMirror.removeEventListener("keyup", handleSelectionChange);
38145
38433
  proseMirror.removeEventListener("focusin", handleSelectionChange);
38146
38434
  document.removeEventListener("selectionchange", handleSelectionChange);
38435
+ surface?.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38147
38436
  surface?.removeEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38148
38437
  window.removeEventListener("resize", handleActiveCellLayoutChange);
38149
38438
  document.removeEventListener("pointermove", handlePointerMove);
@@ -38178,6 +38467,144 @@ function useUEditorTableInteractions(editor, editable = true) {
38178
38467
  var import_react69 = __toESM(require("react"), 1);
38179
38468
  var import_react70 = require("@tiptap/react");
38180
38469
  var import_lucide_react54 = require("lucide-react");
38470
+
38471
+ // src/components/UEditor/preview-html.ts
38472
+ var DEFAULT_TABLE_COLUMN_WIDTH = 100;
38473
+ var TIPTAP_TABLE_MIN_COLUMN_WIDTH = 25;
38474
+ function parsePixelWidth2(value) {
38475
+ if (!value) return null;
38476
+ const match = String(value).match(/(?:^|[\s:])(\d+(?:\.\d+)?)px\b/i) ?? String(value).match(/^(\d+(?:\.\d+)?)$/);
38477
+ if (!match) return null;
38478
+ const parsed = Number.parseFloat(match[1] ?? match[0]);
38479
+ return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
38480
+ }
38481
+ function parseStyleWidth(style) {
38482
+ return parsePixelWidth2(style.width) ?? parsePixelWidth2(style.minWidth);
38483
+ }
38484
+ function parseStyleHeight(style) {
38485
+ return parsePixelWidth2(style.height) ?? parsePixelWidth2(style.minHeight);
38486
+ }
38487
+ function getCellColspan(cell) {
38488
+ return Math.max(1, cell.colSpan || Number.parseInt(cell.getAttribute("colspan") || "1", 10) || 1);
38489
+ }
38490
+ function getCellWidths(cell) {
38491
+ const dataColwidth = cell.getAttribute("data-colwidth") || cell.getAttribute("colwidth");
38492
+ if (dataColwidth) {
38493
+ const widths = dataColwidth.split(/[,\s]+/).map((part) => parsePixelWidth2(part)).filter((width2) => typeof width2 === "number");
38494
+ if (widths.length > 0) return widths;
38495
+ }
38496
+ const width = parsePixelWidth2(cell.getAttribute("width")) ?? parseStyleWidth(cell.style);
38497
+ if (!width) return null;
38498
+ const colspan = getCellColspan(cell);
38499
+ return Array.from({ length: colspan }, () => Math.max(DEFAULT_TABLE_COLUMN_WIDTH, Math.round(width / colspan)));
38500
+ }
38501
+ function getColumnCount(table) {
38502
+ const colCount = table.querySelectorAll("colgroup > col").length;
38503
+ if (colCount > 0) return colCount;
38504
+ return Math.max(
38505
+ 0,
38506
+ ...Array.from(table.rows).map(
38507
+ (row) => Array.from(row.cells).reduce((count, cell) => count + getCellColspan(cell), 0)
38508
+ )
38509
+ );
38510
+ }
38511
+ function resolveColumnWidths(table) {
38512
+ const columnCount = getColumnCount(table);
38513
+ if (columnCount <= 0) return [];
38514
+ const widths = Array.from({ length: columnCount }, () => DEFAULT_TABLE_COLUMN_WIDTH);
38515
+ const cols = Array.from(table.querySelectorAll("colgroup > col"));
38516
+ cols.slice(0, columnCount).forEach((col, index) => {
38517
+ const width = parsePixelWidth2(col.getAttribute("width")) ?? parseStyleWidth(col.style);
38518
+ if (width && width > TIPTAP_TABLE_MIN_COLUMN_WIDTH) {
38519
+ widths[index] = width;
38520
+ }
38521
+ });
38522
+ Array.from(table.rows).forEach((row) => {
38523
+ let columnIndex = 0;
38524
+ Array.from(row.cells).forEach((cell) => {
38525
+ const cellWidths = getCellWidths(cell);
38526
+ if (cellWidths) {
38527
+ cellWidths.slice(0, getCellColspan(cell)).forEach((width, offset) => {
38528
+ if (width > TIPTAP_TABLE_MIN_COLUMN_WIDTH && columnIndex + offset < widths.length) {
38529
+ widths[columnIndex + offset] = width;
38530
+ }
38531
+ });
38532
+ }
38533
+ columnIndex += getCellColspan(cell);
38534
+ });
38535
+ });
38536
+ return widths;
38537
+ }
38538
+ function setStyleProperty(element, property, value) {
38539
+ element.style.setProperty(property, value);
38540
+ }
38541
+ function resolveRowHeight(row) {
38542
+ const explicitRowHeight = parsePixelWidth2(row.getAttribute("data-row-height")) ?? parseStyleHeight(row.style);
38543
+ if (explicitRowHeight) return Math.max(MIN_TABLE_ROW_HEIGHT, explicitRowHeight);
38544
+ const cellHeight = Array.from(row.cells).reduce((maxHeight, cell) => {
38545
+ const height = parsePixelWidth2(cell.getAttribute("height")) ?? parseStyleHeight(cell.style);
38546
+ return height ? Math.max(maxHeight, height) : maxHeight;
38547
+ }, 0);
38548
+ return Math.max(MIN_TABLE_ROW_HEIGHT, cellHeight);
38549
+ }
38550
+ function normalizePreviewRowHeight(row) {
38551
+ const rowHeight = resolveRowHeight(row);
38552
+ row.style.height = `${rowHeight}px`;
38553
+ row.style.minHeight = `${rowHeight}px`;
38554
+ Array.from(row.cells).forEach((cell) => {
38555
+ cell.style.height = `${rowHeight}px`;
38556
+ cell.style.minHeight = `${rowHeight}px`;
38557
+ });
38558
+ }
38559
+ function normalizePreviewTable(table) {
38560
+ const widths = resolveColumnWidths(table);
38561
+ if (widths.length === 0) return;
38562
+ let colgroup = table.querySelector("colgroup");
38563
+ if (!colgroup) {
38564
+ colgroup = document.createElement("colgroup");
38565
+ table.insertBefore(colgroup, table.firstChild);
38566
+ }
38567
+ while (colgroup.children.length < widths.length) {
38568
+ colgroup.appendChild(document.createElement("col"));
38569
+ }
38570
+ Array.from(colgroup.children).forEach((child, index) => {
38571
+ if (child.tagName.toLowerCase() !== "col") return;
38572
+ const col = child;
38573
+ if (index >= widths.length) {
38574
+ child.remove();
38575
+ return;
38576
+ }
38577
+ col.style.width = `${widths[index]}px`;
38578
+ col.style.minWidth = `${widths[index]}px`;
38579
+ col.setAttribute("width", String(widths[index]));
38580
+ });
38581
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
38582
+ setStyleProperty(table, "width", `${tableWidth}px`);
38583
+ setStyleProperty(table, "min-width", `${tableWidth}px`);
38584
+ setStyleProperty(table, "table-layout", "fixed");
38585
+ Array.from(table.rows).forEach((row) => {
38586
+ let columnIndex = 0;
38587
+ normalizePreviewRowHeight(row);
38588
+ Array.from(row.cells).forEach((cell) => {
38589
+ const colspan = getCellColspan(cell);
38590
+ const cellWidth = widths.slice(columnIndex, columnIndex + colspan).reduce((sum, width) => sum + width, 0);
38591
+ if (cellWidth > 0) {
38592
+ cell.style.width = `${cellWidth}px`;
38593
+ cell.style.minWidth = `${cellWidth}px`;
38594
+ }
38595
+ columnIndex += colspan;
38596
+ });
38597
+ });
38598
+ }
38599
+ function prepareUEditorPreviewHtml(html) {
38600
+ if (typeof document === "undefined" || !html) return html;
38601
+ const container = document.createElement("div");
38602
+ container.innerHTML = html;
38603
+ container.querySelectorAll("table").forEach((table) => normalizePreviewTable(table));
38604
+ return container.innerHTML;
38605
+ }
38606
+
38607
+ // src/components/UEditor/menu-bar.tsx
38181
38608
  var import_jsx_runtime94 = require("react/jsx-runtime");
38182
38609
  function MenuTableInsertGrid({
38183
38610
  insertLabel,
@@ -38660,6 +39087,10 @@ var MenuBar = ({
38660
39087
  const [showSourceDialog, setShowSourceDialog] = (0, import_react69.useState)(false);
38661
39088
  const [sourceHtml, setSourceHtml] = (0, import_react69.useState)("");
38662
39089
  const [showPreviewDialog, setShowPreviewDialog] = (0, import_react69.useState)(false);
39090
+ const previewHtml = (0, import_react69.useMemo)(
39091
+ () => showPreviewDialog ? prepareUEditorPreviewHtml(editor.getHTML()) : "",
39092
+ [editor, showPreviewDialog]
39093
+ );
38663
39094
  const openSourceDialog = () => {
38664
39095
  setSourceHtml(editor.getHTML());
38665
39096
  setShowSourceDialog(true);
@@ -38668,10 +39099,7 @@ var MenuBar = ({
38668
39099
  setShowPreviewDialog(true);
38669
39100
  };
38670
39101
  const handlePreview = () => {
38671
- if (onPreview) {
38672
- onPreview();
38673
- return;
38674
- }
39102
+ if (onPreview?.(editor.getHTML()) === false) return;
38675
39103
  openPreviewDialog();
38676
39104
  };
38677
39105
  const applySourceHtml = () => {
@@ -38906,12 +39334,12 @@ var MenuBar = ({
38906
39334
  "div",
38907
39335
  {
38908
39336
  "data-testid": "preview-content",
38909
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-4 md:px-8",
39337
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
38910
39338
  children: editor.isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("p", { className: "text-muted-foreground text-sm", children: t("menubar.previewEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
38911
39339
  "div",
38912
39340
  {
38913
39341
  className: UEDITOR_PROSEMIRROR_CLASS_NAME,
38914
- dangerouslySetInnerHTML: { __html: editor.getHTML() }
39342
+ dangerouslySetInnerHTML: { __html: previewHtml }
38915
39343
  }
38916
39344
  )
38917
39345
  }
@@ -38921,6 +39349,146 @@ var MenuBar = ({
38921
39349
  ] });
38922
39350
  };
38923
39351
 
39352
+ // src/components/UEditor/table-formula-range-picker.ts
39353
+ var import_state9 = require("@tiptap/pm/state");
39354
+ var import_tables4 = require("@tiptap/pm/tables");
39355
+ function getCellText2(cellNode) {
39356
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
39357
+ }
39358
+ function findSelectionFormulaCell(view) {
39359
+ const { $from } = view.state.selection;
39360
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
39361
+ const node = $from.node(depth);
39362
+ if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
39363
+ if (!getCellText2(node).startsWith("=")) return null;
39364
+ const cellPos = $from.before(depth);
39365
+ const cellDom = view.nodeDOM(cellPos);
39366
+ const tableDepth = depth - 2;
39367
+ const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
39368
+ if (!tableNode || tableNode.type.name !== "table") return null;
39369
+ return {
39370
+ cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
39371
+ tablePos: $from.before(tableDepth)
39372
+ };
39373
+ }
39374
+ return null;
39375
+ }
39376
+ function findTableForPos(view, pos) {
39377
+ const $pos = view.state.doc.resolve(pos);
39378
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
39379
+ const node = $pos.node(depth);
39380
+ if (node.type.name === "table") {
39381
+ return {
39382
+ node,
39383
+ pos: $pos.before(depth),
39384
+ start: $pos.start(depth)
39385
+ };
39386
+ }
39387
+ }
39388
+ return null;
39389
+ }
39390
+ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
39391
+ const relativeDomPos = domPos - tableStart;
39392
+ const seen = /* @__PURE__ */ new Set();
39393
+ for (const relativeCellPos of map.map) {
39394
+ if (seen.has(relativeCellPos)) continue;
39395
+ seen.add(relativeCellPos);
39396
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
39397
+ return relativeCellPos;
39398
+ }
39399
+ }
39400
+ return null;
39401
+ }
39402
+ function getPickedCellLabel(view, target, tablePos) {
39403
+ const element = resolveEventElement(target);
39404
+ const cell = element?.closest?.("th,td");
39405
+ if (!(cell instanceof HTMLTableCellElement)) return null;
39406
+ const domPos = view.posAtDOM(cell, 0);
39407
+ const tableInfo = findTableForPos(view, domPos);
39408
+ if (!tableInfo || tableInfo.pos !== tablePos) return null;
39409
+ const map = import_tables4.TableMap.get(tableInfo.node);
39410
+ const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
39411
+ if (relativeCellPos == null) return null;
39412
+ const rect = map.findCell(relativeCellPos);
39413
+ return {
39414
+ cell,
39415
+ label: `${indexToColumnName(rect.left)}${rect.top + 1}`
39416
+ };
39417
+ }
39418
+ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
39419
+ return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
39420
+ }
39421
+ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
39422
+ if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
39423
+ if (nextLabel === pickState.currentLabel) {
39424
+ return {
39425
+ ...pickState,
39426
+ currentCell
39427
+ };
39428
+ }
39429
+ let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
39430
+ const nextTo = pickState.insertedFrom + nextLabel.length;
39431
+ tr = tr.setSelection(import_state9.TextSelection.create(tr.doc, nextTo));
39432
+ view.dispatch(tr);
39433
+ return {
39434
+ ...pickState,
39435
+ insertedTo: nextTo,
39436
+ currentLabel: nextLabel,
39437
+ currentCell
39438
+ };
39439
+ }
39440
+ function beginFormulaRangePick(view, event) {
39441
+ if (event.button !== 0) return null;
39442
+ const formulaCell = findSelectionFormulaCell(view);
39443
+ if (!formulaCell) return null;
39444
+ const picked = getPickedCellLabel(view, event.target, formulaCell.tablePos);
39445
+ if (!picked || picked.cell === formulaCell.cellDom) return null;
39446
+ const { from, to } = view.state.selection;
39447
+ let tr = view.state.tr.insertText(picked.label, from, to);
39448
+ tr = tr.setSelection(import_state9.TextSelection.create(tr.doc, from + picked.label.length));
39449
+ view.dispatch(tr);
39450
+ view.focus();
39451
+ event.preventDefault();
39452
+ event.stopPropagation();
39453
+ return {
39454
+ anchorLabel: picked.label,
39455
+ anchorCell: picked.cell,
39456
+ tablePos: formulaCell.tablePos,
39457
+ insertedFrom: from,
39458
+ insertedTo: from + picked.label.length,
39459
+ currentLabel: picked.label,
39460
+ currentCell: picked.cell
39461
+ };
39462
+ }
39463
+ function updateFormulaRangePick(view, pickState, event) {
39464
+ const picked = getPickedCellLabel(view, event.target, pickState.tablePos);
39465
+ if (!picked) return pickState;
39466
+ event.preventDefault();
39467
+ event.stopPropagation();
39468
+ return replacePickedLabel(
39469
+ view,
39470
+ pickState,
39471
+ normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
39472
+ picked.cell
39473
+ );
39474
+ }
39475
+ function getFormulaRangePickHighlight(container, pickState) {
39476
+ if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
39477
+ const containerRect = container.getBoundingClientRect();
39478
+ const anchorRect = pickState.anchorCell.getBoundingClientRect();
39479
+ const currentRect = pickState.currentCell.getBoundingClientRect();
39480
+ const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
39481
+ const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
39482
+ const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
39483
+ const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
39484
+ return {
39485
+ left,
39486
+ top,
39487
+ width: Math.max(0, right - left),
39488
+ height: Math.max(0, bottom - top)
39489
+ };
39490
+ }
39491
+
38924
39492
  // src/components/UEditor/UEditor.tsx
38925
39493
  var import_jsx_runtime95 = require("react/jsx-runtime");
38926
39494
  var UEditor = import_react71.default.forwardRef(({
@@ -38965,6 +39533,9 @@ var UEditor = import_react71.default.forwardRef(({
38965
39533
  const inFlightPrepareRef = (0, import_react71.useRef)(null);
38966
39534
  const lastAppliedContentRef = (0, import_react71.useRef)(content ?? "");
38967
39535
  const scheduledFormulaRecalculateRef = (0, import_react71.useRef)(false);
39536
+ const formulaRangePickRef = (0, import_react71.useRef)(null);
39537
+ const formulaRangeSurfaceRef = (0, import_react71.useRef)(null);
39538
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react71.default.useState(null);
38968
39539
  const scheduleFormulaRecalculate = import_react71.default.useCallback((editor2, options) => {
38969
39540
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
38970
39541
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
@@ -38972,7 +39543,7 @@ var UEditor = import_react71.default.forwardRef(({
38972
39543
  queueMicrotask(() => {
38973
39544
  scheduledFormulaRecalculateRef.current = false;
38974
39545
  if (!editor2.isDestroyed && (options?.force || !isEditingTableFormulaText(editor2))) {
38975
- recalculateAllTableFormulas(editor2);
39546
+ recalculateActiveTableFormulas(editor2);
38976
39547
  }
38977
39548
  });
38978
39549
  }, []);
@@ -39005,6 +39576,10 @@ var UEditor = import_react71.default.forwardRef(({
39005
39576
  ],
39006
39577
  [effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
39007
39578
  );
39579
+ const syncFormulaRangeHighlight = import_react71.default.useCallback((pickState) => {
39580
+ const container = formulaRangeSurfaceRef.current;
39581
+ setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
39582
+ }, []);
39008
39583
  const editor = (0, import_react72.useEditor)({
39009
39584
  immediatelyRender: false,
39010
39585
  extensions,
@@ -39013,6 +39588,37 @@ var UEditor = import_react71.default.forwardRef(({
39013
39588
  autofocus,
39014
39589
  editorProps: {
39015
39590
  handleDOMEvents: {
39591
+ mousedown: (view, event) => {
39592
+ if (!(event instanceof MouseEvent)) return false;
39593
+ const pickState = beginFormulaRangePick(view, event);
39594
+ if (!pickState) return false;
39595
+ formulaRangePickRef.current = pickState;
39596
+ syncFormulaRangeHighlight(pickState);
39597
+ return true;
39598
+ },
39599
+ mousemove: (view, event) => {
39600
+ if (!(event instanceof MouseEvent)) return false;
39601
+ const pickState = formulaRangePickRef.current;
39602
+ if (!pickState) return false;
39603
+ if (event.buttons === 0) {
39604
+ formulaRangePickRef.current = null;
39605
+ syncFormulaRangeHighlight(null);
39606
+ return false;
39607
+ }
39608
+ const nextPickState = updateFormulaRangePick(view, pickState, event);
39609
+ formulaRangePickRef.current = nextPickState;
39610
+ syncFormulaRangeHighlight(nextPickState);
39611
+ return true;
39612
+ },
39613
+ mouseup: (_view, event) => {
39614
+ if (!(event instanceof MouseEvent)) return false;
39615
+ if (!formulaRangePickRef.current) return false;
39616
+ formulaRangePickRef.current = null;
39617
+ syncFormulaRangeHighlight(null);
39618
+ event.preventDefault();
39619
+ event.stopPropagation();
39620
+ return true;
39621
+ },
39016
39622
  keydown: (_view, event) => {
39017
39623
  if (!(event instanceof KeyboardEvent)) return false;
39018
39624
  if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
@@ -39178,7 +39784,10 @@ var UEditor = import_react71.default.forwardRef(({
39178
39784
  /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(
39179
39785
  "div",
39180
39786
  {
39181
- ref: editorContentRef,
39787
+ ref: (node) => {
39788
+ editorContentRef.current = node;
39789
+ formulaRangeSurfaceRef.current = node;
39790
+ },
39182
39791
  className: "relative flex-1 overflow-y-auto",
39183
39792
  style: {
39184
39793
  minHeight: editable ? minHeight : void 0,
@@ -39210,6 +39819,20 @@ var UEditor = import_react71.default.forwardRef(({
39210
39819
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
39211
39820
  }
39212
39821
  ),
39822
+ formulaRangeHighlight && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
39823
+ "span",
39824
+ {
39825
+ "aria-hidden": "true",
39826
+ "data-ueditor-formula-range-highlight": "",
39827
+ className: "pointer-events-none absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10",
39828
+ style: {
39829
+ left: formulaRangeHighlight.left,
39830
+ top: formulaRangeHighlight.top,
39831
+ width: formulaRangeHighlight.width,
39832
+ height: formulaRangeHighlight.height
39833
+ }
39834
+ }
39835
+ ),
39213
39836
  editable && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(TableControls, { editor, containerRef: editorContentRef }),
39214
39837
  /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
39215
39838
  import_react72.EditorContent,