1
- import React , { Component } from "react" ;
1
+ import React , { useEffect , useRef , useState } from "react" ;
2
2
import PropTypes from "prop-types" ;
3
- import { connect } from "react-redux" ;
3
+ import { useSelector , useDispatch } from "react-redux" ;
4
4
import { onChangeWorkspace , clearStats } from "../../actions/workspaceActions" ;
5
5
6
6
import BlocklyComponent from "./BlocklyComponent" ;
@@ -13,144 +13,162 @@ import { ZoomToFitControl } from "@blockly/zoom-to-fit";
13
13
import { initialXml } from "./initialXml.js" ;
14
14
import { getMaxInstances } from "./helpers/maxInstances" ;
15
15
import { Backpack } from "@blockly/workspace-backpack" ;
16
+ import Snackbar from "../Snackbar" ;
16
17
17
- class BlocklyWindow extends Component {
18
- constructor ( props ) {
19
- super ( props ) ;
20
- this . simpleWorkspace = React . createRef ( ) ;
21
- }
18
+ const BlocklyWindow = ( {
19
+ blockDisabled,
20
+ readOnly = false ,
21
+ trashcan = true ,
22
+ zoomControls = true ,
23
+ grid : gridEnabled = true ,
24
+ move : moveEnabled = true ,
25
+ blocklyCSS,
26
+ svg,
27
+ initialXml : propsInitialXml ,
28
+ } ) => {
29
+ const dispatch = useDispatch ( ) ;
30
+ const renderer = useSelector ( ( state ) => state . general . renderer ) ;
31
+ const sounds = useSelector ( ( state ) => state . general . sounds ) ;
32
+ const language = useSelector ( ( state ) => state . general . language ) ;
33
+ const selectedBoard = useSelector ( ( state ) => state . board . board ) ;
22
34
23
- componentDidMount ( ) {
35
+ const simpleWorkspaceRef = useRef ( null ) ;
36
+ const backpackRef = useRef ( null ) ;
37
+
38
+ const [ snackbarOpen , setSnackbarOpen ] = useState ( false ) ;
39
+ const [ snackInfo , setSnackInfo ] = useState ( {
40
+ type : "success" ,
41
+ message : "" ,
42
+ } ) ;
43
+ const onBackPackChange = ( event ) => {
44
+ if ( event . type !== "backpack_change" ) {
45
+ return ;
46
+ }
47
+ setSnackInfo ( {
48
+ type : "success" ,
49
+ message : "Backpack changed" ,
50
+ } ) ;
51
+ setSnackbarOpen ( true ) ;
52
+ } ;
53
+ // Mount: setup workspace, listeners, zoom & backpack
54
+ useEffect ( ( ) => {
24
55
const workspace = Blockly . getMainWorkspace ( ) ;
25
- this . props . onChangeWorkspace ( { } ) ;
26
- this . props . clearStats ( ) ;
56
+
57
+ dispatch ( onChangeWorkspace ( { } ) ) ;
58
+ dispatch ( clearStats ( ) ) ;
59
+
60
+ // Disable orphan blocks on every change
27
61
workspace . addChangeListener ( Blockly . Events . disableOrphans ) ;
28
62
workspace . addChangeListener ( ( event ) => {
29
- this . props . onChangeWorkspace ( event ) ;
30
-
31
- // switch on that a block is displayed disabled or not depending on whether it is correctly connected
32
- // for SVG display, a deactivated block in the display is undesirable
33
- if ( this . props . blockDisabled ) {
63
+ dispatch ( onChangeWorkspace ( event ) ) ;
64
+ if ( blockDisabled ) {
34
65
Blockly . Events . disableOrphans ( event ) ;
35
66
}
36
67
} ) ;
68
+
69
+ // Resize & zoom-to-fit
37
70
Blockly . svgResize ( workspace ) ;
38
71
const zoomToFit = new ZoomToFitControl ( workspace ) ;
39
72
zoomToFit . init ( ) ;
40
73
41
- // Initialize plugin.
42
74
const backpack = new Backpack ( workspace ) ;
43
-
44
75
backpack . init ( ) ;
45
- }
76
+ workspace . addChangeListener ( onBackPackChange ) ;
77
+ } , [ dispatch , blockDisabled ] ) ;
46
78
47
- componentDidUpdate ( props ) {
79
+ // Update on board, initialXml or language change
80
+ useEffect ( ( ) => {
48
81
const workspace = Blockly . getMainWorkspace ( ) ;
49
- var xml = this . props . initialXml ;
50
- if ( props . selectedBoard !== this . props . selectedBoard ) {
51
- xml = localStorage . getItem ( "autoSaveXML" ) ;
52
- // change board
53
- if ( ! xml ) xml = initialXml ;
54
- var xmlDom = Blockly . utils . xml . textToDom ( xml ) ;
55
- Blockly . Xml . clearWorkspaceAndLoadFromXml ( xmlDom , workspace ) ;
82
+ let xml = propsInitialXml ;
83
+
84
+ // Board gewechselt?
85
+ if ( selectedBoard ) {
86
+ const saved = localStorage . getItem ( "autoSaveXML" ) ;
87
+ xml = saved || initialXml ;
88
+ const dom = Blockly . utils . xml . textToDom ( xml ) ;
89
+ Blockly . Xml . clearWorkspaceAndLoadFromXml ( dom , workspace ) ;
56
90
}
57
91
58
- // if svg is true, then the update process is done in the BlocklySvg component
59
- if ( props . initialXml !== xml && ! this . props . svg ) {
60
- // guarantees that the current xml-code (this.props.initialXml) is rendered
92
+ // Wenn wir nicht im SVG-Modus sind und initialXml sich geändert hat
93
+ if ( ! svg && propsInitialXml ) {
61
94
workspace . clear ( ) ;
62
- if ( ! xml ) xml = initialXml ;
63
- Blockly . Xml . domToWorkspace ( Blockly . utils . xml . textToDom ( xml ) , workspace ) ;
95
+ const dom = Blockly . utils . xml . textToDom ( propsInitialXml ) ;
96
+ Blockly . Xml . domToWorkspace ( dom , workspace ) ;
64
97
}
65
- if ( props . language !== this . props . language ) {
66
- // change language
67
- xml = localStorage . getItem ( "autoSaveXML" ) ;
68
- if ( ! xml ) xml = initialXml ;
69
- xmlDom = Blockly . utils . xml . textToDom ( xml ) ;
70
- Blockly . Xml . clearWorkspaceAndLoadFromXml ( xmlDom , workspace ) ;
71
- // var toolbox = workspace.getToolbox();
72
- // workspace.updateToolbox(toolbox.toolboxDef_);
98
+
99
+ // Sprache geändert?
100
+ if ( language ) {
101
+ const saved = localStorage . getItem ( "autoSaveXML" ) ;
102
+ xml = saved || initialXml ;
103
+ const dom = Blockly . utils . xml . textToDom ( xml ) ;
104
+ Blockly . Xml . clearWorkspaceAndLoadFromXml ( dom , workspace ) ;
73
105
}
106
+
107
+ // Immer nach Update neu skalieren
74
108
Blockly . svgResize ( workspace ) ;
75
- }
76
-
77
- render ( ) {
78
- return (
79
- < div >
80
- < BlocklyComponent
81
- ref = { this . simpleWorkspace }
82
- style = { this . props . svg ? { height : 0 } : this . props . blocklyCSS }
83
- readOnly = {
84
- this . props . readOnly !== undefined ? this . props . readOnly : false
85
- }
86
- trashcan = {
87
- this . props . trashcan !== undefined ? this . props . trashcan : true
88
- }
89
- renderer = { this . props . renderer }
90
- sounds = { this . props . sounds }
91
- maxInstances = { getMaxInstances ( ) }
92
- zoom = { {
93
- // https://developers.google.com/blockly/guides/configure/web/zoom
94
- controls :
95
- this . props . zoomControls !== undefined
96
- ? this . props . zoomControls
97
- : true ,
98
- wheel : false ,
99
- startScale : 1 ,
100
- maxScale : 3 ,
101
- minScale : 0.3 ,
102
- scaleSpeed : 1.2 ,
103
- } }
104
- grid = {
105
- this . props . grid !== undefined && ! this . props . grid
106
- ? { }
107
- : {
108
- // https://developers.google.com/blockly/guides/configure/web/grid
109
- spacing : 20 ,
110
- length : 1 ,
111
- colour : "#4EAF47" , // senseBox-green
112
- snap : false ,
113
- }
114
- }
115
- media = { "/media/blockly/" }
116
- move = {
117
- this . props . move !== undefined && ! this . props . move
118
- ? { }
119
- : {
120
- // https://developers.google.com/blockly/guides/configure/web/move
121
- scrollbars : true ,
122
- drag : true ,
123
- wheel : true ,
124
- }
125
- }
126
- initialXml = {
127
- this . props . initialXml ? this . props . initialXml : initialXml
128
- }
129
- > </ BlocklyComponent >
130
- { this . props . svg && this . props . initialXml ? (
131
- < BlocklySvg initialXml = { this . props . initialXml } />
132
- ) : null }
133
- </ div >
134
- ) ;
135
- }
136
- }
109
+ } , [ selectedBoard , propsInitialXml , language , svg ] ) ;
137
110
138
- BlocklyWindow . propTypes = {
139
- onChangeWorkspace : PropTypes . func . isRequired ,
140
- clearStats : PropTypes . func . isRequired ,
141
- renderer : PropTypes . string . isRequired ,
142
- sounds : PropTypes . bool . isRequired ,
143
- language : PropTypes . string . isRequired ,
144
- selectedBoard : PropTypes . string . isRequired ,
111
+ // Grid- und Move-Konfiguration
112
+ const gridConfig = gridEnabled
113
+ ? {
114
+ spacing : 20 ,
115
+ length : 1 ,
116
+ colour : "#4EAF47" ,
117
+ snap : false ,
118
+ }
119
+ : { } ;
120
+ const moveConfig = moveEnabled
121
+ ? {
122
+ scrollbars : true ,
123
+ drag : true ,
124
+ wheel : true ,
125
+ }
126
+ : { } ;
127
+
128
+ return (
129
+ < div >
130
+ < BlocklyComponent
131
+ ref = { simpleWorkspaceRef }
132
+ style = { svg ? { height : 0 } : blocklyCSS }
133
+ readOnly = { readOnly }
134
+ trashcan = { trashcan }
135
+ renderer = { renderer }
136
+ sounds = { sounds }
137
+ maxInstances = { getMaxInstances ( ) }
138
+ zoom = { {
139
+ controls : zoomControls ,
140
+ wheel : false ,
141
+ startScale : 1 ,
142
+ maxScale : 3 ,
143
+ minScale : 0.3 ,
144
+ scaleSpeed : 1.2 ,
145
+ } }
146
+ grid = { gridConfig }
147
+ media = "/media/blockly/"
148
+ move = { moveConfig }
149
+ initialXml = { propsInitialXml || initialXml }
150
+ />
151
+ { svg && propsInitialXml && < BlocklySvg initialXml = { propsInitialXml } /> }
152
+ < Snackbar
153
+ open = { snackbarOpen }
154
+ message = { snackInfo . message }
155
+ type = { snackInfo . type }
156
+ key = { snackInfo . key }
157
+ />
158
+ </ div >
159
+ ) ;
145
160
} ;
146
161
147
- const mapStateToProps = ( state ) => ( {
148
- renderer : state . general . renderer ,
149
- sounds : state . general . sounds ,
150
- language : state . general . language ,
151
- selectedBoard : state . board . board ,
152
- } ) ;
162
+ BlocklyWindow . propTypes = {
163
+ blockDisabled : PropTypes . bool ,
164
+ readOnly : PropTypes . bool ,
165
+ trashcan : PropTypes . bool ,
166
+ zoomControls : PropTypes . bool ,
167
+ grid : PropTypes . bool ,
168
+ move : PropTypes . bool ,
169
+ blocklyCSS : PropTypes . object ,
170
+ svg : PropTypes . bool ,
171
+ initialXml : PropTypes . string ,
172
+ } ;
153
173
154
- export default connect ( mapStateToProps , { onChangeWorkspace, clearStats } ) (
155
- BlocklyWindow ,
156
- ) ;
174
+ export default BlocklyWindow ;
0 commit comments