Pull request #79: Watermark screen

Merge in RED/ui from watermark to master

* commit 'f240a4b2bafe0813d7c10217387b00b00b076053':
  Watermark
This commit is contained in:
Timo Bejan 2021-01-06 14:23:55 +01:00
commit fbaf76e4f2
12 changed files with 221 additions and 59 deletions

View File

@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, ViewContainerRef } from '@angular/core';
import { AppLoadStateService } from './utils/app-load-state.service';
declare var ace;
@ -11,5 +11,5 @@ ace.config.set('basePath', '/assets/ace-builds/');
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(public appLoadStateService: AppLoadStateService) {}
constructor(public appLoadStateService: AppLoadStateService, public viewContainerRef: ViewContainerRef) {}
}

View File

@ -98,6 +98,7 @@ import { HtmlDebugScreenComponent } from './screens/html-debug-screen/html-debug
import { ReportDownloadBtnComponent } from './components/buttons/report-download-btn/report-download-btn.component';
import { ProjectListingActionsComponent } from './screens/project-listing-screen/project-listing-actions/project-listing-actions.component';
import { HasScrollbarDirective } from './utils/has-scrollbar.directive';
import { MatSliderModule } from '@angular/material/slider';
import { PendingChangesGuard } from './utils/can-deactivate.guard';
export function HttpLoaderFactory(httpClient: HttpClient) {
@ -223,6 +224,7 @@ const matImports = [
MatToolbarModule,
MatButtonModule,
MatSlideToggleModule,
MatSliderModule,
MatMenuModule,
MatIconModule,
MatTooltipModule,

View File

@ -41,6 +41,7 @@ redaction-table-col-name::ng-deep {
}
.dict-name {
z-index: 1;
max-width: 100%;
}

View File

@ -18,13 +18,23 @@
<div class="left-container">
<div class="overlay-shadow"></div>
<div #viewer class="viewer"></div>
<div class="changes-box" *ngIf="changed">
<redaction-icon-button
*ngIf="permissionsService.isAdmin()"
[disabled]="configForm.invalid"
icon="red:check-alt"
(action)="save()"
text="watermark-screen.action.save"
type="primary"
></redaction-icon-button>
<div *ngIf="permissionsService.isAdmin()" (click)="revert()" translate="watermark-screen.action.revert" class="all-caps-label cancel"></div>
</div>
</div>
<div class="right-container">
<div class="heading-xl" [translate]="'watermark-screen.title'"></div>
<form [formGroup]="configForm" (keyup)="configChanged()">
<div class="red-input-group required w-300">
<label translate="watermark-screen.form.text"></label>
<div class="red-input-group w-300">
<textarea
redactionHasScrollbar
formControlName="text"
@ -32,11 +42,36 @@
class="w-full"
name="text"
type="text"
rows="10"
rows="4"
></textarea>
</div>
<div class="red-input-group required w-150">
<label translate="watermark-screen.form.color"></label>
<div class="red-input-group">
<label class="all-caps-label mb-8" translate="watermark-screen.form.orientation"></label>
<div class="square-options">
<div
[class.active]="configForm.get('orientation').value === option"
[ngClass]="option"
(click)="configForm.get('orientation').setValue(option); configChanged()"
*ngFor="let option of ['horizontal', 'diagonal', 'vertical']"
>
<span>ABC</span>
</div>
</div>
</div>
<div class="red-input-group">
<label class="all-caps-label" translate="watermark-screen.form.font-size"></label>
<mat-slider formControlName="fontSize" min="5" max="50" color="primary" (change)="configChanged()"></mat-slider>
</div>
<div class="red-input-group">
<label class="all-caps-label" translate="watermark-screen.form.opacity"></label>
<mat-slider formControlName="opacity" min="1" color="primary" (change)="configChanged()"></mat-slider>
</div>
<div class="red-input-group w-150">
<label class="all-caps-label mb-5" translate="watermark-screen.form.color"></label>
<input
formControlName="hexColor"
class="hex-color-input"
@ -49,6 +84,8 @@
[style.background]="configForm.get('hexColor').value"
[colorPicker]="configForm.get('hexColor').value"
[cpOutputFormat]="'hex'"
[cpPosition]="'top-right'"
[cpUseRootViewContainer]="true"
(colorPickerChange)="configForm.get('hexColor').setValue($event); configChanged()"
>
<mat-icon
@ -58,24 +95,26 @@
</div>
</div>
<div class="red-input-group required w-75">
<label translate="watermark-screen.form.opacity"></label>
<input formControlName="opacity" name="opacity" type="number" (change)="configChanged()" />
</div>
<div class="red-input-group required w-75">
<label translate="watermark-screen.form.font-size"></label>
<input formControlName="fontSize" name="fontSize" type="number" (change)="configChanged()" />
<div class="red-input-group">
<label class="all-caps-label mb-8" translate="watermark-screen.form.font-type"></label>
<div class="square-options">
<div
[class.active]="configForm.get('fontType').value === option.value"
[ngClass]="option.value"
(click)="configForm.get('fontType').setValue(option.value); configChanged()"
*ngFor="
let option of [
{ value: 'sans-serif', display: 'Sans Serif' },
{ value: 'serif', display: 'Serif' },
{ value: 'monospace', display: 'Mono' }
]
"
>
{{ option.display }}
</div>
</div>
</div>
</form>
<div class="actions-container">
<button color="primary" mat-flat-button (click)="save()" [disabled]="configForm.invalid || !changed">
{{ 'watermark-screen.action.save' | translate }}
</button>
<div class="all-caps-label pointer cancel" [class.disabled]="!changed" (click)="revert()" translate="watermark-screen.action.revert"></div>
</div>
</div>
</div>
</section>

View File

@ -1,9 +1,19 @@
@import '../../../../assets/styles/red-variables';
.red-content-inner {
flex-direction: row-reverse;
}
.left-container {
width: calc(100vw - 353px);
.viewer {
height: 100%;
}
.changes-box {
right: 40px;
}
}
.right-container {
@ -11,24 +21,62 @@
flex-direction: column;
width: 353px;
min-width: 353px;
padding: 25px;
overflow: visible;
&:hover {
overflow: visible;
}
padding: 24px;
border-left: none;
border-right: 1px solid $separator;
.heading-xl {
margin-bottom: 24px;
}
.actions-container {
.square-options {
display: flex;
align-items: center;
margin-top: 24px;
button:first-child {
margin-right: 8px;
> div {
width: 60px;
height: 60px;
border-radius: 8px;
background-color: $grey-6;
color: $grey-7;
align-items: center;
justify-content: center;
text-align: center;
display: flex;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: darken($grey-6, 2);
}
&:not(:last-child) {
margin-right: 8px;
}
&.active {
background-color: $primary;
color: $white;
}
&.serif {
font-family: Georgia, serif;
}
&.monospace {
font-family: monospace;
}
&.diagonal {
> span {
transform: rotate(-45deg);
}
}
&.vertical {
> span {
transform: rotate(-90deg);
}
}
}
}
}
@ -37,3 +85,11 @@
max-width: 150px;
width: 150px;
}
.mb-5 {
margin-bottom: 5px;
}
.mb-8 {
margin-bottom: 8px;
}

View File

@ -75,12 +75,12 @@ export class WatermarkScreenComponent implements OnInit {
this._watermarkControllerService.getWatermark(DEFAULT_RUL_SET_UUID).subscribe(
(watermark) => {
this._watermark = watermark;
this.configForm.setValue(this._watermark);
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
this._loadViewer();
},
() => {
this._watermark = DEFAULT_WATERMARK;
this.configForm.setValue(this._watermark);
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
this._loadViewer();
}
);
@ -111,7 +111,7 @@ export class WatermarkScreenComponent implements OnInit {
}
public revert() {
this.configForm.setValue(this._watermark);
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
this.configChanged();
}
@ -135,7 +135,7 @@ export class WatermarkScreenComponent implements OnInit {
});
this._disableElements();
this._instance.loadDocument(`${window.location.origin}/assets/pdftron/sample.pdf`);
this._instance.loadDocument(`${window.location.origin}/assets/pdftron/blank.pdf`);
});
}
@ -144,26 +144,48 @@ export class WatermarkScreenComponent implements OnInit {
}
private _drawWatermark() {
const lines = this.configForm.get('text').value.split('\n');
const text = this.configForm.get('text').value;
const lines = text.split('\n');
const fontSize = this.configForm.get('fontSize').value;
const fontType = this.configForm.get('fontType').value;
const orientation = this.configForm.get('orientation').value;
const lineHeight = fontSize + 4;
const opacity = this.configForm.get('opacity').value;
const color = this.configForm.get('hexColor').value;
this._instance.docViewer.setWatermark({
custom: (ctx, pageNumber, pageWidth, pageHeight) => {
ctx.fillStyle = this.configForm.get('hexColor').value;
ctx.font = `${fontSize}pt Arial`;
ctx.globalAlpha = this.configForm.get('opacity').value / 100;
for (let idx = 0; idx < lines.length; ++idx) {
ctx.save();
ctx.translate(pageWidth / 2 - (lineHeight * (lines.length - 1)) / 4, pageHeight / 2 - (lineHeight * lines.length) / 2);
ctx.rotate(-Math.PI / 4);
ctx.translate(0, 10);
ctx.fillText(lines[idx], 0, idx * lineHeight);
ctx.restore();
if (orientation === 'diagonal') {
this._instance.docViewer.setWatermark({
diagonal: {
text,
fontSize: fontSize,
fontFamily: fontType,
color,
opacity
}
}
});
});
} else {
this._instance.docViewer.setWatermark({
custom: (ctx, pageNumber, pageWidth, pageHeight) => {
ctx.fillStyle = color;
ctx.font = `${fontSize}pt ${fontType}`;
ctx.globalAlpha = opacity / 100;
for (let idx = 0; idx < lines.length; ++idx) {
ctx.save();
if (orientation === 'horizontal') {
ctx.translate(pageWidth / 2, pageHeight / 2 - (lineHeight * lines.length) / 2);
}
if (orientation === 'vertical') {
ctx.translate(pageWidth / 2, 0);
ctx.rotate(-Math.PI / 2);
ctx.translate(-pageHeight / 2, -(lineHeight * lines.length) / 2);
}
ctx.fillText(lines[idx], 0, idx * lineHeight);
ctx.restore();
}
}
});
}
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([0], 0);
@ -174,7 +196,9 @@ export class WatermarkScreenComponent implements OnInit {
text: [null, Validators.required],
hexColor: [null, Validators.required],
opacity: [null, Validators.required],
fontSize: [null, Validators.required]
fontSize: [null, Validators.required],
fontType: [null, Validators.required],
orientation: [null, Validators.required]
});
}
}

View File

@ -627,18 +627,20 @@
},
"watermark-screen": {
"form": {
"text": "Text",
"text-placeholder": "Enter text",
"opacity": "Opacity",
"color": "Color",
"font-size": "Font Size"
"font-size": "Font Size",
"font-type": "Font Type",
"orientation": "Orientation"
},
"action": {
"save": "Save",
"save": "Save Changes",
"revert": "Revert",
"success": "Watermark updated!",
"error": "Failed to update Watermark"
},
"title": "Configure Watermark"
"title": "Watermark"
},
"dictionaries": "Dictionaries",
"user-management": "User Management",

Binary file not shown.

View File

@ -70,6 +70,7 @@ body {
.left-container {
overflow: hidden;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
position: relative;
&.extended {
width: calc(100vw - 60px) !important;

View File

@ -0,0 +1,36 @@
@import 'red-variables';
.mat-slider-horizontal {
width: 140px;
height: 32px !important;
.mat-slider-wrapper {
left: 0 !important;
top: 16px !important;
}
.mat-slider-track-wrapper,
.mat-slider-track-fill {
height: 6px !important;
border-radius: 3px;
}
.mat-slider-track-background {
height: 4px !important;
margin-top: 1px;
border-radius: 3px;
background-color: $grey-4 !important;
}
.mat-slider-focus-ring {
display: none;
}
}
.mat-slider-thumb {
width: 16px !important;
height: 16px !important;
border-width: 0 !important;
transform: none !important;
background-color: $primary !important;
}

View File

@ -23,3 +23,4 @@
@import 'red-grid';
@import 'red-breadcrumbs';
@import 'red-editor';
@import 'red-slider';