ohayolibs
Version:
Ohayo is a set of essential modules for ohayojp.
155 lines (135 loc) • 3.96 kB
text/typescript
import { Platform } from '@angular/cdk/platform';
import { DOCUMENT } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Inject,
OnDestroy,
Optional,
ViewEncapsulation,
} from '@angular/core';
import { OnboardingConfig, OnboardingItem, OnboardingOpType } from './onboarding.types';
interface OnboardingLightData {
el: HTMLElement;
top: number;
left: number;
width: number;
height: number;
clientHeight: number;
clientWidth: number;
}
export class OnboardingComponent implements OnDestroy {
private time: any;
private prevSelectorEl: HTMLElement;
config: OnboardingConfig;
item: OnboardingItem;
active = 0;
max = 0;
readonly op = new EventEmitter<OnboardingOpType>();
running = false;
get first(): boolean {
return this.active === 0;
}
get last(): boolean {
return this.active === this.max - 1;
}
private _getDoc(): Document {
return this.doc;
}
private _getWin(): Window {
return this._getDoc().defaultView || window;
}
constructor(
private el: ElementRef<HTMLElement>,
private doc: any,
private platform: Platform,
private cdr: ChangeDetectorRef,
) {}
private getLightData(): OnboardingLightData | null {
const doc = this._getDoc();
const win = this._getWin();
const el = doc.querySelector(this.item.selectors) as HTMLElement;
if (!el) {
return null;
}
const scrollTop = win.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop;
const scrollLeft = win.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft;
const rect = el.getBoundingClientRect();
const top = rect.top + scrollTop;
const left = rect.left + scrollLeft;
const padding = 8;
const needPadding = top > padding && left > padding;
const offsetPos = needPadding ? padding : 0;
const offsetWH = needPadding ? padding * 2 : 0;
return {
top: top - offsetPos,
left: left - offsetPos,
width: rect.width + offsetWH,
height: rect.height + offsetWH,
el,
clientWidth: doc.body.clientWidth,
clientHeight: doc.body.clientHeight,
};
}
private scroll(pos: OnboardingLightData): void {
this.prevSelectorEl = pos.el;
const scrollY = pos.top - (pos.clientHeight - pos.height) / 2;
this._getWin().scrollTo({ top: scrollY });
this.updatePrevElStatus(true);
}
updateRunning(status: boolean): void {
this.running = status;
this.cdr.detectChanges();
if (!status) {
this.updatePosition();
}
}
private updatePosition(): void {
if (!this.platform.isBrowser) {
return;
}
const pos = this.getLightData();
if (pos == null) {
console.warn(`Did not matches selectors [${this.item.selectors}]`);
return;
}
const lightStyle = (this.el.nativeElement.querySelector('.onboarding__light') as HTMLElement).style;
lightStyle.top = `${pos.top}px`;
lightStyle.left = `${pos.left}px`;
lightStyle.width = `${pos.width}px`;
lightStyle.height = `${pos.height}px`;
this.updatePrevElStatus(false);
this.scroll(pos);
}
private updatePrevElStatus(status: boolean): void {
if (this.prevSelectorEl) {
this.prevSelectorEl.classList[status ? 'add' : 'remove']('onboarding__light-el');
}
}
to(type: OnboardingOpType): void {
this.op.emit(type);
}
handleMask(): void {
if (this.config.maskClosable === true) {
this.to('done');
}
}
ngOnDestroy(): void {
clearTimeout(this.time);
this.updatePrevElStatus(false);
}
}