of(
90 | "initialWindowMetrics",
91 | getInitialWindowMetrics());
92 |
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaUtils.java:
--------------------------------------------------------------------------------
1 | package com.th3rdwave.safeareacontext;
2 |
3 | import android.graphics.Rect;
4 | import android.os.Build;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.view.WindowInsets;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | /* package */ class SafeAreaUtils {
12 |
13 | private static @Nullable EdgeInsets getRootWindowInsetsCompat(View rootView) {
14 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
15 | WindowInsets insets = rootView.getRootWindowInsets();
16 | if (insets == null) {
17 | return null;
18 | }
19 | return new EdgeInsets(
20 | insets.getSystemWindowInsetTop(),
21 | insets.getSystemWindowInsetRight(),
22 | // System insets are more reliable to account for notches but the
23 | // system inset for bottom includes the soft keyboard which we don't
24 | // want to be consistent with iOS. Using the min value makes sure we
25 | // never get the keyboard offset while still working with devices that
26 | // hide the navigation bar.
27 | Math.min(insets.getSystemWindowInsetBottom(), insets.getStableInsetBottom()),
28 | insets.getSystemWindowInsetLeft());
29 | } else {
30 | Rect visibleRect = new Rect();
31 | rootView.getWindowVisibleDisplayFrame(visibleRect);
32 | return new EdgeInsets(
33 | visibleRect.top,
34 | rootView.getWidth() - visibleRect.right,
35 | rootView.getHeight() - visibleRect.bottom,
36 | visibleRect.left);
37 | }
38 | }
39 |
40 | static @Nullable EdgeInsets getSafeAreaInsets(View view) {
41 | // The view has not been layout yet.
42 | if (view.getHeight() == 0) {
43 | return null;
44 | }
45 | View rootView = view.getRootView();
46 | EdgeInsets windowInsets = getRootWindowInsetsCompat(rootView);
47 | if (windowInsets == null) {
48 | return null;
49 | }
50 |
51 | // Calculate the part of the view that overlaps with window insets.
52 | float windowWidth = rootView.getWidth();
53 | float windowHeight = rootView.getHeight();
54 | Rect visibleRect = new Rect();
55 | view.getGlobalVisibleRect(visibleRect);
56 |
57 | windowInsets.top = Math.max(windowInsets.top - visibleRect.top, 0);
58 | windowInsets.left = Math.max(windowInsets.left - visibleRect.left, 0);
59 | windowInsets.bottom = Math.max(Math.min(visibleRect.top + view.getHeight() - windowHeight, 0) + windowInsets.bottom, 0);
60 | windowInsets.right = Math.max(Math.min(visibleRect.left + view.getWidth() - windowWidth, 0) + windowInsets.right, 0);
61 | return windowInsets;
62 | }
63 |
64 | static @Nullable com.th3rdwave.safeareacontext.Rect getFrame(ViewGroup rootView, View view) {
65 | // This can happen while the view gets unmounted.
66 | if (view.getParent() == null) {
67 | return null;
68 | }
69 | Rect offset = new Rect();
70 | view.getDrawingRect(offset);
71 | try {
72 | rootView.offsetDescendantRectToMyCoords(view, offset);
73 | } catch (IllegalArgumentException ex) {
74 | // This can throw if the view is not a descendant of rootView. This should not
75 | // happen but avoid potential crashes.
76 | ex.printStackTrace();
77 | return null;
78 | }
79 |
80 | return new com.th3rdwave.safeareacontext.Rect(offset.left, offset.top, view.getWidth(), view.getHeight());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/safeareaviewexample/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.safeareaviewexample;
8 | import android.content.Context;
9 | import com.facebook.flipper.android.AndroidFlipperClient;
10 | import com.facebook.flipper.android.utils.FlipperUtils;
11 | import com.facebook.flipper.core.FlipperClient;
12 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
13 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
14 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
15 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
16 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
17 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
18 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
19 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21 | import com.facebook.react.ReactInstanceManager;
22 | import com.facebook.react.bridge.ReactContext;
23 | import com.facebook.react.modules.network.NetworkingModule;
24 | import okhttp3.OkHttpClient;
25 | public class ReactNativeFlipper {
26 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
27 | if (FlipperUtils.shouldEnableFlipper(context)) {
28 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
29 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
30 | client.addPlugin(new ReactFlipperPlugin());
31 | client.addPlugin(new DatabasesFlipperPlugin(context));
32 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
33 | client.addPlugin(CrashReporterPlugin.getInstance());
34 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
35 | NetworkingModule.setCustomClientBuilder(
36 | new NetworkingModule.CustomClientBuilder() {
37 | @Override
38 | public void apply(OkHttpClient.Builder builder) {
39 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
40 | }
41 | });
42 | client.addPlugin(networkFlipperPlugin);
43 | client.start();
44 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
45 | // Hence we run if after all native modules have been initialized
46 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
47 | if (reactContext == null) {
48 | reactInstanceManager.addReactInstanceEventListener(
49 | new ReactInstanceManager.ReactInstanceEventListener() {
50 | @Override
51 | public void onReactContextInitialized(ReactContext reactContext) {
52 | reactInstanceManager.removeReactInstanceEventListener(this);
53 | reactContext.runOnNativeModulesQueueThread(
54 | new Runnable() {
55 | @Override
56 | public void run() {
57 | client.addPlugin(new FrescoFlipperPlugin());
58 | }
59 | });
60 | }
61 | });
62 | } else {
63 | client.addPlugin(new FrescoFlipperPlugin());
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/example/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto init
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto init
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :init
68 | @rem Get command-line arguments, handling Windows variants
69 |
70 | if not "%OS%" == "Windows_NT" goto win9xME_args
71 |
72 | :win9xME_args
73 | @rem Slurp the command line arguments.
74 | set CMD_LINE_ARGS=
75 | set _SKIP=2
76 |
77 | :win9xME_args_slurp
78 | if "x%~1" == "x" goto execute
79 |
80 | set CMD_LINE_ARGS=%*
81 |
82 | :execute
83 | @rem Setup the command line
84 |
85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
86 |
87 | @rem Execute Gradle
88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
89 |
90 | :end
91 | @rem End local scope for the variables with windows NT shell
92 | if "%ERRORLEVEL%"=="0" goto mainEnd
93 |
94 | :fail
95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
96 | rem the _cmd.exe /c_ return code!
97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
98 | exit /b 1
99 |
100 | :mainEnd
101 | if "%OS%"=="Windows_NT" endlocal
102 |
103 | :omega
104 |
--------------------------------------------------------------------------------
/ios/SafeAreaView/RNCSafeAreaView.m:
--------------------------------------------------------------------------------
1 | #import "RNCSafeAreaView.h"
2 |
3 | #import
4 | #import
5 |
6 | #import "RNCSafeAreaViewLocalData.h"
7 | #import "RNCSafeAreaViewMode.h"
8 | #import "RNCSafeAreaViewEdges.h"
9 | #import "RCTView+SafeAreaCompat.h"
10 | #import "RNCSafeAreaProvider.h"
11 |
12 | @implementation RNCSafeAreaView {
13 | __weak RCTBridge *_bridge;
14 | UIEdgeInsets _currentSafeAreaInsets;
15 | RNCSafeAreaViewMode _mode;
16 | RNCSafeAreaViewEdges _edges;
17 | __weak UIView * _Nullable _providerView;
18 | }
19 |
20 | - (instancetype)initWithBridge:(RCTBridge *)bridge
21 | {
22 | if (self = [super initWithFrame:CGRectZero]) {
23 | _bridge = bridge;
24 | // Defaults
25 | _mode = RNCSafeAreaViewModePadding;
26 | _edges = RNCSafeAreaViewEdgesAll;
27 | }
28 |
29 | return self;
30 | }
31 |
32 | RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)decoder)
33 | RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
34 |
35 | - (NSString *)description
36 | {
37 | NSString *superDescription = [super description];
38 |
39 | // Cutting the last `>` character.
40 | if (superDescription.length > 0 && [superDescription characterAtIndex:superDescription.length - 1] == '>') {
41 | superDescription = [superDescription substringToIndex:superDescription.length - 1];
42 | }
43 |
44 | return [NSString stringWithFormat:@"%@; RNCSafeAreaInsets = %@; appliedRNCSafeAreaInsets = %@>",
45 | superDescription,
46 | NSStringFromUIEdgeInsets([_providerView safeAreaInsetsOrEmulate]),
47 | NSStringFromUIEdgeInsets(_currentSafeAreaInsets)];
48 | }
49 |
50 | - (void)safeAreaInsetsDidChange
51 | {
52 | [super safeAreaInsetsDidChange];
53 | [self invalidateSafeAreaInsets];
54 | }
55 |
56 | - (void)layoutSubviews
57 | {
58 | [super layoutSubviews];
59 |
60 | if (!self.nativeSafeAreaSupport) {
61 | [self invalidateSafeAreaInsets];
62 | }
63 | }
64 |
65 | - (void)didMoveToWindow
66 | {
67 | _providerView = [self findNearestProvider];
68 | [self invalidateSafeAreaInsets];
69 | }
70 |
71 | - (void)invalidateSafeAreaInsets
72 | {
73 | if (_providerView == nil) {
74 | return;
75 | }
76 | UIEdgeInsets safeAreaInsets = [_providerView safeAreaInsetsOrEmulate];
77 |
78 | if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale())) {
79 | return;
80 | }
81 |
82 | _currentSafeAreaInsets = safeAreaInsets;
83 | [self updateLocalData];
84 | }
85 |
86 | - (UIView *)findNearestProvider
87 | {
88 | UIView *current = self.reactSuperview;
89 | while (current != nil) {
90 | if ([current isKindOfClass:RNCSafeAreaProvider.class] ) {
91 | return current;
92 | }
93 | current = current.reactSuperview;
94 | }
95 | return self;
96 | }
97 |
98 | - (void)updateLocalData
99 | {
100 | if (_providerView == nil) {
101 | return;
102 | }
103 | RNCSafeAreaViewLocalData *localData = [[RNCSafeAreaViewLocalData alloc] initWithInsets:_currentSafeAreaInsets
104 | mode:_mode
105 | edges:_edges];
106 | [_bridge.uiManager setLocalData:localData forView:self];
107 | }
108 |
109 | - (void)setMode:(RNCSafeAreaViewMode)mode
110 | {
111 | _mode = mode;
112 | [self updateLocalData];
113 | }
114 |
115 | - (void)setEdges:(RNCSafeAreaViewEdges)edges
116 | {
117 | _edges = edges;
118 | [self updateLocalData];
119 | }
120 |
121 | @end
122 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler';
2 | import * as React from 'react';
3 | import { DevSettings, View, Text, StatusBar } from 'react-native';
4 | import { enableScreens } from 'react-native-screens';
5 | import AsyncStorage from '@react-native-community/async-storage';
6 | import HooksExample from './HooksExample';
7 | import SafeAreaViewExample from './SafeAreaViewExample';
8 | import ReactNavigation4Example from './ReactNavigation4Example';
9 | import ReactNavigation5Example from './ReactNavigation5Example';
10 | import NativeStackExample from './NativeStackExample';
11 | import ReactNativeSafeAreaView from './ReactNativeSafeAreaView';
12 |
13 | enableScreens();
14 |
15 | const STORAGE_KEY = 'rnsac-current-example';
16 |
17 | export default function App() {
18 | const [currentExample, setCurrentExample] = React.useState(
19 | null,
20 | );
21 | const [statusBarHidden, setStatusBarHidden] = React.useState(false);
22 |
23 | React.useEffect(() => {
24 | async function loadCurrentExample() {
25 | const example = await AsyncStorage.getItem(STORAGE_KEY);
26 | setCurrentExample(example ?? null);
27 | }
28 | loadCurrentExample();
29 | }, []);
30 |
31 | React.useEffect(() => {
32 | async function saveCurrentExample() {
33 | if (currentExample != null) {
34 | await AsyncStorage.setItem(STORAGE_KEY, currentExample);
35 | }
36 | }
37 | saveCurrentExample();
38 | }, [currentExample]);
39 |
40 | React.useEffect(() => {
41 | DevSettings.addMenuItem('Toggle Status Bar', () => {
42 | setStatusBarHidden((s) => !s);
43 | });
44 | DevSettings.addMenuItem('Show SafeAreaView Example', () => {
45 | setCurrentExample('safe-area-view');
46 | });
47 | DevSettings.addMenuItem('Show Hooks Example', () => {
48 | setCurrentExample('hooks');
49 | });
50 | DevSettings.addMenuItem('Show React Navigation 4 Example', () => {
51 | setCurrentExample('react-navigation-4');
52 | });
53 | DevSettings.addMenuItem('Show React Navigation 5 Example', () => {
54 | setCurrentExample('react-navigation-5');
55 | });
56 | DevSettings.addMenuItem('Show Native Stack Example', () => {
57 | setCurrentExample('native-stack');
58 | });
59 | DevSettings.addMenuItem('Show React Native Safe Area View Example', () => {
60 | setCurrentExample('react-native-safe-area-view');
61 | });
62 | }, []);
63 |
64 | let content: React.ReactElement;
65 | switch (currentExample) {
66 | case 'safe-area-view':
67 | content = ;
68 | break;
69 | case 'hooks':
70 | content = ;
71 | break;
72 | case 'react-navigation-4':
73 | content = ;
74 | break;
75 | case 'react-navigation-5':
76 | content = ;
77 | break;
78 | case 'native-stack':
79 | content = ;
80 |
81 | break;
82 | case 'react-native-safe-area-view':
83 | content = ;
84 | break;
85 | default:
86 | content = (
87 |
95 |
96 | Open the dev menu to choose an example
97 |
98 |
99 | );
100 | break;
101 | }
102 |
103 | return (
104 | <>
105 |
109 | {content}
110 | >
111 | );
112 | }
113 |
--------------------------------------------------------------------------------
/example/ios/SafeAreaViewExample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-safe-area-context",
3 | "version": "3.3.2",
4 | "description": "A flexible way to handle safe area, also works on Android and web.",
5 | "main": "lib/commonjs/index.js",
6 | "module": "lib/module/index.js",
7 | "react-native": "src/index.tsx",
8 | "types": "lib/typescript/src/index.d.ts",
9 | "sideEffects": false,
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "jest",
16 | "*.podspec",
17 | "react-native.config.js",
18 | "!**/__tests__",
19 | "!example"
20 | ],
21 | "author": "Janic Duplessis ",
22 | "contributors": [
23 | "Evan Bacon (https://github.com/evanbacon)"
24 | ],
25 | "homepage": "https://github.com/th3rdwave/react-native-safe-area-context#readme",
26 | "license": "MIT",
27 | "scripts": {
28 | "start": "react-native start",
29 | "test": "yarn validate:prettier && yarn validate:eslint && yarn validate:typescript && yarn validate:jest",
30 | "validate:eslint": "eslint \"src/**/*.{js,ts,tsx}\" \"example/**/*.{js,ts,tsx}\"",
31 | "validate:typescript": "tsc --project ./ --noEmit",
32 | "validate:prettier": "prettier \"src/**/*.{js,ts,tsx}\" \"example/**/*.{js,ts,tsx}\" --check",
33 | "validate:jest": "jest",
34 | "prepare": "bob build",
35 | "example:ios": "cd ./example && react-native run-ios",
36 | "example:android": "cd ./example && react-native run-android --no-jetifier",
37 | "example:expo": "expo start --config example/app.json"
38 | },
39 | "keywords": [
40 | "react-native",
41 | "react native",
42 | "react-native-web",
43 | "expo-web",
44 | "safe area",
45 | "view"
46 | ],
47 | "peerDependencies": {
48 | "react": "*",
49 | "react-native": "*"
50 | },
51 | "dependencies": {},
52 | "devDependencies": {
53 | "@react-native-community/async-storage": "^1.11.0",
54 | "react-native-builder-bob": "^0.18.1",
55 | "@react-native-community/eslint-config": "^3.0.0",
56 | "@react-native-community/masked-view": "^0.1.10",
57 | "@react-navigation/native": "^5.5.1",
58 | "@react-navigation/stack": "^5.5.1",
59 | "@react-navigation/bottom-tabs": "^5.5.2",
60 | "@types/jest": "^27.0.1",
61 | "@types/react": "^16.9.35",
62 | "@types/react-native": "^0.62.13",
63 | "@types/react-dom": "^16.9.8",
64 | "@types/react-test-renderer": "^16.9.2",
65 | "@typescript-eslint/eslint-plugin": "^4.30.0",
66 | "@typescript-eslint/parser": "^4.30.0",
67 | "babel-plugin-module-resolver": "^4.1.0",
68 | "eslint": "7.32.0",
69 | "eslint-config-prettier": "^8.3.0",
70 | "eslint-plugin-prettier": "4.0.0",
71 | "expo": "^37.0.12",
72 | "expo-cli": "^3.21.5",
73 | "jest": "^27.1.0",
74 | "metro-react-native-babel-preset": "^0.59.0",
75 | "prettier": "^2.3.2",
76 | "react": "^16.13.1",
77 | "react-dom": "^16.13.1",
78 | "react-native": "^0.63.0-rc.0",
79 | "react-native-web": "^0.12.3",
80 | "react-navigation": "^4.3.9",
81 | "react-navigation-stack": "^2.7.0",
82 | "react-navigation-tabs": "^2.8.13",
83 | "react-native-safe-area-view": "1.1.1",
84 | "react-native-gesture-handler": "^1.6.1",
85 | "react-native-screens": "^2.8.0",
86 | "react-native-reanimated": "^1.9.0",
87 | "react-test-renderer": "^16.13.1",
88 | "typescript": "^4.4.2"
89 | },
90 | "repository": {
91 | "type": "git",
92 | "url": "https://github.com/th3rdwave/react-native-safe-area-context.git"
93 | },
94 | "jest": {
95 | "preset": "react-native",
96 | "testEnvironment": "node",
97 | "clearMocks": true,
98 | "modulePathIgnorePatterns": [
99 | "/lib/"
100 | ]
101 | },
102 | "react-native-builder-bob": {
103 | "source": "src",
104 | "output": "lib",
105 | "targets": [
106 | "commonjs",
107 | "module",
108 | "typescript"
109 | ]
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/SafeAreaContext-test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`SafeAreaContext does not render child until inset values are received 1`] = `
4 |
15 | `;
16 |
17 | exports[`SafeAreaContext renders 1`] = `
18 |
29 | `;
30 |
31 | exports[`SafeAreaContext renders child when inset values are received 1`] = `
32 |
43 | `;
44 |
45 | exports[`SafeAreaContext renders child when inset values are received 2`] = `
46 |
57 |
71 |
72 | `;
73 |
74 | exports[`SafeAreaContext supports setting initial insets 1`] = `
75 |
86 |
100 |
101 | `;
102 |
103 | exports[`SafeAreaContext uses inner insets 1`] = `
104 |
115 |
126 |
140 |
141 |
142 | `;
143 |
144 | exports[`SafeAreaContext uses parent insets when available 1`] = `
145 |
156 |
167 |
181 |
182 |
183 | `;
184 |
--------------------------------------------------------------------------------
/src/__tests__/SafeAreaContext-test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { View } from 'react-native';
3 | import * as ReactTestRenderer from 'react-test-renderer';
4 | import NativeSafeAreaView from '../NativeSafeAreaProvider';
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 |
37 | );
38 | };
39 |
40 | describe('SafeAreaContext', () => {
41 | it('renders', () => {
42 | const component = ReactTestRenderer.create();
43 | expect(component).toMatchSnapshot();
44 | });
45 |
46 | it('does not render child until inset values are received', () => {
47 | const component = ReactTestRenderer.create(
48 |
49 |
50 | ,
51 | );
52 | expect(component).toMatchSnapshot();
53 | });
54 |
55 | it('renders child when inset values are received', () => {
56 | const component = ReactTestRenderer.create(
57 |
58 |
59 | ,
60 | );
61 | expect(component).toMatchSnapshot();
62 | const { onInsetsChange } =
63 | component.root.findByType(NativeSafeAreaView).props;
64 | ReactTestRenderer.act(() => {
65 | onInsetsChange({
66 | nativeEvent: TEST_METRICS_1,
67 | });
68 | });
69 | expect(component).toMatchSnapshot();
70 | });
71 |
72 | it('supports setting initial insets', () => {
73 | const component = ReactTestRenderer.create(
74 |
75 |
76 | ,
77 | );
78 | expect(component).toMatchSnapshot();
79 | });
80 |
81 | it('uses parent insets when available', () => {
82 | const component = ReactTestRenderer.create(
83 |
84 |
85 |
86 |
87 | ,
88 | );
89 | expect(component).toMatchSnapshot();
90 | });
91 |
92 | it('uses inner insets', () => {
93 | const component = ReactTestRenderer.create(
94 |
95 |
96 |
97 |
98 | ,
99 | );
100 | expect(component).toMatchSnapshot();
101 | });
102 |
103 | it('throws when no provider is rendered', () => {
104 | // Silence the React error boundary warning; we expect an uncaught error.
105 | const consoleErrorMock = jest
106 | .spyOn(console, 'error')
107 | .mockImplementation((message) => {
108 | if (message.startsWith('The above error occured in the ')) {
109 | return;
110 | }
111 | });
112 | expect(() => {
113 | ReactTestRenderer.create();
114 | }).toThrow(
115 | 'No safe area insets value available. Make sure you are rendering `` at the top of your app.',
116 | );
117 |
118 | consoleErrorMock.mockRestore();
119 | });
120 | });
121 |
--------------------------------------------------------------------------------
/src/NativeSafeAreaProvider.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-env browser */
2 |
3 | import * as React from 'react';
4 | import { View } from 'react-native';
5 | import { NativeSafeAreaProviderProps } from './SafeArea.types';
6 |
7 | /**
8 | * TODO:
9 | * Currently insets and frame are based on the window and are not
10 | * relative to the provider view. This is inconsistent with iOS and Android.
11 | * However in most cases if the provider view covers the screen this is not
12 | * an issue.
13 | */
14 |
15 | const CSSTransitions: Record = {
16 | WebkitTransition: 'webkitTransitionEnd',
17 | Transition: 'transitionEnd',
18 | MozTransition: 'transitionend',
19 | MSTransition: 'msTransitionEnd',
20 | OTransition: 'oTransitionEnd',
21 | };
22 |
23 | export default function NativeSafeAreaView({
24 | children,
25 | style,
26 | onInsetsChange,
27 | }: NativeSafeAreaProviderProps) {
28 | React.useEffect(() => {
29 | // Skip for SSR.
30 | if (typeof document === 'undefined') {
31 | return;
32 | }
33 |
34 | const element = createContextElement();
35 | document.body.appendChild(element);
36 | const onEnd = () => {
37 | const { paddingTop, paddingBottom, paddingLeft, paddingRight } =
38 | window.getComputedStyle(element);
39 |
40 | const insets = {
41 | top: paddingTop ? parseInt(paddingTop, 10) : 0,
42 | bottom: paddingBottom ? parseInt(paddingBottom, 10) : 0,
43 | left: paddingLeft ? parseInt(paddingLeft, 10) : 0,
44 | right: paddingRight ? parseInt(paddingRight, 10) : 0,
45 | };
46 | const frame = {
47 | x: 0,
48 | y: 0,
49 | width: document.documentElement.offsetWidth,
50 | height: document.documentElement.offsetHeight,
51 | };
52 | // @ts-ignore: missing properties
53 | onInsetsChange({ nativeEvent: { insets, frame } });
54 | };
55 | element.addEventListener(getSupportedTransitionEvent(), onEnd);
56 | onEnd();
57 | return () => {
58 | document.body.removeChild(element);
59 | element.removeEventListener(getSupportedTransitionEvent(), onEnd);
60 | };
61 | }, [onInsetsChange]);
62 |
63 | return {children};
64 | }
65 |
66 | let _supportedTransitionEvent: string | null = null;
67 | function getSupportedTransitionEvent(): string {
68 | if (_supportedTransitionEvent !== null) {
69 | return _supportedTransitionEvent;
70 | }
71 | const element = document.createElement('invalidtype');
72 |
73 | _supportedTransitionEvent = CSSTransitions.Transition;
74 | for (const key in CSSTransitions) {
75 | if (element.style[key as keyof CSSStyleDeclaration] !== undefined) {
76 | _supportedTransitionEvent = CSSTransitions[key];
77 | break;
78 | }
79 | }
80 | return _supportedTransitionEvent;
81 | }
82 |
83 | type CssEnv = 'constant' | 'env';
84 |
85 | let _supportedEnv: CssEnv | null = null;
86 | function getSupportedEnv(): CssEnv {
87 | if (_supportedEnv !== null) {
88 | return _supportedEnv;
89 | }
90 | const { CSS } = window;
91 | if (
92 | CSS &&
93 | CSS.supports &&
94 | CSS.supports('top: constant(safe-area-inset-top)')
95 | ) {
96 | _supportedEnv = 'constant';
97 | } else {
98 | _supportedEnv = 'env';
99 | }
100 | return _supportedEnv;
101 | }
102 |
103 | function getInset(side: string): string {
104 | return `${getSupportedEnv()}(safe-area-inset-${side})`;
105 | }
106 |
107 | function createContextElement(): HTMLElement {
108 | const element = document.createElement('div');
109 | const { style } = element;
110 | style.position = 'fixed';
111 | style.left = '0';
112 | style.top = '0';
113 | style.width = '0';
114 | style.height = '0';
115 | style.zIndex = '-1';
116 | style.overflow = 'hidden';
117 | style.visibility = 'hidden';
118 | // Bacon: Anything faster than this and the callback will be invoked too early with the wrong insets
119 | style.transitionDuration = '0.05s';
120 | style.transitionProperty = 'padding';
121 | style.transitionDelay = '0s';
122 | style.paddingTop = getInset('top');
123 | style.paddingBottom = getInset('bottom');
124 | style.paddingLeft = getInset('left');
125 | style.paddingRight = getInset('right');
126 | return element;
127 | }
128 |
--------------------------------------------------------------------------------
/src/SafeAreaContext.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Dimensions, StyleProp, StyleSheet, ViewStyle } from 'react-native';
3 | import NativeSafeAreaProvider from './NativeSafeAreaProvider';
4 | import { EdgeInsets, InsetChangedEvent, Metrics, Rect } from './SafeArea.types';
5 |
6 | export const SafeAreaInsetsContext = React.createContext(
7 | null,
8 | );
9 | SafeAreaInsetsContext.displayName = 'SafeAreaInsetsContext';
10 |
11 | export const SafeAreaFrameContext = React.createContext(null);
12 | SafeAreaFrameContext.displayName = 'SafeAreaFrameContext';
13 |
14 | export interface SafeAreaViewProps {
15 | children?: React.ReactNode;
16 | initialMetrics?: Metrics | null;
17 | /**
18 | * @deprecated
19 | */
20 | initialSafeAreaInsets?: EdgeInsets | null;
21 | style?: StyleProp;
22 | }
23 |
24 | export function SafeAreaProvider({
25 | children,
26 | initialMetrics,
27 | initialSafeAreaInsets,
28 | style,
29 | }: SafeAreaViewProps) {
30 | const parentInsets = useParentSafeAreaInsets();
31 | const parentFrame = useParentSafeAreaFrame();
32 | const [insets, setInsets] = React.useState(
33 | initialMetrics?.insets ?? initialSafeAreaInsets ?? parentInsets ?? null,
34 | );
35 | const [frame, setFrame] = React.useState(
36 | initialMetrics?.frame ??
37 | parentFrame ?? {
38 | // Backwards compat so we render anyway if we don't have frame.
39 | x: 0,
40 | y: 0,
41 | width: Dimensions.get('window').width,
42 | height: Dimensions.get('window').height,
43 | },
44 | );
45 | const onInsetsChange = React.useCallback(
46 | (event: InsetChangedEvent) => {
47 | const {
48 | nativeEvent: { frame: nextFrame, insets: nextInsets },
49 | } = event;
50 |
51 | if (
52 | // Backwards compat with old native code that won't send frame.
53 | nextFrame &&
54 | (nextFrame.height !== frame.height ||
55 | nextFrame.width !== frame.width ||
56 | nextFrame.x !== frame.x ||
57 | nextFrame.y !== frame.y)
58 | ) {
59 | setFrame(nextFrame);
60 | }
61 |
62 | if (
63 | !insets ||
64 | nextInsets.bottom !== insets.bottom ||
65 | nextInsets.left !== insets.left ||
66 | nextInsets.right !== insets.right ||
67 | nextInsets.top !== insets.top
68 | ) {
69 | setInsets(nextInsets);
70 | }
71 | },
72 | [frame, insets],
73 | );
74 |
75 | return (
76 |
80 | {insets != null ? (
81 |
82 |
83 | {children}
84 |
85 |
86 | ) : null}
87 |
88 | );
89 | }
90 |
91 | const styles = StyleSheet.create({
92 | fill: { flex: 1 },
93 | });
94 |
95 | function useParentSafeAreaInsets(): EdgeInsets | null {
96 | return React.useContext(SafeAreaInsetsContext);
97 | }
98 |
99 | function useParentSafeAreaFrame(): Rect | null {
100 | return React.useContext(SafeAreaFrameContext);
101 | }
102 |
103 | export function useSafeAreaInsets(): EdgeInsets {
104 | const safeArea = React.useContext(SafeAreaInsetsContext);
105 | if (safeArea == null) {
106 | throw new Error(
107 | 'No safe area insets value available. Make sure you are rendering `` at the top of your app.',
108 | );
109 | }
110 | return safeArea;
111 | }
112 |
113 | export function useSafeAreaFrame(): Rect {
114 | const frame = React.useContext(SafeAreaFrameContext);
115 | if (frame == null) {
116 | throw new Error(
117 | 'No safe area frame value available. Make sure you are rendering `` at the top of your app.',
118 | );
119 | }
120 | return frame;
121 | }
122 |
123 | export function withSafeAreaInsets(
124 | WrappedComponent: React.ComponentType,
125 | ) {
126 | return React.forwardRef((props: T, ref: React.Ref) => (
127 |
128 | {(insets) => }
129 |
130 | ));
131 | }
132 |
133 | /**
134 | * @deprecated
135 | */
136 | export function useSafeArea(): EdgeInsets {
137 | return useSafeAreaInsets();
138 | }
139 |
140 | /**
141 | * @deprecated
142 | */
143 | export const SafeAreaConsumer = SafeAreaInsetsContext.Consumer;
144 |
145 | /**
146 | * @deprecated
147 | */
148 | export const SafeAreaContext = SafeAreaInsetsContext;
149 |
--------------------------------------------------------------------------------
/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaView.java:
--------------------------------------------------------------------------------
1 | package com.th3rdwave.safeareacontext;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.ContextWrapper;
6 | import android.util.Log;
7 | import android.view.View;
8 | import android.view.ViewParent;
9 | import android.view.ViewTreeObserver;
10 |
11 | import com.facebook.react.bridge.ReactContext;
12 | import com.facebook.react.uimanager.UIManagerModule;
13 | import com.facebook.react.views.view.ReactViewGroup;
14 |
15 | import java.util.EnumSet;
16 | import java.util.concurrent.atomic.AtomicBoolean;
17 |
18 | import androidx.annotation.Nullable;
19 |
20 | @SuppressLint("ViewConstructor")
21 | public class SafeAreaView extends ReactViewGroup implements ViewTreeObserver.OnPreDrawListener {
22 | private SafeAreaViewMode mMode = SafeAreaViewMode.PADDING;
23 | private @Nullable EdgeInsets mInsets;
24 | private @Nullable EnumSet mEdges;
25 | private @Nullable View mProviderView;
26 |
27 | public SafeAreaView(Context context) {
28 | super(context);
29 | }
30 |
31 | /**
32 | * UIManagerHelper.getReactContext only exists in RN 0.63+ so vendor it here for a while.
33 | */
34 | private static ReactContext getReactContext(View view) {
35 | Context context = view.getContext();
36 | if (!(context instanceof ReactContext) && context instanceof ContextWrapper) {
37 | context = ((ContextWrapper) context).getBaseContext();
38 | }
39 | return (ReactContext) context;
40 | }
41 |
42 | private void updateInsets() {
43 | if (mInsets != null) {
44 | EnumSet edges = mEdges != null
45 | ? mEdges
46 | : EnumSet.allOf(SafeAreaViewEdges.class);
47 |
48 | SafeAreaViewLocalData localData = new SafeAreaViewLocalData(mInsets, mMode, edges);
49 |
50 | ReactContext reactContext = getReactContext(this);
51 | UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
52 | if (uiManager != null) {
53 | uiManager.setViewLocalData(getId(), localData);
54 | waitForReactLayout();
55 | }
56 | }
57 | }
58 |
59 | private static final long MAX_WAIT_TIME_NANO = 500000000L; // 500ms
60 |
61 | private void waitForReactLayout() {
62 | // Block the main thread until the native module thread is finished with
63 | // its current tasks. To do this we use the done boolean as a lock and enqueue
64 | // a task on the native modules thread. When the task runs we can unblock the
65 | // main thread. This should be safe as long as the native modules thread
66 | // does not block waiting on the main thread.
67 | final AtomicBoolean done = new AtomicBoolean(false);
68 | final long startTime = System.nanoTime();
69 | long waitTime = 0L;
70 | getReactContext(this).runOnNativeModulesQueueThread(new Runnable() {
71 | @Override
72 | public void run() {
73 | synchronized (done) {
74 | if (done.compareAndSet(false, true)) {
75 | done.notify();
76 | }
77 | }
78 | }
79 | });
80 | synchronized (done) {
81 | while (!done.get() && waitTime < MAX_WAIT_TIME_NANO) {
82 | try {
83 | done.wait(MAX_WAIT_TIME_NANO / 1000000L);
84 | } catch (InterruptedException e) {
85 | // In case of an interrupt just give up waiting.
86 | done.set(true);
87 | }
88 | waitTime += System.nanoTime() - startTime;
89 | }
90 | // Timed out waiting.
91 | if (waitTime >= MAX_WAIT_TIME_NANO) {
92 | Log.w("SafeAreaView", "Timed out waiting for layout.");
93 | }
94 | }
95 | }
96 |
97 | public void setMode(SafeAreaViewMode mode) {
98 | mMode = mode;
99 | updateInsets();
100 | }
101 |
102 | public void setEdges(EnumSet edges) {
103 | mEdges = edges;
104 | updateInsets();
105 | }
106 |
107 | private boolean maybeUpdateInsets() {
108 | if (mProviderView == null) {
109 | return false;
110 | }
111 | EdgeInsets edgeInsets = SafeAreaUtils.getSafeAreaInsets(mProviderView);
112 | if (edgeInsets != null && (mInsets == null || !mInsets.equalsToEdgeInsets(edgeInsets))) {
113 | mInsets = edgeInsets;
114 | updateInsets();
115 | return true;
116 | }
117 | return false;
118 | }
119 |
120 | private View findProvider() {
121 | ViewParent current = getParent();
122 | while (current != null) {
123 | if (current instanceof SafeAreaProvider) {
124 | return (View) current;
125 | }
126 | current = current.getParent();
127 | }
128 | return this;
129 | }
130 |
131 | @Override
132 | protected void onAttachedToWindow() {
133 | super.onAttachedToWindow();
134 |
135 | mProviderView = findProvider();
136 |
137 | mProviderView.getViewTreeObserver().addOnPreDrawListener(this);
138 | maybeUpdateInsets();
139 | }
140 |
141 | @Override
142 | protected void onDetachedFromWindow() {
143 | super.onDetachedFromWindow();
144 |
145 | if (mProviderView != null) {
146 | mProviderView.getViewTreeObserver().removeOnPreDrawListener(this);
147 | }
148 | mProviderView = null;
149 | }
150 |
151 | @Override
152 | public boolean onPreDraw() {
153 | boolean didUpdate = maybeUpdateInsets();
154 | if (didUpdate) {
155 | requestLayout();
156 | }
157 | return !didUpdate;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/example/ios/SafeAreaViewExample.xcodeproj/xcshareddata/xcschemes/SafeAreaViewExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
65 |
66 |
67 |
68 |
70 |
76 |
77 |
78 |
79 |
80 |
90 |
92 |
98 |
99 |
100 |
101 |
107 |
109 |
115 |
116 |
117 |
118 |
120 |
121 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/example/src/SafeAreaViewExample.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {
3 | View,
4 | Text,
5 | ScrollView,
6 | Switch,
7 | TouchableOpacity,
8 | StyleSheet,
9 | StatusBar,
10 | Platform,
11 | } from 'react-native';
12 | import {
13 | SafeAreaProvider,
14 | SafeAreaView,
15 | Edge,
16 | } from 'react-native-safe-area-context';
17 |
18 | export default function ReactNativeSafeAreaView() {
19 | const [mode, setMode] = React.useState<'padding' | 'margin'>('padding');
20 | const [additionalMargin, setAdditionalMargin] = React.useState(false);
21 | const [additionalPadding, setAdditionalPadding] = React.useState(false);
22 | const [top, setTop] = React.useState(true);
23 | const [right, setRight] = React.useState(true);
24 | const [bottom, setBottom] = React.useState(true);
25 | const [left, setLeft] = React.useState(true);
26 |
27 | const edges: Edge[] = [];
28 | if (top) {
29 | edges.push('top');
30 | }
31 | if (right) {
32 | edges.push('right');
33 | }
34 | if (bottom) {
35 | edges.push('bottom');
36 | }
37 | if (left) {
38 | edges.push('left');
39 | }
40 |
41 | const modeTint = mode === 'padding' ? paddingColor : marginColor;
42 | const edgeSwitchBaseProps = Platform.select({
43 | ios: {
44 | trackColor: { true: modeTint, false: '' },
45 | },
46 | android: {
47 | thumbColor: modeTint,
48 | },
49 | });
50 |
51 | return (
52 |
53 |
57 |
58 |
69 |
73 |
74 | Edges{'\n'}
75 |
76 | Make sure at least one is picked!
77 |
78 |
79 |
80 |
86 |
92 |
98 |
104 |
105 | setMode(mode === 'padding' ? 'margin' : 'padding')}
108 | >
109 |
110 | Safe areas added to{' '}
111 | {mode}
112 | {'\n'}
113 | Tap to toggle
114 |
115 |
116 |
117 |
118 |
119 | Add additional{' '}
120 | padding
121 |
122 |
127 |
128 |
129 |
130 | Add additional{' '}
131 | margin
132 |
133 |
138 |
139 |
140 |
141 |
142 |
143 | );
144 | }
145 |
146 | const marginColor = '#5f27cd';
147 | const paddingColor = '#10ac84';
148 |
149 | const styles = StyleSheet.create({
150 | background: {
151 | flex: 1,
152 | backgroundColor: marginColor,
153 | },
154 | safeArea: {
155 | flex: 1,
156 | backgroundColor: paddingColor,
157 | },
158 | contentContainer: {
159 | flexGrow: 1,
160 | backgroundColor: 'white',
161 | padding: 20,
162 | justifyContent: 'center',
163 | },
164 | edges: {
165 | alignSelf: 'center',
166 | alignItems: 'center',
167 | justifyContent: 'center',
168 | marginTop: 40,
169 | marginBottom: 100,
170 | width: 200,
171 | height: 200,
172 | borderWidth: StyleSheet.hairlineWidth,
173 | borderColor: '#c8d6e5',
174 | borderRadius: 6,
175 | },
176 | row: {
177 | marginVertical: 4,
178 | flexDirection: 'row',
179 | alignItems: 'center',
180 | justifyContent: 'space-between',
181 | },
182 | separator: {
183 | marginVertical: 12,
184 | backgroundColor: '#c8d6e5',
185 | height: StyleSheet.hairlineWidth,
186 | },
187 | text: {
188 | fontSize: 17,
189 | lineHeight: 24,
190 | color: '#222f3e',
191 | },
192 | subText: {
193 | fontSize: 14,
194 | color: '#576574',
195 | },
196 | });
197 |
--------------------------------------------------------------------------------
/example/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin or MSYS, switch paths to Windows format before running java
129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=`expr $i + 1`
158 | done
159 | case $i in
160 | 0) set -- ;;
161 | 1) set -- "$args0" ;;
162 | 2) set -- "$args0" "$args1" ;;
163 | 3) set -- "$args0" "$args1" "$args2" ;;
164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=`save "$@"`
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | exec "$JAVACMD" "$@"
184 |
--------------------------------------------------------------------------------
/ios/SafeAreaView/RNCSafeAreaShadowView.m:
--------------------------------------------------------------------------------
1 | #import "RNCSafeAreaShadowView.h"
2 |
3 | #import
4 | #include
5 |
6 | #import "RNCSafeAreaViewLocalData.h"
7 | #import "RNCSafeAreaViewMode.h"
8 | #import "RNCSafeAreaViewEdges.h"
9 |
10 | // From RCTShadowView.m
11 | typedef NS_ENUM(unsigned int, meta_prop_t) {
12 | META_PROP_LEFT,
13 | META_PROP_TOP,
14 | META_PROP_RIGHT,
15 | META_PROP_BOTTOM,
16 | META_PROP_HORIZONTAL,
17 | META_PROP_VERTICAL,
18 | META_PROP_ALL,
19 | META_PROP_COUNT,
20 | };
21 |
22 | @implementation RNCSafeAreaShadowView {
23 | RNCSafeAreaViewLocalData *_localData;
24 | bool _needsUpdate;
25 | YGValue _paddingMetaProps[META_PROP_COUNT];
26 | YGValue _marginMetaProps[META_PROP_COUNT];
27 | }
28 |
29 | - (instancetype)init
30 | {
31 | self = [super init];
32 | if (self) {
33 | _needsUpdate = false;
34 | for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
35 | _paddingMetaProps[ii] = YGValueUndefined;
36 | _marginMetaProps[ii] = YGValueUndefined;
37 | }
38 | }
39 | return self;
40 | }
41 |
42 | - (void)extractEdges:(YGValue[])_metaProps top:(CGFloat *)top right:(CGFloat *)right bottom:(CGFloat *)bottom left:(CGFloat *)left
43 | {
44 | if (_metaProps[META_PROP_ALL].unit == YGUnitPoint) {
45 | *top = _metaProps[META_PROP_ALL].value;
46 | *right = _metaProps[META_PROP_ALL].value;
47 | *bottom = _metaProps[META_PROP_ALL].value;
48 | *left = _metaProps[META_PROP_ALL].value;
49 | }
50 |
51 | if (_metaProps[META_PROP_HORIZONTAL].unit == YGUnitPoint) {
52 | *right = _metaProps[META_PROP_HORIZONTAL].value;
53 | *left = _metaProps[META_PROP_HORIZONTAL].value;
54 | }
55 |
56 | if (_metaProps[META_PROP_VERTICAL].unit == YGUnitPoint) {
57 | *top = _metaProps[META_PROP_VERTICAL].value;
58 | *bottom = _metaProps[META_PROP_VERTICAL].value;
59 | }
60 |
61 | if (_metaProps[META_PROP_TOP].unit == YGUnitPoint) {
62 | *top = _metaProps[META_PROP_TOP].value;
63 | }
64 |
65 | if (_metaProps[META_PROP_RIGHT].unit == YGUnitPoint) {
66 | *right = _metaProps[META_PROP_RIGHT].value;
67 | }
68 |
69 | if (_metaProps[META_PROP_BOTTOM].unit == YGUnitPoint) {
70 | *bottom = _metaProps[META_PROP_BOTTOM].value;
71 | }
72 |
73 | if (_metaProps[META_PROP_LEFT].unit == YGUnitPoint) {
74 | *left = _metaProps[META_PROP_LEFT].value;
75 | }
76 | }
77 |
78 | - (void)resetInsetsForMode:(RNCSafeAreaViewMode)mode {
79 | if (mode == RNCSafeAreaViewModePadding) {
80 | super.paddingTop = _paddingMetaProps[META_PROP_TOP];
81 | super.paddingRight = _paddingMetaProps[META_PROP_RIGHT];
82 | super.paddingBottom = _paddingMetaProps[META_PROP_BOTTOM];
83 | super.paddingLeft = _paddingMetaProps[META_PROP_LEFT];
84 | } else if (mode == RNCSafeAreaViewModeMargin) {
85 | super.marginTop = _marginMetaProps[META_PROP_TOP];
86 | super.marginRight = _marginMetaProps[META_PROP_RIGHT];
87 | super.marginBottom = _marginMetaProps[META_PROP_BOTTOM];
88 | super.marginLeft = _marginMetaProps[META_PROP_LEFT];
89 | }
90 | }
91 |
92 | - (void)updateInsets
93 | {
94 | if (_localData == nil) {
95 | return;
96 | }
97 |
98 | UIEdgeInsets insets = _localData.insets;
99 | RNCSafeAreaViewMode mode = _localData.mode;
100 | RNCSafeAreaViewEdges edges = _localData.edges;
101 |
102 | CGFloat top = 0;
103 | CGFloat right = 0;
104 | CGFloat bottom = 0;
105 | CGFloat left = 0;
106 |
107 | CGFloat insetTop = (edges & RNCSafeAreaViewEdgesTop) ? insets.top : 0;
108 | CGFloat insetRight = (edges & RNCSafeAreaViewEdgesRight) ? insets.right : 0;
109 | CGFloat insetBottom = (edges & RNCSafeAreaViewEdgesBottom) ? insets.bottom : 0;
110 | CGFloat insetLeft = (edges & RNCSafeAreaViewEdgesLeft) ? insets.left : 0;
111 |
112 | if (mode == RNCSafeAreaViewModePadding) {
113 | [self extractEdges:_paddingMetaProps top:&top right:&right bottom:&bottom left:&left];
114 | super.paddingTop = (YGValue){insetTop + top, YGUnitPoint};
115 | super.paddingRight = (YGValue){insetRight + right, YGUnitPoint};
116 | super.paddingBottom = (YGValue){insetBottom + bottom, YGUnitPoint};
117 | super.paddingLeft = (YGValue){insetLeft + left, YGUnitPoint};
118 | } else if (mode == RNCSafeAreaViewModeMargin) {
119 | [self extractEdges:_marginMetaProps top:&top right:&right bottom:&bottom left:&left];
120 | super.marginTop = (YGValue){insetTop + top, YGUnitPoint};
121 | super.marginRight = (YGValue){insetRight + right, YGUnitPoint};
122 | super.marginBottom = (YGValue){insetBottom + bottom, YGUnitPoint};
123 | super.marginLeft = (YGValue){insetLeft + left, YGUnitPoint};
124 | }
125 | }
126 |
127 | - (void)didSetProps:(NSArray *)changedProps
128 | {
129 | if (_needsUpdate) {
130 | _needsUpdate = false;
131 | [self updateInsets];
132 | }
133 | [super didSetProps:changedProps];
134 | }
135 |
136 | - (void)setLocalData:(RNCSafeAreaViewLocalData *)localData
137 | {
138 | RCTAssert(
139 | [localData isKindOfClass:[RNCSafeAreaViewLocalData class]],
140 | @"Local data object for `RCTRNCSafeAreaShadowView` must be `RCTRNCSafeAreaViewLocalData` instance."
141 | );
142 |
143 | if (_localData != nil && _localData.mode != localData.mode) {
144 | [self resetInsetsForMode:_localData.mode];
145 | }
146 |
147 | _localData = localData;
148 | _needsUpdate = false;
149 | [self updateInsets];
150 |
151 | if (_localData.mode == RNCSafeAreaViewModePadding) {
152 | [super didSetProps:@[@"paddingTop", @"paddingRight", @"paddingBottom", @"paddingLeft"]];
153 | } else {
154 | [super didSetProps:@[@"marginTop", @"marginRight", @"marginBottom", @"marginLeft"]];
155 | }
156 | }
157 |
158 | #define SHADOW_VIEW_MARGIN_PADDING_PROP(edge, metaProp) \
159 | - (void)setPadding##edge:(YGValue)value \
160 | { \
161 | [super setPadding##edge:value]; \
162 | _needsUpdate = true; \
163 | _paddingMetaProps[META_PROP_##metaProp] = value; \
164 | } \
165 | - (void)setMargin##edge:(YGValue)value \
166 | { \
167 | [super setMargin##edge:value]; \
168 | _needsUpdate = true; \
169 | _marginMetaProps[META_PROP_##metaProp] = value; \
170 | }
171 |
172 | SHADOW_VIEW_MARGIN_PADDING_PROP(, ALL);
173 | SHADOW_VIEW_MARGIN_PADDING_PROP(Vertical, VERTICAL);
174 | SHADOW_VIEW_MARGIN_PADDING_PROP(Horizontal, HORIZONTAL);
175 | SHADOW_VIEW_MARGIN_PADDING_PROP(Top, TOP);
176 | SHADOW_VIEW_MARGIN_PADDING_PROP(Right, RIGHT);
177 | SHADOW_VIEW_MARGIN_PADDING_PROP(Bottom, BOTTOM);
178 | SHADOW_VIEW_MARGIN_PADDING_PROP(Left, LEFT);
179 |
180 | @end
181 |
--------------------------------------------------------------------------------
/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewShadowNode.java:
--------------------------------------------------------------------------------
1 | package com.th3rdwave.safeareacontext;
2 |
3 | import com.facebook.react.bridge.Dynamic;
4 | import com.facebook.react.bridge.ReadableType;
5 | import com.facebook.react.uimanager.LayoutShadowNode;
6 | import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
7 | import com.facebook.react.uimanager.PixelUtil;
8 | import com.facebook.react.uimanager.Spacing;
9 | import com.facebook.react.uimanager.ViewProps;
10 | import com.facebook.react.uimanager.annotations.ReactPropGroup;
11 |
12 | import java.util.EnumSet;
13 |
14 | import androidx.annotation.Nullable;
15 |
16 | public class SafeAreaViewShadowNode extends LayoutShadowNode {
17 | private @Nullable SafeAreaViewLocalData mLocalData;
18 |
19 | private float[] mPaddings;
20 | private float[] mMargins;
21 | private boolean mNeedsUpdate = false;
22 |
23 | public SafeAreaViewShadowNode() {
24 | super();
25 |
26 | mPaddings = new float[ViewProps.PADDING_MARGIN_SPACING_TYPES.length];
27 | mMargins = new float[ViewProps.PADDING_MARGIN_SPACING_TYPES.length];
28 |
29 | for (int i = 0; i < ViewProps.PADDING_MARGIN_SPACING_TYPES.length; i += 1) {
30 | mPaddings[i] = Float.NaN;
31 | mMargins[i] = Float.NaN;
32 | }
33 | }
34 |
35 | private void updateInsets() {
36 | if (mLocalData == null) {
37 | return;
38 | }
39 |
40 | float top = 0;
41 | float right = 0;
42 | float bottom = 0;
43 | float left = 0;
44 |
45 | float[] meta = mLocalData.getMode() == SafeAreaViewMode.PADDING ? mPaddings : mMargins;
46 |
47 | float allEdges = meta[Spacing.ALL];
48 | if (!Float.isNaN(allEdges)) {
49 | top = allEdges;
50 | right = allEdges;
51 | bottom = allEdges;
52 | left = allEdges;
53 | }
54 |
55 | float verticalEdges = meta[Spacing.VERTICAL];
56 | if (!Float.isNaN(verticalEdges)) {
57 | top = verticalEdges;
58 | bottom = verticalEdges;
59 | }
60 |
61 | float horizontalEdges = meta[Spacing.HORIZONTAL];
62 | if (!Float.isNaN(horizontalEdges)) {
63 | right = horizontalEdges;
64 | left = horizontalEdges;
65 | }
66 |
67 | float topEdge = meta[Spacing.TOP];
68 | if (!Float.isNaN(topEdge)) {
69 | top = topEdge;
70 | }
71 |
72 | float rightEdge = meta[Spacing.RIGHT];
73 | if (!Float.isNaN(rightEdge)) {
74 | right = rightEdge;
75 | }
76 |
77 | float bottomEdge = meta[Spacing.BOTTOM];
78 | if (!Float.isNaN(bottomEdge)) {
79 | bottom = bottomEdge;
80 | }
81 |
82 | float leftEdge = meta[Spacing.LEFT];
83 | if (!Float.isNaN(leftEdge)) {
84 | left = leftEdge;
85 | }
86 |
87 | top = PixelUtil.toPixelFromDIP(top);
88 | right = PixelUtil.toPixelFromDIP(right);
89 | bottom = PixelUtil.toPixelFromDIP(bottom);
90 | left = PixelUtil.toPixelFromDIP(left);
91 |
92 | EnumSet edges = mLocalData.getEdges();
93 | EdgeInsets insets = mLocalData.getInsets();
94 | float insetTop = edges.contains(SafeAreaViewEdges.TOP) ? insets.top : 0;
95 | float insetRight = edges.contains(SafeAreaViewEdges.RIGHT) ? insets.right : 0;
96 | float insetBottom = edges.contains(SafeAreaViewEdges.BOTTOM) ? insets.bottom : 0;
97 | float insetLeft = edges.contains(SafeAreaViewEdges.LEFT) ? insets.left : 0;
98 |
99 | if (mLocalData.getMode() == SafeAreaViewMode.PADDING) {
100 | super.setPadding(Spacing.TOP, insetTop + top);
101 | super.setPadding(Spacing.RIGHT, insetRight + right);
102 | super.setPadding(Spacing.BOTTOM, insetBottom + bottom);
103 | super.setPadding(Spacing.LEFT, insetLeft + left);
104 | } else {
105 | super.setMargin(Spacing.TOP, insetTop + top);
106 | super.setMargin(Spacing.RIGHT, insetRight + right);
107 | super.setMargin(Spacing.BOTTOM, insetBottom + bottom);
108 | super.setMargin(Spacing.LEFT, insetLeft + left);
109 | }
110 | }
111 |
112 | private void resetInsets(SafeAreaViewMode mode) {
113 | if (mode == SafeAreaViewMode.PADDING) {
114 | super.setPadding(Spacing.TOP, mPaddings[Spacing.TOP]);
115 | super.setPadding(Spacing.RIGHT, mPaddings[Spacing.TOP]);
116 | super.setPadding(Spacing.BOTTOM, mPaddings[Spacing.BOTTOM]);
117 | super.setPadding(Spacing.LEFT, mPaddings[Spacing.LEFT]);
118 | } else {
119 | super.setMargin(Spacing.TOP, mMargins[Spacing.TOP]);
120 | super.setMargin(Spacing.RIGHT, mMargins[Spacing.TOP]);
121 | super.setMargin(Spacing.BOTTOM, mMargins[Spacing.BOTTOM]);
122 | super.setMargin(Spacing.LEFT, mMargins[Spacing.LEFT]);
123 | }
124 | }
125 |
126 | // The signature for onBeforeLayout is different in RN 0.59.
127 | // Remove when we drop support for this version and add back @Override and super call to
128 | // onBeforeLayout(NativeViewHierarchyOptimizer).
129 | public void onBeforeLayout() {
130 | if (mNeedsUpdate) {
131 | mNeedsUpdate = false;
132 | updateInsets();
133 | }
134 | }
135 |
136 | public void onBeforeLayout(NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
137 | if (mNeedsUpdate) {
138 | mNeedsUpdate = false;
139 | updateInsets();
140 | }
141 | }
142 |
143 | @Override
144 | public void setLocalData(Object data) {
145 | if (!(data instanceof SafeAreaViewLocalData)) {
146 | return;
147 | }
148 |
149 | SafeAreaViewLocalData localData = (SafeAreaViewLocalData) data;
150 |
151 | if (mLocalData != null && mLocalData.getMode() != localData.getMode()) {
152 | resetInsets(mLocalData.getMode());
153 | }
154 |
155 | mLocalData = localData;
156 |
157 | mNeedsUpdate = false;
158 | updateInsets();
159 | }
160 |
161 | // Names needs to reflect exact order in LayoutShadowNode.java
162 | @Override
163 | @ReactPropGroup(
164 | names = {
165 | ViewProps.PADDING,
166 | ViewProps.PADDING_VERTICAL,
167 | ViewProps.PADDING_HORIZONTAL,
168 | ViewProps.PADDING_START,
169 | ViewProps.PADDING_END,
170 | ViewProps.PADDING_TOP,
171 | ViewProps.PADDING_BOTTOM,
172 | ViewProps.PADDING_LEFT,
173 | ViewProps.PADDING_RIGHT,
174 | })
175 | public void setPaddings(int index, Dynamic padding) {
176 | int spacingType = ViewProps.PADDING_MARGIN_SPACING_TYPES[index];
177 | mPaddings[spacingType] = padding.getType() == ReadableType.Number ? (float) padding.asDouble() : Float.NaN;
178 | super.setPaddings(index, padding);
179 | mNeedsUpdate = true;
180 | }
181 |
182 | @Override
183 | @ReactPropGroup(
184 | names = {
185 | ViewProps.MARGIN,
186 | ViewProps.MARGIN_VERTICAL,
187 | ViewProps.MARGIN_HORIZONTAL,
188 | ViewProps.MARGIN_START,
189 | ViewProps.MARGIN_END,
190 | ViewProps.MARGIN_TOP,
191 | ViewProps.MARGIN_BOTTOM,
192 | ViewProps.MARGIN_LEFT,
193 | ViewProps.MARGIN_RIGHT,
194 | })
195 | public void setMargins(int index, Dynamic margin) {
196 | int spacingType = ViewProps.PADDING_MARGIN_SPACING_TYPES[index];
197 | mMargins[spacingType] = margin.getType() == ReadableType.Number ? (float) margin.asDouble() : Float.NaN;
198 | super.setMargins(index, margin);
199 | mNeedsUpdate = true;
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation. If none specified and
19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
20 | * // default. Can be overridden with ENTRY_FILE environment variable.
21 | * entryFile: "index.android.js",
22 | *
23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
24 | * bundleCommand: "ram-bundle",
25 | *
26 | * // whether to bundle JS and assets in debug mode
27 | * bundleInDebug: false,
28 | *
29 | * // whether to bundle JS and assets in release mode
30 | * bundleInRelease: true,
31 | *
32 | * // whether to bundle JS and assets in another build variant (if configured).
33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
34 | * // The configuration property can be in the following formats
35 | * // 'bundleIn${productFlavor}${buildType}'
36 | * // 'bundleIn${buildType}'
37 | * // bundleInFreeDebug: true,
38 | * // bundleInPaidRelease: true,
39 | * // bundleInBeta: true,
40 | *
41 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
42 | * // for example: to disable dev mode in the staging build type (if configured)
43 | * devDisabledInStaging: true,
44 | * // The configuration property can be in the following formats
45 | * // 'devDisabledIn${productFlavor}${buildType}'
46 | * // 'devDisabledIn${buildType}'
47 | *
48 | * // the root of your project, i.e. where "package.json" lives
49 | * root: "../../",
50 | *
51 | * // where to put the JS bundle asset in debug mode
52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
53 | *
54 | * // where to put the JS bundle asset in release mode
55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
56 | *
57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
58 | * // require('./image.png')), in debug mode
59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
60 | *
61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
62 | * // require('./image.png')), in release mode
63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
64 | *
65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
69 | * // for example, you might want to remove it from here.
70 | * inputExcludes: ["android/**", "ios/**"],
71 | *
72 | * // override which node gets called and with what additional arguments
73 | * nodeExecutableAndArgs: ["node"],
74 | *
75 | * // supply additional arguments to the packager
76 | * extraPackagerArgs: []
77 | * ]
78 | */
79 |
80 | project.ext.react = [
81 | entryFile: "example/index.js",
82 | enableHermes: false, // clean and rebuild if changing
83 | ]
84 |
85 | apply from: "../../../node_modules/react-native/react.gradle"
86 |
87 | /**
88 | * Set this to true to create two separate APKs instead of one:
89 | * - An APK that only works on ARM devices
90 | * - An APK that only works on x86 devices
91 | * The advantage is the size of the APK is reduced by about 4MB.
92 | * Upload all the APKs to the Play Store and people will download
93 | * the correct one based on the CPU architecture of their device.
94 | */
95 | def enableSeparateBuildPerCPUArchitecture = false
96 |
97 | /**
98 | * Run Proguard to shrink the Java bytecode in release builds.
99 | */
100 | def enableProguardInReleaseBuilds = false
101 |
102 | /**
103 | * The preferred build flavor of JavaScriptCore.
104 | *
105 | * For example, to use the international variant, you can use:
106 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
107 | *
108 | * The international variant includes ICU i18n library and necessary data
109 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
110 | * give correct results when using with locales other than en-US. Note that
111 | * this variant is about 6MiB larger per architecture than default.
112 | */
113 | def jscFlavor = 'org.webkit:android-jsc:+'
114 |
115 | /**
116 | * Whether to enable the Hermes VM.
117 | *
118 | * This should be set on project.ext.react and mirrored here. If it is not set
119 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
120 | * and the benefits of using Hermes will therefore be sharply reduced.
121 | */
122 | def enableHermes = project.ext.react.get("enableHermes", false);
123 |
124 | android {
125 | compileSdkVersion rootProject.ext.compileSdkVersion
126 |
127 | compileOptions {
128 | sourceCompatibility JavaVersion.VERSION_1_8
129 | targetCompatibility JavaVersion.VERSION_1_8
130 | }
131 |
132 | defaultConfig {
133 | applicationId "com.safeareaviewexample"
134 | minSdkVersion rootProject.ext.minSdkVersion
135 | targetSdkVersion rootProject.ext.targetSdkVersion
136 | versionCode 1
137 | versionName "1.0"
138 | }
139 | splits {
140 | abi {
141 | reset()
142 | enable enableSeparateBuildPerCPUArchitecture
143 | universalApk false // If true, also generate a universal APK
144 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
145 | }
146 | }
147 | buildTypes {
148 | debug {
149 | signingConfig signingConfigs.debug
150 | }
151 | release {
152 | // Caution! In production, you need to generate your own keystore file.
153 | // see https://reactnative.dev/docs/signed-apk-android.
154 | signingConfig signingConfigs.debug
155 | minifyEnabled enableProguardInReleaseBuilds
156 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
157 | }
158 | }
159 |
160 | // applicationVariants are e.g. debug, release
161 | applicationVariants.all { variant ->
162 | variant.outputs.each { output ->
163 | // For each separate APK per architecture, set a unique version code as described here:
164 | // https://developer.android.com/studio/build/configure-apk-splits.html
165 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
166 | def abi = output.getFilter(OutputFile.ABI)
167 | if (abi != null) { // null for the universal-debug, universal-release variants
168 | output.versionCodeOverride =
169 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
170 | }
171 |
172 | }
173 | }
174 |
175 | packagingOptions {
176 | pickFirst '**/armeabi-v7a/libc++_shared.so'
177 | pickFirst '**/x86/libc++_shared.so'
178 | pickFirst '**/arm64-v8a/libc++_shared.so'
179 | pickFirst '**/x86_64/libc++_shared.so'
180 | pickFirst '**/x86/libjsc.so'
181 | pickFirst '**/armeabi-v7a/libjsc.so'
182 | }
183 | }
184 |
185 | dependencies {
186 | implementation fileTree(dir: "libs", include: ["*.jar"])
187 | //noinspection GradleDynamicVersion
188 | implementation "com.facebook.react:react-native:+" // From node_modules
189 |
190 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
191 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
192 | exclude group:'com.facebook.fbjni'
193 | }
194 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
195 | exclude group:'com.facebook.flipper'
196 | }
197 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
198 | exclude group:'com.facebook.flipper'
199 | }
200 |
201 | if (enableHermes) {
202 | def hermesPath = "../../../node_modules/hermesvm/android/";
203 | debugImplementation files(hermesPath + "hermes-debug.aar")
204 | releaseImplementation files(hermesPath + "hermes-release.aar")
205 | } else {
206 | implementation jscFlavor
207 | }
208 |
209 | implementation project(":react-native-community-async-storage")
210 | implementation project(":react-native-gesture-handler")
211 | implementation project(":react-native-reanimated")
212 | implementation project(":react-native-screens")
213 |
214 | implementation project(":react-native-safe-area-context")
215 | }
216 |
217 | // Run this once to be able to run the application with BUCK
218 | // puts all compile dependencies into folder libs for BUCK to use
219 | task copyDownloadableDepsToLibs(type: Copy) {
220 | from configurations.compile
221 | into 'libs'
222 | }
223 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [DEPRECATED] _This repository is no longer used, as in Gutenberg we now use the original version of this package, plus fetching [a prebuilt Android artifact from a maven repo on S3](https://github.com/wordpress-mobile/react-native-libraries-publisher/blob/trunk/README.md)._
2 |
3 | # react-native-safe-area-context
4 |
5 | [](https://www.npmjs.com/package/react-native-safe-area-context)  
6 |
7 | [](https://github.com/th3rdwave/react-native-safe-area-context/actions?query=workflow%3AJavaScript%20tests) [](https://github.com/th3rdwave/react-native-safe-area-context/actions?query=workflow%3AiOS%20build) [](https://github.com/th3rdwave/react-native-safe-area-context/actions?query=workflow%3AAndroid%20build)
8 |
9 | A flexible way to handle safe area, also works on Android and Web!
10 |
11 | ## Getting started
12 |
13 | ```
14 | npm install react-native-safe-area-context
15 | ```
16 |
17 | You then need to link the native parts of the library for the platforms you are using.
18 |
19 | #### Linking in React Native >= 0.60
20 |
21 | Linking the package is not required anymore with [Autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md).
22 |
23 | - **iOS Platform:**
24 |
25 | `$ npx pod-install`
26 |
27 | #### Linking in React Native < 0.60
28 |
29 | The easiest way to link the library is using the CLI tool by running this command from the root of your project:
30 |
31 | ```
32 | react-native link react-native-safe-area-context
33 | ```
34 |
35 | If you can't or don't want to use the CLI tool, you can also manually link the library using the instructions below (click on the arrow to show them):
36 |
37 |
38 | Manually link the library on iOS
39 |
40 | Either follow the [instructions in the React Native documentation](https://facebook.github.io/react-native/docs/linking-libraries-ios#manual-linking) to manually link the framework or link using [Cocoapods](https://cocoapods.org) by adding this to your `Podfile`:
41 |
42 | ```ruby
43 | pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
44 | ```
45 |
46 |
47 |
48 |
49 | Manually link the library on Android
50 |
51 | Make the following changes:
52 |
53 | #### `android/settings.gradle`
54 |
55 | ```groovy
56 | include ':react-native-safe-area-context'
57 | project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
58 | ```
59 |
60 | #### `android/app/build.gradle`
61 |
62 | ```groovy
63 | dependencies {
64 | ...
65 | implementation project(':react-native-safe-area-context')
66 | }
67 | ```
68 |
69 | #### `android/app/src/main/.../MainApplication.java`
70 |
71 | On top, where imports are:
72 |
73 | ```java
74 | import com.th3rdwave.safeareacontext.SafeAreaContextPackage;
75 | ```
76 |
77 | Add the `SafeAreaContextPackage` class to your list of exported packages.
78 |
79 | ```java
80 | @Override
81 | protected List getPackages() {
82 | return Arrays.asList(
83 | new MainReactPackage(),
84 | ...
85 | new SafeAreaContextPackage()
86 | );
87 | }
88 | ```
89 |
90 |
91 |
92 | **Note**
93 | Before 3.1.9 release of safe-area-context, Building for React Native 0.59 would not work due to missing header files. Use >= 3.1.9 to work around that.
94 |
95 | ## Usage
96 |
97 | This library has 2 important concepts, if you are familiar with React Context this is very similar.
98 |
99 | ### Providers
100 |
101 | The [SafeAreaProvider](#safeareaprovider) component is a `View` from where insets provided by [Consumers](#consumers) are relative to. This means that if this view overlaps with any system elements (status bar, notches, etc.) these values will be provided to descendent consumers. Usually you will have one provider at the top of your app.
102 |
103 | ### Consumers
104 |
105 | Consumers are components and hooks that allow using inset values provided by the nearest parent [Provider](#providers). Values are always relative to a provider and not to these components.
106 |
107 | - [SafeAreaView](#safeareaview) is the preferred way to consume insets. This is a regular `View` with insets applied as extra padding or margin. It offers better performance by applying insets natively and avoids flickers that can happen with the other JS based consumers.
108 |
109 | - [useSafeAreaInsets](#usesafeareainsets) offers more flexibility, but can cause some layout flicker in certain cases. Use this if you need more control over how insets are applied.
110 |
111 | ## API
112 |
113 | ### SafeAreaProvider
114 |
115 | You should add `SafeAreaProvider` in your app root component. You may need to add it in other places like the root of modals and routes when using [`react-native-screens`](https://github.com/software-mansion/react-native-screens).
116 |
117 | Note that providers should not be inside a `View` that is animated with `Animated` or inside a `ScrollView` since it can cause very frequent updates.
118 |
119 | #### Example
120 |
121 | ```js
122 | import { SafeAreaProvider } from 'react-native-safe-area-context';
123 |
124 | function App() {
125 | return ...;
126 | }
127 | ```
128 |
129 | #### Props
130 |
131 | Accepts all [View](https://reactnative.dev/docs/view#props) props. Has a default style of `{flex: 1}`.
132 |
133 | ##### `initialMetrics`
134 |
135 | Optional, defaults to `null`.
136 |
137 | Can be used to provide the initial value for frame and insets, this allows rendering immediatly. See [optimization](#optimization) for more information on how to use this prop.
138 |
139 | ### SafeAreaView
140 |
141 | `SafeAreaView` is a regular `View` component with the safe area insets applied as padding or margin.
142 |
143 | Padding or margin styles are added to the insets, for example `style={{paddingTop: 10}}` on a `SafeAreaView` that has insets of 20 will result in a top padding of 30.
144 |
145 | #### Example
146 |
147 | ```js
148 | import { SafeAreaView } from 'react-native-safe-area-context';
149 |
150 | function SomeComponent() {
151 | return (
152 |
153 |
154 |
155 | );
156 | }
157 | ```
158 |
159 | #### Props
160 |
161 | Accepts all [View](https://reactnative.dev/docs/view#props) props.
162 |
163 | ##### `edges`
164 |
165 | Optional, array of `top`, `right`, `bottom`, and `left`. Defaults to all.
166 |
167 | Sets the edges to apply the safe area insets to.
168 |
169 | For example if you don't want insets to apply to the top edge because the view does not touch the top of the screen you can use:
170 |
171 | ```js
172 |
173 | ```
174 |
175 | ##### `mode`
176 |
177 | Optional, `padding` (default) or `margin`.
178 |
179 | Apply the safe area to either the padding or the margin.
180 |
181 | This can be useful for example to create a safe area aware separator component:
182 |
183 | ```js
184 |
185 | ```
186 |
187 | ### useSafeAreaInsets
188 |
189 | Returns the safe area insets of the nearest provider. This allows manipulating the inset values from JavaScript. Note that insets are not updated synchronously so it might cause a slight delay for example when rotating the screen.
190 |
191 | Object with `{ top: number, right: number, bottom: number, left: number }`.
192 |
193 | ```js
194 | import { useSafeAreaInsets } from 'react-native-safe-area-context';
195 |
196 | function HookComponent() {
197 | const insets = useSafeAreaInsets();
198 |
199 | return ;
200 | }
201 | ```
202 |
203 | ### useSafeAreaFrame
204 |
205 | Returns the frame of the nearest provider. This can be used as an alternative to the `Dimensions` module.
206 |
207 | Object with `{ x: number, y: number, width: number, height: number }`
208 |
209 | ### `SafeAreaInsetsContext`
210 |
211 | React Context with the value of the safe area insets.
212 |
213 | Can be used with class components:
214 |
215 | ```js
216 | import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
217 |
218 | class ClassComponent extends React.Component {
219 | render() {
220 | return (
221 |
222 | {(insets) => }
223 |
224 | );
225 | }
226 | }
227 | ```
228 |
229 | ### `withSafeAreaInsets`
230 |
231 | Higher order component that provides safe area insets as the `insets` prop.
232 |
233 | ### `SafeAreaFrameContext`
234 |
235 | React Context with the value of the safe area frame.
236 |
237 | ### `initialWindowMetrics`
238 |
239 | Insets and frame of the window on initial render. This can be used with the `initialMetrics` from `SafeAreaProvider`. See [optimization](#optimization) for more information.
240 |
241 | Object with:
242 |
243 | ```ts
244 | {
245 | frame: { x: number, y: number, width: number, height: number },
246 | insets: { top: number, left: number, right: number, bottom: number },
247 | }
248 | ```
249 |
250 | **NOTE:** This value can be null or out of date as it is computed when the native module is created.
251 |
252 | ## Deprecated apis
253 |
254 | ### useSafeArea
255 |
256 | Use `useSafeAreaInsets` instead.
257 |
258 | ### SafeAreaConsumer
259 |
260 | Use `SafeAreaInsetsContext.Consumer` instead.
261 |
262 | ### SafeAreaContext
263 |
264 | Use `SafeAreaInsetsContext` instead.
265 |
266 | ### initialWindowSafeAreaInsets
267 |
268 | Use `initialWindowMetrics` instead.
269 |
270 | ## Web SSR
271 |
272 | If you are doing server side rendering on the web you can use `initialMetrics` to inject insets and frame value based on the device the user has, or simply pass zero values. Since insets measurement is async it will break rendering your page content otherwise.
273 |
274 | ## Optimization
275 |
276 | If you can, use `SafeAreaView`. It's implemented natively so when rotating the device, there is no delay from the asynchronous bridge.
277 |
278 | To speed up the initial render, you can import `initialWindowMetrics` from this package and set as the `initialMetrics` prop on the provider as described in Web SSR. You cannot do this if your provider remounts, or you are using `react-native-navigation`.
279 |
280 | ```js
281 | import {
282 | SafeAreaProvider,
283 | initialWindowMetrics,
284 | } from 'react-native-safe-area-context';
285 |
286 | function App() {
287 | return (
288 |
289 | ...
290 |
291 | );
292 | }
293 | ```
294 |
295 | ## Testing
296 |
297 | This library includes a built in mock for Jest. It will use the following metrics by default:
298 |
299 | ```js
300 | {
301 | frame: {
302 | width: 320,
303 | height: 640,
304 | x: 0,
305 | y: 0,
306 | },
307 | insets: {
308 | left: 0,
309 | right: 0,
310 | bottom: 0,
311 | top: 0,
312 | },
313 | }
314 | ```
315 |
316 | To use it, add the following code to the jest setup file:
317 |
318 | ```js
319 | import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock';
320 |
321 | jest.mock('react-native-safe-area-context', () => mockSafeAreaContext);
322 | ```
323 |
324 | To have more control over the test values it is also possible to pass `initialMetrics` to
325 | `SafeAreaProvider` to provide mock data for frame and insets.
326 |
327 | ```js
328 | export function TestSafeAreaProvider({ children }) {
329 | return (
330 |
336 | {children}
337 |
338 | );
339 | }
340 | ```
341 |
342 | ## Contributing
343 |
344 | See the [Contributing Guide](CONTRIBUTING.md)
345 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - boost-for-react-native (1.63.0)
3 | - DoubleConversion (1.1.6)
4 | - FBLazyVector (0.63.0-rc.1)
5 | - FBReactNativeSpec (0.63.0-rc.1):
6 | - Folly (= 2020.01.13.00)
7 | - RCTRequired (= 0.63.0-rc.1)
8 | - RCTTypeSafety (= 0.63.0-rc.1)
9 | - React-Core (= 0.63.0-rc.1)
10 | - React-jsi (= 0.63.0-rc.1)
11 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
12 | - Folly (2020.01.13.00):
13 | - boost-for-react-native
14 | - DoubleConversion
15 | - Folly/Default (= 2020.01.13.00)
16 | - glog
17 | - Folly/Default (2020.01.13.00):
18 | - boost-for-react-native
19 | - DoubleConversion
20 | - glog
21 | - glog (0.3.5)
22 | - RCTRequired (0.63.0-rc.1)
23 | - RCTTypeSafety (0.63.0-rc.1):
24 | - FBLazyVector (= 0.63.0-rc.1)
25 | - Folly (= 2020.01.13.00)
26 | - RCTRequired (= 0.63.0-rc.1)
27 | - React-Core (= 0.63.0-rc.1)
28 | - React (0.63.0-rc.1):
29 | - React-Core (= 0.63.0-rc.1)
30 | - React-Core/DevSupport (= 0.63.0-rc.1)
31 | - React-Core/RCTWebSocket (= 0.63.0-rc.1)
32 | - React-RCTActionSheet (= 0.63.0-rc.1)
33 | - React-RCTAnimation (= 0.63.0-rc.1)
34 | - React-RCTBlob (= 0.63.0-rc.1)
35 | - React-RCTImage (= 0.63.0-rc.1)
36 | - React-RCTLinking (= 0.63.0-rc.1)
37 | - React-RCTNetwork (= 0.63.0-rc.1)
38 | - React-RCTSettings (= 0.63.0-rc.1)
39 | - React-RCTText (= 0.63.0-rc.1)
40 | - React-RCTVibration (= 0.63.0-rc.1)
41 | - React-callinvoker (0.63.0-rc.1)
42 | - React-Core (0.63.0-rc.1):
43 | - Folly (= 2020.01.13.00)
44 | - glog
45 | - React-Core/Default (= 0.63.0-rc.1)
46 | - React-cxxreact (= 0.63.0-rc.1)
47 | - React-jsi (= 0.63.0-rc.1)
48 | - React-jsiexecutor (= 0.63.0-rc.1)
49 | - Yoga
50 | - React-Core/CoreModulesHeaders (0.63.0-rc.1):
51 | - Folly (= 2020.01.13.00)
52 | - glog
53 | - React-Core/Default
54 | - React-cxxreact (= 0.63.0-rc.1)
55 | - React-jsi (= 0.63.0-rc.1)
56 | - React-jsiexecutor (= 0.63.0-rc.1)
57 | - Yoga
58 | - React-Core/Default (0.63.0-rc.1):
59 | - Folly (= 2020.01.13.00)
60 | - glog
61 | - React-cxxreact (= 0.63.0-rc.1)
62 | - React-jsi (= 0.63.0-rc.1)
63 | - React-jsiexecutor (= 0.63.0-rc.1)
64 | - Yoga
65 | - React-Core/DevSupport (0.63.0-rc.1):
66 | - Folly (= 2020.01.13.00)
67 | - glog
68 | - React-Core/Default (= 0.63.0-rc.1)
69 | - React-Core/RCTWebSocket (= 0.63.0-rc.1)
70 | - React-cxxreact (= 0.63.0-rc.1)
71 | - React-jsi (= 0.63.0-rc.1)
72 | - React-jsiexecutor (= 0.63.0-rc.1)
73 | - React-jsinspector (= 0.63.0-rc.1)
74 | - Yoga
75 | - React-Core/RCTActionSheetHeaders (0.63.0-rc.1):
76 | - Folly (= 2020.01.13.00)
77 | - glog
78 | - React-Core/Default
79 | - React-cxxreact (= 0.63.0-rc.1)
80 | - React-jsi (= 0.63.0-rc.1)
81 | - React-jsiexecutor (= 0.63.0-rc.1)
82 | - Yoga
83 | - React-Core/RCTAnimationHeaders (0.63.0-rc.1):
84 | - Folly (= 2020.01.13.00)
85 | - glog
86 | - React-Core/Default
87 | - React-cxxreact (= 0.63.0-rc.1)
88 | - React-jsi (= 0.63.0-rc.1)
89 | - React-jsiexecutor (= 0.63.0-rc.1)
90 | - Yoga
91 | - React-Core/RCTBlobHeaders (0.63.0-rc.1):
92 | - Folly (= 2020.01.13.00)
93 | - glog
94 | - React-Core/Default
95 | - React-cxxreact (= 0.63.0-rc.1)
96 | - React-jsi (= 0.63.0-rc.1)
97 | - React-jsiexecutor (= 0.63.0-rc.1)
98 | - Yoga
99 | - React-Core/RCTImageHeaders (0.63.0-rc.1):
100 | - Folly (= 2020.01.13.00)
101 | - glog
102 | - React-Core/Default
103 | - React-cxxreact (= 0.63.0-rc.1)
104 | - React-jsi (= 0.63.0-rc.1)
105 | - React-jsiexecutor (= 0.63.0-rc.1)
106 | - Yoga
107 | - React-Core/RCTLinkingHeaders (0.63.0-rc.1):
108 | - Folly (= 2020.01.13.00)
109 | - glog
110 | - React-Core/Default
111 | - React-cxxreact (= 0.63.0-rc.1)
112 | - React-jsi (= 0.63.0-rc.1)
113 | - React-jsiexecutor (= 0.63.0-rc.1)
114 | - Yoga
115 | - React-Core/RCTNetworkHeaders (0.63.0-rc.1):
116 | - Folly (= 2020.01.13.00)
117 | - glog
118 | - React-Core/Default
119 | - React-cxxreact (= 0.63.0-rc.1)
120 | - React-jsi (= 0.63.0-rc.1)
121 | - React-jsiexecutor (= 0.63.0-rc.1)
122 | - Yoga
123 | - React-Core/RCTSettingsHeaders (0.63.0-rc.1):
124 | - Folly (= 2020.01.13.00)
125 | - glog
126 | - React-Core/Default
127 | - React-cxxreact (= 0.63.0-rc.1)
128 | - React-jsi (= 0.63.0-rc.1)
129 | - React-jsiexecutor (= 0.63.0-rc.1)
130 | - Yoga
131 | - React-Core/RCTTextHeaders (0.63.0-rc.1):
132 | - Folly (= 2020.01.13.00)
133 | - glog
134 | - React-Core/Default
135 | - React-cxxreact (= 0.63.0-rc.1)
136 | - React-jsi (= 0.63.0-rc.1)
137 | - React-jsiexecutor (= 0.63.0-rc.1)
138 | - Yoga
139 | - React-Core/RCTVibrationHeaders (0.63.0-rc.1):
140 | - Folly (= 2020.01.13.00)
141 | - glog
142 | - React-Core/Default
143 | - React-cxxreact (= 0.63.0-rc.1)
144 | - React-jsi (= 0.63.0-rc.1)
145 | - React-jsiexecutor (= 0.63.0-rc.1)
146 | - Yoga
147 | - React-Core/RCTWebSocket (0.63.0-rc.1):
148 | - Folly (= 2020.01.13.00)
149 | - glog
150 | - React-Core/Default (= 0.63.0-rc.1)
151 | - React-cxxreact (= 0.63.0-rc.1)
152 | - React-jsi (= 0.63.0-rc.1)
153 | - React-jsiexecutor (= 0.63.0-rc.1)
154 | - Yoga
155 | - React-CoreModules (0.63.0-rc.1):
156 | - FBReactNativeSpec (= 0.63.0-rc.1)
157 | - Folly (= 2020.01.13.00)
158 | - RCTTypeSafety (= 0.63.0-rc.1)
159 | - React-Core/CoreModulesHeaders (= 0.63.0-rc.1)
160 | - React-jsi (= 0.63.0-rc.1)
161 | - React-RCTImage (= 0.63.0-rc.1)
162 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
163 | - React-cxxreact (0.63.0-rc.1):
164 | - boost-for-react-native (= 1.63.0)
165 | - DoubleConversion
166 | - Folly (= 2020.01.13.00)
167 | - glog
168 | - React-callinvoker (= 0.63.0-rc.1)
169 | - React-jsinspector (= 0.63.0-rc.1)
170 | - React-jsi (0.63.0-rc.1):
171 | - boost-for-react-native (= 1.63.0)
172 | - DoubleConversion
173 | - Folly (= 2020.01.13.00)
174 | - glog
175 | - React-jsi/Default (= 0.63.0-rc.1)
176 | - React-jsi/Default (0.63.0-rc.1):
177 | - boost-for-react-native (= 1.63.0)
178 | - DoubleConversion
179 | - Folly (= 2020.01.13.00)
180 | - glog
181 | - React-jsiexecutor (0.63.0-rc.1):
182 | - DoubleConversion
183 | - Folly (= 2020.01.13.00)
184 | - glog
185 | - React-cxxreact (= 0.63.0-rc.1)
186 | - React-jsi (= 0.63.0-rc.1)
187 | - React-jsinspector (0.63.0-rc.1)
188 | - react-native-safe-area-context (3.1.6):
189 | - React
190 | - React-RCTActionSheet (0.63.0-rc.1):
191 | - React-Core/RCTActionSheetHeaders (= 0.63.0-rc.1)
192 | - React-RCTAnimation (0.63.0-rc.1):
193 | - FBReactNativeSpec (= 0.63.0-rc.1)
194 | - Folly (= 2020.01.13.00)
195 | - RCTTypeSafety (= 0.63.0-rc.1)
196 | - React-Core/RCTAnimationHeaders (= 0.63.0-rc.1)
197 | - React-jsi (= 0.63.0-rc.1)
198 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
199 | - React-RCTBlob (0.63.0-rc.1):
200 | - FBReactNativeSpec (= 0.63.0-rc.1)
201 | - Folly (= 2020.01.13.00)
202 | - React-Core/RCTBlobHeaders (= 0.63.0-rc.1)
203 | - React-Core/RCTWebSocket (= 0.63.0-rc.1)
204 | - React-jsi (= 0.63.0-rc.1)
205 | - React-RCTNetwork (= 0.63.0-rc.1)
206 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
207 | - React-RCTImage (0.63.0-rc.1):
208 | - FBReactNativeSpec (= 0.63.0-rc.1)
209 | - Folly (= 2020.01.13.00)
210 | - RCTTypeSafety (= 0.63.0-rc.1)
211 | - React-Core/RCTImageHeaders (= 0.63.0-rc.1)
212 | - React-jsi (= 0.63.0-rc.1)
213 | - React-RCTNetwork (= 0.63.0-rc.1)
214 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
215 | - React-RCTLinking (0.63.0-rc.1):
216 | - FBReactNativeSpec (= 0.63.0-rc.1)
217 | - React-Core/RCTLinkingHeaders (= 0.63.0-rc.1)
218 | - React-jsi (= 0.63.0-rc.1)
219 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
220 | - React-RCTNetwork (0.63.0-rc.1):
221 | - FBReactNativeSpec (= 0.63.0-rc.1)
222 | - Folly (= 2020.01.13.00)
223 | - RCTTypeSafety (= 0.63.0-rc.1)
224 | - React-Core/RCTNetworkHeaders (= 0.63.0-rc.1)
225 | - React-jsi (= 0.63.0-rc.1)
226 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
227 | - React-RCTSettings (0.63.0-rc.1):
228 | - FBReactNativeSpec (= 0.63.0-rc.1)
229 | - Folly (= 2020.01.13.00)
230 | - RCTTypeSafety (= 0.63.0-rc.1)
231 | - React-Core/RCTSettingsHeaders (= 0.63.0-rc.1)
232 | - React-jsi (= 0.63.0-rc.1)
233 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
234 | - React-RCTText (0.63.0-rc.1):
235 | - React-Core/RCTTextHeaders (= 0.63.0-rc.1)
236 | - React-RCTVibration (0.63.0-rc.1):
237 | - FBReactNativeSpec (= 0.63.0-rc.1)
238 | - Folly (= 2020.01.13.00)
239 | - React-Core/RCTVibrationHeaders (= 0.63.0-rc.1)
240 | - React-jsi (= 0.63.0-rc.1)
241 | - ReactCommon/turbomodule/core (= 0.63.0-rc.1)
242 | - ReactCommon/turbomodule/core (0.63.0-rc.1):
243 | - DoubleConversion
244 | - Folly (= 2020.01.13.00)
245 | - glog
246 | - React-callinvoker (= 0.63.0-rc.1)
247 | - React-Core (= 0.63.0-rc.1)
248 | - React-cxxreact (= 0.63.0-rc.1)
249 | - React-jsi (= 0.63.0-rc.1)
250 | - RNCAsyncStorage (1.11.0):
251 | - React
252 | - RNCMaskedView (0.1.10):
253 | - React
254 | - RNGestureHandler (1.6.1):
255 | - React
256 | - RNReanimated (1.9.0):
257 | - React
258 | - RNScreens (2.8.0):
259 | - React
260 | - Yoga (1.14.0)
261 |
262 | DEPENDENCIES:
263 | - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
264 | - FBLazyVector (from `../../node_modules/react-native/Libraries/FBLazyVector`)
265 | - FBReactNativeSpec (from `../../node_modules/react-native/Libraries/FBReactNativeSpec`)
266 | - Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
267 | - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
268 | - RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
269 | - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
270 | - React (from `../../node_modules/react-native/`)
271 | - React-callinvoker (from `../../node_modules/react-native/ReactCommon/callinvoker`)
272 | - React-Core (from `../../node_modules/react-native/`)
273 | - React-Core/DevSupport (from `../../node_modules/react-native/`)
274 | - React-Core/RCTWebSocket (from `../../node_modules/react-native/`)
275 | - React-CoreModules (from `../../node_modules/react-native/React/CoreModules`)
276 | - React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`)
277 | - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
278 | - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
279 | - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
280 | - react-native-safe-area-context (from `../..`)
281 | - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`)
282 | - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
283 | - React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`)
284 | - React-RCTImage (from `../../node_modules/react-native/Libraries/Image`)
285 | - React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`)
286 | - React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`)
287 | - React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`)
288 | - React-RCTText (from `../../node_modules/react-native/Libraries/Text`)
289 | - React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`)
290 | - ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`)
291 | - "RNCAsyncStorage (from `../../node_modules/@react-native-community/async-storage`)"
292 | - "RNCMaskedView (from `../../node_modules/@react-native-community/masked-view`)"
293 | - RNGestureHandler (from `../../node_modules/react-native-gesture-handler`)
294 | - RNReanimated (from `../../node_modules/react-native-reanimated`)
295 | - RNScreens (from `../../node_modules/react-native-screens`)
296 | - Yoga (from `../../node_modules/react-native/ReactCommon/yoga`)
297 |
298 | SPEC REPOS:
299 | trunk:
300 | - boost-for-react-native
301 |
302 | EXTERNAL SOURCES:
303 | DoubleConversion:
304 | :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
305 | FBLazyVector:
306 | :path: "../../node_modules/react-native/Libraries/FBLazyVector"
307 | FBReactNativeSpec:
308 | :path: "../../node_modules/react-native/Libraries/FBReactNativeSpec"
309 | Folly:
310 | :podspec: "../../node_modules/react-native/third-party-podspecs/Folly.podspec"
311 | glog:
312 | :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
313 | RCTRequired:
314 | :path: "../../node_modules/react-native/Libraries/RCTRequired"
315 | RCTTypeSafety:
316 | :path: "../../node_modules/react-native/Libraries/TypeSafety"
317 | React:
318 | :path: "../../node_modules/react-native/"
319 | React-callinvoker:
320 | :path: "../../node_modules/react-native/ReactCommon/callinvoker"
321 | React-Core:
322 | :path: "../../node_modules/react-native/"
323 | React-CoreModules:
324 | :path: "../../node_modules/react-native/React/CoreModules"
325 | React-cxxreact:
326 | :path: "../../node_modules/react-native/ReactCommon/cxxreact"
327 | React-jsi:
328 | :path: "../../node_modules/react-native/ReactCommon/jsi"
329 | React-jsiexecutor:
330 | :path: "../../node_modules/react-native/ReactCommon/jsiexecutor"
331 | React-jsinspector:
332 | :path: "../../node_modules/react-native/ReactCommon/jsinspector"
333 | react-native-safe-area-context:
334 | :path: "../.."
335 | React-RCTActionSheet:
336 | :path: "../../node_modules/react-native/Libraries/ActionSheetIOS"
337 | React-RCTAnimation:
338 | :path: "../../node_modules/react-native/Libraries/NativeAnimation"
339 | React-RCTBlob:
340 | :path: "../../node_modules/react-native/Libraries/Blob"
341 | React-RCTImage:
342 | :path: "../../node_modules/react-native/Libraries/Image"
343 | React-RCTLinking:
344 | :path: "../../node_modules/react-native/Libraries/LinkingIOS"
345 | React-RCTNetwork:
346 | :path: "../../node_modules/react-native/Libraries/Network"
347 | React-RCTSettings:
348 | :path: "../../node_modules/react-native/Libraries/Settings"
349 | React-RCTText:
350 | :path: "../../node_modules/react-native/Libraries/Text"
351 | React-RCTVibration:
352 | :path: "../../node_modules/react-native/Libraries/Vibration"
353 | ReactCommon:
354 | :path: "../../node_modules/react-native/ReactCommon"
355 | RNCAsyncStorage:
356 | :path: "../../node_modules/@react-native-community/async-storage"
357 | RNCMaskedView:
358 | :path: "../../node_modules/@react-native-community/masked-view"
359 | RNGestureHandler:
360 | :path: "../../node_modules/react-native-gesture-handler"
361 | RNReanimated:
362 | :path: "../../node_modules/react-native-reanimated"
363 | RNScreens:
364 | :path: "../../node_modules/react-native-screens"
365 | Yoga:
366 | :path: "../../node_modules/react-native/ReactCommon/yoga"
367 |
368 | SPEC CHECKSUMS:
369 | boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
370 | DoubleConversion: cde416483dac037923206447da6e1454df403714
371 | FBLazyVector: 80d80617d51e24046a0bcc817cd9209027ecfaa9
372 | FBReactNativeSpec: b0fff079b3d224bd19ddd66857a9ebd879248c22
373 | Folly: b73c3869541e86821df3c387eb0af5f65addfab4
374 | glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
375 | RCTRequired: 6d452db9ed41ed479dded8b25cefaea171af100a
376 | RCTTypeSafety: 9f116cdf6450aae848567f03de76a519ce8b5ffa
377 | React: ae32f1a326e384e477a4130efaf35785cf66c482
378 | React-callinvoker: b1222d51ffbc55017208d26e168d76035ade9d53
379 | React-Core: b328df15e9952f937d4497d08821f908ddf63510
380 | React-CoreModules: ddbd7c6b62241597e467cedfa4acd89f1504f613
381 | React-cxxreact: 2e594ee40fb8666e2e48428a1767257a60873877
382 | React-jsi: a968acc454a107677da6027d2111221f57765de9
383 | React-jsiexecutor: c40a9c0e687bd7a44cc0a616ff4592a823732a0d
384 | React-jsinspector: 655f32d922ffb180714c0bec652e7e0923b5a5dd
385 | react-native-safe-area-context: 4fb3cdeb4a405ec19f18aca80ef2144381ffc761
386 | React-RCTActionSheet: ead415a8470cd3552f8cadce3b9b32e0d52e46a7
387 | React-RCTAnimation: 627dc8ad905b206c9bf10e9527079ef754b6a5dc
388 | React-RCTBlob: e70ce946cee9d400c5bfb0e8668dd11db852fa5a
389 | React-RCTImage: 9b8566568c0191e460baf4bee5a48a26efc2649c
390 | React-RCTLinking: 9a72da67543456af38b7fe01d851c247edb91e59
391 | React-RCTNetwork: 9939c3f1c757b03abd3bfda89a41b0989254e2a8
392 | React-RCTSettings: bb29c478fd69f557f939cf9e3a9f6f431df432bb
393 | React-RCTText: 1aa0fd4251b108777dd6218eb92d3613514b150f
394 | React-RCTVibration: b630e8fd023809796d47917d27caa343ade0678d
395 | ReactCommon: 6d0c6086911f7bf87b8406a92bb2ec66aeefd2e1
396 | RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
397 | RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
398 | RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
399 | RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
400 | RNScreens: 62211832af51e0aebcf6e8c36bcf7dd65592f244
401 | Yoga: 5d62aa8f4e862e282e19a25865ef8124c70f2711
402 |
403 | PODFILE CHECKSUM: 90edcd974101399fc4c4cb2f6ffd1ccb481b16ff
404 |
405 | COCOAPODS: 1.9.3
406 |
--------------------------------------------------------------------------------