Skip to content

Commit

Permalink
Add Area Range Component (#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
carloskelly13 authored Jan 8, 2025
1 parent 890dccc commit 136ec9b
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-socks-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"victory-native": minor
---

Add AreaRange component
11 changes: 11 additions & 0 deletions example/app/line-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
import { useState } from "react";
import { SafeAreaView, ScrollView, StyleSheet, View } from "react-native";
import {
AreaRange,
CartesianChart,
type CurveType,
Line,
Expand Down Expand Up @@ -113,6 +114,16 @@ export default function LineChartPage(props: { segment: string }) {
>
{({ points }) => (
<>
<AreaRange
points={points.sales.map((p) => ({
...p,
y: p.y! + 20,
y0: p.y! - 20,
}))}
curveType={curveType}
color={`${colors.stroke}40`}
animate={{ type: "spring" }}
/>
<Line
points={points.sales}
curveType={curveType}
Expand Down
58 changes: 58 additions & 0 deletions lib/src/cartesian/components/AreaRange.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from "react";
import { Path, type PathProps } from "@shopify/react-native-skia";
import type { PointsArray } from "../../types";
import { type AreaPathOptions, useAreaPath } from "../hooks/useAreaPath";
import { AnimatedPath } from "./AnimatedPath";
import { type PathAnimationConfig } from "../../hooks/useAnimatedPath";

// Extend PointsArray but make y represent upper bound and y0 lower bound
export type AreaRangePointsArray = {
x: number;
xValue: PointsArray[number]["xValue"];
y: number;
y0: number;
yValue: PointsArray[number]["yValue"];
}[];

export type AreaRangeProps = {
points: AreaRangePointsArray;
animate?: PathAnimationConfig;
} & AreaPathOptions &
Partial<Pick<PathProps, "color" | "blendMode" | "opacity" | "antiAlias">>;

export function AreaRange({
points,
animate,
curveType,
connectMissingData,
...ops
}: React.PropsWithChildren<AreaRangeProps>) {
const areaRangePoints = React.useMemo(() => {
// Create upper bound points going forward
const upperPoints = points.map((point) => ({
...point,
y: point.y,
}));

// Create lower bound points going backward
const lowerPoints = [...points].reverse().map((point) => ({
...point,
y: point.y0,
}));

// Combine into single array that traces a complete path
return [...upperPoints, ...lowerPoints];
}, [points]);

const { path } = useAreaPath(areaRangePoints, 0, {
curveType,
connectMissingData,
});

return React.createElement(animate ? AnimatedPath : Path, {
path,
style: "fill",
...ops,
...(Boolean(animate) && { animate }),
});
}
1 change: 1 addition & 0 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export { BarGroup } from "./cartesian/components/BarGroup";
export { useAreaPath } from "./cartesian/hooks/useAreaPath";
export { Area } from "./cartesian/components/Area";
export { StackedArea } from "./cartesian/components/StackedArea";
export { AreaRange } from "./cartesian/components/AreaRange";
export { useStackedAreaPaths } from "./cartesian/hooks/useStackedAreaPaths";

// Scatter
Expand Down
71 changes: 71 additions & 0 deletions website/docs/cartesian/area/area-range.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# `AreaRange` (Component)

The `AreaRange` component takes a `PointsArray` prop with upper and lower bounds (`y` and `y0`), as well as options for styling/animating, and returns a Skia `Path` element to draw a shaded area between two bounds. This is commonly used for visualizing error bars, confidence intervals, or data ranges around a central line.

## Example

```tsx
import { CartesianChart, AreaRange, Line } from "victory-native";
import DATA from "./my-data";

export function MyChart() {
return (
<CartesianChart data={DATA} xKey="x" yKeys={["y"]}>
{({ points }) => (
<>
{/* Draw shaded area for error bounds */}
<AreaRange
points={points.y.map(p => ({
...p,
y: p.y + errorMargin, // Upper bound
y0: p.y - errorMargin // Lower bound
}))}
color="rgba(100, 100, 255, 0.2)"
animate={{ type: "spring" }}
/>
{/* Draw the main line */}
<Line
points={points.y}
color="blue"
strokeWidth={2}
animate={{ type: "spring" }}
/>
</>
)}
</CartesianChart>
);
}
```

## Props

### `points`

An `AreaRangePointsArray` array that extends the standard `PointsArray` type but uses:
- `y` to represent the upper bound
- `y0` to represent the lower bound

### `animate`

The `animate` prop takes [a `PathAnimationConfig` object](../../animated-paths.md#animconfig) and will animate the path when the points change.

### `curveType`

[A `CurveType` value](./use-area-path.md#options) that indicates the type of curve should be drawn (e.g. `linear` or `natural`).

### `connectMissingData`

[The `connectMissingData: boolean` value](./use-area-path.md#options) that indicates whether missing data should be interpolated for the resulting `Path`. If set to `true`, the output will be a single, connected area (even if there are missing data values). If set to `false`, if there is missing data values – the path will consist of multiple disconnected "parts".

### `children`

A `children` pass-thru that will be rendered inside of the Skia `Path` element, useful if you'd like to make e.g. a gradient path.

### Paint properties

The `AreaRange` component will also pass the following [painting props](https://shopify.github.io/react-native-skia/docs/paint/overview) down to the underlying `Path` component:

- `color`
- `blendMode`
- `opacity`
- `antiAlias`
4 changes: 4 additions & 0 deletions website/docs/cartesian/area/area.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

The `Area` component takes a `PointsArray` prop, a value for the "bottom" of the area, and some options for styling/animating, and returns a Skia `Path` element to draw the line chart.

:::tip
For visualizing ranges or error bounds around a line, check out the [`AreaRange`](./area-range.md) component.
:::

## Example

```tsx
Expand Down

0 comments on commit 136ec9b

Please sign in to comment.