@oliasoft-open-source/charts-library 5.10.0 → 5.11.0

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.
package/dist/index.d.ts CHANGED
@@ -1193,16 +1193,16 @@ export { }
1193
1193
 
1194
1194
  declare module 'chart.js' {
1195
1195
  interface PluginOptionsByType<TType extends ChartType> {
1196
- calloutConnectorPlugin?: {
1197
- enableCalloutAnnotation?: boolean;
1198
- };
1196
+ annotationDraggerPlugin?: AnnotationDraggerPluginOptions;
1199
1197
  }
1200
1198
  }
1201
1199
 
1202
1200
 
1203
1201
  declare module 'chart.js' {
1204
1202
  interface PluginOptionsByType<TType extends ChartType> {
1205
- annotationDraggerPlugin?: AnnotationDraggerPluginOptions;
1203
+ calloutConnectorPlugin?: {
1204
+ enableCalloutAnnotation?: boolean;
1205
+ };
1206
1206
  }
1207
1207
  }
1208
1208
 
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;
@@ -20892,87 +20892,590 @@ function addTransparency(input, alpha, out = "rgb") {
20892
20892
  }
20893
20893
  //#endregion
20894
20894
  //#region src/components/common/helpers/callout-helpers/callout-helpers.ts
20895
+ var DEFAULT_FONT_SIZE = 12;
20896
+ var DEFAULT_PADDING = 5;
20897
+ var DEFAULT_MARGIN = 8;
20898
+ var DEFAULT_LABEL_GAP = 8;
20899
+ var DEFAULT_MIN_CONNECTOR_LENGTH = 14;
20900
+ var DEFAULT_HORIZONTAL_OFFSET = 18;
20901
+ var DEFAULT_VERTICAL_OFFSET = -18;
20902
+ var DEFAULT_ROW_GAP = 22;
20903
+ var DEFAULT_ITEM_GAP = 8;
20904
+ var DEFAULT_OVERLAP_ONLY_ITEM_GAP = 14;
20905
+ var DEFAULT_LANE_GAP = 12;
20906
+ var clamp$1 = (value, min, max) => {
20907
+ if (Number.isNaN(value)) return min;
20908
+ if (min > max) return min;
20909
+ return Math.min(Math.max(value, min), max);
20910
+ };
20911
+ var clampRelaxed = (value, min, max) => {
20912
+ if (min <= max) return clamp$1(value, min, max);
20913
+ if (value < max) return max;
20914
+ if (value > min) return min;
20915
+ return value;
20916
+ };
20917
+ var getNumericValue = (value, fallback = 0) => {
20918
+ return typeof value === "number" ? value : fallback;
20919
+ };
20920
+ var getCalloutCfg = (annotation) => {
20921
+ return annotation?.labelConfig?.callout ?? {};
20922
+ };
20923
+ var getChartFromCtx$1 = (ctx) => {
20924
+ return ctx?.chart?.chart ?? ctx?.chart;
20925
+ };
20895
20926
  var getXScale = (chart, axisId) => {
20896
20927
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20897
20928
  if (chart?.scales?.x) return chart.scales.x;
20898
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("x"));
20929
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20930
+ return item?.toLowerCase()?.includes("x");
20931
+ });
20899
20932
  return key ? chart?.scales?.[key] : void 0;
20900
20933
  };
20901
20934
  var getYScale = (chart, axisId) => {
20902
20935
  if (axisId && chart?.scales?.[axisId]) return chart.scales[axisId];
20903
20936
  if (chart?.scales?.y) return chart.scales.y;
20904
- const key = Object.keys(chart?.scales ?? {})?.find((k) => k?.toLowerCase()?.includes("y"));
20937
+ const key = Object.keys(chart?.scales ?? {})?.find((item) => {
20938
+ return item?.toLowerCase()?.includes("y");
20939
+ });
20905
20940
  return key ? chart?.scales?.[key] : void 0;
20906
20941
  };
20942
+ var resolveFontSize = (font) => {
20943
+ if (typeof font === "string") {
20944
+ const match = font.match(/(\d+(?:\.\d+)?)px/);
20945
+ return match ? Number(match[1]) : DEFAULT_FONT_SIZE;
20946
+ }
20947
+ if (font && typeof font === "object" && "size" in font) {
20948
+ const size = Number(font.size);
20949
+ return Number.isFinite(size) ? size : DEFAULT_FONT_SIZE;
20950
+ }
20951
+ return DEFAULT_FONT_SIZE;
20952
+ };
20953
+ var resolvePadding = (padding) => {
20954
+ if (typeof padding === "number") return {
20955
+ top: padding,
20956
+ right: padding,
20957
+ bottom: padding,
20958
+ left: padding
20959
+ };
20960
+ if (padding && typeof padding === "object") {
20961
+ const typedPadding = padding;
20962
+ return {
20963
+ top: Number(typedPadding.top ?? DEFAULT_PADDING),
20964
+ right: Number(typedPadding.right ?? DEFAULT_PADDING),
20965
+ bottom: Number(typedPadding.bottom ?? DEFAULT_PADDING),
20966
+ left: Number(typedPadding.left ?? DEFAULT_PADDING)
20967
+ };
20968
+ }
20969
+ return {
20970
+ top: DEFAULT_PADDING,
20971
+ right: DEFAULT_PADDING,
20972
+ bottom: DEFAULT_PADDING,
20973
+ left: DEFAULT_PADDING
20974
+ };
20975
+ };
20976
+ var estimateLabelSize = (chart, annotation, calloutCfg) => {
20977
+ const content = `${annotation?.label ?? ""}`;
20978
+ const fontSize = resolveFontSize(calloutCfg?.font);
20979
+ const padding = resolvePadding(calloutCfg?.padding);
20980
+ const lines = content.split("\n");
20981
+ const fallbackWidth = Math.max(...lines.map((line) => line.length), 0) * fontSize * .62;
20982
+ let measuredWidth = fallbackWidth;
20983
+ if (chart?.ctx) {
20984
+ chart.ctx.save();
20985
+ chart.ctx.font = typeof calloutCfg?.font === "string" ? calloutCfg.font : `${fontSize}px sans-serif`;
20986
+ measuredWidth = Math.max(...lines.map((line) => chart.ctx.measureText(line).width), fallbackWidth);
20987
+ chart.ctx.restore();
20988
+ }
20989
+ return {
20990
+ width: measuredWidth + padding.left + padding.right,
20991
+ height: lines.length * fontSize + padding.top + padding.bottom
20992
+ };
20993
+ };
20994
+ var getAxisId = (annotation, axis) => {
20995
+ const axisScaleId = axis === "x" ? annotation?.xScaleID : annotation?.yScaleID;
20996
+ if (axisScaleId) return axisScaleId;
20997
+ if (annotation?.annotationAxis?.startsWith(axis)) return annotation.annotationAxis;
20998
+ };
20999
+ var getAnchorPixel = (chart, annotation, axis, area) => {
21000
+ const scale = axis === "x" ? getXScale(chart, getAxisId(annotation, "x")) : getYScale(chart, getAxisId(annotation, "y"));
21001
+ const directValue = axis === "x" ? annotation?.xValue : annotation?.yValue;
21002
+ if (typeof directValue === "number" && scale) return scale.getPixelForValue(directValue);
21003
+ const minKey = axis === "x" ? "xMin" : "yMin";
21004
+ const maxKey = axis === "x" ? "xMax" : "yMax";
21005
+ const minValue = annotation?.[minKey];
21006
+ const maxValue = annotation?.[maxKey];
21007
+ if (typeof minValue === "number" && typeof maxValue === "number" && scale) return (scale.getPixelForValue(minValue) + scale.getPixelForValue(maxValue)) / 2;
21008
+ if (typeof minValue === "number" && scale) return scale.getPixelForValue(minValue);
21009
+ if (typeof maxValue === "number" && scale) return scale.getPixelForValue(maxValue);
21010
+ return axis === "x" ? (area.left + area.right) / 2 : (area.top + area.bottom) / 2;
21011
+ };
21012
+ var hasAxisAnchor = (annotation, axis) => {
21013
+ 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";
21014
+ return typeof annotation?.yValue === "number" || typeof annotation?.yMin === "number" || typeof annotation?.yMax === "number" || annotation?.type === "box" || annotation?.type === "label" || annotation?.type === "ellipse";
21015
+ };
21016
+ var getBoxHalfWidth = (chart, area, annotation) => {
21017
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21018
+ if (xScale && typeof annotation?.xMin === "number" && typeof annotation?.xMax === "number") return Math.abs(xScale.getPixelForValue(annotation.xMax) - xScale.getPixelForValue(annotation.xMin)) / 2;
21019
+ if (annotation?.type === "box") return (area.right - area.left) / 2;
21020
+ return annotation?.radius ?? 0;
21021
+ };
21022
+ var getBoxHalfHeight = (chart, annotation) => {
21023
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21024
+ if (yScale && typeof annotation?.yMin === "number" && typeof annotation?.yMax === "number") return Math.abs(yScale.getPixelForValue(annotation.yMax) - yScale.getPixelForValue(annotation.yMin)) / 2;
21025
+ return annotation?.radius ?? 0;
21026
+ };
21027
+ var getReferencePoint$1 = (chart, area, annotation) => {
21028
+ const anchorX = getAnchorPixel(chart, annotation, "x", area);
21029
+ const anchorY = getAnchorPixel(chart, annotation, "y", area);
21030
+ const halfWidth = getBoxHalfWidth(chart, area, annotation);
21031
+ const halfHeight = getBoxHalfHeight(chart, annotation);
21032
+ const labelOffsetPx = getNumericValue(annotation?.labelOffsetPx, 14);
21033
+ const xAdjust = getNumericValue(annotation?.labelConfig?.xAdjust, 0);
21034
+ const yAdjust = getNumericValue(annotation?.labelConfig?.yAdjust, 0);
21035
+ const position = annotation?.labelConfig?.position;
21036
+ let x = anchorX;
21037
+ let y = anchorY;
21038
+ let side = anchorX >= (area.left + area.right) / 2 ? "right" : "left";
21039
+ switch (position) {
21040
+ case Position.Left:
21041
+ x -= Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21042
+ side = "left";
21043
+ break;
21044
+ case Position.Right:
21045
+ x += Math.max(halfWidth, annotation?.radius ?? 0) + labelOffsetPx;
21046
+ side = "right";
21047
+ break;
21048
+ case Position.TopLeft:
21049
+ x -= halfWidth;
21050
+ y -= halfHeight;
21051
+ side = "left";
21052
+ break;
21053
+ case Position.TopRight:
21054
+ x += halfWidth;
21055
+ y -= halfHeight;
21056
+ side = "right";
21057
+ break;
21058
+ case Position.BottomLeft:
21059
+ x -= halfWidth;
21060
+ y += halfHeight;
21061
+ side = "left";
21062
+ break;
21063
+ case Position.BottomRight:
21064
+ x += halfWidth;
21065
+ y += halfHeight;
21066
+ side = "right";
21067
+ break;
21068
+ case Position.Bottom:
21069
+ y += Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21070
+ break;
21071
+ case Position.Top:
21072
+ default:
21073
+ y -= Math.max(halfHeight, annotation?.radius ?? 0) + labelOffsetPx;
21074
+ break;
21075
+ }
21076
+ return {
21077
+ x: x + xAdjust,
21078
+ y: y + yAdjust,
21079
+ side
21080
+ };
21081
+ };
21082
+ var getOriginalLabelRect = (chart, area, annotation, index) => {
21083
+ const size = estimateLabelSize(chart, annotation, getCalloutCfg(annotation));
21084
+ const reference = getReferencePoint$1(chart, area, annotation);
21085
+ const direction = reference.side === "right" ? 1 : -1;
21086
+ const centerX = reference.x + direction * (size.width / 2);
21087
+ const centerY = reference.y;
21088
+ return {
21089
+ id: `callout-annotation-${index}`,
21090
+ side: reference.side,
21091
+ left: centerX - size.width / 2,
21092
+ right: centerX + size.width / 2,
21093
+ top: centerY - size.height / 2,
21094
+ bottom: centerY + size.height / 2
21095
+ };
21096
+ };
21097
+ var isRectOverlapping = (left, right) => {
21098
+ return !(left.right <= right.left || right.right <= left.left || left.bottom <= right.top || right.bottom <= left.top);
21099
+ };
21100
+ var getOverlappingCalloutIds = (chart) => {
21101
+ const area = chart?.chartArea;
21102
+ const annotationsData = (chart?.options)?.annotations?.annotationsData ?? [];
21103
+ if (!chart || !area) return /* @__PURE__ */ new Set();
21104
+ const rects = annotationsData.flatMap((annotation, index) => {
21105
+ const calloutCfg = getCalloutCfg(annotation);
21106
+ if (!calloutCfg?.enabled || !calloutCfg?.onlyWhenOverlapping) return [];
21107
+ return [getOriginalLabelRect(chart, area, annotation, index)];
21108
+ });
21109
+ const overlappingIds = /* @__PURE__ */ new Set();
21110
+ rects.forEach((rect, index) => {
21111
+ rects.slice(index + 1).forEach((otherRect) => {
21112
+ if (rect.side !== otherRect.side) return;
21113
+ if (!isRectOverlapping(rect, otherRect)) return;
21114
+ overlappingIds.add(rect.id);
21115
+ overlappingIds.add(otherRect.id);
21116
+ });
21117
+ });
21118
+ return overlappingIds;
21119
+ };
21120
+ var isCalloutOverlapping = (ctx, refAnnotation, index) => {
21121
+ const chart = getChartFromCtx$1(ctx);
21122
+ if (!getCalloutCfg(refAnnotation)?.onlyWhenOverlapping) return true;
21123
+ if (!chart?.chartArea) return false;
21124
+ return getOverlappingCalloutIds(chart).has(`callout-annotation-${index}`);
21125
+ };
21126
+ var getPreferredX = (chart, area, annotation, calloutCfg, width) => {
21127
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21128
+ const explicitXValue = typeof calloutCfg?.xValue === "number" ? calloutCfg.xValue : void 0;
21129
+ if (typeof explicitXValue === "number" && xScale) return xScale.getPixelForValue(explicitXValue);
21130
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21131
+ const halfWidth = width / 2;
21132
+ const reference = getReferencePoint$1(chart, area, annotation);
21133
+ const direction = reference.side === "right" ? 1 : -1;
21134
+ const gap = getNumericValue(calloutCfg?.gapPx, DEFAULT_LABEL_GAP);
21135
+ const minConnectorLength = getNumericValue(calloutCfg?.minConnectorLengthPx, DEFAULT_MIN_CONNECTOR_LENGTH);
21136
+ return clamp$1(reference.x + direction * (halfWidth + gap + minConnectorLength), area.left + margin + halfWidth, area.right - margin - halfWidth);
21137
+ };
21138
+ var getPreferredY = (chart, area, annotation, calloutCfg) => {
21139
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21140
+ const explicitYValue = typeof calloutCfg?.yValue === "number" ? calloutCfg.yValue : void 0;
21141
+ if (typeof explicitYValue === "number" && yScale) return yScale.getPixelForValue(explicitYValue);
21142
+ const reference = getReferencePoint$1(chart, area, annotation);
21143
+ const dy = typeof calloutCfg?.yAdjust === "number" ? 0 : DEFAULT_VERTICAL_OFFSET;
21144
+ return reference.y + dy;
21145
+ };
21146
+ var getCalloutItems = (chart, area) => {
21147
+ const overlappingIds = getOverlappingCalloutIds(chart);
21148
+ return ((chart?.options)?.annotations?.annotationsData ?? []).flatMap((annotation, index) => {
21149
+ const calloutCfg = getCalloutCfg(annotation);
21150
+ const id = `callout-annotation-${index}`;
21151
+ if (!calloutCfg?.enabled) return [];
21152
+ const size = estimateLabelSize(chart, annotation, calloutCfg);
21153
+ const xScale = getXScale(chart, getAxisId(annotation, "x"));
21154
+ const yScale = getYScale(chart, getAxisId(annotation, "y"));
21155
+ const reference = getReferencePoint$1(chart, area, annotation);
21156
+ const preferredX = getPreferredX(chart, area, annotation, calloutCfg, size.width);
21157
+ const preferredY = getPreferredY(chart, area, annotation, calloutCfg);
21158
+ const hasStoredX = typeof calloutCfg?.xValue === "number" || !xScale;
21159
+ const hasStoredY = typeof calloutCfg?.yValue === "number" || !yScale;
21160
+ const side = reference.side;
21161
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21162
+ return [{
21163
+ annotation,
21164
+ calloutCfg,
21165
+ id,
21166
+ index,
21167
+ anchorY: reference.y,
21168
+ preferredX,
21169
+ preferredY,
21170
+ width: size.width,
21171
+ height: size.height,
21172
+ isFixed: hasStoredX && hasStoredY,
21173
+ isOverlapping: overlappingIds.has(id),
21174
+ overlapOnly: Boolean(calloutCfg?.onlyWhenOverlapping),
21175
+ minX: area.left + margin + size.width / 2,
21176
+ maxX: area.right - margin - size.width / 2,
21177
+ minY: area.top + margin + size.height / 2,
21178
+ maxY: area.bottom - margin - size.height / 2,
21179
+ side
21180
+ }];
21181
+ });
21182
+ };
21183
+ var hasVerticalOverlap = (items) => {
21184
+ const sorted = [...items].sort((left, right) => left.preferredY - right.preferredY);
21185
+ for (let index = 1; index < sorted.length; index += 1) {
21186
+ const previous = sorted[index - 1];
21187
+ const current = sorted[index];
21188
+ const gap = previous.height / 2 + current.height / 2 + DEFAULT_ITEM_GAP;
21189
+ if (current.preferredY - previous.preferredY < gap) return true;
21190
+ }
21191
+ return false;
21192
+ };
21193
+ var overlapsOccupiedRect = (centerY, height, occupiedRects) => {
21194
+ const top = centerY - height / 2;
21195
+ const bottom = centerY + height / 2;
21196
+ return occupiedRects.find((rect) => !(bottom <= rect.top || top >= rect.bottom));
21197
+ };
21198
+ var getLayoutGap = (item) => {
21199
+ return item.overlapOnly ? DEFAULT_OVERLAP_ONLY_ITEM_GAP : DEFAULT_ITEM_GAP;
21200
+ };
21201
+ var getLayoutOrderY = (item) => {
21202
+ return item.isFixed ? item.preferredY : item.anchorY;
21203
+ };
21204
+ var intervalsOverlap = (top, bottom, otherTop, otherBottom) => {
21205
+ return !(bottom <= otherTop || top >= otherBottom);
21206
+ };
21207
+ var assignLabelColumns = (items, layout) => {
21208
+ const columns = [];
21209
+ const hasPrimaryColumn = items.some((item) => {
21210
+ return !item.overlapOnly || !item.isOverlapping;
21211
+ });
21212
+ [...items].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right)).forEach((item) => {
21213
+ const current = layout.get(item.id);
21214
+ if (!current) return;
21215
+ const top = current.yValue - item.height / 2 - getLayoutGap(item) / 2;
21216
+ const bottom = current.yValue + item.height / 2 + getLayoutGap(item) / 2;
21217
+ let columnIndex = hasPrimaryColumn && item.overlapOnly && item.isOverlapping ? 1 : 0;
21218
+ while (true) {
21219
+ if (!(columns[columnIndex] ?? []).some((placed) => {
21220
+ return intervalsOverlap(top, bottom, placed.top, placed.bottom);
21221
+ })) {
21222
+ if (!columns[columnIndex]) columns[columnIndex] = [];
21223
+ columns[columnIndex].push({
21224
+ id: item.id,
21225
+ top,
21226
+ bottom,
21227
+ width: item.width
21228
+ });
21229
+ break;
21230
+ }
21231
+ columnIndex += 1;
21232
+ }
21233
+ });
21234
+ return columns;
21235
+ };
21236
+ var applyLaneLayout = (items, layout, occupiedRects) => {
21237
+ if (items.length <= 1) return;
21238
+ const side = items[0]?.side;
21239
+ const columns = assignLabelColumns(items, layout);
21240
+ const getCurrentRect = (item) => {
21241
+ const centerX = layout.get(item.id)?.xValue ?? item.preferredX;
21242
+ return {
21243
+ left: centerX - item.width / 2,
21244
+ right: centerX + item.width / 2
21245
+ };
21246
+ };
21247
+ const referenceItems = items.filter((item) => {
21248
+ return columns[0]?.some((placed) => placed.id === item.id);
21249
+ });
21250
+ const referenceRects = referenceItems.length ? referenceItems : items;
21251
+ const baseEdge = side === "right" ? occupiedRects.length ? Math.min(...occupiedRects.map((rect) => rect.left)) - DEFAULT_LANE_GAP : Math.min(...referenceRects.map((item) => getCurrentRect(item).left)) : occupiedRects.length ? Math.max(...occupiedRects.map((rect) => rect.right)) + DEFAULT_LANE_GAP : Math.max(...referenceRects.map((item) => getCurrentRect(item).right));
21252
+ columns.forEach((column, columnIndex) => {
21253
+ const columnWidth = Math.max(...column.map((placed) => placed.width));
21254
+ column?.forEach((placed) => {
21255
+ const item = items.find((candidate) => candidate.id === placed.id);
21256
+ const current = layout.get(placed.id);
21257
+ if (!item || !current) return;
21258
+ const xValue = side === "right" ? baseEdge - columnIndex * (columnWidth + DEFAULT_LANE_GAP) - item.width / 2 : baseEdge + columnIndex * (columnWidth + DEFAULT_LANE_GAP) + item.width / 2;
21259
+ layout.set(placed.id, {
21260
+ ...current,
21261
+ xValue: clamp$1(xValue, item.minX, item.maxX)
21262
+ });
21263
+ });
21264
+ });
21265
+ };
21266
+ var getOrderBounds = (item, occupiedRects) => {
21267
+ const previousRect = [...occupiedRects].filter((rect) => rect.centerY <= item.anchorY).at(-1);
21268
+ const nextRect = occupiedRects.find((rect) => rect.centerY >= item.anchorY);
21269
+ const gap = getLayoutGap(item);
21270
+ return {
21271
+ minCenter: previousRect ? previousRect.bottom + gap + item.height / 2 : item.minY,
21272
+ maxCenter: nextRect ? nextRect.top - gap - item.height / 2 : item.maxY
21273
+ };
21274
+ };
21275
+ var layoutSide = (items, occupiedRects = []) => {
21276
+ const sortedOccupiedRects = [...occupiedRects].sort((left, right) => left.centerY - right.centerY);
21277
+ const sorted = [...items].sort((left, right) => getLayoutOrderY(left) - getLayoutOrderY(right));
21278
+ const result = /* @__PURE__ */ new Map();
21279
+ if (!sorted.filter((item) => !item.isFixed).length) {
21280
+ sorted.forEach((item) => {
21281
+ result.set(item.id, {
21282
+ xValue: item.preferredX,
21283
+ yValue: item.preferredY
21284
+ });
21285
+ });
21286
+ return result;
21287
+ }
21288
+ const shouldReflow = sorted.some((item) => !item.overlapOnly) || hasVerticalOverlap(sorted);
21289
+ sorted.forEach((item) => {
21290
+ result.set(item.id, {
21291
+ xValue: item.preferredX,
21292
+ yValue: clamp$1(item.preferredY, item.minY, item.maxY)
21293
+ });
21294
+ });
21295
+ if (!shouldReflow) return result;
21296
+ let previousBottom = Number.NEGATIVE_INFINITY;
21297
+ sorted.forEach((item) => {
21298
+ if (item.isFixed) {
21299
+ const yValue = clamp$1(item.preferredY, item.minY, item.maxY);
21300
+ result.set(item.id, {
21301
+ xValue: item.preferredX,
21302
+ yValue
21303
+ });
21304
+ previousBottom = yValue + item.height / 2;
21305
+ return;
21306
+ }
21307
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21308
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter);
21309
+ const minCenter = Math.max(item.minY, orderBounds.minCenter, previousBottom + item.height / 2 + getLayoutGap(item));
21310
+ let yValue = clampRelaxed(item.preferredY, minCenter, maxCenter);
21311
+ let overlappingRect = overlapsOccupiedRect(yValue, item.height, sortedOccupiedRects);
21312
+ while (overlappingRect) {
21313
+ const nextCandidate = overlappingRect.bottom + getLayoutGap(item) + item.height / 2;
21314
+ if (nextCandidate === yValue) break;
21315
+ yValue = clampRelaxed(nextCandidate, minCenter, maxCenter);
21316
+ overlappingRect = overlapsOccupiedRect(yValue, item.height, sortedOccupiedRects);
21317
+ }
21318
+ result.set(item.id, {
21319
+ xValue: item.preferredX,
21320
+ yValue
21321
+ });
21322
+ previousBottom = yValue + item.height / 2;
21323
+ });
21324
+ let nextTop = Number.POSITIVE_INFINITY;
21325
+ [...sorted].reverse().forEach((item) => {
21326
+ const current = result.get(item.id);
21327
+ if (!current) return;
21328
+ if (item.isFixed) {
21329
+ nextTop = current.yValue - item.height / 2;
21330
+ return;
21331
+ }
21332
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21333
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter, nextTop - item.height / 2 - getLayoutGap(item));
21334
+ const minCenter = Math.max(item.minY, orderBounds.minCenter);
21335
+ const yValue = clampRelaxed(current.yValue, minCenter, maxCenter);
21336
+ result.set(item.id, {
21337
+ ...current,
21338
+ yValue
21339
+ });
21340
+ nextTop = yValue - item.height / 2;
21341
+ });
21342
+ previousBottom = Number.NEGATIVE_INFINITY;
21343
+ sorted.forEach((item) => {
21344
+ const current = result.get(item.id);
21345
+ if (!current) return;
21346
+ if (item.isFixed) {
21347
+ previousBottom = current.yValue + item.height / 2;
21348
+ return;
21349
+ }
21350
+ const orderBounds = getOrderBounds(item, sortedOccupiedRects);
21351
+ const maxCenter = Math.min(item.maxY, orderBounds.maxCenter);
21352
+ const minCenter = Math.max(item.minY, orderBounds.minCenter, previousBottom + item.height / 2 + getLayoutGap(item));
21353
+ const yValue = clampRelaxed(current.yValue, minCenter, maxCenter);
21354
+ result.set(item.id, {
21355
+ ...current,
21356
+ yValue
21357
+ });
21358
+ previousBottom = yValue + item.height / 2;
21359
+ });
21360
+ if (sorted.some((item) => item.overlapOnly)) {
21361
+ const untouchedPrimary = sorted.filter((item) => {
21362
+ return !item.isFixed && item.overlapOnly && !item.isOverlapping;
21363
+ });
21364
+ const secondaryMovable = sorted.filter((item) => {
21365
+ return !item.isFixed && item.overlapOnly && item.isOverlapping;
21366
+ });
21367
+ untouchedPrimary.forEach((item) => {
21368
+ result.set(item.id, {
21369
+ xValue: item.preferredX,
21370
+ yValue: clamp$1(item.preferredY, item.minY, item.maxY)
21371
+ });
21372
+ });
21373
+ if (secondaryMovable.length) applyLaneLayout(secondaryMovable, result, untouchedPrimary.map((item) => {
21374
+ const current = result.get(item.id);
21375
+ const centerX = current?.xValue ?? item.preferredX;
21376
+ const centerY = current?.yValue ?? item.preferredY;
21377
+ return {
21378
+ top: centerY - item.height / 2,
21379
+ bottom: centerY + item.height / 2,
21380
+ centerY,
21381
+ left: centerX - item.width / 2,
21382
+ right: centerX + item.width / 2
21383
+ };
21384
+ }));
21385
+ return result;
21386
+ }
21387
+ applyLaneLayout(sorted.filter((item) => !item.isFixed), result, []);
21388
+ return result;
21389
+ };
21390
+ var getCalloutLayoutPixels = (chart) => {
21391
+ const area = chart?.chartArea;
21392
+ if (!chart || !area) return /* @__PURE__ */ new Map();
21393
+ const grouped = getCalloutItems(chart, area).map((item) => {
21394
+ const xValue = clamp$1(item.preferredX, item.minX, item.maxX);
21395
+ return {
21396
+ ...item,
21397
+ preferredX: xValue
21398
+ };
21399
+ }).reduce((accumulator, item) => {
21400
+ accumulator[item.side].push(item);
21401
+ return accumulator;
21402
+ }, {
21403
+ left: [],
21404
+ right: []
21405
+ });
21406
+ const leftLayout = layoutSide(grouped.left, []);
21407
+ const rightLayout = layoutSide(grouped.right, []);
21408
+ return new Map([...leftLayout.entries(), ...rightLayout.entries()]);
21409
+ };
21410
+ var getCalloutLayout = (ctx, refAnnotation, index) => {
21411
+ const chart = getChartFromCtx$1(ctx);
21412
+ const xScale = chart ? getXScale(chart, getAxisId(refAnnotation, "x")) : void 0;
21413
+ const yScale = chart ? getYScale(chart, getAxisId(refAnnotation, "y")) : void 0;
21414
+ if (!chart || !yScale || !chart.chartArea) return null;
21415
+ if (!xScale && hasAxisAnchor(refAnnotation, "x")) return null;
21416
+ if (!xScale) return null;
21417
+ const layout = getCalloutLayoutPixels(chart).get(`callout-annotation-${index}`);
21418
+ if (!layout) return null;
21419
+ return {
21420
+ xValue: xScale.getValueForPixel(layout.xValue),
21421
+ yValue: yScale.getValueForPixel(layout.yValue)
21422
+ };
21423
+ };
20907
21424
  var resolveCalloutXValue = (ctx, refAnnotation, calloutCfg, index) => {
20908
- const chart = ctx?.chart;
21425
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21426
+ if (layout) return layout.xValue;
21427
+ const chart = getChartFromCtx$1(ctx);
20909
21428
  const xVal = refAnnotation?.xValue;
20910
21429
  if (typeof xVal !== "number") return 0;
20911
21430
  if (!chart) return xVal;
20912
- const scale = getXScale(chart, refAnnotation?.annotationAxis);
21431
+ const scale = getXScale(chart, getAxisId(refAnnotation, "x"));
20913
21432
  if (!scale) return xVal;
20914
21433
  const hasCustomOffset = typeof calloutCfg?.xAdjust === "number";
20915
21434
  const side = index % 2 === 0 ? 1 : -1;
20916
- const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * 60;
21435
+ const dx = hasCustomOffset ? calloutCfg?.xAdjust : side * DEFAULT_HORIZONTAL_OFFSET;
20917
21436
  const basePx = scale.getPixelForValue(xVal);
20918
- const cxBase = basePx + dx;
20919
21437
  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);
21438
+ const element = ctx?.element;
21439
+ if (!area || !element) return scale.getValueForPixel(basePx + dx);
21440
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21441
+ const halfWidth = (element?.width ?? 0) / 2;
21442
+ const clampedPx = clamp$1(basePx + dx, area.left + margin + halfWidth, area.right - margin - halfWidth);
21443
+ return scale.getValueForPixel(clampedPx);
20934
21444
  };
20935
21445
  var resolveCalloutYValue = (ctx, refAnnotation, calloutCfg, index) => {
20936
- const chart = ctx?.chart;
20937
- const yVal = refAnnotation.yValue;
21446
+ const layout = getCalloutLayout(ctx, refAnnotation, index);
21447
+ if (layout) return layout.yValue;
21448
+ const chart = getChartFromCtx$1(ctx);
21449
+ const yVal = refAnnotation?.yValue;
20938
21450
  if (typeof yVal !== "number") return 0;
20939
21451
  if (!chart) return yVal;
20940
- const scale = getYScale(chart, refAnnotation?.annotationAxis);
21452
+ const scale = getYScale(chart, getAxisId(refAnnotation, "y"));
20941
21453
  if (!scale) return yVal;
20942
21454
  const hasCustomOffset = typeof calloutCfg?.yAdjust === "number";
20943
21455
  const row = Math.floor(index / 2);
20944
- const dy = hasCustomOffset ? calloutCfg?.yAdjust : -40 - row * 22;
21456
+ const dy = hasCustomOffset ? calloutCfg?.yAdjust : DEFAULT_VERTICAL_OFFSET - row * DEFAULT_ROW_GAP;
20945
21457
  const basePx = scale.getPixelForValue(yVal);
20946
- const cyBase = basePx + dy;
20947
21458
  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;
21459
+ const element = ctx?.element;
21460
+ if (!area || !element) return scale.getValueForPixel(basePx + dy);
21461
+ const margin = getNumericValue(calloutCfg?.margin, DEFAULT_MARGIN);
21462
+ const halfHeight = (element?.height ?? 0) / 2;
21463
+ const clampedPx = clamp$1(basePx + dy, area.top + margin + halfHeight, area.bottom - margin - halfHeight);
21464
+ return scale.getValueForPixel(clampedPx);
21465
+ };
21466
+ var isInRange = (value, min, max) => value >= min && value <= max;
20964
21467
  var isCalloutAnchorInChartArea = (ctx, refAnnotation, margin = 0) => {
20965
- const chart = ctx?.chart;
21468
+ const chart = getChartFromCtx$1(ctx);
20966
21469
  const area = chart?.chartArea;
20967
21470
  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);
21471
+ const xScale = getXScale(chart, getAxisId(refAnnotation, "x"));
21472
+ const yScale = getYScale(chart, getAxisId(refAnnotation, "y"));
21473
+ const hasXValues = hasAxisAnchor(refAnnotation, "x");
21474
+ const hasYValues = hasAxisAnchor(refAnnotation, "y");
21475
+ if (hasXValues && !xScale || hasYValues && !yScale) return false;
21476
+ if (!hasXValues || !hasYValues) return false;
21477
+ const x = getAnchorPixel(chart, refAnnotation, "x", area);
21478
+ const y = getAnchorPixel(chart, refAnnotation, "y", area);
20976
21479
  return isInRange(x, area.left + margin, area.right - margin) && isInRange(y, area.top + margin, area.bottom - margin);
20977
21480
  };
20978
21481
  //#endregion
@@ -21273,7 +21776,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21273
21776
  const calloutCfg = refAnnotation?.labelConfig?.callout ?? {};
21274
21777
  const baseAnnotation = {
21275
21778
  ...baseRest,
21276
- label: { display: false }
21779
+ label: {
21780
+ ...label,
21781
+ display: () => false
21782
+ }
21277
21783
  };
21278
21784
  const color = calloutCfg?.color ?? "hsl(60, 10.34482759%, 12.5%)";
21279
21785
  const font = calloutCfg?.font ?? `12px "Roobert", "Noto Sans", sans-serif`;
@@ -21281,10 +21787,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21281
21787
  const onCalloutDragStart = typeof calloutCfg?.onDragStart === "function" ? calloutCfg.onDragStart : void 0;
21282
21788
  const onCalloutDrag = typeof calloutCfg?.onDrag === "function" ? calloutCfg.onDrag : void 0;
21283
21789
  const onCalloutDragEnd = typeof calloutCfg?.onDragEnd === "function" ? calloutCfg.onDragEnd : void 0;
21284
- const strokeStyle = calloutCfg.strokeStyle ?? baseAnnotation.borderColor;
21790
+ const strokeStyle = calloutCfg.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
21285
21791
  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;
21792
+ const startOffset = typeof calloutCfg.startOffset === "number" ? calloutCfg.startOffset : 2;
21793
+ const endOffset = typeof calloutCfg.endOffset === "number" ? calloutCfg.endOffset : .5;
21288
21794
  const calloutId = `callout-annotation-${index}`;
21289
21795
  return [baseAnnotation, {
21290
21796
  id: calloutId,
@@ -21294,14 +21800,18 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21294
21800
  color: (ctx) => {
21295
21801
  const chart = getChartFromCtx(ctx);
21296
21802
  if (!chart) return color;
21297
- if (chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21803
+ if (Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) && chart.hoveredAnnotationId === calloutId) return "#DB5B00";
21298
21804
  return color;
21299
21805
  },
21300
21806
  font,
21301
21807
  borderColor,
21302
21808
  borderWidth: BORDER_WIDTH.INITIAL,
21303
21809
  padding: 5,
21304
- display: (ctx) => (refAnnotation.display ?? true) && isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0),
21810
+ display: (ctx) => {
21811
+ if (!(refAnnotation.display ?? true)) return false;
21812
+ if (!isCalloutAnchorInChartArea(ctx, refAnnotation, calloutCfg.margin ?? 0)) return false;
21813
+ return true;
21814
+ },
21305
21815
  xValue: (ctx) => {
21306
21816
  const persistenceId = getChartFromCtx(ctx)?.options?.persistenceId;
21307
21817
  const stored = getAnnotationPosition(persistenceId, `callout-annotation-${index}`);
@@ -21318,10 +21828,10 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21318
21828
  displayDragCoordinates: false,
21319
21829
  enableDrag: true,
21320
21830
  enter: ({ element }, { chart }) => {
21321
- return handleLabelEnter(element, chart, { enableDrag: true });
21831
+ return handleLabelEnter(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21322
21832
  },
21323
21833
  leave: ({ element }, { chart }) => {
21324
- return handleLabelLeave(element, chart, { enableDrag: true });
21834
+ return handleLabelLeave(element, chart, { enableDrag: Boolean(chart?.options?.plugins?.annotationDraggerPlugin?.enabled) });
21325
21835
  },
21326
21836
  onDragStart: onCalloutDragStart ? () => (coords) => {
21327
21837
  return onCalloutDragStart(coords, refAnnotation);
@@ -21333,12 +21843,21 @@ var getCalloutAnnotation = (refAnnotation, index) => {
21333
21843
  return onCalloutDragEnd(coords, refAnnotation);
21334
21844
  } : void 0,
21335
21845
  calloutConnector: {
21336
- enabled: true,
21846
+ enabled: ({ chart }) => {
21847
+ if (!calloutCfg?.onlyWhenOverlapping) return true;
21848
+ return isCalloutOverlapping({ chart }, refAnnotation, index);
21849
+ },
21337
21850
  fromId: baseId,
21338
21851
  strokeStyle,
21339
21852
  lineWidth,
21340
21853
  startOffset,
21341
- endOffset
21854
+ endOffset,
21855
+ anchor: {
21856
+ position: refAnnotation?.labelConfig?.position,
21857
+ xAdjust: refAnnotation?.labelConfig?.xAdjust,
21858
+ yAdjust: refAnnotation?.labelConfig?.yAdjust,
21859
+ labelOffsetPx: refAnnotation?.labelOffsetPx
21860
+ }
21342
21861
  }
21343
21862
  }];
21344
21863
  };
@@ -22138,52 +22657,126 @@ var annotationDraggerPlugin = {
22138
22657
  };
22139
22658
  //#endregion
22140
22659
  //#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;
22660
+ var getRectangleEdgePoint = (el, targetX, targetY, offset) => {
22661
+ const width = el?.width ?? 0;
22662
+ const height = el?.height ?? 0;
22663
+ if (width <= 0 || height <= 0) return {
22664
+ x: el.centerX,
22665
+ y: el.centerY
22666
+ };
22667
+ const halfWidth = width / 2;
22668
+ const halfHeight = height / 2;
22669
+ const dx = targetX - el.centerX;
22670
+ const dy = targetY - el.centerY;
22671
+ const length = Math.sqrt(dx * dx + dy * dy) || 1;
22672
+ const ux = dx / length;
22673
+ const uy = dy / length;
22674
+ const tx = Math.abs(ux) > 1e-6 ? halfWidth / Math.abs(ux) : Number.POSITIVE_INFINITY;
22675
+ const ty = Math.abs(uy) > 1e-6 ? halfHeight / Math.abs(uy) : Number.POSITIVE_INFINITY;
22676
+ const distance = Math.min(tx, ty) + offset;
22677
+ return {
22678
+ x: el.centerX + ux * distance,
22679
+ y: el.centerY + uy * distance
22680
+ };
22681
+ };
22682
+ var getLabelEdgePoint = (labelEl, startX, endOffset) => {
22683
+ const width = labelEl?.width ?? 0;
22684
+ const height = labelEl?.height ?? 0;
22685
+ if (width <= 0 || height <= 0) return {
22686
+ x: labelEl.centerX,
22687
+ y: labelEl.centerY
22688
+ };
22689
+ const halfWidth = width / 2;
22690
+ const direction = startX <= labelEl.centerX ? -1 : 1;
22691
+ return {
22692
+ x: labelEl.centerX + direction * (halfWidth + endOffset),
22693
+ y: labelEl.centerY
22694
+ };
22695
+ };
22696
+ var getReferencePoint = (fromEl, connector) => {
22697
+ const { anchor } = connector;
22698
+ if (!anchor) return {
22699
+ x: fromEl.centerX,
22700
+ y: fromEl.centerY
22701
+ };
22702
+ const width = fromEl?.width ?? 0;
22703
+ const height = fromEl?.height ?? 0;
22704
+ const radius = fromEl?.options?.radius ?? 0;
22705
+ const halfWidth = width / 2;
22706
+ const halfHeight = height / 2;
22707
+ const labelOffset = anchor?.labelOffsetPx ?? 14;
22708
+ const xAdjust = anchor?.xAdjust ?? 0;
22709
+ const yAdjust = anchor?.yAdjust ?? 0;
22710
+ const position = anchor?.position ?? "top";
22711
+ let x = fromEl.centerX;
22712
+ let y = fromEl.centerY;
22713
+ switch (position) {
22714
+ case "left":
22715
+ x -= Math.max(halfWidth, radius) + labelOffset;
22716
+ break;
22717
+ case "right":
22718
+ x += Math.max(halfWidth, radius) + labelOffset;
22719
+ break;
22720
+ case "bottom":
22721
+ y += Math.max(halfHeight, radius) + labelOffset;
22722
+ break;
22723
+ case "top-left":
22724
+ x -= halfWidth;
22725
+ y -= halfHeight;
22726
+ break;
22727
+ case "top-right":
22728
+ x += halfWidth;
22729
+ y -= halfHeight;
22730
+ break;
22731
+ case "bottom-left":
22732
+ x -= halfWidth;
22733
+ y += halfHeight;
22734
+ break;
22735
+ case "bottom-right":
22736
+ x += halfWidth;
22737
+ y += halfHeight;
22738
+ break;
22739
+ default:
22740
+ y -= Math.max(halfHeight, radius) + labelOffset;
22741
+ break;
22742
+ }
22743
+ return {
22744
+ x: x + xAdjust,
22745
+ y: y + yAdjust
22746
+ };
22145
22747
  };
22146
22748
  var computeConnectorPoints = (fromEl, labelEl, connector) => {
22147
22749
  const x1c = fromEl?.centerX;
22148
22750
  const y1c = fromEl?.centerY;
22149
22751
  const cx = labelEl?.centerX;
22150
22752
  const cy = labelEl?.centerY;
22151
- const labelWidth = labelEl?.width ?? 0;
22152
- const labelHeight = labelEl?.height ?? 0;
22153
22753
  const ux = cx - x1c;
22154
22754
  const uy = cy - y1c;
22155
22755
  const len = Math.sqrt(ux * ux + uy * uy) || 1;
22156
22756
  const dx = ux / len;
22157
22757
  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
- }
22758
+ const startOffset = connector?.startOffset ?? 6;
22759
+ const isPointAnchor = typeof fromEl?.options?.radius === "number";
22760
+ const referencePoint = connector?.anchor && !isPointAnchor ? getReferencePoint(fromEl, connector) : isPointAnchor ? {
22761
+ x: x1c,
22762
+ y: y1c
22763
+ } : getRectangleEdgePoint(fromEl, cx, cy, startOffset);
22764
+ const endOffset = connector?.endOffset ?? 1;
22765
+ const end = typeof labelEl?.width === "number" && typeof labelEl?.height === "number" ? getLabelEdgePoint(labelEl, referencePoint.x, endOffset) : {
22766
+ x: cx - dx * endOffset,
22767
+ y: cy - dy * endOffset
22768
+ };
22769
+ const tickHalf = isPointAnchor ? 0 : (connector?.anchor?.tickSize ?? 8) / 2;
22770
+ const lineDirection = end.x >= referencePoint.x ? 1 : -1;
22182
22771
  return {
22183
- startX,
22184
- startY,
22185
- endX,
22186
- endY
22772
+ startX: referencePoint.x + lineDirection * tickHalf,
22773
+ startY: referencePoint.y,
22774
+ endX: end.x,
22775
+ endY: end.y,
22776
+ tickStartX: referencePoint.x - tickHalf,
22777
+ tickStartY: referencePoint.y,
22778
+ tickEndX: referencePoint.x + tickHalf,
22779
+ tickEndY: referencePoint.y
22187
22780
  };
22188
22781
  };
22189
22782
  //#endregion
@@ -22201,22 +22794,35 @@ var calloutConnectorPlugin = {
22201
22794
  if (!raw) return;
22202
22795
  (Array.isArray(raw) ? raw : Object.values(raw)).forEach((opt) => {
22203
22796
  const connector = opt?.calloutConnector;
22204
- if (!connector || !connector.enabled || !opt?.id) return;
22797
+ if (!connector || !opt?.id) return;
22205
22798
  const labelEl = elements.find((el) => el.options && el.options.id === opt.id);
22206
22799
  const fromEl = elements.find((el) => el.options && el.options.id === connector.fromId);
22207
22800
  if (!labelEl || !fromEl) return;
22208
22801
  if (labelEl.options?.display === false) return;
22209
- const { startX, startY, endX, endY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22802
+ if (!(typeof connector.enabled === "function" ? connector.enabled({
22803
+ chart,
22804
+ labelEl,
22805
+ fromEl,
22806
+ option: opt
22807
+ }) : connector.enabled !== false)) return;
22808
+ const { startX, startY, endX, endY, tickStartX, tickStartY, tickEndX, tickEndY } = computeConnectorPoints(fromEl, labelEl, connector) ?? {};
22210
22809
  const ctx = chart?.ctx;
22810
+ const strokeStyle = connector?.strokeStyle ?? "rgba(120, 126, 138, 0.9)";
22211
22811
  ctx?.save?.();
22212
22812
  ctx?.beginPath?.();
22213
22813
  ctx?.moveTo?.(startX, startY);
22214
22814
  ctx?.lineTo?.(endX, endY);
22215
22815
  ctx.lineWidth = connector?.lineWidth ?? 1;
22216
- ctx.strokeStyle = connector?.strokeStyle ?? "rgba(120,120,120,0.9)";
22816
+ ctx.strokeStyle = strokeStyle;
22217
22817
  if (connector?.lineDash && connector?.lineDash?.length) ctx?.setLineDash?.(connector.lineDash);
22218
22818
  ctx.lineCap = "round";
22219
22819
  ctx?.stroke?.();
22820
+ ctx?.beginPath?.();
22821
+ ctx?.moveTo?.(tickStartX, tickStartY);
22822
+ ctx?.lineTo?.(tickEndX, tickEndY);
22823
+ ctx.lineWidth = Math.max(connector?.lineWidth ?? 1, 1);
22824
+ ctx.strokeStyle = strokeStyle;
22825
+ ctx?.stroke?.();
22220
22826
  ctx?.restore?.();
22221
22827
  });
22222
22828
  }
@@ -38649,7 +39255,7 @@ var getGroupedColorScheme = (length, startingColor) => {
38649
39255
  });
38650
39256
  };
38651
39257
  //#endregion
38652
- export { AlignOptions, AnnotationType, AxisType, BarChartWithLegend as BarChart, COLORS, ChartDirection, ChartHoverMode, ChartType, CursorStyle, DragAxis, AnnotationType$1 as DraggableAnnotationType, Events, GradientDirection, Key, LineChartWithLegend as LineChart, PanZoomMode, PieChartWithLegend as PieChart, PointStyle, PointType, Position, ScaleType, ScatterChartWithLegend as ScatterChart, TooltipLabel, getGroupedColorScheme, initializeLineChart };
39258
+ export { AlignOptions, AnnotationType, AxisType, BarChartWithLegend as BarChart, COLORS, ChartDirection, ChartHoverMode, ChartType, CursorStyle, DragAxis, AnnotationType$1 as DraggableAnnotationType, Events, GradientDirection, Key, LineChartWithLegend as LineChart, LineMarkerDirection, LineMarkerLabelPosition, LineMarkerSide, LineMarkerTextAlign, PanZoomMode, PieChartWithLegend as PieChart, PointStyle, PointType, Position, ScaleType, ScatterChartWithLegend as ScatterChart, TooltipLabel, getGroupedColorScheme, initializeLineChart };
38653
39259
  (function() {
38654
39260
  //#region \0vite/all-css
38655
39261
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oliasoft-open-source/charts-library",
3
- "version": "5.10.0",
3
+ "version": "5.11.0",
4
4
  "description": "React Chart Library (based on Chart.js and react-chart-js-2)",
5
5
  "homepage": "https://gitlab.com/oliasoft-open-source/charts-library",
6
6
  "bugs": {