mirror of
https://github.com/documenso/documenso.git
synced 2025-11-18 02:32:00 +10:00
feat: add signature configurations (#1710)
Add ability to enable or disable allowed signature types: - Drawn - Typed - Uploaded **Tabbed style signature dialog**  **Document settings**  **Team preferences**  ## Changes Made - Add multiselect to select allowed signatures in document and templates settings tab - Add multiselect to select allowed signatures in teams preferences - Removed "Enable typed signatures" from document/template edit page - Refactored signature pad to use tabs instead of an all in one signature pad ## Testing Performed Added E2E tests to check settings are applied correctly for documents and templates
This commit is contained in:
150
packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
Normal file
150
packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { parseMessageDescriptor } from '@documenso/lib/utils/i18n';
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter } from '@documenso/ui/primitives/dialog';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Button } from '../button';
|
||||
import { SignaturePad } from './signature-pad';
|
||||
import { SignatureRender } from './signature-render';
|
||||
|
||||
export type SignaturePadDialogProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChange'> & {
|
||||
disabled?: boolean;
|
||||
value?: string;
|
||||
onChange: (_value: string) => void;
|
||||
dialogConfirmText?: MessageDescriptor | string;
|
||||
disableAnimation?: boolean;
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
drawSignatureEnabled?: boolean;
|
||||
};
|
||||
|
||||
export const SignaturePadDialog = ({
|
||||
className,
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
disableAnimation = false,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
dialogConfirmText,
|
||||
}: SignaturePadDialogProps) => {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
const [showSignatureModal, setShowSignatureModal] = useState(false);
|
||||
const [signature, setSignature] = useState<string>(value ?? '');
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'aspect-signature-pad bg-background relative block w-full select-none rounded-lg border',
|
||||
className,
|
||||
{
|
||||
'pointer-events-none opacity-50': disabled,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{value && (
|
||||
<div className="inset-0 h-full w-full">
|
||||
<SignatureRender value={value} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<motion.button
|
||||
data-testid="signature-pad-dialog-button"
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className="absolute inset-0 flex items-center justify-center bg-transparent"
|
||||
onClick={() => setShowSignatureModal(true)}
|
||||
whileHover="onHover"
|
||||
>
|
||||
{!value && !disableAnimation && (
|
||||
<motion.svg
|
||||
width="120"
|
||||
height="120"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="text-muted-foreground/60"
|
||||
variants={{
|
||||
onHover: {
|
||||
scale: 1.1,
|
||||
transition: {
|
||||
type: 'spring',
|
||||
stiffness: 300,
|
||||
damping: 12,
|
||||
mass: 0.8,
|
||||
restDelta: 0.001,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<motion.path
|
||||
d="M1.5 11H14.5M1.5 14C1.5 14 8.72 2 4.86938 2H4.875C2.01 2 1.97437 14.0694 8 6.51188V6.5C8 6.5 9 11.3631 11.5 7.52375V7.5C11.5 7.5 11.5 9 14.5 9"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.1"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
pathLength: {
|
||||
duration: 2,
|
||||
ease: 'easeInOut',
|
||||
},
|
||||
opacity: { duration: 0.6 },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</motion.svg>
|
||||
)}
|
||||
</motion.button>
|
||||
|
||||
<Dialog open={showSignatureModal} onOpenChange={disabled ? undefined : setShowSignatureModal}>
|
||||
<DialogContent hideClose={true} className="p-6 pt-4">
|
||||
<SignaturePad
|
||||
id="signature"
|
||||
value={value}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
onChange={({ value }) => setSignature(value)}
|
||||
typedSignatureEnabled={typedSignatureEnabled}
|
||||
uploadSignatureEnabled={uploadSignatureEnabled}
|
||||
drawSignatureEnabled={drawSignatureEnabled}
|
||||
/>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="ghost">
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
disabled={!signature}
|
||||
onClick={() => {
|
||||
onChange(signature);
|
||||
setShowSignatureModal(false);
|
||||
}}
|
||||
>
|
||||
{dialogConfirmText ? (
|
||||
parseMessageDescriptor(i18n._, dialogConfirmText)
|
||||
) : (
|
||||
<Trans>Next</Trans>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user