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 = () => {
setScreenSize((prev) => ({
...prev,
height: window.innerHeight - 120,
height: window.innerHeight - 100,
}));
};

View File

@@ -81,10 +81,13 @@ export default function GameAnalysis() {
maxWidth: "1200px",
}}
rowGap={2}
maxHeight={{ lg: "calc(95vh - 80px)", xs: "900px" }}
maxHeight={{ lg: "calc(95vh - 60px)", xs: "900px" }}
display="grid"
gridTemplateRows="repeat(3, auto) fit-content(100%)"
marginTop={isLgOrGreater && window.innerHeight > 780 ? 4 : 0}
gridTemplateRows={
gameEval
? "repeat(2, auto) max-content fit-content(100%) fit-content(100%) auto"
: "repeat(3, auto) fit-content(100%)"
}
size={{
xs: 12,
lg: "grow",
@@ -96,77 +99,91 @@ export default function GameAnalysis() {
<PanelToolBar key="review-panel-toolbar" />
)}
{isLgOrGreater && <Divider sx={{ marginX: "5%" }} />}
{!isLgOrGreater && !gameEval && <Divider sx={{ marginX: "5%" }} />}
{!isLgOrGreater && !gameEval && (
<PanelHeader key="analysis-panel-header" />
)}
<Box
sx={{
borderBottom: 1,
borderColor: "divider",
marginX: { sm: "5%", xs: undefined },
}}
>
<Tabs
value={tab}
onChange={(_, newValue) => setTab(newValue)}
aria-label="basic tabs example"
variant="fullWidth"
sx={{ minHeight: 0 }}
{!isLgOrGreater && (
<Box
sx={{
borderBottom: 1,
borderColor: "divider",
marginX: { sm: "5%", xs: undefined },
}}
>
<Tab
label="Analysis"
id="tab0"
icon={<Icon icon="mdi:magnify" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
<Tabs
value={tab}
onChange={(_, newValue) => setTab(newValue)}
aria-label="basic tabs example"
variant="fullWidth"
sx={{ minHeight: 0 }}
>
<Tab
label="Analysis"
id="tab0"
icon={<Icon icon="mdi:magnify" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
<Tab
label="Moves"
id="tab1"
icon={<Icon icon="mdi:format-list-bulleted" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
display: isGameLoaded ? undefined : "none",
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
<Tab
label="Moves"
id="tab1"
icon={<Icon icon="mdi:format-list-bulleted" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
display: isGameLoaded ? undefined : "none",
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
<Tab
label="Graph"
id="tab2"
icon={<Icon icon="mdi:chart-line" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
display: gameEval ? undefined : "none",
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
</Tabs>
</Box>
<Tab
label="Graph"
id="tab2"
icon={<Icon icon="mdi:chart-line" height={15} />}
iconPosition="start"
sx={{
textTransform: "none",
minHeight: 15,
display: gameEval ? undefined : "none",
padding: "5px 0em 12px",
}}
disableFocusRipple
/>
</Tabs>
</Box>
)}
<AnalysisTab role="tabpanel" hidden={tab !== 0} id="tabContent0" />
<ClassificationTab
<GraphTab
role="tabpanel"
hidden={tab !== 1}
id="tabContent1"
hidden={tab !== 2 && !isLgOrGreater}
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 && (
<Box>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,13 +47,13 @@ export default function NavBar({ darkMode, switchDarkMode }: Props) {
}}
enableColorOnDark
>
<Toolbar>
<Toolbar variant="dense">
<IconButton
size="large"
edge="start"
color="inherit"
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)}
>
<Icon icon="mdi:menu" />

View File

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