@opendata-ai/openchart-vanilla 8.0.0 → 8.1.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/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-DCODCLWI.js";
7
7
  import {
8
8
  renderTileMapSVG
9
- } from "./chunk-2NAQRP66.js";
9
+ } from "./chunk-BGJT3GTB.js";
10
10
  import {
11
11
  AnimationScheduler,
12
12
  CANVAS_DEFAULT_UPDATE_MAX_MARKS,
@@ -32,7 +32,7 @@ import {
32
32
  setupAnimationCleanup,
33
33
  setupTableAnimationCleanup,
34
34
  zoom_default
35
- } from "./chunk-6BUCYCLD.js";
35
+ } from "./chunk-RQNAZZJM.js";
36
36
  import {
37
37
  embedFonts,
38
38
  ensureSVGDimensions,
@@ -50,7 +50,7 @@ import {
50
50
  registerMarkRenderer,
51
51
  renderChartSVG,
52
52
  stampAnimationVars
53
- } from "./chunk-EKAUQAAX.js";
53
+ } from "./chunk-XOZ6AM7C.js";
54
54
  import {
55
55
  injectThemeStyleBlock
56
56
  } from "./chunk-RM5XS6NR.js";
@@ -903,8 +903,9 @@ function edgeTier(edge, focus) {
903
903
  if (!focus.hasActive) return "default";
904
904
  return focus.connected.has(edge.source) && focus.connected.has(edge.target) ? "connected" : "dimmed";
905
905
  }
906
- function nodeTier(node, focus) {
906
+ function nodeTier(node, focus, exemptIds) {
907
907
  if (!focus.hasActive) return "default";
908
+ if (exemptIds?.has(node.id)) return "connected";
908
909
  return focus.connected.has(node.id) ? "connected" : "dimmed";
909
910
  }
910
911
  function edgeTierAlpha(tier, dimOpacity) {
@@ -1092,6 +1093,7 @@ var GraphCanvasRenderer = class {
1092
1093
  minRadius,
1093
1094
  nextFocus,
1094
1095
  dimOpacity,
1096
+ state.exemptIds,
1095
1097
  crossfade,
1096
1098
  state.hoverRadiusScale,
1097
1099
  entrance,
@@ -1294,16 +1296,16 @@ var GraphCanvasRenderer = class {
1294
1296
  // -------------------------------------------------------------------------
1295
1297
  // Batched node drawing
1296
1298
  // -------------------------------------------------------------------------
1297
- drawNodesBatched(ctx, nodes, hoveredNodeId, selectedNodeIds, searchMatches, showGlow, theme, minRadius, nextFocus, dimOpacity, crossfade, hoverRadiusScale, entrance, enterAlphaFor) {
1299
+ drawNodesBatched(ctx, nodes, hoveredNodeId, selectedNodeIds, searchMatches, showGlow, theme, minRadius, nextFocus, dimOpacity, exemptIds, crossfade, hoverRadiusScale, entrance, enterAlphaFor) {
1298
1300
  const focusAlpha = (node) => {
1299
1301
  if (crossfade) {
1300
1302
  return lerp(
1301
- nodeTierAlpha(nodeTier(node, crossfade.prev), dimOpacity),
1302
- nodeTierAlpha(nodeTier(node, crossfade.next), dimOpacity),
1303
+ nodeTierAlpha(nodeTier(node, crossfade.prev, exemptIds), dimOpacity),
1304
+ nodeTierAlpha(nodeTier(node, crossfade.next, exemptIds), dimOpacity),
1303
1305
  crossfade.t
1304
1306
  );
1305
1307
  }
1306
- return nodeTierAlpha(nodeTier(node, nextFocus), dimOpacity);
1308
+ return nodeTierAlpha(nodeTier(node, nextFocus, exemptIds), dimOpacity);
1307
1309
  };
1308
1310
  const searchAlpha = (node) => searchMatches !== null && !searchMatches.has(node.id) ? SEARCH_NON_MATCH_ALPHA : 1;
1309
1311
  const entranceAlpha = (node) => entrance ? entrance.nodeAlpha(node) : 1;
@@ -3417,7 +3419,11 @@ function createGraph(container, spec, options) {
3417
3419
  let cameraChangePending = false;
3418
3420
  let highlightSet = null;
3419
3421
  let highlightDimOpacity = null;
3422
+ let seedIds = /* @__PURE__ */ new Set();
3420
3423
  let activeCategories = /* @__PURE__ */ new Set();
3424
+ let transientHighlight = null;
3425
+ let transientTarget = null;
3426
+ const seenWarnings = /* @__PURE__ */ new Set();
3421
3427
  let nodeCategory = /* @__PURE__ */ new Map();
3422
3428
  let focusTransition = null;
3423
3429
  let focusAnim = null;
@@ -3449,6 +3455,12 @@ function createGraph(container, spec, options) {
3449
3455
  height: Math.max(rect.height || 400, 100)
3450
3456
  };
3451
3457
  }
3458
+ function warnOnce(message) {
3459
+ if (seenWarnings.has(message)) return;
3460
+ seenWarnings.add(message);
3461
+ if (options?.onWarn) options.onWarn(message);
3462
+ else console.warn(message);
3463
+ }
3452
3464
  function compile() {
3453
3465
  const { width, height } = getContainerDimensions();
3454
3466
  const darkMode = resolveDarkMode(options?.darkMode);
@@ -3457,7 +3469,8 @@ function createGraph(container, spec, options) {
3457
3469
  height,
3458
3470
  theme: options?.theme,
3459
3471
  darkMode,
3460
- watermark: options?.watermark
3472
+ watermark: options?.watermark,
3473
+ onWarn: warnOnce
3461
3474
  };
3462
3475
  return compileGraph(currentSpec, compileOpts);
3463
3476
  }
@@ -3559,7 +3572,7 @@ function createGraph(container, spec, options) {
3559
3572
  canvas.setAttribute("aria-label", compilation.a11y.altText);
3560
3573
  }
3561
3574
  wrapper.appendChild(canvas);
3562
- if (options?.legend !== false) {
3575
+ if (legendSetting() !== false) {
3563
3576
  legendEl = document.createElement("div");
3564
3577
  legendEl.className = "oc-graph-legend";
3565
3578
  renderLegend2();
@@ -3587,8 +3600,11 @@ function createGraph(container, spec, options) {
3587
3600
  chromeEl.style.display = "";
3588
3601
  }
3589
3602
  }
3603
+ function legendSetting() {
3604
+ return options?.legend ?? currentSpec.legend;
3605
+ }
3590
3606
  function legendConfig() {
3591
- const l = options?.legend;
3607
+ const l = legendSetting();
3592
3608
  if (l && typeof l === "object") {
3593
3609
  return { interactive: l.interactive ?? true, counts: l.counts ?? true };
3594
3610
  }
@@ -3863,6 +3879,32 @@ function createGraph(container, spec, options) {
3863
3879
  for (const [id, cat] of nodeCategory) if (activeCategories.has(cat)) set2.add(id);
3864
3880
  return set2;
3865
3881
  }
3882
+ function recomputeHighlight() {
3883
+ const filter = categoryHighlightSet();
3884
+ const transient = transientHighlight !== null && transientHighlight.size > 0 ? transientHighlight : null;
3885
+ if (filter !== null && transient !== null) {
3886
+ const inter = /* @__PURE__ */ new Set();
3887
+ for (const id of transient) if (filter.has(id)) inter.add(id);
3888
+ highlightSet = inter.size > 0 ? inter : transient;
3889
+ return;
3890
+ }
3891
+ highlightSet = filter ?? transient;
3892
+ }
3893
+ function refreshTransientHighlight() {
3894
+ if (transientTarget === null) {
3895
+ transientHighlight = null;
3896
+ return;
3897
+ }
3898
+ const nextIds = new Set(compilation.nodes.map((n) => n.id));
3899
+ const resolved = new Set(
3900
+ [...resolveHighlightTarget(transientTarget)].filter((id) => nextIds.has(id))
3901
+ );
3902
+ transientHighlight = resolved.size > 0 ? resolved : null;
3903
+ }
3904
+ function effectiveDimOpacity() {
3905
+ const custom = transientHighlight !== null ? highlightDimOpacity : null;
3906
+ return custom ?? compilation.interaction.dimOpacity;
3907
+ }
3866
3908
  function emitHighlightChange() {
3867
3909
  options?.onHighlightChange?.(highlightSet ? [...highlightSet] : null);
3868
3910
  }
@@ -3870,8 +3912,7 @@ function createGraph(container, spec, options) {
3870
3912
  const init = compilation.initialHighlight;
3871
3913
  if (!init) return;
3872
3914
  activeCategories = new Set(init.values);
3873
- highlightSet = categoryHighlightSet();
3874
- highlightDimOpacity = null;
3915
+ recomputeHighlight();
3875
3916
  }
3876
3917
  function startHoverRadiusTween(nodeId) {
3877
3918
  const hoverCfg = compilation.animation?.hover ?? null;
@@ -3955,11 +3996,12 @@ function createGraph(container, spec, options) {
3955
3996
  adjacencyMap,
3956
3997
  theme: compilation.theme,
3957
3998
  searchMatches: searchManager.getMatches(),
3999
+ exemptIds: seedIds,
3958
4000
  isGesturing,
3959
4001
  watermark: compilation.watermark,
3960
4002
  focus: focus ?? { t: 1, prev: settledNext, next: settledNext },
3961
4003
  hoverRadiusScale,
3962
- dimOpacity: highlightDimOpacity ?? compilation.interaction.dimOpacity,
4004
+ dimOpacity: effectiveDimOpacity(),
3963
4005
  entrance: entranceActive && entranceProgress < 1 ? {
3964
4006
  t: entranceProgress,
3965
4007
  stagger: entranceStagger,
@@ -4252,18 +4294,20 @@ function createGraph(container, spec, options) {
4252
4294
  return [...searchManager.getMatches() ?? []];
4253
4295
  }
4254
4296
  function highlight(target, opts) {
4255
- activeCategories = /* @__PURE__ */ new Set();
4256
- highlightSet = resolveHighlightTarget(target);
4297
+ if (destroyed) return;
4298
+ transientTarget = target;
4299
+ refreshTransientHighlight();
4257
4300
  highlightDimOpacity = opts?.dimOpacity ?? null;
4258
- syncLegendActiveState();
4301
+ recomputeHighlight();
4259
4302
  refreshFocus();
4260
4303
  emitHighlightChange();
4261
4304
  }
4262
4305
  function clearHighlight() {
4263
- activeCategories = /* @__PURE__ */ new Set();
4264
- highlightSet = null;
4306
+ if (destroyed) return;
4307
+ transientTarget = null;
4308
+ transientHighlight = null;
4265
4309
  highlightDimOpacity = null;
4266
- syncLegendActiveState();
4310
+ recomputeHighlight();
4267
4311
  refreshFocus();
4268
4312
  emitHighlightChange();
4269
4313
  }
@@ -4290,8 +4334,7 @@ function createGraph(container, spec, options) {
4290
4334
  function setActiveCategories(values) {
4291
4335
  if (destroyed) return;
4292
4336
  activeCategories = new Set(values);
4293
- highlightSet = categoryHighlightSet();
4294
- highlightDimOpacity = null;
4337
+ recomputeHighlight();
4295
4338
  syncLegendActiveState();
4296
4339
  refreshFocus();
4297
4340
  emitHighlightChange();
@@ -4302,8 +4345,7 @@ function createGraph(container, spec, options) {
4302
4345
  function toggleLegendCategory(value) {
4303
4346
  if (activeCategories.has(value)) activeCategories.delete(value);
4304
4347
  else activeCategories.add(value);
4305
- highlightSet = categoryHighlightSet();
4306
- highlightDimOpacity = null;
4348
+ recomputeHighlight();
4307
4349
  syncLegendActiveState();
4308
4350
  refreshFocus();
4309
4351
  options?.onLegendToggle?.([...activeCategories]);
@@ -4338,6 +4380,7 @@ function createGraph(container, spec, options) {
4338
4380
  const prevEdges = positionedEdges;
4339
4381
  const prevConfig = compilation.simulationConfig;
4340
4382
  compilation = compile();
4383
+ seedIds = new Set(compilation.seedNodeIds);
4341
4384
  const diff = diffGraphUpdate(
4342
4385
  prevNodes,
4343
4386
  prevEdges,
@@ -4368,9 +4411,8 @@ function createGraph(container, spec, options) {
4368
4411
  return { ...edge, sourceX: src.x, sourceY: src.y, targetX: tgt.x, targetY: tgt.y };
4369
4412
  });
4370
4413
  spatialIndex.rebuild(positionedNodes);
4371
- if (activeCategories.size > 0) {
4372
- highlightSet = categoryHighlightSet();
4373
- }
4414
+ refreshTransientHighlight();
4415
+ recomputeHighlight();
4374
4416
  reRunSearch();
4375
4417
  renderChrome3();
4376
4418
  renderLegend2();
@@ -4436,12 +4478,8 @@ function createGraph(container, spec, options) {
4436
4478
  const survivingSelection = [...selectedNodeIds].filter((id) => nextIds.has(id));
4437
4479
  selectedNodeIds = new Set(survivingSelection);
4438
4480
  interactionManager?.setSelection(survivingSelection);
4439
- if (activeCategories.size > 0) {
4440
- highlightSet = categoryHighlightSet();
4441
- } else if (highlightSet) {
4442
- highlightSet = new Set([...highlightSet].filter((id) => nextIds.has(id)));
4443
- if (highlightSet.size === 0) highlightSet = null;
4444
- }
4481
+ refreshTransientHighlight();
4482
+ recomputeHighlight();
4445
4483
  syncLegendActiveState();
4446
4484
  reRunSearch();
4447
4485
  focusTransition = null;
@@ -4550,6 +4588,7 @@ function createGraph(container, spec, options) {
4550
4588
  }
4551
4589
  try {
4552
4590
  compilation = compile();
4591
+ seedIds = new Set(compilation.seedNodeIds);
4553
4592
  adjacencyMap = buildAdjacencyMap(compilation.edges);
4554
4593
  buildDataMaps();
4555
4594
  applyInitialHighlight();