@glowhop/core-tour 1.3.1 → 1.5.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,9 +1,9 @@
1
1
  {
2
2
  "name": "@glowhop/core-tour",
3
- "version": "1.3.1",
4
- "description": "Framework-agnostic tour controller and DOM runtime for GlowTour.js.",
3
+ "version": "1.5.0",
4
+ "description": "Framework-agnostic product tour engine for GlowTour.js: the tour controller and DOM runtime behind onboarding tours in any framework.",
5
5
  "license": "MIT",
6
- "homepage": "https://github.com/Glowhop/GlowTour.js#readme",
6
+ "homepage": "https://glowtour.dev/",
7
7
  "bugs": {
8
8
  "url": "https://github.com/Glowhop/GlowTour.js/issues"
9
9
  },
@@ -20,7 +20,9 @@
20
20
  "dom",
21
21
  "headless",
22
22
  "framework-agnostic",
23
- "ssr"
23
+ "ssr",
24
+ "onboarding-tour",
25
+ "intro-js-alternative"
24
26
  ],
25
27
  "engines": {
26
28
  "node": ">=18.19.1"
@@ -1,68 +1,31 @@
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;
65
- resolveTarget(signal: AbortSignal): Promise<HTMLElement | null>;
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;
25
+ /** Returns the target, or the promise to wait on when the resolver is async. */
26
+ resolveTarget(signal: AbortSignal): HTMLElement | Promise<HTMLElement | null> | null;
27
+ /** Marks the step detached and returns the body it stands on, or `null` without a document. */
28
+ detach(): HTMLElement;
66
29
  snapshot(): Readonly<{
67
30
  id: string;
68
31
  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;
@@ -37,13 +37,25 @@ export declare class TourController<T> {
37
37
  private recoveringTarget;
38
38
  private operationToken;
39
39
  private publicationRevision;
40
+ /**
41
+ * The operation currently parked on a target that has not resolved yet, or `null`. The step being
42
+ * left is still on screen while a navigation waits, so the wait is reported on its presentation.
43
+ */
44
+ private awaitingTargetOperation;
40
45
  private operation;
46
+ /** Set by `hidePopover()`, see `TourState.popoverHidden`. Only a running tour reports it. */
47
+ private popoverHidden;
41
48
  private disposed;
42
49
  private retainedPresentation;
43
50
  private readonly stateListeners;
44
51
  private readonly stepPropsSubscriptions;
45
52
  private tourStartedAt;
46
53
  private stepEnteredAt;
54
+ /**
55
+ * The step `tour:start` names, while that event is held back. It is emitted just before the
56
+ * first event that can no longer be taken back, so a start that a `beforeEnter` aborts emits nothing.
57
+ */
58
+ private pendingTourStart;
47
59
  private commandSource;
48
60
  readonly state: Readonly<{
49
61
  get: () => TourState<T>;
@@ -51,26 +63,49 @@ export declare class TourController<T> {
51
63
  }>;
52
64
  constructor(driver: TourViewDriver<T>, options?: TourControllerOptions<T>);
53
65
  create(name: string, options?: StartOptions<T>): WorkflowBuilder<T>;
54
- run(workflow: WorkflowDefinition<T>, runOptions?: RunOptions): Promise<void>;
66
+ start(workflow: WorkflowDefinition<T>, runOptions?: RunOptions): Promise<void>;
55
67
  advance(source?: TourEventSource): Promise<void>;
56
68
  previous(source?: TourEventSource): Promise<void>;
57
- goToStep(index: number): Promise<void>;
69
+ goTo(id: string): Promise<void>;
70
+ /** The direction of a jump from the current step to the step at `index`. */
71
+ private directionTo;
58
72
  cancel(source?: TourEventSource): Promise<void>;
73
+ /** `tour.hidePopover()` and `tour.showPopover()`. */
74
+ setPopoverHidden(hidden: boolean): void;
75
+ private isRunning;
59
76
  dispose(): void;
60
77
  isDisposed(): boolean;
61
78
  /** @internal Called by the private root bridge before it releases DOM resources. */
62
79
  beginMountRelease(): void;
63
80
  /** @internal Called after the private root bridge has finished releasing its lease. */
64
81
  completeMountRelease(): void;
65
- private enter;
82
+ /**
83
+ * Moves to the step at `index`, or further in `direction` past steps skipped for a missing target.
84
+ * Nothing is emitted before `beforeEnter` lets the navigation through, so an abort leaves no trace.
85
+ * Then come the held `tour:start`, a `step:skip` per skipped step, and one `step:leave` for the
86
+ * step being left. `lostStep` is the step whose target disappeared during recovery: it cannot stay
87
+ * on screen, so skipping backward past the first step cancels the tour, and a `beforeEnter` abort
88
+ * turns into its missing-target error.
89
+ */
90
+ private navigate;
66
91
  private transitionFromPublic;
67
92
  private transition;
93
+ /** Runs `beforeEnter` or `beforeLeave`, and tells whether it called `abort()` before settling. */
94
+ private runStepHook;
68
95
  private runActions;
69
96
  private resolveTarget;
97
+ /**
98
+ * Reports a navigation waiting on the next step's target. The presentation on screen still
99
+ * belongs to the step being left, so the driver marks that popover and disables its advance
100
+ * control until the target settles. Keyed by operation: a superseded navigation never clears the
101
+ * wait its replacement declared. The freeze of a target lost mid-step is deliberately not a wait
102
+ * here, see `recoverDisconnectedTarget`.
103
+ */
104
+ private setAwaitingTarget;
70
105
  /**
71
106
  * Repeatedly re-resolves `step.target` until it succeeds or `budgetMs`
72
107
  * elapses, polling every 16ms like `resolveTarget`. Unlike `resolveTarget`
73
- * it never applies `missingTargetStrategy` itself — callers decide what a
108
+ * it never applies `missingTarget.strategy` itself — callers decide what a
74
109
  * timed-out budget means (grace period vs. a "wait" strategy's own
75
110
  * timeout), so the same polling loop serves both.
76
111
  */
@@ -91,22 +126,21 @@ export declare class TourController<T> {
91
126
  * comes back, so it has to keep working.
92
127
  */
93
128
  private recoverDisconnectedTarget;
94
- private advancePastMissingTarget;
95
- private advancePastRecoveryMissingTarget;
96
129
  private missingTargetError;
97
130
  private finish;
98
131
  private cancelCurrent;
99
132
  private createLifecycleHookContext;
100
133
  /**
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).
134
+ * Restores the controller to its pre-`start()` idle state. Used when an
135
+ * aborted hook prevents the tour from ever becoming active (`onStart`, the
136
+ * first step's `beforeEnter`, and the zero-step `onFinish` edge case). A
137
+ * tour this `start()` replaced is still on screen, so it is cleared first.
104
138
  */
105
139
  private resetToIdle;
106
140
  private handleFailure;
107
141
  private beginOperation;
108
142
  private createStepContext;
109
- private createBeforeActionStepContext;
143
+ private createStepHookContext;
110
144
  private invalidateOperation;
111
145
  private signalFor;
112
146
  private assertCurrent;
@@ -132,6 +166,8 @@ export declare class TourController<T> {
132
166
  private hasEventListeners;
133
167
  private emit;
134
168
  private notifyEventListener;
169
+ /** Emits the held `tour:start` once, before the first event that can no longer be taken back. */
170
+ private flushTourStart;
135
171
  /** Emits `step:leave` for the step being left, with the time spent on it. */
136
172
  private emitStepLeave;
137
173
  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;