From bcef981955db69f9465f92e0c9bd177a391ee311 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:11:53 +0900 Subject: [PATCH 1/2] [Update] Change the notification UI. (Change the base notification library from MUI to React-Toastify.) --- package-lock.json | 14 ++ package.json | 1 + .../app/error_boundary/AppErrorBoundary.jsx | 1 + .../AppErrorBoundary.module.scss | 1 + .../ReactToastifyOverrideClass.scss | 109 +++++++++++++++ .../SnackbarController.jsx | 130 +++++++++++++----- .../SnackbarController.module.scss | 94 +++++++++++-- .../app/splash_component/SplashComponent.jsx | 2 +- .../SplashComponent.module.scss | 2 +- src-ui/assets/error.svg | 1 + src-ui/logics/common/useNotificationStatus.js | 3 +- 11 files changed, 308 insertions(+), 50 deletions(-) create mode 100644 src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss create mode 100644 src-ui/assets/error.svg diff --git a/package-lock.json b/package-lock.json index f131d4a8..cbe6e142 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "react-error-boundary": "5.0.0", "react-i18next": "15.5.1", "react-resizable-layout": "0.7.2", + "react-toastify": "11.0.5", "sass": "1.79.4", "semver": "7.7.1" }, @@ -5534,6 +5535,19 @@ "react-dom": ">=17.0.0" } }, + "node_modules/react-toastify": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", + "integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index a2d9030f..c15471e5 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "react-error-boundary": "5.0.0", "react-i18next": "15.5.1", "react-resizable-layout": "0.7.2", + "react-toastify": "11.0.5", "sass": "1.79.4", "semver": "7.7.1" }, diff --git a/src-ui/app/error_boundary/AppErrorBoundary.jsx b/src-ui/app/error_boundary/AppErrorBoundary.jsx index 4aeb34a2..37a1cc23 100644 --- a/src-ui/app/error_boundary/AppErrorBoundary.jsx +++ b/src-ui/app/error_boundary/AppErrorBoundary.jsx @@ -65,6 +65,7 @@ const ErrorContainer = ({error}) => { ); }; +// Duplicated const CloseButtonContainer = () => { const { asyncCloseApp } = useWindow(); return ( diff --git a/src-ui/app/error_boundary/AppErrorBoundary.module.scss b/src-ui/app/error_boundary/AppErrorBoundary.module.scss index 2abb2710..2ff85390 100644 --- a/src-ui/app/error_boundary/AppErrorBoundary.module.scss +++ b/src-ui/app/error_boundary/AppErrorBoundary.module.scss @@ -72,6 +72,7 @@ +// Duplicated .close_button_wrapper { position: absolute; top: 0; diff --git a/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss b/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss new file mode 100644 index 00000000..9325d747 --- /dev/null +++ b/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss @@ -0,0 +1,109 @@ +:root { + --toastify-color-light: #fff; + --toastify-color-dark: var(--dark_950_color); + --toastify-color-info: var(--sent_400_color); + --toastify-color-success: var(--primary_400_color); + --toastify-color-warning: var(--waring_bc_color); + --toastify-color-error: var(--error_bc_color); + --toastify-color-transparent: rgba(255, 255, 255, 0.7); + + --toastify-icon-color-info: var(--toastify-color-info); + --toastify-icon-color-success: var(--toastify-color-success); + --toastify-icon-color-warning: var(--toastify-color-warning); + --toastify-icon-color-error: var(--toastify-color-error); + + --toastify-container-width: fit-content; + --toastify-toast-width: 32rem; + --toastify-toast-offset: 1.6rem; + --toastify-toast-top: max(var(--toastify-toast-offset), env(safe-area-inset-top)); + --toastify-toast-right: max(var(--toastify-toast-offset), env(safe-area-inset-right)); + --toastify-toast-left: max(var(--toastify-toast-offset), env(safe-area-inset-left)); + --toastify-toast-bottom: max(var(--toastify-toast-offset), env(safe-area-inset-bottom)); + --toastify-toast-background: #fff; + --toastify-toast-padding: 1.4rem; + --toastify-toast-min-height: 6.4rem; + --toastify-toast-max-height: 80rem; + --toastify-toast-bd-radius: 0.6rem; + --toastify-toast-shadow: .0 0.4rem 1.2rem rgba(0, 0, 0, 0.1); + --toastify-font-family: var(--font_family); + --toastify-z-index: 9999; + --toastify-text-color-light: #757575; + --toastify-text-color-dark: var(--dark_basic_text_color); + + /* Used only for colored theme */ + --toastify-text-color-info: var(--dark_basic_text_color); + --toastify-text-color-success: var(--dark_basic_text_color); + --toastify-text-color-warning: var(--dark_basic_text_color); + --toastify-text-color-error: var(--dark_basic_text_color); + + --toastify-spinner-color: #616161; + --toastify-spinner-color-empty-area: #e0e0e0; + --toastify-color-progress-light: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55); + --toastify-color-progress-dark: #bb86fc; + --toastify-color-progress-info: var(--toastify-color-info); + --toastify-color-progress-success: var(--toastify-color-success); + --toastify-color-progress-warning: var(--toastify-color-warning); + --toastify-color-progress-error: var(--toastify-color-error); + /* used to control the opacity of the progress trail */ + --toastify-color-progress-bgo: 0.2; +} + + +.Toastify__toast { + // -------------------------------------------------------- + // Default Settings + // -------------------------------------------------------- + position: relative; + touch-action: none; + // width: var(--toastify-toast-width); + min-height: var(--toastify-toast-min-height); + box-sizing: border-box; + margin-bottom: 1rem; + // padding: var(--toastify-toast-padding); + border-radius: 0.6rem; + box-shadow: none; + max-height: var(--toastify-toast-max-height); + // font-family: "Yu Gothic UI"; + // font-family: var(--toastify-font-family); + // z-index: 0; + // display: flex; + // flex: 1 auto; + // align-items: center; + word-break: break-word; + // -------------------------------------------------------- + // -------------------------------------------------------- + + + // Comment out above and override. Commented out is just for memorization. + overflow: hidden; + display: flex; + justify-content: start; + align-items: center; + font-size: 1.4rem; + width: fit-content; + max-width: 50vw; + padding-right: 4rem; + background-color: var(--dark_950_color); + gap: 0.6rem; +} + +.Toastify__progress-bar--success { + background: var(--success_bc_color); +} + +.Toastify__progress-bar--warning { + background: var(--warning_bc_color); +} + +.Toastify__progress-bar--error { + background: var(--error_bc_color); +} + + +.Toastify__toast-icon { + width: fit-content; + max-width: 2.8rem; + min-width: 2.8rem; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/src-ui/app/snackbar_controller/SnackbarController.jsx b/src-ui/app/snackbar_controller/SnackbarController.jsx index e503e695..28bd6325 100644 --- a/src-ui/app/snackbar_controller/SnackbarController.jsx +++ b/src-ui/app/snackbar_controller/SnackbarController.jsx @@ -1,46 +1,114 @@ +import React, { useEffect, useState } from "react"; +import { ToastContainer, toast, Bounce } from "react-toastify"; import clsx from "clsx"; -import Snackbar from "@mui/material/Snackbar"; -import Slide from "@mui/material/Slide"; +import "./ReactToastifyOverrideClass.scss"; import styles from "./SnackbarController.module.scss"; + +import XMarkSvg from "@images/cancel.svg?react"; +import WarningSvg from "@images/warning.svg?react"; +import MegaphoneSvg from "@images/megaphone.svg?react"; +import CheckMarkSvg from "@images/check_mark.svg?react"; +import ErrorSvg from "@images/error.svg?react"; + import { useNotificationStatus } from "@logics_common"; export const SnackbarController = () => { const { currentNotificationStatus, closeNotification } = useNotificationStatus(); - - const handleClose = (event, reason) => { - closeNotification(event, reason); - }; - - const snackbar_classname = clsx(styles.snackbar_content, { - [styles.is_success]: currentNotificationStatus.data.status === "success", - [styles.is_warning]: currentNotificationStatus.data.status === "warning", - [styles.is_error]: currentNotificationStatus.data.status === "error", - }); + const [containerKey, setContainerKey] = useState(0); const settings = currentNotificationStatus.data; - let hide_duration = 5000; - if (settings.options?.hide_duration === null) hide_duration = null; - if (Number(settings.options?.hide_duration)) hide_duration = settings.options.hide_duration; + const snackbar_classname = clsx( + styles.snackbar_content, + { + [styles.is_success]: settings.status === "success", + [styles.is_warning]: settings.status === "warning", + [styles.is_error]: settings.status === "error", + } + ); + + let hideDuration = 5000; + if (settings.options?.hide_duration === null) { + hideDuration = false; + } else if (Number(settings.options?.hide_duration)) { + hideDuration = Number(settings.options?.hide_duration); + } + + useEffect(() => { + if (!settings.is_open) return; + + const message_text = settings.message; + + if (toast.isActive(message_text)) { + setContainerKey(prevKey => prevKey + 1); + + setTimeout(() => { + toast(message_text, { + toastId: message_text, + type: settings.status, + autoClose: hideDuration, + transition: Bounce, + toastClassName: snackbar_classname, + progressClassName: styles.toast_progress, + closeButton: , + onClose: () => { + closeNotification(); + }, + }); + }, 50); + } else { + toast(message_text, { + toastId: message_text, + type: settings.status, + autoClose: hideDuration, + transition: Bounce, + toastClassName: snackbar_classname, + progressClassName: styles.toast_progress, + closeButton: , + onClose: () => { + closeNotification(); + }, + }); + } + }, [settings, hideDuration, closeNotification, snackbar_classname]); return ( -
- -
-

{settings.message}

-
-
-
+ { + switch (type) { + case "info": + return ; + case "error": + return ; + case "success": + return ; + case "warning": + return ; + default: + return null; + } + }} + /> ); }; -const SlideTransition = (props) => { - return ; -}; +const CloseButtonContainer = ({ closeToast }) => { + return ( + + ); +}; \ No newline at end of file diff --git a/src-ui/app/snackbar_controller/SnackbarController.module.scss b/src-ui/app/snackbar_controller/SnackbarController.module.scss index 075e4419..f048a6ba 100644 --- a/src-ui/app/snackbar_controller/SnackbarController.module.scss +++ b/src-ui/app/snackbar_controller/SnackbarController.module.scss @@ -1,19 +1,83 @@ +/* SnackbarController.module.scss */ + +/* ------------------------------------------------- + 1) トースト共通のスタイル +--------------------------------------------------*/ .snackbar_content { - width: 100%; - height: 100%; - padding: 2rem; - color: #fff; - &.is_success { - background-color: var(--success_bc_color); - } - &.is_warning { - background-color: var(--waring_bc_color); - } - &.is_error { - background-color: var(--error_bc_color); - } + position: relative; + padding: 1.2rem 1.6rem; + border-radius: 0.8rem; + font-size: 1.4rem; + box-shadow: 0 0.2rem 0.8rem rgba(0, 0, 0, 0.5); + color: #fff; /* ダークテーマ前提で文字を白に */ } -.snackbar_message { - font-size: 1.4rem; +/* ステータス別に背景色を指定 */ +.is_success { + background-color: var(--success_bc_color); +} + +.is_warning { + background-color: var(--warning_bc_color); + color: #212529; /* 黄系の背景に合わせて文字を濃く */ +} + +.is_error { + background-color: var(--error_bc_color); +} + + +.megaphone_svg { + color: var(--dark_200_color); + width: 2.4rem; +} +.error_svg { + color: var(--error_bc_color); + width: 2.4rem; +} +.check_mark_svg { + color: var(--primary_400_color); + width: 2rem; +} +.warning_svg { + color: var(--waring_color); + width: 2.4rem; +} + + + +// Duplicated (Customized) +.close_button_wrapper { + position: absolute; + top: 0; + left: 100%; + transform: translate(-50%, -50%) rotate(45deg); + display: flex; + justify-content: center; + align-items: end; + width: 5.6rem; + aspect-ratio: 1 / 1; + &:hover { + background-color: var(--error_bc_color); + & .x_mark_svg { + color: var(--dark_200_color); + transform: rotate(45deg); + } + } + &:active { + background-color: var(--error_bc_active_color); + } + transition: all 0.1s ease; +} + +.close_button { + // width: 100%; + // height: 100%; +} + +.x_mark_svg { + width: 2rem; + transform: rotate(-45deg); + color: var(--dark_700_color); + transition: transform 0.3s ease; } \ No newline at end of file diff --git a/src-ui/app/splash_component/SplashComponent.jsx b/src-ui/app/splash_component/SplashComponent.jsx index 2943bad0..809f917c 100644 --- a/src-ui/app/splash_component/SplashComponent.jsx +++ b/src-ui/app/splash_component/SplashComponent.jsx @@ -71,7 +71,7 @@ const AnnouncementsContainer = () => { }; - +// Duplicated const CloseButtonContainer = () => { const { asyncCloseApp } = useWindow(); diff --git a/src-ui/app/splash_component/SplashComponent.module.scss b/src-ui/app/splash_component/SplashComponent.module.scss index f279b21b..bae69063 100644 --- a/src-ui/app/splash_component/SplashComponent.module.scss +++ b/src-ui/app/splash_component/SplashComponent.module.scss @@ -67,7 +67,7 @@ } - +// Duplicated .close_button_wrapper { position: absolute; top: 0; diff --git a/src-ui/assets/error.svg b/src-ui/assets/error.svg new file mode 100644 index 00000000..4842a2a7 --- /dev/null +++ b/src-ui/assets/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src-ui/logics/common/useNotificationStatus.js b/src-ui/logics/common/useNotificationStatus.js index f4aab3ed..c623d091 100644 --- a/src-ui/logics/common/useNotificationStatus.js +++ b/src-ui/logics/common/useNotificationStatus.js @@ -35,8 +35,7 @@ export const useNotificationStatus = () => { }); }; - const closeNotification = (event, reason) => { - if (reason === "clickaway") return; + const closeNotification = () => { updateNotificationStatus((prev) => ({ ...prev.data, is_open: false, From ad0e87f5a1db538894d58d49a919358d5e5b1341 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 5 Jun 2025 22:04:40 +0900 Subject: [PATCH 2/2] [bugfix/Chore] Fix warning progress bar color that was not applied. --- src-ui/app/_index_css/variables.css | 4 +-- .../TranslatorSelectorOpenButton.module.scss | 2 +- .../PluginCompatibilityList.module.scss | 2 +- .../ReactToastifyOverrideClass.scss | 5 +++- .../SnackbarController.module.scss | 28 +++++++------------ 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src-ui/app/_index_css/variables.css b/src-ui/app/_index_css/variables.css index ca2beca1..3ae80370 100644 --- a/src-ui/app/_index_css/variables.css +++ b/src-ui/app/_index_css/variables.css @@ -24,8 +24,8 @@ --error_bc_color: #bb4448; --error_bc_active_color: #9c3938; --success_bc_color: #368777; - --waring_color: #cb944f; - --waring_bc_color: #cf7b1b; + --warning_color: #cb944f; + --warning_bc_color: #cf7b1b; --dark_basic_text_color: #f2f2f2; --dark_100_color: #f5f7fb; diff --git a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.module.scss b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.module.scss index 6549c5b3..ed8a1ab7 100644 --- a/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.module.scss +++ b/src-ui/app/main_page/sidebar_section/language_settings/translator_selector_open_button/TranslatorSelectorOpenButton.module.scss @@ -31,5 +31,5 @@ margin-left: 0.2rem; padding-bottom: 0.2rem; width: 1.8rem; - color: var(--waring_color); + color: var(--warning_color); } \ No newline at end of file diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss index 0b439619..a82112e1 100644 --- a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss @@ -54,7 +54,7 @@ .warning_svg { padding-bottom: 0.4rem; width: 2.4rem; - color: var(--waring_color); + color: var(--warning_color); flex-shrink: 0; } diff --git a/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss b/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss index 9325d747..75ad8e19 100644 --- a/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss +++ b/src-ui/app/snackbar_controller/ReactToastifyOverrideClass.scss @@ -3,7 +3,7 @@ --toastify-color-dark: var(--dark_950_color); --toastify-color-info: var(--sent_400_color); --toastify-color-success: var(--primary_400_color); - --toastify-color-warning: var(--waring_bc_color); + --toastify-color-warning: var(--warning_bc_color); --toastify-color-error: var(--error_bc_color); --toastify-color-transparent: rgba(255, 255, 255, 0.7); @@ -87,6 +87,9 @@ gap: 0.6rem; } +.Toastify__progress-bar--wrp { + height: 0.4rem; +} .Toastify__progress-bar--success { background: var(--success_bc_color); } diff --git a/src-ui/app/snackbar_controller/SnackbarController.module.scss b/src-ui/app/snackbar_controller/SnackbarController.module.scss index f048a6ba..ebb18798 100644 --- a/src-ui/app/snackbar_controller/SnackbarController.module.scss +++ b/src-ui/app/snackbar_controller/SnackbarController.module.scss @@ -1,30 +1,22 @@ -/* SnackbarController.module.scss */ - -/* ------------------------------------------------- - 1) トースト共通のスタイル ---------------------------------------------------*/ .snackbar_content { position: relative; padding: 1.2rem 1.6rem; border-radius: 0.8rem; font-size: 1.4rem; box-shadow: 0 0.2rem 0.8rem rgba(0, 0, 0, 0.5); - color: #fff; /* ダークテーマ前提で文字を白に */ } -/* ステータス別に背景色を指定 */ -.is_success { - background-color: var(--success_bc_color); -} +// .is_success { +// background-color: var(--success_bc_color); +// } -.is_warning { - background-color: var(--warning_bc_color); - color: #212529; /* 黄系の背景に合わせて文字を濃く */ -} +// .is_warning { +// background-color: var(--warning_bc_color); +// } -.is_error { - background-color: var(--error_bc_color); -} +// .is_error { +// background-color: var(--error_bc_color); +// } .megaphone_svg { @@ -40,7 +32,7 @@ width: 2rem; } .warning_svg { - color: var(--waring_color); + color: var(--warning_color); width: 2.4rem; }