├── .babelrc ├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── example ├── chief │ ├── FancyButton │ │ ├── index.js │ │ ├── states.js │ │ └── transitions.js │ ├── Menu │ │ ├── index.js │ │ ├── states.js │ │ └── transitions.js │ ├── SelectIndicator │ │ ├── index.js │ │ ├── states.js │ │ └── transitions.js │ ├── index.js │ ├── states.js │ └── transitions.js ├── f1 │ ├── ExampleButton.js │ ├── getStates.js │ ├── getTransitions.js │ └── index.js ├── react-f1-chief.gif └── react-f1.gif ├── package.json ├── src ├── Chief.js └── index.js └── test ├── UI.js ├── index.js ├── jsDomBoiler.js ├── testChiefGo.js ├── testCustomParsers.js ├── testCustomTargets.js ├── testF1Go.js └── testMergeStates.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-inline-environment-variables"], 3 | "presets": [ 4 | "react", 5 | "es2015" 6 | ] 7 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gif filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | lib/ 7 | /index.js 8 | /Chief.js -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | before_install: 5 | - npm i react react-dom -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /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 |
85 |
96 |
107 |
108 | ; 109 | } 110 | } 111 | 112 | FancyButton.defaultProps = { 113 | width: 200, 114 | height: 50, 115 | onSelect: function() {} 116 | }; 117 | 118 | module.exports = FancyButton; -------------------------------------------------------------------------------- /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 | }; -------------------------------------------------------------------------------- /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 | ]; -------------------------------------------------------------------------------- /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; -------------------------------------------------------------------------------- /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/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/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/SelectIndicator/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 | var marginTop = props.marginTop; 9 | 10 | var selected1 = { 11 | indicator: { 12 | style: { 13 | position: 'absolute', 14 | left: 29, 15 | top: 0, 16 | width: width, 17 | height: height, 18 | opacity: 1, 19 | backgroundColor: [ 255, 0, 0 ] 20 | } 21 | } 22 | }; 23 | 24 | var selected2 = merge( 25 | {}, 26 | selected1, 27 | { 28 | indicator: { 29 | style: { 30 | top: height + marginTop 31 | } 32 | } 33 | } 34 | ); 35 | 36 | var selected3 = merge( 37 | {}, 38 | selected1, 39 | { 40 | indicator: { 41 | style: { 42 | top: (height + marginTop) * 2 43 | } 44 | } 45 | } 46 | ); 47 | 48 | var out = merge( 49 | {}, 50 | selected2, 51 | { 52 | indicator: { 53 | style: { 54 | left: 0, 55 | opacity: 0 56 | } 57 | } 58 | } 59 | ); 60 | 61 | var idle = merge( 62 | {}, 63 | out, 64 | { 65 | indicator: { 66 | style: { 67 | left: 29, 68 | opacity: 0.1 69 | } 70 | } 71 | } 72 | ); 73 | 74 | return { 75 | out: out, 76 | idle: idle, 77 | selected1: selected1, 78 | selected2: selected2, 79 | selected3: selected3 80 | }; 81 | }; -------------------------------------------------------------------------------- /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 | ]; -------------------------------------------------------------------------------- /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/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/f1/ExampleButton.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var ReactF1 = require('./../../src/'); 5 | var getStates = require('./getStates'); 6 | var getTransitions = require('./getTransitions'); 7 | 8 | class ExampleButton extends React.Component { 9 | render() { 10 | return 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/f1/getTransitions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // the eases module contains ease functions to modify animation curves 3 | var eases = require('eases'); 4 | 5 | module.exports = function getTransitions() { 6 | // transtions are arrays of how you can go between 7 | // states. 8 | // 9 | // in this case we have one transition between idle and over 10 | return [ 11 | { 12 | from: 'idle', 13 | to: 'over', 14 | // the following describes a very complex animation 15 | // there's no need to define such a crazy animation 16 | // but sometimes designers like to get craaaazy 17 | animation: { 18 | buttonBG: { 19 | style: { 20 | translate: { 21 | duration: 0.15, 22 | delay: 0.1, 23 | ease: eases.quadOut 24 | }, 25 | 26 | rotate: { 27 | duration: 0.15, 28 | delay: 0.1, 29 | ease: eases.quadOut 30 | }, 31 | 32 | width: { 33 | duration: 0.25, 34 | delay: 0.1, 35 | ease: eases.expoOut 36 | }, 37 | 38 | height: { 39 | duration: 0.25, 40 | delay: 0.4, 41 | ease: eases.expoOut 42 | } 43 | } 44 | } 45 | } 46 | }, 47 | { 48 | from: 'over', 49 | to: 'idle', 50 | animation: { 51 | duration: 0.5, 52 | ease: eases.elasticOut 53 | } 54 | } 55 | ]; 56 | }; -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /example/react-f1-chief.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:63faa92262e7b900e15a8d18f1503bcd9691b9ad12990eb900cff44dbc7af7c8 3 | size 70728 4 | -------------------------------------------------------------------------------- /example/react-f1.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9ef33724e30ea60a4ceea9a429b28aef5e7568c9f913d2e94a40d6b4c29a1eab 3 | size 29739 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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; -------------------------------------------------------------------------------- /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')); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } --------------------------------------------------------------------------------