@korsolutions/guidon 1.0.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.
- package/README.md +334 -0
- package/dist/index-D_JFvCIg.d.mts +314 -0
- package/dist/index-D_JFvCIg.d.ts +314 -0
- package/dist/index.d.mts +128 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +1098 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1073 -0
- package/dist/index.mjs.map +1 -0
- package/dist/persistence/index.d.mts +2 -0
- package/dist/persistence/index.d.ts +2 -0
- package/dist/persistence/index.js +300 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/index.mjs +291 -0
- package/dist/persistence/index.mjs.map +1 -0
- package/package.json +76 -0
- package/src/components/GuidonOverlay.tsx +159 -0
- package/src/components/GuidonProvider.tsx +158 -0
- package/src/components/GuidonTarget.tsx +108 -0
- package/src/components/GuidonTooltip.tsx +365 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +51 -0
- package/src/persistence/adapters.ts +224 -0
- package/src/persistence/hooks.ts +179 -0
- package/src/persistence/index.ts +2 -0
- package/src/store.ts +268 -0
- package/src/types.ts +242 -0
package/README.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# guidon
|
|
2
|
+
|
|
3
|
+
A cross-platform walkthrough/onboarding component library for React Native with web support. Features spotlight effects, customizable tooltips, and flexible persistence options.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn add @guidon
|
|
9
|
+
# or
|
|
10
|
+
npm install @guidon
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Peer Dependencies
|
|
14
|
+
|
|
15
|
+
Make sure you have these dependencies installed:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
yarn add react react-native react-native-reanimated react-native-safe-area-context react-native-svg zustand
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Define Your Guidon Steps
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import type { GuidonConfig } from '@guidon';
|
|
27
|
+
|
|
28
|
+
const exploreGuidonConfig: GuidonConfig = {
|
|
29
|
+
id: 'explore-guidon',
|
|
30
|
+
steps: [
|
|
31
|
+
{
|
|
32
|
+
id: 'welcome',
|
|
33
|
+
targetId: 'search-button',
|
|
34
|
+
title: 'Welcome!',
|
|
35
|
+
description: 'Let us show you around the app.',
|
|
36
|
+
tooltipPosition: 'bottom',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'filters',
|
|
40
|
+
targetId: 'filter-button',
|
|
41
|
+
title: 'Filters',
|
|
42
|
+
description: 'Use filters to find exactly what you need.',
|
|
43
|
+
tooltipPosition: 'bottom',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'card-actions',
|
|
47
|
+
targetId: 'card-dismiss',
|
|
48
|
+
title: 'Card Actions',
|
|
49
|
+
description: 'Swipe left to dismiss or tap the X button.',
|
|
50
|
+
tooltipPosition: 'top',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
theme: {
|
|
54
|
+
primaryColor: '#007AFF',
|
|
55
|
+
backdropOpacity: 0.75,
|
|
56
|
+
},
|
|
57
|
+
onComplete: () => {
|
|
58
|
+
console.log('Guidon completed!');
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Wrap Your Screen with GuidonProvider
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { GuidonProvider } from 'guidon';
|
|
67
|
+
|
|
68
|
+
function ExploreScreen() {
|
|
69
|
+
return (
|
|
70
|
+
<GuidonProvider
|
|
71
|
+
config={exploreGuidonConfig}
|
|
72
|
+
autoStart={true}
|
|
73
|
+
>
|
|
74
|
+
<YourScreenContent />
|
|
75
|
+
</GuidonProvider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Mark Target Elements with GuidonTarget
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { GuidonTarget } from 'guidon';
|
|
84
|
+
|
|
85
|
+
function SearchButton() {
|
|
86
|
+
return (
|
|
87
|
+
<GuidonTarget targetId="search-button">
|
|
88
|
+
<TouchableOpacity onPress={handleSearch}>
|
|
89
|
+
<SearchIcon />
|
|
90
|
+
</TouchableOpacity>
|
|
91
|
+
</GuidonTarget>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Persistence
|
|
97
|
+
|
|
98
|
+
The library supports flexible persistence through adapters. You can save guidon progress to local storage, AsyncStorage, or your backend API.
|
|
99
|
+
|
|
100
|
+
### Using Built-in Adapters
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import {
|
|
104
|
+
GuidonProvider,
|
|
105
|
+
createLocalStorageAdapter,
|
|
106
|
+
createAsyncStorageAdapter,
|
|
107
|
+
} from 'guidon';
|
|
108
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
109
|
+
|
|
110
|
+
// For web (localStorage)
|
|
111
|
+
const webAdapter = createLocalStorageAdapter();
|
|
112
|
+
|
|
113
|
+
// For React Native (AsyncStorage)
|
|
114
|
+
const nativeAdapter = createAsyncStorageAdapter(AsyncStorage);
|
|
115
|
+
|
|
116
|
+
function App() {
|
|
117
|
+
return (
|
|
118
|
+
<GuidonProvider
|
|
119
|
+
config={config}
|
|
120
|
+
persistenceAdapter={nativeAdapter}
|
|
121
|
+
>
|
|
122
|
+
<YourApp />
|
|
123
|
+
</GuidonProvider>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Creating a Custom API Adapter
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { createApiAdapter } from 'guidon';
|
|
132
|
+
|
|
133
|
+
const apiAdapter = createApiAdapter({
|
|
134
|
+
loadProgress: async (guidonId) => {
|
|
135
|
+
const response = await fetch(`/api/guidon/${guidonId}`);
|
|
136
|
+
if (!response.ok) return null;
|
|
137
|
+
return response.json();
|
|
138
|
+
},
|
|
139
|
+
saveProgress: async (progress) => {
|
|
140
|
+
await fetch(`/api/guidon/${progress.guidonId}`, {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
body: JSON.stringify(progress),
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
clearProgress: async (guidonId) => {
|
|
147
|
+
await fetch(`/api/guidon/${guidonId}`, {
|
|
148
|
+
method: 'DELETE',
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Combining Multiple Adapters
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { createCompositeAdapter, createLocalStorageAdapter } from 'guidon';
|
|
158
|
+
|
|
159
|
+
// Save to both local storage and API
|
|
160
|
+
const compositeAdapter = createCompositeAdapter([
|
|
161
|
+
createLocalStorageAdapter(),
|
|
162
|
+
apiAdapter,
|
|
163
|
+
]);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## API Reference
|
|
167
|
+
|
|
168
|
+
### GuidonProvider Props
|
|
169
|
+
|
|
170
|
+
| Prop | Type | Default | Description |
|
|
171
|
+
|------|------|---------|-------------|
|
|
172
|
+
| `config` | `GuidonConfig` | required | Configuration for the guidon |
|
|
173
|
+
| `autoStart` | `boolean` | `true` | Whether to auto-start when mounted |
|
|
174
|
+
| `shouldStart` | `() => boolean \| Promise<boolean>` | - | Custom condition for starting |
|
|
175
|
+
| `persistenceAdapter` | `GuidonPersistenceAdapter` | - | Adapter for saving progress |
|
|
176
|
+
| `portalComponent` | `React.ComponentType` | - | Custom portal for overlay rendering |
|
|
177
|
+
| `renderTooltip` | `(props) => ReactNode` | - | Custom tooltip renderer |
|
|
178
|
+
| `tooltipLabels` | `object` | - | Customize button labels |
|
|
179
|
+
| `onBackdropPress` | `() => void` | - | Called when backdrop is pressed |
|
|
180
|
+
|
|
181
|
+
### GuidonConfig
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
interface GuidonConfig {
|
|
185
|
+
id: string; // Unique identifier
|
|
186
|
+
steps: GuidonStep[]; // Array of steps
|
|
187
|
+
theme?: GuidonTheme; // Theme customization
|
|
188
|
+
animationDuration?: number; // Animation duration (ms)
|
|
189
|
+
onComplete?: () => void; // Called on completion
|
|
190
|
+
onSkip?: () => void; // Called when skipped
|
|
191
|
+
onStepChange?: (index, step) => void; // Called on step change
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### GuidonStep
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
interface GuidonStep {
|
|
199
|
+
id: string; // Unique step identifier
|
|
200
|
+
targetId: string; // ID of the target element
|
|
201
|
+
title: string; // Tooltip title
|
|
202
|
+
description: string; // Tooltip description
|
|
203
|
+
tooltipPosition?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|
204
|
+
customContent?: ReactNode; // Additional content in tooltip
|
|
205
|
+
onStepEnter?: () => void; // Called when step becomes active
|
|
206
|
+
onStepExit?: () => void; // Called when leaving step
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### GuidonTheme
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
interface GuidonTheme {
|
|
214
|
+
backdropColor?: string; // Overlay color (default: '#000000')
|
|
215
|
+
backdropOpacity?: number; // Overlay opacity (default: 0.75)
|
|
216
|
+
tooltipBackgroundColor?: string;
|
|
217
|
+
tooltipBorderColor?: string;
|
|
218
|
+
tooltipBorderRadius?: number;
|
|
219
|
+
titleColor?: string;
|
|
220
|
+
descriptionColor?: string;
|
|
221
|
+
primaryColor?: string; // Button color
|
|
222
|
+
mutedColor?: string; // Secondary text color
|
|
223
|
+
spotlightBorderRadius?: number; // Spotlight cutout radius
|
|
224
|
+
spotlightPadding?: number; // Padding around spotlight
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Controlling the Guidon
|
|
229
|
+
|
|
230
|
+
### Using the Context Hook
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
import { useGuidonContext } from 'guidon';
|
|
234
|
+
|
|
235
|
+
function ReplayButton() {
|
|
236
|
+
const { replay, isCompleted } = useGuidonContext();
|
|
237
|
+
|
|
238
|
+
if (!isCompleted) return null;
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<Button onPress={replay}>
|
|
242
|
+
Replay Tutorial
|
|
243
|
+
</Button>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Using the Guidon API (Outside React)
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { Guidon } from 'guidon';
|
|
252
|
+
|
|
253
|
+
// Start programmatically
|
|
254
|
+
Guidon.start();
|
|
255
|
+
|
|
256
|
+
// Skip to a specific step
|
|
257
|
+
Guidon.goToStep(2);
|
|
258
|
+
|
|
259
|
+
// Complete the guidon
|
|
260
|
+
Guidon.complete();
|
|
261
|
+
|
|
262
|
+
// Reset and replay
|
|
263
|
+
Guidon.reset();
|
|
264
|
+
Guidon.start();
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Using Hook Selectors
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import {
|
|
271
|
+
useGuidonActive,
|
|
272
|
+
useGuidonStep,
|
|
273
|
+
useGuidonProgress,
|
|
274
|
+
} from 'guidon';
|
|
275
|
+
|
|
276
|
+
function GuidonStatus() {
|
|
277
|
+
const isActive = useGuidonActive();
|
|
278
|
+
const currentStep = useGuidonStep();
|
|
279
|
+
const { currentStep: stepNum, totalSteps, percentage } = useGuidonProgress();
|
|
280
|
+
|
|
281
|
+
if (!isActive) return null;
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<Text>
|
|
285
|
+
Step {stepNum} of {totalSteps} ({percentage}%)
|
|
286
|
+
</Text>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Custom Tooltip Rendering
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
<GuidonProvider
|
|
295
|
+
config={config}
|
|
296
|
+
renderTooltip={({ step, currentIndex, totalSteps, onNext, onPrevious, onSkip }) => (
|
|
297
|
+
<View style={styles.customTooltip}>
|
|
298
|
+
<Text style={styles.title}>{step.title}</Text>
|
|
299
|
+
<Text>{step.description}</Text>
|
|
300
|
+
<View style={styles.buttons}>
|
|
301
|
+
<Button onPress={onSkip}>Skip</Button>
|
|
302
|
+
{currentIndex > 0 && <Button onPress={onPrevious}>Back</Button>}
|
|
303
|
+
<Button onPress={onNext}>
|
|
304
|
+
{currentIndex === totalSteps - 1 ? 'Done' : 'Next'}
|
|
305
|
+
</Button>
|
|
306
|
+
</View>
|
|
307
|
+
</View>
|
|
308
|
+
)}
|
|
309
|
+
>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Conditional Starting
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
<GuidonProvider
|
|
316
|
+
config={config}
|
|
317
|
+
autoStart={true}
|
|
318
|
+
shouldStart={async () => {
|
|
319
|
+
// Check if user is new
|
|
320
|
+
const user = await getUser();
|
|
321
|
+
return user.isFirstLogin;
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Platform Support
|
|
327
|
+
|
|
328
|
+
- iOS
|
|
329
|
+
- Android
|
|
330
|
+
- Web (React Native Web)
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Position of tooltip relative to the highlighted element
|
|
5
|
+
*/
|
|
6
|
+
type TooltipPosition = 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|
7
|
+
/**
|
|
8
|
+
* Defines the measurements of a target element
|
|
9
|
+
*/
|
|
10
|
+
interface TargetMeasurements {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A single step in the guidon
|
|
18
|
+
*/
|
|
19
|
+
interface GuidonStep {
|
|
20
|
+
/** Unique identifier for this step */
|
|
21
|
+
id: string;
|
|
22
|
+
/** ID of the target element to highlight */
|
|
23
|
+
targetId: string;
|
|
24
|
+
/** Title displayed in the tooltip */
|
|
25
|
+
title: string;
|
|
26
|
+
/** Description/content displayed in the tooltip */
|
|
27
|
+
description: string;
|
|
28
|
+
/** Preferred position of the tooltip */
|
|
29
|
+
tooltipPosition?: TooltipPosition;
|
|
30
|
+
/** Custom content to render in the tooltip */
|
|
31
|
+
customContent?: ReactNode;
|
|
32
|
+
/** Called when this step becomes active */
|
|
33
|
+
onStepEnter?: () => void;
|
|
34
|
+
/** Called when leaving this step */
|
|
35
|
+
onStepExit?: () => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Theme configuration for guidon styling
|
|
39
|
+
*/
|
|
40
|
+
interface GuidonTheme {
|
|
41
|
+
/** Color of the backdrop overlay */
|
|
42
|
+
backdropColor?: string;
|
|
43
|
+
/** Opacity of the backdrop (0-1) */
|
|
44
|
+
backdropOpacity?: number;
|
|
45
|
+
/** Background color of the tooltip */
|
|
46
|
+
tooltipBackgroundColor?: string;
|
|
47
|
+
/** Border color of the tooltip */
|
|
48
|
+
tooltipBorderColor?: string;
|
|
49
|
+
/** Border radius of the tooltip */
|
|
50
|
+
tooltipBorderRadius?: number;
|
|
51
|
+
/** Color of the title text */
|
|
52
|
+
titleColor?: string;
|
|
53
|
+
/** Color of the description text */
|
|
54
|
+
descriptionColor?: string;
|
|
55
|
+
/** Primary/accent color (buttons, progress) */
|
|
56
|
+
primaryColor?: string;
|
|
57
|
+
/** Muted/secondary color */
|
|
58
|
+
mutedColor?: string;
|
|
59
|
+
/** Border radius of the spotlight cutout */
|
|
60
|
+
spotlightBorderRadius?: number;
|
|
61
|
+
/** Padding around the highlighted element */
|
|
62
|
+
spotlightPadding?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* State of a specific guidon tour
|
|
66
|
+
*/
|
|
67
|
+
interface GuidonProgress {
|
|
68
|
+
/** Unique identifier for the guidon */
|
|
69
|
+
guidonId: string;
|
|
70
|
+
/** Whether this guidon has been completed */
|
|
71
|
+
completed: boolean;
|
|
72
|
+
/** Index of the last viewed step (for resuming) */
|
|
73
|
+
lastStepIndex?: number;
|
|
74
|
+
/** Timestamp of completion */
|
|
75
|
+
completedAt?: string;
|
|
76
|
+
/** Number of times this guidon has been completed */
|
|
77
|
+
completionCount?: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Persistence adapter interface for saving/loading guidon progress
|
|
81
|
+
* Implement this interface to connect the guidon to your backend
|
|
82
|
+
*/
|
|
83
|
+
interface GuidonPersistenceAdapter {
|
|
84
|
+
/**
|
|
85
|
+
* Load the progress for a specific guidon
|
|
86
|
+
* @param guidonId - Unique identifier for the guidon
|
|
87
|
+
* @returns The progress data or null if not found
|
|
88
|
+
*/
|
|
89
|
+
loadProgress: (guidonId: string) => Promise<GuidonProgress | null>;
|
|
90
|
+
/**
|
|
91
|
+
* Save the progress for a specific guidon
|
|
92
|
+
* @param progress - The progress data to save
|
|
93
|
+
*/
|
|
94
|
+
saveProgress: (progress: GuidonProgress) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Load all guidon progress (optional, for bulk operations)
|
|
97
|
+
* @returns Map of guidon IDs to progress data
|
|
98
|
+
*/
|
|
99
|
+
loadAllProgress?: () => Promise<Record<string, GuidonProgress>>;
|
|
100
|
+
/**
|
|
101
|
+
* Clear progress for a specific guidon (for replay functionality)
|
|
102
|
+
* @param guidonId - Unique identifier for the guidon
|
|
103
|
+
*/
|
|
104
|
+
clearProgress?: (guidonId: string) => Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Configuration for a guidon
|
|
108
|
+
*/
|
|
109
|
+
interface GuidonConfig {
|
|
110
|
+
/** Unique identifier for this guidon */
|
|
111
|
+
id: string;
|
|
112
|
+
/** Steps in the guidon */
|
|
113
|
+
steps: GuidonStep[];
|
|
114
|
+
/** Theme customization */
|
|
115
|
+
theme?: GuidonTheme;
|
|
116
|
+
/** Animation duration in milliseconds */
|
|
117
|
+
animationDuration?: number;
|
|
118
|
+
/** Called when the guidon is completed */
|
|
119
|
+
onComplete?: () => void;
|
|
120
|
+
/** Called when the guidon is skipped */
|
|
121
|
+
onSkip?: () => void;
|
|
122
|
+
/** Called when the step changes */
|
|
123
|
+
onStepChange?: (stepIndex: number, step: GuidonStep) => void;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Labels for tooltip buttons
|
|
127
|
+
*/
|
|
128
|
+
interface GuidonTooltipLabels {
|
|
129
|
+
next?: string;
|
|
130
|
+
previous?: string;
|
|
131
|
+
skip?: string;
|
|
132
|
+
finish?: string;
|
|
133
|
+
stepOf?: (current: number, total: number) => string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Props for custom tooltip renderer
|
|
137
|
+
*/
|
|
138
|
+
interface GuidonTooltipRenderProps {
|
|
139
|
+
step: GuidonStep;
|
|
140
|
+
currentIndex: number;
|
|
141
|
+
totalSteps: number;
|
|
142
|
+
onNext: () => void;
|
|
143
|
+
onPrevious: () => void;
|
|
144
|
+
onSkip: () => void;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Props for the GuidonProvider component
|
|
148
|
+
*/
|
|
149
|
+
interface GuidonProviderProps {
|
|
150
|
+
children: ReactNode;
|
|
151
|
+
/** Configuration for the guidon */
|
|
152
|
+
config: GuidonConfig;
|
|
153
|
+
/** Whether to auto-start when the component mounts */
|
|
154
|
+
autoStart?: boolean;
|
|
155
|
+
/** Condition function to determine if guidon should start */
|
|
156
|
+
shouldStart?: () => boolean | Promise<boolean>;
|
|
157
|
+
/** Persistence adapter for saving/loading progress */
|
|
158
|
+
persistenceAdapter?: GuidonPersistenceAdapter;
|
|
159
|
+
/** Custom portal component for rendering overlay */
|
|
160
|
+
portalComponent?: React.ComponentType<{
|
|
161
|
+
children: ReactNode;
|
|
162
|
+
}>;
|
|
163
|
+
/** Custom tooltip renderer */
|
|
164
|
+
renderTooltip?: (props: GuidonTooltipRenderProps) => ReactNode;
|
|
165
|
+
/** Customize tooltip labels */
|
|
166
|
+
tooltipLabels?: GuidonTooltipLabels;
|
|
167
|
+
/** Called when backdrop is pressed */
|
|
168
|
+
onBackdropPress?: () => void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Props for the GuidonTarget component
|
|
172
|
+
*/
|
|
173
|
+
interface GuidonTargetProps {
|
|
174
|
+
children: ReactNode;
|
|
175
|
+
/** Target ID that matches a step's targetId */
|
|
176
|
+
targetId: string;
|
|
177
|
+
/** Whether this target is currently active (for conditional rendering) */
|
|
178
|
+
active?: boolean;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Internal store state
|
|
182
|
+
*/
|
|
183
|
+
interface GuidonState {
|
|
184
|
+
/** Currently active guidon config */
|
|
185
|
+
config: GuidonConfig | null;
|
|
186
|
+
/** Whether the guidon is currently active */
|
|
187
|
+
isActive: boolean;
|
|
188
|
+
/** Current step index */
|
|
189
|
+
currentStepIndex: number;
|
|
190
|
+
/** Whether the guidon has been completed */
|
|
191
|
+
isCompleted: boolean;
|
|
192
|
+
/** Map of target IDs to their measurements */
|
|
193
|
+
targetMeasurements: Record<string, TargetMeasurements>;
|
|
194
|
+
/** Loading state for persistence operations */
|
|
195
|
+
isLoading: boolean;
|
|
196
|
+
/** Error from persistence operations */
|
|
197
|
+
error: string | null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Internal store actions
|
|
201
|
+
*/
|
|
202
|
+
interface GuidonActions {
|
|
203
|
+
/** Configure the guidon */
|
|
204
|
+
configure: (config: GuidonConfig) => void;
|
|
205
|
+
/** Start the guidon */
|
|
206
|
+
start: () => void;
|
|
207
|
+
/** Go to the next step */
|
|
208
|
+
next: () => void;
|
|
209
|
+
/** Go to the previous step */
|
|
210
|
+
previous: () => void;
|
|
211
|
+
/** Go to a specific step */
|
|
212
|
+
goToStep: (index: number) => void;
|
|
213
|
+
/** Skip the guidon */
|
|
214
|
+
skip: () => void;
|
|
215
|
+
/** Complete the guidon */
|
|
216
|
+
complete: () => void;
|
|
217
|
+
/** Reset the guidon state */
|
|
218
|
+
reset: () => void;
|
|
219
|
+
/** Register a target's measurements */
|
|
220
|
+
registerTarget: (targetId: string, measurements: TargetMeasurements) => void;
|
|
221
|
+
/** Unregister a target */
|
|
222
|
+
unregisterTarget: (targetId: string) => void;
|
|
223
|
+
/** Set loading state */
|
|
224
|
+
setLoading: (isLoading: boolean) => void;
|
|
225
|
+
/** Set error state */
|
|
226
|
+
setError: (error: string | null) => void;
|
|
227
|
+
}
|
|
228
|
+
type GuidonStore = GuidonState & GuidonActions;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* No-op adapter that doesn't persist anything
|
|
232
|
+
* Useful for testing or when persistence is not needed
|
|
233
|
+
*/
|
|
234
|
+
declare const createNoopAdapter: () => GuidonPersistenceAdapter;
|
|
235
|
+
/**
|
|
236
|
+
* Memory adapter that stores progress in memory
|
|
237
|
+
* Data is lost when the app is closed
|
|
238
|
+
*/
|
|
239
|
+
declare const createMemoryAdapter: () => GuidonPersistenceAdapter;
|
|
240
|
+
/**
|
|
241
|
+
* localStorage adapter for web
|
|
242
|
+
* Only works in browser environments
|
|
243
|
+
*/
|
|
244
|
+
declare const createLocalStorageAdapter: (keyPrefix?: string) => GuidonPersistenceAdapter;
|
|
245
|
+
/**
|
|
246
|
+
* AsyncStorage adapter for React Native
|
|
247
|
+
* Requires @react-native-async-storage/async-storage to be installed
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
251
|
+
* const adapter = createAsyncStorageAdapter(AsyncStorage);
|
|
252
|
+
*/
|
|
253
|
+
declare const createAsyncStorageAdapter: (asyncStorage: {
|
|
254
|
+
getItem: (key: string) => Promise<string | null>;
|
|
255
|
+
setItem: (key: string, value: string) => Promise<void>;
|
|
256
|
+
removeItem: (key: string) => Promise<void>;
|
|
257
|
+
getAllKeys: () => Promise<readonly string[]>;
|
|
258
|
+
multiGet: (keys: readonly string[]) => Promise<readonly [string, string | null][]>;
|
|
259
|
+
}, keyPrefix?: string) => GuidonPersistenceAdapter;
|
|
260
|
+
/**
|
|
261
|
+
* Create a custom API adapter for backend persistence
|
|
262
|
+
* This is a factory function that creates an adapter based on your API endpoints
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* const adapter = createApiAdapter({
|
|
266
|
+
* loadProgress: async (guidonId) => {
|
|
267
|
+
* const response = await fetch(`/api/guidon/${guidonId}`);
|
|
268
|
+
* return response.json();
|
|
269
|
+
* },
|
|
270
|
+
* saveProgress: async (progress) => {
|
|
271
|
+
* await fetch(`/api/guidon/${progress.guidonId}`, {
|
|
272
|
+
* method: 'POST',
|
|
273
|
+
* body: JSON.stringify(progress),
|
|
274
|
+
* });
|
|
275
|
+
* },
|
|
276
|
+
* });
|
|
277
|
+
*/
|
|
278
|
+
declare const createApiAdapter: (handlers: Partial<GuidonPersistenceAdapter>) => GuidonPersistenceAdapter;
|
|
279
|
+
/**
|
|
280
|
+
* Combine multiple adapters (e.g., save to both local and API)
|
|
281
|
+
* Loads from the first adapter that returns data
|
|
282
|
+
* Saves to all adapters
|
|
283
|
+
*/
|
|
284
|
+
declare const createCompositeAdapter: (adapters: GuidonPersistenceAdapter[]) => GuidonPersistenceAdapter;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Hook to manage guidon's walkthrough progress with a persistence adapter
|
|
288
|
+
*/
|
|
289
|
+
declare function useGuidonPersistence(adapter: GuidonPersistenceAdapter | undefined, guidonId: string): {
|
|
290
|
+
progress: GuidonProgress | null;
|
|
291
|
+
isLoading: boolean;
|
|
292
|
+
error: string | null;
|
|
293
|
+
isCompleted: boolean;
|
|
294
|
+
hasStarted: boolean;
|
|
295
|
+
saveProgress: (newProgress: Omit<GuidonProgress, "guidonId">) => Promise<void>;
|
|
296
|
+
clearProgress: () => Promise<void>;
|
|
297
|
+
markCompleted: () => Promise<void>;
|
|
298
|
+
markStepViewed: (stepIndex: number) => Promise<void>;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Hook to check if a guidon should be shown
|
|
302
|
+
*/
|
|
303
|
+
declare function useShouldShowGuidon(adapter: GuidonPersistenceAdapter | undefined, guidonId: string, options?: {
|
|
304
|
+
/** Show even if completed (for replay) */
|
|
305
|
+
forceShow?: boolean;
|
|
306
|
+
/** Additional condition to check */
|
|
307
|
+
additionalCondition?: () => boolean | Promise<boolean>;
|
|
308
|
+
}): {
|
|
309
|
+
shouldShow: boolean;
|
|
310
|
+
isChecking: boolean;
|
|
311
|
+
isCompleted: boolean;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export { type GuidonTargetProps as G, type TargetMeasurements as T, type GuidonTheme as a, type GuidonStep as b, type GuidonProviderProps as c, type GuidonStore as d, type GuidonConfig as e, type GuidonProgress as f, type GuidonPersistenceAdapter as g, type GuidonTooltipLabels as h, type GuidonTooltipRenderProps as i, type TooltipPosition as j, type GuidonState as k, type GuidonActions as l, createNoopAdapter as m, createMemoryAdapter as n, createLocalStorageAdapter as o, createAsyncStorageAdapter as p, createApiAdapter as q, createCompositeAdapter as r, useShouldShowGuidon as s, useGuidonPersistence as u };
|