Skip to content

wcz-layout

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>
  );
};