spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
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