<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>
+
+
+.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;
+}
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)
})
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'
page: number
rotate: number
splitAfter: boolean
+ selected?: boolean
}
@Component({
templateUrl: './pdf-editor.component.html',
styleUrl: './pdf-editor.component.scss',
imports: [
+ CommonModule,
DragDropModule,
FormsModule,
PdfViewerModule,
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) {
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)
}