mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
- Upgrade CSS variables with proper glass transparency, fixed gradient backgrounds, cubic-bezier(0.2, 0.8, 0.2, 1) fluid transitions, and enhanced shadow/border values matching the Liquid Glass touchstone - Add full backdrop-filter: blur(25px) saturate(180%) to glass-card, glass-input, glass-popover, and glass-backdrop CSS classes - Add lensing inner-glow hover effects and toast-in animation - Update all UI components (Button, Card, Input, Badge, Switch, SegmentedControl, ConfirmDialog, ModalBackdrop, ModalHeader, BackToTop, Navbar) for strict Liquid Glass compliance - Enforce rounded-2xl / rounded-full consistency across all components - Bump version to 4.6.0, update @vercel/analytics to ^2.0.1 Co-Authored-By: Claude Opus 4.6 <[email protected]>
115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import { Button } from './Button';
|
|
import { Card } from './Card';
|
|
|
|
interface ConfirmDialogProps {
|
|
isOpen: boolean;
|
|
title: string;
|
|
message: string;
|
|
onConfirm: () => void;
|
|
onCancel: () => void;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
variant?: 'danger' | 'warning' | 'info';
|
|
dangerous?: boolean;
|
|
}
|
|
|
|
export function ConfirmDialog({
|
|
isOpen,
|
|
title,
|
|
message,
|
|
onConfirm,
|
|
onCancel,
|
|
confirmText = '确认',
|
|
cancelText = '取消',
|
|
variant = 'warning',
|
|
dangerous = false,
|
|
}: ConfirmDialogProps) {
|
|
const cancelButtonRef = useRef<HTMLButtonElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
cancelButtonRef.current?.focus();
|
|
document.body.style.overflow = 'hidden';
|
|
} else {
|
|
document.body.style.overflow = '';
|
|
}
|
|
return () => { document.body.style.overflow = ''; };
|
|
}, [isOpen]);
|
|
|
|
useEffect(() => {
|
|
if (!isOpen) return;
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
onCancel();
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
}, [isOpen, onCancel]);
|
|
|
|
if (!isOpen) return null;
|
|
|
|
const variantStyles = {
|
|
danger: 'bg-red-500 hover:bg-red-600',
|
|
warning: 'bg-[var(--accent-color)] hover:brightness-110',
|
|
info: 'bg-blue-500 hover:bg-blue-600',
|
|
};
|
|
|
|
const finalVariant = dangerous ? 'danger' : variant;
|
|
|
|
return (
|
|
<>
|
|
{/* Backdrop */}
|
|
<div
|
|
className="fixed inset-0 z-[9998] bg-black/30 backdrop-blur-[8px] [-webkit-backdrop-filter:blur(8px)] animate-fade-in"
|
|
onClick={onCancel}
|
|
aria-hidden="true"
|
|
/>
|
|
|
|
{/* Dialog */}
|
|
<div
|
|
role="alertdialog"
|
|
aria-modal="true"
|
|
aria-labelledby="dialog-title"
|
|
aria-describedby="dialog-description"
|
|
className="fixed top-1/2 left-1/2 z-[9999] w-[90%] max-w-md -translate-x-1/2 -translate-y-1/2 animate-scale-in"
|
|
>
|
|
<Card hover={false} className="p-6">
|
|
<h2
|
|
id="dialog-title"
|
|
className="text-xl font-semibold text-[var(--text-color)] mb-3"
|
|
>
|
|
{title}
|
|
</h2>
|
|
<p
|
|
id="dialog-description"
|
|
className="text-[var(--text-color-secondary)] mb-6 leading-relaxed"
|
|
>
|
|
{message}
|
|
</p>
|
|
<div className="flex gap-3 justify-end">
|
|
<Button
|
|
ref={cancelButtonRef}
|
|
variant="secondary"
|
|
onClick={onCancel}
|
|
className="min-w-[100px]"
|
|
>
|
|
{cancelText}
|
|
</Button>
|
|
<Button
|
|
onClick={onConfirm}
|
|
className={`min-w-[100px] ${variantStyles[finalVariant]}`}
|
|
>
|
|
{confirmText}
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|