Service

WrTour

Guided product tour — a spotlight over one element at a time plus a popup explaining it.

Installation

import { WrTour } from 'ngwr/tour';

// Nothing to import into a component — the service owns the overlay.
private readonly tour = inject(WrTour);

Run a tour

Steps are data, not markup: start() takes an array and the service owns the overlay, the cut-out, focus and the keyboard. Escape ends the tour and focus returns where it began. The demo below has a deliberate fourth step pointing at an element that is not on the page — it is skipped rather than shown floating, which is what keeps a tour alive when a feature sits behind a flag or a permission.

this.tour.start([
  { target: '[data-tour="search"]', title: 'Start here', content: 'Type a name…' },
  { target: '[data-tour="filter"]', content: 'Filters stack.', placement: 'right' },
  { target: '[data-tour="save"]', content: 'Nothing is saved until you press this.', placement: 'top' },
]);

Reading the state

Progress is exposed as signals, so a template can show its own progress bar or a “resume tour” affordance without subscribing to anything.

// Signals, so a template can react without a subscription.
tour.active();  // is a tour running
tour.index();   // 0-based step, -1 when idle
tour.total();   // how many steps
tour.step();    // the current WrTourStep | null

API

NameDescriptionTypeDefault
WrTourInjectable service. No module, no component to place.service
start(steps)Begin a tour. Restarts if one is running; a no-op under SSR.(steps: readonly WrTourStep[]) => void
next()Advance. Past the last step it finishes the tour.() => void
prev()Go back. Stays put on the first step.() => void
stop()End the tour and return focus where it started.() => void
activeWhether a tour is running.Signal<boolean>false
index0-based index of the current step, -1 when idle.Signal<number>-1
totalStep count of the running tour.Signal<number>0
stepThe step being shown.Signal<WrTourStep | null>null
WrTourStepOne stop on the tour.interface
targetCSS selector resolved when the step opens, or the element itself. A step matching nothing is SKIPPED.string | HTMLElement— (required)
titleHeading above the copy.string
contentThe step's body text.string— (required)
placementPreferred side. Falls back to the opposite side near a viewport edge.'top' | 'bottom' | 'left' | 'right''bottom'