react-native-rectangle-doc-scanner 3.54.0 → 3.56.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.
@@ -205,8 +205,9 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
205
205
  }), [capture]);
206
206
  const overlayPolygon = detectedRectangle?.rectangleOnScreen ?? detectedRectangle?.rectangleCoordinates ?? null;
207
207
  const overlayIsActive = autoCapture ? isAutoCapturing : (detectedRectangle?.stableCounter ?? 0) > 0;
208
+ const detectionThreshold = autoCapture ? minStableFrames : 9999;
208
209
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
209
- react_1.default.createElement(react_native_document_scanner_1.default, { ref: scannerRef, style: styles.scanner, detectionCountBeforeCapture: minStableFrames, overlayColor: overlayColor, enableTorch: enableTorch, quality: normalizedQuality, useBase64: useBase64, manualOnly: !autoCapture, detectionConfig: detectionConfig, onPictureTaken: handlePictureTaken, onError: handleError, onRectangleDetect: handleRectangleDetect }),
210
+ react_1.default.createElement(react_native_document_scanner_1.default, { ref: scannerRef, style: styles.scanner, detectionCountBeforeCapture: detectionThreshold, overlayColor: overlayColor, enableTorch: enableTorch, quality: normalizedQuality, useBase64: useBase64, manualOnly: !autoCapture, detectionConfig: detectionConfig, onPictureTaken: handlePictureTaken, onError: handleError, onRectangleDetect: handleRectangleDetect }),
210
211
  showGrid && overlayPolygon && (react_1.default.createElement(overlay_1.ScannerOverlay, { active: overlayIsActive, color: gridColor ?? overlayColor, lineWidth: gridLineWidth, polygon: overlayPolygon })),
211
212
  showManualCaptureButton && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: handleManualCapture })),
212
213
  children));
@@ -62,6 +62,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
62
62
  const docScannerRef = (0, react_1.useRef)(null);
63
63
  const captureModeRef = (0, react_1.useRef)(null);
64
64
  const captureInProgressRef = (0, react_1.useRef)(false);
65
+ const rectangleTimeoutRef = (0, react_1.useRef)(null);
65
66
  const mergedStrings = (0, react_1.useMemo)(() => ({
66
67
  captureHint: strings?.captureHint,
67
68
  manualHint: strings?.manualHint,
@@ -236,6 +237,10 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
236
237
  setRectangleDetected(false);
237
238
  captureModeRef.current = null;
238
239
  captureInProgressRef.current = false;
240
+ if (rectangleTimeoutRef.current) {
241
+ clearTimeout(rectangleTimeoutRef.current);
242
+ rectangleTimeoutRef.current = null;
243
+ }
239
244
  // Reset DocScanner state
240
245
  if (docScannerRef.current?.reset) {
241
246
  docScannerRef.current.reset();
@@ -244,7 +249,20 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
244
249
  const handleRectangleDetect = (0, react_1.useCallback)((event) => {
245
250
  const stableCounter = event.stableCounter ?? 0;
246
251
  const hasRectangle = Boolean(event.rectangleOnScreen ?? event.rectangleCoordinates);
247
- const isGoodRectangle = hasRectangle;
252
+ const isGoodRectangle = hasRectangle && event.lastDetectionType === 0;
253
+ if (hasRectangle) {
254
+ if (rectangleTimeoutRef.current) {
255
+ clearTimeout(rectangleTimeoutRef.current);
256
+ }
257
+ rectangleTimeoutRef.current = setTimeout(() => {
258
+ rectangleTimeoutRef.current = null;
259
+ setRectangleDetected(false);
260
+ }, 300);
261
+ }
262
+ else if (rectangleTimeoutRef.current) {
263
+ clearTimeout(rectangleTimeoutRef.current);
264
+ rectangleTimeoutRef.current = null;
265
+ }
248
266
  setRectangleDetected((prev) => {
249
267
  if (prev !== isGoodRectangle) {
250
268
  console.log('[FullDocScanner] Rectangle detection update', {
@@ -257,6 +275,11 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
257
275
  return isGoodRectangle;
258
276
  });
259
277
  }, []);
278
+ (0, react_1.useEffect)(() => () => {
279
+ if (rectangleTimeoutRef.current) {
280
+ clearTimeout(rectangleTimeoutRef.current);
281
+ }
282
+ }, []);
260
283
  return (react_1.default.createElement(react_native_1.View, { style: styles.container },
261
284
  croppedImageData ? (
262
285
  // check_DP: Show confirmation screen
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.54.0",
3
+ "version": "3.56.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -293,12 +293,14 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
293
293
  const overlayPolygon = detectedRectangle?.rectangleOnScreen ?? detectedRectangle?.rectangleCoordinates ?? null;
294
294
  const overlayIsActive = autoCapture ? isAutoCapturing : (detectedRectangle?.stableCounter ?? 0) > 0;
295
295
 
296
- return (
296
+ const detectionThreshold = autoCapture ? minStableFrames : 9999;
297
+
298
+ return (
297
299
  <View style={styles.container}>
298
300
  <DocumentScanner
299
301
  ref={scannerRef}
300
302
  style={styles.scanner}
301
- detectionCountBeforeCapture={minStableFrames}
303
+ detectionCountBeforeCapture={detectionThreshold}
302
304
  overlayColor={overlayColor}
303
305
  enableTorch={enableTorch}
304
306
  quality={normalizedQuality}
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
3
  ActivityIndicator,
4
4
  Alert,
@@ -90,6 +90,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
90
90
  const docScannerRef = useRef<DocScannerHandle | null>(null);
91
91
  const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
92
92
  const captureInProgressRef = useRef(false);
93
+ const rectangleTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
93
94
 
94
95
  const mergedStrings = useMemo(
95
96
  () => ({
@@ -314,6 +315,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
314
315
  setRectangleDetected(false);
315
316
  captureModeRef.current = null;
316
317
  captureInProgressRef.current = false;
318
+ if (rectangleTimeoutRef.current) {
319
+ clearTimeout(rectangleTimeoutRef.current);
320
+ rectangleTimeoutRef.current = null;
321
+ }
317
322
  // Reset DocScanner state
318
323
  if (docScannerRef.current?.reset) {
319
324
  docScannerRef.current.reset();
@@ -323,7 +328,20 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
323
328
  const handleRectangleDetect = useCallback((event: RectangleDetectEvent) => {
324
329
  const stableCounter = event.stableCounter ?? 0;
325
330
  const hasRectangle = Boolean(event.rectangleOnScreen ?? event.rectangleCoordinates);
326
- const isGoodRectangle = hasRectangle;
331
+ const isGoodRectangle = hasRectangle && event.lastDetectionType === 0;
332
+
333
+ if (hasRectangle) {
334
+ if (rectangleTimeoutRef.current) {
335
+ clearTimeout(rectangleTimeoutRef.current);
336
+ }
337
+ rectangleTimeoutRef.current = setTimeout(() => {
338
+ rectangleTimeoutRef.current = null;
339
+ setRectangleDetected(false);
340
+ }, 300);
341
+ } else if (rectangleTimeoutRef.current) {
342
+ clearTimeout(rectangleTimeoutRef.current);
343
+ rectangleTimeoutRef.current = null;
344
+ }
327
345
 
328
346
  setRectangleDetected((prev) => {
329
347
  if (prev !== isGoodRectangle) {
@@ -334,11 +352,16 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
334
352
  isGoodRectangle,
335
353
  });
336
354
  }
337
-
338
355
  return isGoodRectangle;
339
356
  });
340
357
  }, []);
341
358
 
359
+ useEffect(() => () => {
360
+ if (rectangleTimeoutRef.current) {
361
+ clearTimeout(rectangleTimeoutRef.current);
362
+ }
363
+ }, []);
364
+
342
365
  return (
343
366
  <View style={styles.container}>
344
367
  {croppedImageData ? (
@@ -11,6 +11,7 @@
11
11
  @property (nonatomic, assign) BOOL useBase64;
12
12
  @property (nonatomic, assign) BOOL captureMultiple;
13
13
  @property (nonatomic, assign) BOOL saveInAppDocument;
14
+ @property (nonatomic, assign) BOOL manualOnly;
14
15
 
15
16
  - (void) capture;
16
17
 
@@ -78,6 +78,10 @@
78
78
 
79
79
  _lastDetectionType = type;
80
80
 
81
+ if (self.manualOnly) {
82
+ return;
83
+ }
84
+
81
85
  if (self.stableCounter >= self.detectionCountBeforeCapture){
82
86
  NSLog(@"[DocumentScanner] Auto-capture triggered! stableCounter: %ld >= threshold: %ld", (long)self.stableCounter, (long)self.detectionCountBeforeCapture);
83
87
  self.stableCounter = 0; // Reset to prevent multiple captures
@@ -26,6 +26,7 @@ RCT_EXPORT_VIEW_PROPERTY(useFrontCam, BOOL)
26
26
  RCT_EXPORT_VIEW_PROPERTY(useBase64, BOOL)
27
27
  RCT_EXPORT_VIEW_PROPERTY(saveInAppDocument, BOOL)
28
28
  RCT_EXPORT_VIEW_PROPERTY(captureMultiple, BOOL)
29
+ RCT_EXPORT_VIEW_PROPERTY(manualOnly, BOOL)
29
30
  RCT_EXPORT_VIEW_PROPERTY(detectionCountBeforeCapture, NSInteger)
30
31
  RCT_EXPORT_VIEW_PROPERTY(detectionRefreshRateInMS, NSInteger)
31
32
  RCT_EXPORT_VIEW_PROPERTY(saturation, float)