@underverse-ui/underverse 1.0.112 → 1.0.115

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
@@ -7507,6 +7507,7 @@ var Combobox = ({
7507
7507
  id: `${resolvedId}-listbox`,
7508
7508
  role: "listbox",
7509
7509
  "aria-labelledby": labelId,
7510
+ "data-os-ignore": virtualized ? "" : void 0,
7510
7511
  className: cn("overflow-y-auto overscroll-contain", (!useOverlayScrollbar || virtualized) && comboboxScrollClassName),
7511
7512
  style: { maxHeight },
7512
7513
  children: /* @__PURE__ */ jsx29("div", { className: cn(size === "sm" ? "p-1" : size === "lg" ? "p-2" : "p-1.5"), children: loading2 ? /* @__PURE__ */ jsx29("div", { className: "px-3 py-10 text-center", children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col items-center gap-3 animate-in fade-in-0 zoom-in-95 duration-300", children: [
@@ -15763,19 +15764,19 @@ var MultiCombobox = ({
15763
15764
  },
15764
15765
  "data-index": virtualItem?.index,
15765
15766
  style: itemStyle,
15767
+ className: cn(virtualItem && "list-none"),
15766
15768
  onClick: (e) => {
15767
15769
  e.preventDefault();
15768
15770
  e.stopPropagation();
15769
15771
  if (!isDisabled) toggleSelect(item.value);
15770
15772
  inputRef.current?.focus();
15771
15773
  },
15772
- className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"),
15773
- children: renderOption(item, isSelected)
15774
+ children: /* @__PURE__ */ jsx45("div", { className: cn("dropdown-item", isDisabled && "opacity-50 cursor-not-allowed pointer-events-none"), children: renderOption(item, isSelected) })
15774
15775
  },
15775
15776
  item.value
15776
15777
  );
15777
15778
  }
15778
- return /* @__PURE__ */ jsxs35(
15779
+ return /* @__PURE__ */ jsx45(
15779
15780
  "li",
15780
15781
  {
15781
15782
  ref: (node) => {
@@ -15784,37 +15785,43 @@ var MultiCombobox = ({
15784
15785
  },
15785
15786
  "data-index": virtualItem?.index,
15786
15787
  style: itemStyle,
15788
+ className: cn(virtualItem && "list-none"),
15787
15789
  onClick: (e) => {
15788
15790
  e.preventDefault();
15789
15791
  e.stopPropagation();
15790
15792
  if (!isDisabled) toggleSelect(item.value);
15791
15793
  inputRef.current?.focus();
15792
15794
  },
15793
- className: cn(
15794
- "dropdown-item flex cursor-pointer items-center gap-3 rounded-full transition-all duration-200",
15795
- sizeStyles8[size].item,
15796
- "hover:bg-accent/70 hover:shadow-sm",
15797
- index === activeIndex && "bg-accent/60",
15798
- isSelected && "bg-primary/10 text-primary font-medium",
15799
- isDisabled && "opacity-40 cursor-not-allowed pointer-events-none"
15800
- ),
15801
- children: [
15802
- /* @__PURE__ */ jsx45(
15803
- "span",
15804
- {
15805
- className: cn(
15806
- "shrink-0 w-5 h-5 flex items-center justify-center rounded-md border-2 transition-all duration-200",
15807
- isSelected ? "bg-primary border-primary text-primary-foreground" : "border-muted-foreground/30 bg-transparent"
15795
+ children: /* @__PURE__ */ jsxs35(
15796
+ "div",
15797
+ {
15798
+ className: cn(
15799
+ "dropdown-item flex cursor-pointer items-center gap-3 rounded-full transition-all duration-200",
15800
+ sizeStyles8[size].item,
15801
+ "hover:bg-accent/70 hover:shadow-sm",
15802
+ index === activeIndex && "bg-accent/60",
15803
+ isSelected && "bg-primary/10 text-primary font-medium",
15804
+ isDisabled && "opacity-40 cursor-not-allowed pointer-events-none"
15805
+ ),
15806
+ children: [
15807
+ /* @__PURE__ */ jsx45(
15808
+ "span",
15809
+ {
15810
+ className: cn(
15811
+ "shrink-0 w-5 h-5 flex items-center justify-center rounded-md border-2 transition-all duration-200",
15812
+ isSelected ? "bg-primary border-primary text-primary-foreground" : "border-muted-foreground/30 bg-transparent"
15813
+ ),
15814
+ children: isSelected && /* @__PURE__ */ jsx45(Check6, { className: "w-3 h-3" })
15815
+ }
15808
15816
  ),
15809
- children: isSelected && /* @__PURE__ */ jsx45(Check6, { className: "w-3 h-3" })
15810
- }
15811
- ),
15812
- optionIcon && /* @__PURE__ */ jsx45("span", { className: "shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground", children: optionIcon }),
15813
- /* @__PURE__ */ jsxs35("div", { className: "flex-1 min-w-0", children: [
15814
- /* @__PURE__ */ jsx45("div", { className: cn("truncate", isSelected && "font-medium text-primary"), children: item.label }),
15815
- optionDesc && /* @__PURE__ */ jsx45("div", { className: "text-xs text-muted-foreground truncate mt-0.5", children: optionDesc })
15816
- ] })
15817
- ]
15817
+ optionIcon && /* @__PURE__ */ jsx45("span", { className: "shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground", children: optionIcon }),
15818
+ /* @__PURE__ */ jsxs35("div", { className: "flex-1 min-w-0", children: [
15819
+ /* @__PURE__ */ jsx45("div", { className: cn("truncate", isSelected && "font-medium text-primary"), children: item.label }),
15820
+ optionDesc && /* @__PURE__ */ jsx45("div", { className: "text-xs text-muted-foreground truncate mt-0.5", children: optionDesc })
15821
+ ] })
15822
+ ]
15823
+ }
15824
+ )
15818
15825
  },
15819
15826
  item.value
15820
15827
  );
@@ -15880,6 +15887,7 @@ var MultiCombobox = ({
15880
15887
  role: "listbox",
15881
15888
  "aria-multiselectable": "true",
15882
15889
  ref: optionsListRef,
15890
+ "data-os-ignore": virtualized ? "" : void 0,
15883
15891
  style: { maxHeight },
15884
15892
  className: cn(
15885
15893
  "overflow-y-auto p-1.5",
@@ -17282,6 +17290,7 @@ function OverlayControls({
17282
17290
 
17283
17291
  // src/components/CategoryTreeSelect.tsx
17284
17292
  import { useEffect as useEffect25, useId as useId11, useMemo as useMemo19, useRef as useRef20, useState as useState31 } from "react";
17293
+ import { useVirtualizer as useVirtualizer3 } from "@tanstack/react-virtual";
17285
17294
  import { ChevronRight as ChevronRight6, ChevronDown as ChevronDown5, FolderTree, Layers, Search as Search6, SearchX as SearchX3, X as X14 } from "lucide-react";
17286
17295
  import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
17287
17296
  var defaultLabels = {
@@ -17295,6 +17304,7 @@ var TREE_NODE_INDENT_REM = 1;
17295
17304
  var TREE_BRANCH_OFFSET_CLASS = "ml-1.5 pl-1.5";
17296
17305
  var TREE_NODE_GAP_CLASS = "gap-1.5";
17297
17306
  var TREE_EXPANDER_PLACEHOLDER_CLASS = "w-5";
17307
+ var CATEGORY_TREE_DROPDOWN_MAX_HEIGHT = 320;
17298
17308
  function getAncestorPathIds(categories, targetId) {
17299
17309
  const byId = new Map(categories.map((category) => [category.id, category]));
17300
17310
  const expanded = /* @__PURE__ */ new Set();
@@ -17373,6 +17383,24 @@ function pruneAncestorSelection(categories, childrenMap, selected, fromCategoryI
17373
17383
  function toCategoryOrderSelection(categories, selected) {
17374
17384
  return categories.map((category) => category.id).filter((categoryId) => selected.has(categoryId));
17375
17385
  }
17386
+ function flattenVisibleCategories(roots, childrenMap, expandedNodes, expandAllVisibleBranches) {
17387
+ const rows = [];
17388
+ const stack = roots.map((category) => ({ category, level: 0, path: /* @__PURE__ */ new Set() })).reverse();
17389
+ while (stack.length > 0) {
17390
+ const { category, level, path } = stack.pop();
17391
+ if (path.has(category.id)) continue;
17392
+ rows.push({ category, level });
17393
+ const children = childrenMap.get(category.id) ?? [];
17394
+ const isExpanded = children.length > 0 && (expandAllVisibleBranches || expandedNodes.has(category.id));
17395
+ if (!isExpanded) continue;
17396
+ const nextPath = new Set(path);
17397
+ nextPath.add(category.id);
17398
+ for (let index = children.length - 1; index >= 0; index -= 1) {
17399
+ stack.push({ category: children[index], level: level + 1, path: nextPath });
17400
+ }
17401
+ }
17402
+ return rows;
17403
+ }
17376
17404
  function CategoryTreeSelect(props) {
17377
17405
  const tv = useSmartTranslations("ValidationInput");
17378
17406
  const {
@@ -17401,6 +17429,15 @@ function CategoryTreeSelect(props) {
17401
17429
  className,
17402
17430
  useOverlayScrollbar = false,
17403
17431
  leafOnlySelect = false,
17432
+ virtualized = false,
17433
+ estimatedItemHeight = 44,
17434
+ overscan = 8,
17435
+ maxInitialOptions,
17436
+ searchMode = "auto",
17437
+ onSearchChange,
17438
+ searchDebounceMs = 0,
17439
+ minSearchLength = 0,
17440
+ showSearchPromptWhenEmptyQuery = false,
17404
17441
  singleSelect = false
17405
17442
  } = props;
17406
17443
  const [isOpen, setIsOpen] = useState31(false);
@@ -17408,10 +17445,13 @@ function CategoryTreeSelect(props) {
17408
17445
  () => getInitialExpandedNodes(categories, { defaultExpanded, defaultExpandedIds, expandToId, viewOnly, inline })
17409
17446
  );
17410
17447
  const [query, setQuery] = useState31("");
17448
+ const [activeIndex, setActiveIndex] = useState31(null);
17411
17449
  const [localRequiredError, setLocalRequiredError] = useState31();
17412
17450
  const searchInputRef = useRef20(null);
17413
17451
  const dropdownViewportRef = useRef20(null);
17414
- useOverlayScrollbarTarget(dropdownViewportRef, { enabled: useOverlayScrollbar });
17452
+ useOverlayScrollbarTarget(dropdownViewportRef, {
17453
+ enabled: isOpen && useOverlayScrollbar && !virtualized && !inline && !viewOnly
17454
+ });
17415
17455
  const autoId = useId11();
17416
17456
  const resolvedId = id ? String(id) : `category-tree-select-${autoId}`;
17417
17457
  const labelId = label ? `${resolvedId}-label` : void 0;
@@ -17431,7 +17471,7 @@ function CategoryTreeSelect(props) {
17431
17471
  const parentCategories2 = [];
17432
17472
  for (const cat of categories) byId2.set(cat.id, cat);
17433
17473
  for (const cat of categories) {
17434
- if (cat.parent_id == null) {
17474
+ if (cat.parent_id == null || !byId2.has(cat.parent_id)) {
17435
17475
  parentCategories2.push(cat);
17436
17476
  continue;
17437
17477
  }
@@ -17440,12 +17480,19 @@ function CategoryTreeSelect(props) {
17440
17480
  }
17441
17481
  return { parentCategories: parentCategories2, childrenMap: childrenMap2, byId: byId2 };
17442
17482
  }, [categories]);
17443
- const isSearchEnabled = useMemo19(() => enableSearch ?? categories.length > 10, [enableSearch, categories.length]);
17444
- const normalizedQuery = useMemo19(() => query.trim().toLowerCase(), [query]);
17483
+ const isSearchEnabled = useMemo19(
17484
+ () => enableSearch ?? (categories.length > 10 || searchMode === "manual" || minSearchLength > 0 || !!onSearchChange),
17485
+ [categories.length, enableSearch, minSearchLength, onSearchChange, searchMode]
17486
+ );
17487
+ const trimmedQuery = useMemo19(() => query.trim(), [query]);
17488
+ const normalizedQuery = useMemo19(() => trimmedQuery.toLowerCase(), [trimmedQuery]);
17489
+ const queryMeetsMinimum = trimmedQuery.length >= minSearchLength;
17490
+ const shouldPromptForSearch = minSearchLength > 0 && !queryMeetsMinimum && (searchMode === "manual" || showSearchPromptWhenEmptyQuery);
17445
17491
  const isSearchMode = isSearchEnabled && normalizedQuery.length > 0;
17492
+ const shouldAutoExpandSearchResults = searchMode === "auto" && isSearchMode;
17446
17493
  const effectiveExpandedNodes = useMemo19(() => getExpandedNodesState(expandedIds, expandedNodes), [expandedIds, expandedNodes]);
17447
17494
  const visibleIds = useMemo19(() => {
17448
- if (!isSearchMode) return null;
17495
+ if (shouldPromptForSearch || !isSearchMode || searchMode === "manual") return null;
17449
17496
  const matches = categories.filter((c) => c.name.toLowerCase().includes(normalizedQuery));
17450
17497
  if (matches.length === 0) return /* @__PURE__ */ new Set();
17451
17498
  const visible = /* @__PURE__ */ new Set();
@@ -17482,7 +17529,14 @@ function CategoryTreeSelect(props) {
17482
17529
  addDescendants(m.id);
17483
17530
  }
17484
17531
  return visible;
17485
- }, [byId, categories, childrenMap, isSearchMode, normalizedQuery]);
17532
+ }, [byId, categories, childrenMap, isSearchMode, normalizedQuery, searchMode, shouldPromptForSearch]);
17533
+ useEffect25(() => {
17534
+ if (!onSearchChange) return;
17535
+ const timeoutId = window.setTimeout(() => {
17536
+ onSearchChange(trimmedQuery);
17537
+ }, Math.max(0, searchDebounceMs));
17538
+ return () => window.clearTimeout(timeoutId);
17539
+ }, [onSearchChange, searchDebounceMs, trimmedQuery]);
17486
17540
  useEffect25(() => {
17487
17541
  if (!isOpen) return;
17488
17542
  if (!isSearchEnabled) return;
@@ -17495,7 +17549,7 @@ function CategoryTreeSelect(props) {
17495
17549
  }
17496
17550
  }, [disabled, required, valueArray.length]);
17497
17551
  const toggleExpand = (id2) => {
17498
- if (isSearchMode) return;
17552
+ if (shouldAutoExpandSearchResults) return;
17499
17553
  const newExpanded = new Set(effectiveExpandedNodes);
17500
17554
  if (newExpanded.has(id2)) {
17501
17555
  newExpanded.delete(id2);
@@ -17546,27 +17600,137 @@ function CategoryTreeSelect(props) {
17546
17600
  onChange(toCategoryOrderSelection(categories, newSelected));
17547
17601
  }
17548
17602
  };
17549
- const renderCategory = (category, level = 0) => {
17603
+ const effectiveParentCategories = useMemo19(() => {
17604
+ if (shouldPromptForSearch) return [];
17605
+ if (!isSearchMode || searchMode === "manual") return parentCategories;
17606
+ return parentCategories.filter((c) => visibleIds?.has(c.id));
17607
+ }, [isSearchMode, parentCategories, searchMode, shouldPromptForSearch, visibleIds]);
17608
+ const effectiveChildrenMap = useMemo19(() => {
17609
+ if (shouldPromptForSearch || !isSearchMode || !visibleIds || searchMode === "manual") return childrenMap;
17610
+ const nextChildrenMap = /* @__PURE__ */ new Map();
17611
+ for (const [parentId, children] of childrenMap.entries()) {
17612
+ nextChildrenMap.set(
17613
+ parentId,
17614
+ children.filter((child) => visibleIds.has(child.id))
17615
+ );
17616
+ }
17617
+ return nextChildrenMap;
17618
+ }, [childrenMap, isSearchMode, searchMode, shouldPromptForSearch, visibleIds]);
17619
+ const flattenedRows = useMemo19(() => {
17620
+ if (shouldPromptForSearch) return [];
17621
+ const rows = flattenVisibleCategories(effectiveParentCategories, effectiveChildrenMap, effectiveExpandedNodes, shouldAutoExpandSearchResults);
17622
+ if (trimmedQuery || maxInitialOptions === void 0 || maxInitialOptions < 1) {
17623
+ return rows;
17624
+ }
17625
+ const limitedRows = rows.slice(0, maxInitialOptions);
17626
+ const includedIds = new Set(limitedRows.map((row) => row.category.id));
17627
+ const pinnedIds = /* @__PURE__ */ new Set();
17628
+ for (const selectedId of selectedIds) {
17629
+ for (const ancestorId of getAncestorPathIds(categories, selectedId)) {
17630
+ pinnedIds.add(ancestorId);
17631
+ }
17632
+ }
17633
+ if (typeof expandToId === "number") {
17634
+ for (const ancestorId of getAncestorPathIds(categories, expandToId)) {
17635
+ pinnedIds.add(ancestorId);
17636
+ }
17637
+ }
17638
+ for (const row of rows) {
17639
+ if (!pinnedIds.has(row.category.id) || includedIds.has(row.category.id)) continue;
17640
+ limitedRows.push(row);
17641
+ includedIds.add(row.category.id);
17642
+ }
17643
+ return limitedRows;
17644
+ }, [
17645
+ categories,
17646
+ effectiveChildrenMap,
17647
+ effectiveExpandedNodes,
17648
+ effectiveParentCategories,
17649
+ expandToId,
17650
+ maxInitialOptions,
17651
+ selectedIds,
17652
+ shouldAutoExpandSearchResults,
17653
+ shouldPromptForSearch,
17654
+ trimmedQuery
17655
+ ]);
17656
+ const canVirtualize = virtualized && !inline && !viewOnly;
17657
+ const treeVirtualizer = useVirtualizer3({
17658
+ count: canVirtualize ? flattenedRows.length : 0,
17659
+ getScrollElement: () => dropdownViewportRef.current,
17660
+ estimateSize: () => estimatedItemHeight,
17661
+ initialRect: { width: 0, height: CATEGORY_TREE_DROPDOWN_MAX_HEIGHT },
17662
+ overscan,
17663
+ enabled: canVirtualize
17664
+ });
17665
+ const virtualRows = canVirtualize ? treeVirtualizer.getVirtualItems() : [];
17666
+ const scrollVirtualTreeToStart = () => {
17667
+ if (!canVirtualize || flattenedRows.length === 0) return;
17668
+ treeVirtualizer.scrollToIndex(0, { align: "start" });
17669
+ };
17670
+ const moveActiveVirtualRow = (direction) => {
17671
+ if (!canVirtualize || flattenedRows.length === 0) return;
17672
+ const nextIndex = activeIndex === null ? direction === 1 ? 0 : flattenedRows.length - 1 : (activeIndex + direction + flattenedRows.length) % flattenedRows.length;
17673
+ setActiveIndex(nextIndex);
17674
+ treeVirtualizer.scrollToIndex(nextIndex, { align: "auto" });
17675
+ };
17676
+ const handleVirtualTreeKeyDown = (event) => {
17677
+ if (!canVirtualize) return;
17678
+ if (event.key === "ArrowDown") {
17679
+ event.preventDefault();
17680
+ moveActiveVirtualRow(1);
17681
+ return;
17682
+ }
17683
+ if (event.key === "ArrowUp") {
17684
+ event.preventDefault();
17685
+ moveActiveVirtualRow(-1);
17686
+ return;
17687
+ }
17688
+ if (event.key === "Enter" && activeIndex !== null) {
17689
+ const row = flattenedRows[activeIndex];
17690
+ if (!row) return;
17691
+ event.preventDefault();
17692
+ handleSelect(row.category.id, row.category);
17693
+ }
17694
+ };
17695
+ useEffect25(() => {
17696
+ setActiveIndex(null);
17697
+ }, [flattenedRows]);
17698
+ const renderCategoryRow = (category, level = 0, virtualItem) => {
17550
17699
  const children = effectiveChildrenMap.get(category.id) || [];
17551
17700
  const hasChildren = children.length > 0;
17552
- const isExpanded = hasChildren && (isSearchMode || effectiveExpandedNodes.has(category.id));
17701
+ const isExpanded = hasChildren && (shouldAutoExpandSearchResults || effectiveExpandedNodes.has(category.id));
17553
17702
  const isSelected = selectedIds.has(category.id);
17554
17703
  const isSelectable = !viewOnly && (!leafOnlySelect || !hasChildren);
17704
+ const isActive = virtualItem?.index === activeIndex;
17705
+ const rowStyle = virtualItem ? {
17706
+ position: "absolute",
17707
+ top: 0,
17708
+ left: 0,
17709
+ width: "100%",
17710
+ transform: `translateY(${virtualItem.start}px)`
17711
+ } : void 0;
17555
17712
  return /* @__PURE__ */ jsxs39(
17556
17713
  "div",
17557
17714
  {
17558
- className: "min-w-0 animate-in fade-in-50 duration-200 [content-visibility:auto] [contain-intrinsic-size:44px]",
17559
- style: { animationDelay: `${level * 30}ms` },
17715
+ ref: virtualItem ? treeVirtualizer.measureElement : void 0,
17716
+ "data-index": virtualItem?.index,
17717
+ className: cn("min-w-0 [content-visibility:auto] [contain-intrinsic-size:44px]", !virtualItem && "animate-in fade-in-50 duration-200"),
17718
+ style: { animationDelay: virtualItem ? void 0 : `${level * 30}ms`, ...rowStyle },
17560
17719
  children: [
17561
17720
  /* @__PURE__ */ jsxs39(
17562
17721
  "div",
17563
17722
  {
17723
+ role: "treeitem",
17724
+ "aria-level": level + 1,
17725
+ "aria-expanded": hasChildren ? isExpanded : void 0,
17726
+ "aria-selected": viewOnly ? void 0 : isSelected,
17564
17727
  onClick: () => !viewOnly && handleSelect(category.id, category),
17565
17728
  className: cn(
17566
17729
  "relative flex min-w-0 items-center px-3 py-2.5 min-h-11 transition-all duration-200 rounded-3xl",
17567
17730
  TREE_NODE_GAP_CLASS,
17568
17731
  !viewOnly && (isSelectable ? "cursor-pointer" : "cursor-default"),
17569
17732
  isSelectable && !isSelected && "hover:bg-accent/50",
17733
+ canVirtualize && isActive && "bg-accent/50",
17570
17734
  // Selected state - đồng bộ cho tất cả
17571
17735
  !viewOnly && isSelected && "bg-accent/40"
17572
17736
  ),
@@ -17576,6 +17740,7 @@ function CategoryTreeSelect(props) {
17576
17740
  "button",
17577
17741
  {
17578
17742
  type: "button",
17743
+ "aria-label": isExpanded ? `Collapse ${category.name}` : `Expand ${category.name}`,
17579
17744
  onClick: (e) => {
17580
17745
  e.stopPropagation();
17581
17746
  toggleExpand(category.id);
@@ -17585,9 +17750,9 @@ function CategoryTreeSelect(props) {
17585
17750
  "hover:scale-110 active:scale-95",
17586
17751
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
17587
17752
  isExpanded && "text-primary",
17588
- isSearchMode && "opacity-60 cursor-not-allowed hover:scale-100 active:scale-100"
17753
+ shouldAutoExpandSearchResults && "opacity-60 cursor-not-allowed hover:scale-100 active:scale-100"
17589
17754
  ),
17590
- disabled: isSearchMode,
17755
+ disabled: shouldAutoExpandSearchResults,
17591
17756
  children: /* @__PURE__ */ jsx49("div", { className: cn("transition-transform duration-200", isExpanded && "rotate-90"), children: /* @__PURE__ */ jsx49(ChevronRight6, { className: "w-4 h-4" }) })
17592
17757
  }
17593
17758
  ) : /* @__PURE__ */ jsx49("span", { className: TREE_EXPANDER_PLACEHOLDER_CLASS }),
@@ -17618,15 +17783,16 @@ function CategoryTreeSelect(props) {
17618
17783
  ]
17619
17784
  }
17620
17785
  ),
17621
- hasChildren && isExpanded && /* @__PURE__ */ jsx49(
17786
+ !virtualItem && hasChildren && isExpanded && /* @__PURE__ */ jsx49(
17622
17787
  "div",
17623
17788
  {
17789
+ role: "group",
17624
17790
  className: cn(
17625
17791
  TREE_BRANCH_OFFSET_CLASS,
17626
17792
  "border-l-2 border-dashed border-border/50",
17627
17793
  "animate-in slide-in-from-top-2 fade-in-50 duration-200"
17628
17794
  ),
17629
- children: children.map((child) => renderCategory(child, level + 1))
17795
+ children: children.map((child) => renderCategoryRow(child, level + 1))
17630
17796
  }
17631
17797
  )
17632
17798
  ]
@@ -17643,7 +17809,11 @@ function CategoryTreeSelect(props) {
17643
17809
  {
17644
17810
  ref: searchInputRef,
17645
17811
  value: query,
17646
- onChange: (e) => setQuery(e.target.value),
17812
+ onChange: (e) => {
17813
+ setQuery(e.target.value);
17814
+ scrollVirtualTreeToStart();
17815
+ },
17816
+ onKeyDown: handleVirtualTreeKeyDown,
17647
17817
  placeholder: mergedLabels.searchPlaceholder,
17648
17818
  className: cn(
17649
17819
  "peer w-full rounded-full bg-background/90 py-2.5 pl-10 pr-10 text-sm shadow-sm",
@@ -17660,6 +17830,7 @@ function CategoryTreeSelect(props) {
17660
17830
  type: "button",
17661
17831
  onClick: () => {
17662
17832
  setQuery("");
17833
+ scrollVirtualTreeToStart();
17663
17834
  searchInputRef.current?.focus();
17664
17835
  },
17665
17836
  className: cn(
@@ -17674,24 +17845,40 @@ function CategoryTreeSelect(props) {
17674
17845
  )
17675
17846
  ] }) });
17676
17847
  };
17677
- const effectiveParentCategories = useMemo19(() => {
17678
- if (!isSearchMode) return parentCategories;
17679
- return parentCategories.filter((c) => visibleIds?.has(c.id));
17680
- }, [isSearchMode, parentCategories, visibleIds]);
17681
- let effectiveChildrenMap = childrenMap;
17682
- if (isSearchMode && visibleIds) {
17683
- effectiveChildrenMap = /* @__PURE__ */ new Map();
17684
- for (const [parentId, children] of childrenMap.entries()) {
17685
- effectiveChildrenMap.set(
17686
- parentId,
17687
- children.filter((child) => visibleIds.has(child.id))
17688
- );
17689
- }
17690
- }
17691
- const renderTreeContent = () => /* @__PURE__ */ jsx49("div", { className: "space-y-0.5 overflow-x-hidden", children: effectiveParentCategories.length === 0 ? /* @__PURE__ */ jsxs39("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
17848
+ const renderTreeContent = () => /* @__PURE__ */ jsx49("div", { className: "space-y-0.5 overflow-x-hidden", children: shouldPromptForSearch ? /* @__PURE__ */ jsxs39("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
17849
+ /* @__PURE__ */ jsx49("div", { className: "w-12 h-12 rounded-2xl bg-muted/50 flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx49(Search6, { className: "w-6 h-6 text-muted-foreground/50" }) }),
17850
+ /* @__PURE__ */ jsxs39("span", { className: "text-sm text-muted-foreground", children: [
17851
+ "Type at least ",
17852
+ minSearchLength,
17853
+ " characters to search"
17854
+ ] })
17855
+ ] }) : effectiveParentCategories.length === 0 ? /* @__PURE__ */ jsxs39("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
17692
17856
  /* @__PURE__ */ jsx49("div", { className: "w-12 h-12 rounded-2xl bg-muted/50 flex items-center justify-center mb-3", children: isSearchMode ? /* @__PURE__ */ jsx49(SearchX3, { className: "w-6 h-6 text-muted-foreground/50" }) : /* @__PURE__ */ jsx49(Layers, { className: "w-6 h-6 text-muted-foreground/50" }) }),
17693
17857
  /* @__PURE__ */ jsx49("span", { className: "text-sm text-muted-foreground", children: isSearchMode ? mergedLabels.noResultsText : mergedLabels.emptyText })
17694
- ] }) : effectiveParentCategories.map((cat) => renderCategory(cat)) });
17858
+ ] }) : effectiveParentCategories.map((cat) => renderCategoryRow(cat)) });
17859
+ const renderVirtualTreeContent = () => {
17860
+ if (shouldPromptForSearch) {
17861
+ return /* @__PURE__ */ jsxs39("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
17862
+ /* @__PURE__ */ jsx49("div", { className: "w-12 h-12 rounded-2xl bg-muted/50 flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx49(Search6, { className: "w-6 h-6 text-muted-foreground/50" }) }),
17863
+ /* @__PURE__ */ jsxs39("span", { className: "text-sm text-muted-foreground", children: [
17864
+ "Type at least ",
17865
+ minSearchLength,
17866
+ " characters to search"
17867
+ ] })
17868
+ ] });
17869
+ }
17870
+ if (flattenedRows.length === 0) {
17871
+ return /* @__PURE__ */ jsxs39("div", { className: "flex flex-col items-center justify-center py-8 text-center", children: [
17872
+ /* @__PURE__ */ jsx49("div", { className: "w-12 h-12 rounded-2xl bg-muted/50 flex items-center justify-center mb-3", children: isSearchMode ? /* @__PURE__ */ jsx49(SearchX3, { className: "w-6 h-6 text-muted-foreground/50" }) : /* @__PURE__ */ jsx49(Layers, { className: "w-6 h-6 text-muted-foreground/50" }) }),
17873
+ /* @__PURE__ */ jsx49("span", { className: "text-sm text-muted-foreground", children: isSearchMode ? mergedLabels.noResultsText : mergedLabels.emptyText })
17874
+ ] });
17875
+ }
17876
+ return /* @__PURE__ */ jsx49("div", { className: "relative overflow-x-hidden", style: { height: `${treeVirtualizer.getTotalSize()}px` }, children: virtualRows.map((virtualRow) => {
17877
+ const row = flattenedRows[virtualRow.index];
17878
+ if (!row) return null;
17879
+ return renderCategoryRow(row.category, row.level, virtualRow);
17880
+ }) });
17881
+ };
17695
17882
  const renderLabel = () => label ? /* @__PURE__ */ jsx49("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs39(
17696
17883
  Label,
17697
17884
  {
@@ -17734,8 +17921,10 @@ function CategoryTreeSelect(props) {
17734
17921
  "div",
17735
17922
  {
17736
17923
  id: resolvedId,
17924
+ role: "tree",
17737
17925
  "aria-labelledby": labelId,
17738
17926
  "aria-describedby": describedBy,
17927
+ "aria-multiselectable": singleSelect ? void 0 : true,
17739
17928
  className: cn("rounded-2xl border border-border/60 bg-card/50 backdrop-blur-sm p-3 shadow-sm", disabled && "opacity-50"),
17740
17929
  children: [
17741
17930
  renderSearch(),
@@ -17770,8 +17959,10 @@ function CategoryTreeSelect(props) {
17770
17959
  "div",
17771
17960
  {
17772
17961
  id: resolvedId,
17962
+ role: "tree",
17773
17963
  "aria-labelledby": labelId,
17774
17964
  "aria-describedby": describedBy,
17965
+ "aria-multiselectable": singleSelect ? void 0 : true,
17775
17966
  className: cn("rounded-2xl border border-border/60 bg-card/50 backdrop-blur-sm p-3 shadow-sm", disabled && "opacity-50 pointer-events-none"),
17776
17967
  children: [
17777
17968
  renderSearch(),
@@ -17836,6 +18027,7 @@ function CategoryTreeSelect(props) {
17836
18027
  setIsOpen(nextOpen);
17837
18028
  if (!nextOpen) {
17838
18029
  setQuery("");
18030
+ scrollVirtualTreeToStart();
17839
18031
  }
17840
18032
  };
17841
18033
  let displayText;
@@ -17851,15 +18043,20 @@ function CategoryTreeSelect(props) {
17851
18043
  displayText = mergedLabels.selectedText(selectedCount);
17852
18044
  }
17853
18045
  }
17854
- const dropdownBody = /* @__PURE__ */ jsxs39("div", { className: "flex max-h-80 flex-col overflow-hidden", children: [
18046
+ const dropdownBody = /* @__PURE__ */ jsxs39("div", { className: "flex flex-col overflow-hidden", style: { maxHeight: CATEGORY_TREE_DROPDOWN_MAX_HEIGHT }, children: [
17855
18047
  renderSearch({ sticky: false, className: "border-b border-border/30 p-2 pb-2" }),
17856
18048
  /* @__PURE__ */ jsx49(
17857
18049
  "div",
17858
18050
  {
17859
18051
  ref: dropdownViewportRef,
17860
18052
  id: `${resolvedId}-tree`,
18053
+ role: "tree",
18054
+ "aria-multiselectable": singleSelect ? void 0 : true,
18055
+ "data-os-ignore": virtualized ? "" : void 0,
18056
+ tabIndex: canVirtualize ? 0 : void 0,
18057
+ onKeyDown: handleVirtualTreeKeyDown,
17861
18058
  className: cn("min-h-0 flex-1 overflow-auto overflow-x-hidden p-2 pt-2"),
17862
- children: renderTreeContent()
18059
+ children: canVirtualize ? renderVirtualTreeContent() : renderTreeContent()
17863
18060
  }
17864
18061
  )
17865
18062
  ] });
@@ -17914,6 +18111,10 @@ function CategoryTreeSelect(props) {
17914
18111
  if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
17915
18112
  event.preventDefault();
17916
18113
  handleOpenChange(!isOpen);
18114
+ if (event.key === "ArrowDown" && canVirtualize && flattenedRows.length > 0) {
18115
+ setActiveIndex(0);
18116
+ treeVirtualizer.scrollToIndex(0, { align: "start" });
18117
+ }
17917
18118
  }
17918
18119
  },
17919
18120
  className: cn(
@@ -21549,11 +21750,12 @@ var TableCaption = React52.forwardRef(({ className, ...props }, ref) => /* @__PU
21549
21750
  TableCaption.displayName = "TableCaption";
21550
21751
 
21551
21752
  // src/components/DataTable/DataTable.tsx
21753
+ import { useVirtualizer as useVirtualizer4 } from "@tanstack/react-virtual";
21552
21754
  import React62 from "react";
21553
21755
 
21554
21756
  // src/components/DataTable/components/DataTableBody.tsx
21555
21757
  import React53 from "react";
21556
- import { jsx as jsx63, jsxs as jsxs52 } from "react/jsx-runtime";
21758
+ import { Fragment as Fragment22, jsx as jsx63, jsxs as jsxs52 } from "react/jsx-runtime";
21557
21759
  function DataTableOverflowText({
21558
21760
  text,
21559
21761
  align
@@ -21621,8 +21823,17 @@ function DataTableBodyRows({
21621
21823
  getStickyColumnStyle,
21622
21824
  getStickyCellClass,
21623
21825
  t,
21624
- labels
21826
+ labels,
21827
+ virtualRows,
21828
+ virtualPaddingTop = 0,
21829
+ virtualPaddingBottom = 0,
21830
+ measureVirtualRow
21625
21831
  }) {
21832
+ const rowsToRender = virtualRows ? virtualRows.map((virtualRow) => ({
21833
+ row: displayedData[virtualRow.index],
21834
+ idx: virtualRow.index,
21835
+ virtualRow
21836
+ })).filter((item) => Boolean(item.row)) : displayedData.map((row, idx) => ({ row, idx, virtualRow: void 0 }));
21626
21837
  return /* @__PURE__ */ jsx63(TableBody, { children: loading2 ? /* @__PURE__ */ jsx63(TableRow, { children: /* @__PURE__ */ jsx63(TableCell, { colSpan: leafColumns.length, className: "text-center py-8", children: /* @__PURE__ */ jsxs52("div", { className: "flex items-center justify-center gap-2 text-muted-foreground", children: [
21627
21838
  /* @__PURE__ */ jsxs52("svg", { className: "animate-spin h-4 w-4", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
21628
21839
  /* @__PURE__ */ jsx63("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
@@ -21639,48 +21850,54 @@ function DataTableBodyRows({
21639
21850
  t("loading"),
21640
21851
  "\u2026"
21641
21852
  ] })
21642
- ] }) }) }) : displayedData.length === 0 ? /* @__PURE__ */ jsx63(TableRow, { children: /* @__PURE__ */ jsx63(TableCell, { colSpan: leafColumns.length, className: "text-center py-6 text-muted-foreground", children: labels?.noData || t("noData") }) }) : displayedData.map((row, idx) => {
21643
- const isStripedRow = striped && idx % 2 === 0;
21644
- return /* @__PURE__ */ jsx63(
21645
- TableRow,
21646
- {
21647
- className: cn(densityRowClass, isStripedRow ? "bg-surface-1" : "bg-surface-0"),
21648
- style: {
21649
- contentVisibility: "auto",
21650
- containIntrinsicSize: density === "compact" ? "0 36px" : density === "comfortable" ? "0 56px" : "0 48px"
21853
+ ] }) }) }) : displayedData.length === 0 ? /* @__PURE__ */ jsx63(TableRow, { children: /* @__PURE__ */ jsx63(TableCell, { colSpan: leafColumns.length, className: "text-center py-6 text-muted-foreground", children: labels?.noData || t("noData") }) }) : /* @__PURE__ */ jsxs52(Fragment22, { children: [
21854
+ virtualPaddingTop > 0 && /* @__PURE__ */ jsx63(TableRow, { "aria-hidden": "true", className: "border-0 hover:bg-transparent hover:shadow-none", children: /* @__PURE__ */ jsx63(TableCell, { colSpan: leafColumns.length, className: "p-0", style: { height: virtualPaddingTop } }) }),
21855
+ rowsToRender.map(({ row, idx, virtualRow }) => {
21856
+ const isStripedRow = striped && idx % 2 === 0;
21857
+ return /* @__PURE__ */ jsx63(
21858
+ TableRow,
21859
+ {
21860
+ ref: virtualRow ? measureVirtualRow : void 0,
21861
+ "data-index": virtualRow?.index,
21862
+ className: cn(densityRowClass, isStripedRow ? "bg-surface-1" : "bg-surface-0"),
21863
+ style: {
21864
+ contentVisibility: "auto",
21865
+ containIntrinsicSize: density === "compact" ? "0 36px" : density === "comfortable" ? "0 56px" : "0 48px"
21866
+ },
21867
+ children: leafColumns.map((col, colIdx) => {
21868
+ const value = col.dataIndex ? row[col.dataIndex] : void 0;
21869
+ const prevCol = colIdx > 0 ? leafColumns[colIdx - 1] : null;
21870
+ const isAfterFixedLeft = prevCol?.fixed === "left";
21871
+ const showBorderLeft = columnDividers && colIdx > 0 && !isAfterFixedLeft && !col.fixed;
21872
+ return /* @__PURE__ */ jsx63(
21873
+ TableCell,
21874
+ {
21875
+ "data-underverse-column-key": col.key,
21876
+ style: getStickyColumnStyle(col),
21877
+ className: cn(
21878
+ cellPadding,
21879
+ col.align === "right" && "text-right",
21880
+ col.align === "center" && "text-center",
21881
+ showBorderLeft && "border-l border-border/60",
21882
+ getStickyCellClass(col, isStripedRow)
21883
+ ),
21884
+ children: col.render ? col.render(value, row, idx) : /* @__PURE__ */ jsx63(DataTableOverflowText, { text: String(value ?? ""), align: col.align })
21885
+ },
21886
+ col.key
21887
+ );
21888
+ })
21651
21889
  },
21652
- children: leafColumns.map((col, colIdx) => {
21653
- const value = col.dataIndex ? row[col.dataIndex] : void 0;
21654
- const prevCol = colIdx > 0 ? leafColumns[colIdx - 1] : null;
21655
- const isAfterFixedLeft = prevCol?.fixed === "left";
21656
- const showBorderLeft = columnDividers && colIdx > 0 && !isAfterFixedLeft && !col.fixed;
21657
- return /* @__PURE__ */ jsx63(
21658
- TableCell,
21659
- {
21660
- "data-underverse-column-key": col.key,
21661
- style: getStickyColumnStyle(col),
21662
- className: cn(
21663
- cellPadding,
21664
- col.align === "right" && "text-right",
21665
- col.align === "center" && "text-center",
21666
- showBorderLeft && "border-l border-border/60",
21667
- getStickyCellClass(col, isStripedRow)
21668
- ),
21669
- children: col.render ? col.render(value, row, idx) : /* @__PURE__ */ jsx63(DataTableOverflowText, { text: String(value ?? ""), align: col.align })
21670
- },
21671
- col.key
21672
- );
21673
- })
21674
- },
21675
- getRowKey(row, idx)
21676
- );
21677
- }) });
21890
+ getRowKey(row, idx)
21891
+ );
21892
+ }),
21893
+ virtualPaddingBottom > 0 && /* @__PURE__ */ jsx63(TableRow, { "aria-hidden": "true", className: "border-0 hover:bg-transparent hover:shadow-none", children: /* @__PURE__ */ jsx63(TableCell, { colSpan: leafColumns.length, className: "p-0", style: { height: virtualPaddingBottom } }) })
21894
+ ] }) });
21678
21895
  }
21679
21896
 
21680
21897
  // src/components/DataTable/components/DataTableHeader.tsx
21681
21898
  import React54 from "react";
21682
21899
  import { Filter as FilterIcon } from "lucide-react";
21683
- import { Fragment as Fragment22, jsx as jsx64, jsxs as jsxs53 } from "react/jsx-runtime";
21900
+ import { Fragment as Fragment23, jsx as jsx64, jsxs as jsxs53 } from "react/jsx-runtime";
21684
21901
  function getColumnLabel(title) {
21685
21902
  if (typeof title === "string" || typeof title === "number") {
21686
21903
  return String(title).replace(/\s+/g, " ").trim();
@@ -21896,10 +22113,10 @@ function DataTableHeader({
21896
22113
  isCenterAlign && "justify-center",
21897
22114
  !isRightAlign && !isCenterAlign && "justify-start"
21898
22115
  ),
21899
- children: isRightAlign ? /* @__PURE__ */ jsxs53(Fragment22, { children: [
22116
+ children: isRightAlign ? /* @__PURE__ */ jsxs53(Fragment23, { children: [
21900
22117
  filterContent,
21901
22118
  titleContent
21902
- ] }) : /* @__PURE__ */ jsxs53(Fragment22, { children: [
22119
+ ] }) : /* @__PURE__ */ jsxs53(Fragment23, { children: [
21903
22120
  titleContent,
21904
22121
  filterContent
21905
22122
  ] })
@@ -21920,7 +22137,7 @@ function DataTableHeader({
21920
22137
  t
21921
22138
  ]
21922
22139
  );
21923
- return /* @__PURE__ */ jsx64(Fragment22, { children: headerRows.map((row, rowIndex) => /* @__PURE__ */ jsx64(TableRow, { children: row.map((headerCell, cellIndex) => {
22140
+ return /* @__PURE__ */ jsx64(Fragment23, { children: headerRows.map((row, rowIndex) => /* @__PURE__ */ jsx64(TableRow, { children: row.map((headerCell, cellIndex) => {
21924
22141
  const { column: col, colSpan, rowSpan, isLeaf } = headerCell;
21925
22142
  const prevCell = cellIndex > 0 ? row[cellIndex - 1] : null;
21926
22143
  const prevCol = prevCell?.column;
@@ -22741,6 +22958,9 @@ function DataTable({
22741
22958
  horizontalMode = "auto",
22742
22959
  overflowHidden = true,
22743
22960
  useOverlayScrollbar = false,
22961
+ virtualizedRows = false,
22962
+ estimatedRowHeight,
22963
+ overscan = 8,
22744
22964
  enableHeaderAutoFit = true,
22745
22965
  labels
22746
22966
  }) {
@@ -22796,6 +23016,7 @@ function DataTable({
22796
23016
  console.warn("[DataTable] `rowKey` should be provided when using sort/filter/pagination to keep row identity stable.");
22797
23017
  }, [columns, isServerMode, pageSizeOptions, rowKey]);
22798
23018
  const densityRowClass = density === "compact" ? "h-9" : density === "comfortable" ? "h-14" : "h-12";
23019
+ const defaultEstimatedRowHeight = density === "compact" ? 36 : density === "comfortable" ? 56 : 48;
22799
23020
  const cellPadding = density === "compact" ? "py-1.5 px-3" : density === "comfortable" ? "py-3 px-4" : "py-2.5 px-4";
22800
23021
  const headerTitleClass = size === "sm" ? "text-xs" : size === "lg" ? "text-[15px]" : "text-sm";
22801
23022
  const headerMinHeightClass = size === "sm" ? "min-h-9" : size === "lg" ? "min-h-11" : "min-h-10";
@@ -22823,8 +23044,23 @@ function DataTable({
22823
23044
  };
22824
23045
  const viewportRef = React62.useRef(null);
22825
23046
  const tableRef = React62.useRef(null);
23047
+ const canVirtualizeRows = virtualizedRows && !loading2 && displayedData.length > 0;
23048
+ const rowVirtualizer = useVirtualizer4({
23049
+ count: canVirtualizeRows ? displayedData.length : 0,
23050
+ getScrollElement: () => viewportRef.current,
23051
+ estimateSize: () => estimatedRowHeight ?? defaultEstimatedRowHeight,
23052
+ initialRect: {
23053
+ width: 0,
23054
+ height: typeof maxHeight === "number" ? maxHeight : 500
23055
+ },
23056
+ overscan,
23057
+ enabled: canVirtualizeRows
23058
+ });
23059
+ const virtualRows = canVirtualizeRows ? rowVirtualizer.getVirtualItems() : [];
23060
+ const virtualPaddingTop = canVirtualizeRows && virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
23061
+ const virtualPaddingBottom = canVirtualizeRows && virtualRows.length > 0 ? Math.max(0, rowVirtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0)) : 0;
22826
23062
  useOverlayScrollbarTarget(viewportRef, {
22827
- enabled: useOverlayScrollbar,
23063
+ enabled: useOverlayScrollbar && !canVirtualizeRows,
22828
23064
  overflowX: overlayOverflowX
22829
23065
  });
22830
23066
  const autoFitColumn = React62.useCallback((columnKey) => {
@@ -22888,6 +23124,7 @@ function DataTable({
22888
23124
  "div",
22889
23125
  {
22890
23126
  ref: viewportRef,
23127
+ "data-os-ignore": canVirtualizeRows ? "" : void 0,
22891
23128
  className: cn("w-full", viewportOverflowXClass, stickyHeader && "overflow-y-auto"),
22892
23129
  style: stickyHeader ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
22893
23130
  children: /* @__PURE__ */ jsxs56(
@@ -22939,7 +23176,11 @@ function DataTable({
22939
23176
  getStickyColumnStyle,
22940
23177
  getStickyCellClass,
22941
23178
  t,
22942
- labels
23179
+ labels,
23180
+ virtualRows: canVirtualizeRows ? virtualRows : void 0,
23181
+ virtualPaddingTop: canVirtualizeRows ? virtualPaddingTop : void 0,
23182
+ virtualPaddingBottom: canVirtualizeRows ? virtualPaddingBottom : void 0,
23183
+ measureVirtualRow: canVirtualizeRows ? rowVirtualizer.measureElement : void 0
22943
23184
  }
22944
23185
  )
22945
23186
  ]
@@ -23204,7 +23445,7 @@ function AccessDenied({
23204
23445
  import { Moon as Moon2, Sun as Sun2, Monitor } from "lucide-react";
23205
23446
  import { useRef as useRef25, useState as useState40 } from "react";
23206
23447
  import { createPortal as createPortal6 } from "react-dom";
23207
- import { Fragment as Fragment23, jsx as jsx71, jsxs as jsxs60 } from "react/jsx-runtime";
23448
+ import { Fragment as Fragment24, jsx as jsx71, jsxs as jsxs60 } from "react/jsx-runtime";
23208
23449
  function ThemeToggleHeadless({
23209
23450
  theme,
23210
23451
  onChange,
@@ -23254,7 +23495,7 @@ function ThemeToggleHeadless({
23254
23495
  children: /* @__PURE__ */ jsx71(CurrentIcon, { className: "h-5 w-5" })
23255
23496
  }
23256
23497
  ),
23257
- isOpen && /* @__PURE__ */ jsxs60(Fragment23, { children: [
23498
+ isOpen && /* @__PURE__ */ jsxs60(Fragment24, { children: [
23258
23499
  typeof window !== "undefined" && createPortal6(/* @__PURE__ */ jsx71("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23259
23500
  typeof window !== "undefined" && dropdownPosition && createPortal6(
23260
23501
  /* @__PURE__ */ jsx71(
@@ -23306,7 +23547,7 @@ function ThemeToggleHeadless({
23306
23547
  import { useRef as useRef26, useState as useState41 } from "react";
23307
23548
  import { createPortal as createPortal7 } from "react-dom";
23308
23549
  import { Globe } from "lucide-react";
23309
- import { Fragment as Fragment24, jsx as jsx72, jsxs as jsxs61 } from "react/jsx-runtime";
23550
+ import { Fragment as Fragment25, jsx as jsx72, jsxs as jsxs61 } from "react/jsx-runtime";
23310
23551
  function LanguageSwitcherHeadless({
23311
23552
  locales,
23312
23553
  currentLocale,
@@ -23351,7 +23592,7 @@ function LanguageSwitcherHeadless({
23351
23592
  children: /* @__PURE__ */ jsx72(Globe, { className: "h-5 w-5" })
23352
23593
  }
23353
23594
  ),
23354
- isOpen && /* @__PURE__ */ jsxs61(Fragment24, { children: [
23595
+ isOpen && /* @__PURE__ */ jsxs61(Fragment25, { children: [
23355
23596
  typeof window !== "undefined" && createPortal7(/* @__PURE__ */ jsx72("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23356
23597
  typeof window !== "undefined" && dropdownPosition && createPortal7(
23357
23598
  /* @__PURE__ */ jsx72(
@@ -25255,7 +25496,7 @@ function getDefaultLetterSpacings() {
25255
25496
  }
25256
25497
 
25257
25498
  // src/components/UEditor/toolbar.tsx
25258
- import { Fragment as Fragment25, jsx as jsx80, jsxs as jsxs67 } from "react/jsx-runtime";
25499
+ import { Fragment as Fragment26, jsx as jsx80, jsxs as jsxs67 } from "react/jsx-runtime";
25259
25500
  function fileToDataUrl2(file) {
25260
25501
  return new Promise((resolve, reject) => {
25261
25502
  const reader = new FileReader();
@@ -25897,7 +26138,7 @@ var EditorToolbar = ({
25897
26138
  },
25898
26139
  onCancel: () => setShowImageInput(false)
25899
26140
  }
25900
- ) : /* @__PURE__ */ jsxs67(Fragment25, { children: [
26141
+ ) : /* @__PURE__ */ jsxs67(Fragment26, { children: [
25901
26142
  /* @__PURE__ */ jsx80(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true) }),
25902
26143
  /* @__PURE__ */ jsx80(
25903
26144
  DropdownMenuItem,
@@ -26942,7 +27183,7 @@ function findDiffEnd(a, b, posA, posB) {
26942
27183
  posB -= size;
26943
27184
  }
26944
27185
  }
26945
- var Fragment26 = class _Fragment {
27186
+ var Fragment27 = class _Fragment {
26946
27187
  /**
26947
27188
  @internal
26948
27189
  */
@@ -27234,7 +27475,7 @@ var Fragment26 = class _Fragment {
27234
27475
  throw new RangeError("Can not convert " + nodes + " to a Fragment" + (nodes.nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : ""));
27235
27476
  }
27236
27477
  };
27237
- Fragment26.empty = new Fragment26([], 0);
27478
+ Fragment27.empty = new Fragment27([], 0);
27238
27479
  var found = { index: 0, offset: 0 };
27239
27480
  function retIndex(index, offset) {
27240
27481
  found.index = index;
@@ -27459,7 +27700,7 @@ var Slice = class _Slice {
27459
27700
  let openStart = json.openStart || 0, openEnd = json.openEnd || 0;
27460
27701
  if (typeof openStart != "number" || typeof openEnd != "number")
27461
27702
  throw new RangeError("Invalid input for Slice.fromJSON");
27462
- return new _Slice(Fragment26.fromJSON(schema, json.content), openStart, openEnd);
27703
+ return new _Slice(Fragment27.fromJSON(schema, json.content), openStart, openEnd);
27463
27704
  }
27464
27705
  /**
27465
27706
  Create a slice from a fragment by taking the maximum possible
@@ -27474,7 +27715,7 @@ var Slice = class _Slice {
27474
27715
  return new _Slice(fragment, openStart, openEnd);
27475
27716
  }
27476
27717
  };
27477
- Slice.empty = new Slice(Fragment26.empty, 0, 0);
27718
+ Slice.empty = new Slice(Fragment27.empty, 0, 0);
27478
27719
  function removeRange(content, from, to) {
27479
27720
  let { index, offset } = content.findIndex(from), child = content.maybeChild(index);
27480
27721
  let { index: indexTo, offset: offsetTo } = content.findIndex(to);
@@ -27572,7 +27813,7 @@ function replaceThreeWay($from, $start, $end, $to, depth) {
27572
27813
  addNode(close(openEnd, replaceTwoWay($end, $to, depth + 1)), content);
27573
27814
  }
27574
27815
  addRange($to, null, depth, content);
27575
- return new Fragment26(content);
27816
+ return new Fragment27(content);
27576
27817
  }
27577
27818
  function replaceTwoWay($from, $to, depth) {
27578
27819
  let content = [];
@@ -27582,13 +27823,13 @@ function replaceTwoWay($from, $to, depth) {
27582
27823
  addNode(close(type, replaceTwoWay($from, $to, depth + 1)), content);
27583
27824
  }
27584
27825
  addRange($to, null, depth, content);
27585
- return new Fragment26(content);
27826
+ return new Fragment27(content);
27586
27827
  }
27587
27828
  function prepareSliceForReplace(slice, $along) {
27588
27829
  let extra = $along.depth - slice.openStart, parent = $along.node(extra);
27589
27830
  let node = parent.copy(slice.content);
27590
27831
  for (let i = extra - 1; i >= 0; i--)
27591
- node = $along.node(i).copy(Fragment26.from(node));
27832
+ node = $along.node(i).copy(Fragment27.from(node));
27592
27833
  return {
27593
27834
  start: node.resolveNoCache(slice.openStart + extra),
27594
27835
  end: node.resolveNoCache(node.content.size - slice.openEnd - extra)
@@ -27927,7 +28168,7 @@ var Node2 = class _Node {
27927
28168
  this.type = type;
27928
28169
  this.attrs = attrs;
27929
28170
  this.marks = marks;
27930
- this.content = content || Fragment26.empty;
28171
+ this.content = content || Fragment27.empty;
27931
28172
  }
27932
28173
  /**
27933
28174
  The array of this node's child nodes.
@@ -28232,7 +28473,7 @@ var Node2 = class _Node {
28232
28473
  can optionally pass `start` and `end` indices into the
28233
28474
  replacement fragment.
28234
28475
  */
28235
- canReplace(from, to, replacement = Fragment26.empty, start = 0, end = replacement.childCount) {
28476
+ canReplace(from, to, replacement = Fragment27.empty, start = 0, end = replacement.childCount) {
28236
28477
  let one = this.contentMatchAt(from).matchFragment(replacement, start, end);
28237
28478
  let two = one && one.matchFragment(this.content, to);
28238
28479
  if (!two || !two.validEnd)
@@ -28314,7 +28555,7 @@ var Node2 = class _Node {
28314
28555
  throw new RangeError("Invalid text node in JSON");
28315
28556
  return schema.text(json.text, marks);
28316
28557
  }
28317
- let content = Fragment26.fromJSON(schema, json.content);
28558
+ let content = Fragment27.fromJSON(schema, json.content);
28318
28559
  let node = schema.nodeType(json.type).create(json.attrs, content, marks);
28319
28560
  node.type.checkAttrs(node.attrs);
28320
28561
  return node;
@@ -28410,7 +28651,7 @@ var ContentMatch = class _ContentMatch {
28410
28651
  function search(match, types) {
28411
28652
  let finished = match.matchFragment(after, startIndex);
28412
28653
  if (finished && (!toEnd || finished.validEnd))
28413
- return Fragment26.from(types.map((tp) => tp.createAndFill()));
28654
+ return Fragment27.from(types.map((tp) => tp.createAndFill()));
28414
28655
  for (let i = 0; i < match.next.length; i++) {
28415
28656
  let { type, next } = match.next[i];
28416
28657
  if (!(type.isText || type.hasRequiredAttrs()) && seen.indexOf(next) == -1) {
@@ -28978,7 +29219,7 @@ function mapFragment(fragment, f, parent) {
28978
29219
  child = f(child, parent, i);
28979
29220
  mapped.push(child);
28980
29221
  }
28981
- return Fragment26.fromArray(mapped);
29222
+ return Fragment27.fromArray(mapped);
28982
29223
  }
28983
29224
  var AddMarkStep = class _AddMarkStep extends Step {
28984
29225
  /**
@@ -29095,7 +29336,7 @@ var AddNodeMarkStep = class _AddNodeMarkStep extends Step {
29095
29336
  if (!node)
29096
29337
  return StepResult.fail("No node at mark step's position");
29097
29338
  let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
29098
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29339
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29099
29340
  }
29100
29341
  invert(doc) {
29101
29342
  let node = doc.nodeAt(this.pos);
@@ -29141,7 +29382,7 @@ var RemoveNodeMarkStep = class _RemoveNodeMarkStep extends Step {
29141
29382
  if (!node)
29142
29383
  return StepResult.fail("No node at mark step's position");
29143
29384
  let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
29144
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29385
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29145
29386
  }
29146
29387
  invert(doc) {
29147
29388
  let node = doc.nodeAt(this.pos);
@@ -29342,7 +29583,7 @@ var AttrStep = class _AttrStep extends Step {
29342
29583
  attrs[name] = node.attrs[name];
29343
29584
  attrs[this.attr] = this.value;
29344
29585
  let updated = node.type.create(attrs, null, node.marks);
29345
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29586
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29346
29587
  }
29347
29588
  getMap() {
29348
29589
  return StepMap.empty;
@@ -29726,7 +29967,7 @@ var NodeSelection2 = class _NodeSelection extends Selection {
29726
29967
  return new _NodeSelection($pos);
29727
29968
  }
29728
29969
  content() {
29729
- return new Slice(Fragment26.from(this.node), 0, 0);
29970
+ return new Slice(Fragment27.from(this.node), 0, 0);
29730
29971
  }
29731
29972
  eq(other) {
29732
29973
  return other instanceof _NodeSelection && other.anchor == this.anchor;
@@ -30454,10 +30695,10 @@ var CellSelection = class CellSelection2 extends Selection {
30454
30695
  }
30455
30696
  rowContent.push(cell);
30456
30697
  }
30457
- rows.push(table.child(row).copy(Fragment26.from(rowContent)));
30698
+ rows.push(table.child(row).copy(Fragment27.from(rowContent)));
30458
30699
  }
30459
30700
  const fragment = this.isColSelection() && this.isRowSelection() ? table : rows;
30460
- return new Slice(Fragment26.from(fragment), 1, 1);
30701
+ return new Slice(Fragment27.from(fragment), 1, 1);
30461
30702
  }
30462
30703
  replace(tr, content = Slice.empty) {
30463
30704
  const mapFrom = tr.steps.length, ranges = this.ranges;
@@ -30469,7 +30710,7 @@ var CellSelection = class CellSelection2 extends Selection {
30469
30710
  if (sel) tr.setSelection(sel);
30470
30711
  }
30471
30712
  replaceWith(tr, node) {
30472
- this.replace(tr, new Slice(Fragment26.from(node), 0, 0));
30713
+ this.replace(tr, new Slice(Fragment27.from(node), 0, 0));
30473
30714
  }
30474
30715
  forEachCell(f) {
30475
30716
  const table = this.$anchorCell.node(-1);
@@ -31166,7 +31407,7 @@ function getRelativeSelectedCellsMetrics(surface) {
31166
31407
  }
31167
31408
 
31168
31409
  // src/components/UEditor/table-controls.tsx
31169
- import { Fragment as Fragment27, jsx as jsx82, jsxs as jsxs70 } from "react/jsx-runtime";
31410
+ import { Fragment as Fragment28, jsx as jsx82, jsxs as jsxs70 } from "react/jsx-runtime";
31170
31411
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
31171
31412
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
31172
31413
  var MENU_HOVER_PADDING = 18;
@@ -31803,7 +32044,7 @@ function TableControls({ editor, containerRef }) {
31803
32044
  const expandPreviewWidth = dragPreview?.kind === "add-column" ? layout.tableWidth + dragPreview.previewCols * layout.avgColumnWidth : layout.tableWidth;
31804
32045
  const expandPreviewHeight = dragPreview?.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
31805
32046
  const dragStatusText = dragPreview?.kind === "row" ? `${t("tableMenu.dragRow")} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview?.kind === "column" ? `${t("tableMenu.dragColumn")} ${dragPreview.originIndex + 1} -> ${dragPreview.targetIndex + 1}` : dragPreview?.kind === "add-row" ? `+${dragPreview.previewRows}R` : dragPreview?.kind === "add-column" ? `+${dragPreview.previewCols}C` : null;
31806
- return /* @__PURE__ */ jsxs70(Fragment27, { children: [
32047
+ return /* @__PURE__ */ jsxs70(Fragment28, { children: [
31807
32048
  layout.rowHandles.map((rowHandle) => {
31808
32049
  const menuKey = getRowMenuKey(rowHandle.index);
31809
32050
  const visible = controlsVisible || hoverState.rowHandleIndex === rowHandle.index || openMenuKey === menuKey;
@@ -32077,7 +32318,7 @@ function TableControls({ editor, containerRef }) {
32077
32318
  )
32078
32319
  }
32079
32320
  ),
32080
- dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32321
+ dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32081
32322
  /* @__PURE__ */ jsx82(
32082
32323
  "div",
32083
32324
  {
@@ -32105,7 +32346,7 @@ function TableControls({ editor, containerRef }) {
32105
32346
  }
32106
32347
  )
32107
32348
  ] }),
32108
- dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32349
+ dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32109
32350
  /* @__PURE__ */ jsx82(
32110
32351
  "div",
32111
32352
  {
@@ -32133,7 +32374,7 @@ function TableControls({ editor, containerRef }) {
32133
32374
  }
32134
32375
  )
32135
32376
  ] }),
32136
- (dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column") && /* @__PURE__ */ jsx82(Fragment27, { children: /* @__PURE__ */ jsx82(
32377
+ (dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column") && /* @__PURE__ */ jsx82(Fragment28, { children: /* @__PURE__ */ jsx82(
32137
32378
  "div",
32138
32379
  {
32139
32380
  "aria-hidden": "true",