diff --git a/src/hooks/useViewModelInstanceList.ts b/src/hooks/useViewModelInstanceList.ts new file mode 100644 index 0000000..21da86b --- /dev/null +++ b/src/hooks/useViewModelInstanceList.ts @@ -0,0 +1,65 @@ +import { useCallback } from 'react'; +import { ViewModelInstance, ViewModelInstanceList } from '@rive-app/canvas'; +import { UseViewModelInstanceListResult } from '../types'; +import { useViewModelInstanceProperty } from './useViewModelInstanceProperty'; + +/** + * Hook for interacting with list properties of a ViewModelInstance. + * + * @param path - Path to the property (e.g. "items" or "nested/items") + * @param viewModelInstance - The ViewModelInstance containing the list property + * @returns An object with the list length and manipulation functions + */ +export default function useViewModelInstanceList( + path: string, + viewModelInstance?: ViewModelInstance | null +): UseViewModelInstanceListResult { + + const result = useViewModelInstanceProperty>( + path, + viewModelInstance, + { + getProperty: useCallback((vm, p) => vm.list(p), []), + getValue: useCallback((prop) => prop.length, []), + defaultValue: 0, + buildPropertyOperations: useCallback((safePropertyAccess) => ({ + addInstance: (instance: ViewModelInstance) => { + safePropertyAccess(prop => prop.addInstance(instance)); + }, + addInstanceAt: (instance: ViewModelInstance, index: number): boolean => { + let result = false; + safePropertyAccess(prop => { + result = prop.addInstanceAt(instance, index); + }); + return result; + }, + removeInstance: (instance: ViewModelInstance) => { + safePropertyAccess(prop => prop.removeInstance(instance)); + }, + removeInstanceAt: (index: number) => { + safePropertyAccess(prop => prop.removeInstanceAt(index)); + }, + getInstanceAt: (index: number): ViewModelInstance | null => { + let result: ViewModelInstance | null = null; + safePropertyAccess(prop => { + result = prop.instanceAt(index); + }); + return result; + }, + swap: (a: number, b: number) => { + safePropertyAccess(prop => prop.swap(a, b)); + } + }), []) + } + ); + + return { + length: result.value ?? 0, + addInstance: result.addInstance, + addInstanceAt: result.addInstanceAt, + removeInstance: result.removeInstance, + removeInstanceAt: result.removeInstanceAt, + getInstanceAt: result.getInstanceAt, + swap: result.swap + }; +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 8d18fd9..0493f5e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ import { RiveFile, RiveFileParameters, RiveParameters, + ViewModelInstance, } from '@rive-app/canvas'; import { ComponentProps, RefCallback } from 'react'; @@ -196,4 +197,45 @@ export type UseViewModelInstanceImageResult = { * @param value - The image to set. */ setValue: (value: RiveRenderImage | null) => void; +}; + +export type UseViewModelInstanceListResult = { + /** + * The current length of the list. + */ + length: number; + /** + * Add an instance to the end of the list. + * @param instance - The ViewModelInstance to add. + */ + addInstance: (instance: ViewModelInstance) => void; + /** + * Add an instance at a specific index in the list. + * @param instance - The ViewModelInstance to add. + * @param index - The index to add the instance at. + * @returns True if the instance was successfully added, false otherwise. + */ + addInstanceAt: (instance: ViewModelInstance, index: number) => boolean; + /** + * Remove an instance from the list. + * @param instance - The ViewModelInstance to remove. + */ + removeInstance: (instance: ViewModelInstance) => void; + /** + * Remove an instance at a specific index from the list. + * @param index - The index to remove the instance from. + */ + removeInstanceAt: (index: number) => void; + /** + * Get an instance at a specific index from the list. + * @param index - The index to get the instance from. + * @returns The ViewModelInstance at the index, or null if not found. + */ + getInstanceAt: (index: number) => ViewModelInstance | null; + /** + * Swap two instances in the list. + * @param a - The first index. + * @param b - The second index. + */ + swap: (a: number, b: number) => void; }; \ No newline at end of file