increase console max-width, make header responsive

This commit is contained in:
Victor Oliva
2025-06-27 12:07:40 +02:00
parent 93a5111ac4
commit 8c311e35cd
5 changed files with 238 additions and 36 deletions

View File

@@ -13,7 +13,7 @@ export default function App() {
<div className="w-full h-screen bg-background flex flex-col">
<Header />
<div className="flex-1 overflow-auto relative">
<div className="max-w-(--breakpoint-lg) m-auto">
<div className="max-w-(--breakpoint-xl) m-auto">
<Routes>
<Route path="explorer/*" element={<Explorer />} />
<Route path="extrinsics/*" element={<Extrinsics />} />

137
src/components/ui/sheet.tsx Normal file
View File

@@ -0,0 +1,137 @@
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} />
}
function SheetTrigger({
...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
}
function SheetClose({
...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
}
function SheetPortal({
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
}
function SheetOverlay({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
return (
<SheetPrimitive.Overlay
data-slot="sheet-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
)}
{...props}
/>
)
}
function SheetContent({
className,
children,
side = "right",
...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left"
}) {
return (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
data-slot="sheet-content"
className={cn(
"bg-white data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-neutral-950",
side === "right" &&
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
side === "left" &&
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
side === "top" &&
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
side === "bottom" &&
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
className
)}
{...props}
>
{children}
<SheetPrimitive.Close className="ring-offset-white focus:ring-neutral-950 data-[state=open]:bg-neutral-100 absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none dark:ring-offset-neutral-950 dark:focus:ring-neutral-300 dark:data-[state=open]:bg-neutral-800">
<XIcon className="size-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
)
}
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="sheet-header"
className={cn("flex flex-col gap-1.5 p-4", className)}
{...props}
/>
)
}
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="sheet-footer"
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
)
}
function SheetTitle({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
return (
<SheetPrimitive.Title
data-slot="sheet-title"
className={cn("text-neutral-950 font-semibold dark:text-neutral-50", className)}
{...props}
/>
)
}
function SheetDescription({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
return (
<SheetPrimitive.Description
data-slot="sheet-description"
className={cn("text-neutral-500 text-sm dark:text-neutral-400", className)}
{...props}
/>
)
}
export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}

View File

@@ -71,7 +71,7 @@ export const Extrinsics = withSubscribe(
className={twMerge(
"flex flex-col overflow-hidden gap-2 p-4 pb-0",
// Bypassing top-level scroll area, since we need a specific scroll area for the tree view
"absolute w-full h-full max-w-(--breakpoint-lg)",
"absolute w-full h-full max-w-(--breakpoint-xl)",
)}
>
<BinaryDisplay

View File

@@ -1,44 +1,99 @@
import { Button } from "@/components/ui/button"
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
import { Link } from "@/hashParams"
import { FC, PropsWithChildren } from "react"
import { Menu } from "lucide-react"
import { FC, PropsWithChildren, useState } from "react"
import { useLocation } from "react-router-dom"
import { twMerge } from "tailwind-merge"
import { NetworkSwitcher } from "./Network/Network"
export const Header = () => (
<div className="shrink-0 border-b">
<div className="flex p-4 pb-2 items-center gap-2 max-w-(--breakpoint-lg) m-auto">
<div className="flex flex-1 items-center flex-row gap-2 relative">
<img
className="w-14 min-w-14 hidden dark:inline-block"
src="/papi_logo-dark.svg"
alt="papi-logo"
/>
<img
className="w-14 min-w-14 dark:hidden"
src="/papi_logo-light.svg"
alt="papi-logo"
/>
<h1 className="hidden lg:block poppins-regular text-lg">
papi <span className="poppins-extralight">console</span>
</h1>
<div className="absolute -bottom-1 left-0 lg:bottom-0 lg:right-1 text-right text-sm">
(beta)
const navigationItems = [
{ path: "/explorer", label: "Explorer", important: true },
{ path: "/storage", label: "Storage", important: true },
{ path: "/extrinsics", label: "Extrinsics", important: true },
{ path: "/constants", label: "Constants", important: false },
{ path: "/runtimeCalls", label: "Runtime Calls", important: true },
{ path: "/rpcCalls", label: "RPC Calls", important: false },
{ path: "/metadata", label: "Metadata", important: false },
]
export const Header = () => {
const [open, setIsOpen] = useState(false)
return (
<div className="shrink-0 border-b">
<div className="flex p-4 pb-2 items-center gap-2 max-w-(--breakpoint-xl) m-auto">
<div className="flex items-center flex-row gap-2 relative mr-2">
<img
className="w-14 min-w-14 hidden dark:inline-block"
src="/papi_logo-dark.svg"
alt="papi-logo"
/>
<img
className="w-14 min-w-14 dark:hidden"
src="/papi_logo-light.svg"
alt="papi-logo"
/>
<h1 className="hidden min-[72rem]:block poppins-regular text-lg">
papi <span className="poppins-extralight">console</span>
</h1>
<div className="absolute -bottom-1 left-0 min-[72rem]:bottom-0 min-[72rem]:right-1 text-right text-sm">
(beta)
</div>
</div>
</div>
<NetworkSwitcher />
<div className="flex flex-row items-center justify-end px-1 py-1 text-nowrap">
<NavLink to="/explorer">Explorer</NavLink>
<NavLink to="/storage">Storage</NavLink>
<NavLink to="/extrinsics">Extrinsics</NavLink>
<NavLink to="/constants">Constants</NavLink>
<NavLink to="/runtimeCalls">Runtime Calls</NavLink>
<NavLink to="/metadata">Metadata</NavLink>
<NetworkSwitcher />
<div className="flex-1" />
<nav className="flex flex-row items-center justify-end px-1 py-1 text-nowrap flex-wrap min-w-56">
{navigationItems.map(({ path, label, important }) => (
<NavLink
to={path}
key={path}
className={
important ? "text-sm sm:text-base" : "hidden lg:inline-block"
}
>
{label}
</NavLink>
))}
</nav>
<Sheet open={open} onOpenChange={setIsOpen}>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="lg:hidden text-foreground hover:bg-accent"
>
<Menu className="h-5 w-5" />
</Button>
</SheetTrigger>
<SheetContent
side="right"
className="bg-background border-accent w-64 pt-10"
>
<NetworkSwitcher forSmallScreen />
<nav className="flex flex-col gap-4">
{navigationItems
.filter(({ important }) => !important)
.map(({ path, label }) => (
<NavLink
to={path}
key={path}
onClick={() => setIsOpen(false)}
>
{label}
</NavLink>
))}
</nav>
</SheetContent>
</Sheet>
</div>
</div>
</div>
)
)
}
const NavLink: FC<PropsWithChildren<{ to: string }>> = ({ to, children }) => {
const NavLink: FC<
PropsWithChildren<{ to: string; onClick?: () => void; className?: string }>
> = ({ to, children, className, onClick }) => {
const location = useLocation()
const active = location.pathname.startsWith(to)
@@ -49,7 +104,9 @@ const NavLink: FC<PropsWithChildren<{ to: string }>> = ({ to, children }) => {
"transition-colors text-foreground/75 hover:text-foreground cursor-pointer px-3 py-1 rounded",
"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
active && "text-foreground font-bold",
className,
)}
onClick={onClick}
>
{children}
</Link>

View File

@@ -32,8 +32,13 @@ import { addCustomNetwork, getCustomNetwork } from "@/state/chains/networks"
import { useStateObservable } from "@react-rxjs/core"
import { Check, ChevronDown } from "lucide-react"
import { FC, useState } from "react"
import { twMerge } from "tailwind-merge"
export function NetworkSwitcher() {
export function NetworkSwitcher({
forSmallScreen,
}: {
forSmallScreen?: boolean
}) {
const [open, setOpen] = useState(false)
const selectedChain = useStateObservable(selectedChain$)
@@ -42,7 +47,10 @@ export function NetworkSwitcher() {
<DialogTrigger asChild>
<Button
variant="outline"
className="w-[200px] flex gap-0 justify-between text-base px-3 border border-border bg-input"
className={twMerge(
"w-[200px] gap-0 justify-between text-base px-3 border border-border bg-input self-center",
forSmallScreen ? "flex md:hidden" : "hidden md:flex",
)}
>
<span className="overflow-hidden text-ellipsis">
{selectedChain.network.display}