mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-09 13:35:10 +00:00
Update some global components with loading signals
This commit is contained in:
+42
-7
@@ -1,11 +1,14 @@
|
||||
<pngx-widget-frame
|
||||
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
|
||||
[title]="savedView.name"
|
||||
[badge]="count"
|
||||
[loading]="loading"
|
||||
[loading]="false"
|
||||
[draggable]="savedView"
|
||||
>
|
||||
|
||||
@if (count !== null) {
|
||||
<span title-badge class="badge bg-info text-dark ms-2">{{count}}</span>
|
||||
}
|
||||
|
||||
@if (documents.length) {
|
||||
<a class="btn-link text-decoration-none" header-buttons [routerLink]="[]" (click)="showAll()" i18n>Show all</a>
|
||||
}
|
||||
@@ -30,11 +33,24 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (loading && !documents.length) {
|
||||
@for (row of placeholderRows; track row) {
|
||||
<tr>
|
||||
@for (field of displayFields; track field; let j = $index) {
|
||||
<td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': j > 1 }">
|
||||
<div class="placeholder-glow text-start">
|
||||
<span class="placeholder bg-secondary w-50" [ngStyle]="{ opacity: 1 - (row * 1/placeholderRows.length) }"></span>
|
||||
</div>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
@for (doc of documents; track doc.id; let i = $index) {
|
||||
<tr>
|
||||
@for (field of displayFields; track field; let j = $index) {
|
||||
<td class="py-2 py-md-3 position-relative" [ngClass]="{ 'd-none d-md-table-cell': j > 1 }">
|
||||
@if (loading && show) {
|
||||
@if (loading && !documents.length) {
|
||||
<div class="placeholder-glow text-start">
|
||||
<span class="placeholder bg-secondary w-50" [ngStyle]="{ opacity: 1 - (i * 1/documents.length) }"></span>
|
||||
</div>
|
||||
@@ -115,12 +131,22 @@
|
||||
</table>
|
||||
} @else if (displayMode === DisplayMode.SMALL_CARDS) {
|
||||
<div class="row row-cols-paperless-cards my-n2">
|
||||
@if (loading && !documents.length) {
|
||||
@for (row of placeholderRows; track row) {
|
||||
<pngx-document-card-small
|
||||
class="p-0"
|
||||
[ngStyle]="{ opacity: 1 - (row * 1/placeholderRows.length) }"
|
||||
[document]="null"
|
||||
[displayFields]="displayFields">
|
||||
</pngx-document-card-small>
|
||||
}
|
||||
}
|
||||
@for (d of documents; track d.id; let i = $index) {
|
||||
<pngx-document-card-small
|
||||
class="p-0"
|
||||
[ngStyle]="{ opacity: !loading && show ? 1 : 1 - (i * 1/documents.length) }"
|
||||
[ngStyle]="{ opacity: show ? 1 : 1 - (i * 1/documents.length) }"
|
||||
(dblClickDocument)="openDocumentDetail(d)"
|
||||
[document]="!loading && show ? d : null"
|
||||
[document]="d"
|
||||
[displayFields]="displayFields"
|
||||
(clickTag)="clickTag($event)"
|
||||
(clickCorrespondent)="clickCorrespondent($event)"
|
||||
@@ -131,11 +157,20 @@
|
||||
</div>
|
||||
} @else if (displayMode === DisplayMode.LARGE_CARDS) {
|
||||
<div class="row my-n2">
|
||||
@if (loading && !documents.length) {
|
||||
@for (row of placeholderRows; track row) {
|
||||
<pngx-document-card-large
|
||||
[document]="null"
|
||||
[ngStyle]="{ opacity: 1 - (row * 1/placeholderRows.length) }"
|
||||
[displayFields]="displayFields">
|
||||
</pngx-document-card-large>
|
||||
}
|
||||
}
|
||||
@for (d of documents; track d.id; let i = $index) {
|
||||
<pngx-document-card-large
|
||||
(dblClickDocument)="openDocumentDetail(d)"
|
||||
[document]="!loading && show ? d : null"
|
||||
[ngStyle]="{ opacity: !loading && show ? 1 : 1 - (i * 1/documents.length) }"
|
||||
[document]="d"
|
||||
[ngStyle]="{ opacity: show ? 1 : 1 - (i * 1/documents.length) }"
|
||||
[displayFields]="displayFields"
|
||||
(clickTag)="clickTag($event)"
|
||||
(clickCorrespondent)="clickCorrespondent($event)"
|
||||
|
||||
+62
-22
@@ -6,12 +6,13 @@ import {
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
QueryList,
|
||||
signal,
|
||||
ViewChildren,
|
||||
} from '@angular/core'
|
||||
import { Router, RouterModule } from '@angular/router'
|
||||
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { delay, Subject, takeUntil, tap } from 'rxjs'
|
||||
import { Subject, takeUntil } from 'rxjs'
|
||||
import { CustomFieldDisplayComponent } from 'src/app/components/common/custom-field-display/custom-field-display.component'
|
||||
import { PreviewPopupComponent } from 'src/app/components/common/preview-popup/preview-popup.component'
|
||||
import { TagComponent } from 'src/app/components/common/tag/tag.component'
|
||||
@@ -106,7 +107,7 @@ export class SavedViewWidgetComponent
|
||||
@Input()
|
||||
savedView: SavedView
|
||||
|
||||
documents: Document[] = []
|
||||
private documentsSignal = signal<Document[]>([])
|
||||
|
||||
unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
||||
@@ -116,21 +117,56 @@ export class SavedViewWidgetComponent
|
||||
mouseOnPreview = false
|
||||
popoverHidden = true
|
||||
|
||||
displayMode: DisplayMode
|
||||
private displayModeSignal = signal<DisplayMode>(null)
|
||||
|
||||
displayFields: DisplayField[] = DEFAULT_DASHBOARD_DISPLAY_FIELDS
|
||||
private displayFieldsSignal = signal<DisplayField[]>(
|
||||
DEFAULT_DASHBOARD_DISPLAY_FIELDS
|
||||
)
|
||||
|
||||
count: number
|
||||
private countSignal = signal<number>(null)
|
||||
|
||||
placeholderRows: number[] = []
|
||||
|
||||
get documents(): Document[] {
|
||||
return this.documentsSignal()
|
||||
}
|
||||
|
||||
set documents(value: Document[]) {
|
||||
this.documentsSignal.set(value)
|
||||
}
|
||||
|
||||
get displayMode(): DisplayMode {
|
||||
return this.displayModeSignal()
|
||||
}
|
||||
|
||||
set displayMode(value: DisplayMode) {
|
||||
this.displayModeSignal.set(value)
|
||||
}
|
||||
|
||||
get displayFields(): DisplayField[] {
|
||||
return this.displayFieldsSignal()
|
||||
}
|
||||
|
||||
set displayFields(value: DisplayField[]) {
|
||||
this.displayFieldsSignal.set(value)
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this.countSignal()
|
||||
}
|
||||
|
||||
set count(value: number) {
|
||||
this.countSignal.set(value)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.reload()
|
||||
this.placeholderRows = Array.from(
|
||||
{
|
||||
length: this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
|
||||
},
|
||||
(_, index) => index
|
||||
)
|
||||
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
|
||||
this.websocketStatusService
|
||||
.onDocumentConsumptionFinished()
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
this.reload()
|
||||
})
|
||||
|
||||
if (
|
||||
this.permissionsService.currentUserCan(
|
||||
@@ -159,6 +195,14 @@ export class SavedViewWidgetComponent
|
||||
this.settingsService.allDisplayFields.find((f) => f.id === field) !==
|
||||
undefined
|
||||
)
|
||||
|
||||
this.reload()
|
||||
this.websocketStatusService
|
||||
.onDocumentConsumptionFinished()
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => {
|
||||
this.reload()
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -168,6 +212,7 @@ export class SavedViewWidgetComponent
|
||||
|
||||
reload() {
|
||||
this.loading = this.documents.length == 0
|
||||
this.show = true
|
||||
this.documentService
|
||||
.listFiltered(
|
||||
1,
|
||||
@@ -177,18 +222,13 @@ export class SavedViewWidgetComponent
|
||||
this.savedView.filter_rules,
|
||||
{ truncate_content: true }
|
||||
)
|
||||
.pipe(
|
||||
takeUntil(this.unsubscribeNotifier),
|
||||
tap((result) => {
|
||||
this.show = true
|
||||
this.documents = result.results
|
||||
this.count = result.count
|
||||
this.savedViewService.setDocumentCount(this.savedView, result.count)
|
||||
}),
|
||||
delay(500)
|
||||
)
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe((result) => {
|
||||
this.documents = result.results
|
||||
this.count = result.count
|
||||
this.savedViewService.setDocumentCount(this.savedView, result.count)
|
||||
this.loading = false
|
||||
this.show = true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+8
-7
@@ -1,19 +1,20 @@
|
||||
@if (!cardless) {
|
||||
<div class="card shadow-sm bg-light fade" [class.show]="show" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
|
||||
@if (!cardless()) {
|
||||
<div class="card shadow-sm bg-light fade" [class.show]="show" cdkDrag [cdkDragDisabled]="!draggable()" cdkDragPreviewContainer="parent">
|
||||
<div class="card-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
@if (draggable) {
|
||||
@if (draggable()) {
|
||||
<div class="ms-n2 me-1" cdkDragHandle>
|
||||
<i-bs name="grip-vertical"></i-bs>
|
||||
</div>
|
||||
}
|
||||
<h6 class="card-title mb-0">{{title}}</h6>
|
||||
@if (badge) {
|
||||
<span class="badge bg-info text-dark ms-2">{{badge}}</span>
|
||||
<h6 class="card-title mb-0">{{title()}}</h6>
|
||||
<ng-content select="[title-badge]"></ng-content>
|
||||
@if (badge() !== null && badge() !== undefined) {
|
||||
<span class="badge bg-info text-dark ms-2">{{badge()}}</span>
|
||||
}
|
||||
</div>
|
||||
@if (loading) {
|
||||
@if (loading()) {
|
||||
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
|
||||
<div class="visually-hidden" i18n>Loading...</div>
|
||||
}
|
||||
|
||||
+2
-4
@@ -45,21 +45,19 @@ describe('WidgetFrameComponent', () => {
|
||||
})
|
||||
|
||||
it('should show title', () => {
|
||||
component.title = 'Foo'
|
||||
fixture.componentRef.setInput('title', 'Foo')
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain('Foo')
|
||||
})
|
||||
|
||||
it('should show loading indicator', () => {
|
||||
expect(fixture.debugElement.query(By.css('.spinner-border'))).toBeNull()
|
||||
component.loading = true
|
||||
fixture.componentRef.setInput('loading', true)
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.query(By.css('.spinner-border'))).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should show', () => {
|
||||
expect(component.show).toBeFalsy()
|
||||
jest.advanceTimersByTime(100)
|
||||
expect(component.show).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
+15
-27
@@ -1,8 +1,7 @@
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||
import { NgTemplateOutlet } from '@angular/common'
|
||||
import { AfterViewInit, Component, Input } from '@angular/core'
|
||||
import { AfterViewInit, Component, input, signal } from '@angular/core'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { LoadingComponentWithPermissions } from 'src/app/components/loading-component/loading.component'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-widget-frame',
|
||||
@@ -10,39 +9,28 @@ import { LoadingComponentWithPermissions } from 'src/app/components/loading-comp
|
||||
styleUrls: ['./widget-frame.component.scss'],
|
||||
imports: [DragDropModule, NgxBootstrapIconsModule, NgTemplateOutlet],
|
||||
})
|
||||
export class WidgetFrameComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
implements AfterViewInit
|
||||
{
|
||||
constructor() {
|
||||
super()
|
||||
this.loading = false
|
||||
export class WidgetFrameComponent implements AfterViewInit {
|
||||
private showSignal = signal(false)
|
||||
|
||||
loading = input(false)
|
||||
|
||||
get show(): boolean {
|
||||
return this.showSignal()
|
||||
}
|
||||
|
||||
@Input()
|
||||
title: string
|
||||
|
||||
@Input()
|
||||
override set loading(value: boolean) {
|
||||
super.loading = value
|
||||
set show(value: boolean) {
|
||||
this.showSignal.set(value)
|
||||
}
|
||||
|
||||
override get loading(): boolean {
|
||||
return super.loading
|
||||
}
|
||||
title = input<string>()
|
||||
|
||||
@Input()
|
||||
draggable: any
|
||||
draggable = input<any>()
|
||||
|
||||
@Input()
|
||||
cardless: boolean = false
|
||||
cardless = input(false)
|
||||
|
||||
@Input()
|
||||
badge: string
|
||||
badge = input<string | number>(null)
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
this.show = true
|
||||
}, 100)
|
||||
this.show = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user