ngx-ui-tour-ionic
Version:
UI tour library for Angular 15+
204 lines (193 loc) • 20.5 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, inject, DOCUMENT, input, Component, viewChild, contentChild, TemplateRef, signal, ElementRef, Directive, makeEnvironmentProviders } from '@angular/core';
import { TourService, TourHotkeyListenerComponent, BaseTourProxyAnchor, UI_TOUR_OPTIONS } from 'ngx-ui-tour-core';
export { Direction, TourState, UI_TOUR_OPTIONS } from 'ngx-ui-tour-core';
import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCardTitle, IonIcon, IonPopover, createAnimation, Config } from '@ionic/angular/standalone';
import { NgTemplateOutlet } from '@angular/common';
import { addIcons } from 'ionicons';
import { chevronForwardOutline, chevronBackOutline, closeOutline } from 'ionicons/icons';
class TourStepTemplateService {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourStepTemplateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourStepTemplateService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourStepTemplateService, decorators: [{
type: Injectable
}] });
class IonTourService extends TourService {
constructor() {
super(...arguments);
this.document = inject(DOCUMENT);
}
// noinspection JSUnusedGlobalSymbols
showStep(step) {
// In case "scrollContainer" is already set to HTMLElement, we DO want to set it again since the current
// html element reference might be already removed from DOM
if (step.smoothScroll && (!step.scrollContainer || typeof step.scrollContainer !== 'string')) {
const docEl = this.document.documentElement, tabsRouter = docEl.querySelector('ion-router-outlet[tabs=true]'), generalRouter = docEl.querySelector('ion-router-outlet'), router = tabsRouter ?? generalRouter, ionContent = router ? router.querySelector(':scope > .ion-page:not(.ion-page-hidden) ion-content') :
docEl.querySelector('ion-content');
step.scrollContainer = ionContent?.shadowRoot.querySelector('[part=scroll]');
}
return super.showStep(step);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IonTourService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IonTourService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: IonTourService, decorators: [{
type: Injectable
}] });
class TourDefaultStepTemplateComponent {
constructor() {
this.step = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
this.tourService = inject(IonTourService);
this.addIonicIcons();
}
addIonicIcons() {
addIcons({
closeOutline,
chevronBackOutline,
chevronForwardOutline
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourDefaultStepTemplateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TourDefaultStepTemplateComponent, isStandalone: true, selector: "tour-default-step-template", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let step = this.step();\r\n\r\n<ion-card>\r\n <ion-card-header>\r\n <ion-card-title>{{ step.title }}</ion-card-title>\r\n <ion-button\r\n class=\"close\"\r\n fill=\"clear\"\r\n shape=\"round\"\r\n (click)=\"tourService.end()\"\r\n >\r\n <ion-icon slot=\"icon-only\" name=\"close-outline\"/>\r\n </ion-button>\r\n </ion-card-header>\r\n\r\n <ion-card-content\r\n [innerHTML]=\"step.content\"\r\n />\r\n\r\n <div\r\n class=\"footer\"\r\n [class.no-progress]=\"!step.showProgress\"\r\n >\r\n <ion-button\r\n fill=\"clear\"\r\n [disabled]=\"!tourService.hasPrev(step)\"\r\n (click)=\"tourService.prev()\"\r\n >\r\n <ion-icon slot=\"start\" name=\"chevron-back-outline\"/>\r\n {{ step.prevBtnTitle }}\r\n </ion-button>\r\n @if (step.showProgress) {\r\n <div class=\"progress\">{{ tourService.steps.indexOf(step) + 1 }} / {{ tourService.steps.length }}</div>\r\n }\r\n @if (tourService.hasNext(step) && !step.nextOnAnchorClick) {\r\n <ion-button\r\n fill=\"clear\"\r\n (click)=\"tourService.next()\"\r\n >\r\n {{ step.nextBtnTitle }}\r\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"/>\r\n </ion-button>\r\n }\r\n @if (!tourService.hasNext(step)) {\r\n <ion-button\r\n fill=\"clear\"\r\n (click)=\"tourService.end()\"\r\n >\r\n {{ step.endBtnTitle }}\r\n </ion-button>\r\n }\r\n </div>\r\n</ion-card>\r\n", styles: ["ion-card{margin:0;pointer-events:auto}ion-card-header{display:flex;flex-direction:row;align-items:center;justify-content:space-between;padding:8px 16px}ion-card-header ion-card-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}ion-button.close{--padding-start: 5px;--padding-end: 5px;--padding-top: 5px;--padding-bottom: 5px;margin:0 -8px 0 0}.footer{display:grid;grid-template-columns:1fr auto 1fr;padding:0 8px 8px;align-items:center;gap:8px}.footer>*{max-width:fit-content}.footer>*:last-child{justify-self:flex-end}.footer .progress{font-size:12px;font-weight:600;color:#00000061;white-space:nowrap}.footer.no-progress{grid-template-columns:1fr 1fr}.footer ion-button{text-transform:capitalize}\n"], dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourDefaultStepTemplateComponent, decorators: [{
type: Component,
args: [{ selector: 'tour-default-step-template', imports: [
IonButton,
IonCard,
IonCardContent,
IonCardHeader,
IonCardTitle,
IonIcon
], template: "@let step = this.step();\r\n\r\n<ion-card>\r\n <ion-card-header>\r\n <ion-card-title>{{ step.title }}</ion-card-title>\r\n <ion-button\r\n class=\"close\"\r\n fill=\"clear\"\r\n shape=\"round\"\r\n (click)=\"tourService.end()\"\r\n >\r\n <ion-icon slot=\"icon-only\" name=\"close-outline\"/>\r\n </ion-button>\r\n </ion-card-header>\r\n\r\n <ion-card-content\r\n [innerHTML]=\"step.content\"\r\n />\r\n\r\n <div\r\n class=\"footer\"\r\n [class.no-progress]=\"!step.showProgress\"\r\n >\r\n <ion-button\r\n fill=\"clear\"\r\n [disabled]=\"!tourService.hasPrev(step)\"\r\n (click)=\"tourService.prev()\"\r\n >\r\n <ion-icon slot=\"start\" name=\"chevron-back-outline\"/>\r\n {{ step.prevBtnTitle }}\r\n </ion-button>\r\n @if (step.showProgress) {\r\n <div class=\"progress\">{{ tourService.steps.indexOf(step) + 1 }} / {{ tourService.steps.length }}</div>\r\n }\r\n @if (tourService.hasNext(step) && !step.nextOnAnchorClick) {\r\n <ion-button\r\n fill=\"clear\"\r\n (click)=\"tourService.next()\"\r\n >\r\n {{ step.nextBtnTitle }}\r\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"/>\r\n </ion-button>\r\n }\r\n @if (!tourService.hasNext(step)) {\r\n <ion-button\r\n fill=\"clear\"\r\n (click)=\"tourService.end()\"\r\n >\r\n {{ step.endBtnTitle }}\r\n </ion-button>\r\n }\r\n </div>\r\n</ion-card>\r\n", styles: ["ion-card{margin:0;pointer-events:auto}ion-card-header{display:flex;flex-direction:row;align-items:center;justify-content:space-between;padding:8px 16px}ion-card-header ion-card-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}ion-button.close{--padding-start: 5px;--padding-end: 5px;--padding-top: 5px;--padding-bottom: 5px;margin:0 -8px 0 0}.footer{display:grid;grid-template-columns:1fr auto 1fr;padding:0 8px 8px;align-items:center;gap:8px}.footer>*{max-width:fit-content}.footer>*:last-child{justify-self:flex-end}.footer .progress{font-size:12px;font-weight:600;color:#00000061;white-space:nowrap}.footer.no-progress{grid-template-columns:1fr 1fr}.footer ion-button{text-transform:capitalize}\n"] }]
}], ctorParameters: () => [], propDecorators: { step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: true }] }] } });
class TourStepTemplateComponent extends TourHotkeyListenerComponent {
constructor() {
super(...arguments);
this.ionPopover = viewChild(IonPopover, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "ionPopover" }] : /* istanbul ignore next */ []));
this.stepTemplate = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "stepTemplate" }] : /* istanbul ignore next */ []));
this.stepTemplateContent = contentChild(TemplateRef, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "stepTemplateContent" }] : /* istanbul ignore next */ []));
this.step = signal({}, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
this.noopLeaveAnimation = () => createAnimation();
this.tourService = inject(IonTourService);
this.tourStepTemplateService = inject(TourStepTemplateService);
}
ngAfterViewInit() {
this.tourStepTemplateService.templateComponent = this;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourStepTemplateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.2", type: TourStepTemplateComponent, isStandalone: true, selector: "tour-step-template", inputs: { stepTemplate: { classPropertyName: "stepTemplate", publicName: "stepTemplate", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "stepTemplateContent", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "ionPopover", first: true, predicate: IonPopover, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@let step = this.step();\r\n\r\n<ion-popover\r\n [leaveAnimation]=\"noopLeaveAnimation\"\r\n [backdropDismiss]=\"false\"\r\n [keyboardClose]=\"false\"\r\n [showBackdrop]=\"false\"\r\n [dismissOnSelect]=\"false\"\r\n [style.--min-width]=\"step?.stepDimensions?.minWidth\"\r\n [style.--max-width]=\"step?.stepDimensions?.maxWidth\"\r\n [style.--width]=\"step?.stepDimensions?.width\"\r\n [arrow]=\"step?.showArrow ?? true\"\r\n [focusTrap]=\"step?.trapFocus ?? false\"\r\n>\r\n <ng-template>\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n stepTemplate() || stepTemplateContent() || defaultTemplate;\r\n context: {step: step}\r\n \"\r\n />\r\n </ng-template>\r\n</ion-popover>\r\n\r\n<ng-template #defaultTemplate let-step=\"step\">\r\n <tour-default-step-template\r\n [step]=\"step\"\r\n />\r\n</ng-template>\r\n", styles: ["ion-popover{pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IonPopover, selector: "ion-popover" }, { kind: "component", type: TourDefaultStepTemplateComponent, selector: "tour-default-step-template", inputs: ["step"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourStepTemplateComponent, decorators: [{
type: Component,
args: [{ selector: 'tour-step-template', imports: [
NgTemplateOutlet,
IonPopover,
TourDefaultStepTemplateComponent
], template: "@let step = this.step();\r\n\r\n<ion-popover\r\n [leaveAnimation]=\"noopLeaveAnimation\"\r\n [backdropDismiss]=\"false\"\r\n [keyboardClose]=\"false\"\r\n [showBackdrop]=\"false\"\r\n [dismissOnSelect]=\"false\"\r\n [style.--min-width]=\"step?.stepDimensions?.minWidth\"\r\n [style.--max-width]=\"step?.stepDimensions?.maxWidth\"\r\n [style.--width]=\"step?.stepDimensions?.width\"\r\n [arrow]=\"step?.showArrow ?? true\"\r\n [focusTrap]=\"step?.trapFocus ?? false\"\r\n>\r\n <ng-template>\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n stepTemplate() || stepTemplateContent() || defaultTemplate;\r\n context: {step: step}\r\n \"\r\n />\r\n </ng-template>\r\n</ion-popover>\r\n\r\n<ng-template #defaultTemplate let-step=\"step\">\r\n <tour-default-step-template\r\n [step]=\"step\"\r\n />\r\n</ng-template>\r\n", styles: ["ion-popover{pointer-events:none}\n"] }]
}], propDecorators: { ionPopover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => IonPopover), { isSignal: true }] }], stepTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepTemplate", required: false }] }], stepTemplateContent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
class TourAnchorIonPopoverDirective {
constructor() {
this.tourAnchor = input(/* @ts-ignore */
...(ngDevMode ? [undefined, { debugName: "tourAnchor" }] : /* istanbul ignore next */ []));
this.isActive = signal(false, /* @ts-ignore */
...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
this.element = inject(ElementRef);
this.tourService = inject(IonTourService);
this.stepTemplateService = inject(TourStepTemplateService);
}
ngOnInit() {
this.tourService.register(this.tourAnchor(), this);
}
ngOnDestroy() {
this.tourService.unregister(this.tourAnchor());
}
async showTourStep(step) {
const templateComponent = this.stepTemplateService.templateComponent, popover = templateComponent.ionPopover();
this.isActive.set(true);
templateComponent.step.set(step);
popover.alignment = step.placement?.alignment;
popover.side = step.placement?.side ?? 'bottom';
popover.cssClass = step.popoverClass;
const event = {
target: this.element.nativeElement
};
await popover.present(event);
}
async hideTourStep() {
this.isActive.set(false);
const popover = this.stepTemplateService.templateComponent.ionPopover();
await popover.dismiss();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourAnchorIonPopoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: TourAnchorIonPopoverDirective, isStandalone: true, selector: "[tourAnchor]", inputs: { tourAnchor: { classPropertyName: "tourAnchor", publicName: "tourAnchor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.touranchor--is-active": "isActive()" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourAnchorIonPopoverDirective, decorators: [{
type: Directive,
args: [{
selector: '[tourAnchor]',
host: {
'[class.touranchor--is-active]': 'isActive()'
}
}]
}], propDecorators: { tourAnchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "tourAnchor", required: false }] }] } });
class TourProxyAnchorComponent extends BaseTourProxyAnchor {
constructor() {
super(...arguments);
// noinspection JSUnusedGlobalSymbols
this.anchorDirective = inject(TourAnchorIonPopoverDirective, {
host: true
});
this.anchorEl = input.required(/* @ts-ignore */
...(ngDevMode ? [{ debugName: "anchorEl" }] : /* istanbul ignore next */ []));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourProxyAnchorComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: TourProxyAnchorComponent, isStandalone: true, selector: "tour-proxy-anchor", inputs: { anchorEl: { classPropertyName: "anchorEl", publicName: "anchorEl", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, hostDirectives: [{ directive: TourAnchorIonPopoverDirective, inputs: ["tourAnchor", "anchorId"] }], ngImport: i0, template: ``, isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TourProxyAnchorComponent, decorators: [{
type: Component,
args: [{
selector: 'tour-proxy-anchor',
template: ``,
hostDirectives: [{
directive: TourAnchorIonPopoverDirective,
inputs: ['tourAnchor: anchorId']
}]
}]
}], propDecorators: { anchorEl: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorEl", required: true }] }] } });
const TourIonPopover = [
TourStepTemplateComponent, TourAnchorIonPopoverDirective, TourProxyAnchorComponent
];
function provideUiTour(config = {}) {
return makeEnvironmentProviders([
{
provide: UI_TOUR_OPTIONS,
useFactory: () => {
const ionicConfig = inject(Config), isIOS = ionicConfig.get('mode') === 'ios';
return {
trapFocus: false,
showArrow: isIOS,
delayAfterNavigation: isIOS ? 700 : 500,
backdropConfig: {
parentContainer: 'ion-app',
...config.backdropConfig
},
...config
};
}
},
IonTourService,
TourStepTemplateService
]);
}
/**
* Generated bundle index. Do not edit.
*/
export { TourAnchorIonPopoverDirective, TourIonPopover, TourProxyAnchorComponent, IonTourService as TourService, TourStepTemplateComponent, provideUiTour };
//# sourceMappingURL=ngx-ui-tour-ionic.mjs.map