2022-07-14 08:53:26 +03:00
|
|
|
import * as React from "react";
|
|
|
|
|
import { FunctionComponent } from "react";
|
2026-10-16 09:37:50 +03:00
|
|
|
import { PitHighlight } from "@mancala/core";
|
|
|
|
|
import Stones from "./Stones";
|
2022-07-14 08:53:26 +03:00
|
|
|
|
2026-10-16 09:37:50 +03:00
|
|
|
export type PitMarks = {
|
|
|
|
|
highlight?: PitHighlight;
|
|
|
|
|
// Stones a hovered move would drop here.
|
|
|
|
|
preview?: number;
|
|
|
|
|
// The hovered move's last stone lands here, or the shown move's did.
|
|
|
|
|
land?: boolean;
|
|
|
|
|
// The last move was played from here.
|
|
|
|
|
last?: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function getMarkClasses(marks: PitMarks): string {
|
|
|
|
|
const highlight = marks.highlight === "capture" ? " cap" : marks.highlight === "selected" ? " sel"
|
|
|
|
|
: marks.highlight ? " drop" : "";
|
|
|
|
|
return highlight + (marks.preview ? " pv" : "") + (marks.land ? " land" : "") + (marks.last ? " last" : "");
|
2026-07-02 23:40:45 +03:00
|
|
|
}
|
2022-07-14 08:53:26 +03:00
|
|
|
|
2026-10-16 09:37:50 +03:00
|
|
|
// A pit in the top or bottom row. Position is its column from the left, 0-5. The viewer's pits are buttons.
|
|
|
|
|
const PitView: FunctionComponent<{
|
|
|
|
|
index: number;
|
|
|
|
|
stoneCount: number;
|
|
|
|
|
top: boolean;
|
|
|
|
|
position: number;
|
|
|
|
|
themeId: string;
|
|
|
|
|
label: string;
|
|
|
|
|
marks: PitMarks;
|
|
|
|
|
// The viewer can play this pit now.
|
|
|
|
|
playable?: boolean;
|
|
|
|
|
// Set for pits the viewer can click.
|
|
|
|
|
onClick?: () => void;
|
|
|
|
|
// Called with true when a playable pit is hovered or focused, with false when that ends.
|
|
|
|
|
onPreview?: (active: boolean) => void;
|
|
|
|
|
}> = ({ index, stoneCount, top, position, themeId, label, marks, playable, onClick, onPreview }) => {
|
|
|
|
|
const className = "pit" + (playable ? " mine" : "") + getMarkClasses(marks);
|
|
|
|
|
const style = {
|
|
|
|
|
"--hr": top ? "1" : "2",
|
|
|
|
|
"--hc": String(position + 2),
|
|
|
|
|
"--vr": String(position + 2),
|
|
|
|
|
"--vc": top ? "2" : "1",
|
|
|
|
|
} as React.CSSProperties;
|
|
|
|
|
const content = (
|
|
|
|
|
<>
|
|
|
|
|
<Stones count={stoneCount} seed={index * 5.3 + 1} themeId={themeId} />
|
|
|
|
|
<span className={top ? "badge t" : "badge b"}>{stoneCount}</span>
|
|
|
|
|
{!!marks.preview && <span className="pvchip">{`+${marks.preview}`}</span>}
|
|
|
|
|
</>
|
2026-07-02 23:40:45 +03:00
|
|
|
);
|
2026-10-16 09:37:50 +03:00
|
|
|
if (!onClick) {
|
|
|
|
|
return <div className={className} style={style} role="img" aria-label={label}>{content}</div>;
|
|
|
|
|
}
|
2026-07-02 23:40:45 +03:00
|
|
|
return (
|
2026-10-16 09:37:50 +03:00
|
|
|
<button
|
|
|
|
|
className={className}
|
|
|
|
|
style={style}
|
|
|
|
|
aria-label={label}
|
|
|
|
|
aria-disabled={!playable}
|
2026-07-02 23:40:45 +03:00
|
|
|
onClick={onClick}
|
2026-10-16 09:37:50 +03:00
|
|
|
onMouseEnter={() => playable && onPreview?.(true)}
|
|
|
|
|
onMouseLeave={() => onPreview?.(false)}
|
|
|
|
|
onFocus={() => playable && onPreview?.(true)}
|
|
|
|
|
onBlur={() => onPreview?.(false)}>
|
|
|
|
|
{content}
|
|
|
|
|
</button>
|
2026-07-02 23:40:45 +03:00
|
|
|
);
|
2022-07-14 08:53:26 +03:00
|
|
|
};
|
|
|
|
|
|
2026-10-16 09:37:50 +03:00
|
|
|
export default PitView;
|