collapsable side panel

pull/43/head
Jon Eugster 4 years ago
parent 513a6f0a3e
commit 802d748bf4

@ -7,9 +7,11 @@ import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css'; import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.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 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) { function TacticDoc(props) {
return ( return (
@ -54,8 +56,7 @@ function LemmaDocs({ lemmas }) {
}, [lemmas]); }, [lemmas]);
return ( return (
<Paper sx={{ px: 2, py: 1, mt: 2 }}> <div>
<Typography variant="h5">Inventory</Typography>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}> <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs <Tabs
value={curCategory} value={curCategory}
@ -64,20 +65,39 @@ function LemmaDocs({ lemmas }) {
</Tabs> </Tabs>
</Box> </Box>
{curCategory && categories.get(curCategory).map((lemma) => <LemmaDoc lemma={lemma} key={lemma.name} />)} {curCategory && categories.get(curCategory).map((lemma) => <LemmaDoc lemma={lemma} key={lemma.name} />)}
</Paper> </div>)
)
} }
function LeftPanel({ spells, inventory }) { function LeftPanel({ spells, inventory, showSidePanel, setShowSidePanel }) {
return ( return (
<Box> <List className="side">
{spells && spells.length > 0 && <ListItem key="tactics" disablePadding sx={{ display: 'block' }}>
<Paper sx={{ px: 2, py: 1 }}> <ListItemButton sx={{ minHeight: 48, justifyContent: open ? 'initial' : 'center', px: 2.5 }} onClick={() => setShowSidePanel(true)}>
<Typography variant="h5" sx={{ mb: 2 }}>Spell book</Typography> <ListItemIcon sx={{minWidth: 0, mr: open ? 3 : 'auto', justifyContent: 'center' }}>
{spells.map((spell) => <TacticDoc key={spell.name} tactic={spell} />)} <FontAwesomeIcon icon={faHammer}></FontAwesomeIcon>
</Paper>} </ListItemIcon>
{inventory && inventory.length > 0 && <LemmaDocs lemmas={inventory} />} <ListItemText primary="Tactics" sx={{ display: showSidePanel ? null : "none" }} />
</Box>) </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>
)
} }
export default LeftPanel; export default LeftPanel;

@ -7,7 +7,8 @@ import '@fontsource/roboto/700.css';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { MathJax } from "better-react-mathjax"; import { MathJax } from "better-react-mathjax";
import { Link as RouterLink } from 'react-router-dom'; 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 Grid from '@mui/material/Unstable_Grid2';
import LeftPanel from './LeftPanel'; import LeftPanel from './LeftPanel';
import { LeanTaskGutter } from 'lean4web/client/src/editor/taskgutter'; import { LeanTaskGutter } from 'lean4web/client/src/editor/taskgutter';
@ -22,6 +23,82 @@ import { ConnectionContext } from '../connection';
import Infoview from './Infoview'; import Infoview from './Infoview';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useLoadLevelQuery } from '../game/api'; 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 infoviewRef = useRef<HTMLDivElement>(null)
const messagePanelRef = useRef<HTMLDivElement>(null) const messagePanelRef = useRef<HTMLDivElement>(null)
const [showSidePanel, setShowSidePanel] = useState(true)
const toggleSidePanel = () => {
setShowSidePanel(!showSidePanel)
}
const theme = useTheme();
useEffect(() => { useEffect(() => {
// Scroll to top when loading a new level // Scroll to top when loading a new level
messagePanelRef.current!.scrollTo(0,0) messagePanelRef.current!.scrollTo(0,0)
@ -49,35 +134,43 @@ function Level() {
return <> return <>
<Box style={level.isLoading ? null : {display: "none"}} display="flex" alignItems="center" justifyContent="center" sx={{ height: "calc(100vh - 64px)" }}><CircularProgress /></Box> <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 }}> <Box style={level.isLoading ? {display: "none"} : null} display="flex" className="level" sx={{ mt: 0, ml: 0, mr: 0 }} >
<Grid xs={4} className="doc-panel"> <Drawer variant="permanent" open={showSidePanel} className="doc-panel">
<LeftPanel spells={level?.data?.tactics} inventory={level?.data?.tactics} /> <DrawerHeader>
</Grid> </DrawerHeader>
<Grid xs={9} sm={6} className="main-panel"> <Divider />
<div ref={messagePanelRef} className="message-panel"> <IconButton onClick={toggleSidePanel}>
<MathJax><ReactMarkdown>{level?.data?.introduction}</ReactMarkdown></MathJax> <FontAwesomeIcon icon={showSidePanel ? faChevronLeft : faChevronRight}></FontAwesomeIcon>
</div> </IconButton>
<div className="exercise"> <LeftPanel spells={level?.data?.tactics} inventory={level?.data?.lemmas} showSidePanel={showSidePanel} setShowSidePanel={setShowSidePanel} />
<h4>Aufgabe:</h4> </Drawer>
<MathJax><ReactMarkdown>{level?.data?.descrText}</ReactMarkdown></MathJax> <Grid container columnSpacing={{ xs: 1, sm: 2, md: 3 }} sx={{ flexGrow: 1, p: 3 }} className="main-grid">
<div className="statement"><code>{level?.data?.descrFormat}</code></div> <Grid xs={8} className="main-panel">
<div ref={codeviewRef} className="codeview"></div> <div ref={messagePanelRef} className="message-panel">
</div> <MathJax><ReactMarkdown>{level?.data?.introduction}</ReactMarkdown></MathJax>
</Grid> </div>
<Grid xs={3} className="info-panel"> <div className="exercise">
<h4>Aufgabe:</h4>
<Button disabled={levelId <= 1} component={RouterLink} to={`/world/${worldId}/level/${levelId - 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button> <MathJax><ReactMarkdown>{level?.data?.descrText}</ReactMarkdown></MathJax>
<Button disabled={false} component={RouterLink} to={`/world/${worldId}/level/${levelId + 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button> <div className="statement"><code>{level?.data?.descrFormat}</code></div>
<div ref={codeviewRef} className="codeview"></div>
<div style={{display: expertInfoview ? 'block' : 'none' }} ref={infoviewRef} className="infoview vscode-light"></div> </div>
<div style={{display: expertInfoview ? 'none' : 'block' }}> </Grid>
<Infoview leanClient={connection.getLeanClient()} editor={editor} editorApi={infoProvider?.getApi()} /> <Grid xs={4} className="info-panel">
</div>
<FormGroup> <Button disabled={levelId <= 1} component={RouterLink} to={`/world/${worldId}/level/${levelId - 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Previous Level</Button>
<FormControlLabel onChange={() => { setExpertInfoview(!expertInfoview) }} control={<Switch />} label="Expert mode" /> <Button disabled={false} component={RouterLink} to={`/world/${worldId}/level/${levelId + 1}`} sx={{ ml: 3, mt: 2, mb: 2 }} disableFocusRipple>Next Level</Button>
</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>
</Grid> </Box>
</> </>
} }

@ -4,7 +4,7 @@
min-height: 0; min-height: 0;
} }
.main-panel, .info-panel, .doc-panel { .main-panel, .info-panel {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
@ -35,10 +35,18 @@
margin-bottom: 0; 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 */ /* TODO: For development purposes only */
/***************************************/ /***************************************/
.doc-panel { /* .doc-panel {
border: 1px solid red; border: 1px solid red;
} }
@ -61,3 +69,8 @@
.codeview { .codeview {
border: 1px solid rgb(98, 148, 255); border: 1px solid rgb(98, 148, 255);
} }
.main-grid {
border: 1px solid rgb(255, 0, 0);
margin: 1px;
} */

@ -1,17 +1,17 @@
import GameServer.Commands import GameServer.Commands
-- import TestGame.MyNat -- import TestGame.MyNat
-- LemmaDoc zero_add as zero_add in "Addition" LemmaDoc zero_add as zero_add in "Addition"
-- "This lemma says `∀ a : , 0 + a = a`." "This lemma says `∀ a : , 0 + a = a`."
-- LemmaDoc add_zero as add_zero in "Addition" LemmaDoc add_zero as add_zero in "Addition"
-- "This lemma says `∀ a : , a + 0 = a`." "This lemma says `∀ a : , a + 0 = a`."
-- LemmaDoc add_succ as add_succ in "Addition" LemmaDoc add_succ as add_succ in "Addition"
-- "This lemma says `∀ a b : , a + succ b = succ (a + b)`." "This lemma says `∀ a b : , a + succ b = succ (a + b)`."
-- LemmaSet addition : "Addition lemmas" := LemmaSet addition : "Addition lemmas" :=
-- zero_add add_zero zero_add add_zero
LemmaDoc not_not as not_not in "Logic" LemmaDoc not_not as not_not in "Logic"
"`∀ (A : Prop), ¬¬A ↔ A`." "`∀ (A : Prop), ¬¬A ↔ A`."
@ -44,3 +44,6 @@ LemmaDoc even_square as even_square in "Nat"
LemmaSet natural : "Natürliche Zahlen" := LemmaSet natural : "Natürliche Zahlen" :=
even odd not_odd not_even even odd not_odd not_even
LemmaSet logic : "Logik" :=
not_not

@ -31,5 +31,3 @@ Hint : 42 = 42 =>
"Man schreibt eine Taktik pro Zeile, also gib 'rfl' ein gefolgt von ENTER." "Man schreibt eine Taktik pro Zeile, also gib 'rfl' ein gefolgt von ENTER."
Conclusion "Bravo!" Conclusion "Bravo!"
Tactics rfl

@ -26,3 +26,4 @@ deshalb musst du dieses häufig gar nicht mehr schreiben.
" "
Tactics rfl Tactics rfl
Lemmas zero_add

Loading…
Cancel
Save