style : analysis card UI rework for desktop

This commit is contained in:
GuillaumeSD
2025-06-02 01:11:38 +02:00
parent 629fccf484
commit f782b55b5a
11 changed files with 117 additions and 109 deletions

View File

@@ -24,7 +24,7 @@ export const useScreenSize = () => {
const handleResize = () => { const handleResize = () => {
setScreenSize((prev) => ({ setScreenSize((prev) => ({
...prev, ...prev,
height: window.innerHeight - 120, height: window.innerHeight - 100,
})); }));
}; };

View File

@@ -81,10 +81,13 @@ export default function GameAnalysis() {
maxWidth: "1200px", maxWidth: "1200px",
}} }}
rowGap={2} rowGap={2}
maxHeight={{ lg: "calc(95vh - 80px)", xs: "900px" }} maxHeight={{ lg: "calc(95vh - 60px)", xs: "900px" }}
display="grid" display="grid"
gridTemplateRows="repeat(3, auto) fit-content(100%)" gridTemplateRows={
marginTop={isLgOrGreater && window.innerHeight > 780 ? 4 : 0} gameEval
? "repeat(2, auto) max-content fit-content(100%) fit-content(100%) auto"
: "repeat(3, auto) fit-content(100%)"
}
size={{ size={{
xs: 12, xs: 12,
lg: "grow", lg: "grow",
@@ -96,11 +99,14 @@ export default function GameAnalysis() {
<PanelToolBar key="review-panel-toolbar" /> <PanelToolBar key="review-panel-toolbar" />
)} )}
{isLgOrGreater && <Divider sx={{ marginX: "5%" }} />}
{!isLgOrGreater && !gameEval && <Divider sx={{ marginX: "5%" }} />} {!isLgOrGreater && !gameEval && <Divider sx={{ marginX: "5%" }} />}
{!isLgOrGreater && !gameEval && ( {!isLgOrGreater && !gameEval && (
<PanelHeader key="analysis-panel-header" /> <PanelHeader key="analysis-panel-header" />
)} )}
{!isLgOrGreater && (
<Box <Box
sx={{ sx={{
borderBottom: 1, borderBottom: 1,
@@ -157,16 +163,27 @@ export default function GameAnalysis() {
/> />
</Tabs> </Tabs>
</Box> </Box>
)}
<AnalysisTab role="tabpanel" hidden={tab !== 0} id="tabContent0" /> <GraphTab
<ClassificationTab
role="tabpanel" role="tabpanel"
hidden={tab !== 1} hidden={tab !== 2 && !isLgOrGreater}
id="tabContent1" id="tabContent2"
/> />
<GraphTab role="tabpanel" hidden={tab !== 2} id="tabContent2" /> <AnalysisTab
role="tabpanel"
hidden={tab !== 0 && !isLgOrGreater}
id="tabContent0"
/>
{isGameLoaded && (
<ClassificationTab
role="tabpanel"
hidden={tab !== 1 && !isLgOrGreater}
id="tabContent1"
/>
)}
{isLgOrGreater && ( {isLgOrGreater && (
<Box> <Box>

View File

@@ -1,15 +1,9 @@
import { import { Grid2 as Grid, Grid2Props as GridProps, List } from "@mui/material";
Grid2 as Grid,
Grid2Props as GridProps,
List,
Typography,
} from "@mui/material";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { import {
boardAtom, boardAtom,
currentPositionAtom, currentPositionAtom,
engineMultiPvAtom, engineMultiPvAtom,
gameAtom,
gameEvalAtom, gameEvalAtom,
} from "../../states"; } from "../../states";
import LineEvaluation from "./lineEvaluation"; import LineEvaluation from "./lineEvaluation";
@@ -21,19 +15,9 @@ import Opening from "./opening";
export default function AnalysisTab(props: GridProps) { export default function AnalysisTab(props: GridProps) {
const linesNumber = useAtomValue(engineMultiPvAtom); const linesNumber = useAtomValue(engineMultiPvAtom);
const position = useAtomValue(currentPositionAtom); const position = useAtomValue(currentPositionAtom);
const game = useAtomValue(gameAtom);
const board = useAtomValue(boardAtom); const board = useAtomValue(boardAtom);
const gameEval = useAtomValue(gameEvalAtom); const gameEval = useAtomValue(gameEvalAtom);
const boardHistory = board.history();
const gameHistory = game.history();
const isGameOver =
boardHistory.length > 0 &&
(board.isCheckmate() ||
board.isDraw() ||
boardHistory.join() === gameHistory.join());
const linesSkeleton: LineEval[] = Array.from({ length: linesNumber }).map( const linesSkeleton: LineEval[] = Array.from({ length: linesNumber }).map(
(_, i) => ({ pv: [`${i}`], depth: 0, multiPv: i + 1 }) (_, i) => ({ pv: [`${i}`], depth: 0, multiPv: i + 1 })
); );
@@ -49,7 +33,7 @@ export default function AnalysisTab(props: GridProps) {
justifyContent="center" justifyContent="center"
alignItems="start" alignItems="start"
height="100%" height="100%"
rowGap={1.2} rowGap={0.8}
{...props} {...props}
sx={ sx={
props.hidden props.hidden
@@ -77,16 +61,8 @@ export default function AnalysisTab(props: GridProps) {
<Opening /> <Opening />
{isGameOver && (
<Grid size={12}>
<Typography align="center" fontSize="0.9rem">
Game is over
</Typography>
</Grid>
)}
<Grid container justifyContent="center" alignItems="center" size={12}> <Grid container justifyContent="center" alignItems="center" size={12}>
<List sx={{ maxWidth: "95%", padding: 0 }}> <List sx={{ width: { xs: "95%", lg: "90%" }, padding: 0 }}>
{!board.isCheckmate() && {!board.isCheckmate() &&
engineLines.map((line) => ( engineLines.map((line) => (
<LineEvaluation key={line.multiPv} line={line} /> <LineEvaluation key={line.multiPv} line={line} />

View File

@@ -50,11 +50,12 @@ export default function LineEvaluation({ line }: Props) {
<ListItem disablePadding> <ListItem disablePadding>
<Typography <Typography
marginRight={1.5} marginRight={1.5}
marginY={0.5} marginY={0.3}
paddingY={0.2} paddingY={0.2}
noWrap noWrap
overflow="visible" overflow="visible"
width="3.5em" width="3.5em"
minWidth="3.5em"
textAlign="center" textAlign="center"
fontSize="0.8rem" fontSize="0.8rem"
sx={{ sx={{
@@ -78,13 +79,9 @@ export default function LineEvaluation({ line }: Props) {
)} )}
</Typography> </Typography>
<Typography <Typography noWrap fontSize="0.9rem">
noWrap
maxWidth={{ xs: "12em", sm: "25em", md: "30em", lg: "25em" }}
fontSize="0.9rem"
>
{showSkeleton ? ( {showSkeleton ? (
<Skeleton variant="rounded" animation="wave" width="15em" /> <Skeleton variant="rounded" animation="wave" width="20em" />
) : ( ) : (
line.pv.map((uci, i) => { line.pv.map((uci, i) => {
const san = uciToSan(uci); const san = uciToSan(uci);

View File

@@ -1,6 +1,6 @@
import { Grid2 as Grid, Skeleton, Stack, Typography } from "@mui/material"; import { Grid2 as Grid, Skeleton, Stack, Typography } from "@mui/material";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { boardAtom, currentPositionAtom } from "../../states"; import { boardAtom, currentPositionAtom, gameAtom } from "../../states";
import { useMemo } from "react"; import { useMemo } from "react";
import { moveLineUciToSan } from "@/lib/chess"; import { moveLineUciToSan } from "@/lib/chess";
import { MoveClassification } from "@/types/enums"; import { MoveClassification } from "@/types/enums";
@@ -9,6 +9,7 @@ import Image from "next/image";
export default function MoveInfo() { export default function MoveInfo() {
const position = useAtomValue(currentPositionAtom); const position = useAtomValue(currentPositionAtom);
const board = useAtomValue(boardAtom); const board = useAtomValue(boardAtom);
const game = useAtomValue(gameAtom);
const bestMove = position?.lastEval?.bestMove; const bestMove = position?.lastEval?.bestMove;
@@ -21,8 +22,17 @@ export default function MoveInfo() {
return moveLineUciToSan(lastPosition)(bestMove); return moveLineUciToSan(lastPosition)(bestMove);
}, [bestMove, board]); }, [bestMove, board]);
const boardHistory = board.history();
const gameHistory = game.history();
if (board.history().length === 0) return null; if (board.history().length === 0) return null;
const isGameOver =
boardHistory.length > 0 &&
(board.isCheckmate() ||
board.isDraw() ||
boardHistory.join() === gameHistory.join());
if (!bestMoveSan) { if (!bestMoveSan) {
return ( return (
<Grid <Grid
@@ -86,6 +96,7 @@ export default function MoveInfo() {
</Typography> </Typography>
</Stack> </Stack>
)} )}
{bestMoveLabel && ( {bestMoveLabel && (
<Stack direction="row" alignItems="center" spacing={1}> <Stack direction="row" alignItems="center" spacing={1}>
<Image <Image
@@ -103,6 +114,12 @@ export default function MoveInfo() {
</Typography> </Typography>
</Stack> </Stack>
)} )}
{isGameOver && (
<Typography align="center" fontSize="0.9rem">
Game is over
</Typography>
)}
</Grid> </Grid>
); );
} }

View File

@@ -9,7 +9,7 @@ export default function ClassificationTab(props: GridProps) {
justifyContent="center" justifyContent="center"
alignItems="start" alignItems="start"
height="100%" height="100%"
maxHeight="18rem" maxHeight={{ xs: "18rem", lg: "none" }}
{...props} {...props}
sx={ sx={
props.hidden ? { display: "none" } : { overflow: "hidden", ...props.sx } props.hidden ? { display: "none" } : { overflow: "hidden", ...props.sx }

View File

@@ -16,9 +16,10 @@ export default function MovesClassificationsRecap() {
container container
justifyContent="center" justifyContent="center"
alignItems="center" alignItems="center"
rowGap={1} rowGap={0.7}
sx={{ scrollbarWidth: "thin", overflowY: "auto" }} sx={{ scrollbarWidth: "thin", overflowY: "auto" }}
maxHeight="100%" height="100%"
maxHeight="22rem"
size={6} size={6}
> >
<Grid <Grid

View File

@@ -42,9 +42,9 @@ export default function MovesPanel() {
container container
justifyContent="center" justifyContent="center"
alignItems="start" alignItems="start"
gap={0.8} gap={0.6}
sx={{ scrollbarWidth: "thin", overflowY: "auto" }} sx={{ scrollbarWidth: "thin", overflowY: "auto" }}
maxHeight="100%" height="100%"
size={6} size={6}
id="moves-panel" id="moves-panel"
> >

View File

@@ -93,14 +93,14 @@ export default function GraphTab(props: GridProps) {
sx={ sx={
props.hidden props.hidden
? { display: "none" } ? { display: "none" }
: { marginY: 1, overflow: "hidden", overflowY: "auto", ...props.sx } : { overflow: "hidden", overflowY: "auto", ...props.sx }
} }
> >
<Box <Box
width="max(35rem, 90%)" width="max(35rem, 90%)"
maxWidth="100%" maxWidth="100%"
height="max(8rem, 100%)" height="min(8rem, 8vh)"
maxHeight="15rem" maxHeight="6rem"
sx={{ sx={{
backgroundColor: "#2e2e2e", backgroundColor: "#2e2e2e",
borderRadius: "15px", borderRadius: "15px",

View File

@@ -47,13 +47,13 @@ export default function NavBar({ darkMode, switchDarkMode }: Props) {
}} }}
enableColorOnDark enableColorOnDark
> >
<Toolbar> <Toolbar variant="dense">
<IconButton <IconButton
size="large" size="large"
edge="start" edge="start"
color="inherit" color="inherit"
aria-label="menu" aria-label="menu"
sx={{ mr: "min(0.5vw, 0.6rem)" }} sx={{ mr: "min(0.5vw, 0.6rem)", padding: 1, my: 1 }}
onClick={() => setDrawerOpen((val) => !val)} onClick={() => setDrawerOpen((val) => !val)}
> >
<Icon icon="mdi:menu" /> <Icon icon="mdi:menu" />

View File

@@ -36,7 +36,7 @@ export default function Layout({ children }: PropsWithChildren) {
darkMode={isDarkMode} darkMode={isDarkMode}
switchDarkMode={() => setDarkMode((val) => !val)} switchDarkMode={() => setDarkMode((val) => !val)}
/> />
<main style={{ margin: "3vh 2vw" }}>{children}</main> <main style={{ margin: "2vh 1vw" }}>{children}</main>
</ThemeProvider> </ThemeProvider>
); );
} }