@oliasoft-open-source/charts-library 5.9.1-beta-1 → 5.10.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 +769 -136
  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
1203
  declare module 'chart.js' {
1127
1204
  interface PluginOptionsByType<TType extends ChartType> {
1128
- calloutConnectorPlugin?: {
1129
- enableCalloutAnnotation?: boolean;
1130
- };
1205
+ annotationDraggerPlugin?: AnnotationDraggerPluginOptions;
1131
1206
  }
1132
1207
  }
1133
1208
 
1134
1209
 
1135
- /**
1136
- * Vertical Markers Plugin
1137
- *
1138
- * This plugin draws vertical helper lines on top of the chart.
1139
- * with a small triangle end cap ("boot") and a text label.
1140
- *
1141
- * Where to configure:
1142
- * - Enable switch: options.plugins.verticalMarkersPlugin.enabled
1143
- * - Data and style: options.annotations.verticalMarkersAnnotation
1144
- *
1145
- * Global props (verticalMarkersAnnotation):
1146
- * - enabled: Turn markers on or off.
1147
- * - items: List of marker objects to draw.
1148
- * - itemGapPx: Space in pixels between marker lines.
1149
- * - edgePaddingPx: Distance from chart left/right edge when sticky mode is used.
1150
- * - stickToEdge: If true, use chart edge as base X position.
1151
- * - stickSide: Which edge to use in sticky mode: 'left' or 'right'.
1152
- * - xValue: Optional base X value from the X scale (used instead of edge).
1153
- * - yStartValue: Optional start Y value for all lines (if not set, start from top;
1154
- * if reverse is true, start from bottom).
1155
- * - reverse: If true, lines go from bottom to top. If false, top to bottom.
1156
- * - color: Default line and triangle color.
1157
- * - opacity: Default line opacity.
1158
- * - lineWidth: Default line width.
1159
- * - lineDash: Default dash pattern for lines.
1160
- *
1161
- * Item props (each object in items):
1162
- * - value: Required Y value where the marker line ends and triangle is placed.
1163
- * - label: Optional text near the triangle.
1164
- * - display: Show or hide this item.
1165
- * - stickToEdge: Per-item override of global stickToEdge.
1166
- * - stickSide: Per-item override of global stickSide.
1167
- * - xValue: Per-item override of global xValue.
1168
- * - yStartValue: Per-item override of global yStartValue.
1169
- * - reverse: Per-item override of global reverse.
1170
- * - color: Per-item override of global color.
1171
- * - opacity: Per-item override of global opacity.
1172
- * - lineWidth: Per-item override of global lineWidth.
1173
- * - lineDash: Per-item override of global lineDash.
1174
- * - font: Optional label font.
1175
- * - labelOffsetPx: Optional label offset in pixels.
1176
- * - xScaleID: Optional custom X scale id for xValue conversion.
1177
- * - yScaleID: Optional custom Y scale id for value conversion.
1178
- */
1179
1210
  declare module 'chart.js' {
1180
1211
  interface PluginOptionsByType<TType extends ChartType> {
1181
- verticalMarkersPlugin?: {
1182
- enabled?: boolean;
1183
- } | TVerticalMarkersOptions;
1212
+ calloutConnectorPlugin?: {
1213
+ enableCalloutAnnotation?: boolean;
1214
+ };
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
  };
@@ -21506,6 +21519,11 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21506
21519
  state
21507
21520
  ]);
21508
21521
  const dragData = useMemo(() => enableDragPoints && getDraggableData(options), [enableDragPoints, options]);
21522
+ const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
21523
+ generatedDatasets,
21524
+ hoveredPoint,
21525
+ onHover
21526
+ ]);
21509
21527
  const panOptions = useMemo(() => ({
21510
21528
  enabled: panEnabled,
21511
21529
  mode: PanZoomMode.XY,
@@ -21536,13 +21554,13 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21536
21554
  updateAxesRangesFromChart,
21537
21555
  zoomEnabled
21538
21556
  ]);
21539
- const verticalMarkersAnnotation = options?.annotations?.verticalMarkersAnnotation ?? {};
21540
- const verticalMarkersPluginEnabled = Boolean(options?.plugins?.verticalMarkersPlugin?.enabled ?? verticalMarkersAnnotation?.enabled);
21541
- const plugins = useMemo(() => ({
21557
+ const lineMarkersAnnotation = options?.annotations?.lineMarkersAnnotation ?? {};
21558
+ const lineMarkersPluginEnabled = Boolean(options?.plugins?.lineMarkersPlugin?.enabled ?? lineMarkersAnnotation?.enabled);
21559
+ const plugins = {
21542
21560
  datalabels,
21543
21561
  annotationDraggerPlugin: { enabled: state?.enableDragAnnotation },
21544
21562
  calloutConnectorPlugin: { enableCalloutAnnotation: options?.annotations.enableCalloutAnnotation },
21545
- verticalMarkersPlugin: { enabled: verticalMarkersPluginEnabled },
21563
+ lineMarkersPlugin: { enabled: lineMarkersPluginEnabled },
21546
21564
  annotation: toAnnotationObject(annotation),
21547
21565
  zoom: {
21548
21566
  pan: panOptions,
@@ -21567,32 +21585,7 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21567
21585
  lineHeight
21568
21586
  },
21569
21587
  ...dragData
21570
- }), [
21571
- annotation,
21572
- customLegendPlugin,
21573
- datalabels,
21574
- dragData,
21575
- fontSize,
21576
- legend,
21577
- lineHeight,
21578
- maxWidth,
21579
- options?.annotations.enableCalloutAnnotation,
21580
- panOptions,
21581
- position,
21582
- showLabel,
21583
- state?.enableDragAnnotation,
21584
- text,
21585
- tooltip,
21586
- verticalMarkersPluginEnabled,
21587
- xOffset,
21588
- yOffset,
21589
- zoomOptions
21590
- ]);
21591
- const onChartHover = useMemo(() => onHover(hoveredPoint, setHoveredPoint, generatedDatasets), [
21592
- generatedDatasets,
21593
- hoveredPoint,
21594
- onHover
21595
- ]);
21588
+ };
21596
21589
  return useMemo(() => ({
21597
21590
  persistenceId,
21598
21591
  layout: { padding: layoutPadding },
@@ -21620,7 +21613,6 @@ var useChartOptions = ({ chartRef, state, options, dispatch, generatedDatasets,
21620
21613
  layoutPadding,
21621
21614
  lineEnabled,
21622
21615
  onAnimationComplete,
21623
- onChartHover,
21624
21616
  options?.annotations,
21625
21617
  options?.chartStyling?.maintainAspectRatio,
21626
21618
  options?.chartStyling?.performanceMode,
@@ -22230,7 +22222,33 @@ var calloutConnectorPlugin = {
22230
22222
  }
22231
22223
  };
22232
22224
  //#endregion
22233
- //#region src/components/line-chart/plugins/vertical-markers-plugin/utils.ts
22225
+ //#region src/components/line-chart/plugins/line-markers-plugin/types.ts
22226
+ var LineMarkerSide = /* @__PURE__ */ function(LineMarkerSide) {
22227
+ LineMarkerSide["Left"] = "left";
22228
+ LineMarkerSide["Right"] = "right";
22229
+ return LineMarkerSide;
22230
+ }({});
22231
+ var LineMarkerDirection = /* @__PURE__ */ function(LineMarkerDirection) {
22232
+ LineMarkerDirection["Vertical"] = "vertical";
22233
+ LineMarkerDirection["Horizontal"] = "horizontal";
22234
+ return LineMarkerDirection;
22235
+ }({});
22236
+ var LineMarkerLabelPosition = /* @__PURE__ */ function(LineMarkerLabelPosition) {
22237
+ LineMarkerLabelPosition["Left"] = "left";
22238
+ LineMarkerLabelPosition["Right"] = "right";
22239
+ LineMarkerLabelPosition["Top"] = "top";
22240
+ LineMarkerLabelPosition["Bottom"] = "bottom";
22241
+ LineMarkerLabelPosition["OnLine"] = "onLine";
22242
+ return LineMarkerLabelPosition;
22243
+ }({});
22244
+ var LineMarkerTextAlign = /* @__PURE__ */ function(LineMarkerTextAlign) {
22245
+ LineMarkerTextAlign["Left"] = "left";
22246
+ LineMarkerTextAlign["Right"] = "right";
22247
+ LineMarkerTextAlign["Center"] = "center";
22248
+ return LineMarkerTextAlign;
22249
+ }({});
22250
+ //#endregion
22251
+ //#region src/components/line-chart/plugins/line-markers-plugin/utils.ts
22234
22252
  var isFiniteNumber = (v) => {
22235
22253
  return isNumber(v) && Number.isFinite(v);
22236
22254
  };
@@ -22256,68 +22274,335 @@ var clamp = (v, min, max) => {
22256
22274
  var crispLinePx = (v) => {
22257
22275
  return Math.round(v) + .5;
22258
22276
  };
22277
+ var toLines = (label) => {
22278
+ if (!label) return [];
22279
+ return isArray(label) ? label : [label];
22280
+ };
22281
+ var lineHeightPx = (font) => {
22282
+ const match = /(\d+)\s*px/i.exec(font);
22283
+ const size = match ? Number(match[1]) : 12;
22284
+ return Math.max(10, Math.round(size * 1.2));
22285
+ };
22286
+ var clusterByXDistance = (labels, clusterXPx) => {
22287
+ if (!labels.length) return [];
22288
+ const sorted = [...labels].sort((a, b) => a.x - b.x);
22289
+ const clusters = [];
22290
+ let current = [sorted[0]];
22291
+ for (let i = 1; i < sorted.length; i += 1) {
22292
+ const prev = sorted[i - 1];
22293
+ const currentItem = sorted[i];
22294
+ if (Math.abs(currentItem.x - prev.x) <= clusterXPx) {
22295
+ current.push(currentItem);
22296
+ continue;
22297
+ }
22298
+ clusters.push(current);
22299
+ current = [currentItem];
22300
+ }
22301
+ clusters.push(current);
22302
+ return clusters;
22303
+ };
22304
+ var resolveCluster = ({ labels, chartTop, chartBottom, gapPx }) => {
22305
+ const sorted = [...labels].sort((a, b) => a.y - b.y);
22306
+ if (!sorted.length) return {};
22307
+ const placed = sorted.map((item) => ({
22308
+ ...item,
22309
+ placedY: item.y
22310
+ }));
22311
+ for (let i = 1; i < placed.length; i += 1) {
22312
+ const prev = placed[i - 1];
22313
+ const curr = placed[i];
22314
+ const prevBottom = prev.placedY + prev.height / 2;
22315
+ const currTop = curr.placedY - curr.height / 2;
22316
+ const overlap = prevBottom + gapPx - currTop;
22317
+ if (overlap > 0) curr.placedY += overlap;
22318
+ }
22319
+ const last = placed[placed.length - 1];
22320
+ const overflowBottom = last.placedY + last.height / 2 - chartBottom;
22321
+ if (overflowBottom > 0) placed.forEach((item) => {
22322
+ item.placedY -= overflowBottom;
22323
+ });
22324
+ const first = placed[0];
22325
+ const overflowTop = chartTop - (first.placedY - first.height / 2);
22326
+ if (overflowTop > 0) placed.forEach((item) => {
22327
+ item.placedY += overflowTop;
22328
+ });
22329
+ return placed.reduce((acc, item) => {
22330
+ acc[item.id] = clamp(item.placedY, chartTop, chartBottom);
22331
+ return acc;
22332
+ }, {});
22333
+ };
22334
+ var resolveLabelCollisions = ({ labels, chartTop, chartBottom, clusterXPx, gapPx }) => {
22335
+ const result = {};
22336
+ [LineMarkerSide.Left, LineMarkerSide.Right].forEach((side) => {
22337
+ clusterByXDistance(labels.filter((label) => label.side === side), clusterXPx).forEach((cluster) => {
22338
+ Object.assign(result, resolveCluster({
22339
+ labels: cluster,
22340
+ chartTop,
22341
+ chartBottom,
22342
+ gapPx
22343
+ }));
22344
+ });
22345
+ });
22346
+ return result;
22347
+ };
22259
22348
  //#endregion
22260
- //#region src/components/line-chart/plugins/vertical-markers-plugin/draw.ts
22261
- var drawVerticalLine = ({ chart, xPx, y1, y2, color, opacity, lineWidth, lineDash }) => {
22349
+ //#region src/components/line-chart/plugins/line-markers-plugin/draw.ts
22350
+ var drawLineSegment = ({ chart, x1, y1, x2, y2, color, opacity, lineWidth, lineDash }) => {
22262
22351
  const { ctx } = chart ?? {};
22263
- const x = crispLinePx(xPx);
22352
+ const startX = crispLinePx(x1);
22353
+ const endX = crispLinePx(x2);
22354
+ const startY = crispLinePx(y1);
22355
+ const endY = crispLinePx(y2);
22264
22356
  ctx?.save?.();
22265
22357
  ctx.globalAlpha = opacity;
22266
22358
  ctx?.beginPath?.();
22267
22359
  ctx.lineWidth = lineWidth;
22268
22360
  ctx.strokeStyle = color;
22269
22361
  ctx?.setLineDash?.(lineDash);
22270
- ctx.lineCap = "round";
22271
- ctx?.moveTo?.(x, y1);
22272
- ctx?.lineTo?.(x, y2);
22362
+ ctx.lineCap = "butt";
22363
+ ctx?.moveTo?.(startX, startY);
22364
+ ctx?.lineTo?.(endX, endY);
22273
22365
  ctx?.stroke?.();
22274
22366
  ctx?.restore?.();
22275
- return x;
22367
+ return {
22368
+ x1: startX,
22369
+ y1: startY,
22370
+ x2: endX,
22371
+ y2: endY
22372
+ };
22373
+ };
22374
+ var isWithin = (value, min, max) => {
22375
+ return value >= min && value <= max;
22376
+ };
22377
+ var isTickElementInsideByDirection = (args) => {
22378
+ const { direction, chartArea, box } = args;
22379
+ if (direction === LineMarkerDirection.Vertical) return box.top >= chartArea.top && box.bottom <= chartArea.bottom;
22380
+ return box.left >= chartArea.left && box.right <= chartArea.right;
22381
+ };
22382
+ var resolveDefaultLabelPosition = (args) => {
22383
+ const { direction, side } = args;
22384
+ if (direction === LineMarkerDirection.Vertical) return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22385
+ return side === LineMarkerSide.Left ? LineMarkerLabelPosition.Right : LineMarkerLabelPosition.Left;
22386
+ };
22387
+ var getLabelAnchor = (args) => {
22388
+ const { x, y, direction, labelOffset, bootSize, labelPosition } = args;
22389
+ const distance = bootSize + labelOffset;
22390
+ if (direction === LineMarkerDirection.Horizontal) switch (labelPosition) {
22391
+ case LineMarkerLabelPosition.Left: return {
22392
+ x: x - distance,
22393
+ y,
22394
+ textAlign: LineMarkerTextAlign.Right
22395
+ };
22396
+ case LineMarkerLabelPosition.Right: return {
22397
+ x: x + distance,
22398
+ y,
22399
+ textAlign: LineMarkerTextAlign.Left
22400
+ };
22401
+ case LineMarkerLabelPosition.Top: return {
22402
+ x,
22403
+ y: y - distance,
22404
+ textAlign: LineMarkerTextAlign.Center
22405
+ };
22406
+ case LineMarkerLabelPosition.Bottom: return {
22407
+ x,
22408
+ y: y + distance,
22409
+ textAlign: LineMarkerTextAlign.Center
22410
+ };
22411
+ case LineMarkerLabelPosition.OnLine: return {
22412
+ x,
22413
+ y,
22414
+ textAlign: LineMarkerTextAlign.Center
22415
+ };
22416
+ default: return {
22417
+ x,
22418
+ y,
22419
+ textAlign: LineMarkerTextAlign.Center
22420
+ };
22421
+ }
22422
+ switch (labelPosition) {
22423
+ case LineMarkerLabelPosition.Left: return {
22424
+ x: x - distance,
22425
+ y,
22426
+ textAlign: LineMarkerTextAlign.Right
22427
+ };
22428
+ case LineMarkerLabelPosition.Right: return {
22429
+ x: x + distance,
22430
+ y,
22431
+ textAlign: LineMarkerTextAlign.Left
22432
+ };
22433
+ case LineMarkerLabelPosition.Top: return {
22434
+ x,
22435
+ y: y - distance,
22436
+ textAlign: LineMarkerTextAlign.Center
22437
+ };
22438
+ case LineMarkerLabelPosition.Bottom: return {
22439
+ x,
22440
+ y: y + distance,
22441
+ textAlign: LineMarkerTextAlign.Center
22442
+ };
22443
+ case LineMarkerLabelPosition.OnLine: return {
22444
+ x,
22445
+ y,
22446
+ textAlign: LineMarkerTextAlign.Center
22447
+ };
22448
+ default: return {
22449
+ x,
22450
+ y,
22451
+ textAlign: LineMarkerTextAlign.Center
22452
+ };
22453
+ }
22454
+ };
22455
+ var getLineTextBox = (args) => {
22456
+ const { textAlign, x, y, width, lineHeight } = args;
22457
+ return {
22458
+ left: textAlign === LineMarkerTextAlign.Right ? x - width : textAlign === LineMarkerTextAlign.Center ? x - width / 2 : x,
22459
+ right: textAlign === LineMarkerTextAlign.Right ? x : textAlign === LineMarkerTextAlign.Center ? x + width / 2 : x + width,
22460
+ top: y - lineHeight / 2,
22461
+ bottom: y + lineHeight / 2
22462
+ };
22463
+ };
22464
+ var getOppositeLabelPosition = (position) => {
22465
+ switch (position) {
22466
+ case LineMarkerLabelPosition.Left: return LineMarkerLabelPosition.Right;
22467
+ case LineMarkerLabelPosition.Right: return LineMarkerLabelPosition.Left;
22468
+ case LineMarkerLabelPosition.Top: return LineMarkerLabelPosition.Bottom;
22469
+ case LineMarkerLabelPosition.Bottom: return LineMarkerLabelPosition.Top;
22470
+ default: return LineMarkerLabelPosition.OnLine;
22471
+ }
22276
22472
  };
22277
22473
  var drawTick = (args) => {
22278
- const { ctx, x, y, side, tick, defaultColor, defaultFont } = args ?? {};
22474
+ const { ctx, x, y, side, tick, defaultColor, defaultFont, chartArea } = args ?? {};
22475
+ const direction = args?.direction ?? LineMarkerDirection.Vertical;
22279
22476
  const reverse = Boolean(args?.reverse);
22477
+ const labelY = args?.labelY ?? y;
22478
+ const bootEnabled = tick?.enabled ?? true;
22479
+ const hasLabel = Array.isArray(tick?.label) ? tick.label.length > 0 : Boolean(tick?.label);
22480
+ if (!bootEnabled && !hasLabel) return;
22280
22481
  const color = tick?.color ?? defaultColor;
22281
22482
  const font = tick?.font ?? defaultFont;
22282
22483
  const labelOffset = tick?.labelOffsetPx ?? 6;
22283
- const bootSize = 6;
22284
- const dir = side === "left" ? 1 : -1;
22484
+ const bootSize = tick?.sizePx ?? 6;
22485
+ const dir = side === LineMarkerSide.Left ? 1 : -1;
22285
22486
  const xTickEnd = x;
22286
22487
  ctx?.save?.();
22287
22488
  ctx.strokeStyle = color;
22288
22489
  ctx.fillStyle = color;
22289
22490
  ctx.lineWidth = 1;
22290
- const xOuter = xTickEnd + dir * bootSize;
22291
- ctx?.beginPath?.();
22292
- ctx?.moveTo?.(xTickEnd, y);
22293
- ctx?.lineTo?.(xOuter, y);
22294
- ctx?.lineTo?.(xTickEnd, reverse ? y + bootSize : y - bootSize);
22295
- ctx?.closePath?.();
22296
- ctx?.fill?.();
22491
+ const xOuter = direction === LineMarkerDirection.Vertical ? xTickEnd + dir * bootSize : xTickEnd;
22492
+ const yOuter = direction === LineMarkerDirection.Horizontal ? y + (reverse ? bootSize : -bootSize) : y;
22493
+ const xThird = direction === LineMarkerDirection.Vertical ? xTickEnd : xTickEnd + dir * bootSize;
22494
+ const yThird = reverse ? y + bootSize : y - bootSize;
22495
+ const bootBounds = {
22496
+ left: Math.min(xTickEnd, xOuter, xThird),
22497
+ right: Math.max(xTickEnd, xOuter, xThird),
22498
+ top: Math.min(y, yOuter, yThird),
22499
+ bottom: Math.max(y, yOuter, yThird)
22500
+ };
22501
+ const canDrawBoot = (direction === LineMarkerDirection.Vertical ? isWithin(y, chartArea.top, chartArea.bottom) : isWithin(xTickEnd, chartArea.left, chartArea.right)) && isTickElementInsideByDirection({
22502
+ direction,
22503
+ chartArea,
22504
+ box: bootBounds
22505
+ });
22506
+ if (bootEnabled && canDrawBoot) {
22507
+ ctx?.beginPath?.();
22508
+ ctx?.moveTo?.(xTickEnd, y);
22509
+ ctx?.lineTo?.(xOuter, yOuter);
22510
+ ctx?.lineTo?.(xThird, yThird);
22511
+ ctx?.closePath?.();
22512
+ ctx?.fill?.();
22513
+ }
22297
22514
  const label = tick.label;
22298
22515
  if (label) {
22299
22516
  ctx.font = font;
22300
22517
  ctx.textBaseline = "middle";
22301
- ctx.textAlign = side === "left" ? "left" : "right";
22302
- const tx = xOuter + dir * labelOffset;
22303
- if (Array.isArray(label)) {
22304
- const lineH = 12;
22305
- const startY = y - (label.length - 1) * lineH / 2;
22306
- label?.forEach((line, i) => ctx?.fillText?.(String(line), tx, startY + i * lineH));
22307
- } else ctx?.fillText?.(String(label), tx, y);
22518
+ const preferredPosition = tick?.labelPosition ?? resolveDefaultLabelPosition({
22519
+ direction,
22520
+ side
22521
+ });
22522
+ const fallbackPositions = [
22523
+ preferredPosition,
22524
+ getOppositeLabelPosition(preferredPosition),
22525
+ LineMarkerLabelPosition.Top,
22526
+ LineMarkerLabelPosition.Bottom,
22527
+ LineMarkerLabelPosition.OnLine
22528
+ ].filter((value, index, arr) => arr.indexOf(value) === index);
22529
+ for (const labelPosition of fallbackPositions) {
22530
+ const anchor = getLabelAnchor({
22531
+ x: xTickEnd,
22532
+ y: labelY,
22533
+ direction,
22534
+ labelOffset,
22535
+ bootSize,
22536
+ labelPosition
22537
+ });
22538
+ const labelTextAlign = anchor.textAlign;
22539
+ ctx.textAlign = labelTextAlign;
22540
+ if (Array.isArray(label)) {
22541
+ const lineH = lineHeightPx(font);
22542
+ const startY = anchor.y - (label.length - 1) * lineH / 2;
22543
+ const metrics = label.map((line, i) => {
22544
+ const text = String(line);
22545
+ const width = ctx.measureText(text).width;
22546
+ const yLine = startY + i * lineH;
22547
+ return {
22548
+ text,
22549
+ yLine,
22550
+ box: getLineTextBox({
22551
+ textAlign: labelTextAlign,
22552
+ x: anchor.x,
22553
+ y: yLine,
22554
+ width,
22555
+ lineHeight: lineH
22556
+ })
22557
+ };
22558
+ });
22559
+ if (!metrics.every((lineData) => isTickElementInsideByDirection({
22560
+ direction,
22561
+ chartArea,
22562
+ box: lineData.box
22563
+ }))) continue;
22564
+ metrics.forEach((lineData) => ctx?.fillText?.(lineData.text, anchor.x, lineData.yLine));
22565
+ break;
22566
+ } else {
22567
+ const text = String(label);
22568
+ const width = ctx.measureText(text).width;
22569
+ const lineH = lineHeightPx(font);
22570
+ if (!isTickElementInsideByDirection({
22571
+ direction,
22572
+ chartArea,
22573
+ box: getLineTextBox({
22574
+ textAlign: labelTextAlign,
22575
+ x: anchor.x,
22576
+ y: anchor.y,
22577
+ width,
22578
+ lineHeight: lineH
22579
+ })
22580
+ })) continue;
22581
+ ctx?.fillText?.(text, anchor.x, anchor.y);
22582
+ break;
22583
+ }
22584
+ }
22308
22585
  }
22309
22586
  ctx?.restore?.();
22310
22587
  };
22588
+ var getTickLabelHeight = (tick, defaultFont) => {
22589
+ const lines = toLines(tick?.label);
22590
+ if (!lines.length) return 0;
22591
+ const lineHeight = lineHeightPx(tick?.font ?? defaultFont);
22592
+ return lines.length * lineHeight;
22593
+ };
22311
22594
  //#endregion
22312
- //#region src/components/line-chart/plugins/vertical-markers-plugin/vertical-markers-plugin.ts
22595
+ //#region src/components/line-chart/plugins/line-markers-plugin/line-markers-plugin.ts
22596
+ var DEFAULT_FONT = "12px sans-serif";
22597
+ var DEFAULT_COLOR = "rgba(20,20,20,0.9)";
22313
22598
  var readPluginEnable = (chart) => {
22314
- const { verticalMarkersPlugin } = chart?.options?.plugins ?? {};
22315
- return asObject(verticalMarkersPlugin)?.enabled === true;
22599
+ const { lineMarkersPlugin } = chart?.options?.plugins ?? {};
22600
+ return asObject(lineMarkersPlugin)?.enabled === true;
22316
22601
  };
22317
22602
  var readAnnotationConfig = (chart) => {
22318
22603
  const optionsWithAnnotations = chart?.options;
22319
22604
  const configWithAnnotations = chart?.config?.options;
22320
- return asObject(configWithAnnotations?.annotations?.verticalMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.verticalMarkersAnnotation);
22605
+ return asObject(configWithAnnotations?.annotations?.lineMarkersAnnotation) ?? asObject(optionsWithAnnotations?.annotations?.lineMarkersAnnotation);
22321
22606
  };
22322
22607
  var readOwnBoolean = (obj, key) => {
22323
22608
  if (!obj || typeof obj !== "object") return void 0;
@@ -22325,39 +22610,242 @@ var readOwnBoolean = (obj, key) => {
22325
22610
  if (!descriptor || !("value" in descriptor)) return void 0;
22326
22611
  return typeof descriptor?.value === "boolean" ? descriptor?.value : void 0;
22327
22612
  };
22328
- var computeXPxBase = (args) => {
22613
+ var getMarkerDirection = (item, global) => {
22614
+ return item?.direction ?? global?.lineDirection ?? LineMarkerDirection.Vertical;
22615
+ };
22616
+ var resolveSide = (item, global) => {
22617
+ return item?.stickSide ?? global?.stickSide ?? LineMarkerSide.Right;
22618
+ };
22619
+ var getEdgeX = (chartArea, side, edgePaddingPx) => {
22620
+ return clamp(side === LineMarkerSide.Left ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22621
+ };
22622
+ var computeVerticalXPxBase = (args) => {
22329
22623
  const { chart, chartArea, item, global } = args ?? {};
22330
22624
  const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22331
- const stickSide = item?.stickSide ?? global?.stickSide ?? "right";
22625
+ const side = resolveSide(item, global);
22332
22626
  const edgePaddingPx = global?.edgePaddingPx ?? 0;
22333
22627
  const xValue = item?.xValue ?? global?.xValue;
22334
22628
  if (isFiniteNumber(xValue)) return getPixelForValueSafe(resolveScale(chart, item?.xScaleID, "x"), xValue);
22335
- if (stickToEdge) return clamp(stickSide === "left" ? chartArea?.left + edgePaddingPx : chartArea?.right - edgePaddingPx, chartArea?.left, chartArea?.right);
22629
+ if (stickToEdge) return getEdgeX(chartArea, side, edgePaddingPx);
22336
22630
  };
22337
- var computeYSpan = (args) => {
22338
- const { chart, chartArea, item, global } = args ?? {};
22631
+ var computeVerticalLineGeometry = (args) => {
22632
+ const { chart, chartArea, item, global, xPx } = args ?? {};
22339
22633
  const yScale = resolveScale(chart, item?.yScaleID, "y");
22340
22634
  if (!yScale) return void 0;
22341
- const yStartValue = item?.yStartValue ?? global?.yStartValue;
22342
22635
  const reverse = readOwnBoolean(item, "reverse") ?? readOwnBoolean(global, "reverse") ?? false;
22343
- const itemValue = item?.value;
22344
- if (!isFiniteNumber(itemValue)) return void 0;
22345
- const yStartPx = isFiniteNumber(yStartValue) ? clamp(yScale.getPixelForValue(yStartValue), chartArea?.top, chartArea?.bottom) : reverse ? chartArea?.bottom : chartArea?.top;
22346
- const yEndPxRaw = clamp(yScale.getPixelForValue(itemValue), chartArea?.top, chartArea?.bottom);
22636
+ const yStartValue = item?.yStartValue ?? global?.yStartValue;
22637
+ const yEndValue = item?.yEndValue ?? item?.value ?? global?.yEndValue;
22638
+ if (!isFiniteNumber(yEndValue)) return void 0;
22639
+ const y1 = isFiniteNumber(yStartValue) ? yScale.getPixelForValue(yStartValue) : reverse ? chartArea?.bottom : chartArea?.top;
22640
+ const y1InArea = y1 >= chartArea?.top && y1 <= chartArea?.bottom;
22641
+ const y2Raw = yScale.getPixelForValue(yEndValue);
22642
+ const y2InArea = y2Raw >= chartArea?.top && y2Raw <= chartArea?.bottom;
22643
+ const y2 = clamp(y2Raw, chartArea?.top, chartArea?.bottom);
22347
22644
  return {
22348
- yStartPx,
22349
- yEndPx: reverse ? Math.min(yStartPx, yEndPxRaw) : Math.max(yStartPx, yEndPxRaw),
22350
- endTick: {
22351
- label: item.label,
22352
- color: item.color,
22353
- font: item.font,
22354
- labelOffsetPx: item.labelOffsetPx
22355
- },
22356
- reverse
22645
+ x1: xPx,
22646
+ y1: clamp(y1, chartArea?.top, chartArea?.bottom),
22647
+ x2: xPx,
22648
+ y2,
22649
+ startInArea: y1InArea,
22650
+ endInArea: y2InArea,
22651
+ axisInArea: xPx >= chartArea.left && xPx <= chartArea.right,
22652
+ side: resolveSide(item, global),
22653
+ direction: LineMarkerDirection.Vertical
22654
+ };
22655
+ };
22656
+ var resolveHorizontalSpan = (args) => {
22657
+ const { chart, chartArea, item, global, side, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22658
+ const xScale = resolveScale(chart, item?.xScaleID, "x");
22659
+ if (!xScale) return void 0;
22660
+ const stickToEdge = item?.stickToEdge ?? global?.stickToEdge ?? true;
22661
+ const stickToGroup = item?.stickToGroup ?? global?.stickToGroup ?? false;
22662
+ const edgePaddingPx = global?.edgePaddingPx ?? 0;
22663
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22664
+ const fixedLengthPx = item?.lengthPx ?? global?.lengthPx ?? global?.horizontalLineLengthPx;
22665
+ const edgeX = getEdgeX(chartArea, side, edgePaddingPx);
22666
+ const xStartValue = item?.xStartValue ?? global?.xStartValue;
22667
+ const xEndValue = item?.xEndValue ?? global?.xEndValue;
22668
+ const xValue = item?.xValue ?? global?.xValue;
22669
+ const xStartPxRaw = isFiniteNumber(xStartValue) ? xScale.getPixelForValue(xStartValue) : void 0;
22670
+ const xEndPxRaw = isFiniteNumber(xEndValue) ? xScale.getPixelForValue(xEndValue) : void 0;
22671
+ const xStartPx = isFiniteNumber(xStartPxRaw) ? clamp(xStartPxRaw, chartArea?.left, chartArea?.right) : void 0;
22672
+ const xEndPx = isFiniteNumber(xEndPxRaw) ? clamp(xEndPxRaw, chartArea?.left, chartArea?.right) : void 0;
22673
+ if (stickToEdge && stickToGroup && isFiniteNumber(groupAnchorXPx)) {
22674
+ const groupXRaw = groupAnchorXPx;
22675
+ if (groupXRaw < chartArea.left || groupXRaw > chartArea.right) return;
22676
+ const groupX = clamp(groupXRaw, chartArea?.left, chartArea?.right);
22677
+ const edgeX = side === LineMarkerSide.Left ? chartArea.left : chartArea.right;
22678
+ const x2Raw = isFiniteNumber(fixedLengthPx) ? edgeX + dirIntoChart * fixedLengthPx : groupX;
22679
+ return {
22680
+ x1: edgeX,
22681
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22682
+ startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22683
+ endInArea: isFiniteNumber(fixedLengthPx) ? x2Raw >= chartArea.left && x2Raw <= chartArea.right : groupX >= chartArea.left && groupX <= chartArea.right
22684
+ };
22685
+ }
22686
+ if (!stickToEdge && stickToGroup && isFiniteNumber(fixedLengthPx)) {
22687
+ const rawAnchor = previousGroupItemXPx ?? (isFiniteNumber(groupAnchorXPx) ? groupAnchorXPx : void 0);
22688
+ if (!isFiniteNumber(rawAnchor)) return void 0;
22689
+ const anchorX = clamp(rawAnchor, chartArea?.left, chartArea?.right);
22690
+ const x2Raw = anchorX + dirIntoChart * fixedLengthPx;
22691
+ return {
22692
+ x1: anchorX,
22693
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22694
+ startInArea: rawAnchor >= chartArea.left && rawAnchor <= chartArea.right,
22695
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22696
+ };
22697
+ }
22698
+ if (isFiniteNumber(xStartPx) && isFiniteNumber(xEndPx)) {
22699
+ const startRaw = xStartPxRaw;
22700
+ const endRaw = xEndPxRaw;
22701
+ if (!isFiniteNumber(startRaw) || !isFiniteNumber(endRaw)) return void 0;
22702
+ return {
22703
+ x1: xStartPx,
22704
+ x2: xEndPx,
22705
+ startInArea: startRaw >= chartArea.left && startRaw <= chartArea.right,
22706
+ endInArea: endRaw >= chartArea.left && endRaw <= chartArea.right
22707
+ };
22708
+ }
22709
+ if (isFiniteNumber(xStartPx) && isFiniteNumber(fixedLengthPx)) {
22710
+ const x2Raw = xStartPx + dirIntoChart * fixedLengthPx;
22711
+ return {
22712
+ x1: xStartPx,
22713
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22714
+ startInArea: xStartPx >= chartArea.left && xStartPx <= chartArea.right,
22715
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22716
+ };
22717
+ }
22718
+ if (isFiniteNumber(xEndPx) && isFiniteNumber(fixedLengthPx)) {
22719
+ const x1Raw = xEndPx - dirIntoChart * fixedLengthPx;
22720
+ return {
22721
+ x1: clamp(x1Raw, chartArea?.left, chartArea?.right),
22722
+ x2: xEndPx,
22723
+ startInArea: x1Raw >= chartArea.left && x1Raw <= chartArea.right,
22724
+ endInArea: xEndPx >= chartArea.left && xEndPx <= chartArea.right
22725
+ };
22726
+ }
22727
+ const xValuePxRaw = isFiniteNumber(xValue) ? xScale.getPixelForValue(xValue) : void 0;
22728
+ if (isFiniteNumber(xValuePxRaw) && isFiniteNumber(fixedLengthPx)) {
22729
+ const xAnchor = clamp(xValuePxRaw, chartArea?.left, chartArea?.right);
22730
+ const x2Raw = xAnchor + dirIntoChart * fixedLengthPx;
22731
+ return {
22732
+ x1: xAnchor,
22733
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22734
+ startInArea: xValuePxRaw >= chartArea.left && xValuePxRaw <= chartArea.right,
22735
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22736
+ };
22737
+ }
22738
+ if (stickToEdge && isFiniteNumber(fixedLengthPx)) {
22739
+ const x2Raw = edgeX + dirIntoChart * fixedLengthPx;
22740
+ return {
22741
+ x1: edgeX,
22742
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22743
+ startInArea: edgeX >= chartArea.left && edgeX <= chartArea.right,
22744
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right
22745
+ };
22746
+ }
22747
+ };
22748
+ var computeHorizontalLineGeometry = (args) => {
22749
+ const { chart, chartArea, item, global, groupAnchorXPx, previousGroupItemXPx } = args ?? {};
22750
+ const yScale = resolveScale(chart, item?.yScaleID, "y");
22751
+ if (!yScale) return void 0;
22752
+ const yValue = item?.yValue ?? item?.value ?? global?.yValue;
22753
+ if (!isFiniteNumber(yValue)) return void 0;
22754
+ const side = resolveSide(item, global);
22755
+ const span = resolveHorizontalSpan({
22756
+ chart,
22757
+ chartArea,
22758
+ item,
22759
+ global,
22760
+ side,
22761
+ groupAnchorXPx,
22762
+ previousGroupItemXPx
22763
+ });
22764
+ if (!span) return void 0;
22765
+ const yPxRaw = yScale.getPixelForValue(yValue);
22766
+ const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22767
+ const yInArea = yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom;
22768
+ return {
22769
+ x1: span.x1,
22770
+ y1: yPx,
22771
+ x2: span.x2,
22772
+ y2: yPx,
22773
+ startInArea: span.startInArea && yInArea,
22774
+ endInArea: span.endInArea && yInArea,
22775
+ axisInArea: yInArea,
22776
+ side,
22777
+ direction: LineMarkerDirection.Horizontal
22778
+ };
22779
+ };
22780
+ var resolveTick = (args) => {
22781
+ const { globalTick, itemTick, defaultEnabled } = args;
22782
+ return {
22783
+ enabled: itemTick?.enabled ?? globalTick?.enabled ?? defaultEnabled,
22784
+ label: itemTick?.label ?? globalTick?.label,
22785
+ color: itemTick?.color ?? globalTick?.color,
22786
+ font: itemTick?.font ?? globalTick?.font,
22787
+ labelOffsetPx: itemTick?.labelOffsetPx ?? globalTick?.labelOffsetPx,
22788
+ labelPosition: itemTick?.labelPosition ?? globalTick?.labelPosition,
22789
+ reverse: itemTick?.reverse ?? globalTick?.reverse,
22790
+ side: itemTick?.side ?? globalTick?.side,
22791
+ sizePx: itemTick?.sizePx ?? globalTick?.sizePx
22792
+ };
22793
+ };
22794
+ var hasTickLabel = (tick) => {
22795
+ const label = tick?.label;
22796
+ if (Array.isArray(label)) return label.length > 0;
22797
+ return Boolean(label);
22798
+ };
22799
+ var toExtraGeometry = (args) => {
22800
+ const { chart, chartArea, item, extra, xBase, defaultSide } = args;
22801
+ const yScale = resolveScale(chart, extra?.yScaleID ?? item?.yScaleID, "y");
22802
+ if (!yScale) return void 0;
22803
+ const yValue = extra?.yValue ?? extra?.value;
22804
+ if (!isFiniteNumber(yValue)) return void 0;
22805
+ const lengthPx = extra?.lengthPx;
22806
+ if (!isFiniteNumber(lengthPx) || lengthPx <= 0) return void 0;
22807
+ const side = extra?.side ?? defaultSide;
22808
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22809
+ const yPxRaw = yScale.getPixelForValue(yValue);
22810
+ const yPx = clamp(yPxRaw, chartArea?.top, chartArea?.bottom);
22811
+ const x2Raw = xBase + dirIntoChart * lengthPx;
22812
+ return {
22813
+ x1: xBase,
22814
+ y1: yPx,
22815
+ x2: clamp(x2Raw, chartArea?.left, chartArea?.right),
22816
+ y2: yPx,
22817
+ startInArea: xBase >= chartArea.left && xBase <= chartArea.right,
22818
+ endInArea: x2Raw >= chartArea.left && x2Raw <= chartArea.right,
22819
+ axisInArea: yPxRaw >= chartArea.top && yPxRaw <= chartArea.bottom,
22820
+ side,
22821
+ direction: LineMarkerDirection.Horizontal
22822
+ };
22823
+ };
22824
+ var getLineStyle = (item, global) => {
22825
+ const color = item?.color ?? global?.color ?? DEFAULT_COLOR;
22826
+ const opacity = item?.opacity ?? global?.opacity ?? 1;
22827
+ const lineWidth = item?.lineWidth ?? global?.lineWidth ?? 1;
22828
+ const lineDash = asArray(item?.lineDash ?? global?.lineDash);
22829
+ if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
22830
+ return {
22831
+ color,
22832
+ opacity,
22833
+ lineWidth,
22834
+ lineDash
22357
22835
  };
22358
22836
  };
22359
- var verticalMarkersPlugin = {
22360
- id: "verticalMarkers",
22837
+ var getLineReverse = (geometry) => {
22838
+ if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.x2 < geometry.x1;
22839
+ return geometry.y2 < geometry.y1;
22840
+ };
22841
+ var hasVisibleLineSegment = (args) => {
22842
+ const { geometry } = args;
22843
+ const epsilon = .5;
22844
+ if (geometry.direction === LineMarkerDirection.Horizontal) return geometry.axisInArea && Math.abs(geometry.x2 - geometry.x1) > epsilon;
22845
+ return geometry.axisInArea && Math.abs(geometry.y2 - geometry.y1) > epsilon;
22846
+ };
22847
+ var lineMarkersPlugin = {
22848
+ id: "lineMarkers",
22361
22849
  afterDraw: (chart) => {
22362
22850
  if (!readPluginEnable(chart)) return;
22363
22851
  const cfg = readAnnotationConfig(chart);
@@ -22368,50 +22856,195 @@ var verticalMarkersPlugin = {
22368
22856
  const items = asArray(cfg?.items);
22369
22857
  if (!items.length) return;
22370
22858
  const itemGapPx = cfg?.itemGapPx ?? 0;
22371
- const globalStickSide = cfg?.stickSide ?? "right";
22372
- let visibleIndex = 0;
22373
- items?.forEach((item) => {
22859
+ const defaultSide = cfg?.stickSide ?? LineMarkerSide.Right;
22860
+ const lineTasks = [];
22861
+ const tickTasks = [];
22862
+ let groupedVerticalIndexBySide = {};
22863
+ let groupedAnchorBySide = {};
22864
+ let previousGroupedHorizontalEndBySide = {};
22865
+ items.forEach((item, itemIndex) => {
22374
22866
  if (!item || item?.display === false) return;
22375
- const baseXPx = computeXPxBase({
22376
- chart,
22377
- chartArea,
22378
- item,
22379
- global: cfg
22867
+ const direction = getMarkerDirection(item, cfg);
22868
+ const side = resolveSide(item, cfg);
22869
+ const style = getLineStyle(item, cfg);
22870
+ if (!style) return;
22871
+ let geometry;
22872
+ if (direction === LineMarkerDirection.Vertical) {
22873
+ const baseXPx = computeVerticalXPxBase({
22874
+ chart,
22875
+ chartArea,
22876
+ item,
22877
+ global: cfg
22878
+ });
22879
+ if (!isFiniteNumber(baseXPx)) return;
22880
+ const stickToEdge = item?.stickToEdge ?? cfg?.stickToEdge ?? true;
22881
+ const hasExplicitXValue = isFiniteNumber(item?.xValue ?? cfg?.xValue);
22882
+ const isGroupedVertical = stickToEdge && !hasExplicitXValue;
22883
+ const dirIntoChart = side === LineMarkerSide.Left ? 1 : -1;
22884
+ const groupedVerticalIndex = groupedVerticalIndexBySide[side] ?? 0;
22885
+ const groupOffsetPx = item?.groupOffsetPx ?? 0;
22886
+ geometry = computeVerticalLineGeometry({
22887
+ chart,
22888
+ chartArea,
22889
+ item,
22890
+ global: cfg,
22891
+ xPx: isGroupedVertical ? baseXPx + dirIntoChart * groupedVerticalIndex * itemGapPx + dirIntoChart * groupOffsetPx : baseXPx
22892
+ });
22893
+ if (geometry && isGroupedVertical) {
22894
+ groupedVerticalIndexBySide[side] = groupedVerticalIndex + 1;
22895
+ const prevAnchor = groupedAnchorBySide[side];
22896
+ groupedAnchorBySide[side] = isFiniteNumber(prevAnchor) ? side === LineMarkerSide.Left ? Math.min(prevAnchor, geometry.x1) : Math.max(prevAnchor, geometry.x1) : geometry.x1;
22897
+ }
22898
+ } else {
22899
+ const stickToGroup = item?.stickToGroup ?? cfg?.stickToGroup ?? false;
22900
+ geometry = computeHorizontalLineGeometry({
22901
+ chart,
22902
+ chartArea,
22903
+ item,
22904
+ global: cfg,
22905
+ groupAnchorXPx: (() => {
22906
+ const anchor = groupedAnchorBySide[side];
22907
+ if (!isFiniteNumber(anchor)) return void 0;
22908
+ return clamp(anchor, chartArea?.left, chartArea?.right);
22909
+ })(),
22910
+ previousGroupItemXPx: (() => {
22911
+ const prev = previousGroupedHorizontalEndBySide[side];
22912
+ if (!isFiniteNumber(prev)) return void 0;
22913
+ return clamp(prev, chartArea?.left, chartArea?.right);
22914
+ })()
22915
+ });
22916
+ if (geometry && stickToGroup) previousGroupedHorizontalEndBySide[side] = geometry.x2;
22917
+ }
22918
+ if (!geometry) return;
22919
+ if (!hasVisibleLineSegment({ geometry })) return;
22920
+ lineTasks.push({
22921
+ geometry,
22922
+ style
22923
+ });
22924
+ const lineReverse = getLineReverse(geometry);
22925
+ const lineStart = {
22926
+ x: geometry.x1,
22927
+ y: geometry.y1
22928
+ };
22929
+ const lineEnd = {
22930
+ x: geometry.x2,
22931
+ y: geometry.y2
22932
+ };
22933
+ const startTick = resolveTick({
22934
+ globalTick: cfg?.startTick,
22935
+ itemTick: item?.startTick,
22936
+ defaultEnabled: false
22380
22937
  });
22381
- if (!isFiniteNumber(baseXPx)) return;
22382
- const side = item?.stickSide ?? globalStickSide;
22383
- const xPx = baseXPx + (side === "left" ? 1 : -1) * visibleIndex * itemGapPx;
22384
- visibleIndex += 1;
22385
- const ySpan = computeYSpan({
22938
+ const endTick = resolveTick({
22939
+ globalTick: cfg?.endTick,
22940
+ itemTick: item?.endTick,
22941
+ defaultEnabled: direction === LineMarkerDirection.Vertical
22942
+ });
22943
+ const resolvedStartSide = startTick?.side ?? side;
22944
+ const resolvedEndSide = endTick?.side ?? side;
22945
+ if ((startTick?.enabled || hasTickLabel(startTick)) && geometry.startInArea) tickTasks.push({
22946
+ id: `${item?.id ?? `item-${itemIndex}`}-start`,
22947
+ x: lineStart.x,
22948
+ y: lineStart.y,
22949
+ side: resolvedStartSide,
22950
+ direction: geometry.direction,
22951
+ reverse: startTick?.reverse ?? !lineReverse,
22952
+ tick: startTick,
22953
+ defaultColor: style.color,
22954
+ defaultFont: DEFAULT_FONT
22955
+ });
22956
+ if ((endTick?.enabled || hasTickLabel(endTick)) && geometry.endInArea) tickTasks.push({
22957
+ id: `${item?.id ?? `item-${itemIndex}`}-end`,
22958
+ x: lineEnd.x,
22959
+ y: lineEnd.y,
22960
+ side: resolvedEndSide,
22961
+ direction: geometry.direction,
22962
+ reverse: endTick?.reverse ?? lineReverse,
22963
+ tick: endTick,
22964
+ defaultColor: style.color,
22965
+ defaultFont: DEFAULT_FONT
22966
+ });
22967
+ if (direction !== LineMarkerDirection.Vertical) return;
22968
+ asArray(item?.extras).forEach((extra, extraIndex) => {
22969
+ if (!extra || extra?.display === false) return;
22970
+ const extraGeometry = toExtraGeometry({
22971
+ chart,
22972
+ chartArea,
22973
+ item,
22974
+ extra,
22975
+ xBase: geometry.x1,
22976
+ defaultSide: side
22977
+ });
22978
+ if (!extraGeometry) return;
22979
+ const extraStyle = getLineStyle(extra, cfg);
22980
+ if (!extraStyle) return;
22981
+ lineTasks.push({
22982
+ geometry: extraGeometry,
22983
+ style: extraStyle
22984
+ });
22985
+ const extraTick = resolveTick({
22986
+ itemTick: extra?.tick,
22987
+ defaultEnabled: false
22988
+ });
22989
+ if (!extraTick?.enabled && !hasTickLabel(extraTick) || !extraGeometry.endInArea) return;
22990
+ const extraReverse = extraTick?.reverse ?? readOwnBoolean(extra, "reverse") ?? false;
22991
+ tickTasks.push({
22992
+ id: `${item?.id ?? `item-${itemIndex}`}-extra-${extra?.id ?? extraIndex}`,
22993
+ x: extraGeometry.x2,
22994
+ y: extraGeometry.y2,
22995
+ side: extraTick?.side ?? extraGeometry.side ?? defaultSide,
22996
+ direction: LineMarkerDirection.Horizontal,
22997
+ reverse: extraReverse,
22998
+ tick: extraTick,
22999
+ defaultColor: extraStyle.color,
23000
+ defaultFont: DEFAULT_FONT
23001
+ });
23002
+ });
23003
+ });
23004
+ lineTasks.forEach(({ geometry, style }) => {
23005
+ drawLineSegment({
22386
23006
  chart,
22387
- chartArea,
22388
- item,
22389
- global: cfg
23007
+ x1: geometry.x1,
23008
+ y1: geometry.y1,
23009
+ x2: geometry.x2,
23010
+ y2: geometry.y2,
23011
+ color: style.color,
23012
+ opacity: style.opacity,
23013
+ lineWidth: style.lineWidth,
23014
+ lineDash: style.lineDash
22390
23015
  });
22391
- if (!ySpan) return;
22392
- const color = item?.color ?? cfg?.color;
22393
- const opacity = item?.opacity ?? cfg?.opacity;
22394
- const lineWidth = item?.lineWidth ?? cfg?.lineWidth;
22395
- const lineDash = asArray(item?.lineDash ?? cfg?.lineDash);
22396
- if (!color || !isFiniteNumber(opacity) || !isFiniteNumber(lineWidth)) return;
23016
+ });
23017
+ const enableLabelCollisionResolver = cfg?.enableLabelCollisionResolver ?? true;
23018
+ const labelCollisionPx = cfg?.labelCollisionPx ?? 4;
23019
+ const labelCollisionClusterXPx = cfg?.labelCollisionClusterXPx ?? 36;
23020
+ const labelYById = enableLabelCollisionResolver ? resolveLabelCollisions({
23021
+ labels: tickTasks.filter((task) => {
23022
+ return task?.tick?.enabled !== false && isFiniteNumber(getTickLabelHeight(task.tick, task.defaultFont)) && getTickLabelHeight(task.tick, task.defaultFont) > 0;
23023
+ }).map((task) => ({
23024
+ id: task.id,
23025
+ side: task.side,
23026
+ x: task.x,
23027
+ y: task.y,
23028
+ height: getTickLabelHeight(task.tick, task.defaultFont)
23029
+ })),
23030
+ chartTop: chartArea.top,
23031
+ chartBottom: chartArea.bottom,
23032
+ clusterXPx: labelCollisionClusterXPx,
23033
+ gapPx: labelCollisionPx
23034
+ }) : {};
23035
+ tickTasks.forEach((task) => {
22397
23036
  drawTick({
22398
23037
  ctx,
22399
- x: drawVerticalLine({
22400
- chart,
22401
- xPx,
22402
- y1: ySpan?.yStartPx,
22403
- y2: ySpan?.yEndPx,
22404
- color,
22405
- opacity,
22406
- lineWidth,
22407
- lineDash
22408
- }),
22409
- y: ySpan?.yEndPx,
22410
- side,
22411
- reverse: ySpan?.reverse,
22412
- tick: ySpan?.endTick,
22413
- defaultColor: color,
22414
- defaultFont: "12px sans-serif"
23038
+ chartArea,
23039
+ direction: task.direction,
23040
+ x: task.x,
23041
+ y: task.y,
23042
+ side: task.side,
23043
+ reverse: task.reverse,
23044
+ labelY: labelYById[task.id],
23045
+ tick: task.tick,
23046
+ defaultColor: task.defaultColor,
23047
+ defaultFont: task.defaultFont
22415
23048
  });
22416
23049
  });
22417
23050
  }
@@ -22883,7 +23516,7 @@ var Legend = ({ chartRef, legendConfig }) => {
22883
23516
  };
22884
23517
  //#endregion
22885
23518
  //#region src/components/line-chart/line-chart.tsx
22886
- Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, verticalMarkersPlugin);
23519
+ Chart$1.register(LinearScale, PointElement, LineElement, CategoryScale, LogarithmicScale, plugin_legend, plugin_tooltip, plugin_title, index, plugin$1, plugin, annotation, chartAreaTextPlugin, annotationDraggerPlugin, ellipsisAnnotationPlugin, calloutConnectorPlugin, lineMarkersPlugin);
22887
23520
  var LineChart = (props) => {
22888
23521
  setDefaultTheme();
22889
23522
  const chartRef = useRef(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oliasoft-open-source/charts-library",
3
- "version": "5.9.1-beta-1",
3
+ "version": "5.10.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": {