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 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,6 @@
1
+ {
2
+ "platforms": ["ios"],
3
+ "ios": {
4
+ "modules": ["NativeIosChartsModule"]
5
+ }
6
+ }
@@ -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
+ }