Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linechart): add line chart #2317

Open
wants to merge 1 commit into
base: patch
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions example/storybook-nativewind/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@react-native-community/slider": "4.2.4",
"@react-stately/collections": "^3.6.0",
"@react-stately/tree": "^3.5.0",
"@shopify/react-native-skia": "^1.3.8",
"expo": "^47.0.0",
"expo-linear-gradient": "^12.3.0",
"expo-status-bar": "~1.4.2",
Expand All @@ -64,7 +65,7 @@
"react-dom": "^18.2.0",
"react-native": "0.72.4",
"react-native-gesture-handler": "~2.14.0",
"react-native-reanimated": "~3.6.2",
"react-native-reanimated": "^3.14.0",
"react-native-safe-area-context": "^4.4.1",
"react-native-svg": "13.4.0",
"react-native-vector-icons": "^10.0.0",
Expand All @@ -74,7 +75,8 @@
"tailwind-variants": "^0.1.20",
"tailwindcss": "^3.4.1",
"ts-jest": "^29.1.0",
"uuidv4": "^6.2.13"
"uuidv4": "^6.2.13",
"victory-native": "^41.0.1"
},
"devDependencies": {
"@babel/core": "^7.19.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ComponentMeta } from '@storybook/react-native';
import LineChart from './LineChart';

const AccordionMeta: ComponentMeta<typeof LineChart> = {
title: 'stories/Line Chart',
component: LineChart,
// metaInfo is required for figma generation
// @ts-ignore
metaInfo: {
componentDescription: `The Line Chart component displays data compatable with a two-dimensional cartesian plane`,
},
argTypes: {},
};

export default AccordionMeta;

export { LineChart };
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState } from 'react';

import { LineChart } from '@/components/ui/line-chart';

import { Button, View, StyleSheet, useColorScheme } from 'react-native';

const DATA = Array.from({ length: 31 }, (_, i) => ({
x: i,
y: 40 + 30 * Math.random(),
}));
const DATA2 = Array.from({ length: 31 }, (_, i) => ({
x: i,
y: 40 + 30 * Math.random(),
}));

const LineChartBasic = () => {
const [data, setData] = useState(DATA);
const colorMode = useColorScheme();

const labelColor = colorMode === 'dark' ? 'white' : 'black';
const lineColor = colorMode === 'dark' ? 'lightgrey' : 'black';

return (
<View style={style.container}>
<LineChart
width={300}
height={300}
data={data}
outlineColor="lightgreen"
gradientColors={['green', '#90ee9050']}
labelColor={labelColor}
lineColor={lineColor}
topLabelPrefix="$"
/>
<Button
title="Change to Data 1"
onPress={() => {
setData(DATA);
}}
/>
<Button
title="Change to Data 2"
onPress={() => {
setData(DATA2);
}}
/>
</View>
);
};

LineChartBasic.description =
'This is a basic Line Chart example. The Line Chart is a component that lets you display data on a two dimensional cartesian plane';

export default LineChartBasic;

const style = StyleSheet.create({
container: {
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';

import {
Circle,
LinearGradient,
matchFont,
vec,
} from '@shopify/react-native-skia';
import { Platform } from 'react-native';
import { useDerivedValue, type SharedValue } from 'react-native-reanimated';
import { Area, CartesianChart, Line, useChartPressState } from 'victory-native';

import { Text as SKText } from '@shopify/react-native-skia';
import { Box } from '@gluestack-ui/themed';

const fontFamily = Platform.select({ ios: 'Helvetica', default: 'sans-serif' });

const legendFontStyle = {
fontFamily,
fontSize: 14,
fontWeight: 'bold',
};

const indicatorFontStyle = {
fontFamily,
fontSize: 25,
fontWeight: 'bold',
};

// @ts-ignore
const legendFont = matchFont(legendFontStyle);
// @ts-ignore
const indicatorFont = matchFont(indicatorFontStyle);

export interface ChartData {
[x: string]: number;
}

interface Props {
height: number;
width: number;
data: ChartData[];
outlineColor: string;
gradientColors: string[];
labelColor: string;
lineColor: string;
topLabelPrefix?: string;
topLabelSuffix?: string;
}

export const LineChart = ({
height,
width,
data,
outlineColor,
gradientColors,
labelColor,
lineColor,
topLabelPrefix = '',
topLabelSuffix = '',
}: Props) => {
const { state, isActive } = useChartPressState({ x: 0, y: { y: 0 } });

const value = useDerivedValue(() => {
return topLabelPrefix + state.y.y.value.value.toFixed(2) + topLabelSuffix;
}, [state]);

return (
<Box height={height} width={width}>
<CartesianChart
data={data}
xKey="x"
yKeys={['y']}
domainPadding={{ top: 30 }}
axisOptions={{
font: legendFont,
labelColor,
lineColor,
}}
chartPressState={state}
>
{({ points, chartBounds }) => (
<>
<SKText
x={chartBounds.left + 10}
y={chartBounds.top + indicatorFont.measureText('0').height + 5}
font={indicatorFont}
text={value}
color={labelColor}
style={'fill'}
/>
<Line
points={points.y}
color={outlineColor}
strokeWidth={3}
animate={{ type: 'timing', duration: 500 }}
/>
<Area
points={points.y}
y0={chartBounds.bottom}
animate={{ type: 'timing', duration: 500 }}
>
<LinearGradient
start={vec(chartBounds.bottom, 200)}
end={vec(chartBounds.bottom, chartBounds.bottom)}
colors={gradientColors}
/>
</Area>

{isActive ? (
<ToolTip x={state.x.position} y={state.y.y.position} />
) : null}
</>
)}
</CartesianChart>
</Box>
);
};

function ToolTip({ x, y }: { x: SharedValue<number>; y: SharedValue<number> }) {
return <Circle cx={x} cy={y} r={8} color={'grey'} opacity={0.8} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

import { View } from 'react-native';
import { ChartData } from '.';

interface Props {
height: number;
width: number;
data: ChartData[];
outlineColor: string;
gradientColors: string[];
labelColor: string;
lineColor: string;
topLabelPrefix?: string;
topLabelSuffix?: string;
}

export const LineChart = (_: Props) => {
return <View />;
};
6 changes: 4 additions & 2 deletions example/storybook-v7/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-community/datetimepicker": "8.0.1",
"@react-native-community/slider": "4.5.2",
"@shopify/react-native-skia": "1.2.3",
"autoprefixer": "^10.4.19",
"eas-cli": "^9.0.7",
"expo": "^51.0.8",
Expand All @@ -40,12 +41,13 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.1",
"react-native-gesture-handler": "~2.14.0",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.1",
"react-native-svg": "15.2.0",
"react-native-web": "~0.19.10",
"tailwind-variants": "^0.2.1"
"tailwind-variants": "^0.2.1",
"victory-native": "^41.0.1"
},
"devDependencies": {
"@babel/core": "^7.19.3",
Expand Down
Loading
Loading