diff --git a/packages/fieldlens-core/src/features/codes/CodeLab.css b/packages/fieldlens-core/src/features/codes/CodeLab.css index 2b3a736..c12f5ac 100644 --- a/packages/fieldlens-core/src/features/codes/CodeLab.css +++ b/packages/fieldlens-core/src/features/codes/CodeLab.css @@ -144,4 +144,102 @@ .ean-lab-fields b { font-size: 5.8px; } -} \ No newline at end of file +} + +.code-card-heading-actions { + display: flex; + align-items: center; + gap: 9px; +} + +.code-print-button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5px; + min-height: 30px; + padding: 0 9px; + border: 1px solid var(--line); + border-radius: 5px; + color: var(--ink); + background: #fff; + font-size: 9px; + font-weight: 900; + white-space: nowrap; +} + +.code-print-button:hover { + border-color: #c9a72d; + background: #fff8d7; +} + +.barcode-print-sheet { + display: none; +} + +@media print { + @page { + margin: 12mm; + } + + body * { + visibility: hidden !important; + } + + .barcode-print-sheet, + .barcode-print-sheet * { + visibility: visible !important; + } + + .barcode-print-sheet { + position: fixed; + inset: 0; + display: grid !important; + place-items: center; + padding: 0; + background: #fff; + } + + .barcode-print-label { + display: grid; + width: min(170mm, 100%); + place-items: center; + gap: 8mm; + color: #000; + background: #fff; + text-align: center; + } + + .barcode-print-label strong { + font: 700 14pt/1.2 Arial, Helvetica, sans-serif; + } + + .barcode-print-label canvas { + display: block; + max-width: 100%; + height: auto; + } + + .barcode-print-label code { + max-width: 100%; + overflow-wrap: anywhere; + color: #000; + font: 11pt/1.3 "Courier New", monospace; + } +} + +@media (max-width: 760px) { + .code-card-heading { + align-items: flex-start; + } + + .code-card-heading-actions { + align-items: flex-end; + flex-direction: column; + gap: 5px; + } + + .code-print-button { + min-height: 29px; + } +} diff --git a/packages/fieldlens-core/src/features/codes/CodeLab.tsx b/packages/fieldlens-core/src/features/codes/CodeLab.tsx index f916eb8..4f48d28 100644 --- a/packages/fieldlens-core/src/features/codes/CodeLab.tsx +++ b/packages/fieldlens-core/src/features/codes/CodeLab.tsx @@ -1,5 +1,5 @@ import bwipjs from 'bwip-js' -import { RefreshCw } from 'lucide-react' +import { Printer, RefreshCw } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { appendGtinCheckDigit, randomDigits } from '../../lib/barcodes' @@ -65,23 +65,27 @@ function makeEanFixtures(): EanLabelFixture[] { })) } -function CodeCard({ fixture }: { fixture: CodeFixture }) { +function drawBarcode(canvas: HTMLCanvasElement, fixture: CodeFixture, print = false) { + bwipjs.toCanvas(canvas, { + bcid: fixture.bcid, + text: fixture.value, + scale: print ? 5 : 3, + height: fixture.bcid === 'qrcode' ? (print ? 34 : 24) : (print ? 20 : 13), + includetext: fixture.bcid !== 'qrcode', + textxalign: 'center', + backgroundcolor: 'FFFFFF', + paddingwidth: print ? 12 : 8, + paddingheight: print ? 12 : 8, + }) +} + +function CodeCard({ fixture, onPrint }: { fixture: CodeFixture; onPrint: (fixture: CodeFixture) => void }) { const canvasRef = useRef(null) useEffect(() => { if (!canvasRef.current) return try { - bwipjs.toCanvas(canvasRef.current, { - bcid: fixture.bcid, - text: fixture.value, - scale: 3, - height: fixture.bcid === 'qrcode' ? 24 : 13, - includetext: fixture.bcid !== 'qrcode', - textxalign: 'center', - backgroundcolor: 'FFFFFF', - paddingwidth: 8, - paddingheight: 8, - }) + drawBarcode(canvasRef.current, fixture) } catch { // A failed fixture remains labeled so generator errors are visible during testing. } @@ -89,13 +93,44 @@ function CodeCard({ fixture }: { fixture: CodeFixture }) { return (
-
{fixture.name}VALID SAMPLE
+
+ {fixture.name} +
+ VALID SAMPLE + +
+
{fixture.value}
) } +function PrintableBarcode({ fixture }: { fixture: CodeFixture }) { + const canvasRef = useRef(null) + + useEffect(() => { + if (!canvasRef.current) return + try { + drawBarcode(canvasRef.current, fixture, true) + } catch { + // Keep the value visible if rendering fails so the print is diagnosable. + } + }, [fixture]) + + return ( + + ) +} + function EanLabelCard({ fixture }: { fixture: EanLabelFixture }) { return (
@@ -126,12 +161,35 @@ export function CodeLab() { const [tab, setTab] = useState('barcodes') const [fixtures, setFixtures] = useState(makeFixtures) const [eanFixtures, setEanFixtures] = useState(makeEanFixtures) + const [printFixture, setPrintFixture] = useState(null) const regenerate = () => { if (tab === 'ean') setEanFixtures(makeEanFixtures()) else setFixtures(makeFixtures()) } + useEffect(() => { + if (!printFixture) return + + let cancelled = false + let frameTwo = 0 + const clearPrintFixture = () => setPrintFixture(null) + window.addEventListener('afterprint', clearPrintFixture, { once: true }) + + const frameOne = window.requestAnimationFrame(() => { + frameTwo = window.requestAnimationFrame(() => { + if (!cancelled) window.print() + }) + }) + + return () => { + cancelled = true + window.cancelAnimationFrame(frameOne) + if (frameTwo) window.cancelAnimationFrame(frameTwo) + window.removeEventListener('afterprint', clearPrintFixture) + } + }, [printFixture]) + return (
@@ -153,10 +211,11 @@ export function CodeLab() { {tab === 'barcodes' ? ( -
{fixtures.map((fixture) => )}
+
{fixtures.map((fixture) => )}
) : (
{eanFixtures.map((fixture) => )}
)} + {printFixture && }
) } \ No newline at end of file