Pull request #7: Quick navigation

Merge in RED/ui from quick-navigation to master

* commit 'cf50a38a82d1452a4a0ec23c91f32d689db74d21':
  Quick navigation
  File preview right new layout
This commit is contained in:
Timo Bejan 2020-10-12 21:29:13 +02:00
commit 49dd77cb66
16 changed files with 188 additions and 105 deletions

View File

@ -1,6 +1,6 @@
<div class="rectangle-container"> <div class="rectangle-container" [ngClass]="{ small: small }">
<div *ngFor="let rect of config" [className]="'section-wrapper flex-' + rect.length"> <div *ngFor="let rect of config" [className]="'section-wrapper flex-' + rect.length">
<div *ngIf="rect.title" class="subtitle">{{ rect.title }}</div>
<div [className]="'rectangle ' + rect.color "></div> <div [className]="'rectangle ' + rect.color "></div>
<div *ngIf="rect.title" class="subtitle">{{ rect.title }}</div>
</div> </div>
</div> </div>

View File

@ -5,8 +5,16 @@
display: flex; display: flex;
width: 100%; width: 100%;
.subtitle { &.small {
margin-bottom: 8px; .rectangle {
width: 12px;
}
.section-wrapper {
display: flex;
align-items: center;
gap: 10px;
}
} }
.section-wrapper:first-child { .section-wrapper:first-child {

View File

@ -14,6 +14,9 @@ export class StatusBarComponent implements OnInit {
title?: string, title?: string,
}[] = []; }[] = [];
@Input()
public small = false;
constructor() { constructor() {
} }

View File

@ -29,26 +29,41 @@
</div> </div>
<div class="right-fixed-container"> <div class="right-fixed-container">
<div class="actions-row">
<div>Edit</div>
<div>Delete</div>
<div>View</div>
</div>
<div class="tabs"> <!-- Quick navigation tab-->
<div class="tabs-title-row flex-row"> <div class="vertical" (click)="selectTab('NAVIGATION')" [ngClass]="{ active: navigationTab}">
<div class="tab" <div class="tab-title" [ngClass]="navigationTab ? 'heading' : 'subheading'">
[ngClass]="{ active: selectedTab === 'ANNOTATIONS'}" Quick Navigation
(click)="selectTab('ANNOTATIONS')"> </div>
Annotations
</div> <div *ngIf="navigationTab" class="tab-content">
<div class="tab" <div *ngFor="let item of quickNavigation | sortBy:'asc':'number'"
[ngClass]="{ active: selectedTab === 'INFO'}" class="page-navigation"
(click)="selectTab('INFO')"> [ngClass]="{ active: item.pageNumber === selectedPageNumber }"
Info (click)="selectPage(item.pageNumber)"
>
<div class="page-number">Page {{ item.pageNumber }}</div>
<div class="stats-subtitle subtitle mt-5">
<div *ngIf="item.redactions">
<mat-icon svgIcon="red:files"></mat-icon>
{{item.redactions}}
</div>
<div *ngIf="item.hints">
<mat-icon svgIcon="red:files"></mat-icon>
{{item.hints}}
</div>
</div>
</div> </div>
</div> </div>
<div class="tab-content" #annotationsContainer *ngIf="selectedTab === 'ANNOTATIONS'"> </div>
<!-- Annotations tab-->
<div (click)="selectTab('ANNOTATIONS')" class="vertical" [ngClass]="{ active: annotationsTab }">
<div class="tab-title" [ngClass]="annotationsTab ? 'heading' : 'subheading'">
Annotations
</div>
<div *ngIf="annotationsTab" class="tab-content" #annotationsContainer>
<div *ngFor="let annotation of annotations" <div *ngFor="let annotation of annotations"
class="annotation" [id]="'ann-' + annotation.Id" class="annotation" [id]="'ann-' + annotation.Id"
(click)="selectAnnotation(annotation)" (click)="selectAnnotation(annotation)"
@ -59,19 +74,26 @@
<div>{{annotation.getStatus()}}</div> <div>{{annotation.getStatus()}}</div>
</div> </div>
</div> </div>
<div class="tab-content" *ngIf="selectedTab === 'INFO'"> </div>
<redaction-status-bar
[config]="[{ length: 1, color: 'grey', title: 'Unassigned'}]"
></redaction-status-bar>
<div class="subtitle stats-subtitle mt-20"> <!-- Info tab-->
<div class="vertical" (click)="selectTab('INFO')" [ngClass]="{ active: infoTab}">
<div class="tab-title" [ngClass]="infoTab ? 'heading' : 'subheading'">
Info
</div>
<div *ngIf="infoTab" class="tab-content info-container">
<redaction-status-bar [small]="true"
[config]="[{ length: 1, title: 'Unassigned', color: 'grey'}]"></redaction-status-bar>
<div class="subtitle stats-subtitle mt-5">
<div>645</div> <div>645</div>
<div>9</div> <div>9</div>
</div> </div>
<div class="flex-row mt-20"> <div class="flex-row mt-20">
<redaction-initials-avatar size="large"></redaction-initials-avatar> <redaction-initials-avatar size="large" color="red-white"></redaction-initials-avatar>
<a class="assign-reviewer">+ Assign Reviewer</a> <a class="assign-reviewer">Assign Reviewer</a>
</div> </div>
<div class="subheading mt-20"> <div class="subheading mt-20">
@ -97,7 +119,6 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button (click)="showDetailsDialog($event)" aria-label="details" class="details-button" color="primary" mat-fab> <button (click)="showDetailsDialog($event)" aria-label="details" class="details-button" color="primary" mat-fab>
<mat-icon svgIcon="red:info"></mat-icon> <mat-icon svgIcon="red:info"></mat-icon>

View File

@ -14,56 +14,64 @@ redaction-pdf-viewer {
.right-fixed-container { .right-fixed-container {
padding: 0; padding: 0;
width: calc(#{$right-container-width} - 1px); width: calc(#{$right-container-width} - 1px);
display: flex;
.tabs-title-row { .vertical {
border-bottom: 1px solid rgba(226,228,233,0.9); height: 100%;
box-sizing: border-box; border-right: 1px solid $separator;
height: 45px;
.tab { &.active {
font-size: 13px; width: calc(#{$right-container-width} - 80px);
font-weight: 600; padding-top: 0;
line-height: 18px;
padding: 12px;
cursor: pointer;
border-bottom: 3px solid transparent;
&:first-child { .tab-title {
margin-left: 2px; height: 70px;
display: flex;
border-bottom: 1px solid $separator;
align-items: center;
padding: 0 25px;
} }
&.active { .tab-content {
color: $red-1; overflow-y: scroll;
border-bottom: 3px solid $red-1; overflow-x: hidden;
height: calc(100vh - 110px - 73px);
box-sizing: border-box;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
&::-webkit-scrollbar {
width: 0;
background: transparent; /* Chrome/Safari/Webkit */
}
}
.info-container {
padding: 15px;
} }
} }
}
.actions-row { &:not(.active) {
margin: $right-container-padding $right-container-padding 0; width: 40px;
} padding-top: 15px;
cursor: pointer;
.tab-content { .tab-title {
padding: $right-container-padding; transform: translateX(27px) rotate(90deg);
overflow-y: scroll; transform-origin: 0 0;
overflow-x: hidden; position: absolute;
height: calc(100vh - 110px - 40px - 45px - 3*#{$right-container-padding}); }
} }
.stats-subtitle {
font-size: 13px;
line-height: 16px;
} }
.assign-reviewer { .assign-reviewer {
font-size: 13px; font-size: 13px;
line-height: 16px; line-height: 16px;
margin-left: 12px; margin-left: 12px;
} }
.annotation { .annotation {
border: 1px solid $grey-2; border-bottom: 1px solid $separator;
padding: 14px; padding: 14px;
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
@ -72,4 +80,25 @@ redaction-pdf-viewer {
border-left: 2px solid $red-1; border-left: 2px solid $red-1;
} }
} }
.page-navigation {
cursor: pointer;
border-bottom: 1px solid $separator;
padding: 14px;
border-left: 4px solid transparent;
&:hover {
background-color: #F4F5F7;
}
.page-number {
font-size: 13px;
line-height: 18px;
font-weight: 600;
}
&.active {
border-left: 4px solid $red-1;
}
}
} }

View File

@ -22,6 +22,7 @@ export class FilePreviewScreenComponent implements OnInit {
private _readyViewers: string[] = []; private _readyViewers: string[] = [];
private _clickedAnnotationInSidebar = false; private _clickedAnnotationInSidebar = false;
private projectId: string; private projectId: string;
private _selectedTab: 'NAVIGATION' | 'ANNOTATIONS' | 'INFO' = 'NAVIGATION';
@ViewChild(PdfViewerComponent) @ViewChild(PdfViewerComponent)
private _viewerComponent: PdfViewerComponent; private _viewerComponent: PdfViewerComponent;
@ -31,8 +32,9 @@ export class FilePreviewScreenComponent implements OnInit {
public fileId: string; public fileId: string;
public annotations: Annotations.Annotation[] = []; public annotations: Annotations.Annotation[] = [];
public selectedTab: 'ANNOTATIONS' | 'INFO' = 'ANNOTATIONS';
public selectedAnnotation: Annotations.Annotation; public selectedAnnotation: Annotations.Annotation;
public selectedPageNumber: number;
public quickNavigation: { pageNumber: number, redactions: number, hints: number }[] = [];
constructor( constructor(
public readonly appStateService: AppStateService, public readonly appStateService: AppStateService,
@ -53,11 +55,11 @@ export class FilePreviewScreenComponent implements OnInit {
}); });
} }
ngOnInit(): void { public ngOnInit(): void {
this._viewerSyncService.activateViewer('ANNOTATED'); this._viewerSyncService.activateViewer('ANNOTATED');
} }
showDetailsDialog($event: MouseEvent) { public showDetailsDialog($event: MouseEvent) {
$event.stopPropagation(); $event.stopPropagation();
this._dialog.open(FileDetailsDialogComponent, { this._dialog.open(FileDetailsDialogComponent, {
width: '600px', width: '600px',
@ -66,32 +68,41 @@ export class FilePreviewScreenComponent implements OnInit {
}); });
} }
fileReady(viewer: string) { public fileReady(viewer: string) {
this._readyViewers.push(viewer); this._readyViewers.push(viewer);
this._changeDetectorRef.detectChanges(); this._changeDetectorRef.detectChanges();
} }
get viewReady() { public get viewReady() {
return this._readyViewers.length >= 2; return this._readyViewers.length >= 2;
} }
get activeViewer() { public get activeViewer() {
return this._viewerSyncService.activeViewer; return this._viewerSyncService.activeViewer;
} }
activateViewer(value: string) { public activateViewer(value: string) {
this._viewerSyncService.activateViewer(value); this._viewerSyncService.activateViewer(value);
} }
selectTab(value: 'ANNOTATIONS' | 'INFO') { public selectTab(value: 'ANNOTATIONS' | 'INFO' | 'NAVIGATION') {
this.selectedTab = value; this._selectedTab = value;
} }
handleAnnotationsAdded(annotations: Annotations.Annotation[]) { public handleAnnotationsAdded(annotations: Annotations.Annotation[]) {
this._changeDetectorRef.detectChanges(); this._changeDetectorRef.detectChanges();
for (const annotation of annotations) { for (const annotation of annotations) {
if (annotation.Id.indexOf(':') >= 0) { if (annotation.Id.indexOf(':') >= 0) {
this.annotations.push(annotation); this.annotations.push(annotation);
const pageNumber = annotation.getPageNumber();
console.log({pageNumber})
let el = this.quickNavigation.find((page) => page.pageNumber === pageNumber);
if (!el) {
el = { pageNumber, redactions: 0, hints: 0 }
this.quickNavigation.push(el);
}
if (annotation.Id.startsWith('hint:')) { el.hints++; }
if (annotation.Id.startsWith('redaction:')) { el.redactions++; }
} }
} }
this.annotations = AnnotationUtils.sortAnnotations(this.annotations); this.annotations = AnnotationUtils.sortAnnotations(this.annotations);
@ -129,4 +140,21 @@ export class FilePreviewScreenComponent implements OnInit {
this._annotationsContainer.nativeElement.scroll({ top: scrollTop + top - headerHeight, behavior: 'smooth' }); this._annotationsContainer.nativeElement.scroll({ top: scrollTop + top - headerHeight, behavior: 'smooth' });
} }
} }
public get navigationTab() {
return this._selectedTab === 'NAVIGATION';
}
public get annotationsTab() {
return this._selectedTab === 'ANNOTATIONS';
}
public get infoTab() {
return this._selectedTab === 'INFO';
}
public selectPage(pageNumber: number) {
this.selectedPageNumber = pageNumber;
this._viewerComponent.navigateToPage(pageNumber);
}
} }

View File

@ -94,7 +94,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
} }
})); }));
instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler) instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler);
instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'}); instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'});
}); });
} }
@ -162,5 +162,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
0, 0,
annotation.getY() - 100); annotation.getY() - 100);
} }
public navigateToPage(pageNumber: number) {
this.wvInstance.docViewer.displayPageLocation(pageNumber, 0, 0);
}
} }

View File

@ -2,12 +2,6 @@
.stats-subtitle { .stats-subtitle {
margin-top: 6px; margin-top: 6px;
mat-icon {
width: 12px;
height: 12px;
margin-right: 4px;
}
} }
.stats-bar { .stats-bar {

View File

@ -29,10 +29,6 @@
</div> </div>
</div> </div>
<div class="table-header">
<redaction-status-bar [config]="[{ length: 2, color: 'yellow', title: 'text 1'}, { length: 1, color: 'green', title: 'text 2'}]"></redaction-status-bar>
</div>
<div class="table-col-names"> <div class="table-col-names">
<div class="flex-3 subtitle min-width" translate="project-overview.table-col-names.name.label"></div> <div class="flex-3 subtitle min-width" translate="project-overview.table-col-names.name.label"></div>
<div class="flex-2 subtitle min-width" translate="project-overview.table-col-names.added-on.label"></div> <div class="flex-2 subtitle min-width" translate="project-overview.table-col-names.added-on.label"></div>
@ -120,7 +116,7 @@
<div class="project-team mt-20"> <div class="project-team mt-20">
<div class="subheading" translate="project-overview.project-details.project-team.label"></div> <div class="subheading" translate="project-overview.project-details.project-team.label"></div>
<div class="flex mt-20"> <div class="flex mt-20 members-container">
<div *ngFor="let username of ['S H', 'D O', 'E G', 'D V', 'J A', 'T H', 'P B']" class="member"> <div *ngFor="let username of ['S H', 'D O', 'E G', 'D V', 'J A', 'T H', 'P B']" class="member">
<redaction-initials-avatar [username]="username" size="large"></redaction-initials-avatar> <redaction-initials-avatar [username]="username" size="large"></redaction-initials-avatar>
</div> </div>

View File

@ -29,9 +29,7 @@
line-height: 18px; line-height: 18px;
} }
.project-team { .members-container {
.member:not(:last-child) { gap: 5px;
margin-right: 5px;
}
} }
} }

View File

@ -1,6 +1,6 @@
<section class="red-upload-overlay mat-elevation-z4"> <section class="red-upload-overlay mat-elevation-z4">
<div class="red-upload-header" (click)="collapsed = !collapsed"> <div class="red-upload-header" (click)="collapsed = !collapsed">
<div class="text"> <div class="title">
{{ 'upload-status.dialog.title.label' | translate: {p1: uploadService.files.length} }} {{ 'upload-status.dialog.title.label' | translate: {p1: uploadService.files.length} }}
</div> </div>
<div class="collapse-icon" *ngIf="!collapsed"> <div class="collapse-icon" *ngIf="!collapsed">

View File

@ -10,11 +10,11 @@
border-radius: 12px; border-radius: 12px;
font-size: 10px; font-size: 10px;
border: 1px solid #E2E4E9; border: 1px solid #E2E4E9;
font-family: Inter, sans-serif;
letter-spacing: 0; letter-spacing: 0;
line-height: 12px; line-height: 12px;
text-align: center; text-align: center;
text-transform: uppercase;
font-family: Inter, sans-serif;
&.large { &.large {
height: 32px; height: 32px;
@ -50,7 +50,11 @@
align-items: center; align-items: center;
} }
> div:not(:last-child) { gap: 12px;
margin-right: 12px;
mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
} }
} }

View File

@ -6,7 +6,7 @@ html, body {
padding: 0; padding: 0;
height: 100vh; height: 100vh;
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
color: $grey-1; color: $accent;
} }
.page-header { .page-header {
@ -142,8 +142,6 @@ html, body {
.app-name { .app-name {
margin-left: 16px; margin-left: 16px;
height: 20px; height: 20px;
color: #283241;
font-family: Inter, sans-serif;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
letter-spacing: 0; letter-spacing: 0;
@ -158,7 +156,7 @@ html, body {
.breadcrumb { .breadcrumb {
text-decoration: none; text-decoration: none;
color: $grey-1; color: $accent;
font-size: 13px; font-size: 13px;
line-height: 18px; line-height: 18px;
font-weight: 600; font-weight: 600;

View File

@ -47,11 +47,6 @@
@include line-clamp(1); @include line-clamp(1);
} }
.table-item-title--large {
font-size: 16px;
line-height: 20px;
}
.on-hover-wrapper { .on-hover-wrapper {
width: 0; width: 0;
height: 0; height: 0;

View File

@ -24,14 +24,17 @@ a {
} }
.heading-l { .heading-l {
color: #283241;
font-family: Inter, sans-serif;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: 600;
letter-spacing: 0;
line-height: 22px; line-height: 22px;
} }
.heading {
font-size: 16px;
line-height: 20px;
font-weight: 600;
}
.subheading { .subheading {
text-transform: uppercase; text-transform: uppercase;
opacity: 0.7; opacity: 0.7;

View File

@ -16,6 +16,8 @@ $red-1: #F65757;
$yellow-1: #FFB83B; $yellow-1: #FFB83B;
$green-1: #46CE7D; $green-1: #46CE7D;
$separator: rgba(226,228,233,0.9);
$right-container-inside-width: 340px; $right-container-inside-width: 340px;
$right-container-padding: 16px; $right-container-padding: 16px;
$right-container-width: calc(#{$right-container-inside-width} + 2*#{$right-container-padding} + 1px); $right-container-width: calc(#{$right-container-inside-width} + 2*#{$right-container-padding} + 1px);