@underverse-ui/underverse 1.0.211 → 2.0.1

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +243 -706
  4. package/api-reference.json +1 -1
  5. package/dist/{EmojiPicker-FEJJDAWZ.js → EmojiPicker-34YATB7Z.js} +4 -4
  6. package/dist/{chunk-F6A7HJE4.js → chunk-A7XL4RDV.js} +3 -3
  7. package/dist/{chunk-CYCBSKWI.js → chunk-KGNYK5YF.js} +2 -2
  8. package/dist/{chunk-BRDGIALS.js → chunk-PLHJWXCW.js} +24 -30
  9. package/dist/{chunk-BRDGIALS.js.map → chunk-PLHJWXCW.js.map} +1 -1
  10. package/dist/{chunk-GKY5DHGR.js → chunk-UNDGI6ZI.js} +3 -3
  11. package/dist/{chunk-4E43DRP2.js → chunk-Y7SQKJ7X.js} +95 -31
  12. package/dist/chunk-Y7SQKJ7X.js.map +1 -0
  13. package/dist/{chunk-AMAILXHP.js → chunk-Z6WE65J2.js} +44 -18
  14. package/dist/chunk-Z6WE65J2.js.map +1 -0
  15. package/dist/index.cjs +280 -159
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.js +140 -103
  18. package/dist/index.js.map +1 -1
  19. package/dist/{menu-bar-6GS5LLTD.js → menu-bar-AAD3LQTN.js} +4 -4
  20. package/dist/ueditor.cjs +132 -42
  21. package/dist/ueditor.cjs.map +1 -1
  22. package/dist/ueditor.js +4 -4
  23. package/package.json +3 -1
  24. package/dist/chunk-4E43DRP2.js.map +0 -1
  25. package/dist/chunk-AMAILXHP.js.map +0 -1
  26. /package/dist/{EmojiPicker-FEJJDAWZ.js.map → EmojiPicker-34YATB7Z.js.map} +0 -0
  27. /package/dist/{chunk-F6A7HJE4.js.map → chunk-A7XL4RDV.js.map} +0 -0
  28. /package/dist/{chunk-CYCBSKWI.js.map → chunk-KGNYK5YF.js.map} +0 -0
  29. /package/dist/{chunk-GKY5DHGR.js.map → chunk-UNDGI6ZI.js.map} +0 -0
  30. /package/dist/{menu-bar-6GS5LLTD.js.map → menu-bar-AAD3LQTN.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  Modal_default
4
- } from "./chunk-GKY5DHGR.js";
4
+ } from "./chunk-UNDGI6ZI.js";
5
5
  import {
6
6
  DEFAULT_TABLE_ROW_HEIGHT,
7
7
  DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
@@ -21,11 +21,11 @@ import {
21
21
  sanitizeUEditorUrl,
22
22
  useDropdownMenuClose,
23
23
  useSharedEditorUiRenderState
24
- } from "./chunk-AMAILXHP.js";
24
+ } from "./chunk-Z6WE65J2.js";
25
25
  import {
26
26
  cn,
27
27
  useSmartTranslations
28
- } from "./chunk-BRDGIALS.js";
28
+ } from "./chunk-PLHJWXCW.js";
29
29
  import "./chunk-NSBPE2FW.js";
30
30
 
31
31
  // src/components/UEditor/menu-bar.tsx
@@ -944,4 +944,4 @@ var MenuBar = ({
944
944
  export {
945
945
  MenuBar
946
946
  };
947
- //# sourceMappingURL=menu-bar-6GS5LLTD.js.map
947
+ //# sourceMappingURL=menu-bar-AAD3LQTN.js.map
package/dist/ueditor.cjs CHANGED
@@ -3033,7 +3033,7 @@ var init_Tooltip = __esm({
3033
3033
  onBlur: chainEventHandlers(
3034
3034
  triggerPassthroughProps.onBlur,
3035
3035
  childProps.onBlur,
3036
- (e) => {
3036
+ () => {
3037
3037
  handleBlur();
3038
3038
  }
3039
3039
  )
@@ -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
  };
@@ -7829,7 +7855,7 @@ var init_editor_styles = __esm({
7829
7855
  "[&_td]:px-2",
7830
7856
  "[&_td]:py-0",
7831
7857
  "[&_td]:whitespace-normal",
7832
- "[&_td]:[overflow-wrap:anywhere]",
7858
+ "[&_td]:wrap-anywhere",
7833
7859
  "[&_td]:[word-break:normal]",
7834
7860
  "[&_td[data-text-wrap='nowrap']]:whitespace-nowrap",
7835
7861
  "[&_td[data-text-wrap='nowrap']]:[overflow-wrap:normal]",
@@ -7841,7 +7867,7 @@ var init_editor_styles = __esm({
7841
7867
  "[&_th]:px-2",
7842
7868
  "[&_th]:py-0",
7843
7869
  "[&_th]:whitespace-normal",
7844
- "[&_th]:[overflow-wrap:anywhere]",
7870
+ "[&_th]:wrap-anywhere",
7845
7871
  "[&_th]:[word-break:normal]",
7846
7872
  "[&_th[data-text-wrap='nowrap']]:whitespace-nowrap",
7847
7873
  "[&_th[data-text-wrap='nowrap']]:[overflow-wrap:normal]",
@@ -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) => {
@@ -21166,7 +21250,7 @@ function TableResizeHandles({
21166
21250
  "data-resizing": active ? "true" : "false",
21167
21251
  className: cn(
21168
21252
  "pointer-events-none absolute z-[38] box-border border border-primary/55",
21169
- active && "border-primary bg-primary/[0.03]"
21253
+ active && "border-primary bg-primary/3"
21170
21254
  ),
21171
21255
  style: {
21172
21256
  left: layout.tableLeft,
@@ -23229,11 +23313,17 @@ function useUEditorOutput({
23229
23313
  onJsonChange,
23230
23314
  outputDebounceMs
23231
23315
  }) {
23232
- const callbacksRef = React28.useRef({});
23316
+ const callbacksRef = React28.useRef({
23317
+ onChange,
23318
+ onHtmlChange,
23319
+ onJsonChange
23320
+ });
23233
23321
  const pendingEditorRef = React28.useRef(null);
23234
23322
  const timeoutRef = React28.useRef(null);
23235
23323
  const debounceMs = normalizeDebounceMs(outputDebounceMs);
23236
- callbacksRef.current = { onChange, onHtmlChange, onJsonChange };
23324
+ React28.useLayoutEffect(() => {
23325
+ callbacksRef.current = { onChange, onHtmlChange, onJsonChange };
23326
+ }, [onChange, onHtmlChange, onJsonChange]);
23237
23327
  const flushOutputUpdates = React28.useCallback(() => {
23238
23328
  if (timeoutRef.current !== null) {
23239
23329
  clearTimeout(timeoutRef.current);