diff --git a/src/assets/styles/common-breadcrumbs.scss b/src/assets/styles/common-breadcrumbs.scss index 54ebbac..8a29b26 100644 --- a/src/assets/styles/common-breadcrumbs.scss +++ b/src/assets/styles/common-breadcrumbs.scss @@ -13,29 +13,32 @@ min-width: 16px; } } +.breadcrumbs { + display: flex; + align-items: center; + .breadcrumb { + text-decoration: none; + color: var(--iqser-accent); + font-weight: 600; + width: fit-content; + white-space: nowrap; -.breadcrumb { - text-decoration: none; - color: var(--iqser-accent); - font-weight: 600; - width: fit-content; - white-space: nowrap; + &.back { + display: flex !important; + justify-content: center; + align-items: center; - &.back { - display: flex !important; - justify-content: center; - align-items: center; + mat-icon { + margin-right: 8px; + } + } - mat-icon { - margin-right: 8px; + &:last-child { + @include common-mixins.line-clamp(1); + } + + &.active { + color: var(--iqser-primary); } } - - &:last-child { - @include common-mixins.line-clamp(1); - } - - &.active { - color: var(--iqser-primary); - } } diff --git a/src/assets/styles/common-help-mode.scss b/src/assets/styles/common-help-mode.scss index 9b1bb72..9f7a7b8 100644 --- a/src/assets/styles/common-help-mode.scss +++ b/src/assets/styles/common-help-mode.scss @@ -1,103 +1,12 @@ -.help-mode-on-mouse-over { +.help-mode { z-index: 10; position: absolute; - top: -5px; - left: -5px; - width: 100%; - height: 100%; - padding-right: 5px; - padding-bottom: 10px; transition: all 0.25s; } .help-highlight, -.help-mode-on-mouse-over:hover { +.help-mode:hover { background: rgba(92, 229, 148, 0.5); box-shadow: 0 0 0 2px var(--iqser-helpmode-primary) inset; cursor: help; } - -.help-mode-on-mouse-over-new-dossier-button, -.help-mode-on-mouse-over-filter-dossier-list, -.help-mode-on-mouse-over-filter-document-list, -.help-mode-on-mouse-over-dossiers-quickfilter-my-dossiers, -.help-mode-on-mouse-over-new-dossier { - padding-right: 10px; -} - -.help-mode-on-mouse-over-search-in-entire-application, -.help-mode-on-mouse-over-open-notifications, -.help-mode-on-mouse-over-bulk-select-annotations, -.help-mode-on-mouse-over-assign-reviewer { - padding-left: 4px; -} - -.help-mode-on-mouse-over-edit-dossier-owner { - padding-left: 5px; -} - -.help-mode-on-mouse-over-edit-dossier-member { - margin-left: 3px; -} - -.help-mode-on-mouse-over-open-usermenu { - padding-top: 1px; - margin-left: 5px; -} - -.help-mode-on-mouse-over-standard-view, -.help-mode-on-mouse-over-delta-view, -.help-mode-on-mouse-over-preview-view { - margin-left: 1px; -} - -.help-mode-on-mouse-over-workload-filter { - height: 14px; - width: 50px; - margin-top: 10px; - margin-left: 15px; -} - -.help-mode-on-mouse-over-reset-filters { - padding-left: 4px; -} - -.help-mode-on-mouse-over-dossier-list, -.help-mode-on-mouse-over-document-list { - margin-top: 5px; - height: calc(100% - 70px); -} - -.help-mode-on-mouse-over-dossier-features { - height: 50px; - margin-top: 17px; - width: 95%; -} - -.help-mode-on-mouse-over-edit-dossier-from-list { - padding-left: 5px ; -} - -.help-mode-on-mouse-over-redaction-edit-reason, -.help-mode-on-mouse-over-redaction-remove-only-here, -.help-mode-on-mouse-over-redaction-remove-from-dictionary, -.help-mode-on-mouse-over-redaction-false-positive, -.help-mode-on-mouse-over-recommendation-accept-or-reject { - width: 20px; - height: 20px; - margin-left: 9px; - margin-top: 8px; -} - -.help-mode-on-mouse-over-document-features { - margin-top: 7px; - margin-left: 7px; - height: 20px; - width: 95%; -} - -.help-mode-on-mouse-over-navigate-in-breadcrumbs { - height: 20px; - margin-top: 20px; -} - diff --git a/src/lib/filtering/filters.module.ts b/src/lib/filtering/filters.module.ts index 4796037..21b9f2f 100644 --- a/src/lib/filtering/filters.module.ts +++ b/src/lib/filtering/filters.module.ts @@ -8,9 +8,10 @@ import { PopupFilterComponent } from './popup-filter/popup-filter.component'; import { QuickFiltersComponent } from './quick-filters/quick-filters.component'; import { IqserIconsModule } from '../icons'; import { IqserInputsModule } from '../inputs'; +import { IqserHelpModeModule } from '../help-mode'; const matModules = [MatCheckboxModule, MatMenuModule]; -const modules = [TranslateModule, IqserButtonsModule, IqserIconsModule, IqserInputsModule]; +const modules = [TranslateModule, IqserButtonsModule, IqserIconsModule, IqserInputsModule, IqserHelpModeModule]; const components = [QuickFiltersComponent, PopupFilterComponent]; @NgModule({ diff --git a/src/lib/filtering/models/filter.model.ts b/src/lib/filtering/models/filter.model.ts index c955f26..c44e9f8 100644 --- a/src/lib/filtering/models/filter.model.ts +++ b/src/lib/filtering/models/filter.model.ts @@ -8,4 +8,5 @@ export interface IFilter { readonly checker?: (obj?: unknown) => boolean; readonly required?: boolean; readonly disabled?: boolean; + readonly helpModeKey?: string; } diff --git a/src/lib/filtering/models/nested-filter.ts b/src/lib/filtering/models/nested-filter.ts index 5560c52..e035c82 100644 --- a/src/lib/filtering/models/nested-filter.ts +++ b/src/lib/filtering/models/nested-filter.ts @@ -6,6 +6,7 @@ export class NestedFilter extends Filter implements INestedFilter, IListable { expanded: boolean; indeterminate: boolean; disabled?: boolean; + helpModeKey?: string; readonly children: Filter[]; constructor(nestedFilter: INestedFilter) { @@ -13,6 +14,7 @@ export class NestedFilter extends Filter implements INestedFilter, IListable { this.expanded = !!nestedFilter.expanded; this.indeterminate = !!nestedFilter.indeterminate; this.disabled = !!nestedFilter.disabled; + this.helpModeKey = nestedFilter.helpModeKey; this.children = nestedFilter.children ?? []; } } diff --git a/src/lib/filtering/quick-filters/quick-filters.component.html b/src/lib/filtering/quick-filters/quick-filters.component.html index b35d198..5e5cf12 100644 --- a/src/lib/filtering/quick-filters/quick-filters.component.html +++ b/src/lib/filtering/quick-filters/quick-filters.component.html @@ -5,6 +5,7 @@ [class.active]="filter.checked" [class.disabled]="filter.disabled" class="quick-filter" + [iqserHelpMode]="filter.helpModeKey" > {{ filter.label }} diff --git a/src/lib/help-mode/help-mode.directive.ts b/src/lib/help-mode/help-mode.directive.ts index d8e7e7d..dac74b5 100644 --- a/src/lib/help-mode/help-mode.directive.ts +++ b/src/lib/help-mode/help-mode.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; import { HelpModeService } from './help-mode.service'; import { Router } from '@angular/router'; @@ -8,6 +8,7 @@ import { Router } from '@angular/router'; }) export class HelpModeDirective implements OnInit { @Input('iqserHelpMode') elementName!: string; + @Input() isVirtualScrollElement: boolean = false; private _path: string; constructor( @@ -20,32 +21,24 @@ export class HelpModeDirective implements OnInit { } ngOnInit(): void { - this._createHelperElement(); - } - - private _createHelperElement() { - const elementNameWithId = `${this.elementName}-${this._getRandomId()}`; - const element = this._elementRef.nativeElement as HTMLElement; - - if (!this._isDisabledElement()) { - const helperElement = this._renderer.createElement('div') as HTMLElement; - this._renderer.addClass(helperElement, 'help-mode-on-mouse-over'); - this._renderer.addClass(helperElement, `help-mode-on-mouse-over-${this.elementName}`); - - this._helpModeService.addElement(elementNameWithId, element, helperElement); + if (this.elementName) { + this._createHelperElement(); } } - private _isDisabledElement() { - return this._path === 'dossiers' && this.elementName === 'filter-for-status'; + private _createHelperElement() { + + const element = this._elementRef.nativeElement as HTMLElement; + const helperElementName = `${this.elementName}-${this._generateId()}`; + + const helperElement = this._renderer.createElement('a') as HTMLElement; + 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.isVirtualScrollElement); } - private _getRandomId(): string { - return Math.random().toString(36).substr(2, 9); - } - - - @HostListener('click') onClick(): void { - this._helpModeService.openDocsFor(this.elementName); + private _generateId(): string { + return Math.random().toString(36).substring(2, 9); } } diff --git a/src/lib/help-mode/help-mode.service.ts b/src/lib/help-mode/help-mode.service.ts index 8f22d07..de00234 100644 --- a/src/lib/help-mode/help-mode.service.ts +++ b/src/lib/help-mode/help-mode.service.ts @@ -8,8 +8,12 @@ import { HELP_DOCS, MANUAL_BASE_URL } from './tokens'; interface Helper { readonly element: HTMLElement; readonly helperElement: HTMLElement; + readonly isVirtualScrollElement: boolean; } +const VIRTUAL_SCROLL_ID = 'virtual-scroll'; +const SCROLL_BUTTONS_IDS = ['scroll-up', 'scroll-down']; + @Injectable({ providedIn: 'root', }) @@ -19,7 +23,7 @@ export class HelpModeService { private readonly _helpModeDialogIsOpened$ = new BehaviorSubject(false); readonly helpModeDialogIsOpened$ = this._helpModeDialogIsOpened$.asObservable(); - private readonly _elements: Record = {}; + private readonly _helperElements: Record = {}; private readonly _renderer: Renderer2; constructor( @@ -53,29 +57,31 @@ export class HelpModeService { return ref; } - openDocsFor(elementName: string): void { - if (this.isHelpModeActive) { - window.open(`${this._manualBaseURL}${this._docs[elementName][this._translateService.currentLang]}`); - } + getDocsLink(elementName: string): string { + return this._docs[elementName] ? `${this._manualBaseURL}${this._docs[elementName][this._translateService.currentLang]}` : ''; } activateHelpMode(): void { - this._isHelpModeActive$.next(true); - this.openHelpModeDialog(); - this._enableHelperElements(); + if (!this.isHelpModeActive) { + document.body.style.setProperty('overflow', 'hidden'); + this._isHelpModeActive$.next(true); + this.openHelpModeDialog(); + setTimeout(() => { + this._enableHelperElements(); + }); + } } deactivateHelpMode(): void { - this._isHelpModeActive$.next(false); - this._disableHelperElements(); + if (this.isHelpModeActive) { + document.body.style.removeProperty('overflow'); + this._isHelpModeActive$.next(false); + this._disableHelperElements(); + } } highlightHelperElements(): void { - if (!this.isHelpModeActive) { - return; - } - - Object.values(this._elements).forEach(({ helperElement }) => { + Object.values(this._helperElements).forEach(({ element, helperElement }) => { this._renderer.addClass(helperElement, 'help-highlight'); setTimeout(() => { this._renderer.removeClass(helperElement, 'help-highlight'); @@ -83,21 +89,95 @@ export class HelpModeService { }); } - addElement(elementName: string, element: HTMLElement, helperElement: HTMLElement): void { - this._elements[elementName] = { element, helperElement }; + addElement(helperElementName: string, element: HTMLElement, helperElement: HTMLElement, isVirtualScrollElement: boolean): void { + this._helperElements[helperElementName] = { element, helperElement, isVirtualScrollElement }; + } + + updateHelperElements() { + Object.values(this._helperElements).forEach(({element, helperElement, isVirtualScrollElement }) => { + this._updateHelperElement(element, helperElement, isVirtualScrollElement); + }); + } + + private _isElementVisible(element: HTMLElement, isVirtualScrollElement: boolean): boolean { + + const elementRect = element.getBoundingClientRect(); + if (elementRect.top === 0 && elementRect.left === 0 && elementRect.bottom === 0 && elementRect.bottom === 0) { + return false; + } + + if (isVirtualScrollElement) { + const virtualScroll: any = document.getElementById(VIRTUAL_SCROLL_ID); + + if (!virtualScroll) { + return false; + } + + const virtualScrollRect = virtualScroll.getBoundingClientRect(); + + if (!(elementRect.top > virtualScrollRect.top + && elementRect.left > virtualScrollRect.left + && elementRect.bottom < virtualScrollRect.bottom + && elementRect.right < virtualScrollRect.right)) { + return false; + } + + for (const id of SCROLL_BUTTONS_IDS) { + const scroll: any = document.getElementById(id); + + const 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, isVirtualScrollElement: boolean) { + if (this._isElementVisible(element, isVirtualScrollElement)) { + 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._elements).forEach(({ element, helperElement }) => { - this._renderer.setStyle(element, 'position', 'relative'); - this._renderer.appendChild(element, helperElement); + Object.values(this._helperElements).forEach(({ element, helperElement, isVirtualScrollElement }) => { + document.body.appendChild(helperElement) + this._updateHelperElement(element, helperElement, isVirtualScrollElement); }); } private _disableHelperElements() { - Object.values(this._elements).forEach(({ element, helperElement }) => { - this._renderer.removeStyle(element, 'position'); - this._renderer.removeChild(element, helperElement); + 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, + }; + } } diff --git a/src/lib/help-mode/help-mode/help-mode.component.ts b/src/lib/help-mode/help-mode/help-mode.component.ts index 39c246c..c9a189a 100644 --- a/src/lib/help-mode/help-mode/help-mode.component.ts +++ b/src/lib/help-mode/help-mode/help-mode.component.ts @@ -25,6 +25,14 @@ export class HelpModeComponent { } @HostListener('click') onClick(): void { - this.helpModeService.highlightHelperElements(); + if (this.helpModeService.isHelpModeActive) { + this.helpModeService.highlightHelperElements(); + } + } + + @HostListener('window:resize') onResize(event: any) { + if (this.helpModeService.isHelpModeActive) { + this.helpModeService.updateHelperElements(); + } } } diff --git a/src/lib/listing/page-header/models/button-config.model.ts b/src/lib/listing/page-header/models/button-config.model.ts index a51a41e..67ab4d8 100644 --- a/src/lib/listing/page-header/models/button-config.model.ts +++ b/src/lib/listing/page-header/models/button-config.model.ts @@ -3,5 +3,4 @@ import { ActionConfig } from './action-config.model'; export interface ButtonConfig extends ActionConfig { readonly type?: IconButtonType; - readonly helpModeKey?: string; } diff --git a/src/lib/listing/page-header/page-header.component.html b/src/lib/listing/page-header/page-header.component.html index ccdbc72..e23b73c 100644 --- a/src/lib/listing/page-header/page-header.component.html +++ b/src/lib/listing/page-header/page-header.component.html @@ -1,13 +1,13 @@ diff --git a/src/lib/listing/page-header/page-header.component.ts b/src/lib/listing/page-header/page-header.component.ts index 4e9eb9a..5494a04 100644 --- a/src/lib/listing/page-header/page-header.component.ts +++ b/src/lib/listing/page-header/page-header.component.ts @@ -25,7 +25,7 @@ export class PageHeaderComponent { @Input() viewModeSelection?: TemplateRef; @Input() searchPlaceholder?: string; @Input() searchWidth?: number | 'full'; - @Input() helpModeKey?: 'filter-dossier-list' | 'filter-document-list'; + @Input() helpModeKey?: 'dossier' | 'document'; @Input() searchPosition: SearchPosition = SearchPositions.afterFilters; @Output() readonly closeAction = new EventEmitter(); @@ -53,4 +53,12 @@ export class PageHeaderComponent { trackByLabel(index: number, item: K): string | undefined { return item.label; } + + get filterHelpModeKey() { + return !!this.helpModeKey ? `filter_${this.helpModeKey}_list` : ''; + } + + get resetFiltersHelpModeKey() { + return this.helpModeKey === 'dossier' ? 'reset_filters' : `delete_document_filter`; + } } diff --git a/src/lib/listing/scroll-button/scroll-button.component.html b/src/lib/listing/scroll-button/scroll-button.component.html index b04b249..a0954c3 100644 --- a/src/lib/listing/scroll-button/scroll-button.component.html +++ b/src/lib/listing/scroll-button/scroll-button.component.html @@ -1,7 +1,7 @@ - - diff --git a/src/lib/listing/table-content/table-content.component.html b/src/lib/listing/table-content/table-content.component.html index 7e6ce90..12f79ee 100644 --- a/src/lib/listing/table-content/table-content.component.html +++ b/src/lib/listing/table-content/table-content.component.html @@ -5,6 +5,7 @@ [maxBufferPx]="1500" [minBufferPx]="300" iqserHasScrollbar + id="virtual-scroll" > diff --git a/src/lib/listing/table-header/table-header.component.html b/src/lib/listing/table-header/table-header.component.html index 360352c..06d9dc4 100644 --- a/src/lib/listing/table-header/table-header.component.html +++ b/src/lib/listing/table-header/table-header.component.html @@ -12,7 +12,7 @@ - +