@rive-app/react-native 0.1.1 → 0.1.3
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/README.md +18 -7
- package/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt +0 -1
- package/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt +26 -47
- package/android/src/main/java/com/margelo/nitro/rive/HybridViewModelListProperty.kt +64 -0
- package/ios/BaseHybridViewModelProperty.swift +9 -0
- package/ios/HybridViewModelInstance.swift +5 -0
- package/ios/HybridViewModelListProperty.swift +62 -0
- package/lib/module/hooks/useRiveColor.js +0 -1
- package/lib/module/hooks/useRiveColor.js.map +1 -1
- package/lib/module/hooks/useRiveFile.js +9 -2
- package/lib/module/hooks/useRiveFile.js.map +1 -1
- package/lib/module/hooks/useRiveList.js +71 -0
- package/lib/module/hooks/useRiveList.js.map +1 -0
- package/lib/module/hooks/useRiveProperty.js +6 -12
- package/lib/module/hooks/useRiveProperty.js.map +1 -1
- package/lib/module/hooks/useViewModelInstance.js +139 -0
- package/lib/module/hooks/useViewModelInstance.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/hooks/useRiveColor.d.ts +6 -4
- package/lib/typescript/src/hooks/useRiveColor.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRiveFile.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRiveList.d.ts +11 -0
- package/lib/typescript/src/hooks/useRiveList.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useRiveProperty.d.ts +6 -1
- package/lib/typescript/src/hooks/useRiveProperty.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useViewModelInstance.d.ts +86 -0
- package/lib/typescript/src/hooks/useViewModelInstance.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/ViewModel.nitro.d.ts +24 -0
- package/lib/typescript/src/specs/ViewModel.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +47 -3
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.cpp +102 -0
- package/nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.hpp +73 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridViewModelInstanceSpec.kt +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridViewModelListPropertySpec.kt +92 -0
- package/nitrogen/generated/android/rive+autolinking.cmake +2 -0
- package/nitrogen/generated/android/riveOnLoad.cpp +2 -0
- package/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp +44 -0
- package/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/c++/HybridViewModelInstanceSpecSwift.hpp +11 -0
- package/nitrogen/generated/ios/c++/HybridViewModelListPropertySpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridViewModelListPropertySpecSwift.hpp +134 -0
- package/nitrogen/generated/ios/swift/HybridViewModelInstanceSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridViewModelInstanceSpec_cxx.swift +21 -0
- package/nitrogen/generated/ios/swift/HybridViewModelListPropertySpec.swift +63 -0
- package/nitrogen/generated/ios/swift/HybridViewModelListPropertySpec_cxx.swift +248 -0
- package/nitrogen/generated/shared/c++/HybridViewModelInstanceSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridViewModelInstanceSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/HybridViewModelListPropertySpec.cpp +30 -0
- package/nitrogen/generated/shared/c++/HybridViewModelListPropertySpec.hpp +76 -0
- package/package.json +7 -3
- package/src/hooks/useRiveColor.ts +7 -4
- package/src/hooks/useRiveFile.ts +14 -2
- package/src/hooks/useRiveList.ts +108 -0
- package/src/hooks/useRiveProperty.ts +19 -12
- package/src/hooks/useViewModelInstance.ts +195 -0
- package/src/index.tsx +4 -0
- package/src/specs/ViewModel.nitro.ts +28 -0
- package/src/types.tsx +58 -3
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { useMemo, useEffect, useRef } from 'react';
|
|
2
|
+
import type { ViewModel, ViewModelInstance } from '../specs/ViewModel.nitro';
|
|
3
|
+
import type { RiveFile } from '../specs/RiveFile.nitro';
|
|
4
|
+
import type { RiveViewRef } from '../index';
|
|
5
|
+
import { callDispose } from '../core/callDispose';
|
|
6
|
+
|
|
7
|
+
export interface UseViewModelInstanceParams {
|
|
8
|
+
/**
|
|
9
|
+
* Get a specifically named instance from the ViewModel.
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Create a new (blank) instance from the ViewModel.
|
|
14
|
+
*/
|
|
15
|
+
useNew?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* If true, throws an error when the instance cannot be obtained.
|
|
18
|
+
* This is useful with Error Boundaries and ensures TypeScript knows
|
|
19
|
+
* the return value is non-null.
|
|
20
|
+
*/
|
|
21
|
+
required?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Called synchronously when a new instance is created, before the hook returns.
|
|
24
|
+
* Use this to set initial values that need to be available immediately.
|
|
25
|
+
* Note: This callback is excluded from deps - changing it won't recreate the instance.
|
|
26
|
+
*/
|
|
27
|
+
onInit?: (instance: ViewModelInstance) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ViewModelSource = ViewModel | RiveFile | RiveViewRef;
|
|
31
|
+
|
|
32
|
+
function isRiveViewRef(source: ViewModelSource | null): source is RiveViewRef {
|
|
33
|
+
return (
|
|
34
|
+
source !== null && source !== undefined && 'getViewModelInstance' in source
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isRiveFile(source: ViewModelSource | null): source is RiveFile {
|
|
39
|
+
return (
|
|
40
|
+
source !== null &&
|
|
41
|
+
source !== undefined &&
|
|
42
|
+
'defaultArtboardViewModel' in source
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createInstance(
|
|
47
|
+
source: ViewModelSource | null,
|
|
48
|
+
name: string | undefined,
|
|
49
|
+
useNew: boolean
|
|
50
|
+
): { instance: ViewModelInstance | null; needsDispose: boolean } {
|
|
51
|
+
if (!source) {
|
|
52
|
+
return { instance: null, needsDispose: false };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (isRiveViewRef(source)) {
|
|
56
|
+
const vmi = source.getViewModelInstance();
|
|
57
|
+
return { instance: vmi ?? null, needsDispose: false };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isRiveFile(source)) {
|
|
61
|
+
const viewModel = source.defaultArtboardViewModel();
|
|
62
|
+
const vmi = viewModel?.createDefaultInstance();
|
|
63
|
+
return { instance: vmi ?? null, needsDispose: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ViewModel source
|
|
67
|
+
let vmi: ViewModelInstance | undefined;
|
|
68
|
+
if (name) {
|
|
69
|
+
vmi = source.createInstanceByName(name);
|
|
70
|
+
} else if (useNew) {
|
|
71
|
+
vmi = source.createInstance();
|
|
72
|
+
} else {
|
|
73
|
+
vmi = source.createDefaultInstance();
|
|
74
|
+
}
|
|
75
|
+
return { instance: vmi ?? null, needsDispose: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Hook for getting a ViewModelInstance from a RiveFile, ViewModel, or RiveViewRef.
|
|
80
|
+
*
|
|
81
|
+
* @param source - The RiveFile, ViewModel, or RiveViewRef to get an instance from
|
|
82
|
+
* @param params - Configuration for which instance to retrieve (only used with ViewModel)
|
|
83
|
+
* @returns The ViewModelInstance or null if not found
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```tsx
|
|
87
|
+
* // From RiveFile (get default instance)
|
|
88
|
+
* const { riveFile } = useRiveFile(require('./animation.riv'));
|
|
89
|
+
* const instance = useViewModelInstance(riveFile);
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```tsx
|
|
94
|
+
* // From RiveViewRef (get auto-bound instance)
|
|
95
|
+
* const { riveViewRef, setHybridRef } = useRive();
|
|
96
|
+
* const instance = useViewModelInstance(riveViewRef);
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* // From ViewModel
|
|
102
|
+
* const viewModel = file.viewModelByName('main');
|
|
103
|
+
* const instance = useViewModelInstance(viewModel);
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* // Create a new blank instance from ViewModel
|
|
109
|
+
* const viewModel = file.viewModelByName('TodoItem');
|
|
110
|
+
* const newInstance = useViewModelInstance(viewModel, { useNew: true });
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* // With required: true (throws if null, use with Error Boundary)
|
|
116
|
+
* const instance = useViewModelInstance(riveFile, { required: true });
|
|
117
|
+
* // instance is guaranteed to be non-null here
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```tsx
|
|
122
|
+
* // With onInit to set initial values synchronously
|
|
123
|
+
* const instance = useViewModelInstance(riveFile, {
|
|
124
|
+
* onInit: (vmi) => {
|
|
125
|
+
* vmi.numberProperty('count').set(initialCount);
|
|
126
|
+
* vmi.stringProperty('name').set(userName);
|
|
127
|
+
* }
|
|
128
|
+
* });
|
|
129
|
+
* // Values are already set here
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function useViewModelInstance(
|
|
133
|
+
source: ViewModelSource,
|
|
134
|
+
params: UseViewModelInstanceParams & { required: true }
|
|
135
|
+
): ViewModelInstance;
|
|
136
|
+
export function useViewModelInstance(
|
|
137
|
+
source: ViewModelSource | null,
|
|
138
|
+
params?: UseViewModelInstanceParams
|
|
139
|
+
): ViewModelInstance | null;
|
|
140
|
+
export function useViewModelInstance(
|
|
141
|
+
source: ViewModelSource | null,
|
|
142
|
+
params?: UseViewModelInstanceParams
|
|
143
|
+
): ViewModelInstance | null {
|
|
144
|
+
const name = params?.name;
|
|
145
|
+
const useNew = params?.useNew ?? false;
|
|
146
|
+
const required = params?.required ?? false;
|
|
147
|
+
const onInit = params?.onInit;
|
|
148
|
+
|
|
149
|
+
const prevInstanceRef = useRef<{
|
|
150
|
+
instance: ViewModelInstance | null;
|
|
151
|
+
needsDispose: boolean;
|
|
152
|
+
} | null>(null);
|
|
153
|
+
|
|
154
|
+
const result = useMemo(() => {
|
|
155
|
+
const created = createInstance(source, name, useNew);
|
|
156
|
+
if (created.instance && onInit) {
|
|
157
|
+
onInit(created.instance);
|
|
158
|
+
}
|
|
159
|
+
return created;
|
|
160
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- onInit excluded intentionally
|
|
161
|
+
}, [source, name, useNew]);
|
|
162
|
+
|
|
163
|
+
// Dispose previous instance if it changed and needed disposal
|
|
164
|
+
if (
|
|
165
|
+
prevInstanceRef.current &&
|
|
166
|
+
prevInstanceRef.current.instance !== result.instance &&
|
|
167
|
+
prevInstanceRef.current.needsDispose &&
|
|
168
|
+
prevInstanceRef.current.instance
|
|
169
|
+
) {
|
|
170
|
+
callDispose(prevInstanceRef.current.instance);
|
|
171
|
+
}
|
|
172
|
+
prevInstanceRef.current = result;
|
|
173
|
+
|
|
174
|
+
// Cleanup on unmount
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
return () => {
|
|
177
|
+
if (
|
|
178
|
+
prevInstanceRef.current?.needsDispose &&
|
|
179
|
+
prevInstanceRef.current.instance
|
|
180
|
+
) {
|
|
181
|
+
callDispose(prevInstanceRef.current.instance);
|
|
182
|
+
prevInstanceRef.current = null;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
if (required && result.instance === null) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
'useViewModelInstance: Failed to get ViewModelInstance. ' +
|
|
190
|
+
'Ensure the source has a valid ViewModel and instance available.'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result.instance;
|
|
195
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -30,6 +30,7 @@ export type {
|
|
|
30
30
|
ViewModelEnumProperty,
|
|
31
31
|
ViewModelTriggerProperty,
|
|
32
32
|
ViewModelImageProperty,
|
|
33
|
+
ViewModelListProperty,
|
|
33
34
|
} from './specs/ViewModel.nitro';
|
|
34
35
|
export { Fit } from './core/Fit';
|
|
35
36
|
export { Alignment } from './core/Alignment';
|
|
@@ -47,6 +48,9 @@ export { useRiveBoolean } from './hooks/useRiveBoolean';
|
|
|
47
48
|
export { useRiveEnum } from './hooks/useRiveEnum';
|
|
48
49
|
export { useRiveColor } from './hooks/useRiveColor';
|
|
49
50
|
export { useRiveTrigger } from './hooks/useRiveTrigger';
|
|
51
|
+
export { useRiveList } from './hooks/useRiveList';
|
|
52
|
+
export { useViewModelInstance } from './hooks/useViewModelInstance';
|
|
50
53
|
export { useRiveFile } from './hooks/useRiveFile';
|
|
51
54
|
export { type RiveFileInput } from './hooks/useRiveFile';
|
|
55
|
+
export { type SetValueAction } from './types';
|
|
52
56
|
export { DataBindMode };
|
|
@@ -52,6 +52,9 @@ export interface ViewModelInstance
|
|
|
52
52
|
|
|
53
53
|
/** Get an image property from the view model instance at the given path */
|
|
54
54
|
imageProperty(path: string): ViewModelImageProperty | undefined;
|
|
55
|
+
|
|
56
|
+
/** Get a list property from the view model instance at the given path */
|
|
57
|
+
listProperty(path: string): ViewModelListProperty | undefined;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
export interface ViewModelProperty
|
|
@@ -124,3 +127,28 @@ export interface ViewModelImageProperty
|
|
|
124
127
|
/** Add a listener to the view model image property. Returns a function to remove the listener. */
|
|
125
128
|
addListener(onChanged: () => void): () => void;
|
|
126
129
|
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A list property that contains a dynamic collection of {@link ViewModelInstance} objects.
|
|
133
|
+
* @see {@link https://rive.app/docs/runtimes/data-binding#lists Rive Data Binding Lists}
|
|
134
|
+
*/
|
|
135
|
+
export interface ViewModelListProperty
|
|
136
|
+
extends ViewModelProperty,
|
|
137
|
+
ObservableProperty {
|
|
138
|
+
/** The number of instances in the list */
|
|
139
|
+
readonly length: number;
|
|
140
|
+
/** Get the instance at the given index */
|
|
141
|
+
getInstanceAt(index: number): ViewModelInstance | undefined;
|
|
142
|
+
/** Add an instance to the end of the list */
|
|
143
|
+
addInstance(instance: ViewModelInstance): void;
|
|
144
|
+
/** Add an instance at the given index, returns true if successful */
|
|
145
|
+
addInstanceAt(instance: ViewModelInstance, index: number): boolean;
|
|
146
|
+
/** Remove an instance from the list */
|
|
147
|
+
removeInstance(instance: ViewModelInstance): void;
|
|
148
|
+
/** Remove the instance at the given index */
|
|
149
|
+
removeInstanceAt(index: number): void;
|
|
150
|
+
/** Swap the instances at the given indices, returns true if successful */
|
|
151
|
+
swap(index1: number, index2: number): boolean;
|
|
152
|
+
/** Add a listener to be notified when the list changes. Returns a function to remove the listener. */
|
|
153
|
+
addListener(onChanged: () => void): () => void;
|
|
154
|
+
}
|
package/src/types.tsx
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A value or a function that computes a new value from the previous value.
|
|
3
|
+
* Similar to React's SetStateAction pattern.
|
|
4
|
+
*/
|
|
5
|
+
export type SetValueAction<T> = T | ((prevValue: T | undefined) => T);
|
|
6
|
+
|
|
1
7
|
export interface UseRivePropertyResult<T> {
|
|
2
8
|
/**
|
|
3
9
|
* The current value of the property.
|
|
4
10
|
*/
|
|
5
11
|
value: T | undefined;
|
|
6
12
|
/**
|
|
7
|
-
* Set the value of the property.
|
|
8
|
-
*
|
|
13
|
+
* Set the value of the property. Accepts either a direct value or
|
|
14
|
+
* a function that receives the previous value and returns the new value.
|
|
15
|
+
* @example
|
|
16
|
+
* setValue(10) // Set to 10
|
|
17
|
+
* setValue((prev) => (prev ?? 0) + 5) // Increment by 5
|
|
9
18
|
*/
|
|
10
|
-
setValue: (value: T) => void;
|
|
19
|
+
setValue: (value: SetValueAction<T>) => void;
|
|
11
20
|
/**
|
|
12
21
|
* The error if the property is not found.
|
|
13
22
|
*/
|
|
@@ -32,3 +41,49 @@ export interface UseRiveTriggerResult {
|
|
|
32
41
|
export type UseViewModelInstanceTriggerParameters = {
|
|
33
42
|
onTrigger?: () => void;
|
|
34
43
|
};
|
|
44
|
+
|
|
45
|
+
export interface UseRiveListResult {
|
|
46
|
+
/**
|
|
47
|
+
* The number of instances in the list.
|
|
48
|
+
*/
|
|
49
|
+
length: number;
|
|
50
|
+
/**
|
|
51
|
+
* Get the instance at the given index.
|
|
52
|
+
*/
|
|
53
|
+
getInstanceAt: (
|
|
54
|
+
index: number
|
|
55
|
+
) => import('./specs/ViewModel.nitro').ViewModelInstance | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Add an instance to the end of the list.
|
|
58
|
+
*/
|
|
59
|
+
addInstance: (
|
|
60
|
+
instance: import('./specs/ViewModel.nitro').ViewModelInstance
|
|
61
|
+
) => void;
|
|
62
|
+
/**
|
|
63
|
+
* Add an instance at the given index.
|
|
64
|
+
* @returns true if successful
|
|
65
|
+
*/
|
|
66
|
+
addInstanceAt: (
|
|
67
|
+
instance: import('./specs/ViewModel.nitro').ViewModelInstance,
|
|
68
|
+
index: number
|
|
69
|
+
) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Remove an instance from the list.
|
|
72
|
+
*/
|
|
73
|
+
removeInstance: (
|
|
74
|
+
instance: import('./specs/ViewModel.nitro').ViewModelInstance
|
|
75
|
+
) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Remove the instance at the given index.
|
|
78
|
+
*/
|
|
79
|
+
removeInstanceAt: (index: number) => void;
|
|
80
|
+
/**
|
|
81
|
+
* Swap the instances at the given indices.
|
|
82
|
+
* @returns true if successful
|
|
83
|
+
*/
|
|
84
|
+
swap: (index1: number, index2: number) => boolean;
|
|
85
|
+
/**
|
|
86
|
+
* The error if the property is not found.
|
|
87
|
+
*/
|
|
88
|
+
error: Error | null;
|
|
89
|
+
}
|