style : analysis card UI rework for desktop
This commit is contained in:
@@ -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,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,77 +99,91 @@ 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" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
{!isLgOrGreater && (
|
||||||
sx={{
|
<Box
|
||||||
borderBottom: 1,
|
sx={{
|
||||||
borderColor: "divider",
|
borderBottom: 1,
|
||||||
marginX: { sm: "5%", xs: undefined },
|
borderColor: "divider",
|
||||||
}}
|
marginX: { sm: "5%", xs: undefined },
|
||||||
>
|
}}
|
||||||
<Tabs
|
|
||||||
value={tab}
|
|
||||||
onChange={(_, newValue) => setTab(newValue)}
|
|
||||||
aria-label="basic tabs example"
|
|
||||||
variant="fullWidth"
|
|
||||||
sx={{ minHeight: 0 }}
|
|
||||||
>
|
>
|
||||||
<Tab
|
<Tabs
|
||||||
label="Analysis"
|
value={tab}
|
||||||
id="tab0"
|
onChange={(_, newValue) => setTab(newValue)}
|
||||||
icon={<Icon icon="mdi:magnify" height={15} />}
|
aria-label="basic tabs example"
|
||||||
iconPosition="start"
|
variant="fullWidth"
|
||||||
sx={{
|
sx={{ minHeight: 0 }}
|
||||||
textTransform: "none",
|
>
|
||||||
minHeight: 15,
|
<Tab
|
||||||
padding: "5px 0em 12px",
|
label="Analysis"
|
||||||
}}
|
id="tab0"
|
||||||
disableFocusRipple
|
icon={<Icon icon="mdi:magnify" height={15} />}
|
||||||
/>
|
iconPosition="start"
|
||||||
|
sx={{
|
||||||
|
textTransform: "none",
|
||||||
|
minHeight: 15,
|
||||||
|
padding: "5px 0em 12px",
|
||||||
|
}}
|
||||||
|
disableFocusRipple
|
||||||
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="Moves"
|
label="Moves"
|
||||||
id="tab1"
|
id="tab1"
|
||||||
icon={<Icon icon="mdi:format-list-bulleted" height={15} />}
|
icon={<Icon icon="mdi:format-list-bulleted" height={15} />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
sx={{
|
sx={{
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
minHeight: 15,
|
minHeight: 15,
|
||||||
display: isGameLoaded ? undefined : "none",
|
display: isGameLoaded ? undefined : "none",
|
||||||
padding: "5px 0em 12px",
|
padding: "5px 0em 12px",
|
||||||
}}
|
}}
|
||||||
disableFocusRipple
|
disableFocusRipple
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="Graph"
|
label="Graph"
|
||||||
id="tab2"
|
id="tab2"
|
||||||
icon={<Icon icon="mdi:chart-line" height={15} />}
|
icon={<Icon icon="mdi:chart-line" height={15} />}
|
||||||
iconPosition="start"
|
iconPosition="start"
|
||||||
sx={{
|
sx={{
|
||||||
textTransform: "none",
|
textTransform: "none",
|
||||||
minHeight: 15,
|
minHeight: 15,
|
||||||
display: gameEval ? undefined : "none",
|
display: gameEval ? undefined : "none",
|
||||||
padding: "5px 0em 12px",
|
padding: "5px 0em 12px",
|
||||||
}}
|
}}
|
||||||
disableFocusRipple
|
disableFocusRipple
|
||||||
/>
|
/>
|
||||||
</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>
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user