@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/CHANGELOG.md +193 -0
- package/README.md +3 -3
- package/builder/index.d.ts +15 -16
- package/config/index.d.ts +1 -1
- package/config/index.js +164 -135
- package/config/types.d.ts +27 -19
- package/definition/step-props.d.ts +9 -1
- package/definition/types.d.ts +10 -10
- package/definition/workflow-definition.d.ts +4 -7
- package/dom/tour-view-driver.d.ts +134 -3
- package/elements/base.d.ts +24 -2
- package/elements/popover.d.ts +23 -6
- package/index.d.ts +1 -1
- package/index.js +896 -503
- package/package.json +6 -4
- package/runtime/active-step.d.ts +18 -55
- package/runtime/tour-controller.d.ts +48 -12
- package/state/focus-guard.d.ts +15 -0
- package/types/index.d.ts +187 -86
- package/utils/options.d.ts +13 -1
- package/utils/utils.d.ts +16 -8
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glowhop/core-tour",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Framework-agnostic tour controller and DOM runtime
|
|
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://
|
|
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"
|
package/runtime/active-step.d.ts
CHANGED
|
@@ -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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 `
|
|
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; `
|
|
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
|
-
|
|
66
|
+
start(workflow: WorkflowDefinition<T>, runOptions?: RunOptions): Promise<void>;
|
|
55
67
|
advance(source?: TourEventSource): Promise<void>;
|
|
56
68
|
previous(source?: TourEventSource): Promise<void>;
|
|
57
|
-
|
|
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
|
-
|
|
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 `
|
|
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-`
|
|
102
|
-
* aborted
|
|
103
|
-
*
|
|
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
|
|
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;
|
package/state/focus-guard.d.ts
CHANGED
|
@@ -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;
|