victory-native
Version:
A charting library for React Native with a focus on performance and customization.
423 lines (382 loc) • 11.6 kB
text/typescript
import { type SharedValue } from "react-native-reanimated";
import { type ScaleLinear } from "d3-scale";
import {
type Color,
type DashPathEffect,
type SkFont,
type SkPoint,
} from "@shopify/react-native-skia";
import type { ZoomTransform } from "d3-zoom";
import { type PanGesture } from "react-native-gesture-handler";
import type {
UserSelect,
TouchAction,
} from "react-native-gesture-handler/lib/typescript/handlers/gestureHandlerCommon";
export type PrimitiveViewWindow = {
xMin: number;
xMax: number;
yMin: number;
yMax: number;
};
export type ViewWindow = {
xMin: SharedValue<number>;
xMax: SharedValue<number>;
yMin: SharedValue<number>;
yMax: SharedValue<number>;
};
export type MaybeNumber = number | null | undefined;
export type InputDatum = Record<string, unknown>;
export type XAxisSide = "top" | "bottom";
export type YAxisSide = "left" | "right";
export type AxisLabelPosition = "inset" | "outset";
export type AxisTitlePosition = "start" | "center" | "end";
export type AxisLabelMeasurement = {
width: number;
height: number;
fontSize?: number;
lineHeight?: number;
};
export type AxisLabelMeasureArgs<Label = unknown> = {
axis: "x" | "y";
orientation: CartesianChartOrientation;
value: Label;
text: string;
index: number;
};
export type AxisLabelRenderArgs<Label = unknown> = AxisLabelMeasureArgs<Label> &
Required<AxisLabelMeasurement> & {
x: number;
y: number;
color: string;
canFitContent: boolean;
chartBounds: ChartBounds;
rotation?: number;
origin?: SkPoint;
};
export type AxisLabelRenderer<Label = unknown> = {
measure: (args: AxisLabelMeasureArgs<Label>) => AxisLabelMeasurement;
render: (args: AxisLabelRenderArgs<Label>) => React.ReactNode;
};
export type AxisTitle = {
text: string;
font?: SkFont | null;
color?: string;
position?: AxisTitlePosition;
offset?: number;
};
export type AxisScaleType = "linear" | "log";
export type CartesianChartOrientation = "vertical" | "horizontal";
export type AxisScales = {
xAxisScale?: AxisScaleType;
yAxisScale?: AxisScaleType;
};
export type ScatterOptions = {
radius: number;
};
export type ValueOf<T> = T[keyof T];
export type TransformedData<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
YK extends keyof NumericalFields<RawData>,
> = {
ix: InputFields<RawData>[XK][];
ox: number[];
y: { [K in YK]: { i: MaybeNumber[]; o: MaybeNumber[] } };
};
/**
* Used for e.g. padding where you can pass a single value or a sided object
*/
export type SidedNumber =
| number
| { left?: number; right?: number; top?: number; bottom?: number };
export type Viewport = {
x?: [number, number];
y?: [number, number];
};
/**
* Render arg for our line chart.
*/
export type ChartBounds = {
left: number;
right: number;
top: number;
bottom: number;
};
export type CartesianChartRenderArg<
RawData extends Record<string, unknown>,
YK extends keyof NumericalFields<RawData>,
> = {
xScale: Scale;
xTicks: number[];
yScale: Scale;
yTicks: number[];
chartBounds: ChartBounds;
canvasSize: { width: number; height: number };
points: {
[K in YK]: PointsArray;
};
};
export type Scale = ScaleLinear<number, number>;
export type YAxisDomain = [number] | [number, number] | null;
export type PointsArray = {
x: number;
xValue: InputFieldType;
y: MaybeNumber;
yValue: MaybeNumber;
}[];
export type InputFieldType = number | string;
export type InputFields<T> = {
[K in keyof T as T[K] extends InputFieldType
? K
: never]: T[K] extends InputFieldType ? T[K] : never;
};
export type NumericalFields<T> = {
[K in keyof T as T[K] extends MaybeNumber ? K : never]: T[K];
};
export type ColorFields<T> = {
[K in keyof T as T[K] extends Color ? K : never]: T[K];
};
export type StringKeyOf<T> = Extract<keyof T, string>;
/**
* @deprecated This prop will eventually be replaced by the new, separate x/y/frame props below. For now it's being kept around for backwards compatibility sake.
*/
export type AxisProps<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
YK extends keyof NumericalFields<RawData>,
XLabel = InputFields<RawData>[XK],
YLabel = RawData[YK],
> = {
xTicksNormalized: number[];
yTicksNormalized: number[];
xScale: ScaleLinear<number, number, never>;
yScale: ScaleLinear<number, number, never>;
axisScales?: AxisScales;
font?: SkFont | null;
lineColor?: Color | { grid: Color | { x: Color; y: Color }; frame: Color };
lineWidth?:
| number
| { grid: number | { x: number; y: number }; frame: number };
labelColor?: string | { x: string; y: string };
tickCount?: number | { x: number; y: number };
tickValues?: number[] | { x: number[]; y: number[] };
labelOffset?: number | { x: number; y: number };
labelPosition?:
| AxisLabelPosition
| { x: AxisLabelPosition; y: AxisLabelPosition };
axisSide?: { x: XAxisSide; y: YAxisSide };
formatXLabel?: (label: XLabel) => string;
formatYLabel?: (label: YLabel) => string;
domain?: YAxisDomain;
isNumericalData?: boolean;
ix?: InputFields<RawData>[XK][];
};
// The default prop options we pass in
export type AxisPropWithDefaults<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
YK extends keyof NumericalFields<RawData>,
XLabel = InputFields<RawData>[XK],
YLabel = RawData[YK],
> = Omit<
Required<AxisProps<RawData, XK, YK, XLabel, YLabel>>,
| "xScale"
| "yScale"
| "yTicksNormalized"
| "xTicksNormalized"
| "font"
| "tickValues"
| "isNumericalData"
>;
// The optional remaining props afterwards
export type OptionalAxisProps<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
YK extends keyof NumericalFields<RawData>,
XLabel = InputFields<RawData>[XK],
YLabel = RawData[YK],
> = {
tickValues?: number[] | { x: number[]; y: number[] };
font?: SkFont | null;
formatXLabel?: (label: XLabel) => string;
formatYLabel?: (label: YLabel) => 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>,
Label = InputFields<RawData>[XK],
> = {
axisSide?: XAxisSide;
font?: SkFont | null;
formatXLabel?: (label: Label) => string;
labelRenderer?: AxisLabelRenderer<Label>;
title?: AxisTitle;
labelColor?: string;
labelOffset?: number;
labelPosition?: AxisLabelPosition;
labelRotate?: number;
lineColor?: Color;
lineWidth?: number;
tickCount?: number;
tickValues?: number[];
yAxisSide?: YAxisSide;
linePathEffect?: DashPathEffectComponent;
enableRescaling?: boolean;
};
export type XAxisPropsWithDefaults<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
Label = InputFields<RawData>[XK],
> = Required<
Omit<
XAxisInputProps<RawData, XK, Label>,
| "font"
| "tickValues"
| "linePathEffect"
| "enableRescaling"
| "labelRotate"
| "labelRenderer"
| "title"
>
> &
Partial<
Pick<
XAxisInputProps<RawData, XK, Label>,
| "font"
| "tickValues"
| "linePathEffect"
| "enableRescaling"
| "labelRotate"
| "labelRenderer"
| "title"
>
>;
export type XAxisProps<
RawData extends Record<string, unknown>,
XK extends keyof InputFields<RawData>,
Label = InputFields<RawData>[XK],
> = XAxisPropsWithDefaults<RawData, XK, Label> & {
xScale: Scale;
yScale: Scale;
isNumericalData: boolean;
ix: InputFields<RawData>[XK][];
chartBounds: ChartBounds;
orientation: CartesianChartOrientation;
zoom?: ZoomTransform;
};
export type YAxisInputProps<
RawData extends Record<string, unknown>,
YK extends keyof NumericalFields<RawData>,
Label = RawData[YK],
> = {
axisSide?: YAxisSide;
font?: SkFont | null;
formatYLabel?: (label: Label) => string;
labelRenderer?: AxisLabelRenderer<Label>;
title?: AxisTitle;
labelColor?: string;
labelOffset?: number;
labelPosition?: AxisLabelPosition;
lineColor?: Color;
lineWidth?: number;
tickCount?: number;
tickValues?: number[];
yKeys?: YK[];
domain?: YAxisDomain;
linePathEffect?: DashPathEffectComponent;
enableRescaling?: boolean;
};
export type YAxisPropsWithDefaults<
RawData extends Record<string, unknown>,
YK extends keyof NumericalFields<RawData>,
Label = RawData[YK],
> = Required<
Omit<
YAxisInputProps<RawData, YK, Label>,
| "font"
| "tickValues"
| "linePathEffect"
| "enableRescaling"
| "labelRenderer"
| "title"
>
> &
Partial<
Pick<
YAxisInputProps<RawData, YK, Label>,
| "font"
| "tickValues"
| "linePathEffect"
| "enableRescaling"
| "labelRenderer"
| "title"
>
>;
export type YAxisProps<
RawData extends Record<string, unknown>,
YK extends keyof NumericalFields<RawData>,
Label = RawData[YK],
> = YAxisPropsWithDefaults<RawData, YK, Label> & {
xScale: Scale;
yScale: Scale;
yTicksNormalized: number[];
yKeys: YK[];
chartBounds: ChartBounds;
orientation: CartesianChartOrientation;
};
export type FrameInputProps = {
lineWidth?: SidedNumber;
lineColor?: Color;
linePathEffect?: DashPathEffectComponent;
};
export type FramePropsWithDefaults = Required<
Omit<FrameInputProps, "linePathEffect">
> & { linePathEffect?: DashPathEffectComponent };
export type FrameProps = FramePropsWithDefaults & {
xScale: Scale;
yScale: Scale;
};
type ExtractParams<
T extends (...args: any[]) => any, // eslint-disable-line
P extends Parameters<T> = Parameters<T>,
> = P["length"] extends 1 ? P[0] : P;
type ExternalGesture = Parameters<
PanGesture["simultaneousWithExternalGesture"]
>[number];
export type ChartPressPanConfig = {
activateAfterLongPress?: ExtractParams<PanGesture["activateAfterLongPress"]>;
activeOffsetX?: ExtractParams<PanGesture["activeOffsetX"]>;
activeOffsetY?: ExtractParams<PanGesture["activeOffsetY"]>;
failOffsetX?: ExtractParams<PanGesture["failOffsetX"]>;
failOffsetY?: ExtractParams<PanGesture["failOffsetY"]>;
simultaneousWithExternalGesture?: ExternalGesture | ExternalGesture[];
};
export type NonEmptyArray<T> = [T, ...T[]];
/**
* Configuration options for the underlying Gesture Handler.
* These properties correspond directly to the properties available on the
* [`GestureDetector` component](https://docs.swmansion.com/react-native-gesture-handler/docs/api/gestures/gesture-detector#properties)
* from React Native Gesture Handler.
*/
export interface GestureHandlerConfig {
/**
* Controls how text selection behaves when gestures are active.
* Primarily affects web platforms.
* @see https://docs.swmansion.com/react-native-gesture-handler/docs/api/gestures/gesture-detector#userselectuserselect
*/
userSelect?: UserSelect;
/**
* Determines whether the context menu should be enabled.
* Primarily affects web platforms.
* @see https://docs.swmansion.com/react-native-gesture-handler/docs/api/gestures/gesture-detector#enablecontextmenuenablecontextmenu
*/
enableContextMenu?: boolean;
/**
* Manages the touch behavior for the element, such as preventing default browser actions like scrolling.
* Primarily affects web platforms.
* @see https://docs.swmansion.com/react-native-gesture-handler/docs/api/gestures/gesture-detector#touchactiontouchaction
*/
touchAction?: TouchAction;
}