@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.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(
@@ -30954,9 +31049,41 @@ function applyTableCellBackground(editor, color) {
30954
31049
  }
30955
31050
  editor.chain().focus().setCellAttribute("backgroundColor", value).run();
30956
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
+ }
30957
31082
  var BubbleMenuContent = ({
30958
31083
  editor,
30959
31084
  onKeepOpenChange,
31085
+ onLinkInputOpenChange,
31086
+ onRequestClose,
30960
31087
  fontSizes,
30961
31088
  lineHeights,
30962
31089
  initialShowLinkInput = false
@@ -30987,6 +31114,8 @@ var BubbleMenuContent = ({
30987
31114
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
30988
31115
  const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
30989
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";
30990
31119
  const isInTable2 = isSelectionInTable(editor.state);
30991
31120
  const canMergeCells = isInTable2 && editor.can().mergeCells();
30992
31121
  const canSplitCell = isInTable2 && editor.can().splitCell();
@@ -31000,6 +31129,15 @@ var BubbleMenuContent = ({
31000
31129
  () => (lineHeights ?? getDefaultLineHeights()).filter((option) => ["1.2", "1.5", "1.75"].includes(option.value)),
31001
31130
  [lineHeights]
31002
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
+ );
31003
31141
  useEffect36(() => {
31004
31142
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
31005
31143
  }, [currentFontSize]);
@@ -31020,7 +31158,55 @@ var BubbleMenuContent = ({
31020
31158
  };
31021
31159
  useEffect36(() => {
31022
31160
  onKeepOpenChange?.(showLinkInput);
31023
- }, [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]);
31024
31210
  useEffect36(() => {
31025
31211
  if (!showLinkInput) return;
31026
31212
  const close2 = () => setShowLinkInput(false);
@@ -31052,51 +31238,53 @@ var BubbleMenuContent = ({
31052
31238
  const isTextPalette = activeColorPalette === "text";
31053
31239
  const isHighlightPalette = activeColorPalette === "highlight";
31054
31240
  if (activeColorPalette === "cell-border") {
31055
- const currentBorderStyle = editor.getAttributes("tableCell").borderStyle || editor.getAttributes("tableHeader").borderStyle || "solid";
31056
- const currentBorderWidth = editor.getAttributes("tableCell").borderWidth || editor.getAttributes("tableHeader").borderWidth || "1px";
31057
31241
  const currentBorderColor = editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor || "currentColor";
31058
- 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: [
31059
31243
  /* @__PURE__ */ jsx87("div", { className: "font-semibold text-xs text-muted-foreground uppercase tracking-wider mb-1", children: t("tableMenu.cellBorder") || "Cell Borders" }),
31060
31244
  /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-1", children: [
31061
31245
  /* @__PURE__ */ jsx87("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderStyle") || "Border Style" }),
31062
- /* @__PURE__ */ jsxs73(
31063
- "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",
31064
31254
  {
31065
- 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",
31066
- value: currentBorderStyle,
31067
- onChange: (e) => {
31068
- const style = e.target.value;
31069
- editor.chain().focus().setCellAttribute("borderStyle", style).run();
31070
- },
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
+ ),
31071
31263
  children: [
31072
- /* @__PURE__ */ jsx87("option", { value: "solid", children: "Solid" }),
31073
- /* @__PURE__ */ jsx87("option", { value: "dashed", children: "Dashed" }),
31074
- /* @__PURE__ */ jsx87("option", { value: "dotted", children: "Dotted" }),
31075
- /* @__PURE__ */ jsx87("option", { value: "double", children: "Double" }),
31076
- /* @__PURE__ */ jsx87("option", { value: "none", children: "None" })
31264
+ /* @__PURE__ */ jsx87(BorderStylePreviewIcon, { style }),
31265
+ /* @__PURE__ */ jsx87("span", { children: label })
31077
31266
  ]
31078
- }
31079
- )
31267
+ },
31268
+ style
31269
+ )) })
31080
31270
  ] }),
31081
31271
  /* @__PURE__ */ jsxs73("div", { className: "flex flex-col gap-1", children: [
31082
31272
  /* @__PURE__ */ jsx87("label", { className: "text-xs text-muted-foreground", children: t("tableMenu.borderWidth") || "Border Width" }),
31083
- /* @__PURE__ */ jsxs73(
31084
- "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",
31085
31275
  {
31086
- 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",
31087
- value: currentBorderWidth,
31088
- onChange: (e) => {
31089
- const width = e.target.value;
31090
- editor.chain().focus().setCellAttribute("borderWidth", width).run();
31091
- },
31092
- children: [
31093
- /* @__PURE__ */ jsx87("option", { value: "1px", children: "1px" }),
31094
- /* @__PURE__ */ jsx87("option", { value: "2px", children: "2px" }),
31095
- /* @__PURE__ */ jsx87("option", { value: "3px", children: "3px" }),
31096
- /* @__PURE__ */ jsx87("option", { value: "4px", children: "4px" })
31097
- ]
31098
- }
31099
- )
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
+ )) })
31100
31288
  ] }),
31101
31289
  /* @__PURE__ */ jsxs73(
31102
31290
  "button",
@@ -31118,70 +31306,36 @@ var BubbleMenuContent = ({
31118
31306
  ]
31119
31307
  }
31120
31308
  ),
31121
- /* @__PURE__ */ jsxs73("div", { className: "flex items-center justify-between gap-2 mt-2 pt-2 border-t border-border", children: [
31122
- /* @__PURE__ */ jsx87(
31123
- "button",
31124
- {
31125
- type: "button",
31126
- onClick: () => {
31127
- editor.chain().focus().setCellAttribute("borderColor", null).setCellAttribute("borderStyle", null).setCellAttribute("borderWidth", null).run();
31128
- setActiveColorPalette(null);
31129
- },
31130
- className: "text-xs text-destructive hover:underline",
31131
- children: t("tableMenu.clearBorder") || "Clear Border"
31132
- }
31133
- ),
31134
- /* @__PURE__ */ jsx87(
31135
- "button",
31136
- {
31137
- type: "button",
31138
- onClick: () => setActiveColorPalette(null),
31139
- className: "text-xs font-medium text-primary hover:underline",
31140
- children: t("tableMenu.done") || "Done"
31141
- }
31142
- )
31143
- ] })
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
+ ) })
31144
31319
  ] });
31145
31320
  }
31146
31321
  if (activeColorPalette === "cell-border-color") {
31147
31322
  const currentBorderColor = normalizeStyleValue(editor.getAttributes("tableCell").borderColor || editor.getAttributes("tableHeader").borderColor) || "";
31148
- 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(
31149
31324
  EditorColorPalette,
31150
31325
  {
31151
- colors: highlightColors,
31326
+ colors: borderColors,
31152
31327
  currentColor: currentBorderColor,
31153
- onSelect: (color) => {
31154
- const value = color || null;
31155
- editor.chain().focus().setCellAttribute("borderColor", value).run();
31156
- setActiveColorPalette("cell-border");
31157
- },
31328
+ onSelect: applyTableCellBorderColorAndClose,
31158
31329
  label: t("tableMenu.borderColor") || "Border Color"
31159
31330
  }
31160
31331
  ) });
31161
31332
  }
31162
- 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(
31163
31334
  EditorColorPalette,
31164
31335
  {
31165
31336
  colors: isTextPalette ? textColors : highlightColors,
31166
31337
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
31167
- onSelect: (color) => {
31168
- if (isTextPalette) {
31169
- if (color === "inherit") {
31170
- editor.chain().focus().unsetColor().run();
31171
- } else {
31172
- editor.chain().focus().setColor(color).run();
31173
- }
31174
- } else if (isHighlightPalette) {
31175
- if (color === "") {
31176
- editor.chain().focus().unsetHighlight().run();
31177
- } else {
31178
- editor.chain().focus().toggleHighlight({ color }).run();
31179
- }
31180
- } else {
31181
- applyTableCellBackground(editor, color);
31182
- }
31183
- setActiveColorPalette(null);
31184
- },
31338
+ onSelect: applyInlineColorAndClose,
31185
31339
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
31186
31340
  }
31187
31341
  ) });
@@ -31506,6 +31660,9 @@ var BubbleMenuContent = ({
31506
31660
  /* @__PURE__ */ jsx87(
31507
31661
  ToolbarButton,
31508
31662
  {
31663
+ onMouseDown: () => {
31664
+ onKeepOpenChange?.(true);
31665
+ },
31509
31666
  onClick: () => setActiveColorPalette("cell-border"),
31510
31667
  active: Boolean(
31511
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
@@ -31587,14 +31744,27 @@ var CustomBubbleMenu = ({
31587
31744
  const SHOW_DELAY_MS = 180;
31588
31745
  const BUBBLE_MENU_OFFSET = 16;
31589
31746
  const [isVisible, setIsVisible] = useState48(false);
31747
+ const [linkInputOpen, setLinkInputOpen] = useState48(false);
31590
31748
  const [position, setPosition] = useState48({ top: 0, left: 0 });
31591
31749
  const menuRef = useRef33(null);
31592
31750
  const keepOpenRef = useRef33(false);
31593
31751
  const showTimeoutRef = useRef33(null);
31752
+ const suppressShowUntilRef = useRef33(0);
31594
31753
  const setKeepOpen = useCallback22((next) => {
31595
31754
  keepOpenRef.current = next;
31755
+ if (!next) setLinkInputOpen(false);
31596
31756
  if (next) setIsVisible(true);
31597
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
+ }, []);
31598
31768
  useEffect36(() => {
31599
31769
  const clearShowTimeout = () => {
31600
31770
  if (showTimeoutRef.current) {
@@ -31606,6 +31776,11 @@ var CustomBubbleMenu = ({
31606
31776
  const { state, view } = editor;
31607
31777
  const { from, to, empty } = state.selection;
31608
31778
  const isLinkActive = editor.isActive("link");
31779
+ if (Date.now() < suppressShowUntilRef.current) {
31780
+ clearShowTimeout();
31781
+ setIsVisible(false);
31782
+ return;
31783
+ }
31609
31784
  if (!keepOpenRef.current && (empty && !isLinkActive || !view.hasFocus())) {
31610
31785
  clearShowTimeout();
31611
31786
  setIsVisible(false);
@@ -31617,6 +31792,11 @@ var CustomBubbleMenu = ({
31617
31792
  start = view.coordsAtPos(from);
31618
31793
  end = view.coordsAtPos(to);
31619
31794
  } catch {
31795
+ if (keepOpenRef.current) {
31796
+ clearShowTimeout();
31797
+ setIsVisible(true);
31798
+ return;
31799
+ }
31620
31800
  clearShowTimeout();
31621
31801
  setIsVisible(false);
31622
31802
  return;
@@ -31674,15 +31854,34 @@ var CustomBubbleMenu = ({
31674
31854
  left: `${position.left}px`,
31675
31855
  transform: "translate(-50%, -100%)"
31676
31856
  },
31677
- onMouseDown: (e) => e.preventDefault(),
31678
- 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(
31679
31876
  BubbleMenuContent,
31680
31877
  {
31681
31878
  editor,
31682
31879
  onKeepOpenChange: setKeepOpen,
31880
+ onLinkInputOpenChange: setLinkInputOpen,
31881
+ onRequestClose: closeBubbleMenu,
31683
31882
  fontSizes,
31684
31883
  lineHeights,
31685
- initialShowLinkInput: keepOpenRef.current
31884
+ initialShowLinkInput: linkInputOpen
31686
31885
  }
31687
31886
  )
31688
31887
  }
@@ -38129,6 +38328,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38129
38328
  proseMirror.addEventListener("keyup", handleSelectionChange);
38130
38329
  proseMirror.addEventListener("focusin", handleSelectionChange);
38131
38330
  document.addEventListener("selectionchange", handleSelectionChange);
38331
+ surface?.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38132
38332
  surface?.addEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38133
38333
  window.addEventListener("resize", handleActiveCellLayoutChange);
38134
38334
  document.addEventListener("pointermove", handlePointerMove);
@@ -38148,6 +38348,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38148
38348
  proseMirror.removeEventListener("keyup", handleSelectionChange);
38149
38349
  proseMirror.removeEventListener("focusin", handleSelectionChange);
38150
38350
  document.removeEventListener("selectionchange", handleSelectionChange);
38351
+ surface?.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, handleActiveCellLayoutChange);
38151
38352
  surface?.removeEventListener("scroll", handleActiveCellLayoutChange, scrollListenerOptions);
38152
38353
  window.removeEventListener("resize", handleActiveCellLayoutChange);
38153
38354
  document.removeEventListener("pointermove", handlePointerMove);
@@ -38179,7 +38380,7 @@ function useUEditorTableInteractions(editor, editable = true) {
38179
38380
  }
38180
38381
 
38181
38382
  // src/components/UEditor/menu-bar.tsx
38182
- import React82, { useRef as useRef36, useState as useState49 } from "react";
38383
+ import React82, { useMemo as useMemo24, useRef as useRef36, useState as useState49 } from "react";
38183
38384
  import { useEditorState as useEditorState3 } from "@tiptap/react";
38184
38385
  import {
38185
38386
  AlignCenter as AlignCenter4,
@@ -38210,6 +38411,144 @@ import {
38210
38411
  Undo as UndoIcon2,
38211
38412
  Upload as Upload4
38212
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
38213
38552
  import { Fragment as Fragment35, jsx as jsx93, jsxs as jsxs78 } from "react/jsx-runtime";
38214
38553
  function MenuTableInsertGrid({
38215
38554
  insertLabel,
@@ -38692,6 +39031,10 @@ var MenuBar = ({
38692
39031
  const [showSourceDialog, setShowSourceDialog] = useState49(false);
38693
39032
  const [sourceHtml, setSourceHtml] = useState49("");
38694
39033
  const [showPreviewDialog, setShowPreviewDialog] = useState49(false);
39034
+ const previewHtml = useMemo24(
39035
+ () => showPreviewDialog ? prepareUEditorPreviewHtml(editor.getHTML()) : "",
39036
+ [editor, showPreviewDialog]
39037
+ );
38695
39038
  const openSourceDialog = () => {
38696
39039
  setSourceHtml(editor.getHTML());
38697
39040
  setShowSourceDialog(true);
@@ -38700,10 +39043,7 @@ var MenuBar = ({
38700
39043
  setShowPreviewDialog(true);
38701
39044
  };
38702
39045
  const handlePreview = () => {
38703
- if (onPreview) {
38704
- onPreview();
38705
- return;
38706
- }
39046
+ if (onPreview?.(editor.getHTML()) === false) return;
38707
39047
  openPreviewDialog();
38708
39048
  };
38709
39049
  const applySourceHtml = () => {
@@ -38938,12 +39278,12 @@ var MenuBar = ({
38938
39278
  "div",
38939
39279
  {
38940
39280
  "data-testid": "preview-content",
38941
- 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",
38942
39282
  children: editor.isEmpty ? /* @__PURE__ */ jsx93("p", { className: "text-muted-foreground text-sm", children: t("menubar.previewEmpty") }) : /* @__PURE__ */ jsx93(
38943
39283
  "div",
38944
39284
  {
38945
39285
  className: UEDITOR_PROSEMIRROR_CLASS_NAME,
38946
- dangerouslySetInnerHTML: { __html: editor.getHTML() }
39286
+ dangerouslySetInnerHTML: { __html: previewHtml }
38947
39287
  }
38948
39288
  )
38949
39289
  }
@@ -39151,7 +39491,7 @@ var UEditor = React83.forwardRef(({
39151
39491
  }
39152
39492
  });
39153
39493
  }, []);
39154
- const resolvedUploadFile = useMemo24(() => {
39494
+ const resolvedUploadFile = useMemo25(() => {
39155
39495
  if (uploadFile) return uploadFile;
39156
39496
  if (uploadFileForSave) {
39157
39497
  return async (file) => {
@@ -39161,7 +39501,7 @@ var UEditor = React83.forwardRef(({
39161
39501
  }
39162
39502
  return uploadImage;
39163
39503
  }, [uploadFile, uploadFileForSave, uploadImage]);
39164
- const extensions = useMemo24(
39504
+ const extensions = useMemo25(
39165
39505
  () => [
39166
39506
  ...buildUEditorExtensions({
39167
39507
  placeholder: effectivePlaceholder,