@opendata-ai/openchart-vanilla 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 CHANGED
@@ -2116,6 +2116,23 @@ import { compileChart } from "@opendata-ai/openchart-engine";
2116
2116
  // src/svg-renderer.ts
2117
2117
  import { estimateTextWidth } from "@opendata-ai/openchart-core";
2118
2118
  var SVG_NS = "http://www.w3.org/2000/svg";
2119
+ function computeXAxisExtent(layout) {
2120
+ const xAxis = layout.axes.x;
2121
+ if (!xAxis) return 0;
2122
+ if (xAxis.tickAngle && Math.abs(xAxis.tickAngle) > 10) {
2123
+ const fontSize = xAxis.tickLabelStyle.fontSize;
2124
+ const fontWeight = xAxis.tickLabelStyle.fontWeight;
2125
+ const angleRad = Math.abs(xAxis.tickAngle) * (Math.PI / 180);
2126
+ let maxLabelWidth = 40;
2127
+ for (const tick of xAxis.ticks) {
2128
+ const w = estimateTextWidth(tick.label, fontSize, fontWeight);
2129
+ if (w > maxLabelWidth) maxLabelWidth = w;
2130
+ }
2131
+ const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);
2132
+ return xAxis.label ? rotatedHeight + 20 : rotatedHeight;
2133
+ }
2134
+ return xAxis.label ? 48 : 26;
2135
+ }
2119
2136
  function createSVGElement(tag) {
2120
2137
  return document.createElementNS(SVG_NS, tag);
2121
2138
  }
@@ -2160,7 +2177,7 @@ function renderChrome(parent, layout) {
2160
2177
  if (chrome.subtitle) {
2161
2178
  renderChromeElement(g, chrome.subtitle, "viz-subtitle", "subtitle");
2162
2179
  }
2163
- const xAxisExtent = layout.axes.x ? layout.axes.x.label ? 48 : 26 : 0;
2180
+ const xAxisExtent = computeXAxisExtent(layout);
2164
2181
  const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
2165
2182
  if (chrome.source) {
2166
2183
  renderChromeElement(
@@ -2209,11 +2226,23 @@ function renderAxis(parent, axis, orientation, layout) {
2209
2226
  if (orientation === "x") {
2210
2227
  const label = createSVGElement("text");
2211
2228
  label.setAttribute("class", "viz-axis-tick");
2212
- setAttrs(label, {
2213
- x: tick.position,
2214
- y: area.y + area.height + 14,
2215
- "text-anchor": "middle"
2216
- });
2229
+ if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
2230
+ const labelX = tick.position;
2231
+ const labelY = area.y + area.height + 6;
2232
+ setAttrs(label, {
2233
+ x: labelX,
2234
+ y: labelY,
2235
+ "text-anchor": axis.tickAngle < 0 ? "end" : "start",
2236
+ "dominant-baseline": "central",
2237
+ transform: `rotate(${axis.tickAngle}, ${labelX}, ${labelY})`
2238
+ });
2239
+ } else {
2240
+ setAttrs(label, {
2241
+ x: tick.position,
2242
+ y: area.y + area.height + 14,
2243
+ "text-anchor": "middle"
2244
+ });
2245
+ }
2217
2246
  applyTextStyle(label, axis.tickLabelStyle);
2218
2247
  label.textContent = tick.label;
2219
2248
  g.appendChild(label);
@@ -2263,9 +2292,24 @@ function renderAxis(parent, axis, orientation, layout) {
2263
2292
  applyTextStyle(axisLabel, axis.labelStyle);
2264
2293
  axisLabel.textContent = axis.label;
2265
2294
  if (orientation === "x") {
2295
+ let titleY = area.y + area.height + 35;
2296
+ if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
2297
+ const angleRad = Math.abs(axis.tickAngle) * (Math.PI / 180);
2298
+ let maxLabelWidth = 40;
2299
+ for (const tick of axis.ticks) {
2300
+ const w = estimateTextWidth(
2301
+ tick.label,
2302
+ axis.tickLabelStyle.fontSize,
2303
+ axis.tickLabelStyle.fontWeight
2304
+ );
2305
+ if (w > maxLabelWidth) maxLabelWidth = w;
2306
+ }
2307
+ const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);
2308
+ titleY = area.y + area.height + rotatedHeight + 14;
2309
+ }
2266
2310
  setAttrs(axisLabel, {
2267
2311
  x: area.x + area.width / 2,
2268
- y: area.y + area.height + 35,
2312
+ y: titleY,
2269
2313
  "text-anchor": "middle"
2270
2314
  });
2271
2315
  } else {
@@ -2749,7 +2793,7 @@ function brandPosition(layout) {
2749
2793
  const padding = layout.theme.spacing.padding;
2750
2794
  const rightEdge = width - padding;
2751
2795
  const { chrome } = layout;
2752
- const xAxisExtent = layout.axes.x ? layout.axes.x.label ? 48 : 26 : 0;
2796
+ const xAxisExtent = computeXAxisExtent(layout);
2753
2797
  const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
2754
2798
  const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
2755
2799
  const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter;