From c8d6b9953d33147c64bbaf78b8560e10342dbbe5 Mon Sep 17 00:00:00 2001 From: liangfung Date: Mon, 11 Nov 2024 23:48:24 +0700 Subject: [PATCH] update: sidebar tooltip --- .../(dashboard)/components/main-content.tsx | 14 +- .../app/(dashboard)/components/sidebar.tsx | 330 ++++++++--------- ee/tabby-ui/app/(dashboard)/layout.tsx | 4 +- .../(dashboard)/profile/components/avatar.tsx | 2 +- .../detail/components/add-repository-form.tsx | 2 +- .../components/access-policy-view.tsx | 2 +- .../groups/components/membership-view.tsx | 2 +- .../groups/components/upsert-member-form.tsx | 2 +- ee/tabby-ui/app/(home)/page.tsx | 2 +- ee/tabby-ui/app/dashboard/page.tsx | 52 --- .../app/files/components/file-tree-header.tsx | 2 +- ee/tabby-ui/app/globals.css | 32 -- ee/tabby-ui/components/app-sidebar.tsx | 176 --------- ee/tabby-ui/components/demo-banner.tsx | 2 +- ee/tabby-ui/components/header.tsx | 6 +- ee/tabby-ui/components/license-banner.tsx | 2 +- ee/tabby-ui/components/nav-main.tsx | 74 ---- ee/tabby-ui/components/nav-projects.tsx | 89 ----- ee/tabby-ui/components/nav-user.tsx | 114 ------ ee/tabby-ui/components/team-switcher.tsx | 89 ----- ee/tabby-ui/components/ui/breadcrumb.tsx | 52 +-- ee/tabby-ui/components/ui/icons.tsx | 4 +- ee/tabby-ui/components/ui/sidebar.tsx | 342 +++++++++--------- ee/tabby-ui/lib/hooks/use-server-info.ts | 3 +- ee/tabby-ui/tailwind.config.js | 16 +- 25 files changed, 395 insertions(+), 1020 deletions(-) delete mode 100644 ee/tabby-ui/app/dashboard/page.tsx delete mode 100644 ee/tabby-ui/components/app-sidebar.tsx delete mode 100644 ee/tabby-ui/components/nav-main.tsx delete mode 100644 ee/tabby-ui/components/nav-projects.tsx delete mode 100644 ee/tabby-ui/components/nav-user.tsx delete mode 100644 ee/tabby-ui/components/team-switcher.tsx diff --git a/ee/tabby-ui/app/(dashboard)/components/main-content.tsx b/ee/tabby-ui/app/(dashboard)/components/main-content.tsx index 210e52b02326..665c9341e5b8 100644 --- a/ee/tabby-ui/app/(dashboard)/components/main-content.tsx +++ b/ee/tabby-ui/app/(dashboard)/components/main-content.tsx @@ -1,9 +1,10 @@ 'use client' +import { ScrollArea } from '@/components/ui/scroll-area' +import { SidebarInset } from '@/components/ui/sidebar' import { BANNER_HEIGHT, useShowDemoBanner } from '@/components/demo-banner' import { Header } from '@/components/header' import { useShowLicenseBanner } from '@/components/license-banner' -import { SidebarInset } from '@/components/ui/sidebar' export default function MainContent({ children @@ -24,12 +25,11 @@ export default function MainContent({ return ( <> {/* Wraps right hand side into ScrollArea, making scroll bar consistent across all browsers */} - -
-
{children}
+ + +
+
{children}
+ ) diff --git a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx index c533011ee418..86a01a745404 100644 --- a/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx +++ b/ee/tabby-ui/app/(dashboard)/components/sidebar.tsx @@ -1,13 +1,12 @@ 'use client' -import React from 'react' +import React, { FunctionComponent } from 'react' import Image from 'next/image' import Link from 'next/link' import { usePathname } from 'next/navigation' import logoDarkUrl from '@/assets/logo-dark.png' import logoUrl from '@/assets/logo.png' -import { cva } from 'class-variance-authority' -import { escapeRegExp } from 'lodash-es' +import tabbyLogo from '@/assets/tabby.png' import { useMe } from '@/lib/hooks/use-me' import { @@ -22,17 +21,25 @@ import { IconLightingBolt, IconUser } from '@/components/ui/icons' -import { BANNER_HEIGHT, useShowDemoBanner } from '@/components/demo-banner' -import { useShowLicenseBanner } from '@/components/license-banner' +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarHeader, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem +} from '@/components/ui/sidebar' import LoadingWrapper from '@/components/loading-wrapper' -import { Sidebar, SidebarContent, SidebarHeader } from '@/components/ui/sidebar' export interface SidebarProps { children?: React.ReactNode className?: string } -type MenuLeaf = { +type SubMenu = { title: string href: string allowUser?: boolean @@ -40,30 +47,30 @@ type MenuLeaf = { type Menu = | { - title: string - icon: React.ReactNode - allowUser?: boolean - children: MenuLeaf[] - } + title: string + icon: FunctionComponent + allowUser?: boolean + items: SubMenu[] + } | { - title: string - href: string - icon: React.ReactNode - allowUser?: boolean - children?: never - } + title: string + href: string + icon: FunctionComponent + allowUser?: boolean + items?: never + } const menus: Menu[] = [ { title: 'Profile', - icon: , + icon: IconUser, href: '/profile', allowUser: true }, { title: 'Information', - icon: , - children: [ + icon: IconBookOpenText, + items: [ { title: 'System', href: '/system' @@ -84,9 +91,9 @@ const menus: Menu[] = [ }, { title: 'Settings', - icon: , + icon: IconGear, allowUser: true, - children: [ + items: [ { title: 'General', href: '/settings/general' @@ -104,8 +111,8 @@ const menus: Menu[] = [ }, { title: 'Integrations', - icon: , - children: [ + icon: IconLightingBolt, + items: [ { title: 'Context Providers', href: '/settings/providers/git' @@ -122,155 +129,152 @@ const menus: Menu[] = [ } ] -export default function AppSidebar({ children, className }: SidebarProps) { +export default function AppSidebar() { + const pathname = usePathname() const [{ data, fetching: fetchingMe }] = useMe() const isAdmin = data?.me.isAdmin - const [isShowDemoBanner] = useShowDemoBanner() - const [isShowLicenseBanner] = useShowLicenseBanner() - const showBanner = isShowDemoBanner || isShowLicenseBanner - const style = showBanner - ? { - height: `calc(100vh - ${isShowDemoBanner ? BANNER_HEIGHT : '0rem'} - ${isShowLicenseBanner ? BANNER_HEIGHT : '0rem' - })`, - top: isShowDemoBanner ? BANNER_HEIGHT : 0 - } - : { height: '100vh' } return ( - + - - logo - logo + + <> + logo +
+ logo + logo +
+
-
-
- - {menus.map((menu, index) => { - if (menu.allowUser || isAdmin) { - if (menu.children) { - return ( - - {menu.icon} {menu.title} - - } + + + {menus.map(menu => { + if (isAdmin || menu.allowUser) { + if (menu.items) { + return ( + + + + +
+ {menu.title} +
+
+ {menu.items.map(item => { + if (isAdmin || item.allowUser) { + return ( + + + {item.title} + + + ) + } else { + return null + } + })} +
+
+ ), + align: 'start', + side: 'right' + }} + > + {!!menu.icon && } + {menu.title} + + + + + + {menu.items.map(item => { + if (isAdmin || item.allowUser) { + return ( + + + + {item.title} + + + + ) + } + })} + + + + + ) + } else { + return ( + + - {menu.children.map((child, childIndex) => { - if (child.allowUser || isAdmin) { - return ( - - {child.title} - - ) - } - return null - })} - - ) - } else { - return ( - - {menu.icon} {menu.title} - - ) - } + + {!!menu.icon && } + {menu.title} + + + + ) } - return null - })} - -
- + } + return null + })} + +
) } - -interface SidebarButtonProps { - href: string - children: React.ReactNode -} - -const linkVariants = cva( - 'flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:bg-accent', - { - variants: { - state: { - selected: 'bg-accent', - 'not-selected': '' - } - }, - defaultVariants: { - state: 'not-selected' - } - } -) - -function SidebarButton({ href, children }: SidebarButtonProps) { - const pathname = usePathname() - const isSelected = React.useMemo(() => { - if (href === '/') return href === pathname - if (href.startsWith('/settings/providers')) { - return pathname.startsWith('/settings/providers/') - } - - return shouldPathnameHighlight(pathname, href) - }, [pathname, href]) - - const state = isSelected ? 'selected' : 'not-selected' - return ( - - {children} - - ) -} - -function shouldPathnameHighlight( - currentPathname: string, - pathToHighlight: string -) { - const regex = new RegExp(`^${escapeRegExp(pathToHighlight)}(/|\\?|$)`) - return regex.test(currentPathname) -} - -interface SidebarCollapsibleProps { - title: React.ReactNode - children: React.ReactNode - defaultOpen?: boolean -} - -function SidebarCollapsible({ title, children }: SidebarCollapsibleProps) { - return ( - - - - {title} - - - - - {children} - - - ) -} diff --git a/ee/tabby-ui/app/(dashboard)/layout.tsx b/ee/tabby-ui/app/(dashboard)/layout.tsx index 9b2e9edec2b8..8d583190b363 100644 --- a/ee/tabby-ui/app/(dashboard)/layout.tsx +++ b/ee/tabby-ui/app/(dashboard)/layout.tsx @@ -1,10 +1,10 @@ import { Metadata } from 'next' +import { SidebarProvider } from '@/components/ui/sidebar' import { LicenseBanner } from '@/components/license-banner' import MainContent from './components/main-content' import AppSidebar from './components/sidebar' -import { SidebarProvider } from '@/components/ui/sidebar' export const metadata: Metadata = { title: { @@ -21,7 +21,7 @@ export default function RootLayout({ return ( <> - + {children} diff --git a/ee/tabby-ui/app/(dashboard)/profile/components/avatar.tsx b/ee/tabby-ui/app/(dashboard)/profile/components/avatar.tsx index c414828436b8..2a46f0475151 100644 --- a/ee/tabby-ui/app/(dashboard)/profile/components/avatar.tsx +++ b/ee/tabby-ui/app/(dashboard)/profile/components/avatar.tsx @@ -77,7 +77,7 @@ export const Avatar = () => {
diff --git a/ee/tabby-ui/app/(dashboard)/settings/(integrations)/providers/[kind]/detail/components/add-repository-form.tsx b/ee/tabby-ui/app/(dashboard)/settings/(integrations)/providers/[kind]/detail/components/add-repository-form.tsx index 9bc8fb964841..cacf0038881a 100644 --- a/ee/tabby-ui/app/(dashboard)/settings/(integrations)/providers/[kind]/detail/components/add-repository-form.tsx +++ b/ee/tabby-ui/app/(dashboard)/settings/(integrations)/providers/[kind]/detail/components/add-repository-form.tsx @@ -151,7 +151,7 @@ export default function AddRepositoryForm({ align="start" side="bottom" > - + - + diff --git a/ee/tabby-ui/app/(dashboard)/settings/team/groups/components/membership-view.tsx b/ee/tabby-ui/app/(dashboard)/settings/team/groups/components/membership-view.tsx index 76e0484b8863..f01f35537126 100644 --- a/ee/tabby-ui/app/(dashboard)/settings/team/groups/components/membership-view.tsx +++ b/ee/tabby-ui/app/(dashboard)/settings/team/groups/components/membership-view.tsx @@ -346,7 +346,7 @@ function MemberSelect({ align="start" side="bottom" > - + - +
- - -
-
- - - - - - - Building Your Application - - - - - Data Fetching - - - -
-
-
-
-
-
-
-
-
-
- - - ) -} diff --git a/ee/tabby-ui/app/files/components/file-tree-header.tsx b/ee/tabby-ui/app/files/components/file-tree-header.tsx index f43be57f83ab..fb9d0fbd5ee1 100644 --- a/ee/tabby-ui/app/files/components/file-tree-header.tsx +++ b/ee/tabby-ui/app/files/components/file-tree-header.tsx @@ -209,7 +209,7 @@ const FileTreeHeader: React.FC = ({ side="bottom" sideOffset={-8} > - + ) { - return ( - - - - - - - - - - ) -} diff --git a/ee/tabby-ui/components/demo-banner.tsx b/ee/tabby-ui/components/demo-banner.tsx index 6e3566866633..7da6515c06d9 100644 --- a/ee/tabby-ui/components/demo-banner.tsx +++ b/ee/tabby-ui/components/demo-banner.tsx @@ -70,7 +70,7 @@ export function DemoBanner() { return (
- + {newVersionAvailable && ( diff --git a/ee/tabby-ui/components/license-banner.tsx b/ee/tabby-ui/components/license-banner.tsx index 0fdb6812f15e..ac2751d469b1 100644 --- a/ee/tabby-ui/components/license-banner.tsx +++ b/ee/tabby-ui/components/license-banner.tsx @@ -82,7 +82,7 @@ export function LicenseBanner() { return (
- - {items.map((item) => ( - - - - - {item.icon && } - {item.title} - {!!item.items?.length && ( - - )} - - - - - {item.items?.map((subItem) => ( - - - - {subItem.title} - - - - ))} - - - - - ))} - - - ) -} diff --git a/ee/tabby-ui/components/nav-projects.tsx b/ee/tabby-ui/components/nav-projects.tsx deleted file mode 100644 index f50b20de15c4..000000000000 --- a/ee/tabby-ui/components/nav-projects.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client" - -import { - Folder, - Forward, - MoreHorizontal, - Trash2, - type LucideIcon, -} from "lucide-react" - -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { - SidebarGroup, - SidebarGroupLabel, - SidebarMenu, - SidebarMenuAction, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" - -export function NavProjects({ - projects, -}: { - projects: { - name: string - url: string - icon: LucideIcon - }[] -}) { - const { isMobile } = useSidebar() - - return ( - - Projects - - {projects.map((item) => ( - - - - - {item.name} - - - - - - - More - - - - - - View Project - - - - Share Project - - - - - Delete Project - - - - - ))} - - - - More - - - - - ) -} diff --git a/ee/tabby-ui/components/nav-user.tsx b/ee/tabby-ui/components/nav-user.tsx deleted file mode 100644 index d12ef780c278..000000000000 --- a/ee/tabby-ui/components/nav-user.tsx +++ /dev/null @@ -1,114 +0,0 @@ -"use client" - -import { - BadgeCheck, - Bell, - ChevronsUpDown, - CreditCard, - LogOut, - Sparkles, -} from "lucide-react" - -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/components/ui/avatar" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" - -export function NavUser({ - user, -}: { - user: { - name: string - email: string - avatar: string - } -}) { - const { isMobile } = useSidebar() - - return ( - - - - - - - - CN - -
- {user.name} - {user.email} -
- -
-
- - -
- - - CN - -
- {user.name} - {user.email} -
-
-
- - - - - Upgrade to Pro - - - - - - - Account - - - - Billing - - - - Notifications - - - - - - Log out - -
-
-
-
- ) -} diff --git a/ee/tabby-ui/components/team-switcher.tsx b/ee/tabby-ui/components/team-switcher.tsx deleted file mode 100644 index 2808e0a4f1c8..000000000000 --- a/ee/tabby-ui/components/team-switcher.tsx +++ /dev/null @@ -1,89 +0,0 @@ -"use client" - -import * as React from "react" -import { ChevronsUpDown, Plus } from "lucide-react" - -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { - SidebarMenu, - SidebarMenuButton, - SidebarMenuItem, - useSidebar, -} from "@/components/ui/sidebar" - -export function TeamSwitcher({ - teams, -}: { - teams: { - name: string - logo: React.ElementType - plan: string - }[] -}) { - const { isMobile } = useSidebar() - const [activeTeam, setActiveTeam] = React.useState(teams[0]) - - return ( - - - - - -
- -
-
- - {activeTeam.name} - - {activeTeam.plan} -
- -
-
- - - Teams - - {teams.map((team, index) => ( - setActiveTeam(team)} - className="gap-2 p-2" - > -
- -
- {team.name} - ⌘{index + 1} -
- ))} - - -
- -
-
Add team
-
-
-
-
-
- ) -} diff --git a/ee/tabby-ui/components/ui/breadcrumb.tsx b/ee/tabby-ui/components/ui/breadcrumb.tsx index 60e6c96f72f0..78dba9c0a19a 100644 --- a/ee/tabby-ui/components/ui/breadcrumb.tsx +++ b/ee/tabby-ui/components/ui/breadcrumb.tsx @@ -1,108 +1,108 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { ChevronRight, MoreHorizontal } from "lucide-react" +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { ChevronRight, MoreHorizontal } from 'lucide-react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Breadcrumb = React.forwardRef< HTMLElement, - React.ComponentPropsWithoutRef<"nav"> & { + React.ComponentPropsWithoutRef<'nav'> & { separator?: React.ReactNode } >(({ ...props }, ref) =>