adaptation of the functionalities to Blockly package

This commit is contained in:
Delucse 2020-07-23 15:42:01 +02:00
parent d1d7a447bc
commit c2405de04a
11 changed files with 254 additions and 73 deletions

View File

@ -1,5 +1,8 @@
export const NEW_CODE = 'NEW_CODE';
export const CHANGE_WORKSPACE = 'CHANGE_WORKSPACE';
export const NEW_WORKSPACE = 'NEW_WORKSPACE'; export const NEW_WORKSPACE = 'NEW_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,61 @@
import { NEW_WORKSPACE, CREATE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from './types'; import { NEW_CODE, CHANGE_WORKSPACE, NEW_WORKSPACE, CREATE_BLOCK, MOVE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from './types';
export const onChangeWorkspace = (event) => (dispatch, getState) => { import * as Blockly from 'blockly/core';
var oldWorkspace = getState().workspace.new; // stored 'new workspace' is from now on old
var newWorkspace = window.Ardublockly.workspace; export const workspaceChange = () => (dispatch) => {
dispatch({
type: CHANGE_WORKSPACE
})
}
export const initWorkspace = (workspace) => (dispatch) => {
dispatch({ dispatch({
type: NEW_WORKSPACE, type: NEW_WORKSPACE,
payload: {new: newWorkspace, old: oldWorkspace} payload: workspace
}); });
}
export const onChangeWorkspace = (event) => (dispatch, getState) => {
const workspace = Blockly.getMainWorkspace();
dispatch({
type: NEW_WORKSPACE,
payload: workspace
});
dispatch({
type: CHANGE_WORKSPACE,
})
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
});
console.log(event.type);
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,7 +70,8 @@ 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,
@ -45,6 +79,7 @@ export const clearStats = () => (dispatch) => {
}); });
}; };
export const setWorkspace = (workspace) => (dispatch, getState) => { export const setWorkspace = (workspace) => (dispatch, getState) => {
dispatch({ dispatch({
type: NEW_WORKSPACE, type: NEW_WORKSPACE,

View File

@ -1,6 +1,4 @@
#blocklyDiv { #blocklyDiv {
height: 90vH; height: 60vH;
width: 50%; width: 50%;
position: absolute;
bottom: 0;
} }

View File

@ -0,0 +1,68 @@
import React, { Component } from 'react';
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/index';
import './Blockly/generator/index';
class BlocklyView extends Component {
constructor(props) {
super(props);
this.simpleWorkspace = React.createRef();
this.state = {
generatedCode: 'Click text'
}
}
componentDidMount() {
let workspace = Blockly.getMainWorkspace();
workspace.addChangeListener(this.generateCode);
}
generateCode = () => {
var code = Blockly.Arduino.workspaceToCode(this.workspace);
console.log(code);
this.setState({ generatedCode: code })
}
render() {
return (
<BlocklyComponent ref={this.simpleWorkspace}
readOnly={false}
trashcan={true}
media={'media/'}
move={{
scrollbars: true,
drag: true,
wheel: true
}}
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>
);
};
}
export default BlocklyView;

View File

@ -1,7 +1,7 @@
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 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,9 +13,16 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
class ClearWorkspace extends Component { class ClearWorkspace extends Component {
clearWorkspace = () => { clearWorkspace = () => {
this.props.newWorkspace.clear(); if(this.props.workspace){
this.props.workspace.clear();
this.props.workspace.options.maxBlocks = Infinity;
this.props.workspaceChange();
this.props.clearStats(); this.props.clearStats();
} }
else {
alert()
}
}
render() { render() {
return ( return (
@ -28,12 +35,13 @@ class ClearWorkspace extends Component {
} }
ClearWorkspace.propTypes = { ClearWorkspace.propTypes = {
newWorkspace: PropTypes.object.isRequired, workspace: PropTypes.object.isRequired,
clearStats: PropTypes.func.isRequired clearStats: PropTypes.func.isRequired,
workspaceChange: PropTypes.func.isRequired
}; };
const mapStateToProps = state => ({ const mapStateToProps = state => ({
newWorkspace: state.workspace.new workspace: state.workspace.workspace
}); });
export default connect(mapStateToProps, { clearStats })(ClearWorkspace); export default connect(mapStateToProps, { clearStats, workspaceChange })(ClearWorkspace);

View File

@ -0,0 +1,28 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
class CodeViewer extends Component {
render() {
return (
<div>
{this.props.arduino}
<p>{this.props.xml}</p>
</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

@ -1,7 +1,12 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { onChangeWorkspace, initWorkspace } from '../actions/workspaceActions';
import WorkspaceStats from './WorkspaceStats'; import WorkspaceStats from './WorkspaceStats';
import WorkspaceFunc from './WorkspaceFunc'; import WorkspaceFunc from './WorkspaceFunc';
import CodeViewer from './CodeViewer';
import BlocklyComponent, { Block, Value, Field, Shadow, Category } from './Blockly'; import BlocklyComponent, { Block, Value, Field, Shadow, Category } from './Blockly';
import * as Blockly from 'blockly/core'; import * as Blockly from 'blockly/core';
@ -11,25 +16,19 @@ import './Blockly/blocks/index';
import './Blockly/generator/index'; import './Blockly/generator/index';
class Home extends React.Component { class Home extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.simpleWorkspace = React.createRef(); this.simpleWorkspace = React.createRef();
this.state = { generatedCode: 'Click text' }
} }
componentDidMount() { componentDidMount() {
let workspace = Blockly.getMainWorkspace(); let workspace = Blockly.getMainWorkspace();
workspace.addChangeListener(this.generateCode); this.props.initWorkspace(workspace);
} workspace.addChangeListener((event) => {
this.props.onChangeWorkspace(event);
generateCode = () => { });
var code = Blockly.Arduino.workspaceToCode(this.workspace);
console.log(code);
this.setState({ generatedCode: code })
} }
render() { render() {
@ -37,13 +36,16 @@ class Home extends React.Component {
<div> <div>
<WorkspaceStats /> <WorkspaceStats />
<BlocklyComponent ref={this.simpleWorkspace} <BlocklyComponent ref={this.simpleWorkspace}
readOnly={false} trashcan={true} media={'media/'} readOnly={false}
trashcan={true}
media={'media/'}
move={{ move={{
scrollbars: true, scrollbars: true,
drag: true, drag: true,
wheel: true wheel: true
}} }}
initialXml={''}> initialXml={''}
>
<Category name="loops" > <Category name="loops" >
<Block type="controls_for" /> <Block type="controls_for" />
<Block type="controls_repeat_ext" /> <Block type="controls_repeat_ext" />
@ -64,10 +66,28 @@ class Home extends React.Component {
<Block type="logic_boolean"></Block> <Block type="logic_boolean"></Block>
</Category> </Category>
</BlocklyComponent> </BlocklyComponent>
<WorkspaceFunc generateCode={this.generateCode} /> <WorkspaceFunc />
</div> </div>
); );
}; };
} }
export default Home; WorkspaceStats.propTypes = {
workspace: PropTypes.object.isRequired,
create: PropTypes.number.isRequired,
change: PropTypes.number.isRequired,
delete: PropTypes.number.isRequired,
move: PropTypes.number.isRequired,
worskpaceChange: PropTypes.number.isRequired
};
const mapStateToProps = state => ({
workspace: state.workspace.workspace,
create: state.workspace.stats.create,
change: state.workspace.stats.change,
delete: state.workspace.stats.delete,
move: state.workspace.stats.move,
worskpaceChange: state.workspace.change
});
export default connect(null, { onChangeWorkspace, initWorkspace })(Home);

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,9 +18,13 @@ 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
@ -27,9 +33,9 @@ class MaxBlocks extends Component {
type="number" type="number"
onChange={this.onChange} onChange={this.onChange}
value={this.state.max} value={this.state.max}
variant='filled' variant='outlined'
/> />
<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'}} variant="contained" color="primary" onClick={this.setMaxBlocks}>
Maximale Blöcke Maximale Blöcke
</Button> </Button>
</div> </div>
@ -37,13 +43,4 @@ class MaxBlocks extends Component {
}; };
} }
MaxBlocks.propTypes = { export default connect(null, { workspaceChange })(MaxBlocks);
newWorkspace: PropTypes.object.isRequired,
setWorkspace: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
newWorkspace: state.workspace.new
});
export default connect(mapStateToProps, { setWorkspace })(MaxBlocks);

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' }} 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' }} 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,13 @@ 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 remainingBlocksInfinity = this.props.workspace ? this.props.workspace.remainingCapacity() !== Infinity : null;
return ( return (
<div style={{marginBottom: '20px'}}> <div style={{marginBottom: '20px'}}>
<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'}}> {this.props.workspace ? this.props.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 +47,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 +61,31 @@ 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'}}>{this.props.workspace.remainingCapacity()} verbleibende Blöcke</Typography>
</Tooltip> : null : null} </Tooltip> : null}
</div> </div>
); );
}; };
} }
WorkspaceStats.propTypes = { WorkspaceStats.propTypes = {
newWorkspace: PropTypes.object.isRequired, workspace: 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, workspace: state.workspace.workspace,
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));

View File

@ -1,27 +1,40 @@
import { NEW_WORKSPACE, CREATE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from '../actions/types'; import { CHANGE_WORKSPACE, NEW_CODE, NEW_WORKSPACE, CREATE_BLOCK, MOVE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from '../actions/types';
const initialState = { const initialState = {
old: {}, code: {
new: {}, arduino: '',
xml: ''
},
workspace: null,
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_CODE:
return {
...state,
code: action.payload
};
case CHANGE_WORKSPACE:
return {
...state,
change: state.change += 1
};
case NEW_WORKSPACE: case NEW_WORKSPACE:
return { return {
...state, ...state,
old: action.payload.old, workspace: action.payload
new: action.payload.new,
test: state.test += 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: