MOBILE-3833 user-tours: Listen to changes on scroll and resize
parent
09c2b89c44
commit
7236dd34c1
|
@ -13,7 +13,17 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { BackButtonEvent } from '@ionic/core';
|
import { BackButtonEvent } from '@ionic/core';
|
||||||
import { AfterViewInit, Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild } from '@angular/core';
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostBinding,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core';
|
||||||
import { CorePromisedValue } from '@classes/promised-value';
|
import { CorePromisedValue } from '@classes/promised-value';
|
||||||
import { CoreUserToursFocusLayout } from '@features/usertours/classes/focus-layout';
|
import { CoreUserToursFocusLayout } from '@features/usertours/classes/focus-layout';
|
||||||
import { CoreUserToursPopoverLayout } from '@features/usertours/classes/popover-layout';
|
import { CoreUserToursPopoverLayout } from '@features/usertours/classes/popover-layout';
|
||||||
|
@ -22,6 +32,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { AngularFrameworkDelegate } from '@singletons';
|
import { AngularFrameworkDelegate } from '@singletons';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreDom } from '@singletons/dom';
|
import { CoreDom } from '@singletons/dom';
|
||||||
|
import { CoreEventObserver } from '@singletons/events';
|
||||||
|
|
||||||
const ANIMATION_DURATION = 200;
|
const ANIMATION_DURATION = 200;
|
||||||
const USER_TOURS_BACK_BUTTON_PRIORITY = 100;
|
const USER_TOURS_BACK_BUTTON_PRIORITY = 100;
|
||||||
|
@ -36,7 +47,7 @@ const USER_TOURS_BACK_BUTTON_PRIORITY = 100;
|
||||||
templateUrl: 'core-user-tours-user-tour.html',
|
templateUrl: 'core-user-tours-user-tour.html',
|
||||||
styleUrls: ['user-tour.scss'],
|
styleUrls: ['user-tour.scss'],
|
||||||
})
|
})
|
||||||
export class CoreUserToursUserTourComponent implements AfterViewInit {
|
export class CoreUserToursUserTourComponent implements AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
@Input() container!: HTMLElement;
|
@Input() container!: HTMLElement;
|
||||||
@Input() id!: string;
|
@Input() id!: string;
|
||||||
|
@ -59,6 +70,9 @@ export class CoreUserToursUserTourComponent implements AfterViewInit {
|
||||||
private wrapperTransform = '';
|
private wrapperTransform = '';
|
||||||
private wrapperElement = new CorePromisedValue<HTMLElement>();
|
private wrapperElement = new CorePromisedValue<HTMLElement>();
|
||||||
private backButtonListener?: (event: BackButtonEvent) => void;
|
private backButtonListener?: (event: BackButtonEvent) => void;
|
||||||
|
protected resizeListener?: CoreEventObserver;
|
||||||
|
protected scrollListener?: EventListener;
|
||||||
|
protected content?: HTMLIonContentElement | null;
|
||||||
|
|
||||||
constructor({ nativeElement: element }: ElementRef<HTMLElement>) {
|
constructor({ nativeElement: element }: ElementRef<HTMLElement>) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
|
@ -100,18 +114,7 @@ export class CoreUserToursUserTourComponent implements AfterViewInit {
|
||||||
|
|
||||||
this.calculateStyles();
|
this.calculateStyles();
|
||||||
|
|
||||||
// Show tour.
|
this.activate();
|
||||||
this.active = true;
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'ionBackButton',
|
|
||||||
this.backButtonListener = ({ detail }) => detail.register(
|
|
||||||
USER_TOURS_BACK_BUTTON_PRIORITY,
|
|
||||||
() => {
|
|
||||||
// Silence back button.
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.playEnterAnimation();
|
await this.playEnterAnimation();
|
||||||
}
|
}
|
||||||
|
@ -125,8 +128,7 @@ export class CoreUserToursUserTourComponent implements AfterViewInit {
|
||||||
await this.playLeaveAnimation();
|
await this.playLeaveAnimation();
|
||||||
await AngularFrameworkDelegate.removeViewFromDom(wrapper, this.tour);
|
await AngularFrameworkDelegate.removeViewFromDom(wrapper, this.tour);
|
||||||
|
|
||||||
this.active = false;
|
this.deactivate();
|
||||||
this.backButtonListener && document.removeEventListener('ionBackButton', this.backButtonListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,11 +141,11 @@ export class CoreUserToursUserTourComponent implements AfterViewInit {
|
||||||
|
|
||||||
if (this.active) {
|
if (this.active) {
|
||||||
await this.playLeaveAnimation();
|
await this.playLeaveAnimation();
|
||||||
await AngularFrameworkDelegate.removeViewFromDom(this.container, this.element);
|
|
||||||
|
|
||||||
this.backButtonListener && document.removeEventListener('ionBackButton', this.backButtonListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await AngularFrameworkDelegate.removeViewFromDom(this.container, this.element);
|
||||||
|
this.deactivate();
|
||||||
|
|
||||||
acknowledge && await CoreUserTours.acknowledge(this.id);
|
acknowledge && await CoreUserTours.acknowledge(this.id);
|
||||||
|
|
||||||
this.afterDismiss.emit();
|
this.afterDismiss.emit();
|
||||||
|
@ -205,4 +207,76 @@ export class CoreUserToursUserTourComponent implements AfterViewInit {
|
||||||
await Promise.all(animations.map(animation => animation?.finished));
|
await Promise.all(animations.map(animation => animation?.finished));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate tour.
|
||||||
|
*/
|
||||||
|
protected activate(): void {
|
||||||
|
if (this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.active = true;
|
||||||
|
|
||||||
|
if (!this.backButtonListener) {
|
||||||
|
document.addEventListener(
|
||||||
|
'ionBackButton',
|
||||||
|
this.backButtonListener = ({ detail }) => detail.register(
|
||||||
|
USER_TOURS_BACK_BUTTON_PRIORITY,
|
||||||
|
() => {
|
||||||
|
// Silence back button.
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.focus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.resizeListener) {
|
||||||
|
this.resizeListener = CoreDom.onWindowResize(() => {
|
||||||
|
this.calculateStyles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.content) {
|
||||||
|
this.content = CoreDom.closest(this.focus, 'ion-content');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.scrollListener && this.content) {
|
||||||
|
this.content.scrollEvents = true;
|
||||||
|
|
||||||
|
this.content.addEventListener('ionScrollEnd', this.scrollListener = (): void => {
|
||||||
|
this.calculateStyles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate tour.
|
||||||
|
*/
|
||||||
|
protected deactivate(): void {
|
||||||
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.active = false;
|
||||||
|
|
||||||
|
this.resizeListener?.off();
|
||||||
|
this.backButtonListener && document.removeEventListener('ionBackButton', this.backButtonListener);
|
||||||
|
this.backButtonListener = undefined;
|
||||||
|
this.resizeListener = undefined;
|
||||||
|
|
||||||
|
if (this.content && this.scrollListener) {
|
||||||
|
this.content.removeEventListener('ionScrollEnd', this.scrollListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,33 @@ export class CoreDom {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a dom closest function piercing the shadow DOM.
|
||||||
|
*
|
||||||
|
* @param node DOM Element.
|
||||||
|
* @param selector Selector to search.
|
||||||
|
* @return Closest ancestor or null if not found.
|
||||||
|
*/
|
||||||
|
static closest<T = HTMLElement>(node: HTMLElement | Node | null, selector: string): T | null {
|
||||||
|
if (!node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node instanceof ShadowRoot) {
|
||||||
|
return CoreDom.closest(node.host, selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node instanceof HTMLElement) {
|
||||||
|
if (node.matches(selector)) {
|
||||||
|
return node as unknown as T;
|
||||||
|
} else {
|
||||||
|
return CoreDom.closest<T>(node.parentNode, selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CoreDom.closest<T>(node.parentNode, selector);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the position of a element relative to another element.
|
* Retrieve the position of a element relative to another element.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue