@pallasoft/ps-file-components 1.5.0 → 2.0.1

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.
Files changed (151) hide show
  1. package/dist/components/ps-document-scanner-loader.d.ts +1 -1
  2. package/dist/components/ps-document-scanner-sheet.d.ts +1 -1
  3. package/dist/components/ps-document-scanner-sheet.js +40 -110
  4. package/dist/components/ps-document-scanner.d.ts +1 -1
  5. package/dist/components/ps-document-scanner.js +2 -3
  6. package/dist/components/ps-file-delete-confirm.js +1 -1
  7. package/dist/components/ps-scan-camera-view.d.ts +2 -9
  8. package/dist/components/ps-scan-camera-view.js +3 -12
  9. package/dist/components/ps-scan-crop-editor.d.ts +4 -4
  10. package/dist/components/ps-scan-crop-editor.js +9 -9
  11. package/dist/components/ps-scan-permission-view.d.ts +2 -3
  12. package/dist/components/ps-scan-permission-view.js +2 -2
  13. package/dist/components/ps-scan-photo-button.d.ts +2 -3
  14. package/dist/components/ps-scan-photo-button.js +5 -5
  15. package/dist/components/ps-scan-preview.d.ts +12 -0
  16. package/dist/components/ps-scan-preview.js +10 -0
  17. package/dist/components/ps-scan-shutter.d.ts +1 -2
  18. package/dist/components/ps-scan-shutter.js +1 -1
  19. package/dist/components/ps-upload-modal.js +1 -1
  20. package/dist/constants/ps-document-scanner-labels.d.ts +5 -34
  21. package/dist/constants/ps-document-scanner-labels.js +5 -34
  22. package/dist/constants/ps-scan-chrome-classes.d.ts +0 -2
  23. package/dist/constants/ps-scan-chrome-classes.js +0 -2
  24. package/dist/constants/ps-scan-config.d.ts +24 -31
  25. package/dist/constants/ps-scan-config.js +24 -31
  26. package/dist/constants/ps-scan-step.d.ts +1 -1
  27. package/dist/constants/ps-scan-step.js +1 -1
  28. package/dist/esm/components/ps-document-scanner-sheet.js +41 -111
  29. package/dist/esm/components/ps-document-scanner.js +2 -3
  30. package/dist/esm/components/ps-file-delete-confirm.js +2 -2
  31. package/dist/esm/components/ps-scan-camera-view.js +3 -12
  32. package/dist/esm/components/ps-scan-crop-editor.js +9 -9
  33. package/dist/esm/components/ps-scan-permission-view.js +2 -2
  34. package/dist/esm/components/ps-scan-photo-button.js +5 -5
  35. package/dist/esm/components/ps-scan-preview.js +6 -0
  36. package/dist/esm/components/ps-scan-shutter.js +1 -1
  37. package/dist/esm/components/ps-upload-modal.js +2 -2
  38. package/dist/esm/constants/ps-document-scanner-labels.js +5 -34
  39. package/dist/esm/constants/ps-scan-chrome-classes.js +0 -2
  40. package/dist/esm/constants/ps-scan-config.js +24 -31
  41. package/dist/esm/constants/ps-scan-step.js +1 -1
  42. package/dist/esm/hooks/use-ps-scan-live-detection.js +17 -21
  43. package/dist/esm/hooks/use-ps-scan-photo.js +42 -0
  44. package/dist/esm/index.js +0 -2
  45. package/dist/esm/services/ps-scan-camera-service.js +2 -2
  46. package/dist/esm/services/ps-scan-canny-service.js +93 -0
  47. package/dist/esm/services/ps-scan-canvas-service.js +7 -1
  48. package/dist/esm/services/ps-scan-edge-service.js +73 -137
  49. package/dist/esm/services/ps-scan-file-name-service.js +2 -11
  50. package/dist/esm/services/ps-scan-geometry-service.js +36 -0
  51. package/dist/esm/services/ps-scan-hull-service.js +0 -39
  52. package/dist/esm/services/ps-scan-image-filter-service.js +0 -21
  53. package/dist/esm/services/ps-scan-morphology-service.js +44 -0
  54. package/dist/esm/services/ps-scan-pipeline-service.js +9 -19
  55. package/dist/esm/services/ps-scan-polygon-service.js +75 -0
  56. package/dist/esm/services/ps-scan-region-service.js +52 -0
  57. package/dist/esm/services/ps-scan-stability-service.js +9 -22
  58. package/dist/esm/services/ps-scan-warp-service.js +8 -2
  59. package/dist/esm/services/ps-scan-yield-service.js +5 -0
  60. package/dist/hooks/use-ps-scan-live-detection.d.ts +1 -1
  61. package/dist/hooks/use-ps-scan-live-detection.js +16 -20
  62. package/dist/hooks/use-ps-scan-photo.d.ts +9 -0
  63. package/dist/hooks/use-ps-scan-photo.js +46 -0
  64. package/dist/index.d.ts +0 -2
  65. package/dist/index.js +0 -2
  66. package/dist/models/ps-document-scanner-props.d.ts +0 -4
  67. package/dist/models/ps-document-scanner-sheet-props.d.ts +0 -4
  68. package/dist/models/ps-scan-gradient.d.ts +4 -0
  69. package/dist/models/ps-scan-live-detection.d.ts +0 -1
  70. package/dist/models/ps-scan-located.d.ts +5 -0
  71. package/dist/models/ps-scan-photo.d.ts +9 -0
  72. package/dist/models/ps-scan-region.d.ts +5 -0
  73. package/dist/models/ps-scan-result.d.ts +6 -0
  74. package/dist/models/ps-scan-stability.d.ts +0 -3
  75. package/dist/services/ps-scan-camera-service.js +2 -2
  76. package/dist/services/ps-scan-canny-service.d.ts +7 -0
  77. package/dist/services/ps-scan-canny-service.js +97 -0
  78. package/dist/services/ps-scan-canvas-service.js +7 -1
  79. package/dist/services/ps-scan-edge-service.d.ts +5 -5
  80. package/dist/services/ps-scan-edge-service.js +73 -137
  81. package/dist/services/ps-scan-file-name-service.d.ts +1 -3
  82. package/dist/services/ps-scan-file-name-service.js +2 -11
  83. package/dist/services/ps-scan-geometry-service.d.ts +2 -0
  84. package/dist/services/ps-scan-geometry-service.js +36 -0
  85. package/dist/services/ps-scan-hull-service.d.ts +0 -2
  86. package/dist/services/ps-scan-hull-service.js +0 -39
  87. package/dist/services/ps-scan-image-filter-service.d.ts +0 -1
  88. package/dist/services/ps-scan-image-filter-service.js +0 -21
  89. package/dist/services/ps-scan-morphology-service.d.ts +7 -0
  90. package/dist/services/ps-scan-morphology-service.js +48 -0
  91. package/dist/services/ps-scan-pipeline-service.d.ts +2 -5
  92. package/dist/services/ps-scan-pipeline-service.js +9 -19
  93. package/dist/services/ps-scan-polygon-service.d.ts +8 -0
  94. package/dist/services/ps-scan-polygon-service.js +79 -0
  95. package/dist/services/ps-scan-region-service.d.ts +4 -0
  96. package/dist/services/ps-scan-region-service.js +56 -0
  97. package/dist/services/ps-scan-stability-service.d.ts +1 -3
  98. package/dist/services/ps-scan-stability-service.js +9 -22
  99. package/dist/services/ps-scan-warp-service.d.ts +1 -1
  100. package/dist/services/ps-scan-warp-service.js +8 -2
  101. package/dist/services/ps-scan-yield-service.d.ts +3 -0
  102. package/dist/services/ps-scan-yield-service.js +9 -0
  103. package/package.json +3 -5
  104. package/dist/components/ps-scan-page-list.d.ts +0 -14
  105. package/dist/components/ps-scan-page-list.js +0 -22
  106. package/dist/components/ps-scan-page-stack.d.ts +0 -7
  107. package/dist/components/ps-scan-page-stack.js +0 -6
  108. package/dist/components/ps-scan-review.d.ts +0 -24
  109. package/dist/components/ps-scan-review.js +0 -37
  110. package/dist/constants/ps-scan-filter.d.ts +0 -7
  111. package/dist/constants/ps-scan-filter.js +0 -9
  112. package/dist/constants/ps-scan-output-format.d.ts +0 -5
  113. package/dist/constants/ps-scan-output-format.js +0 -7
  114. package/dist/esm/components/ps-scan-page-list.js +0 -18
  115. package/dist/esm/components/ps-scan-page-stack.js +0 -2
  116. package/dist/esm/components/ps-scan-review.js +0 -33
  117. package/dist/esm/constants/ps-scan-filter.js +0 -6
  118. package/dist/esm/constants/ps-scan-output-format.js +0 -4
  119. package/dist/esm/hooks/use-ps-scan-pages.js +0 -62
  120. package/dist/esm/hooks/use-ps-scan-previews.js +0 -57
  121. package/dist/esm/services/ps-scan-enhance-service.js +0 -97
  122. package/dist/esm/services/ps-scan-order-service.js +0 -13
  123. package/dist/esm/services/ps-scan-output-service.js +0 -24
  124. package/dist/esm/services/ps-scan-pdf-service.js +0 -56
  125. package/dist/hooks/use-ps-scan-pages.d.ts +0 -12
  126. package/dist/hooks/use-ps-scan-pages.js +0 -66
  127. package/dist/hooks/use-ps-scan-previews.d.ts +0 -2
  128. package/dist/hooks/use-ps-scan-previews.js +0 -61
  129. package/dist/models/ps-scan-added-page.d.ts +0 -5
  130. package/dist/models/ps-scan-levels.d.ts +0 -4
  131. package/dist/models/ps-scan-output-options.d.ts +0 -7
  132. package/dist/models/ps-scan-page.d.ts +0 -12
  133. package/dist/models/ps-scan-pdf-page.d.ts +0 -5
  134. package/dist/services/ps-scan-enhance-service.d.ts +0 -9
  135. package/dist/services/ps-scan-enhance-service.js +0 -101
  136. package/dist/services/ps-scan-order-service.d.ts +0 -4
  137. package/dist/services/ps-scan-order-service.js +0 -17
  138. package/dist/services/ps-scan-output-service.d.ts +0 -5
  139. package/dist/services/ps-scan-output-service.js +0 -28
  140. package/dist/services/ps-scan-pdf-service.d.ts +0 -5
  141. package/dist/services/ps-scan-pdf-service.js +0 -60
  142. /package/dist/esm/models/{ps-scan-added-page.js → ps-scan-gradient.js} +0 -0
  143. /package/dist/esm/models/{ps-scan-levels.js → ps-scan-located.js} +0 -0
  144. /package/dist/esm/models/{ps-scan-output-options.js → ps-scan-photo.js} +0 -0
  145. /package/dist/esm/models/{ps-scan-page.js → ps-scan-region.js} +0 -0
  146. /package/dist/esm/models/{ps-scan-pdf-page.js → ps-scan-result.js} +0 -0
  147. /package/dist/models/{ps-scan-added-page.js → ps-scan-gradient.js} +0 -0
  148. /package/dist/models/{ps-scan-levels.js → ps-scan-located.js} +0 -0
  149. /package/dist/models/{ps-scan-output-options.js → ps-scan-photo.js} +0 -0
  150. /package/dist/models/{ps-scan-page.js → ps-scan-region.js} +0 -0
  151. /package/dist/models/{ps-scan-pdf-page.js → ps-scan-result.js} +0 -0
@@ -1 +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>;
1
+ export declare const PsDocumentScannerLoader: import("react").LazyExoticComponent<({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }: import("../models/ps-document-scanner-sheet-props").PsDocumentScannerSheetProps) => import("react").ReactPortal | null>;
@@ -1,2 +1,2 @@
1
1
  import type { PsDocumentScannerSheetProps } from '../models/ps-document-scanner-sheet-props';
2
- export declare const PsDocumentScannerSheet: ({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
2
+ export declare const PsDocumentScannerSheet: ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerSheetProps) => import("react").ReactPortal | null;
@@ -12,81 +12,52 @@ const ps_scan_step_1 = require("../constants/ps-scan-step");
12
12
  const use_ps_focus_trap_1 = require("../hooks/use-ps-focus-trap");
13
13
  const use_ps_scan_camera_1 = require("../hooks/use-ps-scan-camera");
14
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");
15
+ const use_ps_scan_photo_1 = require("../hooks/use-ps-scan-photo");
16
+ const ps_scan_file_name_service_1 = require("../services/ps-scan-file-name-service");
18
17
  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
18
  const ps_scan_camera_view_1 = require("./ps-scan-camera-view");
21
19
  const ps_scan_crop_editor_1 = require("./ps-scan-crop-editor");
22
20
  const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
23
21
  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, }) => {
22
+ const ps_scan_preview_1 = require("./ps-scan-preview");
23
+ const PsDocumentScannerSheet = ({ fileName, jpegQuality, labels, maxDimension, onClose, onComplete, }) => {
26
24
  const tier = (0, ps_core_components_1.usePsViewport)();
27
25
  const zIndex = (0, ps_core_components_1.usePsLayerZIndex)(ps_design_system_1.PallasoftZIndex.Modal);
28
26
  const content = (0, react_1.useRef)(null);
29
- const { add, move, pages, remove, replace, setCorners, update } = (0, use_ps_scan_pages_1.usePsScanPages)();
27
+ const { clear, detectAgain, load, photo, setCorners } = (0, use_ps_scan_photo_1.usePsScanPhoto)();
30
28
  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)('');
29
+ const [result, setResult] = (0, react_1.useState)(null);
34
30
  const [announcement, setAnnouncement] = (0, react_1.useState)('');
35
31
  const [error, setError] = (0, react_1.useState)('');
36
- const [isBusy, setIsBusy] = (0, react_1.useState)(false);
37
32
  const [isCapturing, setIsCapturing] = (0, react_1.useState)(false);
38
- const [isAuto, setIsAuto] = (0, react_1.useState)(isAutoCapture);
39
33
  const [flashKey, setFlashKey] = (0, react_1.useState)(0);
40
34
  const camera = (0, use_ps_scan_camera_1.usePsScanCamera)(step === ps_scan_step_1.PsScanStep.Camera);
41
35
  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
36
  const isDesktop = tier === ps_design_system_1.PallasoftViewportTier.Desktop;
47
37
  const isCameraStep = step === ps_scan_step_1.PsScanStep.Camera;
48
38
  const showsCamera = isReady || camera.status === ps_scan_camera_status_1.PsScanCameraStatus.Starting;
39
+ const detection = (0, use_ps_scan_live_detection_1.usePsScanLiveDetection)(camera.videoRef, isCameraStep && isReady);
49
40
  (0, ps_core_components_1.usePsScrollLock)(true);
50
41
  (0, ps_core_components_1.usePsRestoreFocus)(true);
51
42
  (0, use_ps_focus_trap_1.usePsFocusTrap)(content, true, onClose);
43
+ (0, react_1.useEffect)(() => () => {
44
+ if (result !== null)
45
+ URL.revokeObjectURL(result.url);
46
+ }, [result]);
52
47
  const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
53
- const addFiles = async (files, after) => {
48
+ const accept = async (source) => {
54
49
  setError('');
55
50
  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);
51
+ const added = await load(source);
52
+ setAnnouncement(edgeMessage(added.isDetected));
53
+ setStep(ps_scan_step_1.PsScanStep.Crop);
83
54
  }
84
55
  catch {
85
56
  setError(labels.processFailed);
86
57
  }
87
58
  };
88
- const capture = async (isAutomatic) => {
89
- if (isCapturing || isFull)
59
+ const capture = async () => {
60
+ if (isCapturing)
90
61
  return;
91
62
  setIsCapturing(true);
92
63
  try {
@@ -94,93 +65,52 @@ const PsDocumentScannerSheet = ({ fileName, isAutoCapture, jpegQuality, labels,
94
65
  if (blob === null)
95
66
  return;
96
67
  setFlashKey((current) => current + 1);
97
- await addFiles([blob], isAutomatic && retakeId === '' ? null : ps_scan_step_1.PsScanStep.Camera);
68
+ await accept(blob);
98
69
  }
99
70
  finally {
100
71
  setIsCapturing(false);
101
72
  }
102
73
  };
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);
74
+ const autoDetect = async () => setAnnouncement(edgeMessage(await detectAgain()));
112
75
  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);
76
+ clear();
77
+ setResult(null);
78
+ setError('');
123
79
  setStep(ps_scan_step_1.PsScanStep.Camera);
124
80
  };
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);
81
+ const adjust = () => {
82
+ setResult(null);
83
+ setError('');
135
84
  setStep(ps_scan_step_1.PsScanStep.Crop);
136
85
  };
137
- const deletePage = () => {
138
- if (page === undefined)
86
+ const flatten = async () => {
87
+ if (photo === null)
139
88
  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);
89
+ setResult(null);
153
90
  setError('');
91
+ setStep(ps_scan_step_1.PsScanStep.Preview);
154
92
  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();
93
+ const rendered = await ps_scan_pipeline_service_1.PsScanPipelineService.render(photo.source, photo.corners, maxDimension, jpegQuality);
94
+ setResult({ ...rendered, url: URL.createObjectURL(rendered.blob) });
163
95
  }
164
96
  catch {
165
97
  setError(labels.processFailed);
166
- setIsBusy(false);
167
98
  }
168
99
  };
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);
100
+ const use = () => {
101
+ if (result === null)
102
+ return;
103
+ onComplete([
104
+ new File([result.blob], ps_scan_file_name_service_1.PsScanFileNameService.build(fileName), { type: 'image/jpeg' }),
105
+ ]);
106
+ onClose();
107
+ };
178
108
  if (typeof document === 'undefined')
179
109
  return null;
180
110
  return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
181
111
  ? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
182
112
  : 'fixed inset-0 bg-black animate-ps-fade-in', role: 'dialog', style: { zIndex }, children: (0, jsx_runtime_1.jsx)(ps_core_components_1.PsLayer, { zIndex: zIndex, children: (0, jsx_runtime_1.jsxs)("div", { className: isDesktop
183
113
  ? 'relative h-[min(52rem,calc(100dvh-3rem))] w-[min(76rem,calc(100vw-3rem))] overflow-hidden rounded-2xl bg-black text-white shadow-2xl ring-1 ring-white/10 animate-ps-scale-in'
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);
114
+ : 'relative h-full overflow-hidden bg-black text-white', ref: content, children: [(0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), isCameraStep && showsCamera ? ((0, jsx_runtime_1.jsx)(ps_scan_camera_view_1.PsScanCameraView, { canTorch: camera.canTorch, detection: detection, flashKey: flashKey, isBusy: isCapturing, isReady: isReady, isTorchOn: camera.isTorchOn, labels: labels, onCapture: capture, onClose: onClose, onFile: accept, onToggleTorch: camera.toggleTorch, videoRef: camera.videoRef, videoTarget: camera.attachVideo })) : null, isCameraStep && !showsCamera ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_scan_permission_view_1.PsScanPermissionView, { labels: labels, onAllow: camera.allow, onFile: accept, status: camera.status }), (0, jsx_runtime_1.jsx)("div", { className: 'absolute left-4 top-[max(1rem,env(safe-area-inset-top))] flex gap-2', children: (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: labels.close, onClick: onClose }) })] })) : null, step === ps_scan_step_1.PsScanStep.Crop && photo !== null ? ((0, jsx_runtime_1.jsx)(ps_scan_crop_editor_1.PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: setCorners, onContinue: flatten, onRetake: retake, photo: photo })) : null, step === ps_scan_step_1.PsScanStep.Preview ? ((0, jsx_runtime_1.jsx)(ps_scan_preview_1.PsScanPreview, { error: error, labels: labels, onAdjust: adjust, onRetake: retake, onUse: use, result: result })) : null, isCameraStep && error !== '' ? ((0, jsx_runtime_1.jsx)("p", { className: 'absolute inset-x-4 bottom-36 mx-auto max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })) : null] }) }) }), document.body);
185
115
  };
186
116
  exports.PsDocumentScannerSheet = PsDocumentScannerSheet;
@@ -1,2 +1,2 @@
1
1
  import type { PsDocumentScannerProps } from '../models/ps-document-scanner-props';
2
- export declare const PsDocumentScanner: ({ fileName, isAutoCapture, isOpen, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
2
+ export declare const PsDocumentScanner: ({ fileName, isOpen, jpegQuality, labels, maxDimension, onClose, onComplete, }: PsDocumentScannerProps) => import("react").JSX.Element | null;
@@ -6,12 +6,11 @@ const react_1 = require("react");
6
6
  const ps_core_components_1 = require("@pallasoft/ps-core-components");
7
7
  const ps_document_scanner_labels_1 = require("../constants/ps-document-scanner-labels");
8
8
  const ps_scan_config_1 = require("../constants/ps-scan-config");
9
- const ps_scan_output_format_1 = require("../constants/ps-scan-output-format");
10
9
  const ps_document_scanner_loader_1 = require("./ps-document-scanner-loader");
11
- const PsDocumentScanner = ({ fileName = ps_scan_config_1.PsScanConfig.defaultFileName, 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, }) => {
10
+ const PsDocumentScanner = ({ fileName = ps_scan_config_1.PsScanConfig.defaultFileName, isOpen, jpegQuality = ps_scan_config_1.PsScanConfig.defaultJpegQuality, labels, maxDimension = ps_scan_config_1.PsScanConfig.defaultMaxDimension, onClose, onComplete, }) => {
12
11
  const isHydrated = (0, ps_core_components_1.usePsHydrated)();
13
12
  if (!isOpen || !isHydrated)
14
13
  return null;
15
- return ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ps_document_scanner_loader_1.PsDocumentScannerLoader, { fileName: fileName, isAutoCapture: isAutoCapture, jpegQuality: jpegQuality, labels: { ...ps_document_scanner_labels_1.PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, maxPages: maxPages, onClose: onClose, onComplete: onComplete, outputFormat: outputFormat }) }));
14
+ return ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: null, children: (0, jsx_runtime_1.jsx)(ps_document_scanner_loader_1.PsDocumentScannerLoader, { fileName: fileName, jpegQuality: jpegQuality, labels: { ...ps_document_scanner_labels_1.PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, onClose: onClose, onComplete: onComplete }) }));
16
15
  };
17
16
  exports.PsDocumentScanner = PsDocumentScanner;
@@ -4,5 +4,5 @@ exports.PsFileDeleteConfirm = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_core_components_1 = require("@pallasoft/ps-core-components");
6
6
  const ps_popovers_1 = require("@pallasoft/ps-popovers");
7
- const PsFileDeleteConfirm = ({ fileName, isDeleting, isOpen, labels, onCancel, onConfirm, }) => ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { eyebrow: fileName, isOpen: isOpen, onClose: onCancel, size: 'sm', title: labels.deleteConfirm, children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-5', children: [(0, jsx_runtime_1.jsxs)("p", { className: 'text-sm text-ink/70', children: [(0, jsx_runtime_1.jsx)("span", { className: 'font-medium text-ink', children: fileName }), " ", labels.deleteConfirmBody] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex justify-end gap-2 border-t border-line pt-4', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { disabled: isDeleting, onClick: onCancel, type: 'button', variant: 'ghost', children: labels.deleteCancel }), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { isLoading: isDeleting, onClick: onConfirm, type: 'button', variant: 'danger', children: labels.delete })] })] }) }));
7
+ const PsFileDeleteConfirm = ({ fileName, isDeleting, isOpen, labels, onCancel, onConfirm, }) => ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { eyebrow: fileName, footer: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { disabled: isDeleting, onClick: onCancel, type: 'button', variant: 'surface', children: labels.deleteCancel }), (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { isLoading: isDeleting, onClick: onConfirm, type: 'button', variant: 'danger', children: labels.delete })] }), isOpen: isOpen, onClose: onCancel, size: 'sm', title: labels.deleteConfirm, children: (0, jsx_runtime_1.jsxs)("p", { className: 'text-sm text-ink/70', children: [(0, jsx_runtime_1.jsx)("span", { className: 'font-medium text-ink', children: fileName }), " ", labels.deleteConfirmBody] }) }));
8
8
  exports.PsFileDeleteConfirm = PsFileDeleteConfirm;
@@ -6,22 +6,15 @@ export interface PsScanCameraViewProps {
6
6
  canTorch: boolean;
7
7
  detection: PsScanLiveDetection;
8
8
  flashKey: number;
9
- isAuto: boolean;
10
9
  isBusy: boolean;
11
- isFull: boolean;
12
- isMultiple: boolean;
13
10
  isReady: boolean;
14
11
  isTorchOn: boolean;
15
12
  labels: PsLabelSet<typeof PsDocumentScannerLabels>;
16
- lastPreview: string | undefined;
17
13
  onCapture: () => void;
18
14
  onClose: () => void;
19
- onDone: () => void;
20
- onFiles: (files: File[]) => void;
21
- onToggleAuto: () => void;
15
+ onFile: (file: File) => void;
22
16
  onToggleTorch: () => void;
23
- pageCount: number;
24
17
  videoRef: RefObject<HTMLVideoElement | null>;
25
18
  videoTarget: RefCallback<HTMLVideoElement>;
26
19
  }
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;
20
+ export declare const PsScanCameraView: ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }: PsScanCameraViewProps) => import("react").JSX.Element;
@@ -7,28 +7,19 @@ const ps_core_components_1 = require("@pallasoft/ps-core-components");
7
7
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
8
8
  const ps_icons_1 = require("@pallasoft/ps-icons");
9
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
10
  const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
13
- const ps_scan_page_stack_1 = require("./ps-scan-page-stack");
14
11
  const ps_scan_photo_button_1 = require("./ps-scan-photo-button");
15
12
  const ps_scan_shutter_1 = require("./ps-scan-shutter");
16
13
  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, }) => {
14
+ const PsScanCameraView = ({ canTorch, detection, flashKey, isBusy, isReady, isTorchOn, labels, onCapture, onClose, onFile, onToggleTorch, videoRef, videoTarget, }) => {
18
15
  const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
19
16
  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;
17
+ const hint = detection.quad === null ? labels.hintFind : labels.hintFound;
27
18
  const measure = () => {
28
19
  const element = videoRef.current;
29
20
  if (element !== null)
30
21
  setVideo({ height: element.videoHeight, width: element.videoWidth });
31
22
  };
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 })] })] }));
23
+ return ((0, jsx_runtime_1.jsxs)("div", { className: 'relative h-full min-h-0 overflow-hidden bg-black text-white', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-0', ref: areaRef, children: [(0, jsx_runtime_1.jsx)("video", { "aria-hidden": 'true', autoPlay: true, className: ps_design_system_1.PallasoftClassNameService.merge('size-full object-cover transition-opacity duration-300', isReady ? 'opacity-100' : 'opacity-0'), muted: true, onLoadedMetadata: measure, onResize: measure, playsInline: true, ref: videoTarget }), isReady ? ((0, jsx_runtime_1.jsx)(ps_scan_viewfinder_overlay_1.PsScanViewfinderOverlay, { area: area, quad: detection.quad, video: video })) : null] }), isReady ? null : ((0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-0 flex flex-col items-center justify-center gap-3 text-sm text-white/75', role: 'status', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', label: labels.cameraStarting, size: 'small' }), (0, jsx_runtime_1.jsx)("span", { children: labels.cameraStarting })] })), flashKey > 0 ? ((0, jsx_runtime_1.jsx)("div", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 bg-white animate-ps-fade-out' }, flashKey)) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-x-0 top-0 flex items-start justify-between gap-3 bg-gradient-to-b from-black/60 to-transparent px-4 pb-10 pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Close, label: labels.close, onClick: onClose }), (0, jsx_runtime_1.jsx)("div", { className: 'flex items-center gap-2', children: canTorch && isReady ? ((0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Flash, isPressed: isTorchOn, label: isTorchOn ? labels.torchOff : labels.torchOn, onClick: onToggleTorch })) : null })] }), isReady ? ((0, jsx_runtime_1.jsx)("div", { className: 'absolute inset-x-0 top-[calc(max(1rem,env(safe-area-inset-top))+3.75rem)] flex justify-center px-4', children: (0, jsx_runtime_1.jsx)("p", { "aria-live": 'polite', className: ps_scan_chrome_classes_1.PsScanChromeClasses.pill, children: hint }) })) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'absolute inset-x-0 bottom-0 grid grid-cols-[1fr_auto_1fr] items-center gap-4 bg-gradient-to-t from-black/80 to-transparent px-5 pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-12', children: [(0, jsx_runtime_1.jsx)("div", { className: 'flex justify-start', children: (0, jsx_runtime_1.jsx)(ps_scan_photo_button_1.PsScanPhotoButton, { isIconOnly: true, label: labels.choosePhoto, onFile: onFile }) }), (0, jsx_runtime_1.jsx)(ps_scan_shutter_1.PsScanShutter, { disabled: !isReady || isBusy, label: labels.capture, onCapture: onCapture }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' })] })] }));
33
24
  };
34
25
  exports.PsScanCameraView = PsScanCameraView;
@@ -1,13 +1,13 @@
1
1
  import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
2
2
  import type { PsLabelSet } from '../models/ps-label-set';
3
- import type { PsScanPage } from '../models/ps-scan-page';
3
+ import type { PsScanPhoto } from '../models/ps-scan-photo';
4
4
  import type { PsScanQuad } from '../models/ps-scan-quad';
5
5
  export interface PsScanCropEditorProps {
6
6
  labels: PsLabelSet<typeof PsDocumentScannerLabels>;
7
7
  onAutoDetect: () => void;
8
8
  onChange: (corners: PsScanQuad) => void;
9
- onKeep: () => void;
9
+ onContinue: () => void;
10
10
  onRetake: () => void;
11
- page: PsScanPage;
11
+ photo: PsScanPhoto;
12
12
  }
13
- export declare const PsScanCropEditor: ({ labels, onAutoDetect, onChange, onKeep, onRetake, page, }: PsScanCropEditorProps) => import("react").JSX.Element;
13
+ export declare const PsScanCropEditor: ({ labels, onAutoDetect, onChange, onContinue, onRetake, photo, }: PsScanCropEditorProps) => import("react").JSX.Element;
@@ -12,19 +12,19 @@ const ps_scan_geometry_service_1 = require("../services/ps-scan-geometry-service
12
12
  const ps_scan_button_1 = require("./ps-scan-button");
13
13
  const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
14
14
  const ps_scan_loupe_1 = require("./ps-scan-loupe");
15
- const PsScanCropEditor = ({ labels, onAutoDetect, onChange, onKeep, onRetake, page, }) => {
15
+ const PsScanCropEditor = ({ labels, onAutoDetect, onChange, onContinue, onRetake, photo, }) => {
16
16
  const [areaRef, area] = (0, ps_core_components_1.usePsElementSize)();
17
17
  const stageRef = (0, react_1.useRef)(null);
18
18
  const [activeIndex, setActiveIndex] = (0, react_1.useState)(null);
19
19
  const hintId = 'ps-scan-crop-hint';
20
- const size = ps_scan_geometry_service_1.PsScanGeometryService.fit(area, { height: page.height, width: page.width });
20
+ const size = ps_scan_geometry_service_1.PsScanGeometryService.fit(area, { height: photo.height, width: photo.width });
21
21
  const cornerLabels = [
22
22
  labels.cornerTopLeft,
23
23
  labels.cornerTopRight,
24
24
  labels.cornerBottomRight,
25
25
  labels.cornerBottomLeft,
26
26
  ];
27
- const outline = page.corners
27
+ const outline = photo.corners
28
28
  .map((point) => `${point.x * size.width},${point.y * size.height}`)
29
29
  .join(' L');
30
30
  const move = (index, event) => {
@@ -36,7 +36,7 @@ const PsScanCropEditor = ({ labels, onAutoDetect, onChange, onKeep, onRetake, pa
36
36
  x: (event.clientX - rect.left) / rect.width,
37
37
  y: (event.clientY - rect.top) / rect.height,
38
38
  });
39
- const next = page.corners.map((corner, at) => (at === index ? point : corner));
39
+ const next = photo.corners.map((corner, at) => (at === index ? point : corner));
40
40
  if (ps_scan_geometry_service_1.PsScanGeometryService.isUsable(next))
41
41
  onChange(next);
42
42
  };
@@ -56,14 +56,14 @@ const PsScanCropEditor = ({ labels, onAutoDetect, onChange, onKeep, onRetake, pa
56
56
  if (delta === undefined)
57
57
  return;
58
58
  event.preventDefault();
59
- onChange(ps_scan_geometry_service_1.PsScanGeometryService.nudge(page.corners, index, delta[0], delta[1]));
59
+ onChange(ps_scan_geometry_service_1.PsScanGeometryService.nudge(photo.corners, index, delta[0], delta[1]));
60
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) => {
61
+ return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col bg-black text-white animate-ps-fade-in', children: [(0, jsx_runtime_1.jsxs)("header", { className: 'grid shrink-0 grid-cols-[2.75rem_1fr_2.75rem] items-center gap-3 px-4 pb-1 pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' }), (0, jsx_runtime_1.jsx)("h2", { className: 'truncate text-center text-base font-semibold', children: labels.cropTitle }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Spark, label: labels.autoDetect, onClick: onAutoDetect })] }), (0, jsx_runtime_1.jsxs)("p", { className: 'shrink-0 px-6 text-center text-sm text-white/65', id: hintId, children: [photo.isDetected ? labels.edgesFound : labels.edgesNotFound, " ", labels.cropHint] }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-8 py-6', ref: areaRef, children: (0, jsx_runtime_1.jsxs)("div", { className: 'relative touch-none select-none', ref: stageRef, style: { height: size.height, width: size.width }, children: [(0, jsx_runtime_1.jsx)("img", { alt: '', className: 'absolute inset-0 size-full rounded-sm', draggable: false, src: photo.sourceUrl }), (0, jsx_runtime_1.jsxs)("svg", { "aria-hidden": 'true', className: 'pointer-events-none absolute inset-0 size-full overflow-visible', viewBox: `0 0 ${Math.max(1, size.width)} ${Math.max(1, size.height)}`, children: [(0, jsx_runtime_1.jsx)("path", { className: 'fill-black/55', d: `M0 0H${size.width}V${size.height}H0Z M${outline}Z`, fillRule: 'evenodd' }), (0, jsx_runtime_1.jsx)("path", { className: 'fill-none stroke-primary', d: `M${outline}Z`, strokeLinejoin: 'round', strokeWidth: '2.5' })] }), ps_scan_corner_order_1.PsScanCornerOrder.map((key, index) => ((0, jsx_runtime_1.jsx)("button", { "aria-describedby": hintId, "aria-label": cornerLabels[index], className: 'ps-focus-ring absolute flex size-11 -translate-x-1/2 -translate-y-1/2 cursor-grab touch-none items-center justify-center rounded-full active:cursor-grabbing', onKeyDown: (event) => nudge(index, event), onPointerCancel: release, onPointerDown: (event) => {
62
62
  event.currentTarget.setPointerCapture(event.pointerId);
63
63
  setActiveIndex(index);
64
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 })] })] }));
65
+ left: `${photo.corners[index].x * 100}%`,
66
+ top: `${photo.corners[index].y * 100}%`,
67
+ }, type: 'button', children: (0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('size-6 rounded-full border-[3px] border-white bg-primary shadow-lg transition-transform duration-150', activeIndex === index ? 'scale-125' : '') }) }, key))), activeIndex === null ? null : ((0, jsx_runtime_1.jsx)(ps_scan_loupe_1.PsScanLoupe, { corners: photo.corners, point: photo.corners[activeIndex], source: photo.sourceUrl, stage: size }))] }) }), (0, jsx_runtime_1.jsxs)("footer", { className: 'mx-auto grid w-full max-w-md shrink-0 grid-cols-2 gap-3 px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-2', children: [(0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { isLarge: true, onClick: onRetake, children: labels.retake }), (0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { isLarge: true, isPrimary: true, onClick: onContinue, children: labels.continue })] })] }));
68
68
  };
69
69
  exports.PsScanCropEditor = PsScanCropEditor;
@@ -2,10 +2,9 @@ import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels
2
2
  import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
3
3
  import type { PsLabelSet } from '../models/ps-label-set';
4
4
  export interface PsScanPermissionViewProps {
5
- isMultiple: boolean;
6
5
  labels: PsLabelSet<typeof PsDocumentScannerLabels>;
7
6
  onAllow: () => void;
8
- onFiles: (files: File[]) => void;
7
+ onFile: (file: File) => void;
9
8
  status: PsScanCameraStatus;
10
9
  }
11
- export declare const PsScanPermissionView: ({ isMultiple, labels, onAllow, onFiles, status, }: PsScanPermissionViewProps) => import("react").JSX.Element;
10
+ export declare const PsScanPermissionView: ({ labels, onAllow, onFile, status, }: PsScanPermissionViewProps) => import("react").JSX.Element;
@@ -9,7 +9,7 @@ const ps_scan_button_1 = require("./ps-scan-button");
9
9
  const ps_scan_denied_steps_1 = require("./ps-scan-denied-steps");
10
10
  const ps_scan_illustration_1 = require("./ps-scan-illustration");
11
11
  const ps_scan_photo_button_1 = require("./ps-scan-photo-button");
12
- const PsScanPermissionView = ({ isMultiple, labels, onAllow, onFiles, status, }) => {
12
+ const PsScanPermissionView = ({ labels, onAllow, onFile, status, }) => {
13
13
  if (status === ps_scan_camera_status_1.PsScanCameraStatus.Checking || status === ps_scan_camera_status_1.PsScanCameraStatus.Starting)
14
14
  return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full 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 })] }));
15
15
  const screens = {
@@ -43,6 +43,6 @@ const PsScanPermissionView = ({ isMultiple, labels, onAllow, onFiles, status, })
43
43
  const screen = screens[status];
44
44
  const isPrimer = status === ps_scan_camera_status_1.PsScanCameraStatus.Prompt;
45
45
  const canRetry = status === ps_scan_camera_status_1.PsScanCameraStatus.Denied || status === ps_scan_camera_status_1.PsScanCameraStatus.Busy;
46
- return ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full min-h-0 flex-col overflow-y-auto', children: (0, jsx_runtime_1.jsxs)("div", { className: 'm-auto flex w-full max-w-sm flex-col items-center gap-6 px-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-20 text-center animate-ps-slide-up', children: [isPrimer ? ((0, jsx_runtime_1.jsx)(ps_scan_illustration_1.PsScanIllustration, {})) : ((0, jsx_runtime_1.jsx)("span", { className: 'flex size-20 items-center justify-center rounded-full bg-white/10 text-white', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-9', iconKey: screen.iconKey }) })), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-2', children: [(0, jsx_runtime_1.jsx)("h2", { className: 'text-xl font-semibold leading-tight text-white sm:text-2xl', children: screen.title }), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm leading-6 text-white/70', children: screen.body })] }), status === ps_scan_camera_status_1.PsScanCameraStatus.Denied ? (0, jsx_runtime_1.jsx)(ps_scan_denied_steps_1.PsScanDeniedSteps, { labels: labels }) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex w-full flex-col gap-3', children: [isPrimer || canRetry ? ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: 'w-full', isLarge: true, isPrimary: true, leadingIcon: isPrimer ? ((0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Camera })) : undefined, onClick: onAllow, children: isPrimer ? labels.allowCamera : labels.tryAgain })) : null, (0, jsx_runtime_1.jsx)(ps_scan_photo_button_1.PsScanPhotoButton, { className: 'w-full', isMultiple: isMultiple, label: labels.choosePhoto, onFiles: onFiles })] })] }) }));
46
+ return ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-full min-h-0 flex-col overflow-y-auto', children: (0, jsx_runtime_1.jsxs)("div", { className: 'm-auto flex w-full max-w-sm flex-col items-center gap-6 px-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] pt-20 text-center animate-ps-slide-up', children: [isPrimer ? ((0, jsx_runtime_1.jsx)(ps_scan_illustration_1.PsScanIllustration, {})) : ((0, jsx_runtime_1.jsx)("span", { className: 'flex size-20 items-center justify-center rounded-full bg-white/10 text-white', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-9', iconKey: screen.iconKey }) })), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-2', children: [(0, jsx_runtime_1.jsx)("h2", { className: 'text-xl font-semibold leading-tight text-white sm:text-2xl', children: screen.title }), (0, jsx_runtime_1.jsx)("p", { className: 'text-sm leading-6 text-white/70', children: screen.body })] }), status === ps_scan_camera_status_1.PsScanCameraStatus.Denied ? (0, jsx_runtime_1.jsx)(ps_scan_denied_steps_1.PsScanDeniedSteps, { labels: labels }) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'flex w-full flex-col gap-3', children: [isPrimer || canRetry ? ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: 'w-full', isLarge: true, isPrimary: true, leadingIcon: isPrimer ? ((0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Camera })) : undefined, onClick: onAllow, children: isPrimer ? labels.allowCamera : labels.tryAgain })) : null, (0, jsx_runtime_1.jsx)(ps_scan_photo_button_1.PsScanPhotoButton, { className: 'w-full', label: labels.choosePhoto, onFile: onFile })] })] }) }));
47
47
  };
48
48
  exports.PsScanPermissionView = PsScanPermissionView;
@@ -1,8 +1,7 @@
1
1
  export interface PsScanPhotoButtonProps {
2
2
  className?: string;
3
3
  isIconOnly?: boolean;
4
- isMultiple: boolean;
5
4
  label: string;
6
- onFiles: (files: File[]) => void;
5
+ onFile: (file: File) => void;
7
6
  }
8
- export declare const PsScanPhotoButton: ({ className, isIconOnly, isMultiple, label, onFiles, }: PsScanPhotoButtonProps) => import("react").JSX.Element;
7
+ export declare const PsScanPhotoButton: ({ className, isIconOnly, label, onFile, }: PsScanPhotoButtonProps) => import("react").JSX.Element;
@@ -6,15 +6,15 @@ const react_1 = require("react");
6
6
  const ps_icons_1 = require("@pallasoft/ps-icons");
7
7
  const ps_scan_button_1 = require("./ps-scan-button");
8
8
  const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
9
- const PsScanPhotoButton = ({ className, isIconOnly = false, isMultiple, label, onFiles, }) => {
9
+ const PsScanPhotoButton = ({ className, isIconOnly = false, label, onFile, }) => {
10
10
  const input = (0, react_1.useRef)(null);
11
11
  const open = () => input.current?.click();
12
12
  const onChange = (event) => {
13
- const files = Array.from(event.target.files === null ? [] : event.target.files);
13
+ const file = event.target.files === null ? undefined : event.target.files[0];
14
14
  event.target.value = '';
15
- if (files.length > 0)
16
- onFiles(files);
15
+ if (file !== undefined)
16
+ onFile(file);
17
17
  };
18
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isIconOnly ? ((0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { className: className, iconKey: ps_icons_1.PsIconKey.Image, label: label, onClick: open })) : ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: className, isLarge: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Image }), onClick: open, children: label })), (0, jsx_runtime_1.jsx)("input", { accept: 'image/*', "aria-hidden": 'true', className: 'sr-only', multiple: isMultiple, onChange: onChange, ref: input, tabIndex: -1, type: 'file' })] }));
18
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isIconOnly ? ((0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { className: className, iconKey: ps_icons_1.PsIconKey.Image, label: label, onClick: open })) : ((0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: className, isLarge: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Image }), onClick: open, children: label })), (0, jsx_runtime_1.jsx)("input", { accept: 'image/*', "aria-hidden": 'true', className: 'sr-only', onChange: onChange, ref: input, tabIndex: -1, type: 'file' })] }));
19
19
  };
20
20
  exports.PsScanPhotoButton = PsScanPhotoButton;
@@ -0,0 +1,12 @@
1
+ import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
2
+ import type { PsLabelSet } from '../models/ps-label-set';
3
+ import type { PsScanResult } from '../models/ps-scan-result';
4
+ export interface PsScanPreviewProps {
5
+ error: string;
6
+ labels: PsLabelSet<typeof PsDocumentScannerLabels>;
7
+ onAdjust: () => void;
8
+ onRetake: () => void;
9
+ onUse: () => void;
10
+ result: PsScanResult | null;
11
+ }
12
+ export declare const PsScanPreview: ({ error, labels, onAdjust, onRetake, onUse, result, }: PsScanPreviewProps) => import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanPreview = 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_icons_1 = require("@pallasoft/ps-icons");
7
+ const ps_scan_button_1 = require("./ps-scan-button");
8
+ const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
9
+ const PsScanPreview = ({ error, labels, onAdjust, onRetake, onUse, result, }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col bg-black text-white animate-ps-fade-in', children: [(0, jsx_runtime_1.jsxs)("header", { className: 'grid shrink-0 grid-cols-[2.75rem_1fr_2.75rem] items-center gap-3 px-4 pb-1 pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { iconKey: ps_icons_1.PsIconKey.Crop, label: labels.cropTitle, onClick: onAdjust }), (0, jsx_runtime_1.jsx)("h2", { className: 'truncate text-center text-base font-semibold', children: labels.previewTitle }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' })] }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-6 py-4', children: result === null ? ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col items-center gap-3 text-sm text-white/75', role: 'status', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsSpinner, { color: 'current', label: labels.preparing, size: 'small' }), (0, jsx_runtime_1.jsx)("span", { children: labels.preparing })] })) : ((0, jsx_runtime_1.jsx)("img", { alt: labels.previewAlt, className: 'max-h-full max-w-full rounded-sm bg-white object-contain shadow-[0_24px_60px_-20px_rgb(0_0_0/0.9)] animate-ps-fade-in', src: result.url })) }), error === '' ? null : ((0, jsx_runtime_1.jsx)("p", { className: 'mx-auto mb-2 max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })), (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, { disabled: result === null, isLarge: true, isPrimary: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Check }), onClick: onUse, children: labels.useScan })] })] }));
10
+ exports.PsScanPreview = PsScanPreview;
@@ -2,6 +2,5 @@ export interface PsScanShutterProps {
2
2
  disabled: boolean;
3
3
  label: string;
4
4
  onCapture: () => void;
5
- progress: number;
6
5
  }
7
- export declare const PsScanShutter: ({ disabled, label, onCapture, progress }: PsScanShutterProps) => import("react").JSX.Element;
6
+ export declare const PsScanShutter: ({ disabled, label, onCapture }: PsScanShutterProps) => import("react").JSX.Element;
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsScanShutter = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const PsScanShutter = ({ disabled, label, onCapture, progress }) => ((0, jsx_runtime_1.jsxs)("button", { "aria-label": label, className: 'ps-focus-ring group relative flex size-[4.75rem] shrink-0 items-center justify-center rounded-full disabled:opacity-40', disabled: disabled, onClick: onCapture, title: label, type: 'button', children: [(0, jsx_runtime_1.jsxs)("svg", { "aria-hidden": 'true', className: 'absolute inset-0 size-full -rotate-90', viewBox: '0 0 76 76', children: [(0, jsx_runtime_1.jsx)("circle", { className: 'stroke-white', cx: '38', cy: '38', fill: 'none', r: '35', strokeWidth: '4' }), (0, jsx_runtime_1.jsx)("circle", { className: 'stroke-primary transition-[stroke-dashoffset] duration-150 ease-linear', cx: '38', cy: '38', fill: 'none', pathLength: '100', r: '35', strokeDasharray: '100', strokeDashoffset: 100 - Math.round(progress * 100), strokeLinecap: 'round', strokeWidth: '4' })] }), (0, jsx_runtime_1.jsx)("span", { className: 'size-[3.75rem] rounded-full bg-white transition-transform duration-150 group-active:scale-90' })] }));
5
+ const PsScanShutter = ({ disabled, label, onCapture }) => ((0, jsx_runtime_1.jsxs)("button", { "aria-label": label, className: 'ps-focus-ring group relative flex size-[4.75rem] shrink-0 items-center justify-center rounded-full disabled:opacity-40', disabled: disabled, onClick: onCapture, title: label, type: 'button', children: [(0, jsx_runtime_1.jsx)("svg", { "aria-hidden": 'true', className: 'absolute inset-0 size-full -rotate-90', viewBox: '0 0 76 76', children: (0, jsx_runtime_1.jsx)("circle", { className: 'stroke-white', cx: '38', cy: '38', fill: 'none', r: '35', strokeWidth: '4' }) }), (0, jsx_runtime_1.jsx)("span", { className: 'size-[3.75rem] rounded-full bg-white transition-transform duration-150 group-active:scale-90' })] }));
6
6
  exports.PsScanShutter = PsScanShutter;
@@ -11,6 +11,6 @@ const PsUploadModal = ({ isOpen, labels, onClose, queue }) => {
11
11
  const text = { ...ps_upload_labels_1.PsUploadLabels, ...labels };
12
12
  const summary = ps_upload_queue_service_1.PsUploadQueueService.summary(queue.items);
13
13
  const title = queue.isBusy ? text.title : summary.failed > 0 ? text.titleFailed : text.titleDone;
14
- return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { isOpen: isOpen, onClose: onClose, size: 'md', title: title, children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-5', children: [(0, jsx_runtime_1.jsx)("ul", { className: 'flex max-h-[50vh] flex-col gap-2 overflow-y-auto', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: text, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center justify-end gap-2 border-t border-line pt-4', children: [queue.isBusy ? ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { onClick: () => queue.items.forEach((item) => queue.cancel(item.id)), type: 'button', variant: 'ghost', children: text.cancelAll })) : null, (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { onClick: onClose, type: 'button', variant: 'primary', children: text.finish })] })] }) }));
14
+ return ((0, jsx_runtime_1.jsx)(ps_popovers_1.PsModal, { footer: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [queue.isBusy ? ((0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { onClick: () => queue.items.forEach((item) => queue.cancel(item.id)), type: 'button', variant: 'surface', children: text.cancelAll })) : null, (0, jsx_runtime_1.jsx)(ps_core_components_1.PsButton, { onClick: onClose, type: 'button', variant: 'primary', children: text.finish })] }), isOpen: isOpen, onClose: onClose, size: 'md', title: title, children: (0, jsx_runtime_1.jsx)("ul", { className: 'flex flex-col gap-2', children: queue.items.map((item) => ((0, jsx_runtime_1.jsx)(ps_upload_row_1.PsUploadRow, { item: item, labels: text, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) }) }));
15
15
  };
16
16
  exports.PsUploadModal = PsUploadModal;