remove close animation from dialogs

This commit is contained in:
Eduard Gert
2026-06-10 17:36:29 +02:00
parent 7648aa7015
commit cdcdd6a44f
2 changed files with 59 additions and 70 deletions

View File

@@ -1,4 +1,4 @@
import { ReactNode, useEffect, useState } from "react";
import { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import * as Dialog from "@/components/dialog/Dialog";
import { Button } from "@/components/buttons/Button";
@@ -30,22 +30,7 @@ export const ConfirmModal = ({
onCancel,
}: ConfirmModalProps) => {
const { t } = useTranslation();
// Retain last content so it survives Radix's close animation.
type Snapshot = Pick<ConfirmModalProps, "title" | "description" | "confirmLabel" | "danger"> & {
cancelLabel: string;
};
const [snapshot, setSnapshot] = useState<Snapshot | null>(null);
const resolvedCancel = cancelLabel ?? t("common.cancel");
useEffect(() => {
if (open) {
setSnapshot({ title, description, confirmLabel, cancelLabel: resolvedCancel, danger });
}
}, [open, title, description, confirmLabel, resolvedCancel, danger]);
const view = open
? { title, description, confirmLabel, cancelLabel: resolvedCancel, danger }
: snapshot;
return (
<Dialog.Root
@@ -60,36 +45,29 @@ export const ConfirmModal = ({
className="py-5"
onOpenAutoFocus={(e) => e.preventDefault()}
>
{view && (
<div className="flex flex-col gap-5 px-5">
<div className="flex flex-col gap-1 pl-1">
<DialogHeading align={"left"}>{view.title}</DialogHeading>
<DialogDescription align={"left"} className={"whitespace-pre-line"}>
{view.description}
</DialogDescription>
</div>
<DialogActions className={"flex-row justify-end gap-2.5"}>
<Button
variant={"secondary"}
size={"xs2"}
disabled={busy}
onClick={onCancel}
>
{view.cancelLabel}
</Button>
<Button
autoFocus
variant={view.danger ? "danger" : "primary"}
size={"xs2"}
disabled={busy}
onClick={onConfirm}
>
{view.confirmLabel}
</Button>
</DialogActions>
<div className="flex flex-col gap-5 px-5">
<div className="flex flex-col gap-1 pl-1">
<DialogHeading align={"left"}>{title}</DialogHeading>
<DialogDescription align={"left"} className={"whitespace-pre-line"}>
{description}
</DialogDescription>
</div>
)}
<DialogActions className={"flex-row justify-end gap-2.5"}>
<Button variant={"secondary"} size={"xs2"} disabled={busy} onClick={onCancel}>
{resolvedCancel}
</Button>
<Button
autoFocus
variant={danger ? "danger" : "primary"}
size={"xs2"}
disabled={busy}
onClick={onConfirm}
>
{confirmLabel}
</Button>
</DialogActions>
</div>
</Dialog.Content>
</Dialog.Root>
);

View File

@@ -7,50 +7,61 @@ import { cn } from "@/lib/cn";
export const Root = DialogPrimitive.Root;
const Overlay = forwardRef<
ElementRef<typeof DialogPrimitive.Overlay>,
ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(function DialogOverlay({ className, ...props }, ref) {
return (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 grid items-center justify-items-center overflow-y-auto px-10 py-16",
"bg-black/60",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
"duration-150 ease-out",
className,
)}
{...props}
/>
);
});
type OverlayProps = ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & {
exitAnimation?: boolean;
};
const Overlay = forwardRef<ElementRef<typeof DialogPrimitive.Overlay>, OverlayProps>(
function DialogOverlay({ className, exitAnimation = false, ...props }, ref) {
return (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 grid items-center justify-items-center overflow-y-auto px-10 py-16",
"bg-black/60",
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
exitAnimation && "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
"duration-150 ease-out",
className,
)}
{...props}
/>
);
},
);
type ContentProps = ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
showClose?: boolean;
maxWidthClass?: string;
exitAnimation?: boolean;
};
export const Content = forwardRef<ElementRef<typeof DialogPrimitive.Content>, ContentProps>(
function DialogContent(
{ className, children, showClose = true, maxWidthClass = "max-w-md", ...props },
{
className,
children,
showClose = true,
maxWidthClass = "max-w-md",
exitAnimation = false,
...props
},
ref,
) {
const { t } = useTranslation();
return (
<DialogPrimitive.Portal>
<Overlay>
<Overlay exitAnimation={exitAnimation}>
<DialogPrimitive.Content
ref={ref}
className={cn(
"mx-auto relative z-[52] w-full outline-none ring-0",
"focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0",
"border border-nb-gray-900 bg-nb-gray py-7 shadow-2xl rounded-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
"data-[state=closed]:slide-out-to-left-1 data-[state=open]:slide-in-from-left-1",
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
"data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1",
exitAnimation &&
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:slide-out-to-left-1",
"duration-150 ease-out",
maxWidthClass,
className,