@underverse-ui/underverse 1.0.157 → 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)(
@@ -31049,9 +31144,41 @@ function applyTableCellBackground(editor, color) {
31049
31144
  }
31050
31145
  editor.chain().focus().setCellAttribute("backgroundColor", value).run();
31051
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
+ }
31052
31177
  var BubbleMenuContent = ({
31053
31178
  editor,
31054
31179
  onKeepOpenChange,
31180
+ onLinkInputOpenChange,
31181
+ onRequestClose,
31055
31182
  fontSizes,
31056
31183
  lineHeights,
31057
31184
  initialShowLinkInput = false
@@ -31082,6 +31209,8 @@ var BubbleMenuContent = ({
31082
31209
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
31083
31210
  const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
31084
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";
31085
31214
  const isInTable2 = (0, import_tables3.isInTable)(editor.state);
31086
31215
  const canMergeCells = isInTable2 && editor.can().mergeCells();
31087
31216
  const canSplitCell = isInTable2 && editor.can().splitCell();
@@ -31095,6 +31224,15 @@ var BubbleMenuContent = ({
31095
31224
  () => (lineHeights ?? getDefaultLineHeights()).filter((option) => ["1.2", "1.5", "1.75"].includes(option.value)),
31096
31225
  [lineHeights]
31097
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
+ );
31098
31236
  (0, import_react64.useEffect)(() => {
31099
31237
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
31100
31238
  }, [currentFontSize]);
@@ -31115,7 +31253,55 @@ var BubbleMenuContent = ({
31115
31253
  };
31116
31254
  (0, import_react64.useEffect)(() => {
31117
31255
  onKeepOpenChange?.(showLinkInput);
31118
- }, [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]);
31119
31305
  (0, import_react64.useEffect)(() => {
31120
31306
  if (!showLinkInput) return;
31121
31307
  const close2 = () => setShowLinkInput(false);
@@ -31147,51 +31333,53 @@ var BubbleMenuContent = ({
31147
31333
  const isTextPalette = activeColorPalette === "text";
31148
31334
  const isHighlightPalette = activeColorPalette === "highlight";
31149
31335
  if (activeColorPalette === "cell-border") {
31150
- const currentBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
31151
- const currentBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
31152
31336
  const currentBorderColor = editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || "currentColor";
31153
- 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: [
31154
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" }),
31155
31339
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-1", children: [
31156
31340
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderStyle") || "Border Style" }),
31157
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31158
- "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",
31159
31349
  {
31160
- 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",
31161
- value: currentBorderStyle,
31162
- onChange: (e) => {
31163
- const style = e.target.value;
31164
- editor.chain().focus().setCellAttribute("borderStyle", style).run();
31165
- },
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
+ ),
31166
31358
  children: [
31167
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "solid", children: "Solid" }),
31168
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "dashed", children: "Dashed" }),
31169
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "dotted", children: "Dotted" }),
31170
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "double", children: "Double" }),
31171
- /* @__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 })
31172
31361
  ]
31173
- }
31174
- )
31362
+ },
31363
+ style
31364
+ )) })
31175
31365
  ] }),
31176
31366
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex flex-col gap-1", children: [
31177
31367
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderWidth") || "Border Width" }),
31178
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31179
- "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",
31180
31370
  {
31181
- 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",
31182
- value: currentBorderWidth,
31183
- onChange: (e) => {
31184
- const width = e.target.value;
31185
- editor.chain().focus().setCellAttribute("borderWidth", width).run();
31186
- },
31187
- children: [
31188
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "1px", children: "1px" }),
31189
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "2px", children: "2px" }),
31190
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "3px", children: "3px" }),
31191
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("option", { value: "4px", children: "4px" })
31192
- ]
31193
- }
31194
- )
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
+ )) })
31195
31383
  ] }),
31196
31384
  /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(
31197
31385
  "button",
@@ -31213,70 +31401,36 @@ var BubbleMenuContent = ({
31213
31401
  ]
31214
31402
  }
31215
31403
  ),
31216
- /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: [
31217
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31218
- "button",
31219
- {
31220
- type: "button",
31221
- onClick: () => {
31222
- editor.chain().focus().setCellAttribute("borderColor", null).setCellAttribute("borderStyle", null).setCellAttribute("borderWidth", null).run();
31223
- setActiveColorPalette(null);
31224
- },
31225
- className: "text-xs text-destructive hover:underline",
31226
- children: t("tableMenu.clearBorder") || "Clear Border"
31227
- }
31228
- ),
31229
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31230
- "button",
31231
- {
31232
- type: "button",
31233
- onClick: () => setActiveColorPalette(null),
31234
- className: "text-xs font-medium text-primary hover:underline",
31235
- children: t("tableMenu.done") || "Done"
31236
- }
31237
- )
31238
- ] })
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
+ ) })
31239
31414
  ] });
31240
31415
  }
31241
31416
  if (activeColorPalette === "cell-border-color") {
31242
31417
  const currentBorderColor = normalizeStyleValue(editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor) || "";
31243
- 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)(
31244
31419
  EditorColorPalette,
31245
31420
  {
31246
- colors: highlightColors,
31421
+ colors: borderColors,
31247
31422
  currentColor: currentBorderColor,
31248
- onSelect: (color) => {
31249
- const value = color || null;
31250
- editor.chain().focus().setCellAttribute("borderColor", value).run();
31251
- setActiveColorPalette("cell-border");
31252
- },
31423
+ onSelect: applyTableCellBorderColorAndClose,
31253
31424
  label: t("tableMenu.borderColor") || "Border Color"
31254
31425
  }
31255
31426
  ) });
31256
31427
  }
31257
- 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)(
31258
31429
  EditorColorPalette,
31259
31430
  {
31260
31431
  colors: isTextPalette ? textColors : highlightColors,
31261
31432
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
31262
- onSelect: (color) => {
31263
- if (isTextPalette) {
31264
- if (color === "inherit") {
31265
- editor.chain().focus().unsetColor().run();
31266
- } else {
31267
- editor.chain().focus().setColor(color).run();
31268
- }
31269
- } else if (isHighlightPalette) {
31270
- if (color === "") {
31271
- editor.chain().focus().unsetHighlight().run();
31272
- } else {
31273
- editor.chain().focus().toggleHighlight({ color }).run();
31274
- }
31275
- } else {
31276
- applyTableCellBackground(editor, color);
31277
- }
31278
- setActiveColorPalette(null);
31279
- },
31433
+ onSelect: applyInlineColorAndClose,
31280
31434
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
31281
31435
  }
31282
31436
  ) });
@@ -31601,6 +31755,9 @@ var BubbleMenuContent = ({
31601
31755
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(
31602
31756
  ToolbarButton,
31603
31757
  {
31758
+ onMouseDown: () => {
31759
+ onKeepOpenChange?.(true);
31760
+ },
31604
31761
  onClick: () => setActiveColorPalette("cell-border"),
31605
31762
  active: Boolean(
31606
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
@@ -31682,14 +31839,27 @@ var CustomBubbleMenu = ({
31682
31839
  const SHOW_DELAY_MS = 180;
31683
31840
  const BUBBLE_MENU_OFFSET = 16;
31684
31841
  const [isVisible, setIsVisible] = (0, import_react64.useState)(false);
31842
+ const [linkInputOpen, setLinkInputOpen] = (0, import_react64.useState)(false);
31685
31843
  const [position, setPosition] = (0, import_react64.useState)({ top: 0, left: 0 });
31686
31844
  const menuRef = (0, import_react64.useRef)(null);
31687
31845
  const keepOpenRef = (0, import_react64.useRef)(false);
31688
31846
  const showTimeoutRef = (0, import_react64.useRef)(null);
31847
+ const suppressShowUntilRef = (0, import_react64.useRef)(0);
31689
31848
  const setKeepOpen = (0, import_react64.useCallback)((next) => {
31690
31849
  keepOpenRef.current = next;
31850
+ if (!next) setLinkInputOpen(false);
31691
31851
  if (next) setIsVisible(true);
31692
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
+ }, []);
31693
31863
  (0, import_react64.useEffect)(() => {
31694
31864
  const clearShowTimeout = () => {
31695
31865
  if (showTimeoutRef.current) {
@@ -31701,6 +31871,11 @@ var CustomBubbleMenu = ({
31701
31871
  const { state, view } = editor;
31702
31872
  const { from, to, empty } = state.selection;
31703
31873
  const isLinkActive = editor.isActive("link");
31874
+ if (Date.now() < suppressShowUntilRef.current) {
31875
+ clearShowTimeout();
31876
+ setIsVisible(false);
31877
+ return;
31878
+ }
31704
31879
  if (!keepOpenRef.current && (empty && !isLinkActive || !view.hasFocus())) {
31705
31880
  clearShowTimeout();
31706
31881
  setIsVisible(false);
@@ -31712,6 +31887,11 @@ var CustomBubbleMenu = ({
31712
31887
  start = view.coordsAtPos(from);
31713
31888
  end = view.coordsAtPos(to);
31714
31889
  } catch {
31890
+ if (keepOpenRef.current) {
31891
+ clearShowTimeout();
31892
+ setIsVisible(true);
31893
+ return;
31894
+ }
31715
31895
  clearShowTimeout();
31716
31896
  setIsVisible(false);
31717
31897
  return;
@@ -31769,15 +31949,34 @@ var CustomBubbleMenu = ({
31769
31949
  left: `${position.left}px`,
31770
31950
  transform: "translate(-50%, -100%)"
31771
31951
  },
31772
- onMouseDown: (e) => e.preventDefault(),
31773
- 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)(
31774
31971
  BubbleMenuContent,
31775
31972
  {
31776
31973
  editor,
31777
31974
  onKeepOpenChange: setKeepOpen,
31975
+ onLinkInputOpenChange: setLinkInputOpen,
31976
+ onRequestClose: closeBubbleMenu,
31778
31977
  fontSizes,
31779
31978
  lineHeights,
31780
- initialShowLinkInput: keepOpenRef.current
31979
+ initialShowLinkInput: linkInputOpen
31781
31980
  }
31782
31981
  )
31783
31982
  }
@@ -38213,6 +38412,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38213
38412
  proseMirror.addEventListener("keyup", handleSelectionChange);
38214
38413
  proseMirror.addEventListener("focusin", handleSelectionChange);
38215
38414
  document.addEventListener("selectionchange", handleSelectionChange);
38415
+ surface?.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38216
38416
  surface?.addEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38217
38417
  window.addEventListener("resize", handleActiveCellLayoutChange);
38218
38418
  document.addEventListener("pointermove", handlePointerMove);
@@ -38232,6 +38432,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38232
38432
  proseMirror.removeEventListener("keyup", handleSelectionChange);
38233
38433
  proseMirror.removeEventListener("focusin", handleSelectionChange);
38234
38434
  document.removeEventListener("selectionchange", handleSelectionChange);
38435
+ surface?.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38235
38436
  surface?.removeEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38236
38437
  window.removeEventListener("resize", handleActiveCellLayoutChange);
38237
38438
  document.removeEventListener("pointermove", handlePointerMove);
@@ -38266,6 +38467,144 @@ function useUEditorTableInteractions(editor, editable = true) {
38266
38467
  var import_react69 = __toESM(require("react"), 1);
38267
38468
  var import_react70 = require("@tiptap/react");
38268
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
38269
38608
  var import_jsx_runtime94 = require("react/jsx-runtime");
38270
38609
  function MenuTableInsertGrid({
38271
38610
  insertLabel,
@@ -38748,6 +39087,10 @@ var MenuBar = ({
38748
39087
  const [showSourceDialog, setShowSourceDialog] = (0, import_react69.useState)(false);
38749
39088
  const [sourceHtml, setSourceHtml] = (0, import_react69.useState)("");
38750
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
+ );
38751
39094
  const openSourceDialog = () => {
38752
39095
  setSourceHtml(editor.getHTML());
38753
39096
  setShowSourceDialog(true);
@@ -38756,10 +39099,7 @@ var MenuBar = ({
38756
39099
  setShowPreviewDialog(true);
38757
39100
  };
38758
39101
  const handlePreview = () => {
38759
- if (onPreview) {
38760
- onPreview();
38761
- return;
38762
- }
39102
+ if (onPreview?.(editor.getHTML()) === false) return;
38763
39103
  openPreviewDialog();
38764
39104
  };
38765
39105
  const applySourceHtml = () => {
@@ -38994,12 +39334,12 @@ var MenuBar = ({
38994
39334
  "div",
38995
39335
  {
38996
39336
  "data-testid": "preview-content",
38997
- 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",
38998
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)(
38999
39339
  "div",
39000
39340
  {
39001
39341
  className: UEDITOR_PROSEMIRROR_CLASS_NAME,
39002
- dangerouslySetInnerHTML: { __html: editor.getHTML() }
39342
+ dangerouslySetInnerHTML: { __html: previewHtml }
39003
39343
  }
39004
39344
  )
39005
39345
  }