Creating the drag-drop feature in Atri's visual builder
High-level architecture
There are 4 state machines that run when you start the Atri server in development.
A state machine in the development server for the Atri app.
A state machine in the development server for the visual editor.
A state machine in the browser for the Atri app - referred to as
canvasMachineonwards.A state machine in the browser for the visual editor - referred to as
editorAppMachineonwards.
Inside the visual editor, the Atri app runs inside an iframe. The window.postMessage API is used to communicate between the editor's window and the Atri app's window. The API for this communication is located in the following two files:
packages/pwa-builder-manager/src/canvasApi.ts- referred to aseditor.canvasApionwards.packages/atri-app-core/src/api/canvasApi.ts- referred to asapp.canvasApionwards.
How does drag-drop work?
The flow can be broken into three phases:
Phase 1 - User activity to AnyEvent array (Window - iframe communication)
The moment you start dragging a component from the list, a
builder.canvasApi.startDragcall is made. This call informs theeditorAppMachinethat drag has started andeditorAppMachineinforms a piece of code written inbuilder.canvasApito start listening to mouse events. This piece of code frombuilder.canvasApialso informs theapp.canvasApithat drag has started using thewindow.postMessageAPI.Once the mouse pointer enters the canvas, the
app.cavasApiinforms thebuilder.canvasApiwith aINSIDE_CANVASevent.When the user leaves the mouse, an
upWhileDragevent is fired by thecanvasMachine. This event is handled by a piece of code inapp.canvasApithat detects dropped component'sparent. A component'sparentcan be described with three fields:canvasZoneId: Theidof the canvas zone on which the component has been dropped.id: Theidof the component that is theparentof this component.index: Aparentcomponent has all its children in an array. This index stores the position of this component inside its parent.This information is sent back to the editor using
window.postMessageAPI. In the editor, this message is handled byeditor.canvasApi.
The
editor.canvasApiconverts this information about the drag-drop activity that was completed successfully into an array ofAnyEvent(The events saved in theevents.jsonfile is of typeAnyEvent).
Phase 2 - Handle AnyEvent (Convert AnyEvent into `app.componentStoreApi` format)
All the events that might have originated from inside/outside the browser are handled in the file
packages/pwa-builder-manager/src/handleBrowserForestManagerEvents.ts- referred to asforestHandleronwards.The
forestHandlerreceives awireupdate with the details to create a new component. The code handleswireupdates and converts the node from the component tree into a format that can be sent over to theapp.canvasApi.
Phase 3 - Render changes in the CanvasZone
The
app.canvasApireceives a message from the parent window calledCREATE_COMPONENTand some code insideapp.canvasApicallsapp.componentStoreApi.createComponentfunction.The
app.componentStoreApi.createComponentfunction informs theeditor-componentsto render a new component.
Relevant GitHub discussions
https://github.com/Atri-Labs/atrilabs-engine/discussions/249
https://github.com/Atri-Labs/atrilabs-engine/discussions/675