From 6b3c7e267b0b78fbb73e3ed0c2c8495c3ba37a04 Mon Sep 17 00:00:00 2001 From: Paul Wackerow <54227730+wackerow@users.noreply.github.com> Date: Sat, 7 Jun 2025 20:03:15 -0700 Subject: [PATCH 01/15] refactor: nav menu to ssr feat: add nav menu loading skeletons --- app/[locale]/layout.tsx | 5 +- src/components/Nav/Client/index.tsx | 92 +++++++++++++++++++++++++++++ src/components/Nav/index.tsx | 70 ++-------------------- src/components/Search/index.tsx | 2 + src/components/SkipLink.tsx | 25 ++++---- src/layouts/BaseLayout.tsx | 9 ++- src/lib/types.ts | 1 + 7 files changed, 120 insertions(+), 84 deletions(-) create mode 100644 src/components/Nav/Client/index.tsx diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 8e155e0e267..bfb896a5296 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -67,7 +67,10 @@ export default async function LocaleLayout({ - + {children} diff --git a/src/components/Nav/Client/index.tsx b/src/components/Nav/Client/index.tsx new file mode 100644 index 00000000000..b739a539392 --- /dev/null +++ b/src/components/Nav/Client/index.tsx @@ -0,0 +1,92 @@ +"use client" + +import dynamic from "next/dynamic" + +import SearchButton from "@/components/Search/SearchButton" +import SearchInputButton from "@/components/Search/SearchInputButton" +import { Skeleton } from "@/components/ui/skeleton" + +import DesktopNavMenu from "../Desktop" +import { useNav } from "../useNav" + +import { useBreakpointValue } from "@/hooks/useBreakpointValue" + +const Menu = dynamic(() => import("../Menu"), { + ssr: false, + loading: () => ( +
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+ ), +}) + +const MobileNavMenu = dynamic(() => import("../Mobile"), { + ssr: false, + loading: () => , +}) + +const Search = dynamic(() => import("../../Search"), { + ssr: false, + loading: () => ( + <> +
+ + + + + + +
+
+ + +
+ + ), +}) + +const ClientSideNav = () => { + const { toggleColorMode, linkSections } = useNav() + + const desktopScreen = useBreakpointValue({ base: false, md: true }) + return ( +
+ {/* avoid rendering/adding desktop Menu version to DOM on mobile */} + {desktopScreen && ( + + )} + + + {({ onOpen }) => ( +
+ {/* Desktop */} +
+ + + +
+ + {/* Mobile */} +
+ + +
+
+ )} +
+
+ ) +} + +ClientSideNav.displayName = "ClientSideNav" + +export default ClientSideNav diff --git a/src/components/Nav/index.tsx b/src/components/Nav/index.tsx index f3c6766dacc..e9276734642 100644 --- a/src/components/Nav/index.tsx +++ b/src/components/Nav/index.tsx @@ -1,40 +1,17 @@ -"use client" - -import { useRef } from "react" -import dynamic from "next/dynamic" +import { getTranslations } from "next-intl/server" import { EthHomeIcon } from "@/components/icons" -import Search from "@/components/Search" -import SearchButton from "../Search/SearchButton" -import SearchInputButton from "../Search/SearchInputButton" import { BaseLink } from "../ui/Link" -import DesktopNavMenu from "./Desktop" -import { useNav } from "./useNav" - -import { useBreakpointValue } from "@/hooks/useBreakpointValue" -import { useIsClient } from "@/hooks/useIsClient" -import { useTranslation } from "@/hooks/useTranslation" - -const Menu = dynamic(() => import("./Menu"), { - ssr: false, - loading: () =>
, -}) -const MobileNavMenu = dynamic(() => import("./Mobile"), { ssr: false }) +import ClientSideNav from "./Client" -// TODO display page title on mobile -const Nav = () => { - const { toggleColorMode, linkSections } = useNav() - const { t } = useTranslation("common") - const navWrapperRef = useRef(null) - const isClient = useIsClient() - const desktopScreen = useBreakpointValue({ base: false, md: true }) +const Nav = async ({ locale }) => { + const t = await getTranslations({ locale, namespace: "common" }) return (
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 8b10f2f8a2a..7262ef0b5c6 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,3 +1,5 @@ +"use client" + import { useRef } from "react" import dynamic from "next/dynamic" import { useLocale } from "next-intl" diff --git a/src/components/SkipLink.tsx b/src/components/SkipLink.tsx index 2314e92d61c..625e87bf6b3 100644 --- a/src/components/SkipLink.tsx +++ b/src/components/SkipLink.tsx @@ -2,18 +2,13 @@ import { MAIN_CONTENT_ID } from "@/lib/constants" import { BaseLink } from "./ui/Link" -import { useTranslation } from "@/hooks/useTranslation" - -export const SkipLink = () => { - const { t } = useTranslation() - return ( -
- - {t("skip-to-main-content")} - -
- ) -} +export const SkipLink = ({ children }: { children: string }) => ( +
+ + {children} + +
+) diff --git a/src/layouts/BaseLayout.tsx b/src/layouts/BaseLayout.tsx index 59d9d55208a..736f8c5baef 100644 --- a/src/layouts/BaseLayout.tsx +++ b/src/layouts/BaseLayout.tsx @@ -1,5 +1,6 @@ // import { join } from "path" import dynamic from "next/dynamic" +import { getTranslations } from "next-intl/server" import type { Root } from "@/lib/types" @@ -16,12 +17,14 @@ const FeedbackWidget = dynamic(() => import("@/components/FeedbackWidget"), { ssr: false, }) -export const BaseLayout = ({ +export const BaseLayout = async ({ children, // contentIsOutdated, // contentNotTranslated, lastDeployLocaleTimestamp, + locale, }: Root) => { + const t = await getTranslations({ locale, namespace: "common" }) // const { locale, asPath } = useRouter() // const CONTRIBUTING = "/contributing/" @@ -50,9 +53,9 @@ export const BaseLayout = ({ * The Skip Link is positioned above the container to ensure it is not affecting the * layout on initial load. */} - + {t("skip-to-main-content")}
-