mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-21 07:18:13 +00:00
Ok thats a better start, two tabs
This commit is contained in:
4 files changed
+199
-194
No files matched your search
+174
-179
@@ -1,62 +1,20 @@
|
||||
<pngx-page-header [title]="pageTitle" [id]="template.id">
|
||||
<div class="col-12 col-md-auto">
|
||||
<label class="visually-hidden" for="preview-document" i18n>Sample document</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text"><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 documents..."
|
||||
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 class="preview-search-status small" role="status" [class.text-danger]="searchStatus() === 'error'" [class.text-muted]="searchStatus() !== 'error'">
|
||||
@switch (searchStatus()) {
|
||||
@case ('loading') { <span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span><span i18n>Searching...</span> }
|
||||
@case ('empty') { <span i18n>No matching documents.</span> }
|
||||
@case ('error') { <span i18n>Document search failed.</span> }
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<a routerLink="/ocr-templates" class="btn btn-sm btn-outline-secondary" title="Close" i18n-title aria-label="Close" i18n-aria-label>
|
||||
<i-bs width="1.2em" height="1.2em" name="x"></i-bs><span class="d-none d-lg-inline ps-1" i18n>Close</span>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-primary" (click)="save()" [disabled]="saving">
|
||||
@if (saving) {
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span i18n>Save</span>
|
||||
</button>
|
||||
</pngx-page-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4 mb-3">
|
||||
<div class="btn-toolbar mb-1 pb-3 gap-2 border-bottom">
|
||||
<a routerLink="/ocr-templates" class="btn btn-sm btn-outline-secondary" title="Close" i18n-title aria-label="Close" i18n-aria-label>
|
||||
<i-bs width="1.2em" height="1.2em" name="x"></i-bs>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-primary ms-auto" (click)="save()" [disabled]="saving">
|
||||
@if (saving) {
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span i18n>Save</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-underline flex-nowrap flex-md-wrap overflow-auto">
|
||||
<li ngbNavItem="settings">
|
||||
<a ngbNavLink i18n>Settings</a>
|
||||
<a ngbNavLink i18n>Template</a>
|
||||
<ng-template ngbNavContent>
|
||||
<div class="row mb-3">
|
||||
<div class="col-9">
|
||||
@@ -74,30 +32,15 @@
|
||||
|
||||
<li ngbNavItem="zones">
|
||||
<a ngbNavLink><ng-container i18n>Zones</ng-container> <span class="badge bg-primary ms-2">{{ template.zones.length }}</span></a>
|
||||
<ng-template ngbNavContent>
|
||||
<pngx-ocr-template-zone-list
|
||||
[zones]="template.zones"
|
||||
[selectedZoneIndex]="selectedZoneIndex"
|
||||
[previewPage]="previewPage"
|
||||
[previewPageCount]="previewPageCount"
|
||||
[customFields]="customFields"
|
||||
(zoneSelected)="selectZone($event)"
|
||||
(zoneRemoved)="removeZone($event)"
|
||||
></pngx-ocr-template-zone-list>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
||||
<li ngbNavItem="zone">
|
||||
<a ngbNavLink i18n>Zone</a>
|
||||
<ng-template ngbNavContent>
|
||||
@if (selectedZone; as zone) {
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<strong>{{ zone.name }}</strong>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" (click)="deleteSelectedZone()">
|
||||
<i-bs name="trash" class="me-1"></i-bs><ng-container i18n>Delete zone</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-link px-0" (click)="deselectZone()">
|
||||
<i-bs name="arrow-left" class="me-1"></i-bs><ng-container i18n>All zones</ng-container>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" (click)="deleteSelectedZone()">
|
||||
<i-bs name="trash" class="me-1"></i-bs><ng-container i18n>Delete zone</ng-container>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@@ -325,9 +268,15 @@
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
<p class="text-muted" i18n>
|
||||
No zone selected.
|
||||
</p>
|
||||
<pngx-ocr-template-zone-list
|
||||
[zones]="template.zones"
|
||||
[selectedZoneIndex]="selectedZoneIndex"
|
||||
[previewPage]="previewPage"
|
||||
[previewPageCount]="previewPageCount"
|
||||
[customFields]="customFields"
|
||||
(zoneSelected)="selectZone($event)"
|
||||
(zoneRemoved)="removeZone($event)"
|
||||
></pngx-ocr-template-zone-list>
|
||||
}
|
||||
</ng-template>
|
||||
</li>
|
||||
@@ -338,6 +287,44 @@
|
||||
|
||||
<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>
|
||||
<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>
|
||||
@@ -346,117 +333,125 @@
|
||||
<i-bs name="pencil" class="me-1"></i-bs><span i18n>Draw</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group input-group-sm w-auto mw-100 text-nowrap order-1 order-sm-0">
|
||||
<button type="button" class="btn btn-outline-secondary" title="Previous page" i18n-title aria-label="Previous page" i18n-aria-label (click)="prevPage()" [disabled]="!previewDocId || previewStatus === 'loading' || previewPage <= 0">
|
||||
<i-bs name="arrow-left"></i-bs>
|
||||
</button>
|
||||
<span class="input-group-text" i18n>Page</span>
|
||||
<input class="form-control flex-grow-0 preview-page-input" type="number" min="1" [max]="previewPageCount" [(ngModel)]="previewPageDisplay" [disabled]="!previewDocId || previewStatus === 'loading'" aria-label="Preview page" i18n-aria-label />
|
||||
<span class="input-group-text" i18n>of {{ previewPageCount ?? '?' }}</span>
|
||||
<button type="button" class="btn btn-outline-secondary" title="Next page" i18n-title aria-label="Next page" i18n-aria-label (click)="nextPage()" [disabled]="!previewDocId || previewStatus === 'loading' || previewPage >= (previewPageCount ?? 1) - 1">
|
||||
<i-bs name="arrow-right"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm text-nowrap" role="group" aria-label="Zoom" i18n-aria-label>
|
||||
<button type="button" class="btn btn-outline-secondary" title="Zoom out" i18n-title aria-label="Zoom out" i18n-aria-label [disabled]="!imageLoaded || zoom <= 0.5" (click)="zoomOut()"><i-bs name="dash"></i-bs></button>
|
||||
<button type="button" class="btn btn-outline-secondary preview-zoom" title="Reset zoom" i18n-title [disabled]="!imageLoaded" (click)="resetZoom()">{{ zoom | percent: '1.0-0' }}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" title="Zoom in" i18n-title aria-label="Zoom in" i18n-aria-label [disabled]="!imageLoaded || zoom >= 4" (click)="zoomIn()"><i-bs name="plus"></i-bs></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zone-preview-scroll overflow-auto border" [attr.aria-busy]="previewStatus === 'loading'">
|
||||
@if (previewStatus === 'error') {
|
||||
<div class="py-5 px-3 text-center" role="alert">
|
||||
<i-bs name="exclamation-triangle" width="32" height="32" class="text-warning"></i-bs>
|
||||
<p class="my-3">{{ previewError }}</p>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="loadPreview()"><i-bs name="arrow-clockwise" class="me-1"></i-bs><span i18n>Retry</span></button>
|
||||
</div>
|
||||
} @else if (pageImageUrl) {
|
||||
<div class="position-relative" [class.d-none]="!imageLoaded" [style.width.%]="zoom * 100">
|
||||
<img
|
||||
#pageImage
|
||||
[src]="pageImageUrl"
|
||||
(load)="onImageLoad()"
|
||||
(error)="onImageError()"
|
||||
alt="Sample document page"
|
||||
i18n-alt
|
||||
class="d-block w-100"
|
||||
[style.visibility]="imageLoaded ? 'visible' : 'hidden'"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
@if (imageLoaded) {
|
||||
<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"
|
||||
(pointerdown)="onOverlayPointerDown($event)"
|
||||
(pointermove)="onOverlayPointerMove($event)"
|
||||
(pointerup)="onOverlayPointerUp($event)"
|
||||
(pointercancel)="onOverlayPointerCancel($event)"
|
||||
(lostpointercapture)="onOverlayPointerCancel($event)"
|
||||
>
|
||||
@for (zone of template.zones; track $index; let i = $index) {
|
||||
@if (zoneDisplayRect(i); as rect) {
|
||||
<g>
|
||||
<rect
|
||||
class="zone-rect"
|
||||
[class.zone-rect-selected]="selectedZoneIndex === i"
|
||||
[attr.x]="rect.x"
|
||||
[attr.y]="rect.y"
|
||||
[attr.width]="rect.w"
|
||||
[attr.height]="rect.h"
|
||||
[attr.stroke]="zoneColor(i)"
|
||||
[attr.fill]="zoneFill(i)"
|
||||
></rect>
|
||||
<text
|
||||
class="zone-label fw-semibold pe-none"
|
||||
[attr.x]="rect.x + overlayUnitSize(6)"
|
||||
[attr.y]="zoneLabelY(rect)"
|
||||
[attr.font-size]="overlayFontSize()"
|
||||
[attr.fill]="zoneColor(i)"
|
||||
>{{ zoneLabel(zone, i) }}</text>
|
||||
@if (searchStatus() === 'empty' || searchStatus() === 'error') {
|
||||
<div class="small mb-2" role="status" [class.text-danger]="searchStatus() === 'error'" [class.text-muted]="searchStatus() === 'empty'">
|
||||
@if (searchStatus() === 'error') {
|
||||
<span i18n>Document search failed.</span>
|
||||
} @else {
|
||||
<span i18n>No matching documents.</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="position-relative">
|
||||
<div class="zone-preview-scroll overflow-auto border" [attr.aria-busy]="previewStatus === 'loading'">
|
||||
@if (previewStatus === 'error') {
|
||||
<div class="py-5 px-3 text-center" role="alert">
|
||||
<i-bs name="exclamation-triangle" width="32" height="32" class="text-warning"></i-bs>
|
||||
<p class="my-3">{{ previewError }}</p>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" (click)="loadPreview()"><i-bs name="arrow-clockwise" class="me-1"></i-bs><span i18n>Retry</span></button>
|
||||
</div>
|
||||
} @else if (pageImageUrl) {
|
||||
<div class="position-relative" [class.d-none]="!imageLoaded" [style.width.%]="zoom * 100">
|
||||
<img
|
||||
#pageImage
|
||||
[src]="pageImageUrl"
|
||||
(load)="onImageLoad()"
|
||||
(error)="onImageError()"
|
||||
alt="Sample document page"
|
||||
i18n-alt
|
||||
class="d-block w-100"
|
||||
[style.visibility]="imageLoaded ? 'visible' : 'hidden'"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
@if (imageLoaded) {
|
||||
<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"
|
||||
(pointerdown)="onOverlayPointerDown($event)"
|
||||
(pointermove)="onOverlayPointerMove($event)"
|
||||
(pointerup)="onOverlayPointerUp($event)"
|
||||
(pointercancel)="onOverlayPointerCancel($event)"
|
||||
(lostpointercapture)="onOverlayPointerCancel($event)"
|
||||
>
|
||||
@for (zone of template.zones; track $index; let i = $index) {
|
||||
@if (zoneDisplayRect(i); as rect) {
|
||||
<g>
|
||||
<rect
|
||||
class="zone-rect"
|
||||
[class.zone-rect-selected]="selectedZoneIndex === i"
|
||||
[attr.x]="rect.x"
|
||||
[attr.y]="rect.y"
|
||||
[attr.width]="rect.w"
|
||||
[attr.height]="rect.h"
|
||||
[attr.stroke]="zoneColor(i)"
|
||||
[attr.fill]="zoneFill(i)"
|
||||
></rect>
|
||||
<text
|
||||
class="zone-label fw-semibold pe-none"
|
||||
[attr.x]="rect.x + overlayUnitSize(6)"
|
||||
[attr.y]="zoneLabelY(rect)"
|
||||
[attr.font-size]="overlayFontSize()"
|
||||
[attr.fill]="zoneColor(i)"
|
||||
>{{ zoneLabel(zone, i) }}</text>
|
||||
|
||||
@if (selectedZoneIndex === i && overlayMode === 'select') {
|
||||
@for (handle of resizeHandles(rect); track handle.handle) {
|
||||
<rect
|
||||
class="zone-resize-handle"
|
||||
[attr.x]="handle.x - overlayHandleSize() / 2"
|
||||
[attr.y]="handle.y - overlayHandleSize() / 2"
|
||||
[attr.width]="overlayHandleSize()"
|
||||
[attr.height]="overlayHandleSize()"
|
||||
[attr.fill]="zoneColor(i)"
|
||||
></rect>
|
||||
@if (selectedZoneIndex === i && overlayMode === 'select') {
|
||||
@for (handle of resizeHandles(rect); track handle.handle) {
|
||||
<rect
|
||||
class="zone-resize-handle"
|
||||
[attr.x]="handle.x - overlayHandleSize() / 2"
|
||||
[attr.y]="handle.y - overlayHandleSize() / 2"
|
||||
[attr.width]="overlayHandleSize()"
|
||||
[attr.height]="overlayHandleSize()"
|
||||
[attr.fill]="zoneColor(i)"
|
||||
></rect>
|
||||
}
|
||||
}
|
||||
}
|
||||
</g>
|
||||
</g>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if (drawingRect(); as rect) {
|
||||
<rect
|
||||
class="zone-drawing-rect"
|
||||
[attr.x]="rect.x"
|
||||
[attr.y]="rect.y"
|
||||
[attr.width]="rect.w"
|
||||
[attr.height]="rect.h"
|
||||
></rect>
|
||||
}
|
||||
</svg>
|
||||
}
|
||||
</div>
|
||||
@if (previewStatus === 'loading') {
|
||||
<div class="p-5 d-flex justify-content-center align-items-center" role="status">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden" i18n>Loading page...</span>
|
||||
@if (drawingRect(); as rect) {
|
||||
<rect
|
||||
class="zone-drawing-rect"
|
||||
[attr.x]="rect.x"
|
||||
[attr.y]="rect.y"
|
||||
[attr.width]="rect.w"
|
||||
[attr.height]="rect.h"
|
||||
></rect>
|
||||
}
|
||||
</svg>
|
||||
}
|
||||
</div>
|
||||
@if (previewStatus === 'loading') {
|
||||
<div class="p-5 d-flex justify-content-center align-items-center" role="status">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden" i18n>Loading page...</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<div class="py-5 px-3 text-center text-muted">
|
||||
<i-bs name="file-text" width="32" height="32"></i-bs>
|
||||
<p class="mt-3 mb-0" i18n>No sample document selected.</p>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<div class="py-5 px-3 text-center text-muted">
|
||||
<i-bs name="file-text" width="32" height="32"></i-bs>
|
||||
<p class="mt-3 mb-0" i18n>No sample document selected.</p>
|
||||
</div>
|
||||
@if (previewDocId && previewStatus !== 'error') {
|
||||
<div class="position-absolute bottom-0 start-50 translate-middle-x z-1 mb-3 d-flex gap-2 p-1 bg-body border rounded shadow">
|
||||
<div class="input-group input-group-sm w-auto flex-nowrap">
|
||||
<input class="form-control flex-grow-0 preview-page-input" type="number" min="1" [max]="previewPageCount" [(ngModel)]="previewPageDisplay" [disabled]="previewStatus === 'loading'" aria-label="Page" i18n-aria-label />
|
||||
<span class="input-group-text" i18n>of {{ previewPageCount ?? '?' }}</span>
|
||||
</div>
|
||||
<div class="input-group input-group-sm w-auto flex-nowrap" role="group" aria-label="Zoom" i18n-aria-label>
|
||||
<button type="button" class="btn btn-outline-secondary" aria-label="Zoom out" i18n-aria-label [disabled]="!imageLoaded || zoom <= 0.5" (click)="zoomOut()"><i-bs name="dash"></i-bs></button>
|
||||
<button type="button" class="btn btn-outline-secondary preview-zoom" title="Reset zoom" i18n-title [disabled]="!imageLoaded" (click)="resetZoom()">{{ zoom | percent: '1.0-0' }}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" aria-label="Zoom in" i18n-aria-label [disabled]="!imageLoaded || zoom >= 4" (click)="zoomIn()"><i-bs name="plus"></i-bs></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
-4
@@ -6,10 +6,6 @@
|
||||
max-height: 78vh;
|
||||
}
|
||||
|
||||
.preview-search-status {
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.preview-page-input {
|
||||
width: 3.5rem;
|
||||
}
|
||||
|
||||
+18
@@ -198,6 +198,24 @@ describe('OcrTemplateEditorComponent', () => {
|
||||
expect(component.overlayMode).toBe('select')
|
||||
})
|
||||
|
||||
it('opens the zone in the Zones tab and returns to the list', () => {
|
||||
component.template.zones = [zone(), zone()]
|
||||
component.activeTab = 'settings'
|
||||
|
||||
component.selectZone(1)
|
||||
expect(component.activeTab).toBe('zones')
|
||||
expect(component.selectedZone).toBe(component.template.zones[1])
|
||||
|
||||
component.deselectZone()
|
||||
expect(component.selectedZone).toBeNull()
|
||||
expect(component.activeTab).toBe('zones')
|
||||
|
||||
component.selectZone(0)
|
||||
component.deleteSelectedZone()
|
||||
expect(component.template.zones.length).toBe(1)
|
||||
expect(component.selectedZone).toBeNull()
|
||||
})
|
||||
|
||||
it('hides results when extraction settings or geometry change', () => {
|
||||
const selected = zone()
|
||||
component.template.zones = [selected]
|
||||
|
||||
+7
-11
@@ -80,7 +80,7 @@ import {
|
||||
resizeZone,
|
||||
} from './zone-geometry'
|
||||
|
||||
type ActiveTab = 'settings' | 'zones' | 'zone'
|
||||
type ActiveTab = 'settings' | 'zones'
|
||||
type ZoneFieldSelection = OcrBuiltinTarget | number | null
|
||||
type OverlayMode = 'select' | 'draw'
|
||||
type OverlayInteraction =
|
||||
@@ -438,14 +438,6 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
this.loadPreview()
|
||||
}
|
||||
|
||||
prevPage() {
|
||||
this.goToPage(this.previewPage - 1)
|
||||
}
|
||||
|
||||
nextPage() {
|
||||
this.goToPage(this.previewPage + 1)
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.zoom = Math.min(4, Math.round((this.zoom + 0.25) * 100) / 100)
|
||||
}
|
||||
@@ -819,7 +811,7 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
selectZone(index: number) {
|
||||
this.selectedZoneIndex = index
|
||||
this.activeTab = 'zone'
|
||||
this.activeTab = 'zones'
|
||||
this.clearZoneTest()
|
||||
const zone = this.template.zones[index]
|
||||
if (zone) {
|
||||
@@ -828,6 +820,11 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
deselectZone() {
|
||||
this.selectedZoneIndex = null
|
||||
this.clearZoneTest()
|
||||
}
|
||||
|
||||
testZone() {
|
||||
const zone = this.selectedZone
|
||||
if (!zone || !this.previewDocId) return
|
||||
@@ -896,7 +893,6 @@ export class OcrTemplateEditorComponent implements OnInit, OnDestroy {
|
||||
deleteSelectedZone() {
|
||||
if (this.selectedZoneIndex === null) return
|
||||
this.removeZone(this.selectedZoneIndex)
|
||||
this.activeTab = 'zones'
|
||||
}
|
||||
|
||||
save() {
|
||||
|
||||
Reference in new issue
Block a user