not-react-native-macos 0.87.1-rc.4 → 0.87.1-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -132,7 +132,40 @@ type FocusEventProps = Readonly<{
132
132
  // a .macos file because ViewPropTypes is shared, and a prop that is simply
133
133
  // absent on other platforms costs them nothing. The native side is
134
134
  // HostPlatformViewProps under components/view/platform/macos. macOS]
135
+ /**
136
+ * A key the view means to act on itself. A modifier left out is a wildcard:
137
+ * `{key: 'c'}` matches Cmd-C and a bare c alike.
138
+ *
139
+ * @platform macos
140
+ */
141
+ export type HandledKeyEvent = Readonly<{
142
+ key: string,
143
+ altKey?: ?boolean,
144
+ ctrlKey?: ?boolean,
145
+ metaKey?: ?boolean,
146
+ shiftKey?: ?boolean,
147
+ }>;
148
+
135
149
  type MacOSViewProps = Readonly<{
150
+ /**
151
+ * Keys this view handles itself, suppressing AppKit's own interpretation of
152
+ * them -- Tab moving focus, Escape cancelling, an unclaimed key beeping.
153
+ *
154
+ * Separate from `onKeyDown`, deliberately: listening to a key does not change
155
+ * what it does, so a view that observes Tab still lets Tab move focus. Only
156
+ * listing a key here claims it.
157
+ *
158
+ * @platform macos
159
+ */
160
+ keyDownEvents?: ?$ReadOnlyArray<HandledKeyEvent>,
161
+
162
+ /**
163
+ * As `keyDownEvents`, for key release.
164
+ *
165
+ * @platform macos
166
+ */
167
+ keyUpEvents?: ?$ReadOnlyArray<HandledKeyEvent>,
168
+
136
169
  /**
137
170
  * The view's help tag, shown when the pointer rests over it.
138
171
  *
@@ -51,6 +51,8 @@ const validAttributesForNonEventProps = {
51
51
  allowsVibrancy: true,
52
52
  enableFocusRing: true,
53
53
  focusable: true,
54
+ keyDownEvents: true,
55
+ keyUpEvents: true,
54
56
  mouseDownCanMoveWindow: true,
55
57
  tooltip: true,
56
58
  };
@@ -351,6 +351,17 @@ export type KeyEvent = Readonly<{
351
351
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent/isComposing
352
352
  */
353
353
  isComposing?: boolean,
354
+ // [macOS] Modifiers AppKit reports and no other platform has. Optional, so
355
+ // shared code that never reads them is unaffected.
356
+ /** @platform macos */
357
+ capsLockKey?: boolean,
358
+ /** @platform macos */
359
+ numericPadKey?: boolean,
360
+ /** @platform macos */
361
+ helpKey?: boolean,
362
+ /** @platform macos */
363
+ functionKey?: boolean,
364
+ // macOS]
354
365
  }>;
355
366
 
356
367
  export type KeyUpEvent = NativeSyntheticEvent<KeyEvent>;
@@ -10,6 +10,7 @@
10
10
 
11
11
  #import <CoreGraphics/CoreGraphics.h>
12
12
  #import <QuartzCore/QuartzCore.h>
13
+ #import <algorithm> // [macOS] std::find, for matching a press against keyDownEvents
13
14
  #import <objc/runtime.h>
14
15
  #import <ranges>
15
16
 
@@ -740,6 +741,133 @@ static BOOL RCTLayerTransformCollapsesAxis(CALayer *layer)
740
741
  }
741
742
  }
742
743
 
744
+ #pragma mark - Keyboard Events
745
+
746
+ /**
747
+ * The W3C `key` name for a press, per https://www.w3.org/TR/uievents-key/.
748
+ *
749
+ * `charactersIgnoringModifiers` already gives the right answer for anything
750
+ * printable. The cases below are the ones where it gives a private-use unichar
751
+ * (the arrows, the function keys) or a control character (Return, Delete)
752
+ * instead of a name. Tab and Escape are matched on keyCode rather than
753
+ * character because AppKit reports them as \t and \e, which would otherwise
754
+ * arrive as those literal characters.
755
+ *
756
+ * Naming follows the cross-platform reconciliation react-native-windows and
757
+ * react-native-macos both use, so a key handler is portable between them.
758
+ */
759
+ static NSString *RCTKeyFromNSEvent(NSEvent *event)
760
+ {
761
+ NSString *characters = event.charactersIgnoringModifiers;
762
+ unichar code = characters.length > 0 ? [characters characterAtIndex:0] : 0;
763
+
764
+ switch (event.keyCode) {
765
+ case 48:
766
+ return @"Tab";
767
+ case 53:
768
+ return @"Escape";
769
+ default:
770
+ break;
771
+ }
772
+
773
+ switch (code) {
774
+ case NSEnterCharacter:
775
+ case NSNewlineCharacter:
776
+ case NSCarriageReturnCharacter:
777
+ return @"Enter";
778
+ case NSLeftArrowFunctionKey:
779
+ return @"ArrowLeft";
780
+ case NSRightArrowFunctionKey:
781
+ return @"ArrowRight";
782
+ case NSUpArrowFunctionKey:
783
+ return @"ArrowUp";
784
+ case NSDownArrowFunctionKey:
785
+ return @"ArrowDown";
786
+ case NSBackspaceCharacter:
787
+ case NSDeleteCharacter:
788
+ return @"Backspace";
789
+ case NSDeleteFunctionKey:
790
+ return @"Delete";
791
+ case NSHomeFunctionKey:
792
+ return @"Home";
793
+ case NSEndFunctionKey:
794
+ return @"End";
795
+ case NSPageUpFunctionKey:
796
+ return @"PageUp";
797
+ case NSPageDownFunctionKey:
798
+ return @"PageDown";
799
+ default:
800
+ break;
801
+ }
802
+
803
+ if (code >= NSF1FunctionKey && code <= NSF12FunctionKey) {
804
+ return [NSString stringWithFormat:@"F%u", (unsigned)(code - NSF1FunctionKey + 1)];
805
+ }
806
+
807
+ return characters;
808
+ }
809
+
810
+ /**
811
+ * Emits the press and reports whether the view claimed it.
812
+ *
813
+ * Claiming matters because AppKit interprets an unclaimed key itself once the
814
+ * responder chain is done with it: Tab moves focus, Escape cancels, anything
815
+ * else beeps. A view says which keys it means to act on through `keyDownEvents`
816
+ * / `keyUpEvents`, and only those suppress the default behaviour. Listening via
817
+ * `onKeyDown` alone deliberately does not, so observing a key does not change
818
+ * what it does.
819
+ */
820
+ - (BOOL)_handleKeyboardEvent:(NSEvent *)event
821
+ {
822
+ NSEventModifierFlags flags = event.modifierFlags;
823
+ KeyEvent keyEvent = {
824
+ .key = RCTStringFromNSString(RCTKeyFromNSEvent(event)),
825
+ .altKey = static_cast<bool>(flags & NSEventModifierFlagOption),
826
+ .ctrlKey = static_cast<bool>(flags & NSEventModifierFlagControl),
827
+ .shiftKey = static_cast<bool>(flags & NSEventModifierFlagShift),
828
+ .metaKey = static_cast<bool>(flags & NSEventModifierFlagCommand),
829
+ .capsLockKey = static_cast<bool>(flags & NSEventModifierFlagCapsLock),
830
+ .numericPadKey = static_cast<bool>(flags & NSEventModifierFlagNumericPad),
831
+ .helpKey = static_cast<bool>(flags & NSEventModifierFlagHelp),
832
+ .functionKey = static_cast<bool>(flags & NSEventModifierFlagFunction),
833
+ };
834
+
835
+ BOOL isKeyDown = event.type == NSEventTypeKeyDown;
836
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
837
+
838
+ // Calling super walks the responder chain, which is the view hierarchy, so
839
+ // every ancestor view would emit the same press. Fabric bubbles the event
840
+ // through the shadow tree on its own, so only the innermost view should emit.
841
+ // The flag rides on the NSEvent because that is the one object the whole
842
+ // chain shares.
843
+ static const char kEmittedKey = 0;
844
+ if (_eventEmitter != nullptr && !((NSNumber *)objc_getAssociatedObject(event, &kEmittedKey)).boolValue) {
845
+ if (isKeyDown) {
846
+ _eventEmitter->onKeyDown(keyEvent);
847
+ } else {
848
+ _eventEmitter->onKeyUp(keyEvent);
849
+ }
850
+ objc_setAssociatedObject(event, &kEmittedKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
851
+ }
852
+
853
+ const auto &handled = isKeyDown ? viewProps.keyDownEvents : viewProps.keyUpEvents;
854
+ return std::find(handled.cbegin(), handled.cend(), keyEvent) != handled.cend();
855
+ }
856
+
857
+ - (void)keyDown:(NSEvent *)event
858
+ {
859
+ if (![self _handleKeyboardEvent:event]) {
860
+ [super keyDown:event];
861
+ }
862
+ }
863
+
864
+ - (void)keyUp:(NSEvent *)event
865
+ {
866
+ if (![self _handleKeyboardEvent:event]) {
867
+ [super keyUp:event];
868
+ }
869
+ }
870
+
743
871
  - (BOOL)allowsVibrancy
744
872
  {
745
873
  return _allowsVibrancy;
@@ -1922,7 +2050,16 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
1922
2050
 
1923
2051
  - (void)focus
1924
2052
  {
2053
+ #if TARGET_OS_OSX // [macOS] On AppKit -becomeFirstResponder is the window
2054
+ // notifying the view that it happened, not the view asking. Sending it
2055
+ // directly changes nothing: -makeFirstResponder: is the request. Without
2056
+ // this, `focusable` views never receive key events, because nothing ever
2057
+ // makes them first responder -- AppKit moves focus on Tab only, and only
2058
+ // when Full Keyboard Access is on.
2059
+ [self.window makeFirstResponder:self];
2060
+ #else // [macOS]
1925
2061
  [self becomeFirstResponder];
2062
+ #endif // [macOS]
1926
2063
 
1927
2064
  #if TARGET_OS_TV
1928
2065
  RCTSurfaceHostingProxyRootView *rootView = [self containingRootView];
@@ -1938,7 +2075,14 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
1938
2075
 
1939
2076
  - (void)blur
1940
2077
  {
2078
+ #if TARGET_OS_OSX // [macOS] Symmetrically: resigning is granted by the window,
2079
+ // and only if this view still holds the focus.
2080
+ if (self.window.firstResponder == self) {
2081
+ [self.window makeFirstResponder:nil];
2082
+ }
2083
+ #else // [macOS]
1941
2084
  [self resignFirstResponder];
2085
+ #endif // [macOS]
1942
2086
  }
1943
2087
 
1944
2088
  - (BOOL)becomeFirstResponder
@@ -20,6 +20,30 @@ static jsi::Value mouseEventPayload(jsi::Runtime &runtime, const HostPlatformVie
20
20
  payload.setProperty(runtime, "screenY", event.screenY);
21
21
  payload.setProperty(runtime, "pageX", event.pageX);
22
22
  payload.setProperty(runtime, "pageY", event.pageY);
23
+ payload.setProperty(runtime, "altKey", event.altKey);
24
+ payload.setProperty(runtime, "ctrlKey", event.ctrlKey);
25
+ payload.setProperty(runtime, "shiftKey", event.shiftKey);
26
+ payload.setProperty(runtime, "metaKey", event.metaKey);
27
+ payload.setProperty(runtime, "button", event.button);
28
+ // Pressability's click guard reads this to tell a real mouse click apart from
29
+ // one synthesised by the responder system, which would otherwise fire onPress
30
+ // twice.
31
+ payload.setProperty(runtime, "pointerType", "mouse");
32
+ return payload;
33
+ }
34
+
35
+ static jsi::Value keyEventPayload(jsi::Runtime &runtime, const KeyEvent &event)
36
+ {
37
+ auto payload = jsi::Object(runtime);
38
+ payload.setProperty(runtime, "key", jsi::String::createFromUtf8(runtime, event.key));
39
+ payload.setProperty(runtime, "altKey", event.altKey);
40
+ payload.setProperty(runtime, "ctrlKey", event.ctrlKey);
41
+ payload.setProperty(runtime, "shiftKey", event.shiftKey);
42
+ payload.setProperty(runtime, "metaKey", event.metaKey);
43
+ payload.setProperty(runtime, "capsLockKey", event.capsLockKey);
44
+ payload.setProperty(runtime, "numericPadKey", event.numericPadKey);
45
+ payload.setProperty(runtime, "helpKey", event.helpKey);
46
+ payload.setProperty(runtime, "functionKey", event.functionKey);
23
47
  return payload;
24
48
  }
25
49
 
@@ -36,4 +60,15 @@ RCT_MACOS_MOUSE_EVENT(onMouseLeave)
36
60
  RCT_MACOS_MOUSE_EVENT(onDoubleClick)
37
61
  RCT_MACOS_MOUSE_EVENT(onAuxClick)
38
62
 
63
+ #define RCT_MACOS_KEY_EVENT(name) \
64
+ void HostPlatformViewEventEmitter::name(const KeyEvent &event) const \
65
+ { \
66
+ dispatchEvent(#name, [event](jsi::Runtime &runtime) { \
67
+ return keyEventPayload(runtime, event); \
68
+ }); \
69
+ }
70
+
71
+ RCT_MACOS_KEY_EVENT(onKeyDown)
72
+ RCT_MACOS_KEY_EVENT(onKeyUp)
73
+
39
74
  } // namespace facebook::react
@@ -5,12 +5,14 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- // [macOS] Adds the pointer events AppKit can report and UIKit cannot.
8
+ // [macOS] Adds the pointer and key events AppKit can report and UIKit cannot.
9
9
 
10
10
  #pragma once
11
11
 
12
12
  #include <react/renderer/components/view/BaseViewEventEmitter.h>
13
13
 
14
+ #include "KeyEvent.h"
15
+
14
16
  namespace facebook::react {
15
17
 
16
18
  class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
@@ -28,12 +30,23 @@ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
28
30
  Float screenY{};
29
31
  Float pageX{};
30
32
  Float pageY{};
33
+
34
+ bool altKey{false};
35
+ bool ctrlKey{false};
36
+ bool shiftKey{false};
37
+ bool metaKey{false};
38
+
39
+ /** DOM button numbering: 0 left, 1 middle, 2 right. */
40
+ int button{0};
31
41
  };
32
42
 
33
43
  void onMouseEnter(const MouseEvent &event) const;
34
44
  void onMouseLeave(const MouseEvent &event) const;
35
45
  void onDoubleClick(const MouseEvent &event) const;
36
46
  void onAuxClick(const MouseEvent &event) const;
47
+
48
+ void onKeyDown(const KeyEvent &event) const;
49
+ void onKeyUp(const KeyEvent &event) const;
37
50
  };
38
51
 
39
52
  } // namespace facebook::react
@@ -27,6 +27,8 @@ struct HostPlatformViewEvents {
27
27
  MouseLeave = 1,
28
28
  DoubleClick = 2,
29
29
  AuxClick = 3,
30
+ KeyDown = 4,
31
+ KeyUp = 5,
30
32
  };
31
33
 
32
34
  constexpr bool operator[](Offset offset) const
@@ -33,6 +33,8 @@ static inline HostPlatformViewEvents convertRawProp(
33
33
  RCT_MACOS_CONVERT_EVENT("onMouseLeave", MouseLeave);
34
34
  RCT_MACOS_CONVERT_EVENT("onDoubleClick", DoubleClick);
35
35
  RCT_MACOS_CONVERT_EVENT("onAuxClick", AuxClick);
36
+ RCT_MACOS_CONVERT_EVENT("onKeyDown", KeyDown);
37
+ RCT_MACOS_CONVERT_EVENT("onKeyUp", KeyUp);
36
38
 
37
39
  #undef RCT_MACOS_CONVERT_EVENT
38
40
 
@@ -59,6 +61,8 @@ HostPlatformViewProps::HostPlatformViewProps(
59
61
  : convertRawProp(context, rawProps, sourceProps.hostPlatformEvents, {})),
60
62
  RCT_MACOS_PROP(focusable),
61
63
  RCT_MACOS_PROP(enableFocusRing),
64
+ RCT_MACOS_PROP(keyDownEvents),
65
+ RCT_MACOS_PROP(keyUpEvents),
62
66
  RCT_MACOS_PROP(tooltip),
63
67
  RCT_MACOS_PROP(acceptsFirstMouse),
64
68
  RCT_MACOS_PROP(allowsVibrancy),
@@ -99,8 +103,12 @@ void HostPlatformViewProps::setProp(
99
103
  RCT_MACOS_EVENT_CASE(MouseLeave);
100
104
  RCT_MACOS_EVENT_CASE(DoubleClick);
101
105
  RCT_MACOS_EVENT_CASE(AuxClick);
106
+ RCT_MACOS_EVENT_CASE(KeyDown);
107
+ RCT_MACOS_EVENT_CASE(KeyUp);
102
108
  RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
103
109
  RAW_SET_PROP_SWITCH_CASE_BASIC(enableFocusRing);
110
+ RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
111
+ RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);
104
112
  RAW_SET_PROP_SWITCH_CASE_BASIC(tooltip);
105
113
  RAW_SET_PROP_SWITCH_CASE_BASIC(acceptsFirstMouse);
106
114
  RAW_SET_PROP_SWITCH_CASE_BASIC(allowsVibrancy);
@@ -20,8 +20,10 @@
20
20
 
21
21
  #include <optional>
22
22
  #include <string>
23
+ #include <vector>
23
24
 
24
25
  #include "HostPlatformViewEvents.h"
26
+ #include "KeyEvent.h"
25
27
 
26
28
  namespace facebook::react {
27
29
 
@@ -47,6 +49,14 @@ class HostPlatformViewProps : public BaseViewProps {
47
49
  /** Draws the focus ring while first responder. On by default, as in AppKit. */
48
50
  bool enableFocusRing{true};
49
51
 
52
+ /**
53
+ * Keys the view handles itself, rather than letting AppKit interpret them.
54
+ * See HandledKey: a press not declared here still reaches onKeyDown, but
55
+ * AppKit's own handling runs afterwards.
56
+ */
57
+ std::vector<HandledKey> keyDownEvents{};
58
+ std::vector<HandledKey> keyUpEvents{};
59
+
50
60
  /** The view's help tag. std::nullopt leaves any inherited tooltip alone. */
51
61
  std::optional<std::string> tooltip{};
52
62
 
@@ -26,7 +26,8 @@ inline bool formsView(const ViewProps &props)
26
26
  // flattened away: a focusable view has to join the key view loop, a tooltip
27
27
  // has to attach to something, and mouse tracking needs an NSTrackingArea
28
28
  // owner. Flattening is otherwise invisible -- the handler simply never fires.
29
- return props.focusable || props.tooltip.has_value() || props.hostPlatformEvents.bits.any();
29
+ return props.focusable || props.tooltip.has_value() || props.hostPlatformEvents.bits.any() ||
30
+ !props.keyDownEvents.empty() || !props.keyUpEvents.empty();
30
31
  }
31
32
 
32
33
  inline bool isKeyboardFocusable(const ViewProps &props)
@@ -0,0 +1,100 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and 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
+
8
+ // [macOS] Key input, which only a desktop platform has to model.
9
+ //
10
+ // Two shapes, and the distinction matters. `KeyEvent` is what a view reports
11
+ // happened. `HandledKey` is what a view declares in advance that it wants,
12
+ // which is a filter, not an event: AppKit gives a key to the responder chain
13
+ // and then to its own interpretation -- Tab moves focus, Escape cancels, an
14
+ // unclaimed key beeps -- so a view that means to act on a key has to say so
15
+ // before the press, or the default behaviour happens as well.
16
+ //
17
+ // Names follow https://www.w3.org/TR/uievents-key/ rather than AppKit's
18
+ // keyCodes, so `keyDownEvents={[{key: 'Enter'}]}` is the same declaration it
19
+ // would be on the web, and matches microsoft/react-native-macos.
20
+
21
+ #pragma once
22
+
23
+ #include <react/renderer/core/PropsParserContext.h>
24
+ #include <react/renderer/core/propsConversions.h>
25
+
26
+ #include <optional>
27
+ #include <string>
28
+ #include <unordered_map>
29
+
30
+ namespace facebook::react {
31
+
32
+ /**
33
+ * A key the view intends to handle itself.
34
+ *
35
+ * A modifier left unset means "don't care": `{key: 'c'}` matches Cmd-C and a
36
+ * bare c alike, while `{key: 'c', metaKey: true}` matches only the former.
37
+ */
38
+ struct HandledKey {
39
+ std::string key{};
40
+ std::optional<bool> altKey{};
41
+ std::optional<bool> ctrlKey{};
42
+ std::optional<bool> shiftKey{};
43
+ std::optional<bool> metaKey{};
44
+ };
45
+
46
+ inline bool operator==(const HandledKey &lhs, const HandledKey &rhs)
47
+ {
48
+ return lhs.key == rhs.key && lhs.altKey == rhs.altKey && lhs.ctrlKey == rhs.ctrlKey &&
49
+ lhs.shiftKey == rhs.shiftKey && lhs.metaKey == rhs.metaKey;
50
+ }
51
+
52
+ /** A key press, as reported to `onKeyDown` / `onKeyUp`. */
53
+ struct KeyEvent {
54
+ std::string key{};
55
+ bool altKey{false};
56
+ bool ctrlKey{false};
57
+ bool shiftKey{false};
58
+ bool metaKey{false};
59
+ bool capsLockKey{false};
60
+ bool numericPadKey{false};
61
+ bool helpKey{false};
62
+ bool functionKey{false};
63
+ };
64
+
65
+ /** Whether an actual press satisfies a declaration; unset modifiers match anything. */
66
+ inline bool operator==(const KeyEvent &lhs, const HandledKey &rhs)
67
+ {
68
+ return lhs.key == rhs.key && (!rhs.altKey.has_value() || lhs.altKey == *rhs.altKey) &&
69
+ (!rhs.ctrlKey.has_value() || lhs.ctrlKey == *rhs.ctrlKey) &&
70
+ (!rhs.shiftKey.has_value() || lhs.shiftKey == *rhs.shiftKey) &&
71
+ (!rhs.metaKey.has_value() || lhs.metaKey == *rhs.metaKey);
72
+ }
73
+
74
+ /**
75
+ * Accepts either the object form or a bare string, so `keyDownEvents={['Enter']}`
76
+ * works as shorthand for the common no-modifier case.
77
+ */
78
+ inline void fromRawValue(const PropsParserContext &context, const RawValue &value, HandledKey &result)
79
+ {
80
+ if (value.hasType<std::unordered_map<std::string, RawValue>>()) {
81
+ auto map = static_cast<std::unordered_map<std::string, RawValue>>(value);
82
+ for (const auto &pair : map) {
83
+ if (pair.first == "key") {
84
+ result.key = static_cast<std::string>(pair.second);
85
+ } else if (pair.first == "altKey") {
86
+ result.altKey = static_cast<bool>(pair.second);
87
+ } else if (pair.first == "ctrlKey") {
88
+ result.ctrlKey = static_cast<bool>(pair.second);
89
+ } else if (pair.first == "shiftKey") {
90
+ result.shiftKey = static_cast<bool>(pair.second);
91
+ } else if (pair.first == "metaKey") {
92
+ result.metaKey = static_cast<bool>(pair.second);
93
+ }
94
+ }
95
+ } else if (value.hasType<std::string>()) {
96
+ result.key = static_cast<std::string>(value);
97
+ }
98
+ }
99
+
100
+ } // namespace facebook::react
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "not-react-native-macos",
3
- "version": "0.87.1-rc.4",
3
+ "version": "0.87.1-rc.5",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {