@amazon-devices/react-native-safe-area-context 2.0.1758683737
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +315 -0
- package/SystemBundles/e3c0cdeab56b8b622d4f0fd5a80812eafe199fa2287d0e3a09c5e573a1035350.bundle +11031 -0
- package/SystemBundles/e3c0cdeab56b8b622d4f0fd5a80812eafe199fa2287d0e3a09c5e573a1035350.map +1 -0
- package/SystemModules/amzn__react-native-safe-area-context-2.modules.txt +55 -0
- package/SystemModules/amzn__react-native-safe-area-context-2.private.modules.txt +91 -0
- package/SystemModules/keplerscript-system-bundle-config.json +7 -0
- package/THIRD_PARTY_NOTICES.txt +24 -0
- package/android/build.gradle +46 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/EdgeInsets.java +22 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/InsetsChangeEvent.java +33 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/Rect.java +22 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaContextPackage.java +31 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaProvider.java +66 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaProviderManager.java +94 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaUtils.java +82 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaView.java +159 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewEdges.java +8 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewLocalData.java +27 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewManager.java +72 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewMode.java +6 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewShadowNode.java +201 -0
- package/android/src/main/java/com/th3rdwave/safeareacontext/SerializationUtils.java +52 -0
- package/ios/SafeAreaView/RCTView+SafeAreaCompat.h +16 -0
- package/ios/SafeAreaView/RCTView+SafeAreaCompat.m +57 -0
- package/ios/SafeAreaView/RNCSafeAreaProvider.h +13 -0
- package/ios/SafeAreaView/RNCSafeAreaProvider.m +65 -0
- package/ios/SafeAreaView/RNCSafeAreaProviderManager.h +9 -0
- package/ios/SafeAreaView/RNCSafeAreaProviderManager.m +61 -0
- package/ios/SafeAreaView/RNCSafeAreaShadowView.h +9 -0
- package/ios/SafeAreaView/RNCSafeAreaShadowView.m +180 -0
- package/ios/SafeAreaView/RNCSafeAreaView.h +21 -0
- package/ios/SafeAreaView/RNCSafeAreaView.m +118 -0
- package/ios/SafeAreaView/RNCSafeAreaViewEdges.h +9 -0
- package/ios/SafeAreaView/RNCSafeAreaViewEdges.m +13 -0
- package/ios/SafeAreaView/RNCSafeAreaViewLocalData.h +18 -0
- package/ios/SafeAreaView/RNCSafeAreaViewLocalData.m +16 -0
- package/ios/SafeAreaView/RNCSafeAreaViewManager.h +9 -0
- package/ios/SafeAreaView/RNCSafeAreaViewManager.m +25 -0
- package/ios/SafeAreaView/RNCSafeAreaViewMode.h +7 -0
- package/ios/SafeAreaView/RNCSafeAreaViewMode.m +11 -0
- package/ios/SafeAreaView.xcodeproj/project.pbxproj +338 -0
- package/kepler-compatibility.json +8 -0
- package/kepler-transformed-package-info.json +3 -0
- package/lib/commonjs/CompatNativeSafeAreaProvider.js +44 -0
- package/lib/commonjs/CompatNativeSafeAreaProvider.js.map +1 -0
- package/lib/commonjs/InitialWindow.js +13 -0
- package/lib/commonjs/InitialWindow.js.map +1 -0
- package/lib/commonjs/InitialWindow.kepler.js +15 -0
- package/lib/commonjs/InitialWindow.kepler.js.map +1 -0
- package/lib/commonjs/InitialWindow.native.js +15 -0
- package/lib/commonjs/InitialWindow.native.js.map +1 -0
- package/lib/commonjs/NativeSafeAreaProvider.js +132 -0
- package/lib/commonjs/NativeSafeAreaProvider.js.map +1 -0
- package/lib/commonjs/NativeSafeAreaProvider.kepler.js +9 -0
- package/lib/commonjs/NativeSafeAreaProvider.kepler.js.map +1 -0
- package/lib/commonjs/NativeSafeAreaProvider.macos.js +9 -0
- package/lib/commonjs/NativeSafeAreaProvider.macos.js.map +1 -0
- package/lib/commonjs/NativeSafeAreaProvider.native.js +9 -0
- package/lib/commonjs/NativeSafeAreaProvider.native.js.map +1 -0
- package/lib/commonjs/NativeSafeAreaProvider.windows.js +9 -0
- package/lib/commonjs/NativeSafeAreaProvider.windows.js.map +1 -0
- package/lib/commonjs/SafeArea.types.js +6 -0
- package/lib/commonjs/SafeArea.types.js.map +1 -0
- package/lib/commonjs/SafeAreaContext.js +113 -0
- package/lib/commonjs/SafeAreaContext.js.map +1 -0
- package/lib/commonjs/SafeAreaView.js +82 -0
- package/lib/commonjs/SafeAreaView.js.map +1 -0
- package/lib/commonjs/SafeAreaView.kepler.js +9 -0
- package/lib/commonjs/SafeAreaView.kepler.js.map +1 -0
- package/lib/commonjs/SafeAreaView.macos.js +9 -0
- package/lib/commonjs/SafeAreaView.macos.js.map +1 -0
- package/lib/commonjs/SafeAreaView.native.js +9 -0
- package/lib/commonjs/SafeAreaView.native.js.map +1 -0
- package/lib/commonjs/SafeAreaView.windows.js +9 -0
- package/lib/commonjs/SafeAreaView.windows.js.map +1 -0
- package/lib/commonjs/index.js +50 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/useWindowDimensions.js +32 -0
- package/lib/commonjs/useWindowDimensions.js.map +1 -0
- package/lib/module/CompatNativeSafeAreaProvider.js +35 -0
- package/lib/module/CompatNativeSafeAreaProvider.js.map +1 -0
- package/lib/module/InitialWindow.js +7 -0
- package/lib/module/InitialWindow.js.map +1 -0
- package/lib/module/InitialWindow.kepler.js +9 -0
- package/lib/module/InitialWindow.kepler.js.map +1 -0
- package/lib/module/InitialWindow.native.js +9 -0
- package/lib/module/InitialWindow.native.js.map +1 -0
- package/lib/module/NativeSafeAreaProvider.js +124 -0
- package/lib/module/NativeSafeAreaProvider.js.map +1 -0
- package/lib/module/NativeSafeAreaProvider.kepler.js +3 -0
- package/lib/module/NativeSafeAreaProvider.kepler.js.map +1 -0
- package/lib/module/NativeSafeAreaProvider.macos.js +3 -0
- package/lib/module/NativeSafeAreaProvider.macos.js.map +1 -0
- package/lib/module/NativeSafeAreaProvider.native.js +3 -0
- package/lib/module/NativeSafeAreaProvider.native.js.map +1 -0
- package/lib/module/NativeSafeAreaProvider.windows.js +3 -0
- package/lib/module/NativeSafeAreaProvider.windows.js.map +1 -0
- package/lib/module/SafeArea.types.js +2 -0
- package/lib/module/SafeArea.types.js.map +1 -0
- package/lib/module/SafeAreaContext.js +99 -0
- package/lib/module/SafeAreaContext.js.map +1 -0
- package/lib/module/SafeAreaView.js +74 -0
- package/lib/module/SafeAreaView.js.map +1 -0
- package/lib/module/SafeAreaView.kepler.js +3 -0
- package/lib/module/SafeAreaView.kepler.js.map +1 -0
- package/lib/module/SafeAreaView.macos.js +3 -0
- package/lib/module/SafeAreaView.macos.js.map +1 -0
- package/lib/module/SafeAreaView.native.js +3 -0
- package/lib/module/SafeAreaView.native.js.map +1 -0
- package/lib/module/SafeAreaView.windows.js +3 -0
- package/lib/module/SafeAreaView.windows.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/useWindowDimensions.js +26 -0
- package/lib/module/useWindowDimensions.js.map +1 -0
- package/lib/typescript/src/CompatNativeSafeAreaProvider.d.ts +2 -0
- package/lib/typescript/src/InitialWindow.d.ts +6 -0
- package/lib/typescript/src/InitialWindow.kepler.d.ts +6 -0
- package/lib/typescript/src/InitialWindow.native.d.ts +6 -0
- package/lib/typescript/src/NativeSafeAreaProvider.d.ts +2 -0
- package/lib/typescript/src/NativeSafeAreaProvider.kepler.d.ts +2 -0
- package/lib/typescript/src/NativeSafeAreaProvider.macos.d.ts +2 -0
- package/lib/typescript/src/NativeSafeAreaProvider.native.d.ts +3 -0
- package/lib/typescript/src/NativeSafeAreaProvider.windows.d.ts +2 -0
- package/lib/typescript/src/SafeArea.types.d.ts +31 -0
- package/lib/typescript/src/SafeAreaContext.d.ts +30 -0
- package/lib/typescript/src/SafeAreaView.d.ts +2 -0
- package/lib/typescript/src/SafeAreaView.kepler.d.ts +2 -0
- package/lib/typescript/src/SafeAreaView.macos.d.ts +2 -0
- package/lib/typescript/src/SafeAreaView.native.d.ts +2 -0
- package/lib/typescript/src/SafeAreaView.windows.d.ts +2 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/useWindowDimensions.d.ts +8 -0
- package/package.json +1 -0
- package/react-native-safe-area-context.podspec +19 -0
- package/react-native.config.js +8 -0
- package/src/CompatNativeSafeAreaProvider.tsx +29 -0
- package/src/InitialWindow.kepler.ts +10 -0
- package/src/InitialWindow.native.ts +16 -0
- package/src/InitialWindow.ts +8 -0
- package/src/NativeSafeAreaProvider.kepler.tsx +3 -0
- package/src/NativeSafeAreaProvider.macos.tsx +3 -0
- package/src/NativeSafeAreaProvider.native.tsx +6 -0
- package/src/NativeSafeAreaProvider.tsx +129 -0
- package/src/NativeSafeAreaProvider.windows.tsx +3 -0
- package/src/SafeArea.types.ts +43 -0
- package/src/SafeAreaContext.tsx +148 -0
- package/src/SafeAreaView.kepler.tsx +3 -0
- package/src/SafeAreaView.macos.tsx +3 -0
- package/src/SafeAreaView.native.tsx +6 -0
- package/src/SafeAreaView.tsx +85 -0
- package/src/SafeAreaView.windows.tsx +3 -0
- package/src/index.tsx +4 -0
- package/src/useWindowDimensions.tsx +35 -0
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name": "@amazon-devices/react-native-safe-area-context", "version": "2.0.1758683737+3.2.0", "description": "A flexible way to handle safe area, also works on Android and web.", "main": "lib/commonjs/index.js", "module": "lib/module/index.js", "react-native": "src/index.tsx", "types": "lib/typescript/src/index.d.ts", "sideEffects": false, "files": ["src", "lib", "android", "ios", "*.podspec", "react-native.config.js", "!**/__tests__", "kepler-compatibility.json", "!example", "SystemModules", "SystemBundles", "THIRD_PARTY_NOTICES.txt", "kepler-transformed-package-info.json"], "author": "Janic Duplessis <janicduplessis@gmail.com>", "contributors": ["Evan Bacon <bacon@expo.io> (https://github.com/evanbacon)"], "homepage": "https://github.com/th3rdwave/react-native-safe-area-context#readme", "license": "See license in LICENSE", "scripts": {"start": "react-native start", "test": "yarn validate:prettier && yarn validate:eslint && yarn validate:typescript && yarn validate:jest", "validate:eslint": "eslint \"src/**/*.{js,ts,tsx}\" \"example/**/*.{js,ts,tsx}\"", "validate:typescript": "tsc --project ./ --noEmit", "validate:prettier": "prettier \"src/**/*.{js,ts,tsx}\" \"example/**/*.{js,ts,tsx}\" --check", "validate:jest": "jest", "example:ios": "cd ./example && react-native run-ios", "example:android": "cd ./example && react-native run-android --no-jetifier", "example:expo": "expo start --config example/app.json"}, "keywords": ["react-native", "react native", "react-native-web", "expo-web", "safe area", "view"], "kepler": {"temporary-alias-for-package": {"name": "react-native-safe-area-context", "version": "3.2.0"}}, "peerDependencies": {"react": "*", "react-native": "*"}, "dependencies": {}, "devDependencies": {"@react-native-community/async-storage": "1.11.0", "@react-native-community/bob": "0.14.5", "@react-native-community/eslint-config": "2.0.0", "@react-native-community/masked-view": "0.1.10", "@react-navigation/bottom-tabs": "5.5.2", "@react-navigation/native": "5.5.1", "@react-navigation/stack": "5.5.1", "@types/jest": "^27.0.3", "@types/react": "17.0.2", "@types/react-dom": "~17.0.2", "@types/react-test-renderer": "^16.9.2", "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "babel-plugin-module-resolver": "4.0.0", "eslint": "7.2.0", "eslint-config-prettier": "6.11.0", "eslint-plugin-prettier": "3.1.3", "jest": "26.0.1", "metro-react-native-babel-preset": "0.59.0", "prettier": "2.0.5", "react": "17.0.2", "react-dom": "17.0.2", "react-native": "0.72.0", "react-native-gesture-handler": "~2.13.0", "@amazon-devices/keplerscript-react-native-reanimated": "^2.0.0+2.1.0", "react-native-safe-area-view": "1.1.1", "react-native-screens": "~3.20.0", "react-native-web": "0.12.3", "react-navigation": "4.3.9", "react-navigation-stack": "2.7.0", "react-navigation-tabs": "2.8.13", "react-test-renderer": "16.13.1", "typescript": "^4.9.4"}, "repository": {"type": "git", "url": "https://github.com/th3rdwave/react-native-safe-area-context.git"}, "jest": {"preset": "react-native", "testEnvironment": "node", "clearMocks": true, "modulePathIgnorePatterns": ["<rootDir>/lib/"]}, "@react-native-community/bob": {"source": "src", "output": "lib", "targets": ["commonjs", "module", "typescript"]}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "@amazon-devices/react-native-safe-area-context"
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
|
|
11
|
+
s.authors = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = { :ios => "9.0", :tvos => "9.2" }
|
|
14
|
+
|
|
15
|
+
s.source = { :git => "https://github.com/th3rdwave/react-native-safe-area-context.git", :tag => "v#{s.version}" }
|
|
16
|
+
s.source_files = "ios/**/*.{h,m}"
|
|
17
|
+
|
|
18
|
+
s.dependency 'React-Core'
|
|
19
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { NativeSafeAreaProviderProps } from './SafeArea.types';
|
|
4
|
+
import useWindowDimensions from './useWindowDimensions';
|
|
5
|
+
|
|
6
|
+
export function CompatNativeSafeAreaProvider({
|
|
7
|
+
children,
|
|
8
|
+
style,
|
|
9
|
+
onInsetsChange,
|
|
10
|
+
}: NativeSafeAreaProviderProps) {
|
|
11
|
+
const window = useWindowDimensions();
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
const insets = {
|
|
14
|
+
top: 0,
|
|
15
|
+
bottom: 0,
|
|
16
|
+
left: 0,
|
|
17
|
+
right: 0,
|
|
18
|
+
};
|
|
19
|
+
const frame = {
|
|
20
|
+
x: 0,
|
|
21
|
+
y: 0,
|
|
22
|
+
width: window.width,
|
|
23
|
+
height: window.height,
|
|
24
|
+
};
|
|
25
|
+
// @ts-ignore: missing properties
|
|
26
|
+
onInsetsChange({ nativeEvent: { insets, frame } });
|
|
27
|
+
}, [onInsetsChange, window.height, window.width]);
|
|
28
|
+
return <View style={style}>{children}</View>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Metrics } from './SafeArea.types';
|
|
2
|
+
|
|
3
|
+
// amznmod_react for kepler, `initialWindowMetrics` is always null
|
|
4
|
+
// so assigning null directly,
|
|
5
|
+
// and only calling getViewManagerConfig() for non-kepler platforms
|
|
6
|
+
export const initialWindowMetrics = null as Metrics | null;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated
|
|
9
|
+
*/
|
|
10
|
+
export const initialWindowSafeAreaInsets = initialWindowMetrics?.insets;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { UIManager } from 'react-native';
|
|
2
|
+
import { Metrics } from './SafeArea.types';
|
|
3
|
+
|
|
4
|
+
const RNCSafeAreaProviderConfig = UIManager.getViewManagerConfig(
|
|
5
|
+
'RNCSafeAreaProvider',
|
|
6
|
+
) as any;
|
|
7
|
+
|
|
8
|
+
export const initialWindowMetrics = (RNCSafeAreaProviderConfig != null &&
|
|
9
|
+
RNCSafeAreaProviderConfig.Constants != null
|
|
10
|
+
? RNCSafeAreaProviderConfig.Constants.initialWindowMetrics
|
|
11
|
+
: null) as Metrics | null;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated
|
|
15
|
+
*/
|
|
16
|
+
export const initialWindowSafeAreaInsets = initialWindowMetrics?.insets;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { NativeSafeAreaProviderProps } from './SafeArea.types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TODO:
|
|
7
|
+
* Currently insets and frame are based on the window and are not
|
|
8
|
+
* relative to the provider view. This is inconsistent with iOS and Android.
|
|
9
|
+
* However in most cases if the provider view covers the screen this is not
|
|
10
|
+
* an issue.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const CSSTransitions: Record<string, string> = {
|
|
14
|
+
WebkitTransition: 'webkitTransitionEnd',
|
|
15
|
+
Transition: 'transitionEnd',
|
|
16
|
+
MozTransition: 'transitionend',
|
|
17
|
+
MSTransition: 'msTransitionEnd',
|
|
18
|
+
OTransition: 'oTransitionEnd',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function NativeSafeAreaView({
|
|
22
|
+
children,
|
|
23
|
+
style,
|
|
24
|
+
onInsetsChange,
|
|
25
|
+
}: NativeSafeAreaProviderProps) {
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
// Skip for SSR.
|
|
28
|
+
if (typeof document === 'undefined') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const element = createContextElement();
|
|
33
|
+
document.body.appendChild(element);
|
|
34
|
+
const onEnd = () => {
|
|
35
|
+
const {
|
|
36
|
+
paddingTop,
|
|
37
|
+
paddingBottom,
|
|
38
|
+
paddingLeft,
|
|
39
|
+
paddingRight,
|
|
40
|
+
} = window.getComputedStyle(element);
|
|
41
|
+
|
|
42
|
+
const insets = {
|
|
43
|
+
top: paddingTop ? parseInt(paddingTop, 10) : 0,
|
|
44
|
+
bottom: paddingBottom ? parseInt(paddingBottom, 10) : 0,
|
|
45
|
+
left: paddingLeft ? parseInt(paddingLeft, 10) : 0,
|
|
46
|
+
right: paddingRight ? parseInt(paddingRight, 10) : 0,
|
|
47
|
+
};
|
|
48
|
+
const frame = {
|
|
49
|
+
x: 0,
|
|
50
|
+
y: 0,
|
|
51
|
+
width: document.documentElement.offsetWidth,
|
|
52
|
+
height: document.documentElement.offsetHeight,
|
|
53
|
+
};
|
|
54
|
+
// @ts-ignore: missing properties
|
|
55
|
+
onInsetsChange({ nativeEvent: { insets, frame } });
|
|
56
|
+
};
|
|
57
|
+
element.addEventListener(getSupportedTransitionEvent(), onEnd);
|
|
58
|
+
onEnd();
|
|
59
|
+
return () => {
|
|
60
|
+
document.body.removeChild(element);
|
|
61
|
+
element.removeEventListener(getSupportedTransitionEvent(), onEnd);
|
|
62
|
+
};
|
|
63
|
+
}, [onInsetsChange]);
|
|
64
|
+
|
|
65
|
+
return <View style={style}>{children}</View>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let _supportedTransitionEvent: string | null = null;
|
|
69
|
+
function getSupportedTransitionEvent(): string {
|
|
70
|
+
if (_supportedTransitionEvent !== null) {
|
|
71
|
+
return _supportedTransitionEvent;
|
|
72
|
+
}
|
|
73
|
+
const element = document.createElement('invalidtype');
|
|
74
|
+
|
|
75
|
+
_supportedTransitionEvent = CSSTransitions.Transition;
|
|
76
|
+
for (const key in CSSTransitions) {
|
|
77
|
+
if (element.style[key as keyof CSSStyleDeclaration] !== undefined) {
|
|
78
|
+
_supportedTransitionEvent = CSSTransitions[key];
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return _supportedTransitionEvent;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type CssEnv = 'constant' | 'env';
|
|
86
|
+
|
|
87
|
+
let _supportedEnv: CssEnv | null = null;
|
|
88
|
+
function getSupportedEnv(): CssEnv {
|
|
89
|
+
if (_supportedEnv !== null) {
|
|
90
|
+
return _supportedEnv;
|
|
91
|
+
}
|
|
92
|
+
const { CSS } = window;
|
|
93
|
+
if (
|
|
94
|
+
CSS &&
|
|
95
|
+
CSS.supports &&
|
|
96
|
+
CSS.supports('top: constant(safe-area-inset-top)')
|
|
97
|
+
) {
|
|
98
|
+
_supportedEnv = 'constant';
|
|
99
|
+
} else {
|
|
100
|
+
_supportedEnv = 'env';
|
|
101
|
+
}
|
|
102
|
+
return _supportedEnv;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getInset(side: string): string {
|
|
106
|
+
return `${getSupportedEnv()}(safe-area-inset-${side})`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function createContextElement(): HTMLElement {
|
|
110
|
+
const element = document.createElement('div');
|
|
111
|
+
const { style } = element;
|
|
112
|
+
style.position = 'fixed';
|
|
113
|
+
style.left = '0';
|
|
114
|
+
style.top = '0';
|
|
115
|
+
style.width = '0';
|
|
116
|
+
style.height = '0';
|
|
117
|
+
style.zIndex = '-1';
|
|
118
|
+
style.overflow = 'hidden';
|
|
119
|
+
style.visibility = 'hidden';
|
|
120
|
+
// Bacon: Anything faster than this and the callback will be invoked too early with the wrong insets
|
|
121
|
+
style.transitionDuration = '0.05s';
|
|
122
|
+
style.transitionProperty = 'padding';
|
|
123
|
+
style.transitionDelay = '0s';
|
|
124
|
+
style.paddingTop = getInset('top');
|
|
125
|
+
style.paddingBottom = getInset('bottom');
|
|
126
|
+
style.paddingLeft = getInset('left');
|
|
127
|
+
style.paddingRight = getInset('right');
|
|
128
|
+
return element;
|
|
129
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NativeSyntheticEvent,
|
|
3
|
+
StyleProp,
|
|
4
|
+
ViewProps,
|
|
5
|
+
ViewStyle,
|
|
6
|
+
} from 'react-native';
|
|
7
|
+
|
|
8
|
+
export type Edge = 'top' | 'right' | 'bottom' | 'left';
|
|
9
|
+
|
|
10
|
+
export interface EdgeInsets {
|
|
11
|
+
top: number;
|
|
12
|
+
right: number;
|
|
13
|
+
bottom: number;
|
|
14
|
+
left: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Rect {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Metrics {
|
|
25
|
+
insets: EdgeInsets;
|
|
26
|
+
frame: Rect;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type InsetChangedEvent = NativeSyntheticEvent<Metrics>;
|
|
30
|
+
|
|
31
|
+
export type InsetChangeNativeCallback = (event: InsetChangedEvent) => void;
|
|
32
|
+
|
|
33
|
+
export interface NativeSafeAreaProviderProps {
|
|
34
|
+
children?: React.ReactNode;
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
onInsetsChange: InsetChangeNativeCallback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type NativeSafeAreaViewProps = ViewProps & {
|
|
40
|
+
children?: React.ReactNode;
|
|
41
|
+
mode?: 'padding' | 'margin';
|
|
42
|
+
edges?: ReadonlyArray<Edge>;
|
|
43
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
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<EdgeInsets | null>(
|
|
7
|
+
null,
|
|
8
|
+
);
|
|
9
|
+
SafeAreaInsetsContext.displayName = 'SafeAreaInsetsContext';
|
|
10
|
+
|
|
11
|
+
export const SafeAreaFrameContext = React.createContext<Rect | null>(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<ViewStyle>;
|
|
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<EdgeInsets | null>(
|
|
33
|
+
initialMetrics?.insets ?? initialSafeAreaInsets ?? parentInsets ?? null,
|
|
34
|
+
);
|
|
35
|
+
const [frame, setFrame] = React.useState<Rect>(
|
|
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
|
+
<NativeSafeAreaProvider
|
|
77
|
+
style={[styles.fill, style]}
|
|
78
|
+
onInsetsChange={onInsetsChange}
|
|
79
|
+
>
|
|
80
|
+
{insets != null ? (
|
|
81
|
+
<SafeAreaFrameContext.Provider value={frame}>
|
|
82
|
+
<SafeAreaInsetsContext.Provider value={insets}>
|
|
83
|
+
{children}
|
|
84
|
+
</SafeAreaInsetsContext.Provider>
|
|
85
|
+
</SafeAreaFrameContext.Provider>
|
|
86
|
+
) : null}
|
|
87
|
+
</NativeSafeAreaProvider>
|
|
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 `<SafeAreaProvider>` 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 `<SafeAreaProvider>` at the top of your app.',
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return frame;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function withSafeAreaInsets<T>(
|
|
124
|
+
WrappedComponent: React.ComponentType<T>,
|
|
125
|
+
) {
|
|
126
|
+
return React.forwardRef((props: T, ref: React.Ref<T>) => (
|
|
127
|
+
<SafeAreaConsumer>
|
|
128
|
+
{(insets) => <WrappedComponent {...props} insets={insets} ref={ref} />}
|
|
129
|
+
</SafeAreaConsumer>
|
|
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;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { useSafeAreaInsets } from './SafeAreaContext';
|
|
4
|
+
import { Edge, NativeSafeAreaViewProps } from './SafeArea.types';
|
|
5
|
+
|
|
6
|
+
// prettier-ignore
|
|
7
|
+
const TOP = 0b1000,
|
|
8
|
+
RIGHT = 0b0100,
|
|
9
|
+
BOTTOM = 0b0010,
|
|
10
|
+
LEFT = 0b0001,
|
|
11
|
+
ALL = 0b1111;
|
|
12
|
+
|
|
13
|
+
/* eslint-disable no-bitwise */
|
|
14
|
+
|
|
15
|
+
const edgeBitmaskMap: Record<Edge, number> = {
|
|
16
|
+
top: TOP,
|
|
17
|
+
right: RIGHT,
|
|
18
|
+
bottom: BOTTOM,
|
|
19
|
+
left: LEFT,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function SafeAreaView({
|
|
23
|
+
style = {},
|
|
24
|
+
mode,
|
|
25
|
+
edges,
|
|
26
|
+
...rest
|
|
27
|
+
}: NativeSafeAreaViewProps) {
|
|
28
|
+
const insets = useSafeAreaInsets();
|
|
29
|
+
|
|
30
|
+
const edgeBitmask =
|
|
31
|
+
edges != null
|
|
32
|
+
? edges.reduce((accum, edge) => accum | edgeBitmaskMap[edge], 0)
|
|
33
|
+
: ALL;
|
|
34
|
+
|
|
35
|
+
const appliedStyle = React.useMemo(() => {
|
|
36
|
+
const insetTop = edgeBitmask & TOP ? insets.top : 0;
|
|
37
|
+
const insetRight = edgeBitmask & RIGHT ? insets.right : 0;
|
|
38
|
+
const insetBottom = edgeBitmask & BOTTOM ? insets.bottom : 0;
|
|
39
|
+
const insetLeft = edgeBitmask & LEFT ? insets.left : 0;
|
|
40
|
+
|
|
41
|
+
const flatStyle = StyleSheet.flatten(style) as Record<string, number>;
|
|
42
|
+
|
|
43
|
+
if (mode === 'margin') {
|
|
44
|
+
const {
|
|
45
|
+
margin = 0,
|
|
46
|
+
marginVertical = margin,
|
|
47
|
+
marginHorizontal = margin,
|
|
48
|
+
marginTop = marginVertical,
|
|
49
|
+
marginRight = marginHorizontal,
|
|
50
|
+
marginBottom = marginVertical,
|
|
51
|
+
marginLeft = marginHorizontal,
|
|
52
|
+
} = flatStyle;
|
|
53
|
+
|
|
54
|
+
const marginStyle = {
|
|
55
|
+
marginTop: marginTop + insetTop,
|
|
56
|
+
marginRight: marginRight + insetRight,
|
|
57
|
+
marginBottom: marginBottom + insetBottom,
|
|
58
|
+
marginLeft: marginLeft + insetLeft,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return [style, marginStyle];
|
|
62
|
+
} else {
|
|
63
|
+
const {
|
|
64
|
+
padding = 0,
|
|
65
|
+
paddingVertical = padding,
|
|
66
|
+
paddingHorizontal = padding,
|
|
67
|
+
paddingTop = paddingVertical,
|
|
68
|
+
paddingRight = paddingHorizontal,
|
|
69
|
+
paddingBottom = paddingVertical,
|
|
70
|
+
paddingLeft = paddingHorizontal,
|
|
71
|
+
} = flatStyle;
|
|
72
|
+
|
|
73
|
+
const paddingStyle = {
|
|
74
|
+
paddingTop: paddingTop + insetTop,
|
|
75
|
+
paddingRight: paddingRight + insetRight,
|
|
76
|
+
paddingBottom: paddingBottom + insetBottom,
|
|
77
|
+
paddingLeft: paddingLeft + insetLeft,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return [style, paddingStyle];
|
|
81
|
+
}
|
|
82
|
+
}, [style, insets, mode, edgeBitmask]);
|
|
83
|
+
|
|
84
|
+
return <View style={appliedStyle} {...rest} />;
|
|
85
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Dimensions } from 'react-native';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
type DisplayMetrics = {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
scale: number;
|
|
8
|
+
fontScale: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Copied from https://github.com/facebook/react-native/blob/8d57691a/Libraries/Utilities/useWindowDimensions.js
|
|
12
|
+
// for compatibility with React Native < 0.61.
|
|
13
|
+
export default function useWindowDimensions(): DisplayMetrics {
|
|
14
|
+
const [dimensions, setDimensions] = useState(() => Dimensions.get('window'));
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
function handleChange({ window }: { window: DisplayMetrics }) {
|
|
17
|
+
if (
|
|
18
|
+
dimensions.width !== window.width ||
|
|
19
|
+
dimensions.height !== window.height ||
|
|
20
|
+
dimensions.scale !== window.scale ||
|
|
21
|
+
dimensions.fontScale !== window.fontScale
|
|
22
|
+
) {
|
|
23
|
+
setDimensions(window);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const subscription = Dimensions.addEventListener('change', handleChange);
|
|
27
|
+
// We might have missed an update between calling `get` in render and
|
|
28
|
+
// `addEventListener` in this handler, so we set it here. If there was
|
|
29
|
+
// no change, React will filter out this update as a no-op.
|
|
30
|
+
handleChange({ window: Dimensions.get('window') });
|
|
31
|
+
|
|
32
|
+
return () => subscription.remove();
|
|
33
|
+
}, [dimensions]);
|
|
34
|
+
return dimensions;
|
|
35
|
+
}
|