@pallasoft/ps-file-components 1.3.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 -0
- package/dist/components/ps-document-scanner-loader.js +40 -0
- package/dist/components/ps-document-scanner-sheet.d.ts +2 -0
- package/dist/components/ps-document-scanner-sheet.js +186 -0
- package/dist/components/ps-document-scanner.d.ts +2 -0
- package/dist/components/ps-document-scanner.js +17 -0
- 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-file-viewer-pane.d.ts +2 -0
- package/dist/components/ps-file-viewer-pane.js +39 -0
- 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 +27 -0
- package/dist/components/ps-scan-camera-view.js +34 -0
- package/dist/components/ps-scan-crop-editor.d.ts +13 -0
- package/dist/components/ps-scan-crop-editor.js +69 -0
- 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 +11 -0
- package/dist/components/ps-scan-icon-button.js +9 -0
- 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 +14 -0
- package/dist/components/ps-scan-page-list.js +22 -0
- 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 +24 -0
- package/dist/components/ps-scan-review.js +37 -0
- 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 +80 -0
- package/dist/constants/ps-document-scanner-labels.js +83 -0
- 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 +11 -0
- package/dist/constants/ps-scan-camera-status.js +13 -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 +48 -0
- package/dist/constants/ps-scan-config.js +51 -0
- package/dist/constants/ps-scan-corner-order.d.ts +1 -0
- package/dist/constants/ps-scan-corner-order.js +4 -0
- package/dist/constants/ps-scan-filter.d.ts +7 -0
- package/dist/constants/ps-scan-filter.js +9 -0
- package/dist/constants/ps-scan-output-format.d.ts +5 -0
- package/dist/constants/ps-scan-output-format.js +7 -0
- 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 +6 -0
- package/dist/constants/ps-scan-step.js +8 -0
- package/dist/esm/components/ps-document-scanner-loader.js +4 -0
- package/dist/esm/components/ps-document-scanner-sheet.js +182 -0
- package/dist/esm/components/ps-document-scanner.js +13 -0
- 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-file-viewer-pane.js +35 -0
- package/dist/esm/components/ps-scan-button.js +5 -0
- package/dist/esm/components/ps-scan-camera-view.js +30 -0
- package/dist/esm/components/ps-scan-crop-editor.js +65 -0
- package/dist/esm/components/ps-scan-denied-steps.js +29 -0
- package/dist/esm/components/ps-scan-icon-button.js +5 -0
- 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 +18 -0
- 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 +33 -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 +80 -0
- package/dist/esm/constants/ps-file-picker-labels.js +5 -3
- package/dist/esm/constants/ps-scan-camera-status.js +10 -0
- package/dist/esm/constants/ps-scan-chrome-classes.js +12 -0
- package/dist/esm/constants/ps-scan-config.js +48 -0
- package/dist/esm/constants/ps-scan-corner-order.js +1 -0
- package/dist/esm/constants/ps-scan-filter.js +6 -0
- package/dist/esm/constants/ps-scan-output-format.js +4 -0
- package/dist/esm/constants/ps-scan-platform.js +5 -0
- package/dist/esm/constants/ps-scan-step.js +5 -0
- package/dist/esm/hooks/use-ps-focus-trap.js +45 -0
- package/dist/esm/hooks/use-ps-scan-camera.js +99 -0
- package/dist/esm/hooks/use-ps-scan-live-detection.js +45 -0
- package/dist/esm/hooks/use-ps-scan-pages.js +62 -0
- package/dist/esm/hooks/use-ps-scan-previews.js +57 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/models/ps-document-scanner-props.js +1 -0
- package/dist/esm/models/ps-document-scanner-sheet-props.js +1 -0
- package/dist/esm/models/ps-file-picker-scan.js +1 -0
- package/dist/esm/models/ps-file-viewer-pane-props.js +1 -0
- package/dist/esm/models/ps-scan-added-page.js +1 -0
- package/dist/esm/models/ps-scan-camera-session.js +1 -0
- package/dist/esm/models/ps-scan-decoded-image.js +1 -0
- package/dist/esm/models/ps-scan-levels.js +1 -0
- package/dist/esm/models/ps-scan-live-detection.js +1 -0
- package/dist/esm/models/ps-scan-output-options.js +1 -0
- package/dist/esm/models/ps-scan-page.js +1 -0
- package/dist/esm/models/ps-scan-pdf-page.js +1 -0
- package/dist/esm/models/ps-scan-prepared.js +1 -0
- package/dist/esm/models/ps-scan-quad.js +1 -0
- package/dist/esm/models/ps-scan-raster.js +1 -0
- package/dist/esm/models/ps-scan-rendered-page.js +1 -0
- package/dist/esm/models/ps-scan-stability.js +1 -0
- package/dist/esm/services/ps-scan-camera-service.js +95 -0
- package/dist/esm/services/ps-scan-canvas-service.js +52 -0
- package/dist/esm/services/ps-scan-edge-service.js +156 -0
- package/dist/esm/services/ps-scan-enhance-service.js +97 -0
- package/dist/esm/services/ps-scan-file-name-service.js +25 -0
- package/dist/esm/services/ps-scan-geometry-service.js +116 -0
- package/dist/esm/services/ps-scan-homography-service.js +46 -0
- package/dist/esm/services/ps-scan-hull-service.js +59 -0
- package/dist/esm/services/ps-scan-image-filter-service.js +52 -0
- package/dist/esm/services/ps-scan-order-service.js +13 -0
- package/dist/esm/services/ps-scan-output-service.js +24 -0
- package/dist/esm/services/ps-scan-pdf-service.js +56 -0
- package/dist/esm/services/ps-scan-pipeline-service.js +40 -0
- package/dist/esm/services/ps-scan-platform-service.js +15 -0
- package/dist/esm/services/ps-scan-raster-service.js +67 -0
- package/dist/esm/services/ps-scan-stability-service.js +41 -0
- package/dist/esm/services/ps-scan-template-service.js +5 -0
- package/dist/esm/services/ps-scan-warp-service.js +35 -0
- package/dist/hooks/use-ps-focus-trap.d.ts +2 -0
- package/dist/hooks/use-ps-focus-trap.js +49 -0
- package/dist/hooks/use-ps-scan-camera.d.ts +11 -0
- package/dist/hooks/use-ps-scan-camera.js +103 -0
- 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/hooks/use-ps-scan-pages.d.ts +12 -0
- package/dist/hooks/use-ps-scan-pages.js +66 -0
- package/dist/hooks/use-ps-scan-previews.d.ts +2 -0
- package/dist/hooks/use-ps-scan-previews.js +61 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/models/ps-document-scanner-props.d.ts +16 -0
- package/dist/models/ps-document-scanner-props.js +2 -0
- package/dist/models/ps-document-scanner-sheet-props.d.ts +14 -0
- package/dist/models/ps-document-scanner-sheet-props.js +2 -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-file-viewer-pane-props.d.ts +12 -0
- package/dist/models/ps-file-viewer-pane-props.js +2 -0
- package/dist/models/ps-scan-added-page.d.ts +5 -0
- package/dist/models/ps-scan-added-page.js +2 -0
- package/dist/models/ps-scan-camera-session.d.ts +5 -0
- package/dist/models/ps-scan-camera-session.js +2 -0
- package/dist/models/ps-scan-decoded-image.d.ts +6 -0
- package/dist/models/ps-scan-decoded-image.js +2 -0
- package/dist/models/ps-scan-levels.d.ts +4 -0
- package/dist/models/ps-scan-levels.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-output-options.d.ts +7 -0
- package/dist/models/ps-scan-output-options.js +2 -0
- package/dist/models/ps-scan-page.d.ts +12 -0
- package/dist/models/ps-scan-page.js +2 -0
- package/dist/models/ps-scan-pdf-page.d.ts +5 -0
- package/dist/models/ps-scan-pdf-page.js +2 -0
- package/dist/models/ps-scan-prepared.d.ts +7 -0
- package/dist/models/ps-scan-prepared.js +2 -0
- package/dist/models/ps-scan-quad.d.ts +2 -0
- package/dist/models/ps-scan-quad.js +2 -0
- package/dist/models/ps-scan-raster.d.ts +5 -0
- package/dist/models/ps-scan-raster.js +2 -0
- package/dist/models/ps-scan-rendered-page.d.ts +5 -0
- package/dist/models/ps-scan-rendered-page.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 +14 -0
- package/dist/services/ps-scan-camera-service.js +99 -0
- package/dist/services/ps-scan-canvas-service.d.ts +9 -0
- package/dist/services/ps-scan-canvas-service.js +56 -0
- package/dist/services/ps-scan-edge-service.d.ts +9 -0
- package/dist/services/ps-scan-edge-service.js +160 -0
- package/dist/services/ps-scan-enhance-service.d.ts +9 -0
- package/dist/services/ps-scan-enhance-service.js +101 -0
- package/dist/services/ps-scan-file-name-service.d.ts +6 -0
- package/dist/services/ps-scan-file-name-service.js +29 -0
- package/dist/services/ps-scan-geometry-service.d.ts +20 -0
- package/dist/services/ps-scan-geometry-service.js +120 -0
- package/dist/services/ps-scan-homography-service.d.ts +7 -0
- package/dist/services/ps-scan-homography-service.js +50 -0
- package/dist/services/ps-scan-hull-service.d.ts +6 -0
- package/dist/services/ps-scan-hull-service.js +63 -0
- package/dist/services/ps-scan-image-filter-service.d.ts +6 -0
- package/dist/services/ps-scan-image-filter-service.js +56 -0
- package/dist/services/ps-scan-order-service.d.ts +4 -0
- package/dist/services/ps-scan-order-service.js +17 -0
- package/dist/services/ps-scan-output-service.d.ts +5 -0
- package/dist/services/ps-scan-output-service.js +28 -0
- package/dist/services/ps-scan-pdf-service.d.ts +5 -0
- package/dist/services/ps-scan-pdf-service.js +60 -0
- package/dist/services/ps-scan-pipeline-service.d.ts +10 -0
- package/dist/services/ps-scan-pipeline-service.js +44 -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-raster-service.d.ts +6 -0
- package/dist/services/ps-scan-raster-service.js +71 -0
- package/dist/services/ps-scan-stability-service.d.ts +9 -0
- package/dist/services/ps-scan-stability-service.js +45 -0
- package/dist/services/ps-scan-template-service.d.ts +3 -0
- package/dist/services/ps-scan-template-service.js +9 -0
- package/dist/services/ps-scan-warp-service.d.ts +6 -0
- package/dist/services/ps-scan-warp-service.js +39 -0
- package/package.json +9 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsDocumentScannerLoader: import("react").LazyExoticComponent<({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }: import("../models/ps-document-scanner-sheet-props").PsDocumentScannerSheetProps) => import("react").ReactPortal | null>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PsDocumentScannerLoader = void 0;
|
|
37
|
+
const react_1 = require("react");
|
|
38
|
+
exports.PsDocumentScannerLoader = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./ps-document-scanner-sheet'))).then((module) => ({
|
|
39
|
+
default: module.PsDocumentScannerSheet,
|
|
40
|
+
})));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { PsDocumentScannerSheetProps } from '../models/ps-document-scanner-sheet-props';
|
|
2
|
+
export declare const PsDocumentScannerSheet: ({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsDocumentScannerSheet = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
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");
|
|
9
|
+
const react_dom_1 = require("react-dom");
|
|
10
|
+
const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
|
|
11
|
+
const ps_scan_step_1 = require("../constants/ps-scan-step");
|
|
12
|
+
const use_ps_focus_trap_1 = require("../hooks/use-ps-focus-trap");
|
|
13
|
+
const use_ps_scan_camera_1 = require("../hooks/use-ps-scan-camera");
|
|
14
|
+
const use_ps_scan_live_detection_1 = require("../hooks/use-ps-scan-live-detection");
|
|
15
|
+
const use_ps_scan_pages_1 = require("../hooks/use-ps-scan-pages");
|
|
16
|
+
const use_ps_scan_previews_1 = require("../hooks/use-ps-scan-previews");
|
|
17
|
+
const ps_scan_output_service_1 = require("../services/ps-scan-output-service");
|
|
18
|
+
const ps_scan_pipeline_service_1 = require("../services/ps-scan-pipeline-service");
|
|
19
|
+
const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
|
|
20
|
+
const ps_scan_camera_view_1 = require("./ps-scan-camera-view");
|
|
21
|
+
const ps_scan_crop_editor_1 = require("./ps-scan-crop-editor");
|
|
22
|
+
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
23
|
+
const ps_scan_permission_view_1 = require("./ps-scan-permission-view");
|
|
24
|
+
const ps_scan_review_1 = require("./ps-scan-review");
|
|
25
|
+
const PsDocumentScannerSheet = ({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }) => {
|
|
26
|
+
const tier = (0, ps_core_components_1.usePsViewport)();
|
|
27
|
+
const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
|
|
28
|
+
const content = (0, react_1.useRef)(null);
|
|
29
|
+
const { add, move, pages, remove, replace, setCorners, update } = (0, use_ps_scan_pages_1.usePsScanPages)();
|
|
30
|
+
const [step, setStep] = (0, react_1.useState)(ps_scan_step_1.PsScanStep.Camera);
|
|
31
|
+
const [cropReturn, setCropReturn] = (0, react_1.useState)(ps_scan_step_1.PsScanStep.Camera);
|
|
32
|
+
const [activeId, setActiveId] = (0, react_1.useState)('');
|
|
33
|
+
const [retakeId, setRetakeId] = (0, react_1.useState)('');
|
|
34
|
+
const [announcement, setAnnouncement] = (0, react_1.useState)('');
|
|
35
|
+
const [error, setError] = (0, react_1.useState)('');
|
|
36
|
+
const [isBusy, setIsBusy] = (0, react_1.useState)(false);
|
|
37
|
+
const [isCapturing, setIsCapturing] = (0, react_1.useState)(false);
|
|
38
|
+
const [isAuto, setIsAuto] = (0, react_1.useState)(isAutoCapture);
|
|
39
|
+
const [flashKey, setFlashKey] = (0, react_1.useState)(0);
|
|
40
|
+
const camera = (0, use_ps_scan_camera_1.usePsScanCamera)(step === ps_scan_step_1.PsScanStep.Camera);
|
|
41
|
+
const isReady = camera.status === ps_scan_camera_status_1.PsScanCameraStatus.Ready;
|
|
42
|
+
const isFull = pages.length >= maxPages;
|
|
43
|
+
const previews = (0, use_ps_scan_previews_1.usePsScanPreviews)(pages);
|
|
44
|
+
const page = pages.find((item) => item.id === activeId);
|
|
45
|
+
const lastPage = pages[pages.length - 1];
|
|
46
|
+
const isDesktop = tier === ps_design_system_1.PallasoftViewportTier.Desktop;
|
|
47
|
+
const isCameraStep = step === ps_scan_step_1.PsScanStep.Camera;
|
|
48
|
+
const showsCamera = isReady || camera.status === ps_scan_camera_status_1.PsScanCameraStatus.Starting;
|
|
49
|
+
(0, ps_core_components_1.usePsScrollLock)(true);
|
|
50
|
+
(0, ps_core_components_1.usePsRestoreFocus)(true);
|
|
51
|
+
(0, use_ps_focus_trap_1.usePsFocusTrap)(content, true, onClose);
|
|
52
|
+
const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
|
|
53
|
+
const addFiles = async (files, after) => {
|
|
54
|
+
setError('');
|
|
55
|
+
try {
|
|
56
|
+
const images = files.filter((file) => file.type === '' || file.type.startsWith('image/'));
|
|
57
|
+
if (retakeId !== '' && images.length > 0) {
|
|
58
|
+
const added = await replace(retakeId, images[0]);
|
|
59
|
+
setActiveId(retakeId);
|
|
60
|
+
setRetakeId('');
|
|
61
|
+
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
62
|
+
setAnnouncement(edgeMessage(added.isDetected));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const room = Math.max(0, maxPages - pages.length);
|
|
66
|
+
if (images.length > room)
|
|
67
|
+
setError(labels.maxPages);
|
|
68
|
+
const added = await add(images.slice(0, room));
|
|
69
|
+
if (added.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
const position = pages.length + added.length;
|
|
72
|
+
setActiveId(added[added.length - 1].page.id);
|
|
73
|
+
setAnnouncement(ps_scan_template_service_1.PsScanTemplateService.fill(labels.pageAdded, { position }));
|
|
74
|
+
if (after === null)
|
|
75
|
+
return;
|
|
76
|
+
if (added.length === 1) {
|
|
77
|
+
setCropReturn(after);
|
|
78
|
+
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
79
|
+
setAnnouncement(edgeMessage(added[0].isDetected));
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
setStep(ps_scan_step_1.PsScanStep.Review);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
setError(labels.processFailed);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const capture = async (isAutomatic) => {
|
|
89
|
+
if (isCapturing || isFull)
|
|
90
|
+
return;
|
|
91
|
+
setIsCapturing(true);
|
|
92
|
+
try {
|
|
93
|
+
const blob = await camera.capture();
|
|
94
|
+
if (blob === null)
|
|
95
|
+
return;
|
|
96
|
+
setFlashKey((current) => current + 1);
|
|
97
|
+
await addFiles([blob], isAutomatic && retakeId === '' ? null : ps_scan_step_1.PsScanStep.Camera);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
setIsCapturing(false);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const detection = (0, use_ps_scan_live_detection_1.usePsScanLiveDetection)(camera.videoRef, isCameraStep && isReady, isAuto, () => capture(true));
|
|
104
|
+
const autoDetect = async () => {
|
|
105
|
+
if (page === undefined)
|
|
106
|
+
return;
|
|
107
|
+
const prepared = await ps_scan_pipeline_service_1.PsScanPipelineService.prepare(page.source);
|
|
108
|
+
setCorners(page.id, prepared.corners);
|
|
109
|
+
setAnnouncement(edgeMessage(prepared.isDetected));
|
|
110
|
+
};
|
|
111
|
+
const keep = () => setStep(cropReturn === ps_scan_step_1.PsScanStep.Camera && !isFull ? ps_scan_step_1.PsScanStep.Camera : ps_scan_step_1.PsScanStep.Review);
|
|
112
|
+
const retake = () => {
|
|
113
|
+
if (page === undefined)
|
|
114
|
+
return;
|
|
115
|
+
if (step === ps_scan_step_1.PsScanStep.Review)
|
|
116
|
+
setCropReturn(ps_scan_step_1.PsScanStep.Review);
|
|
117
|
+
setRetakeId(page.id);
|
|
118
|
+
setStep(ps_scan_step_1.PsScanStep.Camera);
|
|
119
|
+
};
|
|
120
|
+
const openCamera = () => {
|
|
121
|
+
setRetakeId('');
|
|
122
|
+
setCropReturn(ps_scan_step_1.PsScanStep.Camera);
|
|
123
|
+
setStep(ps_scan_step_1.PsScanStep.Camera);
|
|
124
|
+
};
|
|
125
|
+
const openReview = () => {
|
|
126
|
+
if (pages.length === 0)
|
|
127
|
+
return;
|
|
128
|
+
setRetakeId('');
|
|
129
|
+
if (page === undefined)
|
|
130
|
+
setActiveId(lastPage.id);
|
|
131
|
+
setStep(ps_scan_step_1.PsScanStep.Review);
|
|
132
|
+
};
|
|
133
|
+
const editCrop = () => {
|
|
134
|
+
setCropReturn(ps_scan_step_1.PsScanStep.Review);
|
|
135
|
+
setStep(ps_scan_step_1.PsScanStep.Crop);
|
|
136
|
+
};
|
|
137
|
+
const deletePage = () => {
|
|
138
|
+
if (page === undefined)
|
|
139
|
+
return;
|
|
140
|
+
const index = pages.findIndex((item) => item.id === page.id);
|
|
141
|
+
const rest = pages.filter((item) => item.id !== page.id);
|
|
142
|
+
remove(page.id);
|
|
143
|
+
setAnnouncement(labels.pageDeleted);
|
|
144
|
+
if (rest.length === 0) {
|
|
145
|
+
setActiveId('');
|
|
146
|
+
openCamera();
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
setActiveId(rest[Math.min(index, rest.length - 1)].id);
|
|
150
|
+
};
|
|
151
|
+
const finish = async () => {
|
|
152
|
+
setIsBusy(true);
|
|
153
|
+
setError('');
|
|
154
|
+
try {
|
|
155
|
+
const files = await ps_scan_output_service_1.PsScanOutputService.build(pages, {
|
|
156
|
+
fileName,
|
|
157
|
+
format: outputFormat,
|
|
158
|
+
jpegQuality,
|
|
159
|
+
maxDimension,
|
|
160
|
+
});
|
|
161
|
+
onComplete(files);
|
|
162
|
+
onClose();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
setError(labels.processFailed);
|
|
166
|
+
setIsBusy(false);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const moveTo = (0, react_1.useCallback)((from, to) => {
|
|
170
|
+
move(from, to);
|
|
171
|
+
setAnnouncement(ps_scan_template_service_1.PsScanTemplateService.fill(labels.pageMoved, {
|
|
172
|
+
count: pages.length,
|
|
173
|
+
page: `${labels.page} ${from + 1}`,
|
|
174
|
+
position: to + 1,
|
|
175
|
+
}));
|
|
176
|
+
}, [move, pages.length, labels]);
|
|
177
|
+
const fromLibrary = (files) => addFiles(files, ps_scan_step_1.PsScanStep.Review);
|
|
178
|
+
if (typeof document === 'undefined')
|
|
179
|
+
return null;
|
|
180
|
+
return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
|
|
181
|
+
? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
|
|
182
|
+
: '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
|
|
183
|
+
? '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'
|
|
184
|
+
: '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, 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 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_scan_permission_view_1.PsScanPermissionView, { isMultiple: maxPages > 1, labels: labels, onAllow: camera.allow, onFiles: fromLibrary, 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 }) }), pages.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'absolute right-4 top-[max(1rem,env(safe-area-inset-top))]', children: (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Check, label: ps_scan_template_service_1.PsScanTemplateService.fill(labels.viewPages, { count: pages.length }), onClick: openReview }) })) : null] })) : null, step === ps_scan_step_1.PsScanStep.Crop && page !== undefined ? ((0, jsx_runtime_1.jsx)(ps_scan_crop_editor_1.PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: (corners) => setCorners(page.id, corners), onKeep: keep, 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: !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 !== '' ? ((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);
|
|
185
|
+
};
|
|
186
|
+
exports.PsDocumentScannerSheet = PsDocumentScannerSheet;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { PsDocumentScannerProps } from '../models/ps-document-scanner-props';
|
|
2
|
+
export declare const PsDocumentScanner: ({ fileName, isAutoCapture, isOpen, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsDocumentScanner = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_core_components_1 = require("@pallasoft/ps-core-components");
|
|
7
|
+
const ps_document_scanner_labels_1 = require("../constants/ps-document-scanner-labels");
|
|
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
|
+
const ps_document_scanner_loader_1 = require("./ps-document-scanner-loader");
|
|
11
|
+
const PsDocumentScanner = ({ fileName = ps_scan_config_1.PsScanConfig.defaultFileName, isAutoCapture = false, isOpen, jpegQuality = ps_scan_config_1.PsScanConfig.defaultJpegQuality, labels, maxDimension = ps_scan_config_1.PsScanConfig.defaultMaxDimension, maxPages = ps_scan_config_1.PsScanConfig.defaultMaxPages, onClose, onComplete, outputFormat = ps_scan_output_format_1.PsScanOutputFormat.Pdf, }) => {
|
|
12
|
+
const isHydrated = (0, ps_core_components_1.usePsHydrated)();
|
|
13
|
+
if (!isOpen || !isHydrated)
|
|
14
|
+
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, isAutoCapture: isAutoCapture, jpegQuality: jpegQuality, labels: { ...ps_document_scanner_labels_1.PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, maxPages: maxPages, onClose: onClose, onComplete: onComplete, outputFormat: outputFormat }) }));
|
|
16
|
+
};
|
|
17
|
+
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,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsFileViewerPane = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const ps_icons_1 = require("@pallasoft/ps-icons");
|
|
7
|
+
const ps_file_viewer_config_1 = require("../constants/ps-file-viewer-config");
|
|
8
|
+
const ps_file_viewer_kinds_1 = require("../constants/ps-file-viewer-kinds");
|
|
9
|
+
const ps_file_viewer_labels_1 = require("../constants/ps-file-viewer-labels");
|
|
10
|
+
const ps_file_type_service_1 = require("../services/ps-file-type-service");
|
|
11
|
+
const ps_file_viewer_button_1 = require("./ps-file-viewer-button");
|
|
12
|
+
const ps_file_viewer_content_1 = require("./ps-file-viewer-content");
|
|
13
|
+
const PsFileViewerPane = ({ contentType, headerActions, id, labels = ps_file_viewer_labels_1.PsFileViewerLabels, name, pdfWorkerSrc, url, }) => {
|
|
14
|
+
const [zoom, setZoom] = (0, react_1.useState)(1);
|
|
15
|
+
const [rotation, setRotation] = (0, react_1.useState)(0);
|
|
16
|
+
const kind = ps_file_type_service_1.PsFileTypeService.kindOf(contentType, name);
|
|
17
|
+
const canZoom = ps_file_viewer_kinds_1.PsFileViewerKinds.zoomable.includes(kind);
|
|
18
|
+
const canRotate = ps_file_viewer_kinds_1.PsFileViewerKinds.rotatable.includes(kind);
|
|
19
|
+
const changeZoom = (step) => setZoom((current) => Math.min(ps_file_viewer_config_1.PsFileViewerConfig.maxZoom, Math.max(ps_file_viewer_config_1.PsFileViewerConfig.minZoom, current + step)));
|
|
20
|
+
const fileKey = id === undefined ? url : id;
|
|
21
|
+
const rotate = () => setRotation((current) => (current + ps_file_viewer_config_1.PsFileViewerConfig.rotateStep) % 360);
|
|
22
|
+
const onKeyDown = (event) => {
|
|
23
|
+
if (event.metaKey || event.ctrlKey || event.altKey)
|
|
24
|
+
return;
|
|
25
|
+
if (canZoom && (event.key === '+' || event.key === '='))
|
|
26
|
+
changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep);
|
|
27
|
+
else if (canZoom && (event.key === '-' || event.key === '_'))
|
|
28
|
+
changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep);
|
|
29
|
+
else if (canZoom && event.key === '0')
|
|
30
|
+
setZoom(1);
|
|
31
|
+
else if (canRotate && event.key.toLowerCase() === 'r')
|
|
32
|
+
rotate();
|
|
33
|
+
else
|
|
34
|
+
return;
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
};
|
|
37
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-ps-panel bg-black/85', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 flex-wrap items-center justify-between gap-x-2 border-b border-white/10 px-2 py-1 text-white', children: [(0, jsx_runtime_1.jsx)("p", { className: 'min-w-0 flex-1 basis-32 truncate px-1 text-xs text-white/70', title: name, children: name }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-0.5', role: 'toolbar', children: [canZoom ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: zoom <= ps_file_viewer_config_1.PsFileViewerConfig.minZoom, label: labels.zoomOut, onClick: () => changeZoom(-ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.ZoomOut }) }), (0, jsx_runtime_1.jsxs)(ps_file_viewer_button_1.PsFileViewerButton, { className: 'w-14 text-xs', label: labels.zoomReset, onClick: () => setZoom(1), children: [Math.round(zoom * 100), "%"] }), (0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { disabled: zoom >= ps_file_viewer_config_1.PsFileViewerConfig.maxZoom, label: labels.zoomIn, onClick: () => changeZoom(ps_file_viewer_config_1.PsFileViewerConfig.zoomStep), children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.ZoomIn }) })] })) : null, canRotate ? ((0, jsx_runtime_1.jsx)(ps_file_viewer_button_1.PsFileViewerButton, { label: labels.rotate, onClick: rotate, children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Rotate }) })) : null, headerActions] })] }), (0, jsx_runtime_1.jsx)("div", { className: 'ps-focus-ring flex min-h-0 flex-1 p-2', onKeyDown: onKeyDown, tabIndex: 0, children: (0, jsx_runtime_1.jsx)(ps_file_viewer_content_1.PsFileViewerContent, { activeMatch: 0, file: { contentType, id: fileKey, name, url }, labels: labels, onMatchCount: () => undefined, pdfWorkerSrc: pdfWorkerSrc, query: '', rotation: rotation, zoom: zoom }, fileKey) })] }));
|
|
38
|
+
};
|
|
39
|
+
exports.PsFileViewerPane = PsFileViewerPane;
|
|
@@ -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;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type RefCallback, type RefObject } from 'react';
|
|
2
|
+
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
3
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
4
|
+
import type { PsScanLiveDetection } from '../models/ps-scan-live-detection';
|
|
5
|
+
export interface PsScanCameraViewProps {
|
|
6
|
+
canTorch: boolean;
|
|
7
|
+
detection: PsScanLiveDetection;
|
|
8
|
+
flashKey: number;
|
|
9
|
+
isAuto: boolean;
|
|
10
|
+
isBusy: boolean;
|
|
11
|
+
isFull: boolean;
|
|
12
|
+
isMultiple: boolean;
|
|
13
|
+
isReady: boolean;
|
|
14
|
+
isTorchOn: boolean;
|
|
15
|
+
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
16
|
+
lastPreview: string | undefined;
|
|
17
|
+
onCapture: () => void;
|
|
18
|
+
onClose: () => void;
|
|
19
|
+
onDone: () => void;
|
|
20
|
+
onFiles: (files: File[]) => void;
|
|
21
|
+
onToggleAuto: () => void;
|
|
22
|
+
onToggleTorch: () => void;
|
|
23
|
+
pageCount: number;
|
|
24
|
+
videoRef: RefObject<HTMLVideoElement | null>;
|
|
25
|
+
videoTarget: RefCallback<HTMLVideoElement>;
|
|
26
|
+
}
|
|
27
|
+
export declare const PsScanCameraView: ({ canTorch, detection, flashKey, isAuto, isBusy, isFull, isMultiple, isReady, isTorchOn, labels, lastPreview, onCapture, onClose, onDone, onFiles, onToggleAuto, onToggleTorch, pageCount, videoRef, videoTarget, }: PsScanCameraViewProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanCameraView = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
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");
|
|
9
|
+
const ps_scan_chrome_classes_1 = require("../constants/ps-scan-chrome-classes");
|
|
10
|
+
const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
|
|
11
|
+
const ps_scan_button_1 = require("./ps-scan-button");
|
|
12
|
+
const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
|
|
13
|
+
const ps_scan_page_stack_1 = require("./ps-scan-page-stack");
|
|
14
|
+
const ps_scan_photo_button_1 = require("./ps-scan-photo-button");
|
|
15
|
+
const ps_scan_shutter_1 = require("./ps-scan-shutter");
|
|
16
|
+
const ps_scan_viewfinder_overlay_1 = require("./ps-scan-viewfinder-overlay");
|
|
17
|
+
const PsScanCameraView = ({ canTorch, detection, flashKey, isAuto, isBusy, isFull, isMultiple, isReady, isTorchOn, labels, lastPreview, onCapture, onClose, onDone, onFiles, onToggleAuto, onToggleTorch, pageCount, videoRef, videoTarget, }) => {
|
|
18
|
+
const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
|
|
19
|
+
const [video, setVideo] = (0, react_1.useState)({ height: 0, width: 0 });
|
|
20
|
+
const hint = isFull
|
|
21
|
+
? labels.maxPages
|
|
22
|
+
: detection.quad === null
|
|
23
|
+
? labels.hintFind
|
|
24
|
+
: isAuto
|
|
25
|
+
? labels.hintHold
|
|
26
|
+
: labels.hintFound;
|
|
27
|
+
const measure = () => {
|
|
28
|
+
const element = videoRef.current;
|
|
29
|
+
if (element !== null)
|
|
30
|
+
setVideo({ height: element.videoHeight, width: element.videoWidth });
|
|
31
|
+
};
|
|
32
|
+
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.jsxs)("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, (0, jsx_runtime_1.jsx)("button", { "aria-label": labels.autoCapture, "aria-pressed": isAuto, className: ps_design_system_1.PallasoftClassNameService.merge(ps_scan_chrome_classes_1.PsScanChromeClasses.button, 'border-transparent', isAuto ? ps_scan_chrome_classes_1.PsScanChromeClasses.iconOn : ps_scan_chrome_classes_1.PsScanChromeClasses.iconOff), onClick: onToggleAuto, title: labels.autoCapture, type: 'button', children: labels.auto })] })] }), 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: pageCount > 0 ? ((0, jsx_runtime_1.jsx)(ps_scan_page_stack_1.PsScanPageStack, { count: pageCount, label: ps_scan_template_service_1.PsScanTemplateService.fill(labels.viewPages, { count: pageCount }), onOpen: onDone, preview: lastPreview })) : ((0, jsx_runtime_1.jsx)(ps_scan_photo_button_1.PsScanPhotoButton, { isIconOnly: true, isMultiple: isMultiple, label: labels.choosePhotos, onFiles: onFiles })) }), (0, jsx_runtime_1.jsx)(ps_scan_shutter_1.PsScanShutter, { disabled: !isReady || isFull || isBusy, label: labels.capture, onCapture: onCapture, progress: detection.progress }), (0, jsx_runtime_1.jsx)("div", { className: 'flex justify-end', children: pageCount > 0 ? ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { isPrimary: true, onClick: onDone, children: labels.done })) : null })] })] }));
|
|
33
|
+
};
|
|
34
|
+
exports.PsScanCameraView = PsScanCameraView;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
|
|
2
|
+
import type { PsLabelSet } from '../models/ps-label-set';
|
|
3
|
+
import type { PsScanPage } from '../models/ps-scan-page';
|
|
4
|
+
import type { PsScanQuad } from '../models/ps-scan-quad';
|
|
5
|
+
export interface PsScanCropEditorProps {
|
|
6
|
+
labels: PsLabelSet<typeof PsDocumentScannerLabels>;
|
|
7
|
+
onAutoDetect: () => void;
|
|
8
|
+
onChange: (corners: PsScanQuad) => void;
|
|
9
|
+
onKeep: () => void;
|
|
10
|
+
onRetake: () => void;
|
|
11
|
+
page: PsScanPage;
|
|
12
|
+
}
|
|
13
|
+
export declare const PsScanCropEditor: ({ labels, onAutoDetect, onChange, onKeep, onRetake, page, }: PsScanCropEditorProps) => import("react").JSX.Element;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsScanCropEditor = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
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");
|
|
9
|
+
const ps_scan_config_1 = require("../constants/ps-scan-config");
|
|
10
|
+
const ps_scan_corner_order_1 = require("../constants/ps-scan-corner-order");
|
|
11
|
+
const ps_scan_geometry_service_1 = require("../services/ps-scan-geometry-service");
|
|
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, onKeep, onRetake, page, }) => {
|
|
16
|
+
const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
|
|
17
|
+
const stageRef = (0, react_1.useRef)(null);
|
|
18
|
+
const [activeIndex, setActiveIndex] = (0, react_1.useState)(null);
|
|
19
|
+
const hintId = 'ps-scan-crop-hint';
|
|
20
|
+
const size = ps_scan_geometry_service_1.PsScanGeometryService.fit(area, { height: page.height, width: page.width });
|
|
21
|
+
const cornerLabels = [
|
|
22
|
+
labels.cornerTopLeft,
|
|
23
|
+
labels.cornerTopRight,
|
|
24
|
+
labels.cornerBottomRight,
|
|
25
|
+
labels.cornerBottomLeft,
|
|
26
|
+
];
|
|
27
|
+
const outline = page.corners
|
|
28
|
+
.map((point) => `${point.x * size.width},${point.y * size.height}`)
|
|
29
|
+
.join(' L');
|
|
30
|
+
const move = (index, event) => {
|
|
31
|
+
const stage = stageRef.current;
|
|
32
|
+
if (stage === null || !event.currentTarget.hasPointerCapture(event.pointerId))
|
|
33
|
+
return;
|
|
34
|
+
const rect = stage.getBoundingClientRect();
|
|
35
|
+
const point = ps_scan_geometry_service_1.PsScanGeometryService.clamp({
|
|
36
|
+
x: (event.clientX - rect.left) / rect.width,
|
|
37
|
+
y: (event.clientY - rect.top) / rect.height,
|
|
38
|
+
});
|
|
39
|
+
const next = page.corners.map((corner, at) => (at === index ? point : corner));
|
|
40
|
+
if (ps_scan_geometry_service_1.PsScanGeometryService.isUsable(next))
|
|
41
|
+
onChange(next);
|
|
42
|
+
};
|
|
43
|
+
const release = (event) => {
|
|
44
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
45
|
+
setActiveIndex(null);
|
|
46
|
+
};
|
|
47
|
+
const nudge = (index, event) => {
|
|
48
|
+
const step = event.shiftKey ? ps_scan_config_1.PsScanConfig.cornerNudgeLarge : ps_scan_config_1.PsScanConfig.cornerNudge;
|
|
49
|
+
const deltas = {
|
|
50
|
+
ArrowDown: [0, step],
|
|
51
|
+
ArrowLeft: [-step, 0],
|
|
52
|
+
ArrowRight: [step, 0],
|
|
53
|
+
ArrowUp: [0, -step],
|
|
54
|
+
};
|
|
55
|
+
const delta = deltas[event.key];
|
|
56
|
+
if (delta === undefined)
|
|
57
|
+
return;
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
onChange(ps_scan_geometry_service_1.PsScanGeometryService.nudge(page.corners, index, delta[0], delta[1]));
|
|
60
|
+
};
|
|
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.jsx)("p", { className: 'shrink-0 px-6 text-center text-sm text-white/65', id: hintId, children: 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: page.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-12 -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: `${page.corners[index].x * 100}%`,
|
|
66
|
+
top: `${page.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: page.corners, point: page.corners[activeIndex], source: page.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: onKeep, children: labels.keepScan })] })] }));
|
|
68
|
+
};
|
|
69
|
+
exports.PsScanCropEditor = PsScanCropEditor;
|