mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Merge pull request #628 from shamoon/feature/popover-previews
Feature: popover document previews
This commit is contained in:
		@@ -25,7 +25,6 @@
 | 
			
		||||
    "bootstrap": "^4.5.0",
 | 
			
		||||
    "file-saver": "^2.0.5",
 | 
			
		||||
    "ng-bootstrap": "^1.6.3",
 | 
			
		||||
    "ng2-pdf-viewer": "^6.3.2",
 | 
			
		||||
    "ngx-color": "^6.2.0",
 | 
			
		||||
    "ngx-cookie-service": "^10.1.1",
 | 
			
		||||
    "ngx-file-drop": "^10.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,6 @@ import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-v
 | 
			
		||||
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component';
 | 
			
		||||
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component';
 | 
			
		||||
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component';
 | 
			
		||||
import { PdfViewerModule } from 'ng2-pdf-viewer';
 | 
			
		||||
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component';
 | 
			
		||||
import { YesNoPipe } from './pipes/yes-no.pipe';
 | 
			
		||||
import { FileSizePipe } from './pipes/file-size.pipe';
 | 
			
		||||
@@ -144,7 +143,6 @@ registerLocaleData(localeRo)
 | 
			
		||||
    ReactiveFormsModule,
 | 
			
		||||
    NgxFileDropModule,
 | 
			
		||||
    InfiniteScrollModule,
 | 
			
		||||
    PdfViewerModule,
 | 
			
		||||
    NgSelectModule,
 | 
			
		||||
    ColorSliderModule
 | 
			
		||||
  ],
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,4 @@
 | 
			
		||||
<app-page-header [(title)]="title">
 | 
			
		||||
    <div class="input-group input-group-sm mr-5 d-none d-md-flex" *ngIf="getContentType() == 'application/pdf' && !useNativePdfViewer">
 | 
			
		||||
      <div class="input-group-prepend">
 | 
			
		||||
        <div class="input-group-text" i18n>Page</div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages" [(ngModel)]="previewCurrentPage" />
 | 
			
		||||
      <div class="input-group-append">
 | 
			
		||||
        <div class="input-group-text" i18n>of {{previewNumPages}}</div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <button type="button" class="btn btn-sm btn-outline-danger mr-2 ml-auto" (click)="delete()">
 | 
			
		||||
        <svg class="buttonicon" fill="currentColor">
 | 
			
		||||
            <use xlink:href="assets/bootstrap-icons.svg#trash" />
 | 
			
		||||
@@ -135,12 +125,7 @@
 | 
			
		||||
 | 
			
		||||
    <div class="col-md-6 col-xl-8 mb-3">
 | 
			
		||||
        <ng-container *ngIf="getContentType() == 'application/pdf'">
 | 
			
		||||
            <div class="preview-sticky pdf-viewer-container" *ngIf="!useNativePdfViewer ; else nativePdfViewer">
 | 
			
		||||
                <pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer>
 | 
			
		||||
            </div>
 | 
			
		||||
            <ng-template #nativePdfViewer>
 | 
			
		||||
                <object [data]="previewUrl | safe" type="application/pdf" class="preview-sticky" width="100%"></object>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
          <object [data]="previewUrl | safe" class="preview-sticky" width="100%"></object>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
        <ng-container *ngIf="getContentType() == 'text/plain'">
 | 
			
		||||
            <object [data]="previewUrl | safe" type="text/plain" class="preview-sticky" width="100%"></object>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ import { DocumentService } from 'src/app/services/rest/document.service';
 | 
			
		||||
import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component';
 | 
			
		||||
import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
 | 
			
		||||
import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
 | 
			
		||||
import { PDFDocumentProxy } from 'ng2-pdf-viewer';
 | 
			
		||||
import { ToastService } from 'src/app/services/toast.service';
 | 
			
		||||
import { TextComponent } from '../common/input/text/text.component';
 | 
			
		||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
			
		||||
@@ -61,9 +60,6 @@ export class DocumentDetailComponent implements OnInit {
 | 
			
		||||
    tags: new FormControl([])
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  previewCurrentPage: number = 1
 | 
			
		||||
  previewNumPages: number = 1
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private documentsService: DocumentService,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
@@ -77,10 +73,6 @@ export class DocumentDetailComponent implements OnInit {
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private settings: SettingsService) { }
 | 
			
		||||
 | 
			
		||||
  get useNativePdfViewer(): boolean {
 | 
			
		||||
    return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getContentType() {
 | 
			
		||||
    return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type
 | 
			
		||||
  }
 | 
			
		||||
@@ -226,8 +218,4 @@ export class DocumentDetailComponent implements OnInit {
 | 
			
		||||
    return this.documentListViewService.hasNext(this.documentId)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pdfPreviewLoaded(pdf: PDFDocumentProxy) {
 | 
			
		||||
    this.previewNumPages = pdf.numPages
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable">
 | 
			
		||||
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
 | 
			
		||||
  <div class="row no-gutters">
 | 
			
		||||
    <div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
 | 
			
		||||
      <img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left" [class.inverted]="getIsThumbInverted()">
 | 
			
		||||
@@ -43,19 +43,28 @@
 | 
			
		||||
                <path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
 | 
			
		||||
              </svg> <span class="d-block d-md-inline" i18n>Edit</span>
 | 
			
		||||
            </a>
 | 
			
		||||
            <a class="btn btn-sm btn-outline-secondary" [href]="getPreviewUrl()">
 | 
			
		||||
            <a class="btn btn-sm btn-outline-secondary" [href]="previewUrl" title="Click to view in browser" i18n-title
 | 
			
		||||
            [ngbPopover]="previewContent" [popoverTitle]="document.title | documentTitle"
 | 
			
		||||
            autoClose="true" popoverClass="shadow" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
 | 
			
		||||
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
 | 
			
		||||
                <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
 | 
			
		||||
                <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
 | 
			
		||||
              </svg> <span class="d-block d-md-inline" i18n>View</span>
 | 
			
		||||
            </a>
 | 
			
		||||
            <ng-template #previewContent>
 | 
			
		||||
              <ng-container *ngIf="getContentType() == 'application/pdf'">
 | 
			
		||||
                <object [data]="previewUrl | safe" class="preview" width="100%"></object>
 | 
			
		||||
              </ng-container>
 | 
			
		||||
              <ng-container *ngIf="getContentType() == 'text/plain'">
 | 
			
		||||
                  <object [data]="previewUrl | safe" type="text/plain" class="preview" width="100%"></object>
 | 
			
		||||
              </ng-container>
 | 
			
		||||
            </ng-template>
 | 
			
		||||
            <a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
 | 
			
		||||
              <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
                <path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
 | 
			
		||||
                <path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
 | 
			
		||||
              </svg> <span class="d-block d-md-inline" i18n>Download</span>
 | 
			
		||||
            </a>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          <div *ngIf="searchScore" class="d-flex align-items-center ml-md-auto mt-2 mt-md-0">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
			
		||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
 | 
			
		||||
import { DomSanitizer } from '@angular/platform-browser';
 | 
			
		||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
			
		||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
 | 
			
		||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
			
		||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
			
		||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-document-card-large',
 | 
			
		||||
  templateUrl: './document-card-large.component.html',
 | 
			
		||||
  styleUrls: ['./document-card-large.component.scss']
 | 
			
		||||
  styleUrls: ['./document-card-large.component.scss', '../popover-preview/popover-preview.scss']
 | 
			
		||||
})
 | 
			
		||||
export class DocumentCardLargeComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +43,13 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
			
		||||
  @Input()
 | 
			
		||||
  searchScore: number
 | 
			
		||||
 | 
			
		||||
  @ViewChild('popover') popover: NgbPopover
 | 
			
		||||
 | 
			
		||||
  mouseOnPreview = false
 | 
			
		||||
  popoverHidden = true
 | 
			
		||||
 | 
			
		||||
  metadata: PaperlessDocumentMetadata
 | 
			
		||||
 | 
			
		||||
  get searchScoreClass() {
 | 
			
		||||
    if (this.searchScore > 0.7) {
 | 
			
		||||
      return "success"
 | 
			
		||||
@@ -52,6 +61,9 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.documentService.getMetadata(this.document?.id).subscribe(result => {
 | 
			
		||||
      this.metadata = result
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getIsThumbInverted() {
 | 
			
		||||
@@ -79,7 +91,36 @@ export class DocumentCardLargeComponent implements OnInit {
 | 
			
		||||
    return this.documentService.getDownloadUrl(this.document.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPreviewUrl() {
 | 
			
		||||
  get previewUrl() {
 | 
			
		||||
    return this.documentService.getPreviewUrl(this.document.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getContentType() {
 | 
			
		||||
    return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseEnterPreview() {
 | 
			
		||||
    this.mouseOnPreview = true
 | 
			
		||||
    if (!this.popover.isOpen()) {
 | 
			
		||||
      // we're going to open but hide to pre-load content during hover delay
 | 
			
		||||
      this.popover.open()
 | 
			
		||||
      this.popoverHidden = true
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        if (this.mouseOnPreview) {
 | 
			
		||||
          // show popover
 | 
			
		||||
          this.popoverHidden = false
 | 
			
		||||
        } else {
 | 
			
		||||
          this.popover.close()
 | 
			
		||||
        }
 | 
			
		||||
      }, 600);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseLeavePreview() {
 | 
			
		||||
    this.mouseOnPreview = false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseLeaveCard() {
 | 
			
		||||
    this.popover.close()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<div class="col p-2 h-100">
 | 
			
		||||
  <div class="card h-100 shadow-sm document-card" [class.card-selected]="selected">
 | 
			
		||||
  <div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
 | 
			
		||||
    <div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
 | 
			
		||||
      <img class="card-img doc-img rounded-top" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
 | 
			
		||||
 | 
			
		||||
@@ -37,12 +37,22 @@
 | 
			
		||||
              <path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
          </a>
 | 
			
		||||
          <a [href]="getPreviewUrl()" class="btn btn-sm btn-outline-secondary" title="View in browser" i18n-title>
 | 
			
		||||
          <a [href]="previewUrl" target="_blank" class="btn btn-sm btn-outline-secondary" title="Click to view in browser" i18n-title
 | 
			
		||||
          [ngbPopover]="previewContent" [popoverTitle]="document.title | documentTitle"
 | 
			
		||||
          autoClose="true" popoverClass="shadow" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
 | 
			
		||||
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
 | 
			
		||||
              <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
 | 
			
		||||
              <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
          </a>
 | 
			
		||||
          <ng-template #previewContent>
 | 
			
		||||
            <ng-container *ngIf="getContentType() == 'application/pdf'">
 | 
			
		||||
              <object [data]="previewUrl | safe" class="preview" width="100%"></object>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
            <ng-container *ngIf="getContentType() == 'text/plain'">
 | 
			
		||||
                <object [data]="previewUrl | safe" type="text/plain" class="preview" width="100%"></object>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
          </ng-template>
 | 
			
		||||
          <a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" (click)="$event.stopPropagation()" i18n-title>
 | 
			
		||||
            <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
              <path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 | 
			
		||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
 | 
			
		||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
 | 
			
		||||
import { DocumentService } from 'src/app/services/rest/document.service';
 | 
			
		||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
 | 
			
		||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-document-card-small',
 | 
			
		||||
  templateUrl: './document-card-small.component.html',
 | 
			
		||||
  styleUrls: ['./document-card-small.component.scss']
 | 
			
		||||
  styleUrls: ['./document-card-small.component.scss', '../popover-preview/popover-preview.scss']
 | 
			
		||||
})
 | 
			
		||||
export class DocumentCardSmallComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +17,7 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  selected = false
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  @Output()
 | 
			
		||||
  toggleSelected = new EventEmitter()
 | 
			
		||||
 | 
			
		||||
@@ -30,7 +32,17 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  moreTags: number = null
 | 
			
		||||
 | 
			
		||||
  @ViewChild('popover') popover: NgbPopover
 | 
			
		||||
 | 
			
		||||
  mouseOnPreview = false
 | 
			
		||||
  popoverHidden = true
 | 
			
		||||
 | 
			
		||||
  metadata: PaperlessDocumentMetadata
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.documentService.getMetadata(this.document?.id).subscribe(result => {
 | 
			
		||||
      this.metadata = result
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getIsThumbInverted() {
 | 
			
		||||
@@ -45,7 +57,7 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
			
		||||
    return this.documentService.getDownloadUrl(this.document.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPreviewUrl() {
 | 
			
		||||
  get previewUrl() {
 | 
			
		||||
    return this.documentService.getPreviewUrl(this.document.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -62,4 +74,32 @@ export class DocumentCardSmallComponent implements OnInit {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getContentType() {
 | 
			
		||||
    return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseEnterPreview() {
 | 
			
		||||
    this.mouseOnPreview = true
 | 
			
		||||
    if (!this.popover.isOpen()) {
 | 
			
		||||
      // we're going to open but hide to pre-load content during hover delay
 | 
			
		||||
      this.popover.open()
 | 
			
		||||
      this.popoverHidden = true
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        if (this.mouseOnPreview) {
 | 
			
		||||
          // show popover
 | 
			
		||||
          this.popoverHidden = false
 | 
			
		||||
        } else {
 | 
			
		||||
          this.popover.close()
 | 
			
		||||
        }
 | 
			
		||||
      }, 600);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseLeavePreview() {
 | 
			
		||||
    this.mouseOnPreview = false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mouseLeaveCard() {
 | 
			
		||||
    this.popover.close()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -170,5 +170,5 @@
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
<div class="m-n2 row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'">
 | 
			
		||||
  <app-document-card-small [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)"  [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)"></app-document-card-small>
 | 
			
		||||
  <app-document-card-small [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)"></app-document-card-small>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
::ng-deep .popover {
 | 
			
		||||
  max-width: 40rem;
 | 
			
		||||
 | 
			
		||||
  .preview {
 | 
			
		||||
    min-width: 30rem;
 | 
			
		||||
    min-height: 18rem;
 | 
			
		||||
    max-height: 35rem;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .spinner-border {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 4rem;
 | 
			
		||||
    left: calc(50% - 0.5rem);
 | 
			
		||||
    z-index: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 ::ng-deep .popover-hidden .popover {
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
}
 | 
			
		||||
@@ -78,17 +78,6 @@
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-row form-group">
 | 
			
		||||
          <div class="col-md-3 col-form-label">
 | 
			
		||||
            <span i18n>Document editor</span>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="col">
 | 
			
		||||
 | 
			
		||||
            <app-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></app-input-check>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="form-row form-group">
 | 
			
		||||
          <div class="col-md-3 col-form-label">
 | 
			
		||||
            <span i18n>Dark mode</span>
 | 
			
		||||
@@ -111,11 +100,11 @@
 | 
			
		||||
 | 
			
		||||
      </ng-template>
 | 
			
		||||
    </li>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <li [ngbNavItem]="2">
 | 
			
		||||
      <a ngbNavLink i18n>Notifications</a>
 | 
			
		||||
      <ng-template ngbNavContent>
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        <h4 i18n>Document processing</h4>
 | 
			
		||||
 | 
			
		||||
        <div class="form-row form-group">
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ export class SettingsComponent implements OnInit {
 | 
			
		||||
    'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)),
 | 
			
		||||
    'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)),
 | 
			
		||||
    'darkModeInvertThumbs': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)),
 | 
			
		||||
    'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)),
 | 
			
		||||
    'savedViews': this.savedViewGroup,
 | 
			
		||||
    'displayLanguage': new FormControl(this.settings.getLanguage()),
 | 
			
		||||
    'dateLocale': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_LOCALE)),
 | 
			
		||||
@@ -76,7 +75,6 @@ export class SettingsComponent implements OnInit {
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, (this.settingsForm.value.darkModeInvertThumbs == true).toString())
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale)
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat)
 | 
			
		||||
    this.settings.set(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT, this.settingsForm.value.notificationsConsumerNewDocument)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ export const SETTINGS_KEYS = {
 | 
			
		||||
  DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
 | 
			
		||||
  DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled',
 | 
			
		||||
  DARK_MODE_THUMB_INVERTED: 'general-settings:dark-mode:thumb-inverted',
 | 
			
		||||
  USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer',
 | 
			
		||||
  DATE_LOCALE: 'general-settings:date-display:date-locale',
 | 
			
		||||
  DATE_FORMAT: 'general-settings:date-display:date-format',
 | 
			
		||||
  NOTIFICATIONS_CONSUMER_NEW_DOCUMENT: 'general-settings:notifications:consumer-new-documents',
 | 
			
		||||
@@ -43,7 +42,6 @@ const SETTINGS: PaperlessSettings[] = [
 | 
			
		||||
  {key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
 | 
			
		||||
  {key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false},
 | 
			
		||||
  {key: SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, type: "boolean", default: true},
 | 
			
		||||
  {key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false},
 | 
			
		||||
  {key: SETTINGS_KEYS.DATE_LOCALE, type: "string", default: ""},
 | 
			
		||||
  {key: SETTINGS_KEYS.DATE_FORMAT, type: "string", default: "mediumDate"},
 | 
			
		||||
  {key: SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT, type: "boolean", default: true},
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
$primary-dark-mode: #45973a;
 | 
			
		||||
$danger-dark-mode: #b71631;
 | 
			
		||||
$bg-dark-mode: #161618;
 | 
			
		||||
$bg-dark-mode-accent: #21262d;
 | 
			
		||||
$bg-light-dark-mode: #1c1c1f;
 | 
			
		||||
$text-color-dark-mode: #abb2bf;
 | 
			
		||||
$text-color-dark-mode-accent: lighten($text-color-dark-mode, 10%);
 | 
			
		||||
@@ -384,6 +385,28 @@ $border-color-dark-mode: #47494f;
 | 
			
		||||
    background-color: darken($primary-dark-mode, 5%) !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .popover {
 | 
			
		||||
    .popover-header,
 | 
			
		||||
    .popover-body {
 | 
			
		||||
      background-color: $bg-dark-mode-accent;
 | 
			
		||||
      border-color: $border-color-dark-mode;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $placements: 'top', 'right', 'bottom', 'left';
 | 
			
		||||
 | 
			
		||||
  @each $placement in $placements {
 | 
			
		||||
    .bs-popover-#{$placement} > .arrow::after,
 | 
			
		||||
    .bs-popover-auto[x-placement^=#{$placement}] > .arrow::after {
 | 
			
		||||
      border-#{$placement}-color: $bg-dark-mode-accent;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .bs-popover-bottom .popover-header::before,
 | 
			
		||||
  .bs-popover-auto[x-placement^=bottom] .popover-header::before {
 | 
			
		||||
    border-bottom-color: $bg-dark-mode-accent;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .ngb-dp-header,
 | 
			
		||||
  .ngb-dp-weekdays,
 | 
			
		||||
  .ngb-dp-month {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user