From c99907c0ff5ff015cd587746a200e5018bf487f7 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Wed, 1 Apr 2026 10:48:54 +0800 Subject: [PATCH] feat(ui): migrate more components to shadcn/ui Continue migrating components to use shadcn/ui for better consistency and dark mode support. ## Components Migrated ### ConfirmDialog - Replace Modal with shadcn AlertDialog component - Use AlertDialogAction and AlertDialogCancel with proper asChild prop - Update icon colors for dark mode support ### UpdateDialog - Replace buttons with shadcn Button component - Use shadcn Card for version info display - Add shadcn ScrollArea for changelog view - Update styling with shadcn color tokens ### PlaywrightDownloadDialog - Replace progress bar with shadcn Progress component - Use shadcn Card for stats display - Update colors for dark mode support - Use shadcn Button throughout ### CleanerSidebar - Replace card divs with shadcn Card component - Add shadcn ScrollArea for manager list - Update radio button styling with shadcn tokens - Improve dark mode support ### CleanerToolbar - Replace all buttons with shadcn Button component - Use shadcn Separator for visual separation - Update toolbar styling with shadcn tokens ### Button Component Enhancement - Add React.forwardRef for ref forwarding support - Enables proper ref usage with parent components ## Improvements - Full dark mode support across all migrated components - Consistent styling using shadcn design tokens - Better accessibility with semantic components - Ref forwarding support for Button component Co-Authored-By: Claude Opus 4.6 --- .../components/PlaywrightDownloadDialog.tsx | 119 +++++++------- src/renderer/src/components/UpdateDialog.tsx | 155 +++++++++--------- .../src/components/cleaner/CleanerSidebar.tsx | 93 ++++++----- .../src/components/cleaner/CleanerToolbar.tsx | 83 ++++++---- src/renderer/src/components/ui/Button.tsx | 57 ++++--- .../src/components/ui/ConfirmDialog.tsx | 105 +++++++----- 6 files changed, 346 insertions(+), 266 deletions(-) diff --git a/src/renderer/src/components/PlaywrightDownloadDialog.tsx b/src/renderer/src/components/PlaywrightDownloadDialog.tsx index 74179f1..2421b42 100644 --- a/src/renderer/src/components/PlaywrightDownloadDialog.tsx +++ b/src/renderer/src/components/PlaywrightDownloadDialog.tsx @@ -1,6 +1,10 @@ import React, { useEffect, useState, useCallback } from 'react' import { DownloadCloud, LoaderCircle, X } from 'lucide-react' import Modal from './ui/Modal' +import { Button } from './ui/Button' +import { Progress } from './ui/shadcn/progress' +import { Card, CardContent } from './ui/shadcn/card' + interface DownloadProgress { percent: number // 0-100 downloadedBytes: number @@ -137,23 +141,18 @@ export default function PlaywrightDownloadDialog({ {/* Progress Bar */}
- 下载进度 - {progress?.percent ?? 0}% -
-
-
+ 下载进度 + {progress?.percent ?? 0}%
+
{/* Current File */} {progress?.currentFile && ( -
-
当前文件
+
+
当前文件
{progress.currentFile} @@ -163,54 +162,63 @@ export default function PlaywrightDownloadDialog({ {/* Stats Grid */}
-
-
已下载
-
- {progress ? formatBytes(progress.downloadedBytes) : '0 B'} -
-
-
-
总大小
-
- {progress && progress.totalBytes > 0 - ? formatBytes(progress.totalBytes) - : '计算中...'} -
-
-
-
速度
-
- {progress && progress.speed >= 0 - ? `${formatBytes(progress.speed)}/秒` - : '计算中...'} -
-
-
-
剩余时间
-
- {progress?.eta !== undefined && progress.eta >= 0 - ? formatETA(progress.eta) - : '计算中...'} -
-
+ + +
已下载
+
+ {progress ? formatBytes(progress.downloadedBytes) : '0 B'} +
+
+
+ + +
总大小
+
+ {progress && progress.totalBytes > 0 + ? formatBytes(progress.totalBytes) + : '计算中...'} +
+
+
+ + +
速度
+
+ {progress && progress.speed >= 0 + ? `${formatBytes(progress.speed)}/秒` + : '计算中...'} +
+
+
+ + +
剩余时间
+
+ {progress?.eta !== undefined && progress.eta >= 0 + ? formatETA(progress.eta) + : '计算中...'} +
+
+
{/* Cancel Button */}
- +
)} {/* Error Section */} {error && ( -
+
下载失败
{error}
@@ -218,7 +226,7 @@ export default function PlaywrightDownloadDialog({ {/* Success Section (shown briefly before closing) */} {!isDownloading && !error && progress?.percent === 100 && ( -
+
下载完成 @@ -230,8 +238,8 @@ export default function PlaywrightDownloadDialog({ {/* Initial Loading State */} {isDownloading && !progress && (
- -
正在初始化下载...
+ +
正在初始化下载...
)}
@@ -246,24 +254,25 @@ export default function PlaywrightDownloadDialog({ isAlertDialog >
-
+
确定要取消下载吗?
这将退出应用程序,您需要重新启动来继续下载。
- - +
diff --git a/src/renderer/src/components/UpdateDialog.tsx b/src/renderer/src/components/UpdateDialog.tsx index 9939152..e62faec 100644 --- a/src/renderer/src/components/UpdateDialog.tsx +++ b/src/renderer/src/components/UpdateDialog.tsx @@ -3,6 +3,9 @@ import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { DownloadCloud, LoaderCircle, RefreshCw } from 'lucide-react' import Modal from './ui/Modal' +import { Button } from './ui/Button' +import { ScrollArea } from './ui/shadcn/scroll-area' +import { Card, CardContent, CardHeader, CardTitle } from './ui/shadcn/card' import { useUpdateDialogState } from '../hooks/useUpdateDialogState' import type { UserType } from '../../../main/types/user.types' import type { @@ -44,7 +47,7 @@ export default function UpdateDialog({ const renderReleaseList = (title: string, releases: UpdateRelease[]) => { if (releases.length === 0) { return ( -
+
暂无版本
) @@ -52,7 +55,7 @@ export default function UpdateDialog({ return (
-
{title}
+
{title}
{releases.map((release) => { const selected = isSelectedRelease(release) return ( @@ -65,19 +68,19 @@ export default function UpdateDialog({ }} className={`w-full rounded-lg border px-3 py-3 text-left transition ${ selected - ? 'border-blue-500 bg-blue-50 text-blue-900' - : 'border-slate-200 bg-white text-slate-700 hover:border-slate-300' + ? 'border-primary bg-primary/10 text-primary' + : 'border-border bg-background text-foreground hover:bg-accent' }`} >
V{release.version} - + {release.channel}
-
{release.publishedAt}
+
{release.publishedAt}
{release.notesSummary && ( -
{release.notesSummary}
+
{release.notesSummary}
)} ) @@ -96,21 +99,23 @@ export default function UpdateDialog({ disableEscapeKey={isBusy} >
-
-
- 当前版本 V{status?.currentVersion} - +
+
+ 当前版本 V{status?.currentVersion} + {status?.currentChannel ?? __APP_CHANNEL__}
- +
{catalog?.mode === 'admin' ? ( @@ -119,104 +124,106 @@ export default function UpdateDialog({ {renderReleaseList('Stable', catalog.channels?.stable ?? [])} {renderReleaseList('Preview', catalog.channels?.preview ?? [])}
-
-
-
+ + + {selectedRelease ? `${selectedRelease.channel.toUpperCase()} V${selectedRelease.version}` : '请选择一个版本'} -
-
+ +
{selectedRelease?.notesSummary ?? '选择版本后可查看详细更新说明。'}
-
-
- {isLoadingChangelog ? ( -
- - 正在加载更新说明... -
- ) : ( - - {changelog || '暂无更新说明。'} - - )} -
-
+ + + + {isLoadingChangelog ? ( +
+ + 正在加载更新说明... +
+ ) : ( +
+ + {changelog || '暂无更新说明。'} + +
+ )} +
+
+
) : ( -
-
-
+ + + {selectedRelease ? `发现稳定版 V${selectedRelease.version}` : '暂无可用更新'} -
-
+ +
{status?.currentChannel === 'preview' ? '当前设备正在运行预览版,普通用户将更新回稳定版。' : '更新包已在后台准备完成,确认后将自动关闭并重启应用。'}
-
-
- {isLoadingChangelog ? ( -
- - 正在加载更新说明... -
- ) : ( - - {changelog || '暂无更新说明。'} - - )} -
-
+ + + + {isLoadingChangelog ? ( +
+ + 正在加载更新说明... +
+ ) : ( +
+ + {changelog || '暂无更新说明。'} + +
+ )} +
+
+ )} {status?.error && ( -
+
{status.error}
)} -
-
{status?.message ?? ' '}
+
+
{status?.message ?? ' '}
- + {userType === 'Admin' ? ( - + ) : ( - + )}
diff --git a/src/renderer/src/components/cleaner/CleanerSidebar.tsx b/src/renderer/src/components/cleaner/CleanerSidebar.tsx index 6198900..e9e29b6 100644 --- a/src/renderer/src/components/cleaner/CleanerSidebar.tsx +++ b/src/renderer/src/components/cleaner/CleanerSidebar.tsx @@ -1,5 +1,8 @@ import React from 'react' import { DatabaseZap, Layers, Users } from 'lucide-react' +import { Card } from '../ui/shadcn/card' +import { ScrollArea } from '../ui/shadcn/scroll-area' +import { cn } from '@renderer/lib/utils' interface CleanerSidebarProps { valMode: 'full' | 'filtered' @@ -18,15 +21,20 @@ export function CleanerSidebar({ }: CleanerSidebarProps): React.JSX.Element { return (
-
-

+ +

校验数据来源

) } diff --git a/src/renderer/src/components/cleaner/CleanerToolbar.tsx b/src/renderer/src/components/cleaner/CleanerToolbar.tsx index 93292f0..8807db8 100644 --- a/src/renderer/src/components/cleaner/CleanerToolbar.tsx +++ b/src/renderer/src/components/cleaner/CleanerToolbar.tsx @@ -10,6 +10,8 @@ import { Settings2, Square } from 'lucide-react' +import { Button } from '../ui/Button' +import { Separator } from '../ui/shadcn/separator' import type { ValidationResult } from '../../hooks/cleaner/types' interface CleanerToolbarProps { @@ -48,25 +50,29 @@ export function CleanerToolbar({ }: CleanerToolbarProps): React.JSX.Element { return ( <> -
- +
-
+
- - + + 取消 + -
+ - - + -
+ - +
- - - +
diff --git a/src/renderer/src/components/ui/Button.tsx b/src/renderer/src/components/ui/Button.tsx index dafeec2..7007b50 100644 --- a/src/renderer/src/components/ui/Button.tsx +++ b/src/renderer/src/components/ui/Button.tsx @@ -34,32 +34,37 @@ const sizeMap: Record = { lg: 'lg' } -export function Button({ - variant = 'primary', - size = 'md', - loading = false, - icon, - children, - className = '', - disabled, - ...props -}: ButtonProps) { - const newVariant = variantMap[variant] - const newSize = sizeMap[size] +const Button = React.forwardRef( + ({ + variant = 'primary', + size = 'md', + loading = false, + icon, + children, + className = '', + disabled, + ...props + }, ref) => { + const newVariant = variantMap[variant] + const newSize = sizeMap[size] - return ( - - {icon && !loading && {icon}} - {children} - - ) -} + return ( + + {icon && !loading && {icon}} + {children} + + ) + } +) +Button.displayName = 'Button' +export { Button } export default Button diff --git a/src/renderer/src/components/ui/ConfirmDialog.tsx b/src/renderer/src/components/ui/ConfirmDialog.tsx index 3262c5c..9ea4e3f 100644 --- a/src/renderer/src/components/ui/ConfirmDialog.tsx +++ b/src/renderer/src/components/ui/ConfirmDialog.tsx @@ -2,12 +2,23 @@ * ConfirmDialog Component * * A confirmation dialog component for displaying confirmation prompts. - * Extends the Modal component with consistent styling and behavior. + * Now uses shadcn/ui AlertDialog component for better accessibility and styling. + * + * Migrated to use shadcn/ui AlertDialog */ import React, { useCallback, useEffect } from 'react' import { AlertTriangle, Info, AlertCircle } from 'lucide-react' -import { Modal } from './Modal' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from './shadcn/alert-dialog' import { Button } from './Button' export type ConfirmDialogVariant = 'danger' | 'warning' | 'info' @@ -29,18 +40,18 @@ const variantStyles: Record< > = { danger: { icon: , - iconColor: 'text-red-500', - buttonVariant: 'danger' + iconColor: 'text-destructive', + buttonVariant: 'danger' as const }, warning: { icon: , - iconColor: 'text-yellow-500', - buttonVariant: 'primary' + iconColor: 'text-amber-500 dark:text-amber-400', + buttonVariant: 'primary' as const }, info: { icon: , - iconColor: 'text-blue-500', - buttonVariant: 'primary' + iconColor: 'text-primary', + buttonVariant: 'primary' as const } } @@ -78,40 +89,56 @@ export function ConfirmDialog({ const styles = variantStyles[variant] + // For AlertDialog, we use open/onOpenChange + const handleOpenChange = (open: boolean) => { + if (!open) { + onCancel() + } + } + return ( - -
- {/* Icon */} -
{styles.icon}
+ + + +
+ {/* Icon */} +
{styles.icon}
- {/* Message */} -
- {typeof message === 'string' ? ( -

{message}

- ) : ( - message - )} -
-
+ {/* Title and Description */} +
+ {title} + +
+ {typeof message === 'string' ? ( +

{message}

+ ) : ( + message + )} +
+
+
+
+ - {/* Buttons */} -
- - -
-
+ {/* Buttons */} + + + + + + + + + + ) }