@cleanuidev/react-native-scanner 1.0.0-beta.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.
- package/LICENSE +20 -0
- package/README.md +609 -0
- package/Scanner.podspec +20 -0
- package/android/build.gradle +90 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/scanner/CameraInfoModule.kt +253 -0
- package/android/src/main/java/com/scanner/ScannerPackage.kt +21 -0
- package/android/src/main/java/com/scanner/ScannerView.kt +783 -0
- package/android/src/main/java/com/scanner/ScannerViewManager.kt +181 -0
- package/android/src/main/java/com/scanner/utils/BarcodeFrameManager.kt +170 -0
- package/android/src/main/java/com/scanner/views/BarcodeFrameOverlayView.kt +43 -0
- package/android/src/main/java/com/scanner/views/FocusAreaView.kt +124 -0
- package/ios/BarcodeDetectionManager.swift +229 -0
- package/ios/BarcodeFrameManager.swift +175 -0
- package/ios/BarcodeFrameOverlayView.swift +102 -0
- package/ios/CameraManager.swift +396 -0
- package/ios/CoordinateTransformer.swift +140 -0
- package/ios/FocusAreaOverlayView.swift +161 -0
- package/ios/Models.swift +341 -0
- package/ios/Protocols.swift +194 -0
- package/ios/ScannerView.h +14 -0
- package/ios/ScannerView.mm +358 -0
- package/ios/ScannerViewImpl.swift +580 -0
- package/ios/react-native-scanner-Bridging-Header.h +26 -0
- package/lib/module/CameraInfoModule.js +8 -0
- package/lib/module/CameraInfoModule.js.map +1 -0
- package/lib/module/ScannerViewNativeComponent.ts +121 -0
- package/lib/module/hooks/useCameraInfo.js +106 -0
- package/lib/module/hooks/useCameraInfo.js.map +1 -0
- package/lib/module/index.js +13 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +47 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/CameraInfoModule.d.ts +8 -0
- package/lib/typescript/src/CameraInfoModule.d.ts.map +1 -0
- package/lib/typescript/src/ScannerViewNativeComponent.d.ts +91 -0
- package/lib/typescript/src/ScannerViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useCameraInfo.d.ts +25 -0
- package/lib/typescript/src/hooks/useCameraInfo.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +8 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +145 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +178 -0
- package/src/CameraInfoModule.ts +11 -0
- package/src/ScannerViewNativeComponent.ts +121 -0
- package/src/hooks/useCameraInfo.ts +190 -0
- package/src/index.tsx +30 -0
- package/src/types.ts +177 -0
package/package.json
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cleanuidev/react-native-scanner",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"description": "scanner",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"*.podspec",
|
|
22
|
+
"react-native.config.js",
|
|
23
|
+
"!ios/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/gradle",
|
|
26
|
+
"!android/gradlew",
|
|
27
|
+
"!android/gradlew.bat",
|
|
28
|
+
"!android/local.properties",
|
|
29
|
+
"!**/__tests__",
|
|
30
|
+
"!**/__fixtures__",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/.*",
|
|
33
|
+
"!node_modules",
|
|
34
|
+
"!**/node_modules"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"example": "yarn workspace react-native-scanner-example",
|
|
38
|
+
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
|
39
|
+
"prepare": "bob build",
|
|
40
|
+
"typecheck": "tsc",
|
|
41
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
42
|
+
"test": "jest",
|
|
43
|
+
"release": "release-it --only-version"
|
|
44
|
+
},
|
|
45
|
+
"keywords": [
|
|
46
|
+
"react-native",
|
|
47
|
+
"ios",
|
|
48
|
+
"android"
|
|
49
|
+
],
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/cleanui-dev/react-native-scanner.git"
|
|
53
|
+
},
|
|
54
|
+
"author": "Rahul Gupta <rahulgwebdev@gmail.com> (https://github.com/rahulgwebdev)",
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/cleanui-dev/react-native-scanner/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/cleanui-dev/react-native-scanner#readme",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"registry": "https://registry.npmjs.org/",
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
66
|
+
"@eslint/compat": "^1.3.2",
|
|
67
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
68
|
+
"@eslint/js": "^9.35.0",
|
|
69
|
+
"@react-native/babel-preset": "0.83.0",
|
|
70
|
+
"@react-native/eslint-config": "0.83.0",
|
|
71
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
72
|
+
"@types/jest": "^29.5.14",
|
|
73
|
+
"@types/react": "^19.2.0",
|
|
74
|
+
"commitlint": "^19.8.1",
|
|
75
|
+
"del-cli": "^6.0.0",
|
|
76
|
+
"eslint": "^9.35.0",
|
|
77
|
+
"eslint-config-prettier": "^10.1.8",
|
|
78
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
79
|
+
"jest": "^29.7.0",
|
|
80
|
+
"lefthook": "^2.0.3",
|
|
81
|
+
"prettier": "^2.8.8",
|
|
82
|
+
"react": "19.2.0",
|
|
83
|
+
"react-native": "0.83.0",
|
|
84
|
+
"react-native-builder-bob": "^0.40.17",
|
|
85
|
+
"release-it": "^19.0.4",
|
|
86
|
+
"turbo": "^2.5.6",
|
|
87
|
+
"typescript": "^5.9.2"
|
|
88
|
+
},
|
|
89
|
+
"peerDependencies": {
|
|
90
|
+
"react": "*",
|
|
91
|
+
"react-native": "*"
|
|
92
|
+
},
|
|
93
|
+
"workspaces": [
|
|
94
|
+
"example"
|
|
95
|
+
],
|
|
96
|
+
"packageManager": "yarn@4.11.0",
|
|
97
|
+
"react-native-builder-bob": {
|
|
98
|
+
"source": "src",
|
|
99
|
+
"output": "lib",
|
|
100
|
+
"targets": [
|
|
101
|
+
[
|
|
102
|
+
"module",
|
|
103
|
+
{
|
|
104
|
+
"esm": true
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"typescript",
|
|
109
|
+
{
|
|
110
|
+
"project": "tsconfig.build.json"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
"codegenConfig": {
|
|
116
|
+
"name": "ScannerViewSpec",
|
|
117
|
+
"type": "all",
|
|
118
|
+
"jsSrcsDir": "src",
|
|
119
|
+
"android": {
|
|
120
|
+
"javaPackageName": "com.scanner"
|
|
121
|
+
},
|
|
122
|
+
"ios": {
|
|
123
|
+
"componentProvider": {
|
|
124
|
+
"ScannerView": "ScannerView"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"prettier": {
|
|
129
|
+
"quoteProps": "consistent",
|
|
130
|
+
"singleQuote": true,
|
|
131
|
+
"tabWidth": 2,
|
|
132
|
+
"trailingComma": "es5",
|
|
133
|
+
"useTabs": false
|
|
134
|
+
},
|
|
135
|
+
"jest": {
|
|
136
|
+
"preset": "react-native",
|
|
137
|
+
"modulePathIgnorePatterns": [
|
|
138
|
+
"<rootDir>/example/node_modules",
|
|
139
|
+
"<rootDir>/lib/"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"commitlint": {
|
|
143
|
+
"extends": [
|
|
144
|
+
"@commitlint/config-conventional"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"release-it": {
|
|
148
|
+
"git": {
|
|
149
|
+
"commitMessage": "chore: release ${version}",
|
|
150
|
+
"tagName": "v${version}"
|
|
151
|
+
},
|
|
152
|
+
"npm": {
|
|
153
|
+
"publish": true,
|
|
154
|
+
"tag": "beta"
|
|
155
|
+
},
|
|
156
|
+
"github": {
|
|
157
|
+
"release": true
|
|
158
|
+
},
|
|
159
|
+
"plugins": {
|
|
160
|
+
"@release-it/conventional-changelog": {
|
|
161
|
+
"preset": {
|
|
162
|
+
"name": "angular"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"create-react-native-library": {
|
|
168
|
+
"type": "fabric-view",
|
|
169
|
+
"languages": "kotlin-objc",
|
|
170
|
+
"tools": [
|
|
171
|
+
"eslint",
|
|
172
|
+
"jest",
|
|
173
|
+
"lefthook",
|
|
174
|
+
"release-it"
|
|
175
|
+
],
|
|
176
|
+
"version": "0.56.0"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
import type { DeviceCameraInfo, CurrentCameraInfo } from './types';
|
|
3
|
+
|
|
4
|
+
const { CameraInfoModule } = NativeModules;
|
|
5
|
+
|
|
6
|
+
export interface CameraInfoModuleInterface {
|
|
7
|
+
getCameraInfo(): Promise<DeviceCameraInfo>;
|
|
8
|
+
getCurrentCameraInfo(): Promise<CurrentCameraInfo>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default CameraInfoModule as CameraInfoModuleInterface;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codegenNativeComponent,
|
|
3
|
+
type ViewProps,
|
|
4
|
+
type NativeSyntheticEvent,
|
|
5
|
+
} from 'react-native';
|
|
6
|
+
import type {
|
|
7
|
+
DirectEventHandler,
|
|
8
|
+
Double,
|
|
9
|
+
} from 'react-native/Libraries/Types/CodegenTypesNamespace';
|
|
10
|
+
|
|
11
|
+
// Define codegen types locally (no longer exported from react-native in 0.83)
|
|
12
|
+
|
|
13
|
+
// Event payload types for better TypeScript inference
|
|
14
|
+
export interface BarcodeScannedEventPayload {
|
|
15
|
+
barcodes: {
|
|
16
|
+
data: string;
|
|
17
|
+
format: string;
|
|
18
|
+
timestamp: Double;
|
|
19
|
+
boundingBox?: {
|
|
20
|
+
left: Double;
|
|
21
|
+
top: Double;
|
|
22
|
+
right: Double;
|
|
23
|
+
bottom: Double;
|
|
24
|
+
};
|
|
25
|
+
area?: Double;
|
|
26
|
+
}[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ScannerErrorEventPayload {
|
|
30
|
+
error: string;
|
|
31
|
+
code: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface OnLoadEventPayload {
|
|
35
|
+
success: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Event types for use in handlers
|
|
40
|
+
export type BarcodeScannedEvent =
|
|
41
|
+
NativeSyntheticEvent<BarcodeScannedEventPayload>;
|
|
42
|
+
export type ScannerErrorEvent = NativeSyntheticEvent<ScannerErrorEventPayload>;
|
|
43
|
+
export type OnLoadEvent = NativeSyntheticEvent<OnLoadEventPayload>;
|
|
44
|
+
|
|
45
|
+
// Nested object types for better codegen compatibility
|
|
46
|
+
export interface FocusAreaSize {
|
|
47
|
+
width: Double;
|
|
48
|
+
height: Double;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface FocusAreaPosition {
|
|
52
|
+
x: Double; // 0-100
|
|
53
|
+
y: Double; // 0-100
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface FocusAreaConfig {
|
|
57
|
+
enabled?: boolean;
|
|
58
|
+
showOverlay?: boolean;
|
|
59
|
+
borderColor?: string;
|
|
60
|
+
tintColor?: string;
|
|
61
|
+
// NOTE: Codegen does not support mixed types (number OR object), so we always pass {width,height}.
|
|
62
|
+
size?: FocusAreaSize;
|
|
63
|
+
position?: FocusAreaPosition;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface BarcodeFramesConfig {
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
color?: string;
|
|
69
|
+
onlyInFocusArea?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface BoundingBox {
|
|
73
|
+
left: Double;
|
|
74
|
+
top: Double;
|
|
75
|
+
right: Double;
|
|
76
|
+
bottom: Double;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface BarcodeData {
|
|
80
|
+
data: string;
|
|
81
|
+
format: string;
|
|
82
|
+
timestamp: Double;
|
|
83
|
+
boundingBox?: BoundingBox;
|
|
84
|
+
area?: Double;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface NativeProps extends ViewProps {
|
|
88
|
+
barcodeTypes?: string[];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Focus area configuration (Android: drives overlay + optional filtering).
|
|
92
|
+
* - `showOverlay` controls whether the scanning region is drawn.
|
|
93
|
+
* - `enabled` controls whether scanning is restricted to that region.
|
|
94
|
+
*/
|
|
95
|
+
focusArea?: FocusAreaConfig;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Barcode frames configuration (draw rectangles around detected barcodes).
|
|
99
|
+
*/
|
|
100
|
+
barcodeFrames?: BarcodeFramesConfig;
|
|
101
|
+
|
|
102
|
+
torch?: boolean;
|
|
103
|
+
zoom?: Double;
|
|
104
|
+
pauseScanning?: boolean;
|
|
105
|
+
|
|
106
|
+
barcodeScanStrategy?: string;
|
|
107
|
+
keepScreenOn?: boolean;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Minimum interval (in seconds) between barcode emission events.
|
|
111
|
+
* Prevents rapid duplicate detections. Set to 0 to disable debouncing.
|
|
112
|
+
* @default 0.5
|
|
113
|
+
*/
|
|
114
|
+
barcodeEmissionInterval?: Double;
|
|
115
|
+
|
|
116
|
+
onBarcodeScanned?: DirectEventHandler<BarcodeScannedEventPayload>;
|
|
117
|
+
onScannerError?: DirectEventHandler<ScannerErrorEventPayload>;
|
|
118
|
+
onLoad?: DirectEventHandler<OnLoadEventPayload>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export default codegenNativeComponent<NativeProps>('ScannerView');
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
|
+
import CameraInfoModule from '../CameraInfoModule';
|
|
3
|
+
import type {
|
|
4
|
+
DeviceCameraInfo,
|
|
5
|
+
CurrentCameraInfo,
|
|
6
|
+
CameraInfo,
|
|
7
|
+
CameraFacing,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
export interface UseCameraInfoReturn {
|
|
11
|
+
// Device information
|
|
12
|
+
deviceInfo: DeviceCameraInfo | null;
|
|
13
|
+
currentCameraInfo: CurrentCameraInfo | null;
|
|
14
|
+
|
|
15
|
+
// Camera lists
|
|
16
|
+
allCameras: CameraInfo[];
|
|
17
|
+
backCameras: CameraInfo[];
|
|
18
|
+
frontCameras: CameraInfo[];
|
|
19
|
+
macroCameras: CameraInfo[];
|
|
20
|
+
|
|
21
|
+
// Quick access properties
|
|
22
|
+
hasMultipleCameras: boolean;
|
|
23
|
+
hasBackCamera: boolean;
|
|
24
|
+
hasFrontCamera: boolean;
|
|
25
|
+
hasMacroCamera: boolean;
|
|
26
|
+
hasTorch: boolean;
|
|
27
|
+
|
|
28
|
+
// Default cameras
|
|
29
|
+
defaultBackCamera: CameraInfo | null;
|
|
30
|
+
defaultFrontCamera: CameraInfo | null;
|
|
31
|
+
|
|
32
|
+
// Zoom information
|
|
33
|
+
maxZoom: number;
|
|
34
|
+
minZoom: number;
|
|
35
|
+
|
|
36
|
+
// Loading and error states
|
|
37
|
+
isLoading: boolean;
|
|
38
|
+
error: string | null;
|
|
39
|
+
|
|
40
|
+
// Actions
|
|
41
|
+
refreshInfo: () => Promise<void>;
|
|
42
|
+
getCameraById: (id: string) => CameraInfo | null;
|
|
43
|
+
getCamerasByFacing: (facing: CameraFacing) => CameraInfo[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useCameraInfo(): UseCameraInfoReturn {
|
|
47
|
+
const [deviceInfo, setDeviceInfo] = useState<DeviceCameraInfo | null>(null);
|
|
48
|
+
const [currentCameraInfo, setCurrentCameraInfo] =
|
|
49
|
+
useState<CurrentCameraInfo | null>(null);
|
|
50
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
51
|
+
const [error, setError] = useState<string | null>(null);
|
|
52
|
+
|
|
53
|
+
const loadCameraInfo = useCallback(async () => {
|
|
54
|
+
try {
|
|
55
|
+
setIsLoading(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
|
|
58
|
+
const info = await CameraInfoModule.getCameraInfo();
|
|
59
|
+
setDeviceInfo(info);
|
|
60
|
+
|
|
61
|
+
// Try to get current camera info if available
|
|
62
|
+
try {
|
|
63
|
+
const currentInfo = await CameraInfoModule.getCurrentCameraInfo();
|
|
64
|
+
setCurrentCameraInfo(currentInfo);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
// Current camera info might not be available if no camera is bound
|
|
67
|
+
console.log('Current camera info not available:', e);
|
|
68
|
+
}
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
let errorMessage = 'Failed to load camera information';
|
|
71
|
+
|
|
72
|
+
// Handle specific error types
|
|
73
|
+
if (e?.code === 'PERMISSION_ERROR') {
|
|
74
|
+
errorMessage =
|
|
75
|
+
'Camera permission not granted. Please grant camera permission in app settings.';
|
|
76
|
+
} else if (e?.code === 'NO_CAMERAS_ERROR') {
|
|
77
|
+
errorMessage = 'No cameras found on this device.';
|
|
78
|
+
} else if (e?.code === 'CAMERA_ACCESS_ERROR') {
|
|
79
|
+
errorMessage = 'Unable to access camera information. Please try again.';
|
|
80
|
+
} else if (e?.message) {
|
|
81
|
+
errorMessage = e.message;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
setError(errorMessage);
|
|
85
|
+
console.error('Error loading camera info:', e);
|
|
86
|
+
} finally {
|
|
87
|
+
setIsLoading(false);
|
|
88
|
+
}
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const refreshInfo = useCallback(async () => {
|
|
92
|
+
await loadCameraInfo();
|
|
93
|
+
}, [loadCameraInfo]);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
loadCameraInfo();
|
|
97
|
+
}, [loadCameraInfo]);
|
|
98
|
+
// Computed properties
|
|
99
|
+
const allCameras = useMemo(
|
|
100
|
+
() => deviceInfo?.cameras || [],
|
|
101
|
+
[deviceInfo?.cameras]
|
|
102
|
+
);
|
|
103
|
+
const backCameras = useMemo(
|
|
104
|
+
() => allCameras.filter((camera) => camera.facing === 'back'),
|
|
105
|
+
[allCameras]
|
|
106
|
+
);
|
|
107
|
+
const frontCameras = useMemo(
|
|
108
|
+
() => allCameras.filter((camera) => camera.facing === 'front'),
|
|
109
|
+
[allCameras]
|
|
110
|
+
);
|
|
111
|
+
const macroCameras = useMemo(
|
|
112
|
+
() => allCameras.filter((camera) => camera.isMacroCamera),
|
|
113
|
+
[allCameras]
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const hasMultipleCameras = allCameras.length > 1;
|
|
117
|
+
const hasBackCamera = backCameras.length > 0;
|
|
118
|
+
const hasFrontCamera = frontCameras.length > 0;
|
|
119
|
+
const hasMacroCamera = macroCameras.length > 0;
|
|
120
|
+
const hasTorch = allCameras.some((camera) => camera.hasFlash);
|
|
121
|
+
|
|
122
|
+
const defaultBackCamera = deviceInfo?.defaultBackCamera
|
|
123
|
+
? allCameras.find((camera) => camera.id === deviceInfo.defaultBackCamera) ||
|
|
124
|
+
null
|
|
125
|
+
: backCameras[0] || null;
|
|
126
|
+
|
|
127
|
+
const defaultFrontCamera = deviceInfo?.defaultFrontCamera
|
|
128
|
+
? allCameras.find(
|
|
129
|
+
(camera) => camera.id === deviceInfo.defaultFrontCamera
|
|
130
|
+
) || null
|
|
131
|
+
: frontCameras[0] || null;
|
|
132
|
+
|
|
133
|
+
// Zoom range from all cameras
|
|
134
|
+
const allZoomRanges = allCameras.map((camera) => ({
|
|
135
|
+
min: camera.zoomMin,
|
|
136
|
+
max: camera.zoomMax,
|
|
137
|
+
}));
|
|
138
|
+
const maxZoom = Math.max(...allZoomRanges.map((range) => range.max), 1);
|
|
139
|
+
const minZoom = Math.min(...allZoomRanges.map((range) => range.min), 1);
|
|
140
|
+
|
|
141
|
+
const getCameraById = useCallback(
|
|
142
|
+
(id: string): CameraInfo | null => {
|
|
143
|
+
return allCameras.find((camera) => camera.id === id) || null;
|
|
144
|
+
},
|
|
145
|
+
[allCameras]
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const getCamerasByFacing = useCallback(
|
|
149
|
+
(facing: CameraFacing): CameraInfo[] => {
|
|
150
|
+
return allCameras.filter((camera) => camera.facing === facing);
|
|
151
|
+
},
|
|
152
|
+
[allCameras]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
// Device information
|
|
157
|
+
deviceInfo,
|
|
158
|
+
currentCameraInfo,
|
|
159
|
+
|
|
160
|
+
// Camera lists
|
|
161
|
+
allCameras,
|
|
162
|
+
backCameras,
|
|
163
|
+
frontCameras,
|
|
164
|
+
macroCameras,
|
|
165
|
+
|
|
166
|
+
// Quick access properties
|
|
167
|
+
hasMultipleCameras,
|
|
168
|
+
hasBackCamera,
|
|
169
|
+
hasFrontCamera,
|
|
170
|
+
hasMacroCamera,
|
|
171
|
+
hasTorch,
|
|
172
|
+
|
|
173
|
+
// Default cameras
|
|
174
|
+
defaultBackCamera,
|
|
175
|
+
defaultFrontCamera,
|
|
176
|
+
|
|
177
|
+
// Zoom information
|
|
178
|
+
maxZoom,
|
|
179
|
+
minZoom,
|
|
180
|
+
|
|
181
|
+
// Loading and error states
|
|
182
|
+
isLoading,
|
|
183
|
+
error,
|
|
184
|
+
|
|
185
|
+
// Actions
|
|
186
|
+
refreshInfo,
|
|
187
|
+
getCameraById,
|
|
188
|
+
getCamerasByFacing,
|
|
189
|
+
};
|
|
190
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { BarcodeFormat, BarcodeScanStrategy } from './types';
|
|
2
|
+
export type {
|
|
3
|
+
FrameSize,
|
|
4
|
+
FocusAreaConfig,
|
|
5
|
+
BarcodeFramesConfig,
|
|
6
|
+
BarcodeScannedEventPayload,
|
|
7
|
+
ScannerErrorEventPayload,
|
|
8
|
+
OnLoadEventPayload,
|
|
9
|
+
DeviceCameraInfo,
|
|
10
|
+
CurrentCameraInfo,
|
|
11
|
+
CameraInfo,
|
|
12
|
+
CameraFacing,
|
|
13
|
+
} from './types';
|
|
14
|
+
|
|
15
|
+
// Export the camera info hook
|
|
16
|
+
export { useCameraInfo } from './hooks/useCameraInfo';
|
|
17
|
+
export type { UseCameraInfoReturn } from './hooks/useCameraInfo';
|
|
18
|
+
|
|
19
|
+
// Re-export the native component and event types
|
|
20
|
+
|
|
21
|
+
export { default as ScannerView } from './ScannerViewNativeComponent';
|
|
22
|
+
export * from './ScannerViewNativeComponent';
|
|
23
|
+
|
|
24
|
+
// Export event types for better TypeScript inference
|
|
25
|
+
// These are the wrapped NativeSyntheticEvent types that users should use in their handlers
|
|
26
|
+
export type {
|
|
27
|
+
BarcodeScannedEvent,
|
|
28
|
+
ScannerErrorEvent,
|
|
29
|
+
OnLoadEvent,
|
|
30
|
+
} from './ScannerViewNativeComponent';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
// Barcode format enum
|
|
4
|
+
export enum BarcodeFormat {
|
|
5
|
+
QR_CODE = 'QR_CODE',
|
|
6
|
+
CODE_128 = 'CODE_128',
|
|
7
|
+
CODE_39 = 'CODE_39',
|
|
8
|
+
EAN_13 = 'EAN_13',
|
|
9
|
+
EAN_8 = 'EAN_8',
|
|
10
|
+
UPC_A = 'UPC_A',
|
|
11
|
+
UPC_E = 'UPC_E',
|
|
12
|
+
DATA_MATRIX = 'DATA_MATRIX',
|
|
13
|
+
PDF_417 = 'PDF_417',
|
|
14
|
+
AZTEC = 'AZTEC',
|
|
15
|
+
ITF = 'ITF',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Barcode scan strategy enum
|
|
19
|
+
export enum BarcodeScanStrategy {
|
|
20
|
+
ONE = 'ONE',
|
|
21
|
+
ALL = 'ALL',
|
|
22
|
+
BIGGEST = 'BIGGEST',
|
|
23
|
+
SORT_BY_BIGGEST = 'SORT_BY_BIGGEST',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Frame size configuration - can be a number (square) or object (rectangle)
|
|
27
|
+
export type FrameSize = number | { width: number; height: number };
|
|
28
|
+
|
|
29
|
+
// Focus area configuration
|
|
30
|
+
export type FocusAreaConfig = {
|
|
31
|
+
/**
|
|
32
|
+
* Whether to restrict scanning to focus area only
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Size of the focus area
|
|
38
|
+
* @default 300
|
|
39
|
+
*/
|
|
40
|
+
size?: FrameSize;
|
|
41
|
+
/**
|
|
42
|
+
* Color of the focus area border
|
|
43
|
+
* @default transparent (no border) when not provided
|
|
44
|
+
*/
|
|
45
|
+
borderColor?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Color of the semi-transparent overlay (tint) around the focus area
|
|
48
|
+
* @default '#000000' with 50% opacity when not provided
|
|
49
|
+
*/
|
|
50
|
+
tintColor?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Whether to draw the focus area overlay
|
|
53
|
+
* @default false
|
|
54
|
+
*/
|
|
55
|
+
showOverlay?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Position of the focus area (percentage from 0-100)
|
|
58
|
+
* @default center (50, 50) when not provided
|
|
59
|
+
*/
|
|
60
|
+
position?: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Barcode frame configuration
|
|
67
|
+
export type BarcodeFramesConfig = {
|
|
68
|
+
enabled?: boolean; // Whether to draw frames around detected barcodes
|
|
69
|
+
color?: string; // Color of barcode frames
|
|
70
|
+
onlyInFocusArea?: boolean; // Only show frames for barcodes in focus area
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Camera information types
|
|
74
|
+
export type CameraFacing = 'front' | 'back' | 'unknown';
|
|
75
|
+
|
|
76
|
+
export type ZoomRange = {
|
|
77
|
+
min: number;
|
|
78
|
+
max: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type CameraInfo = {
|
|
82
|
+
id: string;
|
|
83
|
+
facing: CameraFacing;
|
|
84
|
+
sensorOrientation: number;
|
|
85
|
+
minFocusDistance: number;
|
|
86
|
+
hasFlash: boolean;
|
|
87
|
+
isMacroCamera: boolean;
|
|
88
|
+
zoomMin: number;
|
|
89
|
+
zoomMax: number;
|
|
90
|
+
focalLengths: string[];
|
|
91
|
+
aeModes: string[];
|
|
92
|
+
afModes: string[];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type DeviceCameraInfo = {
|
|
96
|
+
cameras: CameraInfo[];
|
|
97
|
+
defaultBackCamera: string;
|
|
98
|
+
defaultFrontCamera: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type CurrentCameraInfo = {
|
|
102
|
+
status: string;
|
|
103
|
+
message: string;
|
|
104
|
+
// Additional properties when camera is bound
|
|
105
|
+
currentZoom?: number;
|
|
106
|
+
isTorchEnabled?: boolean;
|
|
107
|
+
focusMode?: string;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Barcode scanned event payload
|
|
111
|
+
export type BarcodeScannedEventPayload = {
|
|
112
|
+
data: string;
|
|
113
|
+
format: BarcodeFormat;
|
|
114
|
+
timestamp: number;
|
|
115
|
+
boundingBox?: {
|
|
116
|
+
left: number;
|
|
117
|
+
top: number;
|
|
118
|
+
right: number;
|
|
119
|
+
bottom: number;
|
|
120
|
+
};
|
|
121
|
+
area?: number;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Scanner error event payload
|
|
125
|
+
export type ScannerErrorEventPayload = {
|
|
126
|
+
error: string;
|
|
127
|
+
code: string;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// On load event payload (for camera initialization)
|
|
131
|
+
export type OnLoadEventPayload = {
|
|
132
|
+
success: boolean;
|
|
133
|
+
error?: string;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Event types
|
|
137
|
+
export type CameraNativeModuleEvents = {
|
|
138
|
+
onBarcodeScanned: (params: BarcodeScannedEventPayload) => void;
|
|
139
|
+
onScannerError: (params: ScannerErrorEventPayload) => void;
|
|
140
|
+
onLoad: (params: OnLoadEventPayload) => void;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Props interface for the scanner view
|
|
144
|
+
export type CameraNativeModuleViewProps = {
|
|
145
|
+
// Barcode configuration
|
|
146
|
+
barcodeTypes?: BarcodeFormat[];
|
|
147
|
+
|
|
148
|
+
// Focus area configuration
|
|
149
|
+
focusArea?: FocusAreaConfig;
|
|
150
|
+
|
|
151
|
+
// Barcode frame configuration
|
|
152
|
+
barcodeFrames?: BarcodeFramesConfig;
|
|
153
|
+
|
|
154
|
+
// Camera configuration
|
|
155
|
+
torch?: boolean;
|
|
156
|
+
zoom?: number;
|
|
157
|
+
pauseScanning?: boolean;
|
|
158
|
+
keepScreenOn?: boolean;
|
|
159
|
+
|
|
160
|
+
// Event handlers
|
|
161
|
+
onBarcodeScanned?: (event: {
|
|
162
|
+
nativeEvent: BarcodeScannedEventPayload;
|
|
163
|
+
}) => void;
|
|
164
|
+
onScannerError?: (event: { nativeEvent: ScannerErrorEventPayload }) => void;
|
|
165
|
+
onLoad?: (event: { nativeEvent: OnLoadEventPayload }) => void;
|
|
166
|
+
|
|
167
|
+
// Styling
|
|
168
|
+
style?: StyleProp<ViewStyle>;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Method invokers for controlling the scanner
|
|
172
|
+
export type CameraNativeModuleMethods = {
|
|
173
|
+
toggleTorch: () => Promise<boolean>;
|
|
174
|
+
startScanning: () => Promise<void>;
|
|
175
|
+
stopScanning: () => Promise<void>;
|
|
176
|
+
isTorchAvailable: () => Promise<boolean>;
|
|
177
|
+
};
|