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

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 +761 -872
  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,511 @@ 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_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
+ };
20895
20908
  var getXScale = (chart, axisId) => {
20896
20909
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20897
20910
  if (chart?.scales?.x) return chart.scales.x;
20898
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("x"));
20911
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20912
+ return item?.toLowerCase()?.includes("x");
20913
+ });
20899
20914
  return key ? chart?.scales?.[key] : void 0;
20900
20915
  };
20901
20916
  var getYScale = (chart, axisId) => {
20902
20917
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20903
20918
  if (chart?.scales?.y) return chart.scales.y;
20904
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
20919
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20920
+ return item?.toLowerCase()?.includes("y");
20921
+ });
20905
20922
  return key ? chart?.scales?.[key] : void 0;
20906
20923
  };
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
+ };
20907
21332
  var resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
20908
- const chart = ctx?.chart;
21333
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21334
+ if (layout) return layout.xValue;
21335
+ const chart = getChartFromCtx$1(ctx);
20909
21336
  const xVal = refAnnotation?.xValue;
20910
21337
  if (typeof xVal !== "number") return 0;
20911
21338
  if (!chart) return xVal;
20912
- const scale = getXScale(chart, refAnnotation?.annotationAxis);
21339
+ const scale = getXScale(chart, getAxisId(refAnnotation, "x"));
20913
21340
  if (!scale) return xVal;
20914
21341
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
20915
21342
  const side = index % 2 === 0 ? 1 : -1;
20916
- const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
21343
+ const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * DEFAULT_HORIZONTAL_OFFSET;
20917
21344
  const basePx = scale.getPixelForValue(xVal);
20918
- const cxBase = basePx + dx;
20919
21345
  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);
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);
20934
21352
  };
20935
21353
  var resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
20936
- const chart = ctx?.chart;
20937
- const yVal = refAnnotation.yValue;
21354
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21355
+ if (layout) return layout.yValue;
21356
+ const chart = getChartFromCtx$1(ctx);
21357
+ const yVal = refAnnotation?.yValue;
20938
21358
  if (typeof yVal !== "number") return 0;
20939
21359
  if (!chart) return yVal;
20940
- const scale = getYScale(chart, refAnnotation?.annotationAxis);
21360
+ const scale = getYScale(chart, getAxisId(refAnnotation, "y"));
20941
21361
  if (!scale) return yVal;
20942
21362
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
20943
21363
  const row = Math.floor(index / 2);
20944
- const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
21364
+ const dy = hasCustomOffset ? calloutCfg?.yAdjust : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
20945
21365
  const basePx = scale.getPixelForValue(yVal);
20946
- const cyBase = basePx + dy;
20947
21366
  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;
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;
20964
21375
  var isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
20965
- const chart = ctx?.chart;
21376
+ const chart = getChartFromCtx$1(ctx);
20966
21377
  const area = chart?.chartArea;
20967
21378
  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);
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);
20976
21387
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
20977
21388
  };
20978
21389
  //#endregion
@@ -21273,7 +21684,14 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21273
21684
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
21274
21685
  const baseAnnotation = {
21275
21686
  ...baseRest,
21276
- label: { display: false }
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
+ }
21277
21695
  };
21278
21696
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
21279
21697
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
@@ -21281,10 +21699,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21281
21699
  const onCalloutDragStart = typeof calloutCfg?.onDragStart === "function" ? calloutCfg.onDragStart : void 0;
21282
21700
  const onCalloutDrag = typeof calloutCfg?.onDrag === "function" ? calloutCfg.onDrag : void 0;
21283
21701
  const onCalloutDragEnd = typeof calloutCfg?.onDragEnd === "function" ? calloutCfg.onDragEnd : void 0;
21284
- const strokeStyle = calloutCfg.strokeStyle ?? baseAnnotation.borderColor;
21702
+ const strokeStyle = calloutCfg.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
21285
21703
  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;
21704
+ const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 2;
21705
+ const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : .5;
21288
21706
  const calloutId = `callout-annotation-${index}`;
21289
21707
  return [baseAnnotation, {
21290
21708
  id: calloutId,
@@ -21294,14 +21712,18 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21294
21712
  color: (ctx) => {
21295
21713
  const chart = getChartFromCtx(ctx);
21296
21714
  if (!chart) return color;
21297
- if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21715
+ if (Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) && chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21298
21716
  return color;
21299
21717
  },
21300
21718
  font,
21301
21719
  borderColor,
21302
21720
  borderWidth: BORDER_WIDTH.INITIAL,
21303
21721
  padding: 5,
21304
- display: (ctx) => (refAnnotation.display ?? true) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0),
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
+ },
21305
21727
  xValue: (ctx) => {
21306
21728
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
21307
21729
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
@@ -21318,10 +21740,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21318
21740
  displayDragCoordinates: false,
21319
21741
  enableDrag: true,
21320
21742
  enter: ({ element }, { chart }) => {
21321
- return handleLabelEnter(element, chart, { enableDrag: true });
21743
+ return handleLabelEnter(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21322
21744
  },
21323
21745
  leave: ({ element }, { chart }) => {
21324
- return handleLabelLeave(element, chart, { enableDrag: true });
21746
+ return handleLabelLeave(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21325
21747
  },
21326
21748
  onDragStart: onCalloutDragStart ? () => (coords) => {
21327
21749
  return onCalloutDragStart(coords, refAnnotation);
@@ -21338,7 +21760,13 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21338
21760
  strokeStyle,
21339
21761
  lineWidth,
21340
21762
  startOffset,
21341
- endOffset
21763
+ endOffset,
21764
+ anchor: {
21765
+ position: refAnnotation?.labelConfig?.position,
21766
+ xAdjust: refAnnotation?.labelConfig?.xAdjust,
21767
+ yAdjust: refAnnotation?.labelConfig?.yAdjust,
21768
+ labelOffsetPx: refAnnotation?.labelOffsetPx
21769
+ }
21342
21770
  }
21343
21771
  }];
21344
21772
  };
@@ -21519,11 +21947,6 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21519
21947
  state
21520
21948
  ]);
21521
21949
  const dragData = useMemo(() => enableDragPoints && getDraggableData(options), [enableDragPoints, options]);
21522
- const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
21523
- generatedDatasets,
21524
- hoveredPoint,
21525
- onHover
21526
- ]);
21527
21950
  const panOptions = useMemo(() => ({
21528
21951
  enabled: panEnabled,
21529
21952
  mode: PanZoomMode.XY,
@@ -21554,13 +21977,13 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21554
21977
  updateAxesRangesFromChart,
21555
21978
  zoomEnabled
21556
21979
  ]);
21557
- const lineMarkersAnnotation = options?.annotations?.lineMarkersAnnotation ?? {};
21558
- const lineMarkersPluginEnabled = Boolean(options?.plugins?.lineMarkersPlugin?.enabled ?? lineMarkersAnnotation?.enabled);
21559
- const plugins = {
21980
+ const verticalMarkersAnnotation = options?.annotations?.verticalMarkersAnnotation ?? {};
21981
+ const verticalMarkersPluginEnabled = Boolean(options?.plugins?.verticalMarkersPlugin?.enabled ?? verticalMarkersAnnotation?.enabled);
21982
+ const plugins = useMemo(() => ({
21560
21983
  datalabels,
21561
21984
  annotationDraggerPlugin: { enabled: state?.enableDragAnnotation },
21562
21985
  calloutConnectorPlugin: { enableCalloutAnnotation: options?.annotations.enableCalloutAnnotation },
21563
- lineMarkersPlugin: { enabled: lineMarkersPluginEnabled },
21986
+ verticalMarkersPlugin: { enabled: verticalMarkersPluginEnabled },
21564
21987
  annotation: toAnnotationObject(annotation),
21565
21988
  zoom: {
21566
21989
  pan: panOptions,
@@ -21585,7 +22008,32 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21585
22008
  lineHeight
21586
22009
  },
21587
22010
  ...dragData
21588
- };
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
+ ]);
21589
22037
  return useMemo(() => ({
21590
22038
  persistenceId,
21591
22039
  layout: { padding: layoutPadding },
@@ -21613,6 +22061,7 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21613
22061
  layoutPadding,
21614
22062
  lineEnabled,
21615
22063
  onAnimationComplete,
22064
+ onChartHover,
21616
22065
  options?.annotations,
21617
22066
  options?.chartStyling?.maintainAspectRatio,
21618
22067
  options?.chartStyling?.performanceMode,
@@ -22138,52 +22587,126 @@ var annotationDraggerPlugin = {
22138
22587
  };
22139
22588
  //#endregion
22140
22589
  //#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;
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
+ };
22145
22677
  };
22146
22678
  var computeConnectorPoints = (fromEl, labelEl, connector) => {
22147
22679
  const x1c = fromEl?.centerX;
22148
22680
  const y1c = fromEl?.centerY;
22149
22681
  const cx = labelEl?.centerX;
22150
22682
  const cy = labelEl?.centerY;
22151
- const labelWidth = labelEl?.width ?? 0;
22152
- const labelHeight = labelEl?.height ?? 0;
22153
22683
  const ux = cx - x1c;
22154
22684
  const uy = cy - y1c;
22155
22685
  const len = Math.sqrt(ux * ux + uy * uy) || 1;
22156
22686
  const dx = ux / len;
22157
22687
  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
- }
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;
22182
22701
  return {
22183
- startX,
22184
- startY,
22185
- endX,
22186
- endY
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
22187
22710
  };
22188
22711
  };
22189
22712
  //#endregion
@@ -22206,49 +22729,30 @@ var calloutConnectorPlugin = {
22206
22729
  const fromEl = elements.find((el) => el.options && el.options.id === connector.fromId);
22207
22730
  if (!labelEl || !fromEl) return;
22208
22731
  if (labelEl.options?.display === false) return;
22209
- const { startX, startY, endX, endY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22732
+ const { startX, startY, endX, endY, tickStartX, tickStartY, tickEndX, tickEndY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22210
22733
  const ctx = chart?.ctx;
22734
+ const strokeStyle = connector?.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
22211
22735
  ctx?.save?.();
22212
22736
  ctx?.beginPath?.();
22213
22737
  ctx?.moveTo?.(startX, startY);
22214
22738
  ctx?.lineTo?.(endX, endY);
22215
22739
  ctx.lineWidth = connector?.lineWidth ?? 1;
22216
- ctx.strokeStyle = connector?.strokeStyle ?? "rgba(120,120,120,0.9)";
22740
+ ctx.strokeStyle = strokeStyle;
22217
22741
  if (connector?.lineDash && connector?.lineDash?.length) ctx?.setLineDash?.(connector.lineDash);
22218
22742
  ctx.lineCap = "round";
22219
22743
  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?.();
22220
22750
  ctx?.restore?.();
22221
22751
  });
22222
22752
  }
22223
22753
  };
22224
22754
  //#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
22755
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/utils.ts
22252
22756
  var isFiniteNumber = (v) => {
22253
22757
  return isNumber(v) && Number.isFinite(v);
22254
22758
  };
@@ -22274,335 +22778,68 @@ var clamp = (v, min, max) => {
22274
22778
  var crispLinePx = (v) => {
22275
22779
  return Math.round(v) + .5;
22276
22780
  };
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
22781
  //#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 }) => {
22782
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/draw.ts
22783
+ var drawVerticalLine = ({ chart, xPx, y1, y2, color, opacity, lineWidth, lineDash }) => {
22351
22784
  const { ctx } = chart ?? {};
22352
- const startX = crispLinePx(x1);
22353
- const endX = crispLinePx(x2);
22354
- const startY = crispLinePx(y1);
22355
- const endY = crispLinePx(y2);
22785
+ const x = crispLinePx(xPx);
22356
22786
  ctx?.save?.();
22357
22787
  ctx.globalAlpha = opacity;
22358
22788
  ctx?.beginPath?.();
22359
22789
  ctx.lineWidth = lineWidth;
22360
22790
  ctx.strokeStyle = color;
22361
22791
  ctx?.setLineDash?.(lineDash);
22362
- ctx.lineCap = "butt";
22363
- ctx?.moveTo?.(startX, startY);
22364
- ctx?.lineTo?.(endX, endY);
22792
+ ctx.lineCap = "round";
22793
+ ctx?.moveTo?.(x, y1);
22794
+ ctx?.lineTo?.(x, y2);
22365
22795
  ctx?.stroke?.();
22366
22796
  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
- }
22797
+ return x;
22472
22798
  };
22473
22799
  var drawTick = (args) => {
22474
- const { ctx, x, y, side, tick, defaultColor, defaultFont, chartArea } = args ?? {};
22475
- const direction = args?.direction ?? LineMarkerDirection.Vertical;
22800
+ const { ctx, x, y, side, tick, defaultColor, defaultFont } = args ?? {};
22476
22801
  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
22802
  const color = tick?.color ?? defaultColor;
22482
22803
  const font = tick?.font ?? defaultFont;
22483
22804
  const labelOffset = tick?.labelOffsetPx ?? 6;
22484
- const bootSize = tick?.sizePx ?? 6;
22485
- const dir = side === LineMarkerSide.Left ? 1 : -1;
22805
+ const bootSize = 6;
22806
+ const dir = side === "left" ? 1 : -1;
22486
22807
  const xTickEnd = x;
22487
22808
  ctx?.save?.();
22488
22809
  ctx.strokeStyle = color;
22489
22810
  ctx.fillStyle = color;
22490
22811
  ctx.lineWidth = 1;
22491
- const xOuter = direction === LineMarkerDirection.Vertical ? xTickEnd + dir * bootSize : xTickEnd;
22492
- const yOuter = direction === LineMarkerDirection.Horizontal ? y + (reverse ? bootSize : -bootSize) : y;
22493
- const xThird = direction === LineMarkerDirection.Vertical ? xTickEnd : xTickEnd + dir * bootSize;
22494
- const yThird = reverse ? y + bootSize : y - bootSize;
22495
- const bootBounds = {
22496
- left: Math.min(xTickEnd, xOuter, xThird),
22497
- right: Math.max(xTickEnd, xOuter, xThird),
22498
- top: Math.min(y, yOuter, yThird),
22499
- bottom: Math.max(y, yOuter, yThird)
22500
- };
22501
- const canDrawBoot = (direction === LineMarkerDirection.Vertical ? isWithin(y, chartArea.top, chartArea.bottom) : isWithin(xTickEnd, chartArea.left, chartArea.right)) && isTickElementInsideByDirection({
22502
- direction,
22503
- chartArea,
22504
- box: bootBounds
22505
- });
22506
- if (bootEnabled && canDrawBoot) {
22507
- ctx?.beginPath?.();
22508
- ctx?.moveTo?.(xTickEnd, y);
22509
- ctx?.lineTo?.(xOuter, yOuter);
22510
- ctx?.lineTo?.(xThird, yThird);
22511
- ctx?.closePath?.();
22512
- ctx?.fill?.();
22513
- }
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?.();
22514
22819
  const label = tick.label;
22515
22820
  if (label) {
22516
22821
  ctx.font = font;
22517
22822
  ctx.textBaseline = "middle";
22518
- const preferredPosition = tick?.labelPosition ?? resolveDefaultLabelPosition({
22519
- direction,
22520
- side
22521
- });
22522
- const fallbackPositions = [
22523
- preferredPosition,
22524
- getOppositeLabelPosition(preferredPosition),
22525
- LineMarkerLabelPosition.Top,
22526
- LineMarkerLabelPosition.Bottom,
22527
- LineMarkerLabelPosition.OnLine
22528
- ].filter((value, index, arr) => arr.indexOf(value) === index);
22529
- for (const labelPosition of fallbackPositions) {
22530
- const anchor = getLabelAnchor({
22531
- x: xTickEnd,
22532
- y: labelY,
22533
- direction,
22534
- labelOffset,
22535
- bootSize,
22536
- labelPosition
22537
- });
22538
- const labelTextAlign = anchor.textAlign;
22539
- ctx.textAlign = labelTextAlign;
22540
- if (Array.isArray(label)) {
22541
- const lineH = lineHeightPx(font);
22542
- const startY = anchor.y - (label.length - 1) * lineH / 2;
22543
- const metrics = label.map((line, i) => {
22544
- const text = String(line);
22545
- const width = ctx.measureText(text).width;
22546
- const yLine = startY + i * lineH;
22547
- return {
22548
- text,
22549
- yLine,
22550
- box: getLineTextBox({
22551
- textAlign: labelTextAlign,
22552
- x: anchor.x,
22553
- y: yLine,
22554
- width,
22555
- lineHeight: lineH
22556
- })
22557
- };
22558
- });
22559
- if (!metrics.every((lineData) => isTickElementInsideByDirection({
22560
- direction,
22561
- chartArea,
22562
- box: lineData.box
22563
- }))) continue;
22564
- metrics.forEach((lineData) => ctx?.fillText?.(lineData.text, anchor.x, lineData.yLine));
22565
- break;
22566
- } else {
22567
- const text = String(label);
22568
- const width = ctx.measureText(text).width;
22569
- const lineH = lineHeightPx(font);
22570
- if (!isTickElementInsideByDirection({
22571
- direction,
22572
- chartArea,
22573
- box: getLineTextBox({
22574
- textAlign: labelTextAlign,
22575
- x: anchor.x,
22576
- y: anchor.y,
22577
- width,
22578
- lineHeight: lineH
22579
- })
22580
- })) continue;
22581
- ctx?.fillText?.(text, anchor.x, anchor.y);
22582
- break;
22583
- }
22584
- }
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);
22585
22830
  }
22586
22831
  ctx?.restore?.();
22587
22832
  };
22588
- var getTickLabelHeight = (tick, defaultFont) => {
22589
- const lines = toLines(tick?.label);
22590
- if (!lines.length) return 0;
22591
- const lineHeight = lineHeightPx(tick?.font ?? defaultFont);
22592
- return lines.length * lineHeight;
22593
- };
22594
22833
  //#endregion
22595
- //#region src/components/line-chart/plugins/line-markers-plugin/line-markers-plugin.ts
22596
- var DEFAULT_FONT = "12px sans-serif";
22597
- var DEFAULT_COLOR = "rgba(20,20,20,0.9)";
22834
+ //#region src/components/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.ts
22598
22835
  var readPluginEnable = (chart) => {
22599
- const { lineMarkersPlugin } = chart?.options?.plugins ?? {};
22600
- return asObject(lineMarkersPlugin)?.enabled === true;
22836
+ const { verticalMarkersPlugin } = chart?.options?.plugins ?? {};
22837
+ return asObject(verticalMarkersPlugin)?.enabled === true;
22601
22838
  };
22602
22839
  var readAnnotationConfig = (chart) => {
22603
22840
  const optionsWithAnnotations = chart?.options;
22604
22841
  const configWithAnnotations = chart?.config?.options;
22605
- return asObject(configWithAnnotations?.annotations?.lineMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.lineMarkersAnnotation);
22842
+ return asObject(configWithAnnotations?.annotations?.verticalMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.verticalMarkersAnnotation);
22606
22843
  };
22607
22844
  var readOwnBoolean = (obj, key) => {
22608
22845
  if (!obj || typeof obj !== "object") return void 0;
@@ -22610,242 +22847,39 @@ var readOwnBoolean = (obj, key) => {
22610
22847
  if (!descriptor || !("value" in descriptor)) return void 0;
22611
22848
  return typeof descriptor?.value === "boolean" ? descriptor?.value : void 0;
22612
22849
  };
22613
- var getMarkerDirection = (item, global) => {
22614
- return item?.direction ?? global?.lineDirection ?? LineMarkerDirection.Vertical;
22615
- };
22616
- var resolveSide = (item, global) => {
22617
- return item?.stickSide ?? global?.stickSide ?? LineMarkerSide.Right;
22618
- };
22619
- var getEdgeX = (chartArea, side, edgePaddingPx) => {
22620
- return clamp(side === LineMarkerSide.Left ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22621
- };
22622
- var computeVerticalXPxBase = (args) => {
22850
+ var computeXPxBase = (args) => {
22623
22851
  const { chart, chartArea, item, global } = args ?? {};
22624
22852
  const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22625
- const side = resolveSide(item, global);
22853
+ const stickSide = item?.stickSide ?? global?.stickSide ?? "right";
22626
22854
  const edgePaddingPx = global?.edgePaddingPx ?? 0;
22627
22855
  const xValue = item?.xValue ?? global?.xValue;
22628
22856
  if (isFiniteNumber(xValue)) return getPixelForValueSafe(resolveScale(chart, item?.xScaleID, "x"), xValue);
22629
- if (stickToEdge) return getEdgeX(chartArea, side, edgePaddingPx);
22857
+ if (stickToEdge) return clamp(stickSide === "left" ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22630
22858
  };
22631
- var computeVerticalLineGeometry = (args) => {
22632
- const { chart, chartArea, item, global, xPx } = args ?? {};
22859
+ var computeYSpan = (args) => {
22860
+ const { chart, chartArea, item, global } = args ?? {};
22633
22861
  const yScale = resolveScale(chart, item?.yScaleID, "y");
22634
22862
  if (!yScale) return void 0;
22635
- const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22636
22863
  const yStartValue = item?.yStartValue ?? global?.yStartValue;
22637
- const yEndValue = item?.yEndValue ?? item?.value ?? global?.yEndValue;
22638
- if (!isFiniteNumber(yEndValue)) return void 0;
22639
- const y1 = isFiniteNumber(yStartValue) ? yScale.getPixelForValue(yStartValue) : reverse ? chartArea?.bottom : chartArea?.top;
22640
- const y1InArea = y1 >= chartArea?.top && y1 <= chartArea?.bottom;
22641
- const y2Raw = yScale.getPixelForValue(yEndValue);
22642
- const y2InArea = y2Raw >= chartArea?.top && y2Raw <= chartArea?.bottom;
22643
- const y2 = clamp(y2Raw, chartArea?.top, chartArea?.bottom);
22644
- return {
22645
- x1: xPx,
22646
- y1: clamp(y1, chartArea?.top, chartArea?.bottom),
22647
- x2: xPx,
22648
- y2,
22649
- startInArea: y1InArea,
22650
- endInArea: y2InArea,
22651
- axisInArea: xPx >= chartArea.left && xPx <= chartArea.right,
22652
- side: resolveSide(item, global),
22653
- direction: LineMarkerDirection.Vertical
22654
- };
22655
- };
22656
- var resolveHorizontalSpan = (args) => {
22657
- const { chart, chartArea, item, global, side, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22658
- const xScale = resolveScale(chart, item?.xScaleID, "x");
22659
- if (!xScale) return void 0;
22660
- const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22661
- const stickToGroup = item?.stickToGroup ?? global?.stickToGroup ?? false;
22662
- const edgePaddingPx = global?.edgePaddingPx ?? 0;
22663
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22664
- const fixedLengthPx = item?.lengthPx ?? global?.lengthPx ?? global?.horizontalLineLengthPx;
22665
- const edgeX = getEdgeX(chartArea, side, edgePaddingPx);
22666
- const xStartValue = item?.xStartValue ?? global?.xStartValue;
22667
- const xEndValue = item?.xEndValue ?? global?.xEndValue;
22668
- const xValue = item?.xValue ?? global?.xValue;
22669
- const xStartPxRaw = isFiniteNumber(xStartValue) ? xScale.getPixelForValue(xStartValue) : void 0;
22670
- const xEndPxRaw = isFiniteNumber(xEndValue) ? xScale.getPixelForValue(xEndValue) : void 0;
22671
- const xStartPx = isFiniteNumber(xStartPxRaw) ? clamp(xStartPxRaw, chartArea?.left, chartArea?.right) : void 0;
22672
- const xEndPx = isFiniteNumber(xEndPxRaw) ? clamp(xEndPxRaw, chartArea?.left, chartArea?.right) : void 0;
22673
- if (stickToEdge && stickToGroup && isFiniteNumber(groupAnchorXPx)) {
22674
- const groupXRaw = groupAnchorXPx;
22675
- if (groupXRaw < chartArea.left || groupXRaw > chartArea.right) return;
22676
- const groupX = clamp(groupXRaw, chartArea?.left, chartArea?.right);
22677
- const edgeX = side === LineMarkerSide.Left ? chartArea.left : chartArea.right;
22678
- const x2Raw = isFiniteNumber(fixedLengthPx) ? edgeX + dirIntoChart * fixedLengthPx : groupX;
22679
- return {
22680
- x1: edgeX,
22681
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22682
- startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22683
- endInArea: isFiniteNumber(fixedLengthPx) ? x2Raw >= chartArea.left && x2Raw <= chartArea.right : groupX >= chartArea.left && groupX <= chartArea.right
22684
- };
22685
- }
22686
- if (!stickToEdge && stickToGroup && isFiniteNumber(fixedLengthPx)) {
22687
- const rawAnchor = previousGroupItemXPx ?? (isFiniteNumber(groupAnchorXPx) ? groupAnchorXPx : void 0);
22688
- if (!isFiniteNumber(rawAnchor)) return void 0;
22689
- const anchorX = clamp(rawAnchor, chartArea?.left, chartArea?.right);
22690
- const x2Raw = anchorX + dirIntoChart * fixedLengthPx;
22691
- return {
22692
- x1: anchorX,
22693
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22694
- startInArea: rawAnchor >= chartArea.left && rawAnchor <= chartArea.right,
22695
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22696
- };
22697
- }
22698
- if (isFiniteNumber(xStartPx) && isFiniteNumber(xEndPx)) {
22699
- const startRaw = xStartPxRaw;
22700
- const endRaw = xEndPxRaw;
22701
- if (!isFiniteNumber(startRaw) || !isFiniteNumber(endRaw)) return void 0;
22702
- return {
22703
- x1: xStartPx,
22704
- x2: xEndPx,
22705
- startInArea: startRaw >= chartArea.left && startRaw <= chartArea.right,
22706
- endInArea: endRaw >= chartArea.left && endRaw <= chartArea.right
22707
- };
22708
- }
22709
- if (isFiniteNumber(xStartPx) && isFiniteNumber(fixedLengthPx)) {
22710
- const x2Raw = xStartPx + dirIntoChart * fixedLengthPx;
22711
- return {
22712
- x1: xStartPx,
22713
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22714
- startInArea: xStartPx >= chartArea.left && xStartPx <= chartArea.right,
22715
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22716
- };
22717
- }
22718
- if (isFiniteNumber(xEndPx) && isFiniteNumber(fixedLengthPx)) {
22719
- const x1Raw = xEndPx - dirIntoChart * fixedLengthPx;
22720
- return {
22721
- x1: clamp(x1Raw, chartArea?.left, chartArea?.right),
22722
- x2: xEndPx,
22723
- startInArea: x1Raw >= chartArea.left && x1Raw <= chartArea.right,
22724
- endInArea: xEndPx >= chartArea.left && xEndPx <= chartArea.right
22725
- };
22726
- }
22727
- const xValuePxRaw = isFiniteNumber(xValue) ? xScale.getPixelForValue(xValue) : void 0;
22728
- if (isFiniteNumber(xValuePxRaw) && isFiniteNumber(fixedLengthPx)) {
22729
- const xAnchor = clamp(xValuePxRaw, chartArea?.left, chartArea?.right);
22730
- const x2Raw = xAnchor + dirIntoChart * fixedLengthPx;
22731
- return {
22732
- x1: xAnchor,
22733
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22734
- startInArea: xValuePxRaw >= chartArea.left && xValuePxRaw <= chartArea.right,
22735
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22736
- };
22737
- }
22738
- if (stickToEdge && isFiniteNumber(fixedLengthPx)) {
22739
- const x2Raw = edgeX + dirIntoChart * fixedLengthPx;
22740
- return {
22741
- x1: edgeX,
22742
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22743
- startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22744
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22745
- };
22746
- }
22747
- };
22748
- var computeHorizontalLineGeometry = (args) => {
22749
- const { chart, chartArea, item, global, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22750
- const yScale = resolveScale(chart, item?.yScaleID, "y");
22751
- if (!yScale) return void 0;
22752
- const yValue = item?.yValue ?? item?.value ?? global?.yValue;
22753
- if (!isFiniteNumber(yValue)) return void 0;
22754
- const side = resolveSide(item, global);
22755
- const span = resolveHorizontalSpan({
22756
- chart,
22757
- chartArea,
22758
- item,
22759
- global,
22760
- side,
22761
- groupAnchorXPx,
22762
- previousGroupItemXPx
22763
- });
22764
- if (!span) return void 0;
22765
- const yPxRaw = yScale.getPixelForValue(yValue);
22766
- const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22767
- const yInArea = yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom;
22768
- return {
22769
- x1: span.x1,
22770
- y1: yPx,
22771
- x2: span.x2,
22772
- y2: yPx,
22773
- startInArea: span.startInArea && yInArea,
22774
- endInArea: span.endInArea && yInArea,
22775
- axisInArea: yInArea,
22776
- side,
22777
- direction: LineMarkerDirection.Horizontal
22778
- };
22779
- };
22780
- var resolveTick = (args) => {
22781
- const { globalTick, itemTick, defaultEnabled } = args;
22782
- return {
22783
- enabled: itemTick?.enabled ?? globalTick?.enabled ?? defaultEnabled,
22784
- label: itemTick?.label ?? globalTick?.label,
22785
- color: itemTick?.color ?? globalTick?.color,
22786
- font: itemTick?.font ?? globalTick?.font,
22787
- labelOffsetPx: itemTick?.labelOffsetPx ?? globalTick?.labelOffsetPx,
22788
- labelPosition: itemTick?.labelPosition ?? globalTick?.labelPosition,
22789
- reverse: itemTick?.reverse ?? globalTick?.reverse,
22790
- side: itemTick?.side ?? globalTick?.side,
22791
- sizePx: itemTick?.sizePx ?? globalTick?.sizePx
22792
- };
22793
- };
22794
- var hasTickLabel = (tick) => {
22795
- const label = tick?.label;
22796
- if (Array.isArray(label)) return label.length > 0;
22797
- return Boolean(label);
22798
- };
22799
- var toExtraGeometry = (args) => {
22800
- const { chart, chartArea, item, extra, xBase, defaultSide } = args;
22801
- const yScale = resolveScale(chart, extra?.yScaleID ?? item?.yScaleID, "y");
22802
- if (!yScale) return void 0;
22803
- const yValue = extra?.yValue ?? extra?.value;
22804
- if (!isFiniteNumber(yValue)) return void 0;
22805
- const lengthPx = extra?.lengthPx;
22806
- if (!isFiniteNumber(lengthPx) || lengthPx <= 0) return void 0;
22807
- const side = extra?.side ?? defaultSide;
22808
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22809
- const yPxRaw = yScale.getPixelForValue(yValue);
22810
- const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22811
- const x2Raw = xBase + dirIntoChart * lengthPx;
22812
- return {
22813
- x1: xBase,
22814
- y1: yPx,
22815
- x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22816
- y2: yPx,
22817
- startInArea: xBase >= chartArea.left && xBase <= chartArea.right,
22818
- endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right,
22819
- axisInArea: yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom,
22820
- side,
22821
- direction: LineMarkerDirection.Horizontal
22822
- };
22823
- };
22824
- var getLineStyle = (item, global) => {
22825
- const color = item?.color ?? global?.color ?? DEFAULT_COLOR;
22826
- const opacity = item?.opacity ?? global?.opacity ?? 1;
22827
- const lineWidth = item?.lineWidth ?? global?.lineWidth ?? 1;
22828
- const lineDash = asArray(item?.lineDash ?? global?.lineDash);
22829
- if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
22864
+ 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);
22830
22869
  return {
22831
- color,
22832
- opacity,
22833
- lineWidth,
22834
- lineDash
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
22835
22879
  };
22836
22880
  };
22837
- var getLineReverse = (geometry) => {
22838
- if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.x2 < geometry.x1;
22839
- return geometry.y2 < geometry.y1;
22840
- };
22841
- var hasVisibleLineSegment = (args) => {
22842
- const { geometry } = args;
22843
- const epsilon = .5;
22844
- if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.axisInArea && Math.abs(geometry.x2 - geometry.x1) > epsilon;
22845
- return geometry.axisInArea && Math.abs(geometry.y2 - geometry.y1) > epsilon;
22846
- };
22847
- var lineMarkersPlugin = {
22848
- id: "lineMarkers",
22881
+ var verticalMarkersPlugin = {
22882
+ id: "verticalMarkers",
22849
22883
  afterDraw: (chart) => {
22850
22884
  if (!readPluginEnable(chart)) return;
22851
22885
  const cfg = readAnnotationConfig(chart);
@@ -22856,195 +22890,50 @@ var lineMarkersPlugin = {
22856
22890
  const items = asArray(cfg?.items);
22857
22891
  if (!items.length) return;
22858
22892
  const itemGapPx = cfg?.itemGapPx ?? 0;
22859
- const defaultSide = cfg?.stickSide ?? LineMarkerSide.Right;
22860
- const lineTasks = [];
22861
- const tickTasks = [];
22862
- let groupedVerticalIndexBySide = {};
22863
- let groupedAnchorBySide = {};
22864
- let previousGroupedHorizontalEndBySide = {};
22865
- items.forEach((item, itemIndex) => {
22893
+ const globalStickSide = cfg?.stickSide ?? "right";
22894
+ let visibleIndex = 0;
22895
+ items?.forEach((item) => {
22866
22896
  if (!item || item?.display === false) return;
22867
- const direction = getMarkerDirection(item, cfg);
22868
- const side = resolveSide(item, cfg);
22869
- const style = getLineStyle(item, cfg);
22870
- if (!style) return;
22871
- let geometry;
22872
- if (direction === LineMarkerDirection.Vertical) {
22873
- const baseXPx = computeVerticalXPxBase({
22874
- chart,
22875
- chartArea,
22876
- item,
22877
- global: cfg
22878
- });
22879
- if (!isFiniteNumber(baseXPx)) return;
22880
- const stickToEdge = item?.stickToEdge ?? cfg?.stickToEdge ?? true;
22881
- const hasExplicitXValue = isFiniteNumber(item?.xValue ?? cfg?.xValue);
22882
- const isGroupedVertical = stickToEdge && !hasExplicitXValue;
22883
- const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22884
- const groupedVerticalIndex = groupedVerticalIndexBySide[side] ?? 0;
22885
- const groupOffsetPx = item?.groupOffsetPx ?? 0;
22886
- geometry = computeVerticalLineGeometry({
22887
- chart,
22888
- chartArea,
22889
- item,
22890
- global: cfg,
22891
- xPx: isGroupedVertical ? baseXPx + dirIntoChart * groupedVerticalIndex * itemGapPx + dirIntoChart * groupOffsetPx : baseXPx
22892
- });
22893
- if (geometry && isGroupedVertical) {
22894
- groupedVerticalIndexBySide[side] = groupedVerticalIndex + 1;
22895
- const prevAnchor = groupedAnchorBySide[side];
22896
- groupedAnchorBySide[side] = isFiniteNumber(prevAnchor) ? side === LineMarkerSide.Left ? Math.min(prevAnchor, geometry.x1) : Math.max(prevAnchor, geometry.x1) : geometry.x1;
22897
- }
22898
- } else {
22899
- const stickToGroup = item?.stickToGroup ?? cfg?.stickToGroup ?? false;
22900
- geometry = computeHorizontalLineGeometry({
22901
- chart,
22902
- chartArea,
22903
- item,
22904
- global: cfg,
22905
- groupAnchorXPx: (() => {
22906
- const anchor = groupedAnchorBySide[side];
22907
- if (!isFiniteNumber(anchor)) return void 0;
22908
- return clamp(anchor, chartArea?.left, chartArea?.right);
22909
- })(),
22910
- previousGroupItemXPx: (() => {
22911
- const prev = previousGroupedHorizontalEndBySide[side];
22912
- if (!isFiniteNumber(prev)) return void 0;
22913
- return clamp(prev, chartArea?.left, chartArea?.right);
22914
- })()
22915
- });
22916
- if (geometry && stickToGroup) previousGroupedHorizontalEndBySide[side] = geometry.x2;
22917
- }
22918
- if (!geometry) return;
22919
- if (!hasVisibleLineSegment({ geometry })) return;
22920
- lineTasks.push({
22921
- geometry,
22922
- style
22923
- });
22924
- const lineReverse = getLineReverse(geometry);
22925
- const lineStart = {
22926
- x: geometry.x1,
22927
- y: geometry.y1
22928
- };
22929
- const lineEnd = {
22930
- x: geometry.x2,
22931
- y: geometry.y2
22932
- };
22933
- const startTick = resolveTick({
22934
- globalTick: cfg?.startTick,
22935
- itemTick: item?.startTick,
22936
- defaultEnabled: false
22937
- });
22938
- const endTick = resolveTick({
22939
- globalTick: cfg?.endTick,
22940
- itemTick: item?.endTick,
22941
- defaultEnabled: direction === LineMarkerDirection.Vertical
22942
- });
22943
- const resolvedStartSide = startTick?.side ?? side;
22944
- const resolvedEndSide = endTick?.side ?? side;
22945
- if ((startTick?.enabled || hasTickLabel(startTick)) && geometry.startInArea) tickTasks.push({
22946
- id: `${item?.id ?? `item-${itemIndex}`}-start`,
22947
- x: lineStart.x,
22948
- y: lineStart.y,
22949
- side: resolvedStartSide,
22950
- direction: geometry.direction,
22951
- reverse: startTick?.reverse ?? !lineReverse,
22952
- tick: startTick,
22953
- defaultColor: style.color,
22954
- defaultFont: DEFAULT_FONT
22955
- });
22956
- if ((endTick?.enabled || hasTickLabel(endTick)) && geometry.endInArea) tickTasks.push({
22957
- id: `${item?.id ?? `item-${itemIndex}`}-end`,
22958
- x: lineEnd.x,
22959
- y: lineEnd.y,
22960
- side: resolvedEndSide,
22961
- direction: geometry.direction,
22962
- reverse: endTick?.reverse ?? lineReverse,
22963
- tick: endTick,
22964
- defaultColor: style.color,
22965
- defaultFont: DEFAULT_FONT
22966
- });
22967
- if (direction !== LineMarkerDirection.Vertical) return;
22968
- asArray(item?.extras).forEach((extra, extraIndex) => {
22969
- if (!extra || extra?.display === false) return;
22970
- const extraGeometry = toExtraGeometry({
22971
- chart,
22972
- chartArea,
22973
- item,
22974
- extra,
22975
- xBase: geometry.x1,
22976
- defaultSide: side
22977
- });
22978
- if (!extraGeometry) return;
22979
- const extraStyle = getLineStyle(extra, cfg);
22980
- if (!extraStyle) return;
22981
- lineTasks.push({
22982
- geometry: extraGeometry,
22983
- style: extraStyle
22984
- });
22985
- const extraTick = resolveTick({
22986
- itemTick: extra?.tick,
22987
- defaultEnabled: false
22988
- });
22989
- if (!extraTick?.enabled && !hasTickLabel(extraTick) || !extraGeometry.endInArea) return;
22990
- const extraReverse = extraTick?.reverse ?? readOwnBoolean(extra, "reverse") ?? false;
22991
- tickTasks.push({
22992
- id: `${item?.id ?? `item-${itemIndex}`}-extra-${extra?.id ?? extraIndex}`,
22993
- x: extraGeometry.x2,
22994
- y: extraGeometry.y2,
22995
- side: extraTick?.side ?? extraGeometry.side ?? defaultSide,
22996
- direction: LineMarkerDirection.Horizontal,
22997
- reverse: extraReverse,
22998
- tick: extraTick,
22999
- defaultColor: extraStyle.color,
23000
- defaultFont: DEFAULT_FONT
23001
- });
22897
+ const baseXPx = computeXPxBase({
22898
+ chart,
22899
+ chartArea,
22900
+ item,
22901
+ global: cfg
23002
22902
  });
23003
- });
23004
- lineTasks.forEach(({ geometry, style }) => {
23005
- drawLineSegment({
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({
23006
22908
  chart,
23007
- x1: geometry.x1,
23008
- y1: geometry.y1,
23009
- x2: geometry.x2,
23010
- y2: geometry.y2,
23011
- color: style.color,
23012
- opacity: style.opacity,
23013
- lineWidth: style.lineWidth,
23014
- lineDash: style.lineDash
22909
+ chartArea,
22910
+ item,
22911
+ global: cfg
23015
22912
  });
23016
- });
23017
- const enableLabelCollisionResolver = cfg?.enableLabelCollisionResolver ?? true;
23018
- const labelCollisionPx = cfg?.labelCollisionPx ?? 4;
23019
- const labelCollisionClusterXPx = cfg?.labelCollisionClusterXPx ?? 36;
23020
- const labelYById = enableLabelCollisionResolver ? resolveLabelCollisions({
23021
- labels: tickTasks.filter((task) => {
23022
- return task?.tick?.enabled !== false && isFiniteNumber(getTickLabelHeight(task.tick, task.defaultFont)) && getTickLabelHeight(task.tick, task.defaultFont) > 0;
23023
- }).map((task) => ({
23024
- id: task.id,
23025
- side: task.side,
23026
- x: task.x,
23027
- y: task.y,
23028
- height: getTickLabelHeight(task.tick, task.defaultFont)
23029
- })),
23030
- chartTop: chartArea.top,
23031
- chartBottom: chartArea.bottom,
23032
- clusterXPx: labelCollisionClusterXPx,
23033
- gapPx: labelCollisionPx
23034
- }) : {};
23035
- tickTasks.forEach((task) => {
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;
23036
22919
  drawTick({
23037
22920
  ctx,
23038
- chartArea,
23039
- direction: task.direction,
23040
- x: task.x,
23041
- y: task.y,
23042
- side: task.side,
23043
- reverse: task.reverse,
23044
- labelY: labelYById[task.id],
23045
- tick: task.tick,
23046
- defaultColor: task.defaultColor,
23047
- defaultFont: task.defaultFont
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"
23048
22937
  });
23049
22938
  });
23050
22939
  }
@@ -23516,7 +23405,7 @@ var Legend = ({ chartRef, legendConfig }) => {
23516
23405
  };
23517
23406
  //#endregion
23518
23407
  //#region src/components/line-chart/line-chart.tsx
23519
- Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, lineMarkersPlugin);
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
23409
  var LineChart = (props) => {
23521
23410
  setDefaultTheme();
23522
23411
  const chartRef = useRef(null);