@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,31 +1,41 @@
|
|
|
1
1
|
export declare const PsScanConfig: {
|
|
2
|
-
readonly
|
|
3
|
-
readonly
|
|
4
|
-
readonly
|
|
5
|
-
readonly
|
|
2
|
+
readonly cameraPermission: "camera";
|
|
3
|
+
readonly cannyHighPercentile: 0.9;
|
|
4
|
+
readonly cannyLowRatio: 0.45;
|
|
5
|
+
readonly cannyMinHigh: 12;
|
|
6
|
+
readonly capturedJpegQuality: 0.97;
|
|
7
|
+
readonly closeRadii: readonly [1, 2, 4];
|
|
6
8
|
readonly cornerNudge: 0.005;
|
|
7
9
|
readonly cornerNudgeLarge: 0.025;
|
|
8
|
-
readonly
|
|
10
|
+
readonly decodeMaxPixels: 16000000;
|
|
9
11
|
readonly defaultFileName: "scan";
|
|
10
|
-
readonly defaultJpegQuality: 0.
|
|
11
|
-
readonly defaultMaxDimension:
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
20
|
-
readonly
|
|
21
|
-
readonly
|
|
22
|
-
readonly
|
|
23
|
-
readonly
|
|
12
|
+
readonly defaultJpegQuality: 0.92;
|
|
13
|
+
readonly defaultMaxDimension: 4000;
|
|
14
|
+
readonly detectBorderMargin: 3;
|
|
15
|
+
readonly detectExpand: 0.3;
|
|
16
|
+
readonly detectMaxAngle: 140;
|
|
17
|
+
readonly detectMaxAreaRatio: 0.95;
|
|
18
|
+
readonly detectMaxDimension: 480;
|
|
19
|
+
readonly detectMinAngle: 40;
|
|
20
|
+
readonly detectMinAreaRatio: 0.1;
|
|
21
|
+
readonly detectMinQuadFill: 0.88;
|
|
22
|
+
readonly detectMinSideRatio: 0.06;
|
|
23
|
+
readonly guideArm: 36;
|
|
24
|
+
readonly guideArmRatio: 0.18;
|
|
25
|
+
readonly guideAspect: 0.72;
|
|
26
|
+
readonly guideHeightRatio: 0.58;
|
|
27
|
+
readonly guideLift: 0.03;
|
|
28
|
+
readonly guideWidthRatio: 0.76;
|
|
29
|
+
readonly liveDeadzone: 0.006;
|
|
30
|
+
readonly liveDetectIntervalMs: 200;
|
|
31
|
+
readonly liveDetectMaxDimension: 280;
|
|
32
|
+
readonly liveMissLimit: 3;
|
|
33
|
+
readonly liveSmoothing: 0.45;
|
|
34
|
+
readonly loupeOffset: 72;
|
|
35
|
+
readonly loupeSize: 112;
|
|
36
|
+
readonly loupeZoom: 2.5;
|
|
24
37
|
readonly minCornerGap: 0.02;
|
|
25
|
-
readonly
|
|
26
|
-
readonly pdfPointsPerInch: 72;
|
|
27
|
-
readonly previewDebounceMs: 120;
|
|
28
|
-
readonly previewMaxDimension: 1400;
|
|
29
|
-
readonly thumbnailHeight: 56;
|
|
38
|
+
readonly polygonEpsilons: readonly [0.008, 0.012, 0.018, 0.026, 0.036, 0.05, 0.07];
|
|
30
39
|
readonly torchConstraint: "torch";
|
|
40
|
+
readonly warpChunkPixels: 400000;
|
|
31
41
|
};
|
|
@@ -2,33 +2,43 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PsScanConfig = void 0;
|
|
4
4
|
exports.PsScanConfig = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
cameraPermission: 'camera',
|
|
6
|
+
cannyHighPercentile: 0.9,
|
|
7
|
+
cannyLowRatio: 0.45,
|
|
8
|
+
cannyMinHigh: 12,
|
|
9
|
+
capturedJpegQuality: 0.97,
|
|
10
|
+
closeRadii: [1, 2, 4],
|
|
9
11
|
cornerNudge: 0.005,
|
|
10
12
|
cornerNudgeLarge: 0.025,
|
|
11
|
-
|
|
13
|
+
decodeMaxPixels: 16000000,
|
|
12
14
|
defaultFileName: 'scan',
|
|
13
|
-
defaultJpegQuality: 0.
|
|
14
|
-
defaultMaxDimension:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
defaultJpegQuality: 0.92,
|
|
16
|
+
defaultMaxDimension: 4000,
|
|
17
|
+
detectBorderMargin: 3,
|
|
18
|
+
detectExpand: 0.3,
|
|
19
|
+
detectMaxAngle: 140,
|
|
20
|
+
detectMaxAreaRatio: 0.95,
|
|
21
|
+
detectMaxDimension: 480,
|
|
22
|
+
detectMinAngle: 40,
|
|
23
|
+
detectMinAreaRatio: 0.1,
|
|
24
|
+
detectMinQuadFill: 0.88,
|
|
25
|
+
detectMinSideRatio: 0.06,
|
|
26
|
+
guideArm: 36,
|
|
27
|
+
guideArmRatio: 0.18,
|
|
28
|
+
guideAspect: 0.72,
|
|
29
|
+
guideHeightRatio: 0.58,
|
|
30
|
+
guideLift: 0.03,
|
|
31
|
+
guideWidthRatio: 0.76,
|
|
32
|
+
liveDeadzone: 0.006,
|
|
33
|
+
liveDetectIntervalMs: 200,
|
|
34
|
+
liveDetectMaxDimension: 280,
|
|
35
|
+
liveMissLimit: 3,
|
|
36
|
+
liveSmoothing: 0.45,
|
|
37
|
+
loupeOffset: 72,
|
|
38
|
+
loupeSize: 112,
|
|
39
|
+
loupeZoom: 2.5,
|
|
27
40
|
minCornerGap: 0.02,
|
|
28
|
-
|
|
29
|
-
pdfPointsPerInch: 72,
|
|
30
|
-
previewDebounceMs: 120,
|
|
31
|
-
previewMaxDimension: 1400,
|
|
32
|
-
thumbnailHeight: 56,
|
|
41
|
+
polygonEpsilons: [0.008, 0.012, 0.018, 0.026, 0.036, 0.05, 0.07],
|
|
33
42
|
torchConstraint: 'torch',
|
|
43
|
+
warpChunkPixels: 400000,
|
|
34
44
|
};
|
|
@@ -1,134 +1,112 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, 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 {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { usePsScanLiveDetection } from '../hooks/use-ps-scan-live-detection';
|
|
12
|
+
import { usePsScanPhoto } from '../hooks/use-ps-scan-photo';
|
|
13
|
+
import { PsScanFileNameService } from '../services/ps-scan-file-name-service';
|
|
14
14
|
import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
|
|
15
|
-
import { PsScanTemplateService } from '../services/ps-scan-template-service';
|
|
16
15
|
import { PsScanCameraView } from './ps-scan-camera-view';
|
|
17
16
|
import { PsScanCropEditor } from './ps-scan-crop-editor';
|
|
18
17
|
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
19
|
-
import {
|
|
20
|
-
|
|
18
|
+
import { PsScanPermissionView } from './ps-scan-permission-view';
|
|
19
|
+
import { PsScanPreview } from './ps-scan-preview';
|
|
20
|
+
export const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }) => {
|
|
21
21
|
const tier = usePsViewport();
|
|
22
22
|
const zIndex = usePsLayerZIndex(PallasoftZIndex.Modal);
|
|
23
23
|
const content = useRef(null);
|
|
24
|
-
const {
|
|
24
|
+
const { clear, detectAgain, load, photo, setCorners } = usePsScanPhoto();
|
|
25
25
|
const [step, setStep] = useState(PsScanStep.Camera);
|
|
26
|
-
const [
|
|
27
|
-
const [retakeId, setRetakeId] = useState('');
|
|
26
|
+
const [result, setResult] = useState(null);
|
|
28
27
|
const [announcement, setAnnouncement] = useState('');
|
|
29
28
|
const [error, setError] = useState('');
|
|
30
|
-
const [
|
|
29
|
+
const [isCapturing, setIsCapturing] = useState(false);
|
|
30
|
+
const [flashKey, setFlashKey] = useState(0);
|
|
31
31
|
const camera = usePsScanCamera(step === PsScanStep.Camera);
|
|
32
|
-
const
|
|
33
|
-
const page = pages.find((item) => item.id === activeId);
|
|
32
|
+
const isReady = camera.status === PsScanCameraStatus.Ready;
|
|
34
33
|
const isDesktop = tier === PallasoftViewportTier.Desktop;
|
|
35
|
-
|
|
34
|
+
const isCameraStep = step === PsScanStep.Camera;
|
|
35
|
+
const showsCamera = isReady || camera.status === PsScanCameraStatus.Starting;
|
|
36
|
+
const detection = usePsScanLiveDetection(camera.videoRef, isCameraStep && isReady);
|
|
37
|
+
usePsScrollLock(true);
|
|
36
38
|
usePsRestoreFocus(true);
|
|
37
39
|
usePsFocusTrap(content, true, onClose);
|
|
40
|
+
useEffect(() => () => {
|
|
41
|
+
if (result !== null)
|
|
42
|
+
URL.revokeObjectURL(result.url);
|
|
43
|
+
}, [result]);
|
|
38
44
|
const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
|
|
39
|
-
const
|
|
45
|
+
const accept = async (source) => {
|
|
40
46
|
setError('');
|
|
41
47
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
setActiveId(retakeId);
|
|
46
|
-
setRetakeId('');
|
|
47
|
-
setStep(PsScanStep.Crop);
|
|
48
|
-
setAnnouncement(edgeMessage(added.isDetected));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const room = Math.max(0, maxPages - pages.length);
|
|
52
|
-
if (images.length > room)
|
|
53
|
-
setError(labels.maxPages);
|
|
54
|
-
const added = await add(images.slice(0, room));
|
|
55
|
-
if (added.length === 0)
|
|
56
|
-
return;
|
|
57
|
-
setActiveId(added[0].page.id);
|
|
58
|
-
setStep(added.length === 1 ? PsScanStep.Crop : PsScanStep.Review);
|
|
59
|
-
setAnnouncement(added.length === 1
|
|
60
|
-
? edgeMessage(added[0].isDetected)
|
|
61
|
-
: PsScanTemplateService.fill(labels.pageAdded, { position: pages.length + added.length }));
|
|
48
|
+
const added = await load(source);
|
|
49
|
+
setAnnouncement(edgeMessage(added.isDetected));
|
|
50
|
+
setStep(PsScanStep.Crop);
|
|
62
51
|
}
|
|
63
52
|
catch {
|
|
64
53
|
setError(labels.processFailed);
|
|
65
54
|
}
|
|
66
55
|
};
|
|
67
56
|
const capture = async () => {
|
|
68
|
-
|
|
69
|
-
if (blob !== null)
|
|
70
|
-
await addFiles([blob]);
|
|
71
|
-
};
|
|
72
|
-
const autoDetect = async () => {
|
|
73
|
-
if (page === undefined)
|
|
57
|
+
if (isCapturing)
|
|
74
58
|
return;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
59
|
+
setIsCapturing(true);
|
|
60
|
+
try {
|
|
61
|
+
const blob = await camera.capture();
|
|
62
|
+
if (blob === null)
|
|
63
|
+
return;
|
|
64
|
+
setFlashKey((current) => current + 1);
|
|
65
|
+
await accept(blob);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
setIsCapturing(false);
|
|
69
|
+
}
|
|
78
70
|
};
|
|
71
|
+
const autoDetect = async () => setAnnouncement(edgeMessage(await detectAgain()));
|
|
79
72
|
const retake = () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
clear();
|
|
74
|
+
setResult(null);
|
|
75
|
+
setError('');
|
|
83
76
|
setStep(PsScanStep.Camera);
|
|
84
77
|
};
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const rest = pages.filter((item) => item.id !== page.id);
|
|
90
|
-
remove(page.id);
|
|
91
|
-
setAnnouncement(labels.pageDeleted);
|
|
92
|
-
if (rest.length === 0) {
|
|
93
|
-
setActiveId('');
|
|
94
|
-
setStep(PsScanStep.Camera);
|
|
95
|
-
}
|
|
96
|
-
else
|
|
97
|
-
setActiveId(rest[Math.min(index, rest.length - 1)].id);
|
|
78
|
+
const adjust = () => {
|
|
79
|
+
setResult(null);
|
|
80
|
+
setError('');
|
|
81
|
+
setStep(PsScanStep.Crop);
|
|
98
82
|
};
|
|
99
|
-
const
|
|
100
|
-
|
|
83
|
+
const flatten = async () => {
|
|
84
|
+
if (photo === null)
|
|
85
|
+
return;
|
|
86
|
+
setResult(null);
|
|
101
87
|
setError('');
|
|
88
|
+
setStep(PsScanStep.Preview);
|
|
102
89
|
try {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
format: outputFormat,
|
|
106
|
-
jpegQuality,
|
|
107
|
-
maxDimension,
|
|
108
|
-
});
|
|
109
|
-
onComplete(files);
|
|
110
|
-
onClose();
|
|
90
|
+
const rendered = await PsScanPipelineService.render(photo.source, photo.corners, maxDimension, jpegQuality);
|
|
91
|
+
setResult({ ...rendered, url: URL.createObjectURL(rendered.blob) });
|
|
111
92
|
}
|
|
112
93
|
catch {
|
|
113
94
|
setError(labels.processFailed);
|
|
114
|
-
setIsBusy(false);
|
|
115
95
|
}
|
|
116
96
|
};
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
const body = (_jsxs("div", { className: 'relative h-full min-h-0 bg-surface text-ink', ref: content, children: [_jsx("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), step === PsScanStep.Camera ? (_jsx(PsScanCameraView, { canGoBack: pages.length > 0, canTorch: camera.canTorch, isTorchOn: camera.isTorchOn, labels: labels, onBack: () => {
|
|
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 }) }));
|
|
97
|
+
const use = () => {
|
|
98
|
+
if (result === null)
|
|
99
|
+
return;
|
|
100
|
+
onComplete([
|
|
101
|
+
new File([result.blob], PsScanFileNameService.build(fileName), { type: 'image/jpeg' }),
|
|
102
|
+
]);
|
|
103
|
+
onClose();
|
|
104
|
+
};
|
|
131
105
|
if (typeof document === 'undefined')
|
|
132
106
|
return null;
|
|
133
|
-
return createPortal(_jsx("div", { "aria-label": labels.title, "aria-modal": 'true', className:
|
|
107
|
+
return createPortal(_jsx("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
|
|
108
|
+
? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
|
|
109
|
+
: 'fixed inset-0 bg-black animate-ps-fade-in', role: 'dialog', style: { zIndex }, children: _jsx(PsLayer, { zIndex: zIndex, children: _jsxs("div", { className: isDesktop
|
|
110
|
+
? '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'
|
|
111
|
+
: '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, 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 ? (_jsxs(_Fragment, { children: [_jsx(PsScanPermissionView, { labels: labels, onAllow: camera.allow, onFile: accept, 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 }) })] })) : null, step === PsScanStep.Crop && photo !== null ? (_jsx(PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: setCorners, onContinue: flatten, onRetake: retake, photo: photo })) : null, step === PsScanStep.Preview ? (_jsx(PsScanPreview, { error: error, labels: labels, onAdjust: adjust, onRetake: retake, onUse: use, result: result })) : 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
112
|
};
|
|
@@ -3,11 +3,10 @@ import { Suspense } from 'react';
|
|
|
3
3
|
import { usePsHydrated } from '@pallasoft/ps-core-components';
|
|
4
4
|
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
5
5
|
import { PsScanConfig } from '../constants/ps-scan-config';
|
|
6
|
-
import { PsScanOutputFormat } from '../constants/ps-scan-output-format';
|
|
7
6
|
import { PsDocumentScannerLoader } from './ps-document-scanner-loader';
|
|
8
|
-
export const PsDocumentScanner = ({ fileName = PsScanConfig.defaultFileName, isOpen, jpegQuality = PsScanConfig.defaultJpegQuality, labels, maxDimension = PsScanConfig.defaultMaxDimension,
|
|
7
|
+
export const PsDocumentScanner = ({ fileName = PsScanConfig.defaultFileName, isOpen, jpegQuality = PsScanConfig.defaultJpegQuality, labels, maxDimension = PsScanConfig.defaultMaxDimension, onClose, onComplete, }) => {
|
|
9
8
|
const isHydrated = usePsHydrated();
|
|
10
9
|
if (!isOpen || !isHydrated)
|
|
11
10
|
return null;
|
|
12
|
-
return (_jsx(Suspense, { fallback: null, children: _jsx(PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension,
|
|
11
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, onClose: onClose, onComplete: onComplete }) }));
|
|
13
12
|
};
|
|
@@ -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,21 @@
|
|
|
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';
|
|
6
7
|
import { PsScanIconButton } from './ps-scan-icon-button';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
event.target.value = '';
|
|
19
|
-
if (files.length > 0)
|
|
20
|
-
onFiles(files);
|
|
8
|
+
import { PsScanPhotoButton } from './ps-scan-photo-button';
|
|
9
|
+
import { PsScanShutter } from './ps-scan-shutter';
|
|
10
|
+
import { PsScanViewfinderOverlay } from './ps-scan-viewfinder-overlay';
|
|
11
|
+
export const PsScanCameraView = ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }) => {
|
|
12
|
+
const [areaRef, area] = usePsElementSize();
|
|
13
|
+
const [video, setVideo] = useState({ height: 0, width: 0 });
|
|
14
|
+
const hint = detection.quad === null ? labels.hintFind : labels.hintFound;
|
|
15
|
+
const measure = () => {
|
|
16
|
+
const element = videoRef.current;
|
|
17
|
+
if (element !== null)
|
|
18
|
+
setVideo({ height: element.videoHeight, width: element.videoWidth });
|
|
21
19
|
};
|
|
22
|
-
return (_jsxs("div", { className: '
|
|
20
|
+
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 }), _jsx("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 })] }), 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: _jsx(PsScanPhotoButton, { isIconOnly: true, label: labels.choosePhoto, onFile: onFile }) }), _jsx(PsScanShutter, { disabled: !isReady || isBusy, label: labels.capture, onCapture: onCapture }), _jsx("span", { "aria-hidden": 'true' })] })] }));
|
|
23
21
|
};
|