@steppy/angular 0.0.0-stage → 0.3.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/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +128 -2
- package/fesm2022/steppy-angular.mjs +490 -0
- package/fesm2022/steppy-angular.mjs.map +1 -0
- package/package.json +41 -3
- package/types/steppy-angular.d.ts +248 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,129 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @steppy/angular
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Angular adapter for Steppy's in-app tours: guided popovers over your
|
|
4
|
+
live app, compiled from the same Markdown scenarios your end-to-end tests run.
|
|
5
|
+
The overlay itself is `<steppy-tour-host>` from `@steppy/player/tour`; this
|
|
6
|
+
package mounts it the Angular way.
|
|
7
|
+
|
|
8
|
+
- `provideSteppyTours({...})`: configuration, in `app.config.ts`.
|
|
9
|
+
- `<steppy-tours />`: the overlay, placed **once** in the app shell.
|
|
10
|
+
- `SteppyTours`: a root service to `start()` / `stop()` / `next()` tours,
|
|
11
|
+
with `active`, `current` and `lastError` signals and an `events` stream.
|
|
12
|
+
- `steppyTourStart`: a directive that starts a tour on click.
|
|
13
|
+
|
|
14
|
+
Angular 20–22, zone.js or zoneless, SSR-safe (nothing renders or loads on the
|
|
15
|
+
server). Your app needs no `CUSTOM_ELEMENTS_SCHEMA`.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @steppy/angular @steppy/player
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`@steppy/player` is a peer dependency at the same version as the adapter.
|
|
24
|
+
|
|
25
|
+
To use an unreleased build, build both from a Steppy checkout:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm --prefix packages/player ci
|
|
29
|
+
npm --prefix packages/player run build
|
|
30
|
+
npm --prefix packages/angular ci
|
|
31
|
+
npm --prefix packages/angular run build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then, in your app, either depend on the folders
|
|
35
|
+
(`"@steppy/player": "file:<checkout>/packages/player"`,
|
|
36
|
+
`"@steppy/angular": "file:<checkout>/packages/angular/dist"`, and
|
|
37
|
+
`"preserveSymlinks": true` in the build options so Angular is not loaded twice),
|
|
38
|
+
or install the tarballs `npm pack` makes from those two folders.
|
|
39
|
+
|
|
40
|
+
## Use
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// app.config.ts
|
|
44
|
+
import { inject } from '@angular/core';
|
|
45
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
46
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
47
|
+
import { map } from 'rxjs';
|
|
48
|
+
import { provideSteppyTours } from '@steppy/angular';
|
|
49
|
+
|
|
50
|
+
export const appConfig: ApplicationConfig = {
|
|
51
|
+
providers: [
|
|
52
|
+
provideRouter(routes),
|
|
53
|
+
provideSteppyTours({
|
|
54
|
+
// Written by `steppy tours` (an app-assets target in public/tours).
|
|
55
|
+
fallbackManifest: 'tours/tours.manifest.json',
|
|
56
|
+
// Optional: follow your i18n library. Without it the tours follow <html lang>.
|
|
57
|
+
locale: () => {
|
|
58
|
+
const translate = inject(TranslateService);
|
|
59
|
+
return toSignal(translate.onLangChange.pipe(map((e) => e.lang)), { initialValue: translate.getCurrentLang() ?? '' });
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// app.component.ts
|
|
68
|
+
@Component({
|
|
69
|
+
selector: 'app-root',
|
|
70
|
+
imports: [RouterOutlet, SteppyToursComponent, SteppyTourStart],
|
|
71
|
+
template: `
|
|
72
|
+
<button steppyTourStart="create-invoice">Show me how</button>
|
|
73
|
+
<router-outlet />
|
|
74
|
+
<steppy-tours (tourComplete)="onDone($event)" />
|
|
75
|
+
`,
|
|
76
|
+
})
|
|
77
|
+
export class AppComponent {}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// anywhere
|
|
82
|
+
const tours = inject(SteppyTours);
|
|
83
|
+
await tours.start('send-invoice', { params: { invoiceId: 1001 } });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### What the adapter does for you
|
|
87
|
+
|
|
88
|
+
- **Lazy, browser-only loading.** The overlay module is imported after the
|
|
89
|
+
first render, and only in the browser.
|
|
90
|
+
- **Router navigation.** A tour that starts away from its entry route
|
|
91
|
+
navigates with `Router.navigateByUrl`, inside the Angular zone, so the routed
|
|
92
|
+
view renders in zone.js apps. A guard that refuses or redirects the
|
|
93
|
+
navigation fails the tour at once (`tour-error`, reason `entry-params`)
|
|
94
|
+
instead of leaving it waiting for an element that will never render. Set
|
|
95
|
+
`navigate: 'document'` for full page loads, or pass your own function.
|
|
96
|
+
- **Zones.** The overlay's observers, polls and frames run outside the Angular
|
|
97
|
+
zone, so they never drive change detection; events, outputs and the router
|
|
98
|
+
come back inside it.
|
|
99
|
+
- **`start()` from anywhere.** It waits for `<steppy-tours>` to mount and the
|
|
100
|
+
overlay to load (`readyTimeout`, 10 s by default), the last call wins, and it
|
|
101
|
+
resolves `false` — never rejects — when a tour cannot start.
|
|
102
|
+
|
|
103
|
+
Destroying `<steppy-tours>` suspends a running tour rather than dismissing
|
|
104
|
+
it (the overlay keeps its resume record). Call `tours.stop()` first to end it.
|
|
105
|
+
|
|
106
|
+
Inputs on `<steppy-tours>` override the `provideSteppyTours()` settings:
|
|
107
|
+
`fallbackManifest`, `manifest`, `buildId`, `guideOrigin`, `locale`,
|
|
108
|
+
`targetTimeout`, `baseZIndex`, `disabled`, `strings`, `source`. Outputs:
|
|
109
|
+
`tourStart`, `tourStep`, `tourComplete`, `tourDismiss`, `tourOffpath`,
|
|
110
|
+
`tourError`.
|
|
111
|
+
|
|
112
|
+
Hash routing (`withHashLocation()`) is not supported by in-app tours.
|
|
113
|
+
|
|
114
|
+
The full reference — authoring tours, the overlay's options and events,
|
|
115
|
+
remote mode, CSP and Trusted Types — is `docs/tours.md` in the Steppy
|
|
116
|
+
repository.
|
|
117
|
+
|
|
118
|
+
## Develop
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm ci
|
|
122
|
+
npm test # the zoneless suite, then the zone.js suite (ng test --configuration zone)
|
|
123
|
+
npm run build # ng-packagr → dist/; publish dist/, never this folder
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The overlay is replaced by a fake element in the specs (`src/testing/`), except
|
|
127
|
+
in `real-overlay.spec.ts`. The browser test against a real Angular + PrimeNG
|
|
128
|
+
app is `tests/integration/test_tour_angular_it.py` (the demo is
|
|
129
|
+
`fixtures/tour-demo-angular/`).
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, NgZone, PLATFORM_ID, signal, Injectable, computed, isSignal, makeEnvironmentProviders, provideEnvironmentInitializer, isDevMode, input, output, DOCUMENT, ElementRef, afterNextRender, effect, DestroyRef, untracked, ChangeDetectionStrategy, Component, Directive } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
4
|
+
import { Subject } from 'rxjs';
|
|
5
|
+
import { ROUTES, Router } from '@angular/router';
|
|
6
|
+
|
|
7
|
+
const STEPPY_TOURS_CONFIG = new InjectionToken('STEPPY_TOURS_CONFIG');
|
|
8
|
+
const DEFAULT_READY_TIMEOUT_MS = 10_000;
|
|
9
|
+
|
|
10
|
+
const PREFIX$1 = '[steppy-tours]';
|
|
11
|
+
/**
|
|
12
|
+
* Start and follow in-app tours from anywhere in the app.
|
|
13
|
+
*
|
|
14
|
+
* The overlay itself lives in ``<steppy-tours>``, placed once in the app
|
|
15
|
+
* shell; ``start()`` waits for it to be mounted and loaded (up to
|
|
16
|
+
* ``readyTimeout``), so a help button can call it during startup. State is
|
|
17
|
+
* built from the overlay's six events and exposed as signals; on the server
|
|
18
|
+
* every call is a no-op.
|
|
19
|
+
*/
|
|
20
|
+
class SteppyTours {
|
|
21
|
+
zone = inject(NgZone);
|
|
22
|
+
browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
23
|
+
/** @internal */
|
|
24
|
+
ɵconfig = inject(STEPPY_TOURS_CONFIG, { optional: true }) ?? {};
|
|
25
|
+
/** @internal The configured locale source, resolved once in the root injection context. */
|
|
26
|
+
ɵlocale = resolveLocale(this.ɵconfig.locale);
|
|
27
|
+
_active = signal(false, ...(ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []));
|
|
28
|
+
_current = signal(null, ...(ngDevMode ? [{ debugName: "_current" }] : /* istanbul ignore next */ []));
|
|
29
|
+
_lastError = signal(null, ...(ngDevMode ? [{ debugName: "_lastError" }] : /* istanbul ignore next */ []));
|
|
30
|
+
_events = new Subject();
|
|
31
|
+
/** A tour is running (from ``tour-start`` until it completes, is dismissed or fails). */
|
|
32
|
+
active = this._active.asReadonly();
|
|
33
|
+
/** The step shown, or ``null`` before the first step and after the end. */
|
|
34
|
+
current = this._current.asReadonly();
|
|
35
|
+
/** The last ``tour-error`` since the last start (fatal or not). */
|
|
36
|
+
lastError = this._lastError.asReadonly();
|
|
37
|
+
/** Every overlay event, in order, delivered inside the Angular zone. */
|
|
38
|
+
events = this._events.asObservable();
|
|
39
|
+
owner = null;
|
|
40
|
+
host = null;
|
|
41
|
+
failed = false;
|
|
42
|
+
waiters = [];
|
|
43
|
+
seq = 0;
|
|
44
|
+
/**
|
|
45
|
+
* Start a tour. Resolves ``true`` once it started, ``false`` when it could
|
|
46
|
+
* not (a ``tour-error`` says why), when no ``<steppy-tours>`` is mounted
|
|
47
|
+
* in time, when a later ``start()``/``stop()`` superseded this call, or on
|
|
48
|
+
* the server. Never rejects.
|
|
49
|
+
*/
|
|
50
|
+
async start(tourId, options = {}) {
|
|
51
|
+
if (!this.browser)
|
|
52
|
+
return false;
|
|
53
|
+
const seq = ++this.seq;
|
|
54
|
+
const host = await this.whenReady();
|
|
55
|
+
if (seq !== this.seq)
|
|
56
|
+
return false;
|
|
57
|
+
if (!host) {
|
|
58
|
+
console.warn(this.failed
|
|
59
|
+
? `${PREFIX$1} start("${tourId}"): the tour overlay failed to load`
|
|
60
|
+
: `${PREFIX$1} start("${tourId}"): no <steppy-tours> is mounted, or the overlay did not load ` +
|
|
61
|
+
`within ${this.readyTimeout()} ms. Place <steppy-tours /> once in the app shell.`);
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
// Outside the zone: the overlay's observers, polls and frames must not
|
|
65
|
+
// drive change detection.
|
|
66
|
+
return this.zone.runOutsideAngular(() => host.start(tourId, options));
|
|
67
|
+
}
|
|
68
|
+
/** End the running tour (``reason`` is passed to ``tour-dismiss``); also cancels a waiting ``start()``. */
|
|
69
|
+
stop(reason) {
|
|
70
|
+
this.seq++;
|
|
71
|
+
this.flush(null);
|
|
72
|
+
const host = this.host;
|
|
73
|
+
if (host)
|
|
74
|
+
this.zone.runOutsideAngular(() => host.stop(reason));
|
|
75
|
+
}
|
|
76
|
+
/** Advance the current step, whatever its kind. */
|
|
77
|
+
next() {
|
|
78
|
+
const host = this.host;
|
|
79
|
+
if (host)
|
|
80
|
+
this.zone.runOutsideAngular(() => host.next());
|
|
81
|
+
}
|
|
82
|
+
// ── Used by <steppy-tours> ──────────────────────────────────────────────
|
|
83
|
+
/** @internal The first component to mount owns the overlay. */
|
|
84
|
+
ɵclaim(owner) {
|
|
85
|
+
if (this.owner !== null && this.owner !== owner)
|
|
86
|
+
return false;
|
|
87
|
+
this.owner = owner;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/** @internal The overlay is defined (``host``) or failed to load (``null``). */
|
|
91
|
+
ɵready(owner, host) {
|
|
92
|
+
if (this.owner !== owner)
|
|
93
|
+
return;
|
|
94
|
+
this.host = host;
|
|
95
|
+
this.failed = host === null;
|
|
96
|
+
this.flush(host);
|
|
97
|
+
}
|
|
98
|
+
/** @internal The owning component was destroyed. */
|
|
99
|
+
ɵrelease(owner) {
|
|
100
|
+
if (this.owner !== owner)
|
|
101
|
+
return;
|
|
102
|
+
this.owner = null;
|
|
103
|
+
this.host = null;
|
|
104
|
+
this.failed = false;
|
|
105
|
+
this._active.set(false);
|
|
106
|
+
this._current.set(null);
|
|
107
|
+
}
|
|
108
|
+
/** @internal One overlay event (already inside the zone). */
|
|
109
|
+
ɵrecord(event) {
|
|
110
|
+
switch (event.type) {
|
|
111
|
+
case 'tour-start':
|
|
112
|
+
this._active.set(true);
|
|
113
|
+
this._current.set(null);
|
|
114
|
+
this._lastError.set(null);
|
|
115
|
+
break;
|
|
116
|
+
case 'tour-step': {
|
|
117
|
+
const { tourId, stepId, index, total } = event.detail;
|
|
118
|
+
this._active.set(true);
|
|
119
|
+
this._current.set({ tourId, stepId, index, total });
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'tour-complete':
|
|
123
|
+
case 'tour-dismiss':
|
|
124
|
+
this._active.set(false);
|
|
125
|
+
this._current.set(null);
|
|
126
|
+
break;
|
|
127
|
+
case 'tour-error':
|
|
128
|
+
this._lastError.set(event.detail);
|
|
129
|
+
if (event.detail.fatal) {
|
|
130
|
+
this._active.set(false);
|
|
131
|
+
this._current.set(null);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case 'tour-offpath':
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
this._events.next(event);
|
|
138
|
+
}
|
|
139
|
+
// ── Internals ──────────────────────────────────────────────────────────────
|
|
140
|
+
readyTimeout() {
|
|
141
|
+
return this.ɵconfig.readyTimeout ?? DEFAULT_READY_TIMEOUT_MS;
|
|
142
|
+
}
|
|
143
|
+
whenReady() {
|
|
144
|
+
if (this.host)
|
|
145
|
+
return Promise.resolve(this.host);
|
|
146
|
+
if (this.failed)
|
|
147
|
+
return Promise.resolve(null);
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
let timer;
|
|
150
|
+
const done = (host) => {
|
|
151
|
+
clearTimeout(timer);
|
|
152
|
+
this.waiters = this.waiters.filter((w) => w !== done);
|
|
153
|
+
resolve(host);
|
|
154
|
+
};
|
|
155
|
+
this.waiters.push(done);
|
|
156
|
+
// Outside the zone: a pending timer would keep a zone.js app from ever
|
|
157
|
+
// becoming stable while it waits.
|
|
158
|
+
timer = this.zone.runOutsideAngular(() => setTimeout(() => done(null), this.readyTimeout()));
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
flush(host) {
|
|
162
|
+
for (const waiter of [...this.waiters])
|
|
163
|
+
waiter(host);
|
|
164
|
+
}
|
|
165
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyTours, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
166
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyTours, providedIn: 'root' });
|
|
167
|
+
}
|
|
168
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyTours, decorators: [{
|
|
169
|
+
type: Injectable,
|
|
170
|
+
args: [{ providedIn: 'root' }]
|
|
171
|
+
}] });
|
|
172
|
+
function resolveLocale(source) {
|
|
173
|
+
if (source === undefined)
|
|
174
|
+
return computed(() => '');
|
|
175
|
+
if (typeof source === 'string')
|
|
176
|
+
return computed(() => source);
|
|
177
|
+
if (isSignal(source))
|
|
178
|
+
return computed(() => source() ?? '');
|
|
179
|
+
const made = source();
|
|
180
|
+
return typeof made === 'string' ? computed(() => made) : computed(() => made() ?? '');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Configure in-app tours for the app, in ``app.config.ts``:
|
|
185
|
+
*
|
|
186
|
+
* ```ts
|
|
187
|
+
* provideSteppyTours({
|
|
188
|
+
* fallbackManifest: 'tours/tours.manifest.json',
|
|
189
|
+
* locale: () => {
|
|
190
|
+
* const translate = inject(TranslateService);
|
|
191
|
+
* return toSignal(translate.onLangChange.pipe(map((e) => e.lang)), { initialValue: translate.getCurrentLang() ?? '' });
|
|
192
|
+
* },
|
|
193
|
+
* })
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* Then place ``<steppy-tours />`` once in the app shell.
|
|
197
|
+
*/
|
|
198
|
+
function provideSteppyTours(config = {}) {
|
|
199
|
+
return makeEnvironmentProviders([
|
|
200
|
+
{ provide: STEPPY_TOURS_CONFIG, useValue: config },
|
|
201
|
+
provideEnvironmentInitializer(() => {
|
|
202
|
+
if (isDevMode() && inject(SteppyTours).ɵconfig !== config) {
|
|
203
|
+
console.warn('[steppy-tours] provideSteppyTours() has no effect here: the tours service is a root ' +
|
|
204
|
+
'singleton. Call it in the application providers (app.config.ts), not in a lazy route.');
|
|
205
|
+
}
|
|
206
|
+
}),
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The ``navigate`` hook the overlay calls to reach a tour's entry route (or a
|
|
212
|
+
* route linked from step text). Call it in an injection context.
|
|
213
|
+
*
|
|
214
|
+
* The overlay calls it from outside the Angular zone; the router — and the
|
|
215
|
+
* routed view it renders — must run inside it, or a zone.js app never renders
|
|
216
|
+
* the page the tour waits on. Returns ``null`` for ``'document'``, and when the
|
|
217
|
+
* app has no routes, so the overlay falls back to a full page load.
|
|
218
|
+
*/
|
|
219
|
+
function createNavigate(option = 'router') {
|
|
220
|
+
if (option === 'document')
|
|
221
|
+
return null;
|
|
222
|
+
const zone = inject(NgZone);
|
|
223
|
+
let go;
|
|
224
|
+
if (option === 'router') {
|
|
225
|
+
if (!inject(ROUTES, { optional: true }))
|
|
226
|
+
return null;
|
|
227
|
+
go = routerNavigate(inject(Router));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
go = option;
|
|
231
|
+
}
|
|
232
|
+
// ``async``: a synchronous throw becomes a rejection, which the overlay reports.
|
|
233
|
+
return (path, ctx) => zone.run(async () => void (await go(path, ctx)));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Navigate with the router and settle only once the router is where the tour
|
|
237
|
+
* wants to be. ``navigateByUrl`` resolves ``false`` both for a refused
|
|
238
|
+
* navigation and for one to the current URL, and ``true`` for one a guard
|
|
239
|
+
* redirected elsewhere — so the outcome is read from ``router.url``. A
|
|
240
|
+
* rejection makes the overlay fail the start at once (``entry-params``)
|
|
241
|
+
* instead of waiting for a target that will never render.
|
|
242
|
+
*/
|
|
243
|
+
function routerNavigate(router) {
|
|
244
|
+
const pathOf = (url) => router.serializeUrl(router.parseUrl(url)).split(/[?#]/, 1)[0];
|
|
245
|
+
return async (_path, { appPath }) => {
|
|
246
|
+
const navigated = await router.navigateByUrl(appPath);
|
|
247
|
+
if (pathOf(router.url) === pathOf(appPath))
|
|
248
|
+
return;
|
|
249
|
+
throw new Error(navigated
|
|
250
|
+
? `the router redirected ${appPath} to ${router.url}`
|
|
251
|
+
: `the router did not navigate to ${appPath} (a guard or another navigation cancelled it)`);
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The seam between the adapter and the tour overlay (``@steppy/player/tour``).
|
|
257
|
+
*
|
|
258
|
+
* Everything imported from the overlay at the top of this package is a *type*:
|
|
259
|
+
* the overlay touches ``HTMLElement`` when its module loads, so its only
|
|
260
|
+
* runtime reference is the dynamic ``import()`` in ``OVERLAY_LOADER``, which
|
|
261
|
+
* runs in the browser, after the first render. Tests swap the loader for a fake
|
|
262
|
+
* element on a tag of their own.
|
|
263
|
+
*
|
|
264
|
+
* One literal per line: ``tests/test_angular_adapter_sync.py`` reads the tag and
|
|
265
|
+
* the event names with regular expressions and compares them with the overlay.
|
|
266
|
+
*/
|
|
267
|
+
const TOUR_HOST_TAG = 'steppy-tour-host';
|
|
268
|
+
const TOUR_EVENT_NAMES = ['tour-start', 'tour-step', 'tour-complete', 'tour-dismiss', 'tour-offpath', 'tour-error'];
|
|
269
|
+
const OVERLAY_LOADER = new InjectionToken('steppy tour overlay', {
|
|
270
|
+
providedIn: 'root',
|
|
271
|
+
factory: () => ({ tag: TOUR_HOST_TAG, load: () => import('@steppy/player/tour') }),
|
|
272
|
+
});
|
|
273
|
+
/**
|
|
274
|
+
* Write the props onto the element — before it is defined as well: the
|
|
275
|
+
* overlay keeps properties set on the element before its upgrade.
|
|
276
|
+
*/
|
|
277
|
+
function applyProps(el, p) {
|
|
278
|
+
el.fallbackManifest = p.fallbackManifest;
|
|
279
|
+
el.manifest = p.manifest;
|
|
280
|
+
el.buildId = p.buildId;
|
|
281
|
+
el.guideOrigin = p.guideOrigin;
|
|
282
|
+
el.locale = p.locale;
|
|
283
|
+
el.disabled = p.disabled;
|
|
284
|
+
el.strings = p.strings;
|
|
285
|
+
el.source = p.source;
|
|
286
|
+
if (p.targetTimeout !== undefined)
|
|
287
|
+
el.targetTimeout = p.targetTimeout;
|
|
288
|
+
if (p.baseZIndex !== undefined)
|
|
289
|
+
el.baseZIndex = p.baseZIndex;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const PREFIX = '[steppy-tours]';
|
|
293
|
+
/** ``undefined`` stays ``undefined`` (= use the config); anything else is read as a number. */
|
|
294
|
+
function optionalNumber(value) {
|
|
295
|
+
return value === undefined || value === null || value === '' ? undefined : Number(value);
|
|
296
|
+
}
|
|
297
|
+
/** ``undefined`` stays ``undefined``; a bare attribute (``''``) and ``'true'`` are true. */
|
|
298
|
+
function optionalBoolean(value) {
|
|
299
|
+
if (value === undefined || value === null)
|
|
300
|
+
return undefined;
|
|
301
|
+
return value === '' || (value !== false && value !== 'false');
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* The in-app tour overlay. Place it **once**, in the app shell:
|
|
305
|
+
*
|
|
306
|
+
* ```html
|
|
307
|
+
* <router-outlet />
|
|
308
|
+
* <steppy-tours />
|
|
309
|
+
* ```
|
|
310
|
+
*
|
|
311
|
+
* It renders nothing on the server. In the browser it mounts
|
|
312
|
+
* ``<steppy-tour-host>`` after the first render and loads the overlay
|
|
313
|
+
* module lazily; every input overrides the matching
|
|
314
|
+
* ``provideSteppyTours()`` setting. Start tours with the
|
|
315
|
+
* ``SteppyTours`` service or the ``steppyTourStart`` directive.
|
|
316
|
+
*
|
|
317
|
+
* Destroying it suspends a running tour rather than dismissing it: the overlay
|
|
318
|
+
* keeps its resume record, so a tour survives a shell swap. Call
|
|
319
|
+
* ``SteppyTours.stop()`` first to end it for good.
|
|
320
|
+
*/
|
|
321
|
+
class SteppyToursComponent {
|
|
322
|
+
fallbackManifest = input(...(ngDevMode ? [undefined, { debugName: "fallbackManifest" }] : /* istanbul ignore next */ []));
|
|
323
|
+
manifest = input(...(ngDevMode ? [undefined, { debugName: "manifest" }] : /* istanbul ignore next */ []));
|
|
324
|
+
buildId = input(...(ngDevMode ? [undefined, { debugName: "buildId" }] : /* istanbul ignore next */ []));
|
|
325
|
+
guideOrigin = input(...(ngDevMode ? [undefined, { debugName: "guideOrigin" }] : /* istanbul ignore next */ []));
|
|
326
|
+
/** ``null``/``undefined``: the configured locale source. ``''``: follow ``<html lang>``. */
|
|
327
|
+
locale = input(...(ngDevMode ? [undefined, { debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
328
|
+
targetTimeout = input(undefined, { ...(ngDevMode ? { debugName: "targetTimeout" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
329
|
+
baseZIndex = input(undefined, { ...(ngDevMode ? { debugName: "baseZIndex" } : /* istanbul ignore next */ {}), transform: optionalNumber });
|
|
330
|
+
disabled = input(undefined, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: optionalBoolean });
|
|
331
|
+
strings = input(...(ngDevMode ? [undefined, { debugName: "strings" }] : /* istanbul ignore next */ []));
|
|
332
|
+
source = input(...(ngDevMode ? [undefined, { debugName: "source" }] : /* istanbul ignore next */ []));
|
|
333
|
+
tourStart = output();
|
|
334
|
+
tourStep = output();
|
|
335
|
+
tourComplete = output();
|
|
336
|
+
tourDismiss = output();
|
|
337
|
+
tourOffpath = output();
|
|
338
|
+
tourError = output();
|
|
339
|
+
tours = inject(SteppyTours);
|
|
340
|
+
config = this.tours.ɵconfig;
|
|
341
|
+
overlay = inject(OVERLAY_LOADER);
|
|
342
|
+
zone = inject(NgZone);
|
|
343
|
+
document = inject(DOCUMENT);
|
|
344
|
+
hostRef = inject(ElementRef);
|
|
345
|
+
browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
346
|
+
navigate = createNavigate(this.config.navigate);
|
|
347
|
+
el = signal(null, ...(ngDevMode ? [{ debugName: "el" }] : /* istanbul ignore next */ []));
|
|
348
|
+
outputs = {
|
|
349
|
+
'tour-start': this.tourStart,
|
|
350
|
+
'tour-step': this.tourStep,
|
|
351
|
+
'tour-complete': this.tourComplete,
|
|
352
|
+
'tour-dismiss': this.tourDismiss,
|
|
353
|
+
'tour-offpath': this.tourOffpath,
|
|
354
|
+
'tour-error': this.tourError,
|
|
355
|
+
};
|
|
356
|
+
props = computed(() => {
|
|
357
|
+
const c = this.config;
|
|
358
|
+
const locale = this.locale();
|
|
359
|
+
const source = this.source();
|
|
360
|
+
return {
|
|
361
|
+
fallbackManifest: this.fallbackManifest() ?? c.fallbackManifest ?? '',
|
|
362
|
+
manifest: this.manifest() ?? c.manifest ?? '',
|
|
363
|
+
buildId: this.buildId() ?? c.buildId ?? '',
|
|
364
|
+
guideOrigin: this.guideOrigin() ?? c.guideOrigin ?? '',
|
|
365
|
+
locale: locale ?? this.tours.ɵlocale(),
|
|
366
|
+
disabled: this.disabled() ?? c.disabled ?? false,
|
|
367
|
+
strings: this.strings() ?? c.strings,
|
|
368
|
+
source: source !== undefined ? source : (c.source ?? null),
|
|
369
|
+
targetTimeout: this.targetTimeout() ?? c.targetTimeout,
|
|
370
|
+
baseZIndex: this.baseZIndex() ?? c.baseZIndex,
|
|
371
|
+
};
|
|
372
|
+
}, ...(ngDevMode ? [{ debugName: "props" }] : /* istanbul ignore next */ []));
|
|
373
|
+
onEvent = (event) => this.zone.run(() => {
|
|
374
|
+
const record = { type: event.type, detail: event.detail };
|
|
375
|
+
this.tours.ɵrecord(record);
|
|
376
|
+
this.outputs[record.type].emit(record.detail);
|
|
377
|
+
});
|
|
378
|
+
constructor() {
|
|
379
|
+
// afterNextRender runs in the browser only, outside the Angular zone.
|
|
380
|
+
afterNextRender(() => this.mount());
|
|
381
|
+
effect(() => {
|
|
382
|
+
const el = this.el();
|
|
383
|
+
const props = this.props();
|
|
384
|
+
if (el)
|
|
385
|
+
this.zone.runOutsideAngular(() => applyProps(el, props));
|
|
386
|
+
});
|
|
387
|
+
inject(DestroyRef).onDestroy(() => this.unmount());
|
|
388
|
+
}
|
|
389
|
+
mount() {
|
|
390
|
+
if (!this.browser)
|
|
391
|
+
return;
|
|
392
|
+
if (!this.tours.ɵclaim(this)) {
|
|
393
|
+
console.warn(`${PREFIX} another <steppy-tours> is already mounted; this one does nothing. Mount it once, in the app shell.`);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const props = untracked(this.props);
|
|
397
|
+
if (!props.fallbackManifest && !props.manifest) {
|
|
398
|
+
console.warn(`${PREFIX} no manifest: set fallbackManifest (or manifest) in provideSteppyTours() or on <steppy-tours>`);
|
|
399
|
+
}
|
|
400
|
+
// The element is on the page — with its properties and listeners — before
|
|
401
|
+
// the overlay module defines it: the overlay keeps properties set before
|
|
402
|
+
// its upgrade, a resumed tour's tour-start is not missed, and anything that
|
|
403
|
+
// waits for the element to be defined finds it in the DOM.
|
|
404
|
+
const el = this.document.createElement(this.overlay.tag);
|
|
405
|
+
applyProps(el, props);
|
|
406
|
+
el.navigate = this.navigate;
|
|
407
|
+
for (const name of TOUR_EVENT_NAMES)
|
|
408
|
+
el.addEventListener(name, this.onEvent);
|
|
409
|
+
this.hostRef.nativeElement.appendChild(el);
|
|
410
|
+
this.el.set(el);
|
|
411
|
+
this.overlay
|
|
412
|
+
.load()
|
|
413
|
+
.then(() => customElements.whenDefined(this.overlay.tag))
|
|
414
|
+
.then(() => {
|
|
415
|
+
if (this.el() === el)
|
|
416
|
+
this.tours.ɵready(this, el);
|
|
417
|
+
}, (err) => {
|
|
418
|
+
if (this.el() !== el)
|
|
419
|
+
return;
|
|
420
|
+
console.error(`${PREFIX} the tour overlay failed to load`, err);
|
|
421
|
+
this.tours.ɵready(this, null);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
unmount() {
|
|
425
|
+
const el = untracked(this.el);
|
|
426
|
+
if (el) {
|
|
427
|
+
for (const name of TOUR_EVENT_NAMES)
|
|
428
|
+
el.removeEventListener(name, this.onEvent);
|
|
429
|
+
el.remove();
|
|
430
|
+
this.el.set(null);
|
|
431
|
+
}
|
|
432
|
+
this.tours.ɵrelease(this);
|
|
433
|
+
}
|
|
434
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyToursComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
435
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.25", type: SteppyToursComponent, isStandalone: true, selector: "steppy-tours", inputs: { fallbackManifest: { classPropertyName: "fallbackManifest", publicName: "fallbackManifest", isSignal: true, isRequired: false, transformFunction: null }, manifest: { classPropertyName: "manifest", publicName: "manifest", isSignal: true, isRequired: false, transformFunction: null }, buildId: { classPropertyName: "buildId", publicName: "buildId", isSignal: true, isRequired: false, transformFunction: null }, guideOrigin: { classPropertyName: "guideOrigin", publicName: "guideOrigin", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, targetTimeout: { classPropertyName: "targetTimeout", publicName: "targetTimeout", isSignal: true, isRequired: false, transformFunction: null }, baseZIndex: { classPropertyName: "baseZIndex", publicName: "baseZIndex", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, strings: { classPropertyName: "strings", publicName: "strings", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tourStart: "tourStart", tourStep: "tourStep", tourComplete: "tourComplete", tourDismiss: "tourDismiss", tourOffpath: "tourOffpath", tourError: "tourError" }, host: { styleAttribute: "display: contents" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
436
|
+
}
|
|
437
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyToursComponent, decorators: [{
|
|
438
|
+
type: Component,
|
|
439
|
+
args: [{
|
|
440
|
+
selector: 'steppy-tours',
|
|
441
|
+
template: '',
|
|
442
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
443
|
+
host: { style: 'display: contents' },
|
|
444
|
+
}]
|
|
445
|
+
}], ctorParameters: () => [], propDecorators: { fallbackManifest: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackManifest", required: false }] }], manifest: [{ type: i0.Input, args: [{ isSignal: true, alias: "manifest", required: false }] }], buildId: [{ type: i0.Input, args: [{ isSignal: true, alias: "buildId", required: false }] }], guideOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "guideOrigin", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], targetTimeout: [{ type: i0.Input, args: [{ isSignal: true, alias: "targetTimeout", required: false }] }], baseZIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "baseZIndex", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], strings: [{ type: i0.Input, args: [{ isSignal: true, alias: "strings", required: false }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], tourStart: [{ type: i0.Output, args: ["tourStart"] }], tourStep: [{ type: i0.Output, args: ["tourStep"] }], tourComplete: [{ type: i0.Output, args: ["tourComplete"] }], tourDismiss: [{ type: i0.Output, args: ["tourDismiss"] }], tourOffpath: [{ type: i0.Output, args: ["tourOffpath"] }], tourError: [{ type: i0.Output, args: ["tourError"] }] } });
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Start a tour on click:
|
|
449
|
+
*
|
|
450
|
+
* ```html
|
|
451
|
+
* <button steppyTourStart="create-invoice">Show me how</button>
|
|
452
|
+
* <button [steppyTourStart]="'send-invoice'" [steppyTourParams]="{ invoiceId: id }">Help</button>
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
class SteppyTourStart {
|
|
456
|
+
steppyTourStart = input.required(...(ngDevMode ? [{ debugName: "steppyTourStart" }] : /* istanbul ignore next */ []));
|
|
457
|
+
steppyTourParams = input(...(ngDevMode ? [undefined, { debugName: "steppyTourParams" }] : /* istanbul ignore next */ []));
|
|
458
|
+
steppyTourStep = input(...(ngDevMode ? [undefined, { debugName: "steppyTourStep" }] : /* istanbul ignore next */ []));
|
|
459
|
+
tours = inject(SteppyTours);
|
|
460
|
+
onClick() {
|
|
461
|
+
const options = {};
|
|
462
|
+
const params = this.steppyTourParams();
|
|
463
|
+
const step = this.steppyTourStep();
|
|
464
|
+
if (params)
|
|
465
|
+
options.params = params;
|
|
466
|
+
if (step)
|
|
467
|
+
options.step = step;
|
|
468
|
+
void this.tours.start(this.steppyTourStart(), options);
|
|
469
|
+
}
|
|
470
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyTourStart, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
471
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.25", type: SteppyTourStart, isStandalone: true, selector: "[steppyTourStart]", inputs: { steppyTourStart: { classPropertyName: "steppyTourStart", publicName: "steppyTourStart", isSignal: true, isRequired: true, transformFunction: null }, steppyTourParams: { classPropertyName: "steppyTourParams", publicName: "steppyTourParams", isSignal: true, isRequired: false, transformFunction: null }, steppyTourStep: { classPropertyName: "steppyTourStep", publicName: "steppyTourStep", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()" } }, ngImport: i0 });
|
|
472
|
+
}
|
|
473
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SteppyTourStart, decorators: [{
|
|
474
|
+
type: Directive,
|
|
475
|
+
args: [{
|
|
476
|
+
selector: '[steppyTourStart]',
|
|
477
|
+
host: { '(click)': 'onClick()' },
|
|
478
|
+
}]
|
|
479
|
+
}], propDecorators: { steppyTourStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "steppyTourStart", required: true }] }], steppyTourParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "steppyTourParams", required: false }] }], steppyTourStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "steppyTourStep", required: false }] }] } });
|
|
480
|
+
|
|
481
|
+
/*
|
|
482
|
+
* @steppy/angular — the Angular adapter for the Steppy in-app tour overlay.
|
|
483
|
+
*/
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Generated bundle index. Do not edit.
|
|
487
|
+
*/
|
|
488
|
+
|
|
489
|
+
export { SteppyTourStart, SteppyTours, SteppyToursComponent, provideSteppyTours, routerNavigate };
|
|
490
|
+
//# sourceMappingURL=steppy-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steppy-angular.mjs","sources":["../../src/config.ts","../../src/tours.service.ts","../../src/provide.ts","../../src/navigate.ts","../../src/overlay.ts","../../src/tours.component.ts","../../src/tour-start.directive.ts","../../src/public-api.ts","../../src/steppy-angular.ts"],"sourcesContent":["import { InjectionToken, type Signal } from '@angular/core';\nimport type { TourNavigate, TourSource, TourStringOverrides } from '@steppy/player/tour';\n\n/**\n * Where the tours' language comes from. A string or a signal is used as is; a\n * function is called once, in the root injection context, so it may\n * ``inject()`` the app's i18n service and return a signal of its language.\n * ``null``, ``undefined`` and ``''`` all mean \"follow ``<html lang>``\".\n */\nexport type SteppyLocaleSource =\n | string\n | Signal<string | null | undefined>\n | (() => string | Signal<string | null | undefined>);\n\n/** How a tour changes route; ``(path, {appPath, tourId}) => …`` for your own. */\nexport type SteppyNavigate = TourNavigate;\n\nexport interface SteppyToursConfig {\n /** The tour manifest bundled with the app (same origin), e.g. ``tours/tours.manifest.json``. */\n fallbackManifest?: string;\n /** A remote manifest URL; may contain ``{buildId}``. */\n manifest?: string;\n /** The app's build id, for ``{buildId}``. Default: the bundled manifest's. */\n buildId?: string;\n /** The origin trusted for guide links. Default: the bundled manifest's. */\n guideOrigin?: string;\n /** Milliseconds to wait for a step's element (500–120000). Default: the overlay's 8000. */\n targetTimeout?: number;\n /** Stacking where the top layer is unavailable. Default: the overlay's. */\n baseZIndex?: number;\n /** Turns tours off; stops a running one. */\n disabled?: boolean;\n /** Chrome string overrides per locale (buttons, hints). */\n strings?: TourStringOverrides;\n /** Supply tour files yourself; see the overlay's ``source`` property. */\n source?: TourSource | null;\n /** The tours' language. Default: ``<html lang>``. */\n locale?: SteppyLocaleSource;\n /**\n * ``'router'`` (default): the Angular router, inside the zone; a refused or\n * redirected navigation fails the tour at once. ``'document'``: a full page\n * load. Or a function of your own.\n */\n navigate?: 'router' | 'document' | SteppyNavigate;\n /** How long ``start()`` waits for ``<steppy-tours>`` and the overlay to load. Default 10000 ms. */\n readyTimeout?: number;\n}\n\nexport const STEPPY_TOURS_CONFIG = new InjectionToken<SteppyToursConfig>('STEPPY_TOURS_CONFIG');\n\nexport const DEFAULT_READY_TIMEOUT_MS = 10_000;\n","import { Injectable, NgZone, PLATFORM_ID, computed, inject, isSignal, signal, type Signal } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { Observable, Subject } from 'rxjs';\nimport type { TourErrorDetail, TourEventDetailMap, TourPosition, TourStartOptions } from '@steppy/player/tour';\n\nimport { DEFAULT_READY_TIMEOUT_MS, STEPPY_TOURS_CONFIG, type SteppyLocaleSource, type SteppyToursConfig } from './config';\nimport type { TourHostElement } from './overlay';\n\n/** One overlay event, as the service and the component outputs see it. */\nexport type SteppyTourEvent = {\n [K in keyof TourEventDetailMap]: { type: K; detail: TourEventDetailMap[K] };\n}[keyof TourEventDetailMap];\n\nconst PREFIX = '[steppy-tours]';\n\n/**\n * Start and follow in-app tours from anywhere in the app.\n *\n * The overlay itself lives in ``<steppy-tours>``, placed once in the app\n * shell; ``start()`` waits for it to be mounted and loaded (up to\n * ``readyTimeout``), so a help button can call it during startup. State is\n * built from the overlay's six events and exposed as signals; on the server\n * every call is a no-op.\n */\n@Injectable({ providedIn: 'root' })\nexport class SteppyTours {\n private readonly zone = inject(NgZone);\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /** @internal */\n readonly ɵconfig: SteppyToursConfig = inject(STEPPY_TOURS_CONFIG, { optional: true }) ?? {};\n /** @internal The configured locale source, resolved once in the root injection context. */\n readonly ɵlocale: Signal<string> = resolveLocale(this.ɵconfig.locale);\n\n private readonly _active = signal(false);\n private readonly _current = signal<TourPosition | null>(null);\n private readonly _lastError = signal<TourErrorDetail | null>(null);\n private readonly _events = new Subject<SteppyTourEvent>();\n\n /** A tour is running (from ``tour-start`` until it completes, is dismissed or fails). */\n readonly active: Signal<boolean> = this._active.asReadonly();\n /** The step shown, or ``null`` before the first step and after the end. */\n readonly current: Signal<TourPosition | null> = this._current.asReadonly();\n /** The last ``tour-error`` since the last start (fatal or not). */\n readonly lastError: Signal<TourErrorDetail | null> = this._lastError.asReadonly();\n /** Every overlay event, in order, delivered inside the Angular zone. */\n readonly events: Observable<SteppyTourEvent> = this._events.asObservable();\n\n private owner: object | null = null;\n private host: TourHostElement | null = null;\n private failed = false;\n private waiters: ((host: TourHostElement | null) => void)[] = [];\n private seq = 0;\n\n /**\n * Start a tour. Resolves ``true`` once it started, ``false`` when it could\n * not (a ``tour-error`` says why), when no ``<steppy-tours>`` is mounted\n * in time, when a later ``start()``/``stop()`` superseded this call, or on\n * the server. Never rejects.\n */\n async start(tourId: string, options: TourStartOptions = {}): Promise<boolean> {\n if (!this.browser) return false;\n const seq = ++this.seq;\n const host = await this.whenReady();\n if (seq !== this.seq) return false;\n if (!host) {\n console.warn(\n this.failed\n ? `${PREFIX} start(\"${tourId}\"): the tour overlay failed to load`\n : `${PREFIX} start(\"${tourId}\"): no <steppy-tours> is mounted, or the overlay did not load ` +\n `within ${this.readyTimeout()} ms. Place <steppy-tours /> once in the app shell.`,\n );\n return false;\n }\n // Outside the zone: the overlay's observers, polls and frames must not\n // drive change detection.\n return this.zone.runOutsideAngular(() => host.start(tourId, options));\n }\n\n /** End the running tour (``reason`` is passed to ``tour-dismiss``); also cancels a waiting ``start()``. */\n stop(reason?: string): void {\n this.seq++;\n this.flush(null);\n const host = this.host;\n if (host) this.zone.runOutsideAngular(() => host.stop(reason));\n }\n\n /** Advance the current step, whatever its kind. */\n next(): void {\n const host = this.host;\n if (host) this.zone.runOutsideAngular(() => host.next());\n }\n\n // ── Used by <steppy-tours> ──────────────────────────────────────────────\n\n /** @internal The first component to mount owns the overlay. */\n ɵclaim(owner: object): boolean {\n if (this.owner !== null && this.owner !== owner) return false;\n this.owner = owner;\n return true;\n }\n\n /** @internal The overlay is defined (``host``) or failed to load (``null``). */\n ɵready(owner: object, host: TourHostElement | null): void {\n if (this.owner !== owner) return;\n this.host = host;\n this.failed = host === null;\n this.flush(host);\n }\n\n /** @internal The owning component was destroyed. */\n ɵrelease(owner: object): void {\n if (this.owner !== owner) return;\n this.owner = null;\n this.host = null;\n this.failed = false;\n this._active.set(false);\n this._current.set(null);\n }\n\n /** @internal One overlay event (already inside the zone). */\n ɵrecord(event: SteppyTourEvent): void {\n switch (event.type) {\n case 'tour-start':\n this._active.set(true);\n this._current.set(null);\n this._lastError.set(null);\n break;\n case 'tour-step': {\n const { tourId, stepId, index, total } = event.detail;\n this._active.set(true);\n this._current.set({ tourId, stepId, index, total });\n break;\n }\n case 'tour-complete':\n case 'tour-dismiss':\n this._active.set(false);\n this._current.set(null);\n break;\n case 'tour-error':\n this._lastError.set(event.detail);\n if (event.detail.fatal) {\n this._active.set(false);\n this._current.set(null);\n }\n break;\n case 'tour-offpath':\n break;\n }\n this._events.next(event);\n }\n\n // ── Internals ──────────────────────────────────────────────────────────────\n\n private readyTimeout(): number {\n return this.ɵconfig.readyTimeout ?? DEFAULT_READY_TIMEOUT_MS;\n }\n\n private whenReady(): Promise<TourHostElement | null> {\n if (this.host) return Promise.resolve(this.host);\n if (this.failed) return Promise.resolve(null);\n return new Promise((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const done = (host: TourHostElement | null) => {\n clearTimeout(timer);\n this.waiters = this.waiters.filter((w) => w !== done);\n resolve(host);\n };\n this.waiters.push(done);\n // Outside the zone: a pending timer would keep a zone.js app from ever\n // becoming stable while it waits.\n timer = this.zone.runOutsideAngular(() => setTimeout(() => done(null), this.readyTimeout()));\n });\n }\n\n private flush(host: TourHostElement | null): void {\n for (const waiter of [...this.waiters]) waiter(host);\n }\n}\n\nfunction resolveLocale(source: SteppyLocaleSource | undefined): Signal<string> {\n if (source === undefined) return computed(() => '');\n if (typeof source === 'string') return computed(() => source);\n if (isSignal(source)) return computed(() => source() ?? '');\n const made = source();\n return typeof made === 'string' ? computed(() => made) : computed(() => made() ?? '');\n}\n","import { inject, isDevMode, makeEnvironmentProviders, provideEnvironmentInitializer, type EnvironmentProviders } from '@angular/core';\n\nimport { STEPPY_TOURS_CONFIG, type SteppyToursConfig } from './config';\nimport { SteppyTours } from './tours.service';\n\n/**\n * Configure in-app tours for the app, in ``app.config.ts``:\n *\n * ```ts\n * provideSteppyTours({\n * fallbackManifest: 'tours/tours.manifest.json',\n * locale: () => {\n * const translate = inject(TranslateService);\n * return toSignal(translate.onLangChange.pipe(map((e) => e.lang)), { initialValue: translate.getCurrentLang() ?? '' });\n * },\n * })\n * ```\n *\n * Then place ``<steppy-tours />`` once in the app shell.\n */\nexport function provideSteppyTours(config: SteppyToursConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: STEPPY_TOURS_CONFIG, useValue: config },\n provideEnvironmentInitializer(() => {\n if (isDevMode() && inject(SteppyTours).ɵconfig !== config) {\n console.warn(\n '[steppy-tours] provideSteppyTours() has no effect here: the tours service is a root ' +\n 'singleton. Call it in the application providers (app.config.ts), not in a lazy route.',\n );\n }\n }),\n ]);\n}\n","import { inject, NgZone } from '@angular/core';\nimport { Router, ROUTES } from '@angular/router';\nimport type { TourNavigate } from '@steppy/player/tour';\n\nimport type { SteppyToursConfig } from './config';\n\n/**\n * The ``navigate`` hook the overlay calls to reach a tour's entry route (or a\n * route linked from step text). Call it in an injection context.\n *\n * The overlay calls it from outside the Angular zone; the router — and the\n * routed view it renders — must run inside it, or a zone.js app never renders\n * the page the tour waits on. Returns ``null`` for ``'document'``, and when the\n * app has no routes, so the overlay falls back to a full page load.\n */\nexport function createNavigate(option: SteppyToursConfig['navigate'] = 'router'): TourNavigate | null {\n if (option === 'document') return null;\n const zone = inject(NgZone);\n let go: TourNavigate;\n if (option === 'router') {\n if (!inject(ROUTES, { optional: true })) return null;\n go = routerNavigate(inject(Router));\n } else {\n go = option;\n }\n // ``async``: a synchronous throw becomes a rejection, which the overlay reports.\n return (path, ctx) => zone.run(async () => void (await go(path, ctx)));\n}\n\n/**\n * Navigate with the router and settle only once the router is where the tour\n * wants to be. ``navigateByUrl`` resolves ``false`` both for a refused\n * navigation and for one to the current URL, and ``true`` for one a guard\n * redirected elsewhere — so the outcome is read from ``router.url``. A\n * rejection makes the overlay fail the start at once (``entry-params``)\n * instead of waiting for a target that will never render.\n */\nexport function routerNavigate(router: Router): TourNavigate {\n const pathOf = (url: string) => router.serializeUrl(router.parseUrl(url)).split(/[?#]/, 1)[0];\n return async (_path, { appPath }) => {\n const navigated = await router.navigateByUrl(appPath);\n if (pathOf(router.url) === pathOf(appPath)) return;\n throw new Error(\n navigated\n ? `the router redirected ${appPath} to ${router.url}`\n : `the router did not navigate to ${appPath} (a guard or another navigation cancelled it)`,\n );\n };\n}\n","/**\n * The seam between the adapter and the tour overlay (``@steppy/player/tour``).\n *\n * Everything imported from the overlay at the top of this package is a *type*:\n * the overlay touches ``HTMLElement`` when its module loads, so its only\n * runtime reference is the dynamic ``import()`` in ``OVERLAY_LOADER``, which\n * runs in the browser, after the first render. Tests swap the loader for a fake\n * element on a tag of their own.\n *\n * One literal per line: ``tests/test_angular_adapter_sync.py`` reads the tag and\n * the event names with regular expressions and compares them with the overlay.\n */\n\nimport { InjectionToken } from '@angular/core';\nimport type { TourEventName, TourHostApi, TourSource, TourStringOverrides } from '@steppy/player/tour';\n\nexport const TOUR_HOST_TAG = 'steppy-tour-host';\nexport const TOUR_EVENT_NAMES = ['tour-start', 'tour-step', 'tour-complete', 'tour-dismiss', 'tour-offpath', 'tour-error'] as const;\n\n// Fails to compile when the overlay gains an event this list does not name.\ntype AssertNever<T extends never> = T;\nexport type ɵAllTourEventsListed = AssertNever<Exclude<TourEventName, (typeof TOUR_EVENT_NAMES)[number]>>;\n\n/** ``<steppy-tour-host>`` as the adapter drives it. */\nexport type TourHostElement = HTMLElement & TourHostApi;\n\nexport interface OverlayLoader {\n /** The custom element tag the loader defines. */\n readonly tag: string;\n /** Load (and so define) the element. */\n load(): Promise<unknown>;\n}\n\nexport const OVERLAY_LOADER = new InjectionToken<OverlayLoader>('steppy tour overlay', {\n providedIn: 'root',\n factory: () => ({ tag: TOUR_HOST_TAG, load: () => import('@steppy/player/tour') }),\n});\n\n/** What the adapter writes onto the element (``navigate`` is set once, at mount). */\nexport interface HostProps {\n fallbackManifest: string;\n manifest: string;\n buildId: string;\n guideOrigin: string;\n locale: string;\n disabled: boolean;\n strings: TourStringOverrides | undefined;\n source: TourSource | null;\n targetTimeout?: number;\n baseZIndex?: number;\n}\n\n/**\n * Write the props onto the element — before it is defined as well: the\n * overlay keeps properties set on the element before its upgrade.\n */\nexport function applyProps(el: TourHostElement, p: HostProps): void {\n el.fallbackManifest = p.fallbackManifest;\n el.manifest = p.manifest;\n el.buildId = p.buildId;\n el.guideOrigin = p.guideOrigin;\n el.locale = p.locale;\n el.disabled = p.disabled;\n el.strings = p.strings;\n el.source = p.source;\n if (p.targetTimeout !== undefined) el.targetTimeout = p.targetTimeout;\n if (p.baseZIndex !== undefined) el.baseZIndex = p.baseZIndex;\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DOCUMENT,\n DestroyRef,\n ElementRef,\n NgZone,\n PLATFORM_ID,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n untracked,\n type OutputEmitterRef,\n} from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport type { TourEventDetailMap, TourSource, TourStringOverrides } from '@steppy/player/tour';\n\nimport { createNavigate } from './navigate';\nimport { OVERLAY_LOADER, TOUR_EVENT_NAMES, applyProps, type HostProps, type TourHostElement } from './overlay';\nimport { SteppyTours, type SteppyTourEvent } from './tours.service';\n\nconst PREFIX = '[steppy-tours]';\n\n/** ``undefined`` stays ``undefined`` (= use the config); anything else is read as a number. */\nfunction optionalNumber(value: unknown): number | undefined {\n return value === undefined || value === null || value === '' ? undefined : Number(value);\n}\n\n/** ``undefined`` stays ``undefined``; a bare attribute (``''``) and ``'true'`` are true. */\nfunction optionalBoolean(value: unknown): boolean | undefined {\n if (value === undefined || value === null) return undefined;\n return value === '' || (value !== false && value !== 'false');\n}\n\n/**\n * The in-app tour overlay. Place it **once**, in the app shell:\n *\n * ```html\n * <router-outlet />\n * <steppy-tours />\n * ```\n *\n * It renders nothing on the server. In the browser it mounts\n * ``<steppy-tour-host>`` after the first render and loads the overlay\n * module lazily; every input overrides the matching\n * ``provideSteppyTours()`` setting. Start tours with the\n * ``SteppyTours`` service or the ``steppyTourStart`` directive.\n *\n * Destroying it suspends a running tour rather than dismissing it: the overlay\n * keeps its resume record, so a tour survives a shell swap. Call\n * ``SteppyTours.stop()`` first to end it for good.\n */\n@Component({\n selector: 'steppy-tours',\n template: '',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { style: 'display: contents' },\n})\nexport class SteppyToursComponent {\n readonly fallbackManifest = input<string>();\n readonly manifest = input<string>();\n readonly buildId = input<string>();\n readonly guideOrigin = input<string>();\n /** ``null``/``undefined``: the configured locale source. ``''``: follow ``<html lang>``. */\n readonly locale = input<string | null>();\n readonly targetTimeout = input<number | undefined, unknown>(undefined, { transform: optionalNumber });\n readonly baseZIndex = input<number | undefined, unknown>(undefined, { transform: optionalNumber });\n readonly disabled = input<boolean | undefined, unknown>(undefined, { transform: optionalBoolean });\n readonly strings = input<TourStringOverrides>();\n readonly source = input<TourSource | null>();\n\n readonly tourStart = output<TourEventDetailMap['tour-start']>();\n readonly tourStep = output<TourEventDetailMap['tour-step']>();\n readonly tourComplete = output<TourEventDetailMap['tour-complete']>();\n readonly tourDismiss = output<TourEventDetailMap['tour-dismiss']>();\n readonly tourOffpath = output<TourEventDetailMap['tour-offpath']>();\n readonly tourError = output<TourEventDetailMap['tour-error']>();\n\n private readonly tours = inject(SteppyTours);\n private readonly config = this.tours.ɵconfig;\n private readonly overlay = inject(OVERLAY_LOADER);\n private readonly zone = inject(NgZone);\n private readonly document = inject(DOCUMENT);\n private readonly hostRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly navigate = createNavigate(this.config.navigate);\n private readonly el = signal<TourHostElement | null>(null);\n\n private readonly outputs: { [K in keyof TourEventDetailMap]: OutputEmitterRef<TourEventDetailMap[K]> } = {\n 'tour-start': this.tourStart,\n 'tour-step': this.tourStep,\n 'tour-complete': this.tourComplete,\n 'tour-dismiss': this.tourDismiss,\n 'tour-offpath': this.tourOffpath,\n 'tour-error': this.tourError,\n };\n\n private readonly props = computed<HostProps>(() => {\n const c = this.config;\n const locale = this.locale();\n const source = this.source();\n return {\n fallbackManifest: this.fallbackManifest() ?? c.fallbackManifest ?? '',\n manifest: this.manifest() ?? c.manifest ?? '',\n buildId: this.buildId() ?? c.buildId ?? '',\n guideOrigin: this.guideOrigin() ?? c.guideOrigin ?? '',\n locale: locale ?? this.tours.ɵlocale(),\n disabled: this.disabled() ?? c.disabled ?? false,\n strings: this.strings() ?? c.strings,\n source: source !== undefined ? source : (c.source ?? null),\n targetTimeout: this.targetTimeout() ?? c.targetTimeout,\n baseZIndex: this.baseZIndex() ?? c.baseZIndex,\n };\n });\n\n private readonly onEvent = (event: Event): void =>\n this.zone.run(() => {\n const record = { type: event.type, detail: (event as CustomEvent).detail } as SteppyTourEvent;\n this.tours.ɵrecord(record);\n (this.outputs[record.type] as OutputEmitterRef<unknown>).emit(record.detail);\n });\n\n constructor() {\n // afterNextRender runs in the browser only, outside the Angular zone.\n afterNextRender(() => this.mount());\n effect(() => {\n const el = this.el();\n const props = this.props();\n if (el) this.zone.runOutsideAngular(() => applyProps(el, props));\n });\n inject(DestroyRef).onDestroy(() => this.unmount());\n }\n\n private mount(): void {\n if (!this.browser) return;\n if (!this.tours.ɵclaim(this)) {\n console.warn(`${PREFIX} another <steppy-tours> is already mounted; this one does nothing. Mount it once, in the app shell.`);\n return;\n }\n const props = untracked(this.props);\n if (!props.fallbackManifest && !props.manifest) {\n console.warn(`${PREFIX} no manifest: set fallbackManifest (or manifest) in provideSteppyTours() or on <steppy-tours>`);\n }\n // The element is on the page — with its properties and listeners — before\n // the overlay module defines it: the overlay keeps properties set before\n // its upgrade, a resumed tour's tour-start is not missed, and anything that\n // waits for the element to be defined finds it in the DOM.\n const el = this.document.createElement(this.overlay.tag) as TourHostElement;\n applyProps(el, props);\n el.navigate = this.navigate;\n for (const name of TOUR_EVENT_NAMES) el.addEventListener(name, this.onEvent);\n this.hostRef.nativeElement.appendChild(el);\n this.el.set(el);\n this.overlay\n .load()\n .then(() => customElements.whenDefined(this.overlay.tag))\n .then(\n () => {\n if (this.el() === el) this.tours.ɵready(this, el);\n },\n (err: unknown) => {\n if (this.el() !== el) return;\n console.error(`${PREFIX} the tour overlay failed to load`, err);\n this.tours.ɵready(this, null);\n },\n );\n }\n\n private unmount(): void {\n const el = untracked(this.el);\n if (el) {\n for (const name of TOUR_EVENT_NAMES) el.removeEventListener(name, this.onEvent);\n el.remove();\n this.el.set(null);\n }\n this.tours.ɵrelease(this);\n }\n}\n","import { Directive, inject, input } from '@angular/core';\nimport type { TourStartOptions } from '@steppy/player/tour';\n\nimport { SteppyTours } from './tours.service';\n\n/**\n * Start a tour on click:\n *\n * ```html\n * <button steppyTourStart=\"create-invoice\">Show me how</button>\n * <button [steppyTourStart]=\"'send-invoice'\" [steppyTourParams]=\"{ invoiceId: id }\">Help</button>\n * ```\n */\n@Directive({\n selector: '[steppyTourStart]',\n host: { '(click)': 'onClick()' },\n})\nexport class SteppyTourStart {\n readonly steppyTourStart = input.required<string>();\n readonly steppyTourParams = input<Record<string, string | number>>();\n readonly steppyTourStep = input<string>();\n\n private readonly tours = inject(SteppyTours);\n\n protected onClick(): void {\n const options: TourStartOptions = {};\n const params = this.steppyTourParams();\n const step = this.steppyTourStep();\n if (params) options.params = params;\n if (step) options.step = step;\n void this.tours.start(this.steppyTourStart(), options);\n }\n}\n","/*\n * @steppy/angular — the Angular adapter for the Steppy in-app tour overlay.\n */\n\nexport { provideSteppyTours } from './provide';\nexport type { SteppyLocaleSource, SteppyNavigate, SteppyToursConfig } from './config';\nexport { SteppyTours, type SteppyTourEvent } from './tours.service';\nexport { SteppyToursComponent } from './tours.component';\nexport { SteppyTourStart } from './tour-start.directive';\nexport { routerNavigate } from './navigate';\n\n// Types only: erased at build time, so nothing from the overlay loads on the server.\nexport type {\n TourErrorDetail,\n TourEventDetailMap,\n TourNavigateContext,\n TourPosition,\n TourSource,\n TourStartOptions,\n TourStringOverrides,\n} from '@steppy/player/tour';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["PREFIX"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steppy/angular",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Angular adapter for the Steppy in-app tour overlay: provideSteppyTours(), <steppy-tours> and the SteppyTours service",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Steppy-dev/steppy.git",
|
|
9
|
+
"directory": "packages/angular"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"angular",
|
|
13
|
+
"steppy",
|
|
14
|
+
"product-tour",
|
|
15
|
+
"onboarding",
|
|
16
|
+
"playwright"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@angular/common": ">=20.0.0 <23.0.0",
|
|
24
|
+
"@angular/core": ">=20.0.0 <23.0.0",
|
|
25
|
+
"@angular/router": ">=20.0.0 <23.0.0",
|
|
26
|
+
"@steppy/player": "0.3.0",
|
|
27
|
+
"rxjs": "^7.4.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"tslib": "^2.3.0"
|
|
31
|
+
},
|
|
32
|
+
"module": "fesm2022/steppy-angular.mjs",
|
|
33
|
+
"typings": "types/steppy-angular.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
"./package.json": {
|
|
36
|
+
"default": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./types/steppy-angular.d.ts",
|
|
40
|
+
"default": "./fesm2022/steppy-angular.mjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"type": "module"
|
|
6
44
|
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { Signal, EnvironmentProviders, OutputEmitterRef } from '@angular/core';
|
|
3
|
+
import * as _steppy_player_tour from '@steppy/player/tour';
|
|
4
|
+
import { TourNavigate, TourStringOverrides, TourSource, TourHostApi, TourEventDetailMap, TourPosition, TourErrorDetail, TourStartOptions } from '@steppy/player/tour';
|
|
5
|
+
export { TourErrorDetail, TourEventDetailMap, TourNavigateContext, TourPosition, TourSource, TourStartOptions, TourStringOverrides } from '@steppy/player/tour';
|
|
6
|
+
import { Observable } from 'rxjs';
|
|
7
|
+
import * as _steppy_angular from '@steppy/angular';
|
|
8
|
+
import { Router } from '@angular/router';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Where the tours' language comes from. A string or a signal is used as is; a
|
|
12
|
+
* function is called once, in the root injection context, so it may
|
|
13
|
+
* ``inject()`` the app's i18n service and return a signal of its language.
|
|
14
|
+
* ``null``, ``undefined`` and ``''`` all mean "follow ``<html lang>``".
|
|
15
|
+
*/
|
|
16
|
+
type SteppyLocaleSource = string | Signal<string | null | undefined> | (() => string | Signal<string | null | undefined>);
|
|
17
|
+
/** How a tour changes route; ``(path, {appPath, tourId}) => …`` for your own. */
|
|
18
|
+
type SteppyNavigate = TourNavigate;
|
|
19
|
+
interface SteppyToursConfig {
|
|
20
|
+
/** The tour manifest bundled with the app (same origin), e.g. ``tours/tours.manifest.json``. */
|
|
21
|
+
fallbackManifest?: string;
|
|
22
|
+
/** A remote manifest URL; may contain ``{buildId}``. */
|
|
23
|
+
manifest?: string;
|
|
24
|
+
/** The app's build id, for ``{buildId}``. Default: the bundled manifest's. */
|
|
25
|
+
buildId?: string;
|
|
26
|
+
/** The origin trusted for guide links. Default: the bundled manifest's. */
|
|
27
|
+
guideOrigin?: string;
|
|
28
|
+
/** Milliseconds to wait for a step's element (500–120000). Default: the overlay's 8000. */
|
|
29
|
+
targetTimeout?: number;
|
|
30
|
+
/** Stacking where the top layer is unavailable. Default: the overlay's. */
|
|
31
|
+
baseZIndex?: number;
|
|
32
|
+
/** Turns tours off; stops a running one. */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
/** Chrome string overrides per locale (buttons, hints). */
|
|
35
|
+
strings?: TourStringOverrides;
|
|
36
|
+
/** Supply tour files yourself; see the overlay's ``source`` property. */
|
|
37
|
+
source?: TourSource | null;
|
|
38
|
+
/** The tours' language. Default: ``<html lang>``. */
|
|
39
|
+
locale?: SteppyLocaleSource;
|
|
40
|
+
/**
|
|
41
|
+
* ``'router'`` (default): the Angular router, inside the zone; a refused or
|
|
42
|
+
* redirected navigation fails the tour at once. ``'document'``: a full page
|
|
43
|
+
* load. Or a function of your own.
|
|
44
|
+
*/
|
|
45
|
+
navigate?: 'router' | 'document' | SteppyNavigate;
|
|
46
|
+
/** How long ``start()`` waits for ``<steppy-tours>`` and the overlay to load. Default 10000 ms. */
|
|
47
|
+
readyTimeout?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Configure in-app tours for the app, in ``app.config.ts``:
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* provideSteppyTours({
|
|
55
|
+
* fallbackManifest: 'tours/tours.manifest.json',
|
|
56
|
+
* locale: () => {
|
|
57
|
+
* const translate = inject(TranslateService);
|
|
58
|
+
* return toSignal(translate.onLangChange.pipe(map((e) => e.lang)), { initialValue: translate.getCurrentLang() ?? '' });
|
|
59
|
+
* },
|
|
60
|
+
* })
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* Then place ``<steppy-tours />`` once in the app shell.
|
|
64
|
+
*/
|
|
65
|
+
declare function provideSteppyTours(config?: SteppyToursConfig): EnvironmentProviders;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The seam between the adapter and the tour overlay (``@steppy/player/tour``).
|
|
69
|
+
*
|
|
70
|
+
* Everything imported from the overlay at the top of this package is a *type*:
|
|
71
|
+
* the overlay touches ``HTMLElement`` when its module loads, so its only
|
|
72
|
+
* runtime reference is the dynamic ``import()`` in ``OVERLAY_LOADER``, which
|
|
73
|
+
* runs in the browser, after the first render. Tests swap the loader for a fake
|
|
74
|
+
* element on a tag of their own.
|
|
75
|
+
*
|
|
76
|
+
* One literal per line: ``tests/test_angular_adapter_sync.py`` reads the tag and
|
|
77
|
+
* the event names with regular expressions and compares them with the overlay.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/** ``<steppy-tour-host>`` as the adapter drives it. */
|
|
81
|
+
type TourHostElement = HTMLElement & TourHostApi;
|
|
82
|
+
|
|
83
|
+
/** One overlay event, as the service and the component outputs see it. */
|
|
84
|
+
type SteppyTourEvent = {
|
|
85
|
+
[K in keyof TourEventDetailMap]: {
|
|
86
|
+
type: K;
|
|
87
|
+
detail: TourEventDetailMap[K];
|
|
88
|
+
};
|
|
89
|
+
}[keyof TourEventDetailMap];
|
|
90
|
+
/**
|
|
91
|
+
* Start and follow in-app tours from anywhere in the app.
|
|
92
|
+
*
|
|
93
|
+
* The overlay itself lives in ``<steppy-tours>``, placed once in the app
|
|
94
|
+
* shell; ``start()`` waits for it to be mounted and loaded (up to
|
|
95
|
+
* ``readyTimeout``), so a help button can call it during startup. State is
|
|
96
|
+
* built from the overlay's six events and exposed as signals; on the server
|
|
97
|
+
* every call is a no-op.
|
|
98
|
+
*/
|
|
99
|
+
declare class SteppyTours {
|
|
100
|
+
private readonly zone;
|
|
101
|
+
private readonly browser;
|
|
102
|
+
/** @internal */
|
|
103
|
+
readonly ɵconfig: SteppyToursConfig;
|
|
104
|
+
/** @internal The configured locale source, resolved once in the root injection context. */
|
|
105
|
+
readonly ɵlocale: Signal<string>;
|
|
106
|
+
private readonly _active;
|
|
107
|
+
private readonly _current;
|
|
108
|
+
private readonly _lastError;
|
|
109
|
+
private readonly _events;
|
|
110
|
+
/** A tour is running (from ``tour-start`` until it completes, is dismissed or fails). */
|
|
111
|
+
readonly active: Signal<boolean>;
|
|
112
|
+
/** The step shown, or ``null`` before the first step and after the end. */
|
|
113
|
+
readonly current: Signal<TourPosition | null>;
|
|
114
|
+
/** The last ``tour-error`` since the last start (fatal or not). */
|
|
115
|
+
readonly lastError: Signal<TourErrorDetail | null>;
|
|
116
|
+
/** Every overlay event, in order, delivered inside the Angular zone. */
|
|
117
|
+
readonly events: Observable<SteppyTourEvent>;
|
|
118
|
+
private owner;
|
|
119
|
+
private host;
|
|
120
|
+
private failed;
|
|
121
|
+
private waiters;
|
|
122
|
+
private seq;
|
|
123
|
+
/**
|
|
124
|
+
* Start a tour. Resolves ``true`` once it started, ``false`` when it could
|
|
125
|
+
* not (a ``tour-error`` says why), when no ``<steppy-tours>`` is mounted
|
|
126
|
+
* in time, when a later ``start()``/``stop()`` superseded this call, or on
|
|
127
|
+
* the server. Never rejects.
|
|
128
|
+
*/
|
|
129
|
+
start(tourId: string, options?: TourStartOptions): Promise<boolean>;
|
|
130
|
+
/** End the running tour (``reason`` is passed to ``tour-dismiss``); also cancels a waiting ``start()``. */
|
|
131
|
+
stop(reason?: string): void;
|
|
132
|
+
/** Advance the current step, whatever its kind. */
|
|
133
|
+
next(): void;
|
|
134
|
+
/** @internal The first component to mount owns the overlay. */
|
|
135
|
+
ɵclaim(owner: object): boolean;
|
|
136
|
+
/** @internal The overlay is defined (``host``) or failed to load (``null``). */
|
|
137
|
+
ɵready(owner: object, host: TourHostElement | null): void;
|
|
138
|
+
/** @internal The owning component was destroyed. */
|
|
139
|
+
ɵrelease(owner: object): void;
|
|
140
|
+
/** @internal One overlay event (already inside the zone). */
|
|
141
|
+
ɵrecord(event: SteppyTourEvent): void;
|
|
142
|
+
private readyTimeout;
|
|
143
|
+
private whenReady;
|
|
144
|
+
private flush;
|
|
145
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SteppyTours, never>;
|
|
146
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<SteppyTours>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The in-app tour overlay. Place it **once**, in the app shell:
|
|
151
|
+
*
|
|
152
|
+
* ```html
|
|
153
|
+
* <router-outlet />
|
|
154
|
+
* <steppy-tours />
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* It renders nothing on the server. In the browser it mounts
|
|
158
|
+
* ``<steppy-tour-host>`` after the first render and loads the overlay
|
|
159
|
+
* module lazily; every input overrides the matching
|
|
160
|
+
* ``provideSteppyTours()`` setting. Start tours with the
|
|
161
|
+
* ``SteppyTours`` service or the ``steppyTourStart`` directive.
|
|
162
|
+
*
|
|
163
|
+
* Destroying it suspends a running tour rather than dismissing it: the overlay
|
|
164
|
+
* keeps its resume record, so a tour survives a shell swap. Call
|
|
165
|
+
* ``SteppyTours.stop()`` first to end it for good.
|
|
166
|
+
*/
|
|
167
|
+
declare class SteppyToursComponent {
|
|
168
|
+
readonly fallbackManifest: _angular_core.InputSignal<string | undefined>;
|
|
169
|
+
readonly manifest: _angular_core.InputSignal<string | undefined>;
|
|
170
|
+
readonly buildId: _angular_core.InputSignal<string | undefined>;
|
|
171
|
+
readonly guideOrigin: _angular_core.InputSignal<string | undefined>;
|
|
172
|
+
/** ``null``/``undefined``: the configured locale source. ``''``: follow ``<html lang>``. */
|
|
173
|
+
readonly locale: _angular_core.InputSignal<string | null | undefined>;
|
|
174
|
+
readonly targetTimeout: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
|
|
175
|
+
readonly baseZIndex: _angular_core.InputSignalWithTransform<number | undefined, unknown>;
|
|
176
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean | undefined, unknown>;
|
|
177
|
+
readonly strings: _angular_core.InputSignal<Partial<Record<string, Partial<_steppy_player_tour.TourStrings>>> | undefined>;
|
|
178
|
+
readonly source: _angular_core.InputSignal<TourSource | null | undefined>;
|
|
179
|
+
readonly tourStart: OutputEmitterRef<_steppy_player_tour.TourEventMeta & {
|
|
180
|
+
total: number;
|
|
181
|
+
source: _steppy_player_tour.TourSourceKind;
|
|
182
|
+
resumed: boolean;
|
|
183
|
+
entryNavigated: boolean;
|
|
184
|
+
}>;
|
|
185
|
+
readonly tourStep: OutputEmitterRef<_steppy_player_tour.TourStepPosition & {
|
|
186
|
+
advance: _steppy_player_tour.AdvanceKind;
|
|
187
|
+
reason: _steppy_player_tour.StepReason;
|
|
188
|
+
}>;
|
|
189
|
+
readonly tourComplete: OutputEmitterRef<_steppy_player_tour.TourEventMeta & {
|
|
190
|
+
total: number;
|
|
191
|
+
}>;
|
|
192
|
+
readonly tourDismiss: OutputEmitterRef<_steppy_player_tour.TourStepPosition & {
|
|
193
|
+
via: _steppy_player_tour.DismissVia;
|
|
194
|
+
reason?: string;
|
|
195
|
+
}>;
|
|
196
|
+
readonly tourOffpath: OutputEmitterRef<_steppy_player_tour.TourStepPosition & {
|
|
197
|
+
reason: "target-lost";
|
|
198
|
+
}>;
|
|
199
|
+
readonly tourError: OutputEmitterRef<_steppy_angular.TourErrorDetail>;
|
|
200
|
+
private readonly tours;
|
|
201
|
+
private readonly config;
|
|
202
|
+
private readonly overlay;
|
|
203
|
+
private readonly zone;
|
|
204
|
+
private readonly document;
|
|
205
|
+
private readonly hostRef;
|
|
206
|
+
private readonly browser;
|
|
207
|
+
private readonly navigate;
|
|
208
|
+
private readonly el;
|
|
209
|
+
private readonly outputs;
|
|
210
|
+
private readonly props;
|
|
211
|
+
private readonly onEvent;
|
|
212
|
+
constructor();
|
|
213
|
+
private mount;
|
|
214
|
+
private unmount;
|
|
215
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SteppyToursComponent, never>;
|
|
216
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SteppyToursComponent, "steppy-tours", never, { "fallbackManifest": { "alias": "fallbackManifest"; "required": false; "isSignal": true; }; "manifest": { "alias": "manifest"; "required": false; "isSignal": true; }; "buildId": { "alias": "buildId"; "required": false; "isSignal": true; }; "guideOrigin": { "alias": "guideOrigin"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "targetTimeout": { "alias": "targetTimeout"; "required": false; "isSignal": true; }; "baseZIndex": { "alias": "baseZIndex"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "strings": { "alias": "strings"; "required": false; "isSignal": true; }; "source": { "alias": "source"; "required": false; "isSignal": true; }; }, { "tourStart": "tourStart"; "tourStep": "tourStep"; "tourComplete": "tourComplete"; "tourDismiss": "tourDismiss"; "tourOffpath": "tourOffpath"; "tourError": "tourError"; }, never, never, true, never>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Start a tour on click:
|
|
221
|
+
*
|
|
222
|
+
* ```html
|
|
223
|
+
* <button steppyTourStart="create-invoice">Show me how</button>
|
|
224
|
+
* <button [steppyTourStart]="'send-invoice'" [steppyTourParams]="{ invoiceId: id }">Help</button>
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare class SteppyTourStart {
|
|
228
|
+
readonly steppyTourStart: _angular_core.InputSignal<string>;
|
|
229
|
+
readonly steppyTourParams: _angular_core.InputSignal<Record<string, string | number> | undefined>;
|
|
230
|
+
readonly steppyTourStep: _angular_core.InputSignal<string | undefined>;
|
|
231
|
+
private readonly tours;
|
|
232
|
+
protected onClick(): void;
|
|
233
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SteppyTourStart, never>;
|
|
234
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<SteppyTourStart, "[steppyTourStart]", never, { "steppyTourStart": { "alias": "steppyTourStart"; "required": true; "isSignal": true; }; "steppyTourParams": { "alias": "steppyTourParams"; "required": false; "isSignal": true; }; "steppyTourStep": { "alias": "steppyTourStep"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Navigate with the router and settle only once the router is where the tour
|
|
239
|
+
* wants to be. ``navigateByUrl`` resolves ``false`` both for a refused
|
|
240
|
+
* navigation and for one to the current URL, and ``true`` for one a guard
|
|
241
|
+
* redirected elsewhere — so the outcome is read from ``router.url``. A
|
|
242
|
+
* rejection makes the overlay fail the start at once (``entry-params``)
|
|
243
|
+
* instead of waiting for a target that will never render.
|
|
244
|
+
*/
|
|
245
|
+
declare function routerNavigate(router: Router): TourNavigate;
|
|
246
|
+
|
|
247
|
+
export { SteppyTourStart, SteppyTours, SteppyToursComponent, provideSteppyTours, routerNavigate };
|
|
248
|
+
export type { SteppyLocaleSource, SteppyNavigate, SteppyTourEvent, SteppyToursConfig };
|