Skip to content

Commit

Permalink
Add dashPathEffect prop to x/y axis (#372)
Browse files Browse the repository at this point in the history
  • Loading branch information
zibs authored Oct 2, 2024
1 parent d9cc411 commit 69d530c
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .changeset/honest-kiwis-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"victory-native": minor
---

Add dashed path effect option for X and Y axes
10 changes: 6 additions & 4 deletions example/app/bar-chart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
DashPathEffect,
LinearGradient,
useFont,
vec,
Expand Down Expand Up @@ -56,17 +57,18 @@ export default function BarChartPage(props: { segment: string }) {
const date = new Date(2023, value - 1);
return date.toLocaleString("default", { month: "short" });
},
linePathEffect: <DashPathEffect intervals={[4, 4]} />,
}}
frame={{
lineWidth: 0,
}}
yAxis={[
{
yKeys: ["listenCount"],
font,
lineWidth: 0,
linePathEffect: <DashPathEffect intervals={[4, 4]} />,
},
]}
frame={{
lineWidth: 0,
}}
data={data}
>
{({ points, chartBounds }) => {
Expand Down
5 changes: 5 additions & 0 deletions example/app/consts/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ export const ChartRoutes: {
"This is mixture of Pie and Donut charts, showing off the different ways to customize the charts.",
path: "/pie-and-donut-charts",
},
{
title: "Dashed Axes",
description: "This is an Area chart with dashed X and Y axes.",
path: "/dashed-axes",
},
];

if (__DEV__) {
Expand Down
134 changes: 134 additions & 0 deletions example/app/dashed-axes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {
DashPathEffect,
LinearGradient,
useFont,
vec,
} from "@shopify/react-native-skia";
import * as React from "react";
import { SafeAreaView, ScrollView, StyleSheet, View } from "react-native";
import { Area, CartesianChart, Line } from "victory-native";
import inter from "../assets/inter-medium.ttf";
import { appColors } from "./consts/colors";
import { InfoCard } from "../components/InfoCard";
import { descriptionForRoute } from "./consts/routes";
import { Button } from "../components/Button";

const generateData = () =>
Array.from({ length: 12 }, (_, index) => {
const low = Math.round(20 + 20 * Math.random());
const high = Math.round(low + 3 + 20 * Math.random());

return {
month: new Date(2020, index).toLocaleString("default", {
month: "short",
}),
low,
high,
};
});

export default function DashedAxesPage(props: { segment: string }) {
const description = descriptionForRoute(props.segment);
const font = useFont(inter, 12);
const [data, setData] = React.useState(generateData);
const [, setW] = React.useState(0);
const [, setH] = React.useState(0);

return (
<>
<SafeAreaView style={styles.safeView}>
<View style={styles.chart}>
<CartesianChart
data={data}
xKey="month"
yKeys={["low", "high"]}
padding={16}
domain={{ y: [0, 65] }}
domainPadding={{ top: 20 }}
xAxis={{
font,
labelOffset: 4,
linePathEffect: <DashPathEffect intervals={[4, 4]} />,
}}
yAxis={[
{
labelOffset: 8,
yKeys: ["high"],
font,

linePathEffect: <DashPathEffect intervals={[4, 4]} />,
},
]}
onChartBoundsChange={({ left, right, top, bottom }) => {
setW(right - left);
setH(bottom - top);
}}
>
{({ points, chartBounds }) => (
<>
<Area
points={points.high}
y0={chartBounds.bottom}
color="black"
opacity={0.5}
curveType="natural"
animate={{ type: "timing" }}
>
<LinearGradient
start={vec(0, 50)}
end={vec(0, 200)}
colors={["#f7ce64", "#f7ce6420"]}
/>
</Area>
<Line
strokeWidth={3}
color={"#f7ce64"}
curveType="natural"
points={points.high}
/>
</>
)}
</CartesianChart>
</View>
<ScrollView
style={styles.optionsScrollView}
contentContainerStyle={styles.options}
>
<InfoCard style={{ marginBottom: 16 }}>{description}</InfoCard>
<Button
title="Shuffle Data"
onPress={() => setData(generateData())}
style={{ width: "100%" }}
/>
</ScrollView>
</SafeAreaView>
</>
);
}

const styles = StyleSheet.create({
safeView: {
flex: 1,
backgroundColor: appColors.viewBackground.light,
$dark: {
backgroundColor: appColors.viewBackground.dark,
},
},
chart: {
flex: 1,
maxHeight: 350,
},
optionsScrollView: {
flex: 1,
backgroundColor: appColors.cardBackground.light,
$dark: {
backgroundColor: appColors.cardBackground.dark,
},
},
options: {
paddingHorizontal: 20,
paddingVertical: 15,
alignItems: "flex-start",
justifyContent: "flex-start",
},
});
12 changes: 10 additions & 2 deletions lib/src/cartesian/components/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { StyleSheet } from "react-native";
import { Path, Skia } from "@shopify/react-native-skia";
import type { FrameProps } from "../../types";

export const Frame = ({ xScale, yScale, lineColor, lineWidth }: FrameProps) => {
export const Frame = ({
xScale,
yScale,
lineColor,
lineWidth,
linePathEffect,
}: FrameProps) => {
const [x1 = 0, x2 = 0] = xScale.domain();
const [y1 = 0, y2 = 0] = yScale.domain();

Expand Down Expand Up @@ -31,7 +37,9 @@ export const Frame = ({ xScale, yScale, lineColor, lineWidth }: FrameProps) => {
strokeWidth={lineWidth}
style="stroke"
color={lineColor}
/>
>
{linePathEffect ? linePathEffect : null}
</Path>
);
};

Expand Down
6 changes: 5 additions & 1 deletion lib/src/cartesian/components/XAxis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const XAxis = <
formatXLabel = (label: ValueOf<InputDatum>) => String(label),
ix = [],
isNumericalData,
linePathEffect,
}: XAxisProps<RawData, XK>) => {
const [y1 = 0, y2 = 0] = yScale.domain();
const [x1r = 0, x2r = 0] = xScale.range();
Expand Down Expand Up @@ -74,7 +75,9 @@ export const XAxis = <
p2={vec(xScale(tick), yScale(y1))}
color={lineColor}
strokeWidth={lineWidth}
/>
>
{linePathEffect ? linePathEffect : null}
</Line>
) : null}
{font && labelWidth && canFitLabelContent ? (
<Text
Expand All @@ -85,6 +88,7 @@ export const XAxis = <
x={labelX}
/>
) : null}
<></>
</React.Fragment>
);
});
Expand Down
5 changes: 4 additions & 1 deletion lib/src/cartesian/components/YAxis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const YAxis = <
lineColor,
font,
formatYLabel = (label: ValueOf<InputDatum>) => String(label),
linePathEffect,
}: YAxisProps<RawData, YK>) => {
const [x1 = 0, x2 = 0] = xScale.domain();
const [_ = 0, y2 = 0] = yScale.domain();
Expand Down Expand Up @@ -62,7 +63,9 @@ export const YAxis = <
p2={vec(xScale(x2), yScale(tick))}
color={lineColor}
strokeWidth={lineWidth}
/>
>
{linePathEffect ? linePathEffect : null}
</Line>
) : null}
{font
? canFitLabelContent && (
Expand Down
32 changes: 26 additions & 6 deletions lib/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { type SharedValue } from "react-native-reanimated";
import { type ScaleLinear } from "d3-scale";
import { type Color, type SkFont } from "@shopify/react-native-skia";
import {
type Color,
type DashPathEffect,
type SkFont,
} from "@shopify/react-native-skia";

export type PrimitiveViewWindow = {
xMin: number;
Expand Down Expand Up @@ -154,6 +158,9 @@ export type OptionalAxisProps<
formatYLabel?: (label: RawData[YK]) => string;
};

type DashPathEffectProps = React.ComponentProps<typeof DashPathEffect>;
type DashPathEffectComponent = React.ReactElement<DashPathEffectProps>;

export type XAxisInputProps<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
Expand All @@ -169,13 +176,18 @@ export type XAxisInputProps<
tickCount?: number;
tickValues?: number[];
yAxisSide?: YAxisSide;
linePathEffect?: DashPathEffectComponent;
};

export type XAxisPropsWithDefaults<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
> = Required<Omit<XAxisInputProps<RawData, XK>, "font" | "tickValues">> &
Partial<Pick<XAxisInputProps<RawData, XK>, "font" | "tickValues">>;
> = Required<
Omit<XAxisInputProps<RawData, XK>, "font" | "tickValues" | "linePathEffect">
> &
Partial<
Pick<XAxisInputProps<RawData, XK>, "font" | "tickValues" | "linePathEffect">
>;

export type XAxisProps<
RawData extends Record<string, unknown>,
Expand Down Expand Up @@ -203,13 +215,18 @@ export type YAxisInputProps<
tickValues?: number[];
yKeys?: YK[];
domain?: YAxisDomain;
linePathEffect?: DashPathEffectComponent;
};

export type YAxisPropsWithDefaults<
RawData extends Record<string, unknown>,
YK extends keyof NumericalFields<RawData>,
> = Required<Omit<YAxisInputProps<RawData, YK>, "font" | "tickValues">> &
Partial<Pick<YAxisInputProps<RawData, YK>, "font" | "tickValues">>;
> = Required<
Omit<YAxisInputProps<RawData, YK>, "font" | "tickValues" | "linePathEffect">
> &
Partial<
Pick<YAxisInputProps<RawData, YK>, "font" | "tickValues" | "linePathEffect">
>;

export type YAxisProps<
RawData extends Record<string, unknown>,
Expand All @@ -224,8 +241,11 @@ export type YAxisProps<
export type FrameInputProps = {
lineWidth?: number;
lineColor?: Color;
linePathEffect?: DashPathEffectComponent;
};
export type FramePropsWithDefaults = Required<FrameInputProps>;
export type FramePropsWithDefaults = Required<
Omit<FrameInputProps, "linePathEffect">
> & { linePathEffect?: DashPathEffectComponent };
export type FrameProps = FramePropsWithDefaults & {
xScale: Scale;
yScale: Scale;
Expand Down
Loading

0 comments on commit 69d530c

Please sign in to comment.