import { Button, Input, Tree, TreeItemValue, TreeOpenChangeData, TreeOpenChangeEvent, } from "@fluentui/react-components"; import { ArrowSortDown20Regular, ArrowSortUp20Regular, Home16Regular, Search20Regular } from "@fluentui/react-icons"; import { AuthType } from "AuthType"; import { useTreeStyles } from "Explorer/Controls/TreeComponent/Styles"; import { TreeNode, TreeNodeComponent } from "Explorer/Controls/TreeComponent/TreeNodeComponent"; import { createDatabaseTreeNodes, createResourceTokenTreeNodes } from "Explorer/Tree/treeNodeUtil"; import { useDatabases } from "Explorer/useDatabases"; import { useSelectedNode } from "Explorer/useSelectedNode"; import { isFabricMirrored } from "Platform/Fabric/FabricUtil"; import { userContext } from "UserContext"; import { ReactTabKind, useTabs } from "hooks/useTabs"; import * as React from "react"; import { useEffect, useMemo } from "react"; import shallow from "zustand/shallow"; import { useDatabaseLoadScenario } from "../../Metrics/useMetricPhases"; import Explorer from "../Explorer"; import { useNotebook } from "../Notebook/useNotebook"; export const MyNotebooksTitle = "My Notebooks"; interface ResourceTreeProps { explorer: Explorer; } export const DATA_TREE_LABEL = "DATA"; export const MY_DATA_TREE_LABEL = "MY DATA"; export const SAMPLE_DATA_TREE_LABEL = "SAMPLE DATA"; /** * Top-level tree that has no label, but contains all subtrees */ export const ResourceTree: React.FC = ({ explorer }: ResourceTreeProps): JSX.Element => { const [openItems, setOpenItems] = React.useState([]); const treeStyles = useTreeStyles(); const { isNotebookEnabled } = useNotebook( (state) => ({ isNotebookEnabled: state.isNotebookEnabled, }), shallow, ); // We intentionally avoid using a state selector here because we want to re-render the tree if the active tab changes. const { refreshActiveTab } = useTabs(); const { databases, resourceTokenCollection, sampleDataResourceTokenCollection } = useDatabases((state) => ({ databases: state.databases, resourceTokenCollection: state.resourceTokenCollection, sampleDataResourceTokenCollection: state.sampleDataResourceTokenCollection, })); const databasesFetchedSuccessfully = useDatabases((state) => state.databasesFetchedSuccessfully); const searchText = useDatabases((state) => state.searchText); const setSearchText = useDatabases((state) => state.setSearchText); const sortOrder = useDatabases((state) => state.sortOrder); const setSortOrder = useDatabases((state) => state.setSortOrder); const pinnedDatabaseIds = useDatabases((state) => state.pinnedDatabaseIds); const databaseTreeNodes = useMemo(() => { return userContext.authType === AuthType.ResourceToken ? createResourceTokenTreeNodes(resourceTokenCollection) : createDatabaseTreeNodes( explorer, isNotebookEnabled, databases, refreshActiveTab, searchText, sortOrder, pinnedDatabaseIds, ); }, [ resourceTokenCollection, databases, isNotebookEnabled, refreshActiveTab, searchText, sortOrder, pinnedDatabaseIds, ]); const headerNodes: TreeNode[] = isFabricMirrored() ? [] : [ { id: "home", iconSrc: , label: "Home", isSelected: () => useSelectedNode.getState().selectedNode === undefined && useTabs.getState().activeReactTab === ReactTabKind.Home, onClick: () => { useSelectedNode.getState().setSelectedNode(undefined); useTabs.getState().openAndActivateReactTab(ReactTabKind.Home); }, }, ]; const rootNodes: TreeNode[] = useMemo(() => { return [...headerNodes, ...databaseTreeNodes]; // headerNodes is intentionally excluded — it depends only on isFabricMirrored() which is stable. // eslint-disable-next-line react-hooks/exhaustive-deps }, [databaseTreeNodes]); // Track complete DatabaseLoad scenario (start, tree rendered, interactive) useDatabaseLoadScenario(databaseTreeNodes, databasesFetchedSuccessfully); useEffect(() => { const expandedIds: TreeItemValue[] = []; const collectExpandedIds = (node: TreeNode, parentNodeId: string | undefined): void => { const globalId = parentNodeId === undefined ? node.label : `${parentNodeId}/${node.label}`; if (node.isExpanded) { expandedIds.push(globalId); } if (node.children) { for (const child of node.children) { collectExpandedIds(child, globalId); } } }; rootNodes.forEach((n) => collectExpandedIds(n, undefined)); if (expandedIds.length > 0) { setOpenItems((prevOpenItems) => { const prevSet = new Set(prevOpenItems); const newIds = expandedIds.filter((id) => !prevSet.has(id)); return newIds.length > 0 ? [...prevOpenItems, ...newIds] : prevOpenItems; }); } }, [rootNodes]); const handleOpenChange = (event: TreeOpenChangeEvent, data: TreeOpenChangeData) => setOpenItems(Array.from(data.openItems)); const toggleSortOrder = () => { setSortOrder(sortOrder === "az" ? "za" : "az"); }; return (
{userContext.authType !== AuthType.ResourceToken && databases.length > 0 && (
setSearchText(data?.value || "")} size="small" contentBefore={} style={{ flex: 1 }} />
)} {rootNodes.map((node) => ( ))}
); };