RCTViewManager.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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. #import "RCTViewManager.h"
  8. #import "RCTBorderStyle.h"
  9. #import "RCTBridge.h"
  10. #import "RCTConvert+Transform.h"
  11. #import "RCTConvert.h"
  12. #import "RCTEventDispatcher.h"
  13. #import "RCTLog.h"
  14. #import "RCTShadowView.h"
  15. #import "RCTUIManager.h"
  16. #import "RCTUIManagerUtils.h"
  17. #import "RCTUtils.h"
  18. #import "RCTView.h"
  19. #import "UIView+React.h"
  20. #if TARGET_OS_TV
  21. #import "RCTTVView.h"
  22. #endif
  23. @implementation RCTConvert (UIAccessibilityTraits)
  24. RCT_MULTI_ENUM_CONVERTER(
  25. UIAccessibilityTraits,
  26. (@{
  27. @"none" : @(UIAccessibilityTraitNone),
  28. @"button" : @(UIAccessibilityTraitButton),
  29. @"link" : @(UIAccessibilityTraitLink),
  30. @"header" : @(UIAccessibilityTraitHeader),
  31. @"search" : @(UIAccessibilityTraitSearchField),
  32. @"image" : @(UIAccessibilityTraitImage),
  33. @"imagebutton" : @(UIAccessibilityTraitImage | UIAccessibilityTraitButton),
  34. @"selected" : @(UIAccessibilityTraitSelected),
  35. @"plays" : @(UIAccessibilityTraitPlaysSound),
  36. @"key" : @(UIAccessibilityTraitKeyboardKey),
  37. @"keyboardkey" : @(UIAccessibilityTraitKeyboardKey),
  38. @"text" : @(UIAccessibilityTraitStaticText),
  39. @"summary" : @(UIAccessibilityTraitSummaryElement),
  40. @"disabled" : @(UIAccessibilityTraitNotEnabled),
  41. @"frequentUpdates" : @(UIAccessibilityTraitUpdatesFrequently),
  42. @"startsMedia" : @(UIAccessibilityTraitStartsMediaSession),
  43. @"adjustable" : @(UIAccessibilityTraitAdjustable),
  44. @"allowsDirectInteraction" : @(UIAccessibilityTraitAllowsDirectInteraction),
  45. @"pageTurn" : @(UIAccessibilityTraitCausesPageTurn),
  46. @"alert" : @(UIAccessibilityTraitNone),
  47. @"checkbox" : @(UIAccessibilityTraitNone),
  48. @"combobox" : @(UIAccessibilityTraitNone),
  49. @"menu" : @(UIAccessibilityTraitNone),
  50. @"menubar" : @(UIAccessibilityTraitNone),
  51. @"menuitem" : @(UIAccessibilityTraitNone),
  52. @"progressbar" : @(UIAccessibilityTraitNone),
  53. @"radio" : @(UIAccessibilityTraitNone),
  54. @"radiogroup" : @(UIAccessibilityTraitNone),
  55. @"scrollbar" : @(UIAccessibilityTraitNone),
  56. @"spinbutton" : @(UIAccessibilityTraitNone),
  57. @"switch" : @(SwitchAccessibilityTrait),
  58. @"tab" : @(UIAccessibilityTraitNone),
  59. @"tablist" : @(UIAccessibilityTraitNone),
  60. @"timer" : @(UIAccessibilityTraitNone),
  61. @"toolbar" : @(UIAccessibilityTraitNone),
  62. }),
  63. UIAccessibilityTraitNone,
  64. unsignedLongLongValue)
  65. @end
  66. @implementation RCTViewManager
  67. @synthesize bridge = _bridge;
  68. RCT_EXPORT_MODULE()
  69. - (dispatch_queue_t)methodQueue
  70. {
  71. return RCTGetUIManagerQueue();
  72. }
  73. - (UIView *)view
  74. {
  75. #if TARGET_OS_TV
  76. return [RCTTVView new];
  77. #else
  78. return [RCTView new];
  79. #endif
  80. }
  81. - (RCTShadowView *)shadowView
  82. {
  83. return [RCTShadowView new];
  84. }
  85. - (NSArray<NSString *> *)customBubblingEventTypes
  86. {
  87. return @[
  88. // Generic events
  89. @"press",
  90. @"change",
  91. @"focus",
  92. @"blur",
  93. @"submitEditing",
  94. @"endEditing",
  95. @"keyPress",
  96. // Touch events
  97. @"touchStart",
  98. @"touchMove",
  99. @"touchCancel",
  100. @"touchEnd",
  101. ];
  102. }
  103. #pragma mark - View properties
  104. #if TARGET_OS_TV
  105. // TODO: Delete props for Apple TV.
  106. RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL)
  107. RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL)
  108. RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary)
  109. #endif
  110. // Accessibility related properties
  111. RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL)
  112. RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray)
  113. RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
  114. RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString)
  115. RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary)
  116. RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
  117. RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL)
  118. RCT_REMAP_VIEW_PROPERTY(
  119. accessibilityIgnoresInvertColors,
  120. reactAccessibilityElement.shouldAccessibilityIgnoresInvertColors,
  121. BOOL)
  122. RCT_REMAP_VIEW_PROPERTY(onAccessibilityAction, reactAccessibilityElement.onAccessibilityAction, RCTDirectEventBlock)
  123. RCT_REMAP_VIEW_PROPERTY(onAccessibilityTap, reactAccessibilityElement.onAccessibilityTap, RCTDirectEventBlock)
  124. RCT_REMAP_VIEW_PROPERTY(onMagicTap, reactAccessibilityElement.onMagicTap, RCTDirectEventBlock)
  125. RCT_REMAP_VIEW_PROPERTY(onAccessibilityEscape, reactAccessibilityElement.onAccessibilityEscape, RCTDirectEventBlock)
  126. RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString)
  127. RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
  128. RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
  129. RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
  130. RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
  131. RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize)
  132. RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float)
  133. RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
  134. RCT_REMAP_VIEW_PROPERTY(needsOffscreenAlphaCompositing, layer.allowsGroupOpacity, BOOL)
  135. RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView)
  136. {
  137. if (json) {
  138. view.clipsToBounds = [RCTConvert YGOverflow:json] != YGOverflowVisible;
  139. } else {
  140. view.clipsToBounds = defaultView.clipsToBounds;
  141. }
  142. }
  143. RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView)
  144. {
  145. view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize;
  146. view.layer.rasterizationScale =
  147. view.layer.shouldRasterize ? [UIScreen mainScreen].scale : defaultView.layer.rasterizationScale;
  148. }
  149. RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RCTView)
  150. {
  151. view.layer.transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform;
  152. // Enable edge antialiasing in perspective transforms
  153. view.layer.allowsEdgeAntialiasing = !(view.layer.transform.m34 == 0.0f);
  154. }
  155. RCT_CUSTOM_VIEW_PROPERTY(accessibilityRole, UIAccessibilityTraits, RCTView)
  156. {
  157. const UIAccessibilityTraits AccessibilityRolesMask = UIAccessibilityTraitNone | UIAccessibilityTraitButton |
  158. UIAccessibilityTraitLink | UIAccessibilityTraitSearchField | UIAccessibilityTraitImage |
  159. UIAccessibilityTraitKeyboardKey | UIAccessibilityTraitStaticText | UIAccessibilityTraitAdjustable |
  160. UIAccessibilityTraitHeader | UIAccessibilityTraitSummaryElement | SwitchAccessibilityTrait;
  161. view.reactAccessibilityElement.accessibilityTraits =
  162. view.reactAccessibilityElement.accessibilityTraits & ~AccessibilityRolesMask;
  163. UIAccessibilityTraits newTraits = json ? [RCTConvert UIAccessibilityTraits:json] : defaultView.accessibilityTraits;
  164. if (newTraits != UIAccessibilityTraitNone) {
  165. UIAccessibilityTraits maskedTraits = newTraits & AccessibilityRolesMask;
  166. view.reactAccessibilityElement.accessibilityTraits |= maskedTraits;
  167. } else {
  168. NSString *role = json ? [RCTConvert NSString:json] : @"";
  169. view.reactAccessibilityElement.accessibilityRole = role;
  170. }
  171. }
  172. RCT_CUSTOM_VIEW_PROPERTY(accessibilityState, NSDictionary, RCTView)
  173. {
  174. NSDictionary<NSString *, id> *state = json ? [RCTConvert NSDictionary:json] : nil;
  175. NSMutableDictionary<NSString *, id> *newState = [[NSMutableDictionary<NSString *, id> alloc] init];
  176. if (!state) {
  177. return;
  178. }
  179. const UIAccessibilityTraits AccessibilityStatesMask = UIAccessibilityTraitNotEnabled | UIAccessibilityTraitSelected;
  180. view.reactAccessibilityElement.accessibilityTraits =
  181. view.reactAccessibilityElement.accessibilityTraits & ~AccessibilityStatesMask;
  182. for (NSString *s in state) {
  183. id val = [state objectForKey:s];
  184. if (!val) {
  185. continue;
  186. }
  187. if ([s isEqualToString:@"selected"] && [val isKindOfClass:[NSNumber class]] && [val boolValue]) {
  188. view.reactAccessibilityElement.accessibilityTraits |= UIAccessibilityTraitSelected;
  189. } else if ([s isEqualToString:@"disabled"] && [val isKindOfClass:[NSNumber class]] && [val boolValue]) {
  190. view.reactAccessibilityElement.accessibilityTraits |= UIAccessibilityTraitNotEnabled;
  191. } else {
  192. newState[s] = val;
  193. }
  194. }
  195. if (newState.count > 0) {
  196. view.reactAccessibilityElement.accessibilityState = newState;
  197. // Post a layout change notification to make sure VoiceOver get notified for the state
  198. // changes that don't happen upon users' click.
  199. UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
  200. } else {
  201. view.reactAccessibilityElement.accessibilityState = nil;
  202. }
  203. }
  204. RCT_CUSTOM_VIEW_PROPERTY(nativeID, NSString *, RCTView)
  205. {
  206. view.nativeID = json ? [RCTConvert NSString:json] : defaultView.nativeID;
  207. [_bridge.uiManager setNativeID:view.nativeID forView:view];
  208. }
  209. RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView)
  210. {
  211. if ([view respondsToSelector:@selector(setPointerEvents:)]) {
  212. view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents;
  213. return;
  214. }
  215. if (!json) {
  216. view.userInteractionEnabled = defaultView.userInteractionEnabled;
  217. return;
  218. }
  219. switch ([RCTConvert RCTPointerEvents:json]) {
  220. case RCTPointerEventsUnspecified:
  221. // Pointer events "unspecified" acts as if a stylesheet had not specified,
  222. // which is different than "auto" in CSS (which cannot and will not be
  223. // supported in `React`. "auto" may override a parent's "none".
  224. // Unspecified values do not.
  225. // This wouldn't override a container view's `userInteractionEnabled = NO`
  226. view.userInteractionEnabled = YES;
  227. case RCTPointerEventsNone:
  228. view.userInteractionEnabled = NO;
  229. break;
  230. default:
  231. RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
  232. }
  233. }
  234. RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
  235. {
  236. if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
  237. view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
  238. }
  239. }
  240. RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView)
  241. {
  242. if ([view respondsToSelector:@selector(setBorderRadius:)]) {
  243. view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius;
  244. } else {
  245. view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius;
  246. }
  247. }
  248. RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
  249. {
  250. if ([view respondsToSelector:@selector(setBorderColor:)]) {
  251. view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor;
  252. } else {
  253. view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor;
  254. }
  255. }
  256. RCT_CUSTOM_VIEW_PROPERTY(borderWidth, float, RCTView)
  257. {
  258. if ([view respondsToSelector:@selector(setBorderWidth:)]) {
  259. view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth;
  260. } else {
  261. view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth;
  262. }
  263. }
  264. RCT_CUSTOM_VIEW_PROPERTY(borderStyle, RCTBorderStyle, RCTView)
  265. {
  266. if ([view respondsToSelector:@selector(setBorderStyle:)]) {
  267. view.borderStyle = json ? [RCTConvert RCTBorderStyle:json] : defaultView.borderStyle;
  268. }
  269. }
  270. RCT_CUSTOM_VIEW_PROPERTY(hitSlop, UIEdgeInsets, RCTView)
  271. {
  272. if ([view respondsToSelector:@selector(setHitTestEdgeInsets:)]) {
  273. if (json) {
  274. UIEdgeInsets hitSlopInsets = [RCTConvert UIEdgeInsets:json];
  275. view.hitTestEdgeInsets =
  276. UIEdgeInsetsMake(-hitSlopInsets.top, -hitSlopInsets.left, -hitSlopInsets.bottom, -hitSlopInsets.right);
  277. } else {
  278. view.hitTestEdgeInsets = defaultView.hitTestEdgeInsets;
  279. }
  280. }
  281. }
  282. #define RCT_VIEW_BORDER_PROPERTY(SIDE) \
  283. RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, float, RCTView) \
  284. { \
  285. if ([view respondsToSelector:@selector(setBorder##SIDE##Width:)]) { \
  286. view.border##SIDE##Width = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Width; \
  287. } \
  288. } \
  289. RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Color, UIColor, RCTView) \
  290. { \
  291. if ([view respondsToSelector:@selector(setBorder##SIDE##Color:)]) { \
  292. view.border##SIDE##Color = json ? [RCTConvert CGColor:json] : defaultView.border##SIDE##Color; \
  293. } \
  294. }
  295. RCT_VIEW_BORDER_PROPERTY(Top)
  296. RCT_VIEW_BORDER_PROPERTY(Right)
  297. RCT_VIEW_BORDER_PROPERTY(Bottom)
  298. RCT_VIEW_BORDER_PROPERTY(Left)
  299. RCT_VIEW_BORDER_PROPERTY(Start)
  300. RCT_VIEW_BORDER_PROPERTY(End)
  301. #define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
  302. RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView) \
  303. { \
  304. if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \
  305. view.border##SIDE##Radius = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Radius; \
  306. } \
  307. }
  308. RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
  309. RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
  310. RCT_VIEW_BORDER_RADIUS_PROPERTY(TopStart)
  311. RCT_VIEW_BORDER_RADIUS_PROPERTY(TopEnd)
  312. RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
  313. RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
  314. RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomStart)
  315. RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomEnd)
  316. RCT_REMAP_VIEW_PROPERTY(display, reactDisplay, YGDisplay)
  317. RCT_REMAP_VIEW_PROPERTY(zIndex, reactZIndex, NSInteger)
  318. #pragma mark - ShadowView properties
  319. RCT_EXPORT_SHADOW_PROPERTY(top, YGValue)
  320. RCT_EXPORT_SHADOW_PROPERTY(right, YGValue)
  321. RCT_EXPORT_SHADOW_PROPERTY(start, YGValue)
  322. RCT_EXPORT_SHADOW_PROPERTY(end, YGValue)
  323. RCT_EXPORT_SHADOW_PROPERTY(bottom, YGValue)
  324. RCT_EXPORT_SHADOW_PROPERTY(left, YGValue)
  325. RCT_EXPORT_SHADOW_PROPERTY(width, YGValue)
  326. RCT_EXPORT_SHADOW_PROPERTY(height, YGValue)
  327. RCT_EXPORT_SHADOW_PROPERTY(minWidth, YGValue)
  328. RCT_EXPORT_SHADOW_PROPERTY(maxWidth, YGValue)
  329. RCT_EXPORT_SHADOW_PROPERTY(minHeight, YGValue)
  330. RCT_EXPORT_SHADOW_PROPERTY(maxHeight, YGValue)
  331. RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, float)
  332. RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, float)
  333. RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, float)
  334. RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, float)
  335. RCT_EXPORT_SHADOW_PROPERTY(borderStartWidth, float)
  336. RCT_EXPORT_SHADOW_PROPERTY(borderEndWidth, float)
  337. RCT_EXPORT_SHADOW_PROPERTY(borderWidth, float)
  338. RCT_EXPORT_SHADOW_PROPERTY(marginTop, YGValue)
  339. RCT_EXPORT_SHADOW_PROPERTY(marginRight, YGValue)
  340. RCT_EXPORT_SHADOW_PROPERTY(marginBottom, YGValue)
  341. RCT_EXPORT_SHADOW_PROPERTY(marginLeft, YGValue)
  342. RCT_EXPORT_SHADOW_PROPERTY(marginStart, YGValue)
  343. RCT_EXPORT_SHADOW_PROPERTY(marginEnd, YGValue)
  344. RCT_EXPORT_SHADOW_PROPERTY(marginVertical, YGValue)
  345. RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, YGValue)
  346. RCT_EXPORT_SHADOW_PROPERTY(margin, YGValue)
  347. RCT_EXPORT_SHADOW_PROPERTY(paddingTop, YGValue)
  348. RCT_EXPORT_SHADOW_PROPERTY(paddingRight, YGValue)
  349. RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, YGValue)
  350. RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, YGValue)
  351. RCT_EXPORT_SHADOW_PROPERTY(paddingStart, YGValue)
  352. RCT_EXPORT_SHADOW_PROPERTY(paddingEnd, YGValue)
  353. RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, YGValue)
  354. RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, YGValue)
  355. RCT_EXPORT_SHADOW_PROPERTY(padding, YGValue)
  356. RCT_EXPORT_SHADOW_PROPERTY(flex, float)
  357. RCT_EXPORT_SHADOW_PROPERTY(flexGrow, float)
  358. RCT_EXPORT_SHADOW_PROPERTY(flexShrink, float)
  359. RCT_EXPORT_SHADOW_PROPERTY(flexBasis, YGValue)
  360. RCT_EXPORT_SHADOW_PROPERTY(flexDirection, YGFlexDirection)
  361. RCT_EXPORT_SHADOW_PROPERTY(flexWrap, YGWrap)
  362. RCT_EXPORT_SHADOW_PROPERTY(justifyContent, YGJustify)
  363. RCT_EXPORT_SHADOW_PROPERTY(alignItems, YGAlign)
  364. RCT_EXPORT_SHADOW_PROPERTY(alignSelf, YGAlign)
  365. RCT_EXPORT_SHADOW_PROPERTY(alignContent, YGAlign)
  366. RCT_EXPORT_SHADOW_PROPERTY(position, YGPositionType)
  367. RCT_EXPORT_SHADOW_PROPERTY(aspectRatio, float)
  368. RCT_EXPORT_SHADOW_PROPERTY(overflow, YGOverflow)
  369. RCT_EXPORT_SHADOW_PROPERTY(display, YGDisplay)
  370. RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock)
  371. RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection)
  372. @end