├── .gitattributes
├── .travis.yml
├── .gitignore
├── .babelrc
├── example
├── react-f1.gif
├── react-f1-chief.gif
├── chief
│ ├── index.js
│ ├── states.js
│ ├── Menu
│ │ ├── states.js
│ │ ├── transitions.js
│ │ └── index.js
│ ├── SelectIndicator
│ │ ├── index.js
│ │ ├── transitions.js
│ │ └── states.js
│ ├── transitions.js
│ └── FancyButton
│ │ ├── transitions.js
│ │ ├── states.js
│ │ └── index.js
└── f1
│ ├── index.js
│ ├── getTransitions.js
│ ├── ExampleButton.js
│ └── getStates.js
├── .npmignore
├── test
├── index.js
├── UI.js
├── jsDomBoiler.js
├── testCustomTargets.js
├── testCustomParsers.js
├── testF1Go.js
├── testMergeStates.js
└── testChiefGo.js
├── LICENSE.md
├── CHANGELOG.md
├── package.json
├── src
├── Chief.js
└── index.js
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.gif filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | before_install:
5 | - npm i react react-dom
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 | lib/
7 | /index.js
8 | /Chief.js
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["transform-inline-environment-variables"],
3 | "presets": [
4 | "react",
5 | "es2015"
6 | ]
7 | }
--------------------------------------------------------------------------------
/example/react-f1.gif:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9ef33724e30ea60a4ceea9a429b28aef5e7568c9f913d2e94a40d6b4c29a1eab
3 | size 29739
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 | test
7 | test.js
8 | demo/
9 | example/
10 | .npmignore
11 | LICENSE.md
--------------------------------------------------------------------------------
/example/react-f1-chief.gif:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:63faa92262e7b900e15a8d18f1503bcd9691b9ad12990eb900cff44dbc7af7c8
3 | size 70728
4 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | var jsDomBoiler = require('./jsDomBoiler');
2 | var test = require('tape');
3 |
4 | // this will drop in some boiler plate to be able to test
5 | // react without the dom if we're not in the dom
6 | jsDomBoiler();
7 |
8 | test('test f1 via go', require('./testF1Go'));
9 | test('test merging states', require('./testMergeStates'));
10 | test('test chief via go', require('./testChiefGo'));
11 | test('test custom targets', require('./testCustomTargets'));
12 | test('test custom parsers', require('./testCustomParsers'));
--------------------------------------------------------------------------------
/example/chief/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactDom = require('react-dom');
5 | var Menu = require('./Menu');
6 |
7 | // boilerplate since react doesn't allow rendering to body
8 | var container = document.body.appendChild(document.createElement('div'));
9 |
10 | // this function will render to the dom the react-f1 ui component
11 | render('out');
12 | render('idle');
13 |
14 | function render(state) {
15 | ReactDom.render(
16 |
,
19 | container
20 | );
21 | }
--------------------------------------------------------------------------------
/example/chief/states.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | out: {
3 | button1: 'out',
4 | button2: 'out',
5 | button3: 'out',
6 | indicator: 'out'
7 | },
8 |
9 | idle: {
10 | button1: 'idle',
11 | button2: 'idle',
12 | button3: 'idle'
13 | },
14 |
15 | selected1: {
16 | button1: 'selected',
17 | button2: 'idle',
18 | button3: 'idle',
19 | indicator: 'selected1'
20 | },
21 |
22 | selected2: {
23 | button1: 'idle',
24 | button2: 'selected',
25 | button3: 'idle',
26 | indicator: 'selected2'
27 | },
28 |
29 | selected3: {
30 | button1: 'idle',
31 | button2: 'idle',
32 | button3: 'selected',
33 | indicator: 'selected3'
34 | }
35 | };
--------------------------------------------------------------------------------
/example/chief/Menu/states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | out: {
5 | button1: 'out',
6 | button2: 'out',
7 | button3: 'out',
8 | indicator: 'out'
9 | },
10 |
11 | idle: {
12 | button1: 'idle',
13 | button2: 'idle',
14 | button3: 'idle'
15 | },
16 |
17 | selected1: {
18 | button1: 'selected',
19 | button2: 'idle',
20 | button3: 'idle',
21 | indicator: 'selected1'
22 | },
23 |
24 | selected2: {
25 | button1: 'idle',
26 | button2: 'selected',
27 | button3: 'idle',
28 | indicator: 'selected2'
29 | },
30 |
31 | selected3: {
32 | button1: 'idle',
33 | button2: 'idle',
34 | button3: 'selected',
35 | indicator: 'selected3'
36 | }
37 | };
--------------------------------------------------------------------------------
/example/chief/SelectIndicator/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactF1 = require('../../../src/');
5 | var states = require('./states');
6 | var transitions = require('./transitions');
7 |
8 | class SelectIndicator extends React.Component {
9 | render() {
10 | return
17 |
20 |
21 | }
22 | }
23 |
24 | SelectIndicator.defaultProps = {
25 | width: 20,
26 | height: 50,
27 | marginTop: 1
28 | };
29 |
30 | module.exports = SelectIndicator;
--------------------------------------------------------------------------------
/example/chief/transitions.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | from: 'out', to: 'idle', bi: true, animation: {
4 | button1: {
5 | delay: 0.5
6 | },
7 |
8 | button2: {
9 | delay: 0.6
10 | },
11 |
12 | button3: {
13 | delay: 0.7
14 | }
15 | }
16 | },
17 | {
18 | from: 'idle', to: 'selected2', bi: true
19 | },
20 | {
21 | from: 'selected2', to: 'selected1', bi: true, animation: {
22 | indicator: {
23 | delay: 0.5
24 | }
25 | }
26 | },
27 | {
28 | from: 'selected2', to: 'selected3', bi: true, animation: {
29 | indicator: {
30 | delay: 0.5
31 | }
32 | }
33 | },
34 | {
35 | from: 'selected3', to: 'selected1', bi: true, animation: {
36 | indicator: {
37 | delay: 0.5
38 | }
39 | }
40 | }
41 | ];
--------------------------------------------------------------------------------
/example/chief/Menu/transitions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = [
4 | {
5 | from: 'out', to: 'idle', bi: true, animation: {
6 | button1: {
7 | delay: 0.5
8 | },
9 |
10 | button2: {
11 | delay: 0.6
12 | },
13 |
14 | button3: {
15 | delay: 0.7
16 | }
17 | }
18 | },
19 | {
20 | from: 'idle', to: 'selected2', bi: true
21 | },
22 | {
23 | from: 'selected2', to: 'selected1', bi: true, animation: {
24 | indicator: {
25 | delay: 0.5
26 | }
27 | }
28 | },
29 | {
30 | from: 'selected2', to: 'selected3', bi: true, animation: {
31 | indicator: {
32 | delay: 0.5
33 | }
34 | }
35 | },
36 | {
37 | from: 'selected3', to: 'selected1', bi: true, animation: {
38 | indicator: {
39 | delay: 0.5
40 | }
41 | }
42 | }
43 | ];
--------------------------------------------------------------------------------
/example/f1/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactDom = require('react-dom');
5 | var ExampleButton = require('./ExampleButton');
6 |
7 | // boilerplate since react doesn't allow rendering to body
8 | var container = document.body.appendChild(document.createElement('div'));
9 |
10 | // this function will render to the dom the react-f1 ui component
11 | render('idle');
12 |
13 | function render(state) {
14 | ReactDom.render(
15 | {
21 | console.log('ExampleButton is in', state);
22 | }}
23 |
24 | // The following is to add mouse events
25 | // on mouse over go to the over state
26 | // on mouse out go back to the idle state
27 | onMouseOver={render.bind(null, 'over')}
28 | onMouseOut={render.bind(null, 'idle')}
29 | />,
30 | container
31 | );
32 | }
--------------------------------------------------------------------------------
/test/UI.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactF1 = require('./../src/');
5 |
6 | var states = {
7 | out: {
8 | item: {
9 | style: {
10 | width: 100,
11 | height: 100
12 | }
13 | }
14 | },
15 |
16 | idle: {
17 | item: {
18 | style: {
19 | width: 200,
20 | height: 100
21 | }
22 | }
23 | },
24 |
25 | over: {
26 | item: {
27 | style: {
28 | width: 300,
29 | height: 100
30 | }
31 | }
32 | }
33 | };
34 |
35 | var transitions = [
36 | { from: 'out', to: 'idle', bi: true },
37 | { from: 'idle', to: 'over', bi: true }
38 | ];
39 |
40 | class UI extends React.Component {
41 | render() {
42 | return
47 | SNAKES
53 | ;
54 | }
55 | }
56 |
57 | module.exports = UI;
--------------------------------------------------------------------------------
/example/chief/SelectIndicator/transitions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var eases = require('eases');
3 |
4 | module.exports = [
5 | {
6 | from: 'out', to: 'idle', bi: true, animation: {
7 | duration: 0.1
8 | }
9 | },
10 | {
11 | from: 'idle', to: 'selected2', bi: true, animation: {
12 | duration: 0.25,
13 | ease: eases.expoOut
14 | }
15 | },
16 |
17 | {
18 | from: 'selected2', to: 'selected1', animation: {
19 | duration: 0.25,
20 | ease: eases.bounceOut
21 | }
22 | },
23 | {
24 | from: 'selected1', to: 'selected2', animation: {
25 | duration: 0.25,
26 | ease: eases.backOut
27 | }
28 | },
29 |
30 | {
31 | from: 'selected2', to: 'selected3', animation: {
32 | duration: 0.25,
33 | ease: eases.bounceOut
34 | }
35 | },
36 | {
37 | from: 'selected3', to: 'selected2', animation: {
38 | duration: 0.25,
39 | ease: eases.backOut
40 | }
41 | },
42 |
43 |
44 | {
45 | from: 'selected3', to: 'selected1', bi: true, animation: {
46 | duration: 0.5,
47 | ease: eases.bounceOut
48 | }
49 | }
50 | ];
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2015 Jam3
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20 | OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/test/jsDomBoiler.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function() {
4 |
5 | // this will throw and if window doesnt exist then we set up jsdom
6 | try {
7 | window
8 | } catch(e) {
9 | initJSDOM();
10 | }
11 | };
12 |
13 | function initJSDOM() {
14 |
15 | // this boiler plate is borrowed from
16 | // http://jaketrent.com/post/testing-react-with-jsdom/
17 | var jsdom = require('jsdom')
18 |
19 | // setup the simplest document possible
20 | var doc = jsdom.jsdom('')
21 |
22 | // get the window object out of the document
23 | var win = doc.defaultView
24 |
25 | // set globals for mocha that make access to document and window feel
26 | // natural in the test environment
27 | global.document = doc
28 | global.window = win
29 |
30 | // take all properties of the window object and also attach it to the
31 | // mocha global object
32 | propagateToGlobal(win)
33 |
34 | // from mocha-jsdom https://github.com/rstacruz/mocha-jsdom/blob/master/index.js#L80
35 | function propagateToGlobal (window) {
36 | for (let key in window) {
37 | if (!window.hasOwnProperty(key)) continue
38 | if (key in global) continue
39 |
40 | global[key] = window[key]
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example/chief/FancyButton/transitions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var eases = require('eases');
3 |
4 | module.exports = [
5 | { from: 'out', to: 'idle', animation: {
6 | duration: 0.5,
7 | ease: eases.backOut
8 | }
9 | },
10 | { from: 'idle', to: 'out', animation: {
11 | duration: 0.5,
12 | ease: eases.expoIn
13 | }
14 | },
15 |
16 | { from: 'idle', to: 'over', animation: {
17 | duration: 0.25,
18 | ease: eases.backOut
19 | }
20 | },
21 | { from: 'over', to: 'idle', animation: {
22 | duration: 0.25,
23 | ease: eases.expoOut
24 | }
25 | },
26 |
27 | { from: 'over', to: 'preSelected', animation: {
28 | duration: 0.25,
29 | ease: eases.expoIn
30 | }
31 | },
32 | { from: 'preSelected', to: 'over', animation: {
33 | duration: 0.5,
34 | ease: eases.backOut
35 | }
36 | },
37 |
38 |
39 | { from: 'preSelected', to: 'swapSelected', bi: true, animation: {
40 | duration: 0
41 | }
42 | },
43 |
44 | { from: 'swapSelected', to: 'selected', animation: {
45 | duration: 0.5,
46 | ease: eases.backOut
47 | }
48 | },
49 | { from: 'selected', to: 'swapSelected', animation: {
50 | duration: 0.5,
51 | ease: eases.expoIn
52 | }
53 | }
54 | ];
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 7.1.1
2 |
3 | Fixes:
4 | - Passing targets a second time will cause an update to f1 targets and a re-render.
5 |
6 | # 7.1.0
7 |
8 | Fixes:
9 | - Updated `peerDepencies` to work off `react@15.0.0` amd `react-dom@15.0.0`
10 |
11 | Features:
12 | - Can now pass in custom targets which are not defined via `data-f1` tags. This will allow for `react-f1` to control other targets that might manipulate the dom. Example:
13 | ```
14 |
19 | ...
20 | ```
21 | - Custom parsers can now be passed to `react-f1` the format should be the same as what `f1` defines. Example:
22 | ```
23 |
29 |
42 |
51 | react-f1
52 |
53 |
54 | ;
55 | }
56 | }
57 |
58 | module.exports = ExampleButton;
--------------------------------------------------------------------------------
/example/f1/getStates.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function getStates() {
4 | return {
5 | // this is defines the idle state for the button
6 | idle: {
7 | // this is what the bg should look like in the idle state
8 | buttonBG: {
9 | style: {
10 | // regular old width, height, and fontSize
11 | width: 120,
12 | height: 120,
13 | fontSize: 14,
14 |
15 | // the following are passed to css transform
16 | // by recomposing a transformation matrix
17 | //
18 | // translate: [ x, y, z ]
19 | // rotate: [ x, y, z ]
20 | translate: [ 0, 0, 0 ],
21 | rotate: [ 0, 0, 0 ]
22 | }
23 | },
24 |
25 | // this is what the text should look like in the idle state
26 | buttonText: {
27 | style: {
28 | // color's can be represented by arrays that are
29 | // [ red, green, blue, alpha ]
30 | color: [ 0, 0, 0, 1 ],
31 | marginTop: 0
32 | }
33 | }
34 | },
35 |
36 | // this is defines the over state for the button
37 | over: {
38 | // this is what the bg should look like in the over state
39 | buttonBG: {
40 | style: {
41 | // regular old width and height animation
42 | width: 220,
43 | height: 220,
44 | fontSize: 30,
45 |
46 | // fancy properties
47 | translate: [ -40, 0, -200 ],
48 | rotate: [ -20, -45, 0 ]
49 | }
50 | },
51 |
52 | // this is what the text should look like in the over state
53 | buttonText: {
54 | style: {
55 | // color's can be represented by arrays that are
56 | // [ red, green, blue, alpha ]
57 | color: [ 255, 255, 255, 0.2 ],
58 | marginTop: -40
59 | }
60 | }
61 | }
62 | };
63 | };
--------------------------------------------------------------------------------
/example/chief/FancyButton/states.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var merge = require('deep-extend');
4 |
5 | module.exports = function(props) {
6 | var width = props.width;
7 | var height = props.height;
8 |
9 | var idle = {
10 | bg1: {
11 | style: {
12 | transformOrigin: [ 1, 0.5 ],
13 | scale: [ 0, 1 ]
14 | }
15 | },
16 |
17 | bg2: {
18 | style: {
19 | transformOrigin: [ 0, 0.5 ],
20 | scale: [ 1, 1 ]
21 | }
22 | },
23 |
24 | container: {
25 | style: {
26 | rotate: [ 0, 0, 0 ]
27 | }
28 | }
29 | };
30 |
31 | var out = merge(
32 | {},
33 | idle,
34 | {
35 | container: {
36 | style: {
37 | rotate: [ 90, 0, 0 ]
38 | }
39 | }
40 | }
41 | );
42 |
43 | var over = merge(
44 | {},
45 | idle,
46 | {
47 | bg1: {
48 | style: {
49 | scale: [ 0.15, 1 ]
50 | }
51 | },
52 |
53 | bg2: {
54 | style: {
55 | scale: [ 0.85, 1 ]
56 | }
57 | }
58 | }
59 | );
60 |
61 | var preSelected = merge(
62 | {},
63 | over,
64 | {
65 | container: {
66 | style: {
67 | rotate: [ 90, 0, 0 ]
68 | }
69 | }
70 | }
71 | );
72 |
73 | var swapSelected = merge(
74 | {},
75 | preSelected,
76 | {
77 | bg1: {
78 | style: {
79 | scale: [ 1, 1 ]
80 | }
81 | },
82 |
83 | bg2: {
84 | style: {
85 | scale: [ 0, 1 ]
86 | }
87 | }
88 | }
89 | );
90 |
91 | var selected = merge(
92 | {},
93 | swapSelected,
94 | {
95 | container: {
96 | style: {
97 | rotate: [ 180, 0, 0 ]
98 | }
99 | }
100 | }
101 | );
102 |
103 | return {
104 | idle: idle,
105 | over: over,
106 | out: out,
107 | preSelected: preSelected,
108 | swapSelected: swapSelected,
109 | selected: selected
110 | };
111 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-f1",
3 | "version": "8.1.0",
4 | "description": "React UI animation components built on top of f1-dom and f1.",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "author": {
8 | "name": "Mikko Haapoja",
9 | "email": "me@mikkoh.com",
10 | "url": "https://github.com/mikkoh"
11 | },
12 | "scripts": {
13 | "start": "npm run browser",
14 | "prepublish": "npm run compile",
15 | "postpublish": "npm run clean-compile",
16 | "compile": "babel -d ./ src/",
17 | "clean-compile": "rm index.js; rm Chief.js",
18 | "test": "npm run test-pretranspile; npm run test-transpile;",
19 | "test-pretranspile": "PATH_F1='./../src/' PATH_CHIEF='../src/Chief' npm run node",
20 | "test-transpile": "npm run compile; PATH_F1='../' PATH_CHIEF='../Chief' npm run node; npm run clean-compile",
21 | "test-browser": "PATH_F1='./../src/' PATH_CHIEF='../src/Chief' npm run dev-browser",
22 | "example-f1": "budo example/f1/index.js --live --open",
23 | "example-chief": "budo example/chief/index.js --live --open",
24 | "f1-example": "npm run example-f1",
25 | "chief-example": "npm run example-chief",
26 | "browser": "npm run dev-browser",
27 | "node": "babel-node test/index.js",
28 | "dev-browser": "PATH_F1='./../src/' PATH_CHIEF='../src/Chief' budo test/index.js --live --open",
29 | "dev-node": "PATH_F1='./../src/' PATH_CHIEF='../src/Chief' nodemon --exec \"babel-node test/index.js\""
30 | },
31 | "browserify": {
32 | "transform": [
33 | "babelify"
34 | ]
35 | },
36 | "dependencies": {
37 | "babel-plugin-transform-inline-environment-variables": "^6.5.0",
38 | "deep-extend": "^0.4.0",
39 | "f1": "^8.0.0",
40 | "f1-dom": "^9.0.0",
41 | "gl-mat4": "^1.1.4"
42 | },
43 | "peerDependencies": {
44 | "react": "^15.0.0",
45 | "react-dom": "^15.0.0"
46 | },
47 | "devDependencies": {
48 | "async": "^1.5.2",
49 | "babel-cli": "^6.6.5",
50 | "babel-preset-es2015": "^6.6.0",
51 | "babel-preset-react": "^6.5.0",
52 | "babelify": "^7.2.0",
53 | "budo": "^5.1.5",
54 | "dom-select": "^1.1.0",
55 | "eases": "^1.0.8",
56 | "jsdom": "^8.0.2",
57 | "nodemon": "^1.8.1",
58 | "tape": "^4.5.1"
59 | },
60 | "keywords": [
61 | "react,f1,animation,ui,jam3,dom,react-dom"
62 | ],
63 | "repository": {
64 | "type": "git",
65 | "url": "git://github.com/Jam3/react-f1.git"
66 | },
67 | "homepage": "https://github.com/Jam3/react-f1",
68 | "bugs": {
69 | "url": "https://github.com/Jam3/react-f1/issues"
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/test/testCustomTargets.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 | var domSelect = require('dom-select');
4 | var ReactF1 = require(process.env.PATH_F1);
5 | var async = require('async');
6 |
7 | var container;
8 |
9 |
10 | var target;
11 |
12 |
13 | var states = {
14 | out: {
15 | item: {
16 | value: "out"
17 | }
18 | },
19 |
20 | idle: {
21 | item: {
22 | value: "idle"
23 | }
24 |
25 | }
26 | };
27 |
28 | var transitions = [
29 | { from: 'out', to: 'idle', bi: true }
30 | ];
31 |
32 |
33 | module.exports = function(t) {
34 |
35 | var statesVisited = [];
36 |
37 | // each one of these objects will be applied to the `react-f1` component
38 | // a callback will be passed to the onComplete function if no onComplete
39 | // is passed then it is automatically
40 | async.eachSeries(
41 | [
42 | { go: 'out', states: states, transitions: transitions },
43 | {
44 | go: 'idle', states: states, transitions: transitions,
45 | onComplete: function(callback, state, stateName) {
46 | var el = domSelect('#target');
47 | t.equal(stateName, 'idle', 'stateName is idle');
48 | t.equal(typeof state, 'object', 'state is an object');
49 | t.equal(el.value, 'idle', 'target input value is set to idle');
50 |
51 | statesVisited.push(stateName);
52 |
53 | callback(null);
54 | }
55 | },
56 | {
57 | go: 'out', states: states, transitions: transitions,
58 | onComplete: function(callback, state, stateName) {
59 | var el = domSelect('#target');
60 | t.equal(stateName, 'out', 'stateName is out');
61 | t.equal(typeof state, 'object', 'state is an object');
62 | t.equal(el.value, 'out', 'target input value is set to out');
63 |
64 | statesVisited.push(stateName);
65 |
66 | callback(null);
67 | }
68 | }
69 | ],
70 | render,
71 | function() {
72 | t.deepEqual(statesVisited, ['idle', 'out'], 'visited all states');
73 |
74 | container.parentNode.removeChild(container);
75 | target.parentNode.removeChild(target);
76 |
77 | t.end();
78 | }
79 | );
80 | };
81 |
82 |
83 | function render(settings, callback) {
84 |
85 | container = container || document.body.appendChild(document.createElement('div'));
86 |
87 | target = target || document.body.appendChild(document.createElement('input'));
88 | target.id = 'target';
89 |
90 | settings.onComplete = settings.onComplete && settings.onComplete.bind(null, callback);
91 |
92 | settings.targets = {
93 | item: target
94 | };
95 |
96 | var component =
100 | ;
101 |
102 | ReactDom.render(component, container);
103 |
104 | if(!settings.onComplete) {
105 | callback(null);
106 | }
107 | }
--------------------------------------------------------------------------------
/example/chief/FancyButton/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactF1 = require('../../../src');
5 | var states = require('./states');
6 | var transitions = require('./transitions');
7 |
8 | class FancyButton extends React.Component {
9 | constructor(props) {
10 | super(props);
11 |
12 | this.handleMouseOver = this.handleMouseOver.bind(this);
13 | this.handleMouseOut = this.handleMouseOut.bind(this);
14 |
15 | this.state = {
16 | go: 'out'
17 | };
18 | }
19 |
20 | handleMouseOver() {
21 | if(this.state.go === 'idle') {
22 | this.setState({
23 | go: 'over',
24 | onComplete: null
25 | });
26 | }
27 | }
28 |
29 | handleMouseOut() {
30 | if(this.state.go === 'over') {
31 | this.setState({
32 | go: 'idle',
33 | onComplete: null
34 | });
35 | }
36 | }
37 |
38 | updateStateFromProps(props) {
39 | if(this.state.go !== props.go) {
40 | this.setState({
41 | go: props.go,
42 | onComplete: props.onComplete
43 | });
44 | }
45 | }
46 |
47 | componentWillMount() {
48 | this.updateStateFromProps(this.props);
49 | }
50 |
51 | componentWillReceiveProps(nextProps) {
52 | this.updateStateFromProps(nextProps)
53 | }
54 |
55 | render() {
56 | var style = Object.assign(
57 | {},
58 | this.props.style,
59 | {
60 | width: this.props.width,
61 | height: this.props.height
62 | }
63 | );
64 |
65 | return
75 |
108 | ;
109 | }
110 | }
111 |
112 | FancyButton.defaultProps = {
113 | width: 200,
114 | height: 50,
115 | onSelect: function() {}
116 | };
117 |
118 | module.exports = FancyButton;
--------------------------------------------------------------------------------
/test/testCustomParsers.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 | var domSelect = require('dom-select');
4 | var ReactF1 = require(process.env.PATH_F1);
5 | var async = require('async');
6 |
7 | var container;
8 |
9 |
10 | var target;
11 |
12 |
13 | var states = {
14 | out: {
15 | item: {
16 | play: true
17 | }
18 | },
19 |
20 | idle: {
21 | item: {
22 | play: false
23 | }
24 |
25 | }
26 | };
27 |
28 | var transitions = [
29 | { from: 'out', to: 'idle', bi: true }
30 | ];
31 |
32 |
33 | module.exports = function(t) {
34 |
35 | var statesVisited = [];
36 |
37 | // each one of these objects will be applied to the `react-f1` component
38 | // a callback will be passed to the onComplete function if no onComplete
39 | // is passed then it is automatically
40 | async.eachSeries(
41 | [
42 | { go: 'out', states: states, transitions: transitions },
43 | {
44 | go: 'idle', states: states, transitions: transitions,
45 | onComplete: function(callback, state, stateName) {
46 | var el = domSelect('[data-f1]');
47 | t.equal(stateName, 'idle', 'stateName is idle');
48 | t.equal(typeof state, 'object', 'state is an object');
49 | t.equal(el.innerHTML, 'The value has been set to false', 'The div innerHTML has been set to the correct string');
50 |
51 | statesVisited.push(stateName);
52 |
53 | callback(null);
54 | }
55 | },
56 | {
57 | go: 'out', states: states, transitions: transitions,
58 | onComplete: function(callback, state, stateName) {
59 | var el = domSelect('[data-f1]');
60 | t.equal(stateName, 'out', 'stateName is out');
61 | t.equal(typeof state, 'object', 'state is an object');
62 | t.equal(el.innerHTML, 'The value has been set to true', 'The div innerHTML has been set to the correct string');
63 |
64 | statesVisited.push(stateName);
65 |
66 | callback(null);
67 | }
68 | }
69 | ],
70 | render,
71 | function() {
72 | t.deepEqual(statesVisited, ['idle', 'out'], 'visited all states');
73 |
74 | container.parentNode.removeChild(container);
75 |
76 | t.end();
77 | }
78 | );
79 | };
80 |
81 |
82 | function render(settings, callback) {
83 |
84 | container = container || document.body.appendChild(document.createElement('div'));
85 |
86 |
87 | settings.onComplete = settings.onComplete && settings.onComplete.bind(null, callback);
88 |
89 | settings.parsers = {
90 | init: [],
91 | update: [
92 | function(item, state){
93 | item.innerHTML = (state.play)? 'The value has been set to true' : 'The value has been set to false';
94 | }
95 | ]
96 | };
97 |
98 | var component =
102 |
103 | ;
104 |
105 | ReactDom.render(component, container);
106 |
107 | if(!settings.onComplete) {
108 | callback(null);
109 | }
110 | }
--------------------------------------------------------------------------------
/test/testF1Go.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 | var domSelect = require('dom-select');
4 | var ReactF1 = require(process.env.PATH_F1);
5 | var async = require('async');
6 |
7 | var container;
8 |
9 | var states = {
10 | out: {
11 | item: {
12 | style: {
13 | width: 100,
14 | height: 50
15 | }
16 | }
17 | },
18 |
19 | idle: {
20 | item: {
21 | style: {
22 | width: 200,
23 | height: 300
24 | }
25 | }
26 | }
27 | };
28 |
29 | var transitions = [
30 | { from: 'out', to: 'idle', bi: true }
31 | ];
32 |
33 | module.exports = function(t) {
34 |
35 | var statesVisited = [];
36 |
37 | // each one of these objects will be applied to the `react-f1` component
38 | // a callback will be passed to the onComplete function if no onComplete
39 | // is passed then it is automatically
40 | async.eachSeries(
41 | [
42 | { go: 'out', states: states, transitions: transitions },
43 | {
44 | go: 'idle', states: states, transitions: transitions,
45 | style: {
46 | backgroundColor: '#00FFFF'
47 | },
48 | onComplete: function(callback, state, stateName) {
49 | var el = domSelect('[data-f1]');
50 |
51 | t.equal(stateName, 'idle', 'stateName is idle');
52 | t.equal(typeof state, 'object', 'state is an object');
53 | t.equal(el.style.width, '200px', 'width is 200px');
54 | t.equal(el.style.height, '300px', 'height is 300px');
55 | t.equal(el.style.backgroundColor, 'rgb(0, 255, 255)', 'backgroundColor style was mixed in');
56 |
57 | statesVisited.push(stateName);
58 |
59 | callback(null);
60 | }
61 | },
62 | {
63 | go: 'out', states: states, transitions: transitions,
64 | style: {
65 | backgroundColor: '#00FFFF'
66 | },
67 | onComplete: function(callback, state, stateName) {
68 | var el = domSelect('[data-f1]');
69 |
70 | t.equal(stateName, 'out', 'stateName is out');
71 | t.equal(typeof state, 'object', 'state is an object');
72 | t.equal(el.style.width, '100px', 'width is 100px');
73 | t.equal(el.style.height, '50px', 'height is 50px');
74 | t.equal(el.style.backgroundColor, 'rgb(0, 255, 255)', 'backgroundColor style was mixed in');
75 |
76 | statesVisited.push(stateName);
77 |
78 | callback(null);
79 | }
80 | }
81 | ],
82 | render,
83 | function() {
84 | t.deepEqual(statesVisited, ['idle', 'out'], 'visited all states');
85 |
86 | container.parentNode.removeChild(container);
87 |
88 | t.end();
89 | }
90 | );
91 | };
92 |
93 |
94 | function render(settings, callback) {
95 |
96 | container = container || document.body.appendChild(document.createElement('div'));
97 | settings.onComplete = settings.onComplete && settings.onComplete.bind(null, callback);
98 |
99 | var component =
103 | Test
104 | ;
105 |
106 | // TestUtils.renderIntoDocument(component);
107 | ReactDom.render(component, container);
108 |
109 | if(!settings.onComplete) {
110 | callback(null);
111 | }
112 | }
--------------------------------------------------------------------------------
/example/chief/Menu/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 | var ReactF1 = require('../../../src');
5 | var Chief = require('../../../src/Chief');
6 | var states = require('./states');
7 | var transitions = require('./transitions');
8 | var FancyButton = require('../FancyButton');
9 | var SelectIndicator = require('../SelectIndicator');
10 |
11 | /****************************************************************************/
12 | /***** You should probably just to the `render` function because that's *****/
13 | /***********************w here all the MAGIC happens ************************/
14 | /****************************************************************************/
15 | class Menu extends React.Component {
16 |
17 | constructor(props) {
18 | super(props);
19 |
20 | this.state = {};
21 | }
22 |
23 | updateStateFromProps(props) {
24 |
25 | if(this.state.propsGo !== props.go) {
26 | this.setState({
27 | go: props.go,
28 | propsGo: props.go
29 | });
30 | }
31 | }
32 |
33 | componentWillMount() {
34 | this.updateStateFromProps(this.props);
35 | }
36 |
37 | componentWillReceiveProps(nextProps) {
38 | this.updateStateFromProps(nextProps);
39 | }
40 |
41 | handleClick(state) {
42 | this.setState({
43 | go: state
44 | });
45 | }
46 |
47 | render() {
48 | return
58 | {
59 | // with React it's very hard to manipulate deeply nested (grand child)
60 | // component's properties.
61 | // This is why you need to pass in a function that will accept an Object
62 | (states) => {
63 |
64 | var buttonSize = {
65 | width: 200,
66 | height: 50
67 | };
68 |
69 | var paddingBetween = 1;
70 |
71 | return
72 |
79 |
89 |
100 |
111 |
112 | }
113 | }
114 | ;
115 | }
116 | }
117 |
118 | module.exports = Menu;
--------------------------------------------------------------------------------
/test/testMergeStates.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 | var domSelect = require('dom-select');
4 | var ReactF1 = require(process.env.PATH_F1);
5 | var async = require('async');
6 | var merge = require('deep-extend');
7 |
8 | var container;
9 |
10 | var states = {
11 | out: {
12 | item: {
13 | style: {
14 | width: 100,
15 | height: 50
16 | }
17 | }
18 | },
19 |
20 | idle: {
21 | item: {
22 | style: {
23 | width: 10,
24 | height: 10
25 | }
26 | }
27 | }
28 | };
29 |
30 | var transitions = [
31 | { from: 'out', to: 'idle', bi: true }
32 | ];
33 |
34 | module.exports = function(t) {
35 |
36 | // each one of these objects will be applied to the `react-f1` component
37 | // a callback will be passed to the onComplete function if no onComplete
38 | // is passed then it is automatically
39 | async.eachSeries(
40 | [
41 | function() { return { go: 'out', states: states, transitions: transitions } },
42 | function() {
43 | return {
44 | go: 'idle', states: getState('idle', 400, 333), transitions: transitions,
45 | onComplete: function(callback, state, stateName) {
46 | var el = domSelect('[data-f1]');
47 |
48 | t.equal(el.style.width, '400px', 'modified state width was correct');
49 | t.equal(el.style.height, '333px', 'modified state height was correct');
50 |
51 | callback(null);
52 | }
53 | }
54 | },
55 |
56 | // just to test that out didn't get effected in anyway
57 | function() {
58 | return {
59 | go: 'out', states: states, transitions: transitions,
60 | onComplete: function(callback, state, stateName) {
61 | var el = domSelect('[data-f1]');
62 |
63 | t.equal(el.style.width, '100px', 'unchanged state width is correct');
64 | t.equal(el.style.height, '50px', 'unchanged state height is correct');
65 |
66 | callback(null);
67 | }
68 | };
69 | },
70 |
71 | // the following is to test setting states when we're already on that state and not animating
72 | function() {
73 | return {
74 | go: 'out', states: getState('out', 33, 44), transitions: transitions
75 | };
76 | }
77 | ],
78 | render,
79 | function() {
80 | var el = domSelect('[data-f1]');
81 |
82 | t.equal(el.style.width, '33px', 'width correct after set state on static state');
83 | t.equal(el.style.height, '44px', 'height correct after set state on static state');
84 |
85 | container.parentNode.removeChild(container);
86 |
87 | t.end();
88 | }
89 | );
90 | };
91 |
92 | function getState(stateName, width, height) {
93 | var newState = merge({}, states);
94 |
95 | Object.assign(
96 | newState[ stateName ].item.style,
97 | {
98 | width: width,
99 | height: height
100 | }
101 | );
102 |
103 | return newState;
104 | }
105 |
106 | function render(settings, callback) {
107 |
108 | settings = settings();
109 |
110 | container = container || document.body.appendChild(document.createElement('div'));
111 | settings.onComplete = settings.onComplete && settings.onComplete.bind(null, callback);
112 |
113 | var component =
116 | Test
122 | ;
123 |
124 | // TestUtils.renderIntoDocument(component);
125 | ReactDom.render(component, container);
126 |
127 | if(!settings.onComplete) {
128 | process.nextTick(function() {
129 | callback(null);
130 | });
131 | }
132 | }
--------------------------------------------------------------------------------
/src/Chief.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
4 |
5 | var React = require('react');
6 | var f1Chief = require('f1/chief');
7 | var merge = require('deep-extend');
8 | var chiefBridge = function (target, onUpdate) {
9 |
10 | target.state = null;
11 | target.onComplete = null;
12 |
13 | return {
14 | isInitialized: false,
15 |
16 | init: function (state) {
17 | this.isInitialized = true;
18 | target.go = state;
19 | onUpdate();
20 | },
21 |
22 | go: function (state, onComplete) {
23 | target.go = state;
24 | target.onComplete = onComplete;
25 | onUpdate();
26 | }
27 | };
28 | };
29 |
30 | class Chief extends React.Component {
31 |
32 | constructor(props) {
33 | super(props);
34 |
35 | this.handleUpdate = this.handleUpdate.bind(this);
36 |
37 | this.chief = null;
38 | this.chiefStates = null;
39 | this.state = {};
40 | }
41 |
42 | componentWillMount() {
43 | this.chief = f1Chief({
44 | transitions: this.props.transitions,
45 | states: this.props.states,
46 | targets: this.getTargetsFromStates(this.props.states),
47 | onUpdate: function () {
48 | this.props.onUpdate.apply(undefined, arguments);
49 | }.bind(this)
50 | });
51 | }
52 |
53 | componentWillUnMount() {
54 | if (this.chief) {
55 | this.chief.destroy();
56 | }
57 | }
58 |
59 | componentDidMount() {
60 | this.chief.init(this.props.go);
61 | }
62 |
63 | componentWillReceiveProps(nextProps) {
64 |
65 | var goState = nextProps.go;
66 |
67 | if (goState && (this.state.propsState !== goState || this.state.propsOnComplete !== nextProps.onComplete)) {
68 | this.chief.go(goState, nextProps.onComplete);
69 |
70 | this.setState({
71 | propsState: goState,
72 | propsOnComplete: nextProps.onComplete
73 | });
74 | }
75 | }
76 |
77 | handleUpdate() {
78 | this.setState({
79 | chiefStates: this.chiefStates
80 | });
81 | }
82 |
83 | getTargetsFromStates(states) {
84 | var stateName = Object.keys(states)[0];
85 | var targets = {};
86 | var chiefState;
87 |
88 | this.chiefStates = {};
89 |
90 | for (var targetName in states[stateName]) {
91 | chiefState = {};
92 | this.chiefStates[targetName] = chiefState;
93 |
94 | targets[targetName] = chiefBridge(chiefState, this.handleUpdate);
95 | }
96 |
97 | return targets;
98 | }
99 |
100 | getChildrenFromFunction(chiefState) {
101 | return this.props.children(chiefState);
102 | }
103 |
104 | cleanProps(props) {
105 | delete props.go;
106 | delete props.transitions;
107 | delete props.states;
108 | delete props.onComplete;
109 | delete props.onUpdate;
110 | delete props.component;
111 | return props;
112 | }
113 |
114 | render() {
115 |
116 | var chiefState = this.state.chiefStates;
117 | var children;
118 |
119 | if (chiefState) {
120 |
121 | if (typeof this.props.children === 'function') {
122 | children = this.getChildrenFromFunction(chiefState);
123 | } else {
124 | throw new Error('props.children should be a function that accepts chief states');
125 | }
126 | } else {
127 | children = this.props.children;
128 | }
129 |
130 | var props = _extends({}, this.props);
131 |
132 | return React.createElement(
133 | this.props.component || 'div',
134 | this.cleanProps(props),
135 | children
136 | );
137 | }
138 | };
139 |
140 | Chief.defaultProps = {
141 | onUpdate: function () {}, // this.props.onUpdate(state, this.props.go);
142 | onComplete: function () {}
143 | };
144 |
145 | module.exports = Chief;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
4 |
5 | var React = require('react');
6 | var f1DOM = require('f1-dom');
7 | var merge = require('deep-extend');
8 |
9 | class ReactF1 extends React.Component {
10 |
11 | constructor(props) {
12 | super(props);
13 |
14 | this.hasMounted = false;
15 | this.f1 = null;
16 | this.f1States = null;
17 |
18 | this.state = {};
19 | }
20 |
21 | setupFromProps(props) {
22 | if (!this.f1) {
23 | this.initFromProps(props);
24 | } else {
25 | this.updateFromProps(props);
26 | }
27 | }
28 |
29 | initFromProps(props) {
30 | if(this.hasMounted && !this.f1 && props.go && props.states && props.transitions) {
31 |
32 | this.f1States = merge({}, props.states);
33 |
34 | var f1 = this.f1 = f1DOM({
35 | el: this.el,
36 | states: this.f1States,
37 | transitions: props.transitions,
38 | targets: props.targets,
39 | parsers: props.parsers
40 | });
41 |
42 | f1.on('state', this.handleState.bind(this));
43 | f1.on('update', this.handleUpdate.bind(this));
44 | this.updateListenersFromProps(props);
45 |
46 | f1.init(props.go);
47 | }
48 | }
49 |
50 | updateFromProps(props) {
51 | var states;
52 |
53 | if(this.f1) {
54 | // if we've received new states then just mege into current states
55 | if (props.states) {
56 | merge(this.f1States, props.states);
57 | }
58 |
59 | // if we've received targets then reset them
60 | if(props.targets) {
61 | this.f1.targets(props.targets);
62 | }
63 |
64 | // call update to ensure everything looks right and is in its calculated state
65 | if(props.states || props.targets) {
66 | this.f1.update();
67 | }
68 |
69 | if (props.go) {
70 | this.f1.go(props.go, props.onComplete);
71 | }
72 | }
73 |
74 | this.updateListenersFromProps(props);
75 | }
76 |
77 | updateListenersFromProps(props) {
78 | this.setState({
79 | onUpdate: props.onUpdate,
80 | onState: props.onState
81 | });
82 | }
83 |
84 | handleUpdate() {
85 | if (this.state.onUpdate) {
86 | this.state.onUpdate.apply(undefined, arguments);
87 | }
88 | }
89 |
90 | handleState() {
91 | if (this.state.onState) {
92 | this.state.onState.apply(undefined, arguments);
93 | }
94 | }
95 |
96 | componentWillReceiveProps(nextProps) {
97 | this.setupFromProps(nextProps);
98 | }
99 |
100 | componentDidMount() {
101 | this.hasMounted = true;
102 | this.setupFromProps(this.props);
103 | }
104 |
105 | componentWillUnmount() {
106 | if (this.f1) {
107 | this.f1.destroy();
108 | }
109 | }
110 |
111 | cleanProps(props) {
112 | delete props.go;
113 | delete props.transitions;
114 | delete props.states;
115 | delete props.onComplete;
116 | delete props.onUpdate;
117 | delete props.component;
118 | return props;
119 | }
120 |
121 | getElement(el) {
122 | this.el = el;
123 | }
124 |
125 | render() {
126 | var style = merge(
127 | {
128 | perspective: '1000px'
129 | },
130 | this.props.style
131 | );
132 |
133 | if (!this.f1) {
134 | style = merge({}, this.props.style, {
135 | display: 'none'
136 | });
137 | }
138 |
139 | var props = _extends({}, this.props, {
140 | style: style,
141 | ref: this.getElement.bind(this)
142 | });
143 |
144 | return React.createElement(
145 | this.props.component || 'div',
146 | this.cleanProps(props),
147 | this.props.children
148 | );
149 | }
150 | }
151 |
152 | module.exports = ReactF1;
--------------------------------------------------------------------------------
/test/testChiefGo.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDom = require('react-dom');
3 | var domSelect = require('dom-select');
4 | var Chief = require(process.env.PATH_CHIEF);
5 | var UI = require('./UI');
6 | var async = require('async');
7 |
8 | var container;
9 |
10 | var states = {
11 | out: {
12 | item1: 'out',
13 | item2: 'out'
14 | },
15 |
16 | idle: {
17 | item1: 'idle',
18 | item2: 'idle'
19 | },
20 |
21 | idle2: {
22 | item1: 'idle',
23 | item2: 'out'
24 | }
25 | };
26 |
27 | var transitions = [
28 | {
29 | from: 'out', to: 'idle', bi: true, animation: {
30 | item1: {
31 | delay: 0.5
32 | }
33 | }
34 | },
35 | {
36 | from: 'idle', to: 'idle2', bi: true
37 | }
38 | ];
39 |
40 | module.exports = function(t) {
41 |
42 | var statesVisited = [];
43 | var updateItem1WasDelayed = false;
44 | var updateItemsWereInSameState = false;
45 |
46 | // each one of these objects will be applied to the `react-f1` component
47 | // a callback will be passed to the onComplete function if no onComplete
48 | // is passed then it is automatically
49 | async.eachSeries(
50 | [
51 | { go: 'out', states: states, transitions: transitions },
52 | {
53 | go: 'idle', states: states, transitions: transitions,
54 | style: {
55 | backgroundColor: '#00FFFF'
56 | },
57 | onUpdate: function(state, stateName) {
58 | updateItem1WasDelayed = updateItem1WasDelayed || (state.item1 === 'out' && state.item2 === 'idle');
59 | updateItemsWereInSameState = state.item1 === 'idle' && state.item2 === 'idle';
60 | },
61 | onComplete: function(callback, state, stateName) {
62 |
63 | t.deepEqual(stateName, 'idle', 'idle: state name was correct');
64 | t.deepEqual(state, states.idle, 'idle: state was correct for complete');
65 | statesVisited.push(stateName);
66 |
67 | callback(null);
68 | }
69 | },
70 | {
71 | go: 'idle2', states: states, transitions: transitions,
72 | style: {
73 | backgroundColor: '#00FFFF'
74 | },
75 | onComplete: function(callback, state, stateName) {
76 |
77 | t.deepEqual(stateName, 'idle2', 'idle2: state name was correct');
78 | t.deepEqual(state, states.idle2, 'idle2: state was correct for complete');
79 | statesVisited.push(stateName);
80 |
81 | callback(null);
82 | }
83 | },
84 | {
85 | go: 'out', states: states, transitions: transitions,
86 | style: {
87 | backgroundColor: '#00FFFF'
88 | },
89 | onComplete: function(callback, state, stateName) {
90 |
91 | t.deepEqual(stateName, 'out', 'out: state name was correct');
92 | t.deepEqual(state, states.out, 'out: state was correct for complete');
93 | statesVisited.push(stateName);
94 |
95 | callback(null);
96 | }
97 | }
98 | ],
99 | render,
100 | function() {
101 | t.deepEqual(statesVisited, ['idle', 'idle2', 'out'], 'visited all states');
102 | t.ok(updateItem1WasDelayed, 'item 1 was delayed from item 2');
103 | t.ok(updateItemsWereInSameState, 'final call to update had item1 and item2 in idle');
104 |
105 | container.parentNode.removeChild(container);
106 |
107 | t.end();
108 | }
109 | );
110 | };
111 |
112 |
113 | function render(settings, callback) {
114 |
115 | container = container || document.body.appendChild(document.createElement('div'));
116 | settings.onComplete = settings.onComplete && settings.onComplete.bind(null, callback);
117 |
118 | var component =
122 | {
123 | (states) => {
124 | return
125 |
126 |
127 |
;
128 | }
129 | }
130 | ;
131 |
132 | ReactDom.render(component, container);
133 |
134 | if(!settings.onComplete) {
135 | callback(null);
136 | }
137 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-f1
2 |
3 | [](http://github.com/badges/stability-badges)
4 |
5 | React UI animation components built on top of [`f1-dom`](https://www.npmjs.com/package/f1-dom) and [`f1`](https://www.npmjs.com/package/f1).
6 |
7 | ### Features
8 |
9 | - Create complex animations with custom delays, durations, eases
10 | - Animate individual properties (scale, opacity, etc.) independently of each other eg. delay the opacity animation by 0.5 seconds but scale immediately
11 | - Special handling for properties such as css colors and transforms (translate, scale, rotate) to allow for easier animation
12 | - True separation of concerns. Animations defined outside of application implementation (less Spaghetti code)
13 | - Absolute control over animations from page transitions down to individual ui components
14 | - Uses path finding to figure out how to animate from one state to another. For instance how to animate from a button from an out state to a pressed state (write less logic for complex animations)
15 |
16 | ### Table Of Contents
17 | - [Usage](#usage)
18 | - [Examples](#examples)
19 | + [Example ReactF1](#example-reactf1)
20 | + [Example Chief](#example-chief)
21 | - [Documentation](#documentation)
22 | + [ReactF1](#const-reactf1--requirereact-f1)
23 | + [Chief](#const-chief--requirereact-f1chief)
24 |
25 | ### Components
26 |
27 | There are two components which are exposed in this module:
28 | ```
29 | var ReactF1 = require('react-f1');
30 | // and
31 | var Chief = require('react-f1/Chief');
32 | ```
33 |
34 | #### `ReactF1`
35 | `ReactF1` can be used to create complete animated UI components such as buttons, toggles, accordians, etc.
36 |
37 | #### `Chief`
38 | `Chief` is used to control `ReactF1` components. For instance when a page animate's in one `Chief` component can tell all button's (which are `ReactF1` components or other `Chief` components) on a page to animate in staggered/delayed from one another.
39 |
40 | ## Usage
41 |
42 | [](https://www.npmjs.com/package/react-f1)
43 |
44 | #### Install
45 | ```bash
46 | $ npm i react-f1 react react-dom --save
47 | ```
48 |
49 | ## Examples
50 |
51 | There is an example folder distributed with this module. It contains two examples. One being a small example of how to use `ReactF1` and the other on how to use `Chief`.
52 |
53 | Details on how to run these examples are noted below. Once running in your browser you can simply edit the js files listed below and see changes immediately in browser.
54 |
55 | ## Example ReactF1
56 |
57 | 
58 |
59 | This example builds and renders a small animated button that you can see in use above.
60 |
61 | #### To run the ReactF1 example:
62 | ```bash
63 | $ npm run example-f1
64 | ```
65 |
66 | Below is a description of all example files that you might want to edit. All of these files have comments which explain each piece of the application:
67 |
68 | **example/f1/ExampleButton.js:** Example Button Component built using ReactF1.
69 |
70 | **example/f1/getStates.js:** a function which returns an Object which defines what the button should look like in each state.
71 |
72 | **example/f1/getTransitions.js:** a function which returns an Array which defines how to animate between states.
73 |
74 | ## Example Chief
75 |
76 | 
77 |
78 | The above example uses two components `SelectIndicator` (small line that moves up and down on the left) and `FancyButton` which is the buttons on the right. States and logic for selected buttons are handled by `Chief`.
79 |
80 | **To run the Chief example:**
81 | ```bash
82 | $ npm run example-chief
83 | ```
84 |
85 | **example/chief/Menu/:** This folder contains all code including states and transitions used by `Chief` to create a Menu. (this is the menu in the above gif)
86 |
87 | **example/chief/FancyButton/:** This folder contains all code including states and transitions used by the FancyButton. (buttons on the right side of the menu in the above gif)
88 |
89 | **example/chief/SelectIndicator/:** This folder contains all code including states and transitions used by the SelectIndicator. (small line that moves up and down in the above gif)
90 |
91 | ## Documentation
92 |
93 | The following describes on a high level how ReactF1 and Chief components are used.
94 |
95 | ### `const ReactF1 = require('react-f1')`
96 |
97 | ```jsx
98 |
121 | // the following defines components which will be animated
122 | // data-f1 associates these components with properties which
123 | // be defines in the states Object described above
124 |
125 |
126 |
127 | ```
128 |
129 | #### ReactF1 `states`
130 |
131 | ```javascript
132 | // this Object describes what this ui should look like in each state
133 | var states = {
134 |
135 | // this is the name of a state for instance this could be idle
136 | stateName1: {
137 |
138 | // this describes what a piece of the ui should look like in this state
139 | // it links to an item above described through data-f1
140 | targetName1: {
141 |
142 | // the following will describe what css styles should look like
143 | // for targetName1 in this state
144 | style: {
145 | // it should be noted that in order to have predictable behaviour with
146 | // react-f1 you should define each css property in each state that
147 | // maybe animated at some point.
148 | // eg. `left` css property in out is set to 0, idle `left` is 0 again,
149 | // and finally in the over state `left` is 100xw
150 | cssProperty1: value,
151 | cssProperty2: value
152 | }
153 | },
154 |
155 | // this describes another piece of ui associted via the
156 | // data-f1 attribute
157 | targetName2: {
158 | style: {
159 | cssProperty1: value,
160 | cssProperty2: value
161 | }
162 | }
163 | },
164 |
165 | // this is the name of a state for instance this could be mouseOver
166 | stateName2: {
167 | targetName1: {
168 | style: {
169 | cssProperty1: value,
170 | cssProperty2: value
171 | }
172 | },
173 |
174 | targetName2: {
175 | style: {
176 | cssProperty1: value,
177 | cssProperty2: value
178 | }
179 | }
180 | }
181 | }
182 | ```
183 |
184 | #### ReactF1 `transitions`
185 |
186 | ```javascript
187 | var transitions = [
188 | // to define that it's possible to animate from one
189 | // state to another.
190 | // If no animation is defined then a default animation
191 | // will be used
192 | { from: 'stateName1', to: 'stateName2' },
193 |
194 | // Transitions are not bi-directional
195 | // which means we will need to do this also
196 | // { from: 'stateName2', to: 'stateName1' }
197 | // however as a short form you can do
198 | // { from: 'stateName1', to: 'stateName2', bi: true }
199 | {
200 | from: 'stateName2', to: 'stateName1',
201 |
202 | // the following will define the animation to go from
203 | // stateName2 to stateName1
204 | animation: {
205 | // the following would define that all parts of the ui
206 | // will animate in 0.5 seconds and be delayed by 0.1 seconds
207 | duration: 0.5,
208 | delay: 0.1,
209 |
210 | // this will define that all animations will use this easeFunction
211 | // typically this would be one of the ease functions from:
212 | // https://www.npmjs.com/eases
213 | ease: easeFunction1,
214 |
215 | // the following would override the duraration and delay
216 | // which were defined globally just for targetName1
217 | targetName1: {
218 | duration: 0.3,
219 | delay: 0
220 | },
221 |
222 | // you can also override animations all the way down to a
223 | // a property.
224 | // In this case everything for targetName2 would use the globally
225 | // defined animation settings:
226 | // duration 0.5 seconds and be delayed 0.1 and use easeFunction1
227 | // however you can override down to a single animatable property
228 | // in thise case cssProperty1 will have duration of 0.25, delay 0.2,
229 | // and use easeFunction2 for easing
230 | targetName2: {
231 | style: {
232 | cssProperty1: {
233 | duration: 0.25,
234 | delay: 0.2,
235 | ease: easeFunction2
236 | }
237 | }
238 | }
239 | }
240 | }
241 | ]
242 | ```
243 | ### `const Chief = require('react-f1/Chief')`
244 |
245 | ```jsx
246 | // Chief from an API perspective looks very much the way that ReactF1 does
247 |
260 | {
261 | // with React it's very hard to manipulate deeply nested (grand child)
262 | // component's properties.
263 | // This is why you need to pass in a function that will accept an Object
264 | (states) => {
265 | // this function needs to return a single root component
266 | return
267 |
268 | // for simplicity this example does not defines states or transitions
269 | // for these ReactF1 ui components however states.target1 does
270 | // contain a variable called go and a function onComplete which
271 | // will be the state in which this ui Component should be
272 |
277 |
278 |
283 |
284 | // It should be Noted also that a Chief component can control other
285 | // Chief's
286 |
291 |
;
292 | }
293 | }
294 |
295 | ```
296 |
297 | #### Chief `states`
298 |
299 | ```javascript
300 | var states = {
301 | // this will define what state each of the ui components
302 | // should be in during the out state
303 | out: {
304 | target1: 'out',
305 | target2: 'out',
306 | target3: 'out'
307 | },
308 |
309 | // this will define what state each of the ui components
310 | // should be in during the idle state
311 | idle: {
312 | target1: 'idle',
313 | target2: 'idle',
314 | target3: 'idle'
315 | }
316 | };
317 | ```
318 |
319 | #### Chief `transitions`
320 | ```javascript
321 | var transitions = [
322 | // this defines a path for Chief to go from out to idle
323 | // if no animations are passed then all ui's states will
324 | // be set at the same time.
325 | //
326 | // It should be noted you can also do this:
327 | // { from: 'out', to: 'idle', bi: true }
328 | // Which will create a bi-directional transition.
329 | { from: 'out', to: 'idle' },
330 |
331 | // you may want to "stagger" ui going to a state
332 | // this is how you'd do it
333 | { from: 'out', to: 'idle', animation: {
334 | target2: {
335 | delay: 0.25
336 | },
337 |
338 | target3: {
339 | delay: 0.5
340 | }
341 | }
342 | }
343 | ];
344 | ```
345 |
346 | ## License
347 |
348 | MIT, see [LICENSE.md](http://github.com/Jam3/react-f1/blob/master/LICENSE.md) for details.
349 |
--------------------------------------------------------------------------------