Skip to content

Commit

Permalink
Merge pull request #172 from lad-tech/feat/169-custom-color-and-multi…
Browse files Browse the repository at this point in the history
…ple-lines

feat(chart): custom colors and multiply lines #169
  • Loading branch information
Bibazavr authored Mar 28, 2024
2 parents fc49056 + 2b18508 commit d79acb1
Show file tree
Hide file tree
Showing 20 changed files with 398 additions and 131 deletions.
8 changes: 4 additions & 4 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ PODS:
- glog
- react-native-safe-area-context (4.8.2):
- React-Core
- react-native-skia (0.1.236):
- react-native-skia (1.0.4):
- RCT-Folly (= 2021.07.22.00)
- React
- React-callinvoker
Expand Down Expand Up @@ -425,7 +425,7 @@ PODS:
- RNGestureHandler (2.14.1):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- RNReanimated (3.6.2):
- RNReanimated (3.8.1):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- ReactCommon/turbomodule/core
Expand Down Expand Up @@ -644,7 +644,7 @@ SPEC CHECKSUMS:
React-jsinspector: d5ce2ef3eb8fd30c28389d0bc577918c70821bd6
React-logger: 9332c3e7b4ef007a0211c0a9868253aac3e1da82
react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89
react-native-skia: de2c58edde106819e7497fff61a2dfb69744a86f
react-native-skia: 69482406004911464f57cfb901348ddf3c4032bb
React-perflogger: 43392072a5b867a504e2b4857606f8fc5a403d7f
React-RCTActionSheet: c7b67c125bebeda9fb19fc7b200d85cb9d6899c4
React-RCTAnimation: c2de79906f607986633a7114bee44854e4c7e2f5
Expand All @@ -660,7 +660,7 @@ SPEC CHECKSUMS:
ReactCommon: 0c43eaeaaee231d7d8dc24fc5a6e4cf2b75bf196
RNCAsyncStorage: 618d03a5f52fbccb3d7010076bc54712844c18ef
RNGestureHandler: fe2be3be5598dc74329b211c58c9f2d231461769
RNReanimated: 738543ef6ec0024ea0bc9f4ab3ac99af6f068448
RNReanimated: 4943d1e286130877c0539f053c3f36a22d8c66f5
RNScreens: 3c5b9f4a9dcde752466854b6109b79c0e205dad3
RNSVG: 3a79c0c4992213e4f06c08e62730c5e7b9e4dc17
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
"@react-navigation/bottom-tabs": "^6.5.18",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@shopify/react-native-skia": "^0.1.234",
"@shopify/react-native-skia": "^1.0.4",
"react": "18.2.0",
"react-native": "0.71.6",
"react-native-calendars": "^1.1294.0",
"react-native-gesture-handler": "^2.14.1",
"react-native-reanimated": "^3.6.1",
"react-native-reanimated": "^3.8.1",
"react-native-safe-area-context": "^4.8.2",
"react-native-screens": "^3.29.0",
"react-native-svg": "13.6.0",
Expand Down Expand Up @@ -96,4 +96,4 @@
"typescript": "^4.9.5",
"winston": "^3.8.1"
}
}
}
38 changes: 29 additions & 9 deletions packages/chart/src/BarChart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {
Canvas,
Group,
Skia,
Text,
useCanvasRef,
useFont,
} from '@shopify/react-native-skia';
import {useSafeAreaFrame} from 'react-native-safe-area-context';
import {useDerivedValue, useSharedValue} from 'react-native-reanimated';
import {
interpolateColor,
useDerivedValue,
useSharedValue,
} from 'react-native-reanimated';
import {useTheme, View} from '@lad-tech/mobydick-core';
import {StyleProp, ViewStyle} from 'react-native';

Expand All @@ -17,9 +22,9 @@ import {
defaultChartHeightDivider,
} from '../utils/constants';
import Coordinates from '../components/Coordinates';
import Line from '../components/Line';
import {generatePeriodsWithBarPaths} from '../utils/generatePeriodsWithBarPaths';
import Section from '../components/Section';
import Line from '../components/Line';

export interface IBarChartProps {
title?: string;
Expand Down Expand Up @@ -74,13 +79,28 @@ export const BarChart = ({

const chartPath = useDerivedValue(() => {
const {current, next} = state.value;
const start = periodsWithPaths[current];
const end = periodsWithPaths[next];
const start = periodsWithPaths[current]?.chartPath ?? Skia.Path.Make();
const end = periodsWithPaths[next]?.chartPath ?? Skia.Path.Make();

if (start === undefined || end === undefined) {
throw Error('start === undefined || end === undefined');
if (end.isInterpolatable(start)) {
return end.interpolate(start, transition.value)!;
}
return end.chartPath.interpolate(start.chartPath, transition.value)!;

return end;
});

const colorsBar = useDerivedValue(() => {
const {current, next} = state.value;
const start = periodsWithPaths[current]?.colors ?? [];
const end = periodsWithPaths[next]?.colors ?? [];

return end.map((endColor, i) =>
interpolateColor(
transition.value,
[0, 1],
[start[i] ?? endColor, endColor],
),
);
});

const maxY = useDerivedValue(() => {
Expand Down Expand Up @@ -159,9 +179,9 @@ export const BarChart = ({
/>
)}
<Line
path={chartPath}
chartPath={chartPath}
width={width}
colors={colors}
colors={colorsBar}
strokeWidth={20}
/>
<Coordinates
Expand Down
26 changes: 10 additions & 16 deletions packages/chart/src/LineChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import {StyleProp, View, ViewStyle} from 'react-native';
import {useTheme} from '@lad-tech/mobydick-core';

import Coordinates from '../components/Coordinates';
import Line from '../components/Line';
import {
chartPaddingHorizontal,
chartPaddingVertical,
defaultChartHeightDivider,
} from '../utils/constants';
import {IDataset, IFormatter, IGraphState, IRenderSectionItem} from '../types';
import {IChartState, IDataset, IFormatter, IRenderSectionItem} from '../types';
import Section from '../components/Section';
import {generatePeriodsWithLinePaths} from '../utils/generatePeriodsWithLinePaths';
import {Lines} from '../components/Lines';

export interface ILineChartProps {
title?: string;
Expand Down Expand Up @@ -67,22 +67,11 @@ export const LineChart = ({
// animation value to transition from one graph to the next
const transition = useSharedValue(0);
// indices of the current and next graphs
const state = useSharedValue<IGraphState>({
const state = useSharedValue<IChartState>({
next: 0,
current: 0,
});

const chartPath = useDerivedValue(() => {
const {current, next} = state.value;
const start = periodsWithPaths[current];
const end = periodsWithPaths[next];

if (start === undefined || end === undefined) {
throw Error('start === undefined || end === undefined');
}
return end.chartPath.interpolate(start.chartPath, transition.value)!;
});

const maxY = useDerivedValue(() => {
const {current, next} = state.value;
const start = periodsWithPaths[current];
Expand Down Expand Up @@ -136,7 +125,7 @@ export const LineChart = ({
throw Error('start === undefined || end === undefined');
}

return end.coordinatesLength;
return end.maxCoordinatesLength;
});

return (
Expand All @@ -158,7 +147,12 @@ export const LineChart = ({
font={font}
/>
)}
<Line path={chartPath} width={width} colors={colors} />
<Lines
periodsWithPaths={periodsWithPaths}
width={width}
state={state}
transition={transition}
/>
<Coordinates
font={font}
colors={colors}
Expand Down
43 changes: 22 additions & 21 deletions packages/chart/src/components/Line.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
import type {PathProps, SkiaDefaultProps} from '@shopify/react-native-skia';
import {LinearGradient, Path, SkPath, vec} from '@shopify/react-native-skia';
import {
Color,
LinearGradient,
Path,
PathProps,
SkiaDefaultProps,
SkPath,
vec,
} from '@shopify/react-native-skia';
import {SharedValue} from 'react-native-reanimated';
import {defaultTheme} from '@lad-tech/mobydick-core';

interface IChartProps extends SkiaDefaultProps<PathProps, 'start' | 'end'> {
colors: (typeof defaultTheme.colors)[0];
path: Readonly<SharedValue<SkPath>>;
import {COLORS} from '../utils';

interface IChartProps
extends Omit<SkiaDefaultProps<PathProps, 'start' | 'end'>, 'path'> {
chartPath: SharedValue<SkPath>;
width: number;
colors?: SharedValue<Color[]>;
}

export const COLORS = [
'#E0F5E9',
'#9BE1DA',
'#56CDCB',
'#3B8B8E',
'#EF1E1C',
'#F43B1D',
'#F9571D',
'#FF8A57',
];

export const Line = ({path, width, colors, ...rest}: IChartProps) => {
export const Line = ({chartPath, width, colors, ...rest}: IChartProps) => {
return (
<Path
path={path}
path={chartPath}
style="stroke"
strokeJoin="round"
strokeWidth={2}
color={colors.ChartFirst}
{...rest}>
<LinearGradient start={vec(0, 0)} end={vec(width, 0)} colors={COLORS} />
<LinearGradient
start={vec(0, 0)}
end={vec(width, 0)}
colors={colors ?? COLORS}
/>
</Path>
);
};
Expand Down
57 changes: 57 additions & 0 deletions packages/chart/src/components/LineOfPeriod.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {PathProps, Skia, SkiaDefaultProps} from '@shopify/react-native-skia';
import {interpolateColor, useDerivedValue} from 'react-native-reanimated';

import {IChartTransition, ISharedChartState} from '../types';
import {IPeriodsWithPaths} from '../utils';

import Line from './Line';

interface IChartProps
extends Omit<SkiaDefaultProps<PathProps, 'start' | 'end'>, 'path'> {
periodsWithPaths: IPeriodsWithPaths;
index: number;
width: number;
transition: IChartTransition;
state: ISharedChartState;
lineColors?: string[] | undefined;
}

export const LineOfPeriod = ({
periodsWithPaths,
index,
width,
transition,
state,
}: IChartProps) => {
const chartPath = useDerivedValue(() => {
const {current, next} = state.value;

const start =
periodsWithPaths[current]?.lines[index]?.path ?? Skia.Path.Make();
const end = periodsWithPaths[next]?.lines[index]?.path ?? Skia.Path.Make();

if (end.isInterpolatable(start)) {
return end.interpolate(start, transition.value)!;
}

return end;
});

const colors = useDerivedValue(() => {
const {current, next} = state.value;
const start = periodsWithPaths[current]?.lines[index]?.colors ?? [];
const end = periodsWithPaths[next]?.lines[index]?.colors ?? [];

return end.map((endColor, i) =>
interpolateColor(
transition.value,
[0, 1],
[start[i] ?? endColor, endColor],
),
);
});

return <Line colors={colors} width={width} chartPath={chartPath} />;
};

export default LineOfPeriod;
51 changes: 51 additions & 0 deletions packages/chart/src/components/Lines.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {SkPath} from '@shopify/react-native-skia';
import {useDerivedValue} from 'react-native-reanimated';

import {IPeriodsWithPaths} from '../utils';
import {IChartTransition, ISharedChartState} from '../types';

import LineOfPeriod from './LineOfPeriod';

export interface ILine {
name?: string | undefined;
path: SkPath;
colors?: string[] | undefined;
}

interface ILinesProps {
periodsWithPaths: IPeriodsWithPaths;
width: number;
transition: IChartTransition;
state: ISharedChartState;
}

export const Lines = ({
periodsWithPaths,
width,
state,
transition,
}: ILinesProps) => {
const lines = useDerivedValue(() => {
const {next} = state.value;

const end = periodsWithPaths[next]?.lines ?? [];

return end;
});

return (
<>
{lines.value.map(({colors}, index) => (
<LineOfPeriod
key={index}
index={index}
periodsWithPaths={periodsWithPaths}
width={width}
lineColors={colors}
state={state}
transition={transition}
/>
))}
</>
);
};
4 changes: 2 additions & 2 deletions packages/chart/src/components/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import {StyleProp, View, ViewStyle} from 'react-native';
import {SharedValue} from 'react-native-reanimated';
import {createStyles, useStyles} from '@lad-tech/mobydick-core';

import {IDataset, IGraphState, IRenderSectionItem} from '../types';
import {IChartState, IDataset, IRenderSectionItem} from '../types';

import SectionButton from './SectionButton';

export interface ISelectionProps {
state: SharedValue<IGraphState>;
state: SharedValue<IChartState>;
transition: SharedValue<number>;
dataset: IDataset;
renderSectionItem: IRenderSectionItem;
Expand Down
4 changes: 2 additions & 2 deletions packages/chart/src/components/SectionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {createStyles, Pressable, useStyles} from '@lad-tech/mobydick-core';
import {
IChartTransition,
IRenderSectionItem,
ISharedGraphState,
ISharedChartState,
} from '../types';

export interface ISectionButtonProps {
renderSectionItem: IRenderSectionItem;
period: string;
index: number;
state: ISharedGraphState;
state: ISharedChartState;
transition: IChartTransition;
}

Expand Down
Loading

0 comments on commit d79acb1

Please sign in to comment.