├── .gitignore ├── modules ├── index.js └── components │ └── MagicMove.js ├── scripts ├── release ├── test ├── preview-release ├── dev-examples └── build ├── .npmignore ├── .travis.yml ├── examples ├── basic │ ├── index.html │ ├── app.css │ └── app.js └── buddy-list │ ├── index.html │ ├── app.css │ └── app.js ├── CHANGELOG.md ├── bower.json ├── README.md ├── LICENSE ├── webpack.config.js ├── package.json └── dist ├── react-magic-move.min.js └── react-magic-move.js /.gitignore: -------------------------------------------------------------------------------- 1 | examples/**/*-bundle.js 2 | -------------------------------------------------------------------------------- /modules/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./components/MagicMove'); 2 | 3 | -------------------------------------------------------------------------------- /scripts/release: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | scripts/build 3 | node_modules/.bin/release 4 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | NODE_ENV=test node_modules/.bin/karma start "$@" 3 | -------------------------------------------------------------------------------- /scripts/preview-release: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | node_modules/rf-release/node_modules/.bin/changelog -t preview -s 3 | -------------------------------------------------------------------------------- /scripts/dev-examples: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | node_modules/.bin/webpack-dev-server --inline --content-base examples/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | CONTRIBUTING.md 2 | bower.json 3 | examples 4 | karma.conf.js 5 | scripts 6 | specs 7 | webpack.config.js 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - export DISPLAY=:99.0 6 | - sh -e /etc/init.d/xvfb start 7 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | Basic Example 3 | 4 | 5 |
6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/buddy-list/index.html: -------------------------------------------------------------------------------- 1 | 2 | Basic Example 3 | 4 | 5 |
6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/basic/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", Arial; 3 | font-weight: 200; 4 | background: #ccc; 5 | } 6 | 7 | .State { 8 | height: 50px; 9 | width: 50px; 10 | transition: all 500ms ease; 11 | border: 1px solid; 12 | margin: 10px; 13 | display: inline-block; 14 | box-sizing: border-box; 15 | padding: 5px; 16 | background: #fff; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v0.1.0 - Mon, 12 Jan 2015 04:33:34 GMT 2 | -------------------------------------- 3 | 4 | - 5 | 6 | 7 | v0.0.2 - Tue, 25 Nov 2014 16:50:10 GMT 8 | -------------------------------------- 9 | 10 | - [15d2910](../../commit/15d2910) [fixed] dist folder 11 | 12 | 13 | v0.0.1 - Tue, 25 Nov 2014 16:49:27 GMT 14 | -------------------------------------- 15 | 16 | - 17 | 18 | 19 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p dist 3 | 4 | NODE_ENV=production node_modules/.bin/browserify modules/index.js \ 5 | -t [reactify --es6] \ 6 | -t browserify-shim \ 7 | -t envify \ 8 | --detect-globals false \ 9 | -s ReactMagicMove \ 10 | > dist/react-magic-move.js 11 | 12 | node_modules/.bin/uglifyjs dist/react-magic-move.js \ 13 | --compress \ 14 | warnings=false \ 15 | > dist/react-magic-move.min.js 16 | 17 | echo "gzipped, the global build is:" 18 | echo "`gzip -c dist/react-magic-move.min.js | wc -c` bytes" 19 | -------------------------------------------------------------------------------- /examples/buddy-list/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", Arial; 3 | font-weight: 200; 4 | } 5 | 6 | .Buddy, h2 { 7 | margin: 0; 8 | transition: all 1250ms ease; 9 | box-sizing: border-box; 10 | } 11 | 12 | .status { 13 | display: inline-block; 14 | box-sizing: border-box; 15 | width: 8px; 16 | height: 8px; 17 | transition: all 1250ms ease; 18 | margin-right: 4px; 19 | margin-bottom: 2px; 20 | border-radius: 50%; 21 | background-color: hsl(100, 50%, 50%); 22 | } 23 | 24 | .offline { 25 | background-color: hsl(0, 50%, 50%); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-magic-move", 3 | "version": "0.1.0", 4 | "homepage": "https://github.com/rpflorence/react-magic-move", 5 | "authors": [ 6 | "Ryan Florence" 7 | ], 8 | "description": "MagicMove for React.js", 9 | "main": "dist/react-magic-move.js", 10 | "keywords": [ 11 | "react", 12 | "animation" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "specs", 20 | "modules", 21 | "examples", 22 | "script", 23 | "CONTRIBUTING.md", 24 | "karma.conf.js", 25 | "package.json", 26 | "webpack.config.js" 27 | ] 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Magic Move 2 | ================ 3 | 4 | Magic Move for React.JS 5 | 6 | NOT A PRODUCTION MODULE 7 | ----------------------- 8 | 9 | This was just a fun experiment, with some love, it could definitely be a real 10 | thing, but I don't have time. I will merge pull requests to keep it working, 11 | but I don't maintain this right now. 12 | 13 | Usage 14 | ----- 15 | 16 | 1. Wrap some ordered elements in `` 17 | 2. Add a key to each element 18 | 3. Add some CSS transitions and border-box sizing (so the code can 19 | measure it more easily) 20 | 21 | ```css 22 | .Something { 23 | transition: all 500ms ease; 24 | box-sizing: border-box; 25 | } 26 | ``` 27 | 28 | ```js 29 | 30 | {this.sortedElementsWithKeys(this.state.sortBy)} 31 | 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Ryan Florence 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | 5 | var EXAMPLES_DIR = path.resolve(__dirname, 'examples'); 6 | 7 | function isDirectory(dir) { 8 | return fs.lstatSync(dir).isDirectory(); 9 | } 10 | 11 | function buildEntries() { 12 | return fs.readdirSync(EXAMPLES_DIR).reduce(function (entries, dir) { 13 | if (dir === 'build') 14 | return entries; 15 | 16 | var isDraft = dir.charAt(0) === '_'; 17 | 18 | if (!isDraft && isDirectory(path.join(EXAMPLES_DIR, dir))) 19 | entries[dir] = path.join(EXAMPLES_DIR, dir, 'app.js'); 20 | 21 | return entries; 22 | }, {}); 23 | } 24 | 25 | module.exports = { 26 | 27 | entry: buildEntries(), 28 | 29 | output: { 30 | filename: '[name].js', 31 | chunkFilename: '[id].chunk.js', 32 | path: 'examples/__build__', 33 | publicPath: '/__build__/' 34 | }, 35 | 36 | module: { 37 | loaders: [ 38 | { test: /\.js$/, loader: 'jsx-loader?harmony' } 39 | ] 40 | }, 41 | 42 | resolve: { 43 | alias: { 44 | 'react-magic-move': '../../modules/index' 45 | } 46 | }, 47 | 48 | plugins: [ 49 | new webpack.optimize.CommonsChunkPlugin('shared.js') 50 | ] 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-magic-move", 3 | "version": "0.1.0", 4 | "description": "MagicMove for React.js", 5 | "main": "./modules/index", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/rpflorence/react-magic-move.git" 9 | }, 10 | "homepage": "https://github.com/rpflorence/react-magic-move/blob/latest/README.md", 11 | "bugs": "https://github.com/rpflorence/react-magic-move/issues", 12 | "directories": { 13 | "example": "examples" 14 | }, 15 | "scripts": { 16 | "test": "echo 'no tests yet' && exit 0" 17 | }, 18 | "authors": [ 19 | "Ryan Florence" 20 | ], 21 | "license": "MIT", 22 | "devDependencies": { 23 | "browserify": "4.2.3", 24 | "browserify-shim": "^3.8.0", 25 | "bundle-loader": "0.5.1", 26 | "envify": "1.2.0", 27 | "jsx-loader": "0.12.0", 28 | "react": "0.12.x", 29 | "reactify": "^0.17.1", 30 | "rf-release": "0.4.0", 31 | "uglify-js": "^2.4.15", 32 | "uglifyjs": "^2.3.6", 33 | "webpack": "1.4.5", 34 | "webpack-dev-server": "1.6.5" 35 | }, 36 | "peerDependencies": { 37 | "react": "0.12.x" 38 | }, 39 | "tags": [ 40 | "react", 41 | "animation" 42 | ], 43 | "keywords": [ 44 | "react", 45 | "react-component", 46 | "animation" 47 | ], 48 | "browserify-shim": { 49 | "react": "global:React" 50 | } 51 | } -------------------------------------------------------------------------------- /examples/buddy-list/app.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | var React = require('react'); 3 | var MagicMove = require('react-magic-move'); 4 | 5 | var buddies = [ 6 | { name: 'Ryan' }, 7 | { name: 'Michael' }, 8 | { name: 'Vjeux' }, 9 | { name: 'Pete' }, 10 | { name: 'David' } 11 | ]; 12 | 13 | var getBuddies = () => { 14 | return buddies.map((buddy) => { 15 | buddy.online = Math.random() > 0.5; 16 | return buddy; 17 | }); 18 | }; 19 | 20 | var App = React.createClass({ 21 | getInitialState: function() { 22 | return { buddies: getBuddies() }; 23 | }, 24 | 25 | componentDidMount: function () { 26 | setInterval(() => { 27 | this.setState({ buddies: getBuddies() }); 28 | }, 5000); 29 | }, 30 | 31 | addBuddy (event) { 32 | event.preventDefault(); 33 | var name = event.target.elements[0].value; 34 | buddies.push({ name, online: true }); 35 | event.target.reset(); 36 | this.setState({ buddies }); 37 | }, 38 | 39 | renderBuddy (buddy) { 40 | var status = buddy.online ? 'online' : 'offline'; 41 | return ( 42 |
43 | 44 | {buddy.name} 45 |
46 | ); 47 | }, 48 | 49 | renderOnline () { 50 | return sortBy('name', this.state.buddies.filter((buddy) => { 51 | return buddy.online; 52 | })).map(this.renderBuddy); 53 | }, 54 | 55 | renderOffline () { 56 | return sortBy('name', this.state.buddies.filter((buddy) => { 57 | return !buddy.online; 58 | })).map(this.renderBuddy); 59 | }, 60 | 61 | render: function () { 62 | return ( 63 |
64 |

Magic Move

65 |
66 | {' '} 67 | 68 |
69 | 70 |

Online

71 | {this.renderOnline()} 72 |

Offline

73 | {this.renderOffline()} 74 |
75 |
76 | ); 77 | } 78 | }); 79 | 80 | function sortBy(key, arr) { 81 | return arr.slice(0).sort((a, b) => { 82 | return (a[key] < b[key]) ? -1 : (a[key] > b[key]) ? 1 : 0; 83 | }); 84 | } 85 | 86 | React.render(, document.getElementById('example')); 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples/basic/app.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | var React = require('react'); 3 | var MagicMove = require('react-magic-move'); 4 | 5 | var App = React.createClass({ 6 | getInitialState: function() { 7 | return { order: 'alphabetical' }; 8 | }, 9 | 10 | sort: function(order) { 11 | if (order == 'alphabetical' && order == this.state.order) 12 | return; 13 | this.setState({order: order}); 14 | }, 15 | 16 | renderStates: function() { 17 | var states = this.state.order === 'random' ? shuffled() : alphabetical(); 18 | return states.map(function(state) { 19 | return
{state.abbr}
; 20 | }); 21 | }, 22 | 23 | render: function() { 24 | return ( 25 |
26 |

Magic Move

27 |
28 | 29 | 30 |
31 | 32 | 33 | {this.renderStates()} 34 | 35 |
36 | ); 37 | } 38 | }); 39 | 40 | React.render(, document.getElementById('example')); 41 | 42 | function shuffled() { 43 | return shuffle(getStates()); 44 | } 45 | 46 | function alphabetical() { 47 | return sortBy(getStates(), 'name'); 48 | } 49 | 50 | function getStates() { 51 | return [ 52 | { abbr: "AL", name: "Alabama"}, 53 | { abbr: "AK", name: "Alaska"}, 54 | { abbr: "AZ", name: "Arizona"}, 55 | { abbr: "AR", name: "Arkansas"}, 56 | { abbr: "CA", name: "California"}, 57 | { abbr: "CO", name: "Colorado"}, 58 | { abbr: "CT", name: "Connecticut"}, 59 | { abbr: "DE", name: "Delaware"}, 60 | { abbr: "FL", name: "Florida"}, 61 | { abbr: "GA", name: "Georgia"}, 62 | { abbr: "HI", name: "Hawaii"}, 63 | { abbr: "ID", name: "Idaho"}, 64 | { abbr: "IL", name: "Illinois"}, 65 | { abbr: "IN", name: "Indiana"}, 66 | { abbr: "IA", name: "Iowa"}, 67 | { abbr: "KS", name: "Kansas"}, 68 | { abbr: "KY", name: "Kentucky"}, 69 | { abbr: "LA", name: "Louisiana"}, 70 | { abbr: "ME", name: "Maine"}, 71 | { abbr: "MD", name: "Maryland"}, 72 | { abbr: "MA", name: "Massachusetts"}, 73 | { abbr: "MI", name: "Michigan"}, 74 | { abbr: "MN", name: "Minnesota"}, 75 | { abbr: "MS", name: "Mississippi"}, 76 | { abbr: "MO", name: "Missouri"}, 77 | { abbr: "MT", name: "Montana"}, 78 | { abbr: "NE", name: "Nebraska"}, 79 | { abbr: "NV", name: "Nevada"}, 80 | { abbr: "NH", name: "New Hampshire"}, 81 | { abbr: "NJ", name: "New Jersey"}, 82 | { abbr: "NM", name: "New Mexico"}, 83 | { abbr: "NY", name: "New York"}, 84 | { abbr: "NC", name: "North Carolina"}, 85 | { abbr: "ND", name: "North Dakota"}, 86 | { abbr: "OH", name: "Ohio"}, 87 | { abbr: "OK", name: "Oklahoma"}, 88 | { abbr: "OR", name: "Oregon"}, 89 | { abbr: "PA", name: "Pennsylvania"}, 90 | { abbr: "RI", name: "Rhode Island"}, 91 | { abbr: "SC", name: "South Carolina"}, 92 | { abbr: "SD", name: "South Dakota"}, 93 | { abbr: "TN", name: "Tennessee"}, 94 | { abbr: "TX", name: "Texas"}, 95 | { abbr: "UT", name: "Utah"}, 96 | { abbr: "VT", name: "Vermont"}, 97 | { abbr: "VA", name: "Virginia"}, 98 | { abbr: "WA", name: "Washington"}, 99 | { abbr: "WV", name: "West Virginia"}, 100 | { abbr: "WI", name: "Wisconsin"}, 101 | { abbr: "WY", name: "Wyoming"} 102 | ]; 103 | } 104 | 105 | function underscore(str) { 106 | return str.toLowerCase().replace(/ /, '_'); 107 | } 108 | 109 | function stateImage(state) { 110 | return "http://www.50states.com/maps/"+underscore(state)+".gif"; 111 | } 112 | 113 | function sortBy(arr, prop) { 114 | return arr.slice(0).sort(function(a, b) { 115 | if (a[prop] < b[prop]) return -1; 116 | if (a[prop] > b[prop]) return 1; 117 | return 0; 118 | }); 119 | } 120 | 121 | function shuffle(array) { 122 | var currentIndex = array.length, temporaryValue, randomIndex; 123 | while (0 !== currentIndex) { 124 | randomIndex = Math.floor(Math.random() * currentIndex); 125 | currentIndex -= 1; 126 | temporaryValue = array[currentIndex]; 127 | array[currentIndex] = array[randomIndex]; 128 | array[randomIndex] = temporaryValue; 129 | } 130 | return array; 131 | } 132 | -------------------------------------------------------------------------------- /modules/components/MagicMove.js: -------------------------------------------------------------------------------- 1 | /* @jsx React.DOM */ 2 | var React = require('react'); 3 | var cloneWithProps = require('react/lib/cloneWithProps'); 4 | 5 | var Clones = React.createClass({ 6 | displayName: 'MagicMoveClones', 7 | 8 | childrenWithPositions () { 9 | var children = []; 10 | React.Children.forEach(this.props.children, (child) => { 11 | var style = this.props.positions[child.key]; 12 | var key = child.key; 13 | children.push(cloneWithProps(child, { style, key })); 14 | }); 15 | return children.sort(function (a, b) { 16 | return (a.key < b.key) ? -1 : (a.key > b.key) ? 1 : 0; 17 | }); 18 | }, 19 | 20 | render () { 21 | return ( 22 |
23 | {this.childrenWithPositions()} 24 |
25 | ); 26 | } 27 | }); 28 | 29 | var MagicMove = React.createClass({ 30 | 31 | displayName: 'MagicMove', 32 | 33 | getInitialState () { 34 | return { 35 | animating: false 36 | }; 37 | }, 38 | 39 | componentDidMount () { 40 | this.makePortal(); 41 | this.renderClonesInitially(); 42 | }, 43 | 44 | componentWillUnmount () { 45 | document.body.removeChild(this.portalNode); 46 | }, 47 | 48 | componentWillReceiveProps (nextProps) { 49 | this.startAnimation(nextProps); 50 | }, 51 | 52 | componentDidUpdate (prevProps) { 53 | if (this.state.animating) 54 | this.renderClonesToNewPositions(prevProps); 55 | }, 56 | 57 | makePortal () { 58 | this.portalNode = document.createElement('div'); 59 | this.portalNode.style.left = '-9999px'; 60 | document.body.appendChild(this.portalNode); 61 | }, 62 | 63 | addTransitionEndEvent () { 64 | // if you click RIGHT before the transition is done, the animation jumps, 65 | // its because the transitionend event fires even though its not quite 66 | // done, not sure how to hack around it yet. 67 | this._transitionHandler = callOnNthCall(this.props.children.length, this.finishAnimation); 68 | this.portalNode.addEventListener('transitionend', this._transitionHandler); 69 | }, 70 | 71 | removeTransitionEndEvent () { 72 | this.portalNode.removeEventListener('transitionend', this._transitionHandler); 73 | }, 74 | 75 | startAnimation (nextProps) { 76 | if (this.state.animating) 77 | return; 78 | this.addTransitionEndEvent(); 79 | nextProps.animating = true; 80 | nextProps.positions = this.getPositions(); 81 | this.renderClones(nextProps, () => { 82 | this.setState({ animating: true }); 83 | }); 84 | }, 85 | 86 | renderClonesToNewPositions (prevProps) { 87 | prevProps.positions = this.getPositions(); 88 | this.renderClones(prevProps); 89 | }, 90 | 91 | finishAnimation () { 92 | this.removeTransitionEndEvent(); 93 | this.portalNode.style.position = 'absolute'; 94 | this.setState({ animating: false }); 95 | }, 96 | 97 | getPositions () { 98 | var positions = {}; 99 | React.Children.forEach(this.props.children, (child) => { 100 | var ref = child.key; 101 | var node = this.refs[ref].getDOMNode(); 102 | var rect = node.getBoundingClientRect(); 103 | var computedStyle = getComputedStyle(node); 104 | var marginTop = parseInt(computedStyle.marginTop, 10); 105 | var marginLeft = parseInt(computedStyle.marginLeft, 10); 106 | var position = { 107 | top: (rect.top - marginTop), 108 | left: (rect.left - marginLeft), 109 | width: rect.width, 110 | height: rect.height, 111 | position: 'absolute' 112 | }; 113 | positions[ref] = position; 114 | }); 115 | return positions; 116 | }, 117 | 118 | renderClonesInitially () { 119 | this.props.positions = this.getPositions(); 120 | React.render(, this.portalNode); 121 | }, 122 | 123 | renderClones (props, cb) { 124 | this.portalNode.style.position = ''; 125 | React.render(, this.portalNode, cb); 126 | }, 127 | 128 | childrenWithRefs () { 129 | return React.Children.map(this.props.children, (child) => { 130 | return cloneWithProps(child, { ref: child.key}); 131 | }); 132 | }, 133 | 134 | render () { 135 | var style = { opacity: this.state.animating ? 0 : 1 }; 136 | return ( 137 |
138 | {this.childrenWithRefs()} 139 |
140 | ); 141 | } 142 | }); 143 | 144 | function callOnNthCall(n, fn) { 145 | var calls = 0; 146 | return function () { 147 | calls++; 148 | if (calls === n) { 149 | calls = 0; 150 | return fn.apply(this, arguments); 151 | } 152 | }; 153 | } 154 | 155 | module.exports = MagicMove; 156 | -------------------------------------------------------------------------------- /dist/react-magic-move.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.ReactMagicMove=e()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;ob.key?1:0})},render:function(){return React.createElement("div",{className:"MagicMoveClones"},this.childrenWithPositions())}}),MagicMove=React.createClass({displayName:"MagicMove",getInitialState:function(){return{animating:!1}},componentDidMount:function(){this.makePortal(),this.renderClonesInitially()},componentWillUnmount:function(){document.body.removeChild(this.portalNode)},componentWillReceiveProps:function(nextProps){this.startAnimation(nextProps)},componentDidUpdate:function(prevProps){this.state.animating&&this.renderClonesToNewPositions(prevProps)},makePortal:function(){this.portalNode=document.createElement("div"),this.portalNode.style.left="-9999px",document.body.appendChild(this.portalNode)},addTransitionEndEvent:function(){this._transitionHandler=callOnNthCall(this.props.children.length,this.finishAnimation),this.portalNode.addEventListener("transitionend",this._transitionHandler)},removeTransitionEndEvent:function(){this.portalNode.removeEventListener("transitionend",this._transitionHandler)},startAnimation:function(nextProps){this.state.animating||(this.addTransitionEndEvent(),nextProps.animating=!0,nextProps.positions=this.getPositions(),this.renderClones(nextProps,function(){this.setState({animating:!0})}.bind(this)))},renderClonesToNewPositions:function(prevProps){prevProps.positions=this.getPositions(),this.renderClones(prevProps)},finishAnimation:function(){this.removeTransitionEndEvent(),this.portalNode.style.position="absolute",this.setState({animating:!1})},getPositions:function(){var positions={};return React.Children.forEach(this.props.children,function(child){var ref=child.key,node=this.refs[ref].getDOMNode(),rect=node.getBoundingClientRect(),computedStyle=getComputedStyle(node),marginTop=parseInt(computedStyle.marginTop,10),marginLeft=parseInt(computedStyle.marginLeft,10),position={top:rect.top-marginTop,left:rect.left-marginLeft,width:rect.width,height:rect.height,position:"absolute"};positions[ref]=position}.bind(this)),positions},renderClonesInitially:function(){this.props.positions=this.getPositions(),React.render(React.createElement(Clones,React.__spread({},this.props)),this.portalNode)},renderClones:function(props,cb){this.portalNode.style.position="",React.render(React.createElement(Clones,React.__spread({},props)),this.portalNode,cb)},childrenWithRefs:function(){return React.Children.map(this.props.children,function(child){return cloneWithProps(child,{ref:child.key})})},render:function(){var style={opacity:this.state.animating?0:1};return React.createElement("div",{style:style},this.childrenWithRefs())}});module.exports=MagicMove},{"react/lib/cloneWithProps":8}],2:[function(_dereq_,module){module.exports=_dereq_("./components/MagicMove")},{"./components/MagicMove":1}],3:[function(_dereq_,module){function assign(target){if(null==target)throw new TypeError("Object.assign target cannot be null or undefined");for(var to=Object(target),hasOwnProperty=Object.prototype.hasOwnProperty,nextIndex=1;nextIndex1){for(var childArray=Array(childrenLength),i=0;childrenLength>i;i++)childArray[i]=arguments[i+2];props.children=childArray}if(type.defaultProps){var defaultProps=type.defaultProps;for(propName in defaultProps)"undefined"==typeof props[propName]&&(props[propName]=defaultProps[propName])}return new ReactElement(type,key,ref,ReactCurrentOwner.current,ReactContext.current,props)},ReactElement.createFactory=function(type){var factory=ReactElement.createElement.bind(null,type);return factory.type=type,factory},ReactElement.cloneAndReplaceProps=function(oldElement,newProps){var newElement=new ReactElement(oldElement.type,oldElement.key,oldElement.ref,oldElement._owner,oldElement._context,newProps);return newElement},ReactElement.isValidElement=function(object){var isElement=!(!object||!object._isReactElement);return isElement},module.exports=ReactElement},{"./ReactContext":4,"./ReactCurrentOwner":5,"./warning":13}],7:[function(_dereq_,module){"use strict";function createTransferStrategy(mergeStrategy){return function(props,key,value){props[key]=props.hasOwnProperty(key)?mergeStrategy(props[key],value):value}}function transferInto(props,newProps){for(var thisKey in newProps)if(newProps.hasOwnProperty(thisKey)){var transferStrategy=TransferStrategies[thisKey];transferStrategy&&TransferStrategies.hasOwnProperty(thisKey)?transferStrategy(props,thisKey,newProps[thisKey]):props.hasOwnProperty(thisKey)||(props[thisKey]=newProps[thisKey])}return props}var assign=_dereq_("./Object.assign"),emptyFunction=_dereq_("./emptyFunction"),invariant=_dereq_("./invariant"),joinClasses=_dereq_("./joinClasses"),transferStrategyMerge=(_dereq_("./warning"),createTransferStrategy(function(a,b){return assign({},b,a)})),TransferStrategies={children:emptyFunction,className:createTransferStrategy(joinClasses),style:transferStrategyMerge},ReactPropTransferer={TransferStrategies:TransferStrategies,mergeProps:function(oldProps,newProps){return transferInto(assign({},oldProps),newProps)},Mixin:{transferPropsTo:function(element){return invariant(element._owner===this),transferInto(element.props,this.props),element}}};module.exports=ReactPropTransferer},{"./Object.assign":3,"./emptyFunction":9,"./invariant":10,"./joinClasses":11,"./warning":13}],8:[function(_dereq_,module){"use strict";function cloneWithProps(child,props){var newProps=ReactPropTransferer.mergeProps(props,child.props);return!newProps.hasOwnProperty(CHILDREN_PROP)&&child.props.hasOwnProperty(CHILDREN_PROP)&&(newProps.children=child.props.children),ReactElement.createElement(child.type,newProps)}var ReactElement=_dereq_("./ReactElement"),ReactPropTransferer=_dereq_("./ReactPropTransferer"),keyOf=_dereq_("./keyOf"),CHILDREN_PROP=(_dereq_("./warning"),keyOf({children:null}));module.exports=cloneWithProps},{"./ReactElement":6,"./ReactPropTransferer":7,"./keyOf":12,"./warning":13}],9:[function(_dereq_,module){function makeEmptyFunction(arg){return function(){return arg}}function emptyFunction(){}emptyFunction.thatReturns=makeEmptyFunction,emptyFunction.thatReturnsFalse=makeEmptyFunction(!1),emptyFunction.thatReturnsTrue=makeEmptyFunction(!0),emptyFunction.thatReturnsNull=makeEmptyFunction(null),emptyFunction.thatReturnsThis=function(){return this},emptyFunction.thatReturnsArgument=function(arg){return arg},module.exports=emptyFunction},{}],10:[function(_dereq_,module){"use strict";var invariant=function(condition,format,a,b,c,d,e,f){if(!condition){var error;if(void 0===format)error=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var args=[a,b,c,d,e,f],argIndex=0;error=new Error("Invariant Violation: "+format.replace(/%s/g,function(){return args[argIndex++]}))}throw error.framesToPop=1,error}};module.exports=invariant},{}],11:[function(_dereq_,module){"use strict";function joinClasses(className){className||(className="");var nextClass,argLength=arguments.length;if(argLength>1)for(var ii=1;argLength>ii;ii++)nextClass=arguments[ii],nextClass&&(className=(className?className+" ":"")+nextClass);return className}module.exports=joinClasses},{}],12:[function(_dereq_,module){var keyOf=function(oneKeyObj){var key;for(key in oneKeyObj)if(oneKeyObj.hasOwnProperty(key))return key;return null};module.exports=keyOf},{}],13:[function(_dereq_,module){"use strict";var emptyFunction=_dereq_("./emptyFunction"),warning=emptyFunction;module.exports=warning},{"./emptyFunction":9}]},{},[2])(2)}); -------------------------------------------------------------------------------- /dist/react-magic-move.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.ReactMagicMove=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o b.key) ? 1 : 0; 18 | }); 19 | }, 20 | 21 | render:function () { 22 | return ( 23 | React.createElement("div", {className: "MagicMoveClones"}, 24 | this.childrenWithPositions() 25 | ) 26 | ); 27 | } 28 | }); 29 | 30 | var MagicMove = React.createClass({ 31 | 32 | displayName: 'MagicMove', 33 | 34 | getInitialState:function () { 35 | return { 36 | animating: false 37 | }; 38 | }, 39 | 40 | componentDidMount:function () { 41 | this.makePortal(); 42 | this.renderClonesInitially(); 43 | }, 44 | 45 | componentWillUnmount:function () { 46 | document.body.removeChild(this.portalNode); 47 | }, 48 | 49 | componentWillReceiveProps:function (nextProps) { 50 | this.startAnimation(nextProps); 51 | }, 52 | 53 | componentDidUpdate:function (prevProps) { 54 | if (this.state.animating) 55 | this.renderClonesToNewPositions(prevProps); 56 | }, 57 | 58 | makePortal:function () { 59 | this.portalNode = document.createElement('div'); 60 | this.portalNode.style.left = '-9999px'; 61 | document.body.appendChild(this.portalNode); 62 | }, 63 | 64 | addTransitionEndEvent:function () { 65 | // if you click RIGHT before the transition is done, the animation jumps, 66 | // its because the transitionend event fires even though its not quite 67 | // done, not sure how to hack around it yet. 68 | this._transitionHandler = callOnNthCall(this.props.children.length, this.finishAnimation); 69 | this.portalNode.addEventListener('transitionend', this._transitionHandler); 70 | }, 71 | 72 | removeTransitionEndEvent:function () { 73 | this.portalNode.removeEventListener('transitionend', this._transitionHandler); 74 | }, 75 | 76 | startAnimation:function (nextProps) { 77 | if (this.state.animating) 78 | return; 79 | this.addTransitionEndEvent(); 80 | nextProps.animating = true; 81 | nextProps.positions = this.getPositions(); 82 | this.renderClones(nextProps, function() { 83 | this.setState({ animating: true }); 84 | }.bind(this)); 85 | }, 86 | 87 | renderClonesToNewPositions:function (prevProps) { 88 | prevProps.positions = this.getPositions(); 89 | this.renderClones(prevProps); 90 | }, 91 | 92 | finishAnimation:function () { 93 | this.removeTransitionEndEvent(); 94 | this.portalNode.style.position = 'absolute'; 95 | this.setState({ animating: false }); 96 | }, 97 | 98 | getPositions:function () { 99 | var positions = {}; 100 | React.Children.forEach(this.props.children, function(child) { 101 | var ref = child.key; 102 | var node = this.refs[ref].getDOMNode(); 103 | var rect = node.getBoundingClientRect(); 104 | var computedStyle = getComputedStyle(node); 105 | var marginTop = parseInt(computedStyle.marginTop, 10); 106 | var marginLeft = parseInt(computedStyle.marginLeft, 10); 107 | var position = { 108 | top: (rect.top - marginTop), 109 | left: (rect.left - marginLeft), 110 | width: rect.width, 111 | height: rect.height, 112 | position: 'absolute' 113 | }; 114 | positions[ref] = position; 115 | }.bind(this)); 116 | return positions; 117 | }, 118 | 119 | renderClonesInitially:function () { 120 | this.props.positions = this.getPositions(); 121 | React.render(React.createElement(Clones, React.__spread({}, this.props)), this.portalNode); 122 | }, 123 | 124 | renderClones:function (props, cb) { 125 | this.portalNode.style.position = ''; 126 | React.render(React.createElement(Clones, React.__spread({}, props)), this.portalNode, cb); 127 | }, 128 | 129 | childrenWithRefs:function () { 130 | return React.Children.map(this.props.children, function(child) { 131 | return cloneWithProps(child, { ref: child.key}); 132 | }); 133 | }, 134 | 135 | render:function () { 136 | var style = { opacity: this.state.animating ? 0 : 1 }; 137 | return ( 138 | React.createElement("div", {style: style}, 139 | this.childrenWithRefs() 140 | ) 141 | ); 142 | } 143 | }); 144 | 145 | function callOnNthCall(n, fn) { 146 | var calls = 0; 147 | return function () { 148 | calls++; 149 | if (calls === n) { 150 | calls = 0; 151 | return fn.apply(this, arguments); 152 | } 153 | }; 154 | } 155 | 156 | module.exports = MagicMove; 157 | 158 | },{"react/lib/cloneWithProps":8}],2:[function(_dereq_,module,exports){ 159 | module.exports = _dereq_('./components/MagicMove'); 160 | 161 | 162 | },{"./components/MagicMove":1}],3:[function(_dereq_,module,exports){ 163 | /** 164 | * Copyright 2014, Facebook, Inc. 165 | * All rights reserved. 166 | * 167 | * This source code is licensed under the BSD-style license found in the 168 | * LICENSE file in the root directory of this source tree. An additional grant 169 | * of patent rights can be found in the PATENTS file in the same directory. 170 | * 171 | * @providesModule Object.assign 172 | */ 173 | 174 | // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign 175 | 176 | function assign(target, sources) { 177 | if (target == null) { 178 | throw new TypeError('Object.assign target cannot be null or undefined'); 179 | } 180 | 181 | var to = Object(target); 182 | var hasOwnProperty = Object.prototype.hasOwnProperty; 183 | 184 | for (var nextIndex = 1; nextIndex < arguments.length; nextIndex++) { 185 | var nextSource = arguments[nextIndex]; 186 | if (nextSource == null) { 187 | continue; 188 | } 189 | 190 | var from = Object(nextSource); 191 | 192 | // We don't currently support accessors nor proxies. Therefore this 193 | // copy cannot throw. If we ever supported this then we must handle 194 | // exceptions and side-effects. We don't support symbols so they won't 195 | // be transferred. 196 | 197 | for (var key in from) { 198 | if (hasOwnProperty.call(from, key)) { 199 | to[key] = from[key]; 200 | } 201 | } 202 | } 203 | 204 | return to; 205 | }; 206 | 207 | module.exports = assign; 208 | 209 | },{}],4:[function(_dereq_,module,exports){ 210 | /** 211 | * Copyright 2013-2014, Facebook, Inc. 212 | * All rights reserved. 213 | * 214 | * This source code is licensed under the BSD-style license found in the 215 | * LICENSE file in the root directory of this source tree. An additional grant 216 | * of patent rights can be found in the PATENTS file in the same directory. 217 | * 218 | * @providesModule ReactContext 219 | */ 220 | 221 | "use strict"; 222 | 223 | var assign = _dereq_("./Object.assign"); 224 | 225 | /** 226 | * Keeps track of the current context. 227 | * 228 | * The context is automatically passed down the component ownership hierarchy 229 | * and is accessible via `this.context` on ReactCompositeComponents. 230 | */ 231 | var ReactContext = { 232 | 233 | /** 234 | * @internal 235 | * @type {object} 236 | */ 237 | current: {}, 238 | 239 | /** 240 | * Temporarily extends the current context while executing scopedCallback. 241 | * 242 | * A typical use case might look like 243 | * 244 | * render: function() { 245 | * var children = ReactContext.withContext({foo: 'foo'}, () => ( 246 | * 247 | * )); 248 | * return
{children}
; 249 | * } 250 | * 251 | * @param {object} newContext New context to merge into the existing context 252 | * @param {function} scopedCallback Callback to run with the new context 253 | * @return {ReactComponent|array} 254 | */ 255 | withContext: function(newContext, scopedCallback) { 256 | var result; 257 | var previousContext = ReactContext.current; 258 | ReactContext.current = assign({}, previousContext, newContext); 259 | try { 260 | result = scopedCallback(); 261 | } finally { 262 | ReactContext.current = previousContext; 263 | } 264 | return result; 265 | } 266 | 267 | }; 268 | 269 | module.exports = ReactContext; 270 | 271 | },{"./Object.assign":3}],5:[function(_dereq_,module,exports){ 272 | /** 273 | * Copyright 2013-2014, Facebook, Inc. 274 | * All rights reserved. 275 | * 276 | * This source code is licensed under the BSD-style license found in the 277 | * LICENSE file in the root directory of this source tree. An additional grant 278 | * of patent rights can be found in the PATENTS file in the same directory. 279 | * 280 | * @providesModule ReactCurrentOwner 281 | */ 282 | 283 | "use strict"; 284 | 285 | /** 286 | * Keeps track of the current owner. 287 | * 288 | * The current owner is the component who should own any components that are 289 | * currently being constructed. 290 | * 291 | * The depth indicate how many composite components are above this render level. 292 | */ 293 | var ReactCurrentOwner = { 294 | 295 | /** 296 | * @internal 297 | * @type {ReactComponent} 298 | */ 299 | current: null 300 | 301 | }; 302 | 303 | module.exports = ReactCurrentOwner; 304 | 305 | },{}],6:[function(_dereq_,module,exports){ 306 | /** 307 | * Copyright 2014, Facebook, Inc. 308 | * All rights reserved. 309 | * 310 | * This source code is licensed under the BSD-style license found in the 311 | * LICENSE file in the root directory of this source tree. An additional grant 312 | * of patent rights can be found in the PATENTS file in the same directory. 313 | * 314 | * @providesModule ReactElement 315 | */ 316 | 317 | "use strict"; 318 | 319 | var ReactContext = _dereq_("./ReactContext"); 320 | var ReactCurrentOwner = _dereq_("./ReactCurrentOwner"); 321 | 322 | var warning = _dereq_("./warning"); 323 | 324 | var RESERVED_PROPS = { 325 | key: true, 326 | ref: true 327 | }; 328 | 329 | /** 330 | * Warn for mutations. 331 | * 332 | * @internal 333 | * @param {object} object 334 | * @param {string} key 335 | */ 336 | function defineWarningProperty(object, key) { 337 | Object.defineProperty(object, key, { 338 | 339 | configurable: false, 340 | enumerable: true, 341 | 342 | get: function() { 343 | if (!this._store) { 344 | return null; 345 | } 346 | return this._store[key]; 347 | }, 348 | 349 | set: function(value) { 350 | ("production" !== "production" ? warning( 351 | false, 352 | 'Don\'t set the ' + key + ' property of the component. ' + 353 | 'Mutate the existing props object instead.' 354 | ) : null); 355 | this._store[key] = value; 356 | } 357 | 358 | }); 359 | } 360 | 361 | /** 362 | * This is updated to true if the membrane is successfully created. 363 | */ 364 | var useMutationMembrane = false; 365 | 366 | /** 367 | * Warn for mutations. 368 | * 369 | * @internal 370 | * @param {object} element 371 | */ 372 | function defineMutationMembrane(prototype) { 373 | try { 374 | var pseudoFrozenProperties = { 375 | props: true 376 | }; 377 | for (var key in pseudoFrozenProperties) { 378 | defineWarningProperty(prototype, key); 379 | } 380 | useMutationMembrane = true; 381 | } catch (x) { 382 | // IE will fail on defineProperty 383 | } 384 | } 385 | 386 | /** 387 | * Base constructor for all React elements. This is only used to make this 388 | * work with a dynamic instanceof check. Nothing should live on this prototype. 389 | * 390 | * @param {*} type 391 | * @param {string|object} ref 392 | * @param {*} key 393 | * @param {*} props 394 | * @internal 395 | */ 396 | var ReactElement = function(type, key, ref, owner, context, props) { 397 | // Built-in properties that belong on the element 398 | this.type = type; 399 | this.key = key; 400 | this.ref = ref; 401 | 402 | // Record the component responsible for creating this element. 403 | this._owner = owner; 404 | 405 | // TODO: Deprecate withContext, and then the context becomes accessible 406 | // through the owner. 407 | this._context = context; 408 | 409 | if ("production" !== "production") { 410 | // The validation flag and props are currently mutative. We put them on 411 | // an external backing store so that we can freeze the whole object. 412 | // This can be replaced with a WeakMap once they are implemented in 413 | // commonly used development environments. 414 | this._store = { validated: false, props: props }; 415 | 416 | // We're not allowed to set props directly on the object so we early 417 | // return and rely on the prototype membrane to forward to the backing 418 | // store. 419 | if (useMutationMembrane) { 420 | Object.freeze(this); 421 | return; 422 | } 423 | } 424 | 425 | this.props = props; 426 | }; 427 | 428 | // We intentionally don't expose the function on the constructor property. 429 | // ReactElement should be indistinguishable from a plain object. 430 | ReactElement.prototype = { 431 | _isReactElement: true 432 | }; 433 | 434 | if ("production" !== "production") { 435 | defineMutationMembrane(ReactElement.prototype); 436 | } 437 | 438 | ReactElement.createElement = function(type, config, children) { 439 | var propName; 440 | 441 | // Reserved names are extracted 442 | var props = {}; 443 | 444 | var key = null; 445 | var ref = null; 446 | 447 | if (config != null) { 448 | ref = config.ref === undefined ? null : config.ref; 449 | if ("production" !== "production") { 450 | ("production" !== "production" ? warning( 451 | config.key !== null, 452 | 'createElement(...): Encountered component with a `key` of null. In ' + 453 | 'a future version, this will be treated as equivalent to the string ' + 454 | '\'null\'; instead, provide an explicit key or use undefined.' 455 | ) : null); 456 | } 457 | // TODO: Change this back to `config.key === undefined` 458 | key = config.key == null ? null : '' + config.key; 459 | // Remaining properties are added to a new props object 460 | for (propName in config) { 461 | if (config.hasOwnProperty(propName) && 462 | !RESERVED_PROPS.hasOwnProperty(propName)) { 463 | props[propName] = config[propName]; 464 | } 465 | } 466 | } 467 | 468 | // Children can be more than one argument, and those are transferred onto 469 | // the newly allocated props object. 470 | var childrenLength = arguments.length - 2; 471 | if (childrenLength === 1) { 472 | props.children = children; 473 | } else if (childrenLength > 1) { 474 | var childArray = Array(childrenLength); 475 | for (var i = 0; i < childrenLength; i++) { 476 | childArray[i] = arguments[i + 2]; 477 | } 478 | props.children = childArray; 479 | } 480 | 481 | // Resolve default props 482 | if (type.defaultProps) { 483 | var defaultProps = type.defaultProps; 484 | for (propName in defaultProps) { 485 | if (typeof props[propName] === 'undefined') { 486 | props[propName] = defaultProps[propName]; 487 | } 488 | } 489 | } 490 | 491 | return new ReactElement( 492 | type, 493 | key, 494 | ref, 495 | ReactCurrentOwner.current, 496 | ReactContext.current, 497 | props 498 | ); 499 | }; 500 | 501 | ReactElement.createFactory = function(type) { 502 | var factory = ReactElement.createElement.bind(null, type); 503 | // Expose the type on the factory and the prototype so that it can be 504 | // easily accessed on elements. E.g. .type === Foo.type. 505 | // This should not be named `constructor` since this may not be the function 506 | // that created the element, and it may not even be a constructor. 507 | factory.type = type; 508 | return factory; 509 | }; 510 | 511 | ReactElement.cloneAndReplaceProps = function(oldElement, newProps) { 512 | var newElement = new ReactElement( 513 | oldElement.type, 514 | oldElement.key, 515 | oldElement.ref, 516 | oldElement._owner, 517 | oldElement._context, 518 | newProps 519 | ); 520 | 521 | if ("production" !== "production") { 522 | // If the key on the original is valid, then the clone is valid 523 | newElement._store.validated = oldElement._store.validated; 524 | } 525 | return newElement; 526 | }; 527 | 528 | /** 529 | * @param {?object} object 530 | * @return {boolean} True if `object` is a valid component. 531 | * @final 532 | */ 533 | ReactElement.isValidElement = function(object) { 534 | // ReactTestUtils is often used outside of beforeEach where as React is 535 | // within it. This leads to two different instances of React on the same 536 | // page. To identify a element from a different React instance we use 537 | // a flag instead of an instanceof check. 538 | var isElement = !!(object && object._isReactElement); 539 | // if (isElement && !(object instanceof ReactElement)) { 540 | // This is an indicator that you're using multiple versions of React at the 541 | // same time. This will screw with ownership and stuff. Fix it, please. 542 | // TODO: We could possibly warn here. 543 | // } 544 | return isElement; 545 | }; 546 | 547 | module.exports = ReactElement; 548 | 549 | },{"./ReactContext":4,"./ReactCurrentOwner":5,"./warning":13}],7:[function(_dereq_,module,exports){ 550 | /** 551 | * Copyright 2013-2014, Facebook, Inc. 552 | * All rights reserved. 553 | * 554 | * This source code is licensed under the BSD-style license found in the 555 | * LICENSE file in the root directory of this source tree. An additional grant 556 | * of patent rights can be found in the PATENTS file in the same directory. 557 | * 558 | * @providesModule ReactPropTransferer 559 | */ 560 | 561 | "use strict"; 562 | 563 | var assign = _dereq_("./Object.assign"); 564 | var emptyFunction = _dereq_("./emptyFunction"); 565 | var invariant = _dereq_("./invariant"); 566 | var joinClasses = _dereq_("./joinClasses"); 567 | var warning = _dereq_("./warning"); 568 | 569 | var didWarn = false; 570 | 571 | /** 572 | * Creates a transfer strategy that will merge prop values using the supplied 573 | * `mergeStrategy`. If a prop was previously unset, this just sets it. 574 | * 575 | * @param {function} mergeStrategy 576 | * @return {function} 577 | */ 578 | function createTransferStrategy(mergeStrategy) { 579 | return function(props, key, value) { 580 | if (!props.hasOwnProperty(key)) { 581 | props[key] = value; 582 | } else { 583 | props[key] = mergeStrategy(props[key], value); 584 | } 585 | }; 586 | } 587 | 588 | var transferStrategyMerge = createTransferStrategy(function(a, b) { 589 | // `merge` overrides the first object's (`props[key]` above) keys using the 590 | // second object's (`value`) keys. An object's style's existing `propA` would 591 | // get overridden. Flip the order here. 592 | return assign({}, b, a); 593 | }); 594 | 595 | /** 596 | * Transfer strategies dictate how props are transferred by `transferPropsTo`. 597 | * NOTE: if you add any more exceptions to this list you should be sure to 598 | * update `cloneWithProps()` accordingly. 599 | */ 600 | var TransferStrategies = { 601 | /** 602 | * Never transfer `children`. 603 | */ 604 | children: emptyFunction, 605 | /** 606 | * Transfer the `className` prop by merging them. 607 | */ 608 | className: createTransferStrategy(joinClasses), 609 | /** 610 | * Transfer the `style` prop (which is an object) by merging them. 611 | */ 612 | style: transferStrategyMerge 613 | }; 614 | 615 | /** 616 | * Mutates the first argument by transferring the properties from the second 617 | * argument. 618 | * 619 | * @param {object} props 620 | * @param {object} newProps 621 | * @return {object} 622 | */ 623 | function transferInto(props, newProps) { 624 | for (var thisKey in newProps) { 625 | if (!newProps.hasOwnProperty(thisKey)) { 626 | continue; 627 | } 628 | 629 | var transferStrategy = TransferStrategies[thisKey]; 630 | 631 | if (transferStrategy && TransferStrategies.hasOwnProperty(thisKey)) { 632 | transferStrategy(props, thisKey, newProps[thisKey]); 633 | } else if (!props.hasOwnProperty(thisKey)) { 634 | props[thisKey] = newProps[thisKey]; 635 | } 636 | } 637 | return props; 638 | } 639 | 640 | /** 641 | * ReactPropTransferer are capable of transferring props to another component 642 | * using a `transferPropsTo` method. 643 | * 644 | * @class ReactPropTransferer 645 | */ 646 | var ReactPropTransferer = { 647 | 648 | TransferStrategies: TransferStrategies, 649 | 650 | /** 651 | * Merge two props objects using TransferStrategies. 652 | * 653 | * @param {object} oldProps original props (they take precedence) 654 | * @param {object} newProps new props to merge in 655 | * @return {object} a new object containing both sets of props merged. 656 | */ 657 | mergeProps: function(oldProps, newProps) { 658 | return transferInto(assign({}, oldProps), newProps); 659 | }, 660 | 661 | /** 662 | * @lends {ReactPropTransferer.prototype} 663 | */ 664 | Mixin: { 665 | 666 | /** 667 | * Transfer props from this component to a target component. 668 | * 669 | * Props that do not have an explicit transfer strategy will be transferred 670 | * only if the target component does not already have the prop set. 671 | * 672 | * This is usually used to pass down props to a returned root component. 673 | * 674 | * @param {ReactElement} element Component receiving the properties. 675 | * @return {ReactElement} The supplied `component`. 676 | * @final 677 | * @protected 678 | */ 679 | transferPropsTo: function(element) { 680 | ("production" !== "production" ? invariant( 681 | element._owner === this, 682 | '%s: You can\'t call transferPropsTo() on a component that you ' + 683 | 'don\'t own, %s. This usually means you are calling ' + 684 | 'transferPropsTo() on a component passed in as props or children.', 685 | this.constructor.displayName, 686 | typeof element.type === 'string' ? 687 | element.type : 688 | element.type.displayName 689 | ) : invariant(element._owner === this)); 690 | 691 | if ("production" !== "production") { 692 | if (!didWarn) { 693 | didWarn = true; 694 | ("production" !== "production" ? warning( 695 | false, 696 | 'transferPropsTo is deprecated. ' + 697 | 'See http://fb.me/react-transferpropsto for more information.' 698 | ) : null); 699 | } 700 | } 701 | 702 | // Because elements are immutable we have to merge into the existing 703 | // props object rather than clone it. 704 | transferInto(element.props, this.props); 705 | 706 | return element; 707 | } 708 | 709 | } 710 | }; 711 | 712 | module.exports = ReactPropTransferer; 713 | 714 | },{"./Object.assign":3,"./emptyFunction":9,"./invariant":10,"./joinClasses":11,"./warning":13}],8:[function(_dereq_,module,exports){ 715 | /** 716 | * Copyright 2013-2014, Facebook, Inc. 717 | * All rights reserved. 718 | * 719 | * This source code is licensed under the BSD-style license found in the 720 | * LICENSE file in the root directory of this source tree. An additional grant 721 | * of patent rights can be found in the PATENTS file in the same directory. 722 | * 723 | * @typechecks 724 | * @providesModule cloneWithProps 725 | */ 726 | 727 | "use strict"; 728 | 729 | var ReactElement = _dereq_("./ReactElement"); 730 | var ReactPropTransferer = _dereq_("./ReactPropTransferer"); 731 | 732 | var keyOf = _dereq_("./keyOf"); 733 | var warning = _dereq_("./warning"); 734 | 735 | var CHILDREN_PROP = keyOf({children: null}); 736 | 737 | /** 738 | * Sometimes you want to change the props of a child passed to you. Usually 739 | * this is to add a CSS class. 740 | * 741 | * @param {object} child child component you'd like to clone 742 | * @param {object} props props you'd like to modify. They will be merged 743 | * as if you used `transferPropsTo()`. 744 | * @return {object} a clone of child with props merged in. 745 | */ 746 | function cloneWithProps(child, props) { 747 | if ("production" !== "production") { 748 | ("production" !== "production" ? warning( 749 | !child.ref, 750 | 'You are calling cloneWithProps() on a child with a ref. This is ' + 751 | 'dangerous because you\'re creating a new child which will not be ' + 752 | 'added as a ref to its parent.' 753 | ) : null); 754 | } 755 | 756 | var newProps = ReactPropTransferer.mergeProps(props, child.props); 757 | 758 | // Use `child.props.children` if it is provided. 759 | if (!newProps.hasOwnProperty(CHILDREN_PROP) && 760 | child.props.hasOwnProperty(CHILDREN_PROP)) { 761 | newProps.children = child.props.children; 762 | } 763 | 764 | // The current API doesn't retain _owner and _context, which is why this 765 | // doesn't use ReactElement.cloneAndReplaceProps. 766 | return ReactElement.createElement(child.type, newProps); 767 | } 768 | 769 | module.exports = cloneWithProps; 770 | 771 | },{"./ReactElement":6,"./ReactPropTransferer":7,"./keyOf":12,"./warning":13}],9:[function(_dereq_,module,exports){ 772 | /** 773 | * Copyright 2013-2014, Facebook, Inc. 774 | * All rights reserved. 775 | * 776 | * This source code is licensed under the BSD-style license found in the 777 | * LICENSE file in the root directory of this source tree. An additional grant 778 | * of patent rights can be found in the PATENTS file in the same directory. 779 | * 780 | * @providesModule emptyFunction 781 | */ 782 | 783 | function makeEmptyFunction(arg) { 784 | return function() { 785 | return arg; 786 | }; 787 | } 788 | 789 | /** 790 | * This function accepts and discards inputs; it has no side effects. This is 791 | * primarily useful idiomatically for overridable function endpoints which 792 | * always need to be callable, since JS lacks a null-call idiom ala Cocoa. 793 | */ 794 | function emptyFunction() {} 795 | 796 | emptyFunction.thatReturns = makeEmptyFunction; 797 | emptyFunction.thatReturnsFalse = makeEmptyFunction(false); 798 | emptyFunction.thatReturnsTrue = makeEmptyFunction(true); 799 | emptyFunction.thatReturnsNull = makeEmptyFunction(null); 800 | emptyFunction.thatReturnsThis = function() { return this; }; 801 | emptyFunction.thatReturnsArgument = function(arg) { return arg; }; 802 | 803 | module.exports = emptyFunction; 804 | 805 | },{}],10:[function(_dereq_,module,exports){ 806 | /** 807 | * Copyright 2013-2014, Facebook, Inc. 808 | * All rights reserved. 809 | * 810 | * This source code is licensed under the BSD-style license found in the 811 | * LICENSE file in the root directory of this source tree. An additional grant 812 | * of patent rights can be found in the PATENTS file in the same directory. 813 | * 814 | * @providesModule invariant 815 | */ 816 | 817 | "use strict"; 818 | 819 | /** 820 | * Use invariant() to assert state which your program assumes to be true. 821 | * 822 | * Provide sprintf-style format (only %s is supported) and arguments 823 | * to provide information about what broke and what you were 824 | * expecting. 825 | * 826 | * The invariant message will be stripped in production, but the invariant 827 | * will remain to ensure logic does not differ in production. 828 | */ 829 | 830 | var invariant = function(condition, format, a, b, c, d, e, f) { 831 | if ("production" !== "production") { 832 | if (format === undefined) { 833 | throw new Error('invariant requires an error message argument'); 834 | } 835 | } 836 | 837 | if (!condition) { 838 | var error; 839 | if (format === undefined) { 840 | error = new Error( 841 | 'Minified exception occurred; use the non-minified dev environment ' + 842 | 'for the full error message and additional helpful warnings.' 843 | ); 844 | } else { 845 | var args = [a, b, c, d, e, f]; 846 | var argIndex = 0; 847 | error = new Error( 848 | 'Invariant Violation: ' + 849 | format.replace(/%s/g, function() { return args[argIndex++]; }) 850 | ); 851 | } 852 | 853 | error.framesToPop = 1; // we don't care about invariant's own frame 854 | throw error; 855 | } 856 | }; 857 | 858 | module.exports = invariant; 859 | 860 | },{}],11:[function(_dereq_,module,exports){ 861 | /** 862 | * Copyright 2013-2014, Facebook, Inc. 863 | * All rights reserved. 864 | * 865 | * This source code is licensed under the BSD-style license found in the 866 | * LICENSE file in the root directory of this source tree. An additional grant 867 | * of patent rights can be found in the PATENTS file in the same directory. 868 | * 869 | * @providesModule joinClasses 870 | * @typechecks static-only 871 | */ 872 | 873 | "use strict"; 874 | 875 | /** 876 | * Combines multiple className strings into one. 877 | * http://jsperf.com/joinclasses-args-vs-array 878 | * 879 | * @param {...?string} classes 880 | * @return {string} 881 | */ 882 | function joinClasses(className/*, ... */) { 883 | if (!className) { 884 | className = ''; 885 | } 886 | var nextClass; 887 | var argLength = arguments.length; 888 | if (argLength > 1) { 889 | for (var ii = 1; ii < argLength; ii++) { 890 | nextClass = arguments[ii]; 891 | if (nextClass) { 892 | className = (className ? className + ' ' : '') + nextClass; 893 | } 894 | } 895 | } 896 | return className; 897 | } 898 | 899 | module.exports = joinClasses; 900 | 901 | },{}],12:[function(_dereq_,module,exports){ 902 | /** 903 | * Copyright 2013-2014, Facebook, Inc. 904 | * All rights reserved. 905 | * 906 | * This source code is licensed under the BSD-style license found in the 907 | * LICENSE file in the root directory of this source tree. An additional grant 908 | * of patent rights can be found in the PATENTS file in the same directory. 909 | * 910 | * @providesModule keyOf 911 | */ 912 | 913 | /** 914 | * Allows extraction of a minified key. Let's the build system minify keys 915 | * without loosing the ability to dynamically use key strings as values 916 | * themselves. Pass in an object with a single key/val pair and it will return 917 | * you the string key of that single record. Suppose you want to grab the 918 | * value for a key 'className' inside of an object. Key/val minification may 919 | * have aliased that key to be 'xa12'. keyOf({className: null}) will return 920 | * 'xa12' in that case. Resolve keys you want to use once at startup time, then 921 | * reuse those resolutions. 922 | */ 923 | var keyOf = function(oneKeyObj) { 924 | var key; 925 | for (key in oneKeyObj) { 926 | if (!oneKeyObj.hasOwnProperty(key)) { 927 | continue; 928 | } 929 | return key; 930 | } 931 | return null; 932 | }; 933 | 934 | 935 | module.exports = keyOf; 936 | 937 | },{}],13:[function(_dereq_,module,exports){ 938 | /** 939 | * Copyright 2014, Facebook, Inc. 940 | * All rights reserved. 941 | * 942 | * This source code is licensed under the BSD-style license found in the 943 | * LICENSE file in the root directory of this source tree. An additional grant 944 | * of patent rights can be found in the PATENTS file in the same directory. 945 | * 946 | * @providesModule warning 947 | */ 948 | 949 | "use strict"; 950 | 951 | var emptyFunction = _dereq_("./emptyFunction"); 952 | 953 | /** 954 | * Similar to invariant but only logs a warning if the condition is not met. 955 | * This can be used to log issues in development environments in critical 956 | * paths. Removing the logging code for production environments will keep the 957 | * same logic and follow the same code paths. 958 | */ 959 | 960 | var warning = emptyFunction; 961 | 962 | if ("production" !== "production") { 963 | warning = function(condition, format ) {for (var args=[],$__0=2,$__1=arguments.length;$__0<$__1;$__0++) args.push(arguments[$__0]); 964 | if (format === undefined) { 965 | throw new Error( 966 | '`warning(condition, format, ...args)` requires a warning ' + 967 | 'message argument' 968 | ); 969 | } 970 | 971 | if (!condition) { 972 | var argIndex = 0; 973 | console.warn('Warning: ' + format.replace(/%s/g, function() {return args[argIndex++];})); 974 | } 975 | }; 976 | } 977 | 978 | module.exports = warning; 979 | 980 | },{"./emptyFunction":9}]},{},[2]) 981 | (2) 982 | }); --------------------------------------------------------------------------------