feat(frontend): add Tape presentational component
This commit is contained in:
parent
d16bc80ac8
commit
f069eef8a8
2 changed files with 53 additions and 0 deletions
30
frontend/src/components/Tape.tsx
Normal file
30
frontend/src/components/Tape.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import type { Station } from '@lib/types';
|
||||
|
||||
interface Props {
|
||||
station: Station;
|
||||
index: number;
|
||||
active: boolean;
|
||||
onSelect: (id: string) => void;
|
||||
listeners?: number | null;
|
||||
}
|
||||
|
||||
export function Tape({ station, index, active, onSelect, listeners }: Props) {
|
||||
return (
|
||||
<button
|
||||
className={`tape ${active ? 'active' : ''}`}
|
||||
onClick={() => onSelect(station.id)}
|
||||
type="button"
|
||||
>
|
||||
<div className="tape-num">CH.{String(index + 1).padStart(2, '0')}</div>
|
||||
<div className="tape-name">{station.name}</div>
|
||||
{station.tags.length > 0 ? (
|
||||
<div className="tape-jp">{station.tags.slice(0, 2).join(' · ')}</div>
|
||||
) : null}
|
||||
<div className="tape-holes"><span /><span /></div>
|
||||
<div className="tape-meta">
|
||||
<span>{station.mounts.mp3.replace(/^\//, '')}</span>
|
||||
<span>{listeners == null ? '— ppl' : `${listeners} ppl`}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
23
frontend/src/styles/components/tape.css
Normal file
23
frontend/src/styles/components/tape.css
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
.tape {
|
||||
font-family: var(--f-mono);
|
||||
background: var(--cream); color: var(--ink);
|
||||
padding: 10px 12px 12px;
|
||||
border: 2px solid var(--ink);
|
||||
box-shadow: 3px 3px 0 var(--ink);
|
||||
cursor: pointer; text-align: left;
|
||||
transition: transform 120ms ease-out, box-shadow 120ms ease-out;
|
||||
width: 100%;
|
||||
}
|
||||
.tape:hover { transform: translate(-2px,-2px); box-shadow: 5px 5px 0 var(--ink); }
|
||||
.tape.active {
|
||||
background: var(--pink); color: var(--cream);
|
||||
transform: translate(-3px,-3px) rotate(-1deg);
|
||||
box-shadow: 6px 6px 0 var(--lemon);
|
||||
}
|
||||
.tape-num { font-family: var(--f-mono); font-size: 10px; opacity: 0.65; letter-spacing: 1.5px; }
|
||||
.tape-name { font-family: var(--f-pixel); font-size: 16px; line-height: 1.1; margin-top: 2px; }
|
||||
.tape-jp { font-family: var(--f-pixel); font-size: 11px; opacity: 0.85; letter-spacing: 1.5px; margin-top: 2px; }
|
||||
.tape-meta { font-family: var(--f-mono); font-size: 10px; margin-top: 6px; display: flex; justify-content: space-between; opacity: 0.75; }
|
||||
.tape-holes { display: flex; gap: 4px; margin-top: 6px; }
|
||||
.tape-holes span { width: 14px; height: 14px; border-radius: 50%; background: var(--ink); box-shadow: inset 0 0 0 3px var(--cream); }
|
||||
.tape.active .tape-holes span { box-shadow: inset 0 0 0 3px var(--pink); }
|
||||
Loading…
Add table
Add a link
Reference in a new issue