diff --git a/docs/docassets/images/pdf-thumbnails.png b/docs/docassets/images/pdf-thumbnails.png new file mode 100644 index 0000000000..80384ba544 Binary files /dev/null and b/docs/docassets/images/pdf-thumbnails.png differ diff --git a/docs/viewer.component.md b/docs/viewer.component.md index 241f191e97..e084004887 100644 --- a/docs/viewer.component.md +++ b/docs/viewer.component.md @@ -69,6 +69,8 @@ Using with file url: | showSidebar | `boolean` | `false` | Toggles sidebar visibility. Requires `allowSidebar` to be set to `true`. | | sidebarPosition | `string` | `'right'` | The position of the sidebar. Can be `left` or `right`. | | sidebarTemplate | `TemplateRef` | `null` | The template for the sidebar. The template context contains the loaded node data. | +| allowThumbnails | `boolean` | `true` | Enables pdf viewer thumbnails. | +| thumbnailsTemplate | `TemplateRef` | `null` | Custom template content for thumbnails. | | mimeType | `string` | | MIME type of the file content (when not determined by the filename extension). | | fileName | `string` | | Content filename. | | downloadUrl | `string` | `null` | URL to download. | @@ -279,6 +281,38 @@ Everything you put inside the "adf-viewer-sidebar" tags is going to be rendered. ``` +### Custom thumbnails + +By default, the pdf viewer comes with its own thumbnails list but this can be replaced +by providing a custom template and binding to context property `viewer` to access PDFJS.PDFViewer +instance. + +![PDF thumbnails](docassets/images/pdf-thumbnails.png) + +#### Using template injection + +```javascript +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'custom-thumbnails', + template: '

Custom Thumbnails Component

' +}) +export class CustomThumbnailsComponent { + @Input() pdfViewer: any; + + ... +} +``` + +```html + + + + + +``` + ### Custom "Open With" menu You can enable custom "Open With" menu by providing at least one action inside the "adf-viewer-open-with" tag: diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index 469bfe6506..8b5eaa2c49 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -210,6 +210,9 @@ "LOADING": "Loading", "UNKNOWN_FORMAT": "Couldn't load preview", "SIDEBAR": { + "THUMBNAILS": { + "PAGE": "Page {{ pageNum }}" + }, "METADATA": { "MORE_INFORMATION": "More information", "LESS_INFORMATION": "Less information" diff --git a/lib/core/styles/_index.scss b/lib/core/styles/_index.scss index a0205d5fbb..2c1f230a8d 100644 --- a/lib/core/styles/_index.scss +++ b/lib/core/styles/_index.scss @@ -18,6 +18,7 @@ @import '../userinfo/components/user-info.component'; @import '../viewer/components/viewer.component'; @import '../viewer/components/pdfViewer.component'; +@import '../viewer/components/pdfViewer-thumbnails.component'; @import '../viewer/components/txtViewer.component'; @import '../viewer/components/imgViewer.component'; @import '../form/components/form.component'; @@ -42,6 +43,7 @@ @include adf-userinfo-theme($theme); @include adf-viewer-theme($theme); @include adf-pdf-viewer-theme($theme); + @include adf-pdf-thumbnails-theme($theme); @include adf-image-viewer-theme($theme); @include adf-text-viewer-theme($theme); @include adf-form-component-theme($theme); diff --git a/lib/core/viewer/components/pdfViewer-thumb.component.html b/lib/core/viewer/components/pdfViewer-thumb.component.html new file mode 100644 index 0000000000..abce3ef950 --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumb.component.html @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/lib/core/viewer/components/pdfViewer-thumb.component.spec.ts b/lib/core/viewer/components/pdfViewer-thumb.component.spec.ts new file mode 100644 index 0000000000..bf8e129374 --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumb.component.spec.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DomSanitizer } from '@angular/platform-browser'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { PdfThumbComponent } from './pdfViewer-thumb.component'; + +describe('PdfThumbComponent', () => { + + let fixture: ComponentFixture; + let component: PdfThumbComponent; + const domSanitizer = { + bypassSecurityTrustUrl: () => 'image-data' + }; + const page = { + id: 'pageId', + getPage: jasmine.createSpy('getPage').and.returnValue(Promise.resolve({ + getViewport: () => ({ height: 0, width: 0 }), + render: jasmine.createSpy('render').and.returnValue(Promise.resolve()) + })) + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + PdfThumbComponent + ], + providers: [ + { provide: DomSanitizer, useValue: domSanitizer } + ] + }).compileComponents() + .then(() => { + fixture = TestBed.createComponent(PdfThumbComponent); + component = fixture.componentInstance; + }); + })); + + it('should have resolve image data', (done) => { + component.page = page; + fixture.detectChanges(); + + component.image$.then((result) => { + expect(result).toBe('image-data'); + done(); + }); + }); + +}); diff --git a/lib/core/viewer/components/pdfViewer-thumb.component.ts b/lib/core/viewer/components/pdfViewer-thumb.component.ts new file mode 100644 index 0000000000..4e4c34b929 --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumb.component.ts @@ -0,0 +1,62 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, Input, OnInit, ElementRef, ViewEncapsulation } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'adf-pdf-thumb', + templateUrl: './pdfViewer-thumb.component.html', + encapsulation: ViewEncapsulation.None +}) +export class PdfThumbComponent implements OnInit { + @Input('page') page: any = null; + + image$: Promise; + + constructor( + private element: ElementRef, private sanitizer: DomSanitizer) {} + + ngOnInit() { + this.image$ = this.page.getPage().then((page) => this.getThumb(page)); + } + + private getThumb(page): Promise { + const canvas = this.getCanvas(); + const viewport = page.getViewport(1); + const scale = Math.min((canvas.height / viewport.height), (canvas.width / viewport.width)); + + return page.render({ + canvasContext: canvas.getContext('2d'), + viewport: page.getViewport(scale) + }) + .then(() => { + const imageSource = canvas.toDataURL(); + return this.sanitizer.bypassSecurityTrustUrl(imageSource); + }); + } + + private getCanvas(): HTMLCanvasElement { + const elementRect = this.element.nativeElement.getBoundingClientRect(); + const canvas = document.createElement('canvas'); + + canvas.width = elementRect.width; + canvas.height = elementRect.height; + + return canvas; + } +} diff --git a/lib/core/viewer/components/pdfViewer-thumbnails.component.html b/lib/core/viewer/components/pdfViewer-thumbnails.component.html new file mode 100644 index 0000000000..95965c0c0b --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumbnails.component.html @@ -0,0 +1,10 @@ +
+ + +
diff --git a/lib/core/viewer/components/pdfViewer-thumbnails.component.scss b/lib/core/viewer/components/pdfViewer-thumbnails.component.scss new file mode 100644 index 0000000000..cddcd0a774 --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumbnails.component.scss @@ -0,0 +1,39 @@ +@mixin adf-pdf-thumbnails-theme($theme) { + $background: map-get($theme, background); + + .pdf-thumbnails { + display: block; + overflow: hidden; + overflow-y: auto; + height: 100%; + position: relative; + + &__content { + top: 0; + left: 50%; + height: 0; + position: absolute; + } + + &__thumb:first-child { + margin-top: 14px; + } + + &__thumb { + cursor: pointer; + display: block; + height: 140px; + width: 100px; + background: mat-color($background, background); + margin-bottom: 14px; + } + + &__thumb:hover { + box-shadow: 0px 0px 5px 0px $black-87-opacity; + } + + &__thumb--selected:not(:hover) { + box-shadow: 0px 0px 5px 0px $black-87-opacity; + } + } +} \ No newline at end of file diff --git a/lib/core/viewer/components/pdfViewer-thumbnails.component.spec.ts b/lib/core/viewer/components/pdfViewer-thumbnails.component.spec.ts new file mode 100644 index 0000000000..5c2723515d --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumbnails.component.spec.ts @@ -0,0 +1,152 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { PdfThumbListComponent } from './pdfViewer-thumbnails.component'; +import { PdfThumbComponent } from './pdfViewer-thumb.component'; + +declare let PDFJS: any; + +describe('PdfThumbListComponent', () => { + + let fixture: ComponentFixture; + let component: PdfThumbListComponent; + + const page = (id) => { + return { + id, + getPage: () => Promise.resolve() + }; + }; + + const viewerMock = { + _currentPageNumber: null, + set currentPageNumber(pageNum) { + this._currentPageNumber = pageNum; + this.eventBus.dispatch('pagechange', { pageNumber: pageNum }); + }, + get currentPageNumber() { return this._currentPageNumber; }, + pdfDocument: { + getPage: (pageNum) => Promise.resolve({}) + }, + _pages: [ + page(1), page(2), page(3), page(4), + page(5), page(6), page(7), page(8), + page(9), page(10), page(11), page(12), + page(13), page(14), page(15), page(16) + ], + eventBus: new PDFJS.EventBus() + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + PdfThumbListComponent, + PdfThumbComponent + ] + }) + .compileComponents() + .then(() => { + fixture = TestBed.createComponent(PdfThumbListComponent); + component = fixture.componentInstance; + + component.pdfViewer = viewerMock; + + // provide scrollable container + fixture.nativeElement.style.display = 'block'; + fixture.nativeElement.style.height = '700px'; + fixture.nativeElement.style.overflow = 'scroll'; + fixture.debugElement.query(By.css('.pdf-thumbnails__content')) + .nativeElement.style.height = '2000px'; + }); + })); + + it('should render initial rage of items', () => { + fixture.nativeElement.scrollTop = 0; + fixture.detectChanges(); + + const renderedIds = component.renderItems.map(item => item.id); + const rangeIds = viewerMock._pages.slice(0, 5).map(item => item.id); + + expect(renderedIds).toEqual(rangeIds); + }); + + it('should render next range on scroll', () => { + fixture.nativeElement.scrollTop = 700; + fixture.detectChanges(); + + const renderedIds = component.renderItems.map(item => item.id); + const rangeIds = viewerMock._pages.slice(5, 10).map(item => item.id); + + expect(renderedIds).toEqual(rangeIds); + }); + + it('should render items containing current document page', () => { + fixture.nativeElement.scrollTop = 1700; + fixture.detectChanges(); + + const renderedIds = component.renderItems.map(item => item.id); + + expect(renderedIds).not.toContain(3); + + viewerMock.eventBus.dispatch('pagechange', { pageNumber: 3 }); + + const newRenderedIds = component.renderItems.map(item => item.id); + + expect(newRenderedIds).toContain(3); + }); + + it('should not change items if range contains current document page', () => { + fixture.nativeElement.scrollTop = 1700; + fixture.detectChanges(); + + const renderedIds = component.renderItems.map(item => item.id); + + expect(renderedIds).toContain(12); + + viewerMock.eventBus.dispatch('pagechange', { pageNumber: 12 }); + + const newRenderedIds = component.renderItems.map(item => item.id); + + expect(newRenderedIds).toContain(12); + }); + + it('should return current viewed page as selected', () => { + fixture.nativeElement.scrollTop = 0; + fixture.detectChanges(); + + viewerMock.currentPageNumber = 2; + + expect(component.isSelected(2)).toBe(true); + }); + + it('should go to selected page', () => { + fixture.nativeElement.scrollTop = 0; + fixture.detectChanges(); + + const renderedIds = component.renderItems.map(item => item.id); + + expect(renderedIds).not.toContain(12); + + component.goTo(12); + + const newRenderedIds = component.renderItems.map(item => item.id); + + expect(newRenderedIds).toContain(12); + }); +}); diff --git a/lib/core/viewer/components/pdfViewer-thumbnails.component.ts b/lib/core/viewer/components/pdfViewer-thumbnails.component.ts new file mode 100644 index 0000000000..24a5040097 --- /dev/null +++ b/lib/core/viewer/components/pdfViewer-thumbnails.component.ts @@ -0,0 +1,137 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Component, Input, ContentChild, TemplateRef, HostListener, OnInit, + AfterViewInit, ElementRef, OnDestroy, ViewEncapsulation +} from '@angular/core'; + +@Component({ + selector: 'adf-pdf-thumbnails', + templateUrl: './pdfViewer-thumbnails.component.html', + styleUrls: ['./pdfViewer-thumbnails.component.scss'], + host: { 'class': 'pdf-thumbnails' }, + encapsulation: ViewEncapsulation.None +}) +export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy { + @Input() pdfViewer: any; + + virtualHeight: number = 0; + translateY: number = 0; + renderItems = []; + + private items = []; + private itemHeight: number = 140; + + @ContentChild(TemplateRef) + template: any; + + @HostListener('window:resize', ['$event']) + onResize(event) { + this.calculateItems(); + } + + constructor(private element: ElementRef) { + this.calculateItems = this.calculateItems.bind(this); + this.onPageChange = this.onPageChange.bind(this); + } + + ngOnInit() { + this.element.nativeElement.addEventListener('scroll', this.calculateItems, true); + this.pdfViewer.eventBus.on('pagechange', this.onPageChange); + + this.items = this.getPages(); + this.calculateItems(); + } + + ngAfterViewInit() { + setTimeout(() => this.scrollInto(this.pdfViewer.currentPageNumber), 0); + } + + ngOnDestroy() { + this.element.nativeElement.removeEventListener('scroll', this.calculateItems, true); + this.pdfViewer.eventBus.off('pagechange', this.onPageChange); + } + + trackByFn(index: number, item: any): number { + return item.id; + } + + isSelected(pageNum: number) { + return this.pdfViewer.currentPageNumber === pageNum; + } + + goTo(pageNum: number) { + this.pdfViewer.currentPageNumber = pageNum; + } + + scrollInto(item: any) { + if (this.items.length) { + const index: number = this.items.findIndex((element) => element.id === item); + + if (index < 0 || index >= this.items.length) { + return; + } + + this.element.nativeElement.scrollTop = Math.floor(index - 1 ) * this.itemHeight; + + this.calculateItems(); + } + } + + getPages() { + return this.pdfViewer._pages.map((page) => ({ + id: page.id, + getPage: () => this.pdfViewer.pdfDocument.getPage(page.id) + })); + } + + private calculateItems() { + const { element, viewPort, itemsInView } = this.getContainerSetup(); + + const indexByScrollTop = element.scrollTop / viewPort * this.items.length / itemsInView; + + const start = Math.floor(indexByScrollTop); + + const end = Math.ceil(indexByScrollTop) + (itemsInView); + + this.translateY = this.itemHeight * Math.ceil(start); + this.virtualHeight = this.itemHeight * this.items.length - this.translateY; + this.renderItems = this.items.slice(start, end); + } + + private getContainerSetup() { + const element = this.element.nativeElement; + const elementRec = element.getBoundingClientRect(); + const itemsInView = Math.ceil(elementRec.height / this.itemHeight); + const viewPort = (this.itemHeight * this.items.length) / itemsInView; + + return { + element, + viewPort, + itemsInView + }; + } + + private onPageChange(event) { + const index = this.renderItems.findIndex((element) => element.id === event.pageNumber); + + if (index < 0) { + this.scrollInto(event.pageNumber); + } + } +} diff --git a/lib/core/viewer/components/pdfViewer.component.html b/lib/core/viewer/components/pdfViewer.component.html index 5dd57d5476..2e53e1b1cd 100644 --- a/lib/core/viewer/components/pdfViewer.component.html +++ b/lib/core/viewer/components/pdfViewer.component.html @@ -1,9 +1,24 @@ -
-
-
-
- -
+
+ +
+ + + + + + +
+
+ +
+
+
+
+
+ +
+
+
@@ -12,7 +27,8 @@ - diff --git a/lib/core/viewer/components/pdfViewer.component.scss b/lib/core/viewer/components/pdfViewer.component.scss index 2b27210f2b..c182caa0e0 100644 --- a/lib/core/viewer/components/pdfViewer.component.scss +++ b/lib/core/viewer/components/pdfViewer.component.scss @@ -1,7 +1,14 @@ @mixin adf-pdf-viewer-theme($theme) { $foreground: map-get($theme, foreground); + .adf-viewer-content-container { + width: 100%; + } + .adf-pdf-viewer { + width: 100%; + margin: 0; + .loader-container { display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ @@ -14,6 +21,18 @@ height:100%; } + &__thumbnails { + position: relative; height: 100%; width: 140px; background-color: rgba(0, 0, 0, 0.12); + } + + &__container { + display: flex; height: 100%; + } + + &__content { + flex: 1 1 auto; position: relative; + } + .loader-item { margin: auto; max-height:100px; diff --git a/lib/core/viewer/components/pdfViewer.component.spec.ts b/lib/core/viewer/components/pdfViewer.component.spec.ts index 86d4f1ff02..1e26d096a8 100644 --- a/lib/core/viewer/components/pdfViewer.component.spec.ts +++ b/lib/core/viewer/components/pdfViewer.component.spec.ts @@ -26,6 +26,8 @@ import { ToolbarModule } from '../../toolbar/toolbar.module'; import { EventMock } from '../../mock/event.mock'; import { RenderingQueueServices } from '../services/rendering-queue.services'; import { PdfViewerComponent } from './pdfViewer.component'; +import { PdfThumbListComponent } from './pdfViewer-thumbnails.component'; +import { PdfThumbComponent } from './pdfViewer-thumb.component'; declare var require: any; @@ -41,7 +43,11 @@ describe('Test PdfViewer component', () => { ToolbarModule, MaterialModule ], - declarations: [PdfViewerComponent], + declarations: [ + PdfViewerComponent, + PdfThumbListComponent, + PdfThumbComponent + ], providers: [ SettingsService, AuthenticationService, @@ -97,7 +103,7 @@ describe('Test PdfViewer component', () => { it('should Canvas be present', () => { expect(element.querySelector('.pdfViewer')).not.toBeNull(); - expect(element.querySelector('.viewer-pdf-container')).not.toBeNull(); + expect(element.querySelector('.viewer-pdf-viewer')).not.toBeNull(); }); it('should Loader be present', () => { @@ -145,7 +151,7 @@ describe('Test PdfViewer component', () => { it('should Canvas be present', () => { expect(element.querySelector('.pdfViewer')).not.toBeNull(); - expect(element.querySelector('.viewer-pdf-container')).not.toBeNull(); + expect(element.querySelector('.viewer-pdf-viewer')).not.toBeNull(); }); it('should Loader be present', () => { diff --git a/lib/core/viewer/components/pdfViewer.component.ts b/lib/core/viewer/components/pdfViewer.component.ts index eca0febfa9..8cb034c401 100644 --- a/lib/core/viewer/components/pdfViewer.component.ts +++ b/lib/core/viewer/components/pdfViewer.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Component, HostListener, Input, OnChanges, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { Component, TemplateRef, HostListener, Input, OnChanges, OnDestroy, ViewEncapsulation } from '@angular/core'; import { LogService } from '../../services/log.service'; import { RenderingQueueServices } from '../services/rendering-queue.services'; @@ -49,6 +49,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { @Input() allowThumbnails = false; + @Input() + thumbnailsTemplate: TemplateRef = null; + currentPdfDocument: any; page: number; displayPage: number; @@ -64,6 +67,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { MIN_SCALE: number = 0.25; MAX_SCALE: number = 10.0; + showThumbnails = false; + pdfThumbnailsContext: { viewer: any } = { viewer: null }; + get currentScaleText(): string { return Math.round(this.currentScale * 100) + '%'; } @@ -134,7 +140,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { const viewer: any = document.getElementById('viewer-viewerPdf'); - this.documentContainer = document.getElementById('viewer-pdf-container'); + this.documentContainer = document.getElementById('viewer-pdf-viewer'); this.documentContainer.addEventListener('pagechange', this.onPageChange, true); this.pdfViewer = new PDFJS.PDFViewer({ @@ -146,6 +152,8 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { this.renderingQueueServices.setViewer(this.pdfViewer); this.pdfViewer.setDocument(pdfDocument); + + this.pdfThumbnailsContext.viewer = this.pdfViewer; } ngOnDestroy() { @@ -154,6 +162,10 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { } } + toggleThumbnails() { + this.showThumbnails = !this.showThumbnails; + } + /** * Method to scale the page current support implementation * @@ -165,7 +177,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy { if (this.pdfViewer) { let viewerContainer = document.getElementById('viewer-main-container'); - let documentContainer = document.getElementById('viewer-pdf-container'); + let documentContainer = document.getElementById('viewer-pdf-viewer'); let widthContainer; let heightContainer; diff --git a/lib/core/viewer/components/pdfViewerHost.component.scss b/lib/core/viewer/components/pdfViewerHost.component.scss index a9d85f6504..6ec11c6919 100644 --- a/lib/core/viewer/components/pdfViewerHost.component.scss +++ b/lib/core/viewer/components/pdfViewerHost.component.scss @@ -235,7 +235,7 @@ } } -.viewer-pdf-container { +.viewer-pdf-viewer { overflow: auto; -webkit-overflow-scrolling: touch; position: absolute; @@ -245,9 +245,9 @@ left: 0; outline: none; } -html[dir='ltr'] .viewer-pdf-container { +html[dir='ltr'] .viewer-pdf-viewer { box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); } -html[dir='rtl'] .viewer-pdf-container { +html[dir='rtl'] .viewer-pdf-viewer { box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05); } diff --git a/lib/core/viewer/components/viewer.component.html b/lib/core/viewer/components/viewer.component.html index 1dbddb6841..133554f1da 100644 --- a/lib/core/viewer/components/viewer.component.html +++ b/lib/core/viewer/components/viewer.component.html @@ -144,7 +144,7 @@
- + diff --git a/lib/core/viewer/components/viewer.component.scss b/lib/core/viewer/components/viewer.component.scss index 8b8b22ceed..75c433f395 100644 --- a/lib/core/viewer/components/viewer.component.scss +++ b/lib/core/viewer/components/viewer.component.scss @@ -130,6 +130,39 @@ overflow: auto; } + &__thumbnails { + width: 180px; + display: flex; + flex-direction: column; + padding: 0; + background: #e6e6e6; + + .adf-info-drawer-layout { + display: flex; + flex-direction: column; + flex: 1; + background: #e6e6e6; + } + + .adf-info-drawer-layout-header { + margin-bottom: 0; + } + + .adf-info-drawer-layout-content { + padding: 0; + height: 100%; + } + + .info-drawer-content { + height: 100%; + } + + .adf-info-drawer-layout-content > *:last-child { + height: 100%; + overflow: hidden; + } + } + &__toolbar { &-page-scale { cursor: default; diff --git a/lib/core/viewer/components/viewer.component.spec.ts b/lib/core/viewer/components/viewer.component.spec.ts index 47ab40d842..2eee5dfe2f 100644 --- a/lib/core/viewer/components/viewer.component.spec.ts +++ b/lib/core/viewer/components/viewer.component.spec.ts @@ -30,6 +30,8 @@ import { RenderingQueueServices } from '../services/rendering-queue.services'; import { ImgViewerComponent } from './imgViewer.component'; import { MediaPlayerComponent } from './mediaPlayer.component'; import { PdfViewerComponent } from './pdfViewer.component'; +import { PdfThumbListComponent } from './pdfViewer-thumbnails.component'; +import { PdfThumbComponent } from './pdfViewer-thumb.component'; import { TxtViewerComponent } from './txtViewer.component'; import { UnknownFormatComponent } from './unknown-format/unknown-format.component'; import { ViewerMoreActionsComponent } from './viewer-more-actions.component'; @@ -129,6 +131,8 @@ describe('ViewerComponent', () => { declarations: [ ViewerComponent, PdfViewerComponent, + PdfThumbListComponent, + PdfThumbComponent, TxtViewerComponent, MediaPlayerComponent, ImgViewerComponent, diff --git a/lib/core/viewer/components/viewer.component.ts b/lib/core/viewer/components/viewer.component.ts index 574552bb07..a16b9a0fed 100644 --- a/lib/core/viewer/components/viewer.component.ts +++ b/lib/core/viewer/components/viewer.component.ts @@ -18,7 +18,8 @@ import { Location } from '@angular/common'; import { Component, ContentChild, EventEmitter, HostListener, ElementRef, - Input, OnChanges, Output, SimpleChanges, TemplateRef, ViewEncapsulation + Input, OnChanges, Output, SimpleChanges, TemplateRef, + ViewEncapsulation } from '@angular/core'; import { MinimalNodeEntryEntity, RenditionEntry } from 'alfresco-js-api'; import { BaseEvent } from '../../events'; @@ -130,6 +131,10 @@ export class ViewerComponent implements OnChanges { @Input() allowSidebar = false; + /** Toggles PDF thumbnails. */ + @Input() + allowThumbnails = true; + /** Toggles sidebar visibility. Requires `allowSidebar` to be set to `true`. */ @Input() showSidebar = false; @@ -142,6 +147,10 @@ export class ViewerComponent implements OnChanges { @Input() sidebarTemplate: TemplateRef = null; + /** The template for the pdf thumbnails. */ + @Input() + thumbnailsTemplate: TemplateRef = null; + /** MIME type of the file content (when not determined by the filename extension). */ @Input() mimeType: string; @@ -192,6 +201,7 @@ export class ViewerComponent implements OnChanges { @Output() navigateNext = new EventEmitter(); + showPdfThumbnails: boolean = false; viewerType = 'unknown'; isLoading = false; node: MinimalNodeEntryEntity; diff --git a/lib/core/viewer/public-api.ts b/lib/core/viewer/public-api.ts index af0e492972..ab87387f01 100644 --- a/lib/core/viewer/public-api.ts +++ b/lib/core/viewer/public-api.ts @@ -19,6 +19,8 @@ export * from './components/viewer.component'; export * from './components/imgViewer.component'; export * from './components/mediaPlayer.component'; export * from './components/pdfViewer.component'; +export * from './components/pdfViewer-thumbnails.component'; +export * from './components/pdfViewer-thumb.component'; export * from './components/txtViewer.component'; export * from './components/unknown-format/unknown-format.component'; export * from './components/viewer-more-actions.component'; diff --git a/lib/core/viewer/viewer.module.ts b/lib/core/viewer/viewer.module.ts index f77f882601..342cbbef74 100644 --- a/lib/core/viewer/viewer.module.ts +++ b/lib/core/viewer/viewer.module.ts @@ -25,6 +25,8 @@ import { PipeModule } from '../pipes/pipe.module'; import { ImgViewerComponent } from './components/imgViewer.component'; import { MediaPlayerComponent } from './components/mediaPlayer.component'; import { PdfViewerComponent } from './components/pdfViewer.component'; +import { PdfThumbComponent } from './components/pdfViewer-thumb.component'; +import { PdfThumbListComponent } from './components/pdfViewer-thumbnails.component'; import { TxtViewerComponent } from './components/txtViewer.component'; import { UnknownFormatComponent } from './components/unknown-format/unknown-format.component'; import { ViewerMoreActionsComponent } from './components/viewer-more-actions.component'; @@ -50,6 +52,8 @@ import { FlexLayoutModule } from '@angular/flex-layout'; TxtViewerComponent, MediaPlayerComponent, PdfViewerComponent, + PdfThumbComponent, + PdfThumbListComponent, ViewerExtensionDirective, UnknownFormatComponent, ViewerToolbarComponent, @@ -63,6 +67,8 @@ import { FlexLayoutModule } from '@angular/flex-layout'; TxtViewerComponent, MediaPlayerComponent, PdfViewerComponent, + PdfThumbComponent, + PdfThumbListComponent, ViewerExtensionDirective, UnknownFormatComponent, ViewerToolbarComponent,