@opendata-ai/openchart-engine 6.8.0 → 6.9.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.d.ts +1 -0
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compiler/normalize.ts +1 -0
- package/src/sankey/compile-sankey.ts +22 -12
- package/src/sankey/types.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendata-ai/openchart-engine",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.9.0",
|
|
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.9.0",
|
|
49
49
|
"d3-array": "^3.2.0",
|
|
50
50
|
"d3-format": "^3.1.2",
|
|
51
51
|
"d3-interpolate": "^3.0.0",
|
|
@@ -246,6 +246,7 @@ function normalizeSankeySpec(spec: SankeySpec, _warnings: string[]): NormalizedS
|
|
|
246
246
|
nodeAlign: spec.nodeAlign ?? 'justify',
|
|
247
247
|
iterations: spec.iterations ?? 6,
|
|
248
248
|
linkStyle: spec.linkStyle ?? 'gradient',
|
|
249
|
+
nodeLabelAlign: spec.nodeLabelAlign ?? 'auto',
|
|
249
250
|
chrome: normalizeChrome(spec.chrome),
|
|
250
251
|
legend: spec.legend,
|
|
251
252
|
theme: spec.theme ?? {},
|
|
@@ -131,18 +131,28 @@ function getLinkColors(
|
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
133
|
* Determine label position for a node based on its column depth.
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* Middle columns: label to the right (default).
|
|
134
|
+
* Default ('auto'): leftmost/middle columns label right, rightmost column labels left.
|
|
135
|
+
* 'right': all labels to the right. 'left': all labels to the left.
|
|
137
136
|
*/
|
|
138
137
|
function computeNodeLabel(
|
|
139
138
|
node: ComputedNode,
|
|
140
139
|
maxDepth: number,
|
|
141
140
|
theme: ResolvedTheme,
|
|
142
141
|
nodeWidth: number,
|
|
142
|
+
nodeLabelAlign: 'auto' | 'left' | 'right' = 'auto',
|
|
143
143
|
): SankeyNodeMark['label'] {
|
|
144
144
|
const depth = node.depth ?? 0;
|
|
145
|
-
|
|
145
|
+
|
|
146
|
+
// Determine which side to place the label
|
|
147
|
+
let placeLeft: boolean;
|
|
148
|
+
if (nodeLabelAlign === 'left') {
|
|
149
|
+
placeLeft = true;
|
|
150
|
+
} else if (nodeLabelAlign === 'right') {
|
|
151
|
+
placeLeft = false;
|
|
152
|
+
} else {
|
|
153
|
+
// 'auto': rightmost column goes left, everything else goes right
|
|
154
|
+
placeLeft = depth === maxDepth;
|
|
155
|
+
}
|
|
146
156
|
|
|
147
157
|
const style: TextStyle = {
|
|
148
158
|
fontFamily: theme.fonts.family,
|
|
@@ -158,8 +168,7 @@ function computeNodeLabel(
|
|
|
158
168
|
const y1 = node.y1 ?? 0;
|
|
159
169
|
const midY = (y0 + y1) / 2;
|
|
160
170
|
|
|
161
|
-
if (
|
|
162
|
-
// Label to the left of the node
|
|
171
|
+
if (placeLeft) {
|
|
163
172
|
return {
|
|
164
173
|
text: node.label ?? node.id,
|
|
165
174
|
x: x0 - LABEL_GAP,
|
|
@@ -169,7 +178,6 @@ function computeNodeLabel(
|
|
|
169
178
|
};
|
|
170
179
|
}
|
|
171
180
|
|
|
172
|
-
// Label to the right of the node (leftmost and middle columns)
|
|
173
181
|
return {
|
|
174
182
|
text: node.label ?? node.id,
|
|
175
183
|
x: x1 + LABEL_GAP,
|
|
@@ -311,15 +319,17 @@ export function compileSankey(spec: unknown, options: CompileOptions): SankeyLay
|
|
|
311
319
|
sankeySpec.iterations,
|
|
312
320
|
);
|
|
313
321
|
|
|
314
|
-
// 6b. Check if any
|
|
315
|
-
|
|
316
|
-
// which can extend past the drawing area boundary.
|
|
322
|
+
// 6b. Check if any right-side node labels overflow the right edge.
|
|
323
|
+
const nodeLabelAlign = sankeySpec.nodeLabelAlign ?? 'auto';
|
|
317
324
|
const maxDepthFirst = nodes.reduce((max, n) => Math.max(max, n.depth ?? 0), 0);
|
|
318
325
|
const rightEdge = area.x + area.width;
|
|
319
326
|
let maxOverflow = 0;
|
|
320
327
|
for (const node of nodes) {
|
|
321
328
|
const depth = node.depth ?? 0;
|
|
322
|
-
|
|
329
|
+
// Skip nodes whose labels go left (they can't overflow the right edge)
|
|
330
|
+
const labelsLeft =
|
|
331
|
+
nodeLabelAlign === 'left' || (nodeLabelAlign === 'auto' && depth === maxDepthFirst);
|
|
332
|
+
if (labelsLeft) continue;
|
|
323
333
|
const labelX = (node.x1 ?? nodeWidth) + LABEL_GAP;
|
|
324
334
|
const labelText = node.label ?? node.id;
|
|
325
335
|
const labelWidth = estimateTextWidth(labelText, labelFontSize, labelFontWeight);
|
|
@@ -375,7 +385,7 @@ export function compileSankey(spec: unknown, options: CompileOptions): SankeyLay
|
|
|
375
385
|
height: (node.y1 ?? 0) - (node.y0 ?? 0),
|
|
376
386
|
fill,
|
|
377
387
|
cornerRadius: NODE_CORNER_RADIUS,
|
|
378
|
-
label: computeNodeLabel(node, maxDepth, theme, sankeySpec.nodeWidth),
|
|
388
|
+
label: computeNodeLabel(node, maxDepth, theme, sankeySpec.nodeWidth, nodeLabelAlign),
|
|
379
389
|
nodeId: node.id,
|
|
380
390
|
value: node.value ?? 0,
|
|
381
391
|
depth,
|
package/src/sankey/types.ts
CHANGED