diff --git a/.eslintrc.js b/.eslintrc.js index 1dd7d06..7ac2b6d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,27 +1,29 @@ module.exports = { env: { browser: true, - es2021: true, + es2021: true }, - extends: ['plugin:react/recommended', 'prettier'], + extends: ['plugin:react/recommended', 'prettier', 'plugin:storybook/recommended'], parser: '@typescript-eslint/parser', parserOptions: { ecmaFeatures: { - jsx: true, + jsx: true }, ecmaVersion: 12, - sourceType: 'module', + sourceType: 'module' }, plugins: ['@typescript-eslint', 'prettier'], rules: { '@typescript-eslint/no-unused-vars': 'error', - 'prefer-const': ['warn', { destructuring: 'all' }], + 'prefer-const': ['warn', { + destructuring: 'all' + }], 'no-var': 'error', - eqeqeq: ['error', 'smart'], + eqeqeq: ['error', 'smart'] }, settings: { react: { - version: 'detect', - }, - }, -}; + version: 'detect' + } + } +}; \ No newline at end of file diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 0000000..66a9cf7 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,13 @@ +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'], +} \ No newline at end of file diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000..3fbc683 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,17 @@ + + + + + + diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 0000000..84fe0c8 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,39 @@ +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); diff --git a/examples/basic-typescript/.gitignore b/examples/basic-typescript/.gitignore deleted file mode 100644 index 4d29575..0000000 --- a/examples/basic-typescript/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# 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* diff --git a/examples/basic-typescript/README.md b/examples/basic-typescript/README.md deleted file mode 100644 index be35b4a..0000000 --- a/examples/basic-typescript/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Basic Typescript - -This is an example app that uses the useRive hook with typescript. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/basic-typescript/package.json b/examples/basic-typescript/package.json deleted file mode 100644 index 030edab..0000000 --- a/examples/basic-typescript/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "basic-typescript", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "@types/jest": "^26.0.23", - "@types/node": "^12.20.14", - "@types/react": "^17.0.9", - "@types/react-dom": "^17.0.6", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "typescript": "^4.3.2", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/basic-typescript/public/index.html b/examples/basic-typescript/public/index.html deleted file mode 100644 index 6f5d3fe..0000000 --- a/examples/basic-typescript/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - React App - Basic Typescript - - - -
- - diff --git a/examples/basic-typescript/src/App.tsx b/examples/basic-typescript/src/App.tsx deleted file mode 100644 index 5db9624..0000000 --- a/examples/basic-typescript/src/App.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useRive, UseRiveParameters } from 'rive-react'; - -function App() { - const params: UseRiveParameters = { - src: 'poison-loader.riv', - autoplay: true, - }; - - const { RiveComponent } = useRive(params); - - return ( - // The animation will fit to the parent element. -
- -
- ); -} - -export default App; diff --git a/examples/basic-typescript/src/index.tsx b/examples/basic-typescript/src/index.tsx deleted file mode 100644 index c1f31c5..0000000 --- a/examples/basic-typescript/src/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/basic-typescript/src/react-app-env.d.ts b/examples/basic-typescript/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/examples/basic-typescript/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/examples/basic-typescript/tsconfig.json b/examples/basic-typescript/tsconfig.json deleted file mode 100644 index a273b0c..0000000 --- a/examples/basic-typescript/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "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" - ] -} diff --git a/examples/basic-with-hook/README.md b/examples/basic-with-hook/README.md deleted file mode 100644 index 6ecb5d6..0000000 --- a/examples/basic-with-hook/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Basic with hook - -This is a very basic example of using the useRive to autoplay a simple looping animation. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/basic-with-hook/package.json b/examples/basic-with-hook/package.json deleted file mode 100644 index 91b1ce1..0000000 --- a/examples/basic-with-hook/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "basic-with-hook", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "file:../../node_modules/react", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "file:../..", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/basic-with-hook/public/index.html b/examples/basic-with-hook/public/index.html deleted file mode 100644 index ade00a9..0000000 --- a/examples/basic-with-hook/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - Basic with Hook - - - -
- - diff --git a/examples/basic-with-hook/public/magic-ball.riv b/examples/basic-with-hook/public/magic-ball.riv deleted file mode 100644 index f4cce41..0000000 Binary files a/examples/basic-with-hook/public/magic-ball.riv and /dev/null differ diff --git a/examples/basic-with-hook/public/poison-loader.riv b/examples/basic-with-hook/public/poison-loader.riv deleted file mode 100644 index a946fa4..0000000 Binary files a/examples/basic-with-hook/public/poison-loader.riv and /dev/null differ diff --git a/examples/basic-with-hook/src/App.js b/examples/basic-with-hook/src/App.js deleted file mode 100644 index 8ab6816..0000000 --- a/examples/basic-with-hook/src/App.js +++ /dev/null @@ -1,29 +0,0 @@ -import { useRive } from 'rive-react'; - -function App() { - const params = { - src: 'poison-loader.riv', - autoplay: true, - }; - - const { RiveComponent: RiveComponentBasic } = useRive(params); - - const { RiveComponent: RiveComponentTouch } = useRive({ - src: 'magic-ball.riv', - autoplay: true, - stateMachines: "Main State Machine", - }); - - return ( - <> -
- -
-
- -
- - ); -} - -export default App; diff --git a/examples/basic-with-hook/src/index.js b/examples/basic-with-hook/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/basic-with-hook/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/basic/README.md b/examples/basic/README.md deleted file mode 100644 index 146c4ed..0000000 --- a/examples/basic/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Basic - -This is a very basic example of using the Rive component to autoplay a simple looping animation. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/basic/package.json b/examples/basic/package.json deleted file mode 100644 index 2d4166c..0000000 --- a/examples/basic/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "basic-example", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "file:../../node_modules/react", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "file:../..", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/basic/public/index.html b/examples/basic/public/index.html deleted file mode 100644 index 1c3b6f1..0000000 --- a/examples/basic/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - Basic - - - -
- - diff --git a/examples/basic/public/poison-loader.riv b/examples/basic/public/poison-loader.riv deleted file mode 100644 index a946fa4..0000000 Binary files a/examples/basic/public/poison-loader.riv and /dev/null differ diff --git a/examples/basic/src/App.js b/examples/basic/src/App.js deleted file mode 100644 index e784b69..0000000 --- a/examples/basic/src/App.js +++ /dev/null @@ -1,12 +0,0 @@ -import Rive from 'rive-react'; - -function App() { - return ( - // The animation will fit to the parent element. -
- -
- ); -} - -export default App; diff --git a/examples/basic/src/index.js b/examples/basic/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/basic/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/play-on-hover/README.md b/examples/play-on-hover/README.md deleted file mode 100644 index dd8f0b9..0000000 --- a/examples/play-on-hover/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Play on hover - -This provides an example of how to play/pause a Rive animation when the mouse is hovered over it. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/play-on-hover/package.json b/examples/play-on-hover/package.json deleted file mode 100644 index c2e88c7..0000000 --- a/examples/play-on-hover/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "play-on-hover", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/play-on-hover/public/index.html b/examples/play-on-hover/public/index.html deleted file mode 100644 index a64d5f5..0000000 --- a/examples/play-on-hover/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - Play on hover - - - -
- - diff --git a/examples/play-on-hover/public/poison-loader.riv b/examples/play-on-hover/public/poison-loader.riv deleted file mode 100644 index a946fa4..0000000 Binary files a/examples/play-on-hover/public/poison-loader.riv and /dev/null differ diff --git a/examples/play-on-hover/src/App.js b/examples/play-on-hover/src/App.js deleted file mode 100644 index d6a6385..0000000 --- a/examples/play-on-hover/src/App.js +++ /dev/null @@ -1,32 +0,0 @@ -import { useRive } from 'rive-react'; - -function App() { - const params = { - src: 'poison-loader.riv', - autoplay: false, - }; - - const { RiveComponent, rive } = useRive(params); - - 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 ( -
- -
- ); -} - -export default App; diff --git a/examples/play-on-hover/src/index.js b/examples/play-on-hover/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/play-on-hover/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/play-pause-button/README.md b/examples/play-pause-button/README.md deleted file mode 100644 index 92c23df..0000000 --- a/examples/play-pause-button/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Play/Pause Buttons - -This example shows how we can play/pause the Rive animation on a button click and update the text of the button based on events happening in the runtime. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/play-pause-button/package.json b/examples/play-pause-button/package.json deleted file mode 100644 index 0eb50e8..0000000 --- a/examples/play-pause-button/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "play-pause-button", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/play-pause-button/public/index.html b/examples/play-pause-button/public/index.html deleted file mode 100644 index 750f1bc..0000000 --- a/examples/play-pause-button/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - Play/Pause Button - - - -
- - diff --git a/examples/play-pause-button/public/poison-loader.riv b/examples/play-pause-button/public/poison-loader.riv deleted file mode 100644 index a946fa4..0000000 Binary files a/examples/play-pause-button/public/poison-loader.riv and /dev/null differ diff --git a/examples/play-pause-button/src/App.js b/examples/play-pause-button/src/App.js deleted file mode 100644 index 803beaa..0000000 --- a/examples/play-pause-button/src/App.js +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useRive } from 'rive-react'; - -function App() { - const [buttonText, setButtonText] = useState('Pause'); - const { RiveComponent, rive } = useRive({ - src: 'poison-loader.riv', - autoplay: true, - }); - - useEffect(() => { - if (rive) { - // "play" event is fired when the animation starts to play, so we update - // button text on this event. - rive.on('play', () => { - setButtonText('Pause'); - }); - - // As above, the "pause" event is fired when the animation pauses. - rive.on('pause', () => { - setButtonText('Play'); - }); - } - // We listen for changes to the rive object. The rive object will be null - // until the rive file has loaded. - }, [rive]); - - function onButtonClick() { - // `rive` will return as null until the file as fully loaded, so we include this - // guard to prevent any unwanted errors. - if (rive) { - if (rive.isPlaying) { - rive.pause(); - } else { - rive.play(); - } - } - } - - return ( - // The animation will fit to the parent element, so we set a large height - // and width for this example. -
- - -
- ); -} - -export default App; diff --git a/examples/play-pause-button/src/index.js b/examples/play-pause-button/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/play-pause-button/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/state-machine-boolean-input/README.md b/examples/state-machine-boolean-input/README.md deleted file mode 100644 index 3b6f811..0000000 --- a/examples/state-machine-boolean-input/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# State Machine Boolean Input - -This example shows how to interact with a state machine, using various onMouse\* callbacks to trigger transations. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/state-machine-boolean-input/package.json b/examples/state-machine-boolean-input/package.json deleted file mode 100644 index 058ca80..0000000 --- a/examples/state-machine-boolean-input/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "state-machine-boolean-input", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/state-machine-boolean-input/public/index.html b/examples/state-machine-boolean-input/public/index.html deleted file mode 100644 index f7322bc..0000000 --- a/examples/state-machine-boolean-input/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - State Machine Boolean Input - - - -
- - diff --git a/examples/state-machine-boolean-input/src/App.js b/examples/state-machine-boolean-input/src/App.js deleted file mode 100644 index bf7382a..0000000 --- a/examples/state-machine-boolean-input/src/App.js +++ /dev/null @@ -1,58 +0,0 @@ -import { useRive, useStateMachineInput } from 'rive-react'; - -function App() { - const STATE_MACHINE_NAME = 'State Machine 1'; - const ON_HOVER_INPUT_NAME = 'Hover'; - const ON_PRESSED_INPUT_NAME = 'Pressed'; - - const { RiveComponent, rive } = useRive({ - src: 'like.riv', - stateMachines: STATE_MACHINE_NAME, - artboard: 'New Artboard', - autoplay: true, - }); - - // Both onHoverInput and onPressedInput are boolean inputs. To transition - // states we need to set the value property to true or false. - const onHoverInput = useStateMachineInput( - rive, - STATE_MACHINE_NAME, - ON_HOVER_INPUT_NAME - ); - const onPressedInput = useStateMachineInput( - rive, - STATE_MACHINE_NAME, - ON_PRESSED_INPUT_NAME - ); - - function onMouseEnter() { - onHoverInput.value = true; - } - - function onMouseLeave() { - onHoverInput.value = false; - } - - function onMouseDown() { - onPressedInput.value = true; - } - - function onMouseUp() { - onPressedInput.value = false; - } - - return ( - // The animation will fit to the parent element, so we set a large height - // and width for this example. -
- -
- ); -} - -export default App; diff --git a/examples/state-machine-boolean-input/src/index.js b/examples/state-machine-boolean-input/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/state-machine-boolean-input/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/state-machine-number-input/README.md b/examples/state-machine-number-input/README.md deleted file mode 100644 index 756317c..0000000 --- a/examples/state-machine-number-input/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# State Machine Number Input - -This example shows how to interact with a state machine with number inputs. We trigger the transistions by changing the value of a number input. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/state-machine-number-input/package.json b/examples/state-machine-number-input/package.json deleted file mode 100644 index 273d411..0000000 --- a/examples/state-machine-number-input/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "state-machine-number-input", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/state-machine-number-input/public/index.html b/examples/state-machine-number-input/public/index.html deleted file mode 100644 index 46d5d28..0000000 --- a/examples/state-machine-number-input/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - State Machine Number Input - - - -
- - diff --git a/examples/state-machine-number-input/src/App.js b/examples/state-machine-number-input/src/App.js deleted file mode 100644 index 61117a2..0000000 --- a/examples/state-machine-number-input/src/App.js +++ /dev/null @@ -1,34 +0,0 @@ -import { useRive, useStateMachineInput } from 'rive-react'; - -function App() { - const STATE_MACHINE_NAME = 'State Machine '; - const INPUT_NAME = 'Level'; - - const { RiveComponent, rive } = 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. -
- -
- Level: - - - -
-
- ); -} - -export default App; diff --git a/examples/state-machine-number-input/src/index.js b/examples/state-machine-number-input/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/state-machine-number-input/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/state-machine-trigger-input/README.md b/examples/state-machine-trigger-input/README.md deleted file mode 100644 index eb73c91..0000000 --- a/examples/state-machine-trigger-input/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# State Machine Trigger Input - -This example shows how to interact with a state machine with a trigger input. We call the `fire()` function on the state machine input to transition the state when the animation is clicked. - -## To Run - -This example is created using [Create React App](https://reactjs.org/docs/create-a-new-react-app.html). - -To install and run: - -``` -npm install -npm start -``` diff --git a/examples/state-machine-trigger-input/package.json b/examples/state-machine-trigger-input/package.json deleted file mode 100644 index 8483e22..0000000 --- a/examples/state-machine-trigger-input/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "state-machine-trigger-input", - "version": "0.1.0", - "private": true, - "dependencies": { - "@testing-library/jest-dom": "^5.13.0", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "4.0.3", - "rive-react": "latest", - "web-vitals": "^1.1.2" - }, - "scripts": { - "start": "SKIP_PREFLIGHT_CHECK=true react-scripts start" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/examples/state-machine-trigger-input/public/index.html b/examples/state-machine-trigger-input/public/index.html deleted file mode 100644 index f27e47b..0000000 --- a/examples/state-machine-trigger-input/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Rive React - State Machine Trigger Input - - - -
- - diff --git a/examples/state-machine-trigger-input/src/App.js b/examples/state-machine-trigger-input/src/App.js deleted file mode 100644 index 06c223e..0000000 --- a/examples/state-machine-trigger-input/src/App.js +++ /dev/null @@ -1,31 +0,0 @@ -import { useRive, useStateMachineInput } from 'rive-react'; - -function App() { - const STATE_MACHINE_NAME = 'State Machine 1'; - const INPUT_NAME = 'Pressed'; - - const { RiveComponent, rive } = 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 - ); - - return ( - // The animation will fit to the parent element, so we set a large height - // and width for this example. -
- pressedInput.fire()} /> -
- ); -} - -export default App; diff --git a/examples/state-machine-trigger-input/src/index.js b/examples/state-machine-trigger-input/src/index.js deleted file mode 100644 index c1f31c5..0000000 --- a/examples/state-machine-trigger-input/src/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -ReactDOM.render( - - - , - document.getElementById('root') -); diff --git a/examples/stories/AnimationPlayback.stories.mdx b/examples/stories/AnimationPlayback.stories.mdx new file mode 100644 index 0000000..37bd40c --- /dev/null +++ b/examples/stories/AnimationPlayback.stories.mdx @@ -0,0 +1,99 @@ +import { 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'; + + + +# 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. + + + + {() => { + 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 ( +
+ +
+ ); + }} +
+
+ +## 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. + + + + {() => { + 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 (( + <> +
+ + +
+ + )); + }} +
+
+ +## 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. 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. + diff --git a/examples/stories/RiveOverview.stories.mdx b/examples/stories/RiveOverview.stories.mdx new file mode 100644 index 0000000..25dfa29 --- /dev/null +++ b/examples/stories/RiveOverview.stories.mdx @@ -0,0 +1,112 @@ + + +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'; + + + +# 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? + +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 `` element that runs the animation, and a wrapping `
` 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 `
` element, while most other props will be set onto the `` element itself. + + + + {() => ( +
+ +
+ )} +
+
+ +#### Props + +In addition to the props laid out below, the component accepts other props that can be set on the `` element. + + + +### 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. + + + {() => { + 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 (( + <> +
+ +

{animationText}

+ +
+ + )); + }} +
+
+ +#### 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). diff --git a/examples/stories/StateMachineDocs.stories.mdx b/examples/stories/StateMachineDocs.stories.mdx new file mode 100644 index 0000000..4732e43 --- /dev/null +++ b/examples/stories/StateMachineDocs.stories.mdx @@ -0,0 +1,157 @@ + + +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'; + + + +# State Machine Usage + +Not familiar with Rive State Machines? Check out our [help docs](https://help.rive.app/editor/state-machine) 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. + + + + {() => { + 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 ( + <> +
+ +

Hover and click on the canvas

+

Is cursor hovering? {isHovered ? 'Yes' : 'No'}

+
+ + ); + }} +
+
+ +## 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. + + + + {() => { + const STATE_MACHINE_NAME = 'State Machine '; + 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. +
+ +
+ Choose a level: + + + +
+
+ ); + }} +
+
+ +## Trigger inputs + +Unlike the boolean and number inputs, you invoke the `.fire()` method on a 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 ( +
+ pressedInput.fire()} + /> +

Click on the canvas

+
+ ); + }} +
+
diff --git a/examples/state-machine-boolean-input/public/like.riv b/examples/stories/assets/like.riv similarity index 100% rename from examples/state-machine-boolean-input/public/like.riv rename to examples/stories/assets/like.riv diff --git a/examples/state-machine-trigger-input/public/piggy-bank.riv b/examples/stories/assets/piggy-bank.riv similarity index 100% rename from examples/state-machine-trigger-input/public/piggy-bank.riv rename to examples/stories/assets/piggy-bank.riv diff --git a/examples/basic-typescript/public/poison-loader.riv b/examples/stories/assets/poison-loader.riv similarity index 100% rename from examples/basic-typescript/public/poison-loader.riv rename to examples/stories/assets/poison-loader.riv diff --git a/examples/stories/assets/rive_logo_black_2x.png b/examples/stories/assets/rive_logo_black_2x.png new file mode 100644 index 0000000..70e80d2 Binary files /dev/null and b/examples/stories/assets/rive_logo_black_2x.png differ diff --git a/examples/state-machine-number-input/public/skills.riv b/examples/stories/assets/skills.riv similarity index 100% rename from examples/state-machine-number-input/public/skills.riv rename to examples/stories/assets/skills.riv diff --git a/examples/stories/assets/truck.riv b/examples/stories/assets/truck.riv new file mode 100644 index 0000000..5c08413 Binary files /dev/null and b/examples/stories/assets/truck.riv differ diff --git a/examples/stories/components/Button.tsx b/examples/stories/components/Button.tsx new file mode 100644 index 0000000..3897c65 --- /dev/null +++ b/examples/stories/components/Button.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export const Button = ({onClick, children}) => { + return ( + + ); +}; diff --git a/examples/stories/rive-overview.css b/examples/stories/rive-overview.css new file mode 100644 index 0000000..9d1a771 --- /dev/null +++ b/examples/stories/rive-overview.css @@ -0,0 +1,36 @@ +.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; +} diff --git a/package.json b/package.json index 4fc0d6a..745a97e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "lint": "eslint -c .eslintrc.js 'src/**/*{.ts,.tsx}'", "format": "prettier --write src", "types:check": "tsc --noEmit", - "release": "release-it" + "release": "release-it", + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook" }, "repository": { "type": "git", @@ -34,6 +36,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.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", @@ -44,7 +55,8 @@ "@typescript-eslint/eslint-plugin": "^5.7.0", "@typescript-eslint/parser": "^5.7.0", "auto-changelog": "^2.3.0", - "bunchee": "1.8.3", + "babel-loader": "^8.2.5", + "bunchee": "1.8.5", "eslint": "^7.28.0", "eslint-config-prettier": "^8.3.0", "eslint-config-standard": "^16.0.3", @@ -53,6 +65,7 @@ "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-react": "^7.27.1", + "eslint-plugin-storybook": "^0.5.12", "jest": "^27.0.4", "prettier": "^2.3.1", "react": "^17.0.2", diff --git a/src/components/Rive.tsx b/src/components/Rive.tsx index a601524..a774227 100644 --- a/src/components/Rive.tsx +++ b/src/components/Rive.tsx @@ -2,12 +2,31 @@ import { Layout } from '@rive-app/canvas'; import React, { ComponentProps } from 'react'; import useRive from '../hooks/useRive'; -export type RiveProps = { +export interface RiveProps { + /** + * URL of the Rive asset, or path to where the public asset is stored. + */ src: string; + /** + * Artboard to render from the Rive asset. + * Defaults to the first artboard created. + */ artboard?: string; + /** + * Specify a starting animation to play. + */ animations?: string | string[]; + /** + * Specify a starting state machine to play. + */ stateMachines?: string | string[]; + /** + * Specify a starting Layout object to set Fill and Alignment for the drawing surface. See docs at https://help.rive.app/runtimes/layout for more on layout configuration. + */ layout?: Layout; + /** + * For `@rive-app/react-webgl`, sets this property to maintain a single WebGL context for multiple canvases. **We recommend to keep the default value** when rendering multiple Rive instances on a page. + */ useOffscreenRenderer?: boolean; }; diff --git a/tsconfig.json b/tsconfig.json index a96c7ce..2c19277 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,5 +17,5 @@ "target": "es5", "typeRoots": ["./types", "./node_modules/@types"] }, - "include": ["src/**/*"] + "include": ["src/**/*", "../examples/stories"] }