Compare commits

...

155 Commits

Author SHA1 Message Date
zplata
d72e2241e6 chore: release 4.27.1 2026-03-10 18:59:34 +00:00
Zach Plata
881983229f chore: bump js runtime to 2.35.2 2026-03-10 13:08:31 -05:00
Lance Snider
1abd420408 Update supported React versions to include 19.0.0 2026-03-09 11:20:18 -07:00
bodymovin
e7217e5943 chore: release 4.27.0 2026-02-13 17:39:01 +00:00
Hernan Torrisi
e4738c014d rive canvas 2.35.0 2026-02-13 09:31:50 -08:00
bodymovin
e1055c2907 chore: release 4.26.2 2026-01-27 15:02:21 +00:00
Hernan Torrisi
3c1459beb3 bump rive to 2.34.2 2026-01-27 06:55:07 -08:00
HayesGordon
3c59c7a667 chore: release 4.26.1 2026-01-15 07:57:25 +00:00
Hernan Torrisi
2dceda58bf update rive_canvas 2026-01-15 08:55:15 +01:00
avivian
1af00a38c7 chore: release 4.26.0 2026-01-13 12:56:35 +00:00
Arthur Vivian
6ab0496025 chore: bump to 2.34.0 2026-01-13 12:51:04 +00:00
bodymovin
37ced47323 chore: release 4.25.3 2026-01-08 04:11:16 +00:00
Hernan Torrisi
79f1a06df0 update rive to 2.33.3 2026-01-07 20:08:29 -08:00
philter
f0d4c3e6d2 chore: release 4.25.2 2026-01-06 23:23:20 +00:00
Phil Chung
0d3300eca4 Bump rive wasm to 2.33.2 2026-01-06 16:19:09 -07:00
philter
7f6ab5ad10 chore: release 4.25.1 2025-12-20 00:04:31 +00:00
Phil Chung
5df93323ed Bump rive wasm to 2.33.1 2025-12-19 16:59:45 -07:00
HayesGordon
e6bfec7c51 chore: release 4.25.0 2025-12-17 22:01:34 +00:00
CI
770858fb00 chore: auto publish false for release-it 2025-12-17 22:59:26 +01:00
CI
5145a6edb3 chore: ensure latest npm 2025-12-17 22:14:13 +01:00
CI
f74cfbc3d0 chore: bump rive web 2.33.0 2025-12-17 21:34:41 +01:00
CI
925de2fc94 chore: update npm publish workflow with OIDC support 2025-12-17 20:10:39 +01:00
Hernan Torrisi
c7d1d92b9e bump rive wasm to 2.32.1 2025-11-10 19:10:59 -08:00
HayesGordon
19f20268ae chore: release 4.24.0 2025-11-10 13:44:07 +00:00
CI
e6e43564b1 chore: bump rive web 2.32.0 2025-11-10 14:42:06 +01:00
bodymovin
efeee472e6 chore: release 4.23.4 2025-09-23 20:40:08 +00:00
Hernan Torrisi
42d502f452 bump rive to 2.31.6 2025-09-23 09:15:53 -07:00
bodymovin
bd483e0ab7 chore: release 4.23.3 2025-09-04 22:53:16 +00:00
Hernan Torrisi
7c361e4c53 chore: bump rive wasm to 2.31.5 2025-09-04 15:49:34 -07:00
HayesGordon
d80df170aa chore: release 4.23.2 2025-09-04 14:29:04 +00:00
CI
a3118d5984 chore: bump rive wasm to 2.31.4 2025-09-04 16:27:08 +02:00
CI
f732a3b044 docs: update README 2025-09-04 16:27:08 +02:00
bodymovin
57ebc37e3f chore: release 4.23.1 2025-08-13 17:19:55 +00:00
Hernan Torrisi
69a356894d bump rive to 2.31.2 2025-08-13 10:09:58 -07:00
Hernan Torrisi
23d9d7f48b add dependency 2025-08-12 12:27:31 -07:00
Hernan Torrisi
788b7ef68e bump rive canvas to 2.31.1 2025-08-12 12:27:31 -07:00
HayesGordon
69658c204a chore: release 4.23.0 2025-08-08 12:09:50 +00:00
CI
7249fa36e7 chore: bump rive wasm 2.31.0 2025-08-08 12:42:01 +02:00
bodymovin
52dd934e43 chore: release 4.22.1 2025-07-18 14:29:38 +00:00
Hernan Torrisi
c151ee37b5 bump rive to 2.30.4 2025-07-18 07:18:56 -07:00
damzobridge
c660a675c2 chore: release 4.22.0 2025-07-15 20:57:21 +00:00
Adam
74e1d5a5f2 feat: add tests for artboard binding 2025-07-15 12:52:34 -07:00
Adam
963ecc43b8 feat: add useViewModelInstanceArtboard hook 2025-07-15 12:52:34 -07:00
bodymovin
85807f2166 chore: release 4.21.6 2025-07-15 04:51:16 +00:00
Hernan Torrisi
9a33504d3a rive_canvas_2.30.3 2025-07-14 21:48:19 -07:00
HayesGordon
1a4d7e7168 chore: release 4.21.5 2025-07-14 16:12:59 +00:00
CI
b3d0fd4339 chore: bump rive wasm 2.30.2 2025-07-14 17:09:31 +01:00
Adam
c4239ab6b2 fix: lint error with revision 2025-07-07 14:23:22 -07:00
Adam
22f8d5a945 feat: add tests for list property 2025-07-07 14:23:22 -07:00
Adam
721ed786dc feat: add useViewModelInstanceList hook 2025-07-07 14:23:22 -07:00
Adam
eef56fb641 feat: add useViewModelInstanceImage hook 2025-07-07 14:23:22 -07:00
bodymovin
4bc0f496f8 chore: release 4.21.4 2025-06-25 13:39:06 +00:00
Maxwell Talbot
10bb4c69ea fix: update how we use release it with github tokens 2025-06-25 06:26:13 -07:00
Hernan Torrisi
c5b6826996 bump rive to 2.30.1 2025-06-25 06:23:15 -07:00
Adam
ec4875933c refactor: change onLoad to onRiveReady 2025-06-23 11:57:58 -07:00
Adam
d808a8bdea feat: add onLoad callback to useRive 2025-06-23 11:57:58 -07:00
Hernan Torrisi
7b174f7f51 cleanup rive on unmount 2025-06-18 16:25:59 -07:00
bodymovin
eecd0d3c5b chore: release 4.21.3 2025-06-08 17:57:17 +00:00
Hernan Torrisi
6c00364e60 rive react 2.29.3 2025-06-08 10:22:55 -07:00
HayesGordon
d310f1c96d chore: release 4.21.2 2025-06-05 20:43:20 +00:00
CI
68e8fbe46d chore: bump Rive wasm 2.29.2 2025-06-05 22:40:54 +02:00
bodymovin
8ff9a844fe chore: release 4.21.1 2025-05-28 16:15:41 +00:00
Hernan Torrisi
a565795452 bump rive to 2.29.0 2025-05-28 08:10:54 -07:00
HayesGordon
b26280ae12 chore: release 4.21.0 2025-05-23 16:44:06 +00:00
CI
a62e89de94 chore: bump rive wasm 2.28.0 2025-05-23 18:20:22 +02:00
HayesGordon
31255f9746 chore: release 4.20.2 2025-05-23 15:24:28 +00:00
CI
3e768533df chore: bump rive wasm 2.27.5 2025-05-22 14:42:31 +02:00
bodymovin
c790e66723 chore: release 4.20.1 2025-05-14 06:46:19 +00:00
Hernan Torrisi
ab89793032 rive canvas 2.27.3 2025-05-13 23:43:41 -07:00
damzobridge
2088c0bf9b chore: release 4.20.0 2025-05-12 22:51:11 +00:00
Adam
81fdf57736 chore: simplify useViewModelInstance 2025-05-12 14:16:00 -07:00
Adam
3a59585207 fix: implement useViewModel feedback 2025-05-12 14:16:00 -07:00
Adam
46e19874a2 feat: add db test 2025-05-12 14:16:00 -07:00
Lance
d4de776a01 Add onTrigger example 2025-05-12 14:16:00 -07:00
Lance
fb4f543077 Add stocks data binding example 2025-05-12 14:16:00 -07:00
Lance
345905f810 Merge storybook 2025-05-12 14:16:00 -07:00
Adam
0d9dabb135 fix: use default view model if none provide in useViewModelInstance and useViewModel 2025-05-12 14:16:00 -07:00
Adam
ad12fe20d1 fix: unify effects in useViewModelInstance 2025-05-12 14:16:00 -07:00
Adam
4e45f74a47 fix: change useViewModel property hook parameters to match conventions 2025-05-12 14:16:00 -07:00
Adam
efd6c4ce82 chore: simplify useViewModel 2025-05-12 14:16:00 -07:00
Adam
37e379091c fix: avoid rebuilding operations unless necessary 2025-05-12 14:16:00 -07:00
Adam
6d76e9f85d fix: hot reload crash 2025-05-12 14:16:00 -07:00
Adam
e7b64201ca chore: update UseViewModelParameters documentation 2025-05-12 14:16:00 -07:00
Adam
5dc8362107 fix: remove viewmodel hooks from initial release 2025-05-12 14:16:00 -07:00
Adam
56bc96fa76 chore: update color setter methods to be more explicit 2025-05-12 14:16:00 -07:00
Adam
01ab78db97 fix: lint issue 2025-05-12 14:16:00 -07:00
Adam
7800cc041f feat: add instance property hooks 2025-05-12 14:16:00 -07:00
Hernan Torrisi
1f9fc84629 hook with generic 2025-05-12 14:16:00 -07:00
Hernan Torrisi
452eb89e72 inital work for data binding hooks 2025-05-12 14:16:00 -07:00
Lance
3109e45724 Remove GH Action that published Stories to GH Pages 2025-05-12 11:38:30 -06:00
avivian
d303e8c96f chore: release 4.19.1 2025-05-08 11:24:05 +00:00
Arthur Vivian
479d5340e8 chore: rive-wasm -> 2.27.2 2025-05-08 12:20:01 +01:00
lancesnider
ff3976d9fe chore: release 4.19.0 2025-04-29 21:20:41 +00:00
Lance
f2a6860292 Fix failing tests 2025-04-29 15:15:57 -06:00
Lance
8213c6ed7e Upgrade testing 2025-04-29 15:15:57 -06:00
Lance
a9a98fece2 Upgrade React and React DOM 2025-04-29 15:15:57 -06:00
Lance
4052b7492b Update contributing docs 2025-04-29 15:15:57 -06:00
Lance
8bb6f1dc58 Add storybook linter 2025-04-29 15:15:57 -06:00
Lance
6d6b68e868 Call storybook from parent 2025-04-29 15:15:57 -06:00
Lance
d3305f8afe General cleanup 2025-04-29 15:15:57 -06:00
Lance
9eb292c2c5 Remove unnecessary react app stuff 2025-04-29 15:15:57 -06:00
Lance
7277ed2f0d Storybookk page reloads when the package in the parent changes 2025-04-29 15:15:57 -06:00
Lance
5354d1f69b Add examples 2025-04-29 15:15:57 -06:00
Lance
86e67d5f11 Remove old storybook and auto docs 2025-04-29 15:15:57 -06:00
philter
e5c751aa54 chore: release 4.18.9 2025-04-23 19:30:29 +00:00
Phil Chung
60b1dda98d chore: bump rive web to 2.27.1 2025-04-23 13:20:21 -06:00
bodymovin
4914770c70 chore: release 4.18.8 2025-04-16 23:37:08 +00:00
Hernan Torrisi
21e457bda4 rive canvas 2.27.0 2025-04-16 16:30:10 -07:00
susan101566
cc0e91689b chore: release 4.18.7 2025-04-09 18:16:01 +00:00
Susan Wang
8244982000 update to 2.26.8 2025-04-09 12:09:58 -06:00
HayesGordon
439b413782 chore: release 4.18.6 2025-04-03 13:02:07 +00:00
CI
9ab4e378fc chore: bump rive web to 2.26.7 2025-04-03 14:58:32 +02:00
bodymovin
9061a93978 chore: release 4.18.5 2025-03-28 05:48:05 +00:00
Hernan Torrisi
88d1bc000c update to 2.26.6 2025-03-27 22:43:52 -07:00
bodymovin
9b1d3825f8 chore: release 4.18.4 2025-03-24 17:11:49 +00:00
Hernan Torrisi
277f4d7db0 update rive canvas to 2.26.5 2025-03-24 10:01:16 -07:00
bodymovin
e68ca963a9 chore: release 4.18.3 2025-02-28 01:37:00 +00:00
Hernan Torrisi
d820cdf6f1 update rive to 2.26.4 2025-02-27 17:31:48 -08:00
HayesGordon
c9a4dc0b5c chore: release 4.18.2 2025-02-17 11:19:33 +00:00
CI
19b58f5eb6 chore: update rive to 2.26.3 2025-02-17 12:14:25 +01:00
bodymovin
6263449898 chore: release 4.18.1 2025-02-15 01:07:50 +00:00
Hernan Torrisi
630f70a81d update rive to 2.26.2 2025-02-14 17:02:23 -08:00
bodymovin
bbd0d08fed chore: release 4.18.0 2025-02-11 20:20:14 +00:00
Hernan Torrisi
ef23793d26 update rive to 2.26.1 2025-02-11 11:49:47 -08:00
Hernan Torrisi
ca10584ed4 cleanup instance if use effect reruns and instance is not loaded 2025-02-04 07:43:18 -08:00
damzobridge
6fe765df84 chore: release 4.17.10 2025-01-27 23:34:24 +00:00
Chukwuma Okere
c62b80af09 chore: add React 19 support to package.json 2025-01-27 14:47:04 -08:00
bodymovin
777337c481 chore: release 4.17.9 2025-01-27 16:22:26 +00:00
Hernan Torrisi
da4b5978e7 update rive to 2.25.7 2025-01-27 08:17:38 -08:00
bodymovin
09c0d1f947 chore: release 4.17.8 2025-01-24 21:08:47 +00:00
Hernan Torrisi
0ca5933c41 update rive to 2.25.6 2025-01-24 13:03:56 -08:00
bodymovin
f272fd92f0 chore: release 4.17.7 2025-01-23 23:46:21 +00:00
CI
192a562764 chore: bump wasm to 2.25.5 2025-01-23 20:43:34 +02:00
bodymovin
224a768737 chore: release 4.17.6 2025-01-14 14:58:56 +00:00
Hernan Torrisi
c085bd5ea3 update rive to 2.25.4 2025-01-13 19:38:53 -08:00
bodymovin
bb13c71982 chore: release 4.17.5 2024-12-24 18:55:30 +00:00
Hernan Torrisi
779e06583c bump rive canvas to 2.25.3 2024-12-24 10:47:44 -08:00
bodymovin
e172e0f210 chore: release 4.17.4 2024-12-17 19:51:56 +00:00
Hernan Torrisi
abbcf14b70 rive canvas 2.25.2 2024-12-17 11:42:28 -08:00
bodymovin
a0885648ce chore: release 4.17.3 2024-12-13 04:18:27 +00:00
Hernan Torrisi
9f768bdd0c rive canvas 2.25.1 2024-12-12 20:09:59 -08:00
bodymovin
94c9a2a60b chore: release 4.17.2 2024-12-11 18:55:36 +00:00
Hernan Torrisi
dfe0d10936 rive canvas 2.25.0 2024-12-11 10:50:36 -08:00
bodymovin
d27eb7254d chore: release 4.17.1 2024-12-11 02:31:56 +00:00
Hernan Torrisi
c0b27bd018 rive canvas 2.24.1 2024-12-10 18:27:53 -08:00
bodymovin
739ac7c370 chore: release 4.17.0 2024-12-10 03:45:43 +00:00
Hernan Torrisi
b1b8040b4f bump rive canvas to 2.24.0 2024-12-09 19:40:57 -08:00
bodymovin
1f6ae83de3 chore: release 4.16.7 2024-11-27 23:26:32 +00:00
Hernan Torrisi
e5ff233c34 bump rive canvas to 2.23.11 2024-11-27 15:16:09 -08:00
lancesnider
64795564d8 chore: release 4.16.6 2024-11-22 16:22:19 +00:00
Lance
f0b32056a4 Bump rive canvas to 2.23.10 2024-11-22 08:12:52 -08:00
bodymovin
b5dc2fa8da chore: release 4.16.5 2024-11-21 15:08:14 +00:00
Hernan Torrisi
0108d425dd update rive canvas version 2024-11-21 07:04:03 -08:00
bodymovin
74a27143b4 chore: release 4.16.4 2024-11-15 20:29:44 +00:00
Hernan Torrisi
2cfb8a4955 bump rive canvas to 2.23.8 2024-11-15 11:53:45 -08:00
117 changed files with 15633 additions and 1480 deletions

View File

@@ -10,23 +10,29 @@ on:
description: 'Minor'
type: boolean
default: false
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
publish_job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_GITHUB }}
- name: Setup Git config
run: |
git config --local user.email 'hello@rive.app'
git config --local user.name ${{ github.actor }}
- name: Authenticate with registry
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- uses: actions/setup-node@v2
- uses: actions/setup-node@v4
with:
node-version: '16.x'
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Upgrade npm for OIDC support
run: npm install -g npm@latest
- name: Install Modules
run: npm install
- name: Run type check
@@ -38,21 +44,9 @@ jobs:
- if: ${{ inputs.major == true }}
name: Major Release - Bump version number, update changelog, push and tag
run: npm run release:major
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
PAT_GITHUB: ${{ secrets.PAT_GITHUB }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- if: ${{inputs.major == false && inputs.minor == true}}
name: Minor release - Bump version number, update changelog, push and tag
run: npm run release:minor
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
PAT_GITHUB: ${{ secrets.PAT_GITHUB }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- if: ${{inputs.major == false && inputs.minor == false}}
name: Patch release - Bump version number, update changelog, push and tag
run: npm run release:patch
env:
GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }}
PAT_GITHUB: ${{ secrets.PAT_GITHUB }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,30 +0,0 @@
name: Deploy Storybook
on:
# Testing to see if this job is causing the race condition
workflow_dispatch:
# pull_request:
# types: [closed]
# branches:
# - main
# paths: ['src', 'examples/stories/**'] # Trigger the action only when files change in the folders defined here
jobs:
build-and-deploy:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Install and Build 🔧
run: | # Install npm packages and build the Storybook files
npm install
npm run build-storybook
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@3.6.2
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: main # The branch the action should deploy to.
FOLDER: docs-build # The folder that the build-storybook script generates files.
CLEAN: true # Automatically remove deleted files from the deploy branch
TARGET_FOLDER: docs # The folder that we serve our Storybook files from

View File

@@ -7,7 +7,7 @@
"changelog": "npx auto-changelog --stdout --commit-limit false --unreleased --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs"
},
"npm": {
"publish": true
"publish": false
},
"github": {
"release": true,

View File

@@ -1,13 +0,0 @@
module.exports = {
"stories": [
"../examples/stories/*.stories.mdx",
"../examples/stories/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": "@storybook/react",
staticDirs: ['../examples/stories/assets'],
}

View File

@@ -1,17 +0,0 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
data-href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&amp;display=swap"
/>
<link
rel="stylesheet"
data-href="https://fonts.googleapis.com/css2?family=Roboto+Mono&amp;display=swap"
/>
<style>
body {
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
</style>

View File

@@ -1,39 +0,0 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
options: {
storySort: {
method: 'alphabetical',
order: ['Overview', 'Playback Controls', 'State Machines'],
},
},
viewMode: 'docs',
};
// The below function helps to default to the docs page, which contains all the documentation and examples
function clickDocsButtonOnFirstLoad() {
window.removeEventListener("load", clickDocsButtonOnFirstLoad);
try {
const docsButtonSelector = window.parent.document.evaluate(
"//button[contains(., 'Docs')]",
window.parent.document,
null,
XPathResult.ANY_TYPE,
null
);
const button = docsButtonSelector.iterateNext();
button.click();
} catch (error) {
// Do nothing if it wasn't able to click on Docs button.
}
}
window.addEventListener("load", clickDocsButtonOnFirstLoad);

View File

@@ -4,8 +4,396 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v4.27.1](https://github.com/rive-app/rive-react/compare/v4.27.0...v4.27.1)
- chore: bump js runtime to 2.35.2 [`8819832`](https://github.com/rive-app/rive-react/commit/881983229f699de7da516a05cda415158c1517d2)
- Update supported React versions to include 19.0.0 [`1abd420`](https://github.com/rive-app/rive-react/commit/1abd420408b4c06ae172d1f060c2461e7226a3d3)
#### [v4.27.0](https://github.com/rive-app/rive-react/compare/v4.26.2...v4.27.0)
> 13 February 2026
- chore: release 4.27.0 [`e7217e5`](https://github.com/rive-app/rive-react/commit/e7217e5943afa7ab5405d0301465df20ab135c7a)
- rive canvas 2.35.0 [`e4738c0`](https://github.com/rive-app/rive-react/commit/e4738c014de2fa707c3950d2b3b7011bde4624e0)
#### [v4.26.2](https://github.com/rive-app/rive-react/compare/v4.26.1...v4.26.2)
> 27 January 2026
- chore: release 4.26.2 [`e1055c2`](https://github.com/rive-app/rive-react/commit/e1055c2907466a5857ef8880916db30d896f4275)
- bump rive to 2.34.2 [`3c1459b`](https://github.com/rive-app/rive-react/commit/3c1459beb31f68d8a77d0ead405f0c4726cbeb43)
#### [v4.26.1](https://github.com/rive-app/rive-react/compare/v4.26.0...v4.26.1)
> 15 January 2026
- chore: release 4.26.1 [`3c59c7a`](https://github.com/rive-app/rive-react/commit/3c59c7a66736bf1955f38a98e351687df7bd1494)
- update rive_canvas [`2dceda5`](https://github.com/rive-app/rive-react/commit/2dceda58bfc57830fb181796df321789727973cc)
#### [v4.26.0](https://github.com/rive-app/rive-react/compare/v4.25.3...v4.26.0)
> 13 January 2026
- chore: release 4.26.0 [`1af00a3`](https://github.com/rive-app/rive-react/commit/1af00a38c7973141bccca08845e396c59a88c0ba)
- chore: bump to 2.34.0 [`6ab0496`](https://github.com/rive-app/rive-react/commit/6ab0496025739f2d6c78ed84ada0488d6dd58ab2)
#### [v4.25.3](https://github.com/rive-app/rive-react/compare/v4.25.2...v4.25.3)
> 8 January 2026
- chore: release 4.25.3 [`37ced47`](https://github.com/rive-app/rive-react/commit/37ced4732343d6c6076bd018b6e179615df32003)
- update rive to 2.33.3 [`79f1a06`](https://github.com/rive-app/rive-react/commit/79f1a06df09ec95f9bacdc260e7fcabcfb13975e)
#### [v4.25.2](https://github.com/rive-app/rive-react/compare/v4.25.1...v4.25.2)
> 6 January 2026
- chore: release 4.25.2 [`f0d4c3e`](https://github.com/rive-app/rive-react/commit/f0d4c3e6d250527b4cbbda2d0fdbe05058cd1772)
- Bump rive wasm to 2.33.2 [`0d3300e`](https://github.com/rive-app/rive-react/commit/0d3300eca435ca3862cd63ce795dd5f5b561a1a8)
#### [v4.25.1](https://github.com/rive-app/rive-react/compare/v4.25.0...v4.25.1)
> 20 December 2025
- chore: release 4.25.1 [`7f6ab5a`](https://github.com/rive-app/rive-react/commit/7f6ab5ad10a996503e590f362c17cd96ee69ac5d)
- Bump rive wasm to 2.33.1 [`5df9332`](https://github.com/rive-app/rive-react/commit/5df93323eda2e08b64d375e39e57a22f473aab1e)
#### [v4.25.0](https://github.com/rive-app/rive-react/compare/v4.24.0...v4.25.0)
> 17 December 2025
- chore: release 4.25.0 [`e6bfec7`](https://github.com/rive-app/rive-react/commit/e6bfec7c517c5fe001c40476de1bd0d48dd77fd7)
- chore: update npm publish workflow with OIDC support [`925de2f`](https://github.com/rive-app/rive-react/commit/925de2fc94172a306f194f45b628f711fff14d50)
- chore: bump rive web 2.33.0 [`f74cfbc`](https://github.com/rive-app/rive-react/commit/f74cfbc3d04383aeb9707a378d4056be400567c3)
#### [v4.24.0](https://github.com/rive-app/rive-react/compare/v4.23.4...v4.24.0)
> 10 November 2025
- chore: release 4.24.0 [`19f2026`](https://github.com/rive-app/rive-react/commit/19f20268ae1c39d873f9d64cf10a499a4f792970)
- chore: bump rive web 2.32.0 [`e6e4356`](https://github.com/rive-app/rive-react/commit/e6e43564b1af8da608c146a2a76795e29063daf8)
#### [v4.23.4](https://github.com/rive-app/rive-react/compare/v4.23.3...v4.23.4)
> 23 September 2025
- chore: release 4.23.4 [`efeee47`](https://github.com/rive-app/rive-react/commit/efeee472e60946d293f6287e11350fdaf4afe03b)
- bump rive to 2.31.6 [`42d502f`](https://github.com/rive-app/rive-react/commit/42d502f452b6a76cafc332c2cd84bd97e315be77)
#### [v4.23.3](https://github.com/rive-app/rive-react/compare/v4.23.2...v4.23.3)
> 4 September 2025
- chore: release 4.23.3 [`bd483e0`](https://github.com/rive-app/rive-react/commit/bd483e0ab72a99a9c55c7e9fb80bd95827ca54cb)
- chore: bump rive wasm to 2.31.5 [`7c361e4`](https://github.com/rive-app/rive-react/commit/7c361e4c538cf813fdc94f572fac1e4a82258ae8)
#### [v4.23.2](https://github.com/rive-app/rive-react/compare/v4.23.1...v4.23.2)
> 4 September 2025
- chore: release 4.23.2 [`d80df17`](https://github.com/rive-app/rive-react/commit/d80df170aa0d1c575101c8cd1bb76968173c439f)
- docs: update README [`f732a3b`](https://github.com/rive-app/rive-react/commit/f732a3b044e2a56ed0ea178a43d68612423c0548)
- chore: bump rive wasm to 2.31.4 [`a3118d5`](https://github.com/rive-app/rive-react/commit/a3118d59841c45467b46170e3eed7ba3359d4fea)
#### [v4.23.1](https://github.com/rive-app/rive-react/compare/v4.23.0...v4.23.1)
> 13 August 2025
- chore: release 4.23.1 [`57ebc37`](https://github.com/rive-app/rive-react/commit/57ebc37e3f99eb7fd9673e34441f395c990e312b)
- bump rive to 2.31.2 [`69a3568`](https://github.com/rive-app/rive-react/commit/69a356894d3acf44f4d24b708e9f8d8dda5b3046)
- bump rive canvas to 2.31.1 [`788b7ef`](https://github.com/rive-app/rive-react/commit/788b7ef68e9001460175b596f74d7f54616a69d4)
#### [v4.23.0](https://github.com/rive-app/rive-react/compare/v4.22.1...v4.23.0)
> 8 August 2025
- chore: release 4.23.0 [`69658c2`](https://github.com/rive-app/rive-react/commit/69658c204ad1f70a408bab098136c2b23083fd16)
- chore: bump rive wasm 2.31.0 [`7249fa3`](https://github.com/rive-app/rive-react/commit/7249fa36e7b6a2184ec60fb1e34a68f28b4eeb6d)
#### [v4.22.1](https://github.com/rive-app/rive-react/compare/v4.22.0...v4.22.1)
> 18 July 2025
- chore: release 4.22.1 [`52dd934`](https://github.com/rive-app/rive-react/commit/52dd934e439507d079bf4f5009372857dfbb97a6)
- bump rive to 2.30.4 [`c151ee3`](https://github.com/rive-app/rive-react/commit/c151ee37b5482cb7eee258c84f6c52182dbe9db5)
#### [v4.22.0](https://github.com/rive-app/rive-react/compare/v4.21.6...v4.22.0)
> 15 July 2025
- feat: add tests for artboard binding [`74e1d5a`](https://github.com/rive-app/rive-react/commit/74e1d5a5f29f14f46be3af3d052bb51c3d833799)
- feat: add useViewModelInstanceArtboard hook [`963ecc4`](https://github.com/rive-app/rive-react/commit/963ecc43b80e6465d159621d014b70b8cbfee9d4)
- chore: release 4.22.0 [`c660a67`](https://github.com/rive-app/rive-react/commit/c660a675c246af9fca50795ff88b7935c2d2a101)
#### [v4.21.6](https://github.com/rive-app/rive-react/compare/v4.21.5...v4.21.6)
> 15 July 2025
- chore: release 4.21.6 [`85807f2`](https://github.com/rive-app/rive-react/commit/85807f2166fcfba01e4556ac346b769d6fa08341)
- rive_canvas_2.30.3 [`9a33504`](https://github.com/rive-app/rive-react/commit/9a33504d3a315ce2f3dff753192b0ae491a56a04)
#### [v4.21.5](https://github.com/rive-app/rive-react/compare/v4.21.4...v4.21.5)
> 14 July 2025
- feat: add tests for list property [`22f8d5a`](https://github.com/rive-app/rive-react/commit/22f8d5a945c74974b7dabcfe16aaa019f6141326)
- feat: add useViewModelInstanceImage hook [`eef56fb`](https://github.com/rive-app/rive-react/commit/eef56fb641839b55806296873186aa53b3e1d068)
- feat: add useViewModelInstanceList hook [`721ed78`](https://github.com/rive-app/rive-react/commit/721ed786dc43a526eafb54108bfb54f353d7430d)
#### [v4.21.4](https://github.com/rive-app/rive-react/compare/v4.21.3...v4.21.4)
> 25 June 2025
- chore: release 4.21.4 [`4bc0f49`](https://github.com/rive-app/rive-react/commit/4bc0f496f87a54ffda673acb7b9be4b7a8b311c0)
- cleanup rive on unmount [`7b174f7`](https://github.com/rive-app/rive-react/commit/7b174f7f5106b1b863969bd7318a8a6cb1a12b67)
- refactor: change onLoad to onRiveReady [`ec48759`](https://github.com/rive-app/rive-react/commit/ec4875933cad45a3d338290951d55ac9c72df9d0)
#### [v4.21.3](https://github.com/rive-app/rive-react/compare/v4.21.2...v4.21.3)
> 8 June 2025
- chore: release 4.21.3 [`eecd0d3`](https://github.com/rive-app/rive-react/commit/eecd0d3c5be011fe9865e45b05435fbd45e7395d)
- rive react 2.29.3 [`6c00364`](https://github.com/rive-app/rive-react/commit/6c00364e60e91a7a6556e763ebf9ebee4793b336)
#### [v4.21.2](https://github.com/rive-app/rive-react/compare/v4.21.1...v4.21.2)
> 5 June 2025
- chore: release 4.21.2 [`d310f1c`](https://github.com/rive-app/rive-react/commit/d310f1c96dbff6cbb7397d4bea2687c8d3f271f4)
- chore: bump Rive wasm 2.29.2 [`68e8fbe`](https://github.com/rive-app/rive-react/commit/68e8fbe46d4f1824a6228ce2ea0a02735dced5ba)
#### [v4.21.1](https://github.com/rive-app/rive-react/compare/v4.21.0...v4.21.1)
> 28 May 2025
- chore: release 4.21.1 [`8ff9a84`](https://github.com/rive-app/rive-react/commit/8ff9a844fe5b02a2eb1964cf01814479f6c72248)
- bump rive to 2.29.0 [`a565795`](https://github.com/rive-app/rive-react/commit/a565795452444205e88083cba272bc8ca6c9968f)
#### [v4.21.0](https://github.com/rive-app/rive-react/compare/v4.20.2...v4.21.0)
> 23 May 2025
- chore: release 4.21.0 [`b26280a`](https://github.com/rive-app/rive-react/commit/b26280ae125173b52dd9a6147833f45631c2252f)
- chore: bump rive wasm 2.28.0 [`a62e89d`](https://github.com/rive-app/rive-react/commit/a62e89de9436360e439896a1aa11623e3574897e)
#### [v4.20.2](https://github.com/rive-app/rive-react/compare/v4.20.1...v4.20.2)
> 23 May 2025
- chore: release 4.20.2 [`31255f9`](https://github.com/rive-app/rive-react/commit/31255f974635278aea211dcf827e3a0cd0cc138e)
- chore: bump rive wasm 2.27.5 [`3e76853`](https://github.com/rive-app/rive-react/commit/3e768533df747da69acd392332495303077fa8c6)
#### [v4.20.1](https://github.com/rive-app/rive-react/compare/v4.20.0...v4.20.1)
> 14 May 2025
- chore: release 4.20.1 [`c790e66`](https://github.com/rive-app/rive-react/commit/c790e6672389ea68ee222140a49bcb7e4a7d3ca3)
- rive canvas 2.27.3 [`ab89793`](https://github.com/rive-app/rive-react/commit/ab89793032bcadf58f680610cea2e15fcd76d0b2)
#### [v4.20.0](https://github.com/rive-app/rive-react/compare/v4.19.1...v4.20.0)
> 12 May 2025
- feat: add db test [`46e1987`](https://github.com/rive-app/rive-react/commit/46e19874a2ec5893b5d3365f61db871400327087)
- fix: hot reload crash [`6d76e9f`](https://github.com/rive-app/rive-react/commit/6d76e9f85d949ec1e0e4d29458676efbe1c24d1d)
- inital work for data binding hooks [`452eb89`](https://github.com/rive-app/rive-react/commit/452eb89e72ffb73f837917fd969a51ed238a6d05)
#### [v4.19.1](https://github.com/rive-app/rive-react/compare/v4.19.0...v4.19.1)
> 8 May 2025
- chore: release 4.19.1 [`d303e8c`](https://github.com/rive-app/rive-react/commit/d303e8c96f70fa8886d96aba35afd911f0fcef50)
- chore: rive-wasm -&gt; 2.27.2 [`479d534`](https://github.com/rive-app/rive-react/commit/479d5340e87f5335a2525b547e690be60ebafc00)
#### [v4.19.0](https://github.com/rive-app/rive-react/compare/v4.18.9...v4.19.0)
> 29 April 2025
- Add examples [`5354d1f`](https://github.com/rive-app/rive-react/commit/5354d1f69bfe91dc67c39cee80a6ea00c4c70cb1)
- Storybookk page reloads when the package in the parent changes [`7277ed2`](https://github.com/rive-app/rive-react/commit/7277ed2f0d877150637a69f2ff9122db1b151686)
- Upgrade React and React DOM [`a9a98fe`](https://github.com/rive-app/rive-react/commit/a9a98fece2caf727e941ce645be2f031efaf8a89)
#### [v4.18.9](https://github.com/rive-app/rive-react/compare/v4.18.8...v4.18.9)
> 23 April 2025
- chore: release 4.18.9 [`e5c751a`](https://github.com/rive-app/rive-react/commit/e5c751aa542ac1af4693bff476a9eba6355c0df3)
- chore: bump rive web to 2.27.1 [`60b1dda`](https://github.com/rive-app/rive-react/commit/60b1dda98dfb1f35ae905d0741a10f8f4269bfa4)
#### [v4.18.8](https://github.com/rive-app/rive-react/compare/v4.18.7...v4.18.8)
> 16 April 2025
- chore: release 4.18.8 [`4914770`](https://github.com/rive-app/rive-react/commit/4914770c70985a95cd1d7739286b4e4cc262e028)
- rive canvas 2.27.0 [`21e457b`](https://github.com/rive-app/rive-react/commit/21e457bda4bc3c09a0b866bcef062d8172d71105)
#### [v4.18.7](https://github.com/rive-app/rive-react/compare/v4.18.6...v4.18.7)
> 9 April 2025
- chore: release 4.18.7 [`cc0e916`](https://github.com/rive-app/rive-react/commit/cc0e91689b071569ae28728cc8dcab7e400926b3)
- update to 2.26.8 [`8244982`](https://github.com/rive-app/rive-react/commit/82449820004e25b58684db5548183cde4eb59154)
#### [v4.18.6](https://github.com/rive-app/rive-react/compare/v4.18.5...v4.18.6)
> 3 April 2025
- chore: release 4.18.6 [`439b413`](https://github.com/rive-app/rive-react/commit/439b4137828b5be2f945e0f396309fadb78a27c7)
- chore: bump rive web to 2.26.7 [`9ab4e37`](https://github.com/rive-app/rive-react/commit/9ab4e378fc31f462e7c4f3d5317e4ac631b2e386)
#### [v4.18.5](https://github.com/rive-app/rive-react/compare/v4.18.4...v4.18.5)
> 28 March 2025
- chore: release 4.18.5 [`9061a93`](https://github.com/rive-app/rive-react/commit/9061a93978cf482b25bdbedbbb053266687a9ee3)
- update to 2.26.6 [`88d1bc0`](https://github.com/rive-app/rive-react/commit/88d1bc000cd5a8b20edf483a99c8c9b6b8ed28e4)
#### [v4.18.4](https://github.com/rive-app/rive-react/compare/v4.18.3...v4.18.4)
> 24 March 2025
- chore: release 4.18.4 [`9b1d382`](https://github.com/rive-app/rive-react/commit/9b1d3825f890bf8c82985073d50d67da95f9d727)
- update rive canvas to 2.26.5 [`277f4d7`](https://github.com/rive-app/rive-react/commit/277f4d7db0ff0b873c2b10750affa97a9d29cbd3)
#### [v4.18.3](https://github.com/rive-app/rive-react/compare/v4.18.2...v4.18.3)
> 28 February 2025
- chore: release 4.18.3 [`e68ca96`](https://github.com/rive-app/rive-react/commit/e68ca963a93e779104c032ceb055c141f1a565eb)
- update rive to 2.26.4 [`d820cdf`](https://github.com/rive-app/rive-react/commit/d820cdf6f13c53b519080ecd0438f222b9eda764)
#### [v4.18.2](https://github.com/rive-app/rive-react/compare/v4.18.1...v4.18.2)
> 17 February 2025
- chore: release 4.18.2 [`c9a4dc0`](https://github.com/rive-app/rive-react/commit/c9a4dc0b5cafd7696414ff99ae3add7bd852d0a7)
- chore: update rive to 2.26.3 [`19b58f5`](https://github.com/rive-app/rive-react/commit/19b58f5eb6d215a92019b69d1c1a182e1b582af0)
#### [v4.18.1](https://github.com/rive-app/rive-react/compare/v4.18.0...v4.18.1)
> 15 February 2025
- chore: release 4.18.1 [`6263449`](https://github.com/rive-app/rive-react/commit/6263449898abdb77f4a08e92f186e61f9ec4cea8)
- update rive to 2.26.2 [`630f70a`](https://github.com/rive-app/rive-react/commit/630f70a81dbf5292fc339163f3367b53adfb5e65)
#### [v4.18.0](https://github.com/rive-app/rive-react/compare/v4.17.10...v4.18.0)
> 11 February 2025
- chore: release 4.18.0 [`bbd0d08`](https://github.com/rive-app/rive-react/commit/bbd0d08fed0c3f3e5efd04f178e7a380951616ab)
- cleanup instance if use effect reruns and instance is not loaded [`ca10584`](https://github.com/rive-app/rive-react/commit/ca10584ed422be62e9eea2b7d05007cd2abd3870)
- update rive to 2.26.1 [`ef23793`](https://github.com/rive-app/rive-react/commit/ef23793d2629dee2eb448d0c825a3e7cdfb675d9)
#### [v4.17.10](https://github.com/rive-app/rive-react/compare/v4.17.9...v4.17.10)
> 27 January 2025
- chore: release 4.17.10 [`6fe765d`](https://github.com/rive-app/rive-react/commit/6fe765df84e996611b8878155ba0078ee9caff78)
- chore: add React 19 support to package.json [`c62b80a`](https://github.com/rive-app/rive-react/commit/c62b80af09a3b42407dac8d9c63cd0e070738d82)
#### [v4.17.9](https://github.com/rive-app/rive-react/compare/v4.17.8...v4.17.9)
> 27 January 2025
- chore: release 4.17.9 [`777337c`](https://github.com/rive-app/rive-react/commit/777337c4814ecc16f09a7971f3fabccc4be48b33)
- update rive to 2.25.7 [`da4b597`](https://github.com/rive-app/rive-react/commit/da4b5978e7a558bba0d10ca18ef3053deac88691)
#### [v4.17.8](https://github.com/rive-app/rive-react/compare/v4.17.7...v4.17.8)
> 24 January 2025
- chore: release 4.17.8 [`09c0d1f`](https://github.com/rive-app/rive-react/commit/09c0d1f94723f09faf06d0e17c19f322f0e2a1c2)
- update rive to 2.25.6 [`0ca5933`](https://github.com/rive-app/rive-react/commit/0ca5933c411b1a733b35dd785a7bfcdba569b8c5)
#### [v4.17.7](https://github.com/rive-app/rive-react/compare/v4.17.6...v4.17.7)
> 23 January 2025
- chore: release 4.17.7 [`f272fd9`](https://github.com/rive-app/rive-react/commit/f272fd92f0030016b691923f8c61d1ae56182e82)
- chore: bump wasm to 2.25.5 [`192a562`](https://github.com/rive-app/rive-react/commit/192a562764d1cb906d14e2f8f3d6fed3cfa5f039)
#### [v4.17.6](https://github.com/rive-app/rive-react/compare/v4.17.5...v4.17.6)
> 14 January 2025
- chore: release 4.17.6 [`224a768`](https://github.com/rive-app/rive-react/commit/224a7687373bfc8ef05bfb4604f5b1e2be9e4cdd)
- update rive to 2.25.4 [`c085bd5`](https://github.com/rive-app/rive-react/commit/c085bd5ea3c811a5934c725d3347baf723b11225)
#### [v4.17.5](https://github.com/rive-app/rive-react/compare/v4.17.4...v4.17.5)
> 24 December 2024
- chore: release 4.17.5 [`bb13c71`](https://github.com/rive-app/rive-react/commit/bb13c719828b2c5c27b524f0e79b5f7ad3896618)
- bump rive canvas to 2.25.3 [`779e065`](https://github.com/rive-app/rive-react/commit/779e06583ceb853621693d0e0c056537c90998b9)
#### [v4.17.4](https://github.com/rive-app/rive-react/compare/v4.17.3...v4.17.4)
> 17 December 2024
- chore: release 4.17.4 [`e172e0f`](https://github.com/rive-app/rive-react/commit/e172e0f21092a3c4dd9cf528aa71684dc07b3fa7)
- rive canvas 2.25.2 [`abbcf14`](https://github.com/rive-app/rive-react/commit/abbcf14b706fedb8aed1c291b0d35b225206eed8)
#### [v4.17.3](https://github.com/rive-app/rive-react/compare/v4.17.2...v4.17.3)
> 13 December 2024
- chore: release 4.17.3 [`a088564`](https://github.com/rive-app/rive-react/commit/a0885648ceb4d833c1467fd10d36641fc5f79450)
- rive canvas 2.25.1 [`9f768bd`](https://github.com/rive-app/rive-react/commit/9f768bdd0ca07aedec7f67405223b44bcf7380d7)
#### [v4.17.2](https://github.com/rive-app/rive-react/compare/v4.17.1...v4.17.2)
> 11 December 2024
- chore: release 4.17.2 [`94c9a2a`](https://github.com/rive-app/rive-react/commit/94c9a2a60b89244e0a36eb9324e500616a31e970)
- rive canvas 2.25.0 [`dfe0d10`](https://github.com/rive-app/rive-react/commit/dfe0d109368abe8ebadf8dabc80d05116242c301)
#### [v4.17.1](https://github.com/rive-app/rive-react/compare/v4.17.0...v4.17.1)
> 11 December 2024
- chore: release 4.17.1 [`d27eb72`](https://github.com/rive-app/rive-react/commit/d27eb7254dfa2c2338f02264c7d1a65c8bf1f366)
- rive canvas 2.24.1 [`c0b27bd`](https://github.com/rive-app/rive-react/commit/c0b27bd018f0bdb6f0e1679816c96c5ba6d6c5bd)
#### [v4.17.0](https://github.com/rive-app/rive-react/compare/v4.16.7...v4.17.0)
> 10 December 2024
- chore: release 4.17.0 [`739ac7c`](https://github.com/rive-app/rive-react/commit/739ac7c3708b7aa28d0bae01fc13ce99d7fc27ed)
- bump rive canvas to 2.24.0 [`b1b8040`](https://github.com/rive-app/rive-react/commit/b1b8040b4f92375be734070502e73e6609e82448)
#### [v4.16.7](https://github.com/rive-app/rive-react/compare/v4.16.6...v4.16.7)
> 27 November 2024
- chore: release 4.16.7 [`1f6ae83`](https://github.com/rive-app/rive-react/commit/1f6ae83de319a244cb3fb84766cbcc10e2d7197b)
- bump rive canvas to 2.23.11 [`e5ff233`](https://github.com/rive-app/rive-react/commit/e5ff233c34241d19f16cd1b5bb1ceaba1d36a43e)
#### [v4.16.6](https://github.com/rive-app/rive-react/compare/v4.16.5...v4.16.6)
> 22 November 2024
- chore: release 4.16.6 [`6479556`](https://github.com/rive-app/rive-react/commit/64795564d806df0f3bb8c8dfe865a5f7a41dfd2a)
- Bump rive canvas to 2.23.10 [`f0b3205`](https://github.com/rive-app/rive-react/commit/f0b32056a4488aef59f9f6b17522c61ac7f483f6)
#### [v4.16.5](https://github.com/rive-app/rive-react/compare/v4.16.4...v4.16.5)
> 21 November 2024
- chore: release 4.16.5 [`b5dc2fa`](https://github.com/rive-app/rive-react/commit/b5dc2fa8dad4858322e1b0d626c6dd9ae949e2f9)
- update rive canvas version [`0108d42`](https://github.com/rive-app/rive-react/commit/0108d425dd35603db9da1f1d99d0c00a5dcb86b0)
#### [v4.16.4](https://github.com/rive-app/rive-react/compare/v4.16.3...v4.16.4)
> 15 November 2024
- chore: release 4.16.4 [`74a2714`](https://github.com/rive-app/rive-react/commit/74a27143b41773436c3e7125e8047572b5cde009)
- bump rive canvas to 2.23.8 [`2cfb8a4`](https://github.com/rive-app/rive-react/commit/2cfb8a4955b2bc18e1895680cfcd2b305005cce1)
#### [v4.16.3](https://github.com/rive-app/rive-react/compare/v4.16.2...v4.16.3)
> 14 November 2024
- chore: release 4.16.3 [`af6c3f8`](https://github.com/rive-app/rive-react/commit/af6c3f84c6dd4dc21731ca8457efe33a6ae1578c)
- bump rive canvas to 2.23.7 [`351d996`](https://github.com/rive-app/rive-react/commit/351d9966b17180a1402d29ca71a1f5aef1d804ac)
- update react to 2.23.6 [`ba73cb7`](https://github.com/rive-app/rive-react/commit/ba73cb7053a3ac729e24dc05d6e0ae272f84f12f)

View File

@@ -21,7 +21,7 @@ npm run dev
### Running the example storybook locally
We use Storybook to deploy our examples out onto a public-facing page for folks to view and see code examples for. It also serves as the place we'll include any example suites. These story files are stored in `examples/**.stories.mdx`. `.mdx` is an extension Storybook supports to support both `jsx` (React files) and `md` (markdown) in one file.
We use Storybook to deploy our examples out onto a public-facing page for folks to view and see code examples for. It also serves as the place we'll include any example suites. These story files are stored in `/examples`
To run Storybook, run the following command in the terminal:
@@ -29,7 +29,11 @@ To run Storybook, run the following command in the terminal:
npm run storybook
```
Any changes made in any files should reflect automatically, including the `.mdx` example files, and `src/` files.
To see changes made to the Rive React runtime reflected in your storyook, run the following command in a separate terminal window:
```
npm run dev
```
### Testing

View File

@@ -1,5 +1,4 @@
![Build Status](https://github.com/rive-app/rive-react/actions/workflows/tests.yml/badge.svg)
[![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](https://rive-app.github.io/rive-react)
![Discord badge](https://img.shields.io/discord/532365473602600965)
![Twitter handle](https://img.shields.io/twitter/follow/rive_app.svg?style=social&label=Follow)
@@ -7,9 +6,16 @@
![Rive hero image](https://cdn.rive.app/rive_logo_dark_bg.png)
A React runtime library for [Rive](https://rive.app).
[Rive](https://rive.app) combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. This end-to-end pipeline guarantees that what you build in the Rive Editor is exactly what ships in your apps, games, and websites.
This library is a wrapper around the [JS/Wasm runtime](https://github.com/rive-app/rive-wasm), giving full control over the js runtime while providing components and hooks for React applications.
This library is a wrapper around the [JS/Wasm runtime](https://github.com/rive-app/rive-wasm), giving full control over the JS/Wasm runtime while providing components and hooks for React applications.
For more information, check out the following resources:
- [Homepage](https://rive.app/)
- [General Docs](https://rive.app/docs/)
- [React Docs](https://rive.app/docs/runtimes/react/react)
- [Rive Community / Support](https://community.rive.app/c/support/)
## Table of contents
@@ -49,14 +55,12 @@ For more information, see the Runtime sections of the Rive help documentation:
## Supported versions
This library supports React versions `^16.8.0` through `^18.0.0`.
This library supports React versions `^16.8.0` through `^19.0.0`.
## Examples
Check out our Storybook instance that shows how to use the library in small examples, along with code snippets! This includes examples using the basic component, as well as the convenient hooks exported to take advantage of state machines.
- [Example page](https://rive-app.github.io/rive-react)
- [Login screen w/ input tracking](https://rive-app.github.io/rive-use-cases/?path=/story/example-loginformcomponent--primary)
- [Mouse tracking](https://codesandbox.io/s/rive-mouse-track-test-t0y965?file=/src/App.js)
- [Accessibility concerns](https://rive.app/blog/accesible-web-animations-aria-live-regions)

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/

View File

@@ -1 +0,0 @@
{"version":3,"file":"0.233bd6e2.iframe.bundle.js","sources":[],"mappings":";A","sourceRoot":""}

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +0,0 @@
/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{"./node_modules/@storybook/preview-web/dist/esm/renderDocs.js":function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,"renderDocs",(function(){return renderDocs})),__webpack_require__.d(__webpack_exports__,"unmountDocs",(function(){return unmountDocs}));__webpack_require__("./node_modules/regenerator-runtime/runtime.js"),__webpack_require__("./node_modules/core-js/modules/es.object.to-string.js"),__webpack_require__("./node_modules/core-js/modules/es.promise.js");var react=__webpack_require__("./node_modules/react/index.js"),react_default=__webpack_require__.n(react),react_dom=__webpack_require__("./node_modules/react-dom/index.js"),react_dom_default=__webpack_require__.n(react_dom),wrapper={fontSize:"14px",letterSpacing:"0.2px",margin:"10px 0"},main={margin:"auto",padding:30,borderRadius:10,background:"rgba(0,0,0,0.03)"},heading={textAlign:"center"},NoDocs_NoDocs=function NoDocs(){return react_default.a.createElement("div",{style:wrapper,className:"sb-nodocs sb-wrapper"},react_default.a.createElement("div",{style:main},react_default.a.createElement("h1",{style:heading},"No Docs"),react_default.a.createElement("p",null,"Sorry, but there are no docs for the selected story. To add them, set the story's ",react_default.a.createElement("code",null,"docs")," parameter. If you think this is an error:"),react_default.a.createElement("ul",null,react_default.a.createElement("li",null,"Please check the story definition."),react_default.a.createElement("li",null,"Please check the Storybook config."),react_default.a.createElement("li",null,"Try reloading the page.")),react_default.a.createElement("p",null,"If the problem persists, check the browser console, or the terminal you've run Storybook from.")))};function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg),value=info.value}catch(error){return void reject(error)}info.done?resolve(value):Promise.resolve(value).then(_next,_throw)}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(void 0)}))}}function renderDocs(story,docsContext,element,callback){return function renderDocsAsync(_x,_x2,_x3){return _renderDocsAsync.apply(this,arguments)}(story,docsContext,element).then(callback)}function _renderDocsAsync(){return(_renderDocsAsync=_asyncToGenerator(regeneratorRuntime.mark((function _callee(story,docsContext,element){var _docs$getContainer,_docs$getPage,docs,DocsContainer,Page,docsElement;return regeneratorRuntime.wrap((function _callee$(_context){for(;;)switch(_context.prev=_context.next){case 0:if(!(null!=(docs=story.parameters.docs)&&docs.getPage||null!=docs&&docs.page)||(null!=docs&&docs.getContainer||null!=docs&&docs.container)){_context.next=3;break}throw new Error("No `docs.container` set, did you run `addon-docs/preset`?");case 3:if(_context.t1=docs.container,_context.t1){_context.next=8;break}return _context.next=7,null===(_docs$getContainer=docs.getContainer)||void 0===_docs$getContainer?void 0:_docs$getContainer.call(docs);case 7:_context.t1=_context.sent;case 8:if(_context.t0=_context.t1,_context.t0){_context.next=11;break}_context.t0=function(_ref){var children=_ref.children;return react_default.a.createElement(react_default.a.Fragment,null,children)};case 11:if(DocsContainer=_context.t0,_context.t3=docs.page,_context.t3){_context.next=17;break}return _context.next=16,null===(_docs$getPage=docs.getPage)||void 0===_docs$getPage?void 0:_docs$getPage.call(docs);case 16:_context.t3=_context.sent;case 17:if(_context.t2=_context.t3,_context.t2){_context.next=20;break}_context.t2=NoDocs_NoDocs;case 20:return Page=_context.t2,docsElement=react_default.a.createElement(DocsContainer,{key:story.componentId,context:docsContext},react_default.a.createElement(Page,null)),_context.next=24,new Promise((function(resolve){react_dom_default.a.render(docsElement,element,resolve)}));case 24:case"end":return _context.stop()}}),_callee)})))).apply(this,arguments)}function unmountDocs(element){react_dom_default.a.unmountComponentAtNode(element)}NoDocs_NoDocs.displayName="NoDocs"}}]);

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,31 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

View File

@@ -1 +0,0 @@
{"version":3,"file":"6.e2fef599.iframe.bundle.js","sources":[],"mappings":";A","sourceRoot":""}

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +0,0 @@
/*!
* OverlayScrollbars
* https://github.com/KingSora/OverlayScrollbars
*
* Version: 1.13.0
*
* Copyright KingSora | Rene Haas.
* https://github.com/KingSora
*
* Released under the MIT license.
* Date: 02.08.2020
*/

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +0,0 @@
/*!
* OverlayScrollbars
* https://github.com/KingSora/OverlayScrollbars
*
* Version: 1.13.0
*
* Copyright KingSora | Rene Haas.
* https://github.com/KingSora
*
* Released under the MIT license.
* Date: 02.08.2020
*/

View File

@@ -1 +0,0 @@
{"version":3,"file":"9.1e553722.iframe.bundle.js","sources":[],"mappings":";A","sourceRoot":""}

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{931:function(module,exports){module.exports=function(e,n){return n=n||{},new Promise((function(t,r){var s=new XMLHttpRequest,o=[],u=[],i={},a=function(){return{ok:2==(s.status/100|0),statusText:s.statusText,status:s.status,url:s.responseURL,text:function(){return Promise.resolve(s.responseText)},json:function(){return Promise.resolve(s.responseText).then(JSON.parse)},blob:function(){return Promise.resolve(new Blob([s.response]))},clone:a,headers:{keys:function(){return o},entries:function(){return u},get:function(e){return i[e.toLowerCase()]},has:function(e){return e.toLowerCase()in i}}}};for(var l in s.open(n.method||"get",e,!0),s.onload=function(){s.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(function(e,n,t){o.push(n=n.toLowerCase()),u.push([n,t]),i[n]=i[n]?i[n]+","+t:t})),t(a())},s.onerror=r,s.withCredentials="include"==n.credentials,n.headers)s.setRequestHeader(l,n.headers[l]);s.send(n.body||null)}))}}}]);

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,367 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"><base target="_parent"><style>/* While we aren't showing the main block yet, but still preparing, we want everything the user
has rendered, which may or may not be in #root, to be display none */
.sb-show-preparing-story:not(.sb-show-main) > :not(.sb-preparing-story) {
display: none;
}
.sb-show-preparing-docs:not(.sb-show-main) > :not(.sb-preparing-docs) {
display: none;
}
/* Hide our own blocks when we aren't supposed to be showing them */
:not(.sb-show-preparing-story) > .sb-preparing-story,
:not(.sb-show-preparing-docs) > .sb-preparing-docs,
:not(.sb-show-nopreview) > .sb-nopreview,
:not(.sb-show-errordisplay) > .sb-errordisplay {
display: none;
}
.sb-show-main.sb-main-centered {
margin: 0;
display: flex;
align-items: center;
min-height: 100vh;
}
.sb-show-main.sb-main-centered #root {
box-sizing: border-box;
margin: auto;
padding: 1rem;
max-height: 100%; /* Hack for centering correctly in IE11 */
}
/* Vertical centering fix for IE11 */
@media screen and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.sb-show-main.sb-main-centered:after {
content: '';
min-height: inherit;
font-size: 0;
}
}
.sb-show-main.sb-main-fullscreen {
margin: 0;
padding: 0;
display: block;
}
.sb-show-main.sb-main-padded {
margin: 0;
padding: 1rem;
display: block;
box-sizing: border-box;
}
.sb-wrapper {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
font-family: 'Nunito Sans', -apple-system, '.SFNSText-Regular', 'San Francisco',
BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
overflow: auto;
}
.sb-heading {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.2px;
margin: 10px 0;
padding-right: 25px;
}
.sb-nopreview {
display: flex;
align-content: center;
justify-content: center;
}
.sb-nopreview_main {
margin: auto;
padding: 30px;
border-radius: 10px;
background: rgba(0, 0, 0, 0.03);
}
.sb-nopreview_heading {
text-align: center;
}
.sb-errordisplay {
border: 20px solid rgb(187, 49, 49);
background: #222;
color: #fff;
z-index: 999999;
}
.sb-errordisplay_code {
padding: 10px;
background: #000;
color: #eee;
font-family: 'Operator Mono', 'Fira Code Retina', 'Fira Code', 'FiraCode-Retina', 'Andale Mono',
'Lucida Console', Consolas, Monaco, monospace;
}
.sb-errordisplay pre {
white-space: pre-wrap;
}
@-webkit-keyframes sb-rotate360 {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes sb-rotate360 {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@-webkit-keyframes sb-glow {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
@keyframes sb-glow {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
/* We display the preparing loaders *over* the rendering story */
.sb-preparing-story,
.sb-preparing-docs {
background-color: white;
/* Maximum possible z-index. It would be better to use stacking contexts to ensure it's always
on top, but this isn't possible as it would require making CSS changes that could affect user code */
z-index: 2147483647;
}
.sb-loader {
-webkit-animation: sb-rotate360 0.7s linear infinite;
animation: sb-rotate360 0.7s linear infinite;
border-color: rgba(97, 97, 97, 0.29);
border-radius: 50%;
border-style: solid;
border-top-color: #646464;
border-width: 2px;
display: inline-block;
height: 32px;
left: 50%;
margin-left: -16px;
margin-top: -16px;
mix-blend-mode: difference;
overflow: hidden;
position: absolute;
top: 50%;
transition: all 200ms ease-out;
vertical-align: top;
width: 32px;
z-index: 4;
}
.sb-previewBlock {
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
box-shadow: rgba(0, 0, 0, 0.1) 0 1px 3px 0;
margin: 25px auto 40px;
max-width: 600px;
}
.sb-previewBlock_header {
align-items: center;
box-shadow: rgba(0, 0, 0, 0.1) 0 -1px 0 0 inset;
display: flex;
gap: 14px;
height: 40px;
padding: 0 12px;
}
.sb-previewBlock_icon {
-webkit-animation: sb-glow 1.5s ease-in-out infinite;
animation: sb-glow 1.5s ease-in-out infinite;
background: #e6e6e6;
height: 14px;
width: 14px;
}
.sb-previewBlock_icon:last-child {
margin-left: auto;
}
.sb-previewBlock_body {
-webkit-animation: sb-glow 1.5s ease-in-out infinite;
animation: sb-glow 1.5s ease-in-out infinite;
height: 182px;
position: relative;
}
.sb-argstableBlock {
border-collapse: collapse;
border-spacing: 0;
font-size: 13px;
line-height: 20px;
margin: 25px auto 40px;
max-width: 600px;
text-align: left;
width: 100%;
}
.sb-argstableBlock th:first-of-type,
.sb-argstableBlock td:first-of-type {
padding-left: 20px;
}
.sb-argstableBlock th:nth-of-type(2),
.sb-argstableBlock td:nth-of-type(2) {
width: 35%;
}
.sb-argstableBlock th:nth-of-type(3),
.sb-argstableBlock td:nth-of-type(3) {
width: 15%;
}
.sb-argstableBlock th:laste-of-type,
.sb-argstableBlock td:laste-of-type {
width: 25%;
padding-right: 20px;
}
.sb-argstableBlock th span,
.sb-argstableBlock td span {
-webkit-animation: sb-glow 1.5s ease-in-out infinite;
animation: sb-glow 1.5s ease-in-out infinite;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 0;
box-shadow: none;
color: transparent;
}
.sb-argstableBlock th {
padding: 10px 15px;
}
.sb-argstableBlock-body {
border-radius: 4px;
box-shadow: rgba(0, 0, 0, 0.1) 0 1px 3px 1px, rgba(0, 0, 0, 0.065) 0 0 0 1px;
}
.sb-argstableBlock-body tr {
background: transparent;
overflow: hidden;
}
.sb-argstableBlock-body tr:not(:first-child) {
border-top: 1px solid #e6e6e6;
}
.sb-argstableBlock-body tr:first-child td:first-child {
border-top-left-radius: 4px;
}
.sb-argstableBlock-body tr:first-child td:last-child {
border-top-right-radius: 4px;
}
.sb-argstableBlock-body tr:last-child td:first-child {
border-bottom-left-radius: 4px;
}
.sb-argstableBlock-body tr:last-child td:last-child {
border-bottom-right-radius: 4px;
}
.sb-argstableBlock-body td {
background: #fff;
padding-bottom: 10px;
padding-top: 10px;
vertical-align: top;
}
.sb-argstableBlock-body td:not(:first-of-type) {
padding-left: 15px;
padding-right: 15px;
}
.sb-argstableBlock-body button {
-webkit-animation: sb-glow 1.5s ease-in-out infinite;
animation: sb-glow 1.5s ease-in-out infinite;
background-color: rgba(0, 0, 0, 0.1);
border: 0;
border-radius: 0;
box-shadow: none;
color: transparent;
display: inline;
font-size: 12px;
line-height: 1;
padding: 10px 16px;
}
.sb-argstableBlock-summary {
margin-top: 4px;
}
.sb-argstableBlock-code {
margin-right: 4px;
margin-bottom: 4px;
padding: 2px 5px;
}</style><script>/* globals window */
/* eslint-disable no-underscore-dangle */
try {
if (window.top !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.top.__REACT_DEVTOOLS_GLOBAL_HOOK__;
window.__VUE_DEVTOOLS_GLOBAL_HOOK__ = window.top.__VUE_DEVTOOLS_GLOBAL_HOOK__;
window.top.__VUE_DEVTOOLS_CONTEXT__ = window.document;
}
} catch (e) {
// eslint-disable-next-line no-console
console.warn('unable to connect to top frame for connecting dev tools');
}
window.onerror = function onerror(message, source, line, column, err) {
if (window.CONFIG_TYPE !== 'DEVELOPMENT') return;
// eslint-disable-next-line no-var, vars-on-top
var xhr = new window.XMLHttpRequest();
xhr.open('POST', '/runtime-error');
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(
JSON.stringify({
/* eslint-disable object-shorthand */
message: message,
source: source,
line: line,
column: column,
error: err && { message: err.message, name: err.name, stack: err.stack },
origin: 'preview',
/* eslint-enable object-shorthand */
})
);
};</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" data-href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&amp;display=swap"><link rel="stylesheet" data-href="https://fonts.googleapis.com/css2?family=Roboto+Mono&amp;display=swap"><style>body {
font-family: 'Roboto', sans-serif;
font-size: 16px;
}</style><style>#root[hidden],
#docs-root[hidden] {
display: none !important;
}</style></head><body><div class="sb-preparing-story sb-wrapper"><div class="sb-loader"></div></div><div class="sb-preparing-docs sb-wrapper"><div class="sb-previewBlock"><div class="sb-previewBlock_header"><div class="sb-previewBlock_icon"></div><div class="sb-previewBlock_icon"></div><div class="sb-previewBlock_icon"></div><div class="sb-previewBlock_icon"></div></div><div class="sb-previewBlock_body"><div class="sb-loader"></div></div></div><table aria-hidden="true" class="sb-argstableBlock"><thead class="sb-argstableBlock-head"><tr><th><span>Name</span></th><th><span>Description</span></th><th><span>Default</span></th><th><span>Control</span></th></tr></thead><tbody class="sb-argstableBlock-body"><tr><td><span>propertyName</span><span title="Required">*</span></td><td><div><span>This is a short description</span></div><div class="sb-argstableBlock-summary"><div><span class="sb-argstableBlock-code">summary</span></div></div></td><td><div><span class="sb-argstableBlock-code">defaultValue</span></div></td><td><button>Set string</button></td></tr><tr><td><span>propertyName</span><span>*</span></td><td><div><span>This is a short description</span></div><div class="sb-argstableBlock-summary"><div><span class="sb-argstableBlock-code">summary</span></div></div></td><td><div><span class="sb-argstableBlock-code">defaultValue</span></div></td><td><button>Set string</button></td></tr><tr><td><span>propertyName</span><span>*</span></td><td><div><span>This is a short description</span></div><div class="sb-argstableBlock-summary"><div><span class="sb-argstableBlock-code">summary</span></div></div></td><td><div><span class="sb-argstableBlock-code">defaultValue</span></div></td><td><button>Set string</button></td></tr></tbody></table></div><div class="sb-nopreview sb-wrapper"><div class="sb-nopreview_main"><h1 class="sb-nopreview_heading sb-heading">No Preview</h1><p>Sorry, but you either have no stories or none are selected somehow.</p><ul><li>Please check the Storybook config.</li><li>Try reloading the page.</li></ul><p>If the problem persists, check the browser console, or the terminal you've run Storybook from.</p></div></div><div class="sb-errordisplay sb-wrapper"><pre id="error-message" class="sb-heading"></pre><pre class="sb-errordisplay_code"><code id="error-stack"></code></pre></div><div id="root"></div><div id="docs-root"></div><script>window['CONFIG_TYPE'] = "PRODUCTION";
window['LOGLEVEL'] = "info";
window['FRAMEWORK_OPTIONS'] = {};
window['FEATURES'] = {"postcss":true,"emotionAlias":false,"warnOnLegacyHierarchySeparator":true,"buildStoriesJson":false,"storyStoreV7":false,"modernInlineRender":false,"breakingChangesV7":false,"interactionsDebugger":false,"babelModeV7":false,"argTypeTargetsV7":false,"previewMdx2":false};
window['STORIES'] = [{"titlePrefix":"","directory":"./examples/stories","files":"*.stories.mdx","importPathMatcher":"^\\.[\\\\/](?:examples\\/stories\\/(?!\\.)(?=.)[^/]*?\\.stories\\.mdx)$"},{"titlePrefix":"","directory":"./examples/stories","files":"*.stories.@(js|jsx|ts|tsx)","importPathMatcher":"^\\.[\\\\/](?:examples\\/stories\\/(?!\\.)(?=.)[^/]*?\\.stories\\.(js|jsx|ts|tsx))$"}];</script><script src="runtime~main.e7f32bf8.iframe.bundle.js"></script><script src="vendors~main.16f7136c.iframe.bundle.js"></script><script src="main.19f2d967.iframe.bundle.js"></script></body></html>

View File

@@ -1,59 +0,0 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"/><style>html, body {
overflow: hidden;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}</style><script>/* globals window */
/* eslint-disable no-underscore-dangle */
try {
if (window.top !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.top.__REACT_DEVTOOLS_GLOBAL_HOOK__;
}
} catch (e) {
// eslint-disable-next-line no-console
console.warn('unable to connect to top frame for connecting dev tools');
}
window.onerror = function onerror(message, source, line, column, err) {
if (window.CONFIG_TYPE !== 'DEVELOPMENT') return;
// eslint-disable-next-line no-var, vars-on-top
var xhr = new window.XMLHttpRequest();
xhr.open('POST', '/runtime-error');
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(
JSON.stringify({
/* eslint-disable object-shorthand */
message: message,
source: source,
line: line,
column: column,
error: err && { message: err.message, name: err.name, stack: err.stack },
origin: 'manager',
/* eslint-enable object-shorthand */
})
);
};</script><style>#root[hidden],
#docs-root[hidden] {
display: none !important;
}</style></head><body><div id="root"></div><div id="docs-root"></div><script>window['CONFIG_TYPE'] = "PRODUCTION";
window['LOGLEVEL'] = "info";
window['FEATURES'] = {"postcss":true,"emotionAlias":false,"warnOnLegacyHierarchySeparator":true,"buildStoriesJson":false,"storyStoreV7":false,"modernInlineRender":false,"breakingChangesV7":false,"interactionsDebugger":false,"babelModeV7":false,"argTypeTargetsV7":false,"previewMdx2":false};
window['DOCS_MODE'] = false;</script><script src="runtime~main.8ab76b1af44e6332b6fc.manager.bundle.js"></script><script src="vendors~main.59133444ce5b3a186e53.manager.bundle.js"></script><script src="main.5ff01ce9b2f269a4e646.manager.bundle.js"></script></body></html>

View File

Binary file not shown.

View File

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{455:function(module,exports,__webpack_require__){__webpack_require__(456),__webpack_require__(822),__webpack_require__(826),__webpack_require__(827),__webpack_require__(823),__webpack_require__(820),__webpack_require__(819),__webpack_require__(821),__webpack_require__(818),__webpack_require__(824),__webpack_require__(825),module.exports=__webpack_require__(817)},502:function(module,exports){}},[[455,3,4]]]);

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1 +0,0 @@
{"generatedAt":1656373830741,"builder":{"name":"webpack4"},"hasCustomBabel":false,"hasCustomWebpack":false,"hasStaticDirs":true,"hasStorybookEslint":true,"refCount":0,"packageManager":{"type":"npm","version":"8.11.0"},"storybookVersion":"6.5.9","language":"typescript","storybookPackages":{"@storybook/addon-actions":{"version":"6.5.9"},"@storybook/builder-webpack4":{"version":"6.5.9"},"@storybook/manager-webpack4":{"version":"6.5.9"},"@storybook/react":{"version":"6.5.9"},"@storybook/testing-library":{"version":"0.0.11"},"eslint-plugin-storybook":{"version":"0.5.13"}},"framework":{"name":"react"},"addons":{"@storybook/addon-links":{"version":"6.5.9"},"@storybook/addon-essentials":{"version":"6.5.9"},"@storybook/addon-interactions":{"version":"6.5.9"}}}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1 +0,0 @@
!function(modules){function webpackJsonpCallback(data){for(var moduleId,chunkId,chunkIds=data[0],moreModules=data[1],executeModules=data[2],i=0,resolves=[];i<chunkIds.length;i++)chunkId=chunkIds[i],Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]&&resolves.push(installedChunks[chunkId][0]),installedChunks[chunkId]=0;for(moduleId in moreModules)Object.prototype.hasOwnProperty.call(moreModules,moduleId)&&(modules[moduleId]=moreModules[moduleId]);for(parentJsonpFunction&&parentJsonpFunction(data);resolves.length;)resolves.shift()();return deferredModules.push.apply(deferredModules,executeModules||[]),checkDeferredModules()}function checkDeferredModules(){for(var result,i=0;i<deferredModules.length;i++){for(var deferredModule=deferredModules[i],fulfilled=!0,j=1;j<deferredModule.length;j++){var depId=deferredModule[j];0!==installedChunks[depId]&&(fulfilled=!1)}fulfilled&&(deferredModules.splice(i--,1),result=__webpack_require__(__webpack_require__.s=deferredModule[0]))}return result}var installedModules={},installedChunks={3:0},deferredModules=[];function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[],installedChunkData=installedChunks[chunkId];if(0!==installedChunkData)if(installedChunkData)promises.push(installedChunkData[2]);else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var onScriptComplete,script=document.createElement("script");script.charset="utf-8",script.timeout=120,__webpack_require__.nc&&script.setAttribute("nonce",__webpack_require__.nc),script.src=function jsonpScriptSrc(chunkId){return __webpack_require__.p+""+({}[chunkId]||chunkId)+"."+{0:"f3c14cfa225a4c40c8ba",1:"44288d676d5217e7dfaf",5:"c79123fa7941ae9f0387",6:"2d406554b9c913afb448",7:"480c60c60f21bf435941",8:"6c22300703a58b9d6b46",9:"5e61b7f1b71ff49bf163"}[chunkId]+".manager.bundle.js"}(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null,clearTimeout(timeout);var chunk=installedChunks[chunkId];if(0!==chunk){if(chunk){var errorType=event&&("load"===event.type?"missing":event.type),realSrc=event&&event.target&&event.target.src;error.message="Loading chunk "+chunkId+" failed.\n("+errorType+": "+realSrc+")",error.name="ChunkLoadError",error.type=errorType,error.request=realSrc,chunk[1](error)}installedChunks[chunkId]=void 0}};var timeout=setTimeout((function(){onScriptComplete({type:"timeout",target:script})}),12e4);script.onerror=script.onload=onScriptComplete,document.head.appendChild(script)}return Promise.all(promises)},__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{enumerable:!0,get:getter})},__webpack_require__.r=function(exports){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},__webpack_require__.t=function(value,mode){if(1&mode&&(value=__webpack_require__(value)),8&mode)return value;if(4&mode&&"object"==typeof value&&value&&value.__esModule)return value;var ns=Object.create(null);if(__webpack_require__.r(ns),Object.defineProperty(ns,"default",{enumerable:!0,value:value}),2&mode&&"string"!=typeof value)for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module.default}:function getModuleExports(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__.oe=function(err){throw console.error(err),err};var jsonpArray=window.webpackJsonp=window.webpackJsonp||[],oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback,jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;checkDeferredModules()}([]);

View File

@@ -1 +0,0 @@
!function(modules){function webpackJsonpCallback(data){for(var moduleId,chunkId,chunkIds=data[0],moreModules=data[1],executeModules=data[2],i=0,resolves=[];i<chunkIds.length;i++)chunkId=chunkIds[i],Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]&&resolves.push(installedChunks[chunkId][0]),installedChunks[chunkId]=0;for(moduleId in moreModules)Object.prototype.hasOwnProperty.call(moreModules,moduleId)&&(modules[moduleId]=moreModules[moduleId]);for(parentJsonpFunction&&parentJsonpFunction(data);resolves.length;)resolves.shift()();return deferredModules.push.apply(deferredModules,executeModules||[]),checkDeferredModules()}function checkDeferredModules(){for(var result,i=0;i<deferredModules.length;i++){for(var deferredModule=deferredModules[i],fulfilled=!0,j=1;j<deferredModule.length;j++){var depId=deferredModule[j];0!==installedChunks[depId]&&(fulfilled=!1)}fulfilled&&(deferredModules.splice(i--,1),result=__webpack_require__(__webpack_require__.s=deferredModule[0]))}return result}var installedModules={},installedChunks={4:0},deferredModules=[];function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[],installedChunkData=installedChunks[chunkId];if(0!==installedChunkData)if(installedChunkData)promises.push(installedChunkData[2]);else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var onScriptComplete,script=document.createElement("script");script.charset="utf-8",script.timeout=120,__webpack_require__.nc&&script.setAttribute("nonce",__webpack_require__.nc),script.src=function jsonpScriptSrc(chunkId){return __webpack_require__.p+""+({}[chunkId]||chunkId)+"."+{0:"233bd6e2",1:"7e5fbb8d",2:"89943a03",6:"e2fef599",7:"15129502",8:"b569e86b",9:"1e553722"}[chunkId]+".iframe.bundle.js"}(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null,clearTimeout(timeout);var chunk=installedChunks[chunkId];if(0!==chunk){if(chunk){var errorType=event&&("load"===event.type?"missing":event.type),realSrc=event&&event.target&&event.target.src;error.message="Loading chunk "+chunkId+" failed.\n("+errorType+": "+realSrc+")",error.name="ChunkLoadError",error.type=errorType,error.request=realSrc,chunk[1](error)}installedChunks[chunkId]=void 0}};var timeout=setTimeout((function(){onScriptComplete({type:"timeout",target:script})}),12e4);script.onerror=script.onload=onScriptComplete,document.head.appendChild(script)}return Promise.all(promises)},__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{enumerable:!0,get:getter})},__webpack_require__.r=function(exports){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},__webpack_require__.t=function(value,mode){if(1&mode&&(value=__webpack_require__(value)),8&mode)return value;if(4&mode&&"object"==typeof value&&value&&value.__esModule)return value;var ns=Object.create(null);if(__webpack_require__.r(ns),Object.defineProperty(ns,"default",{enumerable:!0,value:value}),2&mode&&"string"!=typeof value)for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module.default}:function getModuleExports(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__.oe=function(err){throw console.error(err),err};var jsonpArray=window.webpackJsonp=window.webpackJsonp||[],oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback,jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;checkDeferredModules()}([]);

View File

Binary file not shown.

View File

Binary file not shown.

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,106 +0,0 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
/*!
* https://github.com/es-shims/es5-shim
* @license es5-shim Copyright 2009-2020 by contributors, MIT License
* see https://github.com/es-shims/es5-shim/blob/master/LICENSE
*/
/*!
* https://github.com/paulmillr/es6-shim
* @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
* and contributors, MIT License
* es6-shim: v0.35.4
* see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
* Details and documentation:
* https://github.com/paulmillr/es6-shim/
*/
/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*!
* isobject <https://github.com/jonschlinkert/isobject>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
//! stable.js 0.1.8, https://github.com/Two-Screen/stable
//! © 2018 Angry Bytes and contributors. MIT licensed.

View File

@@ -1 +0,0 @@
{"version":3,"file":"vendors~main.16f7136c.iframe.bundle.js","sources":[],"mappings":";A","sourceRoot":""}

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,104 +0,0 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
* Fuse.js v3.6.1 - Lightweight fuzzy-search (http://fusejs.io)
*
* Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
* All Rights Reserved. Apache Software License 2.0
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
/*!
* https://github.com/es-shims/es5-shim
* @license es5-shim Copyright 2009-2020 by contributors, MIT License
* see https://github.com/es-shims/es5-shim/blob/master/LICENSE
*/
/*!
* https://github.com/paulmillr/es6-shim
* @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
* and contributors, MIT License
* es6-shim: v0.35.4
* see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
* Details and documentation:
* https://github.com/paulmillr/es6-shim/
*/
/*!
* isobject <https://github.com/jonschlinkert/isobject>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/*! store2 - v2.13.1 - 2021-12-20
* Copyright (c) 2021 Nathan Bubna; Licensed (MIT OR GPL-3.0) */
/**
* React Router DOM v6.0.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/**
* React Router v6.0.2
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

25
examples/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*storybook.log

View File

@@ -0,0 +1,74 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
import path from 'path';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/preset-create-react-app',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
staticDirs: ['../public'],
webpackFinal: async (config) => {
if (!config.resolve) config.resolve = {};
if (!config.resolve.alias) config.resolve.alias = {};
config.resolve.alias['react'] = path.resolve(
__dirname,
'../../node_modules/react'
);
config.resolve.alias['react-dom'] = path.resolve(
__dirname,
'../../node_modules/react-dom'
);
config.resolve.alias['@rive-app/react-canvas'] = path.resolve(
__dirname,
'../../'
);
config.resolve.alias['@rive-app/react-canvas-lite'] = path.resolve(
__dirname,
'../../'
);
config.resolve.alias['@rive-app/react-webgl'] = path.resolve(
__dirname,
'../../'
);
config.resolve.alias['@rive-app/react-webgl2'] = path.resolve(
__dirname,
'../../'
);
config.module?.rules?.push({
test: /\.(ts|tsx|js|jsx)$/,
include: [
path.resolve(__dirname, '../src'),
path.resolve(__dirname, '../../'),
],
use: {
loader: require.resolve('babel-loader'),
options: {
presets: [
require.resolve('@babel/preset-env'),
require.resolve('@babel/preset-react'),
require.resolve('@babel/preset-typescript'),
],
},
},
});
config.watchOptions = {
ignored: /node_modules/,
poll: 1000,
aggregateTimeout: 300,
};
return config;
},
};
export default config;

View File

@@ -0,0 +1,16 @@
import type { Preview } from '@storybook/react';
import './stories.css';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;

View File

@@ -0,0 +1,7 @@
html, body {
height: 100%;
}
#storybook-root {
height: 100%;
}

56
examples/package.json Normal file
View File

@@ -0,0 +1,56 @@
{
"name": "examples",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.126",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"test-storybook": "test-storybook"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"plugin:storybook/recommended"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-onboarding": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/preset-create-react-app": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-webpack5": "^8.6.12",
"@storybook/test": "^8.6.12",
"@storybook/test-runner": "^0.22.0",
"eslint-plugin-storybook": "^0.12.0",
"storybook": "^8.6.12",
"webpack": "^5.99.6"
}
}

View File

Binary file not shown.

BIN
examples/public/avatars.riv Normal file
View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

BIN
examples/public/rating.riv Normal file
View File

Binary file not shown.

BIN
examples/public/stocks.riv Normal file
View File

Binary file not shown.

View File

@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import DataBinding from './DataBinding';
const meta = {
title: 'DataBinding',
component: DataBinding,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof DataBinding>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,147 @@
import React, { useEffect } from 'react';
import {
useRive,
useViewModel,
useViewModelInstance,
useViewModelInstanceColor,
useViewModelInstanceNumber,
useViewModelInstanceString,
useViewModelInstanceEnum,
useViewModelInstanceTrigger,
} from '@rive-app/react-webgl2';
const randomValue = () => Math.random() * 200 - 100;
const DataBinding = () => {
const { rive, RiveComponent } = useRive({
src: 'stocks.riv',
artboard: 'Main',
stateMachines: 'State Machine 1',
autoplay: true,
autoBind: false,
});
// Get the default instance of the view model
const viewModel = useViewModel(rive, { name: 'Dashboard' });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
// Get the view model instance properties
const { setValue: setTitle } = useViewModelInstanceString(
'title',
viewModelInstance
);
const { setValue: setLogoShape } = useViewModelInstanceEnum(
'logoShape',
viewModelInstance
);
const { setValue: setRootColor } = useViewModelInstanceColor(
'rootColor',
viewModelInstance
);
const { trigger: triggerSpinLogo } = useViewModelInstanceTrigger(
'triggerSpinLogo',
viewModelInstance
);
useViewModelInstanceTrigger('triggerButton', viewModelInstance, {
onTrigger: () => console.log('Button Triggered!'),
});
// Apple Values
const { setValue: setAppleName } = useViewModelInstanceString(
'apple/name',
viewModelInstance
);
const { setValue: setAppleStockChange } = useViewModelInstanceNumber(
'apple/stockChange',
viewModelInstance
);
const { value: appleColor } = useViewModelInstanceColor(
'apple/currentColor',
viewModelInstance
);
// Apple Values
const { setValue: setMicrosoftName } = useViewModelInstanceString(
'microsoft/name',
viewModelInstance
);
const { setValue: setMicrosoftStockChange } = useViewModelInstanceNumber(
'microsoft/stockChange',
viewModelInstance
);
// Tesla Values
const { setValue: setTeslaName } = useViewModelInstanceString(
'tesla/name',
viewModelInstance
);
const { setValue: setTeslaStockChange } = useViewModelInstanceNumber(
'tesla/stockChange',
viewModelInstance
);
useEffect(() => {
// Set initial values for the view model
if (
setTitle &&
setLogoShape &&
setRootColor &&
setAppleName &&
setMicrosoftName &&
setTeslaName
) {
setTitle('Rive Stocks Dashboard');
setLogoShape('triangle');
setRootColor(parseInt('ffc0ffee', 16));
setAppleName('AAPL');
setMicrosoftName('MSFT');
setTeslaName('TSLA');
}
// randomly generate stock values every 2 seconds
const interval = setInterval(() => {
const appleValue = randomValue();
const microsoftValue = randomValue();
const teslaValue = randomValue();
setAppleStockChange(appleValue);
setMicrosoftStockChange(microsoftValue);
setTeslaStockChange(teslaValue);
// If all the stock values are either all positive or all negative, spin the logo
if (
(appleValue > 0 && microsoftValue > 0 && teslaValue > 0) ||
(appleValue < 0 && microsoftValue < 0 && teslaValue < 0)
) {
triggerSpinLogo();
}
}, 2000);
return () => clearInterval(interval);
}, [
setTitle,
setLogoShape,
setRootColor,
setAppleName,
setMicrosoftName,
setTeslaName,
setAppleStockChange,
setMicrosoftStockChange,
setTeslaStockChange,
triggerSpinLogo,
]);
// listen for changes to the AAPL color and log them
useEffect(() => {
if (appleColor) {
console.log('Apple color changed:', appleColor);
}
}, [appleColor]);
return <RiveComponent />;
};
export default DataBinding;

View File

@@ -0,0 +1,527 @@
import React, { useEffect } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { within, expect, waitFor, userEvent } from '@storybook/test';
import { StringPropertyTest, NumberPropertyTest, BooleanPropertyTest, ColorPropertyTest, EnumPropertyTest, NestedViewModelTest, TriggerPropertyTest, PersonForm, PersonInstances, ImagePropertyTest, TodoListTest, ArtboardPropertyTest } from './DataBindingTests';
const meta: Meta = {
title: 'Tests/DataBinding',
parameters: {
layout: 'centered',
},
};
export default meta;
export const StringPropertyStory: StoryObj = {
name: 'String Property',
render: () => <StringPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('name-value')).toBeTruthy();
}, { timeout: 3000 });
const nameInput = canvas.getByTestId<HTMLInputElement>('name-input');
await userEvent.clear(nameInput);
// Wait for the input to be cleared
await waitFor(() => {
expect(nameInput.value).toBe('');
}, { timeout: 1000 });
await userEvent.click(nameInput);
await userEvent.paste('Test User');
await waitFor(() => {
expect(nameInput.value).toBe('Test User');
}, { timeout: 2000 });
await waitFor(() => {
expect(canvas.getByTestId('name-value').textContent).toBe('Test User');
}, { timeout: 2000 });
}
};
export const NumberPropertyStory: StoryObj = {
name: 'Number Property',
render: () => <NumberPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('age-value')).toBeTruthy();
}, { timeout: 2000 });
const ageInput = canvas.getByTestId<HTMLInputElement>('age-input');
const currentValue = ageInput.value;
expect(currentValue).toBe('23');
await userEvent.click(ageInput);
await userEvent.clear(ageInput);
await waitFor(() => {
expect(ageInput.value).toBe('0'); // This is a hack to wait for the input to be cleared
}, { timeout: 1000 });
await userEvent.paste('42');
await waitFor(() => {
expect(canvas.getByTestId('age-value').textContent).toBe('42');
}, { timeout: 2000 });
}
};
export const BooleanPropertyStory: StoryObj = {
name: 'Boolean Property',
render: () => <BooleanPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('terms-value')).toBeTruthy();
}, { timeout: 2000 });
const termsCheckbox = canvas.getByTestId<HTMLInputElement>('terms-checkbox');
expect(termsCheckbox.checked).toBe(false);
expect(canvas.getByTestId('terms-value').textContent).toBe('false');
await userEvent.click(termsCheckbox);
// Verify terms update
await waitFor(() => {
expect(canvas.getByTestId('terms-value').textContent).toBe('true');
});
}
};
export const ColorPropertyStory: StoryObj = {
name: 'Color Property',
render: () => <ColorPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load and the component to render
await waitFor(() => {
expect(canvas.getByTestId('color-value')).toBeTruthy();
expect(canvas.getByTestId('set-color-red')).toBeTruthy();
expect(canvas.getByTestId('set-color-blue')).toBeTruthy();
}, { timeout: 5000 });
const numberValueDiv = canvas.getByTestId('number-value');
const hexValueDiv = canvas.getByTestId('hex-value');
// Verify initial state is red
await waitFor(() => {
expect(hexValueDiv.textContent).toContain('Hex value: #ce2323');
expect(numberValueDiv.textContent).toContain('Number value: -3267805');
});
// Change color to Blue ---
const blueButton = canvas.getByTestId('set-color-blue');
await userEvent.click(blueButton);
// Verify Blue State
await waitFor(() => {
expect(numberValueDiv.textContent).toContain('Number value: -16776961');
expect(hexValueDiv.textContent).toContain('Hex value: #0000ff');
});
// Change color back to Red ---
const redButton = canvas.getByTestId('set-color-red');
await userEvent.click(redButton);
// Verify Red State
await waitFor(() => {
expect(numberValueDiv.textContent).toContain('Number value: -65536');
expect(hexValueDiv.textContent).toContain('Hex value: #ff0000');
});
}
};
export const EnumPropertyStory: StoryObj = {
name: 'Enum Property',
render: () => <EnumPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('country-value')).toBeTruthy();
});
// Wait for options to be loaded
await waitFor(() => {
const countrySelect = canvas.getByTestId<HTMLSelectElement>('country-select');
return countrySelect.options.length > 0;
});
const countrySelect = canvas.getByTestId<HTMLSelectElement>('country-select');
// Verify that the dropdown contains usa, japan, and canada
const optionValues = Array.from(countrySelect.options).map(option => option.value);
expect(optionValues).toContain('usa');
expect(optionValues).toContain('japan');
expect(optionValues).toContain('canada');
const currentValue = countrySelect.value;
expect(currentValue).toBe('usa');
let optionToSelect = 'japan';
await userEvent.selectOptions(countrySelect, optionToSelect);
await waitFor(() => {
expect(canvas.getByTestId('country-value').textContent).toBe(optionToSelect);
});
}
};
export const NestedViewModelStory: StoryObj = {
name: 'Nested ViewModel Property',
render: () => <NestedViewModelTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('drink-type-value')).toBeTruthy();
});
// Wait for options to be loaded
await waitFor(() => {
const drinkTypeSelect = canvas.getByTestId<HTMLSelectElement>('drink-type-select');
return drinkTypeSelect.options.length > 0;
}, { timeout: 2000 });
const drinkTypeSelect = canvas.getByTestId<HTMLSelectElement>('drink-type-select');
const optionValues = Array.from(drinkTypeSelect.options).map(option => option.value);
expect(optionValues).toContain('Coffee');
expect(optionValues).toContain('Tea');
expect(drinkTypeSelect.value).toBe('Tea');
let optionToSelect = 'Coffee';
await userEvent.selectOptions(drinkTypeSelect, optionToSelect);
await waitFor(() => {
expect(canvas.getByTestId('drink-type-value').textContent).toBe(optionToSelect);
});
}
};
export const TriggerPropertyStory: StoryObj = {
name: 'Trigger Property',
render: () => <TriggerPropertyTest src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('submit-button')).toBeTruthy();
}, { timeout: 2000 });
expect(canvas.getByTestId('callback-triggered').textContent).toContain('none');
// Trigger submit action
await userEvent.click(canvas.getByTestId('submit-button'));
await waitFor(() => {
expect(canvas.getByTestId('callback-triggered').textContent).toContain('submit-callback');
});
await userEvent.click(canvas.getByTestId('reset-button'));
// Verify onTrigger callback works for reset
await waitFor(() => {
expect(canvas.getByTestId('callback-triggered').textContent).toContain('reset-callback');
});
}
};
export const PersonInstancesStory: StoryObj = {
name: 'Person Instances',
render: () => <PersonInstances src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('instance-name')).toBeTruthy();
expect(canvas.getByTestId('select-jane')).toBeTruthy();
expect(canvas.getByTestId('select-default')).toBeTruthy();
}, { timeout: 2000 });
// Initially should show Steve
expect(canvas.getByTestId('instance-name').textContent).toContain('Steve');
// Switch to Jane
const janeButton = canvas.getByTestId('select-jane');
await userEvent.click(janeButton);
// Verify instance changed to Jane
await waitFor(() => {
expect(canvas.getByTestId('instance-name').textContent).toContain('Jane');
});
// Switch to Default instance
const defaultButton = canvas.getByTestId('select-default');
await userEvent.click(defaultButton);
// Verify instance changed to Default
await waitFor(() => {
expect(canvas.getByTestId('instance-name').textContent).toContain('Default');
});
// Switch back to Steve
const steveButton = canvas.getByTestId('select-steve');
await userEvent.click(steveButton);
// Verify instance changed back to Steve
await waitFor(() => {
expect(canvas.getByTestId('instance-name').textContent).toContain('Steve');
});
}
};
// A configurable form story, so we can test all the properties at once
export const PersonFormStory: StoryObj = {
name: 'Complete Person Form',
render: () => <PersonForm src="person_databinding_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('name-value')).toBeTruthy();
}, { timeout: 2000 });
// Update name
const nameInput = canvas.getByTestId('name-input');
await userEvent.clear(nameInput);
await userEvent.type(nameInput, 'Test User');
// Update age
const ageInput = canvas.getByTestId('age-input');
await userEvent.clear(ageInput);
await userEvent.type(ageInput, '42');
// Toggle terms agreement
const termsCheckbox = canvas.getByTestId('terms-checkbox');
await userEvent.click(termsCheckbox);
// Change color
const colorButton = canvas.getByTestId('set-color-red');
await userEvent.click(colorButton);
// Change country
const countrySelect = canvas.getByTestId<HTMLSelectElement>('country-select');
await userEvent.selectOptions(countrySelect, 'japan');
// Change drink type
const drinkTypeSelect = canvas.getByTestId<HTMLSelectElement>('drink-type-select');
await userEvent.selectOptions(drinkTypeSelect, 'Coffee');
// Submit the form
const submitButton = canvas.getByTestId('submit-button');
await userEvent.click(submitButton);
}
};
export const ImagePropertyStory: StoryObj = {
name: 'Image Property',
render: () => <ImagePropertyTest src="image_db_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('load-random-image')).toBeTruthy();
expect(canvas.getByTestId('clear-image')).toBeTruthy();
}, { timeout: 3000 });
const loadImageButton = canvas.getByTestId('load-random-image');
const clearImageButton = canvas.getByTestId('clear-image');
expect(canvas.queryByTestId('current-image-url')).toBeNull();
// Load a random image
await userEvent.click(loadImageButton);
// Wait for the image to load and URL to appear
await waitFor(() => {
expect(canvas.getByTestId('current-image-url')).toBeTruthy();
}, { timeout: 5000 });
// Verify the image URL is displayed
const imageUrlElement = canvas.getByTestId('current-image-url');
expect(imageUrlElement.textContent).toContain('Current image: https://picsum.photos');
// Clear the image
await userEvent.click(clearImageButton);
// Load another image to test it works multiple times
await userEvent.click(loadImageButton);
// Wait for the new image to load
await waitFor(() => {
expect(canvas.getByTestId('current-image-url')).toBeTruthy();
}, { timeout: 5000 });
}
};
export const TodoListStory: StoryObj = {
name: 'Todo List Property',
render: () => <TodoListTest src="db_list_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('list-length')).toBeTruthy();
}, { timeout: 3000 });
const initialLengthText = canvas.getByTestId('list-length').textContent;
const initialCount = parseInt(initialLengthText?.match(/Items: (\d+)/)?.[1] || '0');
// Test 1: addInstance - Add item to end
const addButton = canvas.getByTestId('add-item-button');
await userEvent.click(addButton);
await waitFor(() => {
expect(canvas.getByTestId('list-length').textContent).toContain(`Items: ${initialCount + 1}`);
});
// Test 2: addInstanceAt - Add item at specific index (if we have items)
if (initialCount > 0) {
const addAtButton = canvas.getByTestId('add-item-at-button');
await userEvent.click(addAtButton);
await waitFor(() => {
expect(canvas.getByTestId('list-length').textContent).toContain(`Items: ${initialCount + 2}`);
});
}
// Test 3: getInstanceAt - Interact with specific items
const currentCount = initialCount + (initialCount > 0 ? 2 : 1);
if (currentCount > 0) {
await waitFor(() => {
expect(canvas.getByTestId('todo-item-0')).toBeTruthy();
});
// Edit the first item
const todoText = canvas.getByTestId('todo-text-0');
await userEvent.clear(todoText);
// Wait for the input to be cleared to avoid issues with autocomplete
await waitFor(() => {
expect((todoText as HTMLInputElement).value).toBe('');
}, { timeout: 2000 });
await userEvent.click(todoText);
await userEvent.paste('Test Item');
await waitFor(() => {
expect(canvas.getByTestId('todo-text-value-0').textContent).toContain('Test Item');
}, { timeout: 3000 });
}
// Test 4: swap - Swap first two items
if (currentCount >= 2) {
const firstText = canvas.getByTestId<HTMLInputElement>('todo-text-0').value;
const secondText = canvas.getByTestId<HTMLInputElement>('todo-text-1').value;
const swapButton = canvas.getByTestId('swap-button');
await userEvent.click(swapButton);
await waitFor(() => {
expect(canvas.getByTestId('todo-text-0')).toHaveValue(secondText);
expect(canvas.getByTestId('todo-text-1')).toHaveValue(firstText);
}, { timeout: 3000 });
}
// Test 5: removeInstance - Remove by instance reference
if (currentCount > 0) {
const removeInstanceButton = canvas.getByTestId('remove-instance-button');
await userEvent.click(removeInstanceButton);
await waitFor(() => {
expect(canvas.getByTestId('list-length').textContent).toContain(`Items: ${currentCount - 1}`);
}, { timeout: 3000 });
}
// Test 6: removeInstanceAt - Remove by index
const afterRemoveInstance = currentCount > 0 ? currentCount - 1 : 0;
if (afterRemoveInstance > 0) {
const removeIndexButton = canvas.getByTestId('remove-index-button');
await userEvent.click(removeIndexButton);
await waitFor(() => {
expect(canvas.getByTestId('list-length').textContent).toContain(`Items: ${afterRemoveInstance - 1}`);
}, { timeout: 3000 });
}
}
};
export const ArtboardPropertyStory: StoryObj = {
name: 'Artboard Property',
render: () => <ArtboardPropertyTest src="artboard_db_test.riv" />,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Wait for the Rive file to load
await waitFor(() => {
expect(canvas.getByTestId('set-artboard1-blue')).toBeTruthy();
expect(canvas.getByTestId('set-artboard1-red')).toBeTruthy();
expect(canvas.getByTestId('set-artboard1-green')).toBeTruthy();
}, { timeout: 3000 });
// Initially should show None
expect(canvas.getByTestId('artboard1-current').textContent).toBe('Current: None');
expect(canvas.getByTestId('artboard2-current').textContent).toBe('Current: None');
// Set artboard 1 to blue
await userEvent.click(canvas.getByTestId('set-artboard1-blue'));
await waitFor(() => {
expect(canvas.getByTestId('artboard1-current').textContent).toBe('Current: ArtboardBlue');
});
// Set artboard 2 to red
await userEvent.click(canvas.getByTestId('set-artboard2-red'));
await waitFor(() => {
expect(canvas.getByTestId('artboard2-current').textContent).toBe('Current: ArtboardRed');
});
// Switch artboard 1 to green
await userEvent.click(canvas.getByTestId('set-artboard1-green'));
await waitFor(() => {
expect(canvas.getByTestId('artboard1-current').textContent).toBe('Current: ArtboardGreen');
});
// Switch artboard 2 to blue
await userEvent.click(canvas.getByTestId('set-artboard2-blue'));
await waitFor(() => {
expect(canvas.getByTestId('artboard2-current').textContent).toBe('Current: ArtboardBlue');
});
}
};

View File

@@ -0,0 +1,881 @@
import React, { useEffect, useState } from 'react';
import Rive, {
useRive,
useViewModel,
useViewModelInstance,
useViewModelInstanceBoolean,
useViewModelInstanceString,
useViewModelInstanceNumber,
useViewModelInstanceEnum,
useViewModelInstanceColor,
useViewModelInstanceTrigger,
useViewModelInstanceImage,
decodeImage,
ViewModelInstance,
useViewModelInstanceList,
useViewModelInstanceArtboard
} from '@rive-app/react-webgl2';
export const StringPropertyTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: name, setValue: setName } = useViewModelInstanceString('name', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
Name:
<input
data-testid="name-input"
type="text"
value={name || ''}
onChange={(e) => setName(e.target.value)}
autoFocus={false}
/>
</label>
<div data-testid="name-value">{name}</div>
</div>
)}
</div>
);
};
export const NumberPropertyTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: age, setValue: setAge } = useViewModelInstanceNumber('age', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
Age:
<input
data-testid="age-input"
type="number"
value={age ?? 0}
onChange={(e) => setAge(Number(e.target.value))}
autoFocus={false}
/>
</label>
<div data-testid="age-value">{age}</div>
</div>
)}
</div>
);
};
export const BooleanPropertyTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: agreedToTerms, setValue: setAgreedToTerms } = useViewModelInstanceBoolean('agreedToTerms', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
<input
data-testid="terms-checkbox"
type="checkbox"
checked={agreedToTerms ?? false}
onChange={(e) => setAgreedToTerms(e.target.checked)}
/>
Agree to Terms
</label>
<div data-testid="terms-value">{agreedToTerms ? 'true' : 'false'}</div>
</div>
)}
</div>
);
};
const colorNumberToHexString = (colorNum: number | null) => {
if (colorNum === null) {
return 'N/A';
}
const unsignedInt = colorNum >>> 0;
const r = (unsignedInt >> 16) & 0xff;
const g = (unsignedInt >> 8) & 0xff;
const b = unsignedInt & 0xff;
const toHex = (c: number) => c.toString(16).padStart(2, '0');
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
};
export const ColorPropertyTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: colorNum, setValue: setColor, setRgb } = useViewModelInstanceColor('favColor', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
Favorite Color:
<div data-testid="color-value" style={{
backgroundColor: typeof colorNum === 'string' ? colorNum : colorNumberToHexString(colorNum),
width: '20px',
height: '20px',
display: 'inline-block',
marginLeft: '10px'
}}></div>
<div data-testid="number-value">
Number value: {typeof colorNum === 'number' ? colorNum : 'N/A'}
</div>
<div data-testid="hex-value">
Hex value: {colorNumberToHexString(colorNum)}
</div>
</label>
<button
data-testid="set-color-red"
type="button"
onClick={() => setRgb(255, 0, 0)}
>
Red
</button>
<button
data-testid="set-color-blue"
type="button"
onClick={() => setRgb(0, 0, 255)}
>
Blue
</button>
</div>
)}
</div>
);
};
export const EnumPropertyTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: country, setValue: setCountry, values: countries } = useViewModelInstanceEnum('country', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
Country:
<select
data-testid="country-select"
value={country || ''}
onChange={(e) => setCountry(e.target.value)}
>
{countries.map(c => (
<option key={c} value={c}>{c}</option>
))}
</select>
</label>
<div data-testid="country-value">{country}</div>
</div>
)}
</div>
);
};
export const NestedViewModelTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: true,
stateMachines: "State Machine 1",
});
const { value: drinkType, setValue: setDrinkType, values: drinkTypes } = useViewModelInstanceEnum('favDrink/type', rive?.viewModelInstance);
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<label>
Favorite Drink Type:
<select
data-testid="drink-type-select"
value={drinkType || ''}
onChange={(e) => setDrinkType(e.target.value)}
>
{drinkTypes.map(dt => (
<option key={dt} value={dt}>{dt}</option>
))}
</select>
</label>
<div data-testid="drink-type-value">{drinkType}</div>
</div>
)}
</div>
);
};
export const TriggerPropertyTest = ({ src }: { src: string }) => {
const [callbackTriggered, setCallbackTriggered] = useState('');
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
autoBind: true,
artboard: "Artboard",
stateMachines: "State Machine 1",
});
const { trigger: onFormSubmit } = useViewModelInstanceTrigger('onFormSubmit', rive?.viewModelInstance,
{
onTrigger: () => {
setCallbackTriggered('submit-callback');
}
}
);
const { trigger: onFormReset } = useViewModelInstanceTrigger('onFormReset', rive?.viewModelInstance,
{
onTrigger: () => {
setCallbackTriggered('reset-callback');
}
}
);
const handleSubmit = () => {
onFormSubmit();
};
const handleReset = () => {
onFormReset();
};
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<button data-testid="submit-button" type="button" onClick={handleSubmit}>Submit</button>
<button data-testid="reset-button" type="button" onClick={handleReset}>Reset</button>
<div data-testid="callback-triggered">
Last callback triggered: {callbackTriggered || 'none'}
</div>
</div>
)}
</div>
);
};
export const PersonForm = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
autoBind: true,
artboard: "Artboard",
stateMachines: "State Machine 1",
});
const { value: name, setValue: setName } = useViewModelInstanceString('name', rive?.viewModelInstance);
const { value: age, setValue: setAge } = useViewModelInstanceNumber('age', rive?.viewModelInstance);
const { value: agreedToTerms, setValue: setAgreedToTerms } = useViewModelInstanceBoolean('agreedToTerms', rive?.viewModelInstance);
const { value: colorNum, setRgb } = useViewModelInstanceColor('favColor', rive?.viewModelInstance);
const { value: country, setValue: setCountry, values: countries } = useViewModelInstanceEnum('country', rive?.viewModelInstance);
const { trigger: onFormSubmit } = useViewModelInstanceTrigger('onFormSubmit', rive?.viewModelInstance);
const { trigger: onFormReset } = useViewModelInstanceTrigger('onFormReset', rive?.viewModelInstance);
// Drink properties (nested viewmodel)
const { value: drinkType, setValue: setDrinkType, values: drinkTypes } = useViewModelInstanceEnum('favDrink/type', rive?.viewModelInstance);
const handleReset = () => {
setName('');
setAge(0);
setAgreedToTerms(false);
setRgb(0, 0, 0);
setCountry(countries[0]);
setDrinkType(drinkTypes[0]);
onFormReset();
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onFormSubmit();
};
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<form onSubmit={handleSubmit}>
<div>
<label>
Name:
<input
data-testid="name-input"
type="text"
value={name || ''}
onChange={(e) => setName(e.target.value)}
/>
</label>
<div data-testid="name-value">{name}</div>
</div>
<div>
<label>
Age:
<input
data-testid="age-input"
type="number"
value={age || 0}
onChange={(e) => setAge(Number(e.target.value))}
/>
</label>
<div data-testid="age-value">{age}</div>
</div>
<div>
<label>
<input
data-testid="terms-checkbox"
type="checkbox"
checked={agreedToTerms || false}
onChange={(e) => setAgreedToTerms(e.target.checked)}
/>
Agree to Terms
</label>
<div data-testid="terms-value">{agreedToTerms ? 'true' : 'false'}</div>
</div>
<div>
<label>
Favorite Color:
<div data-testid="color-value" style={{
backgroundColor: colorNumberToHexString(colorNum),
width: '20px',
height: '20px',
display: 'inline-block',
marginLeft: '10px'
}}></div>
</label>
<button
data-testid="set-color-red"
type="button"
onClick={() => setRgb(255, 0, 0)}
>
Red
</button>
<button
data-testid="set-color-blue"
type="button"
onClick={() => setRgb(0, 0, 255)}
>
Blue
</button>
</div>
<div>
<label>
Country:
<select
data-testid="country-select"
value={country || ''}
onChange={(e) => setCountry(e.target.value)}
>
{countries.map(c => (
<option key={c} value={c}>{c}</option>
))}
</select>
</label>
<div data-testid="country-value">{country}</div>
</div>
<div>
<label>
Favorite Drink Type:
<select
data-testid="drink-type-select"
value={drinkType || ''}
onChange={(e) => setDrinkType(e.target.value)}
>
{drinkTypes.map(dt => (
<option key={dt} value={dt}>{dt}</option>
))}
</select>
</label>
<div data-testid="drink-type-value">{drinkType}</div>
</div>
<div>
<button data-testid="submit-button" type="submit">Submit</button>
<button data-testid="reset-button" type="button" onClick={handleReset}>Reset</button>
</div>
</form>
)}
</div>
);
};
// Component to demonstrate different viewmodel instances
export const PersonInstances = ({ src }: { src: string }) => {
const [activeInstance, setActiveInstance] = useState('Steve');
const [useDefaultInstance, setUseDefaultInstance] = useState(false);
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
stateMachines: "State Machine 1",
});
const viewModel = useViewModel(rive, { name: 'PersonViewModel' });
const params = useDefaultInstance ? { useDefault: true, rive } : { name: activeInstance, rive }
const viewModelInstance = useViewModelInstance(viewModel, params);
const { value: name } = useViewModelInstanceString('name', viewModelInstance);
const { value: age } = useViewModelInstanceNumber('age', viewModelInstance);
const { value: country } = useViewModelInstanceEnum('country', viewModelInstance);
const switchToNamedInstance = (instanceName: string) => {
setActiveInstance(instanceName);
setUseDefaultInstance(false);
};
const switchToDefaultInstance = () => {
setUseDefaultInstance(true);
};
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<button
data-testid="select-steve"
onClick={() => switchToNamedInstance('Steve')}
disabled={!useDefaultInstance && activeInstance === 'Steve'}
>
Steve
</button>
<button
data-testid="select-jane"
onClick={() => switchToNamedInstance('Jane')}
disabled={!useDefaultInstance && activeInstance === 'Jane'}
>
Jane
</button>
<button
data-testid="select-default"
onClick={switchToDefaultInstance}
disabled={useDefaultInstance}
>
Default
</button>
</div>
)}
<div>
<h3 data-testid="instance-name">Instance: {useDefaultInstance ? 'Default' : activeInstance}</h3>
<p data-testid="person-name">Name: {name}</p>
<p data-testid="person-age">Age: {age}</p>
<p data-testid="person-country">Country: {country}</p>
</div>
</div>
);
};
export const ImagePropertyTest = ({ src }: { src: string }) => {
const [currentImageUrl, setCurrentImageUrl] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const { rive, RiveComponent } = useRive({
src,
artboard: "Artboard",
stateMachines: "State Machine 1",
autoplay: true,
autoBind: false,
});
const viewModel = useViewModel(rive, { name: 'Post' });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
const { setValue: setImage } = useViewModelInstanceImage(
'image',
viewModelInstance
);
const loadRandomImage = async () => {
if (!setImage) return;
setIsLoading(true);
try {
const imageUrl = `https://picsum.photos/400/300?random=${Date.now()}`;
setCurrentImageUrl(imageUrl);
const response = await fetch(imageUrl);
const imageBuffer = await response.arrayBuffer();
const decodedImage = await decodeImage(new Uint8Array(imageBuffer));
setImage(decodedImage);
decodedImage.unref();
} catch (error) {
console.error('Failed to load image:', error);
} finally {
setIsLoading(false);
}
};
const clearImage = () => {
if (setImage) {
setImage(null);
setCurrentImageUrl('');
}
};
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '20px' }}>
<div style={{ width: '400px', height: '300px', border: '1px solid #ccc' }}>
<RiveComponent />
</div>
{rive === null ? (
<div data-testid="loading-text">Loading</div>
) : (
<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
<button
onClick={loadRandomImage}
disabled={isLoading}
data-testid="load-random-image"
>
{isLoading ? 'Loading...' : 'Load Random Image'}
</button>
<button
onClick={clearImage}
disabled={isLoading}
data-testid="clear-image"
>
Clear Image
</button>
</div>
)}
{currentImageUrl && (
<div style={{ fontSize: '12px', color: '#666' }}>
<span data-testid="current-image-url">Current image: {currentImageUrl}</span>
</div>
)}
</div>
);
};
// List Property Test
const TodoItemComponent = ({
index,
todoItem
}: {
index: number;
todoItem: ViewModelInstance | null;
}) => {
const { value: text, setValue: setText } = useViewModelInstanceString('text', todoItem);
const { value: isDone, setValue: setIsDone } = useViewModelInstanceBoolean('isDone', todoItem);
if (!todoItem) {
return <div data-testid={`todo-item-${index}`}>Item not found</div>;
}
return (
<div data-testid={`todo-item-${index}`} style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
padding: '8px',
border: '1px solid #ccc',
marginBottom: '4px'
}}>
<input
data-testid={`todo-checkbox-${index}`}
type="checkbox"
checked={isDone ?? false}
onChange={(e) => setIsDone(e.target.checked)}
/>
<input
data-testid={`todo-text-${index}`}
type="text"
value={text || ''}
onChange={(e) => setText(e.target.value)}
style={{ flex: 1 }}
/>
<div data-testid={`todo-text-value-${index}`} style={{ fontSize: '12px', color: '#666' }}>
Text: {text}
</div>
<div data-testid={`todo-done-value-${index}`} style={{ fontSize: '12px', color: '#666' }}>
Done: {isDone ? 'true' : 'false'}
</div>
</div>
);
};
export const TodoListTest = ({ src }: { src: string }) => {
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Artboard",
autoBind: false,
stateMachines: "State Machine 1",
});
const viewModel = useViewModel(rive, { name: 'TodoList' });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
const {
length,
addInstance,
addInstanceAt,
removeInstance,
removeInstanceAt,
getInstanceAt,
swap
} = useViewModelInstanceList('items', viewModelInstance);
const handleAddItem = () => {
const todoItemViewModel = rive?.viewModelByName?.('TodoItem');
if (todoItemViewModel) {
const newTodoItem = todoItemViewModel.instance?.();
if (newTodoItem) {
addInstance(newTodoItem);
}
}
};
const handleAddItemAt = () => {
const todoItemViewModel = rive?.viewModelByName?.('TodoItem');
if (todoItemViewModel && length > 0) {
const newTodoItem = todoItemViewModel.instance?.();
if (newTodoItem) {
addInstanceAt(newTodoItem, 1);
}
}
};
const handleRemoveFirstInstance = () => {
const firstInstance = getInstanceAt(0);
if (firstInstance) {
removeInstance(firstInstance);
}
};
const handleRemoveFirstByIndex = () => {
if (length > 0) {
removeInstanceAt(0);
}
};
const handleSwapItems = () => {
if (length >= 2) {
swap(0, 1);
}
};
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{rive === null ? (
<div data-testid="loading-text">Loading</div>
) : (
<div>
<div data-testid="list-length">Items: {length}</div>
<div style={{ marginBottom: '10px', display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
<button
data-testid="add-item-button"
onClick={handleAddItem}
>
Add Item (End)
</button>
<button
data-testid="add-item-at-button"
onClick={handleAddItemAt}
disabled={length === 0}
>
Add Item at Index 1
</button>
<button
data-testid="remove-instance-button"
onClick={handleRemoveFirstInstance}
disabled={length === 0}
>
Remove First (by Instance)
</button>
<button
data-testid="remove-index-button"
onClick={handleRemoveFirstByIndex}
disabled={length === 0}
>
Remove First (by Index)
</button>
<button
data-testid="swap-button"
onClick={handleSwapItems}
disabled={length < 2}
>
Swap First Two
</button>
</div>
<div data-testid="todo-items">
{Array.from({ length }, (_, index) => (
<TodoItemComponent
key={index}
index={index}
todoItem={getInstanceAt(index)}
/>
))}
</div>
</div>
)}
</div>
);
};
export const ArtboardPropertyTest = ({ src }: { src: string }) => {
const [currentArtboard1, setCurrentArtboard1] = useState<string>('None');
const [currentArtboard2, setCurrentArtboard2] = useState<string>('None');
const { rive, RiveComponent } = useRive({
src,
autoplay: true,
artboard: "Main",
autoBind: true,
stateMachines: "State Machine 1",
});
const { setValue: setArtboard1 } = useViewModelInstanceArtboard('artboard_1', rive?.viewModelInstance);
const { setValue: setArtboard2 } = useViewModelInstanceArtboard('artboard_2', rive?.viewModelInstance);
const handleSetArtboard1 = (artboardName: string) => {
if (rive) {
const artboard = rive.getArtboard(artboardName);
setArtboard1(artboard);
setCurrentArtboard1(artboardName);
}
};
const handleSetArtboard2 = (artboardName: string) => {
if (rive) {
const artboard = rive.getArtboard(artboardName);
setArtboard2(artboard);
setCurrentArtboard2(artboardName);
}
};
return (
<div>
<RiveComponent style={{ width: '400px', height: '400px' }} />
{(rive === null) ? <div data-testid="loading-text">Loading</div> : (
<div>
<div style={{ marginBottom: '20px' }}>
<h4>Artboard 1:</h4>
<div style={{ display: 'flex', gap: '10px', marginBottom: '10px' }}>
<button
data-testid="set-artboard1-blue"
onClick={() => handleSetArtboard1('ArtboardBlue')}
>
Set Blue Artboard
</button>
<button
data-testid="set-artboard1-red"
onClick={() => handleSetArtboard1('ArtboardRed')}
>
Set Red Artboard
</button>
<button
data-testid="set-artboard1-green"
onClick={() => handleSetArtboard1('ArtboardGreen')}
>
Set Green Artboard
</button>
</div>
<div data-testid="artboard1-current">Current: {currentArtboard1}</div>
</div>
<div>
<h4>Artboard 2:</h4>
<div style={{ display: 'flex', gap: '10px', marginBottom: '10px' }}>
<button
data-testid="set-artboard2-blue"
onClick={() => handleSetArtboard2('ArtboardBlue')}
>
Set Blue Artboard
</button>
<button
data-testid="set-artboard2-red"
onClick={() => handleSetArtboard2('ArtboardRed')}
>
Set Red Artboard
</button>
<button
data-testid="set-artboard2-green"
onClick={() => handleSetArtboard2('ArtboardGreen')}
>
Set Green Artboard
</button>
</div>
<div data-testid="artboard2-current">Current: {currentArtboard2}</div>
</div>
</div>
)}
</div>
);
};

View File

@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import Events from './Events';
const meta = {
title: 'Events',
component: Events,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof Events>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,34 @@
import React, { useEffect } from 'react';
import { useRive, EventType, RiveEventType } from '@rive-app/react-canvas';
const Events = () => {
const { rive, RiveComponent } = useRive({
src: 'rating.riv',
stateMachines: 'State Machine 1',
autoplay: true,
automaticallyHandleEvents: true,
});
const onRiveEventReceived = (riveEvent: any) => {
console.log('Rive event received:', riveEvent);
const eventData = riveEvent.data;
const eventProperties = eventData.properties;
if (eventData.type === RiveEventType.General) {
console.log('Event name', eventData.name);
console.log('Rating', eventProperties.rating);
console.log('Message', eventProperties.message);
}
};
// Wait until the rive object is instantiated before adding the Rive
// event listener
useEffect(() => {
if (rive) {
rive.on(EventType.RiveEvent, onRiveEventReceived);
}
}, [rive]);
return <RiveComponent />;
};
export default Events;

View File

@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import Http from './Http';
const meta = {
title: 'Http',
component: Http,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof Http>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { useRive } from '@rive-app/react-canvas';
const Http = () => {
const { RiveComponent } = useRive({
src: 'https://cdn.rive.app/animations/vehicles.riv',
stateMachines: 'bumpy',
autoplay: true,
});
return <RiveComponent />;
};
export default Http;

View File

@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import ResponsiveLayout from './ResponsiveLayout';
const meta = {
title: 'ResponsiveLayout',
component: ResponsiveLayout,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof ResponsiveLayout>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { Fit, useRive, Layout } from '@rive-app/react-canvas';
const ResponsiveLayout = () => {
const { RiveComponent } = useRive({
src: 'layout_test.riv',
artboard: 'Artboard',
stateMachines: 'State Machine 1',
autoplay: true,
layout: new Layout({
fit: Fit.Layout,
}),
});
return <RiveComponent />;
};
export default ResponsiveLayout;

View File

@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from '@storybook/react';
import Simple from './Simple';
const meta = {
title: 'Simple',
component: Simple,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof Simple>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { useRive } from '@rive-app/react-canvas';
const Simple = () => {
const { RiveComponent } = useRive({
src: 'avatars.riv',
artboard: 'Avatar 3',
autoplay: true,
});
return <RiveComponent />;
};
export default Simple;

View File

@@ -1,137 +0,0 @@
import { useEffect, useRef, useState } from 'react';
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import {useRive, useStateMachineInput} from '../../src';
import {Button} from './components/Button';
import './rive-overview.css';
<Meta title="React Runtime/Playback Controls" />
# Animation Playback
When rendering Rives, you may want to control animation playback for certain scenarios. Animation playback allows you to programatically pause, stop, play, reset, and scrub animations as needed. You may find this useful for coordinating certain user interaction or other programatic cases to Rive animations.
**Note:** Just like the Rive web runtime, you invoke playback methods on a `rive` instance. Because of this, you will need to use the `useRive` method to render Rives to your React applications, as it returns a `rive` instance for you to invoke controls on.
## User event-driven playback
You can control Rive animation playback with user interaction directly on the canvas, or even outside the canvas, as you'll see below.
### Play/pause with hover
The example below shows how to start with a Rive instance that does not autoplay initially, but plays whenever the cursor is hovered over the canvas, and returns to a paused state when the mouse leaves the canvas.
<Canvas withSource="open">
<Story name="Play/pause with hover">
{() => {
const { rive, RiveComponent } = useRive({
src: 'poison-loader.riv',
autoplay: false,
});
function onMouseEnter() {
// rive will return as null until the file as fully loaded, so we include this
// guard to prevent any unwanted errors.
if (rive) {
rive.play();
}
}
function onMouseLeave() {
if (rive) {
rive.pause();
}
}
return (
<div className="center">
<RiveComponent
className="base-canvas-size"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
</div>
);
}}
</Story>
</Canvas>
<Canvas withSource="open">
<Story name="Play/pause with intersection observer">
{() => {
const ref = useRef(null);
const { rive, RiveComponent } = useRive({
src: 'baseball.riv',
autoplay: false,
animations: "Hover"
});
useEffect(() => {
const observer = new IntersectionObserver(
([things]) => {
if (rive) {
if (things.isIntersecting) {
rive.play("Hover");
} else {
rive.pause("Hover");
}
}
},
{
rootMargin: '0px',
threshold: 0.1,
}
)
if (ref && ref.current) {
observer.observe(ref.current)
}
}, [rive]);
return (
<div ref={ref} className="center">
<RiveComponent className="base-canvas-size" />
</div>
);
}}
</Story>
</Canvas>
## Play/pause with external elements
This example shows how you can control Rive elements via user interaction outside of the canvas, such as other buttons. Here, the play/pause button will toggle whether or not to play or pause the Rive animation.
<Canvas withSource="open">
<Story name="Play/pause with external elements">
{() => {
const [isPlaying, setIsPlaying] = useState(true);
const { rive, RiveComponent } = useRive({
src: 'truck.riv',
stateMachines: "drive",
artboard: 'Truck',
autoplay: true,
});
const togglePlaying = () => {
if (isPlaying) {
rive.pause();
setIsPlaying(false);
} else {
rive.play();
setIsPlaying(true);
}
};
return ((
<>
<div className="center">
<RiveComponent className="base-canvas-size" />
<Button onClick={togglePlaying}>{isPlaying ? 'Pause' : 'Play'}</Button>
</div>
</>
));
}}
</Story>
</Canvas>
## Additional ways to control playback
While user interaction is a common way to control animation playback for Rives, there are other ways to achieve the same means as well.
### API-driven playback
Another common way to control animation playback is through API responses. Imagine an API that you poll for loading progress of a task. Based on that progress response, you may play or stop a given animation. As long as you hold a reference to the `rive` instance returned from the `useRive` hook, you can invoke control methods on that instance in places such as callbacks from API responses.

View File

@@ -1,118 +0,0 @@
<!-- RiveTestHook.stories.mdx -->
import { useState } from 'react';
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import RiveComponent, { useRive, useStateMachineInput } from '../../src';
import { Button } from './components/Button';
import './rive-overview.css';
<Meta title="React Runtime/Overview" />
# Rive React Runtime
This is an examples/docs page for the official React runtime for Rive. Check out the various pages for how to use this SDK to incorporate Rive into your React-based web applications.
Want to follow along with these examples? Check out the `examples/` folder in the [rive-react](https://github.com/rive-app/rive-react) project to find all these examples and `.riv` assets you can inspect in the Rive editor by dragging and dropping them into your Rive editor file browser.
## What is Rive?
<img className="rive-logo" src="rive_logo_black_2x.png" alt="Rive logo" />
[Rive](https://rive.app/) is a real-time interactive design and animation tool. Use our collaborative editor to create motion graphics that respond to different states and user inputs. Then load your animations into apps, games, and websites with our lightweight open-source runtimes.
## How to use Rive at runtime
There's multiple ways to render Rive using the React runtime. See the associated code snippets that follow each example.
### Rive component
```tsx
import RiveComponent from '@rive-app/react-canvas';
```
The React runtime exports a default React component you can insert as JSX. Under the hood, it renders a `<canvas>` element that runs the animation, and a wrapping `<div>` element that handles sizing of the canvas based on the parent that wraps the component.
**When to use this**: Use this for simple rendering cases where you don't need to control playback or setup state machine inputs to advance state machines. It will simply autoplay the first animation it finds in the `.riv`, the animation name you provide it, or the state machine name if you provide one.
**Note:** Style-specific props set onto the component will pass down to the wrapping `<div>` element, while most other props will be set onto the `<canvas>` element itself.
<Canvas withSource="open">
<Story name="Rive Component">
{() => (
<div className="center">
<RiveComponent src="poison-loader.riv" className="base-canvas-size" />
</div>
)}
</Story>
</Canvas>
#### Props
In addition to the props laid out below, the component accepts other props that can be set on the `<canvas>` element.
<ArgsTable of={RiveComponent} />
### useRive Hook
```tsx
import { useRive } from '@rive-app/react-canvas';
```
The runtime also exports a named `useRive` hook that allows for more control at Rive instantiation, since it passes back a `rive` object you can use to manipulate state machines, control playback, and more.
**When to use this:** When you need to control your Rive animation in any aspect, such as controlling playback, using state machine inputs to advance state machines, add adding callbacks on certain Rive-specific events such as `onStateChange`, `onPause`, etc.
<Canvas withSource="open">
<Story name="useRive Hook">
{() => {
const [isPlaying, setIsPlaying] = useState(true);
const [animationText, setAnimationText] = useState('');
const { rive, RiveComponent: RiveComponentPlayback } = useRive({
src: 'truck.riv',
stateMachines: 'drive',
artboard: 'Truck',
autoplay: true,
onPause: () => {
setAnimationText('Animation paused!');
},
onPlay: () => {
setAnimationText('Animation is playing..');
},
});
const togglePlaying = () => {
if (isPlaying) {
rive.pause();
setIsPlaying(false);
} else {
rive.play();
setIsPlaying(true);
}
};
return (
<>
<div className="center">
<RiveComponentPlayback className="base-canvas-size">
<p>Animation that can be paused and played by clicking on it</p>
</RiveComponentPlayback>
<p>{animationText}</p>
<Button onClick={togglePlaying}>
{isPlaying ? 'Pause' : 'Play'}
</Button>
</div>
</>
);
}}
</Story>
</Canvas>
#### useRive parameters
```tsx
useRive(params: UseRiveParameters, opts?: UseRiveOptions);
```
The parameters available to set on `useRive` can be found [here](https://github.com/rive-app/rive-wasm/blob/master/js/src/rive.ts#L843). These pass down to the web runtime `rive` instance created.
Additionally, there are other options to set on `useRive` that can be found [here](https://github.com/rive-app/rive-react/blob/main/src/types.ts#L6).

View File

@@ -1,222 +0,0 @@
<!-- RiveTestHook.stories.mdx -->
import { useState, useEffect } from 'react';
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import RiveComponent, {
EventType,
useRive,
useStateMachineInput,
} from '../../src';
import { Button } from './components/Button';
import './rive-overview.css';
<Meta title="React Runtime/State Machines" />
# State Machine Usage
Not familiar with Rive State Machines? Check out our [help docs](https://rive.app/community/doc/state-machine/docwH5zPdh93) on what these are first!
The `useStateMachineInput` hook is a helper that makes grabbing references to state machine inputs easier to setup. This hook should be used along with the `useRive` hook, as you need to pass in the `rive` instance returned from `useRive`. See each of the examples below to see usage of the hook creating instance of three types of inputs:
- Booleans
- Numbers
- Triggers
## Boolean inputs
Once you grab a reference to the state machine input, you can get/set the value of the input via the `.value` property.
**Note:** The input instance value itself is not a stateful React variable, therefore, any logic in the component dependent on an input value changing will not trigger a re-render like a React stateful variable change would. You can achieve this effect by keeping reference to the state machine input value inside a React state variable. See the example below for this pattern.
<Canvas withSource="open">
<Story name="Boolean input">
{() => {
const STATE_MACHINE_NAME = 'State Machine 1';
const ON_HOVER_INPUT_NAME = 'Hover';
const ON_PRESSED_INPUT_NAME = 'Pressed';
const { rive, RiveComponent: RiveComponentTouch } = useRive({
src: 'like.riv',
stateMachines: STATE_MACHINE_NAME,
artboard: 'New Artboard',
autoplay: true,
});
const [isHovered, setIsHovered] = useState(false);
// Both onHoverInput and onPressedInput are boolean inputs. To transition
// states we need to set the value property to true or false.
const onPressedInput = useStateMachineInput(
rive,
STATE_MACHINE_NAME,
ON_PRESSED_INPUT_NAME
);
const onHoverInput = useStateMachineInput(
rive,
STATE_MACHINE_NAME,
ON_HOVER_INPUT_NAME
);
function onMouseDown() {
onPressedInput.value = true;
}
function onMouseUp() {
onPressedInput.value = false;
}
function onMouseEnter() {
onHoverInput.value = true;
setIsHovered(true);
}
function onMouseLeave() {
onHoverInput.value = false;
setIsHovered(false);
}
return (
<>
<div className="center">
<RiveComponentTouch
className="base-canvas-size"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
/>
<p>Hover and click on the canvas</p>
<p>Is cursor hovering? {isHovered ? 'Yes' : 'No'}</p>
</div>
</>
);
}}
</Story>
</Canvas>
## Number inputs
Once you grab a reference to the state machine input, you can get/set the value of the input via the `.value` property.
<Canvas withSource="open">
<Story name="Number input">
{() => {
const STATE_MACHINE_NAME = 'skill-controller';
const INPUT_NAME = 'level';
const { rive, RiveComponent: RiveComponentTouch } = useRive({
src: 'skills.riv',
stateMachines: STATE_MACHINE_NAME,
artboard: 'New Artboard',
autoplay: true,
});
// levelInput is a number state machine input. To transition the state machine,
// we need to set the value to a number. For this state machine input, we need
// to set to 0, 1 or 2 for a state transition to occur.
const levelInput = useStateMachineInput(
rive,
STATE_MACHINE_NAME,
INPUT_NAME
);
return (
// The animation will fit to the parent element, so we set a large height
// and width for this example.
<div className="center">
<RiveComponentTouch className="large-canvas-size" />
<div className="btn-group">
Choose a level:
<Button onClick={() => (levelInput.value = 0)}>0</Button>
<Button onClick={() => (levelInput.value = 1)}>1</Button>
<Button onClick={() => (levelInput.value = 2)}>2</Button>
</div>
</div>
);
}}
</Story>
</Canvas>
## Trigger inputs
Unlike the boolean and number inputs, you invoke the `.fire()` method on a trigger input.
<Canvas withSource="open">
<Story name="Trigger input">
{() => {
const STATE_MACHINE_NAME = 'State Machine 1';
const INPUT_NAME = 'Pressed';
const { rive, RiveComponent: RiveComponentTouch } = useRive({
src: 'piggy-bank.riv',
stateMachines: STATE_MACHINE_NAME,
artboard: 'New Artboard',
autoplay: true,
});
// pressedInput in a trigger state machine input. To transition the state
// we need to call the `fire()` method on the input.
const pressedInput = useStateMachineInput(
rive,
STATE_MACHINE_NAME,
INPUT_NAME
);
// The animation will fit to the parent element, so we set a large height
// and width for this example.
return (
<div className="center">
<RiveComponentTouch
className="base-canvas-size"
onClick={() => pressedInput.fire()}
/>
<p>Click on the canvas</p>
</div>
);
}}
</Story>
</Canvas>
## Rive Text
A simple example showing Rive Text rendering. Note that if you are using the `@rive-app/react-canvas-lite` package,
the Rive Text bit will not render on the graphic, however, the rest of the Rive content should without issue.
<Canvas withSource="open">
<Story name="Rive Text">
{() => {
const STATE_MACHINE_NAME = 'State Machine 1';
const { rive, RiveComponent } = useRive({
src: 'text_test.riv',
stateMachines: STATE_MACHINE_NAME,
autoplay: true,
automaticallyHandleEvents: true,
});
return (
<div className="center">
<RiveComponent className="base-canvas-size" />
</div>
);
}}
</Story>
</Canvas>
## Rive Events
To listen for Rive Events reported during state machine play, use the `on` API to add an event listener.
<Canvas withSource="open">
<Story name="Rive Events">
{() => {
const STATE_MACHINE_NAME = 'State Machine 1';
const { rive, RiveComponent } = useRive({
src: 'rating_animation.riv',
stateMachines: STATE_MACHINE_NAME,
autoplay: true,
automaticallyHandleEvents: true,
});
useEffect(() => {
if (rive) {
rive.on(EventType.RiveEvent, onRiveEventReceived);
}
}, [rive]);
const onRiveEventReceived = (riveEvent) => {
console.log('Rive Event Fired', riveEvent);
};
return (
<div className="center">
<RiveComponent className="base-canvas-size" />
<p>Click on the 5 star!</p>
</div>
);
}}
</Story>
</Canvas>

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,7 +0,0 @@
import React from 'react';
export const Button = ({onClick, children}) => {
return (
<button className="btn" onClick={onClick}>{children}</button>
);
};

View File

@@ -1,36 +0,0 @@
.center {
display: flex;
align-content: center;
flex-direction: column;
flex-wrap: wrap;
}
.base-canvas-size {
height: 300px;
width: 300px;
}
.large-canvas-size {
height: 600px;
width: 600px;
}
.btn {
text-decoration: none;
border: none;
background: #0069ed;
border-radius: 2px;
height: 32px;
margin-top: 16px;
color: #ffffff;
cursor: pointer;
}
.btn-group .btn {
margin-left: 8px;
}
.rive-logo {
display: flex;
margin: 16px auto;
}

32
examples/tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"paths": {
"@rive-app/react-canvas": ["../"],
"@rive-app/react-webgl": ["../"],
"@rive-app/react-webgl2": ["../"],
"@rive-app/react-canvas-lite": ["../"]
},
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}

12307
examples/yarn.lock Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@rive-app/react-canvas-lite",
"version": "4.16.3",
"version": "4.27.1",
"description": "React wrapper around the @rive-app/canvas-lite library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -18,9 +18,9 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/canvas-lite": "2.23.7"
"@rive-app/canvas-lite": "2.35.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@rive-app/react-canvas",
"version": "4.16.3",
"version": "4.27.1",
"description": "React wrapper around the @rive-app/canvas library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -18,9 +18,9 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/canvas": "2.23.7"
"@rive-app/canvas": "2.35.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@rive-app/react-webgl",
"version": "4.16.3",
"version": "4.27.1",
"description": "React wrapper around the @rive-app/webgl library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -18,9 +18,9 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/webgl": "2.23.7"
"@rive-app/webgl": "2.35.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@rive-app/react-webgl2",
"version": "4.16.3",
"version": "4.27.1",
"description": "React wrapper around the @rive-app/webgl2 library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -18,9 +18,9 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/webgl2": "2.23.7"
"@rive-app/webgl2": "2.35.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "rive-react",
"version": "4.16.3",
"version": "4.27.1",
"description": "React wrapper around the rive-js library",
"main": "dist/index.js",
"typings": "dist/types/index.d.ts",
@@ -15,15 +15,14 @@
"format": "prettier --write src",
"types:check": "tsc --noEmit",
"release": "release-it",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook -o docs-build",
"release:patch": "npm run release -- --ci",
"release:minor": "npm run release -- minor --ci",
"release:major": "npm run release -- major --ci",
"setup-builds": "./scripts/build.sh",
"setup-packages": "./scripts/setup_all_packages.sh",
"bump-versions": "./scripts/bump_all_versions.sh $npm_package_version",
"publish:all": "./scripts/publish_all.sh --access public"
"publish:all": "./scripts/publish_all.sh --access public",
"storybook": "yarn --cwd examples storybook"
},
"repository": {
"type": "git",
@@ -36,30 +35,22 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/canvas": "2.23.7",
"@rive-app/canvas-lite": "2.23.7",
"@rive-app/webgl": "2.23.7",
"@rive-app/webgl2": "2.23.7"
"@rive-app/canvas": "2.35.2",
"@rive-app/canvas-lite": "2.35.2",
"@rive-app/webgl": "2.35.2",
"@rive-app/webgl2": "2.35.2"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
},
"devDependencies": {
"@babel/core": "^7.18.0",
"@storybook/addon-actions": "^6.5.3",
"@storybook/addon-essentials": "^6.5.3",
"@storybook/addon-interactions": "^6.5.3",
"@storybook/addon-links": "^6.5.3",
"@storybook/builder-webpack4": "^6.5.3",
"@storybook/manager-webpack4": "^6.5.3",
"@storybook/react": "^6.5.3",
"@storybook/testing-library": "^0.0.11",
"@testing-library/jest-dom": "^5.13.0",
"@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "^7.0.0",
"@testing-library/react": "^16.3.0",
"@types/jest": "^27.0.3",
"@types/node": "^18.17.0",
"@types/offscreencanvas": "^2019.6.4",
"@types/react": "^17.0.9",
"@types/react": "^18.0.0",
"@types/testing-library__jest-dom": "^5.9.5",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
@@ -78,8 +69,8 @@
"eslint-plugin-storybook": "^0.5.12",
"jest": "^27.0.4",
"prettier": "^2.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"release-it": "^14.10.0",
"ts-jest": "^27.1.1",
"typescript": "^4.5.4",

View File

@@ -69,6 +69,7 @@ export default function useRive(
): RiveState {
const [canvasElem, setCanvasElem] = useState<HTMLCanvasElement | null>(null);
const containerRef = useRef<HTMLElement | null>(null);
const riveRef = useRef<Rive | null>(null);
const [rive, setRive] = useState<Rive | null>(null);
@@ -126,24 +127,42 @@ export default function useRive(
if (!canvasElem || !riveParams) {
return;
}
let isLoaded = rive != null;
let r: Rive | null;
if (rive == null) {
const { useOffscreenRenderer } = options;
const r = new Rive({
const { onRiveReady, ...restRiveParams } = riveParams;
r = new Rive({
useOffscreenRenderer,
...riveParams,
...restRiveParams,
canvas: canvasElem,
});
if (riveRef.current != null) {
riveRef.current!.cleanup();
}
riveRef.current = r;
r.on(EventType.Load, () => {
isLoaded = true;
if (onRiveReady) {
onRiveReady(r!);
}
// Check if the component/canvas is mounted before setting state to avoid setState
// on an unmounted component in some rare cases
if (canvasElem) {
setRive(r);
} else {
// If unmounted, cleanup the rive object immediately
r.cleanup();
r!.cleanup();
}
});
}
return () => {
if (!isLoaded) {
r?.cleanup();
}
};
}, [canvasElem, isParamsLoaded, rive]);
/**
* Ref callback called when the container element mounts
@@ -229,6 +248,14 @@ export default function useRive(
};
}, [rive, canvasElem]);
useEffect(() => {
return () => {
if (riveRef.current != null) {
riveRef.current!.cleanup();
}
};
}, []);
/**
* Listen for changes in the animations params
*/

55
src/hooks/useViewModel.ts Normal file
View File

@@ -0,0 +1,55 @@
import { useState, useEffect } from 'react';
import { Rive, ViewModel, EventType } from '@rive-app/canvas';
import { UseViewModelParameters } from '../types';
/**
* Hook for fetching a ViewModel from a Rive instance.
*
* @param rive - The Rive instance to retrieve the ViewModel from
* @param params - Options for retrieving a ViewModel
* @param params.name - When provided, specifies the name of the ViewModel to retrieve
* @param params.useDefault - When true, uses the default ViewModel from the Rive instance
* @returns The ViewModel or null if not found
*/
export default function useViewModel(
rive: Rive | null,
params?: UseViewModelParameters
): ViewModel | null {
const { name, useDefault = false } = params ?? {};
const [viewModel, setViewModel] = useState<ViewModel | null>(null);
useEffect(() => {
function fetchViewModel() {
if (!rive) {
setViewModel(null);
return;
}
let model: ViewModel | null = null;
if (name != null) {
model = rive.viewModelByName?.(name) || null;
} else if (useDefault) {
model = rive.defaultViewModel() || null;
} else {
model = rive.defaultViewModel() || null;
}
setViewModel(model);
}
fetchViewModel();
if (rive) {
rive.on(EventType.Load, fetchViewModel);
}
return () => {
if (rive) {
rive.off(EventType.Load, fetchViewModel);
}
};
}, [rive, name, useDefault]);
return viewModel;
}

View File

@@ -0,0 +1,49 @@
import { useState, useEffect } from 'react';
import { ViewModel, ViewModelInstance } from '@rive-app/canvas';
import { UseViewModelInstanceParameters } from '../types';
/**
* Hook for fetching a ViewModelInstance from a ViewModel.
*
* @param viewModel - The ViewModel to get an instance from
* @param params - Options for retrieving a ViewModelInstance
* @param params.name - When provided, specifies the name of the instance to retrieve
* @param params.useDefault - When true, uses the default instance from the ViewModel
* @param params.useNew - When true, creates a new instance of the ViewModel
* @param params.rive - If provided, automatically binds the instance to this Rive instance
* @returns The ViewModelInstance or null if not found
*/
export default function useViewModelInstance(
viewModel: ViewModel | null,
params?: UseViewModelInstanceParameters
): ViewModelInstance | null {
const { name, useDefault = false, useNew = false, rive } = params ?? {};
const [instance, setInstance] = useState<ViewModelInstance | null>(null);
useEffect(() => {
if (!viewModel) {
setInstance(null);
return;
}
let result: ViewModelInstance | null = null;
if (name != null) {
result = viewModel.instanceByName(name) || null;
} else if (useDefault) {
result = viewModel.defaultInstance?.() || null;
} else if (useNew) {
result = viewModel.instance?.() || null;
} else {
result = viewModel.defaultInstance?.() || null;
}
setInstance(result);
if (rive && result && rive.viewModelInstance !== result) {
rive.bindViewModelInstance(result);
}
}, [viewModel, name, useDefault, useNew, rive]);
return instance;
}

Some files were not shown because too many files have changed in this diff Show More