diff --git a/apps/docs/app/guides/layout.tsx b/apps/docs/app/guides/layout.tsx index 20be5f75f9f..81d62a5a99b 100644 --- a/apps/docs/app/guides/layout.tsx +++ b/apps/docs/app/guides/layout.tsx @@ -43,7 +43,7 @@ const getMenuId = (pathname: string | null) => { case pathname.startsWith('storage'): return MenuId.Storage default: - return MenuId.Home + return MenuId.GettingStarted } } diff --git a/apps/docs/app/not-found/layout.tsx b/apps/docs/app/not-found/layout.tsx index fe86169838e..516ffce7a1f 100644 --- a/apps/docs/app/not-found/layout.tsx +++ b/apps/docs/app/not-found/layout.tsx @@ -1,6 +1,5 @@ import { type Metadata } from 'next' import { type PropsWithChildren } from 'react' -import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' import { LayoutMainContent } from '~/layouts/DefaultLayout' import { MainSkeleton } from '~/layouts/MainSkeleton' @@ -12,7 +11,7 @@ const metadata: Metadata = { } const NotFoundLayout = ({ children }: PropsWithChildren) => ( - + {children} ) diff --git a/apps/docs/app/page.tsx b/apps/docs/app/page.tsx index 14a864e6049..b29cb84e606 100644 --- a/apps/docs/app/page.tsx +++ b/apps/docs/app/page.tsx @@ -3,7 +3,7 @@ import Link from 'next/link' import { IconBackground, TextLink } from 'ui' import { IconPanel } from 'ui-patterns/IconPanel' -import HomeMenuIconPicker from '~/components/Navigation/NavigationMenu/HomeMenuIconPicker' +import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker' import { GlassPanelWithIconPicker } from '~/features/ui/GlassPanelWithIconPicker' import { IconPanelWithIconPicker } from '~/features/ui/IconPanelWithIconPicker' import HomeLayout from '~/layouts/HomeLayout' @@ -305,7 +305,7 @@ const HomePage = () => (
- +

Self-Hosting diff --git a/apps/docs/components/Extensions/Extensions.tsx b/apps/docs/components/Extensions/Extensions.tsx index 9f7304ebab2..cd2a80942c9 100644 --- a/apps/docs/components/Extensions/Extensions.tsx +++ b/apps/docs/components/Extensions/Extensions.tsx @@ -58,7 +58,7 @@ export default function Extensions() {

-
+

Filter

    {tags.sort().map((tag) => ( diff --git a/apps/docs/components/Navigation/Navigation.types.ts b/apps/docs/components/Navigation/Navigation.types.ts index 3882fb26e77..dd744fe4b0e 100644 --- a/apps/docs/components/Navigation/Navigation.types.ts +++ b/apps/docs/components/Navigation/Navigation.types.ts @@ -32,7 +32,11 @@ type MenuItem = { community?: boolean } -export type HomepageMenuItems = MenuItem[][] +export type DropdownMenuItem = MenuItem & { + menuItems?: MenuItem[][] +} + +export type GlobalMenuItems = DropdownMenuItem[][] export type NavMenuConstant = Readonly<{ title: string diff --git a/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx new file mode 100644 index 00000000000..e84ae0a852e --- /dev/null +++ b/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx @@ -0,0 +1,198 @@ +import React, { Dispatch, Fragment, SetStateAction, useEffect } from 'react' +import { m, AnimatePresence, LazyMotion, domAnimation } from 'framer-motion' +import Image from 'next/image' +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { useKey } from 'react-use' + +import { useIsLoggedIn, useIsUserLoading } from 'common' +import { Accordion, Button, cn, IconX } from 'ui' +import { ThemeToggle } from 'ui-patterns' + +import { MenuItem, useActiveMenuLabel } from './GlobalNavigationMenu' +import { GLOBAL_MENU_ITEMS } from './NavigationMenu.constants' +import type { DropdownMenuItem } from '../Navigation.types' + +const DEFAULT_EASE = [0.24, 0.25, 0.05, 1] + +const container = { + hidden: { opacity: 0 }, + show: { opacity: 1, transition: { duration: 0.15, staggerChildren: 0.05, ease: DEFAULT_EASE } }, + exit: { opacity: 0, transition: { duration: 0.15 } }, +} + +const listItem = { + hidden: { opacity: 0, y: 10 }, + show: { opacity: 1, y: 0, transition: { duration: 0.25, ease: DEFAULT_EASE } }, + exit: { opacity: 0, transition: { duration: 0.05 } }, +} + +const itemClassName = + 'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:rounded' + +const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => { + const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS) + + return ( + + {section[0].menuItems ? ( + + {section[0].menuItems?.map((menuItem, menuItemIndex) => ( + + {menuItem.map((item) => + !item.href ? ( +
    + {item.label} +
    + ) : ( + + ) + )} +
    + ))} +
    + ) : ( + + {section[0].label} + + )} +
    + ) +} + +const Menu = () => ( + + {GLOBAL_MENU_ITEMS.map((section) => ( + + ))} + +) +interface Props { + open: boolean + setOpen: Dispatch> +} + +const GlobalMobileMenu = ({ open, setOpen }: Props) => { + const isLoggedIn = useIsLoggedIn() + const isUserLoading = useIsUserLoading() + const pathname = usePathname() + + // Close mobile menu on route change + useEffect(() => { + setOpen(false) + }, [pathname]) + + useKey('Escape', () => setOpen(false)) + + return ( + + + {open && ( + +
    + + Supabase Logo + Supabase Logo + DOCS + +
    + + +
    +
    +
    + +
    +
    + {!isUserLoading && ( + <> + {isLoggedIn ? ( + + ) : ( + <> + + + + )} + + )} +
    +
    + )} +
    + + {open && ( + + )} + +
    + ) +} + +export default GlobalMobileMenu diff --git a/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx new file mode 100644 index 00000000000..3adebfe9d72 --- /dev/null +++ b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx @@ -0,0 +1,183 @@ +'use client' + +import React, { FC, Fragment, useEffect, useState } from 'react' +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { + Badge, + cn, + MenubarSeparator, + NavigationMenu, + NavigationMenuContent, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + NavigationMenuTrigger, + navigationMenuTriggerStyle, +} from 'ui' + +import MenuIconPicker from './MenuIconPicker' +import { GLOBAL_MENU_ITEMS } from './NavigationMenu.constants' + +/** + * Get TopNav active label based on current pathname + */ +export const useActiveMenuLabel = (GLOBAL_MENU_ITEMS) => { + const pathname = usePathname() + const [activeLabel, setActiveLabel] = useState('') + + useEffect(() => { + // check if homepage + if (pathname === '/') { + return setActiveLabel('Home') + } + + for (let index = 0; index < GLOBAL_MENU_ITEMS.length; index++) { + const section = GLOBAL_MENU_ITEMS[index] + // check if first level menu items match beginning of url + if (section[0].href?.startsWith(pathname)) { + return setActiveLabel(section[0].label) + } + // check if second level menu items match beginning of url + if (section[0].menuItems) { + section[0].menuItems.map((menuItemGroup) => + menuItemGroup.map( + (menuItem) => menuItem.href?.startsWith(pathname) && setActiveLabel(section[0].label) + ) + ) + } + } + }, [pathname]) + + return activeLabel +} + +const GlobalNavigationMenu: FC = () => { + const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS) + const triggerClassName = + 'h-[var(--header-height)] p-2 bg-transparent border-0 border-b-2 border-transparent font-normal rounded-none text-foreground-light hover:text-foreground data-[state=open]:!text-foreground 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 h-full focus-visible:rounded !shadow-none outline-none transition-all outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-brand-600' + + return ( +
    + + + {GLOBAL_MENU_ITEMS.map((section, sectionIdx) => + section[0].menuItems ? ( + + + {section[0].label === 'Home' ? ( + + ) : ( + section[0].label + )} + + +
    + {section[0].menuItems?.map((menuItem, menuItemIndex) => ( + + {menuItemIndex !== 0 && } + {menuItem.map((item, itemIdx) => + !item.href ? ( +
    + {item.label} +
    + ) : ( + + + + ) + )} +
    + ))} +
    +
    +
    + ) : ( + + + + {section[0].label === 'Home' ? ( + + ) : ( + section[0].label + )} + + + + ) + )} +
    +
    +
    + ) +} + +export const MenuItem = React.forwardRef< + React.ElementRef<'a'>, + React.ComponentPropsWithoutRef<'a'> & { + icon?: string + community?: boolean + } +>(({ className, title, href = '', icon, community, children, ...props }, ref) => { + return ( + + {children ?? ( + <> + {icon && } + {title} + {community && Community} + + )} + + ) +}) + +GlobalNavigationMenu.displayName = 'GlobalNavigationMenu' +MenuItem.displayName = 'MenuItem' + +export default GlobalNavigationMenu diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx deleted file mode 100644 index 944345923cf..00000000000 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import Link from 'next/link' -import { Fragment } from 'react' -import { Badge, cn } from 'ui' -import { HOMEPAGE_MENU_ITEMS } from './NavigationMenu.constants' -import HomeMenuIconPicker from './HomeMenuIconPicker' - -const NavigationMenuHome = () => { - return ( -
    -
      - {HOMEPAGE_MENU_ITEMS.map((section, sectionIndex) => { - return ( - - {sectionIndex !== 0 && ( -
      - )} -
      -
      - {section.map((link, i) => { - if (!link.href) { - return ( -

      - {link.label} -

      - ) - } else { - return ( - -
    • - {link?.icon && } - {link.label} - {link.community && Community} -
    • - - ) - } - })} -
      -
      -
      - ) - })} -
    -
    - ) -} - -export default NavigationMenuHome diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx b/apps/docs/components/Navigation/NavigationMenu/MenuIconPicker.tsx similarity index 87% rename from apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx rename to apps/docs/components/Navigation/NavigationMenu/MenuIconPicker.tsx index af631de7015..8e539d8b63b 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/MenuIconPicker.tsx @@ -26,12 +26,19 @@ import { IconMenuKotlin, IconMenuAI, IconMenuDevCli, -} from './HomeMenuIcons' + IconGitHub, + IconSupport, + IconTerraform, + IconTroubleshooting, + IconBranching, +} from './MenuIcons' function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, className?: string) { switch (menuKey) { case 'home': return + case 'branching': + return case 'getting-started': return case 'database': @@ -78,23 +85,27 @@ function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, c return case 'status': return + case 'github': + return + case 'support': + return default: return } } -type HomeMenuIconPickerProps = { +type MenuIconPickerProps = { icon: string width?: number height?: number className?: string } -export default function HomeMenuIconPicker({ +export default function MenuIconPicker({ icon, width = 16, height = 16, className, -}: HomeMenuIconPickerProps) { +}: MenuIconPickerProps) { return getMenuIcon(icon, width, height, className) } diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx b/apps/docs/components/Navigation/NavigationMenu/MenuIcons.tsx similarity index 76% rename from apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx rename to apps/docs/components/Navigation/NavigationMenu/MenuIcons.tsx index e762f8f3c78..c9dfa878980 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/MenuIcons.tsx @@ -519,3 +519,119 @@ export function IconMenuStatus({ width = 16, height = 16, className }: HomeMenuI ) } + +export function IconBranching({ width = 16, height = 16, className }: HomeMenuIcon) { + return ( + + + + + + + + ) +} + +export function IconGitHub({ width = 16, height = 16, className }: HomeMenuIcon) { + return ( + + + + ) +} + +export function IconSupport({ width = 16, height = 16, className }: HomeMenuIcon) { + return ( + + + + ) +} + +export function IconTerraform({ width = 16, height = 16, className }: HomeMenuIcon) { + return ( + + + + + + ) +} + +export function IconTroubleshooting({ width = 16, height = 16, className }: HomeMenuIcon) { + return ( + + + + ) +} diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index b9a6ab8060e..f8f14fd890a 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1,15 +1,9 @@ -import type { HomepageMenuItems, NavMenuConstant, References } from '../Navigation.types' +import type { GlobalMenuItems, NavMenuConstant, References } from '../Navigation.types' -export const HOMEPAGE_MENU_ITEMS: HomepageMenuItems = [ +export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ [ { - label: 'Home', - icon: 'home', - href: '/', - level: 'home', - }, - { - label: 'Getting Started', + label: 'Start', icon: 'getting-started', href: '/guides/getting-started', level: 'gettingstarted', @@ -17,164 +11,188 @@ export const HOMEPAGE_MENU_ITEMS: HomepageMenuItems = [ ], [ { - label: 'Product', - }, - { - label: 'Database', - icon: 'database', - href: '/guides/database/overview', - level: 'database', - }, - { - label: 'Auth', - icon: 'auth', - href: '/guides/auth', - level: 'auth', - }, - { - label: 'Storage', - icon: 'storage', - href: '/guides/storage', - level: 'storage', - }, - { - label: 'Edge Functions', - icon: 'edge-functions', - href: '/guides/functions', - level: 'functions', - }, - { - label: 'Realtime', - icon: 'realtime', - href: '/guides/realtime', - level: 'realtime', - }, - { - label: 'AI & Vectors', - icon: 'ai', - href: '/guides/ai', - level: 'ai', + label: 'Products', + menuItems: [ + [ + { + label: 'Database', + icon: 'database', + href: '/guides/database/overview', + level: 'database', + }, + { + label: 'Auth', + icon: 'auth', + href: '/guides/auth', + level: 'auth', + }, + { + label: 'Storage', + icon: 'storage', + href: '/guides/storage', + level: 'storage', + }, + { + label: 'Edge Functions', + icon: 'edge-functions', + href: '/guides/functions', + level: 'functions', + }, + { + label: 'Realtime', + icon: 'realtime', + href: '/guides/realtime', + level: 'realtime', + }, + { + label: 'AI & Vectors', + icon: 'ai', + href: '/guides/ai', + level: 'ai', + }, + ], + ], }, ], [ { - label: 'Data API', - }, - { - label: 'REST', - icon: 'rest', - href: '/guides/api', - level: 'api', - }, - { - label: 'GraphQL', - icon: 'graphql', - href: '/guides/graphql', - level: 'graphql', + label: 'Build', + menuItems: [ + [ + { + label: 'Local Dev / CLI', + icon: 'dev-cli', + href: '/guides/cli', + level: 'reference_javascript', + }, + { + label: 'Platform', + icon: 'platform', + href: '/guides/platform', + level: 'platform', + }, + { + label: 'Self-Hosting', + icon: 'self-hosting', + href: '/guides/self-hosting', + level: 'self_hosting', + }, + { + label: 'Integrations', + icon: 'integrations', + hasLightIcon: true, + href: 'https://supabase.com/partners/integrations', + level: 'integrations', + }, + ], + ], }, ], [ { - label: 'Development Cycle', - }, - { - label: 'Local Dev / CLI', - icon: 'dev-cli', - href: '/guides/cli', - level: 'reference_javascript', - }, - { - label: 'Platform', - icon: 'platform', - href: '/guides/platform', - level: 'platform', - }, - { - label: 'Self-Hosting', - icon: 'self-hosting', - href: '/guides/self-hosting', - level: 'self_hosting', - }, - ], - [ - { - label: 'Client Library Reference', - }, - { - label: 'JavaScript', - icon: 'reference-javascript', - href: '/reference/javascript/introduction', - level: 'reference_javascript', - }, - { - label: 'Flutter', - icon: 'reference-dart', - href: '/reference/dart/introduction', - level: 'reference_dart', - }, - { - label: 'Swift', - icon: 'reference-swift', - href: '/reference/swift/introduction', - level: 'reference_swift', - }, - { - label: 'Python', - icon: 'reference-python', - href: '/reference/python/introduction', - level: 'reference_python', - community: true, - }, - { - label: 'C#', - icon: 'reference-csharp', - href: '/reference/csharp/introduction', - level: 'reference_csharp', - community: true, - }, - { - label: 'Kotlin', - icon: 'reference-kotlin', - href: '/reference/kotlin/introduction', - level: 'reference_kotlin', - community: true, + label: 'Reference', + menuItems: [ + [ + { + label: 'Client Library Reference', + }, + { + label: 'JavaScript', + icon: 'reference-javascript', + href: '/reference/javascript/introduction', + level: 'reference_javascript', + }, + { + label: 'Flutter', + icon: 'reference-dart', + href: '/reference/dart/introduction', + level: 'reference_dart', + }, + { + label: 'Swift', + icon: 'reference-swift', + href: '/reference/swift/introduction', + level: 'reference_swift', + }, + { + label: 'Python', + icon: 'reference-python', + href: '/reference/python/introduction', + level: 'reference_python', + community: true, + }, + { + label: 'C#', + icon: 'reference-csharp', + href: '/reference/csharp/introduction', + level: 'reference_csharp', + community: true, + }, + { + label: 'Kotlin', + icon: 'reference-kotlin', + href: '/reference/kotlin/introduction', + level: 'reference_kotlin', + community: true, + }, + ], + [ + { + label: 'CLI Commands', + icon: 'reference-cli', + href: '/reference/cli/introduction', + level: 'reference_javascript', + }, + { + label: 'Management API', + icon: 'reference-api', + href: '/reference/api/introduction', + level: 'reference_javascript', + }, + ], + [ + { label: 'Data API' }, + { + label: 'REST', + icon: 'rest', + href: '/guides/api', + level: 'api', + }, + { + label: 'GraphQL', + icon: 'graphql', + href: '/guides/graphql', + level: 'graphql', + }, + ], + ], }, ], [ { label: 'Resources', - }, - { - label: 'CLI Commands', - icon: 'reference-cli', - href: '/reference/cli/introduction', - level: 'reference_javascript', - }, - { - label: 'Management API', - icon: 'reference-api', - href: '/reference/api/introduction', - level: 'reference_javascript', - }, - { - label: 'Guides and Examples', - icon: 'resources', - href: '/guides/resources', - level: 'resources', - }, - { - label: 'Integrations', - icon: 'integrations', - hasLightIcon: true, - href: 'https://supabase.com/partners/integrations', - level: 'integrations', - }, - ], - [ - { - label: 'Status', - icon: 'status', - href: 'https://status.supabase.com/', + menuItems: [ + [ + { + label: 'Migration guides', + icon: 'resources', + href: '/guides/resources', + level: 'resources', + }, + { + label: 'Changelog', + icon: 'changelog', + hasLightIcon: true, + href: 'https://supabase.com/changelog', + level: 'changelog', + }, + { + label: 'Status', + icon: 'status', + href: 'https://status.supabase.com/', + }, + ], + ], }, ], ] @@ -226,7 +244,7 @@ export const REFERENCES: References = { export const gettingstarted: NavMenuConstant = { icon: 'getting-started', - title: 'Getting Started', + title: 'Start with Supabase', items: [ { name: 'Features', url: '/guides/getting-started/features' }, { name: 'Architecture', url: '/guides/getting-started/architecture' }, diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index 610050fd019..cdb7407a6d8 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -1,10 +1,9 @@ import { memo } from 'react' -import NavigationMenuHome from './HomeMenu' import NavigationMenuGuideList from './NavigationMenuGuideList' import NavigationMenuRefList from './NavigationMenuRefList' +import { useCloseMenuOnRouteChange } from './NavigationMenu.utils' enum MenuId { - Home = 'home', GettingStarted = 'gettingstarted', Database = 'database', Api = 'api', @@ -44,10 +43,6 @@ interface BaseMenu { type: string } -interface HomeMenu extends BaseMenu { - type: 'home' -} - interface GuideMenu extends BaseMenu { type: 'guide' } @@ -59,13 +54,9 @@ interface ReferenceMenu extends BaseMenu { specFile?: string } -type Menu = HomeMenu | GuideMenu | ReferenceMenu +type Menu = GuideMenu | ReferenceMenu const menus: Menu[] = [ - { - id: MenuId.Home, - type: 'home', - }, { id: MenuId.GettingStarted, type: 'guide', @@ -244,7 +235,7 @@ const menus: Menu[] = [ ] function getMenuById(id: MenuId) { - return menus.find((menu) => menu.id === id) ?? menus.find((menu) => menu.id === MenuId.Home) + return menus.find((menu) => menu.id === id) } function getMenuElement(menu: Menu | undefined) { @@ -252,8 +243,6 @@ function getMenuElement(menu: Menu | undefined) { const menuType = menu.type switch (menuType) { - case 'home': - return case 'guide': return case 'reference': @@ -266,7 +255,7 @@ function getMenuElement(menu: Menu | undefined) { /> ) default: - throw new Error(`Unknown menu type '${menuType}'`) + return null } } @@ -274,6 +263,8 @@ const NavigationMenu = ({ menuId }: { menuId: MenuId }) => { const level = menuId const menu = getMenuById(level) + useCloseMenuOnRouteChange() + return getMenuElement(menu) } diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts index 04dc1545765..4cbca0be36a 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts @@ -1,8 +1,10 @@ 'use client' import { useEffect, useState } from 'react' +import { usePathname } from 'next/navigation' import { ICommonItem } from '~/components/reference/Reference.types' import { Json } from '~/types' +import { menuState } from '../../../hooks/useMenuState' export function getPathWithoutHash(relativePath: string) { return new URL(relativePath, 'http://placeholder').pathname @@ -95,3 +97,11 @@ export function useSpec(specFile?: string) { return spec } + +export const useCloseMenuOnRouteChange = () => { + const pathname = usePathname() + + useEffect(() => { + menuState.setMenuMobileOpen(false) + }, [pathname]) +} diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx index 595bb88558f..f50c663887e 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx @@ -1,13 +1,11 @@ import * as Accordion from '@radix-ui/react-accordion' -import { ChevronLeft } from 'lucide-react' import { usePathname } from 'next/navigation' import { useTheme } from 'next-themes' import Image from 'next/legacy/image' import Link from 'next/link' import React, { useEffect, useRef } from 'react' -import HomeMenuIconPicker from './HomeMenuIconPicker' -import { getPathWithoutHash } from './NavigationMenu.utils' +import MenuIconPicker from './MenuIconPicker' const HeaderLink = React.memo(function HeaderLink(props: { title: string @@ -141,24 +139,9 @@ const Content = (props) => { return (
      - -
      -
      - -
      -
      - Back to Home - -
      - +
      diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index 12a50f78e19..80ba4217cbf 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -1,16 +1,15 @@ 'use client' import * as Accordion from '@radix-ui/react-accordion' -import { ChevronLeft, ChevronUp } from 'lucide-react' +import { ChevronUp } from 'lucide-react' import Image from 'next/legacy/image' -import Link from 'next/link' import { Fragment, memo } from 'react' import { cn } from 'ui' import RevVersionDropdown from '~/components/RefVersionDropdown' import { ICommonItem, ICommonSection } from '~/components/reference/Reference.types' import { menuState, useMenuActiveRefId } from '~/hooks/useMenuState' import { BASE_PATH } from '~/lib/constants' -import HomeMenuIconPicker from './HomeMenuIconPicker' +import MenuIconPicker from './MenuIconPicker' import * as NavItems from './NavigationMenu.constants' import { deepFilterSections } from './NavigationMenu.utils' @@ -60,7 +59,10 @@ const FunctionLink = memo(function FunctionLink({ onClick={(e) => { e.preventDefault() history.pushState({}, '', url) - document.getElementById(slug)?.scrollIntoView() + const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches + document.getElementById(slug)?.scrollIntoView({ + behavior: reduceMotion ? 'auto' : 'smooth', + }) onClick() }} className={cn( @@ -180,22 +182,8 @@ const NavigationMenuRefListItems = ({ return (
      - -
      -
      - -
      -
      - Back to Main Menu -
      - +
      diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx index 7be6e326264..fabc74a3c05 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx @@ -1,108 +1,151 @@ -import { useTheme } from 'next-themes' -import Image from 'next/legacy/image' +import { FC, memo, useState } from 'react' import Link from 'next/link' -import { FC } from 'react' -import { Button, IconCommand, IconGitHub, IconSearch } from 'ui' -import { SearchButton } from 'ui-patterns/Cmdk' +import dynamic from 'next/dynamic' + import { useIsLoggedIn, useIsUserLoading } from 'common' -import { ThemeToggle } from 'ui-patterns/ThemeToggle' +import { SearchButton } from 'ui-patterns/Cmdk' +import { Button, buttonVariants, cn } from 'ui' +import { Command, Search, Menu } from 'lucide-react' + +import Image from 'next/image' + +import GlobalNavigationMenu from './GlobalNavigationMenu' +const GlobalMobileMenu = dynamic(() => import('./GlobalMobileMenu')) +const TopNavDropdown = dynamic(() => import('./TopNavDropdown')) const TopNavBar: FC = () => { const isLoggedIn = useIsLoggedIn() const isUserLoading = useIsUserLoading() - const { resolvedTheme } = useTheme() + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) return ( - + + + ) } + +const HeaderLogo = memo(() => { + return ( + + Supabase wordmark + Supabase wordmark + DOCS + + ) +}) + +HeaderLogo.displayName = 'HeaderLogo' + +TopNavBar.displayName = 'TopNavBar' + export default TopNavBar diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx new file mode 100644 index 00000000000..dc33416049b --- /dev/null +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavDropdown.tsx @@ -0,0 +1,116 @@ +'use client' + +import React from 'react' +import Link from 'next/link' +import { useTheme } from 'next-themes' +import { Menu } from 'lucide-react' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuTrigger, + Theme, + buttonVariants, + cn, + themes, +} from 'ui' + +import MenuIconPicker from './MenuIconPicker' + +const menu = [ + [ + { + label: 'Supabase.com', + icon: 'home', + href: 'https://supabase.com', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + { + label: 'GitHub', + icon: 'github', + href: 'https://github.com/supabase/supabase', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + { + label: 'Support', + icon: 'support', + href: 'https://supabase.com/support', + otherProps: { + target: '_blank', + rel: 'noreferrer noopener', + }, + }, + ], +] + +const TopNavDropdown = () => { + const { theme, setTheme } = useTheme() + + return ( + + + + + + {menu.map((menuSection, sectionIdx) => ( + <> + {sectionIdx !== 0 && } + {menuSection.map((sectionItem, itemIdx) => ( + + {}}> + {sectionItem.icon && ( + + )} +

      {sectionItem.label}

      +
      + + ))} + + ))} + + + Theme + { + setTheme(value) + }} + > + {themes + .filter((x) => x.value === 'light' || x.value === 'dark' || x.value === 'system') + .map((theme: Theme) => ( + + {theme.name} + + ))} + + +
      +
      + ) +} + +export default TopNavDropdown diff --git a/apps/docs/components/Navigation/SideBar.tsx b/apps/docs/components/Navigation/SideBar.tsx index 5ebb66b82de..fcf02a89a98 100644 --- a/apps/docs/components/Navigation/SideBar.tsx +++ b/apps/docs/components/Navigation/SideBar.tsx @@ -43,7 +43,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => { return (