Signature Widget
This commit is contained in:
819
front/app/package-lock.json
generated
819
front/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,8 @@
|
||||
"@ngx-formly/core": "^6.0.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@tinymce/tinymce-angular": "^7.0.0",
|
||||
"fabric": "^5.3.0",
|
||||
"@types/fabric": "^5.3.0",
|
||||
"ngx-bootstrap-icons": "^1.9.1",
|
||||
"ngx-wig": "^15.1.4",
|
||||
"rxjs": "~7.5.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { NgbAccordionModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { fabric } from 'fabric';
|
||||
|
||||
|
||||
export class BaseContractsComponent {
|
||||
@@ -29,12 +29,12 @@ export class ContractsCardComponent extends BaseContractsComponent{
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0">
|
||||
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-1">
|
||||
<ngb-panel>
|
||||
<ng-template ngbPanelTitle>
|
||||
<span>Preview</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent class="overflow-hidden">
|
||||
<ng-template ngbPanelContent>
|
||||
<iframe width="100%"
|
||||
src="/api/v1/contract/print/"
|
||||
onload='javascript:(function(o){o.style.height=o.contentWindow.document.body.scrollHeight+"px";o.style.width=o.contentWindow.document.body.scrollWidth+"px";}(this));' style="height:200px;width:100%;border:none;overflow:hidden;"></iframe>
|
||||
@@ -45,11 +45,11 @@ export class ContractsCardComponent extends BaseContractsComponent{
|
||||
<span>Signature</span>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
|
||||
<signature-drawer></signature-drawer>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
`
|
||||
})
|
||||
export class ContractsSignatureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import { FormlyBootstrapModule } from "@ngx-formly/bootstrap";
|
||||
import { ForeignkeyTypeComponent } from "@common/crud/types/foreignkey.type";
|
||||
import { CrudService } from "@common/crud/crud.service";
|
||||
|
||||
import { NgbAccordionModule, NgbCollapseModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { allIcons, NgxBootstrapIconsModule } from "ngx-bootstrap-icons";
|
||||
import { ContractsCardComponent, ContractsListComponent, ContractsNewComponent, ContractsSignatureComponent } from "../contracts/contracts.component";
|
||||
import {NgbAccordionModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import { AlphaRangeComponent, BlackBlueRangeComponent, SignatureDrawerComponent } from "./signature-drawer/signature-drawer.component";
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -19,6 +21,8 @@ import {NgbAccordionModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
BaseViewModule,
|
||||
ContractsRoutingModule,
|
||||
NgbAccordionModule,
|
||||
NgbCollapseModule,
|
||||
NgxBootstrapIconsModule.pick(allIcons),
|
||||
FormlyModule.forRoot({
|
||||
types: [
|
||||
{ name: 'foreign-key', component: ForeignkeyTypeComponent }
|
||||
@@ -34,7 +38,10 @@ import {NgbAccordionModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
ContractsListComponent,
|
||||
ContractsNewComponent,
|
||||
ContractsCardComponent,
|
||||
ContractsSignatureComponent
|
||||
ContractsSignatureComponent,
|
||||
SignatureDrawerComponent,
|
||||
BlackBlueRangeComponent,
|
||||
AlphaRangeComponent
|
||||
],
|
||||
providers: [CrudService]
|
||||
})
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
canvas {
|
||||
border: solid black 1px;
|
||||
}
|
||||
|
||||
.btn-file {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.btn-file input[type=file] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
font-size: 100px;
|
||||
text-align: right;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
outline: none;
|
||||
cursor: inherit;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
<div class="row align-items-start">
|
||||
<canvas id="signatureCanvas" class="col" width="320" height="320"></canvas>
|
||||
<div class="col-sm-2">
|
||||
<div class="card">
|
||||
<span class="btn btn-light btn-file">
|
||||
<i-bs name="image-fill"></i-bs><input #imageInput type="file" accept="image/png, image/gif, image/jpeg, image/bmp" (change)="addImage($event)">
|
||||
</span>
|
||||
|
||||
<div #collapseImage="ngbCollapse" [(ngbCollapse)]="!isEditImage">
|
||||
<color-range [value]="this.currentColor" (change)="updateColor($event)"></color-range>
|
||||
<alpha-range (change)="updateAlpha($event)"></alpha-range>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-light"
|
||||
(click)="this.toggleDrawing();"
|
||||
[ngClass]="{active: this.isDrawing}"
|
||||
><i-bs name="pencil-fill"></i-bs></button>
|
||||
<div #collapseDrawing="ngbCollapse" [(ngbCollapse)]="!isDrawing">
|
||||
<color-range [value]="this.currentColor" (change)="this.updateColor($event)"></color-range>
|
||||
<label for="thickRange" class="form-label">Thickness</label>
|
||||
<input type="range" class="form-range" #thickRange [value]="this.currentThickness" max="100" (input)="updateThickness(thickRange.value)">
|
||||
<input class="form-control" type="text" #thickInput [value]="this.currentThickness" (change)="updateThickness(thickInput.value)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
(click)="delete()"
|
||||
><i-bs name="eraser-fill"></i-bs></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary">Sign!</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
(click)="clear()"
|
||||
>Clear</button>
|
||||
@@ -0,0 +1,225 @@
|
||||
import {Component, Output, EventEmitter, OnInit, Input} from "@angular/core";
|
||||
import { fabric } from 'fabric';
|
||||
|
||||
@Component({
|
||||
selector: 'signature-drawer',
|
||||
templateUrl: './signature-drawer.component.html',
|
||||
styleUrls:['./signature-drawer.component.css']
|
||||
})
|
||||
export class SignatureDrawerComponent implements OnInit
|
||||
{
|
||||
size = 320;
|
||||
canvas: any;
|
||||
isEditImage = false;
|
||||
isDrawing = false;
|
||||
canDelete = false;
|
||||
currentColor = "rgba(0,0,0,1)";
|
||||
currentAlpha = 1;
|
||||
currentThickness = 4;
|
||||
|
||||
elements : any[] = [];
|
||||
|
||||
ngOnInit() {
|
||||
this.canvas = new fabric.Canvas('signatureCanvas');
|
||||
let self = this;
|
||||
this.canvas.on({
|
||||
'selection:updated': function() {self.handleElement()},
|
||||
'selection:created': function() {self.handleElement()}
|
||||
});
|
||||
}
|
||||
|
||||
toggleDrawing() {
|
||||
if (this.isDrawing) {
|
||||
this.isDrawing = false;
|
||||
this.canvas.isDrawingMode = false;
|
||||
} else {
|
||||
this.canvas.discardActiveObject().renderAll();
|
||||
this.isEditImage = false;
|
||||
this.isDrawing = true;
|
||||
this.canvas.isDrawingMode = true;
|
||||
let brush = this.canvas.freeDrawingBrush;
|
||||
brush.color = this.currentColor;
|
||||
brush.width = this.currentThickness;
|
||||
}
|
||||
|
||||
//this.canvas.freeDrawingBrush.width
|
||||
//this.canvas.freeDrawingBrush.shadow.blur = parseInt(this.value, 10)
|
||||
}
|
||||
|
||||
handleElement(){
|
||||
let selectedObjects = this.canvas.getActiveObject();
|
||||
|
||||
if (selectedObjects.hasOwnProperty("filters")) {
|
||||
this.isEditImage = true;
|
||||
} else {
|
||||
this.isEditImage = false;
|
||||
}
|
||||
}
|
||||
|
||||
updateColor(value: string) {
|
||||
if (typeof value == "object") {
|
||||
return;
|
||||
}
|
||||
this.currentColor = value;
|
||||
|
||||
for (let o of this.getSelectedObjects()) {
|
||||
this.updateColorFilter(o);
|
||||
}
|
||||
|
||||
let brush = this.canvas.freeDrawingBrush;
|
||||
brush.color = this.currentColor;
|
||||
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
updateAlpha(value: number) {
|
||||
if (typeof value == "object") {
|
||||
return;
|
||||
}
|
||||
this.currentAlpha = value;
|
||||
|
||||
for (let o of this.getSelectedObjects()) {
|
||||
o.opacity = this.currentAlpha;
|
||||
}
|
||||
|
||||
this.canvas.renderAll();
|
||||
}
|
||||
|
||||
updateThickness(value: string) {
|
||||
this.currentThickness = +value
|
||||
|
||||
let brush = this.canvas.freeDrawingBrush;
|
||||
brush.width = this.currentThickness;
|
||||
}
|
||||
|
||||
getSelectedObjects(): any[] {
|
||||
let obj = this.canvas.getActiveObject();
|
||||
if (obj === undefined || obj === null) {
|
||||
return []
|
||||
} else if (obj.hasOwnProperty("_objects")) {
|
||||
return obj._objects;
|
||||
} else {
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
|
||||
updateColorFilter(o: any) {
|
||||
if (o.hasOwnProperty("filters")) {
|
||||
for (let f of o.filters) {
|
||||
if (f.type == "BlendColor") {
|
||||
f.color = this.currentColor;
|
||||
}
|
||||
}
|
||||
o.applyFilters()
|
||||
}
|
||||
}
|
||||
|
||||
addImage(event: any) {
|
||||
let file = event.target.files[0]
|
||||
let url = URL.createObjectURL(file);
|
||||
|
||||
let self = this;
|
||||
fabric.Image.fromURL(url, function(img: any) {
|
||||
let scale;
|
||||
if (img.width > img.height) {
|
||||
img.scaleToWidth(self.size, true);
|
||||
scale = self.size / img.width;
|
||||
} else {
|
||||
img.scaleToHeight(self.size, true);
|
||||
scale = self.size / img.height;
|
||||
}
|
||||
|
||||
let resizeFilter = new fabric.Image.filters.Resize(({
|
||||
resizeType: 'sliceHack',
|
||||
scaleX: scale,
|
||||
scaleY: scale
|
||||
}));
|
||||
|
||||
//img.filters.push(resizeFilter);
|
||||
img.filters.push(new fabric.Image.filters.Grayscale());
|
||||
|
||||
// @ts-ignore
|
||||
img.filters.push(new fabric.Image.filters.RemoveColor())
|
||||
img.filters.push(new fabric.Image.filters.BlendColor({
|
||||
color: self.currentColor,
|
||||
mode: 'lighten',
|
||||
}));
|
||||
img.applyFilters();
|
||||
self.canvas.add(img);
|
||||
self.canvas.renderAll();
|
||||
|
||||
self.elements.push(img);
|
||||
event.target.value = "";
|
||||
});
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.canvas.remove(this.canvas.getActiveObject());
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.canvas.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'color-range',
|
||||
template: `
|
||||
<div>
|
||||
<label for="colorRange" class="form-label">Color</label>
|
||||
|
||||
<input type="range" class="form-range" #colorRange [value]="this.currentColorRatio" max="255" (input)="this.updateColor(colorRange.value)">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" #colorInput [value]="this.currentColorRatio" (change)="this.updateColor(colorInput.value)">
|
||||
<span class="input-group-text" #colorShow [style.background-color]="this.value" style="width: 40px"> </span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class BlackBlueRangeComponent implements OnInit
|
||||
{
|
||||
@Output() change = new EventEmitter<string>();
|
||||
|
||||
currentColorRatio = 255;
|
||||
@Input() value = `rgba(0,0,${this.currentColorRatio},1)`;
|
||||
|
||||
ngOnInit() {
|
||||
let rgbArr = this.value.substring(4, this.value.length-1).replace(/ /g, '').split(',');
|
||||
this.currentColorRatio = +rgbArr[2];
|
||||
}
|
||||
|
||||
updateColor(value: string) {
|
||||
this.currentColorRatio = +value;
|
||||
this.value = `rgb(0,0,${this.currentColorRatio})`;
|
||||
|
||||
this.change.emit(this.value)
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'alpha-range',
|
||||
template: `
|
||||
<label for="alphaRange" class="form-label">Opacity</label>
|
||||
<input type="range" class="form-range" #alphaRange [value]="this.currentAlphaRatio" max="100" (input)="this.updateAlpha(alphaRange.value)">
|
||||
<input class="form-control" type="text" #alphaInput [value]="this.currentAlphaRatio" (change)="this.updateAlpha(alphaInput.value)">
|
||||
`
|
||||
})
|
||||
export class AlphaRangeComponent implements OnInit
|
||||
{
|
||||
@Output() change = new EventEmitter<number>();
|
||||
|
||||
currentAlphaRatio = 100;
|
||||
|
||||
@Input() value = this.currentAlphaRatio / 100;
|
||||
|
||||
ngOnInit() {
|
||||
this.currentAlphaRatio = this.value * 100;
|
||||
}
|
||||
|
||||
updateAlpha(value: string) {
|
||||
this.currentAlphaRatio = +value;
|
||||
this.value = this.currentAlphaRatio / 100;
|
||||
|
||||
this.change.emit(this.value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user