react-native-edge-to-edge 0.0.0 → 0.1.1

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/android/build.gradle +73 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/AndroidManifestNew.xml +3 -0
  6. package/android/src/main/java/com/zoontek/rnedgetoedge/RNEdgeToEdgeModuleImpl.kt +120 -0
  7. package/android/src/main/java/com/zoontek/rnedgetoedge/RNEdgeToEdgePackage.kt +36 -0
  8. package/android/src/main/res/values/colors.xml +8 -0
  9. package/android/src/main/res/values/public.xml +4 -0
  10. package/android/src/main/res/values/styles.xml +16 -0
  11. package/android/src/main/res/values-night/styles.xml +6 -0
  12. package/android/src/main/res/values-v26/styles.xml +6 -0
  13. package/android/src/main/res/values-v27/styles.xml +8 -0
  14. package/android/src/main/res/values-v29/styles.xml +9 -0
  15. package/android/src/main/res/values-v30/styles.xml +9 -0
  16. package/android/src/newarch/com/zoontek/rnedgetoedge/RNEdgeToEdgeModule.kt +37 -0
  17. package/android/src/oldarch/com/zoontek/rnedgetoedge/RNBarsModule.kt +40 -0
  18. package/app.plugin.js +1 -0
  19. package/dist/commonjs/NativeRNEdgeToEdge.js +9 -0
  20. package/dist/commonjs/NativeRNEdgeToEdge.js.map +1 -0
  21. package/dist/commonjs/SystemBars.js +180 -0
  22. package/dist/commonjs/SystemBars.js.map +1 -0
  23. package/dist/commonjs/expo.js +35 -0
  24. package/dist/commonjs/expo.js.map +1 -0
  25. package/dist/commonjs/index.js +32 -0
  26. package/dist/commonjs/index.js.map +1 -0
  27. package/dist/commonjs/types.js +2 -0
  28. package/dist/commonjs/types.js.map +1 -0
  29. package/dist/module/NativeRNEdgeToEdge.js +5 -0
  30. package/dist/module/NativeRNEdgeToEdge.js.map +1 -0
  31. package/dist/module/SystemBars.js +175 -0
  32. package/dist/module/SystemBars.js.map +1 -0
  33. package/dist/module/expo.js +31 -0
  34. package/dist/module/expo.js.map +1 -0
  35. package/dist/module/index.js +5 -0
  36. package/dist/module/index.js.map +1 -0
  37. package/dist/module/package.json +1 -0
  38. package/dist/module/types.js +2 -0
  39. package/dist/module/types.js.map +1 -0
  40. package/dist/typescript/NativeRNEdgeToEdge.d.ts +12 -0
  41. package/dist/typescript/NativeRNEdgeToEdge.d.ts.map +1 -0
  42. package/dist/typescript/SystemBars.d.ts +8 -0
  43. package/dist/typescript/SystemBars.d.ts.map +1 -0
  44. package/dist/typescript/expo.d.ts +4 -0
  45. package/dist/typescript/expo.d.ts.map +1 -0
  46. package/dist/typescript/index.d.ts +3 -0
  47. package/dist/typescript/index.d.ts.map +1 -0
  48. package/dist/typescript/types.d.ts +14 -0
  49. package/dist/typescript/types.d.ts.map +1 -0
  50. package/package.json +73 -6
  51. package/src/NativeRNEdgeToEdge.ts +14 -0
  52. package/src/SystemBars.ts +222 -0
  53. package/src/expo.ts +55 -0
  54. package/src/index.ts +2 -0
  55. package/src/types.ts +12 -0
package/package.json CHANGED
@@ -1,11 +1,78 @@
1
1
  {
2
2
  "name": "react-native-edge-to-edge",
3
- "version": "0.0.0",
4
- "main": "index.js",
3
+ "version": "0.1.1",
4
+ "license": "MIT",
5
+ "description": "Effortlessly enable edge-to-edge display in React Native",
6
+ "author": "Mathieu Acthernoene <zoontek@gmail.com>",
7
+ "homepage": "https://github.com/zoontek/react-native-edge-to-edge",
8
+ "main": "dist/commonjs/index.js",
9
+ "module": "dist/module/index.js",
10
+ "types": "dist/typescript/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "src",
14
+ "app.plugin.js",
15
+ "android",
16
+ "!android/build",
17
+ "!android/.cxx",
18
+ "!android/.gradle"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/zoontek/react-native-edge-to-edge.git"
23
+ },
24
+ "keywords": [
25
+ "react",
26
+ "react-native",
27
+ "edge-to-edge",
28
+ "status-bar",
29
+ "navigation-bar",
30
+ "system-bar",
31
+ "system-bars"
32
+ ],
5
33
  "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1"
34
+ "clean": "rm -rf dist",
35
+ "format": "prettier '**/*' -u -w",
36
+ "typecheck": "tsc --noEmit",
37
+ "build": "yarn clean && bob build && rm -rf dist/commonjs/package.json",
38
+ "prepack": "prettier '**/*' -u -c && yarn typecheck && yarn build"
7
39
  },
8
- "author": "",
9
- "license": "MIT",
10
- "description": ""
40
+ "react-native-builder-bob": {
41
+ "source": "src",
42
+ "output": "dist",
43
+ "targets": [
44
+ "commonjs",
45
+ "module",
46
+ "typescript"
47
+ ]
48
+ },
49
+ "prettier": {
50
+ "plugins": [
51
+ "prettier-plugin-organize-imports"
52
+ ]
53
+ },
54
+ "peerDependencies": {
55
+ "react": ">=18.2.0",
56
+ "react-native": ">=0.73.0"
57
+ },
58
+ "devDependencies": {
59
+ "@babel/core": "^7.20.0",
60
+ "@babel/preset-env": "^7.20.0",
61
+ "@expo/config-plugins": "^7.0.0 || ^8.0.0",
62
+ "@types/react": "^18.2.6",
63
+ "prettier": "^3.3.3",
64
+ "prettier-plugin-organize-imports": "^4.1.0",
65
+ "react": "18.3.1",
66
+ "react-native": "0.75.4",
67
+ "react-native-builder-bob": "^0.30.2",
68
+ "typescript": "^5.6.3"
69
+ },
70
+ "codegenConfig": {
71
+ "name": "RNEdgeToEdgeSpec",
72
+ "type": "modules",
73
+ "jsSrcsDir": "./src",
74
+ "android": {
75
+ "javaPackageName": "com.zoontek.rnedgetoedge"
76
+ }
77
+ }
11
78
  }
@@ -0,0 +1,14 @@
1
+ import type { TurboModule } from "react-native";
2
+ import { TurboModuleRegistry } from "react-native";
3
+
4
+ type SystemBarsConfig = {
5
+ statusBarHidden: boolean | undefined;
6
+ statusBarStyle: string | undefined;
7
+ navigationBarHidden: boolean | undefined;
8
+ };
9
+
10
+ export interface Spec extends TurboModule {
11
+ setSystemBarsConfig(config: SystemBarsConfig): void;
12
+ }
13
+
14
+ export default TurboModuleRegistry.get<Spec>("RNEdgeToEdge");
@@ -0,0 +1,222 @@
1
+ import { useEffect, useMemo, useRef } from "react";
2
+ import { Appearance, Platform, StatusBar, useColorScheme } from "react-native";
3
+ import NativeModule from "./NativeRNEdgeToEdge";
4
+ import { SystemBarsEntry, SystemBarsProps } from "./types";
5
+
6
+ function getColorScheme(): "light" | "dark" {
7
+ return Appearance?.getColorScheme() ?? "light";
8
+ }
9
+
10
+ /**
11
+ * Merges the entries stack.
12
+ */
13
+ function mergeEntriesStack(
14
+ entriesStack: SystemBarsEntry[],
15
+ ): SystemBarsEntry | null {
16
+ const mergedEntry = entriesStack.reduce<SystemBarsEntry>(
17
+ (prev, cur) => {
18
+ for (const prop in cur) {
19
+ if (cur[prop as keyof SystemBarsEntry] != null) {
20
+ // @ts-expect-error
21
+ prev[prop] = cur[prop];
22
+ }
23
+ }
24
+ return prev;
25
+ },
26
+ {
27
+ statusBarStyle: undefined,
28
+ statusBarHidden: undefined,
29
+ navigationBarHidden: undefined,
30
+ },
31
+ );
32
+
33
+ if (
34
+ mergedEntry.statusBarStyle == null &&
35
+ mergedEntry.statusBarHidden == null &&
36
+ mergedEntry.navigationBarHidden == null
37
+ ) {
38
+ return null;
39
+ } else {
40
+ return mergedEntry;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Returns an object to insert in the props stack from the props.
46
+ */
47
+ function createStackEntry(props: SystemBarsProps): SystemBarsEntry {
48
+ return {
49
+ statusBarStyle: props.style,
50
+ statusBarHidden:
51
+ typeof props.hidden === "boolean"
52
+ ? props.hidden
53
+ : props.hidden?.statusBar,
54
+ navigationBarHidden:
55
+ typeof props.hidden === "boolean"
56
+ ? props.hidden
57
+ : props.hidden?.navigationBar,
58
+ };
59
+ }
60
+
61
+ const entriesStack: SystemBarsEntry[] = [];
62
+
63
+ // Timer for updating the native module values at the end of the frame.
64
+ let updateImmediate: NodeJS.Immediate | null = null;
65
+
66
+ // The current merged values from the entries stack.
67
+ let currentMergedEntries: {
68
+ statusBarStyle: "light" | "dark" | undefined;
69
+ statusBarHidden: boolean | undefined;
70
+ navigationBarHidden: boolean | undefined;
71
+ } | null = null;
72
+
73
+ /**
74
+ * Updates the native system bars with the entries from the stack.
75
+ */
76
+ function updateEntriesStack() {
77
+ if (updateImmediate != null) {
78
+ clearImmediate(updateImmediate);
79
+ }
80
+
81
+ updateImmediate = setImmediate(() => {
82
+ const mergedEntries = mergeEntriesStack(entriesStack);
83
+
84
+ if (mergedEntries != null) {
85
+ const { statusBarHidden, navigationBarHidden } = mergedEntries;
86
+
87
+ const statusBarStyle: "light" | "dark" | undefined =
88
+ mergedEntries.statusBarStyle === "auto"
89
+ ? getColorScheme() === "light"
90
+ ? "dark"
91
+ : "light"
92
+ : mergedEntries.statusBarStyle;
93
+
94
+ if (
95
+ currentMergedEntries?.statusBarStyle !== statusBarStyle ||
96
+ currentMergedEntries?.statusBarHidden !== statusBarHidden ||
97
+ currentMergedEntries?.navigationBarHidden !== navigationBarHidden
98
+ ) {
99
+ if (Platform.OS === "android") {
100
+ NativeModule?.setSystemBarsConfig({
101
+ statusBarStyle,
102
+ statusBarHidden,
103
+ navigationBarHidden,
104
+ });
105
+ } else if (Platform.OS === "ios") {
106
+ // Emulate android behavior with react-native StatusBar
107
+ if (statusBarStyle != null) {
108
+ StatusBar.setBarStyle(`${statusBarStyle}-content`, true);
109
+ }
110
+ if (statusBarHidden != null) {
111
+ StatusBar.setHidden(statusBarHidden, "fade"); // 'slide' doesn't work in this context
112
+ }
113
+ }
114
+ }
115
+
116
+ currentMergedEntries = {
117
+ statusBarStyle,
118
+ statusBarHidden,
119
+ navigationBarHidden,
120
+ };
121
+ } else {
122
+ currentMergedEntries = null;
123
+ }
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Push a SystemBars entry onto the stack.
129
+ * The return value should be passed to `popStackEntry` when complete.
130
+ *
131
+ * @param props Object containing the SystemBars props to use in the stack entry.
132
+ */
133
+ function pushStackEntry(props: SystemBarsProps): SystemBarsEntry {
134
+ const entry = createStackEntry(props);
135
+ entriesStack.push(entry);
136
+ updateEntriesStack();
137
+ return entry;
138
+ }
139
+
140
+ /**
141
+ * Pop a SystemBars entry from the stack.
142
+ *
143
+ * @param entry Entry returned from `pushStackEntry`.
144
+ */
145
+ function popStackEntry(entry: SystemBarsEntry): void {
146
+ const index = entriesStack.indexOf(entry);
147
+ if (index !== -1) {
148
+ entriesStack.splice(index, 1);
149
+ }
150
+ updateEntriesStack();
151
+ }
152
+
153
+ /**
154
+ * Replace an existing SystemBars stack entry with new props.
155
+ *
156
+ * @param entry Entry returned from `pushStackEntry` to replace.
157
+ * @param props Object containing the SystemBars props to use in the replacement stack entry.
158
+ */
159
+ function replaceStackEntry(
160
+ entry: SystemBarsEntry,
161
+ props: SystemBarsProps,
162
+ ): SystemBarsEntry {
163
+ const newEntry = createStackEntry(props);
164
+ const index = entriesStack.indexOf(entry);
165
+ if (index !== -1) {
166
+ entriesStack[index] = newEntry;
167
+ }
168
+ updateEntriesStack();
169
+ return newEntry;
170
+ }
171
+
172
+ export function SystemBars({ hidden, style }: SystemBarsProps) {
173
+ const statusBarHidden =
174
+ typeof hidden === "boolean" ? hidden : hidden?.statusBar;
175
+ const navigationBarHidden =
176
+ typeof hidden === "boolean" ? hidden : hidden?.navigationBar;
177
+
178
+ const stableProps = useMemo<SystemBarsProps>(
179
+ () => ({
180
+ hidden:
181
+ statusBarHidden === navigationBarHidden
182
+ ? statusBarHidden
183
+ : { statusBar: statusBarHidden, navigationBar: navigationBarHidden },
184
+ style,
185
+ }),
186
+ [style, statusBarHidden, navigationBarHidden],
187
+ );
188
+
189
+ const colorScheme = useColorScheme();
190
+ const stackEntryRef = useRef<SystemBarsEntry | null>(null);
191
+
192
+ useEffect(() => {
193
+ // Every time a SystemBars component is mounted, we push it's prop to a stack
194
+ // and always update the native system bars with the props from the top of then
195
+ // stack. This allows having multiple SystemBars components and the one that is
196
+ // added last or is deeper in the view hierarchy will have priority.
197
+ stackEntryRef.current = pushStackEntry(stableProps);
198
+
199
+ return () => {
200
+ // When a SystemBars is unmounted, remove itself from the stack and update
201
+ // the native bars with the next props.
202
+ if (stackEntryRef.current) {
203
+ popStackEntry(stackEntryRef.current);
204
+ }
205
+ };
206
+ }, []);
207
+
208
+ useEffect(() => {
209
+ if (stackEntryRef.current) {
210
+ stackEntryRef.current = replaceStackEntry(
211
+ stackEntryRef.current,
212
+ stableProps,
213
+ );
214
+ }
215
+ }, [colorScheme, stableProps]);
216
+
217
+ return null;
218
+ }
219
+
220
+ SystemBars.pushStackEntry = pushStackEntry;
221
+ SystemBars.popStackEntry = popStackEntry;
222
+ SystemBars.replaceStackEntry = replaceStackEntry;
package/src/expo.ts ADDED
@@ -0,0 +1,55 @@
1
+ import {
2
+ ConfigPlugin,
3
+ createRunOncePlugin,
4
+ withAndroidStyles,
5
+ } from "@expo/config-plugins";
6
+
7
+ const withAndroidEdgeToEdgeTheme: ConfigPlugin = (config) => {
8
+ const ignoreList = new Set([
9
+ "android:enforceNavigationBarContrast",
10
+ "android:enforceStatusBarContrast",
11
+ "android:fitsSystemWindows",
12
+ "android:navigationBarColor",
13
+ "android:statusBarColor",
14
+ "android:windowDrawsSystemBarBackgrounds",
15
+ "android:windowLayoutInDisplayCutoutMode",
16
+ "android:windowLightNavigationBar",
17
+ "android:windowLightStatusBar",
18
+ "android:windowTranslucentNavigation",
19
+ "android:windowTranslucentStatus",
20
+ ]);
21
+
22
+ return withAndroidStyles(config, (config) => {
23
+ const { userInterfaceStyle = "light" } = config;
24
+
25
+ config.modResults.resources.style = config.modResults.resources.style?.map(
26
+ (style): typeof style => {
27
+ if (style.$.name === "AppTheme") {
28
+ style.$.parent = "Theme.EdgeToEdge";
29
+
30
+ if (style.item != null) {
31
+ style.item = style.item.filter(
32
+ (item) => !ignoreList.has(item.$.name),
33
+ );
34
+ }
35
+
36
+ if (userInterfaceStyle !== "automatic") {
37
+ style.item.push({
38
+ $: { name: "android:windowLightStatusBar" },
39
+ _: String(userInterfaceStyle === "light"),
40
+ });
41
+ }
42
+ }
43
+
44
+ return style;
45
+ },
46
+ );
47
+
48
+ return config;
49
+ });
50
+ };
51
+
52
+ export default createRunOncePlugin(
53
+ withAndroidEdgeToEdgeTheme,
54
+ "react-native-edge-to-edge",
55
+ );
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { SystemBars } from "./SystemBars";
2
+ export { SystemBarStyle, SystemBarsEntry, SystemBarsProps } from "./types";
package/src/types.ts ADDED
@@ -0,0 +1,12 @@
1
+ export type SystemBarStyle = "auto" | "light" | "dark";
2
+
3
+ export type SystemBarsEntry = {
4
+ statusBarHidden: boolean | undefined;
5
+ statusBarStyle: SystemBarStyle | undefined;
6
+ navigationBarHidden: boolean | undefined;
7
+ };
8
+
9
+ export type SystemBarsProps = {
10
+ style?: SystemBarStyle;
11
+ hidden?: boolean | { statusBar?: boolean; navigationBar?: boolean };
12
+ };