Browse Source

Support store UI pref (#575)

* support adjust collection width

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* support store ui pref

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 11 months ago
parent
commit
60541b633c

+ 1 - 0
client/src/consts/index.ts

@@ -3,4 +3,5 @@ export * from './Localstorage';
 export * from './Milvus';
 export * from './Prometheus';
 export * from './Util';
+export * from './ui';
 export * from './default';

+ 1 - 0
client/src/consts/ui.ts

@@ -0,0 +1 @@
+export const DEFAULT_TREE_WIDTH = 230;

+ 36 - 0
client/src/context/Data.tsx

@@ -20,6 +20,7 @@ import {
   DatabaseObject,
 } from '@server/types';
 import { WS_EVENTS, WS_EVENTS_TYPE, LOADING_STATE } from '@server/utils/Const';
+import { DEFAULT_TREE_WIDTH } from '@/consts';
 import { checkIndexing, checkLoading } from '@server/utils/Shared';
 
 export const dataContext = createContext<DataContextType>({
@@ -67,6 +68,12 @@ export const dataContext = createContext<DataContextType>({
   setProperty: async () => {
     return {} as CollectionFullObject;
   },
+  ui: {
+    tree: {
+      width: DEFAULT_TREE_WIDTH,
+    },
+  },
+  setUIPref: () => {},
 });
 
 const { Provider } = dataContext;
@@ -76,6 +83,13 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
   const { authReq, isAuth, clientId, logout, setAuthReq } =
     useContext(authContext);
 
+  // UI preferences
+  const [ui, setUI] = useState({
+    tree: {
+      width: DEFAULT_TREE_WIDTH,
+    },
+  });
+
   // local data state
   const [collections, setCollections] = useState<CollectionObject[]>([]);
   const [connected, setConnected] = useState(false);
@@ -344,6 +358,26 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
     return newCollection;
   };
 
+  // set UI preferences
+  const setUIPref = (pref: DataContextType['ui']) => {
+    setUI(pref);
+    localStorage.setItem('attu.ui.tree.width', String(pref.tree.width));
+  };
+
+  // load UI preferences
+  useEffect(() => {
+    const storedWidth = Number(localStorage.getItem('attu.ui.tree.width'));
+    if (storedWidth) {
+      setUI(prevUI => ({
+        ...prevUI,
+        tree: {
+          ...prevUI.tree,
+          width: storedWidth,
+        },
+      }));
+    }
+  }, []);
+
   useEffect(() => {
     const clear = () => {
       // clear collections
@@ -442,6 +476,8 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
         createAlias,
         dropAlias,
         setProperty,
+        ui,
+        setUIPref,
       }}
     >
       {props.children}

+ 7 - 1
client/src/context/Types.ts

@@ -104,7 +104,6 @@ export type DataContextType = {
   setDatabase: Dispatch<SetStateAction<string>>;
   databases: DatabaseObject[];
   setDatabaseList: Dispatch<SetStateAction<DatabaseObject[]>>;
-  // search UI state
 
   // APIs
   // databases
@@ -142,4 +141,11 @@ export type DataContextType = {
     key: string,
     value: any
   ) => Promise<CollectionFullObject>;
+  // UI preferences
+  ui: {
+    tree: {
+      width: number;
+    };
+  };
+  setUIPref: (pref: DataContextType['ui']) => void;
 };

+ 42 - 10
client/src/pages/databases/Databases.tsx

@@ -27,6 +27,20 @@ const DEFAULT_TREE_WIDTH = 230;
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
     flexDirection: 'row',
+    '&.dragging': {
+      cursor: 'ew-resize',
+      '& $tree': {
+        pointerEvents: 'none',
+        userSelect: 'none',
+      },
+      '& $tab': {
+        pointerEvents: 'none',
+        userSelect: 'none',
+      },
+      '& $dragger': {
+        background: theme.palette.divider,
+      },
+    },
   },
   tree: {
     boxShadow: 'none',
@@ -57,7 +71,6 @@ const useStyles = makeStyles((theme: Theme) => ({
       background: theme.palette.divider,
     },
   },
-
   tab: {
     flexGrow: 1,
     flexShrink: 1,
@@ -76,7 +89,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 // Databases page(tree and tabs)
 const Databases = () => {
   // context
-  const { database, collections, loading, fetchCollection } =
+  const { database, collections, loading, fetchCollection, ui, setUIPref } =
     useContext(dataContext);
 
   // UI state
@@ -85,51 +98,62 @@ const Databases = () => {
   );
 
   // tree ref
+  const [isDragging, setIsDragging] = useState(false);
   const treeRef = useRef<HTMLDivElement>(null);
   const draggerRef = useRef<HTMLDivElement>(null);
 
+  // support dragging tree width
   useEffect(() => {
-    let isDragging = false;
+    // local tree width
     let treeWidth = 0;
     const handleMouseMove = (e: MouseEvent) => {
       requestAnimationFrame(() => {
         // get mouse position
         let mouseX = e.clientX - treeRef.current!.offsetLeft;
 
+        // set min and max width
         treeWidth = Math.max(1, Math.min(mouseX, DEFAULT_TREE_WIDTH));
 
         // set tree width
-        treeRef.current!.style.width = `${treeWidth}px`;
-        draggerRef.current!.classList.toggle('tree-collapsed', true);
+        setUIPref({ tree: { width: treeWidth } });
+        // set dragging true
+        setIsDragging(true);
       });
     };
 
     const handleMouseUp = () => {
-      isDragging = false;
       document.removeEventListener('mousemove', handleMouseMove);
       document.removeEventListener('mouseup', handleMouseUp);
+      // highlight dragger alwasy if width === 1
       draggerRef.current!.classList.toggle('tree-collapsed', treeWidth === 1);
+      // set dragging true
+      setIsDragging(false);
     };
 
     const handleMouseDown = (e: MouseEvent) => {
       const t = e.target as HTMLDivElement;
       if (t && t.dataset.id === 'dragger') {
-        isDragging = true;
+        // set dragging true
+        setIsDragging(true);
         document.addEventListener('mousemove', handleMouseMove);
         document.addEventListener('mouseup', handleMouseUp);
       }
     };
 
+    // add event listener
     document.addEventListener('mousedown', handleMouseDown);
     return () => {
+      // remove event listener
       document.removeEventListener('mousedown', handleMouseDown);
+      // set dragging false
+      setIsDragging(false);
     };
   }, []);
 
   // double click on the dragger, recover default
   const handleDoubleClick = () => {
-    treeRef.current!.style.width = `${DEFAULT_TREE_WIDTH}px`;
     draggerRef.current!.classList.toggle('tree-collapsed', false);
+    setUIPref({ tree: { width: DEFAULT_TREE_WIDTH } });
   };
 
   // init search params
@@ -259,8 +283,16 @@ const Databases = () => {
 
   // render
   return (
-    <section className={`page-wrapper ${classes.wrapper}`}>
-      <section className={classes.tree} ref={treeRef}>
+    <section
+      className={`page-wrapper ${classes.wrapper} ${
+        isDragging ? 'dragging' : ''
+      }`}
+    >
+      <section
+        className={classes.tree}
+        ref={treeRef}
+        style={{ width: ui.tree.width }}
+      >
         {loading ? (
           <StatusIcon type={LoadingType.CREATING} />
         ) : (