scanning
68 linesSource
Use when a feature reads barcodes or QR codes, from a handheld laser scanner (keystrokes) via useScanDetection or from the device camera via @yudiel/react-qr-scanner.
Rules
- Laser scanners type the code as fast keystrokes. Catch them with useScanDetection from wcz-layout/hooks, never a hidden input or an own keydown listener. It listens on document, so nothing needs focus.
- useScanDetection({ onComplete, ... }) options (defaults): minLength (1), averageWaitTime (50 ms between keys, slower input counts as typing), timeToEvaluate (100 ms), startCharacter ([]), endCharacter (["Enter", "Escape"]), onError, container (document), ignoreIfFocusOn, preventDefault / stopPropagation (false), enabled (true). Set minLength to the shortest valid code; use enabled: false while scans must be ignored.
- Camera scanning always uses Scanner from @yudiel/react-qr-scanner (install if missing),
never another QR/barcode library. Rear camera, finder and torch are on by default. Always set:
- constraints={{ width: { ideal: 1920 }, height: { ideal: 1080 } }}: Full HD, the default 720p misses small and dense codes.
- formats: only the symbologies the feature expects (qr_code, code_128, ean_13, …). All formats are scanned by default, which is slower and misreads more.
- onError: alert error.message (permission denied, no camera, camera in use).
- Open the camera in a custom dialog that resolves with the first scanned value (see dialogs). It needs HTTPS or localhost; the component is SSR-safe.
- Where both scanners may be used, keep useScanDetection on the page, add a camera button, and send both results to the same handler.
Examples
tsx
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
import { Scanner } from "@yudiel/react-qr-scanner";
import type { DialogProps } from "wcz-layout/hooks";
import { useDialogs, useScanDetection, useTranslation } from "wcz-layout/hooks";
// laser scanner
useScanDetection({ onComplete: handleCode, minLength: 8 });
// camera button
const { open } = useDialogs();
const code = await open(ScanDialog);
if (code) await handleCode(code);
// camera dialog
export const ScanDialog = ({ open, onClose }: DialogProps<undefined, string | undefined>) => {
const { t } = useTranslation();
const { alert } = useDialogs();
return (
<Dialog fullWidth open={open} onClose={() => onClose(undefined)}>
<DialogTitle>{t("Scan")}</DialogTitle>
<DialogContent>
<Scanner
constraints={{ width: { ideal: 1920 }, height: { ideal: 1080 } }}
formats={["qr_code", "code_128"]}
onScan={([code]) => onClose(code.rawValue)}
onError={(error) => alert(error.message)}
/>
</DialogContent>
</Dialog>
);
};