@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.160",
3
+ "version": "1.0.162",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 232,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -771,6 +771,9 @@ var en_default = {
771
771
  loading: "Loading",
772
772
  noData: "No data",
773
773
  clearFilter: "Clear filter",
774
+ selectAll: "Select all",
775
+ default: "Default",
776
+ other: "Other",
774
777
  headerAlign: "Header alignment",
775
778
  alignLeft: "Align left",
776
779
  alignCenter: "Align center",
@@ -1166,6 +1169,9 @@ var vi_default = {
1166
1169
  loading: "\u0110ang t\u1EA3i",
1167
1170
  noData: "Kh\xF4ng c\xF3 d\u1EEF li\u1EC7u",
1168
1171
  clearFilter: "X\xF3a b\u1ED9 l\u1ECDc",
1172
+ selectAll: "Ch\u1ECDn t\u1EA5t c\u1EA3",
1173
+ default: "M\u1EB7c \u0111\u1ECBnh",
1174
+ other: "Kh\xE1c",
1169
1175
  headerAlign: "C\u0103n ch\u1EC9nh ti\xEAu \u0111\u1EC1",
1170
1176
  alignLeft: "C\u0103n tr\xE1i",
1171
1177
  alignCenter: "C\u0103n gi\u1EEFa",
@@ -1561,6 +1567,9 @@ var ko_default = {
1561
1567
  loading: "\uB85C\uB529 \uC911",
1562
1568
  noData: "\uB370\uC774\uD130 \uC5C6\uC74C",
1563
1569
  clearFilter: "\uD544\uD130 \uC9C0\uC6B0\uAE30",
1570
+ selectAll: "\uC804\uCCB4 \uC120\uD0DD",
1571
+ default: "\uAE30\uBCF8\uAC12",
1572
+ other: "\uAE30\uD0C0",
1564
1573
  headerAlign: "\uD5E4\uB354 \uC815\uB82C",
1565
1574
  alignLeft: "\uC67C\uCABD \uC815\uB82C",
1566
1575
  alignCenter: "\uAC00\uC6B4\uB370 \uC815\uB82C",
@@ -1956,6 +1965,9 @@ var ja_default = {
1956
1965
  loading: "\u8AAD\u307F\u8FBC\u307F\u4E2D",
1957
1966
  noData: "\u30C7\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093",
1958
1967
  clearFilter: "\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u30AF\u30EA\u30A2",
1968
+ selectAll: "\u3059\u3079\u3066\u9078\u629E",
1969
+ default: "\u30C7\u30D5\u30A9\u30EB\u30C8",
1970
+ other: "\u305D\u306E\u4ED6",
1959
1971
  headerAlign: "\u30D8\u30C3\u30C0\u30FC\u914D\u7F6E",
1960
1972
  alignLeft: "\u5DE6\u63C3\u3048",
1961
1973
  alignCenter: "\u4E2D\u592E\u63C3\u3048",
@@ -7779,6 +7791,23 @@ var DropdownMenu = ({
7779
7791
  const itemsRef = import_react14.default.useRef([]);
7780
7792
  const [activeIndex, setActiveIndex] = useResettingIndex(open);
7781
7793
  const parentMenu = import_react14.default.useContext(DropdownMenuContext);
7794
+ const closeMenu = import_react14.default.useCallback(() => {
7795
+ setOpen(false);
7796
+ parentMenu?.closeMenu();
7797
+ }, [parentMenu, setOpen]);
7798
+ const getEnabledMenuItems = import_react14.default.useCallback(() => {
7799
+ const menuEl = menuRef.current;
7800
+ if (!menuEl) return [];
7801
+ return Array.from(menuEl.querySelectorAll("[data-dropdown-menu-item]")).filter((el) => !el.disabled);
7802
+ }, []);
7803
+ const focusMenuItem = import_react14.default.useCallback((index) => {
7804
+ const enabled = getEnabledMenuItems();
7805
+ const item = enabled[index];
7806
+ if (!item) return;
7807
+ setActiveIndex(index);
7808
+ item.focus();
7809
+ item.scrollIntoView({ block: "nearest" });
7810
+ }, [getEnabledMenuItems, setActiveIndex]);
7782
7811
  useShadCNAnimations();
7783
7812
  import_react14.default.useEffect(() => {
7784
7813
  if (!open) return;
@@ -7789,38 +7818,34 @@ var DropdownMenu = ({
7789
7818
  if (!active || !triggerEl || !menuEl) return;
7790
7819
  const isInMenu = menuEl.contains(active);
7791
7820
  const isOnTrigger = triggerEl.contains(active);
7792
- if (!isInMenu && !isOnTrigger) return;
7793
- const enabled = itemsRef.current.filter((el) => el && !el.disabled);
7821
+ const enabled = getEnabledMenuItems();
7794
7822
  if (enabled.length === 0) return;
7823
+ const currentIndex = enabled.findIndex((el) => el === active);
7824
+ const baseIndex = currentIndex >= 0 ? currentIndex : activeIndex;
7795
7825
  if (e.key === "ArrowDown") {
7796
7826
  e.preventDefault();
7797
- const next = (activeIndex + 1 + enabled.length) % enabled.length;
7798
- setActiveIndex(next);
7799
- enabled[next]?.focus();
7827
+ const next = (baseIndex + 1 + enabled.length) % enabled.length;
7828
+ focusMenuItem(next);
7800
7829
  } else if (e.key === "ArrowUp") {
7801
7830
  e.preventDefault();
7802
- const prev = (activeIndex - 1 + enabled.length) % enabled.length;
7803
- setActiveIndex(prev);
7804
- enabled[prev]?.focus();
7831
+ const prev = (baseIndex - 1 + enabled.length) % enabled.length;
7832
+ focusMenuItem(prev);
7805
7833
  } else if (e.key === "Home") {
7806
7834
  e.preventDefault();
7807
- setActiveIndex(0);
7808
- enabled[0]?.focus();
7835
+ focusMenuItem(0);
7809
7836
  } else if (e.key === "End") {
7810
7837
  e.preventDefault();
7811
- setActiveIndex(enabled.length - 1);
7812
- enabled[enabled.length - 1]?.focus();
7838
+ focusMenuItem(enabled.length - 1);
7839
+ } else if (e.key === "Escape" && (isInMenu || isOnTrigger)) {
7840
+ e.preventDefault();
7841
+ closeMenu();
7813
7842
  }
7814
7843
  };
7815
- document.addEventListener("keydown", handleKeyNav);
7844
+ document.addEventListener("keydown", handleKeyNav, true);
7816
7845
  return () => {
7817
- document.removeEventListener("keydown", handleKeyNav);
7846
+ document.removeEventListener("keydown", handleKeyNav, true);
7818
7847
  };
7819
- }, [open, activeIndex, setActiveIndex]);
7820
- const closeMenu = import_react14.default.useCallback(() => {
7821
- setOpen(false);
7822
- parentMenu?.closeMenu();
7823
- }, [parentMenu, setOpen]);
7848
+ }, [open, activeIndex, closeMenu, focusMenuItem, getEnabledMenuItems]);
7824
7849
  const menuContext = import_react14.default.useMemo(
7825
7850
  () => ({
7826
7851
  closeMenu,
@@ -7845,6 +7870,7 @@ var DropdownMenu = ({
7845
7870
  onClick: () => handleItemClick(item.onClick),
7846
7871
  disabled: item.disabled,
7847
7872
  role: "menuitem",
7873
+ "data-dropdown-menu-item": "",
7848
7874
  tabIndex: -1,
7849
7875
  style: {
7850
7876
  animationDelay: open ? `${Math.min(index * 20, 200)}ms` : "0ms"
@@ -7880,13 +7906,13 @@ var DropdownMenu = ({
7880
7906
  if (e.key === "ArrowDown") {
7881
7907
  e.preventDefault();
7882
7908
  setOpen(true);
7883
- requestAnimationFrame(() => itemsRef.current.find((el) => el && !el.disabled)?.focus());
7909
+ requestAnimationFrame(() => focusMenuItem(0));
7884
7910
  } else if (e.key === "ArrowUp") {
7885
7911
  e.preventDefault();
7886
7912
  setOpen(true);
7887
7913
  requestAnimationFrame(() => {
7888
- const enabled = itemsRef.current.filter((el) => el && !el.disabled);
7889
- enabled[enabled.length - 1]?.focus();
7914
+ const enabled = getEnabledMenuItems();
7915
+ focusMenuItem(enabled.length - 1);
7890
7916
  });
7891
7917
  } else if (e.key === "Escape") {
7892
7918
  e.preventDefault();
@@ -7937,6 +7963,8 @@ var DropdownMenuItem = ({
7937
7963
  },
7938
7964
  disabled,
7939
7965
  onMouseDown: (e) => e.preventDefault(),
7966
+ "data-dropdown-menu-item": "",
7967
+ tabIndex: -1,
7940
7968
  className: cn(
7941
7969
  "flex w-full items-center gap-2 px-3 py-2 text-sm rounded-lg transition-colors group cursor-pointer",
7942
7970
  "hover:bg-accent hover:text-accent-foreground",
@@ -23079,6 +23107,78 @@ function DataTableBodyRows({
23079
23107
  // src/components/DataTable/components/DataTableHeader.tsx
23080
23108
  var import_react30 = __toESM(require("react"), 1);
23081
23109
  var import_lucide_react36 = require("lucide-react");
23110
+
23111
+ // src/components/DataTable/utils/colorTag.ts
23112
+ function parseColor(color) {
23113
+ if (!color) return null;
23114
+ const c = color.trim().toLowerCase();
23115
+ if (c.startsWith("#")) {
23116
+ let hex = c.substring(1);
23117
+ if (hex.length === 3) {
23118
+ hex = hex.split("").map((char) => char + char).join("");
23119
+ }
23120
+ if (hex.length === 6) {
23121
+ const r = parseInt(hex.substring(0, 2), 16);
23122
+ const g = parseInt(hex.substring(2, 4), 16);
23123
+ const b = parseInt(hex.substring(4, 6), 16);
23124
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
23125
+ return [r, g, b];
23126
+ }
23127
+ }
23128
+ return null;
23129
+ }
23130
+ if (c.startsWith("rgb")) {
23131
+ const match = c.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
23132
+ if (match) {
23133
+ const r = parseInt(match[1], 10);
23134
+ const g = parseInt(match[2], 10);
23135
+ const b = parseInt(match[3], 10);
23136
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
23137
+ return [r, g, b];
23138
+ }
23139
+ }
23140
+ return null;
23141
+ }
23142
+ return null;
23143
+ }
23144
+ function toRgba(r, g, b, alpha) {
23145
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
23146
+ }
23147
+ function getHeaderBgColor(colorTag) {
23148
+ const rgb = parseColor(colorTag);
23149
+ if (!rgb) return colorTag;
23150
+ return toRgba(rgb[0], rgb[1], rgb[2], 1);
23151
+ }
23152
+ function groupColumnsByColorTag(leafCols, colorGroups) {
23153
+ const groupsMap = /* @__PURE__ */ new Map();
23154
+ const ungrouped = [];
23155
+ for (const col of leafCols) {
23156
+ if (col.colorTag && colorGroups && colorGroups[col.colorTag]) {
23157
+ const tag = col.colorTag;
23158
+ if (!groupsMap.has(tag)) {
23159
+ groupsMap.set(tag, []);
23160
+ }
23161
+ groupsMap.get(tag).push(col);
23162
+ } else {
23163
+ ungrouped.push(col);
23164
+ }
23165
+ }
23166
+ const groups = [];
23167
+ if (colorGroups) {
23168
+ for (const [tag, groupConfig] of Object.entries(colorGroups)) {
23169
+ if (groupsMap.has(tag)) {
23170
+ groups.push({
23171
+ colorTag: tag,
23172
+ label: groupConfig.label,
23173
+ columns: groupsMap.get(tag)
23174
+ });
23175
+ }
23176
+ }
23177
+ }
23178
+ return { groups, ungrouped };
23179
+ }
23180
+
23181
+ // src/components/DataTable/components/DataTableHeader.tsx
23082
23182
  var import_jsx_runtime65 = require("react/jsx-runtime");
23083
23183
  function getColumnLabel(title) {
23084
23184
  if (typeof title === "string" || typeof title === "number") {
@@ -23327,6 +23427,7 @@ function DataTableHeader({
23327
23427
  const isAfterFixedLeft = prevCol?.fixed === "left";
23328
23428
  const showBorderLeft = columnDividers && cellIndex > 0 && !isAfterFixedLeft && !col.fixed;
23329
23429
  const isLastCell = cellIndex === row.length - 1;
23430
+ const headerBackgroundColor = isLeaf && col.colorTag ? getHeaderBgColor(col.colorTag) : "var(--muted)";
23330
23431
  return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
23331
23432
  TableHead,
23332
23433
  {
@@ -23335,6 +23436,7 @@ function DataTableHeader({
23335
23436
  "data-underverse-column-key": isLeaf ? col.key : void 0,
23336
23437
  style: {
23337
23438
  width: col.width,
23439
+ backgroundColor: headerBackgroundColor,
23338
23440
  ...getStickyHeaderCellStyle(headerCell)
23339
23441
  },
23340
23442
  className: cn(
@@ -23411,7 +23513,6 @@ function DataTablePagination({
23411
23513
  result.push(totalPages);
23412
23514
  return result;
23413
23515
  }, [curPage, totalPages]);
23414
- if (!(totalItems > 0 && totalPages > 1)) return null;
23415
23516
  const controlButtonSize = size === "lg" ? "md" : "sm";
23416
23517
  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";
23417
23518
  const navIconClass = size === "sm" ? "h-3.5 w-3.5" : size === "lg" ? "h-5 w-5" : "h-4 w-4";
@@ -23593,13 +23694,18 @@ function DataTableToolbar({
23593
23694
  setDensity,
23594
23695
  setHeaderAlign,
23595
23696
  labels,
23596
- t
23697
+ t,
23698
+ columnColorGroups,
23699
+ defaultVisibleKeys
23597
23700
  }) {
23598
23701
  const controlButtonSize = size === "lg" ? "md" : "sm";
23599
23702
  const controlButtonClass = size === "sm" ? "h-7 px-2 text-xs" : size === "lg" ? "h-9 px-3 text-sm" : "h-8 px-2";
23600
23703
  const iconClass = size === "sm" ? "w-3.5 h-3.5 mr-1" : "w-4 h-4 mr-1";
23601
23704
  const captionClass = size === "sm" ? "text-xs" : size === "lg" ? "text-sm" : "text-sm";
23602
23705
  const leafCols = import_react32.default.useMemo(() => getLeafColumns(columns), [columns]);
23706
+ const { groups, ungrouped } = import_react32.default.useMemo(() => groupColumnsByColorTag(leafCols, columnColorGroups), [leafCols, columnColorGroups]);
23707
+ const handleSelectAll = () => setVisibleCols(leafCols.map((c) => c.key));
23708
+ const handleDefault = () => setVisibleCols(defaultVisibleKeys);
23603
23709
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "flex items-center justify-between gap-4 mb-1", children: [
23604
23710
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("div", { className: captionClass + " text-muted-foreground", children: caption }),
23605
23711
  /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -23617,7 +23723,7 @@ function DataTableToolbar({
23617
23723
  ]
23618
23724
  }
23619
23725
  ),
23620
- enableColumnVisibilityToggle && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
23726
+ enableColumnVisibilityToggle && /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
23621
23727
  DropdownMenu_default,
23622
23728
  {
23623
23729
  trigger: /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(Button_default, { variant: "ghost", size: controlButtonSize, className: controlButtonClass, children: [
@@ -23632,19 +23738,49 @@ function DataTableToolbar({
23632
23738
  ) }),
23633
23739
  labels?.columns || t("columns")
23634
23740
  ] }),
23635
- children: leafCols.map((c) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
23636
- DropdownMenuItem,
23637
- {
23638
- onClick: () => {
23639
- setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23640
- },
23641
- children: [
23642
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23643
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "truncate", children: c.title })
23644
- ]
23645
- },
23646
- c.key
23647
- ))
23741
+ children: [
23742
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "flex items-center gap-2 px-2 py-1.5 mb-1", children: [
23743
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(Button_default, { variant: "outline", size: "sm", className: "h-7 text-xs flex-1", onClick: handleSelectAll, children: labels?.selectAll || t("selectAll") || "Select All" }),
23744
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(Button_default, { variant: "outline", size: "sm", className: "h-7 text-xs flex-1", onClick: handleDefault, children: labels?.default || t("default") || "Default" })
23745
+ ] }),
23746
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "max-h-80 overflow-y-auto min-w-50", children: [
23747
+ groups.length > 0 && groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_react32.default.Fragment, { children: [
23748
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("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: [
23749
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "w-2.5 h-2.5 rounded-full shadow-sm", style: { backgroundColor: group.colorTag } }),
23750
+ group.label
23751
+ ] }),
23752
+ group.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
23753
+ DropdownMenuItem,
23754
+ {
23755
+ onClick: () => {
23756
+ setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23757
+ },
23758
+ closeOnSelect: false,
23759
+ children: [
23760
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23761
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "truncate", children: c.title })
23762
+ ]
23763
+ },
23764
+ c.key
23765
+ ))
23766
+ ] }, group.colorTag)),
23767
+ groups.length > 0 && ungrouped.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("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" }),
23768
+ ungrouped.map((c) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
23769
+ DropdownMenuItem,
23770
+ {
23771
+ onClick: () => {
23772
+ setVisibleCols((prev) => prev.includes(c.key) ? prev.filter((k) => k !== c.key) : [...prev, c.key]);
23773
+ },
23774
+ closeOnSelect: false,
23775
+ children: [
23776
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("input", { type: "checkbox", className: "mr-2 rounded-md border-border/50", readOnly: true, checked: visibleCols.includes(c.key) }),
23777
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "truncate", children: c.title })
23778
+ ]
23779
+ },
23780
+ c.key
23781
+ ))
23782
+ ] })
23783
+ ]
23648
23784
  }
23649
23785
  ),
23650
23786
  enableHeaderAlignToggle && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
@@ -23940,7 +24076,7 @@ function useStickyColumns(visibleColumns) {
23940
24076
  const getStickyHeaderClass = import_react37.default.useCallback(
23941
24077
  (col) => {
23942
24078
  if (!col.fixed) return "";
23943
- return cn("sticky", col.fixed === "left" && "left-0", col.fixed === "right" && "right-0", getBoundaryShadowClass(col), "z-50 !bg-muted");
24079
+ return cn("sticky", col.fixed === "left" && "left-0", col.fixed === "right" && "right-0", getBoundaryShadowClass(col), "z-50");
23944
24080
  },
23945
24081
  [getBoundaryShadowClass]
23946
24082
  );
@@ -24145,7 +24281,8 @@ function DataTable({
24145
24281
  estimatedRowHeight,
24146
24282
  overscan = 8,
24147
24283
  enableHeaderAutoFit = true,
24148
- labels
24284
+ labels,
24285
+ columnColorGroups
24149
24286
  }) {
24150
24287
  const t = useSmartTranslations("Common");
24151
24288
  const [columnWidthOverrides, setColumnWidthOverrides] = import_react38.default.useState({});
@@ -24153,6 +24290,9 @@ function DataTable({
24153
24290
  () => applyColumnWidthOverrides(columns, columnWidthOverrides),
24154
24291
  [columnWidthOverrides, columns]
24155
24292
  );
24293
+ const defaultVisibleKeys = import_react38.default.useMemo(() => {
24294
+ return getLeafColumns(columnsWithWidthOverrides).filter((col) => col.visible !== false).map((col) => col.key);
24295
+ }, [columnsWithWidthOverrides]);
24156
24296
  const {
24157
24297
  headerAlign,
24158
24298
  setHeaderAlign,
@@ -24293,7 +24433,9 @@ function DataTable({
24293
24433
  setDensity,
24294
24434
  setHeaderAlign,
24295
24435
  labels,
24296
- t
24436
+ t,
24437
+ columnColorGroups,
24438
+ defaultVisibleKeys
24297
24439
  }
24298
24440
  ),
24299
24441
  /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
@@ -24947,7 +25089,7 @@ var CalloutView = ({ node, updateAttributes, selected }) => {
24947
25089
  children: currentEmoji
24948
25090
  }
24949
25091
  ),
24950
- children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex flex-col gap-3 w-64 max-h-[360px] overflow-y-auto p-1", children: [
25092
+ children: /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "flex flex-col gap-3 w-64 max-h-90 overflow-y-auto p-1", children: [
24951
25093
  /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider px-1", children: t("callout.selectEmoji") }),
24952
25094
  /* @__PURE__ */ (0, import_jsx_runtime76.jsx)("div", { className: "h-44 overflow-y-auto border border-border/40 rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(EmojiPicker2, { onSelect: (emoji) => updateAttributes({ emoji }) }) }),
24953
25095
  /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)("div", { className: "border-t border-border/50 pt-2 mt-1", children: [
@@ -25131,7 +25273,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
25131
25273
  /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "h-4 w-5/6 bg-muted-foreground/20 rounded" })
25132
25274
  ] }),
25133
25275
  /* @__PURE__ */ (0, import_jsx_runtime77.jsx)("div", { className: "h-20 w-32 bg-muted-foreground/20 rounded-lg flex-shrink-0" })
25134
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "flex flex-col sm:flex-row items-stretch justify-between min-h-[100px]", children: [
25276
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "flex flex-col sm:flex-row items-stretch justify-between min-h-25", children: [
25135
25277
  /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "flex-1 p-4 flex flex-col justify-between gap-1 min-w-0", children: [
25136
25278
  /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("div", { className: "flex flex-col gap-1 min-w-0", children: [
25137
25279
  /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)("span", { className: "text-xs text-muted-foreground font-medium flex items-center gap-1.5 truncate", children: [
@@ -25414,7 +25556,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
25414
25556
  /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
25415
25557
  fileSize ? /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { children: formatBytes(fileSize) }) : null,
25416
25558
  isUploading && /* @__PURE__ */ (0, import_jsx_runtime78.jsx)("span", { className: cn(
25417
- "font-medium truncate max-w-[200px]",
25559
+ "font-medium truncate max-w-50",
25418
25560
  uploadError ? "text-destructive" : "text-primary animate-pulse"
25419
25561
  ), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
25420
25562
  isUploading && uploadError && /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
@@ -32196,7 +32338,7 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
32196
32338
  editor.chain().focus().unsetLink().run();
32197
32339
  };
32198
32340
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)("div", { className: "flex items-center gap-1 p-1 max-w-sm", children: [
32199
- /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "text-xs text-muted-foreground font-mono px-2 truncate max-w-[180px]", children: url }),
32341
+ /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("span", { className: "text-xs text-muted-foreground font-mono px-2 truncate max-w-45", children: url }),
32200
32342
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "w-px h-6 bg-border/50 mx-1" }),
32201
32343
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(ToolbarButton, { onClick: handleOpen, title: t("linkPreview.open") || "Open link", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react50.ExternalLink, { className: "w-4 h-4" }) }),
32202
32344
  /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(ToolbarButton, { onClick: onEdit, title: t("linkPreview.edit") || "Edit link", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(import_lucide_react50.Edit2, { className: "w-4 h-4" }) }),
@@ -32210,9 +32352,14 @@ var CustomBubbleMenu = ({
32210
32352
  }) => {
32211
32353
  const SHOW_DELAY_MS = 180;
32212
32354
  const BUBBLE_MENU_OFFSET = 16;
32355
+ const BUBBLE_MENU_ESTIMATED_HEIGHT = 44;
32213
32356
  const [isVisible, setIsVisible] = (0, import_react64.useState)(false);
32214
32357
  const [linkInputOpen, setLinkInputOpen] = (0, import_react64.useState)(false);
32215
- const [position, setPosition] = (0, import_react64.useState)({ top: 0, left: 0 });
32358
+ const [position, setPosition] = (0, import_react64.useState)({
32359
+ top: 0,
32360
+ left: 0,
32361
+ placement: "top"
32362
+ });
32216
32363
  const menuRef = (0, import_react64.useRef)(null);
32217
32364
  const keepOpenRef = (0, import_react64.useRef)(false);
32218
32365
  const showTimeoutRef = (0, import_react64.useRef)(null);
@@ -32269,12 +32416,18 @@ var CustomBubbleMenu = ({
32269
32416
  return;
32270
32417
  }
32271
32418
  const viewportPadding = 8;
32419
+ const menuHeight = menuRef.current?.getBoundingClientRect().height || BUBBLE_MENU_ESTIMATED_HEIGHT;
32420
+ const editorTop = view.dom.getBoundingClientRect().top;
32421
+ const selectionTop = Math.min(start.top, end.top);
32422
+ const selectionBottom = Math.max(start.bottom, end.bottom);
32423
+ const hasRoomAboveEditor = selectionTop - BUBBLE_MENU_OFFSET - menuHeight >= editorTop + viewportPadding;
32424
+ const placement = hasRoomAboveEditor ? "top" : "bottom";
32272
32425
  const left = Math.min(
32273
32426
  window.innerWidth - viewportPadding,
32274
32427
  Math.max(viewportPadding, (start.left + end.left) / 2)
32275
32428
  );
32276
- const top = Math.max(viewportPadding, start.top - BUBBLE_MENU_OFFSET);
32277
- setPosition({ top, left });
32429
+ const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
32430
+ setPosition({ top, left, placement });
32278
32431
  if (keepOpenRef.current) {
32279
32432
  clearShowTimeout();
32280
32433
  setIsVisible(true);
@@ -32319,7 +32472,7 @@ var CustomBubbleMenu = ({
32319
32472
  style: {
32320
32473
  top: `${position.top}px`,
32321
32474
  left: `${position.left}px`,
32322
- transform: "translate(-50%, -100%)"
32475
+ transform: position.placement === "top" ? "translate(-50%, -100%)" : "translate(-50%, 0)"
32323
32476
  },
32324
32477
  onMouseDown: (e) => {
32325
32478
  const target = e.target;