@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.js CHANGED
@@ -5856,7 +5856,7 @@ var Modal = ({
5856
5856
  const modalContent = /* @__PURE__ */ jsxs13(
5857
5857
  "div",
5858
5858
  {
5859
- className: cn("fixed inset-0 z-9999 flex items-center justify-center p-4 md:p-6", overlayClassName),
5859
+ className: cn("fixed inset-0 z-[9999] flex items-center justify-center p-4 md:p-6", overlayClassName),
5860
5860
  style: { overscrollBehavior: "contain" },
5861
5861
  onMouseDown: handleOverlayMouseDown,
5862
5862
  onMouseUp: handleOverlayMouseUp,
@@ -24707,7 +24707,7 @@ function useLocale2() {
24707
24707
  }
24708
24708
 
24709
24709
  // src/components/UEditor/UEditor.tsx
24710
- import React83, { useEffect as useEffect38, useImperativeHandle as useImperativeHandle4, useMemo as useMemo24, useRef as useRef37 } from "react";
24710
+ import React83, { useEffect as useEffect38, useImperativeHandle as useImperativeHandle4, useMemo as useMemo25, useRef as useRef37 } from "react";
24711
24711
  import { useEditor, EditorContent } from "@tiptap/react";
24712
24712
 
24713
24713
  // src/components/UEditor/extensions.ts
@@ -27062,6 +27062,111 @@ import { useEffect as useEffect34, useRef as useRef29, useState as useState44 }
27062
27062
  import Image4 from "@tiptap/extension-image";
27063
27063
  import { mergeAttributes as mergeAttributes4 } from "@tiptap/core";
27064
27064
  import { NodeViewWrapper as NodeViewWrapper4, ReactNodeViewRenderer as ReactNodeViewRenderer4 } from "@tiptap/react";
27065
+
27066
+ // src/components/UEditor/table-dom-utils.ts
27067
+ var MIN_TABLE_ROW_HEIGHT = 36;
27068
+ var COLUMN_RESIZE_LINE_THICKNESS = 2;
27069
+ var ROW_RESIZE_LINE_THICKNESS = 2;
27070
+ var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
27071
+ var TABLE_RESIZE_HIT_ZONE = 10;
27072
+ function findTableRowNodeInfo(view, rowElement) {
27073
+ const firstCell = rowElement.querySelector("th,td");
27074
+ if (!firstCell) return null;
27075
+ const cellPos = view.posAtDOM(firstCell, 0);
27076
+ const $pos = view.state.doc.resolve(cellPos);
27077
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
27078
+ const node = $pos.node(depth);
27079
+ if (node.type.name === "tableRow") {
27080
+ return {
27081
+ pos: $pos.before(depth),
27082
+ node
27083
+ };
27084
+ }
27085
+ }
27086
+ return null;
27087
+ }
27088
+ function resolveEventElement(target) {
27089
+ if (target instanceof Element) return target;
27090
+ if (target instanceof Node) return target.parentElement;
27091
+ return null;
27092
+ }
27093
+ function getSelectionTableCell(view) {
27094
+ const browserSelection = window.getSelection();
27095
+ const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
27096
+ const anchorCell = anchorElement?.closest?.("th,td");
27097
+ if (anchorCell instanceof HTMLElement) {
27098
+ return anchorCell;
27099
+ }
27100
+ const { from } = view.state.selection;
27101
+ const domAtPos = view.domAtPos(from);
27102
+ const element = resolveEventElement(domAtPos.node);
27103
+ const cell = element?.closest?.("th,td");
27104
+ return cell instanceof HTMLElement ? cell : null;
27105
+ }
27106
+ function isRowResizeHotspot(cell, clientX, clientY) {
27107
+ const rect = cell.getBoundingClientRect();
27108
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27109
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27110
+ return nearBottom && !nearRight;
27111
+ }
27112
+ function isColumnResizeHotspot(cell, clientX, clientY) {
27113
+ const rect = cell.getBoundingClientRect();
27114
+ const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27115
+ const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27116
+ return nearRight && !nearBottom;
27117
+ }
27118
+ function getRelativeBoundaryMetrics(surface, table, row, cell) {
27119
+ const surfaceRect = surface.getBoundingClientRect();
27120
+ const tableRect = table.getBoundingClientRect();
27121
+ const rowRect = row.getBoundingClientRect();
27122
+ const cellRect = cell.getBoundingClientRect();
27123
+ return {
27124
+ left: tableRect.left - surfaceRect.left + surface.scrollLeft,
27125
+ top: tableRect.top - surfaceRect.top + surface.scrollTop,
27126
+ width: tableRect.width,
27127
+ height: tableRect.height,
27128
+ rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
27129
+ columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
27130
+ };
27131
+ }
27132
+ function getRelativeCellMetrics(surface, cell) {
27133
+ const surfaceRect = surface.getBoundingClientRect();
27134
+ const cellRect = cell.getBoundingClientRect();
27135
+ return {
27136
+ left: cellRect.left - surfaceRect.left + surface.scrollLeft,
27137
+ top: cellRect.top - surfaceRect.top + surface.scrollTop,
27138
+ width: cellRect.width,
27139
+ height: cellRect.height
27140
+ };
27141
+ }
27142
+ function getRelativeSelectedCellsMetrics(surface) {
27143
+ const selectedCells = Array.from(
27144
+ surface.querySelectorAll("td.selectedCell, th.selectedCell")
27145
+ );
27146
+ if (selectedCells.length === 0) {
27147
+ return null;
27148
+ }
27149
+ const surfaceRect = surface.getBoundingClientRect();
27150
+ let left = Number.POSITIVE_INFINITY;
27151
+ let top = Number.POSITIVE_INFINITY;
27152
+ let right = Number.NEGATIVE_INFINITY;
27153
+ let bottom = Number.NEGATIVE_INFINITY;
27154
+ selectedCells.forEach((cell) => {
27155
+ const rect = cell.getBoundingClientRect();
27156
+ left = Math.min(left, rect.left);
27157
+ top = Math.min(top, rect.top);
27158
+ right = Math.max(right, rect.right);
27159
+ bottom = Math.max(bottom, rect.bottom);
27160
+ });
27161
+ return {
27162
+ left: left - surfaceRect.left + surface.scrollLeft,
27163
+ top: top - surfaceRect.top + surface.scrollTop,
27164
+ width: right - left,
27165
+ height: bottom - top
27166
+ };
27167
+ }
27168
+
27169
+ // src/components/UEditor/resizable-image.tsx
27065
27170
  import { jsx as jsx82, jsxs as jsxs68 } from "react/jsx-runtime";
27066
27171
  var MIN_IMAGE_SIZE_PX = 40;
27067
27172
  var IMAGE_LAYOUTS = /* @__PURE__ */ new Set(["block", "left", "right"]);
@@ -27143,6 +27248,7 @@ function ResizableImageNodeView(props) {
27143
27248
  const { node, selected, updateAttributes, editor, getPos } = props;
27144
27249
  const wrapperRef = useRef29(null);
27145
27250
  const imgRef = useRef29(null);
27251
+ const resizePreviewRef = useRef29(null);
27146
27252
  const [isHovered, setIsHovered] = useState44(false);
27147
27253
  const [isResizing, setIsResizing] = useState44(false);
27148
27254
  const widthAttr = toNullableNumber(node.attrs["width"]);
@@ -27150,11 +27256,59 @@ function ResizableImageNodeView(props) {
27150
27256
  const textAlign = String(node.attrs["textAlign"] ?? "");
27151
27257
  const imageLayout = parseImageLayout(node.attrs["imageLayout"]);
27152
27258
  const dragStateRef = useRef29(null);
27259
+ const dispatchTableLayoutChange2 = () => {
27260
+ editor.view.dom.dispatchEvent(new CustomEvent(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, { bubbles: true }));
27261
+ };
27262
+ const getImageDisplayStyle = (width, height) => ({
27263
+ width: width ? `${width}px` : void 0,
27264
+ height: width && height ? "auto" : height ? `${height}px` : void 0,
27265
+ aspectRatio: width && height ? `${width} / ${height}` : void 0
27266
+ });
27267
+ const setResizePreviewStyle = (width, height, visible, resetBase = false) => {
27268
+ const preview = resizePreviewRef.current;
27269
+ if (!preview) return;
27270
+ const drag = dragStateRef.current;
27271
+ const baseW = resetBase || !drag ? Math.max(MIN_IMAGE_SIZE_PX, Math.round(width)) : drag.previewBaseW;
27272
+ const baseH = resetBase || !drag ? Math.max(MIN_IMAGE_SIZE_PX, Math.round(height)) : drag.previewBaseH;
27273
+ const scaleX = width / baseW;
27274
+ const scaleY = height / baseH;
27275
+ if (resetBase) {
27276
+ preview.style.width = `${baseW}px`;
27277
+ preview.style.height = `${baseH}px`;
27278
+ }
27279
+ preview.style.maxWidth = "none";
27280
+ preview.style.maxHeight = "none";
27281
+ preview.style.transform = visible ? `translateZ(0) scale(${scaleX}, ${scaleY})` : "translateZ(0) scale(1)";
27282
+ preview.style.display = visible ? "block" : "none";
27283
+ };
27284
+ const applyPendingResizeFrame = () => {
27285
+ const drag = dragStateRef.current;
27286
+ if (!drag) return;
27287
+ drag.frameId = null;
27288
+ setResizePreviewStyle(drag.pendingW, drag.pendingH, true);
27289
+ };
27290
+ const scheduleResizeFrame = () => {
27291
+ const drag = dragStateRef.current;
27292
+ if (!drag || drag.frameId !== null) return;
27293
+ drag.frameId = window.requestAnimationFrame(applyPendingResizeFrame);
27294
+ };
27295
+ useEffect34(() => {
27296
+ return () => {
27297
+ const drag = dragStateRef.current;
27298
+ if (drag && drag.frameId !== null) {
27299
+ window.cancelAnimationFrame(drag.frameId);
27300
+ }
27301
+ dragStateRef.current = null;
27302
+ document.body.style.cursor = "";
27303
+ };
27304
+ }, []);
27153
27305
  useEffect34(() => {
27154
27306
  const img = imgRef.current;
27155
27307
  if (!img) return;
27156
- img.style.width = widthAttr ? `${widthAttr}px` : "";
27157
- img.style.height = heightAttr ? `${heightAttr}px` : "";
27308
+ const displayStyle = getImageDisplayStyle(widthAttr, heightAttr);
27309
+ img.style.width = typeof displayStyle.width === "string" ? displayStyle.width : "";
27310
+ img.style.height = typeof displayStyle.height === "string" ? displayStyle.height : "";
27311
+ img.style.aspectRatio = typeof displayStyle.aspectRatio === "string" ? displayStyle.aspectRatio : "";
27158
27312
  }, [widthAttr, heightAttr]);
27159
27313
  const selectNode = () => {
27160
27314
  const pos = typeof getPos === "function" ? getPos() : null;
@@ -27185,12 +27339,17 @@ function ResizableImageNodeView(props) {
27185
27339
  startY: event.clientY,
27186
27340
  startW,
27187
27341
  startH,
27188
- lastW: startW,
27189
- lastH: startH,
27190
27342
  aspect,
27191
- maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW)
27343
+ maxW: Math.max(MIN_IMAGE_SIZE_PX, maxW),
27344
+ previewBaseW: Math.max(MIN_IMAGE_SIZE_PX, Math.round(startW)),
27345
+ previewBaseH: Math.max(MIN_IMAGE_SIZE_PX, Math.round(startH)),
27346
+ pendingW: startW,
27347
+ pendingH: startH,
27348
+ frameId: null
27192
27349
  };
27193
27350
  setIsResizing(true);
27351
+ setResizePreviewStyle(startW, startH, true, true);
27352
+ document.body.style.cursor = "nwse-resize";
27194
27353
  event.currentTarget.setPointerCapture(event.pointerId);
27195
27354
  };
27196
27355
  const onResizePointerMove = (event) => {
@@ -27203,19 +27362,33 @@ function ResizableImageNodeView(props) {
27203
27362
  const nextSize = Math.abs(dx) >= Math.abs(dy) ? sizeFromWidth(drag.startW + dx, drag.aspect, drag.maxW) : sizeFromHeight(drag.startH + dy, drag.aspect, drag.maxW);
27204
27363
  const nextW = nextSize.width;
27205
27364
  const nextH = nextSize.height;
27206
- drag.lastW = nextW;
27207
- drag.lastH = nextH;
27208
- img.style.width = `${Math.round(nextW)}px`;
27209
- img.style.height = `${Math.round(nextH)}px`;
27365
+ drag.pendingW = nextW;
27366
+ drag.pendingH = nextH;
27367
+ scheduleResizeFrame();
27210
27368
  };
27211
27369
  const finishResize = () => {
27212
27370
  const drag = dragStateRef.current;
27213
27371
  dragStateRef.current = null;
27214
27372
  setIsResizing(false);
27373
+ document.body.style.cursor = "";
27215
27374
  if (!drag) return;
27375
+ if (drag.frameId !== null) {
27376
+ window.cancelAnimationFrame(drag.frameId);
27377
+ drag.frameId = null;
27378
+ }
27379
+ const img = imgRef.current;
27380
+ const nextW = Math.round(drag.pendingW);
27381
+ const nextH = Math.round(drag.pendingH);
27382
+ setResizePreviewStyle(nextW, nextH, false);
27383
+ if (img) {
27384
+ img.style.width = `${nextW}px`;
27385
+ img.style.height = "auto";
27386
+ img.style.aspectRatio = `${nextW} / ${nextH}`;
27387
+ }
27388
+ dispatchTableLayoutChange2();
27216
27389
  updateAttributes({
27217
- width: Math.round(drag.lastW),
27218
- height: Math.round(drag.lastH),
27390
+ width: nextW,
27391
+ height: nextH,
27219
27392
  imageWidthPreset: null
27220
27393
  });
27221
27394
  };
@@ -27264,9 +27437,30 @@ function ResizableImageNodeView(props) {
27264
27437
  selected ? "ring-2 ring-primary/60 ring-offset-2 ring-offset-background" : "",
27265
27438
  isResizing ? "select-none" : ""
27266
27439
  ].join(" "),
27440
+ style: getImageDisplayStyle(widthAttr, heightAttr)
27441
+ }
27442
+ ),
27443
+ /* @__PURE__ */ jsx82(
27444
+ "div",
27445
+ {
27446
+ ref: resizePreviewRef,
27447
+ "aria-hidden": "true",
27448
+ "data-ueditor-image-resize-preview": "",
27449
+ className: [
27450
+ "pointer-events-none absolute left-0 top-0 z-20 hidden rounded-lg",
27451
+ "border border-primary/70 bg-background/30 bg-center bg-no-repeat bg-[length:100%_100%]",
27452
+ "opacity-45 shadow-md ring-2 ring-primary/25 will-change-transform",
27453
+ "select-none"
27454
+ ].join(" "),
27267
27455
  style: {
27268
27456
  width: widthAttr ? `${widthAttr}px` : void 0,
27269
- height: heightAttr ? `${heightAttr}px` : void 0
27457
+ height: heightAttr ? `${heightAttr}px` : void 0,
27458
+ maxWidth: "none",
27459
+ maxHeight: "none",
27460
+ backgroundImage: `url("${String(node.attrs["src"] ?? "").replace(/"/g, "%22")}")`,
27461
+ transform: "translateZ(0) scale(1)",
27462
+ transformOrigin: "top left",
27463
+ display: isResizing ? "block" : "none"
27270
27464
  }
27271
27465
  }
27272
27466
  ),
@@ -27274,6 +27468,7 @@ function ResizableImageNodeView(props) {
27274
27468
  "div",
27275
27469
  {
27276
27470
  "aria-hidden": "true",
27471
+ "data-ueditor-image-resize-handle": "",
27277
27472
  onPointerDown: onResizePointerDown,
27278
27473
  onPointerMove: onResizePointerMove,
27279
27474
  onPointerUp: onResizePointerUp,
@@ -27530,109 +27725,6 @@ var letter_spacing_default = LetterSpacing;
27530
27725
  import { Table as Table3 } from "@tiptap/extension-table";
27531
27726
  import { Plugin as Plugin4 } from "@tiptap/pm/state";
27532
27727
 
27533
- // src/components/UEditor/table-dom-utils.ts
27534
- var MIN_TABLE_ROW_HEIGHT = 36;
27535
- var COLUMN_RESIZE_LINE_THICKNESS = 2;
27536
- var ROW_RESIZE_LINE_THICKNESS = 2;
27537
- var UEDITOR_TABLE_LAYOUT_CHANGE_EVENT = "ueditor-table-layout-change";
27538
- var TABLE_RESIZE_HIT_ZONE = 10;
27539
- function findTableRowNodeInfo(view, rowElement) {
27540
- const firstCell = rowElement.querySelector("th,td");
27541
- if (!firstCell) return null;
27542
- const cellPos = view.posAtDOM(firstCell, 0);
27543
- const $pos = view.state.doc.resolve(cellPos);
27544
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
27545
- const node = $pos.node(depth);
27546
- if (node.type.name === "tableRow") {
27547
- return {
27548
- pos: $pos.before(depth),
27549
- node
27550
- };
27551
- }
27552
- }
27553
- return null;
27554
- }
27555
- function resolveEventElement(target) {
27556
- if (target instanceof Element) return target;
27557
- if (target instanceof Node) return target.parentElement;
27558
- return null;
27559
- }
27560
- function getSelectionTableCell(view) {
27561
- const browserSelection = window.getSelection();
27562
- const anchorElement = resolveEventElement(browserSelection?.anchorNode ?? null);
27563
- const anchorCell = anchorElement?.closest?.("th,td");
27564
- if (anchorCell instanceof HTMLElement) {
27565
- return anchorCell;
27566
- }
27567
- const { from } = view.state.selection;
27568
- const domAtPos = view.domAtPos(from);
27569
- const element = resolveEventElement(domAtPos.node);
27570
- const cell = element?.closest?.("th,td");
27571
- return cell instanceof HTMLElement ? cell : null;
27572
- }
27573
- function isRowResizeHotspot(cell, clientX, clientY) {
27574
- const rect = cell.getBoundingClientRect();
27575
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27576
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27577
- return nearBottom && !nearRight;
27578
- }
27579
- function isColumnResizeHotspot(cell, clientX, clientY) {
27580
- const rect = cell.getBoundingClientRect();
27581
- const nearRight = rect.right - clientX <= TABLE_RESIZE_HIT_ZONE;
27582
- const nearBottom = rect.bottom - clientY <= TABLE_RESIZE_HIT_ZONE;
27583
- return nearRight && !nearBottom;
27584
- }
27585
- function getRelativeBoundaryMetrics(surface, table, row, cell) {
27586
- const surfaceRect = surface.getBoundingClientRect();
27587
- const tableRect = table.getBoundingClientRect();
27588
- const rowRect = row.getBoundingClientRect();
27589
- const cellRect = cell.getBoundingClientRect();
27590
- return {
27591
- left: tableRect.left - surfaceRect.left + surface.scrollLeft,
27592
- top: tableRect.top - surfaceRect.top + surface.scrollTop,
27593
- width: tableRect.width,
27594
- height: tableRect.height,
27595
- rowBottom: rowRect.bottom - surfaceRect.top + surface.scrollTop,
27596
- columnRight: cellRect.right - surfaceRect.left + surface.scrollLeft
27597
- };
27598
- }
27599
- function getRelativeCellMetrics(surface, cell) {
27600
- const surfaceRect = surface.getBoundingClientRect();
27601
- const cellRect = cell.getBoundingClientRect();
27602
- return {
27603
- left: cellRect.left - surfaceRect.left + surface.scrollLeft,
27604
- top: cellRect.top - surfaceRect.top + surface.scrollTop,
27605
- width: cellRect.width,
27606
- height: cellRect.height
27607
- };
27608
- }
27609
- function getRelativeSelectedCellsMetrics(surface) {
27610
- const selectedCells = Array.from(
27611
- surface.querySelectorAll("td.selectedCell, th.selectedCell")
27612
- );
27613
- if (selectedCells.length === 0) {
27614
- return null;
27615
- }
27616
- const surfaceRect = surface.getBoundingClientRect();
27617
- let left = Number.POSITIVE_INFINITY;
27618
- let top = Number.POSITIVE_INFINITY;
27619
- let right = Number.NEGATIVE_INFINITY;
27620
- let bottom = Number.NEGATIVE_INFINITY;
27621
- selectedCells.forEach((cell) => {
27622
- const rect = cell.getBoundingClientRect();
27623
- left = Math.min(left, rect.left);
27624
- top = Math.min(top, rect.top);
27625
- right = Math.max(right, rect.right);
27626
- bottom = Math.max(bottom, rect.bottom);
27627
- });
27628
- return {
27629
- left: left - surfaceRect.left + surface.scrollLeft,
27630
- top: top - surfaceRect.top + surface.scrollTop,
27631
- width: right - left,
27632
- height: bottom - top
27633
- };
27634
- }
27635
-
27636
27728
  // src/components/UEditor/table-align-utils.ts
27637
27729
  function findTableNodeInfoAtResolvedPos($pos) {
27638
27730
  for (let depth = $pos.depth; depth > 0; depth -= 1) {
@@ -28472,6 +28564,9 @@ var HIGHLIGHT_COLOR_SWATCHES = [
28472
28564
  function buildColorOptions(colors, prefix) {
28473
28565
  return colors.map((color, index) => ({ name: `${prefix} ${index + 1}`, color }));
28474
28566
  }
28567
+ function getSwatchCheckClass(color) {
28568
+ return /^#(?:fff|ffffff)$/i.test(color) ? "text-foreground" : "text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]";
28569
+ }
28475
28570
  var useEditorColors = () => {
28476
28571
  const t = useSmartTranslations("UEditor");
28477
28572
  const textColors = useMemo22(
@@ -28546,7 +28641,7 @@ var EditorColorPalette = ({
28546
28641
  currentColor === c.color ? "border-primary ring-2 ring-primary/25" : "border-border/70"
28547
28642
  ),
28548
28643
  style: { backgroundColor: c.color || "transparent" },
28549
- children: currentColor === c.color && /* @__PURE__ */ jsx84("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx84(Check11, { className: "h-3.5 w-3.5 text-white drop-shadow-[0_1px_1px_rgba(0,0,0,0.8)]" }) })
28644
+ children: currentColor === c.color && /* @__PURE__ */ jsx84("span", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx84(Check11, { className: cn("h-3.5 w-3.5", getSwatchCheckClass(c.color)) }) })
28550
28645
  }
28551
28646
  ) }, `${c.name}-${c.color}`)) }),
28552
28647
  /* @__PURE__ */ jsxs70(
@@ -28861,6 +28956,20 @@ function createEmptyCellNode(cellNode) {
28861
28956
  function createCellCopyForColumnDuplicate(cellNode) {
28862
28957
  return cellNode.type.create(cellNode.attrs, cellNode.content);
28863
28958
  }
28959
+ function createCellWithDuplicatedLogicalColumn(cellNode, widthIndex) {
28960
+ const colspan = Math.max(1, Number(cellNode.attrs.colspan) || 1);
28961
+ let nextColwidth = null;
28962
+ if (Array.isArray(cellNode.attrs.colwidth)) {
28963
+ nextColwidth = [...cellNode.attrs.colwidth];
28964
+ const duplicateWidth = nextColwidth[widthIndex];
28965
+ nextColwidth.splice(widthIndex + 1, 0, typeof duplicateWidth === "number" ? duplicateWidth : 0);
28966
+ }
28967
+ return cellNode.type.create({
28968
+ ...cellNode.attrs,
28969
+ colspan: colspan + 1,
28970
+ ...nextColwidth ? { colwidth: nextColwidth } : null
28971
+ }, cellNode.content);
28972
+ }
28864
28973
  function getTableRows(tableNode) {
28865
28974
  const rows = [];
28866
28975
  tableNode.forEach((rowNode, rowOffset) => {
@@ -29036,6 +29145,11 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
29036
29145
  return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
29037
29146
  });
29038
29147
  if (!sourceCell) return rowInfo.node;
29148
+ const sourceRect = safeFindCell(map, sourceCell.relativePos);
29149
+ if (sourceRect && (sourceRect.left < columnIndex || sourceRect.right > columnIndex + 1)) {
29150
+ cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
29151
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29152
+ }
29039
29153
  cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
29040
29154
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
29041
29155
  });
@@ -30357,6 +30471,25 @@ function getTableFormulaRecalculationOrder(graph) {
30357
30471
  }
30358
30472
  return { order, circular };
30359
30473
  }
30474
+ function getAffectedTableFormulaLabels(graph, changedLabels) {
30475
+ const affected = /* @__PURE__ */ new Set();
30476
+ const queue = Array.from(changedLabels, (label) => label.toUpperCase());
30477
+ for (const label of queue) {
30478
+ if (graph.formulas.has(label)) {
30479
+ affected.add(label);
30480
+ }
30481
+ }
30482
+ for (let index = 0; index < queue.length; index += 1) {
30483
+ const label = queue[index];
30484
+ if (!label) continue;
30485
+ for (const dependent of graph.dependents.get(label) ?? []) {
30486
+ if (affected.has(dependent)) continue;
30487
+ affected.add(dependent);
30488
+ queue.push(dependent);
30489
+ }
30490
+ }
30491
+ return affected;
30492
+ }
30360
30493
  function evaluateBasicTableFormula(formula, getCellValue) {
30361
30494
  const normalized = normalizeTableFormula(formula);
30362
30495
  if (!normalized) {
@@ -30504,10 +30637,19 @@ var FormulaParser = class {
30504
30637
  const range = parseTableCellRange(token.value);
30505
30638
  if (!range) return { value: null, error: "invalid-reference" };
30506
30639
  for (const label of getTableCellRangeLabels(range)) {
30640
+ if (name === "COUNT") {
30641
+ const cellValue2 = this.readOptionalCellNumber(label);
30642
+ if (cellValue2 != null) values.push(cellValue2);
30643
+ continue;
30644
+ }
30507
30645
  const cellValue = this.readCellNumber(label);
30508
30646
  if (cellValue.error) return cellValue;
30509
30647
  values.push(cellValue.value);
30510
30648
  }
30649
+ } else if (name === "COUNT" && token.type === "cell") {
30650
+ this.index += 1;
30651
+ const cellValue = this.readOptionalCellNumber(token.value);
30652
+ if (cellValue != null) values.push(cellValue);
30511
30653
  } else {
30512
30654
  const value = this.parseExpression();
30513
30655
  if (value.error) return value;
@@ -30521,7 +30663,7 @@ var FormulaParser = class {
30521
30663
  }
30522
30664
  return { value: null, error: "invalid-formula" };
30523
30665
  }
30524
- if (values.length === 0) {
30666
+ if (values.length === 0 && name !== "COUNT") {
30525
30667
  return { value: null, error: "invalid-formula" };
30526
30668
  }
30527
30669
  if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
@@ -30539,6 +30681,11 @@ var FormulaParser = class {
30539
30681
  }
30540
30682
  return { value: parsed, error: null };
30541
30683
  }
30684
+ readOptionalCellNumber(label) {
30685
+ const value = this.getCellValue(label);
30686
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30687
+ return Number.isFinite(parsed) ? parsed : null;
30688
+ }
30542
30689
  peekOperator(operators) {
30543
30690
  const token = this.tokens[this.index];
30544
30691
  return token?.type === "operator" && operators.includes(token.value) ? token : null;
@@ -30602,6 +30749,41 @@ function getSelectionTableCellNode(editor) {
30602
30749
  }
30603
30750
  return null;
30604
30751
  }
30752
+ function getSelectionTableInfo(editor) {
30753
+ const { $from } = editor.state.selection;
30754
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30755
+ const node = $from.node(depth);
30756
+ if (node.type.name === "table") {
30757
+ return {
30758
+ node,
30759
+ pos: $from.before(depth)
30760
+ };
30761
+ }
30762
+ }
30763
+ return null;
30764
+ }
30765
+ function getSelectionTableCellLabel(editor) {
30766
+ const { $from } = editor.state.selection;
30767
+ let cellDepth = -1;
30768
+ let tableDepth = -1;
30769
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
30770
+ const node = $from.node(depth);
30771
+ if (cellDepth < 0 && (node.type.name === "tableCell" || node.type.name === "tableHeader")) {
30772
+ cellDepth = depth;
30773
+ }
30774
+ if (node.type.name === "table") {
30775
+ tableDepth = depth;
30776
+ break;
30777
+ }
30778
+ }
30779
+ if (cellDepth < 0 || tableDepth < 0) return null;
30780
+ const tableNode = $from.node(tableDepth);
30781
+ const tableStart = $from.start(tableDepth);
30782
+ const relativeCellPos = $from.before(cellDepth) - tableStart;
30783
+ const rect = safeFindCell2(TableMap2.get(tableNode), relativeCellPos);
30784
+ if (!rect) return null;
30785
+ return `${indexToColumnName(rect.left)}${rect.top + 1}`;
30786
+ }
30605
30787
  function isEditingTableFormulaText(editor) {
30606
30788
  const cellNode = getSelectionTableCellNode(editor);
30607
30789
  return Boolean(cellNode && getCellText(cellNode).startsWith("="));
@@ -30613,20 +30795,6 @@ function createCellDisplayContent(cellNode, displayValue) {
30613
30795
  }
30614
30796
  return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30615
30797
  }
30616
- function buildTableValueGetter(tableNode) {
30617
- const map = TableMap2.get(tableNode);
30618
- const values = /* @__PURE__ */ new Map();
30619
- for (const rowInfo of getTableRows2(tableNode)) {
30620
- for (const entry of rowInfo.cells) {
30621
- const rect = safeFindCell2(map, entry.relativePos);
30622
- if (!rect) continue;
30623
- const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30624
- const computedValue = entry.node.attrs.computedValue;
30625
- values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30626
- }
30627
- }
30628
- return (label) => values.get(label.toUpperCase());
30629
- }
30630
30798
  function buildTableValueMap(tableNode) {
30631
30799
  const map = TableMap2.get(tableNode);
30632
30800
  const values = /* @__PURE__ */ new Map();
@@ -30641,10 +30809,6 @@ function buildTableValueMap(tableNode) {
30641
30809
  }
30642
30810
  return values;
30643
30811
  }
30644
- function getFormulaComputedValue(formula, tableNode) {
30645
- const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30646
- return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30647
- }
30648
30812
  function normalizeFormulaInput(formula) {
30649
30813
  const trimmed = formula.trim();
30650
30814
  if (!trimmed) return "";
@@ -30655,19 +30819,19 @@ function setSelectedTableCellFormula(editor, formula) {
30655
30819
  const { state, view } = editor;
30656
30820
  if (!normalized) {
30657
30821
  const clearedFormula = setCellAttr("formula", null)(state, view.dispatch.bind(view));
30658
- const clearedValue = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30659
- if (clearedFormula || clearedValue) {
30822
+ const clearedValue2 = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30823
+ if (clearedFormula || clearedValue2) {
30824
+ recalculateActiveTableFormulas(editor);
30660
30825
  view.focus();
30661
30826
  dispatchTableLayoutChange(editor);
30662
30827
  return true;
30663
30828
  }
30664
30829
  return false;
30665
30830
  }
30666
- const rect = selectedRect2(state);
30667
- const computedValue = getFormulaComputedValue(normalized, rect.table);
30668
30831
  const appliedFormula = setCellAttr("formula", normalized)(state, view.dispatch.bind(view));
30669
- const appliedValue = setCellAttr("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30670
- if (appliedFormula || appliedValue) {
30832
+ const clearedValue = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30833
+ if (appliedFormula || clearedValue) {
30834
+ recalculateActiveTableFormulas(editor);
30671
30835
  view.focus();
30672
30836
  dispatchTableLayoutChange(editor);
30673
30837
  return true;
@@ -30749,7 +30913,7 @@ function promoteFormulaTextInTableNode(tableNode) {
30749
30913
  changed
30750
30914
  };
30751
30915
  }
30752
- function recalculateTableNode(tableNode) {
30916
+ function recalculateTableNode(tableNode, options) {
30753
30917
  const promoted = promoteFormulaTextInTableNode(tableNode);
30754
30918
  tableNode = promoted.tableNode;
30755
30919
  const map = TableMap2.get(tableNode);
@@ -30779,13 +30943,16 @@ function recalculateTableNode(tableNode) {
30779
30943
  }))
30780
30944
  );
30781
30945
  const { order, circular } = getTableFormulaRecalculationOrder(graph);
30946
+ const affectedLabels = options?.changedLabels ? getAffectedTableFormulaLabels(graph, options.changedLabels) : null;
30782
30947
  const computedValues = /* @__PURE__ */ new Map();
30783
30948
  const getCellValue = (label) => values.get(label.toUpperCase());
30784
30949
  for (const label of circular) {
30950
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30785
30951
  computedValues.set(label, formatFormulaError("circular-reference"));
30786
30952
  values.set(label, formatFormulaError("circular-reference"));
30787
30953
  }
30788
30954
  for (const label of order) {
30955
+ if (affectedLabels && !affectedLabels.has(label)) continue;
30789
30956
  const entry = formulaEntries.get(label);
30790
30957
  if (!entry) continue;
30791
30958
  const result = evaluateBasicTableFormula(entry.formula, getCellValue);
@@ -30830,6 +30997,22 @@ function recalculateSelectedTable(editor) {
30830
30997
  dispatchTableLayoutChange(editor);
30831
30998
  return true;
30832
30999
  }
31000
+ function recalculateActiveTableFormulas(editor) {
31001
+ const tableInfo = getSelectionTableInfo(editor);
31002
+ if (!tableInfo) {
31003
+ return recalculateAllTableFormulas(editor);
31004
+ }
31005
+ const activeCellLabel = getSelectionTableCellLabel(editor);
31006
+ const nextTable = recalculateTableNode(tableInfo.node, {
31007
+ changedLabels: activeCellLabel ? [activeCellLabel] : null
31008
+ });
31009
+ if (!nextTable) return false;
31010
+ editor.view.dispatch(
31011
+ editor.state.tr.replaceWith(tableInfo.pos, tableInfo.pos + tableInfo.node.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
31012
+ );
31013
+ dispatchTableLayoutChange(editor);
31014
+ return true;
31015
+ }
30833
31016
  function recalculateAllTableFormulas(editor) {
30834
31017
  const replacements = [];
30835
31018
  editor.state.doc.descendants((node, pos) => {
@@ -30866,9 +31049,41 @@ function applyTableCellBackground(editor, color) {
30866
31049
  }
30867
31050
  editor.chain().focus().setCellAttribute("backgroundColor", value).run();
30868
31051
  }
31052
+ function applyTableCellAttribute(editor, name, value, options = {}) {
31053
+ const shouldFocus = options.focus ?? true;
31054
+ const { state, view } = editor;
31055
+ const applied = setCellAttr2(name, value)(state, view.dispatch.bind(view));
31056
+ if (applied) {
31057
+ if (shouldFocus) view.focus();
31058
+ return;
31059
+ }
31060
+ const chain = editor.chain();
31061
+ if (shouldFocus) chain.focus();
31062
+ chain.setCellAttribute(name, value).run();
31063
+ }
31064
+ function BorderStylePreviewIcon({ style }) {
31065
+ const isNone = style === "none";
31066
+ return /* @__PURE__ */ jsx87(
31067
+ "span",
31068
+ {
31069
+ "aria-hidden": "true",
31070
+ className: cn(
31071
+ "relative inline-flex h-4 w-4 shrink-0 rounded-[2px]",
31072
+ isNone ? "border border-border/70 bg-muted/30" : "border text-current"
31073
+ ),
31074
+ style: isNone ? void 0 : {
31075
+ borderStyle: style,
31076
+ borderWidth: style === "double" ? 3 : 2
31077
+ },
31078
+ children: isNone && /* @__PURE__ */ jsx87("span", { className: "absolute left-1/2 top-0 h-full w-px -translate-x-1/2 rotate-45 bg-muted-foreground/70" })
31079
+ }
31080
+ );
31081
+ }
30869
31082
  var BubbleMenuContent = ({
30870
31083
  editor,
30871
31084
  onKeepOpenChange,
31085
+ onLinkInputOpenChange,
31086
+ onRequestClose,
30872
31087
  fontSizes,
30873
31088
  lineHeights,
30874
31089
  initialShowLinkInput = false
@@ -30899,6 +31114,8 @@ var BubbleMenuContent = ({
30899
31114
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
30900
31115
  const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
30901
31116
  const currentCellNumberFormat = normalizeStyleValue(editor.getAttributes("tableCell").numberFormat || editor.getAttributes("tableHeader").numberFormat) || "text";
31117
+ const currentCellBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
31118
+ const currentCellBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
30902
31119
  const isInTable2 = isSelectionInTable(editor.state);
30903
31120
  const canMergeCells = isInTable2 && editor.can().mergeCells();
30904
31121
  const canSplitCell = isInTable2 && editor.can().splitCell();
@@ -30912,6 +31129,15 @@ var BubbleMenuContent = ({
30912
31129
  () => (lineHeights ?? getDefaultLineHeights()).filter((option) => ["1.2", "1.5", "1.75"].includes(option.value)),
30913
31130
  [lineHeights]
30914
31131
  );
31132
+ const borderColors = useMemo23(
31133
+ () => [
31134
+ highlightColors[0] ?? { name: t("colors.default"), color: "" },
31135
+ { name: "Black", color: "#000000" },
31136
+ { name: "White", color: "#ffffff" },
31137
+ ...highlightColors.slice(1)
31138
+ ],
31139
+ [highlightColors, t]
31140
+ );
30915
31141
  useEffect36(() => {
30916
31142
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
30917
31143
  }, [currentFontSize]);
@@ -30932,7 +31158,55 @@ var BubbleMenuContent = ({
30932
31158
  };
30933
31159
  useEffect36(() => {
30934
31160
  onKeepOpenChange?.(showLinkInput);
30935
- }, [onKeepOpenChange, showLinkInput]);
31161
+ onLinkInputOpenChange?.(showLinkInput);
31162
+ }, [onKeepOpenChange, onLinkInputOpenChange, showLinkInput]);
31163
+ useEffect36(() => {
31164
+ onKeepOpenChange?.(Boolean(activeColorPalette));
31165
+ }, [activeColorPalette, onKeepOpenChange]);
31166
+ const closeTransientPanels = useCallback22(() => {
31167
+ setActiveColorPalette(null);
31168
+ setShowLinkInput(false);
31169
+ onKeepOpenChange?.(false);
31170
+ onLinkInputOpenChange?.(false);
31171
+ onRequestClose?.();
31172
+ }, [onKeepOpenChange, onLinkInputOpenChange, onRequestClose]);
31173
+ const applyTableCellAttributeAndClose = useCallback22((name, value) => {
31174
+ applyTableCellAttribute(editor, name, value, { focus: false });
31175
+ closeTransientPanels();
31176
+ }, [closeTransientPanels, editor]);
31177
+ const clearTableCellBorderAndClose = useCallback22(() => {
31178
+ applyTableCellAttribute(editor, "borderColor", null, { focus: false });
31179
+ applyTableCellAttribute(editor, "borderStyle", null, { focus: false });
31180
+ applyTableCellAttribute(editor, "borderWidth", null, { focus: false });
31181
+ closeTransientPanels();
31182
+ }, [closeTransientPanels, editor]);
31183
+ const applyTableCellBorderColorAndClose = useCallback22((color) => {
31184
+ const value = color || null;
31185
+ applyTableCellAttribute(editor, "borderColor", value, { focus: false });
31186
+ closeTransientPanels();
31187
+ }, [closeTransientPanels, editor]);
31188
+ const closeColorPalette = useCallback22(() => {
31189
+ setActiveColorPalette(null);
31190
+ onKeepOpenChange?.(false);
31191
+ }, [onKeepOpenChange]);
31192
+ const applyInlineColorAndClose = useCallback22((color) => {
31193
+ if (activeColorPalette === "text") {
31194
+ if (color === "inherit") {
31195
+ editor.chain().focus().unsetColor().run();
31196
+ } else {
31197
+ editor.chain().focus().setColor(color).run();
31198
+ }
31199
+ } else if (activeColorPalette === "highlight") {
31200
+ if (color === "") {
31201
+ editor.chain().focus().unsetHighlight().run();
31202
+ } else {
31203
+ editor.chain().focus().toggleHighlight({ color }).run();
31204
+ }
31205
+ } else {
31206
+ applyTableCellBackground(editor, color);
31207
+ }
31208
+ closeColorPalette();
31209
+ }, [activeColorPalette, closeColorPalette, editor]);
30936
31210
  useEffect36(() => {
30937
31211
  if (!showLinkInput) return;
30938
31212
  const close2 = () => setShowLinkInput(false);
@@ -30964,51 +31238,53 @@ var BubbleMenuContent = ({
30964
31238
  const isTextPalette = activeColorPalette === "text";
30965
31239
  const isHighlightPalette = activeColorPalette === "highlight";
30966
31240
  if (activeColorPalette === "cell-border") {
30967
- const currentBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
30968
- const currentBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
30969
31241
  const currentBorderColor = editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || "currentColor";
30970
- return /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-2 p-2 w-56 text-sm", children: [
31242
+ return /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-2 p-2 w-56 text-sm", "data-ueditor-keep-open": true, children: [
30971
31243
  /* @__PURE__ */ jsx87("div", { className: "font-semibold text-xs text-muted-foreground uppercase tracking-wider mb-1", children: t("tableMenu.cellBorder") || "Cell Borders" }),
30972
31244
  /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-1", children: [
30973
31245
  /* @__PURE__ */ jsx87("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderStyle") || "Border Style" }),
30974
- /* @__PURE__ */ jsxs73(
30975
- "select",
31246
+ /* @__PURE__ */ jsx87("div", { className: "grid grid-cols-3 gap-1", role: "group", "aria-label": t("tableMenu.borderStyle") || "Border Style", children: [
31247
+ ["solid", "Solid"],
31248
+ ["dashed", "Dashed"],
31249
+ ["dotted", "Dotted"],
31250
+ ["double", "Double"],
31251
+ ["none", "None"]
31252
+ ].map(([style, label]) => /* @__PURE__ */ jsxs73(
31253
+ "button",
30976
31254
  {
30977
- 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",
30978
- value: currentBorderStyle,
30979
- onChange: (e) => {
30980
- const style = e.target.value;
30981
- editor.chain().focus().setCellAttribute("borderStyle", style).run();
30982
- },
31255
+ type: "button",
31256
+ "data-ueditor-close-on-select": true,
31257
+ onMouseDown: (event) => event.preventDefault(),
31258
+ onClick: () => applyTableCellAttributeAndClose("borderStyle", style),
31259
+ className: cn(
31260
+ "inline-flex h-8 items-center justify-center gap-1.5 rounded-md px-2 text-xs font-medium transition-colors hover:bg-muted",
31261
+ currentCellBorderStyle === style ? "bg-primary/10 text-primary" : "bg-muted/40 text-foreground"
31262
+ ),
30983
31263
  children: [
30984
- /* @__PURE__ */ jsx87("option", { value: "solid", children: "Solid" }),
30985
- /* @__PURE__ */ jsx87("option", { value: "dashed", children: "Dashed" }),
30986
- /* @__PURE__ */ jsx87("option", { value: "dotted", children: "Dotted" }),
30987
- /* @__PURE__ */ jsx87("option", { value: "double", children: "Double" }),
30988
- /* @__PURE__ */ jsx87("option", { value: "none", children: "None" })
31264
+ /* @__PURE__ */ jsx87(BorderStylePreviewIcon, { style }),
31265
+ /* @__PURE__ */ jsx87("span", { children: label })
30989
31266
  ]
30990
- }
30991
- )
31267
+ },
31268
+ style
31269
+ )) })
30992
31270
  ] }),
30993
31271
  /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-1", children: [
30994
31272
  /* @__PURE__ */ jsx87("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderWidth") || "Border Width" }),
30995
- /* @__PURE__ */ jsxs73(
30996
- "select",
31273
+ /* @__PURE__ */ jsx87("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__ */ jsx87(
31274
+ "button",
30997
31275
  {
30998
- 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",
30999
- value: currentBorderWidth,
31000
- onChange: (e) => {
31001
- const width = e.target.value;
31002
- editor.chain().focus().setCellAttribute("borderWidth", width).run();
31003
- },
31004
- children: [
31005
- /* @__PURE__ */ jsx87("option", { value: "1px", children: "1px" }),
31006
- /* @__PURE__ */ jsx87("option", { value: "2px", children: "2px" }),
31007
- /* @__PURE__ */ jsx87("option", { value: "3px", children: "3px" }),
31008
- /* @__PURE__ */ jsx87("option", { value: "4px", children: "4px" })
31009
- ]
31010
- }
31011
- )
31276
+ type: "button",
31277
+ "data-ueditor-close-on-select": true,
31278
+ onMouseDown: (event) => event.preventDefault(),
31279
+ onClick: () => applyTableCellAttributeAndClose("borderWidth", width),
31280
+ className: cn(
31281
+ "h-8 rounded-md px-2 text-xs font-medium transition-colors hover:bg-muted",
31282
+ currentCellBorderWidth === width ? "bg-primary/10 text-primary" : "bg-muted/40 text-foreground"
31283
+ ),
31284
+ children: width
31285
+ },
31286
+ width
31287
+ )) })
31012
31288
  ] }),
31013
31289
  /* @__PURE__ */ jsxs73(
31014
31290
  "button",
@@ -31030,70 +31306,36 @@ var BubbleMenuContent = ({
31030
31306
  ]
31031
31307
  }
31032
31308
  ),
31033
- /* @__PURE__ */ jsxs73("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: [
31034
- /* @__PURE__ */ jsx87(
31035
- "button",
31036
- {
31037
- type: "button",
31038
- onClick: () => {
31039
- editor.chain().focus().setCellAttribute("borderColor", null).setCellAttribute("borderStyle", null).setCellAttribute("borderWidth", null).run();
31040
- setActiveColorPalette(null);
31041
- },
31042
- className: "text-xs text-destructive hover:underline",
31043
- children: t("tableMenu.clearBorder") || "Clear Border"
31044
- }
31045
- ),
31046
- /* @__PURE__ */ jsx87(
31047
- "button",
31048
- {
31049
- type: "button",
31050
- onClick: () => setActiveColorPalette(null),
31051
- className: "text-xs font-medium text-primary hover:underline",
31052
- children: t("tableMenu.done") || "Done"
31053
- }
31054
- )
31055
- ] })
31309
+ /* @__PURE__ */ jsx87("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: /* @__PURE__ */ jsx87(
31310
+ "button",
31311
+ {
31312
+ type: "button",
31313
+ "data-ueditor-close-on-select": true,
31314
+ onClick: clearTableCellBorderAndClose,
31315
+ className: "text-xs text-destructive hover:underline",
31316
+ children: t("tableMenu.clearBorder") || "Clear Border"
31317
+ }
31318
+ ) })
31056
31319
  ] });
31057
31320
  }
31058
31321
  if (activeColorPalette === "cell-border-color") {
31059
31322
  const currentBorderColor = normalizeStyleValue(editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor) || "";
31060
- return /* @__PURE__ */ jsx87("div", { className: "w-56", children: /* @__PURE__ */ jsx87(
31323
+ return /* @__PURE__ */ jsx87("div", { className: "w-56", "data-ueditor-keep-open": true, children: /* @__PURE__ */ jsx87(
31061
31324
  EditorColorPalette,
31062
31325
  {
31063
- colors: highlightColors,
31326
+ colors: borderColors,
31064
31327
  currentColor: currentBorderColor,
31065
- onSelect: (color) => {
31066
- const value = color || null;
31067
- editor.chain().focus().setCellAttribute("borderColor", value).run();
31068
- setActiveColorPalette("cell-border");
31069
- },
31328
+ onSelect: applyTableCellBorderColorAndClose,
31070
31329
  label: t("tableMenu.borderColor") || "Border Color"
31071
31330
  }
31072
31331
  ) });
31073
31332
  }
31074
- return /* @__PURE__ */ jsx87("div", { className: "w-56", children: /* @__PURE__ */ jsx87(
31333
+ return /* @__PURE__ */ jsx87("div", { className: "w-56", "data-ueditor-keep-open": true, children: /* @__PURE__ */ jsx87(
31075
31334
  EditorColorPalette,
31076
31335
  {
31077
31336
  colors: isTextPalette ? textColors : highlightColors,
31078
31337
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
31079
- onSelect: (color) => {
31080
- if (isTextPalette) {
31081
- if (color === "inherit") {
31082
- editor.chain().focus().unsetColor().run();
31083
- } else {
31084
- editor.chain().focus().setColor(color).run();
31085
- }
31086
- } else if (isHighlightPalette) {
31087
- if (color === "") {
31088
- editor.chain().focus().unsetHighlight().run();
31089
- } else {
31090
- editor.chain().focus().toggleHighlight({ color }).run();
31091
- }
31092
- } else {
31093
- applyTableCellBackground(editor, color);
31094
- }
31095
- setActiveColorPalette(null);
31096
- },
31338
+ onSelect: applyInlineColorAndClose,
31097
31339
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
31098
31340
  }
31099
31341
  ) });
@@ -31418,6 +31660,9 @@ var BubbleMenuContent = ({
31418
31660
  /* @__PURE__ */ jsx87(
31419
31661
  ToolbarButton,
31420
31662
  {
31663
+ onMouseDown: () => {
31664
+ onKeepOpenChange?.(true);
31665
+ },
31421
31666
  onClick: () => setActiveColorPalette("cell-border"),
31422
31667
  active: Boolean(
31423
31668
  editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth
@@ -31499,14 +31744,27 @@ var CustomBubbleMenu = ({
31499
31744
  const SHOW_DELAY_MS = 180;
31500
31745
  const BUBBLE_MENU_OFFSET = 16;
31501
31746
  const [isVisible, setIsVisible] = useState48(false);
31747
+ const [linkInputOpen, setLinkInputOpen] = useState48(false);
31502
31748
  const [position, setPosition] = useState48({ top: 0, left: 0 });
31503
31749
  const menuRef = useRef33(null);
31504
31750
  const keepOpenRef = useRef33(false);
31505
31751
  const showTimeoutRef = useRef33(null);
31752
+ const suppressShowUntilRef = useRef33(0);
31506
31753
  const setKeepOpen = useCallback22((next) => {
31507
31754
  keepOpenRef.current = next;
31755
+ if (!next) setLinkInputOpen(false);
31508
31756
  if (next) setIsVisible(true);
31509
31757
  }, []);
31758
+ const closeBubbleMenu = useCallback22(() => {
31759
+ suppressShowUntilRef.current = Date.now() + 1e3;
31760
+ keepOpenRef.current = false;
31761
+ setLinkInputOpen(false);
31762
+ setIsVisible(false);
31763
+ if (showTimeoutRef.current) {
31764
+ clearTimeout(showTimeoutRef.current);
31765
+ showTimeoutRef.current = null;
31766
+ }
31767
+ }, []);
31510
31768
  useEffect36(() => {
31511
31769
  const clearShowTimeout = () => {
31512
31770
  if (showTimeoutRef.current) {
@@ -31518,6 +31776,11 @@ var CustomBubbleMenu = ({
31518
31776
  const { state, view } = editor;
31519
31777
  const { from, to, empty } = state.selection;
31520
31778
  const isLinkActive = editor.isActive("link");
31779
+ if (Date.now() < suppressShowUntilRef.current) {
31780
+ clearShowTimeout();
31781
+ setIsVisible(false);
31782
+ return;
31783
+ }
31521
31784
  if (!keepOpenRef.current && (empty && !isLinkActive || !view.hasFocus())) {
31522
31785
  clearShowTimeout();
31523
31786
  setIsVisible(false);
@@ -31529,6 +31792,11 @@ var CustomBubbleMenu = ({
31529
31792
  start = view.coordsAtPos(from);
31530
31793
  end = view.coordsAtPos(to);
31531
31794
  } catch {
31795
+ if (keepOpenRef.current) {
31796
+ clearShowTimeout();
31797
+ setIsVisible(true);
31798
+ return;
31799
+ }
31532
31800
  clearShowTimeout();
31533
31801
  setIsVisible(false);
31534
31802
  return;
@@ -31586,15 +31854,34 @@ var CustomBubbleMenu = ({
31586
31854
  left: `${position.left}px`,
31587
31855
  transform: "translate(-50%, -100%)"
31588
31856
  },
31589
- onMouseDown: (e) => e.preventDefault(),
31590
- children: editor.isActive("link") && !keepOpenRef.current ? /* @__PURE__ */ jsx87(LinkPreviewContent, { editor, onEdit: () => setKeepOpen(true) }) : /* @__PURE__ */ jsx87(
31857
+ onMouseDown: (e) => {
31858
+ const target = e.target;
31859
+ if (target?.closest?.("[data-ueditor-close-on-select]")) {
31860
+ keepOpenRef.current = false;
31861
+ } else if (target?.closest?.("[data-ueditor-keep-open]")) {
31862
+ keepOpenRef.current = true;
31863
+ }
31864
+ e.preventDefault();
31865
+ },
31866
+ children: editor.isActive("link") && !keepOpenRef.current && !linkInputOpen ? /* @__PURE__ */ jsx87(
31867
+ LinkPreviewContent,
31868
+ {
31869
+ editor,
31870
+ onEdit: () => {
31871
+ setLinkInputOpen(true);
31872
+ setKeepOpen(true);
31873
+ }
31874
+ }
31875
+ ) : /* @__PURE__ */ jsx87(
31591
31876
  BubbleMenuContent,
31592
31877
  {
31593
31878
  editor,
31594
31879
  onKeepOpenChange: setKeepOpen,
31880
+ onLinkInputOpenChange: setLinkInputOpen,
31881
+ onRequestClose: closeBubbleMenu,
31595
31882
  fontSizes,
31596
31883
  lineHeights,
31597
- initialShowLinkInput: keepOpenRef.current
31884
+ initialShowLinkInput: linkInputOpen
31598
31885
  }
31599
31886
  )
31600
31887
  }
@@ -38041,6 +38328,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38041
38328
  proseMirror.addEventListener("keyup", handleSelectionChange);
38042
38329
  proseMirror.addEventListener("focusin", handleSelectionChange);
38043
38330
  document.addEventListener("selectionchange", handleSelectionChange);
38331
+ surface?.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38044
38332
  surface?.addEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38045
38333
  window.addEventListener("resize", handleActiveCellLayoutChange);
38046
38334
  document.addEventListener("pointermove", handlePointerMove);
@@ -38060,6 +38348,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38060
38348
  proseMirror.removeEventListener("keyup", handleSelectionChange);
38061
38349
  proseMirror.removeEventListener("focusin", handleSelectionChange);
38062
38350
  document.removeEventListener("selectionchange", handleSelectionChange);
38351
+ surface?.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38063
38352
  surface?.removeEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38064
38353
  window.removeEventListener("resize", handleActiveCellLayoutChange);
38065
38354
  document.removeEventListener("pointermove", handlePointerMove);
@@ -38091,7 +38380,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38091
38380
  }
38092
38381
 
38093
38382
  // src/components/UEditor/menu-bar.tsx
38094
- import React82, { useRef as useRef36, useState as useState49 } from "react";
38383
+ import React82, { useMemo as useMemo24, useRef as useRef36, useState as useState49 } from "react";
38095
38384
  import { useEditorState as useEditorState3 } from "@tiptap/react";
38096
38385
  import {
38097
38386
  AlignCenter as AlignCenter4,
@@ -38122,6 +38411,144 @@ import {
38122
38411
  Undo as UndoIcon2,
38123
38412
  Upload as Upload4
38124
38413
  } from "lucide-react";
38414
+
38415
+ // src/components/UEditor/preview-html.ts
38416
+ var DEFAULT_TABLE_COLUMN_WIDTH = 100;
38417
+ var TIPTAP_TABLE_MIN_COLUMN_WIDTH = 25;
38418
+ function parsePixelWidth2(value) {
38419
+ if (!value) return null;
38420
+ const match = String(value).match(/(?:^|[\s:])(\d+(?:\.\d+)?)px\b/i) ?? String(value).match(/^(\d+(?:\.\d+)?)$/);
38421
+ if (!match) return null;
38422
+ const parsed = Number.parseFloat(match[1] ?? match[0]);
38423
+ return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
38424
+ }
38425
+ function parseStyleWidth(style) {
38426
+ return parsePixelWidth2(style.width) ?? parsePixelWidth2(style.minWidth);
38427
+ }
38428
+ function parseStyleHeight(style) {
38429
+ return parsePixelWidth2(style.height) ?? parsePixelWidth2(style.minHeight);
38430
+ }
38431
+ function getCellColspan(cell) {
38432
+ return Math.max(1, cell.colSpan || Number.parseInt(cell.getAttribute("colspan") || "1", 10) || 1);
38433
+ }
38434
+ function getCellWidths(cell) {
38435
+ const dataColwidth = cell.getAttribute("data-colwidth") || cell.getAttribute("colwidth");
38436
+ if (dataColwidth) {
38437
+ const widths = dataColwidth.split(/[,\s]+/).map((part) => parsePixelWidth2(part)).filter((width2) => typeof width2 === "number");
38438
+ if (widths.length > 0) return widths;
38439
+ }
38440
+ const width = parsePixelWidth2(cell.getAttribute("width")) ?? parseStyleWidth(cell.style);
38441
+ if (!width) return null;
38442
+ const colspan = getCellColspan(cell);
38443
+ return Array.from({ length: colspan }, () => Math.max(DEFAULT_TABLE_COLUMN_WIDTH, Math.round(width / colspan)));
38444
+ }
38445
+ function getColumnCount(table) {
38446
+ const colCount = table.querySelectorAll("colgroup > col").length;
38447
+ if (colCount > 0) return colCount;
38448
+ return Math.max(
38449
+ 0,
38450
+ ...Array.from(table.rows).map(
38451
+ (row) => Array.from(row.cells).reduce((count, cell) => count + getCellColspan(cell), 0)
38452
+ )
38453
+ );
38454
+ }
38455
+ function resolveColumnWidths(table) {
38456
+ const columnCount = getColumnCount(table);
38457
+ if (columnCount <= 0) return [];
38458
+ const widths = Array.from({ length: columnCount }, () => DEFAULT_TABLE_COLUMN_WIDTH);
38459
+ const cols = Array.from(table.querySelectorAll("colgroup > col"));
38460
+ cols.slice(0, columnCount).forEach((col, index) => {
38461
+ const width = parsePixelWidth2(col.getAttribute("width")) ?? parseStyleWidth(col.style);
38462
+ if (width && width > TIPTAP_TABLE_MIN_COLUMN_WIDTH) {
38463
+ widths[index] = width;
38464
+ }
38465
+ });
38466
+ Array.from(table.rows).forEach((row) => {
38467
+ let columnIndex = 0;
38468
+ Array.from(row.cells).forEach((cell) => {
38469
+ const cellWidths = getCellWidths(cell);
38470
+ if (cellWidths) {
38471
+ cellWidths.slice(0, getCellColspan(cell)).forEach((width, offset) => {
38472
+ if (width > TIPTAP_TABLE_MIN_COLUMN_WIDTH && columnIndex + offset < widths.length) {
38473
+ widths[columnIndex + offset] = width;
38474
+ }
38475
+ });
38476
+ }
38477
+ columnIndex += getCellColspan(cell);
38478
+ });
38479
+ });
38480
+ return widths;
38481
+ }
38482
+ function setStyleProperty(element, property, value) {
38483
+ element.style.setProperty(property, value);
38484
+ }
38485
+ function resolveRowHeight(row) {
38486
+ const explicitRowHeight = parsePixelWidth2(row.getAttribute("data-row-height")) ?? parseStyleHeight(row.style);
38487
+ if (explicitRowHeight) return Math.max(MIN_TABLE_ROW_HEIGHT, explicitRowHeight);
38488
+ const cellHeight = Array.from(row.cells).reduce((maxHeight, cell) => {
38489
+ const height = parsePixelWidth2(cell.getAttribute("height")) ?? parseStyleHeight(cell.style);
38490
+ return height ? Math.max(maxHeight, height) : maxHeight;
38491
+ }, 0);
38492
+ return Math.max(MIN_TABLE_ROW_HEIGHT, cellHeight);
38493
+ }
38494
+ function normalizePreviewRowHeight(row) {
38495
+ const rowHeight = resolveRowHeight(row);
38496
+ row.style.height = `${rowHeight}px`;
38497
+ row.style.minHeight = `${rowHeight}px`;
38498
+ Array.from(row.cells).forEach((cell) => {
38499
+ cell.style.height = `${rowHeight}px`;
38500
+ cell.style.minHeight = `${rowHeight}px`;
38501
+ });
38502
+ }
38503
+ function normalizePreviewTable(table) {
38504
+ const widths = resolveColumnWidths(table);
38505
+ if (widths.length === 0) return;
38506
+ let colgroup = table.querySelector("colgroup");
38507
+ if (!colgroup) {
38508
+ colgroup = document.createElement("colgroup");
38509
+ table.insertBefore(colgroup, table.firstChild);
38510
+ }
38511
+ while (colgroup.children.length < widths.length) {
38512
+ colgroup.appendChild(document.createElement("col"));
38513
+ }
38514
+ Array.from(colgroup.children).forEach((child, index) => {
38515
+ if (child.tagName.toLowerCase() !== "col") return;
38516
+ const col = child;
38517
+ if (index >= widths.length) {
38518
+ child.remove();
38519
+ return;
38520
+ }
38521
+ col.style.width = `${widths[index]}px`;
38522
+ col.style.minWidth = `${widths[index]}px`;
38523
+ col.setAttribute("width", String(widths[index]));
38524
+ });
38525
+ const tableWidth = widths.reduce((sum, width) => sum + width, 0);
38526
+ setStyleProperty(table, "width", `${tableWidth}px`);
38527
+ setStyleProperty(table, "min-width", `${tableWidth}px`);
38528
+ setStyleProperty(table, "table-layout", "fixed");
38529
+ Array.from(table.rows).forEach((row) => {
38530
+ let columnIndex = 0;
38531
+ normalizePreviewRowHeight(row);
38532
+ Array.from(row.cells).forEach((cell) => {
38533
+ const colspan = getCellColspan(cell);
38534
+ const cellWidth = widths.slice(columnIndex, columnIndex + colspan).reduce((sum, width) => sum + width, 0);
38535
+ if (cellWidth > 0) {
38536
+ cell.style.width = `${cellWidth}px`;
38537
+ cell.style.minWidth = `${cellWidth}px`;
38538
+ }
38539
+ columnIndex += colspan;
38540
+ });
38541
+ });
38542
+ }
38543
+ function prepareUEditorPreviewHtml(html) {
38544
+ if (typeof document === "undefined" || !html) return html;
38545
+ const container = document.createElement("div");
38546
+ container.innerHTML = html;
38547
+ container.querySelectorAll("table").forEach((table) => normalizePreviewTable(table));
38548
+ return container.innerHTML;
38549
+ }
38550
+
38551
+ // src/components/UEditor/menu-bar.tsx
38125
38552
  import { Fragment as Fragment35, jsx as jsx93, jsxs as jsxs78 } from "react/jsx-runtime";
38126
38553
  function MenuTableInsertGrid({
38127
38554
  insertLabel,
@@ -38604,6 +39031,10 @@ var MenuBar = ({
38604
39031
  const [showSourceDialog, setShowSourceDialog] = useState49(false);
38605
39032
  const [sourceHtml, setSourceHtml] = useState49("");
38606
39033
  const [showPreviewDialog, setShowPreviewDialog] = useState49(false);
39034
+ const previewHtml = useMemo24(
39035
+ () => showPreviewDialog ? prepareUEditorPreviewHtml(editor.getHTML()) : "",
39036
+ [editor, showPreviewDialog]
39037
+ );
38607
39038
  const openSourceDialog = () => {
38608
39039
  setSourceHtml(editor.getHTML());
38609
39040
  setShowSourceDialog(true);
@@ -38612,10 +39043,7 @@ var MenuBar = ({
38612
39043
  setShowPreviewDialog(true);
38613
39044
  };
38614
39045
  const handlePreview = () => {
38615
- if (onPreview) {
38616
- onPreview();
38617
- return;
38618
- }
39046
+ if (onPreview?.(editor.getHTML()) === false) return;
38619
39047
  openPreviewDialog();
38620
39048
  };
38621
39049
  const applySourceHtml = () => {
@@ -38850,12 +39278,12 @@ var MenuBar = ({
38850
39278
  "div",
38851
39279
  {
38852
39280
  "data-testid": "preview-content",
38853
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-4 md:px-8",
39281
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain",
38854
39282
  children: editor.isEmpty ? /* @__PURE__ */ jsx93("p", { className: "text-muted-foreground text-sm", children: t("menubar.previewEmpty") }) : /* @__PURE__ */ jsx93(
38855
39283
  "div",
38856
39284
  {
38857
39285
  className: UEDITOR_PROSEMIRROR_CLASS_NAME,
38858
- dangerouslySetInnerHTML: { __html: editor.getHTML() }
39286
+ dangerouslySetInnerHTML: { __html: previewHtml }
38859
39287
  }
38860
39288
  )
38861
39289
  }
@@ -38865,6 +39293,146 @@ var MenuBar = ({
38865
39293
  ] });
38866
39294
  };
38867
39295
 
39296
+ // src/components/UEditor/table-formula-range-picker.ts
39297
+ import { TextSelection as TextSelection4 } from "@tiptap/pm/state";
39298
+ import { TableMap as TableMap4 } from "@tiptap/pm/tables";
39299
+ function getCellText2(cellNode) {
39300
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
39301
+ }
39302
+ function findSelectionFormulaCell(view) {
39303
+ const { $from } = view.state.selection;
39304
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
39305
+ const node = $from.node(depth);
39306
+ if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
39307
+ if (!getCellText2(node).startsWith("=")) return null;
39308
+ const cellPos = $from.before(depth);
39309
+ const cellDom = view.nodeDOM(cellPos);
39310
+ const tableDepth = depth - 2;
39311
+ const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
39312
+ if (!tableNode || tableNode.type.name !== "table") return null;
39313
+ return {
39314
+ cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
39315
+ tablePos: $from.before(tableDepth)
39316
+ };
39317
+ }
39318
+ return null;
39319
+ }
39320
+ function findTableForPos(view, pos) {
39321
+ const $pos = view.state.doc.resolve(pos);
39322
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
39323
+ const node = $pos.node(depth);
39324
+ if (node.type.name === "table") {
39325
+ return {
39326
+ node,
39327
+ pos: $pos.before(depth),
39328
+ start: $pos.start(depth)
39329
+ };
39330
+ }
39331
+ }
39332
+ return null;
39333
+ }
39334
+ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
39335
+ const relativeDomPos = domPos - tableStart;
39336
+ const seen = /* @__PURE__ */ new Set();
39337
+ for (const relativeCellPos of map.map) {
39338
+ if (seen.has(relativeCellPos)) continue;
39339
+ seen.add(relativeCellPos);
39340
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
39341
+ return relativeCellPos;
39342
+ }
39343
+ }
39344
+ return null;
39345
+ }
39346
+ function getPickedCellLabel(view, target, tablePos) {
39347
+ const element = resolveEventElement(target);
39348
+ const cell = element?.closest?.("th,td");
39349
+ if (!(cell instanceof HTMLTableCellElement)) return null;
39350
+ const domPos = view.posAtDOM(cell, 0);
39351
+ const tableInfo = findTableForPos(view, domPos);
39352
+ if (!tableInfo || tableInfo.pos !== tablePos) return null;
39353
+ const map = TableMap4.get(tableInfo.node);
39354
+ const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
39355
+ if (relativeCellPos == null) return null;
39356
+ const rect = map.findCell(relativeCellPos);
39357
+ return {
39358
+ cell,
39359
+ label: `${indexToColumnName(rect.left)}${rect.top + 1}`
39360
+ };
39361
+ }
39362
+ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
39363
+ return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
39364
+ }
39365
+ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
39366
+ if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
39367
+ if (nextLabel === pickState.currentLabel) {
39368
+ return {
39369
+ ...pickState,
39370
+ currentCell
39371
+ };
39372
+ }
39373
+ let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
39374
+ const nextTo = pickState.insertedFrom + nextLabel.length;
39375
+ tr = tr.setSelection(TextSelection4.create(tr.doc, nextTo));
39376
+ view.dispatch(tr);
39377
+ return {
39378
+ ...pickState,
39379
+ insertedTo: nextTo,
39380
+ currentLabel: nextLabel,
39381
+ currentCell
39382
+ };
39383
+ }
39384
+ function beginFormulaRangePick(view, event) {
39385
+ if (event.button !== 0) return null;
39386
+ const formulaCell = findSelectionFormulaCell(view);
39387
+ if (!formulaCell) return null;
39388
+ const picked = getPickedCellLabel(view, event.target, formulaCell.tablePos);
39389
+ if (!picked || picked.cell === formulaCell.cellDom) return null;
39390
+ const { from, to } = view.state.selection;
39391
+ let tr = view.state.tr.insertText(picked.label, from, to);
39392
+ tr = tr.setSelection(TextSelection4.create(tr.doc, from + picked.label.length));
39393
+ view.dispatch(tr);
39394
+ view.focus();
39395
+ event.preventDefault();
39396
+ event.stopPropagation();
39397
+ return {
39398
+ anchorLabel: picked.label,
39399
+ anchorCell: picked.cell,
39400
+ tablePos: formulaCell.tablePos,
39401
+ insertedFrom: from,
39402
+ insertedTo: from + picked.label.length,
39403
+ currentLabel: picked.label,
39404
+ currentCell: picked.cell
39405
+ };
39406
+ }
39407
+ function updateFormulaRangePick(view, pickState, event) {
39408
+ const picked = getPickedCellLabel(view, event.target, pickState.tablePos);
39409
+ if (!picked) return pickState;
39410
+ event.preventDefault();
39411
+ event.stopPropagation();
39412
+ return replacePickedLabel(
39413
+ view,
39414
+ pickState,
39415
+ normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
39416
+ picked.cell
39417
+ );
39418
+ }
39419
+ function getFormulaRangePickHighlight(container, pickState) {
39420
+ if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
39421
+ const containerRect = container.getBoundingClientRect();
39422
+ const anchorRect = pickState.anchorCell.getBoundingClientRect();
39423
+ const currentRect = pickState.currentCell.getBoundingClientRect();
39424
+ const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
39425
+ const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
39426
+ const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
39427
+ const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
39428
+ return {
39429
+ left,
39430
+ top,
39431
+ width: Math.max(0, right - left),
39432
+ height: Math.max(0, bottom - top)
39433
+ };
39434
+ }
39435
+
38868
39436
  // src/components/UEditor/UEditor.tsx
38869
39437
  import { jsx as jsx94, jsxs as jsxs79 } from "react/jsx-runtime";
38870
39438
  var UEditor = React83.forwardRef(({
@@ -38909,6 +39477,9 @@ var UEditor = React83.forwardRef(({
38909
39477
  const inFlightPrepareRef = useRef37(null);
38910
39478
  const lastAppliedContentRef = useRef37(content ?? "");
38911
39479
  const scheduledFormulaRecalculateRef = useRef37(false);
39480
+ const formulaRangePickRef = useRef37(null);
39481
+ const formulaRangeSurfaceRef = useRef37(null);
39482
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = React83.useState(null);
38912
39483
  const scheduleFormulaRecalculate = React83.useCallback((editor2, options) => {
38913
39484
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
38914
39485
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
@@ -38916,11 +39487,11 @@ var UEditor = React83.forwardRef(({
38916
39487
  queueMicrotask(() => {
38917
39488
  scheduledFormulaRecalculateRef.current = false;
38918
39489
  if (!editor2.isDestroyed && (options?.force || !isEditingTableFormulaText(editor2))) {
38919
- recalculateAllTableFormulas(editor2);
39490
+ recalculateActiveTableFormulas(editor2);
38920
39491
  }
38921
39492
  });
38922
39493
  }, []);
38923
- const resolvedUploadFile = useMemo24(() => {
39494
+ const resolvedUploadFile = useMemo25(() => {
38924
39495
  if (uploadFile) return uploadFile;
38925
39496
  if (uploadFileForSave) {
38926
39497
  return async (file) => {
@@ -38930,7 +39501,7 @@ var UEditor = React83.forwardRef(({
38930
39501
  }
38931
39502
  return uploadImage;
38932
39503
  }, [uploadFile, uploadFileForSave, uploadImage]);
38933
- const extensions = useMemo24(
39504
+ const extensions = useMemo25(
38934
39505
  () => [
38935
39506
  ...buildUEditorExtensions({
38936
39507
  placeholder: effectivePlaceholder,
@@ -38949,6 +39520,10 @@ var UEditor = React83.forwardRef(({
38949
39520
  ],
38950
39521
  [effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
38951
39522
  );
39523
+ const syncFormulaRangeHighlight = React83.useCallback((pickState) => {
39524
+ const container = formulaRangeSurfaceRef.current;
39525
+ setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
39526
+ }, []);
38952
39527
  const editor = useEditor({
38953
39528
  immediatelyRender: false,
38954
39529
  extensions,
@@ -38957,6 +39532,37 @@ var UEditor = React83.forwardRef(({
38957
39532
  autofocus,
38958
39533
  editorProps: {
38959
39534
  handleDOMEvents: {
39535
+ mousedown: (view, event) => {
39536
+ if (!(event instanceof MouseEvent)) return false;
39537
+ const pickState = beginFormulaRangePick(view, event);
39538
+ if (!pickState) return false;
39539
+ formulaRangePickRef.current = pickState;
39540
+ syncFormulaRangeHighlight(pickState);
39541
+ return true;
39542
+ },
39543
+ mousemove: (view, event) => {
39544
+ if (!(event instanceof MouseEvent)) return false;
39545
+ const pickState = formulaRangePickRef.current;
39546
+ if (!pickState) return false;
39547
+ if (event.buttons === 0) {
39548
+ formulaRangePickRef.current = null;
39549
+ syncFormulaRangeHighlight(null);
39550
+ return false;
39551
+ }
39552
+ const nextPickState = updateFormulaRangePick(view, pickState, event);
39553
+ formulaRangePickRef.current = nextPickState;
39554
+ syncFormulaRangeHighlight(nextPickState);
39555
+ return true;
39556
+ },
39557
+ mouseup: (_view, event) => {
39558
+ if (!(event instanceof MouseEvent)) return false;
39559
+ if (!formulaRangePickRef.current) return false;
39560
+ formulaRangePickRef.current = null;
39561
+ syncFormulaRangeHighlight(null);
39562
+ event.preventDefault();
39563
+ event.stopPropagation();
39564
+ return true;
39565
+ },
38960
39566
  keydown: (_view, event) => {
38961
39567
  if (!(event instanceof KeyboardEvent)) return false;
38962
39568
  if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
@@ -39122,7 +39728,10 @@ var UEditor = React83.forwardRef(({
39122
39728
  /* @__PURE__ */ jsxs79(
39123
39729
  "div",
39124
39730
  {
39125
- ref: editorContentRef,
39731
+ ref: (node) => {
39732
+ editorContentRef.current = node;
39733
+ formulaRangeSurfaceRef.current = node;
39734
+ },
39126
39735
  className: "relative flex-1 overflow-y-auto",
39127
39736
  style: {
39128
39737
  minHeight: editable ? minHeight : void 0,
@@ -39154,6 +39763,20 @@ var UEditor = React83.forwardRef(({
39154
39763
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
39155
39764
  }
39156
39765
  ),
39766
+ formulaRangeHighlight && /* @__PURE__ */ jsx94(
39767
+ "span",
39768
+ {
39769
+ "aria-hidden": "true",
39770
+ "data-ueditor-formula-range-highlight": "",
39771
+ className: "pointer-events-none absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10",
39772
+ style: {
39773
+ left: formulaRangeHighlight.left,
39774
+ top: formulaRangeHighlight.top,
39775
+ width: formulaRangeHighlight.width,
39776
+ height: formulaRangeHighlight.height
39777
+ }
39778
+ }
39779
+ ),
39157
39780
  editable && /* @__PURE__ */ jsx94(TableControls, { editor, containerRef: editorContentRef }),
39158
39781
  /* @__PURE__ */ jsx94(
39159
39782
  EditorContent,