@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.
Files changed (216) hide show
  1. package/dist/components/ps-document-scanner-loader.d.ts +1 -0
  2. package/dist/components/ps-document-scanner-loader.js +40 -0
  3. package/dist/components/ps-document-scanner-sheet.d.ts +2 -0
  4. package/dist/components/ps-document-scanner-sheet.js +186 -0
  5. package/dist/components/ps-document-scanner.d.ts +2 -0
  6. package/dist/components/ps-document-scanner.js +17 -0
  7. package/dist/components/ps-file-picker-actions.d.ts +10 -0
  8. package/dist/components/ps-file-picker-actions.js +21 -0
  9. package/dist/components/ps-file-picker.d.ts +7 -1
  10. package/dist/components/ps-file-picker.js +30 -6
  11. package/dist/components/ps-file-viewer-pane.d.ts +2 -0
  12. package/dist/components/ps-file-viewer-pane.js +39 -0
  13. package/dist/components/ps-scan-button.d.ts +13 -0
  14. package/dist/components/ps-scan-button.js +9 -0
  15. package/dist/components/ps-scan-camera-view.d.ts +27 -0
  16. package/dist/components/ps-scan-camera-view.js +34 -0
  17. package/dist/components/ps-scan-crop-editor.d.ts +13 -0
  18. package/dist/components/ps-scan-crop-editor.js +69 -0
  19. package/dist/components/ps-scan-denied-steps.d.ts +6 -0
  20. package/dist/components/ps-scan-denied-steps.js +33 -0
  21. package/dist/components/ps-scan-icon-button.d.ts +11 -0
  22. package/dist/components/ps-scan-icon-button.js +9 -0
  23. package/dist/components/ps-scan-illustration.d.ts +1 -0
  24. package/dist/components/ps-scan-illustration.js +6 -0
  25. package/dist/components/ps-scan-loupe.d.ts +10 -0
  26. package/dist/components/ps-scan-loupe.js +31 -0
  27. package/dist/components/ps-scan-page-list.d.ts +14 -0
  28. package/dist/components/ps-scan-page-list.js +22 -0
  29. package/dist/components/ps-scan-page-stack.d.ts +7 -0
  30. package/dist/components/ps-scan-page-stack.js +6 -0
  31. package/dist/components/ps-scan-permission-view.d.ts +11 -0
  32. package/dist/components/ps-scan-permission-view.js +48 -0
  33. package/dist/components/ps-scan-photo-button.d.ts +8 -0
  34. package/dist/components/ps-scan-photo-button.js +20 -0
  35. package/dist/components/ps-scan-review.d.ts +24 -0
  36. package/dist/components/ps-scan-review.js +37 -0
  37. package/dist/components/ps-scan-shutter.d.ts +7 -0
  38. package/dist/components/ps-scan-shutter.js +6 -0
  39. package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
  40. package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
  41. package/dist/components/ps-upload-row.js +1 -1
  42. package/dist/constants/ps-document-scanner-labels.d.ts +80 -0
  43. package/dist/constants/ps-document-scanner-labels.js +83 -0
  44. package/dist/constants/ps-file-picker-labels.d.ts +5 -3
  45. package/dist/constants/ps-file-picker-labels.js +5 -3
  46. package/dist/constants/ps-scan-camera-status.d.ts +11 -0
  47. package/dist/constants/ps-scan-camera-status.js +13 -0
  48. package/dist/constants/ps-scan-chrome-classes.d.ts +12 -0
  49. package/dist/constants/ps-scan-chrome-classes.js +15 -0
  50. package/dist/constants/ps-scan-config.d.ts +48 -0
  51. package/dist/constants/ps-scan-config.js +51 -0
  52. package/dist/constants/ps-scan-corner-order.d.ts +1 -0
  53. package/dist/constants/ps-scan-corner-order.js +4 -0
  54. package/dist/constants/ps-scan-filter.d.ts +7 -0
  55. package/dist/constants/ps-scan-filter.js +9 -0
  56. package/dist/constants/ps-scan-output-format.d.ts +5 -0
  57. package/dist/constants/ps-scan-output-format.js +7 -0
  58. package/dist/constants/ps-scan-platform.d.ts +6 -0
  59. package/dist/constants/ps-scan-platform.js +8 -0
  60. package/dist/constants/ps-scan-step.d.ts +6 -0
  61. package/dist/constants/ps-scan-step.js +8 -0
  62. package/dist/esm/components/ps-document-scanner-loader.js +4 -0
  63. package/dist/esm/components/ps-document-scanner-sheet.js +182 -0
  64. package/dist/esm/components/ps-document-scanner.js +13 -0
  65. package/dist/esm/components/ps-file-picker-actions.js +17 -0
  66. package/dist/esm/components/ps-file-picker.js +31 -7
  67. package/dist/esm/components/ps-file-viewer-pane.js +35 -0
  68. package/dist/esm/components/ps-scan-button.js +5 -0
  69. package/dist/esm/components/ps-scan-camera-view.js +30 -0
  70. package/dist/esm/components/ps-scan-crop-editor.js +65 -0
  71. package/dist/esm/components/ps-scan-denied-steps.js +29 -0
  72. package/dist/esm/components/ps-scan-icon-button.js +5 -0
  73. package/dist/esm/components/ps-scan-illustration.js +2 -0
  74. package/dist/esm/components/ps-scan-loupe.js +27 -0
  75. package/dist/esm/components/ps-scan-page-list.js +18 -0
  76. package/dist/esm/components/ps-scan-page-stack.js +2 -0
  77. package/dist/esm/components/ps-scan-permission-view.js +44 -0
  78. package/dist/esm/components/ps-scan-photo-button.js +16 -0
  79. package/dist/esm/components/ps-scan-review.js +33 -0
  80. package/dist/esm/components/ps-scan-shutter.js +2 -0
  81. package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
  82. package/dist/esm/components/ps-upload-row.js +1 -1
  83. package/dist/esm/constants/ps-document-scanner-labels.js +80 -0
  84. package/dist/esm/constants/ps-file-picker-labels.js +5 -3
  85. package/dist/esm/constants/ps-scan-camera-status.js +10 -0
  86. package/dist/esm/constants/ps-scan-chrome-classes.js +12 -0
  87. package/dist/esm/constants/ps-scan-config.js +48 -0
  88. package/dist/esm/constants/ps-scan-corner-order.js +1 -0
  89. package/dist/esm/constants/ps-scan-filter.js +6 -0
  90. package/dist/esm/constants/ps-scan-output-format.js +4 -0
  91. package/dist/esm/constants/ps-scan-platform.js +5 -0
  92. package/dist/esm/constants/ps-scan-step.js +5 -0
  93. package/dist/esm/hooks/use-ps-focus-trap.js +45 -0
  94. package/dist/esm/hooks/use-ps-scan-camera.js +99 -0
  95. package/dist/esm/hooks/use-ps-scan-live-detection.js +45 -0
  96. package/dist/esm/hooks/use-ps-scan-pages.js +62 -0
  97. package/dist/esm/hooks/use-ps-scan-previews.js +57 -0
  98. package/dist/esm/index.js +8 -0
  99. package/dist/esm/models/ps-document-scanner-props.js +1 -0
  100. package/dist/esm/models/ps-document-scanner-sheet-props.js +1 -0
  101. package/dist/esm/models/ps-file-picker-scan.js +1 -0
  102. package/dist/esm/models/ps-file-viewer-pane-props.js +1 -0
  103. package/dist/esm/models/ps-scan-added-page.js +1 -0
  104. package/dist/esm/models/ps-scan-camera-session.js +1 -0
  105. package/dist/esm/models/ps-scan-decoded-image.js +1 -0
  106. package/dist/esm/models/ps-scan-levels.js +1 -0
  107. package/dist/esm/models/ps-scan-live-detection.js +1 -0
  108. package/dist/esm/models/ps-scan-output-options.js +1 -0
  109. package/dist/esm/models/ps-scan-page.js +1 -0
  110. package/dist/esm/models/ps-scan-pdf-page.js +1 -0
  111. package/dist/esm/models/ps-scan-prepared.js +1 -0
  112. package/dist/esm/models/ps-scan-quad.js +1 -0
  113. package/dist/esm/models/ps-scan-raster.js +1 -0
  114. package/dist/esm/models/ps-scan-rendered-page.js +1 -0
  115. package/dist/esm/models/ps-scan-stability.js +1 -0
  116. package/dist/esm/services/ps-scan-camera-service.js +95 -0
  117. package/dist/esm/services/ps-scan-canvas-service.js +52 -0
  118. package/dist/esm/services/ps-scan-edge-service.js +156 -0
  119. package/dist/esm/services/ps-scan-enhance-service.js +97 -0
  120. package/dist/esm/services/ps-scan-file-name-service.js +25 -0
  121. package/dist/esm/services/ps-scan-geometry-service.js +116 -0
  122. package/dist/esm/services/ps-scan-homography-service.js +46 -0
  123. package/dist/esm/services/ps-scan-hull-service.js +59 -0
  124. package/dist/esm/services/ps-scan-image-filter-service.js +52 -0
  125. package/dist/esm/services/ps-scan-order-service.js +13 -0
  126. package/dist/esm/services/ps-scan-output-service.js +24 -0
  127. package/dist/esm/services/ps-scan-pdf-service.js +56 -0
  128. package/dist/esm/services/ps-scan-pipeline-service.js +40 -0
  129. package/dist/esm/services/ps-scan-platform-service.js +15 -0
  130. package/dist/esm/services/ps-scan-raster-service.js +67 -0
  131. package/dist/esm/services/ps-scan-stability-service.js +41 -0
  132. package/dist/esm/services/ps-scan-template-service.js +5 -0
  133. package/dist/esm/services/ps-scan-warp-service.js +35 -0
  134. package/dist/hooks/use-ps-focus-trap.d.ts +2 -0
  135. package/dist/hooks/use-ps-focus-trap.js +49 -0
  136. package/dist/hooks/use-ps-scan-camera.d.ts +11 -0
  137. package/dist/hooks/use-ps-scan-camera.js +103 -0
  138. package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
  139. package/dist/hooks/use-ps-scan-live-detection.js +49 -0
  140. package/dist/hooks/use-ps-scan-pages.d.ts +12 -0
  141. package/dist/hooks/use-ps-scan-pages.js +66 -0
  142. package/dist/hooks/use-ps-scan-previews.d.ts +2 -0
  143. package/dist/hooks/use-ps-scan-previews.js +61 -0
  144. package/dist/index.d.ts +8 -0
  145. package/dist/index.js +8 -0
  146. package/dist/models/ps-document-scanner-props.d.ts +16 -0
  147. package/dist/models/ps-document-scanner-props.js +2 -0
  148. package/dist/models/ps-document-scanner-sheet-props.d.ts +14 -0
  149. package/dist/models/ps-document-scanner-sheet-props.js +2 -0
  150. package/dist/models/ps-file-picker-scan.d.ts +4 -0
  151. package/dist/models/ps-file-picker-scan.js +2 -0
  152. package/dist/models/ps-file-viewer-pane-props.d.ts +12 -0
  153. package/dist/models/ps-file-viewer-pane-props.js +2 -0
  154. package/dist/models/ps-scan-added-page.d.ts +5 -0
  155. package/dist/models/ps-scan-added-page.js +2 -0
  156. package/dist/models/ps-scan-camera-session.d.ts +5 -0
  157. package/dist/models/ps-scan-camera-session.js +2 -0
  158. package/dist/models/ps-scan-decoded-image.d.ts +6 -0
  159. package/dist/models/ps-scan-decoded-image.js +2 -0
  160. package/dist/models/ps-scan-levels.d.ts +4 -0
  161. package/dist/models/ps-scan-levels.js +2 -0
  162. package/dist/models/ps-scan-live-detection.d.ts +5 -0
  163. package/dist/models/ps-scan-live-detection.js +2 -0
  164. package/dist/models/ps-scan-output-options.d.ts +7 -0
  165. package/dist/models/ps-scan-output-options.js +2 -0
  166. package/dist/models/ps-scan-page.d.ts +12 -0
  167. package/dist/models/ps-scan-page.js +2 -0
  168. package/dist/models/ps-scan-pdf-page.d.ts +5 -0
  169. package/dist/models/ps-scan-pdf-page.js +2 -0
  170. package/dist/models/ps-scan-prepared.d.ts +7 -0
  171. package/dist/models/ps-scan-prepared.js +2 -0
  172. package/dist/models/ps-scan-quad.d.ts +2 -0
  173. package/dist/models/ps-scan-quad.js +2 -0
  174. package/dist/models/ps-scan-raster.d.ts +5 -0
  175. package/dist/models/ps-scan-raster.js +2 -0
  176. package/dist/models/ps-scan-rendered-page.d.ts +5 -0
  177. package/dist/models/ps-scan-rendered-page.js +2 -0
  178. package/dist/models/ps-scan-stability.d.ts +8 -0
  179. package/dist/models/ps-scan-stability.js +2 -0
  180. package/dist/services/ps-scan-camera-service.d.ts +14 -0
  181. package/dist/services/ps-scan-camera-service.js +99 -0
  182. package/dist/services/ps-scan-canvas-service.d.ts +9 -0
  183. package/dist/services/ps-scan-canvas-service.js +56 -0
  184. package/dist/services/ps-scan-edge-service.d.ts +9 -0
  185. package/dist/services/ps-scan-edge-service.js +160 -0
  186. package/dist/services/ps-scan-enhance-service.d.ts +9 -0
  187. package/dist/services/ps-scan-enhance-service.js +101 -0
  188. package/dist/services/ps-scan-file-name-service.d.ts +6 -0
  189. package/dist/services/ps-scan-file-name-service.js +29 -0
  190. package/dist/services/ps-scan-geometry-service.d.ts +20 -0
  191. package/dist/services/ps-scan-geometry-service.js +120 -0
  192. package/dist/services/ps-scan-homography-service.d.ts +7 -0
  193. package/dist/services/ps-scan-homography-service.js +50 -0
  194. package/dist/services/ps-scan-hull-service.d.ts +6 -0
  195. package/dist/services/ps-scan-hull-service.js +63 -0
  196. package/dist/services/ps-scan-image-filter-service.d.ts +6 -0
  197. package/dist/services/ps-scan-image-filter-service.js +56 -0
  198. package/dist/services/ps-scan-order-service.d.ts +4 -0
  199. package/dist/services/ps-scan-order-service.js +17 -0
  200. package/dist/services/ps-scan-output-service.d.ts +5 -0
  201. package/dist/services/ps-scan-output-service.js +28 -0
  202. package/dist/services/ps-scan-pdf-service.d.ts +5 -0
  203. package/dist/services/ps-scan-pdf-service.js +60 -0
  204. package/dist/services/ps-scan-pipeline-service.d.ts +10 -0
  205. package/dist/services/ps-scan-pipeline-service.js +44 -0
  206. package/dist/services/ps-scan-platform-service.d.ts +4 -0
  207. package/dist/services/ps-scan-platform-service.js +19 -0
  208. package/dist/services/ps-scan-raster-service.d.ts +6 -0
  209. package/dist/services/ps-scan-raster-service.js +71 -0
  210. package/dist/services/ps-scan-stability-service.d.ts +9 -0
  211. package/dist/services/ps-scan-stability-service.js +45 -0
  212. package/dist/services/ps-scan-template-service.d.ts +3 -0
  213. package/dist/services/ps-scan-template-service.js +9 -0
  214. package/dist/services/ps-scan-warp-service.d.ts +6 -0
  215. package/dist/services/ps-scan-warp-service.js +39 -0
  216. package/package.json +9 -6
@@ -2,10 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsFilePickerLabels = void 0;
4
4
  exports.PsFilePickerLabels = {
5
- browse: 'or click to browse your files',
6
- browseTouch: 'or tap to choose from your device',
5
+ choose: 'Choose files',
6
+ chooseOne: 'Choose a file',
7
+ dragHint: 'Drag and drop files here',
8
+ dragHintOne: 'Drag and drop a file here',
7
9
  dropActive: 'Drop to upload',
8
- formats: 'Accepted formats:',
9
10
  invalidType: 'is not a supported file type.',
11
+ scan: 'Scan with camera',
10
12
  tooLarge: 'is too large.',
11
13
  };
@@ -0,0 +1,11 @@
1
+ export declare const PsScanCameraStatus: {
2
+ readonly Busy: "busy";
3
+ readonly Checking: "checking";
4
+ readonly Denied: "denied";
5
+ readonly Insecure: "insecure";
6
+ readonly Prompt: "prompt";
7
+ readonly Ready: "ready";
8
+ readonly Starting: "starting";
9
+ readonly Unavailable: "unavailable";
10
+ };
11
+ export type PsScanCameraStatus = (typeof PsScanCameraStatus)[keyof typeof PsScanCameraStatus];
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanCameraStatus = void 0;
4
+ exports.PsScanCameraStatus = {
5
+ Busy: 'busy',
6
+ Checking: 'checking',
7
+ Denied: 'denied',
8
+ Insecure: 'insecure',
9
+ Prompt: 'prompt',
10
+ Ready: 'ready',
11
+ Starting: 'starting',
12
+ Unavailable: 'unavailable',
13
+ };
@@ -0,0 +1,12 @@
1
+ export declare const PsScanChromeClasses: {
2
+ readonly button: "ps-focus-ring ps-control ps-control-padding inline-flex max-w-full items-center justify-center gap-2 rounded-ps-control border text-sm font-semibold leading-5 transition disabled:pointer-events-none disabled:opacity-40";
3
+ readonly buttonLarge: "ps-control-large ps-control-padding-large";
4
+ readonly buttonPrimary: "border-primary bg-primary text-[color:var(--ps-color-primary-contrast)] hover:border-primary-dark hover:bg-primary-dark";
5
+ readonly buttonQuiet: "border-white/15 bg-white/10 text-white hover:border-white/25 hover:bg-white/20";
6
+ readonly chip: "ps-focus-ring ps-control inline-flex min-w-0 items-center justify-center rounded-ps-control px-2 text-sm font-semibold transition";
7
+ readonly icon: "ps-focus-ring ps-control ps-control-square inline-flex shrink-0 items-center justify-center rounded-ps-control text-white transition disabled:pointer-events-none disabled:opacity-40";
8
+ readonly iconOff: "bg-black/45 backdrop-blur-md hover:bg-white/20";
9
+ readonly iconOn: "bg-white text-black hover:bg-white/90";
10
+ readonly pill: "pointer-events-none rounded-ps-pill bg-black/55 px-3.5 py-1.5 text-sm font-medium text-white backdrop-blur-md";
11
+ readonly tool: "ps-focus-ring flex min-h-14 min-w-16 flex-col items-center justify-center gap-1 rounded-ps-control px-2 text-xs font-medium text-white/80 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-40";
12
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanChromeClasses = void 0;
4
+ exports.PsScanChromeClasses = {
5
+ button: 'ps-focus-ring ps-control ps-control-padding inline-flex max-w-full items-center justify-center gap-2 rounded-ps-control border text-sm font-semibold leading-5 transition disabled:pointer-events-none disabled:opacity-40',
6
+ buttonLarge: 'ps-control-large ps-control-padding-large',
7
+ buttonPrimary: 'border-primary bg-primary text-[color:var(--ps-color-primary-contrast)] hover:border-primary-dark hover:bg-primary-dark',
8
+ buttonQuiet: 'border-white/15 bg-white/10 text-white hover:border-white/25 hover:bg-white/20',
9
+ chip: 'ps-focus-ring ps-control inline-flex min-w-0 items-center justify-center rounded-ps-control px-2 text-sm font-semibold transition',
10
+ icon: 'ps-focus-ring ps-control ps-control-square inline-flex shrink-0 items-center justify-center rounded-ps-control text-white transition disabled:pointer-events-none disabled:opacity-40',
11
+ iconOff: 'bg-black/45 backdrop-blur-md hover:bg-white/20',
12
+ iconOn: 'bg-white text-black hover:bg-white/90',
13
+ pill: 'pointer-events-none rounded-ps-pill bg-black/55 px-3.5 py-1.5 text-sm font-medium text-white backdrop-blur-md',
14
+ tool: 'ps-focus-ring flex min-h-14 min-w-16 flex-col items-center justify-center gap-1 rounded-ps-control px-2 text-xs font-medium text-white/80 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-40',
15
+ };
@@ -0,0 +1,48 @@
1
+ export declare const PsScanConfig: {
2
+ readonly adaptiveRatio: 0.92;
3
+ readonly adaptiveWindowDivisor: 16;
4
+ readonly adaptiveWindowMin: 15;
5
+ readonly autoCaptureMs: 1000;
6
+ readonly cameraPermission: "camera";
7
+ readonly capturedJpegQuality: 0.92;
8
+ readonly cornerNudge: 0.005;
9
+ readonly cornerNudgeLarge: 0.025;
10
+ readonly decodeMaxDimension: 3000;
11
+ readonly defaultFileName: "scan";
12
+ readonly defaultJpegQuality: 0.82;
13
+ readonly defaultMaxDimension: 2000;
14
+ readonly defaultMaxPages: 30;
15
+ readonly detectDilations: 2;
16
+ readonly detectInset: 3;
17
+ readonly detectMaxDimension: 320;
18
+ readonly detectMinAreaRatio: 0.12;
19
+ readonly detectMaxAreaRatio: 0.995;
20
+ readonly detectMinEdgeStrength: 24;
21
+ readonly detectMinHullFill: 0.85;
22
+ readonly detectPercentile: 0.98;
23
+ readonly detectRelativeStrength: 0.3;
24
+ readonly guideArm: 36;
25
+ readonly guideArmRatio: 0.18;
26
+ readonly guideAspect: 0.72;
27
+ readonly guideHeightRatio: 0.58;
28
+ readonly guideLift: 0.03;
29
+ readonly guideWidthRatio: 0.76;
30
+ readonly hullCandidateLimit: 18;
31
+ readonly levelHighPercentile: 0.99;
32
+ readonly levelLowPercentile: 0.01;
33
+ readonly liveDetectIntervalMs: 160;
34
+ readonly liveDetectMaxDimension: 240;
35
+ readonly liveMissLimit: 2;
36
+ readonly liveRearmMovement: 0.08;
37
+ readonly liveSmoothing: 0.5;
38
+ readonly liveStillMovement: 0.02;
39
+ readonly loupeOffset: 72;
40
+ readonly loupeSize: 112;
41
+ readonly loupeZoom: 2.5;
42
+ readonly minCornerGap: 0.02;
43
+ readonly pdfDpi: 150;
44
+ readonly pdfPointsPerInch: 72;
45
+ readonly previewDebounceMs: 120;
46
+ readonly previewMaxDimension: 1400;
47
+ readonly torchConstraint: "torch";
48
+ };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanConfig = void 0;
4
+ exports.PsScanConfig = {
5
+ adaptiveRatio: 0.92,
6
+ adaptiveWindowDivisor: 16,
7
+ adaptiveWindowMin: 15,
8
+ autoCaptureMs: 1000,
9
+ cameraPermission: 'camera',
10
+ capturedJpegQuality: 0.92,
11
+ cornerNudge: 0.005,
12
+ cornerNudgeLarge: 0.025,
13
+ decodeMaxDimension: 3000,
14
+ defaultFileName: 'scan',
15
+ defaultJpegQuality: 0.82,
16
+ defaultMaxDimension: 2000,
17
+ defaultMaxPages: 30,
18
+ detectDilations: 2,
19
+ detectInset: 3,
20
+ detectMaxDimension: 320,
21
+ detectMinAreaRatio: 0.12,
22
+ detectMaxAreaRatio: 0.995,
23
+ detectMinEdgeStrength: 24,
24
+ detectMinHullFill: 0.85,
25
+ detectPercentile: 0.98,
26
+ detectRelativeStrength: 0.3,
27
+ guideArm: 36,
28
+ guideArmRatio: 0.18,
29
+ guideAspect: 0.72,
30
+ guideHeightRatio: 0.58,
31
+ guideLift: 0.03,
32
+ guideWidthRatio: 0.76,
33
+ hullCandidateLimit: 18,
34
+ levelHighPercentile: 0.99,
35
+ levelLowPercentile: 0.01,
36
+ liveDetectIntervalMs: 160,
37
+ liveDetectMaxDimension: 240,
38
+ liveMissLimit: 2,
39
+ liveRearmMovement: 0.08,
40
+ liveSmoothing: 0.5,
41
+ liveStillMovement: 0.02,
42
+ loupeOffset: 72,
43
+ loupeSize: 112,
44
+ loupeZoom: 2.5,
45
+ minCornerGap: 0.02,
46
+ pdfDpi: 150,
47
+ pdfPointsPerInch: 72,
48
+ previewDebounceMs: 120,
49
+ previewMaxDimension: 1400,
50
+ torchConstraint: 'torch',
51
+ };
@@ -0,0 +1 @@
1
+ export declare const PsScanCornerOrder: readonly ["topLeft", "topRight", "bottomRight", "bottomLeft"];
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanCornerOrder = void 0;
4
+ exports.PsScanCornerOrder = ['topLeft', 'topRight', 'bottomRight', 'bottomLeft'];
@@ -0,0 +1,7 @@
1
+ export declare const PsScanFilter: {
2
+ readonly Colour: "colour";
3
+ readonly Document: "document";
4
+ readonly Greyscale: "greyscale";
5
+ readonly Original: "original";
6
+ };
7
+ export type PsScanFilter = (typeof PsScanFilter)[keyof typeof PsScanFilter];
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanFilter = void 0;
4
+ exports.PsScanFilter = {
5
+ Colour: 'colour',
6
+ Document: 'document',
7
+ Greyscale: 'greyscale',
8
+ Original: 'original',
9
+ };
@@ -0,0 +1,5 @@
1
+ export declare const PsScanOutputFormat: {
2
+ readonly Jpeg: "jpeg";
3
+ readonly Pdf: "pdf";
4
+ };
5
+ export type PsScanOutputFormat = (typeof PsScanOutputFormat)[keyof typeof PsScanOutputFormat];
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanOutputFormat = void 0;
4
+ exports.PsScanOutputFormat = {
5
+ Jpeg: 'jpeg',
6
+ Pdf: 'pdf',
7
+ };
@@ -0,0 +1,6 @@
1
+ export declare const PsScanPlatform: {
2
+ readonly Android: "android";
3
+ readonly Desktop: "desktop";
4
+ readonly Ios: "ios";
5
+ };
6
+ export type PsScanPlatform = (typeof PsScanPlatform)[keyof typeof PsScanPlatform];
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanPlatform = void 0;
4
+ exports.PsScanPlatform = {
5
+ Android: 'android',
6
+ Desktop: 'desktop',
7
+ Ios: 'ios',
8
+ };
@@ -0,0 +1,6 @@
1
+ export declare const PsScanStep: {
2
+ readonly Camera: "camera";
3
+ readonly Crop: "crop";
4
+ readonly Review: "review";
5
+ };
6
+ export type PsScanStep = (typeof PsScanStep)[keyof typeof PsScanStep];
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanStep = void 0;
4
+ exports.PsScanStep = {
5
+ Camera: 'camera',
6
+ Crop: 'crop',
7
+ Review: 'review',
8
+ };
@@ -0,0 +1,4 @@
1
+ import { lazy } from 'react';
2
+ export const PsDocumentScannerLoader = lazy(() => import('./ps-document-scanner-sheet').then((module) => ({
3
+ default: module.PsDocumentScannerSheet,
4
+ })));
@@ -0,0 +1,182 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useRef, useState } from 'react';
3
+ import { PsLayer, usePsLayerZIndex, usePsRestoreFocus, usePsScrollLock, usePsViewport, } from '@pallasoft/ps-core-components';
4
+ import { PallasoftViewportTier, PallasoftZIndex } from '@pallasoft/ps-design-system';
5
+ import { PsIconKey } from '@pallasoft/ps-icons';
6
+ import { createPortal } from 'react-dom';
7
+ import { PsScanCameraStatus } from '../constants/ps-scan-camera-status';
8
+ import { PsScanStep } from '../constants/ps-scan-step';
9
+ import { usePsFocusTrap } from '../hooks/use-ps-focus-trap';
10
+ import { usePsScanCamera } from '../hooks/use-ps-scan-camera';
11
+ import { usePsScanLiveDetection } from '../hooks/use-ps-scan-live-detection';
12
+ import { usePsScanPages } from '../hooks/use-ps-scan-pages';
13
+ import { usePsScanPreviews } from '../hooks/use-ps-scan-previews';
14
+ import { PsScanOutputService } from '../services/ps-scan-output-service';
15
+ import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
16
+ import { PsScanTemplateService } from '../services/ps-scan-template-service';
17
+ import { PsScanCameraView } from './ps-scan-camera-view';
18
+ import { PsScanCropEditor } from './ps-scan-crop-editor';
19
+ import { PsScanIconButton } from './ps-scan-icon-button';
20
+ import { PsScanPermissionView } from './ps-scan-permission-view';
21
+ import { PsScanReview } from './ps-scan-review';
22
+ export const PsDocumentScannerSheet = ({ fileName, isAutoCapture, jpegQuality, labels, maxDimension, maxPages, onClose, onComplete, outputFormat, }) => {
23
+ const tier = usePsViewport();
24
+ const zIndex = usePsLayerZIndex(PallasoftZIndex.Modal);
25
+ const content = useRef(null);
26
+ const { add, move, pages, remove, replace, setCorners, update } = usePsScanPages();
27
+ const [step, setStep] = useState(PsScanStep.Camera);
28
+ const [cropReturn, setCropReturn] = useState(PsScanStep.Camera);
29
+ const [activeId, setActiveId] = useState('');
30
+ const [retakeId, setRetakeId] = useState('');
31
+ const [announcement, setAnnouncement] = useState('');
32
+ const [error, setError] = useState('');
33
+ const [isBusy, setIsBusy] = useState(false);
34
+ const [isCapturing, setIsCapturing] = useState(false);
35
+ const [isAuto, setIsAuto] = useState(isAutoCapture);
36
+ const [flashKey, setFlashKey] = useState(0);
37
+ const camera = usePsScanCamera(step === PsScanStep.Camera);
38
+ const isReady = camera.status === PsScanCameraStatus.Ready;
39
+ const isFull = pages.length >= maxPages;
40
+ const previews = usePsScanPreviews(pages);
41
+ const page = pages.find((item) => item.id === activeId);
42
+ const lastPage = pages[pages.length - 1];
43
+ const isDesktop = tier === PallasoftViewportTier.Desktop;
44
+ const isCameraStep = step === PsScanStep.Camera;
45
+ const showsCamera = isReady || camera.status === PsScanCameraStatus.Starting;
46
+ usePsScrollLock(true);
47
+ usePsRestoreFocus(true);
48
+ usePsFocusTrap(content, true, onClose);
49
+ const edgeMessage = (isDetected) => isDetected ? labels.edgesFound : labels.edgesNotFound;
50
+ const addFiles = async (files, after) => {
51
+ setError('');
52
+ try {
53
+ const images = files.filter((file) => file.type === '' || file.type.startsWith('image/'));
54
+ if (retakeId !== '' && images.length > 0) {
55
+ const added = await replace(retakeId, images[0]);
56
+ setActiveId(retakeId);
57
+ setRetakeId('');
58
+ setStep(PsScanStep.Crop);
59
+ setAnnouncement(edgeMessage(added.isDetected));
60
+ return;
61
+ }
62
+ const room = Math.max(0, maxPages - pages.length);
63
+ if (images.length > room)
64
+ setError(labels.maxPages);
65
+ const added = await add(images.slice(0, room));
66
+ if (added.length === 0)
67
+ return;
68
+ const position = pages.length + added.length;
69
+ setActiveId(added[added.length - 1].page.id);
70
+ setAnnouncement(PsScanTemplateService.fill(labels.pageAdded, { position }));
71
+ if (after === null)
72
+ return;
73
+ if (added.length === 1) {
74
+ setCropReturn(after);
75
+ setStep(PsScanStep.Crop);
76
+ setAnnouncement(edgeMessage(added[0].isDetected));
77
+ }
78
+ else
79
+ setStep(PsScanStep.Review);
80
+ }
81
+ catch {
82
+ setError(labels.processFailed);
83
+ }
84
+ };
85
+ const capture = async (isAutomatic) => {
86
+ if (isCapturing || isFull)
87
+ return;
88
+ setIsCapturing(true);
89
+ try {
90
+ const blob = await camera.capture();
91
+ if (blob === null)
92
+ return;
93
+ setFlashKey((current) => current + 1);
94
+ await addFiles([blob], isAutomatic && retakeId === '' ? null : PsScanStep.Camera);
95
+ }
96
+ finally {
97
+ setIsCapturing(false);
98
+ }
99
+ };
100
+ const detection = usePsScanLiveDetection(camera.videoRef, isCameraStep && isReady, isAuto, () => capture(true));
101
+ const autoDetect = async () => {
102
+ if (page === undefined)
103
+ return;
104
+ const prepared = await PsScanPipelineService.prepare(page.source);
105
+ setCorners(page.id, prepared.corners);
106
+ setAnnouncement(edgeMessage(prepared.isDetected));
107
+ };
108
+ const keep = () => setStep(cropReturn === PsScanStep.Camera && !isFull ? PsScanStep.Camera : PsScanStep.Review);
109
+ const retake = () => {
110
+ if (page === undefined)
111
+ return;
112
+ if (step === PsScanStep.Review)
113
+ setCropReturn(PsScanStep.Review);
114
+ setRetakeId(page.id);
115
+ setStep(PsScanStep.Camera);
116
+ };
117
+ const openCamera = () => {
118
+ setRetakeId('');
119
+ setCropReturn(PsScanStep.Camera);
120
+ setStep(PsScanStep.Camera);
121
+ };
122
+ const openReview = () => {
123
+ if (pages.length === 0)
124
+ return;
125
+ setRetakeId('');
126
+ if (page === undefined)
127
+ setActiveId(lastPage.id);
128
+ setStep(PsScanStep.Review);
129
+ };
130
+ const editCrop = () => {
131
+ setCropReturn(PsScanStep.Review);
132
+ setStep(PsScanStep.Crop);
133
+ };
134
+ const deletePage = () => {
135
+ if (page === undefined)
136
+ return;
137
+ const index = pages.findIndex((item) => item.id === page.id);
138
+ const rest = pages.filter((item) => item.id !== page.id);
139
+ remove(page.id);
140
+ setAnnouncement(labels.pageDeleted);
141
+ if (rest.length === 0) {
142
+ setActiveId('');
143
+ openCamera();
144
+ }
145
+ else
146
+ setActiveId(rest[Math.min(index, rest.length - 1)].id);
147
+ };
148
+ const finish = async () => {
149
+ setIsBusy(true);
150
+ setError('');
151
+ try {
152
+ const files = await PsScanOutputService.build(pages, {
153
+ fileName,
154
+ format: outputFormat,
155
+ jpegQuality,
156
+ maxDimension,
157
+ });
158
+ onComplete(files);
159
+ onClose();
160
+ }
161
+ catch {
162
+ setError(labels.processFailed);
163
+ setIsBusy(false);
164
+ }
165
+ };
166
+ const moveTo = useCallback((from, to) => {
167
+ move(from, to);
168
+ setAnnouncement(PsScanTemplateService.fill(labels.pageMoved, {
169
+ count: pages.length,
170
+ page: `${labels.page} ${from + 1}`,
171
+ position: to + 1,
172
+ }));
173
+ }, [move, pages.length, labels]);
174
+ const fromLibrary = (files) => addFiles(files, PsScanStep.Review);
175
+ if (typeof document === 'undefined')
176
+ return null;
177
+ return createPortal(_jsx("div", { "aria-label": labels.title, "aria-modal": 'true', className: isDesktop
178
+ ? 'fixed inset-0 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm animate-ps-fade-in'
179
+ : 'fixed inset-0 bg-black animate-ps-fade-in', role: 'dialog', style: { zIndex }, children: _jsx(PsLayer, { zIndex: zIndex, children: _jsxs("div", { className: isDesktop
180
+ ? 'relative h-[min(52rem,calc(100dvh-3rem))] w-[min(76rem,calc(100vw-3rem))] overflow-hidden rounded-2xl bg-black text-white shadow-2xl ring-1 ring-white/10 animate-ps-scale-in'
181
+ : 'relative h-full overflow-hidden bg-black text-white', ref: content, children: [_jsx("p", { "aria-live": 'polite', className: 'sr-only', role: 'status', children: announcement }), isCameraStep && showsCamera ? (_jsx(PsScanCameraView, { canTorch: camera.canTorch, detection: detection, flashKey: flashKey, isAuto: isAuto, isBusy: isCapturing, isFull: isFull, isMultiple: maxPages > 1, isReady: isReady, isTorchOn: camera.isTorchOn, labels: labels, lastPreview: lastPage === undefined ? undefined : previews[lastPage.id], onCapture: () => capture(false), onClose: onClose, onDone: openReview, onFiles: fromLibrary, onToggleAuto: () => setIsAuto((current) => !current), onToggleTorch: camera.toggleTorch, pageCount: pages.length, videoRef: camera.videoRef, videoTarget: camera.attachVideo })) : null, isCameraStep && !showsCamera ? (_jsxs(_Fragment, { children: [_jsx(PsScanPermissionView, { isMultiple: maxPages > 1, labels: labels, onAllow: camera.allow, onFiles: fromLibrary, status: camera.status }), _jsx("div", { className: 'absolute left-4 top-[max(1rem,env(safe-area-inset-top))] flex gap-2', children: _jsx(PsScanIconButton, { iconKey: PsIconKey.Close, label: labels.close, onClick: onClose }) }), pages.length > 0 ? (_jsx("div", { className: 'absolute right-4 top-[max(1rem,env(safe-area-inset-top))]', children: _jsx(PsScanIconButton, { iconKey: PsIconKey.Check, label: PsScanTemplateService.fill(labels.viewPages, { count: pages.length }), onClick: openReview }) })) : null] })) : null, step === PsScanStep.Crop && page !== undefined ? (_jsx(PsScanCropEditor, { labels: labels, onAutoDetect: autoDetect, onChange: (corners) => setCorners(page.id, corners), onKeep: keep, onRetake: retake, page: page })) : null, step === PsScanStep.Review && page !== undefined ? (_jsx(PsScanReview, { canAddPage: !isFull, error: error, isBusy: isBusy, labels: labels, onAddPage: openCamera, onClose: onClose, onDelete: deletePage, onEditCrop: editCrop, onFilter: (filter) => update(page.id, { filter }), onFinish: finish, onMove: moveTo, onRetake: retake, onRotate: () => update(page.id, { rotation: (page.rotation + 90) % 360 }), onSelect: setActiveId, page: page, pages: pages, previews: previews })) : null, isCameraStep && error !== '' ? (_jsx("p", { className: 'absolute inset-x-4 bottom-36 mx-auto max-w-sm rounded-ps-control bg-danger px-3 py-2 text-center text-sm text-white', role: 'alert', children: error })) : null] }) }) }), document.body);
182
+ };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense } from 'react';
3
+ import { usePsHydrated } from '@pallasoft/ps-core-components';
4
+ import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
5
+ import { PsScanConfig } from '../constants/ps-scan-config';
6
+ import { PsScanOutputFormat } from '../constants/ps-scan-output-format';
7
+ import { PsDocumentScannerLoader } from './ps-document-scanner-loader';
8
+ export const PsDocumentScanner = ({ fileName = PsScanConfig.defaultFileName, isAutoCapture = false, isOpen, jpegQuality = PsScanConfig.defaultJpegQuality, labels, maxDimension = PsScanConfig.defaultMaxDimension, maxPages = PsScanConfig.defaultMaxPages, onClose, onComplete, outputFormat = PsScanOutputFormat.Pdf, }) => {
9
+ const isHydrated = usePsHydrated();
10
+ if (!isOpen || !isHydrated)
11
+ return null;
12
+ return (_jsx(Suspense, { fallback: null, children: _jsx(PsDocumentScannerLoader, { fileName: fileName, isAutoCapture: isAutoCapture, jpegQuality: jpegQuality, labels: { ...PsDocumentScannerLabels, ...labels }, maxDimension: maxDimension, maxPages: maxPages, onClose: onClose, onComplete: onComplete, outputFormat: outputFormat }) }));
13
+ };
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PsButton } from '@pallasoft/ps-core-components';
3
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
+ import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
5
+ export const PsFilePickerActions = ({ chooseLabel, className, disabled, isTouch, onChoose, onScan, scanLabel, }) => {
6
+ const hasScan = scanLabel !== undefined;
7
+ const choose = (_jsx(PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Upload }), onClick: (event) => {
8
+ event.stopPropagation();
9
+ onChoose();
10
+ }, size: isTouch ? 'large' : 'default', variant: hasScan && isTouch ? 'surface' : hasScan || isTouch ? 'primary' : 'surface', children: chooseLabel }));
11
+ return (_jsx("div", { className: PallasoftClassNameService.merge(isTouch
12
+ ? 'w-full max-w-xs flex-col gap-2'
13
+ : 'flex-row flex-wrap items-center justify-center gap-2', className), children: hasScan ? (_jsxs(_Fragment, { children: [isTouch ? null : choose, _jsx(PsButton, { className: isTouch ? 'w-full' : '', disabled: disabled, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Camera }), onClick: (event) => {
14
+ event.stopPropagation();
15
+ onScan();
16
+ }, size: isTouch ? 'large' : 'default', variant: isTouch ? 'primary' : 'surface', children: scanLabel }), isTouch ? choose : null] })) : (choose) }));
17
+ };
@@ -1,15 +1,26 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from 'react';
2
+ import { useMemo, useRef, useState } from 'react';
3
3
  import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
4
  import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
5
5
  import { PsFilePickerLabels } from '../constants/ps-file-picker-labels';
6
+ import { PsScanConfig } from '../constants/ps-scan-config';
7
+ import { PsUploadLabels } from '../constants/ps-upload-labels';
6
8
  import { PsFileSelectionService } from '../services/ps-file-selection-service';
7
- export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLabel, isLoading = false, labels, maxSizeBytes, maxSizeLabel, multiple = false, onError, onFileLoad, onFilesSelect, touchLabel, }) => {
9
+ import { PsDocumentScanner } from './ps-document-scanner';
10
+ import { PsFilePickerActions } from './ps-file-picker-actions';
11
+ import { PsUploadRow } from './ps-upload-row';
12
+ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false, dropLabel, isLoading = false, labels, maxSizeBytes, maxSizeLabel, multiple = false, onError, onFileLoad, onFilesSelect, queue, scan, touchLabel, uploadLabels, }) => {
8
13
  const [isDragging, setIsDragging] = useState(false);
14
+ const [scanName, setScanName] = useState(null);
15
+ const input = useRef(null);
9
16
  const acceptAttr = useMemo(() => accept.join(','), [accept]);
10
17
  const text = { ...PsFilePickerLabels, ...labels };
18
+ const rowLabels = { ...PsUploadLabels, ...uploadLabels };
19
+ const details = [acceptLabel, maxSizeLabel].filter((value) => value !== undefined && value !== '');
20
+ const chooseLabel = multiple ? text.choose : text.chooseOne;
21
+ const scanLabel = scan === undefined ? undefined : text.scan;
11
22
  if (isLoading)
12
- return (_jsx("span", { "aria-busy": 'true', className: PallasoftClassNameService.merge('ps-skeleton block min-h-32 w-full rounded-ps-panel', className), role: 'status' }));
23
+ return (_jsx("span", { "aria-busy": 'true', className: PallasoftClassNameService.merge('ps-skeleton block min-h-48 w-full rounded-ps-control', className), role: 'status' }));
13
24
  const select = (list) => {
14
25
  if (list === null || disabled)
15
26
  return;
@@ -28,6 +39,15 @@ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false,
28
39
  else
29
40
  onFileLoad?.(accepted[0]);
30
41
  };
42
+ const browse = () => {
43
+ if (!disabled)
44
+ input.current?.click();
45
+ };
46
+ const openScan = () => {
47
+ if (disabled || scan === undefined)
48
+ return;
49
+ setScanName(scan.getFileName === undefined ? PsScanConfig.defaultFileName : scan.getFileName());
50
+ };
31
51
  const onChange = (event) => {
32
52
  select(event.target.files);
33
53
  event.target.value = '';
@@ -37,8 +57,12 @@ export const PsFilePicker = ({ accept, acceptLabel, className, disabled = false,
37
57
  if (!disabled)
38
58
  setIsDragging(isOver);
39
59
  };
40
- return (_jsxs("div", { className: PallasoftClassNameService.merge('relative flex h-48 w-full flex-col items-center justify-center rounded-ps-control border-2 border-dashed border-line p-4 transition-all hover:border-primary/50 hover:bg-primary/5 sm:h-64', isDragging ? 'border-primary bg-primary/10' : '', disabled ? 'pointer-events-none opacity-60' : '', className), onDragEnter: (event) => onDrag(event, true), onDragLeave: (event) => onDrag(event, false), onDragOver: (event) => onDrag(event, true), onDrop: (event) => {
41
- onDrag(event, false);
42
- select(event.dataTransfer.files);
43
- }, children: [_jsxs("div", { className: 'pointer-events-none flex flex-col items-center gap-3 text-center', children: [_jsx(PsIcon, { className: 'h-8 w-8 text-primary', iconKey: PsIconKey.Upload }), _jsxs("div", { children: [_jsx("p", { className: 'text-body-2 font-medium text-ink sm:text-body-1', children: isDragging ? (text.dropActive) : (_jsxs(_Fragment, { children: [_jsx("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : (_jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), _jsxs("p", { className: 'mt-1 text-body-3 text-ink/60 sm:text-body-2', children: [_jsx("span", { className: '[@media(pointer:coarse)]:hidden', children: text.browse }), _jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: text.browseTouch })] }), maxSizeLabel ? _jsx("p", { className: 'mt-3 text-body-3 text-ink/60', children: maxSizeLabel }) : null, _jsxs("p", { className: 'text-body-3 text-ink/60', children: [text.formats, " ", acceptLabel] })] })] }), _jsx("input", { accept: acceptAttr, "aria-disabled": disabled, "aria-label": dropLabel, className: 'absolute inset-0 h-full w-full cursor-pointer opacity-0', disabled: disabled, multiple: multiple, onChange: onChange, type: 'file' })] }));
60
+ return (_jsxs("div", { className: PallasoftClassNameService.merge('flex w-full flex-col gap-3', className), children: [_jsxs("div", { "aria-disabled": disabled || undefined, className: PallasoftClassNameService.merge('flex w-full cursor-pointer flex-col items-center gap-5 rounded-ps-control border-2 border-dashed px-5 py-8 text-center transition-colors duration-200 sm:px-8', isDragging
61
+ ? 'border-primary bg-primary/5'
62
+ : 'border-line bg-surface/40 hover:border-primary/40 hover:bg-primary/[0.03]', disabled ? 'pointer-events-none opacity-60' : ''), onClick: browse, onDragEnter: (event) => onDrag(event, true), onDragLeave: (event) => onDrag(event, false), onDragOver: (event) => onDrag(event, true), onDrop: (event) => {
63
+ onDrag(event, false);
64
+ select(event.dataTransfer.files);
65
+ }, children: [_jsx("span", { "aria-hidden": 'true', className: PallasoftClassNameService.merge('flex size-12 items-center justify-center rounded-full transition-colors duration-200', isDragging
66
+ ? 'bg-primary text-[color:var(--ps-color-primary-contrast)]'
67
+ : 'bg-primary-soft text-primary'), children: _jsx(PsIcon, { className: 'size-6', iconKey: PsIconKey.Upload }) }), _jsxs("div", { className: 'flex flex-col gap-1', children: [_jsx("p", { className: 'text-base font-semibold text-ink', children: isDragging ? (text.dropActive) : (_jsxs(_Fragment, { children: [_jsx("span", { className: touchLabel === undefined ? '' : '[@media(pointer:coarse)]:hidden', children: dropLabel }), touchLabel === undefined ? null : (_jsx("span", { className: 'hidden [@media(pointer:coarse)]:inline', children: touchLabel }))] })) }), _jsx("p", { className: 'text-sm text-ink/60 [@media(pointer:coarse)]:hidden', children: multiple ? text.dragHint : text.dragHintOne })] }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'flex [@media(pointer:coarse)]:hidden', disabled: disabled, isTouch: false, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), _jsx(PsFilePickerActions, { chooseLabel: chooseLabel, className: 'hidden [@media(pointer:coarse)]:flex', disabled: disabled, isTouch: true, onChoose: browse, onScan: openScan, scanLabel: scanLabel }), details.length > 0 ? _jsx("p", { className: 'text-xs text-ink/50', children: details.join(' · ') }) : null, _jsx("input", { accept: acceptAttr, "aria-hidden": 'true', className: 'sr-only', disabled: disabled, multiple: multiple, onChange: onChange, onClick: (event) => event.stopPropagation(), ref: input, tabIndex: -1, type: 'file' })] }), queue === undefined || queue.items.length === 0 ? null : (_jsx("ul", { "aria-label": rowLabels.title, className: 'flex flex-col gap-2', children: queue.items.map((item) => (_jsx(PsUploadRow, { item: item, labels: rowLabels, onCancel: () => queue.cancel(item.id), onRemove: () => queue.remove(item.id), onRetry: () => queue.retry(item.id) }, item.id))) })), scan === undefined ? null : (_jsx(PsDocumentScanner, { ...scan, fileName: scanName === null ? PsScanConfig.defaultFileName : scanName, isOpen: scanName !== null, onClose: () => setScanName(null), onComplete: select }))] }));
44
68
  };
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
4
+ import { PsFileViewerConfig } from '../constants/ps-file-viewer-config';
5
+ import { PsFileViewerKinds } from '../constants/ps-file-viewer-kinds';
6
+ import { PsFileViewerLabels } from '../constants/ps-file-viewer-labels';
7
+ import { PsFileTypeService } from '../services/ps-file-type-service';
8
+ import { PsFileViewerButton } from './ps-file-viewer-button';
9
+ import { PsFileViewerContent } from './ps-file-viewer-content';
10
+ export const PsFileViewerPane = ({ contentType, headerActions, id, labels = PsFileViewerLabels, name, pdfWorkerSrc, url, }) => {
11
+ const [zoom, setZoom] = useState(1);
12
+ const [rotation, setRotation] = useState(0);
13
+ const kind = PsFileTypeService.kindOf(contentType, name);
14
+ const canZoom = PsFileViewerKinds.zoomable.includes(kind);
15
+ const canRotate = PsFileViewerKinds.rotatable.includes(kind);
16
+ const changeZoom = (step) => setZoom((current) => Math.min(PsFileViewerConfig.maxZoom, Math.max(PsFileViewerConfig.minZoom, current + step)));
17
+ const fileKey = id === undefined ? url : id;
18
+ const rotate = () => setRotation((current) => (current + PsFileViewerConfig.rotateStep) % 360);
19
+ const onKeyDown = (event) => {
20
+ if (event.metaKey || event.ctrlKey || event.altKey)
21
+ return;
22
+ if (canZoom && (event.key === '+' || event.key === '='))
23
+ changeZoom(PsFileViewerConfig.zoomStep);
24
+ else if (canZoom && (event.key === '-' || event.key === '_'))
25
+ changeZoom(-PsFileViewerConfig.zoomStep);
26
+ else if (canZoom && event.key === '0')
27
+ setZoom(1);
28
+ else if (canRotate && event.key.toLowerCase() === 'r')
29
+ rotate();
30
+ else
31
+ return;
32
+ event.preventDefault();
33
+ };
34
+ return (_jsxs("div", { className: 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-ps-panel bg-black/85', children: [_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: [_jsx("p", { className: 'min-w-0 flex-1 basis-32 truncate px-1 text-xs text-white/70', title: name, children: name }), _jsxs("div", { className: 'flex items-center gap-0.5', role: 'toolbar', children: [canZoom ? (_jsxs(_Fragment, { children: [_jsx(PsFileViewerButton, { disabled: zoom <= PsFileViewerConfig.minZoom, label: labels.zoomOut, onClick: () => changeZoom(-PsFileViewerConfig.zoomStep), children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.ZoomOut }) }), _jsxs(PsFileViewerButton, { className: 'w-14 text-xs', label: labels.zoomReset, onClick: () => setZoom(1), children: [Math.round(zoom * 100), "%"] }), _jsx(PsFileViewerButton, { disabled: zoom >= PsFileViewerConfig.maxZoom, label: labels.zoomIn, onClick: () => changeZoom(PsFileViewerConfig.zoomStep), children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.ZoomIn }) })] })) : null, canRotate ? (_jsx(PsFileViewerButton, { label: labels.rotate, onClick: rotate, children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Rotate }) })) : null, headerActions] })] }), _jsx("div", { className: 'ps-focus-ring flex min-h-0 flex-1 p-2', onKeyDown: onKeyDown, tabIndex: 0, children: _jsx(PsFileViewerContent, { activeMatch: 0, file: { contentType, id: fileKey, name, url }, labels: labels, onMatchCount: () => undefined, pdfWorkerSrc: pdfWorkerSrc, query: '', rotation: rotation, zoom: zoom }, fileKey) })] }));
35
+ };
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PsSpinner } from '@pallasoft/ps-core-components';
3
+ import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
+ import { PsScanChromeClasses } from '../constants/ps-scan-chrome-classes';
5
+ export const PsScanButton = ({ children, className, disabled = false, isLarge = false, isLoading = false, isPrimary = false, leadingIcon, loadingLabel, onClick, }) => (_jsxs("button", { "aria-busy": isLoading || undefined, className: PallasoftClassNameService.merge(PsScanChromeClasses.button, isLarge ? PsScanChromeClasses.buttonLarge : '', isPrimary ? PsScanChromeClasses.buttonPrimary : PsScanChromeClasses.buttonQuiet, 'relative', className), disabled: disabled || isLoading, onClick: onClick, type: 'button', children: [isLoading ? (_jsx(PsSpinner, { className: 'absolute inset-0 m-auto', color: 'current', label: loadingLabel === undefined ? '' : loadingLabel, size: 'small' })) : null, _jsxs("span", { "aria-hidden": isLoading || undefined, className: `inline-flex min-w-0 items-center gap-2 ${isLoading ? 'invisible' : ''}`, children: [leadingIcon === undefined ? null : _jsx("span", { className: 'shrink-0', children: leadingIcon }), _jsx("span", { className: 'truncate', children: children })] })] }));