@unovis/ts 1.5.0-alpha.8 → 1.5.0-beta.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/components/axis/config.d.ts +10 -1
- package/components/axis/config.js +1 -1
- package/components/axis/config.js.map +1 -1
- package/components/axis/index.d.ts +19 -19
- package/components/axis/index.js +63 -8
- package/components/axis/index.js.map +1 -1
- package/components/axis/style.d.ts +1 -0
- package/components/axis/style.js +7 -2
- package/components/axis/style.js.map +1 -1
- package/components/graph/config.d.ts +28 -4
- package/components/graph/config.js +3 -3
- package/components/graph/config.js.map +1 -1
- package/components/graph/index.d.ts +18 -6
- package/components/graph/index.js +230 -89
- package/components/graph/index.js.map +1 -1
- package/components/graph/modules/link/index.d.ts +2 -1
- package/components/graph/modules/link/index.js +36 -17
- package/components/graph/modules/link/index.js.map +1 -1
- package/components/graph/modules/node/index.d.ts +2 -1
- package/components/graph/modules/node/index.js +47 -23
- package/components/graph/modules/node/index.js.map +1 -1
- package/components/graph/modules/node/style.d.ts +2 -0
- package/components/graph/modules/node/style.js +34 -4
- package/components/graph/modules/node/style.js.map +1 -1
- package/components/graph/modules/panel/index.js +1 -0
- package/components/graph/modules/panel/index.js.map +1 -1
- package/components/graph/style.d.ts +1 -0
- package/components/graph/style.js +22 -1
- package/components/graph/style.js.map +1 -1
- package/components/graph/types.d.ts +8 -0
- package/components/graph/types.js +8 -2
- package/components/graph/types.js.map +1 -1
- package/components/leaflet-map/modules/map.js +2 -1
- package/components/leaflet-map/modules/map.js.map +1 -1
- package/components/scatter/index.d.ts +1 -0
- package/components/scatter/index.js +19 -12
- package/components/scatter/index.js.map +1 -1
- package/components/scatter/modules/point.js +1 -3
- package/components/scatter/modules/point.js.map +1 -1
- package/components/scatter/types.d.ts +2 -0
- package/components/topojson-map/config.js +2 -2
- package/components/topojson-map/config.js.map +1 -1
- package/components/topojson-map/index.js +22 -5
- package/components/topojson-map/index.js.map +1 -1
- package/containers/single-container/index.js +2 -2
- package/containers/single-container/index.js.map +1 -1
- package/containers/xy-container/index.js +2 -0
- package/containers/xy-container/index.js.map +1 -1
- package/core/container/config.d.ts +2 -4
- package/core/container/config.js.map +1 -1
- package/core/container/index.d.ts +2 -1
- package/core/container/index.js +20 -14
- package/core/container/index.js.map +1 -1
- package/data-models/graph.d.ts +2 -0
- package/data-models/graph.js +6 -0
- package/data-models/graph.js.map +1 -1
- package/index.d.ts +1 -4
- package/index.js +13 -5
- package/index.js.map +1 -1
- package/maps/world-simple.json.js +2430 -42
- package/package.json +1 -1
- package/types/graph.d.ts +2 -0
- package/types.js +1 -1
- package/utils/index.d.ts +12 -0
- package/utils/index.js +13 -0
- package/utils/index.js.map +1 -0
- package/utils/scale.js +4 -0
- package/utils/scale.js.map +1 -0
- package/utils/type.js +2 -0
- package/utils/type.js.map +1 -0
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import { min, max } from 'd3-array';
|
|
3
3
|
import { select, pointer } from 'd3-selection';
|
|
4
|
-
import {
|
|
4
|
+
import { brush as brush$1 } from 'd3-brush';
|
|
5
|
+
import { zoom, zoomIdentity, zoomTransform } from 'd3-zoom';
|
|
5
6
|
import { drag } from 'd3-drag';
|
|
6
7
|
import { interval } from 'd3-timer';
|
|
7
8
|
import { ComponentCore } from '../../core/component/index.js';
|
|
8
9
|
import { GraphDataModel } from '../../data-models/graph.js';
|
|
9
|
-
import { isNumber, isFunction, clamp, getBoolean, shallowDiff, isPlainObject
|
|
10
|
+
import { isEqual, isNumber, isFunction, clamp, getBoolean, shallowDiff, isPlainObject } from '../../utils/data.js';
|
|
10
11
|
import { smartTransition } from '../../utils/d3.js';
|
|
11
|
-
import { GraphLayoutType, GraphLinkArrowStyle } from './types.js';
|
|
12
|
+
import { GraphLayoutType, GraphNodeSelectionHighlightMode, GraphLinkArrowStyle } from './types.js';
|
|
12
13
|
import { GraphDefaultConfig } from './config.js';
|
|
13
|
-
import { background, graphGroup, root } from './style.js';
|
|
14
|
+
import { background, graphGroup, brush, root } from './style.js';
|
|
14
15
|
import * as style from './modules/node/style.js';
|
|
15
|
-
import { nodes, gNode, gNodeExit, node, nodeGauge, sideLabelGroup, label, greyedOutNode } from './modules/node/style.js';
|
|
16
|
+
import { nodes, gNode, gNodeExit, brushed, brushable, node, nodeGauge, sideLabelGroup, label, greyedOutNode } from './modules/node/style.js';
|
|
16
17
|
import { links, gLink, gLinkExit, link, greyedOutLink } from './modules/link/style.js';
|
|
17
18
|
import { panels, gPanel, panel, panelSelection, label as label$1, labelText, sideIconGroup, sideIconShape, sideIconSymbol } from './modules/panel/style.js';
|
|
18
|
-
import { createNodes, updateNodes, removeNodes,
|
|
19
|
+
import { createNodes, updateNodes, removeNodes, updateNodesPartial, zoomNodesThrottled, zoomNodes } from './modules/node/index.js';
|
|
19
20
|
import { getMaxNodeSize, getX, getY, getNodeSize } from './modules/node/helper.js';
|
|
20
|
-
import { createLinks, updateLinks, removeLinks,
|
|
21
|
+
import { createLinks, updateLinks, removeLinks, updateLinksPartial, animateLinkFlow, zoomLinksThrottled, zoomLinks } from './modules/link/index.js';
|
|
21
22
|
import { getArrowPath, getDoubleArrowPath } from './modules/link/helper.js';
|
|
22
23
|
import { removePanels, createPanels, updatePanels } from './modules/panel/index.js';
|
|
23
24
|
import { updatePanelNumNodes, updatePanelBBoxSize, initPanels, setPanelForNodes } from './modules/panel/helper.js';
|
|
@@ -53,9 +54,16 @@ class Graph extends ComponentCore {
|
|
|
53
54
|
this.setConfig(config);
|
|
54
55
|
this._backgroundRect = this.g.append('rect').attr('class', background);
|
|
55
56
|
this._graphGroup = this.g.append('g').attr('class', graphGroup);
|
|
57
|
+
this._brush = this.g.append('g').attr('class', brush);
|
|
56
58
|
this._zoomBehavior = zoom()
|
|
57
59
|
.scaleExtent(this.config.zoomScaleExtent)
|
|
58
|
-
.on('zoom', (e) => this._onZoom(e.transform, e))
|
|
60
|
+
.on('zoom', (e) => this._onZoom(e.transform, e))
|
|
61
|
+
.on('start', (e) => this._onZoomStart(e.transform, e))
|
|
62
|
+
.on('end', (e) => this._onZoomEnd(e.transform, e));
|
|
63
|
+
this._brushBehavior = brush$1()
|
|
64
|
+
.on('start brush end', this._onBrush.bind(this))
|
|
65
|
+
.filter(event => event.shiftKey)
|
|
66
|
+
.keyModifiers(false);
|
|
59
67
|
this._panelsGroup = this._graphGroup.append('g').attr('class', panels);
|
|
60
68
|
this._linksGroup = this._graphGroup.append('g').attr('class', links);
|
|
61
69
|
this._nodesGroup = this._graphGroup.append('g').attr('class', nodes);
|
|
@@ -63,13 +71,19 @@ class Graph extends ComponentCore {
|
|
|
63
71
|
this._getLinkArrowDefId = this._getLinkArrowDefId.bind(this);
|
|
64
72
|
}
|
|
65
73
|
get selectedNode() {
|
|
66
|
-
|
|
74
|
+
var _a;
|
|
75
|
+
return (_a = this._selectedNodes) === null || _a === void 0 ? void 0 : _a[0];
|
|
76
|
+
}
|
|
77
|
+
get selectedNodes() {
|
|
78
|
+
return this._selectedNodes;
|
|
67
79
|
}
|
|
68
80
|
get selectedLink() {
|
|
69
81
|
return this._selectedLink;
|
|
70
82
|
}
|
|
71
83
|
setData(data) {
|
|
72
84
|
const { config } = this;
|
|
85
|
+
if (isEqual(this.datamodel.data, data))
|
|
86
|
+
return;
|
|
73
87
|
this.datamodel.nodeSort = config.nodeSort;
|
|
74
88
|
this.datamodel.data = data;
|
|
75
89
|
this._shouldRecalculateLayout = true;
|
|
@@ -95,7 +109,7 @@ class Graph extends ComponentCore {
|
|
|
95
109
|
return { top: extraPadding, bottom: extraPadding, left: extraPadding, right: extraPadding };
|
|
96
110
|
}
|
|
97
111
|
_render(customDuration) {
|
|
98
|
-
const { config: { disableZoom, duration, layoutAutofit, zoomEventFilter }, datamodel } = this;
|
|
112
|
+
const { config: { disableBrush, disableZoom, duration, layoutAutofit, zoomEventFilter }, datamodel } = this;
|
|
99
113
|
if (!datamodel.nodes && !datamodel.links)
|
|
100
114
|
return;
|
|
101
115
|
const animDuration = isNumber(customDuration) ? customDuration : duration;
|
|
@@ -109,6 +123,25 @@ class Graph extends ComponentCore {
|
|
|
109
123
|
this._prevWidth = this._width;
|
|
110
124
|
this._prevHeight = this._height;
|
|
111
125
|
}
|
|
126
|
+
// Handle brush behavior
|
|
127
|
+
if (!disableBrush) {
|
|
128
|
+
this._brushBehavior.extent([[0, 0], [this._width, this._height]]);
|
|
129
|
+
this._brush.call(this._brushBehavior);
|
|
130
|
+
// Activate the brush when the shift key is pressed
|
|
131
|
+
select(window)
|
|
132
|
+
.on('keydown.unovis-graph', e => e.key === 'Shift' && this._activateBrush())
|
|
133
|
+
.on('keyup.unovis-graph', e => e.key === 'Shift' && this._clearBrush());
|
|
134
|
+
this._zoomBehavior.filter(event => !event.shiftKey);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this._brush.on('.brush', null);
|
|
138
|
+
select(window)
|
|
139
|
+
.on('keydown.unovis-graph', null)
|
|
140
|
+
.on('keyup.unovis-graph', null);
|
|
141
|
+
// Clear brush in case it was disabled in an active state
|
|
142
|
+
if (this._brush.classed('active'))
|
|
143
|
+
this._clearBrush();
|
|
144
|
+
}
|
|
112
145
|
// Apply layout and render
|
|
113
146
|
if (this._shouldRecalculateLayout || !this._layoutCalculationPromise) {
|
|
114
147
|
this._layoutCalculationPromise = this._calculateLayout();
|
|
@@ -119,20 +152,21 @@ class Graph extends ComponentCore {
|
|
|
119
152
|
(_b = (_a = this.config).onLayoutCalculated) === null || _b === void 0 ? void 0 : _b.call(_a, datamodel.nodes, datamodel.links);
|
|
120
153
|
});
|
|
121
154
|
}
|
|
122
|
-
//
|
|
123
|
-
//
|
|
155
|
+
// Redefining Zoom Behavior filter to the one specified in the config,
|
|
156
|
+
// or to the default one supporting `shiftKey` for node brushing
|
|
157
|
+
// See more: https://d3js.org/d3-zoom#zoom_filter
|
|
124
158
|
this._zoomBehavior.filter(isFunction(zoomEventFilter)
|
|
125
159
|
? zoomEventFilter
|
|
126
|
-
: (e) => !e.shiftKey); // Default filter
|
|
127
|
-
this._layoutCalculationPromise.then((
|
|
128
|
-
var _a, _b;
|
|
160
|
+
: (e) => (!e.ctrlKey || e.type === 'wheel') && !e.button && !e.shiftKey); // Default filter
|
|
161
|
+
this._layoutCalculationPromise.then(() => {
|
|
162
|
+
var _a, _b, _c;
|
|
129
163
|
// If the component has been destroyed while the layout calculation
|
|
130
164
|
// was in progress, we cancel the render
|
|
131
165
|
if (this.isDestroyed())
|
|
132
166
|
return;
|
|
133
167
|
this._initPanelsData();
|
|
134
168
|
// Fit the view
|
|
135
|
-
if (
|
|
169
|
+
if (this._isFirstRender) {
|
|
136
170
|
this._fit();
|
|
137
171
|
this._shouldFitLayout = false;
|
|
138
172
|
}
|
|
@@ -140,19 +174,20 @@ class Graph extends ComponentCore {
|
|
|
140
174
|
this._fit(duration);
|
|
141
175
|
this._shouldFitLayout = false;
|
|
142
176
|
}
|
|
143
|
-
//
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const selectedNode = datamodel.nodes.find(node => node.id === this.config.selectedNodeId);
|
|
150
|
-
this._selectNode(selectedNode);
|
|
177
|
+
// Update Nodes and Links Selection State
|
|
178
|
+
this._resetSelectionGreyoutState();
|
|
179
|
+
if (this.config.selectedNodeId || this.config.selectedNodeIds) {
|
|
180
|
+
const selectedIds = (_a = this.config.selectedNodeIds) !== null && _a !== void 0 ? _a : [this.config.selectedNodeId];
|
|
181
|
+
const selectedNodes = selectedIds.map(id => datamodel.getNodeFromId(id));
|
|
182
|
+
this._setNodeSelectionState(selectedNodes);
|
|
151
183
|
}
|
|
152
184
|
if (this.config.selectedLinkId) {
|
|
153
185
|
const selectedLink = datamodel.links.find(link => link.id === this.config.selectedLinkId);
|
|
154
|
-
this.
|
|
186
|
+
this._setLinkSelectionState(selectedLink);
|
|
155
187
|
}
|
|
188
|
+
// Draw
|
|
189
|
+
this._drawNodes(animDuration);
|
|
190
|
+
this._drawLinks(animDuration);
|
|
156
191
|
// Link flow animation timer
|
|
157
192
|
if (!this._timer) {
|
|
158
193
|
const refreshRateMs = 35;
|
|
@@ -163,10 +198,6 @@ class Graph extends ComponentCore {
|
|
|
163
198
|
this.g.on('.zoom', null);
|
|
164
199
|
else
|
|
165
200
|
this.g.call(this._zoomBehavior).on('dblclick.zoom', null);
|
|
166
|
-
if (!this._isFirstRender && !disableZoom) {
|
|
167
|
-
const transform = zoomTransform(this.g.node());
|
|
168
|
-
this._onZoom(transform);
|
|
169
|
-
}
|
|
170
201
|
// While the graph is animating we disable pointer events on the graph group
|
|
171
202
|
if (animDuration) {
|
|
172
203
|
this._graphGroup.attr('pointer-events', 'none');
|
|
@@ -180,9 +211,9 @@ class Graph extends ComponentCore {
|
|
|
180
211
|
this._setUpComponentEventsThrottled();
|
|
181
212
|
this._setCustomAttributesThrottled();
|
|
182
213
|
// On render complete callback
|
|
183
|
-
(
|
|
214
|
+
(_c = (_b = this.config).onRenderComplete) === null || _c === void 0 ? void 0 : _c.call(_b, this.g, datamodel.nodes, datamodel.links, this.config, animDuration, this._scale, this._containerWidth, this._containerHeight);
|
|
215
|
+
this._isFirstRender = false;
|
|
184
216
|
});
|
|
185
|
-
this._isFirstRender = false;
|
|
186
217
|
}
|
|
187
218
|
_drawNodes(duration) {
|
|
188
219
|
const { config, datamodel } = this;
|
|
@@ -204,9 +235,9 @@ class Graph extends ComponentCore {
|
|
|
204
235
|
const thisRef = this;
|
|
205
236
|
if (!config.disableDrag) {
|
|
206
237
|
const dragBehaviour = drag()
|
|
207
|
-
.on('start', function (event, d) {
|
|
208
|
-
|
|
209
|
-
|
|
238
|
+
.on('start drag end', function (event, d) {
|
|
239
|
+
thisRef._handleDrag(d, event, select(this));
|
|
240
|
+
});
|
|
210
241
|
nodeGroupsMerged.call(dragBehaviour);
|
|
211
242
|
}
|
|
212
243
|
else {
|
|
@@ -261,7 +292,6 @@ class Graph extends ComponentCore {
|
|
|
261
292
|
_calculateLayout() {
|
|
262
293
|
return __awaiter(this, void 0, void 0, function* () {
|
|
263
294
|
const { config, datamodel } = this;
|
|
264
|
-
const firstRender = this._isFirstRender;
|
|
265
295
|
// If the layout type has changed, we need to reset the node positions if they were fixed before
|
|
266
296
|
if (this._currentLayoutType !== config.layoutType) {
|
|
267
297
|
for (const node of datamodel.nodes) {
|
|
@@ -300,7 +330,6 @@ class Graph extends ComponentCore {
|
|
|
300
330
|
this._initPanelsData();
|
|
301
331
|
this._shouldRecalculateLayout = false;
|
|
302
332
|
this._currentLayoutType = config.layoutType;
|
|
303
|
-
return firstRender;
|
|
304
333
|
});
|
|
305
334
|
}
|
|
306
335
|
_initPanelsData() {
|
|
@@ -311,9 +340,10 @@ class Graph extends ComponentCore {
|
|
|
311
340
|
this._shouldSetPanels = false;
|
|
312
341
|
}
|
|
313
342
|
}
|
|
314
|
-
_fit(duration = 0) {
|
|
343
|
+
_fit(duration = 0, nodeIds) {
|
|
315
344
|
const { datamodel: { nodes } } = this;
|
|
316
|
-
const
|
|
345
|
+
const fitViewNodes = (nodeIds === null || nodeIds === void 0 ? void 0 : nodeIds.length) ? nodes.filter(n => nodeIds.includes(n.id)) : nodes;
|
|
346
|
+
const transform = this._getTransform(fitViewNodes);
|
|
317
347
|
smartTransition(this.g, duration)
|
|
318
348
|
.call(this._zoomBehavior.transform, transform);
|
|
319
349
|
this._onZoom(transform);
|
|
@@ -348,38 +378,45 @@ class Graph extends ComponentCore {
|
|
|
348
378
|
.scale(clampedScale);
|
|
349
379
|
return transform;
|
|
350
380
|
}
|
|
351
|
-
|
|
352
|
-
const {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
this._selectedNode = node;
|
|
356
|
-
// Apply grey out
|
|
357
|
-
// Grey out all nodes
|
|
358
|
-
nodes.forEach(n => {
|
|
381
|
+
_setNodeSelectionState(nodesToSelect) {
|
|
382
|
+
const { config, datamodel } = this;
|
|
383
|
+
// Grey out all nodes and set us unselected
|
|
384
|
+
for (const n of datamodel.nodes) {
|
|
359
385
|
n._state.selected = false;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
386
|
+
if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
|
|
387
|
+
n._state.greyout = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
// Grey out all links and set us unselected
|
|
391
|
+
for (const l of datamodel.links) {
|
|
365
392
|
l._state.selected = false;
|
|
393
|
+
if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
|
|
394
|
+
l._state.greyout = true;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// Filter out non-existing nodes
|
|
398
|
+
this._selectedNodes = nodesToSelect.filter(n => {
|
|
399
|
+
const doesNodeExist = Boolean(n);
|
|
400
|
+
if (!doesNodeExist)
|
|
401
|
+
console.warn('Unovis | Graph: Select Node: Not found');
|
|
402
|
+
return doesNodeExist;
|
|
366
403
|
});
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
404
|
+
// Set provided nodes as selected and ungreyout
|
|
405
|
+
for (const n of this._selectedNodes) {
|
|
406
|
+
n._state.selected = true;
|
|
407
|
+
n._state.greyout = false;
|
|
408
|
+
}
|
|
409
|
+
// Highlight connected links and nodes
|
|
410
|
+
if (config.nodeSelectionHighlightMode === GraphNodeSelectionHighlightMode.GreyoutNonConnected) {
|
|
411
|
+
const connectedLinks = datamodel.links.filter(l => this._selectedNodes.includes(l.source) || this._selectedNodes.includes(l.target));
|
|
372
412
|
connectedLinks.forEach(l => {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
source._state.greyout = false;
|
|
376
|
-
target._state.greyout = false;
|
|
413
|
+
l.source._state.greyout = false;
|
|
414
|
+
l.target._state.greyout = false;
|
|
377
415
|
l._state.greyout = false;
|
|
378
416
|
});
|
|
379
417
|
}
|
|
380
|
-
this._updateSelectedElements();
|
|
381
418
|
}
|
|
382
|
-
|
|
419
|
+
_setLinkSelectionState(link) {
|
|
383
420
|
const { datamodel: { nodes, links } } = this;
|
|
384
421
|
if (!link)
|
|
385
422
|
console.warn('Unovis: Graph: Select Link: Not found');
|
|
@@ -409,13 +446,12 @@ class Graph extends ComponentCore {
|
|
|
409
446
|
});
|
|
410
447
|
if (link)
|
|
411
448
|
link._state.selected = true;
|
|
412
|
-
this._updateSelectedElements();
|
|
413
449
|
}
|
|
414
|
-
|
|
450
|
+
_resetSelectionGreyoutState() {
|
|
415
451
|
const { datamodel: { nodes, links } } = this;
|
|
416
|
-
this.
|
|
452
|
+
this._selectedNodes = [];
|
|
417
453
|
this._selectedLink = undefined;
|
|
418
|
-
// Disable
|
|
454
|
+
// Disable Greyout
|
|
419
455
|
nodes.forEach(n => {
|
|
420
456
|
delete n._state.selected;
|
|
421
457
|
delete n._state.greyout;
|
|
@@ -424,27 +460,28 @@ class Graph extends ComponentCore {
|
|
|
424
460
|
delete l._state.greyout;
|
|
425
461
|
delete l._state.selected;
|
|
426
462
|
});
|
|
427
|
-
this._updateSelectedElements();
|
|
428
463
|
}
|
|
429
|
-
|
|
464
|
+
_updateNodesLinksPartial() {
|
|
430
465
|
const { config } = this;
|
|
431
466
|
const linkElements = this._linksGroup.selectAll(`.${gLink}`);
|
|
432
|
-
linkElements.call(
|
|
467
|
+
linkElements.call(updateLinksPartial, config, this._scale);
|
|
433
468
|
const nodeElements = this._nodesGroup.selectAll(`.${gNode}`);
|
|
434
|
-
nodeElements.call(
|
|
435
|
-
// this._drawPanels(nodeElements, 0)
|
|
469
|
+
nodeElements.call(updateNodesPartial, config, config.duration, this._scale);
|
|
436
470
|
}
|
|
437
471
|
_onBackgroundClick() {
|
|
438
|
-
this.
|
|
472
|
+
this._resetSelectionGreyoutState();
|
|
473
|
+
this._updateNodesLinksPartial();
|
|
439
474
|
}
|
|
440
475
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
441
476
|
_onNodeClick(d) {
|
|
442
477
|
}
|
|
443
478
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
444
479
|
_onNodeMouseOut(d) {
|
|
480
|
+
this._updateNodesLinksPartial();
|
|
445
481
|
}
|
|
446
482
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
447
483
|
_onNodeMouseOver(d) {
|
|
484
|
+
this._updateNodesLinksPartial();
|
|
448
485
|
}
|
|
449
486
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
450
487
|
_onLinkClick(d) {
|
|
@@ -453,13 +490,13 @@ class Graph extends ComponentCore {
|
|
|
453
490
|
if (this._isDragging)
|
|
454
491
|
return;
|
|
455
492
|
d._state.hovered = true;
|
|
456
|
-
this.
|
|
493
|
+
this._updateNodesLinksPartial();
|
|
457
494
|
}
|
|
458
495
|
_onLinkMouseOut(d) {
|
|
459
496
|
if (this._isDragging)
|
|
460
497
|
return;
|
|
461
498
|
delete d._state.hovered;
|
|
462
|
-
this.
|
|
499
|
+
this._updateNodesLinksPartial();
|
|
463
500
|
}
|
|
464
501
|
_onLinkFlowTimerFrame(elapsed = 0) {
|
|
465
502
|
const { config: { linkFlow, linkFlowAnimDuration }, datamodel: { links } } = this;
|
|
@@ -478,7 +515,7 @@ class Graph extends ComponentCore {
|
|
|
478
515
|
this._scale = transform.k;
|
|
479
516
|
this._graphGroup.attr('transform', transform.toString());
|
|
480
517
|
if (isFunction(config.onZoom))
|
|
481
|
-
config.onZoom(this._scale, config.zoomScaleExtent, event);
|
|
518
|
+
config.onZoom(this._scale, config.zoomScaleExtent, event, transform);
|
|
482
519
|
// console.warn('Unovis | Graph: Zoom: ', transform)
|
|
483
520
|
if (!this._initialTransform)
|
|
484
521
|
this._initialTransform = transform;
|
|
@@ -503,27 +540,31 @@ class Graph extends ComponentCore {
|
|
|
503
540
|
this._linksGroup.selectAll(`.${gLink}`)
|
|
504
541
|
.call((nodes.length > config.zoomThrottledUpdateNodeThreshold ? zoomLinksThrottled : zoomLinks), config, this._scale, this._getLinkArrowDefId);
|
|
505
542
|
}
|
|
506
|
-
|
|
507
|
-
var _a;
|
|
543
|
+
_onZoomStart(t, event) {
|
|
508
544
|
const { config } = this;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
545
|
+
const transform = t || event.transform;
|
|
546
|
+
this._scale = transform.k;
|
|
547
|
+
if (isFunction(config.onZoomStart))
|
|
548
|
+
config.onZoomStart(this._scale, config.zoomScaleExtent, event, transform);
|
|
513
549
|
}
|
|
514
|
-
|
|
515
|
-
var _a, _b, _c;
|
|
550
|
+
_onZoomEnd(t, event) {
|
|
516
551
|
const { config } = this;
|
|
552
|
+
const transform = t || event.transform;
|
|
553
|
+
this._scale = transform.k;
|
|
554
|
+
if (isFunction(config.onZoomEnd))
|
|
555
|
+
config.onZoomEnd(this._scale, config.zoomScaleExtent, event, transform);
|
|
556
|
+
}
|
|
557
|
+
_updateNodePosition(d, x, y) {
|
|
558
|
+
var _a, _b;
|
|
517
559
|
const transform = zoomTransform(this.g.node());
|
|
518
560
|
const scale = transform.k;
|
|
519
561
|
// Prevent the node from being dragged offscreen or outside its panel
|
|
520
562
|
const panels = (_b = (_a = this._panels) === null || _a === void 0 ? void 0 : _a.filter(p => p.nodes.includes(d._id))) !== null && _b !== void 0 ? _b : [];
|
|
521
|
-
const nodeSizeValue = getNodeSize(d, config.nodeSize, d._index);
|
|
563
|
+
const nodeSizeValue = getNodeSize(d, this.config.nodeSize, d._index);
|
|
522
564
|
const maxY = min([(this._height - transform.y) / scale, ...panels.map(p => p._y + p._height)]) - nodeSizeValue / 2;
|
|
523
565
|
const maxX = min([(this._width - transform.x) / scale, ...panels.map(p => p._x + p._width)]) - nodeSizeValue / 2;
|
|
524
566
|
const minY = max([-transform.y / scale, ...panels.map(p => p._y)]) + nodeSizeValue / 2;
|
|
525
567
|
const minX = max([-transform.x / scale, ...panels.map(p => p._x)]) + nodeSizeValue / 2;
|
|
526
|
-
let [x, y] = pointer(event, this._graphGroup.node());
|
|
527
568
|
if (y < minY)
|
|
528
569
|
y = minY;
|
|
529
570
|
else if (y > maxY)
|
|
@@ -544,6 +585,62 @@ class Graph extends ComponentCore {
|
|
|
544
585
|
delete d._state.fx;
|
|
545
586
|
if (d._state.fy === d.y)
|
|
546
587
|
delete d._state.fy;
|
|
588
|
+
}
|
|
589
|
+
_onBrush(event) {
|
|
590
|
+
var _a;
|
|
591
|
+
if (!event.selection || !event.sourceEvent)
|
|
592
|
+
return;
|
|
593
|
+
const { config } = this;
|
|
594
|
+
const transform = zoomTransform(this._graphGroup.node());
|
|
595
|
+
const [xMin, yMin] = transform.invert(event.selection[0]);
|
|
596
|
+
const [xMax, yMax] = transform.invert(event.selection[1]);
|
|
597
|
+
// Update brushed nodes
|
|
598
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
599
|
+
.each(n => {
|
|
600
|
+
const x = getX(n);
|
|
601
|
+
const y = getY(n);
|
|
602
|
+
n._state.brushed = x >= xMin && x <= xMax && y >= yMin && y <= yMax;
|
|
603
|
+
})
|
|
604
|
+
.classed(brushed, n => n._state.brushed);
|
|
605
|
+
const brushedNodes = this._nodesGroup.selectAll(`.${brushed}`)
|
|
606
|
+
.call(updateNodesPartial, config, 0, this._scale);
|
|
607
|
+
this._brush.classed('active', event.type !== 'end');
|
|
608
|
+
(_a = config.onNodeSelectionBrush) === null || _a === void 0 ? void 0 : _a.call(config, brushedNodes.data(), event);
|
|
609
|
+
}
|
|
610
|
+
_handleDrag(d, event, nodeSelection) {
|
|
611
|
+
if (event.sourceEvent.shiftKey && d._state.brushed) {
|
|
612
|
+
this._dragSelectedNodes(event);
|
|
613
|
+
}
|
|
614
|
+
else if (!event.sourceEvent.shiftKey) {
|
|
615
|
+
switch (event.type) {
|
|
616
|
+
case 'start':
|
|
617
|
+
this._onDragStarted(d, event, nodeSelection);
|
|
618
|
+
break;
|
|
619
|
+
case 'drag':
|
|
620
|
+
this._onDragged(d, event);
|
|
621
|
+
break;
|
|
622
|
+
case 'end':
|
|
623
|
+
this._onDragEnded(d, event, nodeSelection);
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
_onDragStarted(d, event, nodeSelection) {
|
|
629
|
+
var _a;
|
|
630
|
+
const { config } = this;
|
|
631
|
+
this._isDragging = true;
|
|
632
|
+
d._state.isDragged = true;
|
|
633
|
+
nodeSelection.call(updateNodes, config, 0, this._scale);
|
|
634
|
+
(_a = config.onNodeDragStart) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
635
|
+
}
|
|
636
|
+
_onDragged(d, event) {
|
|
637
|
+
var _a;
|
|
638
|
+
const { config } = this;
|
|
639
|
+
const transform = zoomTransform(this.g.node());
|
|
640
|
+
const scale = transform.k;
|
|
641
|
+
// Update node position
|
|
642
|
+
const [x, y] = pointer(event, this._graphGroup.node());
|
|
643
|
+
this._updateNodePosition(d, x, y);
|
|
547
644
|
// Update affected DOM elements
|
|
548
645
|
const nodeSelection = this._nodesGroup.selectAll(`.${gNode}`);
|
|
549
646
|
const nodeToUpdate = nodeSelection.filter((n) => n._id === d._id);
|
|
@@ -558,7 +655,7 @@ class Graph extends ComponentCore {
|
|
|
558
655
|
const linksToAnimate = linksToUpdate.filter(d => d._state.greyout);
|
|
559
656
|
if (linksToAnimate.size())
|
|
560
657
|
animateLinkFlow(linksToAnimate, config, this._scale);
|
|
561
|
-
(
|
|
658
|
+
(_a = config.onNodeDrag) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
562
659
|
}
|
|
563
660
|
_onDragEnded(d, event, nodeSelection) {
|
|
564
661
|
var _a;
|
|
@@ -568,6 +665,49 @@ class Graph extends ComponentCore {
|
|
|
568
665
|
nodeSelection.call(updateNodes, config, 0, this._scale);
|
|
569
666
|
(_a = config.onNodeDragEnd) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
570
667
|
}
|
|
668
|
+
_dragSelectedNodes(event) {
|
|
669
|
+
var _a, _b;
|
|
670
|
+
const { config } = this;
|
|
671
|
+
const curr = pointer(event, this._graphGroup.node());
|
|
672
|
+
const selectedNodes = smartTransition(this._nodesGroup.selectAll(`.${brushed}`));
|
|
673
|
+
if (event.type === 'start') {
|
|
674
|
+
this._groupDragInit = curr;
|
|
675
|
+
this._isDragging = true;
|
|
676
|
+
selectedNodes.each(n => {
|
|
677
|
+
n.x = getX(n);
|
|
678
|
+
n.y = getY(n);
|
|
679
|
+
n._state.isDragged = true;
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
else if (event.type === 'drag') {
|
|
683
|
+
const dx = curr[0] - this._groupDragInit[0];
|
|
684
|
+
const dy = curr[1] - this._groupDragInit[1];
|
|
685
|
+
selectedNodes.each(n => this._updateNodePosition(n, n.x + dx, n.y + dy));
|
|
686
|
+
const connectedLinks = smartTransition(this._linksGroup.selectAll(`.${gLink}`)
|
|
687
|
+
.filter(l => { var _a, _b, _c, _d; return ((_b = (_a = l.source) === null || _a === void 0 ? void 0 : _a._state) === null || _b === void 0 ? void 0 : _b.isDragged) || ((_d = (_c = l.target) === null || _c === void 0 ? void 0 : _c._state) === null || _d === void 0 ? void 0 : _d.isDragged); }));
|
|
688
|
+
connectedLinks.call(updateLinks, this.config, 0, this._scale, this._getLinkArrowDefId);
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
this._isDragging = false;
|
|
692
|
+
selectedNodes.each(n => { n._state.isDragged = false; });
|
|
693
|
+
}
|
|
694
|
+
selectedNodes.call(updateNodes, config, 0, this._scale);
|
|
695
|
+
(_b = (_a = this.config).onNodeSelectionDrag) === null || _b === void 0 ? void 0 : _b.call(_a, selectedNodes.data(), event);
|
|
696
|
+
}
|
|
697
|
+
_activateBrush() {
|
|
698
|
+
this._brush.classed('active', true);
|
|
699
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
700
|
+
.classed(brushable, true);
|
|
701
|
+
}
|
|
702
|
+
_clearBrush() {
|
|
703
|
+
var _a;
|
|
704
|
+
this._brush.classed('active', false).call((_a = this._brushBehavior) === null || _a === void 0 ? void 0 : _a.clear);
|
|
705
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
706
|
+
.classed(brushable, false)
|
|
707
|
+
.classed(brushed, false)
|
|
708
|
+
.each(n => { n._state.brushed = false; })
|
|
709
|
+
.call(updateNodesPartial, this.config, 0, this._scale);
|
|
710
|
+
}
|
|
571
711
|
_shouldLayoutRecalculate() {
|
|
572
712
|
const { prevConfig, config } = this;
|
|
573
713
|
if (prevConfig.layoutType !== config.layoutType)
|
|
@@ -637,9 +777,10 @@ class Graph extends ComponentCore {
|
|
|
637
777
|
getZoom() {
|
|
638
778
|
return zoomTransform(this.g.node()).k;
|
|
639
779
|
}
|
|
640
|
-
fitView(duration = this.config.duration) {
|
|
641
|
-
|
|
642
|
-
|
|
780
|
+
fitView(duration = this.config.duration, nodeIds) {
|
|
781
|
+
var _a;
|
|
782
|
+
(_a = this._layoutCalculationPromise) === null || _a === void 0 ? void 0 : _a.then(() => {
|
|
783
|
+
this._fit(duration, nodeIds);
|
|
643
784
|
});
|
|
644
785
|
}
|
|
645
786
|
/** Enable automatic fitting to container if it was disabled due to previous zoom / pan interactions */
|