import { memo } from 'react'; import type { Orientation, BoardTheme, NotationVisuals } from '../types'; import { FILES, RANKS } from '../types'; interface NotationProps { orientation: Orientation; theme: BoardTheme; showOnMargin: boolean; marginThickness: number; marginRadius: string | number; visuals?: Partial; } export const Notation = memo(function Notation({ orientation, theme, showOnMargin, marginThickness, marginRadius, visuals = {}, }: NotationProps) { const asWhite = orientation === 'white'; const files = asWhite ? FILES : [...FILES].reverse(); const ranks = asWhite ? [...RANKS].reverse() : RANKS; if (showOnMargin) { return ( ); } return ; }); const DEFAULT_NOTATION_VISUALS: Required = { fontFamily: 'var(--font-geist-sans, system-ui, sans-serif)', fontSize: '12px', fontWeight: 500, color: '', onLightSquareColor: '', onDarkSquareColor: '', opacity: 0.85, onBoardFontSize: '10px', onBoardLeftOffset: '2px', onBoardBottomOffset: '1px', }; const coordStyle = (theme: BoardTheme, visuals: Required): React.CSSProperties => ({ display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: visuals.fontSize, fontWeight: visuals.fontWeight, fontFamily: visuals.fontFamily, color: visuals.color || theme.lightSquare, userSelect: 'none', }); function MarginNotation({ files, ranks, theme, thickness, radius, visuals, }: { files: readonly string[]; ranks: readonly string[]; theme: BoardTheme; thickness: number; radius: string | number; visuals: Partial; }) { const notationVisuals = { ...DEFAULT_NOTATION_VISUALS, ...visuals }; const bg = theme.margin || theme.darkSquare; const cs = coordStyle(theme, notationVisuals); return ( <> {/* Top margin */}
{/* Bottom margin with file labels */}
{files.map((f) => (
{f}
))}
{/* Left margin with rank labels */}
{ranks.map((r) => (
{r}
))}
{/* Right margin */}
); } // Lichess-style on-board notation: // - File letters (a-h) at bottom-right corner of each bottom-row square // - Rank numbers (1-8) at top-left corner of each left-column square // - Alternating colors to contrast with the square they're on function OnBoardNotation({ files, ranks, theme, orientation, visuals, }: { files: readonly string[]; ranks: readonly string[]; theme: BoardTheme; orientation: Orientation; visuals: Partial; }) { const asWhite = orientation === 'white'; const notationVisuals = { ...DEFAULT_NOTATION_VISUALS, ...visuals }; // Resolve color for a notation label based on the square it sits on. // Priority: color (uniform override) > onLightSquareColor/onDarkSquareColor > theme defaults. const lightSqColor = notationVisuals.color || notationVisuals.onLightSquareColor || theme.darkSquare; const darkSqColor = notationVisuals.color || notationVisuals.onDarkSquareColor || theme.lightSquare; return (
{/* File letters - bottom-right corner of each square in the bottom row */} {files.map((f, i) => { const bottomRank = asWhite ? 0 : 7; const isLight = (i + bottomRank) % 2 !== 0; return (
{f}
); })} {/* Rank numbers - top-left corner of each square in the left column */} {ranks.map((r, i) => { const leftFile = asWhite ? 0 : 7; const rankIdx = asWhite ? 7 - i : i; const isLight = (leftFile + rankIdx) % 2 !== 0; return (
{r}
); })}
); }