@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.
package/dist/index.cjs CHANGED
@@ -4928,6 +4928,9 @@ function getClipboardData(dataTransfer, type) {
4928
4928
  return "";
4929
4929
  }
4930
4930
  }
4931
+ function hasClipboardHtmlTable(dataTransfer) {
4932
+ return /<table(?:\s|>)/i.test(getClipboardData(dataTransfer, "text/html"));
4933
+ }
4931
4934
  function extractClipboardHtmlFragment(html) {
4932
4935
  const startMarker = "<!--StartFragment-->";
4933
4936
  const endMarker = "<!--EndFragment-->";
@@ -4938,6 +4941,13 @@ function extractClipboardHtmlFragment(html) {
4938
4941
  }
4939
4942
  return html;
4940
4943
  }
4944
+ function hasMeaningfulContentOutsideTable(container) {
4945
+ const clone = container.cloneNode(true);
4946
+ clone.querySelectorAll("table, style, script, meta, link").forEach((element) => element.remove());
4947
+ const remainingText = (clone.textContent ?? "").replace(/\u00a0/g, " ").replace(/\u200b/g, "").trim();
4948
+ if (remainingText) return true;
4949
+ return !!clone.querySelector("img, video, audio, iframe, object, embed, svg, canvas, hr, input, textarea, select, button");
4950
+ }
4941
4951
  function normalizeClipboardCellText(value) {
4942
4952
  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();
4943
4953
  }
@@ -5548,7 +5558,10 @@ function getClipboardTableContent(dataTransfer) {
5548
5558
  const fragment = extractClipboardHtmlFragment(html);
5549
5559
  const fragmentDoc = new DOMParser().parseFromString(fragment, "text/html");
5550
5560
  const styleMap = parseClipboardCssClassStyles(doc);
5551
- const table = fragmentDoc.querySelector("table") ?? doc.querySelector("table");
5561
+ const sourceBody = fragmentDoc.querySelector("table") ? fragmentDoc.body : doc.body;
5562
+ const tables = sourceBody.querySelectorAll("table");
5563
+ if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
5564
+ const table = tables[0];
5552
5565
  if (!(table instanceof HTMLTableElement)) return null;
5553
5566
  return createTableContent(getHtmlTableRows(table, styleMap), 1, {
5554
5567
  backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR
@@ -5727,6 +5740,7 @@ var init_clipboard_images = __esm({
5727
5740
  editor.chain().focus().insertContent(tableContent).run();
5728
5741
  return true;
5729
5742
  }
5743
+ if (hasClipboardHtmlTable(event.clipboardData)) return false;
5730
5744
  const tsvTableContent = getClipboardTsvTableContent(event.clipboardData);
5731
5745
  if (tsvTableContent) {
5732
5746
  event.preventDefault();
@@ -6416,7 +6430,7 @@ var init_colors = __esm({
6416
6430
  "src/components/UEditor/colors.tsx"() {
6417
6431
  "use strict";
6418
6432
  "use client";
6419
- import_react71 = require("react");
6433
+ import_react71 = __toESM(require("react"), 1);
6420
6434
  init_useSmartTranslations();
6421
6435
  import_lucide_react50 = require("lucide-react");
6422
6436
  init_cn();
@@ -6584,12 +6598,25 @@ var init_colors = __esm({
6584
6598
  colors,
6585
6599
  currentColor,
6586
6600
  onSelect,
6601
+ onCustomColorSelect,
6587
6602
  label
6588
6603
  }) => {
6589
6604
  const t = useSmartTranslations("UEditor");
6590
6605
  const colorInputRef = (0, import_react71.useRef)(null);
6591
6606
  const automaticColor = colors[0]?.color ?? "";
6592
6607
  const paletteColors = colors.slice(1);
6608
+ const customColorSelect = onCustomColorSelect ?? onSelect;
6609
+ import_react71.default.useEffect(() => {
6610
+ const input = colorInputRef.current;
6611
+ if (!input) return;
6612
+ const commitColor = () => customColorSelect(input.value);
6613
+ input.addEventListener("change", commitColor);
6614
+ return () => input.removeEventListener("change", commitColor);
6615
+ }, [customColorSelect]);
6616
+ import_react71.default.useEffect(() => {
6617
+ const input = colorInputRef.current;
6618
+ if (input) input.value = currentColor.startsWith("#") ? currentColor : "#000000";
6619
+ }, [currentColor]);
6593
6620
  return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "w-56 p-2", children: [
6594
6621
  /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
6595
6622
  /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(
@@ -6643,20 +6670,19 @@ var init_colors = __esm({
6643
6670
  }
6644
6671
  ),
6645
6672
  /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("span", { className: "flex-1 text-center", children: t("colors.moreColors") }),
6646
- /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(import_lucide_react50.Palette, { className: "h-4 w-4 text-muted-foreground" }),
6647
- /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
6648
- "input",
6649
- {
6650
- ref: colorInputRef,
6651
- type: "color",
6652
- value: currentColor.startsWith("#") ? currentColor : "#000000",
6653
- onChange: (event) => onSelect(event.target.value),
6654
- className: "sr-only",
6655
- tabIndex: -1
6656
- }
6657
- )
6673
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(import_lucide_react50.Palette, { className: "h-4 w-4 text-muted-foreground" })
6658
6674
  ]
6659
6675
  }
6676
+ ),
6677
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(
6678
+ "input",
6679
+ {
6680
+ ref: colorInputRef,
6681
+ type: "color",
6682
+ defaultValue: currentColor.startsWith("#") ? currentColor : "#000000",
6683
+ className: "sr-only",
6684
+ tabIndex: -1
6685
+ }
6660
6686
  )
6661
6687
  ] });
6662
6688
  };
@@ -13684,19 +13710,19 @@ var variantStyles5 = {
13684
13710
  },
13685
13711
  underline: {
13686
13712
  container: "relative border-b border-border/60",
13687
- tab: "relative transition-colors duration-200 pb-3 border-b-2 border-transparent hover:border-primary/30",
13713
+ tab: "relative transition-colors duration-200 border-b-2 border-transparent hover:border-primary/30",
13688
13714
  activeTab: "text-primary border-primary font-semibold",
13689
13715
  inactiveTab: "text-muted-foreground hover:text-foreground"
13690
13716
  },
13691
13717
  card: {
13692
- container: "space-y-1.5 bg-muted/20 p-2 rounded-2xl border border-border/30",
13718
+ container: "bg-muted/20 p-1.5 rounded-2xl border border-border/30",
13693
13719
  tab: "rounded-xl border border-transparent transition-all duration-200",
13694
13720
  activeTab: "bg-primary text-primary-foreground border-primary shadow-md",
13695
13721
  inactiveTab: "text-muted-foreground hover:text-foreground hover:bg-accent/50 hover:border-border/50"
13696
13722
  },
13697
13723
  "underline-card": {
13698
13724
  container: "border-b border-border/60 bg-card/80 backdrop-blur-sm rounded-t-2xl p-1",
13699
- tab: "relative transition-all duration-200 pb-3 px-4 rounded-t-xl border-b-2 border-transparent hover:border-primary/30 hover:bg-accent/30",
13725
+ tab: "relative transition-all duration-200 rounded-t-xl border-b-2 border-transparent hover:border-primary/30 hover:bg-accent/30",
13700
13726
  activeTab: "text-primary border-primary font-semibold bg-accent/30",
13701
13727
  inactiveTab: "text-muted-foreground hover:text-foreground"
13702
13728
  }
@@ -13806,7 +13832,7 @@ var Tabs = ({
13806
13832
  }, [baseId, tabs]);
13807
13833
  const containerClasses = cn(
13808
13834
  "relative",
13809
- orientation === "horizontal" ? "w-full flex space-x-1 overflow-x-auto" : "flex flex-col space-y-1 shrink-0",
13835
+ orientation === "horizontal" ? "w-full flex items-center space-x-1 overflow-x-auto" : "flex flex-col space-y-1 shrink-0",
13810
13836
  variantStyles5[variant].container,
13811
13837
  className
13812
13838
  );
@@ -13841,10 +13867,7 @@ var Tabs = ({
13841
13867
  tab.href && tab.disabled && "pointer-events-none cursor-not-allowed opacity-50"
13842
13868
  );
13843
13869
  const sharedStyle = {
13844
- boxShadow: "none",
13845
- transform: "none",
13846
- outline: "none",
13847
- border: "none"
13870
+ outline: "none"
13848
13871
  };
13849
13872
  const sharedProps = {
13850
13873
  ref: (el) => {
@@ -24800,7 +24823,7 @@ var MultiCombobox = ({
24800
24823
  );
24801
24824
  }),
24802
24825
  hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: cn("inline-flex items-center bg-muted text-muted-foreground rounded-lg shrink-0", sizeStyles8[size].tag), children: gi18n.moreCount ? gi18n.moreCount(hiddenCount) : `+${hiddenCount} more` })
24803
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: cn(formControlValueClass, "font-medium"), children: gi18n.selectedCount ? gi18n.selectedCount(value.length) : `${value.length} item${value.length > 1 ? "s" : ""} selected` }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: cn(formControlValueClass, "text-muted-foreground"), children: placeholder }) }),
24826
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: cn(formControlValueClass, "font-medium"), children: gi18n.selectedCount ? gi18n.selectedCount(value.length) : `${value.length} item${value.length > 1 ? "s" : ""} selected` }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: cn(formControlValueClass, "text-left text-muted-foreground"), children: placeholder }) }),
24804
24827
  /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-1.5 shrink-0", children: [
24805
24828
  showClear && value.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
24806
24829
  "div",
@@ -26247,9 +26270,8 @@ var defaultLabels = {
26247
26270
  searchPlaceholder: "Search...",
26248
26271
  noResultsText: "No results found"
26249
26272
  };
26250
- var TREE_NODE_BASE_PADDING_REM = 0.75;
26251
- var TREE_NODE_INDENT_REM = 1;
26252
- var TREE_BRANCH_OFFSET_CLASS = "ml-1.5 pl-1.5";
26273
+ var TREE_NODE_BASE_PADDING_REM = 0;
26274
+ var TREE_NODE_INDENT_REM = 0.75;
26253
26275
  var TREE_NODE_GAP_CLASS = "gap-1.5";
26254
26276
  var TREE_EXPANDER_PLACEHOLDER_CLASS = "w-5";
26255
26277
  var CATEGORY_TREE_DROPDOWN_MAX_HEIGHT = 320;
@@ -26379,7 +26401,7 @@ function CategoryTreeSelect(props) {
26379
26401
  useOverlayScrollbar = false,
26380
26402
  leafOnlySelect = false,
26381
26403
  virtualized = false,
26382
- estimatedItemHeight = 44,
26404
+ estimatedItemHeight = 36,
26383
26405
  overscan = 8,
26384
26406
  maxInitialOptions,
26385
26407
  searchMode = "auto",
@@ -26389,6 +26411,7 @@ function CategoryTreeSelect(props) {
26389
26411
  showSearchPromptWhenEmptyQuery = false,
26390
26412
  baseIndent = TREE_NODE_BASE_PADDING_REM,
26391
26413
  indentSize = TREE_NODE_INDENT_REM,
26414
+ showTreeLines = true,
26392
26415
  renderItemActions,
26393
26416
  singleSelect = false,
26394
26417
  borderMode
@@ -26676,7 +26699,7 @@ function CategoryTreeSelect(props) {
26676
26699
  {
26677
26700
  ref: virtualItem ? treeVirtualizer.measureElement : void 0,
26678
26701
  "data-index": virtualItem?.index,
26679
- className: cn("min-w-0 [content-visibility:auto] [contain-intrinsic-size:44px]", !virtualItem && "animate-in fade-in-50 duration-200"),
26702
+ className: cn("min-w-0 [content-visibility:auto] [contain-intrinsic-size:36px]", !virtualItem && "animate-in fade-in-50 duration-200"),
26680
26703
  style: { animationDelay: virtualItem ? void 0 : `${level * 30}ms`, ...rowStyle },
26681
26704
  children: [
26682
26705
  /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
@@ -26688,7 +26711,7 @@ function CategoryTreeSelect(props) {
26688
26711
  "aria-selected": viewOnly ? void 0 : isSelected,
26689
26712
  onClick: () => !viewOnly && handleSelect(category.id, category),
26690
26713
  className: cn(
26691
- "relative flex min-w-0 items-center px-3 py-2.5 min-h-11 transition-all duration-200 rounded-3xl",
26714
+ "relative flex min-h-9 min-w-0 items-center rounded-3xl transition-all duration-200",
26692
26715
  TREE_NODE_GAP_CLASS,
26693
26716
  !viewOnly && (isSelectable ? "cursor-pointer" : "cursor-default"),
26694
26717
  isSelectable && !isSelected && "hover:bg-accent/50",
@@ -26698,6 +26721,16 @@ function CategoryTreeSelect(props) {
26698
26721
  ),
26699
26722
  style: { paddingLeft: `${level * indentSize + baseIndent}rem` },
26700
26723
  children: [
26724
+ virtualItem && showTreeLines && Array.from({ length: level }, (_, ancestorLevel) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
26725
+ "span",
26726
+ {
26727
+ "data-tree-line": true,
26728
+ "aria-hidden": "true",
26729
+ className: "pointer-events-none absolute inset-y-0 w-px border-l border-dashed border-border/50",
26730
+ style: { left: `${ancestorLevel * indentSize + baseIndent + 0.5}rem` }
26731
+ },
26732
+ ancestorLevel
26733
+ )),
26701
26734
  hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
26702
26735
  "button",
26703
26736
  {
@@ -26746,16 +26779,23 @@ function CategoryTreeSelect(props) {
26746
26779
  ]
26747
26780
  }
26748
26781
  ),
26749
- !virtualItem && hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
26782
+ !virtualItem && hasChildren && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
26750
26783
  "div",
26751
26784
  {
26752
26785
  role: "group",
26753
- className: cn(
26754
- TREE_BRANCH_OFFSET_CLASS,
26755
- "border-l-2 border-dashed border-border/50",
26756
- "animate-in slide-in-from-top-2 fade-in-50 duration-200"
26757
- ),
26758
- children: children.map((child) => renderCategoryRow(child, level + 1))
26786
+ className: "relative animate-in fade-in-50 slide-in-from-top-2 duration-200",
26787
+ children: [
26788
+ showTreeLines && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
26789
+ "span",
26790
+ {
26791
+ "data-tree-line": true,
26792
+ "aria-hidden": "true",
26793
+ className: "pointer-events-none absolute inset-y-0 w-px border-l border-dashed border-border/50",
26794
+ style: { left: `${level * indentSize + baseIndent + 0.5}rem` }
26795
+ }
26796
+ ),
26797
+ children.map((child) => renderCategoryRow(child, level + 1))
26798
+ ]
26759
26799
  }
26760
26800
  )
26761
26801
  ]
@@ -30902,6 +30942,7 @@ function DataTableBodyRows({
30902
30942
  style: getStickyColumnStyle(col),
30903
30943
  className: cn(
30904
30944
  cellPadding,
30945
+ (!col.align || col.align === "left") && "text-left",
30905
30946
  col.align === "right" && "text-right",
30906
30947
  col.align === "center" && "text-center",
30907
30948
  showBorderLeft && "border-l border-border/60",
@@ -31107,16 +31148,17 @@ function DataTableHeader({
31107
31148
  className: cn(
31108
31149
  "flex items-center gap-1",
31109
31150
  headerMinHeightClass,
31110
- col.align === "right" && "justify-end",
31111
- col.align === "center" && "justify-center",
31112
- !col.align && "justify-start"
31151
+ (col.headerAlign ?? headerAlign) === "right" && "justify-end",
31152
+ (col.headerAlign ?? headerAlign) === "center" && "justify-center",
31153
+ (col.headerAlign ?? headerAlign) === "left" && "justify-start"
31113
31154
  ),
31114
31155
  children: /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: cn("font-medium whitespace-nowrap select-text", headerTitleClass), children: col.title })
31115
31156
  }
31116
31157
  );
31117
31158
  }
31118
- const isRightAlign = col.align === "right" || !col.align && headerAlign === "right";
31119
- const isCenterAlign = col.align === "center" || !col.align && headerAlign === "center";
31159
+ const resolvedHeaderAlign = col.headerAlign ?? headerAlign;
31160
+ const isRightAlign = resolvedHeaderAlign === "right";
31161
+ const isCenterAlign = resolvedHeaderAlign === "center";
31120
31162
  const columnLabel = getColumnLabel(col.title) || col.key;
31121
31163
  const sortByText = sortByLabel ?? gi18n.sortBy ?? "Sort by";
31122
31164
  const sortLabel = `${sortByText} ${columnLabel}`;
@@ -31278,8 +31320,8 @@ function DataTableHeader({
31278
31320
  },
31279
31321
  className: cn(
31280
31322
  "relative",
31281
- (col.align === "right" || !col.align && headerAlign === "right") && "text-right",
31282
- (col.align === "center" || !col.align && headerAlign === "center") && "text-center",
31323
+ (col.headerAlign ?? headerAlign) === "right" && "text-right",
31324
+ (col.headerAlign ?? headerAlign) === "center" && "text-center",
31283
31325
  showBorderLeft && "border-l border-border/60",
31284
31326
  getStickyHeaderClass(col)
31285
31327
  ),
@@ -31801,7 +31843,7 @@ function useDataTableState({
31801
31843
  const allLeafColumns = import_react40.default.useMemo(() => getLeafColumns(columns), [columns]);
31802
31844
  const defaultVisibleLeafKeys = import_react40.default.useMemo(() => allLeafColumns.filter((column) => column.visible !== false).map((column) => column.key), [allLeafColumns]);
31803
31845
  const knownLeafKeysRef = import_react40.default.useRef(new Set(defaultVisibleLeafKeys));
31804
- const [headerAlign, setHeaderAlign] = import_react40.default.useState("left");
31846
+ const [headerAlign, setHeaderAlign] = import_react40.default.useState("center");
31805
31847
  const [visibleCols, setVisibleCols] = import_react40.default.useState(defaultVisibleLeafKeys);
31806
31848
  const [filters, setFilters] = import_react40.default.useState({});
31807
31849
  const [sort, setSort] = import_react40.default.useState(null);
@@ -38601,15 +38643,18 @@ var FloatingMenuContent = ({ editor }) => {
38601
38643
  }
38602
38644
  );
38603
38645
  };
38604
- function applyTableCellBackground(editor, color) {
38646
+ function applyTableCellBackground(editor, color, options = {}) {
38647
+ const shouldFocus = options.focus ?? true;
38605
38648
  const value = color || null;
38606
38649
  const { state, view } = editor;
38607
38650
  const applied = (0, import_tables8.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
38608
38651
  if (applied) {
38609
- view.focus();
38652
+ if (shouldFocus) view.focus();
38610
38653
  return;
38611
38654
  }
38612
- editor.chain().focus().setCellAttribute("backgroundColor", value).run();
38655
+ const chain = editor.chain();
38656
+ if (shouldFocus) chain.focus();
38657
+ chain.setCellAttribute("backgroundColor", value).run();
38613
38658
  }
38614
38659
  function applyTableCellAttribute2(editor, name, value, options = {}) {
38615
38660
  const shouldFocus = options.focus ?? true;
@@ -38667,6 +38712,30 @@ function BorderPositionPreviewIcon({ position }) {
38667
38712
  showVertical && /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("path", { d: "M10 2V18", strokeWidth: "2" })
38668
38713
  ] });
38669
38714
  }
38715
+ function getBubbleMenuPosition({
38716
+ start,
38717
+ end,
38718
+ menuSize,
38719
+ viewport,
38720
+ offset = 16,
38721
+ padding = 8
38722
+ }) {
38723
+ const selectionTop = Math.min(start.top, end.top);
38724
+ const selectionBottom = Math.max(start.bottom, end.bottom);
38725
+ const spaceAbove = selectionTop - offset - padding;
38726
+ const spaceBelow = viewport.height - padding - selectionBottom - offset;
38727
+ const placement = menuSize.height <= spaceAbove || spaceAbove >= spaceBelow ? "top" : "bottom";
38728
+ const halfWidth = menuSize.width / 2;
38729
+ const minLeft = padding + halfWidth;
38730
+ const maxLeft = viewport.width - padding - halfWidth;
38731
+ const selectionCenter = (start.left + end.left) / 2;
38732
+ const left = maxLeft >= minLeft ? Math.min(maxLeft, Math.max(minLeft, selectionCenter)) : viewport.width / 2;
38733
+ const requestedTop = placement === "top" ? selectionTop - offset : selectionBottom + offset;
38734
+ const minTop = placement === "top" ? padding + menuSize.height : padding;
38735
+ const maxTop = placement === "top" ? viewport.height - padding : viewport.height - padding - menuSize.height;
38736
+ const top = maxTop >= minTop ? Math.min(maxTop, Math.max(minTop, requestedTop)) : Math.max(padding, requestedTop);
38737
+ return { top, left, placement };
38738
+ }
38670
38739
  function getBubbleMenuContext(editor) {
38671
38740
  const { selection } = editor.state;
38672
38741
  if (editor.isActive("image")) return "image";
@@ -38832,24 +38901,34 @@ var BubbleMenuContent = ({
38832
38901
  setActiveColorPalette(null);
38833
38902
  onKeepOpenChange?.(false);
38834
38903
  }, [onKeepOpenChange]);
38835
- const applyInlineColorAndClose = (0, import_react75.useCallback)((color) => {
38904
+ const applyInlineColor = (0, import_react75.useCallback)((color, focus) => {
38836
38905
  if (activeColorPalette === "text") {
38906
+ const chain = editor.chain();
38907
+ if (focus) chain.focus();
38837
38908
  if (color === "inherit") {
38838
- editor.chain().focus().unsetColor().run();
38909
+ chain.unsetColor().run();
38839
38910
  } else {
38840
- editor.chain().focus().setColor(color).run();
38911
+ chain.setColor(color).run();
38841
38912
  }
38842
38913
  } else if (activeColorPalette === "highlight") {
38914
+ const chain = editor.chain();
38915
+ if (focus) chain.focus();
38843
38916
  if (color === "") {
38844
- editor.chain().focus().unsetHighlight().run();
38917
+ chain.unsetHighlight().run();
38845
38918
  } else {
38846
- editor.chain().focus().toggleHighlight({ color }).run();
38919
+ chain.toggleHighlight({ color }).run();
38847
38920
  }
38848
38921
  } else {
38849
- applyTableCellBackground(editor, color);
38922
+ applyTableCellBackground(editor, color, { focus });
38850
38923
  }
38924
+ }, [activeColorPalette, editor]);
38925
+ const applyInlineColorAndClose = (0, import_react75.useCallback)((color) => {
38926
+ applyInlineColor(color, true);
38851
38927
  closeColorPalette();
38852
- }, [activeColorPalette, closeColorPalette, editor]);
38928
+ }, [applyInlineColor, closeColorPalette]);
38929
+ const applyCustomColor = (0, import_react75.useCallback)((color) => {
38930
+ applyInlineColor(color, false);
38931
+ }, [applyInlineColor]);
38853
38932
  (0, import_react75.useEffect)(() => {
38854
38933
  if (!showLinkInput) return;
38855
38934
  const close2 = () => setShowLinkInput(false);
@@ -39044,6 +39123,7 @@ var BubbleMenuContent = ({
39044
39123
  colors: isTextPalette ? textColors : highlightColors,
39045
39124
  currentColor: isTextPalette ? currentTextColor : isHighlightPalette ? currentHighlightColor : currentCellBgColor,
39046
39125
  onSelect: applyInlineColorAndClose,
39126
+ onCustomColorSelect: applyCustomColor,
39047
39127
  label: isTextPalette ? t("colors.textColor") : isHighlightPalette ? t("colors.highlight") : t("tableMenu.cellBackground") || "Cell background"
39048
39128
  }
39049
39129
  ) });
@@ -39330,6 +39410,7 @@ var BubbleMenuContent = ({
39330
39410
  /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
39331
39411
  ToolbarButton,
39332
39412
  {
39413
+ onMouseDown: () => onKeepOpenChange?.(true),
39333
39414
  onClick: () => setActiveColorPalette("cell-bg"),
39334
39415
  active: Boolean(currentCellBgColor),
39335
39416
  title: t("tableMenu.cellBackground") || "Cell background",
@@ -39561,6 +39642,8 @@ var CustomBubbleMenu = ({
39561
39642
  placement: "top"
39562
39643
  });
39563
39644
  const menuRef = (0, import_react75.useRef)(null);
39645
+ const updatePositionRef = (0, import_react75.useRef)(() => {
39646
+ });
39564
39647
  const keepOpenRef = (0, import_react75.useRef)(false);
39565
39648
  const [keepOpen, setKeepOpenState] = (0, import_react75.useState)(false);
39566
39649
  const showTimeoutRef = (0, import_react75.useRef)(null);
@@ -39618,19 +39701,15 @@ var CustomBubbleMenu = ({
39618
39701
  setIsVisible(false);
39619
39702
  return;
39620
39703
  }
39621
- const viewportPadding = 8;
39622
39704
  const menuHeight = menuRef.current?.getBoundingClientRect().height || BUBBLE_MENU_ESTIMATED_HEIGHT;
39623
- const editorTop = view.dom.getBoundingClientRect().top;
39624
- const selectionTop = Math.min(start.top, end.top);
39625
- const selectionBottom = Math.max(start.bottom, end.bottom);
39626
- const hasRoomAboveEditor = selectionTop - BUBBLE_MENU_OFFSET - menuHeight >= editorTop + viewportPadding;
39627
- const placement = hasRoomAboveEditor ? "top" : "bottom";
39628
- const left = Math.min(
39629
- window.innerWidth - viewportPadding,
39630
- Math.max(viewportPadding, (start.left + end.left) / 2)
39631
- );
39632
- const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
39633
- setPosition2({ top, left, placement });
39705
+ const menuWidth = menuRef.current?.getBoundingClientRect().width || 0;
39706
+ setPosition2(getBubbleMenuPosition({
39707
+ start,
39708
+ end,
39709
+ menuSize: { width: menuWidth, height: menuHeight },
39710
+ viewport: { width: window.innerWidth, height: window.innerHeight },
39711
+ offset: BUBBLE_MENU_OFFSET
39712
+ }));
39634
39713
  if (keepOpenRef.current) {
39635
39714
  clearShowTimeout();
39636
39715
  setIsVisible(true);
@@ -39659,8 +39738,11 @@ var CustomBubbleMenu = ({
39659
39738
  editor.on("transaction", schedulePositionUpdate);
39660
39739
  editor.on("focus", schedulePositionUpdate);
39661
39740
  editor.on("blur", handleBlur);
39741
+ updatePositionRef.current = updatePosition;
39662
39742
  schedulePositionUpdate();
39663
39743
  return () => {
39744
+ updatePositionRef.current = () => {
39745
+ };
39664
39746
  if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
39665
39747
  clearShowTimeout();
39666
39748
  editor.off("transaction", schedulePositionUpdate);
@@ -39668,6 +39750,24 @@ var CustomBubbleMenu = ({
39668
39750
  editor.off("blur", handleBlur);
39669
39751
  };
39670
39752
  }, [editor]);
39753
+ (0, import_react75.useEffect)(() => {
39754
+ if (!isVisible || typeof ResizeObserver === "undefined") return;
39755
+ const menu = menuRef.current;
39756
+ if (!menu) return;
39757
+ let animationFrameId = null;
39758
+ const observer = new ResizeObserver(() => {
39759
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
39760
+ animationFrameId = requestAnimationFrame(() => {
39761
+ animationFrameId = null;
39762
+ updatePositionRef.current();
39763
+ });
39764
+ });
39765
+ observer.observe(menu);
39766
+ return () => {
39767
+ if (animationFrameId !== null) cancelAnimationFrame(animationFrameId);
39768
+ observer.disconnect();
39769
+ };
39770
+ }, [isVisible]);
39671
39771
  (0, import_react75.useEffect)(() => {
39672
39772
  if (!isVisible) return;
39673
39773
  const handlePointerDown = (event) => {