Skip to content

Commit b787d52

Browse files
fix: set translucentStatusBar prop
1 parent 2b642c7 commit b787d52

File tree

6 files changed

+70
-52
lines changed

6 files changed

+70
-52
lines changed

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,27 @@ for the following:
3434

3535
## ✨ Edge-to-Edge Support
3636

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.
37+
This library supports edge-to-edge display (including Android 15’s enforced mode). The tour overlay already covers the whole screen, system bars included.
38+
39+
When your screens use a translucent status bar you can enable proper handling—and optionally nudge the spotlight—via the new `translucentStatusBar` prop:
40+
41+
```tsx
42+
<SpotlightTourProvider
43+
translucentStatusBar={{
44+
enable: true, // overlay under system bars
45+
coordinateAdjustment: { y: insets.top }, // y = status-bar height (e.g. use SafeArea insets.top)
46+
}}
47+
{...otherProps}
48+
>
49+
{/**/}
50+
</SpotlightTourProvider>
51+
```
52+
53+
`insets.top` is the height of the status bar returned by `react-native-safe-area-context`. Any value that represents the actual status-bar height will work.
54+
55+
`translucentStatusBar` is **optional**—if you omit it or set `enable` to `false`, the tour continues to behave exactly as before.
56+
57+
See the [example app](example/) for a fully-working edge-to-edge implementation.
3858

3959
## Install
4060

example/src/App.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dedent from "dedent";
22
import { type ReactElement, useCallback, useMemo } from "react";
3-
import { Alert, Animated, Button, Dimensions, Platform, StatusBar, Text, View, useAnimatedValue } from "react-native";
4-
import { SafeAreaProvider, useSafeAreaInsets } from "react-native-safe-area-context";
3+
import { Alert, Animated, Button, Dimensions, Text, useAnimatedValue } from "react-native";
4+
import { SafeAreaProvider, SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context";
55
import {
66
AttachStep,
77
type RenderProps,
@@ -24,8 +24,7 @@ import { DocsTooltip } from "./DocsTooltip";
2424

2525
function AppContent(): ReactElement {
2626
const gap = useAnimatedValue(0, { useNativeDriver: true });
27-
const insets = useSafeAreaInsets();
28-
27+
const { top } = useSafeAreaInsets();
2928
const showSummary = useCallback(({ index, isLast }: TourState) => {
3029
Alert.alert(
3130
"Tour Finished",
@@ -121,24 +120,22 @@ function AppContent(): ReactElement {
121120
}], []);
122121

123122
return (
124-
<View style={{
125-
flex: 1,
126-
paddingTop: insets.top,
127-
}}
128-
>
129-
<StatusBar translucent={true} backgroundColor="transparent" barStyle="dark-content" />
123+
<SafeAreaView style={{ flex: 1 }}>
130124
<SpotlightTourProvider
131125
steps={tourSteps}
132126
overlayColor="gray"
133-
overlayOpacity={0.36}
127+
overlayOpacity={0.86}
134128
nativeDriver={true}
135129
onBackdropPress="continue"
136130
onStop={showSummary}
137131
onPause={alertPause}
138132
motion="bounce"
139133
shape="circle"
140134
arrow={{ color: "#B0C4DE" }}
141-
coordinateAdjustment={Platform.OS === "android" ? { y: insets.top } : undefined}
135+
translucentStatusBar={{
136+
coordinateAdjustment: { y: top },
137+
enable: true,
138+
}}
142139
>
143140
{({ resume, start, status }: SpotlightTour) => (
144141
<>
@@ -188,7 +185,7 @@ function AppContent(): ReactElement {
188185
</>
189186
)}
190187
</SpotlightTourProvider>
191-
</View>
188+
</SafeAreaView>
192189
);
193190
}
194191

package/src/lib/SpotlightTour.context.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,6 @@ 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;
161156
/**
162157
* Enables flipping the placement of the tooltip in order to keep it in view.
163158
*
@@ -182,6 +177,11 @@ export interface TooltipProps {
182177
* @default { padding: 8 }
183178
*/
184179
shift?: boolean | ShiftOptions;
180+
/**
181+
* Global coordinate adjustment for all spotlight calculations.
182+
* Useful for edge-to-edge layouts or custom status bar configurations.
183+
*/
184+
translucentStatusBar?: TranslucentStatusBar;
185185
}
186186

187187
export interface TourStep extends TooltipProps {
@@ -276,10 +276,6 @@ export interface SpotlightTourCtx extends SpotlightTour {
276276
* @param spot the spot layout
277277
*/
278278
changeSpot: (spot: LayoutRectangle) => void;
279-
/**
280-
* Global coordinate adjustment for all spotlight calculations.
281-
*/
282-
coordinateAdjustment?: CoordinateAdjustment;
283279
/**
284280
* The spotlight layout.
285281
*/
@@ -288,6 +284,10 @@ export interface SpotlightTourCtx extends SpotlightTour {
288284
* The list of steps for the tour.
289285
*/
290286
steps: TourStep[];
287+
/**
288+
* Global coordinate adjustment for all spotlight calculations.
289+
*/
290+
translucentStatusBar?: TranslucentStatusBar;
291291
}
292292

293293
export const ZERO_SPOT: LayoutRectangle = {
@@ -299,7 +299,6 @@ export const ZERO_SPOT: LayoutRectangle = {
299299

300300
export const SpotlightTourContext = createContext<SpotlightTourCtx>({
301301
changeSpot: () => undefined,
302-
coordinateAdjustment: undefined,
303302
goTo: () => undefined,
304303
next: () => undefined,
305304
pause: () => undefined,
@@ -310,6 +309,7 @@ export const SpotlightTourContext = createContext<SpotlightTourCtx>({
310309
status: "idle",
311310
steps: [],
312311
stop: () => undefined,
312+
translucentStatusBar: undefined,
313313
});
314314

315315
/**
@@ -332,7 +332,6 @@ export function useSpotlightTour(): SpotlightTour {
332332
stop,
333333
};
334334
}
335-
336335
export interface CoordinateAdjustment {
337336
/**
338337
* Global X offset to apply to all spotlight calculations
@@ -343,3 +342,14 @@ export interface CoordinateAdjustment {
343342
*/
344343
y?: number;
345344
}
345+
export interface TranslucentStatusBar {
346+
/**
347+
* Global coordinate adjustment for all spotlight calculations.
348+
*/
349+
coordinateAdjustment?: CoordinateAdjustment;
350+
/**
351+
* Whether to enable the translucent status bar
352+
*/
353+
enable?: boolean;
354+
355+
}

package/src/lib/SpotlightTour.provider.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ export const SpotlightTourProvider = forwardRef<SpotlightTour, SpotlightTourProv
116116
const {
117117
arrow,
118118
children,
119-
coordinateAdjustment,
120119
flip,
121120
motion = "bounce",
122121
nativeDriver = true,
@@ -131,6 +130,7 @@ export const SpotlightTourProvider = forwardRef<SpotlightTour, SpotlightTourProv
131130
shape = "circle",
132131
shift,
133132
steps,
133+
translucentStatusBar,
134134
} = props;
135135

136136
const [current, setCurrent] = useState<number>();
@@ -236,7 +236,6 @@ export const SpotlightTourProvider = forwardRef<SpotlightTour, SpotlightTourProv
236236

237237
const tour = useMemo((): SpotlightTourCtx => ({
238238
changeSpot,
239-
coordinateAdjustment,
240239
current,
241240
goTo,
242241
next,
@@ -248,7 +247,8 @@ export const SpotlightTourProvider = forwardRef<SpotlightTour, SpotlightTourProv
248247
status,
249248
steps,
250249
stop,
251-
}), [changeSpot, coordinateAdjustment, current, goTo, next, previous, spot, start, steps, stop, pause]);
250+
translucentStatusBar,
251+
}), [changeSpot, translucentStatusBar, current, goTo, next, previous, spot, start, steps, stop, pause]);
252252

253253
useImperativeHandle(ref, () => ({
254254
current,
@@ -270,7 +270,7 @@ export const SpotlightTourProvider = forwardRef<SpotlightTour, SpotlightTourProv
270270
}
271271

272272
<TourOverlay
273-
coordinateAdjustment={coordinateAdjustment}
273+
translucentStatusBar={translucentStatusBar}
274274
backdropOpacity={overlayOpacity}
275275
color={overlayColor}
276276
current={current}

package/src/lib/components/attach-step/AttachStep.component.tsx

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,6 @@ export interface AttachStepProps {
5454
* It can be a single index or multiple ones.
5555
*/
5656
index: Array<number> | number;
57-
/**
58-
* Local offset adjustment for this specific AttachStep.
59-
* This will be added to any global coordinate adjustment.
60-
*/
61-
offset?: {
62-
x?: number;
63-
y?: number;
64-
};
6557
/**
6658
* Style applied to AttachStep wrapper
6759
*/
@@ -75,8 +67,9 @@ export interface AttachStepProps {
7567
* @param props the component props
7668
* @returns an AttachStep React element
7769
*/
78-
export function AttachStep({ children, fill = false, index, offset, style }: AttachStepProps): ReactElement {
79-
const { changeSpot, coordinateAdjustment, current } = useContext(SpotlightTourContext);
70+
export function AttachStep({ children, fill = false, index, style }: AttachStepProps):
71+
ReactElement {
72+
const { changeSpot, current, translucentStatusBar } = useContext(SpotlightTourContext);
8073

8174
const ref = useRef<View>(null);
8275

@@ -85,22 +78,18 @@ export function AttachStep({ children, fill = false, index, offset, style }: Att
8578

8679
if (current !== undefined && indexes.includes(current)) {
8780
ref.current?.measureInWindow((x, y, width, height) => {
88-
// Apply global coordinate adjustment
81+
if (!translucentStatusBar?.enable) {
82+
changeSpot({ height, width, x, y });
83+
return;
84+
}
85+
const { coordinateAdjustment } = translucentStatusBar;
8986
const globalX = coordinateAdjustment?.x || 0;
9087
const globalY = coordinateAdjustment?.y || 0;
9188

92-
// Apply local offset
93-
const localX = offset?.x || 0;
94-
const localY = offset?.y || 0;
95-
96-
// Combine all adjustments
97-
const adjustedX = x + globalX + localX;
98-
const adjustedY = y + globalY + localY;
99-
100-
changeSpot({ height, width, x: adjustedX, y: adjustedY });
89+
changeSpot({ height, width, x: x + globalX, y: y + globalY });
10190
});
10291
}
103-
}, [changeSpot, current, JSON.stringify(index), coordinateAdjustment, offset]);
92+
}, [changeSpot, current, JSON.stringify(index), translucentStatusBar]);
10493

10594
const onLayout = useCallback((event: LayoutChangeEvent): void => {
10695
updateSpot();

package/src/lib/components/tour-overlay/TourOverlay.component.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ export const TourOverlay = forwardRef<TourOverlayRef, TourOverlayProps>((props,
7979
...tooltipProps
8080
} = props;
8181

82-
const { goTo, next, pause, previous, resume, start, steps, stop } = useContext(SpotlightTourContext);
82+
const {
83+
goTo, next, pause, previous, resume, start, steps, stop, translucentStatusBar,
84+
} = useContext(SpotlightTourContext);
8385

8486
const arrowRef = useRef<View>(null);
8587

@@ -187,7 +189,7 @@ export const TourOverlay = forwardRef<TourOverlayRef, TourOverlayProps>((props,
187189
supportedOrientations={["portrait", "landscape", "landscape-left", "landscape-right", "portrait-upside-down"]}
188190
transparent={true}
189191
visible={current !== undefined}
190-
statusBarTranslucent={true}
192+
statusBarTranslucent={translucentStatusBar?.enable ?? false}
191193
>
192194
<View testID="Overlay View" style={Css.overlayView}>
193195
<Svg

0 commit comments

Comments
 (0)