@oliasoft-open-source/charts-library 5.8.0 → 5.9.0-beta-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/assets/{Color-6BZIO3FS-C124yz2l.js → Color-6BZIO3FS-CmhoIRR3.js} +1 -1
  2. package/dist/assets/{DocsRenderer-LL677BLK-D4qKUuBB.js → DocsRenderer-LL677BLK-D-z3K9l-.js} +4 -4
  3. package/dist/assets/{WithTooltip-65CFNBJE-CtWnqLza.js → WithTooltip-65CFNBJE-BuWPqs9e.js} +1 -1
  4. package/dist/assets/{bar-chart.stories-C3SMcQsP.js → bar-chart.stories-DpO6wsZ7.js} +2 -2
  5. package/dist/assets/{chunk-YKABRMAI-Ahx4OkN7.js → chunk-YKABRMAI-B1vnVt6R.js} +1 -1
  6. package/dist/assets/{components-DIpk8W7G.js → components-z8NZ0Kvw.js} +4 -4
  7. package/dist/assets/formatter-EIJCOSYU-Bvf14B6E.js +1 -0
  8. package/dist/assets/{get-draggableData-w1LT3_ff.js → get-draggableData-ylXaB6q9.js} +1 -1
  9. package/dist/assets/{iframe-DApKJtpy.js → iframe-Bi6WLX9H.js} +3 -3
  10. package/dist/assets/{legend-context-CQKzSsRL.js → legend-context-BtWRLDaH.js} +3 -3
  11. package/dist/assets/{legend-BIh7BsGA.js → legend-uQ084jKt.js} +39 -39
  12. package/dist/assets/line-chart-DCFghsDm.js +1 -0
  13. package/dist/assets/{line-chart.stories-BI9cuT6Z.js → line-chart.stories-CCs6790G.js} +7 -3
  14. package/dist/assets/{line-chart.test-case.stories-BxTEBLo3.js → line-chart.test-case.stories-C5ynxvy0.js} +127 -4
  15. package/dist/assets/{pie-chart.stories-BHZXRphh.js → pie-chart.stories-JD8YUf2H.js} +1 -1
  16. package/dist/assets/{react-18-JAuqV3gQ.js → react-18-DMnEgq9E.js} +1 -1
  17. package/dist/assets/{react-DPAuT4W0.js → react-CE5jLosF.js} +1 -1
  18. package/dist/assets/{react-dom-CWw972ZY.js → react-dom-DOIjQDPm.js} +1 -1
  19. package/dist/assets/{scatter-chart.stories-DQLyCWVA.js → scatter-chart.stories-CpuFiR5B.js} +1 -1
  20. package/dist/assets/{syntaxhighlighter-ED5Y7EFY-CxhX_a0D.js → syntaxhighlighter-ED5Y7EFY-Cmr1QuFp.js} +1 -1
  21. package/dist/assets/{theming-BGumyAJ5.js → theming-DxqRh7ol.js} +1 -1
  22. package/dist/common/common.interface.d.ts +7 -0
  23. package/dist/common/helpers/callout-helpers/callout-overlap-helpers.d.ts +9 -0
  24. package/dist/common/helpers/get-chart-annotation.d.ts +48 -6
  25. package/dist/iframe.html +1 -1
  26. package/dist/index.js +365 -57
  27. package/dist/index.json +1 -1
  28. package/dist/line-chart/plugins/vertical-markers-plugin/draw.d.ts +24 -0
  29. package/dist/line-chart/plugins/vertical-markers-plugin/types.d.ts +42 -0
  30. package/dist/line-chart/plugins/vertical-markers-plugin/utils.d.ts +23 -0
  31. package/dist/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.d.ts +54 -0
  32. package/dist/project.json +1 -1
  33. package/dist/src/components/common/helpers/callout-helpers/callout-overlap-helpers.d.ts +9 -0
  34. package/package.json +1 -1
  35. package/dist/assets/formatter-EIJCOSYU-B8p8ny5o.js +0 -1
  36. package/dist/assets/line-chart-CcOicjlw.js +0 -1
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$1(n) {
805
+ function isNumber$2(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$1(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4355
+ const maxGapLength = isNumber$2(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$1(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
4478
+ const maxGapLength = isNumber$2(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$1(this.x) && isNumber$1(this.y);
5284
+ return isNumber$2(this.x) && isNumber$2(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$1(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round);
11393
+ if (round) value = round === "week" && (isNumber$2(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$1(weekday) || weekday === true;
11588
+ const hasWeekday = isNumber$2(weekday) || weekday === true;
11589
11589
  const ticks = {};
11590
11590
  let first = min;
11591
11591
  let time, count;
@@ -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$1(value) ? value : elementValue;
15503
- return isNumber$1(opacity) ? clamp$1(opacity, 0, 1) : 1;
15502
+ const opacity = isNumber$2(value) ? value : elementValue;
15503
+ return isNumber$2(opacity) ? clamp$1(opacity, 0, 1) : 1;
15504
15504
  }
15505
15505
  var positions = [
15506
15506
  "left",
@@ -17595,6 +17595,7 @@ 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";
17598
17599
  const LOGARITHMIC_STEPS = [
17599
17600
  1,
17600
17601
  10,
@@ -19404,23 +19405,85 @@ var getYScale = (chart, axisId) => {
19404
19405
  const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
19405
19406
  return key ? chart?.scales?.[key] : void 0;
19406
19407
  };
19408
+ var isNumber$1 = (value) => {
19409
+ return typeof value === "number" && Number.isFinite(value);
19410
+ };
19411
+ var looksLikeXAxis = (axisId) => {
19412
+ return !!axisId && axisId.toLowerCase().includes("x");
19413
+ };
19414
+ var looksLikeYAxis = (axisId) => {
19415
+ return !!axisId && axisId.toLowerCase().includes("y");
19416
+ };
19417
+ var resolveXScale = (chart, refAnnotation) => {
19418
+ return getXScale(chart, refAnnotation?.xScaleID) ?? getXScale(chart, looksLikeXAxis(refAnnotation?.annotationAxis) ? refAnnotation?.annotationAxis : void 0) ?? getXScale(chart, refAnnotation?.annotationAxis);
19419
+ };
19420
+ var resolveYScale = (chart, refAnnotation) => {
19421
+ return getYScale(chart, refAnnotation?.yScaleID) ?? getYScale(chart, looksLikeYAxis(refAnnotation?.annotationAxis) ? refAnnotation?.annotationAxis : void 0) ?? getYScale(chart, refAnnotation?.annotationAxis);
19422
+ };
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;
19428
+ };
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);
19450
+ };
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;
19460
+ }
19461
+ };
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
19468
+ };
19469
+ return c.__calloutLayoutState;
19470
+ };
19407
19471
  const resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
19408
19472
  const chart = ctx?.chart;
19409
- const xVal = refAnnotation?.xValue;
19410
- if (typeof xVal !== "number") return 0;
19473
+ const xVal = resolveAnchorX(chart, refAnnotation);
19411
19474
  if (!chart) return xVal;
19412
- const scale = getXScale(chart, refAnnotation?.annotationAxis);
19475
+ const scale = resolveXScale(chart, refAnnotation);
19413
19476
  if (!scale) return xVal;
19414
19477
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
19415
- const side = index % 2 === 0 ? 1 : -1;
19478
+ const side = getDefaultXDirection(refAnnotation, index);
19416
19479
  const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
19480
+ if (!isNumber$1(xVal)) return xVal;
19417
19481
  const basePx = scale.getPixelForValue(xVal);
19418
19482
  const cxBase = basePx + dx;
19419
19483
  const area = chart.chartArea;
19420
- const el = ctx?.element;
19421
- if (!area || !el) return scale.getValueForPixel(cxBase);
19484
+ if (!area) return scale.getValueForPixel(cxBase);
19422
19485
  const margin = calloutCfg?.margin ?? 8;
19423
- const halfW = (el?.width ?? 0) / 2;
19486
+ const halfW = 160 / 2;
19424
19487
  let cx = cxBase;
19425
19488
  let adjDx = dx;
19426
19489
  if (cx - halfW < area.left + margin) {
@@ -19434,23 +19497,41 @@ const resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
19434
19497
  };
19435
19498
  const resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
19436
19499
  const chart = ctx?.chart;
19437
- const yVal = refAnnotation.yValue;
19438
- if (typeof yVal !== "number") return 0;
19500
+ const yVal = resolveAnchorY(chart, refAnnotation);
19439
19501
  if (!chart) return yVal;
19440
- const scale = getYScale(chart, refAnnotation?.annotationAxis);
19502
+ const scale = resolveYScale(chart, refAnnotation);
19441
19503
  if (!scale) return yVal;
19442
19504
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
19443
- const row = Math.floor(index / 2);
19505
+ const layoutIndex = typeof calloutCfg?.layoutIndex === "number" ? calloutCfg.layoutIndex : index;
19506
+ const row = Math.floor(layoutIndex / 2);
19444
19507
  const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
19508
+ if (!isNumber$1(yVal)) return yVal;
19445
19509
  const basePx = scale.getPixelForValue(yVal);
19446
- const cyBase = basePx + dy;
19510
+ const laneBaseOffset = -24;
19511
+ let cyBase = !hasCustomOffset ? basePx + laneBaseOffset : basePx + dy;
19447
19512
  const area = chart.chartArea;
19448
- const el = ctx?.element;
19449
- if (!area || !el) return scale.getValueForPixel(cyBase);
19513
+ if (!area) return scale.getValueForPixel(cyBase);
19450
19514
  const margin = calloutCfg?.margin ?? 8;
19451
- const halfH = (el?.height ?? 0) / 2;
19515
+ const halfH = 18 / 2;
19516
+ const minGapPx = calloutCfg?.minGapPx ?? halfH * 2 + 4;
19452
19517
  let cy = cyBase;
19453
- let adjDy = dy;
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
+ }
19454
19535
  if (cy - halfH < area.top + margin) {
19455
19536
  adjDy += area.top + margin + halfH - cy;
19456
19537
  cy = basePx + adjDy;
@@ -19465,16 +19546,174 @@ const isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
19465
19546
  const chart = ctx?.chart;
19466
19547
  const area = chart?.chartArea;
19467
19548
  if (!chart || !area) return true;
19468
- const xScale = getXScale(chart, refAnnotation.annotationAxis);
19469
- const yScale = getYScale(chart, refAnnotation.annotationAxis);
19470
- const xVal = refAnnotation.xValue;
19471
- const yVal = refAnnotation.yValue;
19549
+ const xScale = resolveXScale(chart, refAnnotation);
19550
+ const yScale = resolveYScale(chart, refAnnotation);
19551
+ const xVal = resolveAnchorX(chart, refAnnotation);
19552
+ const yVal = resolveAnchorY(chart, refAnnotation);
19472
19553
  if (!xScale || !yScale) return false;
19473
- if (typeof xVal !== "number" || typeof yVal !== "number") return false;
19554
+ if (!isNumber$1(xVal) || !isNumber$1(yVal)) return false;
19474
19555
  const x = xScale.getPixelForValue(xVal);
19475
19556
  const y = yScale.getPixelForValue(yVal);
19476
19557
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
19477
19558
  };
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 && typeof p.x === "number" && typeof p.y === "number");
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
+ if (Math.abs(a.y - b.y) <= yThreshold && Math.abs(a.x - b.x) <= xThreshold) {
19619
+ overlapSet.add(a.idx);
19620
+ overlapSet.add(b.idx);
19621
+ }
19622
+ }
19623
+ return overlapSet;
19624
+ };
19625
+ const buildOverlapCalloutYMap = (annotationsData, overlapSet) => {
19626
+ const points = annotationsData.map((ann, idx) => ({
19627
+ idx,
19628
+ side: getLabelSide(ann),
19629
+ y: getAnchorValueFromData(ann, "y")
19630
+ })).filter((p) => overlapSet.has(p.idx) && typeof p.y === "number");
19631
+ const yMap = /* @__PURE__ */ new Map();
19632
+ if (!points.length) return yMap;
19633
+ const ys = points.map((p) => p.y);
19634
+ const minY = Math.min(...ys);
19635
+ const maxY = Math.max(...ys);
19636
+ const yRange = Math.max(maxY - minY, 1);
19637
+ const minGap = Math.max(130, yRange * .03);
19638
+ const minBound = minY - minGap * .5;
19639
+ const maxBound = maxY + minGap * .5;
19640
+ const groups = {
19641
+ left: [],
19642
+ right: []
19643
+ };
19644
+ points.forEach((p) => {
19645
+ if (p.side === "right") {
19646
+ groups.right.push({
19647
+ idx: p.idx,
19648
+ y: p.y
19649
+ });
19650
+ return;
19651
+ }
19652
+ groups.left.push({
19653
+ idx: p.idx,
19654
+ y: p.y
19655
+ });
19656
+ });
19657
+ Object.keys(groups).forEach((side) => {
19658
+ const group = groups[side].sort((a, b) => a.y - b.y);
19659
+ if (!group.length) return;
19660
+ const packed = group.map((item, i) => {
19661
+ return i === 0 ? item.y : Math.max(item.y, group[i - 1].y + minGap);
19662
+ });
19663
+ const overflowBottom = packed[packed.length - 1] - maxBound;
19664
+ if (overflowBottom > 0) for (let i = 0; i < packed.length; i += 1) packed[i] -= overflowBottom;
19665
+ const overflowTop = minBound - packed[0];
19666
+ if (overflowTop > 0) for (let i = 0; i < packed.length; i += 1) packed[i] += overflowTop;
19667
+ group.forEach((item, i) => {
19668
+ yMap.set(item.idx, packed[i]);
19669
+ });
19670
+ });
19671
+ return yMap;
19672
+ };
19673
+ const getOverlapCalloutXAdjust = (ann) => {
19674
+ const side = getLabelSide(ann);
19675
+ if (side === "right") return -56;
19676
+ if (side === "left") return 56;
19677
+ return 0;
19678
+ };
19679
+ const hasOverlappingLabel = (ctx, refAnnotation, index, annotationsData) => {
19680
+ const chart = getChartFromCtx(ctx);
19681
+ if (!chart) return false;
19682
+ const yScale = getScaleByAxis(chart, "y", refAnnotation);
19683
+ const xScale = getScaleByAxis(chart, "x", refAnnotation);
19684
+ if (!yScale || !xScale) return false;
19685
+ const area = chart.chartArea;
19686
+ if (!area) return false;
19687
+ const yVal = getAnchorValueFromData(refAnnotation, "y");
19688
+ const xVal = getAnchorValueFromData(refAnnotation, "x");
19689
+ if (!isFiniteNumber$1(yVal) || !isFiniteNumber$1(xVal)) return false;
19690
+ const currentYPx = yScale.getPixelForValue(yVal);
19691
+ const currentXPx = xScale.getPixelForValue(xVal);
19692
+ const currentSide = getLabelSide(refAnnotation);
19693
+ const currentFont = refAnnotation?.labelConfig?.font;
19694
+ const currentHeight = parseFontSize(currentFont) + 6;
19695
+ const nearTop = currentYPx - currentHeight <= area.top + 2;
19696
+ const nearBottom = currentYPx + currentHeight >= area.bottom - 2;
19697
+ if (nearTop || nearBottom) return true;
19698
+ for (let i = 0; i < annotationsData.length; i += 1) {
19699
+ if (i === index) continue;
19700
+ const ann = annotationsData[i];
19701
+ if (!ann?.label || ann?.display === false) continue;
19702
+ const otherYScale = getScaleByAxis(chart, "y", ann);
19703
+ const otherXScale = getScaleByAxis(chart, "x", ann);
19704
+ if (!otherYScale || !otherXScale) continue;
19705
+ const otherYVal = getAnchorValueFromData(ann, "y");
19706
+ const otherXVal = getAnchorValueFromData(ann, "x");
19707
+ if (!isFiniteNumber$1(otherYVal) || !isFiniteNumber$1(otherXVal)) continue;
19708
+ const otherYPx = otherYScale.getPixelForValue(otherYVal);
19709
+ const otherXPx = otherXScale.getPixelForValue(otherXVal);
19710
+ const otherSide = getLabelSide(ann);
19711
+ const otherFont = ann?.labelConfig?.font;
19712
+ const yThreshold = (currentHeight + (parseFontSize(otherFont) + 6)) / 2;
19713
+ if ((currentSide === "center" || otherSide === "center" || currentSide === otherSide) && Math.abs(currentYPx - otherYPx) < yThreshold && Math.abs(currentXPx - otherXPx) < 160) return true;
19714
+ }
19715
+ return false;
19716
+ };
19478
19717
  var STORAGE_PREFIX = "chart_annotations";
19479
19718
  var getStorageKey = (persistenceId) => {
19480
19719
  return `${STORAGE_PREFIX}:${persistenceId}`;
@@ -19509,12 +19748,9 @@ const getAnnotationPosition = (persistenceId, annotationId) => {
19509
19748
  if (!persistenceId) return void 0;
19510
19749
  return readStorage(persistenceId)[annotationId];
19511
19750
  };
19512
- const getChartFromCtx = (ctx) => {
19513
- if (!ctx) return;
19514
- if (ctx.chart?.persistenceId !== void 0) return ctx.chart;
19515
- if (ctx.chart?.chart?.persistenceId !== void 0) return ctx.chart.chart;
19516
- if (ctx.$context?.chart?.persistenceId !== void 0) return ctx.$context.chart;
19517
- return ctx.chart?.chart ?? ctx.chart ?? ctx.$context?.chart;
19751
+ var isDragActive = (chart, annotation) => {
19752
+ const pluginEnabled = Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled);
19753
+ return Boolean(annotation?.enableDrag && pluginEnabled);
19518
19754
  };
19519
19755
  var mapToBoxLikeLabelPosition = (position) => {
19520
19756
  switch (position) {
@@ -19554,7 +19790,8 @@ var mapToBoxLikeLabelPosition = (position) => {
19554
19790
  }
19555
19791
  };
19556
19792
  var handleLineEnter = (element, chart, annotation) => {
19557
- chart.canvas.style.cursor = CursorStyle.Pointer;
19793
+ const canDrag = isDragActive(chart, annotation);
19794
+ chart.canvas.style.cursor = canDrag ? CursorStyle.Pointer : CursorStyle.Initial;
19558
19795
  if (element.options.scaleID?.includes(AxisType.X)) {
19559
19796
  if (element.options.label) element.options.label.xAdjust = chart.chartArea.left;
19560
19797
  }
@@ -19563,7 +19800,7 @@ var handleLineEnter = (element, chart, annotation) => {
19563
19800
  element.label.options.display = true;
19564
19801
  element.options.label.enabled = true;
19565
19802
  }
19566
- if (annotation?.enableDrag) {
19803
+ if (canDrag) {
19567
19804
  chart.hoveredAnnotationId = element.options.id || null;
19568
19805
  chart.update("none");
19569
19806
  }
@@ -19577,14 +19814,14 @@ var handleLineLeave = (element, chart, annotation) => {
19577
19814
  element.label.options.display = false;
19578
19815
  element.options.label.enabled = false;
19579
19816
  }
19580
- if (annotation?.enableDrag) {
19817
+ if (isDragActive(chart, annotation)) {
19581
19818
  chart.hoveredAnnotationId = null;
19582
19819
  chart.update("none");
19583
19820
  }
19584
19821
  chart.draw();
19585
19822
  };
19586
19823
  var handleBoxEnter = (element, chart, annotation) => {
19587
- if (annotation?.enableDrag) {
19824
+ if (isDragActive(chart, annotation)) {
19588
19825
  chart.hoveredAnnotationId = element.options.id || null;
19589
19826
  chart.update("none");
19590
19827
  element.options.borderWidth = BORDER_WIDTH.HOVERED;
@@ -19593,7 +19830,7 @@ var handleBoxEnter = (element, chart, annotation) => {
19593
19830
  chart.draw();
19594
19831
  };
19595
19832
  var handleBoxLeave = (element, chart, annotation) => {
19596
- if (annotation?.enableDrag) {
19833
+ if (isDragActive(chart, annotation)) {
19597
19834
  chart.hoveredAnnotationId = null;
19598
19835
  element.options.borderWidth = BORDER_WIDTH.INITIAL;
19599
19836
  chart.canvas.style.cursor = CursorStyle.Initial;
@@ -19602,7 +19839,7 @@ var handleBoxLeave = (element, chart, annotation) => {
19602
19839
  chart.draw();
19603
19840
  };
19604
19841
  var handlePointEnter = (element, chart, annotation) => {
19605
- if (annotation?.enableDrag) {
19842
+ if (isDragActive(chart, annotation)) {
19606
19843
  chart.hoveredAnnotationId = element.options.id || null;
19607
19844
  chart.update("none");
19608
19845
  element.options.borderWidth = BORDER_WIDTH.POINT_HOVERED;
@@ -19611,7 +19848,7 @@ var handlePointEnter = (element, chart, annotation) => {
19611
19848
  chart.draw();
19612
19849
  };
19613
19850
  var handlePointLeave = (element, chart, annotation) => {
19614
- if (annotation?.enableDrag) {
19851
+ if (isDragActive(chart, annotation)) {
19615
19852
  chart.hoveredAnnotationId = null;
19616
19853
  element.options.borderWidth = BORDER_WIDTH.INITIAL;
19617
19854
  chart.canvas.style.cursor = CursorStyle.Initial;
@@ -19620,7 +19857,7 @@ var handlePointLeave = (element, chart, annotation) => {
19620
19857
  chart.draw();
19621
19858
  };
19622
19859
  var handleLabelEnter = (element, chart, annotation) => {
19623
- if (annotation?.enableDrag) {
19860
+ if (isDragActive(chart, annotation)) {
19624
19861
  chart.hoveredAnnotationId = element.options.id || null;
19625
19862
  chart.update("none");
19626
19863
  chart.canvas.style.cursor = CursorStyle.Pointer;
@@ -19628,7 +19865,7 @@ var handleLabelEnter = (element, chart, annotation) => {
19628
19865
  chart.draw();
19629
19866
  };
19630
19867
  var handleLabelLeave = (_element, chart, annotation) => {
19631
- if (annotation?.enableDrag) {
19868
+ if (isDragActive(chart, annotation)) {
19632
19869
  chart.hoveredAnnotationId = null;
19633
19870
  chart.canvas.style.cursor = CursorStyle.Initial;
19634
19871
  chart.update("none");
@@ -19759,25 +19996,41 @@ const generateAnn = (ann, index) => {
19759
19996
  displayDragCoordinates: ann?.displayDragCoordinates ?? true
19760
19997
  };
19761
19998
  };
19762
- var getCalloutAnnotation = (refAnnotation, index) => {
19999
+ var getCalloutAnnotation = (refAnnotation, index, annotationsData, likelyOverlapSet, overlapCalloutYMap, layoutIndex) => {
19763
20000
  const generated = generateAnn(refAnnotation, index);
19764
20001
  if (!generated) return [];
19765
20002
  const { label, ...baseRest } = generated;
19766
20003
  const baseId = baseRest.id;
19767
20004
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
20005
+ const onlyWhenOverlapping = calloutCfg?.onlyWhenOverlapping ?? false;
20006
+ const hideBoxGlyphInOverlapMode = onlyWhenOverlapping && refAnnotation?.type === AnnotationType$1.BOX;
20007
+ const staticShouldShowCallout = likelyOverlapSet.has(index);
20008
+ const shouldShowCallout = (ctx) => {
20009
+ return !onlyWhenOverlapping || staticShouldShowCallout || calloutCfg?.useRuntimeOverlap === true && hasOverlappingLabel(ctx, refAnnotation, index, annotationsData);
20010
+ };
20011
+ const canDisplayCallout = (ctx) => {
20012
+ return (refAnnotation.display ?? true) && shouldShowCallout(ctx) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0);
20013
+ };
19768
20014
  const baseAnnotation = {
19769
20015
  ...baseRest,
19770
- label: { display: false }
20016
+ backgroundColor: hideBoxGlyphInOverlapMode ? TRANSPARENT : baseRest?.backgroundColor,
20017
+ borderWidth: hideBoxGlyphInOverlapMode ? BORDER_WIDTH.ZERO : baseRest?.borderWidth,
20018
+ borderColor: hideBoxGlyphInOverlapMode ? TRANSPARENT : baseRest?.borderColor,
20019
+ label: {
20020
+ ...label,
20021
+ display: (ctx) => (refAnnotation?.display ?? true) && (!calloutCfg?.enabled || !canDisplayCallout(ctx))
20022
+ }
19771
20023
  };
19772
20024
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
19773
20025
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
19774
20026
  const borderColor = calloutCfg?.borderColor ?? "transparent";
19775
- const strokeStyle = calloutCfg.strokeStyle ?? baseAnnotation.borderColor;
20027
+ const connectorStrokeStyle = calloutCfg.strokeStyle ?? (hideBoxGlyphInOverlapMode ? refAnnotation?.color ?? "hsl(60, 10.34482759%, 12.5%)" : baseAnnotation.borderColor);
19776
20028
  const lineWidth = typeof calloutCfg.lineWidth === "number" ? calloutCfg.lineWidth : 1;
19777
20029
  const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 6;
19778
20030
  const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : 1;
19779
20031
  const calloutId = `callout-annotation-${index}`;
19780
- return [baseAnnotation, {
20032
+ const isCalloutDraggable = refAnnotation?.enableDrag !== false;
20033
+ const calloutAnnotation = {
19781
20034
  id: calloutId,
19782
20035
  type: "label",
19783
20036
  backgroundColor: "transparent",
@@ -19785,6 +20038,8 @@ var getCalloutAnnotation = (refAnnotation, index) => {
19785
20038
  color: (ctx) => {
19786
20039
  const chart = getChartFromCtx(ctx);
19787
20040
  if (!chart) return color;
20041
+ const isDragModeEnabled = Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled);
20042
+ if (!isCalloutDraggable || !isDragModeEnabled) return color;
19788
20043
  if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
19789
20044
  return color;
19790
20045
  },
@@ -19792,27 +20047,38 @@ var getCalloutAnnotation = (refAnnotation, index) => {
19792
20047
  borderColor,
19793
20048
  borderWidth: BORDER_WIDTH.INITIAL,
19794
20049
  padding: 5,
19795
- display: (ctx) => (refAnnotation.display ?? true) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0),
20050
+ display: canDisplayCallout,
19796
20051
  xValue: (ctx) => {
19797
20052
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
19798
20053
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
19799
20054
  const explicit = typeof calloutCfg.xValue === "number" ? calloutCfg.xValue : void 0;
19800
- return stored?.x ?? explicit ?? resolveCalloutXValue(ctx, refAnnotation, calloutCfg, index);
20055
+ const overlapCalloutAdjust = onlyWhenOverlapping && !calloutCfg?.xAdjust ? getOverlapCalloutXAdjust(refAnnotation) : calloutCfg?.xAdjust;
20056
+ return stored?.x ?? explicit ?? resolveCalloutXValue(ctx, refAnnotation, {
20057
+ ...calloutCfg,
20058
+ xAdjust: overlapCalloutAdjust,
20059
+ layoutIndex
20060
+ }, index);
19801
20061
  },
19802
20062
  yValue: (ctx) => {
19803
20063
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
19804
20064
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
19805
20065
  const explicit = typeof calloutCfg.yValue === "number" ? calloutCfg.yValue : void 0;
19806
- return stored?.y ?? explicit ?? resolveCalloutYValue(ctx, refAnnotation, calloutCfg, index);
20066
+ const overlapPackedY = onlyWhenOverlapping ? overlapCalloutYMap.get(index) : void 0;
20067
+ const overlapCalloutYAdjust = onlyWhenOverlapping && !calloutCfg?.yAdjust ? 0 : calloutCfg?.yAdjust;
20068
+ return stored?.y ?? explicit ?? overlapPackedY ?? resolveCalloutYValue(ctx, refAnnotation, {
20069
+ ...calloutCfg,
20070
+ yAdjust: overlapCalloutYAdjust,
20071
+ layoutIndex
20072
+ }, index);
19807
20073
  },
19808
20074
  resizable: false,
19809
20075
  displayDragCoordinates: false,
19810
- enableDrag: true,
20076
+ enableDrag: isCalloutDraggable,
19811
20077
  enter: ({ element }, { chart }) => {
19812
- return handleLabelEnter(element, chart, { enableDrag: true });
20078
+ return handleLabelEnter(element, chart, { enableDrag: isCalloutDraggable });
19813
20079
  },
19814
20080
  leave: ({ element }, { chart }) => {
19815
- return handleLabelLeave(element, chart, { enableDrag: true });
20081
+ return handleLabelLeave(element, chart, { enableDrag: isCalloutDraggable });
19816
20082
  },
19817
20083
  onDragStart: calloutCfg?.onDragStart ? () => (coords) => {
19818
20084
  return calloutCfg.onDragStart?.(coords, refAnnotation);
@@ -19826,16 +20092,58 @@ var getCalloutAnnotation = (refAnnotation, index) => {
19826
20092
  calloutConnector: {
19827
20093
  enabled: true,
19828
20094
  fromId: baseId,
19829
- strokeStyle,
20095
+ strokeStyle: connectorStrokeStyle,
19830
20096
  lineWidth,
19831
20097
  startOffset,
19832
20098
  endOffset
19833
20099
  }
19834
- }];
20100
+ };
20101
+ const anchorX = getAnchorValueFromData(refAnnotation, "x");
20102
+ const anchorY = getAnchorValueFromData(refAnnotation, "y");
20103
+ const originTickAnnotation = typeof anchorX === "number" && typeof anchorY === "number" ? {
20104
+ id: `callout-origin-tick-${index}`,
20105
+ type: "line",
20106
+ xMin: anchorX,
20107
+ xMax: (ctx) => {
20108
+ const chart = getChartFromCtx(ctx);
20109
+ const xScale = chart ? getScaleByAxis(chart, "x", refAnnotation) : null;
20110
+ if (!xScale) return anchorX;
20111
+ const px = xScale.getPixelForValue(anchorX);
20112
+ const tickLength = calloutCfg?.originTickLengthPx ?? 8;
20113
+ return xScale.getValueForPixel(px + tickLength);
20114
+ },
20115
+ yMin: anchorY,
20116
+ yMax: anchorY,
20117
+ borderColor: calloutCfg?.originTickColor ?? connectorStrokeStyle,
20118
+ borderWidth: calloutCfg?.originTickWidth ?? 1,
20119
+ borderDash: [],
20120
+ adjustScaleRange: false,
20121
+ label: {
20122
+ display: false,
20123
+ content: ""
20124
+ },
20125
+ display: (ctx) => onlyWhenOverlapping && (refAnnotation.display ?? true) && shouldShowCallout(ctx),
20126
+ resizable: false,
20127
+ displayDragCoordinates: false,
20128
+ enableDrag: false
20129
+ } : null;
20130
+ if (!onlyWhenOverlapping) return [baseAnnotation, calloutAnnotation];
20131
+ return originTickAnnotation ? [
20132
+ baseAnnotation,
20133
+ originTickAnnotation,
20134
+ calloutAnnotation
20135
+ ] : [baseAnnotation, calloutAnnotation];
19835
20136
  };
19836
20137
  var generateAnnotations = (annotationsData) => {
20138
+ const likelyOverlapSet = buildLikelyOverlapSet(annotationsData);
20139
+ const overlapCalloutYMap = buildOverlapCalloutYMap(annotationsData, likelyOverlapSet);
20140
+ let calloutLayoutIndex = 0;
19837
20141
  return annotationsData?.flatMap((ann, idx) => {
19838
- if (ann?.labelConfig?.callout?.enabled ?? false) return getCalloutAnnotation(ann, idx);
20142
+ if (ann?.labelConfig?.callout?.enabled ?? false) {
20143
+ const annotation = getCalloutAnnotation(ann, idx, annotationsData, likelyOverlapSet, overlapCalloutYMap, calloutLayoutIndex);
20144
+ calloutLayoutIndex += 1;
20145
+ return annotation;
20146
+ }
19839
20147
  return generateAnn(ann, idx);
19840
20148
  });
19841
20149
  };