Skip to content

Commit

Permalink
✨ feat: Add FunnelChart
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Nov 8, 2024
1 parent e581837 commit 5c482d5
Show file tree
Hide file tree
Showing 10 changed files with 810 additions and 11 deletions.
23 changes: 14 additions & 9 deletions src/DonutChart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { Skeleton } from 'antd';
import { css, useThemeMode } from 'antd-style';
import { CSSProperties, ComponentType, MouseEvent, forwardRef, useEffect, useState } from 'react';
import { Flexbox } from 'react-layout-kit';
Expand Down Expand Up @@ -32,6 +33,7 @@ export interface DonutChartProps extends BaseAnimationTimingProps {
donutLabel?: string;
index?: string;
label?: string;
loading?: boolean;
noDataText?: NoDataProps['noDataText'];
onValueChange?: (value: EventProps) => void;
showAnimation?: boolean;
Expand Down Expand Up @@ -62,6 +64,7 @@ const DonutChart = forwardRef<HTMLDivElement, DonutChartProps>((props, ref) => {
customTooltip,
className,
width = '100%',
loading,
height = '10rem',
style,
customCategories,
Expand All @@ -76,6 +79,17 @@ const DonutChart = forwardRef<HTMLDivElement, DonutChartProps>((props, ref) => {
const [activeIndex, setActiveIndex] = useState<number | undefined>();
const hasOnValueChange = !!onValueChange;

useEffect(() => {
const pieSectors = document.querySelectorAll('.recharts-pie-sector');
if (pieSectors) {
for (const sector of pieSectors) {
sector.setAttribute('style', 'outline: none');
}
}
}, [activeIndex]);

if (loading || !data) return <Skeleton.Button active block style={{ height, width }} />;

const onShapeClick = (data: any, index: number, event: MouseEvent) => {
event.stopPropagation();

Expand All @@ -92,15 +106,6 @@ const DonutChart = forwardRef<HTMLDivElement, DonutChartProps>((props, ref) => {
}
};

useEffect(() => {
const pieSectors = document.querySelectorAll('.recharts-pie-sector');
if (pieSectors) {
for (const sector of pieSectors) {
sector.setAttribute('style', 'outline: none');
}
}
}, [activeIndex]);

const parseData = (data: any[], colors: string[]) =>
data.map((dataPoint: any, idx: number) => {
const baseColor = idx < colors.length ? colors[idx] : theme.colorPrimary;
Expand Down
9 changes: 9 additions & 0 deletions src/FunnelChart/ArrowRightIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

const ArrowRightIcon = ({ ...props }) => (
<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M16.1716 10.9999L10.8076 5.63589L12.2218 4.22168L20 11.9999L12.2218 19.778L10.8076 18.3638L16.1716 12.9999H4V10.9999H16.1716Z"></path>
</svg>
);

export default ArrowRightIcon;
30 changes: 30 additions & 0 deletions src/FunnelChart/demos/CalculateFrom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FunnelChart, FunnelChartProps } from '@lobehub/charts';

const data: FunnelChartProps['data'] = [
{ name: 'Session start', value: 31_943 },
{ name: 'Account Created', value: 10_474 },
{
name: 'Item Detail Page',
value: 9482,
},
{
name: 'Added to cart',
value: 4684,
},
{
name: 'Complete Purchase',
value: 1283,
},
];

export default () => {
return (
<FunnelChart
calculateFrom={'previous'}
data={data}
evolutionGradient={false}
gradient={true}
showArrow={false}
/>
);
};
47 changes: 47 additions & 0 deletions src/FunnelChart/demos/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { FunnelChart, FunnelChartProps } from '@lobehub/charts';
import { StoryBook, useControls, useCreateStore } from '@lobehub/ui';

const data: FunnelChartProps['data'] = [
{ name: '1. Add credit Card', value: 89 },
{ name: '2. Copy invite code', value: 6 },
{
name: '3. Send invite code',
value: 5,
},
];

export default () => {
const store = useCreateStore();

const props: FunnelChartProps | any = useControls(
{
barGap: '20%',
enableLegendSlider: false,
evolutionGradient: true,
gradient: false,
showArrow: true,
showGridLines: true,
showLegend: true,
showTooltip: true,
showXAxis: true,
showYAxis: true,
variant: {
options: ['base', 'center'],
value: 'base',
},
xAxisLabel: '',
yAxisAlign: {
options: ['left', 'right'],
value: 'left',
},
yAxisLabel: '',
},
{ store },
);

return (
<StoryBook levaStore={store}>
<FunnelChart data={data} {...props} />
</StoryBook>
);
};
17 changes: 17 additions & 0 deletions src/FunnelChart/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
nav: Components
group: Charts
description: A funnel chart illustrates the progression or conversion rates through successive stages of a process.
---

<code src="./demos/index.tsx" nopadding></code>

## Usage example with evolution gradient

The `calculateFrom` prop controls the referenced value to calculate the drop-off between steps. Here we set it to previous, to always reference the preceding bar as the calculation base. We also set `showArrow` to false.

<code src="./demos/CalculateFrom.tsx"></code>

## API

<API></API>
Loading

0 comments on commit 5c482d5

Please sign in to comment.