rn-native-ios-charts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/docs/demo.gif +0 -0
- package/docs/demo.mp4 +0 -0
- package/expo-module.config.json +6 -0
- package/ios/ChartConfig.swift +70 -0
- package/ios/ChartDataPoint.swift +25 -0
- package/ios/ChartGradient.swift +33 -0
- package/ios/ChartMark.swift +64 -0
- package/ios/ChartView.swift +618 -0
- package/ios/NativeIosCharts.podspec +35 -0
- package/ios/NativeIosChartsModule.swift +37 -0
- package/package.json +50 -0
- package/src/AreaChart.tsx +68 -0
- package/src/BarChart.tsx +74 -0
- package/src/Chart.tsx +50 -0
- package/src/LineChart.tsx +104 -0
- package/src/PieChart.tsx +77 -0
- package/src/RangeBarChart.tsx +76 -0
- package/src/ScatterChart.tsx +73 -0
- package/src/index.ts +43 -0
- package/src/support.ts +30 -0
- package/src/types.ts +213 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rn-native-ios-charts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Native SwiftUI Charts for React Native / Expo. iOS-only, zero-compromise charts that bypass the limitations of cross-platform chart libraries.",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"react-native",
|
|
9
|
+
"expo",
|
|
10
|
+
"swiftui",
|
|
11
|
+
"swift-charts",
|
|
12
|
+
"ios",
|
|
13
|
+
"chart",
|
|
14
|
+
"pie",
|
|
15
|
+
"donut",
|
|
16
|
+
"line",
|
|
17
|
+
"area"
|
|
18
|
+
],
|
|
19
|
+
"author": "Abdalla Emadeldin",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/abdallaemadeldin/rn-native-ios-charts.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/abdallaemadeldin/rn-native-ios-charts/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/abdallaemadeldin/rn-native-ios-charts#readme",
|
|
29
|
+
"files": [
|
|
30
|
+
"src",
|
|
31
|
+
"ios",
|
|
32
|
+
"docs",
|
|
33
|
+
"expo-module.config.json",
|
|
34
|
+
"package.json",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"CHANGELOG.md"
|
|
38
|
+
],
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"expo": "*",
|
|
41
|
+
"react": "*",
|
|
42
|
+
"react-native": "*"
|
|
43
|
+
},
|
|
44
|
+
"directories": {
|
|
45
|
+
"doc": "docs"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
AxisConfig,
|
|
6
|
+
DataPoint,
|
|
7
|
+
Gradient,
|
|
8
|
+
Interpolation,
|
|
9
|
+
LegendConfig,
|
|
10
|
+
SelectedPoint,
|
|
11
|
+
TooltipConfig,
|
|
12
|
+
} from "./types";
|
|
13
|
+
|
|
14
|
+
export type AreaDatum = { x: string; y: number; category?: string };
|
|
15
|
+
|
|
16
|
+
export type AreaChartProps = {
|
|
17
|
+
data: AreaDatum[];
|
|
18
|
+
color?: ColorValue;
|
|
19
|
+
gradient?: Gradient;
|
|
20
|
+
interpolation?: Interpolation;
|
|
21
|
+
xAxis?: AxisConfig;
|
|
22
|
+
yAxis?: AxisConfig;
|
|
23
|
+
legend?: LegendConfig;
|
|
24
|
+
tooltip?: TooltipConfig;
|
|
25
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
26
|
+
animate?: boolean;
|
|
27
|
+
style?: ViewStyle;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function AreaChart({
|
|
31
|
+
data,
|
|
32
|
+
color,
|
|
33
|
+
gradient = { startOpacity: 0.35, endOpacity: 0.02 },
|
|
34
|
+
interpolation = "catmullRom",
|
|
35
|
+
xAxis,
|
|
36
|
+
yAxis,
|
|
37
|
+
legend,
|
|
38
|
+
tooltip,
|
|
39
|
+
onSelect,
|
|
40
|
+
animate,
|
|
41
|
+
style,
|
|
42
|
+
}: AreaChartProps) {
|
|
43
|
+
const points: DataPoint[] = data.map((p) => ({
|
|
44
|
+
x: p.x,
|
|
45
|
+
y: p.y,
|
|
46
|
+
category: p.category,
|
|
47
|
+
}));
|
|
48
|
+
return (
|
|
49
|
+
<Chart
|
|
50
|
+
style={style}
|
|
51
|
+
animate={animate}
|
|
52
|
+
xAxis={xAxis}
|
|
53
|
+
yAxis={yAxis}
|
|
54
|
+
legend={legend}
|
|
55
|
+
tooltip={tooltip}
|
|
56
|
+
onSelect={onSelect}
|
|
57
|
+
marks={[
|
|
58
|
+
{
|
|
59
|
+
type: "area",
|
|
60
|
+
data: points,
|
|
61
|
+
color,
|
|
62
|
+
gradient,
|
|
63
|
+
interpolation,
|
|
64
|
+
},
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
package/src/BarChart.tsx
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
AxisConfig,
|
|
6
|
+
DataPoint,
|
|
7
|
+
LegendConfig,
|
|
8
|
+
SelectedPoint,
|
|
9
|
+
TooltipConfig,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
export type BarDatum = {
|
|
13
|
+
x: string;
|
|
14
|
+
y: number;
|
|
15
|
+
/** Optional series key for grouped / colored bars. */
|
|
16
|
+
category?: string;
|
|
17
|
+
color?: ColorValue;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type BarChartProps = {
|
|
21
|
+
data: BarDatum[];
|
|
22
|
+
color?: ColorValue;
|
|
23
|
+
cornerRadius?: number;
|
|
24
|
+
/** Fixed width per bar in pt. 0 = auto. */
|
|
25
|
+
barWidth?: number;
|
|
26
|
+
xAxis?: AxisConfig;
|
|
27
|
+
yAxis?: AxisConfig;
|
|
28
|
+
legend?: LegendConfig;
|
|
29
|
+
tooltip?: TooltipConfig;
|
|
30
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
31
|
+
animate?: boolean;
|
|
32
|
+
style?: ViewStyle;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function BarChart({
|
|
36
|
+
data,
|
|
37
|
+
color,
|
|
38
|
+
cornerRadius = 4,
|
|
39
|
+
barWidth,
|
|
40
|
+
xAxis,
|
|
41
|
+
yAxis,
|
|
42
|
+
legend,
|
|
43
|
+
tooltip,
|
|
44
|
+
onSelect,
|
|
45
|
+
animate,
|
|
46
|
+
style,
|
|
47
|
+
}: BarChartProps) {
|
|
48
|
+
const points: DataPoint[] = data.map((d) => ({
|
|
49
|
+
x: d.x,
|
|
50
|
+
y: d.y,
|
|
51
|
+
category: d.category,
|
|
52
|
+
color: d.color,
|
|
53
|
+
}));
|
|
54
|
+
return (
|
|
55
|
+
<Chart
|
|
56
|
+
style={style}
|
|
57
|
+
animate={animate}
|
|
58
|
+
xAxis={xAxis}
|
|
59
|
+
yAxis={yAxis}
|
|
60
|
+
legend={legend}
|
|
61
|
+
tooltip={tooltip}
|
|
62
|
+
onSelect={onSelect}
|
|
63
|
+
marks={[
|
|
64
|
+
{
|
|
65
|
+
type: "bar",
|
|
66
|
+
data: points,
|
|
67
|
+
color,
|
|
68
|
+
cornerRadius,
|
|
69
|
+
barWidth,
|
|
70
|
+
},
|
|
71
|
+
]}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
package/src/Chart.tsx
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { requireNativeView } from "expo";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Platform, View } from "react-native";
|
|
4
|
+
import type { ChartProps, SelectedPoint } from "./types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generic SwiftUI Charts view. Render any combination of bar / line /
|
|
8
|
+
* area / point / rectangle / rule / sector marks by passing them in
|
|
9
|
+
* the `marks` array. iOS-only — on other platforms this is a no-op
|
|
10
|
+
* placeholder `View`, so consuming code can mount it unconditionally
|
|
11
|
+
* and feature-detect via `Platform.OS`.
|
|
12
|
+
*
|
|
13
|
+
* Use the convenience wrappers (`PieChart`, `LineChart`, `BarChart`,
|
|
14
|
+
* etc.) for common single-mark cases — they all delegate here.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Expo Modules events deliver the payload wrapped in `nativeEvent`.
|
|
18
|
+
// We re-shape it into the public `SelectedPoint` type at the boundary
|
|
19
|
+
// so consumers don't have to deal with the bridge layout.
|
|
20
|
+
type NativeChartProps = Omit<ChartProps, "onSelect"> & {
|
|
21
|
+
onSelect?: (event: {
|
|
22
|
+
nativeEvent: { x?: string; y?: number };
|
|
23
|
+
}) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const NativeChart =
|
|
27
|
+
Platform.OS === "ios"
|
|
28
|
+
? requireNativeView<NativeChartProps>("NativeIosCharts", "ChartView")
|
|
29
|
+
: null;
|
|
30
|
+
|
|
31
|
+
export function Chart(props: ChartProps) {
|
|
32
|
+
if (!NativeChart) {
|
|
33
|
+
return <View style={props.style} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { onSelect, ...rest } = props;
|
|
37
|
+
|
|
38
|
+
// Wrap the user's callback to unwrap the native event shape. Empty
|
|
39
|
+
// payloads (`{}`) signal a cleared selection — emit `null` for them.
|
|
40
|
+
const handleSelect = onSelect
|
|
41
|
+
? (event: { nativeEvent: { x?: string; y?: number } }) => {
|
|
42
|
+
const { x, y } = event.nativeEvent ?? {};
|
|
43
|
+
const point: SelectedPoint =
|
|
44
|
+
typeof x === "string" && typeof y === "number" ? { x, y } : null;
|
|
45
|
+
onSelect(point);
|
|
46
|
+
}
|
|
47
|
+
: undefined;
|
|
48
|
+
|
|
49
|
+
return <NativeChart {...rest} onSelect={handleSelect} />;
|
|
50
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
AxisConfig,
|
|
6
|
+
DataPoint,
|
|
7
|
+
Gradient,
|
|
8
|
+
Interpolation,
|
|
9
|
+
LegendConfig,
|
|
10
|
+
SelectedPoint,
|
|
11
|
+
Symbol,
|
|
12
|
+
TooltipConfig,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
export type LinePoint = { x: string; y: number; category?: string };
|
|
16
|
+
|
|
17
|
+
export type LineChartProps = {
|
|
18
|
+
data: LinePoint[];
|
|
19
|
+
color?: ColorValue;
|
|
20
|
+
lineWidth?: number;
|
|
21
|
+
interpolation?: Interpolation;
|
|
22
|
+
dashArray?: number[];
|
|
23
|
+
/**
|
|
24
|
+
* Set this to render a gradient-filled area beneath the line as
|
|
25
|
+
* well — the standard "shaded line chart" pattern. Same shape as
|
|
26
|
+
* the generic `Gradient`.
|
|
27
|
+
*/
|
|
28
|
+
area?: Gradient | boolean;
|
|
29
|
+
showPoints?: boolean;
|
|
30
|
+
symbol?: Symbol;
|
|
31
|
+
symbolSize?: number;
|
|
32
|
+
xAxis?: AxisConfig;
|
|
33
|
+
yAxis?: AxisConfig;
|
|
34
|
+
legend?: LegendConfig;
|
|
35
|
+
tooltip?: TooltipConfig;
|
|
36
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
37
|
+
animate?: boolean;
|
|
38
|
+
style?: ViewStyle;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function LineChart({
|
|
42
|
+
data,
|
|
43
|
+
color,
|
|
44
|
+
lineWidth = 2.5,
|
|
45
|
+
interpolation = "catmullRom",
|
|
46
|
+
dashArray,
|
|
47
|
+
area,
|
|
48
|
+
showPoints,
|
|
49
|
+
symbol,
|
|
50
|
+
symbolSize,
|
|
51
|
+
xAxis,
|
|
52
|
+
yAxis,
|
|
53
|
+
legend,
|
|
54
|
+
tooltip,
|
|
55
|
+
onSelect,
|
|
56
|
+
animate,
|
|
57
|
+
style,
|
|
58
|
+
}: LineChartProps) {
|
|
59
|
+
const points: DataPoint[] = data.map((p) => ({
|
|
60
|
+
x: p.x,
|
|
61
|
+
y: p.y,
|
|
62
|
+
category: p.category,
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
const marks = [];
|
|
66
|
+
|
|
67
|
+
// Render the area FIRST (so the line draws on top).
|
|
68
|
+
if (area) {
|
|
69
|
+
const gradient: Gradient =
|
|
70
|
+
typeof area === "object" ? area : { startOpacity: 0.35, endOpacity: 0.02 };
|
|
71
|
+
marks.push({
|
|
72
|
+
type: "area" as const,
|
|
73
|
+
data: points,
|
|
74
|
+
color,
|
|
75
|
+
gradient,
|
|
76
|
+
interpolation,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
marks.push({
|
|
81
|
+
type: "line" as const,
|
|
82
|
+
data: points,
|
|
83
|
+
color,
|
|
84
|
+
lineWidth,
|
|
85
|
+
dashArray,
|
|
86
|
+
interpolation,
|
|
87
|
+
showPoints,
|
|
88
|
+
symbol,
|
|
89
|
+
symbolSize,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Chart
|
|
94
|
+
style={style}
|
|
95
|
+
animate={animate}
|
|
96
|
+
xAxis={xAxis}
|
|
97
|
+
yAxis={yAxis}
|
|
98
|
+
legend={legend}
|
|
99
|
+
tooltip={tooltip}
|
|
100
|
+
onSelect={onSelect}
|
|
101
|
+
marks={marks}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
package/src/PieChart.tsx
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
CenterLabel,
|
|
6
|
+
DataPoint,
|
|
7
|
+
LegendConfig,
|
|
8
|
+
SelectedPoint,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
export type PieSlice = {
|
|
12
|
+
label: string;
|
|
13
|
+
value: number;
|
|
14
|
+
color?: ColorValue;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type PieChartProps = {
|
|
18
|
+
data: PieSlice[];
|
|
19
|
+
/** Donut hole ratio, 0–1. 0 = full pie, 0.62 = thin donut. Default 0.62. */
|
|
20
|
+
innerRadius?: number;
|
|
21
|
+
/** Outer radius ratio, 0–1. 0 = auto. */
|
|
22
|
+
outerRadius?: number;
|
|
23
|
+
/** Gap between adjacent slices, pt. Default 2. */
|
|
24
|
+
angularInset?: number;
|
|
25
|
+
cornerRadius?: number;
|
|
26
|
+
/** Center label rendered inside the donut hole. */
|
|
27
|
+
centerLabel?: CenterLabel;
|
|
28
|
+
legend?: LegendConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Fires when the user taps a slice. The payload `x` is the slice
|
|
31
|
+
* label and `y` is its value. `null` when the selection clears.
|
|
32
|
+
* No visual callout is drawn — wire `centerLabel` from this event
|
|
33
|
+
* to update the donut hole copy.
|
|
34
|
+
*/
|
|
35
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
36
|
+
animate?: boolean;
|
|
37
|
+
style?: ViewStyle;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function PieChart({
|
|
41
|
+
data,
|
|
42
|
+
innerRadius = 0.62,
|
|
43
|
+
outerRadius,
|
|
44
|
+
angularInset = 2,
|
|
45
|
+
cornerRadius = 2,
|
|
46
|
+
centerLabel,
|
|
47
|
+
legend,
|
|
48
|
+
onSelect,
|
|
49
|
+
animate,
|
|
50
|
+
style,
|
|
51
|
+
}: PieChartProps) {
|
|
52
|
+
const points: DataPoint[] = data.map((s) => ({
|
|
53
|
+
x: s.label,
|
|
54
|
+
y: s.value,
|
|
55
|
+
color: s.color,
|
|
56
|
+
category: s.label,
|
|
57
|
+
}));
|
|
58
|
+
return (
|
|
59
|
+
<Chart
|
|
60
|
+
style={style}
|
|
61
|
+
animate={animate}
|
|
62
|
+
legend={legend ?? { hidden: true }}
|
|
63
|
+
centerLabel={centerLabel}
|
|
64
|
+
onSelect={onSelect}
|
|
65
|
+
marks={[
|
|
66
|
+
{
|
|
67
|
+
type: "sector",
|
|
68
|
+
data: points,
|
|
69
|
+
innerRadius,
|
|
70
|
+
outerRadius,
|
|
71
|
+
angularInset,
|
|
72
|
+
cornerRadius,
|
|
73
|
+
},
|
|
74
|
+
]}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
AxisConfig,
|
|
6
|
+
DataPoint,
|
|
7
|
+
LegendConfig,
|
|
8
|
+
SelectedPoint,
|
|
9
|
+
TooltipConfig,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
export type RangeDatum = {
|
|
13
|
+
x: string;
|
|
14
|
+
yStart: number;
|
|
15
|
+
yEnd: number;
|
|
16
|
+
category?: string;
|
|
17
|
+
color?: ColorValue;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type RangeBarChartProps = {
|
|
21
|
+
data: RangeDatum[];
|
|
22
|
+
color?: ColorValue;
|
|
23
|
+
cornerRadius?: number;
|
|
24
|
+
xAxis?: AxisConfig;
|
|
25
|
+
yAxis?: AxisConfig;
|
|
26
|
+
legend?: LegendConfig;
|
|
27
|
+
tooltip?: TooltipConfig;
|
|
28
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
29
|
+
animate?: boolean;
|
|
30
|
+
style?: ViewStyle;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Range bars — drawn as `RectangleMark` between `yStart` and `yEnd`.
|
|
35
|
+
* Use for candlestick / OHLC visualisations, Gantt-style timelines,
|
|
36
|
+
* or low/high bands.
|
|
37
|
+
*/
|
|
38
|
+
export function RangeBarChart({
|
|
39
|
+
data,
|
|
40
|
+
color,
|
|
41
|
+
cornerRadius = 2,
|
|
42
|
+
xAxis,
|
|
43
|
+
yAxis,
|
|
44
|
+
legend,
|
|
45
|
+
tooltip,
|
|
46
|
+
onSelect,
|
|
47
|
+
animate,
|
|
48
|
+
style,
|
|
49
|
+
}: RangeBarChartProps) {
|
|
50
|
+
const points: DataPoint[] = data.map((d) => ({
|
|
51
|
+
x: d.x,
|
|
52
|
+
y: d.yStart,
|
|
53
|
+
yEnd: d.yEnd,
|
|
54
|
+
category: d.category,
|
|
55
|
+
color: d.color,
|
|
56
|
+
}));
|
|
57
|
+
return (
|
|
58
|
+
<Chart
|
|
59
|
+
style={style}
|
|
60
|
+
animate={animate}
|
|
61
|
+
xAxis={xAxis}
|
|
62
|
+
yAxis={yAxis}
|
|
63
|
+
legend={legend}
|
|
64
|
+
tooltip={tooltip}
|
|
65
|
+
onSelect={onSelect}
|
|
66
|
+
marks={[
|
|
67
|
+
{
|
|
68
|
+
type: "rectangle",
|
|
69
|
+
data: points,
|
|
70
|
+
color,
|
|
71
|
+
cornerRadius,
|
|
72
|
+
},
|
|
73
|
+
]}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ColorValue, ViewStyle } from "react-native";
|
|
3
|
+
import { Chart } from "./Chart";
|
|
4
|
+
import type {
|
|
5
|
+
AxisConfig,
|
|
6
|
+
DataPoint,
|
|
7
|
+
LegendConfig,
|
|
8
|
+
SelectedPoint,
|
|
9
|
+
Symbol,
|
|
10
|
+
TooltipConfig,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
export type ScatterDatum = {
|
|
14
|
+
x: string;
|
|
15
|
+
y: number;
|
|
16
|
+
category?: string;
|
|
17
|
+
color?: ColorValue;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ScatterChartProps = {
|
|
21
|
+
data: ScatterDatum[];
|
|
22
|
+
color?: ColorValue;
|
|
23
|
+
symbol?: Symbol;
|
|
24
|
+
symbolSize?: number;
|
|
25
|
+
xAxis?: AxisConfig;
|
|
26
|
+
yAxis?: AxisConfig;
|
|
27
|
+
legend?: LegendConfig;
|
|
28
|
+
tooltip?: TooltipConfig;
|
|
29
|
+
onSelect?: (point: SelectedPoint) => void;
|
|
30
|
+
animate?: boolean;
|
|
31
|
+
style?: ViewStyle;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function ScatterChart({
|
|
35
|
+
data,
|
|
36
|
+
color,
|
|
37
|
+
symbol = "circle",
|
|
38
|
+
symbolSize = 36,
|
|
39
|
+
xAxis,
|
|
40
|
+
yAxis,
|
|
41
|
+
legend,
|
|
42
|
+
tooltip,
|
|
43
|
+
onSelect,
|
|
44
|
+
animate,
|
|
45
|
+
style,
|
|
46
|
+
}: ScatterChartProps) {
|
|
47
|
+
const points: DataPoint[] = data.map((d) => ({
|
|
48
|
+
x: d.x,
|
|
49
|
+
y: d.y,
|
|
50
|
+
category: d.category,
|
|
51
|
+
color: d.color,
|
|
52
|
+
}));
|
|
53
|
+
return (
|
|
54
|
+
<Chart
|
|
55
|
+
style={style}
|
|
56
|
+
animate={animate}
|
|
57
|
+
xAxis={xAxis}
|
|
58
|
+
yAxis={yAxis}
|
|
59
|
+
legend={legend}
|
|
60
|
+
tooltip={tooltip}
|
|
61
|
+
onSelect={onSelect}
|
|
62
|
+
marks={[
|
|
63
|
+
{
|
|
64
|
+
type: "point",
|
|
65
|
+
data: points,
|
|
66
|
+
color,
|
|
67
|
+
symbol,
|
|
68
|
+
symbolSize,
|
|
69
|
+
},
|
|
70
|
+
]}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ── Runtime feature detection ──
|
|
2
|
+
export { isChartSupported } from "./support";
|
|
3
|
+
|
|
4
|
+
// ── Generic chart (any combination of marks) ──
|
|
5
|
+
export { Chart } from "./Chart";
|
|
6
|
+
|
|
7
|
+
// ── Convenience wrappers for the common single-mark cases ──
|
|
8
|
+
export { PieChart } from "./PieChart";
|
|
9
|
+
export type { PieChartProps, PieSlice } from "./PieChart";
|
|
10
|
+
|
|
11
|
+
export { LineChart } from "./LineChart";
|
|
12
|
+
export type { LineChartProps, LinePoint } from "./LineChart";
|
|
13
|
+
|
|
14
|
+
export { AreaChart } from "./AreaChart";
|
|
15
|
+
export type { AreaChartProps, AreaDatum } from "./AreaChart";
|
|
16
|
+
|
|
17
|
+
export { BarChart } from "./BarChart";
|
|
18
|
+
export type { BarChartProps, BarDatum } from "./BarChart";
|
|
19
|
+
|
|
20
|
+
export { ScatterChart } from "./ScatterChart";
|
|
21
|
+
export type { ScatterChartProps, ScatterDatum } from "./ScatterChart";
|
|
22
|
+
|
|
23
|
+
export { RangeBarChart } from "./RangeBarChart";
|
|
24
|
+
export type { RangeBarChartProps, RangeDatum } from "./RangeBarChart";
|
|
25
|
+
|
|
26
|
+
// ── Public types — use these to assemble your own marks for the
|
|
27
|
+
// generic `<Chart>` component or to type-share data across screens.
|
|
28
|
+
export type {
|
|
29
|
+
AxisConfig,
|
|
30
|
+
CenterLabel,
|
|
31
|
+
ChartProps,
|
|
32
|
+
DataPoint,
|
|
33
|
+
Gradient,
|
|
34
|
+
GradientStop,
|
|
35
|
+
Interpolation,
|
|
36
|
+
LegendConfig,
|
|
37
|
+
LineCap,
|
|
38
|
+
Mark,
|
|
39
|
+
MarkType,
|
|
40
|
+
SelectedPoint,
|
|
41
|
+
Symbol,
|
|
42
|
+
TooltipConfig,
|
|
43
|
+
} from "./types";
|
package/src/support.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Platform } from "react-native";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Runtime feature-detection helper. Returns true only on devices
|
|
5
|
+
* that can actually render the charts — iOS 17 and above. Use it to
|
|
6
|
+
* mount a fallback chart library (or a placeholder) on older iOS
|
|
7
|
+
* versions and on Android / web:
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { isChartSupported, LineChart } from "rn-native-ios-charts";
|
|
11
|
+
* import { OtherLineChart } from "some-cross-platform-chart-lib";
|
|
12
|
+
*
|
|
13
|
+
* export function MyLineChart(props) {
|
|
14
|
+
* if (isChartSupported()) return <LineChart {...props} />;
|
|
15
|
+
* return <OtherLineChart {...props} />;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The check is cheap (a parse of `Platform.Version`) and stable
|
|
20
|
+
* across the lifetime of the process — safe to call inline in render.
|
|
21
|
+
*/
|
|
22
|
+
export function isChartSupported(): boolean {
|
|
23
|
+
if (Platform.OS !== "ios") return false;
|
|
24
|
+
// Platform.Version on iOS is a string like "17.4". Parsing the
|
|
25
|
+
// leading integer is enough; SwiftUI Charts' unified API
|
|
26
|
+
// (`Chart {}`, `SectorMark`, `chartBackground`,
|
|
27
|
+
// `chartXSelection`) all became available in iOS 17.0.
|
|
28
|
+
const major = parseInt(String(Platform.Version), 10);
|
|
29
|
+
return Number.isFinite(major) && major >= 17;
|
|
30
|
+
}
|