mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: button confirm
This commit is contained in:
@@ -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>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user