mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Enhancement: Improved popup preview, respect embedded viewer, error handling (#4947)
This commit is contained in:
		| @@ -1408,7 +1408,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">116</context> | ||||
|           <context context-type="linenumber">117</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6839066544204061364" datatype="html"> | ||||
| @@ -2257,21 +2257,21 @@ | ||||
|         <source>Sidebar views updated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">252</context> | ||||
|           <context context-type="linenumber">259</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3547923076537026828" datatype="html"> | ||||
|         <source>Error updating sidebar views</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">255</context> | ||||
|           <context context-type="linenumber">262</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2526035785704676448" datatype="html"> | ||||
|         <source>An error occurred while saving update checking settings.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context> | ||||
|           <context context-type="linenumber">276</context> | ||||
|           <context context-type="linenumber">283</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8700121026680200191" datatype="html"> | ||||
| @@ -3604,7 +3604,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.ts</context> | ||||
|           <context context-type="linenumber">80</context> | ||||
|           <context context-type="linenumber">77</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2504502765849142619" datatype="html"> | ||||
| @@ -3705,6 +3705,13 @@ | ||||
|           <context context-type="linenumber">61</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6418218602775540217" datatype="html"> | ||||
|         <source>Error loading preview</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/preview-popup/preview-popup.component.html</context> | ||||
|           <context context-type="linenumber">3</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2984628903434675339" datatype="html"> | ||||
|         <source>Edit Profile</source> | ||||
|         <context-group purpose="location"> | ||||
|   | ||||
| @@ -107,6 +107,7 @@ import { CustomFieldsDropdownComponent } from './components/common/custom-fields | ||||
| import { ProfileEditDialogComponent } from './components/common/profile-edit-dialog/profile-edit-dialog.component' | ||||
| import { PdfViewerComponent } from './components/common/pdf-viewer/pdf-viewer.component' | ||||
| import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component' | ||||
| import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component' | ||||
|  | ||||
| import localeAf from '@angular/common/locales/af' | ||||
| import localeAr from '@angular/common/locales/ar' | ||||
| @@ -261,6 +262,7 @@ function initializeApp(settings: SettingsService) { | ||||
|     ProfileEditDialogComponent, | ||||
|     PdfViewerComponent, | ||||
|     DocumentLinkComponent, | ||||
|     PreviewPopupComponent, | ||||
|   ], | ||||
|   imports: [ | ||||
|     BrowserModule, | ||||
|   | ||||
| @@ -0,0 +1,22 @@ | ||||
| <div class="preview-popup-container"> | ||||
|     <div *ngIf="error; else noError" class="w-100 h-100 position-relative"> | ||||
|         <p class="fst-italic position-absolute top-50 start-50 translate-middle" i18n>Error loading preview</p> | ||||
|     </div> | ||||
|     <ng-template #noError> | ||||
|         <object *ngIf="renderAsObject; else pngxViewer" [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf"></object> | ||||
|         <ng-template #pngxViewer> | ||||
|             <div *ngIf="requiresPassword" class="w-100 h-100 position-relative"> | ||||
|                 <svg width="2em" height="2em" fill="currentColor" class="position-absolute top-50 start-50 translate-middle"> | ||||
|                   <use xlink:href="assets/bootstrap-icons.svg#file-earmark-lock"/> | ||||
|                 </svg> | ||||
|             </div> | ||||
|             <pngx-pdf-viewer *ngIf="!requiresPassword" | ||||
|                 [src]="previewURL" | ||||
|                 [original-size]="false" | ||||
|                 [show-borders]="true" | ||||
|                 [show-all]="true" | ||||
|                 (error)="onError($event)"> | ||||
|             </pngx-pdf-viewer> | ||||
|         </ng-template> | ||||
|     </ng-template> | ||||
| </div> | ||||
| @@ -0,0 +1,9 @@ | ||||
| .preview-popup-container > * { | ||||
|     width: 30rem !important; | ||||
|     height: 22rem !important; | ||||
|     overflow-y: scroll; | ||||
| } | ||||
|  | ||||
| ::ng-deep .popover.popover-preview { | ||||
|     max-width: 32rem; | ||||
| } | ||||
| @@ -0,0 +1,92 @@ | ||||
| import { ComponentFixture, TestBed } from '@angular/core/testing' | ||||
|  | ||||
| import { PreviewPopupComponent } from './preview-popup.component' | ||||
| import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component' | ||||
| import { By } from '@angular/platform-browser' | ||||
| import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' | ||||
| import { SettingsService } from 'src/app/services/settings.service' | ||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
| import { HttpClientTestingModule } from '@angular/common/http/testing' | ||||
| import { DocumentService } from 'src/app/services/rest/document.service' | ||||
|  | ||||
| const doc = { | ||||
|   id: 10, | ||||
|   title: 'Document 10', | ||||
|   content: 'Cupcake ipsum dolor sit amet ice cream.', | ||||
|   original_file_name: 'sample.pdf', | ||||
| } | ||||
|  | ||||
| describe('PreviewPopupComponent', () => { | ||||
|   let component: PreviewPopupComponent | ||||
|   let fixture: ComponentFixture<PreviewPopupComponent> | ||||
|   let settingsService: SettingsService | ||||
|   let documentService: DocumentService | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     TestBed.configureTestingModule({ | ||||
|       declarations: [PreviewPopupComponent, PdfViewerComponent, SafeUrlPipe], | ||||
|       imports: [HttpClientTestingModule], | ||||
|     }) | ||||
|     settingsService = TestBed.inject(SettingsService) | ||||
|     documentService = TestBed.inject(DocumentService) | ||||
|     jest | ||||
|       .spyOn(documentService, 'getPreviewUrl') | ||||
|       .mockImplementation((id) => doc.original_file_name) | ||||
|     fixture = TestBed.createComponent(PreviewPopupComponent) | ||||
|     component = fixture.componentInstance | ||||
|     component.document = doc | ||||
|     fixture.detectChanges() | ||||
|   }) | ||||
|  | ||||
|   it('should guess if file is pdf by file name', () => { | ||||
|     expect(component.isPdf).toBeTruthy() | ||||
|     component.document.archived_file_name = 'sample.pdf' | ||||
|     expect(component.isPdf).toBeTruthy() | ||||
|     component.document.archived_file_name = undefined | ||||
|     component.document.original_file_name = 'sample.txt' | ||||
|     expect(component.isPdf).toBeFalsy() | ||||
|     component.document.original_file_name = 'sample.pdf' | ||||
|   }) | ||||
|  | ||||
|   it('should return settings for native PDF viewer', () => { | ||||
|     settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false) | ||||
|     expect(component.useNativePdfViewer).toBeFalsy() | ||||
|     settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, true) | ||||
|     expect(component.useNativePdfViewer).toBeTruthy() | ||||
|   }) | ||||
|  | ||||
|   it('should render object if native PDF viewer enabled', () => { | ||||
|     settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, true) | ||||
|     fixture.detectChanges() | ||||
|     expect(fixture.debugElement.query(By.css('object'))).not.toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should render pngx viewer if native PDF viewer disabled', () => { | ||||
|     settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false) | ||||
|     fixture.detectChanges() | ||||
|     expect(fixture.debugElement.query(By.css('object'))).toBeNull() | ||||
|     expect(fixture.debugElement.query(By.css('pngx-pdf-viewer'))).not.toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should show lock icon on password error', () => { | ||||
|     settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false) | ||||
|     component.onError({ name: 'PasswordException' }) | ||||
|     fixture.detectChanges() | ||||
|     expect(component.requiresPassword).toBeTruthy() | ||||
|     expect(fixture.debugElement.query(By.css('svg'))).not.toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should fall back to object for non-pdf', () => { | ||||
|     component.document.original_file_name = 'sample.png' | ||||
|     fixture.detectChanges() | ||||
|     expect(fixture.debugElement.query(By.css('object'))).not.toBeNull() | ||||
|   }) | ||||
|  | ||||
|   it('should show message on error', () => { | ||||
|     component.onError({}) | ||||
|     fixture.detectChanges() | ||||
|     expect(fixture.debugElement.nativeElement.textContent).toContain( | ||||
|       'Error loading preview' | ||||
|     ) | ||||
|   }) | ||||
| }) | ||||
| @@ -0,0 +1,52 @@ | ||||
| import { Component, Input } from '@angular/core' | ||||
| import { PaperlessDocument } from 'src/app/data/paperless-document' | ||||
| import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings' | ||||
| import { DocumentService } from 'src/app/services/rest/document.service' | ||||
| import { SettingsService } from 'src/app/services/settings.service' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'pngx-preview-popup', | ||||
|   templateUrl: './preview-popup.component.html', | ||||
|   styleUrls: ['./preview-popup.component.scss'], | ||||
| }) | ||||
| export class PreviewPopupComponent { | ||||
|   @Input() | ||||
|   document: PaperlessDocument | ||||
|  | ||||
|   error = false | ||||
|  | ||||
|   requiresPassword: boolean = false | ||||
|  | ||||
|   get renderAsObject(): boolean { | ||||
|     return (this.isPdf && this.useNativePdfViewer) || !this.isPdf | ||||
|   } | ||||
|  | ||||
|   get previewURL() { | ||||
|     return this.documentService.getPreviewUrl(this.document.id) | ||||
|   } | ||||
|  | ||||
|   get useNativePdfViewer(): boolean { | ||||
|     return this.settingsService.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) | ||||
|   } | ||||
|  | ||||
|   get isPdf(): boolean { | ||||
|     // We dont have time to retrieve metadata, make a best guess by file name | ||||
|     return ( | ||||
|       this.document?.original_file_name?.endsWith('.pdf') || | ||||
|       this.document?.archived_file_name?.endsWith('.pdf') | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   constructor( | ||||
|     private settingsService: SettingsService, | ||||
|     private documentService: DocumentService | ||||
|   ) {} | ||||
|  | ||||
|   onError(event: any) { | ||||
|     if (event.name == 'PasswordException') { | ||||
|       this.requiresPassword = true | ||||
|     } else { | ||||
|       this.error = true | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -17,7 +17,7 @@ | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|       <tr *ngFor="let doc of documents" (mouseleave)="mouseLeaveCard()"> | ||||
|       <tr *ngFor="let doc of documents" (mouseleave)="maybeClosePopover()"> | ||||
|         <td class="py-2 py-md-3"><a routerLink="/documents/{{doc.id}}" class="btn-link text-dark text-decoration-none py-2 py-md-3">{{doc.created_date | customDate}}</a></td> | ||||
|         <td class="py-2 py-md-3"> | ||||
|           <a routerLink="/documents/{{doc.id}}" title="Edit" i18n-title class="btn-link text-dark text-decoration-none py-2 py-md-3">{{doc.title | documentTitle}}</a> | ||||
| @@ -30,13 +30,13 @@ | ||||
|           <div class="btn-group position-absolute top-50 end-0 translate-middle-y"> | ||||
|             <a [href]="getPreviewUrl(doc)" title="View Preview" i18n-title target="_blank" class="btn px-4 btn-dark border-dark-subtle" | ||||
|             [ngbPopover]="previewContent" [popoverTitle]="doc.title | documentTitle" | ||||
|             autoClose="true" popoverClass="shadow popover-preview" container="body" (mouseenter)="mouseEnterPreview(doc)" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover"> | ||||
|             autoClose="true" popoverClass="shadow popover-preview" container="body" (mouseenter)="mouseEnterPreviewButton(doc)" (mouseleave)="mouseLeavePreviewButton()" #popover="ngbPopover"> | ||||
|               <svg class="buttonicon-xs" fill="currentColor"> | ||||
|                 <use xlink:href="assets/bootstrap-icons.svg#eye"/> | ||||
|               </svg> | ||||
|             </a> | ||||
|             <ng-template #previewContent> | ||||
|               <object [data]="getPreviewUrl(doc) | safeUrl" class="preview" width="100%"></object> | ||||
|               <pngx-preview-popup [document]="doc" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()"></pngx-preview-popup> | ||||
|             </ng-template> | ||||
|             <a [href]="getDownloadUrl(doc)" class="btn px-4 btn-dark border-dark-subtle" title="Download" i18n-title (click)="$event.stopPropagation()"> | ||||
|               <svg class="buttonicon-xs" fill="currentColor"> | ||||
|   | ||||
| @@ -29,6 +29,7 @@ import { SavedViewWidgetComponent } from './saved-view-widget.component' | ||||
| import { By } from '@angular/platform-browser' | ||||
| import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' | ||||
| import { DragDropModule } from '@angular/cdk/drag-drop' | ||||
| import { PreviewPopupComponent } from 'src/app/components/common/preview-popup/preview-popup.component' | ||||
|  | ||||
| const savedView: PaperlessSavedView = { | ||||
|   id: 1, | ||||
| @@ -74,6 +75,7 @@ describe('SavedViewWidgetComponent', () => { | ||||
|         CustomDatePipe, | ||||
|         DocumentTitlePipe, | ||||
|         SafeUrlPipe, | ||||
|         PreviewPopupComponent, | ||||
|       ], | ||||
|       providers: [ | ||||
|         PermissionsGuard, | ||||
| @@ -137,15 +139,18 @@ describe('SavedViewWidgetComponent', () => { | ||||
|     ) | ||||
|     component.ngOnInit() | ||||
|     fixture.detectChanges() | ||||
|     component.mouseEnterPreview(documentResults[0]) | ||||
|     component.mouseEnterPreviewButton(documentResults[0]) | ||||
|     expect(component.popover.isOpen()).toBeTruthy() | ||||
|     expect(component.popoverHidden).toBeTruthy() | ||||
|     tick(600) | ||||
|     expect(component.popoverHidden).toBeFalsy() | ||||
|     component.mouseLeaveCard() | ||||
|     component.maybeClosePopover() | ||||
|  | ||||
|     component.mouseEnterPreview(documentResults[1]) | ||||
|     component.mouseEnterPreviewButton(documentResults[1]) | ||||
|     tick(100) | ||||
|     component.mouseLeavePreviewButton() | ||||
|     component.mouseEnterPreview() | ||||
|     expect(component.popover.isOpen()).toBeTruthy() | ||||
|     component.mouseLeavePreview() | ||||
|     tick(600) | ||||
|     expect(component.popover.isOpen()).toBeFalsy() | ||||
|   | ||||
| @@ -26,10 +26,7 @@ import { queryParamsFromFilterRules } from 'src/app/utils/query-params' | ||||
| @Component({ | ||||
|   selector: 'pngx-saved-view-widget', | ||||
|   templateUrl: './saved-view-widget.component.html', | ||||
|   styleUrls: [ | ||||
|     './saved-view-widget.component.scss', | ||||
|     '../../../document-list/popover-preview/popover-preview.scss', | ||||
|   ], | ||||
|   styleUrls: ['./saved-view-widget.component.scss'], | ||||
| }) | ||||
| export class SavedViewWidgetComponent | ||||
|   extends ComponentWithPermissions | ||||
| @@ -121,8 +118,11 @@ export class SavedViewWidgetComponent | ||||
|     return this.documentService.getDownloadUrl(document.id) | ||||
|   } | ||||
|  | ||||
|   mouseEnterPreview(doc: PaperlessDocument) { | ||||
|     this.popover = this.popovers.get(this.documents.indexOf(doc)) | ||||
|   mouseEnterPreviewButton(doc: PaperlessDocument) { | ||||
|     const newPopover = this.popovers.get(this.documents.indexOf(doc)) | ||||
|     if (this.popover !== newPopover && this.popover?.isOpen()) | ||||
|       this.popover.close() | ||||
|     this.popover = newPopover | ||||
|     this.mouseOnPreview = true | ||||
|     if (!this.popover.isOpen()) { | ||||
|       // we're going to open but hide to pre-load content during hover delay | ||||
| @@ -139,12 +139,24 @@ export class SavedViewWidgetComponent | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   mouseLeavePreview() { | ||||
|     this.mouseOnPreview = false | ||||
|   mouseEnterPreview() { | ||||
|     this.mouseOnPreview = true | ||||
|   } | ||||
|  | ||||
|   mouseLeaveCard() { | ||||
|     this.popover?.close() | ||||
|   mouseLeavePreview() { | ||||
|     this.mouseOnPreview = false | ||||
|     this.maybeClosePopover() | ||||
|   } | ||||
|  | ||||
|   mouseLeavePreviewButton() { | ||||
|     this.mouseOnPreview = false | ||||
|     this.maybeClosePopover() | ||||
|   } | ||||
|  | ||||
|   maybeClosePopover() { | ||||
|     setTimeout(() => { | ||||
|       if (!this.mouseOnPreview) this.popover?.close() | ||||
|     }, 300) | ||||
|   } | ||||
|  | ||||
|   getCorrespondentQueryParams(correspondentId: number): Params { | ||||
|   | ||||
| @@ -56,7 +56,7 @@ | ||||
|               </svg> <span class="d-none d-md-inline" i18n>View</span> | ||||
|             </a> | ||||
|             <ng-template #previewContent> | ||||
|               <object [data]="previewUrl | safeUrl" class="preview" width="100%"></object> | ||||
|               <pngx-preview-popup [document]="document"></pngx-preview-popup> | ||||
|             </ng-template> | ||||
|             <a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()"> | ||||
|               <svg class="sidebaricon" fill="currentColor" class="sidebaricon"> | ||||
|   | ||||
| @@ -19,6 +19,7 @@ import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' | ||||
| import { SafeUrlPipe } from 'src/app/pipes/safeurl.pipe' | ||||
| import { DocumentCardLargeComponent } from './document-card-large.component' | ||||
| import { IsNumberPipe } from 'src/app/pipes/is-number.pipe' | ||||
| import { PreviewPopupComponent } from '../../common/preview-popup/preview-popup.component' | ||||
|  | ||||
| const doc = { | ||||
|   id: 10, | ||||
| @@ -50,6 +51,7 @@ describe('DocumentCardLargeComponent', () => { | ||||
|         IfPermissionsDirective, | ||||
|         SafeUrlPipe, | ||||
|         IsNumberPipe, | ||||
|         PreviewPopupComponent, | ||||
|       ], | ||||
|       providers: [DatePipe], | ||||
|       imports: [ | ||||
|   | ||||
| @@ -15,10 +15,7 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission | ||||
| @Component({ | ||||
|   selector: 'pngx-document-card-large', | ||||
|   templateUrl: './document-card-large.component.html', | ||||
|   styleUrls: [ | ||||
|     './document-card-large.component.scss', | ||||
|     '../popover-preview/popover-preview.scss', | ||||
|   ], | ||||
|   styleUrls: ['./document-card-large.component.scss'], | ||||
| }) | ||||
| export class DocumentCardLargeComponent extends ComponentWithPermissions { | ||||
|   constructor( | ||||
|   | ||||
| @@ -94,7 +94,7 @@ | ||||
|             </svg> | ||||
|           </a> | ||||
|           <ng-template #previewContent> | ||||
|             <object [data]="previewUrl | safeUrl" class="preview" width="100%"></object> | ||||
|             <pngx-preview-popup [document]="document"></pngx-preview-popup> | ||||
|           </ng-template> | ||||
|           <a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title (click)="$event.stopPropagation()"> | ||||
|             <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import { By } from '@angular/platform-browser' | ||||
| import { TagComponent } from '../../common/tag/tag.component' | ||||
| import { PaperlessTag } from 'src/app/data/paperless-tag' | ||||
| import { IsNumberPipe } from 'src/app/pipes/is-number.pipe' | ||||
| import { PreviewPopupComponent } from '../../common/preview-popup/preview-popup.component' | ||||
|  | ||||
| const doc = { | ||||
|   id: 10, | ||||
| @@ -64,6 +65,7 @@ describe('DocumentCardSmallComponent', () => { | ||||
|         SafeUrlPipe, | ||||
|         TagComponent, | ||||
|         IsNumberPipe, | ||||
|         PreviewPopupComponent, | ||||
|       ], | ||||
|       providers: [DatePipe], | ||||
|       imports: [ | ||||
|   | ||||
| @@ -16,10 +16,7 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission | ||||
| @Component({ | ||||
|   selector: 'pngx-document-card-small', | ||||
|   templateUrl: './document-card-small.component.html', | ||||
|   styleUrls: [ | ||||
|     './document-card-small.component.scss', | ||||
|     '../popover-preview/popover-preview.scss', | ||||
|   ], | ||||
|   styleUrls: ['./document-card-small.component.scss'], | ||||
| }) | ||||
| export class DocumentCardSmallComponent extends ComponentWithPermissions { | ||||
|   constructor( | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| ::ng-deep .popover.popover-preview { | ||||
|   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; | ||||
| } | ||||
| @@ -555,6 +555,11 @@ table.table { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .popover-hidden .popover { | ||||
|     opacity: 0; | ||||
|     pointer-events: none; | ||||
| } | ||||
|  | ||||
| // Tour | ||||
| .tour-active .popover { | ||||
|   min-width: 360px; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon