react-product-tour-guide 0.2.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/dist/index.js ADDED
@@ -0,0 +1,1320 @@
1
+ 'use strict';
2
+
3
+ var React5 = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var reactDom = require('react-dom');
6
+ var react = require('@floating-ui/react');
7
+ var clsx = require('clsx');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var React5__default = /*#__PURE__*/_interopDefault(React5);
12
+
13
+ var __defProp = Object.defineProperty;
14
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
15
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
16
+
17
+ // src/styles/inject.ts
18
+ var __tourStyles__ = `@layer react-product-tour {
19
+
20
+ :root {
21
+ /* Tour Component Colors */
22
+ --tour--overlay--background: rgba(0, 0, 0, 0.5);
23
+ --tour--tooltip--background: white;
24
+ --tour--tooltip--border: #e5e7eb;
25
+ --tour--tooltip--text: black;
26
+ --tour--tooltip--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
27
+
28
+ /* Tour Component Spacing */
29
+ --tour--tooltip--padding: 1rem;
30
+ --tour--tooltip--gap: 0.5rem;
31
+
32
+ /* Tour Component Border */
33
+ --tour--tooltip--radius: 0.5rem;
34
+ --tour--tooltip--border-width: 1px;
35
+
36
+ /* Tour Component Animation */
37
+ --tour--tooltip--transition: all 0.2s ease-in-out;
38
+
39
+ /* Tour Highlight */
40
+ --tour--highlight--padding: 8px;
41
+ --tour--highlight--radius: 10px;
42
+
43
+ /* Tour Button Colors */
44
+ --tour--button--primary--background: #646cff;
45
+ --tour--button--primary--text: white;
46
+ --tour--button--secondary--background: #e5e7eb;
47
+ --tour--button--secondary--text: #374151;
48
+
49
+ /* Tour Tooltip Size */
50
+ --tour--tooltip--max-width: 300px;
51
+
52
+ /* Tour Progress Bar */
53
+ --tour--progress--background: #e5e7eb;
54
+ --tour--progress--fill: #f43f5e;
55
+ }
56
+
57
+ /* Dark mode variables */
58
+ .dark {
59
+ --tour--tooltip--background: #1f2937;
60
+ --tour--tooltip--border: #374151;
61
+ --tour--tooltip--text: #f9fafb;
62
+ }
63
+
64
+ /* Screen reader utilities */
65
+ .sr-only {
66
+ position: absolute;
67
+ width: 1px;
68
+ height: 1px;
69
+ padding: 0;
70
+ margin: -1px;
71
+ overflow: hidden;
72
+ clip: rect(0, 0, 0, 0);
73
+ white-space: nowrap;
74
+ border: 0;
75
+ }
76
+
77
+ /* Focus styles */
78
+ .tour-button:focus-visible {
79
+ outline: 2px solid #3b82f6;
80
+ outline-offset: 2px;
81
+ }
82
+
83
+ .tour-button:focus:not(:focus-visible) {
84
+ outline: none;
85
+ }
86
+
87
+ /* High contrast mode support */
88
+ @media (forced-colors: active) {
89
+ .tour-button {
90
+ border: 2px solid currentColor;
91
+ }
92
+
93
+ .tour-tooltip {
94
+ border: 2px solid currentColor;
95
+ }
96
+
97
+ .tour-highlight {
98
+ border: 2px solid currentColor;
99
+ }
100
+ }
101
+
102
+ /* Reduced motion */
103
+ @media (prefers-reduced-motion: reduce) {
104
+ .tour-tooltip,
105
+ .tour-highlight,
106
+ .tour-overlay,
107
+ .tour-button {
108
+ animation: none !important;
109
+ transition: none !important;
110
+ }
111
+ }
112
+
113
+ /* Base tooltip styles */
114
+ .tour-tooltip {
115
+ position: absolute;
116
+ background-color: var(--tour--tooltip--background);
117
+ color: var(--tour--tooltip--text);
118
+ padding: var(--tour--tooltip--padding);
119
+ border-radius: var(--tour--tooltip--radius);
120
+ box-shadow: var(--tour--tooltip--shadow);
121
+ max-width: var(--tour--tooltip--max-width);
122
+ z-index: 1001;
123
+ opacity: 0;
124
+ border: var(--tour--tooltip--border-width) solid var(--tour--tooltip--border);
125
+ }
126
+
127
+ /* Allow Tailwind classes to override base styles */
128
+ .tour-tooltip[class*="bg-"] {
129
+ background-color: inherit;
130
+ }
131
+
132
+ .tour-tooltip[class*="text-"] {
133
+ color: inherit;
134
+ }
135
+
136
+ .tour-tooltip[class*="border-"] {
137
+ border-color: inherit;
138
+ }
139
+
140
+ .tour-tooltip[class*="rounded-"] {
141
+ border-radius: inherit;
142
+ }
143
+
144
+ .tour-tooltip[class*="shadow-"] {
145
+ box-shadow: inherit;
146
+ }
147
+
148
+ .tour-tooltip[class*="p-"] {
149
+ padding: inherit;
150
+ }
151
+
152
+ /* Animation variants */
153
+ .tour-tooltip[data-animation="slide"] {
154
+ transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
155
+ transform-origin: center;
156
+ transform: scale(0.95);
157
+ }
158
+
159
+ .tour-tooltip[data-animation="slide"][data-placement="top"] {
160
+ transform: translateY(10px) scale(0.95);
161
+ }
162
+
163
+ .tour-tooltip[data-animation="slide"][data-placement="bottom"] {
164
+ transform: translateY(-10px) scale(0.95);
165
+ }
166
+
167
+ .tour-tooltip[data-animation="slide"][data-placement="left"] {
168
+ transform: translateX(10px) scale(0.95);
169
+ }
170
+
171
+ .tour-tooltip[data-animation="slide"][data-placement="right"] {
172
+ transform: translateX(-10px) scale(0.95);
173
+ }
174
+
175
+ .tour-tooltip[data-animation="slide"].visible {
176
+ opacity: 1;
177
+ transform: scale(1) translate(0, 0);
178
+ }
179
+
180
+ .tour-tooltip[data-animation="bounce"] {
181
+ transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.3s ease-in-out;
182
+ transform-origin: center;
183
+ transform: scale(0.3);
184
+ }
185
+
186
+ .tour-tooltip[data-animation="bounce"][data-placement="top"] {
187
+ transform: translateY(20px) scale(0.3);
188
+ }
189
+
190
+ .tour-tooltip[data-animation="bounce"][data-placement="bottom"] {
191
+ transform: translateY(-20px) scale(0.3);
192
+ }
193
+
194
+ .tour-tooltip[data-animation="bounce"][data-placement="left"] {
195
+ transform: translateX(20px) scale(0.3);
196
+ }
197
+
198
+ .tour-tooltip[data-animation="bounce"][data-placement="right"] {
199
+ transform: translateX(-20px) scale(0.3);
200
+ }
201
+
202
+ .tour-tooltip[data-animation="bounce"].visible {
203
+ opacity: 1;
204
+ transform: scale(1) translate(0, 0);
205
+ }
206
+
207
+ .tour-tooltip[data-animation="fade"] {
208
+ transition: all 0.4s ease-out;
209
+ transform: translateY(10px);
210
+ opacity: 0;
211
+ }
212
+
213
+ .tour-tooltip[data-animation="fade"].visible {
214
+ opacity: 1;
215
+ transform: translateY(0);
216
+ }
217
+
218
+ .tour-tooltip-title {
219
+ margin: 0 0 0.5rem 0;
220
+ font-size: 1rem;
221
+ font-weight: 600;
222
+ color: var(--tour--tooltip--text);
223
+ line-height: 1.3;
224
+ }
225
+
226
+ .tour-tooltip-content {
227
+ margin-bottom: 1rem;
228
+ }
229
+
230
+ .tour-buttons {
231
+ display: flex;
232
+ gap: var(--tour--tooltip--gap);
233
+ justify-content: flex-end;
234
+ }
235
+
236
+ /* Tooltip arrow */
237
+ .tour-tooltip::before {
238
+ content: '';
239
+ position: absolute;
240
+ width: 0.75rem;
241
+ height: 0.75rem;
242
+ background-color: var(--tour--tooltip--background);
243
+ border: var(--tour--tooltip--border-width) solid var(--tour--tooltip--border);
244
+ transform: rotate(45deg);
245
+ }
246
+
247
+ /* Tooltip arrow positions */
248
+ .tour-tooltip[data-placement="top"]::before {
249
+ bottom: -0.375rem;
250
+ border-right: none;
251
+ border-bottom: none;
252
+ }
253
+
254
+ .tour-tooltip[data-placement="bottom"]::before {
255
+ top: -0.375rem;
256
+ border-left: none;
257
+ border-top: none;
258
+ }
259
+
260
+ .tour-tooltip[data-placement="left"]::before {
261
+ right: -0.375rem;
262
+ border-top: none;
263
+ border-right: none;
264
+ }
265
+
266
+ .tour-tooltip[data-placement="right"]::before {
267
+ left: -0.375rem;
268
+ border-bottom: none;
269
+ border-left: none;
270
+ }
271
+
272
+ /* Button styles */
273
+ .tour-button {
274
+ border-radius: var(--tour--tooltip--radius);
275
+ font-weight: 500;
276
+ font-family: inherit;
277
+ cursor: pointer;
278
+ padding: 0.5rem 1rem;
279
+ border: none;
280
+ transition: var(--tour--tooltip--transition);
281
+ }
282
+
283
+ .tour-button-primary {
284
+ background-color: var(--tour--button--primary--background);
285
+ color: var(--tour--button--primary--text);
286
+ }
287
+
288
+ .tour-button-secondary {
289
+ background-color: var(--tour--button--secondary--background);
290
+ color: var(--tour--button--secondary--text);
291
+ }
292
+
293
+ /* Allow Tailwind classes to override button styles */
294
+ .tour-button[class*="bg-"] {
295
+ background-color: inherit;
296
+ }
297
+
298
+ .tour-button[class*="text-"] {
299
+ color: inherit;
300
+ }
301
+
302
+ .tour-button[class*="rounded-"] {
303
+ border-radius: inherit;
304
+ }
305
+
306
+ .tour-button[class*="p-"] {
307
+ padding: inherit;
308
+ }
309
+
310
+ .tour-button[class*="font-"] {
311
+ font-weight: inherit;
312
+ }
313
+
314
+ .tour-button:disabled {
315
+ opacity: 0.5;
316
+ cursor: not-allowed;
317
+ }
318
+
319
+ .dark .tour-button-secondary {
320
+ background-color: #4b5563;
321
+ }
322
+
323
+ /* Overlay styles */
324
+ .tour-overlay {
325
+ position: fixed;
326
+ top: 0;
327
+ left: 0;
328
+ right: 0;
329
+ bottom: 0;
330
+ background-color: var(--tour--overlay--background);
331
+ z-index: 1000;
332
+ }
333
+
334
+ .tour-overlay[class*="bg-"] {
335
+ background-color: inherit;
336
+ }
337
+
338
+ .tour-overlay-blur {
339
+ backdrop-filter: blur(2px);
340
+ }
341
+
342
+ /* Highlight styles */
343
+ .tour-highlight {
344
+ position: absolute;
345
+ border-radius: var(--tour--highlight--radius);
346
+ box-shadow: 0 0 0 9999px var(--tour--overlay--background);
347
+ z-index: 1001;
348
+ opacity: 0;
349
+ }
350
+
351
+ .tour-highlight[class*="rounded-"] {
352
+ border-radius: inherit;
353
+ }
354
+
355
+ .tour-highlight[class*="shadow-"] {
356
+ box-shadow: inherit;
357
+ }
358
+
359
+ .tour-highlight[data-animation="slide"] {
360
+ transition: all 0.3s ease-in-out;
361
+ transform: scale(0.98);
362
+ }
363
+
364
+ .tour-highlight[data-animation="slide"].visible {
365
+ opacity: 1;
366
+ transform: scale(1);
367
+ }
368
+
369
+ .tour-highlight[data-animation="bounce"] {
370
+ transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
371
+ transform: translateY(20px) scale(0.95);
372
+ opacity: 0;
373
+ }
374
+
375
+ .tour-highlight[data-animation="bounce"].visible {
376
+ opacity: 1;
377
+ transform: translateY(0) scale(1);
378
+ }
379
+
380
+ .tour-highlight[data-animation="fade"] {
381
+ transition: all 0.4s ease-out;
382
+ transform: translateY(10px);
383
+ opacity: 0;
384
+ }
385
+
386
+ .tour-highlight[data-animation="fade"].visible {
387
+ opacity: 1;
388
+ transform: translateY(0);
389
+ }
390
+
391
+ .dark .tour-highlight {
392
+ background-color: rgba(255, 255, 255, 0.05);
393
+ border-color: rgba(255, 255, 255, 0.2);
394
+ }
395
+
396
+ } /* end @layer react-product-tour */
397
+ `;
398
+ if (typeof document !== "undefined") {
399
+ const id = "__react_product_tour_styles__";
400
+ if (!document.getElementById(id)) {
401
+ const style = document.createElement("style");
402
+ style.id = id;
403
+ style.textContent = __tourStyles__;
404
+ document.head.insertBefore(style, document.head.firstChild);
405
+ }
406
+ }
407
+
408
+ // src/manager/TourManager.ts
409
+ var _TourManager = class _TourManager {
410
+ constructor() {
411
+ __publicField(this, "state");
412
+ __publicField(this, "listeners");
413
+ this.state = {
414
+ steps: [],
415
+ currentStep: 0,
416
+ isActive: false
417
+ };
418
+ this.listeners = /* @__PURE__ */ new Set();
419
+ }
420
+ static getInstance() {
421
+ if (!_TourManager.instance) {
422
+ _TourManager.instance = new _TourManager();
423
+ }
424
+ return _TourManager.instance;
425
+ }
426
+ getState() {
427
+ return { ...this.state, steps: [...this.state.steps] };
428
+ }
429
+ initialize(steps) {
430
+ this.state.steps = steps;
431
+ this.state.currentStep = 0;
432
+ this.state.isActive = false;
433
+ this.notifyListeners();
434
+ }
435
+ start() {
436
+ if (this.state.steps.length === 0) return;
437
+ this.state.isActive = true;
438
+ this.state.currentStep = 0;
439
+ this.notifyListeners();
440
+ }
441
+ stop() {
442
+ this.state.isActive = false;
443
+ this.notifyListeners();
444
+ }
445
+ next() {
446
+ if (!this.state.isActive || this.state.currentStep >= this.state.steps.length - 1) {
447
+ this.stop();
448
+ return;
449
+ }
450
+ this.state.currentStep += 1;
451
+ this.notifyListeners();
452
+ }
453
+ back() {
454
+ if (!this.state.isActive || this.state.currentStep <= 0) return;
455
+ this.state.currentStep -= 1;
456
+ this.notifyListeners();
457
+ }
458
+ skip() {
459
+ this.stop();
460
+ }
461
+ subscribe(listener) {
462
+ this.listeners.add(listener);
463
+ return () => this.listeners.delete(listener);
464
+ }
465
+ notifyListeners() {
466
+ this.listeners.forEach((listener) => listener({ ...this.state }));
467
+ }
468
+ };
469
+ __publicField(_TourManager, "instance");
470
+ var TourManager = _TourManager;
471
+ var tourManager = TourManager.getInstance();
472
+ var TourContext = React5.createContext(null);
473
+ var TourProvider = ({
474
+ steps,
475
+ children,
476
+ defaultActive = false,
477
+ onComplete,
478
+ onSkip,
479
+ onStepChange,
480
+ onStepEnter,
481
+ onStepExit
482
+ }) => {
483
+ const [currentStep, setCurrentStep] = React5.useState(0);
484
+ const [isActive, setIsActive] = React5.useState(defaultActive);
485
+ React5.useEffect(() => {
486
+ tourManager.initialize(steps);
487
+ }, [steps]);
488
+ const start = React5.useCallback(() => {
489
+ setIsActive(true);
490
+ setCurrentStep(0);
491
+ onStepEnter?.(0, steps[0]);
492
+ }, [steps, onStepEnter]);
493
+ const stop = React5.useCallback(() => {
494
+ if (isActive) {
495
+ onStepExit?.(currentStep, steps[currentStep]);
496
+ }
497
+ setIsActive(false);
498
+ }, [isActive, currentStep, steps, onStepExit]);
499
+ const next = React5.useCallback(async () => {
500
+ const currentStepData = steps[currentStep];
501
+ if (currentStepData?.waitFor) {
502
+ await currentStepData.waitFor();
503
+ }
504
+ if (currentStep < steps.length - 1) {
505
+ onStepExit?.(currentStep, currentStepData);
506
+ const nextStepIndex = currentStep + 1;
507
+ setCurrentStep(nextStepIndex);
508
+ onStepChange?.(nextStepIndex, steps[nextStepIndex]);
509
+ onStepEnter?.(nextStepIndex, steps[nextStepIndex]);
510
+ } else {
511
+ stop();
512
+ onComplete?.();
513
+ }
514
+ }, [currentStep, steps, stop, onComplete, onStepExit, onStepChange, onStepEnter]);
515
+ const back = React5.useCallback(() => {
516
+ if (currentStep > 0) {
517
+ const currentStepData = steps[currentStep];
518
+ onStepExit?.(currentStep, currentStepData);
519
+ const prevStepIndex = currentStep - 1;
520
+ setCurrentStep(prevStepIndex);
521
+ onStepChange?.(prevStepIndex, steps[prevStepIndex]);
522
+ onStepEnter?.(prevStepIndex, steps[prevStepIndex]);
523
+ }
524
+ }, [currentStep, steps, onStepExit, onStepChange, onStepEnter]);
525
+ const skip = React5.useCallback(() => {
526
+ stop();
527
+ onSkip?.();
528
+ }, [stop, onSkip]);
529
+ const value = React5.useMemo(() => ({
530
+ steps,
531
+ currentStep,
532
+ isActive,
533
+ start,
534
+ stop,
535
+ next,
536
+ back,
537
+ skip
538
+ }), [steps, currentStep, isActive, start, stop, next, back, skip]);
539
+ return /* @__PURE__ */ jsxRuntime.jsx(TourContext.Provider, { value, children });
540
+ };
541
+ var useTour = () => {
542
+ const context = React5.useContext(TourContext);
543
+ if (!context) {
544
+ throw new Error("useTour must be used within a TourProvider");
545
+ }
546
+ return context;
547
+ };
548
+ var useTour2 = () => {
549
+ const context = React5.useContext(TourContext);
550
+ const [globalState, setGlobalState] = React5.useState(tourManager.getState());
551
+ React5.useEffect(() => {
552
+ return tourManager.subscribe(setGlobalState);
553
+ }, []);
554
+ if (context) {
555
+ return context;
556
+ }
557
+ return {
558
+ steps: globalState.steps,
559
+ currentStep: globalState.currentStep,
560
+ isActive: globalState.isActive,
561
+ start: tourManager.start.bind(tourManager),
562
+ stop: tourManager.stop.bind(tourManager),
563
+ next: tourManager.next.bind(tourManager),
564
+ back: tourManager.back.bind(tourManager),
565
+ skip: tourManager.skip.bind(tourManager)
566
+ };
567
+ };
568
+ var ProgressBar = ({
569
+ currentStep,
570
+ totalSteps,
571
+ className = ""
572
+ }) => {
573
+ const progress = (currentStep + 1) / totalSteps * 100;
574
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-1 rounded-full ${className}`, style: { background: "var(--tour--progress--background)" }, children: /* @__PURE__ */ jsxRuntime.jsx(
575
+ "div",
576
+ {
577
+ className: "h-full rounded-full transition-all duration-300 ease-in-out",
578
+ style: { width: `${progress}%`, background: "var(--tour--progress--fill)" },
579
+ role: "progressbar",
580
+ "aria-valuenow": progress,
581
+ "aria-valuemin": 0,
582
+ "aria-valuemax": 100
583
+ }
584
+ ) });
585
+ };
586
+ var MediaFallback = ({ type, className = "" }) => {
587
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center justify-center p-4 bg-gray-100 rounded-lg ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
588
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-400 mb-2", children: type === "image" ? "\u{1F5BC}\uFE0F" : "\u{1F3A5}" }),
589
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: type === "image" ? "Image failed to load" : "Video failed to load" })
590
+ ] }) });
591
+ };
592
+ var ErrorBoundary = class extends React5.Component {
593
+ constructor(props) {
594
+ super(props);
595
+ this.state = {
596
+ hasError: false,
597
+ error: null
598
+ };
599
+ }
600
+ /**
601
+ * Updates state when an error occurs
602
+ * @param error - The error that occurred
603
+ * @returns New state
604
+ */
605
+ static getDerivedStateFromError(error) {
606
+ return {
607
+ hasError: true,
608
+ error
609
+ };
610
+ }
611
+ /**
612
+ * Called when an error occurs
613
+ * @param error - The error that occurred
614
+ * @param errorInfo - Additional error information
615
+ */
616
+ componentDidCatch(error, errorInfo) {
617
+ this.props.onError?.(error, errorInfo);
618
+ }
619
+ render() {
620
+ if (this.state.hasError) {
621
+ if (this.props.fallback) {
622
+ return this.props.fallback;
623
+ }
624
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-red-50 border border-red-200 rounded-lg", children: [
625
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-red-600 mb-2", children: "Something went wrong" }),
626
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-red-500", children: this.state.error?.message || "An unexpected error occurred" })
627
+ ] });
628
+ }
629
+ return this.props.children;
630
+ }
631
+ };
632
+ var getMediaSource = (value) => {
633
+ if (typeof value === "string") return value;
634
+ if (typeof value === "object" && value !== null && "type" in value && "src" in value) {
635
+ const mediaSource = value;
636
+ return mediaSource.src;
637
+ }
638
+ return String(value);
639
+ };
640
+ var SAFE_IMG_ATTRS = /* @__PURE__ */ new Set(["alt", "width", "height", "loading", "decoding", "crossOrigin", "referrerPolicy", "sizes", "srcSet", "className", "style"]);
641
+ var SAFE_VIDEO_ATTRS = /* @__PURE__ */ new Set(["width", "height", "loop", "muted", "autoPlay", "playsInline", "preload", "poster", "crossOrigin", "className", "style"]);
642
+ var filterProps = (props, allowList) => {
643
+ if (!props) return {};
644
+ return Object.fromEntries(Object.entries(props).filter(([k]) => allowList.has(k)));
645
+ };
646
+ var ImageContent = ({ src, alt = "Tour content", props }) => {
647
+ const [hasError, setHasError] = React5.useState(false);
648
+ if (hasError) return /* @__PURE__ */ jsxRuntime.jsx(MediaFallback, { type: "image", className: "w-full h-auto" });
649
+ return /* @__PURE__ */ jsxRuntime.jsx(
650
+ "img",
651
+ {
652
+ src,
653
+ alt,
654
+ className: "w-full h-auto rounded-lg",
655
+ onError: () => setHasError(true),
656
+ ...filterProps(props, SAFE_IMG_ATTRS)
657
+ }
658
+ );
659
+ };
660
+ var VideoContent = ({ src, props }) => {
661
+ const [hasError, setHasError] = React5.useState(false);
662
+ if (hasError) return /* @__PURE__ */ jsxRuntime.jsx(MediaFallback, { type: "video", className: "w-full h-auto" });
663
+ return /* @__PURE__ */ jsxRuntime.jsx(
664
+ "video",
665
+ {
666
+ src,
667
+ controls: true,
668
+ className: "w-full h-auto rounded-lg",
669
+ role: "presentation",
670
+ "aria-label": "Tour video content",
671
+ onError: () => setHasError(true),
672
+ ...filterProps(props, SAFE_VIDEO_ATTRS)
673
+ }
674
+ );
675
+ };
676
+ var renderContent = (content) => {
677
+ if (!content) return null;
678
+ if (typeof content === "string" || React5__default.default.isValidElement(content)) {
679
+ return content;
680
+ }
681
+ const contentObj = content;
682
+ if (typeof contentObj === "object" && contentObj !== null && "type" in contentObj && "value" in contentObj) {
683
+ const typedContent = contentObj;
684
+ switch (typedContent.type) {
685
+ case "image":
686
+ return /* @__PURE__ */ jsxRuntime.jsx(ImageContent, { src: getMediaSource(typedContent.value), alt: typedContent.alt, props: typedContent.props });
687
+ case "video":
688
+ return /* @__PURE__ */ jsxRuntime.jsx(VideoContent, { src: getMediaSource(typedContent.value), props: typedContent.props });
689
+ case "custom":
690
+ return typedContent.value;
691
+ case "text":
692
+ default:
693
+ return typedContent.value;
694
+ }
695
+ }
696
+ return content;
697
+ };
698
+ var renderButtons = (props, config) => {
699
+ const {
700
+ onNext,
701
+ onBack,
702
+ onSkip,
703
+ onComplete,
704
+ isFirstStep,
705
+ isLastStep,
706
+ skip
707
+ } = props;
708
+ if (config?.container?.render) {
709
+ return config.container.render(props);
710
+ }
711
+ return /* @__PURE__ */ jsxRuntime.jsxs(
712
+ "div",
713
+ {
714
+ className: clsx.clsx(
715
+ "flex justify-between items-center gap-2",
716
+ config?.container?.className
717
+ ),
718
+ style: {
719
+ flexDirection: config?.container?.direction || "row",
720
+ alignItems: config?.container?.align || "center",
721
+ gap: config?.container?.gap || "0.5rem",
722
+ ...config?.container?.style
723
+ },
724
+ role: "toolbar",
725
+ "aria-label": "Tour navigation",
726
+ children: [
727
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
728
+ !isFirstStep && (config?.secondary?.render ? config.secondary.render(props) : /* @__PURE__ */ jsxRuntime.jsx(
729
+ "button",
730
+ {
731
+ onClick: onBack,
732
+ className: clsx.clsx("tour-button tour-button-secondary", config?.secondary?.className),
733
+ style: config?.secondary?.style,
734
+ "aria-label": "Go to previous step",
735
+ children: config?.secondary?.content || "Back"
736
+ }
737
+ )),
738
+ skip && (config?.secondary?.render ? config.secondary.render(props) : /* @__PURE__ */ jsxRuntime.jsx(
739
+ "button",
740
+ {
741
+ onClick: onSkip,
742
+ className: clsx.clsx("tour-button tour-button-secondary", config?.secondary?.className),
743
+ style: config?.secondary?.style,
744
+ "aria-label": "Skip tour",
745
+ children: config?.secondary?.content || "Skip"
746
+ }
747
+ ))
748
+ ] }),
749
+ config?.primary?.render ? config.primary.render(props) : /* @__PURE__ */ jsxRuntime.jsx(
750
+ "button",
751
+ {
752
+ onClick: isLastStep ? onComplete : onNext,
753
+ className: clsx.clsx("tour-button tour-button-primary", config?.primary?.className),
754
+ style: config?.primary?.style,
755
+ "aria-label": isLastStep ? "Complete tour" : "Go to next step",
756
+ children: config?.primary?.content || (isLastStep ? "Done" : "Next")
757
+ }
758
+ )
759
+ ]
760
+ }
761
+ );
762
+ };
763
+ var TourTooltip = ({
764
+ content,
765
+ placement,
766
+ animation = "slide",
767
+ isFirstStep,
768
+ isLastStep,
769
+ skip,
770
+ onNext,
771
+ onBack,
772
+ onSkip,
773
+ onComplete,
774
+ tooltipClassName,
775
+ buttonClassName,
776
+ buttonContainerClassName,
777
+ targetLabel,
778
+ title,
779
+ floatingStyles,
780
+ setFloating,
781
+ showProgress = false,
782
+ currentStep,
783
+ totalSteps,
784
+ buttonConfig
785
+ }) => {
786
+ const [isVisible, setIsVisible] = React5.useState(false);
787
+ React5.useEffect(() => {
788
+ const timer = setTimeout(() => {
789
+ setIsVisible(true);
790
+ }, 50);
791
+ return () => {
792
+ clearTimeout(timer);
793
+ setIsVisible(false);
794
+ };
795
+ }, [placement]);
796
+ return /* @__PURE__ */ jsxRuntime.jsxs(
797
+ "div",
798
+ {
799
+ ref: setFloating,
800
+ style: floatingStyles,
801
+ className: clsx.clsx("tour-tooltip z-50", tooltipClassName, { visible: isVisible }),
802
+ role: "dialog",
803
+ "aria-modal": "true",
804
+ "aria-labelledby": "tour-step-title",
805
+ "aria-describedby": "tour-step-content",
806
+ "data-placement": placement,
807
+ "data-animation": animation,
808
+ children: [
809
+ title ? /* @__PURE__ */ jsxRuntime.jsx("h3", { id: "tour-step-title", className: "tour-tooltip-title", children: title }) : /* @__PURE__ */ jsxRuntime.jsx("div", { id: "tour-step-title", className: "sr-only", children: `Tour Step: ${targetLabel}` }),
810
+ showProgress && currentStep !== void 0 && totalSteps !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
811
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between items-center mb-1", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", style: { opacity: 0.6 }, "aria-hidden": "true", children: `Step ${currentStep + 1} of ${totalSteps}` }) }),
812
+ /* @__PURE__ */ jsxRuntime.jsx(ProgressBar, { currentStep, totalSteps })
813
+ ] }),
814
+ /* @__PURE__ */ jsxRuntime.jsx("div", { id: "tour-step-content", className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(
815
+ ErrorBoundary,
816
+ {
817
+ fallback: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-gray-50 border border-gray-200 rounded-lg", children: [
818
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600 mb-2", children: "Content Error" }),
819
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500", children: "Failed to render tour content" })
820
+ ] }),
821
+ children: renderContent(content)
822
+ }
823
+ ) }),
824
+ renderButtons(
825
+ {
826
+ onNext,
827
+ onBack,
828
+ onSkip,
829
+ onComplete,
830
+ isFirstStep,
831
+ isLastStep,
832
+ currentStep,
833
+ totalSteps,
834
+ skip
835
+ },
836
+ buttonConfig || {
837
+ primary: { className: buttonClassName },
838
+ secondary: { className: buttonClassName },
839
+ container: { className: buttonContainerClassName }
840
+ }
841
+ )
842
+ ]
843
+ }
844
+ );
845
+ };
846
+ var TourOverlay = ({
847
+ overlayClassName,
848
+ isPartialBlur,
849
+ hasHighlight,
850
+ onDismiss
851
+ }) => {
852
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
853
+ isPartialBlur && /* @__PURE__ */ jsxRuntime.jsx(
854
+ "div",
855
+ {
856
+ className: "fixed inset-0 z-40 backdrop-blur-sm bg-black/40",
857
+ style: {
858
+ mixBlendMode: "multiply"
859
+ },
860
+ onClick: onDismiss,
861
+ role: "presentation",
862
+ "aria-hidden": "true"
863
+ }
864
+ ),
865
+ !hasHighlight && /* @__PURE__ */ jsxRuntime.jsx(
866
+ "div",
867
+ {
868
+ className: clsx.clsx("tour-overlay fixed inset-0 z-40", overlayClassName),
869
+ onClick: onDismiss,
870
+ role: "presentation",
871
+ "aria-hidden": "true"
872
+ }
873
+ )
874
+ ] });
875
+ };
876
+ var TourHighlight = ({
877
+ targetRect,
878
+ highlightConfig,
879
+ animation = "slide"
880
+ }) => {
881
+ const padding = parseFloat(
882
+ getComputedStyle(document.documentElement).getPropertyValue("--tour--highlight--padding") || "8"
883
+ );
884
+ const [isVisible, setIsVisible] = React5.useState(false);
885
+ React5.useEffect(() => {
886
+ const timer = setTimeout(() => {
887
+ setIsVisible(true);
888
+ }, 50);
889
+ return () => {
890
+ clearTimeout(timer);
891
+ setIsVisible(false);
892
+ };
893
+ }, [targetRect]);
894
+ return /* @__PURE__ */ jsxRuntime.jsx(
895
+ "div",
896
+ {
897
+ style: {
898
+ position: "fixed",
899
+ top: targetRect.top - padding,
900
+ left: targetRect.left - padding,
901
+ width: targetRect.width + padding * 2,
902
+ height: targetRect.height + padding * 2,
903
+ ...highlightConfig.style
904
+ },
905
+ className: clsx.clsx(
906
+ "tour-highlight z-50",
907
+ highlightConfig.className,
908
+ { visible: isVisible }
909
+ ),
910
+ "data-animation": animation,
911
+ role: "presentation",
912
+ "aria-hidden": "true"
913
+ }
914
+ );
915
+ };
916
+ var LiveRegion = ({
917
+ currentStep,
918
+ totalSteps,
919
+ targetLabel,
920
+ content,
921
+ isActive,
922
+ announcements
923
+ }) => {
924
+ const ref = React5.useRef(null);
925
+ React5.useEffect(() => {
926
+ if (!isActive || !ref.current) return;
927
+ const contentText = typeof content === "string" ? content : "";
928
+ const defaultStepAnnouncement = `Step ${currentStep + 1} of ${totalSteps}: ${targetLabel}.${contentText ? ` ${contentText}` : ""}`;
929
+ const stepAnnouncement = announcements?.step ? announcements.step.replace("{step}", String(currentStep + 1)).replace("{total}", String(totalSteps)).replace("{content}", contentText) : defaultStepAnnouncement;
930
+ ref.current.textContent = stepAnnouncement;
931
+ }, [currentStep, totalSteps, targetLabel, content, isActive, announcements]);
932
+ return React5__default.default.createElement("div", {
933
+ ref,
934
+ role: "status",
935
+ "aria-live": "polite",
936
+ "aria-atomic": "true",
937
+ className: "sr-only"
938
+ });
939
+ };
940
+ var useTourAccessibility = ({
941
+ currentStep,
942
+ totalSteps,
943
+ targetLabel,
944
+ content,
945
+ isActive,
946
+ enableScreenReader = false,
947
+ announcements,
948
+ focusManagement = "auto",
949
+ focusTrap = true
950
+ }) => {
951
+ const previousFocusRef = React5.useRef(null);
952
+ React5.useEffect(() => {
953
+ if (focusManagement === "manual") return;
954
+ previousFocusRef.current = document.activeElement;
955
+ const focusableElements = document.querySelectorAll(
956
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
957
+ );
958
+ if (focusableElements.length > 0) {
959
+ focusableElements[0].focus();
960
+ }
961
+ return () => {
962
+ if (previousFocusRef.current && document.contains(previousFocusRef.current)) {
963
+ previousFocusRef.current.focus();
964
+ }
965
+ };
966
+ }, [isActive, focusManagement]);
967
+ React5.useEffect(() => {
968
+ if (!focusTrap) return;
969
+ const handleTabKey = (e) => {
970
+ const focusableElements = document.querySelectorAll(
971
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
972
+ );
973
+ if (focusableElements.length === 0) return;
974
+ const firstFocusableElement = focusableElements[0];
975
+ const lastFocusableElement = focusableElements[focusableElements.length - 1];
976
+ if (e.key === "Tab") {
977
+ if (e.shiftKey) {
978
+ if (document.activeElement === firstFocusableElement) {
979
+ e.preventDefault();
980
+ lastFocusableElement.focus();
981
+ }
982
+ } else {
983
+ if (document.activeElement === lastFocusableElement) {
984
+ e.preventDefault();
985
+ firstFocusableElement.focus();
986
+ }
987
+ }
988
+ }
989
+ };
990
+ document.addEventListener("keydown", handleTabKey);
991
+ return () => document.removeEventListener("keydown", handleTabKey);
992
+ }, [isActive, focusTrap]);
993
+ return {
994
+ LiveRegion: () => React5__default.default.createElement(LiveRegion, {
995
+ currentStep,
996
+ totalSteps,
997
+ targetLabel,
998
+ content,
999
+ isActive,
1000
+ announcements
1001
+ }),
1002
+ targetLabel
1003
+ };
1004
+ };
1005
+ function useDebounce(callback, delay) {
1006
+ const timeoutRef = React5.useRef();
1007
+ return React5.useCallback(
1008
+ (...args) => {
1009
+ if (timeoutRef.current) {
1010
+ clearTimeout(timeoutRef.current);
1011
+ }
1012
+ timeoutRef.current = setTimeout(() => {
1013
+ callback(...args);
1014
+ }, delay);
1015
+ },
1016
+ [callback, delay]
1017
+ );
1018
+ }
1019
+ var Spotlight = React5.memo(({
1020
+ targetElement,
1021
+ placement,
1022
+ content,
1023
+ onNext,
1024
+ onBack,
1025
+ onSkip,
1026
+ onComplete,
1027
+ isFirstStep,
1028
+ isLastStep,
1029
+ skip = true,
1030
+ overlayClassName,
1031
+ tooltipClassName,
1032
+ buttonClassName,
1033
+ buttonContainerClassName,
1034
+ highlightTarget = true,
1035
+ currentStep,
1036
+ totalSteps,
1037
+ showProgress = false,
1038
+ accessibility = {},
1039
+ animation = "slide",
1040
+ tooltipOffset = 10,
1041
+ buttonConfig,
1042
+ dismissOnOverlayClick = true,
1043
+ title
1044
+ }) => {
1045
+ const middleware = React5.useMemo(
1046
+ () => [react.offset(tooltipOffset), react.flip(), react.shift()],
1047
+ [tooltipOffset]
1048
+ );
1049
+ const { refs: tooltipRefs, floatingStyles, update } = react.useFloating({
1050
+ placement,
1051
+ middleware,
1052
+ whileElementsMounted: react.autoUpdate
1053
+ });
1054
+ const { LiveRegion: LiveRegion2, targetLabel } = useTourAccessibility({
1055
+ currentStep: currentStep ?? 0,
1056
+ totalSteps: totalSteps ?? 0,
1057
+ targetLabel: targetElement?.getAttribute("aria-label") || "target element",
1058
+ content,
1059
+ isActive: true,
1060
+ enableScreenReader: accessibility.enableScreenReader,
1061
+ announcements: accessibility.announcements,
1062
+ focusManagement: accessibility.focusManagement,
1063
+ focusTrap: accessibility.focusTrap
1064
+ });
1065
+ const highlightConfig = typeof highlightTarget === "object" ? highlightTarget : { className: "tour-highlight" };
1066
+ const isPartialBlur = overlayClassName?.includes("tour-overlay-partial-blur");
1067
+ const [rect, setRect] = React5.useState(
1068
+ () => targetElement ? targetElement.getBoundingClientRect() : null
1069
+ );
1070
+ const debouncedUpdate = useDebounce(update, 100);
1071
+ const debouncedUpdateRef = React5.useRef(debouncedUpdate);
1072
+ React5.useEffect(() => {
1073
+ debouncedUpdateRef.current = debouncedUpdate;
1074
+ });
1075
+ React5.useEffect(() => {
1076
+ if (!targetElement) return;
1077
+ tooltipRefs.setReference(targetElement);
1078
+ targetElement.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
1079
+ setRect(targetElement.getBoundingClientRect());
1080
+ }, [targetElement, tooltipRefs]);
1081
+ React5.useEffect(() => {
1082
+ if (!targetElement) return;
1083
+ const updateRect = () => {
1084
+ setRect(targetElement.getBoundingClientRect());
1085
+ debouncedUpdateRef.current();
1086
+ };
1087
+ window.addEventListener("scroll", updateRect, { passive: true });
1088
+ window.addEventListener("resize", updateRect, { passive: true });
1089
+ return () => {
1090
+ window.removeEventListener("scroll", updateRect);
1091
+ window.removeEventListener("resize", updateRect);
1092
+ };
1093
+ }, [targetElement]);
1094
+ if (!targetElement || !rect) return null;
1095
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1096
+ accessibility.enableScreenReader !== false && /* @__PURE__ */ jsxRuntime.jsx(LiveRegion2, {}),
1097
+ /* @__PURE__ */ jsxRuntime.jsx(
1098
+ TourOverlay,
1099
+ {
1100
+ overlayClassName,
1101
+ isPartialBlur,
1102
+ hasHighlight: !!highlightTarget,
1103
+ onDismiss: dismissOnOverlayClick ? onSkip : void 0
1104
+ }
1105
+ ),
1106
+ highlightTarget && /* @__PURE__ */ jsxRuntime.jsx(
1107
+ TourHighlight,
1108
+ {
1109
+ targetRect: rect,
1110
+ highlightConfig,
1111
+ animation
1112
+ }
1113
+ ),
1114
+ isPartialBlur && targetElement.id && /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1115
+ #${CSS.escape(targetElement.id)} {
1116
+ isolation: isolate;
1117
+ position: relative;
1118
+ z-index: 50;
1119
+ transform: translateZ(0);
1120
+ will-change: transform;
1121
+ }
1122
+ ` }),
1123
+ /* @__PURE__ */ jsxRuntime.jsx(
1124
+ TourTooltip,
1125
+ {
1126
+ content,
1127
+ placement,
1128
+ isFirstStep,
1129
+ isLastStep,
1130
+ skip,
1131
+ onNext,
1132
+ onBack,
1133
+ onSkip,
1134
+ onComplete,
1135
+ tooltipClassName,
1136
+ buttonClassName,
1137
+ buttonContainerClassName,
1138
+ targetLabel,
1139
+ floatingStyles,
1140
+ setFloating: tooltipRefs.setFloating,
1141
+ showProgress,
1142
+ currentStep,
1143
+ totalSteps,
1144
+ animation,
1145
+ buttonConfig,
1146
+ title
1147
+ }
1148
+ )
1149
+ ] });
1150
+ });
1151
+ var Tour = ({
1152
+ overlayClassName,
1153
+ tooltipClassName,
1154
+ buttonClassName,
1155
+ buttonContainerClassName,
1156
+ highlightTarget,
1157
+ skip = true,
1158
+ showProgress = false,
1159
+ accessibility = {},
1160
+ animation = "slide",
1161
+ tooltipOffset,
1162
+ buttonConfig,
1163
+ dismissOnOverlayClick = true
1164
+ }) => {
1165
+ const { steps, currentStep, isActive, next, back, skip: skipTour } = useTour();
1166
+ const [targetElement, setTargetElement] = React5.useState(null);
1167
+ const enableScreenReader = accessibility.enableScreenReader;
1168
+ const startAnnouncement = accessibility.announcements?.start ?? "Tour started. Use arrow keys to navigate between steps.";
1169
+ const endAnnouncement = accessibility.announcements?.end ?? "Tour ended.";
1170
+ React5.useEffect(() => {
1171
+ if (!isActive) return;
1172
+ const handleKeyDown = (e) => {
1173
+ if (e.key === "Escape") {
1174
+ skipTour();
1175
+ return;
1176
+ }
1177
+ const target = e.target;
1178
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable) return;
1179
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1180
+ e.preventDefault();
1181
+ next();
1182
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
1183
+ e.preventDefault();
1184
+ back();
1185
+ }
1186
+ };
1187
+ document.addEventListener("keydown", handleKeyDown);
1188
+ return () => document.removeEventListener("keydown", handleKeyDown);
1189
+ }, [isActive, skipTour, next, back]);
1190
+ React5.useEffect(() => {
1191
+ if (!isActive || enableScreenReader === false) return;
1192
+ const announcement = document.createElement("div");
1193
+ announcement.setAttribute("role", "status");
1194
+ announcement.setAttribute("aria-live", "polite");
1195
+ announcement.setAttribute("aria-atomic", "true");
1196
+ announcement.className = "sr-only";
1197
+ announcement.textContent = startAnnouncement;
1198
+ document.body.appendChild(announcement);
1199
+ const startTimerId = setTimeout(() => {
1200
+ if (announcement.isConnected) document.body.removeChild(announcement);
1201
+ }, 3e3);
1202
+ return () => {
1203
+ clearTimeout(startTimerId);
1204
+ if (announcement.isConnected) document.body.removeChild(announcement);
1205
+ const endEl = document.createElement("div");
1206
+ endEl.setAttribute("role", "status");
1207
+ endEl.setAttribute("aria-live", "polite");
1208
+ endEl.setAttribute("aria-atomic", "true");
1209
+ endEl.className = "sr-only";
1210
+ endEl.textContent = endAnnouncement;
1211
+ document.body.appendChild(endEl);
1212
+ setTimeout(() => {
1213
+ if (endEl.isConnected) document.body.removeChild(endEl);
1214
+ }, 3e3);
1215
+ };
1216
+ }, [isActive, enableScreenReader, startAnnouncement, endAnnouncement]);
1217
+ React5.useEffect(() => {
1218
+ if (!isActive) return;
1219
+ let mounted = true;
1220
+ const currentStepData2 = steps[currentStep];
1221
+ if (!currentStepData2) return;
1222
+ setTargetElement(null);
1223
+ let element = null;
1224
+ try {
1225
+ element = document.querySelector(currentStepData2.selector);
1226
+ } catch {
1227
+ return;
1228
+ }
1229
+ if (element) {
1230
+ setTargetElement(element);
1231
+ } else if (currentStepData2.waitFor) {
1232
+ currentStepData2.waitFor().then(() => {
1233
+ if (!mounted) return;
1234
+ try {
1235
+ const el = document.querySelector(currentStepData2.selector);
1236
+ setTargetElement(el);
1237
+ } catch {
1238
+ }
1239
+ }).catch(() => {
1240
+ });
1241
+ }
1242
+ return () => {
1243
+ mounted = false;
1244
+ };
1245
+ }, [isActive, currentStep, steps]);
1246
+ const currentStepData = steps[currentStep];
1247
+ if (!isActive || !currentStepData) return null;
1248
+ if (!targetElement) {
1249
+ const isLastStep = currentStep === steps.length - 1;
1250
+ return reactDom.createPortal(
1251
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1252
+ /* @__PURE__ */ jsxRuntime.jsx(
1253
+ "div",
1254
+ {
1255
+ className: "tour-overlay fixed inset-0 z-40",
1256
+ onClick: dismissOnOverlayClick ? skipTour : void 0,
1257
+ role: "presentation",
1258
+ "aria-hidden": "true"
1259
+ }
1260
+ ),
1261
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs(
1262
+ "div",
1263
+ {
1264
+ className: "tour-tooltip pointer-events-auto",
1265
+ role: "dialog",
1266
+ "aria-modal": "true",
1267
+ "aria-label": "Tour step unavailable",
1268
+ children: [
1269
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-4 text-sm", style: { opacity: 0.7 }, children: "This step is currently unavailable." }),
1270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
1271
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: skipTour, className: "tour-button tour-button-secondary", "aria-label": "Skip tour", children: "Skip" }),
1272
+ !isLastStep && /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: next, className: "tour-button tour-button-primary", "aria-label": "Go to next step", children: "Next" })
1273
+ ] })
1274
+ ]
1275
+ }
1276
+ ) })
1277
+ ] }),
1278
+ document.body
1279
+ );
1280
+ }
1281
+ return reactDom.createPortal(
1282
+ /* @__PURE__ */ jsxRuntime.jsx(
1283
+ Spotlight,
1284
+ {
1285
+ targetElement,
1286
+ placement: currentStepData.placement || "bottom",
1287
+ title: currentStepData.title,
1288
+ content: currentStepData.content,
1289
+ onNext: next,
1290
+ onBack: back,
1291
+ onSkip: skipTour,
1292
+ onComplete: next,
1293
+ isFirstStep: currentStep === 0,
1294
+ isLastStep: currentStep === steps.length - 1,
1295
+ overlayClassName,
1296
+ tooltipClassName,
1297
+ buttonClassName,
1298
+ buttonContainerClassName,
1299
+ highlightTarget,
1300
+ skip,
1301
+ showProgress,
1302
+ currentStep,
1303
+ totalSteps: steps.length,
1304
+ accessibility,
1305
+ animation,
1306
+ tooltipOffset,
1307
+ buttonConfig,
1308
+ dismissOnOverlayClick
1309
+ }
1310
+ ),
1311
+ document.body
1312
+ );
1313
+ };
1314
+
1315
+ exports.Tour = Tour;
1316
+ exports.TourProvider = TourProvider;
1317
+ exports.tourManager = tourManager;
1318
+ exports.useTour = useTour2;
1319
+ //# sourceMappingURL=index.js.map
1320
+ //# sourceMappingURL=index.js.map