@underverse-ui/underverse 1.0.113 → 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
@@ -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,23 @@ 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 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;
22834
23062
  useOverlayScrollbarTarget(viewportRef, {
22835
- enabled: useOverlayScrollbar,
23063
+ enabled: useOverlayScrollbar && !canVirtualizeRows,
22836
23064
  overflowX: overlayOverflowX
22837
23065
  });
22838
23066
  const autoFitColumn = React62.useCallback((columnKey) => {
@@ -22896,6 +23124,7 @@ function DataTable({
22896
23124
  "div",
22897
23125
  {
22898
23126
  ref: viewportRef,
23127
+ "data-os-ignore": canVirtualizeRows ? "" : void 0,
22899
23128
  className: cn("w-full", viewportOverflowXClass, stickyHeader && "overflow-y-auto"),
22900
23129
  style: stickyHeader ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : void 0,
22901
23130
  children: /* @__PURE__ */ jsxs56(
@@ -22947,7 +23176,11 @@ function DataTable({
22947
23176
  getStickyColumnStyle,
22948
23177
  getStickyCellClass,
22949
23178
  t,
22950
- 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
22951
23184
  }
22952
23185
  )
22953
23186
  ]
@@ -23212,7 +23445,7 @@ function AccessDenied({
23212
23445
  import { Moon as Moon2, Sun as Sun2, Monitor } from "lucide-react";
23213
23446
  import { useRef as useRef25, useState as useState40 } from "react";
23214
23447
  import { createPortal as createPortal6 } from "react-dom";
23215
- 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";
23216
23449
  function ThemeToggleHeadless({
23217
23450
  theme,
23218
23451
  onChange,
@@ -23262,7 +23495,7 @@ function ThemeToggleHeadless({
23262
23495
  children: /* @__PURE__ */ jsx71(CurrentIcon, { className: "h-5 w-5" })
23263
23496
  }
23264
23497
  ),
23265
- isOpen && /* @__PURE__ */ jsxs60(Fragment23, { children: [
23498
+ isOpen && /* @__PURE__ */ jsxs60(Fragment24, { children: [
23266
23499
  typeof window !== "undefined" && createPortal6(/* @__PURE__ */ jsx71("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23267
23500
  typeof window !== "undefined" && dropdownPosition && createPortal6(
23268
23501
  /* @__PURE__ */ jsx71(
@@ -23314,7 +23547,7 @@ function ThemeToggleHeadless({
23314
23547
  import { useRef as useRef26, useState as useState41 } from "react";
23315
23548
  import { createPortal as createPortal7 } from "react-dom";
23316
23549
  import { Globe } from "lucide-react";
23317
- 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";
23318
23551
  function LanguageSwitcherHeadless({
23319
23552
  locales,
23320
23553
  currentLocale,
@@ -23359,7 +23592,7 @@ function LanguageSwitcherHeadless({
23359
23592
  children: /* @__PURE__ */ jsx72(Globe, { className: "h-5 w-5" })
23360
23593
  }
23361
23594
  ),
23362
- isOpen && /* @__PURE__ */ jsxs61(Fragment24, { children: [
23595
+ isOpen && /* @__PURE__ */ jsxs61(Fragment25, { children: [
23363
23596
  typeof window !== "undefined" && createPortal7(/* @__PURE__ */ jsx72("div", { className: "fixed inset-0 z-9998", onClick: () => setIsOpen(false) }), document.body),
23364
23597
  typeof window !== "undefined" && dropdownPosition && createPortal7(
23365
23598
  /* @__PURE__ */ jsx72(
@@ -25263,7 +25496,7 @@ function getDefaultLetterSpacings() {
25263
25496
  }
25264
25497
 
25265
25498
  // src/components/UEditor/toolbar.tsx
25266
- 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";
25267
25500
  function fileToDataUrl2(file) {
25268
25501
  return new Promise((resolve, reject) => {
25269
25502
  const reader = new FileReader();
@@ -25905,7 +26138,7 @@ var EditorToolbar = ({
25905
26138
  },
25906
26139
  onCancel: () => setShowImageInput(false)
25907
26140
  }
25908
- ) : /* @__PURE__ */ jsxs67(Fragment25, { children: [
26141
+ ) : /* @__PURE__ */ jsxs67(Fragment26, { children: [
25909
26142
  /* @__PURE__ */ jsx80(DropdownMenuItem, { icon: LinkIcon, label: t("imageInput.addFromUrl"), onClick: () => setShowImageInput(true) }),
25910
26143
  /* @__PURE__ */ jsx80(
25911
26144
  DropdownMenuItem,
@@ -26950,7 +27183,7 @@ function findDiffEnd(a, b, posA, posB) {
26950
27183
  posB -= size;
26951
27184
  }
26952
27185
  }
26953
- var Fragment26 = class _Fragment {
27186
+ var Fragment27 = class _Fragment {
26954
27187
  /**
26955
27188
  @internal
26956
27189
  */
@@ -27242,7 +27475,7 @@ var Fragment26 = class _Fragment {
27242
27475
  throw new RangeError("Can not convert " + nodes + " to a Fragment" + (nodes.nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : ""));
27243
27476
  }
27244
27477
  };
27245
- Fragment26.empty = new Fragment26([], 0);
27478
+ Fragment27.empty = new Fragment27([], 0);
27246
27479
  var found = { index: 0, offset: 0 };
27247
27480
  function retIndex(index, offset) {
27248
27481
  found.index = index;
@@ -27467,7 +27700,7 @@ var Slice = class _Slice {
27467
27700
  let openStart = json.openStart || 0, openEnd = json.openEnd || 0;
27468
27701
  if (typeof openStart != "number" || typeof openEnd != "number")
27469
27702
  throw new RangeError("Invalid input for Slice.fromJSON");
27470
- return new _Slice(Fragment26.fromJSON(schema, json.content), openStart, openEnd);
27703
+ return new _Slice(Fragment27.fromJSON(schema, json.content), openStart, openEnd);
27471
27704
  }
27472
27705
  /**
27473
27706
  Create a slice from a fragment by taking the maximum possible
@@ -27482,7 +27715,7 @@ var Slice = class _Slice {
27482
27715
  return new _Slice(fragment, openStart, openEnd);
27483
27716
  }
27484
27717
  };
27485
- Slice.empty = new Slice(Fragment26.empty, 0, 0);
27718
+ Slice.empty = new Slice(Fragment27.empty, 0, 0);
27486
27719
  function removeRange(content, from, to) {
27487
27720
  let { index, offset } = content.findIndex(from), child = content.maybeChild(index);
27488
27721
  let { index: indexTo, offset: offsetTo } = content.findIndex(to);
@@ -27580,7 +27813,7 @@ function replaceThreeWay($from, $start, $end, $to, depth) {
27580
27813
  addNode(close(openEnd, replaceTwoWay($end, $to, depth + 1)), content);
27581
27814
  }
27582
27815
  addRange($to, null, depth, content);
27583
- return new Fragment26(content);
27816
+ return new Fragment27(content);
27584
27817
  }
27585
27818
  function replaceTwoWay($from, $to, depth) {
27586
27819
  let content = [];
@@ -27590,13 +27823,13 @@ function replaceTwoWay($from, $to, depth) {
27590
27823
  addNode(close(type, replaceTwoWay($from, $to, depth + 1)), content);
27591
27824
  }
27592
27825
  addRange($to, null, depth, content);
27593
- return new Fragment26(content);
27826
+ return new Fragment27(content);
27594
27827
  }
27595
27828
  function prepareSliceForReplace(slice, $along) {
27596
27829
  let extra = $along.depth - slice.openStart, parent = $along.node(extra);
27597
27830
  let node = parent.copy(slice.content);
27598
27831
  for (let i = extra - 1; i >= 0; i--)
27599
- node = $along.node(i).copy(Fragment26.from(node));
27832
+ node = $along.node(i).copy(Fragment27.from(node));
27600
27833
  return {
27601
27834
  start: node.resolveNoCache(slice.openStart + extra),
27602
27835
  end: node.resolveNoCache(node.content.size - slice.openEnd - extra)
@@ -27935,7 +28168,7 @@ var Node2 = class _Node {
27935
28168
  this.type = type;
27936
28169
  this.attrs = attrs;
27937
28170
  this.marks = marks;
27938
- this.content = content || Fragment26.empty;
28171
+ this.content = content || Fragment27.empty;
27939
28172
  }
27940
28173
  /**
27941
28174
  The array of this node's child nodes.
@@ -28240,7 +28473,7 @@ var Node2 = class _Node {
28240
28473
  can optionally pass `start` and `end` indices into the
28241
28474
  replacement fragment.
28242
28475
  */
28243
- canReplace(from, to, replacement = Fragment26.empty, start = 0, end = replacement.childCount) {
28476
+ canReplace(from, to, replacement = Fragment27.empty, start = 0, end = replacement.childCount) {
28244
28477
  let one = this.contentMatchAt(from).matchFragment(replacement, start, end);
28245
28478
  let two = one && one.matchFragment(this.content, to);
28246
28479
  if (!two || !two.validEnd)
@@ -28322,7 +28555,7 @@ var Node2 = class _Node {
28322
28555
  throw new RangeError("Invalid text node in JSON");
28323
28556
  return schema.text(json.text, marks);
28324
28557
  }
28325
- let content = Fragment26.fromJSON(schema, json.content);
28558
+ let content = Fragment27.fromJSON(schema, json.content);
28326
28559
  let node = schema.nodeType(json.type).create(json.attrs, content, marks);
28327
28560
  node.type.checkAttrs(node.attrs);
28328
28561
  return node;
@@ -28418,7 +28651,7 @@ var ContentMatch = class _ContentMatch {
28418
28651
  function search(match, types) {
28419
28652
  let finished = match.matchFragment(after, startIndex);
28420
28653
  if (finished && (!toEnd || finished.validEnd))
28421
- return Fragment26.from(types.map((tp) => tp.createAndFill()));
28654
+ return Fragment27.from(types.map((tp) => tp.createAndFill()));
28422
28655
  for (let i = 0; i < match.next.length; i++) {
28423
28656
  let { type, next } = match.next[i];
28424
28657
  if (!(type.isText || type.hasRequiredAttrs()) && seen.indexOf(next) == -1) {
@@ -28986,7 +29219,7 @@ function mapFragment(fragment, f, parent) {
28986
29219
  child = f(child, parent, i);
28987
29220
  mapped.push(child);
28988
29221
  }
28989
- return Fragment26.fromArray(mapped);
29222
+ return Fragment27.fromArray(mapped);
28990
29223
  }
28991
29224
  var AddMarkStep = class _AddMarkStep extends Step {
28992
29225
  /**
@@ -29103,7 +29336,7 @@ var AddNodeMarkStep = class _AddNodeMarkStep extends Step {
29103
29336
  if (!node)
29104
29337
  return StepResult.fail("No node at mark step's position");
29105
29338
  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));
29339
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29107
29340
  }
29108
29341
  invert(doc) {
29109
29342
  let node = doc.nodeAt(this.pos);
@@ -29149,7 +29382,7 @@ var RemoveNodeMarkStep = class _RemoveNodeMarkStep extends Step {
29149
29382
  if (!node)
29150
29383
  return StepResult.fail("No node at mark step's position");
29151
29384
  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));
29385
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29153
29386
  }
29154
29387
  invert(doc) {
29155
29388
  let node = doc.nodeAt(this.pos);
@@ -29350,7 +29583,7 @@ var AttrStep = class _AttrStep extends Step {
29350
29583
  attrs[name] = node.attrs[name];
29351
29584
  attrs[this.attr] = this.value;
29352
29585
  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));
29586
+ return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment27.from(updated), 0, node.isLeaf ? 0 : 1));
29354
29587
  }
29355
29588
  getMap() {
29356
29589
  return StepMap.empty;
@@ -29734,7 +29967,7 @@ var NodeSelection2 = class _NodeSelection extends Selection {
29734
29967
  return new _NodeSelection($pos);
29735
29968
  }
29736
29969
  content() {
29737
- return new Slice(Fragment26.from(this.node), 0, 0);
29970
+ return new Slice(Fragment27.from(this.node), 0, 0);
29738
29971
  }
29739
29972
  eq(other) {
29740
29973
  return other instanceof _NodeSelection && other.anchor == this.anchor;
@@ -30462,10 +30695,10 @@ var CellSelection = class CellSelection2 extends Selection {
30462
30695
  }
30463
30696
  rowContent.push(cell);
30464
30697
  }
30465
- rows.push(table.child(row).copy(Fragment26.from(rowContent)));
30698
+ rows.push(table.child(row).copy(Fragment27.from(rowContent)));
30466
30699
  }
30467
30700
  const fragment = this.isColSelection() && this.isRowSelection() ? table : rows;
30468
- return new Slice(Fragment26.from(fragment), 1, 1);
30701
+ return new Slice(Fragment27.from(fragment), 1, 1);
30469
30702
  }
30470
30703
  replace(tr, content = Slice.empty) {
30471
30704
  const mapFrom = tr.steps.length, ranges = this.ranges;
@@ -30477,7 +30710,7 @@ var CellSelection = class CellSelection2 extends Selection {
30477
30710
  if (sel) tr.setSelection(sel);
30478
30711
  }
30479
30712
  replaceWith(tr, node) {
30480
- this.replace(tr, new Slice(Fragment26.from(node), 0, 0));
30713
+ this.replace(tr, new Slice(Fragment27.from(node), 0, 0));
30481
30714
  }
30482
30715
  forEachCell(f) {
30483
30716
  const table = this.$anchorCell.node(-1);
@@ -31174,7 +31407,7 @@ function getRelativeSelectedCellsMetrics(surface) {
31174
31407
  }
31175
31408
 
31176
31409
  // src/components/UEditor/table-controls.tsx
31177
- 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";
31178
31411
  var FALLBACK_TABLE_ROW_HEIGHT = 44;
31179
31412
  var FALLBACK_TABLE_COLUMN_WIDTH = 160;
31180
31413
  var MENU_HOVER_PADDING = 18;
@@ -31811,7 +32044,7 @@ function TableControls({ editor, containerRef }) {
31811
32044
  const expandPreviewWidth = dragPreview?.kind === "add-column" ? layout.tableWidth + dragPreview.previewCols * layout.avgColumnWidth : layout.tableWidth;
31812
32045
  const expandPreviewHeight = dragPreview?.kind === "add-row" ? layout.tableHeight + dragPreview.previewRows * layout.avgRowHeight : layout.tableHeight;
31813
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;
31814
- return /* @__PURE__ */ jsxs70(Fragment27, { children: [
32047
+ return /* @__PURE__ */ jsxs70(Fragment28, { children: [
31815
32048
  layout.rowHandles.map((rowHandle) => {
31816
32049
  const menuKey = getRowMenuKey(rowHandle.index);
31817
32050
  const visible = controlsVisible || hoverState.rowHandleIndex === rowHandle.index || openMenuKey === menuKey;
@@ -32085,7 +32318,7 @@ function TableControls({ editor, containerRef }) {
32085
32318
  )
32086
32319
  }
32087
32320
  ),
32088
- dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32321
+ dragPreview?.kind === "row" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32089
32322
  /* @__PURE__ */ jsx82(
32090
32323
  "div",
32091
32324
  {
@@ -32113,7 +32346,7 @@ function TableControls({ editor, containerRef }) {
32113
32346
  }
32114
32347
  )
32115
32348
  ] }),
32116
- dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment27, { children: [
32349
+ dragPreview?.kind === "column" && /* @__PURE__ */ jsxs70(Fragment28, { children: [
32117
32350
  /* @__PURE__ */ jsx82(
32118
32351
  "div",
32119
32352
  {
@@ -32141,7 +32374,7 @@ function TableControls({ editor, containerRef }) {
32141
32374
  }
32142
32375
  )
32143
32376
  ] }),
32144
- (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(
32145
32378
  "div",
32146
32379
  {
32147
32380
  "aria-hidden": "true",