Multi annotation select
This commit is contained in:
parent
e27578ebe6
commit
c9016f0ccc
@ -20,7 +20,7 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.markTextOnlyAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
(action)="annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
*ngIf="annotationPermissions.canMarkTextOnlyAsFalsePositive && !annotationPermissions.canPerformMultipleRemoveActions"
|
||||
tooltipPosition="before"
|
||||
@ -79,61 +79,9 @@
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.suggestRemoveAnnotation($event, [annotation], false, annotationsChanged)"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere && !annotationPermissions.canPerformMultipleRemoveActions"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
*ngIf="annotationPermissions.canPerformMultipleRemoveActions"
|
||||
(action)="openMenu($event)"
|
||||
[class.active]="menuOpen"
|
||||
[matMenuTriggerFor]="menu"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||
<div
|
||||
(click)="annotationActionsService.suggestRemoveAnnotation($event, [annotation], true, annotationsChanged)"
|
||||
mat-menu-item
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||
>
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
|
||||
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||
</div>
|
||||
<div
|
||||
(click)="annotationActionsService.suggestRemoveAnnotation($event, [annotation], false, annotationsChanged)"
|
||||
mat-menu-item
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||
>
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
|
||||
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
mat-menu-item
|
||||
*ngIf="annotationPermissions.canMarkAsFalsePositive"
|
||||
>
|
||||
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||
</div>
|
||||
<div
|
||||
(click)="annotationActionsService.markTextOnlyAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
mat-menu-item
|
||||
*ngIf="annotationPermissions.canMarkTextOnlyAsFalsePositive"
|
||||
>
|
||||
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
<redaction-annotation-remove-actions
|
||||
[annotations]="[annotation]"
|
||||
[(menuOpen)]="menuOpen"
|
||||
[annotationsChanged]="annotationsChanged"
|
||||
></redaction-annotation-remove-actions>
|
||||
</div>
|
||||
|
||||
@ -48,21 +48,4 @@ export class AnnotationActionsComponent implements OnInit {
|
||||
$event.stopPropagation();
|
||||
this.viewer.annotManager.showAnnotation(this.viewerAnnotation);
|
||||
}
|
||||
|
||||
public openMenu($event: MouseEvent) {
|
||||
$event.preventDefault();
|
||||
this.menuOpen = true;
|
||||
}
|
||||
|
||||
public onMenuClosed() {
|
||||
this.menuOpen = false;
|
||||
}
|
||||
|
||||
get suggestionColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion');
|
||||
}
|
||||
|
||||
get dictionaryColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
<redaction-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
*ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere && permissions.canNotPerformMultipleRemoveActions"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
*ngIf="permissions.canPerformMultipleRemoveActions"
|
||||
(action)="openMenu($event)"
|
||||
[class.active]="menuOpen"
|
||||
[matMenuTriggerFor]="menu"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||
<div (click)="suggestRemoveAnnotations($event, true)" mat-menu-item *ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary">
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
|
||||
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||
</div>
|
||||
<div (click)="suggestRemoveAnnotations($event, false)" mat-menu-item *ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere">
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
|
||||
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
||||
</div>
|
||||
|
||||
<div (click)="markAsFalsePositive($event)" mat-menu-item *ngIf="permissions.canMarkAsFalsePositive">
|
||||
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
@ -0,0 +1,98 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { AnnotationPermissions } from '../../../../models/file/annotation.permissions';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { MatMenuTrigger } from '@angular/material/menu';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-remove-actions',
|
||||
templateUrl: './annotation-remove-actions.component.html',
|
||||
styleUrls: ['./annotation-remove-actions.component.scss']
|
||||
})
|
||||
export class AnnotationRemoveActionsComponent implements OnInit {
|
||||
@Output() menuOpenChange = new EventEmitter<boolean>();
|
||||
@Input() annotationsChanged: EventEmitter<AnnotationWrapper>;
|
||||
@Input() menuOpen: boolean;
|
||||
@Input() btnType: 'dark-bg' | 'primary' = 'dark-bg';
|
||||
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
||||
|
||||
@ViewChild(MatMenuTrigger) matMenuTrigger: MatMenuTrigger;
|
||||
public permissions: {
|
||||
canRemoveOrSuggestToRemoveOnlyHere: boolean;
|
||||
canPerformMultipleRemoveActions: boolean;
|
||||
canNotPerformMultipleRemoveActions: boolean;
|
||||
canRemoveOrSuggestToRemoveFromDictionary: boolean;
|
||||
canMarkAsFalsePositive: boolean;
|
||||
};
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
private _annotations: AnnotationWrapper[];
|
||||
|
||||
public get annotations(): AnnotationWrapper[] {
|
||||
return this._annotations;
|
||||
}
|
||||
|
||||
@Input()
|
||||
public set annotations(value: AnnotationWrapper[]) {
|
||||
this._annotations = value.filter((a) => a !== undefined);
|
||||
this._setPermissions();
|
||||
}
|
||||
|
||||
public get dictionaryColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
|
||||
}
|
||||
|
||||
public get suggestionColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion');
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public openMenu($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.matMenuTrigger.openMenu();
|
||||
this.menuOpenChange.emit(true);
|
||||
}
|
||||
|
||||
public onMenuClosed() {
|
||||
this.menuOpenChange.emit(false);
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||
$event.stopPropagation();
|
||||
this._annotationActionsService.suggestRemoveAnnotation($event, this.annotations, removeFromDict, this.annotationsChanged);
|
||||
}
|
||||
|
||||
public markAsFalsePositive($event) {
|
||||
this._annotationActionsService.markAsFalsePositive($event, this.annotations, this.annotationsChanged);
|
||||
}
|
||||
|
||||
private _setPermissions() {
|
||||
this.permissions = {
|
||||
canRemoveOrSuggestToRemoveOnlyHere: this._annotationsPermissions(['canRemoveOrSuggestToRemoveOnlyHere'], true),
|
||||
canPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], true),
|
||||
canNotPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], false),
|
||||
canRemoveOrSuggestToRemoveFromDictionary: this._annotationsPermissions(['canRemoveOrSuggestToRemoveFromDictionary'], true),
|
||||
canMarkAsFalsePositive: this._annotationsPermissions(['canMarkAsFalsePositive', 'canMarkTextOnlyAsFalsePositive'], true)
|
||||
};
|
||||
}
|
||||
|
||||
private _annotationsPermissions(keys: string[], expectedValue: boolean): boolean {
|
||||
return this.annotations.reduce((prevValue, annotation) => {
|
||||
const annotationPermissions = AnnotationPermissions.forUser(
|
||||
this._permissionsService.isManagerAndOwner(),
|
||||
this._permissionsService.currentUser,
|
||||
annotation
|
||||
);
|
||||
const hasAtLeastOnePermission = keys.reduce((acc, key) => acc || annotationPermissions[key] === expectedValue, false);
|
||||
return prevValue && hasAtLeastOnePermission;
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<div *ngIf="!multiSelectActive" class="all-caps-label primary pointer" (click)="multiSelectActive = true">SELECT</div>
|
||||
<redaction-filter
|
||||
(filtersChanged)="filtersChanged($event)"
|
||||
[chevron]="true"
|
||||
@ -10,103 +11,138 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-content">
|
||||
<div
|
||||
#quickNavigation
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="pagesPanelActive"
|
||||
class="quick-navigation"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollFirstEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||
</div>
|
||||
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[viewedPages]="fileData.viewedPages"
|
||||
>
|
||||
</redaction-page-indicator>
|
||||
</div>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollLastEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="scrollQuickNavLast()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-last"></mat-icon>
|
||||
<div *ngIf="multiSelectActive" class="multi-select">
|
||||
<div class="selected-wrapper">
|
||||
<div *ngIf="!selectedAnnotations?.length" class="select-oval always-visible primary-bg"></div>
|
||||
<mat-icon
|
||||
*ngIf="selectedAnnotations?.length"
|
||||
(click)="selectAnnotations.emit()"
|
||||
class="selection-icon"
|
||||
svgIcon="red:radio-indeterminate"
|
||||
></mat-icon>
|
||||
<span class="all-caps-label">{{ selectedAnnotations?.length || 0 }} selected </span>
|
||||
<redaction-annotation-remove-actions
|
||||
*ngIf="selectedAnnotations?.length > 0"
|
||||
[annotations]="selectedAnnotations"
|
||||
[annotationsChanged]="annotationsChanged"
|
||||
btnType="primary"
|
||||
tooltipPosition="above"
|
||||
></redaction-annotation-remove-actions>
|
||||
</div>
|
||||
<redaction-circle-button (action)="multiSelectActive = false" icon="red:close" type="primary"></redaction-circle-button>
|
||||
</div>
|
||||
|
||||
<div style="overflow: hidden; width: 100%;">
|
||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div class="annotations-wrapper" [class.multi-select-active]="multiSelectActive">
|
||||
<div
|
||||
#annotationsElement
|
||||
#quickNavigation
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="!pagesPanelActive"
|
||||
redactionHasScrollbar
|
||||
class="annotations"
|
||||
tabindex="1"
|
||||
[class.active-panel]="pagesPanelActive"
|
||||
class="quick-navigation"
|
||||
tabindex="0"
|
||||
>
|
||||
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
|
||||
{{ 'file-preview.no-annotations-for-page' | translate }}
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollFirstEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||
</div>
|
||||
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[viewedPages]="fileData.viewedPages"
|
||||
[activeSelection]="pageHasSelection(pageNumber)"
|
||||
>
|
||||
</redaction-page-indicator>
|
||||
</div>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollLastEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="scrollQuickNavLast()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-last"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="overflow: hidden; width: 100%;">
|
||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
||||
></span>
|
||||
|
||||
<div *ngIf="multiSelectActive">
|
||||
<div class="all-caps-label primary pointer" (click)="selectAllOnActivePage()">All</div>
|
||||
<div class="all-caps-label primary pointer" (click)="deselectAllOnActivePage()">None</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation)"
|
||||
class="annotation-wrapper"
|
||||
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
|
||||
attr.annotation-id="{{ annotation.id }}"
|
||||
attr.annotation-page="{{ activeViewerPage }}"
|
||||
[class.active]="annotationIsSelected(annotation)"
|
||||
#annotationsElement
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="!pagesPanelActive"
|
||||
redactionHasScrollbar
|
||||
class="annotations"
|
||||
tabindex="1"
|
||||
>
|
||||
<div class="active-marker"></div>
|
||||
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
|
||||
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
|
||||
<div class="details">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
|
||||
{{ 'file-preview.no-annotations-for-page' | translate }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation, $event)"
|
||||
class="annotation-wrapper"
|
||||
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
|
||||
attr.annotation-id="{{ annotation.id }}"
|
||||
attr.annotation-page="{{ activeViewerPage }}"
|
||||
[class.active]="annotationIsSelected(annotation)"
|
||||
[class.multi-select-active]="multiSelectActive"
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
|
||||
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
|
||||
<div class="details">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
||||
<strong
|
||||
><span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.dictionary | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.content && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
||||
</div>
|
||||
{{ annotation.id }}
|
||||
</div>
|
||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
||||
<strong
|
||||
><span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.dictionary | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.content && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
||||
<ng-container
|
||||
*ngIf="!multiSelectActive || !annotationIsSelected(annotation)"
|
||||
[ngTemplateOutlet]="annotationActionsTemplate"
|
||||
[ngTemplateOutletContext]="{ annotation: annotation }"
|
||||
>
|
||||
</ng-container>
|
||||
<div class="active-icon-marker-container">
|
||||
<mat-icon
|
||||
class="active-icon-marker"
|
||||
*ngIf="multiSelectActive && annotationIsSelected(annotation)"
|
||||
svgIcon="red:check"
|
||||
></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container [ngTemplateOutlet]="annotationActionsTemplate" [ngTemplateOutletContext]="{ annotation: annotation }"> </ng-container>
|
||||
|
||||
<!-- <redaction-annotation-actions-->
|
||||
<!-- (annotationsChanged)="annotationsChangedByReviewAction($event)"-->
|
||||
<!-- [annotation]="annotation"-->
|
||||
<!-- [canPerformAnnotationActions]="canPerformAnnotationActions"-->
|
||||
<!-- [viewer]="activeViewer"-->
|
||||
<!-- ></redaction-annotation-actions>-->
|
||||
</div>
|
||||
</redaction-hidden-action>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</redaction-hidden-action>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,11 +2,51 @@
|
||||
@import '../../../../../assets/styles/red-mixins';
|
||||
|
||||
.right-content {
|
||||
flex-direction: column;
|
||||
|
||||
.no-annotations {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.multi-select {
|
||||
min-height: 40px;
|
||||
background: $primary;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 8px 0 16px;
|
||||
color: $white;
|
||||
|
||||
.selected-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.select-oval,
|
||||
.selection-icon {
|
||||
margin-right: 8px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.all-caps-label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
redaction-annotation-remove-actions {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.annotations-wrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
&.multi-select-active {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.quick-navigation,
|
||||
.annotations {
|
||||
overflow-y: scroll;
|
||||
@ -64,7 +104,16 @@
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: $grey-6;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
|
||||
> div:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.annotations {
|
||||
@ -76,13 +125,23 @@
|
||||
display: flex;
|
||||
border-bottom: 1px solid $separator;
|
||||
|
||||
.active-marker {
|
||||
.active-bar-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.active-icon-marker-container {
|
||||
min-width: 20px;
|
||||
|
||||
.active-icon-marker {
|
||||
color: $primary;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.active-marker {
|
||||
&:not(.multi-select-active) .active-bar-marker {
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { debounce } from '../../../../utils/debounce';
|
||||
import { FileDataModel } from '../../../../models/file/file-data.model';
|
||||
import { AnnotationPermissions } from '../../../../models/file/annotation.permissions';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
@ -22,21 +24,23 @@ export class FileWorkloadComponent {
|
||||
@Input()
|
||||
set annotations(value: AnnotationWrapper[]) {
|
||||
this._annotations = value;
|
||||
// this.computeQuickNavButtonsState();
|
||||
}
|
||||
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Input() activeViewerPage: number;
|
||||
@Input() shouldDeselectAnnotationsOnPageChange: boolean;
|
||||
@Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
|
||||
@Input() dialogRef: MatDialogRef<any>;
|
||||
@Input() annotationFilters: FilterModel[];
|
||||
@Input() fileData: FileDataModel;
|
||||
@Input() hideSkipped: boolean;
|
||||
@Input() annotationActionsTemplate: TemplateRef<any>;
|
||||
|
||||
@Output() selectAnnotation = new EventEmitter<AnnotationWrapper>();
|
||||
@Output() selectAnnotations = new EventEmitter<AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }>();
|
||||
@Output() deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||
@Output() selectPage = new EventEmitter<number>();
|
||||
@Output() toggleSkipped = new EventEmitter<any>();
|
||||
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
||||
|
||||
public quickScrollFirstEnabled = false;
|
||||
public quickScrollLastEnabled = false;
|
||||
@ -46,7 +50,27 @@ export class FileWorkloadComponent {
|
||||
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||
|
||||
constructor(private _changeDetectorRef: ChangeDetectorRef, private _annotationProcessingService: AnnotationProcessingService) {}
|
||||
private _multiSelectActive = false;
|
||||
|
||||
public get multiSelectActive(): boolean {
|
||||
return this._multiSelectActive;
|
||||
}
|
||||
|
||||
public set multiSelectActive(value: boolean) {
|
||||
this._multiSelectActive = value;
|
||||
if (!value) {
|
||||
this.selectAnnotations.emit();
|
||||
} else {
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
private _annotationProcessingService: AnnotationProcessingService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
private get firstSelectedAnnotation() {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||
@ -71,6 +95,20 @@ export class FileWorkloadComponent {
|
||||
console.log(annotation);
|
||||
}
|
||||
|
||||
public pageHasSelection(page: number) {
|
||||
return this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page);
|
||||
}
|
||||
|
||||
public selectAllOnActivePage() {
|
||||
this.selectAnnotations.emit(this.displayedAnnotations[this.activeViewerPage].annotations);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public deselectAllOnActivePage() {
|
||||
this.deselectAnnotations.emit(this.displayedAnnotations[this.activeViewerPage].annotations);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
@debounce(0)
|
||||
public filtersChanged(filters: FilterModel[]) {
|
||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this._annotations, filters);
|
||||
@ -88,9 +126,16 @@ export class FileWorkloadComponent {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public annotationClicked(annotation: AnnotationWrapper) {
|
||||
public annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent) {
|
||||
this.pagesPanelActive = false;
|
||||
this.selectAnnotation.emit(annotation);
|
||||
if (this.annotationIsSelected(annotation)) {
|
||||
this.deselectAnnotations.emit([annotation]);
|
||||
} else {
|
||||
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
}
|
||||
this.selectAnnotations.emit({ annotations: [annotation], multiSelect: true });
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
@ -107,15 +152,18 @@ export class FileWorkloadComponent {
|
||||
if ($event.key === 'ArrowRight') {
|
||||
this.pagesPanelActive = false;
|
||||
// if we activated annotationsPanel - select first annotation from this page in case there is no
|
||||
// selected annotation on this page
|
||||
if (!this.pagesPanelActive) {
|
||||
// selected annotation on this page and not in multi select mode
|
||||
if (!this.pagesPanelActive && !this.multiSelectActive) {
|
||||
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pagesPanelActive) {
|
||||
this._navigateAnnotations($event);
|
||||
// Disable annotation navigation in multi select mode => TODO: maybe implement selection on enter?
|
||||
if (!this.multiSelectActive) {
|
||||
this._navigateAnnotations($event);
|
||||
}
|
||||
} else {
|
||||
this._navigatePages($event);
|
||||
}
|
||||
@ -180,7 +228,7 @@ export class FileWorkloadComponent {
|
||||
(!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) &&
|
||||
this.displayedPages.indexOf(this.activeViewerPage) >= 0
|
||||
) {
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,18 +237,20 @@ export class FileWorkloadComponent {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
if (pageIdx !== -1) {
|
||||
// Displayed page has annotations
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
|
||||
} else {
|
||||
// Displayed page doesn't have annotations
|
||||
if ($event.key === 'ArrowDown') {
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[nextPage].annotations[0]);
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([this.displayedAnnotations[nextPage].annotations[0]]);
|
||||
} else {
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||
this.selectAnnotation.emit(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -212,22 +262,24 @@ export class FileWorkloadComponent {
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (idx + 1 !== annotationsOnPage.length) {
|
||||
// If not last item in page
|
||||
this.selectAnnotation.emit(annotationsOnPage[idx + 1]);
|
||||
this.selectAnnotations.emit([annotationsOnPage[idx + 1]]);
|
||||
} else if (pageIdx + 1 < this.displayedPages.length) {
|
||||
// If not last page
|
||||
const nextPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(nextPageAnnotations[0]);
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([nextPageAnnotations[0]]);
|
||||
}
|
||||
} else {
|
||||
if (idx !== 0) {
|
||||
// If not first item in page
|
||||
this.selectAnnotation.emit(annotationsOnPage[idx - 1]);
|
||||
this.selectAnnotations.emit([annotationsOnPage[idx - 1]]);
|
||||
} else if (pageIdx) {
|
||||
// If not first page
|
||||
const prevPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx - 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,4 +10,5 @@
|
||||
<div class="text">
|
||||
{{ number }}
|
||||
</div>
|
||||
<div class="dot" *ngIf="activeSelection"></div>
|
||||
</div>
|
||||
|
||||
@ -41,4 +41,14 @@
|
||||
color: $grey-1;
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
background: $primary;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
@Input() active: boolean;
|
||||
@Input() number: number;
|
||||
@Input() viewedPages: ViewedPages;
|
||||
@Input() activeSelection = false;
|
||||
|
||||
@Output() pageSelected = new EventEmitter<number>();
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
@Input() canPerformActions = false;
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@Input() shouldDeselectAnnotationsOnPageChange = true;
|
||||
@Input() multiSelectActive: boolean;
|
||||
|
||||
@Output() fileReady = new EventEmitter();
|
||||
@Output() annotationSelected = new EventEmitter<string[]>();
|
||||
@ -83,13 +84,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
this._configureTextPopup();
|
||||
|
||||
instance.annotManager.on('annotationSelected', (annotations, action) => {
|
||||
this.annotationSelected.emit(instance.annotManager.getSelectedAnnotations().map((ann) => ann.Id));
|
||||
if (action === 'deselected') {
|
||||
this.annotationSelected.emit([]);
|
||||
this._toggleRectangleAnnotationAction(true);
|
||||
} else {
|
||||
this._configureAnnotationSpecificActions(annotations);
|
||||
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
|
||||
this.annotationSelected.emit(annotations.map((a) => a.Id));
|
||||
// this.annotationSelected.emit(annotations.map((a) => a.Id));
|
||||
}
|
||||
});
|
||||
|
||||
@ -103,7 +104,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
instance.docViewer.on('pageNumberUpdated', (pageNumber) => {
|
||||
if (this.shouldDeselectAnnotationsOnPageChange) {
|
||||
this.instance.annotManager.deselectAllAnnotations();
|
||||
this.deselectAllAnnotations();
|
||||
}
|
||||
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
|
||||
});
|
||||
@ -253,7 +254,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
quadsObject[activePage] = [quad];
|
||||
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
|
||||
// cleanup selection and button state
|
||||
this.instance.annotManager.deselectAllAnnotations();
|
||||
this.deselectAllAnnotations();
|
||||
this.instance.disableElements(['shapeToolGroupButton']);
|
||||
this.instance.enableElements(['shapeToolGroupButton']);
|
||||
// dispatch event
|
||||
@ -381,12 +382,33 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
};
|
||||
}
|
||||
|
||||
public selectAnnotation(annotation: AnnotationWrapper) {
|
||||
public deselectAllAnnotations() {
|
||||
this.instance.annotManager.deselectAllAnnotations();
|
||||
const annotationFromViewer = this.instance.annotManager.getAnnotationById(annotation.id);
|
||||
this.instance.annotManager.selectAnnotation(annotationFromViewer);
|
||||
this.navigateToPage(annotation.pageNumber);
|
||||
this.instance.annotManager.jumpToAnnotation(annotationFromViewer);
|
||||
}
|
||||
|
||||
public selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
|
||||
let annotations: AnnotationWrapper[];
|
||||
let multiSelect: boolean;
|
||||
if ($event instanceof Array) {
|
||||
annotations = $event;
|
||||
multiSelect = false;
|
||||
} else {
|
||||
annotations = $event.annotations;
|
||||
multiSelect = $event.multiSelect;
|
||||
}
|
||||
|
||||
if (!this.multiSelectActive && !multiSelect) {
|
||||
this.deselectAllAnnotations();
|
||||
}
|
||||
|
||||
const annotationsFromViewer = annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id));
|
||||
this.instance.annotManager.selectAnnotations(annotationsFromViewer);
|
||||
this.navigateToPage(annotations[0].pageNumber);
|
||||
this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
|
||||
}
|
||||
|
||||
public deselectAnnotations(annotations: AnnotationWrapper[]) {
|
||||
this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)));
|
||||
}
|
||||
|
||||
public navigateToPage(pageNumber: number) {
|
||||
|
||||
@ -34,6 +34,7 @@ import { PdfViewerDataService } from './services/pdf-viewer-data.service';
|
||||
import { ManualAnnotationService } from './services/manual-annotation.service';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||
import { AnnotationRemoveActionsComponent } from './components/annotation-remove-actions/annotation-remove-actions.component';
|
||||
|
||||
const screens = [ProjectListingScreenComponent, ProjectOverviewScreenComponent, FilePreviewScreenComponent];
|
||||
|
||||
@ -62,6 +63,7 @@ const components = [
|
||||
ProjectListingActionsComponent,
|
||||
DocumentInfoComponent,
|
||||
FileWorkloadComponent,
|
||||
AnnotationRemoveActionsComponent,
|
||||
|
||||
...screens,
|
||||
...dialogs
|
||||
|
||||
@ -205,6 +205,7 @@
|
||||
[fileStatus]="appStateService.activeFile"
|
||||
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
|
||||
[annotations]="annotations"
|
||||
[multiSelectActive]="!!fileWorkloadComponent?.multiSelectActive"
|
||||
></redaction-pdf-viewer>
|
||||
</div>
|
||||
|
||||
@ -229,15 +230,17 @@
|
||||
[annotations]="annotations"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
|
||||
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
|
||||
[dialogRef]="dialogRef"
|
||||
[annotationFilters]="annotationFilters"
|
||||
[fileData]="fileData"
|
||||
[hideSkipped]="hideSkipped"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
(selectAnnotation)="selectAnnotation($event)"
|
||||
(selectAnnotations)="selectAnnotations($event)"
|
||||
(deselectAnnotations)="deselectAnnotations($event)"
|
||||
(selectPage)="selectPage($event)"
|
||||
(toggleSkipped)="toggleSkipped($event)"
|
||||
(annotationsChanged)="annotationsChangedByReviewAction($event)"
|
||||
></redaction-file-workload>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -38,6 +38,15 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 24px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .right-content {
|
||||
@ -45,139 +54,8 @@
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
//
|
||||
// .quick-navigation,
|
||||
// .annotations {
|
||||
// overflow-y: scroll;
|
||||
// outline: none;
|
||||
//
|
||||
// &.active-panel {
|
||||
// background-color: #fafafa;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .quick-navigation {
|
||||
// border-right: 1px solid $separator;
|
||||
// min-width: 61px;
|
||||
// overflow: hidden;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
//
|
||||
// .jump {
|
||||
// min-height: 32px;
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// cursor: pointer;
|
||||
// transition: background-color 0.25s;
|
||||
//
|
||||
// &:not(.disabled):hover {
|
||||
// background-color: $grey-6;
|
||||
// }
|
||||
//
|
||||
// mat-icon {
|
||||
// width: 16px;
|
||||
// height: 16px;
|
||||
// }
|
||||
//
|
||||
// &.disabled {
|
||||
// cursor: default;
|
||||
//
|
||||
// mat-icon {
|
||||
// opacity: 0.3;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .pages {
|
||||
// @include no-scroll-bar();
|
||||
// overflow: auto;
|
||||
// flex: 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .page-separator {
|
||||
// border-bottom: 1px solid $separator;
|
||||
// height: 32px;
|
||||
// box-sizing: border-box;
|
||||
// padding: 0 10px;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// background-color: $grey-6;
|
||||
// }
|
||||
//
|
||||
// .annotations {
|
||||
// overflow: hidden;
|
||||
// width: 100%;
|
||||
// height: calc(100% - 32px);
|
||||
//
|
||||
// .annotation-wrapper {
|
||||
// display: flex;
|
||||
// border-bottom: 1px solid $separator;
|
||||
//
|
||||
// .active-marker {
|
||||
// min-width: 4px;
|
||||
// min-height: 100%;
|
||||
// }
|
||||
//
|
||||
// &.active {
|
||||
// .active-marker {
|
||||
// background-color: $primary;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .annotation {
|
||||
// padding: 10px 21px 10px 6px;
|
||||
// font-size: 11px;
|
||||
// line-height: 14px;
|
||||
// cursor: pointer;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
//
|
||||
// &.removed {
|
||||
// text-decoration: line-through;
|
||||
// color: $grey-7;
|
||||
// }
|
||||
//
|
||||
// .details {
|
||||
// display: flex;
|
||||
// position: relative;
|
||||
// }
|
||||
//
|
||||
// redaction-type-annotation-icon {
|
||||
// margin-top: 6px;
|
||||
// margin-right: 10px;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:hover {
|
||||
// background-color: #f9fafb;
|
||||
//
|
||||
// ::ng-deep .annotation-actions {
|
||||
// display: flex;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:hover {
|
||||
// overflow-y: auto;
|
||||
// @include scroll-bar;
|
||||
// }
|
||||
//
|
||||
// &.has-scrollbar:hover {
|
||||
// .annotation {
|
||||
// padding-right: 10px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
//.no-annotations {
|
||||
// padding: 24px;
|
||||
// text-align: center;
|
||||
//}
|
||||
|
||||
.assign-actions-wrapper {
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
|
||||
@ -65,6 +65,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
|
||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||
@ViewChild(FileWorkloadComponent) public fileWorkloadComponent: FileWorkloadComponent;
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
@ -111,11 +112,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
get canSwitchToRedactedView() {
|
||||
return !this.permissionsService.fileRequiresReanalysis() && !this.fileData.fileStatus.isExcluded;
|
||||
return this.fileData && !this.permissionsService.fileRequiresReanalysis() && !this.fileData.fileStatus.isExcluded;
|
||||
}
|
||||
|
||||
get canSwitchToDeltaView() {
|
||||
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.isExcluded;
|
||||
return this.fileData && this.fileData.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.isExcluded;
|
||||
}
|
||||
|
||||
get displayData() {
|
||||
@ -233,13 +234,26 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
handleAnnotationSelected(annotationIds: string[]) {
|
||||
this.selectedAnnotations = annotationIds.map((annotationId) => this.annotations.find((annotationWrapper) => annotationWrapper.id === annotationId));
|
||||
this.selectedAnnotations = annotationIds
|
||||
.map((annotationId) => this.annotations.find((annotationWrapper) => annotationWrapper.id === annotationId))
|
||||
.filter((ann) => ann !== undefined);
|
||||
if (this.selectedAnnotations.length > 1) {
|
||||
this._workloadComponent.multiSelectActive = true;
|
||||
}
|
||||
this._workloadComponent.scrollToSelectedAnnotation();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
selectAnnotation(annotation: AnnotationWrapper) {
|
||||
this._viewerComponent.selectAnnotation(annotation);
|
||||
selectAnnotations(annotations?: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
|
||||
if (!!annotations) {
|
||||
this._viewerComponent.selectAnnotations(annotations);
|
||||
} else {
|
||||
this._viewerComponent.deselectAllAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
deselectAnnotations(annotations: AnnotationWrapper[]) {
|
||||
this._viewerComponent.deselectAnnotations(annotations);
|
||||
}
|
||||
|
||||
selectPage(pageNumber: number) {
|
||||
@ -298,7 +312,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
viewerPageChanged($event: any) {
|
||||
if (typeof $event === 'number') {
|
||||
this._scrollViews();
|
||||
this.shouldDeselectAnnotationsOnPageChange = true;
|
||||
if (!this.fileWorkloadComponent.multiSelectActive) {
|
||||
this.shouldDeselectAnnotationsOnPageChange = true;
|
||||
}
|
||||
|
||||
// Add current page in URL query params
|
||||
this._router.navigate([], {
|
||||
@ -321,7 +337,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
setTimeout(() => {
|
||||
this.selectPage(parseInt(pageNumber, 10));
|
||||
this._scrollViews();
|
||||
}, 500);
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,7 +397,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
|
||||
/* Close fullscreen */
|
||||
closeFullScreen() {
|
||||
if (document.exitFullscreen) {
|
||||
if (!!document.fullscreenElement && document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +65,10 @@ export class AnnotationActionsService {
|
||||
|
||||
public markAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
annotations.forEach((annotation) => {
|
||||
this._markAsFalsePositive($event, annotation, this._getFalsePositiveText(annotation), annotationsChanged);
|
||||
const permissions = AnnotationPermissions.forUser(this._permissionsService.isManagerAndOwner(), this._permissionsService.currentUser, annotation);
|
||||
const value = permissions.canMarkTextOnlyAsFalsePositive ? annotation.value : this._getFalsePositiveText(annotation);
|
||||
|
||||
this._markAsFalsePositive($event, annotation, value, annotationsChanged);
|
||||
});
|
||||
}
|
||||
|
||||
@ -85,12 +88,6 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
public markTextOnlyAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
annotations.forEach((annotation) => {
|
||||
this._markAsFalsePositive($event, annotation, annotation.value, annotationsChanged);
|
||||
});
|
||||
}
|
||||
|
||||
private _processObsAndEmit(obs: Observable<any>, annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
obs.subscribe(
|
||||
() => {
|
||||
@ -216,21 +213,10 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
const canMarkTextOnlyAsFalsePositive = annotationPermissions.reduce((acc, next) => acc && next.permissions.canMarkTextOnlyAsFalsePositive, true);
|
||||
if (canMarkTextOnlyAsFalsePositive) {
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: '/assets/icons/general/thumb-down.svg',
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.markTextOnlyAsFalsePositive(null, annotations, annotationsChanged);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const canMarkAsFalsePositive = annotationPermissions.reduce((acc, next) => acc && next.permissions.canMarkAsFalsePositive, true);
|
||||
const canMarkAsFalsePositive = annotationPermissions.reduce(
|
||||
(acc, next) => acc && (next.permissions.canMarkAsFalsePositive || next.permissions.canMarkTextOnlyAsFalsePositive),
|
||||
true
|
||||
);
|
||||
if (canMarkAsFalsePositive) {
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
|
||||
@ -422,6 +422,7 @@
|
||||
"label": "Accept Recommendation"
|
||||
},
|
||||
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
|
||||
"suggest-remove-annotations": "Remove or Suggest to remove selected entries",
|
||||
"reject-suggestion": "Reject Suggestion",
|
||||
"remove-annotation": {
|
||||
"suggest-remove-from-dict": "Suggest to remove from dictionary",
|
||||
|
||||
@ -102,6 +102,10 @@ redaction-icon-button {
|
||||
&[aria-expanded='true'] {
|
||||
button {
|
||||
background: rgba($primary, 0.1);
|
||||
|
||||
&.primary {
|
||||
background: $red-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +194,10 @@
|
||||
&.always-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.primary-bg {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.selection-icon {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user