]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Unified toolbar w select, hover buttons
authorshamoon <4887959+shamoon@users.noreply.github.com>
Tue, 1 Jul 2025 20:35:15 +0000 (13:35 -0700)
committershamoon <4887959+shamoon@users.noreply.github.com>
Sat, 2 Aug 2025 12:22:55 +0000 (08:22 -0400)
src-ui/src/app/components/common/pdf-editor/pdf-editor.component.html
src-ui/src/app/components/common/pdf-editor/pdf-editor.component.scss
src-ui/src/app/components/common/pdf-editor/pdf-editor.component.spec.ts
src-ui/src/app/components/common/pdf-editor/pdf-editor.component.ts

index 229db02a03019c33352e261ba208c14a168677b6..44499be921cb65098f5c383b8461df9a013e4193 100644 (file)
@@ -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="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 mt-3">
+        <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>
index 72bb4e5b515d70cd92f3764c29b5bdb4b9ce52da..166990e19df6df7bd1b6110b3ab4dbd07ce0a750 100644 (file)
@@ -1,9 +1,62 @@
+
+
+.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: 120px;
+  background-color: gray;
+  height: 200px;
+
+  pdf-viewer {
+    width: 100%;
+    height: 100%;
+  }
+}
 
-    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;
+}
index d258e7462f990fe8bab9d8a23702753aca1808b4..47641d309d5d405d0553878a67ba904bd673e93a 100644 (file)
@@ -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)
   })
index ebedbc9c07b452b2c2cd4c50a62dc20044aa4003..d783d1406263d1f046ce376b9c1cf304bdb6d375 100644 (file)
@@ -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)
   }