@opendata-ai/openchart-engine 6.13.0 → 6.15.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/dist/index.js +190 -81
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/encoding-sugar.test.ts +3 -2
- package/src/charts/bar/compute.ts +28 -5
- package/src/charts/bar/index.ts +3 -0
- package/src/charts/bar/labels.ts +31 -13
- package/src/charts/column/index.ts +3 -0
- package/src/charts/column/labels.ts +35 -13
- package/src/charts/dot/index.ts +10 -1
- package/src/charts/dot/labels.ts +37 -6
- package/src/charts/line/area.ts +12 -4
- package/src/charts/line/compute.ts +7 -2
- package/src/charts/line/index.ts +33 -2
- package/src/compile.ts +1 -0
- package/src/compiler/validate.ts +1 -1
- package/src/layout/axes.ts +2 -2
- package/src/layout/scales.ts +15 -12
- package/src/legend/compute.ts +2 -4
- package/src/tooltips/compute.ts +29 -1
package/dist/index.js
CHANGED
|
@@ -727,6 +727,13 @@ function getSequentialColor(scales, value2, fallback = DEFAULT_COLOR) {
|
|
|
727
727
|
}
|
|
728
728
|
|
|
729
729
|
// src/charts/bar/compute.ts
|
|
730
|
+
function orientGradientForHorizontalBar(grad) {
|
|
731
|
+
if (grad.gradient !== "linear") return grad;
|
|
732
|
+
const lg = grad;
|
|
733
|
+
const isDefaultVertical = (lg.x1 === void 0 || lg.x1 === 0) && (lg.y1 === void 0 || lg.y1 === 0) && (lg.x2 === void 0 || lg.x2 === 0) && (lg.y2 === void 0 || lg.y2 === 1);
|
|
734
|
+
if (!isDefaultVertical) return grad;
|
|
735
|
+
return { ...lg, x1: 0, y1: 0, x2: 1, y2: 0 };
|
|
736
|
+
}
|
|
730
737
|
var MIN_BAR_WIDTH = 1;
|
|
731
738
|
function formatBarValue(value2) {
|
|
732
739
|
if (Math.abs(value2) >= 1e3) return abbreviateNumber(value2);
|
|
@@ -837,7 +844,7 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
|
|
|
837
844
|
y: bandY,
|
|
838
845
|
width: barWidth,
|
|
839
846
|
height: bandwidth,
|
|
840
|
-
fill: color2,
|
|
847
|
+
fill: isGradientDef(color2) ? orientGradientForHorizontalBar(color2) : color2,
|
|
841
848
|
cornerRadius: 0,
|
|
842
849
|
data: row,
|
|
843
850
|
aria,
|
|
@@ -884,7 +891,7 @@ function computeGroupedBars(data, valueField, categoryField, colorField, xScale,
|
|
|
884
891
|
y: subY,
|
|
885
892
|
width: barWidth,
|
|
886
893
|
height: subBandHeight,
|
|
887
|
-
fill: color2,
|
|
894
|
+
fill: isGradientDef(color2) ? orientGradientForHorizontalBar(color2) : color2,
|
|
888
895
|
cornerRadius: 2,
|
|
889
896
|
data: row,
|
|
890
897
|
aria,
|
|
@@ -915,7 +922,7 @@ function computeColoredBars(data, valueField, categoryField, colorField, xScale,
|
|
|
915
922
|
y: bandY,
|
|
916
923
|
width: barWidth,
|
|
917
924
|
height: bandwidth,
|
|
918
|
-
fill: color2,
|
|
925
|
+
fill: isGradientDef(color2) ? orientGradientForHorizontalBar(color2) : color2,
|
|
919
926
|
cornerRadius: 2,
|
|
920
927
|
data: row,
|
|
921
928
|
aria,
|
|
@@ -956,7 +963,7 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
956
963
|
y: bandY,
|
|
957
964
|
width: barWidth,
|
|
958
965
|
height: bandwidth,
|
|
959
|
-
fill: color2,
|
|
966
|
+
fill: isGradientDef(color2) ? orientGradientForHorizontalBar(color2) : color2,
|
|
960
967
|
cornerRadius: 2,
|
|
961
968
|
data: row,
|
|
962
969
|
aria,
|
|
@@ -968,11 +975,17 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
968
975
|
|
|
969
976
|
// src/charts/bar/labels.ts
|
|
970
977
|
import {
|
|
978
|
+
abbreviateNumber as abbreviateNumber2,
|
|
971
979
|
buildD3Formatter,
|
|
972
980
|
estimateTextWidth as estimateTextWidth2,
|
|
981
|
+
formatNumber as formatNumber2,
|
|
973
982
|
getRepresentativeColor,
|
|
974
983
|
resolveCollisions
|
|
975
984
|
} from "@opendata-ai/openchart-core";
|
|
985
|
+
function formatBarValue2(value2) {
|
|
986
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber2(value2);
|
|
987
|
+
return formatNumber2(value2);
|
|
988
|
+
}
|
|
976
989
|
var SUFFIX_MULTIPLIERS = {
|
|
977
990
|
K: 1e3,
|
|
978
991
|
M: 1e6,
|
|
@@ -998,21 +1011,30 @@ var LABEL_FONT_SIZE = 11;
|
|
|
998
1011
|
var LABEL_FONT_WEIGHT = 600;
|
|
999
1012
|
var LABEL_PADDING = 6;
|
|
1000
1013
|
var MIN_WIDTH_FOR_INSIDE_LABEL = 40;
|
|
1001
|
-
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix) {
|
|
1014
|
+
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix, valueField) {
|
|
1002
1015
|
if (density === "none") return [];
|
|
1003
1016
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
1004
1017
|
const candidates = [];
|
|
1005
1018
|
const fitsInSegment = [];
|
|
1006
1019
|
const formatter = buildD3Formatter(labelFormat);
|
|
1007
1020
|
for (const mark of targetMarks) {
|
|
1008
|
-
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1021
|
+
let valuePart;
|
|
1022
|
+
const rawNum = valueField != null ? Number(mark.data[valueField]) : NaN;
|
|
1023
|
+
if (formatter && Number.isFinite(rawNum)) {
|
|
1024
|
+
valuePart = formatter(rawNum);
|
|
1025
|
+
} else if (Number.isFinite(rawNum)) {
|
|
1026
|
+
valuePart = formatBarValue2(rawNum);
|
|
1027
|
+
} else {
|
|
1028
|
+
const ariaLabel = mark.aria.label;
|
|
1029
|
+
const lastColon = ariaLabel.lastIndexOf(":");
|
|
1030
|
+
const rawValue = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
1031
|
+
if (!rawValue) continue;
|
|
1032
|
+
if (formatter) {
|
|
1033
|
+
const num = parseDisplayNumber(rawValue);
|
|
1034
|
+
valuePart = !Number.isNaN(num) ? formatter(num) : rawValue;
|
|
1035
|
+
} else {
|
|
1036
|
+
valuePart = rawValue;
|
|
1037
|
+
}
|
|
1016
1038
|
}
|
|
1017
1039
|
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
1018
1040
|
const textWidth = estimateTextWidth2(valuePart, LABEL_FONT_SIZE, LABEL_FONT_WEIGHT);
|
|
@@ -1097,12 +1119,14 @@ function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labe
|
|
|
1097
1119
|
// src/charts/bar/index.ts
|
|
1098
1120
|
var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
1099
1121
|
const marks = computeBarMarks(spec, scales, chartArea, strategy);
|
|
1122
|
+
const valueField = spec.encoding?.x && "field" in spec.encoding.x ? spec.encoding.x.field : void 0;
|
|
1100
1123
|
const labels = computeBarLabels(
|
|
1101
1124
|
marks,
|
|
1102
1125
|
chartArea,
|
|
1103
1126
|
spec.labels.density,
|
|
1104
1127
|
spec.labels.format,
|
|
1105
|
-
spec.labels.prefix
|
|
1128
|
+
spec.labels.prefix,
|
|
1129
|
+
valueField
|
|
1106
1130
|
);
|
|
1107
1131
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
1108
1132
|
marks[i].label = labels[i];
|
|
@@ -1111,11 +1135,11 @@ var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
1111
1135
|
};
|
|
1112
1136
|
|
|
1113
1137
|
// src/charts/column/compute.ts
|
|
1114
|
-
import { abbreviateNumber as
|
|
1138
|
+
import { abbreviateNumber as abbreviateNumber3, formatNumber as formatNumber3, isGradientDef as isGradientDef2 } from "@opendata-ai/openchart-core";
|
|
1115
1139
|
var MIN_COLUMN_HEIGHT = 1;
|
|
1116
1140
|
function formatColumnValue(value2) {
|
|
1117
|
-
if (Math.abs(value2) >= 1e3) return
|
|
1118
|
-
return
|
|
1141
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber3(value2);
|
|
1142
|
+
return formatNumber3(value2);
|
|
1119
1143
|
}
|
|
1120
1144
|
function computeColumnMarks(spec, scales, _chartArea, _strategy) {
|
|
1121
1145
|
const encoding = spec.encoding;
|
|
@@ -1359,28 +1383,43 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
|
|
|
1359
1383
|
|
|
1360
1384
|
// src/charts/column/labels.ts
|
|
1361
1385
|
import {
|
|
1386
|
+
abbreviateNumber as abbreviateNumber4,
|
|
1362
1387
|
buildD3Formatter as buildD3Formatter2,
|
|
1363
1388
|
estimateTextWidth as estimateTextWidth3,
|
|
1389
|
+
formatNumber as formatNumber4,
|
|
1364
1390
|
getRepresentativeColor as getRepresentativeColor2,
|
|
1365
1391
|
resolveCollisions as resolveCollisions2
|
|
1366
1392
|
} from "@opendata-ai/openchart-core";
|
|
1393
|
+
function formatColumnValue2(value2) {
|
|
1394
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber4(value2);
|
|
1395
|
+
return formatNumber4(value2);
|
|
1396
|
+
}
|
|
1367
1397
|
var LABEL_FONT_SIZE2 = 10;
|
|
1368
1398
|
var LABEL_FONT_WEIGHT2 = 600;
|
|
1369
1399
|
var LABEL_OFFSET_Y = 6;
|
|
1370
|
-
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix) {
|
|
1400
|
+
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix, valueField) {
|
|
1371
1401
|
if (density === "none") return [];
|
|
1372
1402
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
1373
1403
|
const formatter = buildD3Formatter2(labelFormat);
|
|
1374
1404
|
const candidates = [];
|
|
1375
1405
|
for (const mark of targetMarks) {
|
|
1376
|
-
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1406
|
+
let valuePart;
|
|
1407
|
+
const rawNum = valueField != null ? Number(mark.data[valueField]) : NaN;
|
|
1408
|
+
if (formatter && Number.isFinite(rawNum)) {
|
|
1409
|
+
valuePart = formatter(rawNum);
|
|
1410
|
+
} else if (Number.isFinite(rawNum)) {
|
|
1411
|
+
valuePart = formatColumnValue2(rawNum);
|
|
1412
|
+
} else {
|
|
1413
|
+
const ariaLabel = mark.aria.label;
|
|
1414
|
+
const lastColon = ariaLabel.lastIndexOf(":");
|
|
1415
|
+
const rawValue = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
1416
|
+
if (!rawValue) continue;
|
|
1417
|
+
if (formatter) {
|
|
1418
|
+
const num = Number(rawValue.replace(/[^0-9.-]/g, ""));
|
|
1419
|
+
valuePart = !Number.isNaN(num) ? formatter(num) : rawValue;
|
|
1420
|
+
} else {
|
|
1421
|
+
valuePart = rawValue;
|
|
1422
|
+
}
|
|
1384
1423
|
}
|
|
1385
1424
|
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
1386
1425
|
const numericValue = parseFloat(valuePart);
|
|
@@ -1423,12 +1462,14 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, l
|
|
|
1423
1462
|
// src/charts/column/index.ts
|
|
1424
1463
|
var columnRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
1425
1464
|
const marks = computeColumnMarks(spec, scales, chartArea, strategy);
|
|
1465
|
+
const valueField = spec.encoding?.y && "field" in spec.encoding.y ? spec.encoding.y.field : void 0;
|
|
1426
1466
|
const labels = computeColumnLabels(
|
|
1427
1467
|
marks,
|
|
1428
1468
|
chartArea,
|
|
1429
1469
|
spec.labels.density,
|
|
1430
1470
|
spec.labels.format,
|
|
1431
|
-
spec.labels.prefix
|
|
1471
|
+
spec.labels.prefix,
|
|
1472
|
+
valueField
|
|
1432
1473
|
);
|
|
1433
1474
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
1434
1475
|
marks[i].label = labels[i];
|
|
@@ -1586,22 +1627,42 @@ function computeLollipopMarks(data, valueField, categoryField, xScale, yScale, b
|
|
|
1586
1627
|
|
|
1587
1628
|
// src/charts/dot/labels.ts
|
|
1588
1629
|
import {
|
|
1630
|
+
abbreviateNumber as abbreviateNumber5,
|
|
1631
|
+
buildD3Formatter as buildD3Formatter3,
|
|
1589
1632
|
estimateTextWidth as estimateTextWidth4,
|
|
1633
|
+
formatNumber as formatNumber5,
|
|
1590
1634
|
getRepresentativeColor as getRepresentativeColor3,
|
|
1591
1635
|
resolveCollisions as resolveCollisions3
|
|
1592
1636
|
} from "@opendata-ai/openchart-core";
|
|
1637
|
+
function formatDotValue(value2) {
|
|
1638
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber5(value2);
|
|
1639
|
+
return formatNumber5(value2);
|
|
1640
|
+
}
|
|
1593
1641
|
var LABEL_FONT_SIZE3 = 11;
|
|
1594
1642
|
var LABEL_FONT_WEIGHT3 = 600;
|
|
1595
1643
|
var LABEL_OFFSET_X = 10;
|
|
1596
|
-
function computeDotLabels(marks, _chartArea, density = "auto", labelPrefix) {
|
|
1644
|
+
function computeDotLabels(marks, _chartArea, density = "auto", labelPrefix, labelFormat, valueField) {
|
|
1597
1645
|
if (density === "none") return [];
|
|
1598
1646
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
1647
|
+
const formatter = buildD3Formatter3(labelFormat);
|
|
1599
1648
|
const candidates = [];
|
|
1600
1649
|
for (const mark of targetMarks) {
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1650
|
+
let valuePart;
|
|
1651
|
+
const rawNum = valueField != null ? Number(mark.data[valueField]) : NaN;
|
|
1652
|
+
if (formatter && Number.isFinite(rawNum)) {
|
|
1653
|
+
valuePart = formatter(rawNum);
|
|
1654
|
+
} else if (Number.isFinite(rawNum)) {
|
|
1655
|
+
valuePart = formatDotValue(rawNum);
|
|
1656
|
+
} else {
|
|
1657
|
+
const ariaLabel = mark.aria.label;
|
|
1658
|
+
const lastColon = ariaLabel.lastIndexOf(":");
|
|
1659
|
+
valuePart = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
1660
|
+
if (!valuePart) continue;
|
|
1661
|
+
if (formatter) {
|
|
1662
|
+
const num = Number(valuePart.replace(/[^0-9.-]/g, ""));
|
|
1663
|
+
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1605
1666
|
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
1606
1667
|
const textWidth = estimateTextWidth4(valuePart, LABEL_FONT_SIZE3, LABEL_FONT_WEIGHT3);
|
|
1607
1668
|
const textHeight = LABEL_FONT_SIZE3 * 1.2;
|
|
@@ -1640,7 +1701,15 @@ function computeDotLabels(marks, _chartArea, density = "auto", labelPrefix) {
|
|
|
1640
1701
|
var dotRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
1641
1702
|
const marks = computeDotMarks(spec, scales, chartArea, strategy);
|
|
1642
1703
|
const pointMarks = marks.filter((m) => m.type === "point");
|
|
1643
|
-
const
|
|
1704
|
+
const valueField = spec.encoding?.x && "field" in spec.encoding.x ? spec.encoding.x.field : void 0;
|
|
1705
|
+
const labels = computeDotLabels(
|
|
1706
|
+
pointMarks,
|
|
1707
|
+
chartArea,
|
|
1708
|
+
spec.labels.density,
|
|
1709
|
+
spec.labels.prefix,
|
|
1710
|
+
spec.labels.format,
|
|
1711
|
+
valueField
|
|
1712
|
+
);
|
|
1644
1713
|
let labelIdx = 0;
|
|
1645
1714
|
for (const mark of marks) {
|
|
1646
1715
|
if (mark.type === "point" && labelIdx < labels.length) {
|
|
@@ -1651,6 +1720,9 @@ var dotRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
1651
1720
|
return marks;
|
|
1652
1721
|
};
|
|
1653
1722
|
|
|
1723
|
+
// src/charts/line/index.ts
|
|
1724
|
+
import { getRepresentativeColor as getRepresentativeColor6 } from "@opendata-ai/openchart-core";
|
|
1725
|
+
|
|
1654
1726
|
// src/charts/line/area.ts
|
|
1655
1727
|
import { getRepresentativeColor as getRepresentativeColor4 } from "@opendata-ai/openchart-core";
|
|
1656
1728
|
|
|
@@ -2597,7 +2669,7 @@ function computeSingleArea(spec, scales, _chartArea) {
|
|
|
2597
2669
|
const marks = [];
|
|
2598
2670
|
for (const [seriesKey, rows] of groups) {
|
|
2599
2671
|
const color2 = getColor(scales, seriesKey);
|
|
2600
|
-
const sortedRows = sortByField(rows, xChannel.field);
|
|
2672
|
+
const sortedRows = xChannel.type === "nominal" || xChannel.type === "ordinal" ? rows : sortByField(rows, xChannel.field);
|
|
2601
2673
|
const validPoints = [];
|
|
2602
2674
|
for (const row of sortedRows) {
|
|
2603
2675
|
const xVal = scaleValue(scales.x.scale, scales.x.type, row[xChannel.field]);
|
|
@@ -2646,7 +2718,7 @@ function computeStackedArea(spec, scales, chartArea) {
|
|
|
2646
2718
|
if (!xChannel || !yChannel || !scales.x || !scales.y || !colorField) {
|
|
2647
2719
|
return computeSingleArea(spec, scales, chartArea);
|
|
2648
2720
|
}
|
|
2649
|
-
const sortedData = sortByField(spec.data, xChannel.field);
|
|
2721
|
+
const sortedData = xChannel.type === "nominal" || xChannel.type === "ordinal" ? spec.data : sortByField(spec.data, xChannel.field);
|
|
2650
2722
|
const seriesKeys = /* @__PURE__ */ new Set();
|
|
2651
2723
|
const xValueSet = /* @__PURE__ */ new Set();
|
|
2652
2724
|
const rowsByXSeries = /* @__PURE__ */ new Map();
|
|
@@ -2763,7 +2835,7 @@ function computeLineMarks(spec, scales, _chartArea, _strategy) {
|
|
|
2763
2835
|
for (const [seriesKey, rows] of groups) {
|
|
2764
2836
|
const color2 = isSequentialColor ? getSequentialColor(scales, _getMidValue(rows, sequentialColorField)) : getColor(scales, seriesKey);
|
|
2765
2837
|
const strokeColor = getRepresentativeColor5(color2);
|
|
2766
|
-
const sortedRows = sortByField(rows, xChannel.field);
|
|
2838
|
+
const sortedRows = xChannel.type === "nominal" || xChannel.type === "ordinal" ? rows : sortByField(rows, xChannel.field);
|
|
2767
2839
|
const pointsWithData = [];
|
|
2768
2840
|
const segments = [];
|
|
2769
2841
|
let currentSegment = [];
|
|
@@ -2951,9 +3023,24 @@ var lineRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
2951
3023
|
};
|
|
2952
3024
|
var areaRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
2953
3025
|
const areas = computeAreaMarks(spec, scales, chartArea);
|
|
2954
|
-
const
|
|
3026
|
+
const encoding = spec.encoding;
|
|
3027
|
+
const hasColor = !!(encoding.color && "field" in encoding.color);
|
|
3028
|
+
const lines = hasColor ? linesFromAreas(areas) : computeLineMarks(spec, scales, chartArea, strategy);
|
|
2955
3029
|
return [...areas, ...lines];
|
|
2956
3030
|
};
|
|
3031
|
+
function linesFromAreas(areas) {
|
|
3032
|
+
return areas.map((a) => ({
|
|
3033
|
+
type: "line",
|
|
3034
|
+
points: a.topPoints,
|
|
3035
|
+
path: a.topPath,
|
|
3036
|
+
stroke: getRepresentativeColor6(a.fill),
|
|
3037
|
+
strokeWidth: a.strokeWidth ?? 1,
|
|
3038
|
+
seriesKey: a.seriesKey,
|
|
3039
|
+
data: a.data,
|
|
3040
|
+
dataPoints: a.dataPoints,
|
|
3041
|
+
aria: { label: `${a.seriesKey ?? "Series"}: line with ${a.topPoints.length} data points` }
|
|
3042
|
+
}));
|
|
3043
|
+
}
|
|
2957
3044
|
|
|
2958
3045
|
// src/charts/pie/compute.ts
|
|
2959
3046
|
import { isConditionalDef, isGradientDef as isGradientDef3 } from "@opendata-ai/openchart-core";
|
|
@@ -3329,7 +3416,7 @@ function clearRenderers() {
|
|
|
3329
3416
|
}
|
|
3330
3417
|
|
|
3331
3418
|
// src/charts/rule/index.ts
|
|
3332
|
-
import { getRepresentativeColor as
|
|
3419
|
+
import { getRepresentativeColor as getRepresentativeColor7 } from "@opendata-ai/openchart-core";
|
|
3333
3420
|
function computeRuleMarks(spec, scales, chartArea) {
|
|
3334
3421
|
const encoding = spec.encoding;
|
|
3335
3422
|
const xChannel = encoding.x;
|
|
@@ -3372,7 +3459,7 @@ function computeRuleMarks(spec, scales, chartArea) {
|
|
|
3372
3459
|
const y2Val = scaleValue(scales.y.scale, scales.y.type, row[y2Channel.field]);
|
|
3373
3460
|
if (y2Val != null) y2 = y2Val;
|
|
3374
3461
|
}
|
|
3375
|
-
const color2 =
|
|
3462
|
+
const color2 = getRepresentativeColor7(
|
|
3376
3463
|
colorField ? getColor(scales, String(row[colorField] ?? "__default__")) : getColor(scales, "__default__")
|
|
3377
3464
|
);
|
|
3378
3465
|
const strokeDashEncoding = encoding.strokeDash && "field" in encoding.strokeDash ? encoding.strokeDash : void 0;
|
|
@@ -6181,7 +6268,7 @@ var scatterRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
6181
6268
|
};
|
|
6182
6269
|
|
|
6183
6270
|
// src/charts/text/index.ts
|
|
6184
|
-
import { getRepresentativeColor as
|
|
6271
|
+
import { getRepresentativeColor as getRepresentativeColor8 } from "@opendata-ai/openchart-core";
|
|
6185
6272
|
function computeTextMarks(spec, scales) {
|
|
6186
6273
|
const encoding = spec.encoding;
|
|
6187
6274
|
const xChannel = encoding.x;
|
|
@@ -6207,7 +6294,7 @@ function computeTextMarks(spec, scales) {
|
|
|
6207
6294
|
}
|
|
6208
6295
|
const text = String(row[textChannel.field] ?? "");
|
|
6209
6296
|
if (!text) continue;
|
|
6210
|
-
const color2 =
|
|
6297
|
+
const color2 = getRepresentativeColor8(
|
|
6211
6298
|
colorField ? getColor(scales, String(row[colorField] ?? "__default__")) : getColor(scales, "__default__")
|
|
6212
6299
|
);
|
|
6213
6300
|
const fontSize = sizeEncoding ? Math.max(8, Math.min(48, Number(row[sizeEncoding.field]) || 12)) : 12;
|
|
@@ -6234,7 +6321,7 @@ var textRenderer = (spec, scales, _chartArea, _strategy, _theme) => {
|
|
|
6234
6321
|
};
|
|
6235
6322
|
|
|
6236
6323
|
// src/charts/tick/index.ts
|
|
6237
|
-
import { getRepresentativeColor as
|
|
6324
|
+
import { getRepresentativeColor as getRepresentativeColor9 } from "@opendata-ai/openchart-core";
|
|
6238
6325
|
var DEFAULT_TICK_LENGTH = 18;
|
|
6239
6326
|
function computeTickMarks(spec, scales, _chartArea) {
|
|
6240
6327
|
const encoding = spec.encoding;
|
|
@@ -6250,7 +6337,7 @@ function computeTickMarks(spec, scales, _chartArea) {
|
|
|
6250
6337
|
const xVal = scaleValue(scales.x.scale, scales.x.type, row[xChannel.field]);
|
|
6251
6338
|
const yVal = scaleValue(scales.y.scale, scales.y.type, row[yChannel.field]);
|
|
6252
6339
|
if (xVal == null || yVal == null) continue;
|
|
6253
|
-
const color2 =
|
|
6340
|
+
const color2 = getRepresentativeColor9(
|
|
6254
6341
|
colorField ? getColor(scales, String(row[colorField] ?? "__default__")) : getColor(scales, "__default__")
|
|
6255
6342
|
);
|
|
6256
6343
|
const aria = {
|
|
@@ -6668,7 +6755,7 @@ function validateChartSpec(spec, errors) {
|
|
|
6668
6755
|
message: `Spec error: encoding.${channel} must have a "field" string`,
|
|
6669
6756
|
path: `encoding.${channel}.field`,
|
|
6670
6757
|
code: "MISSING_FIELD",
|
|
6671
|
-
suggestion: `
|
|
6758
|
+
suggestion: `For constant colors, use mark.fill (e.g., mark: { type: "bar", fill: "#1b7fa3" }) instead of encoding.${channel}. Encoding channels require a data field: ${availableColumns}`
|
|
6672
6759
|
});
|
|
6673
6760
|
continue;
|
|
6674
6761
|
}
|
|
@@ -7681,16 +7768,16 @@ var DEFAULT_COLLISION_PADDING = 5;
|
|
|
7681
7768
|
|
|
7682
7769
|
// src/layout/axes.ts
|
|
7683
7770
|
import {
|
|
7684
|
-
abbreviateNumber as
|
|
7685
|
-
buildD3Formatter as
|
|
7771
|
+
abbreviateNumber as abbreviateNumber6,
|
|
7772
|
+
buildD3Formatter as buildD3Formatter4,
|
|
7686
7773
|
buildTemporalFormatter,
|
|
7687
7774
|
estimateTextWidth as estimateTextWidth7,
|
|
7688
7775
|
formatDate,
|
|
7689
|
-
formatNumber as
|
|
7776
|
+
formatNumber as formatNumber6
|
|
7690
7777
|
} from "@opendata-ai/openchart-core";
|
|
7691
7778
|
var TICK_COUNTS = {
|
|
7692
|
-
full:
|
|
7693
|
-
reduced:
|
|
7779
|
+
full: 10,
|
|
7780
|
+
reduced: 7,
|
|
7694
7781
|
minimal: 3
|
|
7695
7782
|
};
|
|
7696
7783
|
var HEIGHT_MINIMAL_THRESHOLD = 120;
|
|
@@ -7803,11 +7890,11 @@ function formatTickLabel(value2, resolvedScale) {
|
|
|
7803
7890
|
if (NUMERIC_SCALE_TYPES.has(resolvedScale.type)) {
|
|
7804
7891
|
const num = value2;
|
|
7805
7892
|
if (formatStr) {
|
|
7806
|
-
const fmt =
|
|
7893
|
+
const fmt = buildD3Formatter4(formatStr);
|
|
7807
7894
|
if (fmt) return fmt(num);
|
|
7808
7895
|
}
|
|
7809
|
-
if (Math.abs(num) >= 1e3) return
|
|
7810
|
-
return
|
|
7896
|
+
if (Math.abs(num) >= 1e3) return abbreviateNumber6(num);
|
|
7897
|
+
return formatNumber6(num);
|
|
7811
7898
|
}
|
|
7812
7899
|
return String(value2);
|
|
7813
7900
|
}
|
|
@@ -8218,7 +8305,7 @@ function uniqueStrings(values) {
|
|
|
8218
8305
|
return result;
|
|
8219
8306
|
}
|
|
8220
8307
|
function applyCategoricalSort(values, sort) {
|
|
8221
|
-
if (sort
|
|
8308
|
+
if (!sort) return values;
|
|
8222
8309
|
const sorted = [...values].sort((a, b) => a.localeCompare(b, void 0, { numeric: true }));
|
|
8223
8310
|
if (sort === "descending") sorted.reverse();
|
|
8224
8311
|
return sorted;
|
|
@@ -8236,7 +8323,7 @@ function buildTimeScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8236
8323
|
const values = parseDates(fieldValues(data, channel.field));
|
|
8237
8324
|
const domain = channel.scale?.domain ? [new Date(channel.scale.domain[0]), new Date(channel.scale.domain[1])] : extent(values);
|
|
8238
8325
|
const scale = time().domain(domain).range([rangeStart, rangeEnd]);
|
|
8239
|
-
if (channel.scale?.nice
|
|
8326
|
+
if (!channel.scale?.domain && channel.scale?.nice === true) {
|
|
8240
8327
|
scale.nice();
|
|
8241
8328
|
}
|
|
8242
8329
|
applyContinuousConfig(scale, channel);
|
|
@@ -8246,7 +8333,7 @@ function buildUtcScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8246
8333
|
const values = parseDates(fieldValues(data, channel.field));
|
|
8247
8334
|
const domain = channel.scale?.domain ? [new Date(channel.scale.domain[0]), new Date(channel.scale.domain[1])] : extent(values);
|
|
8248
8335
|
const scale = utcTime().domain(domain).range([rangeStart, rangeEnd]);
|
|
8249
|
-
if (channel.scale?.nice
|
|
8336
|
+
if (!channel.scale?.domain && channel.scale?.nice === true) {
|
|
8250
8337
|
scale.nice();
|
|
8251
8338
|
}
|
|
8252
8339
|
applyContinuousConfig(scale, channel);
|
|
@@ -8269,7 +8356,7 @@ function buildLinearScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8269
8356
|
}
|
|
8270
8357
|
}
|
|
8271
8358
|
const scale = linear2().domain([domainMin, domainMax]).range([rangeStart, rangeEnd]);
|
|
8272
|
-
if (channel.scale?.nice !== false) {
|
|
8359
|
+
if (!channel.scale?.domain && channel.scale?.nice !== false) {
|
|
8273
8360
|
scale.nice();
|
|
8274
8361
|
}
|
|
8275
8362
|
applyContinuousConfig(scale, channel);
|
|
@@ -8283,7 +8370,7 @@ function buildLogScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8283
8370
|
if (channel.scale?.base !== void 0) {
|
|
8284
8371
|
scale.base(channel.scale.base);
|
|
8285
8372
|
}
|
|
8286
|
-
if (channel.scale?.nice !== false) {
|
|
8373
|
+
if (!channel.scale?.domain && channel.scale?.nice !== false) {
|
|
8287
8374
|
scale.nice();
|
|
8288
8375
|
}
|
|
8289
8376
|
applyContinuousConfig(scale, channel);
|
|
@@ -8307,7 +8394,7 @@ function buildPowScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8307
8394
|
if (channel.scale?.exponent !== void 0) {
|
|
8308
8395
|
scale.exponent(channel.scale.exponent);
|
|
8309
8396
|
}
|
|
8310
|
-
if (channel.scale?.nice !== false) {
|
|
8397
|
+
if (!channel.scale?.domain && channel.scale?.nice !== false) {
|
|
8311
8398
|
scale.nice();
|
|
8312
8399
|
}
|
|
8313
8400
|
applyContinuousConfig(scale, channel);
|
|
@@ -8328,7 +8415,7 @@ function buildSqrtScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8328
8415
|
}
|
|
8329
8416
|
}
|
|
8330
8417
|
const scale = sqrt2().domain([domainMin, domainMax]).range([rangeStart, rangeEnd]);
|
|
8331
|
-
if (channel.scale?.nice !== false) {
|
|
8418
|
+
if (!channel.scale?.domain && channel.scale?.nice !== false) {
|
|
8332
8419
|
scale.nice();
|
|
8333
8420
|
}
|
|
8334
8421
|
applyContinuousConfig(scale, channel);
|
|
@@ -8352,7 +8439,7 @@ function buildSymlogScale(channel, data, rangeStart, rangeEnd) {
|
|
|
8352
8439
|
if (channel.scale?.constant !== void 0) {
|
|
8353
8440
|
scale.constant(channel.scale.constant);
|
|
8354
8441
|
}
|
|
8355
|
-
if (channel.scale?.nice !== false) {
|
|
8442
|
+
if (!channel.scale?.domain && channel.scale?.nice !== false) {
|
|
8356
8443
|
scale.nice();
|
|
8357
8444
|
}
|
|
8358
8445
|
applyContinuousConfig(scale, channel);
|
|
@@ -8738,7 +8825,7 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
|
|
|
8738
8825
|
1,
|
|
8739
8826
|
Math.floor((maxLegendHeight - LEGEND_PADDING * 2) / (entryHeight + 4))
|
|
8740
8827
|
);
|
|
8741
|
-
const maxEntries = spec.legend?.symbolLimit != null ? Math.
|
|
8828
|
+
const maxEntries = spec.legend?.symbolLimit != null ? Math.max(1, spec.legend.symbolLimit) : maxFromSpace;
|
|
8742
8829
|
if (entries.length > maxEntries) {
|
|
8743
8830
|
entries = truncateEntries(entries, maxEntries);
|
|
8744
8831
|
}
|
|
@@ -8813,10 +8900,10 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
|
|
|
8813
8900
|
// src/sankey/compile-sankey.ts
|
|
8814
8901
|
import {
|
|
8815
8902
|
adaptTheme as adaptTheme2,
|
|
8816
|
-
buildD3Formatter as
|
|
8903
|
+
buildD3Formatter as buildD3Formatter5,
|
|
8817
8904
|
computeChrome as computeChrome3,
|
|
8818
8905
|
estimateTextWidth as estimateTextWidth10,
|
|
8819
|
-
formatNumber as
|
|
8906
|
+
formatNumber as formatNumber7,
|
|
8820
8907
|
resolveTheme as resolveTheme2
|
|
8821
8908
|
} from "@opendata-ai/openchart-core";
|
|
8822
8909
|
|
|
@@ -9670,10 +9757,10 @@ function buildSankeyLegend(nodeColorMap, colorField, data, sourceField, targetFi
|
|
|
9670
9757
|
}
|
|
9671
9758
|
function formatFlowValue(value2, valueFormat) {
|
|
9672
9759
|
if (valueFormat) {
|
|
9673
|
-
const fmt =
|
|
9760
|
+
const fmt = buildD3Formatter5(valueFormat);
|
|
9674
9761
|
if (fmt) return fmt(value2);
|
|
9675
9762
|
}
|
|
9676
|
-
return
|
|
9763
|
+
return formatNumber7(value2);
|
|
9677
9764
|
}
|
|
9678
9765
|
function buildTooltipDescriptors(nodes, links, valueFormat) {
|
|
9679
9766
|
const descriptors = /* @__PURE__ */ new Map();
|
|
@@ -9850,7 +9937,7 @@ function computeCategoryColors(data, column, theme, darkMode) {
|
|
|
9850
9937
|
}
|
|
9851
9938
|
|
|
9852
9939
|
// src/tables/format-cells.ts
|
|
9853
|
-
import { buildD3Formatter as
|
|
9940
|
+
import { buildD3Formatter as buildD3Formatter6, formatDate as formatDate2, formatNumber as formatNumber8 } from "@opendata-ai/openchart-core";
|
|
9854
9941
|
function isNumericValue(value2) {
|
|
9855
9942
|
if (typeof value2 === "number") return Number.isFinite(value2);
|
|
9856
9943
|
return false;
|
|
@@ -9869,7 +9956,7 @@ function formatCell(value2, column) {
|
|
|
9869
9956
|
};
|
|
9870
9957
|
}
|
|
9871
9958
|
if (column.format && isNumericValue(value2)) {
|
|
9872
|
-
const formatter =
|
|
9959
|
+
const formatter = buildD3Formatter6(column.format);
|
|
9873
9960
|
if (formatter) {
|
|
9874
9961
|
return {
|
|
9875
9962
|
value: value2,
|
|
@@ -9881,7 +9968,7 @@ function formatCell(value2, column) {
|
|
|
9881
9968
|
if (isNumericValue(value2)) {
|
|
9882
9969
|
return {
|
|
9883
9970
|
value: value2,
|
|
9884
|
-
formattedValue:
|
|
9971
|
+
formattedValue: formatNumber8(value2),
|
|
9885
9972
|
style
|
|
9886
9973
|
};
|
|
9887
9974
|
}
|
|
@@ -9901,13 +9988,13 @@ function formatCell(value2, column) {
|
|
|
9901
9988
|
function formatValueForSearch(value2, column) {
|
|
9902
9989
|
if (value2 == null) return "";
|
|
9903
9990
|
if (column.format && isNumericValue(value2)) {
|
|
9904
|
-
const formatter =
|
|
9991
|
+
const formatter = buildD3Formatter6(column.format);
|
|
9905
9992
|
if (formatter) {
|
|
9906
9993
|
return formatter(value2);
|
|
9907
9994
|
}
|
|
9908
9995
|
}
|
|
9909
9996
|
if (isNumericValue(value2)) {
|
|
9910
|
-
return
|
|
9997
|
+
return formatNumber8(value2);
|
|
9911
9998
|
}
|
|
9912
9999
|
return String(value2);
|
|
9913
10000
|
}
|
|
@@ -10382,8 +10469,8 @@ function compileTableLayout(spec, options, theme) {
|
|
|
10382
10469
|
import {
|
|
10383
10470
|
buildTemporalFormatter as buildTemporalFormatter2,
|
|
10384
10471
|
formatDate as formatDate3,
|
|
10385
|
-
formatNumber as
|
|
10386
|
-
getRepresentativeColor as
|
|
10472
|
+
formatNumber as formatNumber9,
|
|
10473
|
+
getRepresentativeColor as getRepresentativeColor10
|
|
10387
10474
|
} from "@opendata-ai/openchart-core";
|
|
10388
10475
|
function formatValue(value2, fieldType, format2) {
|
|
10389
10476
|
if (value2 == null) return "";
|
|
@@ -10397,10 +10484,10 @@ function formatValue(value2, fieldType, format2) {
|
|
|
10397
10484
|
try {
|
|
10398
10485
|
return format(format2)(value2);
|
|
10399
10486
|
} catch {
|
|
10400
|
-
return
|
|
10487
|
+
return formatNumber9(value2);
|
|
10401
10488
|
}
|
|
10402
10489
|
}
|
|
10403
|
-
return
|
|
10490
|
+
return formatNumber9(value2);
|
|
10404
10491
|
}
|
|
10405
10492
|
return String(value2);
|
|
10406
10493
|
}
|
|
@@ -10422,11 +10509,22 @@ function buildFields(row, encoding, color2) {
|
|
|
10422
10509
|
return buildExplicitTooltipFields(row, channels);
|
|
10423
10510
|
}
|
|
10424
10511
|
const fields = [];
|
|
10512
|
+
if (encoding.color && "field" in encoding.color) {
|
|
10513
|
+
fields.push({
|
|
10514
|
+
label: resolveLabel(encoding.color),
|
|
10515
|
+
value: formatValue(
|
|
10516
|
+
row[encoding.color.field],
|
|
10517
|
+
encoding.color.type,
|
|
10518
|
+
resolveFormat(encoding.color)
|
|
10519
|
+
),
|
|
10520
|
+
color: color2
|
|
10521
|
+
});
|
|
10522
|
+
}
|
|
10425
10523
|
if (encoding.y) {
|
|
10426
10524
|
fields.push({
|
|
10427
10525
|
label: resolveLabel(encoding.y),
|
|
10428
10526
|
value: formatValue(row[encoding.y.field], encoding.y.type, resolveFormat(encoding.y)),
|
|
10429
|
-
color: color2
|
|
10527
|
+
color: encoding.color ? void 0 : color2
|
|
10430
10528
|
});
|
|
10431
10529
|
}
|
|
10432
10530
|
if (encoding.x) {
|
|
@@ -10460,6 +10558,16 @@ function getTooltipTitle(row, encoding) {
|
|
|
10460
10558
|
if (encoding.y?.type === "nominal" || encoding.y?.type === "ordinal") {
|
|
10461
10559
|
return String(row[encoding.y.field] ?? "");
|
|
10462
10560
|
}
|
|
10561
|
+
if (encoding.x?.type === "quantitative" && encoding.y?.type === "quantitative") {
|
|
10562
|
+
const encodedFields = new Set(
|
|
10563
|
+
[encoding.x, encoding.y, encoding.color, encoding.size, encoding.detail].filter((ch) => !!ch && "field" in ch).map((ch) => ch.field)
|
|
10564
|
+
);
|
|
10565
|
+
for (const [key, value2] of Object.entries(row)) {
|
|
10566
|
+
if (!encodedFields.has(key) && typeof value2 === "string") {
|
|
10567
|
+
return value2;
|
|
10568
|
+
}
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10463
10571
|
if (encoding.color && "field" in encoding.color) {
|
|
10464
10572
|
return String(row[encoding.color.field] ?? "");
|
|
10465
10573
|
}
|
|
@@ -10478,12 +10586,12 @@ function tooltipsForLine(mark, encoding, _markIndex) {
|
|
|
10478
10586
|
}
|
|
10479
10587
|
function tooltipsForPoint(mark, encoding, markIndex) {
|
|
10480
10588
|
const title = getTooltipTitle(mark.data, encoding);
|
|
10481
|
-
const fields = buildFields(mark.data, encoding,
|
|
10589
|
+
const fields = buildFields(mark.data, encoding, getRepresentativeColor10(mark.fill));
|
|
10482
10590
|
return [[`point-${markIndex}`, { title, fields }]];
|
|
10483
10591
|
}
|
|
10484
10592
|
function tooltipsForRect(mark, encoding, markIndex) {
|
|
10485
10593
|
const title = getTooltipTitle(mark.data, encoding);
|
|
10486
|
-
const fields = buildFields(mark.data, encoding,
|
|
10594
|
+
const fields = buildFields(mark.data, encoding, getRepresentativeColor10(mark.fill));
|
|
10487
10595
|
return [[`rect-${markIndex}`, { title, fields }]];
|
|
10488
10596
|
}
|
|
10489
10597
|
function tooltipsForArc(mark, encoding, markIndex) {
|
|
@@ -10496,14 +10604,14 @@ function tooltipsForArc(mark, encoding, markIndex) {
|
|
|
10496
10604
|
fields.push({
|
|
10497
10605
|
label: categoryName,
|
|
10498
10606
|
value: formatValue(row[encoding.y.field], encoding.y.type, resolveFormat(encoding.y)),
|
|
10499
|
-
color:
|
|
10607
|
+
color: getRepresentativeColor10(mark.fill)
|
|
10500
10608
|
});
|
|
10501
10609
|
}
|
|
10502
10610
|
} else if (encoding.y) {
|
|
10503
10611
|
fields.push({
|
|
10504
10612
|
label: resolveLabel(encoding.y),
|
|
10505
10613
|
value: formatValue(row[encoding.y.field], encoding.y.type, resolveFormat(encoding.y)),
|
|
10506
|
-
color:
|
|
10614
|
+
color: getRepresentativeColor10(mark.fill)
|
|
10507
10615
|
});
|
|
10508
10616
|
}
|
|
10509
10617
|
const title = colorEnc ? String(row[colorEnc.field] ?? "") : void 0;
|
|
@@ -10514,7 +10622,7 @@ function tooltipsForArea(mark, encoding, _markIndex) {
|
|
|
10514
10622
|
for (const dp of mark.dataPoints) {
|
|
10515
10623
|
dp.tooltip = {
|
|
10516
10624
|
title: getTooltipTitle(dp.datum, encoding),
|
|
10517
|
-
fields: buildFields(dp.datum, encoding,
|
|
10625
|
+
fields: buildFields(dp.datum, encoding, getRepresentativeColor10(mark.fill))
|
|
10518
10626
|
};
|
|
10519
10627
|
}
|
|
10520
10628
|
}
|
|
@@ -11125,7 +11233,8 @@ function compileChart(spec, options) {
|
|
|
11125
11233
|
height: options.height
|
|
11126
11234
|
},
|
|
11127
11235
|
animation: resolvedAnimation,
|
|
11128
|
-
watermark
|
|
11236
|
+
watermark,
|
|
11237
|
+
measureText: options.measureText
|
|
11129
11238
|
};
|
|
11130
11239
|
}
|
|
11131
11240
|
function compileLayer(spec, options) {
|