feat: add ConversationDeleteDialog and ConversationRenameDialog components with integration in HomePage

This commit is contained in:
duanshuwen
2026-04-19 17:00:09 +08:00
parent ab6f179ab0
commit 2cedc1c234
4 changed files with 308 additions and 19 deletions

View File

@@ -0,0 +1,112 @@
import * as Dialog from '@radix-ui/react-dialog';
import { Loader2, Trash2, X } from 'lucide-react';
type ConversationDeleteDialogProps = {
open: boolean;
busy: boolean;
conversationTitle: string;
onClose: () => void;
onConfirm: () => void;
};
export default function ConversationDeleteDialog({
open,
busy,
conversationTitle,
onClose,
onConfirm,
}: ConversationDeleteDialogProps) {
function handleOpenChange(nextOpen: boolean) {
if (!nextOpen && !busy) {
onClose();
}
}
return (
<Dialog.Root open={open} onOpenChange={handleOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/45 backdrop-blur-[2px]" />
<Dialog.Content
className="fixed left-1/2 top-1/2 z-60 w-[calc(100vw-32px)] max-w-140 -translate-x-1/2 -translate-y-1/2 rounded-3xl bg-[#F4F3EB] p-0 shadow-[0_30px_80px_rgba(15,23,42,0.18)] outline-none dark:bg-[#1f1f22]"
onEscapeKeyDown={(event) => {
if (busy) {
event.preventDefault();
}
}}
onPointerDownOutside={(event) => {
if (busy) {
event.preventDefault();
}
}}
>
<div className="flex items-start justify-between gap-4 border-b border-black/6 px-6 py-5 dark:border-white/6">
<div className="flex min-w-0 items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-red-500/15 bg-red-500/10 text-red-600 dark:border-red-400/20 dark:bg-red-500/10 dark:text-red-300">
<Trash2 className="h-5 w-5" />
</div>
<div className="min-w-0">
<Dialog.Title
className="text-[26px] font-normal leading-none tracking-tight text-[#171717] dark:text-[#f3f4f6]"
style={{ fontFamily: "Georgia, Cambria, 'Times New Roman', Times, serif" }}
>
</Dialog.Title>
<Dialog.Description className="mt-3 text-[14px] leading-6 text-[#525866] dark:text-gray-400">
</Dialog.Description>
</div>
</div>
<button
type="button"
className="rounded-full p-1.5 text-[#99A0AE] transition-colors hover:text-[#171717] disabled:cursor-not-allowed disabled:opacity-50 dark:hover:text-[#f3f4f6]"
onClick={onClose}
disabled={busy}
aria-label="关闭弹窗"
>
<X className="h-5 w-5" />
</button>
</div>
<div className="px-6 py-5">
<div className="rounded-[18px] border border-black/6 bg-white/75 px-4 py-3 shadow-[0_10px_30px_rgba(15,23,42,0.04)] dark:border-white/8 dark:bg-[#202024]">
<div className="text-[12px] uppercase tracking-[0.14em] text-[#99A0AE] dark:text-gray-500">
</div>
<div className="mt-2 truncate text-[15px] font-medium text-[#171717] dark:text-[#f3f4f6]">
{conversationTitle}
</div>
</div>
<div className="mt-5 flex items-center justify-end gap-3">
<button
type="button"
className="inline-flex h-10 items-center rounded-full border border-black/10 px-4 text-[13px] font-medium text-[#171717]/80 transition-colors hover:bg-black/5 hover:text-[#171717] disabled:cursor-not-allowed disabled:opacity-60 dark:border-gray-700 dark:text-gray-300 dark:hover:bg-white/5 dark:hover:text-[#f3f4f6]"
onClick={onClose}
disabled={busy}
>
</button>
<button
type="button"
className="inline-flex h-10 items-center rounded-full bg-red-600 px-4 text-[13px] font-medium text-white transition-colors hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-red-500 dark:hover:bg-red-400"
onClick={onConfirm}
disabled={busy}
>
{busy ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
'确认删除'
)}
</button>
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@@ -0,0 +1,107 @@
import * as Dialog from '@radix-ui/react-dialog';
import { PencilLine, X } from 'lucide-react';
type ConversationRenameDialogProps = {
open: boolean;
value: string;
onValueChange: (value: string) => void;
onClose: () => void;
onConfirm: () => void;
};
export default function ConversationRenameDialog({
open,
value,
onValueChange,
onClose,
onConfirm,
}: ConversationRenameDialogProps) {
const trimmedValue = value.trim();
return (
<Dialog.Root open={open} onOpenChange={(nextOpen) => {
if (!nextOpen) {
onClose();
}
}}
>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/45 backdrop-blur-[2px]" />
<Dialog.Content
className="fixed left-1/2 top-1/2 z-60 w-[calc(100vw-32px)] max-w-140 -translate-x-1/2 -translate-y-1/2 rounded-2xl bg-white p-0 shadow-[0_30px_80px_rgba(15,23,42,0.18)] outline-none dark:bg-[#1f1f22]"
>
<div className="flex items-start justify-between gap-4 border-b border-black/6 px-6 py-5 dark:border-white/6">
<div className="flex min-w-0 items-start gap-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl border border-[#8ea8ff]/20 bg-[#8ea8ff]/10 text-[#5b7cff] dark:border-[#8ea8ff]/20 dark:bg-[#8ea8ff]/10 dark:text-[#a9bbff]">
<PencilLine className="h-5 w-5" />
</div>
<div className="min-w-0">
<Dialog.Title
className="text-[26px] font-normal leading-none tracking-tight text-[#171717] dark:text-[#f3f4f6]"
style={{ fontFamily: "Georgia, Cambria, 'Times New Roman', Times, serif" }}
>
</Dialog.Title>
<Dialog.Description className="mt-3 text-[14px] leading-6 text-[#525866] dark:text-gray-400">
</Dialog.Description>
</div>
</div>
<button
type="button"
className="rounded-full p-1.5 text-[#99A0AE] transition-colors hover:text-[#171717] dark:hover:text-[#f3f4f6]"
onClick={onClose}
aria-label="关闭弹窗"
>
<X className="h-5 w-5" />
</button>
</div>
<form
className="px-6 py-5"
onSubmit={(event) => {
event.preventDefault();
if (!trimmedValue) return;
onConfirm();
}}
>
<div>
<label className="block text-[13px] font-medium text-[#525866] dark:text-gray-300">
</label>
<input
autoFocus
value={value}
onChange={(event) => onValueChange(event.target.value)}
placeholder="输入新的对话名称"
className="mt-3 h-12 w-full rounded-[16px] border border-black/10 bg-white/80 px-4 text-[14px] text-[#171717] outline-none transition-colors placeholder:text-[#99A0AE] focus:border-[#8ea8ff] dark:border-white/10 dark:bg-[#202024] dark:text-[#f3f4f6] dark:placeholder:text-gray-500"
/>
<div className="mt-3 text-[12px] leading-6 text-[#99A0AE] dark:text-gray-500">
</div>
</div>
<div className="mt-5 flex items-center justify-end gap-3">
<button
type="button"
className="inline-flex h-10 items-center rounded-full border border-black/10 px-4 text-[13px] font-medium text-[#171717]/80 transition-colors hover:bg-black/5 hover:text-[#171717] dark:border-gray-700 dark:text-gray-300 dark:hover:bg-white/5 dark:hover:text-[#f3f4f6]"
onClick={onClose}
>
</button>
<button
type="submit"
className="inline-flex h-10 items-center rounded-full bg-[#8ea8ff] px-4 text-[13px] font-medium text-white transition-colors hover:bg-[#7f9afb] disabled:cursor-not-allowed disabled:opacity-60"
disabled={!trimmedValue}
>
</button>
</div>
</form>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@@ -1,2 +1,4 @@
export { default as AddChannelDialog } from './AddChannelDialog';
export { default as ConversationDeleteDialog } from './ConversationDeleteDialog';
export { default as ConversationRenameDialog } from './ConversationRenameDialog';
export { default as TaskOperationDialog } from './TaskOperationDialog';