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-debug.log*
yarn-error.log* yarn-error.log*
package-lock.json package-lock.json
package-lock.json

View File

@ -11,6 +11,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",
"prismjs": "^1.20.0",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-redux": "^7.2.0", "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"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <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="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#4EAF47" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- <!--
manifest.json provides metadata used when your web app is installed on a 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. 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`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>senseBox Blockly</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -3,9 +3,9 @@
"name": "Create React App Sample", "name": "Create React App Sample",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.png",
"sizes": "64x64 32x32 24x24 16x16", "sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon" "type": "image/png"
}, },
{ {
"src": "logo192.png", "src": "logo192.png",
@ -20,6 +20,6 @@
], ],
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"theme_color": "#000000", "theme_color": "#4EAF47",
"background_color": "#ffffff" "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; overflow: hidden;
display: block; display: block;
position: relative; 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 { Provider } from 'react-redux';
import store from './store'; import store from './store';
import './App.css';
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles'; import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import Navbar from './components/Navbar'; import Navbar from './components/Navbar';
@ -15,6 +17,9 @@ const theme = createMuiTheme({
palette: { palette: {
primary: { primary: {
main: '#4EAF47', main: '#4EAF47',
},
secondary: {
main: '#DDDDDD'
} }
} }
}); });
@ -27,9 +32,7 @@ function App() {
<Router> <Router>
<div className="wrapper"> <div className="wrapper">
<Navbar /> <Navbar />
<div style={{ margin: '0 22px' }}> <Routes />
<Routes />
</div>
<Footer /> <Footer />
</div> </div>
</Router> </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 CREATE_BLOCK = 'CREATE_BLOCK';
export const MOVE_BLOCK = 'MOVE_BLOCK';
export const CHANGE_BLOCK = 'CHANGE_BLOCK'; export const CHANGE_BLOCK = 'CHANGE_BLOCK';
export const DELETE_BLOCK = 'DELETE_BLOCK'; export const DELETE_BLOCK = 'DELETE_BLOCK';
export const CLEAR_STATS = 'CLEAR_STATS'; 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) => { 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({ dispatch({
type: NEW_WORKSPACE, type: CHANGE_WORKSPACE,
payload: {new: newWorkspace, old: oldWorkspace} })
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; var stats = getState().workspace.stats;
if (event.type === window.Blockly.Events.CREATE){ if (event.type === Blockly.Events.BLOCK_CREATE){
stats.create += event.ids.length; stats.create += event.ids.length;
dispatch({ dispatch({
type: CREATE_BLOCK, type: CREATE_BLOCK,
payload: stats 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; stats.change += 1;
dispatch({ dispatch({
type: CHANGE_BLOCK, type: CHANGE_BLOCK,
payload: stats payload: stats
}); });
} }
else if (event.type === window.Blockly.Events.DELETE){ else if (event.type === Blockly.Events.BLOCK_DELETE){
if(stats.create > 0){ if(stats.create > 0){
stats.delete += event.ids.length; stats.delete += event.ids.length;
dispatch({ dispatch({
@ -37,17 +59,11 @@ export const clearStats = () => (dispatch) => {
var stats = { var stats = {
create: 0, create: 0,
change: 0, change: 0,
delete: 0 delete: 0,
move: 0
}; };
dispatch({ dispatch({
type: CLEAR_STATS, type: CLEAR_STATS,
payload: 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 { #blocklyDiv {
height: 90vH; height: 100%;
width: 50%; min-height: 500px;
position: absolute; width: 100%;
bottom: 0; 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 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 } from '../actions/workspaceActions'; import { clearStats, workspaceChange } from '../actions/workspaceActions';
import * as Blockly from 'blockly/core';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemIcon from '@material-ui/core/ListItemIcon';
@ -13,7 +15,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
class ClearWorkspace extends Component { class ClearWorkspace extends Component {
clearWorkspace = () => { clearWorkspace = () => {
this.props.newWorkspace.clear(); const workspace = Blockly.getMainWorkspace();
workspace.clear();
workspace.options.maxBlocks = Infinity;
this.props.workspaceChange();
this.props.clearStats(); this.props.clearStats();
} }
@ -28,12 +33,9 @@ class ClearWorkspace extends Component {
} }
ClearWorkspace.propTypes = { 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() { render() {
return ( return (
<footer style={{position:'absolute', bottom: '0', width: '100%'}}> <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%'}}> <div style={{color: 'grey', height: '100%'}}>
<Link to={"/impressum"} style={{textDecoration: 'none', color: 'inherit'}}>Impressum</Link> <Link to={"/impressum"} style={{textDecoration: 'none', color: 'inherit'}}>Impressum</Link>
<Typography style={{margin: '0px 10px 0px 10px', display: 'initial', fontSize: '1rem'}}>|</Typography> <Typography style={{margin: '0px 10px 0px 10px', display: 'initial', fontSize: '1rem'}}>|</Typography>

View File

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

View File

@ -1,7 +1,9 @@
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 { setWorkspace } from '../actions/workspaceActions'; import { workspaceChange } from '../actions/workspaceActions';
import * as Blockly from 'blockly/core';
import TextField from '@material-ui/core/TextField'; import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
@ -16,20 +18,23 @@ class MaxBlocks extends Component {
this.setState({ [e.target.name]: e.target.value}); this.setState({ [e.target.name]: e.target.value});
} }
setMaxBlocks = () => {
const workspace = Blockly.getMainWorkspace();
workspace.options.maxBlocks = this.state.max;
this.props.workspaceChange();
}
render() { 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 ( return (
<div style={{display: 'inline', marginLeft: '10px'}}> <div style={{display: 'inline', marginLeft: '10px'}}>
<TextField <TextField
style={{width: '50px'}} style={{width: '50px', marginRight: '5px'}}
name="max" name="max"
type="number" type="number"
onChange={this.onChange} onChange={this.onChange}
value={this.state.max} 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 Maximale Blöcke
</Button> </Button>
</div> </div>
@ -38,12 +43,7 @@ class MaxBlocks extends Component {
} }
MaxBlocks.propTypes = { MaxBlocks.propTypes = {
newWorkspace: PropTypes.object.isRequired, workspaceChange: PropTypes.func.isRequired
setWorkspace: PropTypes.func.isRequired
}; };
const mapStateToProps = state => ({ export default connect(null, { workspaceChange })(MaxBlocks);
newWorkspace: state.workspace.new
});
export default connect(mapStateToProps, { setWorkspace })(MaxBlocks);

View File

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

View File

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

View File

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

View File

@ -2,11 +2,13 @@ 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 * as Blockly from 'blockly/core';
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 Typography from '@material-ui/core/Typography'; 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"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const styles = (theme) => ({ const styles = (theme) => ({
@ -22,13 +24,14 @@ const styles = (theme) => ({
class WorkspaceStats extends Component { class WorkspaceStats extends Component {
render() { 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 ( 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}> <Tooltip title="Anzahl aktueller Blöcke" style={{marginLeft: 0}} className={this.props.classes.stats}>
<div> <div>
<FontAwesomeIcon icon={faPuzzlePiece} /> <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> </div>
</Tooltip> </Tooltip>
<Tooltip title="Anzahl neuer Blöcke" className={this.props.classes.stats}> <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> <Typography style={{display: 'inline'}}> {this.props.change}</Typography>
</div> </div>
</Tooltip> </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}> <Tooltip title="Anzahl gelöschter Blöcke" className={this.props.classes.stats}>
<div> <div>
<FontAwesomeIcon icon={faTrash} style={{marginRight: '5px'}}/> <FontAwesomeIcon icon={faTrash} style={{marginRight: '5px'}}/>
@ -52,29 +62,29 @@ class WorkspaceStats extends Component {
<Typography style={{display: 'inline'}}> {this.props.delete}</Typography> <Typography style={{display: 'inline'}}> {this.props.delete}</Typography>
</div> </div>
</Tooltip> </Tooltip>
{Object.keys(this.props.newWorkspace).length > 0 ? this.props.newWorkspace.remainingCapacity() !== Infinity ? {remainingBlocksInfinity ?
<Tooltip title="verbleibende Blöcke" className={this.props.classes.stats}> <Tooltip title="verbleibende Blöcke" className={this.props.classes.stats}>
<Typography style={{display: 'inline'}}>{this.props.delete} verbleibende Blöcke</Typography> <Typography style={{display: 'inline'}}>{workspace.remainingCapacity()} verbleibende Blöcke</Typography>
</Tooltip> : null : null} </Tooltip> : null}
</div> </div>
); );
}; };
} }
WorkspaceStats.propTypes = { WorkspaceStats.propTypes = {
newWorkspace: PropTypes.object.isRequired,
create: PropTypes.number.isRequired, create: PropTypes.number.isRequired,
change: PropTypes.number.isRequired, change: PropTypes.number.isRequired,
delete: PropTypes.number.isRequired, delete: PropTypes.number.isRequired,
test: PropTypes.number.isRequired move: PropTypes.number.isRequired,
worskpaceChange: PropTypes.number.isRequired
}; };
const mapStateToProps = state => ({ const mapStateToProps = state => ({
newWorkspace: state.workspace.new,
create: state.workspace.stats.create, create: state.workspace.stats.create,
change: state.workspace.stats.change, change: state.workspace.stats.change,
delete: state.workspace.stats.delete, 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)); 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 = { const initialState = {
old: {}, code: {
new: {}, arduino: '',
xml: ''
},
stats: { stats: {
create: 0, create: 0,
change: 0, change: 0,
delete: 0, delete: 0,
move: 0
}, },
test: 0 change: 0
}; };
export default function(state = initialState, action){ export default function(state = initialState, action){
switch(action.type){ switch(action.type){
case NEW_WORKSPACE: case NEW_CODE:
return { return {
...state, ...state,
old: action.payload.old, code: action.payload
new: action.payload.new, };
test: state.test += 1 case CHANGE_WORKSPACE:
return {
...state,
change: state.change += 1
}; };
case CREATE_BLOCK: case CREATE_BLOCK:
case MOVE_BLOCK:
case CHANGE_BLOCK: case CHANGE_BLOCK:
case DELETE_BLOCK: case DELETE_BLOCK:
case CLEAR_STATS: case CLEAR_STATS: