react-native-rectangle-doc-scanner 3.122.0 → 3.123.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.
@@ -125,6 +125,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
125
125
  const [rotationDegrees, setRotationDegrees] = (0, react_1.useState)(0);
126
126
  const [capturedPhotos, setCapturedPhotos] = (0, react_1.useState)([]);
127
127
  const [currentPhotoIndex, setCurrentPhotoIndex] = (0, react_1.useState)(0);
128
+ const [scannerSession, setScannerSession] = (0, react_1.useState)(0);
128
129
  const resolvedGridColor = gridColor ?? overlayColor;
129
130
  const docScannerRef = (0, react_1.useRef)(null);
130
131
  const captureModeRef = (0, react_1.useRef)(null);
@@ -132,6 +133,29 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
132
133
  const rectangleCaptureTimeoutRef = (0, react_1.useRef)(null);
133
134
  const rectangleHintTimeoutRef = (0, react_1.useRef)(null);
134
135
  const isBusinessMode = type === 'business';
136
+ const resetScannerView = (0, react_1.useCallback)((options) => {
137
+ setProcessing(false);
138
+ setCroppedImageData(null);
139
+ setRotationDegrees(0);
140
+ setRectangleDetected(false);
141
+ setRectangleHint(false);
142
+ captureModeRef.current = null;
143
+ captureInProgressRef.current = false;
144
+ if (rectangleCaptureTimeoutRef.current) {
145
+ clearTimeout(rectangleCaptureTimeoutRef.current);
146
+ rectangleCaptureTimeoutRef.current = null;
147
+ }
148
+ if (rectangleHintTimeoutRef.current) {
149
+ clearTimeout(rectangleHintTimeoutRef.current);
150
+ rectangleHintTimeoutRef.current = null;
151
+ }
152
+ if (docScannerRef.current?.reset) {
153
+ docScannerRef.current.reset();
154
+ }
155
+ if (options?.remount) {
156
+ setScannerSession((prev) => prev + 1);
157
+ }
158
+ }, []);
135
159
  const mergedStrings = (0, react_1.useMemo)(() => ({
136
160
  captureHint: strings?.captureHint,
137
161
  manualHint: strings?.manualHint,
@@ -191,15 +215,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
191
215
  });
192
216
  }
193
217
  catch (error) {
194
- setProcessing(false);
195
- // Reset capture state when cropper fails or is cancelled
196
- captureInProgressRef.current = false;
197
- captureModeRef.current = null;
198
- setRectangleDetected(false);
199
- setRectangleHint(false);
200
- if (docScannerRef.current?.reset) {
201
- docScannerRef.current.reset();
202
- }
218
+ resetScannerView({ remount: true });
203
219
  const errorCode = error?.code;
204
220
  const errorMessageRaw = error?.message ?? String(error);
205
221
  const errorMessage = typeof errorMessageRaw === 'string' ? errorMessageRaw : String(errorMessageRaw);
@@ -220,7 +236,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
220
236
  emitError(error instanceof Error ? error : new Error(errorMessage), 'Failed to crop image. Please try again.');
221
237
  }
222
238
  }
223
- }, [cropWidth, cropHeight, emitError]);
239
+ }, [cropWidth, cropHeight, emitError, resetScannerView]);
224
240
  const handleCapture = (0, react_1.useCallback)(async (document) => {
225
241
  console.log('[FullDocScanner] handleCapture called:', {
226
242
  origin: document.origin,
@@ -419,25 +435,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
419
435
  setCapturedPhotos([currentPhoto]);
420
436
  setCurrentPhotoIndex(1);
421
437
  // 확인 화면을 닫고 카메라로 돌아감
422
- setCroppedImageData(null);
423
- setRotationDegrees(0);
424
- setProcessing(false);
425
- setRectangleDetected(false);
426
- setRectangleHint(false);
427
- captureModeRef.current = null;
428
- captureInProgressRef.current = false;
429
- if (rectangleCaptureTimeoutRef.current) {
430
- clearTimeout(rectangleCaptureTimeoutRef.current);
431
- rectangleCaptureTimeoutRef.current = null;
432
- }
433
- if (rectangleHintTimeoutRef.current) {
434
- clearTimeout(rectangleHintTimeoutRef.current);
435
- rectangleHintTimeoutRef.current = null;
436
- }
437
- if (docScannerRef.current?.reset) {
438
- docScannerRef.current.reset();
439
- }
440
- }, [croppedImageData, rotationDegrees]);
438
+ resetScannerView({ remount: true });
439
+ }, [croppedImageData, resetScannerView, rotationDegrees]);
441
440
  const handleRetake = (0, react_1.useCallback)(() => {
442
441
  console.log('[FullDocScanner] Retake - clearing cropped image and resetting scanner');
443
442
  // Business 모드에서 두 번째 사진을 다시 찍는 경우, 첫 번째 사진 유지
@@ -451,26 +450,8 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
451
450
  setCapturedPhotos([]);
452
451
  setCurrentPhotoIndex(0);
453
452
  }
454
- setCroppedImageData(null);
455
- setRotationDegrees(0);
456
- setProcessing(false);
457
- setRectangleDetected(false);
458
- setRectangleHint(false);
459
- captureModeRef.current = null;
460
- captureInProgressRef.current = false;
461
- if (rectangleCaptureTimeoutRef.current) {
462
- clearTimeout(rectangleCaptureTimeoutRef.current);
463
- rectangleCaptureTimeoutRef.current = null;
464
- }
465
- if (rectangleHintTimeoutRef.current) {
466
- clearTimeout(rectangleHintTimeoutRef.current);
467
- rectangleHintTimeoutRef.current = null;
468
- }
469
- // Reset DocScanner state
470
- if (docScannerRef.current?.reset) {
471
- docScannerRef.current.reset();
472
- }
473
- }, [capturedPhotos.length, isBusinessMode]);
453
+ resetScannerView({ remount: true });
454
+ }, [capturedPhotos.length, isBusinessMode, resetScannerView]);
474
455
  const handleRectangleDetect = (0, react_1.useCallback)((event) => {
475
456
  const stableCounter = event.stableCounter ?? 0;
476
457
  const rectangleCoordinates = event.rectangleOnScreen ?? event.rectangleCoordinates;
@@ -548,7 +529,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
548
529
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
549
530
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
550
531
  react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
551
- react_1.default.createElement(DocScanner_1.DocScanner, { ref: docScannerRef, autoCapture: false, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, onRectangleDetect: handleRectangleDetect, showManualCaptureButton: false, enableTorch: flashEnabled },
532
+ react_1.default.createElement(DocScanner_1.DocScanner, { key: scannerSession, ref: docScannerRef, autoCapture: false, overlayColor: overlayColor, showGrid: showGrid, gridColor: resolvedGridColor, gridLineWidth: gridLineWidth, minStableFrames: minStableFrames ?? 6, detectionConfig: detectionConfig, onCapture: handleCapture, onRectangleDetect: handleRectangleDetect, showManualCaptureButton: false, enableTorch: flashEnabled },
552
533
  react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
553
534
  react_1.default.createElement(react_native_1.TouchableOpacity, { style: [
554
535
  styles.iconButton,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.122.0",
3
+ "version": "3.123.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -183,6 +183,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
183
183
  const [rotationDegrees, setRotationDegrees] = useState(0);
184
184
  const [capturedPhotos, setCapturedPhotos] = useState<FullDocScannerResult[]>([]);
185
185
  const [currentPhotoIndex, setCurrentPhotoIndex] = useState(0);
186
+ const [scannerSession, setScannerSession] = useState(0);
186
187
  const resolvedGridColor = gridColor ?? overlayColor;
187
188
  const docScannerRef = useRef<DocScannerHandle | null>(null);
188
189
  const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
@@ -192,6 +193,37 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
192
193
 
193
194
  const isBusinessMode = type === 'business';
194
195
 
196
+ const resetScannerView = useCallback(
197
+ (options?: { remount?: boolean }) => {
198
+ setProcessing(false);
199
+ setCroppedImageData(null);
200
+ setRotationDegrees(0);
201
+ setRectangleDetected(false);
202
+ setRectangleHint(false);
203
+ captureModeRef.current = null;
204
+ captureInProgressRef.current = false;
205
+
206
+ if (rectangleCaptureTimeoutRef.current) {
207
+ clearTimeout(rectangleCaptureTimeoutRef.current);
208
+ rectangleCaptureTimeoutRef.current = null;
209
+ }
210
+
211
+ if (rectangleHintTimeoutRef.current) {
212
+ clearTimeout(rectangleHintTimeoutRef.current);
213
+ rectangleHintTimeoutRef.current = null;
214
+ }
215
+
216
+ if (docScannerRef.current?.reset) {
217
+ docScannerRef.current.reset();
218
+ }
219
+
220
+ if (options?.remount) {
221
+ setScannerSession((prev) => prev + 1);
222
+ }
223
+ },
224
+ [],
225
+ );
226
+
195
227
  const mergedStrings = useMemo(
196
228
  () => ({
197
229
  captureHint: strings?.captureHint,
@@ -268,20 +300,12 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
268
300
  base64: croppedImage.data ?? undefined,
269
301
  });
270
302
  } catch (error) {
271
- setProcessing(false);
272
-
273
- // Reset capture state when cropper fails or is cancelled
274
- captureInProgressRef.current = false;
275
- captureModeRef.current = null;
276
- setRectangleDetected(false);
277
- setRectangleHint(false);
278
- if (docScannerRef.current?.reset) {
279
- docScannerRef.current.reset();
280
- }
303
+ resetScannerView({ remount: true });
281
304
 
282
305
  const errorCode = (error as any)?.code;
283
306
  const errorMessageRaw = (error as any)?.message ?? String(error);
284
- const errorMessage = typeof errorMessageRaw === 'string' ? errorMessageRaw : String(errorMessageRaw);
307
+ const errorMessage =
308
+ typeof errorMessageRaw === 'string' ? errorMessageRaw : String(errorMessageRaw);
285
309
  const normalizedMessage = errorMessage.toLowerCase();
286
310
  const isUserCancelled =
287
311
  errorCode === 'E_PICKER_CANCELLED' ||
@@ -309,7 +333,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
309
333
  }
310
334
  }
311
335
  },
312
- [cropWidth, cropHeight, emitError],
336
+ [cropWidth, cropHeight, emitError, resetScannerView],
313
337
  );
314
338
 
315
339
  const handleCapture = useCallback(
@@ -564,25 +588,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
564
588
  setCurrentPhotoIndex(1);
565
589
 
566
590
  // 확인 화면을 닫고 카메라로 돌아감
567
- setCroppedImageData(null);
568
- setRotationDegrees(0);
569
- setProcessing(false);
570
- setRectangleDetected(false);
571
- setRectangleHint(false);
572
- captureModeRef.current = null;
573
- captureInProgressRef.current = false;
574
- if (rectangleCaptureTimeoutRef.current) {
575
- clearTimeout(rectangleCaptureTimeoutRef.current);
576
- rectangleCaptureTimeoutRef.current = null;
577
- }
578
- if (rectangleHintTimeoutRef.current) {
579
- clearTimeout(rectangleHintTimeoutRef.current);
580
- rectangleHintTimeoutRef.current = null;
581
- }
582
- if (docScannerRef.current?.reset) {
583
- docScannerRef.current.reset();
584
- }
585
- }, [croppedImageData, rotationDegrees]);
591
+ resetScannerView({ remount: true });
592
+ }, [croppedImageData, resetScannerView, rotationDegrees]);
586
593
 
587
594
 
588
595
  const handleRetake = useCallback(() => {
@@ -599,26 +606,8 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
599
606
  setCurrentPhotoIndex(0);
600
607
  }
601
608
 
602
- setCroppedImageData(null);
603
- setRotationDegrees(0);
604
- setProcessing(false);
605
- setRectangleDetected(false);
606
- setRectangleHint(false);
607
- captureModeRef.current = null;
608
- captureInProgressRef.current = false;
609
- if (rectangleCaptureTimeoutRef.current) {
610
- clearTimeout(rectangleCaptureTimeoutRef.current);
611
- rectangleCaptureTimeoutRef.current = null;
612
- }
613
- if (rectangleHintTimeoutRef.current) {
614
- clearTimeout(rectangleHintTimeoutRef.current);
615
- rectangleHintTimeoutRef.current = null;
616
- }
617
- // Reset DocScanner state
618
- if (docScannerRef.current?.reset) {
619
- docScannerRef.current.reset();
620
- }
621
- }, [capturedPhotos.length, isBusinessMode]);
609
+ resetScannerView({ remount: true });
610
+ }, [capturedPhotos.length, isBusinessMode, resetScannerView]);
622
611
 
623
612
  const handleRectangleDetect = useCallback((event: RectangleDetectEvent) => {
624
613
  const stableCounter = event.stableCounter ?? 0;
@@ -781,6 +770,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
781
770
  ) : (
782
771
  <View style={styles.flex}>
783
772
  <DocScanner
773
+ key={scannerSession}
784
774
  ref={docScannerRef}
785
775
  autoCapture={false}
786
776
  overlayColor={overlayColor}