diff --git a/apps/web/package.json b/apps/web/package.json index d9185ef82..136aa9fda 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -50,7 +50,7 @@ "react-markdown": "9.0.1", "react-player": "2.16.0", "react-syntax-highlighter": "15.6.1", - "react-toastify": "^10.0.6", + "react-toastify": "^11.0.1", "react-use-websocket": "^4.11.1", "recharts": "^2.14.1", "rehype-mathjax": "6.0.0", diff --git a/apps/web/src/components/main-layout.tsx b/apps/web/src/components/main-layout.tsx index ae1f4f4e7..745e50c43 100644 --- a/apps/web/src/components/main-layout.tsx +++ b/apps/web/src/components/main-layout.tsx @@ -40,7 +40,7 @@ export const MainLayout = ({ if (hasJustRegistered) { customToast(t('auth.dashboardUnlocked'), { mode: variant === 'dark' ? 'dark' : 'light', - color: 'secondary', + color: 'primary', time: 5000, closeButton: true, imgSrc: SignInIconLight, diff --git a/apps/web/src/routes/_content/courses/$courseId/index.tsx b/apps/web/src/routes/_content/courses/$courseId/index.tsx index afd5b27b4..561daf623 100644 --- a/apps/web/src/routes/_content/courses/$courseId/index.tsx +++ b/apps/web/src/routes/_content/courses/$courseId/index.tsx @@ -655,7 +655,7 @@ function CourseDetails() { : async () => { if (!isLoggedIn && !hasSeenRegisterToast) { customToast(t('auth.trackProgress'), { - color: 'secondary', + color: 'primary', mode: 'light', imgSrc: SignInIconLight, onClick: () => { @@ -674,7 +674,7 @@ function CourseDetails() { userCourseProgress[0].completedChaptersCount === 0)) ) { customToast(t('courses.details.courseAddedToDashboard'), { - color: 'secondary', + color: 'primary', mode: 'light', imgSrc: SignInIconLight, closeButton: true, diff --git a/packages/ui/src/atoms/toast.tsx b/packages/ui/src/atoms/toast.tsx index c182d40c3..e85e7ff67 100644 --- a/packages/ui/src/atoms/toast.tsx +++ b/packages/ui/src/atoms/toast.tsx @@ -4,18 +4,21 @@ import { IoCloseOutline } from 'react-icons/io5'; import type { IconType } from 'react-icons/lib'; import { toast } from 'react-toastify'; -const toastVariants = cva('md:!w-[299px]', { +const toastVariants = cva('md:!w-[299px] focus:ring-1 focus:ring-newGray-2', { variants: { mode: { light: '', dark: 'dark', }, color: { - primary: '!bg-darkOrange-2 dark:!bg-darkOrange-8', - secondary: '!bg-darkOrange-0 dark:!bg-darkOrange-10', - warning: '!bg-red-1 dark:!bg-red-7', - success: '!bg-brightGreen-1 dark:!bg-brightGreen-9', - neutral: '!bg-newBlack-3 dark:!bg-newGray-6', + primary: + '!bg-darkOrange-0 dark:!bg-darkOrange-10 hover:!bg-darkOrange-1 hover:dark:!bg-darkOrange-9 focus:!bg-darkOrange-1 focus:dark:!bg-darkOrange-9', + warning: + '!bg-red-1 dark:!bg-red-9 hover:!bg-red-2 hover:dark:!bg-red-8 focus:!bg-red-2 focus:dark:!bg-red-8', + success: + '!bg-brightGreen-1 dark:!bg-brightGreen-9 hover:!bg-brightGreen-2 hover:dark:!bg-brightGreen-8 focus:!bg-brightGreen-2 focus:dark:!bg-brightGreen-8', + neutral: + '!bg-newGray-6 dark:!bg-newBlack-3 hover:!bg-newGray-5 hover:dark:!bg-newBlack-4 focus:!bg-newGray-5 focus:dark:!bg-newBlack-4', }, }, defaultVariants: { @@ -43,11 +46,10 @@ const iconVariants = cva('shrink-0', { dark: 'dark', }, color: { - primary: '!text-darkOrange-5', - secondary: '!text-darkOrange-3 dark:!text-darkOrange-5', - warning: '!text-red-5 dark:!text-red-3', - success: '!text-brightGreen-4', - neutral: '!text-newGray-3 dark:!text-newGray-4', + primary: '!text-darkOrange-4 dark:!text-darkOrange-6', + warning: '!text-red-5', + success: '!text-brightGreen-4 dark:!text-brightGreen-6', + neutral: '!text-newGray-3 dark:!text-newGray-2', }, }, defaultVariants: { @@ -64,10 +66,9 @@ const progressBarVariants = cva('', { }, color: { primary: '!bg-darkOrange-4 dark:!bg-darkOrange-6', - secondary: '!bg-darkOrange-2 dark:!bg-darkOrange-7', - warning: '!bg-red-2 dark:!bg-red-4', - success: '!bg-brightGreen-3 dark:!bg-brightGreen-5', - neutral: '!bg-newGray-4 dark:!bg-newGray-3', + warning: '!bg-red-5', + success: '!bg-brightGreen-4 dark:!bg-brightGreen-6', + neutral: '!bg-newGray-3 dark:!bg-newGray-2', }, }, defaultVariants: { @@ -83,10 +84,9 @@ const toastCloseButtonVariants = cva('shrink-0', { dark: 'dark hover:!brightness-110', }, color: { - primary: '!text-darkOrange-6', - secondary: '!text-darkOrange-4 dark:!text-darkOrange-6', - warning: '!text-red-4', - success: '!text-brightGreen-5 dark:!text-brightGreen-6', + primary: '!text-darkOrange-4 dark:!text-darkOrange-6', + warning: '!text-red-5', + success: '!text-brightGreen-4 dark:!text-brightGreen-6', neutral: '!text-newGray-3 dark:!text-newGray-2', }, }, @@ -98,7 +98,7 @@ const toastCloseButtonVariants = cva('shrink-0', { interface ToastProps { mode?: 'light' | 'dark'; - color?: 'primary' | 'secondary' | 'warning' | 'success' | 'neutral'; + color?: 'primary' | 'warning' | 'success' | 'neutral'; } export const customToast = ( @@ -116,51 +116,85 @@ export const customToast = ( ) => { const { closeOnClick = true } = options; - return toast(message, { - autoClose: options.time || 5000, - className: toastVariants({ - mode: options.mode, - color: options.color, - }), - bodyClassName: textVariants({ - mode: options.mode, - }), - progressClassName: progressBarVariants({ - mode: options.mode, - color: options.color, + return toast( + ToastContent({ + message: message, + className: textVariants({ + mode: options.mode, + }), + onClick: options.onClick, }), - icon: options.imgSrc - ? () => ( - {message} - ) - : options.icon && ( - - ), - closeButton: options.onClick - ? false - : options.closeButton - ? ({ - closeToast, - }: { - closeToast: MouseEventHandler; - }) => ( - ( + {message} + ) + : options.icon && ( + - ) - : false, - onClick: options.onClick, - closeOnClick: closeOnClick, - }); + ), + closeButton: options.onClick + ? false + : options.closeButton + ? ({ + closeToast, + }: { + closeToast: MouseEventHandler; + }) => ( + + ) + : false, + onClick: options.onClick, + closeOnClick: closeOnClick, + }, + ); }; -export const ToastCloseButton = ({ +const ToastContent = ({ + message, + className, + onClick, +}: { + message: string; + className: string; + onClick?: () => void; +}) => ( + { + if (onClick && (e.key === 'Enter' || e.key === ' ')) { + e.preventDefault(); + onClick(); + } + }} + role={onClick ? 'button' : undefined} + tabIndex={onClick ? 0 : undefined} + > + {message} + +); + +const ToastCloseButton = ({ closeToast, mode, color, @@ -181,7 +215,7 @@ export const ToastCloseButton = ({ ); -export const ToastIconWithClasses = ({ +const ToastIconWithClasses = ({ icon: Icon, mode, color, diff --git a/packages/ui/src/styles/global.css b/packages/ui/src/styles/global.css index b21acbb6c..75d8280ab 100644 --- a/packages/ui/src/styles/global.css +++ b/packages/ui/src/styles/global.css @@ -677,10 +677,6 @@ transform: translate3d(0, var(--y), 0) scale(var(--s)); transition: transform 0.3s; } -.Toastify__toast--stacked[data-collapsed] .Toastify__toast-body, -.Toastify__toast--stacked[data-collapsed] .Toastify__close-button { - transition: opacity 0.1s; -} .Toastify__toast--stacked[data-collapsed='false'] { overflow: visible; } @@ -723,21 +719,6 @@ .Toastify__toast--close-on-click { cursor: pointer; } -.Toastify__toast-body { - margin: auto 0; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 6px; - display: -ms-flexbox; - display: flex; - -ms-flex-align: center; - align-items: center; -} -.Toastify__toast-body > div:last-child { - word-break: break-word; - -ms-flex: 1; - flex: 1; -} .Toastify__toast-icon { -webkit-margin-end: 12px; margin-inline-end: 12px; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45a9f83ac..39e01de64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,8 +259,8 @@ importers: specifier: 15.6.1 version: 15.6.1(react@18.3.1) react-toastify: - specifier: ^10.0.6 - version: 10.0.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^11.0.1 + version: 11.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-use-websocket: specifier: ^4.11.1 version: 4.11.1 @@ -405,7 +405,7 @@ importers: version: 9.1.0(eslint@8.57.1) eslint-import-resolver-custom-alias: specifier: ^1.3.2 - version: 1.3.2(eslint-plugin-import@2.29.1) + version: 1.3.2(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)) eslint-import-resolver-typescript: specifier: ^3.7.0 version: 3.7.0(eslint-plugin-import@2.29.1)(eslint@8.57.1) @@ -6891,6 +6891,12 @@ packages: react: '>=18' react-dom: '>=18' + react-toastify@11.0.1: + resolution: {integrity: sha512-ZALj1xpjOVlPPDKvdksGLemF9mM6FRltsykvv+JQpW9mildnBiy6nGz0685vo4+W8WitParsUfGk9qACArkZPA==} + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -12389,7 +12395,7 @@ snapshots: eslint: 8.57.1 eslint-plugin-turbo: 2.3.3(eslint@8.57.1) - eslint-import-resolver-custom-alias@1.3.2(eslint-plugin-import@2.29.1): + eslint-import-resolver-custom-alias@1.3.2(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)): dependencies: eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) glob-parent: 6.0.2 @@ -12419,7 +12425,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -12449,7 +12455,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.4.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -14955,6 +14961,12 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-toastify@11.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.26.0