@opendata-ai/openchart-engine 6.9.0 → 6.11.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 +2 -10
- package/dist/index.js +198 -28
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/charts/bar/__tests__/compute.test.ts +92 -0
- package/src/charts/bar/compute.ts +92 -5
- package/src/charts/bar/labels.ts +2 -1
- package/src/charts/column/__tests__/compute.test.ts +66 -0
- package/src/charts/column/compute.ts +98 -6
- package/src/charts/column/labels.ts +2 -1
- package/src/charts/dot/labels.ts +6 -2
- package/src/charts/line/area.ts +3 -2
- package/src/charts/line/compute.ts +5 -2
- package/src/charts/pie/compute.ts +24 -3
- package/src/charts/rule/index.ts +6 -3
- package/src/charts/scatter/compute.ts +2 -1
- package/src/charts/text/index.ts +6 -3
- package/src/charts/tick/index.ts +6 -3
- package/src/charts/utils.ts +3 -3
- package/src/compile.ts +3 -2
- package/src/layout/scales.ts +12 -4
- package/src/tooltips/compute.ts +11 -6
package/src/charts/text/index.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Encoding, Mark, MarkAria, TextMarkLayout } from '@opendata-ai/openchart-core';
|
|
10
|
+
import { getRepresentativeColor } from '@opendata-ai/openchart-core';
|
|
10
11
|
|
|
11
12
|
import type { NormalizedChartSpec } from '../../compiler/types';
|
|
12
13
|
import type { ResolvedScales } from '../../layout/scales';
|
|
@@ -52,9 +53,11 @@ export function computeTextMarks(
|
|
|
52
53
|
const text = String(row[textChannel.field] ?? '');
|
|
53
54
|
if (!text) continue;
|
|
54
55
|
|
|
55
|
-
const color =
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const color = getRepresentativeColor(
|
|
57
|
+
colorField
|
|
58
|
+
? getColor(scales, String(row[colorField] ?? '__default__'))
|
|
59
|
+
: getColor(scales, '__default__'),
|
|
60
|
+
);
|
|
58
61
|
|
|
59
62
|
const fontSize = sizeEncoding
|
|
60
63
|
? Math.max(8, Math.min(48, Number(row[sizeEncoding.field]) || 12))
|
package/src/charts/tick/index.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Encoding, Mark, MarkAria, Rect, TickMarkLayout } from '@opendata-ai/openchart-core';
|
|
10
|
+
import { getRepresentativeColor } from '@opendata-ai/openchart-core';
|
|
10
11
|
|
|
11
12
|
import type { NormalizedChartSpec } from '../../compiler/types';
|
|
12
13
|
import type { ResolvedScales } from '../../layout/scales';
|
|
@@ -48,9 +49,11 @@ export function computeTickMarks(
|
|
|
48
49
|
const yVal = scaleValue(scales.y.scale, scales.y.type, row[yChannel.field]);
|
|
49
50
|
if (xVal == null || yVal == null) continue;
|
|
50
51
|
|
|
51
|
-
const color =
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const color = getRepresentativeColor(
|
|
53
|
+
colorField
|
|
54
|
+
? getColor(scales, String(row[colorField] ?? '__default__'))
|
|
55
|
+
: getColor(scales, '__default__'),
|
|
56
|
+
);
|
|
54
57
|
|
|
55
58
|
const aria: MarkAria = {
|
|
56
59
|
label: `${row[xChannel.field]}, ${row[yChannel.field]}`,
|
package/src/charts/utils.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* data grouping, color lookup, and shared constants.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { DataRow } from '@opendata-ai/openchart-core';
|
|
8
|
+
import type { DataRow, GradientDef } from '@opendata-ai/openchart-core';
|
|
9
9
|
import type { ScaleBand, ScaleLinear, ScalePoint, ScaleTime } from 'd3-scale';
|
|
10
10
|
import type { D3Scale, ResolvedScales } from '../layout/scales';
|
|
11
11
|
|
|
@@ -143,7 +143,7 @@ export function getColor(
|
|
|
143
143
|
key: string,
|
|
144
144
|
_index?: number,
|
|
145
145
|
fallback: string = DEFAULT_COLOR,
|
|
146
|
-
): string {
|
|
146
|
+
): string | GradientDef {
|
|
147
147
|
if (scales.color && key !== '__default__') {
|
|
148
148
|
const colorScale = scales.color.scale as (v: string) => string;
|
|
149
149
|
return colorScale(key);
|
|
@@ -159,7 +159,7 @@ export function getSequentialColor(
|
|
|
159
159
|
scales: ResolvedScales,
|
|
160
160
|
value: number,
|
|
161
161
|
fallback: string = DEFAULT_COLOR,
|
|
162
|
-
): string {
|
|
162
|
+
): string | GradientDef {
|
|
163
163
|
if (scales.color?.type === 'sequential') {
|
|
164
164
|
const colorScale = scales.color.scale as unknown as (v: number) => string;
|
|
165
165
|
return colorScale(value);
|
package/src/compile.ts
CHANGED
|
@@ -386,8 +386,9 @@ export function compileChart(spec: unknown, options: CompileOptions): ChartLayou
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
// Set default color for single-series charts
|
|
390
|
-
|
|
389
|
+
// Set default color for single-series charts. If the user set a fill on the mark def
|
|
390
|
+
// (string or gradient), that takes priority over the theme's first categorical color.
|
|
391
|
+
scales.defaultColor = chartSpec.markDef.fill ?? theme.colors.categorical[0];
|
|
391
392
|
|
|
392
393
|
// Arc charts (pie/donut) don't use axes or gridlines
|
|
393
394
|
const isRadial = chartSpec.markType === 'arc';
|
package/src/layout/scales.ts
CHANGED
|
@@ -99,8 +99,8 @@ export interface ResolvedScales {
|
|
|
99
99
|
y?: ResolvedScale;
|
|
100
100
|
color?: ResolvedScale;
|
|
101
101
|
size?: ResolvedScale;
|
|
102
|
-
/** Default color for single-series charts (first categorical palette color). */
|
|
103
|
-
defaultColor?: string;
|
|
102
|
+
/** Default color for single-series charts (first categorical palette color or markDef.fill gradient). */
|
|
103
|
+
defaultColor?: string | import('@opendata-ai/openchart-core').GradientDef;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// ---------------------------------------------------------------------------
|
|
@@ -622,7 +622,13 @@ export function computeScales(
|
|
|
622
622
|
// For stacked bars, the x-domain needs the max category sum, not max individual value.
|
|
623
623
|
// Without this, stacked bars would clip past the chart area.
|
|
624
624
|
let xData = data;
|
|
625
|
-
|
|
625
|
+
const xStackDisabled = encoding.x.stack === null || encoding.x.stack === false;
|
|
626
|
+
if (
|
|
627
|
+
spec.markType === 'bar' &&
|
|
628
|
+
encoding.color &&
|
|
629
|
+
encoding.x.type === 'quantitative' &&
|
|
630
|
+
!xStackDisabled
|
|
631
|
+
) {
|
|
626
632
|
const yField = encoding.y?.field;
|
|
627
633
|
const xField = encoding.x.field;
|
|
628
634
|
if (yField) {
|
|
@@ -660,10 +666,12 @@ export function computeScales(
|
|
|
660
666
|
spec.markType === 'bar' &&
|
|
661
667
|
(encoding.x?.type === 'nominal' || encoding.x?.type === 'ordinal') &&
|
|
662
668
|
encoding.y.type === 'quantitative';
|
|
669
|
+
const yStackDisabled = encoding.y.stack === null || encoding.y.stack === false;
|
|
663
670
|
if (
|
|
664
671
|
(isVerticalBar || spec.markType === 'area') &&
|
|
665
672
|
encoding.color &&
|
|
666
|
-
encoding.y.type === 'quantitative'
|
|
673
|
+
encoding.y.type === 'quantitative' &&
|
|
674
|
+
!yStackDisabled
|
|
667
675
|
) {
|
|
668
676
|
const xField = encoding.x?.field;
|
|
669
677
|
const yField = encoding.y.field;
|
package/src/tooltips/compute.ts
CHANGED
|
@@ -19,7 +19,12 @@ import type {
|
|
|
19
19
|
TooltipContent,
|
|
20
20
|
TooltipField,
|
|
21
21
|
} from '@opendata-ai/openchart-core';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
buildTemporalFormatter,
|
|
24
|
+
formatDate,
|
|
25
|
+
formatNumber,
|
|
26
|
+
getRepresentativeColor,
|
|
27
|
+
} from '@opendata-ai/openchart-core';
|
|
23
28
|
import { format as d3Format } from 'd3-format';
|
|
24
29
|
|
|
25
30
|
import type { NormalizedChartSpec } from '../compiler/types';
|
|
@@ -155,7 +160,7 @@ function tooltipsForPoint(
|
|
|
155
160
|
markIndex: number,
|
|
156
161
|
): Array<[string, TooltipContent]> {
|
|
157
162
|
const title = getTooltipTitle(mark.data, encoding);
|
|
158
|
-
const fields = buildFields(mark.data, encoding, mark.fill);
|
|
163
|
+
const fields = buildFields(mark.data, encoding, getRepresentativeColor(mark.fill));
|
|
159
164
|
|
|
160
165
|
return [[`point-${markIndex}`, { title, fields }]];
|
|
161
166
|
}
|
|
@@ -166,7 +171,7 @@ function tooltipsForRect(
|
|
|
166
171
|
markIndex: number,
|
|
167
172
|
): Array<[string, TooltipContent]> {
|
|
168
173
|
const title = getTooltipTitle(mark.data, encoding);
|
|
169
|
-
const fields = buildFields(mark.data, encoding, mark.fill);
|
|
174
|
+
const fields = buildFields(mark.data, encoding, getRepresentativeColor(mark.fill));
|
|
170
175
|
|
|
171
176
|
return [[`rect-${markIndex}`, { title, fields }]];
|
|
172
177
|
}
|
|
@@ -187,14 +192,14 @@ function tooltipsForArc(
|
|
|
187
192
|
fields.push({
|
|
188
193
|
label: categoryName,
|
|
189
194
|
value: formatValue(row[encoding.y.field], encoding.y.type, encoding.y.axis?.format),
|
|
190
|
-
color: mark.fill,
|
|
195
|
+
color: getRepresentativeColor(mark.fill),
|
|
191
196
|
});
|
|
192
197
|
}
|
|
193
198
|
} else if (encoding.y) {
|
|
194
199
|
fields.push({
|
|
195
200
|
label: encoding.y.field,
|
|
196
201
|
value: formatValue(row[encoding.y.field], encoding.y.type, encoding.y.axis?.format),
|
|
197
|
-
color: mark.fill,
|
|
202
|
+
color: getRepresentativeColor(mark.fill),
|
|
198
203
|
});
|
|
199
204
|
}
|
|
200
205
|
|
|
@@ -214,7 +219,7 @@ function tooltipsForArea(
|
|
|
214
219
|
for (const dp of mark.dataPoints) {
|
|
215
220
|
dp.tooltip = {
|
|
216
221
|
title: getTooltipTitle(dp.datum, encoding),
|
|
217
|
-
fields: buildFields(dp.datum, encoding, mark.fill),
|
|
222
|
+
fields: buildFields(dp.datum, encoding, getRepresentativeColor(mark.fill)),
|
|
218
223
|
};
|
|
219
224
|
}
|
|
220
225
|
}
|