~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
92 lines · 2.9 KB Raw History
import { ArrowLeftIcon, PanelLeftIcon } from 'lucide-react';
import { useState } from 'react';
import { Button } from '#/components/atoms/button.jsx';
import { Link } from '#/components/atoms/link.jsx';
import { Sidebar } from '#/components/organisms/sidebar.jsx';

/**
 * The console frame: the account rail beside a wide content column headed by
 * the page's title, one-line summary, and a single light action pill. A
 * subpage passes `page.back` to hang a return link above the title.
 */
export function Shell({
  account,
  sections,
  current,
  page,
  onNavigate,
  onSignOut,
  children,
}) {
  const [navOpen, setNavOpen] = useState(false);

  return (
    <div className="flex min-h-svh w-full">
      <Sidebar
        account={account}
        sections={sections}
        current={current}
        onNavigate={onNavigate}
        onSignOut={onSignOut}
        open={navOpen}
        onOpenChange={setNavOpen}
      />

      <main className="flex min-w-0 flex-1 flex-col">
        <div className="flex items-center gap-2 border-border border-b px-3 py-2.5 md:hidden">
          <Button
            variant="ghost"
            size="icon-sm"
            onClick={() => setNavOpen(true)}
            aria-label="Open menu"
          >
            <PanelLeftIcon />
          </Button>
          <span className="truncate font-extrabold text-[14px] text-foreground">
            {account.handle ? `@${account.handle}` : account.did}
          </span>
        </div>

        <div className="mx-auto w-full max-w-[1160px] px-4 py-6 sm:px-6 md:px-9 md:py-7">
          <header className="mb-6 flex flex-wrap items-end gap-x-4 gap-y-3">
            <div className="min-w-0 flex-1">
              {page.back && (
                <Link
                  href={page.back.href}
                  onNavigate={onNavigate}
                  className="mb-2 inline-flex items-center gap-1 text-[12.5px] text-muted-foreground hover:text-foreground"
                >
                  <ArrowLeftIcon className="size-3.5" />
                  {page.back.label}
                </Link>
              )}
              <div className="flex flex-wrap items-center gap-2">
                <h1 className="font-extrabold text-[22px] text-foreground leading-none tracking-tight sm:text-[28px]">
                  {page.title}
                </h1>
                {page.titleBadge}
              </div>
              {page.sub && (
                <p className="mt-2.5 text-[13.5px] text-muted-foreground">
                  {page.sub}
                </p>
              )}
            </div>
            {page.action && (
              <Button
                onClick={page.action.onClick}
                size="pill"
                className="shrink-0"
              >
                {page.action.label}
              </Button>
            )}
          </header>
          <div className="page-enter" key={page.title}>
            {children}
          </div>
        </div>
      </main>
    </div>
  );
}