mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
Just save this
[ci skip]
This commit is contained in:
parent
2c28348b56
commit
4070cd0e1b
@ -1,10 +1,14 @@
|
||||
<a [href]="link ?? previewUrl" class="{{linkClasses}}" [target]="linkTarget" [title]="linkTitle"
|
||||
@if (!previewOnly) {
|
||||
<a [href]="link ?? previewUrl" class="{{linkClasses}}" [target]="linkTarget" [title]="linkTitle"
|
||||
[ngbPopover]="previewContent" [popoverTitle]="document.title | documentTitle" container="body"
|
||||
autoClose="true" [popoverClass]="popoverClass" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
|
||||
<ng-content></ng-content>
|
||||
</a>
|
||||
</a>
|
||||
} @else {
|
||||
<ng-container [ngTemplateOutlet]="previewContent" [ngTemplateOutletContext]="{ $implicit: document }"></ng-container>
|
||||
}
|
||||
<ng-template #previewContent>
|
||||
<div class="preview-popup-container" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview(); close()">
|
||||
<div class="preview-popup-container" [class.full-size]="previewOnly" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview(); close()">
|
||||
@if (error) {
|
||||
<div class="w-100 h-100 position-relative">
|
||||
<p class="fst-italic position-absolute top-50 start-50 translate-middle" i18n>Error loading preview</p>
|
||||
|
@ -4,6 +4,16 @@
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.preview-popup-container.full-size {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
|
||||
> * {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .popover.popover-preview {
|
||||
max-width: 32rem;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { NgTemplateOutlet } from '@angular/common'
|
||||
import { HttpClient } from '@angular/common/http'
|
||||
import { Component, Input, OnDestroy, ViewChild } from '@angular/core'
|
||||
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
@ -17,6 +18,7 @@ import { SettingsService } from 'src/app/services/settings.service'
|
||||
styleUrls: ['./preview-popup.component.scss'],
|
||||
imports: [
|
||||
NgbPopoverModule,
|
||||
NgTemplateOutlet,
|
||||
DocumentTitlePipe,
|
||||
PdfViewerModule,
|
||||
SafeUrlPipe,
|
||||
@ -47,6 +49,9 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
@Input()
|
||||
linkTitle: string = $localize`Open preview`
|
||||
|
||||
@Input()
|
||||
previewOnly: boolean = false
|
||||
|
||||
unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
||||
error = false
|
||||
@ -91,6 +96,8 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.error = false
|
||||
this.requiresPassword = false
|
||||
if (this.document.mime_type?.includes('text')) {
|
||||
this.http
|
||||
.get(this.previewURL, { responseType: 'text' })
|
||||
@ -119,6 +126,7 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
mouseEnterPreview() {
|
||||
if (this.previewOnly) return
|
||||
this.mouseOnPreview = true
|
||||
if (!this.popover.isOpen()) {
|
||||
// we're going to open but hide to pre-load content during hover delay
|
||||
@ -136,10 +144,12 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
mouseLeavePreview() {
|
||||
if (this.previewOnly) return
|
||||
this.mouseOnPreview = false
|
||||
}
|
||||
|
||||
public close(immediate: boolean = false) {
|
||||
if (this.previewOnly) return
|
||||
setTimeout(
|
||||
() => {
|
||||
if (!this.mouseOnPreview) this.popover.close()
|
||||
|
@ -27,6 +27,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group flex-fill" role="group">
|
||||
<input type="radio" class="btn-check" [(ngModel)]="list.displayMode" value="table" id="displayModeDetails" name="displayModeDetails">
|
||||
<label for="displayModeDetails" class="btn btn-outline-primary btn-sm">
|
||||
@ -42,6 +43,13 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="btn-group flex-fill" role="group">
|
||||
<input type="checkbox" class="btn-check" [(ngModel)]="list.showPreviewPane" value="table" id="previewPane" name="previewPane">
|
||||
<label for="previewPane" class="btn btn-outline-primary btn-sm">
|
||||
<i-bs name="window-split"></i-bs>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div ngbDropdown class="btn-group flex-fill">
|
||||
<button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle>
|
||||
<i-bs name="arrow-down-up"></i-bs>
|
||||
@ -105,8 +113,9 @@
|
||||
<pngx-bulk-editor [hidden]="!isBulkEditing" [disabled]="!isBulkEditing"></pngx-bulk-editor>
|
||||
</div>
|
||||
|
||||
|
||||
<ng-template #pagination>
|
||||
<div class="row">
|
||||
<div [class.col-lg-6]="list.showPreviewPane" [class.col]="!list.showPreviewPane">
|
||||
<ng-template #pagination>
|
||||
<div class="d-flex flex-wrap gap-3 justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
@if (list.isReloading) {
|
||||
@ -400,3 +409,39 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (list.showPreviewPane) {
|
||||
<div class="col-lg-6">
|
||||
<div class="row">
|
||||
<div class="btn-toolbar mb-1 border-bottom align-items-center">
|
||||
<div class="btn-group pb-3">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" i18n-title title="Previous" (click)="previousDoc()" [disabled]="list.documents.length === 0 || !hasPrevious">
|
||||
<i-bs width="1.2em" height="1.2em" name="arrow-left" class="me-1"></i-bs><ng-container i18n>Previous</ng-container>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" i18n-title title="Next" (click)="nextDoc()" [disabled]="list.documents.length === 0 || !hasNext">
|
||||
<ng-container i18n>Next</ng-container><i-bs width="1.2em" height="1.2em" name="arrow-right" class="ms-1"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group pb-3 ms-auto">
|
||||
<h5 class="mb-0">
|
||||
{{list.firstSelectedDocument?.title}}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col preview-pane">
|
||||
@if (list.selected.size > 0) {
|
||||
<pngx-preview-popup [document]="list.firstSelectedDocument" [previewOnly]="true"></pngx-preview-popup>
|
||||
} @else {
|
||||
<div class="w-100 h-100 position-relative">
|
||||
<p class="fst-italic">
|
||||
<ng-container i18n>No document selected</ng-container>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -80,3 +80,9 @@ a {
|
||||
pngx-page-header .dropdown-menu {
|
||||
--bs-dropdown-min-width: 12em;
|
||||
}
|
||||
|
||||
.preview-pane {
|
||||
height: 60rem;
|
||||
top: 70px;
|
||||
position: sticky;
|
||||
}
|
||||
|
@ -326,25 +326,37 @@ export class DocumentListComponent
|
||||
this.hotKeyService
|
||||
.addShortcut({
|
||||
keys: 'control.arrowleft',
|
||||
description: $localize`Previous page`,
|
||||
description: $localize`Previous page / document`,
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
if (this.list.showPreviewPane) {
|
||||
if (this.hasPrevious) {
|
||||
this.previousDoc()
|
||||
}
|
||||
} else {
|
||||
if (this.list.currentPage > 1) {
|
||||
this.list.currentPage--
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.hotKeyService
|
||||
.addShortcut({
|
||||
keys: 'control.arrowright',
|
||||
description: $localize`Next page`,
|
||||
description: $localize`Next page / document`,
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
if (this.list.showPreviewPane) {
|
||||
if (this.hasNext) {
|
||||
this.nextDoc()
|
||||
}
|
||||
} else {
|
||||
if (this.list.currentPage < this.list.getLastPage()) {
|
||||
this.list.currentPage++
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -473,4 +485,45 @@ export class DocumentListComponent
|
||||
resetFilters() {
|
||||
this.filterEditor.resetSelected()
|
||||
}
|
||||
|
||||
public get hasPrevious(): boolean {
|
||||
return (
|
||||
(this.list.selected.size > 0 &&
|
||||
this.list.documents.indexOf(this.list.firstSelectedDocument) > 0) ||
|
||||
(this.list.selected.size === 0 && this.list.documents.length > 0)
|
||||
)
|
||||
}
|
||||
|
||||
public get hasNext(): boolean {
|
||||
return (
|
||||
(this.list.selected.size > 0 &&
|
||||
this.list.documents.indexOf(this.list.firstSelectedDocument) <
|
||||
this.list.documents.length - 1) ||
|
||||
(this.list.selected.size === 0 && this.list.documents.length > 0)
|
||||
)
|
||||
}
|
||||
|
||||
public nextDoc(): void {
|
||||
const index =
|
||||
this.list.selected.size === 0
|
||||
? 0
|
||||
: Math.min(
|
||||
this.list.documents.indexOf(this.list.firstSelectedDocument) + 1,
|
||||
this.list.documents.length - 1
|
||||
)
|
||||
this.list.selected.clear()
|
||||
this.list.selected.add(this.list.documents[index].id)
|
||||
}
|
||||
|
||||
public previousDoc(): void {
|
||||
const index =
|
||||
this.list.selected.size === 0
|
||||
? 0
|
||||
: Math.max(
|
||||
this.list.documents.indexOf(this.list.firstSelectedDocument) - 1,
|
||||
0
|
||||
)
|
||||
this.list.selected.clear()
|
||||
this.list.selected.add(this.list.documents[index].id)
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,11 @@ export interface ListViewState {
|
||||
* The fields to display in the document list.
|
||||
*/
|
||||
displayFields?: DisplayField[]
|
||||
|
||||
/**
|
||||
* Whether the preview pane is shown.
|
||||
*/
|
||||
showPreviewPane?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,6 +170,7 @@ export class DocumentListViewService {
|
||||
sortReverse: true,
|
||||
filterRules: [],
|
||||
selected: new Set<number>(),
|
||||
showPreviewPane: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,6 +457,15 @@ export class DocumentListViewService {
|
||||
this.saveDocumentListView()
|
||||
}
|
||||
|
||||
get showPreviewPane(): boolean {
|
||||
return this.activeListViewState.showPreviewPane
|
||||
}
|
||||
|
||||
set showPreviewPane(show: boolean) {
|
||||
this.activeListViewState.showPreviewPane = show
|
||||
this.saveDocumentListView()
|
||||
}
|
||||
|
||||
private saveDocumentListView() {
|
||||
if (this._activeSavedViewId == null) {
|
||||
let savedState: ListViewState = {
|
||||
@ -461,6 +476,7 @@ export class DocumentListViewService {
|
||||
sortReverse: this.activeListViewState.sortReverse,
|
||||
displayMode: this.activeListViewState.displayMode,
|
||||
displayFields: this.activeListViewState.displayFields,
|
||||
showPreviewPane: this.activeListViewState.showPreviewPane,
|
||||
}
|
||||
localStorage.setItem(
|
||||
DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG,
|
||||
@ -626,4 +642,8 @@ export class DocumentListViewService {
|
||||
documentIndexInCurrentView(documentID: number): number {
|
||||
return this.documents.map((d) => d.id).indexOf(documentID)
|
||||
}
|
||||
|
||||
get firstSelectedDocument(): Document {
|
||||
return this.documents.find((d) => this.selected.has(d.id))
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ import {
|
||||
trash,
|
||||
uiRadios,
|
||||
upcScan,
|
||||
windowSplit,
|
||||
windowStack,
|
||||
x,
|
||||
xCircle,
|
||||
@ -323,6 +324,7 @@ const icons = {
|
||||
trash,
|
||||
uiRadios,
|
||||
upcScan,
|
||||
windowSplit,
|
||||
windowStack,
|
||||
x,
|
||||
xCircle,
|
||||
|
Loading…
x
Reference in New Issue
Block a user