1
1
import * as React from "react" ;
2
2
import Moment from "react-moment" ;
3
3
import { MenuState } from "../../models/menu" ;
4
+ import ReactMarkdown from "react-markdown" ;
4
5
import {
5
6
Menu ,
6
7
DropdownProps ,
@@ -11,11 +12,17 @@ import {
11
12
Modal ,
12
13
Form ,
13
14
TextAreaProps ,
15
+ Sidebar ,
16
+ Segment ,
14
17
Message ,
15
18
Input ,
16
19
Label ,
17
20
} from "semantic-ui-react" ;
18
- import { MazeInfo , Maze } from "../../models/maze" ;
21
+ import { MazeInfo , Space } from "../../models/maze" ;
22
+ import * as yup from "yup" ;
23
+ import { Maze } from "../../models/maze" ;
24
+ import { SpaceTypes } from "../Space/types" ;
25
+ import algos from "../../algos" ;
19
26
import { getMazeSize } from "../Maze/Maze" ;
20
27
21
28
import "./Menu.scss" ;
@@ -65,37 +72,130 @@ export interface MenuProps extends MenuState {
65
72
export interface _MenuState {
66
73
value : string ;
67
74
showModal : boolean ;
68
- jsonError : boolean ;
75
+ hasError : boolean ;
76
+ errorMessage : string ;
77
+ sidebar : boolean ;
69
78
mazeCols : number ;
70
79
mazeRows : number ;
71
80
}
72
81
82
+ export const yupSpaceSchema = yup . object ( {
83
+ type : yup
84
+ . mixed ( )
85
+ . oneOf ( [ "wall" , "empty" , "startpoint" , "endpoint" ] )
86
+ . required ( ) ,
87
+ visited : yup . bool ( ) . required ( ) ,
88
+ path : yup . bool ( ) . required ( ) ,
89
+ } ) ;
90
+
73
91
class MenuBar extends React . Component < MenuProps , _MenuState > {
74
92
state = {
75
93
value : "" ,
76
94
showModal : false ,
77
- jsonError : false ,
95
+ hasError : false ,
96
+ errorMessage : "" ,
97
+ sidebar : false ,
78
98
mazeCols : getMazeSize ( this . props . maze . mazeInfo ) . x ,
79
99
mazeRows : getMazeSize ( this . props . maze . mazeInfo ) . y ,
80
100
} ;
81
101
102
+ checkDynamicKeys = ( keys : any [ ] ) : boolean => {
103
+ for ( let i = 0 ; i < keys . length ; i ++ ) {
104
+ const keyIsValid = yup . number ( ) . required ( ) . isValidSync ( keys [ i ] ) ;
105
+ if ( ! keyIsValid ) {
106
+ return false ;
107
+ }
108
+ }
109
+ return true ;
110
+ } ;
111
+
112
+ checkSpace = ( keys : any [ ] , obj : any ) : boolean => {
113
+ // {key: "value"} => [0, 1, 2, 3, 4]
114
+ for ( let i = 0 ; i < keys . length ; i ++ ) {
115
+ const isArray = yup . array ( ) . isValidSync ( obj [ i ] ) ;
116
+ if ( ! isArray ) return false ;
117
+ if ( obj [ i ] . length === 0 ) return false ;
118
+ for ( let j = 0 ; j < obj [ i ] . length ; j ++ ) {
119
+ console . log ( obj [ keys [ i ] ] [ j ] ) ;
120
+ const spaceIsValid = yupSpaceSchema . isValidSync ( obj [ keys [ i ] ] [ j ] ) ;
121
+ if ( ! spaceIsValid ) {
122
+ return false ;
123
+ }
124
+ }
125
+ }
126
+ return true ;
127
+ } ;
128
+
129
+ checkStartEndPoints = ( maze : MazeInfo ) : boolean => {
130
+ let numStart : number = 0 ;
131
+ let numEnd : number = 0 ;
132
+ // eslint-disable-next-line array-callback-return
133
+ Object . keys ( maze ) . map ( ( key : string ) => {
134
+ // eslint-disable-next-line array-callback-return
135
+ maze [ + key ] . map ( ( value : Space ) => {
136
+ numStart += value . type === SpaceTypes . start ? 1 : 0 ;
137
+ numEnd += value . type === SpaceTypes . end ? 1 : 0 ;
138
+ } ) ;
139
+ } ) ;
140
+ return numStart <= 1 && numEnd <= 1 ;
141
+ } ;
142
+
82
143
//event: React.FormEvent<HTMLTextAreaElement>, data: TextAreaProps) => void
83
144
handleChange = (
84
145
event : React . FormEvent < HTMLTextAreaElement > ,
85
146
data : TextAreaProps
86
147
) : void => {
148
+ console . log ( "here" ) ;
87
149
try {
88
- var json = JSON . parse ( data . value as string ) ;
89
- this . setState ( {
90
- ...this . state ,
91
- jsonError : false ,
92
- value : data . value as string ,
93
- } ) ;
150
+ const maze : MazeInfo = JSON . parse ( data . value as string ) ;
151
+ console . log ( maze ) ;
152
+ const keys = Object . keys ( maze ) ;
153
+ if ( keys . length === 0 ) {
154
+ this . setState ( {
155
+ ...this . state ,
156
+ hasError : true ,
157
+ errorMessage : "Object should have keys!" ,
158
+ } ) ;
159
+ } else {
160
+ const keysAreValid = this . checkDynamicKeys ( keys ) ;
161
+ if ( ! keysAreValid ) {
162
+ this . setState ( {
163
+ ...this . state ,
164
+ hasError : true ,
165
+ errorMessage : "Object keys must be numeric!" ,
166
+ } ) ;
167
+ } else {
168
+ const spacesAreValid = this . checkSpace ( keys , maze ) ;
169
+ if ( ! spacesAreValid ) {
170
+ this . setState ( {
171
+ ...this . state ,
172
+ hasError : true ,
173
+ errorMessage :
174
+ "Spaces required parameters type, visited, and path!" ,
175
+ } ) ;
176
+ } else {
177
+ const startEndPointsValid = this . checkStartEndPoints ( maze ) ;
178
+ if ( ! startEndPointsValid ) {
179
+ this . setState ( {
180
+ ...this . state ,
181
+ hasError : true ,
182
+ errorMessage :
183
+ "Maze can only have one start point and one end point" ,
184
+ } ) ;
185
+ } else {
186
+ this . setState ( {
187
+ hasError : false ,
188
+ value : data . value as string ,
189
+ } ) ;
190
+ }
191
+ }
192
+ }
193
+ }
94
194
} catch ( e ) {
95
195
this . setState ( {
96
196
...this . state ,
97
- jsonError : true ,
98
- value : data . value as string ,
197
+ hasError : true ,
198
+ errorMessage : "Maze data must be in JSON format!" ,
99
199
} ) ;
100
200
}
101
201
} ;
@@ -114,6 +214,26 @@ class MenuBar extends React.Component<MenuProps, _MenuState> {
114
214
this . setState ( { ...this . state , showModal : true } ) ;
115
215
} ;
116
216
217
+ getInfo = ( ) => {
218
+ let al = this . props . selectedAlgo ;
219
+ if ( al == null ) {
220
+ al = "Select an algorithm" ;
221
+ } else if ( al == "A*" ) {
222
+ al = algos . a ;
223
+ } else if ( al == "BFS" ) {
224
+ al = algos . BFS ;
225
+ } else if ( al == "DFS" ) {
226
+ al = algos . DFS ;
227
+ } else if ( al == "Djikstras" ) {
228
+ al = algos . Djikstras ;
229
+ }
230
+ return al as string ;
231
+ } ;
232
+
233
+ setVisible = ( b : boolean ) => {
234
+ this . setState ( { ...this . state , sidebar : b } ) ;
235
+ } ;
236
+
117
237
render ( ) {
118
238
const {
119
239
canMoveStart,
@@ -343,14 +463,33 @@ class MenuBar extends React.Component<MenuProps, _MenuState> {
343
463
value = { this . state . value }
344
464
onChange = { this . handleChange . bind ( this ) }
345
465
/>
346
- < Message visible = { this . state . jsonError } >
466
+ < Message visible = { this . state . hasError } >
347
467
Enter properly formatted json
348
468
</ Message >
349
469
< Form . Button content = "Submit" />
350
470
</ Form >
351
471
</ Modal . Description >
352
472
</ Modal . Content >
353
473
</ Modal >
474
+ { /* Essentially just a fancy space */ }
475
+ < Button color = "blue" circular onClick = { ( ) => this . setVisible ( true ) } >
476
+ < Icon name = "info" />
477
+ </ Button >
478
+ < Sidebar
479
+ as = { Menu }
480
+ animation = "push"
481
+ overlay
482
+ icon = "labeled"
483
+ direction = "right"
484
+ onHide = { ( ) => this . setVisible ( false ) }
485
+ vertical
486
+ visible = { this . state . sidebar }
487
+ width = "very wide"
488
+ >
489
+ < Segment textAlign = "left" padded = "very" >
490
+ < ReactMarkdown source = { this . getInfo ( ) } text-color = "white" />
491
+ </ Segment >
492
+ </ Sidebar >
354
493
</ Menu . Item >
355
494
</ Menu >
356
495
< Message attached = "bottom" >
0 commit comments