mirror of https://github.com/vector-im/riot-web
				
				
				
			Move velocity stuff / contextual menu from Vector to React.
							parent
							
								
									1825b0317e
								
							
						
					
					
						commit
						5ba1ef5203
					
				|  | @ -29,7 +29,8 @@ | |||
|     "optimist": "^0.6.1", | ||||
|     "q": "^1.4.1", | ||||
|     "react": "^0.14.2", | ||||
|     "react-dom": "^0.14.2" | ||||
|     "react-dom": "^0.14.2", | ||||
|     "velocity-animate": "^1.2.3" | ||||
|   }, | ||||
|   "//deps": "The loader packages are here because webpack in a project that depends on us needs them in this package's node_modules folder", | ||||
|   "//depsbuglink": "https://github.com/webpack/webpack/issues/1472", | ||||
|  |  | |||
|  | @ -0,0 +1,82 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var ReactDOM = require('react-dom'); | ||||
| 
 | ||||
| // Shamelessly ripped off Modal.js.  There's probably a better way
 | ||||
| // of doing reusable widgets like dialog boxes & menus where we go and
 | ||||
| // pass in a custom control as the actual body.
 | ||||
| 
 | ||||
| module.exports = { | ||||
|     ContextualMenuContainerId: "mx_ContextualMenu_Container", | ||||
| 
 | ||||
|     getOrCreateContainer: function() { | ||||
|         var container = document.getElementById(this.ContextualMenuContainerId); | ||||
| 
 | ||||
|         if (!container) { | ||||
|             container = document.createElement("div"); | ||||
|             container.id = this.ContextualMenuContainerId; | ||||
|             document.body.appendChild(container); | ||||
|         } | ||||
| 
 | ||||
|         return container; | ||||
|     }, | ||||
| 
 | ||||
|     createMenu: function (Element, props) { | ||||
|         var self = this; | ||||
| 
 | ||||
|         var closeMenu = function() { | ||||
|             ReactDOM.unmountComponentAtNode(self.getOrCreateContainer()); | ||||
| 
 | ||||
|             if (props && props.onFinished) props.onFinished.apply(null, arguments); | ||||
|         }; | ||||
| 
 | ||||
|         var position = { | ||||
|             top: props.top - 20, | ||||
|         }; | ||||
| 
 | ||||
|         var chevron = null; | ||||
|         if (props.left) { | ||||
|             chevron = <img className="mx_ContextualMenu_chevron_left" src="img/chevron-left.png" width="9" height="16" /> | ||||
|             position.left = props.left + 8; | ||||
|         } else { | ||||
|             chevron = <img className="mx_ContextualMenu_chevron_right" src="img/chevron-right.png" width="9" height="16" /> | ||||
|             position.right = props.right + 8; | ||||
|         } | ||||
| 
 | ||||
|         var className = 'mx_ContextualMenu_wrapper'; | ||||
| 
 | ||||
|         // FIXME: If a menu uses getDefaultProps it clobbers the onFinished
 | ||||
|         // property set here so you can't close the menu from a button click!
 | ||||
|         var menu = ( | ||||
|             <div className={className}> | ||||
|                 <div className="mx_ContextualMenu" style={position}> | ||||
|                     {chevron} | ||||
|                     <Element {...props} onFinished={closeMenu}/> | ||||
|                 </div> | ||||
|                 <div className="mx_ContextualMenu_background" onClick={closeMenu}></div> | ||||
|             </div> | ||||
|         ); | ||||
| 
 | ||||
|         ReactDOM.render(menu, this.getOrCreateContainer()); | ||||
| 
 | ||||
|         return {close: closeMenu}; | ||||
|     }, | ||||
| }; | ||||
|  | @ -0,0 +1,113 @@ | |||
| var React = require('react'); | ||||
| var ReactDom = require('react-dom'); | ||||
| var Velocity = require('velocity-animate'); | ||||
| 
 | ||||
| /** | ||||
|  * The Velociraptor contains components and animates transitions with velocity. | ||||
|  * It will only pick up direct changes to properties ('left', currently), and so | ||||
|  * will not work for animating positional changes where the position is implicit | ||||
|  * from DOM order. This makes it a lot simpler and lighter: if you need fully | ||||
|  * automatic positional animation, look at react-shuffle or similar libraries. | ||||
|  */ | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'Velociraptor', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         children: React.PropTypes.array, | ||||
|         transition: React.PropTypes.object, | ||||
|         container: React.PropTypes.string | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this.children = {}; | ||||
|         this.nodes = {}; | ||||
|         var self = this; | ||||
|         React.Children.map(this.props.children, function(c) { | ||||
|             self.children[c.key] = c; | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillReceiveProps: function(nextProps) { | ||||
|         var self = this; | ||||
|         var oldChildren = this.children; | ||||
|         this.children = {}; | ||||
|         React.Children.map(nextProps.children, function(c) { | ||||
|             if (oldChildren[c.key]) { | ||||
|                 var old = oldChildren[c.key]; | ||||
|                 var oldNode = ReactDom.findDOMNode(self.nodes[old.key]); | ||||
| 
 | ||||
|                 if (oldNode.style.left != c.props.style.left) { | ||||
|                     Velocity(oldNode, { left: c.props.style.left }, self.props.transition).then(function() { | ||||
|                         // special case visibility because it's nonsensical to animate an invisible element
 | ||||
|                         // so we always hidden->visible pre-transition and visible->hidden after
 | ||||
|                         if (oldNode.style.visibility == 'visible' && c.props.style.visibility == 'hidden') { | ||||
|                             oldNode.style.visibility = c.props.style.visibility; | ||||
|                         } | ||||
|                     }); | ||||
|                     if (oldNode.style.visibility == 'hidden' && c.props.style.visibility == 'visible') { | ||||
|                         oldNode.style.visibility = c.props.style.visibility; | ||||
|                     } | ||||
|                     //console.log("translation: "+oldNode.style.left+" -> "+c.props.style.left);
 | ||||
|                 } | ||||
|                 self.children[c.key] = old; | ||||
|             } else { | ||||
|                 // new element. If it has a startStyle, use that as the style and go through
 | ||||
|                 // the enter animations
 | ||||
|                 var newProps = { | ||||
|                     ref: self.collectNode.bind(self, c.key) | ||||
|                 }; | ||||
|                 if (c.props.startStyle && Object.keys(c.props.startStyle).length) { | ||||
|                     var startStyle = c.props.startStyle; | ||||
|                     if (Array.isArray(startStyle)) { | ||||
|                         startStyle = startStyle[0]; | ||||
|                     } | ||||
|                     newProps._restingStyle = c.props.style; | ||||
|                     newProps.style = startStyle; | ||||
|                     //console.log("mounted@startstyle0: "+JSON.stringify(startStyle));
 | ||||
|                     // apply the enter animations once it's mounted
 | ||||
|                 } | ||||
|                 self.children[c.key] = React.cloneElement(c, newProps); | ||||
|             } | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     collectNode: function(k, node) { | ||||
|         if ( | ||||
|             this.nodes[k] === undefined && | ||||
|             node.props.startStyle && | ||||
|             Object.keys(node.props.startStyle).length | ||||
|         ) { | ||||
|             var domNode = ReactDom.findDOMNode(node); | ||||
|             var startStyles = node.props.startStyle; | ||||
|             var transitionOpts = node.props.enterTransitionOpts; | ||||
|             if (!Array.isArray(startStyles)) { | ||||
|                 startStyles = [ startStyles ]; | ||||
|                 transitionOpts = [ transitionOpts ]; | ||||
|             } | ||||
|             // start from startStyle 1: 0 is the one we gave it
 | ||||
|             // to start with, so now we animate 1 etc.
 | ||||
|             for (var i = 1; i < startStyles.length; ++i) { | ||||
|                 Velocity(domNode, startStyles[i], transitionOpts[i-1]); | ||||
|                 //console.log("start: "+JSON.stringify(startStyles[i]));
 | ||||
|             } | ||||
|             // and then we animate to the resting state
 | ||||
|             Velocity(domNode, node.props._restingStyle, transitionOpts[i-1]); | ||||
|             //console.log("enter: "+JSON.stringify(node.props._restingStyle));
 | ||||
|         } | ||||
|         this.nodes[k] = node; | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var self = this; | ||||
|         var childList = Object.keys(this.children).map(function(k) { | ||||
|             return React.cloneElement(self.children[k], { | ||||
|                 ref: self.collectNode.bind(self, self.children[k].key) | ||||
|             }); | ||||
|         }); | ||||
|         return ( | ||||
|             <span> | ||||
|                 {childList} | ||||
|             </span> | ||||
|         ); | ||||
|     }, | ||||
| }); | ||||
|  | @ -0,0 +1,15 @@ | |||
| var Velocity = require('velocity-animate'); | ||||
| 
 | ||||
| // courtesy of https://github.com/julianshapiro/velocity/issues/283
 | ||||
| // We only use easeOutBounce (easeInBounce is just sort of nonsensical)
 | ||||
| function bounce( p ) { | ||||
|     var pow2, | ||||
|         bounce = 4; | ||||
| 
 | ||||
|     while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} | ||||
|     return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); | ||||
| } | ||||
| 
 | ||||
| Velocity.Easings.easeOutBounce = function(p) { | ||||
|     return 1 - bounce(1 - p); | ||||
| } | ||||
|  | @ -24,10 +24,9 @@ var sdk = require('../../../index'); | |||
| var MatrixClientPeg = require('../../../MatrixClientPeg') | ||||
| var TextForEvent = require('../../../TextForEvent'); | ||||
| 
 | ||||
| // FIXME BROKEN IMPORTS
 | ||||
| var ContextualMenu = require('../../../../ContextualMenu'); | ||||
| var Velociraptor = require('../../../../Velociraptor'); | ||||
| require('../../../../VelocityBounce'); | ||||
| var ContextualMenu = require('../../../ContextualMenu'); | ||||
| var Velociraptor = require('../../../Velociraptor'); | ||||
| require('../../../VelocityBounce'); | ||||
| 
 | ||||
| var bounce = false; | ||||
| try { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Kegan Dougal
						Kegan Dougal