made scrolling gancy
This commit is contained in:
parent
22186b7778
commit
507ae6f5f6
@ -57,6 +57,10 @@
|
||||
<mat-icon svgIcon="red:idea"></mat-icon>
|
||||
{{item.hints}} Hints
|
||||
</div>
|
||||
<div *ngIf="item.hints" class="center">
|
||||
<mat-icon svgIcon="red:info"></mat-icon>
|
||||
{{item.ignore}} Ignore
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -71,7 +75,7 @@
|
||||
|
||||
<div class="tab-content" [class.not-visible]="!annotationsTab">
|
||||
<div *ngFor="let annotation of annotations"
|
||||
class="annotation" [id]="'ann-' + annotation.Id"
|
||||
class="annotation" attr.annotation-id="{{annotation.Id}}"
|
||||
attr.annotation-page="{{annotation.getPageNumber()}}"
|
||||
(click)="selectAnnotation(annotation)"
|
||||
[ngClass]="{ active: selectedAnnotation === annotation }">
|
||||
|
||||
@ -17,7 +17,8 @@ import {PdfViewerComponent} from '../pdf-viewer/pdf-viewer.component';
|
||||
import {AnnotationUtils} from '../../../utils/annotation-utils';
|
||||
import {ManualRedactionDialogComponent} from "../manual-redaction-dialog/manual-redaction-dialog.component";
|
||||
import {UserService} from "../../../user/user.service";
|
||||
|
||||
import {debounce} from "../../../utils/debounce";
|
||||
import scrollIntoView from 'scroll-into-view-if-needed'
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -26,7 +27,6 @@ import {UserService} from "../../../user/user.service";
|
||||
})
|
||||
export class FilePreviewScreenComponent implements OnInit {
|
||||
private _readyViewers: string[] = [];
|
||||
private _clickedAnnotationInSidebar = false;
|
||||
private projectId: string;
|
||||
private _selectedTab: 'NAVIGATION' | 'ANNOTATIONS' | 'INFO' = 'NAVIGATION';
|
||||
|
||||
@ -42,7 +42,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
public fileId: string;
|
||||
public annotations: Annotations.Annotation[] = [];
|
||||
public selectedAnnotation: Annotations.Annotation;
|
||||
public quickNavigation: { pageNumber: number, redactions: number, hints: number }[] = [];
|
||||
public quickNavigation: { pageNumber: number, redactions: number, hints: number, ignore: number; }[] = [];
|
||||
|
||||
private _manualRedactionEntry: ManualRedactionEntry;
|
||||
|
||||
@ -105,26 +105,31 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public selectTab(value: 'ANNOTATIONS' | 'INFO' | 'NAVIGATION') {
|
||||
this._selectedTab = value;
|
||||
setTimeout(() => {
|
||||
this._scrollViews();
|
||||
}, 50);
|
||||
if (value !== this._selectedTab) {
|
||||
this._selectedTab = value;
|
||||
setTimeout(() => {
|
||||
this._scrollViews();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
public handleAnnotationsAdded(annotations: Annotations.Annotation[]) {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
for (const annotation of annotations) {
|
||||
if (annotation.Id.startsWith('hint:') || annotation.Id.startsWith('redaction:')) {
|
||||
if (annotation.Id.indexOf(':') > 0) {
|
||||
this.annotations.push(annotation);
|
||||
const pageNumber = annotation.getPageNumber();
|
||||
let el = this.quickNavigation.find((page) => page.pageNumber === pageNumber);
|
||||
if (!el) {
|
||||
el = {pageNumber, redactions: 0, hints: 0}
|
||||
el = {pageNumber, redactions: 0, hints: 0, ignore: 0}
|
||||
this.quickNavigation.push(el);
|
||||
}
|
||||
if (annotation.Id.startsWith('hint:')) {
|
||||
el.hints++;
|
||||
}
|
||||
if (annotation.Id.startsWith('ignore:')) {
|
||||
el.ignore++;
|
||||
}
|
||||
if (annotation.Id.startsWith('redaction:')) {
|
||||
el.redactions++;
|
||||
}
|
||||
@ -136,30 +141,21 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
public handleAnnotationSelected(annotation: Annotations.Annotation) {
|
||||
this.selectedAnnotation = annotation;
|
||||
this.selectTab('ANNOTATIONS');
|
||||
this.scrollToAnnotation(annotation);
|
||||
this.scrollToSelectedAnnotation();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public selectAnnotation(annotation: Annotations.Annotation) {
|
||||
this._clickedAnnotationInSidebar = true;
|
||||
setTimeout(() => {
|
||||
this._clickedAnnotationInSidebar = false;
|
||||
}, 100);
|
||||
this._viewerComponent.selectAnnotation(annotation);
|
||||
}
|
||||
|
||||
private scrollToAnnotation(annotation: Annotations.Annotation) {
|
||||
if (!annotation || this._clickedAnnotationInSidebar) {
|
||||
@debounce()
|
||||
private scrollToSelectedAnnotation() {
|
||||
if (!this.selectedAnnotation) {
|
||||
return;
|
||||
}
|
||||
const el = document.getElementById('ann-' + annotation.Id);
|
||||
|
||||
if (!el) {
|
||||
console.error(`Annotation with id ${annotation.Id} does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
el.scrollIntoView({block: 'center', inline: 'center', behavior: 'smooth'});
|
||||
const elements: any[] = this._annotationsContainer.nativeElement.querySelectorAll(`div[annotation-id="${this.selectedAnnotation.Id}"].active`);
|
||||
this._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
public get navigationTab() {
|
||||
@ -203,22 +199,34 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
@debounce()
|
||||
private _scrollViews() {
|
||||
console.log('scroll views');
|
||||
this._scrollQuickNavigation();
|
||||
this._scrollAnnotations();
|
||||
}
|
||||
|
||||
private _scrollQuickNavigation() {
|
||||
const elements: any[] = this._navigationTabElement.nativeElement.querySelectorAll(`#quick-nav-page-${this.activeViewerPage}`);
|
||||
if (elements.length > 0) {
|
||||
elements[0].scrollIntoViewIfNeeded();
|
||||
}
|
||||
this._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
private _scrollAnnotations() {
|
||||
if (this.selectedAnnotation?.getPageNumber() === this.activeViewerPage) {
|
||||
return;
|
||||
}
|
||||
const elements: any[] = this._annotationsContainer.nativeElement.querySelectorAll(`div[annotation-page="${this.activeViewerPage}"]`);
|
||||
this._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
private _scrollToFirstElement(elements: HTMLElement[]) {
|
||||
if (elements.length > 0) {
|
||||
elements[0].scrollIntoViewIfNeeded();
|
||||
scrollIntoView(elements[0], {
|
||||
behavior: 'smooth',
|
||||
scrollMode: 'if-needed',
|
||||
block: 'center',
|
||||
inline: 'center',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,56 +265,3 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
(function () {
|
||||
// @ts-ignore
|
||||
if (!Element.prototype.scrollIntoViewIfNeeded) {
|
||||
// @ts-ignore
|
||||
Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
|
||||
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
|
||||
|
||||
const parent = this.parentNode,
|
||||
parentComputedStyle = window.getComputedStyle(parent, null),
|
||||
parentBorderTopWidth = parseInt(
|
||||
parentComputedStyle.getPropertyValue('border-top-width'),
|
||||
10
|
||||
),
|
||||
parentBorderLeftWidth = parseInt(
|
||||
parentComputedStyle.getPropertyValue('border-left-width'),
|
||||
10
|
||||
),
|
||||
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
|
||||
overBottom =
|
||||
this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth >
|
||||
parent.scrollTop + parent.clientHeight,
|
||||
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
|
||||
overRight =
|
||||
this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth >
|
||||
parent.scrollLeft + parent.clientWidth,
|
||||
alignWithTop = overTop && !overBottom;
|
||||
|
||||
if ((overTop || overBottom) && centerIfNeeded) {
|
||||
parent.scrollTop =
|
||||
this.offsetTop -
|
||||
parent.offsetTop -
|
||||
parent.clientHeight / 2 -
|
||||
parentBorderTopWidth +
|
||||
this.clientHeight / 2;
|
||||
}
|
||||
|
||||
if ((overLeft || overRight) && centerIfNeeded) {
|
||||
parent.scrollLeft =
|
||||
this.offsetLeft -
|
||||
parent.offsetLeft -
|
||||
parent.clientWidth / 2 -
|
||||
parentBorderLeftWidth +
|
||||
this.clientWidth / 2;
|
||||
}
|
||||
|
||||
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
|
||||
this.scrollIntoView(alignWithTop);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
14
apps/red-ui/src/app/utils/debounce.ts
Normal file
14
apps/red-ui/src/app/utils/debounce.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export function debounce(delay: number = 300): MethodDecorator {
|
||||
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
let timeout = null;
|
||||
|
||||
const original = descriptor.value;
|
||||
|
||||
descriptor.value = function(...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => original.apply(this, args), delay);
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
@ -51,6 +51,7 @@
|
||||
"ngx-dropzone": "^2.2.2",
|
||||
"ngx-toastr": "^13.0.0",
|
||||
"rxjs": "~6.5.5",
|
||||
"scroll-into-view-if-needed": "^2.2.26",
|
||||
"zone.js": "^0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
63
yarn.lock
63
yarn.lock
@ -1808,13 +1808,6 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/chart.js@^2.9.24":
|
||||
version "2.9.25"
|
||||
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.25.tgz#a22d18f6f7cb5b4499f39de600c4c520f635a58f"
|
||||
integrity sha512-SPgPISpaGM42WL9Dezms4+8fInHxAXDzTs8DwPxhGuJT+jETXlVITTbe8bvK1n+sMmyyelR9B4w0lwMkA24oJg==
|
||||
dependencies:
|
||||
moment "^2.10.2"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
@ -3210,29 +3203,6 @@ chardet@^0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
chart.js@^2.9.3:
|
||||
version "2.9.3"
|
||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.3.tgz#ae3884114dafd381bc600f5b35a189138aac1ef7"
|
||||
integrity sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==
|
||||
dependencies:
|
||||
chartjs-color "^2.1.0"
|
||||
moment "^2.10.2"
|
||||
|
||||
chartjs-color-string@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
|
||||
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@^2.1.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
|
||||
integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
|
||||
dependencies:
|
||||
chartjs-color-string "^0.6.0"
|
||||
color-convert "^1.9.3"
|
||||
|
||||
check-more-types@^2.24.0:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
|
||||
@ -3460,7 +3430,7 @@ collection-visit@^1.0.0:
|
||||
map-visit "^1.0.0"
|
||||
object-visit "^1.0.0"
|
||||
|
||||
color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
|
||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
@ -3574,6 +3544,11 @@ compression@^1.7.4:
|
||||
safe-buffer "5.1.2"
|
||||
vary "~1.1.2"
|
||||
|
||||
compute-scroll-into-view@^1.0.16:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz#5b7bf4f7127ea2c19b750353d7ce6776a90ee088"
|
||||
integrity sha512-a85LHKY81oQnikatZYA90pufpZ6sQx++BoCxOEMsjpZx+ZnaKGQnCyCehTRr/1p9GBIAHTjcU9k71kSYWloLiQ==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
@ -7172,11 +7147,6 @@ locate-path@^5.0.0:
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lodash-es@^4.17.15:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
|
||||
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
|
||||
|
||||
lodash.clonedeep@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
@ -7589,11 +7559,6 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdir
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
moment@^2.10.2:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
|
||||
moment@^2.27.0, moment@^2.28.0:
|
||||
version "2.28.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.28.0.tgz#cdfe73ce01327cee6537b0fafac2e0f21a237d75"
|
||||
@ -7734,15 +7699,6 @@ ng-packagr@^10.1.2:
|
||||
stylus "^0.54.7"
|
||||
terser "^5.0.0"
|
||||
|
||||
ng2-charts@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ng2-charts/-/ng2-charts-2.4.2.tgz#6b2d0a1a66911247c2e8c3d8602ba9aa7339bf22"
|
||||
integrity sha512-mY3C2uKCaApHCQizS2YxEOqQ7sSZZLxdV6N1uM9u/VvUgVtYvlPtdcXbKpN52ak93ZE22I73DiLWVDnDNG4/AQ==
|
||||
dependencies:
|
||||
"@types/chart.js" "^2.9.24"
|
||||
lodash-es "^4.17.15"
|
||||
tslib "^2.0.0"
|
||||
|
||||
ng2-file-upload@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ng2-file-upload/-/ng2-file-upload-1.4.0.tgz#8dea28d573234c52af474ad2a4001b335271e5c4"
|
||||
@ -9792,6 +9748,13 @@ schema-utils@^2.6.1, schema-utils@^2.6.4, schema-utils@^2.6.5, schema-utils@^2.6
|
||||
ajv "^6.12.4"
|
||||
ajv-keywords "^3.5.2"
|
||||
|
||||
scroll-into-view-if-needed@^2.2.26:
|
||||
version "2.2.26"
|
||||
resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.26.tgz#e4917da0c820135ff65ad6f7e4b7d7af568c4f13"
|
||||
integrity sha512-SQ6AOKfABaSchokAmmaxVnL9IArxEnLEX9j4wAZw+x4iUTb40q7irtHG3z4GtAWz5veVZcCnubXDBRyLVQaohw==
|
||||
dependencies:
|
||||
compute-scroll-into-view "^1.0.16"
|
||||
|
||||
select-hose@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user