mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
## Description Adds a dialog that will display when a certain field threshold is reached asking the user if they would like to sign non-critical fields such as name, date, initials, and email with information that is already available. This has not been added to direct templates since we would often not have all the pre-requisite knowledge since users are mostly anonymous. Additionally, this has not been added to the embedding view since it may detract from the experience for some. Will not prompt the user if there is action authentication on the document. See the below demo: https://github.com/user-attachments/assets/71739b5c-1323-4da9-89fd-a1145c9714d5 ## Related Issue #1281 (Older PR relating to the feature) ## Changes Made - Added a new auto-sign dialog that will automatically trigger once certain criteria is met. ## Testing Performed - Tested that the dialog displays when the threshold is met - Tested that the dialog is hidden when the threshold is not met - Tested that the messaging during errors is correct - Tested that the dialog does not display when 2FA or Passkeys are required
148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
|
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
import { X } from 'lucide-react';
|
|
|
|
import { cn } from '../lib/utils';
|
|
|
|
const Dialog = DialogPrimitive.Root;
|
|
|
|
const DialogTrigger = DialogPrimitive.Trigger;
|
|
|
|
const DialogClose = DialogPrimitive.Close;
|
|
|
|
const DialogPortal = ({
|
|
children,
|
|
position = 'start',
|
|
...props
|
|
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' | 'center' }) => (
|
|
<DialogPrimitive.Portal {...props}>
|
|
<div
|
|
className={cn('fixed inset-0 z-[1000] flex justify-center sm:items-center', {
|
|
'items-start': position === 'start',
|
|
'items-end': position === 'end',
|
|
'items-center': position === 'center',
|
|
})}
|
|
>
|
|
{children}
|
|
</div>
|
|
</DialogPrimitive.Portal>
|
|
);
|
|
|
|
DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
|
|
|
const DialogOverlay = React.forwardRef<
|
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
|
>(({ className, ...props }, ref) => (
|
|
<DialogPrimitive.Overlay
|
|
ref={ref}
|
|
className={cn(
|
|
'bg-background/80 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in fixed inset-0 z-50 backdrop-blur-sm transition-all duration-100',
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
));
|
|
|
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
|
|
const DialogContent = React.forwardRef<
|
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
|
position?: 'start' | 'end' | 'center';
|
|
hideClose?: boolean;
|
|
/* Below prop is to add additional classes to the overlay */
|
|
overlayClassName?: string;
|
|
}
|
|
>(
|
|
(
|
|
{ className, children, overlayClassName, position = 'start', hideClose = false, ...props },
|
|
ref,
|
|
) => (
|
|
<DialogPortal position={position}>
|
|
<DialogOverlay className={cn(overlayClassName)} />
|
|
<DialogPrimitive.Content
|
|
ref={ref}
|
|
className={cn(
|
|
'bg-background animate-in data-[state=open]:fade-in-90 sm:zoom-in-90 data-[state=open]:slide-in-from-bottom-10 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
{!hideClose && (
|
|
<DialogPrimitive.Close
|
|
data-testid="btn-dialog-close"
|
|
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
<span className="sr-only">Close</span>
|
|
</DialogPrimitive.Close>
|
|
)}
|
|
</DialogPrimitive.Content>
|
|
</DialogPortal>
|
|
),
|
|
);
|
|
|
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
|
|
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
<div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />
|
|
);
|
|
|
|
DialogHeader.displayName = 'DialogHeader';
|
|
|
|
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
<div
|
|
className={cn(
|
|
'flex flex-col-reverse space-y-2 space-y-reverse sm:flex-row sm:justify-end sm:space-x-2 sm:space-y-0',
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
|
|
DialogFooter.displayName = 'DialogFooter';
|
|
|
|
const DialogTitle = React.forwardRef<
|
|
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
>(({ className, ...props }, ref) => (
|
|
<DialogPrimitive.Title
|
|
ref={ref}
|
|
className={cn('truncate text-lg font-semibold tracking-tight', className)}
|
|
{...props}
|
|
/>
|
|
));
|
|
|
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
|
|
const DialogDescription = React.forwardRef<
|
|
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
>(({ className, ...props }, ref) => (
|
|
<DialogPrimitive.Description
|
|
ref={ref}
|
|
className={cn('text-muted-foreground text-sm', className)}
|
|
{...props}
|
|
/>
|
|
));
|
|
|
|
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
|
|
export {
|
|
Dialog,
|
|
DialogTrigger,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogFooter,
|
|
DialogOverlay,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogPortal,
|
|
DialogClose,
|
|
};
|