@@ -2,10 +2,12 @@ import {
2
2
Alert ,
3
3
alpha ,
4
4
darken ,
5
+ Fab ,
5
6
Popover ,
6
7
type PopoverPosition ,
7
8
type PopoverProps ,
8
9
Snackbar ,
10
+ Typography ,
9
11
useTheme ,
10
12
} from '@mui/material' ;
11
13
import {
@@ -17,6 +19,7 @@ import {
17
19
type EdgeRemoveChange ,
18
20
type NodeChange ,
19
21
type NodeRemoveChange ,
22
+ Panel ,
20
23
ReactFlow ,
21
24
type ReactFlowInstance ,
22
25
reconnectEdge ,
@@ -35,6 +38,7 @@ import EditScopeForm from './forms/edit-scope-form';
35
38
import {
36
39
type DiagramEditorNode ,
37
40
isOperationNode ,
41
+ MaterialSymbol ,
38
42
NODE_TYPES ,
39
43
type OperationNode ,
40
44
} from './nodes' ;
@@ -94,7 +98,7 @@ const DiagramEditor = () => {
94
98
DiagramEditorEdge
95
99
> | null > ( null ) ;
96
100
97
- const [ editorMode ] = useEditorMode ( ) ;
101
+ const [ editorMode , setEditorMode ] = useEditorMode ( ) ;
98
102
99
103
const [ nodes , setNodes ] = React . useState < DiagramEditorNode [ ] > (
100
104
( ) => loadEmpty ( ) . nodes ,
@@ -454,146 +458,159 @@ const DiagramEditor = () => {
454
458
} , [ ] ) ;
455
459
456
460
return (
457
- < >
458
- < ReactFlow
459
- nodes = { renderedNodes }
460
- edges = { renderedEdges }
461
- fitView
462
- fitViewOptions = { { padding : 0.2 } }
463
- nodeTypes = { NODE_TYPES }
464
- edgeTypes = { EDGE_TYPES }
465
- onInit = { ( instance ) => {
466
- reactFlowInstance . current = instance ;
467
-
468
- const queryParams = new URLSearchParams ( window . location . search ) ;
469
- const diagramParam = queryParams . get ( 'diagram' ) ;
470
-
471
- if ( ! diagramParam ) {
472
- return ;
473
- }
461
+ < ReactFlow
462
+ nodes = { renderedNodes }
463
+ edges = { renderedEdges }
464
+ fitView
465
+ fitViewOptions = { { padding : 0.2 } }
466
+ nodeTypes = { NODE_TYPES }
467
+ edgeTypes = { EDGE_TYPES }
468
+ onInit = { ( instance ) => {
469
+ reactFlowInstance . current = instance ;
470
+
471
+ const queryParams = new URLSearchParams ( window . location . search ) ;
472
+ const diagramParam = queryParams . get ( 'diagram' ) ;
473
+
474
+ if ( ! diagramParam ) {
475
+ return ;
476
+ }
474
477
475
- try {
476
- const binaryString = atob ( diagramParam ) ;
477
- const byteArray = new Uint8Array ( binaryString . length ) ;
478
- for ( let i = 0 ; i < binaryString . length ; i ++ ) {
479
- byteArray [ i ] = binaryString . charCodeAt ( i ) ;
480
- }
481
- const diagramJson = strFromU8 ( inflateSync ( byteArray ) ) ;
482
- loadDiagram ( diagramJson ) ;
483
- } catch ( e ) {
484
- if ( e instanceof Error ) {
485
- showErrorToast ( `failed to load diagram: ${ e . message } ` ) ;
486
- } else {
487
- throw e ;
488
- }
478
+ try {
479
+ const binaryString = atob ( diagramParam ) ;
480
+ const byteArray = new Uint8Array ( binaryString . length ) ;
481
+ for ( let i = 0 ; i < binaryString . length ; i ++ ) {
482
+ byteArray [ i ] = binaryString . charCodeAt ( i ) ;
489
483
}
490
- } }
491
- onNodesChange = { handleNodeChanges }
492
- onNodesDelete = { ( ) => {
493
- closeAllPopovers ( ) ;
494
- } }
495
- onEdgesChange = { handleEdgeChanges }
496
- onEdgesDelete = { ( ) => {
497
- closeAllPopovers ( ) ;
498
- } }
499
- onConnect = { ( conn ) => {
500
- const sourceNode = nodes . find ( ( n ) => n . id === conn . source ) ;
501
- const targetNode = nodes . find ( ( n ) => n . id === conn . target ) ;
502
- if ( ! sourceNode || ! targetNode ) {
503
- throw new Error ( 'cannot find source or target node' ) ;
504
- }
505
-
506
- const allowedEdges = getAllowEdges ( sourceNode , targetNode ) ;
507
- if ( allowedEdges . length === 0 ) {
508
- showErrorToast (
509
- `cannot connect "${ sourceNode . type } " to "${ targetNode . type } "` ,
510
- ) ;
511
- return ;
484
+ const diagramJson = strFromU8 ( inflateSync ( byteArray ) ) ;
485
+ loadDiagram ( diagramJson ) ;
486
+ } catch ( e ) {
487
+ if ( e instanceof Error ) {
488
+ showErrorToast ( `failed to load diagram: ${ e . message } ` ) ;
489
+ } else {
490
+ throw e ;
512
491
}
492
+ }
493
+ } }
494
+ onNodesChange = { handleNodeChanges }
495
+ onNodesDelete = { ( ) => {
496
+ closeAllPopovers ( ) ;
497
+ } }
498
+ onEdgesChange = { handleEdgeChanges }
499
+ onEdgesDelete = { ( ) => {
500
+ closeAllPopovers ( ) ;
501
+ } }
502
+ onConnect = { ( conn ) => {
503
+ const sourceNode = nodes . find ( ( n ) => n . id === conn . source ) ;
504
+ const targetNode = nodes . find ( ( n ) => n . id === conn . target ) ;
505
+ if ( ! sourceNode || ! targetNode ) {
506
+ throw new Error ( 'cannot find source or target node' ) ;
507
+ }
513
508
514
- setEdges ( ( prev ) =>
515
- addEdge (
516
- {
517
- ...conn ,
518
- type : allowedEdges [ 0 ] ,
519
- data : defaultEdgeData ( allowedEdges [ 0 ] ) ,
520
- } ,
521
- prev ,
522
- ) ,
509
+ const allowedEdges = getAllowEdges ( sourceNode , targetNode ) ;
510
+ if ( allowedEdges . length === 0 ) {
511
+ showErrorToast (
512
+ `cannot connect "${ sourceNode . type } " to "${ targetNode . type } "` ,
523
513
) ;
524
- } }
525
- onReconnect = { ( oldEdge , newConnection ) =>
526
- setEdges ( ( prev ) => reconnectEdge ( oldEdge , newConnection , prev ) )
514
+ return ;
527
515
}
528
- onNodeClick = { ( ev , node ) => {
529
- ev . stopPropagation ( ) ;
530
- closeAllPopovers ( ) ;
531
516
532
- if ( ! isOperationNode ( node ) ) {
533
- return ;
534
- }
535
- setEditingNodeId ( node . id ) ;
517
+ setEdges ( ( prev ) =>
518
+ addEdge (
519
+ {
520
+ ...conn ,
521
+ type : allowedEdges [ 0 ] ,
522
+ data : defaultEdgeData ( allowedEdges [ 0 ] ) ,
523
+ } ,
524
+ prev ,
525
+ ) ,
526
+ ) ;
527
+ } }
528
+ onReconnect = { ( oldEdge , newConnection ) =>
529
+ setEdges ( ( prev ) => reconnectEdge ( oldEdge , newConnection , prev ) )
530
+ }
531
+ onNodeClick = { ( ev , node ) => {
532
+ ev . stopPropagation ( ) ;
533
+ closeAllPopovers ( ) ;
536
534
537
- setEditOpFormPopoverProps ( {
538
- open : true ,
539
- anchorReference : 'anchorPosition' ,
540
- anchorPosition : { left : ev . clientX , top : ev . clientY } ,
541
- } ) ;
542
- } }
543
- onEdgeClick = { ( ev , edge ) => {
544
- ev . stopPropagation ( ) ;
545
- closeAllPopovers ( ) ;
535
+ if ( ! isOperationNode ( node ) ) {
536
+ return ;
537
+ }
538
+ setEditingNodeId ( node . id ) ;
539
+
540
+ setEditOpFormPopoverProps ( {
541
+ open : true ,
542
+ anchorReference : 'anchorPosition' ,
543
+ anchorPosition : { left : ev . clientX , top : ev . clientY } ,
544
+ } ) ;
545
+ } }
546
+ onEdgeClick = { ( ev , edge ) => {
547
+ ev . stopPropagation ( ) ;
548
+ closeAllPopovers ( ) ;
546
549
547
- const sourceNode = nodes . find ( ( n ) => n . id === edge . source ) ;
548
- const targetNode = nodes . find ( ( n ) => n . id === edge . target ) ;
549
- if ( ! sourceNode || ! targetNode ) {
550
- throw new Error ( 'unable to find source or target node' ) ;
551
- }
550
+ const sourceNode = nodes . find ( ( n ) => n . id === edge . source ) ;
551
+ const targetNode = nodes . find ( ( n ) => n . id === edge . target ) ;
552
+ if ( ! sourceNode || ! targetNode ) {
553
+ throw new Error ( 'unable to find source or target node' ) ;
554
+ }
552
555
553
- setEditingEdgeId ( edge . id ) ;
556
+ setEditingEdgeId ( edge . id ) ;
554
557
555
- setEditOpFormPopoverProps ( {
556
- open : true ,
557
- anchorReference : 'anchorPosition' ,
558
- anchorPosition : { left : ev . clientX , top : ev . clientY } ,
559
- } ) ;
560
- } }
561
- onPaneClick = { ( ev ) => {
562
- if ( addOperationPopover . open || editOpFormPopoverProps . open ) {
563
- closeAllPopovers ( ) ;
564
- return ;
565
- }
558
+ setEditOpFormPopoverProps ( {
559
+ open : true ,
560
+ anchorReference : 'anchorPosition' ,
561
+ anchorPosition : { left : ev . clientX , top : ev . clientY } ,
562
+ } ) ;
563
+ } }
564
+ onPaneClick = { ( ev ) => {
565
+ if ( addOperationPopover . open || editOpFormPopoverProps . open ) {
566
+ closeAllPopovers ( ) ;
567
+ return ;
568
+ }
566
569
567
- // filter out erroneous click after connecting an edge
568
- const now = Date . now ( ) ;
569
- if ( now - mouseDownTime . current > 200 ) {
570
- return ;
571
- }
572
- setAddOperationPopover ( {
573
- open : true ,
574
- popOverPosition : { left : ev . clientX , top : ev . clientY } ,
575
- parentId : null ,
576
- } ) ;
577
- } }
578
- onMouseDownCapture = { handleMouseDown }
579
- onTouchStartCapture = { handleMouseDown }
580
- colorMode = "dark"
581
- deleteKeyCode = { 'Delete' }
582
- >
583
- < Background
584
- bgColor = { backgroundColor }
585
- color = { alpha ( theme . palette . text . primary , 0.3 ) }
586
- gap = { 30 }
587
- />
588
- < CommandPanel
589
- onNodeChanges = { handleNodeChanges }
590
- onExportClick = { React . useCallback (
591
- ( ) => setOpenExportDiagramDialog ( true ) ,
592
- [ ] ,
593
- ) }
594
- onLoadDiagram = { loadDiagram }
595
- />
596
- </ ReactFlow >
570
+ // filter out erroneous click after connecting an edge
571
+ const now = Date . now ( ) ;
572
+ if ( now - mouseDownTime . current > 200 ) {
573
+ return ;
574
+ }
575
+ setAddOperationPopover ( {
576
+ open : true ,
577
+ popOverPosition : { left : ev . clientX , top : ev . clientY } ,
578
+ parentId : null ,
579
+ } ) ;
580
+ } }
581
+ onMouseDownCapture = { handleMouseDown }
582
+ onTouchStartCapture = { handleMouseDown }
583
+ colorMode = "dark"
584
+ deleteKeyCode = { 'Delete' }
585
+ >
586
+ < Background
587
+ bgColor = { backgroundColor }
588
+ color = { alpha ( theme . palette . text . primary , 0.3 ) }
589
+ gap = { 30 }
590
+ />
591
+ { editorMode . mode === EditorMode . Template && (
592
+ < Panel position = "top-left" >
593
+ < Typography variant = "h4" > { editorMode . templateId } </ Typography >
594
+ </ Panel >
595
+ ) }
596
+ < CommandPanel
597
+ onNodeChanges = { handleNodeChanges }
598
+ onExportClick = { React . useCallback (
599
+ ( ) => setOpenExportDiagramDialog ( true ) ,
600
+ [ ] ,
601
+ ) }
602
+ onLoadDiagram = { loadDiagram }
603
+ />
604
+ { editorMode . mode === EditorMode . Template && (
605
+ < Fab
606
+ color = "primary"
607
+ aria-label = "Save"
608
+ sx = { { position : 'absolute' , right : 64 , bottom : 64 } }
609
+ onClick = { ( ) => setEditorMode ( { mode : EditorMode . Normal } ) }
610
+ >
611
+ < MaterialSymbol symbol = "check" />
612
+ </ Fab >
613
+ ) }
597
614
< Popover
598
615
open = { addOperationPopover . open }
599
616
onClose = { ( ) =>
@@ -656,7 +673,7 @@ const DiagramEditor = () => {
656
673
nodes = { nodes }
657
674
edges = { edges }
658
675
/>
659
- </ >
676
+ </ ReactFlow >
660
677
) ;
661
678
} ;
662
679
0 commit comments