collapsable side panel
This commit is contained in:
@@ -7,9 +7,11 @@ import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
|
||||
import { Paper, Box, Typography, Accordion, AccordionSummary, AccordionDetails, Tabs, Tab } from '@mui/material';
|
||||
import { Paper, Box, Typography, Accordion, AccordionSummary, AccordionDetails, Tabs, Tab, Divider, Button, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faUpload, faArrowRotateRight, faChevronLeft, faChevronRight, faBook, faHammer } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
function TacticDoc(props) {
|
||||
return (
|
||||
@@ -54,8 +56,7 @@ function LemmaDocs({ lemmas }) {
|
||||
}, [lemmas]);
|
||||
|
||||
return (
|
||||
<Paper sx={{ px: 2, py: 1, mt: 2 }}>
|
||||
<Typography variant="h5">Inventory</Typography>
|
||||
<div>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs
|
||||
value={curCategory}
|
||||
@@ -64,20 +65,39 @@ function LemmaDocs({ lemmas }) {
|
||||
</Tabs>
|
||||
</Box>
|
||||
{curCategory && categories.get(curCategory).map((lemma) => <LemmaDoc lemma={lemma} key={lemma.name} />)}
|
||||
</Paper>
|
||||
</div>)
|
||||
}
|
||||
|
||||
function LeftPanel({ spells, inventory, showSidePanel, setShowSidePanel }) {
|
||||
|
||||
return (
|
||||
<List className="side">
|
||||
<ListItem key="tactics" disablePadding sx={{ display: 'block' }}>
|
||||
<ListItemButton sx={{ minHeight: 48, justifyContent: open ? 'initial' : 'center', px: 2.5 }} onClick={() => setShowSidePanel(true)}>
|
||||
<ListItemIcon sx={{minWidth: 0, mr: open ? 3 : 'auto', justifyContent: 'center' }}>
|
||||
<FontAwesomeIcon icon={faHammer}></FontAwesomeIcon>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Tactics" sx={{ display: showSidePanel ? null : "none" }} />
|
||||
</ListItemButton>
|
||||
{spells && spells.length > 0 &&
|
||||
<Paper sx={{ px: 2, py: 1, display: showSidePanel ? null : "none" }} elevation={0} >
|
||||
{spells.map((spell) => <TacticDoc key={spell.name} tactic={spell} />)}
|
||||
</Paper>}
|
||||
</ListItem>
|
||||
<ListItem key="lemmas" disablePadding sx={{ display: 'block' }}>
|
||||
<ListItemButton sx={{ minHeight: 48, justifyContent: open ? 'initial' : 'center', px: 2.5 }} >
|
||||
<ListItemIcon sx={{minWidth: 0, mr: open ? 3 : 'auto', justifyContent: 'center' }}>
|
||||
<FontAwesomeIcon icon={faBook}></FontAwesomeIcon>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Lemmas" sx={{ display: showSidePanel ? null : "none" }} />
|
||||
</ListItemButton>
|
||||
{inventory && inventory.length > 0 &&
|
||||
<Paper sx={{ px: 2, py: 1, mt: 2, display: showSidePanel ? null : "none" }} elevation={0} >
|
||||
<LemmaDocs lemmas={inventory} />
|
||||
</Paper>}
|
||||
</ListItem>
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
||||
function LeftPanel({ spells, inventory }) {
|
||||
return (
|
||||
<Box>
|
||||
{spells && spells.length > 0 &&
|
||||
<Paper sx={{ px: 2, py: 1 }}>
|
||||
<Typography variant="h5" sx={{ mb: 2 }}>Spell book</Typography>
|
||||
{spells.map((spell) => <TacticDoc key={spell.name} tactic={spell} />)}
|
||||
</Paper>}
|
||||
{inventory && inventory.length > 0 && <LemmaDocs lemmas={inventory} />}
|
||||
</Box>)
|
||||
}
|
||||
|
||||
export default LeftPanel;
|
||||
|
||||
+120
-27
@@ -7,7 +7,8 @@ import '@fontsource/roboto/700.css';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { MathJax } from "better-react-mathjax";
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { Box, Button, CircularProgress, FormControlLabel, FormGroup, Switch } from '@mui/material';
|
||||
import { Box, Button, CircularProgress, FormControlLabel, FormGroup, Switch, IconButton } from '@mui/material';
|
||||
import MuiDrawer from '@mui/material/Drawer';
|
||||
import Grid from '@mui/material/Unstable_Grid2';
|
||||
import LeftPanel from './LeftPanel';
|
||||
import { LeanTaskGutter } from 'lean4web/client/src/editor/taskgutter';
|
||||
@@ -22,6 +23,82 @@ import { ConnectionContext } from '../connection';
|
||||
import Infoview from './Infoview';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useLoadLevelQuery } from '../game/api';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faUpload, faArrowRotateRight, faChevronLeft, faChevronRight, faBook, faDownload } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
import { styled, useTheme, Theme, CSSObject } from '@mui/material/styles';
|
||||
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import List from '@mui/material/List';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import InboxIcon from '@mui/icons-material/MoveToInbox';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
|
||||
|
||||
/** Drawer Test */
|
||||
const drawerWidth = 400;
|
||||
|
||||
const openedMixin = (theme: Theme): CSSObject => ({
|
||||
width: drawerWidth,
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
});
|
||||
|
||||
const closedMixin = (theme: Theme): CSSObject => ({
|
||||
transition: theme.transitions.create('width', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
overflowX: 'hidden',
|
||||
width: `calc(${theme.spacing(7)} + 1px)`,
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: `calc(${theme.spacing(8)} + 1px)`,
|
||||
},
|
||||
});
|
||||
|
||||
const DrawerHeader = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(0, 1),
|
||||
// necessary for content to be below app bar
|
||||
...theme.mixins.toolbar,
|
||||
}));
|
||||
|
||||
interface AppBarProps extends MuiAppBarProps {
|
||||
open?: boolean;
|
||||
}
|
||||
|
||||
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
|
||||
({ theme, open }) => ({
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
boxSizing: 'border-box',
|
||||
...(open && {
|
||||
...openedMixin(theme),
|
||||
'& .MuiDrawer-paper': openedMixin(theme),
|
||||
}),
|
||||
...(!open && {
|
||||
...closedMixin(theme),
|
||||
'& .MuiDrawer-paper': closedMixin(theme),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
/** End Drawer Test */
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +114,14 @@ function Level() {
|
||||
const infoviewRef = useRef<HTMLDivElement>(null)
|
||||
const messagePanelRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [showSidePanel, setShowSidePanel] = useState(true)
|
||||
|
||||
const toggleSidePanel = () => {
|
||||
setShowSidePanel(!showSidePanel)
|
||||
}
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
// Scroll to top when loading a new level
|
||||
messagePanelRef.current!.scrollTo(0,0)
|
||||
@@ -49,35 +134,43 @@ function Level() {
|
||||
|
||||
return <>
|
||||
<Box style={level.isLoading ? null : {display: "none"}} display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box>
|
||||
<Grid style={level.isLoading ? {display: "none"} : null} className="level" container sx={{ mt: 0, ml: 0, mr: 0 }} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
|
||||
<Grid xs={4} className="doc-panel">
|
||||
<LeftPanel spells={level?.data?.tactics} inventory={level?.data?.tactics} />
|
||||
</Grid>
|
||||
<Grid xs={9} sm={6} className="main-panel">
|
||||
<div ref={messagePanelRef} className="message-panel">
|
||||
<MathJax><ReactMarkdown>{level?.data?.introduction}</ReactMarkdown></MathJax>
|
||||
</div>
|
||||
<div className="exercise">
|
||||
<h4>Aufgabe:</h4>
|
||||
<MathJax><ReactMarkdown>{level?.data?.descrText}</ReactMarkdown></MathJax>
|
||||
<div className="statement"><code>{level?.data?.descrFormat}</code></div>
|
||||
<div ref={codeviewRef} className="codeview"></div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid xs={3} className="info-panel">
|
||||
<Box style={level.isLoading ? {display: "none"} : null} display="flex" className="level" sx={{ mt: 0, ml: 0, mr: 0 }} >
|
||||
<Drawer variant="permanent" open={showSidePanel} className="doc-panel">
|
||||
<DrawerHeader>
|
||||
</DrawerHeader>
|
||||
<Divider />
|
||||
<IconButton onClick={toggleSidePanel}>
|
||||
<FontAwesomeIcon icon={showSidePanel ? faChevronLeft : faChevronRight}></FontAwesomeIcon>
|
||||
</IconButton>
|
||||
<LeftPanel spells={level?.data?.tactics} inventory={level?.data?.lemmas} showSidePanel={showSidePanel} setShowSidePanel={setShowSidePanel} />
|
||||
</Drawer>
|
||||
<Grid container columnSpacing={{ xs: 1, sm: 2, md: 3 }} sx={{ flexGrow: 1, p: 3 }} className="main-grid">
|
||||
<Grid xs={8} className="main-panel">
|
||||
<div ref={messagePanelRef} className="message-panel">
|
||||
<MathJax><ReactMarkdown>{level?.data?.introduction}</ReactMarkdown></MathJax>
|
||||
</div>
|
||||
<div className="exercise">
|
||||
<h4>Aufgabe:</h4>
|
||||
<MathJax><ReactMarkdown>{level?.data?.descrText}</ReactMarkdown></MathJax>
|
||||
<div className="statement"><code>{level?.data?.descrFormat}</code></div>
|
||||
<div ref={codeviewRef} className="codeview"></div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid xs={4} className="info-panel">
|
||||
|
||||
<Button disabled={levelId <= 1} component={RouterLink} to={`/world/${worldId}/level/${levelId - 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button>
|
||||
<Button disabled={false} component={RouterLink} to={`/world/${worldId}/level/${levelId + 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button>
|
||||
<Button disabled={levelId <= 1} component={RouterLink} to={`/world/${worldId}/level/${levelId - 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button>
|
||||
<Button disabled={false} component={RouterLink} to={`/world/${worldId}/level/${levelId + 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button>
|
||||
|
||||
<div style={{display: expertInfoview ? 'block' : 'none' }} ref={infoviewRef} className="infoview vscode-light"></div>
|
||||
<div style={{display: expertInfoview ? 'none' : 'block' }}>
|
||||
<Infoview leanClient={connection.getLeanClient()} editor={editor} editorApi={infoProvider?.getApi()} />
|
||||
</div>
|
||||
<FormGroup>
|
||||
<FormControlLabel onChange={() => { setExpertInfoview(!expertInfoview) }} control={<Switch />} label="Expert mode" />
|
||||
</FormGroup>
|
||||
<div style={{display: expertInfoview ? 'block' : 'none' }} ref={infoviewRef} className="infoview vscode-light"></div>
|
||||
<div style={{display: expertInfoview ? 'none' : 'block' }}>
|
||||
<Infoview leanClient={connection.getLeanClient()} editor={editor} editorApi={infoProvider?.getApi()} />
|
||||
</div>
|
||||
<FormGroup>
|
||||
<FormControlLabel onChange={() => { setExpertInfoview(!expertInfoview) }} control={<Switch />} label="Expert mode" />
|
||||
</FormGroup>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.main-panel, .info-panel, .doc-panel {
|
||||
.main-panel, .info-panel {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -35,10 +35,18 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.doc-panel li {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12); /* This should be teh same colour as `divider` in LeftPanel.tsx */
|
||||
}
|
||||
|
||||
.doc-panel li:first-of-type {
|
||||
border-top: 1px solid rgb(0, 0, 0, 0.12); /* This should be teh same colour as `divider` in LeftPanel.tsx */
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
/* TODO: For development purposes only */
|
||||
/***************************************/
|
||||
.doc-panel {
|
||||
/* .doc-panel {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
@@ -61,3 +69,8 @@
|
||||
.codeview {
|
||||
border: 1px solid rgb(98, 148, 255);
|
||||
}
|
||||
|
||||
.main-grid {
|
||||
border: 1px solid rgb(255, 0, 0);
|
||||
margin: 1px;
|
||||
} */
|
||||
|
||||
Reference in New Issue
Block a user