diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/__tests__/useBarChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/__tests__/useBarChartData.test.ts index dc39453511..da786d3f34 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/__tests__/useBarChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/__tests__/useBarChartData.test.ts @@ -12,10 +12,6 @@ describe('useBarChartData', () => { const mockColorRegistry: GraphColorRegistry = { green: { name: 'green', - gradient: { - normal: ['green1', 'green2'], - hover: ['green3', 'green4'], - }, solid: 'greenSolid', variations: [ 'green1', @@ -34,10 +30,6 @@ describe('useBarChartData', () => { }, purple: { name: 'purple', - gradient: { - normal: ['purple1', 'purple2'], - hover: ['purple3', 'purple4'], - }, solid: 'purpleSolid', variations: [ 'purple1', @@ -99,7 +91,6 @@ describe('useBarChartData', () => { indexValue: 'Jan', colorScheme: { name: 'green', - gradient: mockColorRegistry.green.gradient, }, }); expect(result.current.barConfigs[1]).toMatchObject({ @@ -107,7 +98,6 @@ describe('useBarChartData', () => { indexValue: 'Jan', colorScheme: { name: 'purple', - gradient: mockColorRegistry.purple.gradient, }, }); }); @@ -129,7 +119,6 @@ describe('useBarChartData', () => { label: 'Sales', colorScheme: { name: 'green', - gradient: mockColorRegistry.green.gradient, }, }); expect(result.current.enrichedKeys[0].colorScheme.solid).toBeDefined(); @@ -138,7 +127,6 @@ describe('useBarChartData', () => { label: 'Costs', colorScheme: { name: 'purple', - gradient: mockColorRegistry.purple.gradient, }, }); expect(result.current.enrichedKeys[1].colorScheme.solid).toBeDefined(); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/components/GraphWidgetGaugeChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/components/GraphWidgetGaugeChart.tsx index d97e1c3137..7ba0c24198 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/components/GraphWidgetGaugeChart.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/components/GraphWidgetGaugeChart.tsx @@ -2,7 +2,6 @@ import { GraphWidgetLegend } from '@/page-layout/widgets/graph/components/GraphW import { GraphWidgetTooltip } from '@/page-layout/widgets/graph/components/GraphWidgetTooltip'; import { GaugeChartEndLine } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/components/GaugeChartEndLine'; import { useGaugeChartData } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartData'; -import { useGaugeChartHandlers } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartHandlers'; import { useGaugeChartTooltip } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartTooltip'; import { type GaugeChartData } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/types/GaugeChartData'; import { createGraphColorRegistry } from '@/page-layout/widgets/graph/utils/createGraphColorRegistry'; @@ -17,7 +16,7 @@ import { type RadialBarCustomLayerProps, ResponsiveRadialBar, } from '@nivo/radial-bar'; -import { useId } from 'react'; +import { isDefined } from 'twenty-shared/utils'; import { H1Title, H1TitleFontColor } from 'twenty-ui/display'; type GraphWidgetGaugeChartProps = { @@ -62,7 +61,6 @@ export const GraphWidgetGaugeChart = ({ data, showValue = true, showLegend = true, - id, displayType, decimals, prefix, @@ -71,7 +69,6 @@ export const GraphWidgetGaugeChart = ({ onGaugeClick, }: GraphWidgetGaugeChartProps) => { const theme = useTheme(); - const instanceId = useId(); const colorRegistry = createGraphColorRegistry(theme); const formatOptions: GraphValueFormatOptions = { @@ -81,24 +78,17 @@ export const GraphWidgetGaugeChart = ({ suffix, customFormatter, }; + const handleClick = () => { + onGaugeClick?.(data); + }; - const { isHovered, setIsHovered, handleClick, hasClickableItems } = - useGaugeChartHandlers({ data, onGaugeClick }); + const hasClickableItems = isDefined(onGaugeClick); - const { - colorScheme, - normalizedValue, - clampedNormalizedValue, - chartData, - gradientId, - defs, - } = useGaugeChartData({ - data, - colorRegistry, - id, - instanceId, - isHovered, - }); + const { colorScheme, normalizedValue, clampedNormalizedValue, chartData } = + useGaugeChartData({ + data, + colorRegistry, + }); const { createTooltipData } = useGaugeChartTooltip({ value: data.value, @@ -133,23 +123,13 @@ export const GraphWidgetGaugeChart = ({ endAngle={90} innerRadius={0.7} padding={0.2} - colors={[`url(#${gradientId})`, theme.background.tertiary]} - defs={defs} - fill={[ - { - match: (d: { x: string }) => d.x === 'value', - id: gradientId, - }, - ]} enableTracks={false} enableRadialGrid={false} enableCircularGrid={false} enableLabels={false} isInteractive={true} tooltip={renderTooltip} - onClick={handleClick} - onMouseEnter={() => setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} + onClick={hasClickableItems ? handleClick : undefined} layers={['bars', renderValueEndLine]} /> {showValue && ( diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/__tests__/useGaugeChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/__tests__/useGaugeChartData.test.ts index 530f2a2de5..a1cde3a179 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/__tests__/useGaugeChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/__tests__/useGaugeChartData.test.ts @@ -7,10 +7,6 @@ describe('useGaugeChartData', () => { const mockColorRegistry: GraphColorRegistry = { blue: { name: 'blue', - gradient: { - normal: ['blue1', 'blue2'], - hover: ['blue3', 'blue4'], - }, solid: 'blueSolid', variations: [ 'blue1', @@ -29,10 +25,6 @@ describe('useGaugeChartData', () => { }, green: { name: 'green', - gradient: { - normal: ['green1', 'green2'], - hover: ['green3', 'green4'], - }, solid: 'greenSolid', variations: [ 'green1', @@ -62,9 +54,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -83,9 +72,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -104,9 +90,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -125,9 +108,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -146,9 +126,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -168,9 +145,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -188,9 +162,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -208,9 +179,6 @@ describe('useGaugeChartData', () => { useGaugeChartData({ data, colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, }), ); @@ -225,84 +193,6 @@ describe('useGaugeChartData', () => { ]); }); - it('should generate gradient with correct angle', () => { - const data: GaugeChartData = { - value: 50, - min: 0, - max: 100, - }; - - const { result } = renderHook(() => - useGaugeChartData({ - data, - colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: false, - }), - ); - - expect(result.current.defs[0].id).toBe( - 'gaugeGradient-test-gauge-instance-1', - ); - const expectedAngle = -45; - const expectedRadians = (expectedAngle * Math.PI) / 180 + Math.PI / 2; - const expectedSin = Math.sin(expectedRadians); - const expectedCos = -Math.cos(expectedRadians); - const expectedX1 = 50 - expectedSin * 50; - const expectedY1 = 50 - expectedCos * 50; - - const actualX1 = parseFloat(result.current.defs[0].x1); - const actualY1 = parseFloat(result.current.defs[0].y1); - expect(actualX1).toBeCloseTo(expectedX1, 5); - expect(actualY1).toBeCloseTo(expectedY1, 5); - }); - - it('should handle hover state', () => { - const data: GaugeChartData = { - value: 50, - min: 0, - max: 100, - }; - - const { result } = renderHook(() => - useGaugeChartData({ - data, - colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered: true, - }), - ); - - expect(result.current.defs[0].colors).toEqual([ - { offset: 0, color: 'blue3' }, - { offset: 100, color: 'blue4' }, - ]); - }); - - it('should generate unique gradient id', () => { - const data: GaugeChartData = { - value: 50, - min: 0, - max: 100, - }; - - const { result } = renderHook(() => - useGaugeChartData({ - data, - colorRegistry: mockColorRegistry, - id: 'unique-id', - instanceId: 'unique-instance', - isHovered: false, - }), - ); - - expect(result.current.gradientId).toBe( - 'gaugeGradient-unique-id-unique-instance', - ); - }); - it('should memoize calculations', () => { const data: GaugeChartData = { value: 50, @@ -310,23 +200,16 @@ describe('useGaugeChartData', () => { max: 100, }; - const { result, rerender } = renderHook( - ({ isHovered }) => - useGaugeChartData({ - data, - colorRegistry: mockColorRegistry, - id: 'test-gauge', - instanceId: 'instance-1', - isHovered, - }), - { initialProps: { isHovered: false } }, + const { result } = renderHook(() => + useGaugeChartData({ + data, + colorRegistry: mockColorRegistry, + }), ); const firstColorScheme = result.current.colorScheme; const firstChartData = result.current.chartData; - rerender({ isHovered: false }); - expect(result.current.colorScheme).toBe(firstColorScheme); expect(result.current.chartData).toBe(firstChartData); }); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartData.ts index 6f8946be5a..2746f8b1fa 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartData.ts @@ -1,23 +1,16 @@ import { type GaugeChartData } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/types/GaugeChartData'; import { type GraphColorRegistry } from '@/page-layout/widgets/graph/types/GraphColorRegistry'; -import { createGradientDef } from '@/page-layout/widgets/graph/utils/createGradientDef'; import { getColorScheme } from '@/page-layout/widgets/graph/utils/getColorScheme'; import { useMemo } from 'react'; type UseGaugeChartDataProps = { data: GaugeChartData; colorRegistry: GraphColorRegistry; - id: string; - instanceId: string; - isHovered: boolean; }; export const useGaugeChartData = ({ data, colorRegistry, - id, - instanceId, - isHovered, }: UseGaugeChartDataProps) => { const { value, min, max, color = 'blue' } = data; @@ -53,20 +46,10 @@ export const useGaugeChartData = ({ [clampedNormalizedValue], ); - const gradientId = `gaugeGradient-${id}-${instanceId}`; - const gaugeAngle = -90 + (clampedNormalizedValue / 100) * 90; - - const defs = useMemo( - () => [createGradientDef(colorScheme, gradientId, isHovered, gaugeAngle)], - [colorScheme, gradientId, isHovered, gaugeAngle], - ); - return { colorScheme, normalizedValue, clampedNormalizedValue, chartData, - gradientId, - defs, }; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartHandlers.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartHandlers.ts deleted file mode 100644 index 3eaf9013ac..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetGaugeChart/hooks/useGaugeChartHandlers.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type GaugeChartData } from '@/page-layout/widgets/graph/graphWidgetGaugeChart/types/GaugeChartData'; -import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; - -type UseGaugeChartHandlersProps = { - data: GaugeChartData; - onGaugeClick?: (data: GaugeChartData) => void; -}; - -export const useGaugeChartHandlers = ({ - data, - onGaugeClick, -}: UseGaugeChartHandlersProps) => { - const [isHovered, setIsHovered] = useState(false); - - const handleClick = () => { - onGaugeClick?.(data); - }; - - const hasClickableItems = isDefined(onGaugeClick); - - return { - isHovered, - setIsHovered, - handleClick, - hasClickableItems, - }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/CustomStackedAreasLayer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/CustomStackedAreasLayer.tsx new file mode 100644 index 0000000000..0cbe2ceee4 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/CustomStackedAreasLayer.tsx @@ -0,0 +1,99 @@ +import { LineAnimatedAreaPath } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/LineAnimatedAreaPath'; +import { LineAreaGradientDefs } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/LineAreaGradientDefs'; +import { type LineChartEnrichedSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries'; +import { computeLineAreaPath } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/computeLineAreaPath'; +import { + type ComputedSeries, + type LineCustomSvgLayerProps, + type LineSeries, +} from '@nivo/line'; +import { isNumberOrNaN } from '@sniptt/guards'; +import { useMemo } from 'react'; +import { isDefined } from 'twenty-shared/utils'; + +type CustomStackedAreasLayerProps = { + series: readonly ComputedSeries[]; + innerHeight: number; + enrichedSeries: LineChartEnrichedSeries[]; + enableArea: boolean; + yScale: LineCustomSvgLayerProps['yScale']; + isStacked: boolean; +}; + +type AreaPathData = { + id: string; + path: string; + fillId: string; +}; + +export const CustomStackedAreasLayer = ({ + series, + innerHeight, + enrichedSeries, + enableArea, + yScale, + isStacked, +}: CustomStackedAreasLayerProps) => { + const seriesById = useMemo( + () => + new Map( + enrichedSeries.map((seriesItem) => [String(seriesItem.id), seriesItem]), + ), + [enrichedSeries], + ); + + const baseline = useMemo(() => { + const scaled = yScale(0); + if (!isNumberOrNaN(scaled)) { + return innerHeight; + } + return Math.max(0, Math.min(scaled, innerHeight)); + }, [innerHeight, yScale]); + + const paths = useMemo(() => { + if (!enableArea) { + return []; + } + + const initialAreaPathData: AreaPathData[] = []; + + return series.reduce((acc, currentSeries, index) => { + const previousStackedSeries = + isStacked && index > 0 ? series[index - 1] : null; + + const enriched = seriesById.get(String(currentSeries.id)); + if (!isDefined(enriched)) { + return acc; + } + + const path = computeLineAreaPath({ + currentSeries, + previousStackedSeries, + baseline, + }); + + if (isDefined(path)) { + acc.push({ + id: String(currentSeries.id), + path, + fillId: enriched.areaFillId, + }); + } + + return acc; + }, initialAreaPathData); + }, [enableArea, series, seriesById, baseline, isStacked]); + + if (!enableArea) { + return null; + } + + return ( + + + {paths.map(({ id, path, fillId }) => ( + + ))} + + ); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx index 4fd1fb2326..3fcda678d5 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx @@ -5,6 +5,7 @@ import { type SliceHoverData, } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/CustomCrosshairLayer'; import { CustomPointLabelsLayer } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/CustomPointLabelsLayer'; +import { CustomStackedAreasLayer } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/CustomStackedAreasLayer'; import { GraphLineChartTooltip } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/GraphLineChartTooltip'; import { LINE_CHART_MARGIN_BOTTOM } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginBottom'; import { LINE_CHART_MARGIN_LEFT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginLeft'; @@ -34,12 +35,13 @@ import { type Point, type SliceTooltipProps, } from '@nivo/line'; -import { useCallback, useId, useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { useDebouncedCallback } from 'use-debounce'; type CrosshairLayerProps = LineCustomSvgLayerProps; type PointLabelsLayerProps = LineCustomSvgLayerProps; +type StackedAreasLayerProps = LineCustomSvgLayerProps; type GraphWidgetLineChartProps = { data: LineChartSeries[]; @@ -87,7 +89,6 @@ export const GraphWidgetLineChart = ({ onSliceClick, }: GraphWidgetLineChartProps) => { const theme = useTheme(); - const instanceId = useId(); const colorRegistry = createGraphColorRegistry(theme); const chartTheme = useLineChartTheme(); const containerRef = useRef(null); @@ -105,15 +106,11 @@ export const GraphWidgetLineChart = ({ const effectiveMinimumValue = rangeMin ?? calculatedValueRange.minimum; const effectiveMaximumValue = rangeMax ?? calculatedValueRange.maximum; - const { enrichedSeries, nivoData, defs, fill, colors, legendItems } = - useLineChartData({ - data, - colorRegistry, - id, - instanceId, - enableArea, - theme, - }); + const { enrichedSeries, nivoData, colors, legendItems } = useLineChartData({ + data, + colorRegistry, + id, + }); const hasClickableItems = isDefined(onSliceClick); @@ -194,6 +191,17 @@ export const GraphWidgetLineChart = ({ /> ); + const StackedAreasLayer = (layerProps: StackedAreasLayerProps) => ( + + ); + const axisBottomConfig = getLineChartAxisBottomConfig( xAxisLabel, chartWidth, @@ -232,16 +240,11 @@ export const GraphWidgetLineChart = ({ }} curve={'monotoneX'} lineWidth={1} - enableArea={enableArea} - areaBaselineValue={0} enablePoints={true} pointSize={0} enablePointLabel={false} pointBorderWidth={0} colors={colors} - areaBlendMode={'normal'} - defs={defs} - fill={fill} axisTop={null} axisRight={null} axisBottom={axisBottomConfig} @@ -255,15 +258,13 @@ export const GraphWidgetLineChart = ({ 'grid', 'markers', 'axes', - 'areas', + StackedAreasLayer, 'lines', CrosshairLayer, 'points', PointLabelsLayer, 'legends', ]} - useMesh={true} - crosshairType="cross" theme={chartTheme} /> diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAnimatedAreaPath.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAnimatedAreaPath.tsx new file mode 100644 index 0000000000..725d744e94 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAnimatedAreaPath.tsx @@ -0,0 +1,23 @@ +import { useMotionConfig } from '@nivo/core'; +import { animated, useSpring } from '@react-spring/web'; + +type LineAnimatedAreaPathProps = { + path: string; + fillId: string; +}; + +export const LineAnimatedAreaPath = ({ + path, + fillId, +}: LineAnimatedAreaPathProps) => { + const { animate, config: motionConfig } = useMotionConfig(); + const spring = useSpring({ + d: path, + config: motionConfig, + immediate: !animate, + }); + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAreaGradientDefs.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAreaGradientDefs.tsx new file mode 100644 index 0000000000..5c1aa9d566 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/LineAreaGradientDefs.tsx @@ -0,0 +1,40 @@ +import { type LineChartEnrichedSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries'; +import { createAreaFillDef } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/createAreaFillDef'; + +type LineAreaGradientDefsProps = { + enrichedSeries: LineChartEnrichedSeries[]; +}; + +export const LineAreaGradientDefs = ({ + enrichedSeries, +}: LineAreaGradientDefsProps) => { + return ( + + {enrichedSeries.map((seriesItem) => { + const def = createAreaFillDef( + seriesItem.colorScheme, + seriesItem.areaFillId, + ); + return ( + + {def.colors.map((color, idx) => ( + + ))} + + ); + })} + + ); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillEndOpacity.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillEndOpacity.ts new file mode 100644 index 0000000000..426b35f13b --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillEndOpacity.ts @@ -0,0 +1 @@ +export const LINE_AREA_FILL_END_OPACITY = 0.01; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillStartOpacity.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillStartOpacity.ts new file mode 100644 index 0000000000..85c1fcbba5 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillStartOpacity.ts @@ -0,0 +1 @@ +export const LINE_AREA_FILL_START_OPACITY = 0.18; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/__tests__/useLineChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/__tests__/useLineChartData.test.ts index 9573a7523c..c12a6e123c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/__tests__/useLineChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/__tests__/useLineChartData.test.ts @@ -1,7 +1,6 @@ import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries'; import { type GraphColorRegistry } from '@/page-layout/widgets/graph/types/GraphColorRegistry'; import { renderHook } from '@testing-library/react'; -import { type ThemeType } from 'twenty-ui/theme'; import { useLineChartData } from '../useLineChartData'; @@ -9,10 +8,6 @@ describe('useLineChartData', () => { const mockColorRegistry: GraphColorRegistry = { red: { name: 'red', - gradient: { - normal: ['red1', 'red2'], - hover: ['red3', 'red4'], - }, solid: 'redSolid', variations: [ 'red1', @@ -31,10 +26,6 @@ describe('useLineChartData', () => { }, blue: { name: 'blue', - gradient: { - normal: ['blue1', 'blue2'], - hover: ['blue3', 'blue4'], - }, solid: 'blueSolid', variations: [ 'blue1', @@ -53,8 +44,6 @@ describe('useLineChartData', () => { }, }; - const mockTheme = { name: 'light' } as ThemeType; - const mockData: LineChartSeries[] = [ { id: 'series1', @@ -77,61 +66,24 @@ describe('useLineChartData', () => { }, ]; - it('should enrich series with color schemes', () => { + it('should enrich series with color schemes and area fill ids', () => { const { result } = renderHook(() => useLineChartData({ data: mockData, colorRegistry: mockColorRegistry, id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, }), ); - expect(result.current.enrichedSeries[0].gradientId).toBe( - 'lineGradient-test-chart-instance-1-series1-0', - ); expect(result.current.enrichedSeries[0].label).toBe('Sales'); + expect(result.current.enrichedSeries[0].areaFillId).toBe( + 'areaFill-test-chart-series1-0', + ); expect(result.current.enrichedSeries[1].label).toBe('Costs'); - }); - - it('should handle enableArea per series', () => { - const dataWithArea: LineChartSeries[] = [ - { ...mockData[0], enableArea: true }, - { ...mockData[1], enableArea: false }, - ]; - - const { result } = renderHook(() => - useLineChartData({ - data: dataWithArea, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: true, - theme: mockTheme, - }), + expect(result.current.enrichedSeries[1].areaFillId).toBe( + 'areaFill-test-chart-series2-1', ); - - expect(result.current.enrichedSeries[0].shouldEnableArea).toBe(true); - expect(result.current.enrichedSeries[1].shouldEnableArea).toBe(false); - }); - - it('should use global enableArea when series does not specify', () => { - const { result } = renderHook(() => - useLineChartData({ - data: mockData, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: true, - theme: mockTheme, - }), - ); - - expect(result.current.enrichedSeries[0].shouldEnableArea).toBe(true); - expect(result.current.enrichedSeries[1].shouldEnableArea).toBe(true); }); it('should format data for Nivo', () => { @@ -140,9 +92,6 @@ describe('useLineChartData', () => { data: mockData, colorRegistry: mockColorRegistry, id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, }), ); @@ -166,113 +115,12 @@ describe('useLineChartData', () => { ]); }); - it('should generate defs only for series with area enabled', () => { - const dataWithArea: LineChartSeries[] = [ - { ...mockData[0], enableArea: true }, - { ...mockData[1], enableArea: false }, - ]; - - const { result } = renderHook(() => - useLineChartData({ - data: dataWithArea, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, - }), - ); - - expect(result.current.defs).toHaveLength(1); - expect(result.current.defs[0].id).toBe( - 'lineGradient-test-chart-instance-1-series1-0', - ); - }); - - it('should reverse gradient for light theme', () => { - const dataWithArea: LineChartSeries[] = [ - { ...mockData[0], enableArea: true }, - ]; - - const { result } = renderHook(() => - useLineChartData({ - data: dataWithArea, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, - }), - ); - - expect(result.current.defs[0].colors).toEqual([ - { offset: 0, color: 'red2' }, - { offset: 100, color: 'red1' }, - ]); - }); - - it('should not reverse gradient for dark theme', () => { - const dataWithArea: LineChartSeries[] = [ - { ...mockData[0], enableArea: true }, - ]; - - const darkTheme = { name: 'dark' } as ThemeType; - - const { result } = renderHook(() => - useLineChartData({ - data: dataWithArea, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: darkTheme, - }), - ); - - expect(result.current.defs[0].colors).toEqual([ - { offset: 0, color: 'red1' }, - { offset: 100, color: 'red2' }, - ]); - }); - - it('should generate fill configuration', () => { - const dataWithArea: LineChartSeries[] = [ - { ...mockData[0], enableArea: true }, - { ...mockData[1], enableArea: true }, - ]; - - const { result } = renderHook(() => - useLineChartData({ - data: dataWithArea, - colorRegistry: mockColorRegistry, - id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, - }), - ); - - expect(result.current.fill).toEqual([ - { - match: { id: 'series1' }, - id: 'lineGradient-test-chart-instance-1-series1-0', - }, - { - match: { id: 'series2' }, - id: 'lineGradient-test-chart-instance-1-series2-1', - }, - ]); - }); - it('should calculate legend items with totals', () => { const { result } = renderHook(() => useLineChartData({ data: mockData, colorRegistry: mockColorRegistry, id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, }), ); @@ -296,16 +144,11 @@ describe('useLineChartData', () => { data: [], colorRegistry: mockColorRegistry, id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, }), ); expect(result.current.enrichedSeries).toEqual([]); expect(result.current.nivoData).toEqual([]); - expect(result.current.defs).toEqual([]); - expect(result.current.fill).toEqual([]); expect(result.current.colors).toEqual([]); expect(result.current.legendItems).toEqual([]); }); @@ -323,9 +166,6 @@ describe('useLineChartData', () => { data: dataWithoutLabel, colorRegistry: mockColorRegistry, id: 'test-chart', - instanceId: 'instance-1', - enableArea: false, - theme: mockTheme, }), ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartData.ts index 5bf4999510..d5720bb561 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartData.ts @@ -1,93 +1,50 @@ import { type LineChartEnrichedSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries'; import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries'; import { type GraphColorRegistry } from '@/page-layout/widgets/graph/types/GraphColorRegistry'; -import { createGradientDef } from '@/page-layout/widgets/graph/utils/createGradientDef'; import { getColorScheme } from '@/page-layout/widgets/graph/utils/getColorScheme'; import { type LineSeries } from '@nivo/line'; import { useMemo } from 'react'; -import { type ThemeType } from 'twenty-ui/theme'; type UseLineChartDataProps = { data: LineChartSeries[]; colorRegistry: GraphColorRegistry; id: string; - instanceId: string; - enableArea: boolean; - theme: ThemeType; }; export const useLineChartData = ({ data, colorRegistry, id, - instanceId, - enableArea, - theme, }: UseLineChartDataProps) => { - const enrichedSeries = useMemo((): LineChartEnrichedSeries[] => { - return data.map((series, index) => { + return useMemo(() => { + const enrichedSeries: LineChartEnrichedSeries[] = []; + const nivoData: LineSeries[] = []; + const colors: string[] = []; + const legendItems: { id: string; label: string; color: string }[] = []; + + for (const [index, series] of data.entries()) { const colorScheme = getColorScheme({ registry: colorRegistry, colorName: series.color, fallbackIndex: index, totalGroups: data.length, }); - const shouldEnableArea = series.enableArea ?? enableArea; - const gradientId = `lineGradient-${id}-${instanceId}-${series.id}-${index}`; - return { - ...series, - colorScheme, - gradientId, - shouldEnableArea, - label: series.label || series.id, - }; - }); - }, [data, colorRegistry, id, instanceId, enableArea]); + const sanitizedSeriesId = series.id + .replace(/\s+/g, '_') + .replace(/[^a-zA-Z0-9_-]/g, ''); + const areaFillId = `areaFill-${id}-${sanitizedSeriesId}-${index}`; + const label = series.label || series.id; - const nivoData: LineSeries[] = data.map((series) => ({ - id: series.id, - data: series.data.map((point) => ({ - x: point.x, - y: point.y, - })), - })); + enrichedSeries.push({ ...series, colorScheme, areaFillId, label }); + nivoData.push({ + id: series.id, + data: series.data.map((point) => ({ x: point.x, y: point.y })), + }); + colors.push(colorScheme.solid); + legendItems.push({ id: series.id, label, color: colorScheme.solid }); + } - const seriesWithArea = enrichedSeries.filter( - (series) => series.shouldEnableArea, - ); - - const defs = seriesWithArea.map((series) => - createGradientDef( - series.colorScheme, - series.gradientId, - false, - 90, - theme.name === 'light', - ), - ); - - const fill = seriesWithArea.map((series) => ({ - match: { id: series.id }, - id: series.gradientId, - })); - - const colors = enrichedSeries.map((series) => series.colorScheme.solid); - - const legendItems = enrichedSeries.map((series) => { - return { - id: series.id, - label: series.label, - color: series.colorScheme.solid, - }; - }); - - return { - enrichedSeries, - nivoData, - defs, - fill, - colors, - legendItems, - }; + return { enrichedSeries, nivoData, colors, legendItems }; + }, [data, colorRegistry, id]); }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries.ts index 52e10fbd6b..de588cf936 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries.ts @@ -3,7 +3,6 @@ import { type GraphColorScheme } from '@/page-layout/widgets/graph/types/GraphCo export type LineChartEnrichedSeries = LineChartSeries & { colorScheme: GraphColorScheme; - gradientId: string; - shouldEnableArea: boolean; + areaFillId: string; label: string; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries.ts index a9a6fcde00..34d4f53e5f 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries.ts @@ -6,5 +6,4 @@ export type LineChartSeries = { label?: string; color?: GraphColor; data: LineChartDataPoint[]; - enableArea?: boolean; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/__tests__/computeLineAreaPath.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/__tests__/computeLineAreaPath.test.ts new file mode 100644 index 0000000000..47a4cd99cc --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/__tests__/computeLineAreaPath.test.ts @@ -0,0 +1,76 @@ +import { type ComputedSeries, type LineSeries } from '@nivo/line'; + +jest.mock('d3-shape', () => { + const createAreaGenerator = () => { + let y0Fn: (d: any, i: number) => number = () => 0; + + const generator = ((data: any[]) => + data + .map((d, i) => `(${d.position.x},${y0Fn(d, i)},${d.position.y})`) + .join('|')) as any; + + generator.defined = () => generator; + generator.x = () => generator; + generator.y1 = () => generator; + generator.y0 = (fn: (d: any, i: number) => number) => { + y0Fn = fn; + return generator; + }; + generator.curve = () => generator; + + return generator; + }; + + return { + area: createAreaGenerator, + curveMonotoneX: () => 'mockCurve', + }; +}); + +import { computeLineAreaPath } from '../computeLineAreaPath'; + +const buildComputedSeries = ( + points: { x: number; y: number }[], +): ComputedSeries => + ({ + id: 'series', + data: points.map(({ x, y }) => ({ + data: { x, y }, + position: { x, y }, + })), + }) as unknown as ComputedSeries; + +describe('computeLineAreaPath', () => { + it('fills to the provided baseline for non-stacked mode', () => { + const currentSeries = buildComputedSeries([ + { x: 0, y: 10 }, + { x: 10, y: 20 }, + ]); + + const path = computeLineAreaPath({ + currentSeries, + baseline: 100, + }); + + expect(path).toBe('(0,100,10)|(10,100,20)'); + }); + + it('fills between current and previous series for stacked mode', () => { + const currentSeries = buildComputedSeries([ + { x: 0, y: 10 }, + { x: 10, y: 20 }, + ]); + const previousStackedSeries = buildComputedSeries([ + { x: 0, y: 5 }, + { x: 10, y: 15 }, + ]); + + const path = computeLineAreaPath({ + currentSeries, + previousStackedSeries, + baseline: 100, + }); + + expect(path).toBe('(0,5,10)|(10,15,20)'); + }); +}); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/computeLineAreaPath.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/computeLineAreaPath.ts new file mode 100644 index 0000000000..7bbb6e1ac6 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/computeLineAreaPath.ts @@ -0,0 +1,37 @@ +import { area, curveMonotoneX, type CurveFactory } from 'd3-shape'; +import { isDefined } from 'twenty-shared/utils'; + +import { type ComputedSeries, type LineSeries } from '@nivo/line'; + +type ComputeLineAreaPathParams = { + currentSeries: ComputedSeries; + previousStackedSeries?: ComputedSeries | null; + baseline: number; + curve?: CurveFactory; +}; + +export const computeLineAreaPath = ({ + currentSeries, + previousStackedSeries, + baseline, + curve = curveMonotoneX, +}: ComputeLineAreaPathParams) => { + type PositionData = (typeof currentSeries.data)[number]; + + const areaGenerator = area() + .defined((d) => d.position.x !== null && d.position.y !== null) + .x((d) => d.position.x ?? 0) + .y1((d) => d.position.y ?? 0) + .y0((_, index) => { + if (isDefined(previousStackedSeries)) { + const previousPoint = previousStackedSeries.data[index]; + if (isDefined(previousPoint) && isDefined(previousPoint.position.y)) { + return previousPoint.position.y; + } + } + return baseline; + }) + .curve(curve); + + return areaGenerator(currentSeries.data); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/createAreaFillDef.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/createAreaFillDef.ts new file mode 100644 index 0000000000..0f270558c0 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/utils/createAreaFillDef.ts @@ -0,0 +1,29 @@ +import { LINE_AREA_FILL_END_OPACITY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillEndOpacity'; +import { LINE_AREA_FILL_START_OPACITY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillStartOpacity'; +import { type GraphColorScheme } from '@/page-layout/widgets/graph/types/GraphColorScheme'; + +export const createAreaFillDef = ( + colorScheme: GraphColorScheme, + id: string, +) => { + return { + id, + type: 'linearGradient' as const, + x1: '0%', + y1: '0%', + x2: '0%', + y2: '100%', + colors: [ + { + offset: 0, + color: colorScheme.solid, + opacity: LINE_AREA_FILL_START_OPACITY, + }, + { + offset: 100, + color: colorScheme.solid, + opacity: LINE_AREA_FILL_END_OPACITY, + }, + ], + }; +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/__tests__/usePieChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/__tests__/usePieChartData.test.ts index 7bf68199ce..3ca02ac3d5 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/__tests__/usePieChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/__tests__/usePieChartData.test.ts @@ -12,10 +12,6 @@ describe('usePieChartData', () => { const mockColorRegistry: GraphColorRegistry = { red: { name: 'red', - gradient: { - normal: ['red1', 'red2'], - hover: ['red3', 'red4'], - }, solid: 'redSolid', variations: [ 'red1', @@ -34,10 +30,6 @@ describe('usePieChartData', () => { }, blue: { name: 'blue', - gradient: { - normal: ['blue1', 'blue2'], - hover: ['blue3', 'blue4'], - }, solid: 'blueSolid', variations: [ 'blue1', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/types/GraphColorScheme.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/types/GraphColorScheme.ts index 017d139dd4..c646de8c56 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/types/GraphColorScheme.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/types/GraphColorScheme.ts @@ -1,9 +1,5 @@ export type GraphColorScheme = { name: string; - gradient: { - normal: [string, string]; - hover: [string, string]; - }; solid: string; variations: [ string, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGradientDef.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGradientDef.ts deleted file mode 100644 index 0be9ddc340..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGradientDef.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { type GraphColorScheme } from '../types/GraphColorScheme'; -import { calculateAngularGradient } from './calculateAngularGradient'; - -export const createGradientDef = ( - colorScheme: GraphColorScheme, - id: string, - isHovered: boolean = false, - angle?: number, - reverseGradient: boolean = false, -) => { - const colors = isHovered - ? colorScheme.gradient.hover - : colorScheme.gradient.normal; - - const coords = - angle !== undefined - ? calculateAngularGradient(angle) - : { x1: '0%', y1: '0%', x2: '0%', y2: '100%' }; - - return { - id, - type: 'linearGradient' as const, - ...coords, - colors: reverseGradient - ? [ - { offset: 0, color: colors[1] }, - { offset: 100, color: colors[0] }, - ] - : [ - { offset: 0, color: colors[0] }, - { offset: 100, color: colors[1] }, - ], - }; -}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGraphColorRegistry.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGraphColorRegistry.ts index 9a6c86cc69..82a8997c1c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGraphColorRegistry.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/createGraphColorRegistry.ts @@ -7,10 +7,6 @@ export const createGraphColorRegistry = ( ): GraphColorRegistry => ({ blue: { name: 'blue', - gradient: { - normal: [theme.color.blue3, theme.color.blue5], - hover: [theme.color.blue7, theme.color.blue8], - }, solid: theme.color.blue8, variations: [ theme.color.blue1, @@ -29,10 +25,6 @@ export const createGraphColorRegistry = ( }, purple: { name: 'purple', - gradient: { - normal: [theme.color.purple3, theme.color.purple5], - hover: [theme.color.purple7, theme.color.purple8], - }, solid: theme.color.purple8, variations: [ theme.color.purple1, @@ -51,10 +43,6 @@ export const createGraphColorRegistry = ( }, turquoise: { name: 'turquoise', - gradient: { - normal: [theme.color.turquoise3, theme.color.turquoise5], - hover: [theme.color.turquoise7, theme.color.turquoise8], - }, solid: theme.color.turquoise8, variations: [ theme.color.turquoise1, @@ -73,10 +61,6 @@ export const createGraphColorRegistry = ( }, orange: { name: 'orange', - gradient: { - normal: [theme.color.orange3, theme.color.orange5], - hover: [theme.color.orange7, theme.color.orange8], - }, solid: theme.color.orange8, variations: [ theme.color.orange1, @@ -95,10 +79,6 @@ export const createGraphColorRegistry = ( }, pink: { name: 'pink', - gradient: { - normal: [theme.color.pink3, theme.color.pink5], - hover: [theme.color.pink7, theme.color.pink8], - }, solid: theme.color.pink8, variations: [ theme.color.pink1, @@ -117,10 +97,6 @@ export const createGraphColorRegistry = ( }, yellow: { name: 'yellow', - gradient: { - normal: [theme.color.yellow3, theme.color.yellow5], - hover: [theme.color.yellow7, theme.color.yellow8], - }, solid: theme.color.yellow8, variations: [ theme.color.yellow1, @@ -139,10 +115,6 @@ export const createGraphColorRegistry = ( }, red: { name: 'red', - gradient: { - normal: [theme.color.red3, theme.color.red5], - hover: [theme.color.red7, theme.color.red8], - }, solid: theme.color.red8, variations: [ theme.color.red1, @@ -161,10 +133,6 @@ export const createGraphColorRegistry = ( }, green: { name: 'green', - gradient: { - normal: [theme.color.green3, theme.color.green5], - hover: [theme.color.green7, theme.color.green8], - }, solid: theme.color.green8, variations: [ theme.color.green1, @@ -183,10 +151,6 @@ export const createGraphColorRegistry = ( }, sky: { name: 'sky', - gradient: { - normal: [theme.color.sky3, theme.color.sky5], - hover: [theme.color.sky7, theme.color.sky8], - }, solid: theme.color.sky8, variations: [ theme.color.sky1, @@ -205,10 +169,6 @@ export const createGraphColorRegistry = ( }, gray: { name: 'gray', - gradient: { - normal: [theme.color.gray3, theme.color.gray5], - hover: [theme.color.gray7, theme.color.gray8], - }, solid: theme.color.gray8, variations: [ theme.color.gray1, @@ -227,10 +187,6 @@ export const createGraphColorRegistry = ( }, tomato: { name: 'tomato', - gradient: { - normal: [theme.color.tomato3, theme.color.tomato5], - hover: [theme.color.tomato7, theme.color.tomato8], - }, solid: theme.color.tomato8, variations: [ theme.color.tomato1, @@ -249,10 +205,6 @@ export const createGraphColorRegistry = ( }, ruby: { name: 'ruby', - gradient: { - normal: [theme.color.ruby3, theme.color.ruby5], - hover: [theme.color.ruby7, theme.color.ruby8], - }, solid: theme.color.ruby8, variations: [ theme.color.ruby1, @@ -271,10 +223,6 @@ export const createGraphColorRegistry = ( }, crimson: { name: 'crimson', - gradient: { - normal: [theme.color.crimson3, theme.color.crimson5], - hover: [theme.color.crimson7, theme.color.crimson8], - }, solid: theme.color.crimson8, variations: [ theme.color.crimson1, @@ -293,10 +241,6 @@ export const createGraphColorRegistry = ( }, plum: { name: 'plum', - gradient: { - normal: [theme.color.plum3, theme.color.plum5], - hover: [theme.color.plum7, theme.color.plum8], - }, solid: theme.color.plum8, variations: [ theme.color.plum1, @@ -315,10 +259,6 @@ export const createGraphColorRegistry = ( }, violet: { name: 'violet', - gradient: { - normal: [theme.color.violet3, theme.color.violet5], - hover: [theme.color.violet7, theme.color.violet8], - }, solid: theme.color.violet8, variations: [ theme.color.violet1, @@ -337,10 +277,6 @@ export const createGraphColorRegistry = ( }, iris: { name: 'iris', - gradient: { - normal: [theme.color.iris3, theme.color.iris5], - hover: [theme.color.iris7, theme.color.iris8], - }, solid: theme.color.iris8, variations: [ theme.color.iris1, @@ -359,10 +295,6 @@ export const createGraphColorRegistry = ( }, cyan: { name: 'cyan', - gradient: { - normal: [theme.color.cyan3, theme.color.cyan5], - hover: [theme.color.cyan7, theme.color.cyan8], - }, solid: theme.color.cyan8, variations: [ theme.color.cyan1, @@ -381,10 +313,6 @@ export const createGraphColorRegistry = ( }, jade: { name: 'jade', - gradient: { - normal: [theme.color.jade3, theme.color.jade5], - hover: [theme.color.jade7, theme.color.jade8], - }, solid: theme.color.jade8, variations: [ theme.color.jade1, @@ -403,10 +331,6 @@ export const createGraphColorRegistry = ( }, grass: { name: 'grass', - gradient: { - normal: [theme.color.grass3, theme.color.grass5], - hover: [theme.color.grass7, theme.color.grass8], - }, solid: theme.color.grass8, variations: [ theme.color.grass1, @@ -425,10 +349,6 @@ export const createGraphColorRegistry = ( }, mint: { name: 'mint', - gradient: { - normal: [theme.color.mint3, theme.color.mint5], - hover: [theme.color.mint7, theme.color.mint8], - }, solid: theme.color.mint8, variations: [ theme.color.mint1, @@ -447,10 +367,6 @@ export const createGraphColorRegistry = ( }, lime: { name: 'lime', - gradient: { - normal: [theme.color.lime3, theme.color.lime5], - hover: [theme.color.lime7, theme.color.lime8], - }, solid: theme.color.lime8, variations: [ theme.color.lime1, @@ -469,10 +385,6 @@ export const createGraphColorRegistry = ( }, bronze: { name: 'bronze', - gradient: { - normal: [theme.color.bronze3, theme.color.bronze5], - hover: [theme.color.bronze7, theme.color.bronze8], - }, solid: theme.color.bronze8, variations: [ theme.color.bronze1, @@ -491,10 +403,6 @@ export const createGraphColorRegistry = ( }, gold: { name: 'gold', - gradient: { - normal: [theme.color.gold3, theme.color.gold5], - hover: [theme.color.gold7, theme.color.gold8], - }, solid: theme.color.gold8, variations: [ theme.color.gold1, @@ -513,10 +421,6 @@ export const createGraphColorRegistry = ( }, brown: { name: 'brown', - gradient: { - normal: [theme.color.brown3, theme.color.brown5], - hover: [theme.color.brown7, theme.color.brown8], - }, solid: theme.color.brown8, variations: [ theme.color.brown1, @@ -535,10 +439,6 @@ export const createGraphColorRegistry = ( }, amber: { name: 'amber', - gradient: { - normal: [theme.color.amber3, theme.color.amber5], - hover: [theme.color.amber7, theme.color.amber8], - }, solid: theme.color.amber8, variations: [ theme.color.amber1,