@opendata-ai/openchart-engine 6.1.5 → 6.2.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
@@ -6555,20 +6555,24 @@ function resolveNodeVisuals(nodes, encoding, edges, theme, nodeOverrides) {
6555
6555
  if (encoding.nodeColor?.field) {
6556
6556
  const field = encoding.nodeColor.field;
6557
6557
  const fieldType = encoding.nodeColor.type ?? "nominal";
6558
+ const scaleConfig = encoding.nodeColor.scale;
6558
6559
  if (fieldType === "quantitative") {
6559
6560
  const values = nodes.map((n) => Number(n[field])).filter((v) => Number.isFinite(v));
6560
6561
  const colorMin = min2(values) ?? 0;
6561
6562
  const colorMax = max2(values) ?? 1;
6562
6563
  const seqPalettes = Object.values(theme.colors.sequential);
6563
6564
  const palette = seqPalettes.length > 0 ? seqPalettes[0] : ["#ccc", "#333"];
6564
- const colorScale = linear2().domain([colorMin, colorMax]).range([palette[0], palette[palette.length - 1]]);
6565
+ const domain = scaleConfig?.domain && scaleConfig.domain.length === 2 ? scaleConfig.domain : [colorMin, colorMax];
6566
+ const range2 = scaleConfig?.range && scaleConfig.range.length >= 2 ? scaleConfig.range : [palette[0], palette[palette.length - 1]];
6567
+ const colorScale = linear2().domain(domain).range(range2);
6565
6568
  colorFn = (node) => {
6566
6569
  const val = Number(node[field]);
6567
6570
  return Number.isFinite(val) ? colorScale(val) : theme.colors.categorical[0];
6568
6571
  };
6569
6572
  } else {
6570
- const uniqueValues = [...new Set(nodes.map((n) => String(n[field] ?? "")))];
6571
- const ordinalScale = ordinal().domain(uniqueValues).range(theme.colors.categorical);
6573
+ const domain = scaleConfig?.domain && Array.isArray(scaleConfig.domain) ? scaleConfig.domain : [...new Set(nodes.map((n) => String(n[field] ?? "")))];
6574
+ const range2 = scaleConfig?.range && scaleConfig.range.length > 0 ? scaleConfig.range : theme.colors.categorical;
6575
+ const ordinalScale = ordinal().domain(domain).range(range2);
6572
6576
  colorFn = (node) => ordinalScale(String(node[field] ?? ""));
6573
6577
  }
6574
6578
  }
@@ -6626,20 +6630,24 @@ function resolveEdgeVisuals(edges, encoding, theme) {
6626
6630
  if (encoding.edgeColor?.field) {
6627
6631
  const field = encoding.edgeColor.field;
6628
6632
  const fieldType = encoding.edgeColor.type ?? "nominal";
6633
+ const scaleConfig = encoding.edgeColor.scale;
6629
6634
  if (fieldType === "quantitative") {
6630
6635
  const values = edges.map((e) => Number(e[field])).filter((v) => Number.isFinite(v));
6631
6636
  const colorMin = min2(values) ?? 0;
6632
6637
  const colorMax = max2(values) ?? 1;
6633
6638
  const seqPalettes = Object.values(theme.colors.sequential);
6634
6639
  const palette = seqPalettes.length > 0 ? seqPalettes[0] : ["#ccc", "#333"];
6635
- const colorScale = linear2().domain([colorMin, colorMax]).range([palette[0], palette[palette.length - 1]]);
6640
+ const domain = scaleConfig?.domain && scaleConfig.domain.length === 2 ? scaleConfig.domain : [colorMin, colorMax];
6641
+ const range2 = scaleConfig?.range && scaleConfig.range.length >= 2 ? scaleConfig.range : [palette[0], palette[palette.length - 1]];
6642
+ const colorScale = linear2().domain(domain).range(range2);
6636
6643
  edgeColorFn = (edge) => {
6637
6644
  const val = Number(edge[field]);
6638
6645
  return Number.isFinite(val) ? colorScale(val) : hexWithOpacity(theme.colors.axis, 0.4);
6639
6646
  };
6640
6647
  } else {
6641
- const uniqueValues = [...new Set(edges.map((e) => String(e[field] ?? "")))];
6642
- const ordinalScale = ordinal().domain(uniqueValues).range(theme.colors.categorical);
6648
+ const domain = scaleConfig?.domain && Array.isArray(scaleConfig.domain) ? scaleConfig.domain : [...new Set(edges.map((e) => String(e[field] ?? "")))];
6649
+ const range2 = scaleConfig?.range && scaleConfig.range.length > 0 ? scaleConfig.range : theme.colors.categorical;
6650
+ const ordinalScale = ordinal().domain(domain).range(range2);
6643
6651
  edgeColorFn = (edge) => ordinalScale(String(edge[field] ?? ""));
6644
6652
  }
6645
6653
  }
@@ -6713,7 +6721,7 @@ function applyCommunityColors(nodes, colorMap) {
6713
6721
  var SWATCH_SIZE = 12;
6714
6722
  var SWATCH_GAP = 6;
6715
6723
  var ENTRY_GAP = 16;
6716
- function buildGraphLegend(nodes, communityColorMap, hasCommunities, theme) {
6724
+ function buildGraphLegend(nodes, communityColorMap, hasCommunities, theme, nodeColorField) {
6717
6725
  const labelStyle = {
6718
6726
  fontFamily: theme.fonts.family,
6719
6727
  fontSize: theme.fonts.sizes.small,
@@ -6730,16 +6738,17 @@ function buildGraphLegend(nodes, communityColorMap, hasCommunities, theme) {
6730
6738
  active: true
6731
6739
  }));
6732
6740
  } else {
6733
- const colorLabels = /* @__PURE__ */ new Map();
6741
+ const categoryColors = /* @__PURE__ */ new Map();
6734
6742
  for (const node of nodes) {
6735
- if (!colorLabels.has(node.fill)) {
6736
- colorLabels.set(node.fill, node.label ?? node.id);
6743
+ const category = nodeColorField ? String(node.data[nodeColorField] ?? node.label ?? node.id) : node.label ?? node.id;
6744
+ if (!categoryColors.has(category)) {
6745
+ categoryColors.set(category, node.fill);
6737
6746
  }
6738
6747
  }
6739
- if (colorLabels.size <= 1) {
6748
+ if (categoryColors.size <= 1) {
6740
6749
  entries = [];
6741
6750
  } else {
6742
- entries = [...colorLabels.entries()].map(([color2, label]) => ({
6751
+ entries = [...categoryColors.entries()].map(([label, color2]) => ({
6743
6752
  label,
6744
6753
  color: color2,
6745
6754
  shape: "circle",
@@ -6806,13 +6815,21 @@ function compileGraph(spec, options) {
6806
6815
  const clusteringField = graphSpec.layout.clustering?.field;
6807
6816
  const hasCommunities = !!clusteringField;
6808
6817
  assignCommunities(compiledNodes, clusteringField);
6818
+ const hasNodeColorEncoding = !!graphSpec.encoding.nodeColor?.field;
6809
6819
  let communityColorMap = /* @__PURE__ */ new Map();
6810
- if (hasCommunities) {
6820
+ if (hasCommunities && !hasNodeColorEncoding) {
6811
6821
  communityColorMap = buildCommunityColorMap(compiledNodes, theme);
6812
6822
  applyCommunityColors(compiledNodes, communityColorMap);
6813
6823
  }
6814
6824
  const compiledEdges = resolveEdgeVisuals(graphSpec.edges, graphSpec.encoding, theme);
6815
- const legend = buildGraphLegend(compiledNodes, communityColorMap, hasCommunities, theme);
6825
+ const useCommunitiesForLegend = hasCommunities && !hasNodeColorEncoding;
6826
+ const legend = buildGraphLegend(
6827
+ compiledNodes,
6828
+ communityColorMap,
6829
+ useCommunitiesForLegend,
6830
+ theme,
6831
+ graphSpec.encoding.nodeColor?.field
6832
+ );
6816
6833
  const tooltipDescriptors = buildGraphTooltips(compiledNodes);
6817
6834
  const communityCount = communityColorMap.size;
6818
6835
  const altParts = [