From 9d08560e9151a7e797e3cb3807a7c37ae2bff4b5 Mon Sep 17 00:00:00 2001 From: RrybaN <58100226+RrybaN@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:15:57 +0800 Subject: [PATCH] feat(notify): show duration (#542) * feat(notify): add option to show duration lib.notify new option `showDuration?: boolean -- Option to show duration bar` * fix(notify): allow icon color using hexadecimal values * chore(notify): show duration to default and added tinycolor2 for supporting available color formats * feat(notify): define interface for showDuration --- package/client/resource/interface/notify.ts | 1 + resource/interface/client/notify.lua | 1 + web/package.json | 2 + web/pnpm-lock.yaml | 14 +++ web/src/features/dev/debug/notification.ts | 1 + .../notifications/NotificationWrapper.tsx | 102 +++++++++++++----- web/src/typings/notifications.ts | 1 + 7 files changed, 96 insertions(+), 26 deletions(-) diff --git a/package/client/resource/interface/notify.ts b/package/client/resource/interface/notify.ts index 53a874a..6ed4c88 100644 --- a/package/client/resource/interface/notify.ts +++ b/package/client/resource/interface/notify.ts @@ -18,6 +18,7 @@ interface NotifyProps { title?: string; description?: string; duration?: number; + showDuration?: boolean; position?: NotificationPosition; type?: NotificationType; style?: Sx; diff --git a/resource/interface/client/notify.lua b/resource/interface/client/notify.lua index d804a2d..7f97cc7 100644 --- a/resource/interface/client/notify.lua +++ b/resource/interface/client/notify.lua @@ -7,6 +7,7 @@ ---@field title? string ---@field description? string ---@field duration? number +---@field showDuration? boolean ---@field position? NotificationPosition ---@field type? NotificationType ---@field style? { [string]: any } diff --git a/web/package.json b/web/package.json index ff92dfa..9a91c74 100644 --- a/web/package.json +++ b/web/package.json @@ -24,6 +24,7 @@ "react-hot-toast": "^2.4.0", "react-markdown": "^8.0.1", "remark-gfm": "^3.0.1", + "tinycolor2": "^1.6.0", "typescript": "^4.6.3", "vite": "^5.0.13", "web-vitals": "^2.1.4" @@ -41,6 +42,7 @@ "@types/node": "^20.9.0", "@types/react": "^18.0.24", "@types/react-dom": "^18.0.8", + "@types/tinycolor2": "^1.4.6", "autoprefixer": "^10.0.2", "cross-env": "^7.0.3", "csstype": "^3.0.10", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index c6df306..1bf281f 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -70,6 +70,9 @@ dependencies: remark-gfm: specifier: ^3.0.1 version: 3.0.1 + tinycolor2: + specifier: ^1.6.0 + version: 1.6.0 typescript: specifier: ^4.6.3 version: 4.9.5 @@ -99,6 +102,9 @@ devDependencies: '@types/react-dom': specifier: ^18.0.8 version: 18.0.10 + '@types/tinycolor2': + specifier: ^1.4.6 + version: 1.4.6 autoprefixer: specifier: ^10.0.2 version: 10.4.13(postcss@8.4.31) @@ -1394,6 +1400,10 @@ packages: /@types/scheduler@0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} + /@types/tinycolor2@1.4.6: + resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + dev: true + /@types/unist@2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: false @@ -2632,6 +2642,10 @@ packages: resolution: {integrity: sha512-SYJSIgeyXW7EuX1ytdneO5e8jip42oHWg9xl/o3oTYhmXusZVgiA+VlPvjIN+kHii9v90AmzTZEBcsEvuAY+TA==} dev: false + /tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} diff --git a/web/src/features/dev/debug/notification.ts b/web/src/features/dev/debug/notification.ts index 8f1578d..ea3f55d 100644 --- a/web/src/features/dev/debug/notification.ts +++ b/web/src/features/dev/debug/notification.ts @@ -37,6 +37,7 @@ export const debugCustomNotification = () => { description: 'Notification description', type: 'success', icon: 'microchip', + showDuration: false, }, }, ]); diff --git a/web/src/features/notifications/NotificationWrapper.tsx b/web/src/features/notifications/NotificationWrapper.tsx index 03772b7..22da9df 100644 --- a/web/src/features/notifications/NotificationWrapper.tsx +++ b/web/src/features/notifications/NotificationWrapper.tsx @@ -1,8 +1,9 @@ import { useNuiEvent } from '../../hooks/useNuiEvent'; import { toast, Toaster } from 'react-hot-toast'; import ReactMarkdown from 'react-markdown'; -import { Avatar, Box, createStyles, Group, keyframes, Stack, Text } from '@mantine/core'; +import { Box, Center, createStyles, Group, keyframes, RingProgress, Stack, Text, ThemeIcon } from '@mantine/core'; import React from 'react'; +import tinycolor from 'tinycolor2'; import type { NotificationProps } from '../../typings'; import MarkdownComponents from '../../config/MarkdownComponents'; import LibIcon from '../../components/LibIcon'; @@ -103,11 +104,22 @@ const exitAnimationBottom = keyframes({ }, }); +const durationCircle = keyframes({ + '0%': { strokeDasharray: `0, ${15.1 * 2 * Math.PI}` }, + '100%': { strokeDasharray: `${15.1 * 2 * Math.PI}, 0` }, +}); + const Notifications: React.FC = () => { const { classes } = useStyles(); useNuiEvent('notify', (data) => { if (!data.title && !data.description) return; + + let iconColor: string; + let duration = data.duration || 3000; + + data.showDuration = data.showDuration !== undefined ? data.showDuration : true; + // Backwards compat with old notifications let position = data.position; switch (position) { @@ -134,6 +146,24 @@ const Notifications: React.FC = () => { break; } } + if (!data.iconColor) { + switch (data.type) { + case 'error': + iconColor = 'red.6'; + break; + case 'success': + iconColor = 'teal.6'; + break; + case 'warning': + iconColor = 'yellow.6'; + break; + default: + iconColor = 'blue.6'; + break; + } + } else { + iconColor = tinycolor(data.iconColor).toRgbString(); + } toast.custom( (t) => ( { {data.icon && ( <> - {!data.iconColor ? ( - svg > circle:nth-of-type(2)': { + animation: `${durationCircle} linear forwards reverse`, + animationDuration: `${duration}ms`, + }, + margin: -3, + } + }} + label={ +
+ + + +
+ } + /> + ) : ( + - -
- ) : ( - + + )} )} @@ -205,7 +255,7 @@ const Notifications: React.FC = () => { ), { id: data.id?.toString(), - duration: data.duration || 3000, + duration: duration, position: position || 'top-right', } ); diff --git a/web/src/typings/notifications.ts b/web/src/typings/notifications.ts index 9096e5d..e47abad 100644 --- a/web/src/typings/notifications.ts +++ b/web/src/typings/notifications.ts @@ -8,6 +8,7 @@ export interface NotificationProps { description?: string; title?: string; duration?: number; + showDuration?: boolean; icon?: IconProp; iconColor?: string; iconAnimation?: IconAnimation;