@opendata-ai/openchart-engine 2.3.4 → 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 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 density = strategy.axisLabelDensity;
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, density) : continuousTicks(scales.x, density);
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, density) : continuousTicks(scales.y, density);
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 hasXAxisLabel = !!encoding.x?.axis?.label;
2922
- const xAxisHeight = isRadial ? 0 : hasXAxisLabel ? 48 : 26;
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),