@opendata-ai/openchart-vanilla 8.0.0-rc.13 → 8.0.0-rc.14
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/index.d.ts +4 -0
- package/dist/index.js +142 -17
- package/dist/index.js.map +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -219,6 +219,10 @@ interface GraphInstance {
|
|
|
219
219
|
getHighlight(): string[] | null;
|
|
220
220
|
/** Headless snapshot of the legend (node categories + edge categories). */
|
|
221
221
|
getLegend(): GraphLegendData;
|
|
222
|
+
/** Set the active category filter declaratively (replaces legend toggle state). */
|
|
223
|
+
setActiveCategories(values: string[]): void;
|
|
224
|
+
/** Current active category values (empty = all active, no filter). */
|
|
225
|
+
getActiveCategories(): string[];
|
|
222
226
|
resize(): void;
|
|
223
227
|
destroy(): void;
|
|
224
228
|
}
|
package/dist/index.js
CHANGED
|
@@ -570,6 +570,7 @@ var ZoomTransform = class _ZoomTransform {
|
|
|
570
570
|
if (nodes.length === 0) {
|
|
571
571
|
return { transform: _ZoomTransform.identity(), contentHeight: canvasH };
|
|
572
572
|
}
|
|
573
|
+
const insetTop = Math.min(Math.max(0, opts?.insetTop ?? 0), canvasH * 0.4);
|
|
573
574
|
let minX = Infinity;
|
|
574
575
|
let minY = Infinity;
|
|
575
576
|
let maxX = -Infinity;
|
|
@@ -601,13 +602,13 @@ var ZoomTransform = class _ZoomTransform {
|
|
|
601
602
|
maxY = cy2 + graphH / 2;
|
|
602
603
|
}
|
|
603
604
|
const availW = canvasW - padding * 2;
|
|
604
|
-
const availH = canvasH - padding * 2;
|
|
605
|
+
const availH = canvasH - insetTop - padding * 2;
|
|
605
606
|
const k = Math.min(1, availW / graphW, availH / graphH);
|
|
606
607
|
const cx = (minX + maxX) / 2;
|
|
607
608
|
const cy = (minY + maxY) / 2;
|
|
608
609
|
const tx = canvasW / 2 - cx * k;
|
|
609
|
-
const ty = canvasH / 2 - cy * k;
|
|
610
|
-
const contentHeight = graphH * k + padding * 2;
|
|
610
|
+
const ty = insetTop + (canvasH - insetTop) / 2 - cy * k;
|
|
611
|
+
const contentHeight = graphH * k + padding * 2 + insetTop;
|
|
611
612
|
return {
|
|
612
613
|
transform: new _ZoomTransform(tx, ty, k),
|
|
613
614
|
contentHeight
|
|
@@ -727,6 +728,65 @@ function nodeEnterProgress(globalT, index2, total, buckets = 8) {
|
|
|
727
728
|
return Math.round(clamped * b) / b;
|
|
728
729
|
}
|
|
729
730
|
var ENTRANCE_STAGGER_MAX_NODES = 3e3;
|
|
731
|
+
var ENTRANCE_DRIFT_PX = 16;
|
|
732
|
+
function entranceOrder(nodes) {
|
|
733
|
+
const rank = /* @__PURE__ */ new Map();
|
|
734
|
+
if (nodes.length === 0) return rank;
|
|
735
|
+
let cx = 0;
|
|
736
|
+
let cy = 0;
|
|
737
|
+
for (const n of nodes) {
|
|
738
|
+
cx += n.x;
|
|
739
|
+
cy += n.y;
|
|
740
|
+
}
|
|
741
|
+
cx /= nodes.length;
|
|
742
|
+
cy /= nodes.length;
|
|
743
|
+
const sorted = [...nodes].sort((a2, b) => {
|
|
744
|
+
const da = (a2.x - cx) * (a2.x - cx) + (a2.y - cy) * (a2.y - cy);
|
|
745
|
+
const db = (b.x - cx) * (b.x - cx) + (b.y - cy) * (b.y - cy);
|
|
746
|
+
return da - db;
|
|
747
|
+
});
|
|
748
|
+
for (let i = 0; i < sorted.length; i++) rank.set(sorted[i].id, i);
|
|
749
|
+
return rank;
|
|
750
|
+
}
|
|
751
|
+
function entranceOffsets(nodes, dist = ENTRANCE_DRIFT_PX) {
|
|
752
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
753
|
+
if (nodes.length === 0) return offsets;
|
|
754
|
+
let cx = 0;
|
|
755
|
+
let cy = 0;
|
|
756
|
+
for (const n of nodes) {
|
|
757
|
+
cx += n.x;
|
|
758
|
+
cy += n.y;
|
|
759
|
+
}
|
|
760
|
+
cx /= nodes.length;
|
|
761
|
+
cy /= nodes.length;
|
|
762
|
+
for (const n of nodes) {
|
|
763
|
+
const dx = n.x - cx;
|
|
764
|
+
const dy = n.y - cy;
|
|
765
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
766
|
+
if (len < 1e-6) {
|
|
767
|
+
offsets.set(n.id, { x: 0, y: -dist });
|
|
768
|
+
} else {
|
|
769
|
+
offsets.set(n.id, { x: dx / len * dist, y: dy / len * dist });
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return offsets;
|
|
773
|
+
}
|
|
774
|
+
function popScale(t) {
|
|
775
|
+
if (t <= 0) return 0;
|
|
776
|
+
if (t >= 1) return 1;
|
|
777
|
+
const c1 = 1.70158;
|
|
778
|
+
const c3 = c1 + 1;
|
|
779
|
+
const u = t - 1;
|
|
780
|
+
return 1 + c3 * u * u * u + c1 * u * u;
|
|
781
|
+
}
|
|
782
|
+
function popAlpha(t) {
|
|
783
|
+
const a2 = t / 0.6;
|
|
784
|
+
return a2 >= 1 ? 1 : a2 <= 0 ? 0 : a2;
|
|
785
|
+
}
|
|
786
|
+
function driftFactor(t) {
|
|
787
|
+
const u = 1 - (t <= 0 ? 0 : t >= 1 ? 1 : t);
|
|
788
|
+
return u * u;
|
|
789
|
+
}
|
|
730
790
|
|
|
731
791
|
// src/graph/canvas-renderer.ts
|
|
732
792
|
var LABEL_FONT_MIN = 8;
|
|
@@ -765,12 +825,24 @@ function nodeTierAlpha(tier, dimOpacity) {
|
|
|
765
825
|
function lerp(a2, b, t) {
|
|
766
826
|
return a2 + (b - a2) * t;
|
|
767
827
|
}
|
|
828
|
+
var ZERO_SHIFT = { x: 0, y: 0 };
|
|
768
829
|
function makeEntranceReveal(entrance, total) {
|
|
769
830
|
const g = entrance.t;
|
|
770
|
-
const
|
|
831
|
+
const { stagger, order, offsets } = entrance;
|
|
832
|
+
const rankOf = (node) => order?.get(node.id) ?? node.index;
|
|
833
|
+
const nodeT = (node) => stagger ? nodeEnterProgress(g, rankOf(node), total) : g;
|
|
771
834
|
const edgeAlpha = Math.max(0, (g - 0.3) / 0.7);
|
|
835
|
+
const globalRamp = 0.6 + 0.4 * g;
|
|
772
836
|
return {
|
|
773
|
-
|
|
837
|
+
nodeAlpha: (node) => stagger ? popAlpha(nodeT(node)) : globalRamp,
|
|
838
|
+
nodeScale: (node) => stagger ? popScale(nodeT(node)) : globalRamp,
|
|
839
|
+
shift: (id) => {
|
|
840
|
+
if (!stagger || !offsets || !order) return ZERO_SHIFT;
|
|
841
|
+
const off = offsets.get(id);
|
|
842
|
+
if (!off) return ZERO_SHIFT;
|
|
843
|
+
const f = driftFactor(nodeEnterProgress(g, order.get(id) ?? 0, total));
|
|
844
|
+
return f > 0 ? { x: off.x * f, y: off.y * f } : ZERO_SHIFT;
|
|
845
|
+
},
|
|
774
846
|
edgeAlpha,
|
|
775
847
|
labelAlpha: g
|
|
776
848
|
};
|
|
@@ -1131,8 +1203,10 @@ var GraphCanvasRenderer = class {
|
|
|
1131
1203
|
return nodeTierAlpha(nodeTier(node, nextFocus), dimOpacity);
|
|
1132
1204
|
};
|
|
1133
1205
|
const searchAlpha = (node) => searchMatches !== null && !searchMatches.has(node.id) ? SEARCH_NON_MATCH_ALPHA : 1;
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1206
|
+
const entranceAlpha = (node) => entrance ? entrance.nodeAlpha(node) : 1;
|
|
1207
|
+
const entranceScale = (node) => entrance ? entrance.nodeScale(node) : 1;
|
|
1208
|
+
const entranceShift = (node) => entrance ? entrance.shift(node.id) : ZERO_SHIFT;
|
|
1209
|
+
const effectiveAlpha = (node) => focusAlpha(node) * searchAlpha(node) * entranceAlpha(node) * enterAlphaFor(node.id);
|
|
1136
1210
|
const bulkNodes = [];
|
|
1137
1211
|
const specialNodes = [];
|
|
1138
1212
|
for (const node of nodes) {
|
|
@@ -1142,8 +1216,8 @@ var GraphCanvasRenderer = class {
|
|
|
1142
1216
|
bulkNodes.push(node);
|
|
1143
1217
|
}
|
|
1144
1218
|
}
|
|
1145
|
-
const r = (node) => Math.max(node.radius, minRadius) *
|
|
1146
|
-
if (showGlow) {
|
|
1219
|
+
const r = (node) => Math.max(node.radius, minRadius) * entranceScale(node);
|
|
1220
|
+
if (showGlow && !entrance) {
|
|
1147
1221
|
this.drawGlowBatched(ctx, bulkNodes, searchMatches, minRadius);
|
|
1148
1222
|
}
|
|
1149
1223
|
const fillGroups = /* @__PURE__ */ new Map();
|
|
@@ -1163,8 +1237,10 @@ var GraphCanvasRenderer = class {
|
|
|
1163
1237
|
ctx.beginPath();
|
|
1164
1238
|
for (const node of group) {
|
|
1165
1239
|
const nr = r(node);
|
|
1166
|
-
|
|
1167
|
-
|
|
1240
|
+
if (nr <= 0) continue;
|
|
1241
|
+
const s = entranceShift(node);
|
|
1242
|
+
ctx.moveTo(node.x + s.x + nr, node.y + s.y);
|
|
1243
|
+
ctx.arc(node.x + s.x, node.y + s.y, nr, 0, TWO_PI);
|
|
1168
1244
|
}
|
|
1169
1245
|
ctx.fill();
|
|
1170
1246
|
}
|
|
@@ -1186,8 +1262,10 @@ var GraphCanvasRenderer = class {
|
|
|
1186
1262
|
ctx.beginPath();
|
|
1187
1263
|
for (const node of group) {
|
|
1188
1264
|
const nr = r(node);
|
|
1189
|
-
|
|
1190
|
-
|
|
1265
|
+
if (nr <= 0) continue;
|
|
1266
|
+
const s = entranceShift(node);
|
|
1267
|
+
ctx.moveTo(node.x + s.x + nr, node.y + s.y);
|
|
1268
|
+
ctx.arc(node.x + s.x, node.y + s.y, nr, 0, TWO_PI);
|
|
1191
1269
|
}
|
|
1192
1270
|
ctx.stroke();
|
|
1193
1271
|
}
|
|
@@ -3668,6 +3746,8 @@ function createGraph(container, spec, options) {
|
|
|
3668
3746
|
let entranceProgress = 1;
|
|
3669
3747
|
let entranceActive = false;
|
|
3670
3748
|
let entranceStagger = false;
|
|
3749
|
+
let entranceOrderMap = null;
|
|
3750
|
+
let entranceOffsetMap = null;
|
|
3671
3751
|
let entranceFitInFlight = false;
|
|
3672
3752
|
let entranceReveal = null;
|
|
3673
3753
|
let suppressEntranceOnce = options?.suppressEntrance ?? false;
|
|
@@ -3807,6 +3887,7 @@ function createGraph(container, spec, options) {
|
|
|
3807
3887
|
wrapper.appendChild(legendEl);
|
|
3808
3888
|
}
|
|
3809
3889
|
container.appendChild(wrapper);
|
|
3890
|
+
syncChromeInset();
|
|
3810
3891
|
const canvasHeight = Math.max(height, 200);
|
|
3811
3892
|
renderer = new GraphCanvasRenderer(canvas);
|
|
3812
3893
|
renderer.resize(width, canvasHeight);
|
|
@@ -3856,6 +3937,16 @@ function createGraph(container, spec, options) {
|
|
|
3856
3937
|
}
|
|
3857
3938
|
function syncLegendActiveState() {
|
|
3858
3939
|
if (legendController) legendController.update(legendViewData());
|
|
3940
|
+
syncChromeInset();
|
|
3941
|
+
}
|
|
3942
|
+
function syncChromeInset() {
|
|
3943
|
+
if (!chromeEl) return;
|
|
3944
|
+
const legendW = legendEl?.offsetWidth ?? 0;
|
|
3945
|
+
chromeEl.style.right = legendW > 0 ? `${legendW + 24}px` : "";
|
|
3946
|
+
}
|
|
3947
|
+
function chromeInsetTop() {
|
|
3948
|
+
if (!chromeEl || chromeEl.style.display === "none") return 0;
|
|
3949
|
+
return chromeEl.offsetHeight;
|
|
3859
3950
|
}
|
|
3860
3951
|
function springyDragEnabled() {
|
|
3861
3952
|
return compilation.interaction.springyDrag && compilation.nodes.length <= SPRINGY_DRAG_MAX_NODES;
|
|
@@ -3943,7 +4034,8 @@ function createGraph(container, spec, options) {
|
|
|
3943
4034
|
const { width: cw, height: ch } = getCanvasDimensions();
|
|
3944
4035
|
const warmed = (compilation.simulationConfig.warmupTicks ?? 0) > 0;
|
|
3945
4036
|
const { transform } = ZoomTransform.fitBounds(positionedNodes, cw, ch, void 0, {
|
|
3946
|
-
spread: !warmed
|
|
4037
|
+
spread: !warmed,
|
|
4038
|
+
insetTop: chromeInsetTop()
|
|
3947
4039
|
});
|
|
3948
4040
|
return transform;
|
|
3949
4041
|
}
|
|
@@ -3961,12 +4053,19 @@ function createGraph(container, spec, options) {
|
|
|
3961
4053
|
return;
|
|
3962
4054
|
}
|
|
3963
4055
|
const { width: cw, height: ch } = getCanvasDimensions();
|
|
3964
|
-
const pulledBack = fit.zoomAt(fit.k * 0.
|
|
4056
|
+
const pulledBack = fit.zoomAt(fit.k * 0.85, cw / 2, ch / 2);
|
|
3965
4057
|
interactionManager.setTransform(pulledBack);
|
|
3966
4058
|
cameraChangePending = true;
|
|
3967
4059
|
entranceActive = true;
|
|
3968
4060
|
entranceProgress = 0;
|
|
3969
4061
|
entranceStagger = enter.stagger && positionedNodes.length <= ENTRANCE_STAGGER_MAX_NODES;
|
|
4062
|
+
if (entranceStagger) {
|
|
4063
|
+
entranceOrderMap = entranceOrder(positionedNodes);
|
|
4064
|
+
entranceOffsetMap = entranceOffsets(positionedNodes);
|
|
4065
|
+
} else {
|
|
4066
|
+
entranceOrderMap = null;
|
|
4067
|
+
entranceOffsetMap = null;
|
|
4068
|
+
}
|
|
3970
4069
|
if (enter.cameraFit) {
|
|
3971
4070
|
entranceFitInFlight = true;
|
|
3972
4071
|
flyCamera(fit, { duration: enter.duration + 100 }, () => {
|
|
@@ -4182,7 +4281,12 @@ function createGraph(container, spec, options) {
|
|
|
4182
4281
|
focus: focus ?? { t: 1, prev: settledNext, next: settledNext },
|
|
4183
4282
|
hoverRadiusScale,
|
|
4184
4283
|
dimOpacity: highlightDimOpacity ?? compilation.interaction.dimOpacity,
|
|
4185
|
-
entrance: entranceActive && entranceProgress < 1 ? {
|
|
4284
|
+
entrance: entranceActive && entranceProgress < 1 ? {
|
|
4285
|
+
t: entranceProgress,
|
|
4286
|
+
stagger: entranceStagger,
|
|
4287
|
+
order: entranceOrderMap ?? void 0,
|
|
4288
|
+
offsets: entranceOffsetMap ?? void 0
|
|
4289
|
+
} : void 0,
|
|
4186
4290
|
enterAlpha: enterAlphaMap ?? void 0,
|
|
4187
4291
|
exiting: exitingGhosts ?? void 0
|
|
4188
4292
|
};
|
|
@@ -4421,7 +4525,10 @@ function createGraph(container, spec, options) {
|
|
|
4421
4525
|
positionedNodes,
|
|
4422
4526
|
cw,
|
|
4423
4527
|
ch,
|
|
4424
|
-
opts?.padding
|
|
4528
|
+
opts?.padding,
|
|
4529
|
+
{
|
|
4530
|
+
insetTop: chromeInsetTop()
|
|
4531
|
+
}
|
|
4425
4532
|
);
|
|
4426
4533
|
flyCamera(fitTransform, opts);
|
|
4427
4534
|
}
|
|
@@ -4501,6 +4608,18 @@ function createGraph(container, spec, options) {
|
|
|
4501
4608
|
}))
|
|
4502
4609
|
};
|
|
4503
4610
|
}
|
|
4611
|
+
function setActiveCategories(values) {
|
|
4612
|
+
if (destroyed) return;
|
|
4613
|
+
activeCategories = new Set(values);
|
|
4614
|
+
highlightSet = categoryHighlightSet();
|
|
4615
|
+
highlightDimOpacity = null;
|
|
4616
|
+
syncLegendActiveState();
|
|
4617
|
+
refreshFocus();
|
|
4618
|
+
emitHighlightChange();
|
|
4619
|
+
}
|
|
4620
|
+
function getActiveCategories() {
|
|
4621
|
+
return [...activeCategories];
|
|
4622
|
+
}
|
|
4504
4623
|
function toggleLegendCategory(value) {
|
|
4505
4624
|
if (activeCategories.has(value)) activeCategories.delete(value);
|
|
4506
4625
|
else activeCategories.add(value);
|
|
@@ -4516,6 +4635,7 @@ function createGraph(container, spec, options) {
|
|
|
4516
4635
|
const { width, height } = getContainerDimensions();
|
|
4517
4636
|
const canvasHeight = Math.max(height, 200);
|
|
4518
4637
|
renderer.resize(width, canvasHeight);
|
|
4638
|
+
syncChromeInset();
|
|
4519
4639
|
if (entranceFitInFlight && interactionManager) {
|
|
4520
4640
|
cancelFlight();
|
|
4521
4641
|
entranceFitInFlight = false;
|
|
@@ -4787,6 +4907,9 @@ function createGraph(container, spec, options) {
|
|
|
4787
4907
|
},
|
|
4788
4908
|
getHighlight: () => null,
|
|
4789
4909
|
getLegend: () => ({ field: null, nodes: [], edges: [] }),
|
|
4910
|
+
setActiveCategories() {
|
|
4911
|
+
},
|
|
4912
|
+
getActiveCategories: () => [],
|
|
4790
4913
|
resize() {
|
|
4791
4914
|
},
|
|
4792
4915
|
destroy() {
|
|
@@ -4815,6 +4938,8 @@ function createGraph(container, spec, options) {
|
|
|
4815
4938
|
clearHighlight,
|
|
4816
4939
|
getHighlight,
|
|
4817
4940
|
getLegend,
|
|
4941
|
+
setActiveCategories,
|
|
4942
|
+
getActiveCategories,
|
|
4818
4943
|
resize: doResize,
|
|
4819
4944
|
destroy
|
|
4820
4945
|
};
|