@korsolutions/guidon 1.0.0 → 1.0.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.
- package/README.md +11 -11
- package/dist/commonjs/babel.config.js +15 -0
- package/dist/commonjs/babel.config.js.map +1 -0
- package/dist/commonjs/bob.config.js +11 -0
- package/dist/commonjs/bob.config.js.map +1 -0
- package/dist/commonjs/components/GuidonOverlay.js +206 -0
- package/dist/commonjs/components/GuidonOverlay.js.map +1 -0
- package/dist/commonjs/components/GuidonProvider.js +157 -0
- package/dist/commonjs/components/GuidonProvider.js.map +1 -0
- package/dist/commonjs/components/GuidonTarget.js +108 -0
- package/dist/commonjs/components/GuidonTarget.js.map +1 -0
- package/dist/commonjs/components/GuidonTooltip.js +422 -0
- package/dist/commonjs/components/GuidonTooltip.js.map +1 -0
- package/dist/commonjs/components/index.js +40 -0
- package/dist/commonjs/components/index.js.map +1 -0
- package/dist/commonjs/hooks/index.js +13 -0
- package/dist/commonjs/hooks/index.js.map +1 -0
- package/dist/commonjs/hooks/useGuidonRef.js +132 -0
- package/dist/commonjs/hooks/useGuidonRef.js.map +1 -0
- package/dist/commonjs/index.js +143 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/package.json +1 -0
- package/dist/commonjs/persistence/adapters.js +213 -0
- package/dist/commonjs/persistence/adapters.js.map +1 -0
- package/dist/commonjs/persistence/hooks.js +153 -0
- package/dist/commonjs/persistence/hooks.js.map +1 -0
- package/dist/commonjs/persistence/index.js +28 -0
- package/dist/commonjs/persistence/index.js.map +1 -0
- package/dist/commonjs/store.js +305 -0
- package/dist/commonjs/store.js.map +1 -0
- package/dist/commonjs/tsconfig.json +32 -0
- package/dist/commonjs/types.js +6 -0
- package/dist/commonjs/types.js.map +1 -0
- package/dist/module/babel.config.js +15 -0
- package/dist/module/babel.config.js.map +1 -0
- package/dist/module/bob.config.js +11 -0
- package/dist/module/bob.config.js.map +1 -0
- package/dist/module/components/GuidonOverlay.js +201 -0
- package/dist/module/components/GuidonOverlay.js.map +1 -0
- package/dist/module/components/GuidonProvider.js +152 -0
- package/dist/module/components/GuidonProvider.js.map +1 -0
- package/dist/module/components/GuidonTarget.js +104 -0
- package/dist/module/components/GuidonTarget.js.map +1 -0
- package/dist/module/components/GuidonTooltip.js +417 -0
- package/dist/module/components/GuidonTooltip.js.map +1 -0
- package/dist/module/components/index.js +7 -0
- package/dist/module/components/index.js.map +1 -0
- package/dist/module/hooks/index.js +4 -0
- package/dist/module/hooks/index.js.map +1 -0
- package/dist/module/hooks/useGuidonRef.js +129 -0
- package/dist/module/hooks/useGuidonRef.js.map +1 -0
- package/dist/module/index.js +17 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/persistence/adapters.js +203 -0
- package/dist/module/persistence/adapters.js.map +1 -0
- package/dist/module/persistence/hooks.js +148 -0
- package/dist/module/persistence/hooks.js.map +1 -0
- package/dist/module/persistence/index.js +5 -0
- package/dist/module/persistence/index.js.map +1 -0
- package/dist/module/store.js +295 -0
- package/dist/module/store.js.map +1 -0
- package/dist/module/tsconfig.json +32 -0
- package/dist/module/types.js +4 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/commonjs/components/GuidonOverlay.d.ts +9 -0
- package/dist/typescript/commonjs/components/GuidonOverlay.d.ts.map +1 -0
- package/dist/typescript/commonjs/components/GuidonProvider.d.ts +14 -0
- package/dist/typescript/commonjs/components/GuidonProvider.d.ts.map +1 -0
- package/dist/typescript/commonjs/components/GuidonTarget.d.ts +7 -0
- package/dist/typescript/commonjs/components/GuidonTarget.d.ts.map +1 -0
- package/dist/typescript/commonjs/components/GuidonTooltip.d.ts +24 -0
- package/dist/typescript/commonjs/components/GuidonTooltip.d.ts.map +1 -0
- package/dist/typescript/commonjs/components/index.d.ts +5 -0
- package/dist/typescript/commonjs/components/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/hooks/index.d.ts +2 -0
- package/dist/typescript/commonjs/hooks/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/hooks/useGuidonRef.d.ts +35 -0
- package/dist/typescript/commonjs/hooks/useGuidonRef.d.ts.map +1 -0
- package/dist/typescript/commonjs/index.d.ts +7 -0
- package/dist/typescript/commonjs/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/package.json +1 -0
- package/dist/typescript/commonjs/persistence/adapters.d.ts +57 -0
- package/dist/typescript/commonjs/persistence/adapters.d.ts.map +1 -0
- package/dist/typescript/commonjs/persistence/hooks.d.ts +29 -0
- package/dist/typescript/commonjs/persistence/hooks.d.ts.map +1 -0
- package/dist/typescript/commonjs/persistence/index.d.ts +3 -0
- package/dist/typescript/commonjs/persistence/index.d.ts.map +1 -0
- package/dist/typescript/commonjs/store.d.ts +89 -0
- package/dist/typescript/commonjs/store.d.ts.map +1 -0
- package/dist/{index-D_JFvCIg.d.mts → typescript/commonjs/types.d.ts} +40 -104
- package/dist/typescript/commonjs/types.d.ts.map +1 -0
- package/dist/typescript/module/components/GuidonOverlay.d.ts +9 -0
- package/dist/typescript/module/components/GuidonOverlay.d.ts.map +1 -0
- package/dist/typescript/module/components/GuidonProvider.d.ts +14 -0
- package/dist/typescript/module/components/GuidonProvider.d.ts.map +1 -0
- package/dist/typescript/module/components/GuidonTarget.d.ts +7 -0
- package/dist/typescript/module/components/GuidonTarget.d.ts.map +1 -0
- package/dist/typescript/module/components/GuidonTooltip.d.ts +24 -0
- package/dist/typescript/module/components/GuidonTooltip.d.ts.map +1 -0
- package/dist/typescript/module/components/index.d.ts +5 -0
- package/dist/typescript/module/components/index.d.ts.map +1 -0
- package/dist/typescript/module/hooks/index.d.ts +2 -0
- package/dist/typescript/module/hooks/index.d.ts.map +1 -0
- package/dist/typescript/module/hooks/useGuidonRef.d.ts +35 -0
- package/dist/typescript/module/hooks/useGuidonRef.d.ts.map +1 -0
- package/dist/typescript/module/index.d.ts +7 -0
- package/dist/typescript/module/index.d.ts.map +1 -0
- package/dist/typescript/module/package.json +1 -0
- package/dist/typescript/module/persistence/adapters.d.ts +57 -0
- package/dist/typescript/module/persistence/adapters.d.ts.map +1 -0
- package/dist/typescript/module/persistence/hooks.d.ts +29 -0
- package/dist/typescript/module/persistence/hooks.d.ts.map +1 -0
- package/dist/typescript/module/persistence/index.d.ts +3 -0
- package/dist/typescript/module/persistence/index.d.ts.map +1 -0
- package/dist/typescript/module/store.d.ts +89 -0
- package/dist/typescript/module/store.d.ts.map +1 -0
- package/dist/{index-D_JFvCIg.d.ts → typescript/module/types.d.ts} +40 -104
- package/dist/typescript/module/types.d.ts.map +1 -0
- package/package.json +25 -13
- package/src/babel.config.js +18 -0
- package/src/bob.config.js +14 -0
- package/src/components/GuidonOverlay.tsx +60 -4
- package/src/components/GuidonProvider.tsx +29 -1
- package/src/components/GuidonTarget.tsx +26 -16
- package/src/components/GuidonTooltip.tsx +143 -9
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useGuidonRef.ts +154 -0
- package/src/index.ts +6 -0
- package/src/store.ts +40 -0
- package/src/tsconfig.json +32 -0
- package/src/types.ts +32 -2
- package/dist/index.d.mts +0 -128
- package/dist/index.d.ts +0 -128
- package/dist/index.js +0 -1098
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1073
- package/dist/index.mjs.map +0 -1
- package/dist/persistence/index.d.mts +0 -2
- package/dist/persistence/index.d.ts +0 -2
- package/dist/persistence/index.js +0 -300
- package/dist/persistence/index.js.map +0 -1
- package/dist/persistence/index.mjs +0 -291
- package/dist/persistence/index.mjs.map +0 -1
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
|
|
3
|
-
// src/persistence/adapters.ts
|
|
4
|
-
var STORAGE_KEY_PREFIX = "@guidon:";
|
|
5
|
-
var createNoopAdapter = () => ({
|
|
6
|
-
loadProgress: async () => null,
|
|
7
|
-
saveProgress: async () => {
|
|
8
|
-
},
|
|
9
|
-
loadAllProgress: async () => ({}),
|
|
10
|
-
clearProgress: async () => {
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
var createMemoryAdapter = () => {
|
|
14
|
-
const store = {};
|
|
15
|
-
return {
|
|
16
|
-
loadProgress: async (guidonId) => store[guidonId] ?? null,
|
|
17
|
-
saveProgress: async (progress) => {
|
|
18
|
-
store[progress.guidonId] = progress;
|
|
19
|
-
},
|
|
20
|
-
loadAllProgress: async () => ({ ...store }),
|
|
21
|
-
clearProgress: async (guidonId) => {
|
|
22
|
-
delete store[guidonId];
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
|
-
var createLocalStorageAdapter = (keyPrefix = STORAGE_KEY_PREFIX) => ({
|
|
27
|
-
loadProgress: async (guidonId) => {
|
|
28
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
const data = localStorage.getItem(`${keyPrefix}${guidonId}`);
|
|
33
|
-
return data ? JSON.parse(data) : null;
|
|
34
|
-
} catch {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
saveProgress: async (progress) => {
|
|
39
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
localStorage.setItem(
|
|
44
|
-
`${keyPrefix}${progress.guidonId}`,
|
|
45
|
-
JSON.stringify(progress)
|
|
46
|
-
);
|
|
47
|
-
} catch {
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
loadAllProgress: async () => {
|
|
51
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
const result = {};
|
|
55
|
-
try {
|
|
56
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
57
|
-
const key = localStorage.key(i);
|
|
58
|
-
if (key?.startsWith(keyPrefix)) {
|
|
59
|
-
const data = localStorage.getItem(key);
|
|
60
|
-
if (data) {
|
|
61
|
-
const progress = JSON.parse(data);
|
|
62
|
-
result[progress.guidonId] = progress;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
} catch {
|
|
67
|
-
}
|
|
68
|
-
return result;
|
|
69
|
-
},
|
|
70
|
-
clearProgress: async (guidonId) => {
|
|
71
|
-
if (typeof window === "undefined" || !window.localStorage) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
localStorage.removeItem(`${keyPrefix}${guidonId}`);
|
|
76
|
-
} catch {
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
var createAsyncStorageAdapter = (asyncStorage, keyPrefix = STORAGE_KEY_PREFIX) => ({
|
|
81
|
-
loadProgress: async (guidonId) => {
|
|
82
|
-
try {
|
|
83
|
-
const data = await asyncStorage.getItem(`${keyPrefix}${guidonId}`);
|
|
84
|
-
return data ? JSON.parse(data) : null;
|
|
85
|
-
} catch {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
saveProgress: async (progress) => {
|
|
90
|
-
try {
|
|
91
|
-
await asyncStorage.setItem(
|
|
92
|
-
`${keyPrefix}${progress.guidonId}`,
|
|
93
|
-
JSON.stringify(progress)
|
|
94
|
-
);
|
|
95
|
-
} catch {
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
loadAllProgress: async () => {
|
|
99
|
-
const result = {};
|
|
100
|
-
try {
|
|
101
|
-
const allKeys = await asyncStorage.getAllKeys();
|
|
102
|
-
const relevantKeys = allKeys.filter((key) => key.startsWith(keyPrefix));
|
|
103
|
-
const pairs = await asyncStorage.multiGet(relevantKeys);
|
|
104
|
-
for (const [, value] of pairs) {
|
|
105
|
-
if (value) {
|
|
106
|
-
const progress = JSON.parse(value);
|
|
107
|
-
result[progress.guidonId] = progress;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
} catch {
|
|
111
|
-
}
|
|
112
|
-
return result;
|
|
113
|
-
},
|
|
114
|
-
clearProgress: async (guidonId) => {
|
|
115
|
-
try {
|
|
116
|
-
await asyncStorage.removeItem(`${keyPrefix}${guidonId}`);
|
|
117
|
-
} catch {
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
var createApiAdapter = (handlers) => {
|
|
122
|
-
const noopAdapter = createNoopAdapter();
|
|
123
|
-
return {
|
|
124
|
-
loadProgress: handlers.loadProgress ?? noopAdapter.loadProgress,
|
|
125
|
-
saveProgress: handlers.saveProgress ?? noopAdapter.saveProgress,
|
|
126
|
-
loadAllProgress: handlers.loadAllProgress ?? noopAdapter.loadAllProgress,
|
|
127
|
-
clearProgress: handlers.clearProgress ?? noopAdapter.clearProgress
|
|
128
|
-
};
|
|
129
|
-
};
|
|
130
|
-
var createCompositeAdapter = (adapters) => ({
|
|
131
|
-
loadProgress: async (guidonId) => {
|
|
132
|
-
for (const adapter of adapters) {
|
|
133
|
-
const progress = await adapter.loadProgress(guidonId);
|
|
134
|
-
if (progress) return progress;
|
|
135
|
-
}
|
|
136
|
-
return null;
|
|
137
|
-
},
|
|
138
|
-
saveProgress: async (progress) => {
|
|
139
|
-
await Promise.all(adapters.map((adapter) => adapter.saveProgress(progress)));
|
|
140
|
-
},
|
|
141
|
-
loadAllProgress: async () => {
|
|
142
|
-
const result = {};
|
|
143
|
-
for (const adapter of adapters) {
|
|
144
|
-
if (adapter.loadAllProgress) {
|
|
145
|
-
const data = await adapter.loadAllProgress();
|
|
146
|
-
Object.assign(result, data);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return result;
|
|
150
|
-
},
|
|
151
|
-
clearProgress: async (guidonId) => {
|
|
152
|
-
await Promise.all(
|
|
153
|
-
adapters.map((adapter) => adapter.clearProgress?.(guidonId))
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
function useGuidonPersistence(adapter, guidonId) {
|
|
158
|
-
const [progress, setProgress] = useState(null);
|
|
159
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
160
|
-
const [error, setError] = useState(null);
|
|
161
|
-
useEffect(() => {
|
|
162
|
-
if (!adapter) {
|
|
163
|
-
setIsLoading(false);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
let mounted = true;
|
|
167
|
-
const loadProgress = async () => {
|
|
168
|
-
try {
|
|
169
|
-
setIsLoading(true);
|
|
170
|
-
setError(null);
|
|
171
|
-
const data = await adapter.loadProgress(guidonId);
|
|
172
|
-
if (mounted) {
|
|
173
|
-
setProgress(data);
|
|
174
|
-
}
|
|
175
|
-
} catch (err) {
|
|
176
|
-
if (mounted) {
|
|
177
|
-
setError(err instanceof Error ? err.message : "Failed to load progress");
|
|
178
|
-
}
|
|
179
|
-
} finally {
|
|
180
|
-
if (mounted) {
|
|
181
|
-
setIsLoading(false);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
loadProgress();
|
|
186
|
-
return () => {
|
|
187
|
-
mounted = false;
|
|
188
|
-
};
|
|
189
|
-
}, [adapter, guidonId]);
|
|
190
|
-
const saveProgress = useCallback(
|
|
191
|
-
async (newProgress) => {
|
|
192
|
-
if (!adapter) return;
|
|
193
|
-
const fullProgress = {
|
|
194
|
-
...newProgress,
|
|
195
|
-
guidonId
|
|
196
|
-
};
|
|
197
|
-
try {
|
|
198
|
-
setError(null);
|
|
199
|
-
await adapter.saveProgress(fullProgress);
|
|
200
|
-
setProgress(fullProgress);
|
|
201
|
-
} catch (err) {
|
|
202
|
-
setError(err instanceof Error ? err.message : "Failed to save progress");
|
|
203
|
-
throw err;
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
[adapter, guidonId]
|
|
207
|
-
);
|
|
208
|
-
const clearProgress = useCallback(async () => {
|
|
209
|
-
if (!adapter?.clearProgress) return;
|
|
210
|
-
try {
|
|
211
|
-
setError(null);
|
|
212
|
-
await adapter.clearProgress(guidonId);
|
|
213
|
-
setProgress(null);
|
|
214
|
-
} catch (err) {
|
|
215
|
-
setError(err instanceof Error ? err.message : "Failed to clear progress");
|
|
216
|
-
throw err;
|
|
217
|
-
}
|
|
218
|
-
}, [adapter, guidonId]);
|
|
219
|
-
const markCompleted = useCallback(async () => {
|
|
220
|
-
const currentCount = progress?.completionCount ?? 0;
|
|
221
|
-
await saveProgress({
|
|
222
|
-
completed: true,
|
|
223
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
224
|
-
completionCount: currentCount + 1
|
|
225
|
-
});
|
|
226
|
-
}, [saveProgress, progress?.completionCount]);
|
|
227
|
-
const markStepViewed = useCallback(
|
|
228
|
-
async (stepIndex) => {
|
|
229
|
-
await saveProgress({
|
|
230
|
-
completed: progress?.completed ?? false,
|
|
231
|
-
lastStepIndex: stepIndex,
|
|
232
|
-
completedAt: progress?.completedAt,
|
|
233
|
-
completionCount: progress?.completionCount
|
|
234
|
-
});
|
|
235
|
-
},
|
|
236
|
-
[saveProgress, progress]
|
|
237
|
-
);
|
|
238
|
-
return {
|
|
239
|
-
progress,
|
|
240
|
-
isLoading,
|
|
241
|
-
error,
|
|
242
|
-
isCompleted: progress?.completed ?? false,
|
|
243
|
-
hasStarted: progress !== null,
|
|
244
|
-
saveProgress,
|
|
245
|
-
clearProgress,
|
|
246
|
-
markCompleted,
|
|
247
|
-
markStepViewed
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
function useShouldShowGuidon(adapter, guidonId, options) {
|
|
251
|
-
const { progress, isLoading } = useGuidonPersistence(adapter, guidonId);
|
|
252
|
-
const [shouldShow, setShouldShow] = useState(false);
|
|
253
|
-
const [isChecking, setIsChecking] = useState(true);
|
|
254
|
-
useEffect(() => {
|
|
255
|
-
if (isLoading) return;
|
|
256
|
-
const checkCondition = async () => {
|
|
257
|
-
setIsChecking(true);
|
|
258
|
-
if (options?.forceShow) {
|
|
259
|
-
setShouldShow(true);
|
|
260
|
-
setIsChecking(false);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
if (progress?.completed) {
|
|
264
|
-
setShouldShow(false);
|
|
265
|
-
setIsChecking(false);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
if (options?.additionalCondition) {
|
|
269
|
-
try {
|
|
270
|
-
const result = await options.additionalCondition();
|
|
271
|
-
setShouldShow(result);
|
|
272
|
-
} catch {
|
|
273
|
-
setShouldShow(true);
|
|
274
|
-
}
|
|
275
|
-
} else {
|
|
276
|
-
setShouldShow(true);
|
|
277
|
-
}
|
|
278
|
-
setIsChecking(false);
|
|
279
|
-
};
|
|
280
|
-
checkCondition();
|
|
281
|
-
}, [isLoading, progress?.completed, options?.forceShow, options?.additionalCondition]);
|
|
282
|
-
return {
|
|
283
|
-
shouldShow,
|
|
284
|
-
isChecking: isLoading || isChecking,
|
|
285
|
-
isCompleted: progress?.completed ?? false
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export { createApiAdapter, createAsyncStorageAdapter, createCompositeAdapter, createLocalStorageAdapter, createMemoryAdapter, createNoopAdapter, useGuidonPersistence, useShouldShowGuidon };
|
|
290
|
-
//# sourceMappingURL=index.mjs.map
|
|
291
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/persistence/adapters.ts","../../src/persistence/hooks.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { GuidonPersistenceAdapter, GuidonProgress } from '../types';\n\nconst STORAGE_KEY_PREFIX = '@guidon:';\n\n/**\n * No-op adapter that doesn't persist anything\n * Useful for testing or when persistence is not needed\n */\nexport const createNoopAdapter = (): GuidonPersistenceAdapter => ({\n loadProgress: async () => null,\n saveProgress: async () => {},\n loadAllProgress: async () => ({}),\n clearProgress: async () => {},\n});\n\n/**\n * Memory adapter that stores progress in memory\n * Data is lost when the app is closed\n */\nexport const createMemoryAdapter = (): GuidonPersistenceAdapter => {\n const store: Record<string, GuidonProgress> = {};\n\n return {\n loadProgress: async (guidonId) => store[guidonId] ?? null,\n saveProgress: async (progress) => {\n store[progress.guidonId] = progress;\n },\n loadAllProgress: async () => ({ ...store }),\n clearProgress: async (guidonId) => {\n delete store[guidonId];\n },\n };\n};\n\n/**\n * localStorage adapter for web\n * Only works in browser environments\n */\nexport const createLocalStorageAdapter = (\n keyPrefix: string = STORAGE_KEY_PREFIX\n): GuidonPersistenceAdapter => ({\n loadProgress: async (guidonId) => {\n if (typeof window === 'undefined' || !window.localStorage) {\n return null;\n }\n try {\n const data = localStorage.getItem(`${keyPrefix}${guidonId}`);\n return data ? JSON.parse(data) : null;\n } catch {\n return null;\n }\n },\n saveProgress: async (progress) => {\n if (typeof window === 'undefined' || !window.localStorage) {\n return;\n }\n try {\n localStorage.setItem(\n `${keyPrefix}${progress.guidonId}`,\n JSON.stringify(progress)\n );\n } catch {\n // Silently fail if storage is full\n }\n },\n loadAllProgress: async () => {\n if (typeof window === 'undefined' || !window.localStorage) {\n return {};\n }\n const result: Record<string, GuidonProgress> = {};\n try {\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(keyPrefix)) {\n const data = localStorage.getItem(key);\n if (data) {\n const progress = JSON.parse(data) as GuidonProgress;\n result[progress.guidonId] = progress;\n }\n }\n }\n } catch {\n // Silently fail\n }\n return result;\n },\n clearProgress: async (guidonId) => {\n if (typeof window === 'undefined' || !window.localStorage) {\n return;\n }\n try {\n localStorage.removeItem(`${keyPrefix}${guidonId}`);\n } catch {\n // Silently fail\n }\n },\n});\n\n/**\n * AsyncStorage adapter for React Native\n * Requires @react-native-async-storage/async-storage to be installed\n *\n * @example\n * import AsyncStorage from '@react-native-async-storage/async-storage';\n * const adapter = createAsyncStorageAdapter(AsyncStorage);\n */\nexport const createAsyncStorageAdapter = (\n asyncStorage: {\n getItem: (key: string) => Promise<string | null>;\n setItem: (key: string, value: string) => Promise<void>;\n removeItem: (key: string) => Promise<void>;\n getAllKeys: () => Promise<readonly string[]>;\n multiGet: (keys: readonly string[]) => Promise<readonly [string, string | null][]>;\n },\n keyPrefix: string = STORAGE_KEY_PREFIX\n): GuidonPersistenceAdapter => ({\n loadProgress: async (guidonId) => {\n try {\n const data = await asyncStorage.getItem(`${keyPrefix}${guidonId}`);\n return data ? JSON.parse(data) : null;\n } catch {\n return null;\n }\n },\n saveProgress: async (progress) => {\n try {\n await asyncStorage.setItem(\n `${keyPrefix}${progress.guidonId}`,\n JSON.stringify(progress)\n );\n } catch {\n // Silently fail\n }\n },\n loadAllProgress: async () => {\n const result: Record<string, GuidonProgress> = {};\n try {\n const allKeys = await asyncStorage.getAllKeys();\n const relevantKeys = allKeys.filter((key) => key.startsWith(keyPrefix));\n const pairs = await asyncStorage.multiGet(relevantKeys);\n for (const [, value] of pairs) {\n if (value) {\n const progress = JSON.parse(value) as GuidonProgress;\n result[progress.guidonId] = progress;\n }\n }\n } catch {\n // Silently fail\n }\n return result;\n },\n clearProgress: async (guidonId) => {\n try {\n await asyncStorage.removeItem(`${keyPrefix}${guidonId}`);\n } catch {\n // Silently fail\n }\n },\n});\n\n/**\n * Create a custom API adapter for backend persistence\n * This is a factory function that creates an adapter based on your API endpoints\n *\n * @example\n * const adapter = createApiAdapter({\n * loadProgress: async (guidonId) => {\n * const response = await fetch(`/api/guidon/${guidonId}`);\n * return response.json();\n * },\n * saveProgress: async (progress) => {\n * await fetch(`/api/guidon/${progress.guidonId}`, {\n * method: 'POST',\n * body: JSON.stringify(progress),\n * });\n * },\n * });\n */\nexport const createApiAdapter = (\n handlers: Partial<GuidonPersistenceAdapter>\n): GuidonPersistenceAdapter => {\n const noopAdapter = createNoopAdapter();\n return {\n loadProgress: handlers.loadProgress ?? noopAdapter.loadProgress,\n saveProgress: handlers.saveProgress ?? noopAdapter.saveProgress,\n loadAllProgress: handlers.loadAllProgress ?? noopAdapter.loadAllProgress,\n clearProgress: handlers.clearProgress ?? noopAdapter.clearProgress,\n };\n};\n\n/**\n * Combine multiple adapters (e.g., save to both local and API)\n * Loads from the first adapter that returns data\n * Saves to all adapters\n */\nexport const createCompositeAdapter = (\n adapters: GuidonPersistenceAdapter[]\n): GuidonPersistenceAdapter => ({\n loadProgress: async (guidonId) => {\n for (const adapter of adapters) {\n const progress = await adapter.loadProgress(guidonId);\n if (progress) return progress;\n }\n return null;\n },\n saveProgress: async (progress) => {\n await Promise.all(adapters.map((adapter) => adapter.saveProgress(progress)));\n },\n loadAllProgress: async () => {\n const result: Record<string, GuidonProgress> = {};\n for (const adapter of adapters) {\n if (adapter.loadAllProgress) {\n const data = await adapter.loadAllProgress();\n Object.assign(result, data);\n }\n }\n return result;\n },\n clearProgress: async (guidonId) => {\n await Promise.all(\n adapters.map((adapter) => adapter.clearProgress?.(guidonId))\n );\n },\n});\n","import { useCallback, useEffect, useState } from 'react';\nimport type { GuidonPersistenceAdapter, GuidonProgress } from '../types';\n\n/**\n * Hook to manage guidon's walkthrough progress with a persistence adapter\n */\nexport function useGuidonPersistence(\n adapter: GuidonPersistenceAdapter | undefined,\n guidonId: string\n) {\n const [progress, setProgress] = useState<GuidonProgress | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n // Load progress on mount\n useEffect(() => {\n if (!adapter) {\n setIsLoading(false);\n return;\n }\n\n let mounted = true;\n\n const loadProgress = async () => {\n try {\n setIsLoading(true);\n setError(null);\n const data = await adapter.loadProgress(guidonId);\n if (mounted) {\n setProgress(data);\n }\n } catch (err) {\n if (mounted) {\n setError(err instanceof Error ? err.message : 'Failed to load progress');\n }\n } finally {\n if (mounted) {\n setIsLoading(false);\n }\n }\n };\n\n loadProgress();\n\n return () => {\n mounted = false;\n };\n }, [adapter, guidonId]);\n\n const saveProgress = useCallback(\n async (newProgress: Omit<GuidonProgress, 'guidonId'>) => {\n if (!adapter) return;\n\n const fullProgress: GuidonProgress = {\n ...newProgress,\n guidonId,\n };\n\n try {\n setError(null);\n await adapter.saveProgress(fullProgress);\n setProgress(fullProgress);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to save progress');\n throw err;\n }\n },\n [adapter, guidonId]\n );\n\n const clearProgress = useCallback(async () => {\n if (!adapter?.clearProgress) return;\n\n try {\n setError(null);\n await adapter.clearProgress(guidonId);\n setProgress(null);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to clear progress');\n throw err;\n }\n }, [adapter, guidonId]);\n\n const markCompleted = useCallback(async () => {\n const currentCount = progress?.completionCount ?? 0;\n await saveProgress({\n completed: true,\n completedAt: new Date().toISOString(),\n completionCount: currentCount + 1,\n });\n }, [saveProgress, progress?.completionCount]);\n\n const markStepViewed = useCallback(\n async (stepIndex: number) => {\n await saveProgress({\n completed: progress?.completed ?? false,\n lastStepIndex: stepIndex,\n completedAt: progress?.completedAt,\n completionCount: progress?.completionCount,\n });\n },\n [saveProgress, progress]\n );\n\n return {\n progress,\n isLoading,\n error,\n isCompleted: progress?.completed ?? false,\n hasStarted: progress !== null,\n saveProgress,\n clearProgress,\n markCompleted,\n markStepViewed,\n };\n}\n\n/**\n * Hook to check if a guidon should be shown\n */\nexport function useShouldShowGuidon(\n adapter: GuidonPersistenceAdapter | undefined,\n guidonId: string,\n options?: {\n /** Show even if completed (for replay) */\n forceShow?: boolean;\n /** Additional condition to check */\n additionalCondition?: () => boolean | Promise<boolean>;\n }\n) {\n const { progress, isLoading } = useGuidonPersistence(adapter, guidonId);\n const [shouldShow, setShouldShow] = useState(false);\n const [isChecking, setIsChecking] = useState(true);\n\n useEffect(() => {\n if (isLoading) return;\n\n const checkCondition = async () => {\n setIsChecking(true);\n\n // If forceShow is true, always show\n if (options?.forceShow) {\n setShouldShow(true);\n setIsChecking(false);\n return;\n }\n\n // If already completed, don't show\n if (progress?.completed) {\n setShouldShow(false);\n setIsChecking(false);\n return;\n }\n\n // Check additional condition if provided\n if (options?.additionalCondition) {\n try {\n const result = await options.additionalCondition();\n setShouldShow(result);\n } catch {\n setShouldShow(true); // Default to showing on error\n }\n } else {\n // Default: show if not completed\n setShouldShow(true);\n }\n\n setIsChecking(false);\n };\n\n checkCondition();\n }, [isLoading, progress?.completed, options?.forceShow, options?.additionalCondition]);\n\n return {\n shouldShow,\n isChecking: isLoading || isChecking,\n isCompleted: progress?.completed ?? false,\n };\n}\n"]}
|