Browse Source

refine tree (#427)

* refactor tree

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* refine part icons

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

---------

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>
ryjiang 1 year ago
parent
commit
e255aef68c

+ 0 - 6
client/src/assets/icons/collecion.svg

@@ -1,6 +0,0 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M13.75 7.8333L6.25 3.5083" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M17.5 13.3333V6.66663C17.4997 6.37435 17.4225 6.0873 17.2763 5.83426C17.13 5.58122 16.9198 5.37109 16.6667 5.22496L10.8333 1.89163C10.58 1.74535 10.2926 1.66833 10 1.66833C9.70744 1.66833 9.42003 1.74535 9.16667 1.89163L3.33333 5.22496C3.08022 5.37109 2.86998 5.58122 2.72372 5.83426C2.57745 6.0873 2.5003 6.37435 2.5 6.66663V13.3333C2.5003 13.6256 2.57745 13.9126 2.72372 14.1657C2.86998 14.4187 3.08022 14.6288 3.33333 14.775L9.16667 18.1083C9.42003 18.2546 9.70744 18.3316 10 18.3316C10.2926 18.3316 10.58 18.2546 10.8333 18.1083L16.6667 14.775C16.9198 14.6288 17.13 14.4187 17.2763 14.1657C17.4225 13.9126 17.4997 13.6256 17.5 13.3333Z" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M2.72461 5.80005L9.99961 10.0084L17.2746 5.80005" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M10 18.4V10" stroke="#82838E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
-</svg>

File diff suppressed because it is too large
+ 0 - 1
client/src/assets/icons/system.svg


+ 3 - 3
client/src/assets/imgs/pic.svg

@@ -1,5 +1,5 @@
 <svg width="101" height="26" viewBox="0 0 101 26" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M42.3543 1.34849C42.2296 0.941106 41.7918 0.910427 41.6336 1.29801L35.1123 17.2803C34.6944 18.3045 33.5818 18.3875 33.0751 17.4323L29.3395 10.39C29.1783 10.0861 28.8286 10.0943 28.6761 10.4057L24.79 18.3423C24.5718 18.7881 24.1916 19.0583 23.7829 19.0583H0V18.034H23.7829C23.9191 18.034 24.0459 17.9439 24.1186 17.7954L28.0047 9.85872C28.4621 8.92467 29.5113 8.89982 29.9949 9.81156L33.7305 16.8539C33.8994 17.1723 34.2702 17.1446 34.4096 16.8032L40.9309 0.820936C41.4053 -0.341793 42.7189 -0.249776 43.093 0.972398L48.2151 17.7099C48.2749 17.9055 48.4214 18.034 48.5844 18.034H54V19.0583H48.5844C48.0953 19.0583 47.656 18.6728 47.4764 18.086L42.3543 1.34849Z" fill="#AEAEBB"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M93.167 11.3491C93.0008 10.9756 92.4919 10.9449 92.2837 11.2959L88.5732 17.551C88.0307 18.4656 86.7582 18.545 86.1102 17.7047L84.2853 15.3383C84.0802 15.0723 83.6824 15.0801 83.4875 15.3539L81.2998 18.4273C81.0176 18.8237 80.5661 19.0583 80.0856 19.0583H64V18.0392H80.0856C80.2458 18.0392 80.3963 17.961 80.4903 17.8288L82.6781 14.7554C83.2628 13.934 84.4562 13.9106 85.0716 14.7085L86.8965 17.0749C87.1125 17.355 87.5366 17.3286 87.7174 17.0237L91.428 10.7686L91.8559 11.0322L91.428 10.7686C92.0525 9.71579 93.5792 9.80776 94.0777 10.9282L97.1083 17.74C97.1894 17.9222 97.3674 18.0392 97.5637 18.0392H101V19.0583H97.5637C96.975 19.0583 96.4407 18.7073 96.1976 18.1609L93.167 11.3491Z" fill="#AEAEBB"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M62.2971 8.70321L62.7051 7.81494L63.6133 8.23464L63.2054 9.12291L62.2971 8.70321ZM60.6653 12.2563L61.4812 10.4797L62.3895 10.8994L61.5736 12.676L60.6653 12.2563ZM59.0335 15.8094L59.8494 14.0328L60.7576 14.4525L59.9417 16.2291L59.0335 15.8094ZM57.4017 19.3624L58.2176 17.5859L59.1258 18.0056L58.3099 19.7821L57.4017 19.3624ZM55.7698 22.9155L56.5857 21.139L57.494 21.5587L56.6781 23.3352L55.7698 22.9155ZM54.546 25.5803L54.9539 24.692L55.8622 25.1117L55.4542 26L54.546 25.5803Z" fill="#AEAEBB"/>
+<path fillRule="evenodd" clipRule="evenodd" d="M42.3543 1.34849C42.2296 0.941106 41.7918 0.910427 41.6336 1.29801L35.1123 17.2803C34.6944 18.3045 33.5818 18.3875 33.0751 17.4323L29.3395 10.39C29.1783 10.0861 28.8286 10.0943 28.6761 10.4057L24.79 18.3423C24.5718 18.7881 24.1916 19.0583 23.7829 19.0583H0V18.034H23.7829C23.9191 18.034 24.0459 17.9439 24.1186 17.7954L28.0047 9.85872C28.4621 8.92467 29.5113 8.89982 29.9949 9.81156L33.7305 16.8539C33.8994 17.1723 34.2702 17.1446 34.4096 16.8032L40.9309 0.820936C41.4053 -0.341793 42.7189 -0.249776 43.093 0.972398L48.2151 17.7099C48.2749 17.9055 48.4214 18.034 48.5844 18.034H54V19.0583H48.5844C48.0953 19.0583 47.656 18.6728 47.4764 18.086L42.3543 1.34849Z" fill="#AEAEBB"/>
+<path fillRule="evenodd" clipRule="evenodd" d="M93.167 11.3491C93.0008 10.9756 92.4919 10.9449 92.2837 11.2959L88.5732 17.551C88.0307 18.4656 86.7582 18.545 86.1102 17.7047L84.2853 15.3383C84.0802 15.0723 83.6824 15.0801 83.4875 15.3539L81.2998 18.4273C81.0176 18.8237 80.5661 19.0583 80.0856 19.0583H64V18.0392H80.0856C80.2458 18.0392 80.3963 17.961 80.4903 17.8288L82.6781 14.7554C83.2628 13.934 84.4562 13.9106 85.0716 14.7085L86.8965 17.0749C87.1125 17.355 87.5366 17.3286 87.7174 17.0237L91.428 10.7686L91.8559 11.0322L91.428 10.7686C92.0525 9.71579 93.5792 9.80776 94.0777 10.9282L97.1083 17.74C97.1894 17.9222 97.3674 18.0392 97.5637 18.0392H101V19.0583H97.5637C96.975 19.0583 96.4407 18.7073 96.1976 18.1609L93.167 11.3491Z" fill="#AEAEBB"/>
+<path fillRule="evenodd" clipRule="evenodd" d="M62.2971 8.70321L62.7051 7.81494L63.6133 8.23464L63.2054 9.12291L62.2971 8.70321ZM60.6653 12.2563L61.4812 10.4797L62.3895 10.8994L61.5736 12.676L60.6653 12.2563ZM59.0335 15.8094L59.8494 14.0328L60.7576 14.4525L59.9417 16.2291L59.0335 15.8094ZM57.4017 19.3624L58.2176 17.5859L59.1258 18.0056L58.3099 19.7821L57.4017 19.3624ZM55.7698 22.9155L56.5857 21.139L57.494 21.5587L56.6781 23.3352L55.7698 22.9155ZM54.546 25.5803L54.9539 24.692L55.8622 25.1117L55.4542 26L54.546 25.5803Z" fill="#AEAEBB"/>
 </svg>

+ 0 - 117
client/src/components/CustomTree/index.tsx

@@ -1,117 +0,0 @@
-import TreeView from '@material-ui/lab/TreeView';
-import TreeItem from '@material-ui/lab/TreeItem';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
-import ChevronRightIcon from '@material-ui/icons/ChevronRight';
-
-export type TreeNodeType = 'db' | 'collection' | 'partition' | 'segment';
-
-export interface CustomTreeItem {
-  children?: CustomTreeItem[];
-  id: string;
-  name: string;
-  type: TreeNodeType;
-  expanded?: boolean;
-}
-
-interface CustomToolProps {
-  data: CustomTreeItem;
-  onNodeToggle?: (event: React.ChangeEvent<{}>, nodeIds: string[]) => void;
-  multiSelect?: true;
-  disableSelection?: boolean;
-  defaultSelected?: string[];
-  onNodeClick?: (node: CustomTreeItem) => void;
-}
-
-// get expanded nodes from data
-const getExpanded = (nodes: CustomTreeItem[]) => {
-  const expanded: string[] = [];
-  nodes.forEach(node => {
-    if (node.expanded) {
-      expanded.push(node.id);
-    }
-    if (node.children && node.children.length > 0) {
-      expanded.push(...getExpanded(node.children));
-    }
-  });
-  return expanded;
-};
-
-const CustomTree: React.FC<CustomToolProps> = ({
-  data,
-  onNodeToggle,
-  multiSelect,
-  disableSelection,
-  defaultSelected = [],
-  onNodeClick,
-}) => {
-  // UI data
-  const expanded = getExpanded([data]);
-  // render children
-  const renderTree = (nodes: CustomTreeItem[]) => {
-    return nodes.map(node => {
-      if (node.children && node.children.length > 0) {
-        return (
-          <TreeItem
-            key={node.id}
-            nodeId={node.id}
-            label={node.name}
-            onClick={event => {
-              event.stopPropagation();
-              if (onNodeClick) {
-                onNodeClick(node);
-              }
-            }}
-          >
-            {renderTree(node.children)}
-          </TreeItem>
-        );
-      }
-
-      return (
-        <TreeItem
-          key={node.id}
-          nodeId={node.id}
-          label={node.name}
-          onClick={event => {
-            event.stopPropagation();
-            if (onNodeClick) {
-              onNodeClick(node);
-            }
-          }}
-        />
-      );
-    });
-  };
-
-  return (
-    <TreeView
-      defaultCollapseIcon={<ExpandMoreIcon />}
-      defaultExpandIcon={<ChevronRightIcon />}
-      expanded={expanded}
-      onNodeToggle={onNodeToggle}
-      selected={defaultSelected}
-      multiSelect={multiSelect}
-      disableSelection={disableSelection}
-    >
-      {data && (
-        <TreeItem
-          key={data.id}
-          nodeId={data.id}
-          label={data.name}
-          onClick={event => {
-            event.stopPropagation();
-            if (onNodeClick) {
-              onNodeClick(data);
-            }
-          }}
-        >
-          {data.children && data.children.length > 0
-            ? renderTree(data.children)
-            : [<div key="stub" />]}
-        </TreeItem>
-      )}
-    </TreeView>
-  );
-};
-
-export default CustomTree;

File diff suppressed because it is too large
+ 77 - 19
client/src/components/icons/Icons.tsx


+ 1 - 0
client/src/components/layout/Header.tsx

@@ -114,6 +114,7 @@ const Header: FC<HeaderType> = props => {
                 const database = e.target.value as string;
                 await useDatabase(database);
                 setDatabase(database);
+                navigate(`/databases/${database}`);
               }}
               options={dbOptions}
               variant="filled"

+ 14 - 91
client/src/pages/databases/Databases.tsx

@@ -1,14 +1,11 @@
-import { useEffect, useState, useCallback, useRef, useContext } from 'react';
-import { useParams, useNavigate } from 'react-router-dom';
+import { useEffect, useContext } from 'react';
+import { useParams } from 'react-router-dom';
 import { useTranslation } from 'react-i18next';
 import { makeStyles, Theme } from '@material-ui/core';
 import { useNavigationHook } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import RouteTabList from '@/components/customTabList/RouteTabList';
-import CustomTree, {
-  CustomTreeItem,
-  TreeNodeType,
-} from '@/components/CustomTree';
+import DatabaseTree from '@/pages/databases/tree';
 import { ITab } from '@/components/customTabList/Types';
 import Partitions from '../partitions/Partitions';
 import Schema from '../schema/Schema';
@@ -30,6 +27,7 @@ const useStyles = makeStyles((theme: Theme) => ({
     flexGrow: 0,
     flexShrink: 0,
     overflow: 'auto',
+    boxSizing: 'border-box',
   },
   tab: {
     flexGrow: 1,
@@ -39,25 +37,14 @@ const useStyles = makeStyles((theme: Theme) => ({
 }));
 
 const Databases = () => {
-  // UI stats
-  const [localLoading, setLoading] = useState(false);
-  const [collectionsTree, setCollectionsTree] = useState<CustomTreeItem>({
-    id: '',
-    name: '',
-    type: 'db',
-    children: [],
-  });
-  const [selectedTarget, setSelectedTarget] = useState<string[]>();
-
   // get current collection from url
+  const params = useParams();
   const {
-    databaseItem = '',
     databaseName = '',
     collectionName = '',
-    collectionItem = '',
-  } = useParams();
-  // get navigate
-  const navigate = useNavigate();
+    collectionPage = '',
+  } = params;
+
   // update navigation
   useNavigationHook(ALL_ROUTER_TYPES.COLLECTION_DETAIL, { collectionName });
   // get style
@@ -68,69 +55,6 @@ const Databases = () => {
   // i18n
   const { t: collectionTrans } = useTranslation('collection');
 
-  // fetch data callback
-  const refresh = useCallback(async () => {
-    try {
-      // set UI loading
-      setLoading(true);
-
-      // format tree data
-      const children = collections.map(c => {
-        return {
-          id: c.collection_name,
-          name: c.collection_name,
-          type: 'collection' as TreeNodeType,
-        };
-      });
-      // update tree
-      setCollectionsTree({
-        id: database,
-        name: database,
-        expanded: children.length > 0,
-        type: 'db',
-        children: children,
-      });
-    } finally {
-      setLoading(false);
-    }
-  }, [setCollectionsTree, database, collections]);
-
-  // const onNodeToggle = (event: React.ChangeEvent<{}>, nodeIds: string[]) => {
-  //   console.log('onNodeToggle', event, nodeIds);
-  // };
-
-  const onNodeClick = (node: CustomTreeItem) => {
-    navigate(
-      node.type === 'db'
-        ? `/databases/${database}/${databaseItem || 'collections'}`
-        : `/databases/${database}/${node.id}/${collectionItem || 'data'}`
-    );
-  };
-
-  // change database should go to it's page
-  const firstRender = useRef(false);
-
-  // change database should go to it's page
-  useEffect(() => {
-    if (firstRender.current) {
-      navigate(`/databases/${database}/${databaseItem || 'collections'}`);
-    } else {
-      firstRender.current = true;
-    }
-  }, [database]);
-
-  // fetch data
-  useEffect(() => {
-    refresh();
-  }, [refresh]);
-
-  // active default selected
-  useEffect(() => {
-    setSelectedTarget(
-      collectionName ? [collectionName] : [databaseName || database]
-    );
-  }, [collectionName, database, databaseName]);
-
   const dbTab: ITab[] = [
     {
       label: collectionTrans('collections'),
@@ -165,21 +89,20 @@ const Databases = () => {
     },
   ];
   // get active collection tab
-  const activeColTab = collectionTabs.findIndex(t => t.path === collectionItem);
+  const activeColTab = collectionTabs.findIndex(t => t.path === collectionPage);
 
   // render
-  const uiLoading = localLoading || loading;
   return (
     <section className={`page-wrapper ${classes.wrapper}`}>
       <section className={classes.tree}>
-        {uiLoading ? (
+        {loading ? (
           `loading`
         ) : (
-          <CustomTree
+          <DatabaseTree
             key="collections"
-            data={collectionsTree}
-            defaultSelected={selectedTarget}
-            onNodeClick={onNodeClick}
+            collections={collections}
+            database={database}
+            params={params}
           />
         )}
       </section>

+ 194 - 0
client/src/pages/databases/tree/index.tsx

@@ -0,0 +1,194 @@
+import TreeView from '@material-ui/lab/TreeView';
+import TreeItem from '@material-ui/lab/TreeItem';
+import icons from '@/components/icons/Icons';
+import { makeStyles, Theme } from '@material-ui/core';
+import { useNavigate, Params } from 'react-router-dom';
+import { CollectionObject } from '@server/types';
+
+export type TreeNodeType = 'db' | 'collection' | 'partition' | 'segment';
+
+export interface DatabaseTreeItem {
+  children?: DatabaseTreeItem[];
+  id: string;
+  name: string;
+  type: TreeNodeType;
+  expanded?: boolean;
+}
+
+interface DatabaseToolProps {
+  database: string;
+  collections: CollectionObject[];
+  params: Readonly<Params<string>>;
+}
+
+// get expanded nodes from data
+const getExpanded = (nodes: DatabaseTreeItem[]) => {
+  const expanded: string[] = [];
+  nodes.forEach(node => {
+    if (node.expanded) {
+      expanded.push(node.id);
+    }
+    if (node.children && node.children.length > 0) {
+      expanded.push(...getExpanded(node.children));
+    }
+  });
+  return expanded;
+};
+
+const useStyles = makeStyles((theme: Theme) => ({
+  root: {
+    '& .MuiTreeItem-group': {
+      marginLeft: 0,
+      '& .MuiTreeItem-content': {
+        padding: '0 0 0 16px',
+      },
+    },
+    '& .MuiTreeItem-label:hover': {
+      backgroundColor: 'none',
+    },
+    '& .MuiTreeItem-content': {
+      '&:hover': {
+        backgroundColor: 'rgba(10, 206, 130, 0.08)',
+      },
+      '& .MuiTreeItem-label': {
+        background: 'none',
+      },
+    },
+    '& .Mui-selected': {
+      '& > .MuiTreeItem-content': {
+        backgroundColor: 'rgba(10, 206, 130, 0.08)',
+
+        '& .MuiTreeItem-label': {
+          background: 'none',
+        },
+      },
+      '&:focus': {
+        '& .MuiTreeItem-content': {
+          '& .MuiTreeItem-label': {
+            background: 'none',
+          },
+        },
+      },
+    },
+  },
+  treeItem: {
+    '& .MuiTreeItem-iconContainer': {
+      color: '#888',
+    },
+  },
+}));
+
+const DatabaseTree: React.FC<DatabaseToolProps> = props => {
+  // props
+  const { database, collections, params } = props;
+
+  // format tree data
+  const children = collections.map(c => {
+    return {
+      id: c.collection_name,
+      name: c.collection_name,
+      type: 'collection' as TreeNodeType,
+    };
+  });
+
+  // tree data
+  const tree: DatabaseTreeItem = {
+    id: database,
+    name: database,
+    expanded: children.length > 0,
+    type: 'db',
+    children: children,
+  };
+
+  // UI data
+  const expanded = getExpanded([tree]);
+  // Icons
+  const DatabaseIcon = icons.database;
+  const CollectionIcon = icons.navCollection;
+
+  // hooks
+  const navigate = useNavigate();
+  const classes = useStyles();
+
+  // on node click
+  const onNodeClick = (node: DatabaseTreeItem) => {
+    navigate(
+      node.type === 'db'
+        ? `/databases/${database}/${params.databasePage || 'collections'}`
+        : `/databases/${database}/${node.id}/${params.collectionPage || 'data'}`
+    );
+  };
+
+  // render children
+  const renderTree = (nodes: DatabaseTreeItem[]) => {
+    return nodes.map(node => {
+      if (node.children && node.children.length > 0) {
+        return (
+          <TreeItem
+            key={node.id}
+            nodeId={node.id}
+            icon={<CollectionIcon />}
+            label={node.name}
+            className={classes.treeItem}
+            onClick={event => {
+              event.stopPropagation();
+              if (onNodeClick) {
+                onNodeClick(node);
+              }
+            }}
+          >
+            {renderTree(node.children)}
+          </TreeItem>
+        );
+      }
+
+      return (
+        <TreeItem
+          key={node.id}
+          nodeId={node.id}
+          icon={<CollectionIcon />}
+          label={node.name}
+          className={classes.treeItem}
+          onClick={event => {
+            event.stopPropagation();
+            if (onNodeClick) {
+              onNodeClick(node);
+            }
+          }}
+        />
+      );
+    });
+  };
+
+  return (
+    <TreeView
+      expanded={expanded}
+      multiSelect={false}
+      disableSelection={false}
+      selected={params.collectionName || params.databaseName}
+      className={classes.root}
+    >
+      {
+        <TreeItem
+          key={tree.id}
+          nodeId={tree.id}
+          label={tree.name}
+          className={classes.treeItem}
+          icon={<DatabaseIcon />}
+          onClick={event => {
+            event.stopPropagation();
+            if (onNodeClick) {
+              onNodeClick(tree);
+            }
+          }}
+        >
+          {tree.children && tree.children.length > 0
+            ? renderTree(tree.children)
+            : [<div key="stub" />]}
+        </TreeItem>
+      }
+    </TreeView>
+  );
+};
+
+export default DatabaseTree;

+ 2 - 2
client/src/router/Router.tsx

@@ -23,12 +23,12 @@ const RouterComponent = () => {
           <Route path="databases" element={<Databases />} />
           <Route path="databases/:databaseName" element={<Databases />} />
           <Route
-            path="databases/:databaseName/:databaseItem"
+            path="databases/:databaseName/:databasePage"
             element={<Databases />}
           />
 
           <Route
-            path="databases/:databaseName/:collectionName/:collectionItem"
+            path="databases/:databaseName/:collectionName/:collectionPage"
             element={<Databases />}
           />
 

Some files were not shown because too many files changed in this diff