Skip to main content

Release 1.2.2

· 2 min read

Release 1.2.2 is now available, with new background components for Angular and Vue, as well some reactivity enhancements to Angular and Svelte, overlays, and some minor fixes and improvements. Also in this release we've pushed a version of our popular AI Agent Builder for Svelte and Vue.

General​

  • Internal updates to the update*** methods to ensure that any associated type change is grouped along with the updates in a transaction.
  • Added new relayoutOnVertexRemove option to Surface and Paper. Useful when you're using a static layout such as the Hierarchy layout.

Vue​

  • Added BackgroundComponent, GridBackgroundComponent, ImageBackgroundComponent and TiledImageBackgroundComponent
  • Internal updates to the re-render mechanism
  • Introduced edge specific VueEdgeWrapperProps interface for overlay component props

Angular​

  • Added BackgroundComponent, GridBackgroundComponent, ImageBackgroundComponent and TiledImageBackgroundComponent
  • Added new $edgeData(), $sourceData() and $targetData() signals to the BaseAngularOverlayComponent. These give you reactive access to the data for the edge and for its source and target vertices.

Svelte​

  • Added BackgroundComponent, GridBackgroundComponent, ImageBackgroundComponent and TiledImageBackgroundComponent
  • Added usePaper hook
  • Added hide prop to the snippet passed to SurfacePopup.
  • Updated Svelte component overlays to make the edge (and its source/target) reactive, so the overlay can now show a live view of the edge's backing data or that of its source/target.

Issue Fixes​

  • #5 - Lasso consumes tap event on edge
  • Vue/Angular Fix for the SurfacePopup's hide function - it was not locating the ref correctly.

Breaking​

  • The refreshLayoutOnEdgeConnect Surface/Paper render option was renamed to relayoutOnEdgeConnect

Release 1.2.1

· 7 min read
sporritt

We've pleased to announce the release of VisuallyJs 1.2.1, containing several useful new features and a couple of great starter apps.

  • Popups - components that VisuallyJs automatically keeps aligned with some vertex in your canvas, adjusting for pan/zoom as necessary
  • A brand AI Agent Builder starter app was added
  • Component overlays for Vue and Svelte - use components as overlays, fully wired into the reactive state and the VisuallyJs data model
  • Support for marking up properties inside inspectors as belonging to ports on a vertex, allowing you to edit ports in your data model alongside the nodes they belong to
  • Several new signals were added to the BaseVertexComponent in Angular, providing reactive state that exposes the edges attached to the vertex.
  • We added a new renderer - the Paper component. This is a "static" version of the Surface: nodes, groups and edges are all rendered in the same way, but the view does not support pan or zoom, and the canvas is scaled automatically so that the content is always visible in the viewport.
  • We dusted off the FIFA World Cup visualization from 2018 and released it as a starter app for VisuallyJs.

Popups​

AI Agent Builder

AI Agent Builder​

AI Agent Builder

Everyone's got one of these, and now we do too! But we're pleased to be able to report that this app required far fewer lines of code than similar demonstrations from other libraries (almost 1/5th of the size of the codebase from another well-known diagramming library!). Our AI Agent Builder is fully featured and ready for you to drop in to your app, or use as a base for your own. Check it out here - a link to the repository is on the demo page.

Component overlays​

Support for using components as overlays has been in our Angular and React integrations for some time now. In 1.2.1 we've also added this feature to our Vue and Svelte integrations.

Port Inspectors​

Inspectors provide a convenient means of wiring up property editors to the vertices in your app. Prior to 1.2.1, though, if you wanted to edit the properties of ports on your vertices, you'd have to setup a separate rendering condition for them. In 1.2.1 we've added support for a new vjs-port attribute, which allows you to tell VisuallyJs that some input field in your inspector pertains to a port, not the vertex:

<div>
<input vjs-att="label"/>
<input vjs-port="yes" vjs-att="label"/>
<input vjs-port="no" vjs-att="label"/>
</div>

Here, our inspector allows us to edit the 'label' property on the vertex itself, as well as each of the vertex's ports.

Angular Signals​

With every release we're modernising and tightening up our Angular integration, and 1.2.1 is no exception - we've added several signals to the BaseVertexComponent, allowing you to reactively access the edges attached to a given vertex. This makes it easy to do things like showing an 'add child' button if the node has no children, for example:

@Component({
template:`<div>
<h1>{{$data().label}}</h1>
@if(sourceEdges().length === 0) {
<button (click)="addChild">Add Child</button>
}`
})

export class MyComponent extends BaseNodeComponent {}

You can get the list of source/target edges connected to just the vertex itself, or all of the source/target edges connected to the vertex and any of its ports.

We've also updated the SurfaceComponent, PaperComponent and DiagramComponent to convert the data and url inputs into signals. You can still use them as plain old inputs, but now you can wire them up to signals and have the component reload dynamically.

Paper Component​

Sometimes you want to render a static view of your dataset, without given users the ability to pan, zoom or drag elements around - but you still want them to be able to interact with, and update, the model. The Paper component gives you that. It's a version of the Surface without pan, zoom or element dragging, and it automatically scales its canvas to fit inside the bounds of the viewport (unless you don't want it to; you can switch off scaling and have it fill its natural size, and the parent will scroll if needs be).

We're using this component in the Group Stage and Journey View components of our FIFA World Cup visualizer - we look forward to seeing what you build with it!

FIFA World Cup​

We released this for JsPlumb back in 2018 but we've always had a soft spot for some of the visualizations it contains, so we've updated it for VisuallyJs and added to our list of starter apps. It's a great showcase for the range of things you can easily build with VisuallyJs, containing several different views of the data, and integrating natively with React, Angular, Vue and Svelte.

FIFA World Cup

The group stage view, with its high "data-ink" ratio, is one of our favourites:

FIFA Group Stage

We're also quite partial to the "journey view", which takes advantage of our new Paper component to render a static view:

FIFA Journey Viewer

Check out the demonstration here. There's a link to the repo if you want to clone it.

Changelog​

General​

  • The EVENT_GRAPH_CLEARED event, fired by the model, now passes the model that fired it as a payload.
  • The Surface was updated to fix an issue where it was holding on to stale viewport dimensions: calling zoomToFit after external change to viewport element size would use the cached dimensions.
  • Added the data-vjs-no-events attribute which can be set on any element inside a node vertex, marking it as excluded from firing mouse events for that vertex.
  • Updated centerOn and centerOnAndZoom in the Surface to support optional centering on multiple elements as opposed to just a single one.
  • Added support for a vjs-port attribute on controls inside an inspector. This allows you to edit properties on ports inside your vertices from the vertex inspector.

Breaking​

  • The previous Index class was renamed to GraphSearchIndex
  • In BaseAngularOverlayComponent, the surface member was renamed to ui; its type is now derived from a type parameter on the class (which you declare when you create a subclass)

React​

  • The useZoom hook no longer requires that a ui can be passed in: it can derive one from the context.
  • overlays is now marked optional in the ReactEdgeMapping interface used in view options
  • Added SurfacePopup component, an automatic mechanism for launching a popup on a vertex component and keeping it located correctly as the user pans, zooms and drags the vertex.
  • InspectorComponent updated to function correctly inside a PaperProvider or PaperComponent

Vue​

  • Added SurfacePopup component, an automatic mechanism for launching a popup on a vertex component and keeping it located correctly as the user pans, zooms and drags the vertex.
  • Added support for Vue component overlays

Svelte​

  • Added SurfacePopup component, an automatic mechanism for launching a popup on a vertex component and keeping it located correctly as the user pans, zooms and drags the vertex.
  • Added support for Svelte component overlays

Angular​

  • Added SurfacePopup component, an automatic mechanism for launching a popup on a vertex component and keeping it located correctly as the user pans, zooms and drags the vertex.
  • Added several new signals to the BaseVertexComponent - sourceEdges(), allSourceEdges(), targetEdges() and allTargetEdges(). With these signals you can dynamically respond to connectivity changes inside your templates.
  • Added PaperComponent - a fixed view version of SurfaceComponent, with the same support for rendering but no pan/zoom, and which automatically adjusts its dimensions to fit into its viewport. The "data" and "url" inputs on the SurfaceComponent, PaperComponent and DiagramComponent of the Angular integration were updated to be reactive - you can hook them up to some state and have the component reload the dataset automatically.

Release 1.2.0

· 5 min read
sporritt

This release focuses on a few main things:

Hooks and Reactivity​

We've updated each of our integrations to be more reactive - the React hooks, for instance, now return reactive state instead of Promises and the Angular service exposes its various members as signals, instead of getters with callbacks. Additionally, we've added a useVisuallyJsUpdate() hook to each of our integrations, allowing you to easily extract a dynamically updating value from the underlying model.

Inspectors​

Our Vue and Svelte inspectors have been updated to interoperate seamlessly with a piece of reactive state, cutting down on the amount of boilerplate you're required to write

Starter apps​

Each of our library integrations - Vue, React, Svelte and Angular - offers the same full list of starter apps, and all of our starter apps have been updated to take advantage of the many updates 1.2.0 has to offer.

Changelog​

General updates​

  • Circular layout has new centerContent option, to avoid placing nodes in negative space (useful when used inside a Group in particular)
  • New config groupProperty added to model options. You can set the name of the property that identifies group membership in your node/group data, if the default value of group clashes with your dataset.
  • Added SelectionGenerator type, defining the generator function that can be used to fill a VisuallyJsSelection
  • Updated transaction handling to ensure the transaction(...) method could be nested cleanly
  • Added functionality to the Palette in "tap" mode where you can tap an item again to exit the drag (previously you'd have to press Escape)

React​

  • data and url are now reactive props in the React SurfaceComponent. A small but very powerful change, allowing much greater composability of the SurfaceComponent in your apps
  • Added new React PaperComponent, which functions as a static version of a surface - no pan/zoom or dragging, and the content is zoomed to fit the viewport at all times.
  • Added new useVisuallyJsUpdate hook. This hook lets you respond to updates in the underlying model, and is handy for such use cases as components which display information about the current state of the model.
  • (breaking) The useZoom(), useVisuallyJsModel() and useDiagram() React hooks now return a reactive state object instead of a Promise.

Angular​

  • Added new useVisuallyJsUpdate hook. This hook lets you respond to updates in the underlying model, and is handy for such use cases as components which display information about the current state of the model.
  • Added new useZoom hook, providing access to a signal containing the current zoom for the UI in context.
  • Converted the VisuallyJsService to expose model/surface/paper/diagram as signals, and deprecated the previous callback approach to accessing these members.
  • Updated the Gantt starter app to have feature parity with the React Gantt

Svelte​

  • Added new useVisuallyJsUpdate hook. This hook lets you respond to updates in the underlying model, and is handy for such use cases as components which display information about the current state of the model.
  • Added new NetworkInfrastructure and SupplyChain starter apps. These are clones of the same apps that previously only existed for React/Angular, demonstrating how to build integrated dashboards with various VisuallyJs components sharing a single model.
  • Added BPMN starter app
  • Updated the Gantt starter app to have feature parity with the React Gantt
  • Updated all of the chart components to source their data from a model in scope, when data/url not provided
  • Replaced all usages of <slot></slot> elements with the modern {@render children?.()} syntax
  • Updated InspectorComponent to optionally take a 2-way state object defining the current selection. This reduces the amount of boilerplate code required to use the inspector.
  • Updated the SankeyChartComponent to set the pivot property to be reactive
  • Added Mindmap starter app
  • (breaking) The useZoom() hook no longer takes ui as a prop; it finds the UI from the context

Vue​

  • Added new useVisuallyJsUpdate composable
  • Updated the Gantt starter app to have feature parity with the React Gantt
  • Updated InspectorComponent to support passing in a v-model, which is a reactive object that references the current selected object. Using this, you can do away with the need for refresh and renderEmptyContainer functions (and that approach is now deprecated)
  • Added BPMN starter app
  • Added useSurface, usePaper and useDiagram composables, for access to the UI in scope
  • (breaking) Updated useZoom to not take a ui as argument, instead resolving the UI from the context
  • Added ERD starter app - an entity relationship diagram
  • Added Mindmap starter app
  • Added new NetworkInfrastructure starter app

Breaking​

  • The useZoom(), useVisuallyJsModel() and useDiagram() React hooks now return a reactive state object instead of a Promise.
  • The useZoom() hook in the Svelte integration no longer takes ui as a prop; it finds the UI from the context
  • The useZoom() composable in the Vue integration no longer takes a ui as argument, instead resolving the UI from the context

Deprecated​

  • The getModel, getSurface, getPaper, getInspector and getDiagrams methods of the Angular VisuallyJsService are deprecated. Use the signals based approach instead.
  • The refresh and renderEmptyContainer functions in the InspectorComponent for both the Vue and Svelte integrations are deprecated; see above for alternatives.

Release 1.1.4

· One min read
sporritt

Release 1.1.4 of VisuallyJs is now available, containing several nice updates to the canvas and its plugins.

Miniview​

  • Lasso is shown on miniview while it is being used (can be switched off)

Miniview lasso

  • Miniview now highlights vertices that are selected in the canvas

Miniview lasso

Lasso​

  • Added support for the lasso inside of groups

Autopan​

  • Added support for automatically panning the surface when the user lassos outside of the visible area.
  • Added support for auto pan of the canvas when a user is dragging a new edge or relocating an existing edge.

Miscellaneous​

  • UI fires EVENT_EDGE_DRAG_START, EVENT_EDGE_DRAG, EVENT_EDGE_DRAG_END, EVENT_EDGE_DRAG_ABORT events during edge drag lifecycle now
  • Added addVertexDragFilter(f:VertexDragFilter<BrowserElement>) method to Surface
  • Diagrams do not, by default, draw a border around a selected edge now. Use edges.highlightSelected:true in your Diagram options to enable this behaviour.
  • Improved the way drag handles for edges are drawn to make it easier for users to locate them.

Release 1.1.3

· One min read
sporritt
  • Release 1.1.3 contains a bugfix for custom port separators.

Release 1.1.2

· 3 min read
sporritt

In release 1.1.1 we added support for rotating label and custom overlays to match their connectors, which we thought was a cool feature. After configuring it in our Flowchart builder starter app, though, we decided to make a small update.

Consider this screenshot and have a look at the labels:

Rotating labels

...they are all faithfully rotated to match the slope of the connector at the points at which they are located, but that results in some of them being upside down - which is not perhaps the best UX. So, we've updated the label rotation code to support the concept of "strict" vs "legible" rotation. If you just set labelsRotatable:true you will now get "legible" rotation - the label is never rotated so that it is upside down, or difficult to read. In practice this means that any rotation between 90 degrees and 270 degrees is flipped:


import { DiagramComponent } from "@visuallyjs/browser-ui-react"
import "@visuallyjs/browser-ui/css/visuallyjs.css";

export default function App() {

const data = ...

const options = {
edges: {
showLabels: true,
labelsRotatable: true,
targetMarker: "PlainArrow"
}
}

return <div style={{width:"100%", height:"500px"}}>
<DiagramComponent data={data} options={options}/>
</div>
}

Contrast the above with what you get when you specify "strict" rotation:

Release 1.1.1

· 2 min read
sporritt

Release 1.1.1 adds support for rotating label and custom overlays to match their connectors. Here's our flowchart starter app with this functionality enabled:

Rotating labels

This feature is supported both in the edges config for a diagram:


import { DiagramComponent } from "@visuallyjs/browser-ui-react"
import "@visuallyjs/browser-ui/css/visuallyjs.css";

export default function App() {

const data = ...

const options = {
edges: {
showLabels: true,
labelsRotatable: true,
targetMarker: "PlainArrow"
}
}

return <div style={{width:"100%", height:"500px"}}>
<DiagramComponent data={data} options={options}/>
</div>
}

and inside the edges defined in a view for an App:

I want my MCP

· 3 min read
sporritt

Integrating an MCP server directly into your development workflow provides a powerful bridge between your IDE and the library documentation you rely on, eliminating the friction of context-switching between your editor and a browser and leading to more accurate, idiomatic implementations and a significantly smoother coding experience.

We are pleased to announce that VisuallyJs now offers an MCP for each of our library integrations. It's so useful that we're actually using it ourselves, both when developing new features and when working on our ever-increasing set of demonstrations and starter apps.

Setup​

We provide an MCP server for each of our library integrations, as well as a server that covers every integration.

PackageLibraryCommand
@visuallyjs/browser-ui-angular-mcpAngularnpx @visuallyjs/browser-ui-angular-mcp
@visuallyjs/browser-ui-react-mcpReactnpx @visuallyjs/browser-ui-react-mcp
@visuallyjs/browser-ui-svelte-mcpSveltenpx @visuallyjs/browser-ui-svelte-mcp
@visuallyjs/browser-ui-vue-mcpVuenpx @visuallyjs/browser-ui-vue-mcp
@visuallyjs/browser-ui-vanilla-mcpVanilla JSnpx @visuallyjs/browser-ui-vanilla-mcp
@visuallyjs/browser-ui-mcpAll (Angular, React, Svelte, Vue, Vanilla)npx @visuallyjs/browser-ui-mcp

These are published in the public NPM repository, and, for licensees, our private NPM repository.

If you've done this sort of thing before, the commands listed above will be enough to get you going. To read more about setting up MCP with your library, follow one of these links:

Available tools​

Each library-specific package provides tools with unique suffixes, allowing them to coexist in a flat namespace if multiple servers are connected simultaneously. Tools are named following the pattern search_vjs_LIB> for general documentation and search_vjs_LIB_api for API-specific documentation.

For instance, for React, the tools are:

  • search_vjs_react: Search VisuallyJs React documentation.
  • search_vjs_react_api: Search VisuallyJs React API documentation

The other library suffixes are vue, svelte, vanilla and, for Angular, ng.

Examples​

You can use the MCP to search all of the publically available docs and apidocs. At VisuallyJs we use IntelliJ's Junie or AI Chat - here's a selection of questions and responses showing the MCP in action.

Zoom-specific content​

We asked our React MCP how to hide/show content based on zoom, and received this summary:

Hide/show content MCP

followed by this code snippet:

Hide/show content MCP

We can ask the same question in Angular:

Hide/show content MCP

Hide/show content MCP

List available anchors​

Each of the library integrations has a dependency on @visuallyjs/browser-ui, which contains the core UI concepts. You can search through these - for instance, here, we're asking the React MCP about what anchor types are available:

Release 1.1.0

· 4 min read
sporritt

Release 1.1.0 of VisuallyJs adds a couple of very useful capabilities - a component to manage scrollable lists (such as for data mappers), and the ability to declaratively hide/show content based on zoom level.

Scrolling list manager​

Configure a scrollable list with a single DOM attribute.

export function MyList() {
return <div data-vjs-list={true}>
<div data-vjs-port="1">Item One</div>
<div data-vjs-port="2">Item Two</div>
<div data-vjs-port="3">Item Three</div>
...
</div>
}

VisuallyJs will attach a listener to this component and then whenever it is scrolled, edges attached to any of the child elements that are scrolled out of view will be moved so that they are attached to the component's container.

Displaying content based on zoom​

In some applications, you might want to display more content on your nodes and groups when the canvas is zoomed in, and less when it is zoomed out, to reduce clutter. In 1.1.0 we've added support for this across our various library integrations.

Use the new useZoom hook to get access to the current zoom as state:

import { useZoom } from '@visuallyjs/browser-ui-react';

export default function ZoomDisplay({ ui, obj, model }) {
const zoom = useZoom(ui);

return (
<div style={{ position: 'absolute', top: 10, left: 10, background: 'white', padding: '5px' }}>
Current Zoom: {(zoom * 100).toFixed(0)}%
{zoom > 1.5 && <p>High magnification active</p>}
{zoom < 0.5 && <p>Low magnification active</p>}
</div>
);
}

Other updates​

In addition to these two new capabilities, we've also made a number of small updates:

  • Updated the ForceDirected layout to ensure that attraction/repulsion is clamped to a specific range, to avoid blowouts in positioning.
  • Palette uses .vjs-palette-drag-active and .vjs-palette-drag-hover classes now, instead of using the same ones that are used in the edge drag lifecycle.
  • Improved API docs
  • Fixed issue in Snaplines plugin that would cause it to not reset if drag was aborted.
  • Added the ability to provide sort function to the GridLayout (and ColumnLayout/RowLayout)
  • Fixed positioning issue when labels on a chart were rotated 45 degrees to maximise space
  • Added support for pluggable router

Annotating objects with drag groups

· 8 min read

One of the key differentiators between VisuallyJs and other libraries in this space is VisuallyJs's level of configurability - more often than not you'll find that once you hit a blocker in some other library, VisuallyJs will offer you the ability to do what you need.

A great example of this is VisuallyJs's concept of a DragGroup. Simply put, this is a group of vertices that should be dragged together - but as we'll see, it's not quite as simple as that, and it can be used to great effect with minimal work required on your part.

Active vs passive members​

In this canvas, try dragging the large green box around. You'll see the two red boxes drag along with it. Now try dragging one of the red boxes - nothing else moves. This is because all of the nodes are inside a drag group, but the large green node is marked active and the red nodes are marked passive: