@rn-tools/navigation 2.2.3 → 2.2.5
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 +44 -21
- package/package.json +4 -1
- package/src/__tests__/navigation-reducer.test.tsx +0 -2
- package/src/__tests__/stack.test.tsx +48 -0
- package/src/navigation.tsx +9 -2
- package/src/stack.tsx +43 -71
- package/src/tabs.tsx +9 -13
- package/src/utils.ts +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ A set of useful navigation components for React Native. Built with `react-native
|
|
|
13
13
|
- [Pushing a screen once](#pushing-a-screen-once)
|
|
14
14
|
- [Targeting specific tabs](#targeting-specific-tabs)
|
|
15
15
|
- [Rendering a header](#rendering-a-header)
|
|
16
|
+
- [Configuring screen props](#configuring-screen-props)
|
|
16
17
|
- [Components](#components)
|
|
17
18
|
- [Stack](#stack)
|
|
18
19
|
- [Tabs](#tabs)
|
|
@@ -20,6 +21,7 @@ A set of useful navigation components for React Native. Built with `react-native
|
|
|
20
21
|
- [Authentication](#authentication)
|
|
21
22
|
- [Deep Links](#deep-links)
|
|
22
23
|
- [Preventing going back](#preventing-going-back)
|
|
24
|
+
- [Testing](#testing)
|
|
23
25
|
|
|
24
26
|
## Installation
|
|
25
27
|
|
|
@@ -362,6 +364,27 @@ function MyScreenWithHeader() {
|
|
|
362
364
|
}
|
|
363
365
|
```
|
|
364
366
|
|
|
367
|
+
### Configuring screen props
|
|
368
|
+
|
|
369
|
+
The `Stack.Screen` component is a wrapper around the `Screen` component from `react-native-screens`. [Screen props reference](https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx#L101)
|
|
370
|
+
|
|
371
|
+
Some notable props are `stackPresentation`, `stackAnimation`, and `gestureEnabled`, however there are many more available.
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
function pushNativeModalScreen() {
|
|
375
|
+
navigation.pushScreen(
|
|
376
|
+
<Stack.Screen
|
|
377
|
+
stackPresentation="modal"
|
|
378
|
+
stackAnimation="slide_from_bottom"
|
|
379
|
+
gestureEnabled={true}
|
|
380
|
+
>
|
|
381
|
+
{/* If you want to push more screens inside of the modal, wrap it with a Stack */}
|
|
382
|
+
<Stack.Navigator rootScreen={<MyScreen title="Modal screen" />} />
|
|
383
|
+
</Stack.Screen>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
365
388
|
## Components
|
|
366
389
|
|
|
367
390
|
The `Navigator` components in the previous examples are fairly straightforward wrappers around other lower level `Stack` and `Tabs` components.
|
|
@@ -544,13 +567,13 @@ import * as Linking from "expo-linking";
|
|
|
544
567
|
import * as React from "react";
|
|
545
568
|
import { View, Text, TouchableOpacity } from "react-native";
|
|
546
569
|
|
|
547
|
-
export function DeepLinksExample() {
|
|
570
|
+
export default function DeepLinksExample() {
|
|
548
571
|
// You'll likely want to use Expo's Linking API to get the current URL and path
|
|
549
572
|
// let url = Linking.useURL()
|
|
550
573
|
// let { path } = Linking.parse(url)
|
|
551
574
|
|
|
552
575
|
// But it's easier to test hardcoded strings for the sake of this example
|
|
553
|
-
let path = "/
|
|
576
|
+
let path = "/home/item/2";
|
|
554
577
|
|
|
555
578
|
return (
|
|
556
579
|
<Stack.Navigator
|
|
@@ -559,23 +582,7 @@ export function DeepLinksExample() {
|
|
|
559
582
|
path={path}
|
|
560
583
|
handlers={[
|
|
561
584
|
{
|
|
562
|
-
path: "/
|
|
563
|
-
handler: (params: { itemId: string }) => {
|
|
564
|
-
let itemId = params.itemId;
|
|
565
|
-
|
|
566
|
-
// Go to home tab
|
|
567
|
-
navigation.setTabIndex(0);
|
|
568
|
-
|
|
569
|
-
// Push the screen we want
|
|
570
|
-
navigation.pushScreen(
|
|
571
|
-
<Stack.Screen>
|
|
572
|
-
<MyScreen title={`Item: ${itemId}`} />
|
|
573
|
-
</Stack.Screen>
|
|
574
|
-
);
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
{
|
|
578
|
-
path: "/testing/home/item/:itemId",
|
|
585
|
+
path: "/home/item/:itemId",
|
|
579
586
|
handler: (params: { itemId: string }) => {
|
|
580
587
|
let itemId = params.itemId;
|
|
581
588
|
|
|
@@ -665,7 +672,14 @@ function MyScreen({
|
|
|
665
672
|
}) {
|
|
666
673
|
return (
|
|
667
674
|
<View style={{ flex: 1, backgroundColor: bg }}>
|
|
668
|
-
<View
|
|
675
|
+
<View
|
|
676
|
+
style={{
|
|
677
|
+
flex: 1,
|
|
678
|
+
justifyContent: "center",
|
|
679
|
+
alignItems: "center",
|
|
680
|
+
gap: 4,
|
|
681
|
+
}}
|
|
682
|
+
>
|
|
669
683
|
<Text style={{ fontSize: 26, fontWeight: "semibold" }}>{title}</Text>
|
|
670
684
|
|
|
671
685
|
{!isRoot && (
|
|
@@ -681,7 +695,6 @@ function MyScreen({
|
|
|
681
695
|
</View>
|
|
682
696
|
);
|
|
683
697
|
}
|
|
684
|
-
|
|
685
698
|
```
|
|
686
699
|
|
|
687
700
|
### Preventing going back
|
|
@@ -765,3 +778,13 @@ function MyScreen() {
|
|
|
765
778
|
);
|
|
766
779
|
}
|
|
767
780
|
```
|
|
781
|
+
|
|
782
|
+
### Testing
|
|
783
|
+
|
|
784
|
+
Recommended:
|
|
785
|
+
|
|
786
|
+
- Check out the getting started portion of [React Native Testing Library](https://callstack.github.io/react-native-testing-library/docs/start/quick-start)
|
|
787
|
+
|
|
788
|
+
- Set up [Jest Expo](https://docs.expo.dev/develop/unit-testing/)
|
|
789
|
+
|
|
790
|
+
- Reset the navigation state between each test by calling `navigation.reset()`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rn-tools/navigation",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.5",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@rn-tools/eslint-config": "*",
|
|
16
|
+
"@testing-library/react-native": "^12.5.1",
|
|
16
17
|
"@types/jest": "^27.0.3",
|
|
17
18
|
"@types/react": "~18.2.79",
|
|
18
19
|
"@types/react-native": "^0.72.8",
|
|
20
|
+
"@types/react-test-renderer": "^18",
|
|
19
21
|
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
20
22
|
"@typescript-eslint/parser": "^6.8.0",
|
|
21
23
|
"babel-preset-expo": "~11.0.0",
|
|
@@ -25,6 +27,7 @@
|
|
|
25
27
|
"jest-expo": "^51.0.2",
|
|
26
28
|
"lint-staged": "^15.2.0",
|
|
27
29
|
"prettier": "^3.2.1",
|
|
30
|
+
"react-test-renderer": "18.2.0",
|
|
28
31
|
"typescript": "~5.3.3"
|
|
29
32
|
},
|
|
30
33
|
"peerDependencies": {
|
|
@@ -218,8 +218,6 @@ describe("reducer", () => {
|
|
|
218
218
|
};
|
|
219
219
|
|
|
220
220
|
let newState = reducer(initialStateWithStack, action, context);
|
|
221
|
-
console.log(newState);
|
|
222
|
-
|
|
223
221
|
expect(newState.screens.ids).not.toContain("screen1");
|
|
224
222
|
expect(newState.stacks.lookup.stack1.screens).not.toContain("screen1");
|
|
225
223
|
expect(newState.screens.ids).toContain("screen2");
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fireEvent, render } from "@testing-library/react-native";
|
|
2
|
+
import { Button, Text } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { navigation } from "../navigation";
|
|
5
|
+
import { Stack } from "../stack";
|
|
6
|
+
|
|
7
|
+
describe("<Stack />", () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
navigation.reset();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("render", () => {
|
|
13
|
+
let { getByText } = render(
|
|
14
|
+
<Stack.Navigator
|
|
15
|
+
rootScreen={
|
|
16
|
+
<>
|
|
17
|
+
<Text>Stack works!</Text>
|
|
18
|
+
</>
|
|
19
|
+
}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
let element = getByText("Stack works!");
|
|
23
|
+
expect(element).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("pushing a screen works", () => {
|
|
27
|
+
let { getByText, queryByText } = render(
|
|
28
|
+
<Stack.Navigator
|
|
29
|
+
rootScreen={
|
|
30
|
+
<Button
|
|
31
|
+
title="Push"
|
|
32
|
+
onPress={() =>
|
|
33
|
+
navigation.pushScreen(
|
|
34
|
+
<Stack.Screen>
|
|
35
|
+
<Text>Pushed screen!</Text>
|
|
36
|
+
</Stack.Screen>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
/>
|
|
40
|
+
}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(queryByText("Pushed screen")).toBe(null);
|
|
45
|
+
fireEvent.press(getByText("Push"));
|
|
46
|
+
getByText("Pushed screen!");
|
|
47
|
+
});
|
|
48
|
+
});
|
package/src/navigation.tsx
CHANGED
|
@@ -13,8 +13,6 @@ import type { PushScreenOptions } from "./types";
|
|
|
13
13
|
/**
|
|
14
14
|
* Ideas:
|
|
15
15
|
* - lifecycles / screen tracking
|
|
16
|
-
* - testing guide
|
|
17
|
-
* - routing example -> fragments to ids
|
|
18
16
|
*/
|
|
19
17
|
|
|
20
18
|
export function createNavigation() {
|
|
@@ -66,6 +64,15 @@ function getNavigationFns({ store, dispatch, renderCharts }: NavigationStore) {
|
|
|
66
64
|
0
|
|
67
65
|
);
|
|
68
66
|
let tabIds = renderCharts.tabsByDepth[maxDepth];
|
|
67
|
+
|
|
68
|
+
if (!tabIds || tabIds?.length === 0) {
|
|
69
|
+
if (store.getState().debugModeEnabled) {
|
|
70
|
+
console.warn("No focused tabs found");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
let topTabId = tabIds[tabIds.length - 1];
|
|
70
77
|
return topTabId;
|
|
71
78
|
}
|
package/src/stack.tsx
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import {
|
|
3
3
|
BackHandler,
|
|
4
|
-
PixelRatio,
|
|
5
4
|
Platform,
|
|
6
5
|
StyleSheet,
|
|
7
|
-
useWindowDimensions,
|
|
8
6
|
View,
|
|
9
7
|
type ImageProps,
|
|
10
|
-
type LayoutRectangle,
|
|
11
8
|
type ViewProps,
|
|
12
9
|
type ViewStyle,
|
|
13
10
|
} from "react-native";
|
|
@@ -21,6 +18,7 @@ import {
|
|
|
21
18
|
ScreenStackHeaderCenterView as RNScreenStackHeaderCenterView,
|
|
22
19
|
ScreenStackHeaderConfigProps as RNScreenStackHeaderConfigProps,
|
|
23
20
|
ScreenStackHeaderBackButtonImage as RNScreenStackHeaderBackButtonImage,
|
|
21
|
+
type ScreenProps,
|
|
24
22
|
} from "react-native-screens";
|
|
25
23
|
import ScreenStackNativeComponent from "react-native-screens/src/fabric/ScreenStackNativeComponent";
|
|
26
24
|
|
|
@@ -59,7 +57,7 @@ let RNScreenStack = React.memo(function RNScreenStack(
|
|
|
59
57
|
);
|
|
60
58
|
});
|
|
61
59
|
|
|
62
|
-
type StackRootProps = {
|
|
60
|
+
export type StackRootProps = {
|
|
63
61
|
children: React.ReactNode;
|
|
64
62
|
id?: string;
|
|
65
63
|
};
|
|
@@ -129,7 +127,9 @@ function StackRoot({ children, id }: StackRootProps) {
|
|
|
129
127
|
);
|
|
130
128
|
}
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
export type StackScreensProps = RNScreenStackProps;
|
|
131
|
+
|
|
132
|
+
function StackScreens({ style: styleProp, ...props }: StackScreensProps) {
|
|
133
133
|
let style = React.useMemo(
|
|
134
134
|
() => styleProp || StyleSheet.absoluteFill,
|
|
135
135
|
[styleProp]
|
|
@@ -145,11 +145,14 @@ let defaultScreenStyle: ViewStyle = {
|
|
|
145
145
|
|
|
146
146
|
export type StackScreenProps = RNScreenProps;
|
|
147
147
|
|
|
148
|
+
let HeaderHeightContext = React.createContext<number>(0);
|
|
149
|
+
|
|
148
150
|
let StackScreen = React.memo(function StackScreen({
|
|
149
151
|
children,
|
|
150
152
|
style: styleProp,
|
|
151
153
|
gestureEnabled = true,
|
|
152
154
|
onDismissed: onDismissedProp,
|
|
155
|
+
onHeaderHeightChange: onHeaderHeightChangeProp,
|
|
153
156
|
...props
|
|
154
157
|
}: StackScreenProps) {
|
|
155
158
|
let stackId = React.useContext(StackIdContext);
|
|
@@ -187,17 +190,31 @@ let StackScreen = React.memo(function StackScreen({
|
|
|
187
190
|
|
|
188
191
|
let style = React.useMemo(() => styleProp || defaultScreenStyle, [styleProp]);
|
|
189
192
|
|
|
193
|
+
let [headerHeight, setHeaderHeight] = React.useState(0);
|
|
194
|
+
|
|
195
|
+
let onHeaderHeightChange: ScreenProps["onHeaderHeightChange"] =
|
|
196
|
+
React.useCallback(
|
|
197
|
+
(e) => {
|
|
198
|
+
Platform.OS === "ios" && setHeaderHeight(e.nativeEvent.headerHeight);
|
|
199
|
+
onHeaderHeightChangeProp?.(e);
|
|
200
|
+
},
|
|
201
|
+
[onHeaderHeightChangeProp]
|
|
202
|
+
);
|
|
203
|
+
|
|
190
204
|
return (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
205
|
+
<HeaderHeightContext.Provider value={headerHeight}>
|
|
206
|
+
{/* @ts-expect-error - Ref typings in RNScreens */}
|
|
207
|
+
<RNScreen
|
|
208
|
+
{...props}
|
|
209
|
+
style={style}
|
|
210
|
+
activityState={isActive ? 2 : 0}
|
|
211
|
+
gestureEnabled={gestureEnabled}
|
|
212
|
+
onDismissed={onDismissed}
|
|
213
|
+
onHeaderHeightChange={onHeaderHeightChange}
|
|
214
|
+
>
|
|
215
|
+
{children}
|
|
216
|
+
</RNScreen>
|
|
217
|
+
</HeaderHeightContext.Provider>
|
|
201
218
|
);
|
|
202
219
|
});
|
|
203
220
|
|
|
@@ -233,24 +250,23 @@ let StackSlot = React.memo(function StackSlot({
|
|
|
233
250
|
);
|
|
234
251
|
});
|
|
235
252
|
|
|
253
|
+
export type StackScreenHeaderProps = RNScreenStackHeaderConfigProps;
|
|
254
|
+
|
|
236
255
|
let StackScreenHeader = React.memo(function StackScreenHeader({
|
|
237
256
|
...props
|
|
238
|
-
}:
|
|
239
|
-
let
|
|
240
|
-
let insets = useSafeAreaInsetsSafe();
|
|
241
|
-
|
|
242
|
-
let headerHeight = React.useMemo(() => {
|
|
243
|
-
if (Platform.OS === "android") {
|
|
244
|
-
return 0;
|
|
245
|
-
}
|
|
257
|
+
}: StackScreenHeaderProps) {
|
|
258
|
+
let headerHeight = React.useContext(HeaderHeightContext);
|
|
246
259
|
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
let placeholderStyle = React.useMemo<ViewStyle>(() => {
|
|
261
|
+
return {
|
|
262
|
+
height: headerHeight,
|
|
263
|
+
};
|
|
264
|
+
}, [headerHeight]);
|
|
249
265
|
|
|
250
266
|
return (
|
|
251
267
|
<React.Fragment>
|
|
252
268
|
<RNScreenStackHeaderConfig {...props} />
|
|
253
|
-
<View style={
|
|
269
|
+
<View style={placeholderStyle} />
|
|
254
270
|
</React.Fragment>
|
|
255
271
|
);
|
|
256
272
|
});
|
|
@@ -279,7 +295,7 @@ let ScreenStackHeaderBackButtonImage = React.memo(
|
|
|
279
295
|
}
|
|
280
296
|
);
|
|
281
297
|
|
|
282
|
-
type StackNavigatorProps = Omit<StackRootProps, "children"> & {
|
|
298
|
+
export type StackNavigatorProps = Omit<StackRootProps, "children"> & {
|
|
283
299
|
rootScreen: React.ReactElement<unknown>;
|
|
284
300
|
};
|
|
285
301
|
|
|
@@ -309,47 +325,3 @@ export let Stack = {
|
|
|
309
325
|
Slot: StackSlot,
|
|
310
326
|
Navigator: StackNavigator,
|
|
311
327
|
};
|
|
312
|
-
|
|
313
|
-
// `onLayout` event does not return a value for the native header component
|
|
314
|
-
// This function is copied from react-navigation to get the default header heights
|
|
315
|
-
// Ref: https://github.com/react-navigation/react-navigation/blob/main/packages/elements/src/Header/getDefaultHeaderHeight.tsx#L5
|
|
316
|
-
function getDefaultHeaderHeight(
|
|
317
|
-
layout: Pick<LayoutRectangle, "width" | "height">,
|
|
318
|
-
// TODO - handle modal headers and substacks
|
|
319
|
-
modalPresentation: boolean,
|
|
320
|
-
topInset: number
|
|
321
|
-
): number {
|
|
322
|
-
let headerHeight;
|
|
323
|
-
|
|
324
|
-
// On models with Dynamic Island the status bar height is smaller than the safe area top inset.
|
|
325
|
-
let hasDynamicIsland = Platform.OS === "ios" && topInset > 50;
|
|
326
|
-
let statusBarHeight = hasDynamicIsland
|
|
327
|
-
? topInset - (5 + 1 / PixelRatio.get())
|
|
328
|
-
: topInset;
|
|
329
|
-
|
|
330
|
-
let isLandscape = layout.width > layout.height;
|
|
331
|
-
|
|
332
|
-
if (Platform.OS === "ios") {
|
|
333
|
-
if (Platform.isPad || Platform.isTV) {
|
|
334
|
-
if (modalPresentation) {
|
|
335
|
-
headerHeight = 56;
|
|
336
|
-
} else {
|
|
337
|
-
headerHeight = 50;
|
|
338
|
-
}
|
|
339
|
-
} else {
|
|
340
|
-
if (isLandscape) {
|
|
341
|
-
headerHeight = 32;
|
|
342
|
-
} else {
|
|
343
|
-
if (modalPresentation) {
|
|
344
|
-
headerHeight = 56;
|
|
345
|
-
} else {
|
|
346
|
-
headerHeight = 44;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
} else {
|
|
351
|
-
headerHeight = 64;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return headerHeight + statusBarHeight;
|
|
355
|
-
}
|
package/src/tabs.tsx
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from "./navigation-store";
|
|
29
29
|
import { generateTabId, useSafeAreaInsetsSafe } from "./utils";
|
|
30
30
|
|
|
31
|
-
type TabsRootProps = {
|
|
31
|
+
export type TabsRootProps = {
|
|
32
32
|
children: React.ReactNode;
|
|
33
33
|
id?: string;
|
|
34
34
|
};
|
|
@@ -44,13 +44,7 @@ let useTabsInternal = (tabId = "") =>
|
|
|
44
44
|
return tab;
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
let TabsRoot = React.memo(function TabsRoot({
|
|
48
|
-
children,
|
|
49
|
-
id,
|
|
50
|
-
}: {
|
|
51
|
-
children: React.ReactNode;
|
|
52
|
-
id?: string;
|
|
53
|
-
}) {
|
|
47
|
+
let TabsRoot = React.memo(function TabsRoot({ children, id }: TabsRootProps) {
|
|
54
48
|
let tabIdRef = React.useRef(id || generateTabId());
|
|
55
49
|
let tabId = tabIdRef.current;
|
|
56
50
|
let tabs = useTabsInternal(tabId);
|
|
@@ -99,10 +93,9 @@ let defaultScreenContainerStyle = {
|
|
|
99
93
|
flex: 1,
|
|
100
94
|
};
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}: { children: React.ReactNode } & RNScreenContainerProps) {
|
|
96
|
+
export type TabsScreensProps = RNScreenContainerProps;
|
|
97
|
+
|
|
98
|
+
function TabsScreens({ children, ...props }: TabsScreensProps) {
|
|
106
99
|
return (
|
|
107
100
|
<RNScreenContainer style={defaultScreenContainerStyle} {...props}>
|
|
108
101
|
{React.Children.map(children, (child, index) => {
|
|
@@ -116,11 +109,13 @@ function TabsScreens({
|
|
|
116
109
|
);
|
|
117
110
|
}
|
|
118
111
|
|
|
112
|
+
export type TabsScreenProps = RNScreenProps;
|
|
113
|
+
|
|
119
114
|
let TabsScreen = React.memo(function TabsScreen({
|
|
120
115
|
children,
|
|
121
116
|
style: styleProp,
|
|
122
117
|
...props
|
|
123
|
-
}:
|
|
118
|
+
}: TabsScreenProps) {
|
|
124
119
|
let dispatch = useNavigationDispatch();
|
|
125
120
|
|
|
126
121
|
let tabId = React.useContext(TabIdContext);
|
|
@@ -177,6 +172,7 @@ export let defaultTabbarStyle: ViewStyle = {
|
|
|
177
172
|
backgroundColor: "white",
|
|
178
173
|
};
|
|
179
174
|
|
|
175
|
+
|
|
180
176
|
let TabsTabbar = React.memo(function TabsTabbar({
|
|
181
177
|
children,
|
|
182
178
|
style: styleProp,
|
package/src/utils.ts
CHANGED
|
@@ -34,7 +34,7 @@ export function useSafeAreaInsetsSafe() {
|
|
|
34
34
|
// eslint-disable-next-line
|
|
35
35
|
insets = useSafeAreaInsets();
|
|
36
36
|
} catch (error) {
|
|
37
|
-
console.
|
|
37
|
+
console.warn("`react-native-safe-area-context` missing - Please install and wrap your app in a SafeAreaProvider" );
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
return insets;
|