mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Unified toolbar w select, hover buttons
This commit is contained in:
		| @@ -4,15 +4,37 @@ | ||||
|   <button type="button" class="btn-close" aria-label="Close" (click)="cancel()"></button> | ||||
| </div> | ||||
| <div class="modal-body"> | ||||
|   <div class="btn-group toolbar mb-2"> | ||||
|     <button class="btn btn-sm btn-secondary" (click)="rotateSelected(-90)" [disabled]="!hasSelection()"> | ||||
|       <i-bs name="arrow-counterclockwise"></i-bs> | ||||
|     </button> | ||||
|     <button class="btn btn-sm btn-secondary" (click)="rotateSelected(90)" [disabled]="!hasSelection()"> | ||||
|       <i-bs name="arrow-clockwise"></i-bs> | ||||
|     </button> | ||||
|     <button class="btn btn-sm btn-danger" (click)="deleteSelected()" [disabled]="!hasSelection()"> | ||||
|       <i-bs name="trash"></i-bs> | ||||
|     </button> | ||||
|   </div> | ||||
|   <div cdkDropList (cdkDropListDropped)="drop($event)" cdkDropListOrientation="mixed" class="d-flex flex-wrap row-cols-5"> | ||||
|     @for (p of pages; track p.page; let i = $index) { | ||||
|       <div class="page-item p-2" cdkDrag> | ||||
|         <div class="btn-group mb-1"> | ||||
|           <button class="btn btn-sm btn-secondary" (click)="rotate(i)"><i-bs name="arrow-clockwise"></i-bs></button> | ||||
|           <button class="btn btn-sm btn-outline-secondary" (click)="toggleSplit(i)"><i-bs name="scissors"></i-bs></button> | ||||
|           <button class="btn btn-sm btn-danger" (click)="remove(i)"><i-bs name="trash"></i-bs></button> | ||||
|       <div class="page-item rounded p-2" cdkDrag (click)="toggleSelection(i)" [class.selected]="p.selected"> | ||||
|         <div class="btn-toolbar hover-actions z-10"> | ||||
|           <div class="btn-group"> | ||||
|             <button class="btn btn-sm btn-dark text-danger" (click)="remove(i); $event.stopPropagation()"> | ||||
|               <i-bs name="trash"></i-bs> | ||||
|             </button> | ||||
|             <button class="btn btn-sm btn-dark" (click)="toggleSplit(i); $event.stopPropagation()"> | ||||
|               <i-bs name="scissors"></i-bs> | ||||
|             </button> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="pdf-viewer-container w-100 mt-3"> | ||||
|         <div class="border-end border-bottom bg-light py-1 px-2 document-check z-10"> | ||||
|           <div class="form-check"> | ||||
|             <input type="checkbox" class="form-check-input" id="page{{i}}"  [checked]="p.selected" (click)="toggleSelection(i); $event.stopPropagation()"> | ||||
|             <label class="form-check-label" for="page{{i}}"></label> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="pdf-viewer-container w-100" [class.selected]="p.selected"> | ||||
|           <pdf-viewer [src]="pdfSrc" [page]="p.page" [rotation]="p.rotate" [original-size]="false" [show-all]="false" [render-text]="false"></pdf-viewer> | ||||
|         </div> | ||||
|       </div> | ||||
|   | ||||
| @@ -1,9 +1,62 @@ | ||||
| .pdf-viewer-container { | ||||
|     background-color: gray; | ||||
|     height: 120px; | ||||
|  | ||||
|     pdf-viewer { | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|  | ||||
| .page-item { | ||||
|   position: relative; | ||||
|   cursor: pointer; | ||||
|   border: 1px solid transparent; | ||||
|   background-origin: border-box; | ||||
|  | ||||
|   &.selected { | ||||
|     background-color: var(--pngx-primary-darken-5); | ||||
|   } | ||||
| } | ||||
|  | ||||
| .pdf-viewer-container { | ||||
|   background-color: gray; | ||||
|   height: 200px; | ||||
|  | ||||
|   pdf-viewer { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .hover-actions { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   right: 0; | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .page-item:hover .hover-actions { | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| .document-check { | ||||
|   display: none; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   padding: 0.5rem; | ||||
|   border-top-left-radius: 0.25rem; | ||||
|   border-bottom-right-radius: 0.25rem; | ||||
|   pointer-events: none; | ||||
|  | ||||
|   .form-check { | ||||
|     padding: 0; | ||||
|     min-height: 0; | ||||
|     margin-bottom: 0; | ||||
|  | ||||
|     .form-check-input { | ||||
|       margin-left: 0; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| .page-item:hover .document-check, .selected .document-check { | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| .z-10 { | ||||
|     z-index: 10; | ||||
| } | ||||
|   | ||||
| @@ -23,13 +23,19 @@ describe('PDFEditorComponent', () => { | ||||
|     fixture.detectChanges() | ||||
|   }) | ||||
|  | ||||
|   it('should rotate and reorder pages', () => { | ||||
|   it('should rotate, delete and reorder pages', () => { | ||||
|     component.pages = [ | ||||
|       { page: 1, rotate: 0, splitAfter: false }, | ||||
|       { page: 2, rotate: 0, splitAfter: false }, | ||||
|       { page: 1, rotate: 0, splitAfter: false, selected: false }, | ||||
|       { page: 2, rotate: 0, splitAfter: false, selected: false }, | ||||
|     ] | ||||
|     component.rotate(0) | ||||
|     component.toggleSelection(0) | ||||
|     component.rotateSelected(90) | ||||
|     expect(component.pages[0].rotate).toBe(90) | ||||
|     component.toggleSelection(0) // deselect | ||||
|     component.toggleSelection(1) | ||||
|     component.deleteSelected() | ||||
|     expect(component.pages.length).toBe(1) | ||||
|     component.pages.push({ page: 2, rotate: 0, splitAfter: false }) | ||||
|     component.drop({ previousIndex: 0, currentIndex: 1 } as any) | ||||
|     expect(component.pages[0].page).toBe(2) | ||||
|   }) | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import { | ||||
|   DragDropModule, | ||||
|   moveItemInArray, | ||||
| } from '@angular/cdk/drag-drop' | ||||
| import { CommonModule } from '@angular/common' | ||||
| import { Component, OnInit, inject } from '@angular/core' | ||||
| import { FormsModule } from '@angular/forms' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| @@ -15,6 +16,7 @@ interface PageOperation { | ||||
|   page: number | ||||
|   rotate: number | ||||
|   splitAfter: boolean | ||||
|   selected?: boolean | ||||
| } | ||||
|  | ||||
| @Component({ | ||||
| @@ -22,6 +24,7 @@ interface PageOperation { | ||||
|   templateUrl: './pdf-editor.component.html', | ||||
|   styleUrl: './pdf-editor.component.scss', | ||||
|   imports: [ | ||||
|     CommonModule, | ||||
|     DragDropModule, | ||||
|     FormsModule, | ||||
|     PdfViewerModule, | ||||
| @@ -52,11 +55,16 @@ export class PDFEditorComponent | ||||
|       page: i + 1, | ||||
|       rotate: 0, | ||||
|       splitAfter: false, | ||||
|       selected: false, | ||||
|     })) | ||||
|   } | ||||
|  | ||||
|   rotate(i: number) { | ||||
|     this.pages[i].rotate = (this.pages[i].rotate + 90) % 360 | ||||
|   rotateSelected(dir: number) { | ||||
|     for (let p of this.pages) { | ||||
|       if (p.selected) { | ||||
|         p.rotate = (p.rotate + dir + 360) % 360 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   remove(i: number) { | ||||
| @@ -67,6 +75,18 @@ export class PDFEditorComponent | ||||
|     this.pages[i].splitAfter = !this.pages[i].splitAfter | ||||
|   } | ||||
|  | ||||
|   toggleSelection(i: number) { | ||||
|     this.pages[i].selected = !this.pages[i].selected | ||||
|   } | ||||
|  | ||||
|   deleteSelected() { | ||||
|     this.pages = this.pages.filter((p) => !p.selected) | ||||
|   } | ||||
|  | ||||
|   hasSelection(): boolean { | ||||
|     return this.pages.some((p) => p.selected) | ||||
|   } | ||||
|  | ||||
|   drop(event: CdkDragDrop<PageOperation[]>) { | ||||
|     moveItemInArray(this.pages, event.previousIndex, event.currentIndex) | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon