@opendata-ai/openchart-engine 8.0.0-rc.8 → 8.0.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 +149 -12
- package/dist/index.js +894 -171
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/__test-fixtures__/specs.ts +0 -182
- package/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap +0 -2339
- package/src/__tests__/annotation-behavior.test.ts +0 -852
- package/src/__tests__/axes.test.ts +0 -1579
- package/src/__tests__/compile-animation.test.ts +0 -161
- package/src/__tests__/compile-chart.test.ts +0 -1303
- package/src/__tests__/compile-footnotes.test.ts +0 -277
- package/src/__tests__/compile-layer.test.ts +0 -969
- package/src/__tests__/compile-snapshot.test.ts +0 -156
- package/src/__tests__/compound-labels.test.ts +0 -147
- package/src/__tests__/dimensions.test.ts +0 -918
- package/src/__tests__/encoding-sugar.test.ts +0 -390
- package/src/__tests__/facet.test.ts +0 -215
- package/src/__tests__/helpers/invariants.ts +0 -299
- package/src/__tests__/known-bugs.test.ts +0 -566
- package/src/__tests__/layout-fuzz.test.ts +0 -358
- package/src/__tests__/layout-invariants.test.ts +0 -535
- package/src/__tests__/legend.test.ts +0 -721
- package/src/__tests__/mark-keys.test.ts +0 -294
- package/src/__tests__/ranking-recipes.test.ts +0 -260
- package/src/__tests__/rotation.test.ts +0 -83
- package/src/__tests__/scales.test.ts +0 -323
- package/src/__tests__/series-search.test.ts +0 -92
- package/src/__tests__/spec-sugar.test.ts +0 -801
- package/src/__tests__/you-draw-it.test.ts +0 -160
- package/src/annotations/__tests__/compute.test.ts +0 -1672
- package/src/annotations/__tests__/geometry.test.ts +0 -66
- package/src/annotations/__tests__/placement-fuzz.test.ts +0 -256
- package/src/annotations/__tests__/placement-invariants.test.ts +0 -345
- package/src/annotations/__tests__/placement.test.ts +0 -316
- package/src/annotations/__tests__/resolve-text.test.ts +0 -788
- package/src/annotations/__tests__/rich-text.test.ts +0 -91
- package/src/annotations/__tests__/thinning.test.ts +0 -285
- package/src/annotations/collisions.ts +0 -326
- package/src/annotations/compute.ts +0 -333
- package/src/annotations/constants.ts +0 -162
- package/src/annotations/geometry.ts +0 -376
- package/src/annotations/move.ts +0 -135
- package/src/annotations/placement.ts +0 -636
- package/src/annotations/position.ts +0 -131
- package/src/annotations/resolve-range.ts +0 -117
- package/src/annotations/resolve-refline.ts +0 -157
- package/src/annotations/resolve-text.ts +0 -545
- package/src/annotations/rich-text.ts +0 -65
- package/src/annotations/thinning.ts +0 -190
- package/src/barlist/__tests__/compile-barlist.test.ts +0 -200
- package/src/barlist/compile-barlist.ts +0 -391
- package/src/barlist/types.ts +0 -28
- package/src/charts/__tests__/post-process.test.ts +0 -237
- package/src/charts/__tests__/registry.test.ts +0 -116
- package/src/charts/__tests__/utils.test.ts +0 -195
- package/src/charts/_shared/__tests__/density-filter.test.ts +0 -32
- package/src/charts/_shared/density-filter.ts +0 -26
- package/src/charts/_shared/format-label-value.ts +0 -16
- package/src/charts/bar/__tests__/compute.test.ts +0 -934
- package/src/charts/bar/__tests__/gradient-orient.test.ts +0 -127
- package/src/charts/bar/__tests__/labels.test.ts +0 -292
- package/src/charts/bar/compute.ts +0 -539
- package/src/charts/bar/index.ts +0 -60
- package/src/charts/bar/labels.ts +0 -311
- package/src/charts/beeswarm/__tests__/compute.test.ts +0 -280
- package/src/charts/beeswarm/__tests__/dodge.test.ts +0 -148
- package/src/charts/beeswarm/compute.ts +0 -203
- package/src/charts/beeswarm/dodge.ts +0 -155
- package/src/charts/beeswarm/index.ts +0 -32
- package/src/charts/builtin.ts +0 -75
- package/src/charts/calendar/__tests__/compute.test.ts +0 -417
- package/src/charts/calendar/compute.ts +0 -319
- package/src/charts/calendar/index.ts +0 -32
- package/src/charts/column/__tests__/compute.test.ts +0 -540
- package/src/charts/column/__tests__/labels.test.ts +0 -179
- package/src/charts/column/compute.ts +0 -493
- package/src/charts/column/index.ts +0 -53
- package/src/charts/column/labels.ts +0 -229
- package/src/charts/dot/__tests__/compute.test.ts +0 -380
- package/src/charts/dot/__tests__/labels.test.ts +0 -98
- package/src/charts/dot/compute.ts +0 -285
- package/src/charts/dot/index.ts +0 -62
- package/src/charts/dot/labels.ts +0 -121
- package/src/charts/line/__tests__/compute.test.ts +0 -1194
- package/src/charts/line/__tests__/curves.test.ts +0 -51
- package/src/charts/line/__tests__/labels.test.ts +0 -94
- package/src/charts/line/__tests__/mark-options.test.ts +0 -175
- package/src/charts/line/area.ts +0 -484
- package/src/charts/line/compute.ts +0 -356
- package/src/charts/line/curves.ts +0 -85
- package/src/charts/line/index.ts +0 -176
- package/src/charts/line/labels.ts +0 -177
- package/src/charts/parliament/__tests__/compute.test.ts +0 -411
- package/src/charts/parliament/compute.ts +0 -313
- package/src/charts/parliament/index.ts +0 -32
- package/src/charts/pie/__tests__/compute.test.ts +0 -384
- package/src/charts/pie/__tests__/labels.test.ts +0 -132
- package/src/charts/pie/compute.ts +0 -324
- package/src/charts/pie/index.ts +0 -49
- package/src/charts/pie/labels.ts +0 -143
- package/src/charts/post-process.ts +0 -229
- package/src/charts/range/__tests__/compute.test.ts +0 -486
- package/src/charts/range/compute.ts +0 -273
- package/src/charts/range/index.ts +0 -82
- package/src/charts/range/labels.ts +0 -169
- package/src/charts/rect/__tests__/compute.test.ts +0 -120
- package/src/charts/rect/index.ts +0 -127
- package/src/charts/registry.ts +0 -72
- package/src/charts/rule/index.ts +0 -139
- package/src/charts/scatter/__tests__/compute.test.ts +0 -488
- package/src/charts/scatter/__tests__/trendline.test.ts +0 -191
- package/src/charts/scatter/compute.ts +0 -192
- package/src/charts/scatter/index.ts +0 -46
- package/src/charts/scatter/trendline.ts +0 -100
- package/src/charts/text/__tests__/compute.test.ts +0 -307
- package/src/charts/text/index.ts +0 -141
- package/src/charts/tick/__tests__/compute.test.ts +0 -75
- package/src/charts/tick/index.ts +0 -99
- package/src/charts/utils.ts +0 -170
- package/src/charts/waffle/__tests__/compute.test.ts +0 -305
- package/src/charts/waffle/compute.ts +0 -197
- package/src/charts/waffle/index.ts +0 -31
- package/src/compile/__tests__/color-scale-range.test.ts +0 -158
- package/src/compile/__tests__/color-scale-reverse.test.ts +0 -89
- package/src/compile/__tests__/data-clip.test.ts +0 -127
- package/src/compile/__tests__/fill-patterns.test.ts +0 -154
- package/src/compile/__tests__/size-scale.test.ts +0 -108
- package/src/compile/__tests__/watermark-obstacle.test.ts +0 -93
- package/src/compile/color-scale-range.ts +0 -101
- package/src/compile/data-clip.ts +0 -48
- package/src/compile/fill-patterns.ts +0 -74
- package/src/compile/layer.ts +0 -626
- package/src/compile/size-scale.ts +0 -141
- package/src/compile/spec-sugar.ts +0 -758
- package/src/compile/watermark-obstacle.ts +0 -54
- package/src/compile/you-draw-it.ts +0 -123
- package/src/compile.ts +0 -1450
- package/src/compiler/__tests__/a11y-warnings.test.ts +0 -148
- package/src/compiler/__tests__/animation.test.ts +0 -177
- package/src/compiler/__tests__/compile.test.ts +0 -87
- package/src/compiler/__tests__/fixtures/vl-parity/axis-null.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/color-value.json +0 -20
- package/src/compiler/__tests__/fixtures/vl-parity/count-aggregate.json +0 -22
- package/src/compiler/__tests__/fixtures/vl-parity/data-values.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/layer-axis-null.json +0 -14
- package/src/compiler/__tests__/fixtures/vl-parity/layer-count.json +0 -34
- package/src/compiler/__tests__/fixtures/vl-parity/layer-data-values.json +0 -22
- package/src/compiler/__tests__/fixtures/vl-parity/layer-legend-null.json +0 -41
- package/src/compiler/__tests__/fixtures/vl-parity/layer-scheme.json +0 -38
- package/src/compiler/__tests__/fixtures/vl-parity/layer-sort.json +0 -28
- package/src/compiler/__tests__/fixtures/vl-parity/layer-theta.json +0 -28
- package/src/compiler/__tests__/fixtures/vl-parity/layer-title.json +0 -30
- package/src/compiler/__tests__/fixtures/vl-parity/layer-value-def.json +0 -29
- package/src/compiler/__tests__/fixtures/vl-parity/legend-null.json +0 -32
- package/src/compiler/__tests__/fixtures/vl-parity/scheme.json +0 -25
- package/src/compiler/__tests__/fixtures/vl-parity/sort-by-value.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/theta-arc.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/title-subtitle.json +0 -21
- package/src/compiler/__tests__/fixtures/vl-parity/width-height.json +0 -23
- package/src/compiler/__tests__/keys.test.ts +0 -83
- package/src/compiler/__tests__/normalize-highlight.test.ts +0 -103
- package/src/compiler/__tests__/normalize.test.ts +0 -399
- package/src/compiler/__tests__/sparkline-defaults.test.ts +0 -153
- package/src/compiler/__tests__/validate-did-you-mean.test.ts +0 -170
- package/src/compiler/__tests__/validate-highlight.test.ts +0 -112
- package/src/compiler/__tests__/validate-near-miss.test.ts +0 -251
- package/src/compiler/__tests__/validate.test.ts +0 -629
- package/src/compiler/__tests__/vl-parity.test.ts +0 -68
- package/src/compiler/a11y-warnings.ts +0 -110
- package/src/compiler/animation.ts +0 -142
- package/src/compiler/index.ts +0 -47
- package/src/compiler/keys.ts +0 -55
- package/src/compiler/normalize.ts +0 -734
- package/src/compiler/sparkline-defaults.ts +0 -138
- package/src/compiler/types.ts +0 -241
- package/src/compiler/validate.ts +0 -2041
- package/src/endpoint-labels/__tests__/compute.test.ts +0 -587
- package/src/endpoint-labels/compute.ts +0 -621
- package/src/endpoint-labels/constants.ts +0 -54
- package/src/endpoint-labels/format.ts +0 -60
- package/src/endpoint-labels/predict.ts +0 -139
- package/src/format/__tests__/field-format.test.ts +0 -164
- package/src/format/field-format.ts +0 -27
- package/src/geo/__tests__/compile-map.test.ts +0 -1004
- package/src/geo/compile-map.ts +0 -1341
- package/src/geo/join.ts +0 -115
- package/src/geo/projections.ts +0 -45
- package/src/geo/types.ts +0 -26
- package/src/graphs/__tests__/community.test.ts +0 -228
- package/src/graphs/__tests__/compile-graph.test.ts +0 -396
- package/src/graphs/__tests__/encoding.test.ts +0 -415
- package/src/graphs/community.ts +0 -92
- package/src/graphs/compile-graph.ts +0 -329
- package/src/graphs/encoding.ts +0 -356
- package/src/graphs/types.ts +0 -106
- package/src/index.ts +0 -177
- package/src/layout/__tests__/metrics.test.ts +0 -97
- package/src/layout/__tests__/scales-highlight.test.ts +0 -192
- package/src/layout/axes/rotation.ts +0 -152
- package/src/layout/axes/thinning.ts +0 -98
- package/src/layout/axes/ticks.ts +0 -409
- package/src/layout/axes.ts +0 -757
- package/src/layout/dimensions.ts +0 -912
- package/src/layout/facet.ts +0 -91
- package/src/layout/gridlines.ts +0 -84
- package/src/layout/metrics.ts +0 -132
- package/src/layout/plan.ts +0 -631
- package/src/layout/scales.ts +0 -1053
- package/src/layout/shared.ts +0 -44
- package/src/legend/__tests__/continuous.test.ts +0 -318
- package/src/legend/__tests__/redundancy.test.ts +0 -108
- package/src/legend/__tests__/size.test.ts +0 -348
- package/src/legend/__tests__/suppression.test.ts +0 -382
- package/src/legend/compute.ts +0 -747
- package/src/legend/continuous.ts +0 -327
- package/src/legend/size.ts +0 -407
- package/src/legend/suppression.ts +0 -226
- package/src/legend/wrap.ts +0 -117
- package/src/sankey/__tests__/compile-sankey.test.ts +0 -458
- package/src/sankey/__tests__/layout.test.ts +0 -165
- package/src/sankey/__tests__/node-label-wrap.test.ts +0 -114
- package/src/sankey/__tests__/node-sort.test.ts +0 -45
- package/src/sankey/compile-sankey.ts +0 -706
- package/src/sankey/layout.ts +0 -184
- package/src/sankey/types.ts +0 -41
- package/src/tables/__tests__/bar-column.test.ts +0 -147
- package/src/tables/__tests__/category-colors.test.ts +0 -230
- package/src/tables/__tests__/compile-table.test.ts +0 -236
- package/src/tables/__tests__/format-cells.test.ts +0 -143
- package/src/tables/__tests__/heatmap.test.ts +0 -148
- package/src/tables/__tests__/pagination.test.ts +0 -78
- package/src/tables/__tests__/search.test.ts +0 -94
- package/src/tables/__tests__/sort.test.ts +0 -107
- package/src/tables/__tests__/sparkline.test.ts +0 -122
- package/src/tables/bar-column.ts +0 -94
- package/src/tables/category-colors.ts +0 -78
- package/src/tables/compile-table.ts +0 -427
- package/src/tables/format-cells.ts +0 -115
- package/src/tables/heatmap.ts +0 -141
- package/src/tables/pagination.ts +0 -46
- package/src/tables/search.ts +0 -66
- package/src/tables/sort.ts +0 -69
- package/src/tables/sparkline.ts +0 -113
- package/src/tables/utils.ts +0 -16
- package/src/tilemap/__tests__/compile-tilemap.test.ts +0 -576
- package/src/tilemap/compile-tilemap.ts +0 -755
- package/src/tilemap/layout.ts +0 -172
- package/src/tilemap/types.ts +0 -33
- package/src/tooltips/__tests__/compute.test.ts +0 -582
- package/src/tooltips/compute.ts +0 -700
- package/src/transforms/__tests__/aggregate.test.ts +0 -159
- package/src/transforms/__tests__/bin.test.ts +0 -88
- package/src/transforms/__tests__/calculate.test.ts +0 -146
- package/src/transforms/__tests__/conditional.test.ts +0 -127
- package/src/transforms/__tests__/filter-relative.test.ts +0 -202
- package/src/transforms/__tests__/filter.test.ts +0 -59
- package/src/transforms/__tests__/fold.test.ts +0 -79
- package/src/transforms/__tests__/index.test.ts +0 -93
- package/src/transforms/__tests__/predicates.test.ts +0 -193
- package/src/transforms/__tests__/timeunit.test.ts +0 -129
- package/src/transforms/__tests__/window.test.ts +0 -286
- package/src/transforms/aggregate.ts +0 -131
- package/src/transforms/bin.ts +0 -87
- package/src/transforms/calculate.ts +0 -60
- package/src/transforms/conditional.ts +0 -46
- package/src/transforms/filter.ts +0 -122
- package/src/transforms/fold.ts +0 -49
- package/src/transforms/index.ts +0 -60
- package/src/transforms/predicates.ts +0 -123
- package/src/transforms/timeunit.ts +0 -88
- package/src/transforms/window.ts +0 -185
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Data-coordinate to pixel-coordinate resolution for annotations.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ScaleBand, ScaleLinear, ScalePoint, ScaleTime } from 'd3-scale';
|
|
6
|
-
import type { ResolvedScales } from '../layout/scales';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Interpolate a numeric value between sorted domain entries.
|
|
10
|
-
* Used when an annotation references a value not present in a categorical domain
|
|
11
|
-
* (e.g. "2008" on an axis with data points at "2007" and "2009").
|
|
12
|
-
* Returns null if domain values aren't numeric or the domain is too small.
|
|
13
|
-
*/
|
|
14
|
-
export function interpolateInDomain(
|
|
15
|
-
numValue: number,
|
|
16
|
-
domain: string[],
|
|
17
|
-
positionOf: (entry: string) => number,
|
|
18
|
-
): number | null {
|
|
19
|
-
if (domain.length < 2) return null;
|
|
20
|
-
const nums = domain.map(Number);
|
|
21
|
-
if (!nums.every(Number.isFinite)) return null;
|
|
22
|
-
|
|
23
|
-
// Sort by numeric value so bracket-finding works regardless of data order
|
|
24
|
-
const sorted = nums.map((n, i) => ({ n, i })).sort((a, b) => a.n - b.n);
|
|
25
|
-
|
|
26
|
-
// Find the two sorted neighbors that bracket this value
|
|
27
|
-
let lower = 0;
|
|
28
|
-
let upper = sorted.length - 1;
|
|
29
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
30
|
-
if (sorted[i].n <= numValue) lower = i;
|
|
31
|
-
if (sorted[i].n >= numValue) {
|
|
32
|
-
upper = i;
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const lowerPos = positionOf(domain[sorted[lower].i]);
|
|
38
|
-
const upperPos = positionOf(domain[sorted[upper].i]);
|
|
39
|
-
if (lower === upper) return lowerPos;
|
|
40
|
-
const t = (numValue - sorted[lower].n) / (sorted[upper].n - sorted[lower].n);
|
|
41
|
-
return lowerPos + t * (upperPos - lowerPos);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Resolve a data value to a pixel position on a given axis. */
|
|
45
|
-
export function resolvePosition(
|
|
46
|
-
value: string | number,
|
|
47
|
-
scale: ResolvedScales['x'] | ResolvedScales['y'],
|
|
48
|
-
): number | null {
|
|
49
|
-
if (!scale) return null;
|
|
50
|
-
|
|
51
|
-
const s = scale.scale;
|
|
52
|
-
const type = scale.type;
|
|
53
|
-
|
|
54
|
-
if (type === 'time') {
|
|
55
|
-
const date = new Date(String(value));
|
|
56
|
-
if (Number.isNaN(date.getTime())) return null;
|
|
57
|
-
return (s as ScaleTime<number, number>)(date);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (type === 'linear' || type === 'log') {
|
|
61
|
-
const num = typeof value === 'number' ? value : Number(value);
|
|
62
|
-
if (!Number.isFinite(num)) return null;
|
|
63
|
-
return (s as ScaleLinear<number, number>)(num);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (type === 'band') {
|
|
67
|
-
const bandScale = s as ScaleBand<string>;
|
|
68
|
-
const strValue = String(value);
|
|
69
|
-
const pos = bandScale(strValue);
|
|
70
|
-
if (pos !== undefined) return pos + (bandScale.bandwidth?.() ?? 0) / 2;
|
|
71
|
-
|
|
72
|
-
const bw = bandScale.bandwidth?.() ?? 0;
|
|
73
|
-
return interpolateInDomain(
|
|
74
|
-
Number(strValue),
|
|
75
|
-
bandScale.domain(),
|
|
76
|
-
(entry) => (bandScale(entry) ?? 0) + bw / 2,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// point or ordinal: try direct lookup, fall back to interpolation
|
|
81
|
-
const strValue = String(value);
|
|
82
|
-
const directResult = (s as (v: string) => number | undefined)(strValue);
|
|
83
|
-
if (directResult !== undefined) return directResult;
|
|
84
|
-
|
|
85
|
-
if (type === 'point' || type === 'ordinal') {
|
|
86
|
-
const domain = (s as { domain(): string[] }).domain();
|
|
87
|
-
return interpolateInDomain(
|
|
88
|
-
Number(strValue),
|
|
89
|
-
domain,
|
|
90
|
-
(entry) => (s as (v: string) => number)(entry) ?? 0,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Like resolvePosition, but extends the result to the visual edge of the column
|
|
99
|
-
* for band and point scales. Used by range annotations to span complete columns.
|
|
100
|
-
*
|
|
101
|
-
* For point scales: shifts by ±step/2 using scalePoint.step()
|
|
102
|
-
* For band scales: shifts from center (what resolvePosition returns) to the band edge
|
|
103
|
-
* For all other scales: identical to resolvePosition
|
|
104
|
-
*/
|
|
105
|
-
export function resolvePositionEdge(
|
|
106
|
-
value: string | number,
|
|
107
|
-
scale: ResolvedScales['x'] | ResolvedScales['y'],
|
|
108
|
-
edge: 'start' | 'end',
|
|
109
|
-
): number | null {
|
|
110
|
-
const center = resolvePosition(value, scale);
|
|
111
|
-
if (center === null || !scale) return null;
|
|
112
|
-
|
|
113
|
-
const type = scale.type;
|
|
114
|
-
|
|
115
|
-
if (type === 'point') {
|
|
116
|
-
const s = scale.scale as ScalePoint<string>;
|
|
117
|
-
const halfStep = (s.step?.() ?? 0) / 2;
|
|
118
|
-
return edge === 'start' ? center - halfStep : center + halfStep;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (type === 'band') {
|
|
122
|
-
// center = bandStart + bandwidth/2
|
|
123
|
-
// edge 'start' => bandStart = center - bandwidth/2
|
|
124
|
-
// edge 'end' => bandEnd = center + bandwidth/2
|
|
125
|
-
const s = scale.scale as ScaleBand<string>;
|
|
126
|
-
const halfBw = (s.bandwidth?.() ?? 0) / 2;
|
|
127
|
-
return edge === 'start' ? center - halfBw : center + halfBw;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return center;
|
|
131
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Range annotation resolver: creates a highlighted rectangular band
|
|
3
|
-
* between two data values on x or y axis.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
RangeAnnotation,
|
|
8
|
-
Rect,
|
|
9
|
-
ResolvedAnnotation,
|
|
10
|
-
ResolvedLabel,
|
|
11
|
-
} from '@opendata-ai/openchart-core';
|
|
12
|
-
import type { ResolvedScales } from '../layout/scales';
|
|
13
|
-
import { DEFAULT_RANGE_FILL, DEFAULT_RANGE_OPACITY } from './constants';
|
|
14
|
-
import { applyOffset } from './geometry';
|
|
15
|
-
import { resolvePosition, resolvePositionEdge } from './position';
|
|
16
|
-
import { makeAnnotationLabelStyle } from './resolve-text';
|
|
17
|
-
|
|
18
|
-
export function resolveRangeAnnotation(
|
|
19
|
-
annotation: RangeAnnotation,
|
|
20
|
-
scales: ResolvedScales,
|
|
21
|
-
chartArea: Rect,
|
|
22
|
-
isDark: boolean,
|
|
23
|
-
fontFamily?: string,
|
|
24
|
-
): ResolvedAnnotation | null {
|
|
25
|
-
let x = chartArea.x;
|
|
26
|
-
let y = chartArea.y;
|
|
27
|
-
let width = chartArea.width;
|
|
28
|
-
let height = chartArea.height;
|
|
29
|
-
|
|
30
|
-
// When extendToEdges is false, anchor at the data point's exact position
|
|
31
|
-
// (band/point center) instead of extending to the band/step edge. This insets
|
|
32
|
-
// the range from the axis so it starts at the first data point rather than
|
|
33
|
-
// flush against the axis guide. No effect on linear/time scales.
|
|
34
|
-
const extend = annotation.extendToEdges !== false;
|
|
35
|
-
const resolveEdge = (
|
|
36
|
-
value: string | number,
|
|
37
|
-
scale: typeof scales.x,
|
|
38
|
-
edge: 'start' | 'end',
|
|
39
|
-
): number | null =>
|
|
40
|
-
extend ? resolvePositionEdge(value, scale, edge) : resolvePosition(value, scale);
|
|
41
|
-
|
|
42
|
-
// X-range (vertical band)
|
|
43
|
-
if (annotation.x1 !== undefined && annotation.x2 !== undefined) {
|
|
44
|
-
const x1px = resolveEdge(annotation.x1, scales.x, 'start');
|
|
45
|
-
const x2px = resolveEdge(annotation.x2, scales.x, 'end');
|
|
46
|
-
if (x1px === null || x2px === null) return null;
|
|
47
|
-
|
|
48
|
-
x = Math.min(x1px, x2px);
|
|
49
|
-
width = Math.abs(x2px - x1px);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Y-range (horizontal band)
|
|
53
|
-
if (annotation.y1 !== undefined && annotation.y2 !== undefined) {
|
|
54
|
-
const y1px = resolveEdge(annotation.y1, scales.y, 'end');
|
|
55
|
-
const y2px = resolveEdge(annotation.y2, scales.y, 'start');
|
|
56
|
-
if (y1px === null || y2px === null) return null;
|
|
57
|
-
|
|
58
|
-
y = Math.min(y1px, y2px);
|
|
59
|
-
height = Math.abs(y2px - y1px);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const rect: Rect = { x, y, width, height };
|
|
63
|
-
|
|
64
|
-
// Label positioned within the range, with optional offset.
|
|
65
|
-
// labelAnchor controls horizontal placement:
|
|
66
|
-
// "top" (default): horizontally centered, text-anchor middle
|
|
67
|
-
// "left": left edge, text-anchor start
|
|
68
|
-
// "right": right edge, text-anchor end
|
|
69
|
-
// "bottom"/"auto": horizontally centered, text-anchor middle
|
|
70
|
-
let label: ResolvedLabel | undefined;
|
|
71
|
-
if (annotation.label) {
|
|
72
|
-
const anchor = annotation.labelAnchor ?? 'top';
|
|
73
|
-
const centered = anchor === 'top' || anchor === 'bottom' || anchor === 'auto';
|
|
74
|
-
const baseDx = centered ? 0 : anchor === 'right' ? -4 : 4;
|
|
75
|
-
const baseDy = 14;
|
|
76
|
-
const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
|
|
77
|
-
|
|
78
|
-
const style = makeAnnotationLabelStyle(
|
|
79
|
-
annotation.fontSize ?? 11,
|
|
80
|
-
annotation.fontWeight ?? 500,
|
|
81
|
-
undefined,
|
|
82
|
-
isDark,
|
|
83
|
-
fontFamily,
|
|
84
|
-
);
|
|
85
|
-
if (centered) {
|
|
86
|
-
style.textAnchor = 'middle';
|
|
87
|
-
} else if (anchor === 'right') {
|
|
88
|
-
style.textAnchor = 'end';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Position label horizontally centered within the range band by default.
|
|
92
|
-
// For left/right anchors, position at the respective edge.
|
|
93
|
-
const baseX = centered ? x + width / 2 : anchor === 'right' ? x + width : x;
|
|
94
|
-
|
|
95
|
-
label = {
|
|
96
|
-
text: annotation.label,
|
|
97
|
-
x: baseX + labelDelta.dx,
|
|
98
|
-
y: y + labelDelta.dy,
|
|
99
|
-
style,
|
|
100
|
-
visible: true,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// In dark mode, boost range opacity slightly for better visibility
|
|
105
|
-
const defaultOpacity = isDark ? 0.2 : DEFAULT_RANGE_OPACITY;
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
type: 'range',
|
|
109
|
-
id: annotation.id,
|
|
110
|
-
rect,
|
|
111
|
-
label,
|
|
112
|
-
fill: annotation.fill ?? DEFAULT_RANGE_FILL,
|
|
113
|
-
opacity: annotation.opacity ?? defaultOpacity,
|
|
114
|
-
stroke: annotation.stroke,
|
|
115
|
-
zIndex: annotation.zIndex,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reference line annotation resolver: creates a horizontal or vertical
|
|
3
|
-
* reference line at a data value with optional label.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
Point,
|
|
8
|
-
Rect,
|
|
9
|
-
RefLineAnnotation,
|
|
10
|
-
ResolvedAnnotation,
|
|
11
|
-
ResolvedLabel,
|
|
12
|
-
} from '@opendata-ai/openchart-core';
|
|
13
|
-
import type { ResolvedScales } from '../layout/scales';
|
|
14
|
-
import { DARK_REFLINE_STROKE, DEFAULT_REFLINE_DASH, LIGHT_REFLINE_STROKE } from './constants';
|
|
15
|
-
import { applyOffset } from './geometry';
|
|
16
|
-
import { resolvePosition } from './position';
|
|
17
|
-
import { makeAnnotationLabelStyle } from './resolve-text';
|
|
18
|
-
|
|
19
|
-
export function resolveRefLineAnnotation(
|
|
20
|
-
annotation: RefLineAnnotation,
|
|
21
|
-
scales: ResolvedScales,
|
|
22
|
-
chartArea: Rect,
|
|
23
|
-
isDark: boolean,
|
|
24
|
-
fontFamily?: string,
|
|
25
|
-
): ResolvedAnnotation | null {
|
|
26
|
-
let start: Point;
|
|
27
|
-
let end: Point;
|
|
28
|
-
|
|
29
|
-
if (annotation.y !== undefined) {
|
|
30
|
-
// Horizontal reference line
|
|
31
|
-
const yPx = resolvePosition(annotation.y, scales.y);
|
|
32
|
-
if (yPx === null) return null;
|
|
33
|
-
|
|
34
|
-
start = { x: chartArea.x, y: yPx };
|
|
35
|
-
end = { x: chartArea.x + chartArea.width, y: yPx };
|
|
36
|
-
} else if (annotation.x !== undefined) {
|
|
37
|
-
// Vertical reference line
|
|
38
|
-
const xPx = resolvePosition(annotation.x, scales.x);
|
|
39
|
-
if (xPx === null) return null;
|
|
40
|
-
|
|
41
|
-
start = { x: xPx, y: chartArea.y };
|
|
42
|
-
end = { x: xPx, y: chartArea.y + chartArea.height };
|
|
43
|
-
} else {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Determine dash pattern: strokeDash array wins, then style string
|
|
48
|
-
let strokeDasharray: string | undefined;
|
|
49
|
-
if (annotation.strokeDash && annotation.strokeDash.length > 0) {
|
|
50
|
-
strokeDasharray = annotation.strokeDash.join(' ');
|
|
51
|
-
} else if (annotation.style === 'dashed' || annotation.style === undefined) {
|
|
52
|
-
strokeDasharray = DEFAULT_REFLINE_DASH;
|
|
53
|
-
} else if (annotation.style === 'dotted') {
|
|
54
|
-
strokeDasharray = '2 2';
|
|
55
|
-
}
|
|
56
|
-
// 'solid' gets no dasharray
|
|
57
|
-
|
|
58
|
-
// Label placement on reflines. labelAnchor controls position:
|
|
59
|
-
//
|
|
60
|
-
// Horizontal reflines (y set):
|
|
61
|
-
// "left": left end of line, above "right"/"top" (default): right end, above
|
|
62
|
-
// "bottom": right end of line, below
|
|
63
|
-
//
|
|
64
|
-
// Vertical reflines (x set):
|
|
65
|
-
// "right": label to the left of the line, near top
|
|
66
|
-
// "bottom": label to the right of the line, near bottom
|
|
67
|
-
// "left"/"top" (default): label to the right of the line, near top
|
|
68
|
-
let label: ResolvedLabel | undefined;
|
|
69
|
-
if (annotation.label) {
|
|
70
|
-
const isHorizontal = annotation.y !== undefined;
|
|
71
|
-
const anchor = annotation.labelAnchor ?? (isHorizontal ? 'top' : 'left');
|
|
72
|
-
|
|
73
|
-
let baseDx: number;
|
|
74
|
-
let baseDy: number;
|
|
75
|
-
let labelX: number;
|
|
76
|
-
let labelY: number;
|
|
77
|
-
let textAnchor: 'start' | 'middle' | 'end';
|
|
78
|
-
|
|
79
|
-
if (isHorizontal) {
|
|
80
|
-
if (anchor === 'left') {
|
|
81
|
-
baseDx = 4;
|
|
82
|
-
baseDy = -4;
|
|
83
|
-
labelX = start.x;
|
|
84
|
-
labelY = start.y;
|
|
85
|
-
textAnchor = 'start';
|
|
86
|
-
} else if (anchor === 'bottom') {
|
|
87
|
-
baseDx = -4;
|
|
88
|
-
baseDy = 14;
|
|
89
|
-
labelX = end.x;
|
|
90
|
-
labelY = end.y;
|
|
91
|
-
textAnchor = 'end';
|
|
92
|
-
} else {
|
|
93
|
-
// 'right', 'top' (default), 'auto'
|
|
94
|
-
baseDx = -4;
|
|
95
|
-
baseDy = -4;
|
|
96
|
-
labelX = end.x;
|
|
97
|
-
labelY = end.y;
|
|
98
|
-
textAnchor = 'end';
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
// Vertical refline
|
|
102
|
-
if (anchor === 'right') {
|
|
103
|
-
baseDx = -4;
|
|
104
|
-
baseDy = 14;
|
|
105
|
-
labelX = start.x;
|
|
106
|
-
labelY = start.y;
|
|
107
|
-
textAnchor = 'end';
|
|
108
|
-
} else if (anchor === 'bottom') {
|
|
109
|
-
baseDx = 4;
|
|
110
|
-
baseDy = -4;
|
|
111
|
-
labelX = start.x;
|
|
112
|
-
labelY = end.y;
|
|
113
|
-
textAnchor = 'start';
|
|
114
|
-
} else {
|
|
115
|
-
// 'left', 'top' (default), 'auto' — label to the right of the line, near top
|
|
116
|
-
baseDx = 4;
|
|
117
|
-
baseDy = 14;
|
|
118
|
-
labelX = start.x;
|
|
119
|
-
labelY = start.y;
|
|
120
|
-
textAnchor = 'start';
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
|
|
125
|
-
|
|
126
|
-
const defaultStroke = isDark ? DARK_REFLINE_STROKE : LIGHT_REFLINE_STROKE;
|
|
127
|
-
const style = makeAnnotationLabelStyle(
|
|
128
|
-
annotation.fontSize ?? 11,
|
|
129
|
-
annotation.fontWeight ?? 400,
|
|
130
|
-
annotation.stroke ?? defaultStroke,
|
|
131
|
-
isDark,
|
|
132
|
-
fontFamily,
|
|
133
|
-
);
|
|
134
|
-
style.textAnchor = textAnchor;
|
|
135
|
-
|
|
136
|
-
label = {
|
|
137
|
-
text: annotation.label,
|
|
138
|
-
x: labelX + labelDelta.dx,
|
|
139
|
-
y: labelY + labelDelta.dy,
|
|
140
|
-
style,
|
|
141
|
-
visible: true,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const defaultStroke = isDark ? DARK_REFLINE_STROKE : LIGHT_REFLINE_STROKE;
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
type: 'refline',
|
|
149
|
-
id: annotation.id,
|
|
150
|
-
line: { start, end },
|
|
151
|
-
label,
|
|
152
|
-
stroke: annotation.stroke ?? defaultStroke,
|
|
153
|
-
strokeDasharray,
|
|
154
|
-
strokeWidth: annotation.strokeWidth ?? 1,
|
|
155
|
-
zIndex: annotation.zIndex,
|
|
156
|
-
};
|
|
157
|
-
}
|