Files
supabase/apps/www/components/Blog/BlogViewToggle.tsx
Alan Daniel a97703097c feat(www): cookie-driven blog view, in-category search, author page redesign
- Persist the list/grid view in a cookie so the server renders the chosen view
  on first paint (no localStorage hydration flicker); index/category/author
  pages read it and become dynamically rendered
- Add in-category client-side search to category pages via a new BlogFilters
  onSearch mode
- Rebuild the author page to match the /blog layout: header, divider, search
  aligned with the list/grid toggle; drop the old DefaultLayout (fixes the
  duplicate navbar) and old container
- Extract the shared BlogViewToggle used by BlogFilters and the author page
2026-06-01 20:20:14 -04:00

49 lines
1.4 KiB
TypeScript

'use client'
import { setBlogViewCookie, type BlogView } from 'app/blog/blog-view'
import { motion } from 'framer-motion'
import { AlignJustify, Grid } from 'lucide-react'
import { cn } from 'ui'
export default function BlogViewToggle({
view,
setView,
}: {
view: BlogView
setView: (view: BlogView) => void
}) {
return (
<div className="flex items-center border border-border rounded-md p-0.5 gap-0.5 bg-surface-100">
{(['list', 'grid'] as BlogView[]).map((v) => (
<button
key={v}
onClick={() => {
setView(v)
setBlogViewCookie(v)
}}
aria-label={`${v} view`}
className={cn(
'relative flex items-center justify-center w-7 h-7 rounded-sm transition-colors',
view === v ? 'text-foreground' : 'text-foreground-light hover:text-foreground'
)}
>
{view === v && (
<motion.span
layoutId="blog-view-bg"
className="absolute inset-0 rounded-sm bg-surface-300 border border-border"
transition={{ type: 'spring', duration: 0.3, bounce: 0.15 }}
/>
)}
<span className="relative z-10">
{v === 'list' ? (
<AlignJustify className="w-3.5 h-3.5" />
) : (
<Grid className="w-3.5 h-3.5" />
)}
</span>
</button>
))}
</div>
)
}