From b2825e2e1e7d48c7978f559a95e9f3466bc299fe Mon Sep 17 00:00:00 2001 From: Jong Eun Lee Date: Fri, 15 Nov 2024 15:13:09 +0800 Subject: [PATCH] feat: NEO sider's toggle button and refactoring --- react/src/components/BAISider.tsx | 184 ++++++-------- .../src/components/MainLayout/MainLayout.tsx | 3 + .../src/components/MainLayout/WebUISider.tsx | 225 +++++++++++------- react/src/components/ReverseThemeProvider.tsx | 3 +- react/src/components/SiderToggleButton.tsx | 76 ++++++ src/components/backend-ai-webui-styles.ts | 4 - 6 files changed, 293 insertions(+), 202 deletions(-) create mode 100644 react/src/components/SiderToggleButton.tsx diff --git a/react/src/components/BAISider.tsx b/react/src/components/BAISider.tsx index 3406263261..56c565d060 100644 --- a/react/src/components/BAISider.tsx +++ b/react/src/components/BAISider.tsx @@ -1,36 +1,39 @@ import Flex from './Flex'; -import { ConfigProvider, Grid, SiderProps, Typography, theme } from 'antd'; +import { ConfigProvider, Grid, SiderProps, theme } from 'antd'; import Sider from 'antd/es/layout/Sider'; +import classNames from 'classnames'; import _ from 'lodash'; -import React from 'react'; +import React, { forwardRef } from 'react'; export interface BAISiderProps extends SiderProps { logoCollapsed?: React.ReactNode; logo?: React.ReactNode; logoTitleCollapsed?: React.ReactNode; logoTitle?: React.ReactNode; - bottomText?: React.ReactNode; } -export const DEFAULT_COLLAPSED_WIDTH = 74; -const BAISider: React.FC = ({ - children, - logo, - logoCollapsed, - logoTitle, - logoTitleCollapsed, - bottomText, - theme: siderTheme, - ...otherProps -}) => { - const { token } = theme.useToken(); - const { Text } = Typography; - const { xs } = Grid.useBreakpoint(); +export const COLLAPSED_SIDER_WIDTH = 74; +export const SIDER_WIDTH = 240; +const BAISider = forwardRef( + ( + { + children, + logo, + logoCollapsed, + logoTitle, + logoTitleCollapsed, + theme: siderTheme, + ...otherProps + }, + ref, + ) => { + const { token } = theme.useToken(); + const { xs } = Grid.useBreakpoint(); - return ( - <> - - - + - -
-
- {logo} -
-
- {logoCollapsed} -
-
- {/*
- - {otherProps.collapsed ? logoTitleCollapsed : logoTitle} - -
*/} -
- {children} - {bottomText && ( - <> - + - - {bottomText} - +
+
+ {logo} +
+
+ {logoCollapsed} +
+
- - )} -
-
- - ); -}; + {children} + + + + + ); + }, +); export default BAISider; diff --git a/react/src/components/MainLayout/MainLayout.tsx b/react/src/components/MainLayout/MainLayout.tsx index ed37646318..59f69989aa 100644 --- a/react/src/components/MainLayout/MainLayout.tsx +++ b/react/src/components/MainLayout/MainLayout.tsx @@ -132,6 +132,9 @@ function MainLayout() { !compactSidebarActive && setSideCollapsed(false); } }} + onCollapse={(collapsed) => { + setSideCollapsed(collapsed); + }} webuiplugins={webUIPlugins} /> diff --git a/react/src/components/MainLayout/WebUISider.tsx b/react/src/components/MainLayout/WebUISider.tsx index 09bc19a3fb..b94d4e77d0 100644 --- a/react/src/components/MainLayout/WebUISider.tsx +++ b/react/src/components/MainLayout/WebUISider.tsx @@ -2,7 +2,6 @@ import { filterEmptyItem } from '../../helper'; import { useCustomThemeConfig } from '../../helper/customThemeConfig'; import { useSuspendedBackendaiClient, useWebUINavigate } from '../../hooks'; import { useCurrentUserRole } from '../../hooks/backendai'; -import { useThemeMode } from '../../hooks/useThemeMode'; import EndpointsIcon from '../BAIIcons/EndpointsIcon'; import MyEnvironmentsIcon from '../BAIIcons/MyEnvironmentsIcon'; import SessionsIcon from '../BAIIcons/SessionsIcon'; @@ -10,6 +9,7 @@ import BAIMenu from '../BAIMenu'; import BAISider, { BAISiderProps } from '../BAISider'; import Flex from '../Flex'; import ReverseThemeProvider from '../ReverseThemeProvider'; +import SiderToggleButton from '../SiderToggleButton'; import SignoutModal from '../SignoutModal'; import WebUILink from '../WebUILink'; import { PluginPage, WebUIPluginType } from './MainLayout'; @@ -27,18 +27,19 @@ import { ToolOutlined, UserOutlined, } from '@ant-design/icons'; -import { useToggle } from 'ahooks'; +import { useHover, useToggle } from 'ahooks'; import { theme, MenuProps, Typography, ConfigProvider, Divider, + Grid, } from 'antd'; import { ItemType } from 'antd/lib/menu/interface'; import _ from 'lodash'; import { PlayIcon } from 'lucide-react'; -import React, { useContext } from 'react'; +import React, { useContext, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation } from 'react-router-dom'; @@ -50,6 +51,7 @@ type MenuItem = { interface WebUISiderProps extends Pick { webuiplugins?: WebUIPluginType; + onCollapse?: (collapsed: boolean) => void; } const WebUISider: React.FC = (props) => { const { t } = useTranslation(); @@ -76,6 +78,10 @@ const WebUISider: React.FC = (props) => { const [isOpenSignoutModal, { toggle: toggleSignoutModal }] = useToggle(false); + const siderRef = useRef(null); + const isSiderHover = useHover(siderRef); + const gridBreakpoint = Grid.useBreakpoint(); + const generalMenu = filterEmptyItem([ { label: {t('webui.menu.Summary')}, @@ -231,6 +237,7 @@ const WebUISider: React.FC = (props) => { return ( = (props) => { logoTitleCollapsed={ themeConfig?.logo?.logoTitleCollapsed || siteDescription || 'WebUI' } - bottomText={ - props.collapsed ? null : ( - <> + {...props} + > + { + props.onCollapse?.(collapsed); + }} + hidden={!gridBreakpoint.sm || !isSiderHover} + /> + + + {(currentUserRole === 'superadmin' || currentUserRole === 'admin') && ( + + + {!props.collapsed && ( + + {t('webui.menu.Administration')} + + )} + + ), + children: [...adminMenu, ...superAdminMenu], + }, + ] + : currentUserRole === 'admin' + ? [ + { + type: 'group', + label: ( + + {!props.collapsed && ( + + {t('webui.menu.Administration')} + + )} + + ), + children: [...adminMenu], + }, + ] + : [] + } + /> + + )} + + {props.collapsed ? null : ( + +
- + = (props) => { {`${global.packageVersion}.${globalThis.buildNumber}`} - - ) - } - {...props} - > - - {(currentUserRole === 'superadmin' || currentUserRole === 'admin') && ( - - - {!props.collapsed && ( - - {t('webui.menu.Administration')} - - )} - - ), - children: [...adminMenu, ...superAdminMenu], - }, - ] - : currentUserRole === 'admin' - ? [ - { - type: 'group', - label: ( - - {!props.collapsed && ( - - {t('webui.menu.Administration')} - - )} - - ), - children: [...adminMenu], - }, - ] - : [] - } - /> - + + )} ); diff --git a/react/src/components/ReverseThemeProvider.tsx b/react/src/components/ReverseThemeProvider.tsx index eefce584ce..49a177fc88 100644 --- a/react/src/components/ReverseThemeProvider.tsx +++ b/react/src/components/ReverseThemeProvider.tsx @@ -1,5 +1,4 @@ import { useCustomThemeConfig } from '../helper/customThemeConfig'; -import { useThemeMode } from '../hooks/useThemeMode'; import { theme, ConfigProvider, ConfigProviderProps } from 'antd'; import _ from 'lodash'; import React, { useContext } from 'react'; @@ -12,7 +11,7 @@ const ReverseThemeProvider: React.FC = ({ }) => { const themeConfig = useCustomThemeConfig(); const config = useContext(ConfigProvider.ConfigContext); - const isParentDark = config.theme?.algorithm === theme.darkAlgorithm + const isParentDark = config.theme?.algorithm === theme.darkAlgorithm; return ( void; + hidden?: boolean; + // style?: React.CSSProperties; +} +const SiderToggleButton: React.FC = ({ + collapsed = false, + buttonTop, + onClick, + hidden, +}) => { + const { t } = useTranslation(); + + const { token } = theme.useToken(); + return ( + + + +