mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-21 15:28:13 +00:00
Drop the silly mode toggle
This commit is contained in:
4 files changed
+72
-97
No files matched your search
+36
-47
@@ -176,53 +176,43 @@
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 mb-2" role="toolbar" aria-label="Document preview" i18n-aria-label>
|
||||
<div class="flex-grow-1">
|
||||
<label class="visually-hidden" for="preview-document" i18n>Sample document</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
@if (searchStatus() === 'loading') {
|
||||
<span class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden" i18n>Searching...</span></span>
|
||||
} @else {
|
||||
<i-bs name="file-text"></i-bs>
|
||||
}
|
||||
</span>
|
||||
<input
|
||||
id="preview-document"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="previewDocModel"
|
||||
[ngbTypeahead]="searchDocuments"
|
||||
#documentSearch="ngbTypeahead"
|
||||
[inputFormatter]="documentFormatter"
|
||||
[resultFormatter]="documentFormatter"
|
||||
[resultTemplate]="documentResult"
|
||||
(input)="searchStatus.set('idle')"
|
||||
(selectItem)="onPreviewDocSelected($event)"
|
||||
[editable]="false"
|
||||
container="body"
|
||||
popupClass="ocr-template-document-results col-11 col-md-6 overflow-y-auto"
|
||||
placeholder="Search for a sample document..."
|
||||
i18n-placeholder
|
||||
/>
|
||||
@if (previewDocId) {
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="clearPreviewDoc(); documentSearch.dismissPopup()" title="Clear sample document" i18n-title aria-label="Clear sample document" i18n-aria-label>
|
||||
<i-bs name="x"></i-bs>
|
||||
</button>
|
||||
<div class="mb-2">
|
||||
<label class="visually-hidden" for="preview-document" i18n>Sample document</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
@if (searchStatus() === 'loading') {
|
||||
<span class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden" i18n>Searching...</span></span>
|
||||
} @else {
|
||||
<i-bs name="file-text"></i-bs>
|
||||
}
|
||||
</div>
|
||||
<ng-template #documentResult let-document="result">
|
||||
<span class="d-block text-wrap text-break">{{ documentFormatter(document) }}</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm text-nowrap" role="group" aria-label="Editing mode" i18n-aria-label>
|
||||
<button type="button" class="btn btn-outline-primary" [class.active]="overlayMode === 'select'" [attr.aria-pressed]="overlayMode === 'select'" [disabled]="!imageLoaded" (click)="setOverlayMode('select')" title="Select and move zones" i18n-title>
|
||||
<i-bs name="cursor" class="me-1"></i-bs><span i18n>Select</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-primary" [class.active]="overlayMode === 'draw'" [attr.aria-pressed]="overlayMode === 'draw'" [disabled]="!imageLoaded" (click)="setOverlayMode('draw')" title="Draw a zone" i18n-title>
|
||||
<i-bs name="pencil" class="me-1"></i-bs><span i18n>Draw</span>
|
||||
</button>
|
||||
</span>
|
||||
<input
|
||||
id="preview-document"
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="previewDocModel"
|
||||
[ngbTypeahead]="searchDocuments"
|
||||
#documentSearch="ngbTypeahead"
|
||||
[inputFormatter]="documentFormatter"
|
||||
[resultFormatter]="documentFormatter"
|
||||
[resultTemplate]="documentResult"
|
||||
(input)="searchStatus.set('idle')"
|
||||
(selectItem)="onPreviewDocSelected($event)"
|
||||
[editable]="false"
|
||||
container="body"
|
||||
popupClass="ocr-template-document-results col-11 col-md-6 overflow-y-auto"
|
||||
placeholder="Search for a sample document..."
|
||||
i18n-placeholder
|
||||
/>
|
||||
@if (previewDocId) {
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="clearPreviewDoc(); documentSearch.dismissPopup()" title="Clear sample document" i18n-title aria-label="Clear sample document" i18n-aria-label>
|
||||
<i-bs name="x"></i-bs>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<ng-template #documentResult let-document="result">
|
||||
<span class="d-block text-wrap text-break">{{ documentFormatter(document) }}</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
@if (searchStatus() === 'empty' || searchStatus() === 'error') {
|
||||
<div class="small mb-2" role="status" [class.text-danger]="searchStatus() === 'error'" [class.text-muted]="searchStatus() === 'empty'">
|
||||
@@ -258,7 +248,6 @@
|
||||
<svg
|
||||
#zoneOverlay
|
||||
class="zone-overlay position-absolute top-0 start-0 w-100 h-100"
|
||||
[class.zone-overlay-drawing]="overlayMode === 'draw'"
|
||||
[attr.viewBox]="overlayViewBox()"
|
||||
preserveAspectRatio="none"
|
||||
[style.cursor]="overlayCursor"
|
||||
@@ -289,7 +278,7 @@
|
||||
[attr.fill]="zoneColor(i)"
|
||||
>{{ zoneLabel(zone, i) }}</text>
|
||||
|
||||
@if (selectedZoneIndex === i && overlayMode === 'select') {
|
||||
@if (selectedZoneIndex === i) {
|
||||
@for (handle of resizeHandles(rect); track handle.handle) {
|
||||
<rect
|
||||
class="zone-resize-handle"
|
||||
|
||||
-6
@@ -20,12 +20,6 @@
|
||||
}
|
||||
|
||||
.zone-overlay {
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
|
||||
.zone-overlay-drawing,
|
||||
.zone-rect,
|
||||
.zone-resize-handle {
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
|
||||
+19
-10
@@ -171,18 +171,30 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
expect(fixture.nativeElement.textContent).toBe('empty')
|
||||
})
|
||||
|
||||
it('does not create zones by dragging empty space in Select mode', () => {
|
||||
it('clears the selection on a click in empty space without adding a zone', () => {
|
||||
prepareOverlay()
|
||||
component.onOverlayPointerDown(pointer(100, 100))
|
||||
component.onOverlayPointerMove(pointer(300, 200))
|
||||
component.onOverlayPointerUp(pointer(300, 200))
|
||||
component.template.zones = [zone()]
|
||||
component.selectZone(0)
|
||||
component.onOverlayPointerDown(pointer(600, 600))
|
||||
component.onOverlayPointerUp(pointer(602, 601))
|
||||
|
||||
expect(component.template.zones).toEqual([])
|
||||
expect(component.template.zones.length).toBe(1)
|
||||
expect(component.selectedZoneIndex).toBeNull()
|
||||
})
|
||||
|
||||
it('draws within the page bounds, uses the release position and returns to Select', () => {
|
||||
it('moves a zone when dragging on it instead of drawing', () => {
|
||||
prepareOverlay()
|
||||
component.template.zones = [zone()]
|
||||
component.onOverlayPointerDown(pointer(150, 150))
|
||||
component.onOverlayPointerUp(pointer(250, 200))
|
||||
|
||||
expect(component.template.zones.length).toBe(1)
|
||||
expect(component.template.zones[0]).toMatchObject({ x: 200, y: 150 })
|
||||
expect(component.selectedZoneIndex).toBe(0)
|
||||
})
|
||||
|
||||
it('draws in empty space within the page bounds, using the release position', () => {
|
||||
prepareOverlay()
|
||||
component.setOverlayMode('draw')
|
||||
component.onOverlayPointerDown(pointer(100, 100))
|
||||
component.onOverlayPointerUp(pointer(1200, 250))
|
||||
|
||||
@@ -193,12 +205,10 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
height: 150,
|
||||
})
|
||||
expect(component.selectedZoneIndex).toBe(0)
|
||||
expect(component.overlayMode).toBe('select')
|
||||
})
|
||||
|
||||
it('discards a drawing on Escape and ignores other pointers', () => {
|
||||
prepareOverlay()
|
||||
component.setOverlayMode('draw')
|
||||
component.onOverlayPointerDown(pointer(100, 100))
|
||||
component.onOverlayPointerUp(pointer(300, 200, 2))
|
||||
expect(component.drawingRect()).not.toBeNull()
|
||||
@@ -206,7 +216,6 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
component.cancelDrawing()
|
||||
component.onOverlayPointerUp(pointer(300, 200))
|
||||
expect(component.template.zones).toEqual([])
|
||||
expect(component.overlayMode).toBe('select')
|
||||
})
|
||||
|
||||
it('opens the zone in the Zones tab and returns to the list', () => {
|
||||
|
||||
+17
-34
@@ -88,7 +88,6 @@ import {
|
||||
|
||||
type ActiveTab = 'settings' | 'zones'
|
||||
type ZoneFieldSelection = OcrBuiltinTarget | number | null
|
||||
type OverlayMode = 'select' | 'draw'
|
||||
type OverlayInteraction =
|
||||
| { kind: 'idle' }
|
||||
| { kind: 'drawing'; pointerId: number; rect: DrawingRect }
|
||||
@@ -248,8 +247,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
selectedZoneIndex: number | null = null
|
||||
private overlayInteraction: OverlayInteraction = NO_OVERLAY_INTERACTION
|
||||
overlayMode: OverlayMode = 'select'
|
||||
overlayCursor = 'default'
|
||||
overlayCursor = 'crosshair'
|
||||
|
||||
private readonly zoneTest = signal<ZoneTest | null>(null)
|
||||
|
||||
@@ -484,15 +482,9 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
this.previewError = $localize`Could not load this page.`
|
||||
}
|
||||
|
||||
setOverlayMode(mode: OverlayMode) {
|
||||
this.stopOverlayInteraction()
|
||||
this.overlayMode = mode
|
||||
this.overlayCursor = mode === 'draw' ? 'crosshair' : 'default'
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape')
|
||||
cancelDrawing() {
|
||||
this.setOverlayMode('select')
|
||||
this.stopOverlayInteraction()
|
||||
}
|
||||
|
||||
onOverlayPointerDown(event: PointerEvent) {
|
||||
@@ -501,23 +493,6 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
if (!this.imageLoaded || !point) return
|
||||
event.preventDefault()
|
||||
|
||||
if (this.overlayMode === 'draw') {
|
||||
this.clearZoneTest()
|
||||
this.overlayInteraction = {
|
||||
kind: 'drawing',
|
||||
pointerId: event.pointerId,
|
||||
rect: {
|
||||
startX: point.x,
|
||||
startY: point.y,
|
||||
endX: point.x,
|
||||
endY: point.y,
|
||||
},
|
||||
}
|
||||
this.selectedZoneIndex = null
|
||||
this.overlayRef.nativeElement.setPointerCapture(event.pointerId)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.selectedZoneIndex !== null) {
|
||||
const handle = this.findHandleAt(point, this.selectedZoneIndex)
|
||||
if (handle) {
|
||||
@@ -551,8 +526,21 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
return
|
||||
}
|
||||
|
||||
// Empty space: start drawing a new zone. A click without a drag just clears
|
||||
// the selection (see onOverlayPointerUp).
|
||||
this.clearZoneTest()
|
||||
this.selectedZoneIndex = null
|
||||
this.overlayInteraction = {
|
||||
kind: 'drawing',
|
||||
pointerId: event.pointerId,
|
||||
rect: {
|
||||
startX: point.x,
|
||||
startY: point.y,
|
||||
endX: point.x,
|
||||
endY: point.y,
|
||||
},
|
||||
}
|
||||
this.overlayRef.nativeElement.setPointerCapture(event.pointerId)
|
||||
}
|
||||
|
||||
onOverlayPointerMove(event: PointerEvent) {
|
||||
@@ -594,10 +582,6 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private updateOverlayCursor(point: Point) {
|
||||
if (this.overlayMode === 'draw') {
|
||||
this.overlayCursor = 'crosshair'
|
||||
return
|
||||
}
|
||||
if (this.selectedZoneIndex !== null) {
|
||||
const handle = this.findHandleAt(point, this.selectedZoneIndex)
|
||||
if (handle) {
|
||||
@@ -605,7 +589,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
return
|
||||
}
|
||||
}
|
||||
this.overlayCursor = this.findZoneAt(point) !== null ? 'move' : 'default'
|
||||
this.overlayCursor = this.findZoneAt(point) !== null ? 'move' : 'crosshair'
|
||||
}
|
||||
|
||||
onOverlayPointerUp(event: PointerEvent) {
|
||||
@@ -624,7 +608,6 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.template.zones.push(this.createZoneFromRect(rect))
|
||||
this.setOverlayMode('select')
|
||||
this.selectZone(this.template.zones.length - 1)
|
||||
}
|
||||
|
||||
@@ -662,7 +645,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
private stopOverlayInteraction() {
|
||||
const interaction = this.overlayInteraction
|
||||
this.overlayInteraction = NO_OVERLAY_INTERACTION
|
||||
this.overlayCursor = this.overlayMode === 'draw' ? 'crosshair' : 'default'
|
||||
this.overlayCursor = 'crosshair'
|
||||
const overlay = this.overlayRef?.nativeElement
|
||||
if (
|
||||
interaction.kind !== 'idle' &&
|
||||
|
||||
Reference in new issue
Block a user