Merge branch 'development' into markdown_file_upload
This commit is contained in:
commit
b0ab03d95b
2
.env
2
.env
@ -2,5 +2,7 @@ REACT_APP_COMPILER_URL=https://test.compiler.sensebox.de
|
|||||||
REACT_APP_BOARD=sensebox-mcu
|
REACT_APP_BOARD=sensebox-mcu
|
||||||
REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de
|
REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de
|
||||||
|
|
||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
|
||||||
# in days
|
# in days
|
||||||
REACT_APP_SHARE_LINK_EXPIRES=30
|
REACT_APP_SHARE_LINK_EXPIRES=30
|
||||||
|
44157
package-lock.json
generated
44157
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
48
package.json
48
package.json
@ -1,44 +1,51 @@
|
|||||||
{
|
{
|
||||||
"name": "blockly-react",
|
"name": "blockly-react",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blockly/block-plus-minus": "^2.0.10",
|
"@blockly/block-plus-minus": "^3.0.5",
|
||||||
"@blockly/field-grid-dropdown": "^1.0.25",
|
"@blockly/field-grid-dropdown": "^1.0.31",
|
||||||
"@blockly/field-slider": "^2.1.1",
|
"@blockly/field-slider": "^3.0.5",
|
||||||
"@blockly/plugin-scroll-options": "^1.0.2",
|
"@blockly/plugin-scroll-options": "^2.0.5",
|
||||||
"@blockly/plugin-typed-variable-modal": "^3.1.26",
|
"@blockly/plugin-typed-variable-modal": "^4.0.5",
|
||||||
"@blockly/zoom-to-fit": "^2.0.7",
|
"@blockly/workspace-backpack": "^1.0.14",
|
||||||
|
"@blockly/zoom-to-fit": "^2.0.14",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.11",
|
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
|
"@monaco-editor/react": "^4.3.1",
|
||||||
"@sentry/react": "^6.0.0",
|
"@sentry/react": "^6.0.0",
|
||||||
"@sentry/tracing": "^6.0.0",
|
"@sentry/tracing": "^6.0.0",
|
||||||
"@testing-library/jest-dom": "^5.16.1",
|
"@testing-library/jest-dom": "^5.16.1",
|
||||||
"@testing-library/react": "^12.1.2",
|
"@testing-library/react": "^12.1.2",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
"axios": "^0.22.0",
|
"axios": "^0.22.0",
|
||||||
"blockly": "^6.20210701.0",
|
"blockly": "^7.20211209.4",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"markdown-it": "^12.3.2",
|
"markdown-it": "^12.3.2",
|
||||||
"mnemonic-id": "^3.2.7",
|
"mnemonic-id": "^3.2.7",
|
||||||
"moment": "^2.28.0",
|
"moment": "^2.28.0",
|
||||||
"prismjs": "^1.25.0",
|
"prismjs": "^1.25.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-cookie-consent": "^7.0.0",
|
"react-cookie-consent": "^7.2.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-markdown": "^5.0.2",
|
"react-markdown": "^5.0.2",
|
||||||
"react-markdown-editor-lite": "^1.3.2",
|
"react-markdown-editor-lite": "^1.3.2",
|
||||||
"react-mde": "^11.5.0",
|
"react-mde": "^11.5.0",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.4",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "^4.0.3",
|
"react-scripts": "^5.0.0",
|
||||||
"reactour": "^1.18.0",
|
"reactour": "^1.18.7",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"styled-components": "^4.4.1",
|
"styled-components": "^4.4.1",
|
||||||
"uuid": "^8.3.1"
|
"uuid": "^8.3.1",
|
||||||
|
"watchpack": "^2.3.1"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"//": "See https://github.com/facebook/create-react-app/issues/11773",
|
||||||
|
"react-error-overlay": "6.0.9"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node_modules/react-scripts/bin/react-scripts.js start",
|
"start": "node_modules/react-scripts/bin/react-scripts.js start",
|
||||||
@ -50,16 +57,9 @@
|
|||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": [
|
||||||
"production": [
|
">0.2%",
|
||||||
">0.2%",
|
"not dead",
|
||||||
"not dead",
|
"not op_mini all"
|
||||||
"not op_mini all"
|
]
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
57
src/App.css
57
src/App.css
@ -1,51 +1,50 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
min-height: calc(100vh - 60px); /* will cover the 100% of viewport - height of footer (padding-bottom) */
|
min-height: calc(
|
||||||
|
100vh - 60px
|
||||||
|
); /* will cover the 100% of viewport - height of footer (padding-bottom) */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 60px; /* height of your footer + 30px*/
|
padding-bottom: 60px; /* height of your footer + 30px*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tutorial img {
|
||||||
.tutorial img{
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-height: 40vH;
|
max-height: 40vh;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.news img{
|
.news img {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-height: 40vH;
|
max-height: 40vh;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tutorial blockquote{
|
.tutorial blockquote {
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
border-left: 10px solid#4EAF47;
|
border-left: 10px solid#4EAF47;
|
||||||
margin: 1.5em 10px;
|
margin: 1.5em 10px;
|
||||||
padding: 0.5em 10px;
|
padding: 0.5em 10px;
|
||||||
quotes: "\201C""\201D""\2018""\2019";
|
quotes: "\201C""\201D""\2018""\2019";
|
||||||
}
|
}
|
||||||
blockquote:before {
|
blockquote:before {
|
||||||
color:#4EAF47;
|
color: #4eaf47;
|
||||||
content: open-quote;
|
content: open-quote;
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
line-height: 0.1em;
|
line-height: 0.1em;
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
vertical-align: -0.4em;
|
vertical-align: -0.4em;
|
||||||
}
|
}
|
||||||
blockquote p {
|
blockquote p {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
29
src/App.js
29
src/App.js
@ -1,35 +1,34 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from "react";
|
||||||
|
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from "react-router-dom";
|
||||||
import { createBrowserHistory } from "history";
|
import { createBrowserHistory } from "history";
|
||||||
|
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from "react-redux";
|
||||||
import store from './store';
|
import store from "./store";
|
||||||
import { loadUser } from './actions/authActions';
|
import { loadUser } from "./actions/authActions";
|
||||||
|
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
|
|
||||||
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
|
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
|
||||||
|
|
||||||
import Content from './components/Content';
|
import Content from "./components/Content";
|
||||||
|
|
||||||
const theme = createMuiTheme({
|
const theme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#4EAF47',
|
main: "#4EAF47",
|
||||||
contrastText: '#ffffff'
|
contrastText: "#ffffff",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#DDDDDD'
|
main: "#DDDDDD",
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
compile: '#e27136'
|
compile: "#e27136",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
store.dispatch(loadUser());
|
store.dispatch(loadUser());
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import "./generator/index";
|
|||||||
import { ZoomToFitControl } from "@blockly/zoom-to-fit";
|
import { ZoomToFitControl } from "@blockly/zoom-to-fit";
|
||||||
import { initialXml } from "./initialXml.js";
|
import { initialXml } from "./initialXml.js";
|
||||||
import { getMaxInstances } from "./helpers/maxInstances";
|
import { getMaxInstances } from "./helpers/maxInstances";
|
||||||
|
import { Backpack } from "@blockly/workspace-backpack";
|
||||||
|
|
||||||
class BlocklyWindow extends Component {
|
class BlocklyWindow extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -35,6 +36,9 @@ class BlocklyWindow extends Component {
|
|||||||
Blockly.svgResize(workspace);
|
Blockly.svgResize(workspace);
|
||||||
const zoomToFit = new ZoomToFitControl(workspace);
|
const zoomToFit = new ZoomToFitControl(workspace);
|
||||||
zoomToFit.init();
|
zoomToFit.init();
|
||||||
|
// Initialize plugin.
|
||||||
|
const backpack = new Backpack(workspace);
|
||||||
|
backpack.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
componentDidUpdate(props) {
|
||||||
|
@ -2,10 +2,17 @@ import Blockly from "blockly";
|
|||||||
|
|
||||||
const setVariableFunction = function (defaultValue) {
|
const setVariableFunction = function (defaultValue) {
|
||||||
return function (block) {
|
return function (block) {
|
||||||
const variableName = Blockly["Arduino"].nameDB_.getName(
|
var id = block.getFieldValue("VAR");
|
||||||
block.getFieldValue("VAR"),
|
|
||||||
Blockly.Variables.NAME_TYPE
|
const variableName = Blockly.Variables.getVariable(
|
||||||
);
|
Blockly.getMainWorkspace(),
|
||||||
|
id
|
||||||
|
).name;
|
||||||
|
|
||||||
|
// const variableName = Blockly["Arduino"].nameDB_.getName(
|
||||||
|
// id,
|
||||||
|
// Blockly.Variables.NAME_TYPE
|
||||||
|
// );
|
||||||
const variableValue = Blockly["Arduino"].valueToCode(
|
const variableValue = Blockly["Arduino"].valueToCode(
|
||||||
block,
|
block,
|
||||||
"VALUE",
|
"VALUE",
|
||||||
@ -16,32 +23,12 @@ const setVariableFunction = function (defaultValue) {
|
|||||||
.getVariableMap()
|
.getVariableMap()
|
||||||
.getAllVariables();
|
.getAllVariables();
|
||||||
const myVar = allVars.filter((v) => v.name === variableName)[0];
|
const myVar = allVars.filter((v) => v.name === variableName)[0];
|
||||||
|
console.log(myVar);
|
||||||
var code = "";
|
var code = "";
|
||||||
|
if (myVar !== undefined) {
|
||||||
switch (myVar.type) {
|
Blockly.Arduino.variables_[variableName + myVar.type] =
|
||||||
default:
|
myVar.type + " " + myVar.name + ";\n";
|
||||||
Blockly.Arduino.variables_[variableName + myVar.type] =
|
code = variableName + " = " + (variableValue || defaultValue) + ";\n";
|
||||||
myVar.type + " " + myVar.name + ";\n";
|
|
||||||
code = variableName + " = " + (variableValue || defaultValue) + ";\n";
|
|
||||||
break;
|
|
||||||
case "Array":
|
|
||||||
var arrayType;
|
|
||||||
var number;
|
|
||||||
|
|
||||||
if (this.getChildren().length > 0) {
|
|
||||||
if (this.getChildren()[0].type === "lists_create_empty") {
|
|
||||||
arrayType = this.getChildren()[0].getFieldValue("type");
|
|
||||||
number = Blockly.Arduino.valueToCode(
|
|
||||||
this.getChildren()[0],
|
|
||||||
"NUMBER",
|
|
||||||
Blockly["Arduino"].ORDER_ATOMIC
|
|
||||||
);
|
|
||||||
Blockly.Arduino.variables_[
|
|
||||||
myVar + myVar.type
|
|
||||||
] = `${arrayType} ${myVar.name} [${number}];\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
};
|
};
|
||||||
|
284
src/components/CodeEditor/CodeEditor.js
Normal file
284
src/components/CodeEditor/CodeEditor.js
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useState, useRef } from "react";
|
||||||
|
import { default as MonacoEditor } from "@monaco-editor/react";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import { Button, Grid } from "@material-ui/core";
|
||||||
|
import Blockly from "blockly/core";
|
||||||
|
import Divider from "@material-ui/core/Divider";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import Drawer from "@material-ui/core/Drawer";
|
||||||
|
import Sidebar from "./Sidebar";
|
||||||
|
import Dialog from "../Dialog";
|
||||||
|
import SaveIcon from "./SaveIcon";
|
||||||
|
import store from "../../store";
|
||||||
|
|
||||||
|
const CodeEditor = (props) => {
|
||||||
|
const [fileHandle, setFileHandle] = useState();
|
||||||
|
const [fileContent, setFileContent] = useState("");
|
||||||
|
const [progress, setProgress] = useState(false);
|
||||||
|
const [id, setId] = useState("");
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const editorRef = useRef(null);
|
||||||
|
const [autoSave, setAutoSave] = useState(false);
|
||||||
|
const [time, setTime] = useState(null);
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
const [resetDialog, setResetDialog] = useState(false);
|
||||||
|
const [defaultValue, setDefaultValue] = useState(
|
||||||
|
localStorage.getItem("ArduinoCode")
|
||||||
|
? localStorage.getItem("ArduinoCode")
|
||||||
|
: `
|
||||||
|
#include <senseBoxIO.h> //needs to be always included
|
||||||
|
|
||||||
|
void setup () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const compile = () => {
|
||||||
|
setProgress(true);
|
||||||
|
const data = {
|
||||||
|
board: process.env.REACT_APP_BOARD,
|
||||||
|
sketch: editorRef.current.getValue(),
|
||||||
|
};
|
||||||
|
fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data.code === "Internal Server Error") {
|
||||||
|
setProgress(false);
|
||||||
|
setOpen(true);
|
||||||
|
setError(data.message);
|
||||||
|
}
|
||||||
|
setProgress(false);
|
||||||
|
const result = data.data.id;
|
||||||
|
setId(result);
|
||||||
|
const filename = "sketch";
|
||||||
|
window.open(
|
||||||
|
`${process.env.REACT_APP_COMPILER_URL}/download?id=${result}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`,
|
||||||
|
"_self"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveIno = () => {
|
||||||
|
var filename = "sketch";
|
||||||
|
var code = editorRef.current.getValue();
|
||||||
|
|
||||||
|
filename = `${filename}.ino`;
|
||||||
|
var blob = new Blob([code], { type: "text/plain;charset=utf-8" });
|
||||||
|
saveAs(blob, filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openIno = async () => {
|
||||||
|
const [myFileHandle] = await window.showOpenFilePicker();
|
||||||
|
setFileHandle(myFileHandle);
|
||||||
|
|
||||||
|
const file = await myFileHandle.getFile();
|
||||||
|
const contents = await file.text();
|
||||||
|
setFileContent(contents);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDrawer = (anchor, open) => (event) => {
|
||||||
|
if (
|
||||||
|
event.type === "keydown" &&
|
||||||
|
(event.key === "Tab" || event.key === "Shift")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetCode = () => {
|
||||||
|
const resetCode = `
|
||||||
|
#include <senseBoxIO.h> //needs to be always included
|
||||||
|
|
||||||
|
void setup () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
}`;
|
||||||
|
|
||||||
|
editorRef.current.setValue(resetCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetTimeout = (id, newID) => {
|
||||||
|
clearTimeout(id);
|
||||||
|
return newID;
|
||||||
|
};
|
||||||
|
|
||||||
|
const editValue = (value) => {
|
||||||
|
setTime(resetTimeout(time, setTimeout(saveValue, 400)));
|
||||||
|
setValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveValue = () => {
|
||||||
|
localStorage.setItem("ArduinoCode", value);
|
||||||
|
setAutoSave(true);
|
||||||
|
setTimeout(() => setAutoSave(false), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBlocklyCode = () => {
|
||||||
|
var code = store.getState().workspace.code.arduino;
|
||||||
|
editorRef.current.setValue(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Drawer
|
||||||
|
anchor={"bottom"}
|
||||||
|
open={open}
|
||||||
|
onClose={toggleDrawer("bottom", false)}
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
color: "#4EAF47",
|
||||||
|
paddingLeft: "1rem",
|
||||||
|
paddingRight: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Blockly.Msg.drawer_ideerror_head}
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
color: "#4EAF47",
|
||||||
|
paddingLeft: "1rem",
|
||||||
|
paddingRight: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Blockly.Msg.drawer_ideerror_text}
|
||||||
|
</p>
|
||||||
|
<Divider style={{ backgroundColor: "white" }} />
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
backgroundColor: "black",
|
||||||
|
color: "#E47128",
|
||||||
|
padding: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
{`${error}`}{" "}
|
||||||
|
</p>
|
||||||
|
</Drawer>
|
||||||
|
<Grid item lg={8}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<h1>Code Editor</h1>
|
||||||
|
<SaveIcon loading={autoSave} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MonacoEditor
|
||||||
|
height="80vh"
|
||||||
|
onChange={(value) => {
|
||||||
|
editValue(value);
|
||||||
|
}}
|
||||||
|
defaultLanguage="cpp"
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
value={fileContent}
|
||||||
|
onMount={(editor, monaco) => {
|
||||||
|
editorRef.current = editor;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item lg={4}>
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => compile()}
|
||||||
|
>
|
||||||
|
Kompilieren
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => saveIno()}
|
||||||
|
>
|
||||||
|
Save Code
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => openIno()}
|
||||||
|
>
|
||||||
|
Open Code
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => setResetDialog(true)}
|
||||||
|
>
|
||||||
|
Reset Editor
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => getBlocklyCode()}
|
||||||
|
>
|
||||||
|
getBlocklyCode
|
||||||
|
</Button>
|
||||||
|
<Sidebar />
|
||||||
|
<Dialog
|
||||||
|
style={{ zIndex: 9999999 }}
|
||||||
|
fullWidth
|
||||||
|
maxWidth={"sm"}
|
||||||
|
open={progress}
|
||||||
|
title={"Code wird kompiliert"}
|
||||||
|
content={""}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Dein Code wird nun kompiliert und anschließend auf deinen Computer
|
||||||
|
heruntergeladen
|
||||||
|
</div>
|
||||||
|
</Dialog>{" "}
|
||||||
|
<Dialog
|
||||||
|
open={resetDialog}
|
||||||
|
title={Blockly.Msg.resetDialog_headline}
|
||||||
|
content={Blockly.Msg.resetDialog_text}
|
||||||
|
onClose={() => {
|
||||||
|
setResetDialog(false);
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setResetDialog(false);
|
||||||
|
}}
|
||||||
|
button={Blockly.Msg.button_cancel}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
<div style={{ marginTop: "10px" }}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
resetCode();
|
||||||
|
setResetDialog(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Zurücksetzen
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRouter(CodeEditor);
|
332
src/components/CodeEditor/Compile.js
Normal file
332
src/components/CodeEditor/Compile.js
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { workspaceName } from "../../actions/workspaceActions";
|
||||||
|
|
||||||
|
import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace";
|
||||||
|
|
||||||
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import Backdrop from "@material-ui/core/Backdrop";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
import Divider from "@material-ui/core/Divider";
|
||||||
|
import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import * as Blockly from "blockly/core";
|
||||||
|
import Copy from "../copy.svg";
|
||||||
|
|
||||||
|
import MuiDrawer from "@material-ui/core/Drawer";
|
||||||
|
import Dialog from "../Dialog";
|
||||||
|
|
||||||
|
const styles = (theme) => ({
|
||||||
|
backdrop: {
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
|
backgroundColor: theme.palette.button.compile,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
width: "40px",
|
||||||
|
height: "40px",
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.button.compile,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: theme.palette.button.compile,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.button.compile,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Drawer = withStyles((theme) => ({
|
||||||
|
paperAnchorBottom: {
|
||||||
|
backgroundColor: "black",
|
||||||
|
height: "20vH",
|
||||||
|
},
|
||||||
|
}))(MuiDrawer);
|
||||||
|
|
||||||
|
class Compile extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
progress: false,
|
||||||
|
open: false,
|
||||||
|
file: false,
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
name: props.name,
|
||||||
|
error: "",
|
||||||
|
appLink: "",
|
||||||
|
appDialog: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {}
|
||||||
|
|
||||||
|
componentDidUpdate(props) {
|
||||||
|
if (props.name !== this.props.name) {
|
||||||
|
this.setState({ name: this.props.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compile = () => {
|
||||||
|
this.setState({ progress: true });
|
||||||
|
const data = {
|
||||||
|
board: process.env.REACT_APP_BOARD,
|
||||||
|
sketch: this.props.arduino,
|
||||||
|
};
|
||||||
|
fetch(`${process.env.REACT_APP_COMPILER_URL}/compile`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data.code === "Internal Server Error") {
|
||||||
|
this.setState({
|
||||||
|
progress: false,
|
||||||
|
file: false,
|
||||||
|
open: true,
|
||||||
|
title: Blockly.Msg.compiledialog_headline,
|
||||||
|
content: Blockly.Msg.compiledialog_text,
|
||||||
|
error: data.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.setState({ id: data.data.id }, () => {
|
||||||
|
this.createFileName();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
//this.setState({ progress: false, file: false, open: true, title: Blockly.Msg.compiledialog_headline, content: Blockly.Msg.compiledialog_text });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
download = () => {
|
||||||
|
const id = this.state.id;
|
||||||
|
const filename = detectWhitespacesAndReturnReadableResult(this.state.name);
|
||||||
|
this.toggleDialog();
|
||||||
|
this.props.workspaceName(this.state.name);
|
||||||
|
window.open(
|
||||||
|
`${process.env.REACT_APP_COMPILER_URL}/download?id=${id}&board=${process.env.REACT_APP_BOARD}&filename=${filename}`,
|
||||||
|
"_self"
|
||||||
|
);
|
||||||
|
this.setState({ progress: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleDialog = () => {
|
||||||
|
this.setState({ open: !this.state, progress: false, appDialog: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
createFileName = () => {
|
||||||
|
if (this.props.platform === true) {
|
||||||
|
const filename = detectWhitespacesAndReturnReadableResult(
|
||||||
|
this.state.name
|
||||||
|
);
|
||||||
|
this.setState({
|
||||||
|
link: `blocklyconnect-app://sketch/${filename}/${this.state.id}`,
|
||||||
|
});
|
||||||
|
this.setState({ appDialog: true });
|
||||||
|
} else {
|
||||||
|
if (this.state.name) {
|
||||||
|
this.download();
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
file: true,
|
||||||
|
open: true,
|
||||||
|
title: "Projekt kompilieren",
|
||||||
|
content:
|
||||||
|
"Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (this.state.name) {
|
||||||
|
// this.download();
|
||||||
|
// } else {
|
||||||
|
// this.setState({
|
||||||
|
// file: true,
|
||||||
|
// open: true,
|
||||||
|
// title: "Projekt kompilieren",
|
||||||
|
// content:
|
||||||
|
// "Bitte gib einen Namen für die Bennenung des zu kompilierenden Programms ein und bestätige diesen mit einem Klick auf 'Eingabe'.",
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
setFileName = (e) => {
|
||||||
|
this.setState({ name: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleDrawer = (anchor, open) => (event) => {
|
||||||
|
if (
|
||||||
|
event.type === "keydown" &&
|
||||||
|
(event.key === "Tab" || event.key === "Shift")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ open: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={{}}>
|
||||||
|
{this.props.iconButton ? (
|
||||||
|
<Tooltip
|
||||||
|
title={Blockly.Msg.tooltip_compile_code}
|
||||||
|
arrow
|
||||||
|
style={{ marginRight: "5px" }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
className={`compileBlocks ${this.props.classes.iconButton}`}
|
||||||
|
onClick={() => this.compile()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faClipboardCheck} size="m" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
style={{ float: "right", color: "white" }}
|
||||||
|
variant="contained"
|
||||||
|
className={this.props.classes.button}
|
||||||
|
onClick={() => this.compile()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faClipboardCheck}
|
||||||
|
style={{ marginRight: "5px" }}
|
||||||
|
/>{" "}
|
||||||
|
Kompilieren
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{this.props.platform === false ? (
|
||||||
|
<Backdrop
|
||||||
|
className={this.props.classes.backdrop}
|
||||||
|
open={this.state.progress}
|
||||||
|
>
|
||||||
|
<div className="overlay">
|
||||||
|
<img src={Copy} width="400" alt="copyimage"></img>
|
||||||
|
<h2>{Blockly.Msg.compile_overlay_head}</h2>
|
||||||
|
<p>{Blockly.Msg.compile_overlay_text}</p>
|
||||||
|
<p>
|
||||||
|
{Blockly.Msg.compile_overlay_help}
|
||||||
|
<a href="/faq" target="_blank">
|
||||||
|
FAQ
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
</div>
|
||||||
|
</Backdrop>
|
||||||
|
) : (
|
||||||
|
<Backdrop
|
||||||
|
className={this.props.classes.backdrop}
|
||||||
|
open={this.state.progress}
|
||||||
|
>
|
||||||
|
<div className="overlay">
|
||||||
|
{/* <img src={Copy} width="400" alt="copyimage"></img> */}
|
||||||
|
<h2>Dein Code wird kompiliert!</h2>
|
||||||
|
<p>übertrage ihn anschließend mithlfe der senseBoxConnect-App</p>
|
||||||
|
<p>
|
||||||
|
{Blockly.Msg.compile_overlay_help}
|
||||||
|
<a href="/faq" target="_blank">
|
||||||
|
FAQ
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
</div>
|
||||||
|
</Backdrop>
|
||||||
|
)}
|
||||||
|
<Drawer
|
||||||
|
anchor={"bottom"}
|
||||||
|
open={this.state.open}
|
||||||
|
onClose={this.toggleDrawer("bottom", false)}
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
color: "#4EAF47",
|
||||||
|
paddingLeft: "1rem",
|
||||||
|
paddingRight: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Blockly.Msg.drawer_ideerror_head}
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
color: "#4EAF47",
|
||||||
|
paddingLeft: "1rem",
|
||||||
|
paddingRight: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Blockly.Msg.drawer_ideerror_text}
|
||||||
|
</p>
|
||||||
|
<Divider style={{ backgroundColor: "white" }} />
|
||||||
|
<p
|
||||||
|
style={{
|
||||||
|
backgroundColor: "black",
|
||||||
|
color: "#E47128",
|
||||||
|
padding: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
{`${this.state.error}`}{" "}
|
||||||
|
</p>
|
||||||
|
</Drawer>
|
||||||
|
<Dialog
|
||||||
|
style={{ zIndex: 9999999 }}
|
||||||
|
fullWidth
|
||||||
|
maxWidth={"sm"}
|
||||||
|
open={this.state.appDialog}
|
||||||
|
title=""
|
||||||
|
content={""}
|
||||||
|
onClose={this.toggleDialog}
|
||||||
|
onClick={this.toggleDialog}
|
||||||
|
button={Blockly.Msg.button_close}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p>Dein Code wurde erfolgreich kompiliert</p>
|
||||||
|
<a href={this.state.link}>
|
||||||
|
<Button
|
||||||
|
style={{ color: "white" }}
|
||||||
|
variant="contained"
|
||||||
|
className={this.props.classes.button}
|
||||||
|
onClick={() => this.toggleDialog()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faClipboardCheck}
|
||||||
|
style={{ marginRight: "5px" }}
|
||||||
|
/>{" "}
|
||||||
|
Starte Übertragung
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Compile.propTypes = {
|
||||||
|
arduino: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string,
|
||||||
|
workspaceName: PropTypes.func.isRequired,
|
||||||
|
platform: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
arduino: state.workspace.code.arduino,
|
||||||
|
name: state.workspace.name,
|
||||||
|
platform: state.general.platform,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, { workspaceName })(
|
||||||
|
withStyles(styles, { withTheme: true })(Compile)
|
||||||
|
);
|
40
src/components/CodeEditor/SaveIcon.js
Normal file
40
src/components/CodeEditor/SaveIcon.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faCircleNotch, faSave } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const SaveIcon = ({ loading }) => (
|
||||||
|
<Tooltip title={"Auto save enabled"} arrow placement="right">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
width: "2rem",
|
||||||
|
height: "2rem",
|
||||||
|
margin: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading && (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
style={{ position: "absolute" }}
|
||||||
|
icon={faCircleNotch}
|
||||||
|
spin={true}
|
||||||
|
size="2x"
|
||||||
|
color="grey"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FontAwesomeIcon
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: "50%",
|
||||||
|
top: "50%",
|
||||||
|
transform: "translate(-50%,-50%)",
|
||||||
|
}}
|
||||||
|
icon={faSave}
|
||||||
|
color={loading ? "grey" : "green"}
|
||||||
|
size={loading ? "1x" : "lg"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default SaveIcon;
|
92
src/components/CodeEditor/SerialMonitor.js
Normal file
92
src/components/CodeEditor/SerialMonitor.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
|
||||||
|
const SerialMonitor = () => {
|
||||||
|
const [serialPortContent, setSerialPortContent] = useState([]);
|
||||||
|
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
const handleClick = () => setChecked(!checked);
|
||||||
|
|
||||||
|
const connectPort = async () => {
|
||||||
|
try {
|
||||||
|
const port = await navigator.serial.requestPort();
|
||||||
|
|
||||||
|
await port.open({ baudRate: 9600 });
|
||||||
|
|
||||||
|
while (port.readable) {
|
||||||
|
const reader = port.readable.getReader();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
// Allow the serial port to be closed later.
|
||||||
|
reader.releaseLock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
// byte array to string: https://stackoverflow.com/a/37542820
|
||||||
|
const text = String.fromCharCode.apply(null, value);
|
||||||
|
console.log(text);
|
||||||
|
setSerialPortContent((prevContent) => [
|
||||||
|
...prevContent,
|
||||||
|
[new Date(), text],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setSerialPortContent((prevContent) => [
|
||||||
|
...prevContent,
|
||||||
|
[new Date(), error],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setSerialPortContent((prevContent) => [
|
||||||
|
...prevContent,
|
||||||
|
[new Date(), error],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Button type="button" variant="outlined" onClick={() => connectPort()}>
|
||||||
|
Connect to senseBox
|
||||||
|
</Button>
|
||||||
|
<label className="m-4 text-gray-700 text-base font-semibold px-6 py-3 rounded-lg">
|
||||||
|
Show timestamps
|
||||||
|
<input
|
||||||
|
onChange={handleClick}
|
||||||
|
checked={checked}
|
||||||
|
type="checkbox"
|
||||||
|
className="mx-4"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outlined"
|
||||||
|
onClick={() => setSerialPortContent([])}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="font-mono">
|
||||||
|
{serialPortContent.map((log) => {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{checked && (
|
||||||
|
<span className="font-medium mr-4">{log[0].toISOString()}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<span>{log[1]}</span>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SerialMonitor;
|
132
src/components/CodeEditor/Sidebar.js
Normal file
132
src/components/CodeEditor/Sidebar.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import Blockly from "blockly";
|
||||||
|
import Accordion from "@material-ui/core/Accordion";
|
||||||
|
import AccordionSummary from "@material-ui/core/AccordionSummary";
|
||||||
|
import AccordionDetails from "@material-ui/core/AccordionDetails";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import { LibraryVersions } from "../../data/versions.js";
|
||||||
|
import { useMonaco } from "@monaco-editor/react";
|
||||||
|
import { Button } from "@material-ui/core";
|
||||||
|
import Dialog from "../Dialog";
|
||||||
|
import SerialMonitor from "./SerialMonitor.js";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const Sidebar = () => {
|
||||||
|
const [alert, setAlert] = React.useState(false);
|
||||||
|
const [examples, setExamples] = React.useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get("https://coelho.opensensemap.org/items/blocklysamples")
|
||||||
|
.then((res) => {
|
||||||
|
setExamples(res.data.data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const monaco = useMonaco();
|
||||||
|
const loadCode = (code) => {
|
||||||
|
monaco.editor.getModels()[0].setValue(code);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDialog = () => {
|
||||||
|
setAlert(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{"serial" in navigator ? (
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={""}
|
||||||
|
aria-controls="panel1a-content"
|
||||||
|
id="panel1a-header"
|
||||||
|
>
|
||||||
|
<Typography>Serial Monitor</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography>
|
||||||
|
<SerialMonitor />
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={""}
|
||||||
|
aria-controls="panel1a-content"
|
||||||
|
id="panel1a-header"
|
||||||
|
>
|
||||||
|
<Typography>Beispiele</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>
|
||||||
|
<Typography>
|
||||||
|
{examples.map((object, i) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
style={{ padding: "1rem", margin: "1rem" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => loadCode(object.code)}
|
||||||
|
>
|
||||||
|
{object.name}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
<Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={""}
|
||||||
|
aria-controls="panel2a-content"
|
||||||
|
id="panel2a-header"
|
||||||
|
>
|
||||||
|
<Typography>Installierte Libraries</Typography>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails
|
||||||
|
style={{ padding: 0, height: "60vH", backgroundColor: "white" }}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
style={{ overflow: "auto", width: "100%", padding: "1rem" }}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
For Dokumentation take a look at the installed libraries and their
|
||||||
|
source
|
||||||
|
</p>
|
||||||
|
{LibraryVersions().map((object, i) => {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
<a href={object.link} target="_blank" rel="noreferrer">
|
||||||
|
{object.library} {object.version}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
<Dialog
|
||||||
|
style={{ zIndex: 9999999 }}
|
||||||
|
fullWidth
|
||||||
|
maxWidth={"sm"}
|
||||||
|
open={alert}
|
||||||
|
title={Blockly.Msg.tabletDialog_headline}
|
||||||
|
content={""}
|
||||||
|
onClose={() => toggleDialog()}
|
||||||
|
onClick={() => toggleDialog()}
|
||||||
|
button={Blockly.Msg.button_close}
|
||||||
|
>
|
||||||
|
<div>{Blockly.Msg.tabletDialog_text}</div>
|
||||||
|
<div>
|
||||||
|
{Blockly.Msg.tabletDialog_more}{" "}
|
||||||
|
<a href="https://sensebox.de/app" target="_blank" rel="noreferrer">
|
||||||
|
https://sensebox.de/app
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Sidebar;
|
@ -1,30 +1,25 @@
|
|||||||
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 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 withWidth from '@material-ui/core/withWidth';
|
|
||||||
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';
|
|
||||||
import { Card } from '@material-ui/core';
|
|
||||||
import * as Blockly from 'blockly'
|
|
||||||
|
|
||||||
|
import withWidth from "@material-ui/core/withWidth";
|
||||||
|
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";
|
||||||
|
import { Card } from "@material-ui/core";
|
||||||
|
import * as Blockly from "blockly";
|
||||||
|
import { default as MonacoEditor } from "@monaco-editor/react";
|
||||||
|
|
||||||
const Accordion = withStyles((theme) => ({
|
const Accordion = withStyles((theme) => ({
|
||||||
root: {
|
root: {
|
||||||
border: `1px solid ${theme.palette.secondary.main}`,
|
border: `1px solid ${theme.palette.secondary.main}`,
|
||||||
boxShadow: 'none',
|
boxShadow: "none",
|
||||||
'&:before': {
|
"&:before": {
|
||||||
display: 'none',
|
display: "none",
|
||||||
},
|
},
|
||||||
'&$expanded': {
|
"&$expanded": {
|
||||||
margin: 'auto',
|
margin: "auto",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expanded: {},
|
expanded: {},
|
||||||
@ -34,15 +29,15 @@ const AccordionSummary = withStyles((theme) => ({
|
|||||||
root: {
|
root: {
|
||||||
backgroundColor: theme.palette.secondary.main,
|
backgroundColor: theme.palette.secondary.main,
|
||||||
borderBottom: `1px solid white`,
|
borderBottom: `1px solid white`,
|
||||||
marginBottom: '-1px',
|
marginBottom: "-1px",
|
||||||
minHeight: '50px',
|
minHeight: "50px",
|
||||||
'&$expanded': {
|
"&$expanded": {
|
||||||
minHeight: '50px',
|
minHeight: "50px",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
'&$expanded': {
|
"&$expanded": {
|
||||||
margin: '12px 0',
|
margin: "12px 0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expanded: {},
|
expanded: {},
|
||||||
@ -54,40 +49,60 @@ const AccordionDetails = withStyles((theme) => ({
|
|||||||
},
|
},
|
||||||
}))(MuiAccordionDetails);
|
}))(MuiAccordionDetails);
|
||||||
|
|
||||||
|
|
||||||
class CodeViewer extends Component {
|
class CodeViewer extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
code: this.props.arduino,
|
||||||
|
changed: false,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
componentHeight: null
|
componentHeight: null,
|
||||||
};
|
};
|
||||||
this.myDiv = React.createRef();
|
this.myDiv = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
Prism.highlightAll();
|
this.setState({ componentHeight: this.myDiv.current.offsetHeight + "px" });
|
||||||
this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(props, state) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (this.myDiv.current && this.myDiv.current.offsetHeight + 'px' !== this.state.componentHeight) {
|
// if (this.props.arduino !== prevProps.arduino) {
|
||||||
this.setState({ componentHeight: this.myDiv.current.offsetHeight + 'px' });
|
// this.setState({ changed: true });
|
||||||
|
|
||||||
|
// console.log(`code changed: ${this.state.changed}`);
|
||||||
|
// if (this.state.changed && prevState.code !== this.props.arduino) {
|
||||||
|
// this.setState({ code: this.props.arduino });
|
||||||
|
// this.setState({ changed: false });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (this.state.code !== prevState.code && this.state.changed) {
|
||||||
|
// this.setState({ changed: false });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (this.props.arduino !== this.state.code) {
|
||||||
|
// this.setState({ changed: true });
|
||||||
|
// //this.setState({ code: this.props.arduino });
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.myDiv.current &&
|
||||||
|
this.myDiv.current.offsetHeight + "px" !== this.state.componentHeight
|
||||||
|
) {
|
||||||
|
this.setState({
|
||||||
|
componentHeight: this.myDiv.current.offsetHeight + "px",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Prism.highlightAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange = () => {
|
onChange = () => {
|
||||||
this.setState({ expanded: !this.state.expanded });
|
this.setState({ expanded: !this.state.expanded });
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var curlyBrackets = '{ }';
|
var curlyBrackets = "{ }";
|
||||||
var unequal = '<>';
|
var unequal = "<>";
|
||||||
return (
|
return (
|
||||||
<Card style={{ height: '100%', maxHeight: '60vH' }} ref={this.myDiv}>
|
<Card style={{ height: "100%", maxHeight: "60vH" }} ref={this.myDiv}>
|
||||||
<Accordion
|
<Accordion
|
||||||
square={true}
|
square={true}
|
||||||
style={{ margin: 0 }}
|
style={{ margin: 0 }}
|
||||||
@ -95,15 +110,32 @@ class CodeViewer extends Component {
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
>
|
>
|
||||||
<AccordionSummary>
|
<AccordionSummary>
|
||||||
<b style={{ fontSize: '20px', marginRight: '5px', width: '35px' }}>{curlyBrackets}</b>
|
<b style={{ fontSize: "20px", marginRight: "5px", width: "35px" }}>
|
||||||
<div style={{ margin: 'auto 5px 2px 0px' }}>{Blockly.Msg.codeviewer_arduino}</div>
|
{curlyBrackets}
|
||||||
|
</b>
|
||||||
|
<div style={{ margin: "auto 5px 2px 0px" }}>
|
||||||
|
{Blockly.Msg.codeviewer_arduino}
|
||||||
|
</div>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails style={{ padding: 0, height: `calc(${this.state.componentHeight} - 50px - 50px)`, backgroundColor: 'white' }}>
|
<AccordionDetails
|
||||||
<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' }}>
|
style={{
|
||||||
<code className="language-clike">
|
padding: 0,
|
||||||
{this.props.arduino}
|
height: `calc(${this.state.componentHeight} - 50px - 50px)`,
|
||||||
</code>
|
backgroundColor: "white",
|
||||||
</pre>
|
}}
|
||||||
|
>
|
||||||
|
<MonacoEditor
|
||||||
|
height="80vh"
|
||||||
|
defaultLanguage="cpp"
|
||||||
|
value={this.props.arduino}
|
||||||
|
// modified={this.props.arduino}
|
||||||
|
// original={this.state.code}
|
||||||
|
options={{
|
||||||
|
readOnly: true,
|
||||||
|
|
||||||
|
fontSize: "16px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion
|
<Accordion
|
||||||
@ -113,32 +145,43 @@ class CodeViewer extends Component {
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
>
|
>
|
||||||
<AccordionSummary>
|
<AccordionSummary>
|
||||||
<b style={{ fontSize: '20px', marginRight: '5px', width: '35px' }}>{unequal}</b>
|
<b style={{ fontSize: "20px", marginRight: "5px", width: "35px" }}>
|
||||||
<div style={{ margin: 'auto 5px 2px 0px' }}>{Blockly.Msg.codeviewer_xml}</div>
|
{unequal}
|
||||||
|
</b>
|
||||||
|
<div style={{ margin: "auto 5px 2px 0px" }}>
|
||||||
|
{Blockly.Msg.codeviewer_xml}
|
||||||
|
</div>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails style={{ padding: 0, height: `calc(${this.state.componentHeight} - 50px - 50px)`, backgroundColor: 'white' }}>
|
<AccordionDetails
|
||||||
<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' }}>
|
style={{
|
||||||
<code className="language-xml">
|
padding: 0,
|
||||||
{`${this.props.xml}`}
|
height: `calc(${this.state.componentHeight} - 50px - 50px)`,
|
||||||
</code>
|
backgroundColor: "white",
|
||||||
</pre>
|
}}
|
||||||
|
>
|
||||||
|
<MonacoEditor
|
||||||
|
height="80vh"
|
||||||
|
defaultLanguage="xml"
|
||||||
|
value={this.props.xml}
|
||||||
|
readOnly={true}
|
||||||
|
/>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodeViewer.propTypes = {
|
CodeViewer.propTypes = {
|
||||||
arduino: PropTypes.string.isRequired,
|
arduino: PropTypes.string.isRequired,
|
||||||
xml: PropTypes.string.isRequired,
|
xml: PropTypes.string.isRequired,
|
||||||
tooltip: PropTypes.string.isRequired
|
tooltip: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
arduino: state.workspace.code.arduino,
|
arduino: state.workspace.code.arduino,
|
||||||
xml: state.workspace.code.xml,
|
xml: state.workspace.code.xml,
|
||||||
tooltip: state.workspace.code.tooltip
|
tooltip: state.workspace.code.tooltip,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(withWidth()(CodeViewer));
|
export default connect(mapStateToProps, null)(withWidth()(CodeViewer));
|
||||||
|
@ -22,7 +22,7 @@ import { faCode } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import TooltipViewer from "./TooltipViewer";
|
import TooltipViewer from "./TooltipViewer";
|
||||||
import Dialog from "./Dialog";
|
import Dialog from "./Dialog";
|
||||||
|
// import Autosave from "./Workspace/AutoSave";
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
codeOn: {
|
codeOn: {
|
||||||
backgroundColor: theme.palette.primary.main,
|
backgroundColor: theme.palette.primary.main,
|
||||||
@ -54,11 +54,11 @@ class Home extends Component {
|
|||||||
key: "",
|
key: "",
|
||||||
message: "",
|
message: "",
|
||||||
open: true,
|
open: true,
|
||||||
|
initialXml: localStorage.getItem("autoSaveXML"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
console.log(this.props.platform);
|
|
||||||
if (this.props.platform === true) {
|
if (this.props.platform === true) {
|
||||||
this.setState({ codeOn: false });
|
this.setState({ codeOn: false });
|
||||||
}
|
}
|
||||||
@ -119,10 +119,12 @@ class Home extends Component {
|
|||||||
<WorkspaceStats />
|
<WorkspaceStats />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="workspaceFunc"
|
className="workspaceFunc"
|
||||||
style={{ float: "right", height: "40px", marginBottom: "20px" }}
|
style={{ float: "right", height: "40px", marginBottom: "20px" }}
|
||||||
>
|
>
|
||||||
|
{/* <Autosave /> */}
|
||||||
<WorkspaceFunc
|
<WorkspaceFunc
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
projectType={this.props.projectType}
|
projectType={this.props.projectType}
|
||||||
@ -161,6 +163,7 @@ class Home extends Component {
|
|||||||
<FontAwesomeIcon icon={faCode} size="xs" />
|
<FontAwesomeIcon icon={faCode} size="xs" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<TrashcanButtons />
|
<TrashcanButtons />
|
||||||
<div className="blocklyWindow">
|
<div className="blocklyWindow">
|
||||||
{this.props.project ? (
|
{this.props.project ? (
|
||||||
@ -169,7 +172,10 @@ class Home extends Component {
|
|||||||
initialXml={this.props.project.xml}
|
initialXml={this.props.project.xml}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BlocklyWindow blocklyCSS={{ height: "80vH" }} />
|
<BlocklyWindow
|
||||||
|
blocklyCSS={{ height: "80vH" }}
|
||||||
|
initialXml={this.state.initialXml}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
@ -216,7 +222,7 @@ Home.propTypes = {
|
|||||||
workspaceName: PropTypes.func.isRequired,
|
workspaceName: PropTypes.func.isRequired,
|
||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
statistics: PropTypes.bool.isRequired,
|
statistics: PropTypes.bool.isRequired,
|
||||||
platform: PropTypes.object.isRequired,
|
platform: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
|
@ -21,6 +21,7 @@ import ListItemIcon from "@material-ui/core/ListItemIcon";
|
|||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import LinearProgress from "@material-ui/core/LinearProgress";
|
import LinearProgress from "@material-ui/core/LinearProgress";
|
||||||
import Tour from "reactour";
|
import Tour from "reactour";
|
||||||
|
import { Badge } from "@material-ui/core";
|
||||||
import { home, assessment } from "./Tour";
|
import { home, assessment } from "./Tour";
|
||||||
import {
|
import {
|
||||||
faBars,
|
faBars,
|
||||||
@ -34,6 +35,7 @@ import {
|
|||||||
faChalkboardTeacher,
|
faChalkboardTeacher,
|
||||||
faTools,
|
faTools,
|
||||||
faLightbulb,
|
faLightbulb,
|
||||||
|
faCode,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import * as Blockly from "blockly";
|
import * as Blockly from "blockly";
|
||||||
@ -236,6 +238,11 @@ class Navbar extends Component {
|
|||||||
link: "/project",
|
link: "/project",
|
||||||
restriction: this.props.isAuthenticated,
|
restriction: this.props.isAuthenticated,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: "Code Editor",
|
||||||
|
icon: faCode,
|
||||||
|
link: "/codeeditor",
|
||||||
|
},
|
||||||
].map((item, index) => {
|
].map((item, index) => {
|
||||||
if (
|
if (
|
||||||
item.restriction ||
|
item.restriction ||
|
||||||
@ -253,7 +260,13 @@ class Navbar extends Component {
|
|||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<FontAwesomeIcon icon={item.icon} />
|
<FontAwesomeIcon icon={item.icon} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={item.text} />
|
{item.text === "CodeEditor" ? (
|
||||||
|
<Badge badgeContent={"Experimental"} color="primary">
|
||||||
|
<ListItemText primary={item.text} />
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<ListItemText primary={item.text} />
|
||||||
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -346,9 +359,9 @@ class Navbar extends Component {
|
|||||||
Navbar.propTypes = {
|
Navbar.propTypes = {
|
||||||
tutorialIsLoading: PropTypes.bool.isRequired,
|
tutorialIsLoading: PropTypes.bool.isRequired,
|
||||||
projectIsLoading: PropTypes.bool.isRequired,
|
projectIsLoading: PropTypes.bool.isRequired,
|
||||||
isAuthenticated: PropTypes.bool.isRequired,
|
isAuthenticated: PropTypes.bool,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
tutorial: PropTypes.object.isRequired,
|
tutorial: PropTypes.object,
|
||||||
activeStep: PropTypes.number.isRequired,
|
activeStep: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import Login from "../User/Login";
|
|||||||
import Account from "../User/Account";
|
import Account from "../User/Account";
|
||||||
import News from "../News";
|
import News from "../News";
|
||||||
import Faq from "../Faq";
|
import Faq from "../Faq";
|
||||||
|
import CodeEditor from "../CodeEditor/CodeEditor";
|
||||||
|
|
||||||
class Routes extends Component {
|
class Routes extends Component {
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@ -47,6 +48,9 @@ class Routes extends Component {
|
|||||||
<Route path="/tutorial/:tutorialId" exact>
|
<Route path="/tutorial/:tutorialId" exact>
|
||||||
<Tutorial />
|
<Tutorial />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/CodeEditor" exact>
|
||||||
|
<CodeEditor />
|
||||||
|
</Route>
|
||||||
{/* Sharing */}
|
{/* Sharing */}
|
||||||
<PublicRoute path="/share/:shareId" exact>
|
<PublicRoute path="/share/:shareId" exact>
|
||||||
<Project />
|
<Project />
|
||||||
@ -100,7 +104,7 @@ class Routes extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Home.propTypes = {
|
Home.propTypes = {
|
||||||
visitPage: PropTypes.func.isRequired,
|
visitPage: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(null, { visitPage })(withRouter(Routes));
|
export default connect(null, { visitPage })(withRouter(Routes));
|
||||||
|
@ -4,7 +4,6 @@ import { connect } from "react-redux";
|
|||||||
import { workspaceName } from "../../actions/workspaceActions";
|
import { workspaceName } from "../../actions/workspaceActions";
|
||||||
|
|
||||||
import BlocklyWindow from "../Blockly/BlocklyWindow";
|
import BlocklyWindow from "../Blockly/BlocklyWindow";
|
||||||
import CodeViewer from "../CodeViewer";
|
|
||||||
import WorkspaceFunc from "../Workspace/WorkspaceFunc";
|
import WorkspaceFunc from "../Workspace/WorkspaceFunc";
|
||||||
|
|
||||||
import withWidth, { isWidthDown } from "@material-ui/core/withWidth";
|
import withWidth, { isWidthDown } from "@material-ui/core/withWidth";
|
||||||
@ -13,10 +12,46 @@ import Card from "@material-ui/core/Card";
|
|||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import * as Blockly from "blockly";
|
import * as Blockly from "blockly";
|
||||||
import { initialXml } from "../Blockly/initialXml";
|
import { initialXml } from "../Blockly/initialXml";
|
||||||
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
import CodeViewer from "../CodeViewer";
|
||||||
|
import TooltipViewer from "../TooltipViewer";
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import { faCode } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
|
|
||||||
|
const styles = (theme) => ({
|
||||||
|
codeOn: {
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.primary.contrastText,
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
border: `1px solid ${theme.palette.secondary.main}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
codeOff: {
|
||||||
|
backgroundColor: theme.palette.primary.contrastText,
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
border: `1px solid ${theme.palette.secondary.main}`,
|
||||||
|
"&:hover": {
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.primary.contrastText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
|
||||||
class Assessment extends Component {
|
class Assessment extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
codeOn: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.workspaceName(this.props.name);
|
this.props.workspaceName(this.props.name);
|
||||||
}
|
}
|
||||||
@ -27,6 +62,10 @@ class Assessment extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChange = () => {
|
||||||
|
this.setState({ codeOn: !this.state.codeOn });
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var tutorialId = this.props.tutorial._id;
|
var tutorialId = this.props.tutorial._id;
|
||||||
var currentTask = this.props.step;
|
var currentTask = this.props.step;
|
||||||
@ -40,51 +79,41 @@ class Assessment extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="assessmentDiv" style={{ width: "100%" }}>
|
<div className="assessmentDiv" style={{ width: "100%" }}>
|
||||||
<Typography
|
|
||||||
variant="h4"
|
|
||||||
style={{
|
|
||||||
float: "left",
|
|
||||||
marginBottom: "5px",
|
|
||||||
height: "40px",
|
|
||||||
display: "table",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{currentTask.headline}
|
|
||||||
</Typography>
|
|
||||||
<div style={{ float: "right", height: "40px" }}>
|
<div style={{ float: "right", height: "40px" }}>
|
||||||
<WorkspaceFunc assessment />
|
<WorkspaceFunc assessment />
|
||||||
</div>
|
</div>
|
||||||
<Grid container spacing={2} style={{ marginBottom: "5px" }}>
|
<Grid container spacing={2} style={{ marginBottom: "5px" }}>
|
||||||
<Grid item xs={12} md={6} lg={8}>
|
|
||||||
<BlocklyWindow
|
|
||||||
initialXml={initialXml}
|
|
||||||
blockDisabled
|
|
||||||
blocklyCSS={{ height: "65vH" }}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
<Grid
|
||||||
item
|
item
|
||||||
xs={12}
|
xs={12}
|
||||||
md={6}
|
md={6}
|
||||||
lg={4}
|
lg={3}
|
||||||
style={
|
style={{
|
||||||
isWidthDown("sm", this.props.width)
|
position: "relative",
|
||||||
? { height: "max-content" }
|
// isWidthDown("sm", this.props.width)
|
||||||
: {}
|
// ? { height: "max-content" }
|
||||||
}
|
// : {}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
style={{
|
style={{
|
||||||
height: "calc(50% - 30px)",
|
height: "calc(45vH - 30px)",
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
marginBottom: "10px",
|
marginBottom: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h5">
|
<Typography>
|
||||||
{Blockly.Msg.tutorials_assessment_task}
|
<ReactMarkdown>{currentTask.text}</ReactMarkdown>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography><ReactMarkdown>
|
</Card>
|
||||||
{currentTask.text}</ReactMarkdown></Typography>
|
<Card
|
||||||
|
style={{
|
||||||
|
height: "20vH",
|
||||||
|
padding: "10px",
|
||||||
|
marginBottom: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TooltipViewer />
|
||||||
</Card>
|
</Card>
|
||||||
<div
|
<div
|
||||||
style={
|
style={
|
||||||
@ -92,10 +121,52 @@ class Assessment extends Component {
|
|||||||
? { height: "500px" }
|
? { height: "500px" }
|
||||||
: { height: "50%" }
|
: { height: "50%" }
|
||||||
}
|
}
|
||||||
>
|
></div>
|
||||||
<CodeViewer />
|
|
||||||
</div>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
md={this.state.codeOn ? 6 : 6}
|
||||||
|
lg={this.state.codeOn ? 6 : 9}
|
||||||
|
style={{ position: "relative" }}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
this.state.codeOn
|
||||||
|
? Blockly.Msg.tooltip_hide_code
|
||||||
|
: Blockly.Msg.tooltip_show_code
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
className={`showCode ${
|
||||||
|
this.state.codeOn
|
||||||
|
? this.props.classes.codeOn
|
||||||
|
: this.props.classes.codeOff
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
width: "40px",
|
||||||
|
height: "40px",
|
||||||
|
position: "absolute",
|
||||||
|
top: 6,
|
||||||
|
right: 8,
|
||||||
|
zIndex: 21,
|
||||||
|
}}
|
||||||
|
onClick={() => this.onChange()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCode} size="xs" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<BlocklyWindow
|
||||||
|
initialXml={initialXml}
|
||||||
|
blockDisabled
|
||||||
|
blocklyCSS={{ height: "65vH" }}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
{this.state.codeOn ? (
|
||||||
|
<Grid item xs={12} md={4} lg={3}>
|
||||||
|
<CodeViewer />
|
||||||
|
</Grid>
|
||||||
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -116,5 +187,5 @@ const mapStateToProps = (state) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, { workspaceName })(
|
export default connect(mapStateToProps, { workspaceName })(
|
||||||
withWidth()(Assessment)
|
withWidth()(withStyles(styles, { withTheme: true })(Assessment))
|
||||||
);
|
);
|
||||||
|
@ -1,53 +1,57 @@
|
|||||||
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 { changeContent, deleteProperty, setError, deleteError } from '../../../actions/tutorialBuilderActions';
|
import {
|
||||||
|
changeContent,
|
||||||
|
deleteProperty,
|
||||||
|
setError,
|
||||||
|
deleteError,
|
||||||
|
} from "../../../actions/tutorialBuilderActions";
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from "moment";
|
||||||
import localization from 'moment/locale/de';
|
import localization from "moment/locale/de";
|
||||||
import * as Blockly from 'blockly/core';
|
import * as Blockly from "blockly/core";
|
||||||
|
|
||||||
import { initialXml } from '../../Blockly//initialXml.js';
|
import { initialXml } from "../../Blockly//initialXml.js";
|
||||||
import BlocklyWindow from '../../Blockly/BlocklyWindow';
|
import BlocklyWindow from "../../Blockly/BlocklyWindow";
|
||||||
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import Switch from '@material-ui/core/Switch';
|
import Switch from "@material-ui/core/Switch";
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||||
import FormLabel from '@material-ui/core/FormLabel';
|
import FormLabel from "@material-ui/core/FormLabel";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
import Grid from '@material-ui/core/Grid';
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
errorColor: {
|
errorColor: {
|
||||||
color: theme.palette.error.dark
|
color: theme.palette.error.dark,
|
||||||
},
|
},
|
||||||
errorBorder: {
|
errorBorder: {
|
||||||
border: `1px solid ${theme.palette.error.dark}`
|
border: `1px solid ${theme.palette.error.dark}`,
|
||||||
},
|
},
|
||||||
errorButton: {
|
errorButton: {
|
||||||
marginTop: '5px',
|
marginTop: "5px",
|
||||||
height: '40px',
|
height: "40px",
|
||||||
backgroundColor: theme.palette.error.dark,
|
backgroundColor: theme.palette.error.dark,
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: theme.palette.error.dark
|
backgroundColor: theme.palette.error.dark,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class BlocklyExample extends Component {
|
class BlocklyExample extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
checked: props.task ? props.task : props.value ? true : false,
|
checked: props.task ? props.task : props.value ? true : false,
|
||||||
input: null,
|
input: null,
|
||||||
disabled: false
|
disabled: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
moment.updateLocale('de', localization);
|
moment.updateLocale("de", localization);
|
||||||
this.isError();
|
this.isError();
|
||||||
// if(this.props.task){
|
// if(this.props.task){
|
||||||
// this.props.setError(this.props.index, 'xml');
|
// this.props.setError(this.props.index, 'xml');
|
||||||
@ -56,7 +60,14 @@ class BlocklyExample extends Component {
|
|||||||
|
|
||||||
componentDidUpdate(props, state) {
|
componentDidUpdate(props, state) {
|
||||||
if (props.task !== this.props.task || props.value !== this.props.value) {
|
if (props.task !== this.props.task || props.value !== this.props.value) {
|
||||||
this.setState({ checked: this.props.task ? this.props.task : this.props.value ? true : false },
|
this.setState(
|
||||||
|
{
|
||||||
|
checked: this.props.task
|
||||||
|
? this.props.task
|
||||||
|
: this.props.value
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
|
},
|
||||||
() => this.isError()
|
() => this.isError()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,12 +88,11 @@ class BlocklyExample extends Component {
|
|||||||
// check if value is valid xml;
|
// check if value is valid xml;
|
||||||
try {
|
try {
|
||||||
Blockly.Xml.textToDom(xml);
|
Blockly.Xml.textToDom(xml);
|
||||||
this.props.deleteError(this.props.index, 'xml');
|
this.props.deleteError(this.props.index, "xml");
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
xml = initialXml;
|
xml = initialXml;
|
||||||
// not valid xml, throw error in redux store
|
// not valid xml, throw error in redux store
|
||||||
this.props.setError(this.props.index, 'xml');
|
this.props.setError(this.props.index, "xml");
|
||||||
}
|
}
|
||||||
if (!this.props.task) {
|
if (!this.props.task) {
|
||||||
// instruction can also display only one block, which does not necessarily
|
// instruction can also display only one block, which does not necessarily
|
||||||
@ -90,31 +100,38 @@ class BlocklyExample extends Component {
|
|||||||
xml = xml.replace('deletable="false"', 'deletable="true"');
|
xml = xml.replace('deletable="false"', 'deletable="true"');
|
||||||
}
|
}
|
||||||
this.setState({ xml: xml });
|
this.setState({ xml: xml });
|
||||||
|
} else {
|
||||||
|
this.props.deleteError(this.props.index, "xml");
|
||||||
}
|
}
|
||||||
else {
|
};
|
||||||
this.props.deleteError(this.props.index, 'xml');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange = (value) => {
|
onChange = (value) => {
|
||||||
var oldValue = this.state.checked;
|
var oldValue = this.state.checked;
|
||||||
this.setState({ checked: value });
|
this.setState({ checked: value });
|
||||||
if (oldValue !== value && !value) {
|
if (oldValue !== value && !value) {
|
||||||
this.props.deleteError(this.props.index, 'xml');
|
this.props.deleteError(this.props.index, "xml");
|
||||||
this.props.deleteProperty(this.props.index, 'xml');
|
this.props.deleteProperty(this.props.index, "xml");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
setXml = () => {
|
setXml = () => {
|
||||||
var xml = this.props.xml;
|
var xml = this.props.xml;
|
||||||
this.props.changeContent(xml, this.props.index, 'xml');
|
this.props.changeContent(xml, this.props.index, "xml");
|
||||||
this.setState({ input: moment(Date.now()).format('LTS') });
|
this.setState({ input: moment(Date.now()).format("LTS") });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: '10px', padding: '18.5px 14px', borderRadius: '25px', border: '1px solid lightgrey', width: 'calc(100% - 28px)' }}>
|
<div
|
||||||
{!this.props.task ?
|
style={{
|
||||||
|
marginBottom: "10px",
|
||||||
|
padding: "18.5px 14px",
|
||||||
|
borderRadius: "25px",
|
||||||
|
border: "1px solid lightgrey",
|
||||||
|
width: "calc(100% - 28px)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!this.props.task ? (
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
labelPlacement="end"
|
labelPlacement="end"
|
||||||
label={"Blockly Beispiel"}
|
label={"Blockly Beispiel"}
|
||||||
@ -126,45 +143,77 @@ class BlocklyExample extends Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
: <FormLabel style={{ color: 'black' }}>{Blockly.Msg.builder_solution}</FormLabel>}
|
) : (
|
||||||
{this.state.checked ? !this.props.value || this.props.error ?
|
<FormLabel style={{ color: "black" }}>
|
||||||
<FormHelperText style={{ lineHeight: 'initial' }} className={this.props.classes.errorColor}>{`Reiche deine Blöcke ein, indem du auf den '${this.props.task ? Blockly.Msg.builder_solution_submit : Blockly.Msg.builder_example_submit}'-Button klickst.`}</FormHelperText>
|
{Blockly.Msg.builder_solution}
|
||||||
: this.state.input ? <FormHelperText style={{ lineHeight: 'initial' }}>Die letzte Einreichung erfolgte um {this.state.input} Uhr.</FormHelperText> : null
|
</FormLabel>
|
||||||
: null}
|
)}
|
||||||
{this.state.checked && !this.props.task ?
|
{this.state.checked ? (
|
||||||
<FormHelperText style={{ lineHeight: 'initial' }}>{Blockly.Msg.builder_comment}</FormHelperText>
|
!this.props.value || this.props.error ? (
|
||||||
: null}
|
<FormHelperText
|
||||||
|
style={{ lineHeight: "initial" }}
|
||||||
|
className={this.props.classes.errorColor}
|
||||||
|
>{`Reiche deine Blöcke ein, indem du auf den '${
|
||||||
|
this.props.task
|
||||||
|
? Blockly.Msg.builder_solution_submit
|
||||||
|
: Blockly.Msg.builder_example_submit
|
||||||
|
}'-Button klickst.`}</FormHelperText>
|
||||||
|
) : this.state.input ? (
|
||||||
|
<FormHelperText style={{ lineHeight: "initial" }}>
|
||||||
|
Die letzte Einreichung erfolgte um {this.state.input} Uhr.
|
||||||
|
</FormHelperText>
|
||||||
|
) : null
|
||||||
|
) : null}
|
||||||
|
{this.state.checked && !this.props.task ? (
|
||||||
|
<FormHelperText style={{ lineHeight: "initial" }}>
|
||||||
|
{Blockly.Msg.builder_comment}
|
||||||
|
</FormHelperText>
|
||||||
|
) : null}
|
||||||
{/* ensure that the correct xml-file is displayed in the workspace */}
|
{/* ensure that the correct xml-file is displayed in the workspace */}
|
||||||
{this.state.checked && this.state.xml ? (() => {
|
{this.state.checked && this.state.xml
|
||||||
return (
|
? (() => {
|
||||||
<div style={{ marginTop: '10px' }}>
|
return (
|
||||||
<Grid container className={!this.props.value || this.props.error ? this.props.classes.errorBorder : null}>
|
<div style={{ marginTop: "10px" }}>
|
||||||
<Grid item xs={12}>
|
<Grid
|
||||||
<BlocklyWindow
|
container
|
||||||
blockDisabled={this.props.task}
|
className={
|
||||||
trashcan={false}
|
!this.props.value || this.props.error
|
||||||
initialXml={this.state.xml}
|
? this.props.classes.errorBorder
|
||||||
blocklyCSS={{ height: '500px' }}
|
: null
|
||||||
/>
|
}
|
||||||
</Grid>
|
>
|
||||||
</Grid>
|
<Grid item xs={12}>
|
||||||
<Button
|
<BlocklyWindow
|
||||||
className={!this.props.value || this.props.error ? this.props.classes.errorButton : null}
|
blockDisabled={this.props.task}
|
||||||
style={{ marginTop: '5px', height: '40px' }}
|
trashcan={false}
|
||||||
variant='contained'
|
initialXml={this.state.xml}
|
||||||
color='primary'
|
blocklyCSS={{ height: "500px" }}
|
||||||
disabled={this.state.disabled}
|
/>
|
||||||
onClick={() => this.setXml()}
|
</Grid>
|
||||||
>
|
</Grid>
|
||||||
{this.props.task ? Blockly.Msg.builder_solution_submit : Blockly.Msg.builder_example_submit}
|
<Button
|
||||||
</Button>
|
className={
|
||||||
</div>
|
!this.props.value || this.props.error
|
||||||
)
|
? this.props.classes.errorButton
|
||||||
})()
|
: null
|
||||||
|
}
|
||||||
|
style={{ marginTop: "5px", height: "40px" }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={this.state.disabled}
|
||||||
|
onClick={() => this.setXml()}
|
||||||
|
>
|
||||||
|
{this.props.task
|
||||||
|
? Blockly.Msg.builder_solution_submit
|
||||||
|
: Blockly.Msg.builder_example_submit}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()
|
||||||
: null}
|
: null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocklyExample.propTypes = {
|
BlocklyExample.propTypes = {
|
||||||
@ -172,12 +221,16 @@ BlocklyExample.propTypes = {
|
|||||||
deleteProperty: PropTypes.func.isRequired,
|
deleteProperty: PropTypes.func.isRequired,
|
||||||
setError: PropTypes.func.isRequired,
|
setError: PropTypes.func.isRequired,
|
||||||
deleteError: PropTypes.func.isRequired,
|
deleteError: PropTypes.func.isRequired,
|
||||||
xml: PropTypes.string.isRequired
|
xml: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
xml: state.workspace.code.xml
|
xml: state.workspace.code.xml,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, {
|
||||||
export default connect(mapStateToProps, { changeContent, deleteProperty, setError, deleteError })(withStyles(styles, { withTheme: true })(BlocklyExample));
|
changeContent,
|
||||||
|
deleteProperty,
|
||||||
|
setError,
|
||||||
|
deleteError,
|
||||||
|
})(withStyles(styles, { withTheme: true })(BlocklyExample));
|
||||||
|
@ -1,75 +1,79 @@
|
|||||||
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 Dialog from '../Dialog';
|
import Dialog from "../Dialog";
|
||||||
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import Checkbox from '@material-ui/core/Checkbox';
|
import Checkbox from "@material-ui/core/Checkbox";
|
||||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import * as Blockly from 'blockly'
|
import * as Blockly from "blockly";
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
link: {
|
link: {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
textDecoration: 'none',
|
textDecoration: "none",
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
textDecoration: `underline`
|
textDecoration: `underline`,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontSize: '0.9rem',
|
fontSize: "0.9rem",
|
||||||
color: 'grey'
|
color: "grey",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class HintTutorialExists extends Component {
|
class HintTutorialExists extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
var previousPageWasAnotherDomain = props.pageVisits === 0;
|
var previousPageWasAnotherDomain = props.pageVisits === 0;
|
||||||
var userDoNotWantToSeeNews = window.localStorage.getItem('news') ? true : false;
|
var userDoNotWantToSeeNews = window.localStorage.getItem("news")
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
open: userDoNotWantToSeeNews ? !userDoNotWantToSeeNews : previousPageWasAnotherDomain
|
open: userDoNotWantToSeeNews
|
||||||
|
? !userDoNotWantToSeeNews
|
||||||
|
: previousPageWasAnotherDomain,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDialog = () => {
|
toggleDialog = () => {
|
||||||
this.setState({ open: !this.state });
|
this.setState({ open: !this.state });
|
||||||
}
|
};
|
||||||
|
|
||||||
onChange = (e) => {
|
onChange = (e) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
window.localStorage.setItem('news', e.target.checked);
|
window.localStorage.setItem("news", e.target.checked);
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem("news");
|
||||||
}
|
}
|
||||||
else {
|
};
|
||||||
window.localStorage.removeItem('news');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
style={{ zIndex: 9999999 }}
|
style={{ zIndex: 9999999 }}
|
||||||
fullWidth
|
fullWidth
|
||||||
maxWidth={'sm'}
|
maxWidth={"sm"}
|
||||||
open={this.state.open}
|
open={this.state.open}
|
||||||
title={Blockly.Msg.messages_newblockly_head}
|
title={Blockly.Msg.messages_newblockly_head}
|
||||||
content={''}
|
content={""}
|
||||||
onClose={this.toggleDialog}
|
onClose={this.toggleDialog}
|
||||||
onClick={this.toggleDialog}
|
onClick={this.toggleDialog}
|
||||||
button={Blockly.Msg.button_close}
|
button={Blockly.Msg.button_close}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<ReactMarkdown linkTarget="_blank">{Blockly.Msg.messages_newblockly_text}</ReactMarkdown>
|
<ReactMarkdown linkTarget="_blank">
|
||||||
|
{Blockly.Msg.messages_newblockly_text}
|
||||||
|
</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
style={{ marginTop: '20px' }}
|
style={{ marginTop: "20px" }}
|
||||||
classes={{ label: this.props.classes.label }}
|
classes={{ label: this.props.classes.label }}
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
size={'small'}
|
size={"small"}
|
||||||
value={true}
|
value={true}
|
||||||
checked={this.state.checked}
|
checked={this.state.checked}
|
||||||
onChange={(e) => this.onChange(e)}
|
onChange={(e) => this.onChange(e)}
|
||||||
@ -81,15 +85,18 @@ class HintTutorialExists extends Component {
|
|||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HintTutorialExists.propTypes = {
|
HintTutorialExists.propTypes = {
|
||||||
pageVisits: PropTypes.number.isRequired
|
pageVisits: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
pageVisits: state.general.pageVisits
|
pageVisits: state.general.pageVisits,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(HintTutorialExists));
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(withStyles(styles, { withTheme: true })(HintTutorialExists));
|
||||||
|
@ -15,9 +15,9 @@ class Instruction extends Component {
|
|||||||
var areRequirements = step.requirements && step.requirements.length > 0;
|
var areRequirements = step.requirements && step.requirements.length > 0;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h4" style={{ marginBottom: "5px" }}>
|
{/* <Typography variant="h4" style={{ marginBottom: "5px" }}>
|
||||||
{step.headline}
|
{step.headline}
|
||||||
</Typography>
|
</Typography> */}
|
||||||
<Typography style={isHardware ? {} : { marginBottom: "5px" }}>
|
<Typography style={isHardware ? {} : { marginBottom: "5px" }}>
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
className={"tutorial"}
|
className={"tutorial"}
|
||||||
|
@ -1,51 +1,49 @@
|
|||||||
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 { tutorialCheck, tutorialStep } from '../../actions/tutorialActions';
|
import { tutorialCheck, tutorialStep } from "../../actions/tutorialActions";
|
||||||
|
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from "react-router-dom";
|
||||||
|
|
||||||
import Compile from '../Workspace/Compile';
|
import Compile from "../Workspace/Compile";
|
||||||
import Dialog from '../Dialog';
|
import Dialog from "../Dialog";
|
||||||
|
|
||||||
import { checkXml } from '../../helpers/compareXml';
|
import { checkXml } from "../../helpers/compareXml";
|
||||||
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
|
|
||||||
import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
|
import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import * as Blockly from 'blockly'
|
import * as Blockly from "blockly";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
compile: {
|
compile: {
|
||||||
backgroundColor: theme.palette.button.compile,
|
backgroundColor: theme.palette.button.compile,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: theme.palette.button.compile,
|
backgroundColor: theme.palette.button.compile,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class SolutionCheck extends Component {
|
class SolutionCheck extends Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
open: false,
|
open: false,
|
||||||
msg: ''
|
msg: "",
|
||||||
}
|
};
|
||||||
|
|
||||||
toggleDialog = () => {
|
toggleDialog = () => {
|
||||||
if (this.state.open) {
|
if (this.state.open) {
|
||||||
this.setState({ open: false, msg: '' });
|
this.setState({ open: false, msg: "" });
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.setState({ open: !this.state });
|
this.setState({ open: !this.state });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
check = () => {
|
check = () => {
|
||||||
const tutorial = this.props.tutorial;
|
const tutorial = this.props.tutorial;
|
||||||
@ -53,7 +51,7 @@ class SolutionCheck extends Component {
|
|||||||
var msg = checkXml(step.xml, this.props.xml);
|
var msg = checkXml(step.xml, this.props.xml);
|
||||||
this.props.tutorialCheck(msg.type, step);
|
this.props.tutorialCheck(msg.type, step);
|
||||||
this.setState({ msg, open: true });
|
this.setState({ msg, open: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const steps = this.props.tutorial.steps;
|
const steps = this.props.tutorial.steps;
|
||||||
@ -62,68 +60,74 @@ class SolutionCheck extends Component {
|
|||||||
<Tooltip title={Blockly.Msg.tooltip_check_solution} arrow>
|
<Tooltip title={Blockly.Msg.tooltip_check_solution} arrow>
|
||||||
<IconButton
|
<IconButton
|
||||||
className={`solutionCheck ${this.props.classes.compile}`}
|
className={`solutionCheck ${this.props.classes.compile}`}
|
||||||
style={{ width: '40px', height: '40px', marginRight: '5px' }}
|
style={{ width: "40px", height: "40px", marginRight: "5px" }}
|
||||||
onClick={() => this.check()}
|
onClick={() => this.check()}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faClipboardCheck} size="l" />
|
<FontAwesomeIcon icon={faClipboardCheck} size="m" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
style={{ zIndex: 9999999 }}
|
style={{ zIndex: 9999999 }}
|
||||||
fullWidth
|
fullWidth
|
||||||
maxWidth={'sm'}
|
maxWidth={"sm"}
|
||||||
open={this.state.open}
|
open={this.state.open}
|
||||||
title={this.state.msg.type === 'error' ? 'Fehler' : 'Erfolg'}
|
title={this.state.msg.type === "error" ? "Fehler" : "Erfolg"}
|
||||||
content={this.state.msg.text}
|
content={this.state.msg.text}
|
||||||
onClose={this.toggleDialog}
|
onClose={this.toggleDialog}
|
||||||
onClick={this.toggleDialog}
|
onClick={this.toggleDialog}
|
||||||
button={Blockly.Msg.button_close}
|
button={Blockly.Msg.button_close}
|
||||||
>
|
>
|
||||||
{this.state.msg.type === 'success' ?
|
{this.state.msg.type === "success" ? (
|
||||||
<div style={{ marginTop: '20px', display: 'flex' }}>
|
<div style={{ marginTop: "20px", display: "flex" }}>
|
||||||
<Compile />
|
<Compile />
|
||||||
{this.props.activeStep === steps.length - 1 ?
|
{this.props.activeStep === steps.length - 1 ? (
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: '10px' }}
|
style={{ marginLeft: "10px" }}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => { this.toggleDialog(); this.props.history.push(`/tutorial/`) }}
|
onClick={() => {
|
||||||
|
this.toggleDialog();
|
||||||
|
this.props.history.push(`/tutorial/`);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{Blockly.Msg.button_tutorial_overview}
|
{Blockly.Msg.button_tutorial_overview}
|
||||||
</Button>
|
</Button>
|
||||||
:
|
) : (
|
||||||
<Button
|
<Button
|
||||||
style={{ marginLeft: '10px' }}
|
style={{ marginLeft: "10px" }}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={() => { this.toggleDialog(); this.props.tutorialStep(this.props.activeStep + 1) }}
|
onClick={() => {
|
||||||
|
this.toggleDialog();
|
||||||
|
this.props.tutorialStep(this.props.activeStep + 1);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{Blockly.Msg.button_next}
|
{Blockly.Msg.button_next}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
: null}
|
) : null}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SolutionCheck.propTypes = {
|
SolutionCheck.propTypes = {
|
||||||
tutorialCheck: PropTypes.func.isRequired,
|
tutorialCheck: PropTypes.func.isRequired,
|
||||||
tutorialStep: PropTypes.func.isRequired,
|
tutorialStep: PropTypes.func.isRequired,
|
||||||
activeStep: PropTypes.number.isRequired,
|
activeStep: PropTypes.number.isRequired,
|
||||||
xml: PropTypes.string.isRequired,
|
xml: PropTypes.string.isRequired,
|
||||||
tutorial: PropTypes.object.isRequired
|
tutorial: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
activeStep: state.tutorial.activeStep,
|
activeStep: state.tutorial.activeStep,
|
||||||
xml: state.workspace.code.xml,
|
xml: state.workspace.code.xml,
|
||||||
tutorial: state.tutorial.tutorials[0]
|
tutorial: state.tutorial.tutorials[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(withStyles(styles, { withTheme: true })(withRouter(SolutionCheck)));
|
export default connect(mapStateToProps, { tutorialCheck, tutorialStep })(
|
||||||
|
withStyles(styles, { withTheme: true })(withRouter(SolutionCheck))
|
||||||
|
);
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
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 { withRouter } from 'react-router-dom';
|
import { withRouter } from "react-router-dom";
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from "clsx";
|
||||||
|
|
||||||
// import tutorials from '../../data/tutorials';
|
// import tutorials from '../../data/tutorials';
|
||||||
|
|
||||||
import { fade } from '@material-ui/core/styles/colorManipulator';
|
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
|
|
||||||
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
|
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
stepper: {
|
stepper: {
|
||||||
width: 'calc(100% - 40px)',
|
width: "calc(100% - 40px)",
|
||||||
height: '40px',
|
height: "40px",
|
||||||
borderRadius: '25px',
|
borderRadius: "25px",
|
||||||
padding: '0 20px',
|
padding: "0 20px",
|
||||||
margin: '20px 0',
|
margin: "20px 0",
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
justifyContent: 'space-between'
|
justifyContent: "space-between",
|
||||||
},
|
},
|
||||||
stepperSuccess: {
|
stepperSuccess: {
|
||||||
backgroundColor: fade(theme.palette.primary.main, 0.6),
|
backgroundColor: fade(theme.palette.primary.main, 0.6),
|
||||||
@ -37,73 +37,147 @@ const styles = (theme) => ({
|
|||||||
backgroundColor: fade(theme.palette.secondary.main, 0.6),
|
backgroundColor: fade(theme.palette.secondary.main, 0.6),
|
||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
backgroundColor: 'transparent '
|
backgroundColor: "transparent ",
|
||||||
},
|
},
|
||||||
iconDivSuccess: {
|
iconDivSuccess: {
|
||||||
color: theme.palette.primary.main
|
color: theme.palette.primary.main,
|
||||||
},
|
},
|
||||||
iconDivError: {
|
iconDivError: {
|
||||||
color: theme.palette.error.dark
|
color: theme.palette.error.dark,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class StepperHorizontal extends Component {
|
class StepperHorizontal extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var tutorialId = this.props.tutorial._id;
|
var tutorialId = this.props.tutorial._id;
|
||||||
var status = this.props.status.filter(status => status._id === tutorialId)[0];
|
var status = this.props.status.filter(
|
||||||
|
(status) => status._id === tutorialId
|
||||||
|
)[0];
|
||||||
var tasks = status.tasks;
|
var tasks = status.tasks;
|
||||||
var error = tasks.filter(task => task.type === 'error').length > 0;
|
var error = tasks.filter((task) => task.type === "error").length > 0;
|
||||||
var success = tasks.filter(task => task.type === 'success').length / tasks.length;
|
var success =
|
||||||
var tutorialStatus = success === 1 ? 'Success' : error ? 'Error' : 'Other';
|
tasks.filter((task) => task.type === "success").length / tasks.length;
|
||||||
|
var tutorialStatus = success === 1 ? "Success" : error ? "Error" : "Other";
|
||||||
var title = this.props.tutorial.title;
|
var title = this.props.tutorial.title;
|
||||||
|
var activeStep = this.props.activeStep;
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: "relative" }}>
|
||||||
{error || success > 0 ?
|
{error || success > 0 ? (
|
||||||
<div style={{ zIndex: -1, width: error ? 'calc(100% - 40px)' : `calc(${success * 100}% - 40px)`, borderRadius: success === 1 || error ? '25px' : '25px 0 0 25px', position: 'absolute', margin: 0, left: 0 }} className={clsx(this.props.classes.stepper, error ? this.props.classes.stepperError : this.props.classes.stepperSuccess)}>
|
<div
|
||||||
</div>
|
style={{
|
||||||
: null}
|
zIndex: -1,
|
||||||
{success < 1 && !error ?
|
width: error
|
||||||
<div style={{ zIndex: -2, width: `calc(${(1 - success) * 100}% - 40px)`, borderRadius: success === 0 ? '25px' : '0px 25px 25px 0', position: 'absolute', margin: 0, right: 0 }} className={clsx(this.props.classes.stepper, this.props.classes.stepperOther)}>
|
? "calc(100% - 40px)"
|
||||||
</div>
|
: `calc(${success * 100}% - 40px)`,
|
||||||
: null}
|
borderRadius: success === 1 || error ? "25px" : "25px 0 0 25px",
|
||||||
|
position: "absolute",
|
||||||
|
margin: 0,
|
||||||
|
left: 0,
|
||||||
|
}}
|
||||||
|
className={clsx(
|
||||||
|
this.props.classes.stepper,
|
||||||
|
error
|
||||||
|
? this.props.classes.stepperError
|
||||||
|
: this.props.classes.stepperSuccess
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
) : null}
|
||||||
|
{success < 1 && !error ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
zIndex: -2,
|
||||||
|
width: `calc(${(1 - success) * 100}% - 40px)`,
|
||||||
|
borderRadius: success === 0 ? "25px" : "0px 25px 25px 0",
|
||||||
|
position: "absolute",
|
||||||
|
margin: 0,
|
||||||
|
right: 0,
|
||||||
|
}}
|
||||||
|
className={clsx(
|
||||||
|
this.props.classes.stepper,
|
||||||
|
this.props.classes.stepperOther
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
) : null}
|
||||||
<div className={this.props.classes.stepper}>
|
<div className={this.props.classes.stepper}>
|
||||||
<Button
|
<Button
|
||||||
disabled//={tutorialIndex === 0}
|
disabled //={tutorialIndex === 0}
|
||||||
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex - 1].id}`) }}
|
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex - 1].id}`) }}
|
||||||
>
|
>
|
||||||
{'<'}
|
{"<"}
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip style={{ display: 'flex', width: 'calc(100% - 64px - 64px)', justifyContent: 'center' }} title={title} arrow>
|
<Tooltip
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
width: "calc(100% - 64px - 64px)",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
title={title}
|
||||||
|
arrow
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
{tutorialStatus !== 'Other' ? <div className={tutorialStatus === 'Success' && success === 1 ? this.props.classes.iconDivSuccess : this.props.classes.iconDivError} style={{ margin: 'auto 10px auto 0' }}><FontAwesomeIcon className={this.props.classes.icon} icon={tutorialStatus === 'Success' ? faCheck : faTimes} /></div> : null}
|
{tutorialStatus !== "Other" ? (
|
||||||
<Typography variant='body2' style={{ fontWeight: 'bold', fontSize: '1.75em', margin: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'rgba(0, 0, 0, 0.54)' }}>{title}</Typography>
|
<div
|
||||||
|
className={
|
||||||
|
tutorialStatus === "Success" && success === 1
|
||||||
|
? this.props.classes.iconDivSuccess
|
||||||
|
: this.props.classes.iconDivError
|
||||||
|
}
|
||||||
|
style={{ margin: "auto 10px auto 0" }}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
className={this.props.classes.icon}
|
||||||
|
icon={tutorialStatus === "Success" ? faCheck : faTimes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
style={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: "1.75em",
|
||||||
|
margin: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
color: "rgba(0, 0, 0, 0.54)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
{title !== this.props.tutorial.steps[activeStep].headline
|
||||||
|
? ` - ${this.props.tutorial.steps[activeStep].headline}`
|
||||||
|
: null}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button
|
<Button
|
||||||
disabled//={tutorialIndex + 1 === tutorials.length}
|
disabled //={tutorialIndex + 1 === tutorials.length}
|
||||||
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex + 1].id}`) }}
|
//onClick={() => { this.props.history.push(`/tutorial/${tutorials[tutorialIndex + 1].id}`) }}
|
||||||
>
|
>
|
||||||
{'>'}
|
{">"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StepperHorizontal.propTypes = {
|
StepperHorizontal.propTypes = {
|
||||||
status: PropTypes.array.isRequired,
|
status: PropTypes.array.isRequired,
|
||||||
change: PropTypes.number.isRequired,
|
change: PropTypes.number.isRequired,
|
||||||
currentTutorialIndex: PropTypes.number.isRequired,
|
currentTutorialIndex: PropTypes.number.isRequired,
|
||||||
tutorial: PropTypes.object.isRequired
|
tutorial: PropTypes.object.isRequired,
|
||||||
|
activeStep: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
change: state.tutorial.change,
|
change: state.tutorial.change,
|
||||||
status: state.tutorial.status,
|
status: state.tutorial.status,
|
||||||
currentTutorialIndex: state.tutorial.currentIndex,
|
currentTutorialIndex: state.tutorial.currentIndex,
|
||||||
tutorial: state.tutorial.tutorials[0]
|
activeStep: state.tutorial.activeStep,
|
||||||
|
tutorial: state.tutorial.tutorials[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(withRouter(withStyles(styles, { withTheme: true })(StepperHorizontal)));
|
||||||
|
72
src/components/Workspace/AutoSave.js
Normal file
72
src/components/Workspace/AutoSave.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { workspaceName } from "../../actions/workspaceActions";
|
||||||
|
import SaveIcon from "../CodeEditor/SaveIcon";
|
||||||
|
|
||||||
|
const resetTimeout = (id, newID) => {
|
||||||
|
clearTimeout(id);
|
||||||
|
return newID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoSave extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
timeout: null,
|
||||||
|
value: "",
|
||||||
|
saved: false,
|
||||||
|
autosave: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
editValue = (value) => {
|
||||||
|
this.setState({
|
||||||
|
timeout: resetTimeout(
|
||||||
|
this.state.timeout,
|
||||||
|
setTimeout(this.saveValue, 400)
|
||||||
|
),
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
saveValue = () => {
|
||||||
|
this.setState({ ...this.state, saved: true });
|
||||||
|
localStorage.setItem("autoSaveXML", this.props.xml);
|
||||||
|
setTimeout(() => this.setState({ ...this.state, saved: false }), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
console.log(this.props.xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps.xml !== this.props.xml) {
|
||||||
|
this.editValue(this.props.xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SaveIcon loading={this.state.saved} autosave={this.props.autosave} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoSave.propTypes = {
|
||||||
|
xml: PropTypes.string.isRequired,
|
||||||
|
name: PropTypes.string,
|
||||||
|
workspaceName: PropTypes.func.isRequired,
|
||||||
|
setAutosave: PropTypes.func.isRequired,
|
||||||
|
autosave: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({
|
||||||
|
auto: state.general.autosave,
|
||||||
|
xml: state.workspace.code.xml,
|
||||||
|
name: state.workspace.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, { workspaceName })(AutoSave);
|
@ -16,10 +16,7 @@ import { faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
|
|||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import * as Blockly from "blockly/core";
|
import * as Blockly from "blockly/core";
|
||||||
import Copy from "../copy.svg";
|
import Copy from "../copy.svg";
|
||||||
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 MuiDrawer from "@material-ui/core/Drawer";
|
import MuiDrawer from "@material-ui/core/Drawer";
|
||||||
import Dialog from "../Dialog";
|
import Dialog from "../Dialog";
|
||||||
|
|
||||||
@ -71,15 +68,12 @@ class Compile extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {}
|
||||||
Prism.highlightAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
componentDidUpdate(props) {
|
||||||
if (props.name !== this.props.name) {
|
if (props.name !== this.props.name) {
|
||||||
this.setState({ name: this.props.name });
|
this.setState({ name: this.props.name });
|
||||||
}
|
}
|
||||||
Prism.highlightAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compile = () => {
|
compile = () => {
|
||||||
@ -196,7 +190,7 @@ class Compile extends Component {
|
|||||||
className={`compileBlocks ${this.props.classes.iconButton}`}
|
className={`compileBlocks ${this.props.classes.iconButton}`}
|
||||||
onClick={() => this.compile()}
|
onClick={() => this.compile()}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faClipboardCheck} size="l" />
|
<FontAwesomeIcon icon={faClipboardCheck} size="m" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
|
@ -1,99 +1,110 @@
|
|||||||
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 { workspaceName } from '../../actions/workspaceActions';
|
import { workspaceName } from "../../actions/workspaceActions";
|
||||||
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
|
||||||
import { faCopy } from "@fortawesome/free-solid-svg-icons";
|
import { faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import * as Blockly from 'blockly/core';
|
import * as Blockly from "blockly/core";
|
||||||
import Snackbar from '../Snackbar';
|
import Snackbar from "../Snackbar";
|
||||||
|
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
backdrop: {
|
backdrop: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
color: '#fff',
|
color: "#fff",
|
||||||
|
},
|
||||||
|
iconButton: {
|
||||||
|
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,
|
||||||
},
|
},
|
||||||
iconButton: {
|
},
|
||||||
backgroundColor: theme.palette.primary.main,
|
button: {
|
||||||
color: theme.palette.primary.contrastText,
|
backgroundColor: theme.palette.button.copycode,
|
||||||
width: '40px',
|
color: theme.palette.primary.contrastText,
|
||||||
height: '40px',
|
"&:hover": {
|
||||||
'&:hover': {
|
backgroundColor: theme.palette.button.copycode,
|
||||||
backgroundColor: theme.palette.primary.main,
|
color: theme.palette.primary.contrastText,
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
button: {
|
},
|
||||||
backgroundColor: theme.palette.button.copycode,
|
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: theme.palette.button.copycode,
|
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
class CopyCode extends Component {
|
class CopyCode extends Component {
|
||||||
|
constructor(props) {
|
||||||
constructor(props) {
|
super(props);
|
||||||
super(props);
|
this.state = {
|
||||||
this.state = {
|
snackbar: false,
|
||||||
snackbar: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
copyCode = () => {
|
|
||||||
navigator.clipboard.writeText(this.props.arduino)
|
|
||||||
this.setState({ snackbar: true, type: 'success', key: Date.now(), message: Blockly.Msg.messages_copy_code });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div style={{}}>
|
|
||||||
{this.props.iconButton ?
|
|
||||||
<Tooltip title={Blockly.Msg.tooltip_copy_code} arrow style={{ marginRight: '5px' }}>
|
|
||||||
<IconButton
|
|
||||||
className={`copyCode ${this.props.classes.iconButton}`}
|
|
||||||
onClick={() => this.copyCode()}
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faCopy} size="l" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
:
|
|
||||||
<Button style={{ float: 'right', color: 'white' }} variant="contained" className={this.props.classes.button} onClick={() => this.copyCode()}>
|
|
||||||
<FontAwesomeIcon icon={faCopy} style={{ marginRight: '5px' }} /> Code kopieren
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
<Snackbar
|
|
||||||
open={this.state.snackbar}
|
|
||||||
message={this.state.message}
|
|
||||||
type={this.state.type}
|
|
||||||
key={this.state.key}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div >
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
copyCode = () => {
|
||||||
|
navigator.clipboard.writeText(this.props.arduino);
|
||||||
|
this.setState({
|
||||||
|
snackbar: true,
|
||||||
|
type: "success",
|
||||||
|
key: Date.now(),
|
||||||
|
message: Blockly.Msg.messages_copy_code,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={{}}>
|
||||||
|
{this.props.iconButton ? (
|
||||||
|
<Tooltip
|
||||||
|
title={Blockly.Msg.tooltip_copy_code}
|
||||||
|
arrow
|
||||||
|
style={{ marginRight: "5px" }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
className={`copyCode ${this.props.classes.iconButton}`}
|
||||||
|
onClick={() => this.copyCode()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCopy} size="m" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
style={{ float: "right", color: "white" }}
|
||||||
|
variant="contained"
|
||||||
|
className={this.props.classes.button}
|
||||||
|
onClick={() => this.copyCode()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faCopy} style={{ marginRight: "5px" }} />{" "}
|
||||||
|
Code kopieren
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Snackbar
|
||||||
|
open={this.state.snackbar}
|
||||||
|
message={this.state.message}
|
||||||
|
type={this.state.type}
|
||||||
|
key={this.state.key}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyCode.propTypes = {
|
CopyCode.propTypes = {
|
||||||
arduino: PropTypes.string.isRequired,
|
arduino: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
workspaceName: PropTypes.func.isRequired
|
workspaceName: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
arduino: state.workspace.code.arduino,
|
arduino: state.workspace.code.arduino,
|
||||||
name: state.workspace.name
|
name: state.workspace.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, { workspaceName })(
|
||||||
export default connect(mapStateToProps, { workspaceName })(withStyles(styles, { withTheme: true })(CopyCode));
|
withStyles(styles, { withTheme: true })(CopyCode)
|
||||||
|
);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
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 * as Blockly from 'blockly/core';
|
import * as Blockly from "blockly/core";
|
||||||
|
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from "file-saver";
|
||||||
|
|
||||||
import { detectWhitespacesAndReturnReadableResult } from '../../helpers/whitespace';
|
import { detectWhitespacesAndReturnReadableResult } from "../../helpers/whitespace";
|
||||||
|
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
|
||||||
import { faCamera } from "@fortawesome/free-solid-svg-icons";
|
import { faCamera } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
@ -19,18 +19,16 @@ const styles = (theme) => ({
|
|||||||
button: {
|
button: {
|
||||||
backgroundColor: theme.palette.primary.main,
|
backgroundColor: theme.palette.primary.main,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
width: '40px',
|
width: "40px",
|
||||||
height: '40px',
|
height: "40px",
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
backgroundColor: theme.palette.primary.main,
|
backgroundColor: theme.palette.primary.main,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
class Screenshot extends Component {
|
class Screenshot extends Component {
|
||||||
|
|
||||||
getSvg = () => {
|
getSvg = () => {
|
||||||
const workspace = Blockly.getMainWorkspace();
|
const workspace = Blockly.getMainWorkspace();
|
||||||
var canvas = workspace.svgBlockCanvas_.cloneNode(true);
|
var canvas = workspace.svgBlockCanvas_.cloneNode(true);
|
||||||
@ -39,10 +37,12 @@ class Screenshot extends Component {
|
|||||||
canvas.removeAttribute("transform");
|
canvas.removeAttribute("transform");
|
||||||
// does not work in react
|
// does not work in react
|
||||||
// var cssContent = Blockly.Css.CONTENT.join('');
|
// var cssContent = Blockly.Css.CONTENT.join('');
|
||||||
var cssContent = '';
|
var cssContent = "";
|
||||||
for (var i = 0; i < document.getElementsByTagName('style').length; i++) {
|
for (var i = 0; i < document.getElementsByTagName("style").length; i++) {
|
||||||
if (/^blockly.*$/.test(document.getElementsByTagName('style')[i].id)) {
|
if (/^blockly.*$/.test(document.getElementsByTagName("style")[i].id)) {
|
||||||
cssContent += document.getElementsByTagName('style')[i].firstChild.data.replace(/\..* \./g, '.');
|
cssContent += document
|
||||||
|
.getElementsByTagName("style")
|
||||||
|
[i].firstChild.data.replace(/\..* \./g, ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ensure that fill-opacity is 1, because there cannot be a replacing
|
// ensure that fill-opacity is 1, because there cannot be a replacing
|
||||||
@ -56,19 +56,24 @@ class Screenshot extends Component {
|
|||||||
.blocklyPathLight {
|
.blocklyPathLight {
|
||||||
display: flex;
|
display: flex;
|
||||||
} `;
|
} `;
|
||||||
var css = '<defs><style type="text/css" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[' + cssContent + ']]></style></defs>';
|
var css =
|
||||||
var bbox = document.getElementsByClassName("blocklyBlockCanvas")[0].getBBox();
|
'<defs><style type="text/css" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[' +
|
||||||
|
cssContent +
|
||||||
|
"]]></style></defs>";
|
||||||
|
var bbox = document
|
||||||
|
.getElementsByClassName("blocklyBlockCanvas")[0]
|
||||||
|
.getBBox();
|
||||||
var content = new XMLSerializer().serializeToString(canvas);
|
var content = new XMLSerializer().serializeToString(canvas);
|
||||||
var xml = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
var xml = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
width="${bbox.width}" height="${bbox.height}" viewBox="${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}">
|
width="${bbox.width}" height="${bbox.height}" viewBox="${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}">
|
||||||
${css}">${content}</svg>`;
|
${css}">${content}</svg>`;
|
||||||
var fileName = detectWhitespacesAndReturnReadableResult(this.props.name);
|
var fileName = detectWhitespacesAndReturnReadableResult(this.props.name);
|
||||||
// this.props.workspaceName(this.state.name);
|
// this.props.workspaceName(this.state.name);
|
||||||
fileName = `${fileName}.svg`
|
fileName = `${fileName}.svg`;
|
||||||
var blob = new Blob([xml], { type: 'image/svg+xml;base64' });
|
var blob = new Blob([xml], { type: "image/svg+xml;base64" });
|
||||||
saveAs(blob, fileName);
|
saveAs(blob, fileName);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@ -83,15 +88,18 @@ class Screenshot extends Component {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Screenshot.propTypes = {
|
Screenshot.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
name: state.workspace.name,
|
name: state.workspace.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(withStyles(styles, { withTheme: true })(Screenshot));
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(withStyles(styles, { withTheme: true })(Screenshot));
|
||||||
|
@ -1,101 +1,113 @@
|
|||||||
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 WorkspaceName from './WorkspaceName';
|
|
||||||
import SaveProject from './SaveProject';
|
|
||||||
import Compile from './Compile';
|
|
||||||
import SolutionCheck from '../Tutorial/SolutionCheck';
|
|
||||||
import DownloadProject from './DownloadProject';
|
|
||||||
import OpenProject from './OpenProject';
|
|
||||||
import Screenshot from './Screenshot';
|
|
||||||
import ShareProject from './ShareProject';
|
|
||||||
import ResetWorkspace from './ResetWorkspace';
|
|
||||||
import DeleteProject from './DeleteProject';
|
|
||||||
import CopyCode from './CopyCode';
|
|
||||||
|
|
||||||
|
import WorkspaceName from "./WorkspaceName";
|
||||||
|
import SaveProject from "./SaveProject";
|
||||||
|
import Compile from "./Compile";
|
||||||
|
import SolutionCheck from "../Tutorial/SolutionCheck";
|
||||||
|
import DownloadProject from "./DownloadProject";
|
||||||
|
import OpenProject from "./OpenProject";
|
||||||
|
import Screenshot from "./Screenshot";
|
||||||
|
import ShareProject from "./ShareProject";
|
||||||
|
import ResetWorkspace from "./ResetWorkspace";
|
||||||
|
import DeleteProject from "./DeleteProject";
|
||||||
|
import CopyCode from "./CopyCode";
|
||||||
|
import AutoSave from "./AutoSave";
|
||||||
class WorkspaceFunc extends Component {
|
class WorkspaceFunc extends Component {
|
||||||
|
componentDidUpdate() {
|
||||||
|
console.log(this.props.autosave);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={{ width: 'max-content', display: 'flex' }}>
|
<div
|
||||||
|
style={{ width: "max-content", display: "flex", alignItems: "center" }}
|
||||||
{!this.props.assessment ?
|
>
|
||||||
|
{!this.props.assessment & !this.props.multiple ? <AutoSave /> : null}
|
||||||
|
{!this.props.assessment ? (
|
||||||
<WorkspaceName
|
<WorkspaceName
|
||||||
style={{ marginRight: '5px' }}
|
style={{ marginRight: "5px" }}
|
||||||
multiple={this.props.multiple}
|
multiple={this.props.multiple}
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
projectType={this.props.projectType}
|
projectType={this.props.projectType}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{this.props.assessment ?
|
{this.props.assessment ? (
|
||||||
<SolutionCheck />
|
<SolutionCheck />
|
||||||
: !this.props.multiple ?
|
) : !this.props.multiple ? (
|
||||||
<Compile iconButton />
|
<Compile iconButton />
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{!this.props.multiple ?
|
{!this.props.multiple ? <CopyCode iconButton /> : null}
|
||||||
<CopyCode iconButton />
|
|
||||||
: null}
|
|
||||||
|
|
||||||
|
{this.props.user && !this.props.multiple ? (
|
||||||
{this.props.user && !this.props.multiple ?
|
|
||||||
<SaveProject
|
<SaveProject
|
||||||
style={{ marginRight: '5px' }}
|
style={{ marginRight: "5px" }}
|
||||||
projectType={this.props.projectType}
|
projectType={this.props.projectType}
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{!this.props.multiple ?
|
{!this.props.multiple ? (
|
||||||
<DownloadProject style={{ marginRight: '5px' }} />
|
<DownloadProject style={{ marginRight: "5px" }} />
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
|
{!this.props.assessment && !this.props.multiple ? (
|
||||||
{!this.props.assessment && !this.props.multiple ?
|
|
||||||
<OpenProject
|
<OpenProject
|
||||||
style={{ marginRight: '5px' }}
|
style={{ marginRight: "5px" }}
|
||||||
assessment={this.props.assessment}
|
assessment={this.props.assessment}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{!this.props.assessment && !this.props.multiple ?
|
{!this.props.assessment && !this.props.multiple ? (
|
||||||
<Screenshot style={{ marginRight: '5px' }} />
|
<Screenshot style={{ marginRight: "5px" }} />
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{this.props.projectType !== 'gallery' && !this.props.assessment ?
|
{this.props.projectType !== "gallery" && !this.props.assessment ? (
|
||||||
<ShareProject
|
<ShareProject
|
||||||
style={{ marginRight: '5px' }}
|
style={{ marginRight: "5px" }}
|
||||||
multiple={this.props.multiple}
|
multiple={this.props.multiple}
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
projectType={this.props.projectType}
|
projectType={this.props.projectType}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{!this.props.multiple ?
|
{!this.props.multiple ? (
|
||||||
<ResetWorkspace style={this.props.projectType === 'project' || this.props.projectType === 'gallery' ? { marginRight: '5px' } : null}
|
<ResetWorkspace
|
||||||
|
style={
|
||||||
|
this.props.projectType === "project" ||
|
||||||
|
this.props.projectType === "gallery"
|
||||||
|
? { marginRight: "5px" }
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
{!this.props.assessment && (this.props.projectType === 'project' || this.props.projectType === 'gallery') && this.props.user && this.props.user.email === this.props.project.creator ?
|
{!this.props.assessment &&
|
||||||
|
(this.props.projectType === "project" ||
|
||||||
|
this.props.projectType === "gallery") &&
|
||||||
|
this.props.user &&
|
||||||
|
this.props.user.email === this.props.project.creator ? (
|
||||||
<DeleteProject
|
<DeleteProject
|
||||||
project={this.props.project}
|
project={this.props.project}
|
||||||
projectType={this.props.projectType}
|
projectType={this.props.projectType}
|
||||||
/>
|
/>
|
||||||
: null}
|
) : null}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkspaceFunc.propTypes = {
|
WorkspaceFunc.propTypes = {
|
||||||
user: PropTypes.object
|
user: PropTypes.object,
|
||||||
|
autosave: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
user: state.auth.user
|
user: state.auth.user,
|
||||||
|
autosave: state.workspace.autosave,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(WorkspaceFunc);
|
export default connect(mapStateToProps, null)(WorkspaceFunc);
|
||||||
|
@ -1,51 +1,50 @@
|
|||||||
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 { workspaceName } from '../../actions/workspaceActions';
|
import { workspaceName } from "../../actions/workspaceActions";
|
||||||
import { setDescription, updateProject } from '../../actions/projectActions';
|
import { setDescription, updateProject } from "../../actions/projectActions";
|
||||||
|
|
||||||
import Snackbar from '../Snackbar';
|
import Snackbar from "../Snackbar";
|
||||||
import Dialog from '../Dialog';
|
import Dialog from "../Dialog";
|
||||||
|
|
||||||
import withWidth, { isWidthDown } from '@material-ui/core/withWidth';
|
import withWidth, { isWidthDown } from "@material-ui/core/withWidth";
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from "@material-ui/core/Button";
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
|
||||||
import { faPen } from "@fortawesome/free-solid-svg-icons";
|
import { faPen } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import * as Blockly from 'blockly/core'
|
import * as Blockly from "blockly/core";
|
||||||
|
|
||||||
const styles = (theme) => ({
|
const styles = (theme) => ({
|
||||||
workspaceName: {
|
workspaceName: {
|
||||||
|
minHeight: "40px",
|
||||||
backgroundColor: theme.palette.secondary.main,
|
backgroundColor: theme.palette.secondary.main,
|
||||||
borderRadius: '25px',
|
borderRadius: "25px",
|
||||||
display: 'inline-flex',
|
display: "inline-flex",
|
||||||
cursor: 'pointer',
|
cursor: "pointer",
|
||||||
'&:hover': {
|
"&:hover": {
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
class WorkspaceName extends Component {
|
class WorkspaceName extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.inputRef = React.createRef();
|
this.inputRef = React.createRef();
|
||||||
this.state = {
|
this.state = {
|
||||||
title: '',
|
title: "",
|
||||||
content: '',
|
content: "",
|
||||||
open: false,
|
open: false,
|
||||||
name: props.name,
|
name: props.name,
|
||||||
description: props.description,
|
description: props.description,
|
||||||
snackbar: false,
|
snackbar: false,
|
||||||
type: '',
|
type: "",
|
||||||
key: '',
|
key: "",
|
||||||
message: ''
|
message: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,47 +58,100 @@ class WorkspaceName extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleDialog = () => {
|
toggleDialog = () => {
|
||||||
this.setState({ open: !this.state, title: '', content: '' });
|
this.setState({ open: !this.state, title: "", content: "" });
|
||||||
}
|
};
|
||||||
|
|
||||||
setFileName = (e) => {
|
setFileName = (e) => {
|
||||||
this.setState({ name: e.target.value });
|
this.setState({ name: e.target.value });
|
||||||
}
|
};
|
||||||
|
|
||||||
setDescription = (e) => {
|
setDescription = (e) => {
|
||||||
this.setState({ description: e.target.value });
|
this.setState({ description: e.target.value });
|
||||||
}
|
};
|
||||||
|
|
||||||
renameWorkspace = () => {
|
renameWorkspace = () => {
|
||||||
this.props.workspaceName(this.state.name);
|
this.props.workspaceName(this.state.name);
|
||||||
this.toggleDialog();
|
this.toggleDialog();
|
||||||
if (this.props.projectType === 'project' || this.props.projectType === 'gallery' || this.state.projectType === 'gallery') {
|
if (
|
||||||
if (this.props.projectType === 'gallery' || this.state.projectType === 'gallery') {
|
this.props.projectType === "project" ||
|
||||||
|
this.props.projectType === "gallery" ||
|
||||||
|
this.state.projectType === "gallery"
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
this.props.projectType === "gallery" ||
|
||||||
|
this.state.projectType === "gallery"
|
||||||
|
) {
|
||||||
this.props.setDescription(this.state.description);
|
this.props.setDescription(this.state.description);
|
||||||
}
|
}
|
||||||
if (this.state.projectType === 'gallery') {
|
if (this.state.projectType === "gallery") {
|
||||||
this.saveGallery();
|
this.saveGallery();
|
||||||
} else {
|
} else {
|
||||||
this.props.updateProject(this.props.projectType, this.props.project._id);
|
this.props.updateProject(
|
||||||
|
this.props.projectType,
|
||||||
|
this.props.project._id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({ snackbar: true, type: 'success', key: Date.now(), message: `${Blockly.Msg.messages_rename_success_01} ${this.state.name} ${Blockly.Msg.messages_rename_success_02}` });
|
this.setState({
|
||||||
|
snackbar: true,
|
||||||
|
type: "success",
|
||||||
|
key: Date.now(),
|
||||||
|
message: `${Blockly.Msg.messages_rename_success_01} ${this.state.name} ${Blockly.Msg.messages_rename_success_02}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={this.props.style}>
|
<div style={this.props.style}>
|
||||||
<Tooltip title={`${Blockly.Msg.tooltip_project_title} ${this.props.name ? `: ${this.props.name}` : ''}`} arrow style={{ height: '100%' }}>
|
<Tooltip
|
||||||
|
title={`${Blockly.Msg.tooltip_project_title} ${
|
||||||
|
this.props.name ? `: ${this.props.name}` : ""
|
||||||
|
}`}
|
||||||
|
arrow
|
||||||
|
style={{ height: "100%" }}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={this.props.classes.workspaceName}
|
className={this.props.classes.workspaceName}
|
||||||
onClick={() => { if (this.props.multiple) { this.props.workspaceName(this.props.project.title); if (this.props.projectType === 'gallery') { this.props.setDescription(this.props.project.description); } } this.setState({ open: true, title: this.props.projectType === 'gallery' ? 'Projektdaten ändern' : this.props.projectType === 'project' ? 'Projekt umbenennen' : 'Projekt benennen', content: this.props.projectType === 'gallery' ? 'Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf \'Eingabe\'.' : 'Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf \'Eingabe\'.' }) }}
|
onClick={() => {
|
||||||
|
if (this.props.multiple) {
|
||||||
|
this.props.workspaceName(this.props.project.title);
|
||||||
|
if (this.props.projectType === "gallery") {
|
||||||
|
this.props.setDescription(this.props.project.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
open: true,
|
||||||
|
title:
|
||||||
|
this.props.projectType === "gallery"
|
||||||
|
? "Projektdaten ändern"
|
||||||
|
: this.props.projectType === "project"
|
||||||
|
? "Projekt umbenennen"
|
||||||
|
: "Projekt benennen",
|
||||||
|
content:
|
||||||
|
this.props.projectType === "gallery"
|
||||||
|
? "Bitte gib einen Titel und eine Beschreibung für das Galerie-Projekt ein und bestätige die Angaben mit einem Klick auf 'Eingabe'."
|
||||||
|
: "Bitte gib einen Namen für das Projekt ein und bestätige diesen mit einem Klick auf 'Eingabe'.",
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{this.props.name && !isWidthDown(this.props.projectType === 'project' || this.props.projectType === 'gallery' ? 'xl' : 'xs', this.props.width) ?
|
{this.props.name &&
|
||||||
<Typography style={{ margin: 'auto -3px auto 12px' }}>{this.props.name}</Typography>
|
!isWidthDown(
|
||||||
: null}
|
this.props.projectType === "project" ||
|
||||||
<div style={{ width: '40px', display: 'flex' }}>
|
this.props.projectType === "gallery"
|
||||||
<FontAwesomeIcon icon={faPen} style={{ height: '18px', width: '18px', margin: 'auto' }} />
|
? "xl"
|
||||||
|
: "xs",
|
||||||
|
this.props.width
|
||||||
|
) ? (
|
||||||
|
<Typography style={{ margin: "auto -3px auto 12px" }}>
|
||||||
|
{this.props.name}
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
<div style={{ width: "40px", display: "flex" }}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faPen}
|
||||||
|
style={{ height: "18px", width: "18px", margin: "auto" }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -114,23 +166,69 @@ class WorkspaceName extends Component {
|
|||||||
open={this.state.open}
|
open={this.state.open}
|
||||||
title={this.state.title}
|
title={this.state.title}
|
||||||
content={this.state.content}
|
content={this.state.content}
|
||||||
onClose={() => { this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description }); }}
|
onClose={() => {
|
||||||
onClick={() => { this.toggleDialog(); this.setState({ name: this.props.name, description: this.props.description }); }}
|
this.toggleDialog();
|
||||||
button={'Abbrechen'}
|
this.setState({
|
||||||
|
name: this.props.name,
|
||||||
|
description: this.props.description,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
this.toggleDialog();
|
||||||
|
this.setState({
|
||||||
|
name: this.props.name,
|
||||||
|
description: this.props.description,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
button={"Abbrechen"}
|
||||||
>
|
>
|
||||||
<div style={{ marginTop: '10px' }}>
|
<div style={{ marginTop: "10px" }}>
|
||||||
{this.props.projectType === 'gallery' || this.state.projectType === 'gallery' ?
|
{this.props.projectType === "gallery" ||
|
||||||
|
this.state.projectType === "gallery" ? (
|
||||||
<div>
|
<div>
|
||||||
<TextField autoFocus placeholder={this.state.saveXml ? 'Dateiname' : 'Projekttitel'} value={this.state.name} onChange={this.setFileName} style={{ marginBottom: '10px' }} />
|
<TextField
|
||||||
<TextField fullWidth multiline placeholder={'Projektbeschreibung'} value={this.state.description} onChange={this.setDescription} style={{ marginBottom: '10px' }} />
|
autoFocus
|
||||||
|
placeholder={
|
||||||
|
this.state.saveXml ? "Dateiname" : "Projekttitel"
|
||||||
|
}
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.setFileName}
|
||||||
|
style={{ marginBottom: "10px" }}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
placeholder={"Projektbeschreibung"}
|
||||||
|
value={this.state.description}
|
||||||
|
onChange={this.setDescription}
|
||||||
|
style={{ marginBottom: "10px" }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
: <TextField autoFocus placeholder={this.state.saveXml ? 'Dateiname' : 'Projekttitel'} value={this.state.name} onChange={this.setFileName} style={{ marginRight: '10px' }} />}
|
) : (
|
||||||
<Button disabled={!this.state.name} variant='contained' color='primary' onClick={() => { this.renameWorkspace(); this.toggleDialog(); }}>Eingabe</Button>
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
placeholder={this.state.saveXml ? "Dateiname" : "Projekttitel"}
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.setFileName}
|
||||||
|
style={{ marginRight: "10px" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
disabled={!this.state.name}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
this.renameWorkspace();
|
||||||
|
this.toggleDialog();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Eingabe
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkspaceName.propTypes = {
|
WorkspaceName.propTypes = {
|
||||||
@ -142,10 +240,14 @@ WorkspaceName.propTypes = {
|
|||||||
message: PropTypes.object.isRequired,
|
message: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state) => ({
|
||||||
name: state.workspace.name,
|
name: state.workspace.name,
|
||||||
description: state.project.description,
|
description: state.project.description,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, { workspaceName, setDescription, updateProject })(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceName)));
|
export default connect(mapStateToProps, {
|
||||||
|
workspaceName,
|
||||||
|
setDescription,
|
||||||
|
updateProject,
|
||||||
|
})(withStyles(styles, { withTheme: true })(withWidth()(WorkspaceName)));
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 763 KiB After Width: | Height: | Size: 229 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 14 KiB |
14
src/data/arduinoExamples.js
Normal file
14
src/data/arduinoExamples.js
Normal file
File diff suppressed because one or more lines are too long
102
src/data/versions.js
Normal file
102
src/data/versions.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
export const LibraryVersions = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
version: "1.4.2",
|
||||||
|
library: "sensebox/SenseBoxMCU-Lib",
|
||||||
|
link: "https://github.com/sensebox/SenseBoxMCU-Lib",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.12",
|
||||||
|
library: "sparkfun/SparkFun_SCD30_Arduino_Library",
|
||||||
|
},
|
||||||
|
{ version: "1.2.3", library: "adafruit/Adafruit-GFX-Library" },
|
||||||
|
{
|
||||||
|
version: "2.1.2",
|
||||||
|
library: "adafruit/Adafruit_BME280_Library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "2.1.0",
|
||||||
|
library: "adafruit/Adafruit_BMP280_Library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.1.1",
|
||||||
|
library: "adafruit/Adafruit_BME680",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "2.0.1",
|
||||||
|
library: "adafruit/Adafruit_BMP3XX",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "2.0.0",
|
||||||
|
library: "adafruit/Adafruit_HDC1000_Library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.7.1",
|
||||||
|
library: "adafruit/Adafruit_BusIO",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.6",
|
||||||
|
library: "adafruit/Adafruit_NeoPixel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.1.2",
|
||||||
|
library: "adafruit/Adafruit_SSD1306",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.2",
|
||||||
|
library: "adafruit/Adafruit_Sensor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "3.8.0",
|
||||||
|
library: "milesburton/Arduino-Temperature-Control-Library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.5.0",
|
||||||
|
library: "arduino-libraries/ArduinoBearSSL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.3.4",
|
||||||
|
library: "arduino-libraries/ArduinoECCX08",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "2.0.0",
|
||||||
|
library: "arduino-libraries/ArduinoECCX08", //todo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "0.7.1",
|
||||||
|
library: "cmaglie/FlashStorage",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.5.1",
|
||||||
|
library: "matthijskooijman/arduino-lmic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "3.0.1",
|
||||||
|
library: "thesolarnomad/lora-serialization ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "todo",
|
||||||
|
library: "TSL45xxx",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "2.3.4",
|
||||||
|
library: "teensy/td_libs_OneWire.html",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.0",
|
||||||
|
library: "watterott/Arduino-Libs/tree/master/RV8523",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.0",
|
||||||
|
library: "sensebox/SDS011-select-serial ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.0",
|
||||||
|
library: "Lucas-Steinmann/SSD1306-Plot-Library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: "1.0.0",
|
||||||
|
library: "senseBoxIO",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user