Files
hanzo.ai/components/home/Board.tsx

80 lines
3.5 KiB
TypeScript

'use client'
import { Award } from 'lucide-react'
import { Text, View, XStack, YStack } from '@hanzo/ui'
import { Maker } from '@/components/models/Maker'
import { pct } from '@/lib/enso'
import type { Bar as Row, Bars } from '@/lib/home'
import { Win, useGrow } from './chrome'
/**
* A benchmark board as bars, group by group: ours in ink, every other row in
* outline, each at its share of the top score on the board, growing in once
* seen, each wearing the mark of the lab that makes it (components/models/Maker;
* none for a row that names no lab), and each figure with what lib/home says
* beside it: a cost, or which questions it is on.
*/
function Bar({ r, top, i, play, still }: { r: Row; top: number; i: number; play: boolean; still: boolean }) {
const ref = useGrow(play, still, { delay: i * 80, duration: 800 })
return (
<YStack gap={4} py={5} data-row={r.system}>
<XStack justify="space-between" columnGap={12} rowGap={2} flexWrap="wrap">
<XStack items="center" gap={8} minW={0}>
<Maker id={r.mark} ours={r.ours} />
<Text fontFamily="$mono" fontSize="$2" lineHeight="$2" color="var(--foreground)" fontWeight={r.ours ? '600' : '400'}>
{r.name}
</Text>
</XStack>
<Text ml="auto" fontFamily="$mono" fontSize="$2" lineHeight="$2" color="var(--foreground)" text="right" $platform-web={{ fontVariantNumeric: 'tabular-nums' }}>
{pct(r.accuracy)}
{r.aside ? (
<Text fontFamily="$mono" fontSize="$2" lineHeight="$2" color="var(--muted-foreground)">
{` ยท ${r.aside}`}
</Text>
) : null}
</Text>
</XStack>
<View height={8} rounded={9999} borderWidth={1} borderColor="var(--border)" overflow="hidden" $platform-web={{ borderRadius: '9999px' }}>
<View
ref={ref}
height="100%"
rounded={9999}
width={`${((r.accuracy / top) * 100).toFixed(2)}%`}
bg={r.ours ? 'var(--foreground)' : 'transparent'}
borderWidth={r.ours ? 0 : 1}
borderColor="var(--foreground)"
$platform-web={{ borderRadius: '9999px', transformOrigin: 'left center' }}
/>
</View>
</YStack>
)
}
/** A board's window: its groups of bars, each under its heading, and the line under them; `limit` keeps the first rows of the first group. */
export function Board({ b, title, play, still, slot, limit }: { b: Bars; title: string; play: boolean; still: boolean; slot: string; limit?: number }) {
const groups = b.groups.map((g, i) => (i === 0 && limit ? { ...g, rows: g.rows.slice(0, limit) } : g))
const top = Math.max(...groups.flatMap((g) => g.rows.map((r) => r.accuracy)))
let n = 0
return (
<Win title={title} icon={<Award size={14} color="var(--background)" strokeWidth={2.2} />} aside={b.aside ?? undefined} slot={slot}>
<YStack px={16} py={12} gap={14}>
{groups.map((g) => (
<YStack key={g.title ?? 'rows'} data-group={g.title ?? undefined}>
{g.title ? (
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)" pb={4}>
{g.title}
</Text>
) : null}
{g.rows.map((r) => (
<Bar key={r.key} r={r} top={top} i={n++} play={play} still={still} />
))}
</YStack>
))}
<Text fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
{b.foot}
</Text>
</YStack>
</Win>
)
}