Init: Copy from git@github.com:facebookarchive/react-native-custom-components.git
Showing
35 changed files
with
3716 additions
and
0 deletions
LICENSE
0 → 100644
| 1 | +LICENSE AGREEMENT | ||
| 2 | + | ||
| 3 | +For React Native Custom Components software | ||
| 4 | + | ||
| 5 | +Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 6 | + | ||
| 7 | +Facebook, Inc. (“Facebook”) owns all right, title and interest, including all intellectual property and other proprietary rights, in and to the React Native Custom Components software (the “Software”). Subject to your compliance with these terms, you are hereby granted a non-exclusive, worldwide, royalty-free copyright license to (1) use and copy the Software; and (2) reproduce and distribute the Software as part of your own software (“Your Software”). Facebook reserves all rights not expressly granted to you in this license agreement. | ||
| 8 | + | ||
| 9 | +THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
README.md
0 → 100644
| 1 | +# React Native Legacy Custom Components | ||
| 2 | + | ||
| 3 | +This is a module for legacy "CustomComponents" to gracefully deprecate. | ||
| 4 | + | ||
| 5 | +## Navigator | ||
| 6 | + | ||
| 7 | +The navigator component in this module will behave identically as the one in old version of React native, with one exception: | ||
| 8 | + | ||
| 9 | +Latest documentation is available here: http://facebook.github.io/react-native/releases/0.43/docs/navigator.html | ||
| 10 | + | ||
| 11 | + | ||
| 12 | +### Breaking Changes from react-native | ||
| 13 | + | ||
| 14 | +- Navigator.props.sceneStyle must be a plain object, not a stylesheet! | ||
| 15 | + | ||
| 16 | +(this breaking change is needed to avoid calling React Native's private APIs) |
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "react-native-deprecated-custom-components", | ||
| 3 | + "version": "0.1.1", | ||
| 4 | + "description": "Deprecated custom components that originally shipped with React Native", | ||
| 5 | + "repository": { | ||
| 6 | + "type": "git", | ||
| 7 | + "url": "git@github.com:facebookarchive/react-native-custom-components.git" | ||
| 8 | + }, | ||
| 9 | + "main": "src/CustomComponents.js", | ||
| 10 | + "dependencies": { | ||
| 11 | + "fbjs": "~0.8.9", | ||
| 12 | + "immutable": "~3.7.6", | ||
| 13 | + "create-react-class": "15.6.0", | ||
| 14 | + "prop-types": "^15.5.10", | ||
| 15 | + "react-timer-mixin": "^0.13.2", | ||
| 16 | + "rebound": "^0.0.13" | ||
| 17 | + }, | ||
| 18 | + "peerDependencies": { | ||
| 19 | + "react-native": "*" | ||
| 20 | + } | ||
| 21 | +} |
src/CustomComponents.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +const Navigator = require('./Navigator'); | ||
| 29 | + | ||
| 30 | +module.exports = { | ||
| 31 | + Navigator, | ||
| 32 | +}; |
src/EmitterSubscription.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @noflow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +const EventSubscription = require('./EventSubscription'); | ||
| 30 | + | ||
| 31 | +import type EventEmitter from './EventEmitter'; | ||
| 32 | +import type EventSubscriptionVendor from './EventSubscriptionVendor'; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * EmitterSubscription represents a subscription with listener and context data. | ||
| 36 | + */ | ||
| 37 | +class EmitterSubscription extends EventSubscription { | ||
| 38 | + | ||
| 39 | + emitter: EventEmitter; | ||
| 40 | + listener: Function; | ||
| 41 | + context: ?Object; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * @param {EventEmitter} emitter - The event emitter that registered this | ||
| 45 | + * subscription | ||
| 46 | + * @param {EventSubscriptionVendor} subscriber - The subscriber that controls | ||
| 47 | + * this subscription | ||
| 48 | + * @param {function} listener - Function to invoke when the specified event is | ||
| 49 | + * emitted | ||
| 50 | + * @param {*} context - Optional context object to use when invoking the | ||
| 51 | + * listener | ||
| 52 | + */ | ||
| 53 | + constructor( | ||
| 54 | + emitter: EventEmitter, | ||
| 55 | + subscriber: EventSubscriptionVendor, | ||
| 56 | + listener: Function, | ||
| 57 | + context: ?Object | ||
| 58 | + ) { | ||
| 59 | + super(subscriber); | ||
| 60 | + this.emitter = emitter; | ||
| 61 | + this.listener = listener; | ||
| 62 | + this.context = context; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * Removes this subscription from the emitter that registered it. | ||
| 67 | + * Note: we're overriding the `remove()` method of EventSubscription here | ||
| 68 | + * but deliberately not calling `super.remove()` as the responsibility | ||
| 69 | + * for removing the subscription lies with the EventEmitter. | ||
| 70 | + */ | ||
| 71 | + remove() { | ||
| 72 | + this.emitter.removeSubscription(this); | ||
| 73 | + } | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +module.exports = EmitterSubscription; |
src/EventEmitter.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @noflow | ||
| 26 | + * @typecheck | ||
| 27 | + */ | ||
| 28 | +'use strict'; | ||
| 29 | + | ||
| 30 | +const EmitterSubscription = require('./EmitterSubscription'); | ||
| 31 | +const EventSubscriptionVendor = require('./EventSubscriptionVendor'); | ||
| 32 | + | ||
| 33 | +const emptyFunction = require('fbjs/lib/emptyFunction'); | ||
| 34 | +const invariant = require('fbjs/lib/invariant'); | ||
| 35 | + | ||
| 36 | +/** | ||
| 37 | + * @class EventEmitter | ||
| 38 | + * @description | ||
| 39 | + * An EventEmitter is responsible for managing a set of listeners and publishing | ||
| 40 | + * events to them when it is told that such events happened. In addition to the | ||
| 41 | + * data for the given event it also sends a event control object which allows | ||
| 42 | + * the listeners/handlers to prevent the default behavior of the given event. | ||
| 43 | + * | ||
| 44 | + * The emitter is designed to be generic enough to support all the different | ||
| 45 | + * contexts in which one might want to emit events. It is a simple multicast | ||
| 46 | + * mechanism on top of which extra functionality can be composed. For example, a | ||
| 47 | + * more advanced emitter may use an EventHolder and EventFactory. | ||
| 48 | + */ | ||
| 49 | +class EventEmitter { | ||
| 50 | + | ||
| 51 | + _subscriber: EventSubscriptionVendor; | ||
| 52 | + _currentSubscription: ?EmitterSubscription; | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * @constructor | ||
| 56 | + * | ||
| 57 | + * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance | ||
| 58 | + * to use. If omitted, a new subscriber will be created for the emitter. | ||
| 59 | + */ | ||
| 60 | + constructor(subscriber: ?EventSubscriptionVendor) { | ||
| 61 | + this._subscriber = subscriber || new EventSubscriptionVendor(); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * Adds a listener to be invoked when events of the specified type are | ||
| 66 | + * emitted. An optional calling context may be provided. The data arguments | ||
| 67 | + * emitted will be passed to the listener function. | ||
| 68 | + * | ||
| 69 | + * TODO: Annotate the listener arg's type. This is tricky because listeners | ||
| 70 | + * can be invoked with varargs. | ||
| 71 | + * | ||
| 72 | + * @param {string} eventType - Name of the event to listen to | ||
| 73 | + * @param {function} listener - Function to invoke when the specified event is | ||
| 74 | + * emitted | ||
| 75 | + * @param {*} context - Optional context object to use when invoking the | ||
| 76 | + * listener | ||
| 77 | + */ | ||
| 78 | + addListener( | ||
| 79 | + eventType: string, listener: Function, context: ?Object): EmitterSubscription { | ||
| 80 | + | ||
| 81 | + return (this._subscriber.addSubscription( | ||
| 82 | + eventType, | ||
| 83 | + new EmitterSubscription(this, this._subscriber, listener, context) | ||
| 84 | + ) : any); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * Similar to addListener, except that the listener is removed after it is | ||
| 89 | + * invoked once. | ||
| 90 | + * | ||
| 91 | + * @param {string} eventType - Name of the event to listen to | ||
| 92 | + * @param {function} listener - Function to invoke only once when the | ||
| 93 | + * specified event is emitted | ||
| 94 | + * @param {*} context - Optional context object to use when invoking the | ||
| 95 | + * listener | ||
| 96 | + */ | ||
| 97 | + once(eventType: string, listener: Function, context: ?Object): EmitterSubscription { | ||
| 98 | + return this.addListener(eventType, (...args) => { | ||
| 99 | + this.removeCurrentListener(); | ||
| 100 | + listener.apply(context, args); | ||
| 101 | + }); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + /** | ||
| 105 | + * Removes all of the registered listeners, including those registered as | ||
| 106 | + * listener maps. | ||
| 107 | + * | ||
| 108 | + * @param {?string} eventType - Optional name of the event whose registered | ||
| 109 | + * listeners to remove | ||
| 110 | + */ | ||
| 111 | + removeAllListeners(eventType: ?string) { | ||
| 112 | + this._subscriber.removeAllSubscriptions(eventType); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + /** | ||
| 116 | + * Provides an API that can be called during an eventing cycle to remove the | ||
| 117 | + * last listener that was invoked. This allows a developer to provide an event | ||
| 118 | + * object that can remove the listener (or listener map) during the | ||
| 119 | + * invocation. | ||
| 120 | + * | ||
| 121 | + * If it is called when not inside of an emitting cycle it will throw. | ||
| 122 | + * | ||
| 123 | + * @throws {Error} When called not during an eventing cycle | ||
| 124 | + * | ||
| 125 | + * @example | ||
| 126 | + * var subscription = emitter.addListenerMap({ | ||
| 127 | + * someEvent: function(data, event) { | ||
| 128 | + * console.log(data); | ||
| 129 | + * emitter.removeCurrentListener(); | ||
| 130 | + * } | ||
| 131 | + * }); | ||
| 132 | + * | ||
| 133 | + * emitter.emit('someEvent', 'abc'); // logs 'abc' | ||
| 134 | + * emitter.emit('someEvent', 'def'); // does not log anything | ||
| 135 | + */ | ||
| 136 | + removeCurrentListener() { | ||
| 137 | + invariant( | ||
| 138 | + !!this._currentSubscription, | ||
| 139 | + 'Not in an emitting cycle; there is no current subscription' | ||
| 140 | + ); | ||
| 141 | + this.removeSubscription(this._currentSubscription); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * Removes a specific subscription. Called by the `remove()` method of the | ||
| 146 | + * subscription itself to ensure any necessary cleanup is performed. | ||
| 147 | + */ | ||
| 148 | + removeSubscription(subscription: EmitterSubscription) { | ||
| 149 | + invariant( | ||
| 150 | + subscription.emitter === this, | ||
| 151 | + 'Subscription does not belong to this emitter.' | ||
| 152 | + ); | ||
| 153 | + this._subscriber.removeSubscription(subscription); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + /** | ||
| 157 | + * Returns an array of listeners that are currently registered for the given | ||
| 158 | + * event. | ||
| 159 | + * | ||
| 160 | + * @param {string} eventType - Name of the event to query | ||
| 161 | + * @returns {array} | ||
| 162 | + */ | ||
| 163 | + listeners(eventType: string): [EmitterSubscription] { | ||
| 164 | + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); | ||
| 165 | + return subscriptions | ||
| 166 | + ? subscriptions.filter(emptyFunction.thatReturnsTrue).map( | ||
| 167 | + function(subscription) { | ||
| 168 | + return subscription.listener; | ||
| 169 | + }) | ||
| 170 | + : []; | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + /** | ||
| 174 | + * Emits an event of the given type with the given data. All handlers of that | ||
| 175 | + * particular type will be notified. | ||
| 176 | + * | ||
| 177 | + * @param {string} eventType - Name of the event to emit | ||
| 178 | + * @param {...*} Arbitrary arguments to be passed to each registered listener | ||
| 179 | + * | ||
| 180 | + * @example | ||
| 181 | + * emitter.addListener('someEvent', function(message) { | ||
| 182 | + * console.log(message); | ||
| 183 | + * }); | ||
| 184 | + * | ||
| 185 | + * emitter.emit('someEvent', 'abc'); // logs 'abc' | ||
| 186 | + */ | ||
| 187 | + emit(eventType: string) { | ||
| 188 | + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); | ||
| 189 | + if (subscriptions) { | ||
| 190 | + for (let i = 0, l = subscriptions.length; i < l; i++) { | ||
| 191 | + const subscription = subscriptions[i]; | ||
| 192 | + | ||
| 193 | + // The subscription may have been removed during this event loop. | ||
| 194 | + if (subscription) { | ||
| 195 | + this._currentSubscription = subscription; | ||
| 196 | + subscription.listener.apply( | ||
| 197 | + subscription.context, | ||
| 198 | + Array.prototype.slice.call(arguments, 1) | ||
| 199 | + ); | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + this._currentSubscription = null; | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + /** | ||
| 207 | + * Removes the given listener for event of specific type. | ||
| 208 | + * | ||
| 209 | + * @param {string} eventType - Name of the event to emit | ||
| 210 | + * @param {function} listener - Function to invoke when the specified event is | ||
| 211 | + * emitted | ||
| 212 | + * | ||
| 213 | + * @example | ||
| 214 | + * emitter.removeListener('someEvent', function(message) { | ||
| 215 | + * console.log(message); | ||
| 216 | + * }); // removes the listener if already registered | ||
| 217 | + * | ||
| 218 | + */ | ||
| 219 | + removeListener(eventType: String, listener) { | ||
| 220 | + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); | ||
| 221 | + if (subscriptions) { | ||
| 222 | + for (let i = 0, l = subscriptions.length; i < l; i++) { | ||
| 223 | + const subscription = subscriptions[i]; | ||
| 224 | + | ||
| 225 | + // The subscription may have been removed during this event loop. | ||
| 226 | + // its listener matches the listener in method parameters | ||
| 227 | + if (subscription && subscription.listener === listener) { | ||
| 228 | + subscription.remove(); | ||
| 229 | + } | ||
| 230 | + } | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | +} | ||
| 234 | + | ||
| 235 | +module.exports = EventEmitter; |
src/EventSubscription.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +import type EventSubscriptionVendor from './EventSubscriptionVendor'; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * EventSubscription represents a subscription to a particular event. It can | ||
| 33 | + * remove its own subscription. | ||
| 34 | + */ | ||
| 35 | +class EventSubscription { | ||
| 36 | + | ||
| 37 | + eventType: string; | ||
| 38 | + key: number; | ||
| 39 | + subscriber: EventSubscriptionVendor; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * @param {EventSubscriptionVendor} subscriber the subscriber that controls | ||
| 43 | + * this subscription. | ||
| 44 | + */ | ||
| 45 | + constructor(subscriber: EventSubscriptionVendor) { | ||
| 46 | + this.subscriber = subscriber; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * Removes this subscription from the subscriber that controls it. | ||
| 51 | + */ | ||
| 52 | + remove() { | ||
| 53 | + this.subscriber.removeSubscription(this); | ||
| 54 | + } | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +module.exports = EventSubscription; |
src/EventSubscriptionVendor.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +const invariant = require('fbjs/lib/invariant'); | ||
| 30 | + | ||
| 31 | +import type EventSubscription from './EventSubscription'; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * EventSubscriptionVendor stores a set of EventSubscriptions that are | ||
| 35 | + * subscribed to a particular event type. | ||
| 36 | + */ | ||
| 37 | +class EventSubscriptionVendor { | ||
| 38 | + | ||
| 39 | + _subscriptionsForType: Object; | ||
| 40 | + _currentSubscription: ?EventSubscription; | ||
| 41 | + | ||
| 42 | + constructor() { | ||
| 43 | + this._subscriptionsForType = {}; | ||
| 44 | + this._currentSubscription = null; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * Adds a subscription keyed by an event type. | ||
| 49 | + * | ||
| 50 | + * @param {string} eventType | ||
| 51 | + * @param {EventSubscription} subscription | ||
| 52 | + */ | ||
| 53 | + addSubscription( | ||
| 54 | + eventType: string, subscription: EventSubscription): EventSubscription { | ||
| 55 | + invariant( | ||
| 56 | + subscription.subscriber === this, | ||
| 57 | + 'The subscriber of the subscription is incorrectly set.'); | ||
| 58 | + if (!this._subscriptionsForType[eventType]) { | ||
| 59 | + this._subscriptionsForType[eventType] = []; | ||
| 60 | + } | ||
| 61 | + const key = this._subscriptionsForType[eventType].length; | ||
| 62 | + this._subscriptionsForType[eventType].push(subscription); | ||
| 63 | + subscription.eventType = eventType; | ||
| 64 | + subscription.key = key; | ||
| 65 | + return subscription; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * Removes a bulk set of the subscriptions. | ||
| 70 | + * | ||
| 71 | + * @param {?string} eventType - Optional name of the event type whose | ||
| 72 | + * registered supscriptions to remove, if null remove all subscriptions. | ||
| 73 | + */ | ||
| 74 | + removeAllSubscriptions(eventType: ?string) { | ||
| 75 | + if (eventType === undefined) { | ||
| 76 | + this._subscriptionsForType = {}; | ||
| 77 | + } else { | ||
| 78 | + delete this._subscriptionsForType[eventType]; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * Removes a specific subscription. Instead of calling this function, call | ||
| 84 | + * `subscription.remove()` directly. | ||
| 85 | + * | ||
| 86 | + * @param {object} subscription | ||
| 87 | + */ | ||
| 88 | + removeSubscription(subscription: Object) { | ||
| 89 | + const eventType = subscription.eventType; | ||
| 90 | + const key = subscription.key; | ||
| 91 | + | ||
| 92 | + const subscriptionsForType = this._subscriptionsForType[eventType]; | ||
| 93 | + if (subscriptionsForType) { | ||
| 94 | + delete subscriptionsForType[key]; | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + * Returns the array of subscriptions that are currently registered for the | ||
| 100 | + * given event type. | ||
| 101 | + * | ||
| 102 | + * Note: This array can be potentially sparse as subscriptions are deleted | ||
| 103 | + * from it when they are removed. | ||
| 104 | + * | ||
| 105 | + * TODO: This returns a nullable array. wat? | ||
| 106 | + * | ||
| 107 | + * @param {string} eventType | ||
| 108 | + * @returns {?array} | ||
| 109 | + */ | ||
| 110 | + getSubscriptionsForType(eventType: string): ?[EventSubscription] { | ||
| 111 | + return this._subscriptionsForType[eventType]; | ||
| 112 | + } | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +module.exports = EventSubscriptionVendor; |
src/InteractionMixin.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +import {InteractionManager} from 'react-native'; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * This mixin provides safe versions of InteractionManager start/end methods | ||
| 33 | + * that ensures `clearInteractionHandle` is always called | ||
| 34 | + * once per start, even if the component is unmounted. | ||
| 35 | + */ | ||
| 36 | +var InteractionMixin = { | ||
| 37 | + componentWillUnmount: function() { | ||
| 38 | + while (this._interactionMixinHandles.length) { | ||
| 39 | + InteractionManager.clearInteractionHandle( | ||
| 40 | + this._interactionMixinHandles.pop() | ||
| 41 | + ); | ||
| 42 | + } | ||
| 43 | + }, | ||
| 44 | + | ||
| 45 | + _interactionMixinHandles: ([]: Array<number>), | ||
| 46 | + | ||
| 47 | + createInteractionHandle: function() { | ||
| 48 | + var handle = InteractionManager.createInteractionHandle(); | ||
| 49 | + this._interactionMixinHandles.push(handle); | ||
| 50 | + return handle; | ||
| 51 | + }, | ||
| 52 | + | ||
| 53 | + clearInteractionHandle: function(clearHandle: number) { | ||
| 54 | + InteractionManager.clearInteractionHandle(clearHandle); | ||
| 55 | + this._interactionMixinHandles = this._interactionMixinHandles.filter( | ||
| 56 | + handle => handle !== clearHandle | ||
| 57 | + ); | ||
| 58 | + }, | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Schedule work for after all interactions have completed. | ||
| 62 | + * | ||
| 63 | + * @param {function} callback | ||
| 64 | + */ | ||
| 65 | + runAfterInteractions: function(callback: Function) { | ||
| 66 | + InteractionManager.runAfterInteractions(callback); | ||
| 67 | + }, | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +module.exports = InteractionMixin; |
src/NavigationContext.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @noflow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +var NavigationEvent = require('./NavigationEvent'); | ||
| 30 | +var NavigationEventEmitter = require('./NavigationEventEmitter'); | ||
| 31 | +var NavigationTreeNode = require('./NavigationTreeNode'); | ||
| 32 | + | ||
| 33 | +var emptyFunction = require('fbjs/lib/emptyFunction'); | ||
| 34 | +var invariant = require('fbjs/lib/invariant'); | ||
| 35 | + | ||
| 36 | +import type EventSubscription from 'EventSubscription'; | ||
| 37 | + | ||
| 38 | +var { | ||
| 39 | + AT_TARGET, | ||
| 40 | + BUBBLING_PHASE, | ||
| 41 | + CAPTURING_PHASE, | ||
| 42 | +} = NavigationEvent; | ||
| 43 | + | ||
| 44 | +// Event types that do not support event bubbling, capturing and | ||
| 45 | +// reconciliation API (e.g event.preventDefault(), event.stopPropagation()). | ||
| 46 | +var LegacyEventTypes = new Set([ | ||
| 47 | + 'willfocus', | ||
| 48 | + 'didfocus', | ||
| 49 | +]); | ||
| 50 | + | ||
| 51 | +/** | ||
| 52 | + * Class that contains the info and methods for app navigation. | ||
| 53 | + */ | ||
| 54 | +class NavigationContext { | ||
| 55 | + __node: NavigationTreeNode; | ||
| 56 | + _bubbleEventEmitter: ?NavigationEventEmitter; | ||
| 57 | + _captureEventEmitter: ?NavigationEventEmitter; | ||
| 58 | + _currentRoute: any; | ||
| 59 | + _emitCounter: number; | ||
| 60 | + _emitQueue: Array<any>; | ||
| 61 | + | ||
| 62 | + constructor() { | ||
| 63 | + this._bubbleEventEmitter = new NavigationEventEmitter(this); | ||
| 64 | + this._captureEventEmitter = new NavigationEventEmitter(this); | ||
| 65 | + this._currentRoute = null; | ||
| 66 | + | ||
| 67 | + // Sets the protected property `__node`. | ||
| 68 | + this.__node = new NavigationTreeNode(this); | ||
| 69 | + | ||
| 70 | + this._emitCounter = 0; | ||
| 71 | + this._emitQueue = []; | ||
| 72 | + | ||
| 73 | + this.addListener('willfocus', this._onFocus); | ||
| 74 | + this.addListener('didfocus', this._onFocus); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + /* $FlowFixMe - get/set properties not yet supported */ | ||
| 78 | + get parent(): ?NavigationContext { | ||
| 79 | + var parent = this.__node.getParent(); | ||
| 80 | + return parent ? parent.getValue() : null; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + /* $FlowFixMe - get/set properties not yet supported */ | ||
| 84 | + get top(): ?NavigationContext { | ||
| 85 | + var result = null; | ||
| 86 | + var parentNode = this.__node.getParent(); | ||
| 87 | + while (parentNode) { | ||
| 88 | + result = parentNode.getValue(); | ||
| 89 | + parentNode = parentNode.getParent(); | ||
| 90 | + } | ||
| 91 | + return result; | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + /* $FlowFixMe - get/set properties not yet supported */ | ||
| 95 | + get currentRoute(): any { | ||
| 96 | + return this._currentRoute; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + appendChild(childContext: NavigationContext): void { | ||
| 100 | + this.__node.appendChild(childContext.__node); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + addListener( | ||
| 104 | + eventType: string, | ||
| 105 | + listener: Function, | ||
| 106 | + useCapture: ?boolean | ||
| 107 | + ): EventSubscription { | ||
| 108 | + if (LegacyEventTypes.has(eventType)) { | ||
| 109 | + useCapture = false; | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + var emitter = useCapture ? | ||
| 113 | + this._captureEventEmitter : | ||
| 114 | + this._bubbleEventEmitter; | ||
| 115 | + | ||
| 116 | + if (emitter) { | ||
| 117 | + return emitter.addListener(eventType, listener, this); | ||
| 118 | + } else { | ||
| 119 | + return {remove: emptyFunction}; | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + emit(eventType: String, data: any, didEmitCallback: ?Function): void { | ||
| 124 | + if (this._emitCounter > 0) { | ||
| 125 | + // An event cycle that was previously created hasn't finished yet. | ||
| 126 | + // Put this event cycle into the queue and will finish them later. | ||
| 127 | + var args: any = Array.prototype.slice.call(arguments); | ||
| 128 | + this._emitQueue.push(args); | ||
| 129 | + return; | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + this._emitCounter++; | ||
| 133 | + | ||
| 134 | + if (LegacyEventTypes.has(eventType)) { | ||
| 135 | + // Legacy events does not support event bubbling and reconciliation. | ||
| 136 | + this.__emit( | ||
| 137 | + eventType, | ||
| 138 | + data, | ||
| 139 | + null, | ||
| 140 | + { | ||
| 141 | + defaultPrevented: false, | ||
| 142 | + eventPhase: AT_TARGET, | ||
| 143 | + propagationStopped: true, | ||
| 144 | + target: this, | ||
| 145 | + } | ||
| 146 | + ); | ||
| 147 | + } else { | ||
| 148 | + var targets = [this]; | ||
| 149 | + var parentTarget = this.parent; | ||
| 150 | + while (parentTarget) { | ||
| 151 | + targets.unshift(parentTarget); | ||
| 152 | + parentTarget = parentTarget.parent; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + var propagationStopped = false; | ||
| 156 | + var defaultPrevented = false; | ||
| 157 | + var callback = (event) => { | ||
| 158 | + propagationStopped = propagationStopped || event.isPropagationStopped(); | ||
| 159 | + defaultPrevented = defaultPrevented || event.defaultPrevented; | ||
| 160 | + }; | ||
| 161 | + | ||
| 162 | + // Capture phase | ||
| 163 | + targets.some((currentTarget) => { | ||
| 164 | + if (propagationStopped) { | ||
| 165 | + return true; | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + var extraInfo = { | ||
| 169 | + defaultPrevented, | ||
| 170 | + eventPhase: CAPTURING_PHASE, | ||
| 171 | + propagationStopped, | ||
| 172 | + target: this, | ||
| 173 | + }; | ||
| 174 | + | ||
| 175 | + currentTarget.__emit(eventType, data, callback, extraInfo); | ||
| 176 | + }, this); | ||
| 177 | + | ||
| 178 | + // bubble phase | ||
| 179 | + targets.reverse().some((currentTarget) => { | ||
| 180 | + if (propagationStopped) { | ||
| 181 | + return true; | ||
| 182 | + } | ||
| 183 | + var extraInfo = { | ||
| 184 | + defaultPrevented, | ||
| 185 | + eventPhase: BUBBLING_PHASE, | ||
| 186 | + propagationStopped, | ||
| 187 | + target: this, | ||
| 188 | + }; | ||
| 189 | + currentTarget.__emit(eventType, data, callback, extraInfo); | ||
| 190 | + }, this); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + if (didEmitCallback) { | ||
| 194 | + var event = NavigationEvent.pool(eventType, this, data); | ||
| 195 | + propagationStopped && event.stopPropagation(); | ||
| 196 | + defaultPrevented && event.preventDefault(); | ||
| 197 | + didEmitCallback.call(this, event); | ||
| 198 | + event.dispose(); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + this._emitCounter--; | ||
| 202 | + while (this._emitQueue.length) { | ||
| 203 | + var args: any = this._emitQueue.shift(); | ||
| 204 | + this.emit.apply(this, args); | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + dispose(): void { | ||
| 209 | + // clean up everything. | ||
| 210 | + this._bubbleEventEmitter && this._bubbleEventEmitter.removeAllListeners(); | ||
| 211 | + this._captureEventEmitter && this._captureEventEmitter.removeAllListeners(); | ||
| 212 | + this._bubbleEventEmitter = null; | ||
| 213 | + this._captureEventEmitter = null; | ||
| 214 | + this._currentRoute = null; | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + // This method `__method` is protected. | ||
| 218 | + __emit( | ||
| 219 | + eventType: String, | ||
| 220 | + data: any, | ||
| 221 | + didEmitCallback: ?Function, | ||
| 222 | + extraInfo: Object, | ||
| 223 | + ): void { | ||
| 224 | + var emitter; | ||
| 225 | + switch (extraInfo.eventPhase) { | ||
| 226 | + case CAPTURING_PHASE: // phase = 1 | ||
| 227 | + emitter = this._captureEventEmitter; | ||
| 228 | + break; | ||
| 229 | + | ||
| 230 | + case AT_TARGET: // phase = 2 | ||
| 231 | + emitter = this._bubbleEventEmitter; | ||
| 232 | + break; | ||
| 233 | + | ||
| 234 | + case BUBBLING_PHASE: // phase = 3 | ||
| 235 | + emitter = this._bubbleEventEmitter; | ||
| 236 | + break; | ||
| 237 | + | ||
| 238 | + default: | ||
| 239 | + invariant(false, 'invalid event phase %s', extraInfo.eventPhase); | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + if (extraInfo.target === this) { | ||
| 243 | + // phase = 2 | ||
| 244 | + extraInfo.eventPhase = AT_TARGET; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + if (emitter) { | ||
| 248 | + emitter.emit( | ||
| 249 | + eventType, | ||
| 250 | + data, | ||
| 251 | + didEmitCallback, | ||
| 252 | + extraInfo | ||
| 253 | + ); | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + _onFocus(event: NavigationEvent): void { | ||
| 258 | + invariant( | ||
| 259 | + event.data && event.data.hasOwnProperty('route'), | ||
| 260 | + 'event type "%s" should provide route', | ||
| 261 | + event.type | ||
| 262 | + ); | ||
| 263 | + | ||
| 264 | + this._currentRoute = event.data.route; | ||
| 265 | + } | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +module.exports = NavigationContext; |
src/NavigationEvent.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +const invariant = require('fbjs/lib/invariant'); | ||
| 30 | + | ||
| 31 | +class NavigationEventPool { | ||
| 32 | + _list: Array<any>; | ||
| 33 | + | ||
| 34 | + constructor() { | ||
| 35 | + this._list = []; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + get(type: string, currentTarget: Object, data: any): NavigationEvent { | ||
| 39 | + let event; | ||
| 40 | + if (this._list.length > 0) { | ||
| 41 | + event = this._list.pop(); | ||
| 42 | + event.constructor.call(event, type, currentTarget, data); | ||
| 43 | + } else { | ||
| 44 | + event = new NavigationEvent(type, currentTarget, data); | ||
| 45 | + } | ||
| 46 | + return event; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + put(event: NavigationEvent) { | ||
| 50 | + this._list.push(event); | ||
| 51 | + } | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +const _navigationEventPool = new NavigationEventPool(); | ||
| 55 | + | ||
| 56 | +/** | ||
| 57 | + * The NavigationEvent interface represents any event of the navigation. | ||
| 58 | + * It contains common properties and methods to any event. | ||
| 59 | + * | ||
| 60 | + * == Important Properties == | ||
| 61 | + * | ||
| 62 | + * - target: | ||
| 63 | + * A reference to the navigation context that dispatched the event. It is | ||
| 64 | + * different from event.currentTarget when the event handler is called during | ||
| 65 | + * the bubbling or capturing phase of the event. | ||
| 66 | + * | ||
| 67 | + * - currentTarget: | ||
| 68 | + * Identifies the current target for the event, as the event traverses the | ||
| 69 | + * navigation context tree. It always refers to the navigation context the | ||
| 70 | + * event handler has been attached to as opposed to event.target which | ||
| 71 | + * identifies the navigation context on which the event occurred. | ||
| 72 | + * | ||
| 73 | + * - eventPhase: | ||
| 74 | + * Returns an integer value which specifies the current evaluation phase of | ||
| 75 | + * the event flow; possible values are listed in NavigationEvent phase | ||
| 76 | + * constants below. | ||
| 77 | + */ | ||
| 78 | +class NavigationEvent { | ||
| 79 | + static AT_TARGET: number; | ||
| 80 | + static BUBBLING_PHASE: number; | ||
| 81 | + static CAPTURING_PHASE: number; | ||
| 82 | + static NONE: number; | ||
| 83 | + | ||
| 84 | + _currentTarget: ?Object; | ||
| 85 | + _data: any; | ||
| 86 | + _defaultPrevented: boolean; | ||
| 87 | + _disposed: boolean; | ||
| 88 | + _propagationStopped: boolean; | ||
| 89 | + _type: string; | ||
| 90 | + | ||
| 91 | + target: ?Object; | ||
| 92 | + | ||
| 93 | + // Returns an integer value which specifies the current evaluation phase of | ||
| 94 | + // the event flow. | ||
| 95 | + eventPhase: number; | ||
| 96 | + | ||
| 97 | + static pool(type: string, currentTarget: Object, data: any): NavigationEvent { | ||
| 98 | + return _navigationEventPool.get(type, currentTarget, data); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + constructor(type: string, currentTarget: Object, data: any) { | ||
| 102 | + this.target = currentTarget; | ||
| 103 | + this.eventPhase = NavigationEvent.NONE; | ||
| 104 | + | ||
| 105 | + this._type = type; | ||
| 106 | + this._currentTarget = currentTarget; | ||
| 107 | + this._data = data; | ||
| 108 | + this._defaultPrevented = false; | ||
| 109 | + this._disposed = false; | ||
| 110 | + this._propagationStopped = false; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + get type(): string { | ||
| 114 | + return this._type; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + get currentTarget(): ?Object { | ||
| 118 | + return this._currentTarget; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + get data(): any { | ||
| 122 | + return this._data; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + get defaultPrevented(): boolean { | ||
| 126 | + return this._defaultPrevented; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + preventDefault(): void { | ||
| 130 | + this._defaultPrevented = true; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + stopPropagation(): void { | ||
| 134 | + this._propagationStopped = true; | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + stop(): void { | ||
| 138 | + this.preventDefault(); | ||
| 139 | + this.stopPropagation(); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + isPropagationStopped(): boolean { | ||
| 143 | + return this._propagationStopped; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + /** | ||
| 147 | + * Dispose the event. | ||
| 148 | + * NavigationEvent shall be disposed after being emitted by | ||
| 149 | + * `NavigationEventEmitter`. | ||
| 150 | + */ | ||
| 151 | + dispose(): void { | ||
| 152 | + invariant(!this._disposed, 'NavigationEvent is already disposed'); | ||
| 153 | + this._disposed = true; | ||
| 154 | + | ||
| 155 | + // Clean up. | ||
| 156 | + this.target = null; | ||
| 157 | + this.eventPhase = NavigationEvent.NONE; | ||
| 158 | + this._type = ''; | ||
| 159 | + this._currentTarget = null; | ||
| 160 | + this._data = null; | ||
| 161 | + this._defaultPrevented = false; | ||
| 162 | + | ||
| 163 | + // Put this back to the pool to reuse the instance. | ||
| 164 | + _navigationEventPool.put(this); | ||
| 165 | + } | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +/** | ||
| 169 | + * Event phase constants. | ||
| 170 | + * These values describe which phase the event flow is currently being | ||
| 171 | + * evaluated. | ||
| 172 | + */ | ||
| 173 | + | ||
| 174 | +// No event is being processed at this time. | ||
| 175 | +NavigationEvent.NONE = 0; | ||
| 176 | + | ||
| 177 | +// The event is being propagated through the currentTarget's ancestor objects. | ||
| 178 | +NavigationEvent.CAPTURING_PHASE = 1; | ||
| 179 | + | ||
| 180 | +// The event has arrived at the event's currentTarget. Event listeners registered for | ||
| 181 | +// this phase are called at this time. | ||
| 182 | +NavigationEvent.AT_TARGET = 2; | ||
| 183 | + | ||
| 184 | +// The event is propagating back up through the currentTarget's ancestors in reverse | ||
| 185 | +// order, starting with the parent. This is known as bubbling, and occurs only | ||
| 186 | +// if event propagation isn't prevented. Event listeners registered for this | ||
| 187 | +// phase are triggered during this process. | ||
| 188 | +NavigationEvent.BUBBLING_PHASE = 3; | ||
| 189 | + | ||
| 190 | +module.exports = NavigationEvent; |
src/NavigationEventEmitter.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +var EventEmitter = require('./EventEmitter'); | ||
| 30 | +var NavigationEvent = require('./NavigationEvent'); | ||
| 31 | + | ||
| 32 | +type ExtraInfo = { | ||
| 33 | + defaultPrevented: ?boolean, | ||
| 34 | + eventPhase: ?number, | ||
| 35 | + propagationStopped: ?boolean, | ||
| 36 | + target: ?Object, | ||
| 37 | +}; | ||
| 38 | + | ||
| 39 | +class NavigationEventEmitter extends EventEmitter { | ||
| 40 | + _emitQueue: Array<any>; | ||
| 41 | + _emitting: boolean; | ||
| 42 | + _target: Object; | ||
| 43 | + | ||
| 44 | + constructor(target: Object) { | ||
| 45 | + super(); | ||
| 46 | + this._emitting = false; | ||
| 47 | + this._emitQueue = []; | ||
| 48 | + this._target = target; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + emit( | ||
| 52 | + eventType: string, | ||
| 53 | + data: any, | ||
| 54 | + didEmitCallback: ?Function, | ||
| 55 | + extraInfo: ?ExtraInfo | ||
| 56 | + ): void { | ||
| 57 | + if (this._emitting) { | ||
| 58 | + // An event cycle that was previously created hasn't finished yet. | ||
| 59 | + // Put this event cycle into the queue and will finish them later. | ||
| 60 | + var args: any = Array.prototype.slice.call(arguments); | ||
| 61 | + this._emitQueue.push(args); | ||
| 62 | + return; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + this._emitting = true; | ||
| 66 | + | ||
| 67 | + var event = NavigationEvent.pool(eventType, this._target, data); | ||
| 68 | + | ||
| 69 | + if (extraInfo) { | ||
| 70 | + if (extraInfo.target) { | ||
| 71 | + event.target = extraInfo.target; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + if (extraInfo.eventPhase) { | ||
| 75 | + event.eventPhase = extraInfo.eventPhase; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + if (extraInfo.defaultPrevented) { | ||
| 79 | + event.preventDefault(); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + if (extraInfo.propagationStopped) { | ||
| 83 | + event.stopPropagation(); | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + // EventEmitter#emit only takes `eventType` as `String`. Casting `eventType` | ||
| 88 | + // to `String` to make @flow happy. | ||
| 89 | + super.emit(String(eventType), event); | ||
| 90 | + | ||
| 91 | + if (typeof didEmitCallback === 'function') { | ||
| 92 | + didEmitCallback.call(this._target, event); | ||
| 93 | + } | ||
| 94 | + event.dispose(); | ||
| 95 | + | ||
| 96 | + this._emitting = false; | ||
| 97 | + | ||
| 98 | + while (this._emitQueue.length) { | ||
| 99 | + var args: any = this._emitQueue.shift(); | ||
| 100 | + this.emit.apply(this, args); | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +module.exports = NavigationEventEmitter; |
src/NavigationRouteStack.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +var immutable = require('immutable'); | ||
| 30 | +var invariant = require('fbjs/lib/invariant'); | ||
| 31 | + | ||
| 32 | +type IterationCallback = (route: any, index: number, key: string) => void; | ||
| 33 | + | ||
| 34 | +var {List, Set} = immutable; | ||
| 35 | + | ||
| 36 | +function isRouteEmpty(route: any): boolean { | ||
| 37 | + return (route === undefined || route === null || route === '') || false; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +var _nextID = 0; | ||
| 41 | + | ||
| 42 | +class RouteNode { | ||
| 43 | + key: string; | ||
| 44 | + value: any; | ||
| 45 | + constructor(route: any) { | ||
| 46 | + // Key value gets bigger incrementally. Developer can compare the | ||
| 47 | + // keys of two routes then know which route is added to the stack | ||
| 48 | + // earlier. | ||
| 49 | + this.key = String(_nextID++); | ||
| 50 | + | ||
| 51 | + this.value = route; | ||
| 52 | + } | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +var StackDiffRecord = immutable.Record({ | ||
| 56 | + key: null, | ||
| 57 | + route: null, | ||
| 58 | + index: null, | ||
| 59 | +}); | ||
| 60 | + | ||
| 61 | +/** | ||
| 62 | + * The immutable route stack. | ||
| 63 | + */ | ||
| 64 | +class RouteStack { | ||
| 65 | + _index: number; | ||
| 66 | + | ||
| 67 | + _routeNodes: List<RouteNode>; | ||
| 68 | + | ||
| 69 | + constructor(index: number, routeNodes: List<RouteNode>) { | ||
| 70 | + invariant( | ||
| 71 | + routeNodes.size > 0, | ||
| 72 | + 'size must not be empty' | ||
| 73 | + ); | ||
| 74 | + | ||
| 75 | + invariant( | ||
| 76 | + index > -1 && index <= routeNodes.size - 1, | ||
| 77 | + 'index out of bound' | ||
| 78 | + ); | ||
| 79 | + | ||
| 80 | + this._routeNodes = routeNodes; | ||
| 81 | + this._index = index; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + get size(): number { | ||
| 85 | + return this._routeNodes.size; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + get index(): number { | ||
| 89 | + return this._index; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + toArray(): Array<any> { | ||
| 93 | + var result = []; | ||
| 94 | + var ii = 0; | ||
| 95 | + var nodes = this._routeNodes; | ||
| 96 | + while (ii < nodes.size) { | ||
| 97 | + result.push(nodes.get(ii).value); | ||
| 98 | + ii++; | ||
| 99 | + } | ||
| 100 | + return result; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + get(index: number): any { | ||
| 104 | + if (index < 0 || index > this._routeNodes.size - 1) { | ||
| 105 | + return null; | ||
| 106 | + } | ||
| 107 | + return this._routeNodes.get(index).value; | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + /** | ||
| 111 | + * Returns the key associated with the route. | ||
| 112 | + * When a route is added to a stack, the stack creates a key for this route. | ||
| 113 | + * The key will persist until the initial stack and its derived stack | ||
| 114 | + * no longer contains this route. | ||
| 115 | + */ | ||
| 116 | + keyOf(route: any): ?string { | ||
| 117 | + if (isRouteEmpty(route)) { | ||
| 118 | + return null; | ||
| 119 | + } | ||
| 120 | + var index = this.indexOf(route); | ||
| 121 | + return index > -1 ? | ||
| 122 | + this._routeNodes.get(index).key : | ||
| 123 | + null; | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + indexOf(route: any): number { | ||
| 127 | + if (isRouteEmpty(route)) { | ||
| 128 | + return -1; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + var finder = (node) => { | ||
| 132 | + return (node: RouteNode).value === route; | ||
| 133 | + }; | ||
| 134 | + | ||
| 135 | + return this._routeNodes.findIndex(finder, this); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + slice(begin?: number, end?: number): RouteStack { | ||
| 139 | + var routeNodes = this._routeNodes.slice(begin, end); | ||
| 140 | + var index = Math.min(this._index, routeNodes.size - 1); | ||
| 141 | + return this._update(index, routeNodes); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + /** | ||
| 145 | + * Returns a new stack with the provided route appended, | ||
| 146 | + * starting at this stack size. | ||
| 147 | + */ | ||
| 148 | + push(route: any): RouteStack { | ||
| 149 | + | ||
| 150 | + invariant( | ||
| 151 | + !isRouteEmpty(route), | ||
| 152 | + 'Must supply route to push' | ||
| 153 | + ); | ||
| 154 | + | ||
| 155 | + invariant(this._routeNodes.indexOf(route) === -1, 'route must be unique'); | ||
| 156 | + | ||
| 157 | + // When pushing, removes the rest of the routes past the current index. | ||
| 158 | + var routeNodes = this._routeNodes.withMutations((list: List<RouteNode>) => { | ||
| 159 | + list.slice(0, this._index + 1).push(new RouteNode(route)); | ||
| 160 | + }); | ||
| 161 | + | ||
| 162 | + return this._update(routeNodes.size - 1, routeNodes); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + /** | ||
| 166 | + * Returns a new stack a size ones less than this stack, | ||
| 167 | + * excluding the last index in this stack. | ||
| 168 | + */ | ||
| 169 | + pop(): RouteStack { | ||
| 170 | + invariant(this._routeNodes.size > 1, 'should not pop routeNodes stack to empty'); | ||
| 171 | + | ||
| 172 | + // When popping, removes the rest of the routes past the current index. | ||
| 173 | + var routeNodes = this._routeNodes.slice(0, this._index); | ||
| 174 | + return this._update(routeNodes.size - 1, routeNodes); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + jumpToIndex(index: number): RouteStack { | ||
| 178 | + invariant( | ||
| 179 | + index > -1 && index < this._routeNodes.size, | ||
| 180 | + 'index out of bound' | ||
| 181 | + ); | ||
| 182 | + | ||
| 183 | + return this._update(index, this._routeNodes); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + /** | ||
| 187 | + * Replace a route in the navigation stack. | ||
| 188 | + * | ||
| 189 | + * `index` specifies the route in the stack that should be replaced. | ||
| 190 | + * If it's negative, it counts from the back. | ||
| 191 | + */ | ||
| 192 | + replaceAtIndex(index: number, route: any): RouteStack { | ||
| 193 | + invariant( | ||
| 194 | + !isRouteEmpty(route), | ||
| 195 | + 'Must supply route to replace' | ||
| 196 | + ); | ||
| 197 | + | ||
| 198 | + if (this.get(index) === route) { | ||
| 199 | + return this; | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + invariant(this.indexOf(route) === -1, 'route must be unique'); | ||
| 203 | + | ||
| 204 | + if (index < 0) { | ||
| 205 | + index += this._routeNodes.size; | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + invariant( | ||
| 209 | + index > -1 && index < this._routeNodes.size, | ||
| 210 | + 'index out of bound' | ||
| 211 | + ); | ||
| 212 | + | ||
| 213 | + var routeNodes = this._routeNodes.set(index, new RouteNode(route)); | ||
| 214 | + return this._update(index, routeNodes); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + // Iterations | ||
| 218 | + forEach(callback: IterationCallback, context: ?Object): void { | ||
| 219 | + var ii = 0; | ||
| 220 | + var nodes = this._routeNodes; | ||
| 221 | + while (ii < nodes.size) { | ||
| 222 | + var node = nodes.get(ii); | ||
| 223 | + callback.call(context, node.value, ii, node.key); | ||
| 224 | + ii++; | ||
| 225 | + } | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + mapToArray(callback: IterationCallback, context: ?Object): Array<any> { | ||
| 229 | + var result = []; | ||
| 230 | + this.forEach((route, index, key) => { | ||
| 231 | + result.push(callback.call(context, route, index, key)); | ||
| 232 | + }); | ||
| 233 | + return result; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + /** | ||
| 237 | + * Returns a Set excluding any routes contained within the stack given. | ||
| 238 | + */ | ||
| 239 | + subtract(stack: RouteStack): Set<StackDiffRecord> { | ||
| 240 | + var items = []; | ||
| 241 | + this._routeNodes.forEach((node: RouteNode, index: number) => { | ||
| 242 | + if (!stack._routeNodes.contains(node)) { | ||
| 243 | + items.push( | ||
| 244 | + new StackDiffRecord({ | ||
| 245 | + route: node.value, | ||
| 246 | + index: index, | ||
| 247 | + key: node.key, | ||
| 248 | + }) | ||
| 249 | + ); | ||
| 250 | + } | ||
| 251 | + }); | ||
| 252 | + return new Set(items); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + _update(index: number, routeNodes: List<RouteNode>): RouteStack { | ||
| 256 | + if (this._index === index && this._routeNodes === routeNodes) { | ||
| 257 | + return this; | ||
| 258 | + } | ||
| 259 | + return new RouteStack(index, routeNodes); | ||
| 260 | + } | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +/** | ||
| 264 | + * The first class data structure for NavigationContext to manage the navigation | ||
| 265 | + * stack of routes. | ||
| 266 | + */ | ||
| 267 | +class NavigationRouteStack extends RouteStack { | ||
| 268 | + constructor(index: number, routeNodes: Array<any>) { | ||
| 269 | + // For now, `RouteStack` internally, uses an immutable `List` to keep | ||
| 270 | + // track of routeNodes. Since using `List` is really just the implementation | ||
| 271 | + // detail, we don't want to accept `routeNodes` as `list` from constructor | ||
| 272 | + // for developer. | ||
| 273 | + var nodes = routeNodes.map((route) => { | ||
| 274 | + invariant(!isRouteEmpty(route), 'route must not be mepty'); | ||
| 275 | + return new RouteNode(route); | ||
| 276 | + }); | ||
| 277 | + | ||
| 278 | + super(index, new List(nodes)); | ||
| 279 | + } | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +module.exports = NavigationRouteStack; |
src/NavigationTreeNode.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +var invariant = require('fbjs/lib/invariant'); | ||
| 30 | +var immutable = require('immutable'); | ||
| 31 | + | ||
| 32 | +var {List} = immutable; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * Utility to build a tree of nodes. | ||
| 36 | + * Note that this tree does not perform cyclic redundancy check | ||
| 37 | + * while appending child node. | ||
| 38 | + */ | ||
| 39 | +class NavigationTreeNode { | ||
| 40 | + __parent: ?NavigationTreeNode; | ||
| 41 | + | ||
| 42 | + _children: List<NavigationTreeNode>; | ||
| 43 | + | ||
| 44 | + _value: any; | ||
| 45 | + | ||
| 46 | + constructor(value: any) { | ||
| 47 | + this.__parent = null; | ||
| 48 | + this._children = new List(); | ||
| 49 | + this._value = value; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + getValue(): any { | ||
| 53 | + return this._value; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + getParent(): ?NavigationTreeNode { | ||
| 57 | + return this.__parent; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + getChildrenCount(): number { | ||
| 61 | + return this._children.size; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + getChildAt(index: number): ?NavigationTreeNode { | ||
| 65 | + return index > -1 && index < this._children.size ? | ||
| 66 | + this._children.get(index) : | ||
| 67 | + null; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + appendChild(child: NavigationTreeNode): void { | ||
| 71 | + if (child.__parent) { | ||
| 72 | + child.__parent.removeChild(child); | ||
| 73 | + } | ||
| 74 | + child.__parent = this; | ||
| 75 | + this._children = this._children.push(child); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + removeChild(child: NavigationTreeNode): void { | ||
| 79 | + var index = this._children.indexOf(child); | ||
| 80 | + | ||
| 81 | + invariant( | ||
| 82 | + index > -1, | ||
| 83 | + 'The node to be removed is not a child of this node.' | ||
| 84 | + ); | ||
| 85 | + | ||
| 86 | + child.__parent = null; | ||
| 87 | + | ||
| 88 | + this._children = this._children.splice(index, 1); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + indexOf(child: NavigationTreeNode): number { | ||
| 92 | + return this._children.indexOf(child); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + forEach(callback: Function, context: any): void { | ||
| 96 | + this._children.forEach(callback, context); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + map(callback: Function, context: any): Array<NavigationTreeNode> { | ||
| 100 | + return this._children.map(callback, context).toJS(); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + some(callback: Function, context: any): boolean { | ||
| 104 | + return this._children.some(callback, context); | ||
| 105 | + } | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | + | ||
| 109 | +module.exports = NavigationTreeNode; |
src/Navigator.js
0 → 100644
This diff is collapsed. Click to expand it.
src/NavigatorBreadcrumbNavigationBar.js
0 → 100644
This diff is collapsed. Click to expand it.
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +'use strict'; | ||
| 27 | + | ||
| 28 | +var NavigatorNavigationBarStylesAndroid = require('./NavigatorNavigationBarStylesAndroid'); | ||
| 29 | + | ||
| 30 | +var buildStyleInterpolator = require('./buildStyleInterpolator'); | ||
| 31 | +var merge = require('./merge'); | ||
| 32 | + | ||
| 33 | +var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesAndroid.General.NavBarHeight; | ||
| 34 | + | ||
| 35 | +var SPACING = 8; | ||
| 36 | +var ICON_WIDTH = 40; | ||
| 37 | +var SEPARATOR_WIDTH = 9; | ||
| 38 | +var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH; | ||
| 39 | +var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT; | ||
| 40 | + | ||
| 41 | +var OPACITY_RATIO = 100; | ||
| 42 | +var ICON_INACTIVE_OPACITY = 0.6; | ||
| 43 | +var MAX_BREADCRUMBS = 10; | ||
| 44 | + | ||
| 45 | +var CRUMB_BASE = { | ||
| 46 | + position: 'absolute', | ||
| 47 | + flexDirection: 'row', | ||
| 48 | + top: 0, | ||
| 49 | + width: CRUMB_WIDTH, | ||
| 50 | + height: NAV_ELEMENT_HEIGHT, | ||
| 51 | + backgroundColor: 'transparent', | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +var ICON_BASE = { | ||
| 55 | + width: ICON_WIDTH, | ||
| 56 | + height: NAV_ELEMENT_HEIGHT, | ||
| 57 | +}; | ||
| 58 | + | ||
| 59 | +var SEPARATOR_BASE = { | ||
| 60 | + width: SEPARATOR_WIDTH, | ||
| 61 | + height: NAV_ELEMENT_HEIGHT, | ||
| 62 | +}; | ||
| 63 | + | ||
| 64 | +var TITLE_BASE = { | ||
| 65 | + position: 'absolute', | ||
| 66 | + top: 0, | ||
| 67 | + height: NAV_ELEMENT_HEIGHT, | ||
| 68 | + backgroundColor: 'transparent', | ||
| 69 | + alignItems: 'flex-start', | ||
| 70 | +}; | ||
| 71 | + | ||
| 72 | +var FIRST_TITLE_BASE = merge(TITLE_BASE, { | ||
| 73 | + left: 0, | ||
| 74 | + right: 0, | ||
| 75 | +}); | ||
| 76 | + | ||
| 77 | +var RIGHT_BUTTON_BASE = { | ||
| 78 | + position: 'absolute', | ||
| 79 | + top: 0, | ||
| 80 | + right: 0, | ||
| 81 | + overflow: 'hidden', | ||
| 82 | + opacity: 1, | ||
| 83 | + height: NAV_ELEMENT_HEIGHT, | ||
| 84 | + backgroundColor: 'transparent', | ||
| 85 | +}; | ||
| 86 | + | ||
| 87 | +/** | ||
| 88 | + * Precompute crumb styles so that they don't need to be recomputed on every | ||
| 89 | + * interaction. | ||
| 90 | + */ | ||
| 91 | +var LEFT = []; | ||
| 92 | +var CENTER = []; | ||
| 93 | +var RIGHT = []; | ||
| 94 | +for (var i = 0; i < MAX_BREADCRUMBS; i++) { | ||
| 95 | + var crumbLeft = CRUMB_WIDTH * i + SPACING; | ||
| 96 | + LEFT[i] = { | ||
| 97 | + Crumb: merge(CRUMB_BASE, { left: crumbLeft }), | ||
| 98 | + Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }), | ||
| 99 | + Separator: merge(SEPARATOR_BASE, { opacity: 1 }), | ||
| 100 | + Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }), | ||
| 101 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), | ||
| 102 | + }; | ||
| 103 | + CENTER[i] = { | ||
| 104 | + Crumb: merge(CRUMB_BASE, { left: crumbLeft }), | ||
| 105 | + Icon: merge(ICON_BASE, { opacity: 1 }), | ||
| 106 | + Separator: merge(SEPARATOR_BASE, { opacity: 0 }), | ||
| 107 | + Title: merge(TITLE_BASE, { | ||
| 108 | + left: crumbLeft + ICON_WIDTH, | ||
| 109 | + opacity: 1, | ||
| 110 | + }), | ||
| 111 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }), | ||
| 112 | + }; | ||
| 113 | + var crumbRight = crumbLeft + 50; | ||
| 114 | + RIGHT[i] = { | ||
| 115 | + Crumb: merge(CRUMB_BASE, { left: crumbRight}), | ||
| 116 | + Icon: merge(ICON_BASE, { opacity: 0 }), | ||
| 117 | + Separator: merge(SEPARATOR_BASE, { opacity: 0 }), | ||
| 118 | + Title: merge(TITLE_BASE, { | ||
| 119 | + left: crumbRight + ICON_WIDTH, | ||
| 120 | + opacity: 0, | ||
| 121 | + }), | ||
| 122 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), | ||
| 123 | + }; | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +// Special case the CENTER state of the first scene. | ||
| 127 | +CENTER[0] = { | ||
| 128 | + Crumb: merge(CRUMB_BASE, {left: SPACING + CRUMB_WIDTH}), | ||
| 129 | + Icon: merge(ICON_BASE, {opacity: 0}), | ||
| 130 | + Separator: merge(SEPARATOR_BASE, {opacity: 0}), | ||
| 131 | + Title: merge(FIRST_TITLE_BASE, {opacity: 1}), | ||
| 132 | + RightItem: CENTER[0].RightItem, | ||
| 133 | +}; | ||
| 134 | +LEFT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); | ||
| 135 | +RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); | ||
| 136 | + | ||
| 137 | + | ||
| 138 | +var buildIndexSceneInterpolator = function(startStyles, endStyles) { | ||
| 139 | + return { | ||
| 140 | + Crumb: buildStyleInterpolator({ | ||
| 141 | + left: { | ||
| 142 | + type: 'linear', | ||
| 143 | + from: startStyles.Crumb.left, | ||
| 144 | + to: endStyles.Crumb.left, | ||
| 145 | + min: 0, | ||
| 146 | + max: 1, | ||
| 147 | + extrapolate: true, | ||
| 148 | + }, | ||
| 149 | + }), | ||
| 150 | + Icon: buildStyleInterpolator({ | ||
| 151 | + opacity: { | ||
| 152 | + type: 'linear', | ||
| 153 | + from: startStyles.Icon.opacity, | ||
| 154 | + to: endStyles.Icon.opacity, | ||
| 155 | + min: 0, | ||
| 156 | + max: 1, | ||
| 157 | + }, | ||
| 158 | + }), | ||
| 159 | + Separator: buildStyleInterpolator({ | ||
| 160 | + opacity: { | ||
| 161 | + type: 'linear', | ||
| 162 | + from: startStyles.Separator.opacity, | ||
| 163 | + to: endStyles.Separator.opacity, | ||
| 164 | + min: 0, | ||
| 165 | + max: 1, | ||
| 166 | + }, | ||
| 167 | + }), | ||
| 168 | + Title: buildStyleInterpolator({ | ||
| 169 | + opacity: { | ||
| 170 | + type: 'linear', | ||
| 171 | + from: startStyles.Title.opacity, | ||
| 172 | + to: endStyles.Title.opacity, | ||
| 173 | + min: 0, | ||
| 174 | + max: 1, | ||
| 175 | + }, | ||
| 176 | + left: { | ||
| 177 | + type: 'linear', | ||
| 178 | + from: startStyles.Title.left, | ||
| 179 | + to: endStyles.Title.left, | ||
| 180 | + min: 0, | ||
| 181 | + max: 1, | ||
| 182 | + extrapolate: true, | ||
| 183 | + }, | ||
| 184 | + }), | ||
| 185 | + RightItem: buildStyleInterpolator({ | ||
| 186 | + opacity: { | ||
| 187 | + type: 'linear', | ||
| 188 | + from: startStyles.RightItem.opacity, | ||
| 189 | + to: endStyles.RightItem.opacity, | ||
| 190 | + min: 0, | ||
| 191 | + max: 1, | ||
| 192 | + round: OPACITY_RATIO, | ||
| 193 | + }, | ||
| 194 | + }), | ||
| 195 | + }; | ||
| 196 | +}; | ||
| 197 | + | ||
| 198 | +var Interpolators = CENTER.map(function(_, ii) { | ||
| 199 | + return { | ||
| 200 | + // Animating *into* the center stage from the right | ||
| 201 | + RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]), | ||
| 202 | + // Animating out of the center stage, to the left | ||
| 203 | + CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]), | ||
| 204 | + // Both stages (animating *past* the center stage) | ||
| 205 | + RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]), | ||
| 206 | + }; | ||
| 207 | +}); | ||
| 208 | + | ||
| 209 | +/** | ||
| 210 | + * Contains constants that are used in constructing both `StyleSheet`s and | ||
| 211 | + * inline styles during transitions. | ||
| 212 | + */ | ||
| 213 | +module.exports = { | ||
| 214 | + Interpolators, | ||
| 215 | + Left: LEFT, | ||
| 216 | + Center: CENTER, | ||
| 217 | + Right: RIGHT, | ||
| 218 | + IconWidth: ICON_WIDTH, | ||
| 219 | + IconHeight: NAV_BAR_HEIGHT, | ||
| 220 | + SeparatorWidth: SEPARATOR_WIDTH, | ||
| 221 | + SeparatorHeight: NAV_BAR_HEIGHT, | ||
| 222 | +}; |
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +'use strict'; | ||
| 27 | + | ||
| 28 | +import {Dimensions} from 'react-native'; | ||
| 29 | +var NavigatorNavigationBarStylesIOS = require('./NavigatorNavigationBarStylesIOS'); | ||
| 30 | + | ||
| 31 | +var buildStyleInterpolator = require('./buildStyleInterpolator'); | ||
| 32 | +var merge = require('./merge'); | ||
| 33 | + | ||
| 34 | +var SCREEN_WIDTH = Dimensions.get('window').width; | ||
| 35 | +var STATUS_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.StatusBarHeight; | ||
| 36 | +var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.NavBarHeight; | ||
| 37 | + | ||
| 38 | +var SPACING = 4; | ||
| 39 | +var ICON_WIDTH = 40; | ||
| 40 | +var SEPARATOR_WIDTH = 9; | ||
| 41 | +var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH; | ||
| 42 | + | ||
| 43 | +var OPACITY_RATIO = 100; | ||
| 44 | +var ICON_INACTIVE_OPACITY = 0.6; | ||
| 45 | +var MAX_BREADCRUMBS = 10; | ||
| 46 | + | ||
| 47 | +var CRUMB_BASE = { | ||
| 48 | + position: 'absolute', | ||
| 49 | + flexDirection: 'row', | ||
| 50 | + top: STATUS_BAR_HEIGHT, | ||
| 51 | + width: CRUMB_WIDTH, | ||
| 52 | + height: NAV_BAR_HEIGHT, | ||
| 53 | + backgroundColor: 'transparent', | ||
| 54 | +}; | ||
| 55 | + | ||
| 56 | +var ICON_BASE = { | ||
| 57 | + width: ICON_WIDTH, | ||
| 58 | + height: NAV_BAR_HEIGHT, | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +var SEPARATOR_BASE = { | ||
| 62 | + width: SEPARATOR_WIDTH, | ||
| 63 | + height: NAV_BAR_HEIGHT, | ||
| 64 | +}; | ||
| 65 | + | ||
| 66 | +var TITLE_BASE = { | ||
| 67 | + position: 'absolute', | ||
| 68 | + top: STATUS_BAR_HEIGHT, | ||
| 69 | + height: NAV_BAR_HEIGHT, | ||
| 70 | + backgroundColor: 'transparent', | ||
| 71 | +}; | ||
| 72 | + | ||
| 73 | +// For first title styles, make sure first title is centered | ||
| 74 | +var FIRST_TITLE_BASE = merge(TITLE_BASE, { | ||
| 75 | + left: 0, | ||
| 76 | + right: 0, | ||
| 77 | + alignItems: 'center', | ||
| 78 | + height: NAV_BAR_HEIGHT, | ||
| 79 | +}); | ||
| 80 | + | ||
| 81 | +var RIGHT_BUTTON_BASE = { | ||
| 82 | + position: 'absolute', | ||
| 83 | + top: STATUS_BAR_HEIGHT, | ||
| 84 | + right: SPACING, | ||
| 85 | + overflow: 'hidden', | ||
| 86 | + opacity: 1, | ||
| 87 | + height: NAV_BAR_HEIGHT, | ||
| 88 | + backgroundColor: 'transparent', | ||
| 89 | +}; | ||
| 90 | + | ||
| 91 | +/** | ||
| 92 | + * Precompute crumb styles so that they don't need to be recomputed on every | ||
| 93 | + * interaction. | ||
| 94 | + */ | ||
| 95 | +var LEFT = []; | ||
| 96 | +var CENTER = []; | ||
| 97 | +var RIGHT = []; | ||
| 98 | +for (var i = 0; i < MAX_BREADCRUMBS; i++) { | ||
| 99 | + var crumbLeft = CRUMB_WIDTH * i + SPACING; | ||
| 100 | + LEFT[i] = { | ||
| 101 | + Crumb: merge(CRUMB_BASE, { left: crumbLeft }), | ||
| 102 | + Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }), | ||
| 103 | + Separator: merge(SEPARATOR_BASE, { opacity: 1 }), | ||
| 104 | + Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }), | ||
| 105 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), | ||
| 106 | + }; | ||
| 107 | + CENTER[i] = { | ||
| 108 | + Crumb: merge(CRUMB_BASE, { left: crumbLeft }), | ||
| 109 | + Icon: merge(ICON_BASE, { opacity: 1 }), | ||
| 110 | + Separator: merge(SEPARATOR_BASE, { opacity: 0 }), | ||
| 111 | + Title: merge(TITLE_BASE, { | ||
| 112 | + left: crumbLeft + ICON_WIDTH, | ||
| 113 | + opacity: 1, | ||
| 114 | + }), | ||
| 115 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }), | ||
| 116 | + }; | ||
| 117 | + var crumbRight = SCREEN_WIDTH - 100; | ||
| 118 | + RIGHT[i] = { | ||
| 119 | + Crumb: merge(CRUMB_BASE, { left: crumbRight}), | ||
| 120 | + Icon: merge(ICON_BASE, { opacity: 0 }), | ||
| 121 | + Separator: merge(SEPARATOR_BASE, { opacity: 0 }), | ||
| 122 | + Title: merge(TITLE_BASE, { | ||
| 123 | + left: crumbRight + ICON_WIDTH, | ||
| 124 | + opacity: 0, | ||
| 125 | + }), | ||
| 126 | + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }), | ||
| 127 | + }; | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +// Special case the CENTER state of the first scene. | ||
| 131 | +CENTER[0] = { | ||
| 132 | + Crumb: merge(CRUMB_BASE, {left: SCREEN_WIDTH / 4}), | ||
| 133 | + Icon: merge(ICON_BASE, {opacity: 0}), | ||
| 134 | + Separator: merge(SEPARATOR_BASE, {opacity: 0}), | ||
| 135 | + Title: merge(FIRST_TITLE_BASE, {opacity: 1}), | ||
| 136 | + RightItem: CENTER[0].RightItem, | ||
| 137 | +}; | ||
| 138 | +LEFT[0].Title = merge(FIRST_TITLE_BASE, {left: -SCREEN_WIDTH / 4, opacity: 0}); | ||
| 139 | +RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0}); | ||
| 140 | + | ||
| 141 | + | ||
| 142 | +var buildIndexSceneInterpolator = function(startStyles, endStyles) { | ||
| 143 | + return { | ||
| 144 | + Crumb: buildStyleInterpolator({ | ||
| 145 | + left: { | ||
| 146 | + type: 'linear', | ||
| 147 | + from: startStyles.Crumb.left, | ||
| 148 | + to: endStyles.Crumb.left, | ||
| 149 | + min: 0, | ||
| 150 | + max: 1, | ||
| 151 | + extrapolate: true, | ||
| 152 | + }, | ||
| 153 | + }), | ||
| 154 | + Icon: buildStyleInterpolator({ | ||
| 155 | + opacity: { | ||
| 156 | + type: 'linear', | ||
| 157 | + from: startStyles.Icon.opacity, | ||
| 158 | + to: endStyles.Icon.opacity, | ||
| 159 | + min: 0, | ||
| 160 | + max: 1, | ||
| 161 | + }, | ||
| 162 | + }), | ||
| 163 | + Separator: buildStyleInterpolator({ | ||
| 164 | + opacity: { | ||
| 165 | + type: 'linear', | ||
| 166 | + from: startStyles.Separator.opacity, | ||
| 167 | + to: endStyles.Separator.opacity, | ||
| 168 | + min: 0, | ||
| 169 | + max: 1, | ||
| 170 | + }, | ||
| 171 | + }), | ||
| 172 | + Title: buildStyleInterpolator({ | ||
| 173 | + opacity: { | ||
| 174 | + type: 'linear', | ||
| 175 | + from: startStyles.Title.opacity, | ||
| 176 | + to: endStyles.Title.opacity, | ||
| 177 | + min: 0, | ||
| 178 | + max: 1, | ||
| 179 | + }, | ||
| 180 | + left: { | ||
| 181 | + type: 'linear', | ||
| 182 | + from: startStyles.Title.left, | ||
| 183 | + to: endStyles.Title.left, | ||
| 184 | + min: 0, | ||
| 185 | + max: 1, | ||
| 186 | + extrapolate: true, | ||
| 187 | + }, | ||
| 188 | + }), | ||
| 189 | + RightItem: buildStyleInterpolator({ | ||
| 190 | + opacity: { | ||
| 191 | + type: 'linear', | ||
| 192 | + from: startStyles.RightItem.opacity, | ||
| 193 | + to: endStyles.RightItem.opacity, | ||
| 194 | + min: 0, | ||
| 195 | + max: 1, | ||
| 196 | + round: OPACITY_RATIO, | ||
| 197 | + }, | ||
| 198 | + }), | ||
| 199 | + }; | ||
| 200 | +}; | ||
| 201 | + | ||
| 202 | +var Interpolators = CENTER.map(function(_, ii) { | ||
| 203 | + return { | ||
| 204 | + // Animating *into* the center stage from the right | ||
| 205 | + RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]), | ||
| 206 | + // Animating out of the center stage, to the left | ||
| 207 | + CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]), | ||
| 208 | + // Both stages (animating *past* the center stage) | ||
| 209 | + RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]), | ||
| 210 | + }; | ||
| 211 | +}); | ||
| 212 | + | ||
| 213 | +/** | ||
| 214 | + * Contains constants that are used in constructing both `StyleSheet`s and | ||
| 215 | + * inline styles during transitions. | ||
| 216 | + */ | ||
| 217 | +module.exports = { | ||
| 218 | + Interpolators, | ||
| 219 | + Left: LEFT, | ||
| 220 | + Center: CENTER, | ||
| 221 | + Right: RIGHT, | ||
| 222 | + IconWidth: ICON_WIDTH, | ||
| 223 | + IconHeight: NAV_BAR_HEIGHT, | ||
| 224 | + SeparatorWidth: SEPARATOR_WIDTH, | ||
| 225 | + SeparatorHeight: NAV_BAR_HEIGHT, | ||
| 226 | +}; |
src/NavigatorNavigationBar.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +'use strict'; | ||
| 27 | + | ||
| 28 | +var React = require('react'); | ||
| 29 | +var NavigatorNavigationBarStylesAndroid = require('./NavigatorNavigationBarStylesAndroid'); | ||
| 30 | +var NavigatorNavigationBarStylesIOS = require('./NavigatorNavigationBarStylesIOS'); | ||
| 31 | +import { | ||
| 32 | + Platform, | ||
| 33 | + StyleSheet, | ||
| 34 | + View, | ||
| 35 | + ViewPropTypes, | ||
| 36 | +} from 'react-native'; | ||
| 37 | +var PropTypes = require('prop-types'); | ||
| 38 | + | ||
| 39 | +var guid = require('./guid'); | ||
| 40 | + | ||
| 41 | +var { Map } = require('immutable'); | ||
| 42 | + | ||
| 43 | +var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton']; | ||
| 44 | + | ||
| 45 | +var NavigatorNavigationBarStyles = Platform.OS === 'android' ? | ||
| 46 | + NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS; | ||
| 47 | + | ||
| 48 | +var navStatePresentedIndex = function(navState) { | ||
| 49 | + if (navState.presentedIndex !== undefined) { | ||
| 50 | + return navState.presentedIndex; | ||
| 51 | + } | ||
| 52 | + // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS` | ||
| 53 | + return navState.observedTopOfStack; | ||
| 54 | +}; | ||
| 55 | + | ||
| 56 | +class NavigatorNavigationBar extends React.Component { | ||
| 57 | + static propTypes = { | ||
| 58 | + navigator: PropTypes.object, | ||
| 59 | + routeMapper: PropTypes.shape({ | ||
| 60 | + Title: PropTypes.func.isRequired, | ||
| 61 | + LeftButton: PropTypes.func.isRequired, | ||
| 62 | + RightButton: PropTypes.func.isRequired, | ||
| 63 | + }).isRequired, | ||
| 64 | + navState: PropTypes.shape({ | ||
| 65 | + routeStack: PropTypes.arrayOf(PropTypes.object), | ||
| 66 | + presentedIndex: PropTypes.number, | ||
| 67 | + }), | ||
| 68 | + navigationStyles: PropTypes.object, | ||
| 69 | + style: ViewPropTypes.style, | ||
| 70 | + }; | ||
| 71 | + | ||
| 72 | + static Styles = NavigatorNavigationBarStyles; | ||
| 73 | + static StylesAndroid = NavigatorNavigationBarStylesAndroid; | ||
| 74 | + static StylesIOS = NavigatorNavigationBarStylesIOS; | ||
| 75 | + | ||
| 76 | + static defaultProps = { | ||
| 77 | + navigationStyles: NavigatorNavigationBarStyles, | ||
| 78 | + }; | ||
| 79 | + | ||
| 80 | + componentWillMount() { | ||
| 81 | + this._reset(); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * Stop transtion, immediately resets the cached state and re-render the | ||
| 86 | + * whole view. | ||
| 87 | + */ | ||
| 88 | + immediatelyRefresh = () => { | ||
| 89 | + this._reset(); | ||
| 90 | + this.forceUpdate(); | ||
| 91 | + }; | ||
| 92 | + | ||
| 93 | + _reset = () => { | ||
| 94 | + this._key = guid(); | ||
| 95 | + this._reusableProps = {}; | ||
| 96 | + this._components = {}; | ||
| 97 | + this._descriptors = {}; | ||
| 98 | + | ||
| 99 | + COMPONENT_NAMES.forEach(componentName => { | ||
| 100 | + this._components[componentName] = new Map(); | ||
| 101 | + this._descriptors[componentName] = new Map(); | ||
| 102 | + }); | ||
| 103 | + }; | ||
| 104 | + | ||
| 105 | + _getReusableProps = (/*string*/componentName, /*number*/index) => /*object*/ { | ||
| 106 | + var propStack = this._reusableProps[componentName]; | ||
| 107 | + if (!propStack) { | ||
| 108 | + propStack = this._reusableProps[componentName] = []; | ||
| 109 | + } | ||
| 110 | + var props = propStack[index]; | ||
| 111 | + if (!props) { | ||
| 112 | + props = propStack[index] = {style:{}}; | ||
| 113 | + } | ||
| 114 | + return props; | ||
| 115 | + }; | ||
| 116 | + | ||
| 117 | + _updateIndexProgress = ( | ||
| 118 | + /*number*/progress, | ||
| 119 | + /*number*/index, | ||
| 120 | + /*number*/fromIndex, | ||
| 121 | + /*number*/toIndex, | ||
| 122 | + ) => { | ||
| 123 | + var amount = toIndex > fromIndex ? progress : (1 - progress); | ||
| 124 | + var oldDistToCenter = index - fromIndex; | ||
| 125 | + var newDistToCenter = index - toIndex; | ||
| 126 | + var interpolate; | ||
| 127 | + if (oldDistToCenter > 0 && newDistToCenter === 0 || | ||
| 128 | + newDistToCenter > 0 && oldDistToCenter === 0) { | ||
| 129 | + interpolate = this.props.navigationStyles.Interpolators.RightToCenter; | ||
| 130 | + } else if (oldDistToCenter < 0 && newDistToCenter === 0 || | ||
| 131 | + newDistToCenter < 0 && oldDistToCenter === 0) { | ||
| 132 | + interpolate = this.props.navigationStyles.Interpolators.CenterToLeft; | ||
| 133 | + } else if (oldDistToCenter === newDistToCenter) { | ||
| 134 | + interpolate = this.props.navigationStyles.Interpolators.RightToCenter; | ||
| 135 | + } else { | ||
| 136 | + interpolate = this.props.navigationStyles.Interpolators.RightToLeft; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + COMPONENT_NAMES.forEach(function (componentName) { | ||
| 140 | + var component = this._components[componentName].get(this.props.navState.routeStack[index]); | ||
| 141 | + var props = this._getReusableProps(componentName, index); | ||
| 142 | + if (component && interpolate[componentName](props.style, amount)) { | ||
| 143 | + props.pointerEvents = props.style.opacity === 0 ? 'none' : 'box-none'; | ||
| 144 | + component.setNativeProps(props); | ||
| 145 | + } | ||
| 146 | + }, this); | ||
| 147 | + }; | ||
| 148 | + | ||
| 149 | + updateProgress = (/*number*/progress, /*number*/fromIndex, /*number*/toIndex) => { | ||
| 150 | + var max = Math.max(fromIndex, toIndex); | ||
| 151 | + var min = Math.min(fromIndex, toIndex); | ||
| 152 | + for (var index = min; index <= max; index++) { | ||
| 153 | + this._updateIndexProgress(progress, index, fromIndex, toIndex); | ||
| 154 | + } | ||
| 155 | + }; | ||
| 156 | + | ||
| 157 | + render() { | ||
| 158 | + var navBarStyle = { | ||
| 159 | + height: this.props.navigationStyles.General.TotalNavHeight, | ||
| 160 | + }; | ||
| 161 | + var navState = this.props.navState; | ||
| 162 | + var components = navState.routeStack.map((route, index) => | ||
| 163 | + COMPONENT_NAMES.map(componentName => | ||
| 164 | + this._getComponent(componentName, route, index) | ||
| 165 | + ) | ||
| 166 | + ); | ||
| 167 | + | ||
| 168 | + return ( | ||
| 169 | + <View | ||
| 170 | + key={this._key} | ||
| 171 | + style={[styles.navBarContainer, navBarStyle, this.props.style]}> | ||
| 172 | + {components} | ||
| 173 | + </View> | ||
| 174 | + ); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + _getComponent = (/*string*/componentName, /*object*/route, /*number*/index) => /*?Object*/ { | ||
| 178 | + if (this._descriptors[componentName].includes(route)) { | ||
| 179 | + return this._descriptors[componentName].get(route); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + var rendered = null; | ||
| 183 | + | ||
| 184 | + var content = this.props.routeMapper[componentName]( | ||
| 185 | + this.props.navState.routeStack[index], | ||
| 186 | + this.props.navigator, | ||
| 187 | + index, | ||
| 188 | + this.props.navState | ||
| 189 | + ); | ||
| 190 | + if (!content) { | ||
| 191 | + return null; | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + var componentIsActive = index === navStatePresentedIndex(this.props.navState); | ||
| 195 | + var initialStage = componentIsActive ? | ||
| 196 | + this.props.navigationStyles.Stages.Center : | ||
| 197 | + this.props.navigationStyles.Stages.Left; | ||
| 198 | + rendered = ( | ||
| 199 | + <View | ||
| 200 | + ref={(ref) => { | ||
| 201 | + this._components[componentName] = this._components[componentName].set(route, ref); | ||
| 202 | + }} | ||
| 203 | + pointerEvents={componentIsActive ? 'box-none' : 'none'} | ||
| 204 | + style={initialStage[componentName]}> | ||
| 205 | + {content} | ||
| 206 | + </View> | ||
| 207 | + ); | ||
| 208 | + | ||
| 209 | + this._descriptors[componentName] = this._descriptors[componentName].set(route, rendered); | ||
| 210 | + return rendered; | ||
| 211 | + }; | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | + | ||
| 215 | +var styles = StyleSheet.create({ | ||
| 216 | + navBarContainer: { | ||
| 217 | + position: 'absolute', | ||
| 218 | + top: 0, | ||
| 219 | + left: 0, | ||
| 220 | + right: 0, | ||
| 221 | + backgroundColor: 'transparent', | ||
| 222 | + }, | ||
| 223 | +}); | ||
| 224 | + | ||
| 225 | +module.exports = NavigatorNavigationBar; |
src/NavigatorNavigationBarStylesAndroid.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +'use strict'; | ||
| 27 | + | ||
| 28 | +var buildStyleInterpolator = require('./buildStyleInterpolator'); | ||
| 29 | +var merge = require('./merge'); | ||
| 30 | + | ||
| 31 | +// Android Material Design | ||
| 32 | +var NAV_BAR_HEIGHT = 56; | ||
| 33 | +var TITLE_LEFT = 72; | ||
| 34 | +var BUTTON_SIZE = 24; | ||
| 35 | +var TOUCH_TARGT_SIZE = 48; | ||
| 36 | +var BUTTON_HORIZONTAL_MARGIN = 16; | ||
| 37 | + | ||
| 38 | +var BUTTON_EFFECTIVE_MARGIN = BUTTON_HORIZONTAL_MARGIN - (TOUCH_TARGT_SIZE - BUTTON_SIZE) / 2; | ||
| 39 | +var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT; | ||
| 40 | + | ||
| 41 | +var BASE_STYLES = { | ||
| 42 | + Title: { | ||
| 43 | + position: 'absolute', | ||
| 44 | + bottom: 0, | ||
| 45 | + left: 0, | ||
| 46 | + right: 0, | ||
| 47 | + alignItems: 'flex-start', | ||
| 48 | + height: NAV_ELEMENT_HEIGHT, | ||
| 49 | + backgroundColor: 'transparent', | ||
| 50 | + marginLeft: TITLE_LEFT, | ||
| 51 | + }, | ||
| 52 | + LeftButton: { | ||
| 53 | + position: 'absolute', | ||
| 54 | + top: 0, | ||
| 55 | + left: BUTTON_EFFECTIVE_MARGIN, | ||
| 56 | + overflow: 'hidden', | ||
| 57 | + height: NAV_ELEMENT_HEIGHT, | ||
| 58 | + backgroundColor: 'transparent', | ||
| 59 | + }, | ||
| 60 | + RightButton: { | ||
| 61 | + position: 'absolute', | ||
| 62 | + top: 0, | ||
| 63 | + right: BUTTON_EFFECTIVE_MARGIN, | ||
| 64 | + overflow: 'hidden', | ||
| 65 | + alignItems: 'flex-end', | ||
| 66 | + height: NAV_ELEMENT_HEIGHT, | ||
| 67 | + backgroundColor: 'transparent', | ||
| 68 | + }, | ||
| 69 | +}; | ||
| 70 | + | ||
| 71 | +// There are 3 stages: left, center, right. All previous navigation | ||
| 72 | +// items are in the left stage. The current navigation item is in the | ||
| 73 | +// center stage. All upcoming navigation items are in the right stage. | ||
| 74 | +// Another way to think of the stages is in terms of transitions. When | ||
| 75 | +// we move forward in the navigation stack, we perform a | ||
| 76 | +// right-to-center transition on the new navigation item and a | ||
| 77 | +// center-to-left transition on the current navigation item. | ||
| 78 | +var Stages = { | ||
| 79 | + Left: { | ||
| 80 | + Title: merge(BASE_STYLES.Title, { opacity: 0 }), | ||
| 81 | + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }), | ||
| 82 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), | ||
| 83 | + }, | ||
| 84 | + Center: { | ||
| 85 | + Title: merge(BASE_STYLES.Title, { opacity: 1 }), | ||
| 86 | + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 1 }), | ||
| 87 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }), | ||
| 88 | + }, | ||
| 89 | + Right: { | ||
| 90 | + Title: merge(BASE_STYLES.Title, { opacity: 0 }), | ||
| 91 | + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }), | ||
| 92 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), | ||
| 93 | + }, | ||
| 94 | +}; | ||
| 95 | + | ||
| 96 | + | ||
| 97 | +var opacityRatio = 100; | ||
| 98 | + | ||
| 99 | +function buildSceneInterpolators(startStyles, endStyles) { | ||
| 100 | + return { | ||
| 101 | + Title: buildStyleInterpolator({ | ||
| 102 | + opacity: { | ||
| 103 | + type: 'linear', | ||
| 104 | + from: startStyles.Title.opacity, | ||
| 105 | + to: endStyles.Title.opacity, | ||
| 106 | + min: 0, | ||
| 107 | + max: 1, | ||
| 108 | + }, | ||
| 109 | + left: { | ||
| 110 | + type: 'linear', | ||
| 111 | + from: startStyles.Title.left, | ||
| 112 | + to: endStyles.Title.left, | ||
| 113 | + min: 0, | ||
| 114 | + max: 1, | ||
| 115 | + extrapolate: true, | ||
| 116 | + }, | ||
| 117 | + }), | ||
| 118 | + LeftButton: buildStyleInterpolator({ | ||
| 119 | + opacity: { | ||
| 120 | + type: 'linear', | ||
| 121 | + from: startStyles.LeftButton.opacity, | ||
| 122 | + to: endStyles.LeftButton.opacity, | ||
| 123 | + min: 0, | ||
| 124 | + max: 1, | ||
| 125 | + round: opacityRatio, | ||
| 126 | + }, | ||
| 127 | + left: { | ||
| 128 | + type: 'linear', | ||
| 129 | + from: startStyles.LeftButton.left, | ||
| 130 | + to: endStyles.LeftButton.left, | ||
| 131 | + min: 0, | ||
| 132 | + max: 1, | ||
| 133 | + }, | ||
| 134 | + }), | ||
| 135 | + RightButton: buildStyleInterpolator({ | ||
| 136 | + opacity: { | ||
| 137 | + type: 'linear', | ||
| 138 | + from: startStyles.RightButton.opacity, | ||
| 139 | + to: endStyles.RightButton.opacity, | ||
| 140 | + min: 0, | ||
| 141 | + max: 1, | ||
| 142 | + round: opacityRatio, | ||
| 143 | + }, | ||
| 144 | + left: { | ||
| 145 | + type: 'linear', | ||
| 146 | + from: startStyles.RightButton.left, | ||
| 147 | + to: endStyles.RightButton.left, | ||
| 148 | + min: 0, | ||
| 149 | + max: 1, | ||
| 150 | + extrapolate: true, | ||
| 151 | + }, | ||
| 152 | + }), | ||
| 153 | + }; | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +var Interpolators = { | ||
| 157 | + // Animating *into* the center stage from the right | ||
| 158 | + RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center), | ||
| 159 | + // Animating out of the center stage, to the left | ||
| 160 | + CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left), | ||
| 161 | + // Both stages (animating *past* the center stage) | ||
| 162 | + RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left), | ||
| 163 | +}; | ||
| 164 | + | ||
| 165 | + | ||
| 166 | +module.exports = { | ||
| 167 | + General: { | ||
| 168 | + NavBarHeight: NAV_BAR_HEIGHT, | ||
| 169 | + StatusBarHeight: 0, | ||
| 170 | + TotalNavHeight: NAV_BAR_HEIGHT, | ||
| 171 | + }, | ||
| 172 | + Interpolators, | ||
| 173 | + Stages, | ||
| 174 | +}; |
src/NavigatorNavigationBarStylesIOS.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | +'use strict'; | ||
| 27 | + | ||
| 28 | +import { | ||
| 29 | + Dimensions, | ||
| 30 | + I18nManager, | ||
| 31 | + PixelRatio, | ||
| 32 | +} from 'react-native'; | ||
| 33 | + | ||
| 34 | +var buildStyleInterpolator = require('./buildStyleInterpolator'); | ||
| 35 | +var merge = require('./merge'); | ||
| 36 | + | ||
| 37 | +var SCREEN_WIDTH = Dimensions.get('window').width; | ||
| 38 | +var NAV_BAR_HEIGHT = 44; | ||
| 39 | +var STATUS_BAR_HEIGHT = 20; | ||
| 40 | +var NAV_HEIGHT = NAV_BAR_HEIGHT + STATUS_BAR_HEIGHT; | ||
| 41 | + | ||
| 42 | +var BASE_STYLES = { | ||
| 43 | + Title: { | ||
| 44 | + position: 'absolute', | ||
| 45 | + top: STATUS_BAR_HEIGHT, | ||
| 46 | + left: 0, | ||
| 47 | + right: 0, | ||
| 48 | + alignItems: 'center', | ||
| 49 | + height: NAV_BAR_HEIGHT, | ||
| 50 | + backgroundColor: 'transparent', | ||
| 51 | + }, | ||
| 52 | + LeftButton: { | ||
| 53 | + position: 'absolute', | ||
| 54 | + top: STATUS_BAR_HEIGHT, | ||
| 55 | + left: 0, | ||
| 56 | + overflow: 'hidden', | ||
| 57 | + opacity: 1, | ||
| 58 | + height: NAV_BAR_HEIGHT, | ||
| 59 | + backgroundColor: 'transparent', | ||
| 60 | + }, | ||
| 61 | + RightButton: { | ||
| 62 | + position: 'absolute', | ||
| 63 | + top: STATUS_BAR_HEIGHT, | ||
| 64 | + right: 0, | ||
| 65 | + overflow: 'hidden', | ||
| 66 | + opacity: 1, | ||
| 67 | + alignItems: 'flex-end', | ||
| 68 | + height: NAV_BAR_HEIGHT, | ||
| 69 | + backgroundColor: 'transparent', | ||
| 70 | + }, | ||
| 71 | +}; | ||
| 72 | + | ||
| 73 | +// There are 3 stages: left, center, right. All previous navigation | ||
| 74 | +// items are in the left stage. The current navigation item is in the | ||
| 75 | +// center stage. All upcoming navigation items are in the right stage. | ||
| 76 | +// Another way to think of the stages is in terms of transitions. When | ||
| 77 | +// we move forward in the navigation stack, we perform a | ||
| 78 | +// right-to-center transition on the new navigation item and a | ||
| 79 | +// center-to-left transition on the current navigation item. | ||
| 80 | +var Stages = { | ||
| 81 | + Left: { | ||
| 82 | + Title: merge(BASE_STYLES.Title, { left: -SCREEN_WIDTH / 2, opacity: 0 }), | ||
| 83 | + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }), | ||
| 84 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), | ||
| 85 | + }, | ||
| 86 | + Center: { | ||
| 87 | + Title: merge(BASE_STYLES.Title, { left: 0, opacity: 1 }), | ||
| 88 | + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 1 }), | ||
| 89 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }), | ||
| 90 | + }, | ||
| 91 | + Right: { | ||
| 92 | + Title: merge(BASE_STYLES.Title, { left: SCREEN_WIDTH / 2, opacity: 0 }), | ||
| 93 | + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }), | ||
| 94 | + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }), | ||
| 95 | + }, | ||
| 96 | +}; | ||
| 97 | + | ||
| 98 | + | ||
| 99 | +var opacityRatio = 100; | ||
| 100 | + | ||
| 101 | +function buildSceneInterpolators(startStyles, endStyles) { | ||
| 102 | + return { | ||
| 103 | + Title: buildStyleInterpolator({ | ||
| 104 | + opacity: { | ||
| 105 | + type: 'linear', | ||
| 106 | + from: startStyles.Title.opacity, | ||
| 107 | + to: endStyles.Title.opacity, | ||
| 108 | + min: 0, | ||
| 109 | + max: 1, | ||
| 110 | + }, | ||
| 111 | + left: { | ||
| 112 | + type: 'linear', | ||
| 113 | + from: startStyles.Title.left, | ||
| 114 | + to: endStyles.Title.left, | ||
| 115 | + min: 0, | ||
| 116 | + max: 1, | ||
| 117 | + extrapolate: true, | ||
| 118 | + }, | ||
| 119 | + }), | ||
| 120 | + LeftButton: buildStyleInterpolator({ | ||
| 121 | + opacity: { | ||
| 122 | + type: 'linear', | ||
| 123 | + from: startStyles.LeftButton.opacity, | ||
| 124 | + to: endStyles.LeftButton.opacity, | ||
| 125 | + min: 0, | ||
| 126 | + max: 1, | ||
| 127 | + round: opacityRatio, | ||
| 128 | + }, | ||
| 129 | + left: { | ||
| 130 | + type: 'linear', | ||
| 131 | + from: startStyles.LeftButton.left, | ||
| 132 | + to: endStyles.LeftButton.left, | ||
| 133 | + min: 0, | ||
| 134 | + max: 1, | ||
| 135 | + }, | ||
| 136 | + }), | ||
| 137 | + RightButton: buildStyleInterpolator({ | ||
| 138 | + opacity: { | ||
| 139 | + type: 'linear', | ||
| 140 | + from: startStyles.RightButton.opacity, | ||
| 141 | + to: endStyles.RightButton.opacity, | ||
| 142 | + min: 0, | ||
| 143 | + max: 1, | ||
| 144 | + round: opacityRatio, | ||
| 145 | + }, | ||
| 146 | + left: { | ||
| 147 | + type: 'linear', | ||
| 148 | + from: startStyles.RightButton.left, | ||
| 149 | + to: endStyles.RightButton.left, | ||
| 150 | + min: 0, | ||
| 151 | + max: 1, | ||
| 152 | + extrapolate: true, | ||
| 153 | + }, | ||
| 154 | + }), | ||
| 155 | + }; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +var Interpolators = { | ||
| 159 | + // Animating *into* the center stage from the right | ||
| 160 | + RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center), | ||
| 161 | + // Animating out of the center stage, to the left | ||
| 162 | + CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left), | ||
| 163 | + // Both stages (animating *past* the center stage) | ||
| 164 | + RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left), | ||
| 165 | +}; | ||
| 166 | + | ||
| 167 | + | ||
| 168 | +module.exports = { | ||
| 169 | + General: { | ||
| 170 | + NavBarHeight: NAV_BAR_HEIGHT, | ||
| 171 | + StatusBarHeight: STATUS_BAR_HEIGHT, | ||
| 172 | + TotalNavHeight: NAV_HEIGHT, | ||
| 173 | + }, | ||
| 174 | + Interpolators, | ||
| 175 | + Stages, | ||
| 176 | +}; |
src/NavigatorSceneConfigs.js
0 → 100644
This diff is collapsed. Click to expand it.
src/Subscribable.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +import type EventEmitter from './EventEmitter'; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * Subscribable provides a mixin for safely subscribing a component to an | ||
| 33 | + * eventEmitter | ||
| 34 | + * | ||
| 35 | + * This will be replaced with the observe interface that will be coming soon to | ||
| 36 | + * React Core | ||
| 37 | + */ | ||
| 38 | + | ||
| 39 | +var Subscribable = {}; | ||
| 40 | + | ||
| 41 | +Subscribable.Mixin = { | ||
| 42 | + | ||
| 43 | + componentWillMount: function() { | ||
| 44 | + this._subscribableSubscriptions = []; | ||
| 45 | + }, | ||
| 46 | + | ||
| 47 | + componentWillUnmount: function() { | ||
| 48 | + this._subscribableSubscriptions.forEach( | ||
| 49 | + (subscription) => subscription.remove() | ||
| 50 | + ); | ||
| 51 | + this._subscribableSubscriptions = null; | ||
| 52 | + }, | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * Special form of calling `addListener` that *guarantees* that a | ||
| 56 | + * subscription *must* be tied to a component instance, and therefore will | ||
| 57 | + * be cleaned up when the component is unmounted. It is impossible to create | ||
| 58 | + * the subscription and pass it in - this method must be the one to create | ||
| 59 | + * the subscription and therefore can guarantee it is retained in a way that | ||
| 60 | + * will be cleaned up. | ||
| 61 | + * | ||
| 62 | + * @param {EventEmitter} eventEmitter emitter to subscribe to. | ||
| 63 | + * @param {string} eventType Type of event to listen to. | ||
| 64 | + * @param {function} listener Function to invoke when event occurs. | ||
| 65 | + * @param {object} context Object to use as listener context. | ||
| 66 | + */ | ||
| 67 | + addListenerOn: function( | ||
| 68 | + eventEmitter: EventEmitter, | ||
| 69 | + eventType: string, | ||
| 70 | + listener: Function, | ||
| 71 | + context: Object | ||
| 72 | + ) { | ||
| 73 | + this._subscribableSubscriptions.push( | ||
| 74 | + eventEmitter.addListener(eventType, listener, context) | ||
| 75 | + ); | ||
| 76 | + } | ||
| 77 | +}; | ||
| 78 | + | ||
| 79 | +module.exports = Subscribable; |
src/__tests__/NavigationContext-test.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + */ | ||
| 25 | +'use strict'; | ||
| 26 | + | ||
| 27 | +jest | ||
| 28 | + .disableAutomock() | ||
| 29 | + .mock('ErrorUtils'); | ||
| 30 | + | ||
| 31 | +var NavigationContext = require('NavigationContext'); | ||
| 32 | +var NavigationEvent = require('NavigationEvent'); | ||
| 33 | + | ||
| 34 | +describe('NavigationContext', () => { | ||
| 35 | + it('defaults `currentRoute` to null', () => { | ||
| 36 | + var context = new NavigationContext(); | ||
| 37 | + expect(context.currentRoute).toEqual(null); | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + it('updates `currentRoute`', () => { | ||
| 41 | + var context = new NavigationContext(); | ||
| 42 | + context.emit('didfocus', {route: {name: 'a'}}); | ||
| 43 | + expect(context.currentRoute.name).toEqual('a'); | ||
| 44 | + }); | ||
| 45 | + | ||
| 46 | + it('has parent', () => { | ||
| 47 | + var parent = new NavigationContext(); | ||
| 48 | + var child = new NavigationContext(); | ||
| 49 | + parent.appendChild(child); | ||
| 50 | + expect(child.parent).toBe(parent); | ||
| 51 | + }); | ||
| 52 | + | ||
| 53 | + it('has `top`', () => { | ||
| 54 | + var top = new NavigationContext(); | ||
| 55 | + var parent = new NavigationContext(); | ||
| 56 | + var child = new NavigationContext(); | ||
| 57 | + top.appendChild(parent); | ||
| 58 | + parent.appendChild(child); | ||
| 59 | + expect(child.top).toBe(top); | ||
| 60 | + }); | ||
| 61 | + | ||
| 62 | + it('captures event', () => { | ||
| 63 | + var parent = new NavigationContext(); | ||
| 64 | + var child = new NavigationContext(); | ||
| 65 | + parent.appendChild(child); | ||
| 66 | + | ||
| 67 | + var logs = []; | ||
| 68 | + | ||
| 69 | + var listener = (event) => { | ||
| 70 | + var {currentTarget, eventPhase, target, type} = event; | ||
| 71 | + logs.push({ | ||
| 72 | + currentTarget, | ||
| 73 | + eventPhase, | ||
| 74 | + target, | ||
| 75 | + type, | ||
| 76 | + }); | ||
| 77 | + }; | ||
| 78 | + | ||
| 79 | + parent.addListener('yo', listener, true); | ||
| 80 | + child.addListener('yo', listener, true); | ||
| 81 | + | ||
| 82 | + child.emit('yo'); | ||
| 83 | + | ||
| 84 | + expect(logs).toEqual([ | ||
| 85 | + { | ||
| 86 | + currentTarget: parent, | ||
| 87 | + eventPhase: NavigationEvent.CAPTURING_PHASE, | ||
| 88 | + target: child, | ||
| 89 | + type: 'yo', | ||
| 90 | + }, | ||
| 91 | + { | ||
| 92 | + currentTarget: child, | ||
| 93 | + eventPhase: NavigationEvent.AT_TARGET, | ||
| 94 | + target: child, | ||
| 95 | + type: 'yo', | ||
| 96 | + } | ||
| 97 | + ]); | ||
| 98 | + }); | ||
| 99 | + | ||
| 100 | + it('bubbles events', () => { | ||
| 101 | + var parent = new NavigationContext(); | ||
| 102 | + var child = new NavigationContext(); | ||
| 103 | + parent.appendChild(child); | ||
| 104 | + | ||
| 105 | + var logs = []; | ||
| 106 | + | ||
| 107 | + var listener = (event) => { | ||
| 108 | + var {currentTarget, eventPhase, target, type} = event; | ||
| 109 | + logs.push({ | ||
| 110 | + currentTarget, | ||
| 111 | + eventPhase, | ||
| 112 | + target, | ||
| 113 | + type, | ||
| 114 | + }); | ||
| 115 | + }; | ||
| 116 | + | ||
| 117 | + parent.addListener('yo', listener); | ||
| 118 | + child.addListener('yo', listener); | ||
| 119 | + | ||
| 120 | + child.emit('yo'); | ||
| 121 | + | ||
| 122 | + expect(logs).toEqual([ | ||
| 123 | + { | ||
| 124 | + currentTarget: child, | ||
| 125 | + eventPhase: NavigationEvent.AT_TARGET, | ||
| 126 | + target: child, | ||
| 127 | + type: 'yo', | ||
| 128 | + }, | ||
| 129 | + { | ||
| 130 | + currentTarget: parent, | ||
| 131 | + eventPhase: NavigationEvent.BUBBLING_PHASE, | ||
| 132 | + target: child, | ||
| 133 | + type: 'yo', | ||
| 134 | + }, | ||
| 135 | + ]); | ||
| 136 | + }); | ||
| 137 | + | ||
| 138 | + it('stops event propagation at capture phase', () => { | ||
| 139 | + var parent = new NavigationContext(); | ||
| 140 | + var child = new NavigationContext(); | ||
| 141 | + parent.appendChild(child); | ||
| 142 | + | ||
| 143 | + var counter = 0; | ||
| 144 | + | ||
| 145 | + parent.addListener('yo', event => event.stopPropagation(), true); | ||
| 146 | + child.addListener('yo', event => counter++, true); | ||
| 147 | + | ||
| 148 | + child.emit('yo'); | ||
| 149 | + | ||
| 150 | + expect(counter).toBe(0); | ||
| 151 | + }); | ||
| 152 | + | ||
| 153 | + it('stops event propagation at bubbling phase', () => { | ||
| 154 | + var parent = new NavigationContext(); | ||
| 155 | + var child = new NavigationContext(); | ||
| 156 | + parent.appendChild(child); | ||
| 157 | + | ||
| 158 | + var counter = 0; | ||
| 159 | + | ||
| 160 | + parent.addListener('yo', event => counter++); | ||
| 161 | + child.addListener('yo', event => event.stopPropagation()); | ||
| 162 | + | ||
| 163 | + child.emit('yo'); | ||
| 164 | + | ||
| 165 | + expect(counter).toBe(0); | ||
| 166 | + }); | ||
| 167 | + | ||
| 168 | + it('prevents event at capture phase', () => { | ||
| 169 | + var parent = new NavigationContext(); | ||
| 170 | + var child = new NavigationContext(); | ||
| 171 | + parent.appendChild(child); | ||
| 172 | + | ||
| 173 | + var val; | ||
| 174 | + parent.addListener('yo', event => event.preventDefault(), true); | ||
| 175 | + child.addListener('yo', event => val = event.defaultPrevented, true); | ||
| 176 | + | ||
| 177 | + child.emit('yo'); | ||
| 178 | + | ||
| 179 | + expect(val).toBe(true); | ||
| 180 | + }); | ||
| 181 | + | ||
| 182 | + it('prevents event at bubble phase', () => { | ||
| 183 | + var parent = new NavigationContext(); | ||
| 184 | + var child = new NavigationContext(); | ||
| 185 | + parent.appendChild(child); | ||
| 186 | + | ||
| 187 | + var val; | ||
| 188 | + parent.addListener('yo', event => val = event.defaultPrevented); | ||
| 189 | + child.addListener('yo', event => event.preventDefault()); | ||
| 190 | + | ||
| 191 | + child.emit('yo'); | ||
| 192 | + | ||
| 193 | + expect(val).toBe(true); | ||
| 194 | + }); | ||
| 195 | + | ||
| 196 | + it('emits nested events in order at capture phase', () => { | ||
| 197 | + var parent = new NavigationContext(); | ||
| 198 | + var child = new NavigationContext(); | ||
| 199 | + parent.appendChild(child); | ||
| 200 | + | ||
| 201 | + var logs = []; | ||
| 202 | + | ||
| 203 | + var listener = (event) => { | ||
| 204 | + var {currentTarget, type} = event; | ||
| 205 | + logs.push({ | ||
| 206 | + currentTarget, | ||
| 207 | + type, | ||
| 208 | + }); | ||
| 209 | + }; | ||
| 210 | + | ||
| 211 | + child.addListener('yo', event => { | ||
| 212 | + // event `didyo` should be fired after the full propagation cycle of the | ||
| 213 | + // `yo` event. | ||
| 214 | + child.emit('didyo'); | ||
| 215 | + }); | ||
| 216 | + | ||
| 217 | + parent.addListener('yo', listener, true); | ||
| 218 | + parent.addListener('didyo', listener, true); | ||
| 219 | + child.addListener('yo', listener, true); | ||
| 220 | + | ||
| 221 | + child.emit('yo'); | ||
| 222 | + | ||
| 223 | + expect(logs).toEqual([ | ||
| 224 | + {type: 'yo', currentTarget: parent}, | ||
| 225 | + {type: 'yo', currentTarget: child}, | ||
| 226 | + {type: 'didyo', currentTarget: parent}, | ||
| 227 | + ]); | ||
| 228 | + }); | ||
| 229 | + | ||
| 230 | + it('emits nested events in order at bubbling phase', () => { | ||
| 231 | + var parent = new NavigationContext(); | ||
| 232 | + var child = new NavigationContext(); | ||
| 233 | + parent.appendChild(child); | ||
| 234 | + | ||
| 235 | + var logs = []; | ||
| 236 | + | ||
| 237 | + var listener = (event) => { | ||
| 238 | + var {currentTarget, type} = event; | ||
| 239 | + logs.push({ | ||
| 240 | + currentTarget, | ||
| 241 | + type, | ||
| 242 | + }); | ||
| 243 | + }; | ||
| 244 | + | ||
| 245 | + child.addListener('yo', event => { | ||
| 246 | + // event `didyo` should be fired after the full propagation cycle of the | ||
| 247 | + // `yo` event. | ||
| 248 | + child.emit('didyo'); | ||
| 249 | + }); | ||
| 250 | + | ||
| 251 | + parent.addListener('yo', listener); | ||
| 252 | + child.addListener('yo', listener); | ||
| 253 | + parent.addListener('didyo', listener); | ||
| 254 | + | ||
| 255 | + child.emit('yo'); | ||
| 256 | + | ||
| 257 | + expect(logs).toEqual([ | ||
| 258 | + {type: 'yo', currentTarget: child}, | ||
| 259 | + {type: 'yo', currentTarget: parent}, | ||
| 260 | + {type: 'didyo', currentTarget: parent}, | ||
| 261 | + ]); | ||
| 262 | + }); | ||
| 263 | +}); |
src/__tests__/NavigationEvent-test.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + */ | ||
| 25 | +'use strict'; | ||
| 26 | + | ||
| 27 | +jest | ||
| 28 | + .unmock('NavigationEvent') | ||
| 29 | + .unmock('fbjs/lib/invariant'); | ||
| 30 | + | ||
| 31 | +var NavigationEvent = require('NavigationEvent'); | ||
| 32 | + | ||
| 33 | +describe('NavigationEvent', () => { | ||
| 34 | + it('constructs', () => { | ||
| 35 | + var target = {}; | ||
| 36 | + var event = new NavigationEvent('foo', target, 123); | ||
| 37 | + expect(event.type).toBe('foo'); | ||
| 38 | + expect(event.target).toBe(target); | ||
| 39 | + expect(event.data).toBe(123); | ||
| 40 | + }); | ||
| 41 | + | ||
| 42 | + it('constructs from pool', () => { | ||
| 43 | + var target = {}; | ||
| 44 | + var event = NavigationEvent.pool('foo', target, 123); | ||
| 45 | + expect(event.type).toBe('foo'); | ||
| 46 | + expect(event.target).toBe(target); | ||
| 47 | + expect(event.data).toBe(123); | ||
| 48 | + }); | ||
| 49 | + | ||
| 50 | + it('prevents default', () => { | ||
| 51 | + var event = new NavigationEvent('foo', {}, 123); | ||
| 52 | + expect(event.defaultPrevented).toBe(false); | ||
| 53 | + event.preventDefault(); | ||
| 54 | + expect(event.defaultPrevented).toBe(true); | ||
| 55 | + }); | ||
| 56 | + | ||
| 57 | + it('recycles', () => { | ||
| 58 | + var event1 = NavigationEvent.pool('foo', {}, 123); | ||
| 59 | + event1.dispose(); | ||
| 60 | + expect(event1.type).toBeFalsy(); | ||
| 61 | + expect(event1.data).toBe(null); | ||
| 62 | + expect(event1.target).toBe(null); | ||
| 63 | + | ||
| 64 | + var event2 = NavigationEvent.pool('bar', {}, 456); | ||
| 65 | + expect(event2.type).toBe('bar'); | ||
| 66 | + expect(event2).toBe(event1); | ||
| 67 | + }); | ||
| 68 | +}); |
src/__tests__/NavigationEventEmitter-test.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + */ | ||
| 25 | +'use strict'; | ||
| 26 | + | ||
| 27 | +jest | ||
| 28 | + .unmock('EmitterSubscription') | ||
| 29 | + .unmock('EventSubscription') | ||
| 30 | + .unmock('EventEmitter') | ||
| 31 | + .unmock('EventSubscriptionVendor') | ||
| 32 | + .unmock('NavigationEvent') | ||
| 33 | + .unmock('NavigationEventEmitter'); | ||
| 34 | + | ||
| 35 | +var NavigationEventEmitter = require('NavigationEventEmitter'); | ||
| 36 | + | ||
| 37 | +describe('NavigationEventEmitter', () => { | ||
| 38 | + it('emits event', () => { | ||
| 39 | + var context = {}; | ||
| 40 | + var emitter = new NavigationEventEmitter(context); | ||
| 41 | + var logs = []; | ||
| 42 | + | ||
| 43 | + emitter.addListener('ping', (event) => { | ||
| 44 | + var {type, data, target, defaultPrevented} = event; | ||
| 45 | + | ||
| 46 | + logs.push({ | ||
| 47 | + data, | ||
| 48 | + defaultPrevented, | ||
| 49 | + target, | ||
| 50 | + type, | ||
| 51 | + }); | ||
| 52 | + | ||
| 53 | + }); | ||
| 54 | + | ||
| 55 | + emitter.emit('ping', 'hello'); | ||
| 56 | + | ||
| 57 | + expect(logs.length).toBe(1); | ||
| 58 | + expect(logs[0].target).toBe(context); | ||
| 59 | + expect(logs[0].type).toBe('ping'); | ||
| 60 | + expect(logs[0].data).toBe('hello'); | ||
| 61 | + expect(logs[0].defaultPrevented).toBe(false); | ||
| 62 | + }); | ||
| 63 | + | ||
| 64 | + it('does not emit event that has no listeners', () => { | ||
| 65 | + var context = {}; | ||
| 66 | + var emitter = new NavigationEventEmitter(context); | ||
| 67 | + var pinged = false; | ||
| 68 | + | ||
| 69 | + emitter.addListener('ping', () => { | ||
| 70 | + pinged = true; | ||
| 71 | + }); | ||
| 72 | + | ||
| 73 | + emitter.emit('yo', 'bo'); | ||
| 74 | + expect(pinged).toBe(false); | ||
| 75 | + }); | ||
| 76 | + | ||
| 77 | + it('puts nested emit call in a queue', () => { | ||
| 78 | + var context = {}; | ||
| 79 | + var emitter = new NavigationEventEmitter(context); | ||
| 80 | + var logs = []; | ||
| 81 | + | ||
| 82 | + emitter.addListener('one', () => { | ||
| 83 | + logs.push(1); | ||
| 84 | + emitter.emit('two'); | ||
| 85 | + logs.push(2); | ||
| 86 | + }); | ||
| 87 | + | ||
| 88 | + emitter.addListener('two', () => { | ||
| 89 | + logs.push(3); | ||
| 90 | + emitter.emit('three'); | ||
| 91 | + logs.push(4); | ||
| 92 | + }); | ||
| 93 | + | ||
| 94 | + emitter.addListener('three', () => { | ||
| 95 | + logs.push(5); | ||
| 96 | + }); | ||
| 97 | + | ||
| 98 | + emitter.emit('one'); | ||
| 99 | + | ||
| 100 | + expect(logs).toEqual([1, 2, 3, 4, 5]); | ||
| 101 | + }); | ||
| 102 | + | ||
| 103 | + it('puts nested emit call in a queue should be in sequence order', () => { | ||
| 104 | + var context = {}; | ||
| 105 | + var emitter = new NavigationEventEmitter(context); | ||
| 106 | + var logs = []; | ||
| 107 | + | ||
| 108 | + emitter.addListener('one', () => { | ||
| 109 | + logs.push(1); | ||
| 110 | + emitter.emit('two'); | ||
| 111 | + emitter.emit('three'); | ||
| 112 | + logs.push(2); | ||
| 113 | + }); | ||
| 114 | + | ||
| 115 | + emitter.addListener('two', () => { | ||
| 116 | + logs.push(3); | ||
| 117 | + logs.push(4); | ||
| 118 | + }); | ||
| 119 | + | ||
| 120 | + emitter.addListener('three', () => { | ||
| 121 | + logs.push(5); | ||
| 122 | + }); | ||
| 123 | + | ||
| 124 | + emitter.emit('one'); | ||
| 125 | + | ||
| 126 | + expect(logs).toEqual([1, 2, 3, 4, 5]); | ||
| 127 | + }); | ||
| 128 | + | ||
| 129 | + it('calls callback after emitting', () => { | ||
| 130 | + var context = {}; | ||
| 131 | + var emitter = new NavigationEventEmitter(context); | ||
| 132 | + var logs = []; | ||
| 133 | + | ||
| 134 | + emitter.addListener('ping', (event) => { | ||
| 135 | + var {type, data, target, defaultPrevented} = event; | ||
| 136 | + logs.push({ | ||
| 137 | + data, | ||
| 138 | + defaultPrevented, | ||
| 139 | + target, | ||
| 140 | + type, | ||
| 141 | + }); | ||
| 142 | + event.preventDefault(); | ||
| 143 | + }); | ||
| 144 | + | ||
| 145 | + emitter.emit('ping', 'hello', (event) => { | ||
| 146 | + var {type, data, target, defaultPrevented} = event; | ||
| 147 | + logs.push({ | ||
| 148 | + data, | ||
| 149 | + defaultPrevented, | ||
| 150 | + target, | ||
| 151 | + type, | ||
| 152 | + }); | ||
| 153 | + }); | ||
| 154 | + | ||
| 155 | + expect(logs.length).toBe(2); | ||
| 156 | + expect(logs[1].target).toBe(context); | ||
| 157 | + expect(logs[1].type).toBe('ping'); | ||
| 158 | + expect(logs[1].data).toBe('hello'); | ||
| 159 | + expect(logs[1].defaultPrevented).toBe(true); | ||
| 160 | + }); | ||
| 161 | + | ||
| 162 | + it('calls callback after emitting the current event and before ' + | ||
| 163 | + 'emitting the next event', () => { | ||
| 164 | + var context = {}; | ||
| 165 | + var emitter = new NavigationEventEmitter(context); | ||
| 166 | + var logs = []; | ||
| 167 | + | ||
| 168 | + emitter.addListener('ping', (event) => { | ||
| 169 | + logs.push('ping'); | ||
| 170 | + emitter.emit('pong'); | ||
| 171 | + }); | ||
| 172 | + | ||
| 173 | + emitter.addListener('pong', (event) => { | ||
| 174 | + logs.push('pong'); | ||
| 175 | + }); | ||
| 176 | + | ||
| 177 | + emitter.emit('ping', null, () => { | ||
| 178 | + logs.push('did-ping'); | ||
| 179 | + }); | ||
| 180 | + | ||
| 181 | + expect(logs).toEqual([ | ||
| 182 | + 'ping', | ||
| 183 | + 'did-ping', | ||
| 184 | + 'pong', | ||
| 185 | + ]); | ||
| 186 | + }); | ||
| 187 | +}); |
src/__tests__/NavigationRouteStack-test.js
0 → 100644
This diff is collapsed. Click to expand it.
src/__tests__/NavigationTreeNode-test.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + */ | ||
| 4 | + | ||
| 5 | +'use strict'; | ||
| 6 | + | ||
| 7 | +jest | ||
| 8 | + .unmock('NavigationTreeNode') | ||
| 9 | + .unmock('fbjs/lib/invariant') | ||
| 10 | + .unmock('immutable'); | ||
| 11 | + | ||
| 12 | +var NavigationTreeNode = require('NavigationTreeNode'); | ||
| 13 | + | ||
| 14 | +describe('NavigationTreeNode-test', () => { | ||
| 15 | + it('should be empty', () => { | ||
| 16 | + var node = new NavigationTreeNode(); | ||
| 17 | + expect(node.getValue()).toEqual(undefined); | ||
| 18 | + expect(node.getParent()).toEqual(null); | ||
| 19 | + expect(node.getChildrenCount()).toEqual(0); | ||
| 20 | + expect(node.getChildAt(0)).toEqual(null); | ||
| 21 | + }); | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + it('should contain value', () => { | ||
| 25 | + var node = new NavigationTreeNode(123); | ||
| 26 | + expect(node.getValue()).toEqual(123); | ||
| 27 | + }); | ||
| 28 | + | ||
| 29 | + it('should appendChild', () => { | ||
| 30 | + var papa = new NavigationTreeNode('hedger'); | ||
| 31 | + var baby = new NavigationTreeNode('hedger jr'); | ||
| 32 | + papa.appendChild(baby); | ||
| 33 | + expect(papa.getChildAt(0)).toEqual(baby); | ||
| 34 | + expect(papa.getChildrenCount()).toEqual(1); | ||
| 35 | + expect(baby.getParent()).toEqual(papa); | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + it('should removeChild', () => { | ||
| 39 | + var papa = new NavigationTreeNode('Eddard Stark'); | ||
| 40 | + var baby = new NavigationTreeNode('Robb Stark'); | ||
| 41 | + papa.appendChild(baby); | ||
| 42 | + | ||
| 43 | + papa.removeChild(baby); | ||
| 44 | + expect(papa.getChildAt(0)).toEqual(null); | ||
| 45 | + expect(papa.getChildrenCount()).toEqual(0); | ||
| 46 | + expect(baby.getParent()).toEqual(null); | ||
| 47 | + }); | ||
| 48 | + | ||
| 49 | + it('should not remove non-child', () => { | ||
| 50 | + var papa = new NavigationTreeNode('dog'); | ||
| 51 | + var baby = new NavigationTreeNode('cat'); | ||
| 52 | + expect(papa.removeChild.bind(papa, baby)).toThrow(); | ||
| 53 | + }); | ||
| 54 | + | ||
| 55 | + it('should find child', () => { | ||
| 56 | + var papa = new NavigationTreeNode('Eddard Stark'); | ||
| 57 | + var baby = new NavigationTreeNode('Robb Stark'); | ||
| 58 | + | ||
| 59 | + papa.appendChild(baby); | ||
| 60 | + expect(papa.indexOf(baby)).toEqual(0); | ||
| 61 | + | ||
| 62 | + papa.removeChild(baby); | ||
| 63 | + expect(papa.indexOf(baby)).toEqual(-1); | ||
| 64 | + }); | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + it('should traverse each child', () => { | ||
| 68 | + var parent = new NavigationTreeNode(); | ||
| 69 | + parent.appendChild(new NavigationTreeNode('a')); | ||
| 70 | + parent.appendChild(new NavigationTreeNode('b')); | ||
| 71 | + parent.appendChild(new NavigationTreeNode('c')); | ||
| 72 | + var result = []; | ||
| 73 | + parent.forEach((child, index) => { | ||
| 74 | + result[index] = child.getValue(); | ||
| 75 | + }); | ||
| 76 | + | ||
| 77 | + expect(result).toEqual(['a', 'b', 'c']); | ||
| 78 | + }); | ||
| 79 | + | ||
| 80 | + it('should map children', () => { | ||
| 81 | + var parent = new NavigationTreeNode(); | ||
| 82 | + parent.appendChild(new NavigationTreeNode('a')); | ||
| 83 | + parent.appendChild(new NavigationTreeNode('b')); | ||
| 84 | + parent.appendChild(new NavigationTreeNode('c')); | ||
| 85 | + var result = parent.map((child, index) => { | ||
| 86 | + return child.getValue(); | ||
| 87 | + }); | ||
| 88 | + | ||
| 89 | + expect(result).toEqual(['a', 'b', 'c']); | ||
| 90 | + }); | ||
| 91 | + | ||
| 92 | + it('should traverse some children', () => { | ||
| 93 | + var parent = new NavigationTreeNode(); | ||
| 94 | + parent.appendChild(new NavigationTreeNode('a')); | ||
| 95 | + parent.appendChild(new NavigationTreeNode('b')); | ||
| 96 | + parent.appendChild(new NavigationTreeNode('c')); | ||
| 97 | + | ||
| 98 | + var result = []; | ||
| 99 | + var value = parent.some((child, index) => { | ||
| 100 | + if (index > 1) { | ||
| 101 | + return true; | ||
| 102 | + } else { | ||
| 103 | + result[index] = child.getValue(); | ||
| 104 | + } | ||
| 105 | + }); | ||
| 106 | + | ||
| 107 | + expect(value).toEqual(true); | ||
| 108 | + expect(result).toEqual(['a', 'b']); | ||
| 109 | + }); | ||
| 110 | +}); |
src/buildStyleInterpolator.js
0 → 100644
This diff is collapsed. Click to expand it.
src/clamp.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @typechecks | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * @param {number} value | ||
| 31 | + * @param {number} min | ||
| 32 | + * @param {number} max | ||
| 33 | + * @return {number} | ||
| 34 | + */ | ||
| 35 | +function clamp(min, value, max) { | ||
| 36 | + if (value < min) { | ||
| 37 | + return min; | ||
| 38 | + } | ||
| 39 | + if (value > max) { | ||
| 40 | + return max; | ||
| 41 | + } | ||
| 42 | + return value; | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +module.exports = clamp; |
src/flattenStyle.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @flow | ||
| 26 | + */ | ||
| 27 | +'use strict'; | ||
| 28 | + | ||
| 29 | +var invariant = require('fbjs/lib/invariant'); | ||
| 30 | + | ||
| 31 | +import type { StyleObj } from 'StyleSheetTypes'; | ||
| 32 | + | ||
| 33 | +function getStyle(style) { | ||
| 34 | + if (style && typeof style === 'number') { | ||
| 35 | + debugger; | ||
| 36 | + invariant(false, "Error when using Navigator from react-native-custom-components. Please provide a raw object to `props.sceneStyle` instead of a StyleSheet reference."); | ||
| 37 | + } | ||
| 38 | + return style; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +function flattenStyle(style: ?StyleObj): ?Object { | ||
| 42 | + if (!style) { | ||
| 43 | + return undefined; | ||
| 44 | + } | ||
| 45 | + invariant(style !== true, 'style may be false but not true'); | ||
| 46 | + | ||
| 47 | + if (!Array.isArray(style)) { | ||
| 48 | + return getStyle(style); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + var result = {}; | ||
| 52 | + for (var i = 0, styleLength = style.length; i < styleLength; ++i) { | ||
| 53 | + var computedStyle = flattenStyle(style[i]); | ||
| 54 | + if (computedStyle) { | ||
| 55 | + for (var key in computedStyle) { | ||
| 56 | + result[key] = computedStyle[key]; | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + return result; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +module.exports = flattenStyle; |
src/guid.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | + | ||
| 27 | +/* eslint-disable no-bitwise */ | ||
| 28 | + | ||
| 29 | +'use strict'; | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * Module that provides a function for creating a unique identifier. | ||
| 33 | + * The returned value does not conform to the GUID standard, but should | ||
| 34 | + * be globally unique in the context of the browser. | ||
| 35 | + */ | ||
| 36 | +function guid() { | ||
| 37 | + return 'f' + (Math.random() * (1 << 30)).toString(16).replace('.', ''); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +module.exports = guid; |
src/merge.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + */ | ||
| 26 | + | ||
| 27 | +"use strict"; | ||
| 28 | + | ||
| 29 | +var mergeInto = require('./mergeInto'); | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * Shallow merges two structures into a return value, without mutating either. | ||
| 33 | + * | ||
| 34 | + * @param {?object} one Optional object with properties to merge from. | ||
| 35 | + * @param {?object} two Optional object with properties to merge from. | ||
| 36 | + * @return {object} The shallow extension of one by two. | ||
| 37 | + */ | ||
| 38 | +var merge = function(one, two) { | ||
| 39 | + var result = {}; | ||
| 40 | + mergeInto(result, one); | ||
| 41 | + mergeInto(result, two); | ||
| 42 | + return result; | ||
| 43 | +}; | ||
| 44 | + | ||
| 45 | +module.exports = merge; |
src/mergeHelpers.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * requiresPolyfills: Array.isArray | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | +"use strict"; | ||
| 29 | + | ||
| 30 | +var invariant = require('fbjs/lib/invariant'); | ||
| 31 | +var keyMirror = require('fbjs/lib/keyMirror'); | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * Maximum number of levels to traverse. Will catch circular structures. | ||
| 35 | + * @const | ||
| 36 | + */ | ||
| 37 | +var MAX_MERGE_DEPTH = 36; | ||
| 38 | + | ||
| 39 | +/** | ||
| 40 | + * We won't worry about edge cases like new String('x') or new Boolean(true). | ||
| 41 | + * Functions are considered terminals, and arrays are not. | ||
| 42 | + * @param {*} o The item/object/value to test. | ||
| 43 | + * @return {boolean} true iff the argument is a terminal. | ||
| 44 | + */ | ||
| 45 | +var isTerminal = function(o) { | ||
| 46 | + return typeof o !== 'object' || o === null; | ||
| 47 | +}; | ||
| 48 | + | ||
| 49 | +var mergeHelpers = { | ||
| 50 | + | ||
| 51 | + MAX_MERGE_DEPTH: MAX_MERGE_DEPTH, | ||
| 52 | + | ||
| 53 | + isTerminal: isTerminal, | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * Converts null/undefined values into empty object. | ||
| 57 | + * | ||
| 58 | + * @param {?Object=} arg Argument to be normalized (nullable optional) | ||
| 59 | + * @return {!Object} | ||
| 60 | + */ | ||
| 61 | + normalizeMergeArg: function(arg) { | ||
| 62 | + return arg === undefined || arg === null ? {} : arg; | ||
| 63 | + }, | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * If merging Arrays, a merge strategy *must* be supplied. If not, it is | ||
| 67 | + * likely the caller's fault. If this function is ever called with anything | ||
| 68 | + * but `one` and `two` being `Array`s, it is the fault of the merge utilities. | ||
| 69 | + * | ||
| 70 | + * @param {*} one Array to merge into. | ||
| 71 | + * @param {*} two Array to merge from. | ||
| 72 | + */ | ||
| 73 | + checkMergeArrayArgs: function(one, two) { | ||
| 74 | + invariant( | ||
| 75 | + Array.isArray(one) && Array.isArray(two), | ||
| 76 | + 'Tried to merge arrays, instead got %s and %s.', | ||
| 77 | + one, | ||
| 78 | + two | ||
| 79 | + ); | ||
| 80 | + }, | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * @param {*} one Object to merge into. | ||
| 84 | + * @param {*} two Object to merge from. | ||
| 85 | + */ | ||
| 86 | + checkMergeObjectArgs: function(one, two) { | ||
| 87 | + mergeHelpers.checkMergeObjectArg(one); | ||
| 88 | + mergeHelpers.checkMergeObjectArg(two); | ||
| 89 | + }, | ||
| 90 | + | ||
| 91 | + /** | ||
| 92 | + * @param {*} arg | ||
| 93 | + */ | ||
| 94 | + checkMergeObjectArg: function(arg) { | ||
| 95 | + invariant( | ||
| 96 | + !isTerminal(arg) && !Array.isArray(arg), | ||
| 97 | + 'Tried to merge an object, instead got %s.', | ||
| 98 | + arg | ||
| 99 | + ); | ||
| 100 | + }, | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * @param {*} arg | ||
| 104 | + */ | ||
| 105 | + checkMergeIntoObjectArg: function(arg) { | ||
| 106 | + invariant( | ||
| 107 | + (!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg), | ||
| 108 | + 'Tried to merge into an object, instead got %s.', | ||
| 109 | + arg | ||
| 110 | + ); | ||
| 111 | + }, | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * Checks that a merge was not given a circular object or an object that had | ||
| 115 | + * too great of depth. | ||
| 116 | + * | ||
| 117 | + * @param {number} Level of recursion to validate against maximum. | ||
| 118 | + */ | ||
| 119 | + checkMergeLevel: function(level) { | ||
| 120 | + invariant( | ||
| 121 | + level < MAX_MERGE_DEPTH, | ||
| 122 | + 'Maximum deep merge depth exceeded. You may be attempting to merge ' + | ||
| 123 | + 'circular structures in an unsupported way.' | ||
| 124 | + ); | ||
| 125 | + }, | ||
| 126 | + | ||
| 127 | + /** | ||
| 128 | + * Checks that the supplied merge strategy is valid. | ||
| 129 | + * | ||
| 130 | + * @param {string} Array merge strategy. | ||
| 131 | + */ | ||
| 132 | + checkArrayStrategy: function(strategy) { | ||
| 133 | + invariant( | ||
| 134 | + strategy === undefined || strategy in mergeHelpers.ArrayStrategies, | ||
| 135 | + 'You must provide an array strategy to deep merge functions to ' + | ||
| 136 | + 'instruct the deep merge how to resolve merging two arrays.' | ||
| 137 | + ); | ||
| 138 | + }, | ||
| 139 | + | ||
| 140 | + /** | ||
| 141 | + * Set of possible behaviors of merge algorithms when encountering two Arrays | ||
| 142 | + * that must be merged together. | ||
| 143 | + * - `clobber`: The left `Array` is ignored. | ||
| 144 | + * - `indexByIndex`: The result is achieved by recursively deep merging at | ||
| 145 | + * each index. (not yet supported.) | ||
| 146 | + */ | ||
| 147 | + ArrayStrategies: keyMirror({ | ||
| 148 | + Clobber: true, | ||
| 149 | + IndexByIndex: true | ||
| 150 | + }) | ||
| 151 | + | ||
| 152 | +}; | ||
| 153 | + | ||
| 154 | +module.exports = mergeHelpers; |
src/mergeInto.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Copyright (c) 2015, Facebook, Inc. All rights reserved. | ||
| 3 | + * | ||
| 4 | + * Facebook, Inc. ("Facebook") owns all right, title and interest, including | ||
| 5 | + * all intellectual property and other proprietary rights, in and to the React | ||
| 6 | + * Native CustomComponents software (the "Software"). Subject to your | ||
| 7 | + * compliance with these terms, you are hereby granted a non-exclusive, | ||
| 8 | + * worldwide, royalty-free copyright license to (1) use and copy the Software; | ||
| 9 | + * and (2) reproduce and distribute the Software as part of your own software | ||
| 10 | + * ("Your Software"). Facebook reserves all rights not expressly granted to | ||
| 11 | + * you in this license agreement. | ||
| 12 | + * | ||
| 13 | + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS | ||
| 14 | + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
| 15 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. | ||
| 16 | + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR | ||
| 17 | + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
| 18 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
| 19 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
| 20 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
| 21 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
| 22 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF | ||
| 23 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 24 | + * | ||
| 25 | + * @typechecks static-only | ||
| 26 | + */ | ||
| 27 | + | ||
| 28 | +"use strict"; | ||
| 29 | + | ||
| 30 | +var mergeHelpers = require('./mergeHelpers'); | ||
| 31 | + | ||
| 32 | +var checkMergeObjectArg = mergeHelpers.checkMergeObjectArg; | ||
| 33 | +var checkMergeIntoObjectArg = mergeHelpers.checkMergeIntoObjectArg; | ||
| 34 | + | ||
| 35 | +/** | ||
| 36 | + * Shallow merges two structures by mutating the first parameter. | ||
| 37 | + * | ||
| 38 | + * @param {object|function} one Object to be merged into. | ||
| 39 | + * @param {?object} two Optional object with properties to merge from. | ||
| 40 | + */ | ||
| 41 | +function mergeInto(one, two) { | ||
| 42 | + checkMergeIntoObjectArg(one); | ||
| 43 | + if (two != null) { | ||
| 44 | + checkMergeObjectArg(two); | ||
| 45 | + for (var key in two) { | ||
| 46 | + if (!two.hasOwnProperty(key)) { | ||
| 47 | + continue; | ||
| 48 | + } | ||
| 49 | + one[key] = two[key]; | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +module.exports = mergeInto; |
-
Please register or login to post a comment