96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
import { useMemo, useState } from 'react'
|
|
import { motion } from 'framer-motion'
|
|
import type { WikidataAward } from '../../api/wikidata'
|
|
|
|
interface Props {
|
|
awards: WikidataAward[] | null | undefined
|
|
}
|
|
|
|
const PRIORITY_KEYWORDS = [
|
|
'Academy Award',
|
|
'Oscar',
|
|
'Golden Globe',
|
|
'BAFTA',
|
|
'Emmy',
|
|
'Cannes',
|
|
'Palme',
|
|
'Berlin',
|
|
'Sundance',
|
|
'Venice',
|
|
'Critics',
|
|
'SAG',
|
|
'Hugo',
|
|
'Nebula',
|
|
'Saturn',
|
|
'MTV',
|
|
]
|
|
|
|
/**
|
|
* Compact award grid pulled from Wikidata P166. We surface the most
|
|
* recognised prizes first (Oscars, Globes, BAFTAs, Emmys, etc.), then
|
|
* dedupe and cap at a sensible count. A "Show all" toggle reveals the
|
|
* remainder when there are many.
|
|
*/
|
|
export default function AwardsBlock({ awards }: Props) {
|
|
const [expanded, setExpanded] = useState(false)
|
|
|
|
const ordered = useMemo(() => {
|
|
if (!awards) return []
|
|
const seen = new Set<string>()
|
|
const unique: WikidataAward[] = []
|
|
for (const a of awards) {
|
|
const key = a.label
|
|
if (seen.has(key)) continue
|
|
seen.add(key)
|
|
unique.push(a)
|
|
}
|
|
return unique.sort((a, b) => priority(a) - priority(b))
|
|
}, [awards])
|
|
|
|
if (ordered.length === 0) return null
|
|
const visible = expanded ? ordered : ordered.slice(0, 12)
|
|
|
|
return (
|
|
<div>
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{visible.map((a, i) => (
|
|
<motion.div
|
|
key={a.id || a.label}
|
|
initial={{ opacity: 0, y: 4 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.25, delay: Math.min(i * 0.02, 0.3) }}
|
|
title={[a.ceremony, a.point_in_time?.slice(0, 4), a.for_work].filter(Boolean).join(' · ')}
|
|
className="inline-flex items-center gap-1.5 h-7 px-3 bg-elevated/60 ring-1 ring-border hover:ring-border-strong rounded-full text-[11.5px] text-text-2 tracking-tight"
|
|
>
|
|
<svg className="w-3 h-3 text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
<path d="M10 2a1 1 0 011 1v.5a4.5 4.5 0 014.5 4.5v.5h.5a1.5 1.5 0 010 3H15v.5a5 5 0 01-4 4.9V18h2a1 1 0 110 2H7a1 1 0 110-2h2v-1.1A5 5 0 015 12v-.5h-.5a1.5 1.5 0 010-3H5V8a4.5 4.5 0 014.5-4.5V3a1 1 0 011-1z" />
|
|
</svg>
|
|
<span className="font-medium">{a.label}</span>
|
|
{a.point_in_time && (
|
|
<span className="text-text-4 tabular-nums">
|
|
{a.point_in_time.slice(0, 4)}
|
|
</span>
|
|
)}
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
{ordered.length > 12 && (
|
|
<button
|
|
onClick={() => setExpanded(v => !v)}
|
|
className="mt-3 text-[11.5px] text-text-3 hover:text-accent transition tracking-tight focus-ring rounded"
|
|
>
|
|
{expanded ? 'Show fewer' : `Show all ${ordered.length}`}
|
|
</button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function priority(a: WikidataAward): number {
|
|
const label = a.label || ''
|
|
for (let i = 0; i < PRIORITY_KEYWORDS.length; i++) {
|
|
if (label.includes(PRIORITY_KEYWORDS[i])) return i
|
|
}
|
|
return PRIORITY_KEYWORDS.length
|
|
}
|