Skip to content

Commit 2b642c7

Browse files
fix: set for edge to edge
1 parent 02029b0 commit 2b642c7

File tree

12 files changed

+223
-20
lines changed

12 files changed

+223
-20
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ local.properties
2424

2525
# Packages
2626
*.tgz
27+
.DS_Store

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ for the following:
3232
- [React Native](https://reactnative.dev/) >= 0.50.0
3333
- [react-native-svg](https://github.com/react-native-svg/react-native-svg) >= 12.1.0
3434

35+
## ✨ Edge-to-Edge Support
36+
37+
This library supports edge-to-edge display, including Android 15's enforced edge-to-edge mode. The tour overlay automatically covers the full screen including system bars. When using edge-to-edge layouts, you may need to adjust spotlight positioning using the `coordinateAdjustment` prop or individual `offset` props on `AttachStep` components. See the [example app](example/) for a working edge-to-edge implementation.
38+
3539
## Install
3640

3741
With `npm`:

example/android/app/src/main/java/com/spotlightexample/MainActivity.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
package com.spotlightexample
22

3+
import android.os.Build
4+
import android.os.Bundle
5+
import android.view.View
6+
import android.view.WindowInsetsController
7+
38
import com.facebook.react.ReactActivity
49
import com.facebook.react.ReactActivityDelegate
510
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
611
import com.facebook.react.defaults.DefaultReactActivityDelegate
712

813
class MainActivity : ReactActivity() {
914

15+
override fun onCreate(savedInstanceState: Bundle?) {
16+
super.onCreate(null)
17+
18+
val window = window
19+
20+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
21+
// Edge-to-edge for Android 11+
22+
window.setDecorFitsSystemWindows(false)
23+
val controller = window.insetsController
24+
controller?.systemBarsBehavior =
25+
WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
26+
} else {
27+
// Edge-to-edge for older versions
28+
@Suppress("DEPRECATION")
29+
window.decorView.systemUiVisibility = (
30+
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
31+
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
32+
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
33+
)
34+
}
35+
}
36+
1037
/**
1138
* Returns the name of the main component registered from JavaScript. This is used to schedule
1239
* rendering of the component.

example/ios/Podfile.lock

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,96 @@ PODS:
16481648
- React-RCTFBReactNativeSpec
16491649
- ReactCommon/turbomodule/core
16501650
- SocketRocket
1651+
- react-native-safe-area-context (5.6.1):
1652+
- boost
1653+
- DoubleConversion
1654+
- fast_float
1655+
- fmt
1656+
- glog
1657+
- hermes-engine
1658+
- RCT-Folly
1659+
- RCT-Folly/Fabric
1660+
- RCTRequired
1661+
- RCTTypeSafety
1662+
- React-Core
1663+
- React-debug
1664+
- React-Fabric
1665+
- React-featureflags
1666+
- React-graphics
1667+
- React-hermes
1668+
- React-ImageManager
1669+
- React-jsi
1670+
- react-native-safe-area-context/common (= 5.6.1)
1671+
- react-native-safe-area-context/fabric (= 5.6.1)
1672+
- React-NativeModulesApple
1673+
- React-RCTFabric
1674+
- React-renderercss
1675+
- React-rendererdebug
1676+
- React-utils
1677+
- ReactCodegen
1678+
- ReactCommon/turbomodule/bridging
1679+
- ReactCommon/turbomodule/core
1680+
- SocketRocket
1681+
- Yoga
1682+
- react-native-safe-area-context/common (5.6.1):
1683+
- boost
1684+
- DoubleConversion
1685+
- fast_float
1686+
- fmt
1687+
- glog
1688+
- hermes-engine
1689+
- RCT-Folly
1690+
- RCT-Folly/Fabric
1691+
- RCTRequired
1692+
- RCTTypeSafety
1693+
- React-Core
1694+
- React-debug
1695+
- React-Fabric
1696+
- React-featureflags
1697+
- React-graphics
1698+
- React-hermes
1699+
- React-ImageManager
1700+
- React-jsi
1701+
- React-NativeModulesApple
1702+
- React-RCTFabric
1703+
- React-renderercss
1704+
- React-rendererdebug
1705+
- React-utils
1706+
- ReactCodegen
1707+
- ReactCommon/turbomodule/bridging
1708+
- ReactCommon/turbomodule/core
1709+
- SocketRocket
1710+
- Yoga
1711+
- react-native-safe-area-context/fabric (5.6.1):
1712+
- boost
1713+
- DoubleConversion
1714+
- fast_float
1715+
- fmt
1716+
- glog
1717+
- hermes-engine
1718+
- RCT-Folly
1719+
- RCT-Folly/Fabric
1720+
- RCTRequired
1721+
- RCTTypeSafety
1722+
- React-Core
1723+
- React-debug
1724+
- React-Fabric
1725+
- React-featureflags
1726+
- React-graphics
1727+
- React-hermes
1728+
- React-ImageManager
1729+
- React-jsi
1730+
- react-native-safe-area-context/common
1731+
- React-NativeModulesApple
1732+
- React-RCTFabric
1733+
- React-renderercss
1734+
- React-rendererdebug
1735+
- React-utils
1736+
- ReactCodegen
1737+
- ReactCommon/turbomodule/bridging
1738+
- ReactCommon/turbomodule/core
1739+
- SocketRocket
1740+
- Yoga
16511741
- React-NativeModulesApple (0.80.0):
16521742
- boost
16531743
- DoubleConversion
@@ -2226,6 +2316,7 @@ DEPENDENCIES:
22262316
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
22272317
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
22282318
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
2319+
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
22292320
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
22302321
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
22312322
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -2347,6 +2438,8 @@ EXTERNAL SOURCES:
23472438
:path: "../node_modules/react-native/ReactCommon"
23482439
React-microtasksnativemodule:
23492440
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
2441+
react-native-safe-area-context:
2442+
:path: "../node_modules/react-native-safe-area-context"
23502443
React-NativeModulesApple:
23512444
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
23522445
React-oscompat:
@@ -2455,6 +2548,7 @@ SPEC CHECKSUMS:
24552548
React-logger: b69e65dc60f768e5509ac0cc27a360124bf70478
24562549
React-Mapbuffer: b48f9f3311fd0ec0f7a5dc39d707eff521fb5f38
24572550
React-microtasksnativemodule: d8568d0485a350c720c061ae835e09fc88c28715
2551+
react-native-safe-area-context: fd72bd1eb562ff2a0fda20e9a7828c4b0cabf5a4
24582552
React-NativeModulesApple: f10596688a03af66804cfbe61792be24a7888da8
24592553
React-oscompat: 7c0a341cc31e350da71ddf2e46de0a845d1d1626
24602554
React-perflogger: 4cc44451f694d6205f47bd8d5d87c9c862c3335c
@@ -2488,8 +2582,8 @@ SPEC CHECKSUMS:
24882582
ReactCommon: 658874decaf8c4fd76cfa3a878b94a869db85b1c
24892583
RNSVG: c73af7848d94ca3e8136a5191d055e3c1d6fedab
24902584
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
2491-
Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e
2585+
Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb
24922586

24932587
PODFILE CHECKSUM: 9742088751b8c39a438474c638da6274a8066e53
24942588

2495-
COCOAPODS: 1.14.3
2589+
COCOAPODS: 1.15.2

example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"react-dom": "19.1.0",
2727
"react-is": "19.1.0",
2828
"react-native": "0.80.0",
29+
"react-native-safe-area-context": "^5.6.1",
2930
"react-native-spotlight-tour": "workspace:^",
3031
"react-native-svg": "^15.12.0",
3132
"react-native-svg-web": "^1.0.9",

example/src/App.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import dedent from "dedent";
22
import { type ReactElement, useCallback, useMemo } from "react";
3-
import { Alert, Animated, Button, Dimensions, SafeAreaView, Text, useAnimatedValue } from "react-native";
3+
import { Alert, Animated, Button, Dimensions, Platform, StatusBar, Text, View, useAnimatedValue } from "react-native";
4+
import { SafeAreaProvider, useSafeAreaInsets } from "react-native-safe-area-context";
45
import {
56
AttachStep,
7+
type RenderProps,
8+
type SpotlightTour,
69
SpotlightTourProvider,
710
TourBox,
811
type TourState,
@@ -19,8 +22,9 @@ import {
1922
} from "./App.styles";
2023
import { DocsTooltip } from "./DocsTooltip";
2124

22-
export function App(): ReactElement {
25+
function AppContent(): ReactElement {
2326
const gap = useAnimatedValue(0, { useNativeDriver: true });
27+
const insets = useSafeAreaInsets();
2428

2529
const showSummary = useCallback(({ index, isLast }: TourState) => {
2630
Alert.alert(
@@ -40,7 +44,7 @@ export function App(): ReactElement {
4044
}, []);
4145

4246
const tourSteps = useMemo((): TourStep[] => [{
43-
render: ({ next, pause }) => (
47+
render: ({ next, pause }: RenderProps) => (
4448
<SpotDescriptionView>
4549
<DescriptionText>
4650
<BoldText>{"Tour: Intro section\n"}</BoldText>
@@ -59,7 +63,7 @@ export function App(): ReactElement {
5963
render: DocsTooltip,
6064
}, {
6165
arrow: true,
62-
render: props => (
66+
render: (props: RenderProps) => (
6367
<TourBox
6468
title="Tour: Customization"
6569
backText="Previous"
@@ -92,7 +96,9 @@ export function App(): ReactElement {
9296
.start(() => resolve());
9397
});
9498
},
95-
render: ({ previous, stop }) => (
99+
flip: true,
100+
placement: "top",
101+
render: ({ previous, stop }: RenderProps) => (
96102
<SpotDescriptionView>
97103
<DescriptionText>
98104
<BoldText>{"Tour: Try it!\n"}</BoldText>
@@ -115,7 +121,12 @@ export function App(): ReactElement {
115121
}], []);
116122

117123
return (
118-
<SafeAreaView>
124+
<View style={{
125+
flex: 1,
126+
paddingTop: insets.top,
127+
}}
128+
>
129+
<StatusBar translucent={true} backgroundColor="transparent" barStyle="dark-content" />
119130
<SpotlightTourProvider
120131
steps={tourSteps}
121132
overlayColor="gray"
@@ -127,15 +138,16 @@ export function App(): ReactElement {
127138
motion="bounce"
128139
shape="circle"
129140
arrow={{ color: "#B0C4DE" }}
141+
coordinateAdjustment={Platform.OS === "android" ? { y: insets.top } : undefined}
130142
>
131-
{({ resume, start, status }) => (
143+
{({ resume, start, status }: SpotlightTour) => (
132144
<>
133145
{status !== "paused" && <Button title="Start" onPress={start} />}
134146
{status === "paused" && <Button title="Resume" onPress={resume} />}
135147

136148
<SectionContainerView>
137-
<AttachStep index={1}>
138-
<TitleText>{"Introduction"}</TitleText>
149+
<AttachStep index={0}>
150+
<TitleText>{"Introduction."}</TitleText>
139151
</AttachStep>
140152
<DescriptionText>
141153
{dedent`
@@ -146,7 +158,7 @@ export function App(): ReactElement {
146158
</SectionContainerView>
147159

148160
<SectionContainerView>
149-
<AttachStep index={0}>
161+
<AttachStep index={1}>
150162
<TitleText>{"Documentation"}</TitleText>
151163
</AttachStep>
152164
<DescriptionText>
@@ -176,6 +188,14 @@ export function App(): ReactElement {
176188
</>
177189
)}
178190
</SpotlightTourProvider>
179-
</SafeAreaView>
191+
</View>
192+
);
193+
}
194+
195+
export function App(): ReactElement {
196+
return (
197+
<SafeAreaProvider>
198+
<AppContent />
199+
</SafeAreaProvider>
180200
);
181201
}

example/src/DocsTooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import dedent from "dedent";
22
import { Button } from "react-native";
3-
import { useSpotlightTour } from "react-native-spotlight-tour";
3+
import { type SpotlightTour, useSpotlightTour } from "react-native-spotlight-tour";
44

55
import { BoldText, ButtonsGroupView, DescriptionText, SpotDescriptionView } from "./App.styles";
66

77
import type { ReactElement } from "react";
88

99
export function DocsTooltip(): ReactElement {
1010
// You can also use the hook instead of the props here!
11-
const { next, previous } = useSpotlightTour();
11+
const { next, previous }: SpotlightTour = useSpotlightTour();
1212

1313
return (
1414
<SpotDescriptionView>

package/src/lib/SpotlightTour.context.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ export interface TooltipProps {
153153
* @default 20
154154
*/
155155
arrow?: ArrowOptions | boolean | number;
156+
/**
157+
* Global coordinate adjustment for all spotlight calculations.
158+
* Useful for edge-to-edge layouts or custom status bar configurations.
159+
*/
160+
coordinateAdjustment?: CoordinateAdjustment;
156161
/**
157162
* Enables flipping the placement of the tooltip in order to keep it in view.
158163
*
@@ -271,6 +276,10 @@ export interface SpotlightTourCtx extends SpotlightTour {
271276
* @param spot the spot layout
272277
*/
273278
changeSpot: (spot: LayoutRectangle) => void;
279+
/**
280+
* Global coordinate adjustment for all spotlight calculations.
281+
*/
282+
coordinateAdjustment?: CoordinateAdjustment;
274283
/**
275284
* The spotlight layout.
276285
*/
@@ -290,6 +299,7 @@ export const ZERO_SPOT: LayoutRectangle = {
290299

291300
export const SpotlightTourContext = createContext<SpotlightTourCtx>({
292301
changeSpot: () => undefined,
302+
coordinateAdjustment: undefined,
293303
goTo: () => undefined,
294304
next: () => undefined,
295305
pause: () => undefined,
@@ -322,3 +332,14 @@ export function useSpotlightTour(): SpotlightTour {
322332
stop,
323333
};
324334
}
335+
336+
export interface CoordinateAdjustment {
337+
/**
338+
* Global X offset to apply to all spotlight calculations
339+
*/
340+
x?: number;
341+
/**
342+
* Global Y offset to apply to all spotlight calculations
343+
*/
344+
y?: number;
345+
}

0 commit comments

Comments
 (0)