@unovis/ts 1.6.6 → 1.6.7-stellar-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/area/index.js +7 -3
- package/components/area/index.js.map +1 -1
- package/components/area/style.js +1 -1
- package/components/area/style.js.map +1 -1
- package/components/bullet-legend/style.js +1 -1
- package/components/bullet-legend/style.js.map +1 -1
- package/components/chord-diagram/style.js +1 -1
- package/components/chord-diagram/style.js.map +1 -1
- package/components/donut/style.js +1 -1
- package/components/donut/style.js.map +1 -1
- package/components/flow-legend/style.js +1 -1
- package/components/flow-legend/style.js.map +1 -1
- package/components/free-brush/style.js +1 -1
- package/components/free-brush/style.js.map +1 -1
- package/components/graph/config.d.ts +6 -0
- package/components/graph/config.js +1 -1
- package/components/graph/config.js.map +1 -1
- package/components/graph/index.d.ts +18 -0
- package/components/graph/index.js +251 -7
- package/components/graph/index.js.map +1 -1
- package/components/graph/modules/link/style.js +1 -1
- package/components/graph/modules/link/style.js.map +1 -1
- package/components/graph/modules/node/style.js +1 -1
- package/components/graph/modules/node/style.js.map +1 -1
- package/components/graph/modules/panel/style.js +1 -1
- package/components/graph/modules/panel/style.js.map +1 -1
- package/components/grouped-bar/style.js +1 -1
- package/components/grouped-bar/style.js.map +1 -1
- package/components/plotband/style.js +1 -1
- package/components/plotband/style.js.map +1 -1
- package/components/plotline/style.js +1 -1
- package/components/plotline/style.js.map +1 -1
- package/components/sankey/modules/label.js +8 -2
- package/components/sankey/modules/label.js.map +1 -1
- package/components/stacked-bar/style.js +1 -1
- package/components/stacked-bar/style.js.map +1 -1
- package/components/tooltip/style.js +1 -1
- package/components/tooltip/style.js.map +1 -1
- package/components/vis-controls/style.js +1 -1
- package/components/vis-controls/style.js.map +1 -1
- package/index.js +2 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/index.js +1 -1
- package/styles/index.js.map +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +2 -1
- package/utils/index.js.map +1 -1
- package/utils/style.d.ts +0 -1
- package/utils/style.js +2 -2
- package/utils/style.js.map +1 -1
- package/utils/theme.d.ts +1 -0
- package/utils/theme.js +4 -0
- package/utils/theme.js.map +1 -0
|
@@ -2,12 +2,12 @@ import { __awaiter } from 'tslib';
|
|
|
2
2
|
import { min, max, extent } from 'd3-array';
|
|
3
3
|
import { select, pointer } from 'd3-selection';
|
|
4
4
|
import { brush as brush$1 } from 'd3-brush';
|
|
5
|
-
import { zoom,
|
|
5
|
+
import { zoom, zoomTransform, zoomIdentity } from 'd3-zoom';
|
|
6
6
|
import { drag } from 'd3-drag';
|
|
7
7
|
import { interval } from 'd3-timer';
|
|
8
8
|
import { ComponentCore } from '../../core/component/index.js';
|
|
9
9
|
import { GraphDataModel } from '../../data-models/graph.js';
|
|
10
|
-
import {
|
|
10
|
+
import { isFunction, getBoolean, isNumber, clamp, getNumber, shallowDiff, isPlainObject, isEqual } from '../../utils/data.js';
|
|
11
11
|
import { smartTransition } from '../../utils/d3.js';
|
|
12
12
|
import { GraphLayoutType, GraphFitViewAlignment, GraphNodeSelectionHighlightMode, GraphLinkArrowStyle } from './types.js';
|
|
13
13
|
import { GraphDefaultConfig } from './config.js';
|
|
@@ -35,6 +35,11 @@ class Graph extends ComponentCore {
|
|
|
35
35
|
this._shouldSetPanels = false;
|
|
36
36
|
this._isAutoFitDisabled = false;
|
|
37
37
|
this._isDragging = false;
|
|
38
|
+
this._collapsedNodeIds = new Set();
|
|
39
|
+
this._dataVersion = 0;
|
|
40
|
+
this._lastExpandCollapseTime = 0;
|
|
41
|
+
this._isExpandCollapseInProgress = false;
|
|
42
|
+
this._expandCollapseRenderVersion = -1;
|
|
38
43
|
// A map for storing link total path lengths to optimize rendering performance
|
|
39
44
|
this._linkPathLengthMap = new Map();
|
|
40
45
|
this._linkFlowFrameElapsed = 0;
|
|
@@ -84,11 +89,16 @@ class Graph extends ComponentCore {
|
|
|
84
89
|
return this._selectedLink;
|
|
85
90
|
}
|
|
86
91
|
setData(data) {
|
|
92
|
+
var _a;
|
|
87
93
|
const { config } = this;
|
|
88
|
-
|
|
94
|
+
const prevData = (_a = this._fullData) !== null && _a !== void 0 ? _a : this.datamodel.data;
|
|
95
|
+
if (!config.shouldDataUpdate(prevData, data, this.datamodel))
|
|
89
96
|
return;
|
|
97
|
+
this._fullData = data;
|
|
98
|
+
const visibleData = config.nodeExpandable ? this._computeVisibleData(data) : data;
|
|
90
99
|
this.datamodel.nodeSort = config.nodeSort;
|
|
91
|
-
this.datamodel.data =
|
|
100
|
+
this.datamodel.data = visibleData;
|
|
101
|
+
this._dataVersion++;
|
|
92
102
|
this._shouldRecalculateLayout = true;
|
|
93
103
|
if (config.layoutAutofit)
|
|
94
104
|
this._shouldFitLayout = true;
|
|
@@ -107,6 +117,138 @@ class Graph extends ComponentCore {
|
|
|
107
117
|
this._isAutoFitDisabled = false;
|
|
108
118
|
this._shouldSetPanels = true;
|
|
109
119
|
}
|
|
120
|
+
_computeVisibleData(data) {
|
|
121
|
+
var _a;
|
|
122
|
+
const { config } = this;
|
|
123
|
+
if (!config.nodeExpandable || !config.nodeChildren)
|
|
124
|
+
return data;
|
|
125
|
+
const allNodes = data.nodes;
|
|
126
|
+
const allLinks = (_a = data.links) !== null && _a !== void 0 ? _a : [];
|
|
127
|
+
// Build an id -> node map.
|
|
128
|
+
const nodeById = new Map();
|
|
129
|
+
for (const n of allNodes) {
|
|
130
|
+
if (n.id !== undefined)
|
|
131
|
+
nodeById.set(n.id, n);
|
|
132
|
+
}
|
|
133
|
+
const { visibleIdSet, visibleChildLinkSet, childIdsByParentId } = this._getVisibleNodeIds(allNodes, nodeById);
|
|
134
|
+
const visibleNodes = allNodes.filter(n => n.id !== undefined && visibleIdSet.has(n.id));
|
|
135
|
+
// Resolve a link endpoint to a node id.
|
|
136
|
+
const resolveId = (endpoint) => {
|
|
137
|
+
var _a;
|
|
138
|
+
if (typeof endpoint === 'object')
|
|
139
|
+
return endpoint.id;
|
|
140
|
+
if (typeof endpoint === 'number')
|
|
141
|
+
return (_a = allNodes[endpoint]) === null || _a === void 0 ? void 0 : _a.id;
|
|
142
|
+
return endpoint;
|
|
143
|
+
};
|
|
144
|
+
const visibleLinks = allLinks.filter(l => {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
const srcId = resolveId(l.source);
|
|
147
|
+
const tgtId = resolveId(l.target);
|
|
148
|
+
if (srcId === undefined || tgtId === undefined || !visibleIdSet.has(srcId) || !visibleIdSet.has(tgtId))
|
|
149
|
+
return false;
|
|
150
|
+
const isParentChildLink = ((_a = childIdsByParentId.get(srcId)) === null || _a === void 0 ? void 0 : _a.includes(tgtId)) || ((_b = childIdsByParentId.get(tgtId)) === null || _b === void 0 ? void 0 : _b.includes(srcId));
|
|
151
|
+
if (!isParentChildLink)
|
|
152
|
+
return true;
|
|
153
|
+
return visibleChildLinkSet.has(this._getExpandCollapseLinkKey(srcId, tgtId)) ||
|
|
154
|
+
visibleChildLinkSet.has(this._getExpandCollapseLinkKey(tgtId, srcId));
|
|
155
|
+
});
|
|
156
|
+
return { nodes: visibleNodes, links: visibleLinks };
|
|
157
|
+
}
|
|
158
|
+
_getVisibleNodeIds(allNodes, nodeById) {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
const { config } = this;
|
|
161
|
+
const visible = new Set();
|
|
162
|
+
const visibleChildLinkSet = new Set();
|
|
163
|
+
const hasParent = new Set();
|
|
164
|
+
const childIdsByParentId = new Map();
|
|
165
|
+
for (const node of allNodes) {
|
|
166
|
+
if (node.id === undefined)
|
|
167
|
+
continue;
|
|
168
|
+
const children = (_a = (isFunction(config.nodeChildren) ? config.nodeChildren(node, 0) : config.nodeChildren)) !== null && _a !== void 0 ? _a : [];
|
|
169
|
+
childIdsByParentId.set(node.id, children);
|
|
170
|
+
for (const childId of children)
|
|
171
|
+
hasParent.add(childId);
|
|
172
|
+
}
|
|
173
|
+
const queue = [];
|
|
174
|
+
for (const node of allNodes) {
|
|
175
|
+
if (node.id === undefined)
|
|
176
|
+
continue;
|
|
177
|
+
if (!hasParent.has(node.id)) {
|
|
178
|
+
visible.add(node.id);
|
|
179
|
+
queue.push(node.id);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Fallback for cyclic-only graphs.
|
|
183
|
+
if (!queue.length) {
|
|
184
|
+
for (const node of allNodes) {
|
|
185
|
+
if (node.id === undefined)
|
|
186
|
+
continue;
|
|
187
|
+
visible.add(node.id);
|
|
188
|
+
queue.push(node.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
while (queue.length) {
|
|
192
|
+
const parentId = queue.shift();
|
|
193
|
+
const parentNode = nodeById.get(parentId);
|
|
194
|
+
if (!parentNode)
|
|
195
|
+
continue;
|
|
196
|
+
const isParentExpandable = getBoolean(parentNode, config.nodeExpandable, 0);
|
|
197
|
+
const isParentCollapsed = this._collapsedNodeIds.has(String(parentId));
|
|
198
|
+
if (isParentExpandable && isParentCollapsed)
|
|
199
|
+
continue;
|
|
200
|
+
const children = (_b = childIdsByParentId.get(parentId)) !== null && _b !== void 0 ? _b : [];
|
|
201
|
+
for (const childId of children) {
|
|
202
|
+
if (!nodeById.has(childId))
|
|
203
|
+
continue;
|
|
204
|
+
visibleChildLinkSet.add(this._getExpandCollapseLinkKey(parentId, childId));
|
|
205
|
+
if (!visible.has(childId)) {
|
|
206
|
+
visible.add(childId);
|
|
207
|
+
queue.push(childId);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
visibleIdSet: visible,
|
|
213
|
+
visibleChildLinkSet,
|
|
214
|
+
childIdsByParentId,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
_getExpandCollapseLinkKey(sourceId, targetId) {
|
|
218
|
+
return `${typeof sourceId}:${String(sourceId)}->${typeof targetId}:${String(targetId)}`;
|
|
219
|
+
}
|
|
220
|
+
_applyExpandCollapse() {
|
|
221
|
+
const { config } = this;
|
|
222
|
+
if (!config.nodeExpandable || !this._fullData)
|
|
223
|
+
return;
|
|
224
|
+
const visibleData = this._computeVisibleData(this._fullData);
|
|
225
|
+
this.datamodel.nodeSort = config.nodeSort;
|
|
226
|
+
this.datamodel.data = visibleData;
|
|
227
|
+
this._dataVersion++;
|
|
228
|
+
this._lastExpandCollapseTime = Date.now();
|
|
229
|
+
// Clear dragged positions before layout.
|
|
230
|
+
for (const node of this.datamodel.nodes) {
|
|
231
|
+
delete node._state.fx;
|
|
232
|
+
delete node._state.fy;
|
|
233
|
+
}
|
|
234
|
+
this._shouldRecalculateLayout = true;
|
|
235
|
+
// Use one controlled fit for this cycle.
|
|
236
|
+
if (config.layoutAutofit)
|
|
237
|
+
this._shouldFitLayout = false;
|
|
238
|
+
this._isAutoFitDisabled = false;
|
|
239
|
+
this._shouldSetPanels = true;
|
|
240
|
+
// Do not lock interaction before the first size pass.
|
|
241
|
+
if (this._width <= 0) {
|
|
242
|
+
this._isExpandCollapseInProgress = false;
|
|
243
|
+
this._expandCollapseRenderVersion = -1;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this._isExpandCollapseInProgress = true;
|
|
247
|
+
this._expandCollapseRenderVersion = this._dataVersion;
|
|
248
|
+
this._render();
|
|
249
|
+
// Keep viewport and node transitions aligned.
|
|
250
|
+
this.fitView(this.config.duration);
|
|
251
|
+
}
|
|
110
252
|
get bleed() {
|
|
111
253
|
const padding = this.config.fitViewPadding; // Extra padding to take into account labels and selection outlines
|
|
112
254
|
return isNumber(padding)
|
|
@@ -163,11 +305,11 @@ class Graph extends ComponentCore {
|
|
|
163
305
|
this._zoomBehavior.filter(isFunction(zoomEventFilter)
|
|
164
306
|
? zoomEventFilter
|
|
165
307
|
: (e) => (!e.ctrlKey || e.type === 'wheel') && !e.button && !e.shiftKey); // Default filter
|
|
308
|
+
const renderDataVersion = this._dataVersion;
|
|
166
309
|
this._layoutCalculationPromise.then(() => {
|
|
167
310
|
var _a, _b, _c;
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
if (this.isDestroyed())
|
|
311
|
+
// Cancel if destroyed or data changed.
|
|
312
|
+
if (this.isDestroyed() || this._dataVersion !== renderDataVersion)
|
|
171
313
|
return;
|
|
172
314
|
this._initPanelsData();
|
|
173
315
|
// Fit the view
|
|
@@ -209,9 +351,50 @@ class Graph extends ComponentCore {
|
|
|
209
351
|
this._setCustomAttributesThrottled();
|
|
210
352
|
// On render complete callback
|
|
211
353
|
(_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);
|
|
354
|
+
if (renderDataVersion === this._expandCollapseRenderVersion) {
|
|
355
|
+
if (this._expandCollapseVisibilityCheckTimer) {
|
|
356
|
+
window.clearTimeout(this._expandCollapseVisibilityCheckTimer);
|
|
357
|
+
this._expandCollapseVisibilityCheckTimer = undefined;
|
|
358
|
+
}
|
|
359
|
+
const delay = Math.max(animDuration, 0) + 50;
|
|
360
|
+
this._expandCollapseVisibilityCheckTimer = window.setTimeout(() => {
|
|
361
|
+
this._expandCollapseVisibilityCheckTimer = undefined;
|
|
362
|
+
if (this.isDestroyed())
|
|
363
|
+
return;
|
|
364
|
+
this._ensureGraphVisibleInViewport();
|
|
365
|
+
this._isExpandCollapseInProgress = false;
|
|
366
|
+
}, delay);
|
|
367
|
+
}
|
|
212
368
|
this._isFirstRender = false;
|
|
213
369
|
});
|
|
214
370
|
}
|
|
371
|
+
_ensureGraphVisibleInViewport() {
|
|
372
|
+
var _a;
|
|
373
|
+
if (this.isDestroyed())
|
|
374
|
+
return;
|
|
375
|
+
const { datamodel, config: { nodeSize } } = this;
|
|
376
|
+
if (!((_a = datamodel.nodes) === null || _a === void 0 ? void 0 : _a.length))
|
|
377
|
+
return;
|
|
378
|
+
const maxNodeSize = getMaxNodeSize(datamodel.nodes, nodeSize);
|
|
379
|
+
const xExtent = [
|
|
380
|
+
min(datamodel.nodes, d => getX(d) - maxNodeSize / 2 - (max((d._panels || []).map(p => p._padding.left)) || 0)),
|
|
381
|
+
max(datamodel.nodes, d => getX(d) + maxNodeSize / 2 + (max((d._panels || []).map(p => p._padding.right)) || 0)),
|
|
382
|
+
];
|
|
383
|
+
const yExtent = [
|
|
384
|
+
min(datamodel.nodes, d => getY(d) - maxNodeSize / 2 - (max((d._panels || []).map(p => p._padding.top)) || 0)),
|
|
385
|
+
max(datamodel.nodes, d => getY(d) + maxNodeSize / 2 + (max((d._panels || []).map(p => p._padding.bottom)) || 0)),
|
|
386
|
+
];
|
|
387
|
+
if (xExtent.some(item => item === undefined) || yExtent.some(item => item === undefined))
|
|
388
|
+
return;
|
|
389
|
+
const transform = zoomTransform(this.g.node());
|
|
390
|
+
const left = transform.applyX(xExtent[0]);
|
|
391
|
+
const right = transform.applyX(xExtent[1]);
|
|
392
|
+
const top = transform.applyY(yExtent[0]);
|
|
393
|
+
const bottom = transform.applyY(yExtent[1]);
|
|
394
|
+
const isFullyOffscreen = right < 0 || left > this._width || bottom < 0 || top > this._height;
|
|
395
|
+
if (isFullyOffscreen)
|
|
396
|
+
this.fitView(0);
|
|
397
|
+
}
|
|
215
398
|
_drawNodes(duration) {
|
|
216
399
|
const { config, datamodel } = this;
|
|
217
400
|
const nodes = datamodel.nodes;
|
|
@@ -494,6 +677,17 @@ class Graph extends ComponentCore {
|
|
|
494
677
|
}
|
|
495
678
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
496
679
|
_onNodeClick(d) {
|
|
680
|
+
const { config } = this;
|
|
681
|
+
if (config.nodeExpandable && getBoolean(d, config.nodeExpandable, d._index)) {
|
|
682
|
+
// Ignore clicks during an active expand/collapse render.
|
|
683
|
+
if (this._isExpandCollapseInProgress)
|
|
684
|
+
return;
|
|
685
|
+
// Ignore the second click in a double-click.
|
|
686
|
+
const clickCooldown = 300;
|
|
687
|
+
if (Date.now() - this._lastExpandCollapseTime < clickCooldown)
|
|
688
|
+
return;
|
|
689
|
+
this.toggleNodeExpand(d._id);
|
|
690
|
+
}
|
|
497
691
|
}
|
|
498
692
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
499
693
|
_onNodeMouseOut(d) {
|
|
@@ -872,6 +1066,56 @@ class Graph extends ComponentCore {
|
|
|
872
1066
|
setNodeStateById(nodeId, state) {
|
|
873
1067
|
this.datamodel.setNodeStateById(nodeId, state);
|
|
874
1068
|
}
|
|
1069
|
+
toggleNodeExpand(nodeId) {
|
|
1070
|
+
var _a, _b;
|
|
1071
|
+
const { config } = this;
|
|
1072
|
+
const id = String(nodeId);
|
|
1073
|
+
const wasCollapsed = this._collapsedNodeIds.has(id);
|
|
1074
|
+
if (wasCollapsed) {
|
|
1075
|
+
this._collapsedNodeIds.delete(id);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
this._collapsedNodeIds.add(id);
|
|
1079
|
+
}
|
|
1080
|
+
const expanded = wasCollapsed; // after toggle
|
|
1081
|
+
// Use the full-data node for the callback.
|
|
1082
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
1083
|
+
if (node)
|
|
1084
|
+
(_b = config.onNodeExpand) === null || _b === void 0 ? void 0 : _b.call(config, node, expanded);
|
|
1085
|
+
this._applyExpandCollapse();
|
|
1086
|
+
}
|
|
1087
|
+
expandNode(nodeId) {
|
|
1088
|
+
var _a, _b, _c;
|
|
1089
|
+
const id = String(nodeId);
|
|
1090
|
+
if (!this._collapsedNodeIds.has(id))
|
|
1091
|
+
return;
|
|
1092
|
+
this._collapsedNodeIds.delete(id);
|
|
1093
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
1094
|
+
if (node)
|
|
1095
|
+
(_c = (_b = this.config).onNodeExpand) === null || _c === void 0 ? void 0 : _c.call(_b, node, true);
|
|
1096
|
+
this._applyExpandCollapse();
|
|
1097
|
+
}
|
|
1098
|
+
collapseNode(nodeId) {
|
|
1099
|
+
var _a, _b;
|
|
1100
|
+
const { config } = this;
|
|
1101
|
+
const id = String(nodeId);
|
|
1102
|
+
if (this._collapsedNodeIds.has(id))
|
|
1103
|
+
return;
|
|
1104
|
+
this._collapsedNodeIds.add(id);
|
|
1105
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
1106
|
+
if (node)
|
|
1107
|
+
(_b = config.onNodeExpand) === null || _b === void 0 ? void 0 : _b.call(config, node, false);
|
|
1108
|
+
this._applyExpandCollapse();
|
|
1109
|
+
}
|
|
1110
|
+
isNodeCollapsed(nodeId) {
|
|
1111
|
+
return this._collapsedNodeIds.has(String(nodeId));
|
|
1112
|
+
}
|
|
1113
|
+
setCollapsedNodes(nodeIds) {
|
|
1114
|
+
this._collapsedNodeIds = new Set(nodeIds.map(id => String(id)));
|
|
1115
|
+
this._isAutoFitDisabled = false;
|
|
1116
|
+
this._shouldFitLayout = true;
|
|
1117
|
+
this._applyExpandCollapse();
|
|
1118
|
+
}
|
|
875
1119
|
/** Call a partial render to update the positions of the nodes and their links.
|
|
876
1120
|
* This can be useful when you've changed the node positions manually outside
|
|
877
1121
|
* of the component and want to update the graph.
|