SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
1.8 KB · 30 lines tsx
Raw Blame History
1import { ArrowDownRight, ArrowUpRight, Minus } from 'lucide-react';2import { t } from '@/i18n';3import { cn } from '@/lib/cn';4import { formatChange, formatPeriod } from '@/lib/format';5import type { ChangeValue, FormatSpec } from '@/lib/types';67/**8 * Signed change with ↑/↓ glyph and direction colour. Colour = direction × whether up is good9 * (`higher_is_better`): good → up colour, bad → down colour, unknown → neutral ink. Never colour alone.10 * Uses the API's `formatted` string when present, else lib/format.ts.11 */12export function ChangeChip({ change, spec, prevPeriod, className, showVs = true }: { change: ChangeValue | null | undefined; spec: FormatSpec; prevPeriod?: string | null; className?: string; showVs?: boolean }) {13  if (!change) return null;14  const c = formatChange(change.abs, change.pct, spec);15  if (!c) return null;16  const text = change.formatted ?? c.text;17  const hib = spec.higher_is_better;18  const tone = c.direction === 'flat' || hib == null ? 'neutral' : (c.direction === 'up') === hib ? 'good' : 'bad';19  const Icon = c.direction === 'up' ? ArrowUpRight : c.direction === 'down' ? ArrowDownRight : Minus;20  const label = c.direction === 'up' ? t('common.up') : c.direction === 'down' ? t('common.down') : t('common.flat');21  return (22    <span className={cn('tnum inline-flex items-center gap-0.5 text-xs', tone === 'good' && 'text-up', tone === 'bad' && 'text-down', tone === 'neutral' && 'text-ink-2', className)}>23      <Icon size={13} aria-hidden strokeWidth={2.25} />24      <span className="sr-only">{label} </span>25      <span className="font-medium">{text}</span>26      {showVs && prevPeriod ? <span className="ml-1 font-normal text-ink-3">{t('common.vs', { period: formatPeriod(prevPeriod, spec.frequency ?? 'A') })}</span> : null}27    </span>28  );29}30