@opendata-ai/openchart-engine 2.3.5 → 2.4.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 +56 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/axes.test.ts +185 -1
- package/src/__tests__/compile-chart.test.ts +52 -0
- package/src/__tests__/dimensions.test.ts +68 -0
- package/src/layout/axes.ts +76 -5
- package/src/layout/dimensions.ts +28 -2
package/dist/index.js
CHANGED
|
@@ -2801,6 +2801,22 @@ var TICK_COUNTS = {
|
|
|
2801
2801
|
reduced: 5,
|
|
2802
2802
|
minimal: 3
|
|
2803
2803
|
};
|
|
2804
|
+
var HEIGHT_MINIMAL_THRESHOLD = 120;
|
|
2805
|
+
var HEIGHT_REDUCED_THRESHOLD = 200;
|
|
2806
|
+
var WIDTH_MINIMAL_THRESHOLD = 150;
|
|
2807
|
+
var WIDTH_REDUCED_THRESHOLD = 300;
|
|
2808
|
+
var DENSITY_ORDER = ["full", "reduced", "minimal"];
|
|
2809
|
+
function effectiveDensity(baseDensity, axisLength, minimalThreshold, reducedThreshold) {
|
|
2810
|
+
let density = baseDensity;
|
|
2811
|
+
if (axisLength < minimalThreshold) {
|
|
2812
|
+
density = "minimal";
|
|
2813
|
+
} else if (axisLength < reducedThreshold) {
|
|
2814
|
+
const baseIdx = DENSITY_ORDER.indexOf(baseDensity);
|
|
2815
|
+
const reducedIdx = DENSITY_ORDER.indexOf("reduced");
|
|
2816
|
+
density = DENSITY_ORDER[Math.max(baseIdx, reducedIdx)];
|
|
2817
|
+
}
|
|
2818
|
+
return density;
|
|
2819
|
+
}
|
|
2804
2820
|
function continuousTicks(resolvedScale, density) {
|
|
2805
2821
|
const scale = resolvedScale.scale;
|
|
2806
2822
|
const count = resolvedScale.channel.axis?.tickCount ?? TICK_COUNTS[density];
|
|
@@ -2847,7 +2863,19 @@ function formatTickLabel(value, resolvedScale) {
|
|
|
2847
2863
|
}
|
|
2848
2864
|
function computeAxes(scales, chartArea, strategy, theme) {
|
|
2849
2865
|
const result = {};
|
|
2850
|
-
const
|
|
2866
|
+
const baseDensity = strategy.axisLabelDensity;
|
|
2867
|
+
const yDensity = effectiveDensity(
|
|
2868
|
+
baseDensity,
|
|
2869
|
+
chartArea.height,
|
|
2870
|
+
HEIGHT_MINIMAL_THRESHOLD,
|
|
2871
|
+
HEIGHT_REDUCED_THRESHOLD
|
|
2872
|
+
);
|
|
2873
|
+
const xDensity = effectiveDensity(
|
|
2874
|
+
baseDensity,
|
|
2875
|
+
chartArea.width,
|
|
2876
|
+
WIDTH_MINIMAL_THRESHOLD,
|
|
2877
|
+
WIDTH_REDUCED_THRESHOLD
|
|
2878
|
+
);
|
|
2851
2879
|
const tickLabelStyle = {
|
|
2852
2880
|
fontFamily: theme.fonts.family,
|
|
2853
2881
|
fontSize: theme.fonts.sizes.axisTick,
|
|
@@ -2864,7 +2892,7 @@ function computeAxes(scales, chartArea, strategy, theme) {
|
|
|
2864
2892
|
lineHeight: 1.3
|
|
2865
2893
|
};
|
|
2866
2894
|
if (scales.x) {
|
|
2867
|
-
const ticks = scales.x.type === "band" || scales.x.type === "point" || scales.x.type === "ordinal" ? categoricalTicks(scales.x,
|
|
2895
|
+
const ticks = scales.x.type === "band" || scales.x.type === "point" || scales.x.type === "ordinal" ? categoricalTicks(scales.x, xDensity) : continuousTicks(scales.x, xDensity);
|
|
2868
2896
|
const gridlines = ticks.map((t) => ({
|
|
2869
2897
|
position: t.position,
|
|
2870
2898
|
major: true
|
|
@@ -2875,12 +2903,13 @@ function computeAxes(scales, chartArea, strategy, theme) {
|
|
|
2875
2903
|
label: scales.x.channel.axis?.label,
|
|
2876
2904
|
labelStyle: axisLabelStyle,
|
|
2877
2905
|
tickLabelStyle,
|
|
2906
|
+
tickAngle: scales.x.channel.axis?.tickAngle,
|
|
2878
2907
|
start: { x: chartArea.x, y: chartArea.y + chartArea.height },
|
|
2879
2908
|
end: { x: chartArea.x + chartArea.width, y: chartArea.y + chartArea.height }
|
|
2880
2909
|
};
|
|
2881
2910
|
}
|
|
2882
2911
|
if (scales.y) {
|
|
2883
|
-
const ticks = scales.y.type === "band" || scales.y.type === "point" || scales.y.type === "ordinal" ? categoricalTicks(scales.y,
|
|
2912
|
+
const ticks = scales.y.type === "band" || scales.y.type === "point" || scales.y.type === "ordinal" ? categoricalTicks(scales.y, yDensity) : continuousTicks(scales.y, yDensity);
|
|
2884
2913
|
const gridlines = ticks.map((t) => ({
|
|
2885
2914
|
position: t.position,
|
|
2886
2915
|
major: true
|
|
@@ -2892,6 +2921,7 @@ function computeAxes(scales, chartArea, strategy, theme) {
|
|
|
2892
2921
|
label: scales.y.channel.axis?.label,
|
|
2893
2922
|
labelStyle: axisLabelStyle,
|
|
2894
2923
|
tickLabelStyle,
|
|
2924
|
+
tickAngle: scales.y.channel.axis?.tickAngle,
|
|
2895
2925
|
start: { x: chartArea.x, y: chartArea.y },
|
|
2896
2926
|
end: { x: chartArea.x, y: chartArea.y + chartArea.height }
|
|
2897
2927
|
};
|
|
@@ -2918,8 +2948,29 @@ function computeDimensions(spec, options, legendLayout, theme) {
|
|
|
2918
2948
|
const total = { x: 0, y: 0, width, height };
|
|
2919
2949
|
const isRadial = spec.type === "pie" || spec.type === "donut";
|
|
2920
2950
|
const encoding = spec.encoding;
|
|
2921
|
-
const
|
|
2922
|
-
const
|
|
2951
|
+
const xAxis = encoding.x?.axis;
|
|
2952
|
+
const hasXAxisLabel = !!xAxis?.label;
|
|
2953
|
+
const xTickAngle = xAxis?.tickAngle;
|
|
2954
|
+
let xAxisHeight;
|
|
2955
|
+
if (isRadial) {
|
|
2956
|
+
xAxisHeight = 0;
|
|
2957
|
+
} else if (xTickAngle && Math.abs(xTickAngle) > 10) {
|
|
2958
|
+
const angleRad = Math.abs(xTickAngle) * (Math.PI / 180);
|
|
2959
|
+
const xField = encoding.x?.field;
|
|
2960
|
+
let maxLabelWidth = 40;
|
|
2961
|
+
if (xField) {
|
|
2962
|
+
for (const row of spec.data) {
|
|
2963
|
+
const label = String(row[xField] ?? "");
|
|
2964
|
+
const w = estimateTextWidth7(label, theme.fonts.sizes.axisTick, theme.fonts.weights.normal);
|
|
2965
|
+
if (w > maxLabelWidth) maxLabelWidth = w;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
const rotatedHeight = maxLabelWidth * Math.sin(angleRad) + 6;
|
|
2969
|
+
const labelHeight = Math.min(rotatedHeight, 120);
|
|
2970
|
+
xAxisHeight = hasXAxisLabel ? labelHeight + 20 : labelHeight;
|
|
2971
|
+
} else {
|
|
2972
|
+
xAxisHeight = hasXAxisLabel ? 48 : 26;
|
|
2973
|
+
}
|
|
2923
2974
|
const margins = {
|
|
2924
2975
|
top: padding + chrome.topHeight + axisMargin,
|
|
2925
2976
|
right: padding + (isRadial ? padding : axisMargin),
|