@rive-app/react-native 0.1.1 → 0.1.2

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.
Files changed (61) hide show
  1. package/README.md +18 -7
  2. package/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt +0 -1
  3. package/android/src/main/java/com/margelo/nitro/rive/HybridViewModelInstance.kt +26 -47
  4. package/android/src/main/java/com/margelo/nitro/rive/HybridViewModelListProperty.kt +64 -0
  5. package/ios/BaseHybridViewModelProperty.swift +9 -0
  6. package/ios/HybridViewModelInstance.swift +5 -0
  7. package/ios/HybridViewModelListProperty.swift +62 -0
  8. package/lib/module/hooks/useRiveColor.js +0 -1
  9. package/lib/module/hooks/useRiveColor.js.map +1 -1
  10. package/lib/module/hooks/useRiveList.js +71 -0
  11. package/lib/module/hooks/useRiveList.js.map +1 -0
  12. package/lib/module/hooks/useRiveProperty.js +6 -12
  13. package/lib/module/hooks/useRiveProperty.js.map +1 -1
  14. package/lib/module/hooks/useViewModelInstance.js +139 -0
  15. package/lib/module/hooks/useViewModelInstance.js.map +1 -0
  16. package/lib/module/index.js +2 -0
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/typescript/src/hooks/useRiveColor.d.ts +6 -4
  19. package/lib/typescript/src/hooks/useRiveColor.d.ts.map +1 -1
  20. package/lib/typescript/src/hooks/useRiveList.d.ts +11 -0
  21. package/lib/typescript/src/hooks/useRiveList.d.ts.map +1 -0
  22. package/lib/typescript/src/hooks/useRiveProperty.d.ts +6 -1
  23. package/lib/typescript/src/hooks/useRiveProperty.d.ts.map +1 -1
  24. package/lib/typescript/src/hooks/useViewModelInstance.d.ts +86 -0
  25. package/lib/typescript/src/hooks/useViewModelInstance.d.ts.map +1 -0
  26. package/lib/typescript/src/index.d.ts +4 -1
  27. package/lib/typescript/src/index.d.ts.map +1 -1
  28. package/lib/typescript/src/specs/ViewModel.nitro.d.ts +24 -0
  29. package/lib/typescript/src/specs/ViewModel.nitro.d.ts.map +1 -1
  30. package/lib/typescript/src/types.d.ts +47 -3
  31. package/lib/typescript/src/types.d.ts.map +1 -1
  32. package/nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.cpp +9 -0
  33. package/nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.hpp +1 -0
  34. package/nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.cpp +102 -0
  35. package/nitrogen/generated/android/c++/JHybridViewModelListPropertySpec.hpp +73 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridViewModelInstanceSpec.kt +4 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rive/HybridViewModelListPropertySpec.kt +92 -0
  38. package/nitrogen/generated/android/rive+autolinking.cmake +2 -0
  39. package/nitrogen/generated/android/riveOnLoad.cpp +2 -0
  40. package/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.cpp +17 -0
  41. package/nitrogen/generated/ios/RNRive-Swift-Cxx-Bridge.hpp +44 -0
  42. package/nitrogen/generated/ios/RNRive-Swift-Cxx-Umbrella.hpp +5 -0
  43. package/nitrogen/generated/ios/c++/HybridViewModelInstanceSpecSwift.hpp +11 -0
  44. package/nitrogen/generated/ios/c++/HybridViewModelListPropertySpecSwift.cpp +11 -0
  45. package/nitrogen/generated/ios/c++/HybridViewModelListPropertySpecSwift.hpp +134 -0
  46. package/nitrogen/generated/ios/swift/HybridViewModelInstanceSpec.swift +1 -0
  47. package/nitrogen/generated/ios/swift/HybridViewModelInstanceSpec_cxx.swift +21 -0
  48. package/nitrogen/generated/ios/swift/HybridViewModelListPropertySpec.swift +63 -0
  49. package/nitrogen/generated/ios/swift/HybridViewModelListPropertySpec_cxx.swift +248 -0
  50. package/nitrogen/generated/shared/c++/HybridViewModelInstanceSpec.cpp +1 -0
  51. package/nitrogen/generated/shared/c++/HybridViewModelInstanceSpec.hpp +4 -0
  52. package/nitrogen/generated/shared/c++/HybridViewModelListPropertySpec.cpp +30 -0
  53. package/nitrogen/generated/shared/c++/HybridViewModelListPropertySpec.hpp +76 -0
  54. package/package.json +3 -3
  55. package/src/hooks/useRiveColor.ts +7 -4
  56. package/src/hooks/useRiveList.ts +108 -0
  57. package/src/hooks/useRiveProperty.ts +19 -12
  58. package/src/hooks/useViewModelInstance.ts +195 -0
  59. package/src/index.tsx +4 -0
  60. package/src/specs/ViewModel.nitro.ts +28 -0
  61. 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
- * @param value - The value to set the property to.
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
+ }