react-native-rectangle-doc-scanner 3.85.0 → 3.87.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.
package/dist/FullDocScanner.js
CHANGED
|
@@ -109,6 +109,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
109
109
|
const [isGalleryOpen, setIsGalleryOpen] = (0, react_1.useState)(false);
|
|
110
110
|
const [rectangleDetected, setRectangleDetected] = (0, react_1.useState)(false);
|
|
111
111
|
const [rectangleHint, setRectangleHint] = (0, react_1.useState)(false);
|
|
112
|
+
const [flashEnabled, setFlashEnabled] = (0, react_1.useState)(false);
|
|
112
113
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
113
114
|
const docScannerRef = (0, react_1.useRef)(null);
|
|
114
115
|
const captureModeRef = (0, react_1.useRef)(null);
|
|
@@ -171,6 +172,9 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
171
172
|
catch (error) {
|
|
172
173
|
console.error('[FullDocScanner] openCropper error:', error);
|
|
173
174
|
setProcessing(false);
|
|
175
|
+
// Reset capture state when cropper fails or is cancelled
|
|
176
|
+
captureInProgressRef.current = false;
|
|
177
|
+
captureModeRef.current = null;
|
|
174
178
|
const errorCode = error?.code;
|
|
175
179
|
const errorMessage = error?.message || String(error);
|
|
176
180
|
if (errorCode === CROPPER_TIMEOUT_CODE || errorMessage === CROPPER_TIMEOUT_CODE) {
|
|
@@ -315,6 +319,9 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
315
319
|
const handleClose = (0, react_1.useCallback)(() => {
|
|
316
320
|
onClose?.();
|
|
317
321
|
}, [onClose]);
|
|
322
|
+
const handleFlashToggle = (0, react_1.useCallback)(() => {
|
|
323
|
+
setFlashEnabled(prev => !prev);
|
|
324
|
+
}, []);
|
|
318
325
|
const handleConfirm = (0, react_1.useCallback)(() => {
|
|
319
326
|
if (croppedImageData) {
|
|
320
327
|
onResult({
|
|
@@ -414,7 +421,7 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
414
421
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.retake)),
|
|
415
422
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.confirmButton, styles.confirmButtonPrimary], onPress: handleConfirm, accessibilityLabel: mergedStrings.confirm, accessibilityRole: "button" },
|
|
416
423
|
react_1.default.createElement(react_native_1.Text, { style: styles.confirmButtonText }, mergedStrings.confirm))))) : (react_1.default.createElement(react_native_1.View, { style: styles.flex },
|
|
417
|
-
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 },
|
|
424
|
+
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 },
|
|
418
425
|
react_1.default.createElement(react_native_1.View, { style: styles.overlayTop, pointerEvents: "box-none" },
|
|
419
426
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.closeButton, onPress: handleClose, accessibilityLabel: mergedStrings.cancel, accessibilityRole: "button" },
|
|
420
427
|
react_1.default.createElement(react_native_1.Text, { style: styles.closeButtonLabel }, "\u00D7"))),
|
|
@@ -423,13 +430,17 @@ const FullDocScanner = ({ onResult, onClose, detectionConfig, overlayColor = '#3
|
|
|
423
430
|
mergedStrings.captureHint && (react_1.default.createElement(react_native_1.Text, { style: styles.captureText }, mergedStrings.captureHint)),
|
|
424
431
|
mergedStrings.manualHint && (react_1.default.createElement(react_native_1.Text, { style: styles.captureText }, mergedStrings.manualHint))))),
|
|
425
432
|
react_1.default.createElement(react_native_1.View, { style: styles.shutterContainer, pointerEvents: "box-none" },
|
|
426
|
-
|
|
427
|
-
react_1.default.createElement(react_native_1.
|
|
433
|
+
react_1.default.createElement(react_native_1.View, { style: styles.leftButtonsContainer },
|
|
434
|
+
enableGallery && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.galleryButton, processing && styles.buttonDisabled], onPress: handleGalleryPick, disabled: processing, accessibilityLabel: mergedStrings.galleryButton, accessibilityRole: "button" },
|
|
435
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.galleryButtonText }, "\uD83D\uDCC1"))),
|
|
436
|
+
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.flashButton, processing && styles.buttonDisabled], onPress: handleFlashToggle, disabled: processing, accessibilityLabel: "Toggle flash", accessibilityRole: "button" },
|
|
437
|
+
react_1.default.createElement(react_native_1.Text, { style: styles.flashButtonText }, flashEnabled ? '⚡' : '⚡️'))),
|
|
428
438
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.shutterButton, processing && styles.buttonDisabled], onPress: triggerManualCapture, disabled: processing, accessibilityLabel: mergedStrings.manualHint, accessibilityRole: "button" },
|
|
429
439
|
react_1.default.createElement(react_native_1.View, { style: [
|
|
430
440
|
styles.shutterInner,
|
|
431
441
|
rectangleHint && { backgroundColor: overlayColor }
|
|
432
|
-
] }))
|
|
442
|
+
] })),
|
|
443
|
+
react_1.default.createElement(react_native_1.View, { style: styles.rightButtonsPlaceholder }))))),
|
|
433
444
|
processing && (react_1.default.createElement(react_native_1.View, { style: styles.processingOverlay },
|
|
434
445
|
react_1.default.createElement(react_native_1.ActivityIndicator, { size: "large", color: overlayColor }),
|
|
435
446
|
mergedStrings.processing && (react_1.default.createElement(react_native_1.Text, { style: styles.processingText }, mergedStrings.processing))))));
|
|
@@ -463,11 +474,20 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
463
474
|
left: 0,
|
|
464
475
|
right: 0,
|
|
465
476
|
flexDirection: 'row',
|
|
466
|
-
justifyContent: '
|
|
477
|
+
justifyContent: 'space-between',
|
|
467
478
|
alignItems: 'center',
|
|
468
|
-
|
|
479
|
+
paddingHorizontal: 40,
|
|
469
480
|
zIndex: 10,
|
|
470
481
|
},
|
|
482
|
+
leftButtonsContainer: {
|
|
483
|
+
flexDirection: 'row',
|
|
484
|
+
gap: 12,
|
|
485
|
+
alignItems: 'center',
|
|
486
|
+
flex: 1,
|
|
487
|
+
},
|
|
488
|
+
rightButtonsPlaceholder: {
|
|
489
|
+
flex: 1,
|
|
490
|
+
},
|
|
471
491
|
closeButton: {
|
|
472
492
|
width: 40,
|
|
473
493
|
height: 40,
|
|
@@ -494,9 +514,9 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
494
514
|
textAlign: 'center',
|
|
495
515
|
},
|
|
496
516
|
galleryButton: {
|
|
497
|
-
width:
|
|
498
|
-
height:
|
|
499
|
-
borderRadius:
|
|
517
|
+
width: 56,
|
|
518
|
+
height: 56,
|
|
519
|
+
borderRadius: 28,
|
|
500
520
|
borderWidth: 3,
|
|
501
521
|
borderColor: '#fff',
|
|
502
522
|
justifyContent: 'center',
|
|
@@ -504,7 +524,20 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
504
524
|
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
505
525
|
},
|
|
506
526
|
galleryButtonText: {
|
|
507
|
-
fontSize:
|
|
527
|
+
fontSize: 24,
|
|
528
|
+
},
|
|
529
|
+
flashButton: {
|
|
530
|
+
width: 56,
|
|
531
|
+
height: 56,
|
|
532
|
+
borderRadius: 28,
|
|
533
|
+
borderWidth: 3,
|
|
534
|
+
borderColor: '#fff',
|
|
535
|
+
justifyContent: 'center',
|
|
536
|
+
alignItems: 'center',
|
|
537
|
+
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
538
|
+
},
|
|
539
|
+
flashButtonText: {
|
|
540
|
+
fontSize: 24,
|
|
508
541
|
},
|
|
509
542
|
shutterButton: {
|
|
510
543
|
width: 80,
|
package/package.json
CHANGED
package/src/FullDocScanner.tsx
CHANGED
|
@@ -150,6 +150,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
150
150
|
const [isGalleryOpen, setIsGalleryOpen] = useState(false);
|
|
151
151
|
const [rectangleDetected, setRectangleDetected] = useState(false);
|
|
152
152
|
const [rectangleHint, setRectangleHint] = useState(false);
|
|
153
|
+
const [flashEnabled, setFlashEnabled] = useState(false);
|
|
153
154
|
const resolvedGridColor = gridColor ?? overlayColor;
|
|
154
155
|
const docScannerRef = useRef<DocScannerHandle | null>(null);
|
|
155
156
|
const captureModeRef = useRef<'grid' | 'no-grid' | null>(null);
|
|
@@ -231,6 +232,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
231
232
|
console.error('[FullDocScanner] openCropper error:', error);
|
|
232
233
|
setProcessing(false);
|
|
233
234
|
|
|
235
|
+
// Reset capture state when cropper fails or is cancelled
|
|
236
|
+
captureInProgressRef.current = false;
|
|
237
|
+
captureModeRef.current = null;
|
|
238
|
+
|
|
234
239
|
const errorCode = (error as any)?.code;
|
|
235
240
|
const errorMessage = (error as any)?.message || String(error);
|
|
236
241
|
|
|
@@ -422,6 +427,10 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
422
427
|
onClose?.();
|
|
423
428
|
}, [onClose]);
|
|
424
429
|
|
|
430
|
+
const handleFlashToggle = useCallback(() => {
|
|
431
|
+
setFlashEnabled(prev => !prev);
|
|
432
|
+
}, []);
|
|
433
|
+
|
|
425
434
|
const handleConfirm = useCallback(() => {
|
|
426
435
|
if (croppedImageData) {
|
|
427
436
|
onResult({
|
|
@@ -565,6 +574,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
565
574
|
onCapture={handleCapture}
|
|
566
575
|
onRectangleDetect={handleRectangleDetect}
|
|
567
576
|
showManualCaptureButton={false}
|
|
577
|
+
enableTorch={flashEnabled}
|
|
568
578
|
>
|
|
569
579
|
<View style={styles.overlayTop} pointerEvents="box-none">
|
|
570
580
|
<TouchableOpacity
|
|
@@ -589,17 +599,28 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
589
599
|
</View>
|
|
590
600
|
)}
|
|
591
601
|
<View style={styles.shutterContainer} pointerEvents="box-none">
|
|
592
|
-
{
|
|
602
|
+
<View style={styles.leftButtonsContainer}>
|
|
603
|
+
{enableGallery && (
|
|
604
|
+
<TouchableOpacity
|
|
605
|
+
style={[styles.galleryButton, processing && styles.buttonDisabled]}
|
|
606
|
+
onPress={handleGalleryPick}
|
|
607
|
+
disabled={processing}
|
|
608
|
+
accessibilityLabel={mergedStrings.galleryButton}
|
|
609
|
+
accessibilityRole="button"
|
|
610
|
+
>
|
|
611
|
+
<Text style={styles.galleryButtonText}>📁</Text>
|
|
612
|
+
</TouchableOpacity>
|
|
613
|
+
)}
|
|
593
614
|
<TouchableOpacity
|
|
594
|
-
style={[styles.
|
|
595
|
-
onPress={
|
|
615
|
+
style={[styles.flashButton, processing && styles.buttonDisabled]}
|
|
616
|
+
onPress={handleFlashToggle}
|
|
596
617
|
disabled={processing}
|
|
597
|
-
accessibilityLabel=
|
|
618
|
+
accessibilityLabel="Toggle flash"
|
|
598
619
|
accessibilityRole="button"
|
|
599
620
|
>
|
|
600
|
-
<Text style={styles.
|
|
621
|
+
<Text style={styles.flashButtonText}>{flashEnabled ? '⚡' : '⚡️'}</Text>
|
|
601
622
|
</TouchableOpacity>
|
|
602
|
-
|
|
623
|
+
</View>
|
|
603
624
|
<TouchableOpacity
|
|
604
625
|
style={[styles.shutterButton, processing && styles.buttonDisabled]}
|
|
605
626
|
onPress={triggerManualCapture}
|
|
@@ -612,6 +633,7 @@ export const FullDocScanner: React.FC<FullDocScannerProps> = ({
|
|
|
612
633
|
rectangleHint && { backgroundColor: overlayColor }
|
|
613
634
|
]} />
|
|
614
635
|
</TouchableOpacity>
|
|
636
|
+
<View style={styles.rightButtonsPlaceholder} />
|
|
615
637
|
</View>
|
|
616
638
|
</DocScanner>
|
|
617
639
|
</View>
|
|
@@ -657,11 +679,20 @@ const styles = StyleSheet.create({
|
|
|
657
679
|
left: 0,
|
|
658
680
|
right: 0,
|
|
659
681
|
flexDirection: 'row',
|
|
660
|
-
justifyContent: '
|
|
682
|
+
justifyContent: 'space-between',
|
|
661
683
|
alignItems: 'center',
|
|
662
|
-
|
|
684
|
+
paddingHorizontal: 40,
|
|
663
685
|
zIndex: 10,
|
|
664
686
|
},
|
|
687
|
+
leftButtonsContainer: {
|
|
688
|
+
flexDirection: 'row',
|
|
689
|
+
gap: 12,
|
|
690
|
+
alignItems: 'center',
|
|
691
|
+
flex: 1,
|
|
692
|
+
},
|
|
693
|
+
rightButtonsPlaceholder: {
|
|
694
|
+
flex: 1,
|
|
695
|
+
},
|
|
665
696
|
closeButton: {
|
|
666
697
|
width: 40,
|
|
667
698
|
height: 40,
|
|
@@ -688,9 +719,9 @@ const styles = StyleSheet.create({
|
|
|
688
719
|
textAlign: 'center',
|
|
689
720
|
},
|
|
690
721
|
galleryButton: {
|
|
691
|
-
width:
|
|
692
|
-
height:
|
|
693
|
-
borderRadius:
|
|
722
|
+
width: 56,
|
|
723
|
+
height: 56,
|
|
724
|
+
borderRadius: 28,
|
|
694
725
|
borderWidth: 3,
|
|
695
726
|
borderColor: '#fff',
|
|
696
727
|
justifyContent: 'center',
|
|
@@ -698,7 +729,20 @@ const styles = StyleSheet.create({
|
|
|
698
729
|
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
699
730
|
},
|
|
700
731
|
galleryButtonText: {
|
|
701
|
-
fontSize:
|
|
732
|
+
fontSize: 24,
|
|
733
|
+
},
|
|
734
|
+
flashButton: {
|
|
735
|
+
width: 56,
|
|
736
|
+
height: 56,
|
|
737
|
+
borderRadius: 28,
|
|
738
|
+
borderWidth: 3,
|
|
739
|
+
borderColor: '#fff',
|
|
740
|
+
justifyContent: 'center',
|
|
741
|
+
alignItems: 'center',
|
|
742
|
+
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
743
|
+
},
|
|
744
|
+
flashButtonText: {
|
|
745
|
+
fontSize: 24,
|
|
702
746
|
},
|
|
703
747
|
shutterButton: {
|
|
704
748
|
width: 80,
|
|
@@ -28,13 +28,28 @@
|
|
|
28
28
|
[self setupCameraView];
|
|
29
29
|
[self start];
|
|
30
30
|
_hasSetupCamera = YES;
|
|
31
|
+
} else if (_hasSetupCamera && self.window && !CGRectIsEmpty(self.bounds)) {
|
|
32
|
+
// Check if camera session is running, restart if needed
|
|
33
|
+
if (self.captureSession && !self.captureSession.isRunning) {
|
|
34
|
+
NSLog(@"[DocumentScanner] Camera session not running, restarting...");
|
|
35
|
+
[self start];
|
|
36
|
+
}
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
- (void)didMoveToWindow {
|
|
35
41
|
[super didMoveToWindow];
|
|
36
|
-
if (
|
|
42
|
+
if (self.window && _hasSetupCamera) {
|
|
43
|
+
// Restart camera when view is added back to window
|
|
44
|
+
if (self.captureSession && !self.captureSession.isRunning) {
|
|
45
|
+
NSLog(@"[DocumentScanner] View added to window, restarting camera...");
|
|
46
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
47
|
+
[self start];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
} else if (!self.window && _hasSetupCamera) {
|
|
37
51
|
// Stop camera when view is removed from window
|
|
52
|
+
NSLog(@"[DocumentScanner] View removed from window, stopping camera");
|
|
38
53
|
[self stop];
|
|
39
54
|
}
|
|
40
55
|
}
|