diff --git a/apps/playground-web/src/app/wallets/sign-in/button/RightSection.tsx b/apps/playground-web/src/app/wallets/sign-in/button/RightSection.tsx index f42ceb37789..b3f288d23af 100644 --- a/apps/playground-web/src/app/wallets/sign-in/button/RightSection.tsx +++ b/apps/playground-web/src/app/wallets/sign-in/button/RightSection.tsx @@ -185,7 +185,7 @@ export function RightSection(props: { // TODO: should show the expanded connected modal here instead connectButton ) : ( -
+
} + preview={ +
+ +
+ } /> ); } diff --git a/apps/playground-web/src/components/blocks/APIHeader.tsx b/apps/playground-web/src/components/blocks/APIHeader.tsx index d68b7034adc..e6068101d66 100644 --- a/apps/playground-web/src/components/blocks/APIHeader.tsx +++ b/apps/playground-web/src/components/blocks/APIHeader.tsx @@ -26,10 +26,10 @@ export function PageHeader(props: {
-

+

{props.title}

-

+

{props.description}

diff --git a/apps/playground-web/src/components/blocks/full-path-suspense.tsx b/apps/playground-web/src/components/blocks/full-path-suspense.tsx new file mode 100644 index 00000000000..a8793af4e3d --- /dev/null +++ b/apps/playground-web/src/components/blocks/full-path-suspense.tsx @@ -0,0 +1,23 @@ +import { usePathname } from "next/navigation"; +import { Suspense } from "react"; +import { useFullPath } from "@/hooks/full-path"; + +export function FullPathSuspense(props: { + render: (fullPath: string) => React.ReactNode; +}) { + // use pathname to render the fallback + const pathname = usePathname(); + return ( + + + + ); +} + +function WithFullPath(props: { + render: (fullPath: string) => React.ReactNode; +}) { + // use pathname + search params to render the final result + const fullPath = useFullPath(); + return props.render(fullPath); +} diff --git a/apps/playground-web/src/components/blocks/full-width-sidebar-layout.tsx b/apps/playground-web/src/components/blocks/full-width-sidebar-layout.tsx index 67a74ee7b40..749a2cdd2ef 100644 --- a/apps/playground-web/src/components/blocks/full-width-sidebar-layout.tsx +++ b/apps/playground-web/src/components/blocks/full-width-sidebar-layout.tsx @@ -2,7 +2,7 @@ import { ChevronDownIcon, ChevronRightIcon } from "lucide-react"; import Link from "next/link"; -import { Suspense, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { Collapsible, CollapsibleContent, @@ -29,9 +29,9 @@ import { useSidebar, } from "@/components/ui/sidebar"; import { cn } from "@/lib/utils"; -import { useFullPath } from "../../hooks/full-path"; import { ThirdwebIcon } from "../../icons/ThirdwebMiniLogo"; import { ThemeToggle } from "../ThemeToggle"; +import { FullPathSuspense } from "./full-path-suspense"; type ShadcnSidebarBaseLink = { href: string; @@ -58,12 +58,27 @@ export type ShadcnSidebarLink = links: ShadcnSidebarLink[]; }; -export function FullWidthSidebarLayout(props: { +type FullWidthSidebarLayoutProps = { contentSidebarLinks: ShadcnSidebarLink[]; footerSidebarLinks?: ShadcnSidebarLink[]; children: React.ReactNode; className?: string; -}) { + fullPath: string; +}; + +export function FullWidthSidebarLayout( + props: Omit, +) { + return ( + ( + + )} + /> + ); +} + +function FullWidthSidebarLayoutInner(props: FullWidthSidebarLayoutProps) { const { contentSidebarLinks, children, footerSidebarLinks } = props; const sidebar = useSidebar(); @@ -99,12 +114,18 @@ export function FullWidthSidebarLayout(props: { - + {footerSidebarLinks && ( - + )} @@ -128,6 +149,7 @@ export function FullWidthSidebarLayout(props: {
@@ -138,16 +160,12 @@ export function FullWidthSidebarLayout(props: { ); } -function MobileSidebarTrigger(props: { links: ShadcnSidebarLink[] }) { - return ( - - - - ); -} - -function MobileSidebarTriggerInner(props: { links: ShadcnSidebarLink[] }) { - const activeLink = useActiveShadcnSidebarLink(props.links); +function MobileSidebarTrigger(props: { + links: ShadcnSidebarLink[]; + fullPath: string; +}) { + const { links, fullPath } = props; + const activeLink = useActiveShadcnSidebarLink({ links, fullPath }); return (
@@ -169,19 +187,21 @@ function MobileSidebarTriggerInner(props: { links: ShadcnSidebarLink[] }) { ); } -function useActiveShadcnSidebarLink(links: ShadcnSidebarLink[]) { - const pathname = useFullPath(); - +function useActiveShadcnSidebarLink(params: { + links: ShadcnSidebarLink[]; + fullPath: string; +}) { + const { links, fullPath } = params; const activeLink = useMemo(() => { const isActive = (link: ShadcnSidebarLink): boolean => { if ("href" in link) { // Handle exact match if (link.exactMatch) { - return link.href === pathname; + return link.href === fullPath; } // Handle prefix match (ensure we don't match partial paths) - return pathname.startsWith(link.href); + return fullPath.startsWith(link.href); } if ("links" in link) { @@ -192,23 +212,25 @@ function useActiveShadcnSidebarLink(links: ShadcnSidebarLink[]) { }; return links.find(isActive); - }, [links, pathname]); + }, [links, fullPath]); return activeLink; } -function useIsSubnavActive(links: ShadcnSidebarLink[]) { - const pathname = useFullPath(); - +function useIsSubnavActive(params: { + links: ShadcnSidebarLink[]; + fullPath: string; +}) { + const { links, fullPath } = params; const isSubnavActive = useMemo(() => { const isActive = (link: ShadcnSidebarLink): boolean => { if ("href" in link) { // Handle exact match if (link.exactMatch) { - return link.href === pathname; + return link.href === fullPath; } - return pathname.startsWith(link.href); + return fullPath.startsWith(link.href); } if ("links" in link) { @@ -219,7 +241,7 @@ function useIsSubnavActive(links: ShadcnSidebarLink[]) { }; return links.some(isActive); - }, [links, pathname]); + }, [links, fullPath]); return isSubnavActive; } @@ -227,24 +249,17 @@ function useIsSubnavActive(links: ShadcnSidebarLink[]) { function RenderSidebarGroup(props: { sidebarLinks: ShadcnSidebarLink[]; groupName: string; -}) { - return ( - - - - ); -} - -function RenderSidebarGroupInner(props: { - sidebarLinks: ShadcnSidebarLink[]; - groupName: string; + fullPath: string; }) { return ( {props.groupName} - + @@ -254,20 +269,13 @@ function RenderSidebarGroupInner(props: { function RenderSidebarSubmenu(props: { links: ShadcnSidebarLink[]; subMenu: Omit; + fullPath: string; }) { - return ( - - - - ); -} - -function RenderSidebarSubmenuInner(props: { - links: ShadcnSidebarLink[]; - subMenu: Omit; -}) { + const isSubnavActive = useIsSubnavActive({ + links: props.links, + fullPath: props.fullPath, + }); const sidebar = useSidebar(); - const isSubnavActive = useIsSubnavActive(props.links); const [_open, setOpen] = useState(undefined); const open = _open === undefined ? isSubnavActive : _open; @@ -319,6 +327,7 @@ function RenderSidebarSubmenuInner(props: { className="flex items-center gap-2 text-muted-foreground text-sm hover:bg-accent hover:text-foreground px-2 py-1.5 rounded-lg w-full" exactMatch={link.exactMatch} href={link.href} + fullPath={props.fullPath} onClick={() => { sidebar.setOpenMobile(false); }} @@ -335,6 +344,7 @@ function RenderSidebarSubmenuInner(props: { ); @@ -346,6 +356,7 @@ function RenderSidebarSubmenuInner(props: { key={`group_${link.group}_${index}`} groupName={link.group} sidebarLinks={link.links} + fullPath={props.fullPath} /> ); } @@ -361,7 +372,10 @@ function RenderSidebarSubmenuInner(props: { ); } -function RenderSidebarMenu(props: { links: ShadcnSidebarLink[] }) { +function RenderSidebarMenu(props: { + links: ShadcnSidebarLink[]; + fullPath: string; +}) { const sidebar = useSidebar(); // Add null check for links @@ -382,6 +396,7 @@ function RenderSidebarMenu(props: { links: ShadcnSidebarLink[] }) { className="flex items-center gap-2 text-muted-foreground text-sm hover:bg-accent" exactMatch={link.exactMatch} href={link.href} + fullPath={props.fullPath} onClick={() => { sidebar.setOpenMobile(false); }} @@ -412,6 +427,7 @@ function RenderSidebarMenu(props: { links: ShadcnSidebarLink[] }) { key={`submenu_${link.subMenu.label}_${idx}`} links={link.links} subMenu={link.subMenu} + fullPath={props.fullPath} /> ); } @@ -423,6 +439,7 @@ function RenderSidebarMenu(props: { links: ShadcnSidebarLink[] }) { groupName={link.group} sidebarLinks={link.links} key={`group_${link.group}_${idx}`} + fullPath={props.fullPath} /> ); } diff --git a/apps/playground-web/src/components/styled-connect-embed.tsx b/apps/playground-web/src/components/styled-connect-embed.tsx index 986e9bcbc49..ffe3a52b6d5 100644 --- a/apps/playground-web/src/components/styled-connect-embed.tsx +++ b/apps/playground-web/src/components/styled-connect-embed.tsx @@ -49,6 +49,7 @@ export function StyledConnectEmbed( abstract, ]} client={THIRDWEB_CLIENT} + className="!max-w-full" theme={theme === "light" ? "light" : "dark"} wallets={WALLETS} {...props} diff --git a/apps/playground-web/src/components/ui/NavLink.tsx b/apps/playground-web/src/components/ui/NavLink.tsx index 24959e44ea8..450a5d08a52 100644 --- a/apps/playground-web/src/components/ui/NavLink.tsx +++ b/apps/playground-web/src/components/ui/NavLink.tsx @@ -1,11 +1,9 @@ "use client"; import Link from "next/link"; -import { Suspense } from "react"; import { cn } from "@/lib/utils"; -import { useFullPath } from "../../hooks/full-path"; -export type NavButtonProps = { +export type NavLinkProps = { className?: string; activeClassName?: string; href: string; @@ -13,20 +11,14 @@ export type NavButtonProps = { onClick?: () => void; }; -export function NavLink(props: React.PropsWithChildren) { - return ( - - - - ); -} - -function NavLinkInner(props: React.PropsWithChildren) { - const pathname = useFullPath(); - +export function NavLink( + props: React.PropsWithChildren & { + fullPath: string; + }, +) { const isActive = props.exactMatch - ? pathname === props.href - : pathname.startsWith(props.href); + ? props.fullPath === props.href + : props.fullPath.startsWith(props.href); return (