mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Merge branch 'dev' into pdf-viewer-mobile-improvements
This commit is contained in:
@@ -1,9 +1,18 @@
|
||||
<app-page-header [(title)]="title">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger mr-2" (click)="delete()">
|
||||
<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" />
|
||||
</svg>
|
||||
<span class="d-none d-lg-inline"> Delete</span>
|
||||
</svg> <span class="d-none d-lg-inline" i18n>Delete</span>
|
||||
</button>
|
||||
|
||||
<div class="btn-group mr-2">
|
||||
@@ -11,25 +20,28 @@
|
||||
<a [href]="downloadUrl" class="btn btn-sm btn-outline-primary">
|
||||
<svg class="buttonicon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#download" />
|
||||
</svg>
|
||||
<span class="d-none d-lg-inline"> Download</span>
|
||||
</svg> <span class="d-none d-lg-inline" i18n>Download</span>
|
||||
</a>
|
||||
|
||||
<div class="btn-group" ngbDropdown role="group" *ngIf="metadata?.has_archive_version">
|
||||
<button class="btn btn-sm btn-outline-primary dropdown-toggle-split" ngbDropdownToggle></button>
|
||||
<div class="dropdown-menu shadow" ngbDropdownMenu>
|
||||
<a ngbDropdownItem [href]="downloadOriginalUrl">Download original</a>
|
||||
<a ngbDropdownItem [href]="downloadOriginalUrl" i18n>Download original</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-primary mr-2" (click)="moreLike()">
|
||||
<svg class="buttonicon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#three-dots" />
|
||||
</svg> <span class="d-none d-lg-inline" i18n>More like this</span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="close()">
|
||||
<svg class="buttonicon" fill="currentColor">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#x" />
|
||||
</svg>
|
||||
<span class="d-none d-lg-inline"> Close</span>
|
||||
</svg> <span class="d-none d-lg-inline" i18n>Close</span>
|
||||
</button>
|
||||
</app-page-header>
|
||||
|
||||
@@ -41,27 +53,23 @@
|
||||
|
||||
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
||||
<li [ngbNavItem]="1">
|
||||
<a ngbNavLink>Details</a>
|
||||
<a ngbNavLink i18n>Details</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<app-input-text title="Title" formControlName="title"></app-input-text>
|
||||
<div class="form-group">
|
||||
<label for="archive_serial_number">Archive Serial Number</label>
|
||||
<input type="number" class="form-control" id="archive_serial_number"
|
||||
formControlName='archive_serial_number'>
|
||||
</div>
|
||||
<app-input-date-time titleDate="Date created" formControlName="created"></app-input-date-time>
|
||||
<app-input-select [items]="correspondents" title="Correspondent" formControlName="correspondent"
|
||||
allowNull="true" (createNew)="createCorrespondent()"></app-input-select>
|
||||
<app-input-select [items]="documentTypes" title="Document type" formControlName="document_type"
|
||||
allowNull="true" (createNew)="createDocumentType()"></app-input-select>
|
||||
<app-input-tags formControlName="tags" title="Tags"></app-input-tags>
|
||||
<app-input-text #inputTitle i18n-title title="Title" formControlName="title" [error]="error?.title"></app-input-text>
|
||||
<app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number>
|
||||
<app-input-date i18n-title title="Date created" formControlName="created" [error]="error?.created"></app-input-date>
|
||||
<app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true"
|
||||
(createNew)="createCorrespondent($event)" [suggestions]="suggestions?.correspondents"></app-input-select>
|
||||
<app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true"
|
||||
(createNew)="createDocumentType($event)" [suggestions]="suggestions?.document_types"></app-input-select>
|
||||
<app-input-tags formControlName="tags" [suggestions]="suggestions?.tags"></app-input-tags>
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="2">
|
||||
<a ngbNavLink>Content</a>
|
||||
<a ngbNavLink i18n>Content</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" id="content" rows="20" formControlName='content'></textarea>
|
||||
@@ -70,93 +78,48 @@
|
||||
</li>
|
||||
|
||||
<li [ngbNavItem]="3">
|
||||
<a ngbNavLink>Metadata</a>
|
||||
<a ngbNavLink i18n>Metadata</a>
|
||||
<ng-template ngbNavContent>
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Date modified</td>
|
||||
<td>{{document.modified | date:'medium'}}</td>
|
||||
<td i18n>Date modified</td>
|
||||
<td>{{document.modified | customDate}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Date added</td>
|
||||
<td>{{document.added | date:'medium'}}</td>
|
||||
<td i18n>Date added</td>
|
||||
<td>{{document.added | customDate}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Media filename</td>
|
||||
<td i18n>Media filename</td>
|
||||
<td>{{metadata?.media_filename}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Original MD5 Checksum</td>
|
||||
<td i18n>Original MD5 checksum</td>
|
||||
<td>{{metadata?.original_checksum}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Original file size</td>
|
||||
<td i18n>Original file size</td>
|
||||
<td>{{metadata?.original_size | fileSize}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Original mime type</td>
|
||||
<td i18n>Original mime type</td>
|
||||
<td>{{metadata?.original_mime_type}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="metadata?.has_archive_version">
|
||||
<td>Archive MD5 Checksum</td>
|
||||
<td i18n>Archive MD5 checksum</td>
|
||||
<td>{{metadata?.archive_checksum}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="metadata?.has_archive_version">
|
||||
<td>Archive file size</td>
|
||||
<td i18n>Archive file size</td>
|
||||
<td>{{metadata?.archive_size | fileSize}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h6 *ngIf="metadata?.original_metadata.length > 0">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm mr-2"
|
||||
(click)="expandOriginalMetadata = !expandOriginalMetadata" aria-controls="collapseExample">
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="!expandOriginalMetadata">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-down" />
|
||||
</svg>
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="expandOriginalMetadata">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-up" />
|
||||
</svg>
|
||||
</button>
|
||||
Original document metadata
|
||||
</h6>
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="!expandOriginalMetadata">
|
||||
<table class="table table-borderless">
|
||||
<tbody>
|
||||
<tr *ngFor="let m of metadata?.original_metadata">
|
||||
<td>{{m.prefix}}:{{m.key}}</td>
|
||||
<td>{{m.value}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h6 *ngIf="metadata?.has_archive_version && metadata?.archive_metadata.length > 0">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm mr-2"
|
||||
(click)="expandArchivedMetadata = !expandArchivedMetadata" aria-controls="collapseExample">
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="!expandArchivedMetadata">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-down" />
|
||||
</svg>
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="expandArchivedMetadata">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-up" />
|
||||
</svg>
|
||||
</button>
|
||||
Archived document metadata
|
||||
</h6>
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="!expandArchivedMetadata">
|
||||
<table class="table table-borderless">
|
||||
<tbody>
|
||||
<tr *ngFor="let m of metadata?.archive_metadata">
|
||||
<td>{{m.prefix}}:{{m.key}}</td>
|
||||
<td>{{m.value}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<app-metadata-collapse i18n-title title="Original document metadata" [metadata]="metadata.original_metadata" *ngIf="metadata?.original_metadata?.length > 0"></app-metadata-collapse>
|
||||
<app-metadata-collapse i18n-title title="Archived document metadata" [metadata]="metadata.archive_metadata" *ngIf="metadata?.archive_metadata?.length > 0"></app-metadata-collapse>
|
||||
|
||||
</ng-template>
|
||||
</li>
|
||||
@@ -173,16 +136,24 @@
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="discard()">Discard</button>
|
||||
<button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()">Save & edit
|
||||
next</button>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n [disabled]="networkActive">Discard</button>
|
||||
<button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n [disabled]="networkActive">Save & next</button>
|
||||
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xl-8 mb-3 d-none d-md-block" #pdfPreview>
|
||||
<div class="pdf-viewer-container" *ngIf="getContentType() == 'application/pdf'">
|
||||
<pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true"></pdf-viewer>
|
||||
</div>
|
||||
<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" class="preview-sticky" width="100%"></object>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="getContentType() == 'text/plain'">
|
||||
<object [data]="previewUrl | safe" type="text/plain" class="preview-sticky" width="100%"></object>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,9 @@
|
||||
.pdf-viewer-container {
|
||||
.preview-sticky {
|
||||
height: calc(100vh - 160px);
|
||||
top: 70px;
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.pdf-viewer-container {
|
||||
background-color: gray;
|
||||
}
|
||||
|
@@ -1,21 +1,26 @@
|
||||
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
|
||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||
import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
|
||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
|
||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe';
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
|
||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
|
||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
|
||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { DeleteDialogComponent } from '../common/delete-dialog/delete-dialog.component';
|
||||
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';
|
||||
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions';
|
||||
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type';
|
||||
|
||||
@Component({
|
||||
selector: 'app-document-detail',
|
||||
@@ -24,12 +29,21 @@ import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/do
|
||||
})
|
||||
export class DocumentDetailComponent implements OnInit {
|
||||
|
||||
public expandOriginalMetadata = false;
|
||||
public expandArchivedMetadata = false;
|
||||
@ViewChild("inputTitle")
|
||||
titleInput: TextComponent
|
||||
|
||||
expandOriginalMetadata = false
|
||||
expandArchivedMetadata = false
|
||||
|
||||
error: any
|
||||
|
||||
networkActive = false
|
||||
|
||||
documentId: number
|
||||
document: PaperlessDocument
|
||||
metadata: PaperlessDocumentMetadata
|
||||
suggestions: PaperlessDocumentSuggestions
|
||||
|
||||
title: string
|
||||
previewUrl: string
|
||||
downloadUrl: string
|
||||
@@ -48,6 +62,9 @@ export class DocumentDetailComponent implements OnInit {
|
||||
tags: new FormControl([])
|
||||
})
|
||||
|
||||
previewCurrentPage: number = 1
|
||||
previewNumPages: number = 1
|
||||
|
||||
@ViewChild('nav') nav: NgbNav
|
||||
@ViewChild('pdfPreview') set pdfPreview(element) {
|
||||
// this gets called when compontent added or removed from DOM
|
||||
@@ -66,7 +83,13 @@ export class DocumentDetailComponent implements OnInit {
|
||||
private modalService: NgbModal,
|
||||
private openDocumentService: OpenDocumentsService,
|
||||
private documentListViewService: DocumentListViewService,
|
||||
private titleService: Title) { }
|
||||
private documentTitlePipe: DocumentTitlePipe,
|
||||
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
|
||||
@@ -85,6 +108,7 @@ export class DocumentDetailComponent implements OnInit {
|
||||
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
|
||||
this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
|
||||
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
|
||||
this.suggestions = null
|
||||
if (this.openDocumentService.getOpenDocument(this.documentId)) {
|
||||
this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
|
||||
} else {
|
||||
@@ -99,17 +123,24 @@ export class DocumentDetailComponent implements OnInit {
|
||||
|
||||
updateComponent(doc: PaperlessDocument) {
|
||||
this.document = doc
|
||||
this.titleService.setTitle(`${doc.title} - ${environment.appTitle}`)
|
||||
this.documentsService.getMetadata(doc.id).subscribe(result => {
|
||||
this.metadata = result
|
||||
}, error => {
|
||||
this.metadata = null
|
||||
})
|
||||
this.title = doc.title
|
||||
this.documentsService.getSuggestions(doc.id).subscribe(result => {
|
||||
this.suggestions = result
|
||||
}, error => {
|
||||
this.suggestions = null
|
||||
})
|
||||
this.title = this.documentTitlePipe.transform(doc.title)
|
||||
this.documentForm.patchValue(doc)
|
||||
}
|
||||
|
||||
createDocumentType() {
|
||||
createDocumentType(newName: string) {
|
||||
var modal = this.modalService.open(DocumentTypeEditDialogComponent, {backdrop: 'static'})
|
||||
modal.componentInstance.dialogMode = 'create'
|
||||
if (newName) modal.componentInstance.object = { name: newName }
|
||||
modal.componentInstance.success.subscribe(newDocumentType => {
|
||||
this.documentTypeService.listAll().subscribe(documentTypes => {
|
||||
this.documentTypes = documentTypes.results
|
||||
@@ -118,9 +149,10 @@ export class DocumentDetailComponent implements OnInit {
|
||||
})
|
||||
}
|
||||
|
||||
createCorrespondent() {
|
||||
createCorrespondent(newName: string) {
|
||||
var modal = this.modalService.open(CorrespondentEditDialogComponent, {backdrop: 'static'})
|
||||
modal.componentInstance.dialogMode = 'create'
|
||||
if (newName) modal.componentInstance.object = { name: newName }
|
||||
modal.componentInstance.success.subscribe(newCorrespondent => {
|
||||
this.correspondentService.listAll().subscribe(correspondents => {
|
||||
this.correspondents = correspondents.results
|
||||
@@ -138,55 +170,76 @@ export class DocumentDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.networkActive = true
|
||||
this.documentsService.update(this.document).subscribe(result => {
|
||||
this.close()
|
||||
this.networkActive = false
|
||||
this.error = null
|
||||
}, error => {
|
||||
this.networkActive = false
|
||||
this.error = error.error
|
||||
})
|
||||
}
|
||||
|
||||
saveEditNext() {
|
||||
this.networkActive = true
|
||||
this.documentsService.update(this.document).subscribe(result => {
|
||||
this.error = null
|
||||
this.documentListViewService.getNext(this.document.id).subscribe(nextDocId => {
|
||||
this.networkActive = false
|
||||
if (nextDocId) {
|
||||
this.openDocumentService.closeDocument(this.document)
|
||||
this.router.navigate(['documents', nextDocId])
|
||||
this.titleInput.focus()
|
||||
}
|
||||
}, error => {
|
||||
this.networkActive = false
|
||||
})
|
||||
}, error => {
|
||||
this.networkActive = false
|
||||
this.error = error.error
|
||||
})
|
||||
}
|
||||
|
||||
close() {
|
||||
this.openDocumentService.closeDocument(this.document)
|
||||
if (this.documentListViewService.savedViewId) {
|
||||
this.router.navigate(['view', this.documentListViewService.savedViewId])
|
||||
if (this.documentListViewService.activeSavedViewId) {
|
||||
this.router.navigate(['view', this.documentListViewService.activeSavedViewId])
|
||||
} else {
|
||||
this.router.navigate(['documents'])
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
let modal = this.modalService.open(DeleteDialogComponent, {backdrop: 'static'})
|
||||
modal.componentInstance.message = `Do you really want to delete document '${this.document.title}'?`
|
||||
modal.componentInstance.message2 = `The files for this document will be deleted permanently. This operation cannot be undone.`
|
||||
modal.componentInstance.deleteClicked.subscribe(() => {
|
||||
let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
|
||||
modal.componentInstance.title = $localize`Confirm delete`
|
||||
modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?`
|
||||
modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.`
|
||||
modal.componentInstance.btnClass = "btn-danger"
|
||||
modal.componentInstance.btnCaption = $localize`Delete document`
|
||||
modal.componentInstance.confirmClicked.subscribe(() => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
this.documentsService.delete(this.document).subscribe(() => {
|
||||
modal.close()
|
||||
this.close()
|
||||
}, error => {
|
||||
this.toastService.showError($localize`Error deleting document: ${JSON.stringify(error)}`)
|
||||
modal.componentInstance.buttonsEnabled = true
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
moreLike() {
|
||||
this.documentListViewService.quickFilter([{rule_type: FILTER_FULLTEXT_MORELIKE, value: this.documentId.toString()}])
|
||||
}
|
||||
|
||||
hasNext() {
|
||||
return this.documentListViewService.hasNext(this.documentId)
|
||||
}
|
||||
|
||||
previewCreated() {
|
||||
console.log('Preview Created');
|
||||
|
||||
pdfPreviewLoaded(pdf: PDFDocumentProxy) {
|
||||
this.previewNumPages = pdf.numPages
|
||||
}
|
||||
|
||||
mobilePreviewCreated() {
|
||||
console.log('Mobile Preview Created');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,23 @@
|
||||
<h6>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm mr-2"
|
||||
(click)="expand = !expand">
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="!expand">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-down" />
|
||||
</svg>
|
||||
<svg class="buttonicon" fill="currentColor" *ngIf="expand">
|
||||
<use xlink:href="assets/bootstrap-icons.svg#caret-up" />
|
||||
</svg>
|
||||
</button>
|
||||
{{title}}
|
||||
</h6>
|
||||
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="!expand">
|
||||
<table class="table table-borderless">
|
||||
<tbody>
|
||||
<tr *ngFor="let m of metadata">
|
||||
<td>{{m.prefix}}:{{m.key}}</td>
|
||||
<td class="metadata-column">{{m.value}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@@ -0,0 +1,3 @@
|
||||
.metadata-column {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MetadataCollapseComponent } from './metadata-collapse.component';
|
||||
|
||||
describe('MetadataCollapseComponent', () => {
|
||||
let component: MetadataCollapseComponent;
|
||||
let fixture: ComponentFixture<MetadataCollapseComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MetadataCollapseComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MetadataCollapseComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,23 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-metadata-collapse',
|
||||
templateUrl: './metadata-collapse.component.html',
|
||||
styleUrls: ['./metadata-collapse.component.scss']
|
||||
})
|
||||
export class MetadataCollapseComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
expand = false
|
||||
|
||||
@Input()
|
||||
metadata
|
||||
|
||||
@Input()
|
||||
title = $localize`Metadata`
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user