2015-04-17 00:14:11 +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.
|
|
|
|
|
*
|
|
|
|
|
* @providesModule requireNativeComponent
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2017-09-15 01:01:27 +00:00
|
|
|
|
const ReactNativeBridgeEventPlugin = require('ReactNativeBridgeEventPlugin');
|
2016-10-27 13:54:26 +00:00
|
|
|
|
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
|
|
|
|
|
const UIManager = require('UIManager');
|
|
|
|
|
|
2016-11-04 12:40:26 +00:00
|
|
|
|
const createReactNativeComponentClass = require('createReactNativeComponentClass');
|
2016-10-27 13:54:26 +00:00
|
|
|
|
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');
|
2017-09-06 10:25:01 +00:00
|
|
|
|
/* $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. */
|
2017-09-15 01:01:25 +00:00
|
|
|
|
const invariant = require('fbjs/lib/invariant');
|
2016-10-27 13:54:26 +00:00
|
|
|
|
const warning = require('fbjs/lib/warning');
|
2015-04-17 00:14:11 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Used to create React components that directly wrap native component
|
|
|
|
|
* implementations. Config information is extracted from data exported from the
|
2015-11-27 13:39:00 +00:00
|
|
|
|
* UIManager module. You should also wrap the native component in a
|
2015-04-17 01:17:19 +00:00
|
|
|
|
* hand-written component with full propTypes definitions and other
|
2015-07-25 01:31:55 +00:00
|
|
|
|
* documentation - pass the hand-written component in as `componentInterface` to
|
2015-04-17 01:17:19 +00:00
|
|
|
|
* verify all the native props are documented via `propTypes`.
|
|
|
|
|
*
|
|
|
|
|
* If some native props shouldn't be exposed in the wrapper interface, you can
|
2015-07-25 01:31:55 +00:00
|
|
|
|
* pass null for `componentInterface` and call `verifyPropTypes` directly
|
2015-04-17 01:17:19 +00:00
|
|
|
|
* with `nativePropsToIgnore`;
|
2015-04-17 00:14:11 +00:00
|
|
|
|
*
|
|
|
|
|
* Common types are lined up with the appropriate prop differs with
|
|
|
|
|
* `TypeToDifferMap`. Non-scalar types not in the map default to `deepDiffer`.
|
|
|
|
|
*/
|
2015-07-25 01:31:55 +00:00
|
|
|
|
import type { ComponentInterface } from 'verifyPropTypes';
|
|
|
|
|
|
2015-04-17 01:17:19 +00:00
|
|
|
|
function requireNativeComponent(
|
|
|
|
|
viewName: string,
|
2015-07-25 01:31:55 +00:00
|
|
|
|
componentInterface?: ?ComponentInterface,
|
|
|
|
|
extraConfig?: ?{nativeOnly?: Object},
|
2017-08-18 01:36:54 +00:00
|
|
|
|
): React$ComponentType<any> | string {
|
2017-09-15 01:01:25 +00:00
|
|
|
|
// 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
|
|
|
|
|
);
|
2015-09-17 15:36:08 +00:00
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
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;
|
Support native ViewManager inheritance on iOS
Summary:
**Motivation**
This is a re-worked version of #14260, by shergin's suggestion.
For iOS, if you want to inherit from a native ViewManagers, your custom ViewManager will not automatically export the parents' props. So the only way to do this today, is to basically copy/paste the parent ViewManager-file, and add your own custom logic.
With this PR, this is made more extensible by exporting the `baseModuleName` (i.e. the iOS `superclass` of the ViewManager), and then using that value to re-establish the inheritance relationship in `requireNativeComponent`.
**Test plan**
I've run this with a test project, and it works fine there. But needs more testing.
Opened this PR as [per shergin's suggestion](https://github.com/facebook/react-native/pull/10946#issuecomment-311860545) though, so we can discuss approach.
**Discussion**
* Android already supports inheritance, so this change should be compatible with that. But, not every prop available on `UIManager.RCTView.NativeProps` is actually exported by every ViewManager. So should `UIManager.RCTView.NativeProps` still be merged with `viewConfig.NativeProps`, even if the individual ViewManager does not export/use them to begin with?
* Does this break other platforms? [UWP](https://github.com/Microsoft/react-native-windows)?
Closes https://github.com/facebook/react-native/pull/14775
Differential Revision: D5392953
Pulled By: shergin
fbshipit-source-id: 5212da616acfba50cc285e2997d183cf8b2cd09f
2017-07-10 22:48:34 +00:00
|
|
|
|
} else {
|
2017-09-15 01:01:25 +00:00
|
|
|
|
viewConfig.propTypes = null;
|
Support native ViewManager inheritance on iOS
Summary:
**Motivation**
This is a re-worked version of #14260, by shergin's suggestion.
For iOS, if you want to inherit from a native ViewManagers, your custom ViewManager will not automatically export the parents' props. So the only way to do this today, is to basically copy/paste the parent ViewManager-file, and add your own custom logic.
With this PR, this is made more extensible by exporting the `baseModuleName` (i.e. the iOS `superclass` of the ViewManager), and then using that value to re-establish the inheritance relationship in `requireNativeComponent`.
**Test plan**
I've run this with a test project, and it works fine there. But needs more testing.
Opened this PR as [per shergin's suggestion](https://github.com/facebook/react-native/pull/10946#issuecomment-311860545) though, so we can discuss approach.
**Discussion**
* Android already supports inheritance, so this change should be compatible with that. But, not every prop available on `UIManager.RCTView.NativeProps` is actually exported by every ViewManager. So should `UIManager.RCTView.NativeProps` still be merged with `viewConfig.NativeProps`, even if the individual ViewManager does not export/use them to begin with?
* Does this break other platforms? [UWP](https://github.com/Microsoft/react-native-windows)?
Closes https://github.com/facebook/react-native/pull/14775
Differential Revision: D5392953
Pulled By: shergin
fbshipit-source-id: 5212da616acfba50cc285e2997d183cf8b2cd09f
2017-07-10 22:48:34 +00:00
|
|
|
|
}
|
2016-10-27 13:54:26 +00:00
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2015-09-17 15:36:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
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;
|
2015-09-17 15:36:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
// 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
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-10-06 03:21:48 +00:00
|
|
|
|
|
2017-09-15 01:01:27 +00:00
|
|
|
|
// 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);
|
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
return viewConfig;
|
2015-04-17 01:17:19 +00:00
|
|
|
|
}
|
2016-10-27 13:54:26 +00:00
|
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
|
return createReactNativeComponentClass(viewName, getViewConfig);
|
2015-04-17 00:14:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-25 13:30:05 +00:00
|
|
|
|
const TypeToDifferMap = {
|
2015-04-17 00:14:11 +00:00
|
|
|
|
// iOS Types
|
|
|
|
|
CATransform3D: matricesDiffer,
|
|
|
|
|
CGPoint: pointsDiffer,
|
|
|
|
|
CGSize: sizesDiffer,
|
|
|
|
|
UIEdgeInsets: insetsDiffer,
|
|
|
|
|
// Android Types
|
|
|
|
|
// (not yet implemented)
|
|
|
|
|
};
|
|
|
|
|
|
2017-01-31 21:00:12 +00:00
|
|
|
|
function processColorArray(colors: ?Array<any>): ?Array<?number> {
|
Reimplement color processing
Summary:
**Problem:**
As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:
```
tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')
```
The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.
**Solution:**
While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:
- The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
- Stricter parsing of at
Closes https://github.com/facebook/react-native/pull/5529
Reviewed By: svcscm
Differential Revision: D2872015
Pulled By: nicklockwood
fb-gh-sync-id: df78244eefce6cf8e8ed2ea51f58d6b232de16f9
2016-01-29 17:11:53 +00:00
|
|
|
|
return colors && colors.map(processColor);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-25 13:30:05 +00:00
|
|
|
|
const TypeToProcessorMap = {
|
2015-09-17 15:36:08 +00:00
|
|
|
|
// iOS Types
|
|
|
|
|
CGColor: processColor,
|
Reimplement color processing
Summary:
**Problem:**
As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:
```
tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')
```
The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.
**Solution:**
While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:
- The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
- Stricter parsing of at
Closes https://github.com/facebook/react-native/pull/5529
Reviewed By: svcscm
Differential Revision: D2872015
Pulled By: nicklockwood
fb-gh-sync-id: df78244eefce6cf8e8ed2ea51f58d6b232de16f9
2016-01-29 17:11:53 +00:00
|
|
|
|
CGColorArray: processColorArray,
|
2015-09-17 15:36:08 +00:00
|
|
|
|
UIColor: processColor,
|
Reimplement color processing
Summary:
**Problem:**
As I was trying to document what color formats we supported, I realized that our current implementation based on the open source project tinycolor supported some crazy things. A few examples that were all valid:
```
tinycolor('abc')
tinycolor(' #abc ')
tinycolor('##abc')
tinycolor('rgb 255 0 0')
tinycolor('RGBA(0, 1, 2)')
tinycolor('rgb (0, 1, 2)')
tinycolor('hsv(0, 1, 2)')
tinycolor({r: 10, g: 10, b: 10})
tinycolor('hsl(1%, 2, 3)')
tinycolor('rgb(1.0, 2.0, 3.0)')
tinycolor('rgb(1%, 2%, 3%)')
```
The integrations of tinycolor were also really bad. processColor added "support" for pure numbers and an array of colors!?? ColorPropTypes did some crazy trim().toString() and repeated a bad error message twice.
**Solution:**
While iteratively cleaning the file, I eventually ended up reimplementing it entierly. Major changes are:
- The API is now dead simple: returns null if it doesn't parse or returns the int32 representation of the color
- Stricter parsing of at
Closes https://github.com/facebook/react-native/pull/5529
Reviewed By: svcscm
Differential Revision: D2872015
Pulled By: nicklockwood
fb-gh-sync-id: df78244eefce6cf8e8ed2ea51f58d6b232de16f9
2016-01-29 17:11:53 +00:00
|
|
|
|
UIColorArray: processColorArray,
|
2015-12-08 11:29:08 +00:00
|
|
|
|
CGImage: resolveAssetSource,
|
|
|
|
|
UIImage: resolveAssetSource,
|
|
|
|
|
RCTImageSource: resolveAssetSource,
|
2015-09-17 15:36:08 +00:00
|
|
|
|
// Android Types
|
|
|
|
|
Color: processColor,
|
2016-02-02 15:11:41 +00:00
|
|
|
|
ColorArray: processColorArray,
|
2015-09-17 15:36:08 +00:00
|
|
|
|
};
|
|
|
|
|
|
2015-04-17 00:14:11 +00:00
|
|
|
|
module.exports = requireNativeComponent;
|