feat: support gif toast (#3389)

This commit is contained in:
JimmFly 2023-07-27 13:37:18 +08:00 committed by GitHub
parent fa8086d525
commit bc27412425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 30 deletions

View File

@ -12,6 +12,12 @@ export default {
} satisfies Meta<typeof NotificationCenter>; } satisfies Meta<typeof NotificationCenter>;
let id = 0; let id = 0;
const image = (
<video autoPlay muted loop>
<source src="/editingVideo.mp4" type="video/mp4" />
<source src="/editingVideo.webm" type="video/webm" />
</video>
);
export const Basic = () => { export const Basic = () => {
const push = useSetAtom(pushNotificationAtom); const push = useSetAtom(pushNotificationAtom);
const expand = useAtomValue(expandNotificationCenterAtom); const expand = useAtomValue(expandNotificationCenterAtom);
@ -196,6 +202,27 @@ export const Basic = () => {
dark error dark error
</button> </button>
</div> </div>
<div>
<button
onClick={() => {
const key = id++;
push({
key: `${key}`,
title: `${key} title`,
message: `gif test`,
type: 'info',
multimedia: image,
timeout: 3000,
undo: async () => {
console.log('undo');
},
progressingBar: true,
});
}}
>
gif
</button>
</div>
<NotificationCenter /> <NotificationCenter />
</> </>
); );

View File

@ -2,7 +2,12 @@
// License on the MIT // License on the MIT
// https://github.com/emilkowalski/sonner/blob/5cb703edc108a23fd74979235c2f3c4005edd2a7/src/styles.css // https://github.com/emilkowalski/sonner/blob/5cb703edc108a23fd74979235c2f3c4005edd2a7/src/styles.css
import { keyframes, style, styleVariants } from '@vanilla-extract/css'; import {
globalStyle,
keyframes,
style,
styleVariants,
} from '@vanilla-extract/css';
const swipeOut = keyframes({ const swipeOut = keyframes({
'0%': { '0%': {
@ -19,12 +24,28 @@ const swipeOut = keyframes({
export const notificationCenterViewportStyle = style({ export const notificationCenterViewportStyle = style({
position: 'fixed', position: 'fixed',
bottom: '200px', height: '500px',
right: '60px', bottom: '20px',
right: '20px',
width: '380px', width: '380px',
margin: 0,
zIndex: 2147483647, zIndex: 2147483647,
outline: 'none', outline: 'none',
display: 'flex',
alignItems: 'flex-end',
});
export const notificationMultimediaStyle = style({
position: 'relative',
width: '100%',
height: '230px',
borderRadius: '8px 8px 0 0',
overflow: 'hidden',
marginBottom: '16px',
});
globalStyle(`${notificationMultimediaStyle} > *`, {
width: '100%',
height: '100%',
objectFit: 'cover',
cursor: 'unset',
}); });
export const notificationStyle = style({ export const notificationStyle = style({
@ -128,18 +149,32 @@ export const notificationStyle = style({
export const notificationIconStyle = style({ export const notificationIconStyle = style({
fontSize: '24px', fontSize: '24px',
marginLeft: '18px', marginLeft: '18px',
marginRight: '12px', marginRight: '8px',
color: 'var(--affine-processing-color)', color: 'var(--affine-processing-color)',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
}); });
export const hasMediaStyle = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
paddingTop: '0',
paddingBottom: '16px',
width: '380px',
borderRadius: '8px',
boxShadow: 'var(--affine-shadow-1)',
border: '1px solid var(--affine-border-color)',
background: 'var(--affine-white)',
transition: 'all 0.3s',
});
export const notificationContentStyle = style({ export const notificationContentStyle = style({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
padding: '16px 0', paddingTop: '16px',
width: '100%', paddingBottom: '16px',
width: '380px',
borderRadius: '8px', borderRadius: '8px',
boxShadow: 'var(--affine-shadow-1)', boxShadow: 'var(--affine-shadow-1)',
border: '1px solid var(--affine-border-color)', border: '1px solid var(--affine-border-color)',
@ -181,6 +216,26 @@ export const closeButtonStyle = style({
export const closeButtonWithoutUndoStyle = style({ export const closeButtonWithoutUndoStyle = style({
marginLeft: '92px', marginLeft: '92px',
}); });
export const closeButtonWithMediaStyle = style({
position: 'absolute',
width: '22px',
height: '22px',
fontSize: '16px',
top: '6px',
right: '6px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
cursor: 'pointer',
borderRadius: '4px',
color: 'var(--affine-pure-black)',
':hover': {
background: 'var(--affine-hover-color)',
},
});
export const closeButtonColorStyle = style({
color: 'var(--affine-white)',
});
export const undoButtonStyle = style({ export const undoButtonStyle = style({
fontSize: 'var(--affine-font-sm)', fontSize: 'var(--affine-font-sm)',
background: 'var(--affine-hover-color)', background: 'var(--affine-hover-color)',
@ -189,6 +244,10 @@ export const undoButtonStyle = style({
color: 'var(--affine-processing-color)', color: 'var(--affine-processing-color)',
cursor: 'pointer', cursor: 'pointer',
}); });
export const undoButtonWithMediaStyle = style({
marginLeft: 'auto',
marginRight: '16px',
});
export const messageStyle = style({ export const messageStyle = style({
fontSize: 'var(--affine-font-sm)', fontSize: 'var(--affine-font-sm)',
width: '200px', width: '200px',

View File

@ -8,6 +8,7 @@ export type Notification = {
theme?: 'light' | 'dark'; theme?: 'light' | 'dark';
timeout?: number; timeout?: number;
progressingBar?: boolean; progressingBar?: boolean;
multimedia?: React.ReactNode | JSX.Element | HTMLElement;
// actions // actions
undo?: () => Promise<void>; undo?: () => Promise<void>;
}; };

View File

@ -288,10 +288,20 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
}} }}
> >
<div <div
className={clsx(styles.notificationContentStyle, { className={clsx({
[typeStyle]: notification.theme, [typeStyle]: notification.theme,
[styles.hasMediaStyle]: notification.multimedia,
[styles.notificationContentStyle]: !notification.multimedia,
})} })}
> >
{notification.multimedia ? (
<div className={styles.notificationMultimediaStyle}>
<>{notification.multimedia}</>
<IconButton className={styles.closeButtonWithMediaStyle}>
<CloseIcon onClick={onClickRemove} />
</IconButton>
</div>
) : null}
<Toast.Title <Toast.Title
className={clsx(styles.notificationTitleStyle, { className={clsx(styles.notificationTitleStyle, {
[styles.darkColorStyle]: notification.theme === 'dark', [styles.darkColorStyle]: notification.theme === 'dark',
@ -312,25 +322,28 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
<div <div
className={clsx(styles.undoButtonStyle, { className={clsx(styles.undoButtonStyle, {
[styles.darkColorStyle]: notification.theme === 'dark', [styles.darkColorStyle]: notification.theme === 'dark',
[styles.undoButtonWithMediaStyle]: notification.multimedia,
})} })}
onClick={onClickUndo} onClick={onClickUndo}
> >
UNDO UNDO
</div> </div>
)} )}
<IconButton {notification.multimedia ? null : (
className={clsx(styles.closeButtonStyle, { <IconButton
[styles.closeButtonWithoutUndoStyle]: !notification.undo, className={clsx(styles.closeButtonStyle, {
})} [styles.closeButtonWithoutUndoStyle]: !notification.undo,
style={{ })}
color: style={{
notification.theme === 'dark' color:
? 'var(--affine-white)' notification.theme === 'dark'
: 'var(--affine-icon-color)', ? 'var(--affine-white)'
}} : 'var(--affine-icon-color)',
> }}
<CloseIcon onClick={onClickRemove} /> >
</IconButton> <CloseIcon onClick={onClickRemove} />
</IconButton>
)}
</Toast.Title> </Toast.Title>
<Toast.Description <Toast.Description
className={clsx(styles.messageStyle, { className={clsx(styles.messageStyle, {

View File

@ -9,6 +9,7 @@ import {
ExportToPdfIcon, ExportToPdfIcon,
ExportToPngIcon, ExportToPngIcon,
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import { uuidv4 } from '@blocksuite/store';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { useCallback } from 'react'; import { useCallback } from 'react';
@ -36,7 +37,7 @@ export const ExportToPdfMenuItem = ({
.then(() => { .then(() => {
onSelect?.({ type: 'pdf' }); onSelect?.({ type: 'pdf' });
setPushNotification({ setPushNotification({
key: 'export-to-pdf', key: uuidv4(),
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success', type: 'success',
@ -45,7 +46,7 @@ export const ExportToPdfMenuItem = ({
.catch(err => { .catch(err => {
console.error(err); console.error(err);
setPushNotification({ setPushNotification({
key: 'export-to-pdf', key: uuidv4(),
title: t['com.affine.export.error.title'](), title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](), message: t['com.affine.export.error.message'](),
type: 'error', type: 'error',
@ -59,7 +60,7 @@ export const ExportToPdfMenuItem = ({
.then(() => { .then(() => {
onSelect?.({ type: 'pdf' }); onSelect?.({ type: 'pdf' });
setPushNotification({ setPushNotification({
key: 'export-to-pdf', key: uuidv4(),
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success', type: 'success',
@ -68,7 +69,7 @@ export const ExportToPdfMenuItem = ({
.catch(err => { .catch(err => {
console.error(err); console.error(err);
setPushNotification({ setPushNotification({
key: 'export-to-pdf', key: uuidv4(),
title: t['com.affine.export.error.title'](), title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](), message: t['com.affine.export.error.message'](),
type: 'error', type: 'error',
@ -104,7 +105,7 @@ export const ExportToHtmlMenuItem = ({
.then(() => { .then(() => {
onSelect?.({ type: 'html' }); onSelect?.({ type: 'html' });
setPushNotification({ setPushNotification({
key: 'export-to-html', key: uuidv4(),
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success', type: 'success',
@ -112,6 +113,12 @@ export const ExportToHtmlMenuItem = ({
}) })
.catch(err => { .catch(err => {
console.error(err); console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',
});
}); });
onSelect?.({ type: 'html' }); onSelect?.({ type: 'html' });
}, [currentEditor, onSelect, setPushNotification, t]); }, [currentEditor, onSelect, setPushNotification, t]);
@ -146,7 +153,7 @@ export const ExportToPngMenuItem = ({
.then(() => { .then(() => {
onSelect?.({ type: 'png' }); onSelect?.({ type: 'png' });
setPushNotification({ setPushNotification({
key: 'export-to-png', key: uuidv4(),
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success', type: 'success',
@ -155,7 +162,7 @@ export const ExportToPngMenuItem = ({
.catch(err => { .catch(err => {
console.error(err); console.error(err);
setPushNotification({ setPushNotification({
key: 'export-to-png', key: uuidv4(),
title: t['com.affine.export.error.title'](), title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](), message: t['com.affine.export.error.message'](),
type: 'error', type: 'error',
@ -192,7 +199,7 @@ export const ExportToMarkdownMenuItem = ({
.then(() => { .then(() => {
onSelect?.({ type: 'markdown' }); onSelect?.({ type: 'markdown' });
setPushNotification({ setPushNotification({
key: 'export-to-markdown', key: uuidv4(),
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success', type: 'success',
@ -201,7 +208,7 @@ export const ExportToMarkdownMenuItem = ({
.catch(err => { .catch(err => {
console.error(err); console.error(err);
setPushNotification({ setPushNotification({
key: 'export-to-markdown', key: uuidv4(),
title: t['com.affine.export.error.title'](), title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](), message: t['com.affine.export.error.message'](),
type: 'error', type: 'error',