ontheway-sdk 0.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.
- package/README.md +110 -0
- package/dist/checklist.cjs +319 -0
- package/dist/checklist.cjs.map +1 -0
- package/dist/checklist.d.cts +58 -0
- package/dist/checklist.d.ts +58 -0
- package/dist/checklist.js +314 -0
- package/dist/checklist.js.map +1 -0
- package/dist/chunk-254YHUN3.cjs +26 -0
- package/dist/chunk-254YHUN3.cjs.map +1 -0
- package/dist/chunk-DDAAVRWG.js +23 -0
- package/dist/chunk-DDAAVRWG.js.map +1 -0
- package/dist/chunk-NRUQU5AR.cjs +94 -0
- package/dist/chunk-NRUQU5AR.cjs.map +1 -0
- package/dist/chunk-OKJ5GEH3.js +358 -0
- package/dist/chunk-OKJ5GEH3.js.map +1 -0
- package/dist/chunk-RNQLNLNI.js +91 -0
- package/dist/chunk-RNQLNLNI.js.map +1 -0
- package/dist/chunk-UE3T6TSM.cjs +361 -0
- package/dist/chunk-UE3T6TSM.cjs.map +1 -0
- package/dist/components.cjs +211 -0
- package/dist/components.cjs.map +1 -0
- package/dist/components.d.cts +51 -0
- package/dist/components.d.ts +51 -0
- package/dist/components.js +205 -0
- package/dist/components.js.map +1 -0
- package/dist/devtools.cjs +733 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +18 -0
- package/dist/devtools.d.ts +18 -0
- package/dist/devtools.js +727 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +163 -0
- package/dist/index.d.ts +163 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +18 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +68 -0
- package/dist/react.d.ts +68 -0
- package/dist/react.js +5 -0
- package/dist/react.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk254YHUN3_cjs = require('./chunk-254YHUN3.cjs');
|
|
4
|
+
var driver_js = require('driver.js');
|
|
5
|
+
|
|
6
|
+
var DRIVER_CSS_CDN = "https://cdn.jsdelivr.net/npm/driver.js@1.3.1/dist/driver.css";
|
|
7
|
+
function loadDriverCSS() {
|
|
8
|
+
if (typeof document === "undefined") return;
|
|
9
|
+
if (document.querySelector("link[data-otw-driver-css]")) return;
|
|
10
|
+
const link = document.createElement("link");
|
|
11
|
+
link.rel = "stylesheet";
|
|
12
|
+
link.href = DRIVER_CSS_CDN;
|
|
13
|
+
link.setAttribute("data-otw-driver-css", "1");
|
|
14
|
+
document.head.appendChild(link);
|
|
15
|
+
}
|
|
16
|
+
var CROSS_PAGE_KEY = "otw_active_tour";
|
|
17
|
+
var OnTheWay = class {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.driverInstance = null;
|
|
20
|
+
/** Condition functions registered via `registerCondition` */
|
|
21
|
+
this.conditions = /* @__PURE__ */ new Map();
|
|
22
|
+
this.config = chunk254YHUN3_cjs.__spreadValues({
|
|
23
|
+
apiUrl: "/api"
|
|
24
|
+
}, config);
|
|
25
|
+
this.state = {
|
|
26
|
+
loaded: false,
|
|
27
|
+
tasks: [],
|
|
28
|
+
completedTasks: /* @__PURE__ */ new Set()
|
|
29
|
+
};
|
|
30
|
+
if (config.driverCssUrl !== false) {
|
|
31
|
+
if (config.driverCssUrl) {
|
|
32
|
+
if (typeof document !== "undefined" && !document.querySelector("link[data-otw-driver-css]")) {
|
|
33
|
+
const link = document.createElement("link");
|
|
34
|
+
link.rel = "stylesheet";
|
|
35
|
+
link.href = config.driverCssUrl;
|
|
36
|
+
link.setAttribute("data-otw-driver-css", "1");
|
|
37
|
+
document.head.appendChild(link);
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
loadDriverCSS();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
this.visitorId = this.getOrCreateVisitorId();
|
|
44
|
+
this.init();
|
|
45
|
+
}
|
|
46
|
+
async init() {
|
|
47
|
+
this.loadCompletedTasks();
|
|
48
|
+
await this.fetchTasks();
|
|
49
|
+
this.state.loaded = true;
|
|
50
|
+
if (!this.resumeCrossPageTour()) {
|
|
51
|
+
this.handleAutoStart();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
getOrCreateVisitorId() {
|
|
55
|
+
if (typeof localStorage === "undefined") return "v_ssr";
|
|
56
|
+
const key = "otw_visitor_id";
|
|
57
|
+
let id = localStorage.getItem(key);
|
|
58
|
+
if (!id) {
|
|
59
|
+
id = "v_" + Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
60
|
+
localStorage.setItem(key, id);
|
|
61
|
+
}
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
loadCompletedTasks() {
|
|
65
|
+
if (typeof localStorage === "undefined") return;
|
|
66
|
+
const key = `otw_completed_${this.config.projectId}`;
|
|
67
|
+
const completed = localStorage.getItem(key);
|
|
68
|
+
if (completed) {
|
|
69
|
+
this.state.completedTasks = new Set(JSON.parse(completed));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
saveCompletedTask(taskId) {
|
|
73
|
+
this.state.completedTasks.add(taskId);
|
|
74
|
+
if (typeof localStorage === "undefined") return;
|
|
75
|
+
const key = `otw_completed_${this.config.projectId}`;
|
|
76
|
+
localStorage.setItem(key, JSON.stringify([...this.state.completedTasks]));
|
|
77
|
+
}
|
|
78
|
+
async fetchTasks() {
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(`${this.config.apiUrl}/sdk/${this.config.projectId}/config`);
|
|
81
|
+
if (!res.ok) throw new Error("Failed to fetch config");
|
|
82
|
+
const data = await res.json();
|
|
83
|
+
this.state.tasks = data.tasks || [];
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.warn("[OnTheWay] Failed to load config:", error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
handleAutoStart() {
|
|
89
|
+
var _a, _b, _c;
|
|
90
|
+
if (typeof window === "undefined") return;
|
|
91
|
+
const currentUrl = window.location.href;
|
|
92
|
+
for (const task of this.state.tasks) {
|
|
93
|
+
if (this.state.completedTasks.has(task.id)) continue;
|
|
94
|
+
if ((_a = task.targeting) == null ? void 0 : _a.urlPattern) {
|
|
95
|
+
const pattern = new RegExp(task.targeting.urlPattern);
|
|
96
|
+
if (!pattern.test(currentUrl)) continue;
|
|
97
|
+
}
|
|
98
|
+
if (task.trigger === "auto") {
|
|
99
|
+
this.start(task.slug);
|
|
100
|
+
break;
|
|
101
|
+
} else if (task.trigger === "first-visit") {
|
|
102
|
+
if (typeof localStorage === "undefined") continue;
|
|
103
|
+
const visitKey = `otw_visited_${task.id}`;
|
|
104
|
+
if (!localStorage.getItem(visitKey)) {
|
|
105
|
+
localStorage.setItem(visitKey, "true");
|
|
106
|
+
this.start(task.slug);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
} else if (task.trigger === "condition") {
|
|
110
|
+
const conditionFn = (_c = this.conditions.get(task.slug)) != null ? _c : (_b = task.targeting) == null ? void 0 : _b.condition;
|
|
111
|
+
if (conditionFn && conditionFn()) {
|
|
112
|
+
this.start(task.slug);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ---- Cross-page tour helpers ----
|
|
119
|
+
/**
|
|
120
|
+
* Save cross-page tour state so it survives navigation.
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
saveCrossPageState(taskSlug, stepIndex) {
|
|
124
|
+
if (typeof sessionStorage === "undefined") return;
|
|
125
|
+
const state = {
|
|
126
|
+
taskSlug,
|
|
127
|
+
stepIndex,
|
|
128
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
129
|
+
};
|
|
130
|
+
sessionStorage.setItem(CROSS_PAGE_KEY, JSON.stringify(state));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Clear cross-page tour state.
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
clearCrossPageState() {
|
|
137
|
+
if (typeof sessionStorage === "undefined") return;
|
|
138
|
+
sessionStorage.removeItem(CROSS_PAGE_KEY);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Attempt to resume a cross-page tour from sessionStorage.
|
|
142
|
+
* @returns `true` if a tour was resumed, `false` otherwise.
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
resumeCrossPageTour() {
|
|
146
|
+
if (typeof sessionStorage === "undefined") return false;
|
|
147
|
+
const raw = sessionStorage.getItem(CROSS_PAGE_KEY);
|
|
148
|
+
if (!raw) return false;
|
|
149
|
+
try {
|
|
150
|
+
const state = JSON.parse(raw);
|
|
151
|
+
const task = this.state.tasks.find((t) => t.slug === state.taskSlug);
|
|
152
|
+
if (!task) {
|
|
153
|
+
this.clearCrossPageState();
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
this.startAtStep(task, state.stepIndex);
|
|
157
|
+
return true;
|
|
158
|
+
} catch (e) {
|
|
159
|
+
this.clearCrossPageState();
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check whether a step's URL matches the current page.
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
167
|
+
stepUrlMatches(stepUrl) {
|
|
168
|
+
if (!stepUrl) return true;
|
|
169
|
+
if (typeof window === "undefined") return true;
|
|
170
|
+
const current = window.location.pathname + window.location.search;
|
|
171
|
+
try {
|
|
172
|
+
const parsed = new URL(stepUrl, window.location.origin);
|
|
173
|
+
return parsed.pathname === window.location.pathname && parsed.search === window.location.search;
|
|
174
|
+
} catch (e) {
|
|
175
|
+
return current === stepUrl;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Start a task at a specific step index (used for cross-page resume).
|
|
180
|
+
* @internal
|
|
181
|
+
*/
|
|
182
|
+
startAtStep(task, fromIndex) {
|
|
183
|
+
const allSteps = task.steps;
|
|
184
|
+
const targetStep = allSteps[fromIndex];
|
|
185
|
+
if ((targetStep == null ? void 0 : targetStep.url) && !this.stepUrlMatches(targetStep.url)) {
|
|
186
|
+
this.saveCrossPageState(task.slug, fromIndex);
|
|
187
|
+
window.location.href = targetStep.url;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const steps = allSteps.slice(fromIndex).map((step) => ({
|
|
191
|
+
element: step.element,
|
|
192
|
+
popover: {
|
|
193
|
+
title: step.popover.title,
|
|
194
|
+
description: step.popover.description,
|
|
195
|
+
side: step.popover.side
|
|
196
|
+
}
|
|
197
|
+
}));
|
|
198
|
+
const totalSteps = allSteps.length;
|
|
199
|
+
this.driverInstance = driver_js.driver({
|
|
200
|
+
showProgress: true,
|
|
201
|
+
steps,
|
|
202
|
+
onNextClick: () => {
|
|
203
|
+
var _a;
|
|
204
|
+
if (!this.driverInstance) return;
|
|
205
|
+
const relativeIndex = (_a = this.driverInstance.getActiveIndex()) != null ? _a : 0;
|
|
206
|
+
const absoluteIndex = fromIndex + relativeIndex + 1;
|
|
207
|
+
if (absoluteIndex < allSteps.length) {
|
|
208
|
+
const nextStep = allSteps[absoluteIndex];
|
|
209
|
+
if (nextStep.url && !this.stepUrlMatches(nextStep.url)) {
|
|
210
|
+
this.saveCrossPageState(task.slug, absoluteIndex);
|
|
211
|
+
this.driverInstance.destroy();
|
|
212
|
+
window.location.href = nextStep.url;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
this.driverInstance.moveNext();
|
|
217
|
+
},
|
|
218
|
+
onDestroyStarted: () => {
|
|
219
|
+
var _a, _b, _c, _d, _e, _f;
|
|
220
|
+
if ((_a = this.driverInstance) == null ? void 0 : _a.hasNextStep()) {
|
|
221
|
+
const relativeIndex = this.driverInstance.getActiveIndex() || 0;
|
|
222
|
+
const currentIndex = fromIndex + relativeIndex;
|
|
223
|
+
(_c = (_b = this.config).onSkip) == null ? void 0 : _c.call(_b, task.id, currentIndex);
|
|
224
|
+
this.trackCompletion(task.id, currentIndex, totalSteps, false);
|
|
225
|
+
this.clearCrossPageState();
|
|
226
|
+
} else {
|
|
227
|
+
this.saveCompletedTask(task.id);
|
|
228
|
+
(_e = (_d = this.config).onComplete) == null ? void 0 : _e.call(_d, task.id);
|
|
229
|
+
this.trackCompletion(task.id, totalSteps, totalSteps, true);
|
|
230
|
+
this.clearCrossPageState();
|
|
231
|
+
}
|
|
232
|
+
(_f = this.driverInstance) == null ? void 0 : _f.destroy();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
this.driverInstance.drive();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Register a condition function for a task slug.
|
|
239
|
+
* When the task trigger is `'condition'`, this function will be evaluated
|
|
240
|
+
* during auto-start to decide whether to show the tour.
|
|
241
|
+
*
|
|
242
|
+
* @param slug - Task slug to attach the condition to
|
|
243
|
+
* @param fn - Predicate that returns `true` to trigger the tour
|
|
244
|
+
*/
|
|
245
|
+
registerCondition(slug, fn) {
|
|
246
|
+
this.conditions.set(slug, fn);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Re-evaluate conditions and auto-start eligible tasks.
|
|
250
|
+
* Call this when application state changes (e.g. data loaded) and
|
|
251
|
+
* condition-based tours should be re-checked.
|
|
252
|
+
*/
|
|
253
|
+
checkConditions() {
|
|
254
|
+
this.handleAutoStart();
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Start a task by slug or ID
|
|
258
|
+
*/
|
|
259
|
+
start(slugOrId) {
|
|
260
|
+
const task = this.state.tasks.find((t) => t.slug === slugOrId || t.id === slugOrId);
|
|
261
|
+
if (!task) {
|
|
262
|
+
console.warn(`[OnTheWay] Task not found: ${slugOrId}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
this.startAtStep(task, 0);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Reset a task (allow it to show again)
|
|
269
|
+
*/
|
|
270
|
+
reset(slugOrId) {
|
|
271
|
+
const task = this.state.tasks.find((t) => t.slug === slugOrId || t.id === slugOrId);
|
|
272
|
+
if (task) {
|
|
273
|
+
this.state.completedTasks.delete(task.id);
|
|
274
|
+
if (typeof localStorage !== "undefined") {
|
|
275
|
+
const key = `otw_completed_${this.config.projectId}`;
|
|
276
|
+
localStorage.setItem(key, JSON.stringify([...this.state.completedTasks]));
|
|
277
|
+
localStorage.removeItem(`otw_visited_${task.id}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Reset all tasks for this project
|
|
283
|
+
*/
|
|
284
|
+
resetAll() {
|
|
285
|
+
this.state.completedTasks.clear();
|
|
286
|
+
if (typeof localStorage !== "undefined") {
|
|
287
|
+
localStorage.removeItem(`otw_completed_${this.config.projectId}`);
|
|
288
|
+
this.state.tasks.forEach((task) => {
|
|
289
|
+
localStorage.removeItem(`otw_visited_${task.id}`);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async trackCompletion(taskId, stepsCompleted, totalSteps, completed) {
|
|
294
|
+
try {
|
|
295
|
+
await fetch(`${this.config.apiUrl}/sdk/track`, {
|
|
296
|
+
method: "POST",
|
|
297
|
+
headers: { "Content-Type": "application/json" },
|
|
298
|
+
body: JSON.stringify({
|
|
299
|
+
task_id: taskId,
|
|
300
|
+
visitor_id: this.visitorId,
|
|
301
|
+
steps_completed: stepsCompleted,
|
|
302
|
+
total_steps: totalSteps,
|
|
303
|
+
completed
|
|
304
|
+
})
|
|
305
|
+
});
|
|
306
|
+
} catch (e) {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Get the project ID this SDK instance was configured with.
|
|
311
|
+
*/
|
|
312
|
+
getProjectId() {
|
|
313
|
+
return this.config.projectId;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Check if SDK is loaded
|
|
317
|
+
*/
|
|
318
|
+
isReady() {
|
|
319
|
+
return this.state.loaded;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Check if a task has been completed
|
|
323
|
+
*/
|
|
324
|
+
isTaskCompleted(slugOrId) {
|
|
325
|
+
const task = this.state.tasks.find((t) => t.slug === slugOrId || t.id === slugOrId);
|
|
326
|
+
if (!task) return false;
|
|
327
|
+
return this.state.completedTasks.has(task.id);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get list of available tasks
|
|
331
|
+
*/
|
|
332
|
+
getTasks() {
|
|
333
|
+
return [...this.state.tasks];
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get the set of completed task IDs
|
|
337
|
+
*/
|
|
338
|
+
getCompletedTaskIds() {
|
|
339
|
+
return new Set(this.state.completedTasks);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get pending cross-page tour state, if any.
|
|
343
|
+
* Useful for the React provider to check on mount.
|
|
344
|
+
*/
|
|
345
|
+
static getCrossPageState() {
|
|
346
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
347
|
+
const raw = sessionStorage.getItem(CROSS_PAGE_KEY);
|
|
348
|
+
if (!raw) return null;
|
|
349
|
+
try {
|
|
350
|
+
return JSON.parse(raw);
|
|
351
|
+
} catch (e) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
var src_default = OnTheWay;
|
|
357
|
+
|
|
358
|
+
exports.OnTheWay = OnTheWay;
|
|
359
|
+
exports.src_default = src_default;
|
|
360
|
+
//# sourceMappingURL=chunk-UE3T6TSM.cjs.map
|
|
361
|
+
//# sourceMappingURL=chunk-UE3T6TSM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["__spreadValues","driver"],"mappings":"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","file":"chunk-UE3T6TSM.cjs","sourcesContent":["/**\n * OnTheWay SDK\n * Lightweight onboarding SDK based on Driver.js\n *\n * Usage:\n * import { OnTheWay } from '@ontheway/sdk'\n * const otw = new OnTheWay({ projectId: 'PROJECT_ID' })\n * otw.start('task-slug')\n */\n\nimport { driver, type Driver, type DriveStep } from 'driver.js'\n\n// ---- Runtime CSS injection ----\n\nconst DRIVER_CSS_CDN = 'https://cdn.jsdelivr.net/npm/driver.js@1.3.1/dist/driver.css'\n\nfunction loadDriverCSS() {\n if (typeof document === 'undefined') return\n if (document.querySelector('link[data-otw-driver-css]')) return\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = DRIVER_CSS_CDN\n link.setAttribute('data-otw-driver-css', '1')\n document.head.appendChild(link)\n}\n\n// ---- Cross-page Tour Storage ----\n\n/** @internal Key used in sessionStorage for persisting active cross-page tour state */\nconst CROSS_PAGE_KEY = 'otw_active_tour'\n\n/**\n * State persisted to sessionStorage so a tour can survive page navigations.\n */\nexport interface CrossPageTourState {\n /** Task slug that is currently running */\n taskSlug: string\n /** Index of the step to resume from */\n stepIndex: number\n /** ISO timestamp when the tour started */\n startedAt: string\n}\n\n// ---- Types ----\n\n/** Configuration passed to the OnTheWay constructor */\nexport interface OnTheWayConfig {\n projectId: string\n apiUrl?: string\n /** Custom Driver.js CSS URL. Set to `false` to disable auto-injection. */\n driverCssUrl?: string | false\n onComplete?: (taskId: string) => void\n onSkip?: (taskId: string, stepIndex: number) => void\n}\n\n/** A single task returned by the server or configured locally */\nexport interface TaskConfig {\n id: string\n slug: string\n trigger: 'auto' | 'manual' | 'first-visit' | 'condition'\n steps: StepConfig[]\n targeting?: {\n urlPattern?: string\n newUsersOnly?: boolean\n /**\n * Condition callback evaluated at auto-start time.\n * Return `true` to trigger the tour.\n * Only used when `trigger === 'condition'`.\n */\n condition?: () => boolean\n }\n}\n\n/** Configuration for a single step in a tour */\nexport interface StepConfig {\n element: string\n popover: {\n title: string\n description: string\n side?: 'top' | 'bottom' | 'left' | 'right'\n }\n /**\n * Optional URL this step should be shown on.\n * If the current page does not match, the SDK will navigate to it and\n * resume the tour aftehe page loads.\n */\n url?: string\n}\n\n/** @internal Runtime state of the SDK */\nexport interface SDKState {\n loaded: boolean\n tasks: TaskConfig[]\n completedTasks: Set<string>\n}\n\nexport class OnTheWay {\n private config: OnTheWayConfig\n private state: SDKState\n private driverInstance: Driver | null = null\n private visitorId: string\n /** Condition functions registered via `registerCondition` */\n private conditions: Map<string, () => boolean> = new Map()\n\n constructor(config: OnTheWayConfig) {\n this.config = {\n apiUrl: '/api',\n ...config,\n }\n this.state = {\n loaded: false,\n tasks: [],\n completedTasks: new Set(),\n }\n\n // Inject driver.js CSS at runtime (unless explicitly disabled)\n if (config.driverCssUrl !== false) {\n if (config.driverCssUrl) {\n // User provided a custom CSS URL\n if (typeof document !== 'undefined' && !document.querySelector('link[data-otw-driver-css]')) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = config.driverCssUrl\n link.setAttribute('data-otw-driver-css', '1')\n document.head.appendChild(link)\n }\n } else {\n loadDriverCSS()\n }\n }\n\n this.visitorId = this.getOrCreateVisitorId()\n this.init()\n }\n\n private async init() {\n // Load completed tasks from localStorage\n this.loadCompletedTasks()\n\n // Fetch task configs\n await this.fetchTasks()\n\n this.state.loaded = true\n\n // Check for cross-page tour resume before auto-start\n if (!this.resumeCrossPageTour()) {\n // Auto-start tasks if configured\n this.handleAutoStart()\n }\n }\n\n private getOrCreateVisitorId(): string {\n if (typeof localStorage === 'undefined') return 'v_ssr'\n const key = 'otw_visitor_id'\n let id = localStorage.getItem(key)\n if (!id) {\n id = 'v_' + Math.random().toString(36).substring(2) + Date.now().toString(36)\n localStorage.setItem(key, id)\n }\n return id\n }\n\n private loadCompletedTasks() {\n if (typeof localStorage === 'undefined') return\n const key = `otw_completed_${this.config.projectId}`\n const completed = localStorage.getItem(key)\n if (completed) {\n this.state.completedTasks = new Set(JSON.parse(completed))\n }\n }\n\n private saveCompletedTask(taskId: string) {\n this.state.completedTasks.add(taskId)\n if (typeof localStorage === 'undefined') return\n const key = `otw_completed_${this.config.projectId}`\n localStorage.setItem(key, JSON.stringify([...this.state.completedTasks]))\n }\n\n private async fetchTasks() {\n try {\n const res = await fetch(`${this.config.apiUrl}/sdk/${this.config.projectId}/config`)\n if (!res.ok) throw new Error('Failed to fetch config')\n const data = await res.json()\n this.state.tasks = data.tasks || []\n } catch (error) {\n console.warn('[OnTheWay] Failed to load config:', error)\n }\n }\n\n private handleAutoStart() {\n if (typeof window === 'undefined') return\n const currentUrl = window.location.href\n\n for (const task of this.state.tasks) {\n // Skip completed tasks\n if (this.state.completedTasks.has(task.id)) continue\n\n // Check URL targeting\n if (task.targeting?.urlPattern) {\n const pattern = new RegExp(task.targeting.urlPattern)\n if (!pattern.test(currentUrl)) continue\n }\n\n // Handle trigger types\n if (task.trigger === 'auto') {\n this.start(task.slug)\n break // Only one auto task at a time\n } else if (task.trigger === 'first-visit') {\n if (typeof localStorage === 'undefined') continue\n const visitKey = `otw_visited_${task.id}`\n if (!localStorage.getItem(visitKey)) {\n localStorage.setItem(visitKey, 'true')\n this.start(task.slug)\n break\n }\n } else if (task.trigger === 'condition') {\n // Check registered condition or inline condition\n const conditionFn =\n this.conditions.get(task.slug) ?? task.targeting?.condition\n if (conditionFn && conditionFn()) {\n this.start(task.slug)\n break\n }\n }\n }\n }\n\n // ---- Cross-page tour helpers ----\n\n /**\n * Save cross-page tour state so it survives navigation.\n * @internal\n */\n private saveCrossPageState(taskSlug: string, stepIndex: number) {\n if (typeof sessionStorage === 'undefined') return\n const state: CrossPageTourState = {\n taskSlug,\n stepIndex,\n startedAt: new Date().toISOString(),\n }\n sessionStorage.setItem(CROSS_PAGE_KEY, JSON.stringify(state))\n }\n\n /**\n * Clear cross-page tour state.\n * @internal\n */\n private clearCrossPageState() {\n if (typeof sessionStorage === 'undefined') return\n sessionStorage.removeItem(CROSS_PAGE_KEY)\n }\n\n /**\n * Attempt to resume a cross-page tour from sessionStorage.\n * @returns `true` if a tour was resumed, `false` otherwise.\n * @internal\n */\n private resumeCrossPageTour(): boolean {\n if (typeof sessionStorage === 'undefined') return false\n const raw = sessionStorage.getItem(CROSS_PAGE_KEY)\n if (!raw) return false\n\n try {\n const state: CrossPageTourState = JSON.parse(raw)\n const task = this.state.tasks.find(t => t.slug === state.taskSlug)\n if (!task) {\n this.clearCrossPageState()\n return false\n }\n // Resume from the saved step index\n this.startAtStep(task, state.stepIndex)\n return true\n } catch {\n this.clearCrossPageState()\n return false\n }\n }\n\n /**\n * Check whether a step's URL matches the current page.\n * @internal\n */\n private stepUrlMatches(stepUrl: string | undefined): boolean {\n if (!stepUrl) return true // no url constraint = always matches\n if (typeof window === 'undefined') return true\n const current = window.location.pathname + window.location.search\n // Support both full URLs and path-only\n try {\n const parsed = new URL(stepUrl, window.location.origin)\n return (\n parsed.pathname === window.location.pathname &&\n parsed.search === window.location.search\n )\n } catch {\n return current === stepUrl\n }\n }\n\n /**\n * Start a task at a specific step index (used for cross-page resume).\n * @internal\n */\n private startAtStep(task: TaskConfig, fromIndex: number) {\n const allSteps = task.steps\n\n // Check if the target step's URL matches the current page\n const targetStep = allSteps[fromIndex]\n if (targetStep?.url && !this.stepUrlMatches(targetStep.url)) {\n // Need to navigate — save state and redirect\n this.saveCrossPageState(task.slug, fromIndex)\n window.location.href = targetStep.url\n return\n }\n\n // Build Driver.js steps from fromIndex onward\n const steps: DriveStep[] = allSteps.slice(fromIndex).map(step => ({\n element: step.element,\n popover: {\n title: step.popover.title,\n description: step.popover.description,\n side: step.popover.side,\n },\n }))\n\n const totalSteps = allSteps.length\n\n this.driverInstance = driver({\n showProgress: true,\n steps,\n onNextClick: () => {\n if (!this.driverInstance) return\n const relativeIndex = this.driverInstance.getActiveIndex() ?? 0\n const absoluteIndex = fromIndex + relativeIndex + 1\n\n // Check if next step requires a different page\n if (absoluteIndex < allSteps.length) {\n const nextStep = allSteps[absoluteIndex]\n if (nextStep.url && !this.stepUrlMatches(nextStep.url)) {\n this.saveCrossPageState(task.slug, absoluteIndex)\n this.driverInstance.destroy()\n window.location.href = nextStep.url\n return\n }\n }\n\n this.driverInstance.moveNext()\n },\n onDestroyStarted: () => {\n if (this.driverInstance?.hasNextStep()) {\n // Skipped\n const relativeIndex = this.driverInstance.getActiveIndex() || 0\n const currentIndex = fromIndex + relativeIndex\n this.config.onSkip?.(task.id, currentIndex)\n this.trackCompletion(task.id, currentIndex, totalSteps, false)\n this.clearCrossPageState()\n } else {\n // Completed\n this.saveCompletedTask(task.id)\n this.config.onComplete?.(task.id)\n this.trackCompletion(task.id, totalSteps, totalSteps, true)\n this.clearCrossPageState()\n }\n this.driverInstance?.destroy()\n },\n })\n\n this.driverInstance.drive()\n }\n\n /**\n * Register a condition function for a task slug.\n * When the task trigger is `'condition'`, this function will be evaluated\n * during auto-start to decide whether to show the tour.\n *\n * @param slug - Task slug to attach the condition to\n * @param fn - Predicate that returns `true` to trigger the tour\n */\n public registerCondition(slug: string, fn: () => boolean) {\n this.conditions.set(slug, fn)\n }\n\n /**\n * Re-evaluate conditions and auto-start eligible tasks.\n * Call this when application state changes (e.g. data loaded) and\n * condition-based tours should be re-checked.\n */\n public checkConditions() {\n this.handleAutoStart()\n }\n\n /**\n * Start a task by slug or ID\n */\n public start(slugOrId: string) {\n const task = this.state.tasks.find(t => t.slug === slugOrId || t.id === slugOrId)\n if (!task) {\n console.warn(`[OnTheWay] Task not found: ${slugOrId}`)\n return\n }\n\n this.startAtStep(task, 0)\n }\n\n /**\n * Reset a task (allow it to show again)\n */\n public reset(slugOrId: string) {\n const task = this.state.tasks.find(t => t.slug === slugOrId || t.id === slugOrId)\n if (task) {\n this.state.completedTasks.delete(task.id)\n if (typeof localStorage !== 'undefined') {\n const key = `otw_completed_${this.config.projectId}`\n localStorage.setItem(key, JSON.stringify([...this.state.completedTasks]))\n // Also reset first-visit flag\n localStorage.removeItem(`otw_visited_${task.id}`)\n }\n }\n }\n\n /**\n * Reset all tasks for this project\n */\n public resetAll() {\n this.state.completedTasks.clear()\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(`otw_completed_${this.config.projectId}`)\n this.state.tasks.forEach(task => {\n localStorage.removeItem(`otw_visited_${task.id}`)\n })\n }\n }\n\n private async trackCompletion(\n taskId: string,\n stepsCompleted: number,\n totalSteps: number,\n completed: boolean,\n ) {\n try {\n await fetch(`${this.config.apiUrl}/sdk/track`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n task_id: taskId,\n visitor_id: this.visitorId,\n steps_completed: stepsCompleted,\n total_steps: totalSteps,\n completed,\n }),\n })\n } catch {\n // Silent fail for analytics\n }\n }\n\n /**\n * Get the project ID this SDK instance was configured with.\n */\n public getProjectId(): string {\n return this.config.projectId\n }\n\n /**\n * Check if SDK is loaded\n */\n public isReady(): boolean {\n return this.state.loaded\n }\n\n /**\n * Check if a task has been completed\n */\n public isTaskCompleted(slugOrId: string): boolean {\n const task = this.state.tasks.find(t => t.slug === slugOrId || t.id === slugOrId)\n if (!task) return false\n return this.state.completedTasks.has(task.id)\n }\n\n /**\n * Get list of available tasks\n */\n public getTasks(): TaskConfig[] {\n return [...this.state.tasks]\n }\n\n /**\n * Get the set of completed task IDs\n */\n public getCompletedTaskIds(): Set<string> {\n return new Set(this.state.completedTasks)\n }\n\n /**\n * Get pending cross-page tour state, if any.\n * Useful for the React provider to check on mount.\n */\n public static getCrossPageState(): CrossPageTourState | null {\n if (typeof sessionStorage === 'undefined') return null\n const raw = sessionStorage.getItem(CROSS_PAGE_KEY)\n if (!raw) return null\n try {\n return JSON.parse(raw)\n } catch {\n return null\n }\n }\n}\n\nexport default OnTheWay\n"]}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var chunkNRUQU5AR_cjs = require('./chunk-NRUQU5AR.cjs');
|
|
6
|
+
require('./chunk-UE3T6TSM.cjs');
|
|
7
|
+
var chunk254YHUN3_cjs = require('./chunk-254YHUN3.cjs');
|
|
8
|
+
var react = require('react');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
|
|
11
|
+
function HelpMenu({
|
|
12
|
+
label = "?",
|
|
13
|
+
title = "Help & Guides",
|
|
14
|
+
position = "bottom-right",
|
|
15
|
+
className = "",
|
|
16
|
+
buttonClassName = ""
|
|
17
|
+
}) {
|
|
18
|
+
var _a;
|
|
19
|
+
const { otw, ready, start } = chunkNRUQU5AR_cjs.useOnTheWay();
|
|
20
|
+
const [open, setOpen] = react.useState(false);
|
|
21
|
+
const menuRef = react.useRef(null);
|
|
22
|
+
const tasks = ready ? (_a = otw == null ? void 0 : otw.getTasks()) != null ? _a : [] : [];
|
|
23
|
+
react.useEffect(() => {
|
|
24
|
+
const handleClick = (e) => {
|
|
25
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
26
|
+
setOpen(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (open) document.addEventListener("mousedown", handleClick);
|
|
30
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
31
|
+
}, [open]);
|
|
32
|
+
const positionStyles = {
|
|
33
|
+
"bottom-right": { position: "fixed", bottom: 24, right: 24 },
|
|
34
|
+
"bottom-left": { position: "fixed", bottom: 24, left: 24 },
|
|
35
|
+
"top-right": { position: "fixed", top: 24, right: 24 },
|
|
36
|
+
"top-left": { position: "fixed", top: 24, left: 24 }
|
|
37
|
+
};
|
|
38
|
+
const menuPosition = {
|
|
39
|
+
"bottom-right": { bottom: "100%", right: 0, marginBottom: 8 },
|
|
40
|
+
"bottom-left": { bottom: "100%", left: 0, marginBottom: 8 },
|
|
41
|
+
"top-right": { top: "100%", right: 0, marginTop: 8 },
|
|
42
|
+
"top-left": { top: "100%", left: 0, marginTop: 8 }
|
|
43
|
+
};
|
|
44
|
+
const triggerLabels = {
|
|
45
|
+
auto: "\u{1F504}",
|
|
46
|
+
manual: "\u{1F446}",
|
|
47
|
+
"first-visit": "\u{1F44B}"
|
|
48
|
+
};
|
|
49
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
50
|
+
"div",
|
|
51
|
+
{
|
|
52
|
+
ref: menuRef,
|
|
53
|
+
style: chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({}, positionStyles[position]), { zIndex: 2147483640 }),
|
|
54
|
+
className,
|
|
55
|
+
children: [
|
|
56
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
style: chunk254YHUN3_cjs.__spreadProps(chunk254YHUN3_cjs.__spreadValues({
|
|
60
|
+
position: "absolute"
|
|
61
|
+
}, menuPosition[position]), {
|
|
62
|
+
width: 280,
|
|
63
|
+
background: "#fff",
|
|
64
|
+
borderRadius: 12,
|
|
65
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.15)",
|
|
66
|
+
border: "1px solid #e5e7eb",
|
|
67
|
+
overflow: "hidden",
|
|
68
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
69
|
+
}),
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
+
"div",
|
|
73
|
+
{
|
|
74
|
+
style: {
|
|
75
|
+
padding: "14px 16px",
|
|
76
|
+
borderBottom: "1px solid #f3f4f6",
|
|
77
|
+
fontWeight: 600,
|
|
78
|
+
fontSize: 14,
|
|
79
|
+
color: "#111"
|
|
80
|
+
},
|
|
81
|
+
children: title
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { maxHeight: 320, overflowY: "auto" }, children: tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
85
|
+
"div",
|
|
86
|
+
{
|
|
87
|
+
style: {
|
|
88
|
+
padding: "24px 16px",
|
|
89
|
+
textAlign: "center",
|
|
90
|
+
color: "#9ca3af",
|
|
91
|
+
fontSize: 13
|
|
92
|
+
},
|
|
93
|
+
children: ready ? "No guides available" : "Loading..."
|
|
94
|
+
}
|
|
95
|
+
) : tasks.map((task) => {
|
|
96
|
+
var _a2;
|
|
97
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
98
|
+
"button",
|
|
99
|
+
{
|
|
100
|
+
onClick: () => {
|
|
101
|
+
start(task.slug);
|
|
102
|
+
setOpen(false);
|
|
103
|
+
},
|
|
104
|
+
style: {
|
|
105
|
+
display: "flex",
|
|
106
|
+
alignItems: "center",
|
|
107
|
+
gap: 10,
|
|
108
|
+
width: "100%",
|
|
109
|
+
padding: "12px 16px",
|
|
110
|
+
border: "none",
|
|
111
|
+
background: "none",
|
|
112
|
+
cursor: "pointer",
|
|
113
|
+
textAlign: "left",
|
|
114
|
+
fontSize: 14,
|
|
115
|
+
color: "#374151",
|
|
116
|
+
borderBottom: "1px solid #f9fafb",
|
|
117
|
+
transition: "background 0.15s"
|
|
118
|
+
},
|
|
119
|
+
onMouseEnter: (e) => e.currentTarget.style.background = "#f9fafb",
|
|
120
|
+
onMouseLeave: (e) => e.currentTarget.style.background = "none",
|
|
121
|
+
children: [
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 16 }, children: (_a2 = triggerLabels[task.trigger]) != null ? _a2 : "\u{1F4D6}" }),
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500 }, children: task.slug.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }),
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "#9ca3af" }, children: [
|
|
126
|
+
task.steps.length,
|
|
127
|
+
" steps"
|
|
128
|
+
] })
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#d1d5db", fontSize: 18 }, children: "\u203A" })
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
task.id
|
|
134
|
+
);
|
|
135
|
+
}) })
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
+
"button",
|
|
141
|
+
{
|
|
142
|
+
onClick: () => setOpen(!open),
|
|
143
|
+
className: buttonClassName,
|
|
144
|
+
style: buttonClassName ? void 0 : {
|
|
145
|
+
width: 48,
|
|
146
|
+
height: 48,
|
|
147
|
+
borderRadius: "50%",
|
|
148
|
+
border: "none",
|
|
149
|
+
background: "#111",
|
|
150
|
+
color: "#fff",
|
|
151
|
+
fontSize: 20,
|
|
152
|
+
fontWeight: 700,
|
|
153
|
+
cursor: "pointer",
|
|
154
|
+
boxShadow: "0 4px 14px rgba(0,0,0,0.2)",
|
|
155
|
+
display: "flex",
|
|
156
|
+
alignItems: "center",
|
|
157
|
+
justifyContent: "center",
|
|
158
|
+
transition: "transform 0.2s, box-shadow 0.2s"
|
|
159
|
+
},
|
|
160
|
+
onMouseEnter: (e) => {
|
|
161
|
+
if (!buttonClassName) {
|
|
162
|
+
e.currentTarget.style.transform = "scale(1.1)";
|
|
163
|
+
e.currentTarget.style.boxShadow = "0 6px 20px rgba(0,0,0,0.25)";
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
onMouseLeave: (e) => {
|
|
167
|
+
if (!buttonClassName) {
|
|
168
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
169
|
+
e.currentTarget.style.boxShadow = "0 4px 14px rgba(0,0,0,0.2)";
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"aria-label": "Help menu",
|
|
173
|
+
children: label
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
function HelpTrigger({
|
|
181
|
+
taskSlug,
|
|
182
|
+
children,
|
|
183
|
+
className,
|
|
184
|
+
style
|
|
185
|
+
}) {
|
|
186
|
+
const { start, ready } = chunkNRUQU5AR_cjs.useOnTheWay();
|
|
187
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
188
|
+
"span",
|
|
189
|
+
{
|
|
190
|
+
onClick: () => ready && start(taskSlug),
|
|
191
|
+
className,
|
|
192
|
+
style: chunk254YHUN3_cjs.__spreadValues({ cursor: ready ? "pointer" : "default" }, style),
|
|
193
|
+
role: "button",
|
|
194
|
+
tabIndex: 0,
|
|
195
|
+
onKeyDown: (e) => {
|
|
196
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
ready && start(taskSlug);
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
children
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
var components_default = HelpMenu;
|
|
206
|
+
|
|
207
|
+
exports.HelpMenu = HelpMenu;
|
|
208
|
+
exports.HelpTrigger = HelpTrigger;
|
|
209
|
+
exports.default = components_default;
|
|
210
|
+
//# sourceMappingURL=components.cjs.map
|
|
211
|
+
//# sourceMappingURL=components.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components.tsx"],"names":["useOnTheWay","useState","useRef","useEffect","jsxs","__spreadProps","__spreadValues","jsx","_a"],"mappings":"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","file":"components.cjs","sourcesContent":["'use client'\n\nimport { useState, useRef, useEffect } from 'react'\nimport { useOnTheWay } from './react'\n\n// ---- Types ----\n\ninterface HelpMenuProps {\n /** 按钮文字,默认 \"?\" */\n label?: string | React.ReactNode\n /** 菜单标题 */\n title?: string\n /** 自定义位置 */\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\n /** 自定义样式类 */\n className?: string\n /** 自定义按钮样式类 */\n buttonClassName?: string\n}\n\n/**\n * 帮助菜单组件 - 显示所有可用的 onboarding 任务\n *\n * @example\n * ```tsx\n * // 基础用法\n * <HelpMenu />\n *\n * // 自定义\n * <HelpMenu label=\"Need help?\" title=\"Guides\" position=\"bottom-left\" />\n * ```\n */\nexport function HelpMenu({\n label = '?',\n title = 'Help & Guides',\n position = 'bottom-right',\n className = '',\n buttonClassName = '',\n}: HelpMenuProps) {\n const { otw, ready, start } = useOnTheWay()\n const [open, setOpen] = useState(false)\n const menuRef = useRef<HTMLDivElement>(null)\n\n const tasks = ready ? otw?.getTasks() ?? [] : []\n\n // 点击外部关闭\n useEffect(() => {\n const handleClick = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setOpen(false)\n }\n }\n if (open) document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [open])\n\n const positionStyles: Record<string, React.CSSProperties> = {\n 'bottom-right': { position: 'fixed', bottom: 24, right: 24 },\n 'bottom-left': { position: 'fixed', bottom: 24, left: 24 },\n 'top-right': { position: 'fixed', top: 24, right: 24 },\n 'top-left': { position: 'fixed', top: 24, left: 24 },\n }\n\n const menuPosition: Record<string, React.CSSProperties> = {\n 'bottom-right': { bottom: '100%', right: 0, marginBottom: 8 },\n 'bottom-left': { bottom: '100%', left: 0, marginBottom: 8 },\n 'top-right': { top: '100%', right: 0, marginTop: 8 },\n 'top-left': { top: '100%', left: 0, marginTop: 8 },\n }\n\n const triggerLabels: Record<string, string> = {\n auto: '🔄',\n manual: '👆',\n 'first-visit': '👋',\n }\n\n return (\n <div\n ref={menuRef}\n style={{ ...positionStyles[position], zIndex: 2147483640 }}\n className={className}\n >\n {/* 菜单面板 */}\n {open && (\n <div\n style={{\n position: 'absolute',\n ...menuPosition[position],\n width: 280,\n background: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.15)',\n border: '1px solid #e5e7eb',\n overflow: 'hidden',\n fontFamily: 'system-ui, -apple-system, sans-serif',\n }}\n >\n {/* Header */}\n <div\n style={{\n padding: '14px 16px',\n borderBottom: '1px solid #f3f4f6',\n fontWeight: 600,\n fontSize: 14,\n color: '#111',\n }}\n >\n {title}\n </div>\n\n {/* Task list */}\n <div style={{ maxHeight: 320, overflowY: 'auto' }}>\n {tasks.length === 0 ? (\n <div\n style={{\n padding: '24px 16px',\n textAlign: 'center',\n color: '#9ca3af',\n fontSize: 13,\n }}\n >\n {ready ? 'No guides available' : 'Loading...'}\n </div>\n ) : (\n tasks.map((task) => (\n <button\n key={task.id}\n onClick={() => {\n start(task.slug)\n setOpen(false)\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n width: '100%',\n padding: '12px 16px',\n border: 'none',\n background: 'none',\n cursor: 'pointer',\n textAlign: 'left',\n fontSize: 14,\n color: '#374151',\n borderBottom: '1px solid #f9fafb',\n transition: 'background 0.15s',\n }}\n onMouseEnter={(e) =>\n (e.currentTarget.style.background = '#f9fafb')\n }\n onMouseLeave={(e) =>\n (e.currentTarget.style.background = 'none')\n }\n >\n <span style={{ fontSize: 16 }}>\n {triggerLabels[task.trigger] ?? '📖'}\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: 500 }}>\n {task.slug\n .replace(/[-_]/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase())}\n </div>\n <div style={{ fontSize: 12, color: '#9ca3af' }}>\n {task.steps.length} steps\n </div>\n </div>\n <span style={{ color: '#d1d5db', fontSize: 18 }}>›</span>\n </button>\n ))\n )}\n </div>\n </div>\n )}\n\n {/* 触发按钮 */}\n <button\n onClick={() => setOpen(!open)}\n className={buttonClassName}\n style={\n buttonClassName\n ? undefined\n : {\n width: 48,\n height: 48,\n borderRadius: '50%',\n border: 'none',\n background: '#111',\n color: '#fff',\n fontSize: 20,\n fontWeight: 700,\n cursor: 'pointer',\n boxShadow: '0 4px 14px rgba(0,0,0,0.2)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'transform 0.2s, box-shadow 0.2s',\n }\n }\n onMouseEnter={(e) => {\n if (!buttonClassName) {\n e.currentTarget.style.transform = 'scale(1.1)'\n e.currentTarget.style.boxShadow = '0 6px 20px rgba(0,0,0,0.25)'\n }\n }}\n onMouseLeave={(e) => {\n if (!buttonClassName) {\n e.currentTarget.style.transform = 'scale(1)'\n e.currentTarget.style.boxShadow = '0 4px 14px rgba(0,0,0,0.2)'\n }\n }}\n aria-label=\"Help menu\"\n >\n {label}\n </button>\n </div>\n )\n}\n\n// ---- Inline trigger ----\n\ninterface HelpTriggerProps {\n /** 要触发的任务 slug */\n taskSlug: string\n children: React.ReactNode\n className?: string\n style?: React.CSSProperties\n}\n\n/**\n * 内联触发按钮 - 包裹任何元素,点击启动指定任务\n *\n * @example\n * ```tsx\n * <HelpTrigger taskSlug=\"welcome\">\n * <button>Show Tour</button>\n * </HelpTrigger>\n *\n * <HelpTrigger taskSlug=\"settings-guide\">\n * <span className=\"text-blue-500 cursor-pointer\">Learn more</span>\n * </HelpTrigger>\n * ```\n */\nexport function HelpTrigger({\n taskSlug,\n children,\n className,\n style,\n}: HelpTriggerProps) {\n const { start, ready } = useOnTheWay()\n\n return (\n <span\n onClick={() => ready && start(taskSlug)}\n className={className}\n style={{ cursor: ready ? 'pointer' : 'default', ...style }}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n ready && start(taskSlug)\n }\n }}\n >\n {children}\n </span>\n )\n}\n\nexport default HelpMenu\n"]}
|