react-native-navigation-mode 1.0.4 → 1.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/README.md CHANGED
@@ -32,66 +32,23 @@
32
32
  </table>
33
33
  </div>
34
34
 
35
- ---
36
-
37
- ## 🤔 Why This Library?
38
-
39
- Android devices can use different navigation modes, but detecting which one is active has been a major pain point for React Native developers. Most existing solutions rely on unreliable workarounds:
40
-
41
- ### ❌ Common Bad Approaches
42
-
43
- - **Screen dimension calculations** - Breaks on different screen sizes and orientations
44
- - **Safe area inset guessing** - Inconsistent across devices and Android versions
45
- - **Margin-based detection** - Fragile and depends on UI layout changes
46
- - **Manual device databases** - Impossible to maintain for all Android devices
35
+ ------
47
36
 
48
- ### This Library's Solution
49
-
50
- This library uses **official Android APIs** to directly query the system's navigation configuration:
51
-
52
- - **`config_navBarInteractionMode`** - The actual system resource Android uses internally
53
- - **Settings.Secure provider** - Fallback method for reliable detection
54
- - **Zero guesswork** - No calculations, no assumptions, just direct system queries
55
-
56
- ### 🚀 Critical for Edge-to-Edge Mode
57
-
58
- With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now **essential**:
59
-
60
- - **Edge-to-edge enforcement** - Android 16 will remove the opt-out entirely
61
- - **Expo SDK 53+** - New projects use edge-to-edge by default
62
- - **React Native 0.79+** - Built-in support for 16KB page size and edge-to-edge
63
- - **Safe area management** - Critical for preventing content overlap with system bars (especially noticeable in 3-button navigation mode).
64
-
65
- ### Real-World Impact
66
-
67
- ```typescript
68
- // Before: Unreliable dimension-based guessing
69
- const isGesture = screenHeight === windowHeight; // 😢 Breaks easily
70
-
71
- // After: Direct system detection
72
- const isGesture = await isGestureNavigation(); // 🎯 Always accurate
73
- ```
74
-
75
- **Perfect for:**
76
-
77
- - 🎨 Adaptive UI layouts based on navigation type
78
- - 📱 Bottom sheet positioning and safe areas
79
- - 🧭 Navigation-aware component design
80
- - 🔄 Edge-to-edge layout compatibility
81
- - 📊 Analytics and user experience tracking
82
-
83
- ## ✨ Features
37
+ ## Key Features
84
38
 
85
39
  - 🎯 **Direct Native Detection** - No hacky workarounds or dimension-based guessing
86
- - ⚡ **Turbo Module** - Built with the latest React Native architecture
40
+ - ⚡ **Turbo Module** - Built for React Native New Architecture
87
41
  - 🔄 **Real-time Detection** - Accurate navigation mode identification
42
+ - 📏 **Navigation Bar Height** - Get exact navigation bar height in dp for precise UI calculations
88
43
  - 📱 **Cross Platform** - Android detection + iOS compatibility
89
44
  - 🎣 **React Hooks** - Easy integration with `useNavigationMode()`
90
45
  - 📦 **Zero Dependencies** - Lightweight and performant
91
46
  - 🛡️ **TypeScript** - Full type safety out of the box
92
47
  - ↕️ **Edge To Edge Support** - Full support for `react-native-edge-to-edge`
93
48
 
94
- ## Installation
49
+ ## 🚀 Quick Start
50
+
51
+ ### Installation
95
52
 
96
53
  Using yarn:
97
54
 
@@ -105,59 +62,130 @@ Using npm:
105
62
  npm install react-native-navigation-mode
106
63
  ```
107
64
 
108
- ### For React Native CLI
65
+ > **Note:** Auto-linking should handle setup automatically for all newer RN versions.
109
66
 
110
- Auto-linking handles setup automatically for React Native 0.60+.
67
+ ---
111
68
 
112
- ## Usage
69
+ ### Basic Usage
113
70
 
114
- ### Quick Check
71
+ ```tsx
72
+ import { useNavigationMode } from 'react-native-navigation-mode';
115
73
 
116
- ```typescript
117
- import { isGestureNavigation } from 'react-native-navigation-mode';
74
+ export default function App() {
75
+ const { navigationMode, loading, error } = useNavigationMode();
118
76
 
119
- // Simple boolean check
120
- const isGesture = await isGestureNavigation();
121
- console.log('Gesture navigation:', isGesture); // true/false
122
- ```
77
+ if (loading) return (<Text>Detecting navigation mode...</Text>);
78
+ if (error) return (<Text>Error: {error.message}</Text>);
123
79
 
124
- ### Detailed Information
80
+ return (
81
+ <View>
82
+ <Text>Navigation Type: {navigationMode?.type}</Text>
83
+ <Text>Gesture Navigation: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
84
+ <Text>Navigation Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
85
+ </View>
86
+ );
87
+ }
88
+ ```
125
89
 
126
- ```typescript
127
- import { getNavigationMode } from 'react-native-navigation-mode';
90
+ ---
128
91
 
129
- // Get comprehensive navigation info
130
- const navInfo = await getNavigationMode();
131
- console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
132
- ```
92
+ ## 🔧 API Reference
133
93
 
134
94
  ### React Hook (Recommended)
135
95
 
136
- ```typescript
137
- import React from 'react';
138
- import { View, Text } from 'react-native';
96
+ #### `useNavigationMode(): { navigationMode, loading, error }`
97
+
98
+ - Returned property types:
99
+
100
+ | Property | Type | Description |
101
+ | ------------- | ------------------------------ | ------------------------------------------------------------ |
102
+ | navigatioMode | `NavigationModeInfo` or `null` | All properties mentioned in [NavigationModeInfo](#navigationmodeinfo). |
103
+ | loading | `boolean` | Indicates if navigation mode info is being fetched. |
104
+ | error | `Error` | Typescript error object containing the cause of the error. |
105
+
106
+ The easiest way to detect navigation mode with loading and error states.
107
+
108
+ ```tsx
139
109
  import { useNavigationMode } from 'react-native-navigation-mode';
140
110
 
141
- export default function NavigationInfo() {
111
+ function MyComponent() {
142
112
  const { navigationMode, loading, error } = useNavigationMode();
143
-
144
- if (loading) return <Text>Detecting navigation mode...</Text>;
113
+
114
+ if (loading) return <Text>Loading...</Text>;
145
115
  if (error) return <Text>Error: {error.message}</Text>;
146
-
116
+
147
117
  return (
148
118
  <View>
149
119
  <Text>Navigation Type: {navigationMode?.type}</Text>
150
- <Text>Gesture Navigation: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
120
+ <Text>Is Gesture: {navigationMode?.isGestureNavigation ? 'Yes' : 'No'}</Text>
121
+ <Text>Bar Height: {navigationMode?.navigationBarHeight}dp</Text>
151
122
  </View>
152
123
  );
153
124
  }
154
125
  ```
155
126
 
156
- ### Conditional UI Rendering
127
+ ### Functions
128
+
129
+ #### `getNavigationMode(): Promise<`[NavigationModeInfo](#navigationmodeinfo)`>`
130
+
131
+ Returns comprehensive navigation mode information.
157
132
 
158
133
  ```typescript
159
- import React from 'react';
160
- import { View } from 'react-native';
134
+ import { getNavigationMode } from 'react-native-navigation-mode';
135
+
136
+ const navInfo = await getNavigationMode();
137
+ console.log('Navigation type:', navInfo.type); // '3_button', '2_button', 'gesture', or 'unknown'
138
+ ```
139
+
140
+ #### `isGestureNavigation(): Promise<boolean>`
141
+
142
+ Quick check if device is using gesture navigation.
143
+
144
+ ```typescript
145
+ import { isGestureNavigation } from 'react-native-navigation-mode';
146
+
147
+ const isGesture = await isGestureNavigation();
148
+ console.log('Gesture navigation:', isGesture); // true/false
149
+ ```
150
+
151
+ #### `getNavigationBarHeight(): Promise<number>`
152
+
153
+ Returns the navigation bar height in density-independent pixels (dp).
154
+
155
+ ```typescript
156
+ import { getNavigationBarHeight } from 'react-native-navigation-mode';
157
+
158
+ const height = await getNavigationBarHeight();
159
+ console.log('Navigation bar height:', height); // number (dp)
160
+ ```
161
+
162
+ ### Types
163
+
164
+ #### `NavigationModeInfo`
165
+
166
+ | Property | Type | Description |
167
+ | ------------------- | ----------------------------------------------------------- | --------------------------------------------------------- |
168
+ | type | `'3_button'` or `'2_button'` or `'gesture'` or `'unknown'` | 4 possible Android navigation modes that can be detected. |
169
+ | isGestureNavigation | `boolean` | Whether gesture navigation is active. |
170
+ | interactionMode | `number` or `undefined` | See [Navigation Mode Values](#navigation-mode-values) |
171
+ | navigationBarHeight | `number` or `undefined` | Navigation bar height in density-independent pixels (dp). |
172
+
173
+ ### Navigation Mode Values
174
+
175
+ | Android Mode | Type | Description |
176
+ | ------------ | ---------- | --------------------------------------------------- |
177
+ | 0 | `3_button` | Traditional Android navigation (Back, Home, Recent) |
178
+ | 1 | `2_button` | Two-button navigation (Back, Home) |
179
+ | 2 | `gesture` | Full gesture navigation |
180
+ | -1 | `unknown` | Could not determine navigation mode |
181
+
182
+ ---
183
+
184
+ ## 💡 Usage Examples
185
+
186
+ ### Adaptive UI Layout
187
+
188
+ ```tsx
161
189
  import { useNavigationMode } from 'react-native-navigation-mode';
162
190
 
163
191
  export default function AdaptiveUI() {
@@ -166,101 +194,173 @@ export default function AdaptiveUI() {
166
194
  return (
167
195
  <View
168
196
  style={{
169
- paddingBottom: navigationMode?.isGestureNavigation ? 34 : 48 // Adjust for gesture nav
170
- }}
171
- >
197
+ // paddingBottom using real navigation bar height
198
+ paddingBottom: navigationMode?.navigationBarHeight || 0,
199
+ }}>
172
200
  {/* Your content */}
173
201
  </View>
174
202
  );
175
203
  }
176
204
  ```
177
205
 
178
- ## API Reference
206
+ ### Conditional Rendering
179
207
 
180
- ### Functions
208
+ ```tsx
209
+ import { useNavigationMode } from 'react-native-navigation-mode';
181
210
 
182
- #### `getNavigationMode(): Promise<NavigationModeInfo>`
211
+ export default function ConditionalUI() {
212
+ const { navigationMode } = useNavigationMode();
183
213
 
184
- Returns comprehensive navigation mode information.
214
+ return (
215
+ <View>
216
+ {navigationMode?.isGestureNavigation && (
217
+ <Text>Swipe gestures are available!</Text>
218
+ )}
219
+
220
+ {navigationMode?.type === '3_button' && (
221
+ <Text>Traditional navigation buttons detected</Text>
222
+ )}
223
+ </View>
224
+ );
225
+ }
226
+ ```
185
227
 
186
- #### `isGestureNavigation(): Promise<boolean>`
228
+ ### Manual Detection
187
229
 
188
- Quick check if device is using gesture navigation.
230
+ ```typescript
231
+ import {
232
+ getNavigationMode,
233
+ isGestureNavigation,
234
+ getNavigationBarHeight
235
+ } from 'react-native-navigation-mode';
236
+
237
+ const checkNavigation = async () => {
238
+ // Get all info at once
239
+ const navInfo = await getNavigationMode();
240
+
241
+ // Or get specific info
242
+ const isGesture = await isGestureNavigation();
243
+ const barHeight = await getNavigationBarHeight();
244
+
245
+ console.log('Navigation info:', navInfo);
246
+ console.log('Is gesture:', isGesture);
247
+ console.log('Bar height:', barHeight);
248
+ };
249
+ ```
189
250
 
190
- ### Hooks
251
+ ---
191
252
 
192
- #### `useNavigationMode(): { navigationMode, loading, error }`
253
+ ## 🤔 Why This Library?
193
254
 
194
- React hook for navigation mode detection with loading and error states.
255
+ Android devices can use different navigation modes, but detecting which one is active has been a major pain point for React Native developers. Most existing solutions rely on unreliable workarounds:
195
256
 
196
- ### Types
257
+ ### ❌ Common Bad Approaches
197
258
 
198
- #### `NavigationModeInfo`
259
+ - **Screen dimension calculations** - Breaks on different screen sizes and orientations
260
+ - **Safe area inset guessing** - Inconsistent across devices and Android versions
261
+ - **Margin-based detection** - Fragile and depends on UI layout changes
262
+ - **Manual device databases** - Impossible to maintain for all Android devices
263
+
264
+ ### ✅ This Library's Solution
265
+
266
+ This library uses **official Android APIs** to directly query the system's navigation configuration:
267
+
268
+ - **`config_navBarInteractionMode`** - The actual system resource Android uses internally
269
+ - **Settings.Secure provider** - Fallback method for reliable detection
270
+ - **WindowInsets API** - Accurate navigation bar height detection
271
+ - **Zero guesswork** - No calculations, no assumptions, just direct system queries
272
+
273
+ ### 🚀 Critical for Edge-to-Edge Mode
274
+
275
+ With Android 15 enforcing edge-to-edge display for apps targeting API 35 and Google mandating this for Play Store updates starting August 31, 2025, proper navigation detection is now **essential**:
276
+
277
+ - **Edge-to-edge enforcement** - Android 16 will remove the opt-out entirely
278
+ - **Expo SDK 53+** - New projects use edge-to-edge by default
279
+ - **React Native 0.79+** - Built-in support for 16KB page size and edge-to-edge
280
+ - **Safe area management** - Critical for preventing content overlap with system bars
281
+
282
+ ### Real-World Impact
283
+
284
+ ```typescript
285
+ // Before: Unreliable dimension-based guessing
286
+ const isGesture = screenHeight === windowHeight; // 😢 Breaks easily
199
287
 
200
- | Property | Type | Description |
201
- | ------------------- | ---------------------------------------------- | ------------------------------------------------ |
202
- | type | `'3_button' \| '2_button' \| 'gesture' \| 'unknown'` | Navigation mode type |
203
- | isGestureNavigation | `boolean` | Whether gesture navigation is active |
204
- | interactionMode | `number \| undefined` | Raw Android interaction mode (0, 1, 2, or -1) |
288
+ // After: Direct system detection
289
+ const isGesture = await isGestureNavigation(); // 🎯 Always accurate
290
+ ```
291
+
292
+ **Perfect for:**
293
+
294
+ - 🎨 Adaptive UI layouts based on navigation type
295
+ - 📱 Bottom sheet positioning and safe areas
296
+ - 🧭 Navigation-aware component design
297
+ - 🔄 Edge-to-edge layout compatibility
298
+ - 📊 Analytics and user experience tracking
205
299
 
206
- ## Platform Support
300
+ ---
207
301
 
208
- | Platform | Support | Notes |
209
- |----------|---------|-------|
210
- | Android | ✅ Full | Detects all navigation modes via native Android APIs |
211
- | iOS | ✅ Compatible | Always returns `gesture` (iOS uses gesture navigation) |
302
+ ## 🛠️ Technical Details
303
+
304
+ ### Platform Support
305
+
306
+ | Platform | Support | Notes |
307
+ | -------- | ------------ | ------------------------------------------------------------ |
308
+ | Android | ✅ Full | Detects all navigation modes and navigation bar height via native Android APIs |
309
+ | iOS | ✅ Compatible | Always returns `gesture` and `navigationBarHeight: 0` (iOS uses gesture navigation) |
212
310
 
213
311
  ### Android Compatibility
214
312
 
215
313
  - **API 21+** - Basic navigation bar detection
216
314
  - **API 29+** - Full navigation mode detection (`config_navBarInteractionMode`)
217
315
  - **All versions** - Fallback detection methods included
316
+ - **API 30+** - WindowInsets-based navigation bar height detection
317
+ - **API 24-29** - Resource-based navigation bar height fallback
218
318
 
219
- ## How It Works
319
+ ### How It Works
220
320
 
221
321
  The library uses multiple detection methods for maximum accuracy:
222
322
 
223
323
  1. **`config_navBarInteractionMode`** - Official Android configuration (API 29+)
224
324
  2. **Settings Provider** - Checks `navigation_mode` system setting
225
- 3. **Navigation Bar Detection** - Validates navigation bar presence
226
- 4. **Hardware Key Detection** - Fallback for older devices
227
-
228
- ### Navigation Mode Values
229
-
230
- | Android Mode | Type | Description |
231
- |--------------|------|-------------|
232
- | 0 | `3_button` | Traditional Android navigation (Back, Home, Recent) |
233
- | 1 | `2_button` | Two-button navigation (Back, Home) |
234
- | 2 | `gesture` | Full gesture navigation |
235
- | -1 | `unknown` | Could not determine navigation mode |
325
+ 3. **WindowInsets API** - Accurate navigation bar height detection (API 30+)
326
+ 4. **Resource-based fallback** - Navigation bar height for older devices
236
327
 
237
- ## Notes
328
+ ### Performance Notes
238
329
 
239
- 1. 🍎 **iOS Behavior** - iOS always returns `isGestureNavigation: true` since iOS doesn't have 3-button navigation
330
+ 1. 🍎 **iOS Behavior** - iOS always returns `isGestureNavigation: true` and `navigationBarHeight: 0` since iOS doesn't have Android-style navigation bars
240
331
  2. ⚡ **Performance** - Turbo module ensures minimal performance impact
241
332
  3. 🔄 **Real-time** - Navigation mode is detected at call time, reflecting current device settings
242
333
 
243
- ## Troubleshooting
334
+ ---
335
+
336
+ ## 🐛 Troubleshooting
244
337
 
245
338
  ### Common Issues
246
339
 
247
340
  **"TurboModuleRegistry.getEnforcing(...) is not a function"**
341
+
248
342
  - Ensure you're using React Native 0.68+ with new architecture enabled
249
343
  - For older RN versions, the module will fallback gracefully
250
344
 
251
- **Always returns 'unknown' on Android**
345
+ **Always returns `'unknown'` on Android**
346
+
252
347
  - Check if your device/emulator supports the navigation mode APIs
253
348
  - Some custom ROMs may not expose standard Android navigation settings
254
349
 
255
- ## Contributing
350
+ **Navigation bar height returns `0`**
351
+
352
+ - This is normal on devices without navigation bars (some tablets)
353
+ - On older Android versions, fallback detection may not work on all devices
354
+
355
+ ## 🤝 Contributing
256
356
 
257
357
  See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
258
358
 
259
- ## License
359
+ ## 📄 License
260
360
 
261
361
  MIT
262
362
 
263
- ## Support the project
363
+ ## 💖 Support the Project
264
364
 
265
365
  <p align="center" valign="center">
266
366
  <a href="https://liberapay.com/FutureJJ/donate">
@@ -1,14 +1,15 @@
1
1
  package com.navigationmode
2
2
 
3
+ import android.app.Activity
3
4
  import android.content.Context
4
5
  import android.content.res.Resources
5
6
  import android.os.Build
6
7
  import android.provider.Settings
7
- import android.view.ViewConfiguration
8
+ import android.view.WindowInsets
9
+ import com.facebook.react.bridge.Arguments
8
10
  import com.facebook.react.bridge.Promise
9
11
  import com.facebook.react.bridge.ReactApplicationContext
10
12
  import com.facebook.react.bridge.WritableMap
11
- import com.facebook.react.bridge.Arguments
12
13
  import com.facebook.react.module.annotations.ReactModule
13
14
 
14
15
  @ReactModule(name = NavigationModeModule.NAME)
@@ -21,11 +22,49 @@ class NavigationModeModule(reactContext: ReactApplicationContext) :
21
22
 
22
23
  override fun getName(): String = NAME
23
24
 
25
+ private fun getNavigationBarHeight(context: Context): Int {
26
+ // Try to get Activity for WindowInsets
27
+ val activity = if (context is Activity) context else reactApplicationContext.currentActivity
28
+ val density = context.resources.displayMetrics.density // Get device density
29
+
30
+ if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
31
+ val insets = activity.window.decorView.rootWindowInsets
32
+ if (insets != null) {
33
+ val navBar = insets.getInsets(WindowInsets.Type.navigationBars())
34
+ return (navBar.bottom / density).toInt() // Convert pixels to dp
35
+ }
36
+ }
37
+
38
+ // Fallback to resource-based approach for API < 30
39
+ val resources = context.resources
40
+ val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
41
+ return if (resourceId > 0) {
42
+ // getDimensionPixelSize returns pixels, convert to dp
43
+ (resources.getDimensionPixelSize(resourceId) / density).toInt()
44
+ } else {
45
+ 0 // Fallback for devices without a navigation bar
46
+ }
47
+ }
48
+
49
+ override fun getNavigationBarHeight(promise: Promise) {
50
+ try {
51
+ val context = reactApplicationContext
52
+ val navBarHeight = getNavigationBarHeight(context)
53
+ promise.resolve(navBarHeight)
54
+ } catch (e: Exception) {
55
+ promise.reject("NAV_BAR_HEIGHT_ERROR", "Failed to get navigation bar height: ${e.message}", e)
56
+ }
57
+ }
58
+
24
59
  override fun getNavigationMode(promise: Promise) {
25
60
  try {
26
61
  val result = Arguments.createMap()
27
62
  val context = reactApplicationContext
28
63
 
64
+ // Use reactApplicationContext for navigation bar height
65
+ val navBarHeight = getNavigationBarHeight(context)
66
+ result.putInt("navigationBarHeight", navBarHeight)
67
+
29
68
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
30
69
  val navBarInteractionMode = getNavBarInteractionMode(context)
31
70
  result.putInt("interactionMode", navBarInteractionMode)
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","Platform","NativeModule","OS","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNavigationMode.ts"],"mappings":";;AACA,SAASA,mBAAmB,EAAEC,QAAQ,QAAQ,cAAc;AAa5D;AACA;AACA,MAAMC,YAAY,GAChBD,QAAQ,CAACE,EAAE,KAAK,SAAS,GACrBH,mBAAmB,CAACI,YAAY,CAAO,gBAAgB,CAAC,GACxD,IAAI;AAEV,eAAeF,YAAY","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","Platform","NativeModule","OS","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNavigationMode.ts"],"mappings":";;AACA,SAASA,mBAAmB,EAAEC,QAAQ,QAAQ,cAAc;AAe5D;AACA;AACA,MAAMC,YAAY,GAChBD,QAAQ,CAACE,EAAE,KAAK,SAAS,GACrBH,mBAAmB,CAACI,YAAY,CAAO,gBAAgB,CAAC,GACxD,IAAI;AAEV,eAAeF,YAAY","ignoreList":[]}
@@ -13,7 +13,8 @@ export function getNavigationMode() {
13
13
  // iOS always uses gesture navigation (no 3-button navigation exists)
14
14
  return Promise.resolve({
15
15
  type: 'gesture',
16
- isGestureNavigation: true
16
+ isGestureNavigation: true,
17
+ navigationBarHeight: 0 // iOS doesn't have a navigation bar like Android
17
18
  });
18
19
  }
19
20
 
@@ -36,6 +37,21 @@ export function isGestureNavigation() {
36
37
  return NavigationModeModule.isGestureNavigation();
37
38
  }
38
39
 
40
+ /**
41
+ * Get the navigation bar height in dp
42
+ * @returns Promise<number> - navigation bar height in dp
43
+ */
44
+ export function getNavigationBarHeight() {
45
+ // null check is redundant as it's always null for iOS but it's there to satisfy TypeScript
46
+ if (Platform.OS === 'ios' || NavigationModeModule === null) {
47
+ // iOS doesn't have a navigation bar like Android
48
+ return Promise.resolve(0);
49
+ }
50
+
51
+ // Only call native module on Android
52
+ return NavigationModeModule.getNavigationBarHeight();
53
+ }
54
+
39
55
  /**
40
56
  * Hook for React components to get navigation mode
41
57
  */
@@ -76,6 +92,7 @@ export function useNavigationMode() {
76
92
  export default {
77
93
  getNavigationMode,
78
94
  isGestureNavigation,
95
+ getNavigationBarHeight,
79
96
  useNavigationMode
80
97
  };
81
98
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","React","NavigationModeModule","getNavigationMode","OS","Promise","resolve","type","isGestureNavigation","useNavigationMode","navigationMode","setNavigationMode","useState","loading","setLoading","error","setError","useEffect","mounted","fetchNavigationMode","mode","err","Error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,OAAO;AACzB,OAAOC,oBAAoB,MAEpB,2BAAwB;AAI/B;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAgC;EAC/D;EACA,IAAIH,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC;MACrBC,IAAI,EAAE,SAAS;MACfC,mBAAmB,EAAE;IACvB,CAAC,CAAC;EACJ;;EAEA;EACA,OAAON,oBAAoB,CAACC,iBAAiB,CAAC,CAAC;AACjD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASK,mBAAmBA,CAAA,EAAqB;EACtD;EACA,IAAIR,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC,IAAI,CAAC;EAC9B;;EAEA;EACA,OAAOJ,oBAAoB,CAACM,mBAAmB,CAAC,CAAC;AACnD;;AAEA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAG;EAClC,MAAM,CAACC,cAAc,EAAEC,iBAAiB,CAAC,GACvCV,KAAK,CAACW,QAAQ,CAA4B,IAAI,CAAC;EACjD,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAGb,KAAK,CAACW,QAAQ,CAAC,IAAI,CAAC;EAClD,MAAM,CAACG,KAAK,EAAEC,QAAQ,CAAC,GAAGf,KAAK,CAACW,QAAQ,CAAe,IAAI,CAAC;EAE5DX,KAAK,CAACgB,SAAS,CAAC,MAAM;IACpB,IAAIC,OAAO,GAAG,IAAI;IAElB,eAAeC,mBAAmBA,CAAA,EAAG;MACnC,IAAI;QACF,MAAMC,IAAI,GAAG,MAAMjB,iBAAiB,CAAC,CAAC;QACtC,IAAIe,OAAO,EAAE;UACXP,iBAAiB,CAACS,IAAI,CAAC;UACvBJ,QAAQ,CAAC,IAAI,CAAC;QAChB;MACF,CAAC,CAAC,OAAOK,GAAG,EAAE;QACZ,IAAIH,OAAO,EAAE;UACXF,QAAQ,CAACK,GAAG,YAAYC,KAAK,GAAGD,GAAG,GAAG,IAAIC,KAAK,CAAC,eAAe,CAAC,CAAC;QACnE;MACF,CAAC,SAAS;QACR,IAAIJ,OAAO,EAAE;UACXJ,UAAU,CAAC,KAAK,CAAC;QACnB;MACF;IACF;IAEAK,mBAAmB,CAAC,CAAC;IAErB,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;IACjB,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAEN,OAAO;IAAER,cAAc;IAAEG,OAAO;IAAEE;EAAM,CAAC;AAC3C;AAEA,eAAe;EACbZ,iBAAiB;EACjBK,mBAAmB;EACnBC;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["Platform","React","NavigationModeModule","getNavigationMode","OS","Promise","resolve","type","isGestureNavigation","navigationBarHeight","getNavigationBarHeight","useNavigationMode","navigationMode","setNavigationMode","useState","loading","setLoading","error","setError","useEffect","mounted","fetchNavigationMode","mode","err","Error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,OAAOC,KAAK,MAAM,OAAO;AACzB,OAAOC,oBAAoB,MAEpB,2BAAwB;AAI/B;AACA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAgC;EAC/D;EACA,IAAIH,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC;MACrBC,IAAI,EAAE,SAAS;MACfC,mBAAmB,EAAE,IAAI;MACzBC,mBAAmB,EAAE,CAAC,CAAE;IAC1B,CAAC,CAAC;EACJ;;EAEA;EACA,OAAOP,oBAAoB,CAACC,iBAAiB,CAAC,CAAC;AACjD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASK,mBAAmBA,CAAA,EAAqB;EACtD;EACA,IAAIR,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC,IAAI,CAAC;EAC9B;;EAEA;EACA,OAAOJ,oBAAoB,CAACM,mBAAmB,CAAC,CAAC;AACnD;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,sBAAsBA,CAAA,EAAoB;EACxD;EACA,IAAIV,QAAQ,CAACI,EAAE,KAAK,KAAK,IAAIF,oBAAoB,KAAK,IAAI,EAAE;IAC1D;IACA,OAAOG,OAAO,CAACC,OAAO,CAAC,CAAC,CAAC;EAC3B;;EAEA;EACA,OAAOJ,oBAAoB,CAACQ,sBAAsB,CAAC,CAAC;AACtD;;AAEA;AACA;AACA;AACA,OAAO,SAASC,iBAAiBA,CAAA,EAAG;EAClC,MAAM,CAACC,cAAc,EAAEC,iBAAiB,CAAC,GACvCZ,KAAK,CAACa,QAAQ,CAA4B,IAAI,CAAC;EACjD,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAGf,KAAK,CAACa,QAAQ,CAAC,IAAI,CAAC;EAClD,MAAM,CAACG,KAAK,EAAEC,QAAQ,CAAC,GAAGjB,KAAK,CAACa,QAAQ,CAAe,IAAI,CAAC;EAE5Db,KAAK,CAACkB,SAAS,CAAC,MAAM;IACpB,IAAIC,OAAO,GAAG,IAAI;IAElB,eAAeC,mBAAmBA,CAAA,EAAG;MACnC,IAAI;QACF,MAAMC,IAAI,GAAG,MAAMnB,iBAAiB,CAAC,CAAC;QACtC,IAAIiB,OAAO,EAAE;UACXP,iBAAiB,CAACS,IAAI,CAAC;UACvBJ,QAAQ,CAAC,IAAI,CAAC;QAChB;MACF,CAAC,CAAC,OAAOK,GAAG,EAAE;QACZ,IAAIH,OAAO,EAAE;UACXF,QAAQ,CAACK,GAAG,YAAYC,KAAK,GAAGD,GAAG,GAAG,IAAIC,KAAK,CAAC,eAAe,CAAC,CAAC;QACnE;MACF,CAAC,SAAS;QACR,IAAIJ,OAAO,EAAE;UACXJ,UAAU,CAAC,KAAK,CAAC;QACnB;MACF;IACF;IAEAK,mBAAmB,CAAC,CAAC;IAErB,OAAO,MAAM;MACXD,OAAO,GAAG,KAAK;IACjB,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAEN,OAAO;IAAER,cAAc;IAAEG,OAAO;IAAEE;EAAM,CAAC;AAC3C;AAEA,eAAe;EACbd,iBAAiB;EACjBK,mBAAmB;EACnBE,sBAAsB;EACtBC;AACF,CAAC","ignoreList":[]}
@@ -3,10 +3,12 @@ export interface NavigationModeInfo {
3
3
  type: '3_button' | '2_button' | 'gesture' | 'unknown';
4
4
  isGestureNavigation: boolean;
5
5
  interactionMode?: number;
6
+ navigationBarHeight?: number;
6
7
  }
7
8
  export interface Spec extends TurboModule {
8
9
  getNavigationMode(): Promise<NavigationModeInfo>;
9
10
  isGestureNavigation(): Promise<boolean>;
11
+ getNavigationBarHeight(): Promise<number>;
10
12
  }
11
13
  declare const NativeModule: Spec | null;
12
14
  export default NativeModule;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeNavigationMode.d.ts","sourceRoot":"","sources":["../../../src/NativeNavigationMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjD,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACzC;AAID,QAAA,MAAM,YAAY,aAGR,CAAC;AAEX,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"NativeNavigationMode.d.ts","sourceRoot":"","sources":["../../../src/NativeNavigationMode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjD,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAID,QAAA,MAAM,YAAY,aAGR,CAAC;AAEX,eAAe,YAAY,CAAC"}
@@ -10,6 +10,11 @@ export declare function getNavigationMode(): Promise<NavigationModeInfo>;
10
10
  * @returns Promise<boolean> - true if gesture navigation is active
11
11
  */
12
12
  export declare function isGestureNavigation(): Promise<boolean>;
13
+ /**
14
+ * Get the navigation bar height in dp
15
+ * @returns Promise<number> - navigation bar height in dp
16
+ */
17
+ export declare function getNavigationBarHeight(): Promise<number>;
13
18
  /**
14
19
  * Hook for React components to get navigation mode
15
20
  */
@@ -21,6 +26,7 @@ export declare function useNavigationMode(): {
21
26
  declare const _default: {
22
27
  readonly getNavigationMode: typeof getNavigationMode;
23
28
  readonly isGestureNavigation: typeof isGestureNavigation;
29
+ readonly getNavigationBarHeight: typeof getNavigationBarHeight;
24
30
  readonly useNavigationMode: typeof useNavigationMode;
25
31
  };
26
32
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAA6B,EAC3B,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAY/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAStD;AAED;;GAEG;AACH,wBAAgB,iBAAiB;;;;EAmChC;;;;;;AAED,wBAIW"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAA6B,EAC3B,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAa/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAStD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CASxD;AAED;;GAEG;AACH,wBAAgB,iBAAiB;;;;EAmChC;;;;;;;AAED,wBAKW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-navigation-mode",
3
- "version": "1.0.4",
3
+ "version": "1.1.1",
4
4
  "description": "Detect Android navigation mode (3-button, 2-button, or gesture)",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -5,11 +5,13 @@ export interface NavigationModeInfo {
5
5
  type: '3_button' | '2_button' | 'gesture' | 'unknown';
6
6
  isGestureNavigation: boolean;
7
7
  interactionMode?: number;
8
+ navigationBarHeight?: number;
8
9
  }
9
10
 
10
11
  export interface Spec extends TurboModule {
11
12
  getNavigationMode(): Promise<NavigationModeInfo>;
12
13
  isGestureNavigation(): Promise<boolean>;
14
+ getNavigationBarHeight(): Promise<number>;
13
15
  }
14
16
 
15
17
  // Only get the native module on Android
package/src/index.tsx CHANGED
@@ -17,6 +17,7 @@ export function getNavigationMode(): Promise<NavigationModeInfo> {
17
17
  return Promise.resolve({
18
18
  type: 'gesture',
19
19
  isGestureNavigation: true,
20
+ navigationBarHeight: 0, // iOS doesn't have a navigation bar like Android
20
21
  });
21
22
  }
22
23
 
@@ -39,6 +40,21 @@ export function isGestureNavigation(): Promise<boolean> {
39
40
  return NavigationModeModule.isGestureNavigation();
40
41
  }
41
42
 
43
+ /**
44
+ * Get the navigation bar height in dp
45
+ * @returns Promise<number> - navigation bar height in dp
46
+ */
47
+ export function getNavigationBarHeight(): Promise<number> {
48
+ // null check is redundant as it's always null for iOS but it's there to satisfy TypeScript
49
+ if (Platform.OS === 'ios' || NavigationModeModule === null) {
50
+ // iOS doesn't have a navigation bar like Android
51
+ return Promise.resolve(0);
52
+ }
53
+
54
+ // Only call native module on Android
55
+ return NavigationModeModule.getNavigationBarHeight();
56
+ }
57
+
42
58
  /**
43
59
  * Hook for React components to get navigation mode
44
60
  */
@@ -82,5 +98,6 @@ export function useNavigationMode() {
82
98
  export default {
83
99
  getNavigationMode,
84
100
  isGestureNavigation,
101
+ getNavigationBarHeight,
85
102
  useNavigationMode,
86
103
  } as const;