@underverse-ui/underverse 2.0.0 → 2.0.2

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.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  Modal_default
4
- } from "./chunk-MNZVW2QX.js";
4
+ } from "./chunk-UNDGI6ZI.js";
5
5
  import {
6
6
  DEFAULT_TABLE_ROW_HEIGHT,
7
7
  DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
@@ -21,7 +21,7 @@ import {
21
21
  sanitizeUEditorUrl,
22
22
  useDropdownMenuClose,
23
23
  useSharedEditorUiRenderState
24
- } from "./chunk-LTIU5FQY.js";
24
+ } from "./chunk-Z6WE65J2.js";
25
25
  import {
26
26
  cn,
27
27
  useSmartTranslations
@@ -944,4 +944,4 @@ var MenuBar = ({
944
944
  export {
945
945
  MenuBar
946
946
  };
947
- //# sourceMappingURL=menu-bar-SPAAI5X6.js.map
947
+ //# sourceMappingURL=menu-bar-AAD3LQTN.js.map
package/dist/ueditor.cjs CHANGED
@@ -4152,6 +4152,9 @@ function getClipboardData(dataTransfer, type) {
4152
4152
  return "";
4153
4153
  }
4154
4154
  }
4155
+ function hasClipboardHtmlTable(dataTransfer) {
4156
+ return /<table(?:\s|>)/i.test(getClipboardData(dataTransfer, "text/html"));
4157
+ }
4155
4158
  function extractClipboardHtmlFragment(html) {
4156
4159
  const startMarker = "<!--StartFragment-->";
4157
4160
  const endMarker = "<!--EndFragment-->";
@@ -4162,6 +4165,13 @@ function extractClipboardHtmlFragment(html) {
4162
4165
  }
4163
4166
  return html;
4164
4167
  }
4168
+ function hasMeaningfulContentOutsideTable(container) {
4169
+ const clone = container.cloneNode(true);
4170
+ clone.querySelectorAll("table, style, script, meta, link").forEach((element) => element.remove());
4171
+ const remainingText = (clone.textContent ?? "").replace(/\u00a0/g, " ").replace(/\u200b/g, "").trim();
4172
+ if (remainingText) return true;
4173
+ return !!clone.querySelector("img, video, audio, iframe, object, embed, svg, canvas, hr, input, textarea, select, button");
4174
+ }
4165
4175
  function normalizeClipboardCellText(value) {
4166
4176
  return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\u00a0/g, " ").replace(/[ \t]+\n/g, "\n").replace(/\n[ \t]+/g, "\n").replace(/\n+$/g, "").replace(/^\n+/g, "").trim();
4167
4177
  }
@@ -4772,7 +4782,10 @@ function getClipboardTableContent(dataTransfer) {
4772
4782
  const fragment = extractClipboardHtmlFragment(html);
4773
4783
  const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
4774
4784
  const styleMap = parseClipboardCssClassStyles(doc);
4775
- const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
4785
+ const sourceBody = fragmentDoc.querySelector("table") ? fragmentDoc.body : doc.body;
4786
+ const tables = sourceBody.querySelectorAll("table");
4787
+ if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
4788
+ const table = tables[0];
4776
4789
  if (!(table instanceof HTMLTableElement)) return null;
4777
4790
  return createTableContent(getHtmlTableRows(table, styleMap), 1, {
4778
4791
  backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
@@ -4951,6 +4964,7 @@ var init_clipboard_images = __esm({
4951
4964
  editor.chain().focus().insertContent(tableContent).run();
4952
4965
  return true;
4953
4966
  }
4967
+ if (hasClipboardHtmlTable(event.clipboardData)) return false;
4954
4968
  const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
4955
4969
  if (tsvTableContent) {
4956
4970
  event.preventDefault();
@@ -6009,7 +6023,7 @@ var init_colors = __esm({
6009
6023
  "src/components/UEditor/colors.tsx"() {
6010
6024
  "use strict";
6011
6025
  "use client";
6012
- import_react31 = require("react");
6026
+ import_react31 = __toESM(require("react"), 1);
6013
6027
  init_useSmartTranslations();
6014
6028
  import_lucide_react11 = require("lucide-react");
6015
6029
  init_cn();
@@ -6177,12 +6191,25 @@ var init_colors = __esm({
6177
6191
  colors,
6178
6192
  currentColor,
6179
6193
  onSelect,
6194
+ onCustomColorSelect,
6180
6195
  label
6181
6196
  }) => {
6182
6197
  const t = useSmartTranslations("UEditor");
6183
6198
  const colorInputRef = (0, import_react31.useRef)(null);
6184
6199
  const automaticColor = colors[0]?.color ?? "";
6185
6200
  const paletteColors = colors.slice(1);
6201
+ const customColorSelect = onCustomColorSelect ?? onSelect;
6202
+ import_react31.default.useEffect(() => {
6203
+ const input = colorInputRef.current;
6204
+ if (!input) return;
6205
+ const commitColor = () => customColorSelect(input.value);
6206
+ input.addEventListener("change", commitColor);
6207
+ return () => input.removeEventListener("change", commitColor);
6208
+ }, [customColorSelect]);
6209
+ import_react31.default.useEffect(() => {
6210
+ const input = colorInputRef.current;
6211
+ if (input) input.value = currentColor.startsWith("#") ? currentColor : "#000000";
6212
+ }, [currentColor]);
6186
6213
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "w-56 p-2", children: [
6187
6214
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
6188
6215
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
@@ -6236,20 +6263,19 @@ var init_colors = __esm({
6236
6263
  }
6237
6264
  ),
6238
6265
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "flex-1 text-center", children: t("colors.moreColors") }),
6239
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Palette, { className: "h-4 w-4 text-muted-foreground" }),
6240
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6241
- "input",
6242
- {
6243
- ref: colorInputRef,
6244
- type: "color",
6245
- value: currentColor.startsWith("#") ? currentColor : "#000000",
6246
- onChange: (event) => onSelect(event.target.value),
6247
- className: "sr-only",
6248
- tabIndex: -1
6249
- }
6250
- )
6266
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Palette, { className: "h-4 w-4 text-muted-foreground" })
6251
6267
  ]
6252
6268
  }
6269
+ ),
6270
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
6271
+ "input",
6272
+ {
6273
+ ref: colorInputRef,
6274
+ type: "color",
6275
+ defaultValue: currentColor.startsWith("#") ? currentColor : "#000000",
6276
+ className: "sr-only",
6277
+ tabIndex: -1
6278
+ }
6253
6279
  )
6254
6280
  ] });
6255
6281
  };
@@ -14543,15 +14569,18 @@ var FloatingMenuContent = ({ editor }) => {
14543
14569
  }
14544
14570
  );
14545
14571
  };
14546
- function applyTableCellBackground(editor, color) {
14572
+ function applyTableCellBackground(editor, color, options = {}) {
14573
+ const shouldFocus = options.focus ?? true;
14547
14574
  const value = color || null;
14548
14575
  const { state, view } = editor;
14549
14576
  const applied = (0, import_tables8.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
14550
14577
  if (applied) {
14551
- view.focus();
14578
+ if (shouldFocus) view.focus();
14552
14579
  return;
14553
14580
  }
14554
- editor.chain().focus().setCellAttribute("backgroundColor", value).run();
14581
+ const chain = editor.chain();
14582
+ if (shouldFocus) chain.focus();
14583
+ chain.setCellAttribute("backgroundColor", value).run();
14555
14584
  }
14556
14585
  function applyTableCellAttribute2(editor, name, value, options = {}) {
14557
14586
  const shouldFocus = options.focus ?? true;
@@ -14609,6 +14638,30 @@ function BorderPositionPreviewIcon({ position }) {
14609
14638
  showVertical && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("path", { d: "M10 2V18", strokeWidth: "2" })
14610
14639
  ] });
14611
14640
  }
14641
+ function getBubbleMenuPosition({
14642
+ start,
14643
+ end,
14644
+ menuSize,
14645
+ viewport,
14646
+ offset = 16,
14647
+ padding = 8
14648
+ }) {
14649
+ const selectionTop = Math.min(start.top, end.top);
14650
+ const selectionBottom = Math.max(start.bottom, end.bottom);
14651
+ const spaceAbove = selectionTop - offset - padding;
14652
+ const spaceBelow = viewport.height - padding - selectionBottom - offset;
14653
+ const placement = menuSize.height <= spaceAbove || spaceAbove >= spaceBelow ? "top" : "bottom";
14654
+ const halfWidth = menuSize.width / 2;
14655
+ const minLeft = padding + halfWidth;
14656
+ const maxLeft = viewport.width - padding - halfWidth;
14657
+ const selectionCenter = (start.left + end.left) / 2;
14658
+ const left = maxLeft >= minLeft ? Math.min(maxLeft, Math.max(minLeft, selectionCenter)) : viewport.width / 2;
14659
+ const requestedTop = placement === "top" ? selectionTop - offset : selectionBottom + offset;
14660
+ const minTop = placement === "top" ? padding + menuSize.height : padding;
14661
+ const maxTop = placement === "top" ? viewport.height - padding : viewport.height - padding - menuSize.height;
14662
+ const top = maxTop >= minTop ? Math.min(maxTop, Math.max(minTop, requestedTop)) : Math.max(padding, requestedTop);
14663
+ return { top, left, placement };
14664
+ }
14612
14665
  function getBubbleMenuContext(editor) {
14613
14666
  const { selection } = editor.state;
14614
14667
  if (editor.isActive("image")) return "image";
@@ -14774,24 +14827,34 @@ var BubbleMenuContent = ({
14774
14827
  setActiveColorPalette(null);
14775
14828
  onKeepOpenChange?.(false);
14776
14829
  }, [onKeepOpenChange]);
14777
- const applyInlineColorAndClose = (0, import_react35.useCallback)((color) => {
14830
+ const applyInlineColor = (0, import_react35.useCallback)((color, focus) => {
14778
14831
  if (activeColorPalette === "text") {
14832
+ const chain = editor.chain();
14833
+ if (focus) chain.focus();
14779
14834
  if (color === "inherit") {
14780
- editor.chain().focus().unsetColor().run();
14835
+ chain.unsetColor().run();
14781
14836
  } else {
14782
- editor.chain().focus().setColor(color).run();
14837
+ chain.setColor(color).run();
14783
14838
  }
14784
14839
  } else if (activeColorPalette === "highlight") {
14840
+ const chain = editor.chain();
14841
+ if (focus) chain.focus();
14785
14842
  if (color === "") {
14786
- editor.chain().focus().unsetHighlight().run();
14843
+ chain.unsetHighlight().run();
14787
14844
  } else {
14788
- editor.chain().focus().toggleHighlight({ color }).run();
14845
+ chain.toggleHighlight({ color }).run();
14789
14846
  }
14790
14847
  } else {
14791
- applyTableCellBackground(editor, color);
14848
+ applyTableCellBackground(editor, color, { focus });
14792
14849
  }
14850
+ }, [activeColorPalette, editor]);
14851
+ const applyInlineColorAndClose = (0, import_react35.useCallback)((color) => {
14852
+ applyInlineColor(color, true);
14793
14853
  closeColorPalette();
14794
- }, [activeColorPalette, closeColorPalette, editor]);
14854
+ }, [applyInlineColor, closeColorPalette]);
14855
+ const applyCustomColor = (0, import_react35.useCallback)((color) => {
14856
+ applyInlineColor(color, false);
14857
+ }, [applyInlineColor]);
14795
14858
  (0, import_react35.useEffect)(() => {
14796
14859
  if (!showLinkInput) return;
14797
14860
  const close2 = () => setShowLinkInput(false);
@@ -14986,6 +15049,7 @@ var BubbleMenuContent = ({
14986
15049
  colors: isTextPalette ? textColors : highlightColors,
14987
15050
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
14988
15051
  onSelect: applyInlineColorAndClose,
15052
+ onCustomColorSelect: applyCustomColor,
14989
15053
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
14990
15054
  }
14991
15055
  ) });
@@ -15272,6 +15336,7 @@ var BubbleMenuContent = ({
15272
15336
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
15273
15337
  ToolbarButton,
15274
15338
  {
15339
+ onMouseDown: () => onKeepOpenChange?.(true),
15275
15340
  onClick: () => setActiveColorPalette("cell-bg"),
15276
15341
  active: Boolean(currentCellBgColor),
15277
15342
  title: t("tableMenu.cellBackground") || "Cell background",
@@ -15503,6 +15568,8 @@ var CustomBubbleMenu = ({
15503
15568
  placement: "top"
15504
15569
  });
15505
15570
  const menuRef = (0, import_react35.useRef)(null);
15571
+ const updatePositionRef = (0, import_react35.useRef)(() => {
15572
+ });
15506
15573
  const keepOpenRef = (0, import_react35.useRef)(false);
15507
15574
  const [keepOpen, setKeepOpenState] = (0, import_react35.useState)(false);
15508
15575
  const showTimeoutRef = (0, import_react35.useRef)(null);
@@ -15560,19 +15627,15 @@ var CustomBubbleMenu = ({
15560
15627
  setIsVisible(false);
15561
15628
  return;
15562
15629
  }
15563
- const viewportPadding = 8;
15564
15630
  const menuHeight = menuRef.current?.getBoundingClientRect().height || BUBBLE_MENU_ESTIMATED_HEIGHT;
15565
- const editorTop = view.dom.getBoundingClientRect().top;
15566
- const selectionTop = Math.min(start.top, end.top);
15567
- const selectionBottom = Math.max(start.bottom, end.bottom);
15568
- const hasRoomAboveEditor = selectionTop - BUBBLE_MENU_OFFSET - menuHeight >= editorTop + viewportPadding;
15569
- const placement = hasRoomAboveEditor ? "top" : "bottom";
15570
- const left = Math.min(
15571
- window.innerWidth - viewportPadding,
15572
- Math.max(viewportPadding, (start.left + end.left) / 2)
15573
- );
15574
- const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
15575
- setPosition2({ top, left, placement });
15631
+ const menuWidth = menuRef.current?.getBoundingClientRect().width || 0;
15632
+ setPosition2(getBubbleMenuPosition({
15633
+ start,
15634
+ end,
15635
+ menuSize: { width: menuWidth, height: menuHeight },
15636
+ viewport: { width: window.innerWidth, height: window.innerHeight },
15637
+ offset: BUBBLE_MENU_OFFSET
15638
+ }));
15576
15639
  if (keepOpenRef.current) {
15577
15640
  clearShowTimeout();
15578
15641
  setIsVisible(true);
@@ -15601,8 +15664,11 @@ var CustomBubbleMenu = ({
15601
15664
  editor.on("transaction", schedulePositionUpdate);
15602
15665
  editor.on("focus", schedulePositionUpdate);
15603
15666
  editor.on("blur", handleBlur);
15667
+ updatePositionRef.current = updatePosition;
15604
15668
  schedulePositionUpdate();
15605
15669
  return () => {
15670
+ updatePositionRef.current = () => {
15671
+ };
15606
15672
  if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
15607
15673
  clearShowTimeout();
15608
15674
  editor.off("transaction", schedulePositionUpdate);
@@ -15610,6 +15676,24 @@ var CustomBubbleMenu = ({
15610
15676
  editor.off("blur", handleBlur);
15611
15677
  };
15612
15678
  }, [editor]);
15679
+ (0, import_react35.useEffect)(() => {
15680
+ if (!isVisible || typeof ResizeObserver === "undefined") return;
15681
+ const menu = menuRef.current;
15682
+ if (!menu) return;
15683
+ let animationFrameId = null;
15684
+ const observer = new ResizeObserver(() => {
15685
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
15686
+ animationFrameId = requestAnimationFrame(() => {
15687
+ animationFrameId = null;
15688
+ updatePositionRef.current();
15689
+ });
15690
+ });
15691
+ observer.observe(menu);
15692
+ return () => {
15693
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
15694
+ observer.disconnect();
15695
+ };
15696
+ }, [isVisible]);
15613
15697
  (0, import_react35.useEffect)(() => {
15614
15698
  if (!isVisible) return;
15615
15699
  const handlePointerDown = (event) => {