Merge branch 'blockly-functions' into instruction
This commit is contained in:
		
						commit
						7f3ad89623
					
				| @ -13,6 +13,7 @@ | |||||||
|     "@testing-library/react": "^9.5.0", |     "@testing-library/react": "^9.5.0", | ||||||
|     "@testing-library/user-event": "^7.2.1", |     "@testing-library/user-event": "^7.2.1", | ||||||
|     "blockly": "^3.20200625.2", |     "blockly": "^3.20200625.2", | ||||||
|  |     "file-saver": "^2.0.2", | ||||||
|     "prismjs": "^1.20.0", |     "prismjs": "^1.20.0", | ||||||
|     "react": "^16.13.1", |     "react": "^16.13.1", | ||||||
|     "react-dom": "^16.13.1", |     "react-dom": "^16.13.1", | ||||||
|  | |||||||
| @ -1,46 +0,0 @@ | |||||||
| import React, {Component} from 'react'; |  | ||||||
| import PropTypes from 'prop-types'; |  | ||||||
| import { connect } from 'react-redux'; |  | ||||||
| import { clearStats, onChangeCode } from '../actions/workspaceActions'; |  | ||||||
| import { initialXml } from './Blockly/initialXml.js'; |  | ||||||
| 
 |  | ||||||
| import * as Blockly from 'blockly/core'; |  | ||||||
| 
 |  | ||||||
| import ListItem from '@material-ui/core/ListItem'; |  | ||||||
| import ListItemIcon from '@material-ui/core/ListItemIcon'; |  | ||||||
| import ListItemText from '@material-ui/core/ListItemText'; |  | ||||||
| 
 |  | ||||||
| import { faTrashRestore } from "@fortawesome/free-solid-svg-icons"; |  | ||||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |  | ||||||
| 
 |  | ||||||
| class ClearWorkspace extends Component { |  | ||||||
| 
 |  | ||||||
|   clearWorkspace = () => { |  | ||||||
|     const workspace = Blockly.getMainWorkspace(); |  | ||||||
|     Blockly.Events.disable(); // https://groups.google.com/forum/#!topic/blockly/m7e3g0TC75Y
 |  | ||||||
|     // if events are disabled, then the workspace will be cleared AND the blocks are not in the trashcan
 |  | ||||||
|     const xmlDom = Blockly.Xml.textToDom(initialXml) |  | ||||||
|     Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace); |  | ||||||
|     Blockly.Events.enable(); |  | ||||||
|     workspace.options.maxBlocks = Infinity; |  | ||||||
|     this.props.onChangeCode(); |  | ||||||
|     this.props.clearStats(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   render() { |  | ||||||
|     return ( |  | ||||||
|       <ListItem button onClick={() => {this.clearWorkspace(); this.props.onClick();}}> |  | ||||||
|         <ListItemIcon><FontAwesomeIcon icon={faTrashRestore} /></ListItemIcon> |  | ||||||
|         <ListItemText primary='Zurücksetzen' /> |  | ||||||
|       </ListItem> |  | ||||||
|     ); |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ClearWorkspace.propTypes = { |  | ||||||
|   clearStats: PropTypes.func.isRequired, |  | ||||||
|   onChangeCode: PropTypes.func.isRequired |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| export default connect(null, { clearStats, onChangeCode })(ClearWorkspace); |  | ||||||
| @ -10,11 +10,26 @@ import DialogTitle from '@material-ui/core/DialogTitle'; | |||||||
| import DialogContent from '@material-ui/core/DialogContent'; | import DialogContent from '@material-ui/core/DialogContent'; | ||||||
| import DialogActions from '@material-ui/core/DialogActions'; | import DialogActions from '@material-ui/core/DialogActions'; | ||||||
| import Dialog from '@material-ui/core/Dialog'; | import Dialog from '@material-ui/core/Dialog'; | ||||||
|  | import IconButton from '@material-ui/core/IconButton'; | ||||||
|  | import Tooltip from '@material-ui/core/Tooltip'; | ||||||
|  | 
 | ||||||
|  | import { faPlay } from "@fortawesome/free-solid-svg-icons"; | ||||||
|  | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||||
| 
 | 
 | ||||||
| const styles = (theme) => ({ | const styles = (theme) => ({ | ||||||
|   backdrop: { |   backdrop: { | ||||||
|     zIndex: theme.zIndex.drawer + 1, |     zIndex: theme.zIndex.drawer + 1, | ||||||
|     color: '#fff', |     color: '#fff', | ||||||
|  |   }, | ||||||
|  |   button: { | ||||||
|  |     backgroundColor: theme.palette.primary.main, | ||||||
|  |     color: theme.palette.primary.contrastText, | ||||||
|  |     width: '40px', | ||||||
|  |     height: '40px', | ||||||
|  |     '&:hover': { | ||||||
|  |       backgroundColor: theme.palette.primary.main, | ||||||
|  |       color: theme.palette.primary.contrastText, | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| @ -60,10 +75,21 @@ class Compile extends Component { | |||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     return ( |     return ( | ||||||
|       <div style={{display: 'inline'}}> |       <div style={{}}> | ||||||
|         <Button style={{ float: 'right', color: 'white' }} variant="contained" color="primary" onClick={() => this.compile()}> |         {this.props.iconButton ? | ||||||
|           Kompilieren |           <Tooltip title='Blöcke kompilieren' arrow style={{marginRight: '5px'}}> | ||||||
|         </Button> |             <IconButton | ||||||
|  |               className={this.props.classes.button} | ||||||
|  |               onClick={() => this.compile()} | ||||||
|  |             > | ||||||
|  |               <FontAwesomeIcon icon={faPlay} size="xs"/> | ||||||
|  |             </IconButton> | ||||||
|  |           </Tooltip> | ||||||
|  |          : | ||||||
|  |           <Button style={{ float: 'right', color: 'white' }} variant="contained" color="primary" onClick={() => this.compile()}> | ||||||
|  |             Kompilieren | ||||||
|  |           </Button> | ||||||
|  |         } | ||||||
|         <Backdrop className={this.props.classes.backdrop} open={this.state.progress}> |         <Backdrop className={this.props.classes.backdrop} open={this.state.progress}> | ||||||
|           <CircularProgress color="inherit" /> |           <CircularProgress color="inherit" /> | ||||||
|         </Backdrop> |         </Backdrop> | ||||||
|  | |||||||
| @ -72,7 +72,8 @@ class Home extends Component { | |||||||
|   render() { |   render() { | ||||||
|     return ( |     return ( | ||||||
|       <div> |       <div> | ||||||
|         <WorkspaceStats /> |         <div style={{float: 'right', height: '40px', marginBottom: '20px'}}><WorkspaceFunc /></div> | ||||||
|  |         <div style={{float: 'left', height: '40px', position: 'relative'}}><WorkspaceStats /></div> | ||||||
|         <Grid container spacing={2}> |         <Grid container spacing={2}> | ||||||
|           <Grid item xs={12} md={this.state.codeOn ? 6 : 12} style={{ position: 'relative' }}> |           <Grid item xs={12} md={this.state.codeOn ? 6 : 12} style={{ position: 'relative' }}> | ||||||
|             <Tooltip title={this.state.codeOn ? 'Code ausblenden' : 'Code anzeigen'} > |             <Tooltip title={this.state.codeOn ? 'Code ausblenden' : 'Code anzeigen'} > | ||||||
| @ -93,7 +94,6 @@ class Home extends Component { | |||||||
|             </Grid> |             </Grid> | ||||||
|             : null} |             : null} | ||||||
|         </Grid> |         </Grid> | ||||||
|         <WorkspaceFunc /> |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| import React, { Component } from 'react'; | import React, { Component } from 'react'; | ||||||
| import { Link } from 'react-router-dom'; | import { Link } from 'react-router-dom'; | ||||||
| 
 | 
 | ||||||
| import ClearWorkspace from './ClearWorkspace'; |  | ||||||
| import senseboxLogo from './sensebox_logo.svg'; | import senseboxLogo from './sensebox_logo.svg'; | ||||||
| 
 | 
 | ||||||
| import { withRouter } from 'react-router-dom'; | import { withRouter } from 'react-router-dom'; | ||||||
| @ -105,7 +104,6 @@ class Navbar extends Component { | |||||||
|                 </ListItem> |                 </ListItem> | ||||||
|               </Link> |               </Link> | ||||||
|             ))} |             ))} | ||||||
|             <ClearWorkspace onClick={this.toggleDrawer}/> |  | ||||||
|           </List> |           </List> | ||||||
|           <Divider classes={{root: this.props.classes.appBarColor}} style={{marginTop: 'auto'}}/> |           <Divider classes={{root: this.props.classes.appBarColor}} style={{marginTop: 'auto'}}/> | ||||||
|           <List> |           <List> | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import { connect } from 'react-redux'; | |||||||
| import BlocklyWindow from '../Blockly/BlocklyWindow'; | import BlocklyWindow from '../Blockly/BlocklyWindow'; | ||||||
| import SolutionCheck from './SolutionCheck'; | import SolutionCheck from './SolutionCheck'; | ||||||
| import CodeViewer from '../CodeViewer'; | import CodeViewer from '../CodeViewer'; | ||||||
|  | import WorkspaceFunc from '../WorkspaceFunc'; | ||||||
| 
 | 
 | ||||||
| import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; | import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; | ||||||
| import Grid from '@material-ui/core/Grid'; | import Grid from '@material-ui/core/Grid'; | ||||||
| @ -22,10 +23,10 @@ class Assessment extends Component { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div style={{width: '100%'}}> |       <div style={{width: '100%'}}> | ||||||
|         <Typography variant='h4' style={{marginBottom: '5px'}}>{currentTask.headline}</Typography> |         <Typography variant='h4' style={{float: 'left', marginBottom: '5px', height: '40px', display: 'table'}}>{currentTask.headline}</Typography> | ||||||
|  |         <div style={{float: 'right', height: '40px'}}><WorkspaceFunc solutionCheck/></div> | ||||||
|         <Grid container spacing={2} style={{marginBottom: '5px'}}> |         <Grid container spacing={2} style={{marginBottom: '5px'}}> | ||||||
|           <Grid item xs={12} md={6} lg={8} style={{ position: 'relative' }}> |           <Grid item xs={12} md={6} lg={8}> | ||||||
|             <SolutionCheck /> |  | ||||||
|             <BlocklyWindow initialXml={statusTask ? statusTask.xml ? statusTask.xml : null : null}/> |             <BlocklyWindow initialXml={statusTask ? statusTask.xml ? statusTask.xml : null : null}/> | ||||||
|           </Grid> |           </Grid> | ||||||
|           <Grid item xs={12} md={6} lg={4} style={isWidthDown('sm', this.props.width) ? {height: 'max-content'} : {}}> |           <Grid item xs={12} md={6} lg={4} style={isWidthDown('sm', this.props.width) ? {height: 'max-content'} : {}}> | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ class SolutionCheck extends Component { | |||||||
|         <Tooltip title='Lösung kontrollieren'> |         <Tooltip title='Lösung kontrollieren'> | ||||||
|           <IconButton |           <IconButton | ||||||
|             className={this.props.classes.compile} |             className={this.props.classes.compile} | ||||||
|             style={{width: '40px', height: '40px', position: 'absolute', top: 8, right: 8, zIndex: 21 }} |             style={{width: '40px', height: '40px', marginRight: '5px'}} | ||||||
|             onClick={() => this.check()} |             onClick={() => this.check()} | ||||||
|           > |           > | ||||||
|             <FontAwesomeIcon icon={faPlay} size="xs"/> |             <FontAwesomeIcon icon={faPlay} size="xs"/> | ||||||
|  | |||||||
| @ -1,39 +1,144 @@ | |||||||
| import React, { Component } from 'react'; | import React, { Component } from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
|  | import { clearStats, onChangeCode } from '../actions/workspaceActions'; | ||||||
|  | 
 | ||||||
|  | import * as Blockly from 'blockly/core'; | ||||||
|  | 
 | ||||||
|  | import { saveAs } from 'file-saver'; | ||||||
|  | 
 | ||||||
|  | import { initialXml } from './Blockly/initialXml.js'; | ||||||
| 
 | 
 | ||||||
| import MaxBlocks from './MaxBlocks'; |  | ||||||
| import Compile from './Compile'; | import Compile from './Compile'; | ||||||
|  | import SolutionCheck from './Tutorial/SolutionCheck'; | ||||||
| 
 | 
 | ||||||
|  | import { withStyles } from '@material-ui/core/styles'; | ||||||
| import Button from '@material-ui/core/Button'; | import Button from '@material-ui/core/Button'; | ||||||
| import DialogTitle from '@material-ui/core/DialogTitle'; | import DialogTitle from '@material-ui/core/DialogTitle'; | ||||||
| import DialogContent from '@material-ui/core/DialogContent'; | import DialogContent from '@material-ui/core/DialogContent'; | ||||||
| import DialogActions from '@material-ui/core/DialogActions'; | import DialogActions from '@material-ui/core/DialogActions'; | ||||||
| import Dialog from '@material-ui/core/Dialog'; | import Dialog from '@material-ui/core/Dialog'; | ||||||
|  | import IconButton from '@material-ui/core/IconButton'; | ||||||
|  | import Tooltip from '@material-ui/core/Tooltip'; | ||||||
|  | 
 | ||||||
|  | import { faSave, faUpload, faShare } from "@fortawesome/free-solid-svg-icons"; | ||||||
|  | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||||
|  | 
 | ||||||
|  | const styles = (theme) => ({ | ||||||
|  |   button: { | ||||||
|  |     backgroundColor: theme.palette.primary.main, | ||||||
|  |     color: theme.palette.primary.contrastText, | ||||||
|  |     width: '40px', | ||||||
|  |     height: '40px', | ||||||
|  |     '&:hover': { | ||||||
|  |       backgroundColor: theme.palette.primary.main, | ||||||
|  |       color: theme.palette.primary.contrastText, | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class WorkspaceFunc extends Component { | class WorkspaceFunc extends Component { | ||||||
| 
 | 
 | ||||||
|   state = { |   constructor(props){ | ||||||
|     title: '', |     super(props); | ||||||
|     content: '', |     this.inputRef = React.createRef(); | ||||||
|     open: false |     this.state = { | ||||||
|   } |       title: '', | ||||||
| 
 |       content: '', | ||||||
|   getArduinoCode = () => { |       open: false | ||||||
|     this.setState({ title: 'Adurino Code', content: this.props.arduino, open: true }); |     }; | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getXMLCode = () => { |  | ||||||
|     this.setState({ title: 'XML Code', content: this.props.xml, open: true }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toggleDialog = () => { |   toggleDialog = () => { | ||||||
|     this.setState({ open: !this.state }); |     this.setState({ open: !this.state }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   saveXmlFile = (code) => { | ||||||
|  |     // saveTextFileAs
 | ||||||
|  |     var fileName = 'todo.xml' | ||||||
|  |     var blob = new Blob([code], { type: 'text/xml' }); | ||||||
|  |     saveAs(blob, fileName); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uploadXmlFile = (xmlFile) => { | ||||||
|  |     console.log(xmlFile); | ||||||
|  |     if(xmlFile.type !== 'text/xml'){ | ||||||
|  |       this.setState({ open: true, title: 'Unzulässiger Dateityp', content: 'Die übergebene Datei entsprach nicht dem geforderten Format. Es sind nur XML-Dateien zulässig.' }); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       var reader = new FileReader(); | ||||||
|  |       reader.readAsText(xmlFile); | ||||||
|  |       reader.onloadend = () => { | ||||||
|  |         var xmlDom = null; | ||||||
|  |         try { | ||||||
|  |           xmlDom = Blockly.Xml.textToDom(reader.result); | ||||||
|  |           const workspace = Blockly.getMainWorkspace(); | ||||||
|  |           var xmlBefore = this.props.xml; | ||||||
|  |           workspace.clear(); | ||||||
|  |           this.props.clearStats(); | ||||||
|  |           Blockly.Xml.domToWorkspace(xmlDom, workspace); | ||||||
|  |           if(workspace.getAllBlocks().length < 1){ | ||||||
|  |             Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xmlBefore), workspace) | ||||||
|  |             this.setState({ open: true, title: 'Keine Blöcke', content: 'Es wurden keine Blöcke detektiert. Bitte überprüfe den XML-Code und versuche es erneut.' }); | ||||||
|  |           } | ||||||
|  |         } catch(err){ | ||||||
|  |           this.setState({ open: true, title: 'Ungültige XML', content: 'Die XML-Datei konnte nicht in Blöcke zerlegt werden. Bitte überprüfe den XML-Code und versuche es erneut.' }); | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   resetWorkspace = () => { | ||||||
|  |     const workspace = Blockly.getMainWorkspace(); | ||||||
|  |     Blockly.Events.disable(); // https://groups.google.com/forum/#!topic/blockly/m7e3g0TC75Y
 | ||||||
|  |     // if events are disabled, then the workspace will be cleared AND the blocks are not in the trashcan
 | ||||||
|  |     const xmlDom = Blockly.Xml.textToDom(initialXml) | ||||||
|  |     Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace); | ||||||
|  |     Blockly.Events.enable(); | ||||||
|  |     workspace.options.maxBlocks = Infinity; | ||||||
|  |     this.props.onChangeCode(); | ||||||
|  |     this.props.clearStats(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   render() { |   render() { | ||||||
|     return ( |     return ( | ||||||
|       <div style={{ marginTop: '20px' }}> |       <div style={{width: 'max-content', display: 'flex'}}> | ||||||
|  |         {this.props.solutionCheck ? <SolutionCheck /> : <Compile iconButton />} | ||||||
|  |         <Tooltip title='Blöcke speichern' arrow style={{marginRight: '5px'}}> | ||||||
|  |           <IconButton | ||||||
|  |             className={this.props.classes.button} | ||||||
|  |             onClick={() => this.saveXmlFile(this.props.xml)} | ||||||
|  |           > | ||||||
|  |             <FontAwesomeIcon icon={faSave} size="xs"/> | ||||||
|  |           </IconButton> | ||||||
|  |         </Tooltip> | ||||||
|  |         <div ref={this.inputRef} style={{width: 'max-content', height: '40px', marginRight: '5px'}}> | ||||||
|  |           <input | ||||||
|  |             style={{display: 'none'}} | ||||||
|  |             accept="text/xml" | ||||||
|  |             onChange={(e) => {this.uploadXmlFile(e.target.files[0])}} | ||||||
|  |             id="open-blocks" | ||||||
|  |             type="file" | ||||||
|  |           /> | ||||||
|  |           <label htmlFor="open-blocks"> | ||||||
|  |             <Tooltip title='Blöcke öffnen' arrow style={{marginRight: '5px'}}> | ||||||
|  |               <div className={this.props.classes.button} style={{borderRadius: '50%', cursor: 'pointer', display: 'table-cell', | ||||||
|  |               verticalAlign: 'middle', | ||||||
|  |               textAlign: 'center'}}> | ||||||
|  |                 <FontAwesomeIcon icon={faUpload} style={{width: '18px', height: '18px'}}/> | ||||||
|  |               </div> | ||||||
|  |             </Tooltip> | ||||||
|  |           </label> | ||||||
|  |         </div> | ||||||
|  |         <Tooltip title='Workspace zurücksetzen' arrow> | ||||||
|  |           <IconButton | ||||||
|  |             className={this.props.classes.button} | ||||||
|  |             onClick={() => this.resetWorkspace()} | ||||||
|  |           > | ||||||
|  |             <FontAwesomeIcon icon={faShare} size="xs" flip='horizontal'/> | ||||||
|  |           </IconButton> | ||||||
|  |         </Tooltip> | ||||||
|         <Dialog onClose={this.toggleDialog} open={this.state.open}> |         <Dialog onClose={this.toggleDialog} open={this.state.open}> | ||||||
|           <DialogTitle>{this.state.title}</DialogTitle> |           <DialogTitle>{this.state.title}</DialogTitle> | ||||||
|           <DialogContent dividers> |           <DialogContent dividers> | ||||||
| @ -45,14 +150,6 @@ class WorkspaceFunc extends Component { | |||||||
|             </Button> |             </Button> | ||||||
|           </DialogActions> |           </DialogActions> | ||||||
|         </Dialog> |         </Dialog> | ||||||
|         <Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getArduinoCode()}> |  | ||||||
|           Get Adurino Code |  | ||||||
|         </Button> |  | ||||||
|         <Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getXMLCode()}> |  | ||||||
|           Get XML Code |  | ||||||
|         </Button> |  | ||||||
|         <MaxBlocks /> |  | ||||||
|         <Compile /> |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
| @ -60,7 +157,9 @@ class WorkspaceFunc extends Component { | |||||||
| 
 | 
 | ||||||
| WorkspaceFunc.propTypes = { | WorkspaceFunc.propTypes = { | ||||||
|   arduino: PropTypes.string.isRequired, |   arduino: PropTypes.string.isRequired, | ||||||
|   xml: PropTypes.string.isRequired |   xml: PropTypes.string.isRequired, | ||||||
|  |   clearStats: PropTypes.func.isRequired, | ||||||
|  |   onChangeCode: PropTypes.func.isRequired | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
| @ -68,4 +167,4 @@ const mapStateToProps = state => ({ | |||||||
|   xml: state.workspace.code.xml |   xml: state.workspace.code.xml | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default connect(mapStateToProps, null)(WorkspaceFunc); | export default connect(mapStateToProps, { clearStats, onChangeCode })(withStyles(styles, {withTheme: true})(WorkspaceFunc)); | ||||||
|  | |||||||
| @ -4,12 +4,15 @@ import { connect } from 'react-redux'; | |||||||
| 
 | 
 | ||||||
| import * as Blockly from 'blockly/core'; | import * as Blockly from 'blockly/core'; | ||||||
| 
 | 
 | ||||||
|  | import withWidth, { isWidthDown } from '@material-ui/core/withWidth'; | ||||||
| import { withStyles } from '@material-ui/core/styles'; | import { withStyles } from '@material-ui/core/styles'; | ||||||
| import Tooltip from '@material-ui/core/Tooltip'; | import Tooltip from '@material-ui/core/Tooltip'; | ||||||
|  | import IconButton from '@material-ui/core/IconButton'; | ||||||
| import Chip from '@material-ui/core/Chip'; | import Chip from '@material-ui/core/Chip'; | ||||||
| import Avatar from '@material-ui/core/Avatar'; | import Avatar from '@material-ui/core/Avatar'; | ||||||
|  | import Popover from '@material-ui/core/Popover'; | ||||||
| 
 | 
 | ||||||
| import { faPuzzlePiece, faTrash, faPlus, faPen, faArrowsAlt } from "@fortawesome/free-solid-svg-icons"; | import { faPuzzlePiece, faTrash, faPlus, faPen, faArrowsAlt, faEllipsisH } from "@fortawesome/free-solid-svg-icons"; | ||||||
| import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; | ||||||
| 
 | 
 | ||||||
| const styles = (theme) => ({ | const styles = (theme) => ({ | ||||||
| @ -19,65 +22,123 @@ const styles = (theme) => ({ | |||||||
|     marginLeft: '50px', |     marginLeft: '50px', | ||||||
|     padding: '3px 10px', |     padding: '3px 10px', | ||||||
|     // borderRadius: '25%'
 |     // borderRadius: '25%'
 | ||||||
|  |   }, | ||||||
|  |   menu: { | ||||||
|  |     backgroundColor: theme.palette.secondary.main, | ||||||
|  |     color: theme.palette.secondary.contrastText, | ||||||
|  |     width: '40px', | ||||||
|  |     height: '40px', | ||||||
|  |     '&:hover': { | ||||||
|  |       backgroundColor: theme.palette.secondary.main, | ||||||
|  |       color: theme.palette.primary.main, | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| class WorkspaceStats extends Component { | class WorkspaceStats extends Component { | ||||||
| 
 | 
 | ||||||
|  |   state={ | ||||||
|  |     anchor: null | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClose = () => { | ||||||
|  |     this.setState({ anchor: null }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClick = (event) => { | ||||||
|  |     this.setState({ anchor: event.currentTarget }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|   render() { |   render() { | ||||||
|  |     const bigDisplay = !isWidthDown('xs', this.props.width); | ||||||
|     const workspace = Blockly.getMainWorkspace(); |     const workspace = Blockly.getMainWorkspace(); | ||||||
|     const remainingBlocksInfinity = workspace ? workspace.remainingCapacity() !== Infinity : null; |     const remainingBlocksInfinity = workspace ? workspace.remainingCapacity() !== Infinity : null; | ||||||
|  |     const stats =  <div style={bigDisplay ? {display: 'flex'} : {display: 'inline'}}> | ||||||
|  |                     <Tooltip title="Anzahl aktueller Blöcke" arrow> | ||||||
|  |                       <Chip | ||||||
|  |                         style={bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'}} | ||||||
|  |                         color="primary" | ||||||
|  |                         avatar={<Avatar><FontAwesomeIcon icon={faPuzzlePiece} /></Avatar>} | ||||||
|  |                         label={workspace ? workspace.getAllBlocks().length : 0}> | ||||||
|  |                       </Chip> | ||||||
|  |                     </Tooltip> | ||||||
|  |                     <Tooltip title="Anzahl neuer Blöcke" arrow> | ||||||
|  |                       <Chip | ||||||
|  |                         style={bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'}} | ||||||
|  |                         color="primary" | ||||||
|  |                         avatar={<Avatar><FontAwesomeIcon icon={faPlus} /></Avatar>} | ||||||
|  |                         label={this.props.create > 0 ? this.props.create : 0}> {/* initialXML is created automatically, Block is not part of the statistics */} | ||||||
|  |                       </Chip> | ||||||
|  |                     </Tooltip> | ||||||
|  |                     <Tooltip title="Anzahl veränderter Blöcke" arrow> | ||||||
|  |                       <Chip | ||||||
|  |                         style={bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'}} | ||||||
|  |                         color="primary" | ||||||
|  |                         avatar={<Avatar><FontAwesomeIcon icon={faPen} /></Avatar>} | ||||||
|  |                         label={this.props.change}> | ||||||
|  |                       </Chip> | ||||||
|  |                     </Tooltip> | ||||||
|  |                     <Tooltip title="Anzahl bewegter Blöcke" arrow> | ||||||
|  |                       <Chip | ||||||
|  |                         style={bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'}} | ||||||
|  |                         color="primary" | ||||||
|  |                         avatar={<Avatar><FontAwesomeIcon icon={faArrowsAlt} /></Avatar>} | ||||||
|  |                         label={this.props.move > 0 ? this.props.move : 0}> {/* initialXML is moved automatically, Block is not part of the statistics */} | ||||||
|  |                       </Chip> | ||||||
|  |                     </Tooltip> | ||||||
|  |                     <Tooltip title="Anzahl gelöschter Blöcke" arrow> | ||||||
|  |                       <Chip | ||||||
|  |                         style={remainingBlocksInfinity ? bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'} : {}} | ||||||
|  |                         color="primary" | ||||||
|  |                         avatar={<Avatar><FontAwesomeIcon icon={faTrash} /></Avatar>} | ||||||
|  |                         label={this.props.delete}> | ||||||
|  |                       </Chip> | ||||||
|  |                     </Tooltip> | ||||||
|  |                     {remainingBlocksInfinity ? | ||||||
|  |                       <Tooltip title="Verbleibende Blöcke" arrow> | ||||||
|  |                         <Chip | ||||||
|  |                           style={bigDisplay ? {marginRight: '1rem'} : {marginRight: '1rem', marginBottom: '5px'}} | ||||||
|  |                           color="primary" | ||||||
|  |                           label={workspace.remainingCapacity()}> | ||||||
|  |                         </Chip> | ||||||
|  |                       </Tooltip> : null} | ||||||
|  |                     </div> | ||||||
|     return ( |     return ( | ||||||
|       <div style={{ marginBottom: '20px' }}> |       bigDisplay ? | ||||||
|         <Tooltip title="Anzahl aktueller Blöcke" > |         <div style={{bottom: 0, position: 'absolute'}}> | ||||||
|           <Chip |           {stats} | ||||||
|             style={{ marginRight: '1rem' }} |         </div> | ||||||
|             color="primary" |       : | ||||||
|             avatar={<Avatar><FontAwesomeIcon icon={faPuzzlePiece} /></Avatar>} |         <div> | ||||||
|             label={workspace ? workspace.getAllBlocks().length : 0}> |           <Tooltip title='Statistiken anzeigen' arrow> | ||||||
|           </Chip> |             <IconButton | ||||||
|         </Tooltip> |               className={this.props.classes.menu} | ||||||
|         <Tooltip title="Anzahl neuer Blöcke" > |               onClick={(event) => this.handleClick(event)} | ||||||
|           <Chip |             > | ||||||
|             style={{ marginRight: '1rem' }} |               <FontAwesomeIcon icon={faEllipsisH} size="xs"/> | ||||||
|             color="primary" |             </IconButton> | ||||||
|             avatar={<Avatar><FontAwesomeIcon icon={faPlus} /></Avatar>} |           </Tooltip> | ||||||
|             label={this.props.create > 0 ? this.props.create : 0}> {/* initialXML is created automatically, Block is not part of the statistics */} |           <Popover | ||||||
|           </Chip> |             open={Boolean(this.state.anchor)} | ||||||
|         </Tooltip> |             anchorEl={this.state.anchor} | ||||||
|         <Tooltip title="Anzahl veränderter Blöcke" > |             onClose={this.handleClose} | ||||||
|           <Chip |             anchorOrigin={{ | ||||||
|             style={{ marginRight: '1rem' }} |               vertical: 'bottom', | ||||||
|             color="primary" |               horizontal: 'center', | ||||||
|             avatar={<Avatar><FontAwesomeIcon icon={faPen} /></Avatar>} |             }} | ||||||
|             label={this.props.change}> |             transformOrigin={{ | ||||||
|           </Chip> |               vertical: 'top', | ||||||
|         </Tooltip> |               horizontal: 'center', | ||||||
|         <Tooltip title="Anzahl bewegter Blöcke" > |             }} | ||||||
|           <Chip |             PaperProps={{ | ||||||
|             style={{ marginRight: '1rem' }} |               style: {margin: '5px'} | ||||||
|             color="primary" |             }} | ||||||
|             avatar={<Avatar><FontAwesomeIcon icon={faArrowsAlt} /></Avatar>} |           > | ||||||
|             label={this.props.move > 0 ? this.props.move : 0}> {/* initialXML is moved automatically, Block is not part of the statistics */} |             <div style={{margin: '10px'}}> | ||||||
|           </Chip> |               {stats} | ||||||
|         </Tooltip> |             </div> | ||||||
|         <Tooltip title="Anzahl gelöschter Blöcke" > |           </Popover> | ||||||
|           <Chip |         </div> | ||||||
|             style={{ marginRight: '1rem' }} |  | ||||||
|             color="primary" |  | ||||||
|             avatar={<Avatar><FontAwesomeIcon icon={faTrash} /></Avatar>} |  | ||||||
|             label={this.props.delete}> |  | ||||||
|           </Chip> |  | ||||||
|         </Tooltip> |  | ||||||
|         {remainingBlocksInfinity ? |  | ||||||
|           <Tooltip title="Verbleibende Blöcke" > |  | ||||||
|             <Chip |  | ||||||
|               style={{ marginRight: '1rem' }} |  | ||||||
|               color="primary" |  | ||||||
|               label={workspace.remainingCapacity()}> |  | ||||||
|             </Chip> |  | ||||||
|           </Tooltip> : null} |  | ||||||
|       </div> |  | ||||||
|     ); |     ); | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
| @ -98,4 +159,4 @@ const mapStateToProps = state => ({ | |||||||
|   workspaceChange: state.workspace.change |   workspaceChange: state.workspace.change | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(WorkspaceStats)); | export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceStats))); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user