2015-04-17 00:14:11 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2015-04-17 00:14:11 +00:00
|
|
|
*
|
|
|
|
* @flow
|
2017-09-25 05:57:35 +00:00
|
|
|
* @format
|
2015-04-17 00:14:11 +00:00
|
|
|
*/
|
2018-05-07 23:45:16 +00:00
|
|
|
|
2015-04-17 00:14:11 +00:00
|
|
|
'use strict';
|
|
|
|
|
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');
|
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
|
|
|
|
|
|
|
/**
|
2018-06-01 19:37:22 +00:00
|
|
|
* Creates values that can be used like React components which represent native
|
|
|
|
* view managers. You should create JavaScript modules that wrap these values so
|
|
|
|
* that the results are memoized. Example:
|
2015-04-17 01:17:19 +00:00
|
|
|
*
|
2018-06-01 19:37:22 +00:00
|
|
|
* const View = requireNativeComponent('RCTView');
|
2015-04-17 00:14:11 +00:00
|
|
|
*
|
|
|
|
*/
|
2018-06-01 19:37:22 +00:00
|
|
|
const requireNativeComponent = (uiViewClassName: string): string =>
|
|
|
|
createReactNativeComponentClass(uiViewClassName, () => {
|
|
|
|
const viewConfig = UIManager[uiViewClassName];
|
2017-09-15 01:01:25 +00:00
|
|
|
|
|
|
|
invariant(
|
2018-05-07 23:45:11 +00:00
|
|
|
viewConfig != null && viewConfig.NativeProps != null,
|
2018-05-07 23:45:16 +00:00
|
|
|
'requireNativeComponent: "%s" was not found in the UIManager.',
|
2018-06-01 19:37:22 +00:00
|
|
|
uiViewClassName,
|
2017-09-15 01:01:25 +00:00
|
|
|
);
|
2015-09-17 15:36:08 +00:00
|
|
|
|
2018-05-07 23:45:16 +00:00
|
|
|
// TODO: This seems like a whole lot of runtime initialization for every
|
|
|
|
// native component that can be either avoided or simplified.
|
|
|
|
let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
|
2018-01-12 03:00:27 +00:00
|
|
|
let nativeProps = viewConfig.NativeProps;
|
2017-09-15 01:01:25 +00:00
|
|
|
while (baseModuleName) {
|
|
|
|
const baseModule = UIManager[baseModuleName];
|
|
|
|
if (!baseModule) {
|
|
|
|
warning(false, 'Base module "%s" does not exist', baseModuleName);
|
|
|
|
baseModuleName = null;
|
|
|
|
} else {
|
2018-01-12 03:00:27 +00:00
|
|
|
bubblingEventTypes = {
|
|
|
|
...baseModule.bubblingEventTypes,
|
|
|
|
...bubblingEventTypes,
|
|
|
|
};
|
|
|
|
directEventTypes = {
|
|
|
|
...baseModule.directEventTypes,
|
|
|
|
...directEventTypes,
|
|
|
|
};
|
|
|
|
nativeProps = {
|
|
|
|
...baseModule.NativeProps,
|
|
|
|
...nativeProps,
|
|
|
|
};
|
2017-09-15 01:01:25 +00:00
|
|
|
baseModuleName = baseModule.baseModuleName;
|
|
|
|
}
|
2015-09-17 15:36:08 +00:00
|
|
|
}
|
|
|
|
|
2018-06-01 19:37:22 +00:00
|
|
|
const validAttributes = {};
|
2018-01-12 03:00:27 +00:00
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
for (const key in nativeProps) {
|
2018-05-07 23:45:14 +00:00
|
|
|
const typeName = nativeProps[key];
|
|
|
|
const diff = getDifferForType(typeName);
|
|
|
|
const process = getProcessorForType(typeName);
|
2017-09-15 01:01:25 +00:00
|
|
|
|
2018-06-01 19:37:22 +00:00
|
|
|
validAttributes[key] =
|
2018-05-07 23:45:14 +00:00
|
|
|
diff == null && process == null ? true : {diff, process};
|
2015-09-17 15:36:08 +00:00
|
|
|
}
|
|
|
|
|
2018-05-07 23:45:16 +00:00
|
|
|
// Unfortunately, the current setup declares style properties as top-level
|
|
|
|
// props. This makes it so we allow style properties in the `style` prop.
|
|
|
|
// TODO: Move style properties into a `style` prop and disallow them as
|
|
|
|
// top-level props on the native side.
|
2018-06-01 19:37:22 +00:00
|
|
|
validAttributes.style = ReactNativeStyleAttributes;
|
2018-05-07 23:45:16 +00:00
|
|
|
|
|
|
|
Object.assign(viewConfig, {
|
2018-06-01 19:37:22 +00:00
|
|
|
uiViewClassName,
|
|
|
|
validAttributes,
|
2018-05-07 23:45:16 +00:00
|
|
|
bubblingEventTypes,
|
|
|
|
directEventTypes,
|
|
|
|
});
|
2017-09-15 01:01:25 +00:00
|
|
|
|
2017-11-07 19:16:45 +00:00
|
|
|
if (!hasAttachedDefaultEventTypes) {
|
|
|
|
attachDefaultEventTypes(viewConfig);
|
|
|
|
hasAttachedDefaultEventTypes = true;
|
|
|
|
}
|
2017-10-03 12:24:32 +00:00
|
|
|
|
2017-09-15 01:01:25 +00:00
|
|
|
return viewConfig;
|
2018-05-07 23:45:16 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: Figure out how this makes sense. We're using a global boolean to only
|
|
|
|
// initialize this on the first eagerly initialized native component.
|
|
|
|
let hasAttachedDefaultEventTypes = false;
|
|
|
|
function attachDefaultEventTypes(viewConfig: any) {
|
|
|
|
// This is supported on UIManager platforms (ex: Android),
|
|
|
|
// as lazy view managers are not implemented for all platforms.
|
|
|
|
// See [UIManager] for details on constants and implementations.
|
|
|
|
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,
|
|
|
|
);
|
2015-04-17 01:17:19 +00:00
|
|
|
}
|
2018-05-07 23:45:16 +00:00
|
|
|
}
|
2016-10-27 13:54:26 +00:00
|
|
|
|
2018-05-07 23:45:16 +00:00
|
|
|
// TODO: Figure out how to avoid all this runtime initialization cost.
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
let 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;
|
2015-04-17 00:14:11 +00:00
|
|
|
}
|
|
|
|
|
2018-05-07 23:45:14 +00:00
|
|
|
function getDifferForType(
|
|
|
|
typeName: string,
|
|
|
|
): ?(prevProp: any, nextProp: any) => boolean {
|
|
|
|
switch (typeName) {
|
|
|
|
// iOS Types
|
|
|
|
case 'CATransform3D':
|
|
|
|
return matricesDiffer;
|
|
|
|
case 'CGPoint':
|
|
|
|
return pointsDiffer;
|
|
|
|
case 'CGSize':
|
|
|
|
return sizesDiffer;
|
|
|
|
case 'UIEdgeInsets':
|
|
|
|
return insetsDiffer;
|
|
|
|
// Android Types
|
|
|
|
// (not yet implemented)
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getProcessorForType(typeName: string): ?(nextProp: any) => any {
|
|
|
|
switch (typeName) {
|
|
|
|
// iOS Types
|
|
|
|
case 'CGColor':
|
|
|
|
case 'UIColor':
|
|
|
|
return processColor;
|
|
|
|
case 'CGColorArray':
|
|
|
|
case 'UIColorArray':
|
|
|
|
return processColorArray;
|
|
|
|
case 'CGImage':
|
|
|
|
case 'UIImage':
|
|
|
|
case 'RCTImageSource':
|
|
|
|
return resolveAssetSource;
|
|
|
|
// Android Types
|
|
|
|
case 'Color':
|
|
|
|
return processColor;
|
|
|
|
case 'ColorArray':
|
|
|
|
return processColorArray;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2015-04-17 00:14:11 +00:00
|
|
|
|
2017-01-31 21:00:12 +00:00
|
|
|
function processColorArray(colors: ?Array<any>): ?Array<?number> {
|
2018-05-07 23:45:14 +00:00
|
|
|
return colors == null ? null : colors.map(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
|
|
|
}
|
|
|
|
|
2015-04-17 00:14:11 +00:00
|
|
|
module.exports = requireNativeComponent;
|