rn-smart-tour 1.0.4 → 1.0.5
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.
- package/README.md +8 -0
- package/dist/DapOverlay.js +3 -3
- package/dist/DapProvider.js +20 -5
- package/dist/DapTarget.d.ts +6 -0
- package/dist/DapTarget.js +36 -2
- package/package.json +1 -9
package/README.md
CHANGED
|
@@ -112,6 +112,14 @@ When `autoStart: true` is enabled, the overlay waits **300ms** after registratio
|
|
|
112
112
|
| `autoStart` | `boolean` | Trigger as soon as the first target mounts. |
|
|
113
113
|
| `steps` | `TourStep[]` | Sequence of highlight steps. |
|
|
114
114
|
|
|
115
|
+
### DapTarget
|
|
116
|
+
| Property | Type | Description |
|
|
117
|
+
|:---|:---|:---|
|
|
118
|
+
| `name` | `string` | Unique identifier that matches a `targetId` in a tour step. |
|
|
119
|
+
| `children` | `ReactElement` | The UI element to wrap and highlight. |
|
|
120
|
+
| `asChild` | `boolean` | **New!** If true, clones the child to avoid an extra View wrapper. (Crucial for flex/percentage layouts). |
|
|
121
|
+
| `...props` | `ViewProps` | All standard React Native `View` props are forwarded. |
|
|
122
|
+
|
|
115
123
|
### TourStep
|
|
116
124
|
| Property | Type | Default | Description |
|
|
117
125
|
|:---|:---|:---|:---|
|
package/dist/DapOverlay.js
CHANGED
|
@@ -126,9 +126,9 @@ const DapOverlay = () => {
|
|
|
126
126
|
</react_native_1.Text>)}
|
|
127
127
|
|
|
128
128
|
<react_native_1.View style={styles.actions}>
|
|
129
|
-
<react_native_1.TouchableOpacity onPress={() => stopTour()} style={styles.actionBtn}>
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
{!isLastStep && (<react_native_1.TouchableOpacity onPress={() => stopTour()} style={styles.actionBtn}>
|
|
130
|
+
<react_native_1.Text style={styles.actionText}>Skip</react_native_1.Text>
|
|
131
|
+
</react_native_1.TouchableOpacity>)}
|
|
132
132
|
|
|
133
133
|
{!isFirstStep && (<react_native_1.TouchableOpacity onPress={prevStep} style={styles.actionBtn}>
|
|
134
134
|
<react_native_1.Text style={styles.actionText}>Back</react_native_1.Text>
|
package/dist/DapProvider.js
CHANGED
|
@@ -50,9 +50,15 @@ const DapProvider = ({ children, tours, storageAdapter }) => {
|
|
|
50
50
|
const [currentStepIndex, setCurrentStepIndex] = (0, react_1.useState)(0);
|
|
51
51
|
const [seenTours, setSeenTours] = (0, react_1.useState)({});
|
|
52
52
|
const [isStorageLoaded, setIsStorageLoaded] = (0, react_1.useState)(!storageAdapter);
|
|
53
|
-
// Ref to
|
|
53
|
+
// Ref to always hold the latest activeTourId, avoiding stale closures.
|
|
54
54
|
const activeTourIdRef = (0, react_1.useRef)(activeTourId);
|
|
55
55
|
activeTourIdRef.current = activeTourId;
|
|
56
|
+
/**
|
|
57
|
+
* Ref to track the ID of a tour that was just manually stopped/skipped.
|
|
58
|
+
* This prevents the auto-start engine from immediately restarting the same tour
|
|
59
|
+
* before the 'seenTours' state update has been processed.
|
|
60
|
+
*/
|
|
61
|
+
const tourIdJustStoppedRef = (0, react_1.useRef)(null);
|
|
56
62
|
// Load seen tours on mount if a storage adapter is provided
|
|
57
63
|
(0, react_1.useEffect)(() => {
|
|
58
64
|
const loadStorage = async () => {
|
|
@@ -102,11 +108,14 @@ const DapProvider = ({ children, tours, storageAdapter }) => {
|
|
|
102
108
|
console.warn(`[rn-dap] Tour with id ${tourId} not found.`);
|
|
103
109
|
}
|
|
104
110
|
}, [tours, seenTours]);
|
|
105
|
-
// Uses activeTourIdRef to avoid stale closures and prevent cascading re-renders.
|
|
106
111
|
const stopTour = (0, react_1.useCallback)((markAsSeen = true) => {
|
|
107
112
|
const currentTourId = activeTourIdRef.current;
|
|
108
|
-
if (currentTourId
|
|
109
|
-
|
|
113
|
+
if (currentTourId) {
|
|
114
|
+
if (markAsSeen) {
|
|
115
|
+
saveSeenTour(currentTourId);
|
|
116
|
+
}
|
|
117
|
+
// Block this specific tour from auto-restarting until coordinates or seenTours settle.
|
|
118
|
+
tourIdJustStoppedRef.current = currentTourId;
|
|
110
119
|
}
|
|
111
120
|
setActiveTourId(null);
|
|
112
121
|
setCurrentStepIndex(0);
|
|
@@ -142,7 +151,9 @@ const DapProvider = ({ children, tours, storageAdapter }) => {
|
|
|
142
151
|
autoStartTimerRef.current = setTimeout(() => {
|
|
143
152
|
for (const tourId of Object.keys(tours)) {
|
|
144
153
|
const tour = tours[tourId];
|
|
145
|
-
if
|
|
154
|
+
// Skip if this tour was just manually stopped and haven't confirmed 'seen' yet.
|
|
155
|
+
const wasJustStopped = tourIdJustStoppedRef.current === tourId;
|
|
156
|
+
if (tour.autoStart && !seenTours[tourId] && !wasJustStopped && tour.steps.length > 0) {
|
|
146
157
|
const firstTargetId = tour.steps[0].targetId;
|
|
147
158
|
// If the first target of an unread, auto-starting tour is mounted
|
|
148
159
|
if (targets[firstTargetId]) {
|
|
@@ -151,6 +162,10 @@ const DapProvider = ({ children, tours, storageAdapter }) => {
|
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
}
|
|
165
|
+
// Cleanup the "just stopped" ref once the seenTours state finally reflects the closure.
|
|
166
|
+
if (tourIdJustStoppedRef.current && seenTours[tourIdJustStoppedRef.current]) {
|
|
167
|
+
tourIdJustStoppedRef.current = null;
|
|
168
|
+
}
|
|
154
169
|
}, AUTO_START_DEBOUNCE_MS);
|
|
155
170
|
return () => {
|
|
156
171
|
if (autoStartTimerRef.current) {
|
package/dist/DapTarget.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ import { ViewProps } from 'react-native';
|
|
|
3
3
|
interface DapTargetProps extends ViewProps {
|
|
4
4
|
name: string;
|
|
5
5
|
children: ReactElement;
|
|
6
|
+
/**
|
|
7
|
+
* If true, DapTarget will not wrap your child in a View. Instead, it will
|
|
8
|
+
* clone the child and inject the ref/onLayout logic directly.
|
|
9
|
+
* Useful for maintaining flex layouts or percentage widths (e.g. 33%).
|
|
10
|
+
*/
|
|
11
|
+
asChild?: boolean;
|
|
6
12
|
}
|
|
7
13
|
export declare const DapTarget: React.FC<DapTargetProps>;
|
|
8
14
|
export {};
|
package/dist/DapTarget.js
CHANGED
|
@@ -45,7 +45,22 @@ const DapContext_1 = require("./DapContext");
|
|
|
45
45
|
const MEASUREMENT_DELAYS = [100, 500, 1000];
|
|
46
46
|
/** Threshold in points — ignore sub-pixel drift to avoid unnecessary re-registers. */
|
|
47
47
|
const POSITION_THRESHOLD = 1;
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Utility to merge multiple refs into a single callback ref.
|
|
50
|
+
*/
|
|
51
|
+
function setRefs(...refs) {
|
|
52
|
+
return (value) => {
|
|
53
|
+
refs.forEach((ref) => {
|
|
54
|
+
if (typeof ref === 'function') {
|
|
55
|
+
ref(value);
|
|
56
|
+
}
|
|
57
|
+
else if (ref && typeof ref === 'object') {
|
|
58
|
+
ref.current = value;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const DapTarget = ({ name, children, asChild, ...props }) => {
|
|
49
64
|
const viewRef = (0, react_1.useRef)(null);
|
|
50
65
|
const context = (0, react_1.useContext)(DapContext_1.DapContext);
|
|
51
66
|
/** Holds all scheduled timer IDs so we can cancel them on re-layout or unmount. */
|
|
@@ -116,7 +131,26 @@ const DapTarget = ({ name, children, ...props }) => {
|
|
|
116
131
|
unregisterTarget?.(name);
|
|
117
132
|
};
|
|
118
133
|
}, [name, unregisterTarget, clearAllTimers]);
|
|
119
|
-
//
|
|
134
|
+
// If asChild is enabled, clone the child and inject measurement logic.
|
|
135
|
+
if (asChild && (0, react_1.isValidElement)(children)) {
|
|
136
|
+
try {
|
|
137
|
+
const child = react_1.Children.only(children);
|
|
138
|
+
return (0, react_1.cloneElement)(child, {
|
|
139
|
+
...props,
|
|
140
|
+
ref: setRefs(viewRef, child.ref),
|
|
141
|
+
onLayout: (e) => {
|
|
142
|
+
handleLayout(e);
|
|
143
|
+
child.props.onLayout?.(e);
|
|
144
|
+
},
|
|
145
|
+
// collapsable={false} is vital for Android measurement
|
|
146
|
+
collapsable: false,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
console.warn('[rn-dap] asChild requires a single React element as a child.');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Fallback to standard View wrapper
|
|
120
154
|
return (<react_native_1.View ref={viewRef} onLayout={handleLayout} collapsable={false} {...props}>
|
|
121
155
|
{children}
|
|
122
156
|
</react_native_1.View>);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-smart-tour",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Enterprise-grade Digital Adoption Platform (DAP) package for React Native. Provides guided walkthroughs, tooltips, and app tours.",
|
|
5
5
|
"author": "Vishwas Gaur",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,14 +9,6 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "https://github.com/Vishwasgaur0819/rn-smart-tour"
|
|
15
|
-
},
|
|
16
|
-
"homepage": "https://github.com/Vishwasgaur0819/rn-smart-tour#readme",
|
|
17
|
-
"bugs": {
|
|
18
|
-
"url": "https://github.com/Vishwasgaur0819/rn-smart-tour/issues"
|
|
19
|
-
},
|
|
20
12
|
"keywords": [
|
|
21
13
|
"react-native",
|
|
22
14
|
"tour",
|