mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-07 18:25:09 +00:00
[ADF-1412] Viewer enhancements (#2873)
* collection navigation support for Viewer * code cleanup * full screen mode support * keyboard shortcuts * zooming scale label * layout fixes * test fixes * image toolbar with basic tranformations * test fixes * test fixes
This commit is contained in:
parent
d2d635b94d
commit
08f2cc9236
@ -65,6 +65,10 @@ Using with file url:
|
|||||||
| showSidebar | boolean | false | Toggles sidebar visibility. Requires `allowSidebar` to be set to `true`. |
|
| 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`. |
|
| sidebarPosition | string | right | The position of the sidebar. Can be `left` or `right`. |
|
||||||
| sidebarTemplate | TemplateRef<any> | null | The template intended to be used as a sidebar. The template context contains the loaded node data. |
|
| sidebarTemplate | TemplateRef<any> | null | The template intended to be used as a sidebar. The template context contains the loaded node data. |
|
||||||
|
| allowNavigate | boolean | false | Toggle before/next navigation, arrow buttons to navigate between multiple documents in the collection. |
|
||||||
|
| canNavigateBefore | boolean | true | Toggle the "before" ("<") button. Requires `allowNavigate` to be enabled. |
|
||||||
|
| canNavigateNext | boolean | true | Toggle the next (">") button. Requires `allowNavigate` to be enabled.|
|
||||||
|
| allowFullScreen | boolean | true | Toggle the 'Full Screen' feature. |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@ -74,6 +78,17 @@ Using with file url:
|
|||||||
| download | any | Yes | Raised when user clicks the 'Download' button. |
|
| download | any | Yes | Raised when user clicks the 'Download' button. |
|
||||||
| print | any | Yes | Raised when user clicks the 'Print' button. |
|
| print | any | Yes | Raised when user clicks the 'Print' button. |
|
||||||
| share | any | Yes | Raised when user clicks the 'Share' button. |
|
| share | any | Yes | Raised when user clicks the 'Share' button. |
|
||||||
|
| navigateBefore | any | No | Raised when user clicks 'Navigate Before' ("<") button. |
|
||||||
|
| navigateNext | any | No | Raised when user clicks 'Navigate Next' (">") button. |
|
||||||
|
|
||||||
|
### Keyboard shortcuts
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| Esc | Close the viewer (overlay mode only). |
|
||||||
|
| Left | Invoke 'Navigate before' action. |
|
||||||
|
| Right | Invoke 'Navigate next' action. |
|
||||||
|
| Ctrl+F | Activate full-screen mode. |
|
||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
|
@ -187,7 +187,8 @@
|
|||||||
"PRINT": "Print",
|
"PRINT": "Print",
|
||||||
"SHARE": "Share",
|
"SHARE": "Share",
|
||||||
"MORE_ACTIONS": "More actions",
|
"MORE_ACTIONS": "More actions",
|
||||||
"INFO": "Info"
|
"INFO": "Info",
|
||||||
|
"FULLSCREEN": "Activate full-screen mode"
|
||||||
},
|
},
|
||||||
"ARIA": {
|
"ARIA": {
|
||||||
"PREVIOUS_PAGE": "Previous page",
|
"PREVIOUS_PAGE": "Previous page",
|
||||||
|
@ -24,6 +24,13 @@ export class EventMock {
|
|||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static keyUp(key: any) {
|
||||||
|
let event: any = document.createEvent('Event');
|
||||||
|
event.keyCode = key;
|
||||||
|
event.initEvent('keyup');
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
static resizeMobileView() {
|
static resizeMobileView() {
|
||||||
// todo: no longer compiles with TS 2.0.2 as innerWidth/innerHeight are readonly fields
|
// todo: no longer compiles with TS 2.0.2 as innerWidth/innerHeight are readonly fields
|
||||||
/*
|
/*
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
@import '../toolbar/toolbar.component';
|
@import '../toolbar/toolbar.component';
|
||||||
@import '../userinfo/components/user-info.component';
|
@import '../userinfo/components/user-info.component';
|
||||||
@import '../viewer/components/viewer.component';
|
@import '../viewer/components/viewer.component';
|
||||||
|
@import '../viewer/components/pdfViewer.component';
|
||||||
|
@import '../viewer/components/txtViewer.component';
|
||||||
|
@import '../viewer/components/imgViewer.component';
|
||||||
@import '../form/components/form.component';
|
@import '../form/components/form.component';
|
||||||
@import '../sidebar/sidebar-action-menu.component';
|
@import '../sidebar/sidebar-action-menu.component';
|
||||||
@import '../viewer/components/pdfViewer.component';
|
|
||||||
|
|
||||||
@mixin adf-core-theme($theme) {
|
@mixin adf-core-theme($theme) {
|
||||||
@include adf-colors-theme($theme);
|
@include adf-colors-theme($theme);
|
||||||
@ -40,6 +42,8 @@
|
|||||||
@include adf-userinfo-theme($theme);
|
@include adf-userinfo-theme($theme);
|
||||||
@include adf-viewer-theme($theme);
|
@include adf-viewer-theme($theme);
|
||||||
@include adf-pdf-viewer-theme($theme);
|
@include adf-pdf-viewer-theme($theme);
|
||||||
|
@include adf-image-viewer-theme($theme);
|
||||||
|
@include adf-text-viewer-theme($theme);
|
||||||
@include adf-form-component-theme($theme);
|
@include adf-form-component-theme($theme);
|
||||||
@include adf-sidebar-action-menu-theme($theme);
|
@include adf-sidebar-action-menu-theme($theme);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
<div class="image-container">
|
<div class="image-container" [ngStyle]="{ transform: transform }">
|
||||||
<img id="viewer-image" [src]="urlFile" [alt]="nameFile" />
|
<img id="viewer-image" [src]="urlFile" [alt]="nameFile" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="adf-image-viewer__toolbar" *ngIf="showToolbar">
|
||||||
|
<adf-toolbar>
|
||||||
|
<button mat-icon-button (click)="zoomIn()">
|
||||||
|
<mat-icon>zoom_in</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="zoomOut()">
|
||||||
|
<mat-icon>zoom_out</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="rotateLeft()">
|
||||||
|
<mat-icon>rotate_left</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="rotateRight()">
|
||||||
|
<mat-icon>rotate_right</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-icon-button (click)="flip()">
|
||||||
|
<mat-icon>flip</mat-icon>
|
||||||
|
</button>
|
||||||
|
</adf-toolbar>
|
||||||
|
</div>
|
||||||
|
@ -1,14 +1,33 @@
|
|||||||
.adf-img-viewer {
|
@mixin adf-image-viewer-theme($theme) {
|
||||||
.image-container {
|
.adf-image-viewer {
|
||||||
display: flex;
|
.image-container {
|
||||||
flex: 1;
|
display: flex;
|
||||||
text-align: center;
|
flex: 1;
|
||||||
flex-direction: row;
|
text-align: center;
|
||||||
justify-content: center;
|
flex-direction: row;
|
||||||
height: 90vh;
|
justify-content: center;
|
||||||
img {
|
height: 90vh;
|
||||||
width: 100%;
|
img {
|
||||||
object-fit: contain;
|
width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.adf-toolbar .mat-toolbar {
|
||||||
|
max-height: 48px;
|
||||||
|
background-color: mat-color($primary, default-contrast, 1);
|
||||||
|
border-width: 0;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.24), 0 0 2px 0 rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
|||||||
import { AuthenticationService } from '../../services/authentication.service';
|
import { AuthenticationService } from '../../services/authentication.service';
|
||||||
import { ContentService } from '../../services/content.service';
|
import { ContentService } from '../../services/content.service';
|
||||||
import { SettingsService } from '../../services/settings.service';
|
import { SettingsService } from '../../services/settings.service';
|
||||||
|
import { MaterialModule } from '../../material.module';
|
||||||
|
import { ToolbarModule } from '../../toolbar/toolbar.module';
|
||||||
|
|
||||||
import { ImgViewerComponent } from './imgViewer.component';
|
import { ImgViewerComponent } from './imgViewer.component';
|
||||||
|
|
||||||
@ -38,7 +40,10 @@ describe('Test Img viewer component ', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
MaterialModule,
|
||||||
|
ToolbarModule
|
||||||
|
],
|
||||||
declarations: [ImgViewerComponent],
|
declarations: [ImgViewerComponent],
|
||||||
providers: [
|
providers: [
|
||||||
SettingsService,
|
SettingsService,
|
||||||
|
@ -22,11 +22,14 @@ import { ContentService } from '../../services/content.service';
|
|||||||
selector: 'adf-img-viewer',
|
selector: 'adf-img-viewer',
|
||||||
templateUrl: './imgViewer.component.html',
|
templateUrl: './imgViewer.component.html',
|
||||||
styleUrls: ['./imgViewer.component.scss'],
|
styleUrls: ['./imgViewer.component.scss'],
|
||||||
host: { 'class': 'adf-img-viewer' },
|
host: { 'class': 'adf-image-viewer' },
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ImgViewerComponent implements OnChanges {
|
export class ImgViewerComponent implements OnChanges {
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
showToolbar = true;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
urlFile: string;
|
urlFile: string;
|
||||||
|
|
||||||
@ -36,6 +39,14 @@ export class ImgViewerComponent implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
nameFile: string;
|
nameFile: string;
|
||||||
|
|
||||||
|
rotate: number = 0;
|
||||||
|
scaleX: number = 1.0;
|
||||||
|
scaleY: number = 1.0;
|
||||||
|
|
||||||
|
get transform(): string {
|
||||||
|
return `scale(${this.scaleX}, ${this.scaleY}) rotate(${this.rotate}deg)`
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private contentService: ContentService) {}
|
constructor(private contentService: ContentService) {}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
@ -48,4 +59,31 @@ export class ImgViewerComponent implements OnChanges {
|
|||||||
throw new Error('Attribute urlFile or blobFile is required');
|
throw new Error('Attribute urlFile or blobFile is required');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoomIn() {
|
||||||
|
const ratio = +((this.scaleX + 0.2).toFixed(1));
|
||||||
|
this.scaleX = this.scaleY = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomOut() {
|
||||||
|
let ratio = +((this.scaleX - 0.2).toFixed(1));
|
||||||
|
if (ratio < 0.2) {
|
||||||
|
ratio = 0.2;
|
||||||
|
}
|
||||||
|
this.scaleX = this.scaleY = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateLeft() {
|
||||||
|
const angle = this.rotate - 90;
|
||||||
|
this.rotate = Math.abs(angle) < 360 ? angle : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateRight() {
|
||||||
|
const angle = this.rotate + 90;
|
||||||
|
this.rotate = Math.abs(angle) < 360 ? angle : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
flip() {
|
||||||
|
this.scaleX *= -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
<span>{{ 'ADF_VIEWER.PAGE_LABEL.OF' | translate }} {{ totalPages }}</span>
|
<span>{{ 'ADF_VIEWER.PAGE_LABEL.OF' | translate }} {{ totalPages }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<adf-toolbar-divider></adf-toolbar-divider>
|
<div class="adf-pdf-viewer__toolbar-page-scale">
|
||||||
|
{{ currentScaleText }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="viewer-zoom-in-button"
|
id="viewer-zoom-in-button"
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
@mixin adf-pdf-viewer-theme($theme) {
|
@mixin adf-pdf-viewer-theme($theme) {
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
.adf-pdf-viewer {
|
.adf-pdf-viewer {
|
||||||
.loader-container {
|
.loader-container {
|
||||||
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
|
||||||
@ -39,8 +41,11 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
& > input {
|
& > input {
|
||||||
|
border: 1px solid mat-color($foreground, text, 0.07);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 4px 0;
|
padding: 0;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: 33px;
|
width: 33px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
@ -48,6 +53,18 @@
|
|||||||
outline-color: gray;
|
outline-color: gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-page-scale {
|
||||||
|
cursor: default;
|
||||||
|
width: 79px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid mat-color($foreground, text, 0.07);
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,13 +56,17 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
loadingPercent: number;
|
loadingPercent: number;
|
||||||
pdfViewer: any;
|
pdfViewer: any;
|
||||||
currentScaleMode: string = 'auto';
|
currentScaleMode: string = 'auto';
|
||||||
currentScale: number;
|
currentScale: number = 1;
|
||||||
|
|
||||||
MAX_AUTO_SCALE: number = 1.25;
|
MAX_AUTO_SCALE: number = 1.25;
|
||||||
DEFAULT_SCALE_DELTA: number = 1.1;
|
DEFAULT_SCALE_DELTA: number = 1.1;
|
||||||
MIN_SCALE: number = 0.25;
|
MIN_SCALE: number = 0.25;
|
||||||
MAX_SCALE: number = 10.0;
|
MAX_SCALE: number = 10.0;
|
||||||
|
|
||||||
|
get currentScaleText(): string {
|
||||||
|
return Math.round(this.currentScale * 100) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private renderingQueueServices: RenderingQueueServices,
|
constructor(private renderingQueueServices: RenderingQueueServices,
|
||||||
private logService: LogService) {
|
private logService: LogService) {
|
||||||
// needed to preserve "this" context when setting as a global document event listener
|
// needed to preserve "this" context when setting as a global document event listener
|
||||||
@ -163,21 +167,21 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
let documentContainer = document.getElementById('viewer-pdf-container');
|
let documentContainer = document.getElementById('viewer-pdf-container');
|
||||||
|
|
||||||
let widthContainer;
|
let widthContainer;
|
||||||
let heigthContainer;
|
let heightContainer;
|
||||||
|
|
||||||
if (viewerContainer && viewerContainer.clientWidth <= documentContainer.clientWidth) {
|
if (viewerContainer && viewerContainer.clientWidth <= documentContainer.clientWidth) {
|
||||||
widthContainer = viewerContainer.clientWidth;
|
widthContainer = viewerContainer.clientWidth;
|
||||||
heigthContainer = viewerContainer.clientHeight;
|
heightContainer = viewerContainer.clientHeight;
|
||||||
} else {
|
} else {
|
||||||
widthContainer = documentContainer.clientWidth;
|
widthContainer = documentContainer.clientWidth;
|
||||||
heigthContainer = documentContainer.clientHeight;
|
heightContainer = documentContainer.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentPage = this.pdfViewer._pages[this.pdfViewer._currentPageNumber - 1];
|
let currentPage = this.pdfViewer._pages[this.pdfViewer._currentPageNumber - 1];
|
||||||
|
|
||||||
let padding = 20;
|
let padding = 20;
|
||||||
let pageWidthScale = (widthContainer - padding) / currentPage.width * currentPage.scale;
|
let pageWidthScale = (widthContainer - padding) / currentPage.width * currentPage.scale;
|
||||||
let pageHeightScale = (heigthContainer - padding) / currentPage.width * currentPage.scale;
|
let pageHeightScale = (heightContainer - padding) / currentPage.width * currentPage.scale;
|
||||||
|
|
||||||
let scale;
|
let scale;
|
||||||
|
|
||||||
@ -231,7 +235,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method to check if the request scale of the page is the same for avoid unuseful re-rendering
|
* Check if the request scale of the page is the same for avoid useless re-rendering
|
||||||
*
|
*
|
||||||
* @param {number} oldScale - old scale page
|
* @param {number} oldScale - old scale page
|
||||||
* @param {number} newScale - new scale page
|
* @param {number} newScale - new scale page
|
||||||
@ -243,7 +247,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method to check if is a land scape view
|
* Check if is a land scape view
|
||||||
*
|
*
|
||||||
* @param {number} width
|
* @param {number} width
|
||||||
* @param {number} height
|
* @param {number} height
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
.adf-txt-viewer {
|
@mixin adf-text-viewer-theme($theme) {
|
||||||
background-color: white;
|
$background: map-get($theme, background);
|
||||||
width: 100vw;
|
|
||||||
overflow: hidden;
|
.adf-txt-viewer {
|
||||||
overflow-y: scroll;
|
background-color: mat-color($background, background);
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,15 @@
|
|||||||
<mat-icon>share</mat-icon>
|
<mat-icon>share</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
*ngIf="allowFullScreen"
|
||||||
|
mat-icon-button
|
||||||
|
matTooltip="{{ 'ADF_VIEWER.ACTIONS.FULLSCREEN' | translate }}"
|
||||||
|
data-automation-id="toolbar-fullscreen"
|
||||||
|
(click)="enterFullScreen()">
|
||||||
|
<mat-icon>fullscreen</mat-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
<ng-container *ngIf="mnuMoreActions">
|
<ng-container *ngIf="mnuMoreActions">
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
@ -104,7 +113,7 @@
|
|||||||
<div *ngIf="!isLoading" fxLayout="row" fxFlex="1 1 auto">
|
<div *ngIf="!isLoading" fxLayout="row" fxFlex="1 1 auto">
|
||||||
|
|
||||||
<ng-container *ngIf="allowSidebar && showSidebar">
|
<ng-container *ngIf="allowSidebar && showSidebar">
|
||||||
<div class="adf-viewer__sidebar" fxFlexOrder="{{sidebarPosition === 'left'? 1: 2 }}">
|
<div class="adf-viewer__sidebar" fxFlexOrder="{{sidebarPosition === 'left'? 1 : 4 }}">
|
||||||
<ng-container *ngIf="sidebarTemplate">
|
<ng-container *ngIf="sidebarTemplate">
|
||||||
<ng-container *ngTemplateOutlet="sidebarTemplate;context:sidebarTemplateContext"></ng-container>
|
<ng-container *ngTemplateOutlet="sidebarTemplate;context:sidebarTemplateContext"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -112,10 +121,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div fxFlexOrder="{{sidebarPosition !== 'left'? 1: 2}}" fxFlex="1 1 auto">
|
<ng-container *ngIf="allowNavigate && canNavigateBefore">
|
||||||
<div class="adf-viewer-layout-content">
|
<div class="navigate-before">
|
||||||
<div class="adf-viewer-content-container" [ngSwitch]="viewerType">
|
<button mat-mini-fab color="primary" (click)="onNavigateBeforeClick()">
|
||||||
|
<mat-icon>navigate_before</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="adf-viewer-main" fxFlexOrder="{{sidebarPosition !== 'left'? 1 : 4}}" fxFlex="1 1 auto">
|
||||||
|
<div class="adf-viewer-layout-content adf-viewer__fullscreen-container">
|
||||||
|
<div class="adf-viewer-content-container" [ngSwitch]="viewerType">
|
||||||
<ng-container *ngSwitchCase="'pdf'">
|
<ng-container *ngSwitchCase="'pdf'">
|
||||||
<adf-pdf-viewer [blobFile]="blobFile" [urlFile]="urlFileContent" [nameFile]="displayName"></adf-pdf-viewer>
|
<adf-pdf-viewer [blobFile]="blobFile" [urlFile]="urlFileContent" [nameFile]="displayName"></adf-pdf-viewer>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -142,10 +158,17 @@
|
|||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
<adf-viewer-unknown-format></adf-viewer-unknown-format>
|
<adf-viewer-unknown-format></adf-viewer-unknown-format>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="allowNavigate && canNavigateNext">
|
||||||
|
<div class="navigate-next" fxFlexOrder="{{sidebarPosition !== 'left' ? 3 : 4}}">
|
||||||
|
<button mat-mini-fab color="primary" (click)="onNavigateNextClick()">
|
||||||
|
<mat-icon>navigate_next</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,30 @@
|
|||||||
|
|
||||||
.adf-viewer {
|
.adf-viewer {
|
||||||
|
|
||||||
|
.navigate-before {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
order: 1;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 4px;
|
||||||
|
background-color: mat-color($background, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigate-next {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
order: 3;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 2px;
|
||||||
|
background-color: mat-color($background, background);
|
||||||
|
}
|
||||||
|
|
||||||
&__mimeicon {
|
&__mimeicon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
@ -19,7 +43,7 @@
|
|||||||
|
|
||||||
&-toolbar {
|
&-toolbar {
|
||||||
.mat-toolbar {
|
.mat-toolbar {
|
||||||
background-color: mat-color($primary, default-contrast)
|
background-color: mat-color($primary, default-contrast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ describe('ViewerComponent', () => {
|
|||||||
component.sidebarPosition = 'right';
|
component.sidebarPosition = 'right';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let sidebar = element.querySelector('.adf-viewer__sidebar');
|
let sidebar = element.querySelector('.adf-viewer__sidebar');
|
||||||
expect(getComputedStyle(sidebar).order).toEqual('2');
|
expect(getComputedStyle(sidebar).order).toEqual('4');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display sidebar on the right side as fallback', () => {
|
it('should display sidebar on the right side as fallback', () => {
|
||||||
@ -241,11 +241,71 @@ describe('ViewerComponent', () => {
|
|||||||
component.sidebarPosition = 'unknown-value';
|
component.sidebarPosition = 'unknown-value';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let sidebar = element.querySelector('.adf-viewer__sidebar');
|
let sidebar = element.querySelector('.adf-viewer__sidebar');
|
||||||
expect(getComputedStyle(sidebar).order).toEqual('2');
|
expect(getComputedStyle(sidebar).order).toEqual('4');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Full Screen Mode', () => {
|
||||||
|
|
||||||
|
it('should request only if enabled', () => {
|
||||||
|
const domElement = jasmine.createSpyObj('el', ['requestFullscreen']);
|
||||||
|
spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement);
|
||||||
|
|
||||||
|
component.allowFullScreen = false;
|
||||||
|
component.enterFullScreen();
|
||||||
|
|
||||||
|
expect(domElement.requestFullscreen).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use standard mode', () => {
|
||||||
|
const domElement = jasmine.createSpyObj('el', ['requestFullscreen']);
|
||||||
|
spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement);
|
||||||
|
|
||||||
|
component.enterFullScreen();
|
||||||
|
expect(domElement.requestFullscreen).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use webkit prefix', () => {
|
||||||
|
const domElement = jasmine.createSpyObj('el', ['webkitRequestFullscreen']);
|
||||||
|
spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement);
|
||||||
|
|
||||||
|
component.enterFullScreen();
|
||||||
|
expect(domElement.webkitRequestFullscreen).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use moz prefix', () => {
|
||||||
|
const domElement = jasmine.createSpyObj('el', ['mozRequestFullScreen']);
|
||||||
|
spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement);
|
||||||
|
|
||||||
|
component.enterFullScreen();
|
||||||
|
expect(domElement.mozRequestFullScreen).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use ms prefix', () => {
|
||||||
|
const domElement = jasmine.createSpyObj('el', ['msRequestFullscreen']);
|
||||||
|
spyOn(fixture.nativeElement, 'querySelector').and.returnValue(domElement);
|
||||||
|
|
||||||
|
component.enterFullScreen();
|
||||||
|
expect(domElement.msRequestFullscreen).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Toolbar', () => {
|
describe('Toolbar', () => {
|
||||||
|
|
||||||
|
it('should render fullscreen button', () => {
|
||||||
|
component.allowFullScreen = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(element.querySelector('[data-automation-id="toolbar-fullscreen"]')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render fullscreen button', () => {
|
||||||
|
component.allowFullScreen = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(element.querySelector('[data-automation-id="toolbar-fullscreen"]')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it('should render default download button', () => {
|
it('should render default download button', () => {
|
||||||
component.allowDownload = true;
|
component.allowDownload = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -397,7 +457,7 @@ describe('ViewerComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should Esc button hide the viewer', () => {
|
it('should Esc button hide the viewer', () => {
|
||||||
EventMock.keyDown(27);
|
EventMock.keyUp(27);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
expect(element.querySelector('.adf-viewer-content')).toBeNull();
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
Component, ContentChild, EventEmitter, HostListener,
|
Component, ContentChild, EventEmitter, HostListener, ElementRef,
|
||||||
Input, OnChanges, Output, SimpleChanges, TemplateRef, ViewEncapsulation
|
Input, OnChanges, Output, SimpleChanges, TemplateRef, ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||||
@ -90,6 +90,18 @@ export class ViewerComponent implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
allowShare = false;
|
allowShare = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
allowFullScreen = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
allowNavigate = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
canNavigateBefore = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
canNavigateNext = true;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
allowSidebar = false;
|
allowSidebar = false;
|
||||||
|
|
||||||
@ -129,6 +141,12 @@ export class ViewerComponent implements OnChanges {
|
|||||||
@Output()
|
@Output()
|
||||||
extensionChange = new EventEmitter<string>();
|
extensionChange = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
navigateBefore = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
navigateNext = new EventEmitter();
|
||||||
|
|
||||||
viewerType = 'unknown';
|
viewerType = 'unknown';
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
node: MinimalNodeEntryEntity;
|
node: MinimalNodeEntryEntity;
|
||||||
@ -143,7 +161,7 @@ export class ViewerComponent implements OnChanges {
|
|||||||
private extensions = {
|
private extensions = {
|
||||||
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm'],
|
image: ['png', 'jpg', 'jpeg', 'gif', 'bpm'],
|
||||||
media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'],
|
media: ['wav', 'mp4', 'mp3', 'webm', 'ogg'],
|
||||||
text: ['txt', 'xml', 'js', 'html', 'json'],
|
text: ['txt', 'xml', 'js', 'html', 'json', 'ts'],
|
||||||
pdf: ['pdf']
|
pdf: ['pdf']
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -155,7 +173,8 @@ export class ViewerComponent implements OnChanges {
|
|||||||
constructor(private apiService: AlfrescoApiService,
|
constructor(private apiService: AlfrescoApiService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private location: Location,
|
private location: Location,
|
||||||
private renditionService: RenditionsService) {
|
private renditionService: RenditionsService,
|
||||||
|
private el: ElementRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
isSourceDefined(): boolean {
|
isSourceDefined(): boolean {
|
||||||
@ -354,6 +373,14 @@ export class ViewerComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onNavigateBeforeClick() {
|
||||||
|
this.navigateBefore.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
onNavigateNextClick() {
|
||||||
|
this.navigateNext.next();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* close the viewer
|
* close the viewer
|
||||||
*/
|
*/
|
||||||
@ -409,15 +436,35 @@ export class ViewerComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Litener Keyboard Event
|
* Keyboard event listener
|
||||||
* @param {KeyboardEvent} event
|
* @param {KeyboardEvent} event
|
||||||
*/
|
*/
|
||||||
@HostListener('document:keydown', ['$event'])
|
@HostListener('document:keyup', ['$event'])
|
||||||
handleKeyboardEvent(event: KeyboardEvent) {
|
handleKeyboardEvent(event: KeyboardEvent) {
|
||||||
let key = event.keyCode;
|
const key = event.keyCode;
|
||||||
|
|
||||||
|
// Esc
|
||||||
if (key === 27 && this.overlayMode) { // esc
|
if (key === 27 && this.overlayMode) { // esc
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Left arrow
|
||||||
|
if (key === 37 && this.canNavigateBefore) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.onNavigateBeforeClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right arrow
|
||||||
|
if (key === 39 && this.canNavigateNext) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.onNavigateNextClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+F
|
||||||
|
if (key === 70 && event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.enterFullScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadContent() {
|
downloadContent() {
|
||||||
@ -453,6 +500,26 @@ export class ViewerComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers full screen mode with a main content area displayed.
|
||||||
|
*/
|
||||||
|
enterFullScreen(): void {
|
||||||
|
if (this.allowFullScreen) {
|
||||||
|
const container = this.el.nativeElement.querySelector('.adf-viewer__fullscreen-container');
|
||||||
|
if (container) {
|
||||||
|
if (container.requestFullscreen) {
|
||||||
|
container.requestFullscreen();
|
||||||
|
} else if (container.webkitRequestFullscreen) {
|
||||||
|
container.webkitRequestFullscreen();
|
||||||
|
} else if (container.mozRequestFullScreen) {
|
||||||
|
container.mozRequestFullScreen();
|
||||||
|
} else if (container.msRequestFullscreen) {
|
||||||
|
container.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async displayNodeRendition(nodeId: string) {
|
private async displayNodeRendition(nodeId: string) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user