├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── __tests__
├── ReactChildren-test.js
├── ReactClass-test.js
├── ReactClassMixin-test.js
├── ReactComponent-test.js
├── ReactComponentLifeCycle-test.js
├── ReactCompositeComponentState-test.js
├── ReactDOM-test.js
├── ReactDOMComponent-test.js
├── ReactES6Class-test.js
├── ReactElement-test.js
├── ReactElementClone-test.js
├── ReactEmptyComponent-test.js
├── ReactJSXElement-test.js
├── ReactMount-test.js
├── ReactMountDestruction-test.js
├── ReactMultiChild-test.js
├── ReactPureComponent-test.js
├── ReactStatelessComponent-test.js
├── SelectValueElement-test.js
├── findDOMNode-test.js
├── onlyChild-test.js
└── refs-destruction-test.js
├── addons
├── LinkedStateMixin.js
├── ReactCSSTransitionGroup.js
├── ReactCSSTransitionGroupChild.js
├── ReactChildren.js
├── ReactComponentWithPureRenderMixin.js
├── ReactFragment.js
├── ReactLink.js
├── ReactStateSetters.js
├── ReactTransitionChildMapping.js
├── ReactTransitionEvents.js
├── ReactTransitionGroup.js
├── ReactWithAddons.js
├── react-tap-event-plugin.js
├── renderSubtreeIntoContainer.js
├── shallowCompare.js
├── update.js
└── utils
│ ├── CSSCore.js
│ ├── PooledClass.js
│ ├── emptyFunction.js
│ ├── escapeTextContentForBrowser.js
│ ├── flattenChildren.js
│ ├── getIteratorFn.js
│ ├── keyOf.js
│ ├── quoteAttributeValueForBrowser.js
│ ├── shallowEqual.js
│ └── traverseAllChildren.js
├── build.js
├── dist
├── react-lite.common.js
├── react-lite.js
├── react-lite.min.js
└── react-lite.min.js.gz
├── examples
├── js-repaint-perf
│ ├── ENV.js
│ ├── ga.js
│ ├── react
│ │ ├── app-component.js
│ │ ├── app.js
│ │ ├── index.html
│ │ └── lite.html
│ ├── styles.css
│ └── vendor
│ │ ├── JSXTransformer.js
│ │ ├── bootstrap.min.css
│ │ ├── memory-stats.js
│ │ ├── monitor.js
│ │ ├── react-dom.min.js
│ │ ├── react-with-addons.js
│ │ ├── react-with-addons.min.js
│ │ ├── react.addons.12.2.2.js
│ │ └── react.min.js
└── simple
│ ├── app.js
│ ├── index.html
│ ├── index.js
│ ├── react.js
│ └── react.min.js
├── jest
└── preprocessor.js
├── package.json
├── src
├── CSSPropertyOperations.js
├── Children.js
├── Component.js
├── DOM.js
├── DOMConfig.js
├── DOMPropertyOperations.js
├── PropTypes.js
├── PureComponent.js
├── ReactDOM.js
├── constant.js
├── createClass.js
├── createElement.js
├── event-system.js
├── index.js
├── shallowEqual.js
├── util.js
└── virtual-dom.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | node_modules
4 | coverage
5 | _book
6 | lib
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | examples
4 | test
5 | coverage
6 | _book
7 | book.json
8 | docs
9 | src
10 | jest
11 | __tests__
12 | webpack.config.js
13 | build.js
14 | addons
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 | - "5"
5 | branches:
6 | only:
7 | - master
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 工业聚
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-lite
2 |
3 | [](https://travis-ci.org/Lucifier129/react-lite)
4 | [](https://www.npmjs.com/package/react-lite)
5 | [](https://gitter.im/Lucifier129/react-lite?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 |
7 | ## Introduction
8 | react-lite is an implementation of React that optimizes for small script size.
9 |
10 | Note: react-lite dose not support React v16.x now.
11 |
12 | ### Size Comparison
13 |
14 | | Framework | Version | Minified Size |
15 | |------------------------|------------|---------------|
16 | | Ember | 2.2.0 | 446kb |
17 | | Polymer | 1.0.6 | 183kb |
18 | | Angular | 1.4.8 | 148kb |
19 | | React | 0.14.3 | 136kb |
20 | | Web Components Polyfill| 0.7.19 | 118kb |
21 | | Riot | 2.3.11 | 20kb |
22 | | React-lite | 0.15.6 | 25kb |
23 | | preact + preact-compat | 8.2.1 | 5kb |
24 |
25 | React-lite supports the core APIs of React, such as Virtual DOM, intended as a drop-in
26 | replacement for React, when you don't need server-side rendering in browser(no `ReactDOM.renderToString` & `ReactDOM.renderToStaticMarkup`).
27 |
28 | ### Usage
29 | If you are using webpack, it's so easy to use react-lite, just [config alias](http://webpack.github.io/docs/configuration.html#resolve-alias) in webpack.config.js:
30 |
31 | ```javascript
32 | // webpack.config.js
33 | {
34 | resolve: {
35 | alias: {
36 | 'react': 'react-lite',
37 | 'react-dom': 'react-lite'
38 | }
39 | }
40 | }
41 | ```
42 |
43 | Note: feel free to try react-lite, if something happen and we can't fix it in time, then use [regular react](https://github.com/facebook/react) instead.
44 | ## Installation
45 |
46 | You can install react-lite from npm:
47 |
48 | ```shell
49 | npm install react-lite --save
50 | ```
51 |
52 | ## Browser compatibility
53 |
54 | supports IE9+ / ES5 enviroment
55 |
56 | ## Documentation
57 |
58 | learn react-lite from [React official documentation](https://reactjs.org)
59 |
60 | ## What can react-lite do?
61 |
62 | just the same as what react does, see some demos below(I just add the alias to webpack.config.js, no need to do anything else):
63 |
64 | - works with material-ui: [docs demo](https://lucifier129.github.io/material-ui/build)
65 | - works with react-bootstrap: [docs demo](http://react-lite-with-bootstrap.herokuapp.com/)
66 | - works with ant-design: [demo](http://lucifier129.github.io/ant-design/)
67 | - works with react-router: [examples](http://react-lite-with-react-router.coding.io/)
68 | - works with redux:
69 | * [async](http://lucifier129.github.io/redux-with-react-lite/async/index.html)
70 | * [counter](http://lucifier129.github.io/redux-with-react-lite/counter/index.html)
71 | * [shopping-cart](http://lucifier129.github.io/redux-with-react-lite/shopping-cart/index.html)
72 | * [todomvc](http://lucifier129.github.io/redux-with-react-lite/todomvc/index.html)
73 | * [todos-with-undo](http://lucifier129.github.io/redux-with-react-lite/todos-with-undo/index.html)
74 | - works with react-motion: [demos](http://lucifier129.github.io/react-motion-with-react-lite/index.html)
75 | - works with react-d3-components: [demos](http://lucifier129.github.io/react-d3-components-demos/)
76 | - works with react-d3: [demos](http://lucifier129.github.io/react-d3-demos/)
77 | - react-lite [vdom-benchmark](http://vdom-benchmark.github.io/vdom-benchmark/)
78 | - js-repaint-perf:
79 | * [react](http://lucifier129.github.io/react-lite-repaint-perf/react/index.html)
80 | * [react-lite](http://lucifier129.github.io/react-lite-repaint-perf/react/lite.html)
81 |
82 | ## React-lite vs React
83 |
84 | via react-lite:
85 | - all of React.PropTypes method is no-op(empty function)
86 | - use React in server side rendering, and use React-lite in browser
87 | * react-lite will replace the dom tree with new dom tree
88 | * you had better avoid `script|head|link` tag in client side
89 | - can not use react-dev-tool inspect react-lite, should switch to regular react for debugging
90 | - react-lite only works with a JSX toolchain([issue](https://github.com/Lucifier129/react-lite/issues/51))
91 | - unlike react, `event` object in react-lite is always persistent, and `event.persist` is set as no-op to avoid throwing error.
92 | - react-lite can't work with `react-tap-event-plugin`, please use `fastclick` instead. or add alias `'react-tap-event-plugin': 'react-lite/lib/react-tap-event-plugin'`, just like [here](https://github.com/Lucifier129/material-ui/blob/master/docs/webpack-production.config.js#L21)
93 | - can't work with `transform-react-inline-elements`, you will get a bundle include both `react` and `react-lite`.
94 | - `react-lite` just follow the best practice of `React`.
95 |
96 | ## Test
97 | react-lite reuses react's unitest(170), you can see them in `__test__`, and run the tests with:
98 |
99 | ```shell
100 | npm test
101 | ```
102 |
103 | License: MIT (See LICENSE file for details)
104 |
--------------------------------------------------------------------------------
/__tests__/ReactCompositeComponentState-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var mocks = {
15 | getMockFunction: function() {
16 | return jest.genMockFunction()
17 | }
18 | }
19 |
20 | var React;
21 | var ReactDOM;
22 |
23 | var TestComponent;
24 |
25 | describe('ReactCompositeComponent-state', function() {
26 |
27 | beforeEach(function() {
28 | React = require('../src');
29 | ReactDOM = require('../src');
30 |
31 | TestComponent = React.createClass({
32 | peekAtState: function(from, state) {
33 | state = state || this.state;
34 | this.props.stateListener(from, state && state.color);
35 | },
36 |
37 | peekAtCallback: function(from) {
38 | return () => this.peekAtState(from);
39 | },
40 |
41 | setFavoriteColor: function(nextColor) {
42 | this.setState(
43 | {color: nextColor},
44 | this.peekAtCallback('setFavoriteColor')
45 | );
46 | },
47 |
48 | getInitialState: function() {
49 | this.peekAtState('getInitialState');
50 | return {color: 'red'};
51 | },
52 |
53 | render: function() {
54 | this.peekAtState('render');
55 | return
{this.state.color}
;
56 | },
57 |
58 | componentWillMount: function() {
59 | this.peekAtState('componentWillMount-start');
60 | this.setState(function(state) {
61 | this.peekAtState('before-setState-sunrise', state);
62 | });
63 | this.setState(
64 | {color: 'sunrise'},
65 | this.peekAtCallback('setState-sunrise')
66 | );
67 | this.setState(function(state) {
68 | this.peekAtState('after-setState-sunrise', state);
69 | });
70 | this.peekAtState('componentWillMount-after-sunrise');
71 | this.setState(
72 | {color: 'orange'},
73 | this.peekAtCallback('setState-orange')
74 | );
75 | this.setState(function(state) {
76 | this.peekAtState('after-setState-orange', state);
77 | });
78 | this.peekAtState('componentWillMount-end');
79 | },
80 |
81 | componentDidMount: function() {
82 | this.peekAtState('componentDidMount-start');
83 | this.setState(
84 | {color: 'yellow'},
85 | this.peekAtCallback('setState-yellow')
86 | );
87 | this.peekAtState('componentDidMount-end');
88 | },
89 |
90 | componentWillReceiveProps: function(newProps) {
91 | this.peekAtState('componentWillReceiveProps-start');
92 | if (newProps.nextColor) {
93 | this.setState(function(state) {
94 | this.peekAtState('before-setState-receiveProps', state);
95 | return {color: newProps.nextColor};
96 | });
97 | this.replaceState({color: undefined});
98 | this.setState(
99 | function(state) {
100 | this.peekAtState('before-setState-again-receiveProps', state);
101 | return {color: newProps.nextColor};
102 | },
103 | this.peekAtCallback('setState-receiveProps')
104 | );
105 | this.setState(function(state) {
106 | this.peekAtState('after-setState-receiveProps', state);
107 | });
108 | }
109 | this.peekAtState('componentWillReceiveProps-end');
110 | },
111 |
112 | shouldComponentUpdate: function(nextProps, nextState) {
113 | this.peekAtState('shouldComponentUpdate-currentState');
114 | this.peekAtState('shouldComponentUpdate-nextState', nextState);
115 | return true;
116 | },
117 |
118 | componentWillUpdate: function(nextProps, nextState) {
119 | this.peekAtState('componentWillUpdate-currentState');
120 | this.peekAtState('componentWillUpdate-nextState', nextState);
121 | },
122 |
123 | componentDidUpdate: function(prevProps, prevState) {
124 | this.peekAtState('componentDidUpdate-currentState');
125 | this.peekAtState('componentDidUpdate-prevState', prevState);
126 | },
127 |
128 | componentWillUnmount: function() {
129 | this.peekAtState('componentWillUnmount');
130 | },
131 | });
132 | });
133 |
134 | it('should support setting state', function() {
135 | var container = document.createElement('div');
136 | document.body.appendChild(container);
137 |
138 | var stateListener = mocks.getMockFunction();
139 | var instance = ReactDOM.render(
140 | ,
141 | container,
142 | function peekAtInitialCallback() {
143 | this.peekAtState('initial-callback');
144 | }
145 | );
146 | ReactDOM.render(
147 | ,
148 | container,
149 | instance.peekAtCallback('setProps')
150 | );
151 | instance.setFavoriteColor('blue');
152 | instance.forceUpdate(instance.peekAtCallback('forceUpdate'));
153 |
154 | ReactDOM.unmountComponentAtNode(container);
155 |
156 | expect(stateListener.mock.calls.join('\n')).toEqual([
157 | // there is no state when getInitialState() is called
158 | ['getInitialState', null],
159 | ['componentWillMount-start', 'red'],
160 | // setState()'s only enqueue pending states.
161 | ['componentWillMount-after-sunrise', 'red'],
162 | ['componentWillMount-end', 'red'],
163 | // pending state queue is processed
164 | ['before-setState-sunrise', 'red'],
165 | ['after-setState-sunrise', 'sunrise'],
166 | ['after-setState-orange', 'orange'],
167 | // pending state has been applied
168 | ['render', 'orange'],
169 | ['componentDidMount-start', 'orange'],
170 | // setState-sunrise and setState-orange should be called here,
171 | // after the bug in #1740
172 | // componentDidMount() called setState({color:'yellow'}), which is async.
173 | // The update doesn't happen until the next flush.
174 | ['componentDidMount-end', 'orange'],
175 | ['shouldComponentUpdate-currentState', 'orange'],
176 | ['shouldComponentUpdate-nextState', 'yellow'],
177 | ['componentWillUpdate-currentState', 'orange'],
178 | ['componentWillUpdate-nextState', 'yellow'],
179 | ['render', 'yellow'],
180 | ['componentDidUpdate-currentState', 'yellow'],
181 | ['componentDidUpdate-prevState', 'orange'],
182 | ['setState-sunrise', 'yellow'],
183 | ['setState-orange', 'yellow'],
184 | ['setState-yellow', 'yellow'],
185 | ['initial-callback', 'yellow'],
186 | ['componentWillReceiveProps-start', 'yellow'],
187 | // setState({color:'green'}) only enqueues a pending state.
188 | ['componentWillReceiveProps-end', 'yellow'],
189 | // pending state queue is processed
190 | // before-setState-receiveProps never called, due to replaceState.
191 | ['before-setState-again-receiveProps', undefined],
192 | ['after-setState-receiveProps', 'green'],
193 | ['shouldComponentUpdate-currentState', 'yellow'],
194 | ['shouldComponentUpdate-nextState', 'green'],
195 | ['componentWillUpdate-currentState', 'yellow'],
196 | ['componentWillUpdate-nextState', 'green'],
197 | ['render', 'green'],
198 | ['componentDidUpdate-currentState', 'green'],
199 | ['componentDidUpdate-prevState', 'yellow'],
200 | ['setState-receiveProps', 'green'],
201 | ['setProps', 'green'],
202 | // setFavoriteColor('blue')
203 | ['shouldComponentUpdate-currentState', 'green'],
204 | ['shouldComponentUpdate-nextState', 'blue'],
205 | ['componentWillUpdate-currentState', 'green'],
206 | ['componentWillUpdate-nextState', 'blue'],
207 | ['render', 'blue'],
208 | ['componentDidUpdate-currentState', 'blue'],
209 | ['componentDidUpdate-prevState', 'green'],
210 | ['setFavoriteColor', 'blue'],
211 | // forceUpdate()
212 | ['componentWillUpdate-currentState', 'blue'],
213 | ['componentWillUpdate-nextState', 'blue'],
214 | ['render', 'blue'],
215 | ['componentDidUpdate-currentState', 'blue'],
216 | ['componentDidUpdate-prevState', 'blue'],
217 | ['forceUpdate', 'blue'],
218 | // unmountComponent()
219 | // state is available within `componentWillUnmount()`
220 | ['componentWillUnmount', 'blue'],
221 | ].join('\n'));
222 | });
223 |
224 | it('should batch unmounts', function() {
225 | var outer;
226 | var Inner = React.createClass({
227 | render: function() {
228 | return
;
229 | },
230 | componentWillUnmount: function() {
231 | // This should get silently ignored (maybe with a warning), but it
232 | // shouldn't break React.
233 | outer.setState({showInner: false});
234 | },
235 | });
236 | var Outer = React.createClass({
237 | getInitialState: function() {
238 | return {showInner: true};
239 | },
240 | render: function() {
241 | return {this.state.showInner && }
;
242 | },
243 | });
244 |
245 | var container = document.createElement('div');
246 | outer = ReactDOM.render( , container);
247 | expect(() => {
248 | ReactDOM.unmountComponentAtNode(container);
249 | }).not.toThrow();
250 | });
251 | });
252 |
--------------------------------------------------------------------------------
/__tests__/ReactDOM-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 |
14 | jest.dontMock('../src');
15 | var React = require('../src');
16 | var ReactDOM = require('../src');
17 | var ReactTestUtils = {
18 | renderIntoDocument: function(instance) {
19 | var div = document.createElement('div');
20 | // None of our tests actually require attaching the container to the
21 | // DOM, and doing so creates a mess that we rely on test isolation to
22 | // clean up, so we're going to stop honoring the name of this method
23 | // (and probably rename it eventually) if no problems arise.
24 | // document.documentElement.appendChild(div);
25 | return ReactDOM.render(instance, div);
26 | }
27 | };
28 | var div = React.createFactory('div');
29 |
30 | describe('ReactDOM', function() {
31 | // TODO: uncomment this test once we can run in phantom, which
32 | // supports real submit events.
33 | /*
34 | it('should bubble onSubmit', function() {
35 | var count = 0;
36 | var form;
37 | var Parent = React.createClass({
38 | handleSubmit: function() {
39 | count++;
40 | return false;
41 | },
42 | render: function() {
43 | return ;
44 | }
45 | });
46 | var Child = React.createClass({
47 | render: function() {
48 | return ;
49 | },
50 | componentDidMount: function() {
51 | form = ReactDOM.findDOMNode(this);
52 | }
53 | });
54 | var instance = ReactTestUtils.renderIntoDocument( );
55 | form.submit();
56 | expect(count).toEqual(1);
57 | });
58 | */
59 |
60 | it('allows a DOM element to be used with a string', function() {
61 | var element = React.createElement('div', {className: 'foo'});
62 | var instance = ReactTestUtils.renderIntoDocument(element);
63 | expect(ReactDOM.findDOMNode(instance).tagName).toBe('DIV');
64 | });
65 |
66 | it('should allow children to be passed as an argument', function() {
67 | var argDiv = ReactTestUtils.renderIntoDocument(
68 | div(null, 'child')
69 | );
70 | var argNode = ReactDOM.findDOMNode(argDiv);
71 | expect(argNode.innerHTML).toBe('child');
72 | });
73 |
74 | it('should overwrite props.children with children argument', function() {
75 | var conflictDiv = ReactTestUtils.renderIntoDocument(
76 | div({children: 'fakechild'}, 'child')
77 | );
78 | var conflictNode = ReactDOM.findDOMNode(conflictDiv);
79 | expect(conflictNode.innerHTML).toBe('child');
80 | });
81 |
82 | /**
83 | * We need to make sure that updates occur to the actual node that's in the
84 | * DOM, instead of a stale cache.
85 | */
86 | it('should purge the DOM cache when removing nodes', function() {
87 | var myDiv = ReactTestUtils.renderIntoDocument(
88 |
92 | );
93 | // Warm the cache with theDog
94 | myDiv = ReactTestUtils.renderIntoDocument(
95 |
99 | );
100 | // Remove theDog - this should purge the cache
101 | myDiv = ReactTestUtils.renderIntoDocument(
102 |
105 | );
106 | // Now, put theDog back. It's now a different DOM node.
107 | myDiv = ReactTestUtils.renderIntoDocument(
108 |
112 | );
113 | // Change the className of theDog. It will use the same element
114 | myDiv = ReactTestUtils.renderIntoDocument(
115 |
119 | );
120 | var root = ReactDOM.findDOMNode(myDiv);
121 | var dog = root.childNodes[0];
122 | expect(dog.className).toBe('bigdog');
123 | });
124 |
125 | it('allow React.DOM factories to be called without warnings', function() {
126 | spyOn(console, 'error');
127 | var element = div();
128 | expect(element.type).toBe('div');
129 | expect(console.error.argsForCall.length).toBe(0);
130 | });
131 |
132 | });
133 |
--------------------------------------------------------------------------------
/__tests__/ReactElementClone-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 |
14 | jest.dontMock('../src');
15 | var React;
16 | var ReactDOM;
17 | var ReactTestUtils;
18 |
19 | React = require('../src');
20 | ReactDOM = require('../src');
21 | ReactTestUtils = ReactTestUtils = {
22 | renderIntoDocument: function(instance) {
23 | var div = document.createElement('div');
24 | // None of our tests actually require attaching the container to the
25 | // DOM, and doing so creates a mess that we rely on test isolation to
26 | // clean up, so we're going to stop honoring the name of this method
27 | // (and probably rename it eventually) if no problems arise.
28 | // document.documentElement.appendChild(div);
29 | return ReactDOM.render(instance, div);
30 | }
31 | }
32 |
33 | describe('ReactElementClone', function() {
34 |
35 | beforeEach(function() {
36 | });
37 |
38 | it('should clone a DOM component with new props', function() {
39 | var Grandparent = React.createClass({
40 | render: function() {
41 | return } />;
42 | },
43 | });
44 | var Parent = React.createClass({
45 | render: function() {
46 | return (
47 |
48 | {React.cloneElement(this.props.child, { className: 'xyz' })}
49 |
50 | );
51 | },
52 | });
53 | var component = ReactTestUtils.renderIntoDocument( );
54 | expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz');
55 | });
56 |
57 | it('should clone a composite component with new props', function() {
58 | var Child = React.createClass({
59 | render: function() {
60 | return
;
61 | },
62 | });
63 | var Grandparent = React.createClass({
64 | render: function() {
65 | return } />;
66 | },
67 | });
68 | var Parent = React.createClass({
69 | render: function() {
70 | return (
71 |
72 | {React.cloneElement(this.props.child, { className: 'xyz' })}
73 |
74 | );
75 | },
76 | });
77 | var component = ReactTestUtils.renderIntoDocument( );
78 | expect(ReactDOM.findDOMNode(component).childNodes[0].className).toBe('xyz');
79 | });
80 |
81 | it('should keep the original ref if it is not overridden', function() {
82 | var Grandparent = React.createClass({
83 | render: function() {
84 | return } />;
85 | },
86 | });
87 |
88 | var Parent = React.createClass({
89 | render: function() {
90 | return (
91 |
92 | {React.cloneElement(this.props.child, { className: 'xyz' })}
93 |
94 | );
95 | },
96 | });
97 |
98 | var component = ReactTestUtils.renderIntoDocument( );
99 | expect(component.refs.yolo.tagName).toBe('DIV');
100 | });
101 |
102 | it('should transfer the key property', function() {
103 | var Component = React.createClass({
104 | render: function() {
105 | return null;
106 | },
107 | });
108 | var clone = React.cloneElement( , {key: 'xyz'});
109 | expect(clone.key).toBe('xyz');
110 | });
111 |
112 | it('should transfer children', function() {
113 | var Component = React.createClass({
114 | render: function() {
115 | expect(this.props.children).toBe('xyz');
116 | return
;
117 | },
118 | });
119 |
120 | ReactTestUtils.renderIntoDocument(
121 | React.cloneElement( , {children: 'xyz'})
122 | );
123 | });
124 |
125 | it('should shallow clone children', function() {
126 | var Component = React.createClass({
127 | render: function() {
128 | expect(this.props.children).toBe('xyz');
129 | return
;
130 | },
131 | });
132 |
133 | ReactTestUtils.renderIntoDocument(
134 | React.cloneElement(xyz , {})
135 | );
136 | });
137 |
138 | it('should accept children as rest arguments', function() {
139 | var Component = React.createClass({
140 | render: function() {
141 | return null;
142 | },
143 | });
144 |
145 | var clone = React.cloneElement(
146 | xyz ,
147 | { children: },
148 |
,
149 |
150 | );
151 |
152 | expect(clone.props.children).toEqual([
153 |
,
154 | ,
155 | ]);
156 | });
157 |
158 | it('should support keys and refs', function() {
159 | var Parent = React.createClass({
160 | render: function() {
161 | var clone =
162 | React.cloneElement(this.props.children, {key: 'xyz', ref: 'xyz'});
163 | expect(clone.key).toBe('xyz');
164 | expect(clone.ref).toBe('xyz');
165 | return {clone}
;
166 | },
167 | });
168 |
169 | var Grandparent = React.createClass({
170 | render: function() {
171 | return ;
172 | },
173 | });
174 |
175 | var component = ReactTestUtils.renderIntoDocument( );
176 | expect(component.refs.parent.refs.xyz.tagName).toBe('SPAN');
177 | });
178 |
179 | it('should steal the ref if a new ref is specified', function() {
180 | var Parent = React.createClass({
181 | render: function() {
182 | var clone = React.cloneElement(this.props.children, {ref: 'xyz'});
183 | return {clone}
;
184 | },
185 | });
186 |
187 | var Grandparent = React.createClass({
188 | render: function() {
189 | return ;
190 | },
191 | });
192 |
193 | var component = ReactTestUtils.renderIntoDocument( );
194 | expect(component.refs.child).toBeUndefined();
195 | expect(component.refs.parent.refs.xyz.tagName).toBe('SPAN');
196 | });
197 |
198 | it('should overwrite props', function() {
199 | var Component = React.createClass({
200 | render: function() {
201 | expect(this.props.myprop).toBe('xyz');
202 | return
;
203 | },
204 | });
205 |
206 | ReactTestUtils.renderIntoDocument(
207 | React.cloneElement( , {myprop: 'xyz'})
208 | );
209 | });
210 |
211 | // it('warns for keys for arrays of elements in rest args', function() {
212 | // spyOn(console, 'error');
213 |
214 | // React.cloneElement(
, null, [
,
]);
215 |
216 | // expect(console.error.argsForCall.length).toBe(1);
217 | // expect(console.error.argsForCall[0][0]).toContain(
218 | // 'Each child in an array or iterator should have a unique "key" prop.'
219 | // );
220 | // });
221 |
222 | // it('does not warns for arrays of elements with keys', function() {
223 | // spyOn(console, 'error');
224 |
225 | // React.cloneElement(
, null, [
,
]);
226 |
227 | // expect(console.error.argsForCall.length).toBe(0);
228 | // });
229 |
230 | // it('does not warn when the element is directly in rest args', function() {
231 | // spyOn(console, 'error');
232 |
233 | // React.cloneElement(
, null,
,
);
234 |
235 | // expect(console.error.argsForCall.length).toBe(0);
236 | // });
237 |
238 | // it('does not warn when the array contains a non-element', function() {
239 | // spyOn(console, 'error');
240 |
241 | // React.cloneElement(
, null, [{}, {}]);
242 |
243 | // expect(console.error.argsForCall.length).toBe(0);
244 | // });
245 |
246 | // it('should check declared prop types after clone', function() {
247 | // spyOn(console, 'error');
248 | // var Component = React.createClass({
249 | // propTypes: {
250 | // color: React.PropTypes.string.isRequired,
251 | // },
252 | // render: function() {
253 | // return React.createElement('div', null, 'My color is ' + this.color);
254 | // },
255 | // });
256 | // var Parent = React.createClass({
257 | // render: function() {
258 | // return React.cloneElement(this.props.child, {color: 123});
259 | // },
260 | // });
261 | // var GrandParent = React.createClass({
262 | // render: function() {
263 | // return React.createElement(
264 | // Parent,
265 | // { child: React.createElement(Component, {color: 'red'}) }
266 | // );
267 | // },
268 | // });
269 | // ReactTestUtils.renderIntoDocument(React.createElement(GrandParent));
270 | // expect(console.error.argsForCall.length).toBe(1);
271 | // expect(console.error.calls[0].args[0]).toBe(
272 | // 'Warning: Failed propType: ' +
273 | // 'Invalid prop `color` of type `number` supplied to `Component`, ' +
274 | // 'expected `string`. Check the render method of `Parent`.'
275 | // );
276 | // });
277 |
278 | });
279 |
--------------------------------------------------------------------------------
/__tests__/ReactJSXElement-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React;
15 | var ReactDOM;
16 | var ReactTestUtils;
17 |
18 | React = require('../src');
19 | ReactDOM = require('../src');
20 | ReactTestUtils = ReactTestUtils = {
21 | renderIntoDocument: function(instance) {
22 | var div = document.createElement('div');
23 | // None of our tests actually require attaching the container to the
24 | // DOM, and doing so creates a mess that we rely on test isolation to
25 | // clean up, so we're going to stop honoring the name of this method
26 | // (and probably rename it eventually) if no problems arise.
27 | // document.documentElement.appendChild(div);
28 | return ReactDOM.render(instance, div);
29 | }
30 | }
31 | describe('ReactJSXElement', function() {
32 | var Component;
33 |
34 | beforeEach(function() {
35 | Component = class extends React.Component {
36 | render() {
37 | return
;
38 | }
39 | };
40 | });
41 |
42 | it('returns a complete element according to spec', function() {
43 | var element = ;
44 | expect(element.type).toBe(Component);
45 | expect(element.key).toBe(null);
46 | expect(element.ref).toBe(null);
47 | var expectation = {};
48 | Object.freeze(expectation);
49 | expect(element.props).toEqual(expectation);
50 | });
51 |
52 | it('allows a lower-case to be passed as the string type', function() {
53 | var element =
;
54 | expect(element.type).toBe('div');
55 | expect(element.key).toBe(null);
56 | expect(element.ref).toBe(null);
57 | var expectation = {};
58 | Object.freeze(expectation);
59 | expect(element.props).toEqual(expectation);
60 | });
61 |
62 | it('allows a string to be passed as the type', function() {
63 | var TagName = 'div';
64 | var element = ;
65 | expect(element.type).toBe('div');
66 | expect(element.key).toBe(null);
67 | expect(element.ref).toBe(null);
68 | var expectation = {};
69 | Object.freeze(expectation);
70 | expect(element.props).toEqual(expectation);
71 | });
72 |
73 | // it('returns an immutable element', function() {
74 | // var element = ;
75 | // expect(() => element.type = 'div').toThrow();
76 | // });
77 |
78 | it('does not reuse the object that is spread into props', function() {
79 | var config = {foo: 1};
80 | var element = ;
81 | expect(element.props.foo).toBe(1);
82 | config.foo = 2;
83 | expect(element.props.foo).toBe(1);
84 | });
85 |
86 | // it('extracts key and ref from the rest of the props', function() {
87 | // var element = ;
88 | // expect(element.type).toBe(Component);
89 | // expect(element.key).toBe('12');
90 | // expect(element.ref).toBe('34');
91 | // var expectation = {foo:'56'};
92 | // Object.freeze(expectation);
93 | // expect(element.props).toEqual(expectation);
94 | // });
95 |
96 | // it('coerces the key to a string', function() {
97 | // var element = ;
98 | // expect(element.type).toBe(Component);
99 | // expect(element.key).toBe('12');
100 | // expect(element.ref).toBe(null);
101 | // var expectation = {foo:'56'};
102 | // Object.freeze(expectation);
103 | // expect(element.props).toEqual(expectation);
104 | // });
105 |
106 | // it('merges JSX children onto the children prop', function() {
107 | // spyOn(console, 'error');
108 | // var a = 1;
109 | // var element = {a} ;
110 | // expect(element.props.children).toBe(a);
111 | // expect(console.error.argsForCall.length).toBe(0);
112 | // });
113 |
114 | it('does not override children if no JSX children are provided', function() {
115 | spyOn(console, 'error');
116 | var element = ;
117 | expect(element.props.children).toBe('text');
118 | expect(console.error.argsForCall.length).toBe(0);
119 | });
120 |
121 | // it('overrides children if null is provided as a JSX child', function() {
122 | // spyOn(console, 'error');
123 | // var element = {null} ;
124 | // expect(element.props.children).toBe(null);
125 | // expect(console.error.argsForCall.length).toBe(0);
126 | // });
127 |
128 | // it('merges JSX children onto the children prop in an array', function() {
129 | // spyOn(console, 'error');
130 | // var a = 1;
131 | // var b = 2;
132 | // var c = 3;
133 | // var element = {a}{b}{c} ;
134 | // expect(element.props.children).toEqual([1, 2, 3]);
135 | // expect(console.error.argsForCall.length).toBe(0);
136 | // });
137 |
138 | it('allows static methods to be called using the type property', function() {
139 | spyOn(console, 'error');
140 |
141 | class StaticMethodComponent {
142 | static someStaticMethod() {
143 | return 'someReturnValue';
144 | }
145 | render() {
146 | return
;
147 | }
148 | }
149 |
150 | var element = ;
151 | expect(element.type.someStaticMethod()).toBe('someReturnValue');
152 | expect(console.error.argsForCall.length).toBe(0);
153 | });
154 |
155 | it('identifies valid elements', function() {
156 | expect(React.isValidElement(
)).toEqual(true);
157 | expect(React.isValidElement( )).toEqual(true);
158 |
159 | expect(React.isValidElement(null)).toEqual(false);
160 | expect(React.isValidElement(true)).toEqual(false);
161 | expect(React.isValidElement({})).toEqual(false);
162 | expect(React.isValidElement('string')).toEqual(false);
163 | expect(React.isValidElement(Component)).toEqual(false);
164 | expect(React.isValidElement({ type: 'div', props: {} })).toEqual(false);
165 | });
166 |
167 | // it('is indistinguishable from a plain object', function() {
168 | // var element =
;
169 | // var object = {};
170 | // expect(element.constructor).toBe(object.constructor);
171 | // });
172 |
173 | it('should use default prop value when removing a prop', function() {
174 | Component.defaultProps = {fruit: 'persimmon'};
175 |
176 | var container = document.createElement('div');
177 | var instance = ReactDOM.render(
178 | ,
179 | container
180 | );
181 | expect(instance.props.fruit).toBe('mango');
182 |
183 | ReactDOM.render( , container);
184 | expect(instance.props.fruit).toBe('persimmon');
185 | });
186 |
187 | it('should normalize props with default values', function() {
188 | class NormalizingComponent extends React.Component {
189 | render() {
190 | return {this.props.prop} ;
191 | }
192 | }
193 | NormalizingComponent.defaultProps = {prop: 'testKey'};
194 |
195 | var instance = ReactTestUtils.renderIntoDocument( );
196 | expect(instance.props.prop).toBe('testKey');
197 |
198 | var inst2 =
199 | ReactTestUtils.renderIntoDocument( );
200 | expect(inst2.props.prop).toBe(null);
201 | });
202 |
203 | });
204 |
--------------------------------------------------------------------------------
/__tests__/ReactMountDestruction-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 |
14 | jest.dontMock('../src');
15 | var React = require('../src');
16 | var ReactDOM = require('../src');
17 |
18 | describe('ReactMount', function() {
19 | it('should destroy a react root upon request', function() {
20 | var mainContainerDiv = document.createElement('div');
21 | document.body.appendChild(mainContainerDiv);
22 |
23 | var instanceOne =
;
24 | var firstRootDiv = document.createElement('div');
25 | mainContainerDiv.appendChild(firstRootDiv);
26 | ReactDOM.render(instanceOne, firstRootDiv);
27 |
28 | var instanceTwo =
;
29 | var secondRootDiv = document.createElement('div');
30 | mainContainerDiv.appendChild(secondRootDiv);
31 | ReactDOM.render(instanceTwo, secondRootDiv);
32 |
33 | // Test that two react roots are rendered in isolation
34 | expect(firstRootDiv.firstChild.className).toBe('firstReactDiv');
35 | expect(secondRootDiv.firstChild.className).toBe('secondReactDiv');
36 |
37 | // Test that after unmounting each, they are no longer in the document.
38 | ReactDOM.unmountComponentAtNode(firstRootDiv);
39 | expect(firstRootDiv.firstChild).toBeNull();
40 | ReactDOM.unmountComponentAtNode(secondRootDiv);
41 | expect(secondRootDiv.firstChild).toBeNull();
42 | });
43 |
44 | // it('should warn when unmounting a non-container root node', function() {
45 | // var mainContainerDiv = document.createElement('div');
46 |
47 | // var component =
48 | // ;
51 | // ReactDOM.render(component, mainContainerDiv);
52 |
53 | // // Test that unmounting at a root node gives a helpful warning
54 | // var rootDiv = mainContainerDiv.firstChild;
55 | // spyOn(console, 'error');
56 | // ReactDOM.unmountComponentAtNode(rootDiv);
57 | // expect(console.error.callCount).toBe(1);
58 | // expect(console.error.mostRecentCall.args[0]).toBe(
59 | // 'Warning: unmountComponentAtNode(): The node you\'re attempting to ' +
60 | // 'unmount was rendered by React and is not a top-level container. You ' +
61 | // 'may have accidentally passed in a React root node instead of its ' +
62 | // 'container.'
63 | // );
64 | // });
65 |
66 | // it('should warn when unmounting a non-container, non-root node', function() {
67 | // var mainContainerDiv = document.createElement('div');
68 |
69 | // var component =
70 | // ;
75 | // ReactDOM.render(component, mainContainerDiv);
76 |
77 | // // Test that unmounting at a non-root node gives a different warning
78 | // var nonRootDiv = mainContainerDiv.firstChild.firstChild;
79 | // spyOn(console, 'error');
80 | // ReactDOM.unmountComponentAtNode(nonRootDiv);
81 | // expect(console.error.callCount).toBe(1);
82 | // expect(console.error.mostRecentCall.args[0]).toBe(
83 | // 'Warning: unmountComponentAtNode(): The node you\'re attempting to ' +
84 | // 'unmount was rendered by React and is not a top-level container. ' +
85 | // 'Instead, have the parent component update its state and rerender in ' +
86 | // 'order to remove this component.'
87 | // );
88 | // });
89 | });
90 |
--------------------------------------------------------------------------------
/__tests__/ReactMultiChild-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var mocks = {
15 | getMockFunction: function() {
16 | return jest.genMockFunction()
17 | }
18 | }
19 |
20 | describe('ReactMultiChild', function() {
21 | var React;
22 |
23 | var ReactDOM;
24 |
25 | beforeEach(function() {
26 | React = require('../src');
27 | ReactDOM = require('../src');
28 | });
29 |
30 | describe('reconciliation', function() {
31 | it('should update children when possible', function() {
32 | var container = document.createElement('div');
33 |
34 | var mockMount = mocks.getMockFunction();
35 | var mockUpdate = mocks.getMockFunction();
36 | var mockUnmount = mocks.getMockFunction();
37 |
38 | var MockComponent = React.createClass({
39 | componentDidMount: mockMount,
40 | componentDidUpdate: mockUpdate,
41 | componentWillUnmount: mockUnmount,
42 | render: function() {
43 | return ;
44 | },
45 | });
46 |
47 | expect(mockMount.mock.calls.length).toBe(0);
48 | expect(mockUpdate.mock.calls.length).toBe(0);
49 | expect(mockUnmount.mock.calls.length).toBe(0);
50 |
51 | ReactDOM.render(
, container);
52 |
53 | expect(mockMount.mock.calls.length).toBe(1);
54 | expect(mockUpdate.mock.calls.length).toBe(0);
55 | expect(mockUnmount.mock.calls.length).toBe(0);
56 |
57 | ReactDOM.render(
, container);
58 |
59 | expect(mockMount.mock.calls.length).toBe(1);
60 | expect(mockUpdate.mock.calls.length).toBe(1);
61 | expect(mockUnmount.mock.calls.length).toBe(0);
62 | });
63 |
64 | it('should replace children with different constructors', function() {
65 | var container = document.createElement('div');
66 |
67 | var mockMount = mocks.getMockFunction();
68 | var mockUnmount = mocks.getMockFunction();
69 |
70 | var MockComponent = React.createClass({
71 | componentDidMount: mockMount,
72 | componentWillUnmount: mockUnmount,
73 | render: function() {
74 | return ;
75 | },
76 | });
77 |
78 | expect(mockMount.mock.calls.length).toBe(0);
79 | expect(mockUnmount.mock.calls.length).toBe(0);
80 |
81 | ReactDOM.render(
, container);
82 |
83 | expect(mockMount.mock.calls.length).toBe(1);
84 | expect(mockUnmount.mock.calls.length).toBe(0);
85 |
86 | ReactDOM.render(
, container);
87 |
88 | expect(mockMount.mock.calls.length).toBe(1);
89 | expect(mockUnmount.mock.calls.length).toBe(1);
90 | });
91 |
92 | it('should NOT replace children with different owners', function() {
93 | var container = document.createElement('div');
94 |
95 | var mockMount = mocks.getMockFunction();
96 | var mockUnmount = mocks.getMockFunction();
97 |
98 | var MockComponent = React.createClass({
99 | componentDidMount: mockMount,
100 | componentWillUnmount: mockUnmount,
101 | render: function() {
102 | return ;
103 | },
104 | });
105 |
106 | var WrapperComponent = React.createClass({
107 | render: function() {
108 | return this.props.children || ;
109 | },
110 | });
111 |
112 | expect(mockMount.mock.calls.length).toBe(0);
113 | expect(mockUnmount.mock.calls.length).toBe(0);
114 |
115 | ReactDOM.render( , container);
116 |
117 | expect(mockMount.mock.calls.length).toBe(1);
118 | expect(mockUnmount.mock.calls.length).toBe(0);
119 |
120 | ReactDOM.render(
121 | ,
122 | container
123 | );
124 |
125 | expect(mockMount.mock.calls.length).toBe(1);
126 | expect(mockUnmount.mock.calls.length).toBe(0);
127 | });
128 |
129 | it('should replace children with different keys', function() {
130 | var container = document.createElement('div');
131 |
132 | var mockMount = mocks.getMockFunction();
133 | var mockUnmount = mocks.getMockFunction();
134 |
135 | var MockComponent = React.createClass({
136 | componentDidMount: mockMount,
137 | componentWillUnmount: mockUnmount,
138 | render: function() {
139 | return ;
140 | },
141 | });
142 |
143 | expect(mockMount.mock.calls.length).toBe(0);
144 | expect(mockUnmount.mock.calls.length).toBe(0);
145 |
146 | ReactDOM.render(
, container);
147 |
148 | expect(mockMount.mock.calls.length).toBe(1);
149 | expect(mockUnmount.mock.calls.length).toBe(0);
150 |
151 | ReactDOM.render(
, container);
152 |
153 | expect(mockMount.mock.calls.length).toBe(2);
154 | expect(mockUnmount.mock.calls.length).toBe(1);
155 | });
156 | });
157 |
158 | // describe('innerHTML', function() {
159 | // var setInnerHTML;
160 |
161 | // // Only run this suite if `Element.prototype.innerHTML` can be spied on.
162 | // var innerHTMLDescriptor = Object.getOwnPropertyDescriptor(
163 | // Element.prototype,
164 | // 'innerHTML'
165 | // );
166 | // if (!innerHTMLDescriptor) {
167 | // return;
168 | // }
169 |
170 | // beforeEach(function() {
171 | // var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
172 | // ReactDOMFeatureFlags.useCreateElement = false;
173 |
174 | // Object.defineProperty(Element.prototype, 'innerHTML', {
175 | // set: setInnerHTML = jasmine.createSpy().andCallFake(
176 | // innerHTMLDescriptor.set
177 | // ),
178 | // });
179 | // });
180 |
181 | // it('should only set `innerHTML` once on update', function() {
182 | // var container = document.createElement('div');
183 |
184 | // ReactDOM.render(
185 | //
186 | //
187 | //
188 | //
189 | //
,
190 | // container
191 | // );
192 | // // Warm the cache used by `getMarkupWrap`.
193 | // ReactDOM.render(
194 | //
195 | //
196 | //
197 | //
198 | //
,
199 | // container
200 | // );
201 | // expect(setInnerHTML).toHaveBeenCalled();
202 | // var callCountOnMount = setInnerHTML.calls.length;
203 |
204 | // ReactDOM.render(
205 | //
206 | //
207 | //
208 | //
209 | //
,
210 | // container
211 | // );
212 | // expect(setInnerHTML.calls.length).toBe(callCountOnMount + 1);
213 | // });
214 | // });
215 | });
216 |
--------------------------------------------------------------------------------
/__tests__/ReactPureComponent-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React = require('../src');
15 | var ReactDOM = require('../src');
16 |
17 | describe('ReactPureComponent', function() {
18 | beforeEach(function() {
19 | // React = require('React');
20 | // ReactDOM = require('ReactDOM');
21 | });
22 |
23 | it('should render', function() {
24 | var renders = 0;
25 | class Component extends React.PureComponent {
26 | constructor() {
27 | super();
28 | this.state = {type: 'mushrooms'};
29 | }
30 | render() {
31 | renders++;
32 | return {this.props.text[0]}
;
33 | }
34 | }
35 |
36 | var container = document.createElement('div');
37 | var text;
38 | var component;
39 |
40 | text = ['porcini'];
41 | component = ReactDOM.render( , container);
42 | expect(container.textContent).toBe('porcini');
43 | expect(renders).toBe(1);
44 |
45 | text = ['morel'];
46 | component = ReactDOM.render( , container);
47 | expect(container.textContent).toBe('morel');
48 | expect(renders).toBe(2);
49 |
50 | text[0] = 'portobello';
51 | component = ReactDOM.render( , container);
52 | expect(container.textContent).toBe('morel');
53 | expect(renders).toBe(2);
54 |
55 | // Setting state without changing it doesn't cause a rerender.
56 | component.setState({type: 'mushrooms'});
57 | expect(container.textContent).toBe('morel');
58 | expect(renders).toBe(2);
59 |
60 | // But changing state does.
61 | component.setState({type: 'portobello mushrooms'});
62 | expect(container.textContent).toBe('portobello');
63 | expect(renders).toBe(3);
64 | });
65 |
66 | it('can override shouldComponentUpdate', function() {
67 | var renders = 0;
68 | class Component extends React.PureComponent {
69 | render() {
70 | renders++;
71 | return
;
72 | }
73 | shouldComponentUpdate() {
74 | return true;
75 | }
76 | }
77 | var container = document.createElement('div');
78 | ReactDOM.render( , container);
79 | ReactDOM.render( , container);
80 | expect(renders).toBe(2);
81 | });
82 |
83 | it('extends React.Component', function() {
84 | var renders = 0;
85 | class Component extends React.PureComponent {
86 | render() {
87 | expect(this instanceof React.Component).toBe(true);
88 | expect(this instanceof React.PureComponent).toBe(true);
89 | renders++;
90 | return
;
91 | }
92 | }
93 | ReactDOM.render( , document.createElement('div'));
94 | expect(renders).toBe(1);
95 | });
96 |
97 | });
98 |
--------------------------------------------------------------------------------
/__tests__/ReactStatelessComponent-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React;
15 | var ReactDOM;
16 | var ReactTestUtils;
17 |
18 | function StatelessComponent(props) {
19 | return {props.name}
;
20 | }
21 |
22 | describe('ReactStatelessComponent', function() {
23 |
24 | beforeEach(function() {
25 | React = require('../src');
26 | ReactDOM = require('../src');
27 | ReactTestUtils = {
28 | renderIntoDocument: function(instance) {
29 | var div = document.createElement('div');
30 | // None of our tests actually require attaching the container to the
31 | // DOM, and doing so creates a mess that we rely on test isolation to
32 | // clean up, so we're going to stop honoring the name of this method
33 | // (and probably rename it eventually) if no problems arise.
34 | // document.documentElement.appendChild(div);
35 | return ReactDOM.render(instance, div);
36 | }
37 | };
38 | });
39 |
40 | it('should render stateless component', function() {
41 | var el = document.createElement('div');
42 | ReactDOM.render( , el);
43 |
44 | expect(el.textContent).toBe('A');
45 | });
46 |
47 | it('should update stateless component', function() {
48 | var Parent = React.createClass({
49 | render() {
50 | return ;
51 | },
52 | });
53 |
54 | var el = document.createElement('div');
55 | ReactDOM.render( , el);
56 | expect(el.textContent).toBe('A');
57 |
58 | ReactDOM.render( , el);
59 | expect(el.textContent).toBe('B');
60 | });
61 |
62 | it('should unmount stateless component', function() {
63 | var container = document.createElement('div');
64 |
65 | ReactDOM.render( , container);
66 | expect(container.textContent).toBe('A');
67 |
68 | ReactDOM.unmountComponentAtNode(container);
69 | expect(container.textContent).toBe('');
70 | });
71 |
72 | it('should pass context thru stateless component', function() {
73 | var Child = React.createClass({
74 | contextTypes: {
75 | test: React.PropTypes.string.isRequired,
76 | },
77 |
78 | render: function() {
79 | return {this.context.test}
;
80 | },
81 | });
82 |
83 | function Parent() {
84 | return ;
85 | }
86 |
87 | var GrandParent = React.createClass({
88 | childContextTypes: {
89 | test: React.PropTypes.string.isRequired,
90 | },
91 |
92 | getChildContext() {
93 | return {test: this.props.test};
94 | },
95 |
96 | render: function() {
97 | return ;
98 | },
99 | });
100 |
101 | var el = document.createElement('div');
102 | ReactDOM.render( , el);
103 |
104 | expect(el.textContent).toBe('test');
105 |
106 | ReactDOM.render( , el);
107 |
108 | expect(el.textContent).toBe('mest');
109 | });
110 |
111 | it('should warn when stateless component returns array', function() {
112 | spyOn(console, 'error');
113 | function NotAComponent() {
114 | return [
,
];
115 | }
116 | expect(function() {
117 | ReactTestUtils.renderIntoDocument(
);
118 | }).toThrow();
119 | // expect(console.error.calls.length).toBe(1);
120 | // expect(console.error.argsForCall[0][0]).toContain(
121 | // 'NotAComponent(...): A valid React element (or null) must be returned. '+
122 | // 'You may have returned undefined, an array or some other invalid object.'
123 | // );
124 | });
125 |
126 | // it('should throw on string refs in pure functions', function() {
127 | // function Child() {
128 | // return
;
129 | // }
130 |
131 | // expect(function() {
132 | // ReactTestUtils.renderIntoDocument( );
133 | // }).toThrow(
134 | // 'Stateless function components cannot have refs.'
135 | // );
136 | // });
137 |
138 | // it('should warn when given a ref', function() {
139 | // spyOn(console, 'error');
140 |
141 | // var Parent = React.createClass({
142 | // displayName: 'Parent',
143 | // render: function() {
144 | // return ;
145 | // },
146 | // });
147 | // ReactTestUtils.renderIntoDocument( );
148 |
149 | // expect(console.error.argsForCall.length).toBe(1);
150 | // expect(console.error.argsForCall[0][0]).toContain(
151 | // 'Stateless function components cannot be given refs ' +
152 | // '(See ref "stateless" in StatelessComponent created by Parent). ' +
153 | // 'Attempts to access this ref will fail.'
154 | // );
155 | // });
156 |
157 | it('should provide a null ref', function() {
158 | function Child() {
159 | return
;
160 | }
161 |
162 | var comp = ReactTestUtils.renderIntoDocument( );
163 | expect(comp).toBe(null);
164 | });
165 |
166 | // it('should use correct name in key warning', function() {
167 | // function Child() {
168 | // return {[ ]}
;
169 | // }
170 |
171 | // spyOn(console, 'error');
172 | // ReactTestUtils.renderIntoDocument( );
173 | // expect(console.error.argsForCall.length).toBe(1);
174 | // expect(console.error.argsForCall[0][0]).toContain('a unique "key" prop');
175 | // expect(console.error.argsForCall[0][0]).toContain('Child');
176 | // });
177 |
178 | // it('should support default props and prop types', function() {
179 | // function Child(props) {
180 | // return {props.test}
;
181 | // }
182 | // Child.defaultProps = {test: 2};
183 | // Child.propTypes = {test: React.PropTypes.string};
184 |
185 | // spyOn(console, 'error');
186 | // ReactTestUtils.renderIntoDocument( );
187 | // expect(console.error.argsForCall.length).toBe(1);
188 | // expect(console.error.argsForCall[0][0]).toBe(
189 | // 'Warning: Failed propType: Invalid prop `test` of type `number` ' +
190 | // 'supplied to `Child`, expected `string`.'
191 | // );
192 | // });
193 |
194 | it('should receive context', function() {
195 | var Parent = React.createClass({
196 | childContextTypes: {
197 | lang: React.PropTypes.string,
198 | },
199 | getChildContext: function() {
200 | return {lang: 'en'};
201 | },
202 | render: function() {
203 | return ;
204 | },
205 | });
206 | function Child(props, context) {
207 | return {context.lang}
;
208 | }
209 | Child.contextTypes = {lang: React.PropTypes.string};
210 |
211 | var el = document.createElement('div');
212 | ReactDOM.render( , el);
213 | expect(el.textContent).toBe('en');
214 | });
215 |
216 | it('should work with arrow functions', function() {
217 | var Child = function() {
218 | return
;
219 | };
220 | // Will create a new bound function without a prototype, much like a native
221 | // arrow function.
222 | Child = Child.bind(this);
223 |
224 | expect(() => ReactTestUtils.renderIntoDocument( )).not.toThrow();
225 | });
226 |
227 | it('should allow simple functions to return null', function() {
228 | var Child = function() {
229 | return null;
230 | };
231 | expect(() => ReactTestUtils.renderIntoDocument( )).not.toThrow();
232 | });
233 |
234 | it('should allow simple functions to return false', function() {
235 | function Child() {
236 | return false;
237 | }
238 | expect(() => ReactTestUtils.renderIntoDocument( )).not.toThrow();
239 | });
240 |
241 | // it('should warn when using non-React functions in JSX', function() {
242 | // spyOn(console, 'error');
243 | // function NotAComponent() {
244 | // return [
,
];
245 | // }
246 | // expect(function() {
247 | // ReactTestUtils.renderIntoDocument(
);
248 | // }).toThrow(); // has no method 'render'
249 | // expect(console.error.calls.length).toBe(1);
250 | // expect(console.error.argsForCall[0][0]).toContain(
251 | // 'NotAComponent(...): A valid React element (or null) must be returned. You may ' +
252 | // 'have returned undefined, an array or some other invalid object.'
253 | // );
254 | // });
255 | });
256 |
--------------------------------------------------------------------------------
/__tests__/SelectValueElement-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React = require('../src');
15 | var ReactDOM = require('../src');
16 | var ReactTestUtils = {
17 | renderIntoDocument: function(instance) {
18 | var div = document.createElement('div');
19 | return ReactDOM.render(instance, div);
20 | }
21 | };
22 |
23 | describe('Render Select with multiple values', function() {
24 | class Component extends React.Component {
25 | constructor(props, context) {
26 | super(props, context);
27 | this.state = {selectedValue: [1,3,4]};
28 | }
29 |
30 | render() {
31 | return
32 |
33 | 1
34 | 2
35 | 3
36 | 4
37 |
38 | {this.state.selectedValue}
39 |
;
40 | }
41 | }
42 |
43 | it('should mark correct option as selected', function() {
44 | var instance = ReactTestUtils.renderIntoDocument( );
45 | var root = ReactDOM.findDOMNode(instance);
46 | expect(root.childNodes[0].options[0].selected).toBe(true);
47 | expect(root.childNodes[0].options[1].selected).toBe(false);
48 | expect(root.childNodes[0].options[2].selected).toBe(true);
49 | expect(root.childNodes[0].options[3].selected).toBe(true);
50 | });
51 |
52 | });
53 |
54 | describe('Render Select with single value', function() {
55 | class Component extends React.Component {
56 | constructor(props, context) {
57 | super(props, context);
58 | this.state = {selectedValue: 2};
59 | }
60 |
61 | render() {
62 | return
63 |
64 | 1
65 | 2
66 | 3
67 | 4
68 |
69 | {this.state.selectedValue}
70 |
;
71 | }
72 | }
73 |
74 | it('should mark correct option as selected', function() {
75 | var instance = ReactTestUtils.renderIntoDocument( );
76 | var root = ReactDOM.findDOMNode(instance);
77 |
78 | expect(root.childNodes[0].options[0].selected).toBe(false);
79 | expect(root.childNodes[0].options[1].selected).toBe(true);
80 | expect(root.childNodes[0].options[2].selected).toBe(false);
81 | expect(root.childNodes[0].options[3].selected).toBe(false);
82 | });
83 |
84 | });
85 |
--------------------------------------------------------------------------------
/__tests__/findDOMNode-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React = require('../src');
15 | var ReactDOM = require('../src');
16 | var ReactTestUtils = {
17 | renderIntoDocument: function(instance) {
18 | var div = document.createElement('div');
19 | // None of our tests actually require attaching the container to the
20 | // DOM, and doing so creates a mess that we rely on test isolation to
21 | // clean up, so we're going to stop honoring the name of this method
22 | // (and probably rename it eventually) if no problems arise.
23 | // document.documentElement.appendChild(div);
24 | return ReactDOM.render(instance, div);
25 | }
26 | };
27 |
28 | describe('findDOMNode', function() {
29 | it('findDOMNode should return null if passed null', function() {
30 | expect(ReactDOM.findDOMNode(null)).toBe(null);
31 | });
32 |
33 | it('findDOMNode should find dom element', function() {
34 | var MyNode = React.createClass({
35 | render: function() {
36 | return Noise
;
37 | },
38 | });
39 |
40 | var myNode = ReactTestUtils.renderIntoDocument( );
41 | var myDiv = ReactDOM.findDOMNode(myNode);
42 | var mySameDiv = ReactDOM.findDOMNode(myDiv);
43 | expect(myDiv.tagName).toBe('DIV');
44 | expect(mySameDiv).toBe(myDiv);
45 | });
46 |
47 | it('findDOMNode should reject random objects', function() {
48 | expect(function() {
49 | ReactDOM.findDOMNode({foo: 'bar'});
50 | })
51 | .toThrow();
52 | });
53 |
54 | it('findDOMNode should reject unmounted objects with render func', function() {
55 | var Foo = React.createClass({
56 | render: function() {
57 | return
;
58 | },
59 | });
60 |
61 | var container = document.createElement('div');
62 | var inst = ReactDOM.render( , container);
63 | ReactDOM.unmountComponentAtNode(container);
64 |
65 | expect(() => ReactDOM.findDOMNode(inst)).toThrow();
66 | });
67 |
68 | });
69 |
--------------------------------------------------------------------------------
/__tests__/onlyChild-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | describe('onlyChild', function() {
15 |
16 | var React;
17 | var ReactFragment;
18 | var onlyChild;
19 | var WrapComponent;
20 |
21 | beforeEach(function() {
22 | React = require('../src');
23 | onlyChild = React.Children.only;
24 | WrapComponent = React.createClass({
25 | render: function() {
26 | return (
27 |
28 | {onlyChild(this.props.children, this.props.mapFn, this)}
29 |
30 | );
31 | },
32 | });
33 | });
34 |
35 | it('should fail when passed two children', function() {
36 | expect(function() {
37 | var instance =
38 |
39 |
40 |
41 | ;
42 | onlyChild(instance.props.children);
43 | }).toThrow();
44 | });
45 |
46 | it('should fail when passed nully values', function() {
47 | expect(function() {
48 | var instance =
49 |
50 | {null}
51 | ;
52 | onlyChild(instance.props.children);
53 | }).toThrow();
54 |
55 | expect(function() {
56 | var instance =
57 |
58 | {undefined}
59 | ;
60 | onlyChild(instance.props.children);
61 | }).toThrow();
62 | });
63 |
64 | // it('should fail when key/value objects', function() {
65 | // expect(function() {
66 | // var instance =
67 | //
68 | // {ReactFragment.create({oneThing: })}
69 | // ;
70 | // onlyChild(instance.props.children);
71 | // }).toThrow();
72 | // });
73 |
74 |
75 | it('should not fail when passed interpolated single child', function() {
76 | expect(function() {
77 | var instance =
78 |
79 | { }
80 | ;
81 | onlyChild(instance.props.children);
82 | }).not.toThrow();
83 | });
84 |
85 |
86 | it('should return the only child', function() {
87 | expect(function() {
88 | var instance =
89 |
90 |
91 | ;
92 | onlyChild(instance.props.children);
93 | }).not.toThrow();
94 | });
95 |
96 | });
97 |
--------------------------------------------------------------------------------
/__tests__/refs-destruction-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @emails react-core
10 | */
11 |
12 | 'use strict';
13 | jest.dontMock('../src');
14 | var React;
15 | var ReactDOM;
16 | var ReactTestUtils;
17 |
18 | var TestComponent;
19 |
20 | describe('refs-destruction', function() {
21 | beforeEach(function() {
22 | React = require('../src');
23 | ReactDOM = require('../src');
24 | ReactTestUtils = {
25 | renderIntoDocument: function(instance) {
26 | var div = document.createElement('div');
27 | // None of our tests actually require attaching the container to the
28 | // DOM, and doing so creates a mess that we rely on test isolation to
29 | // clean up, so we're going to stop honoring the name of this method
30 | // (and probably rename it eventually) if no problems arise.
31 | // document.documentElement.appendChild(div);
32 | return ReactDOM.render(instance, div);
33 | }
34 | };
35 |
36 | TestComponent = React.createClass({
37 | render: function() {
38 | return (
39 |
40 | {this.props.destroy ? null :
41 |
42 | Lets try to destroy this.
43 |
44 | }
45 |
46 | );
47 | },
48 | });
49 | });
50 |
51 | it('should remove refs when destroying the parent', function() {
52 | var container = document.createElement('div');
53 | var testInstance = ReactDOM.render( , container);
54 | expect(testInstance.refs.theInnerDiv.tagName === 'DIV')
55 | .toBe(true);
56 | expect(testInstance.refs.theInnerDiv.getDOMNode().tagName === 'DIV')
57 | .toBe(true);
58 | expect(Object.keys(testInstance.refs || {}).length).toEqual(1);
59 | ReactDOM.unmountComponentAtNode(container);
60 | expect(Object.keys(testInstance.refs || {}).length).toEqual(0);
61 | });
62 |
63 | it('should remove refs when destroying the child', function() {
64 | var container = document.createElement('div');
65 | var testInstance = ReactDOM.render( , container);
66 | expect(testInstance.refs.theInnerDiv.tagName === 'DIV')
67 | .toBe(true);
68 | expect(testInstance.refs.theInnerDiv.getDOMNode().tagName === 'DIV')
69 | .toBe(true);
70 | expect(Object.keys(testInstance.refs || {}).length).toEqual(1);
71 | ReactDOM.render( , container);
72 | expect(Object.keys(testInstance.refs || {}).length).toEqual(0);
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/addons/LinkedStateMixin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule LinkedStateMixin
10 | * @typechecks static-only
11 | */
12 | var ReactLink = require('./ReactLink')
13 | var ReactStateSetters = require('./ReactStateSetters')
14 |
15 | /**
16 | * A simple mixin around ReactLink.forState().
17 | */
18 | var LinkedStateMixin = {
19 | /**
20 | * Create a ReactLink that's linked to part of this component's state. The
21 | * ReactLink will have the current value of this.state[key] and will call
22 | * setState() when a change is requested.
23 | *
24 | * @param {string} key state key to update. Note: you may want to use keyOf()
25 | * if you're using Google Closure Compiler advanced mode.
26 | * @return {ReactLink} ReactLink instance linking to the state.
27 | */
28 | linkState: function(key) {
29 | return new ReactLink(
30 | this.state[key],
31 | ReactStateSetters.createStateKeySetter(this, key)
32 | );
33 | },
34 | };
35 |
36 | module.exports = LinkedStateMixin;
37 |
--------------------------------------------------------------------------------
/addons/ReactCSSTransitionGroup.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @typechecks
10 | * @providesModule ReactCSSTransitionGroup
11 | */
12 |
13 | var React = require('../dist/react-lite.common')
14 | var ReactTransitionGroup = require('./ReactTransitionGroup')
15 | var ReactCSSTransitionGroupChild = require('./ReactCSSTransitionGroupChild')
16 | var assign = Object.assign
17 |
18 | function createTransitionTimeoutPropValidator(transitionType) {
19 | var timeoutPropName = 'transition' + transitionType + 'Timeout';
20 | var enabledPropName = 'transition' + transitionType;
21 |
22 | return function(props) {
23 | // If the transition is enabled
24 | if (props[enabledPropName]) {
25 | // If no timeout duration is provided
26 | if (props[timeoutPropName] == null) {
27 | return new Error(
28 | timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionGroup: ' +
29 | 'this can cause unreliable animations and won\'t be supported in ' +
30 | 'a future version of React. See ' +
31 | 'https://fb.me/react-animation-transition-group-timeout for more ' +
32 | 'information.'
33 | );
34 |
35 | // If the duration isn't a number
36 | } else if (typeof props[timeoutPropName] !== 'number') {
37 | return new Error(timeoutPropName + ' must be a number (in milliseconds)');
38 | }
39 | }
40 | };
41 | }
42 |
43 | var ReactCSSTransitionGroup = React.createClass({
44 | displayName: 'ReactCSSTransitionGroup',
45 |
46 | propTypes: {
47 | transitionName: ReactCSSTransitionGroupChild.propTypes.name,
48 |
49 | transitionAppear: React.PropTypes.bool,
50 | transitionEnter: React.PropTypes.bool,
51 | transitionLeave: React.PropTypes.bool,
52 | transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'),
53 | transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
54 | transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave'),
55 | },
56 |
57 | getDefaultProps: function() {
58 | return {
59 | transitionAppear: false,
60 | transitionEnter: true,
61 | transitionLeave: true,
62 | };
63 | },
64 |
65 | _wrapChild: function(child) {
66 | // We need to provide this childFactory so that
67 | // ReactCSSTransitionGroupChild can receive updates to name, enter, and
68 | // leave while it is leaving.
69 | return React.createElement(
70 | ReactCSSTransitionGroupChild,
71 | {
72 | name: this.props.transitionName,
73 | appear: this.props.transitionAppear,
74 | enter: this.props.transitionEnter,
75 | leave: this.props.transitionLeave,
76 | appearTimeout: this.props.transitionAppearTimeout,
77 | enterTimeout: this.props.transitionEnterTimeout,
78 | leaveTimeout: this.props.transitionLeaveTimeout,
79 | },
80 | child
81 | );
82 | },
83 |
84 | render: function() {
85 | return React.createElement(
86 | ReactTransitionGroup,
87 | assign({}, this.props, {childFactory: this._wrapChild})
88 | );
89 | },
90 | });
91 |
92 | module.exports = ReactCSSTransitionGroup;
93 |
--------------------------------------------------------------------------------
/addons/ReactCSSTransitionGroupChild.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @typechecks
10 | * @providesModule ReactCSSTransitionGroupChild
11 | */
12 | var React = require('../dist/react-lite.common')
13 | var ReactDOM = React
14 | var Children = ReactDOM.Children
15 | var CSSCore = require('./utils/CSSCore')
16 | var ReactTransitionEvents = require('./ReactTransitionEvents')
17 |
18 | var onlyChild = Children.only
19 |
20 | // We don't remove the element from the DOM until we receive an animationend or
21 | // transitionend event. If the user screws up and forgets to add an animation
22 | // their node will be stuck in the DOM forever, so we detect if an animation
23 | // does not start and if it doesn't, we just call the end listener immediately.
24 | var TICK = 17;
25 |
26 | var ReactCSSTransitionGroupChild = React.createClass({
27 | displayName: 'ReactCSSTransitionGroupChild',
28 |
29 | propTypes: {
30 | name: React.PropTypes.oneOfType([
31 | React.PropTypes.string,
32 | React.PropTypes.shape({
33 | enter: React.PropTypes.string,
34 | leave: React.PropTypes.string,
35 | active: React.PropTypes.string,
36 | }),
37 | React.PropTypes.shape({
38 | enter: React.PropTypes.string,
39 | enterActive: React.PropTypes.string,
40 | leave: React.PropTypes.string,
41 | leaveActive: React.PropTypes.string,
42 | appear: React.PropTypes.string,
43 | appearActive: React.PropTypes.string,
44 | }),
45 | ]).isRequired,
46 |
47 | // Once we require timeouts to be specified, we can remove the
48 | // boolean flags (appear etc.) and just accept a number
49 | // or a bool for the timeout flags (appearTimeout etc.)
50 | appear: React.PropTypes.bool,
51 | enter: React.PropTypes.bool,
52 | leave: React.PropTypes.bool,
53 | appearTimeout: React.PropTypes.number,
54 | enterTimeout: React.PropTypes.number,
55 | leaveTimeout: React.PropTypes.number,
56 | },
57 |
58 | transition: function(animationType, finishCallback, userSpecifiedDelay) {
59 | var node = ReactDOM.findDOMNode(this);
60 |
61 | if (!node) {
62 | if (finishCallback) {
63 | finishCallback();
64 | }
65 | return;
66 | }
67 |
68 | var className = this.props.name[animationType] || this.props.name + '-' + animationType;
69 | var activeClassName = this.props.name[animationType + 'Active'] || className + '-active';
70 | var timeout = null;
71 |
72 | var endListener = function(e) {
73 | if (e && e.target !== node) {
74 | return;
75 | }
76 |
77 | clearTimeout(timeout);
78 |
79 | CSSCore.removeClass(node, className);
80 | CSSCore.removeClass(node, activeClassName);
81 |
82 | ReactTransitionEvents.removeEndEventListener(node, endListener);
83 |
84 | // Usually this optional callback is used for informing an owner of
85 | // a leave animation and telling it to remove the child.
86 | if (finishCallback) {
87 | finishCallback();
88 | }
89 | };
90 |
91 | CSSCore.addClass(node, className);
92 |
93 | // Need to do this to actually trigger a transition.
94 | this.queueClass(activeClassName);
95 |
96 | // If the user specified a timeout delay.
97 | if (userSpecifiedDelay) {
98 | // Clean-up the animation after the specified delay
99 | timeout = setTimeout(endListener, userSpecifiedDelay);
100 | this.transitionTimeouts.push(timeout);
101 | } else {
102 | // DEPRECATED: this listener will be removed in a future version of react
103 | ReactTransitionEvents.addEndEventListener(node, endListener);
104 | }
105 | },
106 |
107 | queueClass: function(className) {
108 | this.classNameQueue.push(className);
109 |
110 | if (!this.timeout) {
111 | this.timeout = setTimeout(this.flushClassNameQueue, TICK);
112 | }
113 | },
114 |
115 | flushClassNameQueue: function() {
116 | if (this.isMounted()) {
117 | this.classNameQueue.forEach(
118 | CSSCore.addClass.bind(CSSCore, ReactDOM.findDOMNode(this))
119 | );
120 | }
121 | this.classNameQueue.length = 0;
122 | this.timeout = null;
123 | },
124 |
125 | componentWillMount: function() {
126 | this.classNameQueue = [];
127 | this.transitionTimeouts = [];
128 | },
129 |
130 | componentWillUnmount: function() {
131 | if (this.timeout) {
132 | clearTimeout(this.timeout);
133 | }
134 | this.transitionTimeouts.forEach(function(timeout) {
135 | clearTimeout(timeout);
136 | });
137 | },
138 |
139 | componentWillAppear: function(done) {
140 | if (this.props.appear) {
141 | this.transition('appear', done, this.props.appearTimeout);
142 | } else {
143 | done();
144 | }
145 | },
146 |
147 | componentWillEnter: function(done) {
148 | if (this.props.enter) {
149 | this.transition('enter', done, this.props.enterTimeout);
150 | } else {
151 | done();
152 | }
153 | },
154 |
155 | componentWillLeave: function(done) {
156 | if (this.props.leave) {
157 | this.transition('leave', done, this.props.leaveTimeout);
158 | } else {
159 | done();
160 | }
161 | },
162 |
163 | render: function() {
164 | return onlyChild(this.props.children);
165 | },
166 | });
167 |
168 | module.exports = ReactCSSTransitionGroupChild;
169 |
--------------------------------------------------------------------------------
/addons/ReactChildren.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactChildren
10 | */
11 | var React = require('../dist/react-lite.common')
12 | var PooledClass = require('./utils/PooledClass')
13 | var emptyFunction = require('./utils/emptyFunction')
14 | var traverseAllChildren = require('./utils/traverseAllChildren')
15 | var isValidElement = React.isValidElement
16 | var cloneElement = React.cloneElement
17 |
18 | var twoArgumentPooler = PooledClass.twoArgumentPooler;
19 | var fourArgumentPooler = PooledClass.fourArgumentPooler;
20 |
21 | var cloneAndReplaceKey = function(oldElement, newKey) {
22 | var newElement = cloneElement(
23 | oldElement,
24 | { key: newKey }
25 | );
26 |
27 | return newElement;
28 | }
29 |
30 | var userProvidedKeyEscapeRegex = /\/(?!\/)/g;
31 | function escapeUserProvidedKey(text) {
32 | return ('' + text).replace(userProvidedKeyEscapeRegex, '//');
33 | }
34 |
35 |
36 | /**
37 | * PooledClass representing the bookkeeping associated with performing a child
38 | * traversal. Allows avoiding binding callbacks.
39 | *
40 | * @constructor ForEachBookKeeping
41 | * @param {!function} forEachFunction Function to perform traversal with.
42 | * @param {?*} forEachContext Context to perform context with.
43 | */
44 | function ForEachBookKeeping(forEachFunction, forEachContext) {
45 | this.func = forEachFunction;
46 | this.context = forEachContext;
47 | this.count = 0;
48 | }
49 | ForEachBookKeeping.prototype.destructor = function() {
50 | this.func = null;
51 | this.context = null;
52 | this.count = 0;
53 | };
54 | PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler);
55 |
56 | function forEachSingleChild(bookKeeping, child, name) {
57 | var func = bookKeeping.func;
58 | var context = bookKeeping.context;
59 | func.call(context, child, bookKeeping.count++);
60 | }
61 |
62 | /**
63 | * Iterates through children that are typically specified as `props.children`.
64 | *
65 | * The provided forEachFunc(child, index) will be called for each
66 | * leaf child.
67 | *
68 | * @param {?*} children Children tree container.
69 | * @param {function(*, int)} forEachFunc
70 | * @param {*} forEachContext Context for forEachContext.
71 | */
72 | function forEachChildren(children, forEachFunc, forEachContext) {
73 | if (children == null) {
74 | return children;
75 | }
76 | var traverseContext =
77 | ForEachBookKeeping.getPooled(forEachFunc, forEachContext);
78 | traverseAllChildren(children, forEachSingleChild, traverseContext);
79 | ForEachBookKeeping.release(traverseContext);
80 | }
81 |
82 |
83 | /**
84 | * PooledClass representing the bookkeeping associated with performing a child
85 | * mapping. Allows avoiding binding callbacks.
86 | *
87 | * @constructor MapBookKeeping
88 | * @param {!*} mapResult Object containing the ordered map of results.
89 | * @param {!function} mapFunction Function to perform mapping with.
90 | * @param {?*} mapContext Context to perform mapping with.
91 | */
92 | function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) {
93 | this.result = mapResult;
94 | this.keyPrefix = keyPrefix;
95 | this.func = mapFunction;
96 | this.context = mapContext;
97 | this.count = 0;
98 | }
99 | MapBookKeeping.prototype.destructor = function() {
100 | this.result = null;
101 | this.keyPrefix = null;
102 | this.func = null;
103 | this.context = null;
104 | this.count = 0;
105 | };
106 | PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler);
107 |
108 | function mapSingleChildIntoContext(bookKeeping, child, childKey) {
109 | var {result, keyPrefix, func, context} = bookKeeping;
110 |
111 | var mappedChild = func.call(context, child, bookKeeping.count++);
112 | if (Array.isArray(mappedChild)) {
113 | mapIntoWithKeyPrefixInternal(
114 | mappedChild,
115 | result,
116 | childKey,
117 | emptyFunction.thatReturnsArgument
118 | );
119 | } else if (mappedChild != null) {
120 | if (isValidElement(mappedChild)) {
121 | mappedChild = cloneAndReplaceKey(
122 | mappedChild,
123 | // Keep both the (mapped) and old keys if they differ, just as
124 | // traverseAllChildren used to do for objects as children
125 | keyPrefix +
126 | (
127 | mappedChild !== child ?
128 | escapeUserProvidedKey(mappedChild.key || '') + '/' :
129 | ''
130 | ) +
131 | childKey
132 | );
133 | }
134 | result.push(mappedChild);
135 | }
136 | }
137 |
138 | function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
139 | var escapedPrefix = '';
140 | if (prefix != null) {
141 | escapedPrefix = escapeUserProvidedKey(prefix) + '/';
142 | }
143 | var traverseContext = MapBookKeeping.getPooled(
144 | array,
145 | escapedPrefix,
146 | func,
147 | context
148 | );
149 | traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
150 | MapBookKeeping.release(traverseContext);
151 | }
152 |
153 | /**
154 | * Maps children that are typically specified as `props.children`.
155 | *
156 | * The provided mapFunction(child, key, index) will be called for each
157 | * leaf child.
158 | *
159 | * @param {?*} children Children tree container.
160 | * @param {function(*, int)} func The map function.
161 | * @param {*} context Context for mapFunction.
162 | * @return {object} Object containing the ordered map of results.
163 | */
164 | function mapChildren(children, func, context) {
165 | if (children == null) {
166 | return children;
167 | }
168 | var result = [];
169 | mapIntoWithKeyPrefixInternal(children, result, null, func, context);
170 | return result;
171 | }
172 |
173 |
174 |
175 | function forEachSingleChildDummy(traverseContext, child, name) {
176 | return null;
177 | }
178 |
179 | /**
180 | * Count the number of children that are typically specified as
181 | * `props.children`.
182 | *
183 | * @param {?*} children Children tree container.
184 | * @return {number} The number of children.
185 | */
186 | function countChildren(children, context) {
187 | return traverseAllChildren(children, forEachSingleChildDummy, null);
188 | }
189 |
190 |
191 | /**
192 | * Flatten a children object (typically specified as `props.children`) and
193 | * return an array with appropriately re-keyed children.
194 | */
195 | function toArray(children) {
196 | var result = [];
197 | mapIntoWithKeyPrefixInternal(
198 | children,
199 | result,
200 | null,
201 | emptyFunction.thatReturnsArgument
202 | );
203 | return result;
204 | }
205 |
206 |
207 | var ReactChildren = {
208 | forEach: forEachChildren,
209 | map: mapChildren,
210 | mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal,
211 | count: countChildren,
212 | toArray: toArray,
213 | };
214 |
215 | module.exports = ReactChildren;
216 |
--------------------------------------------------------------------------------
/addons/ReactComponentWithPureRenderMixin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactComponentWithPureRenderMixin
10 | */
11 | var shallowCompare = require('./shallowCompare')
12 |
13 | /**
14 | * If your React component's render function is "pure", e.g. it will render the
15 | * same result given the same props and state, provide this Mixin for a
16 | * considerable performance boost.
17 | *
18 | * Most React components have pure render functions.
19 | *
20 | * Example:
21 | *
22 | * var ReactComponentWithPureRenderMixin =
23 | * require('ReactComponentWithPureRenderMixin');
24 | * React.createClass({
25 | * mixins: [ReactComponentWithPureRenderMixin],
26 | *
27 | * render: function() {
28 | * return foo
;
29 | * }
30 | * });
31 | *
32 | * Note: This only checks shallow equality for props and state. If these contain
33 | * complex data structures this mixin may have false-negatives for deeper
34 | * differences. Only mixin to components which have simple props and state, or
35 | * use `forceUpdate()` when you know deep data structures have changed.
36 | */
37 | var ReactComponentWithPureRenderMixin = {
38 | shouldComponentUpdate: function(nextProps, nextState) {
39 | return shallowCompare(this, nextProps, nextState);
40 | },
41 | };
42 |
43 | module.exports = ReactComponentWithPureRenderMixin;
44 |
--------------------------------------------------------------------------------
/addons/ReactFragment.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactFragment
10 | */
11 | var React = require('../dist/react-lite.common')
12 | var isValidElement = React.isValidElement
13 | var ReactChildren = require('./ReactChildren')
14 | var emptyFunction = require('./utils/emptyFunction')
15 | var invariant = function() {}
16 | var warning = function() {}
17 | /**
18 | * We used to allow keyed objects to serve as a collection of ReactElements,
19 | * or nested sets. This allowed us a way to explicitly key a set a fragment of
20 | * components. This is now being replaced with an opaque data structure.
21 | * The upgrade path is to call React.addons.createFragment({ key: value }) to
22 | * create a keyed fragment. The resulting data structure is an array.
23 | */
24 |
25 | var numericPropertyRegex = /^\d+$/;
26 |
27 | var warnedAboutNumeric = false;
28 |
29 | var ReactFragment = {
30 | // Wrap a keyed object in an opaque proxy that warns you if you access any
31 | // of its properties.
32 | create: function(object) {
33 | if (typeof object !== 'object' || !object || Array.isArray(object)) {
34 | warning(
35 | false,
36 | 'React.addons.createFragment only accepts a single object. Got: %s',
37 | object
38 | );
39 | return object;
40 | }
41 | if (isValidElement(object)) {
42 | warning(
43 | false,
44 | 'React.addons.createFragment does not accept a ReactElement ' +
45 | 'without a wrapper object.'
46 | );
47 | return object;
48 | }
49 |
50 | invariant(
51 | object.nodeType !== 1,
52 | 'React.addons.createFragment(...): Encountered an invalid child; DOM ' +
53 | 'elements are not valid children of React components.'
54 | );
55 |
56 | var result = [];
57 |
58 | for (var key in object) {
59 | ReactChildren.mapIntoWithKeyPrefixInternal(
60 | object[key],
61 | result,
62 | key,
63 | emptyFunction.thatReturnsArgument
64 | );
65 | }
66 |
67 | return result;
68 | },
69 | };
70 |
71 | module.exports = ReactFragment;
72 |
--------------------------------------------------------------------------------
/addons/ReactLink.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactLink
10 | * @typechecks static-only
11 | */
12 |
13 | /**
14 | * ReactLink encapsulates a common pattern in which a component wants to modify
15 | * a prop received from its parent. ReactLink allows the parent to pass down a
16 | * value coupled with a callback that, when invoked, expresses an intent to
17 | * modify that value. For example:
18 | *
19 | * React.createClass({
20 | * getInitialState: function() {
21 | * return {value: ''};
22 | * },
23 | * render: function() {
24 | * var valueLink = new ReactLink(this.state.value, this._handleValueChange);
25 | * return ;
26 | * },
27 | * _handleValueChange: function(newValue) {
28 | * this.setState({value: newValue});
29 | * }
30 | * });
31 | *
32 | * We have provided some sugary mixins to make the creation and
33 | * consumption of ReactLink easier; see LinkedValueUtils and LinkedStateMixin.
34 | */
35 | var React = require('../dist/react-lite.common')
36 |
37 | /**
38 | * @param {*} value current value of the link
39 | * @param {function} requestChange callback to request a change
40 | */
41 | function ReactLink(value, requestChange) {
42 | this.value = value;
43 | this.requestChange = requestChange;
44 | }
45 |
46 | /**
47 | * Creates a PropType that enforces the ReactLink API and optionally checks the
48 | * type of the value being passed inside the link. Example:
49 | *
50 | * MyComponent.propTypes = {
51 | * tabIndexLink: ReactLink.PropTypes.link(React.PropTypes.number)
52 | * }
53 | */
54 | function createLinkTypeChecker(linkType) {
55 | var shapes = {
56 | value: typeof linkType === 'undefined' ?
57 | React.PropTypes.any.isRequired :
58 | linkType.isRequired,
59 | requestChange: React.PropTypes.func.isRequired,
60 | };
61 | return React.PropTypes.shape(shapes);
62 | }
63 |
64 | ReactLink.PropTypes = {
65 | link: createLinkTypeChecker,
66 | };
67 |
68 | module.exports = ReactLink;
69 |
--------------------------------------------------------------------------------
/addons/ReactStateSetters.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactStateSetters
10 | */
11 |
12 | 'use strict';
13 |
14 | var ReactStateSetters = {
15 | /**
16 | * Returns a function that calls the provided function, and uses the result
17 | * of that to set the component's state.
18 | *
19 | * @param {ReactCompositeComponent} component
20 | * @param {function} funcReturningState Returned callback uses this to
21 | * determine how to update state.
22 | * @return {function} callback that when invoked uses funcReturningState to
23 | * determined the object literal to setState.
24 | */
25 | createStateSetter: function(component, funcReturningState) {
26 | return function(a, b, c, d, e, f) {
27 | var partialState = funcReturningState.call(component, a, b, c, d, e, f);
28 | if (partialState) {
29 | component.setState(partialState);
30 | }
31 | };
32 | },
33 |
34 | /**
35 | * Returns a single-argument callback that can be used to update a single
36 | * key in the component's state.
37 | *
38 | * Note: this is memoized function, which makes it inexpensive to call.
39 | *
40 | * @param {ReactCompositeComponent} component
41 | * @param {string} key The key in the state that you should update.
42 | * @return {function} callback of 1 argument which calls setState() with
43 | * the provided keyName and callback argument.
44 | */
45 | createStateKeySetter: function(component, key) {
46 | // Memoize the setters.
47 | var cache = component.__keySetters || (component.__keySetters = {});
48 | return cache[key] || (cache[key] = createStateKeySetter(component, key));
49 | },
50 | };
51 |
52 | function createStateKeySetter(component, key) {
53 | // Partial state is allocated outside of the function closure so it can be
54 | // reused with every call, avoiding memory allocation when this function
55 | // is called.
56 | var partialState = {};
57 | return function stateKeySetter(value) {
58 | partialState[key] = value;
59 | component.setState(partialState);
60 | };
61 | }
62 |
63 | ReactStateSetters.Mixin = {
64 | /**
65 | * Returns a function that calls the provided function, and uses the result
66 | * of that to set the component's state.
67 | *
68 | * For example, these statements are equivalent:
69 | *
70 | * this.setState({x: 1});
71 | * this.createStateSetter(function(xValue) {
72 | * return {x: xValue};
73 | * })(1);
74 | *
75 | * @param {function} funcReturningState Returned callback uses this to
76 | * determine how to update state.
77 | * @return {function} callback that when invoked uses funcReturningState to
78 | * determined the object literal to setState.
79 | */
80 | createStateSetter: function(funcReturningState) {
81 | return ReactStateSetters.createStateSetter(this, funcReturningState);
82 | },
83 |
84 | /**
85 | * Returns a single-argument callback that can be used to update a single
86 | * key in the component's state.
87 | *
88 | * For example, these statements are equivalent:
89 | *
90 | * this.setState({x: 1});
91 | * this.createStateKeySetter('x')(1);
92 | *
93 | * Note: this is memoized function, which makes it inexpensive to call.
94 | *
95 | * @param {string} key The key in the state that you should update.
96 | * @return {function} callback of 1 argument which calls setState() with
97 | * the provided keyName and callback argument.
98 | */
99 | createStateKeySetter: function(key) {
100 | return ReactStateSetters.createStateKeySetter(this, key);
101 | },
102 | };
103 |
104 | module.exports = ReactStateSetters;
105 |
--------------------------------------------------------------------------------
/addons/ReactTransitionChildMapping.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @typechecks static-only
10 | * @providesModule ReactTransitionChildMapping
11 | */
12 |
13 | 'use strict';
14 |
15 | var flattenChildren = require('./utils/flattenChildren')
16 |
17 | var ReactTransitionChildMapping = {
18 | /**
19 | * Given `this.props.children`, return an object mapping key to child. Just
20 | * simple syntactic sugar around flattenChildren().
21 | *
22 | * @param {*} children `this.props.children`
23 | * @return {object} Mapping of key to child
24 | */
25 | getChildMapping: function(children) {
26 | if (!children) {
27 | return children;
28 | }
29 | return flattenChildren(children);
30 | },
31 |
32 | /**
33 | * When you're adding or removing children some may be added or removed in the
34 | * same render pass. We want to show *both* since we want to simultaneously
35 | * animate elements in and out. This function takes a previous set of keys
36 | * and a new set of keys and merges them with its best guess of the correct
37 | * ordering. In the future we may expose some of the utilities in
38 | * ReactMultiChild to make this easy, but for now React itself does not
39 | * directly have this concept of the union of prevChildren and nextChildren
40 | * so we implement it here.
41 | *
42 | * @param {object} prev prev children as returned from
43 | * `ReactTransitionChildMapping.getChildMapping()`.
44 | * @param {object} next next children as returned from
45 | * `ReactTransitionChildMapping.getChildMapping()`.
46 | * @return {object} a key set that contains all keys in `prev` and all keys
47 | * in `next` in a reasonable order.
48 | */
49 | mergeChildMappings: function(prev, next) {
50 | prev = prev || {};
51 | next = next || {};
52 |
53 | function getValueForKey(key) {
54 | if (next.hasOwnProperty(key)) {
55 | return next[key];
56 | } else {
57 | return prev[key];
58 | }
59 | }
60 |
61 | // For each key of `next`, the list of keys to insert before that key in
62 | // the combined list
63 | var nextKeysPending = {};
64 |
65 | var pendingKeys = [];
66 | for (var prevKey in prev) {
67 | if (next.hasOwnProperty(prevKey)) {
68 | if (pendingKeys.length) {
69 | nextKeysPending[prevKey] = pendingKeys;
70 | pendingKeys = [];
71 | }
72 | } else {
73 | pendingKeys.push(prevKey);
74 | }
75 | }
76 |
77 | var i;
78 | var childMapping = {};
79 | for (var nextKey in next) {
80 | if (nextKeysPending.hasOwnProperty(nextKey)) {
81 | for (i = 0; i < nextKeysPending[nextKey].length; i++) {
82 | var pendingNextKey = nextKeysPending[nextKey][i];
83 | childMapping[nextKeysPending[nextKey][i]] = getValueForKey(
84 | pendingNextKey
85 | );
86 | }
87 | }
88 | childMapping[nextKey] = getValueForKey(nextKey);
89 | }
90 |
91 | // Finally, add the keys which didn't appear before any key in `next`
92 | for (i = 0; i < pendingKeys.length; i++) {
93 | childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]);
94 | }
95 |
96 | return childMapping;
97 | },
98 | };
99 |
100 | module.exports = ReactTransitionChildMapping;
101 |
--------------------------------------------------------------------------------
/addons/ReactTransitionEvents.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactTransitionEvents
10 | */
11 |
12 | 'use strict';
13 |
14 | /**
15 | * EVENT_NAME_MAP is used to determine which event fired when a
16 | * transition/animation ends, based on the style property used to
17 | * define that event.
18 | */
19 | var EVENT_NAME_MAP = {
20 | transitionend: {
21 | 'transition': 'transitionend',
22 | 'WebkitTransition': 'webkitTransitionEnd',
23 | 'MozTransition': 'mozTransitionEnd',
24 | 'OTransition': 'oTransitionEnd',
25 | 'msTransition': 'MSTransitionEnd',
26 | },
27 |
28 | animationend: {
29 | 'animation': 'animationend',
30 | 'WebkitAnimation': 'webkitAnimationEnd',
31 | 'MozAnimation': 'mozAnimationEnd',
32 | 'OAnimation': 'oAnimationEnd',
33 | 'msAnimation': 'MSAnimationEnd',
34 | },
35 | };
36 |
37 | var endEvents = [];
38 |
39 | function detectEvents() {
40 | var testEl = document.createElement('div');
41 | var style = testEl.style;
42 |
43 | // On some platforms, in particular some releases of Android 4.x,
44 | // the un-prefixed "animation" and "transition" properties are defined on the
45 | // style object but the events that fire will still be prefixed, so we need
46 | // to check if the un-prefixed events are useable, and if not remove them
47 | // from the map
48 | if (!('AnimationEvent' in window)) {
49 | delete EVENT_NAME_MAP.animationend.animation;
50 | }
51 |
52 | if (!('TransitionEvent' in window)) {
53 | delete EVENT_NAME_MAP.transitionend.transition;
54 | }
55 |
56 | for (var baseEventName in EVENT_NAME_MAP) {
57 | var baseEvents = EVENT_NAME_MAP[baseEventName];
58 | for (var styleName in baseEvents) {
59 | if (styleName in style) {
60 | endEvents.push(baseEvents[styleName]);
61 | break;
62 | }
63 | }
64 | }
65 | }
66 |
67 | detectEvents();
68 |
69 | // We use the raw {add|remove}EventListener() call because EventListener
70 | // does not know how to remove event listeners and we really should
71 | // clean up. Also, these events are not triggered in older browsers
72 | // so we should be A-OK here.
73 |
74 | function addEventListener(node, eventName, eventListener) {
75 | node.addEventListener(eventName, eventListener, false);
76 | }
77 |
78 | function removeEventListener(node, eventName, eventListener) {
79 | node.removeEventListener(eventName, eventListener, false);
80 | }
81 |
82 | var ReactTransitionEvents = {
83 | addEndEventListener: function(node, eventListener) {
84 | if (endEvents.length === 0) {
85 | // If CSS transitions are not supported, trigger an "end animation"
86 | // event immediately.
87 | window.setTimeout(eventListener, 0);
88 | return;
89 | }
90 | endEvents.forEach(function(endEvent) {
91 | addEventListener(node, endEvent, eventListener);
92 | });
93 | },
94 |
95 | removeEndEventListener: function(node, eventListener) {
96 | if (endEvents.length === 0) {
97 | return;
98 | }
99 | endEvents.forEach(function(endEvent) {
100 | removeEventListener(node, endEvent, eventListener);
101 | });
102 | },
103 | };
104 |
105 | module.exports = ReactTransitionEvents;
106 |
--------------------------------------------------------------------------------
/addons/ReactTransitionGroup.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactTransitionGroup
10 | */
11 |
12 | var React = require('../dist/react-lite.common')
13 | var ReactTransitionChildMapping = require('./ReactTransitionChildMapping')
14 | var emptyFunction = require('./utils/emptyFunction')
15 | var assign = Object.assign
16 | var ReactTransitionGroup = React.createClass({
17 | displayName: 'ReactTransitionGroup',
18 |
19 | propTypes: {
20 | component: React.PropTypes.any,
21 | childFactory: React.PropTypes.func,
22 | },
23 |
24 | getDefaultProps: function() {
25 | return {
26 | component: 'span',
27 | childFactory: emptyFunction.thatReturnsArgument,
28 | };
29 | },
30 |
31 | getInitialState: function() {
32 | return {
33 | children: ReactTransitionChildMapping.getChildMapping(this.props.children),
34 | };
35 | },
36 |
37 | componentWillMount: function() {
38 | this.currentlyTransitioningKeys = {};
39 | this.keysToEnter = [];
40 | this.keysToLeave = [];
41 | },
42 |
43 | componentDidMount: function() {
44 | var initialChildMapping = this.state.children;
45 | for (var key in initialChildMapping) {
46 | if (initialChildMapping[key]) {
47 | this.performAppear(key);
48 | }
49 | }
50 | },
51 |
52 | componentWillReceiveProps: function(nextProps) {
53 | var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
54 | nextProps.children
55 | );
56 | var prevChildMapping = this.state.children;
57 |
58 | this.setState({
59 | children: ReactTransitionChildMapping.mergeChildMappings(
60 | prevChildMapping,
61 | nextChildMapping
62 | ),
63 | });
64 |
65 | var key;
66 |
67 | for (key in nextChildMapping) {
68 | var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);
69 | if (nextChildMapping[key] && !hasPrev &&
70 | !this.currentlyTransitioningKeys[key]) {
71 | this.keysToEnter.push(key);
72 | }
73 | }
74 |
75 | for (key in prevChildMapping) {
76 | var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);
77 | if (prevChildMapping[key] && !hasNext &&
78 | !this.currentlyTransitioningKeys[key]) {
79 | this.keysToLeave.push(key);
80 | }
81 | }
82 |
83 | // If we want to someday check for reordering, we could do it here.
84 | },
85 |
86 | componentDidUpdate: function() {
87 | var keysToEnter = this.keysToEnter;
88 | this.keysToEnter = [];
89 | keysToEnter.forEach(this.performEnter);
90 |
91 | var keysToLeave = this.keysToLeave;
92 | this.keysToLeave = [];
93 | keysToLeave.forEach(this.performLeave);
94 | },
95 |
96 | performAppear: function(key) {
97 | this.currentlyTransitioningKeys[key] = true;
98 |
99 | var component = this.refs[key];
100 |
101 | if (component.componentWillAppear) {
102 | component.componentWillAppear(
103 | this._handleDoneAppearing.bind(this, key)
104 | );
105 | } else {
106 | this._handleDoneAppearing(key);
107 | }
108 | },
109 |
110 | _handleDoneAppearing: function(key) {
111 | var component = this.refs[key];
112 | if (component.componentDidAppear) {
113 | component.componentDidAppear();
114 | }
115 |
116 | delete this.currentlyTransitioningKeys[key];
117 |
118 | var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
119 | this.props.children
120 | );
121 |
122 | if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
123 | // This was removed before it had fully appeared. Remove it.
124 | this.performLeave(key);
125 | }
126 | },
127 |
128 | performEnter: function(key) {
129 | this.currentlyTransitioningKeys[key] = true;
130 |
131 | var component = this.refs[key];
132 |
133 | if (component.componentWillEnter) {
134 | component.componentWillEnter(
135 | this._handleDoneEntering.bind(this, key)
136 | );
137 | } else {
138 | this._handleDoneEntering(key);
139 | }
140 | },
141 |
142 | _handleDoneEntering: function(key) {
143 | var component = this.refs[key];
144 | if (component.componentDidEnter) {
145 | component.componentDidEnter();
146 | }
147 |
148 | delete this.currentlyTransitioningKeys[key];
149 |
150 | var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
151 | this.props.children
152 | );
153 |
154 | if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
155 | // This was removed before it had fully entered. Remove it.
156 | this.performLeave(key);
157 | }
158 | },
159 |
160 | performLeave: function(key) {
161 | this.currentlyTransitioningKeys[key] = true;
162 |
163 | var component = this.refs[key];
164 | if (component.componentWillLeave) {
165 | component.componentWillLeave(this._handleDoneLeaving.bind(this, key));
166 | } else {
167 | // Note that this is somewhat dangerous b/c it calls setState()
168 | // again, effectively mutating the component before all the work
169 | // is done.
170 | this._handleDoneLeaving(key);
171 | }
172 | },
173 |
174 | _handleDoneLeaving: function(key) {
175 | var component = this.refs[key];
176 |
177 | if (component.componentDidLeave) {
178 | component.componentDidLeave();
179 | }
180 |
181 | delete this.currentlyTransitioningKeys[key];
182 |
183 | var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
184 | this.props.children
185 | );
186 |
187 | if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
188 | // This entered again before it fully left. Add it again.
189 | this.performEnter(key);
190 | } else {
191 | this.setState(function(state) {
192 | var newChildren = assign({}, state.children);
193 | delete newChildren[key];
194 | return {children: newChildren};
195 | });
196 | }
197 | },
198 |
199 | render: function() {
200 | // TODO: we could get rid of the need for the wrapper node
201 | // by cloning a single child
202 | var childrenToRender = [];
203 | for (var key in this.state.children) {
204 | var child = this.state.children[key];
205 | if (child) {
206 | // You may need to apply reactive updates to a child as it is leaving.
207 | // The normal React way to do it won't work since the child will have
208 | // already been removed. In case you need this behavior you can provide
209 | // a childFactory function to wrap every child, even the ones that are
210 | // leaving.
211 | childrenToRender.push(React.cloneElement(
212 | this.props.childFactory(child),
213 | {ref: key, key: key}
214 | ));
215 | }
216 | }
217 | return React.createElement(
218 | this.props.component,
219 | this.props,
220 | childrenToRender
221 | );
222 | },
223 | });
224 |
225 | module.exports = ReactTransitionGroup;
226 |
--------------------------------------------------------------------------------
/addons/ReactWithAddons.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule ReactWithAddons
10 | */
11 |
12 | /**
13 | * This module exists purely in the open source project, and is meant as a way
14 | * to create a separate standalone build of React. This build has "addons", or
15 | * functionality we've built and think might be useful but doesn't have a good
16 | * place to live inside React core.
17 | */
18 | var React = require('../dist/react-lite.common')
19 | var LinkedStateMixin = require('./LinkedStateMixin')
20 | var ReactComponentWithPureRenderMixin = require('./ReactComponentWithPureRenderMixin')
21 | var ReactCSSTransitionGroup = require('./ReactCSSTransitionGroup')
22 | var ReactFragment = require('./ReactFragment')
23 | var ReactTransitionGroup = require('./ReactTransitionGroup')
24 | var ReactUpdates = React
25 | var shallowCompare = require('./shallowCompare')
26 | var update = require('./update')
27 |
28 | var warning = function() {}
29 |
30 | var cloneWithProps = React.cloneElement
31 |
32 | var warnedAboutBatchedUpdates = false;
33 |
34 | React.addons = {
35 | CSSTransitionGroup: ReactCSSTransitionGroup,
36 | LinkedStateMixin: LinkedStateMixin,
37 | PureRenderMixin: ReactComponentWithPureRenderMixin,
38 | TransitionGroup: ReactTransitionGroup,
39 | batchedUpdates: function() {
40 | return ReactUpdates.batchedUpdates.apply(this, arguments);
41 | },
42 | cloneWithProps: cloneWithProps,
43 | createFragment: ReactFragment.create,
44 | shallowCompare: shallowCompare,
45 | update: update,
46 | };
47 |
48 | module.exports = React;
49 |
--------------------------------------------------------------------------------
/addons/react-tap-event-plugin.js:
--------------------------------------------------------------------------------
1 | var Fastclick = require('fastclick')
2 | module.exports = function injectTapEventPlugin() {
3 | var supportTouch = 'ontouchstart' in document
4 | if (supportTouch) {
5 | Fastclick.attach(document.body)
6 | }
7 | }
--------------------------------------------------------------------------------
/addons/renderSubtreeIntoContainer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule renderSubtreeIntoContainer
10 | */
11 |
12 | 'use strict';
13 | var React = require('../dist/react-lite.common')
14 | var unstable_renderSubtreeIntoContainer = React.unstable_renderSubtreeIntoContainer
15 | module.exports = unstable_renderSubtreeIntoContainer
--------------------------------------------------------------------------------
/addons/shallowCompare.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule shallowCompare
10 | */
11 | var shallowEqual = require('./utils/shallowEqual')
12 |
13 | /**
14 | * Does a shallow comparison for props and state.
15 | * See ReactComponentWithPureRenderMixin
16 | */
17 | function shallowCompare(instance, nextProps, nextState) {
18 | return (
19 | !shallowEqual(instance.props, nextProps) ||
20 | !shallowEqual(instance.state, nextState)
21 | );
22 | }
23 |
24 | module.exports = shallowCompare;
25 |
--------------------------------------------------------------------------------
/addons/update.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule update
10 | */
11 |
12 | /* global hasOwnProperty:true */
13 |
14 | var React = require('../dist/react-lite.common')
15 | var assign = Object.assign
16 | var keyOf = require('./utils/keyOf')
17 | var invariant = function() {}
18 | var hasOwnProperty = {}.hasOwnProperty;
19 |
20 | function shallowCopy(x) {
21 | if (Array.isArray(x)) {
22 | return x.concat();
23 | } else if (x && typeof x === 'object') {
24 | return assign(new x.constructor(), x);
25 | } else {
26 | return x;
27 | }
28 | }
29 |
30 | var COMMAND_PUSH = keyOf({$push: null});
31 | var COMMAND_UNSHIFT = keyOf({$unshift: null});
32 | var COMMAND_SPLICE = keyOf({$splice: null});
33 | var COMMAND_SET = keyOf({$set: null});
34 | var COMMAND_MERGE = keyOf({$merge: null});
35 | var COMMAND_APPLY = keyOf({$apply: null});
36 |
37 | var ALL_COMMANDS_LIST = [
38 | COMMAND_PUSH,
39 | COMMAND_UNSHIFT,
40 | COMMAND_SPLICE,
41 | COMMAND_SET,
42 | COMMAND_MERGE,
43 | COMMAND_APPLY,
44 | ];
45 |
46 | var ALL_COMMANDS_SET = {};
47 |
48 | ALL_COMMANDS_LIST.forEach(function(command) {
49 | ALL_COMMANDS_SET[command] = true;
50 | });
51 |
52 | function invariantArrayCase(value, spec, command) {
53 | invariant(
54 | Array.isArray(value),
55 | 'update(): expected target of %s to be an array; got %s.',
56 | command,
57 | value
58 | );
59 | var specValue = spec[command];
60 | invariant(
61 | Array.isArray(specValue),
62 | 'update(): expected spec of %s to be an array; got %s. ' +
63 | 'Did you forget to wrap your parameter in an array?',
64 | command,
65 | specValue
66 | );
67 | }
68 |
69 | function update(value, spec) {
70 | invariant(
71 | typeof spec === 'object',
72 | 'update(): You provided a key path to update() that did not contain one ' +
73 | 'of %s. Did you forget to include {%s: ...}?',
74 | ALL_COMMANDS_LIST.join(', '),
75 | COMMAND_SET
76 | );
77 |
78 | if (hasOwnProperty.call(spec, COMMAND_SET)) {
79 | invariant(
80 | Object.keys(spec).length === 1,
81 | 'Cannot have more than one key in an object with %s',
82 | COMMAND_SET
83 | );
84 |
85 | return spec[COMMAND_SET];
86 | }
87 |
88 | var nextValue = shallowCopy(value);
89 |
90 | if (hasOwnProperty.call(spec, COMMAND_MERGE)) {
91 | var mergeObj = spec[COMMAND_MERGE];
92 | invariant(
93 | mergeObj && typeof mergeObj === 'object',
94 | 'update(): %s expects a spec of type \'object\'; got %s',
95 | COMMAND_MERGE,
96 | mergeObj
97 | );
98 | invariant(
99 | nextValue && typeof nextValue === 'object',
100 | 'update(): %s expects a target of type \'object\'; got %s',
101 | COMMAND_MERGE,
102 | nextValue
103 | );
104 | assign(nextValue, spec[COMMAND_MERGE]);
105 | }
106 |
107 | if (hasOwnProperty.call(spec, COMMAND_PUSH)) {
108 | invariantArrayCase(value, spec, COMMAND_PUSH);
109 | spec[COMMAND_PUSH].forEach(function(item) {
110 | nextValue.push(item);
111 | });
112 | }
113 |
114 | if (hasOwnProperty.call(spec, COMMAND_UNSHIFT)) {
115 | invariantArrayCase(value, spec, COMMAND_UNSHIFT);
116 | spec[COMMAND_UNSHIFT].forEach(function(item) {
117 | nextValue.unshift(item);
118 | });
119 | }
120 |
121 | if (hasOwnProperty.call(spec, COMMAND_SPLICE)) {
122 | invariant(
123 | Array.isArray(value),
124 | 'Expected %s target to be an array; got %s',
125 | COMMAND_SPLICE,
126 | value
127 | );
128 | invariant(
129 | Array.isArray(spec[COMMAND_SPLICE]),
130 | 'update(): expected spec of %s to be an array of arrays; got %s. ' +
131 | 'Did you forget to wrap your parameters in an array?',
132 | COMMAND_SPLICE,
133 | spec[COMMAND_SPLICE]
134 | );
135 | spec[COMMAND_SPLICE].forEach(function(args) {
136 | invariant(
137 | Array.isArray(args),
138 | 'update(): expected spec of %s to be an array of arrays; got %s. ' +
139 | 'Did you forget to wrap your parameters in an array?',
140 | COMMAND_SPLICE,
141 | spec[COMMAND_SPLICE]
142 | );
143 | nextValue.splice.apply(nextValue, args);
144 | });
145 | }
146 |
147 | if (hasOwnProperty.call(spec, COMMAND_APPLY)) {
148 | invariant(
149 | typeof spec[COMMAND_APPLY] === 'function',
150 | 'update(): expected spec of %s to be a function; got %s.',
151 | COMMAND_APPLY,
152 | spec[COMMAND_APPLY]
153 | );
154 | nextValue = spec[COMMAND_APPLY](nextValue);
155 | }
156 |
157 | for (var k in spec) {
158 | if (!(ALL_COMMANDS_SET.hasOwnProperty(k) && ALL_COMMANDS_SET[k])) {
159 | nextValue[k] = update(value[k], spec[k]);
160 | }
161 | }
162 |
163 | return nextValue;
164 | }
165 |
166 | module.exports = update;
167 |
--------------------------------------------------------------------------------
/addons/utils/CSSCore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule CSSCore
10 | * @typechecks
11 | */
12 |
13 | 'use strict';
14 |
15 | var invariant = function() {}
16 |
17 | /**
18 | * The CSSCore module specifies the API (and implements most of the methods)
19 | * that should be used when dealing with the display of elements (via their
20 | * CSS classes and visibility on screen. It is an API focused on mutating the
21 | * display and not reading it as no logical state should be encoded in the
22 | * display of elements.
23 | */
24 |
25 | var CSSCore = {
26 |
27 | /**
28 | * Adds the class passed in to the element if it doesn't already have it.
29 | *
30 | * @param {DOMElement} element the element to set the class on
31 | * @param {string} className the CSS className
32 | * @return {DOMElement} the element passed in
33 | */
34 | addClass: function (element, className) {
35 | !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSSCore.addClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : undefined;
36 |
37 | if (className) {
38 | if (element.classList) {
39 | element.classList.add(className);
40 | } else if (!CSSCore.hasClass(element, className)) {
41 | element.className = element.className + ' ' + className;
42 | }
43 | }
44 | return element;
45 | },
46 |
47 | /**
48 | * Removes the class passed in from the element
49 | *
50 | * @param {DOMElement} element the element to set the class on
51 | * @param {string} className the CSS className
52 | * @return {DOMElement} the element passed in
53 | */
54 | removeClass: function (element, className) {
55 | !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSSCore.removeClass takes only a single class name. "%s" contains ' + 'multiple classes.', className) : invariant(false) : undefined;
56 |
57 | if (className) {
58 | if (element.classList) {
59 | element.classList.remove(className);
60 | } else if (CSSCore.hasClass(element, className)) {
61 | element.className = element.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1').replace(/\s+/g, ' ') // multiple spaces to one
62 | .replace(/^\s*|\s*$/g, ''); // trim the ends
63 | }
64 | }
65 | return element;
66 | },
67 |
68 | /**
69 | * Helper to add or remove a class from an element based on a condition.
70 | *
71 | * @param {DOMElement} element the element to set the class on
72 | * @param {string} className the CSS className
73 | * @param {*} bool condition to whether to add or remove the class
74 | * @return {DOMElement} the element passed in
75 | */
76 | conditionClass: function (element, className, bool) {
77 | return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
78 | },
79 |
80 | /**
81 | * Tests whether the element has the class specified.
82 | *
83 | * @param {DOMNode|DOMWindow} element the element to set the class on
84 | * @param {string} className the CSS className
85 | * @return {boolean} true if the element has the class, false if not
86 | */
87 | hasClass: function (element, className) {
88 | !!/\s/.test(className) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'CSS.hasClass takes only a single class name.') : invariant(false) : undefined;
89 | if (element.classList) {
90 | return !!className && element.classList.contains(className);
91 | }
92 | return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
93 | }
94 |
95 | };
96 |
97 | module.exports = CSSCore;
--------------------------------------------------------------------------------
/addons/utils/PooledClass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule PooledClass
10 | */
11 |
12 | 'use strict';
13 |
14 | var invariant = function() {}
15 |
16 | /**
17 | * Static poolers. Several custom versions for each potential number of
18 | * arguments. A completely generic pooler is easy to implement, but would
19 | * require accessing the `arguments` object. In each of these, `this` refers to
20 | * the Class itself, not an instance. If any others are needed, simply add them
21 | * here, or in their own files.
22 | */
23 | var oneArgumentPooler = function(copyFieldsFrom) {
24 | var Klass = this;
25 | if (Klass.instancePool.length) {
26 | var instance = Klass.instancePool.pop();
27 | Klass.call(instance, copyFieldsFrom);
28 | return instance;
29 | } else {
30 | return new Klass(copyFieldsFrom);
31 | }
32 | };
33 |
34 | var twoArgumentPooler = function(a1, a2) {
35 | var Klass = this;
36 | if (Klass.instancePool.length) {
37 | var instance = Klass.instancePool.pop();
38 | Klass.call(instance, a1, a2);
39 | return instance;
40 | } else {
41 | return new Klass(a1, a2);
42 | }
43 | };
44 |
45 | var threeArgumentPooler = function(a1, a2, a3) {
46 | var Klass = this;
47 | if (Klass.instancePool.length) {
48 | var instance = Klass.instancePool.pop();
49 | Klass.call(instance, a1, a2, a3);
50 | return instance;
51 | } else {
52 | return new Klass(a1, a2, a3);
53 | }
54 | };
55 |
56 | var fourArgumentPooler = function(a1, a2, a3, a4) {
57 | var Klass = this;
58 | if (Klass.instancePool.length) {
59 | var instance = Klass.instancePool.pop();
60 | Klass.call(instance, a1, a2, a3, a4);
61 | return instance;
62 | } else {
63 | return new Klass(a1, a2, a3, a4);
64 | }
65 | };
66 |
67 | var fiveArgumentPooler = function(a1, a2, a3, a4, a5) {
68 | var Klass = this;
69 | if (Klass.instancePool.length) {
70 | var instance = Klass.instancePool.pop();
71 | Klass.call(instance, a1, a2, a3, a4, a5);
72 | return instance;
73 | } else {
74 | return new Klass(a1, a2, a3, a4, a5);
75 | }
76 | };
77 |
78 | var standardReleaser = function(instance) {
79 | var Klass = this;
80 | invariant(
81 | instance instanceof Klass,
82 | 'Trying to release an instance into a pool of a different type.'
83 | );
84 | instance.destructor();
85 | if (Klass.instancePool.length < Klass.poolSize) {
86 | Klass.instancePool.push(instance);
87 | }
88 | };
89 |
90 | var DEFAULT_POOL_SIZE = 10;
91 | var DEFAULT_POOLER = oneArgumentPooler;
92 |
93 | /**
94 | * Augments `CopyConstructor` to be a poolable class, augmenting only the class
95 | * itself (statically) not adding any prototypical fields. Any CopyConstructor
96 | * you give this may have a `poolSize` property, and will look for a
97 | * prototypical `destructor` on instances (optional).
98 | *
99 | * @param {Function} CopyConstructor Constructor that can be used to reset.
100 | * @param {Function} pooler Customizable pooler.
101 | */
102 | var addPoolingTo = function(CopyConstructor, pooler) {
103 | var NewKlass = CopyConstructor;
104 | NewKlass.instancePool = [];
105 | NewKlass.getPooled = pooler || DEFAULT_POOLER;
106 | if (!NewKlass.poolSize) {
107 | NewKlass.poolSize = DEFAULT_POOL_SIZE;
108 | }
109 | NewKlass.release = standardReleaser;
110 | return NewKlass;
111 | };
112 |
113 | var PooledClass = {
114 | addPoolingTo: addPoolingTo,
115 | oneArgumentPooler: oneArgumentPooler,
116 | twoArgumentPooler: twoArgumentPooler,
117 | threeArgumentPooler: threeArgumentPooler,
118 | fourArgumentPooler: fourArgumentPooler,
119 | fiveArgumentPooler: fiveArgumentPooler,
120 | };
121 |
122 | module.exports = PooledClass;
123 |
--------------------------------------------------------------------------------
/addons/utils/emptyFunction.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule emptyFunction
10 | */
11 |
12 | "use strict";
13 |
14 | function makeEmptyFunction(arg) {
15 | return function () {
16 | return arg;
17 | };
18 | }
19 |
20 | /**
21 | * This function accepts and discards inputs; it has no side effects. This is
22 | * primarily useful idiomatically for overridable function endpoints which
23 | * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
24 | */
25 | function emptyFunction() {}
26 |
27 | emptyFunction.thatReturns = makeEmptyFunction;
28 | emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
29 | emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
30 | emptyFunction.thatReturnsNull = makeEmptyFunction(null);
31 | emptyFunction.thatReturnsThis = function () {
32 | return this;
33 | };
34 | emptyFunction.thatReturnsArgument = function (arg) {
35 | return arg;
36 | };
37 |
38 | module.exports = emptyFunction;
--------------------------------------------------------------------------------
/addons/utils/escapeTextContentForBrowser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule escapeTextContentForBrowser
10 | */
11 |
12 | 'use strict';
13 |
14 | var ESCAPE_LOOKUP = {
15 | '&': '&',
16 | '>': '>',
17 | '<': '<',
18 | '"': '"',
19 | '\'': ''',
20 | };
21 |
22 | var ESCAPE_REGEX = /[&><"']/g;
23 |
24 | function escaper(match) {
25 | return ESCAPE_LOOKUP[match];
26 | }
27 |
28 | /**
29 | * Escapes text to prevent scripting attacks.
30 | *
31 | * @param {*} text Text value to escape.
32 | * @return {string} An escaped string.
33 | */
34 | function escapeTextContentForBrowser(text) {
35 | return ('' + text).replace(ESCAPE_REGEX, escaper);
36 | }
37 |
38 | module.exports = escapeTextContentForBrowser;
39 |
--------------------------------------------------------------------------------
/addons/utils/flattenChildren.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule flattenChildren
10 | */
11 | var traverseAllChildren = require('./traverseAllChildren')
12 |
13 | /**
14 | * @param {function} traverseContext Context passed through traversal.
15 | * @param {?ReactComponent} child React child component.
16 | * @param {!string} name String name of key path to child.
17 | */
18 | function flattenSingleChildIntoContext(traverseContext, child, name) {
19 | // We found a component instance.
20 | var result = traverseContext;
21 | var keyUnique = (result[name] === undefined);
22 | if (keyUnique && child != null) {
23 | result[name] = child;
24 | }
25 | }
26 |
27 | /**
28 | * Flattens children that are typically specified as `props.children`. Any null
29 | * children will not be included in the resulting object.
30 | * @return {!object} flattened children keyed by name.
31 | */
32 | function flattenChildren(children) {
33 | if (children == null) {
34 | return children;
35 | }
36 | var result = {};
37 | traverseAllChildren(children, flattenSingleChildIntoContext, result);
38 | return result;
39 | }
40 |
41 | module.exports = flattenChildren;
42 |
--------------------------------------------------------------------------------
/addons/utils/getIteratorFn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule getIteratorFn
10 | * @typechecks static-only
11 | */
12 |
13 | 'use strict';
14 |
15 | /* global Symbol */
16 | var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
17 | var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
18 |
19 | /**
20 | * Returns the iterator method function contained on the iterable object.
21 | *
22 | * Be sure to invoke the function with the iterable as context:
23 | *
24 | * var iteratorFn = getIteratorFn(myIterable);
25 | * if (iteratorFn) {
26 | * var iterator = iteratorFn.call(myIterable);
27 | * ...
28 | * }
29 | *
30 | * @param {?object} maybeIterable
31 | * @return {?function}
32 | */
33 | function getIteratorFn(maybeIterable) {
34 | var iteratorFn = maybeIterable && (
35 | (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL]) ||
36 | maybeIterable[FAUX_ITERATOR_SYMBOL]
37 | );
38 | if (typeof iteratorFn === 'function') {
39 | return iteratorFn;
40 | }
41 | }
42 |
43 | module.exports = getIteratorFn;
44 |
--------------------------------------------------------------------------------
/addons/utils/keyOf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule keyOf
10 | */
11 |
12 | /**
13 | * Allows extraction of a minified key. Let's the build system minify keys
14 | * without losing the ability to dynamically use key strings as values
15 | * themselves. Pass in an object with a single key/val pair and it will return
16 | * you the string key of that single record. Suppose you want to grab the
17 | * value for a key 'className' inside of an object. Key/val minification may
18 | * have aliased that key to be 'xa12'. keyOf({className: null}) will return
19 | * 'xa12' in that case. Resolve keys you want to use once at startup time, then
20 | * reuse those resolutions.
21 | */
22 | "use strict";
23 |
24 | var keyOf = function (oneKeyObj) {
25 | var key;
26 | for (key in oneKeyObj) {
27 | if (!oneKeyObj.hasOwnProperty(key)) {
28 | continue;
29 | }
30 | return key;
31 | }
32 | return null;
33 | };
34 |
35 | module.exports = keyOf;
--------------------------------------------------------------------------------
/addons/utils/quoteAttributeValueForBrowser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule quoteAttributeValueForBrowser
10 | */
11 |
12 | var escapeTextContentForBrowser = require('./escapeTextContentForBrowser')
13 |
14 | /**
15 | * Escapes attribute value to prevent scripting attacks.
16 | *
17 | * @param {*} value Value to escape.
18 | * @return {string} An escaped string.
19 | */
20 | function quoteAttributeValueForBrowser(value) {
21 | return '"' + escapeTextContentForBrowser(value) + '"';
22 | }
23 |
24 | module.exports = quoteAttributeValueForBrowser;
25 |
--------------------------------------------------------------------------------
/addons/utils/shallowEqual.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule shallowEqual
10 | * @typechecks
11 | *
12 | */
13 |
14 | 'use strict';
15 |
16 | var hasOwnProperty = Object.prototype.hasOwnProperty;
17 |
18 | /**
19 | * Performs equality by iterating through keys on an object and returning false
20 | * when any key has values which are not strictly equal between the arguments.
21 | * Returns true when the values of all keys are strictly equal.
22 | */
23 | function shallowEqual(objA, objB) {
24 | if (objA === objB) {
25 | return true;
26 | }
27 |
28 | if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
29 | return false;
30 | }
31 |
32 | var keysA = Object.keys(objA);
33 | var keysB = Object.keys(objB);
34 |
35 | if (keysA.length !== keysB.length) {
36 | return false;
37 | }
38 |
39 | // Test for A's keys different from B.
40 | var bHasOwnProperty = hasOwnProperty.bind(objB);
41 | for (var i = 0; i < keysA.length; i++) {
42 | if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
43 | return false;
44 | }
45 | }
46 |
47 | return true;
48 | }
49 |
50 | module.exports = shallowEqual;
--------------------------------------------------------------------------------
/addons/utils/traverseAllChildren.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013-2015, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | *
9 | * @providesModule traverseAllChildren
10 | */
11 | var React = require('../../dist/react-lite.common')
12 | var isValidElement = React.isValidElement
13 | var getIteratorFn = require('./getIteratorFn')
14 |
15 | var invariant = function() {}
16 | var SEPARATOR = '.';
17 | var SUBSEPARATOR = ':';
18 |
19 | /**
20 | * TODO: Test that a single child and an array with one item have the same key
21 | * pattern.
22 | */
23 |
24 | var userProvidedKeyEscaperLookup = {
25 | '=': '=0',
26 | '.': '=1',
27 | ':': '=2',
28 | };
29 |
30 | var userProvidedKeyEscapeRegex = /[=.:]/g;
31 |
32 | var didWarnAboutMaps = false;
33 |
34 | function userProvidedKeyEscaper(match) {
35 | return userProvidedKeyEscaperLookup[match];
36 | }
37 |
38 | /**
39 | * Generate a key string that identifies a component within a set.
40 | *
41 | * @param {*} component A component that could contain a manual key.
42 | * @param {number} index Index that is used if a manual key is not provided.
43 | * @return {string}
44 | */
45 | function getComponentKey(component, index) {
46 | if (component && component.key != null) {
47 | // Explicit key
48 | return wrapUserProvidedKey(component.key);
49 | }
50 | // Implicit key determined by the index in the set
51 | return index.toString(36);
52 | }
53 |
54 | /**
55 | * Escape a component key so that it is safe to use in a reactid.
56 | *
57 | * @param {*} text Component key to be escaped.
58 | * @return {string} An escaped string.
59 | */
60 | function escapeUserProvidedKey(text) {
61 | return ('' + text).replace(
62 | userProvidedKeyEscapeRegex,
63 | userProvidedKeyEscaper
64 | );
65 | }
66 |
67 | /**
68 | * Wrap a `key` value explicitly provided by the user to distinguish it from
69 | * implicitly-generated keys generated by a component's index in its parent.
70 | *
71 | * @param {string} key Value of a user-provided `key` attribute
72 | * @return {string}
73 | */
74 | function wrapUserProvidedKey(key) {
75 | return '$' + escapeUserProvidedKey(key);
76 | }
77 |
78 | /**
79 | * @param {?*} children Children tree container.
80 | * @param {!string} nameSoFar Name of the key path so far.
81 | * @param {!function} callback Callback to invoke with each child found.
82 | * @param {?*} traverseContext Used to pass information throughout the traversal
83 | * process.
84 | * @return {!number} The number of children in this subtree.
85 | */
86 | function traverseAllChildrenImpl(
87 | children,
88 | nameSoFar,
89 | callback,
90 | traverseContext
91 | ) {
92 | var type = typeof children;
93 |
94 | if (type === 'undefined' || type === 'boolean') {
95 | // All of the above are perceived as null.
96 | children = null;
97 | }
98 |
99 | if (children === null ||
100 | type === 'string' ||
101 | type === 'number' ||
102 | isValidElement(children)) {
103 | callback(
104 | traverseContext,
105 | children,
106 | // If it's the only child, treat the name as if it was wrapped in an array
107 | // so that it's consistent if the number of children grows.
108 | nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
109 | );
110 | return 1;
111 | }
112 |
113 | var child;
114 | var nextName;
115 | var subtreeCount = 0; // Count of children found in the current subtree.
116 | var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
117 |
118 | if (Array.isArray(children)) {
119 | for (var i = 0; i < children.length; i++) {
120 | child = children[i];
121 | nextName = nextNamePrefix + getComponentKey(child, i);
122 | subtreeCount += traverseAllChildrenImpl(
123 | child,
124 | nextName,
125 | callback,
126 | traverseContext
127 | );
128 | }
129 | } else {
130 | var iteratorFn = getIteratorFn(children);
131 | if (iteratorFn) {
132 | var iterator = iteratorFn.call(children);
133 | var step;
134 | if (iteratorFn !== children.entries) {
135 | var ii = 0;
136 | while (!(step = iterator.next()).done) {
137 | child = step.value;
138 | nextName = nextNamePrefix + getComponentKey(child, ii++);
139 | subtreeCount += traverseAllChildrenImpl(
140 | child,
141 | nextName,
142 | callback,
143 | traverseContext
144 | );
145 | }
146 | } else {
147 | // Iterator will provide entry [k,v] tuples rather than values.
148 | while (!(step = iterator.next()).done) {
149 | var entry = step.value;
150 | if (entry) {
151 | child = entry[1];
152 | nextName = (
153 | nextNamePrefix +
154 | wrapUserProvidedKey(entry[0]) + SUBSEPARATOR +
155 | getComponentKey(child, 0)
156 | );
157 | subtreeCount += traverseAllChildrenImpl(
158 | child,
159 | nextName,
160 | callback,
161 | traverseContext
162 | );
163 | }
164 | }
165 | }
166 | } else if (type === 'object') {
167 | var addendum = '';
168 | var childrenString = String(children);
169 | invariant(
170 | false,
171 | 'Objects are not valid as a React child (found: %s).%s',
172 | childrenString === '[object Object]' ?
173 | 'object with keys {' + Object.keys(children).join(', ') + '}' :
174 | childrenString,
175 | addendum
176 | );
177 | }
178 | }
179 |
180 | return subtreeCount;
181 | }
182 |
183 | /**
184 | * Traverses children that are typically specified as `props.children`, but
185 | * might also be specified through attributes:
186 | *
187 | * - `traverseAllChildren(this.props.children, ...)`
188 | * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
189 | *
190 | * The `traverseContext` is an optional argument that is passed through the
191 | * entire traversal. It can be used to store accumulations or anything else that
192 | * the callback might find relevant.
193 | *
194 | * @param {?*} children Children tree object.
195 | * @param {!function} callback To invoke upon traversing each child.
196 | * @param {?*} traverseContext Context for traversal.
197 | * @return {!number} The number of children in this subtree.
198 | */
199 | function traverseAllChildren(children, callback, traverseContext) {
200 | if (children == null) {
201 | return 0;
202 | }
203 |
204 | return traverseAllChildrenImpl(children, '', callback, traverseContext);
205 | }
206 |
207 | module.exports = traverseAllChildren;
208 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | // copy from Vue
2 | var fs = require('fs')
3 | var zlib = require('zlib')
4 | var rollup = require('rollup')
5 | var uglify = require('uglify-js')
6 | var babel = require('rollup-plugin-babel')
7 | var replace = require('rollup-plugin-replace')
8 | var version = process.env.VERSION || require('./package.json').version
9 |
10 | var banner =
11 | '/*!\n' +
12 | ' * react-lite.js v' + version + '\n' +
13 | ' * (c) ' + new Date().getFullYear() + ' Jade Gu\n' +
14 | ' * Released under the MIT License.\n' +
15 | ' */'
16 |
17 | runRollupTask('./src/index', 'react-lite')
18 | // runRollupTask('./addons/ReactWithAddons', 'react-lite-with-addons')
19 |
20 | function runRollupTask(entry, filename) {
21 |
22 | // CommonJS build.
23 | // this is used as the "main" field in package.json
24 | // and used by bundlers like Webpack and Browserify.
25 | rollup.rollup({
26 | entry: entry,
27 | plugins: [
28 | babel({
29 | loose: 'all'
30 | })
31 | ]
32 | })
33 | .then(function(bundle) {
34 | return write('dist/' + filename + '.common.js', bundle.generate({
35 | format: 'cjs',
36 | banner: banner
37 | }).code)
38 | })
39 | // Standalone Dev Build
40 | .then(function() {
41 | return rollup.rollup({
42 | entry: entry,
43 | plugins: [
44 | replace({
45 | 'process.env.NODE_ENV': "'development'"
46 | }),
47 | babel({
48 | loose: 'all'
49 | })
50 | ]
51 | })
52 | .then(function(bundle) {
53 | return write('dist/' + filename + '.js', bundle.generate({
54 | format: 'umd',
55 | banner: banner,
56 | moduleName: 'React'
57 | }).code)
58 | })
59 | })
60 | .then(function() {
61 | // Standalone Production Build
62 | return rollup.rollup({
63 | entry: entry,
64 | plugins: [
65 | replace({
66 | 'process.env.NODE_ENV': "'production'"
67 | }),
68 | babel({
69 | loose: 'all'
70 | })
71 | ]
72 | })
73 | .then(function(bundle) {
74 | var code = bundle.generate({
75 | format: 'umd',
76 | moduleName: 'React'
77 | }).code
78 | var minified = banner + '\n' + uglify.minify(code, {
79 | fromString: true
80 | }).code
81 | return write('dist/' + filename + '.min.js', minified)
82 | })
83 | .then(zip)
84 | })
85 | .catch(logError)
86 |
87 | function zip() {
88 | return new Promise(function(resolve, reject) {
89 | fs.readFile('dist/' + filename + '.min.js', function(err, buf) {
90 | if (err) return reject(err)
91 | zlib.gzip(buf, function(err, buf) {
92 | if (err) return reject(err)
93 | write('dist/' + filename + '.min.js.gz', buf).then(resolve)
94 | })
95 | })
96 | })
97 | }
98 | }
99 |
100 | function write(dest, code) {
101 | return new Promise(function(resolve, reject) {
102 | fs.writeFile(dest, code, function(err) {
103 | if (err) return reject(err)
104 | console.log(blue(dest) + ' ' + getSize(code))
105 | resolve()
106 | })
107 | })
108 | }
109 |
110 | function getSize(code) {
111 | return (code.length / 1024).toFixed(2) + 'kb'
112 | }
113 |
114 | function logError(e) {
115 | console.log(e)
116 | }
117 |
118 | function blue(str) {
119 | return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
120 | }
121 |
--------------------------------------------------------------------------------
/dist/react-lite.min.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lucifier129/react-lite/b7586ae247615f2d4c4373f206e6c284d7931f81/dist/react-lite.min.js.gz
--------------------------------------------------------------------------------
/examples/js-repaint-perf/ENV.js:
--------------------------------------------------------------------------------
1 | var ENV = ENV || (function() {
2 |
3 | var _base;
4 |
5 | (_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
6 | return padding.repeat((toLength - this.length) / padding.length).concat(this);
7 | });
8 |
9 | function formatElapsed(value) {
10 | str = parseFloat(value).toFixed(2);
11 | if (value > 60) {
12 | minutes = Math.floor(value / 60);
13 | comps = (value % 60).toFixed(2).split('.');
14 | seconds = comps[0].lpad('0', 2);
15 | ms = comps[1];
16 | str = minutes + ":" + seconds + "." + ms;
17 | }
18 | return str;
19 | }
20 |
21 | function getElapsedClassName(elapsed) {
22 | var className = 'Query elapsed';
23 | if (elapsed >= 10.0) {
24 | className += ' warn_long';
25 | }
26 | else if (elapsed >= 1.0) {
27 | className += ' warn';
28 | }
29 | else {
30 | className += ' short';
31 | }
32 | return className;
33 | }
34 |
35 | var lastGeneratedDatabases = [];
36 |
37 | function getData() {
38 | // generate some dummy data
39 | data = {
40 | start_at: new Date().getTime() / 1000,
41 | databases: {}
42 | };
43 |
44 | for (var i = 1; i <= ENV.rows; i++) {
45 | data.databases["cluster" + i] = {
46 | queries: []
47 | };
48 |
49 | data.databases["cluster" + i + "slave"] = {
50 | queries: []
51 | };
52 | }
53 |
54 | Object.keys(data.databases).forEach(function(dbname) {
55 |
56 | if (lastGeneratedDatabases.length == 0 || Math.random() < ENV.mutations()) {
57 | var info = data.databases[dbname];
58 | var r = Math.floor((Math.random() * 10) + 1);
59 | for (var i = 0; i < r; i++) {
60 | var elapsed = Math.random() * 15;
61 | var q = {
62 | canvas_action: null,
63 | canvas_context_id: null,
64 | canvas_controller: null,
65 | canvas_hostname: null,
66 | canvas_job_tag: null,
67 | canvas_pid: null,
68 | elapsed: elapsed,
69 | formatElapsed: formatElapsed(elapsed),
70 | elapsedClassName: getElapsedClassName(elapsed),
71 | query: "SELECT blah FROM something",
72 | waiting: Math.random() < 0.5
73 | };
74 |
75 | if (Math.random() < 0.2) {
76 | q.query = " in transaction";
77 | }
78 |
79 | if (Math.random() < 0.1) {
80 | q.query = "vacuum";
81 | }
82 |
83 | info.queries.push(q);
84 | }
85 |
86 | info.queries = info.queries.sort(function (a, b) {
87 | return b.elapsed - a.elapsed;
88 | });
89 | } else {
90 | data.databases[dbname] = lastGeneratedDatabases[dbname];
91 | }
92 | });
93 |
94 | lastGeneratedDatabases = data.databases;
95 |
96 | return data;
97 | }
98 |
99 | var lastDatabases = {
100 | toArray: function() {
101 | return Object.keys(this).filter(function(k) { return k !== 'toArray'; }).map(function(k) { return this[k]; }.bind(this))
102 | }
103 | };
104 |
105 | function generateData() {
106 | var databases = [];
107 | var newData = getData();
108 | Object.keys(newData.databases).forEach(function(dbname) {
109 | var sampleInfo = newData.databases[dbname];
110 | var database = {
111 | dbname: dbname,
112 | samples: []
113 | };
114 |
115 | function countClassName(queries) {
116 | var countClassName = "label";
117 | if (queries.length >= 20) {
118 | countClassName += " label-important";
119 | }
120 | else if (queries.length >= 10) {
121 | countClassName += " label-warning";
122 | }
123 | else {
124 | countClassName += " label-success";
125 | }
126 | return countClassName;
127 | }
128 |
129 | function topFiveQueries(queries) {
130 | var tfq = queries.slice(0, 5);
131 | while (tfq.length < 5) {
132 | tfq.push({ query: "", formatElapsed: '', elapsedClassName: '' });
133 | }
134 | return tfq;
135 | }
136 |
137 | var samples = database.samples;
138 | samples.push({
139 | time: newData.start_at,
140 | queries: sampleInfo.queries,
141 | topFiveQueries: topFiveQueries(sampleInfo.queries),
142 | countClassName: countClassName(sampleInfo.queries)
143 | });
144 | if (samples.length > 5) {
145 | samples.splice(0, samples.length - 5);
146 | }
147 | var samples = database.samples;
148 | database.lastSample = database.samples[database.samples.length - 1];
149 | databases.push(database);
150 | });
151 | return {
152 | toArray: function() {
153 | return databases;
154 | }
155 | };
156 | }
157 |
158 | var mutationsValue = 0.5;
159 |
160 | function mutations(value) {
161 | if (value) {
162 | mutationsValue = value;
163 | return mutationsValue;
164 | } else {
165 | return mutationsValue;
166 | }
167 | }
168 |
169 | var body = document.querySelector('body');
170 | var theFirstChild = body.firstChild;
171 |
172 | var sliderContainer = document.createElement( 'div' );
173 | sliderContainer.style.cssText = "display: flex";
174 | var slider = document.createElement('input');
175 | var text = document.createElement('label');
176 | text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
177 | text.id = "ratioval";
178 | slider.setAttribute("type", "range");
179 | slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px';
180 | slider.addEventListener('change', function(e) {
181 | ENV.mutations(e.target.value / 100);
182 | document.querySelector('#ratioval').innerHTML = 'mutations : ' + (ENV.mutations() * 100).toFixed(0) + '%';
183 | });
184 | sliderContainer.appendChild( text );
185 | sliderContainer.appendChild( slider );
186 | body.insertBefore( sliderContainer, theFirstChild );
187 |
188 | return {
189 | generateData: generateData,
190 | rows: 50,
191 | timeout: 0,
192 | mutations: mutations
193 | };
194 | })();
195 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/ga.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
3 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
4 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
5 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
6 |
7 | ga('create', 'UA-63822970-1', 'auto');
8 | ga('send', 'pageview');
9 | })();
10 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/react/app-component.js:
--------------------------------------------------------------------------------
1 | /** @jsx React.DOM */
2 |
3 | var Query = React.createClass({
4 | render: function() {
5 | return (
6 |
7 | {this.props.formatElapsed}
8 |
9 |
{this.props.query}
10 |
11 |
12 |
13 | );
14 | }
15 | })
16 |
17 | var sample = function (database) {
18 | var _queries = [];
19 | database.lastSample.topFiveQueries.forEach(function(query, index) {
20 | _queries.push(
21 |
26 | );
27 | });
28 | return [
29 |
30 |
31 | {database.lastSample.queries.length}
32 |
33 | ,
34 | _queries
35 | ];
36 | };
37 |
38 | var Database = React.createClass({
39 | render: function() {
40 | var lastSample = this.props.lastSample;
41 | return (
42 |
43 |
44 | {this.props.dbname}
45 |
46 | {sample(this.props)}
47 |
48 | );
49 | }
50 | });
51 |
52 | var DBMon = React.createClass({
53 | getInitialState: function() {
54 | return {
55 | databases: []
56 | };
57 | },
58 |
59 | loadSamples: function () {
60 | this.setState({
61 | databases: ENV.generateData().toArray()
62 | });
63 | Monitoring.renderRate.ping();
64 | setTimeout(this.loadSamples, ENV.timeout);
65 | },
66 |
67 | componentDidMount: function() {
68 | this.loadSamples();
69 | },
70 |
71 | render: function() {
72 | var databases = [];
73 | Object.keys(this.state.databases).forEach(function(dbname) {
74 | databases.push(
75 |
78 | );
79 | }.bind(this));
80 |
81 | var databases = this.state.databases.map(function(database) {
82 | return
86 | });
87 |
88 | return (
89 |
90 |
91 |
92 | {databases}
93 |
94 |
95 |
96 | );
97 | }
98 | });
99 |
100 | React.render( , document.getElementById('dbmon'));
101 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/react/app.js:
--------------------------------------------------------------------------------
1 | /** @jsx React.DOM */
2 |
3 | var DBMon = React.createClass({
4 | getInitialState: function() {
5 | return {
6 | databases: []
7 | };
8 | },
9 |
10 | loadSamples: function () {
11 | this.setState({ databases: ENV.generateData().toArray() });
12 | Monitoring.renderRate.ping();
13 | setTimeout(this.loadSamples.bind(this), ENV.timeout);
14 | },
15 |
16 | componentDidMount: function() {
17 | this.loadSamples();
18 | },
19 |
20 | render: function() {
21 | return (
22 |
23 |
24 |
25 | {
26 | this.state.databases.map(function(database) {
27 | return (
28 |
29 |
30 | {database.dbname}
31 |
32 |
33 |
34 | {database.lastSample.queries.length}
35 |
36 |
37 | {
38 | database.lastSample.topFiveQueries.map(function(query, index) {
39 | return (
40 |
41 | {query.formatElapsed}
42 |
43 |
{query.query}
44 |
45 |
46 |
47 | );
48 | })
49 | }
50 |
51 | );
52 | })
53 | }
54 |
55 |
56 |
57 | );
58 | }
59 | });
60 |
61 | console.time('mount')
62 | ReactDOM.render( , document.getElementById('dbmon'));
63 | console.timeEnd('mount')
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | dbmon (react)
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/react/lite.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | dbmon (react)
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/styles.css:
--------------------------------------------------------------------------------
1 | .Query {
2 | position: relative;
3 | }
4 |
5 | .Query:hover .popover {
6 | left: -100%;
7 | width: 100%;
8 | display: block;
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/vendor/memory-stats.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | * @author jetienne / http://jetienne.com/
4 | * @author paulirish / http://paulirish.com/
5 | */
6 | var MemoryStats = function (){
7 |
8 | var msMin = 100;
9 | var msMax = 0;
10 |
11 | var container = document.createElement( 'div' );
12 | container.id = 'stats';
13 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
14 |
15 | var msDiv = document.createElement( 'div' );
16 | msDiv.id = 'ms';
17 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
18 | container.appendChild( msDiv );
19 |
20 | var msText = document.createElement( 'div' );
21 | msText.id = 'msText';
22 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
23 | msText.innerHTML= 'Memory';
24 | msDiv.appendChild( msText );
25 |
26 | var msGraph = document.createElement( 'div' );
27 | msGraph.id = 'msGraph';
28 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
29 | msDiv.appendChild( msGraph );
30 |
31 | while ( msGraph.children.length < 74 ) {
32 |
33 | var bar = document.createElement( 'span' );
34 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
35 | msGraph.appendChild( bar );
36 |
37 | }
38 |
39 | var updateGraph = function ( dom, height, color ) {
40 |
41 | var child = dom.appendChild( dom.firstChild );
42 | child.style.height = height + 'px';
43 | if( color ) child.style.backgroundColor = color;
44 |
45 | }
46 |
47 | var perf = window.performance || {};
48 | // polyfill usedJSHeapSize
49 | if (!perf && !perf.memory){
50 | perf.memory = { usedJSHeapSize : 0 };
51 | }
52 | if (perf && !perf.memory){
53 | perf.memory = { usedJSHeapSize : 0 };
54 | }
55 |
56 | // support of the API?
57 | if( perf.memory.totalJSHeapSize === 0 ){
58 | console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .')
59 | }
60 |
61 | // TODO, add a sanity check to see if values are bucketed.
62 | // If so, reminde user to adopt the --enable-precise-memory-info flag.
63 | // open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info
64 |
65 | var lastTime = Date.now();
66 | var lastUsedHeap= perf.memory.usedJSHeapSize;
67 | return {
68 | domElement: container,
69 |
70 | update: function () {
71 |
72 | // refresh only 30time per second
73 | if( Date.now() - lastTime < 1000/30 ) return;
74 | lastTime = Date.now()
75 |
76 | var delta = perf.memory.usedJSHeapSize - lastUsedHeap;
77 | lastUsedHeap = perf.memory.usedJSHeapSize;
78 | var color = delta < 0 ? '#830' : '#131';
79 |
80 | var ms = perf.memory.usedJSHeapSize;
81 | msMin = Math.min( msMin, ms );
82 | msMax = Math.max( msMax, ms );
83 | msText.textContent = "Mem: " + bytesToSize(ms, 2);
84 |
85 | var normValue = ms / (30*1024*1024);
86 | var height = Math.min( 30, 30 - normValue * 30 );
87 | updateGraph( msGraph, height, color);
88 |
89 | function bytesToSize( bytes, nFractDigit ){
90 | var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
91 | if (bytes == 0) return 'n/a';
92 | nFractDigit = nFractDigit !== undefined ? nFractDigit : 0;
93 | var precision = Math.pow(10, nFractDigit);
94 | var i = Math.floor(Math.log(bytes) / Math.log(1024));
95 | return Math.round(bytes*precision / Math.pow(1024, i))/precision + ' ' + sizes[i];
96 | };
97 | }
98 |
99 | }
100 |
101 | };
--------------------------------------------------------------------------------
/examples/js-repaint-perf/vendor/monitor.js:
--------------------------------------------------------------------------------
1 | var Monitoring = Monitoring || (function() {
2 |
3 | var stats = new MemoryStats();
4 | stats.domElement.style.position = 'fixed';
5 | stats.domElement.style.right = '0px';
6 | stats.domElement.style.bottom = '0px';
7 | document.body.appendChild( stats.domElement );
8 | requestAnimationFrame(function rAFloop(){
9 | stats.update();
10 | requestAnimationFrame(rAFloop);
11 | });
12 |
13 | var RenderRate = function () {
14 | var container = document.createElement( 'div' );
15 | container.id = 'stats';
16 | container.style.cssText = 'width:150px;opacity:0.9;cursor:pointer;position:fixed;right:80px;bottom:0px;';
17 |
18 | var msDiv = document.createElement( 'div' );
19 | msDiv.id = 'ms';
20 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
21 | container.appendChild( msDiv );
22 |
23 | var msText = document.createElement( 'div' );
24 | msText.id = 'msText';
25 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
26 | msText.innerHTML= 'Repaint rate: 0/sec';
27 | msDiv.appendChild( msText );
28 |
29 | var bucketSize = 20;
30 | var bucket = [];
31 | var lastTime = Date.now();
32 | return {
33 | domElement: container,
34 | ping: function () {
35 | var start = lastTime;
36 | var stop = Date.now();
37 | var rate = 1000 / (stop - start);
38 | bucket.push(rate);
39 | if (bucket.length > bucketSize) {
40 | bucket.shift();
41 | }
42 | var sum = 0;
43 | for (var i = 0; i < bucket.length; i++) {
44 | sum = sum + bucket[i];
45 | }
46 | msText.textContent = "Repaint rate: " + (sum / bucket.length).toFixed(2) + "/sec";
47 | lastTime = stop;
48 | }
49 | }
50 | };
51 |
52 | var renderRate = new RenderRate();
53 | document.body.appendChild( renderRate.domElement );
54 |
55 | return {
56 | memoryStats: stats,
57 | renderRate: renderRate
58 | };
59 |
60 | })();
61 |
--------------------------------------------------------------------------------
/examples/js-repaint-perf/vendor/react-dom.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ReactDOM v15.3.1
3 | *
4 | * Copyright 2013-present, Facebook, Inc.
5 | * All rights reserved.
6 | *
7 | * This source code is licensed under the BSD-style license found in the
8 | * LICENSE file in the root directory of this source tree. An additional grant
9 | * of patent rights can be found in the PATENTS file in the same directory.
10 | *
11 | */
12 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOM=e(f.React)}}(function(e){return e.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED});
--------------------------------------------------------------------------------
/examples/simple/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | simple test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | if (typeof requestAnimationFrame === 'undefined') {
2 | window.requestAnimationFrame = fn => setTimeout(fn, 100 / 6)
3 | window.cancelAnimationFrame = id => clearTimeout(id)
4 | }
5 |
6 | if (typeof console === 'undefined') {
7 | console = { log() {}, time() {}, timeEnd() {} }
8 | }
9 |
10 | const Counter = React.createClass({
11 | getInitialState() {
12 | return {
13 | text: '123123123'
14 | }
15 | },
16 | componentWillMount() {
17 | console.time('Counter mount')
18 | },
19 | componentDidMount() {
20 | console.timeEnd('Counter mount')
21 | },
22 | toNum(num, callback) {
23 | cancelAnimationFrame(this.rid)
24 | let { COUNT } = this.props
25 | let counting = () => {
26 | let { count } = this.props
27 | switch (true) {
28 | case count > num:
29 | COUNT('DECREMENT')
30 | break
31 | case count < num:
32 | COUNT('INCREMENT')
33 | break
34 | case count === num:
35 | return callback && callback()
36 | }
37 | this.rid = requestAnimationFrame(counting)
38 | }
39 | counting()
40 | },
41 | componentWillUpdate() {
42 | // debugger
43 | console.log('willUpdate', 'Counter')
44 | if (this.state.text === '123123123') {
45 | this.setState({
46 | text: '[text set by willUpdate]'
47 | })
48 | }
49 | console.log(this.state.text, 'WillUpdate')
50 |
51 | },
52 | componentDidUpdate() {
53 | console.log(this.state.text, 'DidUpdate')
54 | this;
55 | //debugger
56 | console.log('DidUpdate', 'Counter')
57 | },
58 | componentWillReceiveProps(nextProps) {
59 | let state = this.state
60 | console.log('Counter: receiveProps:setState', state === this.state)
61 | },
62 | shouldComponentUpdate(nextProps, nextState) {
63 | console.log('Counter: shouldComponentUpdate')
64 | return true
65 | },
66 | componentWillUnmount() {
67 | console.log('unmount', 'Counter')
68 | },
69 | getNum(e) {
70 | let num = parseInt(this.input.value, 10)
71 | if (typeof num === 'number') {
72 | this.toNum(num)
73 | }
74 | },
75 | getInput(input) {
76 | this.input = input
77 | },
78 | handleChange(e) {
79 | let text = e.target.value.replace(/[^\d]+/, '')
80 | this.setState({ text })
81 | },
82 | render() {
83 | let { props } = this
84 | let { COUNT } = props
85 | var img = {
89 | console.log('onload this.refs', this.refs)
90 | }}
91 | onError={()=>{
92 | console.log('onerror this.refs', this.refs)
93 | }}
94 | />
95 | return (
96 |
97 | { Math.random() > 0.5 && img }
98 |
0.5 ? '' : 'test-ref'} data-test="abaasdf">count: { props.count }
99 | {' '}
100 |
COUNT('INCREMENT') }>+
101 | {' '}
102 |
COUNT('DECREMENT') }>-
103 | {' '}
104 |
COUNT('INCREMENT_IF_ODD') }>incrementIfOdd
105 | {' '}
106 |
input number:
107 | {
112 | COUNT('INCREMENT')
113 | }}
114 | ref={ this.getInput }
115 | id="myinput"
116 | name="myinput" />
117 |
118 |
run
119 |
test link
120 |
121 |
122 | )
123 | }
124 | })
125 |
126 | var Example = React.createClass({
127 | getInitialState() {
128 | return {
129 | val: 0,
130 | test: 0
131 | };
132 | },
133 |
134 | componentDidMount() {
135 | this.setState({val: this.state.val + 1, test1: 1})
136 | console.log('didMount', this.state); // log 1
137 | this.setState({val: this.state.val + 1, test2: 2});
138 | console.log('didMount', this.state); // log 2
139 |
140 | setTimeout(() => {
141 | this.setState({val: this.state.val + 1});
142 | console.log('setTimeout:', this.state); // log 3
143 | this.setState({val: this.state.val + 1});
144 | console.log('setTimeout:', this.state); // log 4
145 | }, 4);
146 | },
147 |
148 | render() {
149 | return {this.state.val}
;
150 | }
151 | });
152 |
153 | const Wrap = React.createClass({
154 | getInitialState() {
155 | return { count: 0 }
156 | },
157 | COUNT(type) {
158 | let { count } = this.state
159 | switch(type) {
160 | case 'INCREMENT':
161 | count += 1
162 | break
163 | case 'DECREMENT':
164 | count -= 1
165 | break
166 | case 'INCREMENT_IF_ODD':
167 | if (count % 2 === 0) {
168 | return
169 | }
170 | count += 1
171 | break
172 | }
173 | this.setState({ count })
174 |
175 | },
176 | componentWillMount() {
177 | console.time('Wrap mount')
178 | // let state = this.state
179 | // this.setState({
180 | // count: this.props.count
181 | // })
182 | // console.log('componentWillMount:setState', state === this.state)
183 | },
184 | componentDidMount() {
185 | console.timeEnd('Wrap mount')
186 | let state = this.state
187 | this.setState({
188 | count: this.props.count * 2
189 | })
190 | console.log('componentDidMount:setState', state === this.state)
191 | console.log(this)
192 | },
193 | componentWillUpdate() {
194 | // debugger
195 | console.log('willUpdate', 'Wrap')
196 | },
197 | componentDidUpdate() {
198 | //debugger
199 | console.log('DidUpdate', 'Wrap')
200 | },
201 | componentWillReceiveProps(props) {
202 | this.setState({
203 | count: props.count
204 | })
205 | console.log('Wrap:receiveProps')
206 | },
207 | shouldComponentUpdate() {
208 | console.log('Wrap: shouldComponentUpdate')
209 | return true
210 | },
211 | componentWillUnmount() {
212 | console.log('unmount', 'wrap')
213 | },
214 | render() {
215 | let example =
216 | // let count = Math.random() > 0.5
217 | // ?
218 | // : null
219 | return
220 |
221 | {example}
222 | {example}
223 |
224 | }
225 | })
226 |
227 |
228 | var wrap =
229 |
230 | let update = count => {
231 | return React.render(
232 | wrap,
233 | document.getElementById('container')
234 | )
235 | }
236 |
237 | let num = 10
238 | update()
239 |
240 | // const context = {
241 | // THIS_IS_IMPORTANT: function() {}
242 | // }
243 |
244 | // class ContextProvider extends React.Component {
245 | // static childContextTypes = context;
246 |
247 | // getChildContext() {
248 | // return this.props.context
249 | // }
250 |
251 | // render() {
252 | // return this.props.children
253 | // }
254 | // }
255 |
256 | // class Test extends React.Component {
257 |
258 | // static contextTypes = context;
259 |
260 | // // We added THIS constructor
261 | // constructor(props) {
262 | // super(props)
263 | // }
264 |
265 | // render() {
266 | // console.info('This should NOT be undefined:', this.context.THIS_IS_IMPORTANT)
267 | // return Test1
268 | // }
269 | // }
270 |
271 | // // Render HTML on the browser
272 | // React.render( , document.body)
273 |
274 |
275 |
276 | // class TestRootUpdateAtDidMount extends React.Component {
277 | // componentDidMount() {
278 | // console.log('TestRootUpdateAtDidMount didMount')
279 | // updateName('TestRootUpdateAtDidMount1', () => console.log('TestRootUpdateAtDidMount1 done'))
280 | // updateName('TestRootUpdateAtDidMount2', () => console.log('TestRootUpdateAtDidMount2 done'))
281 | // updateName('TestRootUpdateAtDidMount3', () => console.log('TestRootUpdateAtDidMount3 done'))
282 | // }
283 | // render() {
284 | // let { props } = this
285 | // console.log('render count', props.name)
286 | // return {props.name} asdfsdf
287 | // }
288 | // }
289 |
290 | // class TestRootUpdateAtDidMountWrapper extends React.Component {
291 | // state = {
292 | // text: 'TestRootUpdateAtDidMountWrapper text'
293 | // };
294 | // componentDidMount() {
295 | // console.log('TestRootUpdateAtDidMountWrapper didMount')
296 | // this.setState({
297 | // text: 'change at didMount'
298 | // })
299 | // this.setState({
300 | // text: 'change at didMount1'
301 | // })
302 | // updateName('TestRootUpdateAtDidMountWrapper')
303 | // }
304 | // render() {
305 | // let { props } = this
306 | // let children = testCount++ > 0 ? : 'init'
307 | // return (
308 | // {this.state.text + ' ' + (this.props.name || 'default name')}
309 | // {children}
310 | //
)
311 | // }
312 | // }
313 |
314 | // let testCount = 0
315 |
316 |
317 | // let Root = props => {
318 |
319 | // return
320 | // }
321 |
322 | // var globalState = {
323 | // name: 'init'
324 | // }
325 |
326 | // var updateName = (name, callback) => {
327 | // console.log('updateName', name)
328 | // globalState = {
329 | // ...globalState,
330 | // name
331 | // }
332 | // renderTest(callback)
333 | // }
334 |
335 | // let renderTest = (callback) => {
336 | // React.render(
337 | // ,
338 | // document.getElementById('container'),
339 | // callback
340 | // )
341 | // }
342 |
343 | // updateName('init')
344 | // updateName('update')
345 |
346 |
347 |
348 |
349 |
350 | // class Test extends React.Component {
351 | // componentWillMount() {
352 | // console.log(this.props.index, 'willMount')
353 | // debugger
354 | // }
355 | // componentDidMount() {
356 | // console.log(this.props.index, 'didMount')
357 | // debugger
358 | // }
359 | // componentWillUnmount() {
360 | // console.log(this.props.index, 'willUnmount')
361 | // }
362 | // render() {
363 | // return {this.props.index}
364 | // }
365 | // }
366 |
367 | // var root = (
368 | //
369 | //
370 | //
371 | //
372 | //
)
373 |
374 | // React.render(root, document.getElementById('container'))
375 |
376 |
377 |
378 |
379 |
380 |
--------------------------------------------------------------------------------
/jest/preprocessor.js:
--------------------------------------------------------------------------------
1 | // jest/preprocessor.js
2 | var babelJest = require('babel-jest')
3 | var webpackAlias = require('jest-webpack-alias')
4 |
5 | module.exports = {
6 | process: function(src, filename) {
7 | if (filename.indexOf('node_modules') === -1) {
8 | src = babelJest.process(src, filename)
9 | src = webpackAlias.process(src, filename)
10 | }
11 | return src
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-lite",
3 | "version": "0.15.40",
4 | "description": "an implementation of React that optimizes for small script size",
5 | "main": "dist/react-lite.common.js",
6 | "jsnext:main": "src/index.js",
7 | "scripts": {
8 | "test": "jest",
9 | "build:addons": "babel ./addons --out-dir ./lib",
10 | "build": "node build.js && npm run build:addons",
11 | "prepublish": "npm test && npm run build"
12 | },
13 | "publishConfig": {
14 | "registry": "https://registry.npmjs.org"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/Lucifier129/react-lite.git"
19 | },
20 | "jest": {
21 | "scriptPreprocessor": "/jest/preprocessor.js",
22 | "persistModuleRegistryBetweenSpecs": true,
23 | "unmockedModulePathPatterns": [
24 | ""
25 | ]
26 | },
27 | "keywords": [
28 | "react",
29 | "lite",
30 | "react-lite",
31 | "component",
32 | "virtual-dom"
33 | ],
34 | "author": "Jade Gu (https://github.com/Lucifier129)",
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/Lucifier129/react-lite/issues"
38 | },
39 | "homepage": "https://github.com/Lucifier129/react-lite",
40 | "devDependencies": {
41 | "babel": "^5.8.35",
42 | "babel-core": "^5.8.25",
43 | "babel-jest": "^5.3.0",
44 | "babel-loader": "^5.3.2",
45 | "babel-runtime": "^5.8.25",
46 | "jest-cli": "^0.8.1",
47 | "jest-webpack-alias": "^2.0.0",
48 | "rollup": "^0.21.0",
49 | "rollup-plugin-babel": "^1.0.0",
50 | "rollup-plugin-replace": "^1.1.0",
51 | "uglify-js": "^2.6.1",
52 | "webpack": "^1.12.2"
53 | },
54 | "npmName": "react-lite",
55 | "npmFileMap": [{
56 | "basePath": "/dist/",
57 | "files": [
58 | "*.js"
59 | ]
60 | }]
61 | }
--------------------------------------------------------------------------------
/src/CSSPropertyOperations.js:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS Property Operations
3 | */
4 |
5 | export function setStyle(elemStyle, styles) {
6 | for (let styleName in styles) {
7 | if (styles.hasOwnProperty(styleName)) {
8 | setStyleValue(elemStyle, styleName, styles[styleName])
9 | }
10 | }
11 | }
12 |
13 | export function removeStyle(elemStyle, styles) {
14 | for (let styleName in styles) {
15 | if (styles.hasOwnProperty(styleName)) {
16 | elemStyle[styleName] = ''
17 | }
18 | }
19 | }
20 |
21 | export function patchStyle(elemStyle, style, newStyle) {
22 | if (style === newStyle) {
23 | return
24 | }
25 | if (!newStyle && style) {
26 | removeStyle(elemStyle, style)
27 | return
28 | } else if (newStyle && !style) {
29 | setStyle(elemStyle, newStyle)
30 | return
31 | }
32 |
33 | for (let key in style) {
34 | if (newStyle.hasOwnProperty(key)) {
35 | if (newStyle[key] !== style[key]) {
36 | setStyleValue(elemStyle, key, newStyle[key])
37 | }
38 | } else {
39 | elemStyle[key] = ''
40 | }
41 | }
42 | for (let key in newStyle) {
43 | if (!style.hasOwnProperty(key)) {
44 | setStyleValue(elemStyle, key, newStyle[key])
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * CSS properties which accept numbers but are not in units of "px".
51 | */
52 | const isUnitlessNumber = {
53 | animationIterationCount: 1,
54 | borderImageOutset: 1,
55 | borderImageSlice: 1,
56 | borderImageWidth: 1,
57 | boxFlex: 1,
58 | boxFlexGroup: 1,
59 | boxOrdinalGroup: 1,
60 | columnCount: 1,
61 | flex: 1,
62 | flexGrow: 1,
63 | flexPositive: 1,
64 | flexShrink: 1,
65 | flexNegative: 1,
66 | flexOrder: 1,
67 | gridRow: 1,
68 | gridColumn: 1,
69 | fontWeight: 1,
70 | lineClamp: 1,
71 | lineHeight: 1,
72 | opacity: 1,
73 | order: 1,
74 | orphans: 1,
75 | tabSize: 1,
76 | widows: 1,
77 | zIndex: 1,
78 | zoom: 1,
79 |
80 | // SVG-related properties
81 | fillOpacity: 1,
82 | floodOpacity: 1,
83 | stopOpacity: 1,
84 | strokeDasharray: 1,
85 | strokeDashoffset: 1,
86 | strokeMiterlimit: 1,
87 | strokeOpacity: 1,
88 | strokeWidth: 1,
89 | }
90 |
91 | function prefixKey(prefix, key) {
92 | return prefix + key.charAt(0).toUpperCase() + key.substring(1)
93 | }
94 |
95 | let prefixes = ['Webkit', 'ms', 'Moz', 'O']
96 |
97 | Object.keys(isUnitlessNumber).forEach(function(prop) {
98 | prefixes.forEach(function(prefix) {
99 | isUnitlessNumber[prefixKey(prefix, prop)] = 1
100 | })
101 | })
102 |
103 | let RE_NUMBER = /^-?\d+(\.\d+)?$/
104 | function setStyleValue(elemStyle, styleName, styleValue) {
105 |
106 | if (!isUnitlessNumber[styleName] && RE_NUMBER.test(styleValue)) {
107 | elemStyle[styleName] = styleValue + 'px'
108 | return
109 | }
110 |
111 | if (styleName === 'float') {
112 | styleName = 'cssFloat'
113 | }
114 |
115 | if (styleValue == null || typeof styleValue === 'boolean') {
116 | styleValue = ''
117 | }
118 |
119 | elemStyle[styleName] = styleValue
120 | }
--------------------------------------------------------------------------------
/src/Children.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import { cloneElement, isValidElement } from './createElement'
3 |
4 | export function only(children) {
5 | if (isValidElement(children)) {
6 | return children
7 | }
8 | throw new Error('expect only one child')
9 | }
10 |
11 | export function forEach(children, iteratee, context) {
12 | if (children == null) {
13 | return children
14 | }
15 | let index = 0
16 | if (_.isArr(children)) {
17 | _.flatEach(children, child => {
18 | // from traverseAllChildrenImpl in react
19 | var type = typeof child
20 | if (type === 'undefined' || type === 'boolean') {
21 | // All of the above are perceived as null.
22 | child = null
23 | }
24 |
25 | iteratee.call(context, child, index++)
26 | })
27 | } else {
28 | // from traverseAllChildrenImpl in react
29 | var type = typeof children
30 | if (type === 'undefined' || type === 'boolean') {
31 | // All of the above are perceived as null.
32 | children = null
33 | }
34 | iteratee.call(context, children, index)
35 | }
36 | }
37 |
38 | export function map(children, iteratee, context) {
39 | if (children == null) {
40 | return children
41 | }
42 | let store = []
43 | let keyMap = {}
44 | forEach(children, (child, index) => {
45 | let data = {}
46 | data.child = iteratee.call(context, child, index) || child
47 | data.isEqual = data.child === child
48 | let key = data.key = getKey(child, index)
49 | if (keyMap.hasOwnProperty(key)) {
50 | keyMap[key] += 1
51 | } else {
52 | keyMap[key] = 0
53 | }
54 | data.index = keyMap[key]
55 | _.addItem(store, data)
56 | })
57 | let result = []
58 | store.forEach(({ child, key, index, isEqual }) => {
59 | if (child == null || typeof child === 'boolean') {
60 | return
61 | }
62 | if (!isValidElement(child) || key == null) {
63 | _.addItem(result, child)
64 | return
65 | }
66 | if (keyMap[key] !== 0) {
67 | key += ':' + index
68 | }
69 | if (!isEqual) {
70 | key = escapeUserProvidedKey(child.key || '') + '/' + key
71 | }
72 | child = cloneElement(child, { key })
73 | _.addItem(result, child)
74 | })
75 | return result
76 | }
77 |
78 | export function count(children) {
79 | let count = 0
80 | forEach(children, () => {
81 | count++
82 | })
83 | return count
84 | }
85 |
86 | export function toArray(children) {
87 | return map(children, _.identity) || []
88 | }
89 |
90 | function getKey(child, index) {
91 | let key
92 | if (isValidElement(child) && typeof child.key === 'string') {
93 | key = '.$' + child.key
94 | } else {
95 | key = '.' + index.toString(36)
96 | }
97 | return key
98 | }
99 |
100 | let userProvidedKeyEscapeRegex = /\/(?!\/)/g;
101 | function escapeUserProvidedKey(text) {
102 | return ('' + text).replace(userProvidedKeyEscapeRegex, '//')
103 | }
--------------------------------------------------------------------------------
/src/Component.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import {
3 | renderComponent,
4 | clearPending,
5 | compareTwoVnodes,
6 | getChildContext,
7 | syncCache
8 | } from './virtual-dom'
9 |
10 | export let updateQueue = {
11 | updaters: [],
12 | isPending: false,
13 | add(updater) {
14 | _.addItem(this.updaters, updater)
15 | },
16 | batchUpdate() {
17 | if (this.isPending) {
18 | return
19 | }
20 | this.isPending = true
21 | /*
22 | each updater.update may add new updater to updateQueue
23 | clear them with a loop
24 | event bubbles from bottom-level to top-level
25 | reverse the updater order can merge some props and state and reduce the refresh times
26 | see Updater.update method below to know why
27 | */
28 | let { updaters } = this
29 | let updater
30 | while (updater = updaters.pop()) {
31 | updater.updateComponent()
32 | }
33 | this.isPending = false
34 | }
35 | }
36 |
37 | function Updater(instance) {
38 | this.instance = instance
39 | this.pendingStates = []
40 | this.pendingCallbacks = []
41 | this.isPending = false
42 | this.nextProps = this.nextContext = null
43 | this.clearCallbacks = this.clearCallbacks.bind(this)
44 | }
45 |
46 | Updater.prototype = {
47 | emitUpdate(nextProps, nextContext) {
48 | this.nextProps = nextProps
49 | this.nextContext = nextContext
50 | // receive nextProps!! should update immediately
51 | nextProps || !updateQueue.isPending
52 | ? this.updateComponent()
53 | : updateQueue.add(this)
54 | },
55 | updateComponent() {
56 | let { instance, pendingStates, nextProps, nextContext } = this
57 | if (nextProps || pendingStates.length > 0) {
58 | nextProps = nextProps || instance.props
59 | nextContext = nextContext || instance.context
60 | this.nextProps = this.nextContext = null
61 | // merge the nextProps and nextState and update by one time
62 | shouldUpdate(instance, nextProps, this.getState(), nextContext, this.clearCallbacks)
63 | }
64 | },
65 | addState(nextState) {
66 | if (nextState) {
67 | _.addItem(this.pendingStates, nextState)
68 | if (!this.isPending) {
69 | this.emitUpdate()
70 | }
71 | }
72 | },
73 | replaceState(nextState) {
74 | let { pendingStates } = this
75 | pendingStates.pop()
76 | // push special params to point out should replace state
77 | _.addItem(pendingStates, [nextState])
78 | },
79 | getState() {
80 | let { instance, pendingStates } = this
81 | let { state, props } = instance
82 | if (pendingStates.length) {
83 | state = _.extend({}, state)
84 | pendingStates.forEach(nextState => {
85 | let isReplace = _.isArr(nextState)
86 | if (isReplace) {
87 | nextState = nextState[0]
88 | }
89 | if (_.isFn(nextState)) {
90 | nextState = nextState.call(instance, state, props)
91 | }
92 | // replace state
93 | if (isReplace) {
94 | state = _.extend({}, nextState)
95 | } else {
96 | _.extend(state, nextState)
97 | }
98 | })
99 | pendingStates.length = 0
100 | }
101 | return state
102 | },
103 | clearCallbacks() {
104 | let { pendingCallbacks, instance } = this
105 | if (pendingCallbacks.length > 0) {
106 | this.pendingCallbacks = []
107 | pendingCallbacks.forEach(callback => callback.call(instance))
108 | }
109 | },
110 | addCallback(callback) {
111 | if (_.isFn(callback)) {
112 | _.addItem(this.pendingCallbacks, callback)
113 | }
114 | }
115 | }
116 |
117 | export default function Component(props, context) {
118 | this.$updater = new Updater(this)
119 | this.$cache = { isMounted: false }
120 | this.props = props
121 | this.state = {}
122 | this.refs = {}
123 | this.context = context
124 | }
125 |
126 | const ReactComponentSymbol = {}
127 |
128 | Component.prototype = {
129 | constructor: Component,
130 | isReactComponent: ReactComponentSymbol,
131 | // getChildContext: _.noop,
132 | // componentWillUpdate: _.noop,
133 | // componentDidUpdate: _.noop,
134 | // componentWillReceiveProps: _.noop,
135 | // componentWillMount: _.noop,
136 | // componentDidMount: _.noop,
137 | // componentWillUnmount: _.noop,
138 | // shouldComponentUpdate(nextProps, nextState) {
139 | // return true
140 | // },
141 | forceUpdate(callback) {
142 | let { $updater, $cache, props, state, context } = this
143 | if (!$cache.isMounted) {
144 | return
145 | }
146 | // if updater is pending, add state to trigger nexttick update
147 | if ($updater.isPending) {
148 | $updater.addState(state)
149 | return;
150 | }
151 | let nextProps = $cache.props || props
152 | let nextState = $cache.state || state
153 | let nextContext = $cache.context || context
154 | let parentContext = $cache.parentContext
155 | let node = $cache.node
156 | let vnode = $cache.vnode
157 | $cache.props = $cache.state = $cache.context = null
158 | $updater.isPending = true
159 | if (this.componentWillUpdate) {
160 | this.componentWillUpdate(nextProps, nextState, nextContext)
161 | }
162 | this.state = nextState
163 | this.props = nextProps
164 | this.context = nextContext
165 | let newVnode = renderComponent(this)
166 | let newNode = compareTwoVnodes(vnode, newVnode, node, getChildContext(this, parentContext))
167 | if (newNode !== node) {
168 | newNode.cache = newNode.cache || {}
169 | syncCache(newNode.cache, node.cache, newNode)
170 | }
171 | $cache.vnode = newVnode
172 | $cache.node = newNode
173 | clearPending()
174 | if (this.componentDidUpdate) {
175 | this.componentDidUpdate(props, state, context)
176 | }
177 | if (callback) {
178 | callback.call(this)
179 | }
180 | $updater.isPending = false
181 | $updater.emitUpdate()
182 | },
183 | setState(nextState, callback) {
184 | let { $updater } = this
185 | $updater.addCallback(callback)
186 | $updater.addState(nextState)
187 | },
188 | replaceState(nextState, callback) {
189 | let { $updater } = this
190 | $updater.addCallback(callback)
191 | $updater.replaceState(nextState)
192 | },
193 | getDOMNode() {
194 | let node = this.$cache.node
195 | return node && (node.nodeName === '#comment') ? null : node
196 | },
197 | isMounted() {
198 | return this.$cache.isMounted
199 | }
200 | }
201 |
202 | function shouldUpdate(component, nextProps, nextState, nextContext, callback) {
203 | let shouldComponentUpdate = true
204 | if (component.shouldComponentUpdate) {
205 | shouldComponentUpdate = component.shouldComponentUpdate(nextProps, nextState, nextContext)
206 | }
207 | if (shouldComponentUpdate === false) {
208 | component.props = nextProps
209 | component.state = nextState
210 | component.context = nextContext || {}
211 | if(callback) {
212 | callback.call(component)
213 | }
214 | return
215 | }
216 | let cache = component.$cache
217 | cache.props = nextProps
218 | cache.state = nextState
219 | cache.context = nextContext || {}
220 | component.forceUpdate(callback)
221 | }
222 |
--------------------------------------------------------------------------------
/src/DOM.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import { createFactory } from './createElement'
3 |
4 | let tagNames = 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|circle|clipPath|defs|ellipse|g|image|line|linearGradient|mask|path|pattern|polygon|polyline|radialGradient|rect|stop|svg|text|tspan'
5 | let DOM = {}
6 | tagNames.split('|').forEach(tagName => {
7 | DOM[tagName] = createFactory(tagName)
8 | })
9 |
10 | export default DOM
--------------------------------------------------------------------------------
/src/DOMPropertyOperations.js:
--------------------------------------------------------------------------------
1 | /**
2 | * DOM Property Operations
3 | */
4 |
5 | import {
6 | properties,
7 | isCustomAttribute,
8 | VALID_ATTRIBUTE_NAME_REGEX
9 | } from './DOMConfig'
10 | /**
11 | * Sets the value for a property on a node.
12 | *
13 | * @param {DOMElement} node
14 | * @param {string} name
15 | * @param {*} value
16 | */
17 | export function setPropValue(node, name, value) {
18 | let propInfo = properties.hasOwnProperty(name) && properties[name]
19 | if (propInfo) {
20 | // should delete value from dom
21 | if (value == null ||
22 | (propInfo.hasBooleanValue && !value) ||
23 | (propInfo.hasNumericValue && isNaN(value)) ||
24 | (propInfo.hasPositiveNumericValue && (value < 1)) ||
25 | (propInfo.hasOverloadedBooleanValue && value === false)) {
26 | removePropValue(node, name)
27 | } else if (propInfo.mustUseProperty) {
28 | node[propInfo.propertyName] = value
29 | } else {
30 | let attributeName = propInfo.attributeName
31 | let namespace = propInfo.attributeNamespace
32 |
33 | // `setAttribute` with objects becomes only `[object]` in IE8/9,
34 | // ('' + value) makes it output the correct toString()-value.
35 | if (namespace) {
36 | node.setAttributeNS(namespace, attributeName, '' + value)
37 | } else if (propInfo.hasBooleanValue || (propInfo.hasOverloadedBooleanValue && value === true)) {
38 | node.setAttribute(attributeName, '')
39 | } else {
40 | node.setAttribute(attributeName, '' + value)
41 | }
42 | }
43 | } else if (isCustomAttribute(name) && VALID_ATTRIBUTE_NAME_REGEX.test(name)) {
44 | if (value == null) {
45 | node.removeAttribute(name)
46 | } else {
47 | node.setAttribute(name, '' + value)
48 | }
49 | }
50 | }
51 |
52 | /**
53 | * Deletes the value for a property on a node.
54 | *
55 | * @param {DOMElement} node
56 | * @param {string} name
57 | */
58 | export function removePropValue(node, name) {
59 | let propInfo = properties.hasOwnProperty(name) && properties[name]
60 | if (propInfo) {
61 | if (propInfo.mustUseProperty) {
62 | let propName = propInfo.propertyName;
63 | if (propInfo.hasBooleanValue) {
64 | node[propName] = false
65 | } else {
66 | node[propName] = ''
67 | }
68 | } else {
69 | node.removeAttribute(propInfo.attributeName)
70 | }
71 | } else if (isCustomAttribute(name)) {
72 | node.removeAttribute(name)
73 | }
74 | }
75 |
76 | export function updateSelectOptions(select, multiple, propValue) {
77 | var selectedValue, i
78 | var options = select.options
79 |
80 | if (multiple) {
81 | select.multiple = true
82 | if (!Array.isArray(propValue)) {
83 | throw new Error('The value prop supplied to must be an array if `multiple` is true')
84 | }
85 | selectedValue = {}
86 | for (i = 0; i < propValue.length; i++) {
87 | selectedValue['' + propValue[i]] = true
88 | }
89 | for (i = 0; i < options.length; i++) {
90 | var selected = selectedValue.hasOwnProperty(options[i].value)
91 | if (options[i].selected !== selected) {
92 | options[i].selected = selected
93 | }
94 | }
95 | } else {
96 | select.multiple = false
97 | if (Array.isArray(propValue)) {
98 | throw new Error('The value prop supplied to must be a scalar value if `multiple` is false.')
99 | }
100 | // Do not set `select.value` as exact behavior isn't consistent across all
101 | // browsers for all cases.
102 | selectedValue = '' + propValue
103 | for (i = 0; i < options.length; i++) {
104 | var option = options[i]
105 | if (option.value === selectedValue) {
106 | if (!option.selected) {
107 | option.selected = true
108 | }
109 | } else {
110 | if (option.selected) {
111 | option.selected = false
112 | }
113 | }
114 | }
115 |
116 | if (options.selectedIndex < 0 && options.length) {
117 | options[0].selected = true
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/src/PropTypes.js:
--------------------------------------------------------------------------------
1 | let check = () => check
2 | check.isRequired = check
3 | let PropTypes = {
4 | "array": check,
5 | "bool": check,
6 | "func": check,
7 | "number": check,
8 | "object": check,
9 | "string": check,
10 | "any": check,
11 | "arrayOf": check,
12 | "element": check,
13 | "instanceOf": check,
14 | "node": check,
15 | "objectOf": check,
16 | "oneOf": check,
17 | "oneOfType": check,
18 | "shape": check
19 | }
20 |
21 | export default PropTypes
--------------------------------------------------------------------------------
/src/PureComponent.js:
--------------------------------------------------------------------------------
1 | import shallowEqual from './shallowEqual'
2 | import Component from './Component'
3 |
4 | export default function PureComponent(props, context) {
5 | Component.call(this, props, context)
6 | }
7 |
8 | PureComponent.prototype = Object.create(Component.prototype)
9 | PureComponent.prototype.constructor = PureComponent
10 | PureComponent.prototype.isPureReactComponent = true
11 | PureComponent.prototype.shouldComponentUpdate = shallowCompare
12 |
13 | function shallowCompare(nextProps, nextState) {
14 | return !shallowEqual(this.props, nextProps) ||
15 | !shallowEqual(this.state, nextState)
16 | }
--------------------------------------------------------------------------------
/src/ReactDOM.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import {
3 | COMPONENT_ID,
4 | VELEMENT,
5 | VCOMPONENT,
6 | ELEMENT_NODE_TYPE,
7 | DOC_NODE_TYPE,
8 | DOCUMENT_FRAGMENT_NODE_TYPE
9 | } from './constant'
10 | import { initVnode, destroyVnode, clearPending, compareTwoVnodes } from './virtual-dom'
11 | import { updateQueue } from './Component'
12 |
13 | function isValidContainer(node) {
14 | return !!(node && (
15 | node.nodeType === ELEMENT_NODE_TYPE ||
16 | node.nodeType === DOC_NODE_TYPE ||
17 | node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE
18 | ))
19 | }
20 |
21 | let pendingRendering = {}
22 | let vnodeStore = {}
23 | function renderTreeIntoContainer(vnode, container, callback, parentContext) {
24 | if (!vnode.vtype) {
25 | throw new Error(`cannot render ${ vnode } to container`)
26 | }
27 | if (!isValidContainer(container)) {
28 | throw new Error(`container ${container} is not a DOM element`)
29 | }
30 | let id = container[COMPONENT_ID] || (container[COMPONENT_ID] = _.getUid())
31 | let argsCache = pendingRendering[id]
32 |
33 | // component lify cycle method maybe call root rendering
34 | // should bundle them and render by only one time
35 | if (argsCache) {
36 | if (argsCache === true) {
37 | pendingRendering[id] = argsCache = { vnode, callback, parentContext }
38 | } else {
39 | argsCache.vnode = vnode
40 | argsCache.parentContext = parentContext
41 | argsCache.callback = argsCache.callback ? _.pipe(argsCache.callback, callback) : callback
42 | }
43 | return
44 | }
45 |
46 | pendingRendering[id] = true
47 | let oldVnode = null
48 | let rootNode = null
49 | if (oldVnode = vnodeStore[id]) {
50 | rootNode = compareTwoVnodes(oldVnode, vnode, container.firstChild, parentContext)
51 | } else {
52 | rootNode = initVnode(vnode, parentContext, container.namespaceURI)
53 | var childNode = null
54 | while (childNode = container.lastChild) {
55 | container.removeChild(childNode)
56 | }
57 | container.appendChild(rootNode)
58 | }
59 | vnodeStore[id] = vnode
60 | let isPending = updateQueue.isPending
61 | updateQueue.isPending = true
62 | clearPending()
63 | argsCache = pendingRendering[id]
64 | delete pendingRendering[id]
65 |
66 | let result = null
67 | if (typeof argsCache === 'object') {
68 | result = renderTreeIntoContainer(argsCache.vnode, container, argsCache.callback, argsCache.parentContext)
69 | } else if (vnode.vtype === VELEMENT) {
70 | result = rootNode
71 | } else if (vnode.vtype === VCOMPONENT) {
72 | result = rootNode.cache[vnode.uid]
73 | }
74 |
75 | if (!isPending) {
76 | updateQueue.isPending = false
77 | updateQueue.batchUpdate()
78 | }
79 |
80 | if (callback) {
81 | callback.call(result)
82 | }
83 |
84 | return result
85 | }
86 |
87 | export function render(vnode, container, callback) {
88 | return renderTreeIntoContainer(vnode, container, callback)
89 | }
90 |
91 | export function unstable_renderSubtreeIntoContainer(parentComponent, subVnode, container, callback) {
92 | let context = parentComponent.$cache.parentContext
93 | return renderTreeIntoContainer(subVnode, container, callback, context)
94 | }
95 |
96 | export function unmountComponentAtNode(container) {
97 | if (!container.nodeName) {
98 | throw new Error('expect node')
99 | }
100 | let id = container[COMPONENT_ID]
101 | let vnode = null
102 | if (vnode = vnodeStore[id]) {
103 | destroyVnode(vnode, container.firstChild)
104 | container.removeChild(container.firstChild)
105 | delete vnodeStore[id]
106 | return true
107 | }
108 | return false
109 | }
110 |
111 | export function findDOMNode(node) {
112 | if (node == null) {
113 | return null
114 | }
115 | if (node.nodeName) {
116 | return node
117 | }
118 | let component = node
119 | // if component.node equal to false, component must be unmounted
120 | if (component.getDOMNode && component.$cache.isMounted) {
121 | return component.getDOMNode()
122 | }
123 | throw new Error('findDOMNode can not find Node')
124 | }
--------------------------------------------------------------------------------
/src/constant.js:
--------------------------------------------------------------------------------
1 | /*
2 | key/value configs
3 | */
4 | export const HTML_KEY = 'dangerouslySetInnerHTML'
5 | export const SVGNamespaceURI = 'http://www.w3.org/2000/svg'
6 | export const COMPONENT_ID = 'liteid'
7 | export const VTEXT = 1
8 | export const VELEMENT = 2
9 | export const VSTATELESS = 3
10 | export const VCOMPONENT = 4
11 | export const VCOMMENT = 5
12 | export const CREATE = 1
13 | export const REMOVE = 2
14 | export const UPDATE = 3
15 | export const ELEMENT_NODE_TYPE = 1
16 | export const DOC_NODE_TYPE = 9
17 | export const DOCUMENT_FRAGMENT_NODE_TYPE = 11
18 |
--------------------------------------------------------------------------------
/src/createClass.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import Component from './Component'
3 |
4 | function eachMixin(mixins, iteratee) {
5 | mixins.forEach(mixin => {
6 | if (mixin) {
7 | if (_.isArr(mixin.mixins)) {
8 | eachMixin(mixin.mixins, iteratee)
9 | }
10 | iteratee(mixin)
11 | }
12 | })
13 | }
14 |
15 | function combineMixinToProto(proto, mixin) {
16 | for (let key in mixin) {
17 | if (!mixin.hasOwnProperty(key)) {
18 | continue
19 | }
20 | let value = mixin[key]
21 | if (key === 'getInitialState') {
22 | _.addItem(proto.$getInitialStates, value)
23 | continue
24 | }
25 | let curValue = proto[key]
26 | if (_.isFn(curValue) && _.isFn(value)) {
27 | proto[key] = _.pipe(curValue, value)
28 | } else {
29 | proto[key] = value
30 | }
31 | }
32 | }
33 |
34 | function combineMixinToClass(Component, mixin) {
35 | if (mixin.propTypes) {
36 | Component.propTypes = Component.propTypes || {}
37 | _.extend(Component.propTypes, mixin.propTypes)
38 | }
39 | if (mixin.contextTypes) {
40 | Component.contextTypes = Component.contextTypes || {}
41 | _.extend(Component.contextTypes, mixin.contextTypes)
42 | }
43 | _.extend(Component, mixin.statics)
44 | if (_.isFn(mixin.getDefaultProps)) {
45 | Component.defaultProps = Component.defaultProps || {}
46 | _.extend(Component.defaultProps, mixin.getDefaultProps())
47 | }
48 | }
49 |
50 | function bindContext(obj, source) {
51 | for (let key in source) {
52 | if (source.hasOwnProperty(key)) {
53 | if (_.isFn(source[key])) {
54 | obj[key] = source[key].bind(obj)
55 | }
56 | }
57 | }
58 | }
59 |
60 | let Facade = function() {}
61 | Facade.prototype = Component.prototype
62 |
63 | function getInitialState() {
64 | let state = {}
65 | let setState = this.setState
66 | this.setState = Facade
67 | this.$getInitialStates.forEach(getInitialState => {
68 | if (_.isFn(getInitialState)) {
69 | _.extend(state, getInitialState.call(this))
70 | }
71 | })
72 | this.setState = setState
73 | return state
74 | }
75 |
76 | export default function createClass(spec) {
77 | if (!_.isFn(spec.render)) {
78 | throw new Error('createClass: spec.render is not function')
79 | }
80 | let specMixins = spec.mixins || []
81 | let mixins = specMixins.concat(spec)
82 | spec.mixins = null
83 | function Klass(props, context) {
84 | Component.call(this, props, context)
85 | this.constructor = Klass
86 | spec.autobind !== false && bindContext(this, Klass.prototype)
87 | this.state = this.getInitialState() || this.state
88 | }
89 | Klass.displayName = spec.displayName
90 | let proto = Klass.prototype = new Facade()
91 | proto.$getInitialStates = []
92 | eachMixin(mixins, mixin => {
93 | combineMixinToProto(proto, mixin)
94 | combineMixinToClass(Klass, mixin)
95 | })
96 | proto.getInitialState = getInitialState
97 | spec.mixins = specMixins
98 | return Klass
99 | }
--------------------------------------------------------------------------------
/src/createElement.js:
--------------------------------------------------------------------------------
1 | import * as _ from './util'
2 | import {
3 | VELEMENT,
4 | VSTATELESS,
5 | VCOMPONENT,
6 | VCOMMENT
7 | } from './constant'
8 | import { createVnode } from './virtual-dom'
9 |
10 | export default function createElement(type, props, children) {
11 | let vtype = null
12 | if (typeof type === 'string') {
13 | vtype = VELEMENT
14 | } else if (typeof type === 'function') {
15 | if (type.prototype && type.prototype.isReactComponent) {
16 | vtype = VCOMPONENT
17 | } else {
18 | vtype = VSTATELESS
19 | }
20 | } else {
21 | throw new Error(`React.createElement: unexpect type [ ${type} ]`)
22 | }
23 |
24 | let key = null
25 | let ref = null
26 | let finalProps = {}
27 | if (props != null) {
28 | for (let propKey in props) {
29 | if (!props.hasOwnProperty(propKey)) {
30 | continue
31 | }
32 | if (propKey === 'key') {
33 | if (props.key !== undefined) {
34 | key = '' + props.key
35 | }
36 | } else if (propKey === 'ref') {
37 | if (props.ref !== undefined) {
38 | ref = props.ref
39 | }
40 | } else {
41 | finalProps[propKey] = props[propKey]
42 | }
43 | }
44 | }
45 |
46 | let defaultProps = type.defaultProps
47 |
48 | if (defaultProps) {
49 | for (let propKey in defaultProps) {
50 | if (finalProps[propKey] === undefined) {
51 | finalProps[propKey] = defaultProps[propKey]
52 | }
53 | }
54 | }
55 |
56 | let argsLen = arguments.length
57 | let finalChildren = children
58 |
59 | if (argsLen > 3) {
60 | finalChildren = Array(argsLen - 2)
61 | for (let i = 2; i < argsLen; i++) {
62 | finalChildren[i - 2] = arguments[i]
63 | }
64 | }
65 |
66 | if (finalChildren !== undefined) {
67 | finalProps.children = finalChildren
68 | }
69 |
70 | return createVnode(vtype, type, finalProps, key, ref)
71 | }
72 |
73 | export function isValidElement(obj) {
74 | return obj != null && !!obj.vtype
75 | }
76 |
77 | export function cloneElement(originElem, props, ...children) {
78 | let { type, key, ref } = originElem
79 | let newProps = _.extend(_.extend({ key, ref }, originElem.props), props)
80 | let vnode = createElement(type, newProps, ...children)
81 | if (vnode.ref === originElem.ref) {
82 | vnode.refs = originElem.refs
83 | }
84 | return vnode
85 | }
86 |
87 | export function createFactory(type) {
88 | let factory = (...args) => createElement(type, ...args)
89 | factory.type = type
90 | return factory
91 | }
--------------------------------------------------------------------------------
/src/event-system.js:
--------------------------------------------------------------------------------
1 | import { updateQueue } from './Component'
2 | import * as _ from './util'
3 |
4 | // event config
5 | export const unbubbleEvents = {
6 | /**
7 | * should not bind mousemove in document scope
8 | * even though mousemove event can bubble
9 | */
10 | onmousemove: 1,
11 | ontouchmove: 1,
12 | onmouseleave: 1,
13 | onmouseenter: 1,
14 | onload: 1,
15 | onunload: 1,
16 | onscroll: 1,
17 | onfocus: 1,
18 | onblur: 1,
19 | onrowexit: 1,
20 | onbeforeunload: 1,
21 | onstop: 1,
22 | ondragdrop: 1,
23 | ondragenter: 1,
24 | ondragexit: 1,
25 | ondraggesture: 1,
26 | ondragover: 1,
27 | oncontextmenu: 1,
28 | onerror: 1,
29 |
30 | // media event
31 | onabort: 1,
32 | oncanplay: 1,
33 | oncanplaythrough: 1,
34 | ondurationchange: 1,
35 | onemptied: 1,
36 | onended: 1,
37 | onloadeddata: 1,
38 | onloadedmetadata: 1,
39 | onloadstart: 1,
40 | onencrypted: 1,
41 | onpause: 1,
42 | onplay: 1,
43 | onplaying: 1,
44 | onprogress: 1,
45 | onratechange: 1,
46 | onseeking: 1,
47 | onseeked: 1,
48 | onstalled: 1,
49 | onsuspend: 1,
50 | ontimeupdate: 1,
51 | onvolumechange: 1,
52 | onwaiting: 1,
53 | }
54 |
55 | export function getEventName(key) {
56 | if (key === 'onDoubleClick') {
57 | key = 'ondblclick'
58 | } else if (key === 'onTouchTap') {
59 | key = 'onclick'
60 | }
61 |
62 | return key.toLowerCase()
63 | }
64 |
65 |
66 | // Mobile Safari does not fire properly bubble click events on
67 | // non-interactive elements, which means delegated click listeners do not
68 | // fire. The workaround for this bug involves attaching an empty click
69 | // listener on the target node.
70 | let inMobile = 'ontouchstart' in document
71 | let emptyFunction = () => {}
72 | let ON_CLICK_KEY = 'onclick'
73 |
74 | let eventTypes = {}
75 | export function addEvent(elem, eventType, listener) {
76 | eventType = getEventName(eventType)
77 |
78 | let eventStore = elem.eventStore || (elem.eventStore = {})
79 | eventStore[eventType] = listener
80 |
81 | if (unbubbleEvents[eventType] === 1) {
82 | elem[eventType] = dispatchUnbubbleEvent
83 | return
84 | } else if (!eventTypes[eventType]) {
85 | // onclick -> click
86 | document.addEventListener(eventType.substr(2), dispatchEvent, false)
87 | eventTypes[eventType] = true
88 | }
89 |
90 | if (inMobile && eventType === ON_CLICK_KEY) {
91 | elem.addEventListener('click', emptyFunction, false)
92 | return
93 | }
94 |
95 | let nodeName = elem.nodeName
96 |
97 | if (eventType === 'onchange' && supportInputEvent(elem)) {
98 | addEvent(elem, 'oninput', listener)
99 | }
100 | }
101 |
102 | export function removeEvent(elem, eventType) {
103 | eventType = getEventName(eventType)
104 |
105 | let eventStore = elem.eventStore || (elem.eventStore = {})
106 | delete eventStore[eventType]
107 |
108 | if (unbubbleEvents[eventType] === 1) {
109 | elem[eventType] = null
110 | return
111 | } else if (inMobile && eventType === ON_CLICK_KEY) {
112 | elem.removeEventListener('click', emptyFunction, false)
113 | return
114 | }
115 |
116 | let nodeName = elem.nodeName
117 |
118 | if (eventType === 'onchange' && supportInputEvent(elem)) {
119 | delete eventStore['oninput']
120 | }
121 | }
122 |
123 | function dispatchEvent(event) {
124 | let { target, type } = event
125 | let eventType = 'on' + type
126 | let syntheticEvent
127 |
128 | updateQueue.isPending = true
129 | while (target) {
130 | let { eventStore } = target
131 | let listener = eventStore && eventStore[eventType]
132 | if (!listener) {
133 | target = target.parentNode
134 | continue
135 | }
136 | if (!syntheticEvent) {
137 | syntheticEvent = createSyntheticEvent(event)
138 | }
139 | syntheticEvent.currentTarget = target
140 | listener.call(target, syntheticEvent)
141 | if (syntheticEvent.$cancelBubble) {
142 | break
143 | }
144 | target = target.parentNode
145 | }
146 | updateQueue.isPending = false
147 | updateQueue.batchUpdate()
148 | }
149 |
150 | function dispatchUnbubbleEvent(event) {
151 | let target = event.currentTarget || event.target
152 | let eventType = 'on' + event.type
153 | let syntheticEvent = createSyntheticEvent(event)
154 |
155 | syntheticEvent.currentTarget = target
156 | updateQueue.isPending = true
157 |
158 | let { eventStore } = target
159 | let listener = eventStore && eventStore[eventType]
160 | if (listener) {
161 | listener.call(target, syntheticEvent)
162 | }
163 |
164 | updateQueue.isPending = false
165 | updateQueue.batchUpdate()
166 | }
167 |
168 | function createSyntheticEvent(nativeEvent) {
169 | let syntheticEvent = {}
170 | let cancelBubble = () => syntheticEvent.$cancelBubble = true
171 | syntheticEvent.nativeEvent = nativeEvent
172 | syntheticEvent.persist = _.noop
173 | for (let key in nativeEvent) {
174 | if (typeof nativeEvent[key] !== 'function') {
175 | syntheticEvent[key] = nativeEvent[key]
176 | } else if (key === 'stopPropagation' || key === 'stopImmediatePropagation') {
177 | syntheticEvent[key] = cancelBubble
178 | } else {
179 | syntheticEvent[key] = nativeEvent[key].bind(nativeEvent)
180 | }
181 | }
182 | return syntheticEvent
183 | }
184 |
185 | function supportInputEvent(elem) {
186 | let nodeName = elem.nodeName && elem.nodeName.toLowerCase()
187 | return nodeName !== 'select' && !(nodeName === 'input' && elem.type === 'file')
188 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Component from './Component'
2 | import PureComponent from './PureComponent'
3 | import createClass from './createClass'
4 | import createElement, { isValidElement, cloneElement, createFactory } from './createElement'
5 | import * as Children from './Children'
6 | import * as ReactDOM from './ReactDOM'
7 | import PropTypes from './PropTypes'
8 | import DOM from './DOM'
9 | import * as _ from './util'
10 |
11 | let React = _.extend({
12 | version: '0.15.1',
13 | cloneElement,
14 | isValidElement,
15 | createElement,
16 | createFactory,
17 | Component,
18 | PureComponent,
19 | createClass,
20 | Children,
21 | PropTypes,
22 | DOM
23 | }, ReactDOM)
24 |
25 | React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactDOM
26 |
27 | export default React
--------------------------------------------------------------------------------
/src/shallowEqual.js:
--------------------------------------------------------------------------------
1 | export default function shallowEqual(objA, objB) {
2 | if (objA === objB) {
3 | return true
4 | }
5 |
6 | if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
7 | return false
8 | }
9 |
10 | var keysA = Object.keys(objA)
11 | var keysB = Object.keys(objB)
12 |
13 | if (keysA.length !== keysB.length) {
14 | return false
15 | }
16 |
17 | // Test for A's keys different from B.
18 | for (var i = 0; i < keysA.length; i++) {
19 | if (!objB.hasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) {
20 | return false
21 | }
22 | }
23 |
24 | return true
25 | }
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | // util
2 | import { addEvent, removeEvent } from './event-system'
3 | import {
4 | setStyle,
5 | removeStyle,
6 | patchStyle
7 | } from './CSSPropertyOperations.js'
8 | import {
9 | setPropValue,
10 | removePropValue,
11 | updateSelectOptions
12 | } from './DOMPropertyOperations'
13 | import { HTML_KEY } from './constant'
14 |
15 | export function isFn(obj) {
16 | return typeof obj === 'function'
17 | }
18 |
19 | export let isArr = Array.isArray
20 |
21 | export function noop() {}
22 | export function identity(obj) {
23 | return obj
24 | }
25 | export function pipe(fn1, fn2) {
26 | return function() {
27 | fn1.apply(this, arguments)
28 | return fn2.apply(this, arguments)
29 | }
30 | }
31 |
32 | export function addItem(list, item) {
33 | list[list.length] = item
34 | }
35 |
36 | export function flatEach(list, iteratee, a) {
37 | let len = list.length
38 | let i = -1
39 |
40 | while (len--) {
41 | let item = list[++i]
42 | if (isArr(item)) {
43 | flatEach(item, iteratee, a)
44 | } else {
45 | iteratee(item, a)
46 | }
47 | }
48 | }
49 |
50 | export function extend(to, from) {
51 | if (!from) {
52 | return to
53 | }
54 | var keys = Object.keys(from)
55 | var i = keys.length
56 | while (i--) {
57 | to[keys[i]] = from[keys[i]]
58 | }
59 | return to
60 | }
61 |
62 |
63 | let uid = 0
64 | export function getUid() {
65 | return ++uid
66 | }
67 |
68 | export let EVENT_KEYS = /^on/i
69 |
70 | function setProp(elem, key, value, isCustomComponent) {
71 | if (EVENT_KEYS.test(key)) {
72 | addEvent(elem, key, value)
73 | } else if (key === 'style') {
74 | setStyle(elem.style, value)
75 | } else if (key === HTML_KEY) {
76 | if (value && value.__html != null) {
77 | elem.innerHTML = value.__html
78 | }
79 | } else if (isCustomComponent) {
80 | if (value == null) {
81 | elem.removeAttribute(key)
82 | } else {
83 | elem.setAttribute(key, '' + value)
84 | }
85 | } else {
86 | setPropValue(elem, key, value)
87 | }
88 | }
89 |
90 | function removeProp(elem, key, oldValue, isCustomComponent) {
91 | if (EVENT_KEYS.test(key)) {
92 | removeEvent(elem, key)
93 | } else if (key === 'style') {
94 | removeStyle(elem.style, oldValue)
95 | } else if (key === HTML_KEY) {
96 | elem.innerHTML = ''
97 | } else if (isCustomComponent) {
98 | elem.removeAttribute(key)
99 | } else {
100 | removePropValue(elem, key)
101 | }
102 | }
103 |
104 | function patchProp(elem, key, value, oldValue, isCustomComponent) {
105 | if (key === 'value' || key === 'checked') {
106 | oldValue = elem[key]
107 | }
108 | if (value === oldValue) {
109 | return
110 | }
111 | if (value === undefined) {
112 | removeProp(elem, key, oldValue, isCustomComponent)
113 | return
114 | }
115 | if (key === 'style') {
116 | patchStyle(elem.style, oldValue, value)
117 | } else {
118 | setProp(elem, key, value, isCustomComponent)
119 | }
120 | }
121 |
122 | export function setProps(elem, props, isCustomComponent) {
123 | var isSelect = elem.nodeName === 'SELECT'
124 | for (let key in props) {
125 | if (key !== 'children') {
126 | if (isSelect && (key === 'value' || key === 'defaultValue')) {
127 | updateSelectOptions(elem, props.multiple, props[key])
128 | } else {
129 | setProp(elem, key, props[key], isCustomComponent)
130 | }
131 | }
132 | }
133 | }
134 |
135 | export function patchProps(elem, props, newProps, isCustomComponent) {
136 | var isSelect = elem.nodeName === 'SELECT'
137 | for (let key in props) {
138 | if (key !== 'children') {
139 | if (newProps.hasOwnProperty(key)) {
140 | if (isSelect && (key === 'value' || key === 'defaultValue')) {
141 | updateSelectOptions(elem, newProps.multiple, newProps[key])
142 | } else {
143 | patchProp(elem, key, newProps[key], props[key], isCustomComponent)
144 | }
145 | } else {
146 | removeProp(elem, key, props[key], isCustomComponent)
147 | }
148 | }
149 | }
150 | for (let key in newProps) {
151 | if (key !== 'children' && !props.hasOwnProperty(key)) {
152 | if (isSelect && (key === 'value' || key === 'defaultValue')) {
153 | updateSelectOptions(elem, newProps.multiple, newProps[key])
154 | } else {
155 | setProp(elem, key, newProps[key], isCustomComponent)
156 | }
157 | }
158 | }
159 | }
160 |
161 | if (!Object.freeze) {
162 | Object.freeze = identity
163 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var path = require('path');
3 |
4 | module.exports = {
5 | watch: true,
6 | entry: {
7 | simple: './examples/simple/'
8 | },
9 | output: {
10 | path: './examples/',
11 | filename: '[name]/app.js'
12 | },
13 | module: {
14 | loaders: [{
15 | test: /\.jsx?$/,
16 | loader: 'babel-loader',
17 | query: {
18 | stage: 0
19 | },
20 | exclude: /node_modules/
21 | }]
22 | },
23 | resolve: {
24 | extensions: ['', '.js'],
25 | root: __dirname
26 | }
27 | };
28 |
--------------------------------------------------------------------------------