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
@@ -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,19 +181,41 @@ 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={[
@@ -148,35 +231,55 @@ export const TourTooltip = memo(() => {
148
231
  );
149
232
  }
150
233
 
151
- // Default Render
234
+ // 3. Default Render
152
235
  const labels = { ...DEFAULT_LABELS, ...config?.labels };
153
236
  const labelNext = isLast ? labels.finish : labels.next;
154
237
  const labelSkip = labels.skip;
155
238
 
239
+ const isRequired = currentStepData.required === true;
240
+ const isNextDisabled = currentStepData.completed === false;
241
+
156
242
  return (
157
243
  <AnimatedView
158
- // Combined styles: Container (Shadows) + CardStyle (White BG) + TooltipStyle (Position/Opacity)
159
- style={[styles.container, styles.cardStyle, tooltipStyle]}
244
+ style={[styles.container, dynamicStyles.cardStyle, tooltipStyle]}
160
245
  onLayout={handleTooltipLayout}
161
246
  >
162
247
  <View style={styles.header}>
163
- <Text style={styles.title}>{currentStepData.name || 'Step'}</Text>
248
+ <Text style={dynamicStyles.title}>
249
+ {currentStepData.name || 'Step'}
250
+ </Text>
164
251
  <Text style={styles.stepIndicator}>
165
252
  {currentIndex + 1} / {totalSteps}
166
253
  </Text>
167
254
  </View>
168
- <Text style={styles.description}>{currentStepData.description}</Text>
255
+ <Text style={dynamicStyles.description}>
256
+ {currentStepData.description}
257
+ </Text>
169
258
 
170
259
  <View style={styles.footer}>
171
- {!isLast && (
172
- <TouchableOpacity onPress={stop} style={styles.buttonText}>
173
- <Text style={styles.skipText}>{labelSkip}</Text>
260
+ {!isLast && !isRequired && (
261
+ <TouchableOpacity onPress={stop} style={dynamicStyles.buttonText}>
262
+ <Text style={dynamicStyles.skipText}>{labelSkip}</Text>
174
263
  </TouchableOpacity>
175
264
  )}
176
- {isLast && <View style={styles.spacer} />}
265
+ {(isLast || isRequired) && <View style={styles.spacer} />}
177
266
 
178
- <TouchableOpacity onPress={next} style={styles.buttonPrimary}>
179
- <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>
180
283
  </TouchableOpacity>
181
284
  </View>
182
285
  </AnimatedView>
@@ -193,12 +296,6 @@ const styles = StyleSheet.create({
193
296
  elevation: 8,
194
297
  zIndex: 999,
195
298
  },
196
- cardStyle: {
197
- backgroundColor: 'white', // Ensure this is solid white
198
- borderRadius: 12,
199
- padding: 20,
200
- minHeight: 120,
201
- },
202
299
  header: {
203
300
  flexDirection: 'row',
204
301
  justifyContent: 'space-between',
@@ -208,41 +305,11 @@ const styles = StyleSheet.create({
208
305
  fontSize: 12,
209
306
  color: '#999',
210
307
  },
211
- title: {
212
- fontSize: 18,
213
- fontWeight: 'bold',
214
- color: '#000',
215
- flex: 1,
216
- },
217
- description: {
218
- fontSize: 15,
219
- color: '#444',
220
- marginBottom: 20,
221
- lineHeight: 22,
222
- },
223
308
  footer: {
224
309
  flexDirection: 'row',
225
310
  justifyContent: 'space-between',
226
311
  alignItems: 'center',
227
312
  },
228
- buttonText: {
229
- padding: 8,
230
- },
231
- skipText: {
232
- color: '#666',
233
- fontWeight: '600',
234
- },
235
- buttonPrimary: {
236
- backgroundColor: '#007AFF',
237
- paddingVertical: 10,
238
- paddingHorizontal: 20,
239
- borderRadius: 25,
240
- },
241
- primaryButtonText: {
242
- color: '#fff',
243
- fontWeight: 'bold',
244
- fontSize: 14,
245
- },
246
313
  resetStyle: {
247
314
  backgroundColor: 'transparent',
248
315
  shadowOpacity: 0,
@@ -253,4 +320,10 @@ const styles = StyleSheet.create({
253
320
  spacer: {
254
321
  width: 10,
255
322
  },
323
+ disabledButton: {
324
+ opacity: 0.4,
325
+ },
326
+ disabledButtonText: {
327
+ opacity: 0.7,
328
+ },
256
329
  });