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.
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/android/build.gradle +73 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +3 -0
- package/android/src/main/java/com/zoontek/rnedgetoedge/RNEdgeToEdgeModuleImpl.kt +120 -0
- package/android/src/main/java/com/zoontek/rnedgetoedge/RNEdgeToEdgePackage.kt +36 -0
- package/android/src/main/res/values/colors.xml +8 -0
- package/android/src/main/res/values/public.xml +4 -0
- package/android/src/main/res/values/styles.xml +16 -0
- package/android/src/main/res/values-night/styles.xml +6 -0
- package/android/src/main/res/values-v26/styles.xml +6 -0
- package/android/src/main/res/values-v27/styles.xml +8 -0
- package/android/src/main/res/values-v29/styles.xml +9 -0
- package/android/src/main/res/values-v30/styles.xml +9 -0
- package/android/src/newarch/com/zoontek/rnedgetoedge/RNEdgeToEdgeModule.kt +37 -0
- package/android/src/oldarch/com/zoontek/rnedgetoedge/RNBarsModule.kt +40 -0
- package/app.plugin.js +1 -0
- package/dist/commonjs/NativeRNEdgeToEdge.js +9 -0
- package/dist/commonjs/NativeRNEdgeToEdge.js.map +1 -0
- package/dist/commonjs/SystemBars.js +180 -0
- package/dist/commonjs/SystemBars.js.map +1 -0
- package/dist/commonjs/expo.js +35 -0
- package/dist/commonjs/expo.js.map +1 -0
- package/dist/commonjs/index.js +32 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/types.js +2 -0
- package/dist/commonjs/types.js.map +1 -0
- package/dist/module/NativeRNEdgeToEdge.js +5 -0
- package/dist/module/NativeRNEdgeToEdge.js.map +1 -0
- package/dist/module/SystemBars.js +175 -0
- package/dist/module/SystemBars.js.map +1 -0
- package/dist/module/expo.js +31 -0
- package/dist/module/expo.js.map +1 -0
- package/dist/module/index.js +5 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/types.js +2 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/NativeRNEdgeToEdge.d.ts +12 -0
- package/dist/typescript/NativeRNEdgeToEdge.d.ts.map +1 -0
- package/dist/typescript/SystemBars.d.ts +8 -0
- package/dist/typescript/SystemBars.d.ts.map +1 -0
- package/dist/typescript/expo.d.ts +4 -0
- package/dist/typescript/expo.d.ts.map +1 -0
- package/dist/typescript/index.d.ts +3 -0
- package/dist/typescript/index.d.ts.map +1 -0
- package/dist/typescript/types.d.ts +14 -0
- package/dist/typescript/types.d.ts.map +1 -0
- package/package.json +73 -6
- package/src/NativeRNEdgeToEdge.ts +14 -0
- package/src/SystemBars.ts +222 -0
- package/src/expo.ts +55 -0
- package/src/index.ts +2 -0
- 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.
|
|
4
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
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
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
|
+
};
|