From 507ae6f5f62f31e5ea66cbf873ff530efabca976 Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Wed, 14 Oct 2020 16:05:08 +0300 Subject: [PATCH] made scrolling gancy --- .../file-preview-screen.component.html | 6 +- .../file-preview-screen.component.ts | 117 ++++++------------ apps/red-ui/src/app/utils/debounce.ts | 14 +++ package.json | 1 + yarn.lock | 63 ++-------- 5 files changed, 69 insertions(+), 132 deletions(-) create mode 100644 apps/red-ui/src/app/utils/debounce.ts diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html index 2ff6978b6..d4d7495b0 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html @@ -57,6 +57,10 @@ {{item.hints}} Hints +
+ + {{item.ignore}} Ignore +
@@ -71,7 +75,7 @@
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts index e63f48b4f..464c734c6 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts @@ -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); - } - }; - } -})(); diff --git a/apps/red-ui/src/app/utils/debounce.ts b/apps/red-ui/src/app/utils/debounce.ts new file mode 100644 index 000000000..fcd631824 --- /dev/null +++ b/apps/red-ui/src/app/utils/debounce.ts @@ -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; + }; +} diff --git a/package.json b/package.json index cb243d035..2b186944e 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/yarn.lock b/yarn.lock index b363400cd..5d8b34b0e 100644 --- a/yarn.lock +++ b/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"