@glowhop/core-tour 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glowhop/core-tour",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Framework-agnostic tour controller and DOM runtime for GlowTour.js.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/Glowhop/GlowTour.js#readme",
@@ -1,68 +1,30 @@
1
1
  import { type ReadonlyStartOptions, type ReadonlyStepProps, type WorkflowStepDefinition } from "../definition";
2
- import type { StepPropsStore } from "../types";
2
+ import type { StepPropsStore, TourDirection } from "../types";
3
3
  export declare class ActiveStep<T> {
4
4
  readonly definition: WorkflowStepDefinition<T>;
5
5
  readonly path: string;
6
6
  private readonly rootDocument?;
7
7
  readonly initialProps: ReadonlyStepProps<T>;
8
8
  readonly props: StepPropsStore<T>;
9
- readonly behavior: import("../types").StepBehavior | undefined;
10
9
  readonly animated: boolean | undefined;
11
- readonly allowScroll: boolean;
12
10
  target: HTMLElement | null;
11
+ /** Shown without its target (`missingTarget.strategy: "detached"`); `target` is then the body. */
12
+ detached: boolean;
13
+ /** The navigation that last brought the tour to this step. */
14
+ direction: TourDirection;
13
15
  constructor(definition: WorkflowStepDefinition<T>, defaults: ReadonlyStartOptions<T>, reportSubscriberError?: (error: unknown) => void, path?: string, rootDocument?: Document | undefined);
14
- reset(): void;
15
- get overlay(): {
16
- readonly color?: string | undefined;
17
- readonly opacity?: number | undefined;
18
- readonly padding?: number | undefined;
19
- readonly radius?: number | undefined;
20
- readonly animated?: boolean | undefined;
21
- readonly animation?: {
22
- readonly duration: number;
23
- readonly easing: string;
24
- } | undefined;
25
- } | undefined;
26
- get popover(): {
27
- readonly placementTryOrder?: readonly import("../types").TryOrderOptions[] | undefined;
28
- readonly arrow?: {
29
- readonly disabled?: boolean | undefined;
30
- readonly color?: string | undefined;
31
- readonly size?: number | undefined;
32
- readonly borderWidth?: number | undefined;
33
- readonly borderRadius?: number | undefined;
34
- readonly edgePadding?: number | undefined;
35
- readonly styleNonce?: string | undefined;
36
- readonly disableAutoStyles?: boolean | undefined;
37
- } | undefined;
38
- readonly hideFooter?: boolean | undefined;
39
- readonly disablePreviousButton?: boolean | undefined;
40
- readonly hidePreviousButton?: boolean | undefined;
41
- readonly disableAdvanceButton?: boolean | undefined;
42
- readonly hideAdvanceButton?: boolean | undefined;
43
- readonly gap?: number | undefined;
44
- readonly keyboardShortcuts?: {
45
- readonly previous?: readonly string[] | undefined;
46
- readonly advance?: readonly string[] | undefined;
47
- readonly cancel?: readonly string[] | undefined;
48
- } | undefined;
49
- readonly animated?: boolean | undefined;
50
- readonly animation?: {
51
- readonly duration: number;
52
- readonly easing: string;
53
- } | undefined;
54
- } | undefined;
55
- get indicator(): {
56
- readonly disabled?: boolean | undefined;
57
- readonly gap?: number | undefined;
58
- readonly placementTryOrder?: readonly import("../types").TryOrderOptions[] | undefined;
59
- readonly animated?: boolean | undefined;
60
- readonly animation?: {
61
- readonly duration: number;
62
- readonly easing: string;
63
- } | undefined;
64
- } | undefined;
16
+ /**
17
+ * Reads `behavior.allowInteraction` live: `props.update({ behavior })` changes it while the step runs.
18
+ * A detached step has no target to interact with, so it always blocks the page.
19
+ */
20
+ allowsInteraction(): boolean;
21
+ /** Reads `behavior.autoFocus` live: `false` hands every focus move to the page. */
22
+ autoFocuses(): boolean;
23
+ /** Reads `behavior.allowScroll` live: `props.update({ behavior })` changes it while the step runs. */
24
+ allowsScroll(): boolean;
65
25
  resolveTarget(signal: AbortSignal): Promise<HTMLElement | null>;
26
+ /** Marks the step detached and returns the body it stands on, or `null` without a document. */
27
+ detach(): HTMLElement;
66
28
  snapshot(): Readonly<{
67
29
  id: string;
68
30
  initialProps: ReadonlyStepProps<T>;
@@ -4,11 +4,11 @@ import { type TourViewDriver } from "../dom/tour-view-driver";
4
4
  import type { GlowTour, GlowTourOptions, RunOptions, StartOptions, TourEventSource, TourState } from "../types";
5
5
  /**
6
6
  * How long a step stays frozen on its last known position after its target
7
- * disappears from the DOM, before the configured `missingTargetStrategy`
7
+ * disappears from the DOM, before the configured `missingTarget.strategy`
8
8
  * takes over. Covers the dominant case — a framework remounting the target
9
9
  * within a frame or two — without a visible unmount/remount flicker. Not
10
10
  * configurable: it is a presentation detail of the recovery, not a policy
11
- * choice; `missingTargetStrategy` and `targetTimeout` remain the only knobs.
11
+ * choice; `missingTarget.strategy` and `missingTarget.timeout` remain the only knobs.
12
12
  * Exported for the test suite's timing assertions only.
13
13
  */
14
14
  export declare const TARGET_LOSS_GRACE_MS = 150;
@@ -44,6 +44,11 @@ export declare class TourController<T> {
44
44
  private readonly stepPropsSubscriptions;
45
45
  private tourStartedAt;
46
46
  private stepEnteredAt;
47
+ /**
48
+ * The step `tour:start` names, while that event is held back. It is emitted just before the
49
+ * first event that can no longer be taken back, so a start that a `beforeEnter` aborts emits nothing.
50
+ */
51
+ private pendingTourStart;
47
52
  private commandSource;
48
53
  readonly state: Readonly<{
49
54
  get: () => TourState<T>;
@@ -51,10 +56,12 @@ export declare class TourController<T> {
51
56
  }>;
52
57
  constructor(driver: TourViewDriver<T>, options?: TourControllerOptions<T>);
53
58
  create(name: string, options?: StartOptions<T>): WorkflowBuilder<T>;
54
- run(workflow: WorkflowDefinition<T>, runOptions?: RunOptions): Promise<void>;
59
+ start(workflow: WorkflowDefinition<T>, runOptions?: RunOptions): Promise<void>;
55
60
  advance(source?: TourEventSource): Promise<void>;
56
61
  previous(source?: TourEventSource): Promise<void>;
57
- goToStep(index: number): Promise<void>;
62
+ goTo(id: string): Promise<void>;
63
+ /** The direction of a jump from the current step to the step at `index`. */
64
+ private directionTo;
58
65
  cancel(source?: TourEventSource): Promise<void>;
59
66
  dispose(): void;
60
67
  isDisposed(): boolean;
@@ -62,15 +69,25 @@ export declare class TourController<T> {
62
69
  beginMountRelease(): void;
63
70
  /** @internal Called after the private root bridge has finished releasing its lease. */
64
71
  completeMountRelease(): void;
65
- private enter;
72
+ /**
73
+ * Moves to the step at `index`, or further in `direction` past steps skipped for a missing target.
74
+ * Nothing is emitted before `beforeEnter` lets the navigation through, so an abort leaves no trace.
75
+ * Then come the held `tour:start`, a `step:skip` per skipped step, and one `step:leave` for the
76
+ * step being left. `lostStep` is the step whose target disappeared during recovery: it cannot stay
77
+ * on screen, so skipping backward past the first step cancels the tour, and a `beforeEnter` abort
78
+ * turns into its missing-target error.
79
+ */
80
+ private navigate;
66
81
  private transitionFromPublic;
67
82
  private transition;
83
+ /** Runs `beforeEnter` or `beforeLeave`, and tells whether it called `abort()` before settling. */
84
+ private runStepHook;
68
85
  private runActions;
69
86
  private resolveTarget;
70
87
  /**
71
88
  * Repeatedly re-resolves `step.target` until it succeeds or `budgetMs`
72
89
  * elapses, polling every 16ms like `resolveTarget`. Unlike `resolveTarget`
73
- * it never applies `missingTargetStrategy` itself — callers decide what a
90
+ * it never applies `missingTarget.strategy` itself — callers decide what a
74
91
  * timed-out budget means (grace period vs. a "wait" strategy's own
75
92
  * timeout), so the same polling loop serves both.
76
93
  */
@@ -91,22 +108,21 @@ export declare class TourController<T> {
91
108
  * comes back, so it has to keep working.
92
109
  */
93
110
  private recoverDisconnectedTarget;
94
- private advancePastMissingTarget;
95
- private advancePastRecoveryMissingTarget;
96
111
  private missingTargetError;
97
112
  private finish;
98
113
  private cancelCurrent;
99
114
  private createLifecycleHookContext;
100
115
  /**
101
- * Restores the controller to its pre-`run()` idle state. Used when an
102
- * aborted lifecycle hook prevents the tour from ever becoming active
103
- * (`onStart` abort, and the zero-step `onFinish` abort edge case).
116
+ * Restores the controller to its pre-`start()` idle state. Used when an
117
+ * aborted hook prevents the tour from ever becoming active (`onStart`, the
118
+ * first step's `beforeEnter`, and the zero-step `onFinish` edge case). A
119
+ * tour this `start()` replaced is still on screen, so it is cleared first.
104
120
  */
105
121
  private resetToIdle;
106
122
  private handleFailure;
107
123
  private beginOperation;
108
124
  private createStepContext;
109
- private createBeforeActionStepContext;
125
+ private createStepHookContext;
110
126
  private invalidateOperation;
111
127
  private signalFor;
112
128
  private assertCurrent;
@@ -132,6 +148,8 @@ export declare class TourController<T> {
132
148
  private hasEventListeners;
133
149
  private emit;
134
150
  private notifyEventListener;
151
+ /** Emits the held `tour:start` once, before the first event that can no longer be taken back. */
152
+ private flushTourStart;
135
153
  /** Emits `step:leave` for the step being left, with the time spent on it. */
136
154
  private emitStepLeave;
137
155
  private reportSubscriberError;
@@ -5,6 +5,8 @@ export interface FocusGuardScope {
5
5
  allowedTarget?: HTMLElement | null;
6
6
  allowTargetInteraction?: boolean;
7
7
  autoFocus?: boolean;
8
+ /** The caller focuses a control later: keep a focus already in scope until then. */
9
+ deferFocus?: boolean;
8
10
  fallback?: HTMLElement | null;
9
11
  }
10
12
  export declare class FocusGuard {
@@ -22,7 +24,20 @@ export declare class FocusGuard {
22
24
  activate(scope: FocusGuardScope): void;
23
25
  update(scope: FocusGuardScope): void;
24
26
  focus(): void;
27
+ /**
28
+ * Remembers the element focus returns to when the guard deactivates. The driver calls it before
29
+ * making the rest of the page inert, because inerting an ancestor blurs the focused element.
30
+ * Does nothing once a focus is remembered or the guard is active. `pending` is a focus a clear
31
+ * could not give back before this show replaced it, and wins over the current focus.
32
+ */
33
+ captureInitialFocus(reference: HTMLElement, pending?: HTMLElement | null): void;
25
34
  deactivate(): void;
35
+ /**
36
+ * Stops guarding and returns the element focus should go back to, without moving focus. Lets
37
+ * the caller restore it once the page has left `inert`: screen readers ignore focus moved onto
38
+ * content their accessibility tree has not caught up with yet.
39
+ */
40
+ release(): HTMLElement | null;
26
41
  private isAllowed;
27
42
  private focusFallback;
28
43
  private setFallback;
package/types/index.d.ts CHANGED
@@ -16,32 +16,70 @@ export interface TargetResolverContext {
16
16
  }
17
17
  /** Configures step-level interaction behavior and error handling. */
18
18
  export interface StepBehavior {
19
- /** Allow user interaction with the page outside the target element. @default false */
19
+ /** Allow user interaction with the target: the page is no longer inert and pointer events reach the target through the cutout, while the dimmed area still catches clicks. Change it during the step with `context.props.update({ behavior: { allowInteraction } })`. @default false */
20
20
  allowInteraction?: boolean;
21
- /** Disable automatic focus on the target when the step is entered. @default false */
22
- disableAutoFocus?: boolean;
23
- /** Disable automatic scroll to the target when the step is entered. @default false */
24
- disableAutoScroll?: boolean;
25
- /** How to handle when the target is not found: `"wait"` waits and retries, `"skip"` advances to next step, `"error"` halts the tour. @default "error" */
26
- missingTargetStrategy?: "wait" | "skip" | "error";
21
+ /** Leave the page scrollable while the step is shown; set `false` to lock it. Change it during the step with `context.props.update({ behavior: { allowScroll } })`. @default true */
22
+ allowScroll?: boolean;
23
+ /**
24
+ * Focus the popover's Advance control (Back when going back) when the step is shown. Set `false`
25
+ * to never move focus during the step: it stays where it is, on the body when a modal step
26
+ * makes the page inert, and placing it is up to you. Focus that leaves the popover and target
27
+ * is still pulled back, and focus returns to where it was when the tour ends. @default true
28
+ */
29
+ autoFocus?: boolean;
30
+ /** Scroll the target into view when the step is entered. @default true */
31
+ autoScroll?: boolean;
32
+ /** What the step does when its target cannot be found. */
33
+ missingTarget?: MissingTargetOptions;
27
34
  /** Scroll behavior options. */
28
35
  scroll?: ScrollOptions;
29
- /** Timeout in ms to wait for target to appear before applying missingTargetStrategy. @default 3000 */
30
- targetTimeout?: number;
31
36
  /**
32
37
  * Behavior when the dimmed overlay backdrop (outside the cutout around the
33
38
  * target) is clicked: `"advance"` moves to the next step, `"cancel"` ends
34
39
  * the tour, `"none"` ignores the click. Has no effect when
35
- * `allowInteraction` is `true`, since the page stays fully interactive and
36
- * there is no modal backdrop to click.
40
+ * `allowInteraction` is `true`: clicks on the dimmed area are then ignored.
37
41
  * @default "none"
38
42
  */
39
43
  overlayClick?: "none" | "advance" | "cancel";
40
44
  }
45
+ /** How a step handles a target that cannot be found. */
46
+ export interface MissingTargetOptions {
47
+ /**
48
+ * `"wait"` retries until `timeout`, `"skip"` moves past the step, `"error"` fails the tour,
49
+ * `"detached"` shows the popover centered in the viewport over a backdrop that covers the whole
50
+ * screen. A detached step has no pointer and no cutout, does not scroll, keeps the page blocked
51
+ * even when `allowInteraction` is `true`, and binds no `targetEvents`; its `context.target` is the
52
+ * document's `<body>`.
53
+ * @default "error"
54
+ */
55
+ strategy?: "wait" | "skip" | "error" | "detached";
56
+ /** How long to look for the target with the `"wait"` strategy, in milliseconds. @default 3000 */
57
+ timeout?: number;
58
+ }
41
59
  /** Placement directions for positioning the pointer or popover around the target. */
42
60
  export type TryOrderOptions = "top" | "bottom" | "left" | "right";
43
61
  /** A resolved placement direction, including `"center"` for centered positioning. */
44
62
  export type ResolvedPlacement = TryOrderOptions | "center";
63
+ /** One CSS class, or several. A string may hold several space-separated classes. */
64
+ export type ClassValue = string | readonly string[];
65
+ /**
66
+ * Classes added to the tour components while a step is shown, one entry per component.
67
+ *
68
+ * They are added to the classes passed to the component itself, never replacing them. A step's entry
69
+ * overrides the workflow's entry for the same component; the components the step leaves out keep the
70
+ * workflow's classes.
71
+ */
72
+ export interface TourClassNames {
73
+ overlay?: ClassValue;
74
+ popover?: ClassValue;
75
+ pointer?: ClassValue;
76
+ header?: ClassValue;
77
+ content?: ClassValue;
78
+ footer?: ClassValue;
79
+ advance?: ClassValue;
80
+ previous?: ClassValue;
81
+ cancel?: ClassValue;
82
+ }
45
83
  /** Base configuration for animated elements. */
46
84
  export interface BaseOptions {
47
85
  /** Enable or disable animations. */
@@ -51,8 +89,8 @@ export interface BaseOptions {
51
89
  }
52
90
  /** Configures the pointer indicator that highlights the target element. */
53
91
  export interface IndicatorOptions extends BaseOptions {
54
- /** Hide the indicator. @default false */
55
- disabled?: boolean;
92
+ /** Hide the indicator. It only shows on steps that allow interaction. @default false */
93
+ hidden?: boolean;
56
94
  /** Gap between the target and the indicator in pixels. */
57
95
  gap?: number;
58
96
  /** Placement preference order when positioning the indicator. @default ["left", "right", "top", "bottom"] */
@@ -72,7 +110,7 @@ export interface OverlayOptions extends BaseOptions {
72
110
  /** Configures the arrow that points from the popover to the target. */
73
111
  export interface PopoverArrowOptions {
74
112
  /** Hide the arrow. @default false */
75
- disabled?: boolean;
113
+ hidden?: boolean;
76
114
  /** Color of the arrow (CSS color). */
77
115
  color?: string;
78
116
  /** Size of the arrow in pixels. @default 12 */
@@ -90,11 +128,41 @@ export interface PopoverArrowOptions {
90
128
  */
91
129
  styleNonce?: string;
92
130
  /**
93
- * Skip injecting the built-in arrow `<style>` element entirely. Provide the
131
+ * Inject the built-in arrow `<style>` element. Set `false` to provide the
94
132
  * equivalent rules yourself through whatever channel your CSP allows, such
95
133
  * as an external stylesheet.
134
+ * @default true
96
135
  */
97
- disableAutoStyles?: boolean;
136
+ autoStyles?: boolean;
137
+ }
138
+ /**
139
+ * Whether the user may run a tour control. `"enabled"` is the default. New states may be added in
140
+ * a minor version.
141
+ */
142
+ export type TourControlState = "enabled" | "disabled";
143
+ /** One navigation command: whether the user may run it and the keys that run it. */
144
+ export interface TourControl {
145
+ /**
146
+ * `"disabled"` blocks the command everywhere the tour UI offers it: its button is disabled, and
147
+ * its keys and `overlayClick` do nothing. Navigation through the tour API and the step context
148
+ * stays available. To hide a button, give it a class through `classNames`.
149
+ * @default "enabled"
150
+ */
151
+ state?: TourControlState;
152
+ /** Keys that run the command while the step is shown. An empty array turns them off. */
153
+ keys?: readonly string[];
154
+ }
155
+ /**
156
+ * The advance, previous and cancel commands. A step's controls override the workflow ones field by
157
+ * field: a step that only sets `advance.state` keeps the workflow's `advance.keys`.
158
+ */
159
+ export interface TourControls {
160
+ /** @default { state: "enabled", keys: ["Enter", "ArrowRight"] } */
161
+ advance?: TourControl;
162
+ /** @default { state: "enabled", keys: ["ArrowLeft", "Backspace"] } */
163
+ previous?: TourControl;
164
+ /** @default { state: "enabled", keys: ["Escape"] } */
165
+ cancel?: TourControl;
98
166
  }
99
167
  /** Configures the popover box that displays content for each step. */
100
168
  export interface PopoverOptions extends BaseOptions {
@@ -102,39 +170,8 @@ export interface PopoverOptions extends BaseOptions {
102
170
  placementTryOrder?: readonly TryOrderOptions[];
103
171
  /** Arrow configuration. */
104
172
  arrow?: PopoverArrowOptions;
105
- /** Hide the footer section. @default false */
106
- hideFooter?: boolean;
107
- /**
108
- * Disables only previous-button and previous-keyboard controls. Programmatic
109
- * navigation through the tour API and step context remains available.
110
- */
111
- disablePreviousButton?: boolean;
112
- /** Hide the previous button. @default false */
113
- hidePreviousButton?: boolean;
114
- /**
115
- * Disables only advance-button and advance-keyboard controls. Programmatic
116
- * navigation through the tour API and step context remains available.
117
- */
118
- disableAdvanceButton?: boolean;
119
- /** Hide the advance button. @default false */
120
- hideAdvanceButton?: boolean;
121
173
  /** Gap between the target and the popover in pixels. @default 16 */
122
174
  gap?: number;
123
- /** Keyboard shortcuts for navigation. */
124
- keyboardShortcuts?: {
125
- /**
126
- * Keys that trigger previous step. @default ["ArrowLeft", "Backspace"]
127
- */
128
- previous?: readonly string[];
129
- /**
130
- * Keys that trigger advance step. @default ["Enter", "ArrowRight"]
131
- */
132
- advance?: readonly string[];
133
- /**
134
- * Keys that trigger cancel. @default ["Escape"]
135
- */
136
- cancel?: readonly string[];
137
- };
138
175
  }
139
176
  /**
140
177
  * Scroll behavior options passed to Element.scrollIntoView().
@@ -165,8 +202,8 @@ export interface AnimationOptions {
165
202
  export interface LifecycleHookContext<T> {
166
203
  /**
167
204
  * The step associated with this lifecycle transition:
168
- * - `onStart`: the first step about to be entered (`workflow.steps[0]`), or
169
- * `null` if the workflow has no steps.
205
+ * - `onStart`: the step `start()` starts on (the `startAt` step, or the first
206
+ * step), or `null` if the workflow has no steps.
170
207
  * - `onCancel`: the step the tour is currently on when cancellation is
171
208
  * requested. Always non-null in practice, since a step is always active
172
209
  * at the point a tour can be cancelled.
@@ -190,15 +227,6 @@ export interface LifecycleHookContext<T> {
190
227
  }
191
228
  /** Options for starting a tour workflow. */
192
229
  export interface StartOptions<T> {
193
- /** Allow users to cancel the tour. @default true */
194
- cancellable?: boolean;
195
- /**
196
- * Leaves page scroll available while the tour is active. Set `false` to lock
197
- * scroll instead, restoring it on finish, cancel, error, or dispose.
198
- *
199
- * @default true
200
- */
201
- allowScroll?: boolean;
202
230
  /** Default overlay options for all steps. */
203
231
  overlay?: OverlayOptions;
204
232
  /** Default popover options for all steps. */
@@ -209,6 +237,10 @@ export interface StartOptions<T> {
209
237
  animated?: boolean;
210
238
  /** Default step behavior for all steps. */
211
239
  behavior?: StepBehavior;
240
+ /** Default controls for all steps. See `TourControls`. */
241
+ controls?: TourControls;
242
+ /** Classes added to the tour components on every step. See `TourClassNames`. */
243
+ classNames?: TourClassNames;
212
244
  /** Lifecycle hook called when the tour starts. */
213
245
  onStart?: (context: LifecycleHookContext<T>) => void | Promise<void>;
214
246
  /** Lifecycle hook called when the tour is cancelled. */
@@ -225,12 +257,22 @@ export interface StartOptions<T> {
225
257
  }
226
258
  /** Update to step properties, either as a full replacement or via a function. */
227
259
  export type StepPropsUpdate<T> = ReadonlyStepProps<T> | ((current: ReadonlyStepProps<T>) => ReadonlyStepProps<T>);
260
+ /**
261
+ * Partial change to step properties, for `StepPropsStore.update`. Fields it leaves out are kept.
262
+ * `data` is merged key by key; `overlay`, `popover`, `indicator`, `behavior` and `controls` are
263
+ * merged the way step options merge over workflow defaults; arrays such as `placementTryOrder` or
264
+ * `keys` are replaced. `classNames` is merged per component: a component it names gets exactly the
265
+ * classes given.
266
+ */
267
+ export type StepPropsPatch<T> = Partial<ReadonlyStepProps<T>>;
228
268
  /** Store for the current step's properties. */
229
269
  export interface StepPropsStore<T> {
230
270
  /** Get the current step properties. */
231
271
  get(): ReadonlyStepProps<T>;
232
- /** Update the current step properties. */
272
+ /** Replace the current step properties. */
233
273
  set(update: StepPropsUpdate<T>): void;
274
+ /** Merge a partial change into the current step properties. See `StepPropsPatch`. */
275
+ update(patch: StepPropsPatch<T> | ((current: ReadonlyStepProps<T>) => StepPropsPatch<T>)): void;
234
276
  /** Subscribe to changes in step properties. Returns an unsubscribe function. */
235
277
  subscribe(listener: (props: ReadonlyStepProps<T>) => void): () => void;
236
278
  }
@@ -242,17 +284,39 @@ export interface StepContext<T> {
242
284
  cancel(): Promise<void>;
243
285
  /** Navigate to the previous step. */
244
286
  previous(): Promise<void>;
245
- /** The DOM element being highlighted for this step. */
287
+ /**
288
+ * Navigate to the step with this id, skipping the steps in between. Stops the remaining actions
289
+ * of this step, like `advance()`. Throws when no step has this id.
290
+ */
291
+ goTo(id: string): Promise<void>;
292
+ /**
293
+ * The direction of the navigation that entered this step. Captured when the context is created, so
294
+ * it does not change while the step's callbacks run.
295
+ */
296
+ readonly direction: TourDirection;
297
+ /** The step properties as initially configured, before any `props.set()`. */
298
+ readonly initialProps: ReadonlyStepProps<T>;
299
+ /** The DOM element being highlighted for this step, or the document's `<body>` for a detached step. */
246
300
  readonly target: HTMLElement;
247
301
  /** Store for reading and updating the current step's properties. */
248
302
  readonly props: StepPropsStore<T>;
249
303
  /** Signal that aborts when the step is exited or the tour is cancelled. */
250
304
  readonly signal: AbortSignal;
251
305
  }
252
- /** Context passed to transition hooks (beforeAdvance, beforePrevious, beforeCancel). */
253
- export type BeforeActionStepContext<T> = Readonly<ReadonlyStepProps<T> & {
254
- readonly target: HTMLElement;
255
- }>;
306
+ /**
307
+ * Context passed to the `beforeEnter` and `beforeLeave` step hooks. It has no navigation methods:
308
+ * a transition is already in progress when these hooks run. `direction` is the direction of that
309
+ * navigation, so the step being left and the step being entered see the same value.
310
+ */
311
+ export interface StepHookContext<T> extends Omit<StepContext<T>, "advance" | "cancel" | "goTo" | "previous"> {
312
+ /**
313
+ * Call it synchronously, or before the hook's returned promise resolves, to stop the navigation.
314
+ * The tour stays on the step it was on and emits nothing: `beforeLeave` keeps the step, and
315
+ * `beforeEnter` does not show the next one. When `beforeEnter` aborts the first step of `start()`,
316
+ * the tour goes back to `idle`, like an `onStart` abort.
317
+ */
318
+ abort(): void;
319
+ }
256
320
  /** Context passed to target event handlers. */
257
321
  export type StepEventContext<T> = StepContext<T>;
258
322
  /** Options for the waitUntil step action. */
@@ -268,16 +332,16 @@ export type StepActionResult = boolean | void;
268
332
  export type StepAction<T> = (context: StepContext<T>) => Promise<StepActionResult> | StepActionResult;
269
333
  /** A step action or a delay in milliseconds. */
270
334
  export type StepActionInstruction<T> = StepAction<T> | number;
271
- /** A callback that runs before transitioning to the next/previous step or cancelling. */
272
- export type StepTransitionAction<T> = (context: BeforeActionStepContext<T>) => void | Promise<void>;
335
+ /** A callback that runs before a step is entered or left (`beforeEnter` / `beforeLeave`). */
336
+ export type StepHookAction<T> = (context: StepHookContext<T>) => void | Promise<void>;
273
337
  /** Handler for an event fired on the target element during a step. */
274
- export interface EventHandler<TStepProps, TEvent extends Event = Event> {
338
+ export interface TargetEventHandler<TStepProps, TEvent extends Event = Event> {
275
339
  /** Event name(s) to listen for. */
276
340
  event: string;
277
341
  /** Callback invoked when the event fires. */
278
342
  callback: (event: TEvent, context: StepEventContext<TStepProps>) => void | Promise<void>;
279
343
  }
280
- /** Tour lifecycle status. */
344
+ /** Tour lifecycle status. New statuses may be added in a minor release: keep a default branch when switching over it. */
281
345
  export type TourStatus = "idle" | "starting" | "transitioning" | "active" | "finished" | "cancelled" | "error" | "disposed";
282
346
  /** Direction of tour navigation. */
283
347
  export type TourDirection = "advance" | "previous";
@@ -331,13 +395,16 @@ export interface GlowTour<T> {
331
395
  /** Create a new workflow builder with the given name. */
332
396
  create(name: string, options?: StartOptions<T>): WorkflowBuilder<T>;
333
397
  /** Run a workflow, optionally starting at a specific step. */
334
- run(workflow: WorkflowDefinition<T>, options?: RunOptions): Promise<void>;
398
+ start(workflow: WorkflowDefinition<T>, options?: RunOptions): Promise<void>;
335
399
  /** Advance to the next step. */
336
400
  advance(): Promise<void>;
337
401
  /** Go to the previous step. */
338
402
  previous(): Promise<void>;
339
- /** Jump to a specific step by index. */
340
- goToStep(index: number): Promise<void>;
403
+ /**
404
+ * Go to the step with this id, skipping the steps in between. Does nothing while a transition is
405
+ * in progress or when that step is already shown. Throws when no step has this id.
406
+ */
407
+ goTo(id: string): Promise<void>;
341
408
  /** Cancel the current tour. */
342
409
  cancel(): Promise<void>;
343
410
  /** Dispose the tour and free resources. */
@@ -346,7 +413,7 @@ export interface GlowTour<T> {
346
413
  readonly state: ReadonlyTourState<T>;
347
414
  }
348
415
  /**
349
- * Per-run options. Unlike `StartOptions`, these belong to one `run()` call and
416
+ * Per-run options. Unlike `StartOptions`, these belong to one `start()` call and
350
417
  * are never baked into the reusable workflow definition.
351
418
  */
352
419
  export interface RunOptions {
@@ -365,13 +432,15 @@ export interface RunOptions {
365
432
  /**
366
433
  * What triggered a transition.
367
434
  *
368
- * `"api"` covers every call your own code makes — `advance()`, `previous()`,
369
- * `goToStep()`, `cancel()`, and the `context.advance()` available inside a step
370
- * action. The other three are the user acting on the tour UI directly.
435
+ * `"api"` covers every call your own code makes: `advance()`, `previous()`,
436
+ * `goTo()`, `cancel()`, and the same methods on the context of a step action.
437
+ * The other three are the user acting on the tour UI directly.
438
+ *
439
+ * New sources may be added in a minor release: keep a default branch when switching over it.
371
440
  */
372
441
  export type TourEventSource = "api" | "trigger" | "keyboard" | "overlay";
373
- /** Name of a monitoring event. */
374
- export type TourEventType = "tour:start" | "step:enter" | "step:leave" | "tour:complete" | "tour:cancel" | "tour:error";
442
+ /** Name of a monitoring event. New events may be added in a minor release: keep a default branch when switching over it. */
443
+ export type TourEventType = "tour:start" | "step:enter" | "step:leave" | "step:skip" | "tour:complete" | "tour:cancel" | "tour:error";
375
444
  /**
376
445
  * A monitoring event, as handed to `onEvent`.
377
446
  *
@@ -402,7 +471,7 @@ export interface TourEvent {
402
471
  * How long the thing this event names had been running, in milliseconds.
403
472
  *
404
473
  * For `step:leave`, the time spent on that step. For `tour:complete`,
405
- * `tour:cancel` and `tour:error`, the time since `run()` was called. For
474
+ * `tour:cancel` and `tour:error`, the time since `start()` was called. For
406
475
  * `tour:start` and `step:enter` — the beginnings — always `0`.
407
476
  */
408
477
  readonly durationMs: number;
@@ -444,11 +513,6 @@ export type StepParameters<T> = {
444
513
  id: string;
445
514
  /** The target element or selector for this step. */
446
515
  target: TargetResolver;
447
- /**
448
- * Reset step properties to initial values when entering this step.
449
- * @default true
450
- */
451
- resetPropsOnEnter?: boolean;
452
516
  /** Overlay options for this step (overrides workflow defaults). */
453
517
  overlay?: OverlayOptions;
454
518
  /** Popover options for this step (overrides workflow defaults). */
@@ -457,8 +521,12 @@ export type StepParameters<T> = {
457
521
  indicator?: IndicatorOptions;
458
522
  /** Step behavior (overrides workflow defaults). */
459
523
  behavior?: StepBehavior;
460
- /** The title content for this step. */
461
- title: T;
524
+ /** Navigation controls for this step (overrides workflow defaults field by field). See `TourControls`. */
525
+ controls?: TourControls;
526
+ /** Classes added to the tour components on this step (overrides workflow defaults per component). See `TourClassNames`. */
527
+ classNames?: TourClassNames;
528
+ /** The title content for this step. Without a title, the popover is named by its content. */
529
+ title?: T;
462
530
  /** The body content for this step. */
463
531
  content: T;
464
532
  /** Arbitrary data associated with this step. */
@@ -1,7 +1,19 @@
1
- import type { AnimationOptions, IndicatorOptions, OverlayOptions, PopoverOptions, ScrollOptions, StepBehavior } from "../types";
1
+ import type { DeepReadonly, ReadonlyStepProps } from "../definition";
2
+ import type { AnimationOptions, IndicatorOptions, OverlayOptions, PopoverOptions, ScrollOptions, StepBehavior, StepPropsPatch, TourControls } from "../types";
3
+ /**
4
+ * Merges a partial change into step props: fields it leaves out are kept, `data` is merged key by
5
+ * key, `overlay` / `popover` / `indicator` / `behavior` / `controls` go through their option merges,
6
+ * `classNames` is merged per component, and arrays are replaced.
7
+ * Builds a step's initial props over the workflow defaults, and backs `StepPropsStore.update`.
8
+ */
9
+ export declare function mergeStepProps<T>(base: StepPropsPatch<T>, patch: StepPropsPatch<T>): ReadonlyStepProps<T>;
2
10
  export declare function mergeOverlayOptions(defaults?: OverlayOptions, overrides?: OverlayOptions): OverlayOptions | undefined;
3
11
  export declare function mergeIndicatorOptions(defaults?: IndicatorOptions, overrides?: IndicatorOptions): IndicatorOptions | undefined;
4
12
  export declare function mergePopoverOptions(defaults?: PopoverOptions, overrides?: PopoverOptions): PopoverOptions | undefined;
5
13
  export declare function mergeScrollOptions(defaults?: ScrollOptions, overrides?: ScrollOptions): ScrollOptions | undefined;
6
14
  export declare function mergeAnimationOptions(defaults?: AnimationOptions, overrides?: AnimationOptions): AnimationOptions | undefined;
7
15
  export declare function mergeStepBehavior(defaults?: StepBehavior, overrides?: StepBehavior): StepBehavior | undefined;
16
+ /** Whether a control is enabled, so its button, keys and `overlayClick` may run its command. */
17
+ export declare function isControlAvailable(props: {
18
+ readonly controls?: DeepReadonly<TourControls>;
19
+ } | undefined, command: "advance" | "previous" | "cancel"): boolean;