@oneuptime/common 7.0.2990 → 7.0.3010

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 (197) hide show
  1. package/Models/AnalyticsModels/ExceptionInstance.ts +488 -0
  2. package/Models/AnalyticsModels/Index.ts +2 -0
  3. package/Models/AnalyticsModels/Span.ts +2 -2
  4. package/Models/DatabaseModels/Index.ts +4 -0
  5. package/Models/DatabaseModels/TelemetryException.ts +978 -0
  6. package/Server/API/StatusAPI.ts +63 -51
  7. package/Server/EnvironmentConfig.ts +3 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1724610006927-MigrationName.ts +93 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1724613666632-MigrationName.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1724659071843-MigrationName.ts +17 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  12. package/Server/Infrastructure/Queue.ts +36 -1
  13. package/Server/Infrastructure/QueueWorker.ts +2 -2
  14. package/Server/Services/DatabaseService.ts +4 -5
  15. package/Server/Services/ExceptionInstanceService.ts +11 -0
  16. package/Server/Services/Index.ts +5 -0
  17. package/Server/Services/StatusPageDomainService.ts +118 -59
  18. package/Server/Services/TelemetryExceptionService.ts +10 -0
  19. package/Server/Types/Database/Permissions/Index.ts +1 -1
  20. package/Server/Types/Database/UpdateByID.ts +1 -1
  21. package/Server/Utils/Greenlock/Greenlock.ts +331 -204
  22. package/Server/Utils/Logger.ts +8 -2
  23. package/Server/Utils/Telemetry.ts +109 -25
  24. package/Types/Code/CodeType.ts +1 -0
  25. package/Types/Database/PartialEntity.ts +14 -1
  26. package/Types/Date.ts +9 -1
  27. package/Types/Icon/IconProp.ts +2 -0
  28. package/Types/Permission.ts +39 -0
  29. package/UI/Components/ActionCard/ActionCard.tsx +66 -0
  30. package/UI/Components/Alerts/Alert.tsx +69 -60
  31. package/UI/Components/Card/Card.tsx +1 -0
  32. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +4 -17
  33. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1005 -0
  34. package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +3 -0
  35. package/UI/Components/Charts/ChartLibrary/Utils/ChartColors.ts +117 -0
  36. package/UI/Components/Charts/ChartLibrary/Utils/Cx.ts +8 -0
  37. package/UI/Components/Charts/ChartLibrary/Utils/GetYAxisDomain.ts +15 -0
  38. package/UI/Components/Charts/ChartLibrary/Utils/HasOnlyOneValueForKey.ts +19 -0
  39. package/UI/Components/Charts/ChartLibrary/Utils/UseWindowOnResize.ts +17 -0
  40. package/UI/Components/Charts/Line/LineChart.tsx +58 -225
  41. package/UI/Components/Charts/Types/ChartCurve.ts +7 -0
  42. package/UI/Components/Charts/Types/DataPoint.ts +7 -0
  43. package/UI/Components/Charts/Types/SeriesPoint.ts +7 -0
  44. package/UI/Components/Charts/Types/SeriesPoints.ts +6 -0
  45. package/UI/Components/Charts/Types/XAxis/XAxis.ts +21 -0
  46. package/UI/Components/Charts/Types/XAxis/XAxisMaxMin.ts +3 -0
  47. package/UI/Components/Charts/Types/XAxis/XAxisPrecision.ts +26 -0
  48. package/UI/Components/Charts/Types/XAxis/XAxisType.ts +6 -0
  49. package/UI/Components/Charts/Types/XValue.ts +3 -0
  50. package/UI/Components/Charts/Types/YAxis/YAxis.ts +22 -0
  51. package/UI/Components/Charts/Types/YAxis/YAxisMaxMin.ts +3 -0
  52. package/UI/Components/Charts/Types/YAxis/YAxisType.ts +5 -0
  53. package/UI/Components/Charts/Types/YValue.ts +3 -0
  54. package/UI/Components/Charts/Utils/DataPoint.ts +188 -0
  55. package/UI/Components/Charts/Utils/XAxis.ts +267 -0
  56. package/UI/Components/Detail/Detail.tsx +6 -1
  57. package/UI/Components/GanttChart/Row/Row.tsx +4 -2
  58. package/UI/Components/GanttChart/Row/RowLabel.tsx +7 -3
  59. package/UI/Components/Icon/Icon.tsx +16 -0
  60. package/UI/Components/LogsViewer/LogsViewer.tsx +1 -1
  61. package/UI/Components/Types/FieldType.ts +1 -0
  62. package/UI/Config.ts +2 -0
  63. package/UI/Utils/Telemetry.ts +7 -2
  64. package/Utils/Crypto.ts +11 -0
  65. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +445 -0
  66. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -0
  67. package/build/dist/Models/AnalyticsModels/Index.js +2 -0
  68. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  69. package/build/dist/Models/AnalyticsModels/Span.js +2 -2
  70. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  71. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  72. package/build/dist/Models/DatabaseModels/TelemetryException.js +1010 -0
  73. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -0
  74. package/build/dist/Server/API/StatusAPI.js +52 -44
  75. package/build/dist/Server/API/StatusAPI.js.map +1 -1
  76. package/build/dist/Server/EnvironmentConfig.js +1 -0
  77. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  78. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724610006927-MigrationName.js +38 -0
  79. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724610006927-MigrationName.js.map +1 -0
  80. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724613666632-MigrationName.js +26 -0
  81. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724613666632-MigrationName.js.map +1 -0
  82. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724659071843-MigrationName.js +12 -0
  83. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1724659071843-MigrationName.js.map +1 -0
  84. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  85. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  86. package/build/dist/Server/Infrastructure/Queue.js +20 -1
  87. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  88. package/build/dist/Server/Infrastructure/QueueWorker.js +2 -2
  89. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  90. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  91. package/build/dist/Server/Services/ExceptionInstanceService.js +9 -0
  92. package/build/dist/Server/Services/ExceptionInstanceService.js.map +1 -0
  93. package/build/dist/Server/Services/Index.js +4 -0
  94. package/build/dist/Server/Services/Index.js.map +1 -1
  95. package/build/dist/Server/Services/StatusPageDomainService.js +101 -53
  96. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  97. package/build/dist/Server/Services/TelemetryExceptionService.js +9 -0
  98. package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -0
  99. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  100. package/build/dist/Server/Utils/Greenlock/Greenlock.js +234 -157
  101. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  102. package/build/dist/Server/Utils/Logger.js +5 -1
  103. package/build/dist/Server/Utils/Logger.js.map +1 -1
  104. package/build/dist/Server/Utils/Telemetry.js +63 -22
  105. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  106. package/build/dist/Types/Code/CodeType.js +1 -0
  107. package/build/dist/Types/Code/CodeType.js.map +1 -1
  108. package/build/dist/Types/Database/PartialEntity.js +3 -0
  109. package/build/dist/Types/Database/PartialEntity.js.map +1 -1
  110. package/build/dist/Types/Date.js +7 -1
  111. package/build/dist/Types/Date.js.map +1 -1
  112. package/build/dist/Types/Icon/IconProp.js +2 -0
  113. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  114. package/build/dist/Types/Permission.js +33 -0
  115. package/build/dist/Types/Permission.js.map +1 -1
  116. package/build/dist/UI/Components/ActionCard/ActionCard.js +27 -0
  117. package/build/dist/UI/Components/ActionCard/ActionCard.js.map +1 -0
  118. package/build/dist/UI/Components/Alerts/Alert.js +36 -45
  119. package/build/dist/UI/Components/Alerts/Alert.js.map +1 -1
  120. package/build/dist/UI/Components/Card/Card.js +1 -1
  121. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  122. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +4 -9
  123. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  124. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +388 -0
  125. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -0
  126. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +2 -0
  127. package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -0
  128. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js +88 -0
  129. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js.map +1 -0
  130. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/Cx.js +7 -0
  131. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/Cx.js.map +1 -0
  132. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/GetYAxisDomain.js +7 -0
  133. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/GetYAxisDomain.js.map +1 -0
  134. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/HasOnlyOneValueForKey.js +14 -0
  135. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/HasOnlyOneValueForKey.js.map +1 -0
  136. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseWindowOnResize.js +14 -0
  137. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseWindowOnResize.js.map +1 -0
  138. package/build/dist/UI/Components/Charts/Line/LineChart.js +30 -136
  139. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  140. package/build/dist/UI/Components/Charts/Types/ChartCurve.js +8 -0
  141. package/build/dist/UI/Components/Charts/Types/ChartCurve.js.map +1 -0
  142. package/build/dist/UI/Components/Charts/Types/DataPoint.js +2 -0
  143. package/build/dist/UI/Components/Charts/Types/DataPoint.js.map +1 -0
  144. package/build/dist/UI/Components/Charts/Types/SeriesPoint.js +2 -0
  145. package/build/dist/UI/Components/Charts/Types/SeriesPoint.js.map +1 -0
  146. package/build/dist/UI/Components/Charts/Types/SeriesPoints.js +2 -0
  147. package/build/dist/UI/Components/Charts/Types/SeriesPoints.js.map +1 -0
  148. package/build/dist/UI/Components/Charts/Types/XAxis/XAxis.js +8 -0
  149. package/build/dist/UI/Components/Charts/Types/XAxis/XAxis.js.map +1 -0
  150. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisMaxMin.js +2 -0
  151. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisMaxMin.js.map +1 -0
  152. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisPrecision.js +27 -0
  153. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisPrecision.js.map +1 -0
  154. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisType.js +7 -0
  155. package/build/dist/UI/Components/Charts/Types/XAxis/XAxisType.js.map +1 -0
  156. package/build/dist/UI/Components/Charts/Types/XValue.js +2 -0
  157. package/build/dist/UI/Components/Charts/Types/XValue.js.map +1 -0
  158. package/build/dist/UI/Components/Charts/Types/YAxis/YAxis.js +8 -0
  159. package/build/dist/UI/Components/Charts/Types/YAxis/YAxis.js.map +1 -0
  160. package/build/dist/UI/Components/Charts/Types/YAxis/YAxisMaxMin.js +2 -0
  161. package/build/dist/UI/Components/Charts/Types/YAxis/YAxisMaxMin.js.map +1 -0
  162. package/build/dist/UI/Components/Charts/Types/YAxis/YAxisType.js +6 -0
  163. package/build/dist/UI/Components/Charts/Types/YAxis/YAxisType.js.map +1 -0
  164. package/build/dist/UI/Components/Charts/Types/YValue.js +2 -0
  165. package/build/dist/UI/Components/Charts/Types/YValue.js.map +1 -0
  166. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +109 -0
  167. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -0
  168. package/build/dist/UI/Components/Charts/Utils/XAxis.js +241 -0
  169. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -0
  170. package/build/dist/UI/Components/Detail/Detail.js +5 -1
  171. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  172. package/build/dist/UI/Components/GanttChart/Row/Row.js +2 -2
  173. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +1 -1
  174. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +3 -3
  175. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +1 -1
  176. package/build/dist/UI/Components/Icon/Icon.js +6 -0
  177. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  178. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +1 -1
  179. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  180. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  181. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  182. package/build/dist/UI/Config.js +1 -0
  183. package/build/dist/UI/Config.js.map +1 -1
  184. package/build/dist/UI/Utils/Telemetry.js +6 -3
  185. package/build/dist/UI/Utils/Telemetry.js.map +1 -1
  186. package/build/dist/Utils/Crypto.js +10 -0
  187. package/build/dist/Utils/Crypto.js.map +1 -0
  188. package/package.json +15 -13
  189. package/UI/Components/Charts/Bar/Bar.tsx +0 -0
  190. package/UI/Components/Charts/Base/BaseChart.tsx +0 -0
  191. package/UI/Components/Charts/Tooltip/Tooltip.tsx +0 -84
  192. package/build/dist/UI/Components/Charts/Bar/Bar.js +0 -2
  193. package/build/dist/UI/Components/Charts/Bar/Bar.js.map +0 -1
  194. package/build/dist/UI/Components/Charts/Base/BaseChart.js +0 -2
  195. package/build/dist/UI/Components/Charts/Base/BaseChart.js.map +0 -1
  196. package/build/dist/UI/Components/Charts/Tooltip/Tooltip.js +0 -34
  197. package/build/dist/UI/Components/Charts/Tooltip/Tooltip.js.map +0 -1
@@ -0,0 +1,1005 @@
1
+ // Tremor Raw LineChart [v0.3.1]
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+
4
+ "use client";
5
+
6
+ import React from "react";
7
+ import { RiArrowLeftSLine, RiArrowRightSLine } from "@remixicon/react";
8
+ import {
9
+ CartesianGrid,
10
+ Dot,
11
+ Label,
12
+ Line,
13
+ Legend as RechartsLegend,
14
+ LineChart as RechartsLineChart,
15
+ ResponsiveContainer,
16
+ Tooltip,
17
+ XAxis,
18
+ YAxis,
19
+ } from "recharts";
20
+ import { AxisDomain } from "recharts/types/util/types";
21
+
22
+ import { useOnWindowResize } from "../Utils/UseWindowOnResize";
23
+ import {
24
+ AvailableChartColors,
25
+ AvailableChartColorsKeys,
26
+ constructCategoryColors,
27
+ getColorClassName,
28
+ } from "../Utils/ChartColors";
29
+ import { cx } from "../Utils/Cx";
30
+ import { getYAxisDomain } from "../Utils/GetYAxisDomain";
31
+ import { hasOnlyOneValueForKey } from "../Utils/HasOnlyOneValueForKey";
32
+ import ChartCurve from "../../Types/ChartCurve";
33
+
34
+ //#region Legend
35
+
36
+ interface LegendItemProps {
37
+ name: string;
38
+ color: AvailableChartColorsKeys;
39
+ onClick?: (name: string, color: AvailableChartColorsKeys) => void;
40
+ activeLegend?: string;
41
+ }
42
+
43
+ const LegendItem: ({
44
+ name,
45
+ color,
46
+ onClick,
47
+ activeLegend,
48
+ }: LegendItemProps) => React.JSX.Element = ({
49
+ name,
50
+ color,
51
+ onClick,
52
+ activeLegend,
53
+ }: LegendItemProps) => {
54
+ const hasOnValueChange: boolean = Boolean(onClick);
55
+ return (
56
+ <li
57
+ className={cx(
58
+ // base
59
+ "group inline-flex flex-nowrap items-center gap-1.5 whitespace-nowrap rounded px-2 py-1 transition",
60
+ hasOnValueChange
61
+ ? "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800"
62
+ : "cursor-default",
63
+ )}
64
+ onClick={(e: React.MouseEvent<HTMLLIElement, MouseEvent>) => {
65
+ e.stopPropagation();
66
+ onClick?.(name, color);
67
+ }}
68
+ >
69
+ <span
70
+ className={cx(
71
+ "h-[3px] w-3.5 shrink-0 rounded-full",
72
+ getColorClassName(color, "bg"),
73
+ activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
74
+ )}
75
+ aria-hidden={true}
76
+ />
77
+ <p
78
+ className={cx(
79
+ // base
80
+ "truncate whitespace-nowrap text-xs",
81
+ // text color
82
+ "text-gray-700 dark:text-gray-300",
83
+ hasOnValueChange &&
84
+ "group-hover:text-gray-900 dark:group-hover:text-gray-50",
85
+ activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
86
+ )}
87
+ >
88
+ {name}
89
+ </p>
90
+ </li>
91
+ );
92
+ };
93
+
94
+ interface ScrollButtonProps {
95
+ icon: React.ElementType;
96
+ onClick?: () => void;
97
+ disabled?: boolean;
98
+ }
99
+
100
+ const ScrollButton: ({
101
+ icon,
102
+ onClick,
103
+ disabled,
104
+ }: ScrollButtonProps) => React.JSX.Element = ({
105
+ icon,
106
+ onClick,
107
+ disabled,
108
+ }: ScrollButtonProps) => {
109
+ const Icon: React.ElementType<any, keyof React.JSX.IntrinsicElements> = icon;
110
+ const [isPressed, setIsPressed] = React.useState(false);
111
+ const intervalRef: React.MutableRefObject<NodeJS.Timeout | null> =
112
+ React.useRef<NodeJS.Timeout | null>(null);
113
+
114
+ React.useEffect(() => {
115
+ if (isPressed) {
116
+ intervalRef.current = setInterval(() => {
117
+ onClick?.();
118
+ }, 300);
119
+ } else {
120
+ clearInterval(intervalRef.current as NodeJS.Timeout);
121
+ }
122
+ return () => {
123
+ return clearInterval(intervalRef.current as NodeJS.Timeout);
124
+ };
125
+ }, [isPressed, onClick]);
126
+
127
+ React.useEffect(() => {
128
+ if (disabled) {
129
+ clearInterval(intervalRef.current as NodeJS.Timeout);
130
+ setIsPressed(false);
131
+ }
132
+ }, [disabled]);
133
+
134
+ return (
135
+ <button
136
+ type="button"
137
+ className={cx(
138
+ // base
139
+ "group inline-flex size-5 items-center truncate rounded transition",
140
+ disabled
141
+ ? "cursor-not-allowed text-gray-400 dark:text-gray-600"
142
+ : "cursor-pointer text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-50",
143
+ )}
144
+ disabled={disabled}
145
+ onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
146
+ e.stopPropagation();
147
+ onClick?.();
148
+ }}
149
+ onMouseDown={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
150
+ e.stopPropagation();
151
+ setIsPressed(true);
152
+ }}
153
+ onMouseUp={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
154
+ e.stopPropagation();
155
+ setIsPressed(false);
156
+ }}
157
+ >
158
+ <Icon className="size-full" aria-hidden="true" />
159
+ </button>
160
+ );
161
+ };
162
+
163
+ interface LegendProps extends React.OlHTMLAttributes<HTMLOListElement> {
164
+ categories: string[];
165
+ colors?: AvailableChartColorsKeys[];
166
+ onClickLegendItem?: (category: string, color: string) => void;
167
+ activeLegend?: string;
168
+ enableLegendSlider?: boolean;
169
+ }
170
+
171
+ type HasScrollProps = {
172
+ left: boolean;
173
+ right: boolean;
174
+ };
175
+
176
+ const Legend: React.ForwardRefExoticComponent<
177
+ LegendProps & React.RefAttributes<HTMLOListElement>
178
+ > = React.forwardRef<HTMLOListElement, LegendProps>(
179
+ (props: LegendProps, ref: React.ForwardedRef<HTMLOListElement>) => {
180
+ const {
181
+ categories,
182
+ colors = AvailableChartColors,
183
+ className,
184
+ onClickLegendItem,
185
+ activeLegend,
186
+ enableLegendSlider = false,
187
+ ...other
188
+ } = props;
189
+
190
+ const scrollableRef: React.RefObject<HTMLInputElement> =
191
+ React.useRef<HTMLInputElement>(null);
192
+ const scrollButtonsRef: React.RefObject<HTMLDivElement> =
193
+ React.useRef<HTMLDivElement>(null);
194
+ const [hasScroll, setHasScroll] = React.useState<HasScrollProps | null>(
195
+ null,
196
+ );
197
+ const [isKeyDowned, setIsKeyDowned] = React.useState<string | null>(null);
198
+ const intervalRef: React.MutableRefObject<NodeJS.Timeout | null> =
199
+ React.useRef<NodeJS.Timeout | null>(null);
200
+
201
+ const checkScroll: () => void = React.useCallback(() => {
202
+ const scrollable: HTMLInputElement | null = scrollableRef?.current;
203
+ if (!scrollable) {
204
+ return;
205
+ }
206
+
207
+ const hasLeftScroll: boolean = scrollable.scrollLeft > 0;
208
+ const hasRightScroll: boolean =
209
+ scrollable.scrollWidth - scrollable.clientWidth > scrollable.scrollLeft;
210
+
211
+ setHasScroll({ left: hasLeftScroll, right: hasRightScroll });
212
+ }, [setHasScroll]);
213
+
214
+ const scrollToTest: (direction: "left" | "right") => void =
215
+ React.useCallback(
216
+ (direction: "left" | "right") => {
217
+ const element: HTMLInputElement | null = scrollableRef?.current;
218
+ const scrollButtons: HTMLDivElement | null =
219
+ scrollButtonsRef?.current;
220
+ const scrollButtonsWith: number = scrollButtons?.clientWidth ?? 0;
221
+ const width: number = element?.clientWidth ?? 0;
222
+
223
+ if (element && enableLegendSlider) {
224
+ element.scrollTo({
225
+ left:
226
+ direction === "left"
227
+ ? element.scrollLeft - width + scrollButtonsWith
228
+ : element.scrollLeft + width - scrollButtonsWith,
229
+ behavior: "smooth",
230
+ });
231
+ setTimeout(() => {
232
+ checkScroll();
233
+ }, 400);
234
+ }
235
+ },
236
+ [enableLegendSlider, checkScroll],
237
+ );
238
+
239
+ React.useEffect(() => {
240
+ const keyDownHandler: (key: string) => void = (key: string) => {
241
+ if (key === "ArrowLeft") {
242
+ scrollToTest("left");
243
+ } else if (key === "ArrowRight") {
244
+ scrollToTest("right");
245
+ }
246
+ };
247
+ if (isKeyDowned) {
248
+ keyDownHandler(isKeyDowned);
249
+ intervalRef.current = setInterval(() => {
250
+ keyDownHandler(isKeyDowned);
251
+ }, 300);
252
+ } else {
253
+ clearInterval(intervalRef.current!);
254
+ }
255
+ return () => {
256
+ return clearInterval(intervalRef.current as NodeJS.Timeout);
257
+ };
258
+ }, [isKeyDowned, scrollToTest]);
259
+
260
+ const keyDown: (e: KeyboardEvent) => void = (e: KeyboardEvent) => {
261
+ e.stopPropagation();
262
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
263
+ e.preventDefault();
264
+ setIsKeyDowned(e.key);
265
+ }
266
+ };
267
+ const keyUp: (e: KeyboardEvent) => void = (e: KeyboardEvent) => {
268
+ e.stopPropagation();
269
+ setIsKeyDowned(null);
270
+ };
271
+
272
+ React.useEffect(() => {
273
+ const scrollable: HTMLInputElement | null = scrollableRef?.current;
274
+ if (enableLegendSlider) {
275
+ checkScroll();
276
+ scrollable?.addEventListener("keydown", keyDown);
277
+ scrollable?.addEventListener("keyup", keyUp);
278
+ }
279
+
280
+ return () => {
281
+ scrollable?.removeEventListener("keydown", keyDown);
282
+ scrollable?.removeEventListener("keyup", keyUp);
283
+ };
284
+ }, [checkScroll, enableLegendSlider]);
285
+
286
+ return (
287
+ <ol
288
+ ref={ref}
289
+ className={cx("relative overflow-hidden", className)}
290
+ {...other}
291
+ >
292
+ <div
293
+ ref={scrollableRef}
294
+ tabIndex={0}
295
+ className={cx(
296
+ "flex h-full",
297
+ enableLegendSlider
298
+ ? hasScroll?.right || hasScroll?.left
299
+ ? "snap-mandatory items-center overflow-auto pl-4 pr-12 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
300
+ : ""
301
+ : "flex-wrap",
302
+ )}
303
+ >
304
+ {categories.map((category: string, index: number) => {
305
+ return (
306
+ <LegendItem
307
+ key={`item-${index}`}
308
+ name={category}
309
+ color={colors[index] as AvailableChartColorsKeys}
310
+ onClick={onClickLegendItem as any}
311
+ activeLegend={activeLegend!}
312
+ />
313
+ );
314
+ })}
315
+ </div>
316
+ {enableLegendSlider && (hasScroll?.right || hasScroll?.left) ? (
317
+ <>
318
+ <div
319
+ ref={scrollButtonsRef}
320
+ className={cx(
321
+ // base
322
+ "absolute bottom-0 right-0 top-0 flex h-full items-center justify-center pr-1",
323
+ // background color
324
+ "bg-white dark:bg-gray-950",
325
+ )}
326
+ >
327
+ <ScrollButton
328
+ icon={RiArrowLeftSLine}
329
+ onClick={() => {
330
+ setIsKeyDowned(null);
331
+ scrollToTest("left");
332
+ }}
333
+ disabled={!hasScroll?.left}
334
+ />
335
+ <ScrollButton
336
+ icon={RiArrowRightSLine}
337
+ onClick={() => {
338
+ setIsKeyDowned(null);
339
+ scrollToTest("right");
340
+ }}
341
+ disabled={!hasScroll?.right}
342
+ />
343
+ </div>
344
+ </>
345
+ ) : null}
346
+ </ol>
347
+ );
348
+ },
349
+ );
350
+
351
+ Legend.displayName = "Legend";
352
+
353
+ const ChartLegend: (
354
+ { payload }: any,
355
+ categoryColors: Map<string, AvailableChartColorsKeys>,
356
+ setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
357
+ activeLegend: string | undefined,
358
+ onClick?: (category: string, color: string) => void,
359
+ enableLegendSlider?: boolean,
360
+ legendPosition?: "left" | "center" | "right",
361
+ yAxisWidth?: number,
362
+ ) => React.JSX.Element = (
363
+ { payload }: any,
364
+ categoryColors: Map<string, AvailableChartColorsKeys>,
365
+ setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
366
+ activeLegend: string | undefined,
367
+ onClick?: (category: string, color: string) => void,
368
+ enableLegendSlider?: boolean,
369
+ legendPosition?: "left" | "center" | "right",
370
+ yAxisWidth?: number,
371
+ ): React.JSX.Element => {
372
+ const legendRef: React.RefObject<HTMLDivElement> =
373
+ React.useRef<HTMLDivElement>(null);
374
+
375
+ useOnWindowResize(() => {
376
+ const calculateHeight: (height: number | undefined) => number = (
377
+ height: number | undefined,
378
+ ) => {
379
+ return height ? Number(height) + 15 : 60;
380
+ };
381
+ setLegendHeight(calculateHeight(legendRef.current?.clientHeight));
382
+ });
383
+
384
+ const legendPayload: Array<PayloadItem> = payload.filter(
385
+ (item: PayloadItem) => {
386
+ return item.type !== "none";
387
+ },
388
+ );
389
+
390
+ const paddingLeft: number =
391
+ legendPosition === "left" && yAxisWidth ? yAxisWidth - 8 : 0;
392
+
393
+ return (
394
+ <div
395
+ ref={legendRef}
396
+ style={{ paddingLeft: paddingLeft }}
397
+ className={cx(
398
+ "flex items-center",
399
+ { "justify-center": legendPosition === "center" },
400
+ { "justify-start": legendPosition === "left" },
401
+ { "justify-end": legendPosition === "right" },
402
+ )}
403
+ >
404
+ <Legend
405
+ categories={legendPayload.map((entry: PayloadItem) => {
406
+ return entry.value as any;
407
+ })}
408
+ colors={legendPayload.map((entry: PayloadItem) => {
409
+ return categoryColors.get(entry.value! as any)!;
410
+ })}
411
+ onClickLegendItem={onClick!}
412
+ activeLegend={activeLegend!}
413
+ enableLegendSlider={enableLegendSlider!}
414
+ />
415
+ </div>
416
+ );
417
+ };
418
+
419
+ //#region Tooltip
420
+
421
+ type TooltipProps = Pick<ChartTooltipProps, "active" | "payload" | "label">;
422
+
423
+ export type PayloadItem = {
424
+ // eslint-disable-next-line react/no-unused-prop-types
425
+ category: string;
426
+ // eslint-disable-next-line react/no-unused-prop-types
427
+ value: number;
428
+ // eslint-disable-next-line react/no-unused-prop-types
429
+ index: string;
430
+ // eslint-disable-next-line react/no-unused-prop-types
431
+ color: AvailableChartColorsKeys;
432
+ // eslint-disable-next-line react/no-unused-prop-types
433
+ type?: string;
434
+ // eslint-disable-next-line react/no-unused-prop-types
435
+ payload: any;
436
+ };
437
+
438
+ interface ChartTooltipProps {
439
+ active: boolean | undefined;
440
+ payload: PayloadItem[];
441
+ label: string;
442
+ valueFormatter: (value: number) => string;
443
+ }
444
+
445
+ const ChartTooltip: ({
446
+ active,
447
+ payload,
448
+ label,
449
+ valueFormatter,
450
+ }: ChartTooltipProps) => React.JSX.Element | null = ({
451
+ active,
452
+ payload,
453
+ label,
454
+ valueFormatter,
455
+ }: ChartTooltipProps): React.JSX.Element | null => {
456
+ if (active && payload && payload.length) {
457
+ const legendPayload: PayloadItem[] = payload.filter((item: PayloadItem) => {
458
+ return item.type !== "none";
459
+ });
460
+ return (
461
+ <div
462
+ className={cx(
463
+ // base
464
+ "rounded-md border text-sm shadow-md",
465
+ // border color
466
+ "border-gray-200 dark:border-gray-800",
467
+ // background color
468
+ "bg-white dark:bg-gray-950",
469
+ )}
470
+ >
471
+ <div className={cx("border-b border-inherit px-4 py-2")}>
472
+ <p
473
+ className={cx(
474
+ // base
475
+ "font-medium",
476
+ // text color
477
+ "text-gray-900 dark:text-gray-50",
478
+ )}
479
+ >
480
+ {label}
481
+ </p>
482
+ </div>
483
+ <div className={cx("space-y-1 px-4 py-2")}>
484
+ {legendPayload.map(
485
+ ({ value, category, color }: PayloadItem, index: number) => {
486
+ return (
487
+ <div
488
+ key={`id-${index}`}
489
+ className="flex items-center justify-between space-x-8"
490
+ >
491
+ <div className="flex items-center space-x-2">
492
+ <span
493
+ aria-hidden="true"
494
+ className={cx(
495
+ "h-[3px] w-3.5 shrink-0 rounded-full",
496
+ getColorClassName(color, "bg"),
497
+ )}
498
+ />
499
+ <p
500
+ className={cx(
501
+ // base
502
+ "whitespace-nowrap text-right",
503
+ // text color
504
+ "text-gray-700 dark:text-gray-300",
505
+ )}
506
+ >
507
+ {category}
508
+ </p>
509
+ </div>
510
+ <p
511
+ className={cx(
512
+ // base
513
+ "whitespace-nowrap text-right font-medium tabular-nums",
514
+ // text color
515
+ "text-gray-900 dark:text-gray-50",
516
+ )}
517
+ >
518
+ {valueFormatter(value)}
519
+ </p>
520
+ </div>
521
+ );
522
+ },
523
+ )}
524
+ </div>
525
+ </div>
526
+ );
527
+ }
528
+ return null;
529
+ };
530
+
531
+ //#region LineChart
532
+
533
+ interface ActiveDot {
534
+ index?: number;
535
+ dataKey?: string;
536
+ }
537
+
538
+ type BaseEventProps = {
539
+ eventType: "dot" | "category";
540
+ categoryClicked: string;
541
+ [key: string]: number | string;
542
+ };
543
+
544
+ type LineChartEventProps = BaseEventProps | null | undefined;
545
+
546
+ interface LineChartProps extends React.HTMLAttributes<HTMLDivElement> {
547
+ data: Record<string, any>[];
548
+ index: string;
549
+ categories: string[];
550
+ colors?: AvailableChartColorsKeys[];
551
+ valueFormatter?: (value: number) => string;
552
+ startEndOnly?: boolean;
553
+ showXAxis?: boolean;
554
+ showYAxis?: boolean;
555
+ showGridLines?: boolean;
556
+ yAxisWidth?: number;
557
+ intervalType?: "preserveStartEnd" | "equidistantPreserveStart";
558
+ showTooltip?: boolean;
559
+ showLegend?: boolean;
560
+ autoMinValue?: boolean;
561
+ minValue?: number;
562
+ maxValue?: number;
563
+ allowDecimals?: boolean;
564
+ onValueChange?: (value: LineChartEventProps) => void;
565
+ enableLegendSlider?: boolean;
566
+ tickGap?: number;
567
+ connectNulls?: boolean;
568
+ xAxisLabel?: string;
569
+ yAxisLabel?: string;
570
+ curve?: ChartCurve;
571
+ legendPosition?: "left" | "center" | "right";
572
+ tooltipCallback?: (tooltipCallbackContent: TooltipProps) => void;
573
+ customTooltip?: React.ComponentType<TooltipProps>;
574
+ syncId?: string | undefined;
575
+ }
576
+
577
+ const LineChart: React.ForwardRefExoticComponent<
578
+ LineChartProps & React.RefAttributes<HTMLDivElement>
579
+ > = React.forwardRef<HTMLDivElement, LineChartProps>(
580
+ (props: LineChartProps, ref: React.ForwardedRef<HTMLDivElement>) => {
581
+ const {
582
+ data = [],
583
+ categories = [],
584
+ index,
585
+ colors = AvailableChartColors,
586
+ valueFormatter = (value: number) => {
587
+ return value.toString();
588
+ },
589
+ startEndOnly = false,
590
+ showXAxis = true,
591
+ showYAxis = true,
592
+ showGridLines = true,
593
+ yAxisWidth = 56,
594
+ intervalType = "equidistantPreserveStart",
595
+ showTooltip = true,
596
+ showLegend = true,
597
+ autoMinValue = false,
598
+ minValue,
599
+ maxValue,
600
+ allowDecimals = true,
601
+ connectNulls = false,
602
+ className,
603
+ onValueChange,
604
+ enableLegendSlider = false,
605
+ tickGap = 5,
606
+ xAxisLabel,
607
+ yAxisLabel,
608
+ legendPosition = "right",
609
+ tooltipCallback,
610
+ customTooltip,
611
+ ...other
612
+ } = props;
613
+ const CustomTooltip: React.ComponentType<TooltipProps> | undefined =
614
+ customTooltip;
615
+ const paddingValue: 0 | 20 =
616
+ (!showXAxis && !showYAxis) || (startEndOnly && !showYAxis) ? 0 : 20;
617
+ const [legendHeight, setLegendHeight] = React.useState(60);
618
+ const [activeDot, setActiveDot] = React.useState<ActiveDot | undefined>(
619
+ undefined,
620
+ );
621
+ const [activeLegend, setActiveLegend] = React.useState<string | undefined>(
622
+ undefined,
623
+ );
624
+ const categoryColors: Map<string, string | number> =
625
+ constructCategoryColors(categories, colors);
626
+
627
+ const yAxisDomain: (number | "auto")[] = getYAxisDomain(
628
+ autoMinValue,
629
+ minValue,
630
+ maxValue,
631
+ );
632
+ const hasOnValueChange: boolean = Boolean(onValueChange);
633
+ const prevActiveRef: React.MutableRefObject<boolean | undefined> =
634
+ React.useRef<boolean | undefined>(undefined);
635
+ const prevLabelRef: React.MutableRefObject<string | undefined> =
636
+ React.useRef<string | undefined>(undefined);
637
+
638
+ function onDotClick(itemData: any, event: React.MouseEvent): void {
639
+ event.stopPropagation();
640
+
641
+ if (!hasOnValueChange) {
642
+ return;
643
+ }
644
+ if (
645
+ (itemData.index === activeDot?.index &&
646
+ itemData.dataKey === activeDot?.dataKey) ||
647
+ (hasOnlyOneValueForKey(data, itemData.dataKey) &&
648
+ activeLegend &&
649
+ activeLegend === itemData.dataKey)
650
+ ) {
651
+ setActiveLegend(undefined);
652
+ setActiveDot(undefined);
653
+ onValueChange?.(null);
654
+ } else {
655
+ setActiveLegend(itemData.dataKey);
656
+ setActiveDot({
657
+ index: itemData.index,
658
+ dataKey: itemData.dataKey,
659
+ });
660
+ onValueChange?.({
661
+ eventType: "dot",
662
+ categoryClicked: itemData.dataKey,
663
+ ...itemData.payload,
664
+ });
665
+ }
666
+ }
667
+
668
+ function onCategoryClick(dataKey: string): void {
669
+ if (!hasOnValueChange) {
670
+ return;
671
+ }
672
+ if (
673
+ (dataKey === activeLegend && !activeDot) ||
674
+ (hasOnlyOneValueForKey(data, dataKey) &&
675
+ activeDot &&
676
+ activeDot.dataKey === dataKey)
677
+ ) {
678
+ setActiveLegend(undefined);
679
+ onValueChange?.(null);
680
+ } else {
681
+ setActiveLegend(dataKey);
682
+ onValueChange?.({
683
+ eventType: "category",
684
+ categoryClicked: dataKey,
685
+ });
686
+ }
687
+ setActiveDot(undefined);
688
+ }
689
+
690
+ return (
691
+ <div ref={ref} className={cx("h-80 w-full", className)} {...other}>
692
+ <ResponsiveContainer>
693
+ <RechartsLineChart
694
+ data={data}
695
+ syncId={props.syncId}
696
+ onClick={
697
+ hasOnValueChange && (activeLegend || activeDot)
698
+ ? () => {
699
+ setActiveDot(undefined);
700
+ setActiveLegend(undefined);
701
+ onValueChange?.(null);
702
+ }
703
+ : undefined
704
+ }
705
+ margin={{
706
+ bottom: (xAxisLabel ? 30 : undefined) as unknown as number,
707
+ left: (yAxisLabel ? 20 : undefined) as unknown as number,
708
+ right: (yAxisLabel ? 5 : undefined) as unknown as number,
709
+ top: 5,
710
+ }}
711
+ >
712
+ {showGridLines ? (
713
+ <CartesianGrid
714
+ className={cx("stroke-gray-200 stroke-1 dark:stroke-gray-800")}
715
+ horizontal={true}
716
+ vertical={false}
717
+ />
718
+ ) : null}
719
+ <XAxis
720
+ padding={{ left: paddingValue, right: paddingValue }}
721
+ hide={!showXAxis}
722
+ dataKey={index}
723
+ interval={startEndOnly ? "preserveStartEnd" : intervalType}
724
+ tick={{ transform: "translate(0, 6)" }}
725
+ ticks={
726
+ startEndOnly
727
+ ? ([
728
+ (data[0] as any)[index],
729
+ (data[data.length - 1] as any)[index],
730
+ ] as any)
731
+ : undefined
732
+ }
733
+ fill=""
734
+ stroke=""
735
+ className={cx(
736
+ // base
737
+ "text-xs",
738
+ // text fill
739
+ "fill-gray-500 dark:fill-gray-500",
740
+ )}
741
+ tickLine={false}
742
+ axisLine={false}
743
+ minTickGap={tickGap}
744
+ >
745
+ {xAxisLabel && (
746
+ <Label
747
+ position="insideBottom"
748
+ offset={-20}
749
+ className="fill-gray-800 text-sm font-medium dark:fill-gray-200"
750
+ >
751
+ {xAxisLabel}
752
+ </Label>
753
+ )}
754
+ </XAxis>
755
+ <YAxis
756
+ width={yAxisWidth}
757
+ hide={!showYAxis}
758
+ axisLine={false}
759
+ tickLine={false}
760
+ type="number"
761
+ domain={yAxisDomain as AxisDomain}
762
+ tick={{ transform: "translate(-3, 0)" }}
763
+ fill=""
764
+ stroke=""
765
+ className={cx(
766
+ // base
767
+ "text-xs",
768
+ // text fill
769
+ "fill-gray-500 dark:fill-gray-500",
770
+ )}
771
+ tickFormatter={valueFormatter}
772
+ allowDecimals={allowDecimals}
773
+ >
774
+ {yAxisLabel && (
775
+ <Label
776
+ position="insideLeft"
777
+ style={{ textAnchor: "middle" }}
778
+ angle={-90}
779
+ offset={-15}
780
+ className="fill-gray-800 text-sm font-medium dark:fill-gray-200"
781
+ >
782
+ {yAxisLabel}
783
+ </Label>
784
+ )}
785
+ </YAxis>
786
+ <Tooltip
787
+ wrapperStyle={{ outline: "none" }}
788
+ isAnimationActive={true}
789
+ animationDuration={100}
790
+ cursor={{ stroke: "#d1d5db", strokeWidth: 1 }}
791
+ offset={20}
792
+ position={{ y: 0 }}
793
+ content={({ active, payload, label }: any) => {
794
+ const cleanPayload: TooltipProps["payload"] = payload
795
+ ? payload.map((item: any) => {
796
+ return {
797
+ category: item.dataKey,
798
+ value: item.value,
799
+ index: item.payload[index],
800
+ color: categoryColors.get(
801
+ item.dataKey,
802
+ ) as AvailableChartColorsKeys,
803
+ type: item.type,
804
+ payload: item.payload,
805
+ };
806
+ })
807
+ : [];
808
+
809
+ if (
810
+ tooltipCallback &&
811
+ (active !== prevActiveRef.current ||
812
+ label !== prevLabelRef.current)
813
+ ) {
814
+ tooltipCallback({ active, payload: cleanPayload, label });
815
+ prevActiveRef.current = active;
816
+ prevLabelRef.current = label;
817
+ }
818
+
819
+ return showTooltip && active ? (
820
+ CustomTooltip ? (
821
+ <CustomTooltip
822
+ active={active}
823
+ payload={cleanPayload}
824
+ label={label}
825
+ />
826
+ ) : (
827
+ <ChartTooltip
828
+ active={active}
829
+ payload={cleanPayload}
830
+ label={label}
831
+ valueFormatter={valueFormatter}
832
+ />
833
+ )
834
+ ) : null;
835
+ }}
836
+ />
837
+
838
+ {showLegend ? (
839
+ <RechartsLegend
840
+ verticalAlign="top"
841
+ height={legendHeight}
842
+ content={({ payload }: any) => {
843
+ return ChartLegend(
844
+ { payload },
845
+ categoryColors,
846
+ setLegendHeight,
847
+ activeLegend,
848
+ hasOnValueChange
849
+ ? (clickedLegendItem: string) => {
850
+ return onCategoryClick(clickedLegendItem);
851
+ }
852
+ : undefined,
853
+ enableLegendSlider,
854
+ legendPosition,
855
+ yAxisWidth,
856
+ );
857
+ }}
858
+ />
859
+ ) : null}
860
+ {categories.map((category: string) => {
861
+ return (
862
+ <Line
863
+ className={cx(
864
+ getColorClassName(
865
+ categoryColors.get(category) as AvailableChartColorsKeys,
866
+ "stroke",
867
+ ),
868
+ )}
869
+ strokeOpacity={
870
+ activeDot || (activeLegend && activeLegend !== category)
871
+ ? 0.3
872
+ : 1
873
+ }
874
+ activeDot={(props: any) => {
875
+ const {
876
+ cx: cxCoord,
877
+ cy: cyCoord,
878
+ stroke,
879
+ strokeLinecap,
880
+ strokeLinejoin,
881
+ strokeWidth,
882
+ dataKey,
883
+ } = props;
884
+ return (
885
+ <Dot
886
+ className={cx(
887
+ "stroke-white dark:stroke-gray-950",
888
+ onValueChange ? "cursor-pointer" : "",
889
+ getColorClassName(
890
+ categoryColors.get(
891
+ dataKey,
892
+ ) as AvailableChartColorsKeys,
893
+ "fill",
894
+ ),
895
+ )}
896
+ cx={cxCoord}
897
+ cy={cyCoord}
898
+ r={5}
899
+ fill=""
900
+ stroke={stroke}
901
+ strokeLinecap={strokeLinecap}
902
+ strokeLinejoin={strokeLinejoin}
903
+ strokeWidth={strokeWidth}
904
+ onClick={(_: any, event: any) => {
905
+ return onDotClick(props, event);
906
+ }}
907
+ />
908
+ );
909
+ }}
910
+ dot={(props: any) => {
911
+ const {
912
+ stroke,
913
+ strokeLinecap,
914
+ strokeLinejoin,
915
+ strokeWidth,
916
+ cx: cxCoord,
917
+ cy: cyCoord,
918
+ dataKey,
919
+ index,
920
+ } = props;
921
+
922
+ if (
923
+ (hasOnlyOneValueForKey(data, category) &&
924
+ !(
925
+ activeDot ||
926
+ (activeLegend && activeLegend !== category)
927
+ )) ||
928
+ (activeDot?.index === index &&
929
+ activeDot?.dataKey === category)
930
+ ) {
931
+ return (
932
+ <Dot
933
+ key={index}
934
+ cx={cxCoord}
935
+ cy={cyCoord}
936
+ r={5}
937
+ stroke={stroke}
938
+ fill=""
939
+ strokeLinecap={strokeLinecap}
940
+ strokeLinejoin={strokeLinejoin}
941
+ strokeWidth={strokeWidth}
942
+ className={cx(
943
+ "stroke-white dark:stroke-gray-950",
944
+ onValueChange ? "cursor-pointer" : "",
945
+ getColorClassName(
946
+ categoryColors.get(
947
+ dataKey,
948
+ ) as AvailableChartColorsKeys,
949
+ "fill",
950
+ ),
951
+ )}
952
+ />
953
+ );
954
+ }
955
+ return <React.Fragment key={index}></React.Fragment>;
956
+ }}
957
+ key={category}
958
+ name={category}
959
+ type="linear"
960
+ dataKey={category}
961
+ stroke=""
962
+ strokeWidth={2}
963
+ strokeLinejoin="round"
964
+ strokeLinecap="round"
965
+ isAnimationActive={false}
966
+ connectNulls={connectNulls}
967
+ />
968
+ );
969
+ })}
970
+ {/* hidden lines to increase clickable target area */}
971
+ {onValueChange
972
+ ? categories.map((category: string) => {
973
+ return (
974
+ <Line
975
+ className={cx("cursor-pointer")}
976
+ strokeOpacity={0}
977
+ key={category}
978
+ name={category}
979
+ type={props.curve || ChartCurve.LINEAR}
980
+ dataKey={category}
981
+ stroke="transparent"
982
+ fill="transparent"
983
+ legendType="none"
984
+ tooltipType="none"
985
+ strokeWidth={12}
986
+ connectNulls={connectNulls}
987
+ onClick={(props: any, event: any) => {
988
+ event.stopPropagation();
989
+ const { name } = props;
990
+ onCategoryClick(name);
991
+ }}
992
+ />
993
+ );
994
+ })
995
+ : null}
996
+ </RechartsLineChart>
997
+ </ResponsiveContainer>
998
+ </div>
999
+ );
1000
+ },
1001
+ );
1002
+
1003
+ LineChart.displayName = "LineChart";
1004
+
1005
+ export { LineChart, type LineChartEventProps, type TooltipProps };