SPB Git
1.8 KB · 20 lines tsx
1"use client";2import { useRouter, useSearchParams } from "next/navigation";3import { useEffect } from "react";4import { LayoutGrid, List, Rows3, Table2 } from "lucide-react";5import { t, type Locale } from "@/lib/i18n";6const MODES = ["list", "compact", "cards", "table"] as const;7export type DisplayMode = (typeof MODES)[number];8/** Display preference is remembered in localStorage and applied through the `view` query parameter. */9export function DisplayToggle({ locale, current }: { locale: Locale; current: DisplayMode }) {10  const d = t(locale); const router = useRouter(); const sp = useSearchParams();11  useEffect(() => { try { const saved = localStorage.getItem("papers.view") as DisplayMode | null; if (saved && MODES.includes(saved) && !sp.get("view") && saved !== "list") { const q = new URLSearchParams(sp.toString()); q.set("view", saved); router.replace(`?${q.toString()}`); } } catch {} }, [router, sp]);12  const set = (m: DisplayMode) => { try { localStorage.setItem("papers.view", m); } catch {} const q = new URLSearchParams(sp.toString()); q.set("view", m); router.push(`?${q.toString()}`); };13  const icons = { list: List, compact: Rows3, cards: LayoutGrid, table: Table2 }; const labels = { list: d.papers.list, compact: d.papers.compact, cards: d.papers.cards, table: d.papers.table };14  return (15    <div role="group" aria-label={d.papers.display} className="inline-flex rounded-lg border border-line-2 overflow-hidden">16      {MODES.map((m) => { const I = icons[m]; return <button key={m} type="button" aria-pressed={current === m} title={labels[m]} aria-label={labels[m]} onClick={() => set(m)} className={`px-2.5 py-1.5 text-sm ${current === m ? "bg-navy text-accent-fg" : "bg-bg-2 text-fg-2 hover:bg-bg-3"}`}><I size={16} aria-hidden /></button>; })}17    </div>18  );19}20
Files · main
Menu

Theme