Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
ffc0fc9
start
Geliogabalus Sep 2, 2026
c6dff5f
feat(joint-layout-elk): support hierarchical containers, ports and pe…
Geliogabalus Sep 7, 2026
f7703b8
example(layout-elk): add containers, ports and width-restriction demo
Geliogabalus Sep 7, 2026
9d4e982
docs(changeset): add 'new package' changeset for @joint/layout-elk
Geliogabalus Sep 7, 2026
2f9de43
up
Geliogabalus Sep 8, 2026
906698b
port and portLabels wip
Geliogabalus Sep 8, 2026
43a303b
full types wip
Geliogabalus Sep 8, 2026
053d62c
estimate port label size
Geliogabalus Sep 8, 2026
a264870
refactor
Geliogabalus Sep 8, 2026
3d03dbe
up
Geliogabalus Sep 8, 2026
4b0d889
wip
Geliogabalus Sep 10, 2026
8b02d34
interactive wip
Geliogabalus Sep 10, 2026
37ef452
refactor to function
Geliogabalus Sep 10, 2026
f7d76b6
wip
Geliogabalus Sep 11, 2026
2e5cb9f
wip
Geliogabalus Sep 13, 2026
3c93051
wip refactor
Geliogabalus Sep 16, 2026
643d5ab
fix
Geliogabalus Sep 16, 2026
b31e189
update
Geliogabalus Sep 16, 2026
6de9b98
up
Geliogabalus Sep 17, 2026
280a017
wip
Geliogabalus Sep 17, 2026
de1ce70
fixes
Geliogabalus Sep 21, 2026
b86e2bc
update
Geliogabalus Sep 21, 2026
00dc906
fixes
Geliogabalus Sep 21, 2026
a217d80
clean up
Geliogabalus Sep 21, 2026
52348b8
port label fix
Geliogabalus Sep 21, 2026
4aae67c
update
Geliogabalus Sep 21, 2026
13e48ff
updates
Geliogabalus Sep 22, 2026
bee8aa3
link label attributes refactor
Geliogabalus Sep 22, 2026
35853ec
update
Geliogabalus Sep 22, 2026
e748361
update
Geliogabalus Sep 23, 2026
feba41d
update
Geliogabalus Sep 23, 2026
6c40b84
up
Geliogabalus Sep 23, 2026
234c673
wip
Geliogabalus Sep 24, 2026
931346f
update API
Geliogabalus Sep 25, 2026
3713caf
ports core revert
Geliogabalus Sep 25, 2026
88ebe80
restyle
Geliogabalus Sep 28, 2026
d95b025
reordering example wip
Geliogabalus Sep 29, 2026
5ba76aa
comment fixes
Geliogabalus Sep 29, 2026
7238883
tests fix
Geliogabalus Sep 29, 2026
f3d1ca9
to exportLink
Geliogabalus Sep 29, 2026
ca2e851
cleaning comments and README
Geliogabalus Sep 30, 2026
b2fbf66
renaming options
Geliogabalus Sep 30, 2026
41512b6
default behaviour example
Geliogabalus Sep 30, 2026
cf40b8c
update
Geliogabalus Oct 1, 2026
28a531e
up
Geliogabalus Oct 1, 2026
11e57fc
fixes
Geliogabalus Oct 2, 2026
7d5ddc4
fixes and updates
Geliogabalus Oct 2, 2026
a319859
updates
Geliogabalus Oct 5, 2026
d843c3f
worker update
Geliogabalus Oct 5, 2026
7f5b804
optional graph
Geliogabalus Oct 5, 2026
5e78e78
fix
Geliogabalus Oct 5, 2026
2b06033
fix(layout-elk): apply laid out link labels by id, not by position in…
Geliogabalus Oct 6, 2026
f41de9f
fix(layout-elk): export elements with ports with FIXED_POS port const…
Geliogabalus Oct 6, 2026
e182135
fix(layout-elk): close the layout batch when an import callback throws
Geliogabalus Oct 6, 2026
66c29d3
feat(layout-elk): add `signal` option to abort a layout
Geliogabalus Oct 6, 2026
2d2e33b
perf(layout-elk): load main-thread ELK only when a layout runs on the…
Geliogabalus Oct 6, 2026
5ffbb7a
fix(layout-elk): fall back to the main thread only when the worker fa…
Geliogabalus Oct 6, 2026
ab3f9a6
feat(layout-elk): warn once when ELK falls back to the main thread
Geliogabalus Oct 6, 2026
752d465
feat(layout-elk): add `thread` option to choose where the default ELK…
Geliogabalus Oct 6, 2026
cabcbda
chore(layout-elk): set version to 4.3.0
Geliogabalus Oct 6, 2026
6006a52
fix(examples): fix ELK examples
Geliogabalus Oct 6, 2026
0e05c86
feat(layout-elk)!: run ELK on the main thread by default, add createW…
Geliogabalus Oct 6, 2026
0af8594
fix(examples): run ELK in a Web Worker with createWorkerElk()
Geliogabalus Oct 6, 2026
0d1801d
comments fixes
Geliogabalus Oct 7, 2026
2f6bbd7
cleaning examples
Geliogabalus Oct 8, 2026
615912c
fix(layout-elk): track the layouts actually posted to the ELK worker
Geliogabalus Oct 8, 2026
5520858
fix(layout-elk): drop a port label when exportPortLabel returns false
Geliogabalus Oct 8, 2026
cd4a52b
fix(layout-elk): reject an aborted layout with a `null` abort reason …
Geliogabalus Oct 8, 2026
a2bba4c
fix(dia.Element): type portProp(portId) and portProp(portId, object, …
Geliogabalus Oct 8, 2026
42ee573
chore: add @joint/layout-elk and its examples to yarn.lock
Geliogabalus Oct 8, 2026
237443f
fix(layout-elk): read resolved link labels through the protected getC…
Geliogabalus Oct 8, 2026
0dc24c6
up
Geliogabalus Oct 8, 2026
54ee89a
fix(layout-elk): keep ELK ids unique, and lay out a container without…
Geliogabalus Oct 8, 2026
b3138d8
fix(examples): drop the containers-ports example's stale link label `…
Geliogabalus Oct 8, 2026
ebf50d0
docs(layout-elk): document loading elkjs before the UMD build
Geliogabalus Oct 8, 2026
6cf8435
fix(layout-elk): correct link end handling, label positions and failu…
kumilingus Oct 9, 2026
1275709
fix(layout-elk): build the layout hierarchy from `parent()`, not from…
kumilingus Oct 10, 2026
b773a55
refactor(layout-elk)!: drop the `batchName` option
kumilingus Oct 10, 2026
8cccdf3
refactor(layout-elk)!: take a graph or a list of cells, like the othe…
kumilingus Oct 10, 2026
d8ceef5
fix(layout-elk): batch on the graph that was passed, and document wha…
kumilingus Oct 10, 2026
fc45e37
fix(layout-elk): lay out a cell listed more than once only once
kumilingus Oct 10, 2026
d7f4fac
docs(layout-elk): bring the API docs in line with the new signature
kumilingus Oct 10, 2026
fefec99
fix(layout-elk)!: leave a link out of the layout when `exportPort` dr…
kumilingus Oct 10, 2026
57a3ffa
fix(layout-elk): reject two cells that share one ELK id
kumilingus Oct 10, 2026
c5b1cce
feat(layout-elk): warn that element rotation is not taken into account
kumilingus Oct 10, 2026
50faa26
fix(layout-elk): anchor a link end on the point ELK routed to, on a r…
kumilingus Oct 10, 2026
00af896
refactor(layout-elk): shorten the rotated element warning
kumilingus Oct 10, 2026
a186d59
fix(layout-elk): three fixes from a second audit of the package
kumilingus Oct 10, 2026
21058d2
fix(layout-elk): give every node the element's own size
kumilingus Oct 10, 2026
f6b365f
fix(layout-elk): measure a link end's anchor from the position ELK co…
kumilingus Oct 10, 2026
e210964
fix(layout-elk): hand the signal to any `WorkerElk`, not only to our …
kumilingus Oct 10, 2026
f7a3fdd
test(layout-elk): pin down where a port lands when `exportElement` re…
kumilingus Oct 10, 2026
dfb81bb
fix(layout-elk): let `exportElement` size a node around its labels
kumilingus Oct 10, 2026
0ae6a8e
fix(layout-elk): keep a port id as it came, and reject two ports shar…
kumilingus Oct 11, 2026
ec30e48
fix(layout-elk): key the layout hierarchy by the cell id, not by a st…
kumilingus Oct 11, 2026
53a590e
refactor(layout-elk): type a port id as `dia.Cell.ID`, and fail the b…
kumilingus Oct 11, 2026
0109687
feat(layout-elk): tell `setPortAttributes` whether the port moved
kumilingus Oct 11, 2026
8c74c8e
fix(layout-elk): skip a port removed while the layout was running
kumilingus Oct 11, 2026
bce87b3
fix(layout-elk): keep a port the layout did not move where it is
kumilingus Oct 11, 2026
221ce6f
fix(layout-elk): apply the size ELK computed whenever it changed it
kumilingus Oct 11, 2026
247ab3a
refactor(examples): animate the rectpacking layout with CSS transitions
kumilingus Oct 11, 2026
82b6732
fix(layout-elk): compare a node's new size against the element's, not…
kumilingus Oct 11, 2026
b51b990
test(layout-elk): cover the size a node is laid out at
kumilingus Oct 11, 2026
cdba96f
fix(layout-elk): send a container to ELK without a size of its own again
kumilingus Oct 11, 2026
2282a3f
fix(layout-elk): return the bounding box of everything ELK laid out
kumilingus Oct 11, 2026
1ceeb6a
test(layout-elk): cover an edge label in the bounding box
kumilingus Oct 11, 2026
ef75684
fix(layout-elk): record a port's top-left as `exportPort` left it
kumilingus Oct 11, 2026
8ff6e8c
fix(layout-elk): type a port id handed to an export callback as `dia.…
kumilingus Oct 11, 2026
a1cf436
refactor(examples): build the ELK demos with vite instead of webpack
kumilingus Oct 12, 2026
aa6b138
docs(examples): correct how the rectpacking demo applies ELK sizes
kumilingus Oct 12, 2026
342a626
refactor(layout-elk)!: name a callback's parameter type `...CallbackP…
kumilingus Oct 12, 2026
29f6669
refactor(examples): name the ELK demos after what they show
kumilingus Oct 12, 2026
96f3406
chore(layout-elk): drop the `@ts-expect-error` directives for port ids
kumilingus Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/defaults-undefined-sources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@joint/core": patch
---

util - accept `undefined` source objects in the `defaults()`, `defaultsDeep()`, `supplement()` and `deepSupplement()` typings, which the implementations already skip
5 changes: 5 additions & 0 deletions .changeset/layout-elk-new-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@joint/layout-elk": minor
---

new package - automatic layout for JointJS graphs using the Eclipse Layout Kernel (ELK), including layout of embedded elements as containers and of element ports
5 changes: 5 additions & 0 deletions .changeset/port-label-position-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@joint/core": patch
---

dia.Element - fix `PortLabelPositionType` type to use `PortLabelPositionJSON` for a port label's position
5 changes: 5 additions & 0 deletions .changeset/port-prop-overloads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@joint/core": patch
---

dia.Element - fix types to allow `portProp(portId)` and `portProp(portId, object, opt)`
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
build/
dist/
node_modules/
27 changes: 27 additions & 0 deletions examples/layout-elk-containers-and-ports-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# JointJS ELK Containers & Ports Demo

A fixed (non-random) web platform reference architecture laid out automatically with `@joint/layout-elk`: nested containers ("Client Layer" > "Edge", "Core Services" > "Data Layer", "Observability") grouping services that connect through ports, plus a couple of links connecting containers directly - including one that crosses from one container into another. Styled with Material Design, via JointJS's theme mechanism and CSS.

## Setup

Use Yarn to run this demo.

You need to build *JointJS* first. Navigate to the root folder and run:
```bash
yarn install
yarn run build
```

Navigate to this directory, then run:
```bash
yarn dev
```

Then open the URL printed in the terminal (usually `http://localhost:5173`). `yarn build`
makes a production build in `dist/`, `yarn preview` serves it.

## License

The *JointJS* library is licensed under the [Mozilla Public License 2.0](https://github.com/clientIO/joint/blob/master/LICENSE).

Copyright © 2013-2026 client IO
25 changes: 25 additions & 0 deletions examples/layout-elk-containers-and-ports-ts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="An automatic layout demo of a fixed graph with containers and ports, using JointJS and ELK.">
<title>ELK Containers & Ports Layout | JointJS</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
</head>

<body>
<div class="toolbar">
<span id="zoom-out" class="toolbar-button">Zoom Out</span>
<span id="zoom-in" class="toolbar-button">Zoom In</span>
</div>
<div id="canvas" class="canvas"></div>
<script type="module" src="/src/index.ts"></script>

</body>

</html>
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@joint/demo-layout-elk-ts",
"name": "@joint/demo-layout-elk-containers-and-ports-ts",
"version": "4.3.1",
"description": "JointJS - ELK Layout Demo",
"main": "dist/bundle.js",
"type": "module",
"description": "JointJS - ELK Layout Containers & Ports Demo",
"homepage": "https://jointjs.com",
"author": {
"name": "client IO",
Expand All @@ -14,22 +14,18 @@
"hoistingLimits": "workspaces"
},
"scripts": {
"start": "webpack-dev-server",
"build": "webpack"
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@joint/core": "workspace:^",
"elkjs": "^0.11.0"
"@joint/layout-elk": "workspace:^"
},
"devDependencies": {
"css-loader": "3.5.3",
"sass-loader": "8.0.2",
"style-loader": "1.2.1",
"ts-loader": "^9.2.5",
"sass": "^1.93.2",
"typescript": "5.8.2",
"webpack": "5.98.0",
"webpack-cli": "6.0.1",
"webpack-dev-server": "5.2.0"
"vite": "^7.3.1"
},
"volta": {
"node": "22.14.0",
Expand Down
208 changes: 208 additions & 0 deletions examples/layout-elk-containers-and-ports-ts/src/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { dia } from '@joint/core';

// A fixed (non-random) system diagram: three top-level containers, two of
// them with a nested container of their own, grouping eight services that
// communicate over ports - plus a couple of links that connect two
// containers directly, rather than a pair of ports, including one that
// crosses container boundaries. A recognizable, if simplified, web platform
// reference architecture: a client layer talking through an edge (load
// balancer + API gateway) to core services (auth, guarding a data layer of
// cache + database), with logs/metrics/analytics flowing to observability.
// Every plain `example.Service` has exactly one 'in' and one 'out' port; the
// four "hub" services (Load Balancer, API Gateway, Auth Service, Monitoring)
// are `example.HubService` instead, with a custom number of ports and ELK's
// `FIXED_SIDE` port constraint, so it can reorder them to minimize crossings
// (see `index.ts`).
export const graphJSON: dia.Graph.JSON = {
cells: [
// Containers
{
id: 'frontend',
type: 'example.Container',
attrs: { label: { text: 'Client Layer' } }
},
{
id: 'edge',
type: 'example.Container',
parent: 'frontend',
attrs: { label: { text: 'Edge' } }
},
{
id: 'backend',
type: 'example.Container',
attrs: { label: { text: 'Core Services' } }
},
{
id: 'storage',
type: 'example.Container',
parent: 'backend',
attrs: { label: { text: 'Data Layer' } }
},
{
id: 'observability',
type: 'example.Container',
attrs: { label: { text: 'Observability' } }
},

// Client Layer
{
id: 'webui',
type: 'example.Service',
parent: 'frontend',
attrs: { label: { text: 'Web App' } }
},
{
id: 'mobileui',
type: 'example.Service',
parent: 'frontend',
attrs: { label: { text: 'Mobile App' } }
},
{
id: 'lb',
type: 'example.HubService',
parent: 'edge',
size: { width: 130, height: 80 },
attrs: { label: { text: 'Load Balancer' } },
ports: {
items: [
{ id: 'in1', group: 'in', attrs: { text: { text: 'in1' } } },
{ id: 'in2', group: 'in', attrs: { text: { text: 'in2' } } },
{ id: 'out', group: 'out', attrs: { text: { text: 'out' } } }
]
}
},
{
id: 'gateway',
type: 'example.HubService',
parent: 'edge',
size: { width: 130, height: 80 },
attrs: { label: { text: 'API Gateway' } },
ports: {
items: [
{ id: 'in', group: 'in', attrs: { text: { text: 'in' } } },
{ id: 'out1', group: 'out', attrs: { text: { text: 'out1' } } },
{ id: 'out2', group: 'out', attrs: { text: { text: 'out2' } } }
]
}
},

// Core Services
{
id: 'auth',
type: 'example.HubService',
parent: 'backend',
size: { width: 130, height: 80 },
attrs: { label: { text: 'Auth Service' } },
ports: {
items: [
{ id: 'in', group: 'in', attrs: { text: { text: 'in' } } },
{ id: 'out1', group: 'out', attrs: { text: { text: 'out1' } } },
{ id: 'out2', group: 'out', attrs: { text: { text: 'out2' } } }
]
}
},
{
id: 'cache',
type: 'example.Service',
parent: 'storage',
attrs: { label: { text: 'Redis Cache' } }
},
{
id: 'db',
type: 'example.Service',
parent: 'storage',
attrs: { label: { text: 'PostgreSQL' } }
},

// Observability
{
id: 'monitoring',
type: 'example.HubService',
parent: 'observability',
size: { width: 130, height: 80 },
attrs: { label: { text: 'Monitoring' } },
ports: {
items: [
{ id: 'in1', group: 'in', attrs: { text: { text: 'in1' } } },
{ id: 'in2', group: 'in', attrs: { text: { text: 'in2' } } }
]
}
},

// Links
{
id: 'l1',
type: 'example.InteractionLink',
source: { id: 'webui', port: 'out' },
target: { id: 'lb', port: 'in1' },
labels: [{ attrs: { text: { text: 'request' } } }]
},
{
id: 'l2',
type: 'example.InteractionLink',
source: { id: 'mobileui', port: 'out' },
target: { id: 'lb', port: 'in2' },
labels: [{ attrs: { text: { text: 'request' } } }]
},
{
id: 'l3',
type: 'example.InteractionLink',
source: { id: 'lb', port: 'out' },
target: { id: 'gateway', port: 'in' },
labels: [{ attrs: { text: { text: 'route' } } }]
},
{
id: 'l4',
type: 'example.InteractionLink',
source: { id: 'gateway', port: 'out1' },
target: { id: 'auth', port: 'in' },
labels: [{ attrs: { text: { text: 'authenticate' } } }]
},
{
id: 'l5',
type: 'example.InteractionLink',
source: { id: 'gateway', port: 'out2' },
target: { id: 'monitoring', port: 'in1' },
labels: [{ attrs: { text: { text: 'metrics' } } }]
},
{
id: 'l6',
type: 'example.InteractionLink',
source: { id: 'auth', port: 'out1' },
target: { id: 'cache', port: 'in' },
labels: [{ attrs: { text: { text: 'lookup' } } }]
},
{
id: 'l7',
type: 'example.InteractionLink',
source: { id: 'auth', port: 'out2' },
target: { id: 'monitoring', port: 'in2' },
labels: [{ attrs: { text: { text: 'logs' } } }]
},
{
id: 'l8',
type: 'example.InteractionLink',
source: { id: 'cache', port: 'out' },
target: { id: 'db', port: 'in' },
labels: [{ attrs: { text: { text: 'query' } } }]
},

// Container-to-container links - connected to a `example.Container` cell
// itself rather than to one of its ports, aggregating what the individual
// service-to-service links above already carry.
{
id: 'l9',
type: 'example.InteractionLink',
source: { id: 'backend' },
target: { id: 'observability' },
labels: [{ attrs: { text: { text: 'metrics' } } }]
},
{
id: 'l10',
type: 'example.InteractionLink',
source: { id: 'frontend' },
target: { id: 'observability' },
labels: [{ attrs: { text: { text: 'analytics' } } }]
}
]
};
Loading
Loading