vertical tutorial stepper

This commit is contained in:
Delucse 2020-09-03 15:22:14 +02:00
parent ccf901d20b
commit 7148d66d19
4 changed files with 374 additions and 87 deletions

View File

@ -0,0 +1,70 @@
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import tutorials from './tutorials.json';
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
const styles = (theme) => ({
stepper: {
backgroundColor: fade(theme.palette.primary.main, 0.6),
width: 'calc(100% - 40px)',
borderRadius: '25px',
padding: '0 20px',
margin: '20px 0',
display: 'flex',
justifyContent: 'space-between'
},
color: {
backgroundColor: 'transparent '
}
});
class StepperHorizontal extends Component {
state={
tutorialId: Number(this.props.match.params.tutorialId),
}
componentDidUpdate(props, state){
if(state.tutorialId !== Number(this.props.match.params.tutorialId)){
this.setState({tutorialId: Number(this.props.match.params.tutorialId)})
}
}
render() {
var tutorialId = this.state.tutorialId;
return (
<div className={this.props.classes.stepper}>
<Button
disabled={tutorialId-1 === 0}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId-1}`)}}
>
{'<'}
</Button>
<Stepper activeStep={tutorialId} orientation="horizontal"
style={{padding: 0}} classes={{root: this.props.classes.color}}>
<Step expanded completed={false}>
<StepLabel icon={``}>
<h1 style={{margin: 0}}>{tutorials[tutorialId-1].title}</h1>
</StepLabel>
</Step>
</Stepper>
<Button
disabled={tutorialId+1 > tutorials.length}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId+1}`)}}
>
{'>'}
</Button>
</div>
);
};
}
export default withRouter(withStyles(styles, {withTheme: true})(StepperHorizontal));

View File

@ -0,0 +1,185 @@
import React, { Component } from 'react';
import { withRouter, Link } from 'react-router-dom';
import clsx from 'clsx';
import tutorials from './tutorials.json';
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import Tooltip from '@material-ui/core/Tooltip';
import LinearProgress from '@material-ui/core/LinearProgress';
const styles = (theme) => ({
verticalStepper: {
padding: 0,
width: '30px',
},
stepIconSmall: {
border: `2px solid ${theme.palette.primary.main}`,
borderRadius: '50%',
width: '12px',
height: '12px',
margin: '0 auto'
},
stepIconMedium: {
border: `2px solid ${theme.palette.primary.main}`,
borderRadius: '50%',
width: '18px',
height: '18px',
margin: '0 auto'
},
stepIconLarge: {
border: `2px solid ${theme.palette.primary.main}`,
borderRadius: '50%',
width: '24px',
height: '24px'
},
stepIconTransparent: {
border: `2px solid transparent`,
cursor: 'default'
},
stepIconActive: {
backgroundColor: fade(theme.palette.primary.main, 0.6)
},
progress: {
position: 'absolute',
top: 0,
right: 0,
marginRight: '5px',
width: '3px',
},
progressForeground: {
backgroundColor: theme.palette.primary.main
},
progressBackground: {
backgroundColor: fade(theme.palette.primary.main, 0.2),
height: '100%'
}
});
class StepperVertical extends Component {
state={
tutorialArray: Number(this.props.match.params.tutorialId) === 1 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-1, Number(this.props.match.params.tutorialId)+4)
: Number(this.props.match.params.tutorialId) === 2 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-1-1, Number(this.props.match.params.tutorialId)+3)
: Number(this.props.match.params.tutorialId) === tutorials.length ?
tutorials.slice(Number(this.props.match.params.tutorialId)-4-1, Number(this.props.match.params.tutorialId)+4)
: Number(this.props.match.params.tutorialId) === tutorials.length-1 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-3-1, Number(this.props.match.params.tutorialId)+3)
: tutorials.slice(Number(this.props.match.params.tutorialId)-2-1,Number(this.props.match.params.tutorialId)+2),
tutorialId: Number(this.props.match.params.tutorialId),
verticalTutorialId: Number(this.props.match.params.tutorialId)
}
componentDidUpdate(props, state){
if(state.tutorialId !== Number(this.props.match.params.tutorialId)){
this.setState({
tutorialArray: Number(this.props.match.params.tutorialId) === 1 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-1, Number(this.props.match.params.tutorialId)+4)
: Number(this.props.match.params.tutorialId) === 2 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-1-1, Number(this.props.match.params.tutorialId)+3)
: Number(this.props.match.params.tutorialId) === tutorials.length ?
tutorials.slice(Number(this.props.match.params.tutorialId)-4-1, Number(this.props.match.params.tutorialId)+4)
: Number(this.props.match.params.tutorialId) === tutorials.length-1 ?
tutorials.slice(Number(this.props.match.params.tutorialId)-3-1, Number(this.props.match.params.tutorialId)+3)
: tutorials.slice(Number(this.props.match.params.tutorialId)-2-1,Number(this.props.match.params.tutorialId)+2),
tutorialId: Number(this.props.match.params.tutorialId),
verticalTutorialId: Number(this.props.match.params.tutorialId)
})
}
}
verticalStepper = (step) => {
var newTutorialId = this.state.verticalTutorialId + step;
var tutorialArray = Number(newTutorialId) === 1 ?
tutorials.slice(newTutorialId-1, newTutorialId+4)
: newTutorialId === 2 ?
tutorials.slice(newTutorialId-1-1, newTutorialId+3)
: newTutorialId === tutorials.length ?
tutorials.slice(newTutorialId-4-1, newTutorialId+4)
: newTutorialId === tutorials.length-1 ?
tutorials.slice(newTutorialId-3-1, newTutorialId+3)
: tutorials.slice(newTutorialId-2-1, newTutorialId+2);
this.setState({ tutorialArray: tutorialArray, verticalTutorialId: newTutorialId });
}
render() {
var tutorialId = this.state.tutorialId;
var verticalTutorialId = this.state.verticalTutorialId;
return (
<div style={{marginRight: '10px'}}>
<Button
style={{minWidth: '30px', margin: 'auto', minHeight: '25px', padding: '0', writingMode: 'vertical-rl'}}
disabled={this.state.verticalTutorialId === 1}
onClick={() => {this.verticalStepper(-1)}}
>
{'<'}
</Button>
<div style={{display: 'flex', height: 'calc(100% - 25px - 25px)', width: 'max-content'}}>
<div style={{position: 'relative'}}>
<div
className={clsx(this.props.classes.progress, this.props.classes.progressForeground)}
style={{ zIndex: 1, borderRadius: `${verticalTutorialId/tutorials.length === 1 ? '2px' : '2px 2px 0 0'}`, height: `${(verticalTutorialId/tutorials.length)*100}%`}}>
</div>
<div
className={clsx(this.props.classes.progress, this.props.classes.progressBackground)}
style={{borderRadius: `${verticalTutorialId/tutorials.length === 1 ? '2px' : '2px 2px 0 0'}`}}>
</div>
</div>
<Stepper
activeStep={tutorialId}
orientation="vertical"
connector={<div style={{height: '10px'}}></div>}
classes={{root: this.props.classes.verticalStepper}}
>
{this.state.tutorialArray.map((tutorial, i) => {
var index = this.state.tutorialArray.indexOf(tutorials[verticalTutorialId-1]);
return (
<Step key={i}>
<Tooltip title={Object.keys(tutorial).length > 0 ? tutorial.title : ''} placement='right' arrow >
<Link to={`/tutorial/${i === index ? verticalTutorialId : verticalTutorialId - index + i}`}>
<StepLabel
StepIconComponent={'div'}
classes={{
root: tutorial === tutorials[verticalTutorialId-1] ?
tutorial === tutorials[tutorialId-1] ?
clsx(this.props.classes.stepIconLarge, this.props.classes.stepIconActive)
: this.props.classes.stepIconLarge
: tutorial === tutorials[verticalTutorialId-2] || tutorial === tutorials[verticalTutorialId] ?
tutorial === tutorials[tutorialId-1] ?
clsx(this.props.classes.stepIconMedium, this.props.classes.stepIconActive)
: this.props.classes.stepIconMedium
: tutorial === tutorials[tutorialId-1] ?
clsx(this.props.classes.stepIconSmall, this.props.classes.stepIconActive)
: this.props.classes.stepIconSmall
}}
>
</StepLabel>
</Link>
</Tooltip>
</Step>
)})}
</Stepper>
</div>
<Button
style={{minWidth: '30px', minHeight: '25px', padding: '0', writingMode: 'vertical-rl'}}
disabled={this.state.verticalTutorialId === tutorials.length}
onClick={() => {this.verticalStepper(1)}}
>
{'>'}
</Button>
</div>
);
};
}
export default withRouter(withStyles(styles, {withTheme: true})(StepperVertical));

View File

@ -1,119 +1,88 @@
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import Breadcrumbs from '../Breadcrumbs';
import StepperHorizontal from './StepperHorizontal';
import StepperVertical from './StepperVertical';
import BlocklyWindow from '../Blockly/BlocklyWindow';
import CodeViewer from '../CodeViewer';
import NotFound from '../NotFound';
import tutorials from './tutorials.json';
import { fade } from '@material-ui/core/styles/colorManipulator';
import { withStyles } from '@material-ui/core/styles';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import Button from '@material-ui/core/Button';
import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
const styles = (theme) => ({
stepper: {
backgroundColor: fade(theme.palette.primary.main, 0.6),
width: 'calc(100% - 40px)',
// opacity: 0.6,
borderRadius: '25px',
padding: '0 20px',
margin: '20px 0',
display: 'flex',
justifyContent: 'space-between'
},
color: {
backgroundColor: 'transparent '
}
});
class Tutorial extends Component {
state={
value: 'introduction'
value: 'introduction',
tutorialId: Number(this.props.match.params.tutorialId)
}
componentDidUpdate(props, state){
if(state.tutorialId !== Number(this.props.match.params.tutorialId)){
this.setState({tutorialId: Number(this.props.match.params.tutorialId)})
}
}
onChange = (e, value) => {
console.log(value);
this.setState({ value: value });
}
render() {
var tutorialId = Number(this.props.match.params.tutorialId);
var tutorialId = this.state.tutorialId;
return (
!Number.isInteger(tutorialId) || tutorialId < 1 || tutorialId > tutorials.length ?
<NotFound button={{title: 'Zurück zur Tutorials-Übersicht', link: '/tutorial'}}/>
:
<div>
<Breadcrumbs content={[{link: '/', title: 'Home'},{link: '/tutorial', title: 'Tutorial'}, {link: `/tutorial/${tutorialId}`, title: tutorials[tutorialId-1].title}]}/>
<NotFound button={{title: 'Zurück zur Tutorials-Übersicht', link: '/tutorial'}}/>
:
<div>
<Breadcrumbs content={[{link: '/', title: 'Home'},{link: '/tutorial', title: 'Tutorial'}, {link: `/tutorial/${tutorialId}`, title: tutorials[tutorialId-1].title}]}/>
{/* Stepper */}
<div className={this.props.classes.stepper}>
<Button
disabled={tutorialId-1 === 0}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId-1}`)}}
>
{'<'}
</Button>
<Stepper activeStep={tutorialId} orientation="horizontal"
style={{padding: 0}} classes={{root: this.props.classes.color}}>
<Step expanded completed={false}>
<StepLabel icon={``}>
<h1 style={{margin: 0}}>{tutorials[tutorialId-1].title}</h1>
</StepLabel>
</Step>
</Stepper>
<Button
disabled={tutorialId+1 > tutorials.length}
onClick={() => {this.props.history.push(`/tutorial/${tutorialId+1}`)}}
>
{'>'}
</Button>
<StepperHorizontal />
<div style={{display: 'flex'}}>
<StepperVertical />
{/* width of vertical stepper is 30px*/}
<Card style={{width: 'calc(100% - 30px)', padding: '10px'}}>
<Tabs
value={this.state.value}
indicatorColor="primary"
textColor="inherit"
variant='fullWidth'
onChange={this.onChange}
>
<Tab label="Anleitung" value='introduction' disableRipple/>
<Tab label="Aufgabe" value='assessment' disableRipple/>
</Tabs>
<div style={{marginTop: '20px'}}>
{this.state.value === 'introduction' ?
'Hier könnte eine Anleitung stehen.': null }
{this.state.value === 'assessment' ?
<Grid container spacing={2}>
<Grid item xs={12} md={6} lg={8}>
<BlocklyWindow />
</Grid>
<Grid item xs={12} md={6} lg={4}>
<Card style={{height: 'calc(50% - 30px)', padding: '10px', marginBottom: '10px'}}>
Hier könnte die Problemstellung stehen.
</Card>
<div style={{height: '50%'}}>
<CodeViewer />
</div>
</Grid>
</Grid>
: null }
</div>
</Card>
</div>
</div>
<Tabs
value={this.state.value}
indicatorColor="primary"
textColor="inherit"
variant='fullWidth'
onChange={this.onChange}
>
<Tab label="Anleitung" value='introduction' disableRipple/>
<Tab label="Aufgabe" value='assessment' disableRipple/>
</Tabs>
<div style={{marginTop: '20px'}}>
{this.state.value === 'introduction' ?
'Hier könnte eine Anleitung stehen.': null }
{this.state.value === 'assessment' ?
<Grid container spacing={2}>
<Grid item xs={12} md={6} lg={8}>
<BlocklyWindow />
</Grid>
<Grid item xs={12} md={6} lg={4}>
<Card style={{height: 'calc(50% - 30px)', padding: '10px', marginBottom: '10px'}}>
Hier könnte die Problemstellung stehen.
</Card>
<div style={{height: '50%'}}>
<CodeViewer />
</div>
</Grid>
</Grid>
: null }
</div>
</div>
);
};
}
export default withRouter(withStyles(styles, {withTheme: true})(Tutorial));
export default Tutorial;

View File

@ -1,4 +1,67 @@
[
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},
{
"title": "if-Bedingung"
},
{
"title": "for-Schleife"
},
{
"title": "erste Schritte"
},