Janic Duplessis 4 lat temu
rodzic
commit
6c9d3bde46
33 zmienionych plików z 753 dodań i 657 usunięć
  1. 4
    0
      .gitignore
  2. 6
    6
      android/src/main/java/com/th3rdwave/safeareacontext/EdgeInsets.java
  3. 6
    4
      android/src/main/java/com/th3rdwave/safeareacontext/InsetsChangeEvent.java
  4. 22
    0
      android/src/main/java/com/th3rdwave/safeareacontext/Rect.java
  5. 18
    34
      android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaUtils.java
  6. 14
    9
      android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaView.java
  7. 16
    6
      android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewManager.java
  8. 52
    0
      android/src/main/java/com/th3rdwave/safeareacontext/SerializationUtils.java
  9. 30
    0
      android/src/main/java/com/th3rdwave/safeareacontext/WindowChangeEvent.java
  10. 68
    31
      example/App.tsx
  11. 23
    0
      example/android/app/src/main/java/com/safeareaviewexample/MainActivity.java
  12. 7
    0
      example/android/app/src/main/res/values-v28/styles.xml
  13. 1
    1
      example/app.json
  14. 5
    0
      example/index.expo.js
  15. 2
    0
      example/index.js
  16. 15
    2
      ios/SafeAreaView/RNCSafeAreaView.m
  17. 13
    5
      ios/SafeAreaView/RNCSafeAreaViewManager.m
  18. 1
    0
      package.json
  19. 0
    4
      src/InitialWindowSafeAreaInsets.web.ts
  20. 15
    1
      src/NativeSafeAreaView.web.tsx
  21. 13
    1
      src/SafeArea.types.ts
  22. 85
    0
      src/SafeAreaContext.tsx
  23. 25
    0
      src/SafeAreaView.tsx
  24. 121
    0
      src/__tests__/SafeAreaContext-test.tsx
  25. 35
    0
      src/__tests__/SafeAreaView-test.tsx
  26. 32
    72
      src/__tests__/__snapshots__/SafeAreaContext-test.tsx.snap
  27. 57
    0
      src/__tests__/__snapshots__/SafeAreaView-test.tsx.snap
  28. 0
    169
      src/__tests__/index-test.tsx
  29. 41
    0
      src/__tests__/initialWindowMetrics-test.tsx
  30. 4
    80
      src/index.tsx
  31. 4
    4
      src/initialWindowMetrics.ts
  32. 3
    0
      src/initialWindowMetrics.web.ts
  33. 15
    228
      yarn.lock

+ 4
- 0
.gitignore Wyświetl plik

@@ -60,3 +60,7 @@ index.ios.bundle
60 60
 
61 61
 # generated by bob
62 62
 lib/
63
+
64
+# Expo
65
+web-build
66
+.expo

+ 6
- 6
android/src/main/java/com/th3rdwave/safeareacontext/EdgeInsets.java Wyświetl plik

@@ -1,19 +1,19 @@
1 1
 package com.th3rdwave.safeareacontext;
2 2
 
3 3
 /* package */ class EdgeInsets {
4
-  public float top;
5
-  public float right;
6
-  public float bottom;
7
-  public float left;
4
+  float top;
5
+  float right;
6
+  float bottom;
7
+  float left;
8 8
 
9
-  public EdgeInsets(float top, float right, float bottom, float left) {
9
+  EdgeInsets(float top, float right, float bottom, float left) {
10 10
     this.top = top;
11 11
     this.right = right;
12 12
     this.bottom = bottom;
13 13
     this.left = left;
14 14
   }
15 15
 
16
-  public boolean equalsToEdgeInsets(EdgeInsets other) {
16
+  boolean equalsToEdgeInsets(EdgeInsets other) {
17 17
     if (this == other) {
18 18
       return true;
19 19
     }

+ 6
- 4
android/src/main/java/com/th3rdwave/safeareacontext/InsetsChangeEvent.java Wyświetl plik

@@ -2,19 +2,20 @@ package com.th3rdwave.safeareacontext;
2 2
 
3 3
 import com.facebook.react.bridge.Arguments;
4 4
 import com.facebook.react.bridge.WritableMap;
5
-import com.facebook.react.uimanager.PixelUtil;
6 5
 import com.facebook.react.uimanager.events.Event;
7 6
 import com.facebook.react.uimanager.events.RCTEventEmitter;
8 7
 
9 8
 /* package */  class InsetsChangeEvent extends Event<InsetsChangeEvent> {
10
-  public static final String EVENT_NAME = "topInsetsChange";
9
+  static final String EVENT_NAME = "topInsetsChange";
11 10
 
12 11
   private EdgeInsets mInsets;
12
+  private Rect mFrame;
13 13
 
14
-  protected InsetsChangeEvent(int viewTag, EdgeInsets insets) {
14
+  InsetsChangeEvent(int viewTag, EdgeInsets insets, Rect frame) {
15 15
     super(viewTag);
16 16
 
17 17
     mInsets = insets;
18
+    mFrame = frame;
18 19
   }
19 20
 
20 21
   @Override
@@ -25,7 +26,8 @@ import com.facebook.react.uimanager.events.RCTEventEmitter;
25 26
   @Override
26 27
   public void dispatch(RCTEventEmitter rctEventEmitter) {
27 28
     WritableMap event = Arguments.createMap();
28
-    event.putMap("insets", SafeAreaUtils.edgeInsetsToJsMap(mInsets));
29
+    event.putMap("insets", SerializationUtils.edgeInsetsToJsMap(mInsets));
30
+    event.putMap("frame", SerializationUtils.rectToJsMap(mFrame));
29 31
     rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event);
30 32
   }
31 33
 }

+ 22
- 0
android/src/main/java/com/th3rdwave/safeareacontext/Rect.java Wyświetl plik

@@ -0,0 +1,22 @@
1
+package com.th3rdwave.safeareacontext;
2
+
3
+/* package */ class Rect {
4
+  float x;
5
+  float y;
6
+  float width;
7
+  float height;
8
+
9
+  Rect(float x, float y, float width, float height) {
10
+    this.x = x;
11
+    this.y = y;
12
+    this.width = width;
13
+    this.height = height;
14
+  }
15
+
16
+  boolean equalsToRect(Rect other) {
17
+    if (this == other) {
18
+      return true;
19
+    }
20
+    return this.x == other.x && this.y == other.y && this.width == other.width && this.height == other.height;
21
+  }
22
+}

+ 18
- 34
android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaUtils.java Wyświetl plik

@@ -4,41 +4,18 @@ import android.graphics.Rect;
4 4
 import android.os.Build;
5 5
 import android.view.Surface;
6 6
 import android.view.View;
7
+import android.view.ViewGroup;
7 8
 import android.view.WindowInsets;
8 9
 import android.view.WindowManager;
9 10
 
10
-import com.facebook.react.bridge.Arguments;
11
-import com.facebook.react.bridge.WritableMap;
12
-import com.facebook.react.common.MapBuilder;
13
-import com.facebook.react.uimanager.PixelUtil;
14
-
15
-import java.util.Map;
16
-
17 11
 import androidx.annotation.Nullable;
18 12
 
19 13
 /* package */ class SafeAreaUtils {
20
-  static WritableMap edgeInsetsToJsMap(EdgeInsets insets) {
21
-    WritableMap insetsMap = Arguments.createMap();
22
-    insetsMap.putDouble("top", PixelUtil.toDIPFromPixel(insets.top));
23
-    insetsMap.putDouble("right", PixelUtil.toDIPFromPixel(insets.right));
24
-    insetsMap.putDouble("bottom", PixelUtil.toDIPFromPixel(insets.bottom));
25
-    insetsMap.putDouble("left", PixelUtil.toDIPFromPixel(insets.left));
26
-    return insetsMap;
27
-  }
28
-
29
-  static Map<String, Float> edgeInsetsToJavaMap(EdgeInsets insets) {
30
-    return MapBuilder.of(
31
-        "top",
32
-        PixelUtil.toDIPFromPixel(insets.top),
33
-        "right",
34
-        PixelUtil.toDIPFromPixel(insets.right),
35
-        "bottom",
36
-        PixelUtil.toDIPFromPixel(insets.bottom),
37
-        "left",
38
-        PixelUtil.toDIPFromPixel(insets.left));
39
-  }
40
-
41
-  static @Nullable EdgeInsets getSafeAreaInsets(WindowManager windowManager, View rootView) {
14
+  static @Nullable EdgeInsets getSafeAreaInsets(
15
+      WindowManager windowManager,
16
+      View rootView,
17
+      View view
18
+  ) {
42 19
     // Window insets are parts of the window that are covered by system views (status bar,
43 20
     // navigation bar, notches). There are no apis the get these values for android < M so we
44 21
     // do a best effort polyfill.
@@ -73,17 +50,24 @@ import androidx.annotation.Nullable;
73 50
           rotation == Surface.ROTATION_270 ? navbarHeight : 0);
74 51
     }
75 52
 
76
-    // Calculate the part of the root view that overlaps with window insets.
77
-    View contentView = rootView.findViewById(android.R.id.content);
53
+    // Calculate the part of the view that overlaps with window insets.
78 54
     float windowWidth = rootView.getWidth();
79 55
     float windowHeight = rootView.getHeight();
80 56
     Rect visibleRect = new Rect();
81
-    contentView.getGlobalVisibleRect(visibleRect);
57
+    view.getGlobalVisibleRect(visibleRect);
82 58
 
83 59
     windowInsets.top = Math.max(windowInsets.top - visibleRect.top, 0);
84 60
     windowInsets.left = Math.max(windowInsets.left - visibleRect.left, 0);
85
-    windowInsets.bottom = Math.max(visibleRect.top + contentView.getHeight() + windowInsets.bottom - windowHeight, 0);
86
-    windowInsets.right = Math.max(visibleRect.left + contentView.getWidth() + windowInsets.right - windowWidth, 0);
61
+    windowInsets.bottom = Math.max(visibleRect.top + view.getHeight() + windowInsets.bottom - windowHeight, 0);
62
+    windowInsets.right = Math.max(visibleRect.left + view.getWidth() + windowInsets.right - windowWidth, 0);
87 63
     return windowInsets;
88 64
   }
65
+
66
+  static com.th3rdwave.safeareacontext.Rect getFrame(ViewGroup rootView, View view) {
67
+    Rect offset = new Rect();
68
+    view.getDrawingRect(offset);
69
+    rootView.offsetDescendantRectToMyCoords(view, offset);
70
+
71
+    return new com.th3rdwave.safeareacontext.Rect(offset.left, offset.top, view.getWidth(), view.getHeight());
72
+  }
89 73
 }

+ 14
- 9
android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaView.java Wyświetl plik

@@ -1,12 +1,9 @@
1 1
 package com.th3rdwave.safeareacontext;
2 2
 
3
+import android.annotation.SuppressLint;
3 4
 import android.content.Context;
4
-import android.graphics.Rect;
5
-import android.os.Build;
6
-import android.view.Surface;
7
-import android.view.View;
5
+import android.view.ViewGroup;
8 6
 import android.view.ViewTreeObserver;
9
-import android.view.WindowInsets;
10 7
 import android.view.WindowManager;
11 8
 
12 9
 import com.facebook.infer.annotation.Assertions;
@@ -14,14 +11,16 @@ import com.facebook.react.views.view.ReactViewGroup;
14 11
 
15 12
 import androidx.annotation.Nullable;
16 13
 
14
+@SuppressLint("ViewConstructor")
17 15
 public class SafeAreaView extends ReactViewGroup implements ViewTreeObserver.OnGlobalLayoutListener {
18 16
   public interface OnInsetsChangeListener {
19
-    void onInsetsChange(SafeAreaView view, EdgeInsets insets);
17
+    void onInsetsChange(SafeAreaView view, EdgeInsets insets, Rect frame);
20 18
   }
21 19
 
22 20
   private @Nullable OnInsetsChangeListener mInsetsChangeListener;
23 21
   private final WindowManager mWindowManager;
24 22
   private @Nullable EdgeInsets mLastInsets;
23
+  private @Nullable Rect mLastFrame;
25 24
 
26 25
   public SafeAreaView(Context context, WindowManager windowManager) {
27 26
     super(context);
@@ -30,10 +29,16 @@ public class SafeAreaView extends ReactViewGroup implements ViewTreeObserver.OnG
30 29
   }
31 30
 
32 31
   private void maybeUpdateInsets() {
33
-    EdgeInsets edgeInsets = SafeAreaUtils.getSafeAreaInsets(mWindowManager, getRootView());
34
-    if (edgeInsets != null && (mLastInsets == null || !mLastInsets.equalsToEdgeInsets(edgeInsets))) {
35
-      Assertions.assertNotNull(mInsetsChangeListener).onInsetsChange(this, edgeInsets);
32
+    EdgeInsets edgeInsets = SafeAreaUtils.getSafeAreaInsets(mWindowManager, getRootView(), this);
33
+    Rect frame = SafeAreaUtils.getFrame((ViewGroup) getRootView(), this);
34
+    if (edgeInsets != null &&
35
+        (mLastInsets == null ||
36
+            mLastFrame == null ||
37
+            !mLastInsets.equalsToEdgeInsets(edgeInsets) ||
38
+            !mLastFrame.equalsToRect(frame))) {
39
+      Assertions.assertNotNull(mInsetsChangeListener).onInsetsChange(this, edgeInsets, frame);
36 40
       mLastInsets = edgeInsets;
41
+      mLastFrame = frame;
37 42
     }
38 43
   }
39 44
 

+ 16
- 6
android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewManager.java Wyświetl plik

@@ -3,6 +3,7 @@ package com.th3rdwave.safeareacontext;
3 3
 import android.app.Activity;
4 4
 import android.content.Context;
5 5
 import android.view.View;
6
+import android.view.ViewGroup;
6 7
 import android.view.WindowManager;
7 8
 
8 9
 import com.facebook.react.bridge.ReactApplicationContext;
@@ -46,8 +47,8 @@ public class SafeAreaViewManager extends ViewGroupManager<SafeAreaView> {
46 47
         reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
47 48
     view.setOnInsetsChangeListener(new SafeAreaView.OnInsetsChangeListener() {
48 49
       @Override
49
-      public void onInsetsChange(SafeAreaView view, EdgeInsets insets) {
50
-        dispatcher.dispatchEvent(new InsetsChangeEvent(view.getId(), insets));
50
+      public void onInsetsChange(SafeAreaView view, EdgeInsets insets, Rect frame) {
51
+        dispatcher.dispatchEvent(new InsetsChangeEvent(view.getId(), insets, frame));
51 52
       }
52 53
     });
53 54
   }
@@ -67,18 +68,27 @@ public class SafeAreaViewManager extends ViewGroupManager<SafeAreaView> {
67 68
       return null;
68 69
     }
69 70
 
70
-    View decorView = activity.getWindow().getDecorView();
71
+    ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
71 72
     if (decorView == null) {
72 73
       return null;
73 74
     }
74 75
 
75
-    EdgeInsets insets = SafeAreaUtils.getSafeAreaInsets(mWindowManager, decorView);
76
+    View contentView = decorView.findViewById(android.R.id.content);
77
+    EdgeInsets insets = SafeAreaUtils.getSafeAreaInsets(
78
+        mWindowManager,
79
+        decorView,
80
+        contentView);
81
+    Rect frame = SafeAreaUtils.getFrame(decorView, contentView);
76 82
     if (insets == null) {
77 83
       return null;
78 84
     }
79 85
     return MapBuilder.<String, Object>of(
80
-        "initialWindowSafeAreaInsets",
81
-        SafeAreaUtils.edgeInsetsToJavaMap(insets));
86
+        "initialWindowMetrics",
87
+        MapBuilder.<String, Object>of(
88
+            "insets",
89
+            SerializationUtils.edgeInsetsToJavaMap(insets),
90
+            "frame",
91
+            SerializationUtils.rectToJavaMap(frame)));
82 92
 
83 93
   }
84 94
 }

+ 52
- 0
android/src/main/java/com/th3rdwave/safeareacontext/SerializationUtils.java Wyświetl plik

@@ -0,0 +1,52 @@
1
+package com.th3rdwave.safeareacontext;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.WritableMap;
5
+import com.facebook.react.common.MapBuilder;
6
+import com.facebook.react.uimanager.PixelUtil;
7
+
8
+import java.util.Map;
9
+
10
+/* package */ class SerializationUtils {
11
+  static WritableMap edgeInsetsToJsMap(EdgeInsets insets) {
12
+    WritableMap insetsMap = Arguments.createMap();
13
+    insetsMap.putDouble("top", PixelUtil.toDIPFromPixel(insets.top));
14
+    insetsMap.putDouble("right", PixelUtil.toDIPFromPixel(insets.right));
15
+    insetsMap.putDouble("bottom", PixelUtil.toDIPFromPixel(insets.bottom));
16
+    insetsMap.putDouble("left", PixelUtil.toDIPFromPixel(insets.left));
17
+    return insetsMap;
18
+  }
19
+
20
+  static Map<String, Float> edgeInsetsToJavaMap(EdgeInsets insets) {
21
+    return MapBuilder.of(
22
+        "top",
23
+        PixelUtil.toDIPFromPixel(insets.top),
24
+        "right",
25
+        PixelUtil.toDIPFromPixel(insets.right),
26
+        "bottom",
27
+        PixelUtil.toDIPFromPixel(insets.bottom),
28
+        "left",
29
+        PixelUtil.toDIPFromPixel(insets.left));
30
+  }
31
+
32
+  static WritableMap rectToJsMap(Rect rect) {
33
+    WritableMap rectMap = Arguments.createMap();
34
+    rectMap.putDouble("x", PixelUtil.toDIPFromPixel(rect.x));
35
+    rectMap.putDouble("y", PixelUtil.toDIPFromPixel(rect.y));
36
+    rectMap.putDouble("width", PixelUtil.toDIPFromPixel(rect.width));
37
+    rectMap.putDouble("height", PixelUtil.toDIPFromPixel(rect.height));
38
+    return rectMap;
39
+  }
40
+
41
+  static Map<String, Float> rectToJavaMap(Rect rect) {
42
+    return MapBuilder.of(
43
+        "x",
44
+        PixelUtil.toDIPFromPixel(rect.x),
45
+        "y",
46
+        PixelUtil.toDIPFromPixel(rect.y),
47
+        "width",
48
+        PixelUtil.toDIPFromPixel(rect.width),
49
+        "height",
50
+        PixelUtil.toDIPFromPixel(rect.height));
51
+  }
52
+}

+ 30
- 0
android/src/main/java/com/th3rdwave/safeareacontext/WindowChangeEvent.java Wyświetl plik

@@ -0,0 +1,30 @@
1
+package com.th3rdwave.safeareacontext;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.WritableMap;
5
+import com.facebook.react.uimanager.events.Event;
6
+import com.facebook.react.uimanager.events.RCTEventEmitter;
7
+
8
+/* package */  class WindowChangeEvent extends Event<WindowChangeEvent> {
9
+  private static final String EVENT_NAME = "topWindowChange";
10
+
11
+  private Rect mFrame;
12
+
13
+  WindowChangeEvent(int viewTag, Rect frame) {
14
+    super(viewTag);
15
+
16
+    mFrame = frame;
17
+  }
18
+
19
+  @Override
20
+  public String getEventName() {
21
+    return EVENT_NAME;
22
+  }
23
+
24
+  @Override
25
+  public void dispatch(RCTEventEmitter rctEventEmitter) {
26
+    WritableMap event = Arguments.createMap();
27
+    event.putMap("frame", SerializationUtils.rectToJsMap(mFrame));
28
+    rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event);
29
+  }
30
+}

+ 68
- 31
example/App.tsx Wyświetl plik

@@ -1,47 +1,82 @@
1 1
 import * as React from 'react';
2
-import { View, Text, StatusBar } from 'react-native';
2
+import { View, Text, StatusBar, ScrollView } from 'react-native';
3 3
 
4 4
 // import { SafeAreaProvider, useSafeArea } from 'react-native-safe-area-context'; in your app.
5 5
 import {
6 6
   SafeAreaProvider,
7
-  useSafeArea,
8
-  initialWindowSafeAreaInsets,
7
+  useSafeAreaInsets,
8
+  initialWindowMetrics,
9
+  useSafeAreaFrame,
9 10
 } from '../src';
10 11
 
12
+const DataView = ({ data }: { data: object | null | undefined }) => (
13
+  <Text style={{ fontSize: 16, lineHeight: 24, color: '#292929' }}>
14
+    {data == null
15
+      ? 'null'
16
+      : Object.entries(data)
17
+          .map(([key, value]) => `${key}: ${value}`)
18
+          .join('\n')}
19
+  </Text>
20
+);
21
+
22
+const Card = ({
23
+  title,
24
+  children,
25
+}: {
26
+  title: string;
27
+  children: React.ReactNode;
28
+}) => (
29
+  <View style={{ padding: 16, backgroundColor: 'white', marginBottom: 4 }}>
30
+    <Text
31
+      style={{
32
+        fontSize: 20,
33
+        fontWeight: 'bold',
34
+        marginBottom: 16,
35
+        color: '#292929',
36
+      }}
37
+    >
38
+      {title}
39
+    </Text>
40
+    {children}
41
+  </View>
42
+);
43
+
44
+const BORDER_WIDTH = 8;
45
+
11 46
 const Screen = () => {
12
-  const insets = useSafeArea();
47
+  const insets = useSafeAreaInsets();
48
+  const frame = useSafeAreaFrame();
13 49
 
14 50
   return (
15 51
     <>
16
-      <StatusBar
17
-        barStyle="dark-content"
18
-        translucent
19
-        backgroundColor="transparent"
20
-      />
52
+      <StatusBar barStyle="dark-content" backgroundColor="transparent" />
21 53
       <View
22 54
         style={{
23
-          flex: 1,
55
+          width: frame.width,
56
+          height: frame.height,
24 57
           backgroundColor: 'red',
25
-          paddingTop: insets.top,
26
-          paddingLeft: insets.left,
27
-          paddingBottom: insets.bottom,
28
-          paddingRight: insets.right,
58
+          paddingTop: insets.top - BORDER_WIDTH,
59
+          paddingLeft: insets.left - BORDER_WIDTH,
60
+          paddingBottom: insets.bottom - BORDER_WIDTH,
61
+          paddingRight: insets.right - BORDER_WIDTH,
62
+          borderColor: 'blue',
63
+          borderWidth: BORDER_WIDTH,
29 64
         }}
30 65
       >
31
-        <View
32
-          style={{
33
-            flex: 1,
34
-            backgroundColor: 'white',
35
-            alignItems: 'center',
36
-            justifyContent: 'center',
37
-          }}
38
-        >
39
-          <Text>Insets: {JSON.stringify(insets, null, 2)}</Text>
40
-          <Text>
41
-            Initial insets:{' '}
42
-            {JSON.stringify(initialWindowSafeAreaInsets, null, 2)}
43
-          </Text>
44
-        </View>
66
+        <ScrollView style={{ flex: 1, backgroundColor: '#eee' }}>
67
+          <Card title="Provider insets">
68
+            <DataView data={insets} />
69
+          </Card>
70
+          <Card title="Provider frame">
71
+            <DataView data={frame} />
72
+          </Card>
73
+          <Card title="Initial window insets">
74
+            <DataView data={initialWindowMetrics?.insets} />
75
+          </Card>
76
+          <Card title="Initial window frame">
77
+            <DataView data={initialWindowMetrics?.frame} />
78
+          </Card>
79
+        </ScrollView>
45 80
       </View>
46 81
     </>
47 82
   );
@@ -49,8 +84,10 @@ const Screen = () => {
49 84
 
50 85
 export default function App() {
51 86
   return (
52
-    <SafeAreaProvider>
53
-      <Screen />
54
-    </SafeAreaProvider>
87
+    <View style={{ marginTop: 0, flex: 1 }}>
88
+      <SafeAreaProvider>
89
+        <Screen />
90
+      </SafeAreaProvider>
91
+    </View>
55 92
   );
56 93
 }

+ 23
- 0
example/android/app/src/main/java/com/safeareaviewexample/MainActivity.java Wyświetl plik

@@ -1,9 +1,16 @@
1 1
 package com.safeareaviewexample;
2 2
 
3
+import android.os.Bundle;
4
+import android.view.View;
5
+import android.view.WindowManager;
6
+
3 7
 import com.facebook.react.ReactActivity;
4 8
 
5 9
 public class MainActivity extends ReactActivity {
6 10
 
11
+    private static final boolean TEST_TRANSLUCENT_STATUS_BAR = true;
12
+    private static final boolean TEST_TRANSLUCENT_NAVBAR = true;
13
+
7 14
     /**
8 15
      * Returns the name of the main component registered from JavaScript.
9 16
      * This is used to schedule rendering of the component.
@@ -12,4 +19,20 @@ public class MainActivity extends ReactActivity {
12 19
     protected String getMainComponentName() {
13 20
         return "SafeAreaViewExample";
14 21
     }
22
+
23
+    @Override
24
+    protected void onCreate(Bundle savedInstanceState) {
25
+        super.onCreate(savedInstanceState);
26
+
27
+        if (TEST_TRANSLUCENT_STATUS_BAR) {
28
+            getWindow().getDecorView().setSystemUiVisibility(
29
+                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
30
+        }
31
+        if (TEST_TRANSLUCENT_NAVBAR) {
32
+            getWindow().setFlags(
33
+                WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
34
+                WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
35
+        }
36
+
37
+    }
15 38
 }

+ 7
- 0
example/android/app/src/main/res/values-v28/styles.xml Wyświetl plik

@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+
4
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
5
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
6
+    </style>
7
+</resources>

+ 1
- 1
example/app.json Wyświetl plik

@@ -2,7 +2,7 @@
2 2
   "name": "SafeAreaViewExample",
3 3
   "displayName": "SafeAreaViewExample",
4 4
   "expo": {
5
-    "entryPoint": "node_modules/expo/AppEntry",
5
+    "entryPoint": "./example/index",
6 6
     "name": "react-native-safe-area-context",
7 7
     "slug": "react-native-safe-area-context",
8 8
     "version": "0.1.0",

+ 5
- 0
example/index.expo.js Wyświetl plik

@@ -0,0 +1,5 @@
1
+import registerRootComponent from 'expo/build/launch/registerRootComponent';
2
+
3
+import App from './App';
4
+
5
+registerRootComponent(App);

+ 2
- 0
example/index.js Wyświetl plik

@@ -2,4 +2,6 @@ import { AppRegistry } from 'react-native';
2 2
 import App from './App';
3 3
 import { name as appName } from './app.json';
4 4
 
5
+console.log('HEJADWDWD');
6
+
5 7
 AppRegistry.registerComponent(appName, () => App);

+ 15
- 2
ios/SafeAreaView/RNCSafeAreaView.m Wyświetl plik

@@ -16,6 +16,7 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
16 16
 @implementation RNCSafeAreaView
17 17
 {
18 18
   UIEdgeInsets _currentSafeAreaInsets;
19
+  CGRect _currentFrame;
19 20
   BOOL _initialInsetsSent;
20 21
 }
21 22
 
@@ -75,13 +76,19 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
75 76
   }
76 77
 
77 78
   UIEdgeInsets safeAreaInsets = [self realOrEmulateSafeAreaInsets];
79
+  CGRect frame = [self convertRect:self.bounds toView:nil];
78 80
 
79
-  if (_initialInsetsSent && UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale())) {
81
+  if (
82
+    _initialInsetsSent &&
83
+    UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale()) &&
84
+    CGRectEqualToRect(frame, _currentFrame)
85
+  ) {
80 86
     return;
81 87
   }
82 88
 
83 89
   _initialInsetsSent = YES;
84 90
   _currentSafeAreaInsets = safeAreaInsets;
91
+  _currentFrame = frame;
85 92
 
86 93
   self.onInsetsChange(@{
87 94
     @"insets": @{
@@ -89,7 +96,13 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
89 96
       @"right": @(safeAreaInsets.right),
90 97
       @"bottom": @(safeAreaInsets.bottom),
91 98
       @"left": @(safeAreaInsets.left),
92
-    }
99
+    },
100
+    @"frame": @{
101
+      @"x": @(frame.origin.x),
102
+      @"y": @(frame.origin.y),
103
+      @"width": @(frame.size.width),
104
+      @"height": @(frame.size.height),
105
+    },
93 106
   });
94 107
 }
95 108
 

+ 13
- 5
ios/SafeAreaView/RNCSafeAreaViewManager.m Wyświetl plik

@@ -24,11 +24,19 @@ RCT_EXPORT_VIEW_PROPERTY(onInsetsChange, RCTBubblingEventBlock)
24 24
     UIWindow* window = [[UIApplication sharedApplication] keyWindow];
25 25
     UIEdgeInsets safeAreaInsets = window.safeAreaInsets;
26 26
     return @{
27
-      @"initialWindowSafeAreaInsets": @{
28
-        @"top": @(safeAreaInsets.top),
29
-        @"right": @(safeAreaInsets.right),
30
-        @"bottom": @(safeAreaInsets.bottom),
31
-        @"left": @(safeAreaInsets.left),
27
+      @"initialWindowMetrics": @{
28
+        @"insets": @{
29
+          @"top": @(safeAreaInsets.top),
30
+          @"right": @(safeAreaInsets.right),
31
+          @"bottom": @(safeAreaInsets.bottom),
32
+          @"left": @(safeAreaInsets.left),
33
+        },
34
+        @"frame": @{
35
+          @"x": @(window.frame.origin.x),
36
+          @"y": @(window.frame.origin.y),
37
+          @"width": @(window.frame.size.width),
38
+          @"height": @(window.frame.size.height),
39
+        },
32 40
       }
33 41
     };
34 42
   } else {

+ 1
- 0
package.json Wyświetl plik

@@ -48,6 +48,7 @@
48 48
     "@types/jest": "^25.2.1",
49 49
     "@types/react": "^16.9.34",
50 50
     "@types/react-native": "^0.62.5",
51
+    "@types/react-dom": "^16.9.7",
51 52
     "@types/react-test-renderer": "^16.9.2",
52 53
     "@typescript-eslint/eslint-plugin": "^2.30.0",
53 54
     "@typescript-eslint/parser": "^2.30.0",

+ 0
- 4
src/InitialWindowSafeAreaInsets.web.ts Wyświetl plik

@@ -1,4 +0,0 @@
1
-import { EdgeInsets } from './SafeArea.types';
2
-
3
-const initialWindowSafeAreaInsets: EdgeInsets | null = null;
4
-export default initialWindowSafeAreaInsets;

+ 15
- 1
src/NativeSafeAreaView.web.tsx Wyświetl plik

@@ -2,6 +2,14 @@ import * as React from 'react';
2 2
 import { View } from 'react-native';
3 3
 import { NativeSafeAreaViewProps } from './SafeArea.types';
4 4
 
5
+/**
6
+ * TODO:
7
+ * Currently insets and frame are based on the window and are not
8
+ * relative to the provider view. This is inconsistent with iOS and Android.
9
+ * However in most cases if the provider view covers the screen this is not
10
+ * an issue.
11
+ */
12
+
5 13
 const CSSTransitions: Record<string, string> = {
6 14
   WebkitTransition: 'webkitTransitionEnd',
7 15
   Transition: 'transitionEnd',
@@ -37,8 +45,14 @@ export default function NativeSafeAreaView({
37 45
         left: paddingLeft ? parseInt(paddingLeft, 10) : 0,
38 46
         right: paddingRight ? parseInt(paddingRight, 10) : 0,
39 47
       };
48
+      const frame = {
49
+        x: 0,
50
+        y: 0,
51
+        width: document.documentElement.offsetWidth,
52
+        height: document.documentElement.offsetHeight,
53
+      };
40 54
       // @ts-ignore: missing properties
41
-      onInsetsChange({ nativeEvent: { insets } });
55
+      onInsetsChange({ nativeEvent: { insets, frame } });
42 56
     };
43 57
     element.addEventListener(getSupportedTransitionEvent(), onEnd);
44 58
     onEnd();

+ 13
- 1
src/SafeArea.types.ts Wyświetl plik

@@ -7,7 +7,19 @@ export interface EdgeInsets {
7 7
   left: number;
8 8
 }
9 9
 
10
-export type InsetChangedEvent = NativeSyntheticEvent<{ insets: EdgeInsets }>;
10
+export interface Rect {
11
+  x: number;
12
+  y: number;
13
+  width: number;
14
+  height: number;
15
+}
16
+
17
+export interface Metrics {
18
+  insets: EdgeInsets;
19
+  frame: Rect;
20
+}
21
+
22
+export type InsetChangedEvent = NativeSyntheticEvent<Metrics>;
11 23
 
12 24
 export type InsetChangeNativeCallback = (event: InsetChangedEvent) => void;
13 25
 

+ 85
- 0
src/SafeAreaContext.tsx Wyświetl plik

@@ -0,0 +1,85 @@
1
+import * as React from 'react';
2
+import { StyleSheet } from 'react-native';
3
+import NativeSafeAreaView from './NativeSafeAreaView';
4
+import { EdgeInsets, InsetChangedEvent, Metrics, Rect } from './SafeArea.types';
5
+
6
+export const SafeAreaInsetsContext = React.createContext<EdgeInsets | null>(
7
+  null,
8
+);
9
+
10
+export const SafeAreaFrameContext = React.createContext<Rect | null>(null);
11
+
12
+export interface SafeAreaViewProps {
13
+  children?: React.ReactNode;
14
+  initialMetrics?: Metrics | null;
15
+}
16
+
17
+export function SafeAreaProvider({
18
+  children,
19
+  initialMetrics,
20
+}: SafeAreaViewProps) {
21
+  const parentInsets = useParentSafeAreaInsets();
22
+  const parentFrame = useParentSafeAreaFrame();
23
+  const [insets, setInsets] = React.useState<EdgeInsets | null>(
24
+    initialMetrics?.insets ?? parentInsets ?? null,
25
+  );
26
+  const [frame, setFrame] = React.useState<Rect | null>(
27
+    initialMetrics?.frame ?? parentFrame ?? null,
28
+  );
29
+  const onInsetsChange = React.useCallback((event: InsetChangedEvent) => {
30
+    setInsets(event.nativeEvent.insets);
31
+    setFrame(event.nativeEvent.frame);
32
+  }, []);
33
+
34
+  return (
35
+    <NativeSafeAreaView style={styles.fill} onInsetsChange={onInsetsChange}>
36
+      {insets != null && frame != null ? (
37
+        <SafeAreaFrameContext.Provider value={frame}>
38
+          <SafeAreaInsetsContext.Provider value={insets}>
39
+            {children}
40
+          </SafeAreaInsetsContext.Provider>
41
+        </SafeAreaFrameContext.Provider>
42
+      ) : null}
43
+    </NativeSafeAreaView>
44
+  );
45
+}
46
+
47
+const styles = StyleSheet.create({
48
+  fill: { flex: 1 },
49
+});
50
+
51
+function useParentSafeAreaInsets(): EdgeInsets | null {
52
+  return React.useContext(SafeAreaInsetsContext);
53
+}
54
+
55
+function useParentSafeAreaFrame(): Rect | null {
56
+  return React.useContext(SafeAreaFrameContext);
57
+}
58
+
59
+export function useSafeAreaInsets(): EdgeInsets {
60
+  const safeArea = React.useContext(SafeAreaInsetsContext);
61
+  if (safeArea == null) {
62
+    throw new Error(
63
+      'No safe area insets value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
64
+    );
65
+  }
66
+  return safeArea;
67
+}
68
+
69
+export function useSafeAreaFrame(): Rect {
70
+  const frame = React.useContext(SafeAreaFrameContext);
71
+  if (frame == null) {
72
+    throw new Error(
73
+      'No safe area frame value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
74
+    );
75
+  }
76
+  return frame;
77
+}
78
+
79
+/**
80
+ * @deprecated
81
+ */
82
+export function useSafeArea(): EdgeInsets {
83
+  console.warn('useSafeArea is deprecated, use useSafeAreaInsets instead.');
84
+  return useSafeAreaInsets();
85
+}

+ 25
- 0
src/SafeAreaView.tsx Wyświetl plik

@@ -0,0 +1,25 @@
1
+import * as React from 'react';
2
+import { View, ViewProps } from 'react-native';
3
+import { useSafeAreaInsets } from './SafeAreaContext';
4
+
5
+export function SafeAreaView({
6
+  style,
7
+  ...rest
8
+}: ViewProps & { children: React.ReactNode }) {
9
+  const insets = useSafeAreaInsets();
10
+
11
+  return (
12
+    <View
13
+      style={[
14
+        {
15
+          paddingTop: insets.top,
16
+          paddingRight: insets.right,
17
+          paddingBottom: insets.bottom,
18
+          paddingLeft: insets.left,
19
+        },
20
+        style,
21
+      ]}
22
+      {...rest}
23
+    />
24
+  );
25
+}

+ 121
- 0
src/__tests__/SafeAreaContext-test.tsx Wyświetl plik

@@ -0,0 +1,121 @@
1
+import * as React from 'react';
2
+import { View } from 'react-native';
3
+import * as ReactTestRenderer from 'react-test-renderer';
4
+import NativeSafeAreaView from '../NativeSafeAreaView';
5
+import {
6
+  SafeAreaProvider,
7
+  useSafeAreaInsets,
8
+  useSafeAreaFrame,
9
+} from '../SafeAreaContext';
10
+import { Metrics } from '../SafeArea.types';
11
+
12
+const TEST_METRICS_1: Metrics = {
13
+  insets: { top: 1, left: 2, right: 3, bottom: 4 },
14
+  frame: { x: 0, y: 0, height: 100, width: 100 },
15
+};
16
+const TEST_METRICS_2: Metrics = {
17
+  insets: { top: 2, left: 3, right: 4, bottom: 5 },
18
+  frame: { x: 0, y: 0, width: 10, height: 16 },
19
+};
20
+
21
+const PrintInsetsTestView = () => {
22
+  const insets = useSafeAreaInsets();
23
+  const frame = useSafeAreaFrame();
24
+  return (
25
+    <View
26
+      style={{
27
+        paddingTop: insets.top,
28
+        paddingLeft: insets.left,
29
+        paddingBottom: insets.bottom,
30
+        paddingRight: insets.right,
31
+        top: frame.y,
32
+        left: frame.y,
33
+        width: frame.width,
34
+        height: frame.height,
35
+      }}
36
+    />
37
+  );
38
+};
39
+
40
+describe('SafeAreaProvider', () => {
41
+  it('renders', () => {
42
+    const component = ReactTestRenderer.create(<SafeAreaProvider />);
43
+    expect(component).toMatchSnapshot();
44
+  });
45
+
46
+  it('does not render child until inset values are received', () => {
47
+    const component = ReactTestRenderer.create(
48
+      <SafeAreaProvider>
49
+        <PrintInsetsTestView />
50
+      </SafeAreaProvider>,
51
+    );
52
+    expect(component).toMatchSnapshot();
53
+  });
54
+
55
+  it('renders child when inset values are received', () => {
56
+    const component = ReactTestRenderer.create(
57
+      <SafeAreaProvider>
58
+        <PrintInsetsTestView />
59
+      </SafeAreaProvider>,
60
+    );
61
+    expect(component).toMatchSnapshot();
62
+    const { onInsetsChange } = component.root.findByType(
63
+      NativeSafeAreaView,
64
+    ).props;
65
+    ReactTestRenderer.act(() => {
66
+      onInsetsChange({
67
+        nativeEvent: TEST_METRICS_1,
68
+      });
69
+    });
70
+    expect(component).toMatchSnapshot();
71
+  });
72
+
73
+  it('supports setting initial insets', () => {
74
+    const component = ReactTestRenderer.create(
75
+      <SafeAreaProvider initialMetrics={TEST_METRICS_1}>
76
+        <PrintInsetsTestView />
77
+      </SafeAreaProvider>,
78
+    );
79
+    expect(component).toMatchSnapshot();
80
+  });
81
+
82
+  it('uses parent insets when available', () => {
83
+    const component = ReactTestRenderer.create(
84
+      <SafeAreaProvider initialMetrics={TEST_METRICS_1}>
85
+        <SafeAreaProvider>
86
+          <PrintInsetsTestView />
87
+        </SafeAreaProvider>
88
+      </SafeAreaProvider>,
89
+    );
90
+    expect(component).toMatchSnapshot();
91
+  });
92
+
93
+  it('uses inner insets', () => {
94
+    const component = ReactTestRenderer.create(
95
+      <SafeAreaProvider initialMetrics={TEST_METRICS_1}>
96
+        <SafeAreaProvider initialMetrics={TEST_METRICS_2}>
97
+          <PrintInsetsTestView />
98
+        </SafeAreaProvider>
99
+      </SafeAreaProvider>,
100
+    );
101
+    expect(component).toMatchSnapshot();
102
+  });
103
+
104
+  it('throws when no provider is rendered', () => {
105
+    // Silence the React error boundary warning; we expect an uncaught error.
106
+    const consoleErrorMock = jest
107
+      .spyOn(console, 'error')
108
+      .mockImplementation((message) => {
109
+        if (message.startsWith('The above error occured in the ')) {
110
+          return;
111
+        }
112
+      });
113
+    expect(() => {
114
+      ReactTestRenderer.create(<PrintInsetsTestView />);
115
+    }).toThrow(
116
+      'No safe area insets value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
117
+    );
118
+
119
+    consoleErrorMock.mockRestore();
120
+  });
121
+});

+ 35
- 0
src/__tests__/SafeAreaView-test.tsx Wyświetl plik

@@ -0,0 +1,35 @@
1
+import * as React from 'react';
2
+import { View } from 'react-native';
3
+import * as ReactTestRenderer from 'react-test-renderer';
4
+import { SafeAreaProvider } from '../SafeAreaContext';
5
+import { SafeAreaView } from '../SafeAreaView';
6
+import { Metrics } from '../SafeArea.types';
7
+
8
+const INITIAL_METRICS: Metrics = {
9
+  insets: { top: 1, left: 2, right: 3, bottom: 4 },
10
+  frame: { x: 0, y: 0, height: 100, width: 100 },
11
+};
12
+
13
+describe('SafeAreaView', () => {
14
+  it('renders', () => {
15
+    const component = ReactTestRenderer.create(
16
+      <SafeAreaProvider initialMetrics={INITIAL_METRICS}>
17
+        <SafeAreaView>
18
+          <View />
19
+        </SafeAreaView>
20
+      </SafeAreaProvider>,
21
+    );
22
+    expect(component).toMatchSnapshot();
23
+  });
24
+
25
+  it('can override padding styles', () => {
26
+    const component = ReactTestRenderer.create(
27
+      <SafeAreaProvider initialMetrics={INITIAL_METRICS}>
28
+        <SafeAreaView style={{ paddingTop: 0 }}>
29
+          <View />
30
+        </SafeAreaView>
31
+      </SafeAreaProvider>,
32
+    );
33
+    expect(component).toMatchSnapshot();
34
+  });
35
+});

src/__tests__/__snapshots__/index-test.tsx.snap → src/__tests__/__snapshots__/SafeAreaContext-test.tsx.snap Wyświetl plik

@@ -45,10 +45,14 @@ exports[`SafeAreaProvider renders child when inset values are received 2`] = `
45 45
   <View
46 46
     style={
47 47
       Object {
48
-        "bottom": 4,
49
-        "left": 2,
50
-        "right": 3,
51
-        "top": 1,
48
+        "height": 100,
49
+        "left": 0,
50
+        "paddingBottom": 4,
51
+        "paddingLeft": 2,
52
+        "paddingRight": 3,
53
+        "paddingTop": 1,
54
+        "top": 0,
55
+        "width": 100,
52 56
       }
53 57
     }
54 58
   />
@@ -67,10 +71,14 @@ exports[`SafeAreaProvider supports setting initial insets 1`] = `
67 71
   <View
68 72
     style={
69 73
       Object {
70
-        "bottom": 4,
71
-        "left": 2,
72
-        "right": 3,
73
-        "top": 1,
74
+        "height": 100,
75
+        "left": 0,
76
+        "paddingBottom": 4,
77
+        "paddingLeft": 2,
78
+        "paddingRight": 3,
79
+        "paddingTop": 1,
80
+        "top": 0,
81
+        "width": 100,
74 82
       }
75 83
     }
76 84
   />
@@ -97,10 +105,14 @@ exports[`SafeAreaProvider uses inner insets 1`] = `
97 105
     <View
98 106
       style={
99 107
         Object {
100
-          "bottom": 5,
101
-          "left": 3,
102
-          "right": 4,
103
-          "top": 2,
108
+          "height": 16,
109
+          "left": 0,
110
+          "paddingBottom": 5,
111
+          "paddingLeft": 3,
112
+          "paddingRight": 4,
113
+          "paddingTop": 2,
114
+          "top": 0,
115
+          "width": 10,
104 116
         }
105 117
       }
106 118
     />
@@ -128,69 +140,17 @@ exports[`SafeAreaProvider uses parent insets when available 1`] = `
128 140
     <View
129 141
       style={
130 142
         Object {
131
-          "bottom": 4,
132
-          "left": 2,
133
-          "right": 3,
134
-          "top": 1,
135
-        }
136
-      }
137
-    />
138
-  </RNCSafeAreaView>
139
-</RNCSafeAreaView>
140
-`;
141
-
142
-exports[`SafeAreaView can override padding styles 1`] = `
143
-<RNCSafeAreaView
144
-  onInsetsChange={[Function]}
145
-  style={
146
-    Object {
147
-      "flex": 1,
148
-    }
149
-  }
150
->
151
-  <View
152
-    style={
153
-      Array [
154
-        Object {
143
+          "height": 100,
144
+          "left": 0,
155 145
           "paddingBottom": 4,
156 146
           "paddingLeft": 2,
157 147
           "paddingRight": 3,
158 148
           "paddingTop": 1,
159
-        },
160
-        Object {
161
-          "paddingTop": 0,
162
-        },
163
-      ]
164
-    }
165
-  >
166
-    <View />
167
-  </View>
168
-</RNCSafeAreaView>
169
-`;
170
-
171
-exports[`SafeAreaView renders 1`] = `
172
-<RNCSafeAreaView
173
-  onInsetsChange={[Function]}
174
-  style={
175
-    Object {
176
-      "flex": 1,
177
-    }
178
-  }
179
->
180
-  <View
181
-    style={
182
-      Array [
183
-        Object {
184
-          "paddingBottom": 4,
185
-          "paddingLeft": 2,
186
-          "paddingRight": 3,
187
-          "paddingTop": 1,
188
-        },
189
-        undefined,
190
-      ]
191
-    }
192
-  >
193
-    <View />
194
-  </View>
149
+          "top": 0,
150
+          "width": 100,
151
+        }
152
+      }
153
+    />
154
+  </RNCSafeAreaView>
195 155
 </RNCSafeAreaView>
196 156
 `;

+ 57
- 0
src/__tests__/__snapshots__/SafeAreaView-test.tsx.snap Wyświetl plik

@@ -0,0 +1,57 @@
1
+// Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+exports[`SafeAreaView can override padding styles 1`] = `
4
+<RNCSafeAreaView
5
+  onInsetsChange={[Function]}
6
+  style={
7
+    Object {
8
+      "flex": 1,
9
+    }
10
+  }
11
+>
12
+  <View
13
+    style={
14
+      Array [
15
+        Object {
16
+          "paddingBottom": 4,
17
+          "paddingLeft": 2,
18
+          "paddingRight": 3,
19
+          "paddingTop": 1,
20
+        },
21
+        Object {
22
+          "paddingTop": 0,
23
+        },
24
+      ]
25
+    }
26
+  >
27
+    <View />
28
+  </View>
29
+</RNCSafeAreaView>
30
+`;
31
+
32
+exports[`SafeAreaView renders 1`] = `
33
+<RNCSafeAreaView
34
+  onInsetsChange={[Function]}
35
+  style={
36
+    Object {
37
+      "flex": 1,
38
+    }
39
+  }
40
+>
41
+  <View
42
+    style={
43
+      Array [
44
+        Object {
45
+          "paddingBottom": 4,
46
+          "paddingLeft": 2,
47
+          "paddingRight": 3,
48
+          "paddingTop": 1,
49
+        },
50
+        undefined,
51
+      ]
52
+    }
53
+  >
54
+    <View />
55
+  </View>
56
+</RNCSafeAreaView>
57
+`;

+ 0
- 169
src/__tests__/index-test.tsx Wyświetl plik

@@ -1,169 +0,0 @@
1
-import * as React from 'react';
2
-import * as ReactTestRenderer from 'react-test-renderer';
3
-import { View, UIManager } from 'react-native';
4
-import { SafeAreaProvider, SafeAreaView, useSafeArea } from '../index';
5
-import NativeSafeAreaView from '../NativeSafeAreaView';
6
-
7
-const PrintInsetsTestView = () => {
8
-  const insets = useSafeArea();
9
-  return (
10
-    <View
11
-      style={{
12
-        top: insets.top,
13
-        left: insets.left,
14
-        bottom: insets.bottom,
15
-        right: insets.right,
16
-      }}
17
-    />
18
-  );
19
-};
20
-
21
-describe('SafeAreaProvider', () => {
22
-  it('renders', () => {
23
-    const component = ReactTestRenderer.create(<SafeAreaProvider />);
24
-    expect(component).toMatchSnapshot();
25
-  });
26
-
27
-  it('does not render child until inset values are received', () => {
28
-    const component = ReactTestRenderer.create(
29
-      <SafeAreaProvider>
30
-        <PrintInsetsTestView />
31
-      </SafeAreaProvider>,
32
-    );
33
-    expect(component).toMatchSnapshot();
34
-  });
35
-
36
-  it('renders child when inset values are received', () => {
37
-    const component = ReactTestRenderer.create(
38
-      <SafeAreaProvider>
39
-        <PrintInsetsTestView />
40
-      </SafeAreaProvider>,
41
-    );
42
-    expect(component).toMatchSnapshot();
43
-    const { onInsetsChange } = component.root.findByType(
44
-      NativeSafeAreaView,
45
-    ).props;
46
-    ReactTestRenderer.act(() => {
47
-      onInsetsChange({
48
-        nativeEvent: { insets: { top: 1, left: 2, right: 3, bottom: 4 } },
49
-      });
50
-    });
51
-    expect(component).toMatchSnapshot();
52
-  });
53
-
54
-  it('supports setting initial insets', () => {
55
-    const component = ReactTestRenderer.create(
56
-      <SafeAreaProvider
57
-        initialSafeAreaInsets={{ top: 1, left: 2, right: 3, bottom: 4 }}
58
-      >
59
-        <PrintInsetsTestView />
60
-      </SafeAreaProvider>,
61
-    );
62
-    expect(component).toMatchSnapshot();
63
-  });
64
-
65
-  it('uses parent insets when available', () => {
66
-    const component = ReactTestRenderer.create(
67
-      <SafeAreaProvider
68
-        initialSafeAreaInsets={{ top: 1, left: 2, right: 3, bottom: 4 }}
69
-      >
70
-        <SafeAreaProvider>
71
-          <PrintInsetsTestView />
72
-        </SafeAreaProvider>
73
-      </SafeAreaProvider>,
74
-    );
75
-    expect(component).toMatchSnapshot();
76
-  });
77
-
78
-  it('uses inner insets', () => {
79
-    const component = ReactTestRenderer.create(
80
-      <SafeAreaProvider
81
-        initialSafeAreaInsets={{ top: 1, left: 2, right: 3, bottom: 4 }}
82
-      >
83
-        <SafeAreaProvider
84
-          initialSafeAreaInsets={{ top: 2, left: 3, right: 4, bottom: 5 }}
85
-        >
86
-          <PrintInsetsTestView />
87
-        </SafeAreaProvider>
88
-      </SafeAreaProvider>,
89
-    );
90
-    expect(component).toMatchSnapshot();
91
-  });
92
-
93
-  it('throws when no provider is rendered', () => {
94
-    // Silence the React error boundary warning; we expect an uncaught error.
95
-    const consoleErrorMock = jest
96
-      .spyOn(console, 'error')
97
-      .mockImplementation(message => {
98
-        if (message.startsWith('The above error occured in the ')) {
99
-          return;
100
-        }
101
-      });
102
-    expect(() => {
103
-      ReactTestRenderer.create(<PrintInsetsTestView />);
104
-    }).toThrow(
105
-      'No safe area value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
106
-    );
107
-
108
-    consoleErrorMock.mockRestore();
109
-  });
110
-});
111
-
112
-describe('SafeAreaView', () => {
113
-  it('renders', () => {
114
-    const component = ReactTestRenderer.create(
115
-      <SafeAreaProvider
116
-        initialSafeAreaInsets={{ top: 1, left: 2, right: 3, bottom: 4 }}
117
-      >
118
-        <SafeAreaView>
119
-          <View />
120
-        </SafeAreaView>
121
-      </SafeAreaProvider>,
122
-    );
123
-    expect(component).toMatchSnapshot();
124
-  });
125
-
126
-  it('can override padding styles', () => {
127
-    const component = ReactTestRenderer.create(
128
-      <SafeAreaProvider
129
-        initialSafeAreaInsets={{ top: 1, left: 2, right: 3, bottom: 4 }}
130
-      >
131
-        <SafeAreaView style={{ paddingTop: 0 }}>
132
-          <View />
133
-        </SafeAreaView>
134
-      </SafeAreaProvider>,
135
-    );
136
-    expect(component).toMatchSnapshot();
137
-  });
138
-});
139
-
140
-describe('initialWindowSafeAreaInsets', () => {
141
-  it('is null when no view config is available', () => {
142
-    jest.resetModules();
143
-    expect(require('../index').initialWindowSafeAreaInsets).toBe(null);
144
-  });
145
-
146
-  it('it uses the constant provided by the view config', () => {
147
-    jest.resetModules();
148
-    const testInsets = {
149
-      top: 20,
150
-      left: 0,
151
-      right: 0,
152
-      bottom: 0,
153
-    };
154
-    UIManager.getViewManagerConfig = jest.fn(name => {
155
-      if (name === 'RNCSafeAreaView') {
156
-        return {
157
-          Commands: {},
158
-          Constants: {
159
-            initialWindowSafeAreaInsets: testInsets,
160
-          },
161
-        };
162
-      }
163
-      return { Commands: {} };
164
-    });
165
-
166
-    expect(require('../index').initialWindowSafeAreaInsets).toBe(testInsets);
167
-    expect(UIManager.getViewManagerConfig).toBeCalledWith('RNCSafeAreaView');
168
-  });
169
-});

+ 41
- 0
src/__tests__/initialWindowMetrics-test.tsx Wyświetl plik

@@ -0,0 +1,41 @@
1
+import { UIManager } from 'react-native';
2
+import { Metrics } from '../SafeArea.types';
3
+
4
+describe('initialWindowMetrics', () => {
5
+  it('is null when no view config is available', () => {
6
+    jest.resetModules();
7
+    expect(require('../initialWindowMetrics').initialWindowMetrics).toBe(null);
8
+  });
9
+
10
+  it('it uses the constant provided by the view config', () => {
11
+    jest.resetModules();
12
+    const testMetrics: Metrics = {
13
+      insets: {
14
+        top: 20,
15
+        left: 0,
16
+        right: 0,
17
+        bottom: 0,
18
+      },
19
+      frame: {
20
+        x: 0,
21
+        y: 0,
22
+        height: 100,
23
+        width: 100,
24
+      },
25
+    };
26
+    UIManager.getViewManagerConfig = jest.fn((name) => {
27
+      if (name === 'RNCSafeAreaView') {
28
+        return {
29
+          Commands: {},
30
+          Constants: {
31
+            initialWindowMetrics: testMetrics,
32
+          },
33
+        };
34
+      }
35
+      return { Commands: {} };
36
+    });
37
+
38
+    expect(require('../index').initialWindowMetrics).toBe(testMetrics);
39
+    expect(UIManager.getViewManagerConfig).toBeCalledWith('RNCSafeAreaView');
40
+  });
41
+});

+ 4
- 80
src/index.tsx Wyświetl plik

@@ -1,80 +1,4 @@
1
-import * as React from 'react';
2
-import { StyleSheet, View, ViewProps } from 'react-native';
3
-import { EdgeInsets as EdgeInsetsT, InsetChangedEvent } from './SafeArea.types';
4
-import NativeSafeAreaView from './NativeSafeAreaView';
5
-
6
-export { default as initialWindowSafeAreaInsets } from './InitialWindowSafeAreaInsets';
7
-
8
-export const SafeAreaContext = React.createContext<EdgeInsetsT | null>(null);
9
-
10
-export interface SafeAreaViewProps {
11
-  children?: React.ReactNode;
12
-  initialSafeAreaInsets?: EdgeInsetsT | null;
13
-}
14
-
15
-export function SafeAreaProvider({
16
-  children,
17
-  initialSafeAreaInsets,
18
-}: SafeAreaViewProps) {
19
-  const parentInsets = useParentSafeArea();
20
-  const [insets, setInsets] = React.useState<EdgeInsetsT | null | undefined>(
21
-    initialSafeAreaInsets || parentInsets,
22
-  );
23
-  const onInsetsChange = React.useCallback((event: InsetChangedEvent) => {
24
-    setInsets(event.nativeEvent.insets);
25
-  }, []);
26
-
27
-  return (
28
-    <NativeSafeAreaView style={styles.fill} onInsetsChange={onInsetsChange}>
29
-      {insets != null ? (
30
-        <SafeAreaContext.Provider value={insets}>
31
-          {children}
32
-        </SafeAreaContext.Provider>
33
-      ) : null}
34
-    </NativeSafeAreaView>
35
-  );
36
-}
37
-
38
-const styles = StyleSheet.create({
39
-  fill: { flex: 1 },
40
-});
41
-
42
-export const SafeAreaConsumer = SafeAreaContext.Consumer;
43
-
44
-function useParentSafeArea(): React.ContextType<typeof SafeAreaContext> {
45
-  return React.useContext(SafeAreaContext);
46
-}
47
-
48
-export function useSafeArea(): EdgeInsetsT {
49
-  const safeArea = React.useContext(SafeAreaContext);
50
-  if (safeArea == null) {
51
-    throw new Error(
52
-      'No safe area value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
53
-    );
54
-  }
55
-  return safeArea;
56
-}
57
-
58
-export function SafeAreaView({
59
-  style,
60
-  ...rest
61
-}: ViewProps & { children: React.ReactNode }) {
62
-  const insets = useSafeArea();
63
-
64
-  return (
65
-    <View
66
-      style={[
67
-        {
68
-          paddingTop: insets.top,
69
-          paddingRight: insets.right,
70
-          paddingBottom: insets.bottom,
71
-          paddingLeft: insets.left,
72
-        },
73
-        style,
74
-      ]}
75
-      {...rest}
76
-    />
77
-  );
78
-}
79
-
80
-export type EdgeInsets = EdgeInsetsT;
1
+export * from './SafeAreaContext';
2
+export * from './SafeAreaView';
3
+export * from './initialWindowMetrics';
4
+export * from './SafeArea.types';

src/InitialWindowSafeAreaInsets.ts → src/initialWindowMetrics.ts Wyświetl plik

@@ -1,12 +1,12 @@
1 1
 import { UIManager } from 'react-native';
2
-import { EdgeInsets } from './SafeArea.types';
2
+import { Metrics } from './SafeArea.types';
3 3
 
4 4
 const RNCSafeAreaViewConfig = UIManager.getViewManagerConfig(
5 5
   'RNCSafeAreaView',
6 6
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
7 7
 ) as any;
8 8
 
9
-export default (RNCSafeAreaViewConfig != null &&
9
+export const initialWindowMetrics = (RNCSafeAreaViewConfig != null &&
10 10
 RNCSafeAreaViewConfig.Constants != null
11
-  ? RNCSafeAreaViewConfig.Constants.initialWindowSafeAreaInsets
12
-  : null) as EdgeInsets | null;
11
+  ? RNCSafeAreaViewConfig.Constants.initialWindowMetrics
12
+  : null) as Metrics | null;

+ 3
- 0
src/initialWindowMetrics.web.ts Wyświetl plik

@@ -0,0 +1,3 @@
1
+import { EdgeInsets } from './SafeArea.types';
2
+
3
+export const initialWindowMetrics: EdgeInsets | null = null;

+ 15
- 228
yarn.lock Wyświetl plik

@@ -1617,6 +1617,13 @@
1617 1617
   resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.1.tgz#937fab3194766256ee09fcd40b781740758617e7"
1618 1618
   integrity sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==
1619 1619
 
1620
+"@types/react-dom@^16.9.7":
1621
+  version "16.9.7"
1622
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.7.tgz#60844d48ce252d7b2dccf0c7bb937130e27c0cd2"
1623
+  integrity sha512-GHTYhM8/OwUCf254WO5xqR/aqD3gC9kSTLpopWGpQLpnw23jk44RvMHsyUSEplvRJZdHxhJGMMLF0kCPYHPhQA==
1624
+  dependencies:
1625
+    "@types/react" "*"
1626
+
1620 1627
 "@types/react-native@^0.62.5":
1621 1628
   version "0.62.5"
1622 1629
   resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.62.5.tgz#605c2d31485c515ee18b62e8b29ffdbba404a443"
@@ -1732,11 +1739,6 @@ abab@^2.0.0:
1732 1739
   resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
1733 1740
   integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
1734 1741
 
1735
-abbrev@1:
1736
-  version "1.1.1"
1737
-  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
1738
-  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
1739
-
1740 1742
 abort-controller@^3.0.0:
1741 1743
   version "3.0.0"
1742 1744
   resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@@ -1913,19 +1915,6 @@ anymatch@^3.0.3:
1913 1915
     normalize-path "^3.0.0"
1914 1916
     picomatch "^2.0.4"
1915 1917
 
1916
-aproba@^1.0.3:
1917
-  version "1.2.0"
1918
-  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
1919
-  integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
1920
-
1921
-are-we-there-yet@~1.1.2:
1922
-  version "1.1.5"
1923
-  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
1924
-  integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
1925
-  dependencies:
1926
-    delegates "^1.0.0"
1927
-    readable-stream "^2.0.6"
1928
-
1929 1918
 argparse@^1.0.7:
1930 1919
   version "1.0.10"
1931 1920
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -2481,11 +2470,6 @@ chardet@^0.7.0:
2481 2470
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
2482 2471
   integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
2483 2472
 
2484
-chownr@^1.1.1:
2485
-  version "1.1.4"
2486
-  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
2487
-  integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
2488
-
2489 2473
 ci-info@^2.0.0:
2490 2474
   version "2.0.0"
2491 2475
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@@ -2715,11 +2699,6 @@ connect@^3.6.5:
2715 2699
     parseurl "~1.3.3"
2716 2700
     utils-merge "1.0.1"
2717 2701
 
2718
-console-control-strings@^1.0.0, console-control-strings@~1.1.0:
2719
-  version "1.1.0"
2720
-  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
2721
-  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
2722
-
2723 2702
 convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
2724 2703
   version "1.7.0"
2725 2704
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
@@ -2882,13 +2861,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
2882 2861
   dependencies:
2883 2862
     ms "2.0.0"
2884 2863
 
2885
-debug@^3.2.6:
2886
-  version "3.2.6"
2887
-  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
2888
-  integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
2889
-  dependencies:
2890
-    ms "^2.1.1"
2891
-
2892 2864
 debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
2893 2865
   version "4.1.1"
2894 2866
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
@@ -2925,11 +2897,6 @@ deep-assign@^3.0.0:
2925 2897
   dependencies:
2926 2898
     is-obj "^1.0.0"
2927 2899
 
2928
-deep-extend@^0.6.0:
2929
-  version "0.6.0"
2930
-  resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
2931
-  integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
2932
-
2933 2900
 deep-is@~0.1.3:
2934 2901
   version "0.1.3"
2935 2902
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@@ -3005,11 +2972,6 @@ delayed-stream@~1.0.0:
3005 2972
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
3006 2973
   integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
3007 2974
 
3008
-delegates@^1.0.0:
3009
-  version "1.0.0"
3010
-  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
3011
-  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
3012
-
3013 2975
 denodeify@^1.2.1:
3014 2976
   version "1.2.1"
3015 2977
   resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631"
@@ -3025,11 +2987,6 @@ destroy@~1.0.4:
3025 2987
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
3026 2988
   integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
3027 2989
 
3028
-detect-libc@^1.0.2:
3029
-  version "1.0.3"
3030
-  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
3031
-  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
3032
-
3033 2990
 detect-newline@^3.0.0:
3034 2991
   version "3.1.0"
3035 2992
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@@ -3963,13 +3920,6 @@ fs-extra@^8.1.0:
3963 3920
     jsonfile "^4.0.0"
3964 3921
     universalify "^0.1.0"
3965 3922
 
3966
-fs-minipass@^1.2.5:
3967
-  version "1.2.7"
3968
-  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
3969
-  integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
3970
-  dependencies:
3971
-    minipass "^2.6.0"
3972
-
3973 3923
 fs.realpath@^1.0.0:
3974 3924
   version "1.0.0"
3975 3925
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -3998,20 +3948,6 @@ functional-red-black-tree@^1.0.1:
3998 3948
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
3999 3949
   integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
4000 3950
 
4001
-gauge@~2.7.3:
4002
-  version "2.7.4"
4003
-  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
4004
-  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
4005
-  dependencies:
4006
-    aproba "^1.0.3"
4007
-    console-control-strings "^1.0.0"
4008
-    has-unicode "^2.0.0"
4009
-    object-assign "^4.1.0"
4010
-    signal-exit "^3.0.0"
4011
-    string-width "^1.0.1"
4012
-    strip-ansi "^3.0.1"
4013
-    wide-align "^1.1.0"
4014
-
4015 3951
 gensync@^1.0.0-beta.1:
4016 3952
   version "1.0.0-beta.1"
4017 3953
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@@ -4177,11 +4113,6 @@ has-symbols@^1.0.0, has-symbols@^1.0.1:
4177 4113
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
4178 4114
   integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
4179 4115
 
4180
-has-unicode@^2.0.0:
4181
-  version "2.0.1"
4182
-  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
4183
-  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
4184
-
4185 4116
 has-value@^0.3.1:
4186 4117
   version "0.3.1"
4187 4118
   resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -4277,20 +4208,13 @@ hyphenate-style-name@^1.0.2, hyphenate-style-name@^1.0.3:
4277 4208
   resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
4278 4209
   integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==
4279 4210
 
4280
-iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
4211
+iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
4281 4212
   version "0.4.24"
4282 4213
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
4283 4214
   integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
4284 4215
   dependencies:
4285 4216
     safer-buffer ">= 2.1.2 < 3"
4286 4217
 
4287
-ignore-walk@^3.0.1:
4288
-  version "3.0.3"
4289
-  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
4290
-  integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
4291
-  dependencies:
4292
-    minimatch "^3.0.4"
4293
-
4294 4218
 ignore@^4.0.6:
4295 4219
   version "4.0.6"
4296 4220
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
@@ -4358,11 +4282,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3:
4358 4282
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
4359 4283
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
4360 4284
 
4361
-ini@~1.3.0:
4362
-  version "1.3.5"
4363
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
4364
-  integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
4365
-
4366 4285
 inline-style-prefixer@^5.1.0:
4367 4286
   version "5.1.2"
4368 4287
   resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-5.1.2.tgz#e5a5a3515e25600e016b71e39138971228486c33"
@@ -6051,21 +5970,6 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
6051 5970
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
6052 5971
   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
6053 5972
 
6054
-minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
6055
-  version "2.9.0"
6056
-  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
6057
-  integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
6058
-  dependencies:
6059
-    safe-buffer "^5.1.2"
6060
-    yallist "^3.0.0"
6061
-
6062
-minizlib@^1.2.1:
6063
-  version "1.3.3"
6064
-  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
6065
-  integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
6066
-  dependencies:
6067
-    minipass "^2.9.0"
6068
-
6069 5973
 mixin-deep@^1.2.0:
6070 5974
   version "1.3.2"
6071 5975
   resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
@@ -6074,7 +5978,7 @@ mixin-deep@^1.2.0:
6074 5978
     for-in "^1.0.2"
6075 5979
     is-extendable "^1.0.1"
6076 5980
 
6077
-mkdirp@^0.5.0, mkdirp@^0.5.1:
5981
+mkdirp@^0.5.1:
6078 5982
   version "0.5.5"
6079 5983
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
6080 5984
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -6133,15 +6037,6 @@ natural-compare@^1.4.0:
6133 6037
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
6134 6038
   integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
6135 6039
 
6136
-needle@^2.2.1:
6137
-  version "2.4.1"
6138
-  resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a"
6139
-  integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==
6140
-  dependencies:
6141
-    debug "^3.2.6"
6142
-    iconv-lite "^0.4.4"
6143
-    sax "^1.2.4"
6144
-
6145 6040
 negotiator@0.6.2:
6146 6041
   version "0.6.2"
6147 6042
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@@ -6186,22 +6081,6 @@ node-notifier@^6.0.0:
6186 6081
     shellwords "^0.1.1"
6187 6082
     which "^1.3.1"
6188 6083
 
6189
-node-pre-gyp@*:
6190
-  version "0.14.0"
6191
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83"
6192
-  integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==
6193
-  dependencies:
6194
-    detect-libc "^1.0.2"
6195
-    mkdirp "^0.5.1"
6196
-    needle "^2.2.1"
6197
-    nopt "^4.0.1"
6198
-    npm-packlist "^1.1.6"
6199
-    npmlog "^4.0.2"
6200
-    rc "^1.2.7"
6201
-    rimraf "^2.6.1"
6202
-    semver "^5.3.0"
6203
-    tar "^4.4.2"
6204
-
6205 6084
 node-releases@^1.1.53:
6206 6085
   version "1.1.53"
6207 6086
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4"
@@ -6217,14 +6096,6 @@ noop-fn@^1.0.0:
6217 6096
   resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf"
6218 6097
   integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78=
6219 6098
 
6220
-nopt@^4.0.1:
6221
-  version "4.0.3"
6222
-  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
6223
-  integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
6224
-  dependencies:
6225
-    abbrev "1"
6226
-    osenv "^0.1.4"
6227
-
6228 6099
 normalize-css-color@^1.0.2:
6229 6100
   version "1.0.2"
6230 6101
   resolved "https://registry.yarnpkg.com/normalize-css-color/-/normalize-css-color-1.0.2.tgz#02991e97cccec6623fe573afbbf0de6a1f3e9f8d"
@@ -6266,27 +6137,6 @@ normalize-url@^4.1.0:
6266 6137
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
6267 6138
   integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
6268 6139
 
6269
-npm-bundled@^1.0.1:
6270
-  version "1.1.1"
6271
-  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
6272
-  integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
6273
-  dependencies:
6274
-    npm-normalize-package-bin "^1.0.1"
6275
-
6276
-npm-normalize-package-bin@^1.0.1:
6277
-  version "1.0.1"
6278
-  resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
6279
-  integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
6280
-
6281
-npm-packlist@^1.1.6:
6282
-  version "1.4.8"
6283
-  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
6284
-  integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
6285
-  dependencies:
6286
-    ignore-walk "^3.0.1"
6287
-    npm-bundled "^1.0.1"
6288
-    npm-normalize-package-bin "^1.0.1"
6289
-
6290 6140
 npm-run-path@^2.0.0:
6291 6141
   version "2.0.2"
6292 6142
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -6301,16 +6151,6 @@ npm-run-path@^4.0.0:
6301 6151
   dependencies:
6302 6152
     path-key "^3.0.0"
6303 6153
 
6304
-npmlog@^4.0.2:
6305
-  version "4.1.2"
6306
-  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
6307
-  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
6308
-  dependencies:
6309
-    are-we-there-yet "~1.1.2"
6310
-    console-control-strings "~1.1.0"
6311
-    gauge "~2.7.3"
6312
-    set-blocking "~2.0.0"
6313
-
6314 6154
 nullthrows@^1.1.0, nullthrows@^1.1.1:
6315 6155
   version "1.1.1"
6316 6156
   resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
@@ -6488,11 +6328,6 @@ ora@^3.4.0:
6488 6328
     strip-ansi "^5.2.0"
6489 6329
     wcwidth "^1.0.1"
6490 6330
 
6491
-os-homedir@^1.0.0:
6492
-  version "1.0.2"
6493
-  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
6494
-  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
6495
-
6496 6331
 os-locale@^3.0.0:
6497 6332
   version "3.1.0"
6498 6333
   resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
@@ -6507,14 +6342,6 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
6507 6342
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
6508 6343
   integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
6509 6344
 
6510
-osenv@^0.1.4:
6511
-  version "0.1.5"
6512
-  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
6513
-  integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
6514
-  dependencies:
6515
-    os-homedir "^1.0.0"
6516
-    os-tmpdir "^1.0.0"
6517
-
6518 6345
 p-cancelable@^1.0.0:
6519 6346
   version "1.1.0"
6520 6347
   resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
@@ -6908,16 +6735,6 @@ range-parser@~1.2.1:
6908 6735
   resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
6909 6736
   integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
6910 6737
 
6911
-rc@^1.2.7:
6912
-  version "1.2.8"
6913
-  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
6914
-  integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
6915
-  dependencies:
6916
-    deep-extend "^0.6.0"
6917
-    ini "~1.3.0"
6918
-    minimist "^1.2.0"
6919
-    strip-json-comments "~2.0.1"
6920
-
6921 6738
 react-devtools-core@^4.6.0:
6922 6739
   version "4.6.0"
6923 6740
   resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.6.0.tgz#2443b3c6fac78b801702af188abc6d83d56224e6"
@@ -7043,7 +6860,7 @@ read-pkg@^5.2.0:
7043 6860
     parse-json "^5.0.0"
7044 6861
     type-fest "^0.6.0"
7045 6862
 
7046
-readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.6:
6863
+readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@~2.3.6:
7047 6864
   version "2.3.7"
7048 6865
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
7049 6866
   integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -7297,7 +7114,7 @@ rimraf@2.6.3:
7297 7114
   dependencies:
7298 7115
     glob "^7.1.3"
7299 7116
 
7300
-rimraf@^2.5.4, rimraf@^2.6.1:
7117
+rimraf@^2.5.4:
7301 7118
   version "2.7.1"
7302 7119
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
7303 7120
   integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -7394,7 +7211,7 @@ sane@^4.0.3:
7394 7211
     minimist "^1.1.1"
7395 7212
     walker "~1.0.5"
7396 7213
 
7397
-sax@^1.2.1, sax@^1.2.4:
7214
+sax@^1.2.1:
7398 7215
   version "1.2.4"
7399 7216
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
7400 7217
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -7414,7 +7231,7 @@ scheduler@0.19.1, scheduler@^0.19.1:
7414 7231
     loose-envify "^1.1.0"
7415 7232
     object-assign "^4.1.1"
7416 7233
 
7417
-"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
7234
+"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
7418 7235
   version "5.7.1"
7419 7236
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
7420 7237
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -7463,7 +7280,7 @@ serve-static@^1.13.1:
7463 7280
     parseurl "~1.3.3"
7464 7281
     send "0.17.1"
7465 7282
 
7466
-set-blocking@^2.0.0, set-blocking@~2.0.0:
7283
+set-blocking@^2.0.0:
7467 7284
   version "2.0.0"
7468 7285
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
7469 7286
   integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
@@ -7769,7 +7586,7 @@ string-width@^1.0.1:
7769 7586
     is-fullwidth-code-point "^1.0.0"
7770 7587
     strip-ansi "^3.0.0"
7771 7588
 
7772
-"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
7589
+string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1:
7773 7590
   version "2.1.1"
7774 7591
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
7775 7592
   integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -7896,11 +7713,6 @@ strip-json-comments@^3.0.1:
7896 7713
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180"
7897 7714
   integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==
7898 7715
 
7899
-strip-json-comments@~2.0.1:
7900
-  version "2.0.1"
7901
-  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
7902
-  integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
7903
-
7904 7716
 sudo-prompt@^9.0.0:
7905 7717
   version "9.2.1"
7906 7718
   resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd"
@@ -7955,19 +7767,6 @@ table@^5.2.3:
7955 7767
     slice-ansi "^2.1.0"
7956 7768
     string-width "^3.0.0"
7957 7769
 
7958
-tar@^4.4.2:
7959
-  version "4.4.13"
7960
-  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
7961
-  integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
7962
-  dependencies:
7963
-    chownr "^1.1.1"
7964
-    fs-minipass "^1.2.5"
7965
-    minipass "^2.8.6"
7966
-    minizlib "^1.2.1"
7967
-    mkdirp "^0.5.0"
7968
-    safe-buffer "^5.1.2"
7969
-    yallist "^3.0.3"
7970
-
7971 7770
 temp@0.8.3:
7972 7771
   version "0.8.3"
7973 7772
   resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
@@ -8493,13 +8292,6 @@ which@^2.0.1, which@^2.0.2:
8493 8292
   dependencies:
8494 8293
     isexe "^2.0.0"
8495 8294
 
8496
-wide-align@^1.1.0:
8497
-  version "1.1.3"
8498
-  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
8499
-  integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
8500
-  dependencies:
8501
-    string-width "^1.0.2 || 2"
8502
-
8503 8295
 word-wrap@~1.2.3:
8504 8296
   version "1.2.3"
8505 8297
   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
@@ -8642,11 +8434,6 @@ yallist@^2.1.2:
8642 8434
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
8643 8435
   integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
8644 8436
 
8645
-yallist@^3.0.0, yallist@^3.0.3:
8646
-  version "3.1.1"
8647
-  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
8648
-  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
8649
-
8650 8437
 yaml@^1.7.2:
8651 8438
   version "1.9.2"
8652 8439
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed"