77// License, use of this software will be governed by the Apache License, Version 2.0,
88// included in the file licenses/APL.txt.
99
10- import { type Dispatch , type UnknownAction } from "@reduxjs/toolkit" ;
1110import { arc } from "@synnaxlabs/client" ;
1211import { useSelectWindowKey } from "@synnaxlabs/drift/react" ;
1312import {
1413 Access ,
1514 Arc as Base ,
15+ Component ,
1616 Diagram ,
1717 Haul ,
1818 Theming ,
@@ -24,24 +24,26 @@ import { type ReactElement, useCallback, useMemo, useRef } from "react";
2424import { useDispatch } from "react-redux" ;
2525
2626import { Controls } from "@/arc/editor/Controls" ;
27+ import { Provider , useArcEditorContext } from "@/arc/editor/graph/Context" ;
2728import {
2829 select ,
2930 useSelect ,
3031 useSelectNodeProps ,
32+ useSelectSelected ,
3133 useSelectViewportMode ,
3234} from "@/arc/selectors" ;
3335import {
3436 addElement ,
35- clearSelection ,
37+ applyEdgeChanges ,
38+ applyNodeChanges ,
3639 copySelection ,
3740 internalCreate ,
3841 pasteSelection ,
3942 selectAll ,
40- setEdges ,
4143 setEditable ,
4244 setElementProps ,
4345 setFitViewOnResize ,
44- setNodes ,
46+ setSelected ,
4547 setViewport ,
4648 setViewportMode ,
4749 type State ,
@@ -65,42 +67,35 @@ export const filterHaulItems = (items: Haul.Item[]): HaulItem[] =>
6567
6668export const canDropHaulItem = Haul . canDropOfType < HaulItem > ( HAUL_TYPE ) ;
6769
68- interface SymbolRendererProps extends Diagram . SymbolProps {
69- layoutKey : string ;
70- dispatch : Dispatch < UnknownAction > ;
71- }
72-
73- const StageRenderer = ( {
74- symbolKey,
70+ const NodeRenderer = ( {
71+ nodeKey,
7572 position,
7673 selected,
7774 draggable,
78- dispatch,
79- layoutKey,
80- } : SymbolRendererProps ) : ReactElement | null => {
81- const props = useSelectNodeProps ( layoutKey , symbolKey ) ;
75+ } : Diagram . NodeProps ) : ReactElement | null => {
76+ const { layoutKey, dispatch } = useArcEditorContext ( "ArcEditor.NodeRenderer" ) ;
77+ const props = useSelectNodeProps ( layoutKey , nodeKey ) ;
8278 const { key = "" , ...rest } = props ?? { } ;
8379 const handleChange = useCallback (
8480 ( props : object ) => {
8581 if ( key == null ) return ;
8682 dispatch (
8783 setElementProps ( {
8884 layoutKey,
89- key : symbolKey ,
85+ key : nodeKey ,
9086 props : { key, ...props } ,
9187 } ) ,
9288 ) ;
9389 } ,
94- [ symbolKey , layoutKey , key , dispatch ] ,
90+ [ nodeKey , layoutKey , key , dispatch ] ,
9591 ) ;
9692 if ( props == null ) return null ;
9793 const C = Base . Stage . REGISTRY [ key ] ;
9894 if ( C == null ) throw new Error ( `Symbol ${ key } not found` ) ;
9995 return (
10096 < C . Symbol
10197 key = { key }
102- id = { symbolKey }
103- symbolKey = { symbolKey }
98+ nodeKey = { nodeKey }
10499 position = { position }
105100 selected = { selected }
106101 draggable = { draggable }
@@ -110,6 +105,10 @@ const StageRenderer = ({
110105 ) ;
111106} ;
112107
108+ const ArcDiagram = Base . create ( {
109+ node : Component . renderProp ( NodeRenderer ) ,
110+ } ) ;
111+
113112export const ContextMenu : Layout . ContextMenuRenderer = ( { layoutKey } ) => (
114113 < CMenu . Menu >
115114 < Layout . MenuItems layoutKey = { layoutKey } />
@@ -128,33 +127,39 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
128127 const [ undoableDispatch , undo , redo ] = useUndoableDispatch < RootState , State > (
129128 selector ,
130129 internalCreate ,
131- 30 , // roughly the right time needed to prevent actions that get dispatch automatically by Diagram.tsx, like setNodes immediately following addElement
130+ 30 ,
132131 ) ;
133132
134133 const theme = Theming . use ( ) ;
135134 const viewportRef = useSyncedRef ( state . graph . viewport ) ;
136135 const hasUpdatePermission = Access . useUpdateGranted ( arc . ontologyID ( layoutKey ) ) ;
137136 const canEdit = hasUpdatePermission && state . graph . editable ;
138137
139- const handleEdgesChange : Diagram . DiagramProps [ "onEdgesChange" ] = useCallback (
140- ( edges ) => undoableDispatch ( setEdges ( { key : layoutKey , edges } ) ) ,
141- [ layoutKey , undoableDispatch ] ,
138+ const selected = useSelectSelected ( layoutKey ) ;
139+
140+ const handleSelectionChange = useCallback (
141+ ( selected : string [ ] ) => dispatch ( setSelected ( { key : layoutKey , selected } ) ) ,
142+ [ layoutKey , dispatch ] ,
142143 ) ;
143144
144- const handleNodesChange : Diagram . DiagramProps [ "onNodesChange" ] = useCallback (
145- ( nodes , changes ) => {
146- if (
147- // @ts -expect-error - Sometimes, the nodes do have dragging
148- nodes . some ( ( n ) => n . dragging ) ||
149- changes . some ( ( c ) => c . type === "select" )
150- )
151- // don't remember dragging a node or selecting an element
152- dispatch ( setNodes ( { key : layoutKey , nodes } ) ) ;
153- else undoableDispatch ( setNodes ( { key : layoutKey , nodes } ) ) ;
145+ const handleNodesChange = useCallback (
146+ ( changes : Diagram . NodeChange [ ] ) => {
147+ const dragging = changes . some (
148+ ( c ) => c . type === "position" && c . dragging === true ,
149+ ) ;
150+ const action = applyNodeChanges ( { key : layoutKey , changes } ) ;
151+ if ( dragging ) dispatch ( action ) ;
152+ else undoableDispatch ( action ) ;
154153 } ,
155154 [ layoutKey , dispatch , undoableDispatch ] ,
156155 ) ;
157156
157+ const handleEdgesChange = useCallback (
158+ ( changes : Diagram . EdgeChange [ ] ) =>
159+ undoableDispatch ( applyEdgeChanges ( { key : layoutKey , changes } ) ) ,
160+ [ layoutKey , undoableDispatch ] ,
161+ ) ;
162+
158163 const handleViewportChange : Diagram . DiagramProps [ "onViewportChange" ] = useCallback (
159164 ( vp ) => dispatch ( setViewport ( { key : layoutKey , viewport : vp } ) ) ,
160165 [ layoutKey , dispatch ] ,
@@ -171,13 +176,6 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
171176 [ layoutKey , dispatch ] ,
172177 ) ;
173178
174- const elRenderer = useCallback (
175- ( props : Diagram . SymbolProps ) => (
176- < StageRenderer layoutKey = { layoutKey } dispatch = { undoableDispatch } { ...props } />
177- ) ,
178- [ layoutKey , undoableDispatch ] ,
179- ) ;
180-
181179 const ref = useRef < HTMLDivElement > ( null ) ;
182180
183181 const calculateCursorPosition = useCallback (
@@ -264,7 +262,7 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
264262 ) ;
265263
266264 const handleClearSelection = useCallback (
267- ( ) => dispatch ( clearSelection ( { key : layoutKey } ) ) ,
265+ ( ) => dispatch ( setSelected ( { key : layoutKey , selected : [ ] } ) ) ,
268266 [ dispatch , layoutKey ] ,
269267 ) ;
270268
@@ -278,13 +276,15 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
278276 region : ref ,
279277 } ) ;
280278
279+ const ctxValue = useMemo (
280+ ( ) => ( { layoutKey, dispatch : undoableDispatch } ) ,
281+ [ layoutKey , undoableDispatch ] ,
282+ ) ;
283+
281284 return (
282- < div
283- ref = { ref }
284- onDoubleClick = { handleDoubleClick }
285- style = { { width : "inherit" , height : "inherit" , position : "relative" } }
286- >
287- < Base . Arc
285+ < Provider value = { ctxValue } >
286+ < ArcDiagram
287+ ref = { ref }
288288 viewportMode = { viewportMode }
289289 onViewportModeChange = { handleViewportModeChange }
290290 onViewportChange = { handleViewportChange }
@@ -294,6 +294,8 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
294294 // effects on the arc sizing and ensures that we position all the lines
295295 // in the correct place.
296296 viewport = { { ...state . graph . viewport , zoom : 1 } }
297+ selected = { selected }
298+ onSelectionChange = { handleSelectionChange }
297299 onEdgesChange = { handleEdgesChange }
298300 onNodesChange = { handleNodesChange }
299301 onEditableChange = { handleEditableChange }
@@ -305,14 +307,13 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
305307 visible = { visible }
306308 { ...dropProps }
307309 >
308- < Diagram . NodeRenderer > { elRenderer } </ Diagram . NodeRenderer >
309310 < Diagram . Background />
310311 < BaseControls x >
311- < Diagram . FitViewControl />
312- { hasUpdatePermission && < Diagram . ToggleEditControl /> }
312+ < Diagram . Controls . FitView />
313+ { hasUpdatePermission && < Diagram . Controls . ToggleEdit /> }
313314 </ BaseControls >
314- </ Base . Arc >
315+ </ ArcDiagram >
315316 < Controls state = { state } />
316- </ div >
317+ </ Provider >
317318 ) ;
318319} ;
0 commit comments