@opendata-ai/openchart-engine 6.5.2 → 6.7.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 +44 -19
- package/dist/index.js +1353 -363
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/__test-fixtures__/specs.ts +3 -3
- package/src/__tests__/dimensions.test.ts +47 -1
- package/src/annotations/__tests__/compute.test.ts +28 -0
- package/src/charts/bar/index.ts +7 -1
- package/src/charts/bar/labels.ts +2 -0
- package/src/charts/column/index.ts +7 -1
- package/src/charts/column/labels.ts +2 -0
- package/src/charts/dot/index.ts +1 -1
- package/src/charts/dot/labels.ts +3 -1
- package/src/compile.ts +30 -0
- package/src/compiler/__tests__/normalize.test.ts +18 -3
- package/src/compiler/normalize.ts +26 -2
- package/src/compiler/types.ts +9 -3
- package/src/compiler/validate.ts +109 -5
- package/src/index.ts +9 -1
- package/src/sankey/__tests__/compile-sankey.test.ts +353 -0
- package/src/sankey/__tests__/layout.test.ts +165 -0
- package/src/sankey/compile-sankey.ts +593 -0
- package/src/sankey/layout.ts +170 -0
- package/src/sankey/types.ts +36 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/compile.ts
|
|
2
2
|
import {
|
|
3
|
-
adaptTheme as
|
|
3
|
+
adaptTheme as adaptTheme3,
|
|
4
4
|
BRAND_RESERVE_WIDTH as BRAND_RESERVE_WIDTH2,
|
|
5
5
|
computeLabelBounds,
|
|
6
6
|
generateAltText,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getBreakpoint,
|
|
9
9
|
getHeightClass,
|
|
10
10
|
getLayoutStrategy,
|
|
11
|
-
resolveTheme as
|
|
11
|
+
resolveTheme as resolveTheme3
|
|
12
12
|
} from "@opendata-ai/openchart-core";
|
|
13
13
|
|
|
14
14
|
// src/annotations/compute.ts
|
|
@@ -44,23 +44,23 @@ function interpolateInDomain(numValue, domain, positionOf) {
|
|
|
44
44
|
const t = (numValue - sorted[lower].n) / (sorted[upper].n - sorted[lower].n);
|
|
45
45
|
return lowerPos + t * (upperPos - lowerPos);
|
|
46
46
|
}
|
|
47
|
-
function resolvePosition(
|
|
47
|
+
function resolvePosition(value2, scale) {
|
|
48
48
|
if (!scale) return null;
|
|
49
49
|
const s = scale.scale;
|
|
50
50
|
const type = scale.type;
|
|
51
51
|
if (type === "time") {
|
|
52
|
-
const date2 = new Date(String(
|
|
52
|
+
const date2 = new Date(String(value2));
|
|
53
53
|
if (Number.isNaN(date2.getTime())) return null;
|
|
54
54
|
return s(date2);
|
|
55
55
|
}
|
|
56
56
|
if (type === "linear" || type === "log") {
|
|
57
|
-
const num = typeof
|
|
57
|
+
const num = typeof value2 === "number" ? value2 : Number(value2);
|
|
58
58
|
if (!Number.isFinite(num)) return null;
|
|
59
59
|
return s(num);
|
|
60
60
|
}
|
|
61
61
|
if (type === "band") {
|
|
62
62
|
const bandScale = s;
|
|
63
|
-
const strValue2 = String(
|
|
63
|
+
const strValue2 = String(value2);
|
|
64
64
|
const pos = bandScale(strValue2);
|
|
65
65
|
if (pos !== void 0) return pos + (bandScale.bandwidth?.() ?? 0) / 2;
|
|
66
66
|
const bw = bandScale.bandwidth?.() ?? 0;
|
|
@@ -70,7 +70,7 @@ function resolvePosition(value, scale) {
|
|
|
70
70
|
(entry) => (bandScale(entry) ?? 0) + bw / 2
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
|
-
const strValue = String(
|
|
73
|
+
const strValue = String(value2);
|
|
74
74
|
const directResult = s(strValue);
|
|
75
75
|
if (directResult !== void 0) return directResult;
|
|
76
76
|
if (type === "point" || type === "ordinal") {
|
|
@@ -555,15 +555,15 @@ function isFieldPredicate(pred) {
|
|
|
555
555
|
return "field" in pred;
|
|
556
556
|
}
|
|
557
557
|
function evaluateFieldPredicate(datum, pred) {
|
|
558
|
-
const
|
|
558
|
+
const value2 = datum[pred.field];
|
|
559
559
|
if (pred.valid !== void 0) {
|
|
560
|
-
const isValid =
|
|
560
|
+
const isValid = value2 !== null && value2 !== void 0 && !Number.isNaN(value2);
|
|
561
561
|
return pred.valid ? isValid : !isValid;
|
|
562
562
|
}
|
|
563
563
|
if (pred.equal !== void 0) {
|
|
564
|
-
return
|
|
564
|
+
return value2 === pred.equal;
|
|
565
565
|
}
|
|
566
|
-
const numValue = Number(
|
|
566
|
+
const numValue = Number(value2);
|
|
567
567
|
if (pred.lt !== void 0) {
|
|
568
568
|
return numValue < pred.lt;
|
|
569
569
|
}
|
|
@@ -577,11 +577,11 @@ function evaluateFieldPredicate(datum, pred) {
|
|
|
577
577
|
return numValue >= pred.gte;
|
|
578
578
|
}
|
|
579
579
|
if (pred.range !== void 0) {
|
|
580
|
-
const [
|
|
581
|
-
return numValue >=
|
|
580
|
+
const [min4, max4] = pred.range;
|
|
581
|
+
return numValue >= min4 && numValue <= max4;
|
|
582
582
|
}
|
|
583
583
|
if (pred.oneOf !== void 0) {
|
|
584
|
-
return pred.oneOf.includes(
|
|
584
|
+
return pred.oneOf.includes(value2);
|
|
585
585
|
}
|
|
586
586
|
return true;
|
|
587
587
|
}
|
|
@@ -620,18 +620,18 @@ function isConditionalValueDef(def) {
|
|
|
620
620
|
|
|
621
621
|
// src/charts/utils.ts
|
|
622
622
|
var DEFAULT_COLOR = "#1b7fa3";
|
|
623
|
-
function scaleValue(scale, scaleType,
|
|
624
|
-
if (
|
|
623
|
+
function scaleValue(scale, scaleType, value2) {
|
|
624
|
+
if (value2 == null) return null;
|
|
625
625
|
if (scaleType === "time" || scaleType === "utc") {
|
|
626
|
-
const date2 =
|
|
626
|
+
const date2 = value2 instanceof Date ? value2 : new Date(String(value2));
|
|
627
627
|
if (Number.isNaN(date2.getTime())) return null;
|
|
628
628
|
return scale(date2);
|
|
629
629
|
}
|
|
630
630
|
if (scaleType === "point" || scaleType === "band" || scaleType === "ordinal") {
|
|
631
|
-
const result = scale(String(
|
|
631
|
+
const result = scale(String(value2));
|
|
632
632
|
return result ?? null;
|
|
633
633
|
}
|
|
634
|
-
const num = typeof
|
|
634
|
+
const num = typeof value2 === "number" ? value2 : Number(value2);
|
|
635
635
|
if (!Number.isFinite(num)) return null;
|
|
636
636
|
return scale(num);
|
|
637
637
|
}
|
|
@@ -683,19 +683,19 @@ function getColor(scales, key, _index, fallback = DEFAULT_COLOR) {
|
|
|
683
683
|
}
|
|
684
684
|
return scales.defaultColor ?? fallback;
|
|
685
685
|
}
|
|
686
|
-
function getSequentialColor(scales,
|
|
686
|
+
function getSequentialColor(scales, value2, fallback = DEFAULT_COLOR) {
|
|
687
687
|
if (scales.color?.type === "sequential") {
|
|
688
688
|
const colorScale = scales.color.scale;
|
|
689
|
-
return colorScale(
|
|
689
|
+
return colorScale(value2);
|
|
690
690
|
}
|
|
691
691
|
return scales.defaultColor ?? fallback;
|
|
692
692
|
}
|
|
693
693
|
|
|
694
694
|
// src/charts/bar/compute.ts
|
|
695
695
|
var MIN_BAR_WIDTH = 1;
|
|
696
|
-
function formatBarValue(
|
|
697
|
-
if (Math.abs(
|
|
698
|
-
return formatNumber(
|
|
696
|
+
function formatBarValue(value2) {
|
|
697
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber(value2);
|
|
698
|
+
return formatNumber(value2);
|
|
699
699
|
}
|
|
700
700
|
function computeBarMarks(spec, scales, _chartArea, _strategy) {
|
|
701
701
|
const encoding = spec.encoding;
|
|
@@ -750,14 +750,14 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
|
|
|
750
750
|
let cumulativeValue = 0;
|
|
751
751
|
for (const row of rows) {
|
|
752
752
|
const groupKey = String(row[colorField] ?? "");
|
|
753
|
-
const
|
|
754
|
-
if (!Number.isFinite(
|
|
753
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
754
|
+
if (!Number.isFinite(value2) || value2 <= 0) continue;
|
|
755
755
|
const color2 = getColor(scales, groupKey);
|
|
756
756
|
const xLeft = xScale(cumulativeValue);
|
|
757
|
-
const xRight = xScale(cumulativeValue +
|
|
757
|
+
const xRight = xScale(cumulativeValue + value2);
|
|
758
758
|
const barWidth = Math.max(Math.abs(xRight - xLeft), MIN_BAR_WIDTH);
|
|
759
759
|
const aria = {
|
|
760
|
-
label: `${category}, ${groupKey}: ${formatBarValue(
|
|
760
|
+
label: `${category}, ${groupKey}: ${formatBarValue(value2)}`
|
|
761
761
|
};
|
|
762
762
|
marks.push({
|
|
763
763
|
type: "rect",
|
|
@@ -772,7 +772,7 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
|
|
|
772
772
|
orient: "horizontal",
|
|
773
773
|
stackGroup: category
|
|
774
774
|
});
|
|
775
|
-
cumulativeValue +=
|
|
775
|
+
cumulativeValue += value2;
|
|
776
776
|
}
|
|
777
777
|
}
|
|
778
778
|
return marks;
|
|
@@ -781,8 +781,8 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
781
781
|
const marks = [];
|
|
782
782
|
for (const row of data) {
|
|
783
783
|
const category = String(row[categoryField] ?? "");
|
|
784
|
-
const
|
|
785
|
-
if (!Number.isFinite(
|
|
784
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
785
|
+
if (!Number.isFinite(value2)) continue;
|
|
786
786
|
const bandY = yScale(category);
|
|
787
787
|
if (bandY === void 0) continue;
|
|
788
788
|
let color2;
|
|
@@ -791,14 +791,14 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
791
791
|
resolveConditionalValue(row, conditionalColor) ?? getColor(scales, "__default__")
|
|
792
792
|
);
|
|
793
793
|
} else if (sequentialColor) {
|
|
794
|
-
color2 = getSequentialColor(scales,
|
|
794
|
+
color2 = getSequentialColor(scales, value2);
|
|
795
795
|
} else {
|
|
796
796
|
color2 = getColor(scales, "__default__");
|
|
797
797
|
}
|
|
798
|
-
const xPos =
|
|
799
|
-
const barWidth = Math.max(Math.abs(xScale(
|
|
798
|
+
const xPos = value2 >= 0 ? baseline : xScale(value2);
|
|
799
|
+
const barWidth = Math.max(Math.abs(xScale(value2) - baseline), MIN_BAR_WIDTH);
|
|
800
800
|
const aria = {
|
|
801
|
-
label: `${category}: ${formatBarValue(
|
|
801
|
+
label: `${category}: ${formatBarValue(value2)}`
|
|
802
802
|
};
|
|
803
803
|
marks.push({
|
|
804
804
|
type: "rect",
|
|
@@ -844,7 +844,7 @@ var LABEL_FONT_SIZE = 11;
|
|
|
844
844
|
var LABEL_FONT_WEIGHT = 600;
|
|
845
845
|
var LABEL_PADDING = 6;
|
|
846
846
|
var MIN_WIDTH_FOR_INSIDE_LABEL = 40;
|
|
847
|
-
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
847
|
+
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix) {
|
|
848
848
|
if (density === "none") return [];
|
|
849
849
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
850
850
|
const candidates = [];
|
|
@@ -860,6 +860,7 @@ function computeBarLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
|
860
860
|
const num = parseDisplayNumber(rawValue);
|
|
861
861
|
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
862
862
|
}
|
|
863
|
+
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
863
864
|
const textWidth = estimateTextWidth2(valuePart, LABEL_FONT_SIZE, LABEL_FONT_WEIGHT);
|
|
864
865
|
const textHeight = LABEL_FONT_SIZE * 1.2;
|
|
865
866
|
const isStacked = mark.cornerRadius === 0;
|
|
@@ -942,7 +943,13 @@ function computeBarLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
|
942
943
|
// src/charts/bar/index.ts
|
|
943
944
|
var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
944
945
|
const marks = computeBarMarks(spec, scales, chartArea, strategy);
|
|
945
|
-
const labels = computeBarLabels(
|
|
946
|
+
const labels = computeBarLabels(
|
|
947
|
+
marks,
|
|
948
|
+
chartArea,
|
|
949
|
+
spec.labels.density,
|
|
950
|
+
spec.labels.format,
|
|
951
|
+
spec.labels.prefix
|
|
952
|
+
);
|
|
946
953
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
947
954
|
marks[i].label = labels[i];
|
|
948
955
|
}
|
|
@@ -952,9 +959,9 @@ var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
952
959
|
// src/charts/column/compute.ts
|
|
953
960
|
import { abbreviateNumber as abbreviateNumber2, formatNumber as formatNumber2 } from "@opendata-ai/openchart-core";
|
|
954
961
|
var MIN_COLUMN_HEIGHT = 1;
|
|
955
|
-
function formatColumnValue(
|
|
956
|
-
if (Math.abs(
|
|
957
|
-
return formatNumber2(
|
|
962
|
+
function formatColumnValue(value2) {
|
|
963
|
+
if (Math.abs(value2) >= 1e3) return abbreviateNumber2(value2);
|
|
964
|
+
return formatNumber2(value2);
|
|
958
965
|
}
|
|
959
966
|
function computeColumnMarks(spec, scales, _chartArea, _strategy) {
|
|
960
967
|
const encoding = spec.encoding;
|
|
@@ -1019,8 +1026,8 @@ function computeSimpleColumns(data, categoryField, valueField, xScale, yScale, b
|
|
|
1019
1026
|
const marks = [];
|
|
1020
1027
|
for (const row of data) {
|
|
1021
1028
|
const category = String(row[categoryField] ?? "");
|
|
1022
|
-
const
|
|
1023
|
-
if (!Number.isFinite(
|
|
1029
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1030
|
+
if (!Number.isFinite(value2)) continue;
|
|
1024
1031
|
const bandX = xScale(category);
|
|
1025
1032
|
if (bandX === void 0) continue;
|
|
1026
1033
|
let color2;
|
|
@@ -1029,15 +1036,15 @@ function computeSimpleColumns(data, categoryField, valueField, xScale, yScale, b
|
|
|
1029
1036
|
resolveConditionalValue(row, conditionalColor) ?? getColor(scales, "__default__")
|
|
1030
1037
|
);
|
|
1031
1038
|
} else if (sequentialColor) {
|
|
1032
|
-
color2 = getSequentialColor(scales,
|
|
1039
|
+
color2 = getSequentialColor(scales, value2);
|
|
1033
1040
|
} else {
|
|
1034
1041
|
color2 = getColor(scales, "__default__");
|
|
1035
1042
|
}
|
|
1036
|
-
const yPos = yScale(
|
|
1043
|
+
const yPos = yScale(value2);
|
|
1037
1044
|
const columnHeight = Math.max(Math.abs(baseline - yPos), MIN_COLUMN_HEIGHT);
|
|
1038
|
-
const y2 =
|
|
1045
|
+
const y2 = value2 >= 0 ? yPos : baseline;
|
|
1039
1046
|
const aria = {
|
|
1040
|
-
label: `${category}: ${formatColumnValue(
|
|
1047
|
+
label: `${category}: ${formatColumnValue(value2)}`
|
|
1041
1048
|
};
|
|
1042
1049
|
marks.push({
|
|
1043
1050
|
type: "rect",
|
|
@@ -1058,17 +1065,17 @@ function computeColoredColumns(data, categoryField, valueField, colorField, xSca
|
|
|
1058
1065
|
const marks = [];
|
|
1059
1066
|
for (const row of data) {
|
|
1060
1067
|
const category = String(row[categoryField] ?? "");
|
|
1061
|
-
const
|
|
1062
|
-
if (!Number.isFinite(
|
|
1068
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1069
|
+
if (!Number.isFinite(value2)) continue;
|
|
1063
1070
|
const bandX = xScale(category);
|
|
1064
1071
|
if (bandX === void 0) continue;
|
|
1065
1072
|
const groupKey = String(row[colorField] ?? "");
|
|
1066
1073
|
const color2 = getColor(scales, groupKey);
|
|
1067
|
-
const yPos = yScale(
|
|
1074
|
+
const yPos = yScale(value2);
|
|
1068
1075
|
const columnHeight = Math.max(Math.abs(baseline - yPos), MIN_COLUMN_HEIGHT);
|
|
1069
|
-
const y2 =
|
|
1076
|
+
const y2 = value2 >= 0 ? yPos : baseline;
|
|
1070
1077
|
const aria = {
|
|
1071
|
-
label: `${category}, ${groupKey}: ${formatColumnValue(
|
|
1078
|
+
label: `${category}, ${groupKey}: ${formatColumnValue(value2)}`
|
|
1072
1079
|
};
|
|
1073
1080
|
marks.push({
|
|
1074
1081
|
type: "rect",
|
|
@@ -1094,14 +1101,14 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
|
|
|
1094
1101
|
let cumulativeValue = 0;
|
|
1095
1102
|
for (const row of rows) {
|
|
1096
1103
|
const groupKey = String(row[colorField] ?? "");
|
|
1097
|
-
const
|
|
1098
|
-
if (!Number.isFinite(
|
|
1104
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1105
|
+
if (!Number.isFinite(value2) || value2 <= 0) continue;
|
|
1099
1106
|
const color2 = getColor(scales, groupKey);
|
|
1100
|
-
const yTop = yScale(cumulativeValue +
|
|
1107
|
+
const yTop = yScale(cumulativeValue + value2);
|
|
1101
1108
|
const yBottom = yScale(cumulativeValue);
|
|
1102
1109
|
const columnHeight = Math.max(Math.abs(yBottom - yTop), MIN_COLUMN_HEIGHT);
|
|
1103
1110
|
const aria = {
|
|
1104
|
-
label: `${category}, ${groupKey}: ${formatColumnValue(
|
|
1111
|
+
label: `${category}, ${groupKey}: ${formatColumnValue(value2)}`
|
|
1105
1112
|
};
|
|
1106
1113
|
marks.push({
|
|
1107
1114
|
type: "rect",
|
|
@@ -1116,7 +1123,7 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
|
|
|
1116
1123
|
orient: "vertical",
|
|
1117
1124
|
stackGroup: category
|
|
1118
1125
|
});
|
|
1119
|
-
cumulativeValue +=
|
|
1126
|
+
cumulativeValue += value2;
|
|
1120
1127
|
}
|
|
1121
1128
|
}
|
|
1122
1129
|
return marks;
|
|
@@ -1131,7 +1138,7 @@ import {
|
|
|
1131
1138
|
var LABEL_FONT_SIZE2 = 10;
|
|
1132
1139
|
var LABEL_FONT_WEIGHT2 = 600;
|
|
1133
1140
|
var LABEL_OFFSET_Y = 6;
|
|
1134
|
-
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
1141
|
+
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix) {
|
|
1135
1142
|
if (density === "none") return [];
|
|
1136
1143
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
1137
1144
|
const formatter = buildD3Formatter2(labelFormat);
|
|
@@ -1146,6 +1153,7 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
|
1146
1153
|
const num = Number(rawValue.replace(/[^0-9.-]/g, ""));
|
|
1147
1154
|
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
1148
1155
|
}
|
|
1156
|
+
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
1149
1157
|
const numericValue = parseFloat(valuePart);
|
|
1150
1158
|
const isNegative = Number.isFinite(numericValue) && numericValue < 0;
|
|
1151
1159
|
const textWidth = estimateTextWidth3(valuePart, LABEL_FONT_SIZE2, LABEL_FONT_WEIGHT2);
|
|
@@ -1186,7 +1194,13 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
|
1186
1194
|
// src/charts/column/index.ts
|
|
1187
1195
|
var columnRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
1188
1196
|
const marks = computeColumnMarks(spec, scales, chartArea, strategy);
|
|
1189
|
-
const labels = computeColumnLabels(
|
|
1197
|
+
const labels = computeColumnLabels(
|
|
1198
|
+
marks,
|
|
1199
|
+
chartArea,
|
|
1200
|
+
spec.labels.density,
|
|
1201
|
+
spec.labels.format,
|
|
1202
|
+
spec.labels.prefix
|
|
1203
|
+
);
|
|
1190
1204
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
1191
1205
|
marks[i].label = labels[i];
|
|
1192
1206
|
}
|
|
@@ -1247,8 +1261,8 @@ function computeDumbbellMarks(data, valueField, categoryField, colorField, xScal
|
|
|
1247
1261
|
const cy = bandY + bandwidth / 2;
|
|
1248
1262
|
const xValues = [];
|
|
1249
1263
|
for (const row of rows) {
|
|
1250
|
-
const
|
|
1251
|
-
if (Number.isFinite(
|
|
1264
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1265
|
+
if (Number.isFinite(value2)) xValues.push(value2);
|
|
1252
1266
|
}
|
|
1253
1267
|
if (xValues.length === 0) continue;
|
|
1254
1268
|
const minVal = Math.min(...xValues);
|
|
@@ -1272,13 +1286,13 @@ function computeDumbbellMarks(data, valueField, categoryField, colorField, xScal
|
|
|
1272
1286
|
});
|
|
1273
1287
|
}
|
|
1274
1288
|
for (const row of rows) {
|
|
1275
|
-
const
|
|
1276
|
-
if (!Number.isFinite(
|
|
1277
|
-
const cx = xScale(
|
|
1289
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1290
|
+
if (!Number.isFinite(value2)) continue;
|
|
1291
|
+
const cx = xScale(value2);
|
|
1278
1292
|
const colorCategory = String(row[colorField] ?? "");
|
|
1279
1293
|
const color2 = getColor(scales, colorCategory);
|
|
1280
1294
|
const dotAria = {
|
|
1281
|
-
label: `${category}, ${colorCategory}: ${
|
|
1295
|
+
label: `${category}, ${colorCategory}: ${value2}`
|
|
1282
1296
|
};
|
|
1283
1297
|
marks.push({
|
|
1284
1298
|
type: "point",
|
|
@@ -1299,13 +1313,13 @@ function computeLollipopMarks(data, valueField, categoryField, xScale, yScale, b
|
|
|
1299
1313
|
const marks = [];
|
|
1300
1314
|
for (const row of data) {
|
|
1301
1315
|
const category = String(row[categoryField] ?? "");
|
|
1302
|
-
const
|
|
1303
|
-
if (!Number.isFinite(
|
|
1316
|
+
const value2 = Number(row[valueField] ?? 0);
|
|
1317
|
+
if (!Number.isFinite(value2)) continue;
|
|
1304
1318
|
const bandY = yScale(category);
|
|
1305
1319
|
if (bandY === void 0) continue;
|
|
1306
|
-
const cx = xScale(
|
|
1320
|
+
const cx = xScale(value2);
|
|
1307
1321
|
const cy = bandY + bandwidth / 2;
|
|
1308
|
-
const color2 = isSequentialColor ? getSequentialColor(scales,
|
|
1322
|
+
const color2 = isSequentialColor ? getSequentialColor(scales, value2) : getColor(scales, "__default__");
|
|
1309
1323
|
const stemX = Math.min(baseline, cx);
|
|
1310
1324
|
const stemWidth = Math.abs(cx - baseline);
|
|
1311
1325
|
if (stemWidth > 0) {
|
|
@@ -1324,7 +1338,7 @@ function computeLollipopMarks(data, valueField, categoryField, xScale, yScale, b
|
|
|
1324
1338
|
});
|
|
1325
1339
|
}
|
|
1326
1340
|
const dotAria = {
|
|
1327
|
-
label: `${category}: ${
|
|
1341
|
+
label: `${category}: ${value2}`
|
|
1328
1342
|
};
|
|
1329
1343
|
marks.push({
|
|
1330
1344
|
type: "point",
|
|
@@ -1346,15 +1360,16 @@ import { estimateTextWidth as estimateTextWidth4, resolveCollisions as resolveCo
|
|
|
1346
1360
|
var LABEL_FONT_SIZE3 = 11;
|
|
1347
1361
|
var LABEL_FONT_WEIGHT3 = 600;
|
|
1348
1362
|
var LABEL_OFFSET_X = 10;
|
|
1349
|
-
function computeDotLabels(marks, _chartArea, density = "auto") {
|
|
1363
|
+
function computeDotLabels(marks, _chartArea, density = "auto", labelPrefix) {
|
|
1350
1364
|
if (density === "none") return [];
|
|
1351
1365
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
1352
1366
|
const candidates = [];
|
|
1353
1367
|
for (const mark of targetMarks) {
|
|
1354
1368
|
const ariaLabel = mark.aria.label;
|
|
1355
1369
|
const lastColon = ariaLabel.lastIndexOf(":");
|
|
1356
|
-
|
|
1370
|
+
let valuePart = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
1357
1371
|
if (!valuePart) continue;
|
|
1372
|
+
if (labelPrefix) valuePart = labelPrefix + valuePart;
|
|
1358
1373
|
const textWidth = estimateTextWidth4(valuePart, LABEL_FONT_SIZE3, LABEL_FONT_WEIGHT3);
|
|
1359
1374
|
const textHeight = LABEL_FONT_SIZE3 * 1.2;
|
|
1360
1375
|
candidates.push({
|
|
@@ -1392,7 +1407,7 @@ function computeDotLabels(marks, _chartArea, density = "auto") {
|
|
|
1392
1407
|
var dotRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
1393
1408
|
const marks = computeDotMarks(spec, scales, chartArea, strategy);
|
|
1394
1409
|
const pointMarks = marks.filter((m) => m.type === "point");
|
|
1395
|
-
const labels = computeDotLabels(pointMarks, chartArea, spec.labels.density);
|
|
1410
|
+
const labels = computeDotLabels(pointMarks, chartArea, spec.labels.density, spec.labels.prefix);
|
|
1396
1411
|
let labelIdx = 0;
|
|
1397
1412
|
for (const mark of marks) {
|
|
1398
1413
|
if (mark.type === "point" && labelIdx < labels.length) {
|
|
@@ -1405,7 +1420,7 @@ var dotRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
1405
1420
|
|
|
1406
1421
|
// ../../node_modules/.bun/d3-shape@3.2.0/node_modules/d3-shape/src/constant.js
|
|
1407
1422
|
function constant_default(x2) {
|
|
1408
|
-
return function
|
|
1423
|
+
return function constant2() {
|
|
1409
1424
|
return x2;
|
|
1410
1425
|
};
|
|
1411
1426
|
}
|
|
@@ -1842,12 +1857,12 @@ function identity_default(d) {
|
|
|
1842
1857
|
|
|
1843
1858
|
// ../../node_modules/.bun/d3-shape@3.2.0/node_modules/d3-shape/src/pie.js
|
|
1844
1859
|
function pie_default() {
|
|
1845
|
-
var
|
|
1860
|
+
var value2 = identity_default, sortValues = descending_default, sort = null, startAngle = constant_default(0), endAngle = constant_default(tau), padAngle = constant_default(0);
|
|
1846
1861
|
function pie(data) {
|
|
1847
|
-
var i, n = (data = array_default(data)).length, j, k,
|
|
1862
|
+
var i, n = (data = array_default(data)).length, j, k, sum2 = 0, index = new Array(n), arcs = new Array(n), a0 = +startAngle.apply(this, arguments), da = Math.min(tau, Math.max(-tau, endAngle.apply(this, arguments) - a0)), a1, p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)), pa = p * (da < 0 ? -1 : 1), v;
|
|
1848
1863
|
for (i = 0; i < n; ++i) {
|
|
1849
|
-
if ((v = arcs[index[i] = i] = +
|
|
1850
|
-
|
|
1864
|
+
if ((v = arcs[index[i] = i] = +value2(data[i], i, data)) > 0) {
|
|
1865
|
+
sum2 += v;
|
|
1851
1866
|
}
|
|
1852
1867
|
}
|
|
1853
1868
|
if (sortValues != null) index.sort(function(i2, j2) {
|
|
@@ -1856,7 +1871,7 @@ function pie_default() {
|
|
|
1856
1871
|
else if (sort != null) index.sort(function(i2, j2) {
|
|
1857
1872
|
return sort(data[i2], data[j2]);
|
|
1858
1873
|
});
|
|
1859
|
-
for (i = 0, k =
|
|
1874
|
+
for (i = 0, k = sum2 ? (da - n * pa) / sum2 : 0; i < n; ++i, a0 = a1) {
|
|
1860
1875
|
j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
|
|
1861
1876
|
data: data[j],
|
|
1862
1877
|
index: i,
|
|
@@ -1869,7 +1884,7 @@ function pie_default() {
|
|
|
1869
1884
|
return arcs;
|
|
1870
1885
|
}
|
|
1871
1886
|
pie.value = function(_) {
|
|
1872
|
-
return arguments.length ? (
|
|
1887
|
+
return arguments.length ? (value2 = typeof _ === "function" ? _ : constant_default(+_), pie) : value2;
|
|
1873
1888
|
};
|
|
1874
1889
|
pie.sortValues = function(_) {
|
|
1875
1890
|
return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
|
|
@@ -2253,12 +2268,12 @@ function stackSeries(key) {
|
|
|
2253
2268
|
return series;
|
|
2254
2269
|
}
|
|
2255
2270
|
function stack_default() {
|
|
2256
|
-
var keys = constant_default([]), order = none_default2, offset = none_default,
|
|
2271
|
+
var keys = constant_default([]), order = none_default2, offset = none_default, value2 = stackValue;
|
|
2257
2272
|
function stack(data) {
|
|
2258
2273
|
var sz = Array.from(keys.apply(this, arguments), stackSeries), i, n = sz.length, j = -1, oz;
|
|
2259
2274
|
for (const d of data) {
|
|
2260
2275
|
for (i = 0, ++j; i < n; ++i) {
|
|
2261
|
-
(sz[i][j] = [0, +
|
|
2276
|
+
(sz[i][j] = [0, +value2(d, sz[i].key, j, data)]).data = d;
|
|
2262
2277
|
}
|
|
2263
2278
|
}
|
|
2264
2279
|
for (i = 0, oz = array_default(order(sz)); i < n; ++i) {
|
|
@@ -2271,7 +2286,7 @@ function stack_default() {
|
|
|
2271
2286
|
return arguments.length ? (keys = typeof _ === "function" ? _ : constant_default(Array.from(_)), stack) : keys;
|
|
2272
2287
|
};
|
|
2273
2288
|
stack.value = function(_) {
|
|
2274
|
-
return arguments.length ? (
|
|
2289
|
+
return arguments.length ? (value2 = typeof _ === "function" ? _ : constant_default(+_), stack) : value2;
|
|
2275
2290
|
};
|
|
2276
2291
|
stack.order = function(_) {
|
|
2277
2292
|
return arguments.length ? (order = _ == null ? none_default2 : typeof _ === "function" ? _ : constant_default(Array.from(_)), stack) : order;
|
|
@@ -2695,7 +2710,7 @@ var DEFAULT_PALETTE = [
|
|
|
2695
2710
|
"#858078"
|
|
2696
2711
|
];
|
|
2697
2712
|
function groupSmallSlices(slices, threshold2) {
|
|
2698
|
-
const total = slices.reduce((
|
|
2713
|
+
const total = slices.reduce((sum2, s) => sum2 + s.value, 0);
|
|
2699
2714
|
if (total === 0) return slices;
|
|
2700
2715
|
const big = [];
|
|
2701
2716
|
let otherValue = 0;
|
|
@@ -2733,13 +2748,13 @@ function computePieMarks(spec, scales, chartArea, _strategy, isDonut = false) {
|
|
|
2733
2748
|
categoryRows.set(cat, row);
|
|
2734
2749
|
}
|
|
2735
2750
|
}
|
|
2736
|
-
for (const [label,
|
|
2751
|
+
for (const [label, value2] of categoryTotals) {
|
|
2737
2752
|
slices.push({
|
|
2738
2753
|
label,
|
|
2739
|
-
value,
|
|
2754
|
+
value: value2,
|
|
2740
2755
|
originalRow: categoryRows.get(label) ?? {
|
|
2741
2756
|
[categoryField]: label,
|
|
2742
|
-
[valueChannel.field]:
|
|
2757
|
+
[valueChannel.field]: value2
|
|
2743
2758
|
}
|
|
2744
2759
|
});
|
|
2745
2760
|
}
|
|
@@ -2763,8 +2778,8 @@ function computePieMarks(spec, scales, chartArea, _strategy, isDonut = false) {
|
|
|
2763
2778
|
const innerRadius = isDonut ? outerRadius * 0.6 : 0;
|
|
2764
2779
|
const arcGenerator = arc_default().innerRadius(innerRadius).outerRadius(outerRadius);
|
|
2765
2780
|
const marks = [];
|
|
2766
|
-
const
|
|
2767
|
-
const total = slices.reduce((
|
|
2781
|
+
const center2 = { x: centerX, y: centerY };
|
|
2782
|
+
const total = slices.reduce((sum2, s) => sum2 + s.value, 0);
|
|
2768
2783
|
for (let i = 0; i < arcs.length; i++) {
|
|
2769
2784
|
const arcDatum = arcs[i];
|
|
2770
2785
|
const slice2 = arcDatum.data;
|
|
@@ -2788,7 +2803,7 @@ function computePieMarks(spec, scales, chartArea, _strategy, isDonut = false) {
|
|
|
2788
2803
|
x: centroidResult[0] + centerX,
|
|
2789
2804
|
y: centroidResult[1] + centerY
|
|
2790
2805
|
},
|
|
2791
|
-
center,
|
|
2806
|
+
center: center2,
|
|
2792
2807
|
innerRadius,
|
|
2793
2808
|
outerRadius,
|
|
2794
2809
|
startAngle: arcDatum.startAngle,
|
|
@@ -2997,7 +3012,7 @@ function bisector(f) {
|
|
|
2997
3012
|
compare2 = f;
|
|
2998
3013
|
delta = f;
|
|
2999
3014
|
}
|
|
3000
|
-
function
|
|
3015
|
+
function left2(a, x2, lo = 0, hi = a.length) {
|
|
3001
3016
|
if (lo < hi) {
|
|
3002
3017
|
if (compare1(x2, x2) !== 0) return hi;
|
|
3003
3018
|
do {
|
|
@@ -3008,7 +3023,7 @@ function bisector(f) {
|
|
|
3008
3023
|
}
|
|
3009
3024
|
return lo;
|
|
3010
3025
|
}
|
|
3011
|
-
function
|
|
3026
|
+
function right2(a, x2, lo = 0, hi = a.length) {
|
|
3012
3027
|
if (lo < hi) {
|
|
3013
3028
|
if (compare1(x2, x2) !== 0) return hi;
|
|
3014
3029
|
do {
|
|
@@ -3019,11 +3034,11 @@ function bisector(f) {
|
|
|
3019
3034
|
}
|
|
3020
3035
|
return lo;
|
|
3021
3036
|
}
|
|
3022
|
-
function
|
|
3023
|
-
const i =
|
|
3037
|
+
function center2(a, x2, lo = 0, hi = a.length) {
|
|
3038
|
+
const i = left2(a, x2, lo, hi - 1);
|
|
3024
3039
|
return i > lo && delta(a[i - 1], x2) > -delta(a[i], x2) ? i - 1 : i;
|
|
3025
3040
|
}
|
|
3026
|
-
return { left, center, right };
|
|
3041
|
+
return { left: left2, center: center2, right: right2 };
|
|
3027
3042
|
}
|
|
3028
3043
|
function zero() {
|
|
3029
3044
|
return 0;
|
|
@@ -3043,33 +3058,33 @@ var bisect_default = bisectRight;
|
|
|
3043
3058
|
|
|
3044
3059
|
// ../../node_modules/.bun/d3-array@3.2.4/node_modules/d3-array/src/extent.js
|
|
3045
3060
|
function extent(values, valueof) {
|
|
3046
|
-
let
|
|
3047
|
-
let
|
|
3061
|
+
let min4;
|
|
3062
|
+
let max4;
|
|
3048
3063
|
if (valueof === void 0) {
|
|
3049
|
-
for (const
|
|
3050
|
-
if (
|
|
3051
|
-
if (
|
|
3052
|
-
if (
|
|
3064
|
+
for (const value2 of values) {
|
|
3065
|
+
if (value2 != null) {
|
|
3066
|
+
if (min4 === void 0) {
|
|
3067
|
+
if (value2 >= value2) min4 = max4 = value2;
|
|
3053
3068
|
} else {
|
|
3054
|
-
if (
|
|
3055
|
-
if (
|
|
3069
|
+
if (min4 > value2) min4 = value2;
|
|
3070
|
+
if (max4 < value2) max4 = value2;
|
|
3056
3071
|
}
|
|
3057
3072
|
}
|
|
3058
3073
|
}
|
|
3059
3074
|
} else {
|
|
3060
3075
|
let index = -1;
|
|
3061
|
-
for (let
|
|
3062
|
-
if ((
|
|
3063
|
-
if (
|
|
3064
|
-
if (
|
|
3076
|
+
for (let value2 of values) {
|
|
3077
|
+
if ((value2 = valueof(value2, ++index, values)) != null) {
|
|
3078
|
+
if (min4 === void 0) {
|
|
3079
|
+
if (value2 >= value2) min4 = max4 = value2;
|
|
3065
3080
|
} else {
|
|
3066
|
-
if (
|
|
3067
|
-
if (
|
|
3081
|
+
if (min4 > value2) min4 = value2;
|
|
3082
|
+
if (max4 < value2) max4 = value2;
|
|
3068
3083
|
}
|
|
3069
3084
|
}
|
|
3070
3085
|
}
|
|
3071
3086
|
}
|
|
3072
|
-
return [
|
|
3087
|
+
return [min4, max4];
|
|
3073
3088
|
}
|
|
3074
3089
|
|
|
3075
3090
|
// ../../node_modules/.bun/internmap@2.0.3/node_modules/internmap/src/index.js
|
|
@@ -3077,7 +3092,7 @@ var InternMap = class extends Map {
|
|
|
3077
3092
|
constructor(entries, key = keyof) {
|
|
3078
3093
|
super();
|
|
3079
3094
|
Object.defineProperties(this, { _intern: { value: /* @__PURE__ */ new Map() }, _key: { value: key } });
|
|
3080
|
-
if (entries != null) for (const [key2,
|
|
3095
|
+
if (entries != null) for (const [key2, value2] of entries) this.set(key2, value2);
|
|
3081
3096
|
}
|
|
3082
3097
|
get(key) {
|
|
3083
3098
|
return super.get(intern_get(this, key));
|
|
@@ -3085,33 +3100,33 @@ var InternMap = class extends Map {
|
|
|
3085
3100
|
has(key) {
|
|
3086
3101
|
return super.has(intern_get(this, key));
|
|
3087
3102
|
}
|
|
3088
|
-
set(key,
|
|
3089
|
-
return super.set(intern_set(this, key),
|
|
3103
|
+
set(key, value2) {
|
|
3104
|
+
return super.set(intern_set(this, key), value2);
|
|
3090
3105
|
}
|
|
3091
3106
|
delete(key) {
|
|
3092
3107
|
return super.delete(intern_delete(this, key));
|
|
3093
3108
|
}
|
|
3094
3109
|
};
|
|
3095
|
-
function intern_get({ _intern, _key },
|
|
3096
|
-
const key = _key(
|
|
3097
|
-
return _intern.has(key) ? _intern.get(key) :
|
|
3110
|
+
function intern_get({ _intern, _key }, value2) {
|
|
3111
|
+
const key = _key(value2);
|
|
3112
|
+
return _intern.has(key) ? _intern.get(key) : value2;
|
|
3098
3113
|
}
|
|
3099
|
-
function intern_set({ _intern, _key },
|
|
3100
|
-
const key = _key(
|
|
3114
|
+
function intern_set({ _intern, _key }, value2) {
|
|
3115
|
+
const key = _key(value2);
|
|
3101
3116
|
if (_intern.has(key)) return _intern.get(key);
|
|
3102
|
-
_intern.set(key,
|
|
3103
|
-
return
|
|
3117
|
+
_intern.set(key, value2);
|
|
3118
|
+
return value2;
|
|
3104
3119
|
}
|
|
3105
|
-
function intern_delete({ _intern, _key },
|
|
3106
|
-
const key = _key(
|
|
3120
|
+
function intern_delete({ _intern, _key }, value2) {
|
|
3121
|
+
const key = _key(value2);
|
|
3107
3122
|
if (_intern.has(key)) {
|
|
3108
|
-
|
|
3123
|
+
value2 = _intern.get(key);
|
|
3109
3124
|
_intern.delete(key);
|
|
3110
3125
|
}
|
|
3111
|
-
return
|
|
3126
|
+
return value2;
|
|
3112
3127
|
}
|
|
3113
|
-
function keyof(
|
|
3114
|
-
return
|
|
3128
|
+
function keyof(value2) {
|
|
3129
|
+
return value2 !== null && typeof value2 === "object" ? value2.valueOf() : value2;
|
|
3115
3130
|
}
|
|
3116
3131
|
|
|
3117
3132
|
// ../../node_modules/.bun/d3-array@3.2.4/node_modules/d3-array/src/ticks.js
|
|
@@ -3166,42 +3181,42 @@ function tickStep(start, stop, count) {
|
|
|
3166
3181
|
|
|
3167
3182
|
// ../../node_modules/.bun/d3-array@3.2.4/node_modules/d3-array/src/max.js
|
|
3168
3183
|
function max2(values, valueof) {
|
|
3169
|
-
let
|
|
3184
|
+
let max4;
|
|
3170
3185
|
if (valueof === void 0) {
|
|
3171
|
-
for (const
|
|
3172
|
-
if (
|
|
3173
|
-
|
|
3186
|
+
for (const value2 of values) {
|
|
3187
|
+
if (value2 != null && (max4 < value2 || max4 === void 0 && value2 >= value2)) {
|
|
3188
|
+
max4 = value2;
|
|
3174
3189
|
}
|
|
3175
3190
|
}
|
|
3176
3191
|
} else {
|
|
3177
3192
|
let index = -1;
|
|
3178
|
-
for (let
|
|
3179
|
-
if ((
|
|
3180
|
-
|
|
3193
|
+
for (let value2 of values) {
|
|
3194
|
+
if ((value2 = valueof(value2, ++index, values)) != null && (max4 < value2 || max4 === void 0 && value2 >= value2)) {
|
|
3195
|
+
max4 = value2;
|
|
3181
3196
|
}
|
|
3182
3197
|
}
|
|
3183
3198
|
}
|
|
3184
|
-
return
|
|
3199
|
+
return max4;
|
|
3185
3200
|
}
|
|
3186
3201
|
|
|
3187
3202
|
// ../../node_modules/.bun/d3-array@3.2.4/node_modules/d3-array/src/min.js
|
|
3188
3203
|
function min2(values, valueof) {
|
|
3189
|
-
let
|
|
3204
|
+
let min4;
|
|
3190
3205
|
if (valueof === void 0) {
|
|
3191
|
-
for (const
|
|
3192
|
-
if (
|
|
3193
|
-
|
|
3206
|
+
for (const value2 of values) {
|
|
3207
|
+
if (value2 != null && (min4 > value2 || min4 === void 0 && value2 >= value2)) {
|
|
3208
|
+
min4 = value2;
|
|
3194
3209
|
}
|
|
3195
3210
|
}
|
|
3196
3211
|
} else {
|
|
3197
3212
|
let index = -1;
|
|
3198
|
-
for (let
|
|
3199
|
-
if ((
|
|
3200
|
-
|
|
3213
|
+
for (let value2 of values) {
|
|
3214
|
+
if ((value2 = valueof(value2, ++index, values)) != null && (min4 > value2 || min4 === void 0 && value2 >= value2)) {
|
|
3215
|
+
min4 = value2;
|
|
3201
3216
|
}
|
|
3202
3217
|
}
|
|
3203
3218
|
}
|
|
3204
|
-
return
|
|
3219
|
+
return min4;
|
|
3205
3220
|
}
|
|
3206
3221
|
|
|
3207
3222
|
// ../../node_modules/.bun/d3-array@3.2.4/node_modules/d3-array/src/quantile.js
|
|
@@ -3271,9 +3286,9 @@ function ordinal() {
|
|
|
3271
3286
|
scale.domain = function(_) {
|
|
3272
3287
|
if (!arguments.length) return domain.slice();
|
|
3273
3288
|
domain = [], index = new InternMap();
|
|
3274
|
-
for (const
|
|
3275
|
-
if (index.has(
|
|
3276
|
-
index.set(
|
|
3289
|
+
for (const value2 of _) {
|
|
3290
|
+
if (index.has(value2)) continue;
|
|
3291
|
+
index.set(value2, domain.push(value2) - 1);
|
|
3277
3292
|
}
|
|
3278
3293
|
return scale;
|
|
3279
3294
|
};
|
|
@@ -3623,12 +3638,12 @@ function rgb_formatRgb() {
|
|
|
3623
3638
|
function clampa(opacity) {
|
|
3624
3639
|
return isNaN(opacity) ? 1 : Math.max(0, Math.min(1, opacity));
|
|
3625
3640
|
}
|
|
3626
|
-
function clampi(
|
|
3627
|
-
return Math.max(0, Math.min(255, Math.round(
|
|
3641
|
+
function clampi(value2) {
|
|
3642
|
+
return Math.max(0, Math.min(255, Math.round(value2) || 0));
|
|
3628
3643
|
}
|
|
3629
|
-
function hex(
|
|
3630
|
-
|
|
3631
|
-
return (
|
|
3644
|
+
function hex(value2) {
|
|
3645
|
+
value2 = clampi(value2);
|
|
3646
|
+
return (value2 < 16 ? "0" : "") + value2.toString(16);
|
|
3632
3647
|
}
|
|
3633
3648
|
function hsla(h, s, l, a) {
|
|
3634
3649
|
if (a <= 0) h = s = l = NaN;
|
|
@@ -3642,12 +3657,12 @@ function hslConvert(o) {
|
|
|
3642
3657
|
if (!o) return new Hsl();
|
|
3643
3658
|
if (o instanceof Hsl) return o;
|
|
3644
3659
|
o = o.rgb();
|
|
3645
|
-
var r = o.r / 255, g = o.g / 255, b = o.b / 255,
|
|
3660
|
+
var r = o.r / 255, g = o.g / 255, b = o.b / 255, min4 = Math.min(r, g, b), max4 = Math.max(r, g, b), h = NaN, s = max4 - min4, l = (max4 + min4) / 2;
|
|
3646
3661
|
if (s) {
|
|
3647
|
-
if (r ===
|
|
3648
|
-
else if (g ===
|
|
3662
|
+
if (r === max4) h = (g - b) / s + (g < b) * 6;
|
|
3663
|
+
else if (g === max4) h = (b - r) / s + 2;
|
|
3649
3664
|
else h = (r - g) / s + 4;
|
|
3650
|
-
s /= l < 0.5 ?
|
|
3665
|
+
s /= l < 0.5 ? max4 + min4 : 2 - max4 - min4;
|
|
3651
3666
|
h *= 60;
|
|
3652
3667
|
} else {
|
|
3653
3668
|
s = l > 0 && l < 1 ? 0 : h;
|
|
@@ -3692,12 +3707,12 @@ define_default(Hsl, hsl, extend(Color, {
|
|
|
3692
3707
|
return `${a === 1 ? "hsl(" : "hsla("}${clamph(this.h)}, ${clampt(this.s) * 100}%, ${clampt(this.l) * 100}%${a === 1 ? ")" : `, ${a})`}`;
|
|
3693
3708
|
}
|
|
3694
3709
|
}));
|
|
3695
|
-
function clamph(
|
|
3696
|
-
|
|
3697
|
-
return
|
|
3710
|
+
function clamph(value2) {
|
|
3711
|
+
value2 = (value2 || 0) % 360;
|
|
3712
|
+
return value2 < 0 ? value2 + 360 : value2;
|
|
3698
3713
|
}
|
|
3699
|
-
function clampt(
|
|
3700
|
-
return Math.max(0, Math.min(1,
|
|
3714
|
+
function clampt(value2) {
|
|
3715
|
+
return Math.max(0, Math.min(1, value2 || 0));
|
|
3701
3716
|
}
|
|
3702
3717
|
function hsl2rgb(h, m1, m2) {
|
|
3703
3718
|
return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
|
|
@@ -4018,11 +4033,11 @@ function exponent_default(x2) {
|
|
|
4018
4033
|
|
|
4019
4034
|
// ../../node_modules/.bun/d3-format@3.1.2/node_modules/d3-format/src/formatGroup.js
|
|
4020
4035
|
function formatGroup_default(grouping, thousands) {
|
|
4021
|
-
return function(
|
|
4022
|
-
var i =
|
|
4036
|
+
return function(value2, width) {
|
|
4037
|
+
var i = value2.length, t = [], j = 0, g = grouping[0], length = 0;
|
|
4023
4038
|
while (i > 0 && g > 0) {
|
|
4024
4039
|
if (length + g + 1 > width) g = Math.max(1, width - length);
|
|
4025
|
-
t.push(
|
|
4040
|
+
t.push(value2.substring(i -= g, i + g));
|
|
4026
4041
|
if ((length += g + 1) > width) break;
|
|
4027
4042
|
g = grouping[j = (j + 1) % grouping.length];
|
|
4028
4043
|
}
|
|
@@ -4032,8 +4047,8 @@ function formatGroup_default(grouping, thousands) {
|
|
|
4032
4047
|
|
|
4033
4048
|
// ../../node_modules/.bun/d3-format@3.1.2/node_modules/d3-format/src/formatNumerals.js
|
|
4034
4049
|
function formatNumerals_default(numerals) {
|
|
4035
|
-
return function(
|
|
4036
|
-
return
|
|
4050
|
+
return function(value2) {
|
|
4051
|
+
return value2.replace(/[0-9]/g, function(i) {
|
|
4037
4052
|
return numerals[+i];
|
|
4038
4053
|
});
|
|
4039
4054
|
};
|
|
@@ -4147,58 +4162,58 @@ function locale_default(locale3) {
|
|
|
4147
4162
|
var prefix = (options && options.prefix !== void 0 ? options.prefix : "") + (symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : ""), suffix = (symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "") + (options && options.suffix !== void 0 ? options.suffix : "");
|
|
4148
4163
|
var formatType = formatTypes_default[type], maybeSuffix = /[defgprs%]/.test(type);
|
|
4149
4164
|
precision = precision === void 0 ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
|
|
4150
|
-
function format2(
|
|
4165
|
+
function format2(value2) {
|
|
4151
4166
|
var valuePrefix = prefix, valueSuffix = suffix, i, n, c;
|
|
4152
4167
|
if (type === "c") {
|
|
4153
|
-
valueSuffix = formatType(
|
|
4154
|
-
|
|
4168
|
+
valueSuffix = formatType(value2) + valueSuffix;
|
|
4169
|
+
value2 = "";
|
|
4155
4170
|
} else {
|
|
4156
|
-
|
|
4157
|
-
var valueNegative =
|
|
4158
|
-
|
|
4159
|
-
if (trim)
|
|
4160
|
-
if (valueNegative && +
|
|
4171
|
+
value2 = +value2;
|
|
4172
|
+
var valueNegative = value2 < 0 || 1 / value2 < 0;
|
|
4173
|
+
value2 = isNaN(value2) ? nan : formatType(Math.abs(value2), precision);
|
|
4174
|
+
if (trim) value2 = formatTrim_default(value2);
|
|
4175
|
+
if (valueNegative && +value2 === 0 && sign2 !== "+") valueNegative = false;
|
|
4161
4176
|
valuePrefix = (valueNegative ? sign2 === "(" ? sign2 : minus : sign2 === "-" || sign2 === "(" ? "" : sign2) + valuePrefix;
|
|
4162
|
-
valueSuffix = (type === "s" && !isNaN(
|
|
4177
|
+
valueSuffix = (type === "s" && !isNaN(value2) && prefixExponent !== void 0 ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign2 === "(" ? ")" : "");
|
|
4163
4178
|
if (maybeSuffix) {
|
|
4164
|
-
i = -1, n =
|
|
4179
|
+
i = -1, n = value2.length;
|
|
4165
4180
|
while (++i < n) {
|
|
4166
|
-
if (c =
|
|
4167
|
-
valueSuffix = (c === 46 ? decimal +
|
|
4168
|
-
|
|
4181
|
+
if (c = value2.charCodeAt(i), 48 > c || c > 57) {
|
|
4182
|
+
valueSuffix = (c === 46 ? decimal + value2.slice(i + 1) : value2.slice(i)) + valueSuffix;
|
|
4183
|
+
value2 = value2.slice(0, i);
|
|
4169
4184
|
break;
|
|
4170
4185
|
}
|
|
4171
4186
|
}
|
|
4172
4187
|
}
|
|
4173
4188
|
}
|
|
4174
|
-
if (comma && !zero3)
|
|
4175
|
-
var length = valuePrefix.length +
|
|
4176
|
-
if (comma && zero3)
|
|
4189
|
+
if (comma && !zero3) value2 = group(value2, Infinity);
|
|
4190
|
+
var length = valuePrefix.length + value2.length + valueSuffix.length, padding = length < width ? new Array(width - length + 1).join(fill) : "";
|
|
4191
|
+
if (comma && zero3) value2 = group(padding + value2, padding.length ? width - valueSuffix.length : Infinity), padding = "";
|
|
4177
4192
|
switch (align) {
|
|
4178
4193
|
case "<":
|
|
4179
|
-
|
|
4194
|
+
value2 = valuePrefix + value2 + valueSuffix + padding;
|
|
4180
4195
|
break;
|
|
4181
4196
|
case "=":
|
|
4182
|
-
|
|
4197
|
+
value2 = valuePrefix + padding + value2 + valueSuffix;
|
|
4183
4198
|
break;
|
|
4184
4199
|
case "^":
|
|
4185
|
-
|
|
4200
|
+
value2 = padding.slice(0, length = padding.length >> 1) + valuePrefix + value2 + valueSuffix + padding.slice(length);
|
|
4186
4201
|
break;
|
|
4187
4202
|
default:
|
|
4188
|
-
|
|
4203
|
+
value2 = padding + valuePrefix + value2 + valueSuffix;
|
|
4189
4204
|
break;
|
|
4190
4205
|
}
|
|
4191
|
-
return numerals(
|
|
4206
|
+
return numerals(value2);
|
|
4192
4207
|
}
|
|
4193
4208
|
format2.toString = function() {
|
|
4194
4209
|
return specifier + "";
|
|
4195
4210
|
};
|
|
4196
4211
|
return format2;
|
|
4197
4212
|
}
|
|
4198
|
-
function formatPrefix2(specifier,
|
|
4199
|
-
var e = Math.max(-8, Math.min(8, Math.floor(exponent_default(
|
|
4200
|
-
return function(
|
|
4201
|
-
return f(k *
|
|
4213
|
+
function formatPrefix2(specifier, value2) {
|
|
4214
|
+
var e = Math.max(-8, Math.min(8, Math.floor(exponent_default(value2) / 3))) * 3, k = Math.pow(10, -e), f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), { suffix: prefixes[8 + e / 3] });
|
|
4215
|
+
return function(value3) {
|
|
4216
|
+
return f(k * value3);
|
|
4202
4217
|
};
|
|
4203
4218
|
}
|
|
4204
4219
|
return {
|
|
@@ -4229,14 +4244,14 @@ function precisionFixed_default(step) {
|
|
|
4229
4244
|
}
|
|
4230
4245
|
|
|
4231
4246
|
// ../../node_modules/.bun/d3-format@3.1.2/node_modules/d3-format/src/precisionPrefix.js
|
|
4232
|
-
function precisionPrefix_default(step,
|
|
4233
|
-
return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent_default(
|
|
4247
|
+
function precisionPrefix_default(step, value2) {
|
|
4248
|
+
return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent_default(value2) / 3))) * 3 - exponent_default(Math.abs(step)));
|
|
4234
4249
|
}
|
|
4235
4250
|
|
|
4236
4251
|
// ../../node_modules/.bun/d3-format@3.1.2/node_modules/d3-format/src/precisionRound.js
|
|
4237
|
-
function precisionRound_default(step,
|
|
4238
|
-
step = Math.abs(step),
|
|
4239
|
-
return Math.max(0, exponent_default(
|
|
4252
|
+
function precisionRound_default(step, max4) {
|
|
4253
|
+
step = Math.abs(step), max4 = Math.abs(max4) - step;
|
|
4254
|
+
return Math.max(0, exponent_default(max4) - exponent_default(step)) + 1;
|
|
4240
4255
|
}
|
|
4241
4256
|
|
|
4242
4257
|
// ../../node_modules/.bun/d3-scale@4.0.2/node_modules/d3-scale/src/tickFormat.js
|
|
@@ -4245,9 +4260,9 @@ function tickFormat(start, stop, count, specifier) {
|
|
|
4245
4260
|
specifier = formatSpecifier(specifier == null ? ",f" : specifier);
|
|
4246
4261
|
switch (specifier.type) {
|
|
4247
4262
|
case "s": {
|
|
4248
|
-
var
|
|
4249
|
-
if (specifier.precision == null && !isNaN(precision = precisionPrefix_default(step,
|
|
4250
|
-
return formatPrefix(specifier,
|
|
4263
|
+
var value2 = Math.max(Math.abs(start), Math.abs(stop));
|
|
4264
|
+
if (specifier.precision == null && !isNaN(precision = precisionPrefix_default(step, value2))) specifier.precision = precision;
|
|
4265
|
+
return formatPrefix(specifier, value2);
|
|
4251
4266
|
}
|
|
4252
4267
|
case "":
|
|
4253
4268
|
case "e":
|
|
@@ -5246,8 +5261,8 @@ var pads = { "-": "", "_": " ", "0": "0" };
|
|
|
5246
5261
|
var numberRe = /^\s*\d+/;
|
|
5247
5262
|
var percentRe = /^%/;
|
|
5248
5263
|
var requoteRe = /[\\^$*+?|[\]().{}]/g;
|
|
5249
|
-
function pad(
|
|
5250
|
-
var sign2 =
|
|
5264
|
+
function pad(value2, fill, width) {
|
|
5265
|
+
var sign2 = value2 < 0 ? "-" : "", string = (sign2 ? -value2 : value2) + "", length = string.length;
|
|
5251
5266
|
return sign2 + (length < width ? new Array(width - length + 1).join(fill) + string : string);
|
|
5252
5267
|
}
|
|
5253
5268
|
function requote(s) {
|
|
@@ -5598,11 +5613,11 @@ function sequential() {
|
|
|
5598
5613
|
var DEFAULT_POINT_RADIUS2 = 5;
|
|
5599
5614
|
var MIN_BUBBLE_RADIUS = 3;
|
|
5600
5615
|
var MAX_BUBBLE_RADIUS = 30;
|
|
5601
|
-
function resolvePosition2(
|
|
5616
|
+
function resolvePosition2(value2, channelType, scale) {
|
|
5602
5617
|
switch (channelType) {
|
|
5603
5618
|
case "nominal":
|
|
5604
5619
|
case "ordinal": {
|
|
5605
|
-
const s = String(
|
|
5620
|
+
const s = String(value2);
|
|
5606
5621
|
if ("bandwidth" in scale && typeof scale.bandwidth === "function") {
|
|
5607
5622
|
const bw = scale.bandwidth();
|
|
5608
5623
|
const pos = scale(s);
|
|
@@ -5612,11 +5627,11 @@ function resolvePosition2(value, channelType, scale) {
|
|
|
5612
5627
|
return scale(s);
|
|
5613
5628
|
}
|
|
5614
5629
|
case "temporal": {
|
|
5615
|
-
const px = scale(new Date(
|
|
5630
|
+
const px = scale(new Date(value2));
|
|
5616
5631
|
return Number.isNaN(px) ? void 0 : px;
|
|
5617
5632
|
}
|
|
5618
5633
|
default: {
|
|
5619
|
-
const num = Number(
|
|
5634
|
+
const num = Number(value2);
|
|
5620
5635
|
if (!Number.isFinite(num)) return void 0;
|
|
5621
5636
|
return scale(num);
|
|
5622
5637
|
}
|
|
@@ -5849,14 +5864,15 @@ import {
|
|
|
5849
5864
|
isChartSpec,
|
|
5850
5865
|
isGraphSpec,
|
|
5851
5866
|
isLayerSpec,
|
|
5867
|
+
isSankeySpec,
|
|
5852
5868
|
isTableSpec,
|
|
5853
5869
|
resolveMarkDef,
|
|
5854
5870
|
resolveMarkType
|
|
5855
5871
|
} from "@opendata-ai/openchart-core";
|
|
5856
|
-
function normalizeChromeField(
|
|
5857
|
-
if (
|
|
5858
|
-
if (typeof
|
|
5859
|
-
return
|
|
5872
|
+
function normalizeChromeField(value2) {
|
|
5873
|
+
if (value2 === void 0) return void 0;
|
|
5874
|
+
if (typeof value2 === "string") return { text: value2 };
|
|
5875
|
+
return value2;
|
|
5860
5876
|
}
|
|
5861
5877
|
function normalizeChrome(chrome) {
|
|
5862
5878
|
if (!chrome) return {};
|
|
@@ -5874,26 +5890,26 @@ function inferFieldType(data, field) {
|
|
|
5874
5890
|
let dateCount = 0;
|
|
5875
5891
|
let totalNonNull = 0;
|
|
5876
5892
|
for (let i = 0; i < sampleSize; i++) {
|
|
5877
|
-
const
|
|
5878
|
-
if (
|
|
5893
|
+
const value2 = data[i][field];
|
|
5894
|
+
if (value2 == null) continue;
|
|
5879
5895
|
totalNonNull++;
|
|
5880
|
-
if (typeof
|
|
5896
|
+
if (typeof value2 === "number" && Number.isFinite(value2)) {
|
|
5881
5897
|
numericCount++;
|
|
5882
5898
|
continue;
|
|
5883
5899
|
}
|
|
5884
|
-
if (typeof
|
|
5885
|
-
const num = Number(
|
|
5886
|
-
if (!Number.isNaN(num) && Number.isFinite(num) &&
|
|
5900
|
+
if (typeof value2 === "string") {
|
|
5901
|
+
const num = Number(value2);
|
|
5902
|
+
if (!Number.isNaN(num) && Number.isFinite(num) && value2.trim() !== "") {
|
|
5887
5903
|
numericCount++;
|
|
5888
5904
|
continue;
|
|
5889
5905
|
}
|
|
5890
|
-
const date2 = new Date(
|
|
5906
|
+
const date2 = new Date(value2);
|
|
5891
5907
|
if (!Number.isNaN(date2.getTime())) {
|
|
5892
5908
|
dateCount++;
|
|
5893
5909
|
continue;
|
|
5894
5910
|
}
|
|
5895
5911
|
}
|
|
5896
|
-
if (
|
|
5912
|
+
if (value2 instanceof Date && !Number.isNaN(value2.getTime())) {
|
|
5897
5913
|
dateCount++;
|
|
5898
5914
|
}
|
|
5899
5915
|
}
|
|
@@ -5970,6 +5986,7 @@ function normalizeChartSpec(spec, warnings) {
|
|
|
5970
5986
|
labels: {
|
|
5971
5987
|
density: spec.labels?.density ?? "auto",
|
|
5972
5988
|
format: spec.labels?.format ?? "",
|
|
5989
|
+
prefix: spec.labels?.prefix ?? "",
|
|
5973
5990
|
offsets: spec.labels?.offsets
|
|
5974
5991
|
},
|
|
5975
5992
|
legend: spec.legend,
|
|
@@ -5997,6 +6014,23 @@ function normalizeTableSpec(spec, _warnings) {
|
|
|
5997
6014
|
animation: spec.animation
|
|
5998
6015
|
};
|
|
5999
6016
|
}
|
|
6017
|
+
function normalizeSankeySpec(spec, _warnings) {
|
|
6018
|
+
return {
|
|
6019
|
+
type: "sankey",
|
|
6020
|
+
data: spec.data,
|
|
6021
|
+
encoding: spec.encoding,
|
|
6022
|
+
nodeWidth: spec.nodeWidth ?? 12,
|
|
6023
|
+
nodePadding: spec.nodePadding ?? 16,
|
|
6024
|
+
nodeAlign: spec.nodeAlign ?? "justify",
|
|
6025
|
+
iterations: spec.iterations ?? 6,
|
|
6026
|
+
linkStyle: spec.linkStyle ?? "gradient",
|
|
6027
|
+
chrome: normalizeChrome(spec.chrome),
|
|
6028
|
+
legend: spec.legend,
|
|
6029
|
+
theme: spec.theme ?? {},
|
|
6030
|
+
darkMode: spec.darkMode ?? "off",
|
|
6031
|
+
animation: spec.animation
|
|
6032
|
+
};
|
|
6033
|
+
}
|
|
6000
6034
|
function normalizeGraphSpec(spec, _warnings) {
|
|
6001
6035
|
const defaultLayout = {
|
|
6002
6036
|
type: "force",
|
|
@@ -6037,8 +6071,11 @@ function normalizeSpec(spec, warnings = []) {
|
|
|
6037
6071
|
if (isGraphSpec(spec)) {
|
|
6038
6072
|
return normalizeGraphSpec(spec, warnings);
|
|
6039
6073
|
}
|
|
6074
|
+
if (isSankeySpec(spec)) {
|
|
6075
|
+
return normalizeSankeySpec(spec, warnings);
|
|
6076
|
+
}
|
|
6040
6077
|
throw new Error(
|
|
6041
|
-
`Unknown spec shape. Expected mark (chart), layer, type: 'table', or type: '
|
|
6078
|
+
`Unknown spec shape. Expected mark (chart), layer, type: 'table', type: 'graph', or type: 'sankey'.`
|
|
6042
6079
|
);
|
|
6043
6080
|
}
|
|
6044
6081
|
function flattenLayers(spec, parentData, parentEncoding, parentTransforms) {
|
|
@@ -6071,19 +6108,19 @@ import {
|
|
|
6071
6108
|
} from "@opendata-ai/openchart-core";
|
|
6072
6109
|
var VALID_FIELD_TYPES = /* @__PURE__ */ new Set(["quantitative", "temporal", "nominal", "ordinal"]);
|
|
6073
6110
|
var VALID_DARK_MODES = /* @__PURE__ */ new Set(["auto", "force", "off"]);
|
|
6074
|
-
function isParseableDate(
|
|
6075
|
-
if (
|
|
6076
|
-
if (typeof
|
|
6077
|
-
const d = new Date(
|
|
6111
|
+
function isParseableDate(value2) {
|
|
6112
|
+
if (value2 instanceof Date) return !Number.isNaN(value2.getTime());
|
|
6113
|
+
if (typeof value2 === "string") {
|
|
6114
|
+
const d = new Date(value2);
|
|
6078
6115
|
return !Number.isNaN(d.getTime());
|
|
6079
6116
|
}
|
|
6080
|
-
if (typeof
|
|
6117
|
+
if (typeof value2 === "number") return true;
|
|
6081
6118
|
return false;
|
|
6082
6119
|
}
|
|
6083
|
-
function isNumeric(
|
|
6084
|
-
if (typeof
|
|
6085
|
-
if (typeof
|
|
6086
|
-
const n = Number(
|
|
6120
|
+
function isNumeric(value2) {
|
|
6121
|
+
if (typeof value2 === "number") return Number.isFinite(value2);
|
|
6122
|
+
if (typeof value2 === "string") {
|
|
6123
|
+
const n = Number(value2);
|
|
6087
6124
|
return !Number.isNaN(n) && Number.isFinite(n);
|
|
6088
6125
|
}
|
|
6089
6126
|
return false;
|
|
@@ -6498,6 +6535,85 @@ function validateGraphSpec(spec, errors) {
|
|
|
6498
6535
|
}
|
|
6499
6536
|
}
|
|
6500
6537
|
}
|
|
6538
|
+
function validateSankeySpec(spec, errors) {
|
|
6539
|
+
if (!Array.isArray(spec.data)) {
|
|
6540
|
+
errors.push({
|
|
6541
|
+
message: 'Spec error: sankey spec requires a "data" array',
|
|
6542
|
+
path: "data",
|
|
6543
|
+
code: "INVALID_TYPE",
|
|
6544
|
+
suggestion: 'Provide data as an array of objects, e.g. data: [{ source: "A", target: "B", value: 10 }]'
|
|
6545
|
+
});
|
|
6546
|
+
return;
|
|
6547
|
+
}
|
|
6548
|
+
if (spec.data.length === 0) {
|
|
6549
|
+
errors.push({
|
|
6550
|
+
message: 'Spec error: "data" must be a non-empty array',
|
|
6551
|
+
path: "data",
|
|
6552
|
+
code: "EMPTY_DATA",
|
|
6553
|
+
suggestion: 'Add at least one data row, e.g. data: [{ source: "A", target: "B", value: 10 }]'
|
|
6554
|
+
});
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6557
|
+
const firstRow = spec.data[0];
|
|
6558
|
+
if (typeof firstRow !== "object" || firstRow === null || Array.isArray(firstRow)) {
|
|
6559
|
+
errors.push({
|
|
6560
|
+
message: 'Spec error: each item in "data" must be a plain object',
|
|
6561
|
+
path: "data[0]",
|
|
6562
|
+
code: "INVALID_TYPE",
|
|
6563
|
+
suggestion: 'Each data item should be an object, e.g. { source: "A", target: "B", value: 10 }'
|
|
6564
|
+
});
|
|
6565
|
+
return;
|
|
6566
|
+
}
|
|
6567
|
+
if (!spec.encoding || typeof spec.encoding !== "object") {
|
|
6568
|
+
errors.push({
|
|
6569
|
+
message: 'Spec error: sankey spec requires an "encoding" object with source, target, and value channels',
|
|
6570
|
+
path: "encoding",
|
|
6571
|
+
code: "MISSING_FIELD",
|
|
6572
|
+
suggestion: 'Add an encoding object, e.g. encoding: { source: { field: "source", type: "nominal" }, target: { field: "target", type: "nominal" }, value: { field: "value", type: "quantitative" } }'
|
|
6573
|
+
});
|
|
6574
|
+
return;
|
|
6575
|
+
}
|
|
6576
|
+
const encoding = spec.encoding;
|
|
6577
|
+
const dataColumns = new Set(Object.keys(firstRow));
|
|
6578
|
+
const availableColumns = [...dataColumns].join(", ");
|
|
6579
|
+
for (const channel of ["source", "target", "value"]) {
|
|
6580
|
+
const ch = encoding[channel];
|
|
6581
|
+
if (!ch || typeof ch !== "object") {
|
|
6582
|
+
errors.push({
|
|
6583
|
+
message: `Spec error: sankey encoding requires "${channel}" channel`,
|
|
6584
|
+
path: `encoding.${channel}`,
|
|
6585
|
+
code: "MISSING_FIELD",
|
|
6586
|
+
suggestion: `Add encoding.${channel} with a field from your data (${availableColumns}). Example: ${channel}: { field: "${[...dataColumns][0] ?? "myField"}", type: "${channel === "value" ? "quantitative" : "nominal"}" }`
|
|
6587
|
+
});
|
|
6588
|
+
continue;
|
|
6589
|
+
}
|
|
6590
|
+
if (!ch.field || typeof ch.field !== "string") {
|
|
6591
|
+
errors.push({
|
|
6592
|
+
message: `Spec error: encoding.${channel} must have a "field" string`,
|
|
6593
|
+
path: `encoding.${channel}.field`,
|
|
6594
|
+
code: "MISSING_FIELD",
|
|
6595
|
+
suggestion: `Add a field name from your data columns: ${availableColumns}`
|
|
6596
|
+
});
|
|
6597
|
+
continue;
|
|
6598
|
+
}
|
|
6599
|
+
if (!dataColumns.has(ch.field)) {
|
|
6600
|
+
errors.push({
|
|
6601
|
+
message: `Spec error: encoding.${channel}.field "${ch.field}" does not exist in data. Available columns: ${availableColumns}`,
|
|
6602
|
+
path: `encoding.${channel}.field`,
|
|
6603
|
+
code: "DATA_FIELD_MISSING",
|
|
6604
|
+
suggestion: `Use one of the available data columns: ${availableColumns}`
|
|
6605
|
+
});
|
|
6606
|
+
}
|
|
6607
|
+
}
|
|
6608
|
+
if (spec.darkMode !== void 0 && !VALID_DARK_MODES.has(spec.darkMode)) {
|
|
6609
|
+
errors.push({
|
|
6610
|
+
message: 'Spec error: darkMode must be "auto", "force", or "off"',
|
|
6611
|
+
path: "darkMode",
|
|
6612
|
+
code: "INVALID_VALUE",
|
|
6613
|
+
suggestion: 'Use one of: "auto" (system preference), "force" (always dark), or "off" (always light)'
|
|
6614
|
+
});
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6501
6617
|
function validateLayerSpec(spec, errors) {
|
|
6502
6618
|
const layer = spec.layer;
|
|
6503
6619
|
if (layer.length === 0) {
|
|
@@ -6591,17 +6707,18 @@ function validateSpec(spec) {
|
|
|
6591
6707
|
const hasMark = "mark" in obj;
|
|
6592
6708
|
const isTable = obj.type === "table";
|
|
6593
6709
|
const isGraph = obj.type === "graph";
|
|
6594
|
-
const
|
|
6595
|
-
const
|
|
6596
|
-
|
|
6710
|
+
const isSankey = obj.type === "sankey";
|
|
6711
|
+
const isLayer = hasLayer && !isTable && !isGraph && !isSankey;
|
|
6712
|
+
const isChart = hasMark && !hasLayer && !isTable && !isGraph && !isSankey;
|
|
6713
|
+
if (!isChart && !isTable && !isGraph && !isSankey && !isLayer) {
|
|
6597
6714
|
return {
|
|
6598
6715
|
valid: false,
|
|
6599
6716
|
errors: [
|
|
6600
6717
|
{
|
|
6601
|
-
message: 'Spec error: spec must have a "mark" field for charts, a "layer" array for layered charts, or a "type" field for tables/graphs',
|
|
6718
|
+
message: 'Spec error: spec must have a "mark" field for charts, a "layer" array for layered charts, or a "type" field for tables/graphs/sankey',
|
|
6602
6719
|
path: "mark",
|
|
6603
6720
|
code: "MISSING_FIELD",
|
|
6604
|
-
suggestion: `Add a "mark" field for charts (e.g. mark: "bar"), a "layer" array for layered charts, or a "type" field for tables/graphs (type: "table" or type: "
|
|
6721
|
+
suggestion: `Add a "mark" field for charts (e.g. mark: "bar"), a "layer" array for layered charts, or a "type" field for tables/graphs/sankey (type: "table", type: "graph", or type: "sankey"). Valid mark types: ${[...MARK_TYPES].join(", ")}`
|
|
6605
6722
|
}
|
|
6606
6723
|
],
|
|
6607
6724
|
normalized: null
|
|
@@ -6637,6 +6754,8 @@ function validateSpec(spec) {
|
|
|
6637
6754
|
validateTableSpec(obj, errors);
|
|
6638
6755
|
} else if (isGraph) {
|
|
6639
6756
|
validateGraphSpec(obj, errors);
|
|
6757
|
+
} else if (isSankey) {
|
|
6758
|
+
validateSankeySpec(obj, errors);
|
|
6640
6759
|
}
|
|
6641
6760
|
if (errors.length > 0) {
|
|
6642
6761
|
return { valid: false, errors, normalized: null };
|
|
@@ -6923,8 +7042,8 @@ function resolveEdgeVisuals(edges, encoding, theme) {
|
|
|
6923
7042
|
function assignCommunities(nodes, clusteringField) {
|
|
6924
7043
|
if (!clusteringField) return;
|
|
6925
7044
|
for (const node of nodes) {
|
|
6926
|
-
const
|
|
6927
|
-
node.community =
|
|
7045
|
+
const value2 = node.data[clusteringField];
|
|
7046
|
+
node.community = value2 != null ? String(value2) : void 0;
|
|
6928
7047
|
}
|
|
6929
7048
|
}
|
|
6930
7049
|
function buildCommunityColorMap(nodes, theme) {
|
|
@@ -7011,12 +7130,12 @@ function buildGraphTooltips(nodes) {
|
|
|
7011
7130
|
color: node.fill
|
|
7012
7131
|
});
|
|
7013
7132
|
}
|
|
7014
|
-
for (const [key,
|
|
7133
|
+
for (const [key, value2] of Object.entries(node.data)) {
|
|
7015
7134
|
if (key === "id") continue;
|
|
7016
|
-
if (
|
|
7135
|
+
if (value2 == null) continue;
|
|
7017
7136
|
fields.push({
|
|
7018
7137
|
label: key,
|
|
7019
|
-
value: typeof
|
|
7138
|
+
value: typeof value2 === "number" ? value2.toLocaleString() : String(value2)
|
|
7020
7139
|
});
|
|
7021
7140
|
}
|
|
7022
7141
|
descriptors.set(node.id, {
|
|
@@ -7185,19 +7304,19 @@ function continuousTicks(resolvedScale, density) {
|
|
|
7185
7304
|
const scale = resolvedScale.scale;
|
|
7186
7305
|
if (!("ticks" in scale) || typeof scale.ticks !== "function") {
|
|
7187
7306
|
const domain = scale.domain();
|
|
7188
|
-
return domain.map((
|
|
7189
|
-
value,
|
|
7190
|
-
position: scale(
|
|
7191
|
-
label: formatTickLabel(
|
|
7307
|
+
return domain.map((value2) => ({
|
|
7308
|
+
value: value2,
|
|
7309
|
+
position: scale(value2),
|
|
7310
|
+
label: formatTickLabel(value2, resolvedScale)
|
|
7192
7311
|
}));
|
|
7193
7312
|
}
|
|
7194
7313
|
const explicitCount = resolvedScale.channel.axis?.tickCount;
|
|
7195
7314
|
const count = explicitCount ?? TICK_COUNTS[density];
|
|
7196
7315
|
const rawTicks = scale.ticks(count);
|
|
7197
|
-
const ticks2 = rawTicks.map((
|
|
7198
|
-
value,
|
|
7199
|
-
position: scale(
|
|
7200
|
-
label: formatTickLabel(
|
|
7316
|
+
const ticks2 = rawTicks.map((value2) => ({
|
|
7317
|
+
value: value2,
|
|
7318
|
+
position: scale(value2),
|
|
7319
|
+
label: formatTickLabel(value2, resolvedScale)
|
|
7201
7320
|
}));
|
|
7202
7321
|
return ticks2;
|
|
7203
7322
|
}
|
|
@@ -7211,13 +7330,13 @@ function categoricalTicks(resolvedScale, density) {
|
|
|
7211
7330
|
const step = Math.ceil(domain.length / maxTicks);
|
|
7212
7331
|
selectedValues = domain.filter((_, i) => i % step === 0);
|
|
7213
7332
|
}
|
|
7214
|
-
const ticks2 = selectedValues.map((
|
|
7333
|
+
const ticks2 = selectedValues.map((value2) => {
|
|
7215
7334
|
const bandScale = resolvedScale.type === "band" ? scale : null;
|
|
7216
|
-
const pos = bandScale ? (bandScale(
|
|
7335
|
+
const pos = bandScale ? (bandScale(value2) ?? 0) + bandScale.bandwidth() / 2 : scale(value2) ?? 0;
|
|
7217
7336
|
return {
|
|
7218
|
-
value,
|
|
7337
|
+
value: value2,
|
|
7219
7338
|
position: pos,
|
|
7220
|
-
label:
|
|
7339
|
+
label: value2
|
|
7221
7340
|
};
|
|
7222
7341
|
});
|
|
7223
7342
|
return ticks2;
|
|
@@ -7233,16 +7352,16 @@ var NUMERIC_SCALE_TYPES = /* @__PURE__ */ new Set([
|
|
|
7233
7352
|
"threshold"
|
|
7234
7353
|
]);
|
|
7235
7354
|
var TEMPORAL_SCALE_TYPES = /* @__PURE__ */ new Set(["time", "utc"]);
|
|
7236
|
-
function formatTickLabel(
|
|
7355
|
+
function formatTickLabel(value2, resolvedScale) {
|
|
7237
7356
|
const formatStr = resolvedScale.channel.axis?.format;
|
|
7238
7357
|
if (TEMPORAL_SCALE_TYPES.has(resolvedScale.type)) {
|
|
7239
7358
|
const temporalFmt = buildTemporalFormatter(formatStr);
|
|
7240
|
-
if (temporalFmt) return temporalFmt(
|
|
7359
|
+
if (temporalFmt) return temporalFmt(value2);
|
|
7241
7360
|
const useUtc = resolvedScale.type === "utc";
|
|
7242
|
-
return formatDate(
|
|
7361
|
+
return formatDate(value2, void 0, void 0, useUtc);
|
|
7243
7362
|
}
|
|
7244
7363
|
if (NUMERIC_SCALE_TYPES.has(resolvedScale.type)) {
|
|
7245
|
-
const num =
|
|
7364
|
+
const num = value2;
|
|
7246
7365
|
if (formatStr) {
|
|
7247
7366
|
const fmt = buildD3Formatter3(formatStr);
|
|
7248
7367
|
if (fmt) return fmt(num);
|
|
@@ -7250,26 +7369,26 @@ function formatTickLabel(value, resolvedScale) {
|
|
|
7250
7369
|
if (Math.abs(num) >= 1e3) return abbreviateNumber3(num);
|
|
7251
7370
|
return formatNumber3(num);
|
|
7252
7371
|
}
|
|
7253
|
-
return String(
|
|
7372
|
+
return String(value2);
|
|
7254
7373
|
}
|
|
7255
7374
|
function resolveExplicitTicks(values, resolvedScale) {
|
|
7256
7375
|
const scale = resolvedScale.scale;
|
|
7257
|
-
return values.map((
|
|
7376
|
+
return values.map((value2) => {
|
|
7258
7377
|
let position;
|
|
7259
7378
|
if (TEMPORAL_SCALE_TYPES.has(resolvedScale.type)) {
|
|
7260
|
-
const d =
|
|
7379
|
+
const d = value2 instanceof Date ? value2 : new Date(String(value2));
|
|
7261
7380
|
position = scale(d);
|
|
7262
7381
|
} else if (resolvedScale.type === "band" || resolvedScale.type === "point" || resolvedScale.type === "ordinal") {
|
|
7263
|
-
const s = String(
|
|
7382
|
+
const s = String(value2);
|
|
7264
7383
|
const bandScale = resolvedScale.type === "band" ? scale : null;
|
|
7265
7384
|
position = bandScale ? (bandScale(s) ?? 0) + bandScale.bandwidth() / 2 : scale(s) ?? 0;
|
|
7266
7385
|
} else {
|
|
7267
|
-
position = scale(
|
|
7386
|
+
position = scale(value2);
|
|
7268
7387
|
}
|
|
7269
7388
|
return {
|
|
7270
|
-
value,
|
|
7389
|
+
value: value2,
|
|
7271
7390
|
position,
|
|
7272
|
-
label: formatTickLabel(
|
|
7391
|
+
label: formatTickLabel(value2, resolvedScale)
|
|
7273
7392
|
};
|
|
7274
7393
|
});
|
|
7275
7394
|
}
|
|
@@ -8027,8 +8146,8 @@ function extractColorEntries(spec, theme) {
|
|
|
8027
8146
|
const uniqueValues = [...new Set(spec.data.map((d) => String(d[colorEnc.field])))];
|
|
8028
8147
|
const palette = theme.colors.categorical;
|
|
8029
8148
|
const shape = swatchShapeForType(spec.markType);
|
|
8030
|
-
return uniqueValues.map((
|
|
8031
|
-
label:
|
|
8149
|
+
return uniqueValues.map((value2, i) => ({
|
|
8150
|
+
label: value2,
|
|
8032
8151
|
color: palette[i % palette.length],
|
|
8033
8152
|
shape,
|
|
8034
8153
|
active: true
|
|
@@ -8156,9 +8275,9 @@ function computeLegend(spec, strategy, theme, chartArea) {
|
|
|
8156
8275
|
if (maxFit < entries.length) {
|
|
8157
8276
|
entries = truncateEntries(entries, maxFit);
|
|
8158
8277
|
}
|
|
8159
|
-
const totalWidth = entries.reduce((
|
|
8278
|
+
const totalWidth = entries.reduce((sum2, entry) => {
|
|
8160
8279
|
const labelWidth = estimateTextWidth9(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
|
|
8161
|
-
return
|
|
8280
|
+
return sum2 + SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + ENTRY_GAP2;
|
|
8162
8281
|
}, 0);
|
|
8163
8282
|
let rowCount = 1;
|
|
8164
8283
|
let rowWidth = 0;
|
|
@@ -8192,15 +8311,879 @@ function computeLegend(spec, strategy, theme, chartArea) {
|
|
|
8192
8311
|
};
|
|
8193
8312
|
}
|
|
8194
8313
|
|
|
8314
|
+
// src/sankey/compile-sankey.ts
|
|
8315
|
+
import {
|
|
8316
|
+
adaptTheme as adaptTheme2,
|
|
8317
|
+
computeChrome as computeChrome3,
|
|
8318
|
+
estimateTextWidth as estimateTextWidth10,
|
|
8319
|
+
formatNumber as formatNumber4,
|
|
8320
|
+
resolveTheme as resolveTheme2
|
|
8321
|
+
} from "@opendata-ai/openchart-core";
|
|
8322
|
+
|
|
8323
|
+
// ../../node_modules/.bun/d3-array@2.12.1/node_modules/d3-array/src/max.js
|
|
8324
|
+
function max3(values, valueof) {
|
|
8325
|
+
let max4;
|
|
8326
|
+
if (valueof === void 0) {
|
|
8327
|
+
for (const value2 of values) {
|
|
8328
|
+
if (value2 != null && (max4 < value2 || max4 === void 0 && value2 >= value2)) {
|
|
8329
|
+
max4 = value2;
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8332
|
+
} else {
|
|
8333
|
+
let index = -1;
|
|
8334
|
+
for (let value2 of values) {
|
|
8335
|
+
if ((value2 = valueof(value2, ++index, values)) != null && (max4 < value2 || max4 === void 0 && value2 >= value2)) {
|
|
8336
|
+
max4 = value2;
|
|
8337
|
+
}
|
|
8338
|
+
}
|
|
8339
|
+
}
|
|
8340
|
+
return max4;
|
|
8341
|
+
}
|
|
8342
|
+
|
|
8343
|
+
// ../../node_modules/.bun/d3-array@2.12.1/node_modules/d3-array/src/min.js
|
|
8344
|
+
function min3(values, valueof) {
|
|
8345
|
+
let min4;
|
|
8346
|
+
if (valueof === void 0) {
|
|
8347
|
+
for (const value2 of values) {
|
|
8348
|
+
if (value2 != null && (min4 > value2 || min4 === void 0 && value2 >= value2)) {
|
|
8349
|
+
min4 = value2;
|
|
8350
|
+
}
|
|
8351
|
+
}
|
|
8352
|
+
} else {
|
|
8353
|
+
let index = -1;
|
|
8354
|
+
for (let value2 of values) {
|
|
8355
|
+
if ((value2 = valueof(value2, ++index, values)) != null && (min4 > value2 || min4 === void 0 && value2 >= value2)) {
|
|
8356
|
+
min4 = value2;
|
|
8357
|
+
}
|
|
8358
|
+
}
|
|
8359
|
+
}
|
|
8360
|
+
return min4;
|
|
8361
|
+
}
|
|
8362
|
+
|
|
8363
|
+
// ../../node_modules/.bun/d3-array@2.12.1/node_modules/d3-array/src/sum.js
|
|
8364
|
+
function sum(values, valueof) {
|
|
8365
|
+
let sum2 = 0;
|
|
8366
|
+
if (valueof === void 0) {
|
|
8367
|
+
for (let value2 of values) {
|
|
8368
|
+
if (value2 = +value2) {
|
|
8369
|
+
sum2 += value2;
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
} else {
|
|
8373
|
+
let index = -1;
|
|
8374
|
+
for (let value2 of values) {
|
|
8375
|
+
if (value2 = +valueof(value2, ++index, values)) {
|
|
8376
|
+
sum2 += value2;
|
|
8377
|
+
}
|
|
8378
|
+
}
|
|
8379
|
+
}
|
|
8380
|
+
return sum2;
|
|
8381
|
+
}
|
|
8382
|
+
|
|
8383
|
+
// ../../node_modules/.bun/d3-sankey@0.12.3/node_modules/d3-sankey/src/align.js
|
|
8384
|
+
function targetDepth(d) {
|
|
8385
|
+
return d.target.depth;
|
|
8386
|
+
}
|
|
8387
|
+
function left(node) {
|
|
8388
|
+
return node.depth;
|
|
8389
|
+
}
|
|
8390
|
+
function right(node, n) {
|
|
8391
|
+
return n - 1 - node.height;
|
|
8392
|
+
}
|
|
8393
|
+
function justify(node, n) {
|
|
8394
|
+
return node.sourceLinks.length ? node.depth : n - 1;
|
|
8395
|
+
}
|
|
8396
|
+
function center(node) {
|
|
8397
|
+
return node.targetLinks.length ? node.depth : node.sourceLinks.length ? min3(node.sourceLinks, targetDepth) - 1 : 0;
|
|
8398
|
+
}
|
|
8399
|
+
|
|
8400
|
+
// ../../node_modules/.bun/d3-sankey@0.12.3/node_modules/d3-sankey/src/constant.js
|
|
8401
|
+
function constant(x2) {
|
|
8402
|
+
return function() {
|
|
8403
|
+
return x2;
|
|
8404
|
+
};
|
|
8405
|
+
}
|
|
8406
|
+
|
|
8407
|
+
// ../../node_modules/.bun/d3-sankey@0.12.3/node_modules/d3-sankey/src/sankey.js
|
|
8408
|
+
function ascendingSourceBreadth(a, b) {
|
|
8409
|
+
return ascendingBreadth(a.source, b.source) || a.index - b.index;
|
|
8410
|
+
}
|
|
8411
|
+
function ascendingTargetBreadth(a, b) {
|
|
8412
|
+
return ascendingBreadth(a.target, b.target) || a.index - b.index;
|
|
8413
|
+
}
|
|
8414
|
+
function ascendingBreadth(a, b) {
|
|
8415
|
+
return a.y0 - b.y0;
|
|
8416
|
+
}
|
|
8417
|
+
function value(d) {
|
|
8418
|
+
return d.value;
|
|
8419
|
+
}
|
|
8420
|
+
function defaultId(d) {
|
|
8421
|
+
return d.index;
|
|
8422
|
+
}
|
|
8423
|
+
function defaultNodes(graph) {
|
|
8424
|
+
return graph.nodes;
|
|
8425
|
+
}
|
|
8426
|
+
function defaultLinks(graph) {
|
|
8427
|
+
return graph.links;
|
|
8428
|
+
}
|
|
8429
|
+
function find(nodeById, id) {
|
|
8430
|
+
const node = nodeById.get(id);
|
|
8431
|
+
if (!node) throw new Error("missing: " + id);
|
|
8432
|
+
return node;
|
|
8433
|
+
}
|
|
8434
|
+
function computeLinkBreadths({ nodes }) {
|
|
8435
|
+
for (const node of nodes) {
|
|
8436
|
+
let y0 = node.y0;
|
|
8437
|
+
let y1 = y0;
|
|
8438
|
+
for (const link of node.sourceLinks) {
|
|
8439
|
+
link.y0 = y0 + link.width / 2;
|
|
8440
|
+
y0 += link.width;
|
|
8441
|
+
}
|
|
8442
|
+
for (const link of node.targetLinks) {
|
|
8443
|
+
link.y1 = y1 + link.width / 2;
|
|
8444
|
+
y1 += link.width;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8448
|
+
function Sankey() {
|
|
8449
|
+
let x0 = 0, y0 = 0, x1 = 1, y1 = 1;
|
|
8450
|
+
let dx = 24;
|
|
8451
|
+
let dy = 8, py;
|
|
8452
|
+
let id = defaultId;
|
|
8453
|
+
let align = justify;
|
|
8454
|
+
let sort;
|
|
8455
|
+
let linkSort;
|
|
8456
|
+
let nodes = defaultNodes;
|
|
8457
|
+
let links = defaultLinks;
|
|
8458
|
+
let iterations = 6;
|
|
8459
|
+
function sankey() {
|
|
8460
|
+
const graph = { nodes: nodes.apply(null, arguments), links: links.apply(null, arguments) };
|
|
8461
|
+
computeNodeLinks(graph);
|
|
8462
|
+
computeNodeValues(graph);
|
|
8463
|
+
computeNodeDepths(graph);
|
|
8464
|
+
computeNodeHeights(graph);
|
|
8465
|
+
computeNodeBreadths(graph);
|
|
8466
|
+
computeLinkBreadths(graph);
|
|
8467
|
+
return graph;
|
|
8468
|
+
}
|
|
8469
|
+
sankey.update = function(graph) {
|
|
8470
|
+
computeLinkBreadths(graph);
|
|
8471
|
+
return graph;
|
|
8472
|
+
};
|
|
8473
|
+
sankey.nodeId = function(_) {
|
|
8474
|
+
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id;
|
|
8475
|
+
};
|
|
8476
|
+
sankey.nodeAlign = function(_) {
|
|
8477
|
+
return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align;
|
|
8478
|
+
};
|
|
8479
|
+
sankey.nodeSort = function(_) {
|
|
8480
|
+
return arguments.length ? (sort = _, sankey) : sort;
|
|
8481
|
+
};
|
|
8482
|
+
sankey.nodeWidth = function(_) {
|
|
8483
|
+
return arguments.length ? (dx = +_, sankey) : dx;
|
|
8484
|
+
};
|
|
8485
|
+
sankey.nodePadding = function(_) {
|
|
8486
|
+
return arguments.length ? (dy = py = +_, sankey) : dy;
|
|
8487
|
+
};
|
|
8488
|
+
sankey.nodes = function(_) {
|
|
8489
|
+
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
|
|
8490
|
+
};
|
|
8491
|
+
sankey.links = function(_) {
|
|
8492
|
+
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
|
|
8493
|
+
};
|
|
8494
|
+
sankey.linkSort = function(_) {
|
|
8495
|
+
return arguments.length ? (linkSort = _, sankey) : linkSort;
|
|
8496
|
+
};
|
|
8497
|
+
sankey.size = function(_) {
|
|
8498
|
+
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
|
|
8499
|
+
};
|
|
8500
|
+
sankey.extent = function(_) {
|
|
8501
|
+
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
|
|
8502
|
+
};
|
|
8503
|
+
sankey.iterations = function(_) {
|
|
8504
|
+
return arguments.length ? (iterations = +_, sankey) : iterations;
|
|
8505
|
+
};
|
|
8506
|
+
function computeNodeLinks({ nodes: nodes2, links: links2 }) {
|
|
8507
|
+
for (const [i, node] of nodes2.entries()) {
|
|
8508
|
+
node.index = i;
|
|
8509
|
+
node.sourceLinks = [];
|
|
8510
|
+
node.targetLinks = [];
|
|
8511
|
+
}
|
|
8512
|
+
const nodeById = new Map(nodes2.map((d, i) => [id(d, i, nodes2), d]));
|
|
8513
|
+
for (const [i, link] of links2.entries()) {
|
|
8514
|
+
link.index = i;
|
|
8515
|
+
let { source, target } = link;
|
|
8516
|
+
if (typeof source !== "object") source = link.source = find(nodeById, source);
|
|
8517
|
+
if (typeof target !== "object") target = link.target = find(nodeById, target);
|
|
8518
|
+
source.sourceLinks.push(link);
|
|
8519
|
+
target.targetLinks.push(link);
|
|
8520
|
+
}
|
|
8521
|
+
if (linkSort != null) {
|
|
8522
|
+
for (const { sourceLinks, targetLinks } of nodes2) {
|
|
8523
|
+
sourceLinks.sort(linkSort);
|
|
8524
|
+
targetLinks.sort(linkSort);
|
|
8525
|
+
}
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
function computeNodeValues({ nodes: nodes2 }) {
|
|
8529
|
+
for (const node of nodes2) {
|
|
8530
|
+
node.value = node.fixedValue === void 0 ? Math.max(sum(node.sourceLinks, value), sum(node.targetLinks, value)) : node.fixedValue;
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
function computeNodeDepths({ nodes: nodes2 }) {
|
|
8534
|
+
const n = nodes2.length;
|
|
8535
|
+
let current = new Set(nodes2);
|
|
8536
|
+
let next = /* @__PURE__ */ new Set();
|
|
8537
|
+
let x2 = 0;
|
|
8538
|
+
while (current.size) {
|
|
8539
|
+
for (const node of current) {
|
|
8540
|
+
node.depth = x2;
|
|
8541
|
+
for (const { target } of node.sourceLinks) {
|
|
8542
|
+
next.add(target);
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8545
|
+
if (++x2 > n) throw new Error("circular link");
|
|
8546
|
+
current = next;
|
|
8547
|
+
next = /* @__PURE__ */ new Set();
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8550
|
+
function computeNodeHeights({ nodes: nodes2 }) {
|
|
8551
|
+
const n = nodes2.length;
|
|
8552
|
+
let current = new Set(nodes2);
|
|
8553
|
+
let next = /* @__PURE__ */ new Set();
|
|
8554
|
+
let x2 = 0;
|
|
8555
|
+
while (current.size) {
|
|
8556
|
+
for (const node of current) {
|
|
8557
|
+
node.height = x2;
|
|
8558
|
+
for (const { source } of node.targetLinks) {
|
|
8559
|
+
next.add(source);
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
if (++x2 > n) throw new Error("circular link");
|
|
8563
|
+
current = next;
|
|
8564
|
+
next = /* @__PURE__ */ new Set();
|
|
8565
|
+
}
|
|
8566
|
+
}
|
|
8567
|
+
function computeNodeLayers({ nodes: nodes2 }) {
|
|
8568
|
+
const x2 = max3(nodes2, (d) => d.depth) + 1;
|
|
8569
|
+
const kx = (x1 - x0 - dx) / (x2 - 1);
|
|
8570
|
+
const columns = new Array(x2);
|
|
8571
|
+
for (const node of nodes2) {
|
|
8572
|
+
const i = Math.max(0, Math.min(x2 - 1, Math.floor(align.call(null, node, x2))));
|
|
8573
|
+
node.layer = i;
|
|
8574
|
+
node.x0 = x0 + i * kx;
|
|
8575
|
+
node.x1 = node.x0 + dx;
|
|
8576
|
+
if (columns[i]) columns[i].push(node);
|
|
8577
|
+
else columns[i] = [node];
|
|
8578
|
+
}
|
|
8579
|
+
if (sort) for (const column of columns) {
|
|
8580
|
+
column.sort(sort);
|
|
8581
|
+
}
|
|
8582
|
+
return columns;
|
|
8583
|
+
}
|
|
8584
|
+
function initializeNodeBreadths(columns) {
|
|
8585
|
+
const ky = min3(columns, (c) => (y1 - y0 - (c.length - 1) * py) / sum(c, value));
|
|
8586
|
+
for (const nodes2 of columns) {
|
|
8587
|
+
let y2 = y0;
|
|
8588
|
+
for (const node of nodes2) {
|
|
8589
|
+
node.y0 = y2;
|
|
8590
|
+
node.y1 = y2 + node.value * ky;
|
|
8591
|
+
y2 = node.y1 + py;
|
|
8592
|
+
for (const link of node.sourceLinks) {
|
|
8593
|
+
link.width = link.value * ky;
|
|
8594
|
+
}
|
|
8595
|
+
}
|
|
8596
|
+
y2 = (y1 - y2 + py) / (nodes2.length + 1);
|
|
8597
|
+
for (let i = 0; i < nodes2.length; ++i) {
|
|
8598
|
+
const node = nodes2[i];
|
|
8599
|
+
node.y0 += y2 * (i + 1);
|
|
8600
|
+
node.y1 += y2 * (i + 1);
|
|
8601
|
+
}
|
|
8602
|
+
reorderLinks(nodes2);
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
function computeNodeBreadths(graph) {
|
|
8606
|
+
const columns = computeNodeLayers(graph);
|
|
8607
|
+
py = Math.min(dy, (y1 - y0) / (max3(columns, (c) => c.length) - 1));
|
|
8608
|
+
initializeNodeBreadths(columns);
|
|
8609
|
+
for (let i = 0; i < iterations; ++i) {
|
|
8610
|
+
const alpha = Math.pow(0.99, i);
|
|
8611
|
+
const beta = Math.max(1 - alpha, (i + 1) / iterations);
|
|
8612
|
+
relaxRightToLeft(columns, alpha, beta);
|
|
8613
|
+
relaxLeftToRight(columns, alpha, beta);
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
function relaxLeftToRight(columns, alpha, beta) {
|
|
8617
|
+
for (let i = 1, n = columns.length; i < n; ++i) {
|
|
8618
|
+
const column = columns[i];
|
|
8619
|
+
for (const target of column) {
|
|
8620
|
+
let y2 = 0;
|
|
8621
|
+
let w = 0;
|
|
8622
|
+
for (const { source, value: value2 } of target.targetLinks) {
|
|
8623
|
+
let v = value2 * (target.layer - source.layer);
|
|
8624
|
+
y2 += targetTop(source, target) * v;
|
|
8625
|
+
w += v;
|
|
8626
|
+
}
|
|
8627
|
+
if (!(w > 0)) continue;
|
|
8628
|
+
let dy2 = (y2 / w - target.y0) * alpha;
|
|
8629
|
+
target.y0 += dy2;
|
|
8630
|
+
target.y1 += dy2;
|
|
8631
|
+
reorderNodeLinks(target);
|
|
8632
|
+
}
|
|
8633
|
+
if (sort === void 0) column.sort(ascendingBreadth);
|
|
8634
|
+
resolveCollisions6(column, beta);
|
|
8635
|
+
}
|
|
8636
|
+
}
|
|
8637
|
+
function relaxRightToLeft(columns, alpha, beta) {
|
|
8638
|
+
for (let n = columns.length, i = n - 2; i >= 0; --i) {
|
|
8639
|
+
const column = columns[i];
|
|
8640
|
+
for (const source of column) {
|
|
8641
|
+
let y2 = 0;
|
|
8642
|
+
let w = 0;
|
|
8643
|
+
for (const { target, value: value2 } of source.sourceLinks) {
|
|
8644
|
+
let v = value2 * (target.layer - source.layer);
|
|
8645
|
+
y2 += sourceTop(source, target) * v;
|
|
8646
|
+
w += v;
|
|
8647
|
+
}
|
|
8648
|
+
if (!(w > 0)) continue;
|
|
8649
|
+
let dy2 = (y2 / w - source.y0) * alpha;
|
|
8650
|
+
source.y0 += dy2;
|
|
8651
|
+
source.y1 += dy2;
|
|
8652
|
+
reorderNodeLinks(source);
|
|
8653
|
+
}
|
|
8654
|
+
if (sort === void 0) column.sort(ascendingBreadth);
|
|
8655
|
+
resolveCollisions6(column, beta);
|
|
8656
|
+
}
|
|
8657
|
+
}
|
|
8658
|
+
function resolveCollisions6(nodes2, alpha) {
|
|
8659
|
+
const i = nodes2.length >> 1;
|
|
8660
|
+
const subject = nodes2[i];
|
|
8661
|
+
resolveCollisionsBottomToTop(nodes2, subject.y0 - py, i - 1, alpha);
|
|
8662
|
+
resolveCollisionsTopToBottom(nodes2, subject.y1 + py, i + 1, alpha);
|
|
8663
|
+
resolveCollisionsBottomToTop(nodes2, y1, nodes2.length - 1, alpha);
|
|
8664
|
+
resolveCollisionsTopToBottom(nodes2, y0, 0, alpha);
|
|
8665
|
+
}
|
|
8666
|
+
function resolveCollisionsTopToBottom(nodes2, y2, i, alpha) {
|
|
8667
|
+
for (; i < nodes2.length; ++i) {
|
|
8668
|
+
const node = nodes2[i];
|
|
8669
|
+
const dy2 = (y2 - node.y0) * alpha;
|
|
8670
|
+
if (dy2 > 1e-6) node.y0 += dy2, node.y1 += dy2;
|
|
8671
|
+
y2 = node.y1 + py;
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
function resolveCollisionsBottomToTop(nodes2, y2, i, alpha) {
|
|
8675
|
+
for (; i >= 0; --i) {
|
|
8676
|
+
const node = nodes2[i];
|
|
8677
|
+
const dy2 = (node.y1 - y2) * alpha;
|
|
8678
|
+
if (dy2 > 1e-6) node.y0 -= dy2, node.y1 -= dy2;
|
|
8679
|
+
y2 = node.y0 - py;
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
function reorderNodeLinks({ sourceLinks, targetLinks }) {
|
|
8683
|
+
if (linkSort === void 0) {
|
|
8684
|
+
for (const { source: { sourceLinks: sourceLinks2 } } of targetLinks) {
|
|
8685
|
+
sourceLinks2.sort(ascendingTargetBreadth);
|
|
8686
|
+
}
|
|
8687
|
+
for (const { target: { targetLinks: targetLinks2 } } of sourceLinks) {
|
|
8688
|
+
targetLinks2.sort(ascendingSourceBreadth);
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
function reorderLinks(nodes2) {
|
|
8693
|
+
if (linkSort === void 0) {
|
|
8694
|
+
for (const { sourceLinks, targetLinks } of nodes2) {
|
|
8695
|
+
sourceLinks.sort(ascendingTargetBreadth);
|
|
8696
|
+
targetLinks.sort(ascendingSourceBreadth);
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8699
|
+
}
|
|
8700
|
+
function targetTop(source, target) {
|
|
8701
|
+
let y2 = source.y0 - (source.sourceLinks.length - 1) * py / 2;
|
|
8702
|
+
for (const { target: node, width } of source.sourceLinks) {
|
|
8703
|
+
if (node === target) break;
|
|
8704
|
+
y2 += width + py;
|
|
8705
|
+
}
|
|
8706
|
+
for (const { source: node, width } of target.targetLinks) {
|
|
8707
|
+
if (node === source) break;
|
|
8708
|
+
y2 -= width;
|
|
8709
|
+
}
|
|
8710
|
+
return y2;
|
|
8711
|
+
}
|
|
8712
|
+
function sourceTop(source, target) {
|
|
8713
|
+
let y2 = target.y0 - (target.targetLinks.length - 1) * py / 2;
|
|
8714
|
+
for (const { source: node, width } of target.targetLinks) {
|
|
8715
|
+
if (node === source) break;
|
|
8716
|
+
y2 += width + py;
|
|
8717
|
+
}
|
|
8718
|
+
for (const { target: node, width } of source.sourceLinks) {
|
|
8719
|
+
if (node === target) break;
|
|
8720
|
+
y2 -= width;
|
|
8721
|
+
}
|
|
8722
|
+
return y2;
|
|
8723
|
+
}
|
|
8724
|
+
return sankey;
|
|
8725
|
+
}
|
|
8726
|
+
|
|
8727
|
+
// src/sankey/layout.ts
|
|
8728
|
+
var ALIGN_MAP = {
|
|
8729
|
+
justify,
|
|
8730
|
+
left,
|
|
8731
|
+
right,
|
|
8732
|
+
center
|
|
8733
|
+
};
|
|
8734
|
+
function computeSankeyLayout(data, sourceField, targetField, valueField, area, nodeWidth, nodePadding, nodeAlign, iterations) {
|
|
8735
|
+
const nodeSet = /* @__PURE__ */ new Set();
|
|
8736
|
+
for (const row of data) {
|
|
8737
|
+
nodeSet.add(String(row[sourceField]));
|
|
8738
|
+
nodeSet.add(String(row[targetField]));
|
|
8739
|
+
}
|
|
8740
|
+
const nodes = [...nodeSet].map((id) => ({
|
|
8741
|
+
id,
|
|
8742
|
+
label: id
|
|
8743
|
+
}));
|
|
8744
|
+
const links = data.map((row) => ({
|
|
8745
|
+
source: String(row[sourceField]),
|
|
8746
|
+
target: String(row[targetField]),
|
|
8747
|
+
value: Number(row[valueField]) || 0,
|
|
8748
|
+
data: { ...row }
|
|
8749
|
+
}));
|
|
8750
|
+
const alignFn = ALIGN_MAP[nodeAlign] ?? justify;
|
|
8751
|
+
const generator = Sankey().nodeId((d) => d.id).nodeAlign(alignFn).nodeWidth(nodeWidth).nodePadding(nodePadding).extent([
|
|
8752
|
+
[area.x, area.y],
|
|
8753
|
+
[area.x + area.width, area.y + area.height]
|
|
8754
|
+
]).iterations(iterations);
|
|
8755
|
+
const graph = generator({
|
|
8756
|
+
nodes,
|
|
8757
|
+
links
|
|
8758
|
+
});
|
|
8759
|
+
return {
|
|
8760
|
+
nodes: graph.nodes,
|
|
8761
|
+
links: graph.links
|
|
8762
|
+
};
|
|
8763
|
+
}
|
|
8764
|
+
function generateLinkPath(link) {
|
|
8765
|
+
const source = link.source;
|
|
8766
|
+
const target = link.target;
|
|
8767
|
+
const x0 = source.x1 ?? 0;
|
|
8768
|
+
const x1 = target.x0 ?? 0;
|
|
8769
|
+
const y0 = link.y0 ?? 0;
|
|
8770
|
+
const y1 = link.y1 ?? 0;
|
|
8771
|
+
const halfWidth0 = (link.width ?? 0) / 2;
|
|
8772
|
+
const halfWidth1 = halfWidth0;
|
|
8773
|
+
const mx = (x0 + x1) / 2;
|
|
8774
|
+
const topY0 = y0 - halfWidth0;
|
|
8775
|
+
const topY1 = y1 - halfWidth1;
|
|
8776
|
+
const botY0 = y0 + halfWidth0;
|
|
8777
|
+
const botY1 = y1 + halfWidth1;
|
|
8778
|
+
return [
|
|
8779
|
+
`M${x0},${topY0}`,
|
|
8780
|
+
`C${mx},${topY0} ${mx},${topY1} ${x1},${topY1}`,
|
|
8781
|
+
`L${x1},${botY1}`,
|
|
8782
|
+
`C${mx},${botY1} ${mx},${botY0} ${x0},${botY0}`,
|
|
8783
|
+
"Z"
|
|
8784
|
+
].join(" ");
|
|
8785
|
+
}
|
|
8786
|
+
|
|
8787
|
+
// src/sankey/compile-sankey.ts
|
|
8788
|
+
var SWATCH_SIZE3 = 12;
|
|
8789
|
+
var SWATCH_GAP3 = 6;
|
|
8790
|
+
var ENTRY_GAP3 = 16;
|
|
8791
|
+
var LABEL_GAP = 6;
|
|
8792
|
+
var LINK_OPACITY = 0.35;
|
|
8793
|
+
var NODE_CORNER_RADIUS = 2;
|
|
8794
|
+
function pickColor(palette, index) {
|
|
8795
|
+
return palette[index % palette.length];
|
|
8796
|
+
}
|
|
8797
|
+
function buildNodeColorMap(nodes, palette, colorField, data, sourceField, targetField) {
|
|
8798
|
+
const colorMap = /* @__PURE__ */ new Map();
|
|
8799
|
+
if (colorField) {
|
|
8800
|
+
const nodeCategoryMap = /* @__PURE__ */ new Map();
|
|
8801
|
+
for (const row of data) {
|
|
8802
|
+
const src = String(row[sourceField]);
|
|
8803
|
+
const tgt = String(row[targetField]);
|
|
8804
|
+
const cat = String(row[colorField]);
|
|
8805
|
+
if (!nodeCategoryMap.has(src)) nodeCategoryMap.set(src, cat);
|
|
8806
|
+
if (!nodeCategoryMap.has(tgt)) nodeCategoryMap.set(tgt, cat);
|
|
8807
|
+
}
|
|
8808
|
+
const categoryIndex = /* @__PURE__ */ new Map();
|
|
8809
|
+
let nextIdx = 0;
|
|
8810
|
+
for (const node of nodes) {
|
|
8811
|
+
const category = nodeCategoryMap.get(node.id) ?? node.id;
|
|
8812
|
+
if (!categoryIndex.has(category)) {
|
|
8813
|
+
categoryIndex.set(category, nextIdx++);
|
|
8814
|
+
}
|
|
8815
|
+
colorMap.set(node.id, pickColor(palette, categoryIndex.get(category)));
|
|
8816
|
+
}
|
|
8817
|
+
} else {
|
|
8818
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
8819
|
+
colorMap.set(nodes[i].id, pickColor(palette, i));
|
|
8820
|
+
}
|
|
8821
|
+
}
|
|
8822
|
+
return colorMap;
|
|
8823
|
+
}
|
|
8824
|
+
function getLinkColors(linkStyle, sourceColor, targetColor, neutralColor) {
|
|
8825
|
+
switch (linkStyle) {
|
|
8826
|
+
case "source":
|
|
8827
|
+
return { sourceColor, targetColor: sourceColor };
|
|
8828
|
+
case "target":
|
|
8829
|
+
return { sourceColor: targetColor, targetColor };
|
|
8830
|
+
case "neutral":
|
|
8831
|
+
return { sourceColor: neutralColor, targetColor: neutralColor };
|
|
8832
|
+
default:
|
|
8833
|
+
return { sourceColor, targetColor };
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
function computeNodeLabel(node, maxDepth, theme, nodeWidth) {
|
|
8837
|
+
const depth = node.depth ?? 0;
|
|
8838
|
+
const isRightmost = depth === maxDepth;
|
|
8839
|
+
const style = {
|
|
8840
|
+
fontFamily: theme.fonts.family,
|
|
8841
|
+
fontSize: theme.fonts.sizes.small,
|
|
8842
|
+
fontWeight: theme.fonts.weights.normal,
|
|
8843
|
+
fill: theme.colors.text,
|
|
8844
|
+
lineHeight: 1.3
|
|
8845
|
+
};
|
|
8846
|
+
const x0 = node.x0 ?? 0;
|
|
8847
|
+
const x1 = node.x1 ?? nodeWidth;
|
|
8848
|
+
const y0 = node.y0 ?? 0;
|
|
8849
|
+
const y1 = node.y1 ?? 0;
|
|
8850
|
+
const midY = (y0 + y1) / 2;
|
|
8851
|
+
if (isRightmost) {
|
|
8852
|
+
return {
|
|
8853
|
+
text: node.label ?? node.id,
|
|
8854
|
+
x: x0 - LABEL_GAP,
|
|
8855
|
+
y: midY,
|
|
8856
|
+
style: { ...style, textAnchor: "end", dominantBaseline: "central" },
|
|
8857
|
+
visible: true
|
|
8858
|
+
};
|
|
8859
|
+
}
|
|
8860
|
+
return {
|
|
8861
|
+
text: node.label ?? node.id,
|
|
8862
|
+
x: x1 + LABEL_GAP,
|
|
8863
|
+
y: midY,
|
|
8864
|
+
style: { ...style, textAnchor: "start", dominantBaseline: "central" },
|
|
8865
|
+
visible: true
|
|
8866
|
+
};
|
|
8867
|
+
}
|
|
8868
|
+
function compileSankey(spec, options) {
|
|
8869
|
+
const { spec: normalized } = compile(spec);
|
|
8870
|
+
if (!("type" in normalized) || normalized.type !== "sankey") {
|
|
8871
|
+
throw new Error(
|
|
8872
|
+
"compileSankey received a non-sankey spec. Use compileChart, compileTable, or compileGraph instead."
|
|
8873
|
+
);
|
|
8874
|
+
}
|
|
8875
|
+
const sankeySpec = normalized;
|
|
8876
|
+
const mergedThemeConfig = options.theme ? { ...sankeySpec.theme, ...options.theme } : sankeySpec.theme;
|
|
8877
|
+
let theme = resolveTheme2(mergedThemeConfig);
|
|
8878
|
+
if (options.darkMode) {
|
|
8879
|
+
theme = adaptTheme2(theme);
|
|
8880
|
+
}
|
|
8881
|
+
const chrome = computeChrome3(
|
|
8882
|
+
{
|
|
8883
|
+
title: sankeySpec.chrome.title,
|
|
8884
|
+
subtitle: sankeySpec.chrome.subtitle,
|
|
8885
|
+
source: sankeySpec.chrome.source,
|
|
8886
|
+
byline: sankeySpec.chrome.byline,
|
|
8887
|
+
footer: sankeySpec.chrome.footer
|
|
8888
|
+
},
|
|
8889
|
+
theme,
|
|
8890
|
+
options.width,
|
|
8891
|
+
options.measureText
|
|
8892
|
+
);
|
|
8893
|
+
const padding = theme.spacing.padding;
|
|
8894
|
+
const fullArea = {
|
|
8895
|
+
x: padding,
|
|
8896
|
+
y: padding + chrome.topHeight,
|
|
8897
|
+
width: options.width - padding * 2,
|
|
8898
|
+
height: options.height - chrome.topHeight - chrome.bottomHeight - padding * 2
|
|
8899
|
+
};
|
|
8900
|
+
if (fullArea.width <= 0 || fullArea.height <= 0) {
|
|
8901
|
+
return emptyLayout(fullArea, chrome, theme, options);
|
|
8902
|
+
}
|
|
8903
|
+
const sourceField = sankeySpec.encoding.source.field;
|
|
8904
|
+
const targetField = sankeySpec.encoding.target.field;
|
|
8905
|
+
const valueField = sankeySpec.encoding.value.field;
|
|
8906
|
+
const colorField = sankeySpec.encoding.color?.field;
|
|
8907
|
+
const tempNodeIds = /* @__PURE__ */ new Set();
|
|
8908
|
+
for (const row of sankeySpec.data) {
|
|
8909
|
+
tempNodeIds.add(String(row[sourceField]));
|
|
8910
|
+
tempNodeIds.add(String(row[targetField]));
|
|
8911
|
+
}
|
|
8912
|
+
const tempColorMap = buildNodeColorMap(
|
|
8913
|
+
[...tempNodeIds].map((id) => ({ id })),
|
|
8914
|
+
theme.colors.categorical,
|
|
8915
|
+
colorField,
|
|
8916
|
+
sankeySpec.data,
|
|
8917
|
+
sourceField,
|
|
8918
|
+
targetField
|
|
8919
|
+
);
|
|
8920
|
+
const legend = buildSankeyLegend(
|
|
8921
|
+
tempColorMap,
|
|
8922
|
+
colorField,
|
|
8923
|
+
sankeySpec.data,
|
|
8924
|
+
sourceField,
|
|
8925
|
+
targetField,
|
|
8926
|
+
theme,
|
|
8927
|
+
fullArea
|
|
8928
|
+
);
|
|
8929
|
+
const legendGap = legend.entries.length > 0 ? 4 : 0;
|
|
8930
|
+
const area = {
|
|
8931
|
+
x: fullArea.x,
|
|
8932
|
+
y: fullArea.y + legend.bounds.height + legendGap,
|
|
8933
|
+
width: fullArea.width,
|
|
8934
|
+
height: fullArea.height - legend.bounds.height - legendGap
|
|
8935
|
+
};
|
|
8936
|
+
if (area.height <= 0) {
|
|
8937
|
+
return emptyLayout(area, chrome, theme, options);
|
|
8938
|
+
}
|
|
8939
|
+
const { nodes, links } = computeSankeyLayout(
|
|
8940
|
+
sankeySpec.data,
|
|
8941
|
+
sourceField,
|
|
8942
|
+
targetField,
|
|
8943
|
+
valueField,
|
|
8944
|
+
area,
|
|
8945
|
+
sankeySpec.nodeWidth,
|
|
8946
|
+
sankeySpec.nodePadding,
|
|
8947
|
+
sankeySpec.nodeAlign,
|
|
8948
|
+
sankeySpec.iterations
|
|
8949
|
+
);
|
|
8950
|
+
const nodeColorMap = buildNodeColorMap(
|
|
8951
|
+
nodes,
|
|
8952
|
+
theme.colors.categorical,
|
|
8953
|
+
colorField,
|
|
8954
|
+
sankeySpec.data,
|
|
8955
|
+
sourceField,
|
|
8956
|
+
targetField
|
|
8957
|
+
);
|
|
8958
|
+
const maxDepth = nodes.reduce((max4, n) => Math.max(max4, n.depth ?? 0), 0);
|
|
8959
|
+
const nodeMarks = nodes.map((node) => {
|
|
8960
|
+
const fill = nodeColorMap.get(node.id) ?? theme.colors.categorical[0];
|
|
8961
|
+
const depth = node.depth ?? 0;
|
|
8962
|
+
return {
|
|
8963
|
+
type: "sankeyNode",
|
|
8964
|
+
x: node.x0 ?? 0,
|
|
8965
|
+
y: node.y0 ?? 0,
|
|
8966
|
+
width: (node.x1 ?? 0) - (node.x0 ?? 0),
|
|
8967
|
+
height: (node.y1 ?? 0) - (node.y0 ?? 0),
|
|
8968
|
+
fill,
|
|
8969
|
+
cornerRadius: NODE_CORNER_RADIUS,
|
|
8970
|
+
label: computeNodeLabel(node, maxDepth, theme, sankeySpec.nodeWidth),
|
|
8971
|
+
nodeId: node.id,
|
|
8972
|
+
value: node.value ?? 0,
|
|
8973
|
+
depth,
|
|
8974
|
+
data: { id: node.id, label: node.label },
|
|
8975
|
+
aria: {
|
|
8976
|
+
role: "img",
|
|
8977
|
+
label: `${node.label}: ${formatNumber4(node.value ?? 0)}`
|
|
8978
|
+
},
|
|
8979
|
+
animationIndex: 0
|
|
8980
|
+
// Reassigned below after sorting by depth
|
|
8981
|
+
};
|
|
8982
|
+
});
|
|
8983
|
+
nodeMarks.sort((a, b) => a.depth - b.depth || a.y - b.y);
|
|
8984
|
+
for (let i = 0; i < nodeMarks.length; i++) {
|
|
8985
|
+
nodeMarks[i].animationIndex = i;
|
|
8986
|
+
}
|
|
8987
|
+
const neutralColor = theme.colors.gridline;
|
|
8988
|
+
const linkMarks = links.map((link, i) => {
|
|
8989
|
+
const sourceNode = link.source;
|
|
8990
|
+
const targetNode = link.target;
|
|
8991
|
+
const srcColor = nodeColorMap.get(sourceNode.id) ?? theme.colors.categorical[0];
|
|
8992
|
+
const tgtColor = nodeColorMap.get(targetNode.id) ?? theme.colors.categorical[0];
|
|
8993
|
+
const colors = getLinkColors(sankeySpec.linkStyle, srcColor, tgtColor, neutralColor);
|
|
8994
|
+
return {
|
|
8995
|
+
type: "sankeyLink",
|
|
8996
|
+
path: generateLinkPath(link),
|
|
8997
|
+
sourceColor: colors.sourceColor,
|
|
8998
|
+
targetColor: colors.targetColor,
|
|
8999
|
+
fillOpacity: LINK_OPACITY,
|
|
9000
|
+
sourceId: sourceNode.id,
|
|
9001
|
+
targetId: targetNode.id,
|
|
9002
|
+
width: link.width ?? 0,
|
|
9003
|
+
value: link.value,
|
|
9004
|
+
data: link.data ?? {},
|
|
9005
|
+
aria: {
|
|
9006
|
+
role: "img",
|
|
9007
|
+
label: `${sourceNode.label} to ${targetNode.label}: ${formatNumber4(link.value)}`
|
|
9008
|
+
},
|
|
9009
|
+
// Links animate after nodes
|
|
9010
|
+
animationIndex: nodeMarks.length + i
|
|
9011
|
+
};
|
|
9012
|
+
});
|
|
9013
|
+
const finalLegend = buildSankeyLegend(
|
|
9014
|
+
nodeColorMap,
|
|
9015
|
+
colorField,
|
|
9016
|
+
sankeySpec.data,
|
|
9017
|
+
sourceField,
|
|
9018
|
+
targetField,
|
|
9019
|
+
theme,
|
|
9020
|
+
fullArea
|
|
9021
|
+
);
|
|
9022
|
+
const tooltipDescriptors = buildTooltipDescriptors(nodeMarks, linkMarks);
|
|
9023
|
+
const a11y = {
|
|
9024
|
+
altText: `Sankey diagram with ${nodeMarks.length} nodes and ${linkMarks.length} links`,
|
|
9025
|
+
dataTableFallback: linkMarks.map((l) => [l.sourceId, l.targetId, String(l.value)]),
|
|
9026
|
+
role: "img",
|
|
9027
|
+
keyboardNavigable: nodeMarks.length > 0
|
|
9028
|
+
};
|
|
9029
|
+
const resolvedAnimation = resolveAnimation(sankeySpec.animation);
|
|
9030
|
+
return {
|
|
9031
|
+
area,
|
|
9032
|
+
chrome,
|
|
9033
|
+
nodes: nodeMarks,
|
|
9034
|
+
links: linkMarks,
|
|
9035
|
+
legend: finalLegend,
|
|
9036
|
+
tooltipDescriptors,
|
|
9037
|
+
a11y,
|
|
9038
|
+
theme,
|
|
9039
|
+
dimensions: {
|
|
9040
|
+
width: options.width,
|
|
9041
|
+
height: options.height
|
|
9042
|
+
},
|
|
9043
|
+
animation: resolvedAnimation
|
|
9044
|
+
};
|
|
9045
|
+
}
|
|
9046
|
+
function buildSankeyLegend(nodeColorMap, colorField, data, sourceField, targetField, theme, area) {
|
|
9047
|
+
const labelStyle = {
|
|
9048
|
+
fontFamily: theme.fonts.family,
|
|
9049
|
+
fontSize: theme.fonts.sizes.small,
|
|
9050
|
+
fontWeight: theme.fonts.weights.normal,
|
|
9051
|
+
fill: theme.colors.text,
|
|
9052
|
+
lineHeight: 1.3
|
|
9053
|
+
};
|
|
9054
|
+
let entries;
|
|
9055
|
+
if (colorField) {
|
|
9056
|
+
const categoryColors = /* @__PURE__ */ new Map();
|
|
9057
|
+
const nodeCategoryMap = /* @__PURE__ */ new Map();
|
|
9058
|
+
for (const row of data) {
|
|
9059
|
+
const src = String(row[sourceField]);
|
|
9060
|
+
const tgt = String(row[targetField]);
|
|
9061
|
+
const cat = String(row[colorField]);
|
|
9062
|
+
if (!nodeCategoryMap.has(src)) nodeCategoryMap.set(src, cat);
|
|
9063
|
+
if (!nodeCategoryMap.has(tgt)) nodeCategoryMap.set(tgt, cat);
|
|
9064
|
+
}
|
|
9065
|
+
for (const [nodeId, category] of nodeCategoryMap) {
|
|
9066
|
+
if (!categoryColors.has(category)) {
|
|
9067
|
+
categoryColors.set(category, nodeColorMap.get(nodeId) ?? theme.colors.categorical[0]);
|
|
9068
|
+
}
|
|
9069
|
+
}
|
|
9070
|
+
entries = [...categoryColors.entries()].map(([label, color2]) => ({
|
|
9071
|
+
label,
|
|
9072
|
+
color: color2,
|
|
9073
|
+
shape: "square",
|
|
9074
|
+
active: true
|
|
9075
|
+
}));
|
|
9076
|
+
} else {
|
|
9077
|
+
entries = [];
|
|
9078
|
+
}
|
|
9079
|
+
let bounds = { x: 0, y: 0, width: 0, height: 0 };
|
|
9080
|
+
if (entries.length > 0) {
|
|
9081
|
+
const ROW_HEIGHT = SWATCH_SIZE3 + 4;
|
|
9082
|
+
const availableWidth = area.width;
|
|
9083
|
+
let rowCount = 1;
|
|
9084
|
+
let rowX = 0;
|
|
9085
|
+
for (const entry of entries) {
|
|
9086
|
+
const labelWidth = estimateTextWidth10(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
|
|
9087
|
+
const entryWidth = SWATCH_SIZE3 + SWATCH_GAP3 + labelWidth + ENTRY_GAP3;
|
|
9088
|
+
if (rowX > 0 && rowX + entryWidth > availableWidth) {
|
|
9089
|
+
rowCount++;
|
|
9090
|
+
rowX = entryWidth;
|
|
9091
|
+
} else {
|
|
9092
|
+
rowX += entryWidth;
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
rowCount = Math.min(rowCount, 2);
|
|
9096
|
+
const legendHeight = rowCount * ROW_HEIGHT;
|
|
9097
|
+
bounds = {
|
|
9098
|
+
x: area.x,
|
|
9099
|
+
y: area.y,
|
|
9100
|
+
width: availableWidth,
|
|
9101
|
+
height: legendHeight
|
|
9102
|
+
};
|
|
9103
|
+
}
|
|
9104
|
+
return {
|
|
9105
|
+
position: "top",
|
|
9106
|
+
entries,
|
|
9107
|
+
bounds,
|
|
9108
|
+
labelStyle,
|
|
9109
|
+
swatchSize: SWATCH_SIZE3,
|
|
9110
|
+
swatchGap: SWATCH_GAP3,
|
|
9111
|
+
entryGap: ENTRY_GAP3
|
|
9112
|
+
};
|
|
9113
|
+
}
|
|
9114
|
+
function buildTooltipDescriptors(nodes, links) {
|
|
9115
|
+
const descriptors = /* @__PURE__ */ new Map();
|
|
9116
|
+
for (const node of nodes) {
|
|
9117
|
+
const fields = [
|
|
9118
|
+
{
|
|
9119
|
+
label: "Total flow",
|
|
9120
|
+
value: formatNumber4(node.value)
|
|
9121
|
+
}
|
|
9122
|
+
];
|
|
9123
|
+
descriptors.set(`node-${node.nodeId}`, {
|
|
9124
|
+
title: node.label.text,
|
|
9125
|
+
fields
|
|
9126
|
+
});
|
|
9127
|
+
}
|
|
9128
|
+
for (const link of links) {
|
|
9129
|
+
const fields = [
|
|
9130
|
+
{
|
|
9131
|
+
label: "Flow",
|
|
9132
|
+
value: formatNumber4(link.value)
|
|
9133
|
+
}
|
|
9134
|
+
];
|
|
9135
|
+
descriptors.set(`link-${link.sourceId}-${link.targetId}`, {
|
|
9136
|
+
title: `${link.sourceId} \u2192 ${link.targetId}`,
|
|
9137
|
+
fields
|
|
9138
|
+
});
|
|
9139
|
+
}
|
|
9140
|
+
return descriptors;
|
|
9141
|
+
}
|
|
9142
|
+
function emptyLayout(area, chrome, theme, options) {
|
|
9143
|
+
return {
|
|
9144
|
+
area,
|
|
9145
|
+
chrome,
|
|
9146
|
+
nodes: [],
|
|
9147
|
+
links: [],
|
|
9148
|
+
legend: {
|
|
9149
|
+
position: "top",
|
|
9150
|
+
entries: [],
|
|
9151
|
+
bounds: { x: 0, y: 0, width: 0, height: 0 },
|
|
9152
|
+
labelStyle: {
|
|
9153
|
+
fontFamily: theme.fonts.family,
|
|
9154
|
+
fontSize: theme.fonts.sizes.small,
|
|
9155
|
+
fontWeight: theme.fonts.weights.normal,
|
|
9156
|
+
fill: theme.colors.text,
|
|
9157
|
+
lineHeight: 1.3
|
|
9158
|
+
},
|
|
9159
|
+
swatchSize: SWATCH_SIZE3,
|
|
9160
|
+
swatchGap: SWATCH_GAP3,
|
|
9161
|
+
entryGap: ENTRY_GAP3
|
|
9162
|
+
},
|
|
9163
|
+
tooltipDescriptors: /* @__PURE__ */ new Map(),
|
|
9164
|
+
a11y: {
|
|
9165
|
+
altText: "Empty sankey diagram",
|
|
9166
|
+
dataTableFallback: [],
|
|
9167
|
+
role: "img",
|
|
9168
|
+
keyboardNavigable: false
|
|
9169
|
+
},
|
|
9170
|
+
theme,
|
|
9171
|
+
dimensions: {
|
|
9172
|
+
width: options.width,
|
|
9173
|
+
height: options.height
|
|
9174
|
+
}
|
|
9175
|
+
};
|
|
9176
|
+
}
|
|
9177
|
+
|
|
8195
9178
|
// src/tables/compile-table.ts
|
|
8196
|
-
import { computeChrome as
|
|
9179
|
+
import { computeChrome as computeChrome4, estimateTextWidth as estimateTextWidth11 } from "@opendata-ai/openchart-core";
|
|
8197
9180
|
|
|
8198
9181
|
// src/tables/bar-column.ts
|
|
8199
9182
|
var NEGATIVE_BAR_COLOR = "#c44e52";
|
|
8200
|
-
function computeBarCell(
|
|
9183
|
+
function computeBarCell(value2, config, columnMax, columnMin, theme, _darkMode) {
|
|
8201
9184
|
const barColor = config.color ?? theme.colors.categorical[0];
|
|
8202
9185
|
const hasNegatives = columnMin < 0;
|
|
8203
|
-
if (!Number.isFinite(
|
|
9186
|
+
if (!Number.isFinite(value2)) {
|
|
8204
9187
|
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
8205
9188
|
}
|
|
8206
9189
|
if (!hasNegatives) {
|
|
@@ -8208,7 +9191,7 @@ function computeBarCell(value, config, columnMax, columnMin, theme, _darkMode) {
|
|
|
8208
9191
|
if (maxValue <= 0) {
|
|
8209
9192
|
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
8210
9193
|
}
|
|
8211
|
-
const barPercent2 = Math.max(0, Math.min(1,
|
|
9194
|
+
const barPercent2 = Math.max(0, Math.min(1, value2 / maxValue));
|
|
8212
9195
|
return { barPercent: barPercent2, barOffset: 0, barColor, isNegative: false };
|
|
8213
9196
|
}
|
|
8214
9197
|
const maxPos = config.maxValue ?? columnMax;
|
|
@@ -8218,11 +9201,11 @@ function computeBarCell(value, config, columnMax, columnMin, theme, _darkMode) {
|
|
|
8218
9201
|
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
8219
9202
|
}
|
|
8220
9203
|
const zeroPos = absMin / totalRange;
|
|
8221
|
-
if (
|
|
8222
|
-
const barPercent2 =
|
|
9204
|
+
if (value2 >= 0) {
|
|
9205
|
+
const barPercent2 = value2 / totalRange;
|
|
8223
9206
|
return { barPercent: barPercent2, barOffset: zeroPos, barColor, isNegative: false };
|
|
8224
9207
|
}
|
|
8225
|
-
const barPercent = Math.abs(
|
|
9208
|
+
const barPercent = Math.abs(value2) / totalRange;
|
|
8226
9209
|
return {
|
|
8227
9210
|
barPercent,
|
|
8228
9211
|
barOffset: zeroPos - barPercent,
|
|
@@ -8231,24 +9214,24 @@ function computeBarCell(value, config, columnMax, columnMin, theme, _darkMode) {
|
|
|
8231
9214
|
};
|
|
8232
9215
|
}
|
|
8233
9216
|
function computeColumnMax(data, key) {
|
|
8234
|
-
let
|
|
9217
|
+
let max4 = 0;
|
|
8235
9218
|
for (const row of data) {
|
|
8236
9219
|
const val = row[key];
|
|
8237
|
-
if (typeof val === "number" && Number.isFinite(val) && val >
|
|
8238
|
-
|
|
9220
|
+
if (typeof val === "number" && Number.isFinite(val) && val > max4) {
|
|
9221
|
+
max4 = val;
|
|
8239
9222
|
}
|
|
8240
9223
|
}
|
|
8241
|
-
return
|
|
9224
|
+
return max4;
|
|
8242
9225
|
}
|
|
8243
9226
|
function computeColumnMin(data, key) {
|
|
8244
|
-
let
|
|
9227
|
+
let min4 = 0;
|
|
8245
9228
|
for (const row of data) {
|
|
8246
9229
|
const val = row[key];
|
|
8247
|
-
if (typeof val === "number" && Number.isFinite(val) && val <
|
|
8248
|
-
|
|
9230
|
+
if (typeof val === "number" && Number.isFinite(val) && val < min4) {
|
|
9231
|
+
min4 = val;
|
|
8249
9232
|
}
|
|
8250
9233
|
}
|
|
8251
|
-
return
|
|
9234
|
+
return min4;
|
|
8252
9235
|
}
|
|
8253
9236
|
|
|
8254
9237
|
// src/tables/category-colors.ts
|
|
@@ -8301,67 +9284,67 @@ function computeCategoryColors(data, column, theme, darkMode) {
|
|
|
8301
9284
|
}
|
|
8302
9285
|
|
|
8303
9286
|
// src/tables/format-cells.ts
|
|
8304
|
-
import { formatDate as formatDate2, formatNumber as
|
|
8305
|
-
function isNumericValue(
|
|
8306
|
-
if (typeof
|
|
9287
|
+
import { formatDate as formatDate2, formatNumber as formatNumber5 } from "@opendata-ai/openchart-core";
|
|
9288
|
+
function isNumericValue(value2) {
|
|
9289
|
+
if (typeof value2 === "number") return Number.isFinite(value2);
|
|
8307
9290
|
return false;
|
|
8308
9291
|
}
|
|
8309
|
-
function isDateValue(
|
|
8310
|
-
if (
|
|
9292
|
+
function isDateValue(value2) {
|
|
9293
|
+
if (value2 instanceof Date) return !Number.isNaN(value2.getTime());
|
|
8311
9294
|
return false;
|
|
8312
9295
|
}
|
|
8313
|
-
function formatCell(
|
|
9296
|
+
function formatCell(value2, column) {
|
|
8314
9297
|
const style = {};
|
|
8315
|
-
if (
|
|
9298
|
+
if (value2 == null) {
|
|
8316
9299
|
return {
|
|
8317
|
-
value,
|
|
9300
|
+
value: value2,
|
|
8318
9301
|
formattedValue: "",
|
|
8319
9302
|
style
|
|
8320
9303
|
};
|
|
8321
9304
|
}
|
|
8322
|
-
if (column.format && isNumericValue(
|
|
9305
|
+
if (column.format && isNumericValue(value2)) {
|
|
8323
9306
|
try {
|
|
8324
9307
|
const formatter = format(column.format);
|
|
8325
9308
|
return {
|
|
8326
|
-
value,
|
|
8327
|
-
formattedValue: formatter(
|
|
9309
|
+
value: value2,
|
|
9310
|
+
formattedValue: formatter(value2),
|
|
8328
9311
|
style
|
|
8329
9312
|
};
|
|
8330
9313
|
} catch {
|
|
8331
9314
|
}
|
|
8332
9315
|
}
|
|
8333
|
-
if (isNumericValue(
|
|
9316
|
+
if (isNumericValue(value2)) {
|
|
8334
9317
|
return {
|
|
8335
|
-
value,
|
|
8336
|
-
formattedValue:
|
|
9318
|
+
value: value2,
|
|
9319
|
+
formattedValue: formatNumber5(value2),
|
|
8337
9320
|
style
|
|
8338
9321
|
};
|
|
8339
9322
|
}
|
|
8340
|
-
if (isDateValue(
|
|
9323
|
+
if (isDateValue(value2)) {
|
|
8341
9324
|
return {
|
|
8342
|
-
value,
|
|
8343
|
-
formattedValue: formatDate2(
|
|
9325
|
+
value: value2,
|
|
9326
|
+
formattedValue: formatDate2(value2),
|
|
8344
9327
|
style
|
|
8345
9328
|
};
|
|
8346
9329
|
}
|
|
8347
9330
|
return {
|
|
8348
|
-
value,
|
|
8349
|
-
formattedValue: String(
|
|
9331
|
+
value: value2,
|
|
9332
|
+
formattedValue: String(value2),
|
|
8350
9333
|
style
|
|
8351
9334
|
};
|
|
8352
9335
|
}
|
|
8353
|
-
function formatValueForSearch(
|
|
8354
|
-
if (
|
|
8355
|
-
if (column.format && isNumericValue(
|
|
9336
|
+
function formatValueForSearch(value2, column) {
|
|
9337
|
+
if (value2 == null) return "";
|
|
9338
|
+
if (column.format && isNumericValue(value2)) {
|
|
8356
9339
|
try {
|
|
8357
|
-
return format(column.format)(
|
|
9340
|
+
return format(column.format)(value2);
|
|
8358
9341
|
} catch {
|
|
8359
9342
|
}
|
|
8360
9343
|
}
|
|
8361
|
-
if (isNumericValue(
|
|
8362
|
-
return
|
|
9344
|
+
if (isNumericValue(value2)) {
|
|
9345
|
+
return formatNumber5(value2);
|
|
8363
9346
|
}
|
|
8364
|
-
return String(
|
|
9347
|
+
return String(value2);
|
|
8365
9348
|
}
|
|
8366
9349
|
|
|
8367
9350
|
// src/tables/heatmap.ts
|
|
@@ -8406,13 +9389,13 @@ function computeHeatmapColors(data, column, theme, darkMode) {
|
|
|
8406
9389
|
if (config.domain) {
|
|
8407
9390
|
domain = config.domain;
|
|
8408
9391
|
} else {
|
|
8409
|
-
let
|
|
8410
|
-
let
|
|
8411
|
-
for (const { value } of numericValues) {
|
|
8412
|
-
if (
|
|
8413
|
-
if (
|
|
9392
|
+
let min4 = Infinity;
|
|
9393
|
+
let max4 = -Infinity;
|
|
9394
|
+
for (const { value: value2 } of numericValues) {
|
|
9395
|
+
if (value2 < min4) min4 = value2;
|
|
9396
|
+
if (value2 > max4) max4 = value2;
|
|
8414
9397
|
}
|
|
8415
|
-
domain = [
|
|
9398
|
+
domain = [min4, max4];
|
|
8416
9399
|
}
|
|
8417
9400
|
let stops = resolvePalette(config.palette, theme);
|
|
8418
9401
|
if (darkMode) {
|
|
@@ -8422,8 +9405,8 @@ function computeHeatmapColors(data, column, theme, darkMode) {
|
|
|
8422
9405
|
}
|
|
8423
9406
|
const interpolator = interpolatorFromStops(stops);
|
|
8424
9407
|
const scale = sequential(interpolator).domain(domain).clamp(true);
|
|
8425
|
-
for (const { index, value } of numericValues) {
|
|
8426
|
-
const bg = scale(
|
|
9408
|
+
for (const { index, value: value2 } of numericValues) {
|
|
9409
|
+
const bg = scale(value2);
|
|
8427
9410
|
const textColor = accessibleTextColor(bg);
|
|
8428
9411
|
result.set(index, {
|
|
8429
9412
|
backgroundColor: bg,
|
|
@@ -8528,14 +9511,14 @@ function computeSparkline(values, config, theme, _darkMode) {
|
|
|
8528
9511
|
if (values.length === 0) return null;
|
|
8529
9512
|
const type = config.type ?? "line";
|
|
8530
9513
|
const color2 = config.color ?? theme.colors.categorical[0];
|
|
8531
|
-
let
|
|
8532
|
-
let
|
|
9514
|
+
let min4 = Infinity;
|
|
9515
|
+
let max4 = -Infinity;
|
|
8533
9516
|
for (const v of values) {
|
|
8534
|
-
if (v <
|
|
8535
|
-
if (v >
|
|
9517
|
+
if (v < min4) min4 = v;
|
|
9518
|
+
if (v > max4) max4 = v;
|
|
8536
9519
|
}
|
|
8537
|
-
const range2 =
|
|
8538
|
-
const normalize2 = (v) => range2 === 0 ? 0.5 : (v -
|
|
9520
|
+
const range2 = max4 - min4;
|
|
9521
|
+
const normalize2 = (v) => range2 === 0 ? 0.5 : (v - min4) / range2;
|
|
8539
9522
|
const startValue = values[0];
|
|
8540
9523
|
const endValue = values[values.length - 1];
|
|
8541
9524
|
if (type === "line") {
|
|
@@ -8600,13 +9583,13 @@ function estimateColumnWidth(col, data, fontSize) {
|
|
|
8600
9583
|
if (col.image) return (col.image.width ?? 24) + PADDING;
|
|
8601
9584
|
if (col.flag) return 60;
|
|
8602
9585
|
const label = col.label ?? col.key;
|
|
8603
|
-
const headerWidth =
|
|
9586
|
+
const headerWidth = estimateTextWidth11(label, fontSize, 600) + PADDING;
|
|
8604
9587
|
const sampleSize = Math.min(100, data.length);
|
|
8605
9588
|
let maxDataWidth = 0;
|
|
8606
9589
|
for (let i = 0; i < sampleSize; i++) {
|
|
8607
9590
|
const val = data[i][col.key];
|
|
8608
9591
|
const text = val == null ? "" : String(val);
|
|
8609
|
-
const width =
|
|
9592
|
+
const width = estimateTextWidth11(text, fontSize, 400) + PADDING;
|
|
8610
9593
|
if (width > maxDataWidth) maxDataWidth = width;
|
|
8611
9594
|
}
|
|
8612
9595
|
return Math.max(MIN_WIDTH, headerWidth, maxDataWidth);
|
|
@@ -8626,8 +9609,8 @@ function resolveColumns(columns, data, totalWidth, theme) {
|
|
|
8626
9609
|
}
|
|
8627
9610
|
return estimateColumnWidth(col, data, fontSize);
|
|
8628
9611
|
});
|
|
8629
|
-
const fixedTotal = naturalWidths.reduce((
|
|
8630
|
-
const flexTotal = naturalWidths.reduce((
|
|
9612
|
+
const fixedTotal = naturalWidths.reduce((sum2, w, i) => sum2 + (isFixed[i] ? w : 0), 0);
|
|
9613
|
+
const flexTotal = naturalWidths.reduce((sum2, w, i) => sum2 + (isFixed[i] ? 0 : w), 0);
|
|
8631
9614
|
const remainingWidth = totalWidth - fixedTotal;
|
|
8632
9615
|
const flexScale = flexTotal > 0 && remainingWidth > 0 ? remainingWidth / flexTotal : 1;
|
|
8633
9616
|
return columns.map((col, i) => ({
|
|
@@ -8639,9 +9622,9 @@ function resolveColumns(columns, data, totalWidth, theme) {
|
|
|
8639
9622
|
cellType: determineCellType(col)
|
|
8640
9623
|
}));
|
|
8641
9624
|
}
|
|
8642
|
-
function buildCell(
|
|
8643
|
-
const base = formatCell(
|
|
8644
|
-
if (typeof
|
|
9625
|
+
function buildCell(value2, column, resolvedColumn, heatmapStyle, categoryStyle, barData, sparklineData) {
|
|
9626
|
+
const base = formatCell(value2, column);
|
|
9627
|
+
if (typeof value2 === "number") {
|
|
8645
9628
|
base.style = { ...base.style, fontVariant: "tabular-nums" };
|
|
8646
9629
|
}
|
|
8647
9630
|
const cellType = resolvedColumn.cellType;
|
|
@@ -8680,7 +9663,7 @@ function buildCell(value, column, resolvedColumn, heatmapStyle, categoryStyle, b
|
|
|
8680
9663
|
};
|
|
8681
9664
|
}
|
|
8682
9665
|
case "image": {
|
|
8683
|
-
const src = typeof
|
|
9666
|
+
const src = typeof value2 === "string" ? value2 : "";
|
|
8684
9667
|
const imgConfig = column.image ?? {};
|
|
8685
9668
|
return {
|
|
8686
9669
|
...base,
|
|
@@ -8692,7 +9675,7 @@ function buildCell(value, column, resolvedColumn, heatmapStyle, categoryStyle, b
|
|
|
8692
9675
|
};
|
|
8693
9676
|
}
|
|
8694
9677
|
case "flag": {
|
|
8695
|
-
const code = typeof
|
|
9678
|
+
const code = typeof value2 === "string" ? value2 : "";
|
|
8696
9679
|
return {
|
|
8697
9680
|
...base,
|
|
8698
9681
|
cellType: "flag",
|
|
@@ -8767,13 +9750,13 @@ function compileTableLayout(spec, options, theme) {
|
|
|
8767
9750
|
const rowId = spec.rowKey ? String(row[spec.rowKey] ?? origIdx) : String(origIdx);
|
|
8768
9751
|
const cells = spec.columns.map((col, c) => {
|
|
8769
9752
|
const resolved = resolvedColumns[c];
|
|
8770
|
-
const
|
|
9753
|
+
const value2 = row[col.key];
|
|
8771
9754
|
const heatmapStyle = heatmapMaps.get(col.key)?.get(origIdx);
|
|
8772
9755
|
const categoryStyle = categoryMaps.get(col.key)?.get(origIdx);
|
|
8773
9756
|
let barData;
|
|
8774
|
-
if (resolved.cellType === "bar" && col.bar && typeof
|
|
9757
|
+
if (resolved.cellType === "bar" && col.bar && typeof value2 === "number") {
|
|
8775
9758
|
barData = computeBarCell(
|
|
8776
|
-
|
|
9759
|
+
value2,
|
|
8777
9760
|
col.bar,
|
|
8778
9761
|
barMaxes.get(col.key) ?? 0,
|
|
8779
9762
|
barMins.get(col.key) ?? 0,
|
|
@@ -8785,11 +9768,11 @@ function compileTableLayout(spec, options, theme) {
|
|
|
8785
9768
|
if (resolved.cellType === "sparkline" && col.sparkline) {
|
|
8786
9769
|
sparklineData = computeSparklineForRow(row, col.key, col.sparkline, theme, darkMode);
|
|
8787
9770
|
}
|
|
8788
|
-
return buildCell(
|
|
9771
|
+
return buildCell(value2, col, resolved, heatmapStyle, categoryStyle, barData, sparklineData);
|
|
8789
9772
|
});
|
|
8790
9773
|
return { id: rowId, cells, data: row };
|
|
8791
9774
|
});
|
|
8792
|
-
const chrome =
|
|
9775
|
+
const chrome = computeChrome4(
|
|
8793
9776
|
{
|
|
8794
9777
|
title: spec.chrome.title,
|
|
8795
9778
|
subtitle: spec.chrome.subtitle,
|
|
@@ -8826,25 +9809,25 @@ function compileTableLayout(spec, options, theme) {
|
|
|
8826
9809
|
}
|
|
8827
9810
|
|
|
8828
9811
|
// src/tooltips/compute.ts
|
|
8829
|
-
import { buildTemporalFormatter as buildTemporalFormatter2, formatDate as formatDate3, formatNumber as
|
|
8830
|
-
function formatValue(
|
|
8831
|
-
if (
|
|
8832
|
-
if (fieldType === "temporal" ||
|
|
9812
|
+
import { buildTemporalFormatter as buildTemporalFormatter2, formatDate as formatDate3, formatNumber as formatNumber6 } from "@opendata-ai/openchart-core";
|
|
9813
|
+
function formatValue(value2, fieldType, format2) {
|
|
9814
|
+
if (value2 == null) return "";
|
|
9815
|
+
if (fieldType === "temporal" || value2 instanceof Date) {
|
|
8833
9816
|
const temporalFmt = buildTemporalFormatter2(format2);
|
|
8834
|
-
if (temporalFmt) return temporalFmt(
|
|
8835
|
-
return formatDate3(
|
|
9817
|
+
if (temporalFmt) return temporalFmt(value2);
|
|
9818
|
+
return formatDate3(value2);
|
|
8836
9819
|
}
|
|
8837
|
-
if (typeof
|
|
9820
|
+
if (typeof value2 === "number") {
|
|
8838
9821
|
if (format2) {
|
|
8839
9822
|
try {
|
|
8840
|
-
return format(format2)(
|
|
9823
|
+
return format(format2)(value2);
|
|
8841
9824
|
} catch {
|
|
8842
|
-
return
|
|
9825
|
+
return formatNumber6(value2);
|
|
8843
9826
|
}
|
|
8844
9827
|
}
|
|
8845
|
-
return
|
|
9828
|
+
return formatNumber6(value2);
|
|
8846
9829
|
}
|
|
8847
|
-
return String(
|
|
9830
|
+
return String(value2);
|
|
8848
9831
|
}
|
|
8849
9832
|
function buildExplicitTooltipFields(row, channels) {
|
|
8850
9833
|
return channels.map((ch) => ({
|
|
@@ -9009,16 +9992,16 @@ function runBin(data, transform) {
|
|
|
9009
9992
|
const field = transform.field;
|
|
9010
9993
|
let extent2 = params.extent;
|
|
9011
9994
|
if (!extent2) {
|
|
9012
|
-
let
|
|
9013
|
-
let
|
|
9995
|
+
let min4 = Infinity;
|
|
9996
|
+
let max4 = -Infinity;
|
|
9014
9997
|
for (const row of data) {
|
|
9015
9998
|
const v = Number(row[field]);
|
|
9016
9999
|
if (Number.isFinite(v)) {
|
|
9017
|
-
if (v <
|
|
9018
|
-
if (v >
|
|
10000
|
+
if (v < min4) min4 = v;
|
|
10001
|
+
if (v > max4) max4 = v;
|
|
9019
10002
|
}
|
|
9020
10003
|
}
|
|
9021
|
-
extent2 = [
|
|
10004
|
+
extent2 = [min4 === Infinity ? 0 : min4, max4 === -Infinity ? 0 : max4];
|
|
9022
10005
|
}
|
|
9023
10006
|
const step = params.step ?? computeStep(extent2, maxbins, nice2);
|
|
9024
10007
|
const [startAs, endAs] = Array.isArray(transform.as) ? transform.as : [transform.as, void 0];
|
|
@@ -9125,10 +10108,10 @@ function extractTimeUnit(date2, unit2) {
|
|
|
9125
10108
|
return `${String(date2.getHours()).padStart(2, "0")}:${String(date2.getMinutes()).padStart(2, "0")}`;
|
|
9126
10109
|
}
|
|
9127
10110
|
}
|
|
9128
|
-
function toDate(
|
|
9129
|
-
if (
|
|
9130
|
-
if (typeof
|
|
9131
|
-
const d = new Date(
|
|
10111
|
+
function toDate(value2) {
|
|
10112
|
+
if (value2 instanceof Date) return value2;
|
|
10113
|
+
if (typeof value2 === "string" || typeof value2 === "number") {
|
|
10114
|
+
const d = new Date(value2);
|
|
9132
10115
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
9133
10116
|
}
|
|
9134
10117
|
return null;
|
|
@@ -9212,28 +10195,28 @@ function computeBandRowObstacles(marks, scales) {
|
|
|
9212
10195
|
const rows = /* @__PURE__ */ new Map();
|
|
9213
10196
|
for (const mark of marks) {
|
|
9214
10197
|
let cy;
|
|
9215
|
-
let
|
|
9216
|
-
let
|
|
10198
|
+
let left2;
|
|
10199
|
+
let right2;
|
|
9217
10200
|
if (mark.type === "point") {
|
|
9218
10201
|
const pm = mark;
|
|
9219
10202
|
cy = pm.cy;
|
|
9220
|
-
|
|
9221
|
-
|
|
10203
|
+
left2 = pm.cx - pm.r;
|
|
10204
|
+
right2 = pm.cx + pm.r;
|
|
9222
10205
|
} else if (mark.type === "rect") {
|
|
9223
10206
|
const rm = mark;
|
|
9224
10207
|
cy = rm.y + rm.height / 2;
|
|
9225
|
-
|
|
9226
|
-
|
|
10208
|
+
left2 = rm.x;
|
|
10209
|
+
right2 = rm.x + rm.width;
|
|
9227
10210
|
} else {
|
|
9228
10211
|
continue;
|
|
9229
10212
|
}
|
|
9230
10213
|
const key = Math.round(cy);
|
|
9231
10214
|
const existing = rows.get(key);
|
|
9232
10215
|
if (existing) {
|
|
9233
|
-
existing.minX = Math.min(existing.minX,
|
|
9234
|
-
existing.maxX = Math.max(existing.maxX,
|
|
10216
|
+
existing.minX = Math.min(existing.minX, left2);
|
|
10217
|
+
existing.maxX = Math.max(existing.maxX, right2);
|
|
9235
10218
|
} else {
|
|
9236
|
-
rows.set(key, { minX:
|
|
10219
|
+
rows.set(key, { minX: left2, maxX: right2, bandY: cy });
|
|
9237
10220
|
}
|
|
9238
10221
|
}
|
|
9239
10222
|
const bandScale = scales.y.scale;
|
|
@@ -9258,6 +10241,9 @@ function compileChart(spec, options) {
|
|
|
9258
10241
|
if ("type" in normalized && normalized.type === "graph") {
|
|
9259
10242
|
throw new Error("compileChart received a graph spec. Use compileGraph instead.");
|
|
9260
10243
|
}
|
|
10244
|
+
if ("type" in normalized && normalized.type === "sankey") {
|
|
10245
|
+
throw new Error("compileChart received a sankey spec. Use compileSankey instead.");
|
|
10246
|
+
}
|
|
9261
10247
|
let chartSpec = normalized;
|
|
9262
10248
|
const rawTransforms = spec.transform;
|
|
9263
10249
|
if (rawTransforms && rawTransforms.length > 0) {
|
|
@@ -9307,9 +10293,9 @@ function compileChart(spec, options) {
|
|
|
9307
10293
|
const rawAnimationSpec = overrides?.[breakpoint]?.animation ?? rawSpec.animation;
|
|
9308
10294
|
const resolvedAnimation = resolveAnimation(rawAnimationSpec);
|
|
9309
10295
|
const mergedThemeConfig = options.theme ? { ...chartSpec.theme, ...options.theme } : chartSpec.theme;
|
|
9310
|
-
let theme =
|
|
10296
|
+
let theme = resolveTheme3(mergedThemeConfig);
|
|
9311
10297
|
if (options.darkMode) {
|
|
9312
|
-
theme =
|
|
10298
|
+
theme = adaptTheme3(theme);
|
|
9313
10299
|
}
|
|
9314
10300
|
const preliminaryArea = {
|
|
9315
10301
|
x: 0,
|
|
@@ -9571,15 +10557,18 @@ function compileTable(spec, options) {
|
|
|
9571
10557
|
}
|
|
9572
10558
|
const tableSpec = normalized;
|
|
9573
10559
|
const mergedThemeConfig = options.theme ? { ...tableSpec.theme, ...options.theme } : tableSpec.theme;
|
|
9574
|
-
let theme =
|
|
10560
|
+
let theme = resolveTheme3(mergedThemeConfig);
|
|
9575
10561
|
if (options.darkMode) {
|
|
9576
|
-
theme =
|
|
10562
|
+
theme = adaptTheme3(theme);
|
|
9577
10563
|
}
|
|
9578
10564
|
return compileTableLayout(tableSpec, options, theme);
|
|
9579
10565
|
}
|
|
9580
10566
|
function compileGraph2(spec, options) {
|
|
9581
10567
|
return compileGraph(spec, options);
|
|
9582
10568
|
}
|
|
10569
|
+
function compileSankey2(spec, options) {
|
|
10570
|
+
return compileSankey(spec, options);
|
|
10571
|
+
}
|
|
9583
10572
|
export {
|
|
9584
10573
|
clampStaggerDelay,
|
|
9585
10574
|
clearRenderers,
|
|
@@ -9587,6 +10576,7 @@ export {
|
|
|
9587
10576
|
compileChart,
|
|
9588
10577
|
compileGraph2 as compileGraph,
|
|
9589
10578
|
compileLayer,
|
|
10579
|
+
compileSankey2 as compileSankey,
|
|
9590
10580
|
compileTable,
|
|
9591
10581
|
evaluatePredicate,
|
|
9592
10582
|
getChartRenderer,
|