@oliasoft-open-source/charts-library 5.9.0-beta-2 → 5.9.0-beta-4

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.
Files changed (34) hide show
  1. package/dist/assets/{Color-6BZIO3FS-BdmyW5fF.js → Color-6BZIO3FS-DhgzADPX.js} +1 -1
  2. package/dist/assets/{DocsRenderer-LL677BLK-Dmh2MbD-.js → DocsRenderer-LL677BLK-BMxotIzM.js} +4 -4
  3. package/dist/assets/{WithTooltip-65CFNBJE-B1vNinkY.js → WithTooltip-65CFNBJE-DhFWX5Pm.js} +1 -1
  4. package/dist/assets/{bar-chart.stories-Ch0i7N0G.js → bar-chart.stories-BqGiinW-.js} +1 -1
  5. package/dist/assets/{chunk-YKABRMAI-nN4IfCIz.js → chunk-YKABRMAI-Cp0muVto.js} +1 -1
  6. package/dist/assets/{components-s5a1V3CD.js → components-B21IBaJ_.js} +4 -4
  7. package/dist/assets/formatter-EIJCOSYU-BrsmllP3.js +1 -0
  8. package/dist/assets/{get-draggableData-mCkevOrC.js → get-draggableData-CXsYyfeD.js} +1 -1
  9. package/dist/assets/{iframe-B7buKd6W.js → iframe-HxMpIt73.js} +3 -3
  10. package/dist/assets/{legend-Cem_14IV.js → legend-Fq0KKkQ6.js} +1 -1
  11. package/dist/assets/legend-context-DbDqj5Ij.js +10 -0
  12. package/dist/assets/{line-chart-BDauhYk8.js → line-chart-BAzukCgc.js} +1 -1
  13. package/dist/assets/{line-chart.stories-T04m8x5t.js → line-chart.stories-Bbt0hbp6.js} +1 -1
  14. package/dist/assets/{line-chart.test-case.stories-BcA_QlE1.js → line-chart.test-case.stories-DYHAsm6c.js} +174 -126
  15. package/dist/assets/{pie-chart.stories-D2G6WnUw.js → pie-chart.stories-DWFlVV67.js} +1 -1
  16. package/dist/assets/{react-18-DQODWq5X.js → react-18-iJHr1sKj.js} +1 -1
  17. package/dist/assets/{react-CwUuogrH.js → react-BJbrdSXX.js} +1 -1
  18. package/dist/assets/{react-dom-O1LTTn3X.js → react-dom-DS1BD2oL.js} +1 -1
  19. package/dist/assets/{scatter-chart.stories-DJ3Z3dsB.js → scatter-chart.stories-D0zx9mjm.js} +1 -1
  20. package/dist/assets/{syntaxhighlighter-ED5Y7EFY-DYFKPQ-I.js → syntaxhighlighter-ED5Y7EFY-CM81Ddf3.js} +1 -1
  21. package/dist/assets/{theming-DpsViFyv.js → theming-DBjDcFWf.js} +1 -1
  22. package/dist/common/helpers/callout-helpers/callout-helpers.d.ts +1 -0
  23. package/dist/iframe.html +1 -1
  24. package/dist/index.js +638 -434
  25. package/dist/index.json +1 -1
  26. package/dist/line-chart/plugins/callout-plugin/helpers.d.ts +11 -0
  27. package/dist/project.json +1 -1
  28. package/dist/src/components/common/helpers/callout-helpers/callout-helpers.d.ts +1 -0
  29. package/dist/src/components/line-chart/plugins/callout-plugin/helpers.d.ts +11 -0
  30. package/package.json +1 -1
  31. package/dist/assets/formatter-EIJCOSYU-Bo0DMj3p.js +0 -1
  32. package/dist/assets/legend-context-D1oEf-Th.js +0 -9
  33. package/dist/common/helpers/callout-helpers/callout-overlap-helpers.d.ts +0 -9
  34. package/dist/src/components/common/helpers/callout-helpers/callout-overlap-helpers.d.ts +0 -9
package/dist/index.js CHANGED
@@ -802,7 +802,7 @@ function _factorize(value) {
802
802
  function isNonPrimitive(n) {
803
803
  return typeof n === "symbol" || typeof n === "object" && n !== null && !(Symbol.toPrimitive in n || "toString" in n || "valueOf" in n);
804
804
  }
805
- function isNumber$2(n) {
805
+ function isNumber$1(n) {
806
806
  return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n);
807
807
  }
808
808
  function almostWhole(x, epsilon) {
@@ -4352,7 +4352,7 @@ var LineController = class extends DatasetController {
4352
4352
  const iAxis = iScale.axis;
4353
4353
  const vAxis = vScale.axis;
4354
4354
  const { spanGaps, segment } = this.options;
4355
- const maxGapLength = isNumber$2(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4355
+ const maxGapLength = isNumber$1(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4356
4356
  const directUpdate = this.chart._animationsDisabled || reset || mode === "none";
4357
4357
  const end = start + count;
4358
4358
  const pointsCount = points.length;
@@ -4475,7 +4475,7 @@ var ScatterController = class extends DatasetController {
4475
4475
  const iAxis = iScale.axis;
4476
4476
  const vAxis = vScale.axis;
4477
4477
  const { spanGaps, segment } = this.options;
4478
- const maxGapLength = isNumber$2(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4478
+ const maxGapLength = isNumber$1(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4479
4479
  const directUpdate = this.chart._animationsDisabled || reset || mode === "none";
4480
4480
  let prevParsed = start > 0 && this.getParsed(start - 1);
4481
4481
  for (let i = start; i < start + count; ++i) {
@@ -5281,7 +5281,7 @@ var Element = class {
5281
5281
  };
5282
5282
  }
5283
5283
  hasValue() {
5284
- return isNumber$2(this.x) && isNumber$2(this.y);
5284
+ return isNumber$1(this.x) && isNumber$1(this.y);
5285
5285
  }
5286
5286
  getProps(props, final) {
5287
5287
  const anims = this.$animations;
@@ -11390,7 +11390,7 @@ function parse(scale, input) {
11390
11390
  if (typeof parser === "function") value = parser(value);
11391
11391
  if (!isNumberFinite(value)) value = typeof parser === "string" ? adapter.parse(value, parser) : adapter.parse(value);
11392
11392
  if (value === null) return null;
11393
- if (round) value = round === "week" && (isNumber$2(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round);
11393
+ if (round) value = round === "week" && (isNumber$1(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round);
11394
11394
  return +value;
11395
11395
  }
11396
11396
  function determineUnitForAutoTicks(minUnit, min, max, capacity) {
@@ -11585,7 +11585,7 @@ var TimeScale = class extends Scale {
11585
11585
  const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
11586
11586
  const stepSize = valueOrDefault(options.ticks.stepSize, 1);
11587
11587
  const weekday = minor === "week" ? timeOpts.isoWeekday : false;
11588
- const hasWeekday = isNumber$2(weekday) || weekday === true;
11588
+ const hasWeekday = isNumber$1(weekday) || weekday === true;
11589
11589
  const ticks = {};
11590
11590
  let first = min;
11591
11591
  let time, count;
@@ -13597,7 +13597,7 @@ function isZoomingOrPanning(chart) {
13597
13597
  const state = getState(chart);
13598
13598
  return state.panning || state.dragging;
13599
13599
  }
13600
- var clamp$2 = (x, from, to) => Math.min(to, Math.max(from, x));
13600
+ var clamp$3 = (x, from, to) => Math.min(to, Math.max(from, x));
13601
13601
  function removeHandler(chart, type) {
13602
13602
  const { handlers } = getState(chart);
13603
13603
  const handler = handlers[type];
@@ -13683,8 +13683,8 @@ function applyAspectRatio({ begin, end }, aspectRatio) {
13683
13683
  end.y = begin.y + height;
13684
13684
  }
13685
13685
  function applyMinMaxProps(rect, chartArea, points, { min, max, prop }) {
13686
- rect[min] = clamp$2(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
13687
- rect[max] = clamp$2(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
13686
+ rect[min] = clamp$3(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
13687
+ rect[max] = clamp$3(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
13688
13688
  }
13689
13689
  function getRelativePoints(chart, pointEvents, maintainAspectRatio) {
13690
13690
  const points = {
@@ -15073,10 +15073,10 @@ function rotated(point, center, angle) {
15073
15073
  }
15074
15074
  var isOlderPart = (act, req) => req > act || act.length > req.length && act.slice(0, req.length) === req;
15075
15075
  var EPSILON = .001;
15076
- var clamp$1 = (x, from, to) => Math.min(to, Math.max(from, x));
15076
+ var clamp$2 = (x, from, to) => Math.min(to, Math.max(from, x));
15077
15077
  var inLimit = (limit, hitSize) => limit.value >= limit.start - hitSize && limit.value <= limit.end + hitSize;
15078
15078
  function clampAll(obj, from, to) {
15079
- for (const key of Object.keys(obj)) obj[key] = clamp$1(obj[key], from, to);
15079
+ for (const key of Object.keys(obj)) obj[key] = clamp$2(obj[key], from, to);
15080
15080
  return obj;
15081
15081
  }
15082
15082
  function inPointRange(point, center, radius, hitSize) {
@@ -15117,7 +15117,7 @@ function requireVersion(pkg, min, ver, strict = true) {
15117
15117
  }
15118
15118
  var isPercentString = (s) => typeof s === "string" && s.endsWith("%");
15119
15119
  var toPercent = (s) => parseFloat(s) / 100;
15120
- var toPositivePercent = (s) => clamp$1(toPercent(s), 0, 1);
15120
+ var toPositivePercent = (s) => clamp$2(toPercent(s), 0, 1);
15121
15121
  var boxAppering = (x, y) => ({
15122
15122
  x,
15123
15123
  y,
@@ -15499,8 +15499,8 @@ function applyLabelContent(ctx, { x, y }, labels, { fonts, colors }) {
15499
15499
  });
15500
15500
  }
15501
15501
  function getOpacity(value, elementValue) {
15502
- const opacity = isNumber$2(value) ? value : elementValue;
15503
- return isNumber$2(opacity) ? clamp$1(opacity, 0, 1) : 1;
15502
+ const opacity = isNumber$1(value) ? value : elementValue;
15503
+ return isNumber$1(opacity) ? clamp$2(opacity, 0, 1) : 1;
15504
15504
  }
15505
15505
  var positions = [
15506
15506
  "left",
@@ -16834,7 +16834,7 @@ function calculateTAdjust(lineSize, sizes, label, space) {
16834
16834
  const lineH = lineSize.h * space.dy;
16835
16835
  const x = lineW > 0 && (labelSize.w / 2 + padding.left - space.x) / lineW;
16836
16836
  const y = lineH > 0 && (labelSize.h / 2 + padding.top - space.y) / lineH;
16837
- return clamp$1(Math.max(x, y), 0, .25);
16837
+ return clamp$2(Math.max(x, y), 0, .25);
16838
16838
  }
16839
16839
  function spaceAround(properties, chartArea) {
16840
16840
  const { x, x2, y, y2 } = properties;
@@ -17595,7 +17595,6 @@ const BORDER_COLOR = "rgba(0,0,0,0.1)";
17595
17595
  const ANNOTATION_DASH = [10, 2];
17596
17596
  const DEFAULT_FONT_FAMILY = "\"Roobert\", \"Noto Sans\", sans-serif";
17597
17597
  const DEFAULT_COLOR = "hsl(60, 10.34482759%, 12.5%)";
17598
- const TRANSPARENT = "transparent";
17599
17598
  const LOGARITHMIC_STEPS = [
17600
17599
  1,
17601
17600
  10,
@@ -19393,329 +19392,505 @@ function addTransparency(input, alpha, out = "rgb") {
19393
19392
  const c = color.alpha(a);
19394
19393
  return out === "hex" ? c.toHex() : c.toRgbString();
19395
19394
  }
19395
+ var DEFAULT_FONT_SIZE = 12;
19396
+ var DEFAULT_PADDING = 5;
19397
+ var DEFAULT_MARGIN = 8;
19398
+ var DEFAULT_LABEL_GAP = 8;
19399
+ var DEFAULT_MIN_CONNECTOR_LENGTH = 14;
19400
+ var DEFAULT_HORIZONTAL_OFFSET = 18;
19401
+ var DEFAULT_VERTICAL_OFFSET = -18;
19402
+ var DEFAULT_ROW_GAP = 22;
19403
+ var DEFAULT_ITEM_GAP = 8;
19404
+ var DEFAULT_OVERLAP_ONLY_ITEM_GAP = 14;
19405
+ var DEFAULT_SIDE_SPREAD_STEP = 12;
19406
+ var DEFAULT_SIDE_SPREAD_MAX = 48;
19407
+ var clamp$1 = (value, min, max) => {
19408
+ if (Number.isNaN(value)) return min;
19409
+ if (min > max) return min;
19410
+ return Math.min(Math.max(value, min), max);
19411
+ };
19412
+ var getChartFromCtx$1 = (ctx) => {
19413
+ return ctx?.chart?.chart ?? ctx?.chart;
19414
+ };
19396
19415
  var getXScale = (chart, axisId) => {
19397
19416
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
19398
19417
  if (chart?.scales?.x) return chart.scales.x;
19399
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("x"));
19418
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
19419
+ return item?.toLowerCase()?.includes("x");
19420
+ });
19400
19421
  return key ? chart?.scales?.[key] : void 0;
19401
19422
  };
19402
19423
  var getYScale = (chart, axisId) => {
19403
19424
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
19404
19425
  if (chart?.scales?.y) return chart.scales.y;
19405
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
19426
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
19427
+ return item?.toLowerCase()?.includes("y");
19428
+ });
19406
19429
  return key ? chart?.scales?.[key] : void 0;
19407
19430
  };
19408
- var isNumber$1 = (value) => {
19409
- return typeof value === "number" && Number.isFinite(value);
19431
+ var resolveFontSize = (font) => {
19432
+ if (typeof font === "string") {
19433
+ const match = font.match(/(\d+(?:\.\d+)?)px/);
19434
+ return match ? Number(match[1]) : DEFAULT_FONT_SIZE;
19435
+ }
19436
+ if (font && typeof font === "object" && "size" in font) {
19437
+ const size = Number(font.size);
19438
+ return Number.isFinite(size) ? size : DEFAULT_FONT_SIZE;
19439
+ }
19440
+ return DEFAULT_FONT_SIZE;
19410
19441
  };
19411
- var looksLikeXAxis = (axisId) => {
19412
- return !!axisId && axisId.toLowerCase().includes("x");
19442
+ var resolvePadding = (padding) => {
19443
+ if (typeof padding === "number") return {
19444
+ top: padding,
19445
+ right: padding,
19446
+ bottom: padding,
19447
+ left: padding
19448
+ };
19449
+ if (padding && typeof padding === "object") {
19450
+ const typedPadding = padding;
19451
+ return {
19452
+ top: Number(typedPadding.top ?? DEFAULT_PADDING),
19453
+ right: Number(typedPadding.right ?? DEFAULT_PADDING),
19454
+ bottom: Number(typedPadding.bottom ?? DEFAULT_PADDING),
19455
+ left: Number(typedPadding.left ?? DEFAULT_PADDING)
19456
+ };
19457
+ }
19458
+ return {
19459
+ top: DEFAULT_PADDING,
19460
+ right: DEFAULT_PADDING,
19461
+ bottom: DEFAULT_PADDING,
19462
+ left: DEFAULT_PADDING
19463
+ };
19464
+ };
19465
+ var estimateLabelSize = (chart, annotation, calloutCfg) => {
19466
+ const content = `${annotation?.label ?? ""}`;
19467
+ const fontSize = resolveFontSize(calloutCfg?.font);
19468
+ const padding = resolvePadding(calloutCfg?.padding);
19469
+ const lines = content.split("\n");
19470
+ const fallbackWidth = Math.max(...lines.map((line) => line.length), 0) * fontSize * .62;
19471
+ let measuredWidth = fallbackWidth;
19472
+ if (chart?.ctx) {
19473
+ chart.ctx.save();
19474
+ chart.ctx.font = typeof calloutCfg?.font === "string" ? calloutCfg.font : `${fontSize}px sans-serif`;
19475
+ measuredWidth = Math.max(...lines.map((line) => chart.ctx.measureText(line).width), fallbackWidth);
19476
+ chart.ctx.restore();
19477
+ }
19478
+ return {
19479
+ width: measuredWidth + padding.left + padding.right,
19480
+ height: lines.length * fontSize + padding.top + padding.bottom
19481
+ };
19482
+ };
19483
+ var getAxisId = (annotation, axis) => {
19484
+ const axisScaleId = axis === "x" ? annotation?.xScaleID : annotation?.yScaleID;
19485
+ if (axisScaleId) return axisScaleId;
19486
+ if (annotation?.annotationAxis?.startsWith(axis)) return annotation.annotationAxis;
19487
+ };
19488
+ var getAnchorPixel = (chart, annotation, axis, area) => {
19489
+ const scale = axis === "x" ? getXScale(chart, getAxisId(annotation, "x")) : getYScale(chart, getAxisId(annotation, "y"));
19490
+ const directValue = axis === "x" ? annotation?.xValue : annotation?.yValue;
19491
+ if (typeof directValue === "number" && scale) return scale.getPixelForValue(directValue);
19492
+ const minKey = axis === "x" ? "xMin" : "yMin";
19493
+ const maxKey = axis === "x" ? "xMax" : "yMax";
19494
+ const minValue = annotation?.[minKey];
19495
+ const maxValue = annotation?.[maxKey];
19496
+ if (typeof minValue === "number" && typeof maxValue === "number" && scale) return (scale.getPixelForValue(minValue) + scale.getPixelForValue(maxValue)) / 2;
19497
+ if (typeof minValue === "number" && scale) return scale.getPixelForValue(minValue);
19498
+ if (typeof maxValue === "number" && scale) return scale.getPixelForValue(maxValue);
19499
+ return axis === "x" ? (area.left + area.right) / 2 : (area.top + area.bottom) / 2;
19500
+ };
19501
+ var hasAxisAnchor = (annotation, axis) => {
19502
+ if (axis === "x") return typeof annotation?.xValue === "number" || typeof annotation?.xMin === "number" || typeof annotation?.xMax === "number" || annotation?.type === "box" || annotation?.type === "label" || annotation?.type === "ellipse";
19503
+ return typeof annotation?.yValue === "number" || typeof annotation?.yMin === "number" || typeof annotation?.yMax === "number" || annotation?.type === "box" || annotation?.type === "label" || annotation?.type === "ellipse";
19504
+ };
19505
+ var getBoxHalfWidth = (chart, area, annotation) => {
19506
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
19507
+ if (xScale && typeof annotation?.xMin === "number" && typeof annotation?.xMax === "number") return Math.abs(xScale.getPixelForValue(annotation.xMax) - xScale.getPixelForValue(annotation.xMin)) / 2;
19508
+ if (annotation?.type === "box") return (area.right - area.left) / 2;
19509
+ return annotation?.radius ?? 0;
19510
+ };
19511
+ var getBoxHalfHeight = (chart, annotation) => {
19512
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
19513
+ if (yScale && typeof annotation?.yMin === "number" && typeof annotation?.yMax === "number") return Math.abs(yScale.getPixelForValue(annotation.yMax) - yScale.getPixelForValue(annotation.yMin)) / 2;
19514
+ return annotation?.radius ?? 0;
19515
+ };
19516
+ var getReferencePoint$1 = (chart, area, annotation) => {
19517
+ const anchorX = getAnchorPixel(chart, annotation, "x", area);
19518
+ const anchorY = getAnchorPixel(chart, annotation, "y", area);
19519
+ const halfWidth = getBoxHalfWidth(chart, area, annotation);
19520
+ const halfHeight = getBoxHalfHeight(chart, annotation);
19521
+ const labelOffsetPx = annotation?.labelOffsetPx ?? 14;
19522
+ const xAdjust = annotation?.labelConfig?.xAdjust ?? 0;
19523
+ const yAdjust = annotation?.labelConfig?.yAdjust ?? 0;
19524
+ const position = annotation?.labelConfig?.position;
19525
+ let x = anchorX;
19526
+ let y = anchorY;
19527
+ let side = anchorX >= (area.left + area.right) / 2 ? "right" : "left";
19528
+ switch (position) {
19529
+ case Position.Left:
19530
+ x -= Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
19531
+ side = "left";
19532
+ break;
19533
+ case Position.Right:
19534
+ x += Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
19535
+ side = "right";
19536
+ break;
19537
+ case Position.TopLeft:
19538
+ x -= halfWidth;
19539
+ y -= halfHeight;
19540
+ side = "left";
19541
+ break;
19542
+ case Position.TopRight:
19543
+ x += halfWidth;
19544
+ y -= halfHeight;
19545
+ side = "right";
19546
+ break;
19547
+ case Position.BottomLeft:
19548
+ x -= halfWidth;
19549
+ y += halfHeight;
19550
+ side = "left";
19551
+ break;
19552
+ case Position.BottomRight:
19553
+ x += halfWidth;
19554
+ y += halfHeight;
19555
+ side = "right";
19556
+ break;
19557
+ case Position.Bottom:
19558
+ y += Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
19559
+ break;
19560
+ case Position.Top:
19561
+ default:
19562
+ y -= Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
19563
+ break;
19564
+ }
19565
+ return {
19566
+ x: x + xAdjust,
19567
+ y: y + yAdjust,
19568
+ side
19569
+ };
19413
19570
  };
19414
- var looksLikeYAxis = (axisId) => {
19415
- return !!axisId && axisId.toLowerCase().includes("y");
19571
+ var getOriginalLabelRect = (chart, area, annotation, index) => {
19572
+ const size = estimateLabelSize(chart, annotation, annotation?.labelConfig?.callout ?? {});
19573
+ const reference = getReferencePoint$1(chart, area, annotation);
19574
+ const direction = reference.side === "right" ? 1 : -1;
19575
+ const centerX = reference.x + direction * (size.width / 2);
19576
+ const centerY = reference.y;
19577
+ return {
19578
+ id: `callout-annotation-${index}`,
19579
+ side: reference.side,
19580
+ left: centerX - size.width / 2,
19581
+ right: centerX + size.width / 2,
19582
+ top: centerY - size.height / 2,
19583
+ bottom: centerY + size.height / 2
19584
+ };
19416
19585
  };
19417
- var resolveXScale = (chart, refAnnotation) => {
19418
- return getXScale(chart, refAnnotation?.xScaleID) ?? getXScale(chart, looksLikeXAxis(refAnnotation?.annotationAxis) ? refAnnotation?.annotationAxis : void 0) ?? getXScale(chart, refAnnotation?.annotationAxis);
19586
+ var isRectOverlapping = (left, right) => {
19587
+ return !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
19419
19588
  };
19420
- var resolveYScale = (chart, refAnnotation) => {
19421
- return getYScale(chart, refAnnotation?.yScaleID) ?? getYScale(chart, looksLikeYAxis(refAnnotation?.annotationAxis) ? refAnnotation?.annotationAxis : void 0) ?? getYScale(chart, refAnnotation?.annotationAxis);
19589
+ var getOverlappingCalloutIds = (chart) => {
19590
+ const area = chart?.chartArea;
19591
+ const annotationsData = (chart?.options)?.annotations?.annotationsData ?? [];
19592
+ if (!chart || !area) return /* @__PURE__ */ new Set();
19593
+ const rects = annotationsData.flatMap((annotation, index) => {
19594
+ const calloutCfg = annotation?.labelConfig?.callout ?? {};
19595
+ if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
19596
+ return [getOriginalLabelRect(chart, area, annotation, index)];
19597
+ });
19598
+ const overlappingIds = /* @__PURE__ */ new Set();
19599
+ rects.forEach((rect, index) => {
19600
+ rects.slice(index + 1).forEach((otherRect) => {
19601
+ if (rect.side !== otherRect.side) return;
19602
+ if (!isRectOverlapping(rect, otherRect)) return;
19603
+ overlappingIds.add(rect.id);
19604
+ overlappingIds.add(otherRect.id);
19605
+ });
19606
+ });
19607
+ return overlappingIds;
19608
+ };
19609
+ var getOccupiedOriginalLabelRects = (chart, area, overlappingIds) => {
19610
+ return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
19611
+ const calloutCfg = annotation?.labelConfig?.callout ?? {};
19612
+ const id = `callout-annotation-${index}`;
19613
+ if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
19614
+ if (overlappingIds.has(id)) return [];
19615
+ return [getOriginalLabelRect(chart, area, annotation, index)];
19616
+ });
19422
19617
  };
19423
- var getAxisAlignedValue = (refAnnotation, axis) => {
19424
- if (!isNumber$1(refAnnotation?.value)) return;
19425
- const annAxis = refAnnotation?.annotationAxis;
19426
- if (axis === "x" && looksLikeXAxis(annAxis)) return refAnnotation.value;
19427
- if (axis === "y" && looksLikeYAxis(annAxis)) return refAnnotation.value;
19618
+ const isCalloutOverlapping = (ctx, refAnnotation, index) => {
19619
+ const chart = getChartFromCtx$1(ctx);
19620
+ if (!(refAnnotation?.labelConfig?.callout ?? {})?.onlyWhenOverlapping) return true;
19621
+ if (!chart?.chartArea) return false;
19622
+ return getOverlappingCalloutIds(chart).has(`callout-annotation-${index}`);
19623
+ };
19624
+ var getPreferredX = (chart, area, annotation, calloutCfg, width) => {
19625
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
19626
+ const explicitXValue = typeof calloutCfg?.xValue === "number" ? calloutCfg.xValue : void 0;
19627
+ if (typeof explicitXValue === "number" && xScale) return xScale.getPixelForValue(explicitXValue);
19628
+ const margin = calloutCfg?.margin ?? DEFAULT_MARGIN;
19629
+ const halfWidth = width / 2;
19630
+ const reference = getReferencePoint$1(chart, area, annotation);
19631
+ const direction = reference.side === "right" ? 1 : -1;
19632
+ const gap = calloutCfg?.gapPx ?? DEFAULT_LABEL_GAP;
19633
+ const minConnectorLength = calloutCfg?.minConnectorLengthPx ?? DEFAULT_MIN_CONNECTOR_LENGTH;
19634
+ return clamp$1(reference.x + direction * (halfWidth + gap + minConnectorLength), area.left + margin + halfWidth, area.right - margin - halfWidth);
19635
+ };
19636
+ var getPreferredY = (chart, area, annotation, calloutCfg, index) => {
19637
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
19638
+ const explicitYValue = typeof calloutCfg?.yValue === "number" ? calloutCfg.yValue : void 0;
19639
+ if (typeof explicitYValue === "number" && yScale) return yScale.getPixelForValue(explicitYValue);
19640
+ const reference = getReferencePoint$1(chart, area, annotation);
19641
+ const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
19642
+ const row = Math.floor(index / 3);
19643
+ const dy = hasCustomOffset ? 0 : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
19644
+ return reference.y + dy;
19645
+ };
19646
+ var getCalloutItems = (chart, area) => {
19647
+ const overlappingIds = getOverlappingCalloutIds(chart);
19648
+ return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
19649
+ const calloutCfg = annotation?.labelConfig?.callout ?? {};
19650
+ const id = `callout-annotation-${index}`;
19651
+ if (!calloutCfg?.enabled) return [];
19652
+ if (calloutCfg?.onlyWhenOverlapping && !overlappingIds.has(id)) return [];
19653
+ const size = estimateLabelSize(chart, annotation, calloutCfg);
19654
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
19655
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
19656
+ const reference = getReferencePoint$1(chart, area, annotation);
19657
+ const preferredX = getPreferredX(chart, area, annotation, calloutCfg, size.width);
19658
+ const preferredY = getPreferredY(chart, area, annotation, calloutCfg, index);
19659
+ const hasStoredX = typeof calloutCfg?.xValue === "number" || !xScale;
19660
+ const hasStoredY = typeof calloutCfg?.yValue === "number" || !yScale;
19661
+ const side = reference.side;
19662
+ const margin = calloutCfg?.margin ?? DEFAULT_MARGIN;
19663
+ return [{
19664
+ annotation,
19665
+ calloutCfg,
19666
+ id,
19667
+ index,
19668
+ preferredX,
19669
+ preferredY,
19670
+ width: size.width,
19671
+ height: size.height,
19672
+ isFixed: hasStoredX && hasStoredY,
19673
+ overlapOnly: Boolean(calloutCfg?.onlyWhenOverlapping),
19674
+ minX: area.left + margin + size.width / 2,
19675
+ maxX: area.right - margin - size.width / 2,
19676
+ minY: area.top + margin + size.height / 2,
19677
+ maxY: area.bottom - margin - size.height / 2,
19678
+ side
19679
+ }];
19680
+ });
19428
19681
  };
19429
- var resolveAnchorX = (chart, refAnnotation) => {
19430
- if (isNumber$1(refAnnotation?.xValue)) return refAnnotation.xValue;
19431
- if (isNumber$1(refAnnotation?.xMin) && isNumber$1(refAnnotation?.xMax)) return (refAnnotation.xMin + refAnnotation.xMax) / 2;
19432
- const axisValue = getAxisAlignedValue(refAnnotation, "x");
19433
- if (isNumber$1(axisValue)) return axisValue;
19434
- if (!chart) return 0;
19435
- const xScale = resolveXScale(chart, refAnnotation);
19436
- const area = chart.chartArea;
19437
- if (!xScale || !area) return 0;
19438
- return xScale.getValueForPixel((area.left + area.right) / 2);
19439
- };
19440
- var resolveAnchorY = (chart, refAnnotation) => {
19441
- if (isNumber$1(refAnnotation?.yValue)) return refAnnotation.yValue;
19442
- if (isNumber$1(refAnnotation?.yMin) && isNumber$1(refAnnotation?.yMax)) return (refAnnotation.yMin + refAnnotation.yMax) / 2;
19443
- const axisValue = getAxisAlignedValue(refAnnotation, "y");
19444
- if (isNumber$1(axisValue)) return axisValue;
19445
- if (!chart) return 0;
19446
- const yScale = resolveYScale(chart, refAnnotation);
19447
- const area = chart.chartArea;
19448
- if (!yScale || !area) return 0;
19449
- return yScale.getValueForPixel((area.top + area.bottom) / 2);
19682
+ var hasVerticalOverlap = (items) => {
19683
+ const sorted = [...items].sort((left, right) => left.preferredY - right.preferredY);
19684
+ for (let index = 1; index < sorted.length; index += 1) {
19685
+ const previous = sorted[index - 1];
19686
+ const current = sorted[index];
19687
+ const gap = previous.height / 2 + current.height / 2 + DEFAULT_ITEM_GAP;
19688
+ if (current.preferredY - previous.preferredY < gap) return true;
19689
+ }
19690
+ return false;
19450
19691
  };
19451
- var getDefaultXDirection = (refAnnotation, index) => {
19452
- switch (refAnnotation?.labelConfig?.position) {
19453
- case "left":
19454
- case "top-left":
19455
- case "bottom-left": return -1;
19456
- case "right":
19457
- case "top-right":
19458
- case "bottom-right": return 1;
19459
- default: return index % 2 === 0 ? 1 : -1;
19692
+ var overlapsOccupiedRect = (centerY, height, occupiedRects) => {
19693
+ const top = centerY - height / 2;
19694
+ const bottom = centerY + height / 2;
19695
+ return occupiedRects.find((rect) => !(bottom <= rect.top || top >= rect.bottom));
19696
+ };
19697
+ var getLayoutGap = (item) => {
19698
+ return item.overlapOnly ? DEFAULT_OVERLAP_ONLY_ITEM_GAP : DEFAULT_ITEM_GAP;
19699
+ };
19700
+ var layoutSide = (items, occupiedRects = []) => {
19701
+ const sorted = [...items].sort((left, right) => left.preferredY - right.preferredY);
19702
+ const result = /* @__PURE__ */ new Map();
19703
+ if (!sorted.filter((item) => !item.isFixed).length) {
19704
+ sorted.forEach((item) => {
19705
+ result.set(item.id, {
19706
+ xValue: item.preferredX,
19707
+ yValue: item.preferredY
19708
+ });
19709
+ });
19710
+ return result;
19460
19711
  }
19712
+ const shouldReflow = sorted.some((item) => !item.overlapOnly) || hasVerticalOverlap(sorted);
19713
+ sorted.forEach((item) => {
19714
+ result.set(item.id, {
19715
+ xValue: item.preferredX,
19716
+ yValue: clamp$1(item.preferredY, item.minY, item.maxY)
19717
+ });
19718
+ });
19719
+ if (!shouldReflow) return result;
19720
+ let previousBottom = Number.NEGATIVE_INFINITY;
19721
+ sorted.forEach((item) => {
19722
+ if (item.isFixed) {
19723
+ const yValue = clamp$1(item.preferredY, item.minY, item.maxY);
19724
+ result.set(item.id, {
19725
+ xValue: item.preferredX,
19726
+ yValue
19727
+ });
19728
+ previousBottom = yValue + item.height / 2;
19729
+ return;
19730
+ }
19731
+ const minCenter = Math.max(item.minY, previousBottom + item.height / 2 + getLayoutGap(item));
19732
+ let yValue = clamp$1(item.preferredY, minCenter, item.maxY);
19733
+ let overlappingRect = overlapsOccupiedRect(yValue, item.height, occupiedRects);
19734
+ while (overlappingRect) {
19735
+ yValue = clamp$1(overlappingRect.bottom + getLayoutGap(item) + item.height / 2, item.minY, item.maxY);
19736
+ overlappingRect = overlapsOccupiedRect(yValue, item.height, occupiedRects);
19737
+ }
19738
+ result.set(item.id, {
19739
+ xValue: item.preferredX,
19740
+ yValue
19741
+ });
19742
+ previousBottom = yValue + item.height / 2;
19743
+ });
19744
+ let nextTop = Number.POSITIVE_INFINITY;
19745
+ [...sorted].reverse().forEach((item) => {
19746
+ const current = result.get(item.id);
19747
+ if (!current) return;
19748
+ if (item.isFixed) {
19749
+ nextTop = current.yValue - item.height / 2;
19750
+ return;
19751
+ }
19752
+ const maxCenter = Math.min(item.maxY, nextTop - item.height / 2 - getLayoutGap(item));
19753
+ const yValue = clamp$1(current.yValue, item.minY, maxCenter);
19754
+ result.set(item.id, {
19755
+ ...current,
19756
+ yValue
19757
+ });
19758
+ nextTop = yValue - item.height / 2;
19759
+ });
19760
+ const overlapOnlyMovable = sorted.filter((item) => {
19761
+ return item.overlapOnly && !item.isFixed;
19762
+ });
19763
+ if (overlapOnlyMovable.length > 1) {
19764
+ const sortedOverlapOnlyMovable = [...overlapOnlyMovable].sort((left, right) => left.preferredY - right.preferredY);
19765
+ const sharedOuterEdge = sortedOverlapOnlyMovable[0]?.side === "right" ? Math.max(...sortedOverlapOnlyMovable.map((item) => {
19766
+ return (result.get(item.id)?.xValue ?? item.preferredX) + item.width / 2;
19767
+ })) : Math.min(...sortedOverlapOnlyMovable.map((item) => {
19768
+ return (result.get(item.id)?.xValue ?? item.preferredX) - item.width / 2;
19769
+ }));
19770
+ sortedOverlapOnlyMovable.forEach((item, index) => {
19771
+ const current = result.get(item.id);
19772
+ if (!current) return;
19773
+ const spreadOffset = Math.min(index * DEFAULT_SIDE_SPREAD_STEP, DEFAULT_SIDE_SPREAD_MAX);
19774
+ const direction = item.side === "right" ? -1 : 1;
19775
+ const alignedXValue = item.side === "right" ? sharedOuterEdge - item.width / 2 : sharedOuterEdge + item.width / 2;
19776
+ result.set(item.id, {
19777
+ ...current,
19778
+ xValue: clamp$1(alignedXValue + direction * spreadOffset, item.minX, item.maxX)
19779
+ });
19780
+ });
19781
+ return result;
19782
+ }
19783
+ sorted.forEach((item, index) => {
19784
+ const current = result.get(item.id);
19785
+ if (!current || item.isFixed) return;
19786
+ const spreadOffset = Math.min(index * DEFAULT_SIDE_SPREAD_STEP, DEFAULT_SIDE_SPREAD_MAX);
19787
+ const direction = item.side === "right" ? -1 : 1;
19788
+ const xValue = clamp$1(current.xValue + direction * spreadOffset, item.minX, item.maxX);
19789
+ result.set(item.id, {
19790
+ ...current,
19791
+ xValue
19792
+ });
19793
+ });
19794
+ return result;
19461
19795
  };
19462
- var getCalloutLayoutState = (chart, index) => {
19463
- const c = chart;
19464
- if (!c.__calloutLayoutState || index === 0) c.__calloutLayoutState = {
19465
- byIndex: {},
19466
- leftLastPx: Number.NEGATIVE_INFINITY,
19467
- rightLastPx: Number.NEGATIVE_INFINITY
19796
+ var getCalloutLayoutPixels = (chart) => {
19797
+ const area = chart?.chartArea;
19798
+ if (!chart || !area) return /* @__PURE__ */ new Map();
19799
+ const occupiedRects = getOccupiedOriginalLabelRects(chart, area, getOverlappingCalloutIds(chart));
19800
+ const grouped = getCalloutItems(chart, area).map((item) => {
19801
+ const xValue = clamp$1(item.preferredX, item.minX, item.maxX);
19802
+ return {
19803
+ ...item,
19804
+ preferredX: xValue
19805
+ };
19806
+ }).reduce((accumulator, item) => {
19807
+ accumulator[item.side].push(item);
19808
+ return accumulator;
19809
+ }, {
19810
+ left: [],
19811
+ right: []
19812
+ });
19813
+ const leftLayout = layoutSide(grouped.left, occupiedRects.filter((rect) => rect.side === "left").map((rect) => ({
19814
+ top: rect.top,
19815
+ bottom: rect.bottom
19816
+ })));
19817
+ const rightLayout = layoutSide(grouped.right, occupiedRects.filter((rect) => rect.side === "right").map((rect) => ({
19818
+ top: rect.top,
19819
+ bottom: rect.bottom
19820
+ })));
19821
+ return new Map([...leftLayout.entries(), ...rightLayout.entries()]);
19822
+ };
19823
+ var getCalloutLayout = (ctx, refAnnotation, index) => {
19824
+ const chart = getChartFromCtx$1(ctx);
19825
+ const xScale = chart ? getXScale(chart, getAxisId(refAnnotation, "x")) : void 0;
19826
+ const yScale = chart ? getYScale(chart, getAxisId(refAnnotation, "y")) : void 0;
19827
+ if (!chart || !yScale || !chart.chartArea) return null;
19828
+ if (!xScale && hasAxisAnchor(refAnnotation, "x")) return null;
19829
+ if (!xScale) return null;
19830
+ const layout = getCalloutLayoutPixels(chart).get(`callout-annotation-${index}`);
19831
+ if (!layout) return null;
19832
+ return {
19833
+ xValue: xScale.getValueForPixel(layout.xValue),
19834
+ yValue: yScale.getValueForPixel(layout.yValue)
19468
19835
  };
19469
- return c.__calloutLayoutState;
19470
19836
  };
19471
19837
  const resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
19472
- const chart = ctx?.chart;
19473
- const xVal = resolveAnchorX(chart, refAnnotation);
19838
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
19839
+ if (layout) return layout.xValue;
19840
+ const chart = getChartFromCtx$1(ctx);
19841
+ const xVal = refAnnotation?.xValue;
19842
+ if (typeof xVal !== "number") return 0;
19474
19843
  if (!chart) return xVal;
19475
- const scale = resolveXScale(chart, refAnnotation);
19844
+ const scale = getXScale(chart, getAxisId(refAnnotation, "x"));
19476
19845
  if (!scale) return xVal;
19477
19846
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
19478
- const side = getDefaultXDirection(refAnnotation, index);
19479
- const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
19480
- if (!isNumber$1(xVal)) return xVal;
19847
+ const side = index % 2 === 0 ? 1 : -1;
19848
+ const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * DEFAULT_HORIZONTAL_OFFSET;
19481
19849
  const basePx = scale.getPixelForValue(xVal);
19482
- const cxBase = basePx + dx;
19483
19850
  const area = chart.chartArea;
19484
- if (!area) return scale.getValueForPixel(cxBase);
19485
- const margin = calloutCfg?.margin ?? 8;
19486
- const halfW = 160 / 2;
19487
- let cx = cxBase;
19488
- let adjDx = dx;
19489
- if (cx - halfW < area.left + margin) {
19490
- adjDx += area.left + margin + halfW - cx;
19491
- cx = basePx + adjDx;
19492
- } else if (cx + halfW > area.right - margin) {
19493
- adjDx += area.right - margin - halfW - cx;
19494
- cx = basePx + adjDx;
19495
- }
19496
- return scale.getValueForPixel(cx);
19851
+ const element = ctx?.element;
19852
+ if (!area || !element) return scale.getValueForPixel(basePx + dx);
19853
+ const margin = calloutCfg?.margin ?? DEFAULT_MARGIN;
19854
+ const halfWidth = (element?.width ?? 0) / 2;
19855
+ const clampedPx = clamp$1(basePx + dx, area.left + margin + halfWidth, area.right - margin - halfWidth);
19856
+ return scale.getValueForPixel(clampedPx);
19497
19857
  };
19498
19858
  const resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
19499
- const chart = ctx?.chart;
19500
- const yVal = resolveAnchorY(chart, refAnnotation);
19859
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
19860
+ if (layout) return layout.yValue;
19861
+ const chart = getChartFromCtx$1(ctx);
19862
+ const yVal = refAnnotation?.yValue;
19863
+ if (typeof yVal !== "number") return 0;
19501
19864
  if (!chart) return yVal;
19502
- const scale = resolveYScale(chart, refAnnotation);
19865
+ const scale = getYScale(chart, getAxisId(refAnnotation, "y"));
19503
19866
  if (!scale) return yVal;
19504
19867
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
19505
- const layoutIndex = typeof calloutCfg?.layoutIndex === "number" ? calloutCfg.layoutIndex : index;
19506
- const row = Math.floor(layoutIndex / 2);
19507
- const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
19508
- if (!isNumber$1(yVal)) return yVal;
19868
+ const row = Math.floor(index / 2);
19869
+ const dy = hasCustomOffset ? calloutCfg?.yAdjust : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
19509
19870
  const basePx = scale.getPixelForValue(yVal);
19510
- const laneBaseOffset = -24;
19511
- let cyBase = !hasCustomOffset ? basePx + laneBaseOffset : basePx + dy;
19512
19871
  const area = chart.chartArea;
19513
- if (!area) return scale.getValueForPixel(cyBase);
19514
- const margin = calloutCfg?.margin ?? 8;
19515
- const halfH = 18 / 2;
19516
- const minGapPx = calloutCfg?.minGapPx ?? halfH * 2 + 4;
19517
- let cy = cyBase;
19518
- let adjDy = !hasCustomOffset ? laneBaseOffset : dy;
19519
- if (!hasCustomOffset) {
19520
- const side = getDefaultXDirection(refAnnotation, layoutIndex);
19521
- const state = getCalloutLayoutState(chart, layoutIndex);
19522
- const cachedCy = state.byIndex[layoutIndex];
19523
- if (isNumber$1(cachedCy)) cy = cachedCy;
19524
- else {
19525
- if (side < 0) {
19526
- cy = Math.max(cy, state.leftLastPx + minGapPx);
19527
- state.leftLastPx = cy;
19528
- } else {
19529
- cy = Math.max(cy, state.rightLastPx + minGapPx);
19530
- state.rightLastPx = cy;
19531
- }
19532
- state.byIndex[layoutIndex] = cy;
19533
- }
19534
- }
19535
- if (cy - halfH < area.top + margin) {
19536
- adjDy += area.top + margin + halfH - cy;
19537
- cy = basePx + adjDy;
19538
- } else if (cy + halfH > area.bottom - margin) {
19539
- adjDy += area.bottom - margin - halfH - cy;
19540
- cy = basePx + adjDy;
19541
- }
19542
- return scale.getValueForPixel(cy);
19543
- };
19544
- var isInRange = (v, min, max) => v >= min && v <= max;
19872
+ const element = ctx?.element;
19873
+ if (!area || !element) return scale.getValueForPixel(basePx + dy);
19874
+ const margin = calloutCfg?.margin ?? DEFAULT_MARGIN;
19875
+ const halfHeight = (element?.height ?? 0) / 2;
19876
+ const clampedPx = clamp$1(basePx + dy, area.top + margin + halfHeight, area.bottom - margin - halfHeight);
19877
+ return scale.getValueForPixel(clampedPx);
19878
+ };
19879
+ var isInRange = (value, min, max) => value >= min && value <= max;
19545
19880
  const isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
19546
- const chart = ctx?.chart;
19881
+ const chart = getChartFromCtx$1(ctx);
19547
19882
  const area = chart?.chartArea;
19548
19883
  if (!chart || !area) return true;
19549
- const xScale = resolveXScale(chart, refAnnotation);
19550
- const yScale = resolveYScale(chart, refAnnotation);
19551
- const xVal = resolveAnchorX(chart, refAnnotation);
19552
- const yVal = resolveAnchorY(chart, refAnnotation);
19553
- if (!xScale || !yScale) return false;
19554
- if (!isNumber$1(xVal) || !isNumber$1(yVal)) return false;
19555
- const x = xScale.getPixelForValue(xVal);
19556
- const y = yScale.getPixelForValue(yVal);
19884
+ const xScale = getXScale(chart, getAxisId(refAnnotation, "x"));
19885
+ const yScale = getYScale(chart, getAxisId(refAnnotation, "y"));
19886
+ const hasXValues = hasAxisAnchor(refAnnotation, "x");
19887
+ const hasYValues = hasAxisAnchor(refAnnotation, "y");
19888
+ if (hasXValues && !xScale || hasYValues && !yScale) return false;
19889
+ if (!hasXValues || !hasYValues) return false;
19890
+ const x = getAnchorPixel(chart, refAnnotation, "x", area);
19891
+ const y = getAnchorPixel(chart, refAnnotation, "y", area);
19557
19892
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
19558
19893
  };
19559
- const getChartFromCtx = (ctx) => {
19560
- if (!ctx) return;
19561
- if (ctx.chart?.persistenceId !== void 0) return ctx.chart;
19562
- if (ctx.chart?.chart?.persistenceId !== void 0) return ctx.chart.chart;
19563
- if (ctx.$context?.chart?.persistenceId !== void 0) return ctx.$context.chart;
19564
- return ctx.chart?.chart ?? ctx.chart ?? ctx.$context?.chart;
19565
- };
19566
- var isFiniteNumber$1 = (value) => {
19567
- return typeof value === "number" && Number.isFinite(value);
19568
- };
19569
- var parseFontSize = (font) => {
19570
- if (!font) return 12;
19571
- const match = font.match(/(\d+(?:\.\d+)?)px/);
19572
- return match ? Number(match[1]) : 12;
19573
- };
19574
- const getScaleByAxis = (chart, axis, ann) => {
19575
- const preferred = axis === "x" ? [ann?.xScaleID, ann?.annotationAxis] : [ann?.yScaleID, ann?.annotationAxis];
19576
- for (const key of preferred) if (key && chart?.scales?.[key]) return chart.scales[key];
19577
- if (axis === "x" && chart?.scales?.x) return chart.scales.x;
19578
- if (axis === "y" && chart?.scales?.y) return chart.scales.y;
19579
- const contains = axis === "x" ? "x" : "y";
19580
- const key = Object.keys(chart?.scales ?? {}).find((k) => k.toLowerCase().includes(contains));
19581
- return key ? chart.scales[key] : void 0;
19582
- };
19583
- const getAnchorValueFromData = (ann, axis) => {
19584
- const value = axis === "x" ? ann?.xValue : ann?.yValue;
19585
- if (isFiniteNumber$1(value)) return value;
19586
- const min = axis === "x" ? ann?.xMin : ann?.yMin;
19587
- const max = axis === "x" ? ann?.xMax : ann?.yMax;
19588
- if (isFiniteNumber$1(min) && isFiniteNumber$1(max)) return (min + max) / 2;
19589
- const annAxis = `${ann?.annotationAxis ?? ""}`.toLowerCase();
19590
- if (axis === "x" && annAxis.includes("x") && isFiniteNumber$1(ann?.value)) return ann.value;
19591
- if (axis === "y" && annAxis.includes("y") && isFiniteNumber$1(ann?.value)) return ann.value;
19592
- };
19593
- const getLabelSide = (ann) => {
19594
- const p = `${ann?.labelConfig?.position ?? ""}`.toLowerCase();
19595
- if (p.includes("left")) return "left";
19596
- if (p.includes("right")) return "right";
19597
- return "center";
19598
- };
19599
- const buildLikelyOverlapSet = (annotationsData) => {
19600
- const points = annotationsData.map((ann, idx) => ({
19601
- idx,
19602
- side: getLabelSide(ann),
19603
- x: getAnchorValueFromData(ann, "x"),
19604
- y: getAnchorValueFromData(ann, "y"),
19605
- label: ann?.label,
19606
- visible: ann?.display !== false
19607
- })).filter((p) => p.visible && !!p.label && isFiniteNumber$1(p.y));
19608
- if (!points.length) return /* @__PURE__ */ new Set();
19609
- const ys = points.map((p) => p.y);
19610
- const rangeY = Math.max(...ys) - Math.min(...ys);
19611
- const yThreshold = Math.max(120, rangeY * .06);
19612
- const xThreshold = .35;
19613
- const overlapSet = /* @__PURE__ */ new Set();
19614
- for (let i = 0; i < points.length; i += 1) for (let j = i + 1; j < points.length; j += 1) {
19615
- const a = points[i];
19616
- const b = points[j];
19617
- if (!(a.side === "center" || b.side === "center" || a.side === b.side)) continue;
19618
- const xClose = !isFiniteNumber$1(a.x) || !isFiniteNumber$1(b.x) ? true : Math.abs(a.x - b.x) <= xThreshold;
19619
- if (Math.abs(a.y - b.y) <= yThreshold && xClose) {
19620
- overlapSet.add(a.idx);
19621
- overlapSet.add(b.idx);
19622
- }
19623
- }
19624
- return overlapSet;
19625
- };
19626
- const buildOverlapCalloutYMap = (annotationsData, overlapSet) => {
19627
- const points = annotationsData.map((ann, idx) => ({
19628
- idx,
19629
- side: getLabelSide(ann),
19630
- y: getAnchorValueFromData(ann, "y")
19631
- })).filter((p) => overlapSet.has(p.idx) && typeof p.y === "number");
19632
- const yMap = /* @__PURE__ */ new Map();
19633
- if (!points.length) return yMap;
19634
- const ys = points.map((p) => p.y);
19635
- const minY = Math.min(...ys);
19636
- const maxY = Math.max(...ys);
19637
- const yRange = Math.max(maxY - minY, 1);
19638
- const minGap = Math.max(130, yRange * .03);
19639
- const minBound = minY - minGap * .5;
19640
- const maxBound = maxY + minGap * .5;
19641
- const groups = {
19642
- left: [],
19643
- right: []
19644
- };
19645
- points.forEach((p) => {
19646
- if (p.side === "right") {
19647
- groups.right.push({
19648
- idx: p.idx,
19649
- y: p.y
19650
- });
19651
- return;
19652
- }
19653
- groups.left.push({
19654
- idx: p.idx,
19655
- y: p.y
19656
- });
19657
- });
19658
- Object.keys(groups).forEach((side) => {
19659
- const group = groups[side].sort((a, b) => a.y - b.y);
19660
- if (!group.length) return;
19661
- const packed = group.map((item, i) => {
19662
- return i === 0 ? item.y : Math.max(item.y, group[i - 1].y + minGap);
19663
- });
19664
- const overflowBottom = packed[packed.length - 1] - maxBound;
19665
- if (overflowBottom > 0) for (let i = 0; i < packed.length; i += 1) packed[i] -= overflowBottom;
19666
- const overflowTop = minBound - packed[0];
19667
- if (overflowTop > 0) for (let i = 0; i < packed.length; i += 1) packed[i] += overflowTop;
19668
- group.forEach((item, i) => {
19669
- yMap.set(item.idx, packed[i]);
19670
- });
19671
- });
19672
- return yMap;
19673
- };
19674
- const getOverlapCalloutXAdjust = (ann) => {
19675
- const side = getLabelSide(ann);
19676
- if (side === "right") return -56;
19677
- if (side === "left") return 56;
19678
- return 0;
19679
- };
19680
- const hasOverlappingLabel = (ctx, refAnnotation, index, annotationsData) => {
19681
- const chart = getChartFromCtx(ctx);
19682
- if (!chart) return false;
19683
- const yScale = getScaleByAxis(chart, "y", refAnnotation);
19684
- const xScale = getScaleByAxis(chart, "x", refAnnotation);
19685
- if (!yScale || !xScale) return false;
19686
- const area = chart.chartArea;
19687
- if (!area) return false;
19688
- const yVal = getAnchorValueFromData(refAnnotation, "y");
19689
- const xVal = getAnchorValueFromData(refAnnotation, "x");
19690
- if (!isFiniteNumber$1(yVal)) return false;
19691
- const currentYPx = yScale.getPixelForValue(yVal);
19692
- const currentXPx = isFiniteNumber$1(xVal) ? xScale.getPixelForValue(xVal) : void 0;
19693
- const currentSide = getLabelSide(refAnnotation);
19694
- const currentFont = refAnnotation?.labelConfig?.font;
19695
- const currentHeight = parseFontSize(currentFont) + 6;
19696
- const nearTop = currentYPx - currentHeight <= area.top + 2;
19697
- const nearBottom = currentYPx + currentHeight >= area.bottom - 2;
19698
- if (nearTop || nearBottom) return true;
19699
- for (let i = 0; i < annotationsData.length; i += 1) {
19700
- if (i === index) continue;
19701
- const ann = annotationsData[i];
19702
- if (!ann?.label || ann?.display === false) continue;
19703
- const otherYScale = getScaleByAxis(chart, "y", ann);
19704
- const otherXScale = getScaleByAxis(chart, "x", ann);
19705
- if (!otherYScale || !otherXScale) continue;
19706
- const otherYVal = getAnchorValueFromData(ann, "y");
19707
- const otherXVal = getAnchorValueFromData(ann, "x");
19708
- if (!isFiniteNumber$1(otherYVal)) continue;
19709
- const otherYPx = otherYScale.getPixelForValue(otherYVal);
19710
- const otherXPx = isFiniteNumber$1(otherXVal) ? otherXScale.getPixelForValue(otherXVal) : void 0;
19711
- const otherSide = getLabelSide(ann);
19712
- const otherFont = ann?.labelConfig?.font;
19713
- const yThreshold = (currentHeight + (parseFontSize(otherFont) + 6)) / 2;
19714
- const xClose = !isFiniteNumber$1(currentXPx) || !isFiniteNumber$1(otherXPx) ? true : Math.abs(currentXPx - otherXPx) < 160;
19715
- if ((currentSide === "center" || otherSide === "center" || currentSide === otherSide) && Math.abs(currentYPx - otherYPx) < yThreshold && xClose) return true;
19716
- }
19717
- return false;
19718
- };
19719
19894
  var STORAGE_PREFIX = "chart_annotations";
19720
19895
  var getStorageKey = (persistenceId) => {
19721
19896
  return `${STORAGE_PREFIX}:${persistenceId}`;
@@ -19750,9 +19925,12 @@ const getAnnotationPosition = (persistenceId, annotationId) => {
19750
19925
  if (!persistenceId) return void 0;
19751
19926
  return readStorage(persistenceId)[annotationId];
19752
19927
  };
19753
- var isDragActive = (chart, annotation) => {
19754
- const pluginEnabled = Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled);
19755
- return Boolean(annotation?.enableDrag && pluginEnabled);
19928
+ const getChartFromCtx = (ctx) => {
19929
+ if (!ctx) return;
19930
+ if (ctx.chart?.persistenceId !== void 0) return ctx.chart;
19931
+ if (ctx.chart?.chart?.persistenceId !== void 0) return ctx.chart.chart;
19932
+ if (ctx.$context?.chart?.persistenceId !== void 0) return ctx.$context.chart;
19933
+ return ctx.chart?.chart ?? ctx.chart ?? ctx.$context?.chart;
19756
19934
  };
19757
19935
  var mapToBoxLikeLabelPosition = (position) => {
19758
19936
  switch (position) {
@@ -19792,8 +19970,7 @@ var mapToBoxLikeLabelPosition = (position) => {
19792
19970
  }
19793
19971
  };
19794
19972
  var handleLineEnter = (element, chart, annotation) => {
19795
- const canDrag = isDragActive(chart, annotation);
19796
- chart.canvas.style.cursor = canDrag ? CursorStyle.Pointer : CursorStyle.Initial;
19973
+ chart.canvas.style.cursor = CursorStyle.Pointer;
19797
19974
  if (element.options.scaleID?.includes(AxisType.X)) {
19798
19975
  if (element.options.label) element.options.label.xAdjust = chart.chartArea.left;
19799
19976
  }
@@ -19802,7 +19979,7 @@ var handleLineEnter = (element, chart, annotation) => {
19802
19979
  element.label.options.display = true;
19803
19980
  element.options.label.enabled = true;
19804
19981
  }
19805
- if (canDrag) {
19982
+ if (annotation?.enableDrag) {
19806
19983
  chart.hoveredAnnotationId = element.options.id || null;
19807
19984
  chart.update("none");
19808
19985
  }
@@ -19816,14 +19993,14 @@ var handleLineLeave = (element, chart, annotation) => {
19816
19993
  element.label.options.display = false;
19817
19994
  element.options.label.enabled = false;
19818
19995
  }
19819
- if (isDragActive(chart, annotation)) {
19996
+ if (annotation?.enableDrag) {
19820
19997
  chart.hoveredAnnotationId = null;
19821
19998
  chart.update("none");
19822
19999
  }
19823
20000
  chart.draw();
19824
20001
  };
19825
20002
  var handleBoxEnter = (element, chart, annotation) => {
19826
- if (isDragActive(chart, annotation)) {
20003
+ if (annotation?.enableDrag) {
19827
20004
  chart.hoveredAnnotationId = element.options.id || null;
19828
20005
  chart.update("none");
19829
20006
  element.options.borderWidth = BORDER_WIDTH.HOVERED;
@@ -19832,7 +20009,7 @@ var handleBoxEnter = (element, chart, annotation) => {
19832
20009
  chart.draw();
19833
20010
  };
19834
20011
  var handleBoxLeave = (element, chart, annotation) => {
19835
- if (isDragActive(chart, annotation)) {
20012
+ if (annotation?.enableDrag) {
19836
20013
  chart.hoveredAnnotationId = null;
19837
20014
  element.options.borderWidth = BORDER_WIDTH.INITIAL;
19838
20015
  chart.canvas.style.cursor = CursorStyle.Initial;
@@ -19841,7 +20018,7 @@ var handleBoxLeave = (element, chart, annotation) => {
19841
20018
  chart.draw();
19842
20019
  };
19843
20020
  var handlePointEnter = (element, chart, annotation) => {
19844
- if (isDragActive(chart, annotation)) {
20021
+ if (annotation?.enableDrag) {
19845
20022
  chart.hoveredAnnotationId = element.options.id || null;
19846
20023
  chart.update("none");
19847
20024
  element.options.borderWidth = BORDER_WIDTH.POINT_HOVERED;
@@ -19850,7 +20027,7 @@ var handlePointEnter = (element, chart, annotation) => {
19850
20027
  chart.draw();
19851
20028
  };
19852
20029
  var handlePointLeave = (element, chart, annotation) => {
19853
- if (isDragActive(chart, annotation)) {
20030
+ if (annotation?.enableDrag) {
19854
20031
  chart.hoveredAnnotationId = null;
19855
20032
  element.options.borderWidth = BORDER_WIDTH.INITIAL;
19856
20033
  chart.canvas.style.cursor = CursorStyle.Initial;
@@ -19859,7 +20036,7 @@ var handlePointLeave = (element, chart, annotation) => {
19859
20036
  chart.draw();
19860
20037
  };
19861
20038
  var handleLabelEnter = (element, chart, annotation) => {
19862
- if (isDragActive(chart, annotation)) {
20039
+ if (annotation?.enableDrag) {
19863
20040
  chart.hoveredAnnotationId = element.options.id || null;
19864
20041
  chart.update("none");
19865
20042
  chart.canvas.style.cursor = CursorStyle.Pointer;
@@ -19867,7 +20044,7 @@ var handleLabelEnter = (element, chart, annotation) => {
19867
20044
  chart.draw();
19868
20045
  };
19869
20046
  var handleLabelLeave = (_element, chart, annotation) => {
19870
- if (isDragActive(chart, annotation)) {
20047
+ if (annotation?.enableDrag) {
19871
20048
  chart.hoveredAnnotationId = null;
19872
20049
  chart.canvas.style.cursor = CursorStyle.Initial;
19873
20050
  chart.update("none");
@@ -19998,41 +20175,32 @@ const generateAnn = (ann, index) => {
19998
20175
  displayDragCoordinates: ann?.displayDragCoordinates ?? true
19999
20176
  };
20000
20177
  };
20001
- var getCalloutAnnotation = (refAnnotation, index, annotationsData, likelyOverlapSet, overlapCalloutYMap, layoutIndex) => {
20178
+ var getCalloutAnnotation = (refAnnotation, index) => {
20002
20179
  const generated = generateAnn(refAnnotation, index);
20003
20180
  if (!generated) return [];
20004
20181
  const { label, ...baseRest } = generated;
20005
20182
  const baseId = baseRest.id;
20006
20183
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
20007
- const onlyWhenOverlapping = calloutCfg?.onlyWhenOverlapping ?? false;
20008
- const hideBoxGlyphInOverlapMode = onlyWhenOverlapping && refAnnotation?.type === AnnotationType$1.BOX;
20009
- const staticShouldShowCallout = likelyOverlapSet.has(index);
20010
- const shouldShowCallout = (ctx) => {
20011
- return !onlyWhenOverlapping || staticShouldShowCallout || calloutCfg?.useRuntimeOverlap === true && hasOverlappingLabel(ctx, refAnnotation, index, annotationsData);
20012
- };
20013
- const canDisplayCallout = (ctx) => {
20014
- return (refAnnotation.display ?? true) && shouldShowCallout(ctx) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0);
20015
- };
20016
20184
  const baseAnnotation = {
20017
20185
  ...baseRest,
20018
- backgroundColor: hideBoxGlyphInOverlapMode ? TRANSPARENT : baseRest?.backgroundColor,
20019
- borderWidth: hideBoxGlyphInOverlapMode ? BORDER_WIDTH.ZERO : baseRest?.borderWidth,
20020
- borderColor: hideBoxGlyphInOverlapMode ? TRANSPARENT : baseRest?.borderColor,
20021
20186
  label: {
20022
20187
  ...label,
20023
- display: (ctx) => (refAnnotation?.display ?? true) && (!calloutCfg?.enabled || !canDisplayCallout(ctx))
20188
+ display: (ctx) => {
20189
+ if (!(refAnnotation.display ?? true)) return false;
20190
+ if (!calloutCfg?.onlyWhenOverlapping) return false;
20191
+ return !isCalloutOverlapping(ctx, refAnnotation, index);
20192
+ }
20024
20193
  }
20025
20194
  };
20026
20195
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
20027
20196
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
20028
20197
  const borderColor = calloutCfg?.borderColor ?? "transparent";
20029
- const connectorStrokeStyle = calloutCfg.strokeStyle ?? (hideBoxGlyphInOverlapMode ? refAnnotation?.color ?? "hsl(60, 10.34482759%, 12.5%)" : baseAnnotation.borderColor);
20198
+ const strokeStyle = calloutCfg.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
20030
20199
  const lineWidth = typeof calloutCfg.lineWidth === "number" ? calloutCfg.lineWidth : 1;
20031
- const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 6;
20032
- const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : 1;
20200
+ const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 2;
20201
+ const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : .5;
20033
20202
  const calloutId = `callout-annotation-${index}`;
20034
- const isCalloutDraggable = refAnnotation?.enableDrag !== false;
20035
- const calloutAnnotation = {
20203
+ return [baseAnnotation, {
20036
20204
  id: calloutId,
20037
20205
  type: "label",
20038
20206
  backgroundColor: "transparent",
@@ -20040,47 +20208,38 @@ var getCalloutAnnotation = (refAnnotation, index, annotationsData, likelyOverlap
20040
20208
  color: (ctx) => {
20041
20209
  const chart = getChartFromCtx(ctx);
20042
20210
  if (!chart) return color;
20043
- const isDragModeEnabled = Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled);
20044
- if (!isCalloutDraggable || !isDragModeEnabled) return color;
20045
- if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
20211
+ if (Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) && chart.hoveredAnnotationId === calloutId) return "#DB5B00";
20046
20212
  return color;
20047
20213
  },
20048
20214
  font,
20049
20215
  borderColor,
20050
20216
  borderWidth: BORDER_WIDTH.INITIAL,
20051
20217
  padding: 5,
20052
- display: canDisplayCallout,
20218
+ display: (ctx) => {
20219
+ if (!(refAnnotation.display ?? true)) return false;
20220
+ if (!isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0)) return false;
20221
+ return isCalloutOverlapping(ctx, refAnnotation, index);
20222
+ },
20053
20223
  xValue: (ctx) => {
20054
20224
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
20055
20225
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
20056
20226
  const explicit = typeof calloutCfg.xValue === "number" ? calloutCfg.xValue : void 0;
20057
- const overlapCalloutAdjust = onlyWhenOverlapping && !calloutCfg?.xAdjust ? getOverlapCalloutXAdjust(refAnnotation) : calloutCfg?.xAdjust;
20058
- return stored?.x ?? explicit ?? resolveCalloutXValue(ctx, refAnnotation, {
20059
- ...calloutCfg,
20060
- xAdjust: overlapCalloutAdjust,
20061
- layoutIndex
20062
- }, index);
20227
+ return stored?.x ?? explicit ?? resolveCalloutXValue(ctx, refAnnotation, calloutCfg, index);
20063
20228
  },
20064
20229
  yValue: (ctx) => {
20065
20230
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
20066
20231
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
20067
20232
  const explicit = typeof calloutCfg.yValue === "number" ? calloutCfg.yValue : void 0;
20068
- const overlapPackedY = onlyWhenOverlapping ? overlapCalloutYMap.get(index) : void 0;
20069
- const overlapCalloutYAdjust = onlyWhenOverlapping && !calloutCfg?.yAdjust ? 0 : calloutCfg?.yAdjust;
20070
- return stored?.y ?? explicit ?? overlapPackedY ?? resolveCalloutYValue(ctx, refAnnotation, {
20071
- ...calloutCfg,
20072
- yAdjust: overlapCalloutYAdjust,
20073
- layoutIndex
20074
- }, index);
20233
+ return stored?.y ?? explicit ?? resolveCalloutYValue(ctx, refAnnotation, calloutCfg, index);
20075
20234
  },
20076
20235
  resizable: false,
20077
20236
  displayDragCoordinates: false,
20078
- enableDrag: isCalloutDraggable,
20237
+ enableDrag: true,
20079
20238
  enter: ({ element }, { chart }) => {
20080
- return handleLabelEnter(element, chart, { enableDrag: isCalloutDraggable });
20239
+ return handleLabelEnter(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
20081
20240
  },
20082
20241
  leave: ({ element }, { chart }) => {
20083
- return handleLabelLeave(element, chart, { enableDrag: isCalloutDraggable });
20242
+ return handleLabelLeave(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
20084
20243
  },
20085
20244
  onDragStart: calloutCfg?.onDragStart ? () => (coords) => {
20086
20245
  return calloutCfg.onDragStart?.(coords, refAnnotation);
@@ -20094,58 +20253,22 @@ var getCalloutAnnotation = (refAnnotation, index, annotationsData, likelyOverlap
20094
20253
  calloutConnector: {
20095
20254
  enabled: true,
20096
20255
  fromId: baseId,
20097
- strokeStyle: connectorStrokeStyle,
20256
+ strokeStyle,
20098
20257
  lineWidth,
20099
20258
  startOffset,
20100
- endOffset
20259
+ endOffset,
20260
+ anchor: {
20261
+ position: refAnnotation?.labelConfig?.position,
20262
+ xAdjust: refAnnotation?.labelConfig?.xAdjust,
20263
+ yAdjust: refAnnotation?.labelConfig?.yAdjust,
20264
+ labelOffsetPx: refAnnotation?.labelOffsetPx
20265
+ }
20101
20266
  }
20102
- };
20103
- const anchorX = getAnchorValueFromData(refAnnotation, "x");
20104
- const anchorY = getAnchorValueFromData(refAnnotation, "y");
20105
- const originTickAnnotation = typeof anchorX === "number" && typeof anchorY === "number" ? {
20106
- id: `callout-origin-tick-${index}`,
20107
- type: "line",
20108
- xMin: anchorX,
20109
- xMax: (ctx) => {
20110
- const chart = getChartFromCtx(ctx);
20111
- const xScale = chart ? getScaleByAxis(chart, "x", refAnnotation) : null;
20112
- if (!xScale) return anchorX;
20113
- const px = xScale.getPixelForValue(anchorX);
20114
- const tickLength = calloutCfg?.originTickLengthPx ?? 8;
20115
- return xScale.getValueForPixel(px + tickLength);
20116
- },
20117
- yMin: anchorY,
20118
- yMax: anchorY,
20119
- borderColor: calloutCfg?.originTickColor ?? connectorStrokeStyle,
20120
- borderWidth: calloutCfg?.originTickWidth ?? 1,
20121
- borderDash: [],
20122
- adjustScaleRange: false,
20123
- label: {
20124
- display: false,
20125
- content: ""
20126
- },
20127
- display: (ctx) => onlyWhenOverlapping && (refAnnotation.display ?? true) && shouldShowCallout(ctx),
20128
- resizable: false,
20129
- displayDragCoordinates: false,
20130
- enableDrag: false
20131
- } : null;
20132
- if (!onlyWhenOverlapping) return [baseAnnotation, calloutAnnotation];
20133
- return originTickAnnotation ? [
20134
- baseAnnotation,
20135
- originTickAnnotation,
20136
- calloutAnnotation
20137
- ] : [baseAnnotation, calloutAnnotation];
20267
+ }];
20138
20268
  };
20139
20269
  var generateAnnotations = (annotationsData) => {
20140
- const likelyOverlapSet = buildLikelyOverlapSet(annotationsData);
20141
- const overlapCalloutYMap = buildOverlapCalloutYMap(annotationsData, likelyOverlapSet);
20142
- let calloutLayoutIndex = 0;
20143
20270
  return annotationsData?.flatMap((ann, idx) => {
20144
- if (ann?.labelConfig?.callout?.enabled ?? false) {
20145
- const annotation = getCalloutAnnotation(ann, idx, annotationsData, likelyOverlapSet, overlapCalloutYMap, calloutLayoutIndex);
20146
- calloutLayoutIndex += 1;
20147
- return annotation;
20148
- }
20271
+ if (ann?.labelConfig?.callout?.enabled ?? false) return getCalloutAnnotation(ann, idx);
20149
20272
  return generateAnn(ann, idx);
20150
20273
  });
20151
20274
  };
@@ -20844,52 +20967,126 @@ const annotationDraggerPlugin = {
20844
20967
  }
20845
20968
  }
20846
20969
  };
20847
- var getRadius = (el) => {
20848
- if (typeof el.options?.radius === "number") return el.options.radius;
20849
- if (typeof el.width === "number" && typeof el.height === "number") return Math.min(el.width, el.height) / 2;
20850
- return 0;
20970
+ var getRectangleEdgePoint = (el, targetX, targetY, offset) => {
20971
+ const width = el?.width ?? 0;
20972
+ const height = el?.height ?? 0;
20973
+ if (width <= 0 || height <= 0) return {
20974
+ x: el.centerX,
20975
+ y: el.centerY
20976
+ };
20977
+ const halfWidth = width / 2;
20978
+ const halfHeight = height / 2;
20979
+ const dx = targetX - el.centerX;
20980
+ const dy = targetY - el.centerY;
20981
+ const length = Math.sqrt(dx * dx + dy * dy) || 1;
20982
+ const ux = dx / length;
20983
+ const uy = dy / length;
20984
+ const tx = Math.abs(ux) > 1e-6 ? halfWidth / Math.abs(ux) : Number.POSITIVE_INFINITY;
20985
+ const ty = Math.abs(uy) > 1e-6 ? halfHeight / Math.abs(uy) : Number.POSITIVE_INFINITY;
20986
+ const distance = Math.min(tx, ty) + offset;
20987
+ return {
20988
+ x: el.centerX + ux * distance,
20989
+ y: el.centerY + uy * distance
20990
+ };
20991
+ };
20992
+ var getLabelEdgePoint = (labelEl, startX, endOffset) => {
20993
+ const width = labelEl?.width ?? 0;
20994
+ const height = labelEl?.height ?? 0;
20995
+ if (width <= 0 || height <= 0) return {
20996
+ x: labelEl.centerX,
20997
+ y: labelEl.centerY
20998
+ };
20999
+ const halfWidth = width / 2;
21000
+ const direction = startX <= labelEl.centerX ? -1 : 1;
21001
+ return {
21002
+ x: labelEl.centerX + direction * (halfWidth + endOffset),
21003
+ y: labelEl.centerY
21004
+ };
21005
+ };
21006
+ var getReferencePoint = (fromEl, connector) => {
21007
+ const { anchor } = connector;
21008
+ if (!anchor) return {
21009
+ x: fromEl.centerX,
21010
+ y: fromEl.centerY
21011
+ };
21012
+ const width = fromEl?.width ?? 0;
21013
+ const height = fromEl?.height ?? 0;
21014
+ const radius = fromEl?.options?.radius ?? 0;
21015
+ const halfWidth = width / 2;
21016
+ const halfHeight = height / 2;
21017
+ const labelOffset = anchor?.labelOffsetPx ?? 14;
21018
+ const xAdjust = anchor?.xAdjust ?? 0;
21019
+ const yAdjust = anchor?.yAdjust ?? 0;
21020
+ const position = anchor?.position ?? "top";
21021
+ let x = fromEl.centerX;
21022
+ let y = fromEl.centerY;
21023
+ switch (position) {
21024
+ case "left":
21025
+ x -= Math.max(halfWidth, radius) + labelOffset;
21026
+ break;
21027
+ case "right":
21028
+ x += Math.max(halfWidth, radius) + labelOffset;
21029
+ break;
21030
+ case "bottom":
21031
+ y += Math.max(halfHeight, radius) + labelOffset;
21032
+ break;
21033
+ case "top-left":
21034
+ x -= halfWidth;
21035
+ y -= halfHeight;
21036
+ break;
21037
+ case "top-right":
21038
+ x += halfWidth;
21039
+ y -= halfHeight;
21040
+ break;
21041
+ case "bottom-left":
21042
+ x -= halfWidth;
21043
+ y += halfHeight;
21044
+ break;
21045
+ case "bottom-right":
21046
+ x += halfWidth;
21047
+ y += halfHeight;
21048
+ break;
21049
+ default:
21050
+ y -= Math.max(halfHeight, radius) + labelOffset;
21051
+ break;
21052
+ }
21053
+ return {
21054
+ x: x + xAdjust,
21055
+ y: y + yAdjust
21056
+ };
20851
21057
  };
20852
21058
  const computeConnectorPoints = (fromEl, labelEl, connector) => {
20853
21059
  const x1c = fromEl?.centerX;
20854
21060
  const y1c = fromEl?.centerY;
20855
21061
  const cx = labelEl?.centerX;
20856
21062
  const cy = labelEl?.centerY;
20857
- const labelWidth = labelEl?.width ?? 0;
20858
- const labelHeight = labelEl?.height ?? 0;
20859
21063
  const ux = cx - x1c;
20860
21064
  const uy = cy - y1c;
20861
21065
  const len = Math.sqrt(ux * ux + uy * uy) || 1;
20862
21066
  const dx = ux / len;
20863
21067
  const dy = uy / len;
20864
- const fromRadius = getRadius(fromEl);
20865
- const startOffset = (connector?.startOffset ?? 6) + fromRadius;
20866
- const startX = x1c + dx * startOffset;
20867
- const startY = y1c + dy * startOffset;
20868
- let endX = cx;
20869
- let endY = cy;
20870
- if (labelWidth > 0 && labelHeight > 0) {
20871
- const hw = labelWidth / 2;
20872
- const hh = labelHeight / 2;
20873
- const vx = x1c - cx;
20874
- const vy = y1c - cy;
20875
- const vlen = Math.sqrt(vx * vx + vy * vy) || 1;
20876
- const vxn = vx / vlen;
20877
- const vyn = vy / vlen;
20878
- const tx = Math.abs(vxn) > 1e-6 ? hw / Math.abs(vxn) : Number.POSITIVE_INFINITY;
20879
- const ty = Math.abs(vyn) > 1e-6 ? hh / Math.abs(vyn) : Number.POSITIVE_INFINITY;
20880
- const total = Math.min(tx, ty) + (connector.endOffset ?? 1);
20881
- endX = cx + vxn * total;
20882
- endY = cy + vyn * total;
20883
- } else {
20884
- const endOffset = connector.endOffset ?? 1;
20885
- endX = cx - dx * endOffset;
20886
- endY = cy - dy * endOffset;
20887
- }
21068
+ const startOffset = connector?.startOffset ?? 6;
21069
+ const isPointAnchor = typeof fromEl?.options?.radius === "number";
21070
+ const referencePoint = connector?.anchor && !isPointAnchor ? getReferencePoint(fromEl, connector) : isPointAnchor ? {
21071
+ x: x1c,
21072
+ y: y1c
21073
+ } : getRectangleEdgePoint(fromEl, cx, cy, startOffset);
21074
+ const endOffset = connector?.endOffset ?? 1;
21075
+ const end = typeof labelEl?.width === "number" && typeof labelEl?.height === "number" ? getLabelEdgePoint(labelEl, referencePoint.x, endOffset) : {
21076
+ x: cx - dx * endOffset,
21077
+ y: cy - dy * endOffset
21078
+ };
21079
+ const tickHalf = isPointAnchor ? 0 : (connector?.anchor?.tickSize ?? 8) / 2;
21080
+ const lineDirection = end.x >= referencePoint.x ? 1 : -1;
20888
21081
  return {
20889
- startX,
20890
- startY,
20891
- endX,
20892
- endY
21082
+ startX: referencePoint.x + lineDirection * tickHalf,
21083
+ startY: referencePoint.y,
21084
+ endX: end.x,
21085
+ endY: end.y,
21086
+ tickStartX: referencePoint.x - tickHalf,
21087
+ tickStartY: referencePoint.y,
21088
+ tickEndX: referencePoint.x + tickHalf,
21089
+ tickEndY: referencePoint.y
20893
21090
  };
20894
21091
  };
20895
21092
  const calloutConnectorPlugin = {
@@ -20910,17 +21107,24 @@ const calloutConnectorPlugin = {
20910
21107
  const fromEl = elements.find((el) => el.options && el.options.id === connector.fromId);
20911
21108
  if (!labelEl || !fromEl) return;
20912
21109
  if (labelEl.options?.display === false) return;
20913
- const { startX, startY, endX, endY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
21110
+ const { startX, startY, endX, endY, tickStartX, tickStartY, tickEndX, tickEndY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
20914
21111
  const ctx = chart?.ctx;
21112
+ const strokeStyle = connector?.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
20915
21113
  ctx?.save?.();
20916
21114
  ctx?.beginPath?.();
20917
21115
  ctx?.moveTo?.(startX, startY);
20918
21116
  ctx?.lineTo?.(endX, endY);
20919
21117
  ctx.lineWidth = connector?.lineWidth ?? 1;
20920
- ctx.strokeStyle = connector?.strokeStyle ?? "rgba(120,120,120,0.9)";
21118
+ ctx.strokeStyle = strokeStyle;
20921
21119
  if (connector?.lineDash && connector?.lineDash?.length) ctx?.setLineDash?.(connector.lineDash);
20922
21120
  ctx.lineCap = "round";
20923
21121
  ctx?.stroke?.();
21122
+ ctx?.beginPath?.();
21123
+ ctx?.moveTo?.(tickStartX, tickStartY);
21124
+ ctx?.lineTo?.(tickEndX, tickEndY);
21125
+ ctx.lineWidth = Math.max(connector?.lineWidth ?? 1, 1);
21126
+ ctx.strokeStyle = strokeStyle;
21127
+ ctx?.stroke?.();
20924
21128
  ctx?.restore?.();
20925
21129
  });
20926
21130
  }