cherrypick compare view

This commit is contained in:
Timo 2021-06-03 17:39:32 +03:00
parent 2652ab9cab
commit 32723d2fc0
12 changed files with 537 additions and 81 deletions

View File

@ -10,8 +10,8 @@ import { WatermarkControllerService, WatermarkModelRes } from '@redaction/red-ui
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { ActivatedRoute } from '@angular/router';
import { hexToRgb } from '@utils/functions';
import { BASE_HREF } from '../../../../tokens';
import { stampPDFPage } from '../../../../utils/page-stamper';
export const DEFAULT_WATERMARK: WatermarkModelRes = {
text: null,
@ -169,7 +169,6 @@ export class WatermarkScreenComponent implements OnInit {
responseType: 'blob'
})
.subscribe(blobData => {
console.log('load blank');
this._instance.loadDocument(blobData, { filename: 'blank.pdf' });
});
});
@ -184,57 +183,28 @@ export class WatermarkScreenComponent implements OnInit {
const pdfNet = this._instance.PDFNet;
const document = await this._instance.docViewer.getDocument().getPDFDoc();
await pdfNet.runWithCleanup(
async () => {
await document.lock();
const text = this.configForm.get('text').value || '';
const fontSize = this.configForm.get('fontSize').value;
const fontType = this.configForm.get('fontType').value;
const orientation: WatermarkModelRes.OrientationEnum =
this.configForm.get('orientation').value;
const opacity = this.configForm.get('opacity').value;
const color = this.configForm.get('hexColor').value;
const pageSet = await pdfNet.PageSet.createSinglePage(1);
await pdfNet.Stamper.deleteStamps(document, pageSet);
const text = this.configForm.get('text').value || '';
const fontSize = this.configForm.get('fontSize').value;
const fontType = this.configForm.get('fontType').value;
const orientation: WatermarkModelRes.OrientationEnum =
this.configForm.get('orientation').value;
const opacity = this.configForm.get('opacity').value;
const color = this.configForm.get('hexColor').value;
const rgbColor = hexToRgb(color);
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
await stamper.setFontColor(
await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
);
await stamper.setOpacity(opacity / 100);
switch (orientation) {
case WatermarkModelRes.OrientationEnum.VERTICAL:
await stamper.setAlignment(0, 0);
await stamper.setRotation(-90);
break;
case WatermarkModelRes.OrientationEnum.HORIZONTAL:
break;
case WatermarkModelRes.OrientationEnum.DIAGONAL:
default:
await stamper.setAlignment(0, 0);
await stamper.setRotation(-45);
}
const font = await pdfNet.Font.createAndEmbed(
document,
this._convertFont(fontType)
);
await stamper.setFont(font);
await stamper.setTextAlignment(0);
await stamper.stampText(document, text, pageSet);
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([0], 0);
this._changeDetectorRef.detectChanges();
},
environment.licenseKey ? atob(environment.licenseKey) : null
await stampPDFPage(
document,
pdfNet,
text,
fontSize,
fontType,
orientation,
opacity,
color,
1
);
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([0], 0);
this._changeDetectorRef.detectChanges();
}
private _initForm() {
@ -262,16 +232,4 @@ export class WatermarkScreenComponent implements OnInit {
]
});
}
private _convertFont(fontType: any): number {
switch (fontType) {
case 'times-new-roman':
return 0;
case 'helvetica':
return 4;
case 'courier':
return 8;
}
return 4;
}
}

View File

@ -1,3 +1,34 @@
<div class="page">
<div #viewer [id]="fileStatus.fileId" class="viewer"></div>
</div>
<input
#compareFileInput
(change)="uploadFile($event.target['files'])"
class="file-upload-input"
type="file"
/>
<div class="pagination noselect" *ngIf="totalPages && currentPage">
<div (click)="previousPage()">
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
</div>
<div>
<input
#pageInput
[value]="currentPage"
min="1"
[max]="totalPages"
type="number"
(change)="navigateToPage(pageInput.value)"
class="page-number-input"
/>
</div>
<div class="separator">/</div>
<div>
{{ totalPages }}
</div>
<div (click)="nextPage()">
<mat-icon class="chevron-icon" svgIcon="red:nav-next"></mat-icon>
</div>
</div>

View File

@ -1,3 +1,5 @@
@import '../../../../../assets/styles/red-variables';
.page {
display: flex;
flex-direction: column;
@ -20,3 +22,62 @@
margin-left: 15px;
}
}
.pagination {
position: absolute;
bottom: 20px;
left: 50%;
transform: translate(-50%);
background: $white;
color: $grey-7;
border: 1px solid $grey-7;
border-radius: 8px;
padding: 6px 2px;
display: flex;
justify-content: center;
align-items: center;
> div {
height: 16px;
cursor: default;
}
.separator {
padding-left: 2px;
padding-right: 2px;
}
.page-number-input {
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
color: $grey-7;
text-decoration: none;
outline: none;
border: none;
padding: 0;
margin: 0;
cursor: pointer;
&:hover,
&:focus {
font-weight: bold;
border-bottom: 1px solid $grey-7;
}
}
.chevron-icon {
height: 16px;
transform: rotate(-90deg);
cursor: pointer;
padding-left: 4px;
padding-right: 4px;
&:hover {
color: $grey-1;
}
}
}

View File

@ -26,7 +26,10 @@ import { UserPreferenceService } from '@services/user-preference.service';
import { translateQuads } from '@utils/pdf-coordinates';
import { BASE_HREF } from '../../../../tokens';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { stampPDFPage } from '../../../../utils/page-stamper';
import { LoadingService } from '../../../../services/loading.service';
import TextTool = Tools.TextTool;
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
@Component({
selector: 'redaction-pdf-viewer',
@ -48,6 +51,10 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Output() viewerReady = new EventEmitter<WebViewerInstance>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
@ViewChild('viewer', { static: true }) viewer: ElementRef;
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
viewMode: 'STANDARD' | 'COMPARE' = 'STANDARD';
instance: WebViewerInstance;
private _selectedText = '';
private readonly _allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape'];
@ -56,11 +63,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _translateService: TranslateService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _projectsDialogService: DossiersDialogService,
private readonly _ngZone: NgZone,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _annotationActionsService: AnnotationActionsService,
private readonly _appConfigService: AppConfigService
private readonly _appConfigService: AppConfigService,
private readonly _appLoadStateService: LoadingService
) {}
ngOnInit() {
@ -114,7 +123,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this.instance.annotManager.deselectAnnotations(ann);
}
navigateToPage(pageNumber: number) {
navigateToPage(pageNumber) {
const activePage = this.instance.docViewer.getCurrentPage();
if (activePage !== pageNumber) {
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
@ -124,10 +133,11 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
setInitialViewerState() {
// viewer init
this.instance.setFitMode('FitPage');
const instanceDisplayMode = this.instance.docViewer
.getDisplayModeManager()
.getDisplayMode();
instanceDisplayMode.mode = 'Single';
instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing';
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
}
@ -139,6 +149,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this.instance = await WebViewer(
{
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
fullAPI: true,
path: this._convertPath('/assets/wv-resources'),
css: this._convertPath('/assets/pdftron/stylesheet.css'),
backendType: 'ems'
@ -245,6 +256,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
private _disableElements() {
this.instance.disableElements([
'pageNavOverlay',
'menuButton',
'selectToolButton',
'textHighlightToolButton',
@ -279,8 +291,36 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
img: this._convertPath('/assets/icons/general/rectangle.svg'),
title: 'annotation.rectangle'
});
originalHeaderItems.splice(11, 0, {
type: 'actionButton',
element: 'compare',
dataElement: 'compareButton',
img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'),
title: 'Compare',
onClick: () => {
this.compareFileInput.nativeElement.click();
}
});
originalHeaderItems.splice(11, 0, {
type: 'actionButton',
element: 'closeCompare',
dataElement: 'closeCompareButton',
img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'),
title: 'Leave Compare Mode',
onClick: () => {
this.closeCompareMode();
}
});
originalHeaderItems.splice(13, 0, {
type: 'divider',
dataElement: 'compareToolDivider'
});
console.log(originalHeaderItems);
});
this.instance.disableElements(['closeCompareButton']);
this.instance.docViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({
StrokeThickness: 2,
StrokeColor: this._annotationDrawService.getColor(this.instance, 'manual'),
@ -365,8 +405,8 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
// cleanup selection and button state
this.deselectAllAnnotations();
this.instance.disableElements(['shapeToolGroupButton']);
this.instance.enableElements(['shapeToolGroupButton']);
this.instance.disableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
this.instance.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
// dispatch event
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
@ -480,6 +520,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
'add-rectangle',
'add-false-positive',
'shapeToolGroupButton',
'rectangleToolDivider',
'annotationPopup'
]);
if (this._selectedText.length > 2) {
@ -493,6 +534,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
'add-false-positive',
'add-rectangle',
'shapeToolGroupButton',
'rectangleToolDivider',
'annotationPopup'
]);
}
@ -591,4 +633,202 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
private _convertPath(path: string): string {
return this._baseHref + path;
}
async uploadFile(files: any) {
const fileToCompare = files[0];
const fileReader = new FileReader();
if (fileToCompare) {
fileReader.onload = async () => {
const pdfData = fileReader.result;
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(
environment.licenseKey ? atob(environment.licenseKey) : null
);
const mergedDocument = await PDFNet.PDFDoc.create();
const compareDocument = await PDFNet.PDFDoc.createFromBuffer(<any>pdfData);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(
await this.fileData.arrayBuffer()
);
const currentDocumentPageCount = await currentDocument.getPageCount();
const compareDocumentPageCount = await compareDocument.getPageCount();
const loadCompareDocument = async () => {
this._appLoadStateService.start();
try {
const maxPageCount = Math.max(
currentDocumentPageCount,
compareDocumentPageCount
);
for (let i = 1; i <= maxPageCount; i++) {
if (currentDocumentPageCount >= i) {
await mergedDocument.insertPages(
i * 2,
currentDocument,
i,
i,
PDFNet.PDFDoc.InsertFlag.e_none
);
} else {
const pageToCopy = await compareDocument.getPage(i);
const blankPage = await mergedDocument.pageCreate(
await pageToCopy.getCropBox()
);
await blankPage.setRotation(await pageToCopy.getRotation());
await mergedDocument.pagePushBack(blankPage);
await stampPDFPage(
mergedDocument,
PDFNet,
'<< Compare Placeholder Page >>',
20,
'courier',
'DIAGONAL',
33,
'#ffb83b',
await mergedDocument.getPageCount()
);
}
if (compareDocumentPageCount >= i) {
await mergedDocument.insertPages(
i * 2,
compareDocument,
i,
i,
PDFNet.PDFDoc.InsertFlag.e_none
);
} else {
const pageToCopy = await currentDocument.getPage(i);
const blankPage = await mergedDocument.pageCreate(
await pageToCopy.getCropBox()
);
await blankPage.setRotation(await pageToCopy.getRotation());
await mergedDocument.pagePushBack(blankPage);
await stampPDFPage(
mergedDocument,
PDFNet,
'<< Compare Placeholder Page >>',
20,
'courier',
'DIAGONAL',
33,
'#ffb83b',
await mergedDocument.getPageCount()
);
}
}
const buffer = await mergedDocument.saveMemoryBuffer(
PDFNet.SDFDoc.SaveOptions.e_linearized
);
const mergedDocumentBuffer = new Blob([buffer], {
type: 'application/pdf'
});
this.viewMode = 'COMPARE';
this.instance.loadDocument(mergedDocumentBuffer, {
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
});
this.instance.disableElements(['compareButton']);
this.instance.enableElements(['closeCompareButton']);
this.compareFileInput.nativeElement.value = null;
this.navigateToPage(1);
} catch (e) {}
this._appLoadStateService.stop();
};
if (currentDocumentPageCount !== compareDocumentPageCount) {
this._projectsDialogService.openCompareFileConfirmationDialog(
null,
fileToCompare.name,
currentDocumentPageCount,
compareDocumentPageCount,
loadCompareDocument
);
} else {
loadCompareDocument();
}
};
}
fileReader.readAsArrayBuffer(fileToCompare);
}
get isCompareMode() {
return this.viewMode === 'COMPARE';
}
async closeCompareMode() {
this.viewMode = 'STANDARD';
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(
await this.fileData.arrayBuffer()
);
this.instance.loadDocument(currentDocument, {
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
});
this.instance.disableElements(['closeCompareButton']);
this.instance.enableElements(['compareButton']);
this.navigateToPage(1);
}
get currentPage() {
try {
return this.isCompareMode
? Math.ceil(this.instance?.docViewer?.getCurrentPage() / 2)
: this.instance?.docViewer?.getCurrentPage();
} catch (e) {
return null;
}
}
get totalPages() {
try {
return this.isCompareMode
? Math.ceil(this.instance?.docViewer?.getPageCount() / 2)
: this.instance?.docViewer?.getPageCount();
} catch (e) {
return null;
}
}
private get _currentInternalPage() {
return this.instance?.docViewer?.getCurrentPage();
}
private get _totalInternalPages() {
return this.instance?.docViewer?.getPageCount();
}
previousPage() {
if (this._currentInternalPage > 1) {
this.navigateToPage(Math.max(this._currentInternalPage - this.paginationOffset, 1));
}
}
nextPage() {
if (this._currentInternalPage < this._totalInternalPages) {
this.navigateToPage(
Math.min(
this._currentInternalPage + this.paginationOffset,
this._totalInternalPages
)
);
}
}
get paginationOffset() {
return this.isCompareMode ? 2 : 1;
}
}

View File

@ -77,7 +77,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private _instance: WebViewerInstance;
private _lastPage: string;
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
@ViewChild(PdfViewerComponent) viewerComponent: PdfViewerComponent;
constructor(
readonly appStateService: AppStateService,
@ -127,7 +127,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
get activeViewerPage() {
return this._instance?.docViewer?.getCurrentPage();
return this.viewerComponent?.viewMode === 'STANDARD'
? this._instance?.docViewer?.getCurrentPage()
: this._instance?.docViewer?.getCurrentPage() % 2 === 0
? this._instance?.docViewer?.getCurrentPage() / 2
: (this._instance?.docViewer?.getCurrentPage() + 1) / 2;
}
get canSwitchToRedactedView() {
@ -343,18 +347,20 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
| { annotations: AnnotationWrapper[]; multiSelect: boolean }
) {
if (annotations) {
this._viewerComponent.selectAnnotations(annotations);
this.viewerComponent.selectAnnotations(annotations);
} else {
this._viewerComponent.deselectAllAnnotations();
this.viewerComponent.deselectAllAnnotations();
}
}
deselectAnnotations(annotations: AnnotationWrapper[]) {
this._viewerComponent.deselectAnnotations(annotations);
this.viewerComponent.deselectAnnotations(annotations);
}
selectPage(pageNumber: number) {
this._viewerComponent.navigateToPage(pageNumber);
this.viewerComponent.navigateToPage(
this.viewerComponent.isCompareMode ? pageNumber * 2 - 1 : pageNumber
);
this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always');
}
@ -443,6 +449,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
viewerReady($event: WebViewerInstance) {
this._instance = $event;
this._cleanupAndRedrawManualAnnotations();
this._updateCanPerformActions();
this.viewReady = true;
// Go to initial page from query params
const pageNumber = this._lastPage || this._activatedRoute.snapshot.queryParams.page;
@ -586,7 +593,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private _updateCanPerformActions() {
this.canPerformAnnotationActions =
this.permissionsService.canPerformAnnotationActions() && this.viewMode === 'STANDARD';
this.permissionsService.canPerformAnnotationActions() &&
this.viewMode === 'STANDARD' &&
!this.viewerComponent?.isCompareMode;
}
private async _loadFileData(performUpdate: boolean = false): Promise<void> {
@ -626,7 +635,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._annotationDrawService.drawAnnotations(
this._instance,
this.annotationData.allAnnotations,
this.hideSkipped
this.hideSkipped,
this.viewerComponent.isCompareMode
);
});
}
@ -650,7 +660,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._annotationDrawService.drawAnnotations(
this._instance,
newPageAnnotations,
this.hideSkipped
this.hideSkipped,
this.viewerComponent.isCompareMode
);
}
});

View File

@ -22,11 +22,14 @@ export class AnnotationDrawService {
drawAnnotations(
activeViewer: WebViewerInstance,
annotationWrappers: AnnotationWrapper[],
hideSkipped: boolean = false
hideSkipped: boolean = false,
compareMode: boolean = false
) {
const annotations = [];
annotationWrappers.forEach(annotation => {
annotations.push(this.computeAnnotation(activeViewer, annotation, hideSkipped));
annotations.push(
this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode)
);
});
const annotationManager = activeViewer.annotManager;
@ -91,9 +94,12 @@ export class AnnotationDrawService {
computeAnnotation(
activeViewer: WebViewerInstance,
annotationWrapper: AnnotationWrapper,
hideSkipped: boolean = false
hideSkipped: boolean = false,
compareMode: boolean = false
) {
const pageNumber = annotationWrapper.pageNumber;
const pageNumber = compareMode
? annotationWrapper.pageNumber * 2 - 1
: annotationWrapper.pageNumber;
const highlight = new activeViewer.Annotations.TextHighlightAnnotation();
highlight.PageNumber = pageNumber;
highlight.StrokeColor = this.getColor(

View File

@ -363,4 +363,37 @@ export class DossiersDialogService {
return ref;
}
openCompareFileConfirmationDialog(
$event: MouseEvent,
fileName: string,
currentDocumentPageCount: number,
compareDocumentPageCount: number,
callback?: Function
): MatDialogRef<ConfirmationDialogComponent> {
$event?.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, {
...dialogConfig,
data: new ConfirmationDialogInput({
title: 'confirmation-dialog.compare-file.title',
question: 'confirmation-dialog.compare-file.question',
translateParams: {
fileName: fileName,
currentDocumentPageCount,
compareDocumentPageCount
}
})
});
ref.afterClosed().subscribe(result => {
if (result) {
if (callback) {
callback();
}
}
});
return ref;
}
}

View File

@ -0,0 +1,63 @@
import { hexToRgb } from './functions';
import { environment } from '../../environments/environment';
export async function stampPDFPage(
document: any,
PDFNet: any,
text: string,
fontSize: number,
fontType: string,
orientation: string,
opacity: number,
color: string,
page: number
) {
await PDFNet.runWithCleanup(
async () => {
await document.lock();
const pageSet = await PDFNet.PageSet.createSinglePage(page);
await PDFNet.Stamper.deleteStamps(document, pageSet);
const rgbColor = hexToRgb(color);
const stamper = await PDFNet.Stamper.create(3, fontSize, 0);
await stamper.setFontColor(
await PDFNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
);
await stamper.setOpacity(opacity / 100);
switch (orientation) {
case 'VERTICAL':
await stamper.setAlignment(0, 0);
await stamper.setRotation(-90);
break;
case 'HORIZONTAL':
break;
case 'DIAGONAL':
default:
await stamper.setAlignment(0, 0);
await stamper.setRotation(-45);
}
const font = await PDFNet.Font.createAndEmbed(document, convertFont(fontType));
await stamper.setFont(font);
await stamper.setTextAlignment(0);
await stamper.stampText(document, text, pageSet);
},
environment.licenseKey ? atob(environment.licenseKey) : null
);
}
function convertFont(type: string): number {
switch (type) {
case 'times-new-roman':
return 0;
case 'helvetica':
return 4;
case 'courier':
return 8;
}
return 4;
}

View File

@ -678,6 +678,10 @@
"deny": "Cancel"
},
"confirmation-dialog": {
"compare-file": {
"title": "Compare with file: {{fileName}}",
"question": "<strong>Warning!</strong> <br><br> Number of pages does not match, current document has <strong>{{currentDocumentPageCount}} page(s)</strong>. Uploaded document has <strong>{{compareDocumentPageCount}} page(s)</strong>. <br><br> Do you wish to proceed?"
},
"assign-file-to-me": {
"title": "Re-assign reviewer",
"question": "This document is currently reviewed by someone else. Do you want to become the reviewer and assign yourself to this document?"

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 14 14" width="14px" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Styleguide" stroke="none" stroke-width="1">
<g id="Styleguide-Actions" transform="translate(-62.000000, -609.000000)">
<rect height="906" width="904" x="0" y="0"></rect>
<g id="Group-6" transform="translate(52.000000, 599.000000)">
<rect height="34" id="Rectangle" rx="17" width="34" x="0" y="0"></rect>
<g fill="#868E96" fill-rule="nonzero" id="status" transform="translate(10.000000, 10.000000)">
<path
d="M7.7,2.38 L8.75,3.29 L6.86,5.18 L7.7,5.18 C11.2,5.18 14,8.05 14,11.48 L14,11.48 L12.6,11.48 C12.6,8.75 10.43,6.58 7.7,6.58 L7.7,6.58 L6.86,6.58 L8.75,8.47 L7.77,9.45 L4.2,5.88 L7.7,2.38 Z M3.5,2.38 L4.48,3.36 L1.96,5.88 L4.48,8.47 L3.5,9.45 L0,5.88 L3.5,2.38 Z"
id="Back-to-review"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
viewBox="0 0 18.08 18.08" style="enable-background:new 0 0 18.08 18.08;" xml:space="preserve">
<g fill="#868E96">
<path d="M6.504,10.283c-0.101,0-0.197,0.039-0.269,0.111c-0.071,0.07-0.111,0.167-0.111,0.269l0.008,2.55
H1.026V2.245H8.54v1.36c0.151-0.102,0.334-0.162,0.53-0.162h0.547V1.729c0-0.188-0.153-0.342-0.342-0.342H0.342
C0.154,1.387,0,1.54,0,1.729v12.046c0,0.189,0.153,0.343,0.342,0.343h6.664c0.097,0,0.19-0.042,0.255-0.114l0.853-0.956v-2.764
L6.504,10.283L6.504,10.283z" />
<path d="M5.48,9.182l2.208-1.827c0,0,0.175-0.15,0.018-0.307c-0.216-0.216-2.279-1.77-2.279-1.77
s-0.274-0.276-0.274,0.09c0,0.367,0,0.951,0,0.951s-0.159,0-0.403,0c-0.75,0-2.212,0-2.771,0c0,0-0.131-0.004-0.131,0.166
c0,0.169,0,1.211,0,1.448S2.005,8.13,2.005,8.13c0.575,0,1.988,0,2.765,0c0.272,0,0.448,0,0.448,0s0,0.729,0,1.029
C5.217,9.459,5.48,9.182,5.48,9.182z" />
<path d="M17.738,3.962H8.803c-0.188,0-0.341,0.153-0.341,0.342V16.35c0,0.188,0.153,0.342,0.341,0.342
h6.664c0.098,0,0.19-0.042,0.255-0.114l2.271-2.542c0.057-0.063,0.087-0.144,0.087-0.227V4.305
C18.08,4.116,17.926,3.962,17.738,3.962z M17.003,12.859l-2.037-0.001c-0.001,0-0.001,0-0.001,0c-0.101,0-0.196,0.04-0.268,0.11
s-0.111,0.168-0.11,0.269l0.007,2.55H9.49V4.819h7.514L17.003,12.859L17.003,12.859z" />
<path d="M12.654,12.288c0,0,0.274,0.275,0.274-0.09c0-0.367,0-0.951,0-0.951s0.158,0,0.402,0
c0.751,0,2.212,0,2.771,0c0,0,0.132,0.005,0.132-0.165s0-1.211,0-1.449c0-0.237-0.156-0.196-0.156-0.196c-0.575,0-1.989,0-2.766,0
c-0.271,0-0.448,0-0.448,0s0-0.728,0-1.029c0-0.301-0.262-0.023-0.262-0.023l-2.209,1.826c0,0-0.175,0.15-0.018,0.308
C10.59,10.733,12.654,12.288,12.654,12.288z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -36,3 +36,13 @@
width: 10px;
height: 10px;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}