@pallasoft/ps-file-components 1.4.0 → 2.0.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 +64 -86
- package/dist/components/ps-document-scanner.d.ts +1 -1
- package/dist/components/ps-document-scanner.js +2 -3
- package/dist/components/ps-file-picker-actions.d.ts +10 -0
- package/dist/components/ps-file-picker-actions.js +21 -0
- package/dist/components/ps-file-picker.d.ts +7 -1
- package/dist/components/ps-file-picker.js +30 -6
- package/dist/components/ps-scan-button.d.ts +13 -0
- package/dist/components/ps-scan-button.js +9 -0
- package/dist/components/ps-scan-camera-view.d.ts +11 -8
- package/dist/components/ps-scan-camera-view.js +14 -16
- package/dist/components/ps-scan-crop-editor.d.ts +4 -4
- package/dist/components/ps-scan-crop-editor.js +24 -8
- package/dist/components/ps-scan-denied-steps.d.ts +6 -0
- package/dist/components/ps-scan-denied-steps.js +33 -0
- package/dist/components/ps-scan-icon-button.d.ts +4 -3
- package/dist/components/ps-scan-icon-button.js +3 -1
- package/dist/components/ps-scan-illustration.d.ts +1 -0
- package/dist/components/ps-scan-illustration.js +6 -0
- package/dist/components/ps-scan-loupe.d.ts +10 -0
- package/dist/components/ps-scan-loupe.js +31 -0
- package/dist/components/ps-scan-permission-view.d.ts +10 -0
- package/dist/components/ps-scan-permission-view.js +48 -0
- package/dist/components/ps-scan-photo-button.d.ts +7 -0
- package/dist/components/ps-scan-photo-button.js +20 -0
- package/dist/components/ps-scan-preview.d.ts +12 -0
- package/dist/components/ps-scan-preview.js +10 -0
- package/dist/components/ps-scan-shutter.d.ts +6 -0
- package/dist/components/ps-scan-shutter.js +6 -0
- package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
- package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
- package/dist/components/ps-upload-row.js +1 -1
- package/dist/constants/ps-document-scanner-labels.d.ts +39 -38
- package/dist/constants/ps-document-scanner-labels.js +39 -38
- package/dist/constants/ps-file-picker-labels.d.ts +5 -3
- package/dist/constants/ps-file-picker-labels.js +5 -3
- package/dist/constants/ps-scan-camera-status.d.ts +4 -0
- package/dist/constants/ps-scan-camera-status.js +4 -0
- package/dist/constants/ps-scan-chrome-classes.d.ts +10 -0
- package/dist/constants/ps-scan-chrome-classes.js +13 -0
- package/dist/constants/ps-scan-config.d.ts +34 -24
- package/dist/constants/ps-scan-config.js +34 -24
- package/dist/constants/ps-scan-platform.d.ts +6 -0
- package/dist/constants/ps-scan-platform.js +8 -0
- package/dist/constants/ps-scan-step.d.ts +1 -1
- package/dist/constants/ps-scan-step.js +1 -1
- package/dist/esm/components/ps-document-scanner-sheet.js +67 -89
- package/dist/esm/components/ps-document-scanner.js +2 -3
- package/dist/esm/components/ps-file-picker-actions.js +17 -0
- package/dist/esm/components/ps-file-picker.js +31 -7
- package/dist/esm/components/ps-scan-button.js +5 -0
- package/dist/esm/components/ps-scan-camera-view.js +17 -19
- package/dist/esm/components/ps-scan-crop-editor.js +26 -10
- package/dist/esm/components/ps-scan-denied-steps.js +29 -0
- package/dist/esm/components/ps-scan-icon-button.js +3 -1
- package/dist/esm/components/ps-scan-illustration.js +2 -0
- package/dist/esm/components/ps-scan-loupe.js +27 -0
- package/dist/esm/components/ps-scan-permission-view.js +44 -0
- package/dist/esm/components/ps-scan-photo-button.js +16 -0
- package/dist/esm/components/ps-scan-preview.js +6 -0
- package/dist/esm/components/ps-scan-shutter.js +2 -0
- package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
- package/dist/esm/components/ps-upload-row.js +1 -1
- package/dist/esm/constants/ps-document-scanner-labels.js +39 -38
- package/dist/esm/constants/ps-file-picker-labels.js +5 -3
- package/dist/esm/constants/ps-scan-camera-status.js +4 -0
- package/dist/esm/constants/ps-scan-chrome-classes.js +10 -0
- package/dist/esm/constants/ps-scan-config.js +34 -24
- package/dist/esm/constants/ps-scan-platform.js +5 -0
- package/dist/esm/constants/ps-scan-step.js +1 -1
- package/dist/esm/hooks/use-ps-scan-camera.js +52 -15
- package/dist/esm/hooks/use-ps-scan-live-detection.js +41 -0
- package/dist/esm/hooks/use-ps-scan-photo.js +42 -0
- package/dist/esm/index.js +1 -2
- package/dist/esm/models/ps-scan-region.js +1 -0
- package/dist/esm/models/ps-scan-result.js +1 -0
- package/dist/esm/models/ps-scan-stability.js +1 -0
- package/dist/esm/services/ps-scan-camera-service.js +52 -7
- package/dist/esm/services/ps-scan-canny-service.js +93 -0
- package/dist/esm/services/ps-scan-canvas-service.js +13 -1
- package/dist/esm/services/ps-scan-edge-service.js +73 -137
- package/dist/esm/services/ps-scan-file-name-service.js +2 -11
- package/dist/esm/services/ps-scan-geometry-service.js +56 -0
- package/dist/esm/services/ps-scan-hull-service.js +0 -39
- package/dist/esm/services/ps-scan-image-filter-service.js +0 -21
- package/dist/esm/services/ps-scan-morphology-service.js +44 -0
- package/dist/esm/services/ps-scan-pipeline-service.js +9 -19
- package/dist/esm/services/ps-scan-platform-service.js +15 -0
- package/dist/esm/services/ps-scan-polygon-service.js +75 -0
- package/dist/esm/services/ps-scan-region-service.js +52 -0
- package/dist/esm/services/ps-scan-stability-service.js +28 -0
- package/dist/esm/services/ps-scan-warp-service.js +8 -2
- package/dist/esm/services/ps-scan-yield-service.js +5 -0
- package/dist/hooks/use-ps-scan-camera.d.ts +3 -1
- package/dist/hooks/use-ps-scan-camera.js +52 -15
- package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
- package/dist/hooks/use-ps-scan-live-detection.js +45 -0
- package/dist/hooks/use-ps-scan-photo.d.ts +9 -0
- package/dist/hooks/use-ps-scan-photo.js +46 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/models/ps-document-scanner-props.d.ts +0 -3
- package/dist/models/ps-document-scanner-sheet-props.d.ts +0 -3
- package/dist/models/ps-file-picker-scan.d.ts +4 -0
- package/dist/models/ps-scan-gradient.d.ts +4 -0
- package/dist/models/ps-scan-live-detection.d.ts +4 -0
- package/dist/models/ps-scan-located.d.ts +5 -0
- package/dist/models/ps-scan-photo.d.ts +9 -0
- package/dist/models/ps-scan-region.d.ts +5 -0
- package/dist/models/ps-scan-region.js +2 -0
- package/dist/models/ps-scan-result.d.ts +6 -0
- package/dist/models/ps-scan-result.js +2 -0
- package/dist/models/ps-scan-stability.d.ts +5 -0
- package/dist/models/ps-scan-stability.js +2 -0
- package/dist/services/ps-scan-camera-service.d.ts +4 -0
- package/dist/services/ps-scan-camera-service.js +52 -7
- package/dist/services/ps-scan-canny-service.d.ts +7 -0
- package/dist/services/ps-scan-canny-service.js +97 -0
- package/dist/services/ps-scan-canvas-service.d.ts +1 -0
- package/dist/services/ps-scan-canvas-service.js +13 -1
- package/dist/services/ps-scan-edge-service.d.ts +5 -5
- package/dist/services/ps-scan-edge-service.js +73 -137
- package/dist/services/ps-scan-file-name-service.d.ts +1 -3
- package/dist/services/ps-scan-file-name-service.js +2 -11
- package/dist/services/ps-scan-geometry-service.d.ts +4 -0
- package/dist/services/ps-scan-geometry-service.js +56 -0
- package/dist/services/ps-scan-hull-service.d.ts +0 -2
- package/dist/services/ps-scan-hull-service.js +0 -39
- package/dist/services/ps-scan-image-filter-service.d.ts +0 -1
- package/dist/services/ps-scan-image-filter-service.js +0 -21
- package/dist/services/ps-scan-morphology-service.d.ts +7 -0
- package/dist/services/ps-scan-morphology-service.js +48 -0
- package/dist/services/ps-scan-pipeline-service.d.ts +2 -5
- package/dist/services/ps-scan-pipeline-service.js +9 -19
- package/dist/services/ps-scan-platform-service.d.ts +4 -0
- package/dist/services/ps-scan-platform-service.js +19 -0
- package/dist/services/ps-scan-polygon-service.d.ts +8 -0
- package/dist/services/ps-scan-polygon-service.js +79 -0
- package/dist/services/ps-scan-region-service.d.ts +4 -0
- package/dist/services/ps-scan-region-service.js +56 -0
- package/dist/services/ps-scan-stability-service.d.ts +7 -0
- package/dist/services/ps-scan-stability-service.js +32 -0
- package/dist/services/ps-scan-warp-service.d.ts +1 -1
- package/dist/services/ps-scan-warp-service.js +8 -2
- package/dist/services/ps-scan-yield-service.d.ts +3 -0
- package/dist/services/ps-scan-yield-service.js +9 -0
- package/package.json +3 -3
- package/dist/components/ps-scan-page-list.d.ts +0 -12
- package/dist/components/ps-scan-page-list.js +0 -23
- package/dist/components/ps-scan-review.d.ts +0 -23
- package/dist/components/ps-scan-review.js +0 -20
- package/dist/constants/ps-scan-filter.d.ts +0 -7
- package/dist/constants/ps-scan-filter.js +0 -9
- package/dist/constants/ps-scan-output-format.d.ts +0 -5
- package/dist/constants/ps-scan-output-format.js +0 -7
- package/dist/esm/components/ps-scan-page-list.js +0 -19
- package/dist/esm/components/ps-scan-review.js +0 -16
- package/dist/esm/constants/ps-scan-filter.js +0 -6
- package/dist/esm/constants/ps-scan-output-format.js +0 -4
- package/dist/esm/hooks/use-ps-scan-pages.js +0 -62
- package/dist/esm/hooks/use-ps-scan-previews.js +0 -57
- package/dist/esm/services/ps-scan-enhance-service.js +0 -97
- package/dist/esm/services/ps-scan-order-service.js +0 -13
- package/dist/esm/services/ps-scan-output-service.js +0 -24
- package/dist/esm/services/ps-scan-pdf-service.js +0 -56
- package/dist/hooks/use-ps-scan-pages.d.ts +0 -12
- package/dist/hooks/use-ps-scan-pages.js +0 -66
- package/dist/hooks/use-ps-scan-previews.d.ts +0 -2
- package/dist/hooks/use-ps-scan-previews.js +0 -61
- package/dist/models/ps-scan-added-page.d.ts +0 -5
- package/dist/models/ps-scan-levels.d.ts +0 -4
- package/dist/models/ps-scan-output-options.d.ts +0 -7
- package/dist/models/ps-scan-page.d.ts +0 -12
- package/dist/models/ps-scan-pdf-page.d.ts +0 -5
- package/dist/services/ps-scan-enhance-service.d.ts +0 -9
- package/dist/services/ps-scan-enhance-service.js +0 -101
- package/dist/services/ps-scan-order-service.d.ts +0 -4
- package/dist/services/ps-scan-order-service.js +0 -17
- package/dist/services/ps-scan-output-service.d.ts +0 -5
- package/dist/services/ps-scan-output-service.js +0 -28
- package/dist/services/ps-scan-pdf-service.d.ts +0 -5
- package/dist/services/ps-scan-pdf-service.js +0 -60
- /package/dist/esm/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
- /package/dist/esm/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
- /package/dist/esm/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
- /package/dist/esm/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
- /package/dist/esm/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
- /package/dist/models/{ps-scan-added-page.js → ps-file-picker-scan.js} +0 -0
- /package/dist/models/{ps-scan-levels.js → ps-scan-gradient.js} +0 -0
- /package/dist/models/{ps-scan-output-options.js → ps-scan-live-detection.js} +0 -0
- /package/dist/models/{ps-scan-page.js → ps-scan-located.js} +0 -0
- /package/dist/models/{ps-scan-pdf-page.js → ps-scan-photo.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PsDocumentScannerLoader: import("react").LazyExoticComponent<({ fileName, jpegQuality, labels, maxDimension,
|
|
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,2 +1,2 @@
|
|
|
1
1
|
import type { PsDocumentScannerSheetProps } from '../models/ps-document-scanner-sheet-props';
|
|
2
|
-
export declare const PsDocumentScannerSheet: ({ fileName, jpegQuality, labels, maxDimension,
|
|
2
|
+
export declare const PsDocumentScannerSheet: ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
|
|
@@ -6,133 +6,111 @@ const react_1 = require("react");
|
|
|
6
6
|
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
7
7
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
8
8
|
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
9
|
-
const ps_popovers_1 = require("@pallasoft/ps-popovers");
|
|
10
9
|
const react_dom_1 = require("react-dom");
|
|
10
|
+
const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
|
|
11
11
|
const ps_scan_step_1 = require("../constants/ps-scan-step");
|
|
12
12
|
const use_ps_focus_trap_1 = require("../hooks/use-ps-focus-trap");
|
|
13
13
|
const use_ps_scan_camera_1 = require("../hooks/use-ps-scan-camera");
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
14
|
+
const use_ps_scan_live_detection_1 = require("../hooks/use-ps-scan-live-detection");
|
|
15
|
+
const use_ps_scan_photo_1 = require("../hooks/use-ps-scan-photo");
|
|
16
|
+
const ps_scan_file_name_service_1 = require("../services/ps-scan-file-name-service");
|
|
17
17
|
const ps_scan_pipeline_service_1 = require("../services/ps-scan-pipeline-service");
|
|
18
|
-
const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
|
|
19
18
|
const ps_scan_camera_view_1 = require("./ps-scan-camera-view");
|
|
20
19
|
const ps_scan_crop_editor_1 = require("./ps-scan-crop-editor");
|
|
21
20
|
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
22
|
-
const
|
|
23
|
-
const
|
|
21
|
+
const ps_scan_permission_view_1 = require("./ps-scan-permission-view");
|
|
22
|
+
const ps_scan_preview_1 = require("./ps-scan-preview");
|
|
23
|
+
const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }) => {
|
|
24
24
|
const tier = (0, ps_core_components_1.usePsViewport)();
|
|
25
25
|
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
|
|
26
26
|
const content = (0, react_1.useRef)(null);
|
|
27
|
-
const {
|
|
27
|
+
const { clear, detectAgain, load, photo, setCorners } = (0, use_ps_scan_photo_1.usePsScanPhoto)();
|
|
28
28
|
const [step, setStep] = (0, react_1.useState)(ps_scan_step_1.PsScanStep.Camera);
|
|
29
|
-
const [
|
|
30
|
-
const [retakeId, setRetakeId] = (0, react_1.useState)('');
|
|
29
|
+
const [result, setResult] = (0, react_1.useState)(null);
|
|
31
30
|
const [announcement, setAnnouncement] = (0, react_1.useState)('');
|
|
32
31
|
const [error, setError] = (0, react_1.useState)('');
|
|
33
|
-
const [
|
|
32
|
+
const [isCapturing, setIsCapturing] = (0, react_1.useState)(false);
|
|
33
|
+
const [flashKey, setFlashKey] = (0, react_1.useState)(0);
|
|
34
34
|
const camera = (0, use_ps_scan_camera_1.usePsScanCamera)(step === ps_scan_step_1.PsScanStep.Camera);
|
|
35
|
-
const
|
|
36
|
-
const page = pages.find((item) => item.id === activeId);
|
|
35
|
+
const isReady = camera.status === ps_scan_camera_status_1.PsScanCameraStatus.Ready;
|
|
37
36
|
const isDesktop = tier === ps_design_system_1.PallasoftViewportTier.Desktop;
|
|
38
|
-
|
|
37
|
+
const isCameraStep = step === ps_scan_step_1.PsScanStep.Camera;
|
|
38
|
+
const showsCamera = isReady || camera.status === ps_scan_camera_status_1.PsScanCameraStatus.Starting;
|
|
39
|
+
const detection = (0, use_ps_scan_live_detection_1.usePsScanLiveDetection)(camera.videoRef, isCameraStep && isReady);
|
|
40
|
+
(0, ps_core_components_1.usePsScrollLock)(true);
|
|
39
41
|
(0, ps_core_components_1.usePsRestoreFocus)(true);
|
|
40
42
|
(0, use_ps_focus_trap_1.usePsFocusTrap)(content, true, onClose);
|
|
43
|
+
(0, react_1.useEffect)(() => () => {
|
|
44
|
+
if (result !== null)
|
|
45
|
+
URL.revokeObjectURL(result.url);
|
|
46
|
+
}, [result]);
|
|
41
47
|
const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
|
|
42
|
-
const
|
|
48
|
+
const accept = async (source) => {
|
|
43
49
|
setError('');
|
|
44
50
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
setActiveId(retakeId);
|
|
49
|
-
setRetakeId('');
|
|
50
|
-
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
51
|
-
setAnnouncement(edgeMessage(added.isDetected));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const room = Math.max(0, maxPages - pages.length);
|
|
55
|
-
if (images.length > room)
|
|
56
|
-
setError(labels.maxPages);
|
|
57
|
-
const added = await add(images.slice(0, room));
|
|
58
|
-
if (added.length === 0)
|
|
59
|
-
return;
|
|
60
|
-
setActiveId(added[0].page.id);
|
|
61
|
-
setStep(added.length === 1 ? ps_scan_step_1.PsScanStep.Crop : ps_scan_step_1.PsScanStep.Review);
|
|
62
|
-
setAnnouncement(added.length === 1
|
|
63
|
-
? edgeMessage(added[0].isDetected)
|
|
64
|
-
: ps_scan_template_service_1.PsScanTemplateService.fill(labels.pageAdded, { position: pages.length + added.length }));
|
|
51
|
+
const added = await load(source);
|
|
52
|
+
setAnnouncement(edgeMessage(added.isDetected));
|
|
53
|
+
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
65
54
|
}
|
|
66
55
|
catch {
|
|
67
56
|
setError(labels.processFailed);
|
|
68
57
|
}
|
|
69
58
|
};
|
|
70
59
|
const capture = async () => {
|
|
71
|
-
|
|
72
|
-
if (blob !== null)
|
|
73
|
-
await addFiles([blob]);
|
|
74
|
-
};
|
|
75
|
-
const autoDetect = async () => {
|
|
76
|
-
if (page === undefined)
|
|
60
|
+
if (isCapturing)
|
|
77
61
|
return;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
setIsCapturing(true);
|
|
63
|
+
try {
|
|
64
|
+
const blob = await camera.capture();
|
|
65
|
+
if (blob === null)
|
|
66
|
+
return;
|
|
67
|
+
setFlashKey((current) => current + 1);
|
|
68
|
+
await accept(blob);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
setIsCapturing(false);
|
|
72
|
+
}
|
|
81
73
|
};
|
|
74
|
+
const autoDetect = async () => setAnnouncement(edgeMessage(await detectAgain()));
|
|
82
75
|
const retake = () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
clear();
|
|
77
|
+
setResult(null);
|
|
78
|
+
setError('');
|
|
86
79
|
setStep(ps_scan_step_1.PsScanStep.Camera);
|
|
87
80
|
};
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const rest = pages.filter((item) => item.id !== page.id);
|
|
93
|
-
remove(page.id);
|
|
94
|
-
setAnnouncement(labels.pageDeleted);
|
|
95
|
-
if (rest.length === 0) {
|
|
96
|
-
setActiveId('');
|
|
97
|
-
setStep(ps_scan_step_1.PsScanStep.Camera);
|
|
98
|
-
}
|
|
99
|
-
else
|
|
100
|
-
setActiveId(rest[Math.min(index, rest.length - 1)].id);
|
|
81
|
+
const adjust = () => {
|
|
82
|
+
setResult(null);
|
|
83
|
+
setError('');
|
|
84
|
+
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
101
85
|
};
|
|
102
|
-
const
|
|
103
|
-
|
|
86
|
+
const flatten = async () => {
|
|
87
|
+
if (photo === null)
|
|
88
|
+
return;
|
|
89
|
+
setResult(null);
|
|
104
90
|
setError('');
|
|
91
|
+
setStep(ps_scan_step_1.PsScanStep.Preview);
|
|
105
92
|
try {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
format: outputFormat,
|
|
109
|
-
jpegQuality,
|
|
110
|
-
maxDimension,
|
|
111
|
-
});
|
|
112
|
-
onComplete(files);
|
|
113
|
-
onClose();
|
|
93
|
+
const rendered = await ps_scan_pipeline_service_1.PsScanPipelineService.render(photo.source, photo.corners, maxDimension, jpegQuality);
|
|
94
|
+
setResult({ ...rendered, url: URL.createObjectURL(rendered.blob) });
|
|
114
95
|
}
|
|
115
96
|
catch {
|
|
116
97
|
setError(labels.processFailed);
|
|
117
|
-
setIsBusy(false);
|
|
118
98
|
}
|
|
119
99
|
};
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
const body = ((0, jsx_runtime_1.jsxs)("div", { className: 'relative h-full min-h-0 bg-surface text-ink', ref: content, children: [(0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), step === ps_scan_step_1.PsScanStep.Camera ? ((0, jsx_runtime_1.jsx)(ps_scan_camera_view_1.PsScanCameraView, { canGoBack: pages.length > 0, canTorch: camera.canTorch, isTorchOn: camera.isTorchOn, labels: labels, onBack: () => {
|
|
129
|
-
setRetakeId('');
|
|
130
|
-
setStep(ps_scan_step_1.PsScanStep.Review);
|
|
131
|
-
}, onCapture: capture, onFiles: addFiles, onToggleTorch: camera.toggleTorch, status: camera.status, videoRef: camera.videoRef })) : null, step === ps_scan_step_1.PsScanStep.Crop && page !== undefined ? ((0, jsx_runtime_1.jsx)(ps_scan_crop_editor_1.PsScanCropEditor, { labels: labels, onApply: () => setStep(ps_scan_step_1.PsScanStep.Review), onAutoDetect: autoDetect, onChange: (corners) => setCorners(page.id, corners), onRetake: retake, page: page })) : null, step === ps_scan_step_1.PsScanStep.Review && page !== undefined ? ((0, jsx_runtime_1.jsx)(ps_scan_review_1.PsScanReview, { canAddPage: pages.length < maxPages, error: error, isBusy: isBusy, labels: labels, onAddPage: () => setStep(ps_scan_step_1.PsScanStep.Camera), onDelete: deletePage, onEditCrop: () => setStep(ps_scan_step_1.PsScanStep.Crop), onFilter: (filter) => update(page.id, { filter }), onFinish: finish, onMove: moveTo, onRetake: retake, onRotate: () => update(page.id, { rotation: (page.rotation + 90) % 360 }), onSelect: setActiveId, page: page, pages: pages, previews: previews })) : null, step === ps_scan_step_1.PsScanStep.Camera && error !== '' ? ((0, jsx_runtime_1.jsx)("p", { className: 'absolute inset-x-4 top-3 rounded-ps-control bg-danger px-3 py-2 text-sm text-white', role: 'alert', children: error })) : null] }));
|
|
132
|
-
if (isDesktop)
|
|
133
|
-
return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { isOpen: true, onClose: onClose, size: 'xl', title: labels.title, children: (0, jsx_runtime_1.jsx)("div", { className: '-m-4 h-[min(70dvh,38rem)] overflow-hidden sm:-m-5', children: body }) }));
|
|
100
|
+
const use = () => {
|
|
101
|
+
if (result === null)
|
|
102
|
+
return;
|
|
103
|
+
onComplete([
|
|
104
|
+
new File([result.blob], ps_scan_file_name_service_1.PsScanFileNameService.build(fileName), { type: 'image/jpeg' }),
|
|
105
|
+
]);
|
|
106
|
+
onClose();
|
|
107
|
+
};
|
|
134
108
|
if (typeof document === 'undefined')
|
|
135
109
|
return null;
|
|
136
|
-
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": labels.title, "aria-modal": 'true', className:
|
|
110
|
+
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
|
|
111
|
+
? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
|
|
112
|
+
: '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
|
+
? '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);
|
|
137
115
|
};
|
|
138
116
|
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,
|
|
2
|
+
export declare const PsDocumentScanner: ({ fileName, isOpen, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
|
|
@@ -6,12 +6,11 @@ const react_1 = require("react");
|
|
|
6
6
|
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
|
-
const ps_scan_output_format_1 = require("../constants/ps-scan-output-format");
|
|
10
9
|
const ps_document_scanner_loader_1 = require("./ps-document-scanner-loader");
|
|
11
|
-
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,
|
|
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, }) => {
|
|
12
11
|
const isHydrated = (0, ps_core_components_1.usePsHydrated)();
|
|
13
12
|
if (!isOpen || !isHydrated)
|
|
14
13
|
return null;
|
|
15
|
-
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,
|
|
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 }) }));
|
|
16
15
|
};
|
|
17
16
|
exports.PsDocumentScanner = PsDocumentScanner;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface PsFilePickerActionsProps {
|
|
2
|
+
chooseLabel: string;
|
|
3
|
+
className?: string;
|
|
4
|
+
disabled: boolean;
|
|
5
|
+
isTouch: boolean;
|
|
6
|
+
onChoose: () => void;
|
|
7
|
+
onScan: () => void;
|
|
8
|
+
scanLabel?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const PsFilePickerActions: ({ chooseLabel, className, disabled, isTouch, onChoose, onScan, scanLabel, }: PsFilePickerActionsProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFilePickerActions = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
8
|
+
const PsFilePickerActions = ({ chooseLabel, className, disabled, isTouch, onChoose, onScan, scanLabel, }) => {
|
|
9
|
+
const hasScan = scanLabel !== undefined;
|
|
10
|
+
const choose = ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Upload }), onClick: (event) => {
|
|
11
|
+
event.stopPropagation();
|
|
12
|
+
onChoose();
|
|
13
|
+
}, size: isTouch ? 'large' : 'default', variant: hasScan && isTouch ? 'surface' : hasScan || isTouch ? 'primary' : 'surface', children: chooseLabel }));
|
|
14
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: ps_design_system_1.PallasoftClassNameService.merge(isTouch
|
|
15
|
+
? 'w-full max-w-xs flex-col gap-2'
|
|
16
|
+
: 'flex-row flex-wrap items-center justify-center gap-2', className), children: hasScan ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isTouch ? null : choose, (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Camera }), onClick: (event) => {
|
|
17
|
+
event.stopPropagation();
|
|
18
|
+
onScan();
|
|
19
|
+
}, size: isTouch ? 'large' : 'default', variant: isTouch ? 'primary' : 'surface', children: scanLabel }), isTouch ? choose : null] })) : (choose) }));
|
|
20
|
+
};
|
|
21
|
+
exports.PsFilePickerActions = PsFilePickerActions;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { PsFilePickerLabels } from '../constants/ps-file-picker-labels';
|
|
2
|
+
import { PsUploadLabels } from '../constants/ps-upload-labels';
|
|
3
|
+
import type { PsFilePickerScan } from '../models/ps-file-picker-scan';
|
|
2
4
|
import type { PsLabelSet } from '../models/ps-label-set';
|
|
5
|
+
import type { PsUploadQueueState } from '../models/ps-upload-queue-state';
|
|
3
6
|
export interface PsFilePickerProps {
|
|
4
7
|
accept: string[];
|
|
5
8
|
acceptLabel: string;
|
|
@@ -11,9 +14,12 @@ export interface PsFilePickerProps {
|
|
|
11
14
|
maxSizeBytes?: number;
|
|
12
15
|
maxSizeLabel?: string;
|
|
13
16
|
multiple?: boolean;
|
|
17
|
+
queue?: PsUploadQueueState;
|
|
18
|
+
scan?: PsFilePickerScan;
|
|
14
19
|
touchLabel?: string;
|
|
20
|
+
uploadLabels?: Partial<PsLabelSet<typeof PsUploadLabels>>;
|
|
15
21
|
onError?: (message: string) => void;
|
|
16
22
|
onFileLoad?: (file: File) => void;
|
|
17
23
|
onFilesSelect?: (files: File[]) => void;
|
|
18
24
|
}
|
|
19
|
-
export declare const PsFilePicker: ({ accept, acceptLabel, className, disabled, dropLabel, isLoading, labels, maxSizeBytes, maxSizeLabel, multiple, onError, onFileLoad, onFilesSelect, touchLabel, }: PsFilePickerProps) => import("react").JSX.Element;
|
|
25
|
+
export declare const PsFilePicker: ({ accept, acceptLabel, className, disabled, dropLabel, isLoading, labels, maxSizeBytes, maxSizeLabel, multiple, onError, onFileLoad, onFilesSelect, queue, scan, touchLabel, uploadLabels, }: PsFilePickerProps) => import("react").JSX.Element;
|
|
@@ -6,13 +6,24 @@ const react_1 = require("react");
|
|
|
6
6
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
7
|
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
8
8
|
const ps_file_picker_labels_1 = require("../constants/ps-file-picker-labels");
|
|
9
|
+
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
10
|
+
const ps_upload_labels_1 = require("../constants/ps-upload-labels");
|
|
9
11
|
const ps_file_selection_service_1 = require("../services/ps-file-selection-service");
|
|
10
|
-
const
|
|
12
|
+
const ps_document_scanner_1 = require("./ps-document-scanner");
|
|
13
|
+
const ps_file_picker_actions_1 = require("./ps-file-picker-actions");
|
|
14
|
+
const ps_upload_row_1 = require("./ps-upload-row");
|
|
15
|
+
const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLabel, isLoading = false, labels, maxSizeBytes, maxSizeLabel, multiple = false, onError, onFileLoad, onFilesSelect, queue, scan, touchLabel, uploadLabels, }) => {
|
|
11
16
|
const [isDragging, setIsDragging] = (0, react_1.useState)(false);
|
|
17
|
+
const [scanName, setScanName] = (0, react_1.useState)(null);
|
|
18
|
+
const input = (0, react_1.useRef)(null);
|
|
12
19
|
const acceptAttr = (0, react_1.useMemo)(() => accept.join(','), [accept]);
|
|
13
20
|
const text = { ...ps_file_picker_labels_1.PsFilePickerLabels, ...labels };
|
|
21
|
+
const rowLabels = { ...ps_upload_labels_1.PsUploadLabels, ...uploadLabels };
|
|
22
|
+
const details = [acceptLabel, maxSizeLabel].filter((value) => value !== undefined && value !== '');
|
|
23
|
+
const chooseLabel = multiple ? text.choose : text.chooseOne;
|
|
24
|
+
const scanLabel = scan === undefined ? undefined : text.scan;
|
|
14
25
|
if (isLoading)
|
|
15
|
-
return ((0, jsx_runtime_1.jsx)("span", { "aria-busy": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('ps-skeleton block min-h-
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)("span", { "aria-busy": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('ps-skeleton block min-h-48 w-full rounded-ps-control', className), role: 'status' }));
|
|
16
27
|
const select = (list) => {
|
|
17
28
|
if (list === null || disabled)
|
|
18
29
|
return;
|
|
@@ -31,6 +42,15 @@ const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLa
|
|
|
31
42
|
else
|
|
32
43
|
onFileLoad?.(accepted[0]);
|
|
33
44
|
};
|
|
45
|
+
const browse = () => {
|
|
46
|
+
if (!disabled)
|
|
47
|
+
input.current?.click();
|
|
48
|
+
};
|
|
49
|
+
const openScan = () => {
|
|
50
|
+
if (disabled || scan === undefined)
|
|
51
|
+
return;
|
|
52
|
+
setScanName(scan.getFileName === undefined ? ps_scan_config_1.PsScanConfig.defaultFileName : scan.getFileName());
|
|
53
|
+
};
|
|
34
54
|
const onChange = (event) => {
|
|
35
55
|
select(event.target.files);
|
|
36
56
|
event.target.value = '';
|
|
@@ -40,9 +60,13 @@ const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLa
|
|
|
40
60
|
if (!disabled)
|
|
41
61
|
setIsDragging(isOver);
|
|
42
62
|
};
|
|
43
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('flex w-full flex-col gap-3', className), children: [(0, jsx_runtime_1.jsxs)("div", { "aria-disabled": disabled || undefined, className: ps_design_system_1.PallasoftClassNameService.merge('flex w-full cursor-pointer flex-col items-center gap-5 rounded-ps-control border-2 border-dashed px-5 py-8 text-center transition-colors duration-200 sm:px-8', isDragging
|
|
64
|
+
? 'border-primary bg-primary/5'
|
|
65
|
+
: 'border-line bg-surface/40 hover:border-primary/40 hover:bg-primary/[0.03]', disabled ? 'pointer-events-none opacity-60' : ''), onClick: browse, onDragEnter: (event) => onDrag(event, true), onDragLeave: (event) => onDrag(event, false), onDragOver: (event) => onDrag(event, true), onDrop: (event) => {
|
|
66
|
+
onDrag(event, false);
|
|
67
|
+
select(event.dataTransfer.files);
|
|
68
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: ps_design_system_1.PallasoftClassNameService.merge('flex size-12 items-center justify-center rounded-full transition-colors duration-200', isDragging
|
|
69
|
+
? 'bg-primary text-[color:var(--ps-color-primary-contrast)]'
|
|
70
|
+
: 'bg-primary-soft text-primary'), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { className: 'size-6', iconKey: ps_icons_1.PsIconKey.Upload }) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-1', children: [(0, jsx_runtime_1.jsx)("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : ((0, jsx_runtime_1.jsx)("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), (0, jsx_runtime_1.jsx)(ps_file_picker_actions_1.PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? (0, jsx_runtime_1.jsx)("p", { className: 'text-xs text-ink/50', children: details.join(' · ') }) : null, (0, jsx_runtime_1.jsx)("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : ((0, jsx_runtime_1.jsx)("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : ((0, jsx_runtime_1.jsx)(ps_document_scanner_1.PsDocumentScanner, { ...scan, fileName: scanName === null ? ps_scan_config_1.PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
|
|
47
71
|
};
|
|
48
72
|
exports.PsFilePicker = PsFilePicker;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface PsScanButtonProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
isLarge?: boolean;
|
|
7
|
+
isLoading?: boolean;
|
|
8
|
+
isPrimary?: boolean;
|
|
9
|
+
leadingIcon?: ReactNode;
|
|
10
|
+
loadingLabel?: string;
|
|
11
|
+
onClick: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const PsScanButton: ({ children, className, disabled, isLarge, isLoading, isPrimary, leadingIcon, loadingLabel, onClick, }: PsScanButtonProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanButton = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
6
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
|
+
const ps_scan_chrome_classes_1 = require("../constants/ps-scan-chrome-classes");
|
|
8
|
+
const PsScanButton = ({ children, className, disabled = false, isLarge = false, isLoading = false, isPrimary = false, leadingIcon, loadingLabel, onClick, }) => ((0, jsx_runtime_1.jsxs)("button", { "aria-busy": isLoading || undefined, className: ps_design_system_1.PallasoftClassNameService.merge(ps_scan_chrome_classes_1.PsScanChromeClasses.button, isLarge ? ps_scan_chrome_classes_1.PsScanChromeClasses.buttonLarge : '', isPrimary ? ps_scan_chrome_classes_1.PsScanChromeClasses.buttonPrimary : ps_scan_chrome_classes_1.PsScanChromeClasses.buttonQuiet, 'relative', className), disabled: disabled || isLoading, onClick: onClick, type: 'button', children: [isLoading ? ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { className: 'absolute inset-0 m-auto', color: 'current', label: loadingLabel === undefined ? '' : loadingLabel, size: 'small' })) : null, (0, jsx_runtime_1.jsxs)("span", { "aria-hidden": isLoading || undefined, className: `inline-flex min-w-0 items-center gap-2 ${isLoading ? 'invisible' : ''}`, children: [leadingIcon === undefined ? null : (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: leadingIcon }), (0, jsx_runtime_1.jsx)("span", { className: 'truncate', children: children })] })] }));
|
|
9
|
+
exports.PsScanButton = PsScanButton;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { type RefObject } from 'react';
|
|
1
|
+
import { type RefCallback, type RefObject } from 'react';
|
|
2
2
|
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
3
|
-
import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
|
|
4
3
|
import type { PsLabelSet } from '../models/ps-label-set';
|
|
4
|
+
import type { PsScanLiveDetection } from '../models/ps-scan-live-detection';
|
|
5
5
|
export interface PsScanCameraViewProps {
|
|
6
|
-
canGoBack: boolean;
|
|
7
6
|
canTorch: boolean;
|
|
7
|
+
detection: PsScanLiveDetection;
|
|
8
|
+
flashKey: number;
|
|
9
|
+
isBusy: boolean;
|
|
10
|
+
isReady: boolean;
|
|
8
11
|
isTorchOn: boolean;
|
|
9
12
|
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
10
|
-
onBack: () => void;
|
|
11
13
|
onCapture: () => void;
|
|
12
|
-
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
onFile: (file: File) => void;
|
|
13
16
|
onToggleTorch: () => void;
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
videoRef: RefObject<HTMLVideoElement | null>;
|
|
18
|
+
videoTarget: RefCallback<HTMLVideoElement>;
|
|
16
19
|
}
|
|
17
|
-
export declare const PsScanCameraView: ({
|
|
20
|
+
export declare const PsScanCameraView: ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }: PsScanCameraViewProps) => import("react").JSX.Element;
|
|
@@ -4,24 +4,22 @@ exports.PsScanCameraView = void 0;
|
|
|
4
4
|
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
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
7
8
|
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
8
|
-
const
|
|
9
|
+
const ps_scan_chrome_classes_1 = require("../constants/ps-scan-chrome-classes");
|
|
9
10
|
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
event.target.value = '';
|
|
22
|
-
if (files.length > 0)
|
|
23
|
-
onFiles(files);
|
|
11
|
+
const ps_scan_photo_button_1 = require("./ps-scan-photo-button");
|
|
12
|
+
const ps_scan_shutter_1 = require("./ps-scan-shutter");
|
|
13
|
+
const ps_scan_viewfinder_overlay_1 = require("./ps-scan-viewfinder-overlay");
|
|
14
|
+
const PsScanCameraView = ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }) => {
|
|
15
|
+
const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
|
|
16
|
+
const [video, setVideo] = (0, react_1.useState)({ height: 0, width: 0 });
|
|
17
|
+
const hint = detection.quad === null ? labels.hintFind : labels.hintFound;
|
|
18
|
+
const measure = () => {
|
|
19
|
+
const element = videoRef.current;
|
|
20
|
+
if (element !== null)
|
|
21
|
+
setVideo({ height: element.videoHeight, width: element.videoWidth });
|
|
24
22
|
};
|
|
25
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: '
|
|
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, quad: detection.quad, 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' })] })] }));
|
|
26
24
|
};
|
|
27
25
|
exports.PsScanCameraView = PsScanCameraView;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
2
|
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PsScanPhoto } from '../models/ps-scan-photo';
|
|
4
4
|
import type { PsScanQuad } from '../models/ps-scan-quad';
|
|
5
5
|
export interface PsScanCropEditorProps {
|
|
6
6
|
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
7
|
-
onApply: () => void;
|
|
8
7
|
onAutoDetect: () => void;
|
|
9
8
|
onChange: (corners: PsScanQuad) => void;
|
|
9
|
+
onContinue: () => void;
|
|
10
10
|
onRetake: () => void;
|
|
11
|
-
|
|
11
|
+
photo: PsScanPhoto;
|
|
12
12
|
}
|
|
13
|
-
export declare const PsScanCropEditor: ({ labels,
|
|
13
|
+
export declare const PsScanCropEditor: ({ labels, onAutoDetect, onChange, onContinue, onRetake, photo, }: PsScanCropEditorProps) => import("react").JSX.Element;
|
|
@@ -4,20 +4,29 @@ exports.PsScanCropEditor = void 0;
|
|
|
4
4
|
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
|
+
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
8
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
9
|
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
8
10
|
const ps_scan_corner_order_1 = require("../constants/ps-scan-corner-order");
|
|
9
11
|
const ps_scan_geometry_service_1 = require("../services/ps-scan-geometry-service");
|
|
10
|
-
const
|
|
12
|
+
const ps_scan_button_1 = require("./ps-scan-button");
|
|
13
|
+
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
14
|
+
const ps_scan_loupe_1 = require("./ps-scan-loupe");
|
|
15
|
+
const PsScanCropEditor = ({ labels, onAutoDetect, onChange, onContinue, onRetake, photo, }) => {
|
|
11
16
|
const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
|
|
12
17
|
const stageRef = (0, react_1.useRef)(null);
|
|
18
|
+
const [activeIndex, setActiveIndex] = (0, react_1.useState)(null);
|
|
13
19
|
const hintId = 'ps-scan-crop-hint';
|
|
14
|
-
const size = ps_scan_geometry_service_1.PsScanGeometryService.fit(area, { height:
|
|
20
|
+
const size = ps_scan_geometry_service_1.PsScanGeometryService.fit(area, { height: photo.height, width: photo.width });
|
|
15
21
|
const cornerLabels = [
|
|
16
22
|
labels.cornerTopLeft,
|
|
17
23
|
labels.cornerTopRight,
|
|
18
24
|
labels.cornerBottomRight,
|
|
19
25
|
labels.cornerBottomLeft,
|
|
20
26
|
];
|
|
27
|
+
const outline = photo.corners
|
|
28
|
+
.map((point) => `${point.x * size.width},${point.y * size.height}`)
|
|
29
|
+
.join(' L');
|
|
21
30
|
const move = (index, event) => {
|
|
22
31
|
const stage = stageRef.current;
|
|
23
32
|
if (stage === null || !event.currentTarget.hasPointerCapture(event.pointerId))
|
|
@@ -27,10 +36,14 @@ const PsScanCropEditor = ({ labels, onApply, onAutoDetect, onChange, onRetake, p
|
|
|
27
36
|
x: (event.clientX - rect.left) / rect.width,
|
|
28
37
|
y: (event.clientY - rect.top) / rect.height,
|
|
29
38
|
});
|
|
30
|
-
const next =
|
|
39
|
+
const next = photo.corners.map((corner, at) => (at === index ? point : corner));
|
|
31
40
|
if (ps_scan_geometry_service_1.PsScanGeometryService.isUsable(next))
|
|
32
41
|
onChange(next);
|
|
33
42
|
};
|
|
43
|
+
const release = (event) => {
|
|
44
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
45
|
+
setActiveIndex(null);
|
|
46
|
+
};
|
|
34
47
|
const nudge = (index, event) => {
|
|
35
48
|
const step = event.shiftKey ? ps_scan_config_1.PsScanConfig.cornerNudgeLarge : ps_scan_config_1.PsScanConfig.cornerNudge;
|
|
36
49
|
const deltas = {
|
|
@@ -43,11 +56,14 @@ const PsScanCropEditor = ({ labels, onApply, onAutoDetect, onChange, onRetake, p
|
|
|
43
56
|
if (delta === undefined)
|
|
44
57
|
return;
|
|
45
58
|
event.preventDefault();
|
|
46
|
-
onChange(ps_scan_geometry_service_1.PsScanGeometryService.nudge(
|
|
59
|
+
onChange(ps_scan_geometry_service_1.PsScanGeometryService.nudge(photo.corners, index, delta[0], delta[1]));
|
|
47
60
|
};
|
|
48
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col bg-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
61
|
+
return ((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)("span", { "aria-hidden": 'true' }), (0, jsx_runtime_1.jsx)("h2", { className: 'truncate text-center text-base font-semibold', children: labels.cropTitle }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Spark, label: labels.autoDetect, onClick: onAutoDetect })] }), (0, jsx_runtime_1.jsxs)("p", { className: 'shrink-0 px-6 text-center text-sm text-white/65', id: hintId, children: [photo.isDetected ? labels.edgesFound : labels.edgesNotFound, " ", labels.cropHint] }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-8 py-6', ref: areaRef, children: (0, jsx_runtime_1.jsxs)("div", { className: 'relative touch-none select-none', ref: stageRef, style: { height: size.height, width: size.width }, children: [(0, jsx_runtime_1.jsx)("img", { alt: '', className: 'absolute inset-0 size-full rounded-sm', draggable: false, src: photo.sourceUrl }), (0, jsx_runtime_1.jsxs)("svg", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 size-full overflow-visible', viewBox: `0 0 ${Math.max(1, size.width)} ${Math.max(1, size.height)}`, children: [(0, jsx_runtime_1.jsx)("path", { className: 'fill-black/55', d: `M0 0H${size.width}V${size.height}H0Z M${outline}Z`, fillRule: 'evenodd' }), (0, jsx_runtime_1.jsx)("path", { className: 'fill-none stroke-primary', d: `M${outline}Z`, strokeLinejoin: 'round', strokeWidth: '2.5' })] }), ps_scan_corner_order_1.PsScanCornerOrder.map((key, index) => ((0, jsx_runtime_1.jsx)("button", { "aria-describedby": hintId, "aria-label": cornerLabels[index], className: 'ps-focus-ring absolute flex size-11 -translate-x-1/2 -translate-y-1/2 cursor-grab touch-none items-center justify-center rounded-full active:cursor-grabbing', onKeyDown: (event) => nudge(index, event), onPointerCancel: release, onPointerDown: (event) => {
|
|
62
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
63
|
+
setActiveIndex(index);
|
|
64
|
+
}, onPointerMove: (event) => move(index, event), onPointerUp: release, style: {
|
|
65
|
+
left: `${photo.corners[index].x * 100}%`,
|
|
66
|
+
top: `${photo.corners[index].y * 100}%`,
|
|
67
|
+
}, type: 'button', children: (0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('size-6 rounded-full border-[3px] border-white bg-primary shadow-lg transition-transform duration-150', activeIndex === index ? 'scale-125' : '') }) }, key))), activeIndex === null ? null : ((0, jsx_runtime_1.jsx)(ps_scan_loupe_1.PsScanLoupe, { corners: photo.corners, point: photo.corners[activeIndex], source: photo.sourceUrl, stage: size }))] }) }), (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: [(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, { isLarge: true, isPrimary: true, onClick: onContinue, children: labels.continue })] })] }));
|
|
52
68
|
};
|
|
53
69
|
exports.PsScanCropEditor = PsScanCropEditor;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
export interface PsScanDeniedStepsProps {
|
|
4
|
+
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
5
|
+
}
|
|
6
|
+
export declare const PsScanDeniedSteps: ({ labels }: PsScanDeniedStepsProps) => import("react").JSX.Element;
|