@pallasoft/ps-file-components 1.4.0 → 1.5.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 +69 -21
- package/dist/components/ps-document-scanner.d.ts +1 -1
- package/dist/components/ps-document-scanner.js +2 -2
- 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 +17 -7
- package/dist/components/ps-scan-camera-view.js +23 -16
- package/dist/components/ps-scan-crop-editor.d.ts +2 -2
- package/dist/components/ps-scan-crop-editor.js +19 -3
- 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-page-list.d.ts +3 -1
- package/dist/components/ps-scan-page-list.js +7 -8
- package/dist/components/ps-scan-page-stack.d.ts +7 -0
- package/dist/components/ps-scan-page-stack.js +6 -0
- package/dist/components/ps-scan-permission-view.d.ts +11 -0
- package/dist/components/ps-scan-permission-view.js +48 -0
- package/dist/components/ps-scan-photo-button.d.ts +8 -0
- package/dist/components/ps-scan-photo-button.js +20 -0
- package/dist/components/ps-scan-review.d.ts +2 -1
- package/dist/components/ps-scan-review.js +21 -4
- package/dist/components/ps-scan-shutter.d.ts +7 -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 +49 -19
- package/dist/constants/ps-document-scanner-labels.js +49 -19
- 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 +12 -0
- package/dist/constants/ps-scan-chrome-classes.js +15 -0
- package/dist/constants/ps-scan-config.d.ts +18 -1
- package/dist/constants/ps-scan-config.js +18 -1
- package/dist/constants/ps-scan-platform.d.ts +6 -0
- package/dist/constants/ps-scan-platform.js +8 -0
- package/dist/esm/components/ps-document-scanner-sheet.js +71 -23
- package/dist/esm/components/ps-document-scanner.js +2 -2
- 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 +26 -19
- package/dist/esm/components/ps-scan-crop-editor.js +21 -5
- 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-page-list.js +7 -8
- package/dist/esm/components/ps-scan-page-stack.js +2 -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-review.js +21 -4
- 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 +49 -19
- 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 +12 -0
- package/dist/esm/constants/ps-scan-config.js +18 -1
- package/dist/esm/constants/ps-scan-platform.js +5 -0
- package/dist/esm/hooks/use-ps-scan-camera.js +52 -15
- package/dist/esm/hooks/use-ps-scan-live-detection.js +45 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/models/ps-file-picker-scan.js +1 -0
- package/dist/esm/models/ps-scan-live-detection.js +1 -0
- package/dist/esm/models/ps-scan-stability.js +1 -0
- package/dist/esm/services/ps-scan-camera-service.js +50 -5
- package/dist/esm/services/ps-scan-canvas-service.js +6 -0
- package/dist/esm/services/ps-scan-edge-service.js +1 -1
- package/dist/esm/services/ps-scan-geometry-service.js +20 -0
- package/dist/esm/services/ps-scan-platform-service.js +15 -0
- package/dist/esm/services/ps-scan-stability-service.js +41 -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 +49 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/ps-document-scanner-props.d.ts +1 -0
- package/dist/models/ps-document-scanner-sheet-props.d.ts +1 -0
- package/dist/models/ps-file-picker-scan.d.ts +4 -0
- package/dist/models/ps-file-picker-scan.js +2 -0
- package/dist/models/ps-scan-live-detection.d.ts +5 -0
- package/dist/models/ps-scan-live-detection.js +2 -0
- package/dist/models/ps-scan-stability.d.ts +8 -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 +50 -5
- package/dist/services/ps-scan-canvas-service.d.ts +1 -0
- package/dist/services/ps-scan-canvas-service.js +6 -0
- package/dist/services/ps-scan-edge-service.js +1 -1
- package/dist/services/ps-scan-geometry-service.d.ts +2 -0
- package/dist/services/ps-scan-geometry-service.js +20 -0
- 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-stability-service.d.ts +9 -0
- package/dist/services/ps-scan-stability-service.js +45 -0
- package/package.json +3 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export declare const PsFilePickerLabels: {
|
|
2
|
-
readonly
|
|
3
|
-
readonly
|
|
2
|
+
readonly choose: "Choose files";
|
|
3
|
+
readonly chooseOne: "Choose a file";
|
|
4
|
+
readonly dragHint: "Drag and drop files here";
|
|
5
|
+
readonly dragHintOne: "Drag and drop a file here";
|
|
4
6
|
readonly dropActive: "Drop to upload";
|
|
5
|
-
readonly formats: "Accepted formats:";
|
|
6
7
|
readonly invalidType: "is not a supported file type.";
|
|
8
|
+
readonly scan: "Scan with camera";
|
|
7
9
|
readonly tooLarge: "is too large.";
|
|
8
10
|
};
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsFilePickerLabels = void 0;
|
|
4
4
|
exports.PsFilePickerLabels = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
choose: 'Choose files',
|
|
6
|
+
chooseOne: 'Choose a file',
|
|
7
|
+
dragHint: 'Drag and drop files here',
|
|
8
|
+
dragHintOne: 'Drag and drop a file here',
|
|
7
9
|
dropActive: 'Drop to upload',
|
|
8
|
-
formats: 'Accepted formats:',
|
|
9
10
|
invalidType: 'is not a supported file type.',
|
|
11
|
+
scan: 'Scan with camera',
|
|
10
12
|
tooLarge: 'is too large.',
|
|
11
13
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export declare const PsScanCameraStatus: {
|
|
2
|
+
readonly Busy: "busy";
|
|
3
|
+
readonly Checking: "checking";
|
|
2
4
|
readonly Denied: "denied";
|
|
5
|
+
readonly Insecure: "insecure";
|
|
6
|
+
readonly Prompt: "prompt";
|
|
3
7
|
readonly Ready: "ready";
|
|
4
8
|
readonly Starting: "starting";
|
|
5
9
|
readonly Unavailable: "unavailable";
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsScanCameraStatus = void 0;
|
|
4
4
|
exports.PsScanCameraStatus = {
|
|
5
|
+
Busy: 'busy',
|
|
6
|
+
Checking: 'checking',
|
|
5
7
|
Denied: 'denied',
|
|
8
|
+
Insecure: 'insecure',
|
|
9
|
+
Prompt: 'prompt',
|
|
6
10
|
Ready: 'ready',
|
|
7
11
|
Starting: 'starting',
|
|
8
12
|
Unavailable: 'unavailable',
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const PsScanChromeClasses: {
|
|
2
|
+
readonly button: "ps-focus-ring ps-control ps-control-padding inline-flex max-w-full items-center justify-center gap-2 rounded-ps-control border text-sm font-semibold leading-5 transition disabled:pointer-events-none disabled:opacity-40";
|
|
3
|
+
readonly buttonLarge: "ps-control-large ps-control-padding-large";
|
|
4
|
+
readonly buttonPrimary: "border-primary bg-primary text-[color:var(--ps-color-primary-contrast)] hover:border-primary-dark hover:bg-primary-dark";
|
|
5
|
+
readonly buttonQuiet: "border-white/15 bg-white/10 text-white hover:border-white/25 hover:bg-white/20";
|
|
6
|
+
readonly chip: "ps-focus-ring ps-control inline-flex min-w-0 items-center justify-center rounded-ps-control px-2 text-sm font-semibold transition";
|
|
7
|
+
readonly icon: "ps-focus-ring ps-control ps-control-square inline-flex shrink-0 items-center justify-center rounded-ps-control text-white transition disabled:pointer-events-none disabled:opacity-40";
|
|
8
|
+
readonly iconOff: "bg-black/45 backdrop-blur-md hover:bg-white/20";
|
|
9
|
+
readonly iconOn: "bg-white text-black hover:bg-white/90";
|
|
10
|
+
readonly pill: "pointer-events-none rounded-ps-pill bg-black/55 px-3.5 py-1.5 text-sm font-medium text-white backdrop-blur-md";
|
|
11
|
+
readonly tool: "ps-focus-ring flex min-h-14 min-w-16 flex-col items-center justify-center gap-1 rounded-ps-control px-2 text-xs font-medium text-white/80 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-40";
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanChromeClasses = void 0;
|
|
4
|
+
exports.PsScanChromeClasses = {
|
|
5
|
+
button: 'ps-focus-ring ps-control ps-control-padding inline-flex max-w-full items-center justify-center gap-2 rounded-ps-control border text-sm font-semibold leading-5 transition disabled:pointer-events-none disabled:opacity-40',
|
|
6
|
+
buttonLarge: 'ps-control-large ps-control-padding-large',
|
|
7
|
+
buttonPrimary: 'border-primary bg-primary text-[color:var(--ps-color-primary-contrast)] hover:border-primary-dark hover:bg-primary-dark',
|
|
8
|
+
buttonQuiet: 'border-white/15 bg-white/10 text-white hover:border-white/25 hover:bg-white/20',
|
|
9
|
+
chip: 'ps-focus-ring ps-control inline-flex min-w-0 items-center justify-center rounded-ps-control px-2 text-sm font-semibold transition',
|
|
10
|
+
icon: 'ps-focus-ring ps-control ps-control-square inline-flex shrink-0 items-center justify-center rounded-ps-control text-white transition disabled:pointer-events-none disabled:opacity-40',
|
|
11
|
+
iconOff: 'bg-black/45 backdrop-blur-md hover:bg-white/20',
|
|
12
|
+
iconOn: 'bg-white text-black hover:bg-white/90',
|
|
13
|
+
pill: 'pointer-events-none rounded-ps-pill bg-black/55 px-3.5 py-1.5 text-sm font-medium text-white backdrop-blur-md',
|
|
14
|
+
tool: 'ps-focus-ring flex min-h-14 min-w-16 flex-col items-center justify-center gap-1 rounded-ps-control px-2 text-xs font-medium text-white/80 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-40',
|
|
15
|
+
};
|
|
@@ -2,6 +2,8 @@ export declare const PsScanConfig: {
|
|
|
2
2
|
readonly adaptiveRatio: 0.92;
|
|
3
3
|
readonly adaptiveWindowDivisor: 16;
|
|
4
4
|
readonly adaptiveWindowMin: 15;
|
|
5
|
+
readonly autoCaptureMs: 1000;
|
|
6
|
+
readonly cameraPermission: "camera";
|
|
5
7
|
readonly capturedJpegQuality: 0.92;
|
|
6
8
|
readonly cornerNudge: 0.005;
|
|
7
9
|
readonly cornerNudgeLarge: 0.025;
|
|
@@ -11,6 +13,7 @@ export declare const PsScanConfig: {
|
|
|
11
13
|
readonly defaultMaxDimension: 2000;
|
|
12
14
|
readonly defaultMaxPages: 30;
|
|
13
15
|
readonly detectDilations: 2;
|
|
16
|
+
readonly detectInset: 3;
|
|
14
17
|
readonly detectMaxDimension: 320;
|
|
15
18
|
readonly detectMinAreaRatio: 0.12;
|
|
16
19
|
readonly detectMaxAreaRatio: 0.995;
|
|
@@ -18,14 +21,28 @@ export declare const PsScanConfig: {
|
|
|
18
21
|
readonly detectMinHullFill: 0.85;
|
|
19
22
|
readonly detectPercentile: 0.98;
|
|
20
23
|
readonly detectRelativeStrength: 0.3;
|
|
24
|
+
readonly guideArm: 36;
|
|
25
|
+
readonly guideArmRatio: 0.18;
|
|
26
|
+
readonly guideAspect: 0.72;
|
|
27
|
+
readonly guideHeightRatio: 0.58;
|
|
28
|
+
readonly guideLift: 0.03;
|
|
29
|
+
readonly guideWidthRatio: 0.76;
|
|
21
30
|
readonly hullCandidateLimit: 18;
|
|
22
31
|
readonly levelHighPercentile: 0.99;
|
|
23
32
|
readonly levelLowPercentile: 0.01;
|
|
33
|
+
readonly liveDetectIntervalMs: 160;
|
|
34
|
+
readonly liveDetectMaxDimension: 240;
|
|
35
|
+
readonly liveMissLimit: 2;
|
|
36
|
+
readonly liveRearmMovement: 0.08;
|
|
37
|
+
readonly liveSmoothing: 0.5;
|
|
38
|
+
readonly liveStillMovement: 0.02;
|
|
39
|
+
readonly loupeOffset: 72;
|
|
40
|
+
readonly loupeSize: 112;
|
|
41
|
+
readonly loupeZoom: 2.5;
|
|
24
42
|
readonly minCornerGap: 0.02;
|
|
25
43
|
readonly pdfDpi: 150;
|
|
26
44
|
readonly pdfPointsPerInch: 72;
|
|
27
45
|
readonly previewDebounceMs: 120;
|
|
28
46
|
readonly previewMaxDimension: 1400;
|
|
29
|
-
readonly thumbnailHeight: 56;
|
|
30
47
|
readonly torchConstraint: "torch";
|
|
31
48
|
};
|
|
@@ -5,6 +5,8 @@ exports.PsScanConfig = {
|
|
|
5
5
|
adaptiveRatio: 0.92,
|
|
6
6
|
adaptiveWindowDivisor: 16,
|
|
7
7
|
adaptiveWindowMin: 15,
|
|
8
|
+
autoCaptureMs: 1000,
|
|
9
|
+
cameraPermission: 'camera',
|
|
8
10
|
capturedJpegQuality: 0.92,
|
|
9
11
|
cornerNudge: 0.005,
|
|
10
12
|
cornerNudgeLarge: 0.025,
|
|
@@ -14,6 +16,7 @@ exports.PsScanConfig = {
|
|
|
14
16
|
defaultMaxDimension: 2000,
|
|
15
17
|
defaultMaxPages: 30,
|
|
16
18
|
detectDilations: 2,
|
|
19
|
+
detectInset: 3,
|
|
17
20
|
detectMaxDimension: 320,
|
|
18
21
|
detectMinAreaRatio: 0.12,
|
|
19
22
|
detectMaxAreaRatio: 0.995,
|
|
@@ -21,14 +24,28 @@ exports.PsScanConfig = {
|
|
|
21
24
|
detectMinHullFill: 0.85,
|
|
22
25
|
detectPercentile: 0.98,
|
|
23
26
|
detectRelativeStrength: 0.3,
|
|
27
|
+
guideArm: 36,
|
|
28
|
+
guideArmRatio: 0.18,
|
|
29
|
+
guideAspect: 0.72,
|
|
30
|
+
guideHeightRatio: 0.58,
|
|
31
|
+
guideLift: 0.03,
|
|
32
|
+
guideWidthRatio: 0.76,
|
|
24
33
|
hullCandidateLimit: 18,
|
|
25
34
|
levelHighPercentile: 0.99,
|
|
26
35
|
levelLowPercentile: 0.01,
|
|
36
|
+
liveDetectIntervalMs: 160,
|
|
37
|
+
liveDetectMaxDimension: 240,
|
|
38
|
+
liveMissLimit: 2,
|
|
39
|
+
liveRearmMovement: 0.08,
|
|
40
|
+
liveSmoothing: 0.5,
|
|
41
|
+
liveStillMovement: 0.02,
|
|
42
|
+
loupeOffset: 72,
|
|
43
|
+
loupeSize: 112,
|
|
44
|
+
loupeZoom: 2.5,
|
|
27
45
|
minCornerGap: 0.02,
|
|
28
46
|
pdfDpi: 150,
|
|
29
47
|
pdfPointsPerInch: 72,
|
|
30
48
|
previewDebounceMs: 120,
|
|
31
49
|
previewMaxDimension: 1400,
|
|
32
|
-
thumbnailHeight: 56,
|
|
33
50
|
torchConstraint: 'torch',
|
|
34
51
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useRef, useState } from 'react';
|
|
3
3
|
import { PsLayer, usePsLayerZIndex, usePsRestoreFocus, usePsScrollLock, usePsViewport, } from '@pallasoft/ps-core-components';
|
|
4
4
|
import { PallasoftViewportTier, PallasoftZIndex } from '@pallasoft/ps-design-system';
|
|
5
|
-
import {
|
|
6
|
-
import { PsModal } from '@pallasoft/ps-popovers';
|
|
5
|
+
import { PsIconKey } from '@pallasoft/ps-icons';
|
|
7
6
|
import { createPortal } from 'react-dom';
|
|
7
|
+
import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
|
|
8
8
|
import { PsScanStep } from '../constants/ps-scan-step';
|
|
9
9
|
import { usePsFocusTrap } from '../hooks/use-ps-focus-trap';
|
|
10
10
|
import { usePsScanCamera } from '../hooks/use-ps-scan-camera';
|
|
11
|
+
import { usePsScanLiveDetection } from '../hooks/use-ps-scan-live-detection';
|
|
11
12
|
import { usePsScanPages } from '../hooks/use-ps-scan-pages';
|
|
12
13
|
import { usePsScanPreviews } from '../hooks/use-ps-scan-previews';
|
|
13
14
|
import { PsScanOutputService } from '../services/ps-scan-output-service';
|
|
@@ -16,27 +17,37 @@ import { PsScanTemplateService } from '../services/ps-scan-template-service';
|
|
|
16
17
|
import { PsScanCameraView } from './ps-scan-camera-view';
|
|
17
18
|
import { PsScanCropEditor } from './ps-scan-crop-editor';
|
|
18
19
|
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
20
|
+
import { PsScanPermissionView } from './ps-scan-permission-view';
|
|
19
21
|
import { PsScanReview } from './ps-scan-review';
|
|
20
|
-
export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }) => {
|
|
22
|
+
export const PsDocumentScannerSheet = ({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }) => {
|
|
21
23
|
const tier = usePsViewport();
|
|
22
24
|
const zIndex = usePsLayerZIndex(PallasoftZIndex.Modal);
|
|
23
25
|
const content = useRef(null);
|
|
24
26
|
const { add, move, pages, remove, replace, setCorners, update } = usePsScanPages();
|
|
25
27
|
const [step, setStep] = useState(PsScanStep.Camera);
|
|
28
|
+
const [cropReturn, setCropReturn] = useState(PsScanStep.Camera);
|
|
26
29
|
const [activeId, setActiveId] = useState('');
|
|
27
30
|
const [retakeId, setRetakeId] = useState('');
|
|
28
31
|
const [announcement, setAnnouncement] = useState('');
|
|
29
32
|
const [error, setError] = useState('');
|
|
30
33
|
const [isBusy, setIsBusy] = useState(false);
|
|
34
|
+
const [isCapturing, setIsCapturing] = useState(false);
|
|
35
|
+
const [isAuto, setIsAuto] = useState(isAutoCapture);
|
|
36
|
+
const [flashKey, setFlashKey] = useState(0);
|
|
31
37
|
const camera = usePsScanCamera(step === PsScanStep.Camera);
|
|
38
|
+
const isReady = camera.status === PsScanCameraStatus.Ready;
|
|
39
|
+
const isFull = pages.length >= maxPages;
|
|
32
40
|
const previews = usePsScanPreviews(pages);
|
|
33
41
|
const page = pages.find((item) => item.id === activeId);
|
|
42
|
+
const lastPage = pages[pages.length - 1];
|
|
34
43
|
const isDesktop = tier === PallasoftViewportTier.Desktop;
|
|
35
|
-
|
|
44
|
+
const isCameraStep = step === PsScanStep.Camera;
|
|
45
|
+
const showsCamera = isReady || camera.status === PsScanCameraStatus.Starting;
|
|
46
|
+
usePsScrollLock(true);
|
|
36
47
|
usePsRestoreFocus(true);
|
|
37
48
|
usePsFocusTrap(content, true, onClose);
|
|
38
49
|
const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
|
|
39
|
-
const addFiles = async (files) => {
|
|
50
|
+
const addFiles = async (files, after) => {
|
|
40
51
|
setError('');
|
|
41
52
|
try {
|
|
42
53
|
const images = files.filter((file) => file.type === '' || file.type.startsWith('image/'));
|
|
@@ -54,21 +65,39 @@ export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimen
|
|
|
54
65
|
const added = await add(images.slice(0, room));
|
|
55
66
|
if (added.length === 0)
|
|
56
67
|
return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
setAnnouncement(
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const position = pages.length + added.length;
|
|
69
|
+
setActiveId(added[added.length - 1].page.id);
|
|
70
|
+
setAnnouncement(PsScanTemplateService.fill(labels.pageAdded, { position }));
|
|
71
|
+
if (after === null)
|
|
72
|
+
return;
|
|
73
|
+
if (added.length === 1) {
|
|
74
|
+
setCropReturn(after);
|
|
75
|
+
setStep(PsScanStep.Crop);
|
|
76
|
+
setAnnouncement(edgeMessage(added[0].isDetected));
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
setStep(PsScanStep.Review);
|
|
62
80
|
}
|
|
63
81
|
catch {
|
|
64
82
|
setError(labels.processFailed);
|
|
65
83
|
}
|
|
66
84
|
};
|
|
67
|
-
const capture = async () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
const capture = async (isAutomatic) => {
|
|
86
|
+
if (isCapturing || isFull)
|
|
87
|
+
return;
|
|
88
|
+
setIsCapturing(true);
|
|
89
|
+
try {
|
|
90
|
+
const blob = await camera.capture();
|
|
91
|
+
if (blob === null)
|
|
92
|
+
return;
|
|
93
|
+
setFlashKey((current) => current + 1);
|
|
94
|
+
await addFiles([blob], isAutomatic && retakeId === '' ? null : PsScanStep.Camera);
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
setIsCapturing(false);
|
|
98
|
+
}
|
|
71
99
|
};
|
|
100
|
+
const detection = usePsScanLiveDetection(camera.videoRef, isCameraStep && isReady, isAuto, () => capture(true));
|
|
72
101
|
const autoDetect = async () => {
|
|
73
102
|
if (page === undefined)
|
|
74
103
|
return;
|
|
@@ -76,12 +105,32 @@ export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimen
|
|
|
76
105
|
setCorners(page.id, prepared.corners);
|
|
77
106
|
setAnnouncement(edgeMessage(prepared.isDetected));
|
|
78
107
|
};
|
|
108
|
+
const keep = () => setStep(cropReturn === PsScanStep.Camera && !isFull ? PsScanStep.Camera : PsScanStep.Review);
|
|
79
109
|
const retake = () => {
|
|
80
110
|
if (page === undefined)
|
|
81
111
|
return;
|
|
112
|
+
if (step === PsScanStep.Review)
|
|
113
|
+
setCropReturn(PsScanStep.Review);
|
|
82
114
|
setRetakeId(page.id);
|
|
83
115
|
setStep(PsScanStep.Camera);
|
|
84
116
|
};
|
|
117
|
+
const openCamera = () => {
|
|
118
|
+
setRetakeId('');
|
|
119
|
+
setCropReturn(PsScanStep.Camera);
|
|
120
|
+
setStep(PsScanStep.Camera);
|
|
121
|
+
};
|
|
122
|
+
const openReview = () => {
|
|
123
|
+
if (pages.length === 0)
|
|
124
|
+
return;
|
|
125
|
+
setRetakeId('');
|
|
126
|
+
if (page === undefined)
|
|
127
|
+
setActiveId(lastPage.id);
|
|
128
|
+
setStep(PsScanStep.Review);
|
|
129
|
+
};
|
|
130
|
+
const editCrop = () => {
|
|
131
|
+
setCropReturn(PsScanStep.Review);
|
|
132
|
+
setStep(PsScanStep.Crop);
|
|
133
|
+
};
|
|
85
134
|
const deletePage = () => {
|
|
86
135
|
if (page === undefined)
|
|
87
136
|
return;
|
|
@@ -91,7 +140,7 @@ export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimen
|
|
|
91
140
|
setAnnouncement(labels.pageDeleted);
|
|
92
141
|
if (rest.length === 0) {
|
|
93
142
|
setActiveId('');
|
|
94
|
-
|
|
143
|
+
openCamera();
|
|
95
144
|
}
|
|
96
145
|
else
|
|
97
146
|
setActiveId(rest[Math.min(index, rest.length - 1)].id);
|
|
@@ -122,13 +171,12 @@ export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimen
|
|
|
122
171
|
position: to + 1,
|
|
123
172
|
}));
|
|
124
173
|
}, [move, pages.length, labels]);
|
|
125
|
-
const
|
|
126
|
-
setRetakeId('');
|
|
127
|
-
setStep(PsScanStep.Review);
|
|
128
|
-
}, onCapture: capture, onFiles: addFiles, onToggleTorch: camera.toggleTorch, status: camera.status, videoRef: camera.videoRef })) : null, step === PsScanStep.Crop && page !== undefined ? (_jsx(PsScanCropEditor, { labels: labels, onApply: () => setStep(PsScanStep.Review), onAutoDetect: autoDetect, onChange: (corners) => setCorners(page.id, corners), onRetake: retake, page: page })) : null, step === PsScanStep.Review && page !== undefined ? (_jsx(PsScanReview, { canAddPage: pages.length < maxPages, error: error, isBusy: isBusy, labels: labels, onAddPage: () => setStep(PsScanStep.Camera), onDelete: deletePage, onEditCrop: () => setStep(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 === PsScanStep.Camera && error !== '' ? (_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] }));
|
|
129
|
-
if (isDesktop)
|
|
130
|
-
return (_jsx(PsModal, { isOpen: true, onClose: onClose, size: 'xl', title: labels.title, children: _jsx("div", { className: '-m-4 h-[min(70dvh,38rem)] overflow-hidden sm:-m-5', children: body }) }));
|
|
174
|
+
const fromLibrary = (files) => addFiles(files, PsScanStep.Review);
|
|
131
175
|
if (typeof document === 'undefined')
|
|
132
176
|
return null;
|
|
133
|
-
return createPortal(_jsx("div", { "aria-label": labels.title, "aria-modal": 'true', className:
|
|
177
|
+
return createPortal(_jsx("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
|
|
178
|
+
? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
|
|
179
|
+
: 'fixed inset-0 bg-black animate-ps-fade-in', role: 'dialog', style: { zIndex }, children: _jsx(PsLayer, { zIndex: zIndex, children: _jsxs("div", { className: isDesktop
|
|
180
|
+
? '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'
|
|
181
|
+
: 'relative h-full overflow-hidden bg-black text-white', ref: content, children: [_jsx("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), isCameraStep && showsCamera ? (_jsx(PsScanCameraView, { canTorch: camera.canTorch, detection: detection, flashKey: flashKey, isAuto: isAuto, isBusy: isCapturing, isFull: isFull, isMultiple: maxPages > 1, isReady: isReady, isTorchOn: camera.isTorchOn, labels: labels, lastPreview: lastPage === undefined ? undefined : previews[lastPage.id], onCapture: () => capture(false), onClose: onClose, onDone: openReview, onFiles: fromLibrary, onToggleAuto: () => setIsAuto((current) => !current), onToggleTorch: camera.toggleTorch, pageCount: pages.length, videoRef: camera.videoRef, videoTarget: camera.attachVideo })) : null, isCameraStep && !showsCamera ? (_jsxs(_Fragment, { children: [_jsx(PsScanPermissionView, { isMultiple: maxPages > 1, labels: labels, onAllow: camera.allow, onFiles: fromLibrary, status: camera.status }), _jsx("div", { className: 'absolute left-4 top-[max(1rem,env(safe-area-inset-top))] flex gap-2', children: _jsx(PsScanIconButton, { iconKey: PsIconKey.Close, label: labels.close, onClick: onClose }) }), pages.length > 0 ? (_jsx("div", { className: 'absolute right-4 top-[max(1rem,env(safe-area-inset-top))]', children: _jsx(PsScanIconButton, { iconKey: PsIconKey.Check, label: PsScanTemplateService.fill(labels.viewPages, { count: pages.length }), onClick: openReview }) })) : null] })) : null, step === PsScanStep.Crop && page !== undefined ? (_jsx(PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: (corners) => setCorners(page.id, corners), onKeep: keep, onRetake: retake, page: page })) : null, step === PsScanStep.Review && page !== undefined ? (_jsx(PsScanReview, { canAddPage: !isFull, error: error, isBusy: isBusy, labels: labels, onAddPage: openCamera, onClose: onClose, onDelete: deletePage, onEditCrop: editCrop, 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, isCameraStep && error !== '' ? (_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);
|
|
134
182
|
};
|
|
@@ -5,9 +5,9 @@ import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels
|
|
|
5
5
|
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
6
6
|
import { PsScanOutputFormat } from '../constants/ps-scan-output-format';
|
|
7
7
|
import { PsDocumentScannerLoader } from './ps-document-scanner-loader';
|
|
8
|
-
export const PsDocumentScanner = ({ fileName = PsScanConfig.defaultFileName, isOpen, jpegQuality = PsScanConfig.defaultJpegQuality, labels, maxDimension = PsScanConfig.defaultMaxDimension, maxPages = PsScanConfig.defaultMaxPages, onClose, onComplete, outputFormat = PsScanOutputFormat.Pdf, }) => {
|
|
8
|
+
export const PsDocumentScanner = ({ fileName = PsScanConfig.defaultFileName, isAutoCapture = false, isOpen, jpegQuality = PsScanConfig.defaultJpegQuality, labels, maxDimension = PsScanConfig.defaultMaxDimension, maxPages = PsScanConfig.defaultMaxPages, onClose, onComplete, outputFormat = PsScanOutputFormat.Pdf, }) => {
|
|
9
9
|
const isHydrated = usePsHydrated();
|
|
10
10
|
if (!isOpen || !isHydrated)
|
|
11
11
|
return null;
|
|
12
|
-
return (_jsx(Suspense, { fallback: null, children: _jsx(PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, maxPages: maxPages, onClose: onClose, onComplete: onComplete, outputFormat: outputFormat }) }));
|
|
12
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(PsDocumentScannerLoader, { fileName: fileName, isAutoCapture: isAutoCapture, jpegQuality: jpegQuality, labels: { ...PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, maxPages: maxPages, onClose: onClose, onComplete: onComplete, outputFormat: outputFormat }) }));
|
|
13
13
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsButton } from '@pallasoft/ps-core-components';
|
|
3
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
5
|
+
export const PsFilePickerActions = ({ chooseLabel, className, disabled, isTouch, onChoose, onScan, scanLabel, }) => {
|
|
6
|
+
const hasScan = scanLabel !== undefined;
|
|
7
|
+
const choose = (_jsx(PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Upload }), onClick: (event) => {
|
|
8
|
+
event.stopPropagation();
|
|
9
|
+
onChoose();
|
|
10
|
+
}, size: isTouch ? 'large' : 'default', variant: hasScan && isTouch ? 'surface' : hasScan || isTouch ? 'primary' : 'surface', children: chooseLabel }));
|
|
11
|
+
return (_jsx("div", { className: PallasoftClassNameService.merge(isTouch
|
|
12
|
+
? 'w-full max-w-xs flex-col gap-2'
|
|
13
|
+
: 'flex-row flex-wrap items-center justify-center gap-2', className), children: hasScan ? (_jsxs(_Fragment, { children: [isTouch ? null : choose, _jsx(PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Camera }), onClick: (event) => {
|
|
14
|
+
event.stopPropagation();
|
|
15
|
+
onScan();
|
|
16
|
+
}, size: isTouch ? 'large' : 'default', variant: isTouch ? 'primary' : 'surface', children: scanLabel }), isTouch ? choose : null] })) : (choose) }));
|
|
17
|
+
};
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from 'react';
|
|
2
|
+
import { useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
4
|
import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
|
|
5
5
|
import { PsFilePickerLabels } from '../constants/ps-file-picker-labels';
|
|
6
|
+
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
7
|
+
import { PsUploadLabels } from '../constants/ps-upload-labels';
|
|
6
8
|
import { PsFileSelectionService } from '../services/ps-file-selection-service';
|
|
7
|
-
|
|
9
|
+
import { PsDocumentScanner } from './ps-document-scanner';
|
|
10
|
+
import { PsFilePickerActions } from './ps-file-picker-actions';
|
|
11
|
+
import { PsUploadRow } from './ps-upload-row';
|
|
12
|
+
export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLabel, isLoading = false, labels, maxSizeBytes, maxSizeLabel, multiple = false, onError, onFileLoad, onFilesSelect, queue, scan, touchLabel, uploadLabels, }) => {
|
|
8
13
|
const [isDragging, setIsDragging] = useState(false);
|
|
14
|
+
const [scanName, setScanName] = useState(null);
|
|
15
|
+
const input = useRef(null);
|
|
9
16
|
const acceptAttr = useMemo(() => accept.join(','), [accept]);
|
|
10
17
|
const text = { ...PsFilePickerLabels, ...labels };
|
|
18
|
+
const rowLabels = { ...PsUploadLabels, ...uploadLabels };
|
|
19
|
+
const details = [acceptLabel, maxSizeLabel].filter((value) => value !== undefined && value !== '');
|
|
20
|
+
const chooseLabel = multiple ? text.choose : text.chooseOne;
|
|
21
|
+
const scanLabel = scan === undefined ? undefined : text.scan;
|
|
11
22
|
if (isLoading)
|
|
12
|
-
return (_jsx("span", { "aria-busy": 'true', className: PallasoftClassNameService.merge('ps-skeleton block min-h-
|
|
23
|
+
return (_jsx("span", { "aria-busy": 'true', className: PallasoftClassNameService.merge('ps-skeleton block min-h-48 w-full rounded-ps-control', className), role: 'status' }));
|
|
13
24
|
const select = (list) => {
|
|
14
25
|
if (list === null || disabled)
|
|
15
26
|
return;
|
|
@@ -28,6 +39,15 @@ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false,
|
|
|
28
39
|
else
|
|
29
40
|
onFileLoad?.(accepted[0]);
|
|
30
41
|
};
|
|
42
|
+
const browse = () => {
|
|
43
|
+
if (!disabled)
|
|
44
|
+
input.current?.click();
|
|
45
|
+
};
|
|
46
|
+
const openScan = () => {
|
|
47
|
+
if (disabled || scan === undefined)
|
|
48
|
+
return;
|
|
49
|
+
setScanName(scan.getFileName === undefined ? PsScanConfig.defaultFileName : scan.getFileName());
|
|
50
|
+
};
|
|
31
51
|
const onChange = (event) => {
|
|
32
52
|
select(event.target.files);
|
|
33
53
|
event.target.value = '';
|
|
@@ -37,8 +57,12 @@ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false,
|
|
|
37
57
|
if (!disabled)
|
|
38
58
|
setIsDragging(isOver);
|
|
39
59
|
};
|
|
40
|
-
return (_jsxs("div", { className: PallasoftClassNameService.merge('
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
return (_jsxs("div", { className: PallasoftClassNameService.merge('flex w-full flex-col gap-3', className), children: [_jsxs("div", { "aria-disabled": disabled || undefined, className: 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
|
|
61
|
+
? 'border-primary bg-primary/5'
|
|
62
|
+
: '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) => {
|
|
63
|
+
onDrag(event, false);
|
|
64
|
+
select(event.dataTransfer.files);
|
|
65
|
+
}, children: [_jsx("span", { "aria-hidden": 'true', className: PallasoftClassNameService.merge('flex size-12 items-center justify-center rounded-full transition-colors duration-200', isDragging
|
|
66
|
+
? 'bg-primary text-[color:var(--ps-color-primary-contrast)]'
|
|
67
|
+
: 'bg-primary-soft text-primary'), children: _jsx(PsIcon, { className: 'size-6', iconKey: PsIconKey.Upload }) }), _jsxs("div", { className: 'flex flex-col gap-1', children: [_jsx("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : (_jsxs(_Fragment, { children: [_jsx("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : (_jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), _jsx("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? _jsx("p", { className: 'text-xs text-ink/50', children: details.join(' · ') }) : null, _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 : (_jsx("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => (_jsx(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 : (_jsx(PsDocumentScanner, { ...scan, fileName: scanName === null ? PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
|
|
44
68
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { PsSpinner } from '@pallasoft/ps-core-components';
|
|
3
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
4
|
+
import { PsScanChromeClasses } from '../constants/ps-scan-chrome-classes';
|
|
5
|
+
export const PsScanButton = ({ children, className, disabled = false, isLarge = false, isLoading = false, isPrimary = false, leadingIcon, loadingLabel, onClick, }) => (_jsxs("button", { "aria-busy": isLoading || undefined, className: PallasoftClassNameService.merge(PsScanChromeClasses.button, isLarge ? PsScanChromeClasses.buttonLarge : '', isPrimary ? PsScanChromeClasses.buttonPrimary : PsScanChromeClasses.buttonQuiet, 'relative', className), disabled: disabled || isLoading, onClick: onClick, type: 'button', children: [isLoading ? (_jsx(PsSpinner, { className: 'absolute inset-0 m-auto', color: 'current', label: loadingLabel === undefined ? '' : loadingLabel, size: 'small' })) : null, _jsxs("span", { "aria-hidden": isLoading || undefined, className: `inline-flex min-w-0 items-center gap-2 ${isLoading ? 'invisible' : ''}`, children: [leadingIcon === undefined ? null : _jsx("span", { className: 'shrink-0', children: leadingIcon }), _jsx("span", { className: 'truncate', children: children })] })] }));
|
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { PsSpinner, usePsElementSize } from '@pallasoft/ps-core-components';
|
|
4
|
+
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
5
|
+
import { PsIconKey } from '@pallasoft/ps-icons';
|
|
6
|
+
import { PsScanChromeClasses } from '../constants/ps-scan-chrome-classes';
|
|
7
|
+
import { PsScanTemplateService } from '../services/ps-scan-template-service';
|
|
8
|
+
import { PsScanButton } from './ps-scan-button';
|
|
6
9
|
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
import { PsScanPageStack } from './ps-scan-page-stack';
|
|
11
|
+
import { PsScanPhotoButton } from './ps-scan-photo-button';
|
|
12
|
+
import { PsScanShutter } from './ps-scan-shutter';
|
|
13
|
+
import { PsScanViewfinderOverlay } from './ps-scan-viewfinder-overlay';
|
|
14
|
+
export const PsScanCameraView = ({ canTorch, detection, flashKey, isAuto, isBusy, isFull, isMultiple, isReady, isTorchOn, labels, lastPreview, onCapture, onClose, onDone, onFiles, onToggleAuto, onToggleTorch, pageCount, videoRef, videoTarget, }) => {
|
|
15
|
+
const [areaRef, area] = usePsElementSize();
|
|
16
|
+
const [video, setVideo] = useState({ height: 0, width: 0 });
|
|
17
|
+
const hint = isFull
|
|
18
|
+
? labels.maxPages
|
|
19
|
+
: detection.quad === null
|
|
20
|
+
? labels.hintFind
|
|
21
|
+
: isAuto
|
|
22
|
+
? labels.hintHold
|
|
23
|
+
: labels.hintFound;
|
|
24
|
+
const measure = () => {
|
|
25
|
+
const element = videoRef.current;
|
|
26
|
+
if (element !== null)
|
|
27
|
+
setVideo({ height: element.videoHeight, width: element.videoWidth });
|
|
21
28
|
};
|
|
22
|
-
return (_jsxs("div", { className: '
|
|
29
|
+
return (_jsxs("div", { className: 'relative h-full min-h-0 overflow-hidden bg-black text-white', children: [_jsxs("div", { className: 'absolute inset-0', ref: areaRef, children: [_jsx("video", { "aria-hidden": 'true', autoPlay: true, className: 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 ? (_jsx(PsScanViewfinderOverlay, { area: area, quad: detection.quad, video: video })) : null] }), isReady ? null : (_jsxs("div", { className: 'absolute inset-0 flex flex-col items-center justify-center gap-3 text-sm text-white/75', role: 'status', children: [_jsx(PsSpinner, { color: 'current', label: labels.cameraStarting, size: 'small' }), _jsx("span", { children: labels.cameraStarting })] })), flashKey > 0 ? (_jsx("div", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 bg-white animate-ps-fade-out' }, flashKey)) : null, _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: [_jsx(PsScanIconButton, { iconKey: PsIconKey.Close, label: labels.close, onClick: onClose }), _jsxs("div", { className: 'flex items-center gap-2', children: [canTorch && isReady ? (_jsx(PsScanIconButton, { iconKey: PsIconKey.Flash, isPressed: isTorchOn, label: isTorchOn ? labels.torchOff : labels.torchOn, onClick: onToggleTorch })) : null, _jsx("button", { "aria-label": labels.autoCapture, "aria-pressed": isAuto, className: PallasoftClassNameService.merge(PsScanChromeClasses.button, 'border-transparent', isAuto ? PsScanChromeClasses.iconOn : PsScanChromeClasses.iconOff), onClick: onToggleAuto, title: labels.autoCapture, type: 'button', children: labels.auto })] })] }), isReady ? (_jsx("div", { className: 'absolute inset-x-0 top-[calc(max(1rem,env(safe-area-inset-top))+3.75rem)] flex justify-center px-4', children: _jsx("p", { "aria-live": 'polite', className: PsScanChromeClasses.pill, children: hint }) })) : null, _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: [_jsx("div", { className: 'flex justify-start', children: pageCount > 0 ? (_jsx(PsScanPageStack, { count: pageCount, label: PsScanTemplateService.fill(labels.viewPages, { count: pageCount }), onOpen: onDone, preview: lastPreview })) : (_jsx(PsScanPhotoButton, { isIconOnly: true, isMultiple: isMultiple, label: labels.choosePhotos, onFiles: onFiles })) }), _jsx(PsScanShutter, { disabled: !isReady || isFull || isBusy, label: labels.capture, onCapture: onCapture, progress: detection.progress }), _jsx("div", { className: 'flex justify-end', children: pageCount > 0 ? (_jsx(PsScanButton, { isPrimary: true, onClick: onDone, children: labels.done })) : null })] })] }));
|
|
23
30
|
};
|