@oliasoft-open-source/charts-library 5.10.0-beta-2 → 5.10.0-beta-3

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 +95 -64
  2. package/dist/index.js +874 -762
  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$3 = (x, from, to) => Math.min(to, Math.max(from, x));
14367
+ var clamp$2 = (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$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]);
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]);
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$2 = (x, from, to) => Math.min(to, Math.max(from, x));
15892
+ var clamp$1 = (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$2(obj[key], from, to);
15906
+ for (const key of Object.keys(obj)) obj[key] = clamp$1(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$2(toPercent(s), 0, 1);
15980
+ var toPositivePercent = (s) => clamp$1(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$2(opacity, 0, 1) : 1;
16475
+ return isNumber$1(opacity) ? clamp$1(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$2(Math.max(x, y), 0, .25);
17915
+ return clamp$1(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 = "hsl(60, 10.34482759%, 12.5%)";
18842
+ var DEFAULT_COLOR$1 = "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;
19027
+ defaults$1.color = DEFAULT_COLOR$1;
19028
19028
  defaults$1.borderColor = BORDER_COLOR;
19029
19029
  };
19030
19030
  var isEmptyString = (value) => value === "";
@@ -20190,20 +20190,33 @@ var defaultAnnotations$2 = (annotations) => ({
20190
20190
  controlAnnotation: annotationConfig?.controlAnnotation ?? false,
20191
20191
  enableDragAnnotation: annotationConfig?.enableDragAnnotation ?? false,
20192
20192
  enableCalloutAnnotation: annotationConfig?.enableCalloutAnnotation ?? false,
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 ?? []
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 ?? []
20207
20220
  },
20208
20221
  annotationsData: defaultAnnotationsData$2(Array.isArray(annotations) ? annotations : annotations?.annotationsData)
20209
20222
  };
@@ -20879,511 +20892,87 @@ function addTransparency(input, alpha, out = "rgb") {
20879
20892
  }
20880
20893
  //#endregion
20881
20894
  //#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_SIDE_SPREAD_STEP = 12;
20893
- var DEFAULT_SIDE_SPREAD_MAX = 48;
20894
- var clamp$1 = (value, min, max) => {
20895
- if (Number.isNaN(value)) return min;
20896
- if (min > max) return min;
20897
- return Math.min(Math.max(value, min), max);
20898
- };
20899
- var getNumericValue = (value, fallback = 0) => {
20900
- return typeof value === "number" ? value : fallback;
20901
- };
20902
- var getCalloutCfg = (annotation) => {
20903
- return annotation?.labelConfig?.callout ?? {};
20904
- };
20905
- var getChartFromCtx$1 = (ctx) => {
20906
- return ctx?.chart?.chart ?? ctx?.chart;
20907
- };
20908
20895
  var getXScale = (chart, axisId) => {
20909
20896
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20910
20897
  if (chart?.scales?.x) return chart.scales.x;
20911
- const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20912
- return item?.toLowerCase()?.includes("x");
20913
- });
20898
+ const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("x"));
20914
20899
  return key ? chart?.scales?.[key] : void 0;
20915
20900
  };
20916
20901
  var getYScale = (chart, axisId) => {
20917
20902
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20918
20903
  if (chart?.scales?.y) return chart.scales.y;
20919
- const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20920
- return item?.toLowerCase()?.includes("y");
20921
- });
20904
+ const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
20922
20905
  return key ? chart?.scales?.[key] : void 0;
20923
20906
  };
20924
- var resolveFontSize = (font) => {
20925
- if (typeof font === "string") {
20926
- const match = font.match(/(\d+(?:\.\d+)?)px/);
20927
- return match ? Number(match[1]) : DEFAULT_FONT_SIZE;
20928
- }
20929
- if (font && typeof font === "object" && "size" in font) {
20930
- const size = Number(font.size);
20931
- return Number.isFinite(size) ? size : DEFAULT_FONT_SIZE;
20932
- }
20933
- return DEFAULT_FONT_SIZE;
20934
- };
20935
- var resolvePadding = (padding) => {
20936
- if (typeof padding === "number") return {
20937
- top: padding,
20938
- right: padding,
20939
- bottom: padding,
20940
- left: padding
20941
- };
20942
- if (padding && typeof padding === "object") {
20943
- const typedPadding = padding;
20944
- return {
20945
- top: Number(typedPadding.top ?? DEFAULT_PADDING),
20946
- right: Number(typedPadding.right ?? DEFAULT_PADDING),
20947
- bottom: Number(typedPadding.bottom ?? DEFAULT_PADDING),
20948
- left: Number(typedPadding.left ?? DEFAULT_PADDING)
20949
- };
20950
- }
20951
- return {
20952
- top: DEFAULT_PADDING,
20953
- right: DEFAULT_PADDING,
20954
- bottom: DEFAULT_PADDING,
20955
- left: DEFAULT_PADDING
20956
- };
20957
- };
20958
- var estimateLabelSize = (chart, annotation, calloutCfg) => {
20959
- const content = `${annotation?.label ?? ""}`;
20960
- const fontSize = resolveFontSize(calloutCfg?.font);
20961
- const padding = resolvePadding(calloutCfg?.padding);
20962
- const lines = content.split("\n");
20963
- const fallbackWidth = Math.max(...lines.map((line) => line.length), 0) * fontSize * .62;
20964
- let measuredWidth = fallbackWidth;
20965
- if (chart?.ctx) {
20966
- chart.ctx.save();
20967
- chart.ctx.font = typeof calloutCfg?.font === "string" ? calloutCfg.font : `${fontSize}px sans-serif`;
20968
- measuredWidth = Math.max(...lines.map((line) => chart.ctx.measureText(line).width), fallbackWidth);
20969
- chart.ctx.restore();
20970
- }
20971
- return {
20972
- width: measuredWidth + padding.left + padding.right,
20973
- height: lines.length * fontSize + padding.top + padding.bottom
20974
- };
20975
- };
20976
- var getAxisId = (annotation, axis) => {
20977
- const axisScaleId = axis === "x" ? annotation?.xScaleID : annotation?.yScaleID;
20978
- if (axisScaleId) return axisScaleId;
20979
- if (annotation?.annotationAxis?.startsWith(axis)) return annotation.annotationAxis;
20980
- };
20981
- var getAnchorPixel = (chart, annotation, axis, area) => {
20982
- const scale = axis === "x" ? getXScale(chart, getAxisId(annotation, "x")) : getYScale(chart, getAxisId(annotation, "y"));
20983
- const directValue = axis === "x" ? annotation?.xValue : annotation?.yValue;
20984
- if (typeof directValue === "number" && scale) return scale.getPixelForValue(directValue);
20985
- const minKey = axis === "x" ? "xMin" : "yMin";
20986
- const maxKey = axis === "x" ? "xMax" : "yMax";
20987
- const minValue = annotation?.[minKey];
20988
- const maxValue = annotation?.[maxKey];
20989
- if (typeof minValue === "number" && typeof maxValue === "number" && scale) return (scale.getPixelForValue(minValue) + scale.getPixelForValue(maxValue)) / 2;
20990
- if (typeof minValue === "number" && scale) return scale.getPixelForValue(minValue);
20991
- if (typeof maxValue === "number" && scale) return scale.getPixelForValue(maxValue);
20992
- return axis === "x" ? (area.left + area.right) / 2 : (area.top + area.bottom) / 2;
20993
- };
20994
- var hasAxisAnchor = (annotation, axis) => {
20995
- 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";
20996
- return typeof annotation?.yValue === "number" || typeof annotation?.yMin === "number" || typeof annotation?.yMax === "number" || annotation?.type === "box" || annotation?.type === "label" || annotation?.type === "ellipse";
20997
- };
20998
- var getBoxHalfWidth = (chart, area, annotation) => {
20999
- const xScale = getXScale(chart, getAxisId(annotation, "x"));
21000
- if (xScale && typeof annotation?.xMin === "number" && typeof annotation?.xMax === "number") return Math.abs(xScale.getPixelForValue(annotation.xMax) - xScale.getPixelForValue(annotation.xMin)) / 2;
21001
- if (annotation?.type === "box") return (area.right - area.left) / 2;
21002
- return annotation?.radius ?? 0;
21003
- };
21004
- var getBoxHalfHeight = (chart, annotation) => {
21005
- const yScale = getYScale(chart, getAxisId(annotation, "y"));
21006
- if (yScale && typeof annotation?.yMin === "number" && typeof annotation?.yMax === "number") return Math.abs(yScale.getPixelForValue(annotation.yMax) - yScale.getPixelForValue(annotation.yMin)) / 2;
21007
- return annotation?.radius ?? 0;
21008
- };
21009
- var getReferencePoint$1 = (chart, area, annotation) => {
21010
- const anchorX = getAnchorPixel(chart, annotation, "x", area);
21011
- const anchorY = getAnchorPixel(chart, annotation, "y", area);
21012
- const halfWidth = getBoxHalfWidth(chart, area, annotation);
21013
- const halfHeight = getBoxHalfHeight(chart, annotation);
21014
- const labelOffsetPx = getNumericValue(annotation?.labelOffsetPx, 14);
21015
- const xAdjust = getNumericValue(annotation?.labelConfig?.xAdjust, 0);
21016
- const yAdjust = getNumericValue(annotation?.labelConfig?.yAdjust, 0);
21017
- const position = annotation?.labelConfig?.position;
21018
- let x = anchorX;
21019
- let y = anchorY;
21020
- let side = anchorX >= (area.left + area.right) / 2 ? "right" : "left";
21021
- switch (position) {
21022
- case Position.Left:
21023
- x -= Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21024
- side = "left";
21025
- break;
21026
- case Position.Right:
21027
- x += Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21028
- side = "right";
21029
- break;
21030
- case Position.TopLeft:
21031
- x -= halfWidth;
21032
- y -= halfHeight;
21033
- side = "left";
21034
- break;
21035
- case Position.TopRight:
21036
- x += halfWidth;
21037
- y -= halfHeight;
21038
- side = "right";
21039
- break;
21040
- case Position.BottomLeft:
21041
- x -= halfWidth;
21042
- y += halfHeight;
21043
- side = "left";
21044
- break;
21045
- case Position.BottomRight:
21046
- x += halfWidth;
21047
- y += halfHeight;
21048
- side = "right";
21049
- break;
21050
- case Position.Bottom:
21051
- y += Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21052
- break;
21053
- case Position.Top:
21054
- default:
21055
- y -= Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21056
- break;
21057
- }
21058
- return {
21059
- x: x + xAdjust,
21060
- y: y + yAdjust,
21061
- side
21062
- };
21063
- };
21064
- var getOriginalLabelRect = (chart, area, annotation, index) => {
21065
- const size = estimateLabelSize(chart, annotation, getCalloutCfg(annotation));
21066
- const reference = getReferencePoint$1(chart, area, annotation);
21067
- const direction = reference.side === "right" ? 1 : -1;
21068
- const centerX = reference.x + direction * (size.width / 2);
21069
- const centerY = reference.y;
21070
- return {
21071
- id: `callout-annotation-${index}`,
21072
- side: reference.side,
21073
- left: centerX - size.width / 2,
21074
- right: centerX + size.width / 2,
21075
- top: centerY - size.height / 2,
21076
- bottom: centerY + size.height / 2
21077
- };
21078
- };
21079
- var isRectOverlapping = (left, right) => {
21080
- return !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
21081
- };
21082
- var getOverlappingCalloutIds = (chart) => {
21083
- const area = chart?.chartArea;
21084
- const annotationsData = (chart?.options)?.annotations?.annotationsData ?? [];
21085
- if (!chart || !area) return /* @__PURE__ */ new Set();
21086
- const rects = annotationsData.flatMap((annotation, index) => {
21087
- const calloutCfg = getCalloutCfg(annotation);
21088
- if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
21089
- return [getOriginalLabelRect(chart, area, annotation, index)];
21090
- });
21091
- const overlappingIds = /* @__PURE__ */ new Set();
21092
- rects.forEach((rect, index) => {
21093
- rects.slice(index + 1).forEach((otherRect) => {
21094
- if (rect.side !== otherRect.side) return;
21095
- if (!isRectOverlapping(rect, otherRect)) return;
21096
- overlappingIds.add(rect.id);
21097
- overlappingIds.add(otherRect.id);
21098
- });
21099
- });
21100
- return overlappingIds;
21101
- };
21102
- var getOccupiedOriginalLabelRects = (chart, area, overlappingIds) => {
21103
- return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
21104
- const calloutCfg = getCalloutCfg(annotation);
21105
- const id = `callout-annotation-${index}`;
21106
- if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
21107
- if (overlappingIds.has(id)) return [];
21108
- return [getOriginalLabelRect(chart, area, annotation, index)];
21109
- });
21110
- };
21111
- var isCalloutOverlapping = (ctx, refAnnotation, index) => {
21112
- const chart = getChartFromCtx$1(ctx);
21113
- if (!getCalloutCfg(refAnnotation)?.onlyWhenOverlapping) return true;
21114
- if (!chart?.chartArea) return false;
21115
- return getOverlappingCalloutIds(chart).has(`callout-annotation-${index}`);
21116
- };
21117
- var getPreferredX = (chart, area, annotation, calloutCfg, width) => {
21118
- const xScale = getXScale(chart, getAxisId(annotation, "x"));
21119
- const explicitXValue = typeof calloutCfg?.xValue === "number" ? calloutCfg.xValue : void 0;
21120
- if (typeof explicitXValue === "number" && xScale) return xScale.getPixelForValue(explicitXValue);
21121
- const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21122
- const halfWidth = width / 2;
21123
- const reference = getReferencePoint$1(chart, area, annotation);
21124
- const direction = reference.side === "right" ? 1 : -1;
21125
- const gap = getNumericValue(calloutCfg?.gapPx, DEFAULT_LABEL_GAP);
21126
- const minConnectorLength = getNumericValue(calloutCfg?.minConnectorLengthPx, DEFAULT_MIN_CONNECTOR_LENGTH);
21127
- return clamp$1(reference.x + direction * (halfWidth + gap + minConnectorLength), area.left + margin + halfWidth, area.right - margin - halfWidth);
21128
- };
21129
- var getPreferredY = (chart, area, annotation, calloutCfg) => {
21130
- const yScale = getYScale(chart, getAxisId(annotation, "y"));
21131
- const explicitYValue = typeof calloutCfg?.yValue === "number" ? calloutCfg.yValue : void 0;
21132
- if (typeof explicitYValue === "number" && yScale) return yScale.getPixelForValue(explicitYValue);
21133
- const reference = getReferencePoint$1(chart, area, annotation);
21134
- const dy = typeof calloutCfg?.yAdjust === "number" ? 0 : DEFAULT_VERTICAL_OFFSET;
21135
- return reference.y + dy;
21136
- };
21137
- var getCalloutItems = (chart, area) => {
21138
- const overlappingIds = getOverlappingCalloutIds(chart);
21139
- return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
21140
- const calloutCfg = getCalloutCfg(annotation);
21141
- const id = `callout-annotation-${index}`;
21142
- if (!calloutCfg?.enabled) return [];
21143
- if (calloutCfg?.onlyWhenOverlapping && !overlappingIds.has(id)) return [];
21144
- const size = estimateLabelSize(chart, annotation, calloutCfg);
21145
- const xScale = getXScale(chart, getAxisId(annotation, "x"));
21146
- const yScale = getYScale(chart, getAxisId(annotation, "y"));
21147
- const reference = getReferencePoint$1(chart, area, annotation);
21148
- const preferredX = getPreferredX(chart, area, annotation, calloutCfg, size.width);
21149
- const preferredY = getPreferredY(chart, area, annotation, calloutCfg);
21150
- const hasStoredX = typeof calloutCfg?.xValue === "number" || !xScale;
21151
- const hasStoredY = typeof calloutCfg?.yValue === "number" || !yScale;
21152
- const side = reference.side;
21153
- const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21154
- return [{
21155
- annotation,
21156
- calloutCfg,
21157
- id,
21158
- index,
21159
- anchorY: reference.y,
21160
- preferredX,
21161
- preferredY,
21162
- width: size.width,
21163
- height: size.height,
21164
- isFixed: hasStoredX && hasStoredY,
21165
- overlapOnly: Boolean(calloutCfg?.onlyWhenOverlapping),
21166
- minX: area.left + margin + size.width / 2,
21167
- maxX: area.right - margin - size.width / 2,
21168
- minY: area.top + margin + size.height / 2,
21169
- maxY: area.bottom - margin - size.height / 2,
21170
- side
21171
- }];
21172
- });
21173
- };
21174
- var hasVerticalOverlap = (items) => {
21175
- const sorted = [...items].sort((left, right) => left.preferredY - right.preferredY);
21176
- for (let index = 1; index < sorted.length; index += 1) {
21177
- const previous = sorted[index - 1];
21178
- const current = sorted[index];
21179
- const gap = previous.height / 2 + current.height / 2 + DEFAULT_ITEM_GAP;
21180
- if (current.preferredY - previous.preferredY < gap) return true;
21181
- }
21182
- return false;
21183
- };
21184
- var overlapsOccupiedRect = (centerY, height, occupiedRects) => {
21185
- const top = centerY - height / 2;
21186
- const bottom = centerY + height / 2;
21187
- return occupiedRects.find((rect) => !(bottom <= rect.top || top >= rect.bottom));
21188
- };
21189
- var getLayoutGap = (item) => {
21190
- return item.overlapOnly ? DEFAULT_OVERLAP_ONLY_ITEM_GAP : DEFAULT_ITEM_GAP;
21191
- };
21192
- var getLayoutOrderY = (item) => {
21193
- return item.isFixed ? item.preferredY : item.anchorY;
21194
- };
21195
- var layoutSide = (items, occupiedRects = []) => {
21196
- const sorted = [...items].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right));
21197
- const result = /* @__PURE__ */ new Map();
21198
- if (!sorted.filter((item) => !item.isFixed).length) {
21199
- sorted.forEach((item) => {
21200
- result.set(item.id, {
21201
- xValue: item.preferredX,
21202
- yValue: item.preferredY
21203
- });
21204
- });
21205
- return result;
21206
- }
21207
- const shouldReflow = sorted.some((item) => !item.overlapOnly) || hasVerticalOverlap(sorted);
21208
- sorted.forEach((item) => {
21209
- result.set(item.id, {
21210
- xValue: item.preferredX,
21211
- yValue: clamp$1(item.preferredY, item.minY, item.maxY)
21212
- });
21213
- });
21214
- if (!shouldReflow) return result;
21215
- let previousBottom = Number.NEGATIVE_INFINITY;
21216
- sorted.forEach((item) => {
21217
- if (item.isFixed) {
21218
- const yValue = clamp$1(item.preferredY, item.minY, item.maxY);
21219
- result.set(item.id, {
21220
- xValue: item.preferredX,
21221
- yValue
21222
- });
21223
- previousBottom = yValue + item.height / 2;
21224
- return;
21225
- }
21226
- const minCenter = Math.max(item.minY, previousBottom + item.height / 2 + getLayoutGap(item));
21227
- let yValue = clamp$1(item.preferredY, minCenter, item.maxY);
21228
- let overlappingRect = overlapsOccupiedRect(yValue, item.height, occupiedRects);
21229
- while (overlappingRect) {
21230
- yValue = clamp$1(overlappingRect.bottom + getLayoutGap(item) + item.height / 2, item.minY, item.maxY);
21231
- overlappingRect = overlapsOccupiedRect(yValue, item.height, occupiedRects);
21232
- }
21233
- result.set(item.id, {
21234
- xValue: item.preferredX,
21235
- yValue
21236
- });
21237
- previousBottom = yValue + item.height / 2;
21238
- });
21239
- let nextTop = Number.POSITIVE_INFINITY;
21240
- [...sorted].reverse().forEach((item) => {
21241
- const current = result.get(item.id);
21242
- if (!current) return;
21243
- if (item.isFixed) {
21244
- nextTop = current.yValue - item.height / 2;
21245
- return;
21246
- }
21247
- const maxCenter = Math.min(item.maxY, nextTop - item.height / 2 - getLayoutGap(item));
21248
- const yValue = clamp$1(current.yValue, item.minY, maxCenter);
21249
- result.set(item.id, {
21250
- ...current,
21251
- yValue
21252
- });
21253
- nextTop = yValue - item.height / 2;
21254
- });
21255
- const overlapOnlyMovable = sorted.filter((item) => {
21256
- return item.overlapOnly && !item.isFixed;
21257
- });
21258
- if (overlapOnlyMovable.length > 1) {
21259
- const sortedOverlapOnlyMovable = [...overlapOnlyMovable].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right));
21260
- const sharedOuterEdge = sortedOverlapOnlyMovable[0]?.side === "right" ? Math.max(...sortedOverlapOnlyMovable.map((item) => {
21261
- return (result.get(item.id)?.xValue ?? item.preferredX) + item.width / 2;
21262
- })) : Math.min(...sortedOverlapOnlyMovable.map((item) => {
21263
- return (result.get(item.id)?.xValue ?? item.preferredX) - item.width / 2;
21264
- }));
21265
- sortedOverlapOnlyMovable.forEach((item, index) => {
21266
- const current = result.get(item.id);
21267
- if (!current) return;
21268
- const spreadOffset = Math.min(index * DEFAULT_SIDE_SPREAD_STEP, DEFAULT_SIDE_SPREAD_MAX);
21269
- const direction = item.side === "right" ? -1 : 1;
21270
- const alignedXValue = item.side === "right" ? sharedOuterEdge - item.width / 2 : sharedOuterEdge + item.width / 2;
21271
- result.set(item.id, {
21272
- ...current,
21273
- xValue: clamp$1(alignedXValue + direction * spreadOffset, item.minX, item.maxX)
21274
- });
21275
- });
21276
- return result;
21277
- }
21278
- sorted.forEach((item, index) => {
21279
- const current = result.get(item.id);
21280
- if (!current || item.isFixed) return;
21281
- const spreadOffset = Math.min(index * DEFAULT_SIDE_SPREAD_STEP, DEFAULT_SIDE_SPREAD_MAX);
21282
- const direction = item.side === "right" ? -1 : 1;
21283
- const xValue = clamp$1(current.xValue + direction * spreadOffset, item.minX, item.maxX);
21284
- result.set(item.id, {
21285
- ...current,
21286
- xValue
21287
- });
21288
- });
21289
- return result;
21290
- };
21291
- var getCalloutLayoutPixels = (chart) => {
21292
- const area = chart?.chartArea;
21293
- if (!chart || !area) return /* @__PURE__ */ new Map();
21294
- const occupiedRects = getOccupiedOriginalLabelRects(chart, area, getOverlappingCalloutIds(chart));
21295
- const grouped = getCalloutItems(chart, area).map((item) => {
21296
- const xValue = clamp$1(item.preferredX, item.minX, item.maxX);
21297
- return {
21298
- ...item,
21299
- preferredX: xValue
21300
- };
21301
- }).reduce((accumulator, item) => {
21302
- accumulator[item.side].push(item);
21303
- return accumulator;
21304
- }, {
21305
- left: [],
21306
- right: []
21307
- });
21308
- const leftLayout = layoutSide(grouped.left, occupiedRects.filter((rect) => rect.side === "left").map((rect) => ({
21309
- top: rect.top,
21310
- bottom: rect.bottom
21311
- })));
21312
- const rightLayout = layoutSide(grouped.right, occupiedRects.filter((rect) => rect.side === "right").map((rect) => ({
21313
- top: rect.top,
21314
- bottom: rect.bottom
21315
- })));
21316
- return new Map([...leftLayout.entries(), ...rightLayout.entries()]);
21317
- };
21318
- var getCalloutLayout = (ctx, refAnnotation, index) => {
21319
- const chart = getChartFromCtx$1(ctx);
21320
- const xScale = chart ? getXScale(chart, getAxisId(refAnnotation, "x")) : void 0;
21321
- const yScale = chart ? getYScale(chart, getAxisId(refAnnotation, "y")) : void 0;
21322
- if (!chart || !yScale || !chart.chartArea) return null;
21323
- if (!xScale && hasAxisAnchor(refAnnotation, "x")) return null;
21324
- if (!xScale) return null;
21325
- const layout = getCalloutLayoutPixels(chart).get(`callout-annotation-${index}`);
21326
- if (!layout) return null;
21327
- return {
21328
- xValue: xScale.getValueForPixel(layout.xValue),
21329
- yValue: yScale.getValueForPixel(layout.yValue)
21330
- };
21331
- };
21332
20907
  var resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
21333
- const layout = getCalloutLayout(ctx, refAnnotation, index);
21334
- if (layout) return layout.xValue;
21335
- const chart = getChartFromCtx$1(ctx);
20908
+ const chart = ctx?.chart;
21336
20909
  const xVal = refAnnotation?.xValue;
21337
20910
  if (typeof xVal !== "number") return 0;
21338
20911
  if (!chart) return xVal;
21339
- const scale = getXScale(chart, getAxisId(refAnnotation, "x"));
20912
+ const scale = getXScale(chart, refAnnotation?.annotationAxis);
21340
20913
  if (!scale) return xVal;
21341
20914
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
21342
20915
  const side = index % 2 === 0 ? 1 : -1;
21343
- const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * DEFAULT_HORIZONTAL_OFFSET;
20916
+ const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
21344
20917
  const basePx = scale.getPixelForValue(xVal);
20918
+ const cxBase = basePx + dx;
21345
20919
  const area = chart.chartArea;
21346
- const element = ctx?.element;
21347
- if (!area || !element) return scale.getValueForPixel(basePx + dx);
21348
- const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21349
- const halfWidth = (element?.width ?? 0) / 2;
21350
- const clampedPx = clamp$1(basePx + dx, area.left + margin + halfWidth, area.right - margin - halfWidth);
21351
- return scale.getValueForPixel(clampedPx);
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);
21352
20934
  };
21353
20935
  var resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
21354
- const layout = getCalloutLayout(ctx, refAnnotation, index);
21355
- if (layout) return layout.yValue;
21356
- const chart = getChartFromCtx$1(ctx);
21357
- const yVal = refAnnotation?.yValue;
20936
+ const chart = ctx?.chart;
20937
+ const yVal = refAnnotation.yValue;
21358
20938
  if (typeof yVal !== "number") return 0;
21359
20939
  if (!chart) return yVal;
21360
- const scale = getYScale(chart, getAxisId(refAnnotation, "y"));
20940
+ const scale = getYScale(chart, refAnnotation?.annotationAxis);
21361
20941
  if (!scale) return yVal;
21362
20942
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
21363
20943
  const row = Math.floor(index / 2);
21364
- const dy = hasCustomOffset ? calloutCfg?.yAdjust : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
20944
+ const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
21365
20945
  const basePx = scale.getPixelForValue(yVal);
20946
+ const cyBase = basePx + dy;
21366
20947
  const area = chart.chartArea;
21367
- const element = ctx?.element;
21368
- if (!area || !element) return scale.getValueForPixel(basePx + dy);
21369
- const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21370
- const halfHeight = (element?.height ?? 0) / 2;
21371
- const clampedPx = clamp$1(basePx + dy, area.top + margin + halfHeight, area.bottom - margin - halfHeight);
21372
- return scale.getValueForPixel(clampedPx);
21373
- };
21374
- var isInRange = (value, min, max) => value >= min && value <= max;
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;
21375
20964
  var isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
21376
- const chart = getChartFromCtx$1(ctx);
20965
+ const chart = ctx?.chart;
21377
20966
  const area = chart?.chartArea;
21378
20967
  if (!chart || !area) return true;
21379
- const xScale = getXScale(chart, getAxisId(refAnnotation, "x"));
21380
- const yScale = getYScale(chart, getAxisId(refAnnotation, "y"));
21381
- const hasXValues = hasAxisAnchor(refAnnotation, "x");
21382
- const hasYValues = hasAxisAnchor(refAnnotation, "y");
21383
- if (hasXValues && !xScale || hasYValues && !yScale) return false;
21384
- if (!hasXValues || !hasYValues) return false;
21385
- const x = getAnchorPixel(chart, refAnnotation, "x", area);
21386
- const y = getAnchorPixel(chart, refAnnotation, "y", area);
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);
21387
20976
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
21388
20977
  };
21389
20978
  //#endregion
@@ -21684,14 +21273,7 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21684
21273
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
21685
21274
  const baseAnnotation = {
21686
21275
  ...baseRest,
21687
- label: {
21688
- ...label,
21689
- display: (ctx) => {
21690
- if (!(refAnnotation.display ?? true)) return false;
21691
- if (!calloutCfg?.onlyWhenOverlapping) return false;
21692
- return !isCalloutOverlapping(ctx, refAnnotation, index);
21693
- }
21694
- }
21276
+ label: { display: false }
21695
21277
  };
21696
21278
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
21697
21279
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
@@ -21699,10 +21281,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21699
21281
  const onCalloutDragStart = typeof calloutCfg?.onDragStart === "function" ? calloutCfg.onDragStart : void 0;
21700
21282
  const onCalloutDrag = typeof calloutCfg?.onDrag === "function" ? calloutCfg.onDrag : void 0;
21701
21283
  const onCalloutDragEnd = typeof calloutCfg?.onDragEnd === "function" ? calloutCfg.onDragEnd : void 0;
21702
- const strokeStyle = calloutCfg.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
21284
+ const strokeStyle = calloutCfg.strokeStyle ?? baseAnnotation.borderColor;
21703
21285
  const lineWidth = typeof calloutCfg.lineWidth === "number" ? calloutCfg.lineWidth : 1;
21704
- const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 2;
21705
- const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : .5;
21286
+ const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 6;
21287
+ const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : 1;
21706
21288
  const calloutId = `callout-annotation-${index}`;
21707
21289
  return [baseAnnotation, {
21708
21290
  id: calloutId,
@@ -21712,18 +21294,14 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21712
21294
  color: (ctx) => {
21713
21295
  const chart = getChartFromCtx(ctx);
21714
21296
  if (!chart) return color;
21715
- if (Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) && chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21297
+ if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21716
21298
  return color;
21717
21299
  },
21718
21300
  font,
21719
21301
  borderColor,
21720
21302
  borderWidth: BORDER_WIDTH.INITIAL,
21721
21303
  padding: 5,
21722
- display: (ctx) => {
21723
- if (!(refAnnotation.display ?? true)) return false;
21724
- if (!isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0)) return false;
21725
- return isCalloutOverlapping(ctx, refAnnotation, index);
21726
- },
21304
+ display: (ctx) => (refAnnotation.display ?? true) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0),
21727
21305
  xValue: (ctx) => {
21728
21306
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
21729
21307
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
@@ -21740,10 +21318,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21740
21318
  displayDragCoordinates: false,
21741
21319
  enableDrag: true,
21742
21320
  enter: ({ element }, { chart }) => {
21743
- return handleLabelEnter(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21321
+ return handleLabelEnter(element, chart, { enableDrag: true });
21744
21322
  },
21745
21323
  leave: ({ element }, { chart }) => {
21746
- return handleLabelLeave(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21324
+ return handleLabelLeave(element, chart, { enableDrag: true });
21747
21325
  },
21748
21326
  onDragStart: onCalloutDragStart ? () => (coords) => {
21749
21327
  return onCalloutDragStart(coords, refAnnotation);
@@ -21760,13 +21338,7 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21760
21338
  strokeStyle,
21761
21339
  lineWidth,
21762
21340
  startOffset,
21763
- endOffset,
21764
- anchor: {
21765
- position: refAnnotation?.labelConfig?.position,
21766
- xAdjust: refAnnotation?.labelConfig?.xAdjust,
21767
- yAdjust: refAnnotation?.labelConfig?.yAdjust,
21768
- labelOffsetPx: refAnnotation?.labelOffsetPx
21769
- }
21341
+ endOffset
21770
21342
  }
21771
21343
  }];
21772
21344
  };
@@ -21947,6 +21519,11 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21947
21519
  state
21948
21520
  ]);
21949
21521
  const dragData = useMemo(() => enableDragPoints && getDraggableData(options), [enableDragPoints, options]);
21522
+ const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
21523
+ generatedDatasets,
21524
+ hoveredPoint,
21525
+ onHover
21526
+ ]);
21950
21527
  const panOptions = useMemo(() => ({
21951
21528
  enabled: panEnabled,
21952
21529
  mode: PanZoomMode.XY,
@@ -21977,13 +21554,13 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21977
21554
  updateAxesRangesFromChart,
21978
21555
  zoomEnabled
21979
21556
  ]);
21980
- const verticalMarkersAnnotation = options?.annotations?.verticalMarkersAnnotation ?? {};
21981
- const verticalMarkersPluginEnabled = Boolean(options?.plugins?.verticalMarkersPlugin?.enabled ?? verticalMarkersAnnotation?.enabled);
21982
- const plugins = useMemo(() => ({
21557
+ const lineMarkersAnnotation = options?.annotations?.lineMarkersAnnotation ?? {};
21558
+ const lineMarkersPluginEnabled = Boolean(options?.plugins?.lineMarkersPlugin?.enabled ?? lineMarkersAnnotation?.enabled);
21559
+ const plugins = {
21983
21560
  datalabels,
21984
21561
  annotationDraggerPlugin: { enabled: state?.enableDragAnnotation },
21985
21562
  calloutConnectorPlugin: { enableCalloutAnnotation: options?.annotations.enableCalloutAnnotation },
21986
- verticalMarkersPlugin: { enabled: verticalMarkersPluginEnabled },
21563
+ lineMarkersPlugin: { enabled: lineMarkersPluginEnabled },
21987
21564
  annotation: toAnnotationObject(annotation),
21988
21565
  zoom: {
21989
21566
  pan: panOptions,
@@ -22008,32 +21585,7 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22008
21585
  lineHeight
22009
21586
  },
22010
21587
  ...dragData
22011
- }), [
22012
- annotation,
22013
- customLegendPlugin,
22014
- datalabels,
22015
- dragData,
22016
- fontSize,
22017
- legend,
22018
- lineHeight,
22019
- maxWidth,
22020
- options?.annotations.enableCalloutAnnotation,
22021
- panOptions,
22022
- position,
22023
- showLabel,
22024
- state?.enableDragAnnotation,
22025
- text,
22026
- tooltip,
22027
- verticalMarkersPluginEnabled,
22028
- xOffset,
22029
- yOffset,
22030
- zoomOptions
22031
- ]);
22032
- const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
22033
- generatedDatasets,
22034
- hoveredPoint,
22035
- onHover
22036
- ]);
21588
+ };
22037
21589
  return useMemo(() => ({
22038
21590
  persistenceId,
22039
21591
  layout: { padding: layoutPadding },
@@ -22061,7 +21613,6 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22061
21613
  layoutPadding,
22062
21614
  lineEnabled,
22063
21615
  onAnimationComplete,
22064
- onChartHover,
22065
21616
  options?.annotations,
22066
21617
  options?.chartStyling?.maintainAspectRatio,
22067
21618
  options?.chartStyling?.performanceMode,
@@ -22587,126 +22138,52 @@ var annotationDraggerPlugin = {
22587
22138
  };
22588
22139
  //#endregion
22589
22140
  //#region src/components/line-chart/plugins/callout-plugin/helpers.ts
22590
- var getRectangleEdgePoint = (el, targetX, targetY, offset) => {
22591
- const width = el?.width ?? 0;
22592
- const height = el?.height ?? 0;
22593
- if (width <= 0 || height <= 0) return {
22594
- x: el.centerX,
22595
- y: el.centerY
22596
- };
22597
- const halfWidth = width / 2;
22598
- const halfHeight = height / 2;
22599
- const dx = targetX - el.centerX;
22600
- const dy = targetY - el.centerY;
22601
- const length = Math.sqrt(dx * dx + dy * dy) || 1;
22602
- const ux = dx / length;
22603
- const uy = dy / length;
22604
- const tx = Math.abs(ux) > 1e-6 ? halfWidth / Math.abs(ux) : Number.POSITIVE_INFINITY;
22605
- const ty = Math.abs(uy) > 1e-6 ? halfHeight / Math.abs(uy) : Number.POSITIVE_INFINITY;
22606
- const distance = Math.min(tx, ty) + offset;
22607
- return {
22608
- x: el.centerX + ux * distance,
22609
- y: el.centerY + uy * distance
22610
- };
22611
- };
22612
- var getLabelEdgePoint = (labelEl, startX, endOffset) => {
22613
- const width = labelEl?.width ?? 0;
22614
- const height = labelEl?.height ?? 0;
22615
- if (width <= 0 || height <= 0) return {
22616
- x: labelEl.centerX,
22617
- y: labelEl.centerY
22618
- };
22619
- const halfWidth = width / 2;
22620
- const direction = startX <= labelEl.centerX ? -1 : 1;
22621
- return {
22622
- x: labelEl.centerX + direction * (halfWidth + endOffset),
22623
- y: labelEl.centerY
22624
- };
22625
- };
22626
- var getReferencePoint = (fromEl, connector) => {
22627
- const { anchor } = connector;
22628
- if (!anchor) return {
22629
- x: fromEl.centerX,
22630
- y: fromEl.centerY
22631
- };
22632
- const width = fromEl?.width ?? 0;
22633
- const height = fromEl?.height ?? 0;
22634
- const radius = fromEl?.options?.radius ?? 0;
22635
- const halfWidth = width / 2;
22636
- const halfHeight = height / 2;
22637
- const labelOffset = anchor?.labelOffsetPx ?? 14;
22638
- const xAdjust = anchor?.xAdjust ?? 0;
22639
- const yAdjust = anchor?.yAdjust ?? 0;
22640
- const position = anchor?.position ?? "top";
22641
- let x = fromEl.centerX;
22642
- let y = fromEl.centerY;
22643
- switch (position) {
22644
- case "left":
22645
- x -= Math.max(halfWidth, radius) + labelOffset;
22646
- break;
22647
- case "right":
22648
- x += Math.max(halfWidth, radius) + labelOffset;
22649
- break;
22650
- case "bottom":
22651
- y += Math.max(halfHeight, radius) + labelOffset;
22652
- break;
22653
- case "top-left":
22654
- x -= halfWidth;
22655
- y -= halfHeight;
22656
- break;
22657
- case "top-right":
22658
- x += halfWidth;
22659
- y -= halfHeight;
22660
- break;
22661
- case "bottom-left":
22662
- x -= halfWidth;
22663
- y += halfHeight;
22664
- break;
22665
- case "bottom-right":
22666
- x += halfWidth;
22667
- y += halfHeight;
22668
- break;
22669
- default:
22670
- y -= Math.max(halfHeight, radius) + labelOffset;
22671
- break;
22672
- }
22673
- return {
22674
- x: x + xAdjust,
22675
- y: y + yAdjust
22676
- };
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;
22677
22145
  };
22678
22146
  var computeConnectorPoints = (fromEl, labelEl, connector) => {
22679
22147
  const x1c = fromEl?.centerX;
22680
22148
  const y1c = fromEl?.centerY;
22681
22149
  const cx = labelEl?.centerX;
22682
22150
  const cy = labelEl?.centerY;
22151
+ const labelWidth = labelEl?.width ?? 0;
22152
+ const labelHeight = labelEl?.height ?? 0;
22683
22153
  const ux = cx - x1c;
22684
22154
  const uy = cy - y1c;
22685
22155
  const len = Math.sqrt(ux * ux + uy * uy) || 1;
22686
22156
  const dx = ux / len;
22687
22157
  const dy = uy / len;
22688
- const startOffset = connector?.startOffset ?? 6;
22689
- const isPointAnchor = typeof fromEl?.options?.radius === "number";
22690
- const referencePoint = connector?.anchor && !isPointAnchor ? getReferencePoint(fromEl, connector) : isPointAnchor ? {
22691
- x: x1c,
22692
- y: y1c
22693
- } : getRectangleEdgePoint(fromEl, cx, cy, startOffset);
22694
- const endOffset = connector?.endOffset ?? 1;
22695
- const end = typeof labelEl?.width === "number" && typeof labelEl?.height === "number" ? getLabelEdgePoint(labelEl, referencePoint.x, endOffset) : {
22696
- x: cx - dx * endOffset,
22697
- y: cy - dy * endOffset
22698
- };
22699
- const tickHalf = isPointAnchor ? 0 : (connector?.anchor?.tickSize ?? 8) / 2;
22700
- const lineDirection = end.x >= referencePoint.x ? 1 : -1;
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
+ }
22701
22182
  return {
22702
- startX: referencePoint.x + lineDirection * tickHalf,
22703
- startY: referencePoint.y,
22704
- endX: end.x,
22705
- endY: end.y,
22706
- tickStartX: referencePoint.x - tickHalf,
22707
- tickStartY: referencePoint.y,
22708
- tickEndX: referencePoint.x + tickHalf,
22709
- tickEndY: referencePoint.y
22183
+ startX,
22184
+ startY,
22185
+ endX,
22186
+ endY
22710
22187
  };
22711
22188
  };
22712
22189
  //#endregion
@@ -22729,30 +22206,49 @@ var calloutConnectorPlugin = {
22729
22206
  const fromEl = elements.find((el) => el.options && el.options.id === connector.fromId);
22730
22207
  if (!labelEl || !fromEl) return;
22731
22208
  if (labelEl.options?.display === false) return;
22732
- const { startX, startY, endX, endY, tickStartX, tickStartY, tickEndX, tickEndY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22209
+ const { startX, startY, endX, endY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22733
22210
  const ctx = chart?.ctx;
22734
- const strokeStyle = connector?.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
22735
22211
  ctx?.save?.();
22736
22212
  ctx?.beginPath?.();
22737
22213
  ctx?.moveTo?.(startX, startY);
22738
22214
  ctx?.lineTo?.(endX, endY);
22739
22215
  ctx.lineWidth = connector?.lineWidth ?? 1;
22740
- ctx.strokeStyle = strokeStyle;
22216
+ ctx.strokeStyle = connector?.strokeStyle ?? "rgba(120,120,120,0.9)";
22741
22217
  if (connector?.lineDash && connector?.lineDash?.length) ctx?.setLineDash?.(connector.lineDash);
22742
22218
  ctx.lineCap = "round";
22743
22219
  ctx?.stroke?.();
22744
- ctx?.beginPath?.();
22745
- ctx?.moveTo?.(tickStartX, tickStartY);
22746
- ctx?.lineTo?.(tickEndX, tickEndY);
22747
- ctx.lineWidth = Math.max(connector?.lineWidth ?? 1, 1);
22748
- ctx.strokeStyle = strokeStyle;
22749
- ctx?.stroke?.();
22750
22220
  ctx?.restore?.();
22751
22221
  });
22752
22222
  }
22753
22223
  };
22754
22224
  //#endregion
22755
- //#region src/components/line-chart/plugins/vertical-markers-plugin/utils.ts
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
22756
22252
  var isFiniteNumber = (v) => {
22757
22253
  return isNumber(v) && Number.isFinite(v);
22758
22254
  };
@@ -22778,68 +22274,336 @@ var clamp = (v, min, max) => {
22778
22274
  var crispLinePx = (v) => {
22779
22275
  return Math.round(v) + .5;
22780
22276
  };
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
+ };
22781
22348
  //#endregion
22782
- //#region src/components/line-chart/plugins/vertical-markers-plugin/draw.ts
22783
- var drawVerticalLine = ({ chart, xPx, y1, y2, color, opacity, lineWidth, lineDash }) => {
22349
+ //#region src/components/line-chart/plugins/line-markers-plugin/draw.ts
22350
+ var drawLineSegment = ({ chart, x1, y1, x2, y2, color, opacity, lineWidth, lineDash }) => {
22784
22351
  const { ctx } = chart ?? {};
22785
- const x = crispLinePx(xPx);
22352
+ const startX = crispLinePx(x1);
22353
+ const endX = crispLinePx(x2);
22354
+ const startY = crispLinePx(y1);
22355
+ const endY = crispLinePx(y2);
22786
22356
  ctx?.save?.();
22787
22357
  ctx.globalAlpha = opacity;
22788
22358
  ctx?.beginPath?.();
22789
22359
  ctx.lineWidth = lineWidth;
22790
22360
  ctx.strokeStyle = color;
22791
22361
  ctx?.setLineDash?.(lineDash);
22792
- ctx.lineCap = "round";
22793
- ctx?.moveTo?.(x, y1);
22794
- ctx?.lineTo?.(x, y2);
22362
+ ctx.lineCap = "butt";
22363
+ ctx?.moveTo?.(startX, startY);
22364
+ ctx?.lineTo?.(endX, endY);
22795
22365
  ctx?.stroke?.();
22796
22366
  ctx?.restore?.();
22797
- return x;
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
+ }
22798
22472
  };
22799
22473
  var drawTick = (args) => {
22800
- const { ctx, x, y, side, tick, defaultColor, defaultFont } = args ?? {};
22474
+ const { ctx, x, y, side, tick, defaultColor, defaultFont, chartArea } = args ?? {};
22475
+ const direction = args?.direction ?? LineMarkerDirection.Vertical;
22801
22476
  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;
22802
22481
  const color = tick?.color ?? defaultColor;
22803
22482
  const font = tick?.font ?? defaultFont;
22804
22483
  const labelOffset = tick?.labelOffsetPx ?? 6;
22805
- const bootSize = 6;
22806
- const dir = side === "left" ? 1 : -1;
22807
- const xTickEnd = x;
22484
+ const bootSize = tick?.sizePx ?? 6;
22485
+ const dir = side === LineMarkerSide.Left ? 1 : -1;
22486
+ const xTickEnd = crispLinePx(x);
22487
+ const yTickEnd = crispLinePx(y);
22808
22488
  ctx?.save?.();
22809
22489
  ctx.strokeStyle = color;
22810
22490
  ctx.fillStyle = color;
22811
22491
  ctx.lineWidth = 1;
22812
- const xOuter = xTickEnd + dir * bootSize;
22813
- ctx?.beginPath?.();
22814
- ctx?.moveTo?.(xTickEnd, y);
22815
- ctx?.lineTo?.(xOuter, y);
22816
- ctx?.lineTo?.(xTickEnd, reverse ? y + bootSize : y - bootSize);
22817
- ctx?.closePath?.();
22818
- ctx?.fill?.();
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
+ }
22819
22515
  const label = tick.label;
22820
22516
  if (label) {
22821
22517
  ctx.font = font;
22822
22518
  ctx.textBaseline = "middle";
22823
- ctx.textAlign = side === "left" ? "left" : "right";
22824
- const tx = xOuter + dir * labelOffset;
22825
- if (Array.isArray(label)) {
22826
- const lineH = 12;
22827
- const startY = y - (label.length - 1) * lineH / 2;
22828
- label?.forEach((line, i) => ctx?.fillText?.(String(line), tx, startY + i * lineH));
22829
- } else ctx?.fillText?.(String(label), tx, y);
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
+ }
22830
22586
  }
22831
22587
  ctx?.restore?.();
22832
22588
  };
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
+ };
22833
22595
  //#endregion
22834
- //#region src/components/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.ts
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)";
22835
22599
  var readPluginEnable = (chart) => {
22836
- const { verticalMarkersPlugin } = chart?.options?.plugins ?? {};
22837
- return asObject(verticalMarkersPlugin)?.enabled === true;
22600
+ const { lineMarkersPlugin } = chart?.options?.plugins ?? {};
22601
+ return asObject(lineMarkersPlugin)?.enabled === true;
22838
22602
  };
22839
22603
  var readAnnotationConfig = (chart) => {
22840
22604
  const optionsWithAnnotations = chart?.options;
22841
22605
  const configWithAnnotations = chart?.config?.options;
22842
- return asObject(configWithAnnotations?.annotations?.verticalMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.verticalMarkersAnnotation);
22606
+ return asObject(configWithAnnotations?.annotations?.lineMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.lineMarkersAnnotation);
22843
22607
  };
22844
22608
  var readOwnBoolean = (obj, key) => {
22845
22609
  if (!obj || typeof obj !== "object") return void 0;
@@ -22847,39 +22611,242 @@ var readOwnBoolean = (obj, key) => {
22847
22611
  if (!descriptor || !("value" in descriptor)) return void 0;
22848
22612
  return typeof descriptor?.value === "boolean" ? descriptor?.value : void 0;
22849
22613
  };
22850
- var computeXPxBase = (args) => {
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) => {
22851
22624
  const { chart, chartArea, item, global } = args ?? {};
22852
22625
  const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22853
- const stickSide = item?.stickSide ?? global?.stickSide ?? "right";
22626
+ const side = resolveSide(item, global);
22854
22627
  const edgePaddingPx = global?.edgePaddingPx ?? 0;
22855
22628
  const xValue = item?.xValue ?? global?.xValue;
22856
22629
  if (isFiniteNumber(xValue)) return getPixelForValueSafe(resolveScale(chart, item?.xScaleID, "x"), xValue);
22857
- if (stickToEdge) return clamp(stickSide === "left" ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22630
+ if (stickToEdge) return getEdgeX(chartArea, side, edgePaddingPx);
22858
22631
  };
22859
- var computeYSpan = (args) => {
22860
- const { chart, chartArea, item, global } = args ?? {};
22632
+ var computeVerticalLineGeometry = (args) => {
22633
+ const { chart, chartArea, item, global, xPx } = args ?? {};
22861
22634
  const yScale = resolveScale(chart, item?.yScaleID, "y");
22862
22635
  if (!yScale) return void 0;
22863
- const yStartValue = item?.yStartValue ?? global?.yStartValue;
22864
22636
  const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22865
- const itemValue = item?.value;
22866
- if (!isFiniteNumber(itemValue)) return void 0;
22867
- const yStartPx = isFiniteNumber(yStartValue) ? clamp(yScale.getPixelForValue(yStartValue), chartArea?.top, chartArea?.bottom) : reverse ? chartArea?.bottom : chartArea?.top;
22868
- const yEndPxRaw = clamp(yScale.getPixelForValue(itemValue), chartArea?.top, chartArea?.bottom);
22637
+ 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);
22869
22645
  return {
22870
- yStartPx,
22871
- yEndPx: reverse ? Math.min(yStartPx, yEndPxRaw) : Math.max(yStartPx, yEndPxRaw),
22872
- endTick: {
22873
- label: item.label,
22874
- color: item.color,
22875
- font: item.font,
22876
- labelOffsetPx: item.labelOffsetPx
22877
- },
22878
- reverse
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
22879
22655
  };
22880
22656
  };
22881
- var verticalMarkersPlugin = {
22882
- id: "verticalMarkers",
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;
22831
+ return {
22832
+ color,
22833
+ opacity,
22834
+ lineWidth,
22835
+ lineDash
22836
+ };
22837
+ };
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",
22883
22850
  afterDraw: (chart) => {
22884
22851
  if (!readPluginEnable(chart)) return;
22885
22852
  const cfg = readAnnotationConfig(chart);
@@ -22890,50 +22857,195 @@ var verticalMarkersPlugin = {
22890
22857
  const items = asArray(cfg?.items);
22891
22858
  if (!items.length) return;
22892
22859
  const itemGapPx = cfg?.itemGapPx ?? 0;
22893
- const globalStickSide = cfg?.stickSide ?? "right";
22894
- let visibleIndex = 0;
22895
- items?.forEach((item) => {
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) => {
22896
22867
  if (!item || item?.display === false) return;
22897
- const baseXPx = computeXPxBase({
22898
- chart,
22899
- chartArea,
22900
- item,
22901
- global: cfg
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
22902
22924
  });
22903
- if (!isFiniteNumber(baseXPx)) return;
22904
- const side = item?.stickSide ?? globalStickSide;
22905
- const xPx = baseXPx + (side === "left" ? 1 : -1) * visibleIndex * itemGapPx;
22906
- visibleIndex += 1;
22907
- const ySpan = computeYSpan({
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
+ });
23003
+ });
23004
+ });
23005
+ lineTasks.forEach(({ geometry, style }) => {
23006
+ drawLineSegment({
22908
23007
  chart,
22909
- chartArea,
22910
- item,
22911
- global: cfg
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
22912
23016
  });
22913
- if (!ySpan) return;
22914
- const color = item?.color ?? cfg?.color;
22915
- const opacity = item?.opacity ?? cfg?.opacity;
22916
- const lineWidth = item?.lineWidth ?? cfg?.lineWidth;
22917
- const lineDash = asArray(item?.lineDash ?? cfg?.lineDash);
22918
- if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
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) => {
22919
23037
  drawTick({
22920
23038
  ctx,
22921
- x: drawVerticalLine({
22922
- chart,
22923
- xPx,
22924
- y1: ySpan?.yStartPx,
22925
- y2: ySpan?.yEndPx,
22926
- color,
22927
- opacity,
22928
- lineWidth,
22929
- lineDash
22930
- }),
22931
- y: ySpan?.yEndPx,
22932
- side,
22933
- reverse: ySpan?.reverse,
22934
- tick: ySpan?.endTick,
22935
- defaultColor: color,
22936
- defaultFont: "12px sans-serif"
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
22937
23049
  });
22938
23050
  });
22939
23051
  }
@@ -23405,7 +23517,7 @@ var Legend = ({ chartRef, legendConfig }) => {
23405
23517
  };
23406
23518
  //#endregion
23407
23519
  //#region src/components/line-chart/line-chart.tsx
23408
- Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, verticalMarkersPlugin);
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);
23409
23521
  var LineChart = (props) => {
23410
23522
  setDefaultTheme();
23411
23523
  const chartRef = useRef(null);