82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
import { useState } from 'react'
|
|
import { de } from '../strings/de'
|
|
import { CATALOG, farbschlagImageUrl } from '../genetics'
|
|
import './farbkatalog.css'
|
|
|
|
export default function FarbkatalogPage() {
|
|
const [search, setSearch] = useState('')
|
|
|
|
// Filter color catalog by query
|
|
const filtered = CATALOG.filter((variety) => {
|
|
const q = search.toLowerCase()
|
|
return (
|
|
variety.name.toLowerCase().includes(q) ||
|
|
(variety.english && variety.english.toLowerCase().includes(q)) ||
|
|
variety.canonicalGenotype.toLowerCase().includes(q)
|
|
);
|
|
})
|
|
|
|
return (
|
|
<section className="page farbkatalog-page">
|
|
<header className="page-head">
|
|
<div>
|
|
<h2>{de.pages.farbkatalog.title}</h2>
|
|
<p className="muted">
|
|
{de.pages.farbkatalog.subtitle.replace('{count}', String(CATALOG.length))}
|
|
</p>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="farbkatalog-search-bar">
|
|
<input
|
|
type="search"
|
|
className="input"
|
|
placeholder={de.pages.farbkatalog.searchPlaceholder}
|
|
value={search}
|
|
onChange={(e) => setSearch(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="farbkatalog-grid">
|
|
{filtered.map((variety) => {
|
|
const imgUrl = farbschlagImageUrl(variety.image)
|
|
return (
|
|
<div key={variety.name} className="farbkatalog-card">
|
|
<div className="farbkatalog-card-media">
|
|
{imgUrl ? (
|
|
<img
|
|
src={imgUrl}
|
|
alt={variety.name}
|
|
className="farbkatalog-card-img"
|
|
loading="lazy"
|
|
/>
|
|
) : (
|
|
<div className="farbkatalog-card-media-placeholder">
|
|
<span>{variety.name.substring(0, 2).toUpperCase()}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="farbkatalog-card-body">
|
|
<h4 className="farbkatalog-card-title">{variety.name}</h4>
|
|
{variety.english && (
|
|
<span className="farbkatalog-card-subtitle">{variety.english}</span>
|
|
)}
|
|
<div className="farbkatalog-card-genotype">
|
|
<code>{variety.canonicalGenotype}</code>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
|
|
{filtered.length === 0 && (
|
|
<div className="farbkatalog-empty">
|
|
<span className="farbkatalog-empty-icon">🔍</span>
|
|
<p>{de.pages.farbkatalog.empty}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|