This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch folders
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 66a3d2af9724abfd390c69efa8479cb520dbba62
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Thu Jan 29 17:01:56 2026 +0100

    Perf improvements
---
 .../Datasource/FoldersEditor/TreeItem.tsx          |  25 +--
 .../FoldersEditor/VirtualizedTreeList.tsx          |   1 -
 .../Datasource/FoldersEditor/folderOperations.ts   |  32 ++--
 .../FoldersEditor/hooks/useDragHandlers.ts         | 211 ++++++++++-----------
 .../components/Datasource/FoldersEditor/index.tsx  |  58 +++---
 .../Datasource/FoldersEditor/treeUtils.ts          |  50 +++--
 6 files changed, 185 insertions(+), 192 deletions(-)

diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/TreeItem.tsx 
b/superset-frontend/src/components/Datasource/FoldersEditor/TreeItem.tsx
index e9d66df487c..a941fd19474 100644
--- a/superset-frontend/src/components/Datasource/FoldersEditor/TreeItem.tsx
+++ b/superset-frontend/src/components/Datasource/FoldersEditor/TreeItem.tsx
@@ -30,10 +30,12 @@ import {
   Tooltip,
 } from '@superset-ui/core/components';
 import {
+  ColumnLabelExtendedType,
   ColumnMeta,
   ColumnTypeLabel,
   Metric,
 } from '@superset-ui/chart-controls';
+import { GenericDataType } from '@apache-superset/core/api/core';
 import {
   OptionControlContainer,
   Label,
@@ -169,17 +171,18 @@ function TreeItemComponent({
     return name;
   }, [type, metric, column, name]);
 
-  const columnType = useMemo(() => {
-    if (type === FoldersEditorItemType.Metric) {
-      return 'metric';
-    }
-    if (type === FoldersEditorItemType.Column && column) {
-      const hasExpression =
-        column.expression && column.expression !== column.column_name;
-      return hasExpression ? 'expression' : column.type_generic;
-    }
-    return undefined;
-  }, [type, column]);
+  const columnType: ColumnLabelExtendedType | GenericDataType | undefined =
+    useMemo(() => {
+      if (type === FoldersEditorItemType.Metric) {
+        return 'metric';
+      }
+      if (type === FoldersEditorItemType.Column && column) {
+        const hasExpression =
+          column.expression && column.expression !== column.column_name;
+        return hasExpression ? 'expression' : column.type_generic;
+      }
+      return undefined;
+    }, [type, column]);
 
   const hasEmptyName = !name || name.trim() === '';
 
diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/VirtualizedTreeList.tsx
 
b/superset-frontend/src/components/Datasource/FoldersEditor/VirtualizedTreeList.tsx
index 57ba0e33d29..1920b296198 100644
--- 
a/superset-frontend/src/components/Datasource/FoldersEditor/VirtualizedTreeList.tsx
+++ 
b/superset-frontend/src/components/Datasource/FoldersEditor/VirtualizedTreeList.tsx
@@ -163,7 +163,6 @@ export function VirtualizedTreeList({
       itemSeparatorInfo,
       visibleItemIds,
       searchTerm,
-      activeId,
     ],
   );
 
diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/folderOperations.ts 
b/superset-frontend/src/components/Datasource/FoldersEditor/folderOperations.ts
index d84cd9a2414..4543c9cecd6 100644
--- 
a/superset-frontend/src/components/Datasource/FoldersEditor/folderOperations.ts
+++ 
b/superset-frontend/src/components/Datasource/FoldersEditor/folderOperations.ts
@@ -362,27 +362,21 @@ export const ensureDefaultFolders = (
 
   const result = [...enrichedFolders];
 
-  const isItemInFolders = (uuid: string): boolean => {
-    const checkFolder = (folder: DatasourceFolder): boolean => {
-      if (!folder.children) return false;
-
-      return folder.children.some(child => {
-        if (child.uuid === uuid) return true;
-        if (
-          child.type === FoldersEditorItemType.Folder &&
-          'children' in child
-        ) {
-          return checkFolder(child as DatasourceFolder);
-        }
-        return false;
-      });
-    };
-
-    return enrichedFolders.some(checkFolder);
+  // Build a Set of all assigned UUIDs in a single pass for O(1) lookups
+  const assignedIds = new Set<string>();
+  const collectAssignedIds = (folder: DatasourceFolder) => {
+    if (!folder.children) return;
+    for (const child of folder.children) {
+      assignedIds.add(child.uuid);
+      if (child.type === FoldersEditorItemType.Folder && 'children' in child) {
+        collectAssignedIds(child as DatasourceFolder);
+      }
+    }
   };
+  enrichedFolders.forEach(collectAssignedIds);
 
   if (!hasMetricsFolder) {
-    const unassignedMetrics = metrics.filter(m => !isItemInFolders(m.uuid));
+    const unassignedMetrics = metrics.filter(m => !assignedIds.has(m.uuid));
 
     result.push({
       uuid: DEFAULT_METRICS_FOLDER_UUID,
@@ -397,7 +391,7 @@ export const ensureDefaultFolders = (
   }
 
   if (!hasColumnsFolder) {
-    const unassignedColumns = columns.filter(c => !isItemInFolders(c.uuid));
+    const unassignedColumns = columns.filter(c => !assignedIds.has(c.uuid));
 
     result.push({
       uuid: DEFAULT_COLUMNS_FOLDER_UUID,
diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/hooks/useDragHandlers.ts
 
b/superset-frontend/src/components/Datasource/FoldersEditor/hooks/useDragHandlers.ts
index 8b90c961e13..9da07b8a048 100644
--- 
a/superset-frontend/src/components/Datasource/FoldersEditor/hooks/useDragHandlers.ts
+++ 
b/superset-frontend/src/components/Datasource/FoldersEditor/hooks/useDragHandlers.ts
@@ -39,7 +39,6 @@ import {
 import { buildTree, getProjection, serializeForAPI } from '../treeUtils';
 
 interface UseDragHandlersProps {
-  items: TreeItemType[];
   setItems: React.Dispatch<React.SetStateAction<TreeItemType[]>>;
   computeFlattenedItems: (
     activeId: UniqueIdentifier | null,
@@ -51,7 +50,6 @@ interface UseDragHandlersProps {
 }
 
 export function useDragHandlers({
-  items,
   setItems,
   computeFlattenedItems,
   fullFlattenedItems,
@@ -98,6 +96,58 @@ export function useDragHandlers({
     return map;
   }, [flattenedItems]);
 
+  // Shared lookup maps for O(1) access - used by handleDragEnd and 
forbiddenDropFolderIds
+  const fullItemsByUuid = useMemo(() => {
+    const map = new Map<string, FlattenedTreeItem>();
+    fullFlattenedItems.forEach(item => {
+      map.set(item.uuid, item);
+    });
+    return map;
+  }, [fullFlattenedItems]);
+
+  const fullItemsIndexMap = useMemo(() => {
+    const map = new Map<string, number>();
+    fullFlattenedItems.forEach((item, index) => {
+      map.set(item.uuid, index);
+    });
+    return map;
+  }, [fullFlattenedItems]);
+
+  const childrenByParentId = useMemo(() => {
+    const map = new Map<string, FlattenedTreeItem[]>();
+    fullFlattenedItems.forEach(item => {
+      if (item.parentId) {
+        const children = map.get(item.parentId) ?? [];
+        children.push(item);
+        map.set(item.parentId, children);
+      }
+    });
+    return map;
+  }, [fullFlattenedItems]);
+
+  // Shared helper to calculate max folder descendant depth
+  // Only counts folder depths, not items (columns/metrics)
+  const getMaxFolderDescendantDepth = useCallback(
+    (parentId: string, baseDepth: number): number => {
+      const children = childrenByParentId.get(parentId);
+      if (!children || children.length === 0) {
+        return baseDepth;
+      }
+      let maxDepth = baseDepth;
+      for (const child of children) {
+        if (child.type === FoldersEditorItemType.Folder) {
+          maxDepth = Math.max(maxDepth, child.depth);
+          maxDepth = Math.max(
+            maxDepth,
+            getMaxFolderDescendantDepth(child.uuid, child.depth),
+          );
+        }
+      }
+      return maxDepth;
+    },
+    [childrenByParentId],
+  );
+
   const resetDragState = useCallback(() => {
     setActiveId(null);
     setOverId(null);
@@ -151,13 +201,7 @@ export function useDragHandlers({
         setCurrentDropTargetId(newParentId);
       }
     },
-    [
-      activeId,
-      overId,
-      flattenedItems,
-      flattenedItemsIndexMap,
-      setCurrentDropTargetId,
-    ],
+    [activeId, overId, flattenedItems, flattenedItemsIndexMap],
   );
 
   const handleDragOver = useCallback(
@@ -185,7 +229,7 @@ export function useDragHandlers({
         setCurrentDropTargetId(null);
       }
     },
-    [activeId, flattenedItems, flattenedItemsIndexMap, setCurrentDropTargetId],
+    [activeId, flattenedItems, flattenedItemsIndexMap],
   );
 
   const handleDragEnd = ({ active, over }: DragEndEvent) => {
@@ -208,19 +252,17 @@ export function useDragHandlers({
       }
     }
 
-    const activeIndex = fullFlattenedItems.findIndex(
-      ({ uuid }) => uuid === active.id,
-    );
-    const overIndex = fullFlattenedItems.findIndex(
-      ({ uuid }) => uuid === targetOverId,
-    );
+    const activeIndex = fullItemsIndexMap.get(active.id as string) ?? -1;
+    const overIndex = fullItemsIndexMap.get(targetOverId as string) ?? -1;
 
     if (activeIndex === -1 || overIndex === -1) {
       return;
     }
 
+    // Use Set for O(1) lookup instead of Array.includes
+    const itemsBeingDraggedSet = new Set(itemsBeingDragged);
     const draggedItems = fullFlattenedItems.filter((item: FlattenedTreeItem) =>
-      itemsBeingDragged.includes(item.uuid),
+      itemsBeingDraggedSet.has(item.uuid),
     );
 
     let projectedPosition = getProjection(
@@ -229,6 +271,7 @@ export function useDragHandlers({
       targetOverId,
       finalOffsetLeft,
       DRAG_INDENTATION_WIDTH,
+      flattenedItemsIndexMap,
     );
 
     if (isEmptyDrop) {
@@ -250,9 +293,21 @@ export function useDragHandlers({
       }
     }
 
-    const hasNonFolderItems = draggedItems.some(
-      item => item.type !== FoldersEditorItemType.Folder,
-    );
+    // Single pass to gather info about dragged items
+    let hasNonFolderItems = false;
+    let hasDraggedFolder = false;
+    let hasDraggedDefaultFolder = false;
+    for (const item of draggedItems) {
+      if (item.type === FoldersEditorItemType.Folder) {
+        hasDraggedFolder = true;
+        if (isDefaultFolder(item.uuid)) {
+          hasDraggedDefaultFolder = true;
+        }
+      } else {
+        hasNonFolderItems = true;
+      }
+    }
+
     if (hasNonFolderItems) {
       if (!projectedPosition || !projectedPosition.parentId) {
         return;
@@ -260,9 +315,7 @@ export function useDragHandlers({
     }
 
     if (projectedPosition && projectedPosition.parentId) {
-      const targetFolder = fullFlattenedItems.find(
-        ({ uuid }) => uuid === projectedPosition.parentId,
-      );
+      const targetFolder = fullItemsByUuid.get(projectedPosition.parentId);
 
       if (targetFolder && isDefaultFolder(targetFolder.uuid)) {
         const isDefaultMetricsFolder =
@@ -293,55 +346,13 @@ export function useDragHandlers({
       }
     }
 
-    const hasDraggedDefaultFolder = draggedItems.some(
-      item =>
-        item.type === FoldersEditorItemType.Folder &&
-        isDefaultFolder(item.uuid),
-    );
     if (hasDraggedDefaultFolder && projectedPosition?.parentId) {
       addWarningToast(t('Default folders cannot be nested'));
       return;
     }
 
     // Check max depth for folders
-    const hasDraggedFolder = draggedItems.some(
-      item => item.type === FoldersEditorItemType.Folder,
-    );
     if (hasDraggedFolder && projectedPosition) {
-      // Build a children map for O(1) lookups
-      const childrenByParentId = new Map<string, FlattenedTreeItem[]>();
-      fullFlattenedItems.forEach((item: FlattenedTreeItem) => {
-        if (item.parentId) {
-          const children = childrenByParentId.get(item.parentId) ?? [];
-          children.push(item);
-          childrenByParentId.set(item.parentId, children);
-        }
-      });
-
-      // Calculate the maximum depth among FOLDER descendants only
-      // (items like columns/metrics don't count toward the folder nesting 
limit)
-      const getMaxFolderDescendantDepth = (
-        parentId: string,
-        baseDepth: number,
-      ): number => {
-        const children = childrenByParentId.get(parentId);
-        if (!children || children.length === 0) {
-          return baseDepth;
-        }
-        let maxDepth = baseDepth;
-        for (const child of children) {
-          // Only count folder depths, not items (columns/metrics)
-          if (child.type === FoldersEditorItemType.Folder) {
-            maxDepth = Math.max(maxDepth, child.depth);
-            maxDepth = Math.max(
-              maxDepth,
-              getMaxFolderDescendantDepth(child.uuid, child.depth),
-            );
-          }
-        }
-        return maxDepth;
-      };
-
       for (const draggedItem of draggedItems) {
         if (draggedItem.type === FoldersEditorItemType.Folder) {
           const currentDepth = draggedItem.depth;
@@ -390,8 +401,10 @@ export function useDragHandlers({
         parentId: string,
         parentDepthChange: number,
       ) => {
-        fullFlattenedItems.forEach((item: FlattenedTreeItem) => {
-          if (item.parentId === parentId && !itemsToUpdate.has(item.uuid)) {
+        const children = childrenByParentId.get(parentId);
+        if (!children) return;
+        for (const item of children) {
+          if (!itemsToUpdate.has(item.uuid)) {
             itemsToUpdate.set(item.uuid, {
               depth: item.depth + parentDepthChange,
               parentId: undefined,
@@ -400,7 +413,7 @@ export function useDragHandlers({
               collectDescendants(item.uuid, parentDepthChange);
             }
           }
-        });
+        }
       };
 
       draggedItems.forEach((item: FlattenedTreeItem) => {
@@ -427,14 +440,16 @@ export function useDragHandlers({
     const itemsToMoveIds = new Set(itemsBeingDragged);
 
     const collectDescendantIds = (parentId: string) => {
-      fullFlattenedItems.forEach((item: FlattenedTreeItem) => {
-        if (item.parentId === parentId && !itemsToMoveIds.has(item.uuid)) {
+      const children = childrenByParentId.get(parentId);
+      if (!children) return;
+      for (const item of children) {
+        if (!itemsToMoveIds.has(item.uuid)) {
           itemsToMoveIds.add(item.uuid);
           if (item.type === FoldersEditorItemType.Folder) {
             collectDescendantIds(item.uuid);
           }
         }
-      });
+      }
     };
 
     draggedItems.forEach((item: FlattenedTreeItem) => {
@@ -443,17 +458,18 @@ export function useDragHandlers({
       }
     });
 
+    // Indices are already in ascending order since we iterate 
fullFlattenedItems sequentially
     const itemsToMoveIndices: number[] = [];
     fullFlattenedItems.forEach((item: FlattenedTreeItem, idx: number) => {
       if (itemsToMoveIds.has(item.uuid)) {
         itemsToMoveIndices.push(idx);
       }
     });
-    itemsToMoveIndices.sort((a, b) => a - b);
 
     const subtree = itemsToMoveIndices.map(idx => newItems[idx]);
+    const itemsToMoveIndicesSet = new Set(itemsToMoveIndices);
     const remaining = newItems.filter(
-      (_: FlattenedTreeItem, idx: number) => !itemsToMoveIndices.includes(idx),
+      (_: FlattenedTreeItem, idx: number) => !itemsToMoveIndicesSet.has(idx),
     );
 
     let insertionIndex = 0;
@@ -537,48 +553,12 @@ export function useDragHandlers({
       return forbidden;
     }
 
-    // Build a children map for O(1) lookups instead of O(n) scans
-    const childrenByParentId = new Map<string, FlattenedTreeItem[]>();
-    const itemsByUuid = new Map<string, FlattenedTreeItem>();
-    fullFlattenedItems.forEach((item: FlattenedTreeItem) => {
-      itemsByUuid.set(item.uuid, item);
-      if (item.parentId) {
-        const children = childrenByParentId.get(item.parentId) ?? [];
-        children.push(item);
-        childrenByParentId.set(item.parentId, children);
-      }
-    });
-
-    // Helper to calculate max FOLDER descendant depth offset (items don't 
count)
-    const getMaxFolderDescendantDepthOffset = (
-      parentId: string,
-      baseDepth: number,
-    ): number => {
-      const children = childrenByParentId.get(parentId);
-      if (!children || children.length === 0) {
-        return 0;
-      }
-      let maxOffset = 0;
-      for (const child of children) {
-        // Only count folder depths, not items (columns/metrics)
-        if (child.type === FoldersEditorItemType.Folder) {
-          const offset = child.depth - baseDepth;
-          maxOffset = Math.max(maxOffset, offset);
-          maxOffset = Math.max(
-            maxOffset,
-            getMaxFolderDescendantDepthOffset(child.uuid, baseDepth),
-          );
-        }
-      }
-      return maxOffset;
-    };
-
     const draggedTypes = new Set<FoldersEditorItemType>();
     let hasDraggedDefaultFolder = false;
     let maxDraggedFolderDescendantOffset = 0;
 
     draggedItemIds.forEach((id: string) => {
-      const item = itemsByUuid.get(id);
+      const item = fullItemsByUuid.get(id);
       if (item) {
         draggedTypes.add(item.type);
         if (
@@ -589,10 +569,11 @@ export function useDragHandlers({
         }
         // Track the deepest folder descendant offset for dragged folders
         if (item.type === FoldersEditorItemType.Folder) {
-          const descendantOffset = getMaxFolderDescendantDepthOffset(
+          const maxDescendantDepth = getMaxFolderDescendantDepth(
             item.uuid,
             item.depth,
           );
+          const descendantOffset = maxDescendantDepth - item.depth;
           maxDraggedFolderDescendantOffset = Math.max(
             maxDraggedFolderDescendantOffset,
             descendantOffset,
@@ -656,7 +637,12 @@ export function useDragHandlers({
     });
 
     return forbidden;
-  }, [draggedItemIds, fullFlattenedItems]);
+  }, [
+    draggedItemIds,
+    fullFlattenedItems,
+    fullItemsByUuid,
+    getMaxFolderDescendantDepth,
+  ]);
 
   return {
     isDragging: activeId !== null,
@@ -667,6 +653,7 @@ export function useDragHandlers({
     dragOverlayItems,
     forbiddenDropFolderIds,
     currentDropTargetId,
+    fullItemsByUuid,
     handleDragStart,
     handleDragMove,
     handleDragOver,
diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/index.tsx 
b/superset-frontend/src/components/Datasource/FoldersEditor/index.tsx
index 8fe2f69b687..7325569c50c 100644
--- a/superset-frontend/src/components/Datasource/FoldersEditor/index.tsx
+++ b/superset-frontend/src/components/Datasource/FoldersEditor/index.tsx
@@ -38,7 +38,6 @@ import {
   flattenTree,
   buildTree,
   removeChildrenOf,
-  getChildCount,
   serializeForAPI,
 } from './treeUtils';
 import {
@@ -93,23 +92,19 @@ export default function FoldersEditor({
 
   const fullFlattenedItems = useMemo(() => flattenTree(items), [items]);
 
-  const collapsedFolderIds = useMemo(
-    () =>
-      fullFlattenedItems.reduce<UniqueIdentifier[]>(
-        (acc, { uuid, type, children }) => {
-          if (
-            type === FoldersEditorItemType.Folder &&
-            collapsedIds.has(uuid) &&
-            children?.length
-          ) {
-            return [...acc, uuid];
-          }
-          return acc;
-        },
-        [],
-      ),
-    [fullFlattenedItems, collapsedIds],
-  );
+  const collapsedFolderIds = useMemo(() => {
+    const result: UniqueIdentifier[] = [];
+    for (const { uuid, type, children } of fullFlattenedItems) {
+      if (
+        type === FoldersEditorItemType.Folder &&
+        collapsedIds.has(uuid) &&
+        children?.length
+      ) {
+        result.push(uuid);
+      }
+    }
+    return result;
+  }, [fullFlattenedItems, collapsedIds]);
 
   const computeFlattenedItems = useCallback(
     (activeId: UniqueIdentifier | null) =>
@@ -150,13 +145,13 @@ export default function FoldersEditor({
     dragOverlayItems,
     forbiddenDropFolderIds,
     currentDropTargetId,
+    fullItemsByUuid,
     handleDragStart,
     handleDragMove,
     handleDragOver,
     handleDragEnd,
     handleDragCancel,
   } = useDragHandlers({
-    items,
     setItems,
     computeFlattenedItems,
     fullFlattenedItems,
@@ -186,19 +181,19 @@ export default function FoldersEditor({
 
   const allVisibleSelected = useMemo(() => {
     const selectableItems = Array.from(visibleItemIds).filter(id => {
-      const item = fullFlattenedItems.find(i => i.uuid === id);
+      const item = fullItemsByUuid.get(id);
       return item && item.type !== FoldersEditorItemType.Folder;
     });
     return (
       selectableItems.length > 0 &&
       selectableItems.every(id => selectedItemIds.has(id))
     );
-  }, [fullFlattenedItems, visibleItemIds, selectedItemIds]);
+  }, [fullItemsByUuid, visibleItemIds, selectedItemIds]);
 
-  const handleSelectAll = () => {
+  const handleSelectAll = useCallback(() => {
     const itemsToSelect = new Set(
       Array.from(visibleItemIds).filter(id => {
-        const item = fullFlattenedItems.find(i => i.uuid === id);
+        const item = fullItemsByUuid.get(id);
         return item && item.type !== FoldersEditorItemType.Folder;
       }),
     );
@@ -208,7 +203,7 @@ export default function FoldersEditor({
     } else {
       setSelectedItemIds(itemsToSelect);
     }
-  };
+  }, [visibleItemIds, fullItemsByUuid, allVisibleSelected]);
 
   const handleResetToDefault = () => {
     setShowResetConfirm(true);
@@ -382,13 +377,20 @@ export default function FoldersEditor({
 
   const folderChildCounts = useMemo(() => {
     const counts = new Map<string, number>();
-    flattenedItems.forEach(item => {
+    // Initialize all folders with 0
+    for (const item of flattenedItems) {
       if (item.type === FoldersEditorItemType.Folder) {
-        counts.set(item.uuid, getChildCount(items, item.uuid));
+        counts.set(item.uuid, 0);
       }
-    });
+    }
+    // Single pass: count children by parentId
+    for (const item of flattenedItems) {
+      if (item.parentId && counts.has(item.parentId)) {
+        counts.set(item.parentId, counts.get(item.parentId)! + 1);
+      }
+    }
     return counts;
-  }, [flattenedItems, items]);
+  }, [flattenedItems]);
 
   return (
     <FoldersContainer>
diff --git 
a/superset-frontend/src/components/Datasource/FoldersEditor/treeUtils.ts 
b/superset-frontend/src/components/Datasource/FoldersEditor/treeUtils.ts
index bdea03e4257..4938ea4dcbc 100644
--- a/superset-frontend/src/components/Datasource/FoldersEditor/treeUtils.ts
+++ b/superset-frontend/src/components/Datasource/FoldersEditor/treeUtils.ts
@@ -94,23 +94,31 @@ export function getProjection(
 
   let previousItem: FlattenedTreeItem | undefined;
   let nextItem: FlattenedTreeItem | undefined;
+  let previousItemIndex: number;
+  let nextItemIndex: number;
 
   if (activeItemIndex < overItemIndex) {
-    previousItem = items[overItemIndex];
-    nextItem = items[overItemIndex + 1];
+    previousItemIndex = overItemIndex;
+    nextItemIndex = overItemIndex + 1;
   } else if (activeItemIndex > overItemIndex) {
-    previousItem = items[overItemIndex - 1];
-    nextItem = items[overItemIndex];
+    previousItemIndex = overItemIndex - 1;
+    nextItemIndex = overItemIndex;
   } else {
-    previousItem = items[overItemIndex - 1];
-    nextItem = items[overItemIndex + 1];
+    previousItemIndex = overItemIndex - 1;
+    nextItemIndex = overItemIndex + 1;
   }
 
+  previousItem = items[previousItemIndex];
+  nextItem = items[nextItemIndex];
+
+  // Skip over the active item if it's adjacent
   if (previousItem?.uuid === activeId) {
-    previousItem = items[items.indexOf(previousItem) - 1];
+    previousItemIndex -= 1;
+    previousItem = items[previousItemIndex];
   }
   if (nextItem?.uuid === activeId) {
-    nextItem = items[items.indexOf(nextItem) + 1];
+    nextItemIndex += 1;
+    nextItem = items[nextItemIndex];
   }
 
   const dragDepth = getDragDepth(dragOffset, indentationWidth);
@@ -128,7 +136,7 @@ export function getProjection(
   let parentId: string | null = null;
   if (depth > 0 && previousItem) {
     if (depth === previousItem.depth) {
-      parentId = previousItem.parentId;
+      ({ parentId } = previousItem);
     } else if (depth > previousItem.depth) {
       parentId = previousItem.uuid;
     } else {
@@ -136,7 +144,7 @@ export function getProjection(
         activeItemIndex < overItemIndex ? overItemIndex : overItemIndex - 1;
       for (let i = searchEnd; i >= 0; i -= 1) {
         if (items[i].uuid !== activeId && items[i].depth === depth) {
-          parentId = items[i].parentId;
+          ({ parentId } = items[i]);
           break;
         }
       }
@@ -191,11 +199,10 @@ export function flattenTree(items: TreeItem[]): 
FlattenedTreeItem[] {
 
 export function buildTree(flattenedItems: FlattenedTreeItem[]): TreeItem[] {
   const root: TreeItem[] = [];
-  const nodes: Record<string, TreeItem> = {};
-
-  const sortedItems = [...flattenedItems].sort((a, b) => a.depth - b.depth);
+  const nodes = new Map<string, TreeItem>();
 
-  for (const item of sortedItems) {
+  // First pass: create all nodes
+  for (const item of flattenedItems) {
     const { uuid, type, name, description } = item;
 
     const treeItem: TreeItem =
@@ -213,17 +220,18 @@ export function buildTree(flattenedItems: 
FlattenedTreeItem[]): TreeItem[] {
             name,
           } as DatasourceFolderItem);
 
-    nodes[uuid] = treeItem;
+    nodes.set(uuid, treeItem);
   }
 
-  for (const item of sortedItems) {
+  // Second pass: link children to parents (iteration order preserves 
structure)
+  for (const item of flattenedItems) {
     const { uuid, parentId } = item;
-    const treeItem = nodes[uuid];
+    const treeItem = nodes.get(uuid)!;
 
     if (!parentId) {
       root.push(treeItem);
     } else {
-      const parent = nodes[parentId];
+      const parent = nodes.get(parentId);
       if (
         parent &&
         parent.type === FoldersEditorItemType.Folder &&
@@ -267,12 +275,12 @@ export function removeChildrenOf(
   items: FlattenedTreeItem[],
   ids: UniqueIdentifier[],
 ): FlattenedTreeItem[] {
-  const excludeParentIds = [...ids];
+  const excludeParentIds = new Set<UniqueIdentifier>(ids);
 
   return items.filter(item => {
-    if (item.parentId && excludeParentIds.includes(item.parentId)) {
+    if (item.parentId && excludeParentIds.has(item.parentId)) {
       if (item.children?.length) {
-        excludeParentIds.push(item.uuid);
+        excludeParentIds.add(item.uuid);
       }
       return false;
     }

Reply via email to