@quantabit/onboarding-sdk 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.
@@ -0,0 +1,719 @@
1
+ import React, { useState, useCallback, useEffect } from 'react';
2
+ import { BaseApiClient } from '@quantabit/sdk-config';
3
+
4
+ function OnboardingFlow({
5
+ steps = [],
6
+ onComplete,
7
+ onSkip,
8
+ className = ''
9
+ }) {
10
+ const [current, setCurrent] = useState(0);
11
+ const step = steps[current];
12
+ if (!step) return null;
13
+ return /*#__PURE__*/React.createElement("div", {
14
+ className: `qob-flow ${className}`,
15
+ style: {
16
+ textAlign: 'center',
17
+ padding: 40,
18
+ maxWidth: 480,
19
+ margin: '0 auto'
20
+ }
21
+ }, step.image && /*#__PURE__*/React.createElement("img", {
22
+ src: step.image,
23
+ alt: "",
24
+ style: {
25
+ maxWidth: 240,
26
+ height: 'auto',
27
+ marginBottom: 24
28
+ }
29
+ }), step.icon && /*#__PURE__*/React.createElement("div", {
30
+ style: {
31
+ fontSize: 56,
32
+ marginBottom: 16
33
+ }
34
+ }, step.icon), /*#__PURE__*/React.createElement("h2", {
35
+ style: {
36
+ fontSize: 24,
37
+ fontWeight: 800,
38
+ color: '#18181b',
39
+ margin: '0 0 8px'
40
+ }
41
+ }, step.title), /*#__PURE__*/React.createElement("p", {
42
+ style: {
43
+ fontSize: 14,
44
+ color: '#71717a',
45
+ lineHeight: 1.6,
46
+ margin: '0 0 32px'
47
+ }
48
+ }, step.description), /*#__PURE__*/React.createElement("div", {
49
+ style: {
50
+ display: 'flex',
51
+ gap: 6,
52
+ justifyContent: 'center',
53
+ marginBottom: 24
54
+ }
55
+ }, steps.map((_, i) => /*#__PURE__*/React.createElement("div", {
56
+ key: i,
57
+ style: {
58
+ width: i === current ? 24 : 8,
59
+ height: 8,
60
+ borderRadius: 4,
61
+ background: i === current ? '#3b82f6' : i < current ? '#93c5fd' : '#e4e4e7',
62
+ transition: 'all 0.3s'
63
+ }
64
+ }))), /*#__PURE__*/React.createElement("div", {
65
+ style: {
66
+ display: 'flex',
67
+ gap: 12,
68
+ justifyContent: 'center'
69
+ }
70
+ }, onSkip && current < steps.length - 1 && /*#__PURE__*/React.createElement("button", {
71
+ onClick: onSkip,
72
+ style: {
73
+ padding: '10px 24px',
74
+ borderRadius: 10,
75
+ border: 'none',
76
+ background: 'transparent',
77
+ color: '#71717a',
78
+ fontSize: 14,
79
+ cursor: 'pointer'
80
+ }
81
+ }, "Skip"), /*#__PURE__*/React.createElement("button", {
82
+ onClick: () => {
83
+ if (current < steps.length - 1) setCurrent(c => c + 1);else onComplete?.();
84
+ },
85
+ style: {
86
+ padding: '10px 32px',
87
+ borderRadius: 10,
88
+ border: 'none',
89
+ background: '#3b82f6',
90
+ color: '#fff',
91
+ fontSize: 14,
92
+ fontWeight: 700,
93
+ cursor: 'pointer',
94
+ transition: 'all 0.2s'
95
+ }
96
+ }, current === steps.length - 1 ? 'Get Started' : 'Next')));
97
+ }
98
+
99
+ function SpotlightTour({
100
+ steps = [],
101
+ active = false,
102
+ onComplete,
103
+ onSkip
104
+ }) {
105
+ const [current, setCurrent] = useState(0);
106
+ const step = steps[current];
107
+ const [rect, setRect] = useState(null);
108
+ const updateRect = useCallback(() => {
109
+ if (!step?.target) return;
110
+ const el = document.querySelector(step.target);
111
+ if (el) {
112
+ const r = el.getBoundingClientRect();
113
+ setRect({
114
+ top: r.top - 4,
115
+ left: r.left - 4,
116
+ width: r.width + 8,
117
+ height: r.height + 8
118
+ });
119
+ }
120
+ }, [step]);
121
+ useEffect(() => {
122
+ if (active) {
123
+ updateRect();
124
+ window.addEventListener('resize', updateRect);
125
+ return () => window.removeEventListener('resize', updateRect);
126
+ }
127
+ }, [active, updateRect]);
128
+ if (!active || !step || !rect) return null;
129
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
130
+ style: {
131
+ position: 'fixed',
132
+ inset: 0,
133
+ zIndex: 99998
134
+ }
135
+ }, /*#__PURE__*/React.createElement("svg", {
136
+ width: "100%",
137
+ height: "100%",
138
+ style: {
139
+ position: 'absolute'
140
+ }
141
+ }, /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("mask", {
142
+ id: "qob-mask"
143
+ }, /*#__PURE__*/React.createElement("rect", {
144
+ width: "100%",
145
+ height: "100%",
146
+ fill: "white"
147
+ }), /*#__PURE__*/React.createElement("rect", {
148
+ x: rect.left,
149
+ y: rect.top,
150
+ width: rect.width,
151
+ height: rect.height,
152
+ rx: "8",
153
+ fill: "black"
154
+ }))), /*#__PURE__*/React.createElement("rect", {
155
+ width: "100%",
156
+ height: "100%",
157
+ fill: "rgba(0,0,0,0.5)",
158
+ mask: "url(#qob-mask)"
159
+ }))), /*#__PURE__*/React.createElement("div", {
160
+ style: {
161
+ position: 'fixed',
162
+ zIndex: 99999,
163
+ top: rect.top + rect.height + 12,
164
+ left: Math.max(16, Math.min(rect.left, window.innerWidth - 320)),
165
+ width: 300,
166
+ padding: 20,
167
+ borderRadius: 14,
168
+ background: '#fff',
169
+ boxShadow: '0 16px 48px rgba(0,0,0,0.15)',
170
+ animation: 'qob-pop 0.2s ease-out'
171
+ }
172
+ }, /*#__PURE__*/React.createElement("div", {
173
+ style: {
174
+ fontSize: 12,
175
+ color: '#a1a1aa',
176
+ marginBottom: 4
177
+ }
178
+ }, "Step ", current + 1, " of ", steps.length), /*#__PURE__*/React.createElement("h4", {
179
+ style: {
180
+ fontSize: 15,
181
+ fontWeight: 700,
182
+ color: '#18181b',
183
+ margin: '0 0 6px'
184
+ }
185
+ }, step.title), /*#__PURE__*/React.createElement("p", {
186
+ style: {
187
+ fontSize: 13,
188
+ color: '#71717a',
189
+ margin: '0 0 16px',
190
+ lineHeight: 1.5
191
+ }
192
+ }, step.description), /*#__PURE__*/React.createElement("div", {
193
+ style: {
194
+ display: 'flex',
195
+ justifyContent: 'space-between'
196
+ }
197
+ }, onSkip && /*#__PURE__*/React.createElement("button", {
198
+ onClick: onSkip,
199
+ style: {
200
+ border: 'none',
201
+ background: 'none',
202
+ color: '#a1a1aa',
203
+ fontSize: 12,
204
+ cursor: 'pointer'
205
+ }
206
+ }, "Skip tour"), /*#__PURE__*/React.createElement("button", {
207
+ onClick: () => {
208
+ if (current < steps.length - 1) setCurrent(c => c + 1);else {
209
+ onComplete?.();
210
+ setCurrent(0);
211
+ }
212
+ },
213
+ style: {
214
+ padding: '6px 16px',
215
+ borderRadius: 6,
216
+ border: 'none',
217
+ background: '#3b82f6',
218
+ color: '#fff',
219
+ fontSize: 12,
220
+ fontWeight: 600,
221
+ cursor: 'pointer',
222
+ marginLeft: 'auto'
223
+ }
224
+ }, current === steps.length - 1 ? 'Finish' : 'Next'))));
225
+ }
226
+
227
+ function FeatureHighlight({
228
+ title,
229
+ description,
230
+ isNew = false,
231
+ onDismiss,
232
+ position = 'top-right',
233
+ className = ''
234
+ }) {
235
+ const [visible, setVisible] = useState(true);
236
+ if (!visible) return null;
237
+ const dismiss = () => {
238
+ setVisible(false);
239
+ onDismiss?.();
240
+ };
241
+ return /*#__PURE__*/React.createElement("div", {
242
+ className: `qob-highlight ${className}`,
243
+ style: {
244
+ position: 'relative',
245
+ display: 'inline-block'
246
+ }
247
+ }, /*#__PURE__*/React.createElement("div", {
248
+ style: {
249
+ position: 'absolute',
250
+ ...(position === 'top-right' ? {
251
+ top: -8,
252
+ right: -8
253
+ } : position === 'top-left' ? {
254
+ top: -8,
255
+ left: -8
256
+ } : {
257
+ bottom: -8,
258
+ right: -8
259
+ }),
260
+ zIndex: 10
261
+ }
262
+ }, isNew && /*#__PURE__*/React.createElement("span", {
263
+ style: {
264
+ background: '#ef4444',
265
+ color: '#fff',
266
+ fontSize: 10,
267
+ fontWeight: 700,
268
+ padding: '1px 6px',
269
+ borderRadius: 4,
270
+ animation: 'qob-pulse 2s infinite'
271
+ }
272
+ }, "NEW")), /*#__PURE__*/React.createElement("div", {
273
+ className: "qob-tooltip",
274
+ style: {
275
+ position: 'absolute',
276
+ top: 'calc(100% + 8px)',
277
+ left: '50%',
278
+ transform: 'translateX(-50%)',
279
+ padding: '12px 16px',
280
+ borderRadius: 10,
281
+ background: '#18181b',
282
+ color: '#fff',
283
+ fontSize: 12,
284
+ width: 200,
285
+ zIndex: 100,
286
+ boxShadow: '0 8px 24px rgba(0,0,0,0.2)',
287
+ animation: 'qob-pop 0.2s ease-out'
288
+ }
289
+ }, /*#__PURE__*/React.createElement("div", {
290
+ style: {
291
+ fontWeight: 600,
292
+ marginBottom: 4
293
+ }
294
+ }, title), /*#__PURE__*/React.createElement("div", {
295
+ style: {
296
+ opacity: 0.8,
297
+ lineHeight: 1.4
298
+ }
299
+ }, description), /*#__PURE__*/React.createElement("button", {
300
+ onClick: dismiss,
301
+ style: {
302
+ border: 'none',
303
+ background: 'rgba(255,255,255,0.15)',
304
+ color: '#fff',
305
+ padding: '3px 10px',
306
+ borderRadius: 4,
307
+ fontSize: 11,
308
+ cursor: 'pointer',
309
+ marginTop: 8
310
+ }
311
+ }, "Got it")));
312
+ }
313
+
314
+ function WelcomeScreen({
315
+ title = 'Welcome!',
316
+ subtitle,
317
+ features = [],
318
+ cta = 'Get Started',
319
+ onAction,
320
+ logo,
321
+ className = ''
322
+ }) {
323
+ return /*#__PURE__*/React.createElement("div", {
324
+ className: `qob-welcome ${className}`,
325
+ style: {
326
+ textAlign: 'center',
327
+ padding: '48px 32px',
328
+ maxWidth: 560,
329
+ margin: '0 auto'
330
+ }
331
+ }, logo && /*#__PURE__*/React.createElement("div", {
332
+ style: {
333
+ marginBottom: 20
334
+ }
335
+ }, typeof logo === 'string' ? /*#__PURE__*/React.createElement("img", {
336
+ src: logo,
337
+ alt: "",
338
+ style: {
339
+ height: 48
340
+ }
341
+ }) : logo), /*#__PURE__*/React.createElement("h1", {
342
+ style: {
343
+ fontSize: 32,
344
+ fontWeight: 800,
345
+ color: '#18181b',
346
+ margin: '0 0 8px'
347
+ }
348
+ }, title), subtitle && /*#__PURE__*/React.createElement("p", {
349
+ style: {
350
+ fontSize: 16,
351
+ color: '#71717a',
352
+ margin: '0 0 32px',
353
+ lineHeight: 1.6
354
+ }
355
+ }, subtitle), features.length > 0 && /*#__PURE__*/React.createElement("div", {
356
+ style: {
357
+ display: 'grid',
358
+ gridTemplateColumns: 'repeat(auto-fit,minmax(140px,1fr))',
359
+ gap: 16,
360
+ marginBottom: 32,
361
+ textAlign: 'center'
362
+ }
363
+ }, features.map((f, i) => /*#__PURE__*/React.createElement("div", {
364
+ key: i,
365
+ style: {
366
+ padding: 16,
367
+ borderRadius: 12,
368
+ border: '1px solid #f4f4f5'
369
+ }
370
+ }, /*#__PURE__*/React.createElement("div", {
371
+ style: {
372
+ fontSize: 28,
373
+ marginBottom: 8
374
+ }
375
+ }, f.icon), /*#__PURE__*/React.createElement("div", {
376
+ style: {
377
+ fontSize: 13,
378
+ fontWeight: 600,
379
+ color: '#18181b'
380
+ }
381
+ }, f.title), f.description && /*#__PURE__*/React.createElement("div", {
382
+ style: {
383
+ fontSize: 11,
384
+ color: '#71717a',
385
+ marginTop: 4
386
+ }
387
+ }, f.description)))), /*#__PURE__*/React.createElement("button", {
388
+ onClick: onAction,
389
+ style: {
390
+ padding: '14px 40px',
391
+ borderRadius: 12,
392
+ border: 'none',
393
+ background: '#3b82f6',
394
+ color: '#fff',
395
+ fontSize: 16,
396
+ fontWeight: 700,
397
+ cursor: 'pointer',
398
+ transition: 'all 0.2s'
399
+ }
400
+ }, cta));
401
+ }
402
+
403
+ const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
404
+ const messages = {
405
+ en: {
406
+ 'ob.next': 'Next',
407
+ 'ob.skip': 'Skip',
408
+ 'ob.done': 'Get Started',
409
+ 'ob.welcome': 'Welcome!'
410
+ },
411
+ zh: {
412
+ 'ob.next': '下一步',
413
+ 'ob.skip': '跳过',
414
+ 'ob.done': '开始使用',
415
+ 'ob.welcome': '欢迎!'
416
+ },
417
+ ja: {
418
+ 'ob.next': '次へ',
419
+ 'ob.skip': 'スキップ',
420
+ 'ob.done': 'はじめる',
421
+ 'ob.welcome': 'ようこそ!'
422
+ },
423
+ ko: {
424
+ 'ob.next': '다음',
425
+ 'ob.skip': '건너뛰기',
426
+ 'ob.done': '시작하기',
427
+ 'ob.welcome': '환영합니다!'
428
+ }
429
+ };
430
+ let currentLang = 'en';
431
+ function setLanguage(l) {
432
+ if (SUPPORTED_LANGUAGES.includes(l)) currentLang = l;
433
+ }
434
+ function getLanguage() {
435
+ return currentLang;
436
+ }
437
+ function t(k, params = {}) {
438
+ let text = messages[currentLang]?.[k] || messages.en?.[k] || k;
439
+ Object.entries(params).forEach(([key, val]) => {
440
+ text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), val);
441
+ });
442
+ return text;
443
+ }
444
+
445
+ function Tooltip({
446
+ title,
447
+ content,
448
+ placement = 'bottom',
449
+ onNext,
450
+ onSkip,
451
+ isLast
452
+ }) {
453
+ return /*#__PURE__*/React.createElement("div", {
454
+ className: `onboarding-tooltip placement-${placement}`
455
+ }, /*#__PURE__*/React.createElement("h4", null, title), /*#__PURE__*/React.createElement("p", null, content), /*#__PURE__*/React.createElement("div", {
456
+ className: "onboarding-tooltip-actions"
457
+ }, /*#__PURE__*/React.createElement("button", {
458
+ className: "onboarding-btn text",
459
+ onClick: onSkip
460
+ }, t('skip')), /*#__PURE__*/React.createElement("button", {
461
+ className: "onboarding-btn primary",
462
+ onClick: onNext
463
+ }, isLast ? t('done') : t('next'))));
464
+ }
465
+ function CoachMark({
466
+ target,
467
+ children
468
+ }) {
469
+ return /*#__PURE__*/React.createElement("div", {
470
+ className: "onboarding-coach-mark"
471
+ }, /*#__PURE__*/React.createElement("div", {
472
+ className: "onboarding-spotlight"
473
+ }), children);
474
+ }
475
+ function StepIndicator({
476
+ total,
477
+ current
478
+ }) {
479
+ return /*#__PURE__*/React.createElement("div", {
480
+ className: "onboarding-step-indicator"
481
+ }, Array.from({
482
+ length: total
483
+ }, (_, i) => /*#__PURE__*/React.createElement("span", {
484
+ key: i,
485
+ className: `step-dot ${i === current ? 'active' : ''} ${i < current ? 'completed' : ''}`
486
+ })));
487
+ }
488
+ function TutorialOverlay({
489
+ tutorial,
490
+ currentStep,
491
+ onNext,
492
+ onPrev,
493
+ onSkip
494
+ }) {
495
+ if (!tutorial) return null;
496
+ const step = tutorial.steps[currentStep];
497
+ const isFirst = currentStep === 0;
498
+ const isLast = currentStep === tutorial.steps.length - 1;
499
+ return /*#__PURE__*/React.createElement("div", {
500
+ className: "onboarding-overlay"
501
+ }, /*#__PURE__*/React.createElement("div", {
502
+ className: "onboarding-modal"
503
+ }, /*#__PURE__*/React.createElement("h3", null, step.title), /*#__PURE__*/React.createElement("p", null, step.content), /*#__PURE__*/React.createElement(StepIndicator, {
504
+ total: tutorial.steps.length,
505
+ current: currentStep
506
+ }), /*#__PURE__*/React.createElement("div", {
507
+ className: "onboarding-modal-actions"
508
+ }, !isFirst && /*#__PURE__*/React.createElement("button", {
509
+ className: "onboarding-btn secondary",
510
+ onClick: onPrev
511
+ }, t('prev')), /*#__PURE__*/React.createElement("button", {
512
+ className: "onboarding-btn text",
513
+ onClick: onSkip
514
+ }, t('skip')), /*#__PURE__*/React.createElement("button", {
515
+ className: "onboarding-btn primary",
516
+ onClick: onNext
517
+ }, isLast ? t('done') : t('next')))));
518
+ }
519
+
520
+ /**
521
+ * Onboarding SDK - 类型定义
522
+ */
523
+
524
+ const StepType = {
525
+ HIGHLIGHT: 'highlight',
526
+ MODAL: 'modal',
527
+ TOOLTIP: 'tooltip',
528
+ COACH_MARK: 'coach_mark'
529
+ };
530
+ const TutorialStatus = {
531
+ NOT_STARTED: 'not_started',
532
+ IN_PROGRESS: 'in_progress',
533
+ COMPLETED: 'completed',
534
+ SKIPPED: 'skipped'
535
+ };
536
+ function createDefaultStep() {
537
+ return {
538
+ id: '',
539
+ type: StepType.TOOLTIP,
540
+ title: '',
541
+ content: '',
542
+ target: null,
543
+ placement: 'bottom',
544
+ order: 0,
545
+ action: null
546
+ };
547
+ }
548
+ function createDefaultTutorial() {
549
+ return {
550
+ id: '',
551
+ name: '',
552
+ description: '',
553
+ steps: [],
554
+ status: TutorialStatus.NOT_STARTED,
555
+ currentStep: 0
556
+ };
557
+ }
558
+
559
+ /**
560
+ * Onboarding SDK - API 客户端
561
+ * 新手引导系统后端接口封装
562
+ *
563
+ * 使用 BaseApiClient 基类,代码从 ~100 行简化到 ~70 行
564
+ */
565
+
566
+
567
+ /**
568
+ * 新手引导 API 客户端
569
+ */
570
+ class OnboardingApiClient extends BaseApiClient {
571
+ constructor(config = {}) {
572
+ // 只需传入模块路径,所有配置自动处理
573
+ super('/onboarding', config);
574
+ }
575
+
576
+ // ============ 教程管理 ============
577
+
578
+ /**
579
+ * 获取教程列表
580
+ */
581
+ async getTutorials() {
582
+ return this.get('/tutorials');
583
+ }
584
+
585
+ /**
586
+ * 获取教程详情
587
+ * @param {string} tutorialId - 教程 ID
588
+ */
589
+ async getTutorial(tutorialId) {
590
+ return this.get(`/tutorials/${tutorialId}`);
591
+ }
592
+
593
+ /**
594
+ * 开始教程
595
+ * @param {string} tutorialId - 教程 ID
596
+ */
597
+ async startTutorial(tutorialId) {
598
+ return this.post(`/tutorials/${tutorialId}/start`);
599
+ }
600
+
601
+ /**
602
+ * 完成教程步骤
603
+ * @param {string} tutorialId - 教程 ID
604
+ * @param {string} stepId - 步骤 ID
605
+ */
606
+ async completeStep(tutorialId, stepId) {
607
+ return this.post(`/tutorials/${tutorialId}/steps/${stepId}/complete`);
608
+ }
609
+
610
+ /**
611
+ * 跳过教程
612
+ * @param {string} tutorialId - 教程 ID
613
+ */
614
+ async skipTutorial(tutorialId) {
615
+ return this.post(`/tutorials/${tutorialId}/skip`);
616
+ }
617
+
618
+ /**
619
+ * 获取整体进度
620
+ */
621
+ async getProgress() {
622
+ return this.get('/progress');
623
+ }
624
+
625
+ /**
626
+ * 重置教程进度
627
+ * @param {string} tutorialId - 教程 ID(可选,不传则重置全部)
628
+ */
629
+ async resetProgress(tutorialId = null) {
630
+ const endpoint = tutorialId ? `/tutorials/${tutorialId}/reset` : '/progress/reset';
631
+ return this.post(endpoint);
632
+ }
633
+
634
+ /**
635
+ * 获取推荐教程
636
+ */
637
+ async getRecommendedTutorials() {
638
+ return this.get('/tutorials/recommended');
639
+ }
640
+
641
+ /**
642
+ * 标记提示已读
643
+ * @param {string} tipId - 提示 ID
644
+ */
645
+ async dismissTip(tipId) {
646
+ return this.post(`/tips/${tipId}/dismiss`);
647
+ }
648
+
649
+ /**
650
+ * 获取新功能引导
651
+ */
652
+ async getNewFeatures() {
653
+ return this.get('/features/new');
654
+ }
655
+
656
+ /**
657
+ * 标记新功能已查看
658
+ * @param {string} featureId - 功能 ID
659
+ */
660
+ async markFeatureSeen(featureId) {
661
+ return this.post(`/features/${featureId}/seen`);
662
+ }
663
+ }
664
+
665
+ // 创建默认实例
666
+ const onboardingApi = new OnboardingApiClient();
667
+
668
+ /**
669
+ * Onboarding SDK - React Hooks
670
+ */
671
+ function useTutorials() {
672
+ const [tutorials, setTutorials] = useState([]);
673
+ const [loading, setLoading] = useState(true);
674
+ useEffect(() => {
675
+ onboardingApi.getTutorials().then(setTutorials).finally(() => setLoading(false));
676
+ }, []);
677
+ return {
678
+ tutorials,
679
+ loading
680
+ };
681
+ }
682
+ function useTutorial(id) {
683
+ const [tutorial, setTutorial] = useState(null);
684
+ const [currentStep, setCurrentStep] = useState(0);
685
+ const [loading, setLoading] = useState(true);
686
+ useEffect(() => {
687
+ if (id) onboardingApi.getTutorial(id).then(setTutorial).finally(() => setLoading(false));
688
+ }, [id]);
689
+ const nextStep = useCallback(() => {
690
+ if (tutorial && currentStep < tutorial.steps.length - 1) {
691
+ onboardingApi.completeStep(id, tutorial.steps[currentStep].id);
692
+ setCurrentStep(s => s + 1);
693
+ }
694
+ }, [id, tutorial, currentStep]);
695
+ const prevStep = useCallback(() => setCurrentStep(s => Math.max(0, s - 1)), []);
696
+ const skip = useCallback(() => onboardingApi.skipTutorial(id), [id]);
697
+ return {
698
+ tutorial,
699
+ currentStep,
700
+ nextStep,
701
+ prevStep,
702
+ skip,
703
+ loading
704
+ };
705
+ }
706
+ function useOnboardingProgress() {
707
+ const [progress, setProgress] = useState(null);
708
+ const [loading, setLoading] = useState(true);
709
+ useEffect(() => {
710
+ onboardingApi.getProgress().then(setProgress).finally(() => setLoading(false));
711
+ }, []);
712
+ return {
713
+ progress,
714
+ loading
715
+ };
716
+ }
717
+
718
+ export { CoachMark, FeatureHighlight, OnboardingApiClient, OnboardingFlow, SUPPORTED_LANGUAGES, SpotlightTour, StepIndicator, StepType, Tooltip, TutorialOverlay, TutorialStatus, WelcomeScreen, createDefaultStep, createDefaultTutorial, getLanguage, messages, onboardingApi, setLanguage, t, useOnboardingProgress, useTutorial, useTutorials };
719
+ //# sourceMappingURL=index.esm.js.map