v1.6.0

<bmx-diagram>

A diagram designer: flowcharts, BPMN-style processes with swimlanes, and network diagrams, drawn from a palette of over forty shapes. Shapes are connected by dragging from the dot on a side; connectors route themselves around everything in the way (elbow, straight or curved), with labels and arrowheads. Alignment guides, a snapping grid, multiple selection, copy and paste, undo, automatic arrangement in layers, an outline that reads the whole diagram as text, and SVG, PNG, JSON and Mermaid out. A Mermaid flowchart pasted in, or set in mermaid, becomes an editable diagram.

11 properties · 3 events · 22 methods · 5 parts

Example

Show as Mermaid Download PNG


Read-only, with shapes you can open

Click a device.

Two copies kept in step

Each change is a short list of operations. Here they go straight from one copy to the other; between people they would go over your own connection.

Show markup
<bmx-diagram
  id="ex-diagram"
  label="Order process"
  style="--bmx-diagram-height: 36rem"
  mermaid="flowchart LR
    subgraph customer [Customer]
      start([Order placed]) --> pay[/Card payment/]
    end
    subgraph shop [Shop]
      pay --> check{Paid?}
      check -->|Yes| pick[Pick and pack]
      check -->|No| remind[Send a reminder] -.-> pay
    end
    subgraph courier [Courier]
      pick --> ship[Deliver] --> done([Delivered])
    end"
></bmx-diagram>

<div class="row" style="margin-block-start: 0.75rem; gap: 0.5rem; flex-wrap: wrap">
  <bmx-button variant="outline" tone="neutral" id="ex-diagram-mermaid">Show as Mermaid</bmx-button>
  <bmx-button variant="outline" tone="neutral" id="ex-diagram-png">Download PNG</bmx-button>
</div>
<pre class="note" id="ex-diagram-out" style="white-space: pre-wrap"></pre>

<h3>Read-only, with shapes you can open</h3>

<bmx-diagram id="ex-network" readonly label="Office network" style="--bmx-diagram-height: 22rem"></bmx-diagram>
<p class="note" id="ex-network-out">Click a device.</p>

<h3>Two copies kept in step</h3>

<p class="note">Each change is a short list of operations. Here they go straight from one copy to the other; between people they would go over your own connection.</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 0.75rem">
  <bmx-diagram id="ex-left" label="First copy" shapes="flowchart" style="--bmx-diagram-height: 24rem"></bmx-diagram>
  <bmx-diagram id="ex-right" label="Second copy" shapes="flowchart" style="--bmx-diagram-height: 24rem"></bmx-diagram>
</div>

<script type="module">
  await customElements.whenDefined('bmx-diagram');

  const diagram = document.getElementById('ex-diagram');
  const out = document.getElementById('ex-diagram-out');
  document.getElementById('ex-diagram-mermaid').addEventListener('click', async () => (out.textContent = await diagram.toMermaid()));
  document.getElementById('ex-diagram-png').addEventListener('click', () => diagram.download('png', 'order-process'));

  const network = document.getElementById('ex-network');
  const device = (id, shape, x, y, text, tone) => ({ id, shape, x, y, width: 64, height: 64, text, tone });
  network.diagram = {
    nodes: [
      device('internet', 'internet', 40, 120, 'Internet', 'blue'),
      device('firewall', 'firewall', 200, 120, 'Firewall', 'red'),
      device('switch', 'switch', 360, 120, 'Core switch'),
      device('web', 'server', 520, 20, 'Web server', 'green'),
      device('db', 'database-server', 520, 120, 'Database', 'green'),
      device('wifi', 'wireless', 520, 220, 'Wi-Fi'),
      device('laptop', 'laptop', 680, 220, 'Laptops'),
    ],
    edges: [
      { from: 'internet', to: 'firewall', endArrow: 'none' },
      { from: 'firewall', to: 'switch', endArrow: 'none' },
      { from: 'switch', to: 'web', endArrow: 'none' },
      { from: 'switch', to: 'db', endArrow: 'none' },
      { from: 'switch', to: 'wifi', endArrow: 'none' },
      { from: 'wifi', to: 'laptop', endArrow: 'none', dashed: true },
    ],
  };
  network.addEventListener('bmxDiagramNodeClick', e => (document.getElementById('ex-network-out').textContent = `Opened ${e.detail.node.text}.`));

  const left = document.getElementById('ex-left');
  const right = document.getElementById('ex-right');
  await left.loadMermaid('flowchart TD\n  a([Idea]) --> b[Draft] --> c{Approved?}\n  c -->|Yes| d([Publish])\n  c -->|No| b');
  await right.setDiagram(await left.getDiagram());
  for (const [from, to] of [[left, right], [right, left]]) from.addEventListener('bmxDiagramChange', e => to.applyOps(e.detail.ops));
</script>

The diagram is plain JSON (diagram), and every change is reported as a list of operations (bmxDiagramChange) that another copy applies with applyOps - the transport between people editing together is yours. Nothing is sent anywhere by the element itself.

With readonly it shows a diagram to read, pan and zoom, and reports shapes the reader opens (bmxDiagramNodeClick).

Properties

PropertyAttributeTypeDefaultDescription
connector connector BmxDiagramRoute 'orthogonal' How new connectors are drawn.
diagram diagram Partial<BmxDiagramData> | string — The diagram: nodes, edges and lanes. JSON in markup. Kept up to date as it is edited.
grid grid number 20 The grid spacing; 0 hides it.
label label string — What the diagram is, for screen readers.
mermaid mermaid string — A Mermaid flowchart to start from, when diagram is empty.
readonly readonly boolean false Show the diagram to read, pan and zoom, without editing.
shapes shapes readonly BmxDiagramShapeCategory[] | string ['flowchart', 'basic', 'bpmn', 'network'] The shape groups in the palette, in order: any of flowchart, basic, bpmn, network.
snap snap boolean true Shapes snap to the grid as they move.
strings strings Partial<BmxDiagramStrings> | string — Words the designer shows or says: any of BmxDiagramStrings. JSON in markup.
toolbar toolbar boolean true Show the command bar.
validateConnection property only BmxDiagramConnectionCheck — Decides whether two nodes may be connected.

Events

EventDetailDescription
bmxDiagramChange BmxDiagramChangeDetail The diagram changed. ops turn the previous diagram into this one.
bmxDiagramNodeClick BmxDiagramNodeDetail A shape was opened: clicked in a read-only diagram, or Enter on it.
bmxDiagramSelect BmxDiagramSelectDetail The selection changed.

Methods

MethodSignatureDescription
addEdge addEdge(edge: Partial<BmxDiagramEdge> & { from: BmxDiagramEnd | string; to: BmxDiagramEnd | string; }) => Promise<string | null> Connects two nodes. Resolves with the connector's id, or null if refused.
addNode addNode(node: Partial<BmxDiagramNode>) => Promise<string> Adds a node. Give at least shape; it is placed in view if x and y are left out. Resolves with its id.
applyOps applyOps(ops: readonly BmxDiagramOp[]) => Promise<void> Applies changes made somewhere else - operations from another copy's bmxDiagramChange. They are not added to this copy's undo, and raise no change event here.
arrange arrange(direction?: BmxDiagramDirection) => Promise<void> Arranges the diagram in layers, top to bottom (TB) or left to right (LR). Lanes keep their nodes.
clear clear() => Promise<void> Removes everything. Undo can bring it back.
download download(format?: BmxDiagramExportFormat, name?: string) => Promise<void> Saves the diagram as a file: svg, png, json or mermaid (.mmd).
fit fit() => Promise<void> Zooms and scrolls to show the whole diagram.
getDiagram getDiagram() => Promise<BmxDiagramData> The diagram, as JSON-ready data.
getSelection getSelection() => Promise<string[]> The ids selected.
loadMermaid loadMermaid(source: string, append?: boolean) => Promise<readonly string[]> Reads a Mermaid flowchart. With append, it goes beside what is there; otherwise it replaces it. Resolves with any lines that could not be read.
redo redo() => Promise<void>
removeItems removeItems(ids: readonly string[]) => Promise<void> Removes nodes, connectors or lanes by id. A node's connectors go with it.
reveal reveal(id: string) => Promise<void> Brings a node into view and selects it.
setDiagram setDiagram(diagram: Partial<BmxDiagramData> | string) => Promise<void> Replaces the diagram. Undo can step back from it.
setFocus setFocus(options?: FocusOptions) => Promise<void>
setSelection setSelection(ids: readonly string[]) => Promise<void> Selects by id.
toBlob toBlob(type?: "png" | "svg", scale?: number) => Promise<Blob> The diagram as a PNG or SVG file. scale multiplies the PNG's size (default 2).
toMermaid toMermaid() => Promise<string> The diagram as a Mermaid flowchart.
toSvg toSvg() => Promise<string> The diagram as a standalone SVG document.
undo undo() => Promise<void>
updateItem updateItem(id: string, patch: Record<string, unknown>) => Promise<void> Changes a node, a connector or a lane by id.
zoomTo zoomTo(zoom: number) => Promise<void> Sets the zoom (1 is actual size), about the middle of the view.

CSS shadow parts

PartDescription
outline the diagram as a list.
palette the shapes to drag in.
panel the properties of what is selected.
stage the drawing area.
toolbar the row of commands.

CSS custom properties

PropertyDescription
--bmx-diagram-background Behind the drawing.
--bmx-diagram-grid The grid's dots.
--bmx-diagram-height How tall the designer is. Default 40rem.
--bmx-diagram-line Connectors.
--bmx-diagram-selection Selection outlines, handles and guides.