SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
1.7 KB · 38 lines tsx
Raw Blame History
1'use client';2import { Star } from 'lucide-react';3import { cn } from '@/lib/cn';4import { toWatchItem, useWatchlist, WATCH_MAX } from '@/lib/watchlist';56/**7 * Star toggle that adds/removes an entity from the local watchlist (`lib/watchlist.ts`). `aria-pressed` reflects the8 * state; renders a neutral placeholder before hydration so server and client markup match. Sits next to `CompareButton`.9 */10export function WatchButton({ e, size = 'md', className, label = 'Watch' }: { e: { slug: string; name: string; entity_type: string; organization?: { name: string } | string | null }; size?: 'sm' | 'md'; className?: string; label?: string }) {11  const wl = useWatchlist();12  const item = toWatchItem(e);13  const on = wl.ready && wl.has(item.slug);14  const full = wl.ready && wl.full && !on;15  return (16    <button17      type="button"18      onClick={() => {19        if (full) return;20        wl.toggle(item);21      }}22      aria-pressed={on}23      disabled={full}24      title={on ? 'Remove from watchlist' : full ? `The watchlist holds at most ${WATCH_MAX} entities` : 'Add to watchlist (stored in this browser)'}25      data-watch-button26      className={cn(27        'inline-flex shrink-0 items-center gap-1 border text-xs font-medium whitespace-nowrap transition-colors disabled:cursor-not-allowed disabled:opacity-40',28        size === 'sm' ? 'h-7 px-1.5' : 'h-9 px-2.5 text-sm',29        on ? 'border-accent-2 bg-accent-2-soft text-accent-2' : 'border-rule text-ink-2 hover:border-rule-strong hover:text-ink',30        className,31      )}32    >33      <Star className={cn(size === 'sm' ? 'size-3' : 'size-3.5', on && 'fill-current')} aria-hidden />34      <span>{on ? 'Watching' : label}</span>35    </button>36  );37}38