@unovis/ts 1.5.0-version.9 → 1.5.1-exf.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.
Files changed (86) hide show
  1. package/components/axis/config.d.ts +10 -1
  2. package/components/axis/config.js +1 -1
  3. package/components/axis/config.js.map +1 -1
  4. package/components/axis/index.d.ts +19 -19
  5. package/components/axis/index.js +63 -8
  6. package/components/axis/index.js.map +1 -1
  7. package/components/axis/style.d.ts +1 -0
  8. package/components/axis/style.js +7 -2
  9. package/components/axis/style.js.map +1 -1
  10. package/components/crosshair/index.js +6 -0
  11. package/components/crosshair/index.js.map +1 -1
  12. package/components/donut/config.d.ts +4 -0
  13. package/components/donut/config.js +1 -1
  14. package/components/donut/config.js.map +1 -1
  15. package/components/donut/index.d.ts +2 -0
  16. package/components/donut/index.js +41 -6
  17. package/components/donut/index.js.map +1 -1
  18. package/components/graph/config.d.ts +39 -6
  19. package/components/graph/config.js +7 -4
  20. package/components/graph/config.js.map +1 -1
  21. package/components/graph/index.d.ts +22 -15
  22. package/components/graph/index.js +233 -98
  23. package/components/graph/index.js.map +1 -1
  24. package/components/graph/modules/link/index.d.ts +2 -1
  25. package/components/graph/modules/link/index.js +87 -51
  26. package/components/graph/modules/link/index.js.map +1 -1
  27. package/components/graph/modules/link/style.js +0 -2
  28. package/components/graph/modules/link/style.js.map +1 -1
  29. package/components/graph/modules/node/index.d.ts +2 -1
  30. package/components/graph/modules/node/index.js +47 -23
  31. package/components/graph/modules/node/index.js.map +1 -1
  32. package/components/graph/modules/node/style.d.ts +2 -0
  33. package/components/graph/modules/node/style.js +34 -4
  34. package/components/graph/modules/node/style.js.map +1 -1
  35. package/components/graph/modules/panel/index.js +1 -0
  36. package/components/graph/modules/panel/index.js.map +1 -1
  37. package/components/graph/style.d.ts +1 -0
  38. package/components/graph/style.js +22 -1
  39. package/components/graph/style.js.map +1 -1
  40. package/components/graph/types.d.ts +8 -0
  41. package/components/graph/types.js +8 -2
  42. package/components/graph/types.js.map +1 -1
  43. package/components/leaflet-map/modules/map.js +2 -1
  44. package/components/leaflet-map/modules/map.js.map +1 -1
  45. package/components/scatter/index.d.ts +1 -0
  46. package/components/scatter/index.js +19 -12
  47. package/components/scatter/index.js.map +1 -1
  48. package/components/scatter/modules/point.js +1 -3
  49. package/components/scatter/modules/point.js.map +1 -1
  50. package/components/scatter/types.d.ts +2 -0
  51. package/components/tooltip/index.d.ts +7 -0
  52. package/components/tooltip/index.js +17 -5
  53. package/components/tooltip/index.js.map +1 -1
  54. package/components/topojson-map/config.js +2 -2
  55. package/components/topojson-map/config.js.map +1 -1
  56. package/components/topojson-map/index.js +22 -5
  57. package/components/topojson-map/index.js.map +1 -1
  58. package/containers/single-container/index.js +2 -2
  59. package/containers/single-container/index.js.map +1 -1
  60. package/containers/xy-container/index.js +4 -2
  61. package/containers/xy-container/index.js.map +1 -1
  62. package/core/container/config.d.ts +2 -4
  63. package/core/container/config.js.map +1 -1
  64. package/core/container/index.d.ts +2 -1
  65. package/core/container/index.js +26 -19
  66. package/core/container/index.js.map +1 -1
  67. package/data-models/graph.d.ts +2 -0
  68. package/data-models/graph.js +6 -0
  69. package/data-models/graph.js.map +1 -1
  70. package/index.d.ts +1 -4
  71. package/index.js +13 -5
  72. package/index.js.map +1 -1
  73. package/maps/world-simple.json.js +2430 -42
  74. package/package.json +2 -2
  75. package/types/graph.d.ts +6 -0
  76. package/types.js +1 -1
  77. package/utils/data.d.ts +2 -1
  78. package/utils/data.js +18 -8
  79. package/utils/data.js.map +1 -1
  80. package/utils/index.d.ts +12 -0
  81. package/utils/index.js +13 -0
  82. package/utils/index.js.map +1 -0
  83. package/utils/scale.js +4 -0
  84. package/utils/scale.js.map +1 -0
  85. package/utils/type.js +2 -0
  86. 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 { zoom, zoomTransform, zoomIdentity } from 'd3-zoom';
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
10
  import { isNumber, isFunction, clamp, getBoolean, shallowDiff, isPlainObject, isEqual } 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 { links, gLink, gLinkExit, link, greyedOutLink } from './modules/link/style.js';
16
+ import { nodes, gNode, gNodeExit, brushed, brushable, node, nodeGauge, sideLabelGroup, label, greyedOutNode } from './modules/node/style.js';
17
+ import { links, gLink, gLinkExit, link, linkLabelGroup, 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, updateNodeSelectedGreyout, zoomNodesThrottled, zoomNodes } from './modules/node/index.js';
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, updateSelectedLinks, animateLinkFlow, zoomLinksThrottled, zoomLinks } from './modules/link/index.js';
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
- return this._selectedNode;
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 (!config.shouldDataUpdate(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
- // Redefine Zoom Behavior filter is specified in the config
123
- // https://d3js.org/d3-zoom#zoom_filter
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((isFirstRender) => {
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 (isFirstRender) {
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
- // Draw
144
- this._drawNodes(animDuration);
145
- this._drawLinks(animDuration);
146
- // Select Links / Nodes
147
- this._resetSelection();
148
- if (this.config.selectedNodeId) {
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._selectLink(selectedLink);
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,26 +198,14 @@ 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
- // While the graph is animating we disable pointer events on the graph group
171
- if (animDuration) {
172
- this._graphGroup.attr('pointer-events', 'none');
173
- }
174
- smartTransition(this._graphGroup, animDuration)
175
- .on('end interrupt', () => {
176
- this._graphGroup.attr('pointer-events', null);
177
- });
178
201
  // We need to set up events and attributes again because the rendering might have been delayed by the layout
179
202
  // calculation and they were not set up properly (see the render function of `ComponentCore`)
180
203
  this._setUpComponentEventsThrottled();
181
204
  this._setCustomAttributesThrottled();
182
205
  // On render complete callback
183
- (_b = (_a = this.config).onRenderComplete) === null || _b === void 0 ? void 0 : _b.call(_a, this.g, datamodel.nodes, datamodel.links, this.config, animDuration, this._scale, this._containerWidth, this._containerHeight);
206
+ (_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);
207
+ this._isFirstRender = false;
184
208
  });
185
- this._isFirstRender = false;
186
209
  }
187
210
  _drawNodes(duration) {
188
211
  const { config, datamodel } = this;
@@ -204,9 +227,9 @@ class Graph extends ComponentCore {
204
227
  const thisRef = this;
205
228
  if (!config.disableDrag) {
206
229
  const dragBehaviour = drag()
207
- .on('start', function (event, d) { thisRef._onDragStarted(d, event, select(this)); })
208
- .on('drag', function (event, d) { thisRef._onDragged(d, event, nodeGroupsMerged); })
209
- .on('end', function (event, d) { thisRef._onDragEnded(d, event, select(this)); });
230
+ .on('start drag end', function (event, d) {
231
+ thisRef._handleDrag(d, event, select(this));
232
+ });
210
233
  nodeGroupsMerged.call(dragBehaviour);
211
234
  }
212
235
  else {
@@ -261,7 +284,6 @@ class Graph extends ComponentCore {
261
284
  _calculateLayout() {
262
285
  return __awaiter(this, void 0, void 0, function* () {
263
286
  const { config, datamodel } = this;
264
- const firstRender = this._isFirstRender;
265
287
  // If the layout type has changed, we need to reset the node positions if they were fixed before
266
288
  if (this._currentLayoutType !== config.layoutType) {
267
289
  for (const node of datamodel.nodes) {
@@ -300,7 +322,6 @@ class Graph extends ComponentCore {
300
322
  this._initPanelsData();
301
323
  this._shouldRecalculateLayout = false;
302
324
  this._currentLayoutType = config.layoutType;
303
- return firstRender;
304
325
  });
305
326
  }
306
327
  _initPanelsData() {
@@ -311,9 +332,10 @@ class Graph extends ComponentCore {
311
332
  this._shouldSetPanels = false;
312
333
  }
313
334
  }
314
- _fit(duration = 0) {
335
+ _fit(duration = 0, nodeIds) {
315
336
  const { datamodel: { nodes } } = this;
316
- const transform = this._getTransform(nodes);
337
+ const fitViewNodes = (nodeIds === null || nodeIds === void 0 ? void 0 : nodeIds.length) ? nodes.filter(n => nodeIds.includes(n.id)) : nodes;
338
+ const transform = this._getTransform(fitViewNodes);
317
339
  smartTransition(this.g, duration)
318
340
  .call(this._zoomBehavior.transform, transform);
319
341
  this._onZoom(transform);
@@ -348,38 +370,45 @@ class Graph extends ComponentCore {
348
370
  .scale(clampedScale);
349
371
  return transform;
350
372
  }
351
- _selectNode(node) {
352
- const { datamodel: { nodes, links } } = this;
353
- if (!node)
354
- console.warn('Unovis | Graph: Select Node: Not found');
355
- this._selectedNode = node;
356
- // Apply grey out
357
- // Grey out all nodes
358
- nodes.forEach(n => {
373
+ _setNodeSelectionState(nodesToSelect) {
374
+ const { config, datamodel } = this;
375
+ // Grey out all nodes and set us unselected
376
+ for (const n of datamodel.nodes) {
359
377
  n._state.selected = false;
360
- n._state.greyout = true;
361
- });
362
- // Grey out all links
363
- links.forEach(l => {
364
- l._state.greyout = true;
378
+ if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
379
+ n._state.greyout = true;
380
+ }
381
+ }
382
+ // Grey out all links and set us unselected
383
+ for (const l of datamodel.links) {
365
384
  l._state.selected = false;
385
+ if (config.nodeSelectionHighlightMode !== GraphNodeSelectionHighlightMode.None) {
386
+ l._state.greyout = true;
387
+ }
388
+ }
389
+ // Filter out non-existing nodes
390
+ this._selectedNodes = nodesToSelect.filter(n => {
391
+ const doesNodeExist = Boolean(n);
392
+ if (!doesNodeExist)
393
+ console.warn('Unovis | Graph: Select Node: Not found');
394
+ return doesNodeExist;
366
395
  });
367
- // Highlight selected
368
- if (node) {
369
- node._state.selected = true;
370
- node._state.greyout = false;
371
- const connectedLinks = links.filter(l => (l.source === node) || (l.target === node));
396
+ // Set provided nodes as selected and ungreyout
397
+ for (const n of this._selectedNodes) {
398
+ n._state.selected = true;
399
+ n._state.greyout = false;
400
+ }
401
+ // Highlight connected links and nodes
402
+ if (config.nodeSelectionHighlightMode === GraphNodeSelectionHighlightMode.GreyoutNonConnected) {
403
+ const connectedLinks = datamodel.links.filter(l => this._selectedNodes.includes(l.source) || this._selectedNodes.includes(l.target));
372
404
  connectedLinks.forEach(l => {
373
- const source = l.source;
374
- const target = l.target;
375
- source._state.greyout = false;
376
- target._state.greyout = false;
405
+ l.source._state.greyout = false;
406
+ l.target._state.greyout = false;
377
407
  l._state.greyout = false;
378
408
  });
379
409
  }
380
- this._updateSelectedElements();
381
410
  }
382
- _selectLink(link) {
411
+ _setLinkSelectionState(link) {
383
412
  const { datamodel: { nodes, links } } = this;
384
413
  if (!link)
385
414
  console.warn('Unovis: Graph: Select Link: Not found');
@@ -409,13 +438,12 @@ class Graph extends ComponentCore {
409
438
  });
410
439
  if (link)
411
440
  link._state.selected = true;
412
- this._updateSelectedElements();
413
441
  }
414
- _resetSelection() {
442
+ _resetSelectionGreyoutState() {
415
443
  const { datamodel: { nodes, links } } = this;
416
- this._selectedNode = undefined;
444
+ this._selectedNodes = [];
417
445
  this._selectedLink = undefined;
418
- // Disable Grayout
446
+ // Disable Greyout
419
447
  nodes.forEach(n => {
420
448
  delete n._state.selected;
421
449
  delete n._state.greyout;
@@ -424,27 +452,28 @@ class Graph extends ComponentCore {
424
452
  delete l._state.greyout;
425
453
  delete l._state.selected;
426
454
  });
427
- this._updateSelectedElements();
428
455
  }
429
- _updateSelectedElements() {
456
+ _updateNodesLinksPartial() {
430
457
  const { config } = this;
431
458
  const linkElements = this._linksGroup.selectAll(`.${gLink}`);
432
- linkElements.call(updateSelectedLinks, config, this._scale);
459
+ linkElements.call(updateLinksPartial, config, this._scale);
433
460
  const nodeElements = this._nodesGroup.selectAll(`.${gNode}`);
434
- nodeElements.call(updateNodeSelectedGreyout, config);
435
- // this._drawPanels(nodeElements, 0)
461
+ nodeElements.call(updateNodesPartial, config, config.duration, this._scale);
436
462
  }
437
463
  _onBackgroundClick() {
438
- this._resetSelection();
464
+ this._resetSelectionGreyoutState();
465
+ this._updateNodesLinksPartial();
439
466
  }
440
467
  // eslint-disable-next-line @typescript-eslint/no-empty-function
441
468
  _onNodeClick(d) {
442
469
  }
443
470
  // eslint-disable-next-line @typescript-eslint/no-empty-function
444
471
  _onNodeMouseOut(d) {
472
+ this._updateNodesLinksPartial();
445
473
  }
446
474
  // eslint-disable-next-line @typescript-eslint/no-empty-function
447
475
  _onNodeMouseOver(d) {
476
+ this._updateNodesLinksPartial();
448
477
  }
449
478
  // eslint-disable-next-line @typescript-eslint/no-empty-function
450
479
  _onLinkClick(d) {
@@ -452,14 +481,15 @@ class Graph extends ComponentCore {
452
481
  _onLinkMouseOver(d) {
453
482
  if (this._isDragging)
454
483
  return;
455
- d._state.hovered = true;
456
- this._updateSelectedElements();
484
+ if (this.config.linkHighlightOnHover)
485
+ d._state.hovered = true;
486
+ this._updateNodesLinksPartial();
457
487
  }
458
488
  _onLinkMouseOut(d) {
459
489
  if (this._isDragging)
460
490
  return;
461
491
  delete d._state.hovered;
462
- this._updateSelectedElements();
492
+ this._updateNodesLinksPartial();
463
493
  }
464
494
  _onLinkFlowTimerFrame(elapsed = 0) {
465
495
  const { config: { linkFlow, linkFlowAnimDuration }, datamodel: { links } } = this;
@@ -478,7 +508,7 @@ class Graph extends ComponentCore {
478
508
  this._scale = transform.k;
479
509
  this._graphGroup.attr('transform', transform.toString());
480
510
  if (isFunction(config.onZoom))
481
- config.onZoom(this._scale, config.zoomScaleExtent, event);
511
+ config.onZoom(this._scale, config.zoomScaleExtent, event, transform);
482
512
  // console.warn('Unovis | Graph: Zoom: ', transform)
483
513
  if (!this._initialTransform)
484
514
  this._initialTransform = transform;
@@ -503,27 +533,31 @@ class Graph extends ComponentCore {
503
533
  this._linksGroup.selectAll(`.${gLink}`)
504
534
  .call((nodes.length > config.zoomThrottledUpdateNodeThreshold ? zoomLinksThrottled : zoomLinks), config, this._scale, this._getLinkArrowDefId);
505
535
  }
506
- _onDragStarted(d, event, nodeSelection) {
507
- var _a;
536
+ _onZoomStart(t, event) {
508
537
  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);
538
+ const transform = t || event.transform;
539
+ this._scale = transform.k;
540
+ if (isFunction(config.onZoomStart))
541
+ config.onZoomStart(this._scale, config.zoomScaleExtent, event, transform);
513
542
  }
514
- _onDragged(d, event, allNodesSelection) {
515
- var _a, _b, _c;
543
+ _onZoomEnd(t, event) {
516
544
  const { config } = this;
545
+ const transform = t || event.transform;
546
+ this._scale = transform.k;
547
+ if (isFunction(config.onZoomEnd))
548
+ config.onZoomEnd(this._scale, config.zoomScaleExtent, event, transform);
549
+ }
550
+ _updateNodePosition(d, x, y) {
551
+ var _a, _b;
517
552
  const transform = zoomTransform(this.g.node());
518
553
  const scale = transform.k;
519
554
  // Prevent the node from being dragged offscreen or outside its panel
520
555
  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);
556
+ const nodeSizeValue = getNodeSize(d, this.config.nodeSize, d._index);
522
557
  const maxY = min([(this._height - transform.y) / scale, ...panels.map(p => p._y + p._height)]) - nodeSizeValue / 2;
523
558
  const maxX = min([(this._width - transform.x) / scale, ...panels.map(p => p._x + p._width)]) - nodeSizeValue / 2;
524
559
  const minY = max([-transform.y / scale, ...panels.map(p => p._y)]) + nodeSizeValue / 2;
525
560
  const minX = max([-transform.x / scale, ...panels.map(p => p._x)]) + nodeSizeValue / 2;
526
- let [x, y] = pointer(event, this._graphGroup.node());
527
561
  if (y < minY)
528
562
  y = minY;
529
563
  else if (y > maxY)
@@ -544,6 +578,62 @@ class Graph extends ComponentCore {
544
578
  delete d._state.fx;
545
579
  if (d._state.fy === d.y)
546
580
  delete d._state.fy;
581
+ }
582
+ _onBrush(event) {
583
+ var _a;
584
+ if (!event.selection || !event.sourceEvent)
585
+ return;
586
+ const { config } = this;
587
+ const transform = zoomTransform(this._graphGroup.node());
588
+ const [xMin, yMin] = transform.invert(event.selection[0]);
589
+ const [xMax, yMax] = transform.invert(event.selection[1]);
590
+ // Update brushed nodes
591
+ this._nodesGroup.selectAll(`.${gNode}`)
592
+ .each(n => {
593
+ const x = getX(n);
594
+ const y = getY(n);
595
+ n._state.brushed = x >= xMin && x <= xMax && y >= yMin && y <= yMax;
596
+ })
597
+ .classed(brushed, n => n._state.brushed);
598
+ const brushedNodes = this._nodesGroup.selectAll(`.${brushed}`)
599
+ .call(updateNodesPartial, config, 0, this._scale);
600
+ this._brush.classed('active', event.type !== 'end');
601
+ (_a = config.onNodeSelectionBrush) === null || _a === void 0 ? void 0 : _a.call(config, brushedNodes.data(), event);
602
+ }
603
+ _handleDrag(d, event, nodeSelection) {
604
+ if (event.sourceEvent.shiftKey && d._state.brushed) {
605
+ this._dragSelectedNodes(event);
606
+ }
607
+ else if (!event.sourceEvent.shiftKey) {
608
+ switch (event.type) {
609
+ case 'start':
610
+ this._onDragStarted(d, event, nodeSelection);
611
+ break;
612
+ case 'drag':
613
+ this._onDragged(d, event);
614
+ break;
615
+ case 'end':
616
+ this._onDragEnded(d, event, nodeSelection);
617
+ break;
618
+ }
619
+ }
620
+ }
621
+ _onDragStarted(d, event, nodeSelection) {
622
+ var _a;
623
+ const { config } = this;
624
+ this._isDragging = true;
625
+ d._state.isDragged = true;
626
+ nodeSelection.call(updateNodes, config, 0, this._scale);
627
+ (_a = config.onNodeDragStart) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
628
+ }
629
+ _onDragged(d, event) {
630
+ var _a;
631
+ const { config } = this;
632
+ const transform = zoomTransform(this.g.node());
633
+ const scale = transform.k;
634
+ // Update node position
635
+ const [x, y] = pointer(event, this._graphGroup.node());
636
+ this._updateNodePosition(d, x, y);
547
637
  // Update affected DOM elements
548
638
  const nodeSelection = this._nodesGroup.selectAll(`.${gNode}`);
549
639
  const nodeToUpdate = nodeSelection.filter((n) => n._id === d._id);
@@ -558,7 +648,7 @@ class Graph extends ComponentCore {
558
648
  const linksToAnimate = linksToUpdate.filter(d => d._state.greyout);
559
649
  if (linksToAnimate.size())
560
650
  animateLinkFlow(linksToAnimate, config, this._scale);
561
- (_c = config.onNodeDrag) === null || _c === void 0 ? void 0 : _c.call(config, d, event);
651
+ (_a = config.onNodeDrag) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
562
652
  }
563
653
  _onDragEnded(d, event, nodeSelection) {
564
654
  var _a;
@@ -568,6 +658,49 @@ class Graph extends ComponentCore {
568
658
  nodeSelection.call(updateNodes, config, 0, this._scale);
569
659
  (_a = config.onNodeDragEnd) === null || _a === void 0 ? void 0 : _a.call(config, d, event);
570
660
  }
661
+ _dragSelectedNodes(event) {
662
+ var _a, _b;
663
+ const { config } = this;
664
+ const curr = pointer(event, this._graphGroup.node());
665
+ const selectedNodes = smartTransition(this._nodesGroup.selectAll(`.${brushed}`));
666
+ if (event.type === 'start') {
667
+ this._groupDragInit = curr;
668
+ this._isDragging = true;
669
+ selectedNodes.each(n => {
670
+ n.x = getX(n);
671
+ n.y = getY(n);
672
+ n._state.isDragged = true;
673
+ });
674
+ }
675
+ else if (event.type === 'drag') {
676
+ const dx = curr[0] - this._groupDragInit[0];
677
+ const dy = curr[1] - this._groupDragInit[1];
678
+ selectedNodes.each(n => this._updateNodePosition(n, n.x + dx, n.y + dy));
679
+ const connectedLinks = smartTransition(this._linksGroup.selectAll(`.${gLink}`)
680
+ .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); }));
681
+ connectedLinks.call(updateLinks, this.config, 0, this._scale, this._getLinkArrowDefId);
682
+ }
683
+ else {
684
+ this._isDragging = false;
685
+ selectedNodes.each(n => { n._state.isDragged = false; });
686
+ }
687
+ selectedNodes.call(updateNodes, config, 0, this._scale);
688
+ (_b = (_a = this.config).onNodeSelectionDrag) === null || _b === void 0 ? void 0 : _b.call(_a, selectedNodes.data(), event);
689
+ }
690
+ _activateBrush() {
691
+ this._brush.classed('active', true);
692
+ this._nodesGroup.selectAll(`.${gNode}`)
693
+ .classed(brushable, true);
694
+ }
695
+ _clearBrush() {
696
+ var _a;
697
+ this._brush.classed('active', false).call((_a = this._brushBehavior) === null || _a === void 0 ? void 0 : _a.clear);
698
+ this._nodesGroup.selectAll(`.${gNode}`)
699
+ .classed(brushable, false)
700
+ .classed(brushed, false)
701
+ .each(n => { n._state.brushed = false; })
702
+ .call(updateNodesPartial, this.config, 0, this._scale);
703
+ }
571
704
  _shouldLayoutRecalculate() {
572
705
  const { prevConfig, config } = this;
573
706
  if (prevConfig.layoutType !== config.layoutType)
@@ -637,9 +770,10 @@ class Graph extends ComponentCore {
637
770
  getZoom() {
638
771
  return zoomTransform(this.g.node()).k;
639
772
  }
640
- fitView(duration = this.config.duration) {
641
- this._layoutCalculationPromise.then(() => {
642
- this._fit(duration);
773
+ fitView(duration = this.config.duration, nodeIds) {
774
+ var _a;
775
+ (_a = this._layoutCalculationPromise) === null || _a === void 0 ? void 0 : _a.then(() => {
776
+ this._fit(duration, nodeIds);
643
777
  });
644
778
  }
645
779
  /** Enable automatic fitting to container if it was disabled due to previous zoom / pan interactions */
@@ -684,6 +818,7 @@ Graph.selectors = {
684
818
  dimmedNode: greyedOutNode,
685
819
  link: gLink,
686
820
  linkLine: link,
821
+ linkLabel: linkLabelGroup,
687
822
  dimmedLink: greyedOutLink,
688
823
  panel: gPanel,
689
824
  panelRect: panel,