react-native-lumen 1.0.0 → 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 -63
  5. package/lib/module/components/TourProvider.js.map +1 -1
  6. package/lib/module/components/TourTooltip.js +121 -79
  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 +409 -57
  43. package/src/components/TourTooltip.tsx +151 -74
  44. package/src/components/TourZone.tsx +238 -141
  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
@@ -13,11 +13,74 @@ import Animated, {
13
13
  Extrapolation,
14
14
  } from 'react-native-reanimated';
15
15
  import { useTour } from '../hooks/useTour';
16
- import type { CardProps, InternalTourContextType } from '../types';
16
+ import type {
17
+ CardProps,
18
+ InternalTourContextType,
19
+ TooltipStyles,
20
+ } from '../types';
17
21
  import { DEFAULT_LABELS } from '../constants/defaults';
18
22
 
19
23
  const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
20
24
 
25
+ const getDynamicStyles = (tooltipStyles?: TooltipStyles) => {
26
+ const defaultStyles = {
27
+ backgroundColor: tooltipStyles?.backgroundColor || 'white',
28
+ borderRadius: tooltipStyles?.borderRadius || 12,
29
+ titleColor: tooltipStyles?.titleColor || '#000',
30
+ descriptionColor: tooltipStyles?.descriptionColor || '#444',
31
+ primaryButtonColor: tooltipStyles?.primaryButtonColor || '#007AFF',
32
+ primaryButtonTextColor: tooltipStyles?.primaryButtonTextColor || '#fff',
33
+ primaryButtonBorderRadius: tooltipStyles?.primaryButtonBorderRadius || 25,
34
+ skipButtonTextColor: tooltipStyles?.skipButtonTextColor || '#666',
35
+ };
36
+
37
+ return StyleSheet.create({
38
+ cardStyle: {
39
+ backgroundColor: defaultStyles.backgroundColor,
40
+ borderRadius: defaultStyles.borderRadius,
41
+ padding: 20,
42
+ minHeight: 120,
43
+ ...(tooltipStyles?.containerStyle || {}),
44
+ },
45
+ title: {
46
+ fontSize: 18,
47
+ fontWeight: 'bold',
48
+ color: defaultStyles.titleColor,
49
+ flex: 1,
50
+ ...(tooltipStyles?.titleStyle || {}),
51
+ },
52
+ description: {
53
+ fontSize: 15,
54
+ color: defaultStyles.descriptionColor,
55
+ marginBottom: 20,
56
+ lineHeight: 22,
57
+ ...(tooltipStyles?.descriptionStyle || {}),
58
+ },
59
+ buttonPrimary: {
60
+ backgroundColor: defaultStyles.primaryButtonColor,
61
+ paddingVertical: 10,
62
+ paddingHorizontal: 20,
63
+ borderRadius: defaultStyles.primaryButtonBorderRadius,
64
+ ...(tooltipStyles?.primaryButtonStyle || {}),
65
+ },
66
+ primaryButtonText: {
67
+ color: defaultStyles.primaryButtonTextColor,
68
+ fontWeight: 'bold',
69
+ fontSize: 14,
70
+ ...(tooltipStyles?.primaryButtonTextStyle || {}),
71
+ },
72
+ skipText: {
73
+ color: defaultStyles.skipButtonTextColor,
74
+ fontWeight: '600',
75
+ ...(tooltipStyles?.skipButtonTextStyle || {}),
76
+ },
77
+ buttonText: {
78
+ padding: 8,
79
+ ...(tooltipStyles?.skipButtonStyle || {}),
80
+ },
81
+ });
82
+ };
83
+
21
84
  export const TourTooltip = memo(() => {
22
85
  const {
23
86
  targetX,
@@ -31,6 +94,7 @@ export const TourTooltip = memo(() => {
31
94
  stop,
32
95
  opacity,
33
96
  config,
97
+ orderedStepKeys,
34
98
  } = useTour() as InternalTourContextType;
35
99
 
36
100
  const currentStepData = currentStep ? steps[currentStep] : null;
@@ -38,15 +102,13 @@ export const TourTooltip = memo(() => {
38
102
  const tooltipHeight = useSharedValue(150);
39
103
  const [tooltipWidth] = useState(280);
40
104
 
41
- const orderedSteps = useMemo(() => {
42
- const keys = Object.keys(steps);
43
- if (keys.length > 0) {
44
- return keys.sort(
45
- (a, b) => (steps[a]?.order ?? 0) - (steps[b]?.order ?? 0)
46
- );
47
- }
48
- return keys;
49
- }, [steps]);
105
+ const dynamicStyles = useMemo(
106
+ () => getDynamicStyles(config?.tooltipStyles),
107
+ [config?.tooltipStyles]
108
+ );
109
+
110
+ // Use orderedStepKeys from context (consistent with TourProvider's ordering)
111
+ const orderedSteps = orderedStepKeys;
50
112
 
51
113
  const currentIndex = currentStep ? orderedSteps.indexOf(currentStep) : -1;
52
114
  const totalSteps = orderedSteps.length;
@@ -93,8 +155,7 @@ export const TourTooltip = memo(() => {
93
155
  width: tooltipWidth,
94
156
  left: clampedLeft,
95
157
  opacity: activeOpacity,
96
- // Add explicit background here for Reanimated to treat it as a solid block layer
97
- backgroundColor: 'white',
158
+ backgroundColor: config?.tooltipStyles?.backgroundColor || 'white',
98
159
  transform: [{ translateY: interpolate(activeOpacity, [0, 1], [10, 0]) }],
99
160
  };
100
161
 
@@ -120,32 +181,48 @@ export const TourTooltip = memo(() => {
120
181
  }
121
182
  };
122
183
 
123
- // Custom Render
184
+ // Build card props for custom renders
185
+ const cardProps: CardProps = {
186
+ step: currentStepData,
187
+ currentStepIndex: currentIndex,
188
+ totalSteps,
189
+ next,
190
+ prev,
191
+ stop,
192
+ isFirst,
193
+ isLast,
194
+ labels: config?.labels,
195
+ required: currentStepData.required,
196
+ completed: currentStepData.completed,
197
+ };
198
+
199
+ // Priority: Per-step custom card > Global custom card > Default card
200
+ // 1. Per-step custom render (highest priority)
201
+ if (currentStepData.renderCustomCard) {
202
+ return (
203
+ <AnimatedView
204
+ style={[
205
+ styles.container,
206
+ tooltipStyle,
207
+ // Reset styles for custom render so the user has full control
208
+ styles.resetStyle,
209
+ ]}
210
+ onLayout={handleTooltipLayout}
211
+ >
212
+ {currentStepData.renderCustomCard(cardProps)}
213
+ </AnimatedView>
214
+ );
215
+ }
216
+
217
+ // 2. Global custom render
124
218
  if (config?.renderCard) {
125
- const cardProps: CardProps = {
126
- step: currentStepData,
127
- currentStepIndex: currentIndex,
128
- totalSteps,
129
- next,
130
- prev,
131
- stop,
132
- isFirst,
133
- isLast,
134
- labels: config.labels,
135
- };
136
219
  return (
137
220
  <AnimatedView
138
221
  style={[
139
222
  styles.container,
140
223
  tooltipStyle,
141
224
  // Reset styles for custom render so the user has full control
142
- {
143
- backgroundColor: 'transparent',
144
- shadowOpacity: 0,
145
- elevation: 0,
146
- padding: 0,
147
- borderRadius: 0,
148
- },
225
+ styles.resetStyle,
149
226
  ]}
150
227
  onLayout={handleTooltipLayout}
151
228
  >
@@ -154,35 +231,55 @@ export const TourTooltip = memo(() => {
154
231
  );
155
232
  }
156
233
 
157
- // Default Render
234
+ // 3. Default Render
158
235
  const labels = { ...DEFAULT_LABELS, ...config?.labels };
159
236
  const labelNext = isLast ? labels.finish : labels.next;
160
237
  const labelSkip = labels.skip;
161
238
 
239
+ const isRequired = currentStepData.required === true;
240
+ const isNextDisabled = currentStepData.completed === false;
241
+
162
242
  return (
163
243
  <AnimatedView
164
- // Combined styles: Container (Shadows) + CardStyle (White BG) + TooltipStyle (Position/Opacity)
165
- style={[styles.container, styles.cardStyle, tooltipStyle]}
244
+ style={[styles.container, dynamicStyles.cardStyle, tooltipStyle]}
166
245
  onLayout={handleTooltipLayout}
167
246
  >
168
247
  <View style={styles.header}>
169
- <Text style={styles.title}>{currentStepData.name || 'Step'}</Text>
248
+ <Text style={dynamicStyles.title}>
249
+ {currentStepData.name || 'Step'}
250
+ </Text>
170
251
  <Text style={styles.stepIndicator}>
171
252
  {currentIndex + 1} / {totalSteps}
172
253
  </Text>
173
254
  </View>
174
- <Text style={styles.description}>{currentStepData.description}</Text>
255
+ <Text style={dynamicStyles.description}>
256
+ {currentStepData.description}
257
+ </Text>
175
258
 
176
259
  <View style={styles.footer}>
177
- {!isLast && (
178
- <TouchableOpacity onPress={stop} style={styles.buttonText}>
179
- <Text style={styles.skipText}>{labelSkip}</Text>
260
+ {!isLast && !isRequired && (
261
+ <TouchableOpacity onPress={stop} style={dynamicStyles.buttonText}>
262
+ <Text style={dynamicStyles.skipText}>{labelSkip}</Text>
180
263
  </TouchableOpacity>
181
264
  )}
182
- {isLast && <View style={{ width: 10 }} />}
265
+ {(isLast || isRequired) && <View style={styles.spacer} />}
183
266
 
184
- <TouchableOpacity onPress={next} style={styles.buttonPrimary}>
185
- <Text style={styles.primaryButtonText}>{labelNext}</Text>
267
+ <TouchableOpacity
268
+ onPress={isNextDisabled ? undefined : next}
269
+ disabled={isNextDisabled}
270
+ style={[
271
+ dynamicStyles.buttonPrimary,
272
+ isNextDisabled && styles.disabledButton,
273
+ ]}
274
+ >
275
+ <Text
276
+ style={[
277
+ dynamicStyles.primaryButtonText,
278
+ isNextDisabled && styles.disabledButtonText,
279
+ ]}
280
+ >
281
+ {labelNext}
282
+ </Text>
186
283
  </TouchableOpacity>
187
284
  </View>
188
285
  </AnimatedView>
@@ -199,12 +296,6 @@ const styles = StyleSheet.create({
199
296
  elevation: 8,
200
297
  zIndex: 999,
201
298
  },
202
- cardStyle: {
203
- backgroundColor: 'white', // Ensure this is solid white
204
- borderRadius: 12,
205
- padding: 20,
206
- minHeight: 120,
207
- },
208
299
  header: {
209
300
  flexDirection: 'row',
210
301
  justifyContent: 'space-between',
@@ -214,39 +305,25 @@ const styles = StyleSheet.create({
214
305
  fontSize: 12,
215
306
  color: '#999',
216
307
  },
217
- title: {
218
- fontSize: 18,
219
- fontWeight: 'bold',
220
- color: '#000',
221
- flex: 1,
222
- },
223
- description: {
224
- fontSize: 15,
225
- color: '#444',
226
- marginBottom: 20,
227
- lineHeight: 22,
228
- },
229
308
  footer: {
230
309
  flexDirection: 'row',
231
310
  justifyContent: 'space-between',
232
311
  alignItems: 'center',
233
312
  },
234
- buttonText: {
235
- padding: 8,
313
+ resetStyle: {
314
+ backgroundColor: 'transparent',
315
+ shadowOpacity: 0,
316
+ elevation: 0,
317
+ padding: 0,
318
+ borderRadius: 0,
236
319
  },
237
- skipText: {
238
- color: '#666',
239
- fontWeight: '600',
320
+ spacer: {
321
+ width: 10,
240
322
  },
241
- buttonPrimary: {
242
- backgroundColor: '#007AFF',
243
- paddingVertical: 10,
244
- paddingHorizontal: 20,
245
- borderRadius: 25,
323
+ disabledButton: {
324
+ opacity: 0.4,
246
325
  },
247
- primaryButtonText: {
248
- color: '#fff',
249
- fontWeight: 'bold',
250
- fontSize: 14,
326
+ disabledButtonText: {
327
+ opacity: 0.7,
251
328
  },
252
329
  });