@oliasoft-open-source/charts-library 5.10.0-beta-3 → 5.10.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 (3) hide show
  1. package/dist/index.d.ts +66 -97
  2. package/dist/index.js +848 -876
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14364,7 +14364,7 @@ function isZoomingOrPanning(chart) {
14364
14364
  const state = getState(chart);
14365
14365
  return state.panning || state.dragging;
14366
14366
  }
14367
- var clamp$2 = (x, from, to) => Math.min(to, Math.max(from, x));
14367
+ var clamp$3 = (x, from, to) => Math.min(to, Math.max(from, x));
14368
14368
  function removeHandler(chart, type) {
14369
14369
  const { handlers } = getState(chart);
14370
14370
  const handler = handlers[type];
@@ -14450,8 +14450,8 @@ function applyAspectRatio({ begin, end }, aspectRatio) {
14450
14450
  end.y = begin.y + height;
14451
14451
  }
14452
14452
  function applyMinMaxProps(rect, chartArea, points, { min, max, prop }) {
14453
- rect[min] = clamp$2(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
14454
- rect[max] = clamp$2(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
14453
+ rect[min] = clamp$3(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
14454
+ rect[max] = clamp$3(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
14455
14455
  }
14456
14456
  function getRelativePoints(chart, pointEvents, maintainAspectRatio) {
14457
14457
  const points = {
@@ -15889,7 +15889,7 @@ var isOlderPart = (act, req) => req > act || act.length > req.length && act.slic
15889
15889
  * @typedef { import('../../types/element').AnnotationElement } AnnotationElement
15890
15890
  */
15891
15891
  var EPSILON = .001;
15892
- var clamp$1 = (x, from, to) => Math.min(to, Math.max(from, x));
15892
+ var clamp$2 = (x, from, to) => Math.min(to, Math.max(from, x));
15893
15893
  /**
15894
15894
  * @param {{value: number, start: number, end: number}} limit
15895
15895
  * @param {number} hitSize
@@ -15903,7 +15903,7 @@ var inLimit = (limit, hitSize) => limit.value >= limit.start - hitSize && limit.
15903
15903
  * @returns {Object}
15904
15904
  */
15905
15905
  function clampAll(obj, from, to) {
15906
- for (const key of Object.keys(obj)) obj[key] = clamp$1(obj[key], from, to);
15906
+ for (const key of Object.keys(obj)) obj[key] = clamp$2(obj[key], from, to);
15907
15907
  return obj;
15908
15908
  }
15909
15909
  /**
@@ -15977,7 +15977,7 @@ function requireVersion(pkg, min, ver, strict = true) {
15977
15977
  }
15978
15978
  var isPercentString = (s) => typeof s === "string" && s.endsWith("%");
15979
15979
  var toPercent = (s) => parseFloat(s) / 100;
15980
- var toPositivePercent = (s) => clamp$1(toPercent(s), 0, 1);
15980
+ var toPositivePercent = (s) => clamp$2(toPercent(s), 0, 1);
15981
15981
  var boxAppering = (x, y) => ({
15982
15982
  x,
15983
15983
  y,
@@ -16472,7 +16472,7 @@ function applyLabelContent(ctx, { x, y }, labels, { fonts, colors }) {
16472
16472
  }
16473
16473
  function getOpacity(value, elementValue) {
16474
16474
  const opacity = isNumber$1(value) ? value : elementValue;
16475
- return isNumber$1(opacity) ? clamp$1(opacity, 0, 1) : 1;
16475
+ return isNumber$1(opacity) ? clamp$2(opacity, 0, 1) : 1;
16476
16476
  }
16477
16477
  var positions = [
16478
16478
  "left",
@@ -17912,7 +17912,7 @@ function calculateTAdjust(lineSize, sizes, label, space) {
17912
17912
  const lineH = lineSize.h * space.dy;
17913
17913
  const x = lineW > 0 && (labelSize.w / 2 + padding.left - space.x) / lineW;
17914
17914
  const y = lineH > 0 && (labelSize.h / 2 + padding.top - space.y) / lineH;
17915
- return clamp$1(Math.max(x, y), 0, .25);
17915
+ return clamp$2(Math.max(x, y), 0, .25);
17916
17916
  }
17917
17917
  function spaceAround(properties, chartArea) {
17918
17918
  const { x, x2, y, y2 } = properties;
@@ -18839,7 +18839,7 @@ var BORDER_WIDTH = {
18839
18839
  var BORDER_COLOR = "rgba(0,0,0,0.1)";
18840
18840
  var ANNOTATION_DASH = [10, 2];
18841
18841
  var DEFAULT_FONT_FAMILY = "\"Roobert\", \"Noto Sans\", sans-serif";
18842
- var DEFAULT_COLOR$1 = "hsl(60, 10.34482759%, 12.5%)";
18842
+ var DEFAULT_COLOR = "hsl(60, 10.34482759%, 12.5%)";
18843
18843
  var LOGARITHMIC_STEPS = [
18844
18844
  1,
18845
18845
  10,
@@ -19024,7 +19024,7 @@ var getChartFileName = (axes) => {
19024
19024
  var setDefaultTheme = () => {
19025
19025
  defaults$1.font.size = 12;
19026
19026
  defaults$1.font.family = DEFAULT_FONT_FAMILY;
19027
- defaults$1.color = DEFAULT_COLOR$1;
19027
+ defaults$1.color = DEFAULT_COLOR;
19028
19028
  defaults$1.borderColor = BORDER_COLOR;
19029
19029
  };
19030
19030
  var isEmptyString = (value) => value === "";
@@ -20190,33 +20190,20 @@ var defaultAnnotations$2 = (annotations) => ({
20190
20190
  controlAnnotation: annotationConfig?.controlAnnotation ?? false,
20191
20191
  enableDragAnnotation: annotationConfig?.enableDragAnnotation ?? false,
20192
20192
  enableCalloutAnnotation: annotationConfig?.enableCalloutAnnotation ?? false,
20193
- lineMarkersAnnotation: {
20194
- enabled: annotationConfig?.lineMarkersAnnotation?.enabled ?? false,
20195
- itemGapPx: annotationConfig?.lineMarkersAnnotation?.itemGapPx ?? 4,
20196
- edgePaddingPx: annotationConfig?.lineMarkersAnnotation?.edgePaddingPx ?? 8,
20197
- horizontalLineLengthPx: annotationConfig?.lineMarkersAnnotation?.horizontalLineLengthPx,
20198
- lineDirection: annotationConfig?.lineMarkersAnnotation?.lineDirection,
20199
- labelCollisionPx: annotationConfig?.lineMarkersAnnotation?.labelCollisionPx ?? 4,
20200
- labelCollisionClusterXPx: annotationConfig?.lineMarkersAnnotation?.labelCollisionClusterXPx ?? 36,
20201
- enableLabelCollisionResolver: annotationConfig?.lineMarkersAnnotation?.enableLabelCollisionResolver ?? true,
20202
- stickToEdge: annotationConfig?.lineMarkersAnnotation?.stickToEdge ?? true,
20203
- stickToGroup: annotationConfig?.lineMarkersAnnotation?.stickToGroup ?? false,
20204
- stickSide: annotationConfig?.lineMarkersAnnotation?.stickSide ?? "right",
20205
- reverse: annotationConfig?.lineMarkersAnnotation?.reverse ?? false,
20206
- xValue: annotationConfig?.lineMarkersAnnotation?.xValue,
20207
- yValue: annotationConfig?.lineMarkersAnnotation?.yValue,
20208
- yStartValue: annotationConfig?.lineMarkersAnnotation?.yStartValue,
20209
- yEndValue: annotationConfig?.lineMarkersAnnotation?.yEndValue,
20210
- xStartValue: annotationConfig?.lineMarkersAnnotation?.xStartValue,
20211
- xEndValue: annotationConfig?.lineMarkersAnnotation?.xEndValue,
20212
- lengthPx: annotationConfig?.lineMarkersAnnotation?.lengthPx,
20213
- color: annotationConfig?.lineMarkersAnnotation?.color ?? "rgba(20,20,20,0.9)",
20214
- opacity: annotationConfig?.lineMarkersAnnotation?.opacity ?? 1,
20215
- lineWidth: annotationConfig?.lineMarkersAnnotation?.lineWidth ?? 1,
20216
- lineDash: annotationConfig?.lineMarkersAnnotation?.lineDash ?? [],
20217
- startTick: annotationConfig?.lineMarkersAnnotation?.startTick,
20218
- endTick: annotationConfig?.lineMarkersAnnotation?.endTick,
20219
- items: annotationConfig?.lineMarkersAnnotation?.items ?? []
20193
+ verticalMarkersAnnotation: {
20194
+ enabled: annotationConfig?.verticalMarkersAnnotation?.enabled ?? false,
20195
+ itemGapPx: annotationConfig?.verticalMarkersAnnotation?.itemGapPx ?? 4,
20196
+ edgePaddingPx: annotationConfig?.verticalMarkersAnnotation?.edgePaddingPx ?? 8,
20197
+ stickToEdge: annotationConfig?.verticalMarkersAnnotation?.stickToEdge ?? true,
20198
+ stickSide: annotationConfig?.verticalMarkersAnnotation?.stickSide ?? "right",
20199
+ reverse: annotationConfig?.verticalMarkersAnnotation?.reverse ?? false,
20200
+ xValue: annotationConfig?.verticalMarkersAnnotation?.xValue,
20201
+ yStartValue: annotationConfig?.verticalMarkersAnnotation?.yStartValue,
20202
+ color: annotationConfig?.verticalMarkersAnnotation?.color ?? "rgba(20,20,20,0.9)",
20203
+ opacity: annotationConfig?.verticalMarkersAnnotation?.opacity ?? 1,
20204
+ lineWidth: annotationConfig?.verticalMarkersAnnotation?.lineWidth ?? 1,
20205
+ lineDash: annotationConfig?.verticalMarkersAnnotation?.lineDash ?? [],
20206
+ items: annotationConfig?.verticalMarkersAnnotation?.items ?? []
20220
20207
  },
20221
20208
  annotationsData: defaultAnnotationsData$2(Array.isArray(annotations) ? annotations : annotations?.annotationsData)
20222
20209
  };
@@ -20892,87 +20879,590 @@ function addTransparency(input, alpha, out = "rgb") {
20892
20879
  }
20893
20880
  //#endregion
20894
20881
  //#region src/components/common/helpers/callout-helpers/callout-helpers.ts
20882
+ var DEFAULT_FONT_SIZE = 12;
20883
+ var DEFAULT_PADDING = 5;
20884
+ var DEFAULT_MARGIN = 8;
20885
+ var DEFAULT_LABEL_GAP = 8;
20886
+ var DEFAULT_MIN_CONNECTOR_LENGTH = 14;
20887
+ var DEFAULT_HORIZONTAL_OFFSET = 18;
20888
+ var DEFAULT_VERTICAL_OFFSET = -18;
20889
+ var DEFAULT_ROW_GAP = 22;
20890
+ var DEFAULT_ITEM_GAP = 8;
20891
+ var DEFAULT_OVERLAP_ONLY_ITEM_GAP = 14;
20892
+ var DEFAULT_LANE_GAP = 12;
20893
+ var clamp$1 = (value, min, max) => {
20894
+ if (Number.isNaN(value)) return min;
20895
+ if (min > max) return min;
20896
+ return Math.min(Math.max(value, min), max);
20897
+ };
20898
+ var clampRelaxed = (value, min, max) => {
20899
+ if (min <= max) return clamp$1(value, min, max);
20900
+ if (value < max) return max;
20901
+ if (value > min) return min;
20902
+ return value;
20903
+ };
20904
+ var getNumericValue = (value, fallback = 0) => {
20905
+ return typeof value === "number" ? value : fallback;
20906
+ };
20907
+ var getCalloutCfg = (annotation) => {
20908
+ return annotation?.labelConfig?.callout ?? {};
20909
+ };
20910
+ var getChartFromCtx$1 = (ctx) => {
20911
+ return ctx?.chart?.chart ?? ctx?.chart;
20912
+ };
20895
20913
  var getXScale = (chart, axisId) => {
20896
20914
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20897
20915
  if (chart?.scales?.x) return chart.scales.x;
20898
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("x"));
20916
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20917
+ return item?.toLowerCase()?.includes("x");
20918
+ });
20899
20919
  return key ? chart?.scales?.[key] : void 0;
20900
20920
  };
20901
20921
  var getYScale = (chart, axisId) => {
20902
20922
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20903
20923
  if (chart?.scales?.y) return chart.scales.y;
20904
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
20924
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20925
+ return item?.toLowerCase()?.includes("y");
20926
+ });
20905
20927
  return key ? chart?.scales?.[key] : void 0;
20906
20928
  };
20929
+ var resolveFontSize = (font) => {
20930
+ if (typeof font === "string") {
20931
+ const match = font.match(/(\d+(?:\.\d+)?)px/);
20932
+ return match ? Number(match[1]) : DEFAULT_FONT_SIZE;
20933
+ }
20934
+ if (font && typeof font === "object" && "size" in font) {
20935
+ const size = Number(font.size);
20936
+ return Number.isFinite(size) ? size : DEFAULT_FONT_SIZE;
20937
+ }
20938
+ return DEFAULT_FONT_SIZE;
20939
+ };
20940
+ var resolvePadding = (padding) => {
20941
+ if (typeof padding === "number") return {
20942
+ top: padding,
20943
+ right: padding,
20944
+ bottom: padding,
20945
+ left: padding
20946
+ };
20947
+ if (padding && typeof padding === "object") {
20948
+ const typedPadding = padding;
20949
+ return {
20950
+ top: Number(typedPadding.top ?? DEFAULT_PADDING),
20951
+ right: Number(typedPadding.right ?? DEFAULT_PADDING),
20952
+ bottom: Number(typedPadding.bottom ?? DEFAULT_PADDING),
20953
+ left: Number(typedPadding.left ?? DEFAULT_PADDING)
20954
+ };
20955
+ }
20956
+ return {
20957
+ top: DEFAULT_PADDING,
20958
+ right: DEFAULT_PADDING,
20959
+ bottom: DEFAULT_PADDING,
20960
+ left: DEFAULT_PADDING
20961
+ };
20962
+ };
20963
+ var estimateLabelSize = (chart, annotation, calloutCfg) => {
20964
+ const content = `${annotation?.label ?? ""}`;
20965
+ const fontSize = resolveFontSize(calloutCfg?.font);
20966
+ const padding = resolvePadding(calloutCfg?.padding);
20967
+ const lines = content.split("\n");
20968
+ const fallbackWidth = Math.max(...lines.map((line) => line.length), 0) * fontSize * .62;
20969
+ let measuredWidth = fallbackWidth;
20970
+ if (chart?.ctx) {
20971
+ chart.ctx.save();
20972
+ chart.ctx.font = typeof calloutCfg?.font === "string" ? calloutCfg.font : `${fontSize}px sans-serif`;
20973
+ measuredWidth = Math.max(...lines.map((line) => chart.ctx.measureText(line).width), fallbackWidth);
20974
+ chart.ctx.restore();
20975
+ }
20976
+ return {
20977
+ width: measuredWidth + padding.left + padding.right,
20978
+ height: lines.length * fontSize + padding.top + padding.bottom
20979
+ };
20980
+ };
20981
+ var getAxisId = (annotation, axis) => {
20982
+ const axisScaleId = axis === "x" ? annotation?.xScaleID : annotation?.yScaleID;
20983
+ if (axisScaleId) return axisScaleId;
20984
+ if (annotation?.annotationAxis?.startsWith(axis)) return annotation.annotationAxis;
20985
+ };
20986
+ var getAnchorPixel = (chart, annotation, axis, area) => {
20987
+ const scale = axis === "x" ? getXScale(chart, getAxisId(annotation, "x")) : getYScale(chart, getAxisId(annotation, "y"));
20988
+ const directValue = axis === "x" ? annotation?.xValue : annotation?.yValue;
20989
+ if (typeof directValue === "number" && scale) return scale.getPixelForValue(directValue);
20990
+ const minKey = axis === "x" ? "xMin" : "yMin";
20991
+ const maxKey = axis === "x" ? "xMax" : "yMax";
20992
+ const minValue = annotation?.[minKey];
20993
+ const maxValue = annotation?.[maxKey];
20994
+ if (typeof minValue === "number" && typeof maxValue === "number" && scale) return (scale.getPixelForValue(minValue) + scale.getPixelForValue(maxValue)) / 2;
20995
+ if (typeof minValue === "number" && scale) return scale.getPixelForValue(minValue);
20996
+ if (typeof maxValue === "number" && scale) return scale.getPixelForValue(maxValue);
20997
+ return axis === "x" ? (area.left + area.right) / 2 : (area.top + area.bottom) / 2;
20998
+ };
20999
+ var hasAxisAnchor = (annotation, axis) => {
21000
+ 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";
21001
+ return typeof annotation?.yValue === "number" || typeof annotation?.yMin === "number" || typeof annotation?.yMax === "number" || annotation?.type === "box" || annotation?.type === "label" || annotation?.type === "ellipse";
21002
+ };
21003
+ var getBoxHalfWidth = (chart, area, annotation) => {
21004
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21005
+ if (xScale && typeof annotation?.xMin === "number" && typeof annotation?.xMax === "number") return Math.abs(xScale.getPixelForValue(annotation.xMax) - xScale.getPixelForValue(annotation.xMin)) / 2;
21006
+ if (annotation?.type === "box") return (area.right - area.left) / 2;
21007
+ return annotation?.radius ?? 0;
21008
+ };
21009
+ var getBoxHalfHeight = (chart, annotation) => {
21010
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21011
+ if (yScale && typeof annotation?.yMin === "number" && typeof annotation?.yMax === "number") return Math.abs(yScale.getPixelForValue(annotation.yMax) - yScale.getPixelForValue(annotation.yMin)) / 2;
21012
+ return annotation?.radius ?? 0;
21013
+ };
21014
+ var getReferencePoint$1 = (chart, area, annotation) => {
21015
+ const anchorX = getAnchorPixel(chart, annotation, "x", area);
21016
+ const anchorY = getAnchorPixel(chart, annotation, "y", area);
21017
+ const halfWidth = getBoxHalfWidth(chart, area, annotation);
21018
+ const halfHeight = getBoxHalfHeight(chart, annotation);
21019
+ const labelOffsetPx = getNumericValue(annotation?.labelOffsetPx, 14);
21020
+ const xAdjust = getNumericValue(annotation?.labelConfig?.xAdjust, 0);
21021
+ const yAdjust = getNumericValue(annotation?.labelConfig?.yAdjust, 0);
21022
+ const position = annotation?.labelConfig?.position;
21023
+ let x = anchorX;
21024
+ let y = anchorY;
21025
+ let side = anchorX >= (area.left + area.right) / 2 ? "right" : "left";
21026
+ switch (position) {
21027
+ case Position.Left:
21028
+ x -= Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21029
+ side = "left";
21030
+ break;
21031
+ case Position.Right:
21032
+ x += Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21033
+ side = "right";
21034
+ break;
21035
+ case Position.TopLeft:
21036
+ x -= halfWidth;
21037
+ y -= halfHeight;
21038
+ side = "left";
21039
+ break;
21040
+ case Position.TopRight:
21041
+ x += halfWidth;
21042
+ y -= halfHeight;
21043
+ side = "right";
21044
+ break;
21045
+ case Position.BottomLeft:
21046
+ x -= halfWidth;
21047
+ y += halfHeight;
21048
+ side = "left";
21049
+ break;
21050
+ case Position.BottomRight:
21051
+ x += halfWidth;
21052
+ y += halfHeight;
21053
+ side = "right";
21054
+ break;
21055
+ case Position.Bottom:
21056
+ y += Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21057
+ break;
21058
+ case Position.Top:
21059
+ default:
21060
+ y -= Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21061
+ break;
21062
+ }
21063
+ return {
21064
+ x: x + xAdjust,
21065
+ y: y + yAdjust,
21066
+ side
21067
+ };
21068
+ };
21069
+ var getOriginalLabelRect = (chart, area, annotation, index) => {
21070
+ const size = estimateLabelSize(chart, annotation, getCalloutCfg(annotation));
21071
+ const reference = getReferencePoint$1(chart, area, annotation);
21072
+ const direction = reference.side === "right" ? 1 : -1;
21073
+ const centerX = reference.x + direction * (size.width / 2);
21074
+ const centerY = reference.y;
21075
+ return {
21076
+ id: `callout-annotation-${index}`,
21077
+ side: reference.side,
21078
+ left: centerX - size.width / 2,
21079
+ right: centerX + size.width / 2,
21080
+ top: centerY - size.height / 2,
21081
+ bottom: centerY + size.height / 2
21082
+ };
21083
+ };
21084
+ var isRectOverlapping = (left, right) => {
21085
+ return !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
21086
+ };
21087
+ var getOverlappingCalloutIds = (chart) => {
21088
+ const area = chart?.chartArea;
21089
+ const annotationsData = (chart?.options)?.annotations?.annotationsData ?? [];
21090
+ if (!chart || !area) return /* @__PURE__ */ new Set();
21091
+ const rects = annotationsData.flatMap((annotation, index) => {
21092
+ const calloutCfg = getCalloutCfg(annotation);
21093
+ if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
21094
+ return [getOriginalLabelRect(chart, area, annotation, index)];
21095
+ });
21096
+ const overlappingIds = /* @__PURE__ */ new Set();
21097
+ rects.forEach((rect, index) => {
21098
+ rects.slice(index + 1).forEach((otherRect) => {
21099
+ if (rect.side !== otherRect.side) return;
21100
+ if (!isRectOverlapping(rect, otherRect)) return;
21101
+ overlappingIds.add(rect.id);
21102
+ overlappingIds.add(otherRect.id);
21103
+ });
21104
+ });
21105
+ return overlappingIds;
21106
+ };
21107
+ var isCalloutOverlapping = (ctx, refAnnotation, index) => {
21108
+ const chart = getChartFromCtx$1(ctx);
21109
+ if (!getCalloutCfg(refAnnotation)?.onlyWhenOverlapping) return true;
21110
+ if (!chart?.chartArea) return false;
21111
+ return getOverlappingCalloutIds(chart).has(`callout-annotation-${index}`);
21112
+ };
21113
+ var getPreferredX = (chart, area, annotation, calloutCfg, width) => {
21114
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21115
+ const explicitXValue = typeof calloutCfg?.xValue === "number" ? calloutCfg.xValue : void 0;
21116
+ if (typeof explicitXValue === "number" && xScale) return xScale.getPixelForValue(explicitXValue);
21117
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21118
+ const halfWidth = width / 2;
21119
+ const reference = getReferencePoint$1(chart, area, annotation);
21120
+ const direction = reference.side === "right" ? 1 : -1;
21121
+ const gap = getNumericValue(calloutCfg?.gapPx, DEFAULT_LABEL_GAP);
21122
+ const minConnectorLength = getNumericValue(calloutCfg?.minConnectorLengthPx, DEFAULT_MIN_CONNECTOR_LENGTH);
21123
+ return clamp$1(reference.x + direction * (halfWidth + gap + minConnectorLength), area.left + margin + halfWidth, area.right - margin - halfWidth);
21124
+ };
21125
+ var getPreferredY = (chart, area, annotation, calloutCfg) => {
21126
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21127
+ const explicitYValue = typeof calloutCfg?.yValue === "number" ? calloutCfg.yValue : void 0;
21128
+ if (typeof explicitYValue === "number" && yScale) return yScale.getPixelForValue(explicitYValue);
21129
+ const reference = getReferencePoint$1(chart, area, annotation);
21130
+ const dy = typeof calloutCfg?.yAdjust === "number" ? 0 : DEFAULT_VERTICAL_OFFSET;
21131
+ return reference.y + dy;
21132
+ };
21133
+ var getCalloutItems = (chart, area) => {
21134
+ const overlappingIds = getOverlappingCalloutIds(chart);
21135
+ return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
21136
+ const calloutCfg = getCalloutCfg(annotation);
21137
+ const id = `callout-annotation-${index}`;
21138
+ if (!calloutCfg?.enabled) return [];
21139
+ const size = estimateLabelSize(chart, annotation, calloutCfg);
21140
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21141
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21142
+ const reference = getReferencePoint$1(chart, area, annotation);
21143
+ const preferredX = getPreferredX(chart, area, annotation, calloutCfg, size.width);
21144
+ const preferredY = getPreferredY(chart, area, annotation, calloutCfg);
21145
+ const hasStoredX = typeof calloutCfg?.xValue === "number" || !xScale;
21146
+ const hasStoredY = typeof calloutCfg?.yValue === "number" || !yScale;
21147
+ const side = reference.side;
21148
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21149
+ return [{
21150
+ annotation,
21151
+ calloutCfg,
21152
+ id,
21153
+ index,
21154
+ anchorY: reference.y,
21155
+ preferredX,
21156
+ preferredY,
21157
+ width: size.width,
21158
+ height: size.height,
21159
+ isFixed: hasStoredX && hasStoredY,
21160
+ isOverlapping: overlappingIds.has(id),
21161
+ overlapOnly: Boolean(calloutCfg?.onlyWhenOverlapping),
21162
+ minX: area.left + margin + size.width / 2,
21163
+ maxX: area.right - margin - size.width / 2,
21164
+ minY: area.top + margin + size.height / 2,
21165
+ maxY: area.bottom - margin - size.height / 2,
21166
+ side
21167
+ }];
21168
+ });
21169
+ };
21170
+ var hasVerticalOverlap = (items) => {
21171
+ const sorted = [...items].sort((left, right) => left.preferredY - right.preferredY);
21172
+ for (let index = 1; index < sorted.length; index += 1) {
21173
+ const previous = sorted[index - 1];
21174
+ const current = sorted[index];
21175
+ const gap = previous.height / 2 + current.height / 2 + DEFAULT_ITEM_GAP;
21176
+ if (current.preferredY - previous.preferredY < gap) return true;
21177
+ }
21178
+ return false;
21179
+ };
21180
+ var overlapsOccupiedRect = (centerY, height, occupiedRects) => {
21181
+ const top = centerY - height / 2;
21182
+ const bottom = centerY + height / 2;
21183
+ return occupiedRects.find((rect) => !(bottom <= rect.top || top >= rect.bottom));
21184
+ };
21185
+ var getLayoutGap = (item) => {
21186
+ return item.overlapOnly ? DEFAULT_OVERLAP_ONLY_ITEM_GAP : DEFAULT_ITEM_GAP;
21187
+ };
21188
+ var getLayoutOrderY = (item) => {
21189
+ return item.isFixed ? item.preferredY : item.anchorY;
21190
+ };
21191
+ var intervalsOverlap = (top, bottom, otherTop, otherBottom) => {
21192
+ return !(bottom <= otherTop || top >= otherBottom);
21193
+ };
21194
+ var assignLabelColumns = (items, layout) => {
21195
+ const columns = [];
21196
+ const hasPrimaryColumn = items.some((item) => {
21197
+ return !item.overlapOnly || !item.isOverlapping;
21198
+ });
21199
+ [...items].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right)).forEach((item) => {
21200
+ const current = layout.get(item.id);
21201
+ if (!current) return;
21202
+ const top = current.yValue - item.height / 2 - getLayoutGap(item) / 2;
21203
+ const bottom = current.yValue + item.height / 2 + getLayoutGap(item) / 2;
21204
+ let columnIndex = hasPrimaryColumn && item.overlapOnly && item.isOverlapping ? 1 : 0;
21205
+ while (true) {
21206
+ if (!(columns[columnIndex] ?? []).some((placed) => {
21207
+ return intervalsOverlap(top, bottom, placed.top, placed.bottom);
21208
+ })) {
21209
+ if (!columns[columnIndex]) columns[columnIndex] = [];
21210
+ columns[columnIndex].push({
21211
+ id: item.id,
21212
+ top,
21213
+ bottom,
21214
+ width: item.width
21215
+ });
21216
+ break;
21217
+ }
21218
+ columnIndex += 1;
21219
+ }
21220
+ });
21221
+ return columns;
21222
+ };
21223
+ var applyLaneLayout = (items, layout, occupiedRects) => {
21224
+ if (items.length <= 1) return;
21225
+ const side = items[0]?.side;
21226
+ const columns = assignLabelColumns(items, layout);
21227
+ const getCurrentRect = (item) => {
21228
+ const centerX = layout.get(item.id)?.xValue ?? item.preferredX;
21229
+ return {
21230
+ left: centerX - item.width / 2,
21231
+ right: centerX + item.width / 2
21232
+ };
21233
+ };
21234
+ const referenceItems = items.filter((item) => {
21235
+ return columns[0]?.some((placed) => placed.id === item.id);
21236
+ });
21237
+ const referenceRects = referenceItems.length ? referenceItems : items;
21238
+ const baseEdge = side === "right" ? occupiedRects.length ? Math.min(...occupiedRects.map((rect) => rect.left)) - DEFAULT_LANE_GAP : Math.min(...referenceRects.map((item) => getCurrentRect(item).left)) : occupiedRects.length ? Math.max(...occupiedRects.map((rect) => rect.right)) + DEFAULT_LANE_GAP : Math.max(...referenceRects.map((item) => getCurrentRect(item).right));
21239
+ columns.forEach((column, columnIndex) => {
21240
+ const columnWidth = Math.max(...column.map((placed) => placed.width));
21241
+ column?.forEach((placed) => {
21242
+ const item = items.find((candidate) => candidate.id === placed.id);
21243
+ const current = layout.get(placed.id);
21244
+ if (!item || !current) return;
21245
+ const xValue = side === "right" ? baseEdge - columnIndex * (columnWidth + DEFAULT_LANE_GAP) - item.width / 2 : baseEdge + columnIndex * (columnWidth + DEFAULT_LANE_GAP) + item.width / 2;
21246
+ layout.set(placed.id, {
21247
+ ...current,
21248
+ xValue: clamp$1(xValue, item.minX, item.maxX)
21249
+ });
21250
+ });
21251
+ });
21252
+ };
21253
+ var getOrderBounds = (item, occupiedRects) => {
21254
+ const previousRect = [...occupiedRects].filter((rect) => rect.centerY <= item.anchorY).at(-1);
21255
+ const nextRect = occupiedRects.find((rect) => rect.centerY >= item.anchorY);
21256
+ const gap = getLayoutGap(item);
21257
+ return {
21258
+ minCenter: previousRect ? previousRect.bottom + gap + item.height / 2 : item.minY,
21259
+ maxCenter: nextRect ? nextRect.top - gap - item.height / 2 : item.maxY
21260
+ };
21261
+ };
21262
+ var layoutSide = (items, occupiedRects = []) => {
21263
+ const sortedOccupiedRects = [...occupiedRects].sort((left, right) => left.centerY - right.centerY);
21264
+ const sorted = [...items].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right));
21265
+ const result = /* @__PURE__ */ new Map();
21266
+ if (!sorted.filter((item) => !item.isFixed).length) {
21267
+ sorted.forEach((item) => {
21268
+ result.set(item.id, {
21269
+ xValue: item.preferredX,
21270
+ yValue: item.preferredY
21271
+ });
21272
+ });
21273
+ return result;
21274
+ }
21275
+ const shouldReflow = sorted.some((item) => !item.overlapOnly) || hasVerticalOverlap(sorted);
21276
+ sorted.forEach((item) => {
21277
+ result.set(item.id, {
21278
+ xValue: item.preferredX,
21279
+ yValue: clamp$1(item.preferredY, item.minY, item.maxY)
21280
+ });
21281
+ });
21282
+ if (!shouldReflow) return result;
21283
+ let previousBottom = Number.NEGATIVE_INFINITY;
21284
+ sorted.forEach((item) => {
21285
+ if (item.isFixed) {
21286
+ const yValue = clamp$1(item.preferredY, item.minY, item.maxY);
21287
+ result.set(item.id, {
21288
+ xValue: item.preferredX,
21289
+ yValue
21290
+ });
21291
+ previousBottom = yValue + item.height / 2;
21292
+ return;
21293
+ }
21294
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21295
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter);
21296
+ const minCenter = Math.max(item.minY, orderBounds.minCenter, previousBottom + item.height / 2 + getLayoutGap(item));
21297
+ let yValue = clampRelaxed(item.preferredY, minCenter, maxCenter);
21298
+ let overlappingRect = overlapsOccupiedRect(yValue, item.height, sortedOccupiedRects);
21299
+ while (overlappingRect) {
21300
+ const nextCandidate = overlappingRect.bottom + getLayoutGap(item) + item.height / 2;
21301
+ if (nextCandidate === yValue) break;
21302
+ yValue = clampRelaxed(nextCandidate, minCenter, maxCenter);
21303
+ overlappingRect = overlapsOccupiedRect(yValue, item.height, sortedOccupiedRects);
21304
+ }
21305
+ result.set(item.id, {
21306
+ xValue: item.preferredX,
21307
+ yValue
21308
+ });
21309
+ previousBottom = yValue + item.height / 2;
21310
+ });
21311
+ let nextTop = Number.POSITIVE_INFINITY;
21312
+ [...sorted].reverse().forEach((item) => {
21313
+ const current = result.get(item.id);
21314
+ if (!current) return;
21315
+ if (item.isFixed) {
21316
+ nextTop = current.yValue - item.height / 2;
21317
+ return;
21318
+ }
21319
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21320
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter, nextTop - item.height / 2 - getLayoutGap(item));
21321
+ const minCenter = Math.max(item.minY, orderBounds.minCenter);
21322
+ const yValue = clampRelaxed(current.yValue, minCenter, maxCenter);
21323
+ result.set(item.id, {
21324
+ ...current,
21325
+ yValue
21326
+ });
21327
+ nextTop = yValue - item.height / 2;
21328
+ });
21329
+ previousBottom = Number.NEGATIVE_INFINITY;
21330
+ sorted.forEach((item) => {
21331
+ const current = result.get(item.id);
21332
+ if (!current) return;
21333
+ if (item.isFixed) {
21334
+ previousBottom = current.yValue + item.height / 2;
21335
+ return;
21336
+ }
21337
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21338
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter);
21339
+ const minCenter = Math.max(item.minY, orderBounds.minCenter, previousBottom + item.height / 2 + getLayoutGap(item));
21340
+ const yValue = clampRelaxed(current.yValue, minCenter, maxCenter);
21341
+ result.set(item.id, {
21342
+ ...current,
21343
+ yValue
21344
+ });
21345
+ previousBottom = yValue + item.height / 2;
21346
+ });
21347
+ if (sorted.some((item) => item.overlapOnly)) {
21348
+ const untouchedPrimary = sorted.filter((item) => {
21349
+ return !item.isFixed && item.overlapOnly && !item.isOverlapping;
21350
+ });
21351
+ const secondaryMovable = sorted.filter((item) => {
21352
+ return !item.isFixed && item.overlapOnly && item.isOverlapping;
21353
+ });
21354
+ untouchedPrimary.forEach((item) => {
21355
+ result.set(item.id, {
21356
+ xValue: item.preferredX,
21357
+ yValue: clamp$1(item.preferredY, item.minY, item.maxY)
21358
+ });
21359
+ });
21360
+ if (secondaryMovable.length) applyLaneLayout(secondaryMovable, result, untouchedPrimary.map((item) => {
21361
+ const current = result.get(item.id);
21362
+ const centerX = current?.xValue ?? item.preferredX;
21363
+ const centerY = current?.yValue ?? item.preferredY;
21364
+ return {
21365
+ top: centerY - item.height / 2,
21366
+ bottom: centerY + item.height / 2,
21367
+ centerY,
21368
+ left: centerX - item.width / 2,
21369
+ right: centerX + item.width / 2
21370
+ };
21371
+ }));
21372
+ return result;
21373
+ }
21374
+ applyLaneLayout(sorted.filter((item) => !item.isFixed), result, []);
21375
+ return result;
21376
+ };
21377
+ var getCalloutLayoutPixels = (chart) => {
21378
+ const area = chart?.chartArea;
21379
+ if (!chart || !area) return /* @__PURE__ */ new Map();
21380
+ const grouped = getCalloutItems(chart, area).map((item) => {
21381
+ const xValue = clamp$1(item.preferredX, item.minX, item.maxX);
21382
+ return {
21383
+ ...item,
21384
+ preferredX: xValue
21385
+ };
21386
+ }).reduce((accumulator, item) => {
21387
+ accumulator[item.side].push(item);
21388
+ return accumulator;
21389
+ }, {
21390
+ left: [],
21391
+ right: []
21392
+ });
21393
+ const leftLayout = layoutSide(grouped.left, []);
21394
+ const rightLayout = layoutSide(grouped.right, []);
21395
+ return new Map([...leftLayout.entries(), ...rightLayout.entries()]);
21396
+ };
21397
+ var getCalloutLayout = (ctx, refAnnotation, index) => {
21398
+ const chart = getChartFromCtx$1(ctx);
21399
+ const xScale = chart ? getXScale(chart, getAxisId(refAnnotation, "x")) : void 0;
21400
+ const yScale = chart ? getYScale(chart, getAxisId(refAnnotation, "y")) : void 0;
21401
+ if (!chart || !yScale || !chart.chartArea) return null;
21402
+ if (!xScale && hasAxisAnchor(refAnnotation, "x")) return null;
21403
+ if (!xScale) return null;
21404
+ const layout = getCalloutLayoutPixels(chart).get(`callout-annotation-${index}`);
21405
+ if (!layout) return null;
21406
+ return {
21407
+ xValue: xScale.getValueForPixel(layout.xValue),
21408
+ yValue: yScale.getValueForPixel(layout.yValue)
21409
+ };
21410
+ };
20907
21411
  var resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
20908
- const chart = ctx?.chart;
21412
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21413
+ if (layout) return layout.xValue;
21414
+ const chart = getChartFromCtx$1(ctx);
20909
21415
  const xVal = refAnnotation?.xValue;
20910
21416
  if (typeof xVal !== "number") return 0;
20911
21417
  if (!chart) return xVal;
20912
- const scale = getXScale(chart, refAnnotation?.annotationAxis);
21418
+ const scale = getXScale(chart, getAxisId(refAnnotation, "x"));
20913
21419
  if (!scale) return xVal;
20914
21420
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
20915
21421
  const side = index % 2 === 0 ? 1 : -1;
20916
- const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
21422
+ const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * DEFAULT_HORIZONTAL_OFFSET;
20917
21423
  const basePx = scale.getPixelForValue(xVal);
20918
- const cxBase = basePx + dx;
20919
21424
  const area = chart.chartArea;
20920
- const el = ctx?.element;
20921
- if (!area || !el) return scale.getValueForPixel(cxBase);
20922
- const margin = calloutCfg?.margin ?? 8;
20923
- const halfW = (el?.width ?? 0) / 2;
20924
- let cx = cxBase;
20925
- let adjDx = dx;
20926
- if (cx - halfW < area.left + margin) {
20927
- adjDx += area.left + margin + halfW - cx;
20928
- cx = basePx + adjDx;
20929
- } else if (cx + halfW > area.right - margin) {
20930
- adjDx += area.right - margin - halfW - cx;
20931
- cx = basePx + adjDx;
20932
- }
20933
- return scale.getValueForPixel(cx);
21425
+ const element = ctx?.element;
21426
+ if (!area || !element) return scale.getValueForPixel(basePx + dx);
21427
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21428
+ const halfWidth = (element?.width ?? 0) / 2;
21429
+ const clampedPx = clamp$1(basePx + dx, area.left + margin + halfWidth, area.right - margin - halfWidth);
21430
+ return scale.getValueForPixel(clampedPx);
20934
21431
  };
20935
21432
  var resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
20936
- const chart = ctx?.chart;
20937
- const yVal = refAnnotation.yValue;
21433
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21434
+ if (layout) return layout.yValue;
21435
+ const chart = getChartFromCtx$1(ctx);
21436
+ const yVal = refAnnotation?.yValue;
20938
21437
  if (typeof yVal !== "number") return 0;
20939
21438
  if (!chart) return yVal;
20940
- const scale = getYScale(chart, refAnnotation?.annotationAxis);
21439
+ const scale = getYScale(chart, getAxisId(refAnnotation, "y"));
20941
21440
  if (!scale) return yVal;
20942
21441
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
20943
21442
  const row = Math.floor(index / 2);
20944
- const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
21443
+ const dy = hasCustomOffset ? calloutCfg?.yAdjust : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
20945
21444
  const basePx = scale.getPixelForValue(yVal);
20946
- const cyBase = basePx + dy;
20947
21445
  const area = chart.chartArea;
20948
- const el = ctx?.element;
20949
- if (!area || !el) return scale.getValueForPixel(cyBase);
20950
- const margin = calloutCfg?.margin ?? 8;
20951
- const halfH = (el?.height ?? 0) / 2;
20952
- let cy = cyBase;
20953
- let adjDy = dy;
20954
- if (cy - halfH < area.top + margin) {
20955
- adjDy += area.top + margin + halfH - cy;
20956
- cy = basePx + adjDy;
20957
- } else if (cy + halfH > area.bottom - margin) {
20958
- adjDy += area.bottom - margin - halfH - cy;
20959
- cy = basePx + adjDy;
20960
- }
20961
- return scale.getValueForPixel(cy);
20962
- };
20963
- var isInRange = (v, min, max) => v >= min && v <= max;
21446
+ const element = ctx?.element;
21447
+ if (!area || !element) return scale.getValueForPixel(basePx + dy);
21448
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21449
+ const halfHeight = (element?.height ?? 0) / 2;
21450
+ const clampedPx = clamp$1(basePx + dy, area.top + margin + halfHeight, area.bottom - margin - halfHeight);
21451
+ return scale.getValueForPixel(clampedPx);
21452
+ };
21453
+ var isInRange = (value, min, max) => value >= min && value <= max;
20964
21454
  var isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
20965
- const chart = ctx?.chart;
21455
+ const chart = getChartFromCtx$1(ctx);
20966
21456
  const area = chart?.chartArea;
20967
21457
  if (!chart || !area) return true;
20968
- const xScale = getXScale(chart, refAnnotation.annotationAxis);
20969
- const yScale = getYScale(chart, refAnnotation.annotationAxis);
20970
- const xVal = refAnnotation.xValue;
20971
- const yVal = refAnnotation.yValue;
20972
- if (!xScale || !yScale) return false;
20973
- if (typeof xVal !== "number" || typeof yVal !== "number") return false;
20974
- const x = xScale.getPixelForValue(xVal);
20975
- const y = yScale.getPixelForValue(yVal);
21458
+ const xScale = getXScale(chart, getAxisId(refAnnotation, "x"));
21459
+ const yScale = getYScale(chart, getAxisId(refAnnotation, "y"));
21460
+ const hasXValues = hasAxisAnchor(refAnnotation, "x");
21461
+ const hasYValues = hasAxisAnchor(refAnnotation, "y");
21462
+ if (hasXValues && !xScale || hasYValues && !yScale) return false;
21463
+ if (!hasXValues || !hasYValues) return false;
21464
+ const x = getAnchorPixel(chart, refAnnotation, "x", area);
21465
+ const y = getAnchorPixel(chart, refAnnotation, "y", area);
20976
21466
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
20977
21467
  };
20978
21468
  //#endregion
@@ -21273,7 +21763,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21273
21763
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
21274
21764
  const baseAnnotation = {
21275
21765
  ...baseRest,
21276
- label: { display: false }
21766
+ label: {
21767
+ ...label,
21768
+ display: () => false
21769
+ }
21277
21770
  };
21278
21771
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
21279
21772
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
@@ -21281,10 +21774,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21281
21774
  const onCalloutDragStart = typeof calloutCfg?.onDragStart === "function" ? calloutCfg.onDragStart : void 0;
21282
21775
  const onCalloutDrag = typeof calloutCfg?.onDrag === "function" ? calloutCfg.onDrag : void 0;
21283
21776
  const onCalloutDragEnd = typeof calloutCfg?.onDragEnd === "function" ? calloutCfg.onDragEnd : void 0;
21284
- const strokeStyle = calloutCfg.strokeStyle ?? baseAnnotation.borderColor;
21777
+ const strokeStyle = calloutCfg.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
21285
21778
  const lineWidth = typeof calloutCfg.lineWidth === "number" ? calloutCfg.lineWidth : 1;
21286
- const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 6;
21287
- const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : 1;
21779
+ const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 2;
21780
+ const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : .5;
21288
21781
  const calloutId = `callout-annotation-${index}`;
21289
21782
  return [baseAnnotation, {
21290
21783
  id: calloutId,
@@ -21294,14 +21787,18 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21294
21787
  color: (ctx) => {
21295
21788
  const chart = getChartFromCtx(ctx);
21296
21789
  if (!chart) return color;
21297
- if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21790
+ if (Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) && chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21298
21791
  return color;
21299
21792
  },
21300
21793
  font,
21301
21794
  borderColor,
21302
21795
  borderWidth: BORDER_WIDTH.INITIAL,
21303
21796
  padding: 5,
21304
- display: (ctx) => (refAnnotation.display ?? true) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0),
21797
+ display: (ctx) => {
21798
+ if (!(refAnnotation.display ?? true)) return false;
21799
+ if (!isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0)) return false;
21800
+ return true;
21801
+ },
21305
21802
  xValue: (ctx) => {
21306
21803
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
21307
21804
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
@@ -21318,10 +21815,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21318
21815
  displayDragCoordinates: false,
21319
21816
  enableDrag: true,
21320
21817
  enter: ({ element }, { chart }) => {
21321
- return handleLabelEnter(element, chart, { enableDrag: true });
21818
+ return handleLabelEnter(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21322
21819
  },
21323
21820
  leave: ({ element }, { chart }) => {
21324
- return handleLabelLeave(element, chart, { enableDrag: true });
21821
+ return handleLabelLeave(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21325
21822
  },
21326
21823
  onDragStart: onCalloutDragStart ? () => (coords) => {
21327
21824
  return onCalloutDragStart(coords, refAnnotation);
@@ -21333,12 +21830,21 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21333
21830
  return onCalloutDragEnd(coords, refAnnotation);
21334
21831
  } : void 0,
21335
21832
  calloutConnector: {
21336
- enabled: true,
21833
+ enabled: ({ chart }) => {
21834
+ if (!calloutCfg?.onlyWhenOverlapping) return true;
21835
+ return isCalloutOverlapping({ chart }, refAnnotation, index);
21836
+ },
21337
21837
  fromId: baseId,
21338
21838
  strokeStyle,
21339
21839
  lineWidth,
21340
21840
  startOffset,
21341
- endOffset
21841
+ endOffset,
21842
+ anchor: {
21843
+ position: refAnnotation?.labelConfig?.position,
21844
+ xAdjust: refAnnotation?.labelConfig?.xAdjust,
21845
+ yAdjust: refAnnotation?.labelConfig?.yAdjust,
21846
+ labelOffsetPx: refAnnotation?.labelOffsetPx
21847
+ }
21342
21848
  }
21343
21849
  }];
21344
21850
  };
@@ -21519,11 +22025,6 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21519
22025
  state
21520
22026
  ]);
21521
22027
  const dragData = useMemo(() => enableDragPoints && getDraggableData(options), [enableDragPoints, options]);
21522
- const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
21523
- generatedDatasets,
21524
- hoveredPoint,
21525
- onHover
21526
- ]);
21527
22028
  const panOptions = useMemo(() => ({
21528
22029
  enabled: panEnabled,
21529
22030
  mode: PanZoomMode.XY,
@@ -21554,13 +22055,13 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21554
22055
  updateAxesRangesFromChart,
21555
22056
  zoomEnabled
21556
22057
  ]);
21557
- const lineMarkersAnnotation = options?.annotations?.lineMarkersAnnotation ?? {};
21558
- const lineMarkersPluginEnabled = Boolean(options?.plugins?.lineMarkersPlugin?.enabled ?? lineMarkersAnnotation?.enabled);
21559
- const plugins = {
22058
+ const verticalMarkersAnnotation = options?.annotations?.verticalMarkersAnnotation ?? {};
22059
+ const verticalMarkersPluginEnabled = Boolean(options?.plugins?.verticalMarkersPlugin?.enabled ?? verticalMarkersAnnotation?.enabled);
22060
+ const plugins = useMemo(() => ({
21560
22061
  datalabels,
21561
22062
  annotationDraggerPlugin: { enabled: state?.enableDragAnnotation },
21562
22063
  calloutConnectorPlugin: { enableCalloutAnnotation: options?.annotations.enableCalloutAnnotation },
21563
- lineMarkersPlugin: { enabled: lineMarkersPluginEnabled },
22064
+ verticalMarkersPlugin: { enabled: verticalMarkersPluginEnabled },
21564
22065
  annotation: toAnnotationObject(annotation),
21565
22066
  zoom: {
21566
22067
  pan: panOptions,
@@ -21585,7 +22086,32 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21585
22086
  lineHeight
21586
22087
  },
21587
22088
  ...dragData
21588
- };
22089
+ }), [
22090
+ annotation,
22091
+ customLegendPlugin,
22092
+ datalabels,
22093
+ dragData,
22094
+ fontSize,
22095
+ legend,
22096
+ lineHeight,
22097
+ maxWidth,
22098
+ options?.annotations.enableCalloutAnnotation,
22099
+ panOptions,
22100
+ position,
22101
+ showLabel,
22102
+ state?.enableDragAnnotation,
22103
+ text,
22104
+ tooltip,
22105
+ verticalMarkersPluginEnabled,
22106
+ xOffset,
22107
+ yOffset,
22108
+ zoomOptions
22109
+ ]);
22110
+ const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
22111
+ generatedDatasets,
22112
+ hoveredPoint,
22113
+ onHover
22114
+ ]);
21589
22115
  return useMemo(() => ({
21590
22116
  persistenceId,
21591
22117
  layout: { padding: layoutPadding },
@@ -21613,6 +22139,7 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21613
22139
  layoutPadding,
21614
22140
  lineEnabled,
21615
22141
  onAnimationComplete,
22142
+ onChartHover,
21616
22143
  options?.annotations,
21617
22144
  options?.chartStyling?.maintainAspectRatio,
21618
22145
  options?.chartStyling?.performanceMode,
@@ -22138,52 +22665,126 @@ var annotationDraggerPlugin = {
22138
22665
  };
22139
22666
  //#endregion
22140
22667
  //#region src/components/line-chart/plugins/callout-plugin/helpers.ts
22141
- var getRadius = (el) => {
22142
- if (typeof el.options?.radius === "number") return el.options.radius;
22143
- if (typeof el.width === "number" && typeof el.height === "number") return Math.min(el.width, el.height) / 2;
22144
- return 0;
22668
+ var getRectangleEdgePoint = (el, targetX, targetY, offset) => {
22669
+ const width = el?.width ?? 0;
22670
+ const height = el?.height ?? 0;
22671
+ if (width <= 0 || height <= 0) return {
22672
+ x: el.centerX,
22673
+ y: el.centerY
22674
+ };
22675
+ const halfWidth = width / 2;
22676
+ const halfHeight = height / 2;
22677
+ const dx = targetX - el.centerX;
22678
+ const dy = targetY - el.centerY;
22679
+ const length = Math.sqrt(dx * dx + dy * dy) || 1;
22680
+ const ux = dx / length;
22681
+ const uy = dy / length;
22682
+ const tx = Math.abs(ux) > 1e-6 ? halfWidth / Math.abs(ux) : Number.POSITIVE_INFINITY;
22683
+ const ty = Math.abs(uy) > 1e-6 ? halfHeight / Math.abs(uy) : Number.POSITIVE_INFINITY;
22684
+ const distance = Math.min(tx, ty) + offset;
22685
+ return {
22686
+ x: el.centerX + ux * distance,
22687
+ y: el.centerY + uy * distance
22688
+ };
22689
+ };
22690
+ var getLabelEdgePoint = (labelEl, startX, endOffset) => {
22691
+ const width = labelEl?.width ?? 0;
22692
+ const height = labelEl?.height ?? 0;
22693
+ if (width <= 0 || height <= 0) return {
22694
+ x: labelEl.centerX,
22695
+ y: labelEl.centerY
22696
+ };
22697
+ const halfWidth = width / 2;
22698
+ const direction = startX <= labelEl.centerX ? -1 : 1;
22699
+ return {
22700
+ x: labelEl.centerX + direction * (halfWidth + endOffset),
22701
+ y: labelEl.centerY
22702
+ };
22703
+ };
22704
+ var getReferencePoint = (fromEl, connector) => {
22705
+ const { anchor } = connector;
22706
+ if (!anchor) return {
22707
+ x: fromEl.centerX,
22708
+ y: fromEl.centerY
22709
+ };
22710
+ const width = fromEl?.width ?? 0;
22711
+ const height = fromEl?.height ?? 0;
22712
+ const radius = fromEl?.options?.radius ?? 0;
22713
+ const halfWidth = width / 2;
22714
+ const halfHeight = height / 2;
22715
+ const labelOffset = anchor?.labelOffsetPx ?? 14;
22716
+ const xAdjust = anchor?.xAdjust ?? 0;
22717
+ const yAdjust = anchor?.yAdjust ?? 0;
22718
+ const position = anchor?.position ?? "top";
22719
+ let x = fromEl.centerX;
22720
+ let y = fromEl.centerY;
22721
+ switch (position) {
22722
+ case "left":
22723
+ x -= Math.max(halfWidth, radius) + labelOffset;
22724
+ break;
22725
+ case "right":
22726
+ x += Math.max(halfWidth, radius) + labelOffset;
22727
+ break;
22728
+ case "bottom":
22729
+ y += Math.max(halfHeight, radius) + labelOffset;
22730
+ break;
22731
+ case "top-left":
22732
+ x -= halfWidth;
22733
+ y -= halfHeight;
22734
+ break;
22735
+ case "top-right":
22736
+ x += halfWidth;
22737
+ y -= halfHeight;
22738
+ break;
22739
+ case "bottom-left":
22740
+ x -= halfWidth;
22741
+ y += halfHeight;
22742
+ break;
22743
+ case "bottom-right":
22744
+ x += halfWidth;
22745
+ y += halfHeight;
22746
+ break;
22747
+ default:
22748
+ y -= Math.max(halfHeight, radius) + labelOffset;
22749
+ break;
22750
+ }
22751
+ return {
22752
+ x: x + xAdjust,
22753
+ y: y + yAdjust
22754
+ };
22145
22755
  };
22146
22756
  var computeConnectorPoints = (fromEl, labelEl, connector) => {
22147
22757
  const x1c = fromEl?.centerX;
22148
22758
  const y1c = fromEl?.centerY;
22149
22759
  const cx = labelEl?.centerX;
22150
22760
  const cy = labelEl?.centerY;
22151
- const labelWidth = labelEl?.width ?? 0;
22152
- const labelHeight = labelEl?.height ?? 0;
22153
22761
  const ux = cx - x1c;
22154
22762
  const uy = cy - y1c;
22155
22763
  const len = Math.sqrt(ux * ux + uy * uy) || 1;
22156
22764
  const dx = ux / len;
22157
22765
  const dy = uy / len;
22158
- const fromRadius = getRadius(fromEl);
22159
- const startOffset = (connector?.startOffset ?? 6) + fromRadius;
22160
- const startX = x1c + dx * startOffset;
22161
- const startY = y1c + dy * startOffset;
22162
- let endX = cx;
22163
- let endY = cy;
22164
- if (labelWidth > 0 && labelHeight > 0) {
22165
- const hw = labelWidth / 2;
22166
- const hh = labelHeight / 2;
22167
- const vx = x1c - cx;
22168
- const vy = y1c - cy;
22169
- const vlen = Math.sqrt(vx * vx + vy * vy) || 1;
22170
- const vxn = vx / vlen;
22171
- const vyn = vy / vlen;
22172
- const tx = Math.abs(vxn) > 1e-6 ? hw / Math.abs(vxn) : Number.POSITIVE_INFINITY;
22173
- const ty = Math.abs(vyn) > 1e-6 ? hh / Math.abs(vyn) : Number.POSITIVE_INFINITY;
22174
- const total = Math.min(tx, ty) + (connector.endOffset ?? 1);
22175
- endX = cx + vxn * total;
22176
- endY = cy + vyn * total;
22177
- } else {
22178
- const endOffset = connector.endOffset ?? 1;
22179
- endX = cx - dx * endOffset;
22180
- endY = cy - dy * endOffset;
22181
- }
22766
+ const startOffset = connector?.startOffset ?? 6;
22767
+ const isPointAnchor = typeof fromEl?.options?.radius === "number";
22768
+ const referencePoint = connector?.anchor && !isPointAnchor ? getReferencePoint(fromEl, connector) : isPointAnchor ? {
22769
+ x: x1c,
22770
+ y: y1c
22771
+ } : getRectangleEdgePoint(fromEl, cx, cy, startOffset);
22772
+ const endOffset = connector?.endOffset ?? 1;
22773
+ const end = typeof labelEl?.width === "number" && typeof labelEl?.height === "number" ? getLabelEdgePoint(labelEl, referencePoint.x, endOffset) : {
22774
+ x: cx - dx * endOffset,
22775
+ y: cy - dy * endOffset
22776
+ };
22777
+ const tickHalf = isPointAnchor ? 0 : (connector?.anchor?.tickSize ?? 8) / 2;
22778
+ const lineDirection = end.x >= referencePoint.x ? 1 : -1;
22182
22779
  return {
22183
- startX,
22184
- startY,
22185
- endX,
22186
- endY
22780
+ startX: referencePoint.x + lineDirection * tickHalf,
22781
+ startY: referencePoint.y,
22782
+ endX: end.x,
22783
+ endY: end.y,
22784
+ tickStartX: referencePoint.x - tickHalf,
22785
+ tickStartY: referencePoint.y,
22786
+ tickEndX: referencePoint.x + tickHalf,
22787
+ tickEndY: referencePoint.y
22187
22788
  };
22188
22789
  };
22189
22790
  //#endregion
@@ -22201,54 +22802,41 @@ var calloutConnectorPlugin = {
22201
22802
  if (!raw) return;
22202
22803
  (Array.isArray(raw) ? raw : Object.values(raw)).forEach((opt) => {
22203
22804
  const connector = opt?.calloutConnector;
22204
- if (!connector || !connector.enabled || !opt?.id) return;
22805
+ if (!connector || !opt?.id) return;
22205
22806
  const labelEl = elements.find((el) => el.options && el.options.id === opt.id);
22206
22807
  const fromEl = elements.find((el) => el.options && el.options.id === connector.fromId);
22207
22808
  if (!labelEl || !fromEl) return;
22208
22809
  if (labelEl.options?.display === false) return;
22209
- const { startX, startY, endX, endY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22810
+ if (!(typeof connector.enabled === "function" ? connector.enabled({
22811
+ chart,
22812
+ labelEl,
22813
+ fromEl,
22814
+ option: opt
22815
+ }) : connector.enabled !== false)) return;
22816
+ const { startX, startY, endX, endY, tickStartX, tickStartY, tickEndX, tickEndY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22210
22817
  const ctx = chart?.ctx;
22818
+ const strokeStyle = connector?.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
22211
22819
  ctx?.save?.();
22212
22820
  ctx?.beginPath?.();
22213
22821
  ctx?.moveTo?.(startX, startY);
22214
22822
  ctx?.lineTo?.(endX, endY);
22215
22823
  ctx.lineWidth = connector?.lineWidth ?? 1;
22216
- ctx.strokeStyle = connector?.strokeStyle ?? "rgba(120,120,120,0.9)";
22824
+ ctx.strokeStyle = strokeStyle;
22217
22825
  if (connector?.lineDash && connector?.lineDash?.length) ctx?.setLineDash?.(connector.lineDash);
22218
22826
  ctx.lineCap = "round";
22219
22827
  ctx?.stroke?.();
22828
+ ctx?.beginPath?.();
22829
+ ctx?.moveTo?.(tickStartX, tickStartY);
22830
+ ctx?.lineTo?.(tickEndX, tickEndY);
22831
+ ctx.lineWidth = Math.max(connector?.lineWidth ?? 1, 1);
22832
+ ctx.strokeStyle = strokeStyle;
22833
+ ctx?.stroke?.();
22220
22834
  ctx?.restore?.();
22221
22835
  });
22222
22836
  }
22223
22837
  };
22224
22838
  //#endregion
22225
- //#region src/components/line-chart/plugins/line-markers-plugin/types.ts
22226
- var LineMarkerSide = /* @__PURE__ */ function(LineMarkerSide) {
22227
- LineMarkerSide["Left"] = "left";
22228
- LineMarkerSide["Right"] = "right";
22229
- return LineMarkerSide;
22230
- }({});
22231
- var LineMarkerDirection = /* @__PURE__ */ function(LineMarkerDirection) {
22232
- LineMarkerDirection["Vertical"] = "vertical";
22233
- LineMarkerDirection["Horizontal"] = "horizontal";
22234
- return LineMarkerDirection;
22235
- }({});
22236
- var LineMarkerLabelPosition = /* @__PURE__ */ function(LineMarkerLabelPosition) {
22237
- LineMarkerLabelPosition["Left"] = "left";
22238
- LineMarkerLabelPosition["Right"] = "right";
22239
- LineMarkerLabelPosition["Top"] = "top";
22240
- LineMarkerLabelPosition["Bottom"] = "bottom";
22241
- LineMarkerLabelPosition["OnLine"] = "onLine";
22242
- return LineMarkerLabelPosition;
22243
- }({});
22244
- var LineMarkerTextAlign = /* @__PURE__ */ function(LineMarkerTextAlign) {
22245
- LineMarkerTextAlign["Left"] = "left";
22246
- LineMarkerTextAlign["Right"] = "right";
22247
- LineMarkerTextAlign["Center"] = "center";
22248
- return LineMarkerTextAlign;
22249
- }({});
22250
- //#endregion
22251
- //#region src/components/line-chart/plugins/line-markers-plugin/utils.ts
22839
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/utils.ts
22252
22840
  var isFiniteNumber = (v) => {
22253
22841
  return isNumber(v) && Number.isFinite(v);
22254
22842
  };
@@ -22274,336 +22862,68 @@ var clamp = (v, min, max) => {
22274
22862
  var crispLinePx = (v) => {
22275
22863
  return Math.round(v) + .5;
22276
22864
  };
22277
- var toLines = (label) => {
22278
- if (!label) return [];
22279
- return isArray(label) ? label : [label];
22280
- };
22281
- var lineHeightPx = (font) => {
22282
- const match = /(\d+)\s*px/i.exec(font);
22283
- const size = match ? Number(match[1]) : 12;
22284
- return Math.max(10, Math.round(size * 1.2));
22285
- };
22286
- var clusterByXDistance = (labels, clusterXPx) => {
22287
- if (!labels.length) return [];
22288
- const sorted = [...labels].sort((a, b) => a.x - b.x);
22289
- const clusters = [];
22290
- let current = [sorted[0]];
22291
- for (let i = 1; i < sorted.length; i += 1) {
22292
- const prev = sorted[i - 1];
22293
- const currentItem = sorted[i];
22294
- if (Math.abs(currentItem.x - prev.x) <= clusterXPx) {
22295
- current.push(currentItem);
22296
- continue;
22297
- }
22298
- clusters.push(current);
22299
- current = [currentItem];
22300
- }
22301
- clusters.push(current);
22302
- return clusters;
22303
- };
22304
- var resolveCluster = ({ labels, chartTop, chartBottom, gapPx }) => {
22305
- const sorted = [...labels].sort((a, b) => a.y - b.y);
22306
- if (!sorted.length) return {};
22307
- const placed = sorted.map((item) => ({
22308
- ...item,
22309
- placedY: item.y
22310
- }));
22311
- for (let i = 1; i < placed.length; i += 1) {
22312
- const prev = placed[i - 1];
22313
- const curr = placed[i];
22314
- const prevBottom = prev.placedY + prev.height / 2;
22315
- const currTop = curr.placedY - curr.height / 2;
22316
- const overlap = prevBottom + gapPx - currTop;
22317
- if (overlap > 0) curr.placedY += overlap;
22318
- }
22319
- const last = placed[placed.length - 1];
22320
- const overflowBottom = last.placedY + last.height / 2 - chartBottom;
22321
- if (overflowBottom > 0) placed.forEach((item) => {
22322
- item.placedY -= overflowBottom;
22323
- });
22324
- const first = placed[0];
22325
- const overflowTop = chartTop - (first.placedY - first.height / 2);
22326
- if (overflowTop > 0) placed.forEach((item) => {
22327
- item.placedY += overflowTop;
22328
- });
22329
- return placed.reduce((acc, item) => {
22330
- acc[item.id] = clamp(item.placedY, chartTop, chartBottom);
22331
- return acc;
22332
- }, {});
22333
- };
22334
- var resolveLabelCollisions = ({ labels, chartTop, chartBottom, clusterXPx, gapPx }) => {
22335
- const result = {};
22336
- [LineMarkerSide.Left, LineMarkerSide.Right].forEach((side) => {
22337
- clusterByXDistance(labels.filter((label) => label.side === side), clusterXPx).forEach((cluster) => {
22338
- Object.assign(result, resolveCluster({
22339
- labels: cluster,
22340
- chartTop,
22341
- chartBottom,
22342
- gapPx
22343
- }));
22344
- });
22345
- });
22346
- return result;
22347
- };
22348
22865
  //#endregion
22349
- //#region src/components/line-chart/plugins/line-markers-plugin/draw.ts
22350
- var drawLineSegment = ({ chart, x1, y1, x2, y2, color, opacity, lineWidth, lineDash }) => {
22866
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/draw.ts
22867
+ var drawVerticalLine = ({ chart, xPx, y1, y2, color, opacity, lineWidth, lineDash }) => {
22351
22868
  const { ctx } = chart ?? {};
22352
- const startX = crispLinePx(x1);
22353
- const endX = crispLinePx(x2);
22354
- const startY = crispLinePx(y1);
22355
- const endY = crispLinePx(y2);
22869
+ const x = crispLinePx(xPx);
22356
22870
  ctx?.save?.();
22357
22871
  ctx.globalAlpha = opacity;
22358
22872
  ctx?.beginPath?.();
22359
22873
  ctx.lineWidth = lineWidth;
22360
22874
  ctx.strokeStyle = color;
22361
22875
  ctx?.setLineDash?.(lineDash);
22362
- ctx.lineCap = "butt";
22363
- ctx?.moveTo?.(startX, startY);
22364
- ctx?.lineTo?.(endX, endY);
22876
+ ctx.lineCap = "round";
22877
+ ctx?.moveTo?.(x, y1);
22878
+ ctx?.lineTo?.(x, y2);
22365
22879
  ctx?.stroke?.();
22366
22880
  ctx?.restore?.();
22367
- return {
22368
- x1: startX,
22369
- y1: startY,
22370
- x2: endX,
22371
- y2: endY
22372
- };
22373
- };
22374
- var isWithin = (value, min, max) => {
22375
- return value >= min && value <= max;
22376
- };
22377
- var isTickElementInsideByDirection = (args) => {
22378
- const { direction, chartArea, box } = args;
22379
- if (direction === LineMarkerDirection.Vertical) return box.top >= chartArea.top && box.bottom <= chartArea.bottom;
22380
- return box.left >= chartArea.left && box.right <= chartArea.right;
22381
- };
22382
- var resolveDefaultLabelPosition = (args) => {
22383
- const { direction, side } = args;
22384
- if (direction === LineMarkerDirection.Vertical) return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22385
- return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22386
- };
22387
- var getLabelAnchor = (args) => {
22388
- const { x, y, direction, labelOffset, bootSize, labelPosition } = args;
22389
- const distance = bootSize + labelOffset;
22390
- if (direction === LineMarkerDirection.Horizontal) switch (labelPosition) {
22391
- case LineMarkerLabelPosition.Left: return {
22392
- x: x - distance,
22393
- y,
22394
- textAlign: LineMarkerTextAlign.Right
22395
- };
22396
- case LineMarkerLabelPosition.Right: return {
22397
- x: x + distance,
22398
- y,
22399
- textAlign: LineMarkerTextAlign.Left
22400
- };
22401
- case LineMarkerLabelPosition.Top: return {
22402
- x,
22403
- y: y - distance,
22404
- textAlign: LineMarkerTextAlign.Center
22405
- };
22406
- case LineMarkerLabelPosition.Bottom: return {
22407
- x,
22408
- y: y + distance,
22409
- textAlign: LineMarkerTextAlign.Center
22410
- };
22411
- case LineMarkerLabelPosition.OnLine: return {
22412
- x,
22413
- y,
22414
- textAlign: LineMarkerTextAlign.Center
22415
- };
22416
- default: return {
22417
- x,
22418
- y,
22419
- textAlign: LineMarkerTextAlign.Center
22420
- };
22421
- }
22422
- switch (labelPosition) {
22423
- case LineMarkerLabelPosition.Left: return {
22424
- x: x - distance,
22425
- y,
22426
- textAlign: LineMarkerTextAlign.Right
22427
- };
22428
- case LineMarkerLabelPosition.Right: return {
22429
- x: x + distance,
22430
- y,
22431
- textAlign: LineMarkerTextAlign.Left
22432
- };
22433
- case LineMarkerLabelPosition.Top: return {
22434
- x,
22435
- y: y - distance,
22436
- textAlign: LineMarkerTextAlign.Center
22437
- };
22438
- case LineMarkerLabelPosition.Bottom: return {
22439
- x,
22440
- y: y + distance,
22441
- textAlign: LineMarkerTextAlign.Center
22442
- };
22443
- case LineMarkerLabelPosition.OnLine: return {
22444
- x,
22445
- y,
22446
- textAlign: LineMarkerTextAlign.Center
22447
- };
22448
- default: return {
22449
- x,
22450
- y,
22451
- textAlign: LineMarkerTextAlign.Center
22452
- };
22453
- }
22454
- };
22455
- var getLineTextBox = (args) => {
22456
- const { textAlign, x, y, width, lineHeight } = args;
22457
- return {
22458
- left: textAlign === LineMarkerTextAlign.Right ? x - width : textAlign === LineMarkerTextAlign.Center ? x - width / 2 : x,
22459
- right: textAlign === LineMarkerTextAlign.Right ? x : textAlign === LineMarkerTextAlign.Center ? x + width / 2 : x + width,
22460
- top: y - lineHeight / 2,
22461
- bottom: y + lineHeight / 2
22462
- };
22463
- };
22464
- var getOppositeLabelPosition = (position) => {
22465
- switch (position) {
22466
- case LineMarkerLabelPosition.Left: return LineMarkerLabelPosition.Right;
22467
- case LineMarkerLabelPosition.Right: return LineMarkerLabelPosition.Left;
22468
- case LineMarkerLabelPosition.Top: return LineMarkerLabelPosition.Bottom;
22469
- case LineMarkerLabelPosition.Bottom: return LineMarkerLabelPosition.Top;
22470
- default: return LineMarkerLabelPosition.OnLine;
22471
- }
22881
+ return x;
22472
22882
  };
22473
22883
  var drawTick = (args) => {
22474
- const { ctx, x, y, side, tick, defaultColor, defaultFont, chartArea } = args ?? {};
22475
- const direction = args?.direction ?? LineMarkerDirection.Vertical;
22884
+ const { ctx, x, y, side, tick, defaultColor, defaultFont } = args ?? {};
22476
22885
  const reverse = Boolean(args?.reverse);
22477
- const labelY = args?.labelY ?? y;
22478
- const bootEnabled = tick?.enabled ?? true;
22479
- const hasLabel = Array.isArray(tick?.label) ? tick.label.length > 0 : Boolean(tick?.label);
22480
- if (!bootEnabled && !hasLabel) return;
22481
22886
  const color = tick?.color ?? defaultColor;
22482
22887
  const font = tick?.font ?? defaultFont;
22483
22888
  const labelOffset = tick?.labelOffsetPx ?? 6;
22484
- const bootSize = tick?.sizePx ?? 6;
22485
- const dir = side === LineMarkerSide.Left ? 1 : -1;
22486
- const xTickEnd = crispLinePx(x);
22487
- const yTickEnd = crispLinePx(y);
22889
+ const bootSize = 6;
22890
+ const dir = side === "left" ? 1 : -1;
22891
+ const xTickEnd = x;
22488
22892
  ctx?.save?.();
22489
22893
  ctx.strokeStyle = color;
22490
22894
  ctx.fillStyle = color;
22491
22895
  ctx.lineWidth = 1;
22492
- const xOuter = direction === LineMarkerDirection.Vertical ? xTickEnd + dir * bootSize : xTickEnd;
22493
- const yOuter = direction === LineMarkerDirection.Horizontal ? yTickEnd + (reverse ? bootSize : -bootSize) : yTickEnd;
22494
- const xThird = direction === LineMarkerDirection.Vertical ? xTickEnd : xTickEnd + dir * bootSize;
22495
- const yThird = reverse ? yTickEnd + bootSize : yTickEnd - bootSize;
22496
- const bootBounds = {
22497
- left: Math.min(xTickEnd, xOuter, xThird),
22498
- right: Math.max(xTickEnd, xOuter, xThird),
22499
- top: Math.min(yTickEnd, yOuter, yThird),
22500
- bottom: Math.max(yTickEnd, yOuter, yThird)
22501
- };
22502
- const canDrawBoot = (direction === LineMarkerDirection.Vertical ? isWithin(yTickEnd, chartArea.top, chartArea.bottom) : isWithin(xTickEnd, chartArea.left, chartArea.right)) && isTickElementInsideByDirection({
22503
- direction,
22504
- chartArea,
22505
- box: bootBounds
22506
- });
22507
- if (bootEnabled && canDrawBoot) {
22508
- ctx?.beginPath?.();
22509
- ctx?.moveTo?.(xTickEnd, yTickEnd);
22510
- ctx?.lineTo?.(xOuter, yOuter);
22511
- ctx?.lineTo?.(xThird, yThird);
22512
- ctx?.closePath?.();
22513
- ctx?.fill?.();
22514
- }
22896
+ const xOuter = xTickEnd + dir * bootSize;
22897
+ ctx?.beginPath?.();
22898
+ ctx?.moveTo?.(xTickEnd, y);
22899
+ ctx?.lineTo?.(xOuter, y);
22900
+ ctx?.lineTo?.(xTickEnd, reverse ? y + bootSize : y - bootSize);
22901
+ ctx?.closePath?.();
22902
+ ctx?.fill?.();
22515
22903
  const label = tick.label;
22516
22904
  if (label) {
22517
22905
  ctx.font = font;
22518
22906
  ctx.textBaseline = "middle";
22519
- const preferredPosition = tick?.labelPosition ?? resolveDefaultLabelPosition({
22520
- direction,
22521
- side
22522
- });
22523
- const fallbackPositions = [
22524
- preferredPosition,
22525
- getOppositeLabelPosition(preferredPosition),
22526
- LineMarkerLabelPosition.Top,
22527
- LineMarkerLabelPosition.Bottom,
22528
- LineMarkerLabelPosition.OnLine
22529
- ].filter((value, index, arr) => arr.indexOf(value) === index);
22530
- for (const labelPosition of fallbackPositions) {
22531
- const anchor = getLabelAnchor({
22532
- x: xTickEnd,
22533
- y: labelY,
22534
- direction,
22535
- labelOffset,
22536
- bootSize,
22537
- labelPosition
22538
- });
22539
- const labelTextAlign = anchor.textAlign;
22540
- ctx.textAlign = labelTextAlign;
22541
- if (Array.isArray(label)) {
22542
- const lineH = lineHeightPx(font);
22543
- const startY = anchor.y - (label.length - 1) * lineH / 2;
22544
- const metrics = label.map((line, i) => {
22545
- const text = String(line);
22546
- const width = ctx.measureText(text).width;
22547
- const yLine = startY + i * lineH;
22548
- return {
22549
- text,
22550
- yLine,
22551
- box: getLineTextBox({
22552
- textAlign: labelTextAlign,
22553
- x: anchor.x,
22554
- y: yLine,
22555
- width,
22556
- lineHeight: lineH
22557
- })
22558
- };
22559
- });
22560
- if (!metrics.every((lineData) => isTickElementInsideByDirection({
22561
- direction,
22562
- chartArea,
22563
- box: lineData.box
22564
- }))) continue;
22565
- metrics.forEach((lineData) => ctx?.fillText?.(lineData.text, anchor.x, lineData.yLine));
22566
- break;
22567
- } else {
22568
- const text = String(label);
22569
- const width = ctx.measureText(text).width;
22570
- const lineH = lineHeightPx(font);
22571
- if (!isTickElementInsideByDirection({
22572
- direction,
22573
- chartArea,
22574
- box: getLineTextBox({
22575
- textAlign: labelTextAlign,
22576
- x: anchor.x,
22577
- y: anchor.y,
22578
- width,
22579
- lineHeight: lineH
22580
- })
22581
- })) continue;
22582
- ctx?.fillText?.(text, anchor.x, anchor.y);
22583
- break;
22584
- }
22585
- }
22907
+ ctx.textAlign = side === "left" ? "left" : "right";
22908
+ const tx = xOuter + dir * labelOffset;
22909
+ if (Array.isArray(label)) {
22910
+ const lineH = 12;
22911
+ const startY = y - (label.length - 1) * lineH / 2;
22912
+ label?.forEach((line, i) => ctx?.fillText?.(String(line), tx, startY + i * lineH));
22913
+ } else ctx?.fillText?.(String(label), tx, y);
22586
22914
  }
22587
22915
  ctx?.restore?.();
22588
22916
  };
22589
- var getTickLabelHeight = (tick, defaultFont) => {
22590
- const lines = toLines(tick?.label);
22591
- if (!lines.length) return 0;
22592
- const lineHeight = lineHeightPx(tick?.font ?? defaultFont);
22593
- return lines.length * lineHeight;
22594
- };
22595
22917
  //#endregion
22596
- //#region src/components/line-chart/plugins/line-markers-plugin/line-markers-plugin.ts
22597
- var DEFAULT_FONT = "12px sans-serif";
22598
- var DEFAULT_COLOR = "rgba(20,20,20,0.9)";
22918
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.ts
22599
22919
  var readPluginEnable = (chart) => {
22600
- const { lineMarkersPlugin } = chart?.options?.plugins ?? {};
22601
- return asObject(lineMarkersPlugin)?.enabled === true;
22920
+ const { verticalMarkersPlugin } = chart?.options?.plugins ?? {};
22921
+ return asObject(verticalMarkersPlugin)?.enabled === true;
22602
22922
  };
22603
22923
  var readAnnotationConfig = (chart) => {
22604
22924
  const optionsWithAnnotations = chart?.options;
22605
22925
  const configWithAnnotations = chart?.config?.options;
22606
- return asObject(configWithAnnotations?.annotations?.lineMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.lineMarkersAnnotation);
22926
+ return asObject(configWithAnnotations?.annotations?.verticalMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.verticalMarkersAnnotation);
22607
22927
  };
22608
22928
  var readOwnBoolean = (obj, key) => {
22609
22929
  if (!obj || typeof obj !== "object") return void 0;
@@ -22611,242 +22931,39 @@ var readOwnBoolean = (obj, key) => {
22611
22931
  if (!descriptor || !("value" in descriptor)) return void 0;
22612
22932
  return typeof descriptor?.value === "boolean" ? descriptor?.value : void 0;
22613
22933
  };
22614
- var getMarkerDirection = (item, global) => {
22615
- return item?.direction ?? global?.lineDirection ?? LineMarkerDirection.Vertical;
22616
- };
22617
- var resolveSide = (item, global) => {
22618
- return item?.stickSide ?? global?.stickSide ?? LineMarkerSide.Right;
22619
- };
22620
- var getEdgeX = (chartArea, side, edgePaddingPx) => {
22621
- return clamp(side === LineMarkerSide.Left ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22622
- };
22623
- var computeVerticalXPxBase = (args) => {
22934
+ var computeXPxBase = (args) => {
22624
22935
  const { chart, chartArea, item, global } = args ?? {};
22625
22936
  const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22626
- const side = resolveSide(item, global);
22937
+ const stickSide = item?.stickSide ?? global?.stickSide ?? "right";
22627
22938
  const edgePaddingPx = global?.edgePaddingPx ?? 0;
22628
22939
  const xValue = item?.xValue ?? global?.xValue;
22629
22940
  if (isFiniteNumber(xValue)) return getPixelForValueSafe(resolveScale(chart, item?.xScaleID, "x"), xValue);
22630
- if (stickToEdge) return getEdgeX(chartArea, side, edgePaddingPx);
22941
+ if (stickToEdge) return clamp(stickSide === "left" ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22631
22942
  };
22632
- var computeVerticalLineGeometry = (args) => {
22633
- const { chart, chartArea, item, global, xPx } = args ?? {};
22943
+ var computeYSpan = (args) => {
22944
+ const { chart, chartArea, item, global } = args ?? {};
22634
22945
  const yScale = resolveScale(chart, item?.yScaleID, "y");
22635
22946
  if (!yScale) return void 0;
22636
- const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22637
22947
  const yStartValue = item?.yStartValue ?? global?.yStartValue;
22638
- const yEndValue = item?.yEndValue ?? item?.value ?? global?.yEndValue;
22639
- if (!isFiniteNumber(yEndValue)) return void 0;
22640
- const y1 = isFiniteNumber(yStartValue) ? yScale.getPixelForValue(yStartValue) : reverse ? chartArea?.bottom : chartArea?.top;
22641
- const y1InArea = y1 >= chartArea?.top && y1 <= chartArea?.bottom;
22642
- const y2Raw = yScale.getPixelForValue(yEndValue);
22643
- const y2InArea = y2Raw >= chartArea?.top && y2Raw <= chartArea?.bottom;
22644
- const y2 = clamp(y2Raw, chartArea?.top, chartArea?.bottom);
22645
- return {
22646
- x1: xPx,
22647
- y1: clamp(y1, chartArea?.top, chartArea?.bottom),
22648
- x2: xPx,
22649
- y2,
22650
- startInArea: y1InArea,
22651
- endInArea: y2InArea,
22652
- axisInArea: xPx >= chartArea.left && xPx <= chartArea.right,
22653
- side: resolveSide(item, global),
22654
- direction: LineMarkerDirection.Vertical
22655
- };
22656
- };
22657
- var resolveHorizontalSpan = (args) => {
22658
- const { chart, chartArea, item, global, side, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22659
- const xScale = resolveScale(chart, item?.xScaleID, "x");
22660
- if (!xScale) return void 0;
22661
- const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22662
- const stickToGroup = item?.stickToGroup ?? global?.stickToGroup ?? false;
22663
- const edgePaddingPx = global?.edgePaddingPx ?? 0;
22664
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22665
- const fixedLengthPx = item?.lengthPx ?? global?.lengthPx ?? global?.horizontalLineLengthPx;
22666
- const edgeX = getEdgeX(chartArea, side, edgePaddingPx);
22667
- const xStartValue = item?.xStartValue ?? global?.xStartValue;
22668
- const xEndValue = item?.xEndValue ?? global?.xEndValue;
22669
- const xValue = item?.xValue ?? global?.xValue;
22670
- const xStartPxRaw = isFiniteNumber(xStartValue) ? xScale.getPixelForValue(xStartValue) : void 0;
22671
- const xEndPxRaw = isFiniteNumber(xEndValue) ? xScale.getPixelForValue(xEndValue) : void 0;
22672
- const xStartPx = isFiniteNumber(xStartPxRaw) ? clamp(xStartPxRaw, chartArea?.left, chartArea?.right) : void 0;
22673
- const xEndPx = isFiniteNumber(xEndPxRaw) ? clamp(xEndPxRaw, chartArea?.left, chartArea?.right) : void 0;
22674
- if (stickToEdge && stickToGroup && isFiniteNumber(groupAnchorXPx)) {
22675
- const groupXRaw = groupAnchorXPx;
22676
- if (groupXRaw < chartArea.left || groupXRaw > chartArea.right) return;
22677
- const groupX = clamp(groupXRaw, chartArea?.left, chartArea?.right);
22678
- const edgeX = side === LineMarkerSide.Left ? chartArea.left : chartArea.right;
22679
- const x2Raw = isFiniteNumber(fixedLengthPx) ? edgeX + dirIntoChart * fixedLengthPx : groupX;
22680
- return {
22681
- x1: edgeX,
22682
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22683
- startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22684
- endInArea: isFiniteNumber(fixedLengthPx) ? x2Raw >= chartArea.left && x2Raw <= chartArea.right : groupX >= chartArea.left && groupX <= chartArea.right
22685
- };
22686
- }
22687
- if (!stickToEdge && stickToGroup && isFiniteNumber(fixedLengthPx)) {
22688
- const rawAnchor = previousGroupItemXPx ?? (isFiniteNumber(groupAnchorXPx) ? groupAnchorXPx : void 0);
22689
- if (!isFiniteNumber(rawAnchor)) return void 0;
22690
- const anchorX = clamp(rawAnchor, chartArea?.left, chartArea?.right);
22691
- const x2Raw = anchorX + dirIntoChart * fixedLengthPx;
22692
- return {
22693
- x1: anchorX,
22694
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22695
- startInArea: rawAnchor >= chartArea.left && rawAnchor <= chartArea.right,
22696
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22697
- };
22698
- }
22699
- if (isFiniteNumber(xStartPx) && isFiniteNumber(xEndPx)) {
22700
- const startRaw = xStartPxRaw;
22701
- const endRaw = xEndPxRaw;
22702
- if (!isFiniteNumber(startRaw) || !isFiniteNumber(endRaw)) return void 0;
22703
- return {
22704
- x1: xStartPx,
22705
- x2: xEndPx,
22706
- startInArea: startRaw >= chartArea.left && startRaw <= chartArea.right,
22707
- endInArea: endRaw >= chartArea.left && endRaw <= chartArea.right
22708
- };
22709
- }
22710
- if (isFiniteNumber(xStartPx) && isFiniteNumber(fixedLengthPx)) {
22711
- const x2Raw = xStartPx + dirIntoChart * fixedLengthPx;
22712
- return {
22713
- x1: xStartPx,
22714
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22715
- startInArea: xStartPx >= chartArea.left && xStartPx <= chartArea.right,
22716
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22717
- };
22718
- }
22719
- if (isFiniteNumber(xEndPx) && isFiniteNumber(fixedLengthPx)) {
22720
- const x1Raw = xEndPx - dirIntoChart * fixedLengthPx;
22721
- return {
22722
- x1: clamp(x1Raw, chartArea?.left, chartArea?.right),
22723
- x2: xEndPx,
22724
- startInArea: x1Raw >= chartArea.left && x1Raw <= chartArea.right,
22725
- endInArea: xEndPx >= chartArea.left && xEndPx <= chartArea.right
22726
- };
22727
- }
22728
- const xValuePxRaw = isFiniteNumber(xValue) ? xScale.getPixelForValue(xValue) : void 0;
22729
- if (isFiniteNumber(xValuePxRaw) && isFiniteNumber(fixedLengthPx)) {
22730
- const xAnchor = clamp(xValuePxRaw, chartArea?.left, chartArea?.right);
22731
- const x2Raw = xAnchor + dirIntoChart * fixedLengthPx;
22732
- return {
22733
- x1: xAnchor,
22734
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22735
- startInArea: xValuePxRaw >= chartArea.left && xValuePxRaw <= chartArea.right,
22736
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22737
- };
22738
- }
22739
- if (stickToEdge && isFiniteNumber(fixedLengthPx)) {
22740
- const x2Raw = edgeX + dirIntoChart * fixedLengthPx;
22741
- return {
22742
- x1: edgeX,
22743
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22744
- startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22745
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22746
- };
22747
- }
22748
- };
22749
- var computeHorizontalLineGeometry = (args) => {
22750
- const { chart, chartArea, item, global, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22751
- const yScale = resolveScale(chart, item?.yScaleID, "y");
22752
- if (!yScale) return void 0;
22753
- const yValue = item?.yValue ?? item?.value ?? global?.yValue;
22754
- if (!isFiniteNumber(yValue)) return void 0;
22755
- const side = resolveSide(item, global);
22756
- const span = resolveHorizontalSpan({
22757
- chart,
22758
- chartArea,
22759
- item,
22760
- global,
22761
- side,
22762
- groupAnchorXPx,
22763
- previousGroupItemXPx
22764
- });
22765
- if (!span) return void 0;
22766
- const yPxRaw = yScale.getPixelForValue(yValue);
22767
- const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22768
- const yInArea = yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom;
22769
- return {
22770
- x1: span.x1,
22771
- y1: yPx,
22772
- x2: span.x2,
22773
- y2: yPx,
22774
- startInArea: span.startInArea && yInArea,
22775
- endInArea: span.endInArea && yInArea,
22776
- axisInArea: yInArea,
22777
- side,
22778
- direction: LineMarkerDirection.Horizontal
22779
- };
22780
- };
22781
- var resolveTick = (args) => {
22782
- const { globalTick, itemTick, defaultEnabled } = args;
22783
- return {
22784
- enabled: itemTick?.enabled ?? globalTick?.enabled ?? defaultEnabled,
22785
- label: itemTick?.label ?? globalTick?.label,
22786
- color: itemTick?.color ?? globalTick?.color,
22787
- font: itemTick?.font ?? globalTick?.font,
22788
- labelOffsetPx: itemTick?.labelOffsetPx ?? globalTick?.labelOffsetPx,
22789
- labelPosition: itemTick?.labelPosition ?? globalTick?.labelPosition,
22790
- reverse: itemTick?.reverse ?? globalTick?.reverse,
22791
- side: itemTick?.side ?? globalTick?.side,
22792
- sizePx: itemTick?.sizePx ?? globalTick?.sizePx
22793
- };
22794
- };
22795
- var hasTickLabel = (tick) => {
22796
- const label = tick?.label;
22797
- if (Array.isArray(label)) return label.length > 0;
22798
- return Boolean(label);
22799
- };
22800
- var toExtraGeometry = (args) => {
22801
- const { chart, chartArea, item, extra, xBase, defaultSide } = args;
22802
- const yScale = resolveScale(chart, extra?.yScaleID ?? item?.yScaleID, "y");
22803
- if (!yScale) return void 0;
22804
- const yValue = extra?.yValue ?? extra?.value;
22805
- if (!isFiniteNumber(yValue)) return void 0;
22806
- const lengthPx = extra?.lengthPx;
22807
- if (!isFiniteNumber(lengthPx) || lengthPx <= 0) return void 0;
22808
- const side = extra?.side ?? defaultSide;
22809
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22810
- const yPxRaw = yScale.getPixelForValue(yValue);
22811
- const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22812
- const x2Raw = xBase + dirIntoChart * lengthPx;
22813
- return {
22814
- x1: xBase,
22815
- y1: yPx,
22816
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22817
- y2: yPx,
22818
- startInArea: xBase >= chartArea.left && xBase <= chartArea.right,
22819
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right,
22820
- axisInArea: yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom,
22821
- side,
22822
- direction: LineMarkerDirection.Horizontal
22823
- };
22824
- };
22825
- var getLineStyle = (item, global) => {
22826
- const color = item?.color ?? global?.color ?? DEFAULT_COLOR;
22827
- const opacity = item?.opacity ?? global?.opacity ?? 1;
22828
- const lineWidth = item?.lineWidth ?? global?.lineWidth ?? 1;
22829
- const lineDash = asArray(item?.lineDash ?? global?.lineDash);
22830
- if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
22948
+ const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22949
+ const itemValue = item?.value;
22950
+ if (!isFiniteNumber(itemValue)) return void 0;
22951
+ const yStartPx = isFiniteNumber(yStartValue) ? clamp(yScale.getPixelForValue(yStartValue), chartArea?.top, chartArea?.bottom) : reverse ? chartArea?.bottom : chartArea?.top;
22952
+ const yEndPxRaw = clamp(yScale.getPixelForValue(itemValue), chartArea?.top, chartArea?.bottom);
22831
22953
  return {
22832
- color,
22833
- opacity,
22834
- lineWidth,
22835
- lineDash
22954
+ yStartPx,
22955
+ yEndPx: reverse ? Math.min(yStartPx, yEndPxRaw) : Math.max(yStartPx, yEndPxRaw),
22956
+ endTick: {
22957
+ label: item.label,
22958
+ color: item.color,
22959
+ font: item.font,
22960
+ labelOffsetPx: item.labelOffsetPx
22961
+ },
22962
+ reverse
22836
22963
  };
22837
22964
  };
22838
- var getLineReverse = (geometry) => {
22839
- if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.x2 < geometry.x1;
22840
- return geometry.y2 < geometry.y1;
22841
- };
22842
- var hasVisibleLineSegment = (args) => {
22843
- const { geometry } = args;
22844
- const epsilon = .5;
22845
- if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.axisInArea && Math.abs(geometry.x2 - geometry.x1) > epsilon;
22846
- return geometry.axisInArea && Math.abs(geometry.y2 - geometry.y1) > epsilon;
22847
- };
22848
- var lineMarkersPlugin = {
22849
- id: "lineMarkers",
22965
+ var verticalMarkersPlugin = {
22966
+ id: "verticalMarkers",
22850
22967
  afterDraw: (chart) => {
22851
22968
  if (!readPluginEnable(chart)) return;
22852
22969
  const cfg = readAnnotationConfig(chart);
@@ -22857,195 +22974,50 @@ var lineMarkersPlugin = {
22857
22974
  const items = asArray(cfg?.items);
22858
22975
  if (!items.length) return;
22859
22976
  const itemGapPx = cfg?.itemGapPx ?? 0;
22860
- const defaultSide = cfg?.stickSide ?? LineMarkerSide.Right;
22861
- const lineTasks = [];
22862
- const tickTasks = [];
22863
- let groupedVerticalIndexBySide = {};
22864
- let groupedAnchorBySide = {};
22865
- let previousGroupedHorizontalEndBySide = {};
22866
- items.forEach((item, itemIndex) => {
22977
+ const globalStickSide = cfg?.stickSide ?? "right";
22978
+ let visibleIndex = 0;
22979
+ items?.forEach((item) => {
22867
22980
  if (!item || item?.display === false) return;
22868
- const direction = getMarkerDirection(item, cfg);
22869
- const side = resolveSide(item, cfg);
22870
- const style = getLineStyle(item, cfg);
22871
- if (!style) return;
22872
- let geometry;
22873
- if (direction === LineMarkerDirection.Vertical) {
22874
- const baseXPx = computeVerticalXPxBase({
22875
- chart,
22876
- chartArea,
22877
- item,
22878
- global: cfg
22879
- });
22880
- if (!isFiniteNumber(baseXPx)) return;
22881
- const stickToEdge = item?.stickToEdge ?? cfg?.stickToEdge ?? true;
22882
- const hasExplicitXValue = isFiniteNumber(item?.xValue ?? cfg?.xValue);
22883
- const isGroupedVertical = stickToEdge && !hasExplicitXValue;
22884
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22885
- const groupedVerticalIndex = groupedVerticalIndexBySide[side] ?? 0;
22886
- const groupOffsetPx = item?.groupOffsetPx ?? 0;
22887
- geometry = computeVerticalLineGeometry({
22888
- chart,
22889
- chartArea,
22890
- item,
22891
- global: cfg,
22892
- xPx: isGroupedVertical ? baseXPx + dirIntoChart * groupedVerticalIndex * itemGapPx + dirIntoChart * groupOffsetPx : baseXPx
22893
- });
22894
- if (geometry && isGroupedVertical) {
22895
- groupedVerticalIndexBySide[side] = groupedVerticalIndex + 1;
22896
- const prevAnchor = groupedAnchorBySide[side];
22897
- groupedAnchorBySide[side] = isFiniteNumber(prevAnchor) ? side === LineMarkerSide.Left ? Math.min(prevAnchor, geometry.x1) : Math.max(prevAnchor, geometry.x1) : geometry.x1;
22898
- }
22899
- } else {
22900
- const stickToGroup = item?.stickToGroup ?? cfg?.stickToGroup ?? false;
22901
- geometry = computeHorizontalLineGeometry({
22902
- chart,
22903
- chartArea,
22904
- item,
22905
- global: cfg,
22906
- groupAnchorXPx: (() => {
22907
- const anchor = groupedAnchorBySide[side];
22908
- if (!isFiniteNumber(anchor)) return void 0;
22909
- return clamp(anchor, chartArea?.left, chartArea?.right);
22910
- })(),
22911
- previousGroupItemXPx: (() => {
22912
- const prev = previousGroupedHorizontalEndBySide[side];
22913
- if (!isFiniteNumber(prev)) return void 0;
22914
- return clamp(prev, chartArea?.left, chartArea?.right);
22915
- })()
22916
- });
22917
- if (geometry && stickToGroup) previousGroupedHorizontalEndBySide[side] = geometry.x2;
22918
- }
22919
- if (!geometry) return;
22920
- if (!hasVisibleLineSegment({ geometry })) return;
22921
- lineTasks.push({
22922
- geometry,
22923
- style
22924
- });
22925
- const lineReverse = getLineReverse(geometry);
22926
- const lineStart = {
22927
- x: geometry.x1,
22928
- y: geometry.y1
22929
- };
22930
- const lineEnd = {
22931
- x: geometry.x2,
22932
- y: geometry.y2
22933
- };
22934
- const startTick = resolveTick({
22935
- globalTick: cfg?.startTick,
22936
- itemTick: item?.startTick,
22937
- defaultEnabled: false
22938
- });
22939
- const endTick = resolveTick({
22940
- globalTick: cfg?.endTick,
22941
- itemTick: item?.endTick,
22942
- defaultEnabled: direction === LineMarkerDirection.Vertical
22943
- });
22944
- const resolvedStartSide = startTick?.side ?? side;
22945
- const resolvedEndSide = endTick?.side ?? side;
22946
- if ((startTick?.enabled || hasTickLabel(startTick)) && geometry.startInArea) tickTasks.push({
22947
- id: `${item?.id ?? `item-${itemIndex}`}-start`,
22948
- x: lineStart.x,
22949
- y: lineStart.y,
22950
- side: resolvedStartSide,
22951
- direction: geometry.direction,
22952
- reverse: startTick?.reverse ?? !lineReverse,
22953
- tick: startTick,
22954
- defaultColor: style.color,
22955
- defaultFont: DEFAULT_FONT
22956
- });
22957
- if ((endTick?.enabled || hasTickLabel(endTick)) && geometry.endInArea) tickTasks.push({
22958
- id: `${item?.id ?? `item-${itemIndex}`}-end`,
22959
- x: lineEnd.x,
22960
- y: lineEnd.y,
22961
- side: resolvedEndSide,
22962
- direction: geometry.direction,
22963
- reverse: endTick?.reverse ?? lineReverse,
22964
- tick: endTick,
22965
- defaultColor: style.color,
22966
- defaultFont: DEFAULT_FONT
22967
- });
22968
- if (direction !== LineMarkerDirection.Vertical) return;
22969
- asArray(item?.extras).forEach((extra, extraIndex) => {
22970
- if (!extra || extra?.display === false) return;
22971
- const extraGeometry = toExtraGeometry({
22972
- chart,
22973
- chartArea,
22974
- item,
22975
- extra,
22976
- xBase: geometry.x1,
22977
- defaultSide: side
22978
- });
22979
- if (!extraGeometry) return;
22980
- const extraStyle = getLineStyle(extra, cfg);
22981
- if (!extraStyle) return;
22982
- lineTasks.push({
22983
- geometry: extraGeometry,
22984
- style: extraStyle
22985
- });
22986
- const extraTick = resolveTick({
22987
- itemTick: extra?.tick,
22988
- defaultEnabled: false
22989
- });
22990
- if (!extraTick?.enabled && !hasTickLabel(extraTick) || !extraGeometry.endInArea) return;
22991
- const extraReverse = extraTick?.reverse ?? readOwnBoolean(extra, "reverse") ?? false;
22992
- tickTasks.push({
22993
- id: `${item?.id ?? `item-${itemIndex}`}-extra-${extra?.id ?? extraIndex}`,
22994
- x: extraGeometry.x2,
22995
- y: extraGeometry.y2,
22996
- side: extraTick?.side ?? extraGeometry.side ?? defaultSide,
22997
- direction: LineMarkerDirection.Horizontal,
22998
- reverse: extraReverse,
22999
- tick: extraTick,
23000
- defaultColor: extraStyle.color,
23001
- defaultFont: DEFAULT_FONT
23002
- });
22981
+ const baseXPx = computeXPxBase({
22982
+ chart,
22983
+ chartArea,
22984
+ item,
22985
+ global: cfg
23003
22986
  });
23004
- });
23005
- lineTasks.forEach(({ geometry, style }) => {
23006
- drawLineSegment({
22987
+ if (!isFiniteNumber(baseXPx)) return;
22988
+ const side = item?.stickSide ?? globalStickSide;
22989
+ const xPx = baseXPx + (side === "left" ? 1 : -1) * visibleIndex * itemGapPx;
22990
+ visibleIndex += 1;
22991
+ const ySpan = computeYSpan({
23007
22992
  chart,
23008
- x1: geometry.x1,
23009
- y1: geometry.y1,
23010
- x2: geometry.x2,
23011
- y2: geometry.y2,
23012
- color: style.color,
23013
- opacity: style.opacity,
23014
- lineWidth: style.lineWidth,
23015
- lineDash: style.lineDash
22993
+ chartArea,
22994
+ item,
22995
+ global: cfg
23016
22996
  });
23017
- });
23018
- const enableLabelCollisionResolver = cfg?.enableLabelCollisionResolver ?? true;
23019
- const labelCollisionPx = cfg?.labelCollisionPx ?? 4;
23020
- const labelCollisionClusterXPx = cfg?.labelCollisionClusterXPx ?? 36;
23021
- const labelYById = enableLabelCollisionResolver ? resolveLabelCollisions({
23022
- labels: tickTasks.filter((task) => {
23023
- return task?.tick?.enabled !== false && isFiniteNumber(getTickLabelHeight(task.tick, task.defaultFont)) && getTickLabelHeight(task.tick, task.defaultFont) > 0;
23024
- }).map((task) => ({
23025
- id: task.id,
23026
- side: task.side,
23027
- x: task.x,
23028
- y: task.y,
23029
- height: getTickLabelHeight(task.tick, task.defaultFont)
23030
- })),
23031
- chartTop: chartArea.top,
23032
- chartBottom: chartArea.bottom,
23033
- clusterXPx: labelCollisionClusterXPx,
23034
- gapPx: labelCollisionPx
23035
- }) : {};
23036
- tickTasks.forEach((task) => {
22997
+ if (!ySpan) return;
22998
+ const color = item?.color ?? cfg?.color;
22999
+ const opacity = item?.opacity ?? cfg?.opacity;
23000
+ const lineWidth = item?.lineWidth ?? cfg?.lineWidth;
23001
+ const lineDash = asArray(item?.lineDash ?? cfg?.lineDash);
23002
+ if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
23037
23003
  drawTick({
23038
23004
  ctx,
23039
- chartArea,
23040
- direction: task.direction,
23041
- x: task.x,
23042
- y: task.y,
23043
- side: task.side,
23044
- reverse: task.reverse,
23045
- labelY: labelYById[task.id],
23046
- tick: task.tick,
23047
- defaultColor: task.defaultColor,
23048
- defaultFont: task.defaultFont
23005
+ x: drawVerticalLine({
23006
+ chart,
23007
+ xPx,
23008
+ y1: ySpan?.yStartPx,
23009
+ y2: ySpan?.yEndPx,
23010
+ color,
23011
+ opacity,
23012
+ lineWidth,
23013
+ lineDash
23014
+ }),
23015
+ y: ySpan?.yEndPx,
23016
+ side,
23017
+ reverse: ySpan?.reverse,
23018
+ tick: ySpan?.endTick,
23019
+ defaultColor: color,
23020
+ defaultFont: "12px sans-serif"
23049
23021
  });
23050
23022
  });
23051
23023
  }
@@ -23517,7 +23489,7 @@ var Legend = ({ chartRef, legendConfig }) => {
23517
23489
  };
23518
23490
  //#endregion
23519
23491
  //#region src/components/line-chart/line-chart.tsx
23520
- Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, lineMarkersPlugin);
23492
+ Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, verticalMarkersPlugin);
23521
23493
  var LineChart = (props) => {
23522
23494
  setDefaultTheme();
23523
23495
  const chartRef = useRef(null);