UNPKG

ngx-joyride

Version:

An Angular Tour (Joyride) library built entirely in Angular, without using any heavy external dependencies like Bootstrap or JQuery. From now on you can easily guide your users through your site showing them all the sections and features.

1,040 lines (1,024 loc) 88.3 kB
import * as i0 from '@angular/core'; import { Injectable, PLATFORM_ID, Inject, EventEmitter, Directive, Input, Output, Component, ViewEncapsulation, ViewChild, HostListener, NgModule } from '@angular/core'; import * as i10 from '@angular/common'; import { isPlatformBrowser, CommonModule } from '@angular/common'; import { ReplaySubject, of, Observable, Subject } from 'rxjs'; import * as i3 from '@angular/router'; import { RouterModule } from '@angular/router'; import { finalize } from 'rxjs/operators'; import { __awaiter } from 'tslib'; class JoyrideStep { constructor() { this.title = new ReplaySubject(); this.text = new ReplaySubject(); } } class JoyrideError extends Error { constructor(message) { super(message); Object.setPrototypeOf(this, JoyrideError.prototype); } } class JoyrideStepDoesNotExist extends Error { constructor(message) { super(message); Object.setPrototypeOf(this, JoyrideStepDoesNotExist.prototype); } } class JoyrideStepOutOfRange extends Error { constructor(message) { super(message); Object.setPrototypeOf(this, JoyrideStepOutOfRange.prototype); } } const DEFAULT_THEME_COLOR = '#3b5560'; const STEP_DEFAULT_POSITION = 'bottom'; const DEFAULT_TIMEOUT_BETWEEN_STEPS = 1; class ObservableCustomTexts { } const DEFAULT_TEXTS = { prev: of('prev'), next: of('next'), done: of('done'), close: of(null) }; class JoyrideOptionsService { constructor() { this.themeColor = DEFAULT_THEME_COLOR; this.stepDefaultPosition = STEP_DEFAULT_POSITION; this.logsEnabled = false; this.showCounter = true; this.showPrevButton = true; this.stepsOrder = []; } setOptions(options) { this.stepsOrder = options.steps; this.stepDefaultPosition = options.stepDefaultPosition ? options.stepDefaultPosition : this.stepDefaultPosition; this.logsEnabled = typeof options.logsEnabled !== 'undefined' ? options.logsEnabled : this.logsEnabled; this.showCounter = typeof options.showCounter !== 'undefined' ? options.showCounter : this.showCounter; this.showPrevButton = typeof options.showPrevButton !== 'undefined' ? options.showPrevButton : this.showPrevButton; this.themeColor = options.themeColor ? options.themeColor : this.themeColor; this.firstStep = options.startWith; this.waitingTime = typeof options.waitingTime !== 'undefined' ? options.waitingTime : DEFAULT_TIMEOUT_BETWEEN_STEPS; typeof options.customTexts !== 'undefined' ? this.setCustomText(options.customTexts) : this.setCustomText(DEFAULT_TEXTS); } getBackdropColor() { return this.hexToRgb(this.themeColor); } getThemeColor() { return this.themeColor; } getStepDefaultPosition() { return this.stepDefaultPosition; } getStepsOrder() { return this.stepsOrder; } getFirstStep() { return this.firstStep; } getWaitingTime() { return this.waitingTime; } areLogsEnabled() { return this.logsEnabled; } isCounterVisible() { return this.showCounter; } isPrevButtonVisible() { return this.showPrevButton; } getCustomTexts() { return this.customTexts; } setCustomText(texts) { let prev; let next; let done; let close; prev = texts.prev ? texts.prev : DEFAULT_TEXTS.prev; next = texts.next ? texts.next : DEFAULT_TEXTS.next; done = texts.done ? texts.done : DEFAULT_TEXTS.done; close = texts.close ? texts.close : DEFAULT_TEXTS.close; this.customTexts = { prev: this.toObservable(prev), next: this.toObservable(next), done: this.toObservable(done), close: this.toObservable(close) }; } toObservable(value) { return value instanceof Observable ? value : of(value); } hexToRgb(hex) { const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, (m, r, g, b) => { return r + r + g + g + b + b; }); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}` : null; } } JoyrideOptionsService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideOptionsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); JoyrideOptionsService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideOptionsService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideOptionsService, decorators: [{ type: Injectable }] }); const JOYRIDE = 'ngx-joyride:::'; class LoggerService { constructor(optionService) { this.optionService = optionService; } debug(message, data = "") { if (this.optionService.areLogsEnabled()) { console.debug(JOYRIDE + message, data); } } info(message, data = "") { if (this.optionService.areLogsEnabled()) { console.info(JOYRIDE + message, data); } } warn(message, data = "") { if (this.optionService.areLogsEnabled()) { console.warn(JOYRIDE + message, data); } } error(message, data = "") { if (this.optionService.areLogsEnabled()) { console.error(JOYRIDE + message, data); } } } LoggerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: LoggerService, deps: [{ token: JoyrideOptionsService }], target: i0.ɵɵFactoryTarget.Injectable }); LoggerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: LoggerService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: LoggerService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: JoyrideOptionsService }]; } }); const ROUTE_SEPARATOR = '@'; class Step { } var StepActionType; (function (StepActionType) { StepActionType["NEXT"] = "NEXT"; StepActionType["PREV"] = "PREV"; })(StepActionType || (StepActionType = {})); class JoyrideStepsContainerService { constructor(stepOptions, logger) { this.stepOptions = stepOptions; this.logger = logger; this.tempSteps = []; this.currentStepIndex = -2; this.stepHasBeenModified = new Subject(); } getFirstStepIndex() { const firstStep = this.stepOptions.getFirstStep(); const stepIds = this.stepOptions.getStepsOrder(); let index = stepIds.indexOf(firstStep); if (index < 0) { index = 0; if (firstStep !== undefined) this.logger.warn(`The step ${firstStep} does not exist. Check in your step list if it's present.`); } return index; } init() { this.logger.info('Initializing the steps array.'); this.steps = []; this.currentStepIndex = this.getFirstStepIndex() - 1; let stepIds = this.stepOptions.getStepsOrder(); stepIds.forEach(stepId => this.steps.push({ id: stepId, step: null })); } addStep(stepToAdd) { let stepExist = this.tempSteps.filter(step => step.name === stepToAdd.name).length > 0; if (!stepExist) { this.logger.info(`Adding step ${stepToAdd.name} to the steps list.`); this.tempSteps.push(stepToAdd); } else { let stepIndexToReplace = this.tempSteps.findIndex(step => step.name === stepToAdd.name); this.tempSteps[stepIndexToReplace] = stepToAdd; } } get(action) { if (action === StepActionType.NEXT) this.currentStepIndex++; else this.currentStepIndex--; if (this.currentStepIndex < 0 || this.currentStepIndex >= this.steps.length) throw new JoyrideStepOutOfRange('The first or last step of the tour cannot be found!'); const stepName = this.getStepName(this.steps[this.currentStepIndex].id); const index = this.tempSteps.findIndex(step => step.name === stepName); let stepFound = this.tempSteps[index]; this.steps[this.currentStepIndex].step = stepFound; if (stepFound == null) { this.logger.warn(`Step ${this.steps[this.currentStepIndex].id} not found in the DOM. Check if it's hidden by *ngIf directive.`); } return stepFound; } getStepRoute(action) { let stepID; if (action === StepActionType.NEXT) { stepID = this.steps[this.currentStepIndex + 1] ? this.steps[this.currentStepIndex + 1].id : null; } else { stepID = this.steps[this.currentStepIndex - 1] ? this.steps[this.currentStepIndex - 1].id : null; } let stepRoute = stepID && stepID.includes(ROUTE_SEPARATOR) ? stepID.split(ROUTE_SEPARATOR)[1] : ''; return stepRoute; } updatePosition(stepName, position) { let index = this.getStepIndex(stepName); if (this.steps[index].step) { this.steps[index].step.position = position; this.stepHasBeenModified.next(this.steps[index].step); } else { this.logger.warn(`Trying to modify the position of ${stepName} to ${position}. Step not found!Is this step located in a different route?`); } } getStepNumber(stepName) { return this.getStepIndex(stepName) + 1; } getStepsCount() { let stepsOrder = this.stepOptions.getStepsOrder(); return stepsOrder.length; } getStepIndex(stepName) { const index = this.steps .map(step => (step.id.includes(ROUTE_SEPARATOR) ? step.id.split(ROUTE_SEPARATOR)[0] : step.id)) .findIndex(name => stepName === name); if (index === -1) throw new JoyrideError(`The step with name: ${stepName} does not exist in the step list.`); return index; } getStepName(stepID) { let stepName = stepID && stepID.includes(ROUTE_SEPARATOR) ? stepID.split(ROUTE_SEPARATOR)[0] : stepID; return stepName; } } JoyrideStepsContainerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideStepsContainerService, deps: [{ token: JoyrideOptionsService }, { token: LoggerService }], target: i0.ɵɵFactoryTarget.Injectable }); JoyrideStepsContainerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideStepsContainerService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideStepsContainerService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: JoyrideOptionsService }, { type: LoggerService }]; } }); class DomRefService { constructor(platformId) { this.platformId = platformId; this.fakeDocument = { body: {}, documentElement: {} }; this.fakeWindow = { document: this.fakeDocument, navigator: {} }; } getNativeWindow() { if (isPlatformBrowser(this.platformId)) return window; else return this.fakeWindow; } getNativeDocument() { if (isPlatformBrowser(this.platformId)) return document; else return this.fakeDocument; } } DomRefService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DomRefService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); DomRefService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DomRefService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DomRefService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; } }); class TemplatesService { setPrevButton(template) { this._prevButton = template; } getPrevButton() { return this._prevButton; } setNextButton(template) { this._nextButton = template; } getNextButton() { return this._nextButton; } setDoneButton(template) { this._doneButton = template; } getDoneButton() { return this._doneButton; } setCounter(template) { this._counter = template; } getCounter() { return this._counter; } } TemplatesService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: TemplatesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); TemplatesService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: TemplatesService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: TemplatesService, decorators: [{ type: Injectable }] }); const NO_POSITION = 'NO_POSITION'; class JoyrideDirective { constructor(joyrideStepsContainer, viewContainerRef, domService, router, templateService, platformId) { this.joyrideStepsContainer = joyrideStepsContainer; this.viewContainerRef = viewContainerRef; this.domService = domService; this.router = router; this.templateService = templateService; this.platformId = platformId; this.stepPosition = NO_POSITION; this.prev = new EventEmitter(); this.next = new EventEmitter(); this.done = new EventEmitter(); this.subscriptions = []; this.windowRef = this.domService.getNativeWindow(); this.step = new JoyrideStep(); } ngAfterViewInit() { if (!isPlatformBrowser(this.platformId)) return; if (this.prevTemplate) this.templateService.setPrevButton(this.prevTemplate); if (this.nextTemplate) this.templateService.setNextButton(this.nextTemplate); if (this.doneTemplate) this.templateService.setDoneButton(this.doneTemplate); if (this.counterTemplate) this.templateService.setCounter(this.counterTemplate); this.step.position = this.stepPosition; this.step.targetViewContainer = this.viewContainerRef; this.setAsyncFields(this.step); this.step.stepContent = this.stepContent; this.step.stepContentParams = this.stepContentParams; this.step.nextClicked = this.next; this.step.prevCliked = this.prev; this.step.tourDone = this.done; if (!this.name) throw new JoyrideError("All the steps should have the 'joyrideStep' property set with a custom name."); this.step.name = this.name; this.step.route = this.router.url.substr(0, 1) === '/' ? this.router.url.substr(1) : this.router.url; this.step.transformCssStyle = this.windowRef.getComputedStyle(this.viewContainerRef.element.nativeElement).transform; this.step.isElementOrAncestorFixed = this.isElementFixed(this.viewContainerRef.element) || this.isAncestorsFixed(this.viewContainerRef.element.nativeElement.parentElement); this.joyrideStepsContainer.addStep(this.step); } ngOnChanges(changes) { if (changes['title'] || changes['text']) { this.setAsyncFields(this.step); } } isElementFixed(element) { return this.windowRef.getComputedStyle(element.nativeElement).position === 'fixed'; } setAsyncFields(step) { if (this.title instanceof Observable) { this.subscriptions.push(this.title.subscribe(title => { step.title.next(title); })); } else { step.title.next(this.title); } if (this.text instanceof Observable) { this.subscriptions.push(this.text.subscribe(text => { step.text.next(text); })); } else { step.text.next(this.text); } } isAncestorsFixed(nativeElement) { if (!nativeElement || !nativeElement.parentElement) return false; let isElementFixed = this.windowRef.getComputedStyle(nativeElement.parentElement).position === 'fixed'; if (nativeElement.nodeName === 'BODY') { return isElementFixed; } if (isElementFixed) return true; else return this.isAncestorsFixed(nativeElement.parentElement); } ngOnDestroy() { this.subscriptions.forEach(sub => { sub.unsubscribe(); }); } } JoyrideDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideDirective, deps: [{ token: JoyrideStepsContainerService }, { token: i0.ViewContainerRef }, { token: DomRefService }, { token: i3.Router }, { token: TemplatesService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Directive }); JoyrideDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.1.1", type: JoyrideDirective, selector: "joyrideStep, [joyrideStep]", inputs: { name: ["joyrideStep", "name"], nextStep: "nextStep", title: "title", text: "text", stepPosition: "stepPosition", stepContent: "stepContent", stepContentParams: "stepContentParams", prevTemplate: "prevTemplate", nextTemplate: "nextTemplate", doneTemplate: "doneTemplate", counterTemplate: "counterTemplate" }, outputs: { prev: "prev", next: "next", done: "done" }, usesOnChanges: true, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideDirective, decorators: [{ type: Directive, args: [{ selector: 'joyrideStep, [joyrideStep]' }] }], ctorParameters: function () { return [{ type: JoyrideStepsContainerService }, { type: i0.ViewContainerRef }, { type: DomRefService }, { type: i3.Router }, { type: TemplatesService }, { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; }, propDecorators: { name: [{ type: Input, args: ['joyrideStep'] }], nextStep: [{ type: Input }], title: [{ type: Input }], text: [{ type: Input }], stepPosition: [{ type: Input }], stepContent: [{ type: Input }], stepContentParams: [{ type: Input }], prevTemplate: [{ type: Input }], nextTemplate: [{ type: Input }], doneTemplate: [{ type: Input }], counterTemplate: [{ type: Input }], prev: [{ type: Output }], next: [{ type: Output }], done: [{ type: Output }] } }); class JoyrideStepInfo { } class DocumentService { constructor(DOMService, platformId) { this.DOMService = DOMService; if (!isPlatformBrowser(platformId)) { return; } this.setDocumentHeight(); var doc = DOMService.getNativeDocument(); if (doc && !doc.elementsFromPoint) { // IE 11 - Edge browsers doc.elementsFromPoint = this.elementsFromPoint.bind(this); } } getElementFixedTop(elementRef) { return elementRef.nativeElement.getBoundingClientRect().top; } getElementFixedLeft(elementRef) { return elementRef.nativeElement.getBoundingClientRect().left; } getElementAbsoluteTop(elementRef) { const scrollOffsets = this.getScrollOffsets(); return (elementRef.nativeElement.getBoundingClientRect().top + scrollOffsets.y); } getElementAbsoluteLeft(elementRef) { const scrollOffsets = this.getScrollOffsets(); return (elementRef.nativeElement.getBoundingClientRect().left + scrollOffsets.x); } setDocumentHeight() { this.documentHeight = this.calculateDocumentHeight(); } getDocumentHeight() { return this.documentHeight; } isParentScrollable(elementRef) { return (this.getFirstScrollableParent(elementRef.nativeElement) !== this.DOMService.getNativeDocument().body); } isElementBeyondOthers(elementRef, isElementFixed, keywordToDiscard) { const x1 = isElementFixed ? this.getElementFixedLeft(elementRef) : this.getElementAbsoluteLeft(elementRef); const y1 = isElementFixed ? this.getElementFixedTop(elementRef) : this.getElementAbsoluteTop(elementRef); const x2 = x1 + elementRef.nativeElement.getBoundingClientRect().width - 1; const y2 = y1 + elementRef.nativeElement.getBoundingClientRect().height - 1; const elements1 = this.DOMService.getNativeDocument().elementsFromPoint(x1, y1); const elements2 = this.DOMService.getNativeDocument().elementsFromPoint(x2, y2); if (elements1.length === 0 && elements2.length === 0) return 1; if (this.getFirstElementWithoutKeyword(elements1, keywordToDiscard) !== elementRef.nativeElement || this.getFirstElementWithoutKeyword(elements2, keywordToDiscard) !== elementRef.nativeElement) { return 2; } return 3; } scrollIntoView(elementRef, isElementFixed) { const firstScrollableParent = this.getFirstScrollableParent(elementRef.nativeElement); const top = isElementFixed ? this.getElementFixedTop(elementRef) : this.getElementAbsoluteTop(elementRef); if (firstScrollableParent !== this.DOMService.getNativeDocument().body) { if (firstScrollableParent.scrollTo) { firstScrollableParent.scrollTo(0, top - 150); } else { // IE 11 - Edge browsers firstScrollableParent.scrollTop = top - 150; } } else { this.DOMService.getNativeWindow().scrollTo(0, top - 150); } } scrollToTheTop(elementRef) { const firstScrollableParent = this.getFirstScrollableParent(elementRef.nativeElement); if (firstScrollableParent !== this.DOMService.getNativeDocument().body) { if (firstScrollableParent.scrollTo) { firstScrollableParent.scrollTo(0, 0); } else { // IE 11 - Edge browsers firstScrollableParent.scrollTop = 0; } } else { this.DOMService.getNativeWindow().scrollTo(0, 0); } } scrollToTheBottom(elementRef) { const firstScrollableParent = this.getFirstScrollableParent(elementRef.nativeElement); if (firstScrollableParent !== this.DOMService.getNativeDocument().body) { if (firstScrollableParent.scrollTo) { firstScrollableParent.scrollTo(0, this.DOMService.getNativeDocument().body.scrollHeight); } else { // IE 11 - Edge browsers firstScrollableParent.scrollTop = firstScrollableParent.scrollHeight - firstScrollableParent.clientHeight; } } else { this.DOMService.getNativeWindow().scrollTo(0, this.DOMService.getNativeDocument().body.scrollHeight); } } getFirstScrollableParent(node) { const regex = /(auto|scroll|overlay)/; const style = (node, prop) => this.DOMService.getNativeWindow() .getComputedStyle(node, null) .getPropertyValue(prop); const scroll = (node) => regex.test(style(node, 'overflow') + style(node, 'overflow-y') + style(node, 'overflow-x')); const scrollparent = (node) => { return !node || node === this.DOMService.getNativeDocument().body ? this.DOMService.getNativeDocument().body : scroll(node) ? node : scrollparent(node.parentNode); }; return scrollparent(node); } calculateDocumentHeight() { const documentRef = this.DOMService.getNativeDocument(); return Math.max(documentRef.body.scrollHeight, documentRef.documentElement.scrollHeight, documentRef.body.offsetHeight, documentRef.documentElement.offsetHeight, documentRef.body.clientHeight, documentRef.documentElement.clientHeight); } getScrollOffsets() { const winReference = this.DOMService.getNativeWindow(); const docReference = this.DOMService.getNativeDocument(); // This works for all browsers except IE versions 8 and before if (winReference.pageXOffset != null) return { x: winReference.pageXOffset, y: winReference.pageYOffset }; // For IE (or any browser) in Standards mode if (docReference.compatMode == 'CSS1Compat') return { x: docReference.documentElement.scrollLeft, y: docReference.documentElement.scrollTop }; // For browsers in Quirks mode return { x: docReference.body.scrollLeft, y: docReference.body.scrollTop }; } elementsFromPoint(x, y) { var parents = []; var parent = void 0; do { const elem = this.DOMService.getNativeDocument().elementFromPoint(x, y); if (elem && parent !== elem) { parent = elem; parents.push(parent); parent.style.pointerEvents = 'none'; } else { parent = false; } } while (parent); parents.forEach(function (parent) { return (parent.style.pointerEvents = 'all'); }); return parents; } getFirstElementWithoutKeyword(elements, keyword) { while (elements[0] && elements[0].classList.toString().includes(keyword)) { elements.shift(); } return elements[0]; } } DocumentService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DocumentService, deps: [{ token: DomRefService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); DocumentService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DocumentService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: DocumentService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: DomRefService }, { type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }]; } }); class JoyrideBackdropService { constructor(documentService, optionsService, rendererFactory) { this.documentService = documentService; this.optionsService = optionsService; this.rendererFactory = rendererFactory; this.lastXScroll = 0; this.lastYScroll = 0; this.setRenderer(); } setRenderer() { this.renderer = this.rendererFactory.createRenderer(null, null); } draw(step) { this.elementRef = step.targetViewContainer; this.targetAbsoluteTop = this.getTargetTotalTop(step); this.targetAbsoluteLeft = this.getTargetTotalLeft(step); this.currentBackdropContainer = this.renderer.createElement('div'); this.renderer.addClass(this.currentBackdropContainer, 'backdrop-container'); this.renderer.setStyle(this.currentBackdropContainer, 'position', 'fixed'); this.renderer.setStyle(this.currentBackdropContainer, 'top', '0px'); this.renderer.setStyle(this.currentBackdropContainer, 'left', '0px'); this.renderer.setStyle(this.currentBackdropContainer, 'width', '100%'); this.renderer.setStyle(this.currentBackdropContainer, 'height', '100%'); this.renderer.setStyle(this.currentBackdropContainer, 'z-index', '1000'); this.renderer.setAttribute(this.currentBackdropContainer, 'id', 'backdrop-' + step.name); this.backdropContent = this.renderer.createElement('div'); this.renderer.addClass(this.backdropContent, 'backdrop-content'); this.renderer.setStyle(this.backdropContent, 'position', 'relative'); this.renderer.setStyle(this.backdropContent, 'height', '100%'); this.renderer.setStyle(this.backdropContent, 'display', 'flex'); this.renderer.setStyle(this.backdropContent, 'flex-direction', 'column'); this.renderer.appendChild(this.currentBackdropContainer, this.backdropContent); this.backdropTop = this.renderer.createElement('div'); this.renderer.addClass(this.backdropTop, 'joyride-backdrop'); this.renderer.addClass(this.backdropTop, 'backdrop-top'); this.renderer.setStyle(this.backdropTop, 'width', '100%'); this.renderer.setStyle(this.backdropTop, 'height', this.targetAbsoluteTop - this.lastYScroll + 'px'); this.renderer.setStyle(this.backdropTop, 'flex-shrink', '0'); this.renderer.setStyle(this.backdropTop, 'background-color', `rgba(${this.optionsService.getBackdropColor()}, 0.7)`); this.renderer.appendChild(this.backdropContent, this.backdropTop); this.backdropMiddleContainer = this.renderer.createElement('div'); this.renderer.addClass(this.backdropMiddleContainer, 'backdrop-middle-container'); this.renderer.setStyle(this.backdropMiddleContainer, 'height', this.elementRef.element.nativeElement.offsetHeight + 'px'); this.renderer.setStyle(this.backdropMiddleContainer, 'width', '100%'); this.renderer.setStyle(this.backdropMiddleContainer, 'flex-shrink', '0'); this.renderer.appendChild(this.backdropContent, this.backdropMiddleContainer); this.backdropMiddleContent = this.renderer.createElement('div'); this.renderer.addClass(this.backdropMiddleContent, 'backdrop-middle-content'); this.renderer.setStyle(this.backdropMiddleContent, 'display', 'flex'); this.renderer.setStyle(this.backdropMiddleContent, 'width', '100%'); this.renderer.setStyle(this.backdropMiddleContent, 'height', '100%'); this.renderer.appendChild(this.backdropMiddleContainer, this.backdropMiddleContent); this.leftBackdrop = this.renderer.createElement('div'); this.renderer.addClass(this.leftBackdrop, 'joyride-backdrop'); this.renderer.addClass(this.leftBackdrop, 'backdrop-left'); this.renderer.setStyle(this.leftBackdrop, 'flex-shrink', '0'); this.renderer.setStyle(this.leftBackdrop, 'width', this.targetAbsoluteLeft - this.lastXScroll + 'px'); this.renderer.setStyle(this.leftBackdrop, 'background-color', `rgba(${this.optionsService.getBackdropColor()}, 0.7)`); this.renderer.appendChild(this.backdropMiddleContent, this.leftBackdrop); this.targetBackdrop = this.renderer.createElement('div'); this.renderer.addClass(this.targetBackdrop, 'backdrop-target'); this.renderer.setStyle(this.targetBackdrop, 'flex-shrink', '0'); this.renderer.setStyle(this.targetBackdrop, 'width', this.elementRef.element.nativeElement.offsetWidth + 'px'); this.renderer.appendChild(this.backdropMiddleContent, this.targetBackdrop); this.rightBackdrop = this.renderer.createElement('div'); this.renderer.addClass(this.rightBackdrop, 'joyride-backdrop'); this.renderer.addClass(this.rightBackdrop, 'backdrop-right'); this.renderer.setStyle(this.rightBackdrop, 'width', '100%'); this.renderer.setStyle(this.rightBackdrop, 'background-color', `rgba(${this.optionsService.getBackdropColor()}, 0.7)`); this.renderer.appendChild(this.backdropMiddleContent, this.rightBackdrop); this.backdropBottom = this.renderer.createElement('div'); this.renderer.addClass(this.backdropBottom, 'joyride-backdrop'); this.renderer.addClass(this.backdropBottom, 'backdrop-bottom'); this.renderer.setStyle(this.backdropBottom, 'width', '100%'); this.renderer.setStyle(this.backdropBottom, 'height', '100%'); this.renderer.setStyle(this.backdropBottom, 'background-color', `rgba(${this.optionsService.getBackdropColor()}, 0.7)`); this.renderer.appendChild(this.backdropContent, this.backdropBottom); this.removeLastBackdrop(); this.drawCurrentBackdrop(); this.lastBackdropContainer = this.currentBackdropContainer; } remove() { this.removeLastBackdrop(); } redrawTarget(step) { this.targetAbsoluteLeft = this.getTargetTotalLeft(step); this.targetAbsoluteTop = this.getTargetTotalTop(step); this.handleVerticalScroll(step); this.handleHorizontalScroll(step); } getTargetTotalTop(step) { let targetVC = step.targetViewContainer; return step.isElementOrAncestorFixed ? this.documentService.getElementFixedTop(targetVC.element) : this.documentService.getElementAbsoluteTop(targetVC.element); } getTargetTotalLeft(step) { let targetVC = step.targetViewContainer; return step.isElementOrAncestorFixed ? this.documentService.getElementFixedLeft(targetVC.element) : this.documentService.getElementAbsoluteLeft(targetVC.element); } redraw(step, scroll) { if (this.lastYScroll !== scroll.scrollY) { this.lastYScroll = scroll.scrollY; if (this.elementRef) { this.handleVerticalScroll(step); } } if (this.lastXScroll !== scroll.scrollX) { this.lastXScroll = scroll.scrollX; if (this.elementRef) { this.handleHorizontalScroll(step); } } } handleHorizontalScroll(step) { let newBackdropLeftWidth = step.isElementOrAncestorFixed ? this.targetAbsoluteLeft : this.targetAbsoluteLeft - this.lastXScroll; if (newBackdropLeftWidth >= 0) { this.renderer.setStyle(this.leftBackdrop, 'width', newBackdropLeftWidth + 'px'); this.renderer.setStyle(this.targetBackdrop, 'width', this.elementRef.element.nativeElement.offsetWidth + 'px'); } else { this.handleTargetPartialWidth(newBackdropLeftWidth); } } handleTargetPartialWidth(newBackdropLeftWidth) { this.renderer.setStyle(this.leftBackdrop, 'width', 0 + 'px'); let visibleTargetWidth = this.elementRef.element.nativeElement.offsetWidth + newBackdropLeftWidth; if (visibleTargetWidth >= 0) { this.renderer.setStyle(this.targetBackdrop, 'width', visibleTargetWidth + 'px'); } else { this.renderer.setStyle(this.targetBackdrop, 'width', 0 + 'px'); } } handleVerticalScroll(step) { let newBackdropTopHeight = step.isElementOrAncestorFixed ? this.targetAbsoluteTop : this.targetAbsoluteTop - this.lastYScroll; if (newBackdropTopHeight >= 0) { this.renderer.setStyle(this.backdropTop, 'height', newBackdropTopHeight + 'px'); this.renderer.setStyle(this.backdropMiddleContainer, 'height', this.elementRef.element.nativeElement.offsetHeight + 'px'); } else { this.handleTargetPartialHeight(newBackdropTopHeight); } } handleTargetPartialHeight(newBackdropTopHeight) { this.renderer.setStyle(this.backdropTop, 'height', 0 + 'px'); let visibleTargetHeight = this.elementRef.element.nativeElement.offsetHeight + newBackdropTopHeight; if (visibleTargetHeight >= 0) { this.renderer.setStyle(this.backdropMiddleContainer, 'height', visibleTargetHeight + 'px'); } else { this.renderer.setStyle(this.backdropMiddleContainer, 'height', 0 + 'px'); } } removeLastBackdrop() { if (this.lastBackdropContainer) { this.renderer.removeChild(document.body, this.lastBackdropContainer); this.lastBackdropContainer = undefined; } } drawCurrentBackdrop() { this.renderer.appendChild(document.body, this.currentBackdropContainer); } } JoyrideBackdropService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideBackdropService, deps: [{ token: DocumentService }, { token: JoyrideOptionsService }, { token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable }); JoyrideBackdropService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideBackdropService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideBackdropService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: DocumentService }, { type: JoyrideOptionsService }, { type: i0.RendererFactory2 }]; } }); class Scroll { } class EventListenerService { constructor(rendererFactory, DOMService) { this.rendererFactory = rendererFactory; this.DOMService = DOMService; this.scrollEvent = new Subject(); this.resizeEvent = new Subject(); this.renderer = rendererFactory.createRenderer(null, null); } startListeningScrollEvents() { this.scrollUnlisten = this.renderer.listen('document', 'scroll', evt => { this.scrollEvent.next({ scrollX: this.DOMService.getNativeWindow().pageXOffset, scrollY: this.DOMService.getNativeWindow().pageYOffset }); }); } startListeningResizeEvents() { this.resizeUnlisten = this.renderer.listen('window', 'resize', evt => { this.resizeEvent.next(evt); }); } stopListeningScrollEvents() { this.scrollUnlisten(); } stopListeningResizeEvents() { this.resizeUnlisten(); } } EventListenerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: EventListenerService, deps: [{ token: i0.RendererFactory2 }, { token: DomRefService }], target: i0.ɵɵFactoryTarget.Injectable }); EventListenerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: EventListenerService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: EventListenerService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i0.RendererFactory2 }, { type: DomRefService }]; } }); class JoyrideArrowComponent { constructor() { this.position = 'top'; } } JoyrideArrowComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideArrowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); JoyrideArrowComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: JoyrideArrowComponent, selector: "joyride-arrow", inputs: { position: "position" }, ngImport: i0, template: "<div [class.joyride-arrow__top]=\"position == 'top'\"\n [class.joyride-arrow__bottom]=\"position == 'bottom'\"\n [class.joyride-arrow__left]=\"position == 'left'\"\n [class.joyride-arrow__right]=\"position == 'right'\">\n</div>", styles: [".joyride-arrow__top{border-left:11px solid transparent;border-right:11px solid transparent;border-bottom:11px solid #ffffff}.joyride-arrow__bottom{border-left:11px solid transparent;border-right:11px solid transparent;border-top:11px solid #ffffff}.joyride-arrow__right{border-left:11px solid #ffffff;border-bottom:11px solid transparent;border-top:11px solid transparent}.joyride-arrow__left{border-right:11px solid #ffffff;border-top:11px solid transparent;border-bottom:11px solid transparent}\n"], encapsulation: i0.ViewEncapsulation.None }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideArrowComponent, decorators: [{ type: Component, args: [{ selector: 'joyride-arrow', encapsulation: ViewEncapsulation.None, template: "<div [class.joyride-arrow__top]=\"position == 'top'\"\n [class.joyride-arrow__bottom]=\"position == 'bottom'\"\n [class.joyride-arrow__left]=\"position == 'left'\"\n [class.joyride-arrow__right]=\"position == 'right'\">\n</div>", styles: [".joyride-arrow__top{border-left:11px solid transparent;border-right:11px solid transparent;border-bottom:11px solid #ffffff}.joyride-arrow__bottom{border-left:11px solid transparent;border-right:11px solid transparent;border-top:11px solid #ffffff}.joyride-arrow__right{border-left:11px solid #ffffff;border-bottom:11px solid transparent;border-top:11px solid transparent}.joyride-arrow__left{border-right:11px solid #ffffff;border-top:11px solid transparent;border-bottom:11px solid transparent}\n"] }] }], propDecorators: { position: [{ type: Input }] } }); class JoyrideCloseButtonComponent { } JoyrideCloseButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideCloseButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); JoyrideCloseButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: JoyrideCloseButtonComponent, selector: "joy-close-button", ngImport: i0, template: `<svg viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg"> <line x1="1" y1="24" x2="24" y2="1" stroke="black" stroke-width="3"/> <line x1="1" y1="1" x2="24" y2="24" stroke="black" stroke-width="3"/> </svg>`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideCloseButtonComponent, decorators: [{ type: Component, args: [{ selector: 'joy-close-button', template: `<svg viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg"> <line x1="1" y1="24" x2="24" y2="1" stroke="black" stroke-width="3"/> <line x1="1" y1="1" x2="24" y2="24" stroke="black" stroke-width="3"/> </svg>` }] }] }); class JoyrideButtonComponent { constructor() { this.clicked = new EventEmitter(); } onClick() { this.clicked.emit(); } } JoyrideButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); JoyrideButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.1.1", type: JoyrideButtonComponent, selector: "joyride-button", inputs: { color: "color" }, outputs: { clicked: "clicked" }, ngImport: i0, template: "<button (mouseleave)=\"hover=false\" (mouseover)=\"hover=true\"\n [ngStyle]=\"{'background-color': hover ? '#fff' : color, \n 'color': hover ? color : '#fff',\n 'border-color' : hover ? color : 'transparent'}\"\n class=\"joyride-button\" (click)=\"onClick()\">\n <ng-content></ng-content>\n</button>", styles: [".joyride-button{text-transform:uppercase;border:2px solid transparent;outline:none;padding:6px 12px;font-size:12px;font-weight:700;color:#fff;background-color:#3b5560;cursor:pointer}.joyride-button:hover{color:#3b5560;border:2px solid #3b5560;background-color:#fff}\n"], directives: [{ type: i10.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.1.1", ngImport: i0, type: JoyrideButtonComponent, decorators: [{ type: Component, args: [{ selector: 'joyride-button', template: "<button (mouseleave)=\"hover=false\" (mouseover)=\"hover=true\"\n [ngStyle]=\"{'background-color': hover ? '#fff' : color, \n 'color': hover ? color : '#fff',\n 'border-color' : hover ? color : 'transparent'}\"\n class=\"joyride-button\" (click)=\"onClick()\">\n <ng-content></ng-content>\n</button>", styles: [".joyride-button{text-transform:uppercase;border:2px solid transparent;outline:none;padding:6px 12px;font-size:12px;font-weight:700;color:#fff;background-color:#3b5560;cursor:pointer}.joyride-button:hover{color:#3b5560;border:2px solid #3b5560;background-color:#fff}\n"] }] }], propDecorators: { color: [{ type: Input }], clicked: [{ type: Output }] } }); const STEP_MIN_WIDTH = 200; const STEP_MAX_WIDTH = 400; const CUSTOM_STEP_MAX_WIDTH_VW = 90; const STEP_HEIGHT = 200; const ASPECT_RATIO = 1.212; const DEFAULT_DISTANCE_FROM_MARGIN_TOP = 2; const DEFAULT_DISTANCE_FROM_MARGIN_LEFT = 2; const DEFAULT_DISTANCE_FROM_MARGIN_BOTTOM = 5; const DEFAULT_DISTANCE_FROM_MARGIN_RIGHT = 5; var KEY_CODE; (function (KEY_CODE) { KEY_CODE[KEY_CODE["RIGHT_ARROW"] = 39] = "RIGHT_ARROW"; KEY_CODE[KEY_CODE["LEFT_ARROW"] = 37] = "LEFT_ARROW"; KEY_CODE[KEY_CODE["ESCAPE_KEY"] = 27] = "ESCAPE_KEY"; })(KEY_CODE || (KEY_CODE = {})); class JoyrideStepComponent { constructor(injector, stepsContainerService, eventListenerService, documentService, renderer, logger, optionsService, templateService) { this.injector = injector; this.stepsContainerService = stepsContainerService; this.eventListenerService = eventListenerService; this.documentService = documentService; this.renderer = renderer; this.logger = logger; this.optionsService = optionsService; this.templateService = templateService; this.stepWidth = STEP_MIN_WIDTH; this.stepHeight = STEP_HEIGHT; this.showArrow = true; this.arrowSize = ARROW_SIZE; this.subscriptions = []; } ngOnInit() { // Need to Inject here otherwise you will obtain a circular dependency this.joyrideStepService = this.injector.get(JoyrideStepService); this.documentHeight = this.documentService.getDocumentHeight(); this.subscriptions.push(this.subscribeToResizeEvents()); this.title = this.step.title.asObservable(); this.text = this.step.text.asObservable(); this.setCustomTemplates(); this.setCustomTexts(); this.counter = this.getCounter(); this.isCounterVisible = this.optionsService.isCounterVisible(); this.isPrevButtonVisible = this.optionsService.isPrevButtonVisible(); this.themeColor = this.optionsService.getThemeColor(); if (this.text) this.text.subscribe(val => this.checkRedraw(val)); if (this.title) this.title.subscribe(val => this.checkRedraw(val)); } ngAfterViewInit() { if (this.isCustomized()) { this.renderer.setStyle(this.stepContainer.nativeElement, 'max-width', CUSTOM_STEP_MAX_WIDTH_VW + 'vw'); this.updateStepDimensions(); } else { this.renderer.setStyle(this.stepContainer.nativeElement, 'max-width', STEP_MAX_WIDTH + 'px'); let dimensions = this.getDimensionsByAspectRatio(this.stepContainer.nativeElement.clientWidth, this.stepContainer.nativeElement.clientHeight, ASPECT_RATIO); dimensions = this.adjustDimensions(dimensions.width, dimensions.height); this.stepWidth = dimensions.width; this.stepHeight = dimensions.height; this.renderer.setStyle(this.stepContainer.nativeElement, 'width', this.stepWidth + 'px'); this.renderer.setStyle(this.stepContainer.nativeElement, 'height', this.stepHeight + 'px'); } this.drawStep(); } checkRedraw(val) { if (val != null) { // Need to wait that the change is rendered before redrawing setTimeout(() => { this.redrawStep(); }, 2); } } isCustomized() { return (this.step.stepContent || this.templateService.getCounter() || this.templateService.getPrevButton() || this.templateService.getNextButton() || this.templateService.getDoneButton()); } setCustomTexts() { const customeTexts = this.optionsService.getCustomTexts(); this.prevText = customeTexts.prev; this.nextText = customeTexts.next; this.doneText = customeTexts.done; } drawStep() { let position = this.step.isElementOrAncestorFixed ? 'fixed' : 'absolute'; this.renderer.setStyle(this.stepHolder.nativeElement, 'position', position); this.renderer.setStyle(this.stepHolder.nativeElement, 'transform', this.step.transformCssStyle); this.targetWidth = this.step.targetViewContainer.element.nativeElemen