Skip to content

Commit

Permalink
feat(dataviz): add Chart components
Browse files Browse the repository at this point in the history
  • Loading branch information
WilliamKelley committed Jun 18, 2024
1 parent d6f5b60 commit 57432a5
Show file tree
Hide file tree
Showing 18 changed files with 630 additions and 0 deletions.
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@types/react-dom": "18.3.0",
"@typescript-eslint/eslint-plugin": "7.8.0",
"@typescript-eslint/parser": "7.8.0",
"@visx/responsive": "^3.10.2",
"@vitejs/plugin-react": "4.2.1",
"@vitest/ui": "1.3.1",
"babel-jest": "^29.4.1",
Expand Down
1 change: 1 addition & 0 deletions packages/dataviz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@types/d3-scale": "^4.0.8",
"@types/d3-shape": "^3.1.6",
"@visx/responsive": "^3.10.2",
"csstype": "^3.1.3",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
Expand Down
75 changes: 75 additions & 0 deletions packages/dataviz/src/Chart/Chart.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { RadialBar } from '../RadialBar';
import { RadialBarCircle } from '../RadialBarCircle';
import { RadialBarScale } from '../RadialBarScale';
import { RadialBarSweep } from '../RadialBarSweep';
import { Chart } from './Chart';

const meta: Meta<typeof Chart> = {
title: 'Chart',
component: Chart,
};

export default meta;

type Story = StoryObj<typeof Chart>;

export const FullCircles: Story = {
args: {
width: 252 + 8 + 8,
height: 252 + 8 + 8,
marginBottom: 8,
marginLeft: 8,
marginRight: 8,
marginTop: 8,
children: (
<g transform="translate(126,126)">
<RadialBarScale
angleMin={0}
angleMax={2 * Math.PI}
valueMin={0}
valueMax={120}
>
<RadialBar radius={126} ratio={0.77} cornerRadius="50%">
<RadialBarSweep to={120} fill="lightgrey" />
<RadialBarSweep to={105} />
</RadialBar>
<RadialBar radius={93} ratio={0.87} cornerRadius="50%">
<RadialBarSweep to={103} fill="grey" />
</RadialBar>
</RadialBarScale>
</g>
),
},
};

export const HalfCircles: Story = {
args: {
width: 544 + 8 + 8,
height: 162 + 8 + 8,
marginBottom: 8,
marginLeft: 8,
marginRight: 8,
marginTop: 8,
children: (
<g transform="translate(126,126)">
<RadialBarScale
angleMin={(-1 * Math.PI) / 2}
angleMax={Math.PI / 2}
valueMin={1}
valueMax={3}
>
<RadialBar radius={126} ratio={0.77} cornerRadius="50%">
<RadialBarSweep to={3} fill="lightgrey" />
<RadialBarCircle at={2} />
</RadialBar>
<RadialBar radius={93} ratio={0.87} cornerRadius="50%">
<RadialBarSweep to={3} fill="lightgrey" />
<RadialBarCircle at={3} />
</RadialBar>
</RadialBarScale>
</g>
),
},
};
16 changes: 16 additions & 0 deletions packages/dataviz/src/Chart/Chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { ChartElement, ChartProps } from './ChartProps';
import { useChart } from './useChart';

export const Chart = React.forwardRef<ChartElement, ChartProps>(function Chart(
props,
ref
) {
const { svgProps, gProps } = useChart(props);

return (
<svg ref={ref} {...svgProps}>
<g {...gProps} />
</svg>
);
});
8 changes: 8 additions & 0 deletions packages/dataviz/src/Chart/ChartProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import { ChartParams } from './useChart';

export interface ChartElement extends SVGSVGElement {}

export interface ChartProps
extends ChartParams,
Omit<React.SVGProps<ChartElement>, 'width' | 'height'> {}
3 changes: 3 additions & 0 deletions packages/dataviz/src/Chart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './Chart';
export * from './ChartProps';
export * from './useChart';
84 changes: 84 additions & 0 deletions packages/dataviz/src/Chart/useChart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { SVGProps } from 'react';
import { ChartProps } from './ChartProps';
import {
evalMargin,
makeDimensions,
toStandardViewBox,
toViewBoxAttr,
} from '../utils';

export type ChartParams = {
/**
* The width of the container.
*/
width: number;
/**
* The height of the container.
*/
height: number;
/**
* The space between the bottom edge of the container and its surroundings.
*/
marginBottom?: number;
/**
* The space between the left edge of the container and its surroundings.
*/
marginLeft?: number;
/**
* The space between the right edge of the container and its surroundings.
*/
marginRight?: number;
/**
* The space between the top edge of the container and its surroundings.
*/
marginTop?: number;
};

export interface ChartAPI {
svgProps: SVGProps<SVGSVGElement>;
gProps: SVGProps<SVGGElement>;
}

export function useChart(props: ChartProps): ChartAPI {
const {
width,
height,
marginBottom,
marginLeft,
marginRight,
marginTop,
children,
...other
} = props;

const margin = evalMargin({
bottom: marginBottom,
left: marginLeft,
right: marginRight,
top: marginTop,
});

const dimensions = makeDimensions({
width,
height,
margin,
});

const viewBox = toStandardViewBox({ dimensions });

const transform = `translate(${dimensions.margin.left}, ${dimensions.margin.top})`;

return {
svgProps: {
viewBox: toViewBoxAttr({ viewBox }),
width: dimensions.width,
height: dimensions.height,
...other,
style: { maxWidth: '100%', height: 'auto', ...other?.style },
},
gProps: {
transform,
children,
},
};
}
75 changes: 75 additions & 0 deletions packages/dataviz/src/ResponsiveChart/ResponsiveChart.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { RadialBar } from '../RadialBar';
import { RadialBarCircle } from '../RadialBarCircle';
import { RadialBarScale } from '../RadialBarScale';
import { RadialBarSweep } from '../RadialBarSweep';
import { ResponsiveChart } from './ResponsiveChart';

const meta: Meta<typeof ResponsiveChart> = {
title: 'ResponsiveChart',
component: ResponsiveChart,
};

export default meta;

type Story = StoryObj<typeof ResponsiveChart>;

export const FullCircles: Story = {
args: {
innerWidth: 252,
height: 252 + 8 + 8,
marginBottom: 8,
marginLeft: 8,
marginRight: 8,
marginTop: 8,
children: (
<g transform="translate(126,126)">
<RadialBarScale
angleMin={0}
angleMax={2 * Math.PI}
valueMin={0}
valueMax={120}
>
<RadialBar radius={126} ratio={0.77} cornerRadius="50%">
<RadialBarSweep to={120} fill="lightgrey" />
<RadialBarSweep to={105} />
</RadialBar>
<RadialBar radius={93} ratio={0.87} cornerRadius="50%">
<RadialBarSweep to={103} fill="grey" />
</RadialBar>
</RadialBarScale>
</g>
),
},
};

export const HalfCircles: Story = {
args: {
innerWidth: 544,
height: 162 + 8 + 8,
marginBottom: 8,
marginLeft: 8,
marginRight: 8,
marginTop: 8,
children: (
<g transform="translate(126,126)">
<RadialBarScale
angleMin={(-1 * Math.PI) / 2}
angleMax={Math.PI / 2}
valueMin={1}
valueMax={3}
>
<RadialBar radius={126} ratio={0.77} cornerRadius="50%">
<RadialBarSweep to={3} fill="lightgrey" />
<RadialBarCircle at={2} />
</RadialBar>
<RadialBar radius={93} ratio={0.87} cornerRadius="50%">
<RadialBarSweep to={3} fill="lightgrey" />
<RadialBarCircle at={3} />
</RadialBar>
</RadialBarScale>
</g>
),
},
};
47 changes: 47 additions & 0 deletions packages/dataviz/src/ResponsiveChart/ResponsiveChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import {
ResponsiveChartElement,
ResponsiveChartProps,
} from './ResponsiveChartProps';
import { useResponsiveChart } from './useResponsiveChart';

export const ResponsiveChart = React.forwardRef<
ResponsiveChartElement,
ResponsiveChartProps
>(function ResponsiveChart(props, ref) {
const {
width,
height,
innerWidth,
innerHeight,
marginBottom,
marginLeft,
marginRight,
marginTop,
children,
...other
} = props;

const {
container: { props: containerProps, ref: containerRef },
svg: { props: svgProps },
inner: { props: innerProps },
} = useResponsiveChart({
width,
height,
innerWidth,
innerHeight,
marginBottom,
marginLeft,
marginRight,
marginTop,
});

return (
<div ref={containerRef} {...containerProps}>
<svg ref={ref} {...svgProps} {...other}>
<g {...innerProps}>{children}</g>
</svg>
</div>
);
});
7 changes: 7 additions & 0 deletions packages/dataviz/src/ResponsiveChart/ResponsiveChartProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ResponsiveChartParams } from './useResponsiveChart';

export interface ResponsiveChartElement extends SVGSVGElement {}

export interface ResponsiveChartProps
extends ResponsiveChartParams,
Omit<React.SVGProps<ResponsiveChartElement>, 'width' | 'height'> {}
3 changes: 3 additions & 0 deletions packages/dataviz/src/ResponsiveChart/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './ResponsiveChart';
export * from './ResponsiveChartProps';
export * from './useResponsiveChart';
Loading

0 comments on commit 57432a5

Please sign in to comment.