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
canvasMachine
onwards.A state machine in the browser for the visual editor - referred to as
editorAppMachine
onwards.
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.canvasApi
onwards.packages/atri-app-core/src/api/canvasApi.ts
- referred to asapp.canvasApi
onwards.
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.startDrag
call is made. This call informs theeditorAppMachine
that drag has started andeditorAppMachine
informs a piece of code written inbuilder.canvasApi
to start listening to mouse events. This piece of code frombuilder.canvasApi
also informs theapp.canvasApi
that drag has started using thewindow.postMessage
API.Once the mouse pointer enters the canvas, the
app.cavasApi
informs thebuilder.canvasApi
with aINSIDE_CANVAS
event.When the user leaves the mouse, an
upWhileDrag
event is fired by thecanvasMachine
. This event is handled by a piece of code inapp.canvasApi
that detects dropped component'sparent
. A component'sparent
can be described with three fields:canvasZoneId
: Theid
of the canvas zone on which the component has been dropped.id
: Theid
of the component that is theparent
of this component.index
: Aparent
component 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.postMessage
API. In the editor, this message is handled byeditor.canvasApi
.
The
editor.canvasApi
converts this information about the drag-drop activity that was completed successfully into an array ofAnyEvent
(The events saved in theevents.json
file 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 asforestHandler
onwards.The
forestHandler
receives awire
update with the details to create a new component. The code handleswire
updates 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.canvasApi
receives a message from the parent window calledCREATE_COMPONENT
and some code insideapp.canvasApi
callsapp.componentStoreApi.createComponent
function.The
app.componentStoreApi.createComponent
function informs theeditor-components
to 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