boehnenelton2024

BEJSON Flow: Principles of Visual Diagramming

While standard BEJSON schemas excel at defining static data structures, many systems are better described by their processes and relationships. BEJSON Flow is a conceptual application of these principles, using a visual, node-based editor to model dynamic systems like flowcharts, organizational charts, or system architectures. The resulting diagram is then serialized into a portable BEJSON file.

The Core Model: Nodes and Edges

A visual diagramming tool is fundamentally composed of two data types: nodes (Shapes) and edges (Connectors). The entire state of the canvas can be represented by two simple arrays: one for shapes and one for connectors.


export interface Shape {
    id: string;
    type: 'Rectangle' | 'Ellipse' | 'Diamond';
    x: number;
    y: number;
    width: number;
    height: number;
    text: string;
    // ... other style properties
}

export interface Connector {
    id: string;
    from: string; // ID of the starting shape
    to: string;   // ID of the ending shape
    // ... other style properties
}
      

Rendering Strategy: The Power of SVG

For node-based editors, SVG (Scalable Vector Graphics) is often a superior choice to the Canvas API. Because each shape and line in an SVG is a distinct DOM element, you can easily attach event handlers (`onClick`, `onMouseDown`) directly to them. This simplifies interactions like selecting, dragging, and connecting shapes. The rendering is managed declaratively by mapping the state arrays to SVG elements.



  {/* Render all connectors first */}
  {connectors.map(conn => )}
  
  {/* Render all shapes on top */}
  {shapes.map(shape => )}

      

Key User Interactions

A fluid user experience depends on correctly handling a sequence of pointer events to manage different interaction states.

1. Creating Nodes (Drag and Drop)

New shapes are typically added by dragging them from a sidebar palette onto the canvas. This uses the native HTML Drag and Drop API.

1. **Sidebar:** The shape element has an `onDragStart` event that stores the shape type (e.g., "Rectangle") in the `dataTransfer` object. 2. **Canvas:** The canvas has an `onDrop` event that reads the shape type from `dataTransfer` and creates a new `Shape` object in the state at the mouse coordinates where it was dropped.

2. Creating Connections (State Machine)

Drawing a line between two shapes is a stateful process:

1. **Start:** A user presses `onMouseDown` on a shape's anchor point. The application enters a "drawing connector" mode, storing the `fromShapeId` and the line's starting point in a temporary state object. 2. **Draw:** As the user moves the mouse, the `onMouseMove` event on the canvas continuously updates the end point of the temporary line, providing visual feedback. 3. **Finish:** When the user releases the mouse with `onMouseUp` over a valid target shape, the `toShapeId` is identified. A final `Connector` object is created with the `from` and `to` IDs and added to the main `connectors` state array. If the mouse is released elsewhere, the process is cancelled.

3. Editing Properties (The Inspector)

When a user selects a shape or connector, an "Inspector" panel appears. This is a context-aware form that displays input fields for the properties of the selected item (e.g., text, color, line style). Changing a value in the inspector directly updates the corresponding property of the object in the state, triggering an immediate visual refresh on the canvas.

Serialization to BEJSON

The final step is to convert the visual diagram into a portable BEJSON file. The `shapes` and `connectors` arrays are serialized as `Values` in a BEJSON file with a `Format_Type` of `BEFlow_Diagram`. This makes the visual process model machine-readable and easy to store, version, or share.


{
  "Format": "BEJSON",
  "Format_Version": "104a",
  "Format_Creator": "Elton Boehnen",
  "Format_Type": "BEFlow_Diagram",
  "Records_Type": ["Shape", "Connector"],
  "Fields": [
    { "name": "Record_Type_Name", "type": "string" },
    { "name": "id", "type": "string" },
    { "name": "shape_type", "type": "string" },
    { "name": "text", "type": "string" },
    { "name": "x", "type": "integer" },
    { "name": "y", "type": "integer" },
    { "name": "from_shape_id", "type": "string" },
    { "name": "to_shape_id", "type": "string" }
  ],
  "Values": [
    ["Shape", "shape-1", "Rectangle", "Start Process", 50, 50, null, null],
    ["Shape", "shape-2", "Diamond", "Is valid?", 50, 150, null, null],
    ["Connector", "conn-1", null, null, null, null, "shape-1", "shape-2"]
  ]
}