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.

  1. A state machine in the development server for the Atri app.

  2. A state machine in the development server for the visual editor.

  3. A state machine in the browser for the Atri app - referred to as canvasMachine onwards.

  4. 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:

  1. packages/pwa-builder-manager/src/canvasApi.ts - referred to as editor.canvasApi onwards.

  2. packages/atri-app-core/src/api/canvasApi.ts - referred to as app.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)

  1. The moment you start dragging a component from the list, a builder.canvasApi.startDrag call is made. This call informs the editorAppMachine that drag has started and editorAppMachine informs a piece of code written in builder.canvasApi to start listening to mouse events. This piece of code from builder.canvasApi also informs the app.canvasApi that drag has started using the window.postMessage API.

  2. Once the mouse pointer enters the canvas, the app.cavasApi informs the builder.canvasApi with a INSIDE_CANVAS event.

  3. When the user leaves the mouse, an upWhileDrag event is fired by the canvasMachine. This event is handled by a piece of code in app.canvasApi that detects dropped component's parent. A component's parent can be described with three fields:

    1. canvasZoneId: The id of the canvas zone on which the component has been dropped.

    2. id : The id of the component that is the parent of this component.

    3. index: A parent 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 by editor.canvasApi .

  4. The editor.canvasApi converts this information about the drag-drop activity that was completed successfully into an array of AnyEvent (The events saved in the events.json file is of type AnyEvent ).

Phase 2 - Handle AnyEvent (Convert AnyEvent into `app.componentStoreApi` format)

  1. 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 as forestHandler onwards.

  2. The forestHandler receives a wire update with the details to create a new component. The code handles wire updates and converts the node from the component tree into a format that can be sent over to the app.canvasApi.

Phase 3 - Render changes in the CanvasZone

  1. The app.canvasApi receives a message from the parent window called CREATE_COMPONENT and some code inside app.canvasApi calls app.componentStoreApi.createComponent function.

  2. The app.componentStoreApi.createComponent function informs the editor-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