From eb47068421048d136e488a2c56b9337a981fa4f9 Mon Sep 17 00:00:00 2001 From: AmintaCCCP Date: Sun, 26 Apr 2026 17:10:28 +0800 Subject: [PATCH] fix: use React Portal for update dialog and fix banner layout issues - UpdateChecker: wrap the update dialog with ReactDOM.createPortal to render at document.body level, avoiding the fixed overlay being constrained by overflow containers in the Settings scroll area. Also use bg-black/50 syntax and z-[9999] for reliable stacking. Clicking the mask backdrop now dismisses the dialog. - UpdateNotificationBanner: fix duplicate/invalid Tailwind classes (dark:bg-brand-indigo/20/20, duplicate dark:bg-white/[0.04] etc.); add gap-3, min-w-0, flex-shrink-0, flex-wrap, and line-clamp-1 to prevent the version/date row and the "Download" button from wrapping on narrow viewports. --- src/components/UpdateChecker.tsx | 119 +++++++++++--------- src/components/UpdateNotificationBanner.tsx | 24 ++-- 2 files changed, 75 insertions(+), 68 deletions(-) diff --git a/src/components/UpdateChecker.tsx b/src/components/UpdateChecker.tsx index 1382a16..85015c8 100644 --- a/src/components/UpdateChecker.tsx +++ b/src/components/UpdateChecker.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import ReactDOM from 'react-dom'; import { Download, RefreshCw, ExternalLink, Calendar, Package } from 'lucide-react'; import { UpdateService, VersionInfo } from '../services/updateService'; import { useAppStore } from '../store/useAppStore'; @@ -95,69 +96,75 @@ export const UpdateChecker: React.FC = ({ onUpdateAvailable )} - {/* 更新对话框 */} - {showUpdateDialog && updateInfo && ( -
-
-
- {/* 标题 */} -
-
- + {/* 更新对话框 - 使用 Portal 渲染到 body 以避免被 overflow 容器遮挡 */} + {showUpdateDialog && updateInfo && + ReactDOM.createPortal( +
{ if (e.target === e.currentTarget) setShowUpdateDialog(false); }} + > +
+
+ {/* 标题 */} +
+
+ +
+
+

+ {t('发现新版本', 'New Version Available')} +

+

+ v{updateInfo.number} +

+
-
-

- {t('发现新版本', 'New Version Available')} -

-

- v{updateInfo.number} -

-
-
- {/* 版本信息 */} -
-
- - {t('发布日期:', 'Release Date:')} {formatDate(updateInfo.releaseDate)} -
- - {/* 更新日志 */} + {/* 版本信息 */}
-

- {t('更新内容:', 'What\'s New:')} -

-
    - {updateInfo.changelog.map((item, index) => ( -
  • - - {item} -
  • - ))} -
+
+ + {t('发布日期:', 'Release Date:')} {formatDate(updateInfo.releaseDate)} +
+ + {/* 更新日志 */} +
+

+ {t('更新内容:', 'What\'s New:')} +

+
    + {updateInfo.changelog.map((item, index) => ( +
  • + + {item} +
  • + ))} +
+
-
- {/* 按钮 */} -
- - + {/* 按钮 */} +
+ + +
-
-
- )} +
, + document.body + ) + } ); }; diff --git a/src/components/UpdateNotificationBanner.tsx b/src/components/UpdateNotificationBanner.tsx index e40abf2..888772a 100644 --- a/src/components/UpdateNotificationBanner.tsx +++ b/src/components/UpdateNotificationBanner.tsx @@ -26,41 +26,41 @@ export const UpdateNotificationBanner: React.FC = () => { }; return ( -
+
-
-
-
+
+
+
-
-
+
+

{t('发现新版本', 'New Version Available')} v{updateNotification.version}

- + {formatDate(updateNotification.releaseDate)}
-

+

{updateNotification.changelog.slice(0, 2).join(' • ')} {updateNotification.changelog.length > 2 && '...'}

- -
+ +