2015-02-20 04:10:52 +00:00
|
|
|
/**
|
2015-03-23 20:35:08 +00:00
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
2015-02-20 04:10:52 +00:00
|
|
|
*
|
|
|
|
* @providesModule NativeMethodsMixin
|
2015-03-26 00:49:46 +00:00
|
|
|
* @flow
|
2015-02-20 04:10:52 +00:00
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2015-03-17 10:08:52 +00:00
|
|
|
var NativeModules = require('NativeModules');
|
2015-03-18 05:22:03 +00:00
|
|
|
var RCTPOPAnimationManager = NativeModules.POPAnimationManager;
|
|
|
|
var RCTUIManager = NativeModules.UIManager;
|
2015-02-20 04:10:52 +00:00
|
|
|
var TextInputState = require('TextInputState');
|
|
|
|
|
2015-05-13 01:55:13 +00:00
|
|
|
var findNodeHandle = require('findNodeHandle');
|
2015-02-20 04:10:52 +00:00
|
|
|
var flattenStyle = require('flattenStyle');
|
|
|
|
var invariant = require('invariant');
|
|
|
|
var mergeFast = require('mergeFast');
|
2015-04-23 17:23:07 +00:00
|
|
|
var precomputeStyle = require('precomputeStyle');
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-26 00:49:46 +00:00
|
|
|
type MeasureOnSuccessCallback = (
|
|
|
|
x: number,
|
|
|
|
y: number,
|
|
|
|
width: number,
|
|
|
|
height: number,
|
|
|
|
pageX: number,
|
|
|
|
pageY: number
|
|
|
|
) => void
|
|
|
|
|
|
|
|
type MeasureLayoutOnSuccessCallback = (
|
|
|
|
left: number,
|
|
|
|
top: number,
|
|
|
|
width: number,
|
|
|
|
height: number
|
|
|
|
) => void
|
|
|
|
|
|
|
|
var animationIDInvariant = function(
|
|
|
|
funcName: string,
|
|
|
|
anim: number
|
|
|
|
) {
|
2015-02-20 04:10:52 +00:00
|
|
|
invariant(
|
|
|
|
anim,
|
|
|
|
funcName + ' must be called with a valid animation ID returned from' +
|
|
|
|
' POPAnimation.createAnimation, received: "' + anim + '"'
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
var NativeMethodsMixin = {
|
2015-03-26 00:49:46 +00:00
|
|
|
addAnimation: function(anim: number, callback?: (finished: bool) => void) {
|
2015-02-20 04:10:52 +00:00
|
|
|
animationIDInvariant('addAnimation', anim);
|
2015-05-14 01:33:43 +00:00
|
|
|
RCTPOPAnimationManager.addAnimation(
|
|
|
|
findNodeHandle(this),
|
|
|
|
anim,
|
|
|
|
mountSafeCallback(this, callback)
|
|
|
|
);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
2015-03-26 00:49:46 +00:00
|
|
|
removeAnimation: function(anim: number) {
|
2015-02-20 04:10:52 +00:00
|
|
|
animationIDInvariant('removeAnimation', anim);
|
2015-05-13 01:55:13 +00:00
|
|
|
RCTPOPAnimationManager.removeAnimation(findNodeHandle(this), anim);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
2015-03-26 00:49:46 +00:00
|
|
|
measure: function(callback: MeasureOnSuccessCallback) {
|
2015-05-14 01:33:43 +00:00
|
|
|
RCTUIManager.measure(
|
|
|
|
findNodeHandle(this),
|
|
|
|
mountSafeCallback(this, callback)
|
|
|
|
);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
2015-03-26 00:49:46 +00:00
|
|
|
measureLayout: function(
|
|
|
|
relativeToNativeNode: number,
|
|
|
|
onSuccess: MeasureLayoutOnSuccessCallback,
|
|
|
|
onFail: () => void /* currently unused */
|
|
|
|
) {
|
2015-03-17 10:08:46 +00:00
|
|
|
RCTUIManager.measureLayout(
|
2015-05-13 01:55:13 +00:00
|
|
|
findNodeHandle(this),
|
2015-02-20 04:10:52 +00:00
|
|
|
relativeToNativeNode,
|
2015-05-14 01:33:43 +00:00
|
|
|
mountSafeCallback(this, onFail),
|
|
|
|
mountSafeCallback(this, onSuccess)
|
2015-02-20 04:10:52 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function sends props straight to native. They will not participate
|
|
|
|
* in future diff process, this means that if you do not include them in the
|
|
|
|
* next render, they will remain active.
|
|
|
|
*/
|
2015-03-26 00:49:46 +00:00
|
|
|
setNativeProps: function(nativeProps: Object) {
|
2015-02-20 04:10:52 +00:00
|
|
|
// nativeProps contains a style attribute that's going to be flattened
|
|
|
|
// and all the attributes expanded in place. In order to make this
|
|
|
|
// process do as few allocations and copies as possible, we return
|
|
|
|
// one if the other is empty. Only if both have values then we create
|
|
|
|
// a new object and merge.
|
|
|
|
var hasOnlyStyle = true;
|
|
|
|
for (var key in nativeProps) {
|
|
|
|
if (key !== 'style') {
|
|
|
|
hasOnlyStyle = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-04-23 17:23:07 +00:00
|
|
|
var style = precomputeStyle(flattenStyle(nativeProps.style));
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
var props = null;
|
|
|
|
if (hasOnlyStyle) {
|
|
|
|
props = style;
|
|
|
|
} else if (!style) {
|
|
|
|
props = nativeProps;
|
|
|
|
} else {
|
|
|
|
props = mergeFast(nativeProps, style);
|
|
|
|
}
|
|
|
|
|
2015-03-17 10:08:52 +00:00
|
|
|
RCTUIManager.updateView(
|
2015-05-13 01:55:13 +00:00
|
|
|
findNodeHandle(this),
|
2015-02-20 04:10:52 +00:00
|
|
|
this.viewConfig.uiViewClassName,
|
|
|
|
props
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
focus: function() {
|
2015-05-13 01:55:13 +00:00
|
|
|
TextInputState.focusTextInput(findNodeHandle(this));
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
blur: function() {
|
2015-05-13 01:55:13 +00:00
|
|
|
TextInputState.blurTextInput(findNodeHandle(this));
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function throwOnStylesProp(component, props) {
|
|
|
|
if (props.styles !== undefined) {
|
|
|
|
var owner = component._owner || null;
|
|
|
|
var name = component.constructor.displayName;
|
|
|
|
var msg = '`styles` is not a supported property of `' + name + '`, did ' +
|
|
|
|
'you mean `style` (singular)?';
|
|
|
|
if (owner && owner.constructor && owner.constructor.displayName) {
|
|
|
|
msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' +
|
|
|
|
' component.';
|
|
|
|
}
|
|
|
|
throw new Error(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (__DEV__) {
|
2015-03-26 00:49:46 +00:00
|
|
|
// hide this from Flow since we can't define these properties outside of
|
|
|
|
// __DEV__ without actually implementing them (setting them to undefined
|
|
|
|
// isn't allowed by ReactClass)
|
|
|
|
var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
|
2015-02-20 04:10:52 +00:00
|
|
|
invariant(
|
2015-03-26 00:49:46 +00:00
|
|
|
!NativeMethodsMixin_DEV.componentWillMount &&
|
|
|
|
!NativeMethodsMixin_DEV.componentWillReceiveProps,
|
2015-02-20 04:10:52 +00:00
|
|
|
'Do not override existing functions.'
|
|
|
|
);
|
2015-03-26 00:49:46 +00:00
|
|
|
NativeMethodsMixin_DEV.componentWillMount = function () {
|
2015-02-20 04:10:52 +00:00
|
|
|
throwOnStylesProp(this, this.props);
|
|
|
|
};
|
2015-03-26 00:49:46 +00:00
|
|
|
NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) {
|
2015-02-20 04:10:52 +00:00
|
|
|
throwOnStylesProp(this, newProps);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-05-15 17:54:25 +00:00
|
|
|
/**
|
|
|
|
* In the future, we should cleanup callbacks by cancelling them instead of
|
|
|
|
* using this.
|
|
|
|
*/
|
|
|
|
var mountSafeCallback = function(context: ReactComponent, callback: ?Function): any {
|
|
|
|
return function() {
|
|
|
|
if (!callback || (context.isMounted && !context.isMounted())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return callback.apply(context, arguments);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
module.exports = NativeMethodsMixin;
|