diff --git a/example/app/testing-grounds/gradient-playground.tsx b/example/app/testing-grounds/gradient-playground.tsx new file mode 100644 index 0000000..6800ab3 --- /dev/null +++ b/example/app/testing-grounds/gradient-playground.tsx @@ -0,0 +1,5 @@ +import GradientPlaygroundScreen from '~/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen' + +export default function GradientPlaygroundIndex() { + return +} diff --git a/example/screens/testing-grounds/TestingGroundsScreen.tsx b/example/screens/testing-grounds/TestingGroundsScreen.tsx index 9e9ecba..4e4b369 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: 'gradient-playground', + title: 'Gradient Playground', + description: + 'Test CSS gradient strings as backgroundColor. Experiment with linear, radial, and conic gradients, direction/angle controls, color presets, stop positions, and borderRadius clipping.', + route: '/testing-grounds/gradient-playground', + }, { id: 'image-preloading', title: 'Image Preloading', diff --git a/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx b/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx new file mode 100644 index 0000000..7702c6f --- /dev/null +++ b/example/screens/testing-grounds/gradient-playground/GradientPlaygroundScreen.tsx @@ -0,0 +1,291 @@ +import { Link } from 'expo-router' +import React, { useState } 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' + +type GradientType = 'linear' | 'radial' | 'conic' +type Direction = 'to right' | 'to bottom' | 'to bottom right' | 'to top right' + +const GRADIENT_TYPES: GradientType[] = ['linear', 'radial', 'conic'] +const DIRECTIONS: Direction[] = ['to right', 'to bottom', 'to bottom right', 'to top right'] +const DIRECTION_LABELS: Record = { + 'to right': 'to right', + 'to bottom': 'to bottom', + 'to bottom right': 'to bottom right', + 'to top right': 'to top right', +} + +const PRESETS: Array<{ label: string; colors: [string, string, ...string[]] }> = [ + { label: 'Sunset', colors: ['#FF6B6B', '#FFD93D'] }, + { label: 'Ocean', colors: ['#0093E9', '#80D0C7'] }, + { label: 'Purple', colors: ['#8B5CF6', '#EC4899'] }, + { label: 'Tri-color', colors: ['#EF4444', '#10B981', '#3B82F6'] }, +] + +const ANGLE_OPTIONS = [0, 45, 90, 135, 180] + +export default function GradientPlaygroundScreen() { + const [gradientType, setGradientType] = useState('linear') + const [direction, setDirection] = useState('to right') + const [angle, setAngle] = useState(90) + const [useAngle, setUseAngle] = useState(false) + const [preset, setPreset] = useState(0) + const [borderRadius, setBorderRadius] = useState(12) + + const colors = PRESETS[preset].colors + const positionedStops = colors + .map((color, idx) => { + const pct = colors.length === 1 ? 0 : Math.round((idx / (colors.length - 1)) * 100) + return `${color} ${pct}%` + }) + .join(', ') + + const buildGradient = (): string => { + if (gradientType === 'radial') { + return `radial-gradient(circle at center, ${positionedStops})` + } + if (gradientType === 'conic') { + return `conic-gradient(from ${angle}deg at center, ${positionedStops})` + } + // linear + const dir = useAngle ? `${angle}deg` : direction + return `linear-gradient(${dir}, ${positionedStops})` + } + + const gradient = buildGradient() + + const cycleGradientType = () => { + const i = GRADIENT_TYPES.indexOf(gradientType) + setGradientType(GRADIENT_TYPES[(i + 1) % GRADIENT_TYPES.length]) + } + + const cycleDirection = () => { + const i = DIRECTIONS.indexOf(direction) + setDirection(DIRECTIONS[(i + 1) % DIRECTIONS.length]) + } + + const cycleAngle = () => { + const i = ANGLE_OPTIONS.indexOf(angle) + setAngle(ANGLE_OPTIONS[(i + 1) % ANGLE_OPTIONS.length]) + } + + const cyclePreset = () => { + setPreset((prev) => (prev + 1) % PRESETS.length) + } + + const increaseBorderRadius = () => setBorderRadius((prev) => Math.min(prev + 8, 80)) + const decreaseBorderRadius = () => setBorderRadius((prev) => Math.max(prev - 8, 0)) + + return ( + + + Gradient Playground + Test CSS gradient strings as backgroundColor on Voltra views. + + Playground uses parser-compatible CSS syntax only. If a preview is blank, this indicates a gradient parser bug + in iOS rendering. + + + {/* Controls */} + + Controls + + {/* Gradient Type */} + + Type: +