diff --git a/examples/example-react-native/android/app/src/main/assets/custom/counter.riv b/examples/example-react-native/android/app/src/main/assets/custom/expressionui.riv similarity index 67% rename from examples/example-react-native/android/app/src/main/assets/custom/counter.riv rename to examples/example-react-native/android/app/src/main/assets/custom/expressionui.riv index 6e56fd1..6eea665 100644 Binary files a/examples/example-react-native/android/app/src/main/assets/custom/counter.riv and b/examples/example-react-native/android/app/src/main/assets/custom/expressionui.riv differ diff --git a/examples/example-react-native/android/link-assets-manifest.json b/examples/example-react-native/android/link-assets-manifest.json index be42b75..ffb9ae5 100644 --- a/examples/example-react-native/android/link-assets-manifest.json +++ b/examples/example-react-native/android/link-assets-manifest.json @@ -2,8 +2,8 @@ "migIndex": 1, "data": [ { - "path": "assets/counter.riv", - "sha1": "d846439f9b15d0a995e04f743e96c3e11345bf1a" + "path": "assets/expressionui.riv", + "sha1": "dbb99a4f792c3518e6d2bf4f9dd7d0f61ab27859" } ] } diff --git a/examples/example-react-native/assets/counter.riv b/examples/example-react-native/assets/expressionui.riv similarity index 67% rename from examples/example-react-native/assets/counter.riv rename to examples/example-react-native/assets/expressionui.riv index 6e56fd1..6eea665 100644 Binary files a/examples/example-react-native/assets/counter.riv and b/examples/example-react-native/assets/expressionui.riv differ diff --git a/examples/example-react-native/ios/ExampleReactNative.xcodeproj/project.pbxproj b/examples/example-react-native/ios/ExampleReactNative.xcodeproj/project.pbxproj index 3225742..1a6b154 100644 --- a/examples/example-react-native/ios/ExampleReactNative.xcodeproj/project.pbxproj +++ b/examples/example-react-native/ios/ExampleReactNative.xcodeproj/project.pbxproj @@ -14,7 +14,7 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 7699B88040F8A987B510C191 /* libPods-ExampleReactNative-ExampleReactNativeTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-ExampleReactNative-ExampleReactNativeTests.a */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - 89BA4A696B2743C09D86F34E /* counter.riv in Resources */ = {isa = PBXBuildFile; fileRef = A308EA2B6BC7477E81DF25C0 /* counter.riv */; }; + 7D3D7B5BBB3D4FC3BB50EBEE /* expressionui.riv in Resources */ = {isa = PBXBuildFile; fileRef = 50F771C16362441F8570CA26 /* expressionui.riv */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -44,8 +44,8 @@ 5DCACB8F33CDC322A6C60F78 /* libPods-ExampleReactNative.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ExampleReactNative.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ExampleReactNative/LaunchScreen.storyboard; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-ExampleReactNative-ExampleReactNativeTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExampleReactNative-ExampleReactNativeTests.release.xcconfig"; path = "Target Support Files/Pods-ExampleReactNative-ExampleReactNativeTests/Pods-ExampleReactNative-ExampleReactNativeTests.release.xcconfig"; sourceTree = ""; }; - A308EA2B6BC7477E81DF25C0 /* counter.riv */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = undefined; includeInIndex = 0; lastKnownFileType = unknown; name = counter.riv; path = ../assets/counter.riv; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; + 50F771C16362441F8570CA26 /* expressionui.riv */ = {isa = PBXFileReference; name = "expressionui.riv"; path = "../assets/expressionui.riv"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -154,7 +154,7 @@ E8878CE30645494A985B7B44 /* Resources */ = { isa = PBXGroup; children = ( - A308EA2B6BC7477E81DF25C0 /* counter.riv */, + 50F771C16362441F8570CA26 /* expressionui.riv */, ); name = Resources; path = ""; @@ -255,7 +255,7 @@ files = ( 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 89BA4A696B2743C09D86F34E /* counter.riv in Resources */, + 7D3D7B5BBB3D4FC3BB50EBEE /* expressionui.riv in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/examples/example-react-native/ios/link-assets-manifest.json b/examples/example-react-native/ios/link-assets-manifest.json index be42b75..ffb9ae5 100644 --- a/examples/example-react-native/ios/link-assets-manifest.json +++ b/examples/example-react-native/ios/link-assets-manifest.json @@ -2,8 +2,8 @@ "migIndex": 1, "data": [ { - "path": "assets/counter.riv", - "sha1": "d846439f9b15d0a995e04f743e96c3e11345bf1a" + "path": "assets/expressionui.riv", + "sha1": "dbb99a4f792c3518e6d2bf4f9dd7d0f61ab27859" } ] } diff --git a/examples/example-react-native/src/App.tsx b/examples/example-react-native/src/App.tsx index 8c99fda..95630ef 100644 --- a/examples/example-react-native/src/App.tsx +++ b/examples/example-react-native/src/App.tsx @@ -1,8 +1,12 @@ import React from 'react'; -import Intro from './Intro'; import {NavigationContainer} from '@react-navigation/native'; import {createNativeStackNavigator} from '@react-navigation/native-stack'; -import Counter from './Counter'; + +import StartupScreen from './screens/StartupScreen'; +import CrossFrameworkScreen from './screens/CrossFrameworkScreen'; +import DeveloperFriendlyScreen from './screens/DeveloperFriendlyScreen'; +import PixelPerfectionScreen from './screens/PixelPerfectionScreen'; +import ComposableScreen from './screens/ComposableScreen'; import type {NativeStackNavigationOptions} from '@react-navigation/native-stack'; @@ -11,8 +15,11 @@ const options: NativeStackNavigationOptions = { }; export type StackParamList = { - Intro: undefined; - Counter: undefined; + Startup: undefined; + CrossFramework: undefined; + DeveloperFriendly: undefined; + PixelPerfection: undefined; + Composable: undefined; }; const Stack = createNativeStackNavigator(); @@ -20,9 +27,18 @@ const Stack = createNativeStackNavigator(); export default function App() { return ( - - - + + + + + + ); diff --git a/examples/example-react-native/src/Counter/index.tsx b/examples/example-react-native/src/Counter/index.tsx deleted file mode 100644 index a023f31..0000000 --- a/examples/example-react-native/src/Counter/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import Rive, {Fit, RiveRef} from 'rive-react-native'; - -export default function Counter(): React.JSX.Element { - const riveRef = React.useRef(null); - let counter = 0; - - function handleCountIncrement(): void { - counter++; - riveRef.current?.setTextRunValue('Counter', `${counter}`); - } - - return ( - handleCountIncrement()} - /> - ); -} diff --git a/examples/example-react-native/src/Intro/index.tsx b/examples/example-react-native/src/Intro/index.tsx deleted file mode 100644 index c4a7ca8..0000000 --- a/examples/example-react-native/src/Intro/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import {NativeStackScreenProps} from '@react-navigation/native-stack'; -import React from 'react'; -import Rive, {Fit} from 'rive-react-native'; -import {StackParamList} from '../App'; - -type Props = NativeStackScreenProps; - -export default function Intro({navigation}: Props): React.JSX.Element { - return ( - navigation.navigate('Counter')} - /> - ); -} diff --git a/examples/example-react-native/src/components/ExpressionView.tsx b/examples/example-react-native/src/components/ExpressionView.tsx new file mode 100644 index 0000000..9844793 --- /dev/null +++ b/examples/example-react-native/src/components/ExpressionView.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import {Fit} from 'rive-react-native'; +import {ViewStyle} from 'react-native'; + +import StateEvent from '../expression-ui/state-event'; +import BaseExpressionView from '../expression-ui/BaseExpressionView'; +import {EXPRESSION_UI_RIVE_RESOURCE} from '../constants/expression_constants'; + +interface ExpressionViewProps { + artboardName: string; + onEvent?: (event: StateEvent) => void; + scrollable?: boolean; + stateMachineName?: string; + fit?: Fit; + style?: ViewStyle; +} + +const ExpressionView: React.FC = ({ + artboardName, + onEvent, + scrollable, + stateMachineName, + fit, + style, +}) => { + return ( + + ); +}; + +export default ExpressionView; diff --git a/examples/example-react-native/src/constants/expression_constants.ts b/examples/example-react-native/src/constants/expression_constants.ts new file mode 100644 index 0000000..25da522 --- /dev/null +++ b/examples/example-react-native/src/constants/expression_constants.ts @@ -0,0 +1,8 @@ +export const EXPRESSION_UI_RIVE_RESOURCE = 'expressionui'; + +export const INTRO_VIEW = 'intro-view'; +export const CROSS_FRAMEWORK_VIEW = 'cross-framework-view'; +export const DEVELOPER_FRIENDLY_VIEW = 'developer-friendly-view'; +export const PIXEL_PERFECTION_VIEW = 'pixel-perfection-view'; +export const COMPOSABLE_VIEW = 'composable-view'; +export const THEME_VIEW = 'theme-view'; diff --git a/examples/example-react-native/src/expression-ui/BaseExpressionView.tsx b/examples/example-react-native/src/expression-ui/BaseExpressionView.tsx new file mode 100644 index 0000000..33f45b4 --- /dev/null +++ b/examples/example-react-native/src/expression-ui/BaseExpressionView.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import Rive, {Alignment, Fit, RiveRef} from 'rive-react-native'; +import {ScrollView, StyleSheet, ViewStyle} from 'react-native'; + +import StateEvent from './state-event'; + +interface BaseExpressionViewProps { + resourceName: string; + artboardName: string; + onEvent?: (event: StateEvent, riveRef: RiveRef | null) => void; + scrollable?: boolean; + textValues?: Record; + stateMachineName?: string; + fit?: Fit; + style?: ViewStyle; +} + +const BaseExpressionView: React.FC = ({ + resourceName, + artboardName, + onEvent, + scrollable = false, + textValues = {}, + stateMachineName, + fit = Fit.Cover, + style, +}) => { + const riveRef = React.useRef(null); + + React.useEffect(() => { + if (riveRef.current) { + for (const [key, value] of Object.entries(textValues)) { + riveRef.current.setTextRunValue(key, value); + } + } + }, [riveRef, textValues]); + + const riveView = ( + + onEvent?.(new StateEvent(event.name), riveRef.current) + } + style={Object.assign(style ?? {}, scrollable ? styles.animation : {})} + /> + ); + + return scrollable ? ( + {riveView} + ) : ( + riveView + ); +}; + +const styles = StyleSheet.create({ + container: { + flexGrow: 1, + alignItems: 'center', + justifyContent: 'center', + }, + animation: { + width: '100%', + }, +}); + +export default BaseExpressionView; diff --git a/examples/example-react-native/src/expression-ui/state-event.tsx b/examples/example-react-native/src/expression-ui/state-event.tsx new file mode 100644 index 0000000..f30471d --- /dev/null +++ b/examples/example-react-native/src/expression-ui/state-event.tsx @@ -0,0 +1,3 @@ +export default class StateEvent { + constructor(public readonly name: string) {} +} diff --git a/examples/example-react-native/src/screens/ComposableScreen/index.tsx b/examples/example-react-native/src/screens/ComposableScreen/index.tsx new file mode 100644 index 0000000..7ea5ab9 --- /dev/null +++ b/examples/example-react-native/src/screens/ComposableScreen/index.tsx @@ -0,0 +1,8 @@ +import React from 'react'; + +import {COMPOSABLE_VIEW} from '../../constants/expression_constants'; +import ExpressionView from '../../components/ExpressionView'; + +export default function ComposableScreen(): React.JSX.Element { + return ; +} diff --git a/examples/example-react-native/src/screens/CrossFrameworkScreen/index.tsx b/examples/example-react-native/src/screens/CrossFrameworkScreen/index.tsx new file mode 100644 index 0000000..8767ae7 --- /dev/null +++ b/examples/example-react-native/src/screens/CrossFrameworkScreen/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {NativeStackScreenProps} from '@react-navigation/native-stack'; + +import {StackParamList} from '../../App'; +import {CROSS_FRAMEWORK_VIEW} from '../../constants/expression_constants'; +import ExpressionView from '../../components/ExpressionView'; + +type Props = NativeStackScreenProps; + +export default function CrossFrameworkScreen({ + navigation, +}: Props): React.JSX.Element { + return ( + navigation.push('DeveloperFriendly')} + /> + ); +} diff --git a/examples/example-react-native/src/screens/DeveloperFriendlyScreen/index.tsx b/examples/example-react-native/src/screens/DeveloperFriendlyScreen/index.tsx new file mode 100644 index 0000000..4cd5a5a --- /dev/null +++ b/examples/example-react-native/src/screens/DeveloperFriendlyScreen/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {NativeStackScreenProps} from '@react-navigation/native-stack'; + +import {StackParamList} from '../../App'; +import {DEVELOPER_FRIENDLY_VIEW} from '../../constants/expression_constants'; +import ExpressionView from '../../components/ExpressionView'; + +type Props = NativeStackScreenProps; + +export default function DeveloperFriendlyScreen({ + navigation, +}: Props): React.JSX.Element { + return ( + navigation.push('PixelPerfection')} + /> + ); +} diff --git a/examples/example-react-native/src/screens/PixelPerfectionScreen/index.tsx b/examples/example-react-native/src/screens/PixelPerfectionScreen/index.tsx new file mode 100644 index 0000000..2215915 --- /dev/null +++ b/examples/example-react-native/src/screens/PixelPerfectionScreen/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import {NativeStackScreenProps} from '@react-navigation/native-stack'; + +import {StackParamList} from '../../App'; +import {PIXEL_PERFECTION_VIEW} from '../../constants/expression_constants'; +import ExpressionView from '../../components/ExpressionView'; + +type Props = NativeStackScreenProps; + +export default function PixelPerfectionScreen({ + navigation, +}: Props): React.JSX.Element { + return ( + navigation.push('Composable')} + /> + ); +} diff --git a/examples/example-react-native/src/screens/StartupScreen/index.tsx b/examples/example-react-native/src/screens/StartupScreen/index.tsx new file mode 100644 index 0000000..f41c8c7 --- /dev/null +++ b/examples/example-react-native/src/screens/StartupScreen/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import {NativeStackScreenProps} from '@react-navigation/native-stack'; + +import {StackParamList} from '../../App'; +import { + EXPRESSION_UI_RIVE_RESOURCE, + INTRO_VIEW, +} from '../../constants/expression_constants'; +import BaseExpressionView from '../../expression-ui/BaseExpressionView'; + +type Props = NativeStackScreenProps; + +export default function StartupScreen({navigation}: Props): React.JSX.Element { + return ( + navigation.replace('CrossFramework')} + /> + ); +}