import type { HTMLAttributes } from 'react'; import { useState } from 'react'; import { KeyboardIcon, UploadCloudIcon } from 'lucide-react'; import { match } from 'ts-pattern'; import { Trans } from '@lingui/react/macro'; import { DocumentSignatureType } from '@documenso/lib/constants/document'; import { isBase64Image } from '@documenso/lib/constants/signatures'; import { SignatureIcon } from '../../icons/signature'; import { cn } from '../../lib/utils'; import { SignaturePadDraw } from './signature-pad-draw'; import { SignaturePadType } from './signature-pad-type'; import { SignaturePadUpload } from './signature-pad-upload'; import { Tabs, TabsContent, TabsList, TabsTrigger } from './signature-tabs'; export type SignaturePadValue = { type: DocumentSignatureType; value: string; }; export type SignaturePadProps = Omit, 'onChange'> & { value?: string; onChange?: (_value: SignaturePadValue) => void; disabled?: boolean; typedSignatureEnabled?: boolean; uploadSignatureEnabled?: boolean; drawSignatureEnabled?: boolean; onValidityChange?: (isValid: boolean) => void; }; export const SignaturePad = ({ value = '', onChange, disabled = false, typedSignatureEnabled = true, uploadSignatureEnabled = true, drawSignatureEnabled = true, }: SignaturePadProps) => { const [imageSignature, setImageSignature] = useState(isBase64Image(value) ? value : ''); const [drawSignature, setDrawSignature] = useState(isBase64Image(value) ? value : ''); const [typedSignature, setTypedSignature] = useState(isBase64Image(value) ? '' : value); /** * This is cooked. * * Get the first enabled tab that has a signature if possible, otherwise just get * the first enabled tab. */ const [tab, setTab] = useState( ((): 'draw' | 'text' | 'image' => { // First passthrough to check to see if there's a signature for a given tab. if (drawSignatureEnabled && drawSignature) { return 'draw'; } if (typedSignatureEnabled && typedSignature) { return 'text'; } if (uploadSignatureEnabled && imageSignature) { return 'image'; } // Second passthrough to just select the first avaliable tab. if (drawSignatureEnabled) { return 'draw'; } if (typedSignatureEnabled) { return 'text'; } if (uploadSignatureEnabled) { return 'image'; } throw new Error('No signature enabled'); })(), ); const onImageSignatureChange = (value: string) => { setImageSignature(value); onChange?.({ type: DocumentSignatureType.UPLOAD, value, }); }; const onDrawSignatureChange = (value: string) => { setDrawSignature(value); onChange?.({ type: DocumentSignatureType.DRAW, value, }); }; const onTypedSignatureChange = (value: string) => { setTypedSignature(value); onChange?.({ type: DocumentSignatureType.TYPE, value, }); }; const onTabChange = (value: 'draw' | 'text' | 'image') => { if (disabled) { return; } setTab(value); match(value) .with('draw', () => { onDrawSignatureChange(drawSignature); }) .with('text', () => { onTypedSignatureChange(typedSignature); }) .with('image', () => { onImageSignatureChange(imageSignature); }) .exhaustive(); }; if (!drawSignatureEnabled && !typedSignatureEnabled && !uploadSignatureEnabled) { return null; } return ( onTabChange(value as 'draw' | 'text' | 'image')} > {drawSignatureEnabled && ( Draw )} {typedSignatureEnabled && ( Type )} {uploadSignatureEnabled && ( Upload )} ); };