@ayasofyazilim/ui 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/__mocks__/canvas.ts +8 -0
  2. package/components.json +21 -0
  3. package/eslint.config.js +4 -0
  4. package/jest-environment.js +37 -0
  5. package/jest.config.ts +47 -0
  6. package/jest.setup.ts +69 -0
  7. package/package.json +124 -0
  8. package/postcss.config.mjs +6 -0
  9. package/src/aria/index.tsx +1 -0
  10. package/src/aria/number-field.tsx +41 -0
  11. package/src/components/.gitkeep +0 -0
  12. package/src/components/accordion.tsx +66 -0
  13. package/src/components/alert-dialog.tsx +157 -0
  14. package/src/components/alert.tsx +70 -0
  15. package/src/components/aspect-ratio.tsx +11 -0
  16. package/src/components/avatar.tsx +53 -0
  17. package/src/components/badge.tsx +67 -0
  18. package/src/components/breadcrumb.tsx +109 -0
  19. package/src/components/button-group.tsx +83 -0
  20. package/src/components/button.tsx +68 -0
  21. package/src/components/calendar.tsx +219 -0
  22. package/src/components/card.tsx +92 -0
  23. package/src/components/carousel.tsx +241 -0
  24. package/src/components/chart.tsx +363 -0
  25. package/src/components/checkbox.tsx +32 -0
  26. package/src/components/collapsible.tsx +33 -0
  27. package/src/components/command.tsx +184 -0
  28. package/src/components/context-menu.tsx +252 -0
  29. package/src/components/dialog.tsx +144 -0
  30. package/src/components/drawer.tsx +135 -0
  31. package/src/components/dropdown-menu.tsx +258 -0
  32. package/src/components/empty.tsx +100 -0
  33. package/src/components/field.tsx +248 -0
  34. package/src/components/form.tsx +169 -0
  35. package/src/components/hover-card.tsx +44 -0
  36. package/src/components/input-group.tsx +170 -0
  37. package/src/components/input-otp.tsx +77 -0
  38. package/src/components/input.tsx +21 -0
  39. package/src/components/item.tsx +193 -0
  40. package/src/components/kbd.tsx +28 -0
  41. package/src/components/label.tsx +24 -0
  42. package/src/components/menubar.tsx +276 -0
  43. package/src/components/navigation-menu.tsx +168 -0
  44. package/src/components/pagination.tsx +130 -0
  45. package/src/components/popover.tsx +88 -0
  46. package/src/components/progress.tsx +31 -0
  47. package/src/components/radio-group.tsx +45 -0
  48. package/src/components/resizable.tsx +56 -0
  49. package/src/components/scroll-area.tsx +58 -0
  50. package/src/components/select.tsx +189 -0
  51. package/src/components/separator.tsx +28 -0
  52. package/src/components/sheet.tsx +140 -0
  53. package/src/components/sidebar.tsx +862 -0
  54. package/src/components/skeleton.tsx +13 -0
  55. package/src/components/slider.tsx +63 -0
  56. package/src/components/sonner.tsx +40 -0
  57. package/src/components/spinner.tsx +16 -0
  58. package/src/components/stepper.tsx +291 -0
  59. package/src/components/switch.tsx +31 -0
  60. package/src/components/table.tsx +133 -0
  61. package/src/components/tabs.tsx +66 -0
  62. package/src/components/textarea.tsx +18 -0
  63. package/src/components/toggle-group.tsx +83 -0
  64. package/src/components/toggle.tsx +47 -0
  65. package/src/components/tooltip.tsx +66 -0
  66. package/src/custom/action-button.tsx +48 -0
  67. package/src/custom/async-select.tsx +287 -0
  68. package/src/custom/awesome-not-found.tsx +116 -0
  69. package/src/custom/charts/area-chart.tsx +147 -0
  70. package/src/custom/charts/bar-chart.tsx +233 -0
  71. package/src/custom/charts/chart-card.tsx +103 -0
  72. package/src/custom/charts/index.tsx +16 -0
  73. package/src/custom/charts/pie-chart.tsx +168 -0
  74. package/src/custom/charts/radar-chart.tsx +126 -0
  75. package/src/custom/checkbox-tree.tsx +100 -0
  76. package/src/custom/combobox.tsx +296 -0
  77. package/src/custom/confirm-dialog.tsx +102 -0
  78. package/src/custom/country-selector.tsx +204 -0
  79. package/src/custom/date-picker/calendar-rac.tsx +109 -0
  80. package/src/custom/date-picker/datefield-rac.tsx +84 -0
  81. package/src/custom/date-picker/index.tsx +273 -0
  82. package/src/custom/date-picker/types/index.ts +4 -0
  83. package/src/custom/date-picker/utils/index.ts +42 -0
  84. package/src/custom/date-picker-old.tsx +50 -0
  85. package/src/custom/date-tooltip.tsx +98 -0
  86. package/src/custom/document-scanner/consts.ts +5 -0
  87. package/src/custom/document-scanner/corner-adjustment/action-buttons.tsx +33 -0
  88. package/src/custom/document-scanner/corner-adjustment/corner-handle.tsx +43 -0
  89. package/src/custom/document-scanner/corner-adjustment/hooks/use-corner-drag.ts +85 -0
  90. package/src/custom/document-scanner/corner-adjustment/index.tsx +125 -0
  91. package/src/custom/document-scanner/corner-adjustment/types.ts +53 -0
  92. package/src/custom/document-scanner/corner-adjustment/utils/clip-path.ts +22 -0
  93. package/src/custom/document-scanner/corner-adjustment/zoom-magnifier.tsx +115 -0
  94. package/src/custom/document-scanner/hooks/use-document-capture.ts +81 -0
  95. package/src/custom/document-scanner/hooks/use-document-scanner.ts +80 -0
  96. package/src/custom/document-scanner/hooks/use-perspective-crop.ts +38 -0
  97. package/src/custom/document-scanner/index.tsx +255 -0
  98. package/src/custom/document-scanner/lib.ts +407 -0
  99. package/src/custom/document-scanner/types.ts +205 -0
  100. package/src/custom/document-scanner/utils/perspective-correction.ts +139 -0
  101. package/src/custom/document-viewer/controllers.tsx +98 -0
  102. package/src/custom/document-viewer/index.tsx +43 -0
  103. package/src/custom/document-viewer/renderers/image.tsx +37 -0
  104. package/src/custom/document-viewer/renderers/index.tsx +2 -0
  105. package/src/custom/document-viewer/renderers/pdf.tsx +105 -0
  106. package/src/custom/email-input/domains.json +159 -0
  107. package/src/custom/email-input/email.tsx +229 -0
  108. package/src/custom/email-input/index.tsx +4 -0
  109. package/src/custom/email-input/types.ts +104 -0
  110. package/src/custom/file-uploader.tsx +541 -0
  111. package/src/custom/filter-component/fields/async-select.tsx +33 -0
  112. package/src/custom/filter-component/fields/date.tsx +60 -0
  113. package/src/custom/filter-component/fields/multi-select.tsx +30 -0
  114. package/src/custom/filter-component/index.tsx +217 -0
  115. package/src/custom/image-canvas.tsx +260 -0
  116. package/src/custom/json-editor.tsx +22 -0
  117. package/src/custom/master-data-grid/components/dialogs/column-settings-dialog.tsx +100 -0
  118. package/src/custom/master-data-grid/components/dialogs/index.ts +1 -0
  119. package/src/custom/master-data-grid/components/filters/client-filter.tsx +368 -0
  120. package/src/custom/master-data-grid/components/filters/filter-input.tsx +256 -0
  121. package/src/custom/master-data-grid/components/filters/index.ts +3 -0
  122. package/src/custom/master-data-grid/components/filters/inline-column-filter.tsx +233 -0
  123. package/src/custom/master-data-grid/components/filters/multi-filter-dialog.tsx +90 -0
  124. package/src/custom/master-data-grid/components/filters/server-filter.tsx +255 -0
  125. package/src/custom/master-data-grid/components/master-data-grid.tsx +472 -0
  126. package/src/custom/master-data-grid/components/pagination/index.ts +1 -0
  127. package/src/custom/master-data-grid/components/pagination/pagination.tsx +178 -0
  128. package/src/custom/master-data-grid/components/table/cell-renderer.tsx +634 -0
  129. package/src/custom/master-data-grid/components/table/header-cell.tsx +162 -0
  130. package/src/custom/master-data-grid/components/table/index.ts +4 -0
  131. package/src/custom/master-data-grid/components/table/table-body-renderer.tsx +113 -0
  132. package/src/custom/master-data-grid/components/table/virtual-body.tsx +138 -0
  133. package/src/custom/master-data-grid/components/toolbar/index.ts +1 -0
  134. package/src/custom/master-data-grid/components/toolbar/toolbar.tsx +314 -0
  135. package/src/custom/master-data-grid/hooks/index.ts +3 -0
  136. package/src/custom/master-data-grid/hooks/use-columns.tsx +332 -0
  137. package/src/custom/master-data-grid/hooks/use-editing.ts +106 -0
  138. package/src/custom/master-data-grid/hooks/use-table-state-reducer.ts +157 -0
  139. package/src/custom/master-data-grid/hooks/use-table-state.ts +31 -0
  140. package/src/custom/master-data-grid/index.ts +16 -0
  141. package/src/custom/master-data-grid/types.ts +466 -0
  142. package/src/custom/master-data-grid/utils/column-generator.tsx +306 -0
  143. package/src/custom/master-data-grid/utils/export-utils.ts +67 -0
  144. package/src/custom/master-data-grid/utils/filter-fns.ts +290 -0
  145. package/src/custom/master-data-grid/utils/index.ts +8 -0
  146. package/src/custom/master-data-grid/utils/pinning-utils.ts +88 -0
  147. package/src/custom/master-data-grid/utils/translation-utils.ts +42 -0
  148. package/src/custom/multi-select.tsx +432 -0
  149. package/src/custom/password-input.tsx +194 -0
  150. package/src/custom/phone-input.tsx +172 -0
  151. package/src/custom/schema-form/custom/index.tsx +1 -0
  152. package/src/custom/schema-form/custom/label.tsx +53 -0
  153. package/src/custom/schema-form/fields/base-input-field.tsx +82 -0
  154. package/src/custom/schema-form/fields/field.tsx +67 -0
  155. package/src/custom/schema-form/fields/index.tsx +5 -0
  156. package/src/custom/schema-form/fields/object.tsx +12 -0
  157. package/src/custom/schema-form/fields/table-array/array-field-item.tsx +90 -0
  158. package/src/custom/schema-form/fields/table-array/array-field-template.tsx +115 -0
  159. package/src/custom/schema-form/index.tsx +259 -0
  160. package/src/custom/schema-form/templates/description.tsx +20 -0
  161. package/src/custom/schema-form/templates/index.tsx +2 -0
  162. package/src/custom/schema-form/templates/submit.tsx +32 -0
  163. package/src/custom/schema-form/types.ts +64 -0
  164. package/src/custom/schema-form/utils/index.ts +4 -0
  165. package/src/custom/schema-form/utils/schema-dependency.ts +655 -0
  166. package/src/custom/schema-form/utils/schemas.ts +289 -0
  167. package/src/custom/schema-form/utils/validation.ts +23 -0
  168. package/src/custom/schema-form/widgets/boolean.tsx +77 -0
  169. package/src/custom/schema-form/widgets/combobox.tsx +274 -0
  170. package/src/custom/schema-form/widgets/date.tsx +59 -0
  171. package/src/custom/schema-form/widgets/email.tsx +34 -0
  172. package/src/custom/schema-form/widgets/index.tsx +10 -0
  173. package/src/custom/schema-form/widgets/password.tsx +40 -0
  174. package/src/custom/schema-form/widgets/phone.tsx +40 -0
  175. package/src/custom/schema-form/widgets/select.tsx +105 -0
  176. package/src/custom/schema-form/widgets/selectable.tsx +25 -0
  177. package/src/custom/schema-form/widgets/string-array.tsx +296 -0
  178. package/src/custom/schema-form/widgets/url.tsx +56 -0
  179. package/src/custom/section-layout-v2.tsx +212 -0
  180. package/src/custom/select-tabs.tsx +109 -0
  181. package/src/custom/selectable.tsx +316 -0
  182. package/src/custom/stepper.tsx +236 -0
  183. package/src/custom/tab-layout.tsx +213 -0
  184. package/src/custom/tanstack-table/fields/index.tsx +12 -0
  185. package/src/custom/tanstack-table/fields/tanstack-table-action-dialogs.tsx +89 -0
  186. package/src/custom/tanstack-table/fields/tanstack-table-column-header.tsx +66 -0
  187. package/src/custom/tanstack-table/fields/tanstack-table-filter-date.tsx +180 -0
  188. package/src/custom/tanstack-table/fields/tanstack-table-filter-faceted.tsx +158 -0
  189. package/src/custom/tanstack-table/fields/tanstack-table-filter-text.tsx +76 -0
  190. package/src/custom/tanstack-table/fields/tanstack-table-pagination.tsx +136 -0
  191. package/src/custom/tanstack-table/fields/tanstack-table-plain-table.tsx +142 -0
  192. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-confirmation.tsx +77 -0
  193. package/src/custom/tanstack-table/fields/tanstack-table-row-actions-custom-dialog.tsx +87 -0
  194. package/src/custom/tanstack-table/fields/tanstack-table-row-actions.tsx +151 -0
  195. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-custom-dialog.tsx +88 -0
  196. package/src/custom/tanstack-table/fields/tanstack-table-table-actions-schemaform-dialog.tsx +47 -0
  197. package/src/custom/tanstack-table/fields/tanstack-table-toolbar.tsx +143 -0
  198. package/src/custom/tanstack-table/fields/tanstack-table-view-options.tsx +171 -0
  199. package/src/custom/tanstack-table/index.tsx +244 -0
  200. package/src/custom/tanstack-table/types/index.ts +328 -0
  201. package/src/custom/tanstack-table/utils/cell-with-actions.tsx +21 -0
  202. package/src/custom/tanstack-table/utils/column-names.ts +26 -0
  203. package/src/custom/tanstack-table/utils/columns-by-row-data.tsx +312 -0
  204. package/src/custom/tanstack-table/utils/editable-columns-by-row-data.tsx +219 -0
  205. package/src/custom/tanstack-table/utils/faceted-boolean-options.tsx +22 -0
  206. package/src/custom/tanstack-table/utils/index.tsx +10 -0
  207. package/src/custom/tanstack-table/utils/pinning-styles.ts +57 -0
  208. package/src/custom/tanstack-table/utils/table.tsx +83 -0
  209. package/src/custom/tanstack-table/utils/test-conditions.ts +17 -0
  210. package/src/custom/timeline.tsx +208 -0
  211. package/src/custom/tree.tsx +200 -0
  212. package/src/custom/tscanify/browser.ts +66 -0
  213. package/src/custom/tscanify/index.ts +51 -0
  214. package/src/custom/tscanify/tscanify-browser.ts +522 -0
  215. package/src/custom/tscanify/tscanify.ts +262 -0
  216. package/src/custom/tscanify/types.ts +22 -0
  217. package/src/custom/webcam.tsx +737 -0
  218. package/src/hooks/.gitkeep +0 -0
  219. package/src/hooks/use-callback-ref.ts +27 -0
  220. package/src/hooks/use-controllable-state.ts +67 -0
  221. package/src/hooks/use-debounce.ts +19 -0
  222. package/src/hooks/use-is-visible.ts +23 -0
  223. package/src/hooks/use-media-query.ts +21 -0
  224. package/src/hooks/use-mobile.ts +21 -0
  225. package/src/hooks/use-on-window-resize.ts +15 -0
  226. package/src/hooks/use-scroll.tsx +22 -0
  227. package/src/lib/utils.ts +61 -0
  228. package/src/lib/zod.ts +2 -0
  229. package/src/styles/core.css +57 -0
  230. package/src/styles/globals.css +130 -0
  231. package/src/test/email-input.test.tsx +217 -0
  232. package/src/test/password-input.test.tsx +92 -0
  233. package/src/test/select-tabs.test.tsx +302 -0
  234. package/src/test/selectable.test.tsx +1093 -0
  235. package/tsconfig.json +13 -0
  236. package/tsconfig.lint.json +8 -0
@@ -0,0 +1,81 @@
1
+ import { useCallback } from "react";
2
+ import { scanDocument } from "../lib";
3
+ import {
4
+ DocumentCorners,
5
+ DocumentScannerCallbacks,
6
+ DetectionSettings,
7
+ } from "../types";
8
+
9
+ export interface UseDocumentCaptureProps {
10
+ videoDimensions: { width: number; height: number };
11
+ callbacks: DocumentScannerCallbacks;
12
+ customDetectionAlgorithm?: (
13
+ image: string,
14
+ dimensions: { width: number; height: number }
15
+ ) => Promise<DocumentCorners | null>;
16
+ detectionSettings: DetectionSettings;
17
+ setCapturedImage: (image: string | null) => void;
18
+ setDetectedCorners: (corners: DocumentCorners | undefined) => void;
19
+ handleError: (error: string) => void;
20
+ }
21
+
22
+ export function useDocumentCapture({
23
+ videoDimensions,
24
+ callbacks,
25
+ customDetectionAlgorithm,
26
+ detectionSettings,
27
+ setCapturedImage,
28
+ setDetectedCorners,
29
+ handleError,
30
+ }: UseDocumentCaptureProps) {
31
+ return useCallback(
32
+ (capturedImageBase64: string | null) => {
33
+ if (!capturedImageBase64) return;
34
+
35
+ setCapturedImage(capturedImageBase64);
36
+ callbacks.onImageCapture?.(capturedImageBase64);
37
+ callbacks.onScanAttempt?.();
38
+
39
+ // Use custom detection algorithm if provided
40
+ if (customDetectionAlgorithm) {
41
+ customDetectionAlgorithm(capturedImageBase64, videoDimensions)
42
+ .then((corners) => {
43
+ if (corners) {
44
+ setDetectedCorners(corners);
45
+ callbacks.onStatusChange?.("detected");
46
+ callbacks.onDocumentDetected?.(corners, capturedImageBase64);
47
+ } else {
48
+ handleError("Document not detected by custom algorithm");
49
+ }
50
+ })
51
+ .catch((error) => {
52
+ const errorMessage = error?.message || "Custom detection failed";
53
+ handleError(errorMessage);
54
+ });
55
+ } else {
56
+ scanDocument(
57
+ capturedImageBase64,
58
+ videoDimensions,
59
+ (corners) => {
60
+ setDetectedCorners(corners);
61
+ callbacks.onStatusChange?.("detected");
62
+ callbacks.onDocumentDetected?.(corners, capturedImageBase64);
63
+ },
64
+ (error) => {
65
+ handleError(error);
66
+ },
67
+ detectionSettings
68
+ );
69
+ }
70
+ },
71
+ [
72
+ videoDimensions,
73
+ callbacks,
74
+ customDetectionAlgorithm,
75
+ detectionSettings,
76
+ setCapturedImage,
77
+ setDetectedCorners,
78
+ handleError,
79
+ ]
80
+ );
81
+ }
@@ -0,0 +1,80 @@
1
+ import { useCallback, useState } from "react";
2
+ import { DocumentCorners, DocumentScannerCallbacks } from "../types";
3
+ import { DEFAULT_VIDEO_DIMENSIONS } from "../consts";
4
+
5
+ export interface UseDocumentScannerReturn {
6
+ videoDimensions: { width: number; height: number };
7
+ detectedCorners: DocumentCorners | undefined;
8
+ capturedImage: string | null;
9
+ handleVideoReady: (dimensions: { width: number; height: number }) => void;
10
+ handleCornersChange: (newCorners: DocumentCorners) => void;
11
+ handleError: (error: string) => void;
12
+ handleImageCrop: (croppedImageBase64: string) => void;
13
+ handleRetry: () => void;
14
+ setCapturedImage: (image: string | null) => void;
15
+ setDetectedCorners: (corners: DocumentCorners | undefined) => void;
16
+ }
17
+
18
+ export function useDocumentScanner(
19
+ callbacks: DocumentScannerCallbacks
20
+ ): UseDocumentScannerReturn {
21
+ const [videoDimensions, setVideoDimensions] = useState(
22
+ DEFAULT_VIDEO_DIMENSIONS
23
+ );
24
+ const [detectedCorners, setDetectedCorners] = useState<
25
+ DocumentCorners | undefined
26
+ >();
27
+ const [capturedImage, setCapturedImage] = useState<string | null>(null);
28
+
29
+ const handleVideoReady = useCallback(
30
+ (dimensions: { width: number; height: number }) => {
31
+ setVideoDimensions(dimensions);
32
+ callbacks.onStatusChange?.("scanning");
33
+ callbacks.onCameraReady?.(dimensions);
34
+ },
35
+ [callbacks]
36
+ );
37
+
38
+ const handleCornersChange = useCallback(
39
+ (newCorners: DocumentCorners) => {
40
+ setDetectedCorners(newCorners);
41
+ callbacks.onCornersChanged?.(newCorners);
42
+ },
43
+ [callbacks]
44
+ );
45
+
46
+ const handleError = useCallback(
47
+ (error: string) => {
48
+ callbacks.onStatusChange?.("error");
49
+ callbacks.onError?.(error);
50
+ },
51
+ [callbacks]
52
+ );
53
+
54
+ const handleImageCrop = useCallback(
55
+ (croppedImageBase64: string) => {
56
+ callbacks.onStatusChange?.("cropped");
57
+ callbacks.onDocumentCropped?.(croppedImageBase64);
58
+ },
59
+ [callbacks]
60
+ );
61
+
62
+ const handleRetry = useCallback(() => {
63
+ setDetectedCorners(undefined);
64
+ setCapturedImage(null);
65
+ callbacks.onStatusChange?.("scanning");
66
+ }, [callbacks]);
67
+
68
+ return {
69
+ videoDimensions,
70
+ detectedCorners,
71
+ capturedImage,
72
+ handleVideoReady,
73
+ handleCornersChange,
74
+ handleError,
75
+ handleImageCrop,
76
+ handleRetry,
77
+ setCapturedImage,
78
+ setDetectedCorners,
79
+ };
80
+ }
@@ -0,0 +1,38 @@
1
+ import { useCallback } from "react";
2
+ import { perspectiveCorrection } from "../utils/perspective-correction";
3
+ import { DocumentCorners, Dimensions } from "../types";
4
+
5
+ export interface UsePerspectiveCropProps {
6
+ videoDimensions: Dimensions;
7
+ imageQuality: number;
8
+ handleImageCrop: (croppedImageBase64: string) => void;
9
+ handleError: (error: string) => void;
10
+ }
11
+
12
+ export function usePerspectiveCrop({
13
+ videoDimensions,
14
+ imageQuality,
15
+ handleImageCrop,
16
+ handleError,
17
+ }: UsePerspectiveCropProps) {
18
+ return useCallback(
19
+ async (capturedImage: string, detectedCorners: DocumentCorners) => {
20
+ if (!capturedImage || !detectedCorners) return;
21
+
22
+ try {
23
+ const croppedImage = await perspectiveCorrection(
24
+ capturedImage,
25
+ detectedCorners,
26
+ videoDimensions,
27
+ { imageQuality }
28
+ );
29
+ handleImageCrop(croppedImage);
30
+ } catch (error) {
31
+ const errorMessage =
32
+ error instanceof Error ? error.message : "Cropping failed";
33
+ handleError(errorMessage);
34
+ }
35
+ },
36
+ [videoDimensions, imageQuality, handleImageCrop, handleError]
37
+ );
38
+ }
@@ -0,0 +1,255 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { Loader } from "lucide-react";
3
+ import { cn } from "@repo/ayasofyazilim-ui/lib/utils";
4
+ import { Webcam } from "../webcam";
5
+ import {
6
+ DEFAULT_CAPTURE_INTERVAL,
7
+ DEFAULT_IMAGE_QUALITY,
8
+ DEFAULT_MAX_DOCUMENT_SIZE,
9
+ DEFAULT_MIN_DOCUMENT_SIZE,
10
+ } from "./consts";
11
+ import { CornerAdjustment } from "./corner-adjustment";
12
+ import { useDocumentCapture } from "./hooks/use-document-capture";
13
+ import { useDocumentScanner } from "./hooks/use-document-scanner";
14
+ import { usePerspectiveCrop } from "./hooks/use-perspective-crop";
15
+ import { DocumentScannerProps, ScannerStatus } from "./types";
16
+ import { Skeleton } from "@repo/ayasofyazilim-ui/components/skeleton";
17
+
18
+ export function DocumentScanner({
19
+ // Core callbacks
20
+ onDocumentCropped,
21
+ onDocumentDetected,
22
+ onCornersChanged,
23
+ onError,
24
+ onStatusChange,
25
+ onCameraReady,
26
+ onScanAttempt,
27
+ onImageCapture,
28
+
29
+ // Basic configuration
30
+ className = "",
31
+ captureInterval = DEFAULT_CAPTURE_INTERVAL,
32
+
33
+ // Feature toggles
34
+ allowCrop = true,
35
+ allowRetry = true,
36
+ allowCornerAdjustment = true,
37
+ allowCameraSwitch = true,
38
+ showWebcamControls = false,
39
+
40
+ // UI Text customization
41
+ cropButtonText = "Crop",
42
+ retryButtonText = "Scan Again",
43
+
44
+ // Visual styling
45
+ cornerColor = "bg-white/80",
46
+ cornerTouchAreaSize = 20,
47
+
48
+ // Detection settings
49
+ minDocumentSize = DEFAULT_MIN_DOCUMENT_SIZE,
50
+ maxDocumentSize = DEFAULT_MAX_DOCUMENT_SIZE,
51
+ detectionConfidence = 0.8,
52
+
53
+ // Image processing
54
+ imageQuality = DEFAULT_IMAGE_QUALITY,
55
+
56
+ // Magnifier settings
57
+ showMagnifier = true,
58
+ magnifierSize = 60,
59
+ zoomLevel = 2,
60
+
61
+ // Advanced settings
62
+ customDetectionAlgorithm,
63
+
64
+ // Custom components
65
+ customControls,
66
+ customOverlay,
67
+
68
+ // Webcam interface
69
+ interfaceLocation = "absolute",
70
+ showBorder = true,
71
+ }: DocumentScannerProps) {
72
+ const [webCamKey, setWebCamKey] = useState(() => Date.now().toString());
73
+ const [status, setStatus] = useState<ScannerStatus>("scanning");
74
+
75
+ // Combine callbacks for hooks
76
+ const callbacks = useMemo(
77
+ () => ({
78
+ onDocumentCropped,
79
+ onDocumentDetected,
80
+ onCornersChanged,
81
+ onError,
82
+ onStatusChange: (newStatus: ScannerStatus) => {
83
+ setStatus(newStatus);
84
+ onStatusChange?.(newStatus);
85
+ },
86
+ onCameraReady,
87
+ onScanAttempt,
88
+ onImageCapture,
89
+ }),
90
+ [
91
+ onDocumentCropped,
92
+ onDocumentDetected,
93
+ onCornersChanged,
94
+ onError,
95
+ onStatusChange,
96
+ onCameraReady,
97
+ onScanAttempt,
98
+ onImageCapture,
99
+ ]
100
+ );
101
+
102
+ // Detection settings
103
+ const detectionSettings = useMemo(
104
+ () => ({
105
+ minDocumentSize,
106
+ maxDocumentSize,
107
+ detectionConfidence,
108
+ }),
109
+ [minDocumentSize, maxDocumentSize, detectionConfidence]
110
+ );
111
+
112
+ // Main document scanner state
113
+ const {
114
+ videoDimensions,
115
+ detectedCorners,
116
+ capturedImage,
117
+ handleVideoReady,
118
+ handleCornersChange,
119
+ handleError,
120
+ handleImageCrop,
121
+ handleRetry: originalHandleRetry,
122
+ setCapturedImage,
123
+ setDetectedCorners,
124
+ } = useDocumentScanner(callbacks);
125
+
126
+ // Enhanced retry handler that resets webcam
127
+ const handleRetry = useCallback(() => {
128
+ originalHandleRetry();
129
+ setWebCamKey(Date.now().toString());
130
+ }, [originalHandleRetry]);
131
+
132
+ // Document capture logic
133
+ const handleCapture = useDocumentCapture({
134
+ videoDimensions,
135
+ callbacks,
136
+ customDetectionAlgorithm,
137
+ detectionSettings,
138
+ setCapturedImage,
139
+ setDetectedCorners,
140
+ handleError,
141
+ });
142
+
143
+ // Perspective cropping logic
144
+ const handleCrop = usePerspectiveCrop({
145
+ videoDimensions,
146
+ imageQuality,
147
+ handleImageCrop,
148
+ handleError,
149
+ });
150
+
151
+ // Webcam configuration
152
+ const webcamCallbacks = useMemo(
153
+ () => ({
154
+ onWebcamReady: handleVideoReady,
155
+ onAutoPhotoCaptured: handleCapture,
156
+ }),
157
+ [handleVideoReady, handleCapture]
158
+ );
159
+
160
+ const webcamClassNames = useMemo(
161
+ () => ({
162
+ placeholder: "block",
163
+ }),
164
+ []
165
+ );
166
+
167
+ const webcamAutoCapture = useMemo(
168
+ () => ({
169
+ captureInterval,
170
+ hasInterface: showWebcamControls,
171
+ startOnReady: true,
172
+ quality: imageQuality,
173
+ stopOnCapture: true,
174
+ }),
175
+ [captureInterval, imageQuality, showWebcamControls]
176
+ );
177
+
178
+ const webcamPlaceholder = useMemo(() => {
179
+ if (status === "scanning") {
180
+ return (
181
+ <Skeleton className="w-full h-full bg-white/50 flex items-center justify-center">
182
+ <Loader className="size-4 animate-spin" />
183
+ </Skeleton>
184
+ );
185
+ }
186
+ if (detectedCorners && capturedImage && allowCornerAdjustment) {
187
+ return (
188
+ <CornerAdjustment
189
+ capturedImage={capturedImage}
190
+ detectedCorners={detectedCorners}
191
+ videoDimensions={videoDimensions}
192
+ onCornersChange={handleCornersChange}
193
+ onCrop={() => handleCrop(capturedImage, detectedCorners)}
194
+ onRetry={handleRetry}
195
+ cornerColor={cornerColor}
196
+ cornerTouchAreaSize={cornerTouchAreaSize}
197
+ allowCrop={allowCrop}
198
+ allowRetry={allowRetry}
199
+ cropButtonText={cropButtonText}
200
+ retryButtonText={retryButtonText}
201
+ showMagnifier={showMagnifier}
202
+ magnifierSize={magnifierSize}
203
+ zoomLevel={zoomLevel}
204
+ />
205
+ );
206
+ }
207
+
208
+ return undefined;
209
+ }, [
210
+ status,
211
+ detectedCorners,
212
+ allowCornerAdjustment,
213
+ videoDimensions,
214
+ capturedImage,
215
+ cornerTouchAreaSize,
216
+ cornerColor,
217
+ handleCornersChange,
218
+ handleCrop,
219
+ handleRetry,
220
+ allowCrop,
221
+ allowRetry,
222
+ cropButtonText,
223
+ retryButtonText,
224
+ showMagnifier,
225
+ magnifierSize,
226
+ zoomLevel,
227
+ ]);
228
+
229
+ const isInCornerAdjustmentMode =
230
+ (detectedCorners && capturedImage && allowCornerAdjustment) || false;
231
+
232
+ return (
233
+ <div
234
+ className={cn(
235
+ "container relative mx-auto max-w-3xl overflow-hidden p-4",
236
+ className
237
+ )}
238
+ >
239
+ {customOverlay}
240
+ <Webcam
241
+ key={webCamKey}
242
+ classNames={webcamClassNames}
243
+ callbacks={webcamCallbacks}
244
+ defaultCamera="back"
245
+ autoCapture={webcamAutoCapture}
246
+ allowCameraSwitch={allowCameraSwitch}
247
+ placeholder={webcamPlaceholder}
248
+ interfaceLocation={interfaceLocation}
249
+ showBorder={showBorder}
250
+ forceHideInterface={isInCornerAdjustmentMode}
251
+ />
252
+ {customControls && <div className="mt-4">{customControls}</div>}
253
+ </div>
254
+ );
255
+ }