sag_components 2.0.0-beta52 → 2.0.0-beta54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
2
2
  import styled, { keyframes, css } from 'styled-components';
3
- import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip as Tooltip$2, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Bar, LabelList, ReferenceLine, Brush, LineChart, Line, AreaChart as AreaChart$1, Legend, Area } from 'recharts';
3
+ import { ResponsiveContainer, PieChart as PieChart$1, Pie, Cell, Tooltip as Tooltip$2, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Bar, LabelList, ReferenceLine, Brush, LineChart, Line, AreaChart as AreaChart$1, Legend, Area, ScatterChart, ZAxis, Scatter, ComposedChart } from 'recharts';
4
4
  import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
5
5
  import { motion, AnimatePresence } from 'framer-motion';
6
6
 
@@ -1902,7 +1902,7 @@ const ControlsContainer$a = styled.div`
1902
1902
  box-sizing: border-box;
1903
1903
  }
1904
1904
  `;
1905
- const Controls$8 = styled.div`
1905
+ const Controls$9 = styled.div`
1906
1906
  display: flex;
1907
1907
  flex-direction: column;
1908
1908
  width: 100%;
@@ -2026,7 +2026,7 @@ const ControlsContainer$9 = styled.div`
2026
2026
  //border: 1px solid red;
2027
2027
  align-items: center;
2028
2028
  `;
2029
- const Controls$7 = styled.div`
2029
+ const Controls$8 = styled.div`
2030
2030
  position: relative;
2031
2031
  border-radius: 100px;
2032
2032
  width: ${props => props.width.toString().concat('', 'px')};
@@ -2282,7 +2282,7 @@ const Benchmark = props => {
2282
2282
  id: "Tooltip",
2283
2283
  content: getTooltipText(),
2284
2284
  direction: tooltipDirection
2285
- }, /*#__PURE__*/React$1.createElement(Controls$7, {
2285
+ }, /*#__PURE__*/React$1.createElement(Controls$8, {
2286
2286
  id: "ControlsBenchmark",
2287
2287
  height: height,
2288
2288
  width: width
@@ -2407,7 +2407,7 @@ const PieChart = props => {
2407
2407
  }, legendData.length === 0 || legendData.every(item => item.value === undefined || item.value === null) ? /*#__PURE__*/React$1.createElement(NoDataFoundMessage, {
2408
2408
  className: "NoDataFoundMessage",
2409
2409
  noDataText: noDataText
2410
- }) : /*#__PURE__*/React$1.createElement(Controls$8, {
2410
+ }) : /*#__PURE__*/React$1.createElement(Controls$9, {
2411
2411
  className: "Controls",
2412
2412
  height: height,
2413
2413
  width: width
@@ -10037,6 +10037,7 @@ const RangePicker = _ref => {
10037
10037
  setValue(e.target.value);
10038
10038
  };
10039
10039
  const toggleDatePicker = () => {
10040
+ if (disabled) return;
10040
10041
  setIsOpen(!isOpen);
10041
10042
  };
10042
10043
  const handleFocus = () => {
@@ -24103,22 +24104,21 @@ const DeleteIcon = styled.div`
24103
24104
  position: absolute;
24104
24105
  `;
24105
24106
 
24106
- const QuickFilterDropdownSingle = _ref => {
24107
- let {
24108
- label,
24109
- hoverColor,
24110
- options,
24111
- selectedValue,
24112
- placeHolder,
24113
- onChange,
24114
- disabled,
24115
- width,
24116
- error,
24117
- errorMessage,
24118
- xIconShow,
24119
- labelColor,
24120
- showLabelOnTop
24121
- } = _ref;
24107
+ const QuickFilterDropdownSingle = ({
24108
+ label,
24109
+ hoverColor,
24110
+ options,
24111
+ selectedValue,
24112
+ placeHolder,
24113
+ onChange,
24114
+ disabled,
24115
+ width,
24116
+ error,
24117
+ errorMessage,
24118
+ xIconShow,
24119
+ labelColor,
24120
+ showLabelOnTop
24121
+ }) => {
24122
24122
  const [isFocused, setIsFocused] = useState(false);
24123
24123
  const [showOptions, setShowOptions] = useState(false);
24124
24124
  const [inputValue, setInputValue] = useState("");
@@ -24516,24 +24516,23 @@ const IconContainer$2 = styled.div`
24516
24516
  cursor: pointer;
24517
24517
  `;
24518
24518
 
24519
- const QuickFilterDropdownMultiSelection = _ref => {
24520
- let {
24521
- label,
24522
- labelEmptyValue,
24523
- options,
24524
- selectedValue,
24525
- placeHolder,
24526
- onChange,
24527
- required,
24528
- disabled,
24529
- width,
24530
- error,
24531
- errorMessage,
24532
- labelColor,
24533
- xIconShow,
24534
- checkBoxColor,
24535
- showLabelOnTop
24536
- } = _ref;
24519
+ const QuickFilterDropdownMultiSelection = ({
24520
+ label,
24521
+ labelEmptyValue,
24522
+ options,
24523
+ selectedValue,
24524
+ placeHolder,
24525
+ onChange,
24526
+ required,
24527
+ disabled,
24528
+ width,
24529
+ error,
24530
+ errorMessage,
24531
+ labelColor,
24532
+ xIconShow,
24533
+ checkBoxColor,
24534
+ showLabelOnTop
24535
+ }) => {
24537
24536
  const [isFocused, setIsFocused] = useState(false);
24538
24537
  const [showOptions, setShowOptions] = useState(false);
24539
24538
  const [inputValue, setInputValue] = useState('');
@@ -25147,8 +25146,8 @@ function styleInject(css, ref) {
25147
25146
  }
25148
25147
  }
25149
25148
 
25150
- var css_248z = "@keyframes react-loading-skeleton {\n 100% {\n transform: translateX(100%);\n }\n}\n\n.react-loading-skeleton {\n --base-color: #ebebeb;\n --highlight-color: #f5f5f5;\n --animation-duration: 1.5s;\n --animation-direction: normal;\n --pseudo-element-display: block; /* Enable animation */\n\n background-color: var(--base-color);\n\n width: 100%;\n border-radius: 0.25rem;\n display: inline-flex;\n line-height: 1;\n\n position: relative;\n user-select: none;\n overflow: hidden;\n}\n\n.react-loading-skeleton::after {\n content: ' ';\n display: var(--pseudo-element-display);\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 100%;\n background-repeat: no-repeat;\n background-image: var(\n --custom-highlight-background,\n linear-gradient(\n 90deg,\n var(--base-color) 0%,\n var(--highlight-color) 50%,\n var(--base-color) 100%\n )\n );\n transform: translateX(-100%);\n\n animation-name: react-loading-skeleton;\n animation-direction: var(--animation-direction);\n animation-duration: var(--animation-duration);\n animation-timing-function: ease-in-out;\n animation-iteration-count: infinite;\n}\n\n@media (prefers-reduced-motion) {\n .react-loading-skeleton {\n --pseudo-element-display: none; /* Disable animation */\n }\n}\n";
25151
- styleInject(css_248z);
25149
+ var css_248z$1 = "@keyframes react-loading-skeleton {\n 100% {\n transform: translateX(100%);\n }\n}\n\n.react-loading-skeleton {\n --base-color: #ebebeb;\n --highlight-color: #f5f5f5;\n --animation-duration: 1.5s;\n --animation-direction: normal;\n --pseudo-element-display: block; /* Enable animation */\n\n background-color: var(--base-color);\n\n width: 100%;\n border-radius: 0.25rem;\n display: inline-flex;\n line-height: 1;\n\n position: relative;\n user-select: none;\n overflow: hidden;\n}\n\n.react-loading-skeleton::after {\n content: ' ';\n display: var(--pseudo-element-display);\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 100%;\n background-repeat: no-repeat;\n background-image: var(\n --custom-highlight-background,\n linear-gradient(\n 90deg,\n var(--base-color) 0%,\n var(--highlight-color) 50%,\n var(--base-color) 100%\n )\n );\n transform: translateX(-100%);\n\n animation-name: react-loading-skeleton;\n animation-direction: var(--animation-direction);\n animation-duration: var(--animation-duration);\n animation-timing-function: ease-in-out;\n animation-iteration-count: infinite;\n}\n\n@media (prefers-reduced-motion) {\n .react-loading-skeleton {\n --pseudo-element-display: none; /* Disable animation */\n }\n}\n";
25150
+ styleInject(css_248z$1);
25152
25151
 
25153
25152
  const OneColumnContainer = props => {
25154
25153
  const {
@@ -25345,7 +25344,7 @@ const ControlsContainer$7 = styled.div`
25345
25344
  box-sizing: border-box;
25346
25345
  }
25347
25346
  `;
25348
- const Controls$6 = styled.div`
25347
+ const Controls$7 = styled.div`
25349
25348
  display: flex;
25350
25349
  flex-direction: column;
25351
25350
  width: 100%;
@@ -25413,7 +25412,7 @@ const FormattedValue$3 = props => {
25413
25412
  height: height,
25414
25413
  width: width,
25415
25414
  textcolor: textcolor
25416
- }, /*#__PURE__*/React$1.createElement(Controls$6, {
25415
+ }, /*#__PURE__*/React$1.createElement(Controls$7, {
25417
25416
  className: "Controls",
25418
25417
  height: height,
25419
25418
  width: width
@@ -25473,7 +25472,7 @@ const ControlsContainer$6 = styled.div`
25473
25472
  height: ${props => props.height};
25474
25473
  min-width: 250px;
25475
25474
  `;
25476
- const Controls$5 = styled.div`
25475
+ const Controls$6 = styled.div`
25477
25476
  height: 100%;
25478
25477
  width: 100%;
25479
25478
  background: white;
@@ -25606,7 +25605,7 @@ const ControlsContainer$5 = styled.div`
25606
25605
  box-sizing: border-box;
25607
25606
  }
25608
25607
  `;
25609
- const Controls$4 = styled.div`
25608
+ const Controls$5 = styled.div`
25610
25609
  display: flex;
25611
25610
  gap: 20px;
25612
25611
  flex-direction: column;
@@ -25669,7 +25668,7 @@ const PerformanceAnalyticsLegend = props => {
25669
25668
  className: className,
25670
25669
  height: height,
25671
25670
  width: width
25672
- }, legendData?.length > 0 ? /*#__PURE__*/React$1.createElement(Controls$4, {
25671
+ }, legendData?.length > 0 ? /*#__PURE__*/React$1.createElement(Controls$5, {
25673
25672
  height: height,
25674
25673
  width: width
25675
25674
  }, /*#__PURE__*/React$1.createElement(LegendDataContainer, {
@@ -25837,7 +25836,7 @@ const BarChartsByWeeks = props => {
25837
25836
  height: height,
25838
25837
  width: width,
25839
25838
  ref: controlsContainerRef
25840
- }, /*#__PURE__*/React$1.createElement(Controls$5, {
25839
+ }, /*#__PURE__*/React$1.createElement(Controls$6, {
25841
25840
  height: getControlsHeight()
25842
25841
  }, showTitle && /*#__PURE__*/React$1.createElement(TitleAndValueContainer$2, null, /*#__PURE__*/React$1.createElement(Title$b, null, title), /*#__PURE__*/React$1.createElement(FormattedValue$3, {
25843
25842
  title: headerValueTopTitle,
@@ -26043,7 +26042,7 @@ const ControlsContainer$4 = styled.div`
26043
26042
  box-sizing: border-box;
26044
26043
  }
26045
26044
  `;
26046
- const Controls$3 = styled.div`
26045
+ const Controls$4 = styled.div`
26047
26046
  display: flex;
26048
26047
  flex-direction: column;
26049
26048
  width: 100%;
@@ -26269,7 +26268,7 @@ const TotalDoughnutChart = props => {
26269
26268
  }, legendData.length === 0 || legendData.every(item => item.value === undefined || item.value === null) ? /*#__PURE__*/React$1.createElement(NoDataFoundMessage, {
26270
26269
  className: "NoDataFoundMessage",
26271
26270
  noDataText: noDataText
26272
- }) : /*#__PURE__*/React$1.createElement(Controls$3, {
26271
+ }) : /*#__PURE__*/React$1.createElement(Controls$4, {
26273
26272
  className: "Controls",
26274
26273
  height: height,
26275
26274
  width: width
@@ -28521,7 +28520,7 @@ const ControlsContainer$1 = styled.div`
28521
28520
  height: ${props => props.height};
28522
28521
  min-width: 250px;
28523
28522
  `;
28524
- const Controls$2 = styled.div`
28523
+ const Controls$3 = styled.div`
28525
28524
  height: 100%;
28526
28525
  width: 100%;
28527
28526
  background: white;
@@ -28706,7 +28705,7 @@ const BarChart = props => {
28706
28705
  height: height,
28707
28706
  width: width,
28708
28707
  ref: controlsContainerRef
28709
- }, /*#__PURE__*/React$1.createElement(Controls$2, null, /*#__PURE__*/React$1.createElement(Title$3, null, title), /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
28708
+ }, /*#__PURE__*/React$1.createElement(Controls$3, null, /*#__PURE__*/React$1.createElement(Title$3, null, title), /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
28710
28709
  width: "100%",
28711
28710
  height: 400
28712
28711
  }, /*#__PURE__*/React$1.createElement(BarChart$1, {
@@ -28870,7 +28869,7 @@ const LegendContainer = styled.div`
28870
28869
  width: ${props => `${props.width}px`};
28871
28870
  bottom: 20px;
28872
28871
  `;
28873
- const Controls$1 = styled.div`
28872
+ const Controls$2 = styled.div`
28874
28873
  /* height: calc(100% - 30px); */
28875
28874
  height: 100%;
28876
28875
  display: flex;
@@ -29134,7 +29133,7 @@ const DoubleBarSingleLine = props => {
29134
29133
  hasScroll: hasScroll
29135
29134
  }, data.length === 0 ? /*#__PURE__*/React$1.createElement(NoDataFoundMessage, {
29136
29135
  noDataText: noDataText
29137
- }) : /*#__PURE__*/React$1.createElement(Controls$1, {
29136
+ }) : /*#__PURE__*/React$1.createElement(Controls$2, {
29138
29137
  className: "Controls"
29139
29138
  }, title && title.trim() !== '' && /*#__PURE__*/React$1.createElement(Title$2, null, title), /*#__PURE__*/React$1.createElement(ChartsWrapper, {
29140
29139
  width: hasScroll ? `${data.length * 178}px` : 'auto'
@@ -29224,7 +29223,7 @@ const AreaChartContaner = styled.div`
29224
29223
  height: ${props => props.height};
29225
29224
  min-width: 1100px;
29226
29225
  `;
29227
- const HeaderContainer$1 = styled.div`
29226
+ const HeaderContainer$2 = styled.div`
29228
29227
  display: flex;
29229
29228
  justify-content: space-between;
29230
29229
  padding: 10px 40px;
@@ -29283,7 +29282,7 @@ const ControlsContainer = styled.div`
29283
29282
  font-family: Poppins;
29284
29283
  margin: 0;
29285
29284
  `;
29286
- const Controls = styled.div`
29285
+ const Controls$1 = styled.div`
29287
29286
  display: flex;
29288
29287
  align-items: center;
29289
29288
  `;
@@ -29329,7 +29328,7 @@ const CheckBox = props => {
29329
29328
  fontSize: fontSize,
29330
29329
  width: width,
29331
29330
  height: height
29332
- }, /*#__PURE__*/React$1.createElement(Controls, {
29331
+ }, /*#__PURE__*/React$1.createElement(Controls$1, {
29333
29332
  className: "Controls",
29334
29333
  "data-testid": "controls"
29335
29334
  }, checkedState ? /*#__PURE__*/React$1.createElement(CheckBoxIconContainer, {
@@ -29561,7 +29560,7 @@ const AreaChart = props => {
29561
29560
  noDataText: "",
29562
29561
  width: width,
29563
29562
  height: height
29564
- }) : /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement(HeaderContainer$1, {
29563
+ }) : /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement(HeaderContainer$2, {
29565
29564
  "data-testid": "header-container"
29566
29565
  }, /*#__PURE__*/React$1.createElement(Title$1, {
29567
29566
  "data-testid": "title"
@@ -30055,7 +30054,7 @@ const BreakdownPanelContainer = styled.div`
30055
30054
  height: ${props => props.height};
30056
30055
  min-width: 1100px;
30057
30056
  `;
30058
- const HeaderContainer = styled.div`
30057
+ const HeaderContainer$1 = styled.div`
30059
30058
  display: flex;
30060
30059
  justify-content: space-between;
30061
30060
  align-items: center;
@@ -30128,7 +30127,7 @@ const BreakdownPanel = props => {
30128
30127
  noDataText: "",
30129
30128
  width: width,
30130
30129
  height: height
30131
- }) : /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement(HeaderContainer, {
30130
+ }) : /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement(HeaderContainer$1, {
30132
30131
  "data-testid": "header-container"
30133
30132
  }, /*#__PURE__*/React$1.createElement(Title, {
30134
30133
  "data-testid": "title"
@@ -30154,264 +30153,2070 @@ const BreakdownPanel = props => {
30154
30153
  }))))));
30155
30154
  };
30156
30155
 
30157
- const BatteryChartContainer = styled.div`
30158
- position: relative;
30159
- font-family: "Poppins", sans-serif;
30160
- display: grid;
30161
- grid-template-columns: auto auto;
30162
- grid-template-rows: auto 1fr auto;
30163
- color: #212121;
30164
- background-color: ${props => props.backgroundColor || 'transparent'};
30165
- padding: ${props => props.containerPadding || '25px 30px 0'};
30166
- width: ${props => props.width};
30167
- height: ${props => props.height};
30168
- `;
30169
- const PanelSide = styled.div`
30170
- display: flex;
30171
- flex-direction: column;
30172
- justify-content: space-between;
30156
+ const BubbleChartContainer = styled.div`
30157
+ position: relative;
30158
+ width: ${props => props.width};
30159
+ height: ${props => props.height};
30160
+ display: flex;
30161
+ flex-direction: column;
30162
+ background-color: ${props => props.backgroundColor};
30163
+ border-radius: 8px;
30164
+ padding: 10px;
30165
+ box-sizing: border-box;
30166
+ font-family: "Poppins", sans-serif;
30167
+ min-width: 800px;
30168
+
30169
+ /* Enable proper overflow handling */
30170
+ overflow: hidden;
30173
30171
  `;
30174
- const ProgressDetails = styled.div`
30175
- display: flex;
30176
- flex-direction: column;
30177
- flex-grow: 1;
30172
+ styled.div`
30173
+ position: absolute;
30174
+ font-family: "Poppins", sans-serif;
30175
+ font-size: 12px;
30176
+ font-weight: 500;
30177
+ color: #555;
30178
+ text-align: center;
30179
+
30180
+ &.top-left {
30181
+ top: 20px;
30182
+ left: 20px;
30183
+ }
30184
+
30185
+ &.top-right {
30186
+ top: 20px;
30187
+ right: 20px;
30188
+ }
30189
+
30190
+ &.bottom-left {
30191
+ bottom: 20px;
30192
+ left: 20px;
30193
+ }
30194
+
30195
+ &.bottom-right {
30196
+ bottom: 20px;
30197
+ right: 20px;
30198
+ }
30178
30199
  `;
30179
- const HeadingText = styled.p`
30180
- grid-column: 1 / span 2;
30181
- color: #212121;
30182
- font-size: 18px;
30183
- font-weight: 400;
30184
- padding-left: 10px;
30200
+ const CustomTooltipContainer = styled.div`
30201
+ background-color: white;
30202
+ border-radius: 6px;
30203
+ z-index: 1000;
30204
+ padding: 10px 14px;
30205
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
30206
+ max-width: 220px;
30207
+ font-family: "Poppins", sans-serif;
30208
+
30209
+ h4 {
30185
30210
  margin: 0 0 8px;
30211
+ font-size: 14px;
30212
+ font-weight: 600;
30213
+ }
30214
+
30215
+ p {
30216
+ margin: 3px 0;
30217
+ font-size: 12px;
30218
+ color: #555;
30219
+ }
30220
+
30221
+ @media (max-width: 768px) {
30222
+ padding: 8px 12px;
30223
+ max-width: 180px;
30224
+
30225
+ h4 {
30226
+ font-size: 12px;
30227
+ margin: 0 0 6px;
30228
+ }
30229
+
30230
+ p {
30231
+ font-size: 10px;
30232
+ }
30233
+ }
30186
30234
  `;
30187
- const HeadingPercentage = styled.p`
30188
- color: #212121;
30189
- font-size: 32px;
30235
+ const TooltipMetric = styled.div`
30236
+ display: flex;
30237
+ justify-content: space-between;
30238
+ margin: 5px 0;
30239
+ font-size: 12px;
30240
+
30241
+ span:last-child {
30190
30242
  font-weight: 500;
30191
- padding-left: 10px;
30192
- margin: 0;
30193
- `;
30194
- const ChartSide = styled.div`
30195
- text-align: center;
30196
- `;
30197
- const ChartWrapper = styled.div`
30198
- position: relative;
30199
- width: 110px;
30200
- `;
30201
- const GoalValue = styled.div`
30202
- text-align: center;
30203
- font-size: 18px;
30204
- color: #8B8989;
30205
- line-height: 1;
30206
- &.overload {
30207
- position: absolute;
30208
- top: ${props => props.goalValuePosition}px;
30209
- left: 0;
30210
- right: 0;
30211
- z-index: 2;
30212
- margin: 0 auto;
30213
- width: 40%;
30214
- display: inline;
30215
- padding: 0px 8px;
30216
- border: 2px solid #F2F2F2;
30217
- border-bottom: none;
30218
- border-radius: 6px 6px 0px 0px;
30219
- background-color: white;
30220
- }
30243
+ }
30244
+
30245
+ @media (max-width: 768px) {
30246
+ font-size: 10px;
30247
+ margin: 4px 0;
30248
+ }
30221
30249
  `;
30222
- const Indicator = styled.div`
30223
- position: absolute;
30224
- top: ${props => props.top}px;
30225
- left: 0;
30226
- transform: translateY(50%);
30227
- width: calc(100% + 60px);
30228
- display: flex;
30229
- gap: 6px;
30230
- justify-content: center;
30231
- padding-left: 4px;
30232
- align-items: center;
30233
- z-index: 1;
30250
+ styled.div`
30251
+ position: relative;
30252
+ width: 100%;
30253
+ height: 100%;
30234
30254
  `;
30235
- const IndicatorLine = styled.div`
30236
- width: 100%;
30237
- height: 0;
30238
- border-top: 4px dashed ${props => props.color};
30255
+ const HeaderContainer = styled.div`
30256
+ display: flex;
30257
+ justify-content: space-between;
30258
+ align-items: baseline;
30259
+ padding: 0;
30260
+ margin-bottom: 10px;
30239
30261
  `;
30240
- const IndicatorText = styled.span`
30241
- font-size: 18px;
30242
- font-weight: 400;
30243
- color: ${props => props.color};
30262
+ const ChartTitle = styled.h2`
30263
+ font-family: "Poppins", sans-serif;
30264
+ font-size: 18px;
30265
+ font-weight: 400;
30266
+ margin: 0;
30267
+ color: #212121;
30244
30268
  `;
30245
- const ProgressLegend = styled.div`
30246
- margin-top: 12px;
30269
+ const ChartSubtitle = styled.p`
30270
+ font-family: "Poppins", sans-serif;
30271
+ font-size: 14px;
30272
+ font-weight: 500;
30273
+ color: #484A4C;
30274
+ margin: 0;
30247
30275
  `;
30248
- const LegendItem = styled.div`
30249
- display: flex;
30250
- gap: 4px;
30251
- align-items: center;
30252
- margin-bottom: 12px;
30276
+ const ChartContainer = styled.div`
30277
+ position: relative;
30278
+ width: 100%;
30279
+ height: 90%; /* Reduced from 93% to make room for the zoom controls */
30280
+ flex: 1;
30253
30281
  `;
30254
- const LegendText = styled.span`
30255
- color: #8B8989;
30256
- font-size: 14px;
30282
+
30283
+ /* Updated Zoom Controls Styles to match the new design */
30284
+ const ZoomControlsContainer = styled.div`
30285
+ display: flex;
30286
+ align-items: center;
30287
+ align-self: flex-start;
30288
+ margin-top: 10px;
30289
+ background: linear-gradient(to right, rgba(255, 255, 255, 0.98), rgba(245, 245, 245, 0.92));
30290
+ border-radius: 8px;
30291
+ padding: 6px 6px 6px 12px;
30292
+ box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.1);
30293
+ z-index: 10;
30294
+ font-family: "Poppins", sans-serif;
30295
+ backdrop-filter: blur(8px);
30296
+ transition: background-color 0.3s ease, box-shadow 0.3s ease;
30257
30297
  `;
30258
- const LegendCube = styled.div`
30259
- border-radius: 3px;
30260
- width: 17px;
30261
- height: 17px;
30262
- background-color: ${props => props.color};
30298
+ const ZoomPercentage = styled.span`
30299
+ font-size: 18px;
30300
+ font-weight: 500;
30301
+ color: #726F6F;
30302
+ margin: 0;
30303
+ min-width: 50px;
30263
30304
  `;
30264
- const ProgressDescription = styled.div`
30265
- color: #8B8989;
30266
- font-size: 12px;
30267
- font-weight: 400;
30268
- margin-top: auto;
30269
- `;
30270
- const ProgressItem = styled.p`
30271
- margin: 0 0 6px;
30272
- `;
30273
- const StarText = styled.span`
30274
- width: 100%;
30275
- color: #B1B1B1;
30276
- font-size: 9px;
30277
- grid-column: 1 / span 2;
30278
- padding-top: 10px;
30305
+ const ZoomButton = styled.button`
30306
+ width: 26px;
30307
+ height: 20px;
30308
+ border: none;
30309
+ background: none;
30310
+ font-size: 24px;
30311
+ font-weight: 500;
30312
+ color: #726F6F;
30313
+ cursor: pointer;
30314
+ display: flex;
30315
+ align-items: center;
30316
+ justify-content: center;
30317
+ padding: 0;
30318
+ margin: 0 ;
30319
+
30320
+ &:hover {
30321
+ color: #333;
30322
+ }
30323
+
30324
+ &:focus {
30325
+ outline: none;
30326
+ }
30279
30327
  `;
30328
+ const ZoomResetButton = styled.button`
30329
+ font-family: "Poppins", sans-serif;
30330
+ font-size: 14px;
30331
+ font-weight: 400;
30332
+ height: 32px;
30333
+ padding: 0 12px;
30334
+ margin-left: 5px;
30335
+ border-radius: 8px;
30336
+ background-color: white;
30337
+ border: 1px solid #ddd;
30338
+ cursor: pointer;
30339
+ color: #212121;
30340
+ background: linear-gradient(to right, rgba(255, 255, 255, 0.98), rgba(245, 245, 245, 0.92));
30341
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
30342
+ transition: all 0.2s ease-in-out;
30280
30343
 
30281
- const BatteryChart = props => {
30344
+ &:hover {
30345
+ background: linear-gradient(to right, #f0f0f0, #eaeaea);
30346
+ border-color: #ccc;
30347
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
30348
+ }
30349
+
30350
+ &:active {
30351
+ background: #e0e0e0;
30352
+ transform: scale(0.98);
30353
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
30354
+ }
30355
+
30356
+ &:focus {
30357
+ outline: none;
30358
+ box-shadow: 0 0 0 3px rgba(100, 150, 250, 0.4);
30359
+ }
30360
+ `;
30361
+
30362
+ // Enhanced color palette with bubble-like transparency and gradients
30363
+ const defaultColorPalette = [
30364
+ // Top Left - Yellow Bubbles
30365
+ 'rgba(255, 249, 220, 0.7)',
30366
+ // Inner highlight
30367
+ 'rgba(255, 235, 165, 0.4)',
30368
+ // Outer edge
30369
+
30370
+ // Top Right - Green Bubbles
30371
+ 'rgba(235, 255, 240, 0.7)',
30372
+ // Inner highlight
30373
+ 'rgba(190, 235, 200, 0.4)',
30374
+ // Outer edge
30375
+
30376
+ // Bottom Left - Pink Bubbles
30377
+ 'rgba(255, 235, 240, 0.7)',
30378
+ // Inner highlight
30379
+ 'rgba(255, 190, 200, 0.4)',
30380
+ // Outer edge
30381
+
30382
+ // Bottom Right - Cream/Yellow Bubbles
30383
+ 'rgba(255, 250, 230, 0.7)',
30384
+ // Inner highlight
30385
+ 'rgba(255, 235, 180, 0.4)' // Outer edge
30386
+ ];
30387
+
30388
+ // Main component
30389
+ const BubbleChart = _ref => {
30390
+ let {
30391
+ data = [],
30392
+ title = 'Event Performance Based on Incrementality',
30393
+ subtitle = '* Total cost is reflected by the circle size',
30394
+ leftHeader = 'Low INC Sales ROI',
30395
+ rightHeader = 'High INC Sales ROI',
30396
+ topHeader = 'High INC Sales',
30397
+ bottomHeader = 'Low INC Sales',
30398
+ colorPalette = defaultColorPalette,
30399
+ height = '600px',
30400
+ width = '100%',
30401
+ backgroundColor = 'white',
30402
+ showAxis = false
30403
+ } = _ref;
30404
+ // Calculate the medians and domain for raw X, Y, and Z values
30282
30405
  const {
30283
- className,
30284
- containerPadding,
30285
- width,
30286
- height,
30287
- title,
30288
- color,
30289
- backgroundColor,
30290
- indicatorColor,
30291
- noCommitment,
30292
- goalAmount,
30293
- currentAmount,
30294
- ask,
30295
- target,
30296
- totalSegmentsLines,
30297
- starText
30298
- } = props;
30299
- const [top, setTop] = useState(0);
30300
- const [goalValuePosition, setGoalValuePosition] = useState(0);
30301
- const [containerHeight, setContainerHeight] = useState(0); // State to store container height
30302
- const containerRef = useRef(null); // Ref for BatteryChartContainer
30406
+ xMedian,
30407
+ yMedian,
30408
+ xDomain,
30409
+ yDomain,
30410
+ zRange
30411
+ } = useMemo(() => {
30412
+ if (!data || !Array.isArray(data) || data.length === 0) {
30413
+ return {
30414
+ xMedian: 0,
30415
+ yMedian: 0,
30416
+ xDomain: [0, 100],
30417
+ yDomain: [0, 100],
30418
+ zRange: [0, 3600]
30419
+ };
30420
+ }
30303
30421
 
30304
- // Calculate the percentage and completed segments
30305
- const percentage = Math.round(currentAmount / goalAmount * 100);
30306
- const completedSegments = Math.min(Math.floor(currentAmount / goalAmount * totalSegmentsLines), totalSegmentsLines);
30422
+ // Extract x, y, and size values
30423
+ const xValues = data.map(item => item.x).filter(x => x !== undefined && x !== null);
30424
+ const yValues = data.map(item => item.y).filter(y => y !== undefined && y !== null);
30425
+ const sizeValues = data.map(item => item.size).filter(size => size !== undefined && size !== null);
30426
+
30427
+ // Calculate min and max values
30428
+ const minX = Math.min(...xValues);
30429
+ const maxX = Math.max(...xValues);
30430
+ const minY = Math.min(...yValues);
30431
+ const maxY = Math.max(...yValues);
30432
+ Math.min(...sizeValues);
30433
+ const maxSize = Math.max(...sizeValues);
30434
+
30435
+ // Calculate medians
30436
+ const calculateMedian = arr => {
30437
+ if (!arr || arr.length === 0) return 0;
30438
+ const sorted = [...arr].sort((a, b) => a - b);
30439
+ const middle = Math.floor(sorted.length / 2);
30440
+ if (sorted.length % 2 === 1) {
30441
+ return sorted[middle];
30442
+ }
30443
+ return (sorted[middle - 1] + sorted[middle]) / 2;
30444
+ };
30445
+ const medianX = calculateMedian(xValues);
30446
+ const medianY = calculateMedian(yValues);
30307
30447
 
30308
- // Dynamically create the data for the bar chart
30309
- let barsChips = Array.from({
30310
- length: totalSegmentsLines
30311
- }).map((_, index) => ({
30312
- name: `segment-${index}`,
30313
- value: 100,
30314
- isCompleted: index >= totalSegmentsLines - completedSegments,
30315
- // Fill from bottom up
30316
- color: index >= totalSegmentsLines - completedSegments ? color : "#e5e7eb" // Completed or remaining color
30317
- }));
30448
+ // Calculate domains based on median-centered logic
30449
+ const calculateDomainBasedOnMedian = (min, max, median) => {
30450
+ const distance = Math.max(Math.abs(max - median), Math.abs(min - median));
30451
+ return [median - distance, median + distance];
30452
+ };
30453
+ const xDom = calculateDomainBasedOnMedian(minX, maxX, medianX);
30454
+ const yDom = calculateDomainBasedOnMedian(minY, maxY, medianY);
30318
30455
 
30319
- // Add 4 extra chips if currentAmount > goalAmount
30320
- if (currentAmount > goalAmount && !noCommitment) {
30321
- const extraSegments = 4;
30322
- const extraData = Array.from({
30323
- length: extraSegments
30324
- }).map((_, index) => ({
30325
- name: `extra-segment-${index}`,
30326
- value: 100,
30327
- isCompleted: true,
30328
- color: "#E8F5EB"
30329
- }));
30456
+ // Determine z-axis range scaled appropriately
30457
+ const zRng = [0, maxSize * 7];
30458
+ return {
30459
+ xMedian: medianX,
30460
+ yMedian: medianY,
30461
+ xDomain: xDom,
30462
+ yDomain: yDom,
30463
+ zRange: zRng
30464
+ };
30465
+ }, [data]);
30330
30466
 
30331
- // Prepend the extra segments to the barsChips array
30332
- barsChips = [...extraData, ...barsChips];
30333
- }
30467
+ // Init viewDomain with calculated domain values
30468
+ const fullDomain = useMemo(() => ({
30469
+ x: xDomain,
30470
+ y: yDomain
30471
+ }), [xDomain, yDomain]);
30334
30472
 
30335
- // Calculate the top position for the indicator and goal value
30473
+ // State for current view domain
30474
+ const [viewDomain, setViewDomain] = useState(fullDomain);
30475
+ const [zoomLevel, setZoomLevel] = useState(100);
30476
+
30477
+ // Drag state
30478
+ const [isDragging, setIsDragging] = useState(false);
30479
+ const [dragStart, setDragStart] = useState({
30480
+ x: 0,
30481
+ y: 0
30482
+ });
30483
+ const [domainStart, setDomainStart] = useState(fullDomain);
30484
+
30485
+ // Refs for the chart container
30486
+ const containerRef = useRef(null);
30487
+ const scatterChartRef = useRef(null);
30488
+
30489
+ // Set up initial domain
30336
30490
  useEffect(() => {
30337
- const barHeight = containerHeight / (totalSegmentsLines + (currentAmount > goalAmount ? 4 : 0)); // Adjust for extra chips
30338
- let newTop;
30339
- let newGoalValuePosition;
30340
- if (currentAmount > goalAmount) {
30341
- // Position the indicator slightly below the top chip
30342
- newTop = barHeight * 1.5; // Slightly below the 4th chip
30343
- // Position the goal value just above the active chips
30491
+ // Set initial viewDomain from fullDomain
30492
+ setViewDomain(fullDomain);
30493
+ }, [fullDomain]);
30494
+
30495
+ // Function to get gradient fill based on quadrant
30496
+ const getFill = useCallback((x, y) => {
30497
+ if (x < xMedian && y > yMedian) return 'url(#topLeftGradient)'; // Top Left
30498
+ if (x >= xMedian && y > yMedian) return 'url(#topRightGradient)'; // Top Right
30499
+ if (x < xMedian && y <= yMedian) return 'url(#bottomLeftGradient)'; // Bottom Left
30500
+ if (x >= xMedian && y <= yMedian) return 'url(#bottomRightGradient)'; // Bottom Right
30501
+ return 'gray'; // fallback
30502
+ }, [xMedian, yMedian]);
30503
+
30504
+ // CustomTooltip component inside BubbleChart
30505
+ const CustomTooltip = useCallback(_ref2 => {
30506
+ let {
30507
+ active,
30508
+ payload
30509
+ } = _ref2;
30510
+ if (active && payload && payload.length && payload[0].payload.tooltip) {
30511
+ const data = payload[0].payload;
30512
+ return /*#__PURE__*/React$1.createElement(CustomTooltipContainer, {
30513
+ "data-testid": "tooltip-container"
30514
+ }, /*#__PURE__*/React$1.createElement("h4", {
30515
+ "data-testid": "tooltip-title"
30516
+ }, data.tooltip.title), data.tooltip.description && /*#__PURE__*/React$1.createElement(TooltipMetric, {
30517
+ "data-testid": "tooltip-description"
30518
+ }, /*#__PURE__*/React$1.createElement("span", null, data.tooltip.description)), /*#__PURE__*/React$1.createElement(TooltipMetric, {
30519
+ "data-testid": "tooltip-cost"
30520
+ }, /*#__PURE__*/React$1.createElement("span", null, "Total cost:"), /*#__PURE__*/React$1.createElement("span", null, formatCurrency(data.tooltip.cost))), /*#__PURE__*/React$1.createElement(TooltipMetric, {
30521
+ "data-testid": "tooltip-sales"
30522
+ }, /*#__PURE__*/React$1.createElement("span", null, "INC Sales:"), /*#__PURE__*/React$1.createElement("span", null, formatCurrency(data.tooltip.incSales))), /*#__PURE__*/React$1.createElement(TooltipMetric, {
30523
+ "data-testid": "tooltip-roi"
30524
+ }, /*#__PURE__*/React$1.createElement("span", null, "INC Sales ROI:"), /*#__PURE__*/React$1.createElement("span", null, formatROI(data.tooltip.roi))), /*#__PURE__*/React$1.createElement(TooltipMetric, {
30525
+ "data-testid": "tooltip-units"
30526
+ }, /*#__PURE__*/React$1.createElement("span", null, "INC Units:"), /*#__PURE__*/React$1.createElement("span", null, formatUnits(data.tooltip.units))));
30527
+ }
30528
+ return null;
30529
+ }, []);
30344
30530
 
30345
- newGoalValuePosition = barHeight * 2.3;
30346
- // newGoalValuePosition = (totalSegmentsLines - completedSegments + 4) * barHeight - barHeight / 2;
30531
+ // Process all chart data - use original data values
30532
+ const allChartData = useMemo(() => {
30533
+ return data.map((item, index) => {
30534
+ if (item.x === undefined || item.y === undefined || item.size === undefined) {
30535
+ console.warn('Skipping data item with missing values:', item);
30536
+ return null;
30537
+ }
30538
+ const tooltipInfo = {
30539
+ title: item.name || 'Marketing event',
30540
+ description: item.description || '',
30541
+ cost: item.size || 0,
30542
+ units: item.units || 0,
30543
+ roi: item.x || 0,
30544
+ incSales: item.y || 0,
30545
+ ...(item.tooltip || {})
30546
+ };
30547
+ return {
30548
+ x: item.x,
30549
+ // Use original x value
30550
+ y: item.y,
30551
+ // Use original y value
30552
+ z: item.size,
30553
+ // Use original size value
30554
+ tooltip: tooltipInfo,
30555
+ fill: getFill(item.x, item.y),
30556
+ key: `bubble-${index}` // Use index for stable keys
30557
+ };
30558
+ }).filter(Boolean); // Filter out null items
30559
+ }, [data, getFill]);
30560
+
30561
+ // Function to determine which data points should be visible
30562
+ const getVisibleData = useCallback(() => {
30563
+ return allChartData.filter(item => item.x >= viewDomain.x[0] && item.x <= viewDomain.x[1] && item.y >= viewDomain.y[0] && item.y <= viewDomain.y[1]);
30564
+ }, [allChartData, viewDomain]);
30565
+
30566
+ // Get visible data - memoized
30567
+ const visibleData = useMemo(() => getVisibleData(), [getVisibleData]);
30568
+
30569
+ // Calculate a new domain based on zoom level and center point - memoized
30570
+ const calculateDomain = useCallback((center, zoomFactor) => {
30571
+ // Calculate new domain size
30572
+ const xSize = (fullDomain.x[1] - fullDomain.x[0]) * zoomFactor;
30573
+ const ySize = (fullDomain.y[1] - fullDomain.y[0]) * zoomFactor;
30574
+
30575
+ // Calculate new domain
30576
+ return {
30577
+ x: [center.x - xSize / 2, center.x + xSize / 2],
30578
+ y: [center.y - ySize / 2, center.y + ySize / 2]
30579
+ };
30580
+ }, [fullDomain]);
30581
+
30582
+ // Rendering gradients with enhanced bubble-like appearance
30583
+ const renderGradients = useCallback(() => /*#__PURE__*/React$1.createElement("defs", null, /*#__PURE__*/React$1.createElement("radialGradient", {
30584
+ id: "topLeftGradient",
30585
+ cx: "25%",
30586
+ cy: "25%",
30587
+ r: "70%",
30588
+ fx: "20%",
30589
+ fy: "20%"
30590
+ }, /*#__PURE__*/React$1.createElement("stop", {
30591
+ offset: "0%",
30592
+ stopColor: colorPalette[0]
30593
+ }), /*#__PURE__*/React$1.createElement("stop", {
30594
+ offset: "85%",
30595
+ stopColor: colorPalette[1]
30596
+ })), /*#__PURE__*/React$1.createElement("radialGradient", {
30597
+ id: "topRightGradient",
30598
+ cx: "25%",
30599
+ cy: "25%",
30600
+ r: "70%",
30601
+ fx: "20%",
30602
+ fy: "20%"
30603
+ }, /*#__PURE__*/React$1.createElement("stop", {
30604
+ offset: "0%",
30605
+ stopColor: colorPalette[2]
30606
+ }), /*#__PURE__*/React$1.createElement("stop", {
30607
+ offset: "85%",
30608
+ stopColor: colorPalette[3]
30609
+ })), /*#__PURE__*/React$1.createElement("radialGradient", {
30610
+ id: "bottomLeftGradient",
30611
+ cx: "25%",
30612
+ cy: "25%",
30613
+ r: "70%",
30614
+ fx: "20%",
30615
+ fy: "20%"
30616
+ }, /*#__PURE__*/React$1.createElement("stop", {
30617
+ offset: "0%",
30618
+ stopColor: colorPalette[4]
30619
+ }), /*#__PURE__*/React$1.createElement("stop", {
30620
+ offset: "85%",
30621
+ stopColor: colorPalette[5]
30622
+ })), /*#__PURE__*/React$1.createElement("radialGradient", {
30623
+ id: "bottomRightGradient",
30624
+ cx: "25%",
30625
+ cy: "25%",
30626
+ r: "70%",
30627
+ fx: "20%",
30628
+ fy: "20%"
30629
+ }, /*#__PURE__*/React$1.createElement("stop", {
30630
+ offset: "0%",
30631
+ stopColor: colorPalette[6]
30632
+ }), /*#__PURE__*/React$1.createElement("stop", {
30633
+ offset: "85%",
30634
+ stopColor: colorPalette[7]
30635
+ })), /*#__PURE__*/React$1.createElement("filter", {
30636
+ id: "bubble-blur",
30637
+ x: "-50%",
30638
+ y: "-50%",
30639
+ width: "200%",
30640
+ height: "200%"
30641
+ }, /*#__PURE__*/React$1.createElement("feGaussianBlur", {
30642
+ in: "SourceGraphic",
30643
+ stdDeviation: "0.3"
30644
+ }))), [colorPalette]);
30645
+
30646
+ // Handle mouse down for dragging
30647
+ const handleMouseDown = useCallback(e => {
30648
+ if (containerRef.current?.contains(e.target)) {
30649
+ setIsDragging(true);
30650
+ setDragStart({
30651
+ x: e.clientX,
30652
+ y: e.clientY
30653
+ });
30654
+ setDomainStart({
30655
+ ...viewDomain
30656
+ }); // Store current domain, not fullDomain
30657
+ e.preventDefault();
30658
+ }
30659
+ }, [viewDomain]); // Add viewDomain as dependency to capture current value
30660
+
30661
+ // Handle mouse move for dragging
30662
+ const handleMouseMove = useCallback(e => {
30663
+ if (!isDragging) return;
30664
+ const dx = e.clientX - dragStart.x;
30665
+ const dy = e.clientY - dragStart.y;
30666
+ const containerWidth = containerRef.current?.clientWidth || 800;
30667
+ const containerHeight = containerRef.current?.clientHeight || 600;
30668
+
30669
+ // Calculate the domain range from the domainStart (which preserves zoom level)
30670
+ const xRange = domainStart.x[1] - domainStart.x[0];
30671
+ const yRange = domainStart.y[1] - domainStart.y[0];
30672
+
30673
+ // Calculate the shift amount (negative for dx to make drag direction feel natural)
30674
+ const xShift = -(dx / containerWidth) * xRange * 1.5; // Multiply by 1.5 to make dragging more responsive
30675
+ const yShift = dy / containerHeight * yRange * 1.5; // Y is inverted in charts
30676
+
30677
+ // Create new domain values based on the domainStart (preserves zoom level)
30678
+ const newXDomain = [domainStart.x[0] + xShift, domainStart.x[1] + xShift];
30679
+ const newYDomain = [domainStart.y[0] + yShift, domainStart.y[1] + yShift];
30680
+
30681
+ // Update the domain
30682
+ setViewDomain({
30683
+ x: newXDomain,
30684
+ y: newYDomain
30685
+ });
30686
+ }, [isDragging, dragStart, domainStart]);
30687
+
30688
+ // Handle mouse up for dragging
30689
+ const handleMouseUp = useCallback(() => {
30690
+ setIsDragging(false);
30691
+ }, []);
30692
+
30693
+ // Handle zoom in
30694
+ const handleZoomIn = useCallback(() => {
30695
+ if (zoomLevel >= 400) return;
30696
+ const newZoomLevel = zoomLevel + 20;
30697
+ setZoomLevel(newZoomLevel);
30698
+
30699
+ // Calculate zoom factor (smaller factor = more zoomed in)
30700
+ const zoomFactor = 100 / newZoomLevel;
30701
+
30702
+ // Get current domain center
30703
+ const centerX = (viewDomain.x[0] + viewDomain.x[1]) / 2;
30704
+ const centerY = (viewDomain.y[0] + viewDomain.y[1]) / 2;
30705
+
30706
+ // Calculate new domain
30707
+ const newDomain = calculateDomain({
30708
+ x: centerX,
30709
+ y: centerY
30710
+ }, zoomFactor);
30711
+
30712
+ // Update domain
30713
+ setViewDomain(newDomain);
30714
+ }, [zoomLevel, viewDomain, calculateDomain]);
30715
+
30716
+ // Handle zoom out
30717
+ const handleZoomOut = useCallback(() => {
30718
+ if (zoomLevel <= 50) return;
30719
+ const newZoomLevel = zoomLevel - 20;
30720
+
30721
+ // If returning to 100%, reset to full domain
30722
+ if (newZoomLevel === 100) {
30723
+ setViewDomain({
30724
+ ...fullDomain
30725
+ });
30726
+ setZoomLevel(100);
30727
+ return;
30728
+ }
30729
+ setZoomLevel(newZoomLevel);
30730
+
30731
+ // Calculate zoom factor (larger factor = more zoomed out)
30732
+ const zoomFactor = 100 / newZoomLevel;
30733
+
30734
+ // Get current domain center
30735
+ const centerX = (viewDomain.x[0] + viewDomain.x[1]) / 2;
30736
+ const centerY = (viewDomain.y[0] + viewDomain.y[1]) / 2;
30737
+
30738
+ // Calculate new domain size
30739
+ const xSize = (fullDomain.x[1] - fullDomain.x[0]) * zoomFactor;
30740
+ const ySize = (fullDomain.y[1] - fullDomain.y[0]) * zoomFactor;
30741
+
30742
+ // If new domain would be larger than full domain, reset to full
30743
+ if (xSize >= fullDomain.x[1] - fullDomain.x[0] || ySize >= fullDomain.y[1] - fullDomain.y[0]) {
30744
+ setViewDomain({
30745
+ ...fullDomain
30746
+ });
30747
+ setZoomLevel(100);
30748
+ return;
30749
+ }
30750
+
30751
+ // Calculate new domain
30752
+ const newDomain = calculateDomain({
30753
+ x: centerX,
30754
+ y: centerY
30755
+ }, zoomFactor);
30756
+
30757
+ // Update domain
30758
+ setViewDomain(newDomain);
30759
+ }, [zoomLevel, viewDomain, fullDomain, calculateDomain]);
30760
+
30761
+ // Handle reset
30762
+ const handleReset = useCallback(() => {
30763
+ setZoomLevel(100);
30764
+ setViewDomain({
30765
+ ...fullDomain
30766
+ });
30767
+ }, [fullDomain]);
30768
+
30769
+ // Handle mouse wheel
30770
+ const handleWheel = useCallback(e => {
30771
+ e.preventDefault();
30772
+ if (e.deltaY < 0) {
30773
+ handleZoomIn();
30347
30774
  } else {
30348
- // Normal case: position the indicator above the active chips
30349
- newTop = (totalSegmentsLines - completedSegments) * barHeight / 1.65;
30350
- // Position the goal value just above the active chips
30351
- newGoalValuePosition = newTop;
30775
+ handleZoomOut();
30352
30776
  }
30353
- setTop(newTop);
30354
- setGoalValuePosition(newGoalValuePosition);
30355
- }, [completedSegments, totalSegmentsLines, currentAmount, goalAmount, containerHeight]);
30777
+ }, [handleZoomIn, handleZoomOut]);
30778
+
30779
+ // Memoize the visibility condition for the middle value label
30780
+ const isMiddleValueVisible = useMemo(() => xMedian >= viewDomain.x[0] && xMedian <= viewDomain.x[1] && yMedian >= viewDomain.y[0] && yMedian <= viewDomain.y[1], [viewDomain, xMedian, yMedian]);
30781
+
30782
+ // Set up event listeners
30356
30783
  useEffect(() => {
30357
- if (containerRef.current) {
30358
- const height = containerRef.current.offsetHeight;
30359
- if (height > 0) {
30360
- setContainerHeight(height);
30361
- }
30784
+ const container = containerRef.current;
30785
+ if (!container) return;
30786
+ container.addEventListener('wheel', handleWheel, {
30787
+ passive: false
30788
+ });
30789
+ container.addEventListener('mousedown', handleMouseDown);
30790
+ window.addEventListener('mousemove', handleMouseMove);
30791
+ window.addEventListener('mouseup', handleMouseUp);
30792
+ return () => {
30793
+ container.removeEventListener('wheel', handleWheel);
30794
+ container.removeEventListener('mousedown', handleMouseDown);
30795
+ window.removeEventListener('mousemove', handleMouseMove);
30796
+ window.removeEventListener('mouseup', handleMouseUp);
30797
+ };
30798
+ }, [handleWheel, handleMouseDown, handleMouseMove, handleMouseUp]);
30799
+
30800
+ // Unified arrow component with direction parameter
30801
+ const Arrow = _ref3 => {
30802
+ let {
30803
+ viewBox,
30804
+ direction
30805
+ } = _ref3;
30806
+ // Define points based on direction
30807
+ let points = '';
30808
+ switch (direction) {
30809
+ case 'top':
30810
+ points = `${viewBox.x},${viewBox.y - 10} ${viewBox.x - 5},${viewBox.y} ${viewBox.x + 5},${viewBox.y}`;
30811
+ break;
30812
+ case 'bottom':
30813
+ points = `${viewBox.x},${viewBox.y + viewBox.height + 10} ${viewBox.x - 5},${viewBox.y + viewBox.height} ${viewBox.x + 5},${viewBox.y + viewBox.height}`;
30814
+ break;
30815
+ case 'left':
30816
+ points = `${viewBox.x - 10},${viewBox.y} ${viewBox.x},${viewBox.y - 5} ${viewBox.x},${viewBox.y + 5}`;
30817
+ break;
30818
+ case 'right':
30819
+ points = `${viewBox.x + viewBox.width + 10},${viewBox.y} ${viewBox.x + viewBox.width},${viewBox.y - 5} ${viewBox.x + viewBox.width},${viewBox.y + 5}`;
30820
+ break;
30362
30821
  }
30363
- }, [containerRef, width, height]);
30364
- return /*#__PURE__*/React$1.createElement(BatteryChartContainer, {
30365
- ref: containerRef,
30822
+ return /*#__PURE__*/React$1.createElement("polygon", {
30823
+ points: points,
30824
+ fill: "#D0D0D0"
30825
+ });
30826
+ };
30827
+
30828
+ // Function to format currency values
30829
+ const formatCurrency = value => {
30830
+ if (Math.abs(value) >= 1000000) {
30831
+ return `$${(value / 1000000).toFixed(1)}M`;
30832
+ } else if (Math.abs(value) >= 1000) {
30833
+ return `$${(value / 1000).toFixed(1)}K`;
30834
+ }
30835
+ return `$${value.toFixed(1)}`;
30836
+ };
30837
+
30838
+ // Function to format units values
30839
+ const formatUnits = value => {
30840
+ if (Math.abs(value) >= 1000000) {
30841
+ return `${(value / 1000000).toFixed(1)}M`;
30842
+ } else if (Math.abs(value) >= 1000) {
30843
+ return `${(value / 1000).toFixed(1)}K`;
30844
+ }
30845
+ return `${value.toFixed(1)}`;
30846
+ };
30847
+
30848
+ // Function to format ROI values
30849
+ const formatROI = value => {
30850
+ return value.toFixed(1);
30851
+ };
30852
+
30853
+ // Format the median values for display
30854
+ const formattedMedianValue = useMemo(() => {
30855
+ const formattedX = formatROI(xMedian);
30856
+ const formattedY = formatUnits(yMedian);
30857
+ return `${formattedX}, ${formattedY}`;
30858
+ }, [xMedian, yMedian]);
30859
+
30860
+ // Render the component
30861
+ return /*#__PURE__*/React$1.createElement(BubbleChartContainer, {
30366
30862
  width: width,
30367
30863
  height: height,
30368
- className: className,
30369
- containerPadding: containerPadding,
30370
- backgroundColor: backgroundColor
30371
- }, /*#__PURE__*/React$1.createElement(HeadingText, null, title), /*#__PURE__*/React$1.createElement(PanelSide, null, /*#__PURE__*/React$1.createElement(ProgressDetails, null, !noCommitment && /*#__PURE__*/React$1.createElement(HeadingPercentage, null, percentage, "%"), /*#__PURE__*/React$1.createElement(ProgressDescription, null, ask && /*#__PURE__*/React$1.createElement(ProgressItem, null, "ASK: ", `$${getFormattedValue(ask, false) + getFormattedUnits(ask)}`), target && /*#__PURE__*/React$1.createElement(ProgressItem, null, "Target: ", `$${getFormattedValue(target, false) + getFormattedUnits(target)}`)), /*#__PURE__*/React$1.createElement(ProgressLegend, null, /*#__PURE__*/React$1.createElement(LegendItem, null, /*#__PURE__*/React$1.createElement(LegendCube, {
30372
- color: color
30373
- }), /*#__PURE__*/React$1.createElement(LegendText, null, "Spend")), !noCommitment && /*#__PURE__*/React$1.createElement(LegendItem, null, /*#__PURE__*/React$1.createElement(LegendCube, {
30374
- color: "#E3E4E5"
30375
- }), /*#__PURE__*/React$1.createElement(LegendText, null, "Commitment"))))), /*#__PURE__*/React$1.createElement(ChartSide, null, /*#__PURE__*/React$1.createElement(ChartWrapper, null, /*#__PURE__*/React$1.createElement(GoalValue, {
30376
- className: currentAmount > goalAmount && !noCommitment ? "overload" : "",
30377
- goalValuePosition: goalValuePosition
30378
- }, !noCommitment ? `$${getFormattedValue(goalAmount, false) + getFormattedUnits(goalAmount)}` : `$${getFormattedValue(currentAmount, false) + getFormattedUnits(currentAmount)}`), !noCommitment && /*#__PURE__*/React$1.createElement(Indicator, {
30379
- top: top
30380
- }, /*#__PURE__*/React$1.createElement(IndicatorLine, {
30381
- color: indicatorColor
30382
- }), /*#__PURE__*/React$1.createElement(IndicatorText, {
30383
- color: indicatorColor
30384
- }, `$${getFormattedValue(currentAmount, false) + getFormattedUnits(currentAmount)}`)), /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
30385
- height: Math.max(containerHeight - 105, 210)
30386
- }, /*#__PURE__*/React$1.createElement(BarChart$1, {
30387
- data: barsChips,
30388
- layout: "vertical",
30864
+ ref: containerRef,
30865
+ style: {
30866
+ cursor: isDragging ? 'grabbing' : 'grab'
30867
+ },
30868
+ backgroundColor: backgroundColor,
30869
+ "data-testid": "bubble-chart-container"
30870
+ }, /*#__PURE__*/React$1.createElement(HeaderContainer, {
30871
+ "data-testid": "header-container"
30872
+ }, /*#__PURE__*/React$1.createElement(ChartTitle, {
30873
+ "data-testid": "chart-title"
30874
+ }, title), /*#__PURE__*/React$1.createElement(ChartSubtitle, {
30875
+ "data-testid": "chart-subtitle"
30876
+ }, subtitle)), /*#__PURE__*/React$1.createElement(ChartContainer, {
30877
+ ref: scatterChartRef,
30878
+ "data-testid": "chart-container"
30879
+ }, /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
30880
+ width: "100%",
30881
+ height: "100%",
30882
+ "data-testid": "responsive-container"
30883
+ }, /*#__PURE__*/React$1.createElement(ScatterChart, {
30389
30884
  margin: {
30390
- top: 5,
30391
- right: 5,
30392
- left: 5,
30393
- bottom: 5
30885
+ top: 60,
30886
+ right: 190,
30887
+ bottom: 50,
30888
+ left: 180
30394
30889
  },
30395
- barSize: 4
30396
- }, /*#__PURE__*/React$1.createElement(XAxis, {
30890
+ "data-testid": "scatter-chart"
30891
+ }, renderGradients(), /*#__PURE__*/React$1.createElement(XAxis, {
30397
30892
  type: "number",
30398
- hide: true,
30399
- domain: [0, 100]
30893
+ dataKey: "x",
30894
+ domain: viewDomain.x,
30895
+ axisLine: showAxis,
30896
+ tickLine: showAxis,
30897
+ tick: showAxis ? {
30898
+ fontSize: 10,
30899
+ fontWeight: 400,
30900
+ fontFamily: 'Poppins'
30901
+ } : false,
30902
+ tickFormatter: value => formatUnits(value),
30903
+ hide: !showAxis,
30904
+ "data-testid": "x-axis",
30905
+ style: {
30906
+ fontSize: '10px',
30907
+ fontWeight: 400,
30908
+ fontFamily: 'Poppins'
30909
+ }
30400
30910
  }), /*#__PURE__*/React$1.createElement(YAxis, {
30401
- type: "category",
30402
- hide: true,
30403
- dataKey: "name"
30404
- }), /*#__PURE__*/React$1.createElement(Bar, {
30405
- dataKey: "value",
30406
- background: {
30407
- fill: "#f3f4f6"
30408
- },
30409
- radius: [8, 8, 8, 8]
30410
- }, barsChips.map((entry, index) => /*#__PURE__*/React$1.createElement(Cell, {
30411
- key: `cell-${index}`,
30412
- fill: entry.color
30413
- }))))))), starText !== "" && /*#__PURE__*/React$1.createElement(StarText, null, starText));
30911
+ type: "number",
30912
+ dataKey: "y",
30913
+ domain: viewDomain.y,
30914
+ axisLine: showAxis,
30915
+ tickLine: showAxis,
30916
+ tick: showAxis ? {
30917
+ fontSize: 10,
30918
+ fontWeight: 400,
30919
+ fontFamily: 'Poppins'
30920
+ } : false,
30921
+ tickFormatter: value => formatUnits(value),
30922
+ hide: !showAxis,
30923
+ "data-testid": "y-axis",
30924
+ style: {
30925
+ fontSize: '10px',
30926
+ fontWeight: 400,
30927
+ fontFamily: 'Poppins'
30928
+ }
30929
+ }), /*#__PURE__*/React$1.createElement(ZAxis, {
30930
+ type: "number",
30931
+ dataKey: "z",
30932
+ range: [0, 7000],
30933
+ domain: [0, Math.max(...data.map(item => item.size || 0))],
30934
+ "data-testid": "z-axis"
30935
+ }), /*#__PURE__*/React$1.createElement(ReferenceLine, {
30936
+ x: xMedian,
30937
+ stroke: "#D0D0D0",
30938
+ strokeDasharray: "4 4",
30939
+ isFront: false,
30940
+ "data-testid": "reference-line-x",
30941
+ label: xMedian >= viewDomain.x[0] && xMedian <= viewDomain.x[1] ? {
30942
+ value: topHeader,
30943
+ position: "top",
30944
+ offset: 40,
30945
+ fill: '#484A4C',
30946
+ fontSize: 16,
30947
+ fontWeight: 500
30948
+ } : null
30949
+ }), xMedian >= viewDomain.x[0] && xMedian <= viewDomain.x[1] && /*#__PURE__*/React$1.createElement(ReferenceLine, {
30950
+ x: xMedian,
30951
+ stroke: "transparent",
30952
+ isFront: true,
30953
+ label: _ref4 => {
30954
+ let {
30955
+ viewBox
30956
+ } = _ref4;
30957
+ return /*#__PURE__*/React$1.createElement(Arrow, {
30958
+ viewBox: viewBox,
30959
+ direction: "top"
30960
+ });
30961
+ }
30962
+ }), /*#__PURE__*/React$1.createElement(ReferenceLine, {
30963
+ x: xMedian,
30964
+ stroke: "transparent",
30965
+ isFront: false,
30966
+ "data-testid": "reference-line-x-bottom",
30967
+ label: xMedian >= viewDomain.x[0] && xMedian <= viewDomain.x[1] ? {
30968
+ value: bottomHeader,
30969
+ position: "bottom",
30970
+ offset: 30,
30971
+ fill: '#484A4C',
30972
+ fontSize: 16,
30973
+ fontWeight: 500
30974
+ } : null
30975
+ }), xMedian >= viewDomain.x[0] && xMedian <= viewDomain.x[1] && /*#__PURE__*/React$1.createElement(ReferenceLine, {
30976
+ x: xMedian,
30977
+ stroke: "transparent",
30978
+ isFront: true,
30979
+ label: _ref5 => {
30980
+ let {
30981
+ viewBox
30982
+ } = _ref5;
30983
+ return /*#__PURE__*/React$1.createElement(Arrow, {
30984
+ viewBox: viewBox,
30985
+ direction: "bottom"
30986
+ });
30987
+ }
30988
+ }), /*#__PURE__*/React$1.createElement(ReferenceLine, {
30989
+ y: yMedian,
30990
+ stroke: "#D0D0D0",
30991
+ strokeDasharray: "4 4",
30992
+ isFront: false,
30993
+ "data-testid": "reference-line-y",
30994
+ label: yMedian >= viewDomain.y[0] && yMedian <= viewDomain.y[1] ? {
30995
+ value: leftHeader,
30996
+ position: "left",
30997
+ offset: 30,
30998
+ fill: '#484A4C',
30999
+ fontSize: 16,
31000
+ fontWeight: 500
31001
+ } : null
31002
+ }), yMedian >= viewDomain.y[0] && yMedian <= viewDomain.y[1] && /*#__PURE__*/React$1.createElement(ReferenceLine, {
31003
+ y: yMedian,
31004
+ isFront: true,
31005
+ stroke: "transparent",
31006
+ label: _ref6 => {
31007
+ let {
31008
+ viewBox
31009
+ } = _ref6;
31010
+ return /*#__PURE__*/React$1.createElement(Arrow, {
31011
+ viewBox: viewBox,
31012
+ direction: "left"
31013
+ });
31014
+ }
31015
+ }), /*#__PURE__*/React$1.createElement(ReferenceLine, {
31016
+ y: yMedian,
31017
+ stroke: "transparent",
31018
+ isFront: false,
31019
+ "data-testid": "reference-line-y-transparent",
31020
+ label: yMedian >= viewDomain.y[0] && yMedian <= viewDomain.y[1] ? {
31021
+ value: rightHeader,
31022
+ position: "right",
31023
+ offset: 30,
31024
+ fill: '#484A4C',
31025
+ fontSize: 16,
31026
+ fontWeight: 500
31027
+ } : null
31028
+ }), yMedian >= viewDomain.y[0] && yMedian <= viewDomain.y[1] && /*#__PURE__*/React$1.createElement(ReferenceLine, {
31029
+ y: yMedian,
31030
+ isFront: true,
31031
+ stroke: "transparent",
31032
+ label: _ref7 => {
31033
+ let {
31034
+ viewBox
31035
+ } = _ref7;
31036
+ return /*#__PURE__*/React$1.createElement(Arrow, {
31037
+ viewBox: viewBox,
31038
+ direction: "right"
31039
+ });
31040
+ }
31041
+ }), /*#__PURE__*/React$1.createElement(Tooltip$2, {
31042
+ content: /*#__PURE__*/React$1.createElement(CustomTooltip, null),
31043
+ cursor: false,
31044
+ wrapperStyle: {
31045
+ zIndex: 9999,
31046
+ position: 'absolute',
31047
+ pointerEvents: 'auto'
31048
+ },
31049
+ allowEscapeViewBox: {
31050
+ x: false,
31051
+ y: false
31052
+ },
31053
+ "data-testid": "tooltip"
31054
+ }), /*#__PURE__*/React$1.createElement(Scatter, {
31055
+ name: "Bubble Chart",
31056
+ data: visibleData,
31057
+ shape: "circle",
31058
+ fill: "transparent",
31059
+ isAnimationActive: false,
31060
+ animationBegin: 0,
31061
+ animationDuration: 0,
31062
+ "data-testid": "scatter"
31063
+ }, visibleData.map((entry, index) => /*#__PURE__*/React$1.createElement(Cell, {
31064
+ key: entry.key,
31065
+ fill: entry.fill,
31066
+ stroke: "rgba(255,255,255,0.6)",
31067
+ strokeWidth: 1.5,
31068
+ filter: "url(#bubble-blur)",
31069
+ "data-testid": `cell-${index}`
31070
+ }))), isMiddleValueVisible && /*#__PURE__*/React$1.createElement(ReferenceLine, {
31071
+ x: xMedian,
31072
+ y: yMedian,
31073
+ stroke: "transparent",
31074
+ isFront: true,
31075
+ "data-testid": "middle-value-reference",
31076
+ label: _ref8 => {
31077
+ let {
31078
+ viewBox
31079
+ } = _ref8;
31080
+ // Calculate the exact position of the median point in the SVG
31081
+ const xPos = viewBox.x + (xMedian - viewDomain.x[0]) / (viewDomain.x[1] - viewDomain.x[0]) * viewBox.width;
31082
+ const yPos = viewBox.y + (1 - (yMedian - viewDomain.y[0]) / (viewDomain.y[1] - viewDomain.y[0])) * viewBox.height;
31083
+ return /*#__PURE__*/React$1.createElement("text", {
31084
+ x: xPos,
31085
+ y: yPos,
31086
+ textAnchor: "middle",
31087
+ dominantBaseline: "middle",
31088
+ fill: "#484A4C",
31089
+ fontSize: 16,
31090
+ fontWeight: 600,
31091
+ style: {
31092
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
31093
+ padding: '2px'
31094
+ }
31095
+ }, formattedMedianValue);
31096
+ }
31097
+ })))), /*#__PURE__*/React$1.createElement(ZoomControlsContainer, {
31098
+ "data-testid": "zoom-controls-container"
31099
+ }, /*#__PURE__*/React$1.createElement(ZoomPercentage, {
31100
+ "data-testid": "zoom-percentage"
31101
+ }, zoomLevel, "%"), /*#__PURE__*/React$1.createElement(ZoomButton, {
31102
+ onClick: handleZoomOut,
31103
+ title: "Zoom Out",
31104
+ "data-testid": "zoom-out-button"
31105
+ }, "\u2212"), /*#__PURE__*/React$1.createElement(ZoomButton, {
31106
+ onClick: handleZoomIn,
31107
+ title: "Zoom In",
31108
+ "data-testid": "zoom-in-button"
31109
+ }, "+"), /*#__PURE__*/React$1.createElement(ZoomResetButton, {
31110
+ onClick: handleReset,
31111
+ "data-testid": "zoom-reset-button"
31112
+ }, "Reset")));
31113
+ };
31114
+
31115
+ const BatteryChartContainer = styled.div`
31116
+ position: relative;
31117
+ font-family: "Poppins", sans-serif;
31118
+ display: grid;
31119
+ grid-template-columns: auto auto;
31120
+ grid-template-rows: auto 1fr auto;
31121
+ color: #212121;
31122
+ background-color: ${props => props.backgroundColor || 'transparent'};
31123
+ padding: ${props => props.containerPadding || '25px 30px 0'};
31124
+ width: ${props => props.width};
31125
+ height: ${props => props.height};
31126
+ `;
31127
+ const PanelSide = styled.div`
31128
+ display: flex;
31129
+ flex-direction: column;
31130
+ justify-content: space-between;
31131
+ `;
31132
+ const ProgressDetails = styled.div`
31133
+ display: flex;
31134
+ flex-direction: column;
31135
+ flex-grow: 1;
31136
+ `;
31137
+ const HeadingText = styled.p`
31138
+ grid-column: 1 / span 2;
31139
+ color: #212121;
31140
+ font-size: 18px;
31141
+ font-weight: 400;
31142
+ padding-left: 10px;
31143
+ margin: 0 0 8px;
31144
+ `;
31145
+ const HeadingPercentage = styled.p`
31146
+ color: #212121;
31147
+ font-size: 32px;
31148
+ font-weight: 500;
31149
+ padding-left: 10px;
31150
+ margin: 0;
31151
+ `;
31152
+ const ChartSide = styled.div`
31153
+ text-align: center;
31154
+ `;
31155
+ const ChartWrapper = styled.div`
31156
+ position: relative;
31157
+ width: 110px;
31158
+ `;
31159
+ const GoalValue = styled.div`
31160
+ text-align: center;
31161
+ font-size: 18px;
31162
+ color: #8B8989;
31163
+ line-height: 1;
31164
+ &.overload {
31165
+ position: absolute;
31166
+ top: ${props => props.goalValuePosition}px;
31167
+ left: 0;
31168
+ right: 0;
31169
+ z-index: 2;
31170
+ margin: 0 auto;
31171
+ width: 40%;
31172
+ display: inline;
31173
+ padding: 0px 8px;
31174
+ border: 2px solid #F2F2F2;
31175
+ border-bottom: none;
31176
+ border-radius: 6px 6px 0px 0px;
31177
+ background-color: white;
31178
+ }
31179
+ `;
31180
+ const Indicator = styled.div`
31181
+ position: absolute;
31182
+ top: ${props => props.top}px;
31183
+ left: 0;
31184
+ transform: translateY(50%);
31185
+ width: calc(100% + 60px);
31186
+ display: flex;
31187
+ gap: 6px;
31188
+ justify-content: center;
31189
+ padding-left: 4px;
31190
+ align-items: center;
31191
+ z-index: 1;
31192
+ `;
31193
+ const IndicatorLine = styled.div`
31194
+ width: 100%;
31195
+ height: 0;
31196
+ border-top: 4px dashed ${props => props.color};
31197
+ `;
31198
+ const IndicatorText = styled.span`
31199
+ font-size: 18px;
31200
+ font-weight: 400;
31201
+ color: ${props => props.color};
31202
+ `;
31203
+ const ProgressLegend = styled.div`
31204
+ margin-top: 12px;
31205
+ `;
31206
+ const LegendItem = styled.div`
31207
+ display: flex;
31208
+ gap: 4px;
31209
+ align-items: center;
31210
+ margin-bottom: 12px;
31211
+ `;
31212
+ const LegendText = styled.span`
31213
+ color: #8B8989;
31214
+ font-size: 14px;
31215
+ `;
31216
+ const LegendCube = styled.div`
31217
+ border-radius: 3px;
31218
+ width: 17px;
31219
+ height: 17px;
31220
+ background-color: ${props => props.color};
31221
+ `;
31222
+ const ProgressDescription = styled.div`
31223
+ color: #8B8989;
31224
+ font-size: 12px;
31225
+ font-weight: 400;
31226
+ margin-top: auto;
31227
+ `;
31228
+ const ProgressItem = styled.p`
31229
+ margin: 0 0 6px;
31230
+ `;
31231
+ const StarText = styled.span`
31232
+ width: 100%;
31233
+ color: #B1B1B1;
31234
+ font-size: 9px;
31235
+ grid-column: 1 / span 2;
31236
+ padding-top: 10px;
31237
+ `;
31238
+
31239
+ const BatteryChart = props => {
31240
+ const {
31241
+ className,
31242
+ containerPadding,
31243
+ width,
31244
+ height,
31245
+ title,
31246
+ color,
31247
+ backgroundColor,
31248
+ indicatorColor,
31249
+ noCommitment,
31250
+ goalAmount,
31251
+ currentAmount,
31252
+ ask,
31253
+ target,
31254
+ totalSegmentsLines,
31255
+ starText
31256
+ } = props;
31257
+ const [top, setTop] = useState(0);
31258
+ const [goalValuePosition, setGoalValuePosition] = useState(0);
31259
+ const [containerHeight, setContainerHeight] = useState(0); // State to store container height
31260
+ const containerRef = useRef(null); // Ref for BatteryChartContainer
31261
+
31262
+ // Calculate the percentage and completed segments
31263
+ const percentage = Math.round(currentAmount / goalAmount * 100);
31264
+ const completedSegments = Math.min(Math.floor(currentAmount / goalAmount * totalSegmentsLines), totalSegmentsLines);
31265
+
31266
+ // Dynamically create the data for the bar chart
31267
+ let barsChips = Array.from({
31268
+ length: totalSegmentsLines
31269
+ }).map((_, index) => ({
31270
+ name: `segment-${index}`,
31271
+ value: 100,
31272
+ isCompleted: index >= totalSegmentsLines - completedSegments,
31273
+ // Fill from bottom up
31274
+ color: index >= totalSegmentsLines - completedSegments ? color : "#e5e7eb" // Completed or remaining color
31275
+ }));
31276
+
31277
+ // Add 4 extra chips if currentAmount > goalAmount
31278
+ if (currentAmount > goalAmount && !noCommitment) {
31279
+ const extraSegments = 4;
31280
+ const extraData = Array.from({
31281
+ length: extraSegments
31282
+ }).map((_, index) => ({
31283
+ name: `extra-segment-${index}`,
31284
+ value: 100,
31285
+ isCompleted: true,
31286
+ color: "#E8F5EB"
31287
+ }));
31288
+
31289
+ // Prepend the extra segments to the barsChips array
31290
+ barsChips = [...extraData, ...barsChips];
31291
+ }
31292
+
31293
+ // Calculate the top position for the indicator and goal value
31294
+ useEffect(() => {
31295
+ const barHeight = containerHeight / (totalSegmentsLines + (currentAmount > goalAmount ? 4 : 0)); // Adjust for extra chips
31296
+ let newTop;
31297
+ let newGoalValuePosition;
31298
+ if (currentAmount > goalAmount) {
31299
+ // Position the indicator slightly below the top chip
31300
+ newTop = barHeight * 1.5; // Slightly below the 4th chip
31301
+ // Position the goal value just above the active chips
31302
+
31303
+ newGoalValuePosition = barHeight * 2.3;
31304
+ // newGoalValuePosition = (totalSegmentsLines - completedSegments + 4) * barHeight - barHeight / 2;
31305
+ } else {
31306
+ // Normal case: position the indicator above the active chips
31307
+ newTop = (totalSegmentsLines - completedSegments) * barHeight / 1.65;
31308
+ // Position the goal value just above the active chips
31309
+ newGoalValuePosition = newTop;
31310
+ }
31311
+ setTop(newTop);
31312
+ setGoalValuePosition(newGoalValuePosition);
31313
+ }, [completedSegments, totalSegmentsLines, currentAmount, goalAmount, containerHeight]);
31314
+ useEffect(() => {
31315
+ if (containerRef.current) {
31316
+ const height = containerRef.current.offsetHeight;
31317
+ if (height > 0) {
31318
+ setContainerHeight(height);
31319
+ }
31320
+ }
31321
+ }, [containerRef, width, height]);
31322
+ return /*#__PURE__*/React$1.createElement(BatteryChartContainer, {
31323
+ ref: containerRef,
31324
+ width: width,
31325
+ height: height,
31326
+ className: className,
31327
+ containerPadding: containerPadding,
31328
+ backgroundColor: backgroundColor
31329
+ }, /*#__PURE__*/React$1.createElement(HeadingText, null, title), /*#__PURE__*/React$1.createElement(PanelSide, null, /*#__PURE__*/React$1.createElement(ProgressDetails, null, !noCommitment && /*#__PURE__*/React$1.createElement(HeadingPercentage, null, percentage, "%"), /*#__PURE__*/React$1.createElement(ProgressDescription, null, ask && /*#__PURE__*/React$1.createElement(ProgressItem, null, "ASK: ", `$${getFormattedValue(ask, false) + getFormattedUnits(ask)}`), target && /*#__PURE__*/React$1.createElement(ProgressItem, null, "Target: ", `$${getFormattedValue(target, false) + getFormattedUnits(target)}`)), /*#__PURE__*/React$1.createElement(ProgressLegend, null, /*#__PURE__*/React$1.createElement(LegendItem, null, /*#__PURE__*/React$1.createElement(LegendCube, {
31330
+ color: color
31331
+ }), /*#__PURE__*/React$1.createElement(LegendText, null, "Spend")), !noCommitment && /*#__PURE__*/React$1.createElement(LegendItem, null, /*#__PURE__*/React$1.createElement(LegendCube, {
31332
+ color: "#E3E4E5"
31333
+ }), /*#__PURE__*/React$1.createElement(LegendText, null, "Commitment"))))), /*#__PURE__*/React$1.createElement(ChartSide, null, /*#__PURE__*/React$1.createElement(ChartWrapper, null, /*#__PURE__*/React$1.createElement(GoalValue, {
31334
+ className: currentAmount > goalAmount && !noCommitment ? "overload" : "",
31335
+ goalValuePosition: goalValuePosition
31336
+ }, !noCommitment ? `$${getFormattedValue(goalAmount, false) + getFormattedUnits(goalAmount)}` : `$${getFormattedValue(currentAmount, false) + getFormattedUnits(currentAmount)}`), !noCommitment && /*#__PURE__*/React$1.createElement(Indicator, {
31337
+ top: top
31338
+ }, /*#__PURE__*/React$1.createElement(IndicatorLine, {
31339
+ color: indicatorColor
31340
+ }), /*#__PURE__*/React$1.createElement(IndicatorText, {
31341
+ color: indicatorColor
31342
+ }, `$${getFormattedValue(currentAmount, false) + getFormattedUnits(currentAmount)}`)), /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
31343
+ height: Math.max(containerHeight - 105, 210)
31344
+ }, /*#__PURE__*/React$1.createElement(BarChart$1, {
31345
+ data: barsChips,
31346
+ layout: "vertical",
31347
+ margin: {
31348
+ top: 5,
31349
+ right: 5,
31350
+ left: 5,
31351
+ bottom: 5
31352
+ },
31353
+ barSize: 4
31354
+ }, /*#__PURE__*/React$1.createElement(XAxis, {
31355
+ type: "number",
31356
+ hide: true,
31357
+ domain: [0, 100]
31358
+ }), /*#__PURE__*/React$1.createElement(YAxis, {
31359
+ type: "category",
31360
+ hide: true,
31361
+ dataKey: "name"
31362
+ }), /*#__PURE__*/React$1.createElement(Bar, {
31363
+ dataKey: "value",
31364
+ background: {
31365
+ fill: "#f3f4f6"
31366
+ },
31367
+ radius: [8, 8, 8, 8]
31368
+ }, barsChips.map((entry, index) => /*#__PURE__*/React$1.createElement(Cell, {
31369
+ key: `cell-${index}`,
31370
+ fill: entry.color
31371
+ }))))))), starText !== "" && /*#__PURE__*/React$1.createElement(StarText, null, starText));
31372
+ };
31373
+
31374
+ const originalData$1 = [{
31375
+ label: "Vendor Selling Event: Week 28",
31376
+ inc_unit: 20000,
31377
+ inc_roi: 1.2
31378
+ }, {
31379
+ label: "Vendor Selling Event: Week 29",
31380
+ inc_unit: 10000,
31381
+ inc_roi: 1.2
31382
+ }, {
31383
+ label: "Vendor Selling Event: Week 30",
31384
+ inc_unit: 8000,
31385
+ inc_roi: 0.8
31386
+ }, {
31387
+ label: "Vendor Selling Event: Week 33",
31388
+ inc_unit: 12000,
31389
+ inc_roi: 1.1
31390
+ }, {
31391
+ label: "Vendor Selling Event: Week 36",
31392
+ inc_unit: 8000,
31393
+ inc_roi: 1.2
31394
+ }, {
31395
+ label: "Vendor Selling Event: Week 34",
31396
+ inc_unit: 8000,
31397
+ inc_roi: 0.7
31398
+ }, {
31399
+ label: "Vendor Selling Event: Week 35",
31400
+ inc_unit: 8000,
31401
+ inc_roi: 0.8
31402
+ }, {
31403
+ label: "Vendor Selling Event: Week 38",
31404
+ inc_unit: 9000,
31405
+ inc_roi: 1.1
31406
+ }];
31407
+ const dataWithIndex$1 = originalData$1.map((item, index) => ({
31408
+ ...item,
31409
+ index,
31410
+ shortLabel: item.label.replace("Vendor Selling Event: ", "Wk ")
31411
+ }));
31412
+ const CustomizedTick$1 = ({
31413
+ x,
31414
+ y,
31415
+ payload
31416
+ }) => {
31417
+ const label = dataWithIndex$1[payload.value]?.label ?? "";
31418
+ const parts = label.replace("Vendor Selling Event: ", "").split(" ");
31419
+ return /*#__PURE__*/React$1.createElement("g", {
31420
+ transform: `translate(${x},${y})`
31421
+ }, /*#__PURE__*/React$1.createElement("text", {
31422
+ x: 0,
31423
+ y: 0,
31424
+ dy: 16,
31425
+ textAnchor: "middle",
31426
+ fill: "#212121",
31427
+ fontSize: 11,
31428
+ fontWeight: "400",
31429
+ fontFamily: "Poppins"
31430
+ }, /*#__PURE__*/React$1.createElement("tspan", {
31431
+ x: 0,
31432
+ dy: 8
31433
+ }, "Vendor Selling"), /*#__PURE__*/React$1.createElement("tspan", {
31434
+ x: 0,
31435
+ dy: 18
31436
+ }, "Event: ", parts.join(" "))));
31437
+ };
31438
+
31439
+ // Common chart configuration
31440
+ const chartMargins = {
31441
+ top: 15,
31442
+ right: 40,
31443
+ left: 20,
31444
+ bottom: 40
31445
+ };
31446
+ const SeparatedLineBarChart = () => {
31447
+ const [viewWindow, setViewWindow] = useState({
31448
+ startIndex: 0,
31449
+ endIndex: dataWithIndex$1.length - 1
31450
+ });
31451
+ return /*#__PURE__*/React$1.createElement("div", {
31452
+ style: {
31453
+ width: "100%",
31454
+ height: 700
31455
+ }
31456
+ }, /*#__PURE__*/React$1.createElement("div", {
31457
+ style: {
31458
+ height: "40%"
31459
+ }
31460
+ }, /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
31461
+ width: "100%",
31462
+ height: "100%"
31463
+ }, /*#__PURE__*/React$1.createElement(LineChart, {
31464
+ data: dataWithIndex$1 // Use full dataset for alignment
31465
+ ,
31466
+ margin: chartMargins
31467
+ }, /*#__PURE__*/React$1.createElement(CartesianGrid, {
31468
+ strokeDasharray: "3 3",
31469
+ vertical: false
31470
+ }), /*#__PURE__*/React$1.createElement(XAxis, {
31471
+ dataKey: "index",
31472
+ type: "number",
31473
+ domain: ['dataMin', 'dataMax'],
31474
+ padding: {
31475
+ left: 20,
31476
+ right: 20
31477
+ },
31478
+ hide: true
31479
+ }), /*#__PURE__*/React$1.createElement(YAxis, {
31480
+ domain: [0, 2],
31481
+ hide: true
31482
+ }), /*#__PURE__*/React$1.createElement(Tooltip$2, null), /*#__PURE__*/React$1.createElement(Line, {
31483
+ type: "monotone",
31484
+ dataKey: "inc_roi",
31485
+ stroke: "#F8CD00",
31486
+ strokeWidth: 2,
31487
+ dot: {
31488
+ r: 4,
31489
+ fill: "#F8CD00"
31490
+ },
31491
+ activeDot: false,
31492
+ name: "INC Sales ROI"
31493
+ }, /*#__PURE__*/React$1.createElement(LabelList, {
31494
+ dataKey: "inc_roi",
31495
+ position: "top",
31496
+ formatter: value => value.toFixed(1)
31497
+ }))))), /*#__PURE__*/React$1.createElement("div", {
31498
+ style: {
31499
+ height: "60%"
31500
+ }
31501
+ }, /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
31502
+ width: "100%",
31503
+ height: "100%"
31504
+ }, /*#__PURE__*/React$1.createElement(ComposedChart, {
31505
+ data: dataWithIndex$1,
31506
+ margin: chartMargins
31507
+ }, /*#__PURE__*/React$1.createElement(CartesianGrid, {
31508
+ strokeDasharray: "3 3",
31509
+ vertical: false
31510
+ }), /*#__PURE__*/React$1.createElement(XAxis, {
31511
+ dataKey: "index",
31512
+ type: "number",
31513
+ domain: ['dataMin', 'dataMax'],
31514
+ padding: {
31515
+ left: 20,
31516
+ right: 20
31517
+ },
31518
+ tick: /*#__PURE__*/React$1.createElement(CustomizedTick$1, null),
31519
+ interval: 0 // Force display all ticks
31520
+ ,
31521
+ height: 60,
31522
+ tickLine: false,
31523
+ ticks: dataWithIndex$1.map(item => item.index)
31524
+ }), /*#__PURE__*/React$1.createElement(YAxis, {
31525
+ tickFormatter: v => `$${v / 1000}k`,
31526
+ hide: true
31527
+ }), /*#__PURE__*/React$1.createElement(Tooltip$2, null), /*#__PURE__*/React$1.createElement(Bar, {
31528
+ dataKey: "inc_unit",
31529
+ fill: "#CCDCDD",
31530
+ gap: "4px",
31531
+ borderRadius: [4, 4, 0, 8],
31532
+ barSize: 40,
31533
+ name: "INC Sales"
31534
+ }, /*#__PURE__*/React$1.createElement(LabelList, {
31535
+ dataKey: "inc_unit",
31536
+ position: "top",
31537
+ formatter: value => `$${value / 1000}k`,
31538
+ fill: "#212121",
31539
+ fontSize: 12,
31540
+ fontWeight: "400",
31541
+ fontFamily: "Poppins"
31542
+ })), /*#__PURE__*/React$1.createElement(Brush, {
31543
+ dataKey: "index",
31544
+ height: 30,
31545
+ stroke: "#212121",
31546
+ startIndex: viewWindow.startIndex,
31547
+ endIndex: viewWindow.endIndex,
31548
+ onChange: e => {
31549
+ setViewWindow({
31550
+ startIndex: e.startIndex ?? 0,
31551
+ endIndex: e.endIndex ?? dataWithIndex$1.length - 1
31552
+ });
31553
+ },
31554
+ travellerWidth: 12
31555
+ })), /*#__PURE__*/React$1.createElement("div", {
31556
+ style: {
31557
+ marginTop: "-28px"
31558
+ }
31559
+ }, /*#__PURE__*/React$1.createElement(PerformanceAnalyticsLegend, {
31560
+ legendData: [{
31561
+ iconColor: "#CCDCDD",
31562
+ iconType: "Square",
31563
+ title: "INC Sales"
31564
+ }, {
31565
+ iconColor: "#F8CD00",
31566
+ iconType: "LegendUnionIcon",
31567
+ title: "INC Sales ROI"
31568
+ }]
31569
+ })))));
31570
+ };
31571
+
31572
+ const originalData = [{
31573
+ label: "Vendor Selling Event: Week 28",
31574
+ inc_unit: 20000
31575
+ }, {
31576
+ label: "Vendor Selling Event: Week 29",
31577
+ inc_unit: 10000
31578
+ }, {
31579
+ label: "Vendor Selling Event: Week 30",
31580
+ inc_unit: 8000
31581
+ }, {
31582
+ label: "Vendor Selling Event: Week 33",
31583
+ inc_unit: 12000
31584
+ }, {
31585
+ label: "Vendor Selling Event: Week 36",
31586
+ inc_unit: 8000
31587
+ }, {
31588
+ label: "Vendor Selling Event: Week 34",
31589
+ inc_unit: 8000
31590
+ }, {
31591
+ label: "Vendor Selling Event: Week 35",
31592
+ inc_unit: 8000
31593
+ }, {
31594
+ label: "Vendor Selling Event: Week 38",
31595
+ inc_unit: 9000
31596
+ }];
31597
+
31598
+ // Adding index and shortLabel to original data
31599
+ const dataWithIndex = originalData.map((item, index) => ({
31600
+ ...item,
31601
+ index,
31602
+ shortLabel: item.label.replace("Vendor Selling Event: ", "Wk ")
31603
+ }));
31604
+ const CustomizedTick = ({
31605
+ x,
31606
+ y,
31607
+ payload
31608
+ }) => {
31609
+ const label = dataWithIndex[payload.value]?.label ?? "";
31610
+ const parts = label.replace("Vendor Selling Event: ", "").split(" ");
31611
+ return /*#__PURE__*/React$1.createElement("g", {
31612
+ transform: `translate(${x},${y})`
31613
+ }, /*#__PURE__*/React$1.createElement("text", {
31614
+ x: 0,
31615
+ y: 0,
31616
+ dy: 16,
31617
+ textAnchor: "middle",
31618
+ fill: "#212121",
31619
+ fontSize: 11,
31620
+ fontWeight: "400",
31621
+ fontFamily: "Poppins"
31622
+ }, /*#__PURE__*/React$1.createElement("tspan", {
31623
+ x: 0,
31624
+ dy: 8
31625
+ }, "Vendor Selling"), /*#__PURE__*/React$1.createElement("tspan", {
31626
+ x: 0,
31627
+ dy: 18
31628
+ }, "Event: ", parts.join(" "))));
31629
+ };
31630
+ const SingleChart = () => {
31631
+ const [startIndex, setStartIndex] = useState(0);
31632
+ const [endIndex, setEndIndex] = useState(dataWithIndex.length - 1);
31633
+ dataWithIndex.slice(startIndex, endIndex + 1);
31634
+ return /*#__PURE__*/React$1.createElement("div", {
31635
+ style: {
31636
+ width: "100%",
31637
+ height: 600
31638
+ }
31639
+ }, /*#__PURE__*/React$1.createElement("div", {
31640
+ style: {
31641
+ height: "100%"
31642
+ }
31643
+ }, /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
31644
+ width: "100%",
31645
+ height: "100%"
31646
+ }, /*#__PURE__*/React$1.createElement(ComposedChart, {
31647
+ data: dataWithIndex,
31648
+ margin: {
31649
+ top: 40,
31650
+ right: 40,
31651
+ left: 20,
31652
+ bottom: 40
31653
+ }
31654
+ }, /*#__PURE__*/React$1.createElement(CartesianGrid, {
31655
+ strokeDasharray: "3 3",
31656
+ vertical: false
31657
+ }), /*#__PURE__*/React$1.createElement(XAxis, {
31658
+ dataKey: "index",
31659
+ tick: /*#__PURE__*/React$1.createElement(CustomizedTick, null),
31660
+ interval: 0,
31661
+ height: 60,
31662
+ padding: {
31663
+ left: 20,
31664
+ right: 20
31665
+ },
31666
+ tickLine: false
31667
+ }), /*#__PURE__*/React$1.createElement(YAxis, {
31668
+ tickFormatter: v => `$${v / 1000}k`,
31669
+ hide: true
31670
+ }), /*#__PURE__*/React$1.createElement(Tooltip$2, null), /*#__PURE__*/React$1.createElement(Bar, {
31671
+ dataKey: "inc_unit",
31672
+ fill: "#CCDCDD",
31673
+ gap: "4px",
31674
+ borderRadius: [4, 4, 0, 8],
31675
+ barSize: 40,
31676
+ name: "INC Sales"
31677
+ }, /*#__PURE__*/React$1.createElement(LabelList, {
31678
+ dataKey: "inc_unit",
31679
+ position: "top",
31680
+ formatter: value => `$${value / 1000}k`,
31681
+ fill: "#212121",
31682
+ fontSize: 12,
31683
+ fontWeight: "400",
31684
+ fontFamily: "Poppins"
31685
+ })), /*#__PURE__*/React$1.createElement(Brush, {
31686
+ dataKey: "index",
31687
+ height: 30,
31688
+ stroke: "#212121",
31689
+ startIndex: startIndex,
31690
+ endIndex: endIndex,
31691
+ onChange: e => {
31692
+ setStartIndex(e.startIndex ?? 0);
31693
+ setEndIndex(e.endIndex ?? dataWithIndex.length - 1);
31694
+ },
31695
+ travellerWidth: 12
31696
+ })), /*#__PURE__*/React$1.createElement(PerformanceAnalyticsLegend, {
31697
+ legendData: [{
31698
+ iconColor: "#CCDCDD",
31699
+ iconType: "Square",
31700
+ title: "INC Units"
31701
+ }]
31702
+ }))));
31703
+ };
31704
+
31705
+ styled.div`
31706
+ display: flex;
31707
+ gap: 28px;
31708
+ @media (max-width: 1536px) {
31709
+ gap: 20px;
31710
+ }
31711
+ @media (max-width: 1366px) {
31712
+ gap: 17px;
31713
+ }
31714
+ `;
31715
+ const ButtonsControlsContainer = styled.div`
31716
+ display: flex;
31717
+ font: ${props => props.fontSize?.toString().concat('', 'px Poppins, sans-serif')};
31718
+ margin: 0;
31719
+ --highlight-width: auto;
31720
+ --highlight-x-pos: 0;
31721
+ -webkit-font-smoothing: antialiased;
31722
+ -moz-osx-font-smoothing: grayscale;
31723
+ float: left;
31724
+
31725
+ &:not(:first-of-type) {
31726
+ padding-left: 40px;
31727
+ @media (max-width: 1536px) {
31728
+ padding-left: 35px;
31729
+ }
31730
+ @media (max-width: 1366px) {
31731
+ padding-left: 24px;
31732
+ }
31733
+ }
31734
+ `;
31735
+ const Controls = styled.div`
31736
+ display: inline-flex;
31737
+ justify-content: space-between;
31738
+ background: #f2f2f2;
31739
+ border-radius: ${props => props.controlradius.toString().concat('', 'px')}; //12px
31740
+ // max-width: 500px; //use this to limit max lenght of the control
31741
+ padding: 6px;
31742
+ gap: ${props => props.gap};
31743
+ overflow: hidden;
31744
+ position: relative;
31745
+ &.controls::before {
31746
+ content: "";
31747
+ color: #212121;
31748
+ background: #FFFFFF;
31749
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
31750
+ border-radius: ${props => props.segmentradius.toString().concat('', 'px')};
31751
+ width: var(--highlight-width);
31752
+ transform: translateX(var(--highlight-x-pos));
31753
+ position: absolute;
31754
+ top: 3px;
31755
+ bottom: 3px;
31756
+ left: 0;
31757
+ z-index: 0;
31758
+ }
31759
+ &.controls.ready::before {
31760
+ transition: transform 0.3s ease, width 0.3s ease;
31761
+ }
31762
+ `;
31763
+ const ControlsInput = styled.input`
31764
+ opacity: 0;
31765
+ margin: 0;
31766
+ top: 0;
31767
+ right: 0;
31768
+ bottom: 0;
31769
+ left: 0;
31770
+ position: absolute;
31771
+ width: 100%;
31772
+ cursor: pointer;
31773
+ height: 100%;
31774
+ `;
31775
+ const Segment = styled.div`
31776
+ /* width: 100%; uncomment for each segment to have matching width */
31777
+ width: 50%;
31778
+ min-width: ${props => props.segmentwidth.toString().concat('', 'px')}; //120px;
31779
+ position: relative;
31780
+ text-align: center;
31781
+ z-index: 1;
31782
+ `;
31783
+ const SegmentLabel = styled.label`
31784
+ cursor: pointer;
31785
+ display: block;
31786
+ padding: 0 6px;
31787
+ transition: color 0.5s ease;
31788
+ &.active {
31789
+ color: ${props => props.selectedtextcolor};
31790
+ }
31791
+ &.inactive {
31792
+ color: ${props => props.unselectedtextcolor};
31793
+ }
31794
+ `;
31795
+
31796
+ /* eslint-disable react/require-default-props */
31797
+
31798
+ /* SegmentedButton */
31799
+ const SegmentedButton = props => {
31800
+ const {
31801
+ name,
31802
+ options,
31803
+ width,
31804
+ height,
31805
+ controlradius,
31806
+ segmentradius,
31807
+ fontSize,
31808
+ selectedsegmentcolor,
31809
+ selectedtextcolor,
31810
+ unselectedtextcolor,
31811
+ defaultIndex,
31812
+ onClick,
31813
+ gap
31814
+ } = props;
31815
+ const [activeIndex, setActiveIndex] = useState(defaultIndex);
31816
+ const controlRef = useRef();
31817
+ const componentReady = useRef();
31818
+ const optionsRef = options?.map((item, i) => ({
31819
+ ...item,
31820
+ id: i,
31821
+ ref: useRef()
31822
+ }));
31823
+
31824
+ // Determine when the component is "ready"
31825
+ useEffect(() => {
31826
+ componentReady.current = true;
31827
+ }, []);
31828
+ useEffect(() => {
31829
+ if (defaultIndex >= 0 && defaultIndex < options?.length) {
31830
+ setActiveIndex(defaultIndex);
31831
+ }
31832
+ }, [defaultIndex]);
31833
+ useEffect(() => {
31834
+ const activeSegmentRef = optionsRef[activeIndex].ref;
31835
+ const {
31836
+ offsetWidth,
31837
+ offsetLeft
31838
+ } = activeSegmentRef.current;
31839
+ const {
31840
+ style
31841
+ } = controlRef.current;
31842
+ style.setProperty('--highlight-width', `${offsetWidth}px`);
31843
+ style.setProperty('--highlight-x-pos', `${offsetLeft}px`);
31844
+ }, [activeIndex, controlRef, optionsRef]);
31845
+ const onInputChangeHandler = (value, index) => {
31846
+ setActiveIndex(index);
31847
+ onClick({
31848
+ index,
31849
+ value
31850
+ });
31851
+ };
31852
+ let segmentwidth = 120;
31853
+ if (options && options.length > 0 && width && width > 0) {
31854
+ segmentwidth = width / options.length;
31855
+ }
31856
+ return /*#__PURE__*/React$1.createElement(ButtonsControlsContainer, {
31857
+ fontSize: fontSize,
31858
+ ref: controlRef
31859
+ }, /*#__PURE__*/React$1.createElement(Controls, {
31860
+ id: "Controls",
31861
+ selectedsegmentcolor: selectedsegmentcolor,
31862
+ segmentradius: segmentradius,
31863
+ gap: gap,
31864
+ controlradius: controlradius,
31865
+ segmentwidth: segmentwidth,
31866
+ className: `controls ${componentReady.current ? 'ready' : 'idle'}`
31867
+ }, optionsRef?.map((item, i) => /*#__PURE__*/React$1.createElement(Segment, {
31868
+ id: "Segment",
31869
+ key: item.id,
31870
+ selectedsegmentcolor: selectedsegmentcolor,
31871
+ controlradius: controlradius,
31872
+ segmentwidth: segmentwidth,
31873
+ className: `${activeIndex === i ? 'active' : 'inactive'}`,
31874
+ ref: item.ref
31875
+ }, /*#__PURE__*/React$1.createElement(ControlsInput, {
31876
+ type: "radio",
31877
+ value: item.value,
31878
+ id: item.id,
31879
+ name: name,
31880
+ onChange: () => onInputChangeHandler(item.value, item.id),
31881
+ checked: i === activeIndex
31882
+ }), /*#__PURE__*/React$1.createElement(SegmentLabel, {
31883
+ id: "SegmentLabel",
31884
+ selectedtextcolor: selectedtextcolor,
31885
+ unselectedtextcolor: unselectedtextcolor,
31886
+ segmentheight: height / 5,
31887
+ className: `${activeIndex === i ? 'active' : 'inactive'} `,
31888
+ htmlFor: item.value
31889
+ }, item.value)))));
31890
+ };
31891
+ SegmentedButton.propTypes = {
31892
+ name: PropTypes.string,
31893
+ options: PropTypes.arrayOf(PropTypes.shape({
31894
+ value: PropTypes.string
31895
+ })),
31896
+ width: PropTypes.number,
31897
+ height: PropTypes.number,
31898
+ controlradius: PropTypes.number,
31899
+ segmentradius: PropTypes.number,
31900
+ gap: PropTypes.string,
31901
+ fontSize: PropTypes.number,
31902
+ selectedsegmentcolor: PropTypes.string,
31903
+ selectedtextcolor: PropTypes.string,
31904
+ unselectedtextcolor: PropTypes.string,
31905
+ defaultIndex: PropTypes.number,
31906
+ onClick: PropTypes.func
31907
+ };
31908
+ SegmentedButton.defaultProps = {
31909
+ name: '',
31910
+ options: [{
31911
+ value: '4 w'
31912
+ }, {
31913
+ value: '8 w'
31914
+ }, {
31915
+ value: '13 w'
31916
+ }],
31917
+ width: 120,
31918
+ height: 40,
31919
+ controlradius: 8,
31920
+ segmentradius: 8,
31921
+ gap: '0px',
31922
+ fontSize: 14,
31923
+ selectedsegmentcolor: '#3a9df9',
31924
+ selectedtextcolor: '#212121',
31925
+ unselectedtextcolor: 'black',
31926
+ defaultIndex: 0,
31927
+ onClick: () => {}
31928
+ };
31929
+
31930
+ var css_248z = ".BarLine {\r\n font-family: sans-serif;\r\n text-align: center;\r\n }\r\n \r\n /* Make brush background darker */\r\n /* .recharts-brush .recharts-brush-slide {\r\n fill: #a52a2a !important;\r\n stroke: none;\r\n }\r\n \r\n /* Style draggable handles (travellers) */\r\n /* .recharts-brush-traveller {\r\n fill: limegreen !important; /* bright green like in the image */\r\n /* cursor: ew-resize;\r\n } */\r\n */\r\n \r\n /* Optional: center grip arrows (you can fake it with CSS) */\r\n /* .recharts-brush .recharts-brush-traveller > rect {\r\n rx: 2;\r\n ry: 2;\r\n width: 6;\r\n fill: #a52a2a !important; /* dark gray */\r\n /* } */ */\r\n */\r\n \r\n /* Remove tick labels if needed */\r\n .recharts-brush .recharts-layer text {\r\n display: none;\r\n }\r\n ";
31931
+ styleInject(css_248z);
31932
+
31933
+ function InnerBarChart() {
31934
+ const data = [{
31935
+ week: "Week 28",
31936
+ total: 20000,
31937
+ actual: 4000
31938
+ }, {
31939
+ week: "Week 29",
31940
+ total: 10000,
31941
+ actual: 8000
31942
+ }, {
31943
+ week: "Week 30",
31944
+ total: 8000,
31945
+ actual: 950
31946
+ }, {
31947
+ week: "Week 33",
31948
+ total: 12000,
31949
+ actual: 1000
31950
+ }, {
31951
+ week: "Week 36",
31952
+ total: 16000,
31953
+ actual: 13000
31954
+ }, {
31955
+ week: "Week 34",
31956
+ total: 30000,
31957
+ actual: 3000
31958
+ }, {
31959
+ week: "Week 35",
31960
+ total: 8000,
31961
+ actual: 950
31962
+ }, {
31963
+ week: "Week 37",
31964
+ total: 9000,
31965
+ actual: 3000
31966
+ }];
31967
+ const originalData = [{
31968
+ label: "Vendor Selling Event: Week 28",
31969
+ inc_unit: 20000,
31970
+ inc_roi: 1.2
31971
+ }, {
31972
+ label: "Vendor Selling Event: Week 29",
31973
+ inc_unit: 10000,
31974
+ inc_roi: 1.2
31975
+ }, {
31976
+ label: "Vendor Selling Event: Week 30",
31977
+ inc_unit: 8000,
31978
+ inc_roi: 0.8
31979
+ }, {
31980
+ label: "Vendor Selling Event: Week 33",
31981
+ inc_unit: 12000,
31982
+ inc_roi: 1.1
31983
+ }, {
31984
+ label: "Vendor Selling Event: Week 36",
31985
+ inc_unit: 8000,
31986
+ inc_roi: 1.2
31987
+ }, {
31988
+ label: "Vendor Selling Event: Week 34",
31989
+ inc_unit: 8000,
31990
+ inc_roi: 0.7
31991
+ }, {
31992
+ label: "Vendor Selling Event: Week 35",
31993
+ inc_unit: 8000,
31994
+ inc_roi: 0.8
31995
+ }, {
31996
+ label: "Vendor Selling Event: Week 38",
31997
+ inc_unit: 9000,
31998
+ inc_roi: 1.1
31999
+ }];
32000
+ const format = v => v >= 1000 ? `${v / 1000}K` : v;
32001
+ const CustomBarLabel = ({
32002
+ x,
32003
+ y,
32004
+ width,
32005
+ payload
32006
+ }) => {
32007
+ if (!payload || typeof payload.total === "undefined") {
32008
+ console.error("Invalid payload:", payload);
32009
+ return null;
32010
+ }
32011
+ const centerX = x + width / 2;
32012
+ return /*#__PURE__*/React$1.createElement("text", {
32013
+ x: centerX,
32014
+ y: Math.max(y - 8, 10),
32015
+ textAnchor: "middle",
32016
+ fill: "black"
32017
+ }, format(payload.total));
32018
+ };
32019
+ const BarWithInner = props => {
32020
+ const {
32021
+ x,
32022
+ y,
32023
+ width,
32024
+ height,
32025
+ payload
32026
+ } = props;
32027
+ const totalHeight = height;
32028
+ const actualRatio = payload.actual / payload.total;
32029
+ const innerHeight = totalHeight * actualRatio;
32030
+ const innerY = y + (totalHeight - innerHeight);
32031
+ return /*#__PURE__*/React$1.createElement("g", null, /*#__PURE__*/React$1.createElement("rect", {
32032
+ x: x,
32033
+ y: y,
32034
+ width: width,
32035
+ height: height,
32036
+ fill: "#d0e6ea",
32037
+ rx: 4,
32038
+ ry: 4
32039
+ }), /*#__PURE__*/React$1.createElement("text", {
32040
+ x: x + width / 2,
32041
+ y: y - 8,
32042
+ textAnchor: "middle",
32043
+ fontSize: 11,
32044
+ fontWeight: 400,
32045
+ fontFamily: "Poppins",
32046
+ fill: "#212121"
32047
+ }, format(payload.total)), /*#__PURE__*/React$1.createElement("rect", {
32048
+ x: x + width * 0.2,
32049
+ y: innerY,
32050
+ width: width * 0.6,
32051
+ height: innerHeight,
32052
+ fill: "#07575b",
32053
+ rx: 2,
32054
+ ry: 2
32055
+ }), innerHeight > 16 && /*#__PURE__*/React$1.createElement("text", {
32056
+ x: x + width / 2,
32057
+ y: innerY - 6,
32058
+ textAnchor: "middle",
32059
+ fontSize: 11,
32060
+ fontWeight: 400,
32061
+ fontFamily: "Poppins",
32062
+ fill: "#212121"
32063
+ }, format(payload.actual)));
32064
+ };
32065
+ const CustomTooltip = ({
32066
+ active,
32067
+ payload,
32068
+ label
32069
+ }) => {
32070
+ if (!active || !payload || !payload.length) return null;
32071
+ const data = payload[0].payload;
32072
+ return /*#__PURE__*/React$1.createElement("div", {
32073
+ style: {
32074
+ background: "white",
32075
+ border: "1px solid #ccc",
32076
+ padding: "10px",
32077
+ borderRadius: "8px",
32078
+ fontSize: "14px"
32079
+ }
32080
+ }, /*#__PURE__*/React$1.createElement("strong", null, label), /*#__PURE__*/React$1.createElement("div", null, "Actual: ", format(data.actual)), /*#__PURE__*/React$1.createElement("div", null, "Total: ", format(data.total)));
32081
+ };
32082
+ const dataWithIndex = originalData.map((item, index) => ({
32083
+ ...item,
32084
+ index,
32085
+ shortLabel: item.label.replace("Vendor Selling Event: ", "Wk ")
32086
+ }));
32087
+ const CustomizedTick = ({
32088
+ x,
32089
+ y,
32090
+ payload
32091
+ }) => {
32092
+ const label = dataWithIndex[payload.value]?.label ?? "";
32093
+ const parts = label.replace("Vendor Selling Event: ", "").split(" ");
32094
+ return /*#__PURE__*/React$1.createElement("g", {
32095
+ transform: `translate(${x},${y})`
32096
+ }, /*#__PURE__*/React$1.createElement("text", {
32097
+ x: 0,
32098
+ y: 0,
32099
+ dy: 16,
32100
+ textAnchor: "middle",
32101
+ fill: "#212121",
32102
+ fontSize: 11,
32103
+ fontWeight: "400",
32104
+ fontFamily: "Poppins"
32105
+ }, /*#__PURE__*/React$1.createElement("tspan", {
32106
+ x: 0,
32107
+ dy: 8
32108
+ }, "Vendor Selling"), /*#__PURE__*/React$1.createElement("tspan", {
32109
+ x: 0,
32110
+ dy: 18
32111
+ }, "Event: ", parts.join(" "))));
32112
+ };
32113
+ return /*#__PURE__*/React$1.createElement(ResponsiveContainer, {
32114
+ width: "100%",
32115
+ height: 400
32116
+ }, /*#__PURE__*/React$1.createElement(BarChart$1, {
32117
+ data: data,
32118
+ margin: {
32119
+ top: 20,
32120
+ right: 30,
32121
+ left: 20,
32122
+ bottom: 10
32123
+ }
32124
+ }, /*#__PURE__*/React$1.createElement(CartesianGrid, {
32125
+ strokeDasharray: "3 3",
32126
+ vertical: false
32127
+ }), /*#__PURE__*/React$1.createElement(XAxis, {
32128
+ dataKey: "week",
32129
+ tick: /*#__PURE__*/React$1.createElement(CustomizedTick, null)
32130
+ }), /*#__PURE__*/React$1.createElement(YAxis, {
32131
+ type: "number",
32132
+ domain: [0, "dataMax + 2000"],
32133
+ hide: true
32134
+ }), /*#__PURE__*/React$1.createElement(Tooltip$2, {
32135
+ content: /*#__PURE__*/React$1.createElement(CustomTooltip, null)
32136
+ }), /*#__PURE__*/React$1.createElement(Brush, {
32137
+ height: 30,
32138
+ travellerWidth: 10,
32139
+ startIndex: 1,
32140
+ endIndex: 5,
32141
+ y: 369
32142
+ }), /*#__PURE__*/React$1.createElement(Bar, {
32143
+ dataKey: "total",
32144
+ shape: /*#__PURE__*/React$1.createElement(BarWithInner, null),
32145
+ label: /*#__PURE__*/React$1.createElement(CustomBarLabel, null),
32146
+ barSize: 30
32147
+ })));
32148
+ }
32149
+
32150
+ styled.div`
32151
+ font-family: sans-serif;
32152
+ text-align: center;
32153
+ `;
32154
+ styled.div`
32155
+ height: 25%;
32156
+ `;
32157
+ styled.div`
32158
+ height: 75%;
32159
+ `;
32160
+ styled.div`
32161
+ height: 100%;
32162
+ display: flex;
32163
+ flex-direction: column;
32164
+ background-color: white;
32165
+ `;
32166
+ const Container = styled.div`
32167
+ width: 100%;
32168
+ height: 100%;
32169
+ display: flex;
32170
+ flex-direction: column;
32171
+ #Segment {
32172
+ width: auto;
32173
+ white-space: nowrap;
32174
+ font-size: 14px;
32175
+ font-weight: 500;
32176
+ font-family: "Poppins"
32177
+ }
32178
+ `;
32179
+ /* Make brush background darker */
32180
+ /* .recharts-brush .recharts-brush-slide {
32181
+ fill: #a52a2a !important;
32182
+ stroke: none;
32183
+ }
32184
+
32185
+ /* Style draggable handles (travellers) */
32186
+ /* .recharts-brush-traveller {
32187
+ fill: limegreen !important; /* bright green like in the image */
32188
+ /* cursor: ew-resize;
32189
+ } */
32190
+
32191
+ /* Optional: center grip arrows (you can fake it with CSS) */
32192
+ /* .recharts-brush .recharts-brush-traveller > rect {
32193
+ rx: 2;
32194
+ ry: 2;
32195
+ width: 6;
32196
+ fill: #a52a2a !important; /* dark gray */
32197
+ /* } */
32198
+
32199
+ /* Remove tick labels if needed */
32200
+ // .recharts-brush .recharts-layer text {
32201
+ // display: none;
32202
+ // }
32203
+
32204
+ const BrushChart = () => {
32205
+ const segmentedbuttonOptions = ["New Shoppers & Repeaters", "INC Sales & ROI", "INC Units", "Basket Lift"];
32206
+ const [selectedOption, setSelectedOption] = useState(segmentedbuttonOptions[0]);
32207
+ return /*#__PURE__*/React$1.createElement(Container, null, /*#__PURE__*/React$1.createElement(SegmentedButton, {
32208
+ gap: '8px',
32209
+ options: segmentedbuttonOptions.map(value => ({
32210
+ value
32211
+ })),
32212
+ onClick: _ref => {
32213
+ let {
32214
+ value
32215
+ } = _ref;
32216
+ return setSelectedOption(value);
32217
+ }
32218
+ }), selectedOption === "New Shoppers & Repeaters" && /*#__PURE__*/React$1.createElement(InnerBarChart, null), selectedOption === "INC Sales & ROI" && /*#__PURE__*/React$1.createElement(SeparatedLineBarChart, null), selectedOption === "INC Units" && /*#__PURE__*/React$1.createElement(SingleChart, null), selectedOption === "Basket Lift" && /*#__PURE__*/React$1.createElement(SingleChart, null));
30414
32219
  };
30415
32220
 
30416
- export { AreaChart, BannerEventBoxList, BarChart, BarChartsByWeeks, BatteryChart, BreakdownPanel, Button, CollapseHeader, DialogOverlay, DoubleBarSingleLine, DoublePanelDataRow, EventDetailsCard, EventList, FilterPanel, Heatmap, IconButton, LinkButton, LinnerDataBox, MarketShareDescription, OneColumnContainer, PerformanceAnalyticsLegend, PieChart, PopupCharts, QuickFilter, ReportTable, TabMenu, TopToggleList, TotalDoughnutChart, TotalHorizontalCharts };
32221
+ export { AreaChart, BannerEventBoxList, BarChart, BarChartsByWeeks, BatteryChart, BreakdownPanel, BrushChart, BubbleChart, Button, CollapseHeader, DialogOverlay, DoubleBarSingleLine, DoublePanelDataRow, EventDetailsCard, EventList, FilterPanel, Heatmap, IconButton, LinkButton, LinnerDataBox, MarketShareDescription, OneColumnContainer, PerformanceAnalyticsLegend, PieChart, PopupCharts, QuickFilter, ReportTable, TabMenu, TopToggleList, TotalDoughnutChart, TotalHorizontalCharts };
30417
32222
  //# sourceMappingURL=index.esm.js.map