@unovis/ts 1.6.6-beta.0 → 1.6.6-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 +1 -3
- package/components/area/index.js.map +1 -1
- package/components/crosshair/index.js +4 -3
- package/components/crosshair/index.js.map +1 -1
- package/components/graph/config.d.ts +13 -0
- package/components/graph/config.js +1 -1
- package/components/graph/config.js.map +1 -1
- package/components/graph/index.d.ts +26 -0
- package/components/graph/index.js +166 -4
- package/components/graph/index.js.map +1 -1
- package/components/graph/modules/link/index.js +40 -5
- package/components/graph/modules/link/index.js.map +1 -1
- package/components/scatter/index.d.ts +4 -0
- package/components/scatter/index.js +12 -0
- package/components/scatter/index.js.map +1 -1
- package/components/stacked-bar/index.d.ts +4 -1
- package/components/stacked-bar/index.js +17 -6
- package/components/stacked-bar/index.js.map +1 -1
- package/components/timeline/config.d.ts +2 -0
- package/components/timeline/config.js +1 -1
- package/components/timeline/config.js.map +1 -1
- package/components/timeline/index.js +2 -4
- package/components/timeline/index.js.map +1 -1
- package/components/topojson-map/index.d.ts +2 -2
- package/components/topojson-map/index.js +24 -5
- package/components/topojson-map/index.js.map +1 -1
- package/core/component/index.d.ts +4 -1
- package/core/component/index.js +6 -6
- package/core/component/index.js.map +1 -1
- package/package.json +6 -5
- package/types/misc.js +2 -0
- package/types/misc.js.map +1 -0
- package/types.d.ts +1 -0
- package/types.js +1 -0
- package/types.js.map +1 -1
|
@@ -7,7 +7,7 @@ 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 { getBoolean, isFunction, 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,10 @@ class Graph extends ComponentCore {
|
|
|
35
35
|
this._shouldSetPanels = false;
|
|
36
36
|
this._isAutoFitDisabled = false;
|
|
37
37
|
this._isDragging = false;
|
|
38
|
+
/** Set of node ids (string) that are currently collapsed */
|
|
39
|
+
this._collapsedNodeIds = new Set();
|
|
40
|
+
/** Incremented every time datamodel.data is replaced; used to detect stale layout .then() callbacks */
|
|
41
|
+
this._dataVersion = 0;
|
|
38
42
|
// A map for storing link total path lengths to optimize rendering performance
|
|
39
43
|
this._linkPathLengthMap = new Map();
|
|
40
44
|
this._linkFlowFrameElapsed = 0;
|
|
@@ -87,8 +91,11 @@ class Graph extends ComponentCore {
|
|
|
87
91
|
const { config } = this;
|
|
88
92
|
if (!config.shouldDataUpdate(this.datamodel.data, data, this.datamodel))
|
|
89
93
|
return;
|
|
94
|
+
this._fullData = data;
|
|
95
|
+
const visibleData = config.nodeExpandable ? this._computeVisibleData(data) : data;
|
|
90
96
|
this.datamodel.nodeSort = config.nodeSort;
|
|
91
|
-
this.datamodel.data =
|
|
97
|
+
this.datamodel.data = visibleData;
|
|
98
|
+
this._dataVersion++;
|
|
92
99
|
this._shouldRecalculateLayout = true;
|
|
93
100
|
if (config.layoutAutofit)
|
|
94
101
|
this._shouldFitLayout = true;
|
|
@@ -107,6 +114,92 @@ class Graph extends ComponentCore {
|
|
|
107
114
|
this._isAutoFitDisabled = false;
|
|
108
115
|
this._shouldSetPanels = true;
|
|
109
116
|
}
|
|
117
|
+
/** Compute the visible subset of `data` based on `_collapsedNodeIds` */
|
|
118
|
+
_computeVisibleData(data) {
|
|
119
|
+
var _a;
|
|
120
|
+
const { config } = this;
|
|
121
|
+
if (!config.nodeExpandable || !config.nodeChildren)
|
|
122
|
+
return data;
|
|
123
|
+
const allNodes = data.nodes;
|
|
124
|
+
const allLinks = (_a = data.links) !== null && _a !== void 0 ? _a : [];
|
|
125
|
+
// Build a map of id → node for quick lookup (use the node's id property)
|
|
126
|
+
const nodeById = new Map();
|
|
127
|
+
for (const n of allNodes) {
|
|
128
|
+
if (n.id !== undefined)
|
|
129
|
+
nodeById.set(n.id, n);
|
|
130
|
+
}
|
|
131
|
+
// Collect ids to hide (all descendants of collapsed nodes)
|
|
132
|
+
const hiddenIds = this._getHiddenNodeIds(allNodes, nodeById);
|
|
133
|
+
const visibleIdSet = new Set(allNodes.filter(n => !hiddenIds.has(n.id)).map(n => n.id));
|
|
134
|
+
const visibleNodes = allNodes.filter(n => !hiddenIds.has(n.id));
|
|
135
|
+
// Helper: resolve a link endpoint (number index, string id, or node object) to the node's 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
|
+
const srcId = resolveId(l.source);
|
|
146
|
+
const tgtId = resolveId(l.target);
|
|
147
|
+
return visibleIdSet.has(srcId) && visibleIdSet.has(tgtId);
|
|
148
|
+
});
|
|
149
|
+
return { nodes: visibleNodes, links: visibleLinks };
|
|
150
|
+
}
|
|
151
|
+
_getHiddenNodeIds(allNodes, nodeById) {
|
|
152
|
+
var _a;
|
|
153
|
+
const { config } = this;
|
|
154
|
+
const hidden = new Set();
|
|
155
|
+
const hide = (id) => {
|
|
156
|
+
var _a;
|
|
157
|
+
if (id === undefined || hidden.has(id))
|
|
158
|
+
return;
|
|
159
|
+
hidden.add(id);
|
|
160
|
+
const node = nodeById.get(id);
|
|
161
|
+
if (node) {
|
|
162
|
+
const children = getBoolean(node, config.nodeExpandable, 0)
|
|
163
|
+
? (isFunction(config.nodeChildren) ? config.nodeChildren(node, 0) : (_a = config.nodeChildren) !== null && _a !== void 0 ? _a : [])
|
|
164
|
+
: [];
|
|
165
|
+
for (const childId of children)
|
|
166
|
+
hide(childId);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
for (const id of this._collapsedNodeIds) {
|
|
170
|
+
const node = nodeById.get(id);
|
|
171
|
+
if (node && getBoolean(node, config.nodeExpandable, 0) && config.nodeChildren) {
|
|
172
|
+
const nodeChildIds = isFunction(config.nodeChildren) ? config.nodeChildren(node, 0) : (_a = config.nodeChildren) !== null && _a !== void 0 ? _a : [];
|
|
173
|
+
for (const childId of nodeChildIds !== null && nodeChildIds !== void 0 ? nodeChildIds : [])
|
|
174
|
+
hide(childId);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return hidden;
|
|
178
|
+
}
|
|
179
|
+
_applyExpandCollapse() {
|
|
180
|
+
const { config } = this;
|
|
181
|
+
if (!config.nodeExpandable || !this._fullData)
|
|
182
|
+
return;
|
|
183
|
+
const visibleData = this._computeVisibleData(this._fullData);
|
|
184
|
+
this.datamodel.nodeSort = config.nodeSort;
|
|
185
|
+
this.datamodel.data = visibleData;
|
|
186
|
+
this._dataVersion++;
|
|
187
|
+
// Clear any fixed (dragged) positions so the fresh layout output is used
|
|
188
|
+
for (const node of this.datamodel.nodes) {
|
|
189
|
+
delete node._state.fx;
|
|
190
|
+
delete node._state.fy;
|
|
191
|
+
}
|
|
192
|
+
this._shouldRecalculateLayout = true;
|
|
193
|
+
if (config.layoutAutofit)
|
|
194
|
+
this._shouldFitLayout = true;
|
|
195
|
+
this._shouldSetPanels = true;
|
|
196
|
+
// Only call _render() if the component has been sized. When called before the first
|
|
197
|
+
// container render (e.g. from a useEffect on mount), _width is 0 and layout functions
|
|
198
|
+
// produce NaN coordinates (Infinity * 0). The flags above ensure the next
|
|
199
|
+
// container-triggered render will recalculate and draw with correct dimensions.
|
|
200
|
+
if (this._width > 0)
|
|
201
|
+
this._render();
|
|
202
|
+
}
|
|
110
203
|
get bleed() {
|
|
111
204
|
const padding = this.config.fitViewPadding; // Extra padding to take into account labels and selection outlines
|
|
112
205
|
return isNumber(padding)
|
|
@@ -163,11 +256,13 @@ class Graph extends ComponentCore {
|
|
|
163
256
|
this._zoomBehavior.filter(isFunction(zoomEventFilter)
|
|
164
257
|
? zoomEventFilter
|
|
165
258
|
: (e) => (!e.ctrlKey || e.type === 'wheel') && !e.button && !e.shiftKey); // Default filter
|
|
259
|
+
const renderDataVersion = this._dataVersion;
|
|
166
260
|
this._layoutCalculationPromise.then(() => {
|
|
167
261
|
var _a, _b, _c;
|
|
168
262
|
// If the component has been destroyed while the layout calculation
|
|
169
|
-
// was in progress,
|
|
170
|
-
|
|
263
|
+
// was in progress, or data has been replaced (e.g. React re-render
|
|
264
|
+
// fired between layout resolving and this callback), cancel the render
|
|
265
|
+
if (this.isDestroyed() || this._dataVersion !== renderDataVersion)
|
|
171
266
|
return;
|
|
172
267
|
this._initPanelsData();
|
|
173
268
|
// Fit the view
|
|
@@ -338,6 +433,8 @@ class Graph extends ComponentCore {
|
|
|
338
433
|
}
|
|
339
434
|
}
|
|
340
435
|
_fit(duration = 0, nodeIds, alignment = this.config.fitViewAlign) {
|
|
436
|
+
if (this.isDestroyed())
|
|
437
|
+
return;
|
|
341
438
|
const { datamodel, config: { nodeSize } } = this;
|
|
342
439
|
const nodes = (nodeIds === null || nodeIds === void 0 ? void 0 : nodeIds.length) ? datamodel.nodes.filter(n => nodeIds.includes(n.id)) : datamodel.nodes;
|
|
343
440
|
const maxNodeSize = getMaxNodeSize(nodes, nodeSize);
|
|
@@ -492,6 +589,10 @@ class Graph extends ComponentCore {
|
|
|
492
589
|
}
|
|
493
590
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
494
591
|
_onNodeClick(d) {
|
|
592
|
+
const { config } = this;
|
|
593
|
+
if (config.nodeExpandable && getBoolean(d, config.nodeExpandable, d._index)) {
|
|
594
|
+
this.toggleNodeExpand(d._id);
|
|
595
|
+
}
|
|
495
596
|
}
|
|
496
597
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
497
598
|
_onNodeMouseOut(d) {
|
|
@@ -870,6 +971,67 @@ class Graph extends ComponentCore {
|
|
|
870
971
|
setNodeStateById(nodeId, state) {
|
|
871
972
|
this.datamodel.setNodeStateById(nodeId, state);
|
|
872
973
|
}
|
|
974
|
+
/** Toggle the expanded/collapsed state of a node by its id.
|
|
975
|
+
* Only works when `nodeExpandable` and `nodeChildren` config options are set. */
|
|
976
|
+
toggleNodeExpand(nodeId) {
|
|
977
|
+
var _a, _b;
|
|
978
|
+
const { config } = this;
|
|
979
|
+
const id = String(nodeId);
|
|
980
|
+
const wasCollapsed = this._collapsedNodeIds.has(id);
|
|
981
|
+
if (wasCollapsed) {
|
|
982
|
+
this._collapsedNodeIds.delete(id);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
this._collapsedNodeIds.add(id);
|
|
986
|
+
}
|
|
987
|
+
const expanded = wasCollapsed; // after toggle
|
|
988
|
+
// Find the node in the full data to fire the callback
|
|
989
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
990
|
+
if (node)
|
|
991
|
+
(_b = config.onNodeExpand) === null || _b === void 0 ? void 0 : _b.call(config, node, expanded);
|
|
992
|
+
this._applyExpandCollapse();
|
|
993
|
+
}
|
|
994
|
+
/** Expand a node by its id. No-op if already expanded. */
|
|
995
|
+
expandNode(nodeId) {
|
|
996
|
+
var _a, _b, _c;
|
|
997
|
+
const id = String(nodeId);
|
|
998
|
+
if (!this._collapsedNodeIds.has(id))
|
|
999
|
+
return;
|
|
1000
|
+
this._collapsedNodeIds.delete(id);
|
|
1001
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
1002
|
+
if (node)
|
|
1003
|
+
(_c = (_b = this.config).onNodeExpand) === null || _c === void 0 ? void 0 : _c.call(_b, node, true);
|
|
1004
|
+
this._applyExpandCollapse();
|
|
1005
|
+
}
|
|
1006
|
+
/** Collapse a node by its id. No-op if already collapsed. */
|
|
1007
|
+
collapseNode(nodeId) {
|
|
1008
|
+
var _a, _b;
|
|
1009
|
+
const { config } = this;
|
|
1010
|
+
const id = String(nodeId);
|
|
1011
|
+
if (this._collapsedNodeIds.has(id))
|
|
1012
|
+
return;
|
|
1013
|
+
this._collapsedNodeIds.add(id);
|
|
1014
|
+
const node = (_a = this._fullData) === null || _a === void 0 ? void 0 : _a.nodes.find(n => String(n.id) === id);
|
|
1015
|
+
if (node)
|
|
1016
|
+
(_b = config.onNodeExpand) === null || _b === void 0 ? void 0 : _b.call(config, node, false);
|
|
1017
|
+
this._applyExpandCollapse();
|
|
1018
|
+
}
|
|
1019
|
+
/** Returns whether a node is currently collapsed. */
|
|
1020
|
+
isNodeCollapsed(nodeId) {
|
|
1021
|
+
return this._collapsedNodeIds.has(String(nodeId));
|
|
1022
|
+
}
|
|
1023
|
+
/** Set the complete set of collapsed node ids in one shot.
|
|
1024
|
+
* All currently collapsed nodes not in the new set are expanded; all ids in the new set are
|
|
1025
|
+
* collapsed. A single layout recalculation is triggered at the end, avoiding the race condition
|
|
1026
|
+
* that occurs when `collapseNode`/`expandNode` are called in a loop.
|
|
1027
|
+
* `nodeCollapsible` is still respected for the collapse direction.
|
|
1028
|
+
*/
|
|
1029
|
+
setCollapsedNodes(nodeIds) {
|
|
1030
|
+
this._collapsedNodeIds = new Set(nodeIds.map(id => String(id)));
|
|
1031
|
+
this._isAutoFitDisabled = false;
|
|
1032
|
+
this._shouldFitLayout = true;
|
|
1033
|
+
this._applyExpandCollapse();
|
|
1034
|
+
}
|
|
873
1035
|
/** Call a partial render to update the positions of the nodes and their links.
|
|
874
1036
|
* This can be useful when you've changed the node positions manually outside
|
|
875
1037
|
* of the component and want to update the graph.
|