From af1d99d2ff720d48af03bfcd192ce1da627e3fdb Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Wed, 3 Apr 2024 11:30:22 +0800 Subject: [PATCH] feat(v2): add mobile layout --- src/client/App.tsx | 5 +- src/client/pages/Layout/DesktopLayout.tsx | 111 +++++++++++----------- src/client/pages/Layout/MobileLayout.tsx | 103 +++++++++++++++++++- src/client/pages/Layout/UserConfig.tsx | 35 ++++++- src/client/pages/LayoutV2.tsx | 2 +- 5 files changed, 195 insertions(+), 61 deletions(-) diff --git a/src/client/App.tsx b/src/client/App.tsx index 4049574..ef9055c 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -23,6 +23,7 @@ import { isDev } from './utils/env'; import { RouterProvider, createRouter } from '@tanstack/react-router'; import { routeTree } from './routeTree.gen'; import { DefaultNotFound } from './components/DefaultNotFound'; +import { TooltipProvider } from './components/ui/tooltip'; const router = createRouter({ routeTree, @@ -95,7 +96,9 @@ export const App: React.FC = React.memo(() => { {isDev ? ( // Compatible with old routes - + + + ) : ( diff --git a/src/client/pages/Layout/DesktopLayout.tsx b/src/client/pages/Layout/DesktopLayout.tsx index 1d4b597..e8a24b7 100644 --- a/src/client/pages/Layout/DesktopLayout.tsx +++ b/src/client/pages/Layout/DesktopLayout.tsx @@ -22,6 +22,7 @@ import { Outlet } from '@tanstack/react-router'; import { trpc } from '@/api/trpc'; import { useUserStore } from '@/store/user'; import { LayoutProps } from './types'; +import { useTranslation } from '@i18next-toolkit/react'; const defaultLayout: [number, number, number] = [265, 440, 655]; @@ -45,6 +46,7 @@ export const DesktopLayout: React.FC = React.memo((props) => { enabled: !!workspaceId, } ); + const { t } = useTranslation(); const navbar = ( <> @@ -61,31 +63,31 @@ export const DesktopLayout: React.FC = React.memo((props) => { isCollapsed={isCollapsed} links={[ { - title: 'Website', + title: t('Website'), label: String(serviceCount?.website ?? ''), icon: LuAreaChart, to: '/website', }, { - title: 'Monitor', + title: t('Monitor'), label: String(serviceCount?.monitor ?? ''), icon: LuMonitorDot, to: '/monitor', }, { - title: 'Servers', + title: t('Servers'), label: '', icon: LuServer, to: '/server', }, { - title: 'Telemetry', + title: t('Telemetry'), label: String(serviceCount?.telemetry ?? ''), icon: LuWifi, to: '/telemetry', }, { - title: 'Pages', + title: t('Pages'), label: String(serviceCount?.page ?? ''), icon: LuFilePieChart, to: '/page', @@ -103,60 +105,57 @@ export const DesktopLayout: React.FC = React.memo((props) => { ); return ( - - { - if (sizes.length === 3) { - setLayout(sizes as typeof defaultLayout); - } else if (sizes.length === 2) { - const listSize = layout[1]; - const rest = 100 - sizes[0] - listSize; - setLayout([sizes[0], listSize, rest]); - } + { + if (sizes.length === 3) { + setLayout(sizes as typeof defaultLayout); + } else if (sizes.length === 2) { + const listSize = layout[1]; + const rest = 100 - sizes[0] - listSize; + setLayout([sizes[0], listSize, rest]); + } + }} + className="h-full items-stretch" + > + { + setIsCollapsed(true); }} - className="h-full items-stretch" - > - { - setIsCollapsed(true); - }} - onExpand={() => { - setIsCollapsed(false); - }} - className={cn( - 'flex flex-col', - isCollapsed && - 'min-w-[50px] transition-all duration-300 ease-in-out' - )} - > - {navbar} - - - {props.list && ( - <> - - -
{props.list}
-
- + onExpand={() => { + setIsCollapsed(false); + }} + className={cn( + 'flex flex-col', + isCollapsed && 'min-w-[50px] transition-all duration-300 ease-in-out' )} + > + {navbar} +
- - -
- {props.children ?? } -
-
-
-
+ {props.list && ( + <> + + +
{props.list}
+
+ + )} + + + +
+ {props.children ?? } +
+
+ ); }); DesktopLayout.displayName = 'DesktopLayout'; diff --git a/src/client/pages/Layout/MobileLayout.tsx b/src/client/pages/Layout/MobileLayout.tsx index 793e88f..faa64b3 100644 --- a/src/client/pages/Layout/MobileLayout.tsx +++ b/src/client/pages/Layout/MobileLayout.tsx @@ -1,6 +1,105 @@ import React from 'react'; +import { + LuAreaChart, + LuFilePieChart, + LuMenu, + LuMonitorDot, + LuServer, + LuWifi, +} from 'react-icons/lu'; +import { useTranslation } from '@i18next-toolkit/react'; +import { IconType } from 'react-icons'; +import { useRouterState, Link, Outlet } from '@tanstack/react-router'; +import { cn } from '@/utils/style'; +import { Separator } from '@/components/ui/separator'; +import { LayoutProps } from './types'; +import { UserConfig } from './UserConfig'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; +import { Button } from '@/components/ui/button'; -export const MobileLayout: React.FC = React.memo(() => { - return
Mobile
; +export const MobileLayout: React.FC = React.memo((props) => { + const { t } = useTranslation(); + + return ( +
+
+ + + + + + {props.list} + + + +
+ +
+ +
+ +
+
+ + + +
+ {props.children ?? } +
+ + + +
+
+ + + + + +
+
+
+ ); }); MobileLayout.displayName = 'MobileLayout'; + +const MobileNavItem: React.FC<{ + title: string; + icon: IconType; + to: string; +}> = React.memo((props) => { + const pathname = useRouterState({ + select: (state) => state.location.pathname, + }); + + const isSelect = pathname.startsWith(props.to); + + return ( + + +
+ {props.title} +
+ + ); +}); +MobileNavItem.displayName = 'MobileNavItem'; diff --git a/src/client/pages/Layout/UserConfig.tsx b/src/client/pages/Layout/UserConfig.tsx index 3027581..89364c1 100644 --- a/src/client/pages/Layout/UserConfig.tsx +++ b/src/client/pages/Layout/UserConfig.tsx @@ -16,7 +16,7 @@ import { } from '@/components/ui/dropdown-menu'; import { useEvent } from '@/hooks/useEvent'; import { useSettingsStore } from '@/store/settings'; -import { useUserInfo } from '@/store/user'; +import { useCurrentWorkspaceId, useUserInfo, useUserStore } from '@/store/user'; import { languages } from '@/utils/constants'; import { useTranslation, setLanguage } from '@i18next-toolkit/react'; import { useNavigate } from '@tanstack/react-router'; @@ -32,6 +32,20 @@ export const UserConfig: React.FC = React.memo((props) => { const { i18n, t } = useTranslation(); const navigate = useNavigate(); const colorScheme = useSettingsStore((state) => state.colorScheme); + const workspaceId = useCurrentWorkspaceId(); + const workspaces = useUserStore((state) => { + const userInfo = state.info; + if (userInfo) { + return userInfo.workspaces.map((w) => ({ + id: w.workspace.id, + name: w.workspace.name, + role: w.role, + current: userInfo.currentWorkspace?.id === w.workspace.id, + })); + } + + return []; + }); const handleChangeColorSchema = useEvent((colorScheme) => { useSettingsStore.setState({ @@ -97,6 +111,25 @@ export const UserConfig: React.FC = React.memo((props) => { {t('Notifications')} + + {t('Workspaces')} + + + + {workspaces.map((workspace) => ( + + {workspace.name} + + ))} + + + + + {t('Language')} diff --git a/src/client/pages/LayoutV2.tsx b/src/client/pages/LayoutV2.tsx index 98b6b24..80601de 100644 --- a/src/client/pages/LayoutV2.tsx +++ b/src/client/pages/LayoutV2.tsx @@ -8,7 +8,7 @@ export const LayoutV2: React.FC = React.memo((props) => { const isMobile = useIsMobile(); if (isMobile) { - return ; + return ; } return ;