diff --git a/apps/storybook/src/stories/notification-center.stories.tsx b/apps/storybook/src/stories/notification-center.stories.tsx index 0b82836dd2..3b2af6ac7e 100644 --- a/apps/storybook/src/stories/notification-center.stories.tsx +++ b/apps/storybook/src/stories/notification-center.stories.tsx @@ -223,6 +223,21 @@ export const Basic = () => { gif +
+ +
); diff --git a/packages/component/src/components/notification-center/index.css.ts b/packages/component/src/components/notification-center/index.css.ts index 64f78b0978..ab80885ce5 100644 --- a/packages/component/src/components/notification-center/index.css.ts +++ b/packages/component/src/components/notification-center/index.css.ts @@ -177,21 +177,20 @@ export const notificationContentStyle = style({ width: '380px', borderRadius: '8px', boxShadow: 'var(--affine-shadow-1)', - border: '1px solid var(--affine-border-color)', + border: '1px solid var(--affine-black-10)', background: 'var(--affine-white)', transition: 'all 0.3s', }); export const notificationTitleContactStyle = style({ marginRight: '22px', width: '200px', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden', - lineHeight: '1.5', + overflow: 'wrap', + lineHeight: '24px', + fontSize: 'var(--affine-font-base)', }); export const notificationTitleStyle = style({ display: 'flex', - alignItems: 'center', + alignItems: 'flex-start', width: '100%', justifyContent: 'flex-start', }); @@ -199,6 +198,7 @@ export const notificationDescriptionStyle = style({ fontSize: 'var(--affine-font-sm)', color: 'var(--affine-text-secondary-color)', marginBottom: '4px', + lineHeight: '22px', }); export const notificationTimeStyle = style({ fontSize: 'var(--affine-font-sm)', @@ -234,7 +234,7 @@ export const closeButtonWithMediaStyle = style({ }, }); export const closeButtonColorStyle = style({ - color: 'var(--affine-white)', + color: 'var(--affine-text-primary-color)', }); export const undoButtonStyle = style({ fontSize: 'var(--affine-font-sm)', @@ -296,10 +296,10 @@ export const lightWarningStyle = style({ borderRadius: '8px', }); export const darkColorStyle = style({ - color: 'var(--affine-white)', + color: 'var(--affine-pure-white)', }); export const lightInfoIconStyle = style({ - color: 'var(--affine-processing-color)', + color: 'var(--affine-icon-color)', }); export const defaultCollapseStyle = styleVariants({ secondary: { diff --git a/packages/component/src/components/notification-center/index.jotai.ts b/packages/component/src/components/notification-center/index.jotai.ts index 97a071ccb7..8ecccb6a12 100644 --- a/packages/component/src/components/notification-center/index.jotai.ts +++ b/packages/component/src/components/notification-center/index.jotai.ts @@ -1,11 +1,12 @@ +import { uuidv4 } from '@blocksuite/store'; import { atom } from 'jotai'; export type Notification = { - key: string; + key?: string; title: string; - message: string; + message?: string; type: 'success' | 'error' | 'warning' | 'info'; - theme?: 'light' | 'dark'; + theme?: 'light' | 'dark' | 'default'; timeout?: number; progressingBar?: boolean; multimedia?: React.ReactNode | JSX.Element | HTMLElement; @@ -41,6 +42,7 @@ export const removeNotificationAtom = atom(null, (_, set, key: string) => { export const pushNotificationAtom = atom( null, (_, set, newNotification) => { + newNotification.key = newNotification.key || uuidv4(); const key = newNotification.key; const removeNotification = () => set(notificationsBaseAtom, notifications => diff --git a/packages/component/src/components/notification-center/index.tsx b/packages/component/src/components/notification-center/index.tsx index e1e973cf6f..380ce250bc 100644 --- a/packages/component/src/components/notification-center/index.tsx +++ b/packages/component/src/components/notification-center/index.tsx @@ -2,7 +2,7 @@ // License on the MIT // https://github.com/emilkowalski/sonner/blob/5cb703edc108a23fd74979235c2f3c4005edd2a7/src/index.tsx -import { CloseIcon, InformationIcon } from '@blocksuite/icons'; +import { CloseIcon, InformationFillDuotoneIcon } from '@blocksuite/icons'; import * as Toast from '@radix-ui/react-toast'; import { IconButton } from '@toeverything/components/button'; import clsx from 'clsx'; @@ -33,7 +33,7 @@ export { }; type Height = { height: number; - notificationKey: number | string; + notificationKey: number | string | undefined; }; export type NotificationCardProps = { notification: Notification; @@ -46,24 +46,29 @@ const typeColorMap = { info: { light: styles.lightInfoStyle, dark: styles.darkInfoStyle, + default: '', }, success: { light: styles.lightSuccessStyle, dark: styles.darkSuccessStyle, + default: '', }, warning: { light: styles.lightWarningStyle, dark: styles.darkWarningStyle, + default: '', }, error: { light: styles.lightErrorStyle, dark: styles.darkErrorStyle, + default: '', }, }; function NotificationCard(props: NotificationCardProps): ReactElement { const removeNotification = useSetAtom(removeNotificationAtom); const { notification, notifications, setHeights, heights, index } = props; + const [expand, setExpand] = useAtom(expandNotificationCenterAtom); // const setNotificationRemoveAnimation = useSetAtom(notificationRemoveAnimationAtom); const [mounted, setMounted] = useState(false); @@ -89,6 +94,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement { const duration = notification.timeout || 3000; const offset = useRef(0); const pointerStartYRef = useRef(null); + const notificationsHeightBefore = useMemo(() => { return heights.reduce((prev, curr, reducerIndex) => { // Calculate offset up until current notification @@ -149,7 +155,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement { }, [notification.title, notification.key, mounted, setHeights]); const typeStyle = - typeColorMap[notification.type][notification.theme || 'light']; + typeColorMap[notification.type][notification.theme || 'dark']; const onClickRemove = useCallback(() => { // Save the offset for the exit swipe animation @@ -159,6 +165,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement { h.filter(height => height.notificationKey !== notification.key) ); setTimeout(() => { + if (!notification.key) { + return; + } removeNotification(notification.key); }, 200); }, [setHeights, notification.key, removeNotification, offset]); @@ -291,7 +300,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement { >
- +
{notification.title} @@ -323,7 +336,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement { {notification.undo && (
@@ -349,7 +365,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement { {notification.message} @@ -410,16 +428,18 @@ export function NotificationCenter(): ReactElement { if (!notifications.length) return <>; return ( - {notifications.map((notification, index) => ( - - ))} + {notifications.map((notification, index) => + notification.key ? ( + + ) : null + )} { onSelect?.({ type: 'pdf' }); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.success.title'](), message: t['com.affine.export.success.message'](), type: 'success', @@ -50,7 +48,6 @@ export const ExportToPdfMenuItem = ({ .catch(err => { console.error(err); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.error.title'](), message: t['com.affine.export.error.message'](), type: 'error', @@ -64,7 +61,6 @@ export const ExportToPdfMenuItem = ({ .then(() => { onSelect?.({ type: 'pdf' }); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.success.title'](), message: t['com.affine.export.success.message'](), type: 'success', @@ -73,7 +69,6 @@ export const ExportToPdfMenuItem = ({ .catch(err => { console.error(err); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.error.title'](), message: t['com.affine.export.error.message'](), type: 'error', @@ -110,7 +105,6 @@ export const ExportToHtmlMenuItem = ({ .then(() => { onSelect?.({ type: 'html' }); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.success.title'](), message: t['com.affine.export.success.message'](), type: 'success', @@ -119,7 +113,6 @@ export const ExportToHtmlMenuItem = ({ .catch(err => { console.error(err); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.error.title'](), message: t['com.affine.export.error.message'](), type: 'error', @@ -159,7 +152,6 @@ export const ExportToPngMenuItem = ({ .then(() => { onSelect?.({ type: 'png' }); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.success.title'](), message: t['com.affine.export.success.message'](), type: 'success', @@ -168,7 +160,6 @@ export const ExportToPngMenuItem = ({ .catch(err => { console.error(err); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.error.title'](), message: t['com.affine.export.error.message'](), type: 'error', @@ -206,7 +197,6 @@ export const ExportToMarkdownMenuItem = ({ .then(() => { onSelect?.({ type: 'markdown' }); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.success.title'](), message: t['com.affine.export.success.message'](), type: 'success', @@ -215,7 +205,6 @@ export const ExportToMarkdownMenuItem = ({ .catch(err => { console.error(err); setPushNotification({ - key: uuidv4(), title: t['com.affine.export.error.title'](), message: t['com.affine.export.error.message'](), type: 'error',