From 1e7d1fe9197c2c70073baa6700717e8d8b8b6700 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Fri, 13 Feb 2026 22:39:22 +0100 Subject: [PATCH 01/10] feat: add chart component --- .../main/java/voltra/generated/ShortNames.kt | 6 + data/components.json | 52 +- .../app/testing-grounds/chart-playground.tsx | 5 + .../testing-grounds/TestingGroundsScreen.tsx | 7 + .../ChartPlaygroundScreen.tsx | 289 +++++++++ ios/shared/ComponentTypeID.swift | 4 + ios/shared/ShortNames.swift | 6 + ios/shared/VoltraNode.swift | 7 + .../Parameters/ChartParameters.swift | 30 + ios/ui/Views/VoltraChart.swift | 605 ++++++++++++++++++ src/jsx/AreaMark.tsx | 14 + src/jsx/BarMark.tsx | 16 + src/jsx/Chart.tsx | 116 ++++ src/jsx/LineMark.tsx | 15 + src/jsx/PointMark.tsx | 14 + src/jsx/RuleMark.tsx | 13 + src/jsx/SectorMark.tsx | 15 + src/jsx/chart-types.ts | 2 + src/jsx/primitives.ts | 8 + src/jsx/props/Chart.ts | 20 + src/payload/component-ids.ts | 2 + src/payload/short-names.ts | 12 + website/docs/ios/_meta.json | 5 + website/docs/ios/charts.md | 335 ++++++++++ 24 files changed, 1597 insertions(+), 1 deletion(-) create mode 100644 example/app/testing-grounds/chart-playground.tsx create mode 100644 example/screens/testing-grounds/chart-playground/ChartPlaygroundScreen.tsx create mode 100644 ios/ui/Generated/Parameters/ChartParameters.swift create mode 100644 ios/ui/Views/VoltraChart.swift create mode 100644 src/jsx/AreaMark.tsx create mode 100644 src/jsx/BarMark.tsx create mode 100644 src/jsx/Chart.tsx create mode 100644 src/jsx/LineMark.tsx create mode 100644 src/jsx/PointMark.tsx create mode 100644 src/jsx/RuleMark.tsx create mode 100644 src/jsx/SectorMark.tsx create mode 100644 src/jsx/chart-types.ts create mode 100644 src/jsx/props/Chart.ts create mode 100644 website/docs/ios/charts.md diff --git a/android/src/main/java/voltra/generated/ShortNames.kt b/android/src/main/java/voltra/generated/ShortNames.kt index e883a51e..c4caf836 100644 --- a/android/src/main/java/voltra/generated/ShortNames.kt +++ b/android/src/main/java/voltra/generated/ShortNames.kt @@ -31,6 +31,7 @@ object ShortNames { "bw" to "borderWidth", "b" to "bottom", "bs" to "buttonStyle", + "csa" to "chartScrollableAxes", "chk" to "checked", "clip" to "clipped", "c" to "color", @@ -67,6 +68,7 @@ object ShortNames { "fvar" to "fontVariant", "fw" to "fontWeight", "fgs" to "foregroundStyle", + "fss" to "foregroundStyleScale", "f" to "frame", "g" to "gap", "gs" to "gaugeStyle", @@ -83,6 +85,7 @@ object ShortNames { "ly" to "layout", "lp" to "layoutPriority", "l" to "left", + "lgv" to "legendVisibility", "ls" to "letterSpacing", "lh" to "lineHeight", "ll" to "lineLimit", @@ -95,6 +98,7 @@ object ShortNames { "mr" to "marginRight", "mt" to "marginTop", "mv" to "marginVertical", + "mrk" to "marks", "me" to "maskElement", "maxh" to "maxHeight", "max" to "maximumValue", @@ -163,6 +167,8 @@ object ShortNames { "valig" to "verticalAlignment", "wt" to "weight", "w" to "width", + "xav" to "xAxisVisibility", + "yav" to "yAxisVisibility", "zi" to "zIndex", ) diff --git a/data/components.json b/data/components.json index 23f27238..403ac77c 100644 --- a/data/components.json +++ b/data/components.json @@ -149,7 +149,13 @@ "scaleEffect": "sce", "rotationEffect": "re", "border": "bd", - "clipped": "clip" + "clipped": "clip", + "chartScrollableAxes": "csa", + "foregroundStyleScale": "fss", + "legendVisibility": "lgv", + "marks": "mrk", + "xAxisVisibility": "xav", + "yAxisVisibility": "yav" }, "styleProperties": [ "padding", @@ -1238,6 +1244,50 @@ "swiftAvailability": "iOS 16.0, macOS 13.0", "hasChildren": true, "parameters": {} + }, + { + "name": "Chart", + "description": "SwiftUI Charts component for data visualization", + "swiftAvailability": "iOS 16.0, macOS 13.0", + "hasChildren": true, + "parameters": { + "marks": { + "type": "array", + "optional": true, + "jsonEncoded": true, + "description": "Compact mark data encoded from children by toJSON" + }, + "xAxisVisibility": { + "type": "string", + "optional": true, + "enum": ["automatic", "visible", "hidden"], + "description": "Show or hide the x-axis" + }, + "yAxisVisibility": { + "type": "string", + "optional": true, + "enum": ["automatic", "visible", "hidden"], + "description": "Show or hide the y-axis" + }, + "legendVisibility": { + "type": "string", + "optional": true, + "enum": ["automatic", "visible", "hidden"], + "description": "Show or hide the chart legend" + }, + "foregroundStyleScale": { + "type": "object", + "optional": true, + "jsonEncoded": true, + "description": "Map of series name to color string" + }, + "chartScrollableAxes": { + "type": "string", + "optional": true, + "enum": ["horizontal", "vertical"], + "description": "Enable scrolling on the given axis" + } + } } ] } diff --git a/example/app/testing-grounds/chart-playground.tsx b/example/app/testing-grounds/chart-playground.tsx new file mode 100644 index 00000000..03d633af --- /dev/null +++ b/example/app/testing-grounds/chart-playground.tsx @@ -0,0 +1,5 @@ +import ChartPlaygroundScreen from '~/screens/testing-grounds/chart-playground/ChartPlaygroundScreen' + +export default function ChartPlaygroundIndex() { + return +} diff --git a/example/screens/testing-grounds/TestingGroundsScreen.tsx b/example/screens/testing-grounds/TestingGroundsScreen.tsx index 9e9ecba7..d8a8a058 100644 --- a/example/screens/testing-grounds/TestingGroundsScreen.tsx +++ b/example/screens/testing-grounds/TestingGroundsScreen.tsx @@ -55,6 +55,13 @@ const TESTING_GROUNDS_SECTIONS = [ 'Interactive playground for experimenting with flex layout properties. Test alignItems, justifyContent, flexDirection, spacing, and padding with live visual feedback.', route: '/testing-grounds/flex-playground', }, + { + id: 'chart-playground', + title: 'Chart Playground', + description: + 'Explore all SwiftUI chart mark types: BarMark, LineMark, AreaMark, PointMark, RuleMark, and SectorMark. Randomize data to see animated transitions.', + route: '/testing-grounds/chart-playground', + }, { id: 'image-preloading', title: 'Image Preloading', diff --git a/example/screens/testing-grounds/chart-playground/ChartPlaygroundScreen.tsx b/example/screens/testing-grounds/chart-playground/ChartPlaygroundScreen.tsx new file mode 100644 index 00000000..402cc659 --- /dev/null +++ b/example/screens/testing-grounds/chart-playground/ChartPlaygroundScreen.tsx @@ -0,0 +1,289 @@ +import { Link } from 'expo-router' +import React, { useState, useCallback } from 'react' +import { ScrollView, StyleSheet, Text, View } from 'react-native' +import { Voltra } from 'voltra' +import { VoltraView } from 'voltra/client' + +import { Button } from '~/components/Button' +import { Card } from '~/components/Card' + +// ─── data helpers ─────────────────────────────────────────────────────────── + +const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'] + +const randomValue = (min: number, max: number) => Math.round(Math.random() * (max - min) + min) + +const randomBarData = () => MONTHS.map((m) => ({ x: m, y: randomValue(20, 120) })) + +const randomMultiSeriesData = () => [ + ...MONTHS.map((m) => ({ x: m, y: randomValue(20, 100), series: 'A' })), + ...MONTHS.map((m) => ({ x: m, y: randomValue(20, 100), series: 'B' })), +] + +const randomLineData = () => MONTHS.map((m) => ({ x: m, y: randomValue(30, 100) })) + +const randomAreaData = () => MONTHS.map((m) => ({ x: m, y: randomValue(10, 90) })) + +const randomPointData = () => Array.from({ length: 12 }, (_, i) => ({ x: randomValue(0, 100), y: randomValue(0, 100) })) + +const randomSectorData = () => { + const raw = [ + { category: 'Work', value: randomValue(20, 50) }, + { category: 'Sleep', value: randomValue(20, 40) }, + { category: 'Leisure', value: randomValue(10, 30) }, + { category: 'Exercise', value: randomValue(5, 20) }, + ] + return raw +} + +const randomRuleY = () => randomValue(30, 80) + +// ─── chart preview wrapper ─────────────────────────────────────────────────── + +function ChartPreview({ children }: { children: React.ReactNode }) { + return {children} +} + +// ─── screen ───────────────────────────────────────────────────────────────── + +export default function ChartPlaygroundScreen() { + const [barData, setBarData] = useState(randomBarData) + const [multiData, setMultiData] = useState(randomMultiSeriesData) + const [lineData, setLineData] = useState(randomLineData) + const [areaData, setAreaData] = useState(randomAreaData) + const [pointData, setPointData] = useState(randomPointData) + const [sectorData, setSectorData] = useState(randomSectorData) + const [ruleY, setRuleY] = useState(randomRuleY) + const [comboBarData, setComboBarData] = useState(randomBarData) + const [comboLineData, setComboLineData] = useState(randomLineData) + + const randomizeAll = useCallback(() => { + setBarData(randomBarData()) + setMultiData(randomMultiSeriesData()) + setLineData(randomLineData()) + setAreaData(randomAreaData()) + setPointData(randomPointData()) + setSectorData(randomSectorData()) + setRuleY(randomRuleY()) + setComboBarData(randomBarData()) + setComboLineData(randomLineData()) + }, []) + + return ( + + + Chart Playground + + All SwiftUI chart mark types powered by Voltra. Tap Randomize to animate between data sets. + + + +