react-native-lumen 1.1.0 → 1.1.2

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 (34) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +75 -712
  3. package/lib/module/components/TourOverlay.js +10 -50
  4. package/lib/module/components/TourOverlay.js.map +1 -1
  5. package/lib/module/components/TourProvider.js +41 -33
  6. package/lib/module/components/TourProvider.js.map +1 -1
  7. package/lib/module/components/TourTooltip.js +1 -1
  8. package/lib/module/components/TourZone.js +67 -30
  9. package/lib/module/components/TourZone.js.map +1 -1
  10. package/lib/module/constants/animations.js +16 -16
  11. package/lib/module/constants/defaults.js +5 -5
  12. package/lib/module/hooks/useTourScrollView.js +28 -25
  13. package/lib/module/hooks/useTourScrollView.js.map +1 -1
  14. package/lib/module/utils/storage.js +26 -26
  15. package/lib/typescript/src/components/TourOverlay.d.ts.map +1 -1
  16. package/lib/typescript/src/components/TourProvider.d.ts.map +1 -1
  17. package/lib/typescript/src/components/TourZone.d.ts.map +1 -1
  18. package/lib/typescript/src/hooks/useTourScrollView.d.ts +12 -1
  19. package/lib/typescript/src/hooks/useTourScrollView.d.ts.map +1 -1
  20. package/lib/typescript/src/types/index.d.ts +20 -0
  21. package/lib/typescript/src/types/index.d.ts.map +1 -1
  22. package/package.json +2 -6
  23. package/src/components/TourOverlay.tsx +0 -196
  24. package/src/components/TourProvider.tsx +0 -713
  25. package/src/components/TourTooltip.tsx +0 -329
  26. package/src/components/TourZone.tsx +0 -469
  27. package/src/constants/animations.ts +0 -71
  28. package/src/constants/defaults.ts +0 -66
  29. package/src/context/TourContext.ts +0 -4
  30. package/src/hooks/useTour.ts +0 -10
  31. package/src/hooks/useTourScrollView.ts +0 -111
  32. package/src/index.tsx +0 -35
  33. package/src/types/index.ts +0 -447
  34. package/src/utils/storage.ts +0 -226
@@ -1,469 +0,0 @@
1
- import React, {
2
- useEffect,
3
- useCallback,
4
- useMemo,
5
- type ComponentType,
6
- } from 'react';
7
- import type { ViewStyle, StyleProp } from 'react-native';
8
- import { useTour } from '../hooks/useTour';
9
- import {
10
- useAnimatedRef,
11
- measure,
12
- useFrameCallback,
13
- withSpring,
14
- default as Animated,
15
- type AnimatedRef,
16
- useSharedValue,
17
- } from 'react-native-reanimated';
18
- import { Dimensions } from 'react-native';
19
- import type {
20
- InternalTourContextType,
21
- ZoneStyle,
22
- ZoneShape,
23
- CardProps,
24
- } from '../types';
25
-
26
- const { height: SCREEN_HEIGHT } = Dimensions.get('window');
27
-
28
- const AnimatedView = Animated.View as unknown as ComponentType<any>;
29
-
30
- interface TourZoneProps {
31
- stepKey: string;
32
- name?: string;
33
- description: string;
34
- order?: number;
35
- shape?: ZoneShape;
36
- borderRadius?: number;
37
- children: React.ReactNode;
38
- style?: StyleProp<ViewStyle>;
39
- clickable?: boolean;
40
- preventInteraction?: boolean;
41
- required?: boolean;
42
- completed?: boolean;
43
- zonePadding?: number;
44
- zonePaddingTop?: number;
45
- zonePaddingRight?: number;
46
- zonePaddingBottom?: number;
47
- zonePaddingLeft?: number;
48
- zoneBorderWidth?: number;
49
- zoneBorderColor?: string;
50
- zoneGlowColor?: string;
51
- zoneGlowRadius?: number;
52
- zoneGlowSpread?: number;
53
- zoneGlowOffsetX?: number;
54
- zoneGlowOffsetY?: number;
55
- zoneStyle?: ZoneStyle;
56
- renderCustomCard?: (props: CardProps) => React.ReactNode;
57
- }
58
-
59
- export const TourZone: React.FC<TourZoneProps> = ({
60
- stepKey,
61
- name,
62
- description,
63
- order,
64
- shape = 'rounded-rect',
65
- borderRadius = 10,
66
- children,
67
- style,
68
- clickable,
69
- preventInteraction,
70
- required,
71
- completed,
72
- zonePadding,
73
- zonePaddingTop,
74
- zonePaddingRight,
75
- zonePaddingBottom,
76
- zonePaddingLeft,
77
- zoneBorderWidth,
78
- zoneBorderColor,
79
- zoneGlowColor,
80
- zoneGlowRadius,
81
- zoneGlowSpread,
82
- zoneGlowOffsetX,
83
- zoneGlowOffsetY,
84
- zoneStyle,
85
- renderCustomCard,
86
- }) => {
87
- const {
88
- registerStep,
89
- unregisterStep,
90
- updateStepLayout,
91
- currentStep,
92
- containerRef,
93
- scrollViewRef,
94
- targetX,
95
- targetY,
96
- targetWidth,
97
- targetHeight,
98
- targetRadius,
99
- config,
100
- } = useTour() as InternalTourContextType;
101
-
102
- const viewRef = useAnimatedRef<any>();
103
- const isActive = currentStep === stepKey;
104
-
105
- // The critical lock for the UI thread
106
- const isScrolling = useSharedValue(false);
107
-
108
- console.log(`zoneGlowRadius ${stepKey}`, zoneGlowRadius);
109
-
110
- const resolvedZoneStyle: ZoneStyle = useMemo(
111
- () => ({
112
- ...zoneStyle,
113
- ...(zonePadding !== undefined && { padding: zonePadding }),
114
- ...(zonePaddingTop !== undefined && { paddingTop: zonePaddingTop }),
115
- ...(zonePaddingRight !== undefined && { paddingRight: zonePaddingRight }),
116
- ...(zonePaddingBottom !== undefined && {
117
- paddingBottom: zonePaddingBottom,
118
- }),
119
- ...(zonePaddingLeft !== undefined && { paddingLeft: zonePaddingLeft }),
120
- ...(zoneBorderWidth !== undefined && { borderWidth: zoneBorderWidth }),
121
- ...(zoneBorderColor !== undefined && { borderColor: zoneBorderColor }),
122
- ...(zoneGlowColor !== undefined && { glowColor: zoneGlowColor }),
123
- ...(zoneGlowRadius !== undefined && { glowRadius: zoneGlowRadius }),
124
- ...(zoneGlowSpread !== undefined && { glowSpread: zoneGlowSpread }),
125
- ...(zoneGlowOffsetX !== undefined && { glowOffsetX: zoneGlowOffsetX }),
126
- ...(zoneGlowOffsetY !== undefined && { glowOffsetY: zoneGlowOffsetY }),
127
- shape,
128
- borderRadius,
129
- }),
130
- [
131
- zoneStyle,
132
- zonePadding,
133
- zonePaddingTop,
134
- zonePaddingRight,
135
- zonePaddingBottom,
136
- zonePaddingLeft,
137
- zoneBorderWidth,
138
- zoneBorderColor,
139
- zoneGlowColor,
140
- zoneGlowRadius,
141
- zoneGlowSpread,
142
- zoneGlowOffsetX,
143
- zoneGlowOffsetY,
144
- shape,
145
- borderRadius,
146
- ]
147
- );
148
-
149
- /**
150
- * Captures the final, perfect coordinates and UNLOCKS the UI thread.
151
- * This is explicitly the ONLY function allowed to set isScrolling.value = false.
152
- */
153
- const measureJS = useCallback(() => {
154
- if (!isActive) return;
155
-
156
- const view = viewRef.current as any;
157
- const container = containerRef.current as any;
158
-
159
- if (view && container) {
160
- view.measure(
161
- (
162
- _x: number,
163
- _y: number,
164
- width: number,
165
- height: number,
166
- pageX: number,
167
- pageY: number
168
- ) => {
169
- container.measure(
170
- (
171
- _cx: number,
172
- _cy: number,
173
- _cw: number,
174
- _ch: number,
175
- containerPageX: number,
176
- containerPageY: number
177
- ) => {
178
- if (width > 0 && height > 0 && !isNaN(pageX) && !isNaN(pageY)) {
179
- const finalX = pageX - containerPageX;
180
- const finalY = pageY - containerPageY;
181
-
182
- updateStepLayout(stepKey, {
183
- x: finalX,
184
- y: finalY,
185
- width,
186
- height,
187
- });
188
-
189
- // Unlock the UI thread to take over tracking
190
- isScrolling.value = false;
191
- }
192
- }
193
- );
194
- }
195
- );
196
- }
197
- }, [containerRef, isActive, isScrolling, stepKey, updateStepLayout, viewRef]);
198
-
199
- /**
200
- * Unified Pipeline: Measure -> Predict Scroll -> Scroll -> Measure Final -> Unlock
201
- * Replaces all independent timers to prevent race conditions on consecutive steps.
202
- */
203
- useEffect(() => {
204
- if (!isActive || !scrollViewRef?.current || !viewRef.current) {
205
- return;
206
- }
207
-
208
- let cancelled = false;
209
- let attemptCount = 0;
210
- const maxAttempts = 5;
211
- let hasInitiatedScroll = false;
212
-
213
- // 1. Lock immediately so the UI thread doesn't grab off-screen coordinates
214
- isScrolling.value = true;
215
-
216
- const checkAndScroll = (delay: number) => {
217
- const timeoutId = setTimeout(() => {
218
- if (cancelled || hasInitiatedScroll) return;
219
- attemptCount++;
220
-
221
- const view = viewRef.current as any;
222
- const scroll = scrollViewRef.current as any;
223
- const container = containerRef.current as any;
224
-
225
- view.measure(
226
- (
227
- _mx: number,
228
- _my: number,
229
- mw: number,
230
- mh: number,
231
- px: number,
232
- py: number
233
- ) => {
234
- if (cancelled) return;
235
-
236
- if (mw > 0 && mh > 0 && !isNaN(px) && !isNaN(py)) {
237
- const topBuffer = 100;
238
- const bottomBuffer = 150;
239
- const needsScroll =
240
- py < topBuffer || py + mh > SCREEN_HEIGHT - bottomBuffer;
241
-
242
- if (needsScroll) {
243
- hasInitiatedScroll = true;
244
-
245
- scroll.measure(
246
- (
247
- _sx: number,
248
- _sy: number,
249
- _sw: number,
250
- _sh: number,
251
- scrollPx: number,
252
- scrollPy: number
253
- ) => {
254
- if (cancelled) return;
255
-
256
- if (view.measureLayout) {
257
- view.measureLayout(
258
- scroll,
259
- (contentX: number, contentY: number) => {
260
- if (cancelled) return;
261
-
262
- const centerY =
263
- contentY - SCREEN_HEIGHT / 2 + mh / 2 + 50;
264
- const scrollY = Math.max(0, centerY);
265
-
266
- container.measure(
267
- (
268
- _cx: number,
269
- _cy: number,
270
- _cw: number,
271
- _ch: number,
272
- containerPx: number,
273
- containerPy: number
274
- ) => {
275
- if (cancelled) return;
276
-
277
- // Calculate predictive screen coordinates so the zone smoothly jumps
278
- // to the destination *while* the screen is scrolling.
279
- const targetScreenY =
280
- scrollPy + contentY - scrollY - containerPy;
281
- const targetScreenX =
282
- scrollPx + contentX - containerPx;
283
-
284
- updateStepLayout(stepKey, {
285
- x: targetScreenX,
286
- y: targetScreenY,
287
- width: mw,
288
- height: mh,
289
- });
290
-
291
- try {
292
- scroll.scrollTo({ y: scrollY, animated: true });
293
- } catch (e) {
294
- console.error(e);
295
- }
296
-
297
- // Wait for the scroll animation to settle, then verify exact position
298
- setTimeout(() => {
299
- if (!cancelled) measureJS();
300
- }, 800);
301
- }
302
- );
303
- },
304
- () => {
305
- // Fallback if measureLayout is unavailable
306
- setTimeout(() => {
307
- if (!cancelled) measureJS();
308
- }, 800);
309
- }
310
- );
311
- }
312
- }
313
- );
314
- } else {
315
- // No scroll needed, just get the perfect coordinates and unlock
316
- measureJS();
317
- }
318
- } else if (attemptCount < maxAttempts) {
319
- checkAndScroll(150);
320
- }
321
- }
322
- );
323
- }, delay);
324
-
325
- return timeoutId;
326
- };
327
-
328
- const initialTimeout = checkAndScroll(50);
329
-
330
- return () => {
331
- cancelled = true;
332
- clearTimeout(initialTimeout);
333
- };
334
- }, [
335
- isActive,
336
- scrollViewRef,
337
- viewRef,
338
- containerRef,
339
- stepKey,
340
- updateStepLayout,
341
- measureJS,
342
- isScrolling,
343
- ]);
344
-
345
- // UI Thread tracking
346
- useFrameCallback(() => {
347
- 'worklet';
348
- if (!isActive || isScrolling.value) {
349
- return;
350
- }
351
- try {
352
- const measured = measure(viewRef);
353
- const container = measure(containerRef as AnimatedRef<any>);
354
-
355
- if (measured && container) {
356
- const x = measured.pageX - container.pageX;
357
- const y = measured.pageY - container.pageY;
358
- const width = measured.width;
359
- const height = measured.height;
360
-
361
- if (
362
- width > 0 &&
363
- height > 0 &&
364
- !isNaN(x) &&
365
- !isNaN(y) &&
366
- isFinite(x) &&
367
- isFinite(y)
368
- ) {
369
- const springConfig = config?.springConfig ?? {
370
- damping: 100,
371
- stiffness: 100,
372
- };
373
-
374
- const zpt =
375
- resolvedZoneStyle.paddingTop ?? resolvedZoneStyle.padding ?? 0;
376
- const zpr =
377
- resolvedZoneStyle.paddingRight ?? resolvedZoneStyle.padding ?? 0;
378
- const zpb =
379
- resolvedZoneStyle.paddingBottom ?? resolvedZoneStyle.padding ?? 0;
380
- const zpl =
381
- resolvedZoneStyle.paddingLeft ?? resolvedZoneStyle.padding ?? 0;
382
- const zShape = resolvedZoneStyle.shape ?? 'rounded-rect';
383
-
384
- let sx = x - zpl;
385
- let sy = y - zpt;
386
- let sw = width + zpl + zpr;
387
- let sh = height + zpt + zpb;
388
- let sr = borderRadius;
389
-
390
- if (zShape === 'circle') {
391
- const cx = x + width / 2;
392
- const cy = y + height / 2;
393
- const radius =
394
- Math.max(width, height) / 2 + (resolvedZoneStyle.padding ?? 0);
395
- sx = cx - radius;
396
- sy = cy - radius;
397
- sw = radius * 2;
398
- sh = radius * 2;
399
- sr = radius;
400
- } else if (zShape === 'pill') {
401
- sx = x - zpl;
402
- sy = y - zpt;
403
- sw = width + zpl + zpr;
404
- sh = height + zpt + zpb;
405
- sr = sh / 2;
406
- }
407
-
408
- targetX.value = withSpring(sx, springConfig);
409
- targetY.value = withSpring(sy, springConfig);
410
- targetWidth.value = withSpring(sw, springConfig);
411
- targetHeight.value = withSpring(sh, springConfig);
412
- targetRadius.value = withSpring(sr, springConfig);
413
- }
414
- }
415
- } catch {
416
- // Silently ignore measurement errors on UI thread
417
- }
418
- }, isActive);
419
-
420
- // Sync position if the element physically resizes, but strictly avoid
421
- // measuring if we are currently handling an orchestrated scroll.
422
- const onLayout = useCallback(() => {
423
- if (isActive && !isScrolling.value) {
424
- measureJS();
425
- }
426
- }, [isActive, isScrolling.value, measureJS]);
427
-
428
- useEffect(() => {
429
- registerStep({
430
- key: stepKey,
431
- name,
432
- description,
433
- order,
434
- clickable,
435
- preventInteraction,
436
- required,
437
- completed,
438
- meta: { shape: resolvedZoneStyle.shape, borderRadius },
439
- zoneStyle: resolvedZoneStyle,
440
- renderCustomCard,
441
- });
442
- return () => unregisterStep(stepKey);
443
- }, [
444
- stepKey,
445
- name,
446
- description,
447
- order,
448
- borderRadius,
449
- registerStep,
450
- unregisterStep,
451
- clickable,
452
- preventInteraction,
453
- required,
454
- completed,
455
- resolvedZoneStyle,
456
- renderCustomCard,
457
- ]);
458
-
459
- return (
460
- <AnimatedView
461
- ref={viewRef}
462
- onLayout={onLayout}
463
- style={style}
464
- collapsable={false}
465
- >
466
- {children}
467
- </AnimatedView>
468
- );
469
- };
@@ -1,71 +0,0 @@
1
- import type { WithSpringConfig } from 'react-native-reanimated';
2
-
3
- /**
4
- * Default spring configuration matching Reanimated 3 defaults.
5
- */
6
- export const Reanimated3DefaultSpringConfig: WithSpringConfig = {
7
- damping: 10,
8
- mass: 1,
9
- stiffness: 100,
10
- };
11
-
12
- /**
13
- * Spring configuration with duration.
14
- */
15
- export const Reanimated3DefaultSpringConfigWithDuration: WithSpringConfig = {
16
- duration: 1333,
17
- dampingRatio: 0.5,
18
- };
19
-
20
- /**
21
- * A bouncy and energetic spring configuration.
22
- */
23
- export const WigglySpringConfig: WithSpringConfig = {
24
- damping: 90,
25
- mass: 4,
26
- stiffness: 900,
27
- };
28
-
29
- /**
30
- * A bouncy spring configuration with fixed duration.
31
- */
32
- export const WigglySpringConfigWithDuration: WithSpringConfig = {
33
- duration: 550,
34
- dampingRatio: 0.75,
35
- };
36
-
37
- /**
38
- * A gentle and smooth spring configuration.
39
- */
40
- export const GentleSpringConfig: WithSpringConfig = {
41
- damping: 120,
42
- mass: 4,
43
- stiffness: 900,
44
- };
45
-
46
- /**
47
- * A gentle spring configuration with fixed duration.
48
- */
49
- export const GentleSpringConfigWithDuration: WithSpringConfig = {
50
- duration: 550,
51
- dampingRatio: 1,
52
- };
53
-
54
- /**
55
- * A snappy and responsive spring configuration.
56
- */
57
- export const SnappySpringConfig: WithSpringConfig = {
58
- damping: 110,
59
- mass: 4,
60
- stiffness: 900,
61
- overshootClamping: true,
62
- };
63
-
64
- /**
65
- * A snappy spring configuration with fixed duration.
66
- */
67
- export const SnappySpringConfigWithDuration: WithSpringConfig = {
68
- duration: 550,
69
- dampingRatio: 0.92,
70
- overshootClamping: true,
71
- };
@@ -1,66 +0,0 @@
1
- import type { WithSpringConfig } from 'react-native-reanimated';
2
- import type { ZoneStyle } from '../types';
3
-
4
- export const DEFAULT_SPRING_CONFIG: WithSpringConfig = {
5
- damping: 20,
6
- stiffness: 90,
7
- };
8
-
9
- export const DEFAULT_BACKDROP_OPACITY = 0.5;
10
-
11
- export const DEFAULT_LABELS = {
12
- next: 'Next',
13
- previous: 'Previous',
14
- finish: 'Finish',
15
- skip: 'Skip',
16
- };
17
-
18
- /**
19
- * Default zone style configuration.
20
- * These values are used when no custom style is provided.
21
- */
22
- export const DEFAULT_ZONE_STYLE: Required<ZoneStyle> = {
23
- padding: 0,
24
- paddingTop: 0,
25
- paddingRight: 0,
26
- paddingBottom: 0,
27
- paddingLeft: 0,
28
- borderRadius: 10,
29
- shape: 'rounded-rect',
30
- borderWidth: 0,
31
- borderColor: 'transparent',
32
- glowColor: '#FFFFFF',
33
- glowRadius: 10,
34
- glowSpread: 5,
35
- glowOffsetX: 0,
36
- glowOffsetY: 0,
37
- springDamping: 20,
38
- springStiffness: 90,
39
- };
40
-
41
- /**
42
- * Merges global and per-step zone styles with defaults.
43
- */
44
- export function resolveZoneStyle(
45
- globalStyle?: ZoneStyle,
46
- stepStyle?: ZoneStyle
47
- ): Required<ZoneStyle> {
48
- const merged = {
49
- ...DEFAULT_ZONE_STYLE,
50
- ...globalStyle,
51
- ...stepStyle,
52
- };
53
-
54
- // Handle individual padding overrides
55
- return {
56
- ...merged,
57
- paddingTop:
58
- stepStyle?.paddingTop ?? globalStyle?.paddingTop ?? merged.padding,
59
- paddingRight:
60
- stepStyle?.paddingRight ?? globalStyle?.paddingRight ?? merged.padding,
61
- paddingBottom:
62
- stepStyle?.paddingBottom ?? globalStyle?.paddingBottom ?? merged.padding,
63
- paddingLeft:
64
- stepStyle?.paddingLeft ?? globalStyle?.paddingLeft ?? merged.padding,
65
- };
66
- }
@@ -1,4 +0,0 @@
1
- import { createContext } from 'react';
2
- import type { InternalTourContextType } from '../types';
3
-
4
- export const TourContext = createContext<InternalTourContextType | null>(null);
@@ -1,10 +0,0 @@
1
- import { useContext } from 'react';
2
- import { TourContext } from '../context/TourContext';
3
-
4
- export const useTour = () => {
5
- const context = useContext(TourContext);
6
- if (!context) {
7
- throw new Error('useTour must be used within a TourProvider');
8
- }
9
- return context;
10
- };