getNativeComponentAttributes.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @flow
  8. * @format
  9. */
  10. 'use strict';
  11. const ReactNativeStyleAttributes = require('../Components/View/ReactNativeStyleAttributes');
  12. const UIManager = require('./UIManager');
  13. const insetsDiffer = require('../Utilities/differ/insetsDiffer');
  14. const invariant = require('invariant');
  15. const matricesDiffer = require('../Utilities/differ/matricesDiffer');
  16. const pointsDiffer = require('../Utilities/differ/pointsDiffer');
  17. const processColor = require('../StyleSheet/processColor');
  18. const processColorArray = require('../StyleSheet/processColorArray');
  19. const resolveAssetSource = require('../Image/resolveAssetSource');
  20. const sizesDiffer = require('../Utilities/differ/sizesDiffer');
  21. const warning = require('fbjs/lib/warning');
  22. function getNativeComponentAttributes(uiViewClassName: string): any {
  23. const viewConfig = UIManager.getViewManagerConfig(uiViewClassName);
  24. invariant(
  25. viewConfig != null && viewConfig.NativeProps != null,
  26. 'requireNativeComponent: "%s" was not found in the UIManager.',
  27. uiViewClassName,
  28. );
  29. // TODO: This seems like a whole lot of runtime initialization for every
  30. // native component that can be either avoided or simplified.
  31. let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
  32. let nativeProps = viewConfig.NativeProps;
  33. while (baseModuleName) {
  34. const baseModule = UIManager.getViewManagerConfig(baseModuleName);
  35. if (!baseModule) {
  36. warning(false, 'Base module "%s" does not exist', baseModuleName);
  37. baseModuleName = null;
  38. } else {
  39. bubblingEventTypes = {
  40. ...baseModule.bubblingEventTypes,
  41. ...bubblingEventTypes,
  42. };
  43. directEventTypes = {
  44. ...baseModule.directEventTypes,
  45. ...directEventTypes,
  46. };
  47. nativeProps = {
  48. ...baseModule.NativeProps,
  49. ...nativeProps,
  50. };
  51. baseModuleName = baseModule.baseModuleName;
  52. }
  53. }
  54. const validAttributes = {};
  55. for (const key in nativeProps) {
  56. const typeName = nativeProps[key];
  57. const diff = getDifferForType(typeName);
  58. const process = getProcessorForType(typeName);
  59. validAttributes[key] =
  60. diff == null && process == null ? true : {diff, process};
  61. }
  62. // Unfortunately, the current setup declares style properties as top-level
  63. // props. This makes it so we allow style properties in the `style` prop.
  64. // TODO: Move style properties into a `style` prop and disallow them as
  65. // top-level props on the native side.
  66. validAttributes.style = ReactNativeStyleAttributes;
  67. Object.assign(viewConfig, {
  68. uiViewClassName,
  69. validAttributes,
  70. bubblingEventTypes,
  71. directEventTypes,
  72. });
  73. if (!hasAttachedDefaultEventTypes) {
  74. attachDefaultEventTypes(viewConfig);
  75. hasAttachedDefaultEventTypes = true;
  76. }
  77. return viewConfig;
  78. }
  79. // TODO: Figure out how this makes sense. We're using a global boolean to only
  80. // initialize this on the first eagerly initialized native component.
  81. let hasAttachedDefaultEventTypes = false;
  82. function attachDefaultEventTypes(viewConfig: any) {
  83. // This is supported on UIManager platforms (ex: Android),
  84. // as lazy view managers are not implemented for all platforms.
  85. // See [UIManager] for details on constants and implementations.
  86. const constants = UIManager.getConstants();
  87. if (constants.ViewManagerNames || constants.LazyViewManagersEnabled) {
  88. // Lazy view managers enabled.
  89. viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
  90. } else {
  91. viewConfig.bubblingEventTypes = merge(
  92. viewConfig.bubblingEventTypes,
  93. constants.genericBubblingEventTypes,
  94. );
  95. viewConfig.directEventTypes = merge(
  96. viewConfig.directEventTypes,
  97. constants.genericDirectEventTypes,
  98. );
  99. }
  100. }
  101. // TODO: Figure out how to avoid all this runtime initialization cost.
  102. function merge(destination: ?Object, source: ?Object): ?Object {
  103. if (!source) {
  104. return destination;
  105. }
  106. if (!destination) {
  107. return source;
  108. }
  109. for (const key in source) {
  110. if (!source.hasOwnProperty(key)) {
  111. continue;
  112. }
  113. let sourceValue = source[key];
  114. if (destination.hasOwnProperty(key)) {
  115. const destinationValue = destination[key];
  116. if (
  117. typeof sourceValue === 'object' &&
  118. typeof destinationValue === 'object'
  119. ) {
  120. sourceValue = merge(destinationValue, sourceValue);
  121. }
  122. }
  123. destination[key] = sourceValue;
  124. }
  125. return destination;
  126. }
  127. function getDifferForType(
  128. typeName: string,
  129. ): ?(prevProp: any, nextProp: any) => boolean {
  130. switch (typeName) {
  131. // iOS Types
  132. case 'CATransform3D':
  133. return matricesDiffer;
  134. case 'CGPoint':
  135. return pointsDiffer;
  136. case 'CGSize':
  137. return sizesDiffer;
  138. case 'UIEdgeInsets':
  139. return insetsDiffer;
  140. // Android Types
  141. // (not yet implemented)
  142. }
  143. return null;
  144. }
  145. function getProcessorForType(typeName: string): ?(nextProp: any) => any {
  146. switch (typeName) {
  147. // iOS Types
  148. case 'CGColor':
  149. case 'UIColor':
  150. return processColor;
  151. case 'CGColorArray':
  152. case 'UIColorArray':
  153. return processColorArray;
  154. case 'CGImage':
  155. case 'UIImage':
  156. case 'RCTImageSource':
  157. return resolveAssetSource;
  158. // Android Types
  159. case 'Color':
  160. return processColor;
  161. case 'ColorArray':
  162. return processColorArray;
  163. }
  164. return null;
  165. }
  166. module.exports = getNativeComponentAttributes;