@oneuptime/common 11.5.0 → 11.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.ts +2 -2
  2. package/Server/Infrastructure/Postgres/SchemaMigrations/1783937343400-MigrationName.ts +386 -0
  3. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  4. package/Server/Services/AnalyticsDatabaseService.ts +59 -7
  5. package/Server/Services/MetricService.ts +124 -28
  6. package/Server/Services/SpanService.ts +11 -0
  7. package/Server/Types/AnalyticsDatabase/AggregateBy.ts +39 -0
  8. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +8 -5
  9. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +84 -0
  10. package/Tests/Server/Services/MetricServiceAggregate.test.ts +167 -0
  11. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +40 -0
  12. package/Types/BaseDatabase/AggregateBy.ts +11 -1
  13. package/Types/BaseDatabase/AggregationInterval.ts +10 -0
  14. package/Types/BaseDatabase/AggregationIntervalUtil.ts +23 -0
  15. package/Types/Metrics/MetricFormulaConfigData.ts +5 -0
  16. package/Types/Metrics/MetricQueryConfigData.ts +19 -0
  17. package/UI/Components/Charts/Area/AreaChart.tsx +15 -2
  18. package/UI/Components/Charts/Bar/BarChart.tsx +16 -2
  19. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +31 -32
  20. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +41 -17
  21. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +38 -34
  22. package/UI/Components/Charts/ChartLibrary/SparkChart/SparkChart.tsx +4 -3
  23. package/UI/Components/Charts/ChartLibrary/Utils/ChartColors.ts +48 -10
  24. package/UI/Components/Charts/Line/LineChart.tsx +15 -2
  25. package/UI/Components/MoreMenu/MoreMenu.tsx +61 -2
  26. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.js +2 -2
  27. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.js.map +1 -1
  28. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783937343400-MigrationName.js +384 -0
  29. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783937343400-MigrationName.js.map +1 -0
  30. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  31. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  32. package/build/dist/Server/Services/AnalyticsDatabaseService.js +43 -5
  33. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  34. package/build/dist/Server/Services/MetricService.js +110 -23
  35. package/build/dist/Server/Services/MetricService.js.map +1 -1
  36. package/build/dist/Server/Services/SpanService.js +10 -0
  37. package/build/dist/Server/Services/SpanService.js.map +1 -1
  38. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js +32 -0
  39. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js.map +1 -1
  40. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +2 -1
  41. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  42. package/build/dist/Types/BaseDatabase/AggregationInterval.js +10 -0
  43. package/build/dist/Types/BaseDatabase/AggregationInterval.js.map +1 -1
  44. package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js +13 -0
  45. package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js.map +1 -1
  46. package/build/dist/UI/Components/Charts/Area/AreaChart.js +3 -1
  47. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  48. package/build/dist/UI/Components/Charts/Bar/BarChart.js +3 -1
  49. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  50. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +13 -9
  51. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  52. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +19 -7
  53. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  54. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +21 -9
  55. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  56. package/build/dist/UI/Components/Charts/ChartLibrary/SparkChart/SparkChart.js.map +1 -1
  57. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js +26 -0
  58. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js.map +1 -1
  59. package/build/dist/UI/Components/Charts/Line/LineChart.js +3 -1
  60. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  61. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +44 -4
  62. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  63. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import GroupBy from "../../Server/Types/Database/GroupBy";
2
2
  import GenericObject from "../GenericObject";
3
+ import AggregationInterval from "./AggregationInterval";
3
4
  import AggregationType from "./AggregationType";
4
5
  import Query from "./Query";
5
6
  import Sort from "./Sort";
@@ -7,7 +8,16 @@ import Sort from "./Sort";
7
8
  export default interface AggregateBy<TBaseModel extends GenericObject> {
8
9
  aggregateColumnName: keyof TBaseModel;
9
10
  aggregationType: AggregationType;
10
- // aggregationInterval?: AggregationInterval;
11
+ /**
12
+ * Optional explicit time-bucket size. When omitted (the default), the
13
+ * bucket size is derived from the query window (≤3h → minute, ≤7d →
14
+ * hour, ≤6w → day, …) — see AggregationIntervalUtil. Set this to pin a
15
+ * granularity independent of the window (e.g. `Day` buckets over a
16
+ * one-week window), or to `Total` to aggregate the whole window into a
17
+ * single value per group (no time bucketing). Invalid values are
18
+ * rejected with a 400 in AnalyticsDatabaseService.aggregateBy.
19
+ */
20
+ aggregationInterval?: AggregationInterval | undefined;
11
21
  aggregationTimestampColumnName: keyof TBaseModel;
12
22
  startTimestamp: Date;
13
23
  endTimestamp: Date;
@@ -5,6 +5,16 @@ export enum AggregationInterval {
5
5
  Week = "Week",
6
6
  Month = "Month",
7
7
  Year = "Year",
8
+ /**
9
+ * No time bucketing: collapse the entire [startTimestamp, endTimestamp]
10
+ * window into a single aggregate per group (one total value over the
11
+ * whole range). Only meaningful as an explicit
12
+ * `AggregateBy.aggregationInterval` override — the window-derived
13
+ * interval picker never returns it. Builders emit one row per group with
14
+ * the earliest sample timestamp in the window as the bucket label (see
15
+ * AggregateUtil.buildBucketTimestampSelect).
16
+ */
17
+ Total = "Total",
8
18
  }
9
19
 
10
20
  export default AggregationInterval;
@@ -16,7 +16,21 @@ export class AggregationIntervalUtil {
16
16
  public static getAggregationIntervalForWindow(data: {
17
17
  startDate: Date;
18
18
  endDate: Date;
19
+ /**
20
+ * Explicit override. When set to a valid AggregationInterval it is
21
+ * returned verbatim (including `Total`), so callers can pin a bucket
22
+ * size independent of the window. Unknown/undefined falls through to
23
+ * the window-derived interval below.
24
+ */
25
+ aggregationInterval?: AggregationInterval | undefined;
19
26
  }): AggregationInterval {
27
+ if (
28
+ data.aggregationInterval &&
29
+ Object.values(AggregationInterval).includes(data.aggregationInterval)
30
+ ) {
31
+ return data.aggregationInterval;
32
+ }
33
+
20
34
  const startDate: Date = OneUptimeDate.fromString(data.startDate);
21
35
  const endDate: Date = OneUptimeDate.fromString(data.endDate);
22
36
 
@@ -65,6 +79,15 @@ export class AggregationIntervalUtil {
65
79
  return 1000 * 60 * 60 * 24 * 30;
66
80
  case AggregationInterval.Year:
67
81
  return 1000 * 60 * 60 * 24 * 365;
82
+ case AggregationInterval.Total:
83
+ /*
84
+ * `Total` is a single whole-window bucket with no fixed width. The
85
+ * window-derived picker never returns it, so grid-reconstruction
86
+ * callers (heartbeat charts, XAxis) never see it. Return a very
87
+ * large finite width (~1000 years) so any defensive fixed-step
88
+ * grid walk emits a single slot instead of looping forever.
89
+ */
90
+ return 1000 * 60 * 60 * 24 * 365 * 1000;
68
91
  default:
69
92
  return 1000 * 60;
70
93
  }
@@ -8,4 +8,9 @@ export default interface MetricFormulaConfigData {
8
8
  chartType?: MetricChartType | undefined;
9
9
  warningThreshold?: number | undefined;
10
10
  criticalThreshold?: number | undefined;
11
+ /*
12
+ * Optional user-chosen color for the formula's series, stored as a hex
13
+ * string (e.g. "#6366f1"). Unset = auto-assigned from the chart palette.
14
+ */
15
+ color?: string | undefined;
11
16
  }
@@ -31,6 +31,25 @@ export default interface MetricQueryConfigData {
31
31
  | undefined;
32
32
  warningThreshold?: number | undefined;
33
33
  criticalThreshold?: number | undefined;
34
+ /*
35
+ * Optional user-chosen color for this query's series. Stored as a hex
36
+ * string (e.g. "#6366f1") — the preset swatches write their palette hex
37
+ * and the custom picker writes an arbitrary hex. When set, it becomes the
38
+ * lead color for this query's chart (single-series → the series color;
39
+ * grouped multi-series → the first series, with the default palette filling
40
+ * the rest). Unset = auto-assigned from the chart-type palette.
41
+ */
42
+ color?: string | undefined;
43
+ /*
44
+ * Per-group color pins for GROUP-BY queries. Keyed by the individual
45
+ * "key=value" segment that MetricCharts' series renderer emits for a
46
+ * grouped series — e.g. { "service.name=api": "#f59e0b" }. Use "(unset)"
47
+ * as the value for null/empty groups to match the series name. A series
48
+ * whose composed name contains a pinned segment renders in that color
49
+ * (first matching segment wins for multi-key group-bys); unpinned series
50
+ * fall back to `color` (lead) then the chart-type palette. Stored as hex.
51
+ */
52
+ colorsByGroup?: Record<string, string> | undefined;
34
53
  /*
35
54
  * When true, the post-aggregation series points are transformed into
36
55
  * a per-second rate of change: `(value - previousValue) / Δt`. This is
@@ -1,5 +1,8 @@
1
1
  import { AreaChart } from "../ChartLibrary/AreaChart/AreaChart";
2
- import { AvailableChartColorsKeys } from "../ChartLibrary/Utils/ChartColors";
2
+ import {
3
+ AvailableChartColorsKeys,
4
+ ChartColorValue,
5
+ } from "../ChartLibrary/Utils/ChartColors";
3
6
  import React, {
4
7
  FunctionComponent,
5
8
  ReactElement,
@@ -51,6 +54,12 @@ export interface ComponentProps {
51
54
  */
52
55
  anomalyBandLowerSeriesName?: string | undefined;
53
56
  anomalyBandUpperSeriesName?: string | undefined;
57
+ /*
58
+ * Optional per-series color override (named palette keys or hex strings).
59
+ * When provided, replaces the default AreaChartPalette. Indexed by series
60
+ * position (index % length).
61
+ */
62
+ colors?: Array<ChartColorValue> | undefined;
54
63
  }
55
64
 
56
65
  export interface AreaInternalProps extends ComponentProps {
@@ -138,7 +147,11 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
138
147
  tickGap={30}
139
148
  index={"Time"}
140
149
  categories={categories}
141
- colors={AreaChartPalette}
150
+ colors={
151
+ props.colors && props.colors.length > 0
152
+ ? props.colors
153
+ : AreaChartPalette
154
+ }
142
155
  valueFormatter={props.yAxis.options.formatter || undefined}
143
156
  showTooltip={true}
144
157
  showLegend={props.showLegend !== false}
@@ -1,5 +1,8 @@
1
1
  import { BarChart } from "../ChartLibrary/BarChart/BarChart";
2
- import { AvailableChartColorsKeys } from "../ChartLibrary/Utils/ChartColors";
2
+ import {
3
+ AvailableChartColorsKeys,
4
+ ChartColorValue,
5
+ } from "../ChartLibrary/Utils/ChartColors";
3
6
  import React, { FunctionComponent, ReactElement, useEffect } from "react";
4
7
  import SeriesPoint from "../Types/SeriesPoints";
5
8
  import { XAxis } from "../Types/XAxis/XAxis";
@@ -29,6 +32,13 @@ export interface ComponentProps {
29
32
  heightInPx?: number | undefined;
30
33
  referenceLines?: Array<ChartReferenceLineProps> | undefined;
31
34
  showLegend?: boolean | undefined;
35
+ /*
36
+ * Optional per-series color override. Each entry may be a named palette key
37
+ * or a raw hex string. When provided (and non-empty), it replaces the
38
+ * default BarChartPalette so callers can assign custom colors; the array is
39
+ * indexed by series position (index % length), matching the default palette.
40
+ */
41
+ colors?: Array<ChartColorValue> | undefined;
32
42
  }
33
43
 
34
44
  export interface BarInternalProps extends ComponentProps {
@@ -71,7 +81,11 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
71
81
  tickGap={30}
72
82
  index={"Time"}
73
83
  categories={categories}
74
- colors={BarChartPalette}
84
+ colors={
85
+ props.colors && props.colors.length > 0
86
+ ? props.colors
87
+ : BarChartPalette
88
+ }
75
89
  valueFormatter={props.yAxis.options.formatter || undefined}
76
90
  showTooltip={true}
77
91
  showLegend={props.showLegend !== false}
@@ -26,10 +26,11 @@ import { AxisDomain } from "recharts/types/util/types";
26
26
  import { useOnWindowResize } from "../Utils/UseWindowOnResize";
27
27
  import {
28
28
  AvailableChartColors,
29
- AvailableChartColorsKeys,
29
+ ChartColorValue,
30
30
  constructCategoryColors,
31
31
  getColorClassName,
32
32
  getColorHex,
33
+ isHexColorValue,
33
34
  } from "../Utils/ChartColors";
34
35
  import { cx } from "../Utils/Cx";
35
36
  import { getYAxisDomain } from "../Utils/GetYAxisDomain";
@@ -40,8 +41,8 @@ import ChartCurve from "../../Types/ChartCurve";
40
41
 
41
42
  interface LegendItemProps {
42
43
  name: string;
43
- color: AvailableChartColorsKeys;
44
- onClick?: (name: string, color: AvailableChartColorsKeys) => void;
44
+ color: ChartColorValue;
45
+ onClick?: (name: string, color: ChartColorValue) => void;
45
46
  activeLegend?: string;
46
47
  }
47
48
 
@@ -76,6 +77,11 @@ const LegendItem: ({
76
77
  getColorClassName(color, "bg"),
77
78
  activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
78
79
  )}
80
+ style={
81
+ isHexColorValue(color)
82
+ ? { backgroundColor: getColorHex(color) }
83
+ : undefined
84
+ }
79
85
  aria-hidden={true}
80
86
  />
81
87
  <p
@@ -163,7 +169,7 @@ const ScrollButton: ({
163
169
 
164
170
  interface LegendProps extends React.OlHTMLAttributes<HTMLOListElement> {
165
171
  categories: string[];
166
- colors?: AvailableChartColorsKeys[];
172
+ colors?: ChartColorValue[];
167
173
  onClickLegendItem?: (category: string, color: string) => void;
168
174
  activeLegend?: string;
169
175
  enableLegendSlider?: boolean;
@@ -307,7 +313,7 @@ const Legend: React.ForwardRefExoticComponent<
307
313
  <LegendItem
308
314
  key={`item-${index}`}
309
315
  name={category}
310
- color={colors[index] as AvailableChartColorsKeys}
316
+ color={colors[index] as ChartColorValue}
311
317
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
312
318
  onClick={onClickLegendItem as any}
313
319
  activeLegend={activeLegend!}
@@ -356,7 +362,7 @@ type PayloadItem = {
356
362
  category: string;
357
363
  value: number;
358
364
  index: string;
359
- color: AvailableChartColorsKeys;
365
+ color: ChartColorValue;
360
366
  type?: string;
361
367
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
362
368
  payload: any;
@@ -366,7 +372,7 @@ type PayloadItem = {
366
372
  const ChartLegend: (
367
373
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
368
374
  { payload }: any,
369
- categoryColors: Map<string, AvailableChartColorsKeys>,
375
+ categoryColors: Map<string, ChartColorValue>,
370
376
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
371
377
  activeLegend: string | undefined,
372
378
  onClick?: (category: string, color: string) => void,
@@ -376,7 +382,7 @@ const ChartLegend: (
376
382
  ) => React.JSX.Element = (
377
383
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
378
384
  { payload }: any,
379
- categoryColors: Map<string, AvailableChartColorsKeys>,
385
+ categoryColors: Map<string, ChartColorValue>,
380
386
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
381
387
  activeLegend: string | undefined,
382
388
  onClick?: (category: string, color: string) => void,
@@ -485,6 +491,11 @@ const ChartTooltip: ({
485
491
  "h-[3px] w-3.5 shrink-0 rounded-full",
486
492
  getColorClassName(color, "bg"),
487
493
  )}
494
+ style={
495
+ isHexColorValue(color)
496
+ ? { backgroundColor: getColorHex(color) }
497
+ : undefined
498
+ }
488
499
  />
489
500
  <p
490
501
  className={cx(
@@ -534,7 +545,7 @@ interface AreaChartProps extends React.HTMLAttributes<HTMLDivElement> {
534
545
  data: Record<string, any>[];
535
546
  index: string;
536
547
  categories: string[];
537
- colors?: AvailableChartColorsKeys[];
548
+ colors?: ChartColorValue[];
538
549
  valueFormatter?: (value: number) => string;
539
550
  startEndOnly?: boolean;
540
551
  showXAxis?: boolean;
@@ -620,7 +631,7 @@ const AreaChart: React.ForwardRefExoticComponent<
620
631
  const [activeLegend, setActiveLegend] = React.useState<string | undefined>(
621
632
  undefined,
622
633
  );
623
- const categoryColors: Map<string, string | number> =
634
+ const categoryColors: Map<string, ChartColorValue> =
624
635
  constructCategoryColors(categories, colors);
625
636
 
626
637
  const yAxisDomain: (number | "auto")[] = getYAxisDomain(
@@ -716,9 +727,8 @@ const AreaChart: React.ForwardRefExoticComponent<
716
727
  >
717
728
  <defs>
718
729
  {categories.map((category: string, i: number) => {
719
- const colorKey: AvailableChartColorsKeys =
720
- (colors[i % colors.length] as AvailableChartColorsKeys) ||
721
- "blue";
730
+ const colorKey: ChartColorValue =
731
+ (colors[i % colors.length] as ChartColorValue) || "blue";
722
732
  const hex: string = getColorHex(colorKey);
723
733
  return (
724
734
  <linearGradient
@@ -833,7 +843,7 @@ const AreaChart: React.ForwardRefExoticComponent<
833
843
  index: item.payload[index],
834
844
  color: categoryColors.get(
835
845
  item.dataKey,
836
- ) as AvailableChartColorsKeys,
846
+ ) as ChartColorValue,
837
847
  type: item.type,
838
848
  payload: item.payload,
839
849
  };
@@ -921,10 +931,11 @@ const AreaChart: React.ForwardRefExoticComponent<
921
931
  ) : null}
922
932
  {categories.map((category: string) => {
923
933
  const gradientId: string = `gradient-${category.replace(/[^a-zA-Z0-9]/g, "_")}`;
924
- const colorKey: AvailableChartColorsKeys = categoryColors.get(
934
+ const colorKey: ChartColorValue = categoryColors.get(
925
935
  category,
926
- ) as AvailableChartColorsKeys;
936
+ ) as ChartColorValue;
927
937
  const hex: string = getColorHex(colorKey);
938
+ const isCustomColor: boolean = isHexColorValue(colorKey);
928
939
 
929
940
  return (
930
941
  <Area
@@ -954,24 +965,18 @@ const AreaChart: React.ForwardRefExoticComponent<
954
965
  strokeLinecap: slc,
955
966
  strokeLinejoin: slj,
956
967
  strokeWidth,
957
- dataKey,
958
968
  } = dotProps;
959
969
  return (
960
970
  <Dot
961
971
  className={cx(
962
972
  "stroke-white",
963
973
  onValueChange ? "cursor-pointer" : "",
964
- getColorClassName(
965
- categoryColors.get(
966
- dataKey,
967
- ) as AvailableChartColorsKeys,
968
- "fill",
969
- ),
974
+ getColorClassName(colorKey, "fill"),
970
975
  )}
971
976
  cx={cxCoord}
972
977
  cy={cyCoord}
973
978
  r={5}
974
- fill=""
979
+ fill={isCustomColor ? hex : ""}
975
980
  stroke={stroke}
976
981
  strokeLinecap={slc}
977
982
  strokeLinejoin={slj}
@@ -992,7 +997,6 @@ const AreaChart: React.ForwardRefExoticComponent<
992
997
  strokeWidth,
993
998
  cx: cxCoord,
994
999
  cy: cyCoord,
995
- dataKey,
996
1000
  index: dotIndex,
997
1001
  } = dotProps;
998
1002
 
@@ -1012,19 +1016,14 @@ const AreaChart: React.ForwardRefExoticComponent<
1012
1016
  cy={cyCoord}
1013
1017
  r={5}
1014
1018
  stroke={stroke}
1015
- fill=""
1019
+ fill={isCustomColor ? hex : ""}
1016
1020
  strokeLinecap={slc}
1017
1021
  strokeLinejoin={slj}
1018
1022
  strokeWidth={strokeWidth}
1019
1023
  className={cx(
1020
1024
  "stroke-white",
1021
1025
  onValueChange ? "cursor-pointer" : "",
1022
- getColorClassName(
1023
- categoryColors.get(
1024
- dataKey,
1025
- ) as AvailableChartColorsKeys,
1026
- "fill",
1027
- ),
1026
+ getColorClassName(colorKey, "fill"),
1028
1027
  )}
1029
1028
  />
1030
1029
  );
@@ -22,9 +22,11 @@ import type { AxisDomain } from "recharts/types/util/types";
22
22
 
23
23
  import {
24
24
  AvailableChartColors,
25
- type AvailableChartColorsKeys,
25
+ type ChartColorValue,
26
26
  constructCategoryColors,
27
27
  getColorClassName,
28
+ getColorHex,
29
+ isHexColorValue,
28
30
  } from "../Utils/ChartColors";
29
31
  import { cx } from "../Utils/Cx";
30
32
  import { getYAxisDomain } from "../Utils/GetYAxisDomain";
@@ -73,7 +75,7 @@ const renderShape: (
73
75
  activeLegend: string | undefined,
74
76
  layout: string,
75
77
  ): React.ReactElement => {
76
- const { fillOpacity, name, payload, value } = props;
78
+ const { fillOpacity, name, payload, value, fill } = props;
77
79
  let { x, width, y, height } = props;
78
80
 
79
81
  if (layout === "horizontal" && height < 0) {
@@ -120,6 +122,12 @@ const renderShape: (
120
122
  return (
121
123
  <path
122
124
  d={path}
125
+ /*
126
+ * A custom hex color is passed through as the Bar's `fill` prop; apply
127
+ * it directly on the path. For named palette colors `fill` is "" and the
128
+ * path inherits the Tailwind `fill-*` class set on the parent Bar.
129
+ */
130
+ {...(fill ? { fill } : {})}
123
131
  opacity={
124
132
  activeBar || (activeLegend && activeLegend !== name)
125
133
  ? deepEqual(activeBar, { ...payload, value })
@@ -135,7 +143,7 @@ const renderShape: (
135
143
 
136
144
  interface LegendItemProps {
137
145
  name: string;
138
- color: AvailableChartColorsKeys;
146
+ color: ChartColorValue;
139
147
  onClick?: (name: string, color: string) => void;
140
148
  activeLegend?: string;
141
149
  }
@@ -167,6 +175,11 @@ const LegendItem: React.FunctionComponent<LegendItemProps> = ({
167
175
  getColorClassName(color, "bg"),
168
176
  activeLegend && activeLegend !== name ? "opacity-40" : "opacity-100",
169
177
  )}
178
+ style={
179
+ isHexColorValue(color)
180
+ ? { backgroundColor: getColorHex(color) }
181
+ : undefined
182
+ }
170
183
  aria-hidden={true}
171
184
  />
172
185
  <p
@@ -252,7 +265,7 @@ const ScrollButton: React.FunctionComponent<ScrollButtonProps> = ({
252
265
 
253
266
  interface LegendProps extends React.OlHTMLAttributes<HTMLOListElement> {
254
267
  categories: string[];
255
- colors?: AvailableChartColorsKeys[];
268
+ colors?: ChartColorValue[];
256
269
  onClickLegendItem?: (category: string, color: string) => void;
257
270
  activeLegend?: string;
258
271
  enableLegendSlider?: boolean;
@@ -398,7 +411,7 @@ const Legend: React.ForwardRefExoticComponent<
398
411
  <LegendItem
399
412
  key={`item-${index}`}
400
413
  name={category}
401
- color={colors[index] as AvailableChartColorsKeys}
414
+ color={colors[index] as ChartColorValue}
402
415
  {...(onClickLegendItem ? { onClick: onClickLegendItem } : {})}
403
416
  {...(activeLegend ? { activeLegend: activeLegend } : {})}
404
417
  />
@@ -443,7 +456,7 @@ Legend.displayName = "Legend";
443
456
 
444
457
  const ChartLegend: (
445
458
  payload: any,
446
- categoryColors: Map<string, AvailableChartColorsKeys>,
459
+ categoryColors: Map<string, ChartColorValue>,
447
460
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
448
461
  activeLegend: string | undefined,
449
462
  onClick?: (category: string, color: string) => void,
@@ -452,7 +465,7 @@ const ChartLegend: (
452
465
  yAxisWidth?: number,
453
466
  ) => React.ReactElement = (
454
467
  { payload }: any,
455
- categoryColors: Map<string, AvailableChartColorsKeys>,
468
+ categoryColors: Map<string, ChartColorValue>,
456
469
  setLegendHeight: React.Dispatch<React.SetStateAction<number>>,
457
470
  activeLegend: string | undefined,
458
471
  onClick?: (category: string, color: string) => void,
@@ -497,7 +510,7 @@ const ChartLegend: (
497
510
  return entry.value;
498
511
  })}
499
512
  colors={filteredPayload.map((entry: any) => {
500
- return categoryColors.get(entry.value) as AvailableChartColorsKeys;
513
+ return categoryColors.get(entry.value) as ChartColorValue;
501
514
  })}
502
515
  {...(onClick ? { onClickLegendItem: onClick } : {})}
503
516
  {...(activeLegend ? { activeLegend: activeLegend } : {})}
@@ -518,7 +531,7 @@ type PayloadItem = {
518
531
  category: string; // eslint-disable-line react/no-unused-prop-types
519
532
  value: number; // eslint-disable-line react/no-unused-prop-types
520
533
  index: string; // eslint-disable-line react/no-unused-prop-types
521
- color: AvailableChartColorsKeys; // eslint-disable-line react/no-unused-prop-types
534
+ color: ChartColorValue; // eslint-disable-line react/no-unused-prop-types
522
535
  type?: string; // eslint-disable-line react/no-unused-prop-types
523
536
  payload: any;
524
537
  };
@@ -575,6 +588,11 @@ const ChartTooltip: React.FunctionComponent<ChartTooltipProps> = ({
575
588
  "size-2 shrink-0 rounded-xs",
576
589
  getColorClassName(color, "bg"),
577
590
  )}
591
+ style={
592
+ isHexColorValue(color)
593
+ ? { backgroundColor: getColorHex(color) }
594
+ : undefined
595
+ }
578
596
  />
579
597
  <p
580
598
  className={cx(
@@ -622,7 +640,7 @@ interface BarChartProps extends React.HTMLAttributes<HTMLDivElement> {
622
640
  data: Record<string, any>[];
623
641
  index: string;
624
642
  categories: string[];
625
- colors?: AvailableChartColorsKeys[];
643
+ colors?: ChartColorValue[];
626
644
  valueFormatter?: (value: number) => string;
627
645
  startEndOnly?: boolean;
628
646
  showXAxis?: boolean;
@@ -699,7 +717,7 @@ const BarChart: React.ForwardRefExoticComponent<
699
717
  const [activeLegend, setActiveLegend] = React.useState<string | undefined>(
700
718
  undefined,
701
719
  );
702
- const categoryColors: Map<string, AvailableChartColorsKeys> =
720
+ const categoryColors: Map<string, ChartColorValue> =
703
721
  constructCategoryColors(categories, colors);
704
722
  const [activeBar, setActiveBar] = React.useState<any | undefined>(
705
723
  undefined,
@@ -942,7 +960,7 @@ const BarChart: React.ForwardRefExoticComponent<
942
960
  index: item.payload[index],
943
961
  color: categoryColors.get(
944
962
  item.dataKey,
945
- ) as AvailableChartColorsKeys,
963
+ ) as ChartColorValue,
946
964
  type: item.type,
947
965
  payload: item.payload,
948
966
  };
@@ -1000,13 +1018,14 @@ const BarChart: React.ForwardRefExoticComponent<
1000
1018
  />
1001
1019
  ) : null}
1002
1020
  {categories.map((category: string) => {
1021
+ const barColor: ChartColorValue = categoryColors.get(
1022
+ category,
1023
+ ) as ChartColorValue;
1024
+ const isCustomColor: boolean = isHexColorValue(barColor);
1003
1025
  return (
1004
1026
  <Bar
1005
1027
  className={cx(
1006
- getColorClassName(
1007
- categoryColors.get(category) as AvailableChartColorsKeys,
1008
- "fill",
1009
- ),
1028
+ getColorClassName(barColor, "fill"),
1010
1029
  onValueChange ? "cursor-pointer" : "",
1011
1030
  )}
1012
1031
  key={category}
@@ -1015,7 +1034,12 @@ const BarChart: React.ForwardRefExoticComponent<
1015
1034
  dataKey={category}
1016
1035
  {...(stacked ? { stackId: "stack" } : {})}
1017
1036
  isAnimationActive={false}
1018
- fill=""
1037
+ /*
1038
+ * Custom hex → apply the color inline (Tailwind has no class
1039
+ * for arbitrary user hex); named palette → keep "" so the
1040
+ * `fill-*` class on the Bar cascades to the shape path.
1041
+ */
1042
+ fill={isCustomColor ? getColorHex(barColor) : ""}
1019
1043
  shape={shapeRenderer}
1020
1044
  onClick={onBarClick}
1021
1045
  />