"use client" import { useEffect, useRef, useState } from "react" import { Eraser, Save } from "lucide-react" import { Button } from "@/shared/components/ui/button" type Props = { label: string existingUrl?: string | null onSave: (dataUrl: string) => Promise | void saving?: boolean } /** * Minimal in-house signature pad — uses + pointer events. * Works with mouse, touch, and stylus on any device. No external dep. */ export function SignaturePad({ label, existingUrl, onSave, saving }: Props) { const canvasRef = useRef(null) const drawingRef = useRef(false) const lastPointRef = useRef<{ x: number; y: number } | null>(null) const [hasDrawn, setHasDrawn] = useState(false) const getCtx = () => { const c = canvasRef.current if (!c) return null const ctx = c.getContext("2d") if (!ctx) return null ctx.lineWidth = 2.2 ctx.lineCap = "round" ctx.lineJoin = "round" ctx.strokeStyle = "#111" return ctx } useEffect(() => { // Size the canvas to its CSS size while keeping crisp lines on HiDPI screens. const c = canvasRef.current if (!c) return const dpr = window.devicePixelRatio || 1 const rect = c.getBoundingClientRect() c.width = Math.round(rect.width * dpr) c.height = Math.round(rect.height * dpr) const ctx = c.getContext("2d") ctx?.scale(dpr, dpr) }, []) const pointFromEvent = (e: React.PointerEvent) => { const rect = (e.target as HTMLCanvasElement).getBoundingClientRect() return { x: e.clientX - rect.left, y: e.clientY - rect.top } } const handleDown = (e: React.PointerEvent) => { e.preventDefault() ;(e.target as HTMLCanvasElement).setPointerCapture(e.pointerId) drawingRef.current = true lastPointRef.current = pointFromEvent(e) } const handleMove = (e: React.PointerEvent) => { if (!drawingRef.current) return const ctx = getCtx() if (!ctx || !lastPointRef.current) return const p = pointFromEvent(e) ctx.beginPath() ctx.moveTo(lastPointRef.current.x, lastPointRef.current.y) ctx.lineTo(p.x, p.y) ctx.stroke() lastPointRef.current = p setHasDrawn(true) } const handleUp = () => { drawingRef.current = false lastPointRef.current = null } const clear = () => { const c = canvasRef.current const ctx = getCtx() if (!c || !ctx) return ctx.clearRect(0, 0, c.width, c.height) setHasDrawn(false) } const submit = async () => { if (!canvasRef.current || !hasDrawn) return const dataUrl = canvasRef.current.toDataURL("image/png") await onSave(dataUrl) } return (
{label}
{existingUrl && !hasDrawn && (
Previously signed:
)}
) }