spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1'use client';2import { cn } from '@/lib/cn';3import { fmtInt } from '@/lib/format';4import { ORBIT_CLASS_COLORS } from '@/lib/site';5import { ORBIT_CLASSES, type GlobeFilters } from './filters';6import type { GlobeData } from './use-positions';78/** Orbit-class legend with live counts; each entry is a toggle (tap target ≥ 44 px on touch via padding). */9export function GlobeLegend({ data, filters, onToggle, className, dense = false }: { data: GlobeData | null; filters: GlobeFilters; onToggle: (cls: string) => void; className?: string; dense?: boolean }) {10 return (11 <ul className={cn('flex flex-wrap items-center gap-1', className)} aria-label="Orbit classes">12 {ORBIT_CLASSES.map((c) => {13 const idx = data?.legend.cls.indexOf(c) ?? -1;14 const count = idx >= 0 ? data?.counts.cls[idx] ?? null : null;15 const on = filters.cls.size === 0 || filters.cls.has(c);16 const color = ORBIT_CLASS_COLORS[c] ?? 'var(--other)';17 return (18 <li key={c}>19 <button20 type="button"21 onClick={() => onToggle(c)}22 aria-pressed={filters.cls.has(c)}23 className={cn('mono inline-flex min-h-[36px] items-center gap-1.5 rounded-md border px-2 text-[11px] transition-colors', on ? 'border-rule bg-plane/70 text-ink' : 'border-transparent text-ink-3 opacity-60', dense ? 'min-h-[32px]' : '')}24 title={`${c}: ${count === null ? 'count unavailable' : `${fmtInt(count)} objects`} — click to isolate`}25 >26 <span className="inline-block size-2 rounded-full" style={{ background: color, boxShadow: on ? `0 0 8px ${color}` : 'none' }} aria-hidden />27 {c}28 <span className="tnum text-ink-3">{count === null ? '—' : fmtInt(count)}</span>29 </button>30 </li>31 );32 })}33 </ul>34 );35}36