@kazant/flexgraph 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +30 -1
- package/css/graph.css +44 -0
- package/dist/flexgraph.esm.js +2 -2
- package/dist/flexgraph.umd.cjs +2 -2
- package/dist/flexgraph.umd.js +2 -2
- package/dist/flexgraph.worker.js +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. This project follows [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [1.3.0] — 2026-10-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Stacking (animated fold / unfold): `stackable: "upstream" | "downstream"` and `stacked` node options, `view.stack(id)`,
|
|
9
|
+
`unstack`, `toggleStack`, `unstackAll`, `isStacked`, `getStacked`, and `stack` / `unstack` events. Folded nodes
|
|
10
|
+
slide in underneath their node, which shows a card stack and a `+N` toggle; unfolding brings them back out from
|
|
11
|
+
underneath. Lines into a stack are redirected (dashed) to the node that holds it. Part of undo / redo and of
|
|
12
|
+
`exportState()` / `importState()`.
|
|
13
|
+
- `view.addNodes(..., { from })` and `view.removeNodes(ids, { to })`: new nodes come out from underneath a node,
|
|
14
|
+
removed nodes slide into one. `updateGraph(data, { from, to })` does the same.
|
|
15
|
+
- Nodes that appear in any update now come out of the node they belong to (their first existing predecessor), and
|
|
16
|
+
nodes that disappear fade out, instead of popping in and out.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Focusing a button inside a node no longer scrolls the viewport.
|
|
20
|
+
- When a relayout keeps a node in place, the view now pans in step with the node animation.
|
|
21
|
+
|
|
5
22
|
## [1.2.0] — 2026-10-12
|
|
6
23
|
|
|
7
24
|
### Added
|
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ Edge `type` sets the CSS class `fg-edge--<type>` and default markers (`has-many`
|
|
|
76
76
|
|
|
77
77
|
### Events
|
|
78
78
|
|
|
79
|
-
`view.on(name, cb)` with `layout`, `history`, `select`, `selectionchange`, `hover`, `nodeclick`, `edgeclick`, `groupclick`, `dragstart`, `drag`, `dragend`, `pin`, `change`, `viewport`, `animationend`, `contextmenu`, `expand`, `explore`.
|
|
79
|
+
`view.on(name, cb)` with `layout`, `history`, `select`, `selectionchange`, `hover`, `nodeclick`, `edgeclick`, `groupclick`, `dragstart`, `drag`, `dragend`, `pin`, `change`, `viewport`, `animationend`, `contextmenu`, `expand`, `stack`, `unstack`, `explore`.
|
|
80
80
|
|
|
81
81
|
### Undo / redo
|
|
82
82
|
|
|
@@ -122,6 +122,35 @@ view.expand("api"); view.collapse("api"); view.toggleExpand("api"); view.isExpan
|
|
|
122
122
|
|
|
123
123
|
Opt a node out with `expandable: false`, or start it open with `expanded: true`. The toggle text is `expandText` / `collapseText`.
|
|
124
124
|
|
|
125
|
+
### Stacking (animated fold / unfold)
|
|
126
|
+
|
|
127
|
+
A node can fold the nodes before or after it underneath itself, with an animation:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
const data = {
|
|
131
|
+
nodes: [
|
|
132
|
+
{ id: "signed", label: "Contract signed", stackable: "upstream" }, // can fold its whole history
|
|
133
|
+
{ id: "delivery", label: "Delivery", stackable: "downstream", stacked: true }, // starts folded
|
|
134
|
+
// …
|
|
135
|
+
],
|
|
136
|
+
edges: [ /* … */ ]
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
view.stack("signed"); // the previous nodes slide in underneath it; it shows a card stack and "+6"
|
|
140
|
+
view.unstack("signed"); // they come back out from underneath and move to their places
|
|
141
|
+
view.toggleStack("delivery"); view.isStacked("delivery"); view.getStacked("delivery"); view.unstackAll();
|
|
142
|
+
|
|
143
|
+
view.addNodes({ nodes: [...], edges: [...] }, { from: "golive" }); // new nodes come out from underneath "golive"
|
|
144
|
+
view.removeNodes(["a", "b"], { to: "c" }); // nodes slide into "c" and disappear
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- `stackable: "upstream"` folds every node that leads into this one; nodes that also lead somewhere else stay visible.
|
|
148
|
+
- `stackable: "downstream"` (or `true`) folds everything after the node; nodes that are also reached from elsewhere stay visible.
|
|
149
|
+
- A round toggle on the node edge shows `−`, or `+N` while folded. Lines into a stack are drawn dashed to the node that holds it.
|
|
150
|
+
- The node you act on keeps its place on screen. Stacking is part of undo / redo and of `exportState()` / `importState()`.
|
|
151
|
+
- `updateGraph(data, { from, to })` animates the same way; new nodes otherwise come out of their first existing predecessor.
|
|
152
|
+
- Follows `animate: false` and `prefers-reduced-motion`.
|
|
153
|
+
|
|
125
154
|
### Explore mode (huge hierarchies)
|
|
126
155
|
|
|
127
156
|
For trees too big to show at once (say 10 start nodes, each with 1 000 children, each with 1 000 more), `explore: true` shows only the nodes without a parent. Clicking a node opens its children; clicking it again closes it. Only the open part of the graph is laid out and rendered.
|
package/css/graph.css
CHANGED
|
@@ -317,3 +317,47 @@
|
|
|
317
317
|
.fg-node, .fg-edge__line, .fg-node__toggle svg { transition: none; }
|
|
318
318
|
.fg-explore .fg-node { animation: none; }
|
|
319
319
|
}
|
|
320
|
+
|
|
321
|
+
/* ---- stacking: nodes folded under another node ---------------------------- */
|
|
322
|
+
|
|
323
|
+
/* stacked cards peeking out behind a node that holds a stack */
|
|
324
|
+
.fg-stack {
|
|
325
|
+
position: absolute;
|
|
326
|
+
inset: -1px;
|
|
327
|
+
z-index: -1;
|
|
328
|
+
border-radius: var(--fg-node-radius);
|
|
329
|
+
pointer-events: none;
|
|
330
|
+
box-shadow: 5px 5px 0 -1px var(--fg-node-bg), 5px 5px 0 0 var(--fg-node-border);
|
|
331
|
+
}
|
|
332
|
+
.fg-stack--many {
|
|
333
|
+
box-shadow:
|
|
334
|
+
5px 5px 0 -1px var(--fg-node-bg), 5px 5px 0 0 var(--fg-node-border),
|
|
335
|
+
10px 10px 0 -1px var(--fg-node-bg), 10px 10px 0 0 var(--fg-node-border);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* the round +/− toggle on the node edge (upstream: entry side, downstream: exit side) */
|
|
339
|
+
.fg-stack-toggle {
|
|
340
|
+
position: absolute;
|
|
341
|
+
z-index: 2;
|
|
342
|
+
min-width: 20px;
|
|
343
|
+
height: 20px;
|
|
344
|
+
padding: 0 5px;
|
|
345
|
+
border: 1px solid var(--fg-node-border);
|
|
346
|
+
border-radius: 999px;
|
|
347
|
+
background: var(--fg-node-bg);
|
|
348
|
+
color: var(--fg-node-text);
|
|
349
|
+
font: 600 11px/18px system-ui, sans-serif;
|
|
350
|
+
cursor: pointer;
|
|
351
|
+
box-shadow: var(--fg-node-shadow);
|
|
352
|
+
transform: translate(-50%, -50%);
|
|
353
|
+
}
|
|
354
|
+
.fg-stack-toggle:hover, .fg-stack-toggle:focus-visible { border-color: var(--fg-accent); color: var(--fg-accent); outline: none; }
|
|
355
|
+
.fg-stack-toggle--stacked { background: var(--fg-accent); border-color: var(--fg-accent); color: #fff; }
|
|
356
|
+
.fg-stack-toggle--stacked:hover, .fg-stack-toggle--stacked:focus-visible { color: #fff; filter: brightness(1.1); }
|
|
357
|
+
.fg-stack-toggle--top { left: calc(100% - 14px); top: 0; }
|
|
358
|
+
.fg-stack-toggle--bottom { left: calc(100% - 14px); top: 100%; }
|
|
359
|
+
.fg-stack-toggle--left { left: 0; top: calc(100% - 10px); }
|
|
360
|
+
.fg-stack-toggle--right { left: 100%; top: calc(100% - 10px); }
|
|
361
|
+
|
|
362
|
+
/* lines into a stack are drawn to the node that holds it */
|
|
363
|
+
.fg-edge--proxy .fg-edge__line { stroke-dasharray: 5 4; }
|