@opendata-ai/openchart-core 7.2.4 → 7.4.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.js CHANGED
@@ -813,6 +813,441 @@ var DIVERGING_PALETTES = {
813
813
  brownTeal: [...DIVERGING_BROWN_TEAL.stops]
814
814
  };
815
815
 
816
+ // src/layout/text-measure.ts
817
+ var CHAR_WIDTHS_400 = {
818
+ " ": 0.278,
819
+ "!": 0.278,
820
+ '"': 0.355,
821
+ "#": 0.556,
822
+ $: 0.556,
823
+ "%": 0.889,
824
+ "&": 0.667,
825
+ "'": 0.191,
826
+ "(": 0.333,
827
+ ")": 0.333,
828
+ "*": 0.389,
829
+ "+": 0.584,
830
+ ",": 0.278,
831
+ "-": 0.333,
832
+ ".": 0.278,
833
+ "/": 0.278,
834
+ "0": 0.556,
835
+ "1": 0.556,
836
+ "2": 0.556,
837
+ "3": 0.556,
838
+ "4": 0.556,
839
+ "5": 0.556,
840
+ "6": 0.556,
841
+ "7": 0.556,
842
+ "8": 0.556,
843
+ "9": 0.556,
844
+ ":": 0.278,
845
+ ";": 0.278,
846
+ "<": 0.584,
847
+ "=": 0.584,
848
+ ">": 0.584,
849
+ "?": 0.556,
850
+ "@": 1.015,
851
+ A: 0.667,
852
+ B: 0.667,
853
+ C: 0.722,
854
+ D: 0.722,
855
+ E: 0.667,
856
+ F: 0.611,
857
+ G: 0.778,
858
+ H: 0.722,
859
+ I: 0.278,
860
+ J: 0.5,
861
+ K: 0.667,
862
+ L: 0.556,
863
+ M: 0.833,
864
+ N: 0.722,
865
+ O: 0.778,
866
+ P: 0.667,
867
+ Q: 0.778,
868
+ R: 0.722,
869
+ S: 0.667,
870
+ T: 0.611,
871
+ U: 0.722,
872
+ V: 0.667,
873
+ W: 0.944,
874
+ X: 0.667,
875
+ Y: 0.667,
876
+ Z: 0.611,
877
+ "[": 0.278,
878
+ "\\": 0.278,
879
+ "]": 0.278,
880
+ "^": 0.469,
881
+ _: 0.556,
882
+ "`": 0.333,
883
+ a: 0.556,
884
+ b: 0.556,
885
+ c: 0.5,
886
+ d: 0.556,
887
+ e: 0.556,
888
+ f: 0.278,
889
+ g: 0.556,
890
+ h: 0.556,
891
+ i: 0.222,
892
+ j: 0.222,
893
+ k: 0.5,
894
+ l: 0.222,
895
+ m: 0.833,
896
+ n: 0.556,
897
+ o: 0.556,
898
+ p: 0.556,
899
+ q: 0.556,
900
+ r: 0.333,
901
+ s: 0.5,
902
+ t: 0.278,
903
+ u: 0.556,
904
+ v: 0.5,
905
+ w: 0.722,
906
+ x: 0.5,
907
+ y: 0.5,
908
+ z: 0.5,
909
+ "{": 0.334,
910
+ "|": 0.26,
911
+ "}": 0.334,
912
+ "~": 0.584,
913
+ "\u20B9": 0.52,
914
+ "\u20A9": 1.022,
915
+ "\xA5": 0.556,
916
+ "\uFFE5": 1,
917
+ "\u20AC": 0.744,
918
+ "\xA3": 0.556,
919
+ "\xA2": 0.556,
920
+ "\xA7": 0.556,
921
+ "\xA9": 0.737,
922
+ "\xAE": 0.737,
923
+ "\u2122": 1,
924
+ "\xB0": 0.4,
925
+ "\u2013": 0.556,
926
+ "\u2014": 1,
927
+ "\u2018": 0.222,
928
+ "\u2019": 0.222,
929
+ "\u201C": 0.333,
930
+ "\u201D": 0.333,
931
+ "\u2026": 1,
932
+ "\xB1": 0.549,
933
+ "\xD7": 0.584,
934
+ "\xF7": 0.549
935
+ };
936
+ var AVG_CHAR_WIDTH_400 = 0.53;
937
+ var CHAR_WIDTHS_700 = {
938
+ " ": 0.278,
939
+ "!": 0.333,
940
+ '"': 0.474,
941
+ "#": 0.556,
942
+ $: 0.556,
943
+ "%": 0.889,
944
+ "&": 0.722,
945
+ "'": 0.238,
946
+ "(": 0.333,
947
+ ")": 0.333,
948
+ "*": 0.389,
949
+ "+": 0.584,
950
+ ",": 0.278,
951
+ "-": 0.333,
952
+ ".": 0.278,
953
+ "/": 0.278,
954
+ "0": 0.556,
955
+ "1": 0.556,
956
+ "2": 0.556,
957
+ "3": 0.556,
958
+ "4": 0.556,
959
+ "5": 0.556,
960
+ "6": 0.556,
961
+ "7": 0.556,
962
+ "8": 0.556,
963
+ "9": 0.556,
964
+ ":": 0.333,
965
+ ";": 0.333,
966
+ "<": 0.584,
967
+ "=": 0.584,
968
+ ">": 0.584,
969
+ "?": 0.611,
970
+ "@": 0.975,
971
+ A: 0.722,
972
+ B: 0.722,
973
+ C: 0.722,
974
+ D: 0.722,
975
+ E: 0.667,
976
+ F: 0.611,
977
+ G: 0.778,
978
+ H: 0.722,
979
+ I: 0.278,
980
+ J: 0.556,
981
+ K: 0.722,
982
+ L: 0.611,
983
+ M: 0.833,
984
+ N: 0.722,
985
+ O: 0.778,
986
+ P: 0.667,
987
+ Q: 0.778,
988
+ R: 0.722,
989
+ S: 0.667,
990
+ T: 0.611,
991
+ U: 0.722,
992
+ V: 0.667,
993
+ W: 0.944,
994
+ X: 0.667,
995
+ Y: 0.667,
996
+ Z: 0.611,
997
+ "[": 0.333,
998
+ "\\": 0.278,
999
+ "]": 0.333,
1000
+ "^": 0.584,
1001
+ _: 0.556,
1002
+ "`": 0.333,
1003
+ a: 0.556,
1004
+ b: 0.611,
1005
+ c: 0.556,
1006
+ d: 0.611,
1007
+ e: 0.556,
1008
+ f: 0.333,
1009
+ g: 0.611,
1010
+ h: 0.611,
1011
+ i: 0.278,
1012
+ j: 0.278,
1013
+ k: 0.556,
1014
+ l: 0.278,
1015
+ m: 0.889,
1016
+ n: 0.611,
1017
+ o: 0.611,
1018
+ p: 0.611,
1019
+ q: 0.611,
1020
+ r: 0.389,
1021
+ s: 0.556,
1022
+ t: 0.333,
1023
+ u: 0.611,
1024
+ v: 0.556,
1025
+ w: 0.778,
1026
+ x: 0.556,
1027
+ y: 0.556,
1028
+ z: 0.5,
1029
+ "{": 0.389,
1030
+ "|": 0.28,
1031
+ "}": 0.389,
1032
+ "~": 0.584,
1033
+ "\u20B9": 0.553,
1034
+ "\u20A9": 1.024,
1035
+ "\xA5": 0.556,
1036
+ "\uFFE5": 1,
1037
+ "\u20AC": 0.744,
1038
+ "\xA3": 0.556,
1039
+ "\xA2": 0.556,
1040
+ "\xA7": 0.556,
1041
+ "\xA9": 0.737,
1042
+ "\xAE": 0.737,
1043
+ "\u2122": 1,
1044
+ "\xB0": 0.4,
1045
+ "\u2013": 0.556,
1046
+ "\u2014": 1,
1047
+ "\u2018": 0.278,
1048
+ "\u2019": 0.278,
1049
+ "\u201C": 0.5,
1050
+ "\u201D": 0.5,
1051
+ "\u2026": 1,
1052
+ "\xB1": 0.549,
1053
+ "\xD7": 0.584,
1054
+ "\xF7": 0.549
1055
+ };
1056
+ var AVG_CHAR_WIDTH_700 = 0.554;
1057
+ var WEIGHT_ADJUSTMENT = {
1058
+ 100: 0.9,
1059
+ 200: 0.92,
1060
+ 300: 0.95,
1061
+ 400: 1,
1062
+ 500: 1.02,
1063
+ 600: 1.05,
1064
+ 700: 1,
1065
+ // uses CHAR_WIDTHS_700 directly
1066
+ 800: 1.1,
1067
+ 900: 1.12
1068
+ };
1069
+ function resolveTable(fontWeight) {
1070
+ if (fontWeight >= 650) {
1071
+ return {
1072
+ table: CHAR_WIDTHS_700,
1073
+ avg: AVG_CHAR_WIDTH_700,
1074
+ scale: WEIGHT_ADJUSTMENT[fontWeight] ?? 1
1075
+ };
1076
+ }
1077
+ return {
1078
+ table: CHAR_WIDTHS_400,
1079
+ avg: AVG_CHAR_WIDTH_400,
1080
+ scale: WEIGHT_ADJUSTMENT[fontWeight] ?? 1
1081
+ };
1082
+ }
1083
+ function charWidthRatio(ch, table, avg) {
1084
+ const direct = table[ch];
1085
+ if (direct !== void 0) return direct;
1086
+ const code = ch.codePointAt(0) ?? 0;
1087
+ if (code >= 768 && code <= 879 || code >= 6832 && code <= 6911) return 0;
1088
+ if (code >= 4352 && code <= 4607 || code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65280 && code <= 65376)
1089
+ return 1;
1090
+ return avg;
1091
+ }
1092
+ function estimateCharWidth(fontSize, fontWeight = 400) {
1093
+ const { avg, scale } = resolveTable(fontWeight);
1094
+ return fontSize * avg * scale;
1095
+ }
1096
+ function estimateTextWidth(text, fontSize, fontWeight = 400) {
1097
+ const { table, avg, scale } = resolveTable(fontWeight);
1098
+ const measureLine = (line) => {
1099
+ let sum = 0;
1100
+ for (const ch of line) {
1101
+ sum += charWidthRatio(ch, table, avg);
1102
+ }
1103
+ return sum * fontSize * scale;
1104
+ };
1105
+ if (text.includes("\n")) {
1106
+ let maxWidth = 0;
1107
+ for (const line of text.split("\n")) {
1108
+ const w = measureLine(line);
1109
+ if (w > maxWidth) maxWidth = w;
1110
+ }
1111
+ return maxWidth;
1112
+ }
1113
+ return measureLine(text);
1114
+ }
1115
+ var BRAND_RESERVE_WIDTH = 130;
1116
+ var BRAND_FONT_SIZE = 12;
1117
+ var BRAND_MIN_WIDTH = 120;
1118
+ var COMPACT_WIDTH = 420;
1119
+ function estimateTextHeight(fontSize, lineCount = 1, lineHeight = 1.3) {
1120
+ return fontSize * lineHeight * lineCount;
1121
+ }
1122
+ function textAscent(fontSize) {
1123
+ return fontSize * 0.8;
1124
+ }
1125
+ var heuristicMeasure = (text, fontSize, fontWeight) => ({
1126
+ width: estimateTextWidth(text, fontSize, fontWeight),
1127
+ height: estimateTextHeight(fontSize)
1128
+ });
1129
+ function resolveMeasurer(measureText) {
1130
+ return measureText ?? heuristicMeasure;
1131
+ }
1132
+
1133
+ // src/responsive/breakpoints.ts
1134
+ var BREAKPOINT_COMPACT_MAX = 400;
1135
+ var BREAKPOINT_MEDIUM_MAX = 700;
1136
+ function getBreakpoint(width) {
1137
+ if (width < BREAKPOINT_COMPACT_MAX) return "compact";
1138
+ if (width <= BREAKPOINT_MEDIUM_MAX) return "medium";
1139
+ return "full";
1140
+ }
1141
+ var HEIGHT_CRAMPED_MAX = 200;
1142
+ var HEIGHT_SHORT_MAX = 350;
1143
+ function getHeightClass(height) {
1144
+ if (height < HEIGHT_CRAMPED_MAX) return "cramped";
1145
+ if (height <= HEIGHT_SHORT_MAX) return "short";
1146
+ return "normal";
1147
+ }
1148
+ function getWidthStrategy(breakpoint) {
1149
+ switch (breakpoint) {
1150
+ case "compact":
1151
+ return {
1152
+ labelMode: "none",
1153
+ legendPosition: "top",
1154
+ annotationPosition: "tooltip-only",
1155
+ axisLabelDensity: "minimal",
1156
+ chromeMode: "full",
1157
+ legendMaxHeight: -1
1158
+ };
1159
+ case "medium":
1160
+ return {
1161
+ labelMode: "important",
1162
+ legendPosition: "top",
1163
+ annotationPosition: "inline",
1164
+ axisLabelDensity: "reduced",
1165
+ chromeMode: "full",
1166
+ legendMaxHeight: -1
1167
+ };
1168
+ case "full":
1169
+ return {
1170
+ labelMode: "all",
1171
+ legendPosition: "right",
1172
+ annotationPosition: "inline",
1173
+ axisLabelDensity: "full",
1174
+ chromeMode: "full",
1175
+ legendMaxHeight: -1
1176
+ };
1177
+ }
1178
+ }
1179
+ function applyHeightConstraints(strategy, heightClass) {
1180
+ if (heightClass === "normal") return strategy;
1181
+ if (heightClass === "cramped") {
1182
+ return {
1183
+ ...strategy,
1184
+ chromeMode: "hidden",
1185
+ legendMaxHeight: 0,
1186
+ labelMode: "none",
1187
+ annotationPosition: "tooltip-only"
1188
+ };
1189
+ }
1190
+ return {
1191
+ ...strategy,
1192
+ chromeMode: "compact",
1193
+ legendMaxHeight: 0.15
1194
+ };
1195
+ }
1196
+ function getLayoutStrategy(breakpoint, heightClass = "normal") {
1197
+ const base = getWidthStrategy(breakpoint);
1198
+ return applyHeightConstraints(base, heightClass);
1199
+ }
1200
+
1201
+ // src/responsive/metrics.ts
1202
+ var AXIS_TITLE_OFFSET_COMPACT = 35;
1203
+ var AXIS_TITLE_OFFSET_DEFAULT = 45;
1204
+ function getAxisTitleOffset(width) {
1205
+ return width < BREAKPOINT_COMPACT_MAX ? AXIS_TITLE_OFFSET_COMPACT : AXIS_TITLE_OFFSET_DEFAULT;
1206
+ }
1207
+ var HPAD_COMPACT_FRACTION = 0.5;
1208
+ var HPAD_COMPACT_MIN = 4;
1209
+ var TICK_LABEL_OFFSET = 6;
1210
+ var AXIS_TITLE_TRAILING_PAD = 4;
1211
+ var AXIS_TITLE_GAP = 7;
1212
+ function axisTitleOffset(maxTickLabelWidth, titleFontSize, width) {
1213
+ const halfTitleGlyph = Math.ceil(titleFontSize / 2);
1214
+ const dynamic = TICK_LABEL_OFFSET + maxTickLabelWidth + AXIS_TITLE_GAP + halfTitleGlyph;
1215
+ return Math.max(dynamic, getAxisTitleOffset(width));
1216
+ }
1217
+ var NARROW_VIEWPORT_MAX = 500;
1218
+ var TOP_PAD_EXTRA_NARROW = 10;
1219
+ var TOP_PAD_NARROW_MAX = NARROW_VIEWPORT_MAX;
1220
+ var LABEL_GAP_COMPACT = 8;
1221
+ var LABEL_GAP_DEFAULT = 12;
1222
+ var LABEL_GAP_NARROW_MAX = NARROW_VIEWPORT_MAX;
1223
+ var MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX = 600;
1224
+ var MAX_LEFT_LABEL_FRACTION_COMPACT = 0.45;
1225
+ var MAX_LEFT_LABEL_FRACTION_MEDIUM = 0.55;
1226
+ var MAX_LEFT_LABEL_FRACTION_DEFAULT = 1;
1227
+ var X_AXIS_BAND_HEIGHT = 26;
1228
+ var X_AXIS_TITLE_BAND = 22;
1229
+ var X_AXIS_TITLE_BAND_ROTATED = 20;
1230
+ function computeXAxisExtentFromLabels(input) {
1231
+ const measure = input.measure ?? estimateTextWidth;
1232
+ const baseHeight = input.xAxisHeight ?? X_AXIS_BAND_HEIGHT;
1233
+ if (input.tickAngle && Math.abs(input.tickAngle) > 10) {
1234
+ const angleRad = Math.abs(input.tickAngle) * (Math.PI / 180);
1235
+ let maxLabelWidth = 40;
1236
+ for (const label of input.labels) {
1237
+ const w = measure(label, input.tickFontSize, input.tickFontWeight);
1238
+ if (w > maxLabelWidth) maxLabelWidth = w;
1239
+ }
1240
+ const lineHeight = input.tickFontSize * 1.2;
1241
+ const AXIS_TO_LABEL_GAP = 3;
1242
+ const rotatedHeight = Math.min(
1243
+ maxLabelWidth * Math.sin(angleRad) + lineHeight * Math.cos(angleRad) + AXIS_TO_LABEL_GAP,
1244
+ 120
1245
+ );
1246
+ return input.hasTitle ? rotatedHeight + X_AXIS_TITLE_BAND_ROTATED : rotatedHeight;
1247
+ }
1248
+ return input.hasTitle ? baseHeight + X_AXIS_TITLE_BAND : baseHeight;
1249
+ }
1250
+
816
1251
  // src/theme/defaults.ts
817
1252
  var DEFAULT_THEME = {
818
1253
  colors: {
@@ -856,10 +1291,12 @@ var DEFAULT_THEME = {
856
1291
  chromeToChart: 8,
857
1292
  chartToFooter: 8,
858
1293
  axisMargin: 6,
859
- xAxisHeight: 26,
1294
+ xAxisHeight: X_AXIS_BAND_HEIGHT,
860
1295
  // Gap (px) between the x-axis line and the TOP of the tick labels. The
861
- // renderer anchors x-tick labels at their top edge (hanging baseline), so
862
- // this is a literal top gap that holds regardless of font size.
1296
+ // renderer treats this as a literal top gap and then shifts the label down
1297
+ // by textAscent() to land it on the alphabetic baseline (rather than
1298
+ // dominant-baseline:hanging, which WebKit positions from different metrics),
1299
+ // so the gap holds regardless of font size.
863
1300
  xAxisLabelPadding: 4
864
1301
  },
865
1302
  borderRadius: 2,
@@ -1164,42 +1601,6 @@ function resolveTheme(userTheme, base = DEFAULT_THEME) {
1164
1601
  };
1165
1602
  }
1166
1603
 
1167
- // src/layout/text-measure.ts
1168
- var AVG_CHAR_WIDTH_RATIO = 0.57;
1169
- var WEIGHT_ADJUSTMENT = {
1170
- 100: 0.9,
1171
- 200: 0.92,
1172
- 300: 0.95,
1173
- 400: 1,
1174
- 500: 1.02,
1175
- 600: 1.05,
1176
- 700: 1.08,
1177
- 800: 1.1,
1178
- 900: 1.12
1179
- };
1180
- function estimateCharWidth(fontSize, fontWeight = 400) {
1181
- const weightFactor = WEIGHT_ADJUSTMENT[fontWeight] ?? 1;
1182
- return fontSize * AVG_CHAR_WIDTH_RATIO * weightFactor;
1183
- }
1184
- function estimateTextWidth(text, fontSize, fontWeight = 400) {
1185
- const charWidth = estimateCharWidth(fontSize, fontWeight);
1186
- if (text.includes("\n")) {
1187
- let maxLen = 0;
1188
- for (const line of text.split("\n")) {
1189
- if (line.length > maxLen) maxLen = line.length;
1190
- }
1191
- return maxLen * charWidth;
1192
- }
1193
- return text.length * charWidth;
1194
- }
1195
- var BRAND_RESERVE_WIDTH = 130;
1196
- var BRAND_FONT_SIZE = 12;
1197
- var BRAND_MIN_WIDTH = 120;
1198
- var COMPACT_WIDTH = 420;
1199
- function estimateTextHeight(fontSize, lineCount = 1, lineHeight = 1.3) {
1200
- return fontSize * lineHeight * lineCount;
1201
- }
1202
-
1203
1604
  // src/layout/chrome.ts
1204
1605
  function normalizeChromeText(value) {
1205
1606
  if (value === void 0) return null;
@@ -1222,8 +1623,7 @@ function buildTextStyle(defaults, fontFamily, textColor, width, overrides) {
1222
1623
  fontWeight: overrides?.fontWeight ?? defaults.fontWeight,
1223
1624
  fill: overrides?.color ?? textColor ?? defaults.color,
1224
1625
  lineHeight: defaults.lineHeight,
1225
- textAnchor: "start",
1226
- dominantBaseline: "hanging"
1626
+ textAnchor: "start"
1227
1627
  };
1228
1628
  }
1229
1629
  function estimateLineCount(text, style, maxWidth, measureText) {
@@ -1505,101 +1905,6 @@ function wrapText(text, fontSize, fontWeight, maxWidth, measureText) {
1505
1905
  return lines;
1506
1906
  }
1507
1907
 
1508
- // src/responsive/breakpoints.ts
1509
- var BREAKPOINT_COMPACT_MAX = 400;
1510
- var BREAKPOINT_MEDIUM_MAX = 700;
1511
- function getBreakpoint(width) {
1512
- if (width < BREAKPOINT_COMPACT_MAX) return "compact";
1513
- if (width <= BREAKPOINT_MEDIUM_MAX) return "medium";
1514
- return "full";
1515
- }
1516
- var HEIGHT_CRAMPED_MAX = 200;
1517
- var HEIGHT_SHORT_MAX = 350;
1518
- function getHeightClass(height) {
1519
- if (height < HEIGHT_CRAMPED_MAX) return "cramped";
1520
- if (height <= HEIGHT_SHORT_MAX) return "short";
1521
- return "normal";
1522
- }
1523
- function getWidthStrategy(breakpoint) {
1524
- switch (breakpoint) {
1525
- case "compact":
1526
- return {
1527
- labelMode: "none",
1528
- legendPosition: "top",
1529
- annotationPosition: "tooltip-only",
1530
- axisLabelDensity: "minimal",
1531
- chromeMode: "full",
1532
- legendMaxHeight: -1
1533
- };
1534
- case "medium":
1535
- return {
1536
- labelMode: "important",
1537
- legendPosition: "top",
1538
- annotationPosition: "inline",
1539
- axisLabelDensity: "reduced",
1540
- chromeMode: "full",
1541
- legendMaxHeight: -1
1542
- };
1543
- case "full":
1544
- return {
1545
- labelMode: "all",
1546
- legendPosition: "right",
1547
- annotationPosition: "inline",
1548
- axisLabelDensity: "full",
1549
- chromeMode: "full",
1550
- legendMaxHeight: -1
1551
- };
1552
- }
1553
- }
1554
- function applyHeightConstraints(strategy, heightClass) {
1555
- if (heightClass === "normal") return strategy;
1556
- if (heightClass === "cramped") {
1557
- return {
1558
- ...strategy,
1559
- chromeMode: "hidden",
1560
- legendMaxHeight: 0,
1561
- labelMode: "none",
1562
- annotationPosition: "tooltip-only"
1563
- };
1564
- }
1565
- return {
1566
- ...strategy,
1567
- chromeMode: "compact",
1568
- legendMaxHeight: 0.15
1569
- };
1570
- }
1571
- function getLayoutStrategy(breakpoint, heightClass = "normal") {
1572
- const base = getWidthStrategy(breakpoint);
1573
- return applyHeightConstraints(base, heightClass);
1574
- }
1575
-
1576
- // src/responsive/metrics.ts
1577
- var AXIS_TITLE_OFFSET_COMPACT = 35;
1578
- var AXIS_TITLE_OFFSET_DEFAULT = 45;
1579
- function getAxisTitleOffset(width) {
1580
- return width < BREAKPOINT_COMPACT_MAX ? AXIS_TITLE_OFFSET_COMPACT : AXIS_TITLE_OFFSET_DEFAULT;
1581
- }
1582
- var HPAD_COMPACT_FRACTION = 0.5;
1583
- var HPAD_COMPACT_MIN = 4;
1584
- var TICK_LABEL_OFFSET = 6;
1585
- var AXIS_TITLE_TRAILING_PAD = 4;
1586
- var AXIS_TITLE_GAP = 7;
1587
- function axisTitleOffset(maxTickLabelWidth, titleFontSize, width) {
1588
- const halfTitleGlyph = Math.ceil(titleFontSize / 2);
1589
- const dynamic = TICK_LABEL_OFFSET + maxTickLabelWidth + AXIS_TITLE_GAP + halfTitleGlyph;
1590
- return Math.max(dynamic, getAxisTitleOffset(width));
1591
- }
1592
- var NARROW_VIEWPORT_MAX = 500;
1593
- var TOP_PAD_EXTRA_NARROW = 10;
1594
- var TOP_PAD_NARROW_MAX = NARROW_VIEWPORT_MAX;
1595
- var LABEL_GAP_COMPACT = 8;
1596
- var LABEL_GAP_DEFAULT = 12;
1597
- var LABEL_GAP_NARROW_MAX = NARROW_VIEWPORT_MAX;
1598
- var MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX = 600;
1599
- var MAX_LEFT_LABEL_FRACTION_COMPACT = 0.45;
1600
- var MAX_LEFT_LABEL_FRACTION_MEDIUM = 0.55;
1601
- var MAX_LEFT_LABEL_FRACTION_DEFAULT = 1;
1602
-
1603
1908
  // src/labels/collision.ts
1604
1909
  var PRIORITY_ORDER = {
1605
1910
  data: 0,
@@ -1609,6 +1914,11 @@ var PRIORITY_ORDER = {
1609
1914
  function detectCollision(a, b) {
1610
1915
  return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y;
1611
1916
  }
1917
+ function overlapArea(a, b) {
1918
+ const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
1919
+ const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);
1920
+ return w > 0 && h > 0 ? w * h : 0;
1921
+ }
1612
1922
  var OFFSET_STRATEGIES = [
1613
1923
  { dx: 0, dy: 0 },
1614
1924
  // original position
@@ -1680,6 +1990,9 @@ function resolveCollisions(labels, strategies = OFFSET_STRATEGIES) {
1680
1990
  y: bestY,
1681
1991
  style: label.style,
1682
1992
  visible: true,
1993
+ // Only carry the index through when the caller set one, so labels that
1994
+ // don't need it keep an unchanged output shape.
1995
+ ...label.index !== void 0 ? { index: label.index } : {},
1683
1996
  connector: needsConnector ? {
1684
1997
  from: { x: bestX, y: bestY },
1685
1998
  to: { x: label.anchorX, y: label.anchorY },
@@ -1693,7 +2006,8 @@ function resolveCollisions(labels, strategies = OFFSET_STRATEGIES) {
1693
2006
  x: label.anchorX,
1694
2007
  y: label.anchorY,
1695
2008
  style: label.style,
1696
- visible: false
2009
+ visible: false,
2010
+ ...label.index !== void 0 ? { index: label.index } : {}
1697
2011
  });
1698
2012
  }
1699
2013
  }
@@ -1710,7 +2024,16 @@ function computeLabelBounds(label) {
1710
2024
  } else if (label.style.textAnchor === "end") {
1711
2025
  x = label.x - width;
1712
2026
  }
1713
- return { x, y: label.y, width, height };
2027
+ const baseline = label.style.dominantBaseline;
2028
+ let y;
2029
+ if (baseline === "central") {
2030
+ y = label.y - height / 2;
2031
+ } else if (baseline === "hanging" || baseline === "text-after-edge") {
2032
+ y = label.y;
2033
+ } else {
2034
+ y = label.y - fontSize * 0.8;
2035
+ }
2036
+ return { x, y, width, height };
1714
2037
  }
1715
2038
 
1716
2039
  // ../../node_modules/.bun/d3-format@3.1.2/node_modules/d3-format/src/formatDecimal.js
@@ -3093,6 +3416,9 @@ export {
3093
3416
  TICK_LABEL_OFFSET,
3094
3417
  TOP_PAD_EXTRA_NARROW,
3095
3418
  TOP_PAD_NARROW_MAX,
3419
+ X_AXIS_BAND_HEIGHT,
3420
+ X_AXIS_TITLE_BAND,
3421
+ X_AXIS_TITLE_BAND_ROTATED,
3096
3422
  abbreviateNumber,
3097
3423
  adaptColorForDarkMode,
3098
3424
  adaptTheme,
@@ -3105,6 +3431,7 @@ export {
3105
3431
  columnChart,
3106
3432
  computeChrome,
3107
3433
  computeLabelBounds,
3434
+ computeXAxisExtentFromLabels,
3108
3435
  contrastRatio,
3109
3436
  dataTable,
3110
3437
  detectCollision,
@@ -3123,6 +3450,7 @@ export {
3123
3450
  getHeightClass,
3124
3451
  getLayoutStrategy,
3125
3452
  getRepresentativeColor,
3453
+ heuristicMeasure,
3126
3454
  inferFieldType,
3127
3455
  isBarListSpec,
3128
3456
  isChartSpec,
@@ -3139,14 +3467,17 @@ export {
3139
3467
  isTileMapSpec,
3140
3468
  lineChart,
3141
3469
  meetsAA,
3470
+ overlapArea,
3142
3471
  pickLabelColor,
3143
3472
  pieChart,
3144
3473
  resolveCollisions,
3145
3474
  resolveMarkDef,
3146
3475
  resolveMarkType,
3476
+ resolveMeasurer,
3147
3477
  resolveTheme,
3148
3478
  scatterChart,
3149
3479
  simulateColorBlindness,
3480
+ textAscent,
3150
3481
  tileMap,
3151
3482
  wrapText
3152
3483
  };