mirror of
https://github.com/documenso/documenso.git
synced 2025-11-23 13:11:32 +10:00
feat: add digitized signatures
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { KeyboardIcon, UploadCloudIcon } from 'lucide-react';
|
||||
import { Keyboard, KeyboardIcon, UploadCloudIcon } from 'lucide-react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { DocumentSignatureType } from '@documenso/lib/constants/document';
|
||||
@ -10,6 +10,7 @@ import { isBase64Image } from '@documenso/lib/constants/signatures';
|
||||
import { SignatureIcon } from '../../icons/signature';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { SignaturePadDraw } from './signature-pad-draw';
|
||||
import { SignaturePadKeyboard } from './signature-pad-keyboard';
|
||||
import { SignaturePadType } from './signature-pad-type';
|
||||
import { SignaturePadUpload } from './signature-pad-upload';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from './signature-tabs';
|
||||
@ -28,6 +29,7 @@ export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChang
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
drawSignatureEnabled?: boolean;
|
||||
keyboardSignatureEnabled?: boolean;
|
||||
|
||||
onValidityChange?: (isValid: boolean) => void;
|
||||
};
|
||||
@ -39,10 +41,12 @@ export const SignaturePad = ({
|
||||
typedSignatureEnabled = true,
|
||||
uploadSignatureEnabled = true,
|
||||
drawSignatureEnabled = true,
|
||||
keyboardSignatureEnabled = true,
|
||||
}: SignaturePadProps) => {
|
||||
const [imageSignature, setImageSignature] = useState(isBase64Image(value) ? value : '');
|
||||
const [drawSignature, setDrawSignature] = useState(isBase64Image(value) ? value : '');
|
||||
const [typedSignature, setTypedSignature] = useState(isBase64Image(value) ? '' : value);
|
||||
const [keyboardSignature, setKeyboardSignature] = useState(isBase64Image(value) ? value : '');
|
||||
|
||||
/**
|
||||
* This is cooked.
|
||||
@ -51,7 +55,7 @@ export const SignaturePad = ({
|
||||
* the first enabled tab.
|
||||
*/
|
||||
const [tab, setTab] = useState(
|
||||
((): 'draw' | 'text' | 'image' => {
|
||||
((): 'draw' | 'text' | 'image' | 'keyboard' => {
|
||||
// First passthrough to check to see if there's a signature for a given tab.
|
||||
if (drawSignatureEnabled && drawSignature) {
|
||||
return 'draw';
|
||||
@ -65,6 +69,10 @@ export const SignaturePad = ({
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (keyboardSignatureEnabled && keyboardSignature) {
|
||||
return 'keyboard';
|
||||
}
|
||||
|
||||
// Second passthrough to just select the first avaliable tab.
|
||||
if (drawSignatureEnabled) {
|
||||
return 'draw';
|
||||
@ -78,6 +86,10 @@ export const SignaturePad = ({
|
||||
return 'image';
|
||||
}
|
||||
|
||||
if (keyboardSignatureEnabled) {
|
||||
return 'keyboard';
|
||||
}
|
||||
|
||||
throw new Error('No signature enabled');
|
||||
})(),
|
||||
);
|
||||
@ -109,7 +121,16 @@ export const SignaturePad = ({
|
||||
});
|
||||
};
|
||||
|
||||
const onTabChange = (value: 'draw' | 'text' | 'image') => {
|
||||
const onKeyboardSignatureChange = (value: string) => {
|
||||
setKeyboardSignature(value);
|
||||
|
||||
onChange?.({
|
||||
type: DocumentSignatureType.KEYBOARD,
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
const onTabChange = (value: 'draw' | 'text' | 'image' | 'keyboard') => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
@ -126,10 +147,18 @@ export const SignaturePad = ({
|
||||
.with('image', () => {
|
||||
onImageSignatureChange(imageSignature);
|
||||
})
|
||||
.with('keyboard', () => {
|
||||
onKeyboardSignatureChange(keyboardSignature);
|
||||
})
|
||||
.exhaustive();
|
||||
};
|
||||
|
||||
if (!drawSignatureEnabled && !typedSignatureEnabled && !uploadSignatureEnabled) {
|
||||
if (
|
||||
!drawSignatureEnabled &&
|
||||
!typedSignatureEnabled &&
|
||||
!uploadSignatureEnabled &&
|
||||
!keyboardSignatureEnabled
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -140,7 +169,7 @@ export const SignaturePad = ({
|
||||
'pointer-events-none': disabled,
|
||||
})}
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
onValueChange={(value) => onTabChange(value as 'draw' | 'text' | 'image')}
|
||||
onValueChange={(value) => onTabChange(value as 'draw' | 'text' | 'image' | 'keyboard')}
|
||||
>
|
||||
<TabsList>
|
||||
{drawSignatureEnabled && (
|
||||
@ -163,6 +192,13 @@ export const SignaturePad = ({
|
||||
Upload
|
||||
</TabsTrigger>
|
||||
)}
|
||||
|
||||
{keyboardSignatureEnabled && (
|
||||
<TabsTrigger value="keyboard">
|
||||
<Keyboard className="mr-2 size-4" />
|
||||
Keyboard
|
||||
</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
<TabsContent
|
||||
@ -194,6 +230,13 @@ export const SignaturePad = ({
|
||||
>
|
||||
<SignaturePadUpload value={imageSignature} onChange={onImageSignatureChange} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="keyboard"
|
||||
className="border-border aspect-signature-pad dark:bg-background relative flex items-center justify-center rounded-md border bg-neutral-50 text-center"
|
||||
>
|
||||
<SignaturePadKeyboard value={keyboardSignature} onChange={onKeyboardSignatureChange} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user