@underverse-ui/underverse 1.0.113 → 1.0.116

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
@@ -17290,6 +17290,7 @@ function OverlayControls({
17290
17290
 
17291
17291
  // src/components/CategoryTreeSelect.tsx
17292
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";
17293
17294
  import { ChevronRight as ChevronRight6, ChevronDown as ChevronDown5, FolderTree, Layers, Search as Search6, SearchX as SearchX3, X as X14 } from "lucide-react";
17294
17295
  import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
17295
17296
  var defaultLabels = {
@@ -17303,6 +17304,7 @@ var TREE_NODE_INDENT_REM = 1;
17303
17304
  var TREE_BRANCH_OFFSET_CLASS = "ml-1.5 pl-1.5";
17304
17305
  var TREE_NODE_GAP_CLASS = "gap-1.5";
17305
17306
  var TREE_EXPANDER_PLACEHOLDER_CLASS = "w-5";
17307
+ var CATEGORY_TREE_DROPDOWN_MAX_HEIGHT = 320;
17306
17308
  function getAncestorPathIds(categories, targetId) {
17307
17309
  const byId = new Map(categories.map((category) => [category.id, category]));
17308
17310
  const expanded = /* @__PURE__ */ new Set();
@@ -17381,6 +17383,24 @@ function pruneAncestorSelection(categories, childrenMap, selected, fromCategoryI
17381
17383
  function toCategoryOrderSelection(categories, selected) {
17382
17384
  return categories.map((category) => category.id).filter((categoryId) => selected.has(categoryId));
17383
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
+ }
17384
17404
  function CategoryTreeSelect(props) {
17385
17405
  const tv = useSmartTranslations("ValidationInput");
17386
17406
  const {
@@ -17409,6 +17429,15 @@ function CategoryTreeSelect(props) {
17409
17429
  className,
17410
17430
  useOverlayScrollbar = false,
17411
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,
17412
17441
  singleSelect = false
17413
17442
  } = props;
17414
17443
  const [isOpen, setIsOpen] = useState31(false);
@@ -17416,10 +17445,13 @@ function CategoryTreeSelect(props) {
17416
17445
  () => getInitialExpandedNodes(categories, { defaultExpanded, defaultExpandedIds, expandToId, viewOnly, inline })
17417
17446
  );
17418
17447
  const [query, setQuery] = useState31("");
17448
+ const [activeIndex, setActiveIndex] = useState31(null);
17419
17449
  const [localRequiredError, setLocalRequiredError] = useState31();
17420
17450
  const searchInputRef = useRef20(null);
17421
17451
  const dropdownViewportRef = useRef20(null);
17422
- useOverlayScrollbarTarget(dropdownViewportRef, { enabled: useOverlayScrollbar });
17452
+ useOverlayScrollbarTarget(dropdownViewportRef, {
17453
+ enabled: isOpen && useOverlayScrollbar && !virtualized && !inline && !viewOnly
17454
+ });
17423
17455
  const autoId = useId11();
17424
17456
  const resolvedId = id ? String(id) : `category-tree-select-${autoId}`;
17425
17457
  const labelId = label ? `${resolvedId}-label` : void 0;
@@ -17439,7 +17471,7 @@ function CategoryTreeSelect(props) {
17439
17471
  const parentCategories2 = [];
17440
17472
  for (const cat of categories) byId2.set(cat.id, cat);
17441
17473
  for (const cat of categories) {
17442
- if (cat.parent_id == null) {
17474
+ if (cat.parent_id == null || !byId2.has(cat.parent_id)) {
17443
17475
  parentCategories2.push(cat);
17444
17476
  continue;
17445
17477
  }
@@ -17448,12 +17480,19 @@ function CategoryTreeSelect(props) {
17448
17480
  }
17449
17481
  return { parentCategories: parentCategories2, childrenMap: childrenMap2, byId: byId2 };
17450
17482
  }, [categories]);
17451
- const isSearchEnabled = useMemo19(() => enableSearch ?? categories.length > 10, [enableSearch, categories.length]);
17452
- 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);
17453
17491
  const isSearchMode = isSearchEnabled && normalizedQuery.length > 0;
17492
+ const shouldAutoExpandSearchResults = searchMode === "auto" && isSearchMode;
17454
17493
  const effectiveExpandedNodes = useMemo19(() => getExpandedNodesState(expandedIds, expandedNodes), [expandedIds, expandedNodes]);
17455
17494
  const visibleIds = useMemo19(() => {
17456
- if (!isSearchMode) return null;
17495
+ if (shouldPromptForSearch || !isSearchMode || searchMode === "manual") return null;
17457
17496
  const matches = categories.filter((c) => c.name.toLowerCase().includes(normalizedQuery));
17458
17497
  if (matches.length === 0) return /* @__PURE__ */ new Set();
17459
17498
  const visible = /* @__PURE__ */ new Set();
@@ -17490,7 +17529,14 @@ function CategoryTreeSelect(props) {
17490
17529
  addDescendants(m.id);
17491
17530
  }
17492
17531
  return visible;
17493
- }, [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]);
17494
17540
  useEffect25(() => {
17495
17541
  if (!isOpen) return;
17496
17542
  if (!isSearchEnabled) return;
@@ -17503,7 +17549,7 @@ function CategoryTreeSelect(props) {
17503
17549
  }
17504
17550
  }, [disabled, required, valueArray.length]);
17505
17551
  const toggleExpand = (id2) => {
17506
- if (isSearchMode) return;
17552
+ if (shouldAutoExpandSearchResults) return;
17507
17553
  const newExpanded = new Set(effectiveExpandedNodes);
17508
17554
  if (newExpanded.has(id2)) {
17509
17555
  newExpanded.delete(id2);
@@ -17554,27 +17600,137 @@ function CategoryTreeSelect(props) {
17554
17600
  onChange(toCategoryOrderSelection(categories, newSelected));
17555
17601
  }
17556
17602
  };
17557
- 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) => {
17558
17699
  const children = effectiveChildrenMap.get(category.id) || [];
17559
17700
  const hasChildren = children.length > 0;
17560
- const isExpanded = hasChildren && (isSearchMode || effectiveExpandedNodes.has(category.id));
17701
+ const isExpanded = hasChildren && (shouldAutoExpandSearchResults || effectiveExpandedNodes.has(category.id));
17561
17702
  const isSelected = selectedIds.has(category.id);
17562
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;
17563
17712
  return /* @__PURE__ */ jsxs39(
17564
17713
  "div",
17565
17714
  {
17566
- className: "min-w-0 animate-in fade-in-50 duration-200 [content-visibility:auto] [contain-intrinsic-size:44px]",
17567
- 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 },
17568
17719
  children: [
17569
17720
  /* @__PURE__ */ jsxs39(
17570
17721
  "div",
17571
17722
  {
17723
+ role: "treeitem",
17724
+ "aria-level": level + 1,
17725
+ "aria-expanded": hasChildren ? isExpanded : void 0,
17726
+ "aria-selected": viewOnly ? void 0 : isSelected,
17572
17727
  onClick: () => !viewOnly && handleSelect(category.id, category),
17573
17728
  className: cn(
17574
17729
  "relative flex min-w-0 items-center px-3 py-2.5 min-h-11 transition-all duration-200 rounded-3xl",
17575
17730
  TREE_NODE_GAP_CLASS,
17576
17731
  !viewOnly && (isSelectable ? "cursor-pointer" : "cursor-default"),
17577
17732
  isSelectable && !isSelected && "hover:bg-accent/50",
17733
+ canVirtualize && isActive && "bg-accent/50",
17578
17734
  // Selected state - đồng bộ cho tất cả
17579
17735
  !viewOnly && isSelected && "bg-accent/40"
17580
17736
  ),
@@ -17584,6 +17740,7 @@ function CategoryTreeSelect(props) {
17584
17740
  "button",
17585
17741
  {
17586
17742
  type: "button",
17743
+ "aria-label": isExpanded ? `Collapse ${category.name}` : `Expand ${category.name}`,
17587
17744
  onClick: (e) => {
17588
17745
  e.stopPropagation();
17589
17746
  toggleExpand(category.id);
@@ -17593,9 +17750,9 @@ function CategoryTreeSelect(props) {
17593
17750
  "hover:scale-110 active:scale-95",
17594
17751
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50",
17595
17752
  isExpanded && "text-primary",
17596
- 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"
17597
17754
  ),
17598
- disabled: isSearchMode,
17755
+ disabled: shouldAutoExpandSearchResults,
17599
17756
  children: /* @__PURE__ */ jsx49("div", { className: cn("transition-transform duration-200", isExpanded && "rotate-90"), children: /* @__PURE__ */ jsx49(ChevronRight6, { className: "w-4 h-4" }) })
17600
17757
  }
17601
17758
  ) : /* @__PURE__ */ jsx49("span", { className: TREE_EXPANDER_PLACEHOLDER_CLASS }),
@@ -17626,15 +17783,16 @@ function CategoryTreeSelect(props) {
17626
17783
  ]
17627
17784
  }
17628
17785
  ),
17629
- hasChildren && isExpanded && /* @__PURE__ */ jsx49(
17786
+ !virtualItem && hasChildren && isExpanded && /* @__PURE__ */ jsx49(
17630
17787
  "div",
17631
17788
  {
17789
+ role: "group",
17632
17790
  className: cn(
17633
17791
  TREE_BRANCH_OFFSET_CLASS,
17634
17792
  "border-l-2 border-dashed border-border/50",
17635
17793
  "animate-in slide-in-from-top-2 fade-in-50 duration-200"
17636
17794
  ),
17637
- children: children.map((child) => renderCategory(child, level + 1))
17795
+ children: children.map((child) => renderCategoryRow(child, level + 1))
17638
17796
  }
17639
17797
  )
17640
17798
  ]
@@ -17651,7 +17809,11 @@ function CategoryTreeSelect(props) {
17651
17809
  {
17652
17810
  ref: searchInputRef,
17653
17811
  value: query,
17654
- onChange: (e) => setQuery(e.target.value),
17812
+ onChange: (e) => {
17813
+ setQuery(e.target.value);
17814
+ scrollVirtualTreeToStart();
17815
+ },
17816
+ onKeyDown: handleVirtualTreeKeyDown,
17655
17817
  placeholder: mergedLabels.searchPlaceholder,
17656
17818
  className: cn(
17657
17819
  "peer w-full rounded-full bg-background/90 py-2.5 pl-10 pr-10 text-sm shadow-sm",
@@ -17668,6 +17830,7 @@ function CategoryTreeSelect(props) {
17668
17830
  type: "button",
17669
17831
  onClick: () => {
17670
17832
  setQuery("");
17833
+ scrollVirtualTreeToStart();
17671
17834
  searchInputRef.current?.focus();
17672
17835
  },
17673
17836
  className: cn(
@@ -17682,24 +17845,40 @@ function CategoryTreeSelect(props) {
17682
17845
  )
17683
17846
  ] }) });
17684
17847
  };
17685
- const effectiveParentCategories = useMemo19(() => {
17686
- if (!isSearchMode) return parentCategories;
17687
- return parentCategories.filter((c) => visibleIds?.has(c.id));
17688
- }, [isSearchMode, parentCategories, visibleIds]);
17689
- let effectiveChildrenMap = childrenMap;
17690
- if (isSearchMode && visibleIds) {
17691
- effectiveChildrenMap = /* @__PURE__ */ new Map();
17692
- for (const [parentId, children] of childrenMap.entries()) {
17693
- effectiveChildrenMap.set(
17694
- parentId,
17695
- children.filter((child) => visibleIds.has(child.id))
17696
- );
17697
- }
17698
- }
17699
- 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: [
17700
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" }) }),
17701
17857
  /* @__PURE__ */ jsx49("span", { className: "text-sm text-muted-foreground", children: isSearchMode ? mergedLabels.noResultsText : mergedLabels.emptyText })
17702
- ] }) : 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
+ };
17703
17882
  const renderLabel = () => label ? /* @__PURE__ */ jsx49("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs39(
17704
17883
  Label,
17705
17884
  {
@@ -17742,8 +17921,10 @@ function CategoryTreeSelect(props) {
17742
17921
  "div",
17743
17922
  {
17744
17923
  id: resolvedId,
17924
+ role: "tree",
17745
17925
  "aria-labelledby": labelId,
17746
17926
  "aria-describedby": describedBy,
17927
+ "aria-multiselectable": singleSelect ? void 0 : true,
17747
17928
  className: cn("rounded-2xl border border-border/60 bg-card/50 backdrop-blur-sm p-3 shadow-sm", disabled && "opacity-50"),
17748
17929
  children: [
17749
17930
  renderSearch(),
@@ -17778,8 +17959,10 @@ function CategoryTreeSelect(props) {
17778
17959
  "div",
17779
17960
  {
17780
17961
  id: resolvedId,
17962
+ role: "tree",
17781
17963
  "aria-labelledby": labelId,
17782
17964
  "aria-describedby": describedBy,
17965
+ "aria-multiselectable": singleSelect ? void 0 : true,
17783
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"),
17784
17967
  children: [
17785
17968
  renderSearch(),
@@ -17844,6 +18027,7 @@ function CategoryTreeSelect(props) {
17844
18027
  setIsOpen(nextOpen);
17845
18028
  if (!nextOpen) {
17846
18029
  setQuery("");
18030
+ scrollVirtualTreeToStart();
17847
18031
  }
17848
18032
  };
17849
18033
  let displayText;
@@ -17859,15 +18043,20 @@ function CategoryTreeSelect(props) {
17859
18043
  displayText = mergedLabels.selectedText(selectedCount);
17860
18044
  }
17861
18045
  }
17862
- 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: [
17863
18047
  renderSearch({ sticky: false, className: "border-b border-border/30 p-2 pb-2" }),
17864
18048
  /* @__PURE__ */ jsx49(
17865
18049
  "div",
17866
18050
  {
17867
18051
  ref: dropdownViewportRef,
17868
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,
17869
18058
  className: cn("min-h-0 flex-1 overflow-auto overflow-x-hidden p-2 pt-2"),
17870
- children: renderTreeContent()
18059
+ children: canVirtualize ? renderVirtualTreeContent() : renderTreeContent()
17871
18060
  }
17872
18061
  )
17873
18062
  ] });
@@ -17922,6 +18111,10 @@ function CategoryTreeSelect(props) {
17922
18111
  if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
17923
18112
  event.preventDefault();
17924
18113
  handleOpenChange(!isOpen);
18114
+ if (event.key === "ArrowDown" && canVirtualize && flattenedRows.length > 0) {
18115
+ setActiveIndex(0);
18116
+ treeVirtualizer.scrollToIndex(0, { align: "start" });
18117
+ }
17925
18118
  }
17926
18119
  },
17927
18120
  className: cn(
@@ -21557,11 +21750,12 @@ var TableCaption = React52.forwardRef(({ className, ...props }, ref) => /* @__PU
21557
21750
  TableCaption.displayName = "TableCaption";
21558
21751
 
21559
21752
  // src/components/DataTable/DataTable.tsx
21753
+ import { useVirtualizer as useVirtualizer4 } from "@tanstack/react-virtual";
21560
21754
  import React62 from "react";
21561
21755
 
21562
21756
  // src/components/DataTable/components/DataTableBody.tsx
21563
21757
  import React53 from "react";
21564
- 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";
21565
21759
  function DataTableOverflowText({
21566
21760
  text,
21567
21761
  align
@@ -21629,8 +21823,17 @@ function DataTableBodyRows({
21629
21823
  getStickyColumnStyle,
21630
21824
  getStickyCellClass,
21631
21825
  t,
21632
- labels
21826
+ labels,
21827
+ virtualRows,
21828
+ virtualPaddingTop = 0,
21829
+ virtualPaddingBottom = 0,
21830
+ measureVirtualRow
21633
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 }));
21634
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: [
21635
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: [
21636
21839
  /* @__PURE__ */ jsx63("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
@@ -21647,48 +21850,54 @@ function DataTableBodyRows({
21647
21850
  t("loading"),
21648
21851
  "\u2026"
21649
21852
  ] })
21650
- ] }) }) }) : 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) => {
21651
- const isStripedRow = striped && idx % 2 === 0;
21652
- return /* @__PURE__ */ jsx63(
21653
- TableRow,
21654
- {
21655
- className: cn(densityRowClass, isStripedRow ? "bg-surface-1" : "bg-surface-0"),
21656
- style: {
21657
- contentVisibility: "auto",
21658
- 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
+ })
21659
21889
  },
21660
- children: leafColumns.map((col, colIdx) => {
21661
- const value = col.dataIndex ? row[col.dataIndex] : void 0;
21662
- const prevCol = colIdx > 0 ? leafColumns[colIdx - 1] : null;
21663
- const isAfterFixedLeft = prevCol?.fixed === "left";
21664
- const showBorderLeft = columnDividers && colIdx > 0 && !isAfterFixedLeft && !col.fixed;
21665
- return /* @__PURE__ */ jsx63(
21666
- TableCell,
21667
- {
21668
- "data-underverse-column-key": col.key,
21669
- style: getStickyColumnStyle(col),
21670
- className: cn(
21671
- cellPadding,
21672
- col.align === "right" && "text-right",
21673
- col.align === "center" && "text-center",
21674
- showBorderLeft && "border-l border-border/60",
21675
- getStickyCellClass(col, isStripedRow)
21676
- ),
21677
- children: col.render ? col.render(value, row, idx) : /* @__PURE__ */ jsx63(DataTableOverflowText, { text: String(value ?? ""), align: col.align })
21678
- },
21679
- col.key
21680
- );
21681
- })
21682
- },
21683
- getRowKey(row, idx)
21684
- );
21685
- }) });
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
+ ] }) });
21686
21895
  }
21687
21896
 
21688
21897
  // src/components/DataTable/components/DataTableHeader.tsx
21689
21898
  import React54 from "react";
21690
21899
  import { Filter as FilterIcon } from "lucide-react";
21691
- 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";
21692
21901
  function getColumnLabel(title) {
21693
21902
  if (typeof title === "string" || typeof title === "number") {
21694
21903
  return String(title).replace(/\s+/g, " ").trim();
@@ -21904,10 +22113,10 @@ function DataTableHeader({
21904
22113
  isCenterAlign && "justify-center",
21905
22114
  !isRightAlign && !isCenterAlign && "justify-start"
21906
22115
  ),
21907
- children: isRightAlign ? /* @__PURE__ */ jsxs53(Fragment22, { children: [
22116
+ children: isRightAlign ? /* @__PURE__ */ jsxs53(Fragment23, { children: [
21908
22117
  filterContent,
21909
22118
  titleContent
21910
- ] }) : /* @__PURE__ */ jsxs53(Fragment22, { children: [
22119
+ ] }) : /* @__PURE__ */ jsxs53(Fragment23, { children: [
21911
22120
  titleContent,
21912
22121
  filterContent
21913
22122
  ] })
@@ -21928,7 +22137,7 @@ function DataTableHeader({
21928
22137
  t
21929
22138
  ]
21930
22139
  );
21931
- 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) => {
21932
22141
  const { column: col, colSpan, rowSpan, isLeaf } = headerCell;
21933
22142
  const prevCell = cellIndex > 0 ? row[cellIndex - 1] : null;
21934
22143
  const prevCol = prevCell?.column;
@@ -22749,6 +22958,9 @@ function DataTable({
22749
22958
  horizontalMode = "auto",
22750
22959
  overflowHidden = true,
22751
22960
  useOverlayScrollbar = false,
22961
+ virtualizedRows = false,
22962
+ estimatedRowHeight,
22963
+ overscan = 8,
22752
22964
  enableHeaderAutoFit = true,
22753
22965
  labels
22754
22966
  }) {
@@ -22804,6 +23016,7 @@ function DataTable({
22804
23016
  console.warn("[DataTable] `rowKey` should be provided when using sort/filter/pagination to keep row identity stable.");
22805
23017
  }, [columns, isServerMode, pageSizeOptions, rowKey]);
22806
23018
  const densityRowClass = density === "compact" ? "h-9" : density === "comfortable" ? "h-14" : "h-12";
23019
+ const defaultEstimatedRowHeight = density === "compact" ? 36 : density === "comfortable" ? 56 : 48;
22807
23020
  const cellPadding = density === "compact" ? "py-1.5 px-3" : density === "comfortable" ? "py-3 px-4" : "py-2.5 px-4";
22808
23021
  const headerTitleClass = size === "sm" ? "text-xs" : size === "lg" ? "text-[15px]" : "text-sm";
22809
23022
  const headerMinHeightClass = size === "sm" ? "min-h-9" : size === "lg" ? "min-h-11" : "min-h-10";
@@ -22831,8 +23044,24 @@ function DataTable({
22831
23044
  };
22832
23045
  const viewportRef = React62.useRef(null);
22833
23046
  const tableRef = React62.useRef(null);
23047
+ const canVirtualizeRows = virtualizedRows && !loading2 && displayedData.length > 0;
23048
+ const shouldUseScrollViewport = stickyHeader || canVirtualizeRows;
23049
+ const rowVirtualizer = useVirtualizer4({
23050
+ count: canVirtualizeRows ? displayedData.length : 0,
23051
+ getScrollElement: () => viewportRef.current,
23052
+ estimateSize: () => estimatedRowHeight ?? defaultEstimatedRowHeight,
23053
+ initialRect: {
23054
+ width: 0,
23055
+ height: typeof maxHeight === "number" ? maxHeight : 500
23056
+ },
23057
+ overscan,
23058
+ enabled: canVirtualizeRows
23059
+ });
23060
+ const virtualRows = canVirtualizeRows ? rowVirtualizer.getVirtualItems() : [];
23061
+ const virtualPaddingTop = canVirtualizeRows && virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
23062
+ const virtualPaddingBottom = canVirtualizeRows && virtualRows.length > 0 ? Math.max(0, rowVirtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0)) : 0;
22834
23063
  useOverlayScrollbarTarget(viewportRef, {
22835
- enabled: useOverlayScrollbar,
23064
+ enabled: useOverlayScrollbar && !canVirtualizeRows,
22836
23065
  overflowX: overlayOverflowX
22837
23066
  });
22838
23067
  const autoFitColumn = React62.useCallback((columnKey) => {
@@ -22896,8 +23125,9 @@ function DataTable({
22896
23125
  "div",
22897
23126
  {
22898
23127
  ref: viewportRef,
22899
- className: cn("w-full", viewportOverflowXClass, stickyHeader && "overflow-y-auto"),
22900
- style: stickyHeader ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
23128
+ "data-os-ignore": canVirtualizeRows ? "" : void 0,
23129
+ className: cn("w-full", viewportOverflowXClass, shouldUseScrollViewport && "overflow-y-auto"),
23130
+ style: shouldUseScrollViewport ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
22901
23131
  children: /* @__PURE__ */ jsxs56(
22902
23132
  Table,
22903
23133
  {
@@ -22947,7 +23177,11 @@ function DataTable({
22947
23177
  getStickyColumnStyle,
22948
23178
  getStickyCellClass,
22949
23179
  t,
22950
- labels
23180
+ labels,
23181
+ virtualRows: canVirtualizeRows ? virtualRows : void 0,
23182
+ virtualPaddingTop: canVirtualizeRows ? virtualPaddingTop : void 0,
23183
+ virtualPaddingBottom: canVirtualizeRows ? virtualPaddingBottom : void 0,
23184
+ measureVirtualRow: canVirtualizeRows ? rowVirtualizer.measureElement : void 0
22951
23185
  }
22952
23186
  )
22953
23187
  ]
@@ -23212,7 +23446,7 @@ function AccessDenied({
23212
23446
  import { Moon as Moon2, Sun as Sun2, Monitor } from "lucide-react";
23213
23447
  import { useRef as useRef25, useState as useState40 } from "react";
23214
23448
  import { createPortal as createPortal6 } from "react-dom";
23215
- import { Fragment as Fragment23, jsx as jsx71, jsxs as jsxs60 } from "react/jsx-runtime";
23449
+ import { Fragment as Fragment24, jsx as jsx71, jsxs as jsxs60 } from "react/jsx-runtime";
23216
23450
  function ThemeToggleHeadless({
23217
23451
  theme,
23218
23452
  onChange,
@@ -23262,7 +23496,7 @@ function ThemeToggleHeadless({
23262
23496
  children: /* @__PURE__ */ jsx71(CurrentIcon, { className: "h-5 w-5" })
23263
23497
  }
23264
23498
  ),
23265
- isOpen && /* @__PURE__ */ jsxs60(Fragment23, { children: [
23499
+ isOpen && /* @__PURE__ */ jsxs60(Fragment24, { children: [
23266
23500
  typeof window !== "undefined" && createPortal6(/* @__PURE__ */ jsx71("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23267
23501
  typeof window !== "undefined" && dropdownPosition && createPortal6(
23268
23502
  /* @__PURE__ */ jsx71(
@@ -23314,7 +23548,7 @@ function ThemeToggleHeadless({
23314
23548
  import { useRef as useRef26, useState as useState41 } from "react";
23315
23549
  import { createPortal as createPortal7 } from "react-dom";
23316
23550
  import { Globe } from "lucide-react";
23317
- import { Fragment as Fragment24, jsx as jsx72, jsxs as jsxs61 } from "react/jsx-runtime";
23551
+ import { Fragment as Fragment25, jsx as jsx72, jsxs as jsxs61 } from "react/jsx-runtime";
23318
23552
  function LanguageSwitcherHeadless({
23319
23553
  locales,
23320
23554
  currentLocale,
@@ -23359,7 +23593,7 @@ function LanguageSwitcherHeadless({
23359
23593
  children: /* @__PURE__ */ jsx72(Globe, { className: "h-5 w-5" })
23360
23594
  }
23361
23595
  ),
23362
- isOpen && /* @__PURE__ */ jsxs61(Fragment24, { children: [
23596
+ isOpen && /* @__PURE__ */ jsxs61(Fragment25, { children: [
23363
23597
  typeof window !== "undefined" && createPortal7(/* @__PURE__ */ jsx72("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23364
23598
  typeof window !== "undefined" && dropdownPosition && createPortal7(
23365
23599
  /* @__PURE__ */ jsx72(
@@ -25263,7 +25497,7 @@ function getDefaultLetterSpacings() {
25263
25497
  }
25264
25498
 
25265
25499
  // src/components/UEditor/toolbar.tsx
25266
- import { Fragment as Fragment25, jsx as jsx80, jsxs as jsxs67 } from "react/jsx-runtime";
25500
+ import { Fragment as Fragment26, jsx as jsx80, jsxs as jsxs67 } from "react/jsx-runtime";
25267
25501
  function fileToDataUrl2(file) {
25268
25502
  return new Promise((resolve, reject) => {
25269
25503
  const reader = new FileReader();
@@ -25905,7 +26139,7 @@ var EditorToolbar = ({
25905
26139
  },
25906
26140
  onCancel: () => setShowImageInput(false)
25907
26141
  }
25908
- ) : /* @__PURE__ */ jsxs67(Fragment25, { children: [
26142
+ ) : /* @__PURE__ */ jsxs67(Fragment26, { children: [
25909
26143
  /* @__PURE__ */ jsx80(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true) }),
25910
26144
  /* @__PURE__ */ jsx80(
25911
26145
  DropdownMenuItem,
@@ -26950,7 +27184,7 @@ function findDiffEnd(a, b, posA, posB) {
26950
27184
  posB -= size;
26951
27185
  }
26952
27186
  }
26953
- var Fragment26 = class _Fragment {
27187
+ var Fragment27 = class _Fragment {
26954
27188
  /**
26955
27189
  @internal
26956
27190
  */
@@ -27242,7 +27476,7 @@ var Fragment26 = class _Fragment {
27242
27476
  throw new RangeError("Can not convert " + nodes + " to a Fragment" + (nodes.nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : ""));
27243
27477
  }
27244
27478
  };
27245
- Fragment26.empty = new Fragment26([], 0);
27479
+ Fragment27.empty = new Fragment27([], 0);
27246
27480
  var found = { index: 0, offset: 0 };
27247
27481
  function retIndex(index, offset) {
27248
27482
  found.index = index;
@@ -27467,7 +27701,7 @@ var Slice = class _Slice {
27467
27701
  let openStart = json.openStart || 0, openEnd = json.openEnd || 0;
27468
27702
  if (typeof openStart != "number" || typeof openEnd != "number")
27469
27703
  throw new RangeError("Invalid input for Slice.fromJSON");
27470
- return new _Slice(Fragment26.fromJSON(schema, json.content), openStart, openEnd);
27704
+ return new _Slice(Fragment27.fromJSON(schema, json.content), openStart, openEnd);
27471
27705
  }
27472
27706
  /**
27473
27707
  Create a slice from a fragment by taking the maximum possible
@@ -27482,7 +27716,7 @@ var Slice = class _Slice {
27482
27716
  return new _Slice(fragment, openStart, openEnd);
27483
27717
  }
27484
27718
  };
27485
- Slice.empty = new Slice(Fragment26.empty, 0, 0);
27719
+ Slice.empty = new Slice(Fragment27.empty, 0, 0);
27486
27720
  function removeRange(content, from, to) {
27487
27721
  let { index, offset } = content.findIndex(from), child = content.maybeChild(index);
27488
27722
  let { index: indexTo, offset: offsetTo } = content.findIndex(to);
@@ -27580,7 +27814,7 @@ function replaceThreeWay($from, $start, $end, $to, depth) {
27580
27814
  addNode(close(openEnd, replaceTwoWay($end, $to, depth + 1)), content);
27581
27815
  }
27582
27816
  addRange($to, null, depth, content);
27583
- return new Fragment26(content);
27817
+ return new Fragment27(content);
27584
27818
  }
27585
27819
  function replaceTwoWay($from, $to, depth) {
27586
27820
  let content = [];
@@ -27590,13 +27824,13 @@ function replaceTwoWay($from, $to, depth) {
27590
27824
  addNode(close(type, replaceTwoWay($from, $to, depth + 1)), content);
27591
27825
  }
27592
27826
  addRange($to, null, depth, content);
27593
- return new Fragment26(content);
27827
+ return new Fragment27(content);
27594
27828
  }
27595
27829
  function prepareSliceForReplace(slice, $along) {
27596
27830
  let extra = $along.depth - slice.openStart, parent = $along.node(extra);
27597
27831
  let node = parent.copy(slice.content);
27598
27832
  for (let i = extra - 1; i >= 0; i--)
27599
- node = $along.node(i).copy(Fragment26.from(node));
27833
+ node = $along.node(i).copy(Fragment27.from(node));
27600
27834
  return {
27601
27835
  start: node.resolveNoCache(slice.openStart + extra),
27602
27836
  end: node.resolveNoCache(node.content.size - slice.openEnd - extra)
@@ -27935,7 +28169,7 @@ var Node2 = class _Node {
27935
28169
  this.type = type;
27936
28170
  this.attrs = attrs;
27937
28171
  this.marks = marks;
27938
- this.content = content || Fragment26.empty;
28172
+ this.content = content || Fragment27.empty;
27939
28173
  }
27940
28174
  /**
27941
28175
  The array of this node's child nodes.
@@ -28240,7 +28474,7 @@ var Node2 = class _Node {
28240
28474
  can optionally pass `start` and `end` indices into the
28241
28475
  replacement fragment.
28242
28476
  */
28243
- canReplace(from, to, replacement = Fragment26.empty, start = 0, end = replacement.childCount) {
28477
+ canReplace(from, to, replacement = Fragment27.empty, start = 0, end = replacement.childCount) {
28244
28478
  let one = this.contentMatchAt(from).matchFragment(replacement, start, end);
28245
28479
  let two = one && one.matchFragment(this.content, to);
28246
28480
  if (!two || !two.validEnd)
@@ -28322,7 +28556,7 @@ var Node2 = class _Node {
28322
28556
  throw new RangeError("Invalid text node in JSON");
28323
28557
  return schema.text(json.text, marks);
28324
28558
  }
28325
- let content = Fragment26.fromJSON(schema, json.content);
28559
+ let content = Fragment27.fromJSON(schema, json.content);
28326
28560
  let node = schema.nodeType(json.type).create(json.attrs, content, marks);
28327
28561
  node.type.checkAttrs(node.attrs);
28328
28562
  return node;
@@ -28418,7 +28652,7 @@ var ContentMatch = class _ContentMatch {
28418
28652
  function search(match, types) {
28419
28653
  let finished = match.matchFragment(after, startIndex);
28420
28654
  if (finished && (!toEnd || finished.validEnd))
28421
- return Fragment26.from(types.map((tp) => tp.createAndFill()));
28655
+ return Fragment27.from(types.map((tp) => tp.createAndFill()));
28422
28656
  for (let i = 0; i < match.next.length; i++) {
28423
28657
  let { type, next } = match.next[i];
28424
28658
  if (!(type.isText || type.hasRequiredAttrs()) && seen.indexOf(next) == -1) {
@@ -28986,7 +29220,7 @@ function mapFragment(fragment, f, parent) {
28986
29220
  child = f(child, parent, i);
28987
29221
  mapped.push(child);
28988
29222
  }
28989
- return Fragment26.fromArray(mapped);
29223
+ return Fragment27.fromArray(mapped);
28990
29224
  }
28991
29225
  var AddMarkStep = class _AddMarkStep extends Step {
28992
29226
  /**
@@ -29103,7 +29337,7 @@ var AddNodeMarkStep = class _AddNodeMarkStep extends Step {
29103
29337
  if (!node)
29104
29338
  return StepResult.fail("No node at mark step's position");
29105
29339
  let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks));
29106
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29340
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29107
29341
  }
29108
29342
  invert(doc) {
29109
29343
  let node = doc.nodeAt(this.pos);
@@ -29149,7 +29383,7 @@ var RemoveNodeMarkStep = class _RemoveNodeMarkStep extends Step {
29149
29383
  if (!node)
29150
29384
  return StepResult.fail("No node at mark step's position");
29151
29385
  let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks));
29152
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29386
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29153
29387
  }
29154
29388
  invert(doc) {
29155
29389
  let node = doc.nodeAt(this.pos);
@@ -29350,7 +29584,7 @@ var AttrStep = class _AttrStep extends Step {
29350
29584
  attrs[name] = node.attrs[name];
29351
29585
  attrs[this.attr] = this.value;
29352
29586
  let updated = node.type.create(attrs, null, node.marks);
29353
- return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment26.from(updated), 0, node.isLeaf ? 0 : 1));
29587
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29354
29588
  }
29355
29589
  getMap() {
29356
29590
  return StepMap.empty;
@@ -29734,7 +29968,7 @@ var NodeSelection2 = class _NodeSelection extends Selection {
29734
29968
  return new _NodeSelection($pos);
29735
29969
  }
29736
29970
  content() {
29737
- return new Slice(Fragment26.from(this.node), 0, 0);
29971
+ return new Slice(Fragment27.from(this.node), 0, 0);
29738
29972
  }
29739
29973
  eq(other) {
29740
29974
  return other instanceof _NodeSelection && other.anchor == this.anchor;
@@ -30462,10 +30696,10 @@ var CellSelection = class CellSelection2 extends Selection {
30462
30696
  }
30463
30697
  rowContent.push(cell);
30464
30698
  }
30465
- rows.push(table.child(row).copy(Fragment26.from(rowContent)));
30699
+ rows.push(table.child(row).copy(Fragment27.from(rowContent)));
30466
30700
  }
30467
30701
  const fragment = this.isColSelection() && this.isRowSelection() ? table : rows;
30468
- return new Slice(Fragment26.from(fragment), 1, 1);
30702
+ return new Slice(Fragment27.from(fragment), 1, 1);
30469
30703
  }
30470
30704
  replace(tr, content = Slice.empty) {
30471
30705
  const mapFrom = tr.steps.length, ranges = this.ranges;
@@ -30477,7 +30711,7 @@ var CellSelection = class CellSelection2 extends Selection {
30477
30711
  if (sel) tr.setSelection(sel);
30478
30712
  }
30479
30713
  replaceWith(tr, node) {
30480
- this.replace(tr, new Slice(Fragment26.from(node), 0, 0));
30714
+ this.replace(tr, new Slice(Fragment27.from(node), 0, 0));
30481
30715
  }
30482
30716
  forEachCell(f) {
30483
30717
  const table = this.$anchorCell.node(-1);
@@ -31174,7 +31408,7 @@ function getRelativeSelectedCellsMetrics(surface) {
31174
31408
  }
31175
31409
 
31176
31410
  // src/components/UEditor/table-controls.tsx
31177
- import { Fragment as Fragment27, jsx as jsx82, jsxs as jsxs70 } from "react/jsx-runtime";
31411
+ import { Fragment as Fragment28, jsx as jsx82, jsxs as jsxs70 } from "react/jsx-runtime";
31178
31412
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
31179
31413
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
31180
31414
  var MENU_HOVER_PADDING = 18;
@@ -31811,7 +32045,7 @@ function TableControls({ editor, containerRef }) {
31811
32045
  const expandPreviewWidth = dragPreview?.kind === "add-column" ? layout.tableWidth + dragPreview.previewCols * layout.avgColumnWidth : layout.tableWidth;
31812
32046
  const expandPreviewHeight = dragPreview?.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
31813
32047
  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;
31814
- return /* @__PURE__ */ jsxs70(Fragment27, { children: [
32048
+ return /* @__PURE__ */ jsxs70(Fragment28, { children: [
31815
32049
  layout.rowHandles.map((rowHandle) => {
31816
32050
  const menuKey = getRowMenuKey(rowHandle.index);
31817
32051
  const visible = controlsVisible || hoverState.rowHandleIndex === rowHandle.index || openMenuKey === menuKey;
@@ -32085,7 +32319,7 @@ function TableControls({ editor, containerRef }) {
32085
32319
  )
32086
32320
  }
32087
32321
  ),
32088
- dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32322
+ dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32089
32323
  /* @__PURE__ */ jsx82(
32090
32324
  "div",
32091
32325
  {
@@ -32113,7 +32347,7 @@ function TableControls({ editor, containerRef }) {
32113
32347
  }
32114
32348
  )
32115
32349
  ] }),
32116
- dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32350
+ dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32117
32351
  /* @__PURE__ */ jsx82(
32118
32352
  "div",
32119
32353
  {
@@ -32141,7 +32375,7 @@ function TableControls({ editor, containerRef }) {
32141
32375
  }
32142
32376
  )
32143
32377
  ] }),
32144
- (dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column") && /* @__PURE__ */ jsx82(Fragment27, { children: /* @__PURE__ */ jsx82(
32378
+ (dragPreview?.kind === "add-row" || dragPreview?.kind === "add-column") && /* @__PURE__ */ jsx82(Fragment28, { children: /* @__PURE__ */ jsx82(
32145
32379
  "div",
32146
32380
  {
32147
32381
  "aria-hidden": "true",