@opendata-ai/openchart-vanilla 8.5.1 → 8.5.2
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/dist/{chunk-WOLX5EZT.js → chunk-273EHRBC.js} +22 -18
- package/dist/chunk-273EHRBC.js.map +1 -0
- package/dist/graph-3d/index.d.ts +5 -4
- package/dist/graph-3d/index.js +214 -27
- package/dist/graph-3d/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-WOLX5EZT.js.map +0 -1
package/dist/graph-3d/index.d.ts
CHANGED
|
@@ -200,10 +200,11 @@ declare const LINK_WIDTH_MAX_EDGES = 2000;
|
|
|
200
200
|
* setters.
|
|
201
201
|
*
|
|
202
202
|
* Differences from 2D that are deliberate and documented in RFC 27: no
|
|
203
|
-
* keyboard navigation, no node drag, no cursor repulsion or springy drag,
|
|
204
|
-
* wall-clock warmup budget
|
|
205
|
-
* (
|
|
206
|
-
* impulse
|
|
203
|
+
* keyboard navigation, no node drag, no cursor repulsion or springy drag, and
|
|
204
|
+
* no wall-clock warmup budget. A structural `update()` DOES animate like 2D
|
|
205
|
+
* (enter fade, exit ghosts, churn-scaled local reheat), but it reaches the
|
|
206
|
+
* impulse indirectly: `graphData()` forces alpha to 1 and the library exposes no
|
|
207
|
+
* alpha setter, so `beginUpdateReheat` damps it from outside instead.
|
|
207
208
|
*/
|
|
208
209
|
|
|
209
210
|
/**
|
package/dist/graph-3d/index.js
CHANGED
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
popAlpha,
|
|
12
12
|
popScale,
|
|
13
13
|
registerGraphRenderer,
|
|
14
|
+
reheatAlpha,
|
|
14
15
|
resolveHighlightTarget,
|
|
15
16
|
seedNodePositions
|
|
16
|
-
} from "../chunk-
|
|
17
|
+
} from "../chunk-273EHRBC.js";
|
|
17
18
|
import {
|
|
18
19
|
AnimationScheduler,
|
|
19
20
|
createTween,
|
|
@@ -224,7 +225,7 @@ function forceCluster3D(strength) {
|
|
|
224
225
|
};
|
|
225
226
|
return force;
|
|
226
227
|
}
|
|
227
|
-
function applySimulationConfig(graph, config, nodeCount) {
|
|
228
|
+
function applySimulationConfig(graph, config, nodeCount, opts = {}) {
|
|
228
229
|
const charge = graph.d3Force("charge");
|
|
229
230
|
if (charge && typeof charge.strength === "function") {
|
|
230
231
|
charge.strength(config.chargeStrength);
|
|
@@ -247,9 +248,24 @@ function applySimulationConfig(graph, config, nodeCount) {
|
|
|
247
248
|
);
|
|
248
249
|
graph.d3VelocityDecay(config.velocityDecay);
|
|
249
250
|
graph.d3AlphaDecay(config.alphaDecay);
|
|
250
|
-
graph.warmupTicks(
|
|
251
|
+
graph.warmupTicks(opts.warmupTicks ?? configuredWarmupTicks(config, nodeCount));
|
|
251
252
|
graph.cooldownTicks(ticksToSettle(config.alphaDecay));
|
|
252
253
|
}
|
|
254
|
+
function configuredWarmupTicks(config, nodeCount) {
|
|
255
|
+
return budgetedWarmupTicks(config.warmupTicks ?? 0, nodeCount, config.warmupBudgetMs);
|
|
256
|
+
}
|
|
257
|
+
function beginUpdateReheat(graph, config, initialAlpha) {
|
|
258
|
+
graph.d3Force("center", null);
|
|
259
|
+
const scale = Math.min(1, Math.max(0, initialAlpha));
|
|
260
|
+
graph.d3VelocityDecay(1 - (1 - config.velocityDecay) * scale);
|
|
261
|
+
}
|
|
262
|
+
function endUpdateReheat(graph, config, nodeCount) {
|
|
263
|
+
graph.d3VelocityDecay(config.velocityDecay);
|
|
264
|
+
graph.warmupTicks(configuredWarmupTicks(config, nodeCount));
|
|
265
|
+
if (config.centerForce !== false) {
|
|
266
|
+
graph.d3Force("center", graph.d3Force("center") ?? forceCenter());
|
|
267
|
+
}
|
|
268
|
+
}
|
|
253
269
|
|
|
254
270
|
// src/graph-3d/labels.ts
|
|
255
271
|
var LABEL_BUDGET_3D = 40;
|
|
@@ -389,6 +405,13 @@ function updateLinkPosition(obj, start, end) {
|
|
|
389
405
|
if (obj.dashed) obj.object.computeLineDistances();
|
|
390
406
|
return true;
|
|
391
407
|
}
|
|
408
|
+
function placeGhostLink(obj, start, end) {
|
|
409
|
+
if (updateLinkPosition(obj, start, end)) return;
|
|
410
|
+
const mesh = obj.object;
|
|
411
|
+
mesh.position.set(start.x, start.y, start.z);
|
|
412
|
+
mesh.lookAt(end.x, end.y, end.z);
|
|
413
|
+
mesh.scale.z = Math.hypot(end.x - start.x, end.y - start.y, end.z - start.z);
|
|
414
|
+
}
|
|
392
415
|
function disposeLinkObject(obj) {
|
|
393
416
|
obj.geometry.dispose();
|
|
394
417
|
obj.material.dispose();
|
|
@@ -545,6 +568,8 @@ function createGraph3DRenderer(ctx) {
|
|
|
545
568
|
const entranceEdgeAlpha = /* @__PURE__ */ new Map();
|
|
546
569
|
let entranceCameraPull = 1;
|
|
547
570
|
let emphasisTween = null;
|
|
571
|
+
let ghosts = [];
|
|
572
|
+
let reheatActive = false;
|
|
548
573
|
let pumpId = null;
|
|
549
574
|
const scheduler = new AnimationScheduler(() => pump());
|
|
550
575
|
function pump() {
|
|
@@ -620,11 +645,13 @@ function createGraph3DRenderer(ctx) {
|
|
|
620
645
|
const p = positions.get(node.id) ?? [0, 0, 0];
|
|
621
646
|
return { id: node.id, node, x: p[0], y: p[1], z: p[2] };
|
|
622
647
|
});
|
|
648
|
+
const keys = edgeKeys(compilation.edges);
|
|
623
649
|
linkData = compilation.edges.map((edge, edgeIndex) => ({
|
|
624
650
|
source: edge.source,
|
|
625
651
|
target: edge.target,
|
|
626
652
|
edge,
|
|
627
|
-
edgeIndex
|
|
653
|
+
edgeIndex,
|
|
654
|
+
key: keys[edgeIndex]
|
|
628
655
|
}));
|
|
629
656
|
nodeById = new Map(nodeData.map((d) => [d.id, d]));
|
|
630
657
|
graph.graphData({ nodes: nodeData, links: linkData });
|
|
@@ -647,11 +674,11 @@ function createGraph3DRenderer(ctx) {
|
|
|
647
674
|
}
|
|
648
675
|
const linkThreeObject = (datum) => linkObjectFor(datum).object;
|
|
649
676
|
function linkObjectFor(datum) {
|
|
650
|
-
let obj = linkObjects.get(datum.
|
|
677
|
+
let obj = linkObjects.get(datum.key);
|
|
651
678
|
if (!obj) {
|
|
652
679
|
const resting = edgeBaseAlpha?.get(datum.edgeIndex) ?? 0.3;
|
|
653
680
|
obj = createLinkObject(datum.edge, useLinkWidth, resting);
|
|
654
|
-
linkObjects.set(datum.
|
|
681
|
+
linkObjects.set(datum.key, obj);
|
|
655
682
|
const base = displayEdgeAlpha.get(datum.edgeIndex) ?? resting;
|
|
656
683
|
obj.material.opacity = entranceActive ? base * (entranceEdgeAlpha.get(datum.edgeIndex) ?? 0) : base;
|
|
657
684
|
}
|
|
@@ -700,10 +727,11 @@ function createGraph3DRenderer(ctx) {
|
|
|
700
727
|
obj.material.opacity = a;
|
|
701
728
|
}
|
|
702
729
|
}
|
|
703
|
-
for (const
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
obj
|
|
730
|
+
for (const datum of linkData) {
|
|
731
|
+
const obj = linkObjects.get(datum.key);
|
|
732
|
+
const a = displayEdgeAlpha.get(datum.edgeIndex);
|
|
733
|
+
if (!obj || a === void 0) continue;
|
|
734
|
+
obj.material.opacity = entranceActive ? a * (entranceEdgeAlpha.get(datum.edgeIndex) ?? 0) : a;
|
|
707
735
|
}
|
|
708
736
|
}
|
|
709
737
|
function armEmphasis() {
|
|
@@ -1204,6 +1232,104 @@ function createGraph3DRenderer(ctx) {
|
|
|
1204
1232
|
});
|
|
1205
1233
|
scheduler.add(tween);
|
|
1206
1234
|
}
|
|
1235
|
+
function ghostParent() {
|
|
1236
|
+
for (const obj of nodeObjects.values()) {
|
|
1237
|
+
if (obj.group.parent) return obj.group.parent;
|
|
1238
|
+
}
|
|
1239
|
+
return graph.scene() ?? null;
|
|
1240
|
+
}
|
|
1241
|
+
function nodeGhost(node, pos, alpha, withLabel) {
|
|
1242
|
+
const obj = createNodeObject(node, labelColor());
|
|
1243
|
+
obj.group.position.set(pos.x, pos.y, pos.z);
|
|
1244
|
+
obj.material.opacity = alpha;
|
|
1245
|
+
const fades = [{ material: obj.material, base: alpha }];
|
|
1246
|
+
if (withLabel) {
|
|
1247
|
+
const sprite = ensureLabelSprite(obj);
|
|
1248
|
+
if (sprite) {
|
|
1249
|
+
sprite.visible = true;
|
|
1250
|
+
fades.push({
|
|
1251
|
+
material: sprite.material,
|
|
1252
|
+
base: 1
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return { object: obj.group, fades, dispose: () => disposeNodeObject(obj) };
|
|
1257
|
+
}
|
|
1258
|
+
function linkGhost(edge, start, end, alpha, useWidth) {
|
|
1259
|
+
const obj = createLinkObject(edge, useWidth, alpha);
|
|
1260
|
+
placeGhostLink(obj, start, end);
|
|
1261
|
+
return {
|
|
1262
|
+
object: obj.object,
|
|
1263
|
+
fades: [{ material: obj.material, base: alpha }],
|
|
1264
|
+
dispose: () => disposeLinkObject(obj)
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
function startExitFade(pending) {
|
|
1268
|
+
if (pending.length === 0) return;
|
|
1269
|
+
const cfg = compilation.animation?.exit ?? null;
|
|
1270
|
+
const parent = ghostParent();
|
|
1271
|
+
if (!cfg || prefersReducedMotion() || !parent) {
|
|
1272
|
+
for (const g of pending) g.dispose();
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
for (const g of pending) {
|
|
1276
|
+
for (const f of g.fades) f.material.transparent = true;
|
|
1277
|
+
parent.add(g.object);
|
|
1278
|
+
}
|
|
1279
|
+
ghosts = ghosts.concat(pending);
|
|
1280
|
+
scheduler.add(
|
|
1281
|
+
createTween({
|
|
1282
|
+
duration: cfg.duration,
|
|
1283
|
+
ease: resolveEase(cfg.ease),
|
|
1284
|
+
apply: (t) => {
|
|
1285
|
+
for (const g of pending) {
|
|
1286
|
+
for (const f of g.fades) f.material.opacity = f.base * (1 - t);
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1289
|
+
onDone: () => removeGhosts(pending)
|
|
1290
|
+
})
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
function removeGhosts(list) {
|
|
1294
|
+
for (const g of list) {
|
|
1295
|
+
g.object.parent?.remove(g.object);
|
|
1296
|
+
g.dispose();
|
|
1297
|
+
}
|
|
1298
|
+
const gone = new Set(list);
|
|
1299
|
+
ghosts = ghosts.filter((g) => !gone.has(g));
|
|
1300
|
+
}
|
|
1301
|
+
function startEnterFade(enteringIds, enteringEdgeIndices) {
|
|
1302
|
+
const cfg = compilation.animation?.update ?? null;
|
|
1303
|
+
if (enteringIds.length === 0 && enteringEdgeIndices.length === 0) return;
|
|
1304
|
+
if (!cfg || prefersReducedMotion()) return;
|
|
1305
|
+
entranceActive = true;
|
|
1306
|
+
for (const node of compilation.nodes) {
|
|
1307
|
+
entranceNodeAlpha.set(node.id, 1);
|
|
1308
|
+
entranceNodeScale.set(node.id, 1);
|
|
1309
|
+
}
|
|
1310
|
+
for (let i = 0; i < compilation.edges.length; i++) entranceEdgeAlpha.set(i, 1);
|
|
1311
|
+
for (const id of enteringIds) {
|
|
1312
|
+
entranceNodeAlpha.set(id, 0);
|
|
1313
|
+
entranceNodeScale.set(id, ENTRANCE_MIN_SCALE);
|
|
1314
|
+
}
|
|
1315
|
+
for (const index of enteringEdgeIndices) entranceEdgeAlpha.set(index, 0);
|
|
1316
|
+
paint();
|
|
1317
|
+
scheduler.add(
|
|
1318
|
+
createTween({
|
|
1319
|
+
duration: cfg.duration,
|
|
1320
|
+
ease: resolveEase(cfg.ease),
|
|
1321
|
+
apply: (t) => {
|
|
1322
|
+
for (const id of enteringIds) {
|
|
1323
|
+
entranceNodeAlpha.set(id, popAlpha(t));
|
|
1324
|
+
entranceNodeScale.set(id, Math.max(ENTRANCE_MIN_SCALE, popScale(t)));
|
|
1325
|
+
}
|
|
1326
|
+
for (const index of enteringEdgeIndices) entranceEdgeAlpha.set(index, t);
|
|
1327
|
+
paint();
|
|
1328
|
+
},
|
|
1329
|
+
onDone: endEntrance
|
|
1330
|
+
})
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1207
1333
|
function search(query) {
|
|
1208
1334
|
if (destroyed) return;
|
|
1209
1335
|
searchManager.search(query, compilation.nodes);
|
|
@@ -1307,7 +1433,7 @@ function createGraph3DRenderer(ctx) {
|
|
|
1307
1433
|
const edge = compilation.edges[i];
|
|
1308
1434
|
linkData[i].edge = edge;
|
|
1309
1435
|
if (shapeClassChanged) continue;
|
|
1310
|
-
const obj = linkObjects.get(i);
|
|
1436
|
+
const obj = linkObjects.get(linkData[i].key);
|
|
1311
1437
|
if (obj) applyLinkVisuals(obj, edge, useLinkWidth);
|
|
1312
1438
|
}
|
|
1313
1439
|
if (shapeClassChanged) {
|
|
@@ -1320,17 +1446,46 @@ function createGraph3DRenderer(ctx) {
|
|
|
1320
1446
|
return;
|
|
1321
1447
|
}
|
|
1322
1448
|
const nextNodeIds = new Set(compilation.nodes.map((n) => n.id));
|
|
1449
|
+
const keys = edgeKeys(compilation.edges);
|
|
1450
|
+
const nextKeys = new Set(keys);
|
|
1451
|
+
const prevLinkByKey = new Map(linkData.map((d) => [d.key, d]));
|
|
1452
|
+
const pendingGhosts = [];
|
|
1323
1453
|
for (const [id, obj] of nodeObjects) {
|
|
1324
|
-
if (
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1454
|
+
if (nextNodeIds.has(id)) continue;
|
|
1455
|
+
const datum = nodeById.get(id);
|
|
1456
|
+
if (datum) {
|
|
1457
|
+
pendingGhosts.push(
|
|
1458
|
+
nodeGhost(
|
|
1459
|
+
datum.node,
|
|
1460
|
+
{ x: datum.x ?? 0, y: datum.y ?? 0, z: datum.z ?? 0 },
|
|
1461
|
+
displayNodeAlpha.get(id) ?? datum.node.opacity,
|
|
1462
|
+
Boolean(obj.sprite?.visible)
|
|
1463
|
+
)
|
|
1464
|
+
);
|
|
1330
1465
|
}
|
|
1466
|
+
disposeNodeObject(obj);
|
|
1467
|
+
nodeObjects.delete(id);
|
|
1468
|
+
displayNodeAlpha.delete(id);
|
|
1469
|
+
entranceNodeAlpha.delete(id);
|
|
1470
|
+
entranceNodeScale.delete(id);
|
|
1471
|
+
}
|
|
1472
|
+
for (const [key, obj] of linkObjects) {
|
|
1473
|
+
if (nextKeys.has(key) && !shapeClassChanged) continue;
|
|
1474
|
+
const datum = prevLinkByKey.get(key);
|
|
1475
|
+
if (datum && !nextKeys.has(key)) {
|
|
1476
|
+
pendingGhosts.push(
|
|
1477
|
+
linkGhost(
|
|
1478
|
+
datum.edge,
|
|
1479
|
+
endpointPos(datum.source),
|
|
1480
|
+
endpointPos(datum.target),
|
|
1481
|
+
displayEdgeAlpha.get(datum.edgeIndex) ?? 0.3,
|
|
1482
|
+
prevUseLinkWidth
|
|
1483
|
+
)
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
disposeLinkObject(obj);
|
|
1487
|
+
linkObjects.delete(key);
|
|
1331
1488
|
}
|
|
1332
|
-
for (const obj of linkObjects.values()) disposeLinkObject(obj);
|
|
1333
|
-
linkObjects.clear();
|
|
1334
1489
|
displayEdgeAlpha.clear();
|
|
1335
1490
|
entranceEdgeAlpha.clear();
|
|
1336
1491
|
const survivors = new Map(nodeById);
|
|
@@ -1351,15 +1506,33 @@ function createGraph3DRenderer(ctx) {
|
|
|
1351
1506
|
z: seedZ(node.id, node.radius, node.community)
|
|
1352
1507
|
};
|
|
1353
1508
|
});
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
edgeIndex
|
|
1359
|
-
|
|
1509
|
+
const enteringEdgeIndices = [];
|
|
1510
|
+
linkData = compilation.edges.map((edge, edgeIndex) => {
|
|
1511
|
+
const key = keys[edgeIndex];
|
|
1512
|
+
const existing = shapeClassChanged ? void 0 : prevLinkByKey.get(key);
|
|
1513
|
+
if (!prevLinkByKey.has(key)) enteringEdgeIndices.push(edgeIndex);
|
|
1514
|
+
if (existing) {
|
|
1515
|
+
existing.edge = edge;
|
|
1516
|
+
existing.edgeIndex = edgeIndex;
|
|
1517
|
+
const obj = linkObjects.get(key);
|
|
1518
|
+
if (obj) applyLinkVisuals(obj, edge, useLinkWidth);
|
|
1519
|
+
return existing;
|
|
1520
|
+
}
|
|
1521
|
+
return { source: edge.source, target: edge.target, edge, edgeIndex, key };
|
|
1522
|
+
});
|
|
1360
1523
|
nodeById = new Map(nodeData.map((d) => [d.id, d]));
|
|
1361
|
-
applySimulationConfig(graph, compilation.simulationConfig, nodeData.length
|
|
1524
|
+
applySimulationConfig(graph, compilation.simulationConfig, nodeData.length, {
|
|
1525
|
+
warmupTicks: 0
|
|
1526
|
+
});
|
|
1527
|
+
beginUpdateReheat(
|
|
1528
|
+
graph,
|
|
1529
|
+
compilation.simulationConfig,
|
|
1530
|
+
reheatAlpha(diff, compilation.edges.length)
|
|
1531
|
+
);
|
|
1532
|
+
reheatActive = true;
|
|
1362
1533
|
graph.graphData({ nodes: nodeData, links: linkData });
|
|
1534
|
+
startEnterFade(diff.enteringIds, enteringEdgeIndices);
|
|
1535
|
+
startExitFade(pendingGhosts);
|
|
1363
1536
|
pruneInteractionState();
|
|
1364
1537
|
armEmphasis();
|
|
1365
1538
|
}
|
|
@@ -1381,6 +1554,7 @@ function createGraph3DRenderer(ctx) {
|
|
|
1381
1554
|
if (destroyed) return;
|
|
1382
1555
|
destroyed = true;
|
|
1383
1556
|
scheduler.cancelAll();
|
|
1557
|
+
removeGhosts(ghosts);
|
|
1384
1558
|
if (pumpId !== null) {
|
|
1385
1559
|
cancelAnimationFrame(pumpId);
|
|
1386
1560
|
pumpId = null;
|
|
@@ -1423,7 +1597,7 @@ function createGraph3DRenderer(ctx) {
|
|
|
1423
1597
|
graph.backgroundColor(resolvedSurface(compilation.theme));
|
|
1424
1598
|
}
|
|
1425
1599
|
graph.showNavInfo(false).enableNodeDrag(false).nodeId("id").nodeLabel("").linkLabel("").nodeVal((d) => d.node.radius).nodeColor((d) => d.node.fill).nodeThreeObject((d) => nodeObjectFor(d).group).linkColor((d) => d.edge.stroke).linkWidth((d) => useLinkWidth ? d.edge.strokeWidth : 0).linkThreeObject(linkThreeObject).linkPositionUpdate((_obj, coords, d) => {
|
|
1426
|
-
const link = linkObjects.get(d.
|
|
1600
|
+
const link = linkObjects.get(d.key);
|
|
1427
1601
|
return link ? updateLinkPosition(link, coords.start, coords.end) : false;
|
|
1428
1602
|
}).linkHoverPrecision(useLinkWidth ? 4 : 8);
|
|
1429
1603
|
const { width, height } = shell.getSize();
|
|
@@ -1484,6 +1658,10 @@ function createGraph3DRenderer(ctx) {
|
|
|
1484
1658
|
}
|
|
1485
1659
|
}).onEngineStop(() => {
|
|
1486
1660
|
if (destroyed) return;
|
|
1661
|
+
if (reheatActive) {
|
|
1662
|
+
reheatActive = false;
|
|
1663
|
+
endUpdateReheat(graph, compilation.simulationConfig, nodeData.length);
|
|
1664
|
+
}
|
|
1487
1665
|
if (autoFit && options?.fitOnLoad !== false) {
|
|
1488
1666
|
autoFit = false;
|
|
1489
1667
|
fitNow({ duration: 0 });
|
|
@@ -1531,6 +1709,15 @@ function createGraph3DRenderer(ctx) {
|
|
|
1531
1709
|
destroy
|
|
1532
1710
|
};
|
|
1533
1711
|
}
|
|
1712
|
+
function edgeKeys(edges) {
|
|
1713
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1714
|
+
return edges.map((edge) => {
|
|
1715
|
+
const base = `${edge.source}\0${edge.target}`;
|
|
1716
|
+
const n = seen.get(base) ?? 0;
|
|
1717
|
+
seen.set(base, n + 1);
|
|
1718
|
+
return `${base}\0${n}`;
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1534
1721
|
function themeBackground(theme) {
|
|
1535
1722
|
const colors = theme?.colors;
|
|
1536
1723
|
if (!colors || Array.isArray(colors)) return void 0;
|