torchlit 0.2.1 → 0.2.3

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.
@@ -55,12 +55,21 @@ export declare class TorchlitOverlay extends LitElement {
55
55
  * Resolves as soon as `deepQuery` finds the target, or after `timeout` ms.
56
56
  */
57
57
  private waitForTarget;
58
+ /**
59
+ * Whether the target element (plus its tooltip) fits comfortably inside the
60
+ * viewport. When it doesn't, we only need the top of the target visible —
61
+ * the tooltip tracks scroll, so the user can explore the rest naturally.
62
+ */
63
+ private fitsInViewport;
58
64
  private handleTourChange;
59
65
  /**
60
66
  * Scroll an element into view and wait for the scroll to finish.
61
- * For tall elements (> 60% of viewport), scrolls to the top so the user
62
- * sees the start of the element plus the tooltip. For smaller elements,
63
- * centers them in the viewport.
67
+ *
68
+ * Small elements that fit (with their tooltip) are centered in the viewport.
69
+ * Large elements are scrolled with a **placement-aware** offset so there is
70
+ * room for the tooltip on the preferred side. When `placement` is `'top'`,
71
+ * we reserve enough space above the target for the tooltip; for other
72
+ * placements the tooltip goes below or beside, so a smaller offset suffices.
64
73
  */
65
74
  private scrollAndSettle;
66
75
  private handleResize;
@@ -84,6 +93,8 @@ export declare class TorchlitOverlay extends LitElement {
84
93
  private getArrowOffset;
85
94
  render(): import("lit").TemplateResult<1>;
86
95
  private renderProgressDots;
96
+ private renderFooter;
97
+ private renderAutoProgress;
87
98
  private renderCenteredStep;
88
99
  }
89
100
  declare global {
@@ -1 +1 @@
1
- {"version":3,"file":"tour-overlay.d.ts","sourceRoot":"","sources":["../src/tour-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAIrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAWrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAG7C,OAAgB,MAAM,0BAkUpB;IAIF;;;OAGG;IAEH,OAAO,EAAG,WAAW,CAAC;IAEb,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,OAAO,CAAS;IAEjC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,qBAAqB,CAA2B;IACxD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAuB;IAIlC,iBAAiB;IAUjB,oBAAoB;IAUpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAoB9C;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,gBAAgB;IAUxB;;;OAGG;IACH,OAAO,CAAC,aAAa;YAsCP,gBAAgB;IAwF9B;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAmCvB,OAAO,CAAC,YAAY,CAIlB;IAEF,wEAAwE;IACxE,OAAO,CAAC,YAAY,CAQlB;IAEF,OAAO,CAAC,aAAa,CAmBnB;IAEF,OAAO,CAAC,mBAAmB,CAGzB;IAIF,OAAO,CAAC,SAAS;IA6BjB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAsCrB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;IAUrB;;;OAGG;IACH,OAAO,CAAC,cAAc;IA2Bb,MAAM;IA+Hf,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,kBAAkB;CA8E3B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,eAAe,CAAC;KACrC;CACF"}
1
+ {"version":3,"file":"tour-overlay.d.ts","sourceRoot":"","sources":["../src/tour-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,KAAK,CAAC;AAIrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAWrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,eAAgB,SAAQ,UAAU;IAG7C,OAAgB,MAAM,0BAkUpB;IAIF;;;OAGG;IAEH,OAAO,EAAG,WAAW,CAAC;IAEb,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,OAAO,CAAS;IAEjC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,qBAAqB,CAA2B;IACxD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAuB;IAIlC,iBAAiB;IAUjB,oBAAoB;IAUpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAoB9C;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,gBAAgB;IAUxB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAsCrB;;;;OAIG;IACH,OAAO,CAAC,cAAc;YAMR,gBAAgB;IA+F9B;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAmDvB,OAAO,CAAC,YAAY,CAIlB;IAEF,wEAAwE;IACxE,OAAO,CAAC,YAAY,CAQlB;IAEF,OAAO,CAAC,aAAa,CAmBnB;IAEF,OAAO,CAAC,mBAAmB,CAGzB;IAIF,OAAO,CAAC,SAAS;IA6BjB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAsCrB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;IAUrB;;;OAGG;IACH,OAAO,CAAC,cAAc;IA2Bb,MAAM;IAiFf,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,YAAY;IAoDpB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,kBAAkB;CAyC3B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,eAAe,CAAC;KACrC;CACF"}
@@ -164,6 +164,15 @@ let TorchlitOverlay = class extends LitElement {
164
164
  }, timeout);
165
165
  });
166
166
  }
167
+ /* ── Scroll helpers ─────────────────────────────── */
168
+ /**
169
+ * Whether the target element (plus its tooltip) fits comfortably inside the
170
+ * viewport. When it doesn't, we only need the top of the target visible —
171
+ * the tooltip tracks scroll, so the user can explore the rest naturally.
172
+ */
173
+ fitsInViewport(el) {
174
+ return el.getBoundingClientRect().height + TOOLTIP_H_MAX + GAP * 2 < window.innerHeight;
175
+ }
167
176
  /* ── Tour state handler ─────────────────────────── */
168
177
  async handleTourChange(snapshot) {
169
178
  this.clearAutoAdvance();
@@ -217,10 +226,12 @@ let TorchlitOverlay = class extends LitElement {
217
226
  if (this.snapshot?.targetElement) {
218
227
  const rect = this.snapshot.targetElement.getBoundingClientRect();
219
228
  const vh = window.innerHeight;
220
- const isTall = rect.height > vh * 0.6;
221
- const inView = isTall ? rect.top >= 0 && rect.top < vh * 0.5 : rect.top >= 0 && rect.bottom <= vh && rect.left >= 0 && rect.right <= window.innerWidth;
229
+ const fits = this.fitsInViewport(this.snapshot.targetElement);
230
+ const placement = this.snapshot.step.placement;
231
+ const PADDING = this.service?.spotlightPadding ?? 10;
232
+ const inView = fits ? rect.top >= 0 && rect.bottom <= vh && rect.left >= 0 && rect.right <= window.innerWidth : placement === "top" ? rect.top >= TOOLTIP_H_MAX + GAP + PADDING && rect.top < vh : rect.top >= 0 && rect.top < vh;
222
233
  if (!inView) {
223
- await this.scrollAndSettle(this.snapshot.targetElement);
234
+ await this.scrollAndSettle(this.snapshot.targetElement, placement);
224
235
  this.snapshot = this.service.getSnapshot();
225
236
  }
226
237
  }
@@ -233,14 +244,24 @@ let TorchlitOverlay = class extends LitElement {
233
244
  }
234
245
  /**
235
246
  * Scroll an element into view and wait for the scroll to finish.
236
- * For tall elements (> 60% of viewport), scrolls to the top so the user
237
- * sees the start of the element plus the tooltip. For smaller elements,
238
- * centers them in the viewport.
247
+ *
248
+ * Small elements that fit (with their tooltip) are centered in the viewport.
249
+ * Large elements are scrolled with a **placement-aware** offset so there is
250
+ * room for the tooltip on the preferred side. When `placement` is `'top'`,
251
+ * we reserve enough space above the target for the tooltip; for other
252
+ * placements the tooltip goes below or beside, so a smaller offset suffices.
239
253
  */
240
- scrollAndSettle(el) {
254
+ scrollAndSettle(el, placement) {
241
255
  const vh = window.innerHeight;
242
- const isTall = el.getBoundingClientRect().height > vh * 0.6;
243
- el.scrollIntoView({ behavior: "smooth", block: isTall ? "start" : "center", inline: "nearest" });
256
+ const rect = el.getBoundingClientRect();
257
+ if (this.fitsInViewport(el)) {
258
+ el.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
259
+ } else {
260
+ const PADDING = this.service?.spotlightPadding ?? 10;
261
+ const desiredTop = placement === "top" ? TOOLTIP_H_MAX + GAP + PADDING : vh * 0.15;
262
+ const scrollTarget = window.scrollY + rect.top - desiredTop;
263
+ window.scrollTo({ top: Math.max(0, scrollTarget), behavior: "smooth" });
264
+ }
244
265
  return new Promise((resolve) => {
245
266
  let lastTop = el.getBoundingClientRect().top;
246
267
  let stableFrames = 0;
@@ -466,78 +487,87 @@ let TorchlitOverlay = class extends LitElement {
466
487
  <div class="tour-message" id="tour-desc">${step.message}</div>
467
488
 
468
489
  ${this.renderProgressDots(stepIndex, totalSteps)}
469
-
470
- <div class="tour-footer">
471
- <button
472
- class="tour-skip"
473
- aria-label="Skip tour"
474
- @click=${() => {
490
+ ${this.renderFooter(stepIndex, totalSteps)}
491
+ ${this.renderAutoProgress(step, stepIndex)}
492
+ </div>
493
+ `;
494
+ }
495
+ renderProgressDots(current, total) {
496
+ if (total <= 1) return nothing;
497
+ return html`
498
+ <div class="tour-progress" role="group" aria-label="Tour progress">
499
+ ${Array.from({ length: total }, (_, i) => html`
500
+ <div
501
+ class="tour-dot ${i === current ? "active" : i < current ? "completed" : ""}"
502
+ role="presentation"
503
+ ></div>
504
+ `)}
505
+ </div>
506
+ `;
507
+ }
508
+ renderFooter(stepIndex, totalSteps, finishLabel = "Finish", finishAriaLabel = "Finish tour", showNavIcons = true) {
509
+ return html`
510
+ <div class="tour-footer">
511
+ <button
512
+ class="tour-skip"
513
+ aria-label="Skip tour"
514
+ @click=${() => {
475
515
  this.clearAutoAdvance();
476
516
  this.service.skipTour();
477
517
  }}
478
- >
479
- Skip tour
480
- </button>
481
- <div class="tour-nav">
482
- ${stepIndex > 0 ? html`
483
- <button
484
- class="tour-btn"
485
- aria-label="Go to previous step"
486
- @click=${() => {
518
+ >
519
+ Skip tour
520
+ </button>
521
+ <div class="tour-nav">
522
+ ${stepIndex > 0 ? html`
523
+ <button
524
+ class="tour-btn"
525
+ aria-label="Go to previous step"
526
+ @click=${() => {
487
527
  this.clearAutoAdvance();
488
528
  this.service.prevStep();
489
529
  }}
490
- >
530
+ >
531
+ ${showNavIcons ? html`
491
532
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
492
533
  <polyline points="15 18 9 12 15 6"></polyline>
493
534
  </svg>
494
- Back
495
- </button>
496
- ` : nothing}
497
- <button
498
- class="tour-btn primary"
499
- aria-label="${stepIndex === totalSteps - 1 ? "Finish tour" : "Go to next step"}"
500
- @click=${() => {
535
+ ` : nothing}
536
+ Back
537
+ </button>
538
+ ` : nothing}
539
+ <button
540
+ class="tour-btn primary"
541
+ aria-label="${stepIndex === totalSteps - 1 ? finishAriaLabel : "Go to next step"}"
542
+ @click=${() => {
501
543
  this.clearAutoAdvance();
502
544
  this.service.nextStep();
503
545
  }}
504
- >
505
- ${stepIndex === totalSteps - 1 ? "Finish" : "Next"}
506
- ${stepIndex < totalSteps - 1 ? html`
507
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
508
- <polyline points="9 18 15 12 9 6"></polyline>
509
- </svg>
510
- ` : html`
511
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
512
- <polyline points="20 6 9 17 4 12"></polyline>
513
- </svg>
514
- `}
515
- </button>
516
- </div>
546
+ >
547
+ ${stepIndex === totalSteps - 1 ? finishLabel : "Next"}
548
+ ${stepIndex < totalSteps - 1 ? html`
549
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
550
+ <polyline points="9 18 15 12 9 6"></polyline>
551
+ </svg>
552
+ ` : showNavIcons ? html`
553
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
554
+ <polyline points="20 6 9 17 4 12"></polyline>
555
+ </svg>
556
+ ` : nothing}
557
+ </button>
517
558
  </div>
518
-
519
- ${step.autoAdvance ? keyed(stepIndex, html`
520
- <div
521
- class="tour-auto-progress"
522
- style="animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;"
523
- aria-hidden="true"
524
- ></div>
525
- `) : nothing}
526
559
  </div>
527
560
  `;
528
561
  }
529
- renderProgressDots(current, total) {
530
- if (total <= 1) return nothing;
531
- return html`
532
- <div class="tour-progress" role="group" aria-label="Tour progress">
533
- ${Array.from({ length: total }, (_, i) => html`
534
- <div
535
- class="tour-dot ${i === current ? "active" : i < current ? "completed" : ""}"
536
- role="presentation"
537
- ></div>
538
- `)}
539
- </div>
540
- `;
562
+ renderAutoProgress(step, stepIndex) {
563
+ if (!step.autoAdvance) return nothing;
564
+ return keyed(stepIndex, html`
565
+ <div
566
+ class="tour-auto-progress"
567
+ style="animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;"
568
+ aria-hidden="true"
569
+ ></div>
570
+ `);
541
571
  }
542
572
  renderCenteredStep(step, stepIndex, totalSteps) {
543
573
  const stepLabel = `Step ${stepIndex + 1} of ${totalSteps}: ${step.title}`;
@@ -574,54 +604,8 @@ let TorchlitOverlay = class extends LitElement {
574
604
  <div class="tour-message" id="tour-desc-center">${step.message}</div>
575
605
 
576
606
  ${this.renderProgressDots(stepIndex, totalSteps)}
577
-
578
- <div class="tour-footer">
579
- <button
580
- class="tour-skip"
581
- aria-label="Skip tour"
582
- @click=${() => {
583
- this.clearAutoAdvance();
584
- this.service.skipTour();
585
- }}
586
- >
587
- Skip tour
588
- </button>
589
- <div class="tour-nav">
590
- ${stepIndex > 0 ? html`
591
- <button
592
- class="tour-btn"
593
- aria-label="Go to previous step"
594
- @click=${() => {
595
- this.clearAutoAdvance();
596
- this.service.prevStep();
597
- }}
598
- >Back</button>
599
- ` : nothing}
600
- <button
601
- class="tour-btn primary"
602
- aria-label="${stepIndex === totalSteps - 1 ? "Start the tour" : "Go to next step"}"
603
- @click=${() => {
604
- this.clearAutoAdvance();
605
- this.service.nextStep();
606
- }}
607
- >
608
- ${stepIndex === totalSteps - 1 ? "Let's go!" : "Next"}
609
- ${stepIndex < totalSteps - 1 ? html`
610
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
611
- <polyline points="9 18 15 12 9 6"></polyline>
612
- </svg>
613
- ` : nothing}
614
- </button>
615
- </div>
616
- </div>
617
-
618
- ${step.autoAdvance ? keyed(stepIndex, html`
619
- <div
620
- class="tour-auto-progress"
621
- style="animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;"
622
- aria-hidden="true"
623
- ></div>
624
- `) : nothing}
607
+ ${this.renderFooter(stepIndex, totalSteps, "Let's go!", "Start the tour", false)}
608
+ ${this.renderAutoProgress(step, stepIndex)}
625
609
  </div>
626
610
  `;
627
611
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tour-overlay.js","sources":["../src/tour-overlay.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { keyed } from 'lit/directives/keyed.js';\nimport { deepQuery } from './utils/deep-query.js';\nimport type { TourService } from './tour-service.js';\nimport type { TourStep, TourSnapshot, TourPlacement } from './types.js';\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nconst TOOLTIP_W = 320;\nconst TOOLTIP_H_MAX = 270; // conservative max height for clamp & flip checks\nconst GAP = 16;\nconst VIEWPORT_MARGIN = 24;\nconst MUTATION_TIMEOUT = 3000;\n\n/**\n * `<torchlit-overlay>` — Full-screen overlay that renders a spotlight cutout\n * around the current tour target, a tooltip with title / message / progress,\n * and navigation controls.\n *\n * Wire it to a `TourService` instance via the `service` property:\n *\n * ```html\n * <torchlit-overlay></torchlit-overlay>\n * ```\n * ```js\n * document.querySelector('torchlit-overlay').service = myTourService;\n * ```\n *\n * @fires tour-route-change - When a step has a `route` property, dispatched\n * with `{ route: string }` so the host app can switch views.\n *\n * @csspart backdrop - The semi-transparent overlay behind the spotlight.\n * @csspart spotlight - The cutout highlight around the target element.\n * @csspart tooltip - The floating tooltip card.\n * @csspart center-card - The centered card shown when there is no target.\n */\n@customElement('torchlit-overlay')\nexport class TorchlitOverlay extends LitElement {\n /* ── Styles ─────────────────────────────────────── */\n\n static override styles = css`\n :host {\n display: block;\n }\n\n /* ── Visually hidden (sr-only) ─────────────────── */\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ── Backdrop ──────────────────────────────────── */\n\n .tour-backdrop {\n position: fixed;\n inset: 0;\n z-index: 9998;\n pointer-events: auto;\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .tour-backdrop.visible {\n opacity: 1;\n }\n\n /* ── Spotlight (box-shadow cutout) ─────────────── */\n\n .tour-spotlight {\n position: fixed;\n z-index: 9999;\n border-radius: var(--tour-spotlight-radius, var(--radius-lg, 0.75rem));\n box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);\n transition: top 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n left 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n width 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n height 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n /* Subtle pulsing ring around spotlight */\n .tour-spotlight::after {\n content: '';\n position: absolute;\n inset: -4px;\n border-radius: inherit;\n border: 2px solid var(--tour-primary, var(--primary, #F26122));\n opacity: 0.5;\n animation: spotlightPulse 2s ease-in-out infinite;\n }\n\n @keyframes spotlightPulse {\n 0%, 100% { opacity: 0.3; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.01); }\n }\n\n /* ── Tooltip ───────────────────────────────────── */\n\n .tour-tooltip {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n width: 320px;\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n border-radius: var(--tour-tooltip-radius, var(--radius-lg, 0.75rem));\n box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.2),\n 0 8px 16px -4px rgba(0, 0, 0, 0.1);\n padding: 1.25rem;\n pointer-events: auto;\n opacity: 0;\n transform: translateY(8px) scale(0.96);\n transition: opacity 0.25s ease, transform 0.25s ease,\n top 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n left 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .tour-tooltip:focus {\n outline: none;\n }\n\n .tour-tooltip.visible {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n\n /* Arrow — position along edge is set via inline --arrow-offset */\n .tour-arrow {\n position: absolute;\n width: 12px;\n height: 12px;\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n transform: rotate(45deg);\n }\n\n /* tooltip is above target → arrow at bottom of tooltip pointing down */\n .tour-arrow.arrow-top {\n bottom: -7px;\n left: var(--arrow-offset, 50%);\n margin-left: -6px;\n border-top: none;\n border-left: none;\n }\n\n /* tooltip is below target → arrow at top of tooltip pointing up */\n .tour-arrow.arrow-bottom {\n top: -7px;\n left: var(--arrow-offset, 50%);\n margin-left: -6px;\n border-bottom: none;\n border-right: none;\n }\n\n /* tooltip is right of target → arrow on left edge pointing left */\n .tour-arrow.arrow-left {\n right: -7px;\n top: var(--arrow-offset, 50%);\n margin-top: -6px;\n border-bottom: none;\n border-left: none;\n }\n\n /* tooltip is left of target → arrow on right edge pointing right */\n .tour-arrow.arrow-right {\n left: -7px;\n top: var(--arrow-offset, 50%);\n margin-top: -6px;\n border-top: none;\n border-right: none;\n }\n\n /* ── Tooltip content ──────────────────────────── */\n\n .tour-step-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--tour-primary, var(--primary, #F26122));\n margin-bottom: 0.5rem;\n }\n\n .tour-title {\n margin: 0 0 0.375rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n line-height: 1.3;\n }\n\n .tour-message {\n margin: 0 0 1rem;\n font-size: 0.8125rem;\n color: var(--tour-muted-foreground, var(--muted-foreground, #737373));\n line-height: 1.55;\n }\n\n /* ── Progress dots ────────────────────────────── */\n\n .tour-progress {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n margin-bottom: 1rem;\n }\n\n .tour-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--tour-muted, var(--muted, #e5e5e5));\n transition: background 0.2s, transform 0.2s;\n }\n\n .tour-dot.active {\n background: var(--tour-primary, var(--primary, #F26122));\n transform: scale(1.3);\n }\n\n .tour-dot.completed {\n background: var(--tour-primary, var(--primary, #F26122));\n opacity: 0.5;\n }\n\n /* ── Auto-advance progress bar ────────────────── */\n\n .tour-auto-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n max-width: 100%;\n height: 3px;\n background: var(--tour-primary, var(--primary, #F26122));\n opacity: 0.7;\n border-radius: 0 0 var(--tour-tooltip-radius, var(--radius-lg, 0.75rem)) var(--tour-tooltip-radius, var(--radius-lg, 0.75rem));\n }\n\n @keyframes autoAdvanceFill {\n from { width: 0%; }\n to { width: 100%; }\n }\n\n /* ── Footer buttons ───────────────────────────── */\n\n .tour-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .tour-skip {\n font-size: 0.75rem;\n color: var(--tour-muted-foreground, var(--muted-foreground, #737373));\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem 0;\n transition: color 0.15s;\n }\n\n .tour-skip:hover {\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n }\n\n .tour-nav {\n display: flex;\n gap: 0.5rem;\n }\n\n .tour-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.4rem 0.875rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border-radius: var(--tour-btn-radius, var(--radius-md, 0.5rem));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n background: var(--tour-background, var(--background, #fff));\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .tour-btn:hover {\n background: var(--tour-muted, var(--muted, #f5f5f5));\n }\n\n .tour-btn:focus-visible {\n outline: 2px solid var(--tour-primary, var(--primary, #F26122));\n outline-offset: 2px;\n }\n\n .tour-btn.primary {\n background: var(--tour-primary, var(--primary, #F26122));\n color: var(--tour-primary-foreground, var(--primary-foreground, #fff));\n border-color: var(--tour-primary, var(--primary, #F26122));\n }\n\n .tour-btn.primary:hover {\n opacity: 0.9;\n }\n\n .tour-btn svg {\n width: 14px;\n height: 14px;\n }\n\n /* ── Welcome / no-target step ─────────────────── */\n\n .tour-center-card {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.96);\n width: 400px;\n max-width: calc(100vw - 2rem);\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n border-radius: var(--tour-card-radius, var(--radius-xl, 1rem));\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n padding: 2rem;\n text-align: center;\n pointer-events: auto;\n opacity: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .tour-center-card:focus {\n outline: none;\n }\n\n .tour-center-card.visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .tour-center-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 1rem;\n background: var(--tour-primary, var(--primary, #F26122));\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--tour-primary-foreground, var(--primary-foreground, #fff));\n }\n `;\n\n /* ── Properties ──────────────────────────────────── */\n\n /**\n * The `TourService` instance this overlay subscribes to.\n * Must be set before the overlay will render anything.\n */\n @property({ attribute: false })\n service!: TourService;\n\n @state() private snapshot: TourSnapshot | null = null;\n @state() private visible = false;\n\n private unsubscribe?: () => void;\n private previouslyFocused: HTMLElement | null = null;\n private autoAdvanceTimer: ReturnType<typeof setTimeout> | null = null;\n private lastResolvedPlacement: TourPlacement = 'bottom';\n private scrollRafId = 0;\n private savedScrollY = 0;\n private activeTourId: string | null = null;\n\n /* ── Lifecycle ──────────────────────────────────── */\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.service) {\n this.attachService();\n }\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('keydown', this.handleKeydown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.unsubscribe?.();\n this.clearAutoAdvance();\n if (this.scrollRafId) cancelAnimationFrame(this.scrollRafId);\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('keydown', this.handleKeydown);\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('service') && this.service) {\n this.unsubscribe?.();\n this.attachService();\n }\n\n if (this.visible && this.snapshot) {\n // Measure the actual tooltip and correct position for 'top' placement\n this.adjustTooltipPosition();\n\n // Focus the dialog container\n this.updateComplete.then(() => {\n const dialog = this.shadowRoot?.querySelector<HTMLElement>(\n '.tour-tooltip, .tour-center-card',\n );\n dialog?.focus();\n });\n }\n }\n\n /**\n * After rendering, measure the tooltip's actual height and correct\n * its position for 'top' placement (the only one that depends on\n * tooltip height). This eliminates hardcoded height estimates.\n */\n private adjustTooltipPosition() {\n if (this.lastResolvedPlacement !== 'top') return;\n\n const tooltip = this.shadowRoot?.querySelector<HTMLElement>('.tour-tooltip');\n const targetRect = this.snapshot?.targetRect;\n if (!tooltip || !targetRect) return;\n\n const PADDING = this.service?.spotlightPadding ?? 10;\n const actualHeight = tooltip.getBoundingClientRect().height;\n const correctTop = targetRect.top - PADDING - GAP - actualHeight;\n const clampedTop = Math.max(VIEWPORT_MARGIN, correctTop);\n\n tooltip.style.top = `${clampedTop}px`;\n }\n\n private attachService() {\n this.unsubscribe = this.service.subscribe(snap => this.handleTourChange(snap));\n }\n\n /* ── Auto-advance ───────────────────────────────── */\n\n private clearAutoAdvance() {\n if (this.autoAdvanceTimer !== null) {\n clearTimeout(this.autoAdvanceTimer);\n this.autoAdvanceTimer = null;\n }\n }\n\n private startAutoAdvance(ms: number) {\n this.clearAutoAdvance();\n this.autoAdvanceTimer = setTimeout(() => {\n this.autoAdvanceTimer = null;\n this.service?.nextStep();\n }, ms);\n }\n\n /* ── MutationObserver target resolution ─────────── */\n\n /**\n * Wait for a target element to appear in the DOM using a MutationObserver.\n * Resolves as soon as `deepQuery` finds the target, or after `timeout` ms.\n */\n private waitForTarget(\n targetId: string,\n timeout = MUTATION_TIMEOUT,\n ): Promise<Element | null> {\n const attr = this.service?.targetAttribute ?? 'data-tour-id';\n const selector = `[${attr}=\"${targetId}\"]`;\n\n // Fast path — already in the DOM\n const existing = deepQuery(selector, document.body);\n if (existing) return Promise.resolve(existing);\n\n return new Promise<Element | null>(resolve => {\n let resolved = false;\n const observer = new MutationObserver(() => {\n const el = deepQuery(selector, document.body);\n if (el) {\n resolved = true;\n observer.disconnect();\n resolve(el);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n setTimeout(() => {\n if (!resolved) {\n observer.disconnect();\n resolve(deepQuery(selector, document.body));\n }\n }, timeout);\n });\n }\n\n /* ── Tour state handler ─────────────────────────── */\n\n private async handleTourChange(snapshot: TourSnapshot | null) {\n this.clearAutoAdvance();\n\n if (!snapshot) {\n // Tour ended — fade out, restore focus, and restore scroll\n const endingTourId = this.activeTourId;\n this.visible = false;\n this.activeTourId = null;\n setTimeout(() => {\n this.snapshot = null;\n if (this.previouslyFocused) {\n this.previouslyFocused.focus();\n this.previouslyFocused = null;\n }\n // Scroll restore\n const tour = endingTourId ? this.service?.getTour(endingTourId) : null;\n const scrollMode = tour?.onEndScroll ?? 'restore';\n if (scrollMode === 'restore') {\n window.scrollTo({ top: this.savedScrollY, behavior: 'smooth' });\n } else if (scrollMode === 'top') {\n window.scrollTo({ top: 0, behavior: 'smooth' });\n }\n }, 300);\n return;\n }\n\n // Save the element that had focus and scroll position before the tour started\n if (!this.snapshot) {\n if (document.activeElement instanceof HTMLElement) {\n this.previouslyFocused = document.activeElement;\n }\n this.savedScrollY = window.scrollY;\n this.activeTourId = snapshot.tourId;\n }\n\n // Run beforeShow hook if present\n if (snapshot.step.beforeShow) {\n try {\n await snapshot.step.beforeShow();\n } catch (err) {\n console.error('[torchlit] beforeShow hook failed:', err);\n }\n }\n\n // Emit route-change event if the step has a route\n if (snapshot.step.route) {\n this.dispatchEvent(new CustomEvent('tour-route-change', {\n detail: { route: snapshot.step.route },\n bubbles: true,\n composed: true,\n }));\n }\n\n // Wait for the target element to appear (handles lazy rendering / route transitions)\n if (snapshot.step.target && snapshot.step.target !== '_none_') {\n await this.waitForTarget(snapshot.step.target);\n this.snapshot = this.service.getSnapshot();\n } else {\n this.snapshot = snapshot;\n }\n\n // Scroll into view if needed, then show\n if (this.snapshot?.targetElement) {\n const rect = this.snapshot.targetElement.getBoundingClientRect();\n const vh = window.innerHeight;\n const isTall = rect.height > vh * 0.6;\n // For tall targets, only require the top to be visible (user can scroll further).\n // For normal targets, require the whole element in view.\n const inView = isTall\n ? rect.top >= 0 && rect.top < vh * 0.5\n : rect.top >= 0 && rect.bottom <= vh && rect.left >= 0 && rect.right <= window.innerWidth;\n\n if (!inView) {\n await this.scrollAndSettle(this.snapshot.targetElement);\n // Recalculate rect at the post-scroll position\n this.snapshot = this.service.getSnapshot();\n }\n }\n\n requestAnimationFrame(() => {\n this.visible = true;\n // Start auto-advance timer if configured\n if (this.snapshot?.step.autoAdvance) {\n this.startAutoAdvance(this.snapshot.step.autoAdvance);\n }\n });\n }\n\n /**\n * Scroll an element into view and wait for the scroll to finish.\n * For tall elements (> 60% of viewport), scrolls to the top so the user\n * sees the start of the element plus the tooltip. For smaller elements,\n * centers them in the viewport.\n */\n private scrollAndSettle(el: Element): Promise<void> {\n const vh = window.innerHeight;\n const isTall = el.getBoundingClientRect().height > vh * 0.6;\n el.scrollIntoView({ behavior: 'smooth', block: isTall ? 'start' : 'center', inline: 'nearest' });\n\n return new Promise(resolve => {\n let lastTop = el.getBoundingClientRect().top;\n let stableFrames = 0;\n let rafId = 0;\n const maxWait = setTimeout(() => { cancelAnimationFrame(rafId); resolve(); }, 1500);\n\n const poll = () => {\n const top = el.getBoundingClientRect().top;\n if (Math.abs(top - lastTop) < 1) {\n stableFrames++;\n } else {\n stableFrames = 0;\n }\n lastTop = top;\n\n // Consider settled after 3 consecutive stable frames (~50ms)\n if (stableFrames >= 3) {\n clearTimeout(maxWait);\n resolve();\n } else {\n rafId = requestAnimationFrame(poll);\n }\n };\n\n rafId = requestAnimationFrame(poll);\n });\n }\n\n /* ── Event handlers ─────────────────────────────── */\n\n private handleResize = () => {\n if (this.snapshot && this.service) {\n this.snapshot = this.service.getSnapshot();\n }\n };\n\n /** Throttled scroll handler — refreshes the snapshot once per frame. */\n private handleScroll = () => {\n if (!this.snapshot || !this.service || this.scrollRafId) return;\n this.scrollRafId = requestAnimationFrame(() => {\n this.scrollRafId = 0;\n if (this.snapshot && this.service) {\n this.snapshot = this.service.getSnapshot();\n }\n });\n };\n\n private handleKeydown = (e: KeyboardEvent) => {\n if (!this.snapshot || !this.service) return;\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.skipTour();\n } else if (e.key === 'ArrowRight' || e.key === 'Enter') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.nextStep();\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.prevStep();\n } else if (e.key === 'Tab') {\n // Focus trap — keep Tab within the tooltip\n this.trapFocus(e);\n }\n };\n\n private handleBackdropClick = () => {\n this.clearAutoAdvance();\n this.service?.skipTour();\n };\n\n /* ── Focus trap ─────────────────────────────────── */\n\n private trapFocus(e: KeyboardEvent) {\n const container = this.shadowRoot?.querySelector<HTMLElement>(\n '.tour-tooltip, .tour-center-card',\n );\n if (!container) return;\n\n const focusable = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey) {\n if (this.shadowRoot?.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (this.shadowRoot?.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n }\n\n /* ── Smart auto-positioning ─────────────────────── */\n\n /**\n * Determine the best placement for the tooltip, flipping when the preferred\n * placement would clip the viewport. Tries: preferred → opposite → perpendicular.\n */\n private bestPlacement(rect: DOMRect, preferred: TourPlacement): TourPlacement {\n const PADDING = this.service?.spotlightPadding ?? 10;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n const fits = (p: TourPlacement): boolean => {\n switch (p) {\n case 'bottom':\n return rect.bottom + PADDING + GAP + TOOLTIP_H_MAX < vh;\n case 'top':\n return rect.top - PADDING - GAP - TOOLTIP_H_MAX > 0;\n case 'right':\n return rect.right + PADDING + GAP + TOOLTIP_W < vw;\n case 'left':\n return rect.left - PADDING - GAP - TOOLTIP_W > 0;\n }\n };\n\n const opposite: Record<TourPlacement, TourPlacement> = {\n top: 'bottom', bottom: 'top', left: 'right', right: 'left',\n };\n\n const perpendicular: Record<TourPlacement, [TourPlacement, TourPlacement]> = {\n top: ['left', 'right'], bottom: ['left', 'right'],\n left: ['top', 'bottom'], right: ['top', 'bottom'],\n };\n\n if (fits(preferred)) return preferred;\n if (fits(opposite[preferred])) return opposite[preferred];\n for (const p of perpendicular[preferred]) {\n if (fits(p)) return p;\n }\n // Nothing fits perfectly — keep preferred, clampToViewport will save us\n return preferred;\n }\n\n /* ── Tooltip positioning ────────────────────────── */\n\n private getTooltipPosition(rect: DOMRect, placement: TourPlacement): { top: number; left: number } {\n const PADDING = this.service?.spotlightPadding ?? 10;\n const vh = window.innerHeight;\n\n // For tall targets, use the visible center rather than the absolute center.\n // This keeps the tooltip near the portion of the target the user can actually see.\n const visibleTop = Math.max(0, rect.top);\n const visibleBottom = Math.min(vh, rect.bottom);\n const visibleCenterY = (visibleTop + visibleBottom) / 2;\n\n switch (placement) {\n case 'right':\n return {\n top: visibleCenterY - 80,\n left: rect.right + PADDING + GAP,\n };\n case 'left':\n return {\n top: visibleCenterY - 80,\n left: rect.left - PADDING - GAP - TOOLTIP_W,\n };\n case 'bottom':\n return {\n top: rect.bottom + PADDING + GAP,\n left: rect.left + rect.width / 2 - TOOLTIP_W / 2,\n };\n case 'top':\n // Initial estimate — corrected after render in adjustTooltipPosition()\n return {\n top: rect.top - PADDING - GAP,\n left: rect.left + rect.width / 2 - TOOLTIP_W / 2,\n };\n default:\n return { top: rect.bottom + GAP, left: rect.left };\n }\n }\n\n private clampToViewport(pos: { top: number; left: number }): { top: number; left: number } {\n return {\n top: Math.max(VIEWPORT_MARGIN, Math.min(pos.top, window.innerHeight - TOOLTIP_H_MAX - VIEWPORT_MARGIN)),\n left: Math.max(VIEWPORT_MARGIN, Math.min(pos.left, window.innerWidth - TOOLTIP_W - VIEWPORT_MARGIN)),\n };\n }\n\n private getArrowClass(placement: TourPlacement): string {\n switch (placement) {\n case 'right': return 'arrow-right';\n case 'left': return 'arrow-left';\n case 'bottom': return 'arrow-bottom';\n case 'top': return 'arrow-top';\n default: return 'arrow-bottom';\n }\n }\n\n /**\n * Compute the arrow's offset along the tooltip edge so it points at\n * the center of the target element, clamped to stay within the tooltip.\n */\n private getArrowOffset(\n targetRect: DOMRect,\n tooltipPos: { top: number; left: number },\n placement: TourPlacement,\n ): string {\n const ARROW_SIZE = 12;\n const MIN = ARROW_SIZE + 8;\n\n if (placement === 'top' || placement === 'bottom') {\n // Horizontal offset\n const targetCenterX = targetRect.left + targetRect.width / 2;\n const offset = targetCenterX - tooltipPos.left;\n const clamped = Math.max(MIN, Math.min(offset, TOOLTIP_W - MIN));\n return `${clamped}px`;\n }\n\n // Vertical offset (left / right placement) — use visible center for tall targets\n const visibleTop = Math.max(0, targetRect.top);\n const visibleBottom = Math.min(window.innerHeight, targetRect.bottom);\n const targetCenterY = (visibleTop + visibleBottom) / 2;\n const offset = targetCenterY - tooltipPos.top;\n const clamped = Math.max(MIN, Math.min(offset, TOOLTIP_H_MAX - MIN));\n return `${clamped}px`;\n }\n\n /* ── Render ─────────────────────────────────────── */\n\n override render() {\n if (!this.snapshot) return html``;\n\n const { step, stepIndex, totalSteps, targetRect } = this.snapshot;\n\n // No target found — show centered card\n if (!targetRect) {\n return this.renderCenteredStep(step, stepIndex, totalSteps);\n }\n\n const PADDING = this.service?.spotlightPadding ?? 10;\n\n // Per-step spotlight border-radius override\n const spotlightRadius = step.spotlightBorderRadius\n ? `border-radius: ${step.spotlightBorderRadius};`\n : '';\n\n const spotlightStyle = `\n top: ${targetRect.top - PADDING}px;\n left: ${targetRect.left - PADDING}px;\n width: ${targetRect.width + PADDING * 2}px;\n height: ${targetRect.height + PADDING * 2}px;\n ${spotlightRadius}\n `;\n\n // Smart placement — flip if the preferred side clips\n const resolved = this.bestPlacement(targetRect, step.placement);\n this.lastResolvedPlacement = resolved;\n\n const tooltipPos = this.clampToViewport(\n this.getTooltipPosition(targetRect, resolved),\n );\n const arrowOffset = this.getArrowOffset(targetRect, tooltipPos, resolved);\n const tooltipStyle = `top: ${tooltipPos.top}px; left: ${tooltipPos.left}px;`;\n const stepLabel = `Step ${stepIndex + 1} of ${totalSteps}: ${step.title}`;\n\n return html`\n <!-- Screen reader announcement -->\n <div class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n ${stepLabel}\n </div>\n\n <div\n class=\"tour-backdrop ${this.visible ? 'visible' : ''}\"\n part=\"backdrop\"\n @click=${this.handleBackdropClick}\n ></div>\n\n <div class=\"tour-spotlight\" part=\"spotlight\" style=${spotlightStyle}></div>\n\n <div\n class=\"tour-tooltip ${this.visible ? 'visible' : ''}\"\n part=\"tooltip\"\n style=${tooltipStyle}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"${step.title}\"\n aria-describedby=\"tour-desc\"\n tabindex=\"-1\"\n >\n <div class=\"tour-arrow ${this.getArrowClass(resolved)}\" style=\"--arrow-offset: ${arrowOffset}\"></div>\n\n <div class=\"tour-step-badge\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"></path>\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"></line>\n </svg>\n Step ${stepIndex + 1} of ${totalSteps}\n </div>\n\n <h3 class=\"tour-title\">${step.title}</h3>\n <div class=\"tour-message\" id=\"tour-desc\">${step.message}</div>\n\n ${this.renderProgressDots(stepIndex, totalSteps)}\n\n <div class=\"tour-footer\">\n <button\n class=\"tour-skip\"\n aria-label=\"Skip tour\"\n @click=${() => { this.clearAutoAdvance(); this.service.skipTour(); }}\n >\n Skip tour\n </button>\n <div class=\"tour-nav\">\n ${stepIndex > 0 ? html`\n <button\n class=\"tour-btn\"\n aria-label=\"Go to previous step\"\n @click=${() => { this.clearAutoAdvance(); this.service.prevStep(); }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"15 18 9 12 15 6\"></polyline>\n </svg>\n Back\n </button>\n ` : nothing}\n <button\n class=\"tour-btn primary\"\n aria-label=\"${stepIndex === totalSteps - 1 ? 'Finish tour' : 'Go to next step'}\"\n @click=${() => { this.clearAutoAdvance(); this.service.nextStep(); }}\n >\n ${stepIndex === totalSteps - 1 ? 'Finish' : 'Next'}\n ${stepIndex < totalSteps - 1 ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"9 18 15 12 9 6\"></polyline>\n </svg>\n ` : html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n `}\n </button>\n </div>\n </div>\n\n ${step.autoAdvance ? keyed(stepIndex, html`\n <div\n class=\"tour-auto-progress\"\n style=\"animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;\"\n aria-hidden=\"true\"\n ></div>\n `) : nothing}\n </div>\n `;\n }\n\n private renderProgressDots(current: number, total: number) {\n if (total <= 1) return nothing;\n return html`\n <div class=\"tour-progress\" role=\"group\" aria-label=\"Tour progress\">\n ${Array.from({ length: total }, (_, i) => html`\n <div\n class=\"tour-dot ${i === current ? 'active' : i < current ? 'completed' : ''}\"\n role=\"presentation\"\n ></div>\n `)}\n </div>\n `;\n }\n\n private renderCenteredStep(step: TourStep, stepIndex: number, totalSteps: number) {\n const stepLabel = `Step ${stepIndex + 1} of ${totalSteps}: ${step.title}`;\n\n return html`\n <!-- Screen reader announcement -->\n <div class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n ${stepLabel}\n </div>\n\n <div\n class=\"tour-backdrop ${this.visible ? 'visible' : ''}\"\n part=\"backdrop\"\n @click=${this.handleBackdropClick}\n ></div>\n\n <div\n class=\"tour-center-card ${this.visible ? 'visible' : ''}\"\n part=\"center-card\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"${step.title}\"\n aria-describedby=\"tour-desc-center\"\n tabindex=\"-1\"\n >\n <div class=\"tour-center-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"></path>\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"></line>\n </svg>\n </div>\n\n <h3 class=\"tour-title\">${step.title}</h3>\n <div class=\"tour-message\" id=\"tour-desc-center\">${step.message}</div>\n\n ${this.renderProgressDots(stepIndex, totalSteps)}\n\n <div class=\"tour-footer\">\n <button\n class=\"tour-skip\"\n aria-label=\"Skip tour\"\n @click=${() => { this.clearAutoAdvance(); this.service.skipTour(); }}\n >\n Skip tour\n </button>\n <div class=\"tour-nav\">\n ${stepIndex > 0 ? html`\n <button\n class=\"tour-btn\"\n aria-label=\"Go to previous step\"\n @click=${() => { this.clearAutoAdvance(); this.service.prevStep(); }}\n >Back</button>\n ` : nothing}\n <button\n class=\"tour-btn primary\"\n aria-label=\"${stepIndex === totalSteps - 1 ? 'Start the tour' : 'Go to next step'}\"\n @click=${() => { this.clearAutoAdvance(); this.service.nextStep(); }}\n >\n ${stepIndex === totalSteps - 1 ? \"Let's go!\" : 'Next'}\n ${stepIndex < totalSteps - 1 ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"9 18 15 12 9 6\"></polyline>\n </svg>\n ` : nothing}\n </button>\n </div>\n </div>\n\n ${step.autoAdvance ? keyed(stepIndex, html`\n <div\n class=\"tour-auto-progress\"\n style=\"animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;\"\n aria-hidden=\"true\"\n ></div>\n `) : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'torchlit-overlay': TorchlitOverlay;\n }\n}\n"],"names":["offset","clamped"],"mappings":"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"}
1
+ {"version":3,"file":"tour-overlay.js","sources":["../src/tour-overlay.ts"],"sourcesContent":["import { LitElement, html, css, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { keyed } from 'lit/directives/keyed.js';\nimport { deepQuery } from './utils/deep-query.js';\nimport type { TourService } from './tour-service.js';\nimport type { TourStep, TourSnapshot, TourPlacement } from './types.js';\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nconst TOOLTIP_W = 320;\nconst TOOLTIP_H_MAX = 270; // conservative max height for clamp & flip checks\nconst GAP = 16;\nconst VIEWPORT_MARGIN = 24;\nconst MUTATION_TIMEOUT = 3000;\n\n/**\n * `<torchlit-overlay>` — Full-screen overlay that renders a spotlight cutout\n * around the current tour target, a tooltip with title / message / progress,\n * and navigation controls.\n *\n * Wire it to a `TourService` instance via the `service` property:\n *\n * ```html\n * <torchlit-overlay></torchlit-overlay>\n * ```\n * ```js\n * document.querySelector('torchlit-overlay').service = myTourService;\n * ```\n *\n * @fires tour-route-change - When a step has a `route` property, dispatched\n * with `{ route: string }` so the host app can switch views.\n *\n * @csspart backdrop - The semi-transparent overlay behind the spotlight.\n * @csspart spotlight - The cutout highlight around the target element.\n * @csspart tooltip - The floating tooltip card.\n * @csspart center-card - The centered card shown when there is no target.\n */\n@customElement('torchlit-overlay')\nexport class TorchlitOverlay extends LitElement {\n /* ── Styles ─────────────────────────────────────── */\n\n static override styles = css`\n :host {\n display: block;\n }\n\n /* ── Visually hidden (sr-only) ─────────────────── */\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n\n /* ── Backdrop ──────────────────────────────────── */\n\n .tour-backdrop {\n position: fixed;\n inset: 0;\n z-index: 9998;\n pointer-events: auto;\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .tour-backdrop.visible {\n opacity: 1;\n }\n\n /* ── Spotlight (box-shadow cutout) ─────────────── */\n\n .tour-spotlight {\n position: fixed;\n z-index: 9999;\n border-radius: var(--tour-spotlight-radius, var(--radius-lg, 0.75rem));\n box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);\n transition: top 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n left 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n width 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n height 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n pointer-events: none;\n }\n\n /* Subtle pulsing ring around spotlight */\n .tour-spotlight::after {\n content: '';\n position: absolute;\n inset: -4px;\n border-radius: inherit;\n border: 2px solid var(--tour-primary, var(--primary, #F26122));\n opacity: 0.5;\n animation: spotlightPulse 2s ease-in-out infinite;\n }\n\n @keyframes spotlightPulse {\n 0%, 100% { opacity: 0.3; transform: scale(1); }\n 50% { opacity: 0.7; transform: scale(1.01); }\n }\n\n /* ── Tooltip ───────────────────────────────────── */\n\n .tour-tooltip {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n width: 320px;\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n border-radius: var(--tour-tooltip-radius, var(--radius-lg, 0.75rem));\n box-shadow: 0 20px 40px -8px rgba(0, 0, 0, 0.2),\n 0 8px 16px -4px rgba(0, 0, 0, 0.1);\n padding: 1.25rem;\n pointer-events: auto;\n opacity: 0;\n transform: translateY(8px) scale(0.96);\n transition: opacity 0.25s ease, transform 0.25s ease,\n top 0.35s cubic-bezier(0.4, 0, 0.2, 1),\n left 0.35s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .tour-tooltip:focus {\n outline: none;\n }\n\n .tour-tooltip.visible {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n\n /* Arrow — position along edge is set via inline --arrow-offset */\n .tour-arrow {\n position: absolute;\n width: 12px;\n height: 12px;\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n transform: rotate(45deg);\n }\n\n /* tooltip is above target → arrow at bottom of tooltip pointing down */\n .tour-arrow.arrow-top {\n bottom: -7px;\n left: var(--arrow-offset, 50%);\n margin-left: -6px;\n border-top: none;\n border-left: none;\n }\n\n /* tooltip is below target → arrow at top of tooltip pointing up */\n .tour-arrow.arrow-bottom {\n top: -7px;\n left: var(--arrow-offset, 50%);\n margin-left: -6px;\n border-bottom: none;\n border-right: none;\n }\n\n /* tooltip is right of target → arrow on left edge pointing left */\n .tour-arrow.arrow-left {\n right: -7px;\n top: var(--arrow-offset, 50%);\n margin-top: -6px;\n border-bottom: none;\n border-left: none;\n }\n\n /* tooltip is left of target → arrow on right edge pointing right */\n .tour-arrow.arrow-right {\n left: -7px;\n top: var(--arrow-offset, 50%);\n margin-top: -6px;\n border-top: none;\n border-right: none;\n }\n\n /* ── Tooltip content ──────────────────────────── */\n\n .tour-step-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--tour-primary, var(--primary, #F26122));\n margin-bottom: 0.5rem;\n }\n\n .tour-title {\n margin: 0 0 0.375rem;\n font-size: 1rem;\n font-weight: 600;\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n line-height: 1.3;\n }\n\n .tour-message {\n margin: 0 0 1rem;\n font-size: 0.8125rem;\n color: var(--tour-muted-foreground, var(--muted-foreground, #737373));\n line-height: 1.55;\n }\n\n /* ── Progress dots ────────────────────────────── */\n\n .tour-progress {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n margin-bottom: 1rem;\n }\n\n .tour-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--tour-muted, var(--muted, #e5e5e5));\n transition: background 0.2s, transform 0.2s;\n }\n\n .tour-dot.active {\n background: var(--tour-primary, var(--primary, #F26122));\n transform: scale(1.3);\n }\n\n .tour-dot.completed {\n background: var(--tour-primary, var(--primary, #F26122));\n opacity: 0.5;\n }\n\n /* ── Auto-advance progress bar ────────────────── */\n\n .tour-auto-progress {\n position: absolute;\n bottom: 0;\n left: 0;\n max-width: 100%;\n height: 3px;\n background: var(--tour-primary, var(--primary, #F26122));\n opacity: 0.7;\n border-radius: 0 0 var(--tour-tooltip-radius, var(--radius-lg, 0.75rem)) var(--tour-tooltip-radius, var(--radius-lg, 0.75rem));\n }\n\n @keyframes autoAdvanceFill {\n from { width: 0%; }\n to { width: 100%; }\n }\n\n /* ── Footer buttons ───────────────────────────── */\n\n .tour-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .tour-skip {\n font-size: 0.75rem;\n color: var(--tour-muted-foreground, var(--muted-foreground, #737373));\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem 0;\n transition: color 0.15s;\n }\n\n .tour-skip:hover {\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n }\n\n .tour-nav {\n display: flex;\n gap: 0.5rem;\n }\n\n .tour-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.4rem 0.875rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border-radius: var(--tour-btn-radius, var(--radius-md, 0.5rem));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n background: var(--tour-background, var(--background, #fff));\n color: var(--tour-foreground, var(--foreground, #1a1a1a));\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .tour-btn:hover {\n background: var(--tour-muted, var(--muted, #f5f5f5));\n }\n\n .tour-btn:focus-visible {\n outline: 2px solid var(--tour-primary, var(--primary, #F26122));\n outline-offset: 2px;\n }\n\n .tour-btn.primary {\n background: var(--tour-primary, var(--primary, #F26122));\n color: var(--tour-primary-foreground, var(--primary-foreground, #fff));\n border-color: var(--tour-primary, var(--primary, #F26122));\n }\n\n .tour-btn.primary:hover {\n opacity: 0.9;\n }\n\n .tour-btn svg {\n width: 14px;\n height: 14px;\n }\n\n /* ── Welcome / no-target step ─────────────────── */\n\n .tour-center-card {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%) scale(0.96);\n width: 400px;\n max-width: calc(100vw - 2rem);\n background: var(--tour-card, var(--card, #fff));\n border: 1px solid var(--tour-border, var(--border, #e5e5e5));\n border-radius: var(--tour-card-radius, var(--radius-xl, 1rem));\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\n padding: 2rem;\n text-align: center;\n pointer-events: auto;\n opacity: 0;\n transition: opacity 0.3s ease, transform 0.3s ease;\n }\n\n .tour-center-card:focus {\n outline: none;\n }\n\n .tour-center-card.visible {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n\n .tour-center-icon {\n width: 48px;\n height: 48px;\n margin: 0 auto 1rem;\n background: var(--tour-primary, var(--primary, #F26122));\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--tour-primary-foreground, var(--primary-foreground, #fff));\n }\n `;\n\n /* ── Properties ──────────────────────────────────── */\n\n /**\n * The `TourService` instance this overlay subscribes to.\n * Must be set before the overlay will render anything.\n */\n @property({ attribute: false })\n service!: TourService;\n\n @state() private snapshot: TourSnapshot | null = null;\n @state() private visible = false;\n\n private unsubscribe?: () => void;\n private previouslyFocused: HTMLElement | null = null;\n private autoAdvanceTimer: ReturnType<typeof setTimeout> | null = null;\n private lastResolvedPlacement: TourPlacement = 'bottom';\n private scrollRafId = 0;\n private savedScrollY = 0;\n private activeTourId: string | null = null;\n\n /* ── Lifecycle ──────────────────────────────────── */\n\n override connectedCallback() {\n super.connectedCallback();\n if (this.service) {\n this.attachService();\n }\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('scroll', this.handleScroll, true);\n window.addEventListener('keydown', this.handleKeydown);\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this.unsubscribe?.();\n this.clearAutoAdvance();\n if (this.scrollRafId) cancelAnimationFrame(this.scrollRafId);\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('scroll', this.handleScroll, true);\n window.removeEventListener('keydown', this.handleKeydown);\n }\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('service') && this.service) {\n this.unsubscribe?.();\n this.attachService();\n }\n\n if (this.visible && this.snapshot) {\n // Measure the actual tooltip and correct position for 'top' placement\n this.adjustTooltipPosition();\n\n // Focus the dialog container\n this.updateComplete.then(() => {\n const dialog = this.shadowRoot?.querySelector<HTMLElement>(\n '.tour-tooltip, .tour-center-card',\n );\n dialog?.focus();\n });\n }\n }\n\n /**\n * After rendering, measure the tooltip's actual height and correct\n * its position for 'top' placement (the only one that depends on\n * tooltip height). This eliminates hardcoded height estimates.\n */\n private adjustTooltipPosition() {\n if (this.lastResolvedPlacement !== 'top') return;\n\n const tooltip = this.shadowRoot?.querySelector<HTMLElement>('.tour-tooltip');\n const targetRect = this.snapshot?.targetRect;\n if (!tooltip || !targetRect) return;\n\n const PADDING = this.service?.spotlightPadding ?? 10;\n const actualHeight = tooltip.getBoundingClientRect().height;\n const correctTop = targetRect.top - PADDING - GAP - actualHeight;\n const clampedTop = Math.max(VIEWPORT_MARGIN, correctTop);\n\n tooltip.style.top = `${clampedTop}px`;\n }\n\n private attachService() {\n this.unsubscribe = this.service.subscribe(snap => this.handleTourChange(snap));\n }\n\n /* ── Auto-advance ───────────────────────────────── */\n\n private clearAutoAdvance() {\n if (this.autoAdvanceTimer !== null) {\n clearTimeout(this.autoAdvanceTimer);\n this.autoAdvanceTimer = null;\n }\n }\n\n private startAutoAdvance(ms: number) {\n this.clearAutoAdvance();\n this.autoAdvanceTimer = setTimeout(() => {\n this.autoAdvanceTimer = null;\n this.service?.nextStep();\n }, ms);\n }\n\n /* ── MutationObserver target resolution ─────────── */\n\n /**\n * Wait for a target element to appear in the DOM using a MutationObserver.\n * Resolves as soon as `deepQuery` finds the target, or after `timeout` ms.\n */\n private waitForTarget(\n targetId: string,\n timeout = MUTATION_TIMEOUT,\n ): Promise<Element | null> {\n const attr = this.service?.targetAttribute ?? 'data-tour-id';\n const selector = `[${attr}=\"${targetId}\"]`;\n\n // Fast path — already in the DOM\n const existing = deepQuery(selector, document.body);\n if (existing) return Promise.resolve(existing);\n\n return new Promise<Element | null>(resolve => {\n let resolved = false;\n const observer = new MutationObserver(() => {\n const el = deepQuery(selector, document.body);\n if (el) {\n resolved = true;\n observer.disconnect();\n resolve(el);\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n setTimeout(() => {\n if (!resolved) {\n observer.disconnect();\n resolve(deepQuery(selector, document.body));\n }\n }, timeout);\n });\n }\n\n /* ── Scroll helpers ─────────────────────────────── */\n\n /**\n * Whether the target element (plus its tooltip) fits comfortably inside the\n * viewport. When it doesn't, we only need the top of the target visible —\n * the tooltip tracks scroll, so the user can explore the rest naturally.\n */\n private fitsInViewport(el: Element): boolean {\n return el.getBoundingClientRect().height + TOOLTIP_H_MAX + GAP * 2 < window.innerHeight;\n }\n\n /* ── Tour state handler ─────────────────────────── */\n\n private async handleTourChange(snapshot: TourSnapshot | null) {\n this.clearAutoAdvance();\n\n if (!snapshot) {\n // Tour ended — fade out, restore focus, and restore scroll\n const endingTourId = this.activeTourId;\n this.visible = false;\n this.activeTourId = null;\n setTimeout(() => {\n this.snapshot = null;\n if (this.previouslyFocused) {\n this.previouslyFocused.focus();\n this.previouslyFocused = null;\n }\n // Scroll restore\n const tour = endingTourId ? this.service?.getTour(endingTourId) : null;\n const scrollMode = tour?.onEndScroll ?? 'restore';\n if (scrollMode === 'restore') {\n window.scrollTo({ top: this.savedScrollY, behavior: 'smooth' });\n } else if (scrollMode === 'top') {\n window.scrollTo({ top: 0, behavior: 'smooth' });\n }\n }, 300);\n return;\n }\n\n // Save the element that had focus and scroll position before the tour started\n if (!this.snapshot) {\n if (document.activeElement instanceof HTMLElement) {\n this.previouslyFocused = document.activeElement;\n }\n this.savedScrollY = window.scrollY;\n this.activeTourId = snapshot.tourId;\n }\n\n // Run beforeShow hook if present\n if (snapshot.step.beforeShow) {\n try {\n await snapshot.step.beforeShow();\n } catch (err) {\n console.error('[torchlit] beforeShow hook failed:', err);\n }\n }\n\n // Emit route-change event if the step has a route\n if (snapshot.step.route) {\n this.dispatchEvent(new CustomEvent('tour-route-change', {\n detail: { route: snapshot.step.route },\n bubbles: true,\n composed: true,\n }));\n }\n\n // Wait for the target element to appear (handles lazy rendering / route transitions)\n if (snapshot.step.target && snapshot.step.target !== '_none_') {\n await this.waitForTarget(snapshot.step.target);\n this.snapshot = this.service.getSnapshot();\n } else {\n this.snapshot = snapshot;\n }\n\n // Scroll into view if needed, then show\n if (this.snapshot?.targetElement) {\n const rect = this.snapshot.targetElement.getBoundingClientRect();\n const vh = window.innerHeight;\n const fits = this.fitsInViewport(this.snapshot.targetElement);\n const placement = this.snapshot.step.placement;\n const PADDING = this.service?.spotlightPadding ?? 10;\n\n // Small targets that fit with their tooltip: require the whole element visible.\n // Large targets: placement-aware — for 'top' placement, ensure there is\n // enough room above the target for the tooltip; for other placements,\n // just require the top to be somewhere on screen.\n const inView = fits\n ? rect.top >= 0 && rect.bottom <= vh && rect.left >= 0 && rect.right <= window.innerWidth\n : placement === 'top'\n ? rect.top >= TOOLTIP_H_MAX + GAP + PADDING && rect.top < vh\n : rect.top >= 0 && rect.top < vh;\n\n if (!inView) {\n await this.scrollAndSettle(this.snapshot.targetElement, placement);\n // Recalculate rect at the post-scroll position\n this.snapshot = this.service.getSnapshot();\n }\n }\n\n requestAnimationFrame(() => {\n this.visible = true;\n // Start auto-advance timer if configured\n if (this.snapshot?.step.autoAdvance) {\n this.startAutoAdvance(this.snapshot.step.autoAdvance);\n }\n });\n }\n\n /**\n * Scroll an element into view and wait for the scroll to finish.\n *\n * Small elements that fit (with their tooltip) are centered in the viewport.\n * Large elements are scrolled with a **placement-aware** offset so there is\n * room for the tooltip on the preferred side. When `placement` is `'top'`,\n * we reserve enough space above the target for the tooltip; for other\n * placements the tooltip goes below or beside, so a smaller offset suffices.\n */\n private scrollAndSettle(el: Element, placement: TourPlacement): Promise<void> {\n const vh = window.innerHeight;\n const rect = el.getBoundingClientRect();\n\n if (this.fitsInViewport(el)) {\n // Small targets — center them for a balanced feel\n el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });\n } else {\n const PADDING = this.service?.spotlightPadding ?? 10;\n\n // Reserve space above the target based on preferred tooltip placement.\n // 'top': the tooltip sits above the target, so leave room for it.\n // Others: tooltip goes below or beside, so a small offset suffices.\n const desiredTop = placement === 'top'\n ? TOOLTIP_H_MAX + GAP + PADDING // ~296px — room for the tooltip\n : vh * 0.15; // ~15% — comfortable context\n\n const scrollTarget = window.scrollY + rect.top - desiredTop;\n window.scrollTo({ top: Math.max(0, scrollTarget), behavior: 'smooth' });\n }\n\n return new Promise(resolve => {\n let lastTop = el.getBoundingClientRect().top;\n let stableFrames = 0;\n let rafId = 0;\n const maxWait = setTimeout(() => { cancelAnimationFrame(rafId); resolve(); }, 1500);\n\n const poll = () => {\n const top = el.getBoundingClientRect().top;\n if (Math.abs(top - lastTop) < 1) {\n stableFrames++;\n } else {\n stableFrames = 0;\n }\n lastTop = top;\n\n // Consider settled after 3 consecutive stable frames (~50ms)\n if (stableFrames >= 3) {\n clearTimeout(maxWait);\n resolve();\n } else {\n rafId = requestAnimationFrame(poll);\n }\n };\n\n rafId = requestAnimationFrame(poll);\n });\n }\n\n /* ── Event handlers ─────────────────────────────── */\n\n private handleResize = () => {\n if (this.snapshot && this.service) {\n this.snapshot = this.service.getSnapshot();\n }\n };\n\n /** Throttled scroll handler — refreshes the snapshot once per frame. */\n private handleScroll = () => {\n if (!this.snapshot || !this.service || this.scrollRafId) return;\n this.scrollRafId = requestAnimationFrame(() => {\n this.scrollRafId = 0;\n if (this.snapshot && this.service) {\n this.snapshot = this.service.getSnapshot();\n }\n });\n };\n\n private handleKeydown = (e: KeyboardEvent) => {\n if (!this.snapshot || !this.service) return;\n\n if (e.key === 'Escape') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.skipTour();\n } else if (e.key === 'ArrowRight' || e.key === 'Enter') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.nextStep();\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n this.clearAutoAdvance();\n this.service.prevStep();\n } else if (e.key === 'Tab') {\n // Focus trap — keep Tab within the tooltip\n this.trapFocus(e);\n }\n };\n\n private handleBackdropClick = () => {\n this.clearAutoAdvance();\n this.service?.skipTour();\n };\n\n /* ── Focus trap ─────────────────────────────────── */\n\n private trapFocus(e: KeyboardEvent) {\n const container = this.shadowRoot?.querySelector<HTMLElement>(\n '.tour-tooltip, .tour-center-card',\n );\n if (!container) return;\n\n const focusable = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusable.length === 0) return;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n\n if (e.shiftKey) {\n if (this.shadowRoot?.activeElement === first) {\n e.preventDefault();\n last.focus();\n }\n } else {\n if (this.shadowRoot?.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n }\n }\n\n /* ── Smart auto-positioning ─────────────────────── */\n\n /**\n * Determine the best placement for the tooltip, flipping when the preferred\n * placement would clip the viewport. Tries: preferred → opposite → perpendicular.\n */\n private bestPlacement(rect: DOMRect, preferred: TourPlacement): TourPlacement {\n const PADDING = this.service?.spotlightPadding ?? 10;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n const fits = (p: TourPlacement): boolean => {\n switch (p) {\n case 'bottom':\n return rect.bottom + PADDING + GAP + TOOLTIP_H_MAX < vh;\n case 'top':\n return rect.top - PADDING - GAP - TOOLTIP_H_MAX > 0;\n case 'right':\n return rect.right + PADDING + GAP + TOOLTIP_W < vw;\n case 'left':\n return rect.left - PADDING - GAP - TOOLTIP_W > 0;\n }\n };\n\n const opposite: Record<TourPlacement, TourPlacement> = {\n top: 'bottom', bottom: 'top', left: 'right', right: 'left',\n };\n\n const perpendicular: Record<TourPlacement, [TourPlacement, TourPlacement]> = {\n top: ['left', 'right'], bottom: ['left', 'right'],\n left: ['top', 'bottom'], right: ['top', 'bottom'],\n };\n\n if (fits(preferred)) return preferred;\n if (fits(opposite[preferred])) return opposite[preferred];\n for (const p of perpendicular[preferred]) {\n if (fits(p)) return p;\n }\n // Nothing fits perfectly — keep preferred, clampToViewport will save us\n return preferred;\n }\n\n /* ── Tooltip positioning ────────────────────────── */\n\n private getTooltipPosition(rect: DOMRect, placement: TourPlacement): { top: number; left: number } {\n const PADDING = this.service?.spotlightPadding ?? 10;\n const vh = window.innerHeight;\n\n // For tall targets, use the visible center rather than the absolute center.\n // This keeps the tooltip near the portion of the target the user can actually see.\n const visibleTop = Math.max(0, rect.top);\n const visibleBottom = Math.min(vh, rect.bottom);\n const visibleCenterY = (visibleTop + visibleBottom) / 2;\n\n switch (placement) {\n case 'right':\n return {\n top: visibleCenterY - 80,\n left: rect.right + PADDING + GAP,\n };\n case 'left':\n return {\n top: visibleCenterY - 80,\n left: rect.left - PADDING - GAP - TOOLTIP_W,\n };\n case 'bottom':\n return {\n top: rect.bottom + PADDING + GAP,\n left: rect.left + rect.width / 2 - TOOLTIP_W / 2,\n };\n case 'top':\n // Initial estimate — corrected after render in adjustTooltipPosition()\n return {\n top: rect.top - PADDING - GAP,\n left: rect.left + rect.width / 2 - TOOLTIP_W / 2,\n };\n default:\n return { top: rect.bottom + GAP, left: rect.left };\n }\n }\n\n private clampToViewport(pos: { top: number; left: number }): { top: number; left: number } {\n return {\n top: Math.max(VIEWPORT_MARGIN, Math.min(pos.top, window.innerHeight - TOOLTIP_H_MAX - VIEWPORT_MARGIN)),\n left: Math.max(VIEWPORT_MARGIN, Math.min(pos.left, window.innerWidth - TOOLTIP_W - VIEWPORT_MARGIN)),\n };\n }\n\n private getArrowClass(placement: TourPlacement): string {\n switch (placement) {\n case 'right': return 'arrow-right';\n case 'left': return 'arrow-left';\n case 'bottom': return 'arrow-bottom';\n case 'top': return 'arrow-top';\n default: return 'arrow-bottom';\n }\n }\n\n /**\n * Compute the arrow's offset along the tooltip edge so it points at\n * the center of the target element, clamped to stay within the tooltip.\n */\n private getArrowOffset(\n targetRect: DOMRect,\n tooltipPos: { top: number; left: number },\n placement: TourPlacement,\n ): string {\n const ARROW_SIZE = 12;\n const MIN = ARROW_SIZE + 8;\n\n if (placement === 'top' || placement === 'bottom') {\n // Horizontal offset\n const targetCenterX = targetRect.left + targetRect.width / 2;\n const offset = targetCenterX - tooltipPos.left;\n const clamped = Math.max(MIN, Math.min(offset, TOOLTIP_W - MIN));\n return `${clamped}px`;\n }\n\n // Vertical offset (left / right placement) — use visible center for tall targets\n const visibleTop = Math.max(0, targetRect.top);\n const visibleBottom = Math.min(window.innerHeight, targetRect.bottom);\n const targetCenterY = (visibleTop + visibleBottom) / 2;\n const offset = targetCenterY - tooltipPos.top;\n const clamped = Math.max(MIN, Math.min(offset, TOOLTIP_H_MAX - MIN));\n return `${clamped}px`;\n }\n\n /* ── Render ─────────────────────────────────────── */\n\n override render() {\n if (!this.snapshot) return html``;\n\n const { step, stepIndex, totalSteps, targetRect } = this.snapshot;\n\n // No target found — show centered card\n if (!targetRect) {\n return this.renderCenteredStep(step, stepIndex, totalSteps);\n }\n\n const PADDING = this.service?.spotlightPadding ?? 10;\n\n // Per-step spotlight border-radius override\n const spotlightRadius = step.spotlightBorderRadius\n ? `border-radius: ${step.spotlightBorderRadius};`\n : '';\n\n const spotlightStyle = `\n top: ${targetRect.top - PADDING}px;\n left: ${targetRect.left - PADDING}px;\n width: ${targetRect.width + PADDING * 2}px;\n height: ${targetRect.height + PADDING * 2}px;\n ${spotlightRadius}\n `;\n\n // Smart placement — flip if the preferred side clips\n const resolved = this.bestPlacement(targetRect, step.placement);\n this.lastResolvedPlacement = resolved;\n\n const tooltipPos = this.clampToViewport(\n this.getTooltipPosition(targetRect, resolved),\n );\n const arrowOffset = this.getArrowOffset(targetRect, tooltipPos, resolved);\n const tooltipStyle = `top: ${tooltipPos.top}px; left: ${tooltipPos.left}px;`;\n const stepLabel = `Step ${stepIndex + 1} of ${totalSteps}: ${step.title}`;\n\n return html`\n <!-- Screen reader announcement -->\n <div class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n ${stepLabel}\n </div>\n\n <div\n class=\"tour-backdrop ${this.visible ? 'visible' : ''}\"\n part=\"backdrop\"\n @click=${this.handleBackdropClick}\n ></div>\n\n <div class=\"tour-spotlight\" part=\"spotlight\" style=${spotlightStyle}></div>\n\n <div\n class=\"tour-tooltip ${this.visible ? 'visible' : ''}\"\n part=\"tooltip\"\n style=${tooltipStyle}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"${step.title}\"\n aria-describedby=\"tour-desc\"\n tabindex=\"-1\"\n >\n <div class=\"tour-arrow ${this.getArrowClass(resolved)}\" style=\"--arrow-offset: ${arrowOffset}\"></div>\n\n <div class=\"tour-step-badge\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"></path>\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"></line>\n </svg>\n Step ${stepIndex + 1} of ${totalSteps}\n </div>\n\n <h3 class=\"tour-title\">${step.title}</h3>\n <div class=\"tour-message\" id=\"tour-desc\">${step.message}</div>\n\n ${this.renderProgressDots(stepIndex, totalSteps)}\n ${this.renderFooter(stepIndex, totalSteps)}\n ${this.renderAutoProgress(step, stepIndex)}\n </div>\n `;\n }\n\n private renderProgressDots(current: number, total: number) {\n if (total <= 1) return nothing;\n return html`\n <div class=\"tour-progress\" role=\"group\" aria-label=\"Tour progress\">\n ${Array.from({ length: total }, (_, i) => html`\n <div\n class=\"tour-dot ${i === current ? 'active' : i < current ? 'completed' : ''}\"\n role=\"presentation\"\n ></div>\n `)}\n </div>\n `;\n }\n\n private renderFooter(\n stepIndex: number,\n totalSteps: number,\n finishLabel = 'Finish',\n finishAriaLabel = 'Finish tour',\n showNavIcons = true,\n ) {\n return html`\n <div class=\"tour-footer\">\n <button\n class=\"tour-skip\"\n aria-label=\"Skip tour\"\n @click=${() => { this.clearAutoAdvance(); this.service.skipTour(); }}\n >\n Skip tour\n </button>\n <div class=\"tour-nav\">\n ${stepIndex > 0 ? html`\n <button\n class=\"tour-btn\"\n aria-label=\"Go to previous step\"\n @click=${() => { this.clearAutoAdvance(); this.service.prevStep(); }}\n >\n ${showNavIcons ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"15 18 9 12 15 6\"></polyline>\n </svg>\n ` : nothing}\n Back\n </button>\n ` : nothing}\n <button\n class=\"tour-btn primary\"\n aria-label=\"${stepIndex === totalSteps - 1 ? finishAriaLabel : 'Go to next step'}\"\n @click=${() => { this.clearAutoAdvance(); this.service.nextStep(); }}\n >\n ${stepIndex === totalSteps - 1 ? finishLabel : 'Next'}\n ${stepIndex < totalSteps - 1 ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"9 18 15 12 9 6\"></polyline>\n </svg>\n ` : showNavIcons ? html`\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n ` : nothing}\n </button>\n </div>\n </div>\n `;\n }\n\n private renderAutoProgress(step: TourStep, stepIndex: number) {\n if (!step.autoAdvance) return nothing;\n return keyed(stepIndex, html`\n <div\n class=\"tour-auto-progress\"\n style=\"animation: autoAdvanceFill ${step.autoAdvance}ms linear forwards;\"\n aria-hidden=\"true\"\n ></div>\n `);\n }\n\n private renderCenteredStep(step: TourStep, stepIndex: number, totalSteps: number) {\n const stepLabel = `Step ${stepIndex + 1} of ${totalSteps}: ${step.title}`;\n\n return html`\n <!-- Screen reader announcement -->\n <div class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n ${stepLabel}\n </div>\n\n <div\n class=\"tour-backdrop ${this.visible ? 'visible' : ''}\"\n part=\"backdrop\"\n @click=${this.handleBackdropClick}\n ></div>\n\n <div\n class=\"tour-center-card ${this.visible ? 'visible' : ''}\"\n part=\"center-card\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"${step.title}\"\n aria-describedby=\"tour-desc-center\"\n tabindex=\"-1\"\n >\n <div class=\"tour-center-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"></path>\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"></line>\n </svg>\n </div>\n\n <h3 class=\"tour-title\">${step.title}</h3>\n <div class=\"tour-message\" id=\"tour-desc-center\">${step.message}</div>\n\n ${this.renderProgressDots(stepIndex, totalSteps)}\n ${this.renderFooter(stepIndex, totalSteps, \"Let's go!\", 'Start the tour', false)}\n ${this.renderAutoProgress(step, stepIndex)}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'torchlit-overlay': TorchlitOverlay;\n }\n}\n"],"names":["offset","clamped"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "torchlit",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Lightweight guided-tour & onboarding library built on Lit web components. Shadow DOM aware, framework-agnostic, tiny footprint.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -51,6 +51,14 @@
51
51
  "peerDependencies": {
52
52
  "lit": "^3.0.0"
53
53
  },
54
+ "peerDependenciesMeta": {
55
+ "lit": {
56
+ "optional": false
57
+ }
58
+ },
59
+ "engines": {
60
+ "node": ">=18"
61
+ },
54
62
  "devDependencies": {
55
63
  "jsdom": "^28.0.0",
56
64
  "lit": "^3.2.1",