fix: button confirm

This commit is contained in:
Théo LAGACHE
2026-03-02 11:17:54 +01:00
parent 7028982ba1
commit ec02dc8579
@@ -1,161 +1,198 @@
"use client" "use client";
import {Button, ButtonVariantsProps} from "@/components/ui/button"; import { Button, ButtonVariantsProps } from "@/components/ui/button";
import {ReactNode, useState} from "react"; import { ReactNode, useState } from "react";
import {Loader2} from "lucide-react"; import { Loader2 } from "lucide-react";
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover"; import {
import {cn} from "@/lib/utils"; Popover,
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip"; PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
export type ButtonWithConfirmProps = { export type ButtonWithConfirmProps = {
title: string; title: string;
description: string; description: string;
button?: { button?: {
main: { main: {
className?: string; className?: string;
type?: "button" | "submit" | "reset" | undefined; type?: "button" | "submit" | "reset" | undefined;
text?: string; text?: string;
icon?: any; icon?: any;
variant?: ButtonVariantsProps["variant"]; variant?: ButtonVariantsProps["variant"];
size?: ButtonVariantsProps["size"]; size?: ButtonVariantsProps["size"];
disabled?: boolean; disabled?: boolean;
tooltipText?: string; tooltipText?: string;
};
confirm: {
className?: string;
text: string;
icon?: any;
variant?: ButtonVariantsProps["variant"];
size?: ButtonVariantsProps["size"];
onClick?: () => void;
};
cancel: {
className?: string;
text: string;
icon?: any;
variant?: ButtonVariantsProps["variant"];
size?: ButtonVariantsProps["size"];
onClick?: () => void;
};
}; };
children?: ReactNode; confirm: {
onConfirm?: (e: React.MouseEvent) => void; className?: string;
onCancel?: (e: React.MouseEvent) => void; text: string;
confirmButtonText?: string; icon?: any;
cancelButtonText?: string; variant?: ButtonVariantsProps["variant"];
isPending?: boolean; size?: ButtonVariantsProps["size"];
onClick?: () => void;
};
cancel: {
className?: string;
text: string;
icon?: any;
variant?: ButtonVariantsProps["variant"];
size?: ButtonVariantsProps["size"];
onClick?: () => void;
};
};
children?: ReactNode;
onConfirm?: (e: React.MouseEvent) => void;
onCancel?: (e: React.MouseEvent) => void;
confirmButtonText?: string;
cancelButtonText?: string;
isPending?: boolean;
}; };
export const ButtonWithConfirm = (props: ButtonWithConfirmProps) => { export const ButtonWithConfirm = (props: ButtonWithConfirmProps) => {
const [isConfirming, setIsConfirming] = useState(false); const [isConfirming, setIsConfirming] = useState(false);
const isLegacy = !!props.button; const isLegacy = !!props.button;
const isDisabled = isLegacy ? !!props.button?.main.disabled : false; const isDisabled = isLegacy ? !!props.button?.main.disabled : false;
const handleConfirm = (e: React.MouseEvent) => { const handleConfirm = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
if (isLegacy) {
props.button?.confirm.onClick?.();
} else {
props.onConfirm?.(e);
}
setIsConfirming(false);
};
const handleCancel = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
if (isLegacy) {
props.button?.cancel.onClick?.();
} else {
props.onCancel?.(e);
}
setIsConfirming(false);
};
const triggerContent = isLegacy ? (
<Button
type={props.button?.main.type}
disabled={isDisabled}
variant={props.button?.main.variant ?? "default"}
size={props.button?.main.size ?? "default"}
className={props.button?.main.className}
onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (isLegacy) { if (!isDisabled) setIsConfirming(true);
props.button?.confirm.onClick?.(); }}
} else { >
props.onConfirm?.(e); {props.isPending && <Loader2 className="animate-spin mr-4" size={16} />}
} {props.button?.main.icon}
setIsConfirming(false); {props.button?.main.text && <span>{props.button.main.text}</span>}
}; </Button>
) : (
const handleCancel = (e: React.MouseEvent) => { <div
onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (isLegacy) { setIsConfirming(true);
props.button?.cancel.onClick?.(); }}
} else { >
props.onCancel?.(e); {props.children}
} </div>
setIsConfirming(false); );
};
const triggerContent = isLegacy ? ( const withTooltip =
<Button isLegacy && props.button?.main.tooltipText && isDisabled ? (
type={props.button?.main.type} <TooltipProvider>
disabled={isDisabled} <Tooltip>
variant={props.button?.main.variant ?? "default"} <TooltipTrigger asChild>
size={props.button?.main.size ?? "default"} <div>{triggerContent}</div>
className={props.button?.main.className} </TooltipTrigger>
onClick={(e) => { <TooltipContent>
e.preventDefault(); <p>{props.button?.main.tooltipText}</p>
e.stopPropagation(); </TooltipContent>
if (!isDisabled) setIsConfirming(true); </Tooltip>
}} </TooltipProvider>
>
{props.isPending && <Loader2 className="animate-spin mr-4" size={16}/>}
{props.button?.main.icon}
{props.button?.main.text && <span>{props.button.main.text}</span>}
</Button>
) : ( ) : (
<div onClick={(e) => { triggerContent
e.preventDefault();
e.stopPropagation();
setIsConfirming(true);
}}>
{props.children}
</div>
); );
const withTooltip = isLegacy && props.button?.main.tooltipText && isDisabled ? ( return (
<TooltipProvider> <Popover open={isConfirming} onOpenChange={setIsConfirming}>
<Tooltip> <PopoverTrigger asChild>{withTooltip}</PopoverTrigger>
<TooltipTrigger asChild> <PopoverContent
<div>{triggerContent}</div> className="w-80"
</TooltipTrigger> onPointerDownOutside={(e) => e.preventDefault()}
<TooltipContent> onInteractOutside={(e) => e.preventDefault()}
<p>{props.button?.main.tooltipText}</p> onClick={(e) => e.stopPropagation()}
</TooltipContent> onMouseMove={(e) => e.stopPropagation()}
</Tooltip> onMouseEnter={(e) => e.stopPropagation()}
</TooltipProvider> onMouseLeave={(e) => e.stopPropagation()}
) : triggerContent; onOpenAutoFocus={(e) => e.preventDefault()}
>
return ( <div className="grid gap-4">
<Popover open={isConfirming} onOpenChange={setIsConfirming}> <div className="space-y-2">
<PopoverTrigger asChild> <h4 className="font-medium leading-none">{props.title}</h4>
{withTooltip} <p className="text-sm text-muted-foreground">{props.description}</p>
</PopoverTrigger> </div>
<PopoverContent <div className="grid gap-2">
className="w-80" <Button
onPointerDownOutside={(e) => e.preventDefault()} onClick={handleConfirm}
onInteractOutside={(e) => e.preventDefault()} variant={
onClick={(e) => e.stopPropagation()} isLegacy
onMouseMove={(e) => e.stopPropagation()} ? (props.button?.confirm.variant ?? "default")
onMouseEnter={(e) => e.stopPropagation()} : "default"
onMouseLeave={(e) => e.stopPropagation()} }
onOpenAutoFocus={(e) => e.preventDefault()} size={
isLegacy ? (props.button?.confirm.size ?? "default") : "default"
}
className={cn(
isLegacy ? props.button?.confirm.className : "",
"w-full",
)}
> >
<div className="grid gap-4"> {props.isPending && (
<div className="space-y-2"> <Loader2 className="animate-spin mr-4" size={16} />
<h4 className="font-medium leading-none">{props.title}</h4> )}
<p className="text-sm text-muted-foreground">{props.description}</p> {isLegacy && props.button?.confirm.icon}
</div> <span>
<div className="grid gap-2"> {isLegacy
<Button ? props.button?.confirm.text
onClick={handleConfirm} : (props.confirmButtonText ?? "Confirm")}
variant={isLegacy ? (props.button?.confirm.variant ?? "default") : "default"} </span>
size={isLegacy ? (props.button?.main.size ?? "default") : "default"} </Button>
className={cn(isLegacy ? props.button?.main.className : "", "w-full")} <Button
> variant={
{props.isPending && <Loader2 className="animate-spin mr-4" size={16}/>} isLegacy
{isLegacy && props.button?.confirm.icon} ? (props.button?.cancel.variant ?? "outline")
<span>{isLegacy ? props.button?.confirm.text : (props.confirmButtonText ?? "Confirm")}</span> : "outline"
</Button> }
<Button onClick={handleCancel}
variant={isLegacy ? (props.button?.cancel.variant ?? "outline") : "outline"} className={cn(
onClick={handleCancel} isLegacy ? props.button?.cancel.className : "",
className={cn(isLegacy ? props.button?.main.className : "", "w-full")} "w-full",
size={isLegacy ? (props.button?.main.size ?? "default") : "default"} )}
> size={
{isLegacy ? (props.button?.cancel.text ?? "Cancel") : (props.cancelButtonText ?? "Cancel")} isLegacy ? (props.button?.cancel.size ?? "default") : "default"
</Button> }
</div> >
</div> {isLegacy
</PopoverContent> ? (props.button?.cancel.text ?? "Cancel")
</Popover> : (props.cancelButtonText ?? "Cancel")}
); </Button>
</div>
</div>
</PopoverContent>
</Popover>
);
}; };