Merge branch 'master' into add-new-blocks

This commit is contained in:
Mario 2020-07-26 12:15:57 +02:00
commit 44f2f3ca94
42 changed files with 462 additions and 123 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
package-lock.json

View File

@ -11,6 +11,7 @@
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"blockly": "^3.20200625.2",
"prismjs": "^1.20.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -2,13 +2,9 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<meta name="theme-color" content="#4EAF47" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
@ -24,7 +20,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>senseBox Blockly</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -3,9 +3,9 @@
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"src": "favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
"type": "image/png"
},
{
"src": "logo192.png",
@ -20,6 +20,6 @@
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
"theme_color": "#4EAF47",
"background_color": "#4EAF47"
}

BIN
public/media/1x1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

BIN
public/media/click.mp3 Normal file

Binary file not shown.

BIN
public/media/click.ogg Normal file

Binary file not shown.

BIN
public/media/click.wav Normal file

Binary file not shown.

BIN
public/media/delete.mp3 Normal file

Binary file not shown.

BIN
public/media/delete.ogg Normal file

Binary file not shown.

BIN
public/media/delete.wav Normal file

Binary file not shown.

BIN
public/media/disconnect.mp3 Normal file

Binary file not shown.

BIN
public/media/disconnect.ogg Normal file

Binary file not shown.

BIN
public/media/disconnect.wav Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="12.71" height="8.79" viewBox="0 0 12.71 8.79"><title>dropdown-arrow</title><g opacity="0.1"><path d="M12.71,2.44A2.41,2.41,0,0,1,12,4.16L8.08,8.08a2.45,2.45,0,0,1-3.45,0L0.72,4.16A2.42,2.42,0,0,1,0,2.44,2.48,2.48,0,0,1,.71.71C1,0.47,1.43,0,6.36,0S11.75,0.46,12,.71A2.44,2.44,0,0,1,12.71,2.44Z" fill="#231f20"/></g><path d="M6.36,7.79a1.43,1.43,0,0,1-1-.42L1.42,3.45a1.44,1.44,0,0,1,0-2c0.56-.56,9.31-0.56,9.87,0a1.44,1.44,0,0,1,0,2L7.37,7.37A1.43,1.43,0,0,1,6.36,7.79Z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 569 B

BIN
public/media/handclosed.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

BIN
public/media/handdelete.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
public/media/handopen.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

BIN
public/media/pilcrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

BIN
public/media/quote0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

BIN
public/media/quote1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

BIN
public/media/sprites.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

74
public/media/sprites.svg Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="96px" height="124px">
<style type="text/css">
#background {
fill: none;
}
.arrows {
fill: #000;
stroke: none;
}
.selected>.arrows {
fill: #fff;
}
.checkmark {
fill: #000;
font-family: sans-serif;
font-size: 10pt;
text-anchor: middle;
}
.trash {
fill: #888;
}
.zoom {
fill: none;
stroke: #888;
stroke-width: 2;
stroke-linecap: round;
}
.zoom>.center {
fill: #888;
stroke-width: 0;
}
</style>
<rect id="background" width="96" height="124" x="0" y="0" />
<g>
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<g class="selected" transform="translate(0, 16)">
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
</g>
<text class="checkmark" x="55.5" y="28">&#10003;</text>
<g class="trash">
<path d="M 2,41 v 6 h 42 v -6 h -10.5 l -3,-3 h -15 l -3,3 z" />
<rect width="36" height="20" x="5" y="50" />
<rect width="36" height="42" x="5" y="50" rx="4" ry="4" />
</g>
<g class="zoom">
<circle r="11.5" cx="16" cy="108" />
<circle r="4.3" cx="16" cy="108" class="center" />
<path d="m 28,108 h3" />
<path d="m 1,108 h3" />
<path d="m 16,120 v3" />
<path d="m 16,93 v3" />
</g>
<g class="zoom">
<circle r="15" cx="48" cy="108" />
<path d="m 48,101.6 v12.8" />
<path d="m 41.6,108 h12.8" />
</g>
<g class="zoom">
<circle r="15" cx="80" cy="108" />
<path d="m 73.6,108 h12.8" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -3,5 +3,5 @@
overflow: hidden;
display: block;
position: relative;
padding-bottom: 60px; /* height of your footer + 30px*/y
/* padding-bottom: 60px; /* height of your footer + 30px*/
}

View File

@ -5,6 +5,8 @@ import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store';
import './App.css';
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import Navbar from './components/Navbar';
@ -15,6 +17,9 @@ const theme = createMuiTheme({
palette: {
primary: {
main: '#4EAF47',
},
secondary: {
main: '#DDDDDD'
}
}
});
@ -27,9 +32,7 @@ function App() {
<Router>
<div className="wrapper">
<Navbar />
<div style={{ margin: '0 22px' }}>
<Routes />
</div>
<Routes />
<Footer />
</div>
</Router>

View File

@ -1,5 +1,7 @@
export const NEW_WORKSPACE = 'NEW_WORKSPACE';
export const NEW_CODE = 'NEW_CODE';
export const CHANGE_WORKSPACE = 'CHANGE_WORKSPACE';
export const CREATE_BLOCK = 'CREATE_BLOCK';
export const MOVE_BLOCK = 'MOVE_BLOCK';
export const CHANGE_BLOCK = 'CHANGE_BLOCK';
export const DELETE_BLOCK = 'DELETE_BLOCK';
export const CLEAR_STATS = 'CLEAR_STATS';

View File

@ -1,28 +1,50 @@
import { NEW_WORKSPACE, CREATE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from './types';
import { NEW_CODE, CHANGE_WORKSPACE, CREATE_BLOCK, MOVE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from './types';
import * as Blockly from 'blockly/core';
export const workspaceChange = () => (dispatch) => {
dispatch({
type: CHANGE_WORKSPACE
})
}
export const onChangeWorkspace = (event) => (dispatch, getState) => {
var oldWorkspace = getState().workspace.new; // stored 'new workspace' is from now on old
var newWorkspace = window.Ardublockly.workspace;
dispatch({
type: NEW_WORKSPACE,
payload: {new: newWorkspace, old: oldWorkspace}
type: CHANGE_WORKSPACE,
})
const workspace = Blockly.getMainWorkspace();
var code = getState().workspace.code;
code.arduino = Blockly.Arduino.workspaceToCode(workspace);
var xmlDom = Blockly.Xml.workspaceToDom(workspace);
code.xml = Blockly.Xml.domToPrettyText(xmlDom);
dispatch({
type: NEW_CODE,
payload: code
});
var stats = getState().workspace.stats;
if (event.type === window.Blockly.Events.CREATE){
if (event.type === Blockly.Events.BLOCK_CREATE){
stats.create += event.ids.length;
dispatch({
type: CREATE_BLOCK,
payload: stats
});
}
else if (event.type === window.Blockly.Events.CHANGE){
else if (event.type === Blockly.Events.BLOCK_MOVE){
stats.move += 1;
dispatch({
type: MOVE_BLOCK,
payload: stats
});
}
else if (event.type === Blockly.Events.BLOCK_CHANGE){
stats.change += 1;
dispatch({
type: CHANGE_BLOCK,
payload: stats
});
}
else if (event.type === window.Blockly.Events.DELETE){
else if (event.type === Blockly.Events.BLOCK_DELETE){
if(stats.create > 0){
stats.delete += event.ids.length;
dispatch({
@ -37,17 +59,11 @@ export const clearStats = () => (dispatch) => {
var stats = {
create: 0,
change: 0,
delete: 0
delete: 0,
move: 0
};
dispatch({
type: CLEAR_STATS,
payload: stats
});
};
export const setWorkspace = (workspace) => (dispatch, getState) => {
dispatch({
type: NEW_WORKSPACE,
payload: {new: workspace, old: getState().workspace.new}
});
};

View File

@ -1,6 +1,6 @@
#blocklyDiv {
height: 90vH;
width: 50%;
position: absolute;
bottom: 0;
height: 100%;
min-height: 500px;
width: 100%;
border: 1px solid #4EAF47;
}

View File

@ -0,0 +1,85 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { onChangeWorkspace } from '../../actions/workspaceActions';
import BlocklyComponent, { Block, Value, Field, Shadow, Category } from './';
import * as Blockly from 'blockly/core';
import * as De from './msg/de'; // de locale files
//import * as En from './Blockly/msg/en'; // de locale files
import './blocks/index';
import './generator/index';
class BlocklyWindow extends Component {
constructor(props) {
super(props);
this.simpleWorkspace = React.createRef();
}
componentDidMount() {
const workspace = Blockly.getMainWorkspace();
this.props.onChangeWorkspace({});
workspace.addChangeListener((event) => {
this.props.onChangeWorkspace(event);
});
}
render() {
return (
<BlocklyComponent ref={this.simpleWorkspace}
readOnly={false}
trashcan={true}
zoom={{ // https://developers.google.com/blockly/guides/configure/web/zoom
controls: true,
wheel: true,
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.2
}}
grid={{ // https://developers.google.com/blockly/guides/configure/web/grid
spacing: 20,
length: 1,
colour: '#4EAF47', // senseBox-green
snap: false
}}
media={'media/'}
move={{ // https://developers.google.com/blockly/guides/configure/web/move
scrollbars: true,
drag: true,
wheel: false
}}
initialXml={''}
>
<Category name="loops" >
<Block type="controls_for" />
<Block type="controls_repeat_ext" />
<Block type="controls_whileUntil" />
</Category>
<Category name="senseBox" colour="120" >
<Category name="Sensoren" colour="120" >
<Block type="sensebox_sensor_temp_hum"></Block>
</Category>
<Block type="sensebox_telegram" />
</Category>
<Category name="Logic" colour="#b063c5">
<Block type="control_if"></Block>
<Block type="controls_ifelse"></Block>
<Block type="logic_compare"></Block>
<Block type="logic_operation"></Block>
<Block type="logic_negate"></Block>
<Block type="logic_boolean"></Block>
</Category>
</BlocklyComponent>
);
};
}
BlocklyWindow.propTypes = {
onChangeWorkspace: PropTypes.func.isRequired
};
export default connect(null, { onChangeWorkspace })(BlocklyWindow);

View File

@ -1,7 +1,9 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { clearStats } from '../actions/workspaceActions';
import { clearStats, workspaceChange } from '../actions/workspaceActions';
import * as Blockly from 'blockly/core';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
@ -13,7 +15,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
class ClearWorkspace extends Component {
clearWorkspace = () => {
this.props.newWorkspace.clear();
const workspace = Blockly.getMainWorkspace();
workspace.clear();
workspace.options.maxBlocks = Infinity;
this.props.workspaceChange();
this.props.clearStats();
}
@ -28,12 +33,9 @@ class ClearWorkspace extends Component {
}
ClearWorkspace.propTypes = {
newWorkspace: PropTypes.object.isRequired,
clearStats: PropTypes.func.isRequired
clearStats: PropTypes.func.isRequired,
workspaceChange: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
newWorkspace: state.workspace.new
});
export default connect(mapStateToProps, { clearStats })(ClearWorkspace);
export default connect(null, { clearStats, workspaceChange })(ClearWorkspace);

View File

@ -0,0 +1,128 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Prism from "prismjs";
import "prismjs/themes/prism.css";
import "prismjs/plugins/line-numbers/prism-line-numbers";
import "prismjs/plugins/line-numbers/prism-line-numbers.css";
import { withStyles } from '@material-ui/core/styles';
import MuiAccordion from '@material-ui/core/Accordion';
import MuiAccordionSummary from '@material-ui/core/AccordionSummary';
import MuiAccordionDetails from '@material-ui/core/AccordionDetails';
const Accordion = withStyles((theme) => ({
root: {
border: `1px solid ${theme.palette.secondary.main}`,
boxShadow: 'none',
'&:before': {
display: 'none',
},
'&$expanded': {
margin: 'auto',
},
},
expanded: {},
}))(MuiAccordion);
const AccordionSummary = withStyles((theme) => ({
root: {
backgroundColor: theme.palette.secondary.main,
borderBottom: `1px solid white`,
marginBottom: -1,
minHeight: 50,
'&$expanded': {
minHeight: 50,
},
},
content: {
'&$expanded': {
margin: '12px 0',
},
},
expanded: {},
}))(MuiAccordionSummary);
const AccordionDetails = withStyles((theme) => ({
root: {
padding: theme.spacing(2),
},
}))(MuiAccordionDetails);
class CodeViewer extends Component {
state = {
expanded: true
}
componentDidMount() {
Prism.highlightAll();
}
componentDidUpdate() {
Prism.highlightAll();
}
onChange = () => {
this.setState({ expanded: !this.state.expanded });
}
render() {
var curlyBrackets = '{ }';
var unequal = '<>';
return (
<div style={{height: '500px'}}>
<Accordion
square={true}
style={{margin: 0}}
expanded={this.state.expanded}
onChange={this.onChange}
>
<AccordionSummary>
<b style={{fontSize: '20px', marginRight: '5px', width: '35px'}}>{curlyBrackets}</b>
<div style={{margin: 'auto 5px 2px 0px'}}>Arduino Quellcode</div>
</AccordionSummary>
<AccordionDetails style={{padding: 0, height: 'calc(500px - 50px - 50px)', backgroundColor: 'white'}}>
<pre className="line-numbers" style={{paddingBottom: 0, width: '100%', overflow: 'auto', scrollbarWidth: 'thin', height: 'calc(100% - 30px)', margin: '15px 0', paddingTop: 0, whiteSpace: 'pre-wrap', backgroundColor: 'white'}}>
<code className="language-clike">
{this.props.arduino}
</code>
</pre>
</AccordionDetails>
</Accordion>
<Accordion
square={true}
style={{margin: 0}}
expanded={!this.state.expanded}
onChange={this.onChange}
>
<AccordionSummary>
<b style={{fontSize: '20px', marginRight: '5px', width: '35px'}}>{unequal}</b>
<div style={{margin: 'auto 5px 2px 0px'}}>XML Blöcke</div>
</AccordionSummary>
<AccordionDetails style={{padding: 0, height: 'calc(500px - 50px - 50px)', backgroundColor: 'white'}}>
<pre className="line-numbers" style={{paddingBottom: 0, width: '100%', overflow: 'auto', scrollbarWidth: 'thin', height: 'calc(100% - 30px)', margin: '15px 0', paddingTop: 0, whiteSpace: 'pre-wrap', backgroundColor: 'white'}}>
<code className="language-xml">
{`${this.props.xml}`}
</code>
</pre>
</AccordionDetails>
</Accordion>
</div>
);
};
}
CodeViewer.propTypes = {
arduino: PropTypes.string.isRequired,
xml: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
arduino: state.workspace.code.arduino,
xml: state.workspace.code.xml
});
export default connect(mapStateToProps, null)(CodeViewer);

View File

@ -8,7 +8,7 @@ class Footer extends Component {
render() {
return (
<footer style={{position:'absolute', bottom: '0', width: '100%'}}>
<div style={{minHeight:'30px', backgroundColor:'lightgrey', textAlign: 'center', paddingTop: '2px'}}>
<div style={{minHeight:'30px', backgroundColor:'#DDDDDD', textAlign: 'center', paddingTop: '2px'}}>
<div style={{color: 'grey', height: '100%'}}>
<Link to={"/impressum"} style={{textDecoration: 'none', color: 'inherit'}}>Impressum</Link>
<Typography style={{margin: '0px 10px 0px 10px', display: 'initial', fontSize: '1rem'}}>|</Typography>

View File

@ -1,51 +1,55 @@
import React, { Component } from 'react';
import * as Blockly from 'blockly/core';
import WorkspaceStats from './WorkspaceStats';
import WorkspaceFunc from './WorkspaceFunc';
import BlocklyWindow from './Blockly/BlocklyWindow';
import CodeViewer from './CodeViewer';
import BlocklyComponent, { Block, Value, Field, Shadow, Category } from './Blockly';
import * as Blockly from 'blockly/core';
import * as De from './Blockly/msg/de'; // de locale files
//import * as En from './Blockly/msg/en'; // de locale files
import './Blockly/blocks/';
import './Blockly/generator/';
import Grid from '@material-ui/core/Grid';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
class Home extends Component {
class Home extends React.Component {
constructor(props) {
super(props);
this.simpleWorkspace = React.createRef();
this.state = { generatedCode: 'Click text' }
state = {
codeOn: false
}
componentDidMount() {
let workspace = Blockly.getMainWorkspace();
workspace.addChangeListener(this.generateCode);
componentDidUpdate() {
/* Resize and reposition all of the workspace chrome (toolbox, trash,
scrollbars etc.) This should be called when something changes that requires
recalculating dimensions and positions of the trash, zoom, toolbox, etc.
(e.g. window resize). */
const workspace = Blockly.getMainWorkspace();
Blockly.svgResize(workspace);
}
generateCode = () => {
var code = Blockly.Arduino.workspaceToCode(this.workspace);
console.log(code);
this.setState({ generatedCode: code })
onChange = () => {
this.setState({ codeOn: !this.state.codeOn });
}
render() {
return (
<div>
<WorkspaceStats />
<BlocklyComponent ref={this.simpleWorkspace}
readOnly={false} trashcan={true} media={'media/'}
move={{
scrollbars: true,
drag: true,
wheel: true
}}
initialXml={''} />
<WorkspaceFunc generateCode={this.generateCode} />
<Grid container spacing={2}>
<Grid item xs={12} md={this.state.codeOn ? 6 : 12} style={{ position: 'relative' }}>
<FormControlLabel
style={{ margin: '5px 10px 0 0', position: 'absolute', top: 0, right: 0, zIndex: 1 }}
control={<Switch checked={this.state.codeOn} onChange={this.onChange} color='primary' />}
label="Code"
/>
<BlocklyWindow />
</Grid>
{this.state.codeOn ?
<Grid item xs={12} md={6}>
<CodeViewer />
</Grid>
: null}
</Grid>
<WorkspaceFunc />
</div>
);
};

View File

@ -1,7 +1,9 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { setWorkspace } from '../actions/workspaceActions';
import { workspaceChange } from '../actions/workspaceActions';
import * as Blockly from 'blockly/core';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
@ -16,20 +18,23 @@ class MaxBlocks extends Component {
this.setState({ [e.target.name]: e.target.value});
}
setMaxBlocks = () => {
const workspace = Blockly.getMainWorkspace();
workspace.options.maxBlocks = this.state.max;
this.props.workspaceChange();
}
render() {
// var blockLeft = Object.keys(this.props.newWorkspace).length > 0 ? <p>{this.props.newWorkspace.remainingCapacity()} verbleibende Blöcke möglich</p> : null
// var error = this.state.error ? <div>{this.state.error}</div> : null;
return (
<div style={{display: 'inline', marginLeft: '10px'}}>
<TextField
style={{width: '50px'}}
style={{width: '50px', marginRight: '5px'}}
name="max"
type="number"
onChange={this.onChange}
value={this.state.max}
variant='filled'
/>
<Button style={{marginRight: '10px'}} variant="contained" color="primary" onClick={() => {this.props.newWorkspace.options.maxBlocks = this.state.max; this.props.setWorkspace(this.props.newWorkspace)}}>
<Button style={{marginRight: '10px', color: 'white'}} variant="contained" color="primary" onClick={this.setMaxBlocks}>
Maximale Blöcke
</Button>
</div>
@ -38,12 +43,7 @@ class MaxBlocks extends Component {
}
MaxBlocks.propTypes = {
newWorkspace: PropTypes.object.isRequired,
setWorkspace: PropTypes.func.isRequired
workspaceChange: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
newWorkspace: state.workspace.new
});
export default connect(mapStateToProps, { setWorkspace })(MaxBlocks);
export default connect(null, { workspaceChange })(MaxBlocks);

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import ClearWorkspace from './ClearWorkspace';
import senseboxLogo from './sensebox_logo.svg';
import { withStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
@ -51,7 +52,7 @@ class Navbar extends Component {
style={{height: '50px', marginBottom: '30px'}}
classes={{root: this.props.classes.appBarColor}}
>
<Toolbar style={{height: '50px', minHeight: '50px', padding: 0}}>
<Toolbar style={{height: '50px', minHeight: '50px', padding: 0, color: 'white'}}>
<IconButton
color="inherit"
onClick={this.toggleDrawer}
@ -64,6 +65,9 @@ class Navbar extends Component {
senseBox Blockly
</Typography>
</Link>
<Link to={"/"} style={{marginLeft: '10px'}}>
<img src={senseboxLogo} alt="senseBox-Logo" width="30"/>
</Link>
</Toolbar>
</AppBar>
<Drawer

View File

@ -9,10 +9,12 @@ class Routes extends Component {
render() {
return (
<Switch>
<Route path="/" exact component={Home} />
<Route component={NotFound} />
</Switch>
<div style={{ margin: '0 22px' }}>
<Switch>
<Route path="/" exact component={Home} />
<Route component={NotFound} />
</Switch>
</div>
);
}
}

View File

@ -18,11 +18,12 @@ class WorkspaceFunc extends Component {
open: false
}
getArduinoCode = () => {
this.setState({ title: 'Adurino Code', content: this.props.arduino, open: true });
}
getXMLCode = () => {
var code = window.Ardublockly.generateXml();
this.setState({ title: 'XML Code', content: code, open: true });
this.setState({ title: 'XML Code', content: this.props.xml, open: true });
}
toggleDialog = () => {
@ -43,15 +44,12 @@ class WorkspaceFunc extends Component {
</Button>
</DialogActions>
</Dialog>
<Button style={{ marginRight: '10px' }} variant="contained" color="primary" onClick={() => this.props.generateCode()}>
<Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getArduinoCode()}>
Get Adurino Code
</Button>
<Button style={{ marginRight: '10px' }} variant="contained" color="primary" onClick={() => this.getXMLCode()}>
<Button style={{ marginRight: '10px', color: 'white' }} variant="contained" color="primary" onClick={() => this.getXMLCode()}>
Get XML Code
</Button>
<Button variant="contained" color="primary" onClick={() => { var blocks = this.props.newWorkspace; console.log(blocks); }}>
Get workspace
</Button>
<MaxBlocks />
</div>
);
@ -59,11 +57,13 @@ class WorkspaceFunc extends Component {
}
WorkspaceFunc.propTypes = {
newWorkspace: PropTypes.object.isRequired
arduino: PropTypes.string.isRequired,
xml: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
newWorkspace: state.workspace.new
arduino: state.workspace.code.arduino,
xml: state.workspace.code.xml
});
export default connect(mapStateToProps, null)(WorkspaceFunc);

View File

@ -2,11 +2,13 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import * as Blockly from 'blockly/core';
import { withStyles } from '@material-ui/core/styles';
import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import { faPuzzlePiece, faTrash, faPlus, faPen } from "@fortawesome/free-solid-svg-icons";
import { faPuzzlePiece, faTrash, faPlus, faPen, faArrowsAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({
@ -22,13 +24,14 @@ const styles = (theme) => ({
class WorkspaceStats extends Component {
render() {
// var check = Object.keys(this.props.newWorkspace).length > 0 && this.props.create - this.props.delete !== this.props.newWorkspace.getAllBlocks().length ? <h1>FEHLER!</h1> : null;
const workspace = Blockly.getMainWorkspace();
const remainingBlocksInfinity = workspace ? workspace.remainingCapacity() !== Infinity : null;
return (
<div style={{marginBottom: '20px'}}>
<div style={{marginBottom: '20px', color: 'white'}}>
<Tooltip title="Anzahl aktueller Blöcke" style={{marginLeft: 0}} className={this.props.classes.stats}>
<div>
<FontAwesomeIcon icon={faPuzzlePiece} />
<Typography style={{display: 'inline'}}> {Object.keys(this.props.newWorkspace).length > 0 ? this.props.newWorkspace.getAllBlocks().length : 0}</Typography>
<Typography style={{display: 'inline'}}> {workspace ? workspace.getAllBlocks().length : 0}</Typography>
</div>
</Tooltip>
<Tooltip title="Anzahl neuer Blöcke" className={this.props.classes.stats}>
@ -45,6 +48,13 @@ class WorkspaceStats extends Component {
<Typography style={{display: 'inline'}}> {this.props.change}</Typography>
</div>
</Tooltip>
<Tooltip title="Anzahl bewegter Blöcke" className={this.props.classes.stats}>
<div>
<FontAwesomeIcon icon={faArrowsAlt} style={{marginRight: '5px'}}/>
<FontAwesomeIcon icon={faPuzzlePiece} />
<Typography style={{display: 'inline'}}> {this.props.move}</Typography>
</div>
</Tooltip>
<Tooltip title="Anzahl gelöschter Blöcke" className={this.props.classes.stats}>
<div>
<FontAwesomeIcon icon={faTrash} style={{marginRight: '5px'}}/>
@ -52,29 +62,29 @@ class WorkspaceStats extends Component {
<Typography style={{display: 'inline'}}> {this.props.delete}</Typography>
</div>
</Tooltip>
{Object.keys(this.props.newWorkspace).length > 0 ? this.props.newWorkspace.remainingCapacity() !== Infinity ?
{remainingBlocksInfinity ?
<Tooltip title="verbleibende Blöcke" className={this.props.classes.stats}>
<Typography style={{display: 'inline'}}>{this.props.delete} verbleibende Blöcke</Typography>
</Tooltip> : null : null}
<Typography style={{display: 'inline'}}>{workspace.remainingCapacity()} verbleibende Blöcke</Typography>
</Tooltip> : null}
</div>
);
};
}
WorkspaceStats.propTypes = {
newWorkspace: PropTypes.object.isRequired,
create: PropTypes.number.isRequired,
change: PropTypes.number.isRequired,
delete: PropTypes.number.isRequired,
test: PropTypes.number.isRequired
move: PropTypes.number.isRequired,
worskpaceChange: PropTypes.number.isRequired
};
const mapStateToProps = state => ({
newWorkspace: state.workspace.new,
create: state.workspace.stats.create,
change: state.workspace.stats.change,
delete: state.workspace.stats.delete,
test: state.workspace.test
move: state.workspace.stats.move,
worskpaceChange: state.workspace.change
});
export default connect(mapStateToProps, null)(withStyles(styles, {withTheme: true})(WorkspaceStats));

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,27 +1,34 @@
import { NEW_WORKSPACE, CREATE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from '../actions/types';
import { CHANGE_WORKSPACE, NEW_CODE, CREATE_BLOCK, MOVE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from '../actions/types';
const initialState = {
old: {},
new: {},
code: {
arduino: '',
xml: ''
},
stats: {
create: 0,
change: 0,
delete: 0,
move: 0
},
test: 0
change: 0
};
export default function(state = initialState, action){
switch(action.type){
case NEW_WORKSPACE:
case NEW_CODE:
return {
...state,
old: action.payload.old,
new: action.payload.new,
test: state.test += 1
code: action.payload
};
case CHANGE_WORKSPACE:
return {
...state,
change: state.change += 1
};
case CREATE_BLOCK:
case MOVE_BLOCK:
case CHANGE_BLOCK:
case DELETE_BLOCK:
case CLEAR_STATS: