/** * 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. * * @providesModule NativeMethodsMixin * @flow */ 'use strict'; var NativeModules = require('NativeModules'); var RCTPOPAnimationManager = NativeModules.POPAnimationManager; var RCTUIManager = NativeModules.UIManager; var TextInputState = require('TextInputState'); var findNodeHandle = require('findNodeHandle'); var flattenStyle = require('flattenStyle'); var invariant = require('invariant'); var mergeFast = require('mergeFast'); var precomputeStyle = require('precomputeStyle'); 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 ) { invariant( anim, funcName + ' must be called with a valid animation ID returned from' + ' POPAnimation.createAnimation, received: "' + anim + '"' ); }; var NativeMethodsMixin = { addAnimation: function(anim: number, callback?: (finished: bool) => void) { animationIDInvariant('addAnimation', anim); RCTPOPAnimationManager.addAnimation( findNodeHandle(this), anim, mountSafeCallback(this, callback) ); }, removeAnimation: function(anim: number) { animationIDInvariant('removeAnimation', anim); RCTPOPAnimationManager.removeAnimation(findNodeHandle(this), anim); }, measure: function(callback: MeasureOnSuccessCallback) { RCTUIManager.measure( findNodeHandle(this), mountSafeCallback(this, callback) ); }, measureLayout: function( relativeToNativeNode: number, onSuccess: MeasureLayoutOnSuccessCallback, onFail: () => void /* currently unused */ ) { RCTUIManager.measureLayout( findNodeHandle(this), relativeToNativeNode, mountSafeCallback(this, onFail), mountSafeCallback(this, onSuccess) ); }, /** * 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. */ setNativeProps: function(nativeProps: Object) { // 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; } } var style = precomputeStyle(flattenStyle(nativeProps.style)); var props = null; if (hasOnlyStyle) { props = style; } else if (!style) { props = nativeProps; } else { props = mergeFast(nativeProps, style); } RCTUIManager.updateView( findNodeHandle(this), this.viewConfig.uiViewClassName, props ); }, focus: function() { TextInputState.focusTextInput(findNodeHandle(this)); }, blur: function() { TextInputState.blurTextInput(findNodeHandle(this)); } }; 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__) { // 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); invariant( !NativeMethodsMixin_DEV.componentWillMount && !NativeMethodsMixin_DEV.componentWillReceiveProps, 'Do not override existing functions.' ); NativeMethodsMixin_DEV.componentWillMount = function () { throwOnStylesProp(this, this.props); }; NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) { throwOnStylesProp(this, newProps); }; } /** * 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); }; }; module.exports = NativeMethodsMixin;