DEV Community

Alexey Boyko
Alexey Boyko

Posted on • Updated on

JavaScript SVG diagram editor 3.9 KB (open source library)

JavaScript SVG diagram editor 3.9 KB

Demo | GitHub

Experiments with creating of diagram editor with Blazor Webassembly (Blazor Webassembly SVG Drag And Drop, Blazor WebAssembly: Connecting Lines in SVG) have shown that Blazor is not suitable for intensive DOM manipulation.

It was known in advance that there would be a subsidence: WebAssembly has no direct access to DOM, WebAssembly uses JavaScript interop to change the DOM. The delays were so high that dragging on mobile slowed down after the third shape was added.

Rejection of frameworks (for this task)

Suspicion that Blazor’s virtual DOM incorrectly tracks changes (maybe Blazor is trying to update more DOM objects than needed) is not justified. Event throttling and other Microsoft recommendations (ASP.NET Core Blazor performance best practices) did not help.

There are so many nuances to just update one attribute:

  • virtual DOM, trees, subtrees,
  • "loops" of changes (changes in one place lead to changes in another, changes there lead to a change in the first),
  • nuances of passing parameters to components and tracking their changes, subscription / unsubscription.

A trivial JavaScript task just got too complicated with Blazor. The framework only gets in the way here. Excessive complexity is manifested not only in Blazor, but also in other frameworks. If you haven’t seen it yet, check out talk of creator of Svelte "Rich Harris — Rethinking reactivity". In the video there is an example with the brakes of the React application: there the DOM is rebuilt on the fly when entering into the text field. Here, the DOM is rebuilt on the fly as the mouse moves (dragging the shape).

Vanilla-JavaScript prototype showed no signs of slowing down at 1000 shapes.

After years of using Angular, doing something in vanilla JavaScript seemed like a regression. All right: manually to read HTML attributes and to hang up handlers. But how to develop without components, without IoC, without templates? Most importantly — without “reactivity”? However, withdrawal. passed quickly enough. It turned out that there is life beyond the borders of frameworks, and in some ways more complete.

Rejection of TypeScript (of TypeScript compiler)

Type Checking, intellisense and other tooling — that’s why TypeScript is loved. TypeScript has interfaces, literals, and even generics. TypeScript is so addictive, that it’s easy to forget that TypeScript is just a way to describe types for JavaScript. Yes, it says on the main page of typescriptlang.org: "TypeScript is JavaScript with syntax for types".

JSDoc provides all the same features (type checks, intellisense, etc.).

An example of “typing” with JSDoc:

/**
 * @param {SVGGraphicsElement} svgEl
 * @param {number} transform
 * @param {SVGSVGElement=} svg pass if svgEl not yet in DOM
 * @returns {SVGTransform}
 */
 function ensureTransform(svgEl, transform, svg) {
    ...
    return ...;
}
Enter fullscreen mode Exit fullscreen mode

You can even describe types in TypeScript and use them in js files:

// ts-file
interface IDiagram {
    on(evtType: DiagramEventType,
        listener: EventListenerOrEventListenerObject): this;
    shapeAdd(param: PresenterShapeAppendParam): IDiagramShape;
    shapeDel(shape: IDiagramShape): void;
    shapeConnect(param: DiagramShapeConnectParam): void;
}
// js-file - Diagram implements IDiagram
/** @implements {IDiagram} */
export class Diagram {
    
}
Enter fullscreen mode Exit fullscreen mode

In this case, both "find all references" and "renaming" and "checking that the object implements the interface" will work (at least in Visual Studio Code everything worked out of the box).

Pros of not using the TypeScript compiler:

  • JS code is exactly what you wrote,
  • speeds up development — no need to wait for compilation,
  • no need for map files, easier to debug.

JSDoc is not as concise as TypeScript, has unusual syntax, has worse IDE support.
A mixed approach turned out to be convenient:

  • type descriptions in ts-files in TypeScript
  • real code in JavaScript with JSDoc.

DgrmJS

The result is a vanilla-JavaScript library.

DgrmJS is a JavaScript library for creating SVG diagrams.
The main goal of the library is to set up workflows in BPM (Business Process Management) systems.

  • works on desktop and mobile
  • has no dependency
  • small size
  • shapes are created declaratively

Main idea

  • Allow developers to use standard SVG objects and features to declaratively create shapes that will be used in the diagram. To create shape, developers should add special data- attributes to standard SVG markup. So any svg images can be used as a shape in a diagram.
  • DgrmJS dispatch events, like ‘shape is selected’ or ‘shape is connecting to another shape’. Developers can use these events to implement their own logic, for example, make a JSON description of the workflow.

An example of a declarative description of the "circle" shape template:

<g data-templ="circle">
    <circle ... />
    <text data-key="text"></text>

    <!--
        out connector
        data-connect-point - point into shape where connector line starts
        data-connect-dir - direction of connector line
    -->
    <circle
        data-connect="out"
        data-connect-point="60,0"
        data-connect-dir="right" ...>
    </circle>

    <!--
        in connector
    -->
    <circle
        data-connect="in"
        data-connect-point="-60,0"
        data-connect-dir="left" ...>
    </circle>
</g>
Enter fullscreen mode Exit fullscreen mode

The figure below shows two shapes (two circles) created using the “circle” template. By clicking on the figure — output connectors are displayed, from where you can pull out the connecting line. When you hover the end of the connecting line over the figure, the input connectors are displayed.

Connection of shapes

Code for adding a shape to a diagram:

import { svgDiagramCreate } from './diagram/svg-presenter/svg-diagram-factory.js';
const diagram =
    svgDiagramCreate(document.getElementById('diagram'));
diagram.shapeAdd({
    templateKey: 'circle',
    position: { x: 120, y: 120 }
});
Enter fullscreen mode Exit fullscreen mode

More examples on GitHub.

Conclusion

The article does not call for abandoning frameworks or TypeScript. Long-term adherence to the same paradigms, approaches, frameworks, as a result, can “blind”, narrow the scope of vision. Often we don’t even make a choice — try to find a vacancy for Blazor WebAssembly or Svelte developer, you can only choose between React and Angular (also Vue).

It’s good to have the opportunity to experiment. It was interesting to emerge from the “reactive approach” bubble.

Other articles about dgrm.net

Top comments (0)