@pallasoft/ps-file-components 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ps-document-scanner-loader.d.ts +1 -1
- package/dist/components/ps-document-scanner-sheet.d.ts +1 -1
- package/dist/components/ps-document-scanner-sheet.js +87 -2
- package/dist/components/ps-document-scanner.d.ts +1 -1
- package/dist/components/ps-document-scanner.js +2 -2
- package/dist/components/ps-image-viewer.js +35 -16
- package/dist/components/ps-pdf-viewer.js +18 -1
- package/dist/components/ps-scan-camera-view.js +2 -2
- package/dist/components/ps-scan-page-strip.d.ts +8 -0
- package/dist/components/ps-scan-page-strip.js +8 -0
- package/dist/components/ps-scan-preview.d.ts +3 -1
- package/dist/components/ps-scan-preview.js +5 -1
- package/dist/components/ps-scan-viewfinder-overlay.d.ts +4 -2
- package/dist/components/ps-scan-viewfinder-overlay.js +6 -6
- package/dist/constants/ps-file-viewer-config.d.ts +1 -0
- package/dist/constants/ps-file-viewer-config.js +1 -0
- package/dist/constants/ps-scan-config.d.ts +7 -3
- package/dist/constants/ps-scan-config.js +7 -3
- package/dist/constants/ps-scan-detect-profiles.d.ts +10 -0
- package/dist/constants/ps-scan-detect-profiles.js +8 -0
- package/dist/esm/components/ps-document-scanner-sheet.js +87 -2
- package/dist/esm/components/ps-document-scanner.js +2 -2
- package/dist/esm/components/ps-image-viewer.js +35 -16
- package/dist/esm/components/ps-pdf-viewer.js +18 -1
- package/dist/esm/components/ps-scan-camera-view.js +2 -2
- package/dist/esm/components/ps-scan-page-strip.js +4 -0
- package/dist/esm/components/ps-scan-preview.js +5 -1
- package/dist/esm/components/ps-scan-viewfinder-overlay.js +6 -6
- package/dist/esm/constants/ps-file-viewer-config.js +1 -0
- package/dist/esm/constants/ps-scan-config.js +7 -3
- package/dist/esm/constants/ps-scan-detect-profiles.js +5 -0
- package/dist/esm/hooks/use-ps-drag-pan.js +66 -0
- package/dist/esm/hooks/use-ps-scan-live-detection.js +47 -22
- package/dist/esm/hooks/use-ps-scan-quad-tween.js +34 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/models/ps-document-scanner-page-labels.js +1 -0
- package/dist/esm/models/ps-drag-pan-handlers.js +1 -0
- package/dist/esm/models/ps-drag-pan-options.js +1 -0
- package/dist/esm/models/ps-scan-detect-profile.js +1 -0
- package/dist/esm/models/ps-scan-multi-page.js +1 -0
- package/dist/esm/models/ps-scan-page.js +1 -0
- package/dist/esm/models/ps-scan-pdf-page.js +1 -0
- package/dist/esm/services/ps-pan-service.js +13 -0
- package/dist/esm/services/ps-scan-edge-service.js +7 -4
- package/dist/esm/services/ps-scan-file-name-service.js +3 -0
- package/dist/esm/services/ps-scan-frame-service.js +10 -0
- package/dist/esm/services/ps-scan-pdf-service.js +69 -0
- package/dist/esm/services/ps-scan-quad-tween-service.js +10 -0
- package/dist/hooks/use-ps-drag-pan.d.ts +3 -0
- package/dist/hooks/use-ps-drag-pan.js +70 -0
- package/dist/hooks/use-ps-scan-live-detection.js +46 -21
- package/dist/hooks/use-ps-scan-quad-tween.d.ts +3 -0
- package/dist/hooks/use-ps-scan-quad-tween.js +38 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/models/ps-document-scanner-page-labels.d.ts +6 -0
- package/dist/models/ps-document-scanner-page-labels.js +2 -0
- package/dist/models/ps-document-scanner-props.d.ts +3 -0
- package/dist/models/ps-document-scanner-sheet-props.d.ts +2 -0
- package/dist/models/ps-drag-pan-handlers.d.ts +8 -0
- package/dist/models/ps-drag-pan-handlers.js +2 -0
- package/dist/models/ps-drag-pan-options.d.ts +9 -0
- package/dist/models/ps-drag-pan-options.js +2 -0
- package/dist/models/ps-scan-detect-profile.d.ts +4 -0
- package/dist/models/ps-scan-detect-profile.js +2 -0
- package/dist/models/ps-scan-live-detection.d.ts +3 -1
- package/dist/models/ps-scan-multi-page.d.ts +9 -0
- package/dist/models/ps-scan-multi-page.js +2 -0
- package/dist/models/ps-scan-page.d.ts +4 -0
- package/dist/models/ps-scan-page.js +2 -0
- package/dist/models/ps-scan-pdf-page.d.ts +5 -0
- package/dist/models/ps-scan-pdf-page.js +2 -0
- package/dist/services/ps-pan-service.d.ts +6 -0
- package/dist/services/ps-pan-service.js +17 -0
- package/dist/services/ps-scan-edge-service.d.ts +2 -1
- package/dist/services/ps-scan-edge-service.js +7 -4
- package/dist/services/ps-scan-file-name-service.d.ts +1 -0
- package/dist/services/ps-scan-file-name-service.js +3 -0
- package/dist/services/ps-scan-frame-service.d.ts +3 -0
- package/dist/services/ps-scan-frame-service.js +14 -0
- package/dist/services/ps-scan-pdf-service.d.ts +10 -0
- package/dist/services/ps-scan-pdf-service.js +73 -0
- package/dist/services/ps-scan-quad-tween-service.d.ts +4 -0
- package/dist/services/ps-scan-quad-tween-service.js +14 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PsDocumentScannerLoader: import("react").LazyExoticComponent<({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }: import("../models/ps-document-scanner-sheet-props").PsDocumentScannerSheetProps) => import("react").ReactPortal | null>;
|
|
1
|
+
export declare const PsDocumentScannerLoader: import("react").LazyExoticComponent<({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, pageLabels, }: import("../models/ps-document-scanner-sheet-props").PsDocumentScannerSheetProps) => import("react").ReactPortal | null>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentScannerSheetProps } from '../models/ps-document-scanner-sheet-props';
|
|
2
|
-
export declare const PsDocumentScannerSheet: ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
|
|
2
|
+
export declare const PsDocumentScannerSheet: ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, pageLabels, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
|
|
@@ -8,25 +8,32 @@ const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
|
8
8
|
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
9
9
|
const react_dom_1 = require("react-dom");
|
|
10
10
|
const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
|
|
11
|
+
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
11
12
|
const ps_scan_step_1 = require("../constants/ps-scan-step");
|
|
12
13
|
const use_ps_focus_trap_1 = require("../hooks/use-ps-focus-trap");
|
|
13
14
|
const use_ps_scan_camera_1 = require("../hooks/use-ps-scan-camera");
|
|
14
15
|
const use_ps_scan_live_detection_1 = require("../hooks/use-ps-scan-live-detection");
|
|
15
16
|
const use_ps_scan_photo_1 = require("../hooks/use-ps-scan-photo");
|
|
16
17
|
const ps_scan_file_name_service_1 = require("../services/ps-scan-file-name-service");
|
|
18
|
+
const ps_scan_pdf_service_1 = require("../services/ps-scan-pdf-service");
|
|
17
19
|
const ps_scan_pipeline_service_1 = require("../services/ps-scan-pipeline-service");
|
|
18
20
|
const ps_scan_camera_view_1 = require("./ps-scan-camera-view");
|
|
19
21
|
const ps_scan_crop_editor_1 = require("./ps-scan-crop-editor");
|
|
20
22
|
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
23
|
+
const ps_scan_page_strip_1 = require("./ps-scan-page-strip");
|
|
21
24
|
const ps_scan_permission_view_1 = require("./ps-scan-permission-view");
|
|
22
25
|
const ps_scan_preview_1 = require("./ps-scan-preview");
|
|
23
|
-
const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }) => {
|
|
26
|
+
const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, pageLabels, }) => {
|
|
24
27
|
const tier = (0, ps_core_components_1.usePsViewport)();
|
|
25
28
|
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
|
|
26
29
|
const content = (0, react_1.useRef)(null);
|
|
27
30
|
const { clear, detectAgain, load, photo, setCorners } = (0, use_ps_scan_photo_1.usePsScanPhoto)();
|
|
28
31
|
const [step, setStep] = (0, react_1.useState)(ps_scan_step_1.PsScanStep.Camera);
|
|
29
32
|
const [result, setResult] = (0, react_1.useState)(null);
|
|
33
|
+
const [pages, setPages] = (0, react_1.useState)([]);
|
|
34
|
+
const [isAddingPage, setIsAddingPage] = (0, react_1.useState)(false);
|
|
35
|
+
const pagesRef = (0, react_1.useRef)([]);
|
|
36
|
+
const nextPageId = (0, react_1.useRef)(1);
|
|
30
37
|
const [announcement, setAnnouncement] = (0, react_1.useState)('');
|
|
31
38
|
const [error, setError] = (0, react_1.useState)('');
|
|
32
39
|
const [isCapturing, setIsCapturing] = (0, react_1.useState)(false);
|
|
@@ -44,6 +51,12 @@ const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, o
|
|
|
44
51
|
if (result !== null)
|
|
45
52
|
URL.revokeObjectURL(result.url);
|
|
46
53
|
}, [result]);
|
|
54
|
+
(0, react_1.useEffect)(() => {
|
|
55
|
+
pagesRef.current = pages;
|
|
56
|
+
}, [pages]);
|
|
57
|
+
(0, react_1.useEffect)(() => () => {
|
|
58
|
+
pagesRef.current.forEach((page) => URL.revokeObjectURL(page.url));
|
|
59
|
+
}, []);
|
|
47
60
|
const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
|
|
48
61
|
const accept = async (source) => {
|
|
49
62
|
setError('');
|
|
@@ -97,20 +110,92 @@ const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, o
|
|
|
97
110
|
setError(labels.processFailed);
|
|
98
111
|
}
|
|
99
112
|
};
|
|
113
|
+
const multiPageDimension = Math.min(maxDimension, ps_scan_config_1.PsScanConfig.multiPageMaxDimension);
|
|
114
|
+
const renderPage = async () => {
|
|
115
|
+
if (photo === null)
|
|
116
|
+
return null;
|
|
117
|
+
const rendered = await ps_scan_pipeline_service_1.PsScanPipelineService.render(photo.source, photo.corners, multiPageDimension, jpegQuality);
|
|
118
|
+
const id = nextPageId.current;
|
|
119
|
+
nextPageId.current += 1;
|
|
120
|
+
return { ...rendered, id, url: URL.createObjectURL(rendered.blob) };
|
|
121
|
+
};
|
|
122
|
+
const addPage = async () => {
|
|
123
|
+
if (result === null || isAddingPage)
|
|
124
|
+
return;
|
|
125
|
+
setIsAddingPage(true);
|
|
126
|
+
setError('');
|
|
127
|
+
try {
|
|
128
|
+
const page = await renderPage();
|
|
129
|
+
if (page === null)
|
|
130
|
+
return;
|
|
131
|
+
setPages((current) => [...current, page]);
|
|
132
|
+
clear();
|
|
133
|
+
setResult(null);
|
|
134
|
+
setStep(ps_scan_step_1.PsScanStep.Camera);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
setError(labels.processFailed);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
setIsAddingPage(false);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const removePage = (id) => {
|
|
144
|
+
const removed = pages.find((page) => page.id === id);
|
|
145
|
+
if (removed !== undefined)
|
|
146
|
+
URL.revokeObjectURL(removed.url);
|
|
147
|
+
setPages((current) => current.filter((page) => page.id !== id));
|
|
148
|
+
};
|
|
149
|
+
const useAsPdf = async () => {
|
|
150
|
+
if (isAddingPage)
|
|
151
|
+
return;
|
|
152
|
+
setIsAddingPage(true);
|
|
153
|
+
setError('');
|
|
154
|
+
try {
|
|
155
|
+
const last = await renderPage();
|
|
156
|
+
if (last === null)
|
|
157
|
+
return;
|
|
158
|
+
const pdf = await ps_scan_pdf_service_1.PsScanPdfService.build([...pages, last]);
|
|
159
|
+
URL.revokeObjectURL(last.url);
|
|
160
|
+
onComplete([
|
|
161
|
+
new File([pdf], ps_scan_file_name_service_1.PsScanFileNameService.buildPdf(fileName), { type: 'application/pdf' }),
|
|
162
|
+
]);
|
|
163
|
+
onClose();
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
setError(labels.processFailed);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
setIsAddingPage(false);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
100
172
|
const use = () => {
|
|
101
173
|
if (result === null)
|
|
102
174
|
return;
|
|
175
|
+
if (pages.length > 0) {
|
|
176
|
+
void useAsPdf();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
103
179
|
onComplete([
|
|
104
180
|
new File([result.blob], ps_scan_file_name_service_1.PsScanFileNameService.build(fileName), { type: 'image/jpeg' }),
|
|
105
181
|
]);
|
|
106
182
|
onClose();
|
|
107
183
|
};
|
|
184
|
+
const multiPage = pageLabels === null
|
|
185
|
+
? undefined
|
|
186
|
+
: {
|
|
187
|
+
isBusy: isAddingPage,
|
|
188
|
+
labels: pageLabels,
|
|
189
|
+
onAddPage: addPage,
|
|
190
|
+
onRemovePage: removePage,
|
|
191
|
+
pages,
|
|
192
|
+
};
|
|
108
193
|
if (typeof document === 'undefined')
|
|
109
194
|
return null;
|
|
110
195
|
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
|
|
111
196
|
? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
|
|
112
197
|
: 'fixed inset-0 bg-black animate-ps-fade-in', role: 'dialog', style: { zIndex }, children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsLayer, { zIndex: zIndex, children: (0, jsx_runtime_1.jsxs)("div", { className: isDesktop
|
|
113
198
|
? 'relative h-[min(52rem,calc(100dvh-3rem))] w-[min(76rem,calc(100vw-3rem))] overflow-hidden rounded-2xl bg-black text-white shadow-2xl ring-1 ring-white/10 animate-ps-scale-in'
|
|
114
|
-
: 'relative h-full overflow-hidden bg-black text-white', ref: content, children: [(0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), isCameraStep && showsCamera ? ((0, jsx_runtime_1.jsx)(ps_scan_camera_view_1.PsScanCameraView, { canTorch: camera.canTorch, detection: detection, flashKey: flashKey, isBusy: isCapturing, isReady: isReady, isTorchOn: camera.isTorchOn, labels: labels, onCapture: capture, onClose: onClose, onFile: accept, onToggleTorch: camera.toggleTorch, videoRef: camera.videoRef, videoTarget: camera.attachVideo })) : null, isCameraStep && !showsCamera ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_scan_permission_view_1.PsScanPermissionView, { labels: labels, onAllow: camera.allow, onFile: accept, status: camera.status }), (0, jsx_runtime_1.jsx)("div", { className: 'absolute left-4 top-[max(1rem,env(safe-area-inset-top))] flex gap-2', children: (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: labels.close, onClick: onClose }) })] })) : null, step === ps_scan_step_1.PsScanStep.Crop && photo !== null ? ((0, jsx_runtime_1.jsx)(ps_scan_crop_editor_1.PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: setCorners, onContinue: flatten, onRetake: retake, photo: photo })) : null, step === ps_scan_step_1.PsScanStep.Preview ? ((0, jsx_runtime_1.jsx)(ps_scan_preview_1.PsScanPreview, { error: error, labels: labels, onAdjust: adjust, onRetake: retake, onUse: use, result: result })) : null, isCameraStep && error !== '' ? ((0, jsx_runtime_1.jsx)("p", { className: 'absolute inset-x-4 bottom-36 mx-auto max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })) : null] }) }) }), document.body);
|
|
199
|
+
: 'relative h-full overflow-hidden bg-black text-white', ref: content, children: [(0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), isCameraStep && showsCamera ? ((0, jsx_runtime_1.jsx)(ps_scan_camera_view_1.PsScanCameraView, { canTorch: camera.canTorch, detection: detection, flashKey: flashKey, isBusy: isCapturing, isReady: isReady, isTorchOn: camera.isTorchOn, labels: labels, onCapture: capture, onClose: onClose, onFile: accept, onToggleTorch: camera.toggleTorch, videoRef: camera.videoRef, videoTarget: camera.attachVideo })) : null, isCameraStep && !showsCamera ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_scan_permission_view_1.PsScanPermissionView, { labels: labels, onAllow: camera.allow, onFile: accept, status: camera.status }), (0, jsx_runtime_1.jsx)("div", { className: 'absolute left-4 top-[max(1rem,env(safe-area-inset-top))] flex gap-2', children: (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: labels.close, onClick: onClose }) })] })) : null, step === ps_scan_step_1.PsScanStep.Crop && photo !== null ? ((0, jsx_runtime_1.jsx)(ps_scan_crop_editor_1.PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: setCorners, onContinue: flatten, onRetake: retake, photo: photo })) : null, step === ps_scan_step_1.PsScanStep.Preview ? ((0, jsx_runtime_1.jsx)(ps_scan_preview_1.PsScanPreview, { error: error, labels: labels, multiPage: multiPage, onAdjust: adjust, onRetake: retake, onUse: use, result: result })) : null, isCameraStep && pageLabels !== null && pages.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'absolute inset-x-0 bottom-32 flex justify-center', children: (0, jsx_runtime_1.jsx)(ps_scan_page_strip_1.PsScanPageStrip, { labels: pageLabels, onRemove: removePage, pages: pages }) })) : null, isCameraStep && error !== '' ? ((0, jsx_runtime_1.jsx)("p", { className: 'absolute inset-x-4 bottom-36 mx-auto max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })) : null] }) }) }), document.body);
|
|
115
200
|
};
|
|
116
201
|
exports.PsDocumentScannerSheet = PsDocumentScannerSheet;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentScannerProps } from '../models/ps-document-scanner-props';
|
|
2
|
-
export declare const PsDocumentScanner: ({ fileName, isOpen, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
|
|
2
|
+
export declare const PsDocumentScanner: ({ fileName, isMultiPage, isOpen, jpegQuality, labels, maxDimension, onClose, onComplete, pageLabels, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
|
|
@@ -7,10 +7,10 @@ const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
|
7
7
|
const ps_document_scanner_labels_1 = require("../constants/ps-document-scanner-labels");
|
|
8
8
|
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
9
9
|
const ps_document_scanner_loader_1 = require("./ps-document-scanner-loader");
|
|
10
|
-
const PsDocumentScanner = ({ fileName = ps_scan_config_1.PsScanConfig.defaultFileName, isOpen, jpegQuality = ps_scan_config_1.PsScanConfig.defaultJpegQuality, labels, maxDimension = ps_scan_config_1.PsScanConfig.defaultMaxDimension, onClose, onComplete, }) => {
|
|
10
|
+
const PsDocumentScanner = ({ fileName = ps_scan_config_1.PsScanConfig.defaultFileName, isMultiPage = false, isOpen, jpegQuality = ps_scan_config_1.PsScanConfig.defaultJpegQuality, labels, maxDimension = ps_scan_config_1.PsScanConfig.defaultMaxDimension, onClose, onComplete, pageLabels, }) => {
|
|
11
11
|
const isHydrated = (0, ps_core_components_1.usePsHydrated)();
|
|
12
12
|
if (!isOpen || !isHydrated)
|
|
13
13
|
return null;
|
|
14
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ps_document_scanner_loader_1.PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...ps_document_scanner_labels_1.PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, onClose: onClose, onComplete: onComplete }) }));
|
|
14
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ps_document_scanner_loader_1.PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...ps_document_scanner_labels_1.PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, onClose: onClose, onComplete: onComplete, pageLabels: isMultiPage && pageLabels !== undefined ? pageLabels : null }) }));
|
|
15
15
|
};
|
|
16
16
|
exports.PsDocumentScanner = PsDocumentScanner;
|
|
@@ -3,31 +3,50 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PsImageViewer = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const react_1 = require("react");
|
|
6
|
+
const use_ps_drag_pan_1 = require("../hooks/use-ps-drag-pan");
|
|
7
|
+
const ps_pan_service_1 = require("../services/ps-pan-service");
|
|
6
8
|
const PsImageViewer = ({ file, labels, rotation, zoom }) => {
|
|
7
9
|
const [offset, setOffset] = (0, react_1.useState)({ x: 0, y: 0 });
|
|
8
10
|
const [hasFailed, setHasFailed] = (0, react_1.useState)(false);
|
|
9
|
-
const
|
|
11
|
+
const pan = (0, react_1.useRef)(null);
|
|
12
|
+
const base = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
13
|
+
const frame = (0, react_1.useRef)(0);
|
|
14
|
+
const pending = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
10
15
|
(0, react_1.useEffect)(() => {
|
|
11
16
|
setOffset({ x: 0, y: 0 });
|
|
12
17
|
setHasFailed(false);
|
|
13
18
|
}, [file.id, zoom === 1]);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
19
|
-
};
|
|
20
|
-
const onPointerMove = (event) => {
|
|
21
|
-
if (drag.current === null)
|
|
22
|
-
return;
|
|
23
|
-
setOffset({ x: event.clientX - drag.current.x, y: event.clientY - drag.current.y });
|
|
19
|
+
(0, react_1.useEffect)(() => () => cancelAnimationFrame(frame.current), []);
|
|
20
|
+
const write = (next) => {
|
|
21
|
+
if (pan.current !== null)
|
|
22
|
+
pan.current.style.transform = ps_pan_service_1.PsPanService.translate(next);
|
|
24
23
|
};
|
|
24
|
+
const handlers = (0, use_ps_drag_pan_1.usePsDragPan)({
|
|
25
|
+
activationDistance: 0,
|
|
26
|
+
isEnabled: zoom > 1,
|
|
27
|
+
isMouseOnly: false,
|
|
28
|
+
onEnd: (delta) => {
|
|
29
|
+
cancelAnimationFrame(frame.current);
|
|
30
|
+
frame.current = 0;
|
|
31
|
+
const next = ps_pan_service_1.PsPanService.add(base.current, delta);
|
|
32
|
+
write(next);
|
|
33
|
+
setOffset(next);
|
|
34
|
+
},
|
|
35
|
+
onMove: (delta) => {
|
|
36
|
+
pending.current = ps_pan_service_1.PsPanService.add(base.current, delta);
|
|
37
|
+
if (frame.current !== 0)
|
|
38
|
+
return;
|
|
39
|
+
frame.current = requestAnimationFrame(() => {
|
|
40
|
+
frame.current = 0;
|
|
41
|
+
write(pending.current);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
onStart: () => {
|
|
45
|
+
base.current = offset;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
25
48
|
if (hasFailed)
|
|
26
49
|
return (0, jsx_runtime_1.jsx)("p", { className: 'm-auto text-sm text-white/70', children: labels.loadFailed });
|
|
27
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: `
|
|
28
|
-
drag.current = null;
|
|
29
|
-
}, children: (0, jsx_runtime_1.jsx)("img", { alt: file.name, className: 'max-h-full max-w-full select-none object-contain shadow-2xl transition-transform duration-150', draggable: false, onError: () => setHasFailed(true), src: file.url, style: {
|
|
30
|
-
transform: `translate(${offset.x}px, ${offset.y}px) scale(${zoom}) rotate(${rotation}deg)`,
|
|
31
|
-
} }) }));
|
|
50
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `h-full w-full touch-none overflow-hidden ${zoom > 1 ? 'cursor-grab active:cursor-grabbing' : ''}`, ...handlers, children: (0, jsx_runtime_1.jsx)("div", { className: 'flex h-full w-full items-center justify-center', ref: pan, style: { transform: ps_pan_service_1.PsPanService.translate(offset) }, children: (0, jsx_runtime_1.jsx)("img", { alt: file.name, className: 'max-h-full max-w-full select-none object-contain shadow-2xl transition-transform duration-150', draggable: false, onError: () => setHasFailed(true), src: file.url, style: { transform: `scale(${zoom}) rotate(${rotation}deg)` } }) }) }));
|
|
32
51
|
};
|
|
33
52
|
exports.PsImageViewer = PsImageViewer;
|
|
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
7
7
|
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
8
|
+
const use_ps_drag_pan_1 = require("../hooks/use-ps-drag-pan");
|
|
8
9
|
const ps_pdf_service_1 = require("../services/ps-pdf-service");
|
|
9
10
|
const ps_pdf_page_1 = require("./ps-pdf-page");
|
|
10
11
|
const ps_pdf_thumbnail_1 = require("./ps-pdf-thumbnail");
|
|
@@ -16,6 +17,22 @@ const PsPdfViewer = ({ activeMatch, file, labels, onMatchCount, pdfWorkerSrc, qu
|
|
|
16
17
|
const [currentPage, setCurrentPage] = (0, react_1.useState)(1);
|
|
17
18
|
const [frame, frameSize] = (0, ps_core_components_1.usePsElementSize)();
|
|
18
19
|
const scroller = (0, react_1.useRef)(null);
|
|
20
|
+
const scrollStart = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
21
|
+
const handlers = (0, use_ps_drag_pan_1.usePsDragPan)({
|
|
22
|
+
activationDistance: ps_file_viewer_config_1.PsFileViewerConfig.dragActivationDistance,
|
|
23
|
+
isEnabled: true,
|
|
24
|
+
isMouseOnly: true,
|
|
25
|
+
onEnd: () => undefined,
|
|
26
|
+
onMove: (delta) => {
|
|
27
|
+
if (scroller.current === null)
|
|
28
|
+
return;
|
|
29
|
+
scroller.current.scrollLeft = scrollStart.current.x - delta.x;
|
|
30
|
+
scroller.current.scrollTop = scrollStart.current.y - delta.y;
|
|
31
|
+
},
|
|
32
|
+
onStart: (element) => {
|
|
33
|
+
scrollStart.current = { x: element.scrollLeft, y: element.scrollTop };
|
|
34
|
+
},
|
|
35
|
+
});
|
|
19
36
|
(0, react_1.useEffect)(() => {
|
|
20
37
|
let isCurrent = true;
|
|
21
38
|
let opened = null;
|
|
@@ -86,7 +103,7 @@ const PsPdfViewer = ({ activeMatch, file, labels, onMatchCount, pdfWorkerSrc, qu
|
|
|
86
103
|
return Math.max(0.1, fit * zoom);
|
|
87
104
|
}, [frameSize.width, pageWidth, zoom]);
|
|
88
105
|
const pages = document === null ? [] : Array.from({ length: document.numPages }, (_, index) => index + 1);
|
|
89
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full w-full min-w-0', children: [document !== null && document.numPages > 1 ? ((0, jsx_runtime_1.jsx)("nav", { className: 'ps-scrollbar-hidden hidden w-24 shrink-0 flex-col items-center gap-2 overflow-y-auto border-r border-white/10 py-3 md:flex', children: pages.map((pageNumber) => ((0, jsx_runtime_1.jsx)(ps_pdf_thumbnail_1.PsPdfThumbnail, { document: document, hasMatches: matches.some((match) => match.pageNumber === pageNumber), isCurrent: pageNumber === currentPage, label: labels.page, onSelect: () => scrollToPage(pageNumber), pageNumber: pageNumber, rotation: rotation }, pageNumber))) })) : null, (0, jsx_runtime_1.jsx)("div", { className: 'min-w-0 flex-1', ref: frame, children: hasFailed ? ((0, jsx_runtime_1.jsx)("p", { className: 'flex h-full items-center justify-center text-sm text-white/70', children: labels.loadFailed })) : document === null ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full items-center justify-center text-white', children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', size: 'large' }) })) : ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full flex-col items-center gap-4 overflow-auto px-6 py-6', onScroll: (event) => {
|
|
106
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full w-full min-w-0', children: [document !== null && document.numPages > 1 ? ((0, jsx_runtime_1.jsx)("nav", { className: 'ps-scrollbar-hidden hidden w-24 shrink-0 flex-col items-center gap-2 overflow-y-auto border-r border-white/10 py-3 md:flex', children: pages.map((pageNumber) => ((0, jsx_runtime_1.jsx)(ps_pdf_thumbnail_1.PsPdfThumbnail, { document: document, hasMatches: matches.some((match) => match.pageNumber === pageNumber), isCurrent: pageNumber === currentPage, label: labels.page, onSelect: () => scrollToPage(pageNumber), pageNumber: pageNumber, rotation: rotation }, pageNumber))) })) : null, (0, jsx_runtime_1.jsx)("div", { className: 'min-w-0 flex-1', ref: frame, children: hasFailed ? ((0, jsx_runtime_1.jsx)("p", { className: 'flex h-full items-center justify-center text-sm text-white/70', children: labels.loadFailed })) : document === null ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full items-center justify-center text-white', children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', size: 'large' }) })) : ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full cursor-grab flex-col items-center gap-4 overflow-auto px-6 py-6 active:cursor-grabbing', ...handlers, onScroll: (event) => {
|
|
90
107
|
const top = event.currentTarget.scrollTop + event.currentTarget.clientHeight / 3;
|
|
91
108
|
const visible = Array.from(event.currentTarget.querySelectorAll('[data-ps-pdf-page]')).find((node) => node.offsetTop + node.offsetHeight > top);
|
|
92
109
|
if (visible)
|
|
@@ -14,12 +14,12 @@ const ps_scan_viewfinder_overlay_1 = require("./ps-scan-viewfinder-overlay");
|
|
|
14
14
|
const PsScanCameraView = ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }) => {
|
|
15
15
|
const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
|
|
16
16
|
const [video, setVideo] = (0, react_1.useState)({ height: 0, width: 0 });
|
|
17
|
-
const hint = detection.
|
|
17
|
+
const hint = detection.hasQuad ? labels.hintFound : labels.hintFind;
|
|
18
18
|
const measure = () => {
|
|
19
19
|
const element = videoRef.current;
|
|
20
20
|
if (element !== null)
|
|
21
21
|
setVideo({ height: element.videoHeight, width: element.videoWidth });
|
|
22
22
|
};
|
|
23
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'relative h-full min-h-0 overflow-hidden bg-black text-white', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-0', ref: areaRef, children: [(0, jsx_runtime_1.jsx)("video", { "aria-hidden": 'true', autoPlay: true, className: ps_design_system_1.PallasoftClassNameService.merge('size-full object-cover transition-opacity duration-300', isReady ? 'opacity-100' : 'opacity-0'), muted: true, onLoadedMetadata: measure, onResize: measure, playsInline: true, ref: videoTarget }), isReady ? ((0, jsx_runtime_1.jsx)(ps_scan_viewfinder_overlay_1.PsScanViewfinderOverlay, { area: area,
|
|
23
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'relative h-full min-h-0 overflow-hidden bg-black text-white', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-0', ref: areaRef, children: [(0, jsx_runtime_1.jsx)("video", { "aria-hidden": 'true', autoPlay: true, className: ps_design_system_1.PallasoftClassNameService.merge('size-full object-cover transition-opacity duration-300', isReady ? 'opacity-100' : 'opacity-0'), muted: true, onLoadedMetadata: measure, onResize: measure, playsInline: true, ref: videoTarget }), isReady ? ((0, jsx_runtime_1.jsx)(ps_scan_viewfinder_overlay_1.PsScanViewfinderOverlay, { area: area, hasQuad: detection.hasQuad, quadRef: detection.quadRef, video: video })) : null] }), isReady ? null : ((0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-0 flex flex-col items-center justify-center gap-3 text-sm text-white/75', role: 'status', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', label: labels.cameraStarting, size: 'small' }), (0, jsx_runtime_1.jsx)("span", { children: labels.cameraStarting })] })), flashKey > 0 ? ((0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 bg-white animate-ps-fade-out' }, flashKey)) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-x-0 top-0 flex items-start justify-between gap-3 bg-gradient-to-b from-black/60 to-transparent px-4 pb-10 pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: labels.close, onClick: onClose }), (0, jsx_runtime_1.jsx)("div", { className: 'flex items-center gap-2', children: canTorch && isReady ? ((0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Flash, isPressed: isTorchOn, label: isTorchOn ? labels.torchOff : labels.torchOn, onClick: onToggleTorch })) : null })] }), isReady ? ((0, jsx_runtime_1.jsx)("div", { className: 'absolute inset-x-0 top-[calc(max(1rem,env(safe-area-inset-top))+3.75rem)] flex justify-center px-4', children: (0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: ps_scan_chrome_classes_1.PsScanChromeClasses.pill, children: hint }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-x-0 bottom-0 grid grid-cols-[1fr_auto_1fr] items-center gap-4 bg-gradient-to-t from-black/80 to-transparent px-5 pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-12', children: [(0, jsx_runtime_1.jsx)("div", { className: 'flex justify-start', children: (0, jsx_runtime_1.jsx)(ps_scan_photo_button_1.PsScanPhotoButton, { isIconOnly: true, label: labels.choosePhoto, onFile: onFile }) }), (0, jsx_runtime_1.jsx)(ps_scan_shutter_1.PsScanShutter, { disabled: !isReady || isBusy, label: labels.capture, onCapture: onCapture }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' })] })] }));
|
|
24
24
|
};
|
|
25
25
|
exports.PsScanCameraView = PsScanCameraView;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PsDocumentScannerPageLabels } from '../models/ps-document-scanner-page-labels';
|
|
2
|
+
import type { PsScanPage } from '../models/ps-scan-page';
|
|
3
|
+
export interface PsScanPageStripProps {
|
|
4
|
+
labels: PsDocumentScannerPageLabels;
|
|
5
|
+
onRemove: (id: number) => void;
|
|
6
|
+
pages: PsScanPage[];
|
|
7
|
+
}
|
|
8
|
+
export declare const PsScanPageStrip: ({ labels, onRemove, pages }: PsScanPageStripProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanPageStrip = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
6
|
+
const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
|
|
7
|
+
const PsScanPageStrip = ({ labels, onRemove, pages }) => ((0, jsx_runtime_1.jsx)("ul", { className: 'ps-scrollbar-hidden flex min-w-0 items-center justify-center gap-3 overflow-x-auto px-4 py-2', children: pages.map((page, index) => ((0, jsx_runtime_1.jsxs)("li", { className: 'relative shrink-0', children: [(0, jsx_runtime_1.jsx)("img", { alt: ps_scan_template_service_1.PsScanTemplateService.fill(labels.pageLabel, { n: index + 1 }), className: 'h-16 w-auto rounded-sm bg-white object-contain ring-1 ring-white/25', src: page.url }), (0, jsx_runtime_1.jsx)("button", { "aria-label": ps_scan_template_service_1.PsScanTemplateService.fill(labels.removePage, { n: index + 1 }), className: 'ps-focus-ring ps-control-square ps-control-compact absolute -right-2 -top-2 inline-flex items-center justify-center rounded-ps-control bg-black/80 text-white transition hover:bg-black', onClick: () => onRemove(page.id), type: 'button', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Close }) })] }, page.id))) }));
|
|
8
|
+
exports.PsScanPageStrip = PsScanPageStrip;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
2
|
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsScanMultiPage } from '../models/ps-scan-multi-page';
|
|
3
4
|
import type { PsScanResult } from '../models/ps-scan-result';
|
|
4
5
|
export interface PsScanPreviewProps {
|
|
5
6
|
error: string;
|
|
6
7
|
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
8
|
+
multiPage?: PsScanMultiPage;
|
|
7
9
|
onAdjust: () => void;
|
|
8
10
|
onRetake: () => void;
|
|
9
11
|
onUse: () => void;
|
|
10
12
|
result: PsScanResult | null;
|
|
11
13
|
}
|
|
12
|
-
export declare const PsScanPreview: ({ error, labels, onAdjust, onRetake, onUse, result, }: PsScanPreviewProps) => import("react").JSX.Element;
|
|
14
|
+
export declare const PsScanPreview: ({ error, labels, multiPage, onAdjust, onRetake, onUse, result, }: PsScanPreviewProps) => import("react").JSX.Element;
|
|
@@ -4,7 +4,11 @@ exports.PsScanPreview = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
6
|
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
+
const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
|
|
7
8
|
const ps_scan_button_1 = require("./ps-scan-button");
|
|
8
9
|
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
9
|
-
const
|
|
10
|
+
const ps_scan_page_strip_1 = require("./ps-scan-page-strip");
|
|
11
|
+
const PsScanPreview = ({ error, labels, multiPage, onAdjust, onRetake, onUse, result, }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col bg-black text-white animate-ps-fade-in', children: [(0, jsx_runtime_1.jsxs)("header", { className: 'grid shrink-0 grid-cols-[2.75rem_1fr_2.75rem] items-center gap-3 px-4 pb-1 pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Crop, label: labels.cropTitle, onClick: onAdjust }), (0, jsx_runtime_1.jsx)("h2", { className: 'truncate text-center text-base font-semibold', children: labels.previewTitle }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' })] }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-6 py-4', children: result === null ? ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col items-center gap-3 text-sm text-white/75', role: 'status', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', label: labels.preparing, size: 'small' }), (0, jsx_runtime_1.jsx)("span", { children: labels.preparing })] })) : ((0, jsx_runtime_1.jsx)("img", { alt: labels.previewAlt, className: 'max-h-full max-w-full rounded-sm bg-white object-contain shadow-[0_24px_60px_-20px_rgb(0_0_0/0.9)] animate-ps-fade-in', src: result.url })) }), error === '' ? null : ((0, jsx_runtime_1.jsx)("p", { className: 'mx-auto mb-2 max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })), multiPage === undefined || multiPage.pages.length === 0 ? null : ((0, jsx_runtime_1.jsxs)("div", { className: 'shrink-0', children: [(0, jsx_runtime_1.jsx)(ps_scan_page_strip_1.PsScanPageStrip, { labels: multiPage.labels, onRemove: multiPage.onRemovePage, pages: multiPage.pages }), (0, jsx_runtime_1.jsx)("p", { className: 'text-center text-sm text-white/75', children: ps_scan_template_service_1.PsScanTemplateService.fill(multiPage.labels.pageCount, {
|
|
12
|
+
count: multiPage.pages.length + 1,
|
|
13
|
+
}) })] })), (0, jsx_runtime_1.jsxs)("footer", { className: 'mx-auto grid w-full max-w-md shrink-0 grid-cols-2 gap-3 px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-2', children: [multiPage === undefined ? null : ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: 'col-span-2', disabled: result === null, isLoading: multiPage.isBusy, isLarge: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Plus }), onClick: multiPage.onAddPage, children: multiPage.labels.addPage })), (0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { isLarge: true, onClick: onRetake, children: labels.retake }), (0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { disabled: result === null, isLarge: true, isPrimary: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Check }), onClick: onUse, children: labels.useScan })] })] }));
|
|
10
14
|
exports.PsScanPreview = PsScanPreview;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
1
2
|
import type { PsPageSize } from '../models/ps-page-size';
|
|
2
3
|
import type { PsScanQuad } from '../models/ps-scan-quad';
|
|
3
4
|
export interface PsScanViewfinderOverlayProps {
|
|
4
5
|
area: PsPageSize;
|
|
5
|
-
|
|
6
|
+
hasQuad: boolean;
|
|
7
|
+
quadRef: RefObject<PsScanQuad | null>;
|
|
6
8
|
video: PsPageSize;
|
|
7
9
|
}
|
|
8
|
-
export declare const PsScanViewfinderOverlay: ({ area,
|
|
10
|
+
export declare const PsScanViewfinderOverlay: ({ area, hasQuad, quadRef, video, }: PsScanViewfinderOverlayProps) => import("react").JSX.Element | null;
|
|
@@ -3,13 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PsScanViewfinderOverlay = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
6
|
+
const use_ps_scan_quad_tween_1 = require("../hooks/use-ps-scan-quad-tween");
|
|
6
7
|
const ps_scan_geometry_service_1 = require("../services/ps-scan-geometry-service");
|
|
7
|
-
const PsScanViewfinderOverlay = ({ area,
|
|
8
|
-
if (area.width <= 0 || area.height <= 0)
|
|
9
|
-
return null;
|
|
8
|
+
const PsScanViewfinderOverlay = ({ area, hasQuad, quadRef, video, }) => {
|
|
10
9
|
const box = ps_scan_geometry_service_1.PsScanGeometryService.cover(area, video);
|
|
11
10
|
const left = (area.width - box.width) / 2;
|
|
12
11
|
const top = (area.height - box.height) / 2;
|
|
12
|
+
const polygon = (0, use_ps_scan_quad_tween_1.usePsScanQuadTween)(quadRef, (quad) => quad.map((point) => `${left + point.x * box.width},${top + point.y * box.height}`).join(' '));
|
|
13
|
+
if (area.width <= 0 || area.height <= 0)
|
|
14
|
+
return null;
|
|
13
15
|
const frameHeight = Math.min(area.height * ps_scan_config_1.PsScanConfig.guideHeightRatio, (area.width * ps_scan_config_1.PsScanConfig.guideWidthRatio) / ps_scan_config_1.PsScanConfig.guideAspect);
|
|
14
16
|
const frameWidth = frameHeight * ps_scan_config_1.PsScanConfig.guideAspect;
|
|
15
17
|
const frameLeft = (area.width - frameWidth) / 2;
|
|
@@ -21,8 +23,6 @@ const PsScanViewfinderOverlay = ({ area, quad, video }) => {
|
|
|
21
23
|
`M${frameLeft + frameWidth} ${frameTop + frameHeight - arm}V${frameTop + frameHeight}H${frameLeft + frameWidth - arm}`,
|
|
22
24
|
`M${frameLeft + arm} ${frameTop + frameHeight}H${frameLeft}V${frameTop + frameHeight - arm}`,
|
|
23
25
|
].join('');
|
|
24
|
-
return ((0, jsx_runtime_1.jsx)("svg", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 size-full', viewBox: `0 0 ${area.width} ${area.height}`, children:
|
|
25
|
-
.map((point) => `${left + point.x * box.width},${top + point.y * box.height}`)
|
|
26
|
-
.join(' '), strokeLinejoin: 'round', strokeWidth: '3' })) }));
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)("svg", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 size-full', viewBox: `0 0 ${area.width} ${area.height}`, children: !hasQuad || box.width === 0 ? ((0, jsx_runtime_1.jsx)("path", { className: 'stroke-white/75 animate-ps-fade-in', d: corners, fill: 'none', strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: '4' })) : ((0, jsx_runtime_1.jsx)("polygon", { className: 'fill-primary/10 stroke-primary', ref: polygon, strokeLinejoin: 'round', strokeWidth: '3' })) }));
|
|
27
27
|
};
|
|
28
28
|
exports.PsScanViewfinderOverlay = PsScanViewfinderOverlay;
|
|
@@ -27,14 +27,18 @@ export declare const PsScanConfig: {
|
|
|
27
27
|
readonly guideLift: 0.03;
|
|
28
28
|
readonly guideWidthRatio: 0.76;
|
|
29
29
|
readonly liveDeadzone: 0.006;
|
|
30
|
-
readonly
|
|
30
|
+
readonly liveCloseRadii: readonly [2];
|
|
31
|
+
readonly liveDetectMinIntervalMs: 70;
|
|
31
32
|
readonly liveDetectMaxDimension: 280;
|
|
32
|
-
readonly liveMissLimit:
|
|
33
|
-
readonly liveSmoothing: 0.
|
|
33
|
+
readonly liveMissLimit: 1;
|
|
34
|
+
readonly liveSmoothing: 0.68;
|
|
35
|
+
readonly liveTweenMs: 55;
|
|
34
36
|
readonly loupeOffset: 72;
|
|
35
37
|
readonly loupeSize: 112;
|
|
36
38
|
readonly loupeZoom: 2.5;
|
|
37
39
|
readonly minCornerGap: 0.02;
|
|
40
|
+
readonly multiPageMaxDimension: 2400;
|
|
41
|
+
readonly pdfLongEdgePoints: 842;
|
|
38
42
|
readonly polygonEpsilons: readonly [0.008, 0.012, 0.018, 0.026, 0.036, 0.05, 0.07];
|
|
39
43
|
readonly torchConstraint: "torch";
|
|
40
44
|
readonly warpChunkPixels: 400000;
|
|
@@ -30,14 +30,18 @@ exports.PsScanConfig = {
|
|
|
30
30
|
guideLift: 0.03,
|
|
31
31
|
guideWidthRatio: 0.76,
|
|
32
32
|
liveDeadzone: 0.006,
|
|
33
|
-
|
|
33
|
+
liveCloseRadii: [2],
|
|
34
|
+
liveDetectMinIntervalMs: 70,
|
|
34
35
|
liveDetectMaxDimension: 280,
|
|
35
|
-
liveMissLimit:
|
|
36
|
-
liveSmoothing: 0.
|
|
36
|
+
liveMissLimit: 1,
|
|
37
|
+
liveSmoothing: 0.68,
|
|
38
|
+
liveTweenMs: 55,
|
|
37
39
|
loupeOffset: 72,
|
|
38
40
|
loupeSize: 112,
|
|
39
41
|
loupeZoom: 2.5,
|
|
40
42
|
minCornerGap: 0.02,
|
|
43
|
+
multiPageMaxDimension: 2400,
|
|
44
|
+
pdfLongEdgePoints: 842,
|
|
41
45
|
polygonEpsilons: [0.008, 0.012, 0.018, 0.026, 0.036, 0.05, 0.07],
|
|
42
46
|
torchConstraint: 'torch',
|
|
43
47
|
warpChunkPixels: 400000,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanDetectProfiles = void 0;
|
|
4
|
+
const ps_scan_config_1 = require("./ps-scan-config");
|
|
5
|
+
exports.PsScanDetectProfiles = {
|
|
6
|
+
full: { closeRadii: ps_scan_config_1.PsScanConfig.closeRadii, isYielding: true },
|
|
7
|
+
live: { closeRadii: ps_scan_config_1.PsScanConfig.liveCloseRadii, isYielding: false },
|
|
8
|
+
};
|