@opendata-ai/openchart-engine 6.18.0 → 6.19.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendata-ai/openchart-engine",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.19.1",
|
|
4
4
|
"description": "Headless compiler for openchart: spec validation, data compilation, scales, and layout",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Riley Hilliard",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@opendata-ai/openchart-core": "6.
|
|
48
|
+
"@opendata-ai/openchart-core": "6.19.1",
|
|
49
49
|
"d3-array": "^3.2.0",
|
|
50
50
|
"d3-format": "^3.1.2",
|
|
51
51
|
"d3-interpolate": "^3.0.0",
|
|
@@ -866,8 +866,8 @@ describe('computeAnnotations', () => {
|
|
|
866
866
|
|
|
867
867
|
// Curve should start from right edge of text, not top edge
|
|
868
868
|
// Right edge x ≈ label.x + textWidth
|
|
869
|
-
// "Curve test" = 10 chars * 12 * 0.
|
|
870
|
-
expect(connector.from.x).toBeCloseTo(label.x +
|
|
869
|
+
// "Curve test" = 10 chars * 12 * 0.57 = 68.4
|
|
870
|
+
expect(connector.from.x).toBeCloseTo(label.x + 68.4, 1);
|
|
871
871
|
});
|
|
872
872
|
});
|
|
873
873
|
|
package/src/compile.ts
CHANGED
|
@@ -240,7 +240,7 @@ export function compileChart(spec: unknown, options: CompileOptions): ChartLayou
|
|
|
240
240
|
// Responsive strategy
|
|
241
241
|
const breakpoint = getBreakpoint(options.width);
|
|
242
242
|
const heightClass = getHeightClass(options.height);
|
|
243
|
-
|
|
243
|
+
let strategy = getLayoutStrategy(breakpoint, heightClass);
|
|
244
244
|
|
|
245
245
|
// Apply breakpoint-conditional overrides from the expanded spec
|
|
246
246
|
const rawSpec = expandedSpec as Record<string, unknown>;
|
|
@@ -286,6 +286,9 @@ export function compileChart(spec: unknown, options: CompileOptions): ChartLayou
|
|
|
286
286
|
...chartSpec,
|
|
287
287
|
annotations: bp.annotations as NormalizedChartSpec['annotations'],
|
|
288
288
|
};
|
|
289
|
+
// User explicitly provided annotations at this breakpoint — override the
|
|
290
|
+
// responsive strategy so they render inline instead of being stripped.
|
|
291
|
+
strategy = { ...strategy, annotationPosition: 'inline' };
|
|
289
292
|
}
|
|
290
293
|
}
|
|
291
294
|
|
package/src/legend/compute.ts
CHANGED
|
@@ -289,9 +289,11 @@ export function computeLegend(
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
// Top/bottom-positioned legend: horizontal flow with overflow protection.
|
|
292
|
-
// Reserve space on the right so
|
|
292
|
+
// Reserve space on the right for bottom legends so they don't overlap the brand
|
|
293
|
+
// watermark. Top legends don't need this since the brand renders at the bottom.
|
|
294
|
+
const reserveBrand = watermark && resolvedPosition === 'bottom';
|
|
293
295
|
const availableWidth =
|
|
294
|
-
chartArea.width - LEGEND_PADDING * 2 - (
|
|
296
|
+
chartArea.width - LEGEND_PADDING * 2 - (reserveBrand ? BRAND_RESERVE_WIDTH : 0);
|
|
295
297
|
|
|
296
298
|
// Apply symbolLimit first if set (minimum 1), then fit remaining entries to available rows.
|
|
297
299
|
if (spec.legend?.symbolLimit != null) {
|
|
@@ -140,6 +140,8 @@ function computeNodeLabel(
|
|
|
140
140
|
theme: ResolvedTheme,
|
|
141
141
|
nodeWidth: number,
|
|
142
142
|
nodeLabelAlign: 'auto' | 'left' | 'right' = 'auto',
|
|
143
|
+
containerWidth?: number,
|
|
144
|
+
padding?: number,
|
|
143
145
|
): SankeyNodeMark['label'] {
|
|
144
146
|
const depth = node.depth ?? 0;
|
|
145
147
|
|
|
@@ -168,6 +170,20 @@ function computeNodeLabel(
|
|
|
168
170
|
const y1 = node.y1 ?? 0;
|
|
169
171
|
const midY = (y0 + y1) / 2;
|
|
170
172
|
|
|
173
|
+
// Compute maxWidth: space from label position to the container edge
|
|
174
|
+
const pad = padding ?? 0;
|
|
175
|
+
let maxWidth: number | undefined;
|
|
176
|
+
if (containerWidth !== undefined) {
|
|
177
|
+
if (placeLeft) {
|
|
178
|
+
// Label goes left from x0: available space is from left padding to x0
|
|
179
|
+
maxWidth = x0 - LABEL_GAP - pad;
|
|
180
|
+
} else {
|
|
181
|
+
// Label goes right from x1: available space is from x1 to right edge
|
|
182
|
+
maxWidth = containerWidth - pad - (x1 + LABEL_GAP);
|
|
183
|
+
}
|
|
184
|
+
if (maxWidth !== undefined && maxWidth < 0) maxWidth = 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
171
187
|
if (placeLeft) {
|
|
172
188
|
return {
|
|
173
189
|
text: node.label ?? node.id,
|
|
@@ -175,6 +191,7 @@ function computeNodeLabel(
|
|
|
175
191
|
y: midY,
|
|
176
192
|
style: { ...style, textAnchor: 'end', dominantBaseline: 'central' },
|
|
177
193
|
visible: true,
|
|
194
|
+
maxWidth,
|
|
178
195
|
};
|
|
179
196
|
}
|
|
180
197
|
|
|
@@ -184,6 +201,7 @@ function computeNodeLabel(
|
|
|
184
201
|
y: midY,
|
|
185
202
|
style: { ...style, textAnchor: 'start', dominantBaseline: 'central' },
|
|
186
203
|
visible: true,
|
|
204
|
+
maxWidth,
|
|
187
205
|
};
|
|
188
206
|
}
|
|
189
207
|
|
|
@@ -394,7 +412,15 @@ export function compileSankey(spec: unknown, options: CompileOptions): SankeyLay
|
|
|
394
412
|
height: (node.y1 ?? 0) - (node.y0 ?? 0),
|
|
395
413
|
fill,
|
|
396
414
|
cornerRadius: NODE_CORNER_RADIUS,
|
|
397
|
-
label: computeNodeLabel(
|
|
415
|
+
label: computeNodeLabel(
|
|
416
|
+
node,
|
|
417
|
+
maxDepth,
|
|
418
|
+
theme,
|
|
419
|
+
sankeySpec.nodeWidth,
|
|
420
|
+
nodeLabelAlign,
|
|
421
|
+
options.width,
|
|
422
|
+
padding,
|
|
423
|
+
),
|
|
398
424
|
nodeId: node.id,
|
|
399
425
|
value: node.value ?? 0,
|
|
400
426
|
depth,
|