├── .watchmanconfig ├── .eslintrc ├── .gitignore ├── src ├── __tests__ │ └── index.test.js └── index.js ├── .flowconfig ├── .babelrc ├── try ├── index.html ├── redux.js ├── flip.js └── slideshow.js ├── package.json ├── README.md ├── flow-typed └── npm │ └── react-redux_v5.x.x.js └── lib └── index.js /.watchmanconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | /dist/ 4 | -------------------------------------------------------------------------------- /src/__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // $FlowFixMe 3 | test('it should do something', () => {}); 4 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [lints] 8 | 9 | [options] 10 | 11 | [strict] 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react", "stage-0"], 3 | "plugins": ["transform-class-properties", "babel-plugin-jsx-adopt"] 4 | } 5 | -------------------------------------------------------------------------------- /try/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 17 | 18 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freezus", 3 | "version": "1.1.0", 4 | "main": "lib/index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "babel-cli": "^6.26.0", 8 | "nullthrows": "^1.0.1", 9 | "react": "16.4.0", 10 | "react-redux": "^5.0.7", 11 | "redux": "^4.0.0", 12 | "regenerator-runtime": "^0.11.1" 13 | }, 14 | "scripts": { 15 | "build": "babel src -d lib", 16 | "start": "parcel try/index.html", 17 | "test": "jest" 18 | }, 19 | "devDependencies": { 20 | "babel-eslint": "^7.2.3", 21 | "babel-plugin-jsx-adopt": "^0.1.0", 22 | "babel-plugin-transform-class-properties": "^6.24.1", 23 | "babel-polyfill": "^6.26.0", 24 | "babel-preset-env": "^1.7.0", 25 | "babel-preset-react": "^6.24.1", 26 | "babel-preset-stage-0": "^6.24.1", 27 | "eslint": "^4.1.1", 28 | "eslint-config-react-app": "^2.1.0", 29 | "eslint-plugin-flowtype": "^2.34.1", 30 | "eslint-plugin-import": "^2.6.0", 31 | "eslint-plugin-jsx-a11y": "^5.1.1", 32 | "eslint-plugin-react": "^7.1.0", 33 | "flow-bin": "^0.73.0", 34 | "jest": "^23.0.1", 35 | "parcel-bundler": "^1.8.1", 36 | "popmotion": "^8.2.3", 37 | "popmotion-pose": "^2.0.1", 38 | "react-art": "^16.4.0", 39 | "react-dom": "16.4.0", 40 | "react-motion": "^0.5.2", 41 | "react-native-web": "^0.7.3", 42 | "react-pose": "^2.0.0" 43 | }, 44 | "alias": { 45 | "react-native": "react-native-web" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /try/redux.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | // @format 3 | import 'regenerator-runtime/runtime'; 4 | import React, { Component, Fragment } from 'react'; 5 | 6 | import type { Node } from 'react'; 7 | import { render, findDOMNode } from 'react-dom'; 8 | 9 | import Page from '../src'; 10 | 11 | import { createStore, combineReducers } from 'redux'; 12 | import { connect, Provider } from 'react-redux'; 13 | 14 | type State = { count: number }; 15 | 16 | const initial = { count: 0 }; 17 | 18 | function reducer(state = initial, action) { 19 | switch (action.type) { 20 | case 'increment': 21 | return { ...state, count: state.count + 1 }; 22 | case 'decrement': 23 | return { ...state, count: state.count - 1 }; 24 | } 25 | return state; 26 | } 27 | 28 | const store = createStore(reducer); 29 | 30 | function increment() { 31 | return { type: 'increment' }; 32 | } 33 | function decrement() { 34 | return { type: 'decrement' }; 35 | } 36 | 37 | function select(state) { 38 | return { count: state.count }; 39 | } 40 | 41 | const Counter = connect(select, { increment, decrement })( 42 | class extends Component<{ 43 | count: number, 44 | }> { 45 | render() { 46 | return this.props.count; 47 | } 48 | }, 49 | ); 50 | 51 | function sleep(n) { 52 | return new Promise(resolve => { 53 | setTimeout(resolve, n); 54 | }); 55 | } 56 | 57 | const App = connect(select, { increment, decrement })(props => ( 58 | 59 | 60 | 61 | 67 | 68 | 69 | 70 | )); 71 | 72 | render( 73 | 74 | 75 | , 76 | window.root, 77 | ); 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## freezus 2 | 3 | [work in progress] 4 | 5 | [ost](https://soundcloud.com/sahandii/cold-as-ice-remake) 6 | 7 | `yarn add freezus` 8 | 9 | ```jsx 10 | import Transition from 'freezus' 11 | 12 | // ... 13 | 14 | ...} 17 | onExit={async function*() => { 18 | // do whatever! 19 | // the previous render sticks around until this function exits 20 | // and by magic, redux state is frozen inside it! 21 | // local state and everything else works as expected 22 | 23 | // you can now manually animate that old element out, 24 | // do a shared element transition, whatever. 25 | // use jquery for all I care 26 | // refs are doubly useful here :) 27 | }}> 28 | 29 | 30 | ``` 31 | 32 | ## rationale 33 | 34 | * traditionally, by rendering a new view in a position, we implicitly destroy the previous view (if it was different) 35 | * alternately, we have to manually maintain ui state, mixing our animation concerns with business logic. 36 | * `` abstracts that for you 37 | 38 | ## how does it work 39 | 40 | * fragments to render multiple phases at once 41 | * lifecycle - onEnter, onExit 42 | * cancellable 43 | * freeze redux state for the subtree 44 | 45 | ## cancellation 46 | 47 | async generators handle cancellation pretty nicely 48 | 49 | ```jsx 50 | onExit={async function*() => { 51 | try{ 52 | await sleep(1000) 53 | } 54 | finally { 55 | const isCancelled = yield; 56 | // do the thing 57 | } 58 | }} 59 | ``` 60 | 61 | ## state 62 | 63 | by combining `reduce` and the `yield`s from `onEnter`/`onExit`, you can 64 | implement a redux-like pipeline to manage state. the default reducer is 65 | `(x, y) => y` i.e. - it saves the last action as the state. You can read 66 | from this state with `Transition.Consumer` 67 | 68 | ```jsx 69 | [todo]; 70 | ``` 71 | 72 | todo - 73 | 74 | * examples 75 | * tests 76 | * freeze react-router 77 | * freeze anything on context 78 | * ` { 16 | getDerivedStateFromProps(nextProps, prevState) { 17 | // if entering and previously available in stack 18 | // opacity: 0 19 | // onMount, measure dimensions, delta from previous one 20 | // add to stack 21 | // opacity one, [stack transforms] 22 | // set state, rerender 23 | // if exiting 24 | // 25 | } 26 | render() { 27 | // $FlowFixMe 28 | const pose = adopt(); 29 | return this.props.children; 30 | } 31 | } 32 | 33 | class Screen extends React.Component<{}> { 34 | positions = Array.from({ length: 4 }, () => Math.round(Math.random() * 20)); 35 | render() { 36 | return ( 37 |
38 | {Array.from({ length: 20 }, (_, i) => ( 39 | = 0} 41 | key={i} 42 | shareKey={i} 43 | style={{ backgroundColor: 'red' }} 44 | > 45 |
{i}
46 |
47 | ))} 48 |
49 | ); 50 | } 51 | } 52 | 53 | function sleep(n: number) { 54 | return new Promise(resolve => setTimeout(resolve, n)); 55 | } 56 | 57 | class Display extends React.Component<{}, { screen: number }> { 58 | state = { screen: 0 }; 59 | render() { 60 | return } />; 61 | } 62 | } 63 | 64 | AppRegistry.registerComponent('App', () => Display); 65 | 66 | AppRegistry.runApplication('App', { 67 | initialProps: {}, 68 | rootTag: window.root, 69 | }); 70 | 71 | // A <-> B <-> C 72 | // 73 | // content 79 | // set to position A' 80 | // put that inside another 81 | 82 | /* 83 | // ... 84 | 85 | some content 86 | 87 | */ 88 | -------------------------------------------------------------------------------- /try/slideshow.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import 'regenerator-runtime/runtime'; 3 | import React, { type Node } from 'react'; 4 | import { render } from 'react-dom'; 5 | import posed from 'react-pose'; 6 | import Transition from '../src'; 7 | 8 | const boxStyle = { 9 | width: 200, 10 | height: 200, 11 | position: 'absolute', 12 | backgroundColor: `#ccc`, 13 | top: 40, 14 | fontSize: 40, 15 | fontWeight: 'bold', 16 | display: 'flex', 17 | alignItems: 'center', 18 | justifyContent: 'center', 19 | }; 20 | 21 | const swoosh = { type: 'spring', stiffness: 10, damping: 10 }; 22 | 23 | const initial = { 24 | backward: { left: -100, opacity: 0 }, 25 | forward: { left: 100, opacity: 0 }, 26 | }; 27 | 28 | const config = { 29 | enter: { left: 0, opacity: 1, transition: swoosh }, 30 | exitLeft: { left: -100, opacity: 0, transition: swoosh }, 31 | exitRight: { left: 100, opacity: 0, transition: swoosh }, 32 | }; 33 | 34 | const Box = posed.div(config); 35 | 36 | type Direction = 'forward' | 'backward'; 37 | 38 | class Slide extends React.Component<{ 39 | children: Node, 40 | direction: Direction, 41 | }> { 42 | style = { 43 | ...boxStyle, 44 | ...initial[this.props.direction], 45 | }; 46 | render() { 47 | // $FlowFixMe 48 | const pose = adopt(); 49 | return ( 50 | 51 | {this.props.children} 52 | 53 | ); 54 | } 55 | } 56 | 57 | function sleep(n: number) { 58 | return new Promise(resolve => setTimeout(resolve, n)); 59 | } 60 | 61 | export class Slideshow extends React.Component< 62 | { fn: number => Node }, 63 | { slide: number, direction: Direction }, 64 | > { 65 | state = { slide: 0, direction: 'forward' }; 66 | 67 | next = () => 68 | this.setState(x => ({ slide: x.slide + 1, direction: 'forward' })); 69 | 70 | prev = () => 71 | this.setState(x => ({ slide: x.slide - 1, direction: 'backward' })); 72 | 73 | render() { 74 | return ( 75 |
76 | 77 | 78 | 88 | 89 | {this.props.fn(this.state.slide)} 90 | 91 | 92 |
93 | ); 94 | } 95 | } 96 | render( x} />, window.root); 97 | -------------------------------------------------------------------------------- /flow-typed/npm/react-redux_v5.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 59b0c4be0e1408f21e2446be96c79804 2 | // flow-typed version: 9092387fd2/react-redux_v5.x.x/flow_>=v0.54.x 3 | 4 | import type { Dispatch, Store } from "redux"; 5 | 6 | declare module "react-redux" { 7 | /* 8 | 9 | S = State 10 | A = Action 11 | OP = OwnProps 12 | SP = StateProps 13 | DP = DispatchProps 14 | 15 | */ 16 | 17 | declare type MapStateToProps = ( 18 | state: S, 19 | ownProps: OP 20 | ) => ((state: S, ownProps: OP) => SP) | SP; 21 | 22 | declare type MapDispatchToProps = 23 | | ((dispatch: Dispatch, ownProps: OP) => DP) 24 | | DP; 25 | 26 | declare type MergeProps = ( 27 | stateProps: SP, 28 | dispatchProps: DP, 29 | ownProps: OP 30 | ) => P; 31 | 32 | declare type Context = { store: Store<*, *> }; 33 | 34 | declare type ComponentWithDefaultProps = Class< 35 | React$Component 36 | > & { defaultProps: DP }; 37 | 38 | declare class ConnectedComponentWithDefaultProps< 39 | OP, 40 | DP, 41 | CP 42 | > extends React$Component { 43 | static defaultProps: DP, // <= workaround for https://github.com/facebook/flow/issues/4644 44 | static WrappedComponent: Class>, 45 | getWrappedInstance(): React$Component, 46 | props: OP, 47 | state: void 48 | } 49 | 50 | declare class ConnectedComponent extends React$Component { 51 | static WrappedComponent: Class>, 52 | getWrappedInstance(): React$Component

, 53 | props: OP, 54 | state: void 55 | } 56 | 57 | declare type ConnectedComponentWithDefaultPropsClass = Class< 58 | ConnectedComponentWithDefaultProps 59 | >; 60 | 61 | declare type ConnectedComponentClass = Class< 62 | ConnectedComponent 63 | >; 64 | 65 | declare type Connector = (( 66 | component: ComponentWithDefaultProps 67 | ) => ConnectedComponentWithDefaultPropsClass) & 68 | ((component: React$ComponentType

) => ConnectedComponentClass); 69 | 70 | declare class Provider extends React$Component<{ 71 | store: Store, 72 | children?: any 73 | }> {} 74 | 75 | declare function createProvider( 76 | storeKey?: string, 77 | subKey?: string 78 | ): Provider<*, *>; 79 | 80 | declare type ConnectOptions = { 81 | pure?: boolean, 82 | withRef?: boolean 83 | }; 84 | 85 | declare type Null = null | void; 86 | 87 | declare function connect( 88 | ...rest: Array // <= workaround for https://github.com/facebook/flow/issues/2360 89 | ): Connector } & OP>>; 90 | 91 | declare function connect( 92 | mapStateToProps: Null, 93 | mapDispatchToProps: Null, 94 | mergeProps: Null, 95 | options: ConnectOptions 96 | ): Connector } & OP>>; 97 | 98 | declare function connect( 99 | mapStateToProps: MapStateToProps, 100 | mapDispatchToProps: Null, 101 | mergeProps: Null, 102 | options?: ConnectOptions 103 | ): Connector } & OP>>; 104 | 105 | declare function connect( 106 | mapStateToProps: Null, 107 | mapDispatchToProps: MapDispatchToProps, 108 | mergeProps: Null, 109 | options?: ConnectOptions 110 | ): Connector>; 111 | 112 | declare function connect( 113 | mapStateToProps: MapStateToProps, 114 | mapDispatchToProps: MapDispatchToProps, 115 | mergeProps: Null, 116 | options?: ConnectOptions 117 | ): Connector>; 118 | 119 | declare function connect( 120 | mapStateToProps: MapStateToProps, 121 | mapDispatchToProps: Null, 122 | mergeProps: MergeProps, 123 | options?: ConnectOptions 124 | ): Connector; 125 | 126 | declare function connect( 127 | mapStateToProps: MapStateToProps, 128 | mapDispatchToProps: MapDispatchToProps, 129 | mergeProps: MergeProps, 130 | options?: ConnectOptions 131 | ): Connector; 132 | } 133 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React, { Component, type Node } from 'react'; 3 | import { type Store } from 'redux'; 4 | import { Provider } from 'react-redux'; 5 | 6 | // this component freezes redux state for its children when `cold` is true 7 | // after every render, it saves a snapshot of the last used redux state 8 | // it also replacies the redux store with a 'proxy' store, which, when cold 9 | // - no-ops all action dispatches 10 | // - returns state from the snapshot 11 | class Freeze extends Component<{ cold: boolean, children: Node }> { 12 | context: { 13 | store: Store, 14 | }; 15 | static contextTypes = { 16 | store: x => null, 17 | }; 18 | // our proxy store 19 | store = { 20 | getState: () => { 21 | if (this.props.cold) { 22 | return this.snapshot; 23 | } 24 | return this.context.store.getState(); 25 | }, 26 | dispatch: action => { 27 | if (!this.props.cold) { 28 | this.context.store.dispatch(action); 29 | } 30 | }, 31 | subscribe: listener => { 32 | return this.context.store.subscribe(() => { 33 | if (!this.props.cold) { 34 | listener(); 35 | } 36 | }); 37 | }, 38 | replaceReducer: next => { 39 | // should this be a no-op too? 40 | return this.context.store.replaceReducer(next); 41 | }, 42 | }; 43 | 44 | snapshot = this.context.store && this.context.store.getState(); 45 | 46 | componentDidUpdate() { 47 | if (this.context.store && !this.props.cold) { 48 | this.snapshot = this.context.store.getState(); 49 | } 50 | } 51 | 52 | render() { 53 | return {this.props.children}; 54 | } 55 | } 56 | 57 | class Iterator extends React.Component<{ 58 | generator: ?() => AsyncGenerator<*, *, *>, 59 | onAction: any => void, 60 | onDone: ?() => void, 61 | }> { 62 | done: boolean = false; 63 | unmounted: boolean = false; 64 | iterator: ?AsyncGenerator<*, *, *>; 65 | 66 | async componentDidMount() { 67 | if (this.props.generator) { 68 | const iterator = (this.iterator = this.props.generator()); 69 | for await (const action of iterator) { 70 | if (this.unmounted) { 71 | break; 72 | } 73 | this.props.onAction(action); 74 | } 75 | } 76 | if (!this.unmounted) { 77 | this.done = true; 78 | this.props.onDone && this.props.onDone(); 79 | } 80 | } 81 | 82 | componentWillUnmount() { 83 | this.unmounted = true; 84 | const { iterator } = this; 85 | if (iterator && !this.done) { 86 | iterator.next(true); 87 | iterator.return(true); 88 | } 89 | } 90 | 91 | render() { 92 | return null; 93 | } 94 | } 95 | 96 | const FrameState = React.createContext(null); 97 | 98 | class Frame extends React.Component< 99 | { 100 | children: Node, 101 | onEnter: ?() => AsyncGenerator<*, *, *>, 102 | onExit: ?() => AsyncGenerator<*, *, *>, 103 | exiting: boolean, 104 | reduce: (any, any) => any, 105 | onExited: () => void, 106 | }, 107 | { store: any }, 108 | > { 109 | state = { store: null }; 110 | 111 | reduce = action => 112 | this.setState(x => ({ store: this.props.reduce(x.store, action) })); 113 | 114 | render() { 115 | return ( 116 | 117 | 125 | {this.props.children} 126 | 127 | ); 128 | } 129 | } 130 | 131 | // ok, so. the basic idea here is to abtract out the logic that's 132 | // recreated by hand every time - maintain state for multiple components 'in flight' 133 | // during a transition, and render them. we recommend you use some 134 | // form of a 'stretchy' around your content, usally with style 135 | // `{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }` 136 | // or a flex version 137 | // `{ flex: 1, alignSelf: stretch }` 138 | // and position them during the transitions 139 | // AsyncGenerator<*, *, *>, 150 | onExit?: () => AsyncGenerator<*, *, *>, 151 | reduce?: (any, any) => any, 152 | }; 153 | 154 | type TransitionState = { 155 | stack: Array, 156 | }; 157 | 158 | export default class Transition extends Component< 159 | TransitionProps, 160 | TransitionState, 161 | > { 162 | state = { stack: [this.props] }; 163 | 164 | static Consumer = FrameState.Consumer; // todo - we want a consumer with (state, setState) => ... 165 | 166 | static getDerivedStateFromProps( 167 | nextProps: TransitionProps, 168 | prevState: TransitionState, 169 | ): TransitionState { 170 | return { 171 | stack: [nextProps, ...prevState.stack.filter(x => x.id !== nextProps.id)], 172 | }; 173 | } 174 | 175 | render(): Node { 176 | return this.state.stack.map( 177 | ({ id, children, onEnter, onExit, reduce }, i) => ( 178 | 179 | y)} 181 | exiting={i !== 0} 182 | onEnter={onEnter} 183 | onExit={onExit} 184 | onExited={() => 185 | this.setState(prevState => ({ 186 | stack: prevState.stack.filter(x => x.id !== id), 187 | })) 188 | } 189 | > 190 | {children} 191 | 192 | 193 | ), 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | 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; }; 8 | 9 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 10 | 11 | var _react = require('react'); 12 | 13 | var _react2 = _interopRequireDefault(_react); 14 | 15 | require('redux'); 16 | 17 | var _reactRedux = require('react-redux'); 18 | 19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 20 | 21 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 22 | 23 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 24 | 25 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 26 | 27 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 28 | 29 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 30 | 31 | // this component freezes redux state for its children when `cold` is true 32 | // after every render, it saves a snapshot of the last used redux state 33 | // it also replacies the redux store with a 'proxy' store, which, when cold 34 | // - no-ops all action dispatches 35 | // - returns state from the snapshot 36 | var Freeze = function (_Component) { 37 | _inherits(Freeze, _Component); 38 | 39 | function Freeze() { 40 | var _ref; 41 | 42 | var _temp, _this, _ret; 43 | 44 | _classCallCheck(this, Freeze); 45 | 46 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 47 | args[_key] = arguments[_key]; 48 | } 49 | 50 | return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Freeze.__proto__ || Object.getPrototypeOf(Freeze)).call.apply(_ref, [this].concat(args))), _this), _this.store = { 51 | getState: function getState() { 52 | if (_this.props.cold) { 53 | return _this.snapshot; 54 | } 55 | return _this.context.store.getState(); 56 | }, 57 | dispatch: function dispatch(action) { 58 | if (!_this.props.cold) { 59 | _this.context.store.dispatch(action); 60 | } 61 | }, 62 | subscribe: function subscribe(listener) { 63 | return _this.context.store.subscribe(function () { 64 | if (!_this.props.cold) { 65 | listener(); 66 | } 67 | }); 68 | }, 69 | replaceReducer: function replaceReducer(next) { 70 | // should this be a no-op too? 71 | return _this.context.store.replaceReducer(next); 72 | } 73 | }, _this.snapshot = _this.context.store && _this.context.store.getState(), _temp), _possibleConstructorReturn(_this, _ret); 74 | } 75 | // our proxy store 76 | 77 | 78 | _createClass(Freeze, [{ 79 | key: 'componentDidUpdate', 80 | value: function componentDidUpdate() { 81 | if (this.context.store && !this.props.cold) { 82 | this.snapshot = this.context.store.getState(); 83 | } 84 | } 85 | }, { 86 | key: 'render', 87 | value: function render() { 88 | return _react2.default.createElement( 89 | _reactRedux.Provider, 90 | { store: this.store }, 91 | this.props.children 92 | ); 93 | } 94 | }]); 95 | 96 | return Freeze; 97 | }(_react.Component); 98 | 99 | Freeze.contextTypes = { 100 | store: function store(x) { 101 | return null; 102 | } 103 | }; 104 | 105 | var Transition = function (_Component2) { 106 | _inherits(Transition, _Component2); 107 | 108 | function Transition() { 109 | var _ref2, 110 | _this3 = this; 111 | 112 | var _temp2, _this2, _ret2; 113 | 114 | _classCallCheck(this, Transition); 115 | 116 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 117 | args[_key2] = arguments[_key2]; 118 | } 119 | 120 | return _ret2 = (_temp2 = (_this2 = _possibleConstructorReturn(this, (_ref2 = Transition.__proto__ || Object.getPrototypeOf(Transition)).call.apply(_ref2, [this].concat(args))), _this2), _this2.entering = {}, _this2.exiting = {}, _this2.createExitRef = function (id) { 121 | return function (ele) { 122 | var found = ele && _this2.state.stack.find(function (x) { 123 | return x.id === id; 124 | }); 125 | if (found) { 126 | _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { 127 | var token; 128 | return regeneratorRuntime.wrap(function _callee$(_context) { 129 | while (1) { 130 | switch (_context.prev = _context.next) { 131 | case 0: 132 | if (_this2.entering[id]) { 133 | _this2.entering[id] = null; 134 | } 135 | token = _this2.exiting[id] = {}; 136 | 137 | if (!found.onExit) { 138 | _context.next = 5; 139 | break; 140 | } 141 | 142 | _context.next = 5; 143 | return found.onExit(id, function () { 144 | return _this2.exiting[id] !== token; 145 | }); 146 | 147 | case 5: 148 | 149 | if (_this2.exiting[id] === token) { 150 | delete _this2.exiting[id]; 151 | _this2.setState({ 152 | stack: _this2.state.stack.filter(function (x) { 153 | return x !== found; 154 | }) 155 | }); 156 | } 157 | 158 | case 6: 159 | case 'end': 160 | return _context.stop(); 161 | } 162 | } 163 | }, _callee, _this3); 164 | }))(); 165 | } 166 | }; 167 | }, _this2.createEnterRef = function (id) { 168 | return function (ele) { 169 | var found = ele && _this2.state.stack.find(function (x) { 170 | return x.id === id; 171 | }); 172 | if (found) { 173 | _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { 174 | var token; 175 | return regeneratorRuntime.wrap(function _callee2$(_context2) { 176 | while (1) { 177 | switch (_context2.prev = _context2.next) { 178 | case 0: 179 | if (_this2.exiting[id]) { 180 | _this2.exiting[id] = null; 181 | } 182 | token = _this2.entering[id] = {}; 183 | 184 | if (!found.onEnter) { 185 | _context2.next = 5; 186 | break; 187 | } 188 | 189 | _context2.next = 5; 190 | return found.onEnter(id, function () { 191 | return _this2.entering[id] !== token; 192 | }); 193 | 194 | case 5: 195 | 196 | if (_this2.entering[id] === token) { 197 | delete _this2.entering[id]; 198 | } 199 | 200 | case 6: 201 | case 'end': 202 | return _context2.stop(); 203 | } 204 | } 205 | }, _callee2, _this3); 206 | }))(); 207 | } 208 | }; 209 | }, _this2.state = { 210 | stack: [{ 211 | id: _this2.props.id, 212 | children: _this2.props.children, 213 | onExit: _this2.props.onExit, 214 | onEnter: _this2.props.onEnter, 215 | exitRef: _this2.createExitRef(_this2.props.id), 216 | enterRef: _this2.createEnterRef(_this2.props.id) 217 | }] 218 | }, _temp2), _possibleConstructorReturn(_this2, _ret2); 219 | } 220 | 221 | _createClass(Transition, [{ 222 | key: 'componentWillReceiveProps', 223 | value: function componentWillReceiveProps(newProps) { 224 | var found = void 0; 225 | // todo - shallow equal test to prevent a render 226 | 227 | if (newProps.id === this.state.stack[0].id) { 228 | this.setState({ 229 | stack: [_extends({}, this.state.stack[0], newProps)].concat(_toConsumableArray(this.state.stack.slice(1))) 230 | }); 231 | } else if (found = this.state.stack.find(function (x) { 232 | return x.id === newProps.id; 233 | })) { 234 | this.setState({ 235 | stack: [_extends({}, found, { 236 | exitRef: this.createExitRef(newProps.id), 237 | enterRef: this.createEnterRef(newProps.id) 238 | })].concat(_toConsumableArray(this.state.stack.filter(function (x) { 239 | return x !== found; 240 | }))) 241 | }); 242 | } else { 243 | this.setState({ 244 | stack: [_extends({ 245 | exitRef: this.createExitRef(newProps.id), 246 | enterRef: this.createEnterRef(newProps.id) 247 | }, newProps)].concat(_toConsumableArray(this.state.stack)) 248 | }); 249 | } 250 | } 251 | }, { 252 | key: 'render', 253 | value: function render() { 254 | return _react2.default.createElement( 255 | _react.Fragment, 256 | null, 257 | this.state.stack.map(function (_ref5, i) { 258 | var id = _ref5.id, 259 | children = _ref5.children, 260 | enterRef = _ref5.enterRef, 261 | exitRef = _ref5.exitRef; 262 | return _react2.default.createElement( 263 | Freeze, 264 | { key: id, cold: i !== 0, ref: i !== 0 ? exitRef : enterRef }, 265 | children 266 | ); 267 | }) 268 | ); 269 | } 270 | }]); 271 | 272 | return Transition; 273 | }(_react.Component); 274 | 275 | // class ReactTransitionChild extends React.Component<{}, { 276 | // transition: 'entering' | 'entered' | 'exiting' | 'exited' 277 | // }>{ 278 | // state = { 279 | // transition: '' 280 | // } 281 | // render(){ 282 | // const {onEnter, onExit, } 283 | // return 291 | // } 292 | // } 293 | 294 | // 295 | // ## TransitionGroup 296 | // 297 | // ```jsx 298 | // ...} 300 | // onExit={async id => ...} 301 | // onMove={async id => ...} 302 | // > 303 | // 304 | // 305 | // 306 | // 307 | // 308 | // ``` 309 | 310 | 311 | exports.default = Transition; --------------------------------------------------------------------------------