@opendata-ai/openchart-engine 6.1.4 → 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 +37 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/graphs/__tests__/compile-graph.test.ts +56 -0
- package/src/graphs/compile-graph.ts +28 -13
- package/src/graphs/encoding.ts +44 -14
- package/src/layout/axes.ts +3 -1
- package/src/tooltips/compute.ts +3 -1
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
|
|
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
|
|
6571
|
-
const
|
|
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
|
|
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
|
|
6642
|
-
const
|
|
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
|
|
6741
|
+
const categoryColors = /* @__PURE__ */ new Map();
|
|
6734
6742
|
for (const node of nodes) {
|
|
6735
|
-
|
|
6736
|
-
|
|
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 (
|
|
6748
|
+
if (categoryColors.size <= 1) {
|
|
6740
6749
|
entries = [];
|
|
6741
6750
|
} else {
|
|
6742
|
-
entries = [...
|
|
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
|
|
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 = [
|
|
@@ -6873,6 +6890,7 @@ var DEFAULT_COLLISION_PADDING = 5;
|
|
|
6873
6890
|
import {
|
|
6874
6891
|
abbreviateNumber as abbreviateNumber3,
|
|
6875
6892
|
buildD3Formatter as buildD3Formatter3,
|
|
6893
|
+
buildTemporalFormatter,
|
|
6876
6894
|
estimateTextWidth as estimateTextWidth7,
|
|
6877
6895
|
formatDate,
|
|
6878
6896
|
formatNumber as formatNumber3
|
|
@@ -6984,7 +7002,8 @@ var TEMPORAL_SCALE_TYPES = /* @__PURE__ */ new Set(["time", "utc"]);
|
|
|
6984
7002
|
function formatTickLabel(value, resolvedScale) {
|
|
6985
7003
|
const formatStr = resolvedScale.channel.axis?.format;
|
|
6986
7004
|
if (TEMPORAL_SCALE_TYPES.has(resolvedScale.type)) {
|
|
6987
|
-
|
|
7005
|
+
const temporalFmt = buildTemporalFormatter(formatStr);
|
|
7006
|
+
if (temporalFmt) return temporalFmt(value);
|
|
6988
7007
|
return formatDate(value);
|
|
6989
7008
|
}
|
|
6990
7009
|
if (NUMERIC_SCALE_TYPES.has(resolvedScale.type)) {
|
|
@@ -8550,10 +8569,12 @@ function compileTableLayout(spec, options, theme) {
|
|
|
8550
8569
|
}
|
|
8551
8570
|
|
|
8552
8571
|
// src/tooltips/compute.ts
|
|
8553
|
-
import { formatDate as formatDate3, formatNumber as formatNumber5 } from "@opendata-ai/openchart-core";
|
|
8572
|
+
import { buildTemporalFormatter as buildTemporalFormatter2, formatDate as formatDate3, formatNumber as formatNumber5 } from "@opendata-ai/openchart-core";
|
|
8554
8573
|
function formatValue(value, fieldType, format2) {
|
|
8555
8574
|
if (value == null) return "";
|
|
8556
8575
|
if (fieldType === "temporal" || value instanceof Date) {
|
|
8576
|
+
const temporalFmt = buildTemporalFormatter2(format2);
|
|
8577
|
+
if (temporalFmt) return temporalFmt(value);
|
|
8557
8578
|
return formatDate3(value);
|
|
8558
8579
|
}
|
|
8559
8580
|
if (typeof value === "number") {
|