SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
4.0 KB · 83 lines tsx
Raw Blame History
1import Link from "next/link";2import type { ReactNode } from "react";3import { cx } from "@/lib/format";45export function Page({ children, className, wide }: { children: ReactNode; className?: string; wide?: boolean }) {6  return <div className={cx("mx-auto w-full px-3 py-5 sm:px-5 sm:py-7", wide ? "max-w-[1440px]" : "max-w-[1200px]", className)}>{children}</div>;7}89export function PageHeader({ title, lead, kicker, actions, className }: { title: ReactNode; lead?: ReactNode; kicker?: ReactNode; actions?: ReactNode; className?: string }) {10  return (11    <div className={cx("mb-5 flex flex-wrap items-end justify-between gap-3 sm:mb-7", className)}>12      <div className="min-w-0">13        {kicker && <div className="mb-1 text-[11px] font-medium uppercase tracking-wide text-ink-3">{kicker}</div>}14        <h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">{title}</h1>15        {lead && <p className="mt-1.5 max-w-2xl text-sm text-ink-2">{lead}</p>}16      </div>17      {actions && <div className="flex flex-wrap items-center gap-2">{actions}</div>}18    </div>19  );20}2122export function Section({ title, hint, href, hrefLabel = "View all", children, className, id, badge }: { title: ReactNode; hint?: ReactNode; href?: string; hrefLabel?: string; children: ReactNode; className?: string; id?: string; badge?: ReactNode }) {23  return (24    <section id={id} className={cx("mt-8 first:mt-0", className)}>25      <div className="mb-2.5 flex items-baseline justify-between gap-3 border-b border-rule pb-2">26        <div className="flex min-w-0 items-baseline gap-2">27          <h2 className="text-[15px] font-semibold tracking-tight">{title}</h2>28          {badge}29          {hint && <span className="hidden truncate text-xs text-ink-3 sm:inline">{hint}</span>}30        </div>31        {href && (32          <Link href={href} className="shrink-0 text-xs text-accent hover:underline">33            {hrefLabel} →34          </Link>35        )}36      </div>37      {children}38    </section>39  );40}4142export function Empty({ children, className }: { children: ReactNode; className?: string }) {43  return <div className={cx("rounded-md border border-dashed border-rule px-4 py-8 text-center text-sm text-ink-3", className)}>{children}</div>;44}4546export function Kv({ items, className, cols = 2 }: { items: Array<[ReactNode, ReactNode]>; className?: string; cols?: 1 | 2 | 3 | 4 }) {47  return (48    <dl className={cx("grid gap-x-6", cols === 1 ? "grid-cols-1" : cols === 2 ? "grid-cols-2" : cols === 3 ? "grid-cols-2 sm:grid-cols-3" : "grid-cols-2 sm:grid-cols-4", className)}>49      {items.map(([k, v], i) => (50        <div key={i} className="flex items-baseline justify-between gap-3 border-b border-rule py-1.5 text-sm">51          <dt className="text-ink-3">{k}</dt>52          <dd className="mono truncate text-right tnum">{v}</dd>53        </div>54      ))}55    </dl>56  );57}5859export function Stat({ label, value, sub, tone, className }: { label: ReactNode; value: ReactNode; sub?: ReactNode; tone?: "pos" | "neg" | "warn" | "muted"; className?: string }) {60  return (61    <div className={cx("min-w-0", className)}>62      <div className="text-[11px] font-medium uppercase tracking-wide text-ink-3">{label}</div>63      <div className={cx("mono mt-0.5 truncate text-xl font-semibold tracking-tight tnum sm:text-2xl", tone === "pos" && "text-positive", tone === "neg" && "text-negative", tone === "warn" && "text-warning", tone === "muted" && "text-ink-2")}>{value}</div>64      {sub && <div className="mt-0.5 truncate text-xs text-ink-3">{sub}</div>}65    </div>66  );67}6869export function Pill({ children, active, href, onClick }: { children: ReactNode; active?: boolean; href?: string; onClick?: () => void }) {70  const cls = cx("inline-flex h-8 items-center whitespace-nowrap rounded-full border px-3 text-xs", active ? "border-ink bg-ink text-canvas" : "border-rule text-ink-2 hover:border-rule-strong hover:text-ink");71  if (href)72    return (73      <Link href={href} className={cls}>74        {children}75      </Link>76    );77  return (78    <button type="button" onClick={onClick} className={cls}>79      {children}80    </button>81  );82}83