62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import {
|
|
C4_COLS,
|
|
C4_ROWS,
|
|
c4DropRow,
|
|
c4WinningCells,
|
|
type Cell,
|
|
} from '@chat-app/shared/chat';
|
|
|
|
interface Props {
|
|
board: Cell[];
|
|
myPlayerIdx: 0 | 1 | null;
|
|
disabled: boolean;
|
|
onMove: (column: number) => void;
|
|
}
|
|
|
|
export function ConnectFourBoard({ board, myPlayerIdx, disabled, onMove }: Props) {
|
|
const winCells = c4WinningCells(board);
|
|
const winSet = new Set<number>(winCells ?? []);
|
|
|
|
return (
|
|
<div
|
|
className="mx-auto w-full max-w-2xl rounded-2xl bg-sky-900/40 p-3"
|
|
role="grid"
|
|
aria-label="Vier-Gewinnt"
|
|
>
|
|
<div
|
|
className="grid gap-1.5"
|
|
style={{ gridTemplateColumns: 'repeat(' + C4_COLS + ', minmax(0, 1fr))' }}
|
|
>
|
|
{Array.from({ length: C4_ROWS * C4_COLS }, (_, idx) => {
|
|
const cell = board[idx];
|
|
const col = idx % C4_COLS;
|
|
const dropTo = c4DropRow(board, col);
|
|
const canClickColumn = !disabled && dropTo >= 0 && myPlayerIdx !== null;
|
|
const inWin = winSet.has(idx);
|
|
return (
|
|
<button
|
|
key={idx}
|
|
type="button"
|
|
onClick={() => canClickColumn && onMove(col)}
|
|
disabled={!canClickColumn}
|
|
aria-label={'Spalte ' + (col + 1) + (cell !== null ? ' belegt' : '')}
|
|
className={
|
|
'flex aspect-square items-center justify-center rounded-full border-2 transition ' +
|
|
(inWin
|
|
? 'border-emerald-300 bg-emerald-400 shadow-[0_0_12px_rgba(110,231,183,0.7)]'
|
|
: cell === 0
|
|
? 'border-rose-300 bg-rose-500'
|
|
: cell === 1
|
|
? 'border-amber-300 bg-amber-400'
|
|
: canClickColumn
|
|
? 'cursor-pointer border-sky-700 bg-sky-950 hover:bg-sky-900'
|
|
: 'cursor-not-allowed border-sky-800 bg-sky-950 opacity-80')
|
|
}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|