build nested navigation context based on the hierarchy of navigators.

Reviewed By: zjj010104

Differential Revision: D2598388

fb-gh-sync-id: 9655bcc86021678984e2a29df20ad2496a1762d1
This commit is contained in:
Hedger Wang 2015-10-30 11:06:35 -07:00 committed by facebook-github-bot-9
parent 854689dcfa
commit a33fe94dac
1 changed files with 80 additions and 43 deletions

View File

@ -30,6 +30,8 @@ var NavigationEvent = require('NavigationEvent');
var NavigationEventEmitter = require('NavigationEventEmitter');
var NavigationTreeNode = require('NavigationTreeNode');
var Set = require('Set');
var emptyFunction = require('emptyFunction');
var invariant = require('invariant');
@ -41,6 +43,13 @@ var {
CAPTURING_PHASE,
} = NavigationEvent;
// Event types that do not support event bubbling, capturing and
// reconciliation API (e.g event.preventDefault(), event.stopPropagation()).
var LegacyEventTypes = new Set([
'willfocus',
'didfocus',
]);
/**
* Class that contains the info and methods for app navigation.
*/
@ -88,9 +97,14 @@ class NavigationContext {
context: ?Object,
useCapture: ?boolean
): EventSubscription {
if (LegacyEventTypes.has(eventType)) {
useCapture = false;
}
var emitter = useCapture ?
this._captureEventEmitter :
this._bubbleEventEmitter;
if (emitter) {
return emitter.addListener(eventType, listener, context);
} else {
@ -109,50 +123,65 @@ class NavigationContext {
this._emitCounter++;
var targets = [this];
var parentTarget = this.parent;
while (parentTarget) {
targets.unshift(parentTarget);
parentTarget = parentTarget.parent;
if (LegacyEventTypes.has(eventType)) {
// Legacy events does not support event bubbling and reconciliation.
this.__emit(
eventType,
data,
null,
{
defaultPrevented: false,
eventPhase: AT_TARGET,
propagationStopped: true,
target: this,
}
);
} else {
var targets = [this];
var parentTarget = this.parent;
while (parentTarget) {
targets.unshift(parentTarget);
parentTarget = parentTarget.parent;
}
var propagationStopped = false;
var defaultPrevented = false;
var callback = (event) => {
propagationStopped = propagationStopped || event.isPropagationStopped();
defaultPrevented = defaultPrevented || event.defaultPrevented;
};
// Capture phase
targets.some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: CAPTURING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
// bubble phase
targets.reverse().some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: BUBBLING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
}
var propagationStopped = false;
var defaultPrevented = false;
var callback = (event) => {
propagationStopped = propagationStopped || event.isPropagationStopped();
defaultPrevented = defaultPrevented || event.defaultPrevented;
};
// capture phase
targets.some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: CAPTURING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
// bubble phase
targets.reverse().some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: BUBBLING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
if (didEmitCallback) {
var event = NavigationEvent.pool(eventType, this, data);
propagationStopped && event.stopPropagation();
@ -189,9 +218,15 @@ class NavigationContext {
case CAPTURING_PHASE: // phase = 1
emitter = this._captureEventEmitter;
break;
case AT_TARGET: // phase = 2
emitter = this._bubbleEventEmitter;
break;
case BUBBLING_PHASE: // phase = 3
emitter = this._bubbleEventEmitter;
break;
default:
invariant(false, 'invalid event phase %s', extraInfo.eventPhase);
}
@ -214,8 +249,10 @@ class NavigationContext {
_onFocus(event: NavigationEvent): void {
invariant(
event.data && event.data.hasOwnProperty('route'),
'didfocus event should provide route'
'event type "%s" should provide route',
event.type
);
this._currentRoute = event.data.route;
}
}