1
1
import randomWords from "random-words" ;
2
- import { v4 as uuid } from "uuid" ;
3
2
import React , { useMemo , useState , useEffect , useCallback } from "react" ;
4
3
import { connectToDB , getConnection } from "./sharedb" ;
5
4
import { useTransition } from "./react-experimental" ;
5
+ import {
6
+ Flow ,
7
+ Node ,
8
+ insertNodeOp ,
9
+ removeNodeOp ,
10
+ setFlowOp ,
11
+ connectOp ,
12
+ } from "./flow" ;
6
13
import {
7
14
Link ,
8
15
BrowserRouter as Router ,
9
16
useHistory ,
10
17
useLocation ,
11
18
} from "react-router-dom" ;
12
19
13
- interface Node {
14
- text : string ;
15
- }
16
-
17
- type Flow = {
18
- nodes : Record < string , Node > ;
19
- edges : Array < [ string | null , string ] > ;
20
- } ;
21
-
22
20
// Custom hook for talking to a flow in ShareDB
23
21
function useFlow ( config : {
24
22
id : string ;
25
23
} ) : {
26
24
state : Flow | null ;
27
- addNode : ( ) => void ;
25
+ insertNode : ( ) => void ;
28
26
removeNode : ( id : string ) => void ;
29
- reset : ( flow : Flow ) => void ;
27
+ connectNodes : ( src : string , tgt : string ) => void ;
28
+ setFlow : ( flow : Flow ) => void ;
30
29
isPending : boolean ;
31
30
} {
32
31
// Setup
@@ -56,20 +55,27 @@ function useFlow(config: {
56
55
57
56
// Methods
58
57
59
- const addNode = useCallback ( ( ) => {
60
- doc . submitOp ( [ { p : [ "nodes" , uuid ( ) ] , oi : { text : randomWords ( ) } } ] ) ;
58
+ const insertNode = useCallback ( ( ) => {
59
+ doc . submitOp ( insertNodeOp ( ) ) ;
61
60
} , [ doc ] ) ;
62
61
63
62
const removeNode = useCallback (
64
63
( id ) => {
65
- doc . submitOp ( [ { p : [ "nodes" , id ] , od : { } } ] ) ;
64
+ doc . submitOp ( removeNodeOp ( id , doc . data ) ) ;
65
+ } ,
66
+ [ doc ]
67
+ ) ;
68
+
69
+ const connectNodes = useCallback (
70
+ ( src , tgt ) => {
71
+ doc . submitOp ( connectOp ( src , tgt , doc . data ) ) ;
66
72
} ,
67
73
[ doc ]
68
74
) ;
69
75
70
- const reset = useCallback (
76
+ const setFlow = useCallback (
71
77
( flow ) => {
72
- doc . submitOp ( [ { p : [ ] , od : doc . data , oi : flow } ] ) ;
78
+ doc . submitOp ( setFlowOp ( flow , doc . data ) ) ;
73
79
} ,
74
80
[ doc ]
75
81
) ;
@@ -78,16 +84,33 @@ function useFlow(config: {
78
84
79
85
return {
80
86
state,
81
- addNode ,
87
+ insertNode ,
82
88
removeNode,
83
- reset,
89
+ setFlow,
90
+ connectNodes,
84
91
isPending,
85
92
} ;
86
93
}
87
94
88
- const Flow : React . FC < { id : string } > = ( { id } ) => {
95
+ const FlowView : React . FC < { id : string } > = ( { id } ) => {
89
96
const flow = useFlow ( { id } ) ;
90
97
98
+ const [ selected , setSelected ] = useState < string | null > ( null ) ;
99
+
100
+ const onSelect = useCallback (
101
+ ( id : string ) => {
102
+ if ( selected === null ) {
103
+ setSelected ( id ) ;
104
+ } else if ( selected === id ) {
105
+ setSelected ( null ) ;
106
+ } else {
107
+ flow . connectNodes ( selected , id ) ;
108
+ setSelected ( null ) ;
109
+ }
110
+ } ,
111
+ [ selected , setSelected ]
112
+ ) ;
113
+
91
114
if ( flow . state === null ) {
92
115
return < p > Loading...</ p > ;
93
116
}
@@ -97,7 +120,7 @@ const Flow: React.FC<{ id: string }> = ({ id }) => {
97
120
< main >
98
121
< button
99
122
onClick = { ( ) => {
100
- flow . addNode ( ) ;
123
+ flow . insertNode ( ) ;
101
124
} }
102
125
>
103
126
Add
@@ -106,31 +129,47 @@ const Flow: React.FC<{ id: string }> = ({ id }) => {
106
129
onClick = { ( ) => {
107
130
fetch ( "/flow.json" )
108
131
. then ( ( res ) => res . json ( ) )
109
- . then ( ( flowData ) => {
110
- flow . reset ( flowData ) ;
132
+ . then ( ( flowData : Flow ) => {
133
+ flow . setFlow ( flowData ) ;
111
134
} ) ;
112
135
} }
113
136
>
114
137
Import flow
115
138
</ button >
116
139
< button
117
140
onClick = { ( ) => {
118
- flow . reset ( {
141
+ flow . setFlow ( {
119
142
nodes : { } ,
120
143
edges : [ ] ,
121
144
} ) ;
122
145
} }
123
146
>
124
147
Reset
125
148
</ button >
126
- { Object . keys ( flow . state . nodes ) . map ( ( k ) => (
127
- < NodeView
128
- key = { k }
129
- onRemove = { flow . removeNode }
130
- id = { k }
131
- node = { flow . state . nodes [ k ] }
132
- />
133
- ) ) }
149
+ < div className = "row mt" >
150
+ < div >
151
+ < h3 > Nodes</ h3 >
152
+ { Object . keys ( flow . state . nodes ) . map ( ( k ) => (
153
+ < NodeView
154
+ key = { k }
155
+ onRemove = { flow . removeNode }
156
+ onSelect = { onSelect }
157
+ id = { k }
158
+ node = { flow . state . nodes [ k ] }
159
+ activeId = { selected }
160
+ />
161
+ ) ) }
162
+ </ div >
163
+ < div >
164
+ < h3 > Edges</ h3 >
165
+ { flow . state . edges . map ( ( [ src , tgt ] ) => (
166
+ < p >
167
+ { src ? src . slice ( 0 , 6 ) : "root" } -{ " " }
168
+ { tgt ? tgt . slice ( 0 , 6 ) : "root" }
169
+ </ p >
170
+ ) ) }
171
+ </ div >
172
+ </ div >
134
173
</ main >
135
174
{ flow . isPending && < div className = "overlay" /> }
136
175
</ >
@@ -142,12 +181,16 @@ const NodeView = React.memo(
142
181
id,
143
182
node,
144
183
onRemove,
184
+ onSelect,
185
+ activeId,
145
186
} : {
146
187
id : string ;
147
188
node : Node ;
148
189
onRemove : ( id : string ) => void ;
190
+ onSelect : ( id : string ) => void ;
191
+ activeId : string | null ;
149
192
} ) => (
150
- < div className = "node" >
193
+ < div className = { `node ${ id === activeId ? "node--active" : "" } ` } >
151
194
< button
152
195
className = "remove-button"
153
196
onClick = { ( ) => {
@@ -156,14 +199,33 @@ const NodeView = React.memo(
156
199
>
157
200
×
158
201
</ button >
159
- < p >
160
- { node . text || "unset" } { Math . round ( Math . random ( ) * 1000 ) }
161
- </ p >
202
+ < div >
203
+ < p >
204
+ { node . text || "unset" } { " " }
205
+ < small > { Math . round ( Math . random ( ) * 1000 ) } </ small >
206
+ </ p >
207
+ < p >
208
+ < small > { id . slice ( 0 , 6 ) } ..</ small >
209
+ </ p >
210
+ </ div >
211
+ < button
212
+ onClick = { ( ) => {
213
+ onSelect ( id ) ;
214
+ } }
215
+ >
216
+ { activeId === id
217
+ ? "Deselect"
218
+ : activeId !== null
219
+ ? "Connect"
220
+ : "Select" }
221
+ </ button >
162
222
</ div >
163
223
) ,
164
224
( prevProps , nextProps ) =>
165
225
prevProps . id === nextProps . id &&
166
226
prevProps . onRemove === nextProps . onRemove &&
227
+ prevProps . onSelect === nextProps . onSelect &&
228
+ prevProps . activeId === nextProps . activeId &&
167
229
JSON . stringify ( prevProps . node ) === JSON . stringify ( nextProps . node )
168
230
) ;
169
231
@@ -209,7 +271,7 @@ const App = () => {
209
271
New flow
210
272
</ button >
211
273
</ nav >
212
- < Flow id = { id } />
274
+ < FlowView id = { id } />
213
275
</ div >
214
276
) ;
215
277
} ;
0 commit comments