Pull request #13: VM/RED-3780

Merge in SL/common-ui from VM/RED-3780 to master

* commit '97757e7e39edd9751b18f713e884629418a6b04e':
  RED-3780 - updated z-index
  RED-3780 - updated help mode to activate highlights also for modal elements
  added separate component for help mode button and updated help mode component to be used also for modals
This commit is contained in:
Valentin-Gabriel Mihai 2022-04-06 21:28:12 +02:00
commit 7da8f7fda7
15 changed files with 112 additions and 42 deletions

View File

@ -34,8 +34,12 @@
display: flex;
> * {
> *:not(iqser-help-button) {
margin-right: 16px;
}
}
.dialog-actions iqser-help-button{
margin-left: auto;
}
}

View File

@ -1,5 +1,5 @@
.help-mode {
z-index: 10;
z-index: 1100;
position: absolute;
transition: all 0.25s;
}

View File

@ -7,9 +7,10 @@ import { ChevronButtonComponent } from './chevron-button/chevron-button.componen
import { CircleButtonComponent } from './circle-button/circle-button.component';
import { IconButtonComponent } from './icon-button/icon-button.component';
import { IqserIconsModule } from '../icons';
import { HelpButtonComponent } from './help-button/help-button.component';
const matModules = [MatButtonModule, MatTooltipModule];
const components = [ChevronButtonComponent, CircleButtonComponent, IconButtonComponent];
const components = [ChevronButtonComponent, CircleButtonComponent, IconButtonComponent, HelpButtonComponent];
@NgModule({
declarations: [...components],

View File

@ -0,0 +1,14 @@
<iqser-icon-button
*ngIf="dialogButton"
(action)="helpModeService.activateHelpMode(true)"
icon="iqser:help-outline"
type="help"
[label]="'help-mode.button-text' | translate"
></iqser-icon-button>
<iqser-circle-button
*ngIf="!dialogButton"
(action)="helpModeService.activateHelpMode()"
icon="iqser:help-outline"
type="help"
[tooltip]="'help-mode.button-text' | translate"
></iqser-circle-button>

View File

@ -0,0 +1,13 @@
import { Component, Input } from '@angular/core';
import { HelpModeService } from '@iqser/common-ui';
@Component({
selector: 'iqser-help-button',
templateUrl: './help-button.component.html',
styleUrls: ['./help-button.component.scss'],
})
export class HelpButtonComponent {
@Input() dialogButton = true;
constructor(readonly helpModeService: HelpModeService) {}
}

View File

@ -4,6 +4,7 @@
[class.overlay]="showDot"
[class.primary]="type === iconButtonTypes.primary"
[class.show-bg]="type === iconButtonTypes.dark"
[class.help]="type === iconButtonTypes.help"
[disabled]="disabled"
mat-button
type="button"

View File

@ -2,6 +2,7 @@ export const IconButtonTypes = {
default: 'default',
dark: 'dark',
primary: 'primary',
help: 'help',
} as const;
export type IconButtonType = keyof typeof IconButtonTypes;

View File

@ -1,16 +1,16 @@
section {
section {
background: #ecedf0;
display: flex;
justify-content: center;
}
.content {
width: 440px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-top: 20px;
padding-bottom: 30px;
line-height: 18px;
}
.content {
width: 440px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-top: 20px;
padding-bottom: 30px;
line-height: 18px;
}
}

View File

@ -1,8 +1,27 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
const HIGHER_CDK_OVERLAY_CONTAINER_ZINDEX = '1200';
const DEFAULT_CDK_OVERLAY_CONTAINER_ZINDEX = '800';
@Component({
templateUrl: './help-mode-dialog.component.html',
styleUrls: ['./help-mode-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelpModeDialogComponent {}
export class HelpModeDialogComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this._setCdkOverlayContainerZindex(HIGHER_CDK_OVERLAY_CONTAINER_ZINDEX);
}
ngOnDestroy(): void {
this._setCdkOverlayContainerZindex(DEFAULT_CDK_OVERLAY_CONTAINER_ZINDEX);
}
private _setCdkOverlayContainerZindex(zIndex: string): void {
const cdkOverlayContainer = document.querySelector<HTMLElement>('.cdk-overlay-container');
if (cdkOverlayContainer) {
cdkOverlayContainer.style.zIndex = zIndex;
}
}
}

View File

@ -9,6 +9,7 @@ import { Router } from '@angular/router';
export class HelpModeDirective implements OnInit {
@Input('iqserHelpMode') elementName!: string;
@Input() scrollableParentView!: ScrollableParentView;
@Input() dialogElement = false;
private _path: string;
constructor(
@ -35,7 +36,8 @@ export class HelpModeDirective implements OnInit {
this._renderer.setAttribute(helperElement, 'href', this._helpModeService.getDocsLink(this.elementName));
this._renderer.setAttribute(helperElement, 'target', '_blank');
this._renderer.addClass(helperElement, 'help-mode');
this._helpModeService.addElement(helperElementName, element, helperElement, this.scrollableParentView);
this._helpModeService.addElement(helperElementName, element, helperElement, this.scrollableParentView, this.dialogElement);
}
private _generateId(): string {

View File

@ -11,7 +11,7 @@ const SCROLL_BUTTONS_IDS = ['scroll-up', 'scroll-down'];
export const ScrollableParentViews = {
VIRTUAL_SCROLL: 'VIRTUAL_SCROLL',
ANNOTATIONS_LIST: 'ANNOTATIONS_LIST'
ANNOTATIONS_LIST: 'ANNOTATIONS_LIST',
} as const;
export type ScrollableParentView = keyof typeof ScrollableParentViews;
@ -20,6 +20,7 @@ interface Helper {
readonly element: HTMLElement;
readonly helperElement: HTMLElement;
readonly scrollableParentView: ScrollableParentView;
readonly dialogElement: boolean;
}
@Injectable({
@ -34,6 +35,8 @@ export class HelpModeService {
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,
@ -69,11 +72,12 @@ export class HelpModeService {
return this._docs[elementName] ? `${this._manualBaseURL}${this._docs[elementName][this._translateService.currentLang]}` : '';
}
activateHelpMode(): void {
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();
});
@ -97,26 +101,32 @@ export class HelpModeService {
});
}
addElement(helperElementName: string, element: HTMLElement, helperElement: HTMLElement, scrollableParentView: ScrollableParentView): void {
this._helperElements[helperElementName] = { element, helperElement, scrollableParentView };
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 }) => {
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView }) => {
this._updateHelperElement(element, helperElement, scrollableParentView);
});
}
private _isElementVisible(element: HTMLElement, scrollableParentView: ScrollableParentView): boolean {
const elementRect = element.getBoundingClientRect();
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: any = document.getElementById(scrollableElementId);
const scrollableElement: HTMLElement = <HTMLElement>document.getElementById(scrollableElementId);
if (!scrollableElement) {
return false;
@ -133,16 +143,16 @@ export class HelpModeService {
if (scrollableParentView === ScrollableParentViews.VIRTUAL_SCROLL) {
for (const id of SCROLL_BUTTONS_IDS) {
const scroll: any = document.getElementById(id);
const scroll: HTMLElement = <HTMLElement>document.getElementById(id);
const elementRect = element.getBoundingClientRect();
const scrollRect = scroll.getBoundingClientRect();
elementRect = element.getBoundingClientRect();
const scrollRect = scroll?.getBoundingClientRect();
if(elementRect.top + elementRect.height > scrollRect.top
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 false;
}
}
}
@ -160,17 +170,19 @@ export class HelpModeService {
width:${dimensions.width}px;
height:${dimensions.height}px;
`;
helperElement.classList.add('help-mode')
helperElement.classList.add('help-mode');
} else {
helperElement.classList.remove('help-mode')
helperElement.classList.remove('help-mode');
}
}
private _enableHelperElements() {
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView }) => {
document.body.appendChild(helperElement)
this._updateHelperElement(element, helperElement, scrollableParentView);
Object.values(this._helperElements).forEach(({ element, helperElement, scrollableParentView, dialogElement }) => {
if (this._dialogMode === dialogElement ) {
document.body.appendChild(helperElement);
this._updateHelperElement(element, helperElement, scrollableParentView);
}
});
}

View File

@ -1,12 +1,12 @@
<div class="help-mode-border" *ngIf="helpModeService.isHelpModeActive$ | async">
<div class="bottom">
<p class="heading">{{ 'help-mode.text' | translate }}</p>
<p class="heading">{{ 'help-mode.bottom-text' | translate }}</p>
<a *ngIf="(helpModeService.helpModeDialogIsOpened$ | async) === false" (click)="helpModeService.openHelpModeDialog()">
{{ 'help-mode.instructions' | translate }}
</a>
<div class="close">
(esc)
<iqser-circle-button icon="iqser:close" (click)="helpModeService.deactivateHelpMode()"></iqser-circle-button>
<iqser-circle-button icon="iqser:close" type="help" (click)="helpModeService.deactivateHelpMode()"></iqser-circle-button>
</div>
</div>
</div>

View File

@ -42,7 +42,7 @@
border-right: 8px solid var(--iqser-helpmode-primary);
border-top: 8px solid var(--iqser-helpmode-primary);
border-bottom: 60px solid var(--iqser-helpmode-primary);
z-index: 5;
z-index: 1000;
position: absolute;
display: flex;
justify-content: center;

View File

@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component, HostListener } from '@angular/core';
import { HelpModeService } from '../help-mode.service';
import { IqserEventTarget } from '../../utils';
import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'iqser-help-mode',
@ -9,10 +10,11 @@ import { IqserEventTarget } from '../../utils';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelpModeComponent {
constructor(readonly helpModeService: HelpModeService) {}
constructor(private readonly _dialog: MatDialog, readonly helpModeService: HelpModeService) {}
@HostListener('document:keydown.escape') onEscKeydownHandler(): void {
if (!this.helpModeService.helpModeDialogIsOpened) {
@HostListener('document:keydown.escape', ['$event']) onEscKeydownHandler(event: KeyboardEvent): void {
if (!this.helpModeService.helpModeDialogIsOpened && this.helpModeService.isHelpModeActive) {
event?.stopPropagation();
this.helpModeService.deactivateHelpMode();
}
}
@ -20,7 +22,8 @@ export class HelpModeComponent {
@HostListener('document:keydown.h', ['$event']) onHKeydownHandler(event: KeyboardEvent): void {
const node = (event.target as IqserEventTarget).localName;
if (!this.helpModeService.isHelpModeActive && node !== 'input' && node !== 'textarea') {
this.helpModeService.activateHelpMode();
const dialogMode = !!this._dialog.openDialogs.length;
this.helpModeService.activateHelpMode(dialogMode);
}
}