Files
supabase/apps/www/components/Nav/index.tsx
Gildas Garcia b30db91d71 chore: cleanup UI patterns exports (#47406)
## Problem

We now export components under a subpath in ui-patterns to avoid barrel
files as they slow down every tools (from IDE to linters, etc.) and may
also affect bundles our users have to download.

## Solution

- Remove the UI patterns index file
- Fix invalid impors
2026-06-30 09:23:17 +02:00

220 lines
8.4 KiB
TypeScript

'use client'
import { useIsLoggedIn, useIsUserLoading, useUser } from 'common'
import ScrollProgress from 'components/ScrollProgress'
import { getMenu } from 'data/nav'
import { DevToolbarTrigger } from 'dev-tools'
import { useSendTelemetryEvent } from 'lib/telemetry'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import React, { useState } from 'react'
import { useWindowSize } from 'react-use'
import { Button, buttonVariants, cn } from 'ui'
import { AuthenticatedDropdownMenu } from 'ui-patterns/AuthenticatedDropdownMenu'
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from 'ui/src/components/shadcn/ui/navigation-menu'
import GitHubButton from './GitHubButton'
import HamburgerButton from './HamburgerMenu'
import MenuItem from './MenuItem'
import { MobileMenu } from './MobileMenu'
import RightClickBrandLogo from './RightClickBrandLogo'
import useDropdownMenu from './useDropdownMenu'
interface Props {
hideNavbar: boolean
stickyNavbar?: boolean
}
const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => {
const pathname = usePathname()
const { width } = useWindowSize()
const [open, setOpen] = useState(false)
const isLoggedIn = useIsLoggedIn()
const isUserLoading = useIsUserLoading()
const user = useUser()
const menu = getMenu()
const sendTelemetryEvent = useSendTelemetryEvent()
const userMenu = useDropdownMenu(user)
const isLaunchWeekXPage = pathname === '/launch-week/x'
const isLaunchWeek12Page = pathname === '/launch-week/12'
const isLaunchWeek13Page = pathname === '/launch-week/13'
const isGAWeekSection = pathname?.startsWith('/ga-week')
const isStateOfStartupsPage = pathname?.startsWith('/state-of-startups')
const disableStickyNav =
isLaunchWeekXPage ||
isGAWeekSection ||
isLaunchWeekXPage ||
isLaunchWeek12Page ||
isLaunchWeek13Page ||
!stickyNavbar
const showLaunchWeekNavMode = (isGAWeekSection || isLaunchWeekXPage) && !open
const [scrolled, setScrolled] = React.useState(false)
React.useEffect(() => {
if (!isStateOfStartupsPage) return
const handleScroll = () => setScrolled(window.scrollY > 50)
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [isStateOfStartupsPage])
const isTransparent = isStateOfStartupsPage && !scrolled && !open
React.useEffect(() => {
if (open) {
// Prevent scrolling on mount
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = 'auto'
}
}, [open])
// Close mobile menu when desktop
React.useEffect(() => {
if (width >= 1024) setOpen(false)
}, [width])
if (hideNavbar) {
return null
}
return (
<>
<div
className={cn(
'sticky top-0 z-40 transform',
disableStickyNav && 'relative',
isStateOfStartupsPage && 'fixed left-0 right-0'
)}
style={{ transform: 'translate3d(0,0,999px)' }}
data-nav-transparent={isTransparent ? '' : undefined}
>
<div
className={cn(
'absolute inset-0 h-full w-full bg-background/90 dark:bg-background/95 transition-all duration-300',
!showLaunchWeekNavMode && !isTransparent && 'opacity-100!',
showLaunchWeekNavMode && 'bg-transparent! dark:bg-black!',
isGAWeekSection && 'dark:bg-alternative!',
isTransparent && 'bg-transparent! dark:bg-transparent! opacity-100!'
)}
/>
<nav
className={cn(
`relative z-40 border-default border-b backdrop-blur-xs transition-all duration-300`,
showLaunchWeekNavMode && 'border-muted border-b bg-transparent',
isTransparent && 'border-transparent backdrop-blur-none'
)}
>
<div className="section-container relative flex justify-between h-16">
<div className="flex items-center flex-1 sm:items-stretch justify-between">
<div className="flex items-center">
<div className="flex items-center shrink-0">
<RightClickBrandLogo />
</div>
<NavigationMenu
delayDuration={0}
className="hidden pl-8 sm:space-x-4 lg:flex h-16"
viewportClassName="rounded-xl bg-background"
>
<NavigationMenuList>
{menu.primaryNav.map((menuItem) =>
menuItem.hasDropdown ? (
<NavigationMenuItem className="text-sm font-medium" key={menuItem.title}>
<NavigationMenuTrigger
className={cn(
buttonVariants({ variant: 'text', size: 'small' }),
'bg-transparent! hover:text-brand-link data-open:text-brand-link! data-radix-collection-item:focus-visible:ring-2 data-radix-collection-item:focus-visible:ring-foreground-lighter data-radix-collection-item:focus-visible:text-foreground px-2 h-auto'
)}
>
{menuItem.title}
</NavigationMenuTrigger>
<NavigationMenuContent>{menuItem.dropdown}</NavigationMenuContent>
</NavigationMenuItem>
) : (
<NavigationMenuItem className="text-sm font-medium" key={menuItem.title}>
<NavigationMenuLink asChild>
<MenuItem
href={menuItem.url}
title={menuItem.title}
className="group-hover:bg-transparent text-foreground focus-visible:text-brand-link"
hoverColor="brand"
/>
</NavigationMenuLink>
</NavigationMenuItem>
)
)}
</NavigationMenuList>
</NavigationMenu>
</div>
<div className="flex items-center gap-2 opacity-0 animate-fade-in scale-100! delay-300">
<div
className={cn(
'flex items-center gap-2 transition-opacity',
isUserLoading ? 'opacity-0' : 'opacity-100'
)}
>
<DevToolbarTrigger />
<GitHubButton />
{isLoggedIn ? (
<>
<Button className="hidden lg:block" asChild>
<Link href="/dashboard/projects">Dashboard</Link>
</Button>
<AuthenticatedDropdownMenu menu={userMenu} user={user} site="www" />
</>
) : (
<>
<Button variant="default" className="hidden lg:block" asChild>
<Link
href="https://supabase.com/dashboard"
onClick={() =>
sendTelemetryEvent({
action: 'sign_in_button_clicked',
properties: { buttonLocation: 'Header Nav' },
})
}
>
Sign in
</Link>
</Button>
<Button className="hidden lg:block" asChild>
<Link
href="https://supabase.com/dashboard/sign-up"
onClick={() =>
sendTelemetryEvent({
action: 'start_project_button_clicked',
properties: { buttonLocation: 'Header Nav' },
})
}
>
Start your project
</Link>
</Button>
</>
)}
</div>
</div>
</div>
<HamburgerButton
toggleFlyOut={() => setOpen(true)}
showLaunchWeekNavMode={showLaunchWeekNavMode}
/>
</div>
<MobileMenu open={open} setOpen={setOpen} menu={menu} />
</nav>
<ScrollProgress />
</div>
</>
)
}
export default Nav