@underverse-ui/underverse 1.0.160 → 1.0.162

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
@@ -585,6 +585,9 @@ var en_default = {
585
585
  loading: "Loading",
586
586
  noData: "No data",
587
587
  clearFilter: "Clear filter",
588
+ selectAll: "Select all",
589
+ default: "Default",
590
+ other: "Other",
588
591
  headerAlign: "Header alignment",
589
592
  alignLeft: "Align left",
590
593
  alignCenter: "Align center",
@@ -980,6 +983,9 @@ var vi_default = {
980
983
  loading: "\u0110ang t\u1EA3i",
981
984
  noData: "Kh\xF4ng c\xF3 d\u1EEF li\u1EC7u",
982
985
  clearFilter: "X\xF3a b\u1ED9 l\u1ECDc",
986
+ selectAll: "Ch\u1ECDn t\u1EA5t c\u1EA3",
987
+ default: "M\u1EB7c \u0111\u1ECBnh",
988
+ other: "Kh\xE1c",
983
989
  headerAlign: "C\u0103n ch\u1EC9nh ti\xEAu \u0111\u1EC1",
984
990
  alignLeft: "C\u0103n tr\xE1i",
985
991
  alignCenter: "C\u0103n gi\u1EEFa",
@@ -1375,6 +1381,9 @@ var ko_default = {
1375
1381
  loading: "\uB85C\uB529 \uC911",
1376
1382
  noData: "\uB370\uC774\uD130 \uC5C6\uC74C",
1377
1383
  clearFilter: "\uD544\uD130 \uC9C0\uC6B0\uAE30",
1384
+ selectAll: "\uC804\uCCB4 \uC120\uD0DD",
1385
+ default: "\uAE30\uBCF8\uAC12",
1386
+ other: "\uAE30\uD0C0",
1378
1387
  headerAlign: "\uD5E4\uB354 \uC815\uB82C",
1379
1388
  alignLeft: "\uC67C\uCABD \uC815\uB82C",
1380
1389
  alignCenter: "\uAC00\uC6B4\uB370 \uC815\uB82C",
@@ -1770,6 +1779,9 @@ var ja_default = {
1770
1779
  loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D",
1771
1780
  noData: "\u30C7\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093",
1772
1781
  clearFilter: "\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u30AF\u30EA\u30A2",
1782
+ selectAll: "\u3059\u3079\u3066\u9078\u629E",
1783
+ default: "\u30C7\u30D5\u30A9\u30EB\u30C8",
1784
+ other: "\u305D\u306E\u4ED6",
1773
1785
  headerAlign: "\u30D8\u30C3\u30C0\u30FC\u914D\u7F6E",
1774
1786
  alignLeft: "\u5DE6\u63C3\u3048",
1775
1787
  alignCenter: "\u4E2D\u592E\u63C3\u3048",
@@ -7593,6 +7605,23 @@ var DropdownMenu = ({
7593
7605
  const itemsRef = React25.useRef([]);
7594
7606
  const [activeIndex, setActiveIndex] = useResettingIndex(open);
7595
7607
  const parentMenu = React25.useContext(DropdownMenuContext);
7608
+ const closeMenu = React25.useCallback(() => {
7609
+ setOpen(false);
7610
+ parentMenu?.closeMenu();
7611
+ }, [parentMenu, setOpen]);
7612
+ const getEnabledMenuItems = React25.useCallback(() => {
7613
+ const menuEl = menuRef.current;
7614
+ if (!menuEl) return [];
7615
+ return Array.from(menuEl.querySelectorAll("[data-dropdown-menu-item]")).filter((el) => !el.disabled);
7616
+ }, []);
7617
+ const focusMenuItem = React25.useCallback((index) => {
7618
+ const enabled = getEnabledMenuItems();
7619
+ const item = enabled[index];
7620
+ if (!item) return;
7621
+ setActiveIndex(index);
7622
+ item.focus();
7623
+ item.scrollIntoView({ block: "nearest" });
7624
+ }, [getEnabledMenuItems, setActiveIndex]);
7596
7625
  useShadCNAnimations();
7597
7626
  React25.useEffect(() => {
7598
7627
  if (!open) return;
@@ -7603,38 +7632,34 @@ var DropdownMenu = ({
7603
7632
  if (!active || !triggerEl || !menuEl) return;
7604
7633
  const isInMenu = menuEl.contains(active);
7605
7634
  const isOnTrigger = triggerEl.contains(active);
7606
- if (!isInMenu && !isOnTrigger) return;
7607
- const enabled = itemsRef.current.filter((el) => el && !el.disabled);
7635
+ const enabled = getEnabledMenuItems();
7608
7636
  if (enabled.length === 0) return;
7637
+ const currentIndex = enabled.findIndex((el) => el === active);
7638
+ const baseIndex = currentIndex >= 0 ? currentIndex : activeIndex;
7609
7639
  if (e.key === "ArrowDown") {
7610
7640
  e.preventDefault();
7611
- const next = (activeIndex + 1 + enabled.length) % enabled.length;
7612
- setActiveIndex(next);
7613
- enabled[next]?.focus();
7641
+ const next = (baseIndex + 1 + enabled.length) % enabled.length;
7642
+ focusMenuItem(next);
7614
7643
  } else if (e.key === "ArrowUp") {
7615
7644
  e.preventDefault();
7616
- const prev = (activeIndex - 1 + enabled.length) % enabled.length;
7617
- setActiveIndex(prev);
7618
- enabled[prev]?.focus();
7645
+ const prev = (baseIndex - 1 + enabled.length) % enabled.length;
7646
+ focusMenuItem(prev);
7619
7647
  } else if (e.key === "Home") {
7620
7648
  e.preventDefault();
7621
- setActiveIndex(0);
7622
- enabled[0]?.focus();
7649
+ focusMenuItem(0);
7623
7650
  } else if (e.key === "End") {
7624
7651
  e.preventDefault();
7625
- setActiveIndex(enabled.length - 1);
7626
- enabled[enabled.length - 1]?.focus();
7652
+ focusMenuItem(enabled.length - 1);
7653
+ } else if (e.key === "Escape" && (isInMenu || isOnTrigger)) {
7654
+ e.preventDefault();
7655
+ closeMenu();
7627
7656
  }
7628
7657
  };
7629
- document.addEventListener("keydown", handleKeyNav);
7658
+ document.addEventListener("keydown", handleKeyNav, true);
7630
7659
  return () => {
7631
- document.removeEventListener("keydown", handleKeyNav);
7660
+ document.removeEventListener("keydown", handleKeyNav, true);
7632
7661
  };
7633
- }, [open, activeIndex, setActiveIndex]);
7634
- const closeMenu = React25.useCallback(() => {
7635
- setOpen(false);
7636
- parentMenu?.closeMenu();
7637
- }, [parentMenu, setOpen]);
7662
+ }, [open, activeIndex, closeMenu, focusMenuItem, getEnabledMenuItems]);
7638
7663
  const menuContext = React25.useMemo(
7639
7664
  () => ({
7640
7665
  closeMenu,
@@ -7659,6 +7684,7 @@ var DropdownMenu = ({
7659
7684
  onClick: () => handleItemClick(item.onClick),
7660
7685
  disabled: item.disabled,
7661
7686
  role: "menuitem",
7687
+ "data-dropdown-menu-item": "",
7662
7688
  tabIndex: -1,
7663
7689
  style: {
7664
7690
  animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
@@ -7694,13 +7720,13 @@ var DropdownMenu = ({
7694
7720
  if (e.key === "ArrowDown") {
7695
7721
  e.preventDefault();
7696
7722
  setOpen(true);
7697
- requestAnimationFrame(() => itemsRef.current.find((el) => el && !el.disabled)?.focus());
7723
+ requestAnimationFrame(() => focusMenuItem(0));
7698
7724
  } else if (e.key === "ArrowUp") {
7699
7725
  e.preventDefault();
7700
7726
  setOpen(true);
7701
7727
  requestAnimationFrame(() => {
7702
- const enabled = itemsRef.current.filter((el) => el && !el.disabled);
7703
- enabled[enabled.length - 1]?.focus();
7728
+ const enabled = getEnabledMenuItems();
7729
+ focusMenuItem(enabled.length - 1);
7704
7730
  });
7705
7731
  } else if (e.key === "Escape") {
7706
7732
  e.preventDefault();
@@ -7751,6 +7777,8 @@ var DropdownMenuItem = ({
7751
7777
  },
7752
7778
  disabled,
7753
7779
  onMouseDown: (e) => e.preventDefault(),
7780
+ "data-dropdown-menu-item": "",
7781
+ tabIndex: -1,
7754
7782
  className: cn(
7755
7783
  "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7756
7784
  "hover:bg-accent hover:text-accent-foreground",
@@ -22909,6 +22937,78 @@ function DataTableBodyRows({
22909
22937
  // src/components/DataTable/components/DataTableHeader.tsx
22910
22938
  import React56 from "react";
22911
22939
  import { Filter as FilterIcon } from "lucide-react";
22940
+
22941
+ // src/components/DataTable/utils/colorTag.ts
22942
+ function parseColor(color) {
22943
+ if (!color) return null;
22944
+ const c = color.trim().toLowerCase();
22945
+ if (c.startsWith("#")) {
22946
+ let hex = c.substring(1);
22947
+ if (hex.length === 3) {
22948
+ hex = hex.split("").map((char) => char + char).join("");
22949
+ }
22950
+ if (hex.length === 6) {
22951
+ const r = parseInt(hex.substring(0, 2), 16);
22952
+ const g = parseInt(hex.substring(2, 4), 16);
22953
+ const b = parseInt(hex.substring(4, 6), 16);
22954
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
22955
+ return [r, g, b];
22956
+ }
22957
+ }
22958
+ return null;
22959
+ }
22960
+ if (c.startsWith("rgb")) {
22961
+ const match = c.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
22962
+ if (match) {
22963
+ const r = parseInt(match[1], 10);
22964
+ const g = parseInt(match[2], 10);
22965
+ const b = parseInt(match[3], 10);
22966
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
22967
+ return [r, g, b];
22968
+ }
22969
+ }
22970
+ return null;
22971
+ }
22972
+ return null;
22973
+ }
22974
+ function toRgba(r, g, b, alpha) {
22975
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
22976
+ }
22977
+ function getHeaderBgColor(colorTag) {
22978
+ const rgb = parseColor(colorTag);
22979
+ if (!rgb) return colorTag;
22980
+ return toRgba(rgb[0], rgb[1], rgb[2], 1);
22981
+ }
22982
+ function groupColumnsByColorTag(leafCols, colorGroups) {
22983
+ const groupsMap = /* @__PURE__ */ new Map();
22984
+ const ungrouped = [];
22985
+ for (const col of leafCols) {
22986
+ if (col.colorTag && colorGroups && colorGroups[col.colorTag]) {
22987
+ const tag = col.colorTag;
22988
+ if (!groupsMap.has(tag)) {
22989
+ groupsMap.set(tag, []);
22990
+ }
22991
+ groupsMap.get(tag).push(col);
22992
+ } else {
22993
+ ungrouped.push(col);
22994
+ }
22995
+ }
22996
+ const groups = [];
22997
+ if (colorGroups) {
22998
+ for (const [tag, groupConfig] of Object.entries(colorGroups)) {
22999
+ if (groupsMap.has(tag)) {
23000
+ groups.push({
23001
+ colorTag: tag,
23002
+ label: groupConfig.label,
23003
+ columns: groupsMap.get(tag)
23004
+ });
23005
+ }
23006
+ }
23007
+ }
23008
+ return { groups, ungrouped };
23009
+ }
23010
+
23011
+ // src/components/DataTable/components/DataTableHeader.tsx
22912
23012
  import { Fragment as Fragment24, jsx as jsx65, jsxs as jsxs53 } from "react/jsx-runtime";
22913
23013
  function getColumnLabel(title) {
22914
23014
  if (typeof title === "string" || typeof title === "number") {
@@ -23157,6 +23257,7 @@ function DataTableHeader({
23157
23257
  const isAfterFixedLeft = prevCol?.fixed === "left";
23158
23258
  const showBorderLeft = columnDividers && cellIndex > 0 && !isAfterFixedLeft && !col.fixed;
23159
23259
  const isLastCell = cellIndex === row.length - 1;
23260
+ const headerBackgroundColor = isLeaf && col.colorTag ? getHeaderBgColor(col.colorTag) : "var(--muted)";
23160
23261
  return /* @__PURE__ */ jsxs53(
23161
23262
  TableHead,
23162
23263
  {
@@ -23165,6 +23266,7 @@ function DataTableHeader({
23165
23266
  "data-underverse-column-key": isLeaf ? col.key : void 0,
23166
23267
  style: {
23167
23268
  width: col.width,
23269
+ backgroundColor: headerBackgroundColor,
23168
23270
  ...getStickyHeaderCellStyle(headerCell)
23169
23271
  },
23170
23272
  className: cn(
@@ -23241,7 +23343,6 @@ function DataTablePagination({
23241
23343
  result.push(totalPages);
23242
23344
  return result;
23243
23345
  }, [curPage, totalPages]);
23244
- if (!(totalItems > 0 && totalPages > 1)) return null;
23245
23346
  const controlButtonSize = size === "lg" ? "md" : "sm";
23246
23347
  const navBtnClass = size === "sm" ? "h-6 w-6 p-0 rounded-full" : size === "lg" ? "h-8 w-8 p-0 rounded-full" : "h-7 w-7 p-0 rounded-full";
23247
23348
  const navIconClass = size === "sm" ? "h-3.5 w-3.5" : size === "lg" ? "h-5 w-5" : "h-4 w-4";
@@ -23423,13 +23524,18 @@ function DataTableToolbar({
23423
23524
  setDensity,
23424
23525
  setHeaderAlign,
23425
23526
  labels,
23426
- t
23527
+ t,
23528
+ columnColorGroups,
23529
+ defaultVisibleKeys
23427
23530
  }) {
23428
23531
  const controlButtonSize = size === "lg" ? "md" : "sm";
23429
23532
  const controlButtonClass = size === "sm" ? "h-7 px-2 text-xs" : size === "lg" ? "h-9 px-3 text-sm" : "h-8 px-2";
23430
23533
  const iconClass = size === "sm" ? "w-3.5 h-3.5 mr-1" : "w-4 h-4 mr-1";
23431
23534
  const captionClass = size === "sm" ? "text-xs" : size === "lg" ? "text-sm" : "text-sm";
23432
23535
  const leafCols = React58.useMemo(() => getLeafColumns(columns), [columns]);
23536
+ const { groups, ungrouped } = React58.useMemo(() => groupColumnsByColorTag(leafCols, columnColorGroups), [leafCols, columnColorGroups]);
23537
+ const handleSelectAll = () => setVisibleCols(leafCols.map((c) => c.key));
23538
+ const handleDefault = () => setVisibleCols(defaultVisibleKeys);
23433
23539
  return /* @__PURE__ */ jsxs55("div", { className: "flex items-center justify-between gap-4 mb-1", children: [
23434
23540
  /* @__PURE__ */ jsx67("div", { className: captionClass + " text-muted-foreground", children: caption }),
23435
23541
  /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2", children: [
@@ -23447,7 +23553,7 @@ function DataTableToolbar({
23447
23553
  ]
23448
23554
  }
23449
23555
  ),
23450
- enableColumnVisibilityToggle && /* @__PURE__ */ jsx67(
23556
+ enableColumnVisibilityToggle && /* @__PURE__ */ jsxs55(
23451
23557
  DropdownMenu_default,
23452
23558
  {
23453
23559
  trigger: /* @__PURE__ */ jsxs55(Button_default, { variant: "ghost", size: controlButtonSize, className: controlButtonClass, children: [
@@ -23462,19 +23568,49 @@ function DataTableToolbar({
23462
23568
  ) }),
23463
23569
  labels?.columns || t("columns")
23464
23570
  ] }),
23465
- children: leafCols.map((c) => /* @__PURE__ */ jsxs55(
23466
- DropdownMenuItem,
23467
- {
23468
- onClick: () => {
23469
- setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23470
- },
23471
- children: [
23472
- /* @__PURE__ */ jsx67("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23473
- /* @__PURE__ */ jsx67("span", { className: "truncate", children: c.title })
23474
- ]
23475
- },
23476
- c.key
23477
- ))
23571
+ children: [
23572
+ /* @__PURE__ */ jsxs55("div", { className: "flex items-center gap-2 px-2 py-1.5 mb-1", children: [
23573
+ /* @__PURE__ */ jsx67(Button_default, { variant: "outline", size: "sm", className: "h-7 text-xs flex-1", onClick: handleSelectAll, children: labels?.selectAll || t("selectAll") || "Select All" }),
23574
+ /* @__PURE__ */ jsx67(Button_default, { variant: "outline", size: "sm", className: "h-7 text-xs flex-1", onClick: handleDefault, children: labels?.default || t("default") || "Default" })
23575
+ ] }),
23576
+ /* @__PURE__ */ jsxs55("div", { className: "max-h-80 overflow-y-auto min-w-50", children: [
23577
+ groups.length > 0 && groups.map((group) => /* @__PURE__ */ jsxs55(React58.Fragment, { children: [
23578
+ /* @__PURE__ */ jsxs55("div", { className: "px-3 py-1.5 text-[11px] font-semibold uppercase text-muted-foreground flex items-center gap-2 mt-1 bg-accent/30 sticky top-0 z-10 backdrop-blur-sm", children: [
23579
+ /* @__PURE__ */ jsx67("span", { className: "w-2.5 h-2.5 rounded-full shadow-sm", style: { backgroundColor: group.colorTag } }),
23580
+ group.label
23581
+ ] }),
23582
+ group.columns.map((c) => /* @__PURE__ */ jsxs55(
23583
+ DropdownMenuItem,
23584
+ {
23585
+ onClick: () => {
23586
+ setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23587
+ },
23588
+ closeOnSelect: false,
23589
+ children: [
23590
+ /* @__PURE__ */ jsx67("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23591
+ /* @__PURE__ */ jsx67("span", { className: "truncate", children: c.title })
23592
+ ]
23593
+ },
23594
+ c.key
23595
+ ))
23596
+ ] }, group.colorTag)),
23597
+ groups.length > 0 && ungrouped.length > 0 && /* @__PURE__ */ jsx67("div", { className: "px-3 py-1.5 text-[11px] font-semibold uppercase text-muted-foreground mt-1 bg-accent/30 sticky top-0 z-10 backdrop-blur-sm", children: t("other") || "Other" }),
23598
+ ungrouped.map((c) => /* @__PURE__ */ jsxs55(
23599
+ DropdownMenuItem,
23600
+ {
23601
+ onClick: () => {
23602
+ setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23603
+ },
23604
+ closeOnSelect: false,
23605
+ children: [
23606
+ /* @__PURE__ */ jsx67("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23607
+ /* @__PURE__ */ jsx67("span", { className: "truncate", children: c.title })
23608
+ ]
23609
+ },
23610
+ c.key
23611
+ ))
23612
+ ] })
23613
+ ]
23478
23614
  }
23479
23615
  ),
23480
23616
  enableHeaderAlignToggle && /* @__PURE__ */ jsx67(
@@ -23770,7 +23906,7 @@ function useStickyColumns(visibleColumns) {
23770
23906
  const getStickyHeaderClass = React63.useCallback(
23771
23907
  (col) => {
23772
23908
  if (!col.fixed) return "";
23773
- return cn("sticky", col.fixed === "left" && "left-0", col.fixed === "right" && "right-0", getBoundaryShadowClass(col), "z-50 !bg-muted");
23909
+ return cn("sticky", col.fixed === "left" && "left-0", col.fixed === "right" && "right-0", getBoundaryShadowClass(col), "z-50");
23774
23910
  },
23775
23911
  [getBoundaryShadowClass]
23776
23912
  );
@@ -23975,7 +24111,8 @@ function DataTable({
23975
24111
  estimatedRowHeight,
23976
24112
  overscan = 8,
23977
24113
  enableHeaderAutoFit = true,
23978
- labels
24114
+ labels,
24115
+ columnColorGroups
23979
24116
  }) {
23980
24117
  const t = useSmartTranslations("Common");
23981
24118
  const [columnWidthOverrides, setColumnWidthOverrides] = React64.useState({});
@@ -23983,6 +24120,9 @@ function DataTable({
23983
24120
  () => applyColumnWidthOverrides(columns, columnWidthOverrides),
23984
24121
  [columnWidthOverrides, columns]
23985
24122
  );
24123
+ const defaultVisibleKeys = React64.useMemo(() => {
24124
+ return getLeafColumns(columnsWithWidthOverrides).filter((col) => col.visible !== false).map((col) => col.key);
24125
+ }, [columnsWithWidthOverrides]);
23986
24126
  const {
23987
24127
  headerAlign,
23988
24128
  setHeaderAlign,
@@ -24123,7 +24263,9 @@ function DataTable({
24123
24263
  setDensity,
24124
24264
  setHeaderAlign,
24125
24265
  labels,
24126
- t
24266
+ t,
24267
+ columnColorGroups,
24268
+ defaultVisibleKeys
24127
24269
  }
24128
24270
  ),
24129
24271
  /* @__PURE__ */ jsx68(
@@ -24777,7 +24919,7 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
24777
24919
  children: currentEmoji
24778
24920
  }
24779
24921
  ),
24780
- children: /* @__PURE__ */ jsxs62("div", { className: "flex flex-col gap-3 w-64 max-h-[360px] overflow-y-auto p-1", children: [
24922
+ children: /* @__PURE__ */ jsxs62("div", { className: "flex flex-col gap-3 w-64 max-h-90 overflow-y-auto p-1", children: [
24781
24923
  /* @__PURE__ */ jsx76("div", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1", children: t("callout.selectEmoji") }),
24782
24924
  /* @__PURE__ */ jsx76("div", { className: "h-44 overflow-y-auto border border-border/40 rounded-lg", children: /* @__PURE__ */ jsx76(EmojiPicker2, { onSelect: (emoji) => updateAttributes({ emoji }) }) }),
24783
24925
  /* @__PURE__ */ jsxs62("div", { className: "border-t border-border/50 pt-2 mt-1", children: [
@@ -24961,7 +25103,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
24961
25103
  /* @__PURE__ */ jsx77("div", { className: "h-4 w-5/6 bg-muted-foreground/20 rounded" })
24962
25104
  ] }),
24963
25105
  /* @__PURE__ */ jsx77("div", { className: "h-20 w-32 bg-muted-foreground/20 rounded-lg flex-shrink-0" })
24964
- ] }) : /* @__PURE__ */ jsxs63("div", { className: "flex flex-col sm:flex-row items-stretch justify-between min-h-[100px]", children: [
25106
+ ] }) : /* @__PURE__ */ jsxs63("div", { className: "flex flex-col sm:flex-row items-stretch justify-between min-h-25", children: [
24965
25107
  /* @__PURE__ */ jsxs63("div", { className: "flex-1 p-4 flex flex-col justify-between gap-1 min-w-0", children: [
24966
25108
  /* @__PURE__ */ jsxs63("div", { className: "flex flex-col gap-1 min-w-0", children: [
24967
25109
  /* @__PURE__ */ jsxs63("span", { className: "text-xs text-muted-foreground font-medium flex items-center gap-1.5 truncate", children: [
@@ -25244,7 +25386,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25244
25386
  /* @__PURE__ */ jsxs64("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
25245
25387
  fileSize ? /* @__PURE__ */ jsx78("span", { children: formatBytes(fileSize) }) : null,
25246
25388
  isUploading && /* @__PURE__ */ jsx78("span", { className: cn(
25247
- "font-medium truncate max-w-[200px]",
25389
+ "font-medium truncate max-w-50",
25248
25390
  uploadError ? "text-destructive" : "text-primary animate-pulse"
25249
25391
  ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
25250
25392
  isUploading && uploadError && /* @__PURE__ */ jsxs64(
@@ -32109,7 +32251,7 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
32109
32251
  editor.chain().focus().unsetLink().run();
32110
32252
  };
32111
32253
  return /* @__PURE__ */ jsxs73("div", { className: "flex items-center gap-1 p-1 max-w-sm", children: [
32112
- /* @__PURE__ */ jsx87("span", { className: "text-xs text-muted-foreground font-mono px-2 truncate max-w-[180px]", children: url }),
32254
+ /* @__PURE__ */ jsx87("span", { className: "text-xs text-muted-foreground font-mono px-2 truncate max-w-45", children: url }),
32113
32255
  /* @__PURE__ */ jsx87("div", { className: "w-px h-6 bg-border/50 mx-1" }),
32114
32256
  /* @__PURE__ */ jsx87(ToolbarButton, { onClick: handleOpen, title: t("linkPreview.open") || "Open link", children: /* @__PURE__ */ jsx87(ExternalLink3, { className: "w-4 h-4" }) }),
32115
32257
  /* @__PURE__ */ jsx87(ToolbarButton, { onClick: onEdit, title: t("linkPreview.edit") || "Edit link", children: /* @__PURE__ */ jsx87(Edit2, { className: "w-4 h-4" }) }),
@@ -32123,9 +32265,14 @@ var CustomBubbleMenu = ({
32123
32265
  }) => {
32124
32266
  const SHOW_DELAY_MS = 180;
32125
32267
  const BUBBLE_MENU_OFFSET = 16;
32268
+ const BUBBLE_MENU_ESTIMATED_HEIGHT = 44;
32126
32269
  const [isVisible, setIsVisible] = useState48(false);
32127
32270
  const [linkInputOpen, setLinkInputOpen] = useState48(false);
32128
- const [position, setPosition] = useState48({ top: 0, left: 0 });
32271
+ const [position, setPosition] = useState48({
32272
+ top: 0,
32273
+ left: 0,
32274
+ placement: "top"
32275
+ });
32129
32276
  const menuRef = useRef33(null);
32130
32277
  const keepOpenRef = useRef33(false);
32131
32278
  const showTimeoutRef = useRef33(null);
@@ -32182,12 +32329,18 @@ var CustomBubbleMenu = ({
32182
32329
  return;
32183
32330
  }
32184
32331
  const viewportPadding = 8;
32332
+ const menuHeight = menuRef.current?.getBoundingClientRect().height || BUBBLE_MENU_ESTIMATED_HEIGHT;
32333
+ const editorTop = view.dom.getBoundingClientRect().top;
32334
+ const selectionTop = Math.min(start.top, end.top);
32335
+ const selectionBottom = Math.max(start.bottom, end.bottom);
32336
+ const hasRoomAboveEditor = selectionTop - BUBBLE_MENU_OFFSET - menuHeight >= editorTop + viewportPadding;
32337
+ const placement = hasRoomAboveEditor ? "top" : "bottom";
32185
32338
  const left = Math.min(
32186
32339
  window.innerWidth - viewportPadding,
32187
32340
  Math.max(viewportPadding, (start.left + end.left) / 2)
32188
32341
  );
32189
- const top = Math.max(viewportPadding, start.top - BUBBLE_MENU_OFFSET);
32190
- setPosition({ top, left });
32342
+ const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
32343
+ setPosition({ top, left, placement });
32191
32344
  if (keepOpenRef.current) {
32192
32345
  clearShowTimeout();
32193
32346
  setIsVisible(true);
@@ -32232,7 +32385,7 @@ var CustomBubbleMenu = ({
32232
32385
  style: {
32233
32386
  top: `${position.top}px`,
32234
32387
  left: `${position.left}px`,
32235
- transform: "translate(-50%, -100%)"
32388
+ transform: position.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0)"
32236
32389
  },
32237
32390
  onMouseDown: (e) => {
32238
32391
  const target = e.target;