241 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) 2015-present, Facebook, Inc.
 | 
						|
 *
 | 
						|
 * This source code is licensed under the MIT license found in the
 | 
						|
 * LICENSE file in the root directory of this source tree.
 | 
						|
 *
 | 
						|
 * @providesModule requireFabricComponent
 | 
						|
 * @flow
 | 
						|
 * @format
 | 
						|
 */
 | 
						|
'use strict';
 | 
						|
 | 
						|
const Platform = require('Platform');
 | 
						|
const {
 | 
						|
  ReactNativeBridgeEventPlugin,
 | 
						|
  createReactNativeComponentClass,
 | 
						|
} = require('ReactFabricInternals');
 | 
						|
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
 | 
						|
const UIManager = require('UIManager');
 | 
						|
 | 
						|
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');
 | 
						|
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';
 | 
						|
 | 
						|
let hasAttachedDefaultEventTypes: boolean = false;
 | 
						|
 | 
						|
function requireNativeComponent(
 | 
						|
  viewName: string,
 | 
						|
  componentInterface?: ?ComponentInterface,
 | 
						|
  extraConfig?: ?{nativeOnly?: Object},
 | 
						|
): React$ComponentType<any> | string {
 | 
						|
  function attachDefaultEventTypes(viewConfig: any) {
 | 
						|
    if (Platform.OS === 'android') {
 | 
						|
      // This is supported on Android platform only,
 | 
						|
      // as lazy view managers discovery is Android-specific.
 | 
						|
      if (UIManager.ViewManagerNames) {
 | 
						|
        // Lazy view managers enabled.
 | 
						|
        viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
 | 
						|
      } else {
 | 
						|
        viewConfig.bubblingEventTypes = merge(
 | 
						|
          viewConfig.bubblingEventTypes,
 | 
						|
          UIManager.genericBubblingEventTypes,
 | 
						|
        );
 | 
						|
        viewConfig.directEventTypes = merge(
 | 
						|
          viewConfig.directEventTypes,
 | 
						|
          UIManager.genericDirectEventTypes,
 | 
						|
        );
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function merge(destination: ?Object, source: ?Object): ?Object {
 | 
						|
    if (!source) {
 | 
						|
      return destination;
 | 
						|
    }
 | 
						|
    if (!destination) {
 | 
						|
      return source;
 | 
						|
    }
 | 
						|
 | 
						|
    for (const key in source) {
 | 
						|
      if (!source.hasOwnProperty(key)) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      var sourceValue = source[key];
 | 
						|
      if (destination.hasOwnProperty(key)) {
 | 
						|
        const destinationValue = destination[key];
 | 
						|
        if (
 | 
						|
          typeof sourceValue === 'object' &&
 | 
						|
          typeof destinationValue === 'object'
 | 
						|
        ) {
 | 
						|
          sourceValue = merge(destinationValue, sourceValue);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      destination[key] = sourceValue;
 | 
						|
    }
 | 
						|
    return destination;
 | 
						|
  }
 | 
						|
 | 
						|
  // 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 bubblingEventTypes = viewConfig.bubblingEventTypes;
 | 
						|
    let directEventTypes = viewConfig.directEventTypes;
 | 
						|
    let nativeProps = viewConfig.NativeProps;
 | 
						|
    while (baseModuleName) {
 | 
						|
      const baseModule = UIManager[baseModuleName];
 | 
						|
      if (!baseModule) {
 | 
						|
        warning(false, 'Base module "%s" does not exist', baseModuleName);
 | 
						|
        baseModuleName = null;
 | 
						|
      } else {
 | 
						|
        bubblingEventTypes = {
 | 
						|
          ...baseModule.bubblingEventTypes,
 | 
						|
          ...bubblingEventTypes,
 | 
						|
        };
 | 
						|
        directEventTypes = {
 | 
						|
          ...baseModule.directEventTypes,
 | 
						|
          ...directEventTypes,
 | 
						|
        };
 | 
						|
        nativeProps = {
 | 
						|
          ...baseModule.NativeProps,
 | 
						|
          ...nativeProps,
 | 
						|
        };
 | 
						|
        baseModuleName = baseModule.baseModuleName;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    viewConfig.bubblingEventTypes = bubblingEventTypes;
 | 
						|
    viewConfig.directEventTypes = directEventTypes;
 | 
						|
 | 
						|
    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,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    if (!hasAttachedDefaultEventTypes) {
 | 
						|
      attachDefaultEventTypes(viewConfig);
 | 
						|
      hasAttachedDefaultEventTypes = true;
 | 
						|
    }
 | 
						|
 | 
						|
    // Register this view's event types with the ReactNative renderer.
 | 
						|
    // This enables view managers to be initialized lazily, improving perf,
 | 
						|
    // While also enabling 3rd party components to define custom event types.
 | 
						|
    ReactNativeBridgeEventPlugin.processEventTypes(viewConfig);
 | 
						|
 | 
						|
    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;
 |