77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
|
|
import { useEffect, useState } from 'react'
|
|||
|
|
import classNames from 'classnames'
|
|||
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|||
|
|
import { writeBarcodeSvg, type BarcodeWriteFormat } from '@/utils/barcode'
|
|||
|
|
|
|||
|
|
export type BarcodeViewerProps = {
|
|||
|
|
/** Barkoda çevrilecek veri. Form içinde kolona bağlanan alan budur. */
|
|||
|
|
value?: string
|
|||
|
|
format?: BarcodeWriteFormat
|
|||
|
|
label?: string
|
|||
|
|
/** Kodun altına okunabilir metni de basar. */
|
|||
|
|
showText?: boolean
|
|||
|
|
/** Modül (en ince çubuk) kalınlığı; büyüdükçe kod büyür. */
|
|||
|
|
scale?: number
|
|||
|
|
/** Kodu kapsayıcının genişliğine yayar; kapalıyken `scale` boyutu korunur. */
|
|||
|
|
fitWidth?: boolean
|
|||
|
|
className?: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Herhangi bir veriyi barkod/QR olarak çizer. Tasarımcıdan sürüklenip bırakılan
|
|||
|
|
* düğüm ve üretilen kod aynı bileşeni kullanır; `value` bir Form'un kaydına
|
|||
|
|
* bağlandığında alan değiştikçe kod da yeniden çizilir.
|
|||
|
|
*
|
|||
|
|
* Kod SVG olarak üretilir: baskıda ve büyütmede kırılmaz, karanlık temada da
|
|||
|
|
* okunabilir kalsın diye beyaz zemin üstünde çizilir.
|
|||
|
|
*/
|
|||
|
|
const BarcodeViewer = ({
|
|||
|
|
value,
|
|||
|
|
format = 'QRCode',
|
|||
|
|
label,
|
|||
|
|
showText = false,
|
|||
|
|
scale = 4,
|
|||
|
|
fitWidth = false,
|
|||
|
|
className,
|
|||
|
|
}: BarcodeViewerProps) => {
|
|||
|
|
const { translate } = useLocalization()
|
|||
|
|
const [svg, setSvg] = useState('')
|
|||
|
|
const [error, setError] = useState('')
|
|||
|
|
const text = value === undefined || value === null ? '' : String(value)
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
let cancelled = false
|
|||
|
|
void writeBarcodeSvg(text, { format, scale, showText }).then((result) => {
|
|||
|
|
if (cancelled) return
|
|||
|
|
setSvg(result.svg)
|
|||
|
|
setError(result.error)
|
|||
|
|
})
|
|||
|
|
return () => {
|
|||
|
|
cancelled = true
|
|||
|
|
}
|
|||
|
|
}, [text, format, scale, showText])
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className={className}>
|
|||
|
|
{label && <label className="mb-1 block text-sm font-semibold">{label}</label>}
|
|||
|
|
{!text ? (
|
|||
|
|
<span className="text-sm opacity-70">{translate('::App.BarcodeViewer.NoValue')}</span>
|
|||
|
|
) : error ? (
|
|||
|
|
<span className="text-sm text-red-500">{error}</span>
|
|||
|
|
) : (
|
|||
|
|
<div
|
|||
|
|
// zxing'in ürettiği SVG'dir, kullanıcı verisi değil: metin HRI içine
|
|||
|
|
// kaçışlanarak yazılır, bu yüzden gömmek güvenlidir.
|
|||
|
|
dangerouslySetInnerHTML={{ __html: svg }}
|
|||
|
|
className={classNames(
|
|||
|
|
'inline-block rounded bg-white p-2',
|
|||
|
|
fitWidth && 'block w-full [&>svg]:h-auto [&>svg]:w-full',
|
|||
|
|
)}
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default BarcodeViewer
|