@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
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanRegionService = void 0;
4
+ class PsScanRegionService {
5
+ static enclosed(mask, width, height, minArea) {
6
+ const seen = new Uint8Array(mask.length);
7
+ const regions = [];
8
+ for (let start = 0; start < mask.length; start += 1) {
9
+ if (mask[start] === 1 || seen[start] === 1)
10
+ continue;
11
+ const boundary = [];
12
+ const stack = [start];
13
+ let area = 0;
14
+ seen[start] = 1;
15
+ while (stack.length > 0) {
16
+ const index = stack.pop();
17
+ if (index === undefined)
18
+ break;
19
+ const x = index % width;
20
+ const y = (index - x) / width;
21
+ let isBoundary = false;
22
+ area += 1;
23
+ const visit = (next) => {
24
+ if (mask[next] === 1)
25
+ isBoundary = true;
26
+ else if (seen[next] === 0) {
27
+ seen[next] = 1;
28
+ stack.push(next);
29
+ }
30
+ };
31
+ if (x > 0)
32
+ visit(index - 1);
33
+ else
34
+ isBoundary = true;
35
+ if (x < width - 1)
36
+ visit(index + 1);
37
+ else
38
+ isBoundary = true;
39
+ if (y > 0)
40
+ visit(index - width);
41
+ else
42
+ isBoundary = true;
43
+ if (y < height - 1)
44
+ visit(index + width);
45
+ else
46
+ isBoundary = true;
47
+ if (isBoundary)
48
+ boundary.push({ x, y });
49
+ }
50
+ if (area >= minArea)
51
+ regions.push({ area, boundary });
52
+ }
53
+ return regions;
54
+ }
55
+ }
56
+ exports.PsScanRegionService = PsScanRegionService;
@@ -1,9 +1,7 @@
1
1
  import type { PsScanQuad } from '../models/ps-scan-quad';
2
2
  import type { PsScanStability } from '../models/ps-scan-stability';
3
3
  export declare class PsScanStabilityService {
4
- static disarm(state: PsScanStability): PsScanStability;
5
4
  static initial(): PsScanStability;
6
5
  static movement(from: PsScanQuad, to: PsScanQuad): number;
7
- static next(state: PsScanStability, detected: PsScanQuad | null, elapsedMs: number): PsScanStability;
8
- static progress(state: PsScanStability): number;
6
+ static next(state: PsScanStability, detected: PsScanQuad | null): PsScanStability;
9
7
  }
@@ -4,42 +4,29 @@ exports.PsScanStabilityService = void 0;
4
4
  const ps_scan_config_1 = require("../constants/ps-scan-config");
5
5
  const ps_scan_geometry_service_1 = require("./ps-scan-geometry-service");
6
6
  class PsScanStabilityService {
7
- static disarm(state) {
8
- return { ...state, isArmed: false, stableMs: 0 };
9
- }
10
7
  static initial() {
11
- return { isArmed: true, misses: 0, quad: null, raw: null, stableMs: 0 };
8
+ return { misses: 0, quad: null };
12
9
  }
13
10
  static movement(from, to) {
14
11
  return Math.max(...from.map((point, index) => ps_scan_geometry_service_1.PsScanGeometryService.distance(point, to[index])));
15
12
  }
16
- static next(state, detected, elapsedMs) {
13
+ static next(state, detected) {
17
14
  if (detected === null) {
18
15
  const misses = state.misses + 1;
19
16
  return misses > ps_scan_config_1.PsScanConfig.liveMissLimit
20
- ? { isArmed: true, misses, quad: null, raw: null, stableMs: 0 }
21
- : { ...state, misses, stableMs: 0 };
17
+ ? { misses, quad: null }
18
+ : { misses, quad: state.quad };
22
19
  }
23
- if (state.quad === null || state.raw === null)
24
- return { ...state, misses: 0, quad: detected, raw: detected, stableMs: 0 };
25
- const movement = PsScanStabilityService.movement(state.raw, detected);
20
+ if (state.quad === null)
21
+ return { misses: 0, quad: detected };
22
+ if (PsScanStabilityService.movement(state.quad, detected) < ps_scan_config_1.PsScanConfig.liveDeadzone)
23
+ return { misses: 0, quad: state.quad };
26
24
  const weight = ps_scan_config_1.PsScanConfig.liveSmoothing;
27
25
  const quad = state.quad.map((point, index) => ({
28
26
  x: point.x + (detected[index].x - point.x) * weight,
29
27
  y: point.y + (detected[index].y - point.y) * weight,
30
28
  }));
31
- return {
32
- isArmed: state.isArmed || movement > ps_scan_config_1.PsScanConfig.liveRearmMovement,
33
- misses: 0,
34
- quad,
35
- raw: detected,
36
- stableMs: movement <= ps_scan_config_1.PsScanConfig.liveStillMovement ? state.stableMs + elapsedMs : 0,
37
- };
38
- }
39
- static progress(state) {
40
- if (!state.isArmed || state.quad === null)
41
- return 0;
42
- return Math.min(1, state.stableMs / ps_scan_config_1.PsScanConfig.autoCaptureMs);
29
+ return { misses: 0, quad };
43
30
  }
44
31
  }
45
32
  exports.PsScanStabilityService = PsScanStabilityService;
@@ -1,6 +1,6 @@
1
1
  import type { PsScanQuad } from '../models/ps-scan-quad';
2
2
  import type { PsScanRaster } from '../models/ps-scan-raster';
3
3
  export declare class PsScanWarpService {
4
- static warp(source: PsScanRaster, pixelQuad: PsScanQuad, width: number, height: number): PsScanRaster;
4
+ static warp(source: PsScanRaster, pixelQuad: PsScanQuad, width: number, height: number): Promise<PsScanRaster>;
5
5
  private static sample;
6
6
  }
@@ -1,19 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsScanWarpService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
4
5
  const ps_scan_homography_service_1 = require("./ps-scan-homography-service");
5
6
  const ps_scan_raster_service_1 = require("./ps-scan-raster-service");
7
+ const ps_scan_yield_service_1 = require("./ps-scan-yield-service");
6
8
  class PsScanWarpService {
7
- static warp(source, pixelQuad, width, height) {
9
+ static async warp(source, pixelQuad, width, height) {
8
10
  const matrix = ps_scan_homography_service_1.PsScanHomographyService.fromUnitSquare(pixelQuad);
9
11
  const target = ps_scan_raster_service_1.PsScanRasterService.create(width, height);
10
12
  const spanX = Math.max(1, width - 1);
11
13
  const spanY = Math.max(1, height - 1);
12
- for (let y = 0; y < height; y += 1)
14
+ const rowsPerChunk = Math.max(1, Math.floor(ps_scan_config_1.PsScanConfig.warpChunkPixels / width));
15
+ for (let y = 0; y < height; y += 1) {
13
16
  for (let x = 0; x < width; x += 1) {
14
17
  const point = ps_scan_homography_service_1.PsScanHomographyService.apply(matrix, x / spanX, y / spanY);
15
18
  PsScanWarpService.sample(source, point.x, point.y, target.data, (y * width + x) * 4);
16
19
  }
20
+ if ((y + 1) % rowsPerChunk === 0)
21
+ await ps_scan_yield_service_1.PsScanYieldService.next();
22
+ }
17
23
  return target;
18
24
  }
19
25
  static sample(source, x, y, out, at) {
@@ -0,0 +1,3 @@
1
+ export declare class PsScanYieldService {
2
+ static next(): Promise<void>;
3
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanYieldService = void 0;
4
+ class PsScanYieldService {
5
+ static async next() {
6
+ await new Promise((resolve) => setTimeout(resolve, 0));
7
+ }
8
+ }
9
+ exports.PsScanYieldService = PsScanYieldService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-file-components",
3
- "version": "1.5.0",
3
+ "version": "2.0.1",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -20,8 +20,6 @@
20
20
  "scripts": {
21
21
  "husky:prepare": "npx husky",
22
22
  "build": "tsc && tsc -p tsconfig.esm.json && node -e \"require('fs').writeFileSync('dist/esm/package.json', JSON.stringify({ type: 'module' }))\"",
23
- "setup:graphify": "sh scripts/graphify-setup.sh",
24
- "update:graphify": "sh scripts/graphify-update.sh",
25
23
  "lint": "npm run lint:eslint",
26
24
  "lint:fix": "npm run lint:eslint:fix",
27
25
  "format:check": "npx prettier --check --ignore-unknown \"src/**/*.{ts,tsx,json,md}\"",
@@ -32,9 +30,9 @@
32
30
  "test": "vitest run"
33
31
  },
34
32
  "dependencies": {
35
- "@pallasoft/ps-core-components": "^2.29.0",
33
+ "@pallasoft/ps-core-components": "^2.37.0",
36
34
  "@pallasoft/ps-icons": "^1.9.0",
37
- "@pallasoft/ps-popovers": "^1.10.2",
35
+ "@pallasoft/ps-popovers": "^1.13.0",
38
36
  "@pallasoft/ps-utilities": "^2.19.1",
39
37
  "pdfjs-dist": "^6.3.289"
40
38
  },
@@ -1,14 +0,0 @@
1
- import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
2
- import type { PsLabelSet } from '../models/ps-label-set';
3
- import type { PsScanPage } from '../models/ps-scan-page';
4
- export interface PsScanPageListProps {
5
- activeId: string;
6
- canAddPage: boolean;
7
- labels: PsLabelSet<typeof PsDocumentScannerLabels>;
8
- onAddPage: () => void;
9
- onMove: (from: number, to: number) => void;
10
- onSelect: (id: string) => void;
11
- pages: PsScanPage[];
12
- previews: Record<string, string>;
13
- }
14
- export declare const PsScanPageList: ({ activeId, canAddPage, labels, onAddPage, onMove, onSelect, pages, previews, }: PsScanPageListProps) => import("react").JSX.Element;
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PsScanPageList = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const ps_core_components_1 = require("@pallasoft/ps-core-components");
6
- const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
- const ps_icons_1 = require("@pallasoft/ps-icons");
8
- const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
9
- const PsScanPageList = ({ activeId, canAddPage, labels, onAddPage, onMove, onSelect, pages, previews, }) => {
10
- const items = pages.map((page, index) => ({
11
- id: page.id,
12
- label: `${labels.page} ${index + 1}`,
13
- }));
14
- const say = (template, label, position) => ps_scan_template_service_1.PsScanTemplateService.fill(template, { count: pages.length, page: label, position });
15
- return ((0, jsx_runtime_1.jsxs)("div", { className: 'ps-scrollbar-hidden -mx-4 flex items-center gap-3 overflow-x-auto px-4 py-2 sm:mx-0 sm:px-0 lg:flex-wrap lg:overflow-visible', children: [(0, jsx_runtime_1.jsx)(ps_core_components_1.PsSortableList, { announcements: {
16
- cancelled: (label) => say(labels.reorderCancelled, label, 0),
17
- dropped: (label, position) => say(labels.reorderDropped, label, position),
18
- lifted: (label, position) => say(labels.reorderLifted, label, position),
19
- moved: (label, position) => say(labels.reorderMoved, label, position),
20
- }, className: 'flex items-center gap-3 lg:flex-wrap', getHandleLabel: (label) => ps_scan_template_service_1.PsScanTemplateService.fill(labels.dragPage, { page: label }), handleClassName: 'ps-focus-ring absolute inset-0 cursor-grab touch-none rounded-md active:cursor-grabbing', handleIcon: (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true' }), instructions: labels.reorderInstructions, isHorizontal: true, items: items, onMove: (_id, from, to) => onMove(from, to), renderItem: ({ handle, index, isOverlay, item }) => ((0, jsx_runtime_1.jsxs)("div", { "aria-current": item.id === activeId, className: 'relative flex flex-col items-center gap-1.5', onClick: () => onSelect(item.id), role: 'listitem', children: [(0, jsx_runtime_1.jsxs)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('relative block h-[4.5rem] w-14 overflow-hidden rounded-md bg-white/10 ring-offset-2 ring-offset-black transition', item.id === activeId ? 'ring-2 ring-primary' : 'opacity-75', isOverlay ? 'shadow-2xl' : ''), children: [previews[item.id] === undefined ? ((0, jsx_runtime_1.jsx)("span", { className: 'ps-skeleton block size-full' })) : ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'size-full object-cover', src: previews[item.id] })), handle] }), (0, jsx_runtime_1.jsx)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('text-xs font-semibold tabular-nums', item.id === activeId ? 'text-white' : 'text-white/55'), children: index + 1 })] })), role: 'list' }), canAddPage ? ((0, jsx_runtime_1.jsxs)("div", { className: 'flex shrink-0 flex-col items-center gap-1.5', children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": labels.addPage, className: 'ps-focus-ring flex h-[4.5rem] w-14 items-center justify-center rounded-md border-2 border-dashed border-white/25 text-white/70 transition hover:border-white/50 hover:text-white', onClick: onAddPage, title: labels.addPage, type: 'button', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: ps_icons_1.PsIconKey.Plus }) }), (0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'text-xs text-transparent', children: "0" })] })) : null] }));
21
- };
22
- exports.PsScanPageList = PsScanPageList;
@@ -1,7 +0,0 @@
1
- export interface PsScanPageStackProps {
2
- count: number;
3
- label: string;
4
- onOpen: () => void;
5
- preview: string | undefined;
6
- }
7
- export declare const PsScanPageStack: ({ count, label, onOpen, preview }: PsScanPageStackProps) => import("react").JSX.Element;
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PsScanPageStack = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const PsScanPageStack = ({ count, label, onOpen, preview }) => ((0, jsx_runtime_1.jsxs)("button", { "aria-label": label, className: 'ps-focus-ring relative flex h-16 w-12 shrink-0 rounded-md animate-ps-scale-in', onClick: onOpen, title: label, type: 'button', children: [count > 1 ? ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'absolute inset-0 rotate-6 rounded-md border-2 border-white/60 bg-white/30' })) : null, (0, jsx_runtime_1.jsx)("span", { className: 'relative block size-full overflow-hidden rounded-md border-2 border-white bg-white/20', children: preview === undefined ? null : ((0, jsx_runtime_1.jsx)("img", { alt: '', className: 'size-full object-cover', src: preview })) }), (0, jsx_runtime_1.jsx)("span", { className: 'absolute -right-2 -top-2 flex h-6 min-w-6 items-center justify-center rounded-full bg-primary px-1.5 text-xs font-bold tabular-nums text-[color:var(--ps-color-primary-contrast)]', children: count })] }));
6
- exports.PsScanPageStack = PsScanPageStack;
@@ -1,24 +0,0 @@
1
- import { PsDocumentScannerLabels } from '../constants/ps-document-scanner-labels';
2
- import { PsScanFilter } from '../constants/ps-scan-filter';
3
- import type { PsLabelSet } from '../models/ps-label-set';
4
- import type { PsScanPage } from '../models/ps-scan-page';
5
- export interface PsScanReviewProps {
6
- canAddPage: boolean;
7
- error: string;
8
- isBusy: boolean;
9
- labels: PsLabelSet<typeof PsDocumentScannerLabels>;
10
- onAddPage: () => void;
11
- onClose: () => void;
12
- onDelete: () => void;
13
- onEditCrop: () => void;
14
- onFilter: (filter: PsScanFilter) => void;
15
- onFinish: () => void;
16
- onMove: (from: number, to: number) => void;
17
- onRetake: () => void;
18
- onRotate: () => void;
19
- onSelect: (id: string) => void;
20
- page: PsScanPage;
21
- pages: PsScanPage[];
22
- previews: Record<string, string>;
23
- }
24
- export declare const PsScanReview: ({ canAddPage, error, isBusy, labels, onAddPage, onClose, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }: PsScanReviewProps) => import("react").JSX.Element;
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PsScanReview = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
- const ps_icons_1 = require("@pallasoft/ps-icons");
7
- const ps_scan_chrome_classes_1 = require("../constants/ps-scan-chrome-classes");
8
- const ps_scan_filter_1 = require("../constants/ps-scan-filter");
9
- const ps_scan_template_service_1 = require("../services/ps-scan-template-service");
10
- const ps_scan_button_1 = require("./ps-scan-button");
11
- const ps_scan_icon_button_1 = require("./ps-scan-icon-button");
12
- const ps_scan_page_list_1 = require("./ps-scan-page-list");
13
- const PsScanReview = ({ canAddPage, error, isBusy, labels, onAddPage, onClose, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }) => {
14
- const index = pages.findIndex((item) => item.id === page.id);
15
- const preview = previews[page.id];
16
- const filters = [
17
- { key: ps_scan_filter_1.PsScanFilter.Original, label: labels.filterOriginal },
18
- { key: ps_scan_filter_1.PsScanFilter.Colour, label: labels.filterColour },
19
- { key: ps_scan_filter_1.PsScanFilter.Greyscale, label: labels.filterGreyscale },
20
- { key: ps_scan_filter_1.PsScanFilter.Document, label: labels.filterDocument },
21
- ];
22
- const tools = [
23
- { iconKey: ps_icons_1.PsIconKey.Crop, label: labels.editCrop, onClick: onEditCrop },
24
- { iconKey: ps_icons_1.PsIconKey.Rotate, label: labels.rotate, onClick: onRotate },
25
- { iconKey: ps_icons_1.PsIconKey.Refresh, label: labels.retake, onClick: onRetake },
26
- { iconKey: ps_icons_1.PsIconKey.Trash, label: labels.deletePage, onClick: onDelete },
27
- ];
28
- return ((0, jsx_runtime_1.jsxs)("div", { className: 'flex h-full min-h-0 flex-col bg-black text-white animate-ps-fade-in lg:flex-row', children: [(0, jsx_runtime_1.jsxs)("section", { className: 'flex min-h-0 flex-1 flex-col', 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.Close, label: labels.close, onClick: onClose }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center justify-center gap-1', children: [(0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { disabled: index <= 0, iconClassName: 'rotate-180', iconKey: ps_icons_1.PsIconKey.ChevronRight, label: labels.previousPage, onClick: () => onSelect(pages[index - 1].id) }), (0, jsx_runtime_1.jsx)("h2", { className: 'min-w-16 text-center text-sm font-semibold tabular-nums', children: ps_scan_template_service_1.PsScanTemplateService.fill(labels.pageOf, {
29
- count: pages.length,
30
- position: index + 1,
31
- }) }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { disabled: index >= pages.length - 1, iconKey: ps_icons_1.PsIconKey.ChevronRight, label: labels.nextPage, onClick: () => onSelect(pages[index + 1].id) })] }), (0, jsx_runtime_1.jsx)(ps_scan_icon_button_1.PsScanIconButton, { disabled: !canAddPage, iconKey: ps_icons_1.PsIconKey.Camera, label: labels.addPage, onClick: onAddPage })] }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-6 py-4', children: preview === undefined ? ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'ps-skeleton block h-4/5 w-3/5 rounded-sm' })) : ((0, jsx_runtime_1.jsx)("img", { alt: `${labels.page} ${index + 1}`, className: 'max-h-full max-w-full rounded-sm object-contain shadow-[0_24px_60px_-20px_rgb(0_0_0/0.9)] animate-ps-fade-in', src: preview }, preview)) })] }), (0, jsx_runtime_1.jsxs)("aside", { className: 'flex shrink-0 flex-col gap-3 border-white/10 px-4 pb-[max(1rem,env(safe-area-inset-bottom))] lg:w-[22rem] lg:gap-5 lg:overflow-y-auto lg:border-l lg:pt-[max(1rem,env(safe-area-inset-top))]', children: [(0, jsx_runtime_1.jsx)("div", { "aria-label": labels.filters, className: 'grid grid-cols-4 gap-2 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none lg:grid-cols-2', role: 'group', children: filters.map((filter) => ((0, jsx_runtime_1.jsx)("button", { "aria-pressed": page.filter === filter.key, className: ps_design_system_1.PallasoftClassNameService.merge(ps_scan_chrome_classes_1.PsScanChromeClasses.chip, page.filter === filter.key
32
- ? ps_scan_chrome_classes_1.PsScanChromeClasses.iconOn
33
- : 'bg-white/10 text-white/85 hover:bg-white/20'), onClick: () => onFilter(filter.key), type: 'button', children: filter.label }, filter.key))) }), (0, jsx_runtime_1.jsx)("div", { className: 'grid grid-cols-4 gap-1 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none', children: tools.map((tool) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_scan_chrome_classes_1.PsScanChromeClasses.tool, onClick: tool.onClick, type: 'button', children: [(0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: tool.iconKey }), (0, jsx_runtime_1.jsx)("span", { children: tool.label })] }, tool.label))) }), (0, jsx_runtime_1.jsxs)("section", { "aria-label": labels.pages, className: 'min-w-0 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none', children: [(0, jsx_runtime_1.jsx)("h3", { className: 'hidden text-xs font-semibold uppercase tracking-wide text-white/50 lg:block', children: labels.pages }), (0, jsx_runtime_1.jsx)(ps_scan_page_list_1.PsScanPageList, { activeId: page.id, canAddPage: canAddPage, labels: labels, onAddPage: onAddPage, onMove: onMove, onSelect: onSelect, pages: pages, previews: previews })] }), error === '' ? null : ((0, jsx_runtime_1.jsx)("p", { className: 'rounded-ps-control bg-danger px-3 py-2 text-sm text-white', role: 'alert', children: error })), (0, jsx_runtime_1.jsx)(ps_scan_button_1.PsScanButton, { className: 'w-full sm:mx-auto sm:max-w-md lg:mt-auto lg:max-w-none', isLarge: true, isLoading: isBusy, isPrimary: true, leadingIcon: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: ps_icons_1.PsIconKey.Upload }), loadingLabel: labels.finishing, onClick: onFinish, children: pages.length === 1
34
- ? labels.finishOne
35
- : ps_scan_template_service_1.PsScanTemplateService.fill(labels.finish, { count: pages.length }) })] })] }));
36
- };
37
- exports.PsScanReview = PsScanReview;
@@ -1,7 +0,0 @@
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];
@@ -1,9 +0,0 @@
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
- };
@@ -1,5 +0,0 @@
1
- export declare const PsScanOutputFormat: {
2
- readonly Jpeg: "jpeg";
3
- readonly Pdf: "pdf";
4
- };
5
- export type PsScanOutputFormat = (typeof PsScanOutputFormat)[keyof typeof PsScanOutputFormat];
@@ -1,7 +0,0 @@
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
- };
@@ -1,18 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { PsSortableList } from '@pallasoft/ps-core-components';
3
- import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
4
- import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
5
- import { PsScanTemplateService } from '../services/ps-scan-template-service';
6
- export const PsScanPageList = ({ activeId, canAddPage, labels, onAddPage, onMove, onSelect, pages, previews, }) => {
7
- const items = pages.map((page, index) => ({
8
- id: page.id,
9
- label: `${labels.page} ${index + 1}`,
10
- }));
11
- const say = (template, label, position) => PsScanTemplateService.fill(template, { count: pages.length, page: label, position });
12
- return (_jsxs("div", { className: 'ps-scrollbar-hidden -mx-4 flex items-center gap-3 overflow-x-auto px-4 py-2 sm:mx-0 sm:px-0 lg:flex-wrap lg:overflow-visible', children: [_jsx(PsSortableList, { announcements: {
13
- cancelled: (label) => say(labels.reorderCancelled, label, 0),
14
- dropped: (label, position) => say(labels.reorderDropped, label, position),
15
- lifted: (label, position) => say(labels.reorderLifted, label, position),
16
- moved: (label, position) => say(labels.reorderMoved, label, position),
17
- }, className: 'flex items-center gap-3 lg:flex-wrap', getHandleLabel: (label) => PsScanTemplateService.fill(labels.dragPage, { page: label }), handleClassName: 'ps-focus-ring absolute inset-0 cursor-grab touch-none rounded-md active:cursor-grabbing', handleIcon: _jsx("span", { "aria-hidden": 'true' }), instructions: labels.reorderInstructions, isHorizontal: true, items: items, onMove: (_id, from, to) => onMove(from, to), renderItem: ({ handle, index, isOverlay, item }) => (_jsxs("div", { "aria-current": item.id === activeId, className: 'relative flex flex-col items-center gap-1.5', onClick: () => onSelect(item.id), role: 'listitem', children: [_jsxs("span", { className: PallasoftClassNameService.merge('relative block h-[4.5rem] w-14 overflow-hidden rounded-md bg-white/10 ring-offset-2 ring-offset-black transition', item.id === activeId ? 'ring-2 ring-primary' : 'opacity-75', isOverlay ? 'shadow-2xl' : ''), children: [previews[item.id] === undefined ? (_jsx("span", { className: 'ps-skeleton block size-full' })) : (_jsx("img", { alt: '', className: 'size-full object-cover', src: previews[item.id] })), handle] }), _jsx("span", { className: PallasoftClassNameService.merge('text-xs font-semibold tabular-nums', item.id === activeId ? 'text-white' : 'text-white/55'), children: index + 1 })] })), role: 'list' }), canAddPage ? (_jsxs("div", { className: 'flex shrink-0 flex-col items-center gap-1.5', children: [_jsx("button", { "aria-label": labels.addPage, className: 'ps-focus-ring flex h-[4.5rem] w-14 items-center justify-center rounded-md border-2 border-dashed border-white/25 text-white/70 transition hover:border-white/50 hover:text-white', onClick: onAddPage, title: labels.addPage, type: 'button', children: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-5', iconKey: PsIconKey.Plus }) }), _jsx("span", { "aria-hidden": 'true', className: 'text-xs text-transparent', children: "0" })] })) : null] }));
18
- };
@@ -1,2 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const PsScanPageStack = ({ count, label, onOpen, preview }) => (_jsxs("button", { "aria-label": label, className: 'ps-focus-ring relative flex h-16 w-12 shrink-0 rounded-md animate-ps-scale-in', onClick: onOpen, title: label, type: 'button', children: [count > 1 ? (_jsx("span", { "aria-hidden": 'true', className: 'absolute inset-0 rotate-6 rounded-md border-2 border-white/60 bg-white/30' })) : null, _jsx("span", { className: 'relative block size-full overflow-hidden rounded-md border-2 border-white bg-white/20', children: preview === undefined ? null : (_jsx("img", { alt: '', className: 'size-full object-cover', src: preview })) }), _jsx("span", { className: 'absolute -right-2 -top-2 flex h-6 min-w-6 items-center justify-center rounded-full bg-primary px-1.5 text-xs font-bold tabular-nums text-[color:var(--ps-color-primary-contrast)]', children: count })] }));
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
3
- import { PsIcon, PsIconKey } from '@pallasoft/ps-icons';
4
- import { PsScanChromeClasses } from '../constants/ps-scan-chrome-classes';
5
- import { PsScanFilter } from '../constants/ps-scan-filter';
6
- import { PsScanTemplateService } from '../services/ps-scan-template-service';
7
- import { PsScanButton } from './ps-scan-button';
8
- import { PsScanIconButton } from './ps-scan-icon-button';
9
- import { PsScanPageList } from './ps-scan-page-list';
10
- export const PsScanReview = ({ canAddPage, error, isBusy, labels, onAddPage, onClose, onDelete, onEditCrop, onFilter, onFinish, onMove, onRetake, onRotate, onSelect, page, pages, previews, }) => {
11
- const index = pages.findIndex((item) => item.id === page.id);
12
- const preview = previews[page.id];
13
- const filters = [
14
- { key: PsScanFilter.Original, label: labels.filterOriginal },
15
- { key: PsScanFilter.Colour, label: labels.filterColour },
16
- { key: PsScanFilter.Greyscale, label: labels.filterGreyscale },
17
- { key: PsScanFilter.Document, label: labels.filterDocument },
18
- ];
19
- const tools = [
20
- { iconKey: PsIconKey.Crop, label: labels.editCrop, onClick: onEditCrop },
21
- { iconKey: PsIconKey.Rotate, label: labels.rotate, onClick: onRotate },
22
- { iconKey: PsIconKey.Refresh, label: labels.retake, onClick: onRetake },
23
- { iconKey: PsIconKey.Trash, label: labels.deletePage, onClick: onDelete },
24
- ];
25
- return (_jsxs("div", { className: 'flex h-full min-h-0 flex-col bg-black text-white animate-ps-fade-in lg:flex-row', children: [_jsxs("section", { className: 'flex min-h-0 flex-1 flex-col', children: [_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: [_jsx(PsScanIconButton, { iconKey: PsIconKey.Close, label: labels.close, onClick: onClose }), _jsxs("div", { className: 'flex items-center justify-center gap-1', children: [_jsx(PsScanIconButton, { disabled: index <= 0, iconClassName: 'rotate-180', iconKey: PsIconKey.ChevronRight, label: labels.previousPage, onClick: () => onSelect(pages[index - 1].id) }), _jsx("h2", { className: 'min-w-16 text-center text-sm font-semibold tabular-nums', children: PsScanTemplateService.fill(labels.pageOf, {
26
- count: pages.length,
27
- position: index + 1,
28
- }) }), _jsx(PsScanIconButton, { disabled: index >= pages.length - 1, iconKey: PsIconKey.ChevronRight, label: labels.nextPage, onClick: () => onSelect(pages[index + 1].id) })] }), _jsx(PsScanIconButton, { disabled: !canAddPage, iconKey: PsIconKey.Camera, label: labels.addPage, onClick: onAddPage })] }), _jsx("div", { className: 'flex min-h-0 flex-1 items-center justify-center px-6 py-4', children: preview === undefined ? (_jsx("span", { "aria-hidden": 'true', className: 'ps-skeleton block h-4/5 w-3/5 rounded-sm' })) : (_jsx("img", { alt: `${labels.page} ${index + 1}`, className: 'max-h-full max-w-full rounded-sm object-contain shadow-[0_24px_60px_-20px_rgb(0_0_0/0.9)] animate-ps-fade-in', src: preview }, preview)) })] }), _jsxs("aside", { className: 'flex shrink-0 flex-col gap-3 border-white/10 px-4 pb-[max(1rem,env(safe-area-inset-bottom))] lg:w-[22rem] lg:gap-5 lg:overflow-y-auto lg:border-l lg:pt-[max(1rem,env(safe-area-inset-top))]', children: [_jsx("div", { "aria-label": labels.filters, className: 'grid grid-cols-4 gap-2 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none lg:grid-cols-2', role: 'group', children: filters.map((filter) => (_jsx("button", { "aria-pressed": page.filter === filter.key, className: PallasoftClassNameService.merge(PsScanChromeClasses.chip, page.filter === filter.key
29
- ? PsScanChromeClasses.iconOn
30
- : 'bg-white/10 text-white/85 hover:bg-white/20'), onClick: () => onFilter(filter.key), type: 'button', children: filter.label }, filter.key))) }), _jsx("div", { className: 'grid grid-cols-4 gap-1 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none', children: tools.map((tool) => (_jsxs("button", { className: PsScanChromeClasses.tool, onClick: tool.onClick, type: 'button', children: [_jsx(PsIcon, { "aria-hidden": 'true', className: 'size-ps-control-icon-standalone', iconKey: tool.iconKey }), _jsx("span", { children: tool.label })] }, tool.label))) }), _jsxs("section", { "aria-label": labels.pages, className: 'min-w-0 sm:mx-auto sm:w-full sm:max-w-md lg:max-w-none', children: [_jsx("h3", { className: 'hidden text-xs font-semibold uppercase tracking-wide text-white/50 lg:block', children: labels.pages }), _jsx(PsScanPageList, { activeId: page.id, canAddPage: canAddPage, labels: labels, onAddPage: onAddPage, onMove: onMove, onSelect: onSelect, pages: pages, previews: previews })] }), error === '' ? null : (_jsx("p", { className: 'rounded-ps-control bg-danger px-3 py-2 text-sm text-white', role: 'alert', children: error })), _jsx(PsScanButton, { className: 'w-full sm:mx-auto sm:max-w-md lg:mt-auto lg:max-w-none', isLarge: true, isLoading: isBusy, isPrimary: true, leadingIcon: _jsx(PsIcon, { "aria-hidden": 'true', className: 'size-4', iconKey: PsIconKey.Upload }), loadingLabel: labels.finishing, onClick: onFinish, children: pages.length === 1
31
- ? labels.finishOne
32
- : PsScanTemplateService.fill(labels.finish, { count: pages.length }) })] })] }));
33
- };
@@ -1,6 +0,0 @@
1
- export const PsScanFilter = {
2
- Colour: 'colour',
3
- Document: 'document',
4
- Greyscale: 'greyscale',
5
- Original: 'original',
6
- };
@@ -1,4 +0,0 @@
1
- export const PsScanOutputFormat = {
2
- Jpeg: 'jpeg',
3
- Pdf: 'pdf',
4
- };
@@ -1,62 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
- import { PsScanFilter } from '../constants/ps-scan-filter';
3
- import { PsScanOrderService } from '../services/ps-scan-order-service';
4
- import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
5
- export const usePsScanPages = () => {
6
- const [pages, setPages] = useState([]);
7
- const urls = useRef(new Set());
8
- const counter = useRef(0);
9
- useEffect(() => () => {
10
- urls.current.forEach((url) => URL.revokeObjectURL(url));
11
- urls.current.clear();
12
- }, []);
13
- const build = useCallback(async (source) => {
14
- const prepared = await PsScanPipelineService.prepare(source);
15
- const sourceUrl = URL.createObjectURL(source);
16
- counter.current += 1;
17
- urls.current.add(sourceUrl);
18
- return {
19
- isDetected: prepared.isDetected,
20
- page: {
21
- corners: prepared.corners,
22
- filter: PsScanFilter.Document,
23
- height: prepared.height,
24
- id: `scan-page-${counter.current}`,
25
- rotation: 0,
26
- source,
27
- sourceUrl,
28
- width: prepared.width,
29
- },
30
- };
31
- }, []);
32
- const add = useCallback(async (sources) => {
33
- const built = [];
34
- for (const source of sources)
35
- built.push(await build(source));
36
- setPages((current) => [...current, ...built.map((item) => item.page)]);
37
- return built;
38
- }, [build]);
39
- const replace = useCallback(async (id, source) => {
40
- const built = await build(source);
41
- setPages((current) => current.map((page) => {
42
- if (page.id !== id)
43
- return page;
44
- URL.revokeObjectURL(page.sourceUrl);
45
- urls.current.delete(page.sourceUrl);
46
- return { ...built.page, filter: page.filter, id: page.id };
47
- }));
48
- return built;
49
- }, [build]);
50
- const update = useCallback((id, patch) => setPages((current) => current.map((page) => (page.id === id ? { ...page, ...patch } : page))), []);
51
- const setCorners = useCallback((id, corners) => update(id, { corners }), [update]);
52
- const remove = useCallback((id) => setPages((current) => {
53
- const target = current.find((page) => page.id === id);
54
- if (target !== undefined) {
55
- URL.revokeObjectURL(target.sourceUrl);
56
- urls.current.delete(target.sourceUrl);
57
- }
58
- return current.filter((page) => page.id !== id);
59
- }), []);
60
- const move = useCallback((from, to) => setPages((current) => PsScanOrderService.move(current, from, to)), []);
61
- return { add, move, pages, remove, replace, setCorners, update };
62
- };
@@ -1,57 +0,0 @@
1
- import { useEffect, useRef, useState } from 'react';
2
- import { PsScanConfig } from '../constants/ps-scan-config';
3
- import { PsScanCanvasService } from '../services/ps-scan-canvas-service';
4
- import { PsScanPipelineService } from '../services/ps-scan-pipeline-service';
5
- export const usePsScanPreviews = (pages) => {
6
- const rasters = useRef(new Map());
7
- const urls = useRef(new Map());
8
- const [previews, setPreviews] = useState({});
9
- const signature = pages
10
- .map((page) => `${page.id}:${page.filter}:${page.rotation}:${page.corners.map((point) => `${point.x.toFixed(4)},${point.y.toFixed(4)}`).join(';')}`)
11
- .join('|');
12
- useEffect(() => {
13
- let isCancelled = false;
14
- const timer = setTimeout(async () => {
15
- const stale = [];
16
- for (const page of pages) {
17
- if (isCancelled)
18
- return;
19
- let raster = rasters.current.get(page.id);
20
- if (raster === undefined) {
21
- raster = await PsScanPipelineService.measure(page.source);
22
- rasters.current.set(page.id, raster);
23
- }
24
- const processed = PsScanPipelineService.process(page, raster, PsScanConfig.previewMaxDimension);
25
- const blob = await PsScanCanvasService.encode(processed, PsScanConfig.defaultJpegQuality);
26
- if (isCancelled)
27
- return;
28
- const previous = urls.current.get(page.id);
29
- if (previous !== undefined)
30
- stale.push(previous);
31
- urls.current.set(page.id, URL.createObjectURL(blob));
32
- }
33
- if (isCancelled)
34
- return;
35
- const ids = new Set(pages.map((page) => page.id));
36
- urls.current.forEach((url, id) => {
37
- if (ids.has(id))
38
- return;
39
- stale.push(url);
40
- urls.current.delete(id);
41
- rasters.current.delete(id);
42
- });
43
- setPreviews(Object.fromEntries(urls.current));
44
- setTimeout(() => stale.forEach((url) => URL.revokeObjectURL(url)), 1000);
45
- }, PsScanConfig.previewDebounceMs);
46
- return () => {
47
- isCancelled = true;
48
- clearTimeout(timer);
49
- };
50
- }, [signature]);
51
- useEffect(() => () => {
52
- urls.current.forEach((url) => URL.revokeObjectURL(url));
53
- urls.current.clear();
54
- rasters.current.clear();
55
- }, []);
56
- return previews;
57
- };