moved all help mode service constants in a separate file, updated service properties, added logic to use helper elements over the webviewer iframe
This commit is contained in:
parent
484ff6eed5
commit
76ee8afbc2
@ -1,12 +1,13 @@
|
||||
import { Directive, ElementRef, Input, OnInit, Optional, Renderer2 } from '@angular/core';
|
||||
import { HelpModeService, OverlappingElement, ScrollableParentView } from './help-mode.service';
|
||||
import { HELP_MODE_CLASS, OverlappingElement, ScrollableParentView } from './utils/constants';
|
||||
import { Helper, HelpModeService } from './help-mode.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[iqserHelpMode]',
|
||||
exportAs: 'iqserHelpMode',
|
||||
})
|
||||
export class HelpModeDirective implements OnInit {
|
||||
@Input('iqserHelpMode') elementName!: string;
|
||||
@Input('iqserHelpMode') helpModeKey!: string;
|
||||
@Input() scrollableParentView!: ScrollableParentView;
|
||||
@Input() overlappingElements: OverlappingElement[] = [];
|
||||
@Input() dialogElement = false;
|
||||
@ -18,28 +19,29 @@ export class HelpModeDirective implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.elementName && this._helpModeService) {
|
||||
if (this.helpModeKey && this._helpModeService) {
|
||||
this._createHelperElement();
|
||||
}
|
||||
}
|
||||
|
||||
private _createHelperElement() {
|
||||
const element = this._elementRef.nativeElement as HTMLElement;
|
||||
const helperElementName = `${this.elementName}-${this._generateId()}`;
|
||||
const helperKey = `${this.helpModeKey}-${this._generateId()}`;
|
||||
|
||||
const helperElement = this._renderer.createElement('a') as HTMLElement;
|
||||
this._renderer.setAttribute(helperElement, 'href', this._helpModeService.getDocsLink(this.elementName));
|
||||
this._renderer.setAttribute(helperElement, 'href', this._helpModeService.getDocsLink(this.helpModeKey));
|
||||
this._renderer.setAttribute(helperElement, 'target', '_blank');
|
||||
this._renderer.addClass(helperElement, 'help-mode');
|
||||
this._renderer.addClass(helperElement, HELP_MODE_CLASS);
|
||||
|
||||
this._helpModeService.addElement(
|
||||
helperElementName,
|
||||
const helper: Helper = {
|
||||
element,
|
||||
helperElement,
|
||||
this.scrollableParentView,
|
||||
this.overlappingElements,
|
||||
this.dialogElement,
|
||||
);
|
||||
scrollableParentView: this.scrollableParentView,
|
||||
overlappingElements: this.overlappingElements,
|
||||
dialogElement: this.dialogElement,
|
||||
}
|
||||
|
||||
this._helpModeService.addHelper(helperKey, helper);
|
||||
}
|
||||
|
||||
private _generateId(): string {
|
||||
|
||||
@ -5,49 +5,34 @@ import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
||||
import { HelpModeDialogComponent } from './help-mode-dialog/help-mode-dialog.component';
|
||||
import { HELP_DOCS, MANUAL_BASE_URL } from './tokens';
|
||||
import { HelpDocs } from './help-docs';
|
||||
import {
|
||||
ANNOTATIONS_LIST_ID, HELP_HIGHLIGHT_CLASS, HELP_MODE_CLASS, OVERLAPPING_DROPDOWNS_IDS,
|
||||
OverlappingElement, PDF_TRON_IFRAME_ID,
|
||||
SCROLL_BUTTONS_IDS,
|
||||
ScrollableParentView,
|
||||
ScrollableParentViews,
|
||||
VIRTUAL_SCROLL_ID, WEB_VIEWER_ELEMENTS
|
||||
} from './utils/constants';
|
||||
|
||||
const VIRTUAL_SCROLL_ID = 'virtual-scroll';
|
||||
const ANNOTATIONS_LIST_ID = 'annotations-list';
|
||||
const OVERLAPPING_DROPDOWNS_IDS = {
|
||||
USER_MENU: 'user-menu-items',
|
||||
WORKLOAD_FILTER: 'workload-filters',
|
||||
DOCUMENT_INFO: 'document-info',
|
||||
};
|
||||
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;
|
||||
|
||||
export const OverlappingElements = {
|
||||
USER_MENU: 'USER_MENU',
|
||||
WORKLOAD_FILTER: 'WORKLOAD_FILTER',
|
||||
DOCUMENT_INFO: 'DOCUMENT_INFO',
|
||||
} as const;
|
||||
|
||||
export type OverlappingElement = keyof typeof OverlappingElements;
|
||||
|
||||
interface Helper {
|
||||
export interface Helper {
|
||||
readonly element: HTMLElement;
|
||||
readonly helperElement: HTMLElement;
|
||||
readonly scrollableParentView: ScrollableParentView;
|
||||
readonly overlappingElements: OverlappingElement[];
|
||||
readonly scrollableParentView?: ScrollableParentView;
|
||||
readonly overlappingElements?: OverlappingElement[];
|
||||
readonly dialogElement?: boolean;
|
||||
readonly iframeElement?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class HelpModeService {
|
||||
helpButtonKey: string | undefined;
|
||||
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;
|
||||
readonly #isHelpModeActive$ = new BehaviorSubject(false);
|
||||
readonly isHelpModeActive$ = this.#isHelpModeActive$.asObservable();
|
||||
readonly #helpModeDialogIsOpened$ = new BehaviorSubject(false);
|
||||
readonly helpModeDialogIsOpened$ = this.#helpModeDialogIsOpened$.asObservable();
|
||||
readonly #helpers: Record<string, Helper> = {};
|
||||
readonly #renderer: Renderer2;
|
||||
#dialogMode = false;
|
||||
|
||||
constructor(
|
||||
@Inject(HELP_DOCS) private readonly _docs: HelpDocs,
|
||||
@ -56,26 +41,26 @@ export class HelpModeService {
|
||||
private readonly _rendererFactory: RendererFactory2,
|
||||
private readonly _translateService: TranslateService,
|
||||
) {
|
||||
this._renderer = this._rendererFactory.createRenderer(null, null);
|
||||
this.#renderer = this._rendererFactory.createRenderer(null, null);
|
||||
}
|
||||
|
||||
get isHelpModeActive(): boolean {
|
||||
return this._isHelpModeActive$.getValue();
|
||||
return this.#isHelpModeActive$.getValue();
|
||||
}
|
||||
|
||||
get helpModeDialogIsOpened(): boolean {
|
||||
return this._helpModeDialogIsOpened$.getValue();
|
||||
return this.#helpModeDialogIsOpened$.getValue();
|
||||
}
|
||||
|
||||
openHelpModeDialog(): MatDialogRef<HelpModeDialogComponent> {
|
||||
this._helpModeDialogIsOpened$.next(true);
|
||||
this.#helpModeDialogIsOpened$.next(true);
|
||||
|
||||
const ref = this._dialog.open(HelpModeDialogComponent, {
|
||||
width: '600px',
|
||||
});
|
||||
|
||||
firstValueFrom(ref.afterClosed()).then(() => {
|
||||
this._helpModeDialogIsOpened$.next(false);
|
||||
this.#helpModeDialogIsOpened$.next(false);
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
@ -87,11 +72,12 @@ export class HelpModeService {
|
||||
activateHelpMode(dialogMode: boolean = false): void {
|
||||
if (!this.isHelpModeActive) {
|
||||
document.body.style.setProperty('overflow', 'unset');
|
||||
this._isHelpModeActive$.next(true);
|
||||
this.#isHelpModeActive$.next(true);
|
||||
this.openHelpModeDialog();
|
||||
this._dialogMode = dialogMode;
|
||||
this.#dialogMode = dialogMode;
|
||||
setTimeout(() => {
|
||||
this._enableHelperElements();
|
||||
this.#createWebViewerHelpers();
|
||||
this.#enableHelperElements();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
@ -99,56 +85,65 @@ export class HelpModeService {
|
||||
deactivateHelpMode(): void {
|
||||
if (this.isHelpModeActive) {
|
||||
document.body.style.removeProperty('overflow');
|
||||
this._isHelpModeActive$.next(false);
|
||||
this._disableHelperElements();
|
||||
this.#isHelpModeActive$.next(false);
|
||||
this.#disableHelperElements();
|
||||
}
|
||||
}
|
||||
|
||||
highlightHelperElements(): void {
|
||||
Object.values(this._helperElements).forEach(({ helperElement }) => {
|
||||
this._renderer.addClass(helperElement, 'help-highlight');
|
||||
Object.values(this.#helpers).forEach(helper => {
|
||||
this.#renderer.addClass(helper.helperElement, HELP_HIGHLIGHT_CLASS);
|
||||
setTimeout(() => {
|
||||
this._renderer.removeClass(helperElement, 'help-highlight');
|
||||
this.#renderer.removeClass(helper.helperElement, HELP_HIGHLIGHT_CLASS);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
addElement(
|
||||
helperElementName: string,
|
||||
element: HTMLElement,
|
||||
helperElement: HTMLElement,
|
||||
scrollableParentView: ScrollableParentView,
|
||||
overlappingElements: OverlappingElement[],
|
||||
dialogElement: boolean,
|
||||
): void {
|
||||
this._helperElements[helperElementName] = {
|
||||
element,
|
||||
helperElement,
|
||||
scrollableParentView,
|
||||
overlappingElements,
|
||||
dialogElement,
|
||||
};
|
||||
#createWebViewerHelpers(): void {
|
||||
const iframe: HTMLIFrameElement = document.getElementById(PDF_TRON_IFRAME_ID) as HTMLIFrameElement;
|
||||
if (iframe) {
|
||||
WEB_VIEWER_ELEMENTS.forEach(e => {
|
||||
const element: HTMLElement = iframe.contentWindow?.document.querySelector(e.querySelector) as HTMLElement;
|
||||
|
||||
const helperElement: HTMLElement = this.#renderer.createElement('a') as HTMLElement;
|
||||
this.#renderer.setAttribute(helperElement, 'href', this.getDocsLink(e.helpModeKey));
|
||||
this.#renderer.setAttribute(helperElement, 'target', '_blank');
|
||||
this.#renderer.addClass(helperElement, HELP_MODE_CLASS);
|
||||
|
||||
const helper: Helper = {
|
||||
element,
|
||||
helperElement,
|
||||
dialogElement: false,
|
||||
iframeElement: true
|
||||
}
|
||||
|
||||
this.addHelper(e.helpModeKey, helper)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addHelper(helperKey: string, helper: Helper): void {
|
||||
this.#helpers[helperKey] = helper;
|
||||
}
|
||||
|
||||
updateHelperElements() {
|
||||
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView, overlappingElements }) => {
|
||||
this._updateHelperElement(element, helperElement, scrollableParentView, overlappingElements);
|
||||
});
|
||||
Object.values(this.#helpers).forEach(helper => this.#updateHelperElement(helper));
|
||||
}
|
||||
|
||||
private _isElementVisible(
|
||||
element: HTMLElement,
|
||||
scrollableParentView: ScrollableParentView,
|
||||
overlappingElements: OverlappingElement[],
|
||||
): boolean {
|
||||
let elementRect: DOMRect = element.getBoundingClientRect();
|
||||
#isElementVisible(helper: Helper): boolean {
|
||||
if (helper.iframeElement && !this.#isFilePreviewPage()) {
|
||||
return false;
|
||||
}
|
||||
let elementRect: DOMRect = helper.element.getBoundingClientRect();
|
||||
|
||||
if (elementRect.top === 0 && elementRect.left === 0 && elementRect.bottom === 0 && elementRect.bottom === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scrollableParentView) {
|
||||
if (helper.scrollableParentView) {
|
||||
const scrollableElementId =
|
||||
scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL ? VIRTUAL_SCROLL_ID : ANNOTATIONS_LIST_ID;
|
||||
helper.scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL ? VIRTUAL_SCROLL_ID : ANNOTATIONS_LIST_ID;
|
||||
const scrollableElement: HTMLElement = <HTMLElement>document.getElementById(scrollableElementId);
|
||||
|
||||
if (!scrollableElement) {
|
||||
@ -168,11 +163,11 @@ export class HelpModeService {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL) {
|
||||
if (helper.scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL) {
|
||||
for (const id of SCROLL_BUTTONS_IDS) {
|
||||
const scroll: HTMLElement = <HTMLElement>document.getElementById(id);
|
||||
|
||||
elementRect = element.getBoundingClientRect();
|
||||
elementRect = helper.element.getBoundingClientRect();
|
||||
const scrollRect = scroll?.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
@ -187,64 +182,67 @@ export class HelpModeService {
|
||||
}
|
||||
}
|
||||
|
||||
for (const overlappingElementName of overlappingElements) {
|
||||
const overlappingElement = document.getElementById(OVERLAPPING_DROPDOWNS_IDS[overlappingElementName]);
|
||||
if (helper.overlappingElements) {
|
||||
for (const overlappingElementName of helper.overlappingElements) {
|
||||
const overlappingElement = document.getElementById(OVERLAPPING_DROPDOWNS_IDS[overlappingElementName]);
|
||||
|
||||
if (!overlappingElement) {
|
||||
continue;
|
||||
if (!overlappingElement) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const overlappingElementRect = overlappingElement.getBoundingClientRect();
|
||||
|
||||
return !(
|
||||
Math.max(elementRect.left, overlappingElementRect.left) < Math.min(elementRect.right, overlappingElementRect.right) &&
|
||||
Math.max(elementRect.top, overlappingElementRect.top) < Math.min(elementRect.bottom, overlappingElementRect.bottom)
|
||||
);
|
||||
}
|
||||
|
||||
const overlappingElementRect = overlappingElement.getBoundingClientRect();
|
||||
|
||||
return !(
|
||||
Math.max(elementRect.left, overlappingElementRect.left) < Math.min(elementRect.right, overlappingElementRect.right) &&
|
||||
Math.max(elementRect.top, overlappingElementRect.top) < Math.min(elementRect.bottom, overlappingElementRect.bottom)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private _updateHelperElement(
|
||||
element: HTMLElement,
|
||||
helperElement: HTMLElement,
|
||||
scrollableParentView: ScrollableParentView,
|
||||
overlappingElements: OverlappingElement[],
|
||||
) {
|
||||
if (this._isElementVisible(element, scrollableParentView, overlappingElements)) {
|
||||
const dimensions = this._getElementDimensions(element);
|
||||
helperElement.style.cssText = `
|
||||
#updateHelperElement(helper: Helper) {
|
||||
if (this.#isElementVisible(helper)) {
|
||||
const dimensions = this.#getElementDimensions(helper.element);
|
||||
|
||||
if (helper.iframeElement) {
|
||||
const iframe: HTMLIFrameElement = document.getElementById(PDF_TRON_IFRAME_ID) as HTMLIFrameElement;
|
||||
const iframeRect = iframe.getBoundingClientRect();
|
||||
dimensions.y += iframeRect.top;
|
||||
}
|
||||
|
||||
helper.helperElement.style.cssText = `
|
||||
top:${dimensions.y}px;
|
||||
left:${dimensions.x}px;
|
||||
width:${dimensions.width}px;
|
||||
height:${dimensions.height}px;
|
||||
`;
|
||||
helperElement.classList.add('help-mode');
|
||||
helper.helperElement.classList.add(HELP_MODE_CLASS);
|
||||
} else {
|
||||
helperElement.classList.remove('help-mode');
|
||||
helper.helperElement.classList.remove(HELP_MODE_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
private _enableHelperElements() {
|
||||
Object.values(this._helperElements).forEach(
|
||||
({ element, helperElement, scrollableParentView, overlappingElements, dialogElement }) => {
|
||||
if (this._dialogMode === dialogElement) {
|
||||
document.body.appendChild(helperElement);
|
||||
this._updateHelperElement(element, helperElement, scrollableParentView, overlappingElements);
|
||||
#enableHelperElements() {
|
||||
Object.values(this.#helpers).forEach(helper => {
|
||||
if (this.#dialogMode === helper.dialogElement) {
|
||||
document.body.appendChild(helper.helperElement);
|
||||
this.#updateHelperElement(helper);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private _disableHelperElements() {
|
||||
Object.values(this._helperElements).forEach(({ helperElement }) => {
|
||||
if (document.body.contains(helperElement)) {
|
||||
document.body.removeChild(helperElement);
|
||||
#disableHelperElements() {
|
||||
Object.values(this.#helpers).forEach(helper => {
|
||||
if (document.body.contains(helper.helperElement)) {
|
||||
document.body.removeChild(helper.helperElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _getElementDimensions(element: HTMLElement) {
|
||||
#getElementDimensions(element: HTMLElement) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
y: rect.top,
|
||||
@ -253,4 +251,8 @@ export class HelpModeService {
|
||||
width: rect.width,
|
||||
};
|
||||
}
|
||||
|
||||
#isFilePreviewPage() {
|
||||
return window.location.href.includes('/file/');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,3 +6,4 @@ export * from './help-mode.directive';
|
||||
export * from './help-mode/help-mode.component';
|
||||
export * from './help-button/help-button.component';
|
||||
export * from './help-mode-dialog/help-mode-dialog.component';
|
||||
export * from './utils/constants';
|
||||
|
||||
31
src/lib/help-mode/utils/constants.ts
Normal file
31
src/lib/help-mode/utils/constants.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export const VIRTUAL_SCROLL_ID = 'virtual-scroll';
|
||||
export const ANNOTATIONS_LIST_ID = 'annotations-list';
|
||||
export const OVERLAPPING_DROPDOWNS_IDS = {
|
||||
USER_MENU: 'user-menu-items',
|
||||
WORKLOAD_FILTER: 'workload-filters',
|
||||
DOCUMENT_INFO: 'document-info',
|
||||
};
|
||||
export const SCROLL_BUTTONS_IDS = ['scroll-up', 'scroll-down'];
|
||||
export const PDF_TRON_IFRAME_ID = 'webviewer-1';
|
||||
export const WEB_VIEWER_ELEMENTS = [{
|
||||
querySelector: '.HeaderItems',
|
||||
helpModeKey: 'pdf_features'
|
||||
}];
|
||||
|
||||
export const ScrollableParentViews = {
|
||||
VIRTUAL_SCROLL: 'VIRTUAL_SCROLL',
|
||||
ANNOTATIONS_LIST: 'ANNOTATIONS_LIST',
|
||||
} as const;
|
||||
|
||||
export type ScrollableParentView = keyof typeof ScrollableParentViews;
|
||||
|
||||
export const OverlappingElements = {
|
||||
USER_MENU: 'USER_MENU',
|
||||
WORKLOAD_FILTER: 'WORKLOAD_FILTER',
|
||||
DOCUMENT_INFO: 'DOCUMENT_INFO',
|
||||
} as const;
|
||||
|
||||
export type OverlappingElement = keyof typeof OverlappingElements;
|
||||
|
||||
export const HELP_MODE_CLASS = 'help-mode';
|
||||
export const HELP_HIGHLIGHT_CLASS = 'help-highlight';
|
||||
Loading…
x
Reference in New Issue
Block a user