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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **rn-native-ios-charts** are documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-05-11
|
|
10
|
+
|
|
11
|
+
Initial public release. iOS-only Expo module that bridges every SwiftUI
|
|
12
|
+
`Charts` mark type to React Native with a single composable `<Chart />`
|
|
13
|
+
primitive plus convenience wrappers.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Generic `<Chart />`** — renders any combination of `bar`, `line`,
|
|
18
|
+
`area`, `point`, `rectangle`, `rule`, and `sector` marks in a single
|
|
19
|
+
view.
|
|
20
|
+
- **Convenience wrappers** — `<PieChart />`, `<LineChart />`,
|
|
21
|
+
`<AreaChart />`, `<BarChart />`, `<ScatterChart />`, `<RangeBarChart />`.
|
|
22
|
+
All delegate to the generic primitive.
|
|
23
|
+
- **Pie / donut `centerLabel`** — value + caption slot rendered inside
|
|
24
|
+
the chart's plot frame via SwiftUI's `chartBackground` +
|
|
25
|
+
`ChartProxy.plotFrame`. Tracks the donut centre natively, no JS
|
|
26
|
+
overlays.
|
|
27
|
+
- **Native gradients** — `LinearGradient` on any mark's
|
|
28
|
+
`foregroundStyle` with two-stop shorthand (`startOpacity` /
|
|
29
|
+
`endOpacity`) or full multi-stop `stops`.
|
|
30
|
+
- **Per-point overrides** — every datum can carry its own `color` and
|
|
31
|
+
`category` (series key for auto-coloring + legend grouping).
|
|
32
|
+
- **Interactive tooltips** — opt-in `tooltip` prop drives SwiftUI's
|
|
33
|
+
native `chartXSelection` scrubber: vertical rule + highlighted dot +
|
|
34
|
+
auto-clamped callout. All rendered inside the SwiftUI view hierarchy,
|
|
35
|
+
no JS frame round-trips. Configurable colors, value prefix/suffix,
|
|
36
|
+
decimal places.
|
|
37
|
+
- **`onSelect` event** — fires when the user picks a point via the
|
|
38
|
+
scrubber or taps a pie sector. Pie uses native `chartAngleSelection`.
|
|
39
|
+
- **Line interpolation** — `linear`, `catmullRom`, `monotone`,
|
|
40
|
+
`stepStart`, `stepEnd`, `stepCenter`.
|
|
41
|
+
- **Symbols** — `circle`, `square`, `triangle`, `diamond`, `pentagon`,
|
|
42
|
+
`plus`, `cross`, `asterisk`. With `symbolSize` and `showPoints`.
|
|
43
|
+
- **Axis config** — hidden, grid lines, tick labels, label color & font
|
|
44
|
+
size, custom `[domainMin, domainMax]`.
|
|
45
|
+
- **Legend config** — hidden, placement (`top`, `bottom`, `leading`,
|
|
46
|
+
`trailing`, `overlay`, `automatic`).
|
|
47
|
+
- **`isChartSupported()`** — runtime feature-detection helper. Use it
|
|
48
|
+
to mount a fallback chart library on iOS < 17 and on Android / web.
|
|
49
|
+
|
|
50
|
+
### Platform support
|
|
51
|
+
|
|
52
|
+
- **iOS 17+** — full rendering (SwiftUI Charts unified API).
|
|
53
|
+
- **iOS 15.1–16.x** — pod installs cleanly so the module can be a
|
|
54
|
+
dependency of any modern Expo app, but `<Chart />` renders an empty
|
|
55
|
+
view. The SwiftUI Charts unified API isn't available pre-17.
|
|
56
|
+
- **Android / web** — components render a transparent placeholder
|
|
57
|
+
`View` so consuming code doesn't need to feature-detect. Pair with
|
|
58
|
+
`isChartSupported()` to swap in an alternative renderer.
|
|
59
|
+
|
|
60
|
+
[Unreleased]: https://github.com/abdallaemadeldin/rn-native-ios-charts/compare/v0.1.0...HEAD
|
|
61
|
+
[0.1.0]: https://github.com/abdallaemadeldin/rn-native-ios-charts/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdalla Emadeldin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# rn-native-ios-charts
|
|
2
|
+
|
|
3
|
+
> Native SwiftUI Charts for React Native / Expo. **iOS-only.** No SVG, no
|
|
4
|
+
> Skia, no canvas approximations — every line and slice is drawn by Apple's
|
|
5
|
+
> own `Charts` framework.
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://raw.githubusercontent.com/abdallaemadeldin/rn-native-ios-charts/HEAD/docs/demo.gif" alt="rn-native-ios-charts demo" width="360" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
> [▶ Watch HD version](https://github.com/abdallaemadeldin/rn-native-ios-charts/raw/HEAD/docs/demo.mp4) — every chart type, native gradients, interactive tooltips, and pie center-label snapping.
|
|
12
|
+
|
|
13
|
+
Cross-platform RN chart libraries (Victory, Skia, gifted-charts, etc.) all
|
|
14
|
+
hit the same iOS ceilings:
|
|
15
|
+
|
|
16
|
+
- Pie / donut charts can't put a label inside the hole that actually
|
|
17
|
+
tracks the chart's plot frame.
|
|
18
|
+
- Line and area charts can't use a real `LinearGradient` for the fill —
|
|
19
|
+
they either solid-fill or fake it with `<defs><linearGradient>`.
|
|
20
|
+
- Tooltips, when they exist, are JS overlays that flicker and lag
|
|
21
|
+
behind the gesture — instead of SwiftUI's native `chartXSelection`
|
|
22
|
+
that snaps to data points with zero JS round-trips.
|
|
23
|
+
- iOS 17+ Charts features (`chartBackground`, `chartXScale`, mixed
|
|
24
|
+
marks in a single Chart, native interpolation methods, etc.) aren't
|
|
25
|
+
exposed at all.
|
|
26
|
+
|
|
27
|
+
This module is a thin Expo wrapper over SwiftUI `Charts` that exposes
|
|
28
|
+
every mark type and every modifier we've needed in production. iOS-only
|
|
29
|
+
by design — Android / web mount a no-op `<View />` so consuming code
|
|
30
|
+
doesn't need to feature-detect.
|
|
31
|
+
|
|
32
|
+
## Components
|
|
33
|
+
|
|
34
|
+
### `<Chart />` — the generic, composable view
|
|
35
|
+
|
|
36
|
+
Render any combination of marks in a single chart. This is what every
|
|
37
|
+
convenience wrapper below delegates to.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { Chart } from "rn-native-ios-charts";
|
|
41
|
+
|
|
42
|
+
<Chart
|
|
43
|
+
style={{ width: "100%", height: 240 }}
|
|
44
|
+
marks={[
|
|
45
|
+
{
|
|
46
|
+
type: "area",
|
|
47
|
+
data: yearTotals,
|
|
48
|
+
color: "#1FA92E",
|
|
49
|
+
gradient: { startOpacity: 0.35, endOpacity: 0.02 },
|
|
50
|
+
interpolation: "catmullRom",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: "line",
|
|
54
|
+
data: yearTotals,
|
|
55
|
+
color: "#1FA92E",
|
|
56
|
+
lineWidth: 2.5,
|
|
57
|
+
interpolation: "catmullRom",
|
|
58
|
+
showPoints: true,
|
|
59
|
+
symbol: "circle",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: "rule",
|
|
63
|
+
data: [],
|
|
64
|
+
ruleValue: 0,
|
|
65
|
+
color: "#9BA1A6",
|
|
66
|
+
dashArray: [4, 4],
|
|
67
|
+
},
|
|
68
|
+
]}
|
|
69
|
+
xAxis={{ hidden: false, gridLines: true }}
|
|
70
|
+
yAxis={{ hidden: false, gridLines: true }}
|
|
71
|
+
legend={{ hidden: true }}
|
|
72
|
+
animate
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Convenience wrappers
|
|
77
|
+
|
|
78
|
+
| Component | What it renders |
|
|
79
|
+
| ----------------- | --------------------------------------------------------------------- |
|
|
80
|
+
| `<PieChart />` | `sector` marks. Donut hole + optional center label slot. |
|
|
81
|
+
| `<LineChart />` | `line` mark, optionally with `area` underneath (the `area` prop). |
|
|
82
|
+
| `<AreaChart />` | `area` mark with native linear gradient fill. |
|
|
83
|
+
| `<BarChart />` | `bar` marks. Single series or multi-series via `category`. |
|
|
84
|
+
| `<ScatterChart />`| `point` marks with configurable symbol + size. |
|
|
85
|
+
| `<RangeBarChart />`| `rectangle` marks between `yStart` and `yEnd` (candles, ranges, etc).|
|
|
86
|
+
|
|
87
|
+
### Runtime helper
|
|
88
|
+
|
|
89
|
+
| Export | Returns |
|
|
90
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
91
|
+
| `isChartSupported()` | `true` only on iOS 17+. Use to mount a fallback renderer on iOS 15–16 and on Android / web. See [Feature-detecting at runtime](#feature-detecting-at-runtime). |
|
|
92
|
+
|
|
93
|
+
Example — pie with center label:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { PieChart } from "rn-native-ios-charts";
|
|
97
|
+
|
|
98
|
+
<PieChart
|
|
99
|
+
style={{ width: 240, height: 240 }}
|
|
100
|
+
data={[
|
|
101
|
+
{ label: "Cash", value: 86, color: "#1FA92E" },
|
|
102
|
+
{ label: "Stocks", value: 50, color: "#3B82F6" },
|
|
103
|
+
]}
|
|
104
|
+
innerRadius={0.62}
|
|
105
|
+
angularInset={2}
|
|
106
|
+
centerLabel={{
|
|
107
|
+
value: "$136",
|
|
108
|
+
label: "Total",
|
|
109
|
+
valueColor: "#FFFFFF",
|
|
110
|
+
labelColor: "#9BA1A6",
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The center label is rendered **inside the chart's plot frame** via
|
|
116
|
+
SwiftUI's `chartBackground` + `ChartProxy.plotFrame`, so it tracks the
|
|
117
|
+
donut's actual center and scales with the chart automatically. No JS
|
|
118
|
+
overlays, no `onLayout` tricks.
|
|
119
|
+
|
|
120
|
+
Example — gradient line:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { LineChart } from "rn-native-ios-charts";
|
|
124
|
+
|
|
125
|
+
<LineChart
|
|
126
|
+
style={{ width: "100%", height: 200 }}
|
|
127
|
+
data={[
|
|
128
|
+
{ x: "2024", y: 12000 },
|
|
129
|
+
{ x: "2025", y: 38000 },
|
|
130
|
+
{ x: "2026", y: 86000 },
|
|
131
|
+
]}
|
|
132
|
+
color="#1FA92E"
|
|
133
|
+
area={{ startOpacity: 0.35, endOpacity: 0.02 }}
|
|
134
|
+
interpolation="catmullRom"
|
|
135
|
+
showPoints
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Interactivity — native tooltips & selection
|
|
140
|
+
|
|
141
|
+
All cartesian charts (line, area, bar, point, rectangle) support
|
|
142
|
+
SwiftUI's native `chartXSelection` scrubber. Long-press + drag and
|
|
143
|
+
the tooltip follows your finger, snapping to the nearest data point.
|
|
144
|
+
No JS frame round-trips — the highlight, scrubber rule and callout
|
|
145
|
+
are all drawn inside the SwiftUI view hierarchy.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { LineChart } from "rn-native-ios-charts";
|
|
149
|
+
|
|
150
|
+
<LineChart
|
|
151
|
+
data={monthlyRevenue}
|
|
152
|
+
color="#1FA92E"
|
|
153
|
+
area={{ startOpacity: 0.35, endOpacity: 0.02 }}
|
|
154
|
+
tooltip={{
|
|
155
|
+
enabled: true,
|
|
156
|
+
valuePrefix: "$",
|
|
157
|
+
valueDecimals: 0,
|
|
158
|
+
backgroundColor: "#161618",
|
|
159
|
+
textColor: "#FFFFFF",
|
|
160
|
+
borderColor: "#2A2A2D",
|
|
161
|
+
}}
|
|
162
|
+
onSelect={(point) => {
|
|
163
|
+
if (point) console.log(`${point.x}: $${point.y}`);
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `tooltip` config
|
|
169
|
+
|
|
170
|
+
| Field | Default | Notes |
|
|
171
|
+
| ----------------- | ---------------------- | ------------------------------------------------------ |
|
|
172
|
+
| `enabled` | `false` | Opt-in — charts stay static unless you set this. |
|
|
173
|
+
| `showRule` | `true` | Dashed vertical line at the selected X. |
|
|
174
|
+
| `showDot` | `true` | Filled dot at the active point, ringed in `backgroundColor`. |
|
|
175
|
+
| `showTitle` | `true` | Show the `x` label above the value in the callout. |
|
|
176
|
+
| `backgroundColor` | system background | Callout fill. |
|
|
177
|
+
| `textColor` | system label | Callout text. |
|
|
178
|
+
| `borderColor` | system separator | Callout border + scrubber rule. |
|
|
179
|
+
| `valuePrefix` | `""` | Prepended to the y value, e.g. `"$"`. |
|
|
180
|
+
| `valueSuffix` | `""` | Appended, e.g. `"%"`. |
|
|
181
|
+
| `valueDecimals` | `0` | Decimal places. Numbers always get thousands separators. |
|
|
182
|
+
|
|
183
|
+
The callout is positioned above the active point and **auto-clamped
|
|
184
|
+
to the plot frame** — it never overflows the chart's bounds, even at
|
|
185
|
+
the leftmost / rightmost / topmost data points.
|
|
186
|
+
|
|
187
|
+
### `onSelect` event
|
|
188
|
+
|
|
189
|
+
Fires every time the selection changes (including when it clears):
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
onSelect?: (point: { x: string; y: number } | null) => void;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For pie / donut charts there's no visual callout — `onSelect` fires
|
|
196
|
+
on slice taps via `chartAngleSelection`, and the natural place to
|
|
197
|
+
display the info is the `centerLabel`:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { useState } from "react";
|
|
201
|
+
import { PieChart } from "rn-native-ios-charts";
|
|
202
|
+
|
|
203
|
+
const [center, setCenter] = useState({ value: "$148K", label: "Total" });
|
|
204
|
+
|
|
205
|
+
<PieChart
|
|
206
|
+
data={portfolio}
|
|
207
|
+
innerRadius={0.62}
|
|
208
|
+
centerLabel={{ ...center, valueColor: "#FFFFFF", labelColor: "#9BA1A6" }}
|
|
209
|
+
onSelect={(point) => {
|
|
210
|
+
setCenter(
|
|
211
|
+
point
|
|
212
|
+
? { value: `$${point.y}K`, label: point.x }
|
|
213
|
+
: { value: "$148K", label: "Total" }
|
|
214
|
+
);
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Supported marks
|
|
220
|
+
|
|
221
|
+
| Mark type | What it draws |
|
|
222
|
+
| ------------- | ------------------------------------------ |
|
|
223
|
+
| `bar` | Vertical bars |
|
|
224
|
+
| `line` | Connected line |
|
|
225
|
+
| `area` | Filled area under a line |
|
|
226
|
+
| `point` | Discrete symbols at each datum |
|
|
227
|
+
| `rectangle` | Rectangle between `yStart` and `yEnd` |
|
|
228
|
+
| `rule` | Horizontal or vertical reference line |
|
|
229
|
+
| `sector` | Pie / donut wedge (iOS 17+) |
|
|
230
|
+
|
|
231
|
+
## Supported per-mark config
|
|
232
|
+
|
|
233
|
+
- **Color:** solid `color` (any RN ColorValue) or `gradient` (linear,
|
|
234
|
+
multi-stop, custom start/end points).
|
|
235
|
+
- **Line interpolation:** `linear`, `catmullRom`, `monotone`,
|
|
236
|
+
`stepStart`, `stepEnd`, `stepCenter`.
|
|
237
|
+
- **Line stroke:** `lineWidth`, `dashArray`, `lineCap`.
|
|
238
|
+
- **Symbols:** `circle`, `square`, `triangle`, `diamond`, `pentagon`,
|
|
239
|
+
`plus`, `cross`, `asterisk`. With `symbolSize` and `showPoints`.
|
|
240
|
+
- **Bar / rectangle:** `cornerRadius`, fixed `barWidth`.
|
|
241
|
+
- **Sector:** `innerRadius`, `outerRadius`, `angularInset`,
|
|
242
|
+
`cornerRadius`.
|
|
243
|
+
- **Per-point overrides:** every datum can carry its own `color` and
|
|
244
|
+
`category` (series key for auto-coloring + legend grouping).
|
|
245
|
+
- **Opacity** per mark.
|
|
246
|
+
|
|
247
|
+
## Supported chart-level config
|
|
248
|
+
|
|
249
|
+
- `xAxis` / `yAxis`: hidden, grid lines, tick labels, label color &
|
|
250
|
+
font size, custom `[domainMin, domainMax]`.
|
|
251
|
+
- `legend`: hidden, placement (`top`, `bottom`, `leading`, `trailing`,
|
|
252
|
+
`overlay`, `automatic`).
|
|
253
|
+
- `centerLabel`: the in-plot value + caption pair, rendered inside
|
|
254
|
+
the chart's plot frame.
|
|
255
|
+
- `tooltip`: interactive scrubber tooltip with native
|
|
256
|
+
`chartXSelection` — vertical rule + dot + auto-clamped callout. See
|
|
257
|
+
[Interactivity](#interactivity--native-tooltips--selection) above.
|
|
258
|
+
- `onSelect(point)`: event fired when the user picks a point via the
|
|
259
|
+
scrubber or taps a pie sector. Payload is `{ x, y }` or `null`.
|
|
260
|
+
- `animate`: toggle SwiftUI's native ease-in-out on data changes.
|
|
261
|
+
|
|
262
|
+
Top-level utility:
|
|
263
|
+
|
|
264
|
+
- `isChartSupported()`: runtime feature-detection helper — `true` on
|
|
265
|
+
iOS 17+, `false` elsewhere. Pair with a fallback chart library on
|
|
266
|
+
older iOS or non-iOS platforms. See
|
|
267
|
+
[Feature-detecting at runtime](#feature-detecting-at-runtime).
|
|
268
|
+
|
|
269
|
+
## Platform support
|
|
270
|
+
|
|
271
|
+
- **iOS 17+** — full rendering. SwiftUI Charts unified API,
|
|
272
|
+
`chartBackground`, `SectorMark`, `chartXSelection`,
|
|
273
|
+
`chartAngleSelection`.
|
|
274
|
+
- **iOS 15.1–16.x** — the pod installs cleanly so this library can
|
|
275
|
+
be a dependency of any modern Expo app, but `<Chart />` renders an
|
|
276
|
+
empty `UIHostingController` on these versions. The SwiftUI Charts
|
|
277
|
+
unified API isn't available pre-17, so there's nothing to draw.
|
|
278
|
+
- **Other platforms** — the components render a transparent placeholder
|
|
279
|
+
`View` so consuming code doesn't need to feature-detect.
|
|
280
|
+
|
|
281
|
+
### Feature-detecting at runtime
|
|
282
|
+
|
|
283
|
+
Use `isChartSupported()` to swap in an alternative renderer
|
|
284
|
+
(`react-native-gifted-charts`, Victory, your own placeholder, etc.)
|
|
285
|
+
on iOS < 17 and on Android / web:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { isChartSupported, LineChart } from "rn-native-ios-charts";
|
|
289
|
+
import { LineChart as GiftedLine } from "react-native-gifted-charts";
|
|
290
|
+
|
|
291
|
+
export function MyChart(props) {
|
|
292
|
+
return isChartSupported()
|
|
293
|
+
? <LineChart {...props} />
|
|
294
|
+
: <GiftedLine {...mapToGiftedProps(props)} />;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The check is a single integer parse of `Platform.Version` — cheap
|
|
299
|
+
enough to call inline on every render.
|
|
300
|
+
|
|
301
|
+
## Installation
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
npm install rn-native-ios-charts
|
|
305
|
+
cd ios && pod install
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Rebuild the native app (Metro reload alone won't pick this up — it
|
|
309
|
+
ships a native module).
|
|
310
|
+
|
|
311
|
+
For local / monorepo development, place the package at
|
|
312
|
+
`modules/rn-native-ios-charts/` and reference it via the `link:`
|
|
313
|
+
protocol so edits propagate without reinstalling:
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
// package.json
|
|
317
|
+
{
|
|
318
|
+
"dependencies": {
|
|
319
|
+
"rn-native-ios-charts": "link:./modules/rn-native-ios-charts"
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Expo autolinking picks up the symlinked module on the next
|
|
325
|
+
`pod install` automatically.
|
package/docs/demo.gif
ADDED
|
Binary file
|
package/docs/demo.mp4
ADDED
|
Binary file
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
/// Per-axis config. Each field is optional; omit to use SwiftUI's
|
|
4
|
+
/// automatic behavior.
|
|
5
|
+
internal struct ChartAxisConfig: Record {
|
|
6
|
+
@Field var hidden: Bool = false
|
|
7
|
+
@Field var gridLines: Bool = true
|
|
8
|
+
@Field var tickLabels: Bool = true
|
|
9
|
+
/// Hex color string parsed by Expo's UIColor converter. `nil` =
|
|
10
|
+
/// system default.
|
|
11
|
+
@Field var labelColor: UIColor?
|
|
12
|
+
@Field var gridColor: UIColor?
|
|
13
|
+
@Field var labelFontSize: Double = 11
|
|
14
|
+
/// Explicit numeric domain. Both must be set to take effect.
|
|
15
|
+
@Field var domainMin: Double?
|
|
16
|
+
@Field var domainMax: Double?
|
|
17
|
+
|
|
18
|
+
init() {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Legend placement + visibility. SwiftUI handles auto-coloring when
|
|
22
|
+
/// `category` is set on data points.
|
|
23
|
+
internal struct ChartLegendConfig: Record {
|
|
24
|
+
@Field var hidden: Bool = false
|
|
25
|
+
/// "automatic" | "top" | "bottom" | "leading" | "trailing" | "overlay".
|
|
26
|
+
@Field var placement: String = "automatic"
|
|
27
|
+
|
|
28
|
+
init() {}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Center label drawn inside the chart's plot frame via
|
|
32
|
+
/// `chartBackground`. Most useful with `sector` marks (the donut hole)
|
|
33
|
+
/// but works on any chart.
|
|
34
|
+
internal struct ChartCenterLabel: Record {
|
|
35
|
+
@Field var value: String?
|
|
36
|
+
@Field var label: String?
|
|
37
|
+
@Field var valueColor: UIColor?
|
|
38
|
+
@Field var labelColor: UIColor?
|
|
39
|
+
@Field var valueFontSize: Double = 18
|
|
40
|
+
@Field var labelFontSize: Double = 11
|
|
41
|
+
|
|
42
|
+
init() {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Tooltip overlay shown when the user touches/drags on a cartesian
|
|
46
|
+
/// chart. Uses SwiftUI Charts' native `chartXSelection`, so the
|
|
47
|
+
/// scrubber snaps to data points automatically. For `sector` marks
|
|
48
|
+
/// (pie / donut), `chartAngleSelection` fires the `onSelect` event
|
|
49
|
+
/// but no visual callout is drawn — use the event to update a
|
|
50
|
+
/// `centerLabel` or your own JS overlay.
|
|
51
|
+
internal struct ChartTooltipConfig: Record {
|
|
52
|
+
@Field var enabled: Bool = false
|
|
53
|
+
/// Draw a vertical rule at the selected X.
|
|
54
|
+
@Field var showRule: Bool = true
|
|
55
|
+
/// Highlight the active point with a filled dot.
|
|
56
|
+
@Field var showDot: Bool = true
|
|
57
|
+
/// Show the x label above the y value in the callout.
|
|
58
|
+
@Field var showTitle: Bool = true
|
|
59
|
+
@Field var backgroundColor: UIColor?
|
|
60
|
+
@Field var textColor: UIColor?
|
|
61
|
+
@Field var borderColor: UIColor?
|
|
62
|
+
/// Decimal places for the y value when formatting. Default 0.
|
|
63
|
+
@Field var valueDecimals: Int = 0
|
|
64
|
+
/// Optional prefix (eg "$") prepended to the y value.
|
|
65
|
+
@Field var valuePrefix: String = ""
|
|
66
|
+
/// Optional suffix (eg "%") appended to the y value.
|
|
67
|
+
@Field var valueSuffix: String = ""
|
|
68
|
+
|
|
69
|
+
init() {}
|
|
70
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
/// One datum on any chart. The shape is intentionally permissive so a
|
|
4
|
+
/// single struct serves bar, line, area, point, rectangle, rule and
|
|
5
|
+
/// sector marks.
|
|
6
|
+
///
|
|
7
|
+
/// - `x` / `y`: primary coordinate. `x` is stringly-typed because
|
|
8
|
+
/// SwiftUI Charts categorical axes want strings; if your data is
|
|
9
|
+
/// numeric, stringify it on the JS side (`String(year)`).
|
|
10
|
+
/// - `yEnd`: optional second value — used by range bar / rectangle
|
|
11
|
+
/// marks (a stacked low/high pair).
|
|
12
|
+
/// - `category`: optional grouping key. When present, the chart
|
|
13
|
+
/// colors by `.value("Category", category)` so SwiftUI assigns a
|
|
14
|
+
/// consistent color per series across the legend.
|
|
15
|
+
/// - `color`: per-point override. Wins over the mark-level color
|
|
16
|
+
/// when both are set.
|
|
17
|
+
internal struct ChartDataPoint: Record {
|
|
18
|
+
@Field var x: String = ""
|
|
19
|
+
@Field var y: Double = 0
|
|
20
|
+
@Field var yEnd: Double?
|
|
21
|
+
@Field var category: String?
|
|
22
|
+
@Field var color: UIColor?
|
|
23
|
+
|
|
24
|
+
init() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
/// One stop in a multi-stop gradient.
|
|
4
|
+
internal struct ChartGradientStop: Record {
|
|
5
|
+
/// Position along the gradient axis, 0–1.
|
|
6
|
+
@Field var offset: Double = 0
|
|
7
|
+
@Field var color: UIColor?
|
|
8
|
+
/// 0 = transparent, 1 = opaque. Multiplied with the stop's color alpha.
|
|
9
|
+
@Field var opacity: Double = 1.0
|
|
10
|
+
|
|
11
|
+
init() {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Gradient fill applied to a mark's `foregroundStyle`. Two-stop
|
|
15
|
+
/// shorthand (`startOpacity` + `endOpacity`) is the common case; for
|
|
16
|
+
/// fancier multi-stop gradients use `stops`.
|
|
17
|
+
internal struct ChartGradient: Record {
|
|
18
|
+
/// "linear" | "radial". Default linear.
|
|
19
|
+
@Field var kind: String = "linear"
|
|
20
|
+
/// Linear gradient start point in unit coords. Defaults to .top.
|
|
21
|
+
@Field var startX: Double = 0.5
|
|
22
|
+
@Field var startY: Double = 0
|
|
23
|
+
/// Linear gradient end point in unit coords. Defaults to .bottom.
|
|
24
|
+
@Field var endX: Double = 0.5
|
|
25
|
+
@Field var endY: Double = 1
|
|
26
|
+
/// Two-stop shorthand — used when `stops` is empty.
|
|
27
|
+
@Field var startOpacity: Double = 0.35
|
|
28
|
+
@Field var endOpacity: Double = 0.02
|
|
29
|
+
/// Explicit stops. Overrides `startOpacity` / `endOpacity` when set.
|
|
30
|
+
@Field var stops: [ChartGradientStop] = []
|
|
31
|
+
|
|
32
|
+
init() {}
|
|
33
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
|
|
3
|
+
/// One mark on the chart. `type` discriminates between bar / line /
|
|
4
|
+
/// area / point / rectangle / rule / sector — Swift switches on it to
|
|
5
|
+
/// render the appropriate SwiftUI Mark.
|
|
6
|
+
///
|
|
7
|
+
/// All optional fields default to sensible no-ops so consumers only
|
|
8
|
+
/// pass what they care about.
|
|
9
|
+
internal struct ChartMark: Record {
|
|
10
|
+
/// Discriminator. One of: "bar", "line", "area", "point",
|
|
11
|
+
/// "rectangle", "rule", "sector".
|
|
12
|
+
@Field var type: String = "line"
|
|
13
|
+
@Field var data: [ChartDataPoint] = []
|
|
14
|
+
|
|
15
|
+
// ─── Color / fill ───
|
|
16
|
+
@Field var color: UIColor?
|
|
17
|
+
@Field var opacity: Double = 1.0
|
|
18
|
+
/// Optional gradient — wins over solid `color` when set. Most
|
|
19
|
+
/// commonly used on AreaMark for the "fill under the curve" look.
|
|
20
|
+
@Field var gradient: ChartGradient?
|
|
21
|
+
|
|
22
|
+
// ─── Stroke (line, rule) ───
|
|
23
|
+
@Field var lineWidth: Double = 2.0
|
|
24
|
+
/// Dash pattern in points. Empty array = solid line.
|
|
25
|
+
@Field var dashArray: [Double] = []
|
|
26
|
+
/// "butt" | "round" | "square". Default "round".
|
|
27
|
+
@Field var lineCap: String = "round"
|
|
28
|
+
|
|
29
|
+
// ─── Line / area interpolation ───
|
|
30
|
+
/// One of: "linear", "catmullRom", "monotone", "stepStart",
|
|
31
|
+
/// "stepEnd", "stepCenter". Default "linear".
|
|
32
|
+
@Field var interpolation: String = "linear"
|
|
33
|
+
|
|
34
|
+
// ─── Symbols (point marks, line w/ points) ───
|
|
35
|
+
/// One of: "circle", "square", "triangle", "diamond", "pentagon",
|
|
36
|
+
/// "plus", "cross", "asterisk".
|
|
37
|
+
@Field var symbol: String = "circle"
|
|
38
|
+
@Field var symbolSize: Double = 36
|
|
39
|
+
/// When true on a `line` mark, draw point symbols at each datum.
|
|
40
|
+
@Field var showPoints: Bool = false
|
|
41
|
+
|
|
42
|
+
// ─── Bar / rectangle ───
|
|
43
|
+
@Field var cornerRadius: Double = 0
|
|
44
|
+
/// Bar width (pt). 0 = auto.
|
|
45
|
+
@Field var barWidth: Double = 0
|
|
46
|
+
|
|
47
|
+
// ─── Sector (pie / donut) ───
|
|
48
|
+
/// Inner radius as a ratio of outer, 0–1. 0 = full pie, 0.62 = thin donut.
|
|
49
|
+
@Field var innerRadius: Double = 0
|
|
50
|
+
/// Outer radius as a ratio of available space, 0–1. 0 = auto.
|
|
51
|
+
@Field var outerRadius: Double = 0
|
|
52
|
+
/// Gap between adjacent sectors, pt.
|
|
53
|
+
@Field var angularInset: Double = 0
|
|
54
|
+
|
|
55
|
+
// ─── Rule mark orientation ───
|
|
56
|
+
/// "horizontal" | "vertical". A horizontal rule draws a constant-y
|
|
57
|
+
/// reference line across the chart's x extent (and vice-versa).
|
|
58
|
+
@Field var orientation: String = "horizontal"
|
|
59
|
+
/// Constant value the rule is drawn at (interpretation depends on
|
|
60
|
+
/// orientation). When set, `data` can be empty.
|
|
61
|
+
@Field var ruleValue: Double?
|
|
62
|
+
|
|
63
|
+
init() {}
|
|
64
|
+
}
|