diff --git a/apps/www/app/blog/BlogClient.tsx b/apps/www/app/blog/BlogClient.tsx index 132664c4249..294ead38086 100644 --- a/apps/www/app/blog/BlogClient.tsx +++ b/apps/www/app/blog/BlogClient.tsx @@ -41,7 +41,7 @@ function BlogListItemSkeleton() { function BlogGridItemSkeleton() { return ( -
+
diff --git a/apps/www/components/Nav/DevelopersDropdown.tsx b/apps/www/components/Nav/DevelopersDropdown.tsx index 58fc8b4e087..2e252c85cbe 100644 --- a/apps/www/components/Nav/DevelopersDropdown.tsx +++ b/apps/www/components/Nav/DevelopersDropdown.tsx @@ -15,8 +15,8 @@ export const DevelopersDropdown = () => { const { jobsCount, latestBlogPosts } = staticContent return ( -
-
+
+
{DevelopersData['navigation'].map((column) => (
))}
-
-
+
+
{ const isTablet = useBreakpoint(1279) return ( -
-
-
-
    - {Object.values(MainProductsData) - .filter((product) => product.name !== 'Vector') - .map((product) => ( - - - - ))} -
-
+
+
+
+
+
+ Features +
+
    + {Object.values(MainProductsData) + .filter((product) => product.name !== 'Vector') + .map((product) => ( + + + + ))} +
+
+
Modules
    {Object.values(ProductModulesData).map((productModule) => ( - -
    - -
    -
    -
    -

    {productModule.name}

    - -
    - {productModule.description_short && ( -

    - {productModule.description_short} -

    - )} -
    - + description={productModule.description_short} + icon={productModule.icon} + className="h-fit p-0" + hasChevron + />
    ))} -
-
-
- +
+
@@ -113,17 +81,17 @@ export const ProductDropdown = () => { className="w-3 h-3 text-foreground transition-all will-change-transform -translate-x-1 opacity-0 group-hover/menu-item:translate-x-0 group-hover/menu-item:opacity-100" />
- + Explore everything you can do with Supabase.
-
+
-
-
+
+
{ href={customer.url} className="group flex items-center gap-3 focus-ring focus-visible:ring-offset-4 focus-visible:ring-offset-background-alternative rounded-sm" > -
+
{customer.title} {
-
-
-

- {ComparisonsData.label} -

-
    - {ComparisonsData.comparisons.map((link) => ( -
  • - -
  • - ))} -
-
+
+

+ {ComparisonsData.label} +

+
    + {ComparisonsData.comparisons.map((link) => ( +
  • + +
  • + ))} +
diff --git a/apps/www/components/Nav/SolutionsDropdown.tsx b/apps/www/components/Nav/SolutionsDropdown.tsx index 10972cd986b..2bb7fc0c930 100644 --- a/apps/www/components/Nav/SolutionsDropdown.tsx +++ b/apps/www/components/Nav/SolutionsDropdown.tsx @@ -1,9 +1,10 @@ +import { useBreakpoint, useReducedMotion } from 'common' import { navData as DevelopersData } from 'data/Solutions' import { AnimatePresence, motion } from 'framer-motion' import { ArrowLeftRight, ChevronRight } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' -import { useWindowSize } from 'react-use' +import { useMedia } from 'react-use' type LinkProps = { text: string @@ -14,16 +15,16 @@ type LinkProps = { } export const SolutionsDropdown = () => ( -
-
+
+
{/* Skill Level, Who it's for, App Type */} {DevelopersData['navigation'].slice(0, 3).map((column) => ( ))}
-
-
+
+
@@ -68,26 +69,34 @@ const LinksGroup = ({ links, label }: { links: LinkProps[]; label: string }) => const MotionLink = motion(Link) const MigrationLinkCard = ({ link }: { link: LinkProps }) => { - const [hovered, setHovered] = useState(false) - const { width } = useWindowSize() - const isDesktop = width >= 1280 + const [isHighlighted, setIsHighlighted] = useState(false) + const canHover = useMedia('(hover: hover)', true) + const reduceMotion = useReducedMotion() + const isDesktop = !useBreakpoint('lg') + const duration = isDesktop && !reduceMotion ? 0.2 : 0 + const isIconVisible = isHighlighted || !isDesktop || !canHover return ( setHovered(true)} - onMouseLeave={() => setHovered(false)} + onMouseEnter={() => setIsHighlighted(true)} + onMouseLeave={() => setIsHighlighted(false)} + onFocus={() => setIsHighlighted(true)} + onBlur={() => setIsHighlighted(false)} layout + layoutDependency={isHighlighted} + transition={{ duration, ease: 'easeInOut' }} > - {(hovered || !isDesktop) && ( + {isIconVisible && ( {link.icon && } @@ -97,7 +106,8 @@ const MigrationLinkCard = ({ link }: { link: LinkProps }) => { @@ -105,14 +115,16 @@ const MigrationLinkCard = ({ link }: { link: LinkProps }) => { {link.text} { const pathname = usePathname() const { width } = useWindowSize() const [open, setOpen] = useState(false) + const [activeDropdown, setActiveDropdown] = useState('') + const [visibleDropdown, setVisibleDropdown] = useState('') + const [isSwitchingDropdown, setIsSwitchingDropdown] = useState(false) + const [openedDropdowns, setOpenedDropdowns] = useState([]) + const handleDropdownChange = (value: string) => { + // animate card height between two open menus only + setIsSwitchingDropdown(value !== '' && activeDropdown !== '') + setActiveDropdown(value) + if (value === '') return + setVisibleDropdown(value) + if (!openedDropdowns.includes(value)) setOpenedDropdowns([...openedDropdowns, value]) + } const isLoggedIn = useIsLoggedIn() const isUserLoading = useIsUserLoading() const user = useUser() const menu = getMenu() + const dropdownTitles: string[] = menu.primaryNav + .filter((menuItem) => menuItem.hasDropdown) + .map((menuItem) => menuItem.title) + const getDropdownSide = (title: string) => { + if (title === visibleDropdown) return 'active' + return dropdownTitles.indexOf(title) < dropdownTitles.indexOf(visibleDropdown) ? 'start' : 'end' + } const sendTelemetryEvent = useSendTelemetryEvent() const userMenu = useDropdownMenu(user) @@ -116,22 +136,43 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => {
- + {menu.primaryNav.map((menuItem) => menuItem.hasDropdown ? ( - + {menuItem.title} - {menuItem.dropdown} + + {openedDropdowns.includes(menuItem.title) ? menuItem.dropdown : null} + ) : ( @@ -139,7 +180,7 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => { @@ -147,6 +188,22 @@ const Nav = ({ hideNavbar, stickyNavbar = true }: Props) => { ) )} +
diff --git a/packages/ui/src/components/shadcn/ui/navigation-menu.tsx b/packages/ui/src/components/shadcn/ui/navigation-menu.tsx index 503edd18d4a..c32ab78165a 100644 --- a/packages/ui/src/components/shadcn/ui/navigation-menu.tsx +++ b/packages/ui/src/components/shadcn/ui/navigation-menu.tsx @@ -1,8 +1,8 @@ 'use client' -import { NavigationMenu as NavigationMenuPrimitive } from 'radix-ui' import { cva } from 'class-variance-authority' import { ChevronDown } from 'lucide-react' +import { NavigationMenu as NavigationMenuPrimitive } from 'radix-ui' import * as React from 'react' import { cn } from '../../../lib/utils/cn'