Unified toolbar w select, hover buttons

This commit is contained in:
shamoon 2025-07-01 13:35:15 -07:00
parent d3644463cc
commit 0418bc58b5
No known key found for this signature in database
4 changed files with 119 additions and 18 deletions

View File

@ -4,15 +4,37 @@
<button type="button" class="btn-close" aria-label="Close" (click)="cancel()"></button> <button type="button" class="btn-close" aria-label="Close" (click)="cancel()"></button>
</div> </div>
<div class="modal-body"> <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"> <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) { @for (p of pages; track p.page; let i = $index) {
<div class="page-item p-2" cdkDrag> <div class="page-item rounded p-2" cdkDrag (click)="toggleSelection(i)" [class.selected]="p.selected">
<div class="btn-group mb-1"> <div class="btn-toolbar hover-actions z-10">
<button class="btn btn-sm btn-secondary" (click)="rotate(i)"><i-bs name="arrow-clockwise"></i-bs></button> <div class="btn-group">
<button class="btn btn-sm btn-outline-secondary" (click)="toggleSplit(i)"><i-bs name="scissors"></i-bs></button> <button class="btn btn-sm btn-dark text-danger" (click)="remove(i); $event.stopPropagation()">
<button class="btn btn-sm btn-danger" (click)="remove(i)"><i-bs name="trash"></i-bs></button> <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>
<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> <pdf-viewer [src]="pdfSrc" [page]="p.page" [rotation]="p.rotate" [original-size]="false" [show-all]="false" [render-text]="false"></pdf-viewer>
</div> </div>
</div> </div>

View File

@ -1,9 +1,62 @@
.pdf-viewer-container {
background-color: gray;
height: 120px;
pdf-viewer {
width: 100%; .page-item {
height: 100%; 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;
}

View File

@ -23,13 +23,19 @@ describe('PDFEditorComponent', () => {
fixture.detectChanges() fixture.detectChanges()
}) })
it('should rotate and reorder pages', () => { it('should rotate, delete and reorder pages', () => {
component.pages = [ component.pages = [
{ page: 1, rotate: 0, splitAfter: false }, { page: 1, rotate: 0, splitAfter: false, selected: false },
{ page: 2, rotate: 0, splitAfter: 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) 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) component.drop({ previousIndex: 0, currentIndex: 1 } as any)
expect(component.pages[0].page).toBe(2) expect(component.pages[0].page).toBe(2)
}) })

View File

@ -3,6 +3,7 @@ import {
DragDropModule, DragDropModule,
moveItemInArray, moveItemInArray,
} from '@angular/cdk/drag-drop' } from '@angular/cdk/drag-drop'
import { CommonModule } from '@angular/common'
import { Component, OnInit, inject } from '@angular/core' import { Component, OnInit, inject } from '@angular/core'
import { FormsModule } from '@angular/forms' import { FormsModule } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
@ -15,6 +16,7 @@ interface PageOperation {
page: number page: number
rotate: number rotate: number
splitAfter: boolean splitAfter: boolean
selected?: boolean
} }
@Component({ @Component({
@ -22,6 +24,7 @@ interface PageOperation {
templateUrl: './pdf-editor.component.html', templateUrl: './pdf-editor.component.html',
styleUrl: './pdf-editor.component.scss', styleUrl: './pdf-editor.component.scss',
imports: [ imports: [
CommonModule,
DragDropModule, DragDropModule,
FormsModule, FormsModule,
PdfViewerModule, PdfViewerModule,
@ -52,11 +55,16 @@ export class PDFEditorComponent
page: i + 1, page: i + 1,
rotate: 0, rotate: 0,
splitAfter: false, splitAfter: false,
selected: false,
})) }))
} }
rotate(i: number) { rotateSelected(dir: number) {
this.pages[i].rotate = (this.pages[i].rotate + 90) % 360 for (let p of this.pages) {
if (p.selected) {
p.rotate = (p.rotate + dir + 360) % 360
}
}
} }
remove(i: number) { remove(i: number) {
@ -67,6 +75,18 @@ export class PDFEditorComponent
this.pages[i].splitAfter = !this.pages[i].splitAfter 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[]>) { drop(event: CdkDragDrop<PageOperation[]>) {
moveItemInArray(this.pages, event.previousIndex, event.currentIndex) moveItemInArray(this.pages, event.previousIndex, event.currentIndex)
} }