207 lines
7.6 KiB
TypeScript
207 lines
7.6 KiB
TypeScript
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
|
|
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
|
import { HelpModeDialogComponent } from './help-mode-dialog/help-mode-dialog.component';
|
|
import { HELP_DOCS, MANUAL_BASE_URL } from './tokens';
|
|
|
|
const VIRTUAL_SCROLL_ID = 'virtual-scroll';
|
|
const ANNOTATIONS_LIST_ID = 'annotations-list';
|
|
const SCROLL_BUTTONS_IDS = ['scroll-up', 'scroll-down'];
|
|
|
|
export const ScrollableParentViews = {
|
|
VIRTUAL_SCROLL: 'VIRTUAL_SCROLL',
|
|
ANNOTATIONS_LIST: 'ANNOTATIONS_LIST',
|
|
} as const;
|
|
|
|
export type ScrollableParentView = keyof typeof ScrollableParentViews;
|
|
|
|
interface Helper {
|
|
readonly element: HTMLElement;
|
|
readonly helperElement: HTMLElement;
|
|
readonly scrollableParentView: ScrollableParentView;
|
|
readonly dialogElement: boolean;
|
|
}
|
|
|
|
@Injectable({
|
|
providedIn: 'root',
|
|
})
|
|
export class HelpModeService {
|
|
private readonly _isHelpModeActive$ = new BehaviorSubject(false);
|
|
readonly isHelpModeActive$ = this._isHelpModeActive$.asObservable();
|
|
private readonly _helpModeDialogIsOpened$ = new BehaviorSubject(false);
|
|
readonly helpModeDialogIsOpened$ = this._helpModeDialogIsOpened$.asObservable();
|
|
|
|
private readonly _helperElements: Record<string, Helper> = {};
|
|
private readonly _renderer: Renderer2;
|
|
|
|
private _dialogMode = false;
|
|
|
|
constructor(
|
|
@Inject(HELP_DOCS) private readonly _docs: Record<string, Record<string, string>>,
|
|
@Inject(MANUAL_BASE_URL) private readonly _manualBaseURL: string,
|
|
private readonly _dialog: MatDialog,
|
|
private readonly _rendererFactory: RendererFactory2,
|
|
private readonly _translateService: TranslateService,
|
|
) {
|
|
this._renderer = this._rendererFactory.createRenderer(null, null);
|
|
}
|
|
|
|
get isHelpModeActive(): boolean {
|
|
return this._isHelpModeActive$.getValue();
|
|
}
|
|
|
|
get helpModeDialogIsOpened(): boolean {
|
|
return this._helpModeDialogIsOpened$.getValue();
|
|
}
|
|
|
|
openHelpModeDialog(): MatDialogRef<HelpModeDialogComponent> {
|
|
this._helpModeDialogIsOpened$.next(true);
|
|
|
|
const ref = this._dialog.open(HelpModeDialogComponent, {
|
|
width: '600px',
|
|
});
|
|
|
|
firstValueFrom(ref.afterClosed()).then(() => {
|
|
this._helpModeDialogIsOpened$.next(false);
|
|
});
|
|
return ref;
|
|
}
|
|
|
|
getDocsLink(elementName: string): string {
|
|
return this._docs[elementName] ? `${this._manualBaseURL}${this._docs[elementName][this._translateService.currentLang]}` : '';
|
|
}
|
|
|
|
activateHelpMode(dialogMode: boolean = false): void {
|
|
if (!this.isHelpModeActive) {
|
|
document.body.style.setProperty('overflow', 'hidden');
|
|
this._isHelpModeActive$.next(true);
|
|
this.openHelpModeDialog();
|
|
this._dialogMode = dialogMode;
|
|
setTimeout(() => {
|
|
this._enableHelperElements();
|
|
});
|
|
}
|
|
}
|
|
|
|
deactivateHelpMode(): void {
|
|
if (this.isHelpModeActive) {
|
|
document.body.style.removeProperty('overflow');
|
|
this._isHelpModeActive$.next(false);
|
|
this._disableHelperElements();
|
|
}
|
|
}
|
|
|
|
highlightHelperElements(): void {
|
|
Object.values(this._helperElements).forEach(({ element, helperElement }) => {
|
|
this._renderer.addClass(helperElement, 'help-highlight');
|
|
setTimeout(() => {
|
|
this._renderer.removeClass(helperElement, 'help-highlight');
|
|
}, 500);
|
|
});
|
|
}
|
|
|
|
addElement(
|
|
helperElementName: string,
|
|
element: HTMLElement,
|
|
helperElement: HTMLElement,
|
|
scrollableParentView: ScrollableParentView,
|
|
dialogElement: boolean,
|
|
): void {
|
|
this._helperElements[helperElementName] = { element, helperElement, scrollableParentView, dialogElement };
|
|
}
|
|
|
|
updateHelperElements() {
|
|
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView }) => {
|
|
this._updateHelperElement(element, helperElement, scrollableParentView);
|
|
});
|
|
}
|
|
|
|
private _isElementVisible(element: HTMLElement, scrollableParentView: ScrollableParentView): boolean {
|
|
|
|
let elementRect: DOMRect = element.getBoundingClientRect();
|
|
if (elementRect.top === 0 && elementRect.left === 0 && elementRect.bottom === 0 && elementRect.bottom === 0) {
|
|
return false;
|
|
}
|
|
|
|
if (scrollableParentView) {
|
|
const scrollableElementId = scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL ? VIRTUAL_SCROLL_ID : ANNOTATIONS_LIST_ID;
|
|
const scrollableElement: HTMLElement = <HTMLElement>document.getElementById(scrollableElementId);
|
|
|
|
if (!scrollableElement) {
|
|
return false;
|
|
}
|
|
|
|
const scrollableElementRect = scrollableElement.getBoundingClientRect();
|
|
|
|
if (!(elementRect.top > scrollableElementRect.top
|
|
&& elementRect.left > scrollableElementRect.left
|
|
&& elementRect.bottom < scrollableElementRect.bottom
|
|
&& elementRect.right < scrollableElementRect.right)) {
|
|
return false;
|
|
}
|
|
|
|
if (scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL) {
|
|
for (const id of SCROLL_BUTTONS_IDS) {
|
|
const scroll: HTMLElement = <HTMLElement>document.getElementById(id);
|
|
|
|
elementRect = element.getBoundingClientRect();
|
|
const scrollRect = scroll?.getBoundingClientRect();
|
|
|
|
if (elementRect.top + elementRect.height > scrollRect.top
|
|
&& elementRect.left + elementRect.width > scrollRect.left
|
|
&& elementRect.bottom - elementRect.height < scrollRect.bottom
|
|
&& elementRect.right - elementRect.width < scrollRect.right) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private _updateHelperElement(element: HTMLElement, helperElement: HTMLElement, scrollableParentView: ScrollableParentView) {
|
|
if (this._isElementVisible(element, scrollableParentView)) {
|
|
const dimensions = this._getElementDimensions(element);
|
|
helperElement.style.cssText = `
|
|
top:${dimensions.y}px;
|
|
left:${dimensions.x}px;
|
|
width:${dimensions.width}px;
|
|
height:${dimensions.height}px;
|
|
`;
|
|
helperElement.classList.add('help-mode');
|
|
} else {
|
|
helperElement.classList.remove('help-mode');
|
|
}
|
|
|
|
}
|
|
|
|
private _enableHelperElements() {
|
|
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView, dialogElement }) => {
|
|
if (this._dialogMode === dialogElement ) {
|
|
document.body.appendChild(helperElement);
|
|
this._updateHelperElement(element, helperElement, scrollableParentView);
|
|
}
|
|
});
|
|
}
|
|
|
|
private _disableHelperElements() {
|
|
Object.values(this._helperElements).forEach(({ helperElement }) => {
|
|
if (document.body.contains(helperElement)) {
|
|
document.body.removeChild(helperElement);
|
|
}
|
|
});
|
|
}
|
|
|
|
private _getElementDimensions(element: HTMLElement) {
|
|
const rect = element.getBoundingClientRect();
|
|
return {
|
|
y: rect.top,
|
|
x: rect.left,
|
|
height: rect.height,
|
|
width: rect.width,
|
|
};
|
|
}
|
|
}
|