From 3d9557f9e6f781485ff7d4a92c61a721b8bbb6a2 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Sun, 30 Jul 2023 16:15:27 +0200 Subject: [PATCH 01/17] add number of months --- src/stories/input-elements/DateRangePicker.stories.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/stories/input-elements/DateRangePicker.stories.tsx b/src/stories/input-elements/DateRangePicker.stories.tsx index 2cfadc7b0..424e98482 100644 --- a/src/stories/input-elements/DateRangePicker.stories.tsx +++ b/src/stories/input-elements/DateRangePicker.stories.tsx @@ -199,3 +199,8 @@ UncontrolledWithoutAllowClear.args = { defaultValue: { from: new Date(2022, 10, 1), to: new Date() }, enableClear: false, }; + +export const WithNumberOfMonths = UncontrolledTemplate.bind({}); +WithNumberOfMonths.args = { + numberOfMonths: 2 +}; \ No newline at end of file From 75a94273f81ee65450e0b103ffcc913c697ecdc0 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Sun, 30 Jul 2023 16:15:54 +0200 Subject: [PATCH 02/17] add number of mnths --- .../input-elements/DateRangePicker/DateRangePicker.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/input-elements/DateRangePicker/DateRangePicker.tsx b/src/components/input-elements/DateRangePicker/DateRangePicker.tsx index b32bdda26..f792d5a14 100644 --- a/src/components/input-elements/DateRangePicker/DateRangePicker.tsx +++ b/src/components/input-elements/DateRangePicker/DateRangePicker.tsx @@ -48,6 +48,7 @@ export interface DateRangePickerProps locale?: Locale; enableClear?: boolean; enableYearNavigation?: boolean; + numberOfMonths?: number; children?: React.ReactElement[] | React.ReactElement; } @@ -67,6 +68,7 @@ const DateRangePicker = React.forwardRef(( children, className, enableYearNavigation = false, + numberOfMonths = 1, ...other } = props; @@ -242,6 +244,7 @@ const DateRangePicker = React.forwardRef(( from: selectedStartDate, to: selectedEndDate, }} + numberOfMonths={numberOfMonths ?? 1} onSelect={ ((v: DateRange) => { onValueChange?.({ from: v?.from, to: v?.to }); From d29e907a0f879888981fcb946197a6fdf3af86a5 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 11:59:55 +0200 Subject: [PATCH 03/17] test for line chart --- .../chart-elements/LineChart/LineChart.tsx | 446 +++++++++++------- .../chart-elements/common/ChartTooltip.tsx | 254 +++++----- 2 files changed, 423 insertions(+), 277 deletions(-) diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index 5c1e6819c..c9e7a9dad 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -1,14 +1,15 @@ "use client"; import React, { useState } from "react"; import { - CartesianGrid, - Legend, - Line, - LineChart as ReChartsLineChart, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, + CartesianGrid, + Legend, + Line, + LineChart as ReChartsLineChart, + ReferenceArea, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, } from "recharts"; import { AxisDomain } from "recharts/types/util/types"; @@ -19,176 +20,285 @@ import ChartLegend from "components/chart-elements/common/ChartLegend"; import ChartTooltip from "../common/ChartTooltip"; import { - BaseColors, - colorPalette, - defaultValueFormatter, - getColorClassNames, - themeColorRange, - tremorTwMerge, + BaseColors, + colorPalette, + defaultValueFormatter, + getColorClassNames, + themeColorRange, + tremorTwMerge, } from "lib"; import { CurveType } from "../../../lib/inputTypes"; export interface LineChartProps extends BaseChartProps { - curveType?: CurveType; - connectNulls?: boolean; + curveType?: CurveType; + connectNulls?: boolean; +} + +export type SelectionProps = { + leftArea?: any; + rightArea?: any; } const LineChart = React.forwardRef((props, ref) => { - const { - data = [], - categories = [], - index, - colors = themeColorRange, - valueFormatter = defaultValueFormatter, - startEndOnly = false, - showXAxis = true, - showYAxis = true, - yAxisWidth = 56, - animationDuration = 900, - showAnimation = true, - showTooltip = true, - showLegend = true, - showGridLines = true, - autoMinValue = false, - curveType = "linear", - minValue, - maxValue, - connectNulls = false, - allowDecimals = true, - noDataText, - className, - ...other - } = props; - const [legendHeight, setLegendHeight] = useState(60); - const categoryColors = constructCategoryColors(categories, colors); + const { + data = [], + categories = [], + index, + colors = themeColorRange, + valueFormatter = defaultValueFormatter, + startEndOnly = false, + showXAxis = true, + showYAxis = true, + yAxisWidth = 56, + animationDuration = 900, + showAnimation = true, + showTooltip = true, + showLegend = true, + showGridLines = true, + autoMinValue = false, + curveType = "linear", + minValue, + maxValue, + connectNulls = false, + allowDecimals = true, + noDataText, + className, + ...other + } = props; + const [selection, setSelection] = useState({}) - const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); + const [legendHeight, setLegendHeight] = useState(60); + const categoryColors = constructCategoryColors(categories, colors); - return ( -
- - {data?.length ? ( - - {showGridLines ? ( - - ) : null} - - - {showTooltip ? ( - ( - + + const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); + + return ( +
+ + {data?.length ? ( + setSelection({ leftArea: e })} + onMouseMove={(e) => selection.leftArea && setSelection(prev => ({ ...prev, rightArea: e }))} + onMouseUp={() => setSelection({})} + + > + {showGridLines ? ( + + ) : null} + + + {(showTooltip) ? ( + ( + + )} + position={{ y: 0 }} + /> + ) : null} + {showLegend ? ( + ChartLegend({ payload }, categoryColors, setLegendHeight)} + /> + ) : null} + {categories.map((category) => ( + { + if (props.payload[index] === selection?.leftArea?.activeLabel) { + return ( + + + + ) + } + + return <> + }} + key={category} + name={category} + type={curveType} + dataKey={category} + stroke="" + strokeWidth={2} + strokeLinejoin="round" + strokeLinecap="round" + isAnimationActive={showAnimation} + animationDuration={animationDuration} + connectNulls={connectNulls} + /> + ))} + {selection.leftArea?.activeLabel && selection.rightArea?.activeLabel ? ( + { + return ( + <> + + + + + ); + }} + /> + ) : null} + + ) : ( + )} - position={{ y: 0 }} - /> - ) : null} - {showLegend ? ( - ChartLegend({ payload }, categoryColors, setLegendHeight)} - /> - ) : null} - {categories.map((category) => ( - - ))} - - ) : ( - - )} - -
- ); +
+
+ ); }); LineChart.displayName = "LineChart"; diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index d0126ab34..aa5f9f8ae 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -4,131 +4,167 @@ import { tremorTwMerge } from "../../../lib"; import { Color, ValueFormatter } from "../../../lib"; import { BaseColors, border, getColorClassNames, sizing, spacing } from "lib"; import { colorPalette } from "lib/theme"; +import { SelectionProps } from "components/chart-elements/LineChart/LineChart"; export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => ( -
- {children} -
+
+ {children} +
); export interface ChartTooltipRowProps { - value: string; - name: string; - color: Color; + value: string; + name: string; + color: Color; + textColor?: 'green' | 'red' | null } -export const ChartTooltipRow = ({ value, name, color }: ChartTooltipRowProps) => ( -
-
- -

- {name} -

+const getSelectionPayloadValue = (payload: any, dataKey: string) => ( + payload.find((e: any) => e.dataKey === dataKey)?.value ?? 0 +) + +export const ChartTooltipRow = ({ value, name, color, textColor }: ChartTooltipRowProps) => ( +
+
+ +

+ {name} +

+
+

+ {value} +

-

- {value} -

-
); export interface ChartTooltipProps { - active: boolean | undefined; - payload: any; - label: string; - categoryColors: Map; - valueFormatter: ValueFormatter; + active: boolean | undefined; + payload: any; + label: string; + categoryColors: Map; + valueFormatter: ValueFormatter; + selection?: SelectionProps; } const ChartTooltip = ({ - active, - payload, - label, - categoryColors, - valueFormatter, + active, + payload, + label, + categoryColors, + valueFormatter, + selection }: ChartTooltipProps) => { - if (active && payload) { - return ( - -
-

- {label} -

-
+ const hasSelection = Boolean(selection?.leftArea && selection?.rightArea) -
- {payload.map(({ value, name }: { value: number; name: string }, idx: number) => ( - - ))} -
-
- ); - } - return null; + if(hasSelection && selection?.leftArea?.activeLabel === selection?.rightArea?.activeLabel) + return null + + if (active && payload ) { + return ( + +
+

+ {hasSelection ? ( + selection?.leftArea?.chartX < selection?.rightArea?.chartX ? `${selection?.leftArea?.activeLabel} - ${selection?.rightArea?.activeLabel}` : `${selection?.rightArea?.activeLabel} - ${selection?.leftArea?.activeLabel}` + ) : ( + label + )} +

+
+ +
+ {payload.map(({ value, name }: { value: number; name: string }, idx: number) => { + const isBeforeLeftValue = selection?.leftArea?.chartX > selection?.rightArea?.chartX + + const displayedValue = hasSelection ? (getSelectionPayloadValue(selection?.rightArea?.activePayload, name) - getSelectionPayloadValue(selection?.leftArea?.activePayload, name)) * (isBeforeLeftValue ? -1 : 1) : value + const percentage = hasSelection ? ( + (100 - (isBeforeLeftValue ? ( + getSelectionPayloadValue(selection?.leftArea?.activePayload, name) / getSelectionPayloadValue(selection?.rightArea?.activePayload, name) + ) : ( + getSelectionPayloadValue(selection?.rightArea?.activePayload, name) / getSelectionPayloadValue(selection?.leftArea?.activePayload, name) + )) * 100) * -1 + ) : ( + 0 + ) + + const percentageValue = hasSelection ? `(${percentage.toFixed(2)}%)` : '' + + return ( + = 0 ? 'green' : 'red') : null} + /> + ) + })} +
+
+ ); + } + return null; }; export default ChartTooltip; From 2faf271a19369a592ca0a0f93327ff2e661f5a72 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 17:31:48 +0200 Subject: [PATCH 04/17] add range selection on line & area --- .../chart-elements/AreaChart/AreaChart.tsx | 51 +- .../chart-elements/LineChart/LineChart.tsx | 486 ++++++++---------- .../chart-elements/common/ChartTooltip.tsx | 283 +++++----- .../chart-elements/common/RangeProps.tsx | 6 + .../common/RangeReferenceShape.tsx | 69 +++ 5 files changed, 477 insertions(+), 418 deletions(-) create mode 100644 src/components/chart-elements/common/RangeProps.tsx create mode 100644 src/components/chart-elements/common/RangeReferenceShape.tsx diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 27a3c4027..9289ead9d 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -5,6 +5,7 @@ import { CartesianGrid, Legend, AreaChart as ReChartsAreaChart, + ReferenceArea, ResponsiveContainer, Tooltip, XAxis, @@ -27,6 +28,8 @@ import { tremorTwMerge, } from "lib"; import { CurveType } from "../../../lib/inputTypes"; +import RangeProps from "components/chart-elements/common/RangeProps"; +import RangeReferenceShape from "components/chart-elements/common/RangeReferenceShape"; export interface AreaChartProps extends BaseChartProps { stack?: boolean; @@ -62,6 +65,7 @@ const AreaChart = React.forwardRef((props, ref) className, ...other } = props; + const [range, setRange] = useState({}); const [legendHeight, setLegendHeight] = useState(60); const categoryColors = constructCategoryColors(categories, colors); @@ -71,7 +75,12 @@ const AreaChart = React.forwardRef((props, ref)
{data?.length ? ( - + setRange({ leftArea: e })} + onMouseMove={(e) => range.leftArea && setRange((prev) => ({ ...prev, rightArea: e }))} + onMouseUp={() => setRange({})} + > {showGridLines ? ( ((props, ref) ( ((props, ref) label={label} valueFormatter={valueFormatter} categoryColors={categoryColors} + range={range} /> )} position={{ y: 0 }} @@ -210,7 +223,29 @@ const AreaChart = React.forwardRef((props, ref) ).fillColor, ), }} - dot={false} + dot={(props) => { + const { payload, width, height, cx, cy } = props; + if (payload[index] === range?.leftArea?.activeLabel) { + return ( + + + + ); + } + + return <>; + }} key={category} name={category} type={curveType} @@ -226,6 +261,16 @@ const AreaChart = React.forwardRef((props, ref) connectNulls={connectNulls} /> ))} + {range.leftArea?.activeLabel && range.rightArea?.activeLabel ? ( + ( + + )} + /> + ) : null} ) : ( diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index c9e7a9dad..6b6d2830c 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -1,15 +1,15 @@ "use client"; import React, { useState } from "react"; import { - CartesianGrid, - Legend, - Line, - LineChart as ReChartsLineChart, - ReferenceArea, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, + CartesianGrid, + Legend, + Line, + LineChart as ReChartsLineChart, + ReferenceArea, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, } from "recharts"; import { AxisDomain } from "recharts/types/util/types"; @@ -18,287 +18,223 @@ import NoData from "../common/NoData"; import BaseChartProps from "../common/BaseChartProps"; import ChartLegend from "components/chart-elements/common/ChartLegend"; import ChartTooltip from "../common/ChartTooltip"; +import RangeProps from "components/chart-elements/common/RangeProps"; +import RangeReferenceShape from "components/chart-elements/common/RangeReferenceShape"; import { - BaseColors, - colorPalette, - defaultValueFormatter, - getColorClassNames, - themeColorRange, - tremorTwMerge, + BaseColors, + colorPalette, + defaultValueFormatter, + getColorClassNames, + themeColorRange, + tremorTwMerge, } from "lib"; import { CurveType } from "../../../lib/inputTypes"; export interface LineChartProps extends BaseChartProps { - curveType?: CurveType; - connectNulls?: boolean; -} - -export type SelectionProps = { - leftArea?: any; - rightArea?: any; + curveType?: CurveType; + connectNulls?: boolean; } const LineChart = React.forwardRef((props, ref) => { - const { - data = [], - categories = [], - index, - colors = themeColorRange, - valueFormatter = defaultValueFormatter, - startEndOnly = false, - showXAxis = true, - showYAxis = true, - yAxisWidth = 56, - animationDuration = 900, - showAnimation = true, - showTooltip = true, - showLegend = true, - showGridLines = true, - autoMinValue = false, - curveType = "linear", - minValue, - maxValue, - connectNulls = false, - allowDecimals = true, - noDataText, - className, - ...other - } = props; - const [selection, setSelection] = useState({}) - - const [legendHeight, setLegendHeight] = useState(60); - const categoryColors = constructCategoryColors(categories, colors); - + const { + data = [], + categories = [], + index, + colors = themeColorRange, + valueFormatter = defaultValueFormatter, + startEndOnly = false, + showXAxis = true, + showYAxis = true, + yAxisWidth = 56, + animationDuration = 900, + showAnimation = true, + showTooltip = true, + showLegend = true, + showGridLines = true, + autoMinValue = false, + curveType = "linear", + minValue, + maxValue, + connectNulls = false, + allowDecimals = true, + noDataText, + className, + ...other + } = props; + const [range, setRange] = useState({}); - const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); + const [legendHeight, setLegendHeight] = useState(60); + const categoryColors = constructCategoryColors(categories, colors); - return ( -
- - {data?.length ? ( - setSelection({ leftArea: e })} - onMouseMove={(e) => selection.leftArea && setSelection(prev => ({ ...prev, rightArea: e }))} - onMouseUp={() => setSelection({})} + const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); - > - {showGridLines ? ( - - ) : null} - - + + {data?.length ? ( + setRange({ leftArea: e })} + onMouseMove={(e) => range.leftArea && setRange((prev) => ({ ...prev, rightArea: e }))} + onMouseUp={() => setRange({})} + > + {showGridLines ? ( + + ) : null} + + + {showTooltip ? ( + ( + + )} + position={{ y: 0 }} + /> + ) : null} + {showLegend ? ( + ChartLegend({ payload }, categoryColors, setLegendHeight)} + /> + ) : null} + {categories.map((category) => ( + { + const { payload, width, height, cx, cy } = props; + if (payload[index] === range?.leftArea?.activeLabel) { + return ( + + - {(showTooltip) ? ( - ( - - )} - position={{ y: 0 }} - /> - ) : null} - {showLegend ? ( - ChartLegend({ payload }, categoryColors, setLegendHeight)} - /> - ) : null} - {categories.map((category) => ( - { - if (props.payload[index] === selection?.leftArea?.activeLabel) { - return ( - - - - ) - } + + ); + } - return <> - }} - key={category} - name={category} - type={curveType} - dataKey={category} - stroke="" - strokeWidth={2} - strokeLinejoin="round" - strokeLinecap="round" - isAnimationActive={showAnimation} - animationDuration={animationDuration} - connectNulls={connectNulls} - /> - ))} - {selection.leftArea?.activeLabel && selection.rightArea?.activeLabel ? ( - { - return ( - <> - - - - - ); - }} - /> - ) : null} - - ) : ( - + return <>; + }} + key={category} + name={category} + type={curveType} + dataKey={category} + stroke="" + strokeWidth={2} + strokeLinejoin="round" + strokeLinecap="round" + isAnimationActive={showAnimation} + animationDuration={animationDuration} + connectNulls={connectNulls} + /> + ))} + {range.leftArea?.activeLabel && range.rightArea?.activeLabel ? ( + ( + )} - -
- ); + /> + ) : null} + + ) : ( + + )} +
+
+ ); }); LineChart.displayName = "LineChart"; diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index aa5f9f8ae..4eb2dba5e 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -4,167 +4,170 @@ import { tremorTwMerge } from "../../../lib"; import { Color, ValueFormatter } from "../../../lib"; import { BaseColors, border, getColorClassNames, sizing, spacing } from "lib"; import { colorPalette } from "lib/theme"; -import { SelectionProps } from "components/chart-elements/LineChart/LineChart"; +import RangeProps from "components/chart-elements/common/RangeProps"; export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => ( -
- {children} -
+
+ {children} +
); export interface ChartTooltipRowProps { - value: string; - name: string; - color: Color; - textColor?: 'green' | 'red' | null + value: string; + name: string; + color: Color; + textColor?: "green" | "red" | null; } -const getSelectionPayloadValue = (payload: any, dataKey: string) => ( - payload.find((e: any) => e.dataKey === dataKey)?.value ?? 0 -) +const getRangePayloadValue = (payload: any, dataKey: string) => + payload.find((e: any) => e.dataKey === dataKey)?.value ?? 0; export const ChartTooltipRow = ({ value, name, color, textColor }: ChartTooltipRowProps) => ( -
-
- -

- {name} -

-
-

- {value} -

+
+
+ +

+ {name} +

+

+ {value} +

+
); export interface ChartTooltipProps { - active: boolean | undefined; - payload: any; - label: string; - categoryColors: Map; - valueFormatter: ValueFormatter; - selection?: SelectionProps; + active: boolean | undefined; + payload: any; + label: string; + categoryColors: Map; + valueFormatter: ValueFormatter; + range?: RangeProps; } const ChartTooltip = ({ - active, - payload, - label, - categoryColors, - valueFormatter, - selection + active, + payload, + label, + categoryColors, + valueFormatter, + range, }: ChartTooltipProps) => { - const hasSelection = Boolean(selection?.leftArea && selection?.rightArea) + const hasRange = Boolean(range?.leftArea && range?.rightArea); + + if (hasRange && range?.leftArea?.activeLabel === range?.rightArea?.activeLabel) return null; + + if (active && payload) { + return ( + +
+

+ {hasRange + ? range?.leftArea?.chartX < range?.rightArea?.chartX + ? `${range?.leftArea?.activeLabel} - ${range?.rightArea?.activeLabel}` + : `${range?.rightArea?.activeLabel} - ${range?.leftArea?.activeLabel}` + : label} +

+
- if(hasSelection && selection?.leftArea?.activeLabel === selection?.rightArea?.activeLabel) - return null +
+ {payload.map(({ value, name }: { value: number; name: string }, idx: number) => { + const isBeforeLeftValue = range?.leftArea?.chartX > range?.rightArea?.chartX; - if (active && payload ) { - return ( - -
-

- {hasSelection ? ( - selection?.leftArea?.chartX < selection?.rightArea?.chartX ? `${selection?.leftArea?.activeLabel} - ${selection?.rightArea?.activeLabel}` : `${selection?.rightArea?.activeLabel} - ${selection?.leftArea?.activeLabel}` - ) : ( - label - )} -

-
+ const displayedValue = hasRange + ? (getRangePayloadValue(range?.rightArea?.activePayload, name) - + getRangePayloadValue(range?.leftArea?.activePayload, name)) * + (isBeforeLeftValue ? -1 : 1) + : value; + const percentage = hasRange + ? (100 - + (isBeforeLeftValue + ? getRangePayloadValue(range?.leftArea?.activePayload, name) / + getRangePayloadValue(range?.rightArea?.activePayload, name) + : getRangePayloadValue(range?.rightArea?.activePayload, name) / + getRangePayloadValue(range?.leftArea?.activePayload, name)) * + 100) * + -1 + : 0; -
- {payload.map(({ value, name }: { value: number; name: string }, idx: number) => { - const isBeforeLeftValue = selection?.leftArea?.chartX > selection?.rightArea?.chartX - - const displayedValue = hasSelection ? (getSelectionPayloadValue(selection?.rightArea?.activePayload, name) - getSelectionPayloadValue(selection?.leftArea?.activePayload, name)) * (isBeforeLeftValue ? -1 : 1) : value - const percentage = hasSelection ? ( - (100 - (isBeforeLeftValue ? ( - getSelectionPayloadValue(selection?.leftArea?.activePayload, name) / getSelectionPayloadValue(selection?.rightArea?.activePayload, name) - ) : ( - getSelectionPayloadValue(selection?.rightArea?.activePayload, name) / getSelectionPayloadValue(selection?.leftArea?.activePayload, name) - )) * 100) * -1 - ) : ( - 0 - ) - - const percentageValue = hasSelection ? `(${percentage.toFixed(2)}%)` : '' + const percentageValue = hasRange ? `(${percentage.toFixed(2)}%)` : ""; - return ( - = 0 ? 'green' : 'red') : null} - /> - ) - })} -
-
- ); - } - return null; + return ( + = 0 ? "green" : "red") : null} + /> + ); + })} +
+
+ ); + } + return null; }; export default ChartTooltip; diff --git a/src/components/chart-elements/common/RangeProps.tsx b/src/components/chart-elements/common/RangeProps.tsx new file mode 100644 index 000000000..6fe78bf36 --- /dev/null +++ b/src/components/chart-elements/common/RangeProps.tsx @@ -0,0 +1,6 @@ +interface RangeProps { + leftArea?: any; + rightArea?: any; +} + +export default RangeProps; diff --git a/src/components/chart-elements/common/RangeReferenceShape.tsx b/src/components/chart-elements/common/RangeReferenceShape.tsx new file mode 100644 index 000000000..05f46f427 --- /dev/null +++ b/src/components/chart-elements/common/RangeReferenceShape.tsx @@ -0,0 +1,69 @@ +import { tremorTwMerge } from "lib"; +import React from "react"; + +interface RangeReferenceShapeProps { + x: number; + y: number; + width: number; + height: number; +} + +const RangeReferenceShape = (props: RangeReferenceShapeProps) => { + const { x, y, width, height } = props; + + return ( + <> + + + + + ); +}; + +export default RangeReferenceShape; From ff3feea90674f11decf24fd8579b068196b2910b Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 17:35:36 +0200 Subject: [PATCH 05/17] fix dot svg --- .../chart-elements/AreaChart/AreaChart.tsx | 18 ++++++++++++++++-- .../chart-elements/LineChart/LineChart.tsx | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 9289ead9d..de493f78e 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -73,7 +73,7 @@ const AreaChart = React.forwardRef((props, ref) return (
- + {data?.length ? ( ((props, ref) ), }} dot={(props) => { - const { payload, width, height, cx, cy } = props; + const { + payload, + width, + height, + cx, + cy, + stroke, + strokeLinecap, + strokeLinejoin, + strokeWidth, + } = props; if (payload[index] === range?.leftArea?.activeLabel) { return ( @@ -232,6 +242,10 @@ const AreaChart = React.forwardRef((props, ref) cx={cx} cy={cy} r={4} + stroke={stroke} + strokeLinecap={strokeLinecap} + strokeLinejoin={strokeLinejoin} + strokeWidth={strokeWidth} className={tremorTwMerge( "stroke-tremor-background dark:stroke-dark-tremor-background", getColorClassNames( diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index 6b6d2830c..01bdc36ed 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -183,7 +183,17 @@ const LineChart = React.forwardRef((props, ref) ), }} dot={(props) => { - const { payload, width, height, cx, cy } = props; + const { + payload, + width, + height, + cx, + cy, + stroke, + strokeLinecap, + strokeLinejoin, + strokeWidth, + } = props; if (payload[index] === range?.leftArea?.activeLabel) { return ( @@ -191,6 +201,10 @@ const LineChart = React.forwardRef((props, ref) cx={cx} cy={cy} r={4} + stroke={stroke} + strokeLinecap={strokeLinecap} + strokeLinejoin={strokeLinejoin} + strokeWidth={strokeWidth} className={tremorTwMerge( "stroke-tremor-background dark:stroke-dark-tremor-background", getColorClassNames( From a7fcef869d9ea2c6d073fba3610d760bb69b5548 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 18:49:30 +0200 Subject: [PATCH 06/17] add to barchart and add enableDeltaCalculation prop --- .../chart-elements/AreaChart/AreaChart.tsx | 27 +++++++++-------- .../chart-elements/BarChart/BarChart.tsx | 27 +++++++++++++++-- .../chart-elements/LineChart/LineChart.tsx | 27 +++++++++-------- .../chart-elements/common/BaseChartProps.tsx | 1 + .../chart-elements/common/ChartTooltip.tsx | 30 +++++++++---------- .../common/DeltaCalculationProps.tsx | 6 ++++ ...tsx => DeltaCalculationReferenceShape.tsx} | 6 ++-- .../chart-elements/common/RangeProps.tsx | 6 ---- .../chart-elements/AreaChart.stories.tsx | 7 +++++ .../chart-elements/BarChart.stories.tsx | 7 +++++ .../chart-elements/LineChart.stories.tsx | 7 +++++ 11 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 src/components/chart-elements/common/DeltaCalculationProps.tsx rename src/components/chart-elements/common/{RangeReferenceShape.tsx => DeltaCalculationReferenceShape.tsx} (87%) delete mode 100644 src/components/chart-elements/common/RangeProps.tsx diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index de493f78e..d8b21e548 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -28,8 +28,8 @@ import { tremorTwMerge, } from "lib"; import { CurveType } from "../../../lib/inputTypes"; -import RangeProps from "components/chart-elements/common/RangeProps"; -import RangeReferenceShape from "components/chart-elements/common/RangeReferenceShape"; +import DeltaCalculationProps from "components/chart-elements/common/DeltaCalculationProps"; +import DeltaCalculationReferenceShape from "components/chart-elements/common/DeltaCalculationReferenceShape"; export interface AreaChartProps extends BaseChartProps { stack?: boolean; @@ -61,11 +61,12 @@ const AreaChart = React.forwardRef((props, ref) maxValue, connectNulls = false, allowDecimals = true, + enableDeltaCalculation = false, noDataText, className, ...other } = props; - const [range, setRange] = useState({}); + const [deltaCalculation, setDeltaCalculation] = useState({}); const [legendHeight, setLegendHeight] = useState(60); const categoryColors = constructCategoryColors(categories, colors); @@ -77,9 +78,9 @@ const AreaChart = React.forwardRef((props, ref) {data?.length ? ( setRange({ leftArea: e })} - onMouseMove={(e) => range.leftArea && setRange((prev) => ({ ...prev, rightArea: e }))} - onMouseUp={() => setRange({})} + onMouseDown={(e) => enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} + onMouseMove={(e) => (enableDeltaCalculation && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( ((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: range.leftArea?.activeLabel && range.rightArea?.activeLabel ? 0 : 1, + strokeWidth: deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, }} content={({ active, payload, label }) => ( ((props, ref) label={label} valueFormatter={valueFormatter} categoryColors={categoryColors} - range={range} + deltaCalculation={deltaCalculation} /> )} position={{ y: 0 }} @@ -235,7 +236,7 @@ const AreaChart = React.forwardRef((props, ref) strokeLinejoin, strokeWidth, } = props; - if (payload[index] === range?.leftArea?.activeLabel) { + if (payload[index] === deltaCalculation?.leftArea?.activeLabel) { return ( ((props, ref) connectNulls={connectNulls} /> ))} - {range.leftArea?.activeLabel && range.rightArea?.activeLabel ? ( + {deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? ( ( - + )} /> ) : null} diff --git a/src/components/chart-elements/BarChart/BarChart.tsx b/src/components/chart-elements/BarChart/BarChart.tsx index 20db08b34..16889fb6a 100644 --- a/src/components/chart-elements/BarChart/BarChart.tsx +++ b/src/components/chart-elements/BarChart/BarChart.tsx @@ -7,6 +7,7 @@ import { CartesianGrid, Legend, BarChart as ReChartsBarChart, + ReferenceArea, ResponsiveContainer, Tooltip, XAxis, @@ -21,6 +22,8 @@ import ChartTooltip from "../common/ChartTooltip"; import NoData from "../common/NoData"; import { BaseColors, defaultValueFormatter, themeColorRange } from "lib"; +import DeltaCalculationProps from "components/chart-elements/common/DeltaCalculationProps"; +import DeltaCalculationReferenceShape from "components/chart-elements/common/DeltaCalculationReferenceShape"; export interface BarChartProps extends BaseChartProps { layout?: "vertical" | "horizontal"; @@ -51,10 +54,12 @@ const BarChart = React.forwardRef((props, ref) => minValue, maxValue, allowDecimals = true, + enableDeltaCalculation = false, noDataText, className, ...other } = props; + const [deltaCalculation, setDeltaCalculation] = useState({}); const [legendHeight, setLegendHeight] = useState(60); const categoryColors = constructCategoryColors(categories, colors); @@ -62,12 +67,15 @@ const BarChart = React.forwardRef((props, ref) => return (
- + {data?.length ? ( (enableDeltaCalculation && layout === "horizontal") && setDeltaCalculation({ leftArea: e })} + onMouseMove={(e) => (enableDeltaCalculation && layout === "horizontal" && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( ((props, ref) => // ongoing issue: https://github.com/recharts/recharts/issues/2920 wrapperStyle={{ outline: "none" }} isAnimationActive={false} - cursor={{ fill: "#d1d5db", opacity: "0.15" }} + cursor={{ + fill: "#d1d5db", + opacity: + deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? "0" : "0.15", + }} content={({ active, payload, label }) => ( ((props, ref) => label={label} valueFormatter={valueFormatter} categoryColors={categoryColors} + deltaCalculation={deltaCalculation} /> )} position={{ y: 0 }} @@ -219,6 +232,16 @@ const BarChart = React.forwardRef((props, ref) => animationDuration={animationDuration} /> ))} + {deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? ( + ( + + )} + /> + ) : null} ) : ( diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index 01bdc36ed..060fbf317 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -18,8 +18,8 @@ import NoData from "../common/NoData"; import BaseChartProps from "../common/BaseChartProps"; import ChartLegend from "components/chart-elements/common/ChartLegend"; import ChartTooltip from "../common/ChartTooltip"; -import RangeProps from "components/chart-elements/common/RangeProps"; -import RangeReferenceShape from "components/chart-elements/common/RangeReferenceShape"; +import DeltaCalculationProps from "components/chart-elements/common/DeltaCalculationProps"; +import DeltaCalculationReferenceShape from "components/chart-elements/common/DeltaCalculationReferenceShape"; import { BaseColors, @@ -58,11 +58,12 @@ const LineChart = React.forwardRef((props, ref) maxValue, connectNulls = false, allowDecimals = true, + enableDeltaCalculation = false, noDataText, className, ...other } = props; - const [range, setRange] = useState({}); + const [deltaCalculation, setDeltaCalculation] = useState({}); const [legendHeight, setLegendHeight] = useState(60); const categoryColors = constructCategoryColors(categories, colors); @@ -75,9 +76,9 @@ const LineChart = React.forwardRef((props, ref) {data?.length ? ( setRange({ leftArea: e })} - onMouseMove={(e) => range.leftArea && setRange((prev) => ({ ...prev, rightArea: e }))} - onMouseUp={() => setRange({})} + onMouseDown={(e) => enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} + onMouseMove={(e) => (enableDeltaCalculation && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( ((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: range.leftArea?.activeLabel && range.rightArea?.activeLabel ? 0 : 1, + strokeWidth: deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, }} content={({ active, payload, label }) => ( ((props, ref) label={label} valueFormatter={valueFormatter} categoryColors={categoryColors} - range={range} + deltaCalculation={deltaCalculation} /> )} position={{ y: 0 }} @@ -194,7 +195,7 @@ const LineChart = React.forwardRef((props, ref) strokeLinejoin, strokeWidth, } = props; - if (payload[index] === range?.leftArea?.activeLabel) { + if (payload[index] === deltaCalculation?.leftArea?.activeLabel) { return ( ((props, ref) connectNulls={connectNulls} /> ))} - {range.leftArea?.activeLabel && range.rightArea?.activeLabel ? ( + {deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? ( ( - + )} /> ) : null} diff --git a/src/components/chart-elements/common/BaseChartProps.tsx b/src/components/chart-elements/common/BaseChartProps.tsx index 8a9621e7b..ff4c44ec6 100644 --- a/src/components/chart-elements/common/BaseChartProps.tsx +++ b/src/components/chart-elements/common/BaseChartProps.tsx @@ -20,6 +20,7 @@ interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAttributes< maxValue?: number; allowDecimals?: boolean; noDataText?: string; + enableDeltaCalculation?: boolean; } export default BaseChartProps; diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index 4eb2dba5e..da1b40bec 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -4,7 +4,7 @@ import { tremorTwMerge } from "../../../lib"; import { Color, ValueFormatter } from "../../../lib"; import { BaseColors, border, getColorClassNames, sizing, spacing } from "lib"; import { colorPalette } from "lib/theme"; -import RangeProps from "components/chart-elements/common/RangeProps"; +import DeltaCalculationProps from "components/chart-elements/common/DeltaCalculationProps"; export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => (
; valueFormatter: ValueFormatter; - range?: RangeProps; + deltaCalculation?: DeltaCalculationProps; } const ChartTooltip = ({ @@ -93,11 +93,11 @@ const ChartTooltip = ({ label, categoryColors, valueFormatter, - range, + deltaCalculation, }: ChartTooltipProps) => { - const hasRange = Boolean(range?.leftArea && range?.rightArea); + const hasRange = Boolean(deltaCalculation?.leftArea && deltaCalculation?.rightArea); - if (hasRange && range?.leftArea?.activeLabel === range?.rightArea?.activeLabel) return null; + if (hasRange && deltaCalculation?.leftArea?.activeLabel === deltaCalculation?.rightArea?.activeLabel) return null; if (active && payload) { return ( @@ -124,29 +124,29 @@ const ChartTooltip = ({ )} > {hasRange - ? range?.leftArea?.chartX < range?.rightArea?.chartX - ? `${range?.leftArea?.activeLabel} - ${range?.rightArea?.activeLabel}` - : `${range?.rightArea?.activeLabel} - ${range?.leftArea?.activeLabel}` + ? deltaCalculation?.leftArea?.chartX < deltaCalculation?.rightArea?.chartX + ? `${deltaCalculation?.leftArea?.activeLabel} - ${deltaCalculation?.rightArea?.activeLabel}` + : `${deltaCalculation?.rightArea?.activeLabel} - ${deltaCalculation?.leftArea?.activeLabel}` : label}

{payload.map(({ value, name }: { value: number; name: string }, idx: number) => { - const isBeforeLeftValue = range?.leftArea?.chartX > range?.rightArea?.chartX; + const isBeforeLeftValue = deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; const displayedValue = hasRange - ? (getRangePayloadValue(range?.rightArea?.activePayload, name) - - getRangePayloadValue(range?.leftArea?.activePayload, name)) * + ? (getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) - + getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * (isBeforeLeftValue ? -1 : 1) : value; const percentage = hasRange ? (100 - (isBeforeLeftValue - ? getRangePayloadValue(range?.leftArea?.activePayload, name) / - getRangePayloadValue(range?.rightArea?.activePayload, name) - : getRangePayloadValue(range?.rightArea?.activePayload, name) / - getRangePayloadValue(range?.leftArea?.activePayload, name)) * + ? getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name) / + getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) + : getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) / + getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * 100) * -1 : 0; diff --git a/src/components/chart-elements/common/DeltaCalculationProps.tsx b/src/components/chart-elements/common/DeltaCalculationProps.tsx new file mode 100644 index 000000000..3a6871910 --- /dev/null +++ b/src/components/chart-elements/common/DeltaCalculationProps.tsx @@ -0,0 +1,6 @@ +interface DeltaCalculationProps { + leftArea?: any; + rightArea?: any; +} + +export default DeltaCalculationProps; diff --git a/src/components/chart-elements/common/RangeReferenceShape.tsx b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx similarity index 87% rename from src/components/chart-elements/common/RangeReferenceShape.tsx rename to src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx index 05f46f427..ddc1da0c0 100644 --- a/src/components/chart-elements/common/RangeReferenceShape.tsx +++ b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx @@ -1,14 +1,14 @@ import { tremorTwMerge } from "lib"; import React from "react"; -interface RangeReferenceShapeProps { +interface DeltaCalculationReferenceShapeProps { x: number; y: number; width: number; height: number; } -const RangeReferenceShape = (props: RangeReferenceShapeProps) => { +const DeltaCalculationReferenceShape = (props: DeltaCalculationReferenceShapeProps) => { const { x, y, width, height } = props; return ( @@ -66,4 +66,4 @@ const RangeReferenceShape = (props: RangeReferenceShapeProps) => { ); }; -export default RangeReferenceShape; +export default DeltaCalculationReferenceShape; diff --git a/src/components/chart-elements/common/RangeProps.tsx b/src/components/chart-elements/common/RangeProps.tsx deleted file mode 100644 index 6fe78bf36..000000000 --- a/src/components/chart-elements/common/RangeProps.tsx +++ /dev/null @@ -1,6 +0,0 @@ -interface RangeProps { - leftArea?: any; - rightArea?: any; -} - -export default RangeProps; diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index 4feb4c27b..8548c7e4c 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -200,3 +200,10 @@ WithShortAnimationDuration.args = { categories: ["Sales", "Successful Payments"], index: "month", }; + +export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); +WithEnableDeltaCalculation.args = { + ...args, + data: data, + enableDeltaCalculation: true +}; \ No newline at end of file diff --git a/src/stories/chart-elements/BarChart.stories.tsx b/src/stories/chart-elements/BarChart.stories.tsx index a7ffad6b4..94cca2935 100644 --- a/src/stories/chart-elements/BarChart.stories.tsx +++ b/src/stories/chart-elements/BarChart.stories.tsx @@ -197,3 +197,10 @@ WithShortAnimationDuration.args = { categories: ["Sales", "Successful Payments"], index: "month", }; + +export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); +WithEnableDeltaCalculation.args = { + ...args, + data: data, + enableDeltaCalculation: true +}; diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index 7dfa4b300..ed1dc1cc6 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -186,3 +186,10 @@ WithShortAnimationDuration.args = { categories: ["Sales", "Successful Payments"], index: "month", }; + +export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); +WithEnableDeltaCalculation.args = { + ...args, + data: data, + enableDeltaCalculation: true +}; From 7a41a3ba84d9309f595ae23257f8a87e9958a398 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 18:55:24 +0200 Subject: [PATCH 07/17] fix lint --- .../chart-elements/AreaChart/AreaChart.tsx | 12 ++++++++++-- .../chart-elements/BarChart/BarChart.tsx | 18 +++++++++++++++--- .../chart-elements/LineChart/LineChart.tsx | 12 ++++++++++-- .../chart-elements/common/ChartTooltip.tsx | 17 ++++++++++------- .../chart-elements/AreaChart.stories.tsx | 4 ++-- .../chart-elements/BarChart.stories.tsx | 2 +- .../chart-elements/LineChart.stories.tsx | 2 +- 7 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index d8b21e548..effa079a7 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -79,7 +79,11 @@ const AreaChart = React.forwardRef((props, ref) enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} - onMouseMove={(e) => (enableDeltaCalculation && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseMove={(e) => + enableDeltaCalculation && + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) + } onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( @@ -145,7 +149,11 @@ const AreaChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, + strokeWidth: + deltaCalculation.leftArea?.activeLabel && + deltaCalculation.rightArea?.activeLabel + ? 0 + : 1, }} content={({ active, payload, label }) => ( ((props, ref) => data={data} stackOffset={relative ? "expand" : "none"} layout={layout === "vertical" ? "vertical" : "horizontal"} - onMouseDown={(e) => (enableDeltaCalculation && layout === "horizontal") && setDeltaCalculation({ leftArea: e })} - onMouseMove={(e) => (enableDeltaCalculation && layout === "horizontal" && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseDown={(e) => + enableDeltaCalculation && + layout === "horizontal" && + setDeltaCalculation({ leftArea: e }) + } + onMouseMove={(e) => + enableDeltaCalculation && + layout === "horizontal" && + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) + } onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( @@ -192,7 +201,10 @@ const BarChart = React.forwardRef((props, ref) => cursor={{ fill: "#d1d5db", opacity: - deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? "0" : "0.15", + deltaCalculation.leftArea?.activeLabel && + deltaCalculation.rightArea?.activeLabel + ? "0" + : "0.15", }} content={({ active, payload, label }) => ( ((props, ref) enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} - onMouseMove={(e) => (enableDeltaCalculation && deltaCalculation.leftArea) && setDeltaCalculation((prev) => ({ ...prev, rightArea: e }))} + onMouseMove={(e) => + enableDeltaCalculation && + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) + } onMouseUp={() => setDeltaCalculation({})} > {showGridLines ? ( @@ -144,7 +148,11 @@ const LineChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, + strokeWidth: + deltaCalculation.leftArea?.activeLabel && + deltaCalculation.rightArea?.activeLabel + ? 0 + : 1, }} content={({ active, payload, label }) => ( { const hasRange = Boolean(deltaCalculation?.leftArea && deltaCalculation?.rightArea); - if (hasRange && deltaCalculation?.leftArea?.activeLabel === deltaCalculation?.rightArea?.activeLabel) return null; + if ( + hasRange && + deltaCalculation?.leftArea?.activeLabel === deltaCalculation?.rightArea?.activeLabel + ) + return null; if (active && payload) { return ( @@ -133,8 +137,8 @@ const ChartTooltip = ({
{payload.map(({ value, name }: { value: number; name: string }, idx: number) => { - const isBeforeLeftValue = deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; - + const isBeforeLeftValue = + deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; const displayedValue = hasRange ? (getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) - getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * @@ -145,12 +149,11 @@ const ChartTooltip = ({ (isBeforeLeftValue ? getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name) / getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) - : getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) / - getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * - 100) * + : (getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) / + getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * + 100)) * -1 : 0; - const percentageValue = hasRange ? `(${percentage.toFixed(2)}%)` : ""; return ( diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index 8548c7e4c..8343dd762 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -205,5 +205,5 @@ export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); WithEnableDeltaCalculation.args = { ...args, data: data, - enableDeltaCalculation: true -}; \ No newline at end of file + enableDeltaCalculation: true, +}; diff --git a/src/stories/chart-elements/BarChart.stories.tsx b/src/stories/chart-elements/BarChart.stories.tsx index 94cca2935..8465a8db9 100644 --- a/src/stories/chart-elements/BarChart.stories.tsx +++ b/src/stories/chart-elements/BarChart.stories.tsx @@ -202,5 +202,5 @@ export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); WithEnableDeltaCalculation.args = { ...args, data: data, - enableDeltaCalculation: true + enableDeltaCalculation: true, }; diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index ed1dc1cc6..a7dd6979e 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -191,5 +191,5 @@ export const WithEnableDeltaCalculation = DefaultTemplate.bind({}); WithEnableDeltaCalculation.args = { ...args, data: data, - enableDeltaCalculation: true + enableDeltaCalculation: true, }; From d9bac2506fc40a76dab656216a23b0d2972cf896 Mon Sep 17 00:00:00 2001 From: mbauchet Date: Wed, 13 Sep 2023 19:06:02 +0200 Subject: [PATCH 08/17] remove numberofmonth (error) --- .../input-elements/DateRangePicker/DateRangePicker.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/input-elements/DateRangePicker/DateRangePicker.tsx b/src/components/input-elements/DateRangePicker/DateRangePicker.tsx index 5aad14d55..6fdba43b3 100644 --- a/src/components/input-elements/DateRangePicker/DateRangePicker.tsx +++ b/src/components/input-elements/DateRangePicker/DateRangePicker.tsx @@ -259,7 +259,6 @@ const DateRangePicker = React.forwardRef(( from: selectedStartDate, to: selectedEndDate, }} - numberOfMonths={1} onSelect={ ((v: DateRange) => { onValueChange?.({ from: v?.from, to: v?.to }); From 963156a56e79042bb45d0fd196ec552b30faf455 Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Thu, 28 Sep 2023 16:23:02 +0200 Subject: [PATCH 09/17] fix mouse event propagation --- .../chart-elements/AreaChart/AreaChart.tsx | 26 ++++++++++------- .../chart-elements/BarChart/BarChart.tsx | 28 +++++++++++-------- .../chart-elements/LineChart/LineChart.tsx | 24 ++++++++++------ .../chart-elements/AreaChart.stories.tsx | 11 +++++++- .../chart-elements/BarChart.stories.tsx | 2 +- .../chart-elements/LineChart.stories.tsx | 2 +- 6 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index d4c287b6a..86316642d 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -137,13 +137,20 @@ const AreaChart = React.forwardRef((props, ref) {data?.length ? ( enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} - onMouseMove={(e) => + onMouseDown={(value, e) => { + e.stopPropagation(); + enableDeltaCalculation && setDeltaCalculation({ leftArea: value }); + }} + onMouseMove={(value, e) => { + e.stopPropagation(); enableDeltaCalculation && - deltaCalculation.leftArea && - setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) - } - onMouseUp={() => setDeltaCalculation({})} + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); + }} + onMouseUp={(_, e) => { + e.stopPropagation(); + setDeltaCalculation({}); + }} onClick={ hasOnValueChange && (activeLegend || activeDot) ? () => { @@ -217,8 +224,7 @@ const AreaChart = React.forwardRef((props, ref) cursor={{ stroke: "#d1d5db", strokeWidth: - deltaCalculation.leftArea?.activeLabel && - deltaCalculation.rightArea?.activeLabel + deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, }} @@ -354,12 +360,12 @@ const AreaChart = React.forwardRef((props, ref) dataKey, index: idx, } = props; - + if ( (hasOnlyOneValueForThisKey(data, category) && !(activeDot || (activeLegend && activeLegend !== category))) || (activeDot?.index === idx && activeDot?.dataKey === category) || - (payload[index] === deltaCalculation?.leftArea?.activeLabel) + payload[index] === deltaCalculation?.leftArea?.activeLabel ) { return ( ((props, ref) => data={data} stackOffset={relative ? "expand" : "none"} layout={layout === "vertical" ? "vertical" : "horizontal"} - onMouseDown={(e) => + onMouseDown={(value, e) => { + e.stopPropagation(); enableDeltaCalculation && - layout === "horizontal" && - setDeltaCalculation({ leftArea: e }) - } - onMouseMove={(e) => + layout === "horizontal" && + setDeltaCalculation({ leftArea: value }); + }} + onMouseMove={(value, e) => { + e.stopPropagation(); enableDeltaCalculation && - layout === "horizontal" && - deltaCalculation.leftArea && - setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) - } - onMouseUp={() => setDeltaCalculation({})} + layout === "horizontal" && + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); + }} + onMouseUp={(_, e) => { + e.stopPropagation(); + setDeltaCalculation({}); + }} onClick={ hasOnValueChange && (activeLegend || activeBar) ? () => { @@ -266,8 +271,7 @@ const BarChart = React.forwardRef((props, ref) => cursor={{ stroke: "#d1d5db", strokeWidth: - deltaCalculation.leftArea?.activeLabel && - deltaCalculation.rightArea?.activeLabel + deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, }} diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index b085d85c1..9d6bdeabd 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -135,13 +135,20 @@ const LineChart = React.forwardRef((props, ref) {data?.length ? ( enableDeltaCalculation && setDeltaCalculation({ leftArea: e })} - onMouseMove={(e) => + onMouseDown={(value, e) => { + e.stopPropagation(); + enableDeltaCalculation && setDeltaCalculation({ leftArea: value }); + }} + onMouseMove={(value, e) => { + e.stopPropagation(); enableDeltaCalculation && - deltaCalculation.leftArea && - setDeltaCalculation((prev) => ({ ...prev, rightArea: e })) - } - onMouseUp={() => setDeltaCalculation({})} + deltaCalculation.leftArea && + setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); + }} + onMouseUp={(_, e) => { + e.stopPropagation(); + setDeltaCalculation({}); + }} onClick={ hasOnValueChange && (activeLegend || activeDot) ? () => { @@ -214,8 +221,7 @@ const LineChart = React.forwardRef((props, ref) cursor={{ stroke: "#d1d5db", strokeWidth: - deltaCalculation.leftArea?.activeLabel && - deltaCalculation.rightArea?.activeLabel + deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? 0 : 1, }} @@ -299,7 +305,7 @@ const LineChart = React.forwardRef((props, ref) cy, dataKey, index: idx, - payload + payload, } = props; if ( diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index 592950f95..80d5ba251 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -222,7 +222,7 @@ WithEnableDeltaCalculation.args = { ...args, data: data, enableDeltaCalculation: true, -} +}; export const WithOnValueChange = DefaultTemplate.bind({}); // More on args: https://storybook.js.org/docs/react/writing-stories/args @@ -232,6 +232,15 @@ WithOnValueChange.args = { data, }; +export const WithOnValueChangeAndEnableDeltaCalculation = DefaultTemplate.bind({}); +// More on args: https://storybook.js.org/docs/react/writing-stories/args +WithOnValueChangeAndEnableDeltaCalculation.args = { + ...args, + onValueChange: (v: any) => alert(JSON.stringify(v)), + data, + enableDeltaCalculation: true, +}; + export const WithOneDataValue = ResponsiveTemplate.bind({}); WithOneDataValue.args = { ...args, diff --git a/src/stories/chart-elements/BarChart.stories.tsx b/src/stories/chart-elements/BarChart.stories.tsx index a7815c646..b7a05d1c5 100644 --- a/src/stories/chart-elements/BarChart.stories.tsx +++ b/src/stories/chart-elements/BarChart.stories.tsx @@ -208,7 +208,7 @@ WithEnableDeltaCalculation.args = { ...args, data: data, enableDeltaCalculation: true, -} +}; export const WithOnValueChange = ResponsiveTemplate.bind({}); // More on args: https://storybook.js.org/docs/react/writing-stories/args diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index 766047e90..0d160ef67 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -219,7 +219,7 @@ WithEnableDeltaCalculation.args = { ...args, data: data, enableDeltaCalculation: true, -} +}; export const WithoutOnClickAnimation = DefaultTemplate.bind({}); WithoutOnClickAnimation.args = { From e02d536b27414eb3d179e7fc505e276b6c0214a4 Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Fri, 29 Sep 2023 11:38:30 +0200 Subject: [PATCH 10/17] update style --- .../chart-elements/AreaChart/AreaChart.tsx | 50 +++++++++++-------- .../chart-elements/LineChart/LineChart.tsx | 15 +++--- .../chart-elements/common/ChartTooltip.tsx | 39 ++++++++++----- .../common/DeltaCalculationReferenceShape.tsx | 2 +- 4 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 86316642d..4d788638e 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -78,7 +78,7 @@ const AreaChart = React.forwardRef((props, ref) onValueChange, ...other } = props; - const [deltaCalculation, setDeltaCalculation] = useState({}); + const [deltaCalculation, setDeltaCalculation] = useState(null); const [legendHeight, setLegendHeight] = useState(60); const [activeDot, setActiveDot] = useState(undefined); const [activeLegend, setActiveLegend] = useState(undefined); @@ -86,6 +86,8 @@ const AreaChart = React.forwardRef((props, ref) const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); const hasOnValueChange = !!onValueChange; + const hasDeltaCalculation = deltaCalculation && deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel + function onDotClick(itemData: any, event: React.MouseEvent) { event.stopPropagation(); @@ -131,35 +133,40 @@ const AreaChart = React.forwardRef((props, ref) } setActiveDot(undefined); } + return (
{data?.length ? ( { + setActiveDot(undefined); + setActiveLegend(undefined); + onValueChange?.(null); + } + : undefined + } + //had to fix legend click onMouseDown={(value, e) => { e.stopPropagation(); enableDeltaCalculation && setDeltaCalculation({ leftArea: value }); }} onMouseMove={(value, e) => { e.stopPropagation(); - enableDeltaCalculation && + enableDeltaCalculation && + deltaCalculation && deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} - onMouseUp={(_, e) => { + onMouseUp={(value, e) => { e.stopPropagation(); - setDeltaCalculation({}); + enableDeltaCalculation && + hasDeltaCalculation && + setDeltaCalculation(null); }} - onClick={ - hasOnValueChange && (activeLegend || activeDot) - ? () => { - setActiveDot(undefined); - setActiveLegend(undefined); - onValueChange?.(null); - } - : undefined - } > {" "} {showGridLines ? ( @@ -223,10 +230,7 @@ const AreaChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: - deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel - ? 0 - : 1, + strokeWidth: hasDeltaCalculation ? 0 : 1 }} content={ showTooltip ? ( @@ -266,7 +270,8 @@ const AreaChart = React.forwardRef((props, ref) {categories.map((category) => { return ( - {showGradient ? ( + {!hasDeltaCalculation ? ( + showGradient ? ( ((props, ref) y2="1" > ((props, ref) } /> - )} + ) + ) : ( + null + )} ); })} @@ -405,7 +413,7 @@ const AreaChart = React.forwardRef((props, ref) connectNulls={connectNulls} /> ))} - {deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? ( + {hasDeltaCalculation ? ( ((props, ref) onValueChange, ...other } = props; - const [deltaCalculation, setDeltaCalculation] = useState({}); + const [deltaCalculation, setDeltaCalculation] = useState(null); const [legendHeight, setLegendHeight] = useState(60); const [activeDot, setActiveDot] = useState(undefined); @@ -83,6 +83,7 @@ const LineChart = React.forwardRef((props, ref) const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); const hasOnValueChange = !!onValueChange; + const hasDeltaCalculation = deltaCalculation && deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel function onDotClick(itemData: any, event: React.MouseEvent) { event.stopPropagation(); @@ -142,12 +143,15 @@ const LineChart = React.forwardRef((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && + deltaCalculation && deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(_, e) => { e.stopPropagation(); - setDeltaCalculation({}); + enableDeltaCalculation && + hasDeltaCalculation && + setDeltaCalculation(null); }} onClick={ hasOnValueChange && (activeLegend || activeDot) @@ -220,10 +224,7 @@ const LineChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: - deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel - ? 0 - : 1, + strokeWidth: hasDeltaCalculation ? 0 : 1 }} content={ showTooltip ? ( @@ -350,7 +351,7 @@ const LineChart = React.forwardRef((props, ref) connectNulls={connectNulls} /> ))} - {deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel ? ( + {hasDeltaCalculation ? ( 0) { + return "text-emerald-600 dark:text-emerald-500"; + } else if (value < 0) { + return "text-red-600 dark:text-red-500"; + } else { + return "text-gray-500 dark:text-white"; + } +} + export const ChartTooltipFrame = ({ children }: { children: React.ReactNode }) => (
@@ -65,12 +77,12 @@ export const ChartTooltipRow = ({ value, name, color, textColor }: ChartTooltipR

{value} @@ -84,7 +96,7 @@ export interface ChartTooltipProps { label: string; categoryColors: Map; valueFormatter: ValueFormatter; - deltaCalculation?: DeltaCalculationProps; + deltaCalculation?: DeltaCalculationProps | null; } const ChartTooltip = ({ @@ -139,24 +151,27 @@ const ChartTooltip = ({

{filteredPayload.map(({ value, name }: { value: number; name: string }, idx: number) => { + const rightRangePayloadValue = hasRange && getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name); + const leftRangePayloadValue = hasRange && getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name); + const isBeforeLeftValue = deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; const displayedValue = hasRange - ? (getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) - - getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * + ? (rightRangePayloadValue - + leftRangePayloadValue) * (isBeforeLeftValue ? -1 : 1) : value; const percentage = hasRange ? (100 - (isBeforeLeftValue - ? getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name) / - getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) - : (getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name) / - getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name)) * + ? leftRangePayloadValue / + rightRangePayloadValue + : (rightRangePayloadValue / + leftRangePayloadValue) * 100)) * -1 : 0; - const percentageValue = hasRange ? `(${percentage.toFixed(2)}%)` : ""; + const percentageValue = hasRange ? `(${percentage > 0 ? "+" : ""}${percentage.toFixed(1)}%)` : ""; return ( = 0 ? "green" : "red") : null} + textColor={hasRange ? getTooltipValueColor(displayedValue) : null} /> ); })} diff --git a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx index 381e9ef61..7245b22ab 100644 --- a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx +++ b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx @@ -37,7 +37,7 @@ const DeltaCalculationReferenceShape = (props: DeltaCalculationReferenceShapePro x2={x} y1={y} y2={y + height} - strokeDasharray={"3 3"} + strokeDasharray={"2 2"} className={tremorTwMerge( // common "stroke-1", From ec9a857588f13b7b9e36f4bc472dce012d0b416d Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Fri, 29 Sep 2023 12:14:19 +0200 Subject: [PATCH 11/17] fix percentage calculation --- .../chart-elements/AreaChart/AreaChart.tsx | 124 +++++++++--------- .../chart-elements/LineChart/LineChart.tsx | 13 +- .../chart-elements/common/ChartTooltip.tsx | 35 ++--- 3 files changed, 86 insertions(+), 86 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 4d788638e..818813ab3 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -86,8 +86,10 @@ const AreaChart = React.forwardRef((props, ref) const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); const hasOnValueChange = !!onValueChange; - const hasDeltaCalculation = deltaCalculation && deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel - + const hasDeltaCalculation = + deltaCalculation && + deltaCalculation.leftArea?.activeLabel && + deltaCalculation.rightArea?.activeLabel; function onDotClick(itemData: any, event: React.MouseEvent) { event.stopPropagation(); @@ -133,7 +135,7 @@ const AreaChart = React.forwardRef((props, ref) } setActiveDot(undefined); } - + return (
@@ -141,31 +143,29 @@ const AreaChart = React.forwardRef((props, ref) { - setActiveDot(undefined); - setActiveLegend(undefined); - onValueChange?.(null); - } - : undefined - } - //had to fix legend click + hasOnValueChange && (activeLegend || activeDot) + ? () => { + setActiveDot(undefined); + setActiveLegend(undefined); + onValueChange?.(null); + } + : undefined + } + //had to fix legend click onMouseDown={(value, e) => { e.stopPropagation(); enableDeltaCalculation && setDeltaCalculation({ leftArea: value }); }} onMouseMove={(value, e) => { e.stopPropagation(); - enableDeltaCalculation && - deltaCalculation && + enableDeltaCalculation && + deltaCalculation && deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(value, e) => { e.stopPropagation(); - enableDeltaCalculation && - hasDeltaCalculation && - setDeltaCalculation(null); + enableDeltaCalculation && hasDeltaCalculation && setDeltaCalculation(null); }} > {" "} @@ -230,7 +230,7 @@ const AreaChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: hasDeltaCalculation ? 0 : 1 + strokeWidth: hasDeltaCalculation ? 0 : 1, }} content={ showTooltip ? ( @@ -272,53 +272,51 @@ const AreaChart = React.forwardRef((props, ref) {!hasDeltaCalculation ? ( showGradient ? ( - - - - - ) : ( - - + + + + ) : ( + - - ) - ) : ( - null - )} + id={categoryColors.get(category)} + x1="0" + y1="0" + x2="0" + y2="1" + > + + + ) + ) : null} ); })} diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index 970352f59..8426a0aaa 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -83,7 +83,10 @@ const LineChart = React.forwardRef((props, ref) const yAxisDomain = getYAxisDomain(autoMinValue, minValue, maxValue); const hasOnValueChange = !!onValueChange; - const hasDeltaCalculation = deltaCalculation && deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel + const hasDeltaCalculation = + deltaCalculation && + deltaCalculation.leftArea?.activeLabel && + deltaCalculation.rightArea?.activeLabel; function onDotClick(itemData: any, event: React.MouseEvent) { event.stopPropagation(); @@ -143,15 +146,13 @@ const LineChart = React.forwardRef((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && - deltaCalculation && + deltaCalculation && deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(_, e) => { e.stopPropagation(); - enableDeltaCalculation && - hasDeltaCalculation && - setDeltaCalculation(null); + enableDeltaCalculation && hasDeltaCalculation && setDeltaCalculation(null); }} onClick={ hasOnValueChange && (activeLegend || activeDot) @@ -224,7 +225,7 @@ const LineChart = React.forwardRef((props, ref) isAnimationActive={false} cursor={{ stroke: "#d1d5db", - strokeWidth: hasDeltaCalculation ? 0 : 1 + strokeWidth: hasDeltaCalculation ? 0 : 1, }} content={ showTooltip ? ( diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index 16088200b..b27d42d7d 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -6,9 +6,12 @@ import { BaseColors, border, getColorClassNames, sizing, spacing } from "lib"; import { colorPalette } from "lib/theme"; import DeltaCalculationProps from "components/chart-elements/common/DeltaCalculationProps"; -type TooltipValueColors = "text-emerald-600 dark:text-emerald-500" | "text-red-600 dark:text-red-500" | "text-gray-500 dark:text-white"; +type TooltipValueColors = + | "text-emerald-600 dark:text-emerald-500" + | "text-red-600 dark:text-red-500" + | "text-gray-500 dark:text-white"; -function getTooltipValueColor (value: number): TooltipValueColors { +function getTooltipValueColor(value: number): TooltipValueColors { if (value > 0) { return "text-emerald-600 dark:text-emerald-500"; } else if (value < 0) { @@ -151,27 +154,25 @@ const ChartTooltip = ({
{filteredPayload.map(({ value, name }: { value: number; name: string }, idx: number) => { - const rightRangePayloadValue = hasRange && getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name); - const leftRangePayloadValue = hasRange && getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name); - + const rightRangePayloadValue = + hasRange && getRangePayloadValue(deltaCalculation?.rightArea?.activePayload, name); + const leftRangePayloadValue = + hasRange && getRangePayloadValue(deltaCalculation?.leftArea?.activePayload, name); + const isBeforeLeftValue = deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; const displayedValue = hasRange - ? (rightRangePayloadValue - - leftRangePayloadValue) * - (isBeforeLeftValue ? -1 : 1) + ? (rightRangePayloadValue - leftRangePayloadValue) * (isBeforeLeftValue ? -1 : 1) : value; const percentage = hasRange - ? (100 - - (isBeforeLeftValue - ? leftRangePayloadValue / - rightRangePayloadValue - : (rightRangePayloadValue / - leftRangePayloadValue) * - 100)) * - -1 + ? (isBeforeLeftValue + ? (-1 * (rightRangePayloadValue - leftRangePayloadValue)) / leftRangePayloadValue + : (rightRangePayloadValue - leftRangePayloadValue) / rightRangePayloadValue) * 100 : 0; - const percentageValue = hasRange ? `(${percentage > 0 ? "+" : ""}${percentage.toFixed(1)}%)` : ""; + + const percentageValue = hasRange + ? `(${percentage > 0 ? "+" : ""}${percentage.toFixed(1)}%)` + : ""; return ( Date: Fri, 29 Sep 2023 13:53:12 +0200 Subject: [PATCH 12/17] add area for range selection --- .../chart-elements/AreaChart/AreaChart.tsx | 162 +- .../chart-elements/common/ChartTooltip.tsx | 4 +- .../common/DeltaCalculationReferenceShape.tsx | 9 +- .../chart-elements/AreaChart.stories.tsx | 8 + .../chart-elements/LineChart.stories.tsx | 8 + .../chart-elements/helpers/largeDataSet.json | 3397 +++++++++++++++++ 6 files changed, 3533 insertions(+), 55 deletions(-) create mode 100644 src/stories/chart-elements/helpers/largeDataSet.json diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 818813ab3..5317d5f74 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -91,6 +91,34 @@ const AreaChart = React.forwardRef((props, ref) deltaCalculation.leftArea?.activeLabel && deltaCalculation.rightArea?.activeLabel; + const isBeforeLeftValue = + deltaCalculation?.leftArea?.chartX > deltaCalculation?.rightArea?.chartX; + const deltaCalculationData = + hasDeltaCalculation && + deltaCalculation.leftArea?.activeLabel !== deltaCalculation.rightArea?.activeLabel + ? data.map((item, idx) => { + if ( + isBeforeLeftValue + ? idx <= deltaCalculation.leftArea.activeTooltipIndex && + idx >= deltaCalculation.rightArea.activeTooltipIndex + : idx >= deltaCalculation.leftArea.activeTooltipIndex && + idx <= deltaCalculation.rightArea.activeTooltipIndex + ) { + return { + ...item, + ...categories.reduce((acc, category) => { + return { + ...acc, + [`${category}TremorRange`]: item[category], + }; + }, {}), + }; + } + + return item; + }) + : data; + function onDotClick(itemData: any, event: React.MouseEvent) { event.stopPropagation(); @@ -139,9 +167,9 @@ const AreaChart = React.forwardRef((props, ref) return (
- {data?.length ? ( + {deltaCalculationData?.length ? ( { @@ -270,53 +298,51 @@ const AreaChart = React.forwardRef((props, ref) {categories.map((category) => { return ( - {!hasDeltaCalculation ? ( - showGradient ? ( - + - - - - ) : ( - + + + ) : ( + + - - - ) - ) : null} + /> + + )} ); })} @@ -328,7 +354,11 @@ const AreaChart = React.forwardRef((props, ref) colorPalette.text, ).strokeColor } - strokeOpacity={activeDot || (activeLegend && activeLegend !== category) ? 0.3 : 1} + strokeOpacity={ + activeDot || (activeLegend && activeLegend !== category) || hasDeltaCalculation + ? 0.3 + : 1 + } activeDot={(props: any) => { const { cx, cy, stroke, strokeLinecap, strokeLinejoin, strokeWidth, dataKey } = props; @@ -401,7 +431,7 @@ const AreaChart = React.forwardRef((props, ref) type={curveType} dataKey={category} stroke="" - fill={`url(#${categoryColors.get(category)})`} + fill={hasDeltaCalculation ? "transparent" : `url(#${categoryColors.get(category)})`} strokeWidth={2} strokeLinejoin="round" strokeLinecap="round" @@ -417,10 +447,42 @@ const AreaChart = React.forwardRef((props, ref) x2={deltaCalculation.rightArea.activeLabel} fillOpacity={0.2} shape={({ x, y, width, height }) => ( - + )} /> ) : null} + {hasDeltaCalculation + ? categories.map((category) => ( + + )) + : null} {onValueChange ? categories.map((category) => ( 0 ? "+" : ""}${valueFormatter( + displayedValue, + )} ${percentageValue}`} name={name} color={categoryColors.get(name) ?? BaseColors.Blue} textColor={hasRange ? getTooltipValueColor(displayedValue) : null} diff --git a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx index 7245b22ab..32c466b70 100644 --- a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx +++ b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx @@ -6,10 +6,11 @@ interface DeltaCalculationReferenceShapeProps { y: number; width: number; height: number; + fill?: boolean; } const DeltaCalculationReferenceShape = (props: DeltaCalculationReferenceShapeProps) => { - const { x, y, width, height } = props; + const { x, y, width, height, fill = true } = props; return ( <> @@ -26,9 +27,9 @@ const DeltaCalculationReferenceShape = (props: DeltaCalculationReferenceShapePro // common "text-tremor-label", // light - "fill-tremor-content-subtle", //gray-300 could be nice - // dark - "dark:fill-dark-tremor-content-subtle", + fill + ? "fill-tremor-content-subtle dark:fill-dark-tremor-content-subtle" + : "fill-transparent", )} fillOpacity={0.2} /> diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index 80d5ba251..ef5697ad4 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -266,3 +266,11 @@ WithOneAndMultipleDataValueAndOnValueChange.args = { data: singleAndMultipleData, onValueChange: (v: any) => alert(JSON.stringify(v)), }; + +export const WithLargeDataSetAndEnableDeltaCalculation = DefaultTemplate.bind({}); +WithLargeDataSetAndEnableDeltaCalculation.args = { + ...args, + data: require("./helpers/largeDataSet.json"), + index: "date", + enableDeltaCalculation: true, +}; diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index 0d160ef67..11abc6698 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -261,3 +261,11 @@ WithOneAndMultipleDataValueAndOnValueChange.args = { data: singleAndMultipleData, onValueChange: (v: any) => alert(JSON.stringify(v)), }; + +export const WithLargeDataSetAndEnableDeltaCalculation = DefaultTemplate.bind({}); +WithLargeDataSetAndEnableDeltaCalculation.args = { + ...args, + data: require("./helpers/largeDataSet.json"), + index: "date", + enableDeltaCalculation: true, +}; diff --git a/src/stories/chart-elements/helpers/largeDataSet.json b/src/stories/chart-elements/helpers/largeDataSet.json new file mode 100644 index 000000000..825247bc1 --- /dev/null +++ b/src/stories/chart-elements/helpers/largeDataSet.json @@ -0,0 +1,3397 @@ +[ + { + "date": "2022-01-01", + "Sales": 148, + "Successful Payments": 114 + }, + { + "date": "2022-01-02", + "Sales": 107, + "Successful Payments": 134 + }, + { + "date": "2022-01-03", + "Sales": 100, + "Successful Payments": 145 + }, + { + "date": "2022-01-04", + "Sales": 108, + "Successful Payments": 111 + }, + { + "date": "2022-01-05", + "Sales": 127, + "Successful Payments": 102 + }, + { + "date": "2022-01-06", + "Sales": 109, + "Successful Payments": 111 + }, + { + "date": "2022-01-07", + "Sales": 133, + "Successful Payments": 144 + }, + { + "date": "2022-01-08", + "Sales": 126, + "Successful Payments": 109 + }, + { + "date": "2022-01-09", + "Sales": 128, + "Successful Payments": 132 + }, + { + "date": "2022-01-10", + "Sales": 131, + "Successful Payments": 112 + }, + { + "date": "2022-01-11", + "Sales": 134, + "Successful Payments": 132 + }, + { + "date": "2022-01-13", + "Sales": 150, + "Successful Payments": 106 + }, + { + "date": "2022-01-14", + "Sales": 109, + "Successful Payments": 111 + }, + { + "date": "2022-01-15", + "Sales": 150, + "Successful Payments": 136 + }, + { + "date": "2022-01-16", + "Sales": 106, + "Successful Payments": 128 + }, + { + "date": "2022-01-17", + "Sales": 135, + "Successful Payments": 105 + }, + { + "date": "2022-01-18", + "Sales": 122, + "Successful Payments": 116 + }, + { + "date": "2022-01-19", + "Sales": 101, + "Successful Payments": 104 + }, + { + "date": "2022-01-20", + "Sales": 116, + "Successful Payments": 141 + }, + { + "date": "2022-01-21", + "Sales": 134, + "Successful Payments": 135 + }, + { + "date": "2022-01-22", + "Sales": 137, + "Successful Payments": 135 + }, + { + "date": "2022-01-23", + "Sales": 147, + "Successful Payments": 140 + }, + { + "date": "2022-01-24", + "Sales": 122, + "Successful Payments": 110 + }, + { + "date": "2022-01-25", + "Sales": 130, + "Successful Payments": 134 + }, + { + "date": "2022-01-26", + "Sales": 145, + "Successful Payments": 122 + }, + { + "date": "2022-01-27", + "Sales": 149, + "Successful Payments": 121 + }, + { + "date": "2022-01-28", + "Sales": 122, + "Successful Payments": 103 + }, + { + "date": "2022-01-29", + "Sales": 131, + "Successful Payments": 131 + }, + { + "date": "2022-01-30", + "Sales": 112, + "Successful Payments": 109 + }, + { + "date": "2022-01-31", + "Sales": 147, + "Successful Payments": 149 + }, + { + "date": "2022-02-01", + "Sales": 108, + "Successful Payments": 119 + }, + { + "date": "2022-02-02", + "Sales": 136, + "Successful Payments": 116 + }, + { + "date": "2022-02-03", + "Sales": 104, + "Successful Payments": 150 + }, + { + "date": "2022-02-05", + "Sales": 112, + "Successful Payments": 132 + }, + { + "date": "2022-02-06", + "Sales": 130, + "Successful Payments": 126 + }, + { + "date": "2022-02-07", + "Sales": 143, + "Successful Payments": 114 + }, + { + "date": "2022-02-08", + "Sales": 114, + "Successful Payments": 109 + }, + { + "date": "2022-02-09", + "Sales": 137, + "Successful Payments": 129 + }, + { + "date": "2022-02-10", + "Sales": 139, + "Successful Payments": 134 + }, + { + "date": "2022-02-11", + "Sales": 120, + "Successful Payments": 137 + }, + { + "date": "2022-02-12", + "Sales": 147, + "Successful Payments": 142 + }, + { + "date": "2022-02-14", + "Sales": 114, + "Successful Payments": 124 + }, + { + "date": "2022-02-15", + "Sales": 132, + "Successful Payments": 100 + }, + { + "date": "2022-02-16", + "Sales": 113, + "Successful Payments": 124 + }, + { + "date": "2022-02-17", + "Sales": 128, + "Successful Payments": 142 + }, + { + "date": "2022-02-18", + "Sales": 115, + "Successful Payments": 140 + }, + { + "date": "2022-02-19", + "Sales": 150, + "Successful Payments": 122 + }, + { + "date": "2022-02-20", + "Sales": 145, + "Successful Payments": 123 + }, + { + "date": "2022-02-21", + "Sales": 116, + "Successful Payments": 135 + }, + { + "date": "2022-02-22", + "Sales": 118, + "Successful Payments": 141 + }, + { + "date": "2022-02-23", + "Sales": 128, + "Successful Payments": 113 + }, + { + "date": "2022-02-24", + "Sales": 150, + "Successful Payments": 120 + }, + { + "date": "2022-02-25", + "Sales": 105, + "Successful Payments": 138 + }, + { + "date": "2022-02-26", + "Sales": 108, + "Successful Payments": 127 + }, + { + "date": "2022-02-27", + "Sales": 150, + "Successful Payments": 134 + }, + { + "date": "2022-02-28", + "Sales": 119, + "Successful Payments": 147 + }, + { + "date": "2022-03-02", + "Sales": 113, + "Successful Payments": 123 + }, + { + "date": "2022-03-03", + "Sales": 129, + "Successful Payments": 137 + }, + { + "date": "2022-03-04", + "Sales": 116, + "Successful Payments": 127 + }, + { + "date": "2022-03-05", + "Sales": 109, + "Successful Payments": 103 + }, + { + "date": "2022-03-06", + "Sales": 107, + "Successful Payments": 142 + }, + { + "date": "2022-03-07", + "Sales": 110, + "Successful Payments": 134 + }, + { + "date": "2022-03-08", + "Sales": 125, + "Successful Payments": 101 + }, + { + "date": "2022-03-09", + "Sales": 124, + "Successful Payments": 132 + }, + { + "date": "2022-03-10", + "Sales": 131, + "Successful Payments": 101 + }, + { + "date": "2022-03-11", + "Sales": 139, + "Successful Payments": 121 + }, + { + "date": "2022-03-12", + "Sales": 132, + "Successful Payments": 118 + }, + { + "date": "2022-03-13", + "Sales": 102, + "Successful Payments": 134 + }, + { + "date": "2022-03-14", + "Sales": 133, + "Successful Payments": 134 + }, + { + "date": "2022-03-15", + "Sales": 135, + "Successful Payments": 106 + }, + { + "date": "2022-03-16", + "Sales": 131, + "Successful Payments": 148 + }, + { + "date": "2022-03-17", + "Sales": 145, + "Successful Payments": 137 + }, + { + "date": "2022-03-18", + "Sales": 144, + "Successful Payments": 129 + }, + { + "date": "2022-03-19", + "Sales": 105, + "Successful Payments": 135 + }, + { + "date": "2022-03-20", + "Sales": 148, + "Successful Payments": 119 + }, + { + "date": "2022-03-21", + "Sales": 102, + "Successful Payments": 126 + }, + { + "date": "2022-03-22", + "Sales": 133, + "Successful Payments": 131 + }, + { + "date": "2022-03-23", + "Sales": 103, + "Successful Payments": 150 + }, + { + "date": "2022-03-24", + "Sales": 115, + "Successful Payments": 111 + }, + { + "date": "2022-03-25", + "Sales": 101, + "Successful Payments": 102 + }, + { + "date": "2022-03-26", + "Sales": 145, + "Successful Payments": 127 + }, + { + "date": "2022-03-27", + "Sales": 133, + "Successful Payments": 123 + }, + { + "date": "2022-03-28", + "Sales": 126, + "Successful Payments": 143 + }, + { + "date": "2022-03-29", + "Sales": 113, + "Successful Payments": 145 + }, + { + "date": "2022-03-30", + "Sales": 150, + "Successful Payments": 116 + }, + { + "date": "2022-03-31", + "Sales": 102, + "Successful Payments": 133 + }, + { + "date": "2022-04-02", + "Sales": 121, + "Successful Payments": 137 + }, + { + "date": "2022-04-03", + "Sales": 148, + "Successful Payments": 148 + }, + { + "date": "2022-04-04", + "Sales": 126, + "Successful Payments": 135 + }, + { + "date": "2022-04-06", + "Sales": 150, + "Successful Payments": 140 + }, + { + "date": "2022-04-07", + "Sales": 132, + "Successful Payments": 142 + }, + { + "date": "2022-04-09", + "Sales": 139, + "Successful Payments": 105 + }, + { + "date": "2022-04-10", + "Sales": 109, + "Successful Payments": 130 + }, + { + "date": "2022-04-11", + "Sales": 120, + "Successful Payments": 147 + }, + { + "date": "2022-04-12", + "Sales": 147, + "Successful Payments": 116 + }, + { + "date": "2022-04-13", + "Sales": 138, + "Successful Payments": 100 + }, + { + "date": "2022-04-14", + "Sales": 100, + "Successful Payments": 146 + }, + { + "date": "2022-04-15", + "Sales": 108, + "Successful Payments": 117 + }, + { + "date": "2022-04-16", + "Sales": 135, + "Successful Payments": 142 + }, + { + "date": "2022-04-17", + "Sales": 108, + "Successful Payments": 150 + }, + { + "date": "2022-04-18", + "Sales": 132, + "Successful Payments": 108 + }, + { + "date": "2022-04-19", + "Sales": 118, + "Successful Payments": 120 + }, + { + "date": "2022-04-20", + "Sales": 109, + "Successful Payments": 135 + }, + { + "date": "2022-04-21", + "Sales": 139, + "Successful Payments": 101 + }, + { + "date": "2022-04-22", + "Sales": 147, + "Successful Payments": 125 + }, + { + "date": "2022-04-23", + "Sales": 111, + "Successful Payments": 132 + }, + { + "date": "2022-04-24", + "Sales": 122, + "Successful Payments": 143 + }, + { + "date": "2022-04-26", + "Sales": 120, + "Successful Payments": 111 + }, + { + "date": "2022-04-27", + "Sales": 146, + "Successful Payments": 119 + }, + { + "date": "2022-04-28", + "Sales": 105, + "Successful Payments": 108 + }, + { + "date": "2022-04-29", + "Sales": 117, + "Successful Payments": 131 + }, + { + "date": "2022-04-30", + "Sales": 111, + "Successful Payments": 134 + }, + { + "date": "2022-05-01", + "Sales": 104, + "Successful Payments": 146 + }, + { + "date": "2022-05-02", + "Sales": 110, + "Successful Payments": 131 + }, + { + "date": "2022-05-03", + "Sales": 124, + "Successful Payments": 146 + }, + { + "date": "2022-05-04", + "Sales": 126, + "Successful Payments": 132 + }, + { + "date": "2022-05-05", + "Sales": 105, + "Successful Payments": 123 + }, + { + "date": "2022-05-06", + "Sales": 147, + "Successful Payments": 147 + }, + { + "date": "2022-05-07", + "Sales": 132, + "Successful Payments": 120 + }, + { + "date": "2022-05-08", + "Sales": 123, + "Successful Payments": 138 + }, + { + "date": "2022-05-09", + "Sales": 125, + "Successful Payments": 114 + }, + { + "date": "2022-05-10", + "Sales": 106, + "Successful Payments": 142 + }, + { + "date": "2022-05-11", + "Sales": 138, + "Successful Payments": 141 + }, + { + "date": "2022-05-12", + "Sales": 119, + "Successful Payments": 136 + }, + { + "date": "2022-05-13", + "Sales": 111, + "Successful Payments": 116 + }, + { + "date": "2022-05-14", + "Sales": 150, + "Successful Payments": 106 + }, + { + "date": "2022-05-15", + "Sales": 117, + "Successful Payments": 108 + }, + { + "date": "2022-05-16", + "Sales": 108, + "Successful Payments": 130 + }, + { + "date": "2022-05-17", + "Sales": 127, + "Successful Payments": 111 + }, + { + "date": "2022-05-18", + "Sales": 115, + "Successful Payments": 116 + }, + { + "date": "2022-05-19", + "Sales": 103, + "Successful Payments": 131 + }, + { + "date": "2022-05-20", + "Sales": 150, + "Successful Payments": 120 + }, + { + "date": "2022-05-21", + "Sales": 147, + "Successful Payments": 124 + }, + { + "date": "2022-05-22", + "Sales": 132, + "Successful Payments": 137 + }, + { + "date": "2022-05-23", + "Sales": 141, + "Successful Payments": 147 + }, + { + "date": "2022-05-24", + "Sales": 144, + "Successful Payments": 118 + }, + { + "date": "2022-05-25", + "Sales": 112, + "Successful Payments": 141 + }, + { + "date": "2022-05-26", + "Sales": 101, + "Successful Payments": 121 + }, + { + "date": "2022-05-27", + "Sales": 135, + "Successful Payments": 124 + }, + { + "date": "2022-05-28", + "Sales": 122, + "Successful Payments": 141 + }, + { + "date": "2022-05-29", + "Sales": 125, + "Successful Payments": 144 + }, + { + "date": "2022-05-31", + "Sales": 100, + "Successful Payments": 145 + }, + { + "date": "2022-06-01", + "Sales": 133, + "Successful Payments": 138 + }, + { + "date": "2022-06-02", + "Sales": 147, + "Successful Payments": 144 + }, + { + "date": "2022-06-03", + "Sales": 149, + "Successful Payments": 110 + }, + { + "date": "2022-06-04", + "Sales": 126, + "Successful Payments": 133 + }, + { + "date": "2022-06-05", + "Sales": 135, + "Successful Payments": 133 + }, + { + "date": "2022-06-06", + "Sales": 101, + "Successful Payments": 123 + }, + { + "date": "2022-06-07", + "Sales": 126, + "Successful Payments": 142 + }, + { + "date": "2022-06-08", + "Sales": 145, + "Successful Payments": 147 + }, + { + "date": "2022-06-09", + "Sales": 117, + "Successful Payments": 125 + }, + { + "date": "2022-06-10", + "Sales": 123, + "Successful Payments": 123 + }, + { + "date": "2022-06-11", + "Sales": 138, + "Successful Payments": 128 + }, + { + "date": "2022-06-12", + "Sales": 129, + "Successful Payments": 133 + }, + { + "date": "2022-06-13", + "Sales": 114, + "Successful Payments": 139 + }, + { + "date": "2022-06-14", + "Sales": 147, + "Successful Payments": 131 + }, + { + "date": "2022-06-15", + "Sales": 149, + "Successful Payments": 145 + }, + { + "date": "2022-06-16", + "Sales": 124, + "Successful Payments": 123 + }, + { + "date": "2022-06-18", + "Sales": 138, + "Successful Payments": 107 + }, + { + "date": "2022-06-20", + "Sales": 138, + "Successful Payments": 106 + }, + { + "date": "2022-06-21", + "Sales": 138, + "Successful Payments": 126 + }, + { + "date": "2022-06-22", + "Sales": 106, + "Successful Payments": 148 + }, + { + "date": "2022-06-23", + "Sales": 126, + "Successful Payments": 131 + }, + { + "date": "2022-06-24", + "Sales": 112, + "Successful Payments": 146 + }, + { + "date": "2022-06-25", + "Sales": 111, + "Successful Payments": 131 + }, + { + "date": "2022-06-26", + "Sales": 114, + "Successful Payments": 119 + }, + { + "date": "2022-06-27", + "Sales": 128, + "Successful Payments": 113 + }, + { + "date": "2022-06-28", + "Sales": 135, + "Successful Payments": 105 + }, + { + "date": "2022-06-29", + "Sales": 102, + "Successful Payments": 146 + }, + { + "date": "2022-06-30", + "Sales": 131, + "Successful Payments": 126 + }, + { + "date": "2022-07-02", + "Sales": 144, + "Successful Payments": 135 + }, + { + "date": "2022-07-03", + "Sales": 140, + "Successful Payments": 137 + }, + { + "date": "2022-07-04", + "Sales": 125, + "Successful Payments": 106 + }, + { + "date": "2022-07-05", + "Sales": 113, + "Successful Payments": 103 + }, + { + "date": "2022-07-06", + "Sales": 114, + "Successful Payments": 141 + }, + { + "date": "2022-07-07", + "Sales": 137, + "Successful Payments": 148 + }, + { + "date": "2022-07-09", + "Sales": 124, + "Successful Payments": 104 + }, + { + "date": "2022-07-10", + "Sales": 118, + "Successful Payments": 114 + }, + { + "date": "2022-07-11", + "Sales": 147, + "Successful Payments": 100 + }, + { + "date": "2022-07-12", + "Sales": 140, + "Successful Payments": 118 + }, + { + "date": "2022-07-13", + "Sales": 150, + "Successful Payments": 139 + }, + { + "date": "2022-07-14", + "Sales": 132, + "Successful Payments": 114 + }, + { + "date": "2022-07-15", + "Sales": 107, + "Successful Payments": 128 + }, + { + "date": "2022-07-16", + "Sales": 112, + "Successful Payments": 132 + }, + { + "date": "2022-07-17", + "Sales": 108, + "Successful Payments": 146 + }, + { + "date": "2022-07-19", + "Sales": 112, + "Successful Payments": 102 + }, + { + "date": "2022-07-20", + "Sales": 136, + "Successful Payments": 110 + }, + { + "date": "2022-07-21", + "Sales": 146, + "Successful Payments": 140 + }, + { + "date": "2022-07-22", + "Sales": 133, + "Successful Payments": 136 + }, + { + "date": "2022-07-23", + "Sales": 108, + "Successful Payments": 110 + }, + { + "date": "2022-07-24", + "Sales": 136, + "Successful Payments": 106 + }, + { + "date": "2022-07-25", + "Sales": 105, + "Successful Payments": 139 + }, + { + "date": "2022-07-26", + "Sales": 122, + "Successful Payments": 102 + }, + { + "date": "2022-07-27", + "Sales": 149, + "Successful Payments": 146 + }, + { + "date": "2022-07-28", + "Sales": 114, + "Successful Payments": 149 + }, + { + "date": "2022-07-29", + "Sales": 108, + "Successful Payments": 114 + }, + { + "date": "2022-07-30", + "Sales": 149, + "Successful Payments": 123 + }, + { + "date": "2022-07-31", + "Sales": 116, + "Successful Payments": 143 + }, + { + "date": "2022-08-01", + "Sales": 126, + "Successful Payments": 122 + }, + { + "date": "2022-08-02", + "Sales": 105, + "Successful Payments": 133 + }, + { + "date": "2022-08-03", + "Sales": 103, + "Successful Payments": 130 + }, + { + "date": "2022-08-04", + "Sales": 136, + "Successful Payments": 100 + }, + { + "date": "2022-08-05", + "Sales": 106, + "Successful Payments": 139 + }, + { + "date": "2022-08-06", + "Sales": 120, + "Successful Payments": 122 + }, + { + "date": "2022-08-07", + "Sales": 109, + "Successful Payments": 118 + }, + { + "date": "2022-08-08", + "Sales": 133, + "Successful Payments": 115 + }, + { + "date": "2022-08-09", + "Sales": 130, + "Successful Payments": 124 + }, + { + "date": "2022-08-10", + "Sales": 107, + "Successful Payments": 135 + }, + { + "date": "2022-08-11", + "Sales": 146, + "Successful Payments": 114 + }, + { + "date": "2022-08-12", + "Sales": 103, + "Successful Payments": 128 + }, + { + "date": "2022-08-14", + "Sales": 116, + "Successful Payments": 115 + }, + { + "date": "2022-08-15", + "Sales": 132, + "Successful Payments": 150 + }, + { + "date": "2022-08-16", + "Sales": 123, + "Successful Payments": 144 + }, + { + "date": "2022-08-17", + "Sales": 120, + "Successful Payments": 134 + }, + { + "date": "2022-08-18", + "Sales": 111, + "Successful Payments": 106 + }, + { + "date": "2022-08-19", + "Sales": 114, + "Successful Payments": 139 + }, + { + "date": "2022-08-21", + "Sales": 133, + "Successful Payments": 137 + }, + { + "date": "2022-08-22", + "Sales": 115, + "Successful Payments": 115 + }, + { + "date": "2022-08-23", + "Sales": 104, + "Successful Payments": 109 + }, + { + "date": "2022-08-24", + "Sales": 110, + "Successful Payments": 116 + }, + { + "date": "2022-08-25", + "Sales": 134, + "Successful Payments": 119 + }, + { + "date": "2022-08-26", + "Sales": 104, + "Successful Payments": 110 + }, + { + "date": "2022-08-27", + "Sales": 143, + "Successful Payments": 114 + }, + { + "date": "2022-08-28", + "Sales": 103, + "Successful Payments": 119 + }, + { + "date": "2022-08-29", + "Sales": 148, + "Successful Payments": 113 + }, + { + "date": "2022-08-30", + "Sales": 111, + "Successful Payments": 116 + }, + { + "date": "2022-08-31", + "Sales": 121, + "Successful Payments": 150 + }, + { + "date": "2022-09-01", + "Sales": 150, + "Successful Payments": 103 + }, + { + "date": "2022-09-02", + "Sales": 126, + "Successful Payments": 128 + }, + { + "date": "2022-09-03", + "Sales": 147, + "Successful Payments": 120 + }, + { + "date": "2022-09-04", + "Sales": 120, + "Successful Payments": 107 + }, + { + "date": "2022-09-05", + "Sales": 142, + "Successful Payments": 133 + }, + { + "date": "2022-09-06", + "Sales": 132, + "Successful Payments": 111 + }, + { + "date": "2022-09-07", + "Sales": 137, + "Successful Payments": 108 + }, + { + "date": "2022-09-08", + "Sales": 134, + "Successful Payments": 109 + }, + { + "date": "2022-09-09", + "Sales": 102, + "Successful Payments": 146 + }, + { + "date": "2022-09-10", + "Sales": 112, + "Successful Payments": 147 + }, + { + "date": "2022-09-11", + "Sales": 118, + "Successful Payments": 101 + }, + { + "date": "2022-09-12", + "Sales": 130, + "Successful Payments": 123 + }, + { + "date": "2022-09-13", + "Sales": 115, + "Successful Payments": 129 + }, + { + "date": "2022-09-14", + "Sales": 120, + "Successful Payments": 134 + }, + { + "date": "2022-09-15", + "Sales": 104, + "Successful Payments": 137 + }, + { + "date": "2022-09-16", + "Sales": 122, + "Successful Payments": 118 + }, + { + "date": "2022-09-17", + "Sales": 128, + "Successful Payments": 136 + }, + { + "date": "2022-09-18", + "Sales": 124, + "Successful Payments": 136 + }, + { + "date": "2022-09-19", + "Sales": 111, + "Successful Payments": 133 + }, + { + "date": "2022-09-20", + "Sales": 130, + "Successful Payments": 130 + }, + { + "date": "2022-09-21", + "Sales": 136, + "Successful Payments": 109 + }, + { + "date": "2022-09-22", + "Sales": 130, + "Successful Payments": 118 + }, + { + "date": "2022-09-23", + "Sales": 103, + "Successful Payments": 125 + }, + { + "date": "2022-09-24", + "Sales": 113, + "Successful Payments": 147 + }, + { + "date": "2022-09-25", + "Sales": 131, + "Successful Payments": 140 + }, + { + "date": "2022-09-26", + "Sales": 149, + "Successful Payments": 144 + }, + { + "date": "2022-09-27", + "Sales": 137, + "Successful Payments": 138 + }, + { + "date": "2022-09-28", + "Sales": 136, + "Successful Payments": 106 + }, + { + "date": "2022-09-29", + "Sales": 132, + "Successful Payments": 119 + }, + { + "date": "2022-09-30", + "Sales": 112, + "Successful Payments": 133 + }, + { + "date": "2022-10-02", + "Sales": 124, + "Successful Payments": 123 + }, + { + "date": "2022-10-04", + "Sales": 135, + "Successful Payments": 136 + }, + { + "date": "2022-10-05", + "Sales": 102, + "Successful Payments": 136 + }, + { + "date": "2022-10-06", + "Sales": 130, + "Successful Payments": 123 + }, + { + "date": "2022-10-07", + "Sales": 124, + "Successful Payments": 128 + }, + { + "date": "2022-10-08", + "Sales": 117, + "Successful Payments": 127 + }, + { + "date": "2022-10-09", + "Sales": 102, + "Successful Payments": 142 + }, + { + "date": "2022-10-10", + "Sales": 103, + "Successful Payments": 145 + }, + { + "date": "2022-10-11", + "Sales": 148, + "Successful Payments": 139 + }, + { + "date": "2022-10-12", + "Sales": 115, + "Successful Payments": 112 + }, + { + "date": "2022-10-13", + "Sales": 123, + "Successful Payments": 104 + }, + { + "date": "2022-10-14", + "Sales": 119, + "Successful Payments": 110 + }, + { + "date": "2022-10-15", + "Sales": 145, + "Successful Payments": 120 + }, + { + "date": "2022-10-16", + "Sales": 132, + "Successful Payments": 110 + }, + { + "date": "2022-10-18", + "Sales": 126, + "Successful Payments": 130 + }, + { + "date": "2022-10-19", + "Sales": 122, + "Successful Payments": 120 + }, + { + "date": "2022-10-20", + "Sales": 135, + "Successful Payments": 133 + }, + { + "date": "2022-10-21", + "Sales": 100, + "Successful Payments": 129 + }, + { + "date": "2022-10-22", + "Sales": 133, + "Successful Payments": 146 + }, + { + "date": "2022-10-23", + "Sales": 121, + "Successful Payments": 123 + }, + { + "date": "2022-10-25", + "Sales": 108, + "Successful Payments": 113 + }, + { + "date": "2022-10-26", + "Sales": 141, + "Successful Payments": 106 + }, + { + "date": "2022-10-27", + "Sales": 145, + "Successful Payments": 102 + }, + { + "date": "2022-10-29", + "Sales": 120, + "Successful Payments": 110 + }, + { + "date": "2022-10-30", + "Sales": 119, + "Successful Payments": 130 + }, + { + "date": "2022-10-31", + "Sales": 148, + "Successful Payments": 102 + }, + { + "date": "2022-11-01", + "Sales": 122, + "Successful Payments": 117 + }, + { + "date": "2022-11-02", + "Sales": 135, + "Successful Payments": 143 + }, + { + "date": "2022-11-03", + "Sales": 129, + "Successful Payments": 103 + }, + { + "date": "2022-11-04", + "Sales": 144, + "Successful Payments": 139 + }, + { + "date": "2022-11-05", + "Sales": 106, + "Successful Payments": 106 + }, + { + "date": "2022-11-06", + "Sales": 121, + "Successful Payments": 130 + }, + { + "date": "2022-11-07", + "Sales": 130, + "Successful Payments": 118 + }, + { + "date": "2022-11-08", + "Sales": 112, + "Successful Payments": 143 + }, + { + "date": "2022-11-09", + "Sales": 101, + "Successful Payments": 116 + }, + { + "date": "2022-11-10", + "Sales": 128, + "Successful Payments": 131 + }, + { + "date": "2022-11-11", + "Sales": 125, + "Successful Payments": 114 + }, + { + "date": "2022-11-12", + "Sales": 123, + "Successful Payments": 112 + }, + { + "date": "2022-11-13", + "Sales": 134, + "Successful Payments": 130 + }, + { + "date": "2022-11-14", + "Sales": 107, + "Successful Payments": 119 + }, + { + "date": "2022-11-15", + "Sales": 146, + "Successful Payments": 144 + }, + { + "date": "2022-11-16", + "Sales": 142, + "Successful Payments": 138 + }, + { + "date": "2022-11-17", + "Sales": 118, + "Successful Payments": 105 + }, + { + "date": "2022-11-18", + "Sales": 101, + "Successful Payments": 150 + }, + { + "date": "2022-11-19", + "Sales": 104, + "Successful Payments": 125 + }, + { + "date": "2022-11-20", + "Sales": 146, + "Successful Payments": 110 + }, + { + "date": "2022-11-21", + "Sales": 133, + "Successful Payments": 111 + }, + { + "date": "2022-11-22", + "Sales": 133, + "Successful Payments": 112 + }, + { + "date": "2022-11-23", + "Sales": 101, + "Successful Payments": 132 + }, + { + "date": "2022-11-24", + "Sales": 125, + "Successful Payments": 133 + }, + { + "date": "2022-11-25", + "Sales": 149, + "Successful Payments": 103 + }, + { + "date": "2022-11-26", + "Sales": 104, + "Successful Payments": 136 + }, + { + "date": "2022-11-28", + "Sales": 125, + "Successful Payments": 142 + }, + { + "date": "2022-11-29", + "Sales": 140, + "Successful Payments": 107 + }, + { + "date": "2022-11-30", + "Sales": 127, + "Successful Payments": 121 + }, + { + "date": "2022-12-01", + "Sales": 105, + "Successful Payments": 118 + }, + { + "date": "2022-12-02", + "Sales": 131, + "Successful Payments": 114 + }, + { + "date": "2022-12-03", + "Sales": 103, + "Successful Payments": 106 + }, + { + "date": "2022-12-04", + "Sales": 106, + "Successful Payments": 103 + }, + { + "date": "2022-12-05", + "Sales": 126, + "Successful Payments": 136 + }, + { + "date": "2022-12-06", + "Sales": 132, + "Successful Payments": 127 + }, + { + "date": "2022-12-07", + "Sales": 100, + "Successful Payments": 110 + }, + { + "date": "2022-12-08", + "Sales": 133, + "Successful Payments": 118 + }, + { + "date": "2022-12-09", + "Sales": 141, + "Successful Payments": 140 + }, + { + "date": "2022-12-10", + "Sales": 122, + "Successful Payments": 116 + }, + { + "date": "2022-12-11", + "Sales": 140, + "Successful Payments": 107 + }, + { + "date": "2022-12-12", + "Sales": 130, + "Successful Payments": 132 + }, + { + "date": "2022-12-14", + "Sales": 120, + "Successful Payments": 126 + }, + { + "date": "2022-12-16", + "Sales": 142, + "Successful Payments": 112 + }, + { + "date": "2022-12-17", + "Sales": 116, + "Successful Payments": 149 + }, + { + "date": "2022-12-18", + "Sales": 134, + "Successful Payments": 129 + }, + { + "date": "2022-12-19", + "Sales": 134, + "Successful Payments": 117 + }, + { + "date": "2022-12-20", + "Sales": 104, + "Successful Payments": 137 + }, + { + "date": "2022-12-21", + "Sales": 129, + "Successful Payments": 141 + }, + { + "date": "2022-12-23", + "Sales": 102, + "Successful Payments": 131 + }, + { + "date": "2022-12-24", + "Sales": 139, + "Successful Payments": 112 + }, + { + "date": "2022-12-25", + "Sales": 138, + "Successful Payments": 102 + }, + { + "date": "2022-12-26", + "Sales": 113, + "Successful Payments": 103 + }, + { + "date": "2022-12-27", + "Sales": 141, + "Successful Payments": 149 + }, + { + "date": "2022-12-28", + "Sales": 103, + "Successful Payments": 148 + }, + { + "date": "2022-12-30", + "Sales": 142, + "Successful Payments": 114 + }, + { + "date": "2023-01-01", + "Sales": 128, + "Successful Payments": 131 + }, + { + "date": "2023-01-02", + "Sales": 146, + "Successful Payments": 134 + }, + { + "date": "2023-01-03", + "Sales": 125, + "Successful Payments": 150 + }, + { + "date": "2023-01-05", + "Sales": 100, + "Successful Payments": 134 + }, + { + "date": "2023-01-06", + "Sales": 108, + "Successful Payments": 102 + }, + { + "date": "2023-01-07", + "Sales": 136, + "Successful Payments": 131 + }, + { + "date": "2023-01-08", + "Sales": 107, + "Successful Payments": 116 + }, + { + "date": "2023-01-09", + "Sales": 121, + "Successful Payments": 107 + }, + { + "date": "2023-01-10", + "Sales": 114, + "Successful Payments": 102 + }, + { + "date": "2023-01-11", + "Sales": 133, + "Successful Payments": 114 + }, + { + "date": "2023-01-12", + "Sales": 136, + "Successful Payments": 127 + }, + { + "date": "2023-01-13", + "Sales": 134, + "Successful Payments": 130 + }, + { + "date": "2023-01-14", + "Sales": 125, + "Successful Payments": 131 + }, + { + "date": "2023-01-15", + "Sales": 134, + "Successful Payments": 131 + }, + { + "date": "2023-01-16", + "Sales": 114, + "Successful Payments": 137 + }, + { + "date": "2023-01-17", + "Sales": 108, + "Successful Payments": 141 + }, + { + "date": "2023-01-18", + "Sales": 106, + "Successful Payments": 133 + }, + { + "date": "2023-01-19", + "Sales": 115, + "Successful Payments": 139 + }, + { + "date": "2023-01-20", + "Sales": 131, + "Successful Payments": 105 + }, + { + "date": "2023-01-21", + "Sales": 118, + "Successful Payments": 122 + }, + { + "date": "2023-01-22", + "Sales": 140, + "Successful Payments": 129 + }, + { + "date": "2023-01-23", + "Sales": 138, + "Successful Payments": 136 + }, + { + "date": "2023-01-24", + "Sales": 127, + "Successful Payments": 111 + }, + { + "date": "2023-01-25", + "Sales": 110, + "Successful Payments": 118 + }, + { + "date": "2023-01-26", + "Sales": 116, + "Successful Payments": 121 + }, + { + "date": "2023-01-27", + "Sales": 141, + "Successful Payments": 112 + }, + { + "date": "2023-01-28", + "Sales": 148, + "Successful Payments": 140 + }, + { + "date": "2023-01-29", + "Sales": 130, + "Successful Payments": 105 + }, + { + "date": "2023-01-30", + "Sales": 125, + "Successful Payments": 129 + }, + { + "date": "2023-01-31", + "Sales": 142, + "Successful Payments": 150 + }, + { + "date": "2023-02-01", + "Sales": 115, + "Successful Payments": 107 + }, + { + "date": "2023-02-02", + "Sales": 138, + "Successful Payments": 134 + }, + { + "date": "2023-02-03", + "Sales": 127, + "Successful Payments": 123 + }, + { + "date": "2023-02-05", + "Sales": 144, + "Successful Payments": 110 + }, + { + "date": "2023-02-06", + "Sales": 101, + "Successful Payments": 100 + }, + { + "date": "2023-02-08", + "Sales": 142, + "Successful Payments": 124 + }, + { + "date": "2023-02-09", + "Sales": 111, + "Successful Payments": 115 + }, + { + "date": "2023-02-10", + "Sales": 143, + "Successful Payments": 141 + }, + { + "date": "2023-02-11", + "Sales": 108, + "Successful Payments": 129 + }, + { + "date": "2023-02-12", + "Sales": 118, + "Successful Payments": 134 + }, + { + "date": "2023-02-13", + "Sales": 133, + "Successful Payments": 128 + }, + { + "date": "2023-02-14", + "Sales": 149, + "Successful Payments": 142 + }, + { + "date": "2023-02-15", + "Sales": 128, + "Successful Payments": 120 + }, + { + "date": "2023-02-16", + "Sales": 112, + "Successful Payments": 142 + }, + { + "date": "2023-02-17", + "Sales": 148, + "Successful Payments": 134 + }, + { + "date": "2023-02-18", + "Sales": 146, + "Successful Payments": 143 + }, + { + "date": "2023-02-19", + "Sales": 124, + "Successful Payments": 102 + }, + { + "date": "2023-02-20", + "Sales": 114, + "Successful Payments": 113 + }, + { + "date": "2023-02-21", + "Sales": 119, + "Successful Payments": 134 + }, + { + "date": "2023-02-22", + "Sales": 127, + "Successful Payments": 118 + }, + { + "date": "2023-02-23", + "Sales": 131, + "Successful Payments": 114 + }, + { + "date": "2023-02-24", + "Sales": 143, + "Successful Payments": 113 + }, + { + "date": "2023-02-25", + "Sales": 121, + "Successful Payments": 118 + }, + { + "date": "2023-02-26", + "Sales": 110, + "Successful Payments": 131 + }, + { + "date": "2023-02-27", + "Sales": 114, + "Successful Payments": 110 + }, + { + "date": "2023-02-28", + "Sales": 121, + "Successful Payments": 125 + }, + { + "date": "2023-03-01", + "Sales": 104, + "Successful Payments": 119 + }, + { + "date": "2023-03-02", + "Sales": 111, + "Successful Payments": 101 + }, + { + "date": "2023-03-04", + "Sales": 137, + "Successful Payments": 123 + }, + { + "date": "2023-03-05", + "Sales": 140, + "Successful Payments": 117 + }, + { + "date": "2023-03-07", + "Sales": 102, + "Successful Payments": 137 + }, + { + "date": "2023-03-08", + "Sales": 128, + "Successful Payments": 122 + }, + { + "date": "2023-03-09", + "Sales": 143, + "Successful Payments": 107 + }, + { + "date": "2023-03-10", + "Sales": 105, + "Successful Payments": 102 + }, + { + "date": "2023-03-11", + "Sales": 105, + "Successful Payments": 113 + }, + { + "date": "2023-03-12", + "Sales": 141, + "Successful Payments": 109 + }, + { + "date": "2023-03-13", + "Sales": 119, + "Successful Payments": 144 + }, + { + "date": "2023-03-14", + "Sales": 136, + "Successful Payments": 125 + }, + { + "date": "2023-03-15", + "Sales": 106, + "Successful Payments": 134 + }, + { + "date": "2023-03-16", + "Sales": 105, + "Successful Payments": 114 + }, + { + "date": "2023-03-17", + "Sales": 148, + "Successful Payments": 113 + }, + { + "date": "2023-03-18", + "Sales": 107, + "Successful Payments": 132 + }, + { + "date": "2023-03-19", + "Sales": 116, + "Successful Payments": 138 + }, + { + "date": "2023-03-20", + "Sales": 102, + "Successful Payments": 106 + }, + { + "date": "2023-03-21", + "Sales": 130, + "Successful Payments": 147 + }, + { + "date": "2023-03-22", + "Sales": 142, + "Successful Payments": 148 + }, + { + "date": "2023-03-23", + "Sales": 110, + "Successful Payments": 106 + }, + { + "date": "2023-03-24", + "Sales": 105, + "Successful Payments": 144 + }, + { + "date": "2023-03-25", + "Sales": 142, + "Successful Payments": 115 + }, + { + "date": "2023-03-26", + "Sales": 139, + "Successful Payments": 147 + }, + { + "date": "2023-03-27", + "Sales": 109, + "Successful Payments": 111 + }, + { + "date": "2023-03-28", + "Sales": 132, + "Successful Payments": 114 + }, + { + "date": "2023-03-29", + "Sales": 116, + "Successful Payments": 103 + }, + { + "date": "2023-03-30", + "Sales": 115, + "Successful Payments": 108 + }, + { + "date": "2023-03-31", + "Sales": 138, + "Successful Payments": 127 + }, + { + "date": "2023-04-01", + "Sales": 148, + "Successful Payments": 110 + }, + { + "date": "2023-04-02", + "Sales": 143, + "Successful Payments": 107 + }, + { + "date": "2023-04-03", + "Sales": 126, + "Successful Payments": 123 + }, + { + "date": "2023-04-04", + "Sales": 150, + "Successful Payments": 101 + }, + { + "date": "2023-04-05", + "Sales": 148, + "Successful Payments": 136 + }, + { + "date": "2023-04-06", + "Sales": 111, + "Successful Payments": 149 + }, + { + "date": "2023-04-07", + "Sales": 117, + "Successful Payments": 105 + }, + { + "date": "2023-04-08", + "Sales": 144, + "Successful Payments": 149 + }, + { + "date": "2023-04-09", + "Sales": 125, + "Successful Payments": 125 + }, + { + "date": "2023-04-10", + "Sales": 131, + "Successful Payments": 111 + }, + { + "date": "2023-04-11", + "Sales": 107, + "Successful Payments": 103 + }, + { + "date": "2023-04-12", + "Sales": 133, + "Successful Payments": 102 + }, + { + "date": "2023-04-13", + "Sales": 143, + "Successful Payments": 124 + }, + { + "date": "2023-04-14", + "Sales": 125, + "Successful Payments": 136 + }, + { + "date": "2023-04-15", + "Sales": 145, + "Successful Payments": 141 + }, + { + "date": "2023-04-17", + "Sales": 111, + "Successful Payments": 105 + }, + { + "date": "2023-04-18", + "Sales": 133, + "Successful Payments": 127 + }, + { + "date": "2023-04-19", + "Sales": 121, + "Successful Payments": 101 + }, + { + "date": "2023-04-20", + "Sales": 122, + "Successful Payments": 146 + }, + { + "date": "2023-04-21", + "Sales": 139, + "Successful Payments": 104 + }, + { + "date": "2023-04-22", + "Sales": 100, + "Successful Payments": 112 + }, + { + "date": "2023-04-23", + "Sales": 149, + "Successful Payments": 111 + }, + { + "date": "2023-04-24", + "Sales": 102, + "Successful Payments": 137 + }, + { + "date": "2023-04-25", + "Sales": 117, + "Successful Payments": 104 + }, + { + "date": "2023-04-26", + "Sales": 103, + "Successful Payments": 117 + }, + { + "date": "2023-04-27", + "Sales": 139, + "Successful Payments": 122 + }, + { + "date": "2023-04-28", + "Sales": 146, + "Successful Payments": 114 + }, + { + "date": "2023-04-29", + "Sales": 149, + "Successful Payments": 127 + }, + { + "date": "2023-04-30", + "Sales": 141, + "Successful Payments": 102 + }, + { + "date": "2023-05-01", + "Sales": 109, + "Successful Payments": 112 + }, + { + "date": "2023-05-02", + "Sales": 147, + "Successful Payments": 144 + }, + { + "date": "2023-05-03", + "Sales": 149, + "Successful Payments": 100 + }, + { + "date": "2023-05-04", + "Sales": 103, + "Successful Payments": 125 + }, + { + "date": "2023-05-05", + "Sales": 136, + "Successful Payments": 137 + }, + { + "date": "2023-05-06", + "Sales": 113, + "Successful Payments": 143 + }, + { + "date": "2023-05-07", + "Sales": 110, + "Successful Payments": 102 + }, + { + "date": "2023-05-08", + "Sales": 129, + "Successful Payments": 130 + }, + { + "date": "2023-05-09", + "Sales": 138, + "Successful Payments": 106 + }, + { + "date": "2023-05-10", + "Sales": 140, + "Successful Payments": 131 + }, + { + "date": "2023-05-11", + "Sales": 109, + "Successful Payments": 142 + }, + { + "date": "2023-05-12", + "Sales": 143, + "Successful Payments": 117 + }, + { + "date": "2023-05-13", + "Sales": 102, + "Successful Payments": 132 + }, + { + "date": "2023-05-14", + "Sales": 149, + "Successful Payments": 141 + }, + { + "date": "2023-05-15", + "Sales": 122, + "Successful Payments": 147 + }, + { + "date": "2023-05-16", + "Sales": 102, + "Successful Payments": 105 + }, + { + "date": "2023-05-18", + "Sales": 131, + "Successful Payments": 139 + }, + { + "date": "2023-05-19", + "Sales": 140, + "Successful Payments": 148 + }, + { + "date": "2023-05-20", + "Sales": 150, + "Successful Payments": 141 + }, + { + "date": "2023-05-21", + "Sales": 104, + "Successful Payments": 116 + }, + { + "date": "2023-05-22", + "Sales": 130, + "Successful Payments": 108 + }, + { + "date": "2023-05-23", + "Sales": 120, + "Successful Payments": 139 + }, + { + "date": "2023-05-24", + "Sales": 119, + "Successful Payments": 105 + }, + { + "date": "2023-05-25", + "Sales": 129, + "Successful Payments": 122 + }, + { + "date": "2023-05-26", + "Sales": 142, + "Successful Payments": 122 + }, + { + "date": "2023-05-27", + "Sales": 147, + "Successful Payments": 129 + }, + { + "date": "2023-05-28", + "Sales": 134, + "Successful Payments": 121 + }, + { + "date": "2023-05-29", + "Sales": 135, + "Successful Payments": 103 + }, + { + "date": "2023-05-30", + "Sales": 128, + "Successful Payments": 103 + }, + { + "date": "2023-05-31", + "Sales": 149, + "Successful Payments": 116 + }, + { + "date": "2023-06-01", + "Sales": 102, + "Successful Payments": 134 + }, + { + "date": "2023-06-02", + "Sales": 102, + "Successful Payments": 120 + }, + { + "date": "2023-06-05", + "Sales": 114, + "Successful Payments": 117 + }, + { + "date": "2023-06-06", + "Sales": 137, + "Successful Payments": 111 + }, + { + "date": "2023-06-07", + "Sales": 101, + "Successful Payments": 140 + }, + { + "date": "2023-06-08", + "Sales": 134, + "Successful Payments": 121 + }, + { + "date": "2023-06-09", + "Sales": 123, + "Successful Payments": 105 + }, + { + "date": "2023-06-10", + "Sales": 123, + "Successful Payments": 113 + }, + { + "date": "2023-06-11", + "Sales": 110, + "Successful Payments": 119 + }, + { + "date": "2023-06-12", + "Sales": 132, + "Successful Payments": 135 + }, + { + "date": "2023-06-13", + "Sales": 102, + "Successful Payments": 141 + }, + { + "date": "2023-06-14", + "Sales": 140, + "Successful Payments": 121 + }, + { + "date": "2023-06-15", + "Sales": 150, + "Successful Payments": 121 + }, + { + "date": "2023-06-16", + "Sales": 146, + "Successful Payments": 122 + }, + { + "date": "2023-06-17", + "Sales": 102, + "Successful Payments": 102 + }, + { + "date": "2023-06-18", + "Sales": 120, + "Successful Payments": 150 + }, + { + "date": "2023-06-19", + "Sales": 130, + "Successful Payments": 121 + }, + { + "date": "2023-06-20", + "Sales": 131, + "Successful Payments": 125 + }, + { + "date": "2023-06-21", + "Sales": 137, + "Successful Payments": 116 + }, + { + "date": "2023-06-22", + "Sales": 150, + "Successful Payments": 129 + }, + { + "date": "2023-06-23", + "Sales": 150, + "Successful Payments": 127 + }, + { + "date": "2023-06-24", + "Sales": 120, + "Successful Payments": 107 + }, + { + "date": "2023-06-25", + "Sales": 112, + "Successful Payments": 145 + }, + { + "date": "2023-06-26", + "Sales": 110, + "Successful Payments": 108 + }, + { + "date": "2023-06-27", + "Sales": 122, + "Successful Payments": 123 + }, + { + "date": "2023-06-28", + "Sales": 146, + "Successful Payments": 145 + }, + { + "date": "2023-06-29", + "Sales": 127, + "Successful Payments": 107 + }, + { + "date": "2023-06-30", + "Sales": 128, + "Successful Payments": 119 + }, + { + "date": "2023-07-01", + "Sales": 102, + "Successful Payments": 148 + }, + { + "date": "2023-07-02", + "Sales": 107, + "Successful Payments": 109 + }, + { + "date": "2023-07-03", + "Sales": 136, + "Successful Payments": 147 + }, + { + "date": "2023-07-05", + "Sales": 128, + "Successful Payments": 119 + }, + { + "date": "2023-07-06", + "Sales": 139, + "Successful Payments": 104 + }, + { + "date": "2023-07-07", + "Sales": 116, + "Successful Payments": 149 + }, + { + "date": "2023-07-08", + "Sales": 115, + "Successful Payments": 104 + }, + { + "date": "2023-07-09", + "Sales": 109, + "Successful Payments": 121 + }, + { + "date": "2023-07-10", + "Sales": 101, + "Successful Payments": 120 + }, + { + "date": "2023-07-11", + "Sales": 141, + "Successful Payments": 125 + }, + { + "date": "2023-07-12", + "Sales": 150, + "Successful Payments": 139 + }, + { + "date": "2023-07-13", + "Sales": 113, + "Successful Payments": 147 + }, + { + "date": "2023-07-14", + "Sales": 148, + "Successful Payments": 113 + }, + { + "date": "2023-07-15", + "Sales": 136, + "Successful Payments": 133 + }, + { + "date": "2023-07-16", + "Sales": 118, + "Successful Payments": 128 + }, + { + "date": "2023-07-17", + "Sales": 131, + "Successful Payments": 103 + }, + { + "date": "2023-07-18", + "Sales": 149, + "Successful Payments": 121 + }, + { + "date": "2023-07-19", + "Sales": 100, + "Successful Payments": 135 + }, + { + "date": "2023-07-20", + "Sales": 132, + "Successful Payments": 141 + }, + { + "date": "2023-07-21", + "Sales": 140, + "Successful Payments": 149 + }, + { + "date": "2023-07-22", + "Sales": 120, + "Successful Payments": 124 + }, + { + "date": "2023-07-23", + "Sales": 144, + "Successful Payments": 145 + }, + { + "date": "2023-07-24", + "Sales": 133, + "Successful Payments": 138 + }, + { + "date": "2023-07-25", + "Sales": 109, + "Successful Payments": 137 + }, + { + "date": "2023-07-26", + "Sales": 136, + "Successful Payments": 142 + }, + { + "date": "2023-07-27", + "Sales": 109, + "Successful Payments": 128 + }, + { + "date": "2023-07-28", + "Sales": 119, + "Successful Payments": 135 + }, + { + "date": "2023-07-29", + "Sales": 109, + "Successful Payments": 110 + }, + { + "date": "2023-07-30", + "Sales": 100, + "Successful Payments": 114 + }, + { + "date": "2023-07-31", + "Sales": 101, + "Successful Payments": 110 + }, + { + "date": "2023-08-01", + "Sales": 130, + "Successful Payments": 135 + }, + { + "date": "2023-08-02", + "Sales": 125, + "Successful Payments": 130 + }, + { + "date": "2023-08-04", + "Sales": 125, + "Successful Payments": 140 + }, + { + "date": "2023-08-06", + "Sales": 116, + "Successful Payments": 144 + }, + { + "date": "2023-08-07", + "Sales": 121, + "Successful Payments": 133 + }, + { + "date": "2023-08-08", + "Sales": 150, + "Successful Payments": 141 + }, + { + "date": "2023-08-09", + "Sales": 150, + "Successful Payments": 124 + }, + { + "date": "2023-08-10", + "Sales": 119, + "Successful Payments": 124 + }, + { + "date": "2023-08-12", + "Sales": 150, + "Successful Payments": 124 + }, + { + "date": "2023-08-13", + "Sales": 132, + "Successful Payments": 127 + }, + { + "date": "2023-08-14", + "Sales": 110, + "Successful Payments": 144 + }, + { + "date": "2023-08-15", + "Sales": 123, + "Successful Payments": 148 + }, + { + "date": "2023-08-16", + "Sales": 117, + "Successful Payments": 111 + }, + { + "date": "2023-08-18", + "Sales": 102, + "Successful Payments": 135 + }, + { + "date": "2023-08-19", + "Sales": 106, + "Successful Payments": 107 + }, + { + "date": "2023-08-21", + "Sales": 149, + "Successful Payments": 130 + }, + { + "date": "2023-08-22", + "Sales": 111, + "Successful Payments": 126 + }, + { + "date": "2023-08-23", + "Sales": 129, + "Successful Payments": 135 + }, + { + "date": "2023-08-24", + "Sales": 110, + "Successful Payments": 102 + }, + { + "date": "2023-08-25", + "Sales": 138, + "Successful Payments": 116 + }, + { + "date": "2023-08-26", + "Sales": 121, + "Successful Payments": 102 + }, + { + "date": "2023-08-27", + "Sales": 145, + "Successful Payments": 122 + }, + { + "date": "2023-08-28", + "Sales": 148, + "Successful Payments": 103 + }, + { + "date": "2023-08-29", + "Sales": 148, + "Successful Payments": 101 + }, + { + "date": "2023-08-30", + "Sales": 100, + "Successful Payments": 130 + }, + { + "date": "2023-08-31", + "Sales": 142, + "Successful Payments": 128 + }, + { + "date": "2023-09-01", + "Sales": 147, + "Successful Payments": 150 + }, + { + "date": "2023-09-02", + "Sales": 119, + "Successful Payments": 100 + }, + { + "date": "2023-09-03", + "Sales": 149, + "Successful Payments": 133 + }, + { + "date": "2023-09-04", + "Sales": 150, + "Successful Payments": 128 + }, + { + "date": "2023-09-05", + "Sales": 145, + "Successful Payments": 133 + }, + { + "date": "2023-09-06", + "Sales": 111, + "Successful Payments": 146 + }, + { + "date": "2023-09-07", + "Sales": 147, + "Successful Payments": 122 + }, + { + "date": "2023-09-09", + "Sales": 107, + "Successful Payments": 142 + }, + { + "date": "2023-09-10", + "Sales": 117, + "Successful Payments": 115 + }, + { + "date": "2023-09-11", + "Sales": 142, + "Successful Payments": 101 + }, + { + "date": "2023-09-12", + "Sales": 137, + "Successful Payments": 112 + }, + { + "date": "2023-09-13", + "Sales": 142, + "Successful Payments": 128 + }, + { + "date": "2023-09-14", + "Sales": 103, + "Successful Payments": 117 + }, + { + "date": "2023-09-15", + "Sales": 148, + "Successful Payments": 148 + }, + { + "date": "2023-09-16", + "Sales": 126, + "Successful Payments": 129 + }, + { + "date": "2023-09-17", + "Sales": 131, + "Successful Payments": 149 + }, + { + "date": "2023-09-18", + "Sales": 140, + "Successful Payments": 111 + }, + { + "date": "2023-09-19", + "Sales": 108, + "Successful Payments": 132 + }, + { + "date": "2023-09-21", + "Sales": 106, + "Successful Payments": 109 + }, + { + "date": "2023-09-22", + "Sales": 137, + "Successful Payments": 101 + }, + { + "date": "2023-09-23", + "Sales": 136, + "Successful Payments": 130 + }, + { + "date": "2023-09-24", + "Sales": 107, + "Successful Payments": 123 + }, + { + "date": "2023-09-25", + "Sales": 122, + "Successful Payments": 133 + }, + { + "date": "2023-09-26", + "Sales": 107, + "Successful Payments": 126 + }, + { + "date": "2023-09-27", + "Sales": 145, + "Successful Payments": 106 + }, + { + "date": "2023-09-28", + "Sales": 102, + "Successful Payments": 138 + }, + { + "date": "2023-09-29", + "Sales": 110, + "Successful Payments": 141 + }, + { + "date": "2023-09-30", + "Sales": 137, + "Successful Payments": 140 + }, + { + "date": "2023-10-01", + "Sales": 134, + "Successful Payments": 124 + }, + { + "date": "2023-10-02", + "Sales": 101, + "Successful Payments": 143 + }, + { + "date": "2023-10-03", + "Sales": 105, + "Successful Payments": 135 + }, + { + "date": "2023-10-04", + "Sales": 122, + "Successful Payments": 129 + }, + { + "date": "2023-10-05", + "Sales": 132, + "Successful Payments": 148 + }, + { + "date": "2023-10-06", + "Sales": 118, + "Successful Payments": 106 + }, + { + "date": "2023-10-07", + "Sales": 112, + "Successful Payments": 132 + }, + { + "date": "2023-10-08", + "Sales": 124, + "Successful Payments": 143 + }, + { + "date": "2023-10-09", + "Sales": 121, + "Successful Payments": 123 + }, + { + "date": "2023-10-10", + "Sales": 123, + "Successful Payments": 117 + }, + { + "date": "2023-10-11", + "Sales": 110, + "Successful Payments": 116 + }, + { + "date": "2023-10-12", + "Sales": 146, + "Successful Payments": 123 + }, + { + "date": "2023-10-13", + "Sales": 111, + "Successful Payments": 102 + }, + { + "date": "2023-10-14", + "Sales": 136, + "Successful Payments": 125 + }, + { + "date": "2023-10-15", + "Sales": 148, + "Successful Payments": 126 + }, + { + "date": "2023-10-16", + "Sales": 143, + "Successful Payments": 100 + }, + { + "date": "2023-10-17", + "Sales": 110, + "Successful Payments": 135 + }, + { + "date": "2023-10-18", + "Sales": 102, + "Successful Payments": 140 + }, + { + "date": "2023-10-19", + "Sales": 143, + "Successful Payments": 120 + }, + { + "date": "2023-10-20", + "Sales": 141, + "Successful Payments": 110 + }, + { + "date": "2023-10-21", + "Sales": 126, + "Successful Payments": 115 + }, + { + "date": "2023-10-22", + "Sales": 141, + "Successful Payments": 128 + }, + { + "date": "2023-10-23", + "Sales": 144, + "Successful Payments": 139 + }, + { + "date": "2023-10-24", + "Sales": 120, + "Successful Payments": 100 + }, + { + "date": "2023-10-25", + "Sales": 147, + "Successful Payments": 107 + }, + { + "date": "2023-10-26", + "Sales": 144, + "Successful Payments": 148 + }, + { + "date": "2023-10-27", + "Sales": 110, + "Successful Payments": 134 + }, + { + "date": "2023-10-28", + "Sales": 126, + "Successful Payments": 109 + }, + { + "date": "2023-10-29", + "Sales": 109, + "Successful Payments": 131 + }, + { + "date": "2023-10-30", + "Sales": 105, + "Successful Payments": 122 + }, + { + "date": "2023-11-02", + "Sales": 123, + "Successful Payments": 103 + }, + { + "date": "2023-11-03", + "Sales": 118, + "Successful Payments": 138 + }, + { + "date": "2023-11-04", + "Sales": 142, + "Successful Payments": 145 + }, + { + "date": "2023-11-05", + "Sales": 146, + "Successful Payments": 100 + }, + { + "date": "2023-11-06", + "Sales": 137, + "Successful Payments": 134 + }, + { + "date": "2023-11-08", + "Sales": 149, + "Successful Payments": 114 + }, + { + "date": "2023-11-09", + "Sales": 108, + "Successful Payments": 121 + }, + { + "date": "2023-11-10", + "Sales": 103, + "Successful Payments": 125 + }, + { + "date": "2023-11-11", + "Sales": 110, + "Successful Payments": 108 + }, + { + "date": "2023-11-14", + "Sales": 150, + "Successful Payments": 122 + }, + { + "date": "2023-11-15", + "Sales": 118, + "Successful Payments": 121 + }, + { + "date": "2023-11-16", + "Sales": 108, + "Successful Payments": 120 + }, + { + "date": "2023-11-17", + "Sales": 129, + "Successful Payments": 132 + }, + { + "date": "2023-11-18", + "Sales": 103, + "Successful Payments": 134 + }, + { + "date": "2023-11-19", + "Sales": 107, + "Successful Payments": 118 + }, + { + "date": "2023-11-20", + "Sales": 120, + "Successful Payments": 103 + }, + { + "date": "2023-11-21", + "Sales": 143, + "Successful Payments": 106 + }, + { + "date": "2023-11-22", + "Sales": 111, + "Successful Payments": 126 + }, + { + "date": "2023-11-23", + "Sales": 113, + "Successful Payments": 105 + }, + { + "date": "2023-11-24", + "Sales": 122, + "Successful Payments": 113 + }, + { + "date": "2023-11-25", + "Sales": 121, + "Successful Payments": 125 + }, + { + "date": "2023-11-26", + "Sales": 136, + "Successful Payments": 110 + }, + { + "date": "2023-11-27", + "Sales": 104, + "Successful Payments": 125 + }, + { + "date": "2023-11-28", + "Sales": 105, + "Successful Payments": 103 + }, + { + "date": "2023-11-30", + "Sales": 116, + "Successful Payments": 147 + }, + { + "date": "2023-12-01", + "Sales": 147, + "Successful Payments": 116 + }, + { + "date": "2023-12-02", + "Sales": 134, + "Successful Payments": 122 + }, + { + "date": "2023-12-03", + "Sales": 123, + "Successful Payments": 125 + }, + { + "date": "2023-12-04", + "Sales": 121, + "Successful Payments": 138 + }, + { + "date": "2023-12-05", + "Sales": 110, + "Successful Payments": 145 + }, + { + "date": "2023-12-06", + "Sales": 121, + "Successful Payments": 143 + }, + { + "date": "2023-12-07", + "Sales": 139, + "Successful Payments": 103 + }, + { + "date": "2023-12-08", + "Sales": 143, + "Successful Payments": 146 + }, + { + "date": "2023-12-09", + "Sales": 125, + "Successful Payments": 104 + }, + { + "date": "2023-12-10", + "Sales": 127, + "Successful Payments": 136 + }, + { + "date": "2023-12-11", + "Sales": 105, + "Successful Payments": 139 + }, + { + "date": "2023-12-12", + "Sales": 103, + "Successful Payments": 122 + }, + { + "date": "2023-12-13", + "Sales": 148, + "Successful Payments": 118 + }, + { + "date": "2023-12-14", + "Sales": 102, + "Successful Payments": 136 + }, + { + "date": "2023-12-15", + "Sales": 104, + "Successful Payments": 136 + }, + { + "date": "2023-12-16", + "Sales": 136, + "Successful Payments": 148 + }, + { + "date": "2023-12-17", + "Sales": 111, + "Successful Payments": 111 + }, + { + "date": "2023-12-18", + "Sales": 118, + "Successful Payments": 106 + }, + { + "date": "2023-12-19", + "Sales": 106, + "Successful Payments": 135 + }, + { + "date": "2023-12-20", + "Sales": 132, + "Successful Payments": 101 + }, + { + "date": "2023-12-21", + "Sales": 136, + "Successful Payments": 131 + }, + { + "date": "2023-12-22", + "Sales": 142, + "Successful Payments": 150 + }, + { + "date": "2023-12-23", + "Sales": 133, + "Successful Payments": 133 + }, + { + "date": "2023-12-24", + "Sales": 103, + "Successful Payments": 119 + }, + { + "date": "2023-12-25", + "Sales": 122, + "Successful Payments": 146 + }, + { + "date": "2023-12-26", + "Sales": 108, + "Successful Payments": 120 + }, + { + "date": "2023-12-27", + "Sales": 115, + "Successful Payments": 123 + }, + { + "date": "2023-12-28", + "Sales": 105, + "Successful Payments": 140 + }, + { + "date": "2023-12-29", + "Sales": 130, + "Successful Payments": 123 + }, + { + "date": "2023-12-30", + "Sales": 142, + "Successful Payments": 122 + } +] \ No newline at end of file From 19583594c46b7f9a4a556851505caf051ab98fa2 Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Fri, 29 Sep 2023 14:00:39 +0200 Subject: [PATCH 13/17] fix line click --- src/components/chart-elements/AreaChart/AreaChart.tsx | 5 +---- src/components/chart-elements/LineChart/LineChart.tsx | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 5317d5f74..d804a3ed4 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -187,16 +187,13 @@ const AreaChart = React.forwardRef((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && - deltaCalculation && - deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(value, e) => { e.stopPropagation(); - enableDeltaCalculation && hasDeltaCalculation && setDeltaCalculation(null); + enableDeltaCalculation && setDeltaCalculation(null); }} > - {" "} {showGridLines ? ( ((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && - deltaCalculation && - deltaCalculation.leftArea && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(_, e) => { e.stopPropagation(); - enableDeltaCalculation && hasDeltaCalculation && setDeltaCalculation(null); + enableDeltaCalculation && setDeltaCalculation(null); }} onClick={ hasOnValueChange && (activeLegend || activeDot) From 12216f3dd7a3c8d717530bfc5c4ae8bfeed67496 Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Fri, 29 Sep 2023 14:17:44 +0200 Subject: [PATCH 14/17] fix mouseMove --- src/components/chart-elements/AreaChart/AreaChart.tsx | 3 ++- src/components/chart-elements/LineChart/LineChart.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index d804a3ed4..071ab5541 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -187,11 +187,12 @@ const AreaChart = React.forwardRef((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && + deltaCalculation && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(value, e) => { e.stopPropagation(); - enableDeltaCalculation && setDeltaCalculation(null); + enableDeltaCalculation && deltaCalculation && setDeltaCalculation(null); }} > {showGridLines ? ( diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index 97d5225e1..802dcb266 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -146,11 +146,12 @@ const LineChart = React.forwardRef((props, ref) onMouseMove={(value, e) => { e.stopPropagation(); enableDeltaCalculation && + deltaCalculation && setDeltaCalculation((prev) => ({ ...prev, rightArea: value })); }} onMouseUp={(_, e) => { e.stopPropagation(); - enableDeltaCalculation && setDeltaCalculation(null); + enableDeltaCalculation && deltaCalculation && setDeltaCalculation(null); }} onClick={ hasOnValueChange && (activeLegend || activeDot) From 08c41970363f61c02b09d9e3ed330d60853be65d Mon Sep 17 00:00:00 2001 From: Maxime BAUCHET Date: Fri, 29 Sep 2023 17:03:56 +0200 Subject: [PATCH 15/17] fix tootip, reference area stroke color and position --- .../chart-elements/AreaChart/AreaChart.tsx | 32 +++++++++---------- .../chart-elements/LineChart/LineChart.tsx | 21 ++++++------ .../chart-elements/common/ChartTooltip.tsx | 2 +- .../common/DeltaCalculationReferenceShape.tsx | 8 ++--- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 071ab5541..4a1bdf6ee 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -344,6 +344,22 @@ const AreaChart = React.forwardRef((props, ref) ); })} + {hasDeltaCalculation ? ( + ( + + )} + /> + ) : null} {categories.map((category) => ( ((props, ref) connectNulls={connectNulls} /> ))} - {hasDeltaCalculation ? ( - ( - - )} - /> - ) : null} {hasDeltaCalculation ? categories.map((category) => ( ((props, ref) } position={{ y: 0 }} /> - {showLegend ? ( ((props, ref) } /> ) : null} + {hasDeltaCalculation ? ( + ( + + )} + /> + ) : null} {categories.map((category) => ( ((props, ref) connectNulls={connectNulls} /> ))} - {hasDeltaCalculation ? ( - ( - - )} - /> - ) : null} {onValueChange ? categories.map((category) => ( 0 ? "+" : ""}${valueFormatter( + value={`${displayedValue > 0 && hasRange ? "+" : ""}${valueFormatter( displayedValue, )} ${percentageValue}`} name={name} diff --git a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx index 32c466b70..314e43be0 100644 --- a/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx +++ b/src/components/chart-elements/common/DeltaCalculationReferenceShape.tsx @@ -43,9 +43,9 @@ const DeltaCalculationReferenceShape = (props: DeltaCalculationReferenceShapePro // common "stroke-1", // light - "stroke-tremor-content-subtle", + "stroke-tremor-border", // dark - "dark:stroke-dark-tremor-content-subtle", + "dark:stroke-dark-tremor-border", )} /> From ad9657353146c5bb0a7f124eecdfc0efc80254cb Mon Sep 17 00:00:00 2001 From: mbauchet Date: Sun, 1 Oct 2023 13:45:49 +0200 Subject: [PATCH 16/17] fix curve type delta for area --- src/components/chart-elements/AreaChart/AreaChart.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index 4a1bdf6ee..fb05ce276 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -355,7 +355,7 @@ const AreaChart = React.forwardRef((props, ref) y={y} width={width} height={height} - fill={false} + fill={["natural", "monotone"].includes(curveType)} /> )} /> @@ -369,7 +369,7 @@ const AreaChart = React.forwardRef((props, ref) ).strokeColor } strokeOpacity={ - activeDot || (activeLegend && activeLegend !== category) || hasDeltaCalculation + activeDot || (activeLegend && activeLegend !== category) || (hasDeltaCalculation && !["natural", "monotone"].includes(curveType)) ? 0.3 : 1 } @@ -455,7 +455,7 @@ const AreaChart = React.forwardRef((props, ref) connectNulls={connectNulls} /> ))} - {hasDeltaCalculation + {hasDeltaCalculation && !["natural", "monotone"].includes(curveType) ? categories.map((category) => ( Date: Mon, 2 Oct 2023 11:21:39 +0200 Subject: [PATCH 17/17] add isIncreasePositive prop --- src/components/chart-elements/AreaChart/AreaChart.tsx | 6 +++++- src/components/chart-elements/LineChart/LineChart.tsx | 2 ++ src/components/chart-elements/common/BaseChartProps.tsx | 1 + src/components/chart-elements/common/ChartTooltip.tsx | 8 +++++++- src/stories/chart-elements/AreaChart.stories.tsx | 8 ++++++++ src/stories/chart-elements/LineChart.stories.tsx | 8 ++++++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/components/chart-elements/AreaChart/AreaChart.tsx b/src/components/chart-elements/AreaChart/AreaChart.tsx index fb05ce276..37399303a 100644 --- a/src/components/chart-elements/AreaChart/AreaChart.tsx +++ b/src/components/chart-elements/AreaChart/AreaChart.tsx @@ -73,6 +73,7 @@ const AreaChart = React.forwardRef((props, ref) connectNulls = false, allowDecimals = true, enableDeltaCalculation = false, + isIncreasePositive = true, noDataText, className, onValueChange, @@ -268,6 +269,7 @@ const AreaChart = React.forwardRef((props, ref) valueFormatter={valueFormatter} categoryColors={categoryColors} deltaCalculation={deltaCalculation} + isIncreasePositive={isIncreasePositive} /> ) ) : ( @@ -369,7 +371,9 @@ const AreaChart = React.forwardRef((props, ref) ).strokeColor } strokeOpacity={ - activeDot || (activeLegend && activeLegend !== category) || (hasDeltaCalculation && !["natural", "monotone"].includes(curveType)) + activeDot || + (activeLegend && activeLegend !== category) || + (hasDeltaCalculation && !["natural", "monotone"].includes(curveType)) ? 0.3 : 1 } diff --git a/src/components/chart-elements/LineChart/LineChart.tsx b/src/components/chart-elements/LineChart/LineChart.tsx index f1d5aee74..38f1f4cb5 100644 --- a/src/components/chart-elements/LineChart/LineChart.tsx +++ b/src/components/chart-elements/LineChart/LineChart.tsx @@ -69,6 +69,7 @@ const LineChart = React.forwardRef((props, ref) connectNulls = false, allowDecimals = true, enableDeltaCalculation = false, + isIncreasePositive = true, noDataText, className, onValueChange, @@ -236,6 +237,7 @@ const LineChart = React.forwardRef((props, ref) valueFormatter={valueFormatter} categoryColors={categoryColors} deltaCalculation={deltaCalculation} + isIncreasePositive={isIncreasePositive} /> ) ) : ( diff --git a/src/components/chart-elements/common/BaseChartProps.tsx b/src/components/chart-elements/common/BaseChartProps.tsx index fea4173b2..5ca22aa50 100644 --- a/src/components/chart-elements/common/BaseChartProps.tsx +++ b/src/components/chart-elements/common/BaseChartProps.tsx @@ -32,6 +32,7 @@ interface BaseChartProps extends BaseAnimationTimingProps, React.HTMLAttributes< allowDecimals?: boolean; noDataText?: string; enableDeltaCalculation?: boolean; + isIncreasePositive?: boolean; onValueChange?: (value: EventProps) => void; } diff --git a/src/components/chart-elements/common/ChartTooltip.tsx b/src/components/chart-elements/common/ChartTooltip.tsx index d87714078..0274598d5 100644 --- a/src/components/chart-elements/common/ChartTooltip.tsx +++ b/src/components/chart-elements/common/ChartTooltip.tsx @@ -100,6 +100,7 @@ export interface ChartTooltipProps { categoryColors: Map; valueFormatter: ValueFormatter; deltaCalculation?: DeltaCalculationProps | null; + isIncreasePositive?: boolean; } const ChartTooltip = ({ @@ -109,6 +110,7 @@ const ChartTooltip = ({ categoryColors, valueFormatter, deltaCalculation, + isIncreasePositive, }: ChartTooltipProps) => { const hasRange = Boolean(deltaCalculation?.leftArea && deltaCalculation?.rightArea); @@ -182,7 +184,11 @@ const ChartTooltip = ({ )} ${percentageValue}`} name={name} color={categoryColors.get(name) ?? BaseColors.Blue} - textColor={hasRange ? getTooltipValueColor(displayedValue) : null} + textColor={ + hasRange + ? getTooltipValueColor(displayedValue * (isIncreasePositive ? 1 : -1)) + : null + } /> ); })} diff --git a/src/stories/chart-elements/AreaChart.stories.tsx b/src/stories/chart-elements/AreaChart.stories.tsx index ef5697ad4..fe0d772e8 100644 --- a/src/stories/chart-elements/AreaChart.stories.tsx +++ b/src/stories/chart-elements/AreaChart.stories.tsx @@ -224,6 +224,14 @@ WithEnableDeltaCalculation.args = { enableDeltaCalculation: true, }; +export const WithEnableDeltaCalculationAndIncreaseNegative = DefaultTemplate.bind({}); +WithEnableDeltaCalculationAndIncreaseNegative.args = { + ...args, + data: data, + enableDeltaCalculation: true, + isIncreasePositive: false, +}; + export const WithOnValueChange = DefaultTemplate.bind({}); // More on args: https://storybook.js.org/docs/react/writing-stories/args WithOnValueChange.args = { diff --git a/src/stories/chart-elements/LineChart.stories.tsx b/src/stories/chart-elements/LineChart.stories.tsx index 11abc6698..fd9a03e89 100644 --- a/src/stories/chart-elements/LineChart.stories.tsx +++ b/src/stories/chart-elements/LineChart.stories.tsx @@ -221,6 +221,14 @@ WithEnableDeltaCalculation.args = { enableDeltaCalculation: true, }; +export const WithEnableDeltaCalculationAndIncreaseNegative = DefaultTemplate.bind({}); +WithEnableDeltaCalculationAndIncreaseNegative.args = { + ...args, + data: data, + enableDeltaCalculation: true, + isIncreasePositive: false, +}; + export const WithoutOnClickAnimation = DefaultTemplate.bind({}); WithoutOnClickAnimation.args = { data: data,