@mleonard9/vin-scanner 1.3.0 → 1.4.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/README.md +129 -12
- package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +26 -17
- package/lib/commonjs/ManualVinInput.js +147 -0
- package/lib/commonjs/ManualVinInput.js.map +1 -0
- package/lib/commonjs/PendingVinBanner.js +120 -0
- package/lib/commonjs/PendingVinBanner.js.map +1 -0
- package/lib/commonjs/TextVinPrompt.js +132 -0
- package/lib/commonjs/TextVinPrompt.js.map +1 -0
- package/lib/commonjs/haptics.js +36 -0
- package/lib/commonjs/haptics.js.map +1 -0
- package/lib/commonjs/index.js +184 -13
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/useVinScanner.js +164 -6
- package/lib/commonjs/useVinScanner.js.map +1 -1
- package/lib/commonjs/vinUtils.js +23 -9
- package/lib/commonjs/vinUtils.js.map +1 -1
- package/lib/module/ManualVinInput.js +139 -0
- package/lib/module/ManualVinInput.js.map +1 -0
- package/lib/module/PendingVinBanner.js +112 -0
- package/lib/module/PendingVinBanner.js.map +1 -0
- package/lib/module/TextVinPrompt.js +124 -0
- package/lib/module/TextVinPrompt.js.map +1 -0
- package/lib/module/haptics.js +27 -0
- package/lib/module/haptics.js.map +1 -0
- package/lib/module/index.js +171 -12
- package/lib/module/index.js.map +1 -1
- package/lib/module/useVinScanner.js +165 -7
- package/lib/module/useVinScanner.js.map +1 -1
- package/lib/module/vinUtils.js +23 -9
- package/lib/module/vinUtils.js.map +1 -1
- package/lib/typescript/src/ManualVinInput.d.ts +11 -0
- package/lib/typescript/src/ManualVinInput.d.ts.map +1 -0
- package/lib/typescript/src/PendingVinBanner.d.ts +17 -0
- package/lib/typescript/src/PendingVinBanner.d.ts.map +1 -0
- package/lib/typescript/src/TextVinPrompt.d.ts +20 -0
- package/lib/typescript/src/TextVinPrompt.d.ts.map +1 -0
- package/lib/typescript/src/haptics.d.ts +4 -0
- package/lib/typescript/src/haptics.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +46 -7
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useVinScanner.d.ts +3 -1
- package/lib/typescript/src/useVinScanner.d.ts.map +1 -1
- package/lib/typescript/src/vinUtils.d.ts +7 -2
- package/lib/typescript/src/vinUtils.d.ts.map +1 -1
- package/package.json +5 -1
- package/src/ManualVinInput.tsx +145 -0
- package/src/PendingVinBanner.tsx +128 -0
- package/src/TextVinPrompt.tsx +139 -0
- package/src/haptics.ts +32 -0
- package/src/index.tsx +195 -22
- package/src/types.ts +46 -7
- package/src/useVinScanner.ts +188 -8
- package/src/vinUtils.ts +34 -17
- package/lib/commonjs/VinScannerOverlay.js +0 -60
- package/lib/commonjs/VinScannerOverlay.js.map +0 -1
- package/lib/module/VinScannerOverlay.js +0 -53
- package/lib/module/VinScannerOverlay.js.map +0 -1
- package/lib/typescript/src/VinScannerOverlay.d.ts +0 -14
- package/lib/typescript/src/VinScannerOverlay.d.ts.map +0 -1
- package/src/VinScannerOverlay.tsx +0 -55
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ High-performance VIN detection for React Native powered by Google ML Kit barcode
|
|
|
12
12
|
|
|
13
13
|
- `react-native-vision-camera` >= 3.9.0
|
|
14
14
|
- `react-native-worklets-core` >= 0.4.0
|
|
15
|
+
- `react-native-gesture-handler` >= 2.0.0 (for tap-to-focus)
|
|
16
|
+
- `react-native-reanimated` >= 3.0.0 (for tap-to-focus)
|
|
15
17
|
- iOS 13+ / Android 21+
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
@@ -73,11 +75,45 @@ export function VinScannerExample(): JSX.Element {
|
|
|
73
75
|
|
|
74
76
|
Every frame, the camera runs ML Kit barcode + text recognition, extracts 17-character VIN candidates, validates them (checksum included), and routes a payload to `callback`.
|
|
75
77
|
|
|
78
|
+
## Camera Gestures
|
|
79
|
+
|
|
80
|
+
The VIN Scanner camera includes built-in support for intuitive camera controls:
|
|
81
|
+
|
|
82
|
+
### Pinch to Zoom
|
|
83
|
+
|
|
84
|
+
Pinch-to-zoom is **enabled by default**. Simply pinch on the camera view to zoom in and out. The zoom gesture is natively implemented by `react-native-vision-camera` for optimal performance.
|
|
85
|
+
|
|
86
|
+
### Tap to Focus
|
|
87
|
+
|
|
88
|
+
Tap anywhere on the camera view to focus at that point. This feature requires `react-native-gesture-handler` and `react-native-reanimated`:
|
|
89
|
+
|
|
90
|
+
**Installation:**
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
yarn add react-native-gesture-handler react-native-reanimated
|
|
94
|
+
# or
|
|
95
|
+
npm install react-native-gesture-handler react-native-reanimated
|
|
96
|
+
|
|
97
|
+
# iOS
|
|
98
|
+
cd ios && pod install
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Note:** These dependencies are likely already installed if you're using React Navigation or other common React Native libraries.
|
|
102
|
+
|
|
103
|
+
The tap-to-focus functionality works automatically once these dependencies are installed. Simply tap on the camera view where you want to focus, and the camera will adjust both auto-focus (AF) and auto-exposure (AE) for that point.
|
|
104
|
+
|
|
105
|
+
**How it works:**
|
|
106
|
+
- Tap on a VIN to focus precisely on that area
|
|
107
|
+
- The camera adjusts focus and exposure automatically
|
|
108
|
+
- Works seamlessly with the pinch-to-zoom gesture
|
|
109
|
+
- No additional configuration required
|
|
110
|
+
|
|
111
|
+
|
|
76
112
|
## Advanced Features
|
|
77
113
|
|
|
78
114
|
### AR Overlay with Confidence Scoring
|
|
79
115
|
|
|
80
|
-
The package
|
|
116
|
+
The package previously included an AR overlay component for bounding boxes; it has been removed for now while alignment issues are addressed. Default barcode formats are tuned for VIN labels (`code-39`, `code-128`, `pdf-417`) with an automatic fallback to all formats after sustained misses.
|
|
81
117
|
|
|
82
118
|
**Installation:**
|
|
83
119
|
|
|
@@ -90,7 +126,7 @@ npm install @shopify/react-native-skia
|
|
|
90
126
|
**Usage:**
|
|
91
127
|
|
|
92
128
|
```tsx
|
|
93
|
-
|
|
129
|
+
// Overlay component removed (was VinScannerOverlay)
|
|
94
130
|
|
|
95
131
|
export function VinScannerWithOverlay() {
|
|
96
132
|
const [candidates, setCandidates] = useState<VinCandidate[]>([]);
|
|
@@ -108,10 +144,7 @@ export function VinScannerWithOverlay() {
|
|
|
108
144
|
frameProcessor={frameProcessor}
|
|
109
145
|
style={StyleSheet.absoluteFill}
|
|
110
146
|
/>
|
|
111
|
-
|
|
112
|
-
candidates={candidates}
|
|
113
|
-
colors={{ high: '#00FF00', medium: '#FFFF00', low: '#FF0000' }}
|
|
114
|
-
/>
|
|
147
|
+
{/* Overlay removed; render your own UI if needed */}
|
|
115
148
|
</View>
|
|
116
149
|
);
|
|
117
150
|
}
|
|
@@ -220,16 +253,101 @@ The `candidates` array contains every potential VIN found in the frame. `firstCa
|
|
|
220
253
|
| `options.barcode.formats` | `BarcodeFormat[]` | Restrict ML Kit formats (`'code-39'`, `'code-128'`, `'pdf-417'`, etc.) | `['all']` |
|
|
221
254
|
| `options.text.enabled` | boolean | Enable text recognition | `true` |
|
|
222
255
|
| `options.text.language` | `'latin' \| 'chinese' \| 'devanagari' \| 'japanese' \| 'korean'` | ML Kit language pack | `'latin'` |
|
|
256
|
+
| `options.text.requireConfirmation` | boolean | When true, text VINs are held until you confirm; barcodes still emit immediately | `false` |
|
|
257
|
+
| `options.text.pendingTtlMs` | number | Auto-dismiss pending text VINs after this many ms (when `requireConfirmation` is true) | `5000` |
|
|
223
258
|
| `options.detection.textScanInterval` | number | Run text recognition every Nth frame (1 = every frame) | `3` |
|
|
224
259
|
| `options.detection.maxFrameRate` | number | Max FPS budget for frame processing (drops surplus frames to avoid blocking) | `30` |
|
|
225
260
|
| `options.detection.forceOrientation` | `'portrait' \| 'portrait-upside-down' \| 'landscape-left' \| 'landscape-right'` | Forces ML Kit to interpret every frame using the given orientation (useful when the UI is locked to portrait but the sensor reports landscape) | `null` |
|
|
226
261
|
| `options.detection.scanRegion` | `ScanRegion` | Restrict ML Kit processing to a specific region of the frame (normalized coordinates 0.0-1.0). Significantly improves performance by ignoring irrelevant areas. | `{ x: 0.15, y: 0.15, width: 0.7, height: 0.7 }` |
|
|
227
|
-
| `options.detection.enableFrameQualityCheck` | boolean |
|
|
262
|
+
| `options.detection.enableFrameQualityCheck` | boolean | Deprecated; use `minLuma`/`minSharpness` instead | `true` |
|
|
263
|
+
| `options.detection.minLuma` | number | Minimum mean luma (0–255) required to process a frame; skips too-dark frames | `30` |
|
|
264
|
+
| `options.detection.minSharpness` | number | Minimum sharpness metric required; skips blurry frames | `12` |
|
|
265
|
+
| `options.detection.minConfidence` | number | Minimum candidate confidence required before emitting | `0.6` |
|
|
266
|
+
| `options.detection.barcodeFallbackAfter` | number | Frames without barcode hits before scanning all formats | `45` |
|
|
228
267
|
| `options.duplicateDebounceMs` | number | Time in milliseconds to suppress duplicate VIN callbacks for the same value | `1500` |
|
|
229
|
-
| `options.showOverlay` | boolean |
|
|
230
|
-
| `options.overlayColors` | `OverlayColors` |
|
|
231
|
-
| `options.cameraSettings` | `CameraSettings` | Camera configuration: `{ fps, lowLightBoost, videoStabilizationMode }` | `{ fps:
|
|
268
|
+
| `options.showOverlay` | boolean | Deprecated; overlay component removed | `false` |
|
|
269
|
+
| `options.overlayColors` | `OverlayColors` | Deprecated; overlay component removed | `{ high: '#00FF00', medium: '#FFFF00', low: '#FF0000' }` |
|
|
270
|
+
| `options.cameraSettings` | `CameraSettings` | Camera configuration: `{ fps (clamped 24–30), lowLightBoost, videoStabilizationMode }` | `{ fps: 24, lowLightBoost: true, videoStabilizationMode: 'cinematic' }` |
|
|
232
271
|
| `options.onResult` | `(candidates, event) => void` | Convenience callback when using `useVinScanner`; receives all candidates and the raw event | `undefined` |
|
|
272
|
+
| `options.onTextPending` | `(pending) => void` | Invoked when `text.requireConfirmation` is true and text VINs are detected | `undefined` |
|
|
273
|
+
| `options.haptics` | boolean | Enable built-in haptic cues (requires `react-native-haptic-feedback` installed) | `true` |
|
|
274
|
+
|
|
275
|
+
### Behaviors & defaults
|
|
276
|
+
- Barcode-first: barcodes emit immediately; text VINs can require confirmation.
|
|
277
|
+
- Session dedupe: VINs are not re-emitted within a scan session (in addition to time-based debounce).
|
|
278
|
+
- Quality gate: frames below `minLuma` or `minSharpness` are skipped.
|
|
279
|
+
- Confidence gate: candidates below `minConfidence` are dropped.
|
|
280
|
+
- Barcode formats: defaults to `code-39`, `code-128`, `pdf-417` with automatic fallback to all formats after `barcodeFallbackAfter` empty frames.
|
|
281
|
+
- Camera hints: FPS clamped to 24–30 and `videoStabilizationMode` defaults to `cinematic` to keep headroom and reduce jitter.
|
|
282
|
+
|
|
283
|
+
### Text confirmation UI (barcode = instant, text = tap-to-confirm)
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
import { useState } from 'react';
|
|
287
|
+
import { Camera, useCameraDevice } from 'react-native-vision-camera';
|
|
288
|
+
import { useVinScanner, TextVinPrompt, VinCandidate } from '@mleonard9/vin-scanner';
|
|
289
|
+
|
|
290
|
+
export function ConfirmingScanner() {
|
|
291
|
+
const device = useCameraDevice('back');
|
|
292
|
+
const [pending, setPending] = useState<VinCandidate[]>([]);
|
|
293
|
+
|
|
294
|
+
const { frameProcessor, pendingTextCandidates, confirmTextCandidate } = useVinScanner({
|
|
295
|
+
text: { requireConfirmation: true },
|
|
296
|
+
onTextPending: setPending,
|
|
297
|
+
onResult: (candidates) => {
|
|
298
|
+
// barcode VINs (or confirmed text VINs) arrive here
|
|
299
|
+
console.log('confirmed VINs', candidates.map((c) => c.value));
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<>
|
|
305
|
+
{device && (
|
|
306
|
+
<Camera style={{ flex: 1 }} device={device} frameProcessor={frameProcessor} isActive />
|
|
307
|
+
)}
|
|
308
|
+
<TextVinPrompt
|
|
309
|
+
visible={pendingTextCandidates.length > 0}
|
|
310
|
+
candidates={pendingTextCandidates}
|
|
311
|
+
buttonLabel=\"Book It\"
|
|
312
|
+
buttonColor=\"#0A84FF\"
|
|
313
|
+
onConfirm={(candidate) => confirmTextCandidate(candidate.value)}
|
|
314
|
+
onDismiss={() => setPending([])}
|
|
315
|
+
/>
|
|
316
|
+
</>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Manual VIN keypad with checksum guard
|
|
322
|
+
|
|
323
|
+
```tsx
|
|
324
|
+
import { ManualVinInput } from '@mleonard9/vin-scanner';
|
|
325
|
+
|
|
326
|
+
export function ManualEntry({ onSubmit }: { onSubmit: (vin: string) => void }) {
|
|
327
|
+
return (
|
|
328
|
+
<ManualVinInput
|
|
329
|
+
buttonLabel=\"Book It\"
|
|
330
|
+
buttonColor=\"#0A84FF\"
|
|
331
|
+
onSubmit={onSubmit}
|
|
332
|
+
/>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Pending banner (alternative to modal)
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
import { PendingVinBanner } from '@mleonard9/vin-scanner';
|
|
341
|
+
|
|
342
|
+
<PendingVinBanner
|
|
343
|
+
visible={pendingTextCandidates.length > 0}
|
|
344
|
+
candidates={pendingTextCandidates}
|
|
345
|
+
buttonLabel=\"Book It\"
|
|
346
|
+
buttonColor=\"#0A84FF\"
|
|
347
|
+
onConfirm={(candidate) => confirmTextCandidate(candidate.value)}
|
|
348
|
+
onDismiss={() => setPending([])}
|
|
349
|
+
/>;
|
|
350
|
+
```
|
|
233
351
|
|
|
234
352
|
### Performance
|
|
235
353
|
|
|
@@ -242,7 +360,7 @@ Phase 1 optimizations dramatically improve scanning performance through native R
|
|
|
242
360
|
| ROI + text interval (3 frames) | ~45ms | **75% faster** |
|
|
243
361
|
| ROI + quality check + throttle | ~30ms | **83% faster** |
|
|
244
362
|
|
|
245
|
-
**Default configuration** uses ROI scanning (`scanRegion: { x: 0.15, y: 0.15, width: 0.7, height: 0.7 }`)
|
|
363
|
+
**Default configuration** uses ROI scanning (`scanRegion: { x: 0.15, y: 0.15, width: 0.7, height: 0.7 }`) and a text scan interval of 3. This provides excellent accuracy while maintaining real-time performance on mid-range devices.
|
|
246
364
|
|
|
247
365
|
**Tip:** For challenging lighting or distance scenarios, set `textScanInterval: 1` to scan every frame at the cost of higher CPU usage.
|
|
248
366
|
|
|
@@ -302,4 +420,3 @@ npm publish --access public
|
|
|
302
420
|
```
|
|
303
421
|
|
|
304
422
|
Ensure the authenticated npm user has access to the `@mleonard9` scope.
|
|
305
|
-
|
package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt
CHANGED
|
@@ -48,24 +48,33 @@ class VisionCameraBarcodeScannerModule(
|
|
|
48
48
|
|
|
49
49
|
private fun buildScannerOptions(effective: Map<String, Any>): BarcodeScannerOptions {
|
|
50
50
|
val builder = BarcodeScannerOptions.Builder()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
|
|
52
|
+
var mask = 0
|
|
53
|
+
fun maybeAdd(enabled: Boolean, format: Int) {
|
|
54
|
+
if (enabled) mask = mask or format
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Allow multiple formats at once; fall back to ALL when none specified.
|
|
58
|
+
maybeAdd(effective["all"].toString().toBoolean(), FORMAT_ALL_FORMATS)
|
|
59
|
+
maybeAdd(effective["code-128"].toString().toBoolean(), FORMAT_CODE_128)
|
|
60
|
+
maybeAdd(effective["code-39"].toString().toBoolean(), FORMAT_CODE_39)
|
|
61
|
+
maybeAdd(effective["code-93"].toString().toBoolean(), FORMAT_CODE_93)
|
|
62
|
+
maybeAdd(effective["codabar"].toString().toBoolean(), FORMAT_CODABAR)
|
|
63
|
+
maybeAdd(effective["ean-13"].toString().toBoolean(), FORMAT_EAN_13)
|
|
64
|
+
maybeAdd(effective["ean-8"].toString().toBoolean(), FORMAT_EAN_8)
|
|
65
|
+
maybeAdd(effective["itf"].toString().toBoolean(), FORMAT_ITF)
|
|
66
|
+
maybeAdd(effective["upc-e"].toString().toBoolean(), FORMAT_UPC_E)
|
|
67
|
+
maybeAdd(effective["upc-a"].toString().toBoolean(), FORMAT_UPC_A)
|
|
68
|
+
maybeAdd(effective["qr"].toString().toBoolean(), FORMAT_QR_CODE)
|
|
69
|
+
maybeAdd(effective["pdf-417"].toString().toBoolean(), FORMAT_PDF417)
|
|
70
|
+
maybeAdd(effective["aztec"].toString().toBoolean(), FORMAT_AZTEC)
|
|
71
|
+
maybeAdd(effective["data-matrix"].toString().toBoolean(), FORMAT_DATA_MATRIX)
|
|
72
|
+
|
|
73
|
+
if (mask == 0) {
|
|
74
|
+
mask = FORMAT_ALL_FORMATS
|
|
67
75
|
}
|
|
68
|
-
|
|
76
|
+
|
|
77
|
+
return builder.setBarcodeFormats(mask).build()
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
private fun orientationToDegrees(orientation: String?): Int? {
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ManualVinInput = ManualVinInput;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _vinUtils = require("./vinUtils");
|
|
11
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
+
const VIN_CHARS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
|
|
13
|
+
const DIGITS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
14
|
+
function ManualVinInput({
|
|
15
|
+
initialValue = '',
|
|
16
|
+
onSubmit,
|
|
17
|
+
buttonLabel = 'Use VIN',
|
|
18
|
+
buttonColor = '#0A84FF',
|
|
19
|
+
placeholder = 'Enter VIN (17 chars)'
|
|
20
|
+
}) {
|
|
21
|
+
const [value, setValue] = (0, _react.useState)(initialValue.toUpperCase());
|
|
22
|
+
const isValid = (0, _react.useMemo)(() => (0, _vinUtils.isValidVin)(value), [value]);
|
|
23
|
+
const showError = value.length >= 17 && !isValid;
|
|
24
|
+
const handlePress = ch => {
|
|
25
|
+
setValue(prev => (prev + ch).slice(0, 17).toUpperCase());
|
|
26
|
+
};
|
|
27
|
+
const handleBackspace = () => {
|
|
28
|
+
setValue(prev => prev.slice(0, -1));
|
|
29
|
+
};
|
|
30
|
+
const handleSubmit = () => {
|
|
31
|
+
if (isValid && onSubmit) {
|
|
32
|
+
onSubmit(value);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
36
|
+
style: styles.container
|
|
37
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.TextInput, {
|
|
38
|
+
value: value,
|
|
39
|
+
onChangeText: txt => setValue(txt.replace(/[^A-HJ-NPR-Z0-9]/gi, '').slice(0, 17).toUpperCase()),
|
|
40
|
+
style: [styles.input, showError && styles.errorInput],
|
|
41
|
+
placeholder: placeholder,
|
|
42
|
+
autoCapitalize: "characters",
|
|
43
|
+
autoCorrect: false
|
|
44
|
+
}), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
45
|
+
style: styles.row
|
|
46
|
+
}, VIN_CHARS.map(c => /*#__PURE__*/_react.default.createElement(Key, {
|
|
47
|
+
key: c,
|
|
48
|
+
label: c,
|
|
49
|
+
onPress: () => handlePress(c)
|
|
50
|
+
}))), /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
51
|
+
style: styles.row
|
|
52
|
+
}, DIGITS.map(c => /*#__PURE__*/_react.default.createElement(Key, {
|
|
53
|
+
key: c,
|
|
54
|
+
label: c,
|
|
55
|
+
onPress: () => handlePress(c)
|
|
56
|
+
})), /*#__PURE__*/_react.default.createElement(Key, {
|
|
57
|
+
label: "\u232B",
|
|
58
|
+
onPress: handleBackspace,
|
|
59
|
+
wide: true
|
|
60
|
+
})), /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
61
|
+
style: [styles.submit, {
|
|
62
|
+
backgroundColor: buttonColor,
|
|
63
|
+
opacity: isValid ? 1 : 0.5
|
|
64
|
+
}],
|
|
65
|
+
disabled: !isValid,
|
|
66
|
+
onPress: handleSubmit
|
|
67
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
68
|
+
style: styles.submitText
|
|
69
|
+
}, buttonLabel)), showError && /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
70
|
+
style: styles.errorText
|
|
71
|
+
}, "Checksum invalid"));
|
|
72
|
+
}
|
|
73
|
+
function Key({
|
|
74
|
+
label,
|
|
75
|
+
onPress,
|
|
76
|
+
wide
|
|
77
|
+
}) {
|
|
78
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
79
|
+
onPress: onPress,
|
|
80
|
+
style: [styles.key, wide && styles.keyWide]
|
|
81
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
82
|
+
style: styles.keyText
|
|
83
|
+
}, label));
|
|
84
|
+
}
|
|
85
|
+
const styles = _reactNative.StyleSheet.create({
|
|
86
|
+
container: {
|
|
87
|
+
width: '100%',
|
|
88
|
+
padding: 12,
|
|
89
|
+
backgroundColor: '#0D0D0D',
|
|
90
|
+
borderRadius: 12
|
|
91
|
+
},
|
|
92
|
+
input: {
|
|
93
|
+
backgroundColor: '#111827',
|
|
94
|
+
color: 'white',
|
|
95
|
+
paddingHorizontal: 12,
|
|
96
|
+
paddingVertical: 10,
|
|
97
|
+
borderRadius: 8,
|
|
98
|
+
fontSize: 16,
|
|
99
|
+
letterSpacing: 1,
|
|
100
|
+
borderWidth: 1,
|
|
101
|
+
borderColor: '#1F2937',
|
|
102
|
+
marginBottom: 10
|
|
103
|
+
},
|
|
104
|
+
errorInput: {
|
|
105
|
+
borderColor: '#EF4444'
|
|
106
|
+
},
|
|
107
|
+
row: {
|
|
108
|
+
flexDirection: 'row',
|
|
109
|
+
flexWrap: 'wrap',
|
|
110
|
+
gap: 6,
|
|
111
|
+
marginBottom: 8
|
|
112
|
+
},
|
|
113
|
+
key: {
|
|
114
|
+
paddingVertical: 10,
|
|
115
|
+
paddingHorizontal: 12,
|
|
116
|
+
backgroundColor: '#1F2937',
|
|
117
|
+
borderRadius: 8,
|
|
118
|
+
minWidth: 42,
|
|
119
|
+
alignItems: 'center'
|
|
120
|
+
},
|
|
121
|
+
keyWide: {
|
|
122
|
+
minWidth: 64
|
|
123
|
+
},
|
|
124
|
+
keyText: {
|
|
125
|
+
color: 'white',
|
|
126
|
+
fontSize: 14,
|
|
127
|
+
fontWeight: '700'
|
|
128
|
+
},
|
|
129
|
+
submit: {
|
|
130
|
+
marginTop: 8,
|
|
131
|
+
paddingVertical: 12,
|
|
132
|
+
borderRadius: 10,
|
|
133
|
+
alignItems: 'center'
|
|
134
|
+
},
|
|
135
|
+
submitText: {
|
|
136
|
+
color: 'white',
|
|
137
|
+
fontSize: 16,
|
|
138
|
+
fontWeight: '700'
|
|
139
|
+
},
|
|
140
|
+
errorText: {
|
|
141
|
+
color: '#F87171',
|
|
142
|
+
marginTop: 6,
|
|
143
|
+
fontSize: 13
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
var _default = exports.default = ManualVinInput;
|
|
147
|
+
//# sourceMappingURL=ManualVinInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_vinUtils","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","VIN_CHARS","DIGITS","ManualVinInput","initialValue","onSubmit","buttonLabel","buttonColor","placeholder","value","setValue","useState","toUpperCase","isValid","useMemo","isValidVin","showError","length","handlePress","ch","prev","slice","handleBackspace","handleSubmit","createElement","View","style","styles","container","TextInput","onChangeText","txt","replace","input","errorInput","autoCapitalize","autoCorrect","row","map","c","Key","key","label","onPress","wide","Pressable","submit","backgroundColor","opacity","disabled","Text","submitText","errorText","keyWide","keyText","StyleSheet","create","width","padding","borderRadius","color","paddingHorizontal","paddingVertical","fontSize","letterSpacing","borderWidth","borderColor","marginBottom","flexDirection","flexWrap","gap","minWidth","alignItems","fontWeight","marginTop","_default","exports"],"sourceRoot":"../../src","sources":["ManualVinInput.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.PendingVinBanner = PendingVinBanner;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
/**
|
|
12
|
+
* Compact bottom banner for pending text VINs.
|
|
13
|
+
* Works well together with `text.requireConfirmation`.
|
|
14
|
+
*/
|
|
15
|
+
function PendingVinBanner({
|
|
16
|
+
visible,
|
|
17
|
+
candidates,
|
|
18
|
+
onConfirm,
|
|
19
|
+
onDismiss,
|
|
20
|
+
buttonLabel = 'Use VIN',
|
|
21
|
+
buttonColor = '#0A84FF'
|
|
22
|
+
}) {
|
|
23
|
+
const anim = _react.default.useRef(new _reactNative.Animated.Value(0)).current;
|
|
24
|
+
_react.default.useEffect(() => {
|
|
25
|
+
_reactNative.Animated.timing(anim, {
|
|
26
|
+
toValue: visible ? 1 : 0,
|
|
27
|
+
duration: 220,
|
|
28
|
+
useNativeDriver: true
|
|
29
|
+
}).start();
|
|
30
|
+
}, [visible, anim]);
|
|
31
|
+
const translateY = anim.interpolate({
|
|
32
|
+
inputRange: [0, 1],
|
|
33
|
+
outputRange: [160, 0]
|
|
34
|
+
});
|
|
35
|
+
if (!candidates.length) return null;
|
|
36
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.Animated.View, {
|
|
37
|
+
pointerEvents: visible ? 'auto' : 'none',
|
|
38
|
+
style: [styles.container, {
|
|
39
|
+
transform: [{
|
|
40
|
+
translateY
|
|
41
|
+
}],
|
|
42
|
+
opacity: anim
|
|
43
|
+
}]
|
|
44
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
45
|
+
style: styles.header
|
|
46
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
47
|
+
style: styles.label
|
|
48
|
+
}, candidates.length > 1 ? `${candidates.length} VINs detected` : 'VIN detected'), onDismiss && /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
49
|
+
onPress: onDismiss,
|
|
50
|
+
hitSlop: 12,
|
|
51
|
+
style: styles.dismiss
|
|
52
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
53
|
+
style: styles.dismissText
|
|
54
|
+
}, "Dismiss"))), candidates.map(vin => /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
55
|
+
key: vin.value,
|
|
56
|
+
style: styles.row
|
|
57
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
58
|
+
style: styles.vin
|
|
59
|
+
}, vin.value), /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
60
|
+
onPress: () => onConfirm(vin),
|
|
61
|
+
style: [styles.button, {
|
|
62
|
+
backgroundColor: buttonColor
|
|
63
|
+
}]
|
|
64
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
65
|
+
style: styles.buttonText
|
|
66
|
+
}, buttonLabel)))));
|
|
67
|
+
}
|
|
68
|
+
const styles = _reactNative.StyleSheet.create({
|
|
69
|
+
container: {
|
|
70
|
+
position: 'absolute',
|
|
71
|
+
left: 16,
|
|
72
|
+
right: 16,
|
|
73
|
+
bottom: 40,
|
|
74
|
+
backgroundColor: 'rgba(0,0,0,0.9)',
|
|
75
|
+
borderRadius: 16,
|
|
76
|
+
padding: 14,
|
|
77
|
+
gap: 10
|
|
78
|
+
},
|
|
79
|
+
header: {
|
|
80
|
+
flexDirection: 'row',
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
justifyContent: 'space-between'
|
|
83
|
+
},
|
|
84
|
+
label: {
|
|
85
|
+
color: '#E5E7EB',
|
|
86
|
+
fontSize: 14,
|
|
87
|
+
fontWeight: '700'
|
|
88
|
+
},
|
|
89
|
+
dismiss: {
|
|
90
|
+
padding: 4
|
|
91
|
+
},
|
|
92
|
+
dismissText: {
|
|
93
|
+
color: '#9CA3AF',
|
|
94
|
+
fontSize: 12
|
|
95
|
+
},
|
|
96
|
+
row: {
|
|
97
|
+
flexDirection: 'row',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
justifyContent: 'space-between',
|
|
100
|
+
gap: 10
|
|
101
|
+
},
|
|
102
|
+
vin: {
|
|
103
|
+
flex: 1,
|
|
104
|
+
color: 'white',
|
|
105
|
+
fontSize: 16,
|
|
106
|
+
letterSpacing: 1
|
|
107
|
+
},
|
|
108
|
+
button: {
|
|
109
|
+
paddingHorizontal: 14,
|
|
110
|
+
paddingVertical: 10,
|
|
111
|
+
borderRadius: 10
|
|
112
|
+
},
|
|
113
|
+
buttonText: {
|
|
114
|
+
color: 'white',
|
|
115
|
+
fontSize: 14,
|
|
116
|
+
fontWeight: '700'
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
var _default = exports.default = PendingVinBanner;
|
|
120
|
+
//# sourceMappingURL=PendingVinBanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","e","__esModule","default","PendingVinBanner","visible","candidates","onConfirm","onDismiss","buttonLabel","buttonColor","anim","React","useRef","Animated","Value","current","useEffect","timing","toValue","duration","useNativeDriver","start","translateY","interpolate","inputRange","outputRange","length","createElement","View","pointerEvents","style","styles","container","transform","opacity","header","Text","label","Pressable","onPress","hitSlop","dismiss","dismissText","map","vin","key","value","row","button","backgroundColor","buttonText","StyleSheet","create","position","left","right","bottom","borderRadius","padding","gap","flexDirection","alignItems","justifyContent","color","fontSize","fontWeight","flex","letterSpacing","paddingHorizontal","paddingVertical","_default","exports"],"sourceRoot":"../../src","sources":["PendingVinBanner.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TextVinPrompt = TextVinPrompt;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
/**
|
|
12
|
+
* Lightweight confirmation UI for text VINs.
|
|
13
|
+
* - Shows detected VINs in a simple modal.
|
|
14
|
+
* - Barcodes should be handled upstream (they bypass this UI).
|
|
15
|
+
*/
|
|
16
|
+
function TextVinPrompt({
|
|
17
|
+
visible,
|
|
18
|
+
candidates,
|
|
19
|
+
onConfirm,
|
|
20
|
+
onDismiss,
|
|
21
|
+
title = 'VIN detected',
|
|
22
|
+
subtitle,
|
|
23
|
+
buttonLabel = 'Use VIN',
|
|
24
|
+
buttonColor = '#0A84FF'
|
|
25
|
+
}) {
|
|
26
|
+
if (!visible) return null;
|
|
27
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.Modal, {
|
|
28
|
+
transparent: true,
|
|
29
|
+
animationType: "fade",
|
|
30
|
+
visible: visible,
|
|
31
|
+
onRequestClose: onDismiss
|
|
32
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
33
|
+
style: styles.backdrop
|
|
34
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
35
|
+
style: styles.sheet
|
|
36
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
37
|
+
style: styles.title
|
|
38
|
+
}, title), subtitle ? /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
39
|
+
style: styles.subtitle
|
|
40
|
+
}, subtitle) : null, /*#__PURE__*/_react.default.createElement(_reactNative.ScrollView, {
|
|
41
|
+
style: styles.list,
|
|
42
|
+
contentContainerStyle: styles.listContent
|
|
43
|
+
}, candidates.map(candidate => /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
44
|
+
key: candidate.value,
|
|
45
|
+
style: styles.row
|
|
46
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
47
|
+
style: styles.vin,
|
|
48
|
+
numberOfLines: 1
|
|
49
|
+
}, candidate.value), /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
50
|
+
onPress: () => onConfirm(candidate),
|
|
51
|
+
style: [styles.button, {
|
|
52
|
+
backgroundColor: buttonColor
|
|
53
|
+
}],
|
|
54
|
+
android_ripple: {
|
|
55
|
+
color: '#ffffff22'
|
|
56
|
+
}
|
|
57
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
58
|
+
style: styles.buttonText
|
|
59
|
+
}, buttonLabel))))), onDismiss && /*#__PURE__*/_react.default.createElement(_reactNative.Pressable, {
|
|
60
|
+
onPress: onDismiss,
|
|
61
|
+
style: styles.dismiss
|
|
62
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
63
|
+
style: styles.dismissText
|
|
64
|
+
}, "Dismiss")))));
|
|
65
|
+
}
|
|
66
|
+
const styles = _reactNative.StyleSheet.create({
|
|
67
|
+
backdrop: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
backgroundColor: 'rgba(0,0,0,0.55)',
|
|
70
|
+
alignItems: 'center',
|
|
71
|
+
justifyContent: 'center',
|
|
72
|
+
padding: 16
|
|
73
|
+
},
|
|
74
|
+
sheet: {
|
|
75
|
+
width: '100%',
|
|
76
|
+
maxWidth: 420,
|
|
77
|
+
backgroundColor: '#0D0D0D',
|
|
78
|
+
borderRadius: 16,
|
|
79
|
+
padding: 16
|
|
80
|
+
},
|
|
81
|
+
title: {
|
|
82
|
+
color: 'white',
|
|
83
|
+
fontSize: 18,
|
|
84
|
+
fontWeight: '700',
|
|
85
|
+
marginBottom: 12
|
|
86
|
+
},
|
|
87
|
+
subtitle: {
|
|
88
|
+
color: '#D1D5DB',
|
|
89
|
+
fontSize: 14,
|
|
90
|
+
marginBottom: 8
|
|
91
|
+
},
|
|
92
|
+
list: {
|
|
93
|
+
maxHeight: 220
|
|
94
|
+
},
|
|
95
|
+
listContent: {
|
|
96
|
+
gap: 12
|
|
97
|
+
},
|
|
98
|
+
row: {
|
|
99
|
+
flexDirection: 'row',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'space-between',
|
|
102
|
+
gap: 12
|
|
103
|
+
},
|
|
104
|
+
vin: {
|
|
105
|
+
flex: 1,
|
|
106
|
+
color: 'white',
|
|
107
|
+
fontSize: 16,
|
|
108
|
+
letterSpacing: 1
|
|
109
|
+
},
|
|
110
|
+
button: {
|
|
111
|
+
paddingHorizontal: 14,
|
|
112
|
+
paddingVertical: 10,
|
|
113
|
+
borderRadius: 10
|
|
114
|
+
},
|
|
115
|
+
buttonText: {
|
|
116
|
+
color: 'white',
|
|
117
|
+
fontSize: 14,
|
|
118
|
+
fontWeight: '700'
|
|
119
|
+
},
|
|
120
|
+
dismiss: {
|
|
121
|
+
marginTop: 12,
|
|
122
|
+
alignSelf: 'flex-end',
|
|
123
|
+
paddingHorizontal: 8,
|
|
124
|
+
paddingVertical: 6
|
|
125
|
+
},
|
|
126
|
+
dismissText: {
|
|
127
|
+
color: '#9CA3AF',
|
|
128
|
+
fontSize: 14
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
var _default = exports.default = TextVinPrompt;
|
|
132
|
+
//# sourceMappingURL=TextVinPrompt.js.map
|