move annotations list to a separate component
This commit is contained in:
parent
162d900b9c
commit
ff4cf841e3
@ -60,8 +60,6 @@ export class AnnotationWrapper {
|
||||
|
||||
private _origin: RedactionLogEntryWrapper;
|
||||
|
||||
constructor() {}
|
||||
|
||||
get isChangeLogRemoved() {
|
||||
return this.changeLogType === 'REMOVED';
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
<div
|
||||
(click)="annotationClicked(annotation, $event)"
|
||||
*ngFor="let annotation of annotations"
|
||||
[attr.annotation-id]="annotation.id"
|
||||
[attr.annotation-page]="activeViewerPage"
|
||||
[class.active]="isSelected(annotation.annotationId)"
|
||||
[class.multi-select-active]="multiSelectActive"
|
||||
class="annotation-wrapper"
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
|
||||
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
||||
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation?.type !== 'manual'">
|
||||
<strong>
|
||||
<span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.type | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.shortContent && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-icon-marker-container">
|
||||
<iqser-round-checkbox
|
||||
*ngIf="multiSelectActive && isSelected(annotation.annotationId)"
|
||||
[active]="true"
|
||||
></iqser-round-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-wrapper">
|
||||
<div
|
||||
(click)="comments.toggleExpandComments($event)"
|
||||
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
|
||||
class="comments-counter"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
{{ annotation.comments.length }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="!multiSelectActive" class="actions">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ annotation: annotation }"
|
||||
[ngTemplateOutlet]="annotationActionsTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<redaction-comments #comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,89 @@
|
||||
@use 'variables';
|
||||
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid variables.$separator;
|
||||
|
||||
.active-bar-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.active-icon-marker-container {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.lower-height) .active-bar-marker {
|
||||
background-color: variables.$primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 16px 8px 10px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: variables.$grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actions-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
min-height: 34px;
|
||||
padding-left: 18px;
|
||||
|
||||
.comments-counter {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
transition: background-color 0.2s;
|
||||
line-height: 13px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-8;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { IqserEventTarget } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotations-list',
|
||||
templateUrl: './annotations-list.component.html',
|
||||
styleUrls: ['./annotations-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AnnotationsListComponent {
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Input() annotationActionsTemplate: TemplateRef<unknown>;
|
||||
@Input() multiSelectActive = false;
|
||||
@Input() activeViewerPage: number;
|
||||
@Input() canMultiSelect = true;
|
||||
|
||||
@Output() readonly multiSelectActiveChange = new EventEmitter<boolean>();
|
||||
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
|
||||
@Output() readonly selectAnnotations = new EventEmitter<
|
||||
AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
|
||||
>();
|
||||
@Output() readonly deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
|
||||
if (($event.target as IqserEventTarget).localName === 'input') {
|
||||
return;
|
||||
}
|
||||
this.pagesPanelActive.emit(false);
|
||||
if (this.isSelected(annotation.annotationId)) {
|
||||
this.deselectAnnotations.emit([annotation]);
|
||||
} else {
|
||||
if (this.canMultiSelect && ($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
this.multiSelectActiveChange.emit(true);
|
||||
}
|
||||
this.selectAnnotations.emit({
|
||||
annotations: [annotation],
|
||||
multiSelect: this.multiSelectActive
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isSelected(annotationId: string): boolean {
|
||||
return !!this.selectedAnnotations?.find(a => a?.annotationId === annotationId);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,3 @@
|
||||
<!-- This is a hack to subscribe to an observable using async pipe instead of component class -->
|
||||
<ng-container *ngIf="displayedAnnotations$ | async"></ng-container>
|
||||
|
||||
<div *ngIf="!excludePages" class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<div
|
||||
@ -150,8 +147,7 @@
|
||||
(click)="actionPerformed.emit('view-exclude-pages')"
|
||||
class="with-underline"
|
||||
translate="file-preview.excluded-from-redaction"
|
||||
>
|
||||
</a
|
||||
></a
|
||||
>.
|
||||
</ng-container>
|
||||
</iqser-empty-state>
|
||||
@ -174,62 +170,17 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation, $event)"
|
||||
*ngFor="let annotation of displayedAnnotations.get(activeViewerPage)"
|
||||
[attr.annotation-id]="annotation.id"
|
||||
[attr.annotation-page]="activeViewerPage"
|
||||
[class.active]="isSelected(annotation)"
|
||||
[class.multi-select-active]="multiSelectActive"
|
||||
class="annotation-wrapper"
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
||||
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation?.type !== 'manual'">
|
||||
<strong>
|
||||
<span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.type | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.shortContent && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-icon-marker-container">
|
||||
<iqser-round-checkbox
|
||||
*ngIf="multiSelectActive && isSelected(annotation)"
|
||||
[active]="true"
|
||||
></iqser-round-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-wrapper">
|
||||
<div
|
||||
(click)="toggleExpandComments(annotation, $event)"
|
||||
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
|
||||
class="comments-counter"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
{{ annotation.comments.length }}
|
||||
</div>
|
||||
<div *ngIf="!multiSelectActive" class="actions">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ annotation: annotation }"
|
||||
[ngTemplateOutlet]="annotationActionsTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-annotations-list
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[(multiSelectActive)]="multiSelectActive"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
(pagesPanelActive)="pagesPanelActive = $event"
|
||||
(selectAnnotations)="selectAnnotations.emit($event)"
|
||||
(deselectAnnotations)="deselectAnnotations.emit($event)"
|
||||
></redaction-annotations-list>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@ -157,96 +157,12 @@
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid variables.$separator;
|
||||
|
||||
.active-bar-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.active-icon-marker-container {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.lower-height) .active-bar-marker {
|
||||
background-color: variables.$primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 16px 8px 10px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: variables.$grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actions-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
min-height: 34px;
|
||||
padding-left: 18px;
|
||||
|
||||
.comments-counter {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
transition: background-color 0.2s;
|
||||
line-height: 13px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-8;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover .annotation-wrapper .annotation {
|
||||
&.has-scrollbar:hover::ng-deep .annotation-wrapper .annotation {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,10 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
Output,
|
||||
QueryList,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, IqserEventTarget, NestedFilter } from '@iqser/common-ui';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { CommentsComponent } from '../comments/comments.component';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
@ -56,7 +43,6 @@ export class FileWorkloadComponent {
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
displayedPages: number[] = [];
|
||||
pagesPanelActive = true;
|
||||
@ViewChildren(CommentsComponent) readonly annotationCommentsComponents: QueryList<CommentsComponent>;
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
|
||||
@ -118,15 +104,6 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
}
|
||||
|
||||
isSelected(annotation: AnnotationWrapper) {
|
||||
return this.selectedAnnotations?.find(a => a?.id === annotation.id);
|
||||
}
|
||||
|
||||
toggleExpandComments(annotation: AnnotationWrapper, $event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.annotationCommentsComponents.find(c => c.annotation === annotation).toggleExpandComments();
|
||||
}
|
||||
|
||||
logAnnotation(annotation: AnnotationWrapper) {
|
||||
console.log(annotation);
|
||||
}
|
||||
@ -156,25 +133,6 @@ export class FileWorkloadComponent {
|
||||
return this.displayedAnnotations;
|
||||
}
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
|
||||
if (($event.target as IqserEventTarget).localName === 'input') {
|
||||
return;
|
||||
}
|
||||
this.pagesPanelActive = false;
|
||||
this.logAnnotation(annotation);
|
||||
if (this.isSelected(annotation)) {
|
||||
this.deselectAnnotations.emit([annotation]);
|
||||
} else {
|
||||
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
}
|
||||
this.selectAnnotations.emit({
|
||||
annotations: [annotation],
|
||||
multiSelect: this.multiSelectActive
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent): void {
|
||||
if (
|
||||
|
||||
@ -49,6 +49,7 @@ import { DossiersService } from './services/dossiers.service';
|
||||
import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component';
|
||||
import { SearchScreenComponent } from './screens/search-screen/search-screen.component';
|
||||
import { EditDossierDeletedDocumentsComponent } from './dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component';
|
||||
import { AnnotationsListComponent } from './components/file-workload/components/annotations-list/annotations-list.component';
|
||||
|
||||
const screens = [DossierListingScreenComponent, DossierOverviewScreenComponent, FilePreviewScreenComponent, SearchScreenComponent];
|
||||
|
||||
@ -89,6 +90,7 @@ const components = [
|
||||
PageExclusionComponent,
|
||||
DossierDetailsStatsComponent,
|
||||
EditDossierDeletedDocumentsComponent,
|
||||
AnnotationsListComponent,
|
||||
|
||||
...screens,
|
||||
...dialogs
|
||||
@ -111,4 +113,5 @@ const services = [
|
||||
providers: [...services],
|
||||
imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule]
|
||||
})
|
||||
export class DossiersModule {}
|
||||
export class DossiersModule {
|
||||
}
|
||||
|
||||
@ -16,11 +16,12 @@ export class AnnotationDrawService {
|
||||
) {}
|
||||
|
||||
drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[], hideSkipped = false, compareMode = false) {
|
||||
const annotations = [];
|
||||
annotationWrappers.forEach(annotation => {
|
||||
annotations.push(this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode));
|
||||
});
|
||||
|
||||
if (!activeViewer) {
|
||||
return;
|
||||
}
|
||||
const annotations = annotationWrappers.map(annotation =>
|
||||
this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode)
|
||||
);
|
||||
const annotationManager = activeViewer.Core.annotationManager;
|
||||
annotationManager.addAnnotations(annotations, { imported: true });
|
||||
annotationManager.drawAnnotationsFromList(annotations);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user