@unovis/ts 1.5.0-alpha.8 → 1.5.0-nikita.1
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/graph/config.d.ts +23 -3
- package/components/graph/config.js +3 -3
- package/components/graph/config.js.map +1 -1
- package/components/graph/index.d.ts +15 -5
- package/components/graph/index.js +209 -86
- 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 +7 -4
- 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/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/data-models/graph.d.ts +2 -0
- package/data-models/graph.js +6 -0
- package/data-models/graph.js.map +1 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/types/graph.d.ts +2 -0
- package/types.js +1 -1
|
@@ -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,14 @@ 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
60
|
.on('zoom', (e) => this._onZoom(e.transform, e));
|
|
61
|
+
this._brushBehavior = brush$1()
|
|
62
|
+
.on('start brush end', this._onBrush.bind(this))
|
|
63
|
+
.filter(event => event.shiftKey)
|
|
64
|
+
.keyModifiers(false);
|
|
59
65
|
this._panelsGroup = this._graphGroup.append('g').attr('class', panels);
|
|
60
66
|
this._linksGroup = this._graphGroup.append('g').attr('class', links);
|
|
61
67
|
this._nodesGroup = this._graphGroup.append('g').attr('class', nodes);
|
|
@@ -63,13 +69,19 @@ class Graph extends ComponentCore {
|
|
|
63
69
|
this._getLinkArrowDefId = this._getLinkArrowDefId.bind(this);
|
|
64
70
|
}
|
|
65
71
|
get selectedNode() {
|
|
66
|
-
|
|
72
|
+
var _a;
|
|
73
|
+
return (_a = this._selectedNodes) === null || _a === void 0 ? void 0 : _a[0];
|
|
74
|
+
}
|
|
75
|
+
get selectedNodes() {
|
|
76
|
+
return this._selectedNodes;
|
|
67
77
|
}
|
|
68
78
|
get selectedLink() {
|
|
69
79
|
return this._selectedLink;
|
|
70
80
|
}
|
|
71
81
|
setData(data) {
|
|
72
82
|
const { config } = this;
|
|
83
|
+
if (isEqual(this.datamodel.data, data))
|
|
84
|
+
return;
|
|
73
85
|
this.datamodel.nodeSort = config.nodeSort;
|
|
74
86
|
this.datamodel.data = data;
|
|
75
87
|
this._shouldRecalculateLayout = true;
|
|
@@ -95,7 +107,7 @@ class Graph extends ComponentCore {
|
|
|
95
107
|
return { top: extraPadding, bottom: extraPadding, left: extraPadding, right: extraPadding };
|
|
96
108
|
}
|
|
97
109
|
_render(customDuration) {
|
|
98
|
-
const { config: { disableZoom, duration, layoutAutofit, zoomEventFilter }, datamodel } = this;
|
|
110
|
+
const { config: { disableBrush, disableZoom, duration, layoutAutofit, zoomEventFilter }, datamodel } = this;
|
|
99
111
|
if (!datamodel.nodes && !datamodel.links)
|
|
100
112
|
return;
|
|
101
113
|
const animDuration = isNumber(customDuration) ? customDuration : duration;
|
|
@@ -109,6 +121,25 @@ class Graph extends ComponentCore {
|
|
|
109
121
|
this._prevWidth = this._width;
|
|
110
122
|
this._prevHeight = this._height;
|
|
111
123
|
}
|
|
124
|
+
// Handle brush behavior
|
|
125
|
+
if (!disableBrush) {
|
|
126
|
+
this._brushBehavior.extent([[0, 0], [this._width, this._height]]);
|
|
127
|
+
this._brush.call(this._brushBehavior);
|
|
128
|
+
// Activate the brush when the shift key is pressed
|
|
129
|
+
select(window)
|
|
130
|
+
.on('keydown.unovis-graph', e => e.key === 'Shift' && this._activateBrush())
|
|
131
|
+
.on('keyup.unovis-graph', e => e.key === 'Shift' && this._clearBrush());
|
|
132
|
+
this._zoomBehavior.filter(event => !event.shiftKey);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this._brush.on('.brush', null);
|
|
136
|
+
select(window)
|
|
137
|
+
.on('keydown.unovis-graph', null)
|
|
138
|
+
.on('keyup.unovis-graph', null);
|
|
139
|
+
// Clear brush in case it was disabled in an active state
|
|
140
|
+
if (this._brush.classed('active'))
|
|
141
|
+
this._clearBrush();
|
|
142
|
+
}
|
|
112
143
|
// Apply layout and render
|
|
113
144
|
if (this._shouldRecalculateLayout || !this._layoutCalculationPromise) {
|
|
114
145
|
this._layoutCalculationPromise = this._calculateLayout();
|
|
@@ -119,20 +150,21 @@ class Graph extends ComponentCore {
|
|
|
119
150
|
(_b = (_a = this.config).onLayoutCalculated) === null || _b === void 0 ? void 0 : _b.call(_a, datamodel.nodes, datamodel.links);
|
|
120
151
|
});
|
|
121
152
|
}
|
|
122
|
-
//
|
|
123
|
-
//
|
|
153
|
+
// Redefining Zoom Behavior filter to the one specified in the config,
|
|
154
|
+
// or to the default one supporting `shiftKey` for node brushing
|
|
155
|
+
// See more: https://d3js.org/d3-zoom#zoom_filter
|
|
124
156
|
this._zoomBehavior.filter(isFunction(zoomEventFilter)
|
|
125
157
|
? zoomEventFilter
|
|
126
|
-
: (e) => !e.shiftKey); // Default filter
|
|
127
|
-
this._layoutCalculationPromise.then((
|
|
128
|
-
var _a, _b;
|
|
158
|
+
: (e) => (!e.ctrlKey || e.type === 'wheel') && !e.button && !e.shiftKey); // Default filter
|
|
159
|
+
this._layoutCalculationPromise.then(() => {
|
|
160
|
+
var _a, _b, _c;
|
|
129
161
|
// If the component has been destroyed while the layout calculation
|
|
130
162
|
// was in progress, we cancel the render
|
|
131
163
|
if (this.isDestroyed())
|
|
132
164
|
return;
|
|
133
165
|
this._initPanelsData();
|
|
134
166
|
// Fit the view
|
|
135
|
-
if (
|
|
167
|
+
if (this._isFirstRender) {
|
|
136
168
|
this._fit();
|
|
137
169
|
this._shouldFitLayout = false;
|
|
138
170
|
}
|
|
@@ -140,19 +172,20 @@ class Graph extends ComponentCore {
|
|
|
140
172
|
this._fit(duration);
|
|
141
173
|
this._shouldFitLayout = false;
|
|
142
174
|
}
|
|
143
|
-
//
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const selectedNode = datamodel.nodes.find(node => node.id === this.config.selectedNodeId);
|
|
150
|
-
this._selectNode(selectedNode);
|
|
175
|
+
// Update Nodes and Links Selection State
|
|
176
|
+
this._resetSelectionGreyoutState();
|
|
177
|
+
if (this.config.selectedNodeId || this.config.selectedNodeIds) {
|
|
178
|
+
const selectedIds = (_a = this.config.selectedNodeIds) !== null && _a !== void 0 ? _a : [this.config.selectedNodeId];
|
|
179
|
+
const selectedNodes = selectedIds.map(id => datamodel.getNodeFromId(id));
|
|
180
|
+
this._setNodeSelectionState(selectedNodes);
|
|
151
181
|
}
|
|
152
182
|
if (this.config.selectedLinkId) {
|
|
153
183
|
const selectedLink = datamodel.links.find(link => link.id === this.config.selectedLinkId);
|
|
154
|
-
this.
|
|
184
|
+
this._setLinkSelectionState(selectedLink);
|
|
155
185
|
}
|
|
186
|
+
// Draw
|
|
187
|
+
this._drawNodes(animDuration);
|
|
188
|
+
this._drawLinks(animDuration);
|
|
156
189
|
// Link flow animation timer
|
|
157
190
|
if (!this._timer) {
|
|
158
191
|
const refreshRateMs = 35;
|
|
@@ -163,10 +196,6 @@ class Graph extends ComponentCore {
|
|
|
163
196
|
this.g.on('.zoom', null);
|
|
164
197
|
else
|
|
165
198
|
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
199
|
// While the graph is animating we disable pointer events on the graph group
|
|
171
200
|
if (animDuration) {
|
|
172
201
|
this._graphGroup.attr('pointer-events', 'none');
|
|
@@ -180,9 +209,9 @@ class Graph extends ComponentCore {
|
|
|
180
209
|
this._setUpComponentEventsThrottled();
|
|
181
210
|
this._setCustomAttributesThrottled();
|
|
182
211
|
// On render complete callback
|
|
183
|
-
(
|
|
212
|
+
(_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);
|
|
213
|
+
this._isFirstRender = false;
|
|
184
214
|
});
|
|
185
|
-
this._isFirstRender = false;
|
|
186
215
|
}
|
|
187
216
|
_drawNodes(duration) {
|
|
188
217
|
const { config, datamodel } = this;
|
|
@@ -204,9 +233,9 @@ class Graph extends ComponentCore {
|
|
|
204
233
|
const thisRef = this;
|
|
205
234
|
if (!config.disableDrag) {
|
|
206
235
|
const dragBehaviour = drag()
|
|
207
|
-
.on('start', function (event, d) {
|
|
208
|
-
|
|
209
|
-
|
|
236
|
+
.on('start drag end', function (event, d) {
|
|
237
|
+
thisRef._handleDrag(d, event, select(this));
|
|
238
|
+
});
|
|
210
239
|
nodeGroupsMerged.call(dragBehaviour);
|
|
211
240
|
}
|
|
212
241
|
else {
|
|
@@ -261,7 +290,6 @@ class Graph extends ComponentCore {
|
|
|
261
290
|
_calculateLayout() {
|
|
262
291
|
return __awaiter(this, void 0, void 0, function* () {
|
|
263
292
|
const { config, datamodel } = this;
|
|
264
|
-
const firstRender = this._isFirstRender;
|
|
265
293
|
// If the layout type has changed, we need to reset the node positions if they were fixed before
|
|
266
294
|
if (this._currentLayoutType !== config.layoutType) {
|
|
267
295
|
for (const node of datamodel.nodes) {
|
|
@@ -300,7 +328,6 @@ class Graph extends ComponentCore {
|
|
|
300
328
|
this._initPanelsData();
|
|
301
329
|
this._shouldRecalculateLayout = false;
|
|
302
330
|
this._currentLayoutType = config.layoutType;
|
|
303
|
-
return firstRender;
|
|
304
331
|
});
|
|
305
332
|
}
|
|
306
333
|
_initPanelsData() {
|
|
@@ -348,38 +375,45 @@ class Graph extends ComponentCore {
|
|
|
348
375
|
.scale(clampedScale);
|
|
349
376
|
return transform;
|
|
350
377
|
}
|
|
351
|
-
|
|
352
|
-
const {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
this._selectedNode = node;
|
|
356
|
-
// Apply grey out
|
|
357
|
-
// Grey out all nodes
|
|
358
|
-
nodes.forEach(n => {
|
|
378
|
+
_setNodeSelectionState(nodesToSelect) {
|
|
379
|
+
const { config, datamodel } = this;
|
|
380
|
+
// Grey out all nodes and set us unselected
|
|
381
|
+
for (const n of datamodel.nodes) {
|
|
359
382
|
n._state.selected = false;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
383
|
+
if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
|
|
384
|
+
n._state.greyout = true;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Grey out all links and set us unselected
|
|
388
|
+
for (const l of datamodel.links) {
|
|
365
389
|
l._state.selected = false;
|
|
390
|
+
if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
|
|
391
|
+
l._state.greyout = true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// Filter out non-existing nodes
|
|
395
|
+
this._selectedNodes = nodesToSelect.filter(n => {
|
|
396
|
+
const doesNodeExist = Boolean(n);
|
|
397
|
+
if (!doesNodeExist)
|
|
398
|
+
console.warn('Unovis | Graph: Select Node: Not found');
|
|
399
|
+
return doesNodeExist;
|
|
366
400
|
});
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
401
|
+
// Set provided nodes as selected and ungreyout
|
|
402
|
+
for (const n of this._selectedNodes) {
|
|
403
|
+
n._state.selected = true;
|
|
404
|
+
n._state.greyout = false;
|
|
405
|
+
}
|
|
406
|
+
// Highlight connected links and nodes
|
|
407
|
+
if (config.nodeSelectionHighlightMode === GraphNodeSelectionHighlightMode.GreyoutNonConnected) {
|
|
408
|
+
const connectedLinks = datamodel.links.filter(l => this._selectedNodes.includes(l.source) || this._selectedNodes.includes(l.target));
|
|
372
409
|
connectedLinks.forEach(l => {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
source._state.greyout = false;
|
|
376
|
-
target._state.greyout = false;
|
|
410
|
+
l.source._state.greyout = false;
|
|
411
|
+
l.target._state.greyout = false;
|
|
377
412
|
l._state.greyout = false;
|
|
378
413
|
});
|
|
379
414
|
}
|
|
380
|
-
this._updateSelectedElements();
|
|
381
415
|
}
|
|
382
|
-
|
|
416
|
+
_setLinkSelectionState(link) {
|
|
383
417
|
const { datamodel: { nodes, links } } = this;
|
|
384
418
|
if (!link)
|
|
385
419
|
console.warn('Unovis: Graph: Select Link: Not found');
|
|
@@ -409,13 +443,12 @@ class Graph extends ComponentCore {
|
|
|
409
443
|
});
|
|
410
444
|
if (link)
|
|
411
445
|
link._state.selected = true;
|
|
412
|
-
this._updateSelectedElements();
|
|
413
446
|
}
|
|
414
|
-
|
|
447
|
+
_resetSelectionGreyoutState() {
|
|
415
448
|
const { datamodel: { nodes, links } } = this;
|
|
416
|
-
this.
|
|
449
|
+
this._selectedNodes = [];
|
|
417
450
|
this._selectedLink = undefined;
|
|
418
|
-
// Disable
|
|
451
|
+
// Disable Greyout
|
|
419
452
|
nodes.forEach(n => {
|
|
420
453
|
delete n._state.selected;
|
|
421
454
|
delete n._state.greyout;
|
|
@@ -424,27 +457,28 @@ class Graph extends ComponentCore {
|
|
|
424
457
|
delete l._state.greyout;
|
|
425
458
|
delete l._state.selected;
|
|
426
459
|
});
|
|
427
|
-
this._updateSelectedElements();
|
|
428
460
|
}
|
|
429
|
-
|
|
461
|
+
_updateNodesLinksPartial() {
|
|
430
462
|
const { config } = this;
|
|
431
463
|
const linkElements = this._linksGroup.selectAll(`.${gLink}`);
|
|
432
|
-
linkElements.call(
|
|
464
|
+
linkElements.call(updateLinksPartial, config, this._scale);
|
|
433
465
|
const nodeElements = this._nodesGroup.selectAll(`.${gNode}`);
|
|
434
|
-
nodeElements.call(
|
|
435
|
-
// this._drawPanels(nodeElements, 0)
|
|
466
|
+
nodeElements.call(updateNodesPartial, config, config.duration, this._scale);
|
|
436
467
|
}
|
|
437
468
|
_onBackgroundClick() {
|
|
438
|
-
this.
|
|
469
|
+
this._resetSelectionGreyoutState();
|
|
470
|
+
this._updateNodesLinksPartial();
|
|
439
471
|
}
|
|
440
472
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
441
473
|
_onNodeClick(d) {
|
|
442
474
|
}
|
|
443
475
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
444
476
|
_onNodeMouseOut(d) {
|
|
477
|
+
this._updateNodesLinksPartial();
|
|
445
478
|
}
|
|
446
479
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
447
480
|
_onNodeMouseOver(d) {
|
|
481
|
+
this._updateNodesLinksPartial();
|
|
448
482
|
}
|
|
449
483
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
450
484
|
_onLinkClick(d) {
|
|
@@ -453,13 +487,13 @@ class Graph extends ComponentCore {
|
|
|
453
487
|
if (this._isDragging)
|
|
454
488
|
return;
|
|
455
489
|
d._state.hovered = true;
|
|
456
|
-
this.
|
|
490
|
+
this._updateNodesLinksPartial();
|
|
457
491
|
}
|
|
458
492
|
_onLinkMouseOut(d) {
|
|
459
493
|
if (this._isDragging)
|
|
460
494
|
return;
|
|
461
495
|
delete d._state.hovered;
|
|
462
|
-
this.
|
|
496
|
+
this._updateNodesLinksPartial();
|
|
463
497
|
}
|
|
464
498
|
_onLinkFlowTimerFrame(elapsed = 0) {
|
|
465
499
|
const { config: { linkFlow, linkFlowAnimDuration }, datamodel: { links } } = this;
|
|
@@ -478,7 +512,7 @@ class Graph extends ComponentCore {
|
|
|
478
512
|
this._scale = transform.k;
|
|
479
513
|
this._graphGroup.attr('transform', transform.toString());
|
|
480
514
|
if (isFunction(config.onZoom))
|
|
481
|
-
config.onZoom(this._scale, config.zoomScaleExtent, event);
|
|
515
|
+
config.onZoom(this._scale, config.zoomScaleExtent, event, transform);
|
|
482
516
|
// console.warn('Unovis | Graph: Zoom: ', transform)
|
|
483
517
|
if (!this._initialTransform)
|
|
484
518
|
this._initialTransform = transform;
|
|
@@ -503,27 +537,17 @@ class Graph extends ComponentCore {
|
|
|
503
537
|
this._linksGroup.selectAll(`.${gLink}`)
|
|
504
538
|
.call((nodes.length > config.zoomThrottledUpdateNodeThreshold ? zoomLinksThrottled : zoomLinks), config, this._scale, this._getLinkArrowDefId);
|
|
505
539
|
}
|
|
506
|
-
|
|
507
|
-
var _a;
|
|
508
|
-
const { config } = this;
|
|
509
|
-
this._isDragging = true;
|
|
510
|
-
d._state.isDragged = true;
|
|
511
|
-
nodeSelection.call(updateNodes, config, 0, this._scale);
|
|
512
|
-
(_a = config.onNodeDragStart) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
513
|
-
}
|
|
514
|
-
_onDragged(d, event, allNodesSelection) {
|
|
515
|
-
var _a, _b, _c;
|
|
516
|
-
const { config } = this;
|
|
540
|
+
_updateNodePosition(d, x, y) {
|
|
541
|
+
var _a, _b;
|
|
517
542
|
const transform = zoomTransform(this.g.node());
|
|
518
543
|
const scale = transform.k;
|
|
519
544
|
// Prevent the node from being dragged offscreen or outside its panel
|
|
520
545
|
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);
|
|
546
|
+
const nodeSizeValue = getNodeSize(d, this.config.nodeSize, d._index);
|
|
522
547
|
const maxY = min([(this._height - transform.y) / scale, ...panels.map(p => p._y + p._height)]) - nodeSizeValue / 2;
|
|
523
548
|
const maxX = min([(this._width - transform.x) / scale, ...panels.map(p => p._x + p._width)]) - nodeSizeValue / 2;
|
|
524
549
|
const minY = max([-transform.y / scale, ...panels.map(p => p._y)]) + nodeSizeValue / 2;
|
|
525
550
|
const minX = max([-transform.x / scale, ...panels.map(p => p._x)]) + nodeSizeValue / 2;
|
|
526
|
-
let [x, y] = pointer(event, this._graphGroup.node());
|
|
527
551
|
if (y < minY)
|
|
528
552
|
y = minY;
|
|
529
553
|
else if (y > maxY)
|
|
@@ -544,6 +568,62 @@ class Graph extends ComponentCore {
|
|
|
544
568
|
delete d._state.fx;
|
|
545
569
|
if (d._state.fy === d.y)
|
|
546
570
|
delete d._state.fy;
|
|
571
|
+
}
|
|
572
|
+
_onBrush(event) {
|
|
573
|
+
var _a;
|
|
574
|
+
if (!event.selection || !event.sourceEvent)
|
|
575
|
+
return;
|
|
576
|
+
const { config } = this;
|
|
577
|
+
const transform = zoomTransform(this._graphGroup.node());
|
|
578
|
+
const [xMin, yMin] = transform.invert(event.selection[0]);
|
|
579
|
+
const [xMax, yMax] = transform.invert(event.selection[1]);
|
|
580
|
+
// Update brushed nodes
|
|
581
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
582
|
+
.each(n => {
|
|
583
|
+
const x = getX(n);
|
|
584
|
+
const y = getY(n);
|
|
585
|
+
n._state.brushed = x >= xMin && x <= xMax && y >= yMin && y <= yMax;
|
|
586
|
+
})
|
|
587
|
+
.classed(brushed, n => n._state.brushed);
|
|
588
|
+
const brushedNodes = this._nodesGroup.selectAll(`.${brushed}`)
|
|
589
|
+
.call(updateNodesPartial, config, 0, this._scale);
|
|
590
|
+
this._brush.classed('active', event.type !== 'end');
|
|
591
|
+
(_a = config.onNodeSelectionBrush) === null || _a === void 0 ? void 0 : _a.call(config, brushedNodes.data(), event);
|
|
592
|
+
}
|
|
593
|
+
_handleDrag(d, event, nodeSelection) {
|
|
594
|
+
if (event.sourceEvent.shiftKey && d._state.brushed) {
|
|
595
|
+
this._dragSelectedNodes(event);
|
|
596
|
+
}
|
|
597
|
+
else if (!event.sourceEvent.shiftKey) {
|
|
598
|
+
switch (event.type) {
|
|
599
|
+
case 'start':
|
|
600
|
+
this._onDragStarted(d, event, nodeSelection);
|
|
601
|
+
break;
|
|
602
|
+
case 'drag':
|
|
603
|
+
this._onDragged(d, event);
|
|
604
|
+
break;
|
|
605
|
+
case 'end':
|
|
606
|
+
this._onDragEnded(d, event, nodeSelection);
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
_onDragStarted(d, event, nodeSelection) {
|
|
612
|
+
var _a;
|
|
613
|
+
const { config } = this;
|
|
614
|
+
this._isDragging = true;
|
|
615
|
+
d._state.isDragged = true;
|
|
616
|
+
nodeSelection.call(updateNodes, config, 0, this._scale);
|
|
617
|
+
(_a = config.onNodeDragStart) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
618
|
+
}
|
|
619
|
+
_onDragged(d, event) {
|
|
620
|
+
var _a;
|
|
621
|
+
const { config } = this;
|
|
622
|
+
const transform = zoomTransform(this.g.node());
|
|
623
|
+
const scale = transform.k;
|
|
624
|
+
// Update node position
|
|
625
|
+
const [x, y] = pointer(event, this._graphGroup.node());
|
|
626
|
+
this._updateNodePosition(d, x, y);
|
|
547
627
|
// Update affected DOM elements
|
|
548
628
|
const nodeSelection = this._nodesGroup.selectAll(`.${gNode}`);
|
|
549
629
|
const nodeToUpdate = nodeSelection.filter((n) => n._id === d._id);
|
|
@@ -558,7 +638,7 @@ class Graph extends ComponentCore {
|
|
|
558
638
|
const linksToAnimate = linksToUpdate.filter(d => d._state.greyout);
|
|
559
639
|
if (linksToAnimate.size())
|
|
560
640
|
animateLinkFlow(linksToAnimate, config, this._scale);
|
|
561
|
-
(
|
|
641
|
+
(_a = config.onNodeDrag) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
562
642
|
}
|
|
563
643
|
_onDragEnded(d, event, nodeSelection) {
|
|
564
644
|
var _a;
|
|
@@ -568,6 +648,49 @@ class Graph extends ComponentCore {
|
|
|
568
648
|
nodeSelection.call(updateNodes, config, 0, this._scale);
|
|
569
649
|
(_a = config.onNodeDragEnd) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
|
|
570
650
|
}
|
|
651
|
+
_dragSelectedNodes(event) {
|
|
652
|
+
var _a, _b;
|
|
653
|
+
const { config } = this;
|
|
654
|
+
const curr = pointer(event, this._graphGroup.node());
|
|
655
|
+
const selectedNodes = smartTransition(this._nodesGroup.selectAll(`.${brushed}`));
|
|
656
|
+
if (event.type === 'start') {
|
|
657
|
+
this._groupDragInit = curr;
|
|
658
|
+
this._isDragging = true;
|
|
659
|
+
selectedNodes.each(n => {
|
|
660
|
+
n.x = getX(n);
|
|
661
|
+
n.y = getY(n);
|
|
662
|
+
n._state.isDragged = true;
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
else if (event.type === 'drag') {
|
|
666
|
+
const dx = curr[0] - this._groupDragInit[0];
|
|
667
|
+
const dy = curr[1] - this._groupDragInit[1];
|
|
668
|
+
selectedNodes.each(n => this._updateNodePosition(n, n.x + dx, n.y + dy));
|
|
669
|
+
const connectedLinks = smartTransition(this._linksGroup.selectAll(`.${gLink}`)
|
|
670
|
+
.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); }));
|
|
671
|
+
connectedLinks.call(updateLinks, this.config, 0, this._scale, this._getLinkArrowDefId);
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
this._isDragging = false;
|
|
675
|
+
selectedNodes.each(n => { n._state.isDragged = false; });
|
|
676
|
+
}
|
|
677
|
+
selectedNodes.call(updateNodes, config, 0, this._scale);
|
|
678
|
+
(_b = (_a = this.config).onNodeSelectionDrag) === null || _b === void 0 ? void 0 : _b.call(_a, selectedNodes.data(), event);
|
|
679
|
+
}
|
|
680
|
+
_activateBrush() {
|
|
681
|
+
this._brush.classed('active', true);
|
|
682
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
683
|
+
.classed(brushable, true);
|
|
684
|
+
}
|
|
685
|
+
_clearBrush() {
|
|
686
|
+
var _a;
|
|
687
|
+
this._brush.classed('active', false).call((_a = this._brushBehavior) === null || _a === void 0 ? void 0 : _a.clear);
|
|
688
|
+
this._nodesGroup.selectAll(`.${gNode}`)
|
|
689
|
+
.classed(brushable, false)
|
|
690
|
+
.classed(brushed, false)
|
|
691
|
+
.each(n => { n._state.brushed = false; })
|
|
692
|
+
.call(updateNodesPartial, this.config, 0, this._scale);
|
|
693
|
+
}
|
|
571
694
|
_shouldLayoutRecalculate() {
|
|
572
695
|
const { prevConfig, config } = this;
|
|
573
696
|
if (prevConfig.layoutType !== config.layoutType)
|