164 lines
5.5 KiB
JavaScript
164 lines
5.5 KiB
JavaScript
/**
|
||
* 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 requireNativeComponent
|
||
* @flow
|
||
*/
|
||
'use strict';
|
||
|
||
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
|
||
const UIManager = require('UIManager');
|
||
|
||
const createReactNativeComponentClass = require('createReactNativeComponentClass');
|
||
const insetsDiffer = require('insetsDiffer');
|
||
const matricesDiffer = require('matricesDiffer');
|
||
const pointsDiffer = require('pointsDiffer');
|
||
const processColor = require('processColor');
|
||
const resolveAssetSource = require('resolveAssetSource');
|
||
const sizesDiffer = require('sizesDiffer');
|
||
const verifyPropTypes = require('verifyPropTypes');
|
||
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
|
||
* found when Flow v0.54 was deployed. To see the error delete this comment and
|
||
* run Flow. */
|
||
const invariant = require('fbjs/lib/invariant');
|
||
const warning = require('fbjs/lib/warning');
|
||
|
||
/**
|
||
* Used to create React components that directly wrap native component
|
||
* implementations. Config information is extracted from data exported from the
|
||
* UIManager module. You should also wrap the native component in a
|
||
* hand-written component with full propTypes definitions and other
|
||
* documentation - pass the hand-written component in as `componentInterface` to
|
||
* verify all the native props are documented via `propTypes`.
|
||
*
|
||
* If some native props shouldn't be exposed in the wrapper interface, you can
|
||
* pass null for `componentInterface` and call `verifyPropTypes` directly
|
||
* with `nativePropsToIgnore`;
|
||
*
|
||
* Common types are lined up with the appropriate prop differs with
|
||
* `TypeToDifferMap`. Non-scalar types not in the map default to `deepDiffer`.
|
||
*/
|
||
import type { ComponentInterface } from 'verifyPropTypes';
|
||
|
||
function requireNativeComponent(
|
||
viewName: string,
|
||
componentInterface?: ?ComponentInterface,
|
||
extraConfig?: ?{nativeOnly?: Object},
|
||
): React$ComponentType<any> | string {
|
||
// Don't load the ViewConfig from UIManager until it's needed for rendering.
|
||
// Lazy-loading this can help avoid Prepack deopts.
|
||
function getViewConfig() {
|
||
const viewConfig = UIManager[viewName];
|
||
|
||
invariant(
|
||
viewConfig != null &&
|
||
!viewConfig.NativeProps != null,
|
||
'Native component for "%s" does not exist',
|
||
viewName
|
||
);
|
||
|
||
viewConfig.uiViewClassName = viewName;
|
||
viewConfig.validAttributes = {};
|
||
|
||
// ReactNative `View.propTypes` have been deprecated in favor of
|
||
// `ViewPropTypes`. In their place a temporary getter has been added with a
|
||
// deprecated warning message. Avoid triggering that warning here by using
|
||
// temporary workaround, __propTypesSecretDontUseThesePlease.
|
||
// TODO (bvaughn) Revert this particular change any time after April 1
|
||
if (componentInterface) {
|
||
viewConfig.propTypes =
|
||
typeof componentInterface.__propTypesSecretDontUseThesePlease === 'object'
|
||
? componentInterface.__propTypesSecretDontUseThesePlease
|
||
: componentInterface.propTypes;
|
||
} else {
|
||
viewConfig.propTypes = null;
|
||
}
|
||
|
||
let baseModuleName = viewConfig.baseModuleName;
|
||
let nativeProps = { ...viewConfig.NativeProps };
|
||
while (baseModuleName) {
|
||
const baseModule = UIManager[baseModuleName];
|
||
if (!baseModule) {
|
||
warning(false, 'Base module "%s" does not exist', baseModuleName);
|
||
baseModuleName = null;
|
||
} else {
|
||
nativeProps = { ...nativeProps, ...baseModule.NativeProps };
|
||
baseModuleName = baseModule.baseModuleName;
|
||
}
|
||
}
|
||
|
||
for (const key in nativeProps) {
|
||
let useAttribute = false;
|
||
const attribute = {};
|
||
|
||
const differ = TypeToDifferMap[nativeProps[key]];
|
||
if (differ) {
|
||
attribute.diff = differ;
|
||
useAttribute = true;
|
||
}
|
||
|
||
const processor = TypeToProcessorMap[nativeProps[key]];
|
||
if (processor) {
|
||
attribute.process = processor;
|
||
useAttribute = true;
|
||
}
|
||
|
||
viewConfig.validAttributes[key] = useAttribute ? attribute : true;
|
||
}
|
||
|
||
// Unfortunately, the current set up puts the style properties on the top
|
||
// level props object. We also need to add the nested form for API
|
||
// compatibility. This allows these props on both the top level and the
|
||
// nested style level. TODO: Move these to nested declarations on the
|
||
// native side.
|
||
viewConfig.validAttributes.style = ReactNativeStyleAttributes;
|
||
|
||
if (__DEV__) {
|
||
componentInterface && verifyPropTypes(
|
||
componentInterface,
|
||
viewConfig,
|
||
extraConfig && extraConfig.nativeOnly
|
||
);
|
||
}
|
||
|
||
return viewConfig;
|
||
}
|
||
|
||
return createReactNativeComponentClass(viewName, getViewConfig);
|
||
}
|
||
|
||
const TypeToDifferMap = {
|
||
// iOS Types
|
||
CATransform3D: matricesDiffer,
|
||
CGPoint: pointsDiffer,
|
||
CGSize: sizesDiffer,
|
||
UIEdgeInsets: insetsDiffer,
|
||
// Android Types
|
||
// (not yet implemented)
|
||
};
|
||
|
||
function processColorArray(colors: ?Array<any>): ?Array<?number> {
|
||
return colors && colors.map(processColor);
|
||
}
|
||
|
||
const TypeToProcessorMap = {
|
||
// iOS Types
|
||
CGColor: processColor,
|
||
CGColorArray: processColorArray,
|
||
UIColor: processColor,
|
||
UIColorArray: processColorArray,
|
||
CGImage: resolveAssetSource,
|
||
UIImage: resolveAssetSource,
|
||
RCTImageSource: resolveAssetSource,
|
||
// Android Types
|
||
Color: processColor,
|
||
ColorArray: processColorArray,
|
||
};
|
||
|
||
module.exports = requireNativeComponent;
|