react-native-lumen 1.0.1 → 1.1.0

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.
Files changed (51) hide show
  1. package/README.md +763 -231
  2. package/lib/module/components/TourOverlay.js +43 -3
  3. package/lib/module/components/TourOverlay.js.map +1 -1
  4. package/lib/module/components/TourProvider.js +318 -61
  5. package/lib/module/components/TourProvider.js.map +1 -1
  6. package/lib/module/components/TourTooltip.js +113 -73
  7. package/lib/module/components/TourTooltip.js.map +1 -1
  8. package/lib/module/components/TourZone.js +186 -119
  9. package/lib/module/components/TourZone.js.map +1 -1
  10. package/lib/module/constants/defaults.js +43 -0
  11. package/lib/module/constants/defaults.js.map +1 -1
  12. package/lib/module/context/TourContext.js +5 -0
  13. package/lib/module/context/TourContext.js.map +1 -0
  14. package/lib/module/hooks/useTour.js +1 -1
  15. package/lib/module/hooks/useTour.js.map +1 -1
  16. package/lib/module/hooks/useTourScrollView.js +71 -0
  17. package/lib/module/hooks/useTourScrollView.js.map +1 -0
  18. package/lib/module/index.js +6 -0
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/utils/storage.js +188 -0
  21. package/lib/module/utils/storage.js.map +1 -0
  22. package/lib/typescript/src/components/TourOverlay.d.ts.map +1 -1
  23. package/lib/typescript/src/components/TourProvider.d.ts +21 -4
  24. package/lib/typescript/src/components/TourProvider.d.ts.map +1 -1
  25. package/lib/typescript/src/components/TourTooltip.d.ts.map +1 -1
  26. package/lib/typescript/src/components/TourZone.d.ts +19 -1
  27. package/lib/typescript/src/components/TourZone.d.ts.map +1 -1
  28. package/lib/typescript/src/constants/defaults.d.ts +10 -0
  29. package/lib/typescript/src/constants/defaults.d.ts.map +1 -1
  30. package/lib/typescript/src/context/TourContext.d.ts +3 -0
  31. package/lib/typescript/src/context/TourContext.d.ts.map +1 -0
  32. package/lib/typescript/src/hooks/useTourScrollView.d.ts +65 -0
  33. package/lib/typescript/src/hooks/useTourScrollView.d.ts.map +1 -0
  34. package/lib/typescript/src/index.d.ts +4 -0
  35. package/lib/typescript/src/index.d.ts.map +1 -1
  36. package/lib/typescript/src/types/index.d.ts +296 -1
  37. package/lib/typescript/src/types/index.d.ts.map +1 -1
  38. package/lib/typescript/src/utils/storage.d.ts +51 -0
  39. package/lib/typescript/src/utils/storage.d.ts.map +1 -0
  40. package/package.json +173 -171
  41. package/src/components/TourOverlay.tsx +45 -2
  42. package/src/components/TourProvider.tsx +408 -56
  43. package/src/components/TourTooltip.tsx +144 -71
  44. package/src/components/TourZone.tsx +238 -140
  45. package/src/constants/defaults.ts +51 -0
  46. package/src/context/TourContext.ts +4 -0
  47. package/src/hooks/useTour.ts +1 -1
  48. package/src/hooks/useTourScrollView.ts +111 -0
  49. package/src/index.tsx +27 -0
  50. package/src/types/index.ts +306 -1
  51. package/src/utils/storage.ts +226 -0
package/README.md CHANGED
@@ -1,231 +1,763 @@
1
- # React Native Lumen 💡
2
-
3
- > A high-performance, fully customizable app tour library for React Native, powered by Reanimated 3.
4
-
5
- ![Banner](./assets/banner.png)
6
-
7
- ## Demo
8
-
9
- <p>
10
- <img src="./assets/showcase.gif" width="220" alt="App Tour Demo" />
11
- </p>
12
-
13
- ## Features
14
-
15
- - ⚡ **High Performance**: Built with `react-native-reanimated` worklets for 60fps animations.
16
- - 🎨 **Fully Customizable**: Custom Renderers for tooltips, customizable shapes, and backdrops.
17
- - 📱 **Expo Compatible**: Works seamlessly with Expo and bare React Native projects.
18
- - 🤸 **Smooth Transitions**: Fluid morphing animations between steps.
19
- - **Animation Presets**: Ships with beautiful bouncy, gentle, and snappy spring presets.
20
- - 📜 **Auto Scrolling**: Automatically scrolls to next steps.
21
- - 👆 **Interaction Control**: Choose to block or allow interactions with the underlying app.
22
-
23
- ## Requirements
24
-
25
- This library relies on strict peer dependencies to ensure performance:
26
-
27
- - `react-native` >= 0.70.0
28
- - `react-native-reanimated` >= 3.0.0
29
- - `react-native-svg` >= 12.0.0
30
- - `react-native-gesture-handler` >= 2.0.0
31
-
32
- ## Installation
33
-
34
- ```sh
35
- npm install react-native-lumen react-native-reanimated react-native-svg react-native-gesture-handler react-native-worklets
36
- ```
37
-
38
- ## Usage
39
-
40
- 1. **Wrap your App with `TourProvider`**:
41
-
42
- ```tsx
43
- import { TourProvider } from 'react-native-lumen';
44
-
45
- export default function App() {
46
- return (
47
- <TourProvider>
48
- <YourAppContent />
49
- </TourProvider>
50
- );
51
- }
52
- ```
53
-
54
- 2. **Highlight Elements with `TourZone`**:
55
-
56
- ```tsx
57
- import { TourZone } from 'react-native-lumen';
58
-
59
- <TourZone
60
- stepKey="step-1"
61
- name="My Feature"
62
- description="This is an awesome feature you should know about."
63
- order={1}
64
- borderRadius={10}
65
- >
66
- <MyButton />
67
- </TourZone>;
68
- ```
69
-
70
- 3. **Control the Tour**:
71
-
72
- ```tsx
73
- import { useTour } from 'react-native-lumen';
74
-
75
- const MyComponent = () => {
76
- const { start } = useTour();
77
- return <Button title="Start Tour" onPress={() => start()} />;
78
- };
79
- ```
80
-
81
- ## API Documentation
82
-
83
- ### `TourProvider`
84
-
85
- The main context provider. Place this at the root of your application.
86
-
87
- | Prop | Type | Default | Description |
88
- | :---------------- | :---------------- | :---------- | :------------------------------------------------------- |
89
- | `children` | `React.ReactNode` | Required | Application content. |
90
- | `stepsOrder` | `string[]` | `undefined` | Optional array of step keys to define a forced sequence. |
91
- | `backdropOpacity` | `number` | `0.5` | Opacity of the dark background overlay (0-1). |
92
- | `config` | `TourConfig` | `undefined` | Global configuration options. |
93
-
94
- ### `TourZone`
95
-
96
- Wrapper component to register an element as a tour step.
97
-
98
- | Prop | Type | Default | Description |
99
- | :------------- | :------------------- | :---------- | :---------------------------------------------- |
100
- | `stepKey` | `string` | Required | Unique identifier for the step. |
101
- | `name` | `string` | `undefined` | Title of the step. |
102
- | `description` | `string` | Required | Description text shown in the tooltip. |
103
- | `order` | `number` | `undefined` | Order of appearance (if `stepsOrder` not used). |
104
- | `shape` | `'rect' \| 'circle'` | `'rect'` | Shape of the spotlight. |
105
- | `borderRadius` | `number` | `10` | Border radius of the spotlight. |
106
- | `clickable` | `boolean` | `false` | If `true`, the step remains interactive. |
107
- | `style` | `ViewStyle` | `undefined` | Style for the wrapping container. |
108
-
109
- ### `TourConfig`
110
-
111
- Configuration object needed for `TourProvider`.
112
-
113
- ```tsx
114
- interface TourConfig {
115
- /**
116
- * Animation configuration for the spotlight movement.
117
- * You can use presets like WigglySpringConfig, GentleSpringConfig etc.
118
- */
119
- springConfig?: WithSpringConfig;
120
- /**
121
- * If true, prevents interaction with the underlying app while tour is active.
122
- */
123
- preventInteraction?: boolean;
124
- /**
125
- * Custom labels for buttons.
126
- */
127
- labels?: {
128
- next?: string;
129
- previous?: string;
130
- finish?: string;
131
- skip?: string;
132
- };
133
- /**
134
- * Custom renderer for the card/tooltip.
135
- */
136
- renderCard?: (props: CardProps) => React.ReactNode;
137
- /**
138
- * Initial overlay opacity. Default 0.5
139
- */
140
- backdropOpacity?: number;
141
- }
142
- ```
143
-
144
- ## Customization Guide
145
-
146
- ### Animation Presets
147
-
148
- React Native Lumen comes with built-in Reanimated spring configs for easy usage.
149
-
150
- ```tsx
151
- import { TourProvider, WigglySpringConfig } from 'react-native-lumen';
152
-
153
- <TourProvider config={{ springConfig: WigglySpringConfig }}>...</TourProvider>;
154
- ```
155
-
156
- ### Auto Scroll Support
157
-
158
- React Native Lumen supports auto-scrolling to steps that are off-screen. To enable this, simply attach the `scrollViewRef` provided by the hook to your scroll container.
159
-
160
- ```tsx
161
- import { useTour } from 'react-native-lumen';
162
- import Animated from 'react-native-reanimated';
163
-
164
- const MyScrollableScreen = () => {
165
- const { scrollViewRef } = useTour();
166
-
167
- return (
168
- <Animated.ScrollView ref={scrollViewRef}>
169
- {/* ... content with TourZones ... */}
170
- </Animated.ScrollView>
171
- );
172
- };
173
- ```
174
-
175
- > **Note:** The scroll view must be compatible with Reanimated refs (e.g. `Animated.ScrollView`).
176
-
177
- Available presets:
178
-
179
- - `Reanimated3DefaultSpringConfig`
180
- - `WigglySpringConfig` (Bouncy)
181
- - `GentleSpringConfig` (Smooth)
182
- - `SnappySpringConfig` (Fast & Responsive)
183
- - `and more!`
184
-
185
- ### Custom Tooltip Card
186
-
187
- You can fully replace the default tooltip with your own beautiful UI using the `renderCard` prop in `config`.
188
-
189
- ```tsx
190
- import { TourProvider, CardProps } from 'react-native-lumen';
191
-
192
- const CustomCard = ({
193
- step,
194
- next,
195
- prev,
196
- stop,
197
- isLast,
198
- currentStepIndex,
199
- totalSteps,
200
- }: CardProps) => (
201
- <View style={{ padding: 20, backgroundColor: 'white', borderRadius: 20 }}>
202
- <Text style={{ fontWeight: 'bold', fontSize: 20 }}>{step.name}</Text>
203
- <Text>{step.description}</Text>
204
- <Text style={{ color: 'gray' }}>
205
- Step {currentStepIndex + 1} of {totalSteps}
206
- </Text>
207
-
208
- <View style={{ flexDirection: 'row', marginTop: 10 }}>
209
- <Button onPress={stop} title="Close" />
210
- <View style={{ flex: 1 }} />
211
- {!isLast ? (
212
- <Button onPress={next} title="Next" />
213
- ) : (
214
- <Button onPress={stop} title="Finish" />
215
- )}
216
- </View>
217
- </View>
218
- );
219
-
220
- export default function App() {
221
- return (
222
- <TourProvider config={{ renderCard: (props) => <CustomCard {...props} /> }}>
223
- <AppContent />
224
- </TourProvider>
225
- );
226
- }
227
- ```
228
-
229
- ## License
230
-
231
- MIT
1
+ # React Native Lumen 💡
2
+
3
+ > A high-performance, fully customizable app tour library for React Native, powered by Reanimated 3.
4
+
5
+ ![Banner](./assets/banner.png)
6
+
7
+ ## Demo
8
+
9
+ <p>
10
+ <img src="./assets/showcase1.gif" width="220" alt="App Tour Demo" />
11
+ <img src="./assets/showcase2.gif" width="220" alt="App Tour Demo" />
12
+ </p>
13
+
14
+ ## Features
15
+
16
+ - **High Performance**: Built with `react-native-reanimated` worklets for 60fps animations.
17
+ - 🎨 **Fully Customizable**: Custom Renderers for tooltips, customizable shapes, and backdrops.
18
+ - 🌟 **Glow Effects**: Beautiful, customizable glow effects around your highlighted elements.
19
+ - 📱 **Expo Compatible**: Works seamlessly with Expo and bare React Native projects.
20
+ - 🤸 **Smooth Transitions**: Fluid morphing animations between steps.
21
+ - **Animation Presets**: Ships with beautiful bouncy, gentle, and snappy spring presets.
22
+ - 📜 **Auto Scrolling**: Automatically scrolls to next steps.
23
+ - 👆 **Interaction Control**: Choose to block or allow interactions with the underlying app.
24
+ - 🔒 **Step Enforcement**: Gate step progression with `required` and `completed` props.
25
+ - 📱 **Multi-Screen Tours**: Seamlessly run tours across multiple screens/tabs with screen-grouped step ordering.
26
+
27
+ ## Requirements
28
+
29
+ This library relies on strict peer dependencies to ensure performance:
30
+
31
+ - `react-native` >= 0.70.0
32
+ - `react-native-reanimated` >= 3.0.0
33
+ - `react-native-svg` >= 12.0.0
34
+ - `react-native-gesture-handler` >= 2.0.0
35
+
36
+ ## Installation
37
+
38
+ ```sh
39
+ npm install react-native-lumen react-native-reanimated react-native-svg react-native-gesture-handler react-native-worklets
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ > **Note**: For a complete, running example showcasing different configurations and styles, check out the `example` folder in this repository.
45
+
46
+ 1. **Wrap your App with `TourProvider`**:
47
+
48
+ ```tsx
49
+ import { TourProvider, SnappySpringConfig } from 'react-native-lumen';
50
+
51
+ export default function App() {
52
+ return (
53
+ <TourProvider
54
+ stepsOrder={['bio', 'prompt', 'poll']}
55
+ config={{
56
+ springConfig: SnappySpringConfig,
57
+ enableGlow: true, // Enable the glow effect globally
58
+ // See config details below
59
+ }}
60
+ >
61
+ <YourComponentThatNeedsTouring />
62
+ </TourProvider>
63
+ );
64
+ }
65
+ ```
66
+
67
+ 2. **Highlight Elements with `TourZone`**:
68
+
69
+ ```tsx
70
+ import { TourZone } from 'react-native-lumen';
71
+
72
+ <TourZone
73
+ stepKey="step-1"
74
+ name="My Feature"
75
+ description="This is an awesome feature you should know about."
76
+ order={1}
77
+ borderRadius={10}
78
+ >
79
+ <MyButton />
80
+ </TourZone>;
81
+ ```
82
+
83
+ 3. **Control the Tour**:
84
+
85
+ ```tsx
86
+ import { useTour } from 'react-native-lumen';
87
+
88
+ const MyComponent = () => {
89
+ const { start } = useTour();
90
+ return <Button title="Start Tour" onPress={() => start()} />;
91
+ };
92
+ ```
93
+
94
+ ## API Documentation
95
+
96
+ ### `TourProvider`
97
+
98
+ The main context provider. Place this at the root of your application.
99
+
100
+ | Prop | Type | Default | Description |
101
+ | :---------------- | :------------------------------------- | :---------- | :---------------------------------------------------- |
102
+ | `children` | `React.ReactNode` | Required | Application content. |
103
+ | `stepsOrder` | `string[] \| Record<string, string[]>` | `undefined` | Step ordering. Flat array or screen-grouped object. |
104
+ | `backdropOpacity` | `number` | `0.5` | Opacity of the dark background overlay (0-1). |
105
+ | `config` | `TourConfig` | `undefined` | Global configuration options, including `enableGlow`. |
106
+
107
+ ### `TourZone`
108
+
109
+ Wrapper component to register an element as a tour step. All styling props can also be passed via the `zoneStyle` object prop.
110
+
111
+ | Prop | Type | Default | Description |
112
+ | :------------------- | :------------------------------------- | :--------------- | :--------------------------------------------------- |
113
+ | `stepKey` | `string` | Required | Unique identifier for the step. |
114
+ | `name` | `string` | `undefined` | Title of the step. |
115
+ | `description` | `string` | Required | Description text shown in the tooltip. |
116
+ | `order` | `number` | `undefined` | Order of appearance (if `stepsOrder` not used). |
117
+ | `shape` | `'rounded-rect' \| 'circle' \| 'pill'` | `'rounded-rect'` | Shape of the zone cutout. |
118
+ | `borderRadius` | `number` | `10` | Border radius of the zone (for rounded-rect). |
119
+ | `clickable` | `boolean` | `false` | If `true`, the step remains interactive. |
120
+ | `preventInteraction` | `boolean` | `undefined` | Overrides global `preventInteraction` for this step. |
121
+ | `required` | `boolean` | `false` | If `true`, hides the skip button for this step. |
122
+ | `completed` | `boolean` | `undefined` | If `false`, disables the next button until `true`. |
123
+ | `style` | `ViewStyle` | `undefined` | Style for the wrapping container. |
124
+ | `zonePadding` | `number` | `0` | Uniform padding around the highlighted element. |
125
+ | `zonePaddingTop` | `number` | `undefined` | Top padding override. |
126
+ | `zonePaddingRight` | `number` | `undefined` | Right padding override. |
127
+ | `zonePaddingBottom` | `number` | `undefined` | Bottom padding override. |
128
+ | `zonePaddingLeft` | `number` | `undefined` | Left padding override. |
129
+ | `zoneBorderWidth` | `number` | `0` | Width of the zone border. |
130
+ | `zoneBorderColor` | `string` | `'transparent'` | Color of the zone border. |
131
+ | `zoneGlowColor` | `string` | `'#FFFFFF'` | Color of the outer glow effect. |
132
+ | `zoneGlowRadius` | `number` | `10` | Blur radius of the glow effect. |
133
+ | `zoneGlowSpread` | `number` | `5` | Spread radius of the glow effect. |
134
+ | `zoneGlowOffsetX` | `number` | `0` | Horizontal offset of the glow effect. |
135
+ | `zoneGlowOffsetY` | `number` | `0` | Vertical offset of the glow effect. |
136
+ | `zoneStyle` | `ZoneStyle` | `undefined` | Complete zone style object (groups above props). |
137
+ | `renderCustomCard` | `(props) => ReactNode` | `undefined` | Custom render function for this step's card. |
138
+
139
+ ### `TourConfig`
140
+
141
+ Configuration object needed for `TourProvider`.
142
+
143
+ ```tsx
144
+ import { SnappySpringConfig, WigglySpringConfig } from 'react-native-lumen';
145
+
146
+ interface TourConfig {
147
+ /**
148
+ * Animation configuration for the zone movement.
149
+ * You can use presets like WigglySpringConfig, GentleSpringConfig etc.
150
+ */
151
+ springConfig?: WithSpringConfig;
152
+ /**
153
+ * If true, prevents interaction with the underlying app while tour is active.
154
+ */
155
+ preventInteraction?: boolean;
156
+ /**
157
+ * Custom labels for buttons.
158
+ */
159
+ labels?: {
160
+ next?: string;
161
+ previous?: string;
162
+ finish?: string;
163
+ skip?: string;
164
+ };
165
+ /**
166
+ * Custom renderer for the card/tooltip.
167
+ */
168
+ renderCard?: (props: CardProps) => React.ReactNode;
169
+ /**
170
+ * Initial overlay opacity. Default 0.5
171
+ */
172
+ backdropOpacity?: number;
173
+ /**
174
+ * Global zone style settings.
175
+ * Can be overridden per-step via TourZone props.
176
+ */
177
+ zoneStyle?: ZoneStyle;
178
+ /**
179
+ * Persistence configuration for saving/restoring tour progress.
180
+ */
181
+ persistence?: TourPersistenceConfig;
182
+ /**
183
+ * Defines whether to apply a shadow/glow effect to the active tour zone highlight.
184
+ */
185
+ enableGlow?: boolean;
186
+ /**
187
+ * Custom styles for the default tooltip appearance.
188
+ */
189
+ tooltipStyles?: TooltipStyles;
190
+ }
191
+ ```
192
+
193
+ ### `ZoneStyle`
194
+
195
+ Customization options for the zone appearance.
196
+
197
+ ```tsx
198
+ interface ZoneStyle {
199
+ padding?: number; // Uniform padding around the element
200
+ paddingTop?: number; // Top padding override
201
+ paddingRight?: number; // Right padding override
202
+ paddingBottom?: number; // Bottom padding override
203
+ paddingLeft?: number; // Left padding override
204
+ borderRadius?: number; // Border radius for 'rounded-rect' shape
205
+ shape?: 'rounded-rect' | 'circle' | 'pill'; // Zone shape
206
+ borderWidth?: number; // Border ring width
207
+ borderColor?: string; // Border color
208
+ glowColor?: string; // Outer glow color (use rgba/hex-alpha for opacity) (requires enableGlow: true)
209
+ glowRadius?: number; // Glow blur radius (requires enableGlow: true)
210
+ glowSpread?: number; // Glow spread radius (requires enableGlow: true)
211
+ glowOffsetX?: number; // Horizontal offset for the glow (requires enableGlow: true)
212
+ glowOffsetY?: number; // Vertical offset for the glow (requires enableGlow: true)
213
+ springDamping?: number; // Per-step spring damping override
214
+ springStiffness?: number; // Per-step spring stiffness override
215
+ }
216
+ ```
217
+
218
+ ### `CardProps`
219
+
220
+ Props passed to custom card render functions (`renderCard`, `renderCustomCard`).
221
+
222
+ ```tsx
223
+ interface CardProps {
224
+ step: TourStep;
225
+ currentStepIndex: number;
226
+ totalSteps: number;
227
+ next: () => void;
228
+ prev: () => void;
229
+ stop: () => void;
230
+ isFirst: boolean;
231
+ isLast: boolean;
232
+ labels?: TourLabels;
233
+ required?: boolean; // Whether skip should be hidden
234
+ completed?: boolean; // Whether next should be disabled (false = disabled)
235
+ }
236
+ ```
237
+
238
+ ## Step Enforcement
239
+
240
+ Use `required` and `completed` props on `TourZone` to control how users progress through the tour.
241
+
242
+ ### `required` - Hide the Skip Button
243
+
244
+ When `required={true}`, the skip button is hidden. The user must complete the step or press next to continue.
245
+
246
+ ```tsx
247
+ <TourZone
248
+ stepKey="bio"
249
+ order={1}
250
+ description="Edit your bio before continuing."
251
+ required={true}
252
+ >
253
+ <Bio user={user} />
254
+ </TourZone>
255
+ ```
256
+
257
+ ### `completed` - Gate the Next Button
258
+
259
+ When `completed` is set to `false`, the next/finish button is disabled (grayed out, non-pressable). Once the condition is met and `completed` becomes `true`, the button enables.
260
+
261
+ ```tsx
262
+ const [tourComplete, setTourComplete] = useState(false);
263
+
264
+ <TourZone
265
+ stepKey="bio"
266
+ order={1}
267
+ description="Double tap to edit your bio and tell others about yourself."
268
+ required={true}
269
+ completed={tourComplete}
270
+ >
271
+ <Bio
272
+ user={User}
273
+ onUpdateBio={(newBio) => {
274
+ updateBio(newBio);
275
+ setTourComplete(true);
276
+ }}
277
+ />
278
+ </TourZone>;
279
+ ```
280
+
281
+ ### Combining `required` and `completed`
282
+
283
+ Use both props together for full enforcement:
284
+
285
+ - `required={true}` - No skip button, user cannot bypass the step.
286
+ - `completed={tourComplete}` - Next button is disabled until `tourComplete` is `true`.
287
+
288
+ This is useful for onboarding flows where you need the user to perform an action (e.g., edit their bio, upload a photo) before proceeding.
289
+
290
+ ### Enforcement in Custom Cards
291
+
292
+ When using `renderCard` or `renderCustomCard`, the `required` and `completed` values are passed through `CardProps`. You can use them to control your custom UI:
293
+
294
+ ```tsx
295
+ const MyCustomCard = ({ step, next, stop, required, completed }: CardProps) => (
296
+ <View style={styles.card}>
297
+ <Text>{step.description}</Text>
298
+ <View style={styles.buttons}>
299
+ {!required && <Button onPress={stop} title="Skip" />}
300
+ <Button
301
+ onPress={next}
302
+ title="Next"
303
+ disabled={completed === false}
304
+ color={completed === false ? '#ccc' : '#007AFF'}
305
+ />
306
+ </View>
307
+ </View>
308
+ );
309
+ ```
310
+
311
+ ## Multi-Screen Tours
312
+
313
+ React Native Lumen supports tours that span multiple screens (e.g., tabs in a bottom navigation bar). This is common when your app has separate screens for profile, home, settings, etc.
314
+
315
+ ### The Problem
316
+
317
+ When using a navigator (e.g., `BottomTabNavigator`, `CurvedBottomBar`), TourZones on inactive screens are not mounted. If the tour tries to advance to a step on an unmounted screen, it would fail because that step hasn't been registered or measured.
318
+
319
+ ### The Solution: Screen-Grouped `stepsOrder`
320
+
321
+ Pass `stepsOrder` as an object where keys are screen names and values are arrays of step keys:
322
+
323
+ ```tsx
324
+ <TourProvider
325
+ stepsOrder={{
326
+ ProfileSelf: ['bio', 'prompt', 'poll'],
327
+ HomeSwipe: ['filters'],
328
+ SwipeableCards: ['swipeableCards'],
329
+ }}
330
+ config={{
331
+ springConfig: SnappySpringConfig,
332
+ preventInteraction: true,
333
+ persistence: {
334
+ enabled: true,
335
+ tourId: 'main-tour',
336
+ autoResume: true,
337
+ clearOnComplete: true,
338
+ },
339
+ }}
340
+ >
341
+ <CurvedBottomBar.Navigator ...>
342
+ {/* screens */}
343
+ </CurvedBottomBar.Navigator>
344
+ </TourProvider>
345
+ ```
346
+
347
+ This is equivalent to a flat array in terms of step ordering:
348
+
349
+ ```tsx
350
+ stepsOrder={['bio', 'prompt', 'poll', 'filters', 'swipeableCards']}
351
+ ```
352
+
353
+ But with the screen-grouped format, the library knows which steps belong to which screen. When the tour advances to a step on a different screen:
354
+
355
+ 1. The overlay hides automatically.
356
+ 2. The tour enters a **pending** state, waiting for that step's `TourZone` to mount.
357
+ 3. When the user navigates to the correct screen and the `TourZone` mounts, the tour resumes automatically.
358
+
359
+ ### Flat Array (Also Works Cross-Screen)
360
+
361
+ You can also use a flat array for multi-screen tours. The behavior is the same: if the next step isn't mounted, the tour waits for it:
362
+
363
+ ```tsx
364
+ stepsOrder={['bio', 'prompt', 'poll', 'filters', 'swipeableCards']}
365
+ ```
366
+
367
+ ### Example: Multi-Screen Tour
368
+
369
+ ```tsx
370
+ // App.tsx
371
+ <TourProvider
372
+ stepsOrder={{
373
+ ProfileSelf: ['bio', 'prompt', 'poll'],
374
+ HomeSwipe: ['filters'],
375
+ SwipeableCards: ['swipeableCards'],
376
+ }}
377
+ config={{ springConfig: SnappySpringConfig }}
378
+ >
379
+ <BottomTabNavigator />
380
+ </TourProvider>
381
+
382
+ // ProfileSelf.tsx
383
+ <TourZone stepKey="bio" order={1} description="Edit your bio.">
384
+ <Bio />
385
+ </TourZone>
386
+
387
+ <TourZone stepKey="prompt" order={2} description="Add prompts.">
388
+ <PromptCard />
389
+ </TourZone>
390
+
391
+ <TourZone stepKey="poll" order={3} description="Create polls.">
392
+ <PollCard />
393
+ </TourZone>
394
+
395
+ // HomeSwipe.tsx
396
+ <TourZone stepKey="filters" order={1} description="Filter your matches.">
397
+ <FilterButton />
398
+ </TourZone>
399
+
400
+ // SwipeableCards.tsx
401
+ <TourZone stepKey="swipeableCards" order={1} description="Swipe to match!">
402
+ <CardStack />
403
+ </TourZone>
404
+ ```
405
+
406
+ When the tour finishes the ProfileSelf steps and calls `next()` on the last ProfileSelf step (`poll`), the overlay hides. Once the user navigates to HomeSwipe, the `filters` TourZone mounts and the tour automatically resumes.
407
+
408
+ ## Tour Control (`useTour` Hook)
409
+
410
+ The `useTour` hook provides full control over the tour lifecycle.
411
+
412
+ ```tsx
413
+ import { useTour } from 'react-native-lumen';
414
+
415
+ const {
416
+ start, // Start or resume the tour
417
+ stop, // Stop the tour (hides overlay, preserves progress)
418
+ next, // Advance to the next step
419
+ prev, // Go back to the previous step
420
+ currentStep, // Currently active step key (null if inactive)
421
+ steps, // Map of all registered steps
422
+ orderedStepKeys, // Full ordered list of step keys
423
+ clearProgress, // Clear saved progress from storage
424
+ hasSavedProgress, // Whether there's saved progress to resume
425
+ scrollViewRef, // Attach to your ScrollView for auto-scrolling
426
+ } = useTour();
427
+ ```
428
+
429
+ ### `start(stepKey?: string)`
430
+
431
+ Starts (or resumes) the tour.
432
+
433
+ - **No arguments**: Starts at the first step. If persistence is enabled with `autoResume: true`, resumes from the last saved step.
434
+ - **With `stepKey`**: Starts at the specified step.
435
+ - If the target step is not mounted yet (on a different screen), the tour enters a pending state and automatically resumes when the step's `TourZone` mounts.
436
+
437
+ ```tsx
438
+ // Start from the beginning (or resume if autoResume is enabled)
439
+ start();
440
+
441
+ // Start at a specific step
442
+ start('filters');
443
+ ```
444
+
445
+ ### `stop()`
446
+
447
+ Stops the tour and hides the overlay. Does **NOT** clear saved progress. The user can resume later by calling `start()` again.
448
+
449
+ ```tsx
450
+ stop();
451
+ ```
452
+
453
+ ### `next()`
454
+
455
+ Advances to the next step. If the current step has `completed={false}`, the call is ignored (the user must complete the step first).
456
+
457
+ If the next step is on an unmounted screen, the overlay hides and the tour waits for that step to mount.
458
+
459
+ On the last step, `next()` ends the tour and clears progress if `clearOnComplete: true`.
460
+
461
+ ### `prev()`
462
+
463
+ Goes back to the previous step. If the previous step is on an unmounted screen, the overlay hides and the tour waits for it to mount.
464
+
465
+ ### `clearProgress()`
466
+
467
+ Manually clears all saved progress from storage. Useful for a "Reset Tour" button.
468
+
469
+ ```tsx
470
+ const { clearProgress } = useTour();
471
+
472
+ const handleResetTour = async () => {
473
+ await clearProgress();
474
+ };
475
+ ```
476
+
477
+ ## Customization Guide
478
+
479
+ ### Zone Shapes
480
+
481
+ React Native Lumen supports three zone shapes:
482
+
483
+ ```tsx
484
+ // Rounded rectangle (default)
485
+ <TourZone stepKey="feature" shape="rounded-rect" borderRadius={12}>
486
+ <MyComponent />
487
+ </TourZone>
488
+
489
+ // Circle - great for FAB buttons
490
+ <TourZone stepKey="action" shape="circle">
491
+ <FloatingActionButton />
492
+ </TourZone>
493
+
494
+ // Pill - great for horizontal elements like tag rows
495
+ <TourZone stepKey="tags" shape="pill">
496
+ <TagList />
497
+ </TourZone>
498
+ ```
499
+
500
+ ### Glow Styles & Per-Step Zone Styling
501
+
502
+ React Native Lumen features an optional glow effect around the highlighted zone, which can bring a beautiful focus to your UI elements.
503
+
504
+ **1. Enable Glow Globally**
505
+ Set `enableGlow: true` in your `TourProvider` config. This allows the glow effect to render.
506
+
507
+ ```tsx
508
+ <TourProvider config={{ enableGlow: true }}>{/* App Content */}</TourProvider>
509
+ ```
510
+
511
+ **2. Customize Per-Step styles**
512
+ You can customize the standard padding, border, and glow effects per-step using individual `zone*` props or the `zoneStyle` object:
513
+
514
+ ```tsx
515
+ // Using individual props
516
+ <TourZone
517
+ stepKey="important"
518
+ zoneGlowColor="rgba(255, 107, 107, 0.6)"
519
+ zoneBorderColor="#FF6B6B"
520
+ zoneGlowSpread={5}
521
+ zoneGlowOffsetY={2}
522
+ zonePadding={16}
523
+ >
524
+ <ImportantFeature />
525
+ </TourZone>
526
+
527
+ // Using zoneStyle object
528
+ <TourZone
529
+ stepKey="premium"
530
+ zoneStyle={{
531
+ shape: 'pill',
532
+ glowColor: 'rgba(255, 215, 0, 0.5)',
533
+ borderColor: '#FFD700',
534
+ borderWidth: 3,
535
+ }}
536
+ >
537
+ <PremiumBadge />
538
+ </TourZone>
539
+ ```
540
+
541
+ ### Per-Step Custom Cards
542
+
543
+ Render custom tooltip cards for specific steps:
544
+
545
+ ```tsx
546
+ <TourZone
547
+ stepKey="special"
548
+ renderCustomCard={({ step, next, stop, currentStepIndex, totalSteps }) => (
549
+ <View style={styles.customCard}>
550
+ <Text>{step.name}</Text>
551
+ <Text>{step.description}</Text>
552
+ <Button title="Continue" onPress={next} />
553
+ </View>
554
+ )}
555
+ >
556
+ <SpecialFeature />
557
+ </TourZone>
558
+ ```
559
+
560
+ ### Animation Presets
561
+
562
+ React Native Lumen comes with built-in Reanimated spring configs for easy usage.
563
+
564
+ ```tsx
565
+ import { TourProvider, WigglySpringConfig } from 'react-native-lumen';
566
+
567
+ <TourProvider config={{ springConfig: WigglySpringConfig }}>...</TourProvider>;
568
+ ```
569
+
570
+ ### Auto Scroll Support
571
+
572
+ React Native Lumen supports auto-scrolling to steps that are off-screen. To enable this, simply attach the `scrollViewRef` provided by the hook to your scroll container.
573
+
574
+ ```tsx
575
+ import { useTour } from 'react-native-lumen';
576
+ import Animated from 'react-native-reanimated';
577
+
578
+ const MyScrollableScreen = () => {
579
+ const { scrollViewRef } = useTour();
580
+
581
+ return (
582
+ <Animated.ScrollView ref={scrollViewRef}>
583
+ {/* ... content with TourZones ... */}
584
+ </Animated.ScrollView>
585
+ );
586
+ };
587
+ ```
588
+
589
+ > **Note:** The scroll view must be compatible with Reanimated refs (e.g. `Animated.ScrollView`).
590
+
591
+ Available presets:
592
+
593
+ - `Reanimated3DefaultSpringConfig`
594
+ - `WigglySpringConfig` (Bouncy)
595
+ - `GentleSpringConfig` (Smooth)
596
+ - `SnappySpringConfig` (Fast & Responsive)
597
+ - `and more!`
598
+
599
+ ### Custom Tooltip Card
600
+
601
+ You can fully replace the default tooltip with your own beautiful UI using the `renderCard` prop in `config`.
602
+
603
+ ```tsx
604
+ import { TourProvider, CardProps } from 'react-native-lumen';
605
+
606
+ const CustomCard = ({
607
+ step,
608
+ next,
609
+ prev,
610
+ stop,
611
+ isLast,
612
+ currentStepIndex,
613
+ totalSteps,
614
+ required,
615
+ completed,
616
+ }: CardProps) => (
617
+ <View style={{ padding: 20, backgroundColor: 'white', borderRadius: 20 }}>
618
+ <Text style={{ fontWeight: 'bold', fontSize: 20 }}>{step.name}</Text>
619
+ <Text>{step.description}</Text>
620
+ <Text style={{ color: 'gray' }}>
621
+ Step {currentStepIndex + 1} of {totalSteps}
622
+ </Text>
623
+
624
+ <View style={{ flexDirection: 'row', marginTop: 10 }}>
625
+ {!required && <Button onPress={stop} title="Close" />}
626
+ <View style={{ flex: 1 }} />
627
+ {!isLast ? (
628
+ <Button onPress={next} title="Next" disabled={completed === false} />
629
+ ) : (
630
+ <Button onPress={next} title="Finish" disabled={completed === false} />
631
+ )}
632
+ </View>
633
+ </View>
634
+ );
635
+
636
+ export default function App() {
637
+ return (
638
+ <TourProvider config={{ renderCard: (props) => <CustomCard {...props} /> }}>
639
+ <AppContent />
640
+ </TourProvider>
641
+ );
642
+ }
643
+ ```
644
+
645
+ ### Persistence (Resume Tours)
646
+
647
+ React Native Lumen supports saving tour progress so users can resume where they left off. The library auto-detects available storage (MMKV v4 or AsyncStorage).
648
+
649
+ ```tsx
650
+ import { TourProvider } from 'react-native-lumen';
651
+
652
+ export default function App() {
653
+ return (
654
+ <TourProvider
655
+ config={{
656
+ persistence: {
657
+ enabled: true,
658
+ tourId: 'onboarding-v1', // Unique ID for this tour
659
+ autoResume: true, // Auto-resume from saved step (default: true)
660
+ clearOnComplete: true, // Clear progress when tour finishes (default: true)
661
+ maxAge: 7 * 24 * 60 * 60 * 1000, // Optional: expire after 7 days
662
+ },
663
+ }}
664
+ >
665
+ <AppContent />
666
+ </TourProvider>
667
+ );
668
+ }
669
+ ```
670
+
671
+ #### Storage Support
672
+
673
+ The library automatically detects and uses:
674
+
675
+ - **MMKV v4** (`react-native-mmkv` ^4.0.0) - Fastest, recommended
676
+ - **AsyncStorage** (`@react-native-async-storage/async-storage`) - Fallback
677
+
678
+ No additional setup required if either package is installed.
679
+
680
+ #### Custom Storage
681
+
682
+ You can provide a custom storage adapter:
683
+
684
+ ```tsx
685
+ import { TourProvider, type StorageAdapter } from 'react-native-lumen';
686
+
687
+ const customStorage: StorageAdapter = {
688
+ getItem: (key) => myStorage.get(key),
689
+ setItem: (key, value) => myStorage.set(key, value),
690
+ removeItem: (key) => myStorage.remove(key),
691
+ };
692
+
693
+ <TourProvider
694
+ config={{
695
+ persistence: {
696
+ enabled: true,
697
+ tourId: 'my-tour',
698
+ storage: customStorage,
699
+ },
700
+ }}
701
+ >
702
+ ...
703
+ </TourProvider>;
704
+ ```
705
+
706
+ #### Persistence API
707
+
708
+ ```tsx
709
+ const { start, clearProgress, hasSavedProgress } = useTour();
710
+
711
+ // Check if there's saved progress
712
+ if (hasSavedProgress) {
713
+ // Show "Resume Tour" button
714
+ }
715
+
716
+ // Start tour (auto-resumes if enabled)
717
+ start();
718
+
719
+ // Clear saved progress manually
720
+ await clearProgress();
721
+ ```
722
+
723
+ #### Manually Clearing Storage
724
+
725
+ If you need to clear tour data directly from storage outside the tour context (e.g., during logout or a full app reset):
726
+
727
+ **MMKV v4:**
728
+
729
+ ```tsx
730
+ import { createMMKV } from 'react-native-mmkv';
731
+
732
+ // The library uses a dedicated MMKV instance with this ID
733
+ const storage = createMMKV({ id: 'react-native-lumen-tour' });
734
+
735
+ // Clear a specific tour
736
+ storage.remove('@lumen_tour_onboarding-v1');
737
+
738
+ // Or clear all tour data - delete all keys starting with @lumen_tour_
739
+ const allKeys = storage.getAllKeys();
740
+ allKeys
741
+ .filter((key) => key.startsWith('@lumen_tour_'))
742
+ .forEach((key) => storage.remove(key));
743
+ ```
744
+
745
+ **AsyncStorage:**
746
+
747
+ ```tsx
748
+ import AsyncStorage from '@react-native-async-storage/async-storage';
749
+
750
+ // Clear a specific tour
751
+ await AsyncStorage.removeItem('@lumen_tour_onboarding-v1');
752
+
753
+ // Or clear all tour data
754
+ const allKeys = await AsyncStorage.getAllKeys();
755
+ const tourKeys = allKeys.filter((key) => key.startsWith('@lumen_tour_'));
756
+ await AsyncStorage.multiRemove(tourKeys);
757
+ ```
758
+
759
+ The storage key format is `@lumen_tour_{tourId}` where `tourId` is the value you passed in `persistence.tourId`.
760
+
761
+ ## License
762
+
763
+ MIT