@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.
Files changed (156) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +315 -0
  3. package/SystemBundles/e3c0cdeab56b8b622d4f0fd5a80812eafe199fa2287d0e3a09c5e573a1035350.bundle +11031 -0
  4. package/SystemBundles/e3c0cdeab56b8b622d4f0fd5a80812eafe199fa2287d0e3a09c5e573a1035350.map +1 -0
  5. package/SystemModules/amzn__react-native-safe-area-context-2.modules.txt +55 -0
  6. package/SystemModules/amzn__react-native-safe-area-context-2.private.modules.txt +91 -0
  7. package/SystemModules/keplerscript-system-bundle-config.json +7 -0
  8. package/THIRD_PARTY_NOTICES.txt +24 -0
  9. package/android/build.gradle +46 -0
  10. package/android/src/main/AndroidManifest.xml +6 -0
  11. package/android/src/main/java/com/th3rdwave/safeareacontext/EdgeInsets.java +22 -0
  12. package/android/src/main/java/com/th3rdwave/safeareacontext/InsetsChangeEvent.java +33 -0
  13. package/android/src/main/java/com/th3rdwave/safeareacontext/Rect.java +22 -0
  14. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaContextPackage.java +31 -0
  15. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaProvider.java +66 -0
  16. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaProviderManager.java +94 -0
  17. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaUtils.java +82 -0
  18. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaView.java +159 -0
  19. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewEdges.java +8 -0
  20. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewLocalData.java +27 -0
  21. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewManager.java +72 -0
  22. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewMode.java +6 -0
  23. package/android/src/main/java/com/th3rdwave/safeareacontext/SafeAreaViewShadowNode.java +201 -0
  24. package/android/src/main/java/com/th3rdwave/safeareacontext/SerializationUtils.java +52 -0
  25. package/ios/SafeAreaView/RCTView+SafeAreaCompat.h +16 -0
  26. package/ios/SafeAreaView/RCTView+SafeAreaCompat.m +57 -0
  27. package/ios/SafeAreaView/RNCSafeAreaProvider.h +13 -0
  28. package/ios/SafeAreaView/RNCSafeAreaProvider.m +65 -0
  29. package/ios/SafeAreaView/RNCSafeAreaProviderManager.h +9 -0
  30. package/ios/SafeAreaView/RNCSafeAreaProviderManager.m +61 -0
  31. package/ios/SafeAreaView/RNCSafeAreaShadowView.h +9 -0
  32. package/ios/SafeAreaView/RNCSafeAreaShadowView.m +180 -0
  33. package/ios/SafeAreaView/RNCSafeAreaView.h +21 -0
  34. package/ios/SafeAreaView/RNCSafeAreaView.m +118 -0
  35. package/ios/SafeAreaView/RNCSafeAreaViewEdges.h +9 -0
  36. package/ios/SafeAreaView/RNCSafeAreaViewEdges.m +13 -0
  37. package/ios/SafeAreaView/RNCSafeAreaViewLocalData.h +18 -0
  38. package/ios/SafeAreaView/RNCSafeAreaViewLocalData.m +16 -0
  39. package/ios/SafeAreaView/RNCSafeAreaViewManager.h +9 -0
  40. package/ios/SafeAreaView/RNCSafeAreaViewManager.m +25 -0
  41. package/ios/SafeAreaView/RNCSafeAreaViewMode.h +7 -0
  42. package/ios/SafeAreaView/RNCSafeAreaViewMode.m +11 -0
  43. package/ios/SafeAreaView.xcodeproj/project.pbxproj +338 -0
  44. package/kepler-compatibility.json +8 -0
  45. package/kepler-transformed-package-info.json +3 -0
  46. package/lib/commonjs/CompatNativeSafeAreaProvider.js +44 -0
  47. package/lib/commonjs/CompatNativeSafeAreaProvider.js.map +1 -0
  48. package/lib/commonjs/InitialWindow.js +13 -0
  49. package/lib/commonjs/InitialWindow.js.map +1 -0
  50. package/lib/commonjs/InitialWindow.kepler.js +15 -0
  51. package/lib/commonjs/InitialWindow.kepler.js.map +1 -0
  52. package/lib/commonjs/InitialWindow.native.js +15 -0
  53. package/lib/commonjs/InitialWindow.native.js.map +1 -0
  54. package/lib/commonjs/NativeSafeAreaProvider.js +132 -0
  55. package/lib/commonjs/NativeSafeAreaProvider.js.map +1 -0
  56. package/lib/commonjs/NativeSafeAreaProvider.kepler.js +9 -0
  57. package/lib/commonjs/NativeSafeAreaProvider.kepler.js.map +1 -0
  58. package/lib/commonjs/NativeSafeAreaProvider.macos.js +9 -0
  59. package/lib/commonjs/NativeSafeAreaProvider.macos.js.map +1 -0
  60. package/lib/commonjs/NativeSafeAreaProvider.native.js +9 -0
  61. package/lib/commonjs/NativeSafeAreaProvider.native.js.map +1 -0
  62. package/lib/commonjs/NativeSafeAreaProvider.windows.js +9 -0
  63. package/lib/commonjs/NativeSafeAreaProvider.windows.js.map +1 -0
  64. package/lib/commonjs/SafeArea.types.js +6 -0
  65. package/lib/commonjs/SafeArea.types.js.map +1 -0
  66. package/lib/commonjs/SafeAreaContext.js +113 -0
  67. package/lib/commonjs/SafeAreaContext.js.map +1 -0
  68. package/lib/commonjs/SafeAreaView.js +82 -0
  69. package/lib/commonjs/SafeAreaView.js.map +1 -0
  70. package/lib/commonjs/SafeAreaView.kepler.js +9 -0
  71. package/lib/commonjs/SafeAreaView.kepler.js.map +1 -0
  72. package/lib/commonjs/SafeAreaView.macos.js +9 -0
  73. package/lib/commonjs/SafeAreaView.macos.js.map +1 -0
  74. package/lib/commonjs/SafeAreaView.native.js +9 -0
  75. package/lib/commonjs/SafeAreaView.native.js.map +1 -0
  76. package/lib/commonjs/SafeAreaView.windows.js +9 -0
  77. package/lib/commonjs/SafeAreaView.windows.js.map +1 -0
  78. package/lib/commonjs/index.js +50 -0
  79. package/lib/commonjs/index.js.map +1 -0
  80. package/lib/commonjs/useWindowDimensions.js +32 -0
  81. package/lib/commonjs/useWindowDimensions.js.map +1 -0
  82. package/lib/module/CompatNativeSafeAreaProvider.js +35 -0
  83. package/lib/module/CompatNativeSafeAreaProvider.js.map +1 -0
  84. package/lib/module/InitialWindow.js +7 -0
  85. package/lib/module/InitialWindow.js.map +1 -0
  86. package/lib/module/InitialWindow.kepler.js +9 -0
  87. package/lib/module/InitialWindow.kepler.js.map +1 -0
  88. package/lib/module/InitialWindow.native.js +9 -0
  89. package/lib/module/InitialWindow.native.js.map +1 -0
  90. package/lib/module/NativeSafeAreaProvider.js +124 -0
  91. package/lib/module/NativeSafeAreaProvider.js.map +1 -0
  92. package/lib/module/NativeSafeAreaProvider.kepler.js +3 -0
  93. package/lib/module/NativeSafeAreaProvider.kepler.js.map +1 -0
  94. package/lib/module/NativeSafeAreaProvider.macos.js +3 -0
  95. package/lib/module/NativeSafeAreaProvider.macos.js.map +1 -0
  96. package/lib/module/NativeSafeAreaProvider.native.js +3 -0
  97. package/lib/module/NativeSafeAreaProvider.native.js.map +1 -0
  98. package/lib/module/NativeSafeAreaProvider.windows.js +3 -0
  99. package/lib/module/NativeSafeAreaProvider.windows.js.map +1 -0
  100. package/lib/module/SafeArea.types.js +2 -0
  101. package/lib/module/SafeArea.types.js.map +1 -0
  102. package/lib/module/SafeAreaContext.js +99 -0
  103. package/lib/module/SafeAreaContext.js.map +1 -0
  104. package/lib/module/SafeAreaView.js +74 -0
  105. package/lib/module/SafeAreaView.js.map +1 -0
  106. package/lib/module/SafeAreaView.kepler.js +3 -0
  107. package/lib/module/SafeAreaView.kepler.js.map +1 -0
  108. package/lib/module/SafeAreaView.macos.js +3 -0
  109. package/lib/module/SafeAreaView.macos.js.map +1 -0
  110. package/lib/module/SafeAreaView.native.js +3 -0
  111. package/lib/module/SafeAreaView.native.js.map +1 -0
  112. package/lib/module/SafeAreaView.windows.js +3 -0
  113. package/lib/module/SafeAreaView.windows.js.map +1 -0
  114. package/lib/module/index.js +5 -0
  115. package/lib/module/index.js.map +1 -0
  116. package/lib/module/useWindowDimensions.js +26 -0
  117. package/lib/module/useWindowDimensions.js.map +1 -0
  118. package/lib/typescript/src/CompatNativeSafeAreaProvider.d.ts +2 -0
  119. package/lib/typescript/src/InitialWindow.d.ts +6 -0
  120. package/lib/typescript/src/InitialWindow.kepler.d.ts +6 -0
  121. package/lib/typescript/src/InitialWindow.native.d.ts +6 -0
  122. package/lib/typescript/src/NativeSafeAreaProvider.d.ts +2 -0
  123. package/lib/typescript/src/NativeSafeAreaProvider.kepler.d.ts +2 -0
  124. package/lib/typescript/src/NativeSafeAreaProvider.macos.d.ts +2 -0
  125. package/lib/typescript/src/NativeSafeAreaProvider.native.d.ts +3 -0
  126. package/lib/typescript/src/NativeSafeAreaProvider.windows.d.ts +2 -0
  127. package/lib/typescript/src/SafeArea.types.d.ts +31 -0
  128. package/lib/typescript/src/SafeAreaContext.d.ts +30 -0
  129. package/lib/typescript/src/SafeAreaView.d.ts +2 -0
  130. package/lib/typescript/src/SafeAreaView.kepler.d.ts +2 -0
  131. package/lib/typescript/src/SafeAreaView.macos.d.ts +2 -0
  132. package/lib/typescript/src/SafeAreaView.native.d.ts +2 -0
  133. package/lib/typescript/src/SafeAreaView.windows.d.ts +2 -0
  134. package/lib/typescript/src/index.d.ts +4 -0
  135. package/lib/typescript/src/useWindowDimensions.d.ts +8 -0
  136. package/package.json +1 -0
  137. package/react-native-safe-area-context.podspec +19 -0
  138. package/react-native.config.js +8 -0
  139. package/src/CompatNativeSafeAreaProvider.tsx +29 -0
  140. package/src/InitialWindow.kepler.ts +10 -0
  141. package/src/InitialWindow.native.ts +16 -0
  142. package/src/InitialWindow.ts +8 -0
  143. package/src/NativeSafeAreaProvider.kepler.tsx +3 -0
  144. package/src/NativeSafeAreaProvider.macos.tsx +3 -0
  145. package/src/NativeSafeAreaProvider.native.tsx +6 -0
  146. package/src/NativeSafeAreaProvider.tsx +129 -0
  147. package/src/NativeSafeAreaProvider.windows.tsx +3 -0
  148. package/src/SafeArea.types.ts +43 -0
  149. package/src/SafeAreaContext.tsx +148 -0
  150. package/src/SafeAreaView.kepler.tsx +3 -0
  151. package/src/SafeAreaView.macos.tsx +3 -0
  152. package/src/SafeAreaView.native.tsx +6 -0
  153. package/src/SafeAreaView.tsx +85 -0
  154. package/src/SafeAreaView.windows.tsx +3 -0
  155. package/src/index.tsx +4 -0
  156. package/src/useWindowDimensions.tsx +35 -0
@@ -0,0 +1,8 @@
1
+ type DisplayMetrics = {
2
+ width: number;
3
+ height: number;
4
+ scale: number;
5
+ fontScale: number;
6
+ };
7
+ export default function useWindowDimensions(): DisplayMetrics;
8
+ export {};
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,8 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ macos: null,
5
+ windows: null,
6
+ },
7
+ },
8
+ };
@@ -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,8 @@
1
+ import { EdgeInsets, Metrics } from './SafeArea.types';
2
+
3
+ export const initialWindowMetrics: Metrics | null = null;
4
+
5
+ /**
6
+ * @deprecated
7
+ */
8
+ export const initialWindowSafeAreaInsets: EdgeInsets | null = null;
@@ -0,0 +1,3 @@
1
+ import { CompatNativeSafeAreaProvider } from './CompatNativeSafeAreaProvider';
2
+
3
+ export default CompatNativeSafeAreaProvider;
@@ -0,0 +1,3 @@
1
+ import { CompatNativeSafeAreaProvider } from './CompatNativeSafeAreaProvider';
2
+
3
+ export default CompatNativeSafeAreaProvider;
@@ -0,0 +1,6 @@
1
+ import { requireNativeComponent } from 'react-native';
2
+ import { NativeSafeAreaProviderProps } from './SafeArea.types';
3
+
4
+ export default requireNativeComponent<NativeSafeAreaProviderProps>(
5
+ 'RNCSafeAreaProvider',
6
+ );
@@ -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,3 @@
1
+ import { CompatNativeSafeAreaProvider } from './CompatNativeSafeAreaProvider';
2
+
3
+ export default CompatNativeSafeAreaProvider;
@@ -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,3 @@
1
+ import { View } from 'react-native';
2
+
3
+ export const SafeAreaView = View;
@@ -0,0 +1,3 @@
1
+ import { View } from 'react-native';
2
+
3
+ export const SafeAreaView = View;
@@ -0,0 +1,6 @@
1
+ import { requireNativeComponent } from 'react-native';
2
+ import { NativeSafeAreaViewProps } from './SafeArea.types';
3
+
4
+ export const SafeAreaView = requireNativeComponent<NativeSafeAreaViewProps>(
5
+ 'RNCSafeAreaView',
6
+ );
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ import { View } from 'react-native';
2
+
3
+ export const SafeAreaView = View;
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ export * from './SafeAreaContext';
2
+ export * from './SafeAreaView';
3
+ export * from './InitialWindow';
4
+ export * from './SafeArea.types';
@@ -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
+ }