@pablo2410/shared-ui 0.20.0 → 0.22.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.
@@ -1,5 +1,5 @@
1
1
  // src/tour/TourShell.tsx
2
- import { useEffect, useRef } from "react";
2
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
3
3
  import {
4
4
  X,
5
5
  ChevronLeft,
@@ -7,7 +7,167 @@ import {
7
7
  ArrowRight,
8
8
  ExternalLink
9
9
  } from "lucide-react";
10
- import { jsx, jsxs } from "react/jsx-runtime";
10
+
11
+ // src/tour/useSpotlight.ts
12
+ import { useEffect, useRef, useState } from "react";
13
+ var RESIZE_DEBOUNCE_MS = 100;
14
+ function useSpotlight(selector) {
15
+ const [rect, setRect] = useState(null);
16
+ const [found, setFound] = useState(false);
17
+ const scrolledForSelector = useRef(null);
18
+ useEffect(() => {
19
+ if (!selector) {
20
+ setRect(null);
21
+ setFound(false);
22
+ return;
23
+ }
24
+ let resizeTimer;
25
+ let cancelled = false;
26
+ const measure = () => {
27
+ if (cancelled) return;
28
+ const el = document.querySelector(
29
+ `[data-tour-target="${selector}"]`
30
+ );
31
+ if (!el) {
32
+ setRect(null);
33
+ setFound(false);
34
+ return;
35
+ }
36
+ const domRect = el.getBoundingClientRect();
37
+ setRect({
38
+ top: domRect.top,
39
+ left: domRect.left,
40
+ width: domRect.width,
41
+ height: domRect.height
42
+ });
43
+ setFound(true);
44
+ if (scrolledForSelector.current !== selector) {
45
+ scrolledForSelector.current = selector;
46
+ el.scrollIntoView({ block: "center", behavior: "smooth" });
47
+ }
48
+ };
49
+ measure();
50
+ const initialFrame = requestAnimationFrame(measure);
51
+ const onResize = () => {
52
+ if (resizeTimer) clearTimeout(resizeTimer);
53
+ resizeTimer = setTimeout(measure, RESIZE_DEBOUNCE_MS);
54
+ };
55
+ const onScroll = () => measure();
56
+ window.addEventListener("resize", onResize);
57
+ window.addEventListener("scroll", onScroll, { capture: true });
58
+ const observer = new MutationObserver(measure);
59
+ observer.observe(document.body, {
60
+ childList: true,
61
+ subtree: true,
62
+ attributes: true
63
+ });
64
+ return () => {
65
+ cancelled = true;
66
+ cancelAnimationFrame(initialFrame);
67
+ if (resizeTimer) clearTimeout(resizeTimer);
68
+ window.removeEventListener("resize", onResize);
69
+ window.removeEventListener("scroll", onScroll, { capture: true });
70
+ observer?.disconnect();
71
+ };
72
+ }, [selector]);
73
+ return { rect, found };
74
+ }
75
+
76
+ // src/tour/spotlightMath.ts
77
+ function computeSpotlightFrame(target, viewport) {
78
+ const targetBottom = target.top + target.height;
79
+ const targetRight = target.left + target.width;
80
+ return {
81
+ // Full-width strip above the target.
82
+ top: {
83
+ top: 0,
84
+ left: 0,
85
+ width: viewport.width,
86
+ height: Math.max(0, target.top)
87
+ },
88
+ // Full-width strip below the target.
89
+ bottom: {
90
+ top: targetBottom,
91
+ left: 0,
92
+ width: viewport.width,
93
+ height: Math.max(0, viewport.height - targetBottom)
94
+ },
95
+ // Strip to the left of the target, spanning only the target's own row
96
+ // (the top/bottom strips already cover the rest of those rows).
97
+ left: {
98
+ top: target.top,
99
+ left: 0,
100
+ width: Math.max(0, target.left),
101
+ height: target.height
102
+ },
103
+ // Strip to the right of the target, same row-only span as `left`.
104
+ right: {
105
+ top: target.top,
106
+ left: targetRight,
107
+ width: Math.max(0, viewport.width - targetRight),
108
+ height: target.height
109
+ }
110
+ };
111
+ }
112
+ var FALLBACK_ORDER = {
113
+ bottom: ["bottom", "top", "right", "left"],
114
+ top: ["top", "bottom", "right", "left"],
115
+ right: ["right", "left", "bottom", "top"],
116
+ left: ["left", "right", "bottom", "top"]
117
+ };
118
+ var AUTO_ORDER = FALLBACK_ORDER.bottom;
119
+ var PANEL_GAP = 12;
120
+ function computePanelPosition(target, panelSize, viewport, placement = "auto") {
121
+ const order = placement === "auto" ? AUTO_ORDER : FALLBACK_ORDER[placement];
122
+ for (const candidate of order) {
123
+ const pos2 = placeAt(candidate, target, panelSize);
124
+ if (fits(pos2, panelSize, viewport)) {
125
+ return { ...pos2, placement: candidate };
126
+ }
127
+ }
128
+ const fallback = order[0];
129
+ const pos = placeAt(fallback, target, panelSize);
130
+ return {
131
+ top: clamp(pos.top, 0, Math.max(0, viewport.height - panelSize.height)),
132
+ left: clamp(pos.left, 0, Math.max(0, viewport.width - panelSize.width)),
133
+ placement: fallback
134
+ };
135
+ }
136
+ function placeAt(side, target, panelSize) {
137
+ const targetCenterX = target.left + target.width / 2;
138
+ const targetCenterY = target.top + target.height / 2;
139
+ switch (side) {
140
+ case "bottom":
141
+ return {
142
+ top: target.top + target.height + PANEL_GAP,
143
+ left: targetCenterX - panelSize.width / 2
144
+ };
145
+ case "top":
146
+ return {
147
+ top: target.top - panelSize.height - PANEL_GAP,
148
+ left: targetCenterX - panelSize.width / 2
149
+ };
150
+ case "right":
151
+ return {
152
+ top: targetCenterY - panelSize.height / 2,
153
+ left: target.left + target.width + PANEL_GAP
154
+ };
155
+ case "left":
156
+ return {
157
+ top: targetCenterY - panelSize.height / 2,
158
+ left: target.left - panelSize.width - PANEL_GAP
159
+ };
160
+ }
161
+ }
162
+ function fits(pos, panelSize, viewport) {
163
+ return pos.top >= 0 && pos.left >= 0 && pos.top + panelSize.height <= viewport.height && pos.left + panelSize.width <= viewport.width;
164
+ }
165
+ function clamp(value, min, max) {
166
+ return Math.min(Math.max(value, min), max);
167
+ }
168
+
169
+ // src/tour/TourShell.tsx
170
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
171
  var defaultSkin = {
12
172
  accent: "#8C34E9",
13
173
  overlayBg: "rgba(10,14,26,0.72)",
@@ -17,6 +177,7 @@ var defaultSkin = {
17
177
  bodyText: "#c0c6d0",
18
178
  softText: "#8890a0"
19
179
  };
180
+ var SPOTLIGHT_PANEL_SIZE = { width: 320, height: 280 };
20
181
  function TourShell({
21
182
  step,
22
183
  index,
@@ -30,19 +191,124 @@ function TourShell({
30
191
  narrationPending,
31
192
  children,
32
193
  skin = defaultSkin,
33
- orb
194
+ orb,
195
+ onSpotlightTargetActivated
34
196
  }) {
35
- const panelRef = useRef(null);
36
- useEffect(() => {
197
+ const panelRef = useRef2(null);
198
+ const { rect, found } = useSpotlight(step.spotlight?.selector ?? null);
199
+ const isSpotlight = Boolean(step.spotlight) && found && rect !== null;
200
+ useEffect2(() => {
37
201
  const onKeyDown = (e) => {
38
202
  if (e.key === "Escape") onDismiss();
39
203
  };
40
204
  document.addEventListener("keydown", onKeyDown);
41
205
  return () => document.removeEventListener("keydown", onKeyDown);
42
206
  }, [onDismiss]);
43
- useEffect(() => {
207
+ useEffect2(() => {
44
208
  panelRef.current?.focus();
45
209
  }, [step.id]);
210
+ useEffect2(() => {
211
+ if (!isSpotlight || !onSpotlightTargetActivated || !step.spotlight) return;
212
+ const selector = step.spotlight.selector;
213
+ const onClickCapture = (e) => {
214
+ const target = e.target;
215
+ if (target?.closest(`[data-tour-target="${selector}"]`)) {
216
+ onSpotlightTargetActivated();
217
+ }
218
+ };
219
+ document.addEventListener("click", onClickCapture, { capture: true });
220
+ return () => document.removeEventListener("click", onClickCapture, {
221
+ capture: true
222
+ });
223
+ }, [isSpotlight, onSpotlightTargetActivated, step.spotlight]);
224
+ if (isSpotlight && rect) {
225
+ const viewport = { width: window.innerWidth, height: window.innerHeight };
226
+ const frame = computeSpotlightFrame(rect, viewport);
227
+ const panelPos = computePanelPosition(
228
+ rect,
229
+ SPOTLIGHT_PANEL_SIZE,
230
+ viewport,
231
+ step.spotlight?.placement ?? "auto"
232
+ );
233
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
234
+ ["top", "bottom", "left", "right"].map((side) => {
235
+ const segment = frame[side];
236
+ if (segment.width <= 0 || segment.height <= 0) return null;
237
+ return /* @__PURE__ */ jsx(
238
+ "div",
239
+ {
240
+ className: "fixed z-50",
241
+ style: {
242
+ top: segment.top,
243
+ left: segment.left,
244
+ width: segment.width,
245
+ height: segment.height,
246
+ background: skin.overlayBg
247
+ },
248
+ onClick: onDismiss
249
+ },
250
+ side
251
+ );
252
+ }),
253
+ /* @__PURE__ */ jsx(
254
+ "div",
255
+ {
256
+ className: "fixed z-50 rounded-xl pointer-events-none transition-all",
257
+ style: {
258
+ top: rect.top - 4,
259
+ left: rect.left - 4,
260
+ width: rect.width + 8,
261
+ height: rect.height + 8,
262
+ boxShadow: `0 0 0 2px ${skin.accent}, 0 0 24px 4px ${skin.accent}66`
263
+ }
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsxs(
267
+ "div",
268
+ {
269
+ ref: panelRef,
270
+ tabIndex: -1,
271
+ role: "dialog",
272
+ "aria-modal": "true",
273
+ "aria-label": step.label,
274
+ className: "fixed z-50 rounded-xl overflow-hidden flex flex-col outline-none",
275
+ style: {
276
+ top: panelPos.top,
277
+ left: panelPos.left,
278
+ width: SPOTLIGHT_PANEL_SIZE.width,
279
+ maxHeight: SPOTLIGHT_PANEL_SIZE.height,
280
+ background: skin.panelBg,
281
+ border: skin.panelBorder
282
+ },
283
+ children: [
284
+ /* @__PURE__ */ jsx(TourShellHeader, { step, orb, onDismiss }),
285
+ /* @__PURE__ */ jsx(TourShellStepDots, { total, index, skin }),
286
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto px-4 py-3 space-y-3", children: /* @__PURE__ */ jsx(
287
+ TourShellBody,
288
+ {
289
+ step,
290
+ narration,
291
+ narrationPending,
292
+ skin,
293
+ children
294
+ }
295
+ ) }),
296
+ /* @__PURE__ */ jsx(
297
+ TourShellFooter,
298
+ {
299
+ isFirst,
300
+ isLast,
301
+ onNext,
302
+ onBack,
303
+ onDismiss,
304
+ skin
305
+ }
306
+ )
307
+ ]
308
+ }
309
+ )
310
+ ] });
311
+ }
46
312
  return /* @__PURE__ */ jsx(
47
313
  "div",
48
314
  {
@@ -62,140 +328,27 @@ function TourShell({
62
328
  className: "w-full max-w-md rounded-xl overflow-hidden flex flex-col max-h-[85vh] outline-none",
63
329
  style: { background: skin.panelBg, border: skin.panelBorder },
64
330
  children: [
65
- /* @__PURE__ */ jsxs(
66
- "div",
331
+ /* @__PURE__ */ jsx(TourShellHeader, { step, orb, onDismiss }),
332
+ /* @__PURE__ */ jsx(TourShellStepDots, { total, index, skin }),
333
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto px-5 py-4 space-y-4", children: /* @__PURE__ */ jsx(
334
+ TourShellBody,
67
335
  {
68
- className: "flex items-center justify-between gap-3 px-5 py-4 shrink-0",
69
- style: {
70
- background: "linear-gradient(135deg, #2a1450 0%, #1a0e33 100%)"
71
- },
72
- children: [
73
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
74
- orb,
75
- /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-white truncate", children: step.label })
76
- ] }),
77
- /* @__PURE__ */ jsx(
78
- "button",
79
- {
80
- onClick: onDismiss,
81
- className: "p-1 rounded hover:bg-white/10 transition-colors shrink-0",
82
- title: "Skip tour",
83
- children: /* @__PURE__ */ jsx(X, { size: 16, className: "text-white/70" })
84
- }
85
- )
86
- ]
336
+ step,
337
+ narration,
338
+ narrationPending,
339
+ skin,
340
+ children
87
341
  }
88
- ),
342
+ ) }),
89
343
  /* @__PURE__ */ jsx(
90
- "div",
91
- {
92
- className: "flex items-center gap-1.5 px-5 py-3 shrink-0",
93
- style: { borderBottom: skin.panelBorder },
94
- children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx(
95
- "span",
96
- {
97
- className: "h-1.5 rounded-full transition-all",
98
- style: {
99
- width: i === index ? 20 : 8,
100
- background: i === index ? skin.accent : skin.mutedText,
101
- opacity: i === index ? 1 : 0.4
102
- }
103
- },
104
- i
105
- ))
106
- }
107
- ),
108
- /* @__PURE__ */ jsxs("div", { className: "overflow-y-auto px-5 py-4 space-y-4", children: [
109
- /* @__PURE__ */ jsx(
110
- "p",
111
- {
112
- className: "text-[13px] leading-relaxed transition-opacity",
113
- style: {
114
- color: skin.bodyText,
115
- opacity: narrationPending ? 0.7 : 1
116
- },
117
- children: narration
118
- }
119
- ),
120
- step.highlights && step.highlights.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: step.highlights.map((h, i) => /* @__PURE__ */ jsxs(
121
- "li",
122
- {
123
- className: "text-[12.5px] leading-relaxed pl-3 relative",
124
- style: { color: skin.softText },
125
- children: [
126
- /* @__PURE__ */ jsx(
127
- "span",
128
- {
129
- className: "absolute left-0",
130
- style: { color: skin.mutedText },
131
- children: "\u2022"
132
- }
133
- ),
134
- h
135
- ]
136
- },
137
- i
138
- )) }),
139
- step.action && /* @__PURE__ */ jsxs(
140
- "a",
141
- {
142
- href: step.action.href,
143
- target: step.action.external ? "_blank" : void 0,
144
- rel: step.action.external ? "noopener noreferrer" : void 0,
145
- className: "inline-flex items-center gap-1.5 px-3.5 py-2 rounded-lg text-[13px] font-semibold transition-opacity hover:opacity-90",
146
- style: { background: `${skin.accent}20`, color: skin.accent },
147
- children: [
148
- step.action.label,
149
- step.action.external ? /* @__PURE__ */ jsx(ExternalLink, { size: 13 }) : /* @__PURE__ */ jsx(ArrowRight, { size: 13 })
150
- ]
151
- }
152
- ),
153
- children
154
- ] }),
155
- /* @__PURE__ */ jsxs(
156
- "div",
344
+ TourShellFooter,
157
345
  {
158
- className: "flex items-center justify-between gap-2 px-5 py-3.5 shrink-0",
159
- style: { borderTop: skin.panelBorder },
160
- children: [
161
- /* @__PURE__ */ jsx(
162
- "button",
163
- {
164
- onClick: onDismiss,
165
- className: "text-[12px] font-medium transition-colors hover:underline",
166
- style: { color: skin.mutedText },
167
- children: "Skip tour"
168
- }
169
- ),
170
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
171
- !isFirst && /* @__PURE__ */ jsxs(
172
- "button",
173
- {
174
- onClick: onBack,
175
- className: "flex items-center gap-1 px-3 py-1.5 rounded-md text-[12.5px] font-medium transition-colors",
176
- style: { color: skin.bodyText, border: skin.panelBorder },
177
- children: [
178
- /* @__PURE__ */ jsx(ChevronLeft, { size: 13 }),
179
- "Back"
180
- ]
181
- }
182
- ),
183
- /* @__PURE__ */ jsxs(
184
- "button",
185
- {
186
- onClick: onNext,
187
- className: "flex items-center gap-1 px-3.5 py-1.5 rounded-md text-[12.5px] font-bold text-white transition-opacity hover:opacity-90",
188
- style: {
189
- background: "linear-gradient(135deg, #8C34E9 0%, #5B1FA6 100%)"
190
- },
191
- children: [
192
- isLast ? "Done" : "Next",
193
- !isLast && /* @__PURE__ */ jsx(ChevronRight, { size: 13 })
194
- ]
195
- }
196
- )
197
- ] })
198
- ]
346
+ isFirst,
347
+ isLast,
348
+ onNext,
349
+ onBack,
350
+ onDismiss,
351
+ skin
199
352
  }
200
353
  )
201
354
  ]
@@ -204,9 +357,180 @@ function TourShell({
204
357
  }
205
358
  );
206
359
  }
360
+ function TourShellHeader({
361
+ step,
362
+ orb,
363
+ onDismiss
364
+ }) {
365
+ return /* @__PURE__ */ jsxs(
366
+ "div",
367
+ {
368
+ className: "flex items-center justify-between gap-3 px-5 py-4 shrink-0",
369
+ style: {
370
+ background: "linear-gradient(135deg, #2a1450 0%, #1a0e33 100%)"
371
+ },
372
+ children: [
373
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [
374
+ orb,
375
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-white truncate", children: step.label })
376
+ ] }),
377
+ /* @__PURE__ */ jsx(
378
+ "button",
379
+ {
380
+ onClick: onDismiss,
381
+ className: "p-1 rounded hover:bg-white/10 transition-colors shrink-0",
382
+ title: "Skip tour",
383
+ children: /* @__PURE__ */ jsx(X, { size: 16, className: "text-white/70" })
384
+ }
385
+ )
386
+ ]
387
+ }
388
+ );
389
+ }
390
+ function TourShellStepDots({
391
+ total,
392
+ index,
393
+ skin
394
+ }) {
395
+ return /* @__PURE__ */ jsx(
396
+ "div",
397
+ {
398
+ className: "flex items-center gap-1.5 px-5 py-3 shrink-0",
399
+ style: { borderBottom: skin.panelBorder },
400
+ children: Array.from({ length: total }, (_, i) => /* @__PURE__ */ jsx(
401
+ "span",
402
+ {
403
+ className: "h-1.5 rounded-full transition-all",
404
+ style: {
405
+ width: i === index ? 20 : 8,
406
+ background: i === index ? skin.accent : skin.mutedText,
407
+ opacity: i === index ? 1 : 0.4
408
+ }
409
+ },
410
+ i
411
+ ))
412
+ }
413
+ );
414
+ }
415
+ function TourShellBody({
416
+ step,
417
+ narration,
418
+ narrationPending,
419
+ skin,
420
+ children
421
+ }) {
422
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
423
+ /* @__PURE__ */ jsx(
424
+ "p",
425
+ {
426
+ className: "text-[13px] leading-relaxed transition-opacity",
427
+ style: {
428
+ color: skin.bodyText,
429
+ opacity: narrationPending ? 0.7 : 1
430
+ },
431
+ children: narration
432
+ }
433
+ ),
434
+ step.features && step.features.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: step.features.map((f, i) => /* @__PURE__ */ jsxs("li", { className: "text-[12.5px] leading-relaxed", children: [
435
+ /* @__PURE__ */ jsx("span", { className: "font-semibold", style: { color: skin.accent }, children: f.name }),
436
+ /* @__PURE__ */ jsxs("span", { style: { color: skin.softText }, children: [
437
+ " \u2014 ",
438
+ f.detail
439
+ ] })
440
+ ] }, i)) }) : step.highlights && step.highlights.length > 0 && /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: step.highlights.map((h, i) => /* @__PURE__ */ jsxs(
441
+ "li",
442
+ {
443
+ className: "text-[12.5px] leading-relaxed pl-3 relative",
444
+ style: { color: skin.softText },
445
+ children: [
446
+ /* @__PURE__ */ jsx(
447
+ "span",
448
+ {
449
+ className: "absolute left-0",
450
+ style: { color: skin.mutedText },
451
+ children: "\u2022"
452
+ }
453
+ ),
454
+ h
455
+ ]
456
+ },
457
+ i
458
+ )) }),
459
+ step.action && /* @__PURE__ */ jsxs(
460
+ "a",
461
+ {
462
+ href: step.action.href,
463
+ target: step.action.external ? "_blank" : void 0,
464
+ rel: step.action.external ? "noopener noreferrer" : void 0,
465
+ className: "inline-flex items-center gap-1.5 px-3.5 py-2 rounded-lg text-[13px] font-semibold transition-opacity hover:opacity-90",
466
+ style: { background: `${skin.accent}20`, color: skin.accent },
467
+ children: [
468
+ step.action.label,
469
+ step.action.external ? /* @__PURE__ */ jsx(ExternalLink, { size: 13 }) : /* @__PURE__ */ jsx(ArrowRight, { size: 13 })
470
+ ]
471
+ }
472
+ ),
473
+ children
474
+ ] });
475
+ }
476
+ function TourShellFooter({
477
+ isFirst,
478
+ isLast,
479
+ onNext,
480
+ onBack,
481
+ onDismiss,
482
+ skin
483
+ }) {
484
+ return /* @__PURE__ */ jsxs(
485
+ "div",
486
+ {
487
+ className: "flex items-center justify-between gap-2 px-5 py-3.5 shrink-0",
488
+ style: { borderTop: skin.panelBorder },
489
+ children: [
490
+ /* @__PURE__ */ jsx(
491
+ "button",
492
+ {
493
+ onClick: onDismiss,
494
+ className: "text-[12px] font-medium transition-colors hover:underline",
495
+ style: { color: skin.mutedText },
496
+ children: "Skip tour"
497
+ }
498
+ ),
499
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
500
+ !isFirst && /* @__PURE__ */ jsxs(
501
+ "button",
502
+ {
503
+ onClick: onBack,
504
+ className: "flex items-center gap-1 px-3 py-1.5 rounded-md text-[12.5px] font-medium transition-colors",
505
+ style: { color: skin.bodyText, border: skin.panelBorder },
506
+ children: [
507
+ /* @__PURE__ */ jsx(ChevronLeft, { size: 13 }),
508
+ "Back"
509
+ ]
510
+ }
511
+ ),
512
+ /* @__PURE__ */ jsxs(
513
+ "button",
514
+ {
515
+ onClick: onNext,
516
+ className: "flex items-center gap-1 px-3.5 py-1.5 rounded-md text-[12.5px] font-bold text-white transition-opacity hover:opacity-90",
517
+ style: {
518
+ background: "linear-gradient(135deg, #8C34E9 0%, #5B1FA6 100%)"
519
+ },
520
+ children: [
521
+ isLast ? "Done" : "Next",
522
+ !isLast && /* @__PURE__ */ jsx(ChevronRight, { size: 13 })
523
+ ]
524
+ }
525
+ )
526
+ ] })
527
+ ]
528
+ }
529
+ );
530
+ }
207
531
 
208
532
  // src/tour/useTour.ts
209
- import { useCallback, useState } from "react";
533
+ import { useCallback, useState as useState2 } from "react";
210
534
 
211
535
  // src/tour/tourMachine.ts
212
536
  function resolveInitialIndex(steps, initialStepId) {
@@ -233,7 +557,7 @@ function useTour(steps, opts = {}) {
233
557
  if (steps.length === 0) {
234
558
  throw new Error("useTour requires at least one step");
235
559
  }
236
- const [index, setIndex] = useState(
560
+ const [index, setIndex] = useState2(
237
561
  () => resolveInitialIndex(steps, opts.initialStepId)
238
562
  );
239
563
  const next = useCallback(() => {
@@ -277,6 +601,9 @@ function useTour(steps, opts = {}) {
277
601
  }
278
602
  export {
279
603
  TourShell,
604
+ computePanelPosition,
605
+ computeSpotlightFrame,
606
+ useSpotlight,
280
607
  useTour
281
608
  };
282
609
  //# sourceMappingURL=index.js.map