SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.2 KB · 51 lines tsx
Raw Blame History
1import type { ReactNode } from "react";2import { cn } from "@/components/ui/cn";34export interface KvItem {5  label: ReactNode;6  value: ReactNode;7  /** hide the row when the value is unknown (default) or show “—” */8  keepEmpty?: boolean;9}1011/**12 * Compact definition list for entity facts (address, tier, cooling, website…). Rows with null / undefined / "" / "—"13 * values are dropped unless `keepEmpty`, so pages never show a wall of dashes.14 */15export function KeyValueList({ items, className, columns = 1 }: { items: KvItem[]; className?: string; columns?: 1 | 2 }) {16  const rows = items.filter((it) => it.keepEmpty || (it.value !== null && it.value !== undefined && it.value !== "" && it.value !== "—"));17  if (!rows.length) return null;18  return (19    <dl className={cn("grid gap-x-6", columns === 2 ? "sm:grid-cols-2" : "", className)}>20      {rows.map((it, i) => (21        <div key={i} className="grid grid-cols-[minmax(96px,120px)_minmax(0,1fr)] gap-x-3 border-b border-hair py-2 text-[13px] last:border-b-0 sm:grid-cols-[minmax(120px,160px)_minmax(0,1fr)]">22          <dt className="text-[12px] text-ink-3">{it.label}</dt>23          <dd className="min-w-0 break-words text-ink">{it.value ?? <span className="text-ink-4">—</span>}</dd>24        </div>25      ))}26    </dl>27  );28}2930/** Honest placeholder for a section whose data the source does not publish. */31export function NotPublished({ what, className }: { what: string; className?: string }) {32  return <p className={cn("rounded-sm border border-dashed border-hair-2 px-3 py-2.5 text-[12.5px] text-ink-3", className)}>{what} — not published by the sources indexed so far.</p>;33}3435/** External link with safe rel and host-only label by default. */36export function ExtLink({ href, children, className }: { href: string; children?: ReactNode; className?: string }) {37  let label: ReactNode = children;38  if (!label) {39    try {40      label = new URL(href).host.replace(/^www\./, "");41    } catch {42      label = href;43    }44  }45  return (46    <a href={href} target="_blank" rel="noopener noreferrer" className={cn("break-all text-ink underline decoration-hair-2 underline-offset-2 hover:text-accent", className)}>47      {label}48    </a>49  );50}51