Chorehancement: update to Angular v22, 'zoneless' / 'reactive' (#13114)

This commit is contained in:
shamoon
2026-07-10 00:42:16 -07:00
committed by GitHub
parent f244442c65
commit 106b41a15c
213 changed files with 5363 additions and 5842 deletions
@@ -1,22 +1,25 @@
<pngx-widget-frame
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
[title]="savedView.name"
[badge]="count"
[loading]="loading"
[loading]="false"
[draggable]="savedView"
>
@if (documents.length) {
@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>
}
<div content class="wrapper fade" [class.show]="show">
@if (displayMode === DisplayMode.TABLE) {
<div content class="wrapper fade" [class.show]="show()">
@if (displayMode() === DisplayMode.TABLE) {
<table class="table table-hover mb-0 mt-n2 align-middle">
<thead>
<tr>
@for (field of displayFields; track field; let i = $index) {
@if (displayFields.includes(field)) {
@for (field of displayFields(); track field; let i = $index) {
@if (displayFields().includes(field)) {
<th
scope="col"
[ngClass]="{
@@ -30,13 +33,26 @@
</tr>
</thead>
<tbody>
@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) {
@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 - (i * 1/documents.length) }"></span>
<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() && !documents().length) {
<div class="placeholder-glow text-start">
<span class="placeholder bg-secondary w-50" [ngStyle]="{ opacity: 1 - (i * 1/documents().length) }"></span>
</div>
} @else {
@switch (field) {
@@ -96,7 +112,7 @@
@if (field.startsWith(DisplayField.CUSTOM_FIELD)) {
<pngx-custom-field-display [document]="doc" [fieldDisplayKey]="field"></pngx-custom-field-display>
}
@if (j === displayFields.length - 1) {
@if (j === displayFields().length - 1) {
<div class="btn-group position-absolute top-50 end-0 translate-middle-y" (mouseleave)="popupPreview.close()">
<pngx-preview-popup [document]="doc" linkClasses="btn px-4 btn-dark border-dark-subtle" #popupPreview>
<i-bs width="0.8em" height="0.8em" name="eye"></i-bs>
@@ -113,15 +129,25 @@
}
</tbody>
</table>
} @else if (displayMode === DisplayMode.SMALL_CARDS) {
} @else if (displayMode() === DisplayMode.SMALL_CARDS) {
<div class="row row-cols-paperless-cards my-n2">
@for (d of documents; track d.id; let i = $index) {
@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"
[displayFields]="displayFields"
[document]="d"
[displayFields]="displayFields()"
(clickTag)="clickTag($event)"
(clickCorrespondent)="clickCorrespondent($event)"
(clickStoragePath)="clickStoragePath($event)"
@@ -129,14 +155,23 @@
</pngx-document-card-small>
}
</div>
} @else if (displayMode === DisplayMode.LARGE_CARDS) {
} @else if (displayMode() === DisplayMode.LARGE_CARDS) {
<div class="row my-n2">
@for (d of documents; track d.id; let i = $index) {
@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) }"
[displayFields]="displayFields"
[document]="d"
[ngStyle]="{ opacity: show() ? 1 : 1 - (i * 1/documents().length) }"
[displayFields]="displayFields()"
(clickTag)="clickTag($event)"
(clickCorrespondent)="clickCorrespondent($event)"
(clickStoragePath)="clickStoragePath($event)"
@@ -2,12 +2,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
import { DatePipe } from '@angular/common'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { By } from '@angular/platform-browser'
import { Router } from '@angular/router'
import { RouterTestingModule } from '@angular/router/testing'
@@ -186,7 +181,8 @@ describe('SavedViewWidgetComponent', () => {
fixture.detectChanges()
})
it('should show a list of documents', fakeAsync(() => {
it('should show a list of documents', async () => {
jest.useFakeTimers()
jest.spyOn(documentService, 'listFiltered').mockReturnValue(
of({
all: [2, 3],
@@ -195,7 +191,8 @@ describe('SavedViewWidgetComponent', () => {
})
)
component.ngOnInit()
tick(500)
jest.advanceTimersByTime(500)
await fixture.whenStable()
fixture.detectChanges()
expect(fixture.debugElement.nativeElement.textContent).toContain('doc2')
expect(fixture.debugElement.nativeElement.textContent).toContain('doc3')
@@ -206,7 +203,8 @@ describe('SavedViewWidgetComponent', () => {
expect(
fixture.debugElement.queryAll(By.css('td a.btn'))[1].attributes['href']
).toEqual(component.getDownloadUrl(documentResults[0]))
}))
jest.useRealTimers()
})
it('should call api endpoint and load results', () => {
const listAllSpy = jest.spyOn(documentService, 'listFiltered')
@@ -229,7 +227,7 @@ describe('SavedViewWidgetComponent', () => {
}
)
fixture.detectChanges()
expect(component.documents).toEqual(documentResults)
expect(component.documents()).toEqual(documentResults)
})
it('should reload on document consumption finished', () => {
@@ -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[] = []
readonly documents = signal<Document[]>([])
unsubscribeNotifier: Subject<any> = new Subject()
@@ -116,21 +117,24 @@ export class SavedViewWidgetComponent
mouseOnPreview = false
popoverHidden = true
displayMode: DisplayMode
readonly displayMode = signal<DisplayMode>(null)
displayFields: DisplayField[] = DEFAULT_DASHBOARD_DISPLAY_FIELDS
readonly displayFields = signal<DisplayField[]>(
DEFAULT_DASHBOARD_DISPLAY_FIELDS
)
count: number
readonly count = signal<number>(null)
placeholderRows: number[] = []
ngOnInit(): void {
this.reload()
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
this.websocketStatusService
.onDocumentConsumptionFinished()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
this.reload()
})
this.placeholderRows = Array.from(
{
length: this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE,
},
(_, index) => index
)
this.displayMode.set(this.savedView.display_mode ?? DisplayMode.TABLE)
if (
this.permissionsService.currentUserCan(
@@ -150,15 +154,23 @@ export class SavedViewWidgetComponent
this.savedView.display_fields &&
this.savedView.display_fields.length > 0
) {
this.displayFields = this.savedView.display_fields
this.displayFields.set(this.savedView.display_fields)
}
// filter by perms etc
this.displayFields = this.displayFields.filter(
(field) =>
this.settingsService.allDisplayFields.find((f) => f.id === field) !==
undefined
this.displayFields.set(
this.displayFields().filter((field) =>
this.settingsService.allDisplayFields().some((f) => f.id === field)
)
)
this.reload()
this.websocketStatusService
.onDocumentConsumptionFinished()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
this.reload()
})
}
ngOnDestroy(): void {
@@ -167,7 +179,8 @@ export class SavedViewWidgetComponent
}
reload() {
this.loading = this.documents.length == 0
this.loading.set(this.documents().length == 0)
this.show.set(true)
this.documentService
.listFiltered(
1,
@@ -177,18 +190,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.loading = false
this.documents.set(result.results)
this.count.set(result.count)
this.savedViewService.setDocumentCount(this.savedView, result.count)
this.loading.set(false)
this.show.set(true)
})
}
@@ -1,7 +1,7 @@
<pngx-widget-frame title="Statistics" [loading]="loading" i18n-title>
<pngx-widget-frame title="Statistics" [loading]="loading()" i18n-title>
<ng-container content>
<div class="list-group border-light placeholder-glow">
@if (loading) {
@if (loading()) {
<div class="list-group-item d-flex">
<div class="placeholder w-50"></div>
<span class="placeholder badge rounded-pill ms-auto" style="width: 25px;">&nbsp;</span>
@@ -24,32 +24,32 @@
<div class="placeholder w-100 d-block"></div>
</div>
} @else {
@if (statistics?.documents_inbox !== null) {
@if (statistics()?.documents_inbox !== null) {
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void(0)" (click)="goToInbox()">
<ng-container i18n>Documents in inbox</ng-container>:
<span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
<span class="badge rounded-pill" [class.bg-primary]="statistics()?.documents_inbox > 0" [class.bg-muted]="statistics()?.documents_inbox === 0">{{statistics()?.documents_inbox}}</span>
</a>
}
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
<ng-container i18n>Total documents</ng-container>:
<span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
<span class="badge bg-primary rounded-pill">{{statistics()?.documents_total}}</span>
</a>
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Total characters</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.character_count | number}}</span>
</div>
@if (statistics?.current_asn) {
@if (statistics()?.current_asn) {
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Current ASN</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.current_asn}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.current_asn}}</span>
</div>
}
}
@if (statistics?.document_file_type_counts?.length > 1) {
@if (statistics()?.document_file_type_counts?.length > 1) {
<div class="list-group-item filetypes">
<div class="d-flex justify-content-between align-items-center my-2">
<div class="progress flex-grow-1">
@for (filetype of statistics?.document_file_type_counts; track filetype; let i = $index; let last = $last) {
@for (filetype of statistics()?.document_file_type_counts; track filetype; let i = $index; let last = $last) {
<div
class="progress-bar bg-primary"
role="progressbar"
@@ -69,7 +69,7 @@
</div>
</div>
<div class="d-flex flex-wrap align-items-start">
@for (filetype of statistics?.document_file_type_counts; track filetype; let i = $index) {
@for (filetype of statistics()?.document_file_type_counts; track filetype; let i = $index) {
<div class="d-flex">
<div class="text-nowrap me-2" [class.cursor-pointer]="!filetype.is_other" (click)="filterByFileType(filetype)">
<span class="badge rounded-pill bg-primary d-inline-block p-0 me-1" [style.opacity]="getItemOpacity(i)"></span>
@@ -84,54 +84,54 @@
<div class="list-group border-light mt-3">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.tag_count > 0) {
@if (statistics()?.tag_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/tags/">
<ng-container i18n>Tags</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.tag_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.tag_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.correspondent_count > 0) {
@if (statistics()?.correspondent_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/correspondents/">
<ng-container i18n>Correspondents</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.correspondent_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.correspondent_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.document_type_count > 0) {
@if (statistics()?.document_type_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documenttypes/">
<ng-container i18n>Document Types</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.document_type_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.document_type_count | number}}</span>
</a>
}
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
@if (loading) {
@if (loading()) {
<div class="placeholder-glow list-group-item">
<span class="placeholder w-100"></span>
</div>
}
@if (statistics?.storage_path_count > 0) {
@if (statistics()?.storage_path_count > 0) {
<a class="list-group-item d-flex justify-content-between align-items-center" routerLink="/storagepaths/">
<ng-container i18n>Storage Paths</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.storage_path_count | number}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{statistics()?.storage_path_count | number}}</span>
</a>
}
</ng-container>
@@ -74,7 +74,7 @@ describe('StatisticsWidgetComponent', () => {
it('should not call statistics endpoint on reload if already loading', () => {
httpTestingController.expectOne(`${environment.apiBaseUrl}statistics/`)
component.loading = true
component.loading.set(true)
component.reload()
httpTestingController.expectNone(`${environment.apiBaseUrl}statistics/`)
})
@@ -1,6 +1,6 @@
import { DecimalPipe } from '@angular/common'
import { HttpClient } from '@angular/common/http'
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core'
import { RouterModule } from '@angular/router'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import * as mimeTypeNames from 'mime-names'
@@ -55,21 +55,20 @@ export class StatisticsWidgetComponent
private websocketConnectionService = inject(WebsocketStatusService)
private documentListViewService = inject(DocumentListViewService)
loading: boolean = false
statistics: Statistics = {}
readonly loading = signal(false)
readonly statistics = signal<Statistics>({})
subscription: Subscription
private unsubscribeNotifer: Subject<any> = new Subject()
reload() {
if (this.loading) return
this.loading = true
if (this.loading()) return
this.loading.set(true)
this.http
.get<Statistics>(`${environment.apiBaseUrl}statistics/`)
.pipe(takeUntil(this.unsubscribeNotifer), first())
.subscribe((statistics) => {
this.loading = false
this.loading.set(false)
const fileTypeMax = 5
if (statistics.document_file_type_counts?.length > fileTypeMax) {
const others = statistics.document_file_type_counts.slice(fileTypeMax)
@@ -85,7 +84,7 @@ export class StatisticsWidgetComponent
),
})
}
this.statistics = statistics
this.statistics.set(statistics)
})
}
@@ -101,11 +100,11 @@ export class StatisticsWidgetComponent
}
getFileTypePercent(filetype: DocumentFileType): number {
return (filetype.mime_type_count / this.statistics?.documents_total) * 100
return (filetype.mime_type_count / this.statistics()?.documents_total) * 100
}
getItemOpacity(i: number): number {
return 1 - i / this.statistics?.document_file_type_counts.length
return 1 - i / this.statistics()?.document_file_type_counts.length
}
ngOnInit(): void {
@@ -127,8 +126,8 @@ export class StatisticsWidgetComponent
this.documentListViewService.quickFilter([
{
rule_type: FILTER_HAS_TAGS_ANY,
value: this.statistics.inbox_tags
.map((tagID) => tagID.toString())
value: this.statistics()
.inbox_tags.map((tagID) => tagID.toString())
.join(','),
},
])
@@ -1,11 +1,6 @@
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { By } from '@angular/platform-browser'
import { RouterTestingModule } from '@angular/router/testing'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
@@ -133,7 +128,8 @@ describe('UploadFileWidgetComponent', () => {
expect(dismissSpy).toHaveBeenCalled()
})
it('should allow dismissing completed alerts', fakeAsync(() => {
it('should allow dismissing completed alerts', () => {
jest.useFakeTimers()
mockConsumerStatuses(websocketStatusService)
fixture.detectChanges()
jest
@@ -141,10 +137,11 @@ describe('UploadFileWidgetComponent', () => {
.mockImplementation(() => SUCCESS_STATUSES)
const dismissSpy = jest.spyOn(websocketStatusService, 'dismiss')
component.dismissCompleted()
tick(1000)
jest.advanceTimersByTime(1000)
fixture.detectChanges()
expect(dismissSpy).toHaveBeenCalledTimes(4)
}))
jest.useRealTimers()
})
})
function mockConsumerStatuses(consumerStatusService) {
@@ -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>
}
@@ -25,7 +26,7 @@
</div>
</div>
} @else {
<div class="fade" [class.show]="show">
<div class="fade" [class.show]="show()">
<ng-container [ngTemplateOutlet]="content"></ng-container>
</div>
}
@@ -1,3 +1,23 @@
i-bs {
cursor: move;
}
.fade.show {
animation: pngx-entry-fade 160ms ease-out;
}
@keyframes pngx-entry-fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.fade.show {
animation: none;
}
}
@@ -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()
expect(component.show()).toBeTruthy()
})
})
@@ -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,32 +9,20 @@ 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()
}
export class WidgetFrameComponent implements AfterViewInit {
readonly show = signal(false)
@Input()
title: string
loading = input(false)
@Input()
loading: boolean = false
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.set(true)
}
}