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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +98 -67
  2. package/dist/index.js +772 -138
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -329,7 +329,7 @@ export declare interface ICommonAnnotations {
329
329
  controlAnnotation?: boolean;
330
330
  enableDragAnnotation?: boolean;
331
331
  enableCalloutAnnotation?: boolean;
332
- verticalMarkersAnnotation?: IVerticalMarkersAnnotation;
332
+ lineMarkersAnnotation?: ILineMarkersAnnotation;
333
333
  annotationsData?: ICommonAnnotationsData[];
334
334
  labelAnnotation?: ICommonAnnotation;
335
335
  }
@@ -483,7 +483,7 @@ export declare interface ICommonOptions {
483
483
  dragData?: ICommonDragData;
484
484
  annotations?: ICommonAnnotations;
485
485
  plugins?: {
486
- verticalMarkersPlugin?: {
486
+ lineMarkersPlugin?: {
487
487
  enabled?: boolean;
488
488
  };
489
489
  };
@@ -771,6 +771,8 @@ export declare interface ILineLegend extends ICommonLegend {
771
771
  usePointStyle?: boolean;
772
772
  }
773
773
 
774
+ export declare type ILineMarkersAnnotation = TLineMarkersOptions;
775
+
774
776
  export declare interface ILineRange {
775
777
  [key: string]: ILineChartRange;
776
778
  }
@@ -1002,14 +1004,36 @@ export declare interface IUnitOptions {
1002
1004
  setSelectedUnit: () => void;
1003
1005
  }
1004
1006
 
1005
- export declare type IVerticalMarkersAnnotation = TVerticalMarkersOptions;
1006
-
1007
1007
  export declare enum Key {
1008
1008
  Shift = "Shift"
1009
1009
  }
1010
1010
 
1011
1011
  export declare const LineChart: (props: ILineChartProps) => JSX_2.Element;
1012
1012
 
1013
+ export declare enum LineMarkerDirection {
1014
+ Vertical = "vertical",
1015
+ Horizontal = "horizontal"
1016
+ }
1017
+
1018
+ export declare enum LineMarkerLabelPosition {
1019
+ Left = "left",
1020
+ Right = "right",
1021
+ Top = "top",
1022
+ Bottom = "bottom",
1023
+ OnLine = "onLine"
1024
+ }
1025
+
1026
+ export declare enum LineMarkerSide {
1027
+ Left = "left",
1028
+ Right = "right"
1029
+ }
1030
+
1031
+ export declare enum LineMarkerTextAlign {
1032
+ Left = "left",
1033
+ Right = "right",
1034
+ Center = "center"
1035
+ }
1036
+
1013
1037
  export declare enum PanZoomMode {
1014
1038
  X = "x",
1015
1039
  Y = "y",
@@ -1059,58 +1083,109 @@ export declare type TGeneratedPieChartDatasets = IGeneratedPieChartDataset[];
1059
1083
 
1060
1084
  export declare type TGeneratedScatterChartDatasets = IGeneratedScatterChartDataset[];
1061
1085
 
1062
- export declare enum TooltipLabel {
1063
- Y = "yLabel",
1064
- X = "xLabel"
1065
- }
1066
-
1067
- export declare type TPrimitive = string | number | boolean | null;
1086
+ export declare type TLineMarkerDirection = LineMarkerDirection;
1068
1087
 
1069
- export declare type TStickSide = 'left' | 'right';
1070
-
1071
- export declare type TVerticalMarkerItem = {
1088
+ export declare type TLineMarkerItem = {
1072
1089
  id?: string;
1090
+ direction?: TLineMarkerDirection;
1073
1091
  xScaleID?: string;
1074
1092
  yScaleID?: string;
1075
1093
  xValue?: number;
1094
+ yValue?: number;
1076
1095
  yStartValue?: number;
1096
+ yEndValue?: number;
1097
+ xStartValue?: number;
1098
+ xEndValue?: number;
1099
+ lengthPx?: number;
1077
1100
  value?: number;
1078
1101
  label?: string | string[];
1079
1102
  font?: string;
1080
1103
  labelOffsetPx?: number;
1081
1104
  stickToEdge?: boolean;
1105
+ stickToGroup?: boolean;
1082
1106
  stickSide?: TStickSide;
1107
+ groupOffsetPx?: number;
1083
1108
  reverse?: boolean;
1084
1109
  color?: string;
1085
1110
  opacity?: number;
1086
1111
  lineWidth?: number;
1087
1112
  lineDash?: number[];
1113
+ startTick?: TLineMarkerTick;
1114
+ endTick?: TLineMarkerTick;
1115
+ extras?: TMarkerExtraItem[];
1088
1116
  display?: boolean;
1089
1117
  };
1090
1118
 
1091
- export declare type TVerticalMarkersOptions = {
1119
+ export declare type TLineMarkerLabelPosition = LineMarkerLabelPosition;
1120
+
1121
+ export declare type TLineMarkersOptions = {
1092
1122
  enabled?: boolean;
1093
1123
  itemGapPx?: number;
1094
1124
  edgePaddingPx?: number;
1125
+ horizontalLineLengthPx?: number;
1126
+ lineDirection?: TLineMarkerDirection;
1127
+ labelCollisionPx?: number;
1128
+ labelCollisionClusterXPx?: number;
1129
+ enableLabelCollisionResolver?: boolean;
1095
1130
  stickToEdge?: boolean;
1131
+ stickToGroup?: boolean;
1096
1132
  stickSide?: TStickSide;
1097
1133
  reverse?: boolean;
1098
1134
  xValue?: number;
1135
+ yValue?: number;
1136
+ yEndValue?: number;
1137
+ xStartValue?: number;
1138
+ xEndValue?: number;
1139
+ lengthPx?: number;
1099
1140
  yStartValue?: number;
1100
1141
  color?: string;
1101
1142
  opacity?: number;
1102
1143
  lineWidth?: number;
1103
1144
  lineDash?: number[];
1104
- items?: TVerticalMarkerItem[];
1145
+ startTick?: TLineMarkerTick;
1146
+ endTick?: TLineMarkerTick;
1147
+ items?: TLineMarkerItem[];
1105
1148
  };
1106
1149
 
1107
- export declare type TVerticalMarkerTick = {
1150
+ export declare type TLineMarkerTextAlign = LineMarkerTextAlign;
1151
+
1152
+ export declare type TLineMarkerTick = {
1153
+ enabled?: boolean;
1108
1154
  label?: string | string[];
1109
1155
  color?: string;
1110
1156
  font?: string;
1111
1157
  labelOffsetPx?: number;
1158
+ labelPosition?: TLineMarkerLabelPosition;
1159
+ side?: TStickSide;
1160
+ reverse?: boolean;
1161
+ sizePx?: number;
1112
1162
  };
1113
1163
 
1164
+ export declare type TMarkerExtraItem = {
1165
+ id?: string;
1166
+ display?: boolean;
1167
+ yScaleID?: string;
1168
+ yValue?: number;
1169
+ value?: number;
1170
+ lengthPx?: number;
1171
+ side?: TStickSide;
1172
+ reverse?: boolean;
1173
+ color?: string;
1174
+ opacity?: number;
1175
+ lineWidth?: number;
1176
+ lineDash?: number[];
1177
+ tick?: TLineMarkerTick;
1178
+ };
1179
+
1180
+ export declare enum TooltipLabel {
1181
+ Y = "yLabel",
1182
+ X = "xLabel"
1183
+ }
1184
+
1185
+ export declare type TPrimitive = string | number | boolean | null;
1186
+
1187
+ export declare type TStickSide = LineMarkerSide;
1188
+
1114
1189
  export declare type UnusedParameter = unknown;
1115
1190
 
1116
1191
  export { }
@@ -1118,68 +1193,24 @@ export { }
1118
1193
 
1119
1194
  declare module 'chart.js' {
1120
1195
  interface PluginOptionsByType<TType extends ChartType> {
1121
- annotationDraggerPlugin?: AnnotationDraggerPluginOptions;
1196
+ lineMarkersPlugin?: {
1197
+ enabled?: boolean;
1198
+ } | TLineMarkersOptions;
1122
1199
  }
1123
1200
  }
1124
1201
 
1125
1202
 
1126
- /**
1127
- * Vertical Markers Plugin
1128
- *
1129
- * This plugin draws vertical helper lines on top of the chart.
1130
- * with a small triangle end cap ("boot") and a text label.
1131
- *
1132
- * Where to configure:
1133
- * - Enable switch: options.plugins.verticalMarkersPlugin.enabled
1134
- * - Data and style: options.annotations.verticalMarkersAnnotation
1135
- *
1136
- * Global props (verticalMarkersAnnotation):
1137
- * - enabled: Turn markers on or off.
1138
- * - items: List of marker objects to draw.
1139
- * - itemGapPx: Space in pixels between marker lines.
1140
- * - edgePaddingPx: Distance from chart left/right edge when sticky mode is used.
1141
- * - stickToEdge: If true, use chart edge as base X position.
1142
- * - stickSide: Which edge to use in sticky mode: 'left' or 'right'.
1143
- * - xValue: Optional base X value from the X scale (used instead of edge).
1144
- * - yStartValue: Optional start Y value for all lines (if not set, start from top;
1145
- * if reverse is true, start from bottom).
1146
- * - reverse: If true, lines go from bottom to top. If false, top to bottom.
1147
- * - color: Default line and triangle color.
1148
- * - opacity: Default line opacity.
1149
- * - lineWidth: Default line width.
1150
- * - lineDash: Default dash pattern for lines.
1151
- *
1152
- * Item props (each object in items):
1153
- * - value: Required Y value where the marker line ends and triangle is placed.
1154
- * - label: Optional text near the triangle.
1155
- * - display: Show or hide this item.
1156
- * - stickToEdge: Per-item override of global stickToEdge.
1157
- * - stickSide: Per-item override of global stickSide.
1158
- * - xValue: Per-item override of global xValue.
1159
- * - yStartValue: Per-item override of global yStartValue.
1160
- * - reverse: Per-item override of global reverse.
1161
- * - color: Per-item override of global color.
1162
- * - opacity: Per-item override of global opacity.
1163
- * - lineWidth: Per-item override of global lineWidth.
1164
- * - lineDash: Per-item override of global lineDash.
1165
- * - font: Optional label font.
1166
- * - labelOffsetPx: Optional label offset in pixels.
1167
- * - xScaleID: Optional custom X scale id for xValue conversion.
1168
- * - yScaleID: Optional custom Y scale id for value conversion.
1169
- */
1170
1203
  declare module 'chart.js' {
1171
1204
  interface PluginOptionsByType<TType extends ChartType> {
1172
- verticalMarkersPlugin?: {
1173
- enabled?: boolean;
1174
- } | TVerticalMarkersOptions;
1205
+ calloutConnectorPlugin?: {
1206
+ enableCalloutAnnotation?: boolean;
1207
+ };
1175
1208
  }
1176
1209
  }
1177
1210
 
1178
1211
 
1179
1212
  declare module 'chart.js' {
1180
1213
  interface PluginOptionsByType<TType extends ChartType> {
1181
- calloutConnectorPlugin?: {
1182
- enableCalloutAnnotation?: boolean;
1183
- };
1214
+ annotationDraggerPlugin?: AnnotationDraggerPluginOptions;
1184
1215
  }
1185
1216
  }
package/dist/index.js CHANGED
@@ -18839,7 +18839,7 @@ var BORDER_WIDTH = {
18839
18839
  var BORDER_COLOR = "rgba(0,0,0,0.1)";
18840
18840
  var ANNOTATION_DASH = [10, 2];
18841
18841
  var DEFAULT_FONT_FAMILY = "\"Roobert\", \"Noto Sans\", sans-serif";
18842
- var DEFAULT_COLOR = "hsl(60, 10.34482759%, 12.5%)";
18842
+ var DEFAULT_COLOR$1 = "hsl(60, 10.34482759%, 12.5%)";
18843
18843
  var LOGARITHMIC_STEPS = [
18844
18844
  1,
18845
18845
  10,
@@ -19024,7 +19024,7 @@ var getChartFileName = (axes) => {
19024
19024
  var setDefaultTheme = () => {
19025
19025
  defaults$1.font.size = 12;
19026
19026
  defaults$1.font.family = DEFAULT_FONT_FAMILY;
19027
- defaults$1.color = DEFAULT_COLOR;
19027
+ defaults$1.color = DEFAULT_COLOR$1;
19028
19028
  defaults$1.borderColor = BORDER_COLOR;
19029
19029
  };
19030
19030
  var isEmptyString = (value) => value === "";
@@ -20190,20 +20190,33 @@ var defaultAnnotations$2 = (annotations) => ({
20190
20190
  controlAnnotation: annotationConfig?.controlAnnotation ?? false,
20191
20191
  enableDragAnnotation: annotationConfig?.enableDragAnnotation ?? false,
20192
20192
  enableCalloutAnnotation: annotationConfig?.enableCalloutAnnotation ?? false,
20193
- verticalMarkersAnnotation: {
20194
- enabled: annotationConfig?.verticalMarkersAnnotation?.enabled ?? false,
20195
- itemGapPx: annotationConfig?.verticalMarkersAnnotation?.itemGapPx ?? 4,
20196
- edgePaddingPx: annotationConfig?.verticalMarkersAnnotation?.edgePaddingPx ?? 8,
20197
- stickToEdge: annotationConfig?.verticalMarkersAnnotation?.stickToEdge ?? true,
20198
- stickSide: annotationConfig?.verticalMarkersAnnotation?.stickSide ?? "right",
20199
- reverse: annotationConfig?.verticalMarkersAnnotation?.reverse ?? false,
20200
- xValue: annotationConfig?.verticalMarkersAnnotation?.xValue,
20201
- yStartValue: annotationConfig?.verticalMarkersAnnotation?.yStartValue,
20202
- color: annotationConfig?.verticalMarkersAnnotation?.color ?? "rgba(20,20,20,0.9)",
20203
- opacity: annotationConfig?.verticalMarkersAnnotation?.opacity ?? 1,
20204
- lineWidth: annotationConfig?.verticalMarkersAnnotation?.lineWidth ?? 1,
20205
- lineDash: annotationConfig?.verticalMarkersAnnotation?.lineDash ?? [],
20206
- items: annotationConfig?.verticalMarkersAnnotation?.items ?? []
20193
+ lineMarkersAnnotation: {
20194
+ enabled: annotationConfig?.lineMarkersAnnotation?.enabled ?? false,
20195
+ itemGapPx: annotationConfig?.lineMarkersAnnotation?.itemGapPx ?? 4,
20196
+ edgePaddingPx: annotationConfig?.lineMarkersAnnotation?.edgePaddingPx ?? 8,
20197
+ horizontalLineLengthPx: annotationConfig?.lineMarkersAnnotation?.horizontalLineLengthPx,
20198
+ lineDirection: annotationConfig?.lineMarkersAnnotation?.lineDirection,
20199
+ labelCollisionPx: annotationConfig?.lineMarkersAnnotation?.labelCollisionPx ?? 4,
20200
+ labelCollisionClusterXPx: annotationConfig?.lineMarkersAnnotation?.labelCollisionClusterXPx ?? 36,
20201
+ enableLabelCollisionResolver: annotationConfig?.lineMarkersAnnotation?.enableLabelCollisionResolver ?? true,
20202
+ stickToEdge: annotationConfig?.lineMarkersAnnotation?.stickToEdge ?? true,
20203
+ stickToGroup: annotationConfig?.lineMarkersAnnotation?.stickToGroup ?? false,
20204
+ stickSide: annotationConfig?.lineMarkersAnnotation?.stickSide ?? "right",
20205
+ reverse: annotationConfig?.lineMarkersAnnotation?.reverse ?? false,
20206
+ xValue: annotationConfig?.lineMarkersAnnotation?.xValue,
20207
+ yValue: annotationConfig?.lineMarkersAnnotation?.yValue,
20208
+ yStartValue: annotationConfig?.lineMarkersAnnotation?.yStartValue,
20209
+ yEndValue: annotationConfig?.lineMarkersAnnotation?.yEndValue,
20210
+ xStartValue: annotationConfig?.lineMarkersAnnotation?.xStartValue,
20211
+ xEndValue: annotationConfig?.lineMarkersAnnotation?.xEndValue,
20212
+ lengthPx: annotationConfig?.lineMarkersAnnotation?.lengthPx,
20213
+ color: annotationConfig?.lineMarkersAnnotation?.color ?? "rgba(20,20,20,0.9)",
20214
+ opacity: annotationConfig?.lineMarkersAnnotation?.opacity ?? 1,
20215
+ lineWidth: annotationConfig?.lineMarkersAnnotation?.lineWidth ?? 1,
20216
+ lineDash: annotationConfig?.lineMarkersAnnotation?.lineDash ?? [],
20217
+ startTick: annotationConfig?.lineMarkersAnnotation?.startTick,
20218
+ endTick: annotationConfig?.lineMarkersAnnotation?.endTick,
20219
+ items: annotationConfig?.lineMarkersAnnotation?.items ?? []
20207
20220
  },
20208
20221
  annotationsData: defaultAnnotationsData$2(Array.isArray(annotations) ? annotations : annotations?.annotationsData)
20209
20222
  };
@@ -22025,6 +22038,11 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22025
22038
  state
22026
22039
  ]);
22027
22040
  const dragData = useMemo(() => enableDragPoints && getDraggableData(options), [enableDragPoints, options]);
22041
+ const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
22042
+ generatedDatasets,
22043
+ hoveredPoint,
22044
+ onHover
22045
+ ]);
22028
22046
  const panOptions = useMemo(() => ({
22029
22047
  enabled: panEnabled,
22030
22048
  mode: PanZoomMode.XY,
@@ -22055,13 +22073,13 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22055
22073
  updateAxesRangesFromChart,
22056
22074
  zoomEnabled
22057
22075
  ]);
22058
- const verticalMarkersAnnotation = options?.annotations?.verticalMarkersAnnotation ?? {};
22059
- const verticalMarkersPluginEnabled = Boolean(options?.plugins?.verticalMarkersPlugin?.enabled ?? verticalMarkersAnnotation?.enabled);
22060
- const plugins = useMemo(() => ({
22076
+ const lineMarkersAnnotation = options?.annotations?.lineMarkersAnnotation ?? {};
22077
+ const lineMarkersPluginEnabled = Boolean(options?.plugins?.lineMarkersPlugin?.enabled ?? lineMarkersAnnotation?.enabled);
22078
+ const plugins = {
22061
22079
  datalabels,
22062
22080
  annotationDraggerPlugin: { enabled: state?.enableDragAnnotation },
22063
22081
  calloutConnectorPlugin: { enableCalloutAnnotation: options?.annotations.enableCalloutAnnotation },
22064
- verticalMarkersPlugin: { enabled: verticalMarkersPluginEnabled },
22082
+ lineMarkersPlugin: { enabled: lineMarkersPluginEnabled },
22065
22083
  annotation: toAnnotationObject(annotation),
22066
22084
  zoom: {
22067
22085
  pan: panOptions,
@@ -22086,32 +22104,7 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22086
22104
  lineHeight
22087
22105
  },
22088
22106
  ...dragData
22089
- }), [
22090
- annotation,
22091
- customLegendPlugin,
22092
- datalabels,
22093
- dragData,
22094
- fontSize,
22095
- legend,
22096
- lineHeight,
22097
- maxWidth,
22098
- options?.annotations.enableCalloutAnnotation,
22099
- panOptions,
22100
- position,
22101
- showLabel,
22102
- state?.enableDragAnnotation,
22103
- text,
22104
- tooltip,
22105
- verticalMarkersPluginEnabled,
22106
- xOffset,
22107
- yOffset,
22108
- zoomOptions
22109
- ]);
22110
- const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
22111
- generatedDatasets,
22112
- hoveredPoint,
22113
- onHover
22114
- ]);
22107
+ };
22115
22108
  return useMemo(() => ({
22116
22109
  persistenceId,
22117
22110
  layout: { padding: layoutPadding },
@@ -22139,7 +22132,6 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
22139
22132
  layoutPadding,
22140
22133
  lineEnabled,
22141
22134
  onAnimationComplete,
22142
- onChartHover,
22143
22135
  options?.annotations,
22144
22136
  options?.chartStyling?.maintainAspectRatio,
22145
22137
  options?.chartStyling?.performanceMode,
@@ -22836,7 +22828,33 @@ var calloutConnectorPlugin = {
22836
22828
  }
22837
22829
  };
22838
22830
  //#endregion
22839
- //#region src/components/line-chart/plugins/vertical-markers-plugin/utils.ts
22831
+ //#region src/components/line-chart/plugins/line-markers-plugin/types.ts
22832
+ var LineMarkerSide = /* @__PURE__ */ function(LineMarkerSide) {
22833
+ LineMarkerSide["Left"] = "left";
22834
+ LineMarkerSide["Right"] = "right";
22835
+ return LineMarkerSide;
22836
+ }({});
22837
+ var LineMarkerDirection = /* @__PURE__ */ function(LineMarkerDirection) {
22838
+ LineMarkerDirection["Vertical"] = "vertical";
22839
+ LineMarkerDirection["Horizontal"] = "horizontal";
22840
+ return LineMarkerDirection;
22841
+ }({});
22842
+ var LineMarkerLabelPosition = /* @__PURE__ */ function(LineMarkerLabelPosition) {
22843
+ LineMarkerLabelPosition["Left"] = "left";
22844
+ LineMarkerLabelPosition["Right"] = "right";
22845
+ LineMarkerLabelPosition["Top"] = "top";
22846
+ LineMarkerLabelPosition["Bottom"] = "bottom";
22847
+ LineMarkerLabelPosition["OnLine"] = "onLine";
22848
+ return LineMarkerLabelPosition;
22849
+ }({});
22850
+ var LineMarkerTextAlign = /* @__PURE__ */ function(LineMarkerTextAlign) {
22851
+ LineMarkerTextAlign["Left"] = "left";
22852
+ LineMarkerTextAlign["Right"] = "right";
22853
+ LineMarkerTextAlign["Center"] = "center";
22854
+ return LineMarkerTextAlign;
22855
+ }({});
22856
+ //#endregion
22857
+ //#region src/components/line-chart/plugins/line-markers-plugin/utils.ts
22840
22858
  var isFiniteNumber = (v) => {
22841
22859
  return isNumber(v) && Number.isFinite(v);
22842
22860
  };
@@ -22862,68 +22880,336 @@ var clamp = (v, min, max) => {
22862
22880
  var crispLinePx = (v) => {
22863
22881
  return Math.round(v) + .5;
22864
22882
  };
22883
+ var toLines = (label) => {
22884
+ if (!label) return [];
22885
+ return isArray(label) ? label : [label];
22886
+ };
22887
+ var lineHeightPx = (font) => {
22888
+ const match = /(\d+)\s*px/i.exec(font);
22889
+ const size = match ? Number(match[1]) : 12;
22890
+ return Math.max(10, Math.round(size * 1.2));
22891
+ };
22892
+ var clusterByXDistance = (labels, clusterXPx) => {
22893
+ if (!labels.length) return [];
22894
+ const sorted = [...labels].sort((a, b) => a.x - b.x);
22895
+ const clusters = [];
22896
+ let current = [sorted[0]];
22897
+ for (let i = 1; i < sorted.length; i += 1) {
22898
+ const prev = sorted[i - 1];
22899
+ const currentItem = sorted[i];
22900
+ if (Math.abs(currentItem.x - prev.x) <= clusterXPx) {
22901
+ current.push(currentItem);
22902
+ continue;
22903
+ }
22904
+ clusters.push(current);
22905
+ current = [currentItem];
22906
+ }
22907
+ clusters.push(current);
22908
+ return clusters;
22909
+ };
22910
+ var resolveCluster = ({ labels, chartTop, chartBottom, gapPx }) => {
22911
+ const sorted = [...labels].sort((a, b) => a.y - b.y);
22912
+ if (!sorted.length) return {};
22913
+ const placed = sorted.map((item) => ({
22914
+ ...item,
22915
+ placedY: item.y
22916
+ }));
22917
+ for (let i = 1; i < placed.length; i += 1) {
22918
+ const prev = placed[i - 1];
22919
+ const curr = placed[i];
22920
+ const prevBottom = prev.placedY + prev.height / 2;
22921
+ const currTop = curr.placedY - curr.height / 2;
22922
+ const overlap = prevBottom + gapPx - currTop;
22923
+ if (overlap > 0) curr.placedY += overlap;
22924
+ }
22925
+ const last = placed[placed.length - 1];
22926
+ const overflowBottom = last.placedY + last.height / 2 - chartBottom;
22927
+ if (overflowBottom > 0) placed.forEach((item) => {
22928
+ item.placedY -= overflowBottom;
22929
+ });
22930
+ const first = placed[0];
22931
+ const overflowTop = chartTop - (first.placedY - first.height / 2);
22932
+ if (overflowTop > 0) placed.forEach((item) => {
22933
+ item.placedY += overflowTop;
22934
+ });
22935
+ return placed.reduce((acc, item) => {
22936
+ acc[item.id] = clamp(item.placedY, chartTop, chartBottom);
22937
+ return acc;
22938
+ }, {});
22939
+ };
22940
+ var resolveLabelCollisions = ({ labels, chartTop, chartBottom, clusterXPx, gapPx }) => {
22941
+ const result = {};
22942
+ [LineMarkerSide.Left, LineMarkerSide.Right].forEach((side) => {
22943
+ clusterByXDistance(labels.filter((label) => label.side === side), clusterXPx).forEach((cluster) => {
22944
+ Object.assign(result, resolveCluster({
22945
+ labels: cluster,
22946
+ chartTop,
22947
+ chartBottom,
22948
+ gapPx
22949
+ }));
22950
+ });
22951
+ });
22952
+ return result;
22953
+ };
22865
22954
  //#endregion
22866
- //#region src/components/line-chart/plugins/vertical-markers-plugin/draw.ts
22867
- var drawVerticalLine = ({ chart, xPx, y1, y2, color, opacity, lineWidth, lineDash }) => {
22955
+ //#region src/components/line-chart/plugins/line-markers-plugin/draw.ts
22956
+ var drawLineSegment = ({ chart, x1, y1, x2, y2, color, opacity, lineWidth, lineDash }) => {
22868
22957
  const { ctx } = chart ?? {};
22869
- const x = crispLinePx(xPx);
22958
+ const startX = crispLinePx(x1);
22959
+ const endX = crispLinePx(x2);
22960
+ const startY = crispLinePx(y1);
22961
+ const endY = crispLinePx(y2);
22870
22962
  ctx?.save?.();
22871
22963
  ctx.globalAlpha = opacity;
22872
22964
  ctx?.beginPath?.();
22873
22965
  ctx.lineWidth = lineWidth;
22874
22966
  ctx.strokeStyle = color;
22875
22967
  ctx?.setLineDash?.(lineDash);
22876
- ctx.lineCap = "round";
22877
- ctx?.moveTo?.(x, y1);
22878
- ctx?.lineTo?.(x, y2);
22968
+ ctx.lineCap = "butt";
22969
+ ctx?.moveTo?.(startX, startY);
22970
+ ctx?.lineTo?.(endX, endY);
22879
22971
  ctx?.stroke?.();
22880
22972
  ctx?.restore?.();
22881
- return x;
22973
+ return {
22974
+ x1: startX,
22975
+ y1: startY,
22976
+ x2: endX,
22977
+ y2: endY
22978
+ };
22979
+ };
22980
+ var isWithin = (value, min, max) => {
22981
+ return value >= min && value <= max;
22982
+ };
22983
+ var isTickElementInsideByDirection = (args) => {
22984
+ const { direction, chartArea, box } = args;
22985
+ if (direction === LineMarkerDirection.Vertical) return box.top >= chartArea.top && box.bottom <= chartArea.bottom;
22986
+ return box.left >= chartArea.left && box.right <= chartArea.right;
22987
+ };
22988
+ var resolveDefaultLabelPosition = (args) => {
22989
+ const { direction, side } = args;
22990
+ if (direction === LineMarkerDirection.Vertical) return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22991
+ return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22992
+ };
22993
+ var getLabelAnchor = (args) => {
22994
+ const { x, y, direction, labelOffset, bootSize, labelPosition } = args;
22995
+ const distance = bootSize + labelOffset;
22996
+ if (direction === LineMarkerDirection.Horizontal) switch (labelPosition) {
22997
+ case LineMarkerLabelPosition.Left: return {
22998
+ x: x - distance,
22999
+ y,
23000
+ textAlign: LineMarkerTextAlign.Right
23001
+ };
23002
+ case LineMarkerLabelPosition.Right: return {
23003
+ x: x + distance,
23004
+ y,
23005
+ textAlign: LineMarkerTextAlign.Left
23006
+ };
23007
+ case LineMarkerLabelPosition.Top: return {
23008
+ x,
23009
+ y: y - distance,
23010
+ textAlign: LineMarkerTextAlign.Center
23011
+ };
23012
+ case LineMarkerLabelPosition.Bottom: return {
23013
+ x,
23014
+ y: y + distance,
23015
+ textAlign: LineMarkerTextAlign.Center
23016
+ };
23017
+ case LineMarkerLabelPosition.OnLine: return {
23018
+ x,
23019
+ y,
23020
+ textAlign: LineMarkerTextAlign.Center
23021
+ };
23022
+ default: return {
23023
+ x,
23024
+ y,
23025
+ textAlign: LineMarkerTextAlign.Center
23026
+ };
23027
+ }
23028
+ switch (labelPosition) {
23029
+ case LineMarkerLabelPosition.Left: return {
23030
+ x: x - distance,
23031
+ y,
23032
+ textAlign: LineMarkerTextAlign.Right
23033
+ };
23034
+ case LineMarkerLabelPosition.Right: return {
23035
+ x: x + distance,
23036
+ y,
23037
+ textAlign: LineMarkerTextAlign.Left
23038
+ };
23039
+ case LineMarkerLabelPosition.Top: return {
23040
+ x,
23041
+ y: y - distance,
23042
+ textAlign: LineMarkerTextAlign.Center
23043
+ };
23044
+ case LineMarkerLabelPosition.Bottom: return {
23045
+ x,
23046
+ y: y + distance,
23047
+ textAlign: LineMarkerTextAlign.Center
23048
+ };
23049
+ case LineMarkerLabelPosition.OnLine: return {
23050
+ x,
23051
+ y,
23052
+ textAlign: LineMarkerTextAlign.Center
23053
+ };
23054
+ default: return {
23055
+ x,
23056
+ y,
23057
+ textAlign: LineMarkerTextAlign.Center
23058
+ };
23059
+ }
23060
+ };
23061
+ var getLineTextBox = (args) => {
23062
+ const { textAlign, x, y, width, lineHeight } = args;
23063
+ return {
23064
+ left: textAlign === LineMarkerTextAlign.Right ? x - width : textAlign === LineMarkerTextAlign.Center ? x - width / 2 : x,
23065
+ right: textAlign === LineMarkerTextAlign.Right ? x : textAlign === LineMarkerTextAlign.Center ? x + width / 2 : x + width,
23066
+ top: y - lineHeight / 2,
23067
+ bottom: y + lineHeight / 2
23068
+ };
23069
+ };
23070
+ var getOppositeLabelPosition = (position) => {
23071
+ switch (position) {
23072
+ case LineMarkerLabelPosition.Left: return LineMarkerLabelPosition.Right;
23073
+ case LineMarkerLabelPosition.Right: return LineMarkerLabelPosition.Left;
23074
+ case LineMarkerLabelPosition.Top: return LineMarkerLabelPosition.Bottom;
23075
+ case LineMarkerLabelPosition.Bottom: return LineMarkerLabelPosition.Top;
23076
+ default: return LineMarkerLabelPosition.OnLine;
23077
+ }
22882
23078
  };
22883
23079
  var drawTick = (args) => {
22884
- const { ctx, x, y, side, tick, defaultColor, defaultFont } = args ?? {};
23080
+ const { ctx, x, y, side, tick, defaultColor, defaultFont, chartArea } = args ?? {};
23081
+ const direction = args?.direction ?? LineMarkerDirection.Vertical;
22885
23082
  const reverse = Boolean(args?.reverse);
23083
+ const labelY = args?.labelY ?? y;
23084
+ const bootEnabled = tick?.enabled ?? true;
23085
+ const hasLabel = Array.isArray(tick?.label) ? tick.label.length > 0 : Boolean(tick?.label);
23086
+ if (!bootEnabled && !hasLabel) return;
22886
23087
  const color = tick?.color ?? defaultColor;
22887
23088
  const font = tick?.font ?? defaultFont;
22888
23089
  const labelOffset = tick?.labelOffsetPx ?? 6;
22889
- const bootSize = 6;
22890
- const dir = side === "left" ? 1 : -1;
22891
- const xTickEnd = x;
23090
+ const bootSize = tick?.sizePx ?? 6;
23091
+ const dir = side === LineMarkerSide.Left ? 1 : -1;
23092
+ const xTickEnd = crispLinePx(x);
23093
+ const yTickEnd = crispLinePx(y);
22892
23094
  ctx?.save?.();
22893
23095
  ctx.strokeStyle = color;
22894
23096
  ctx.fillStyle = color;
22895
23097
  ctx.lineWidth = 1;
22896
- const xOuter = xTickEnd + dir * bootSize;
22897
- ctx?.beginPath?.();
22898
- ctx?.moveTo?.(xTickEnd, y);
22899
- ctx?.lineTo?.(xOuter, y);
22900
- ctx?.lineTo?.(xTickEnd, reverse ? y + bootSize : y - bootSize);
22901
- ctx?.closePath?.();
22902
- ctx?.fill?.();
23098
+ const xOuter = direction === LineMarkerDirection.Vertical ? xTickEnd + dir * bootSize : xTickEnd;
23099
+ const yOuter = direction === LineMarkerDirection.Horizontal ? yTickEnd + (reverse ? bootSize : -bootSize) : yTickEnd;
23100
+ const xThird = direction === LineMarkerDirection.Vertical ? xTickEnd : xTickEnd + dir * bootSize;
23101
+ const yThird = reverse ? yTickEnd + bootSize : yTickEnd - bootSize;
23102
+ const bootBounds = {
23103
+ left: Math.min(xTickEnd, xOuter, xThird),
23104
+ right: Math.max(xTickEnd, xOuter, xThird),
23105
+ top: Math.min(yTickEnd, yOuter, yThird),
23106
+ bottom: Math.max(yTickEnd, yOuter, yThird)
23107
+ };
23108
+ const canDrawBoot = (direction === LineMarkerDirection.Vertical ? isWithin(yTickEnd, chartArea.top, chartArea.bottom) : isWithin(xTickEnd, chartArea.left, chartArea.right)) && isTickElementInsideByDirection({
23109
+ direction,
23110
+ chartArea,
23111
+ box: bootBounds
23112
+ });
23113
+ if (bootEnabled && canDrawBoot) {
23114
+ ctx?.beginPath?.();
23115
+ ctx?.moveTo?.(xTickEnd, yTickEnd);
23116
+ ctx?.lineTo?.(xOuter, yOuter);
23117
+ ctx?.lineTo?.(xThird, yThird);
23118
+ ctx?.closePath?.();
23119
+ ctx?.fill?.();
23120
+ }
22903
23121
  const label = tick.label;
22904
23122
  if (label) {
22905
23123
  ctx.font = font;
22906
23124
  ctx.textBaseline = "middle";
22907
- ctx.textAlign = side === "left" ? "left" : "right";
22908
- const tx = xOuter + dir * labelOffset;
22909
- if (Array.isArray(label)) {
22910
- const lineH = 12;
22911
- const startY = y - (label.length - 1) * lineH / 2;
22912
- label?.forEach((line, i) => ctx?.fillText?.(String(line), tx, startY + i * lineH));
22913
- } else ctx?.fillText?.(String(label), tx, y);
23125
+ const preferredPosition = tick?.labelPosition ?? resolveDefaultLabelPosition({
23126
+ direction,
23127
+ side
23128
+ });
23129
+ const fallbackPositions = [
23130
+ preferredPosition,
23131
+ getOppositeLabelPosition(preferredPosition),
23132
+ LineMarkerLabelPosition.Top,
23133
+ LineMarkerLabelPosition.Bottom,
23134
+ LineMarkerLabelPosition.OnLine
23135
+ ].filter((value, index, arr) => arr.indexOf(value) === index);
23136
+ for (const labelPosition of fallbackPositions) {
23137
+ const anchor = getLabelAnchor({
23138
+ x: xTickEnd,
23139
+ y: labelY,
23140
+ direction,
23141
+ labelOffset,
23142
+ bootSize,
23143
+ labelPosition
23144
+ });
23145
+ const labelTextAlign = anchor.textAlign;
23146
+ ctx.textAlign = labelTextAlign;
23147
+ if (Array.isArray(label)) {
23148
+ const lineH = lineHeightPx(font);
23149
+ const startY = anchor.y - (label.length - 1) * lineH / 2;
23150
+ const metrics = label.map((line, i) => {
23151
+ const text = String(line);
23152
+ const width = ctx.measureText(text).width;
23153
+ const yLine = startY + i * lineH;
23154
+ return {
23155
+ text,
23156
+ yLine,
23157
+ box: getLineTextBox({
23158
+ textAlign: labelTextAlign,
23159
+ x: anchor.x,
23160
+ y: yLine,
23161
+ width,
23162
+ lineHeight: lineH
23163
+ })
23164
+ };
23165
+ });
23166
+ if (!metrics.every((lineData) => isTickElementInsideByDirection({
23167
+ direction,
23168
+ chartArea,
23169
+ box: lineData.box
23170
+ }))) continue;
23171
+ metrics.forEach((lineData) => ctx?.fillText?.(lineData.text, anchor.x, lineData.yLine));
23172
+ break;
23173
+ } else {
23174
+ const text = String(label);
23175
+ const width = ctx.measureText(text).width;
23176
+ const lineH = lineHeightPx(font);
23177
+ if (!isTickElementInsideByDirection({
23178
+ direction,
23179
+ chartArea,
23180
+ box: getLineTextBox({
23181
+ textAlign: labelTextAlign,
23182
+ x: anchor.x,
23183
+ y: anchor.y,
23184
+ width,
23185
+ lineHeight: lineH
23186
+ })
23187
+ })) continue;
23188
+ ctx?.fillText?.(text, anchor.x, anchor.y);
23189
+ break;
23190
+ }
23191
+ }
22914
23192
  }
22915
23193
  ctx?.restore?.();
22916
23194
  };
23195
+ var getTickLabelHeight = (tick, defaultFont) => {
23196
+ const lines = toLines(tick?.label);
23197
+ if (!lines.length) return 0;
23198
+ const lineHeight = lineHeightPx(tick?.font ?? defaultFont);
23199
+ return lines.length * lineHeight;
23200
+ };
22917
23201
  //#endregion
22918
- //#region src/components/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.ts
23202
+ //#region src/components/line-chart/plugins/line-markers-plugin/line-markers-plugin.ts
23203
+ var DEFAULT_FONT = "12px sans-serif";
23204
+ var DEFAULT_COLOR = "rgba(20,20,20,0.9)";
22919
23205
  var readPluginEnable = (chart) => {
22920
- const { verticalMarkersPlugin } = chart?.options?.plugins ?? {};
22921
- return asObject(verticalMarkersPlugin)?.enabled === true;
23206
+ const { lineMarkersPlugin } = chart?.options?.plugins ?? {};
23207
+ return asObject(lineMarkersPlugin)?.enabled === true;
22922
23208
  };
22923
23209
  var readAnnotationConfig = (chart) => {
22924
23210
  const optionsWithAnnotations = chart?.options;
22925
23211
  const configWithAnnotations = chart?.config?.options;
22926
- return asObject(configWithAnnotations?.annotations?.verticalMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.verticalMarkersAnnotation);
23212
+ return asObject(configWithAnnotations?.annotations?.lineMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.lineMarkersAnnotation);
22927
23213
  };
22928
23214
  var readOwnBoolean = (obj, key) => {
22929
23215
  if (!obj || typeof obj !== "object") return void 0;
@@ -22931,39 +23217,242 @@ var readOwnBoolean = (obj, key) => {
22931
23217
  if (!descriptor || !("value" in descriptor)) return void 0;
22932
23218
  return typeof descriptor?.value === "boolean" ? descriptor?.value : void 0;
22933
23219
  };
22934
- var computeXPxBase = (args) => {
23220
+ var getMarkerDirection = (item, global) => {
23221
+ return item?.direction ?? global?.lineDirection ?? LineMarkerDirection.Vertical;
23222
+ };
23223
+ var resolveSide = (item, global) => {
23224
+ return item?.stickSide ?? global?.stickSide ?? LineMarkerSide.Right;
23225
+ };
23226
+ var getEdgeX = (chartArea, side, edgePaddingPx) => {
23227
+ return clamp(side === LineMarkerSide.Left ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
23228
+ };
23229
+ var computeVerticalXPxBase = (args) => {
22935
23230
  const { chart, chartArea, item, global } = args ?? {};
22936
23231
  const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22937
- const stickSide = item?.stickSide ?? global?.stickSide ?? "right";
23232
+ const side = resolveSide(item, global);
22938
23233
  const edgePaddingPx = global?.edgePaddingPx ?? 0;
22939
23234
  const xValue = item?.xValue ?? global?.xValue;
22940
23235
  if (isFiniteNumber(xValue)) return getPixelForValueSafe(resolveScale(chart, item?.xScaleID, "x"), xValue);
22941
- if (stickToEdge) return clamp(stickSide === "left" ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
23236
+ if (stickToEdge) return getEdgeX(chartArea, side, edgePaddingPx);
22942
23237
  };
22943
- var computeYSpan = (args) => {
22944
- const { chart, chartArea, item, global } = args ?? {};
23238
+ var computeVerticalLineGeometry = (args) => {
23239
+ const { chart, chartArea, item, global, xPx } = args ?? {};
22945
23240
  const yScale = resolveScale(chart, item?.yScaleID, "y");
22946
23241
  if (!yScale) return void 0;
22947
- const yStartValue = item?.yStartValue ?? global?.yStartValue;
22948
23242
  const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22949
- const itemValue = item?.value;
22950
- if (!isFiniteNumber(itemValue)) return void 0;
22951
- const yStartPx = isFiniteNumber(yStartValue) ? clamp(yScale.getPixelForValue(yStartValue), chartArea?.top, chartArea?.bottom) : reverse ? chartArea?.bottom : chartArea?.top;
22952
- const yEndPxRaw = clamp(yScale.getPixelForValue(itemValue), chartArea?.top, chartArea?.bottom);
23243
+ const yStartValue = item?.yStartValue ?? global?.yStartValue;
23244
+ const yEndValue = item?.yEndValue ?? item?.value ?? global?.yEndValue;
23245
+ if (!isFiniteNumber(yEndValue)) return void 0;
23246
+ const y1 = isFiniteNumber(yStartValue) ? yScale.getPixelForValue(yStartValue) : reverse ? chartArea?.bottom : chartArea?.top;
23247
+ const y1InArea = y1 >= chartArea?.top && y1 <= chartArea?.bottom;
23248
+ const y2Raw = yScale.getPixelForValue(yEndValue);
23249
+ const y2InArea = y2Raw >= chartArea?.top && y2Raw <= chartArea?.bottom;
23250
+ const y2 = clamp(y2Raw, chartArea?.top, chartArea?.bottom);
22953
23251
  return {
22954
- yStartPx,
22955
- yEndPx: reverse ? Math.min(yStartPx, yEndPxRaw) : Math.max(yStartPx, yEndPxRaw),
22956
- endTick: {
22957
- label: item.label,
22958
- color: item.color,
22959
- font: item.font,
22960
- labelOffsetPx: item.labelOffsetPx
22961
- },
22962
- reverse
23252
+ x1: xPx,
23253
+ y1: clamp(y1, chartArea?.top, chartArea?.bottom),
23254
+ x2: xPx,
23255
+ y2,
23256
+ startInArea: y1InArea,
23257
+ endInArea: y2InArea,
23258
+ axisInArea: xPx >= chartArea.left && xPx <= chartArea.right,
23259
+ side: resolveSide(item, global),
23260
+ direction: LineMarkerDirection.Vertical
23261
+ };
23262
+ };
23263
+ var resolveHorizontalSpan = (args) => {
23264
+ const { chart, chartArea, item, global, side, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
23265
+ const xScale = resolveScale(chart, item?.xScaleID, "x");
23266
+ if (!xScale) return void 0;
23267
+ const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
23268
+ const stickToGroup = item?.stickToGroup ?? global?.stickToGroup ?? false;
23269
+ const edgePaddingPx = global?.edgePaddingPx ?? 0;
23270
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
23271
+ const fixedLengthPx = item?.lengthPx ?? global?.lengthPx ?? global?.horizontalLineLengthPx;
23272
+ const edgeX = getEdgeX(chartArea, side, edgePaddingPx);
23273
+ const xStartValue = item?.xStartValue ?? global?.xStartValue;
23274
+ const xEndValue = item?.xEndValue ?? global?.xEndValue;
23275
+ const xValue = item?.xValue ?? global?.xValue;
23276
+ const xStartPxRaw = isFiniteNumber(xStartValue) ? xScale.getPixelForValue(xStartValue) : void 0;
23277
+ const xEndPxRaw = isFiniteNumber(xEndValue) ? xScale.getPixelForValue(xEndValue) : void 0;
23278
+ const xStartPx = isFiniteNumber(xStartPxRaw) ? clamp(xStartPxRaw, chartArea?.left, chartArea?.right) : void 0;
23279
+ const xEndPx = isFiniteNumber(xEndPxRaw) ? clamp(xEndPxRaw, chartArea?.left, chartArea?.right) : void 0;
23280
+ if (stickToEdge && stickToGroup && isFiniteNumber(groupAnchorXPx)) {
23281
+ const groupXRaw = groupAnchorXPx;
23282
+ if (groupXRaw < chartArea.left || groupXRaw > chartArea.right) return;
23283
+ const groupX = clamp(groupXRaw, chartArea?.left, chartArea?.right);
23284
+ const edgeX = side === LineMarkerSide.Left ? chartArea.left : chartArea.right;
23285
+ const x2Raw = isFiniteNumber(fixedLengthPx) ? edgeX + dirIntoChart * fixedLengthPx : groupX;
23286
+ return {
23287
+ x1: edgeX,
23288
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23289
+ startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
23290
+ endInArea: isFiniteNumber(fixedLengthPx) ? x2Raw >= chartArea.left && x2Raw <= chartArea.right : groupX >= chartArea.left && groupX <= chartArea.right
23291
+ };
23292
+ }
23293
+ if (!stickToEdge && stickToGroup && isFiniteNumber(fixedLengthPx)) {
23294
+ const rawAnchor = previousGroupItemXPx ?? (isFiniteNumber(groupAnchorXPx) ? groupAnchorXPx : void 0);
23295
+ if (!isFiniteNumber(rawAnchor)) return void 0;
23296
+ const anchorX = clamp(rawAnchor, chartArea?.left, chartArea?.right);
23297
+ const x2Raw = anchorX + dirIntoChart * fixedLengthPx;
23298
+ return {
23299
+ x1: anchorX,
23300
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23301
+ startInArea: rawAnchor >= chartArea.left && rawAnchor <= chartArea.right,
23302
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
23303
+ };
23304
+ }
23305
+ if (isFiniteNumber(xStartPx) && isFiniteNumber(xEndPx)) {
23306
+ const startRaw = xStartPxRaw;
23307
+ const endRaw = xEndPxRaw;
23308
+ if (!isFiniteNumber(startRaw) || !isFiniteNumber(endRaw)) return void 0;
23309
+ return {
23310
+ x1: xStartPx,
23311
+ x2: xEndPx,
23312
+ startInArea: startRaw >= chartArea.left && startRaw <= chartArea.right,
23313
+ endInArea: endRaw >= chartArea.left && endRaw <= chartArea.right
23314
+ };
23315
+ }
23316
+ if (isFiniteNumber(xStartPx) && isFiniteNumber(fixedLengthPx)) {
23317
+ const x2Raw = xStartPx + dirIntoChart * fixedLengthPx;
23318
+ return {
23319
+ x1: xStartPx,
23320
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23321
+ startInArea: xStartPx >= chartArea.left && xStartPx <= chartArea.right,
23322
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
23323
+ };
23324
+ }
23325
+ if (isFiniteNumber(xEndPx) && isFiniteNumber(fixedLengthPx)) {
23326
+ const x1Raw = xEndPx - dirIntoChart * fixedLengthPx;
23327
+ return {
23328
+ x1: clamp(x1Raw, chartArea?.left, chartArea?.right),
23329
+ x2: xEndPx,
23330
+ startInArea: x1Raw >= chartArea.left && x1Raw <= chartArea.right,
23331
+ endInArea: xEndPx >= chartArea.left && xEndPx <= chartArea.right
23332
+ };
23333
+ }
23334
+ const xValuePxRaw = isFiniteNumber(xValue) ? xScale.getPixelForValue(xValue) : void 0;
23335
+ if (isFiniteNumber(xValuePxRaw) && isFiniteNumber(fixedLengthPx)) {
23336
+ const xAnchor = clamp(xValuePxRaw, chartArea?.left, chartArea?.right);
23337
+ const x2Raw = xAnchor + dirIntoChart * fixedLengthPx;
23338
+ return {
23339
+ x1: xAnchor,
23340
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23341
+ startInArea: xValuePxRaw >= chartArea.left && xValuePxRaw <= chartArea.right,
23342
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
23343
+ };
23344
+ }
23345
+ if (stickToEdge && isFiniteNumber(fixedLengthPx)) {
23346
+ const x2Raw = edgeX + dirIntoChart * fixedLengthPx;
23347
+ return {
23348
+ x1: edgeX,
23349
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23350
+ startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
23351
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
23352
+ };
23353
+ }
23354
+ };
23355
+ var computeHorizontalLineGeometry = (args) => {
23356
+ const { chart, chartArea, item, global, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
23357
+ const yScale = resolveScale(chart, item?.yScaleID, "y");
23358
+ if (!yScale) return void 0;
23359
+ const yValue = item?.yValue ?? item?.value ?? global?.yValue;
23360
+ if (!isFiniteNumber(yValue)) return void 0;
23361
+ const side = resolveSide(item, global);
23362
+ const span = resolveHorizontalSpan({
23363
+ chart,
23364
+ chartArea,
23365
+ item,
23366
+ global,
23367
+ side,
23368
+ groupAnchorXPx,
23369
+ previousGroupItemXPx
23370
+ });
23371
+ if (!span) return void 0;
23372
+ const yPxRaw = yScale.getPixelForValue(yValue);
23373
+ const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
23374
+ const yInArea = yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom;
23375
+ return {
23376
+ x1: span.x1,
23377
+ y1: yPx,
23378
+ x2: span.x2,
23379
+ y2: yPx,
23380
+ startInArea: span.startInArea && yInArea,
23381
+ endInArea: span.endInArea && yInArea,
23382
+ axisInArea: yInArea,
23383
+ side,
23384
+ direction: LineMarkerDirection.Horizontal
23385
+ };
23386
+ };
23387
+ var resolveTick = (args) => {
23388
+ const { globalTick, itemTick, defaultEnabled } = args;
23389
+ return {
23390
+ enabled: itemTick?.enabled ?? globalTick?.enabled ?? defaultEnabled,
23391
+ label: itemTick?.label ?? globalTick?.label,
23392
+ color: itemTick?.color ?? globalTick?.color,
23393
+ font: itemTick?.font ?? globalTick?.font,
23394
+ labelOffsetPx: itemTick?.labelOffsetPx ?? globalTick?.labelOffsetPx,
23395
+ labelPosition: itemTick?.labelPosition ?? globalTick?.labelPosition,
23396
+ reverse: itemTick?.reverse ?? globalTick?.reverse,
23397
+ side: itemTick?.side ?? globalTick?.side,
23398
+ sizePx: itemTick?.sizePx ?? globalTick?.sizePx
23399
+ };
23400
+ };
23401
+ var hasTickLabel = (tick) => {
23402
+ const label = tick?.label;
23403
+ if (Array.isArray(label)) return label.length > 0;
23404
+ return Boolean(label);
23405
+ };
23406
+ var toExtraGeometry = (args) => {
23407
+ const { chart, chartArea, item, extra, xBase, defaultSide } = args;
23408
+ const yScale = resolveScale(chart, extra?.yScaleID ?? item?.yScaleID, "y");
23409
+ if (!yScale) return void 0;
23410
+ const yValue = extra?.yValue ?? extra?.value;
23411
+ if (!isFiniteNumber(yValue)) return void 0;
23412
+ const lengthPx = extra?.lengthPx;
23413
+ if (!isFiniteNumber(lengthPx) || lengthPx <= 0) return void 0;
23414
+ const side = extra?.side ?? defaultSide;
23415
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
23416
+ const yPxRaw = yScale.getPixelForValue(yValue);
23417
+ const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
23418
+ const x2Raw = xBase + dirIntoChart * lengthPx;
23419
+ return {
23420
+ x1: xBase,
23421
+ y1: yPx,
23422
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
23423
+ y2: yPx,
23424
+ startInArea: xBase >= chartArea.left && xBase <= chartArea.right,
23425
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right,
23426
+ axisInArea: yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom,
23427
+ side,
23428
+ direction: LineMarkerDirection.Horizontal
23429
+ };
23430
+ };
23431
+ var getLineStyle = (item, global) => {
23432
+ const color = item?.color ?? global?.color ?? DEFAULT_COLOR;
23433
+ const opacity = item?.opacity ?? global?.opacity ?? 1;
23434
+ const lineWidth = item?.lineWidth ?? global?.lineWidth ?? 1;
23435
+ const lineDash = asArray(item?.lineDash ?? global?.lineDash);
23436
+ if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
23437
+ return {
23438
+ color,
23439
+ opacity,
23440
+ lineWidth,
23441
+ lineDash
22963
23442
  };
22964
23443
  };
22965
- var verticalMarkersPlugin = {
22966
- id: "verticalMarkers",
23444
+ var getLineReverse = (geometry) => {
23445
+ if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.x2 < geometry.x1;
23446
+ return geometry.y2 < geometry.y1;
23447
+ };
23448
+ var hasVisibleLineSegment = (args) => {
23449
+ const { geometry } = args;
23450
+ const epsilon = .5;
23451
+ if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.axisInArea && Math.abs(geometry.x2 - geometry.x1) > epsilon;
23452
+ return geometry.axisInArea && Math.abs(geometry.y2 - geometry.y1) > epsilon;
23453
+ };
23454
+ var lineMarkersPlugin = {
23455
+ id: "lineMarkers",
22967
23456
  afterDraw: (chart) => {
22968
23457
  if (!readPluginEnable(chart)) return;
22969
23458
  const cfg = readAnnotationConfig(chart);
@@ -22974,50 +23463,195 @@ var verticalMarkersPlugin = {
22974
23463
  const items = asArray(cfg?.items);
22975
23464
  if (!items.length) return;
22976
23465
  const itemGapPx = cfg?.itemGapPx ?? 0;
22977
- const globalStickSide = cfg?.stickSide ?? "right";
22978
- let visibleIndex = 0;
22979
- items?.forEach((item) => {
23466
+ const defaultSide = cfg?.stickSide ?? LineMarkerSide.Right;
23467
+ const lineTasks = [];
23468
+ const tickTasks = [];
23469
+ let groupedVerticalIndexBySide = {};
23470
+ let groupedAnchorBySide = {};
23471
+ let previousGroupedHorizontalEndBySide = {};
23472
+ items.forEach((item, itemIndex) => {
22980
23473
  if (!item || item?.display === false) return;
22981
- const baseXPx = computeXPxBase({
22982
- chart,
22983
- chartArea,
22984
- item,
22985
- global: cfg
23474
+ const direction = getMarkerDirection(item, cfg);
23475
+ const side = resolveSide(item, cfg);
23476
+ const style = getLineStyle(item, cfg);
23477
+ if (!style) return;
23478
+ let geometry;
23479
+ if (direction === LineMarkerDirection.Vertical) {
23480
+ const baseXPx = computeVerticalXPxBase({
23481
+ chart,
23482
+ chartArea,
23483
+ item,
23484
+ global: cfg
23485
+ });
23486
+ if (!isFiniteNumber(baseXPx)) return;
23487
+ const stickToEdge = item?.stickToEdge ?? cfg?.stickToEdge ?? true;
23488
+ const hasExplicitXValue = isFiniteNumber(item?.xValue ?? cfg?.xValue);
23489
+ const isGroupedVertical = stickToEdge && !hasExplicitXValue;
23490
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
23491
+ const groupedVerticalIndex = groupedVerticalIndexBySide[side] ?? 0;
23492
+ const groupOffsetPx = item?.groupOffsetPx ?? 0;
23493
+ geometry = computeVerticalLineGeometry({
23494
+ chart,
23495
+ chartArea,
23496
+ item,
23497
+ global: cfg,
23498
+ xPx: isGroupedVertical ? baseXPx + dirIntoChart * groupedVerticalIndex * itemGapPx + dirIntoChart * groupOffsetPx : baseXPx
23499
+ });
23500
+ if (geometry && isGroupedVertical) {
23501
+ groupedVerticalIndexBySide[side] = groupedVerticalIndex + 1;
23502
+ const prevAnchor = groupedAnchorBySide[side];
23503
+ groupedAnchorBySide[side] = isFiniteNumber(prevAnchor) ? side === LineMarkerSide.Left ? Math.min(prevAnchor, geometry.x1) : Math.max(prevAnchor, geometry.x1) : geometry.x1;
23504
+ }
23505
+ } else {
23506
+ const stickToGroup = item?.stickToGroup ?? cfg?.stickToGroup ?? false;
23507
+ geometry = computeHorizontalLineGeometry({
23508
+ chart,
23509
+ chartArea,
23510
+ item,
23511
+ global: cfg,
23512
+ groupAnchorXPx: (() => {
23513
+ const anchor = groupedAnchorBySide[side];
23514
+ if (!isFiniteNumber(anchor)) return void 0;
23515
+ return clamp(anchor, chartArea?.left, chartArea?.right);
23516
+ })(),
23517
+ previousGroupItemXPx: (() => {
23518
+ const prev = previousGroupedHorizontalEndBySide[side];
23519
+ if (!isFiniteNumber(prev)) return void 0;
23520
+ return clamp(prev, chartArea?.left, chartArea?.right);
23521
+ })()
23522
+ });
23523
+ if (geometry && stickToGroup) previousGroupedHorizontalEndBySide[side] = geometry.x2;
23524
+ }
23525
+ if (!geometry) return;
23526
+ if (!hasVisibleLineSegment({ geometry })) return;
23527
+ lineTasks.push({
23528
+ geometry,
23529
+ style
23530
+ });
23531
+ const lineReverse = getLineReverse(geometry);
23532
+ const lineStart = {
23533
+ x: geometry.x1,
23534
+ y: geometry.y1
23535
+ };
23536
+ const lineEnd = {
23537
+ x: geometry.x2,
23538
+ y: geometry.y2
23539
+ };
23540
+ const startTick = resolveTick({
23541
+ globalTick: cfg?.startTick,
23542
+ itemTick: item?.startTick,
23543
+ defaultEnabled: false
22986
23544
  });
22987
- if (!isFiniteNumber(baseXPx)) return;
22988
- const side = item?.stickSide ?? globalStickSide;
22989
- const xPx = baseXPx + (side === "left" ? 1 : -1) * visibleIndex * itemGapPx;
22990
- visibleIndex += 1;
22991
- const ySpan = computeYSpan({
23545
+ const endTick = resolveTick({
23546
+ globalTick: cfg?.endTick,
23547
+ itemTick: item?.endTick,
23548
+ defaultEnabled: direction === LineMarkerDirection.Vertical
23549
+ });
23550
+ const resolvedStartSide = startTick?.side ?? side;
23551
+ const resolvedEndSide = endTick?.side ?? side;
23552
+ if ((startTick?.enabled || hasTickLabel(startTick)) && geometry.startInArea) tickTasks.push({
23553
+ id: `${item?.id ?? `item-${itemIndex}`}-start`,
23554
+ x: lineStart.x,
23555
+ y: lineStart.y,
23556
+ side: resolvedStartSide,
23557
+ direction: geometry.direction,
23558
+ reverse: startTick?.reverse ?? !lineReverse,
23559
+ tick: startTick,
23560
+ defaultColor: style.color,
23561
+ defaultFont: DEFAULT_FONT
23562
+ });
23563
+ if ((endTick?.enabled || hasTickLabel(endTick)) && geometry.endInArea) tickTasks.push({
23564
+ id: `${item?.id ?? `item-${itemIndex}`}-end`,
23565
+ x: lineEnd.x,
23566
+ y: lineEnd.y,
23567
+ side: resolvedEndSide,
23568
+ direction: geometry.direction,
23569
+ reverse: endTick?.reverse ?? lineReverse,
23570
+ tick: endTick,
23571
+ defaultColor: style.color,
23572
+ defaultFont: DEFAULT_FONT
23573
+ });
23574
+ if (direction !== LineMarkerDirection.Vertical) return;
23575
+ asArray(item?.extras).forEach((extra, extraIndex) => {
23576
+ if (!extra || extra?.display === false) return;
23577
+ const extraGeometry = toExtraGeometry({
23578
+ chart,
23579
+ chartArea,
23580
+ item,
23581
+ extra,
23582
+ xBase: geometry.x1,
23583
+ defaultSide: side
23584
+ });
23585
+ if (!extraGeometry) return;
23586
+ const extraStyle = getLineStyle(extra, cfg);
23587
+ if (!extraStyle) return;
23588
+ lineTasks.push({
23589
+ geometry: extraGeometry,
23590
+ style: extraStyle
23591
+ });
23592
+ const extraTick = resolveTick({
23593
+ itemTick: extra?.tick,
23594
+ defaultEnabled: false
23595
+ });
23596
+ if (!extraTick?.enabled && !hasTickLabel(extraTick) || !extraGeometry.endInArea) return;
23597
+ const extraReverse = extraTick?.reverse ?? readOwnBoolean(extra, "reverse") ?? false;
23598
+ tickTasks.push({
23599
+ id: `${item?.id ?? `item-${itemIndex}`}-extra-${extra?.id ?? extraIndex}`,
23600
+ x: extraGeometry.x2,
23601
+ y: extraGeometry.y2,
23602
+ side: extraTick?.side ?? extraGeometry.side ?? defaultSide,
23603
+ direction: LineMarkerDirection.Horizontal,
23604
+ reverse: extraReverse,
23605
+ tick: extraTick,
23606
+ defaultColor: extraStyle.color,
23607
+ defaultFont: DEFAULT_FONT
23608
+ });
23609
+ });
23610
+ });
23611
+ lineTasks.forEach(({ geometry, style }) => {
23612
+ drawLineSegment({
22992
23613
  chart,
22993
- chartArea,
22994
- item,
22995
- global: cfg
23614
+ x1: geometry.x1,
23615
+ y1: geometry.y1,
23616
+ x2: geometry.x2,
23617
+ y2: geometry.y2,
23618
+ color: style.color,
23619
+ opacity: style.opacity,
23620
+ lineWidth: style.lineWidth,
23621
+ lineDash: style.lineDash
22996
23622
  });
22997
- if (!ySpan) return;
22998
- const color = item?.color ?? cfg?.color;
22999
- const opacity = item?.opacity ?? cfg?.opacity;
23000
- const lineWidth = item?.lineWidth ?? cfg?.lineWidth;
23001
- const lineDash = asArray(item?.lineDash ?? cfg?.lineDash);
23002
- if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
23623
+ });
23624
+ const enableLabelCollisionResolver = cfg?.enableLabelCollisionResolver ?? true;
23625
+ const labelCollisionPx = cfg?.labelCollisionPx ?? 4;
23626
+ const labelCollisionClusterXPx = cfg?.labelCollisionClusterXPx ?? 36;
23627
+ const labelYById = enableLabelCollisionResolver ? resolveLabelCollisions({
23628
+ labels: tickTasks.filter((task) => {
23629
+ return task?.tick?.enabled !== false && isFiniteNumber(getTickLabelHeight(task.tick, task.defaultFont)) && getTickLabelHeight(task.tick, task.defaultFont) > 0;
23630
+ }).map((task) => ({
23631
+ id: task.id,
23632
+ side: task.side,
23633
+ x: task.x,
23634
+ y: task.y,
23635
+ height: getTickLabelHeight(task.tick, task.defaultFont)
23636
+ })),
23637
+ chartTop: chartArea.top,
23638
+ chartBottom: chartArea.bottom,
23639
+ clusterXPx: labelCollisionClusterXPx,
23640
+ gapPx: labelCollisionPx
23641
+ }) : {};
23642
+ tickTasks.forEach((task) => {
23003
23643
  drawTick({
23004
23644
  ctx,
23005
- x: drawVerticalLine({
23006
- chart,
23007
- xPx,
23008
- y1: ySpan?.yStartPx,
23009
- y2: ySpan?.yEndPx,
23010
- color,
23011
- opacity,
23012
- lineWidth,
23013
- lineDash
23014
- }),
23015
- y: ySpan?.yEndPx,
23016
- side,
23017
- reverse: ySpan?.reverse,
23018
- tick: ySpan?.endTick,
23019
- defaultColor: color,
23020
- defaultFont: "12px sans-serif"
23645
+ chartArea,
23646
+ direction: task.direction,
23647
+ x: task.x,
23648
+ y: task.y,
23649
+ side: task.side,
23650
+ reverse: task.reverse,
23651
+ labelY: labelYById[task.id],
23652
+ tick: task.tick,
23653
+ defaultColor: task.defaultColor,
23654
+ defaultFont: task.defaultFont
23021
23655
  });
23022
23656
  });
23023
23657
  }
@@ -23489,7 +24123,7 @@ var Legend = ({ chartRef, legendConfig }) => {
23489
24123
  };
23490
24124
  //#endregion
23491
24125
  //#region src/components/line-chart/line-chart.tsx
23492
- Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, verticalMarkersPlugin);
24126
+ Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, lineMarkersPlugin);
23493
24127
  var LineChart = (props) => {
23494
24128
  setDefaultTheme();
23495
24129
  const chartRef = useRef(null);
@@ -38621,7 +39255,7 @@ var getGroupedColorScheme = (length, startingColor) => {
38621
39255
  });
38622
39256
  };
38623
39257
  //#endregion
38624
- 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 };
38625
39259
  (function() {
38626
39260
  //#region \0vite/all-css
38627
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-beta-4",
3
+ "version": "5.11.0-beta-1",
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": {