mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
refactor(core): move actions to footer of notification card (#11894)
This PR move all actions button to the footer of `NotificationCard`. There are some example as following: ### No Changes    ### Changes ### Before  #### After 
This commit is contained in:
@@ -4,7 +4,8 @@ import clsx from 'clsx';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { Button, IconButton } from '../../button';
|
||||
import type { NotificationCardProps } from '../types';
|
||||
import { FlexWrapper } from '../../layout/wrapper';
|
||||
import type { NotificationActionProps, NotificationCardProps } from '../types';
|
||||
import { getCardVars } from '../utils';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
@@ -18,10 +19,9 @@ export const DesktopNotificationCard = ({
|
||||
icon = <InformationFillDuotoneIcon />,
|
||||
iconColor,
|
||||
thumb,
|
||||
action,
|
||||
actions,
|
||||
error,
|
||||
title,
|
||||
footer,
|
||||
alignMessage = 'title',
|
||||
onDismiss,
|
||||
rootAttrs,
|
||||
@@ -33,13 +33,6 @@ export const DesktopNotificationCard = ({
|
||||
? t[errorI18nKey](error?.data)
|
||||
: undefined;
|
||||
|
||||
const onActionClicked = useCallback(() => {
|
||||
action?.onClick()?.catch(console.error);
|
||||
if (action?.autoClose !== false) {
|
||||
onDismiss?.();
|
||||
}
|
||||
}, [action, onDismiss]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={getCardVars(style, theme, iconColor)}
|
||||
@@ -56,18 +49,6 @@ export const DesktopNotificationCard = ({
|
||||
</div>
|
||||
) : null}
|
||||
<div className={styles.title}>{title || errorTitle}</div>
|
||||
|
||||
{action ? (
|
||||
<div className={clsx(styles.headAlignWrapper, styles.action)}>
|
||||
<Button
|
||||
className={styles.actionButton}
|
||||
onClick={onActionClicked}
|
||||
{...action.buttonProps}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
data-float={!!thumb}
|
||||
className={clsx(styles.headAlignWrapper, styles.closeButton)}
|
||||
@@ -83,8 +64,42 @@ export const DesktopNotificationCard = ({
|
||||
<main data-align={alignMessage} className={styles.main}>
|
||||
{notification.message}
|
||||
</main>
|
||||
<footer>{footer}</footer>
|
||||
<footer>
|
||||
<FlexWrapper marginTop={8} justifyContent="flex-end" gap="12px">
|
||||
{actions?.map(action => (
|
||||
<NotificationCardAction
|
||||
key={action.key}
|
||||
action={action}
|
||||
onDismiss={onDismiss}
|
||||
/>
|
||||
))}
|
||||
</FlexWrapper>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NotificationCardAction = ({
|
||||
action,
|
||||
onDismiss,
|
||||
}: NotificationActionProps) => {
|
||||
const onActionClicked = useCallback(() => {
|
||||
action.onClick()?.catch(console.error);
|
||||
if (action.autoClose !== false) {
|
||||
onDismiss?.();
|
||||
}
|
||||
}, [action, onDismiss]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="plain"
|
||||
data-testid={action.key}
|
||||
className={styles.actionButton}
|
||||
onClick={onActionClicked}
|
||||
{...action.buttonProps}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -55,14 +55,14 @@ export const title = style({
|
||||
fontSize: 15,
|
||||
marginRight: 10,
|
||||
});
|
||||
export const action = style({
|
||||
marginRight: 16,
|
||||
});
|
||||
|
||||
export const actionButton = style({
|
||||
color: actionTextColor,
|
||||
position: 'relative',
|
||||
background: 'transparent',
|
||||
border: 'none',
|
||||
fontSize: cssVar('fontSm'),
|
||||
lineHeight: '22px',
|
||||
});
|
||||
export const closeButton = style({
|
||||
selectors: {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCallback, useState } from 'react';
|
||||
|
||||
import { Button, IconButton } from '../../button';
|
||||
import { Modal } from '../../modal';
|
||||
import type { NotificationCardProps } from '../types';
|
||||
import type { NotificationActionProps, NotificationCardProps } from '../types';
|
||||
import { getCardVars } from '../utils';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
@@ -70,8 +70,7 @@ const MobileNotifyDetail = ({
|
||||
iconColor,
|
||||
title,
|
||||
message,
|
||||
footer,
|
||||
action,
|
||||
actions,
|
||||
error,
|
||||
} = notification;
|
||||
const t = useI18n();
|
||||
@@ -87,12 +86,6 @@ const MobileNotifyDetail = ({
|
||||
},
|
||||
[onClose]
|
||||
);
|
||||
const onActionClicked = useCallback(() => {
|
||||
action?.onClick()?.catch(console.error);
|
||||
if (action?.autoClose !== false) {
|
||||
onClose?.();
|
||||
}
|
||||
}, [action, onClose]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -114,14 +107,33 @@ const MobileNotifyDetail = ({
|
||||
<main className={styles.detailContent}>{message}</main>
|
||||
{/* actions */}
|
||||
<div className={styles.detailActions}>
|
||||
{action ? (
|
||||
<Button onClick={onActionClicked} {...action.buttonProps}>
|
||||
{action.label}
|
||||
</Button>
|
||||
) : null}
|
||||
{footer}
|
||||
{actions?.map(action => (
|
||||
<NotificationCardAction
|
||||
key={action.key}
|
||||
action={action}
|
||||
onDismiss={onClose}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const NotificationCardAction = ({
|
||||
action,
|
||||
onDismiss,
|
||||
}: NotificationActionProps) => {
|
||||
const onActionClicked = useCallback(() => {
|
||||
action.onClick()?.catch(console.error);
|
||||
if (action.autoClose !== false) {
|
||||
onDismiss?.();
|
||||
}
|
||||
}, [action, onDismiss]);
|
||||
|
||||
return (
|
||||
<Button onClick={onActionClicked} {...action.buttonProps}>
|
||||
{action.label}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -181,10 +181,13 @@ export const WithAction: StoryFn = () => {
|
||||
),
|
||||
style,
|
||||
theme,
|
||||
action: {
|
||||
label: 'UNDO',
|
||||
onClick: () => console.log('undo'),
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
key: 'undo',
|
||||
label: 'UNDO',
|
||||
onClick: () => console.log('undo'),
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
>
|
||||
@@ -204,11 +207,14 @@ export const WithAction: StoryFn = () => {
|
||||
{
|
||||
title: 'Disable auto close',
|
||||
message: 'Test with disable auto close',
|
||||
action: {
|
||||
label: 'UNDO',
|
||||
onClick: () => console.log('undo'),
|
||||
autoClose: false,
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
key: 'undo',
|
||||
label: 'UNDO',
|
||||
onClick: () => console.log('undo'),
|
||||
autoClose: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ duration: 22222222 }
|
||||
);
|
||||
@@ -296,25 +302,12 @@ export const DifferentSize: StoryFn = () => {
|
||||
{ duration: 60000 }
|
||||
);
|
||||
};
|
||||
const openWithFooter = () => {
|
||||
notify(
|
||||
{
|
||||
title: 'With footer',
|
||||
message: 'With basic title and one line message',
|
||||
footer: (
|
||||
<Button onClick={() => console.log('clicked')}>Click me</Button>
|
||||
),
|
||||
},
|
||||
{ duration: 60000 }
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Root style={{ display: 'flex', gap: 8 }}>
|
||||
<Button onClick={openTiny}>Open Tiny</Button>
|
||||
<Button onClick={openNormal}>Open Normal</Button>
|
||||
<Button onClick={openLarge}>Open Large</Button>
|
||||
<Button onClick={openWithThumb}>Open with thumb</Button>
|
||||
<Button onClick={openWithFooter}>Open with footer</Button>
|
||||
</Root>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,8 @@ export interface Notification {
|
||||
background?: string;
|
||||
foreground?: string;
|
||||
alignMessage?: 'title' | 'icon';
|
||||
action?: {
|
||||
actions?: {
|
||||
key: string;
|
||||
label: ReactNode;
|
||||
onClick: (() => void) | (() => Promise<void>);
|
||||
buttonProps?: ButtonProps;
|
||||
@@ -22,7 +23,7 @@ export interface Notification {
|
||||
* @default true
|
||||
*/
|
||||
autoClose?: boolean;
|
||||
};
|
||||
}[];
|
||||
|
||||
rootAttrs?: HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
@@ -33,7 +34,6 @@ export interface Notification {
|
||||
error?: UserFriendlyError;
|
||||
icon?: ReactNode;
|
||||
iconColor?: string;
|
||||
footer?: ReactNode;
|
||||
|
||||
// events
|
||||
onDismiss?: () => void;
|
||||
@@ -50,3 +50,8 @@ export interface NotificationCustomRendererProps {
|
||||
export interface NotificationCardProps extends HTMLAttributes<HTMLDivElement> {
|
||||
notification: Notification;
|
||||
}
|
||||
|
||||
export interface NotificationActionProps {
|
||||
action: NonNullable<Notification['actions']>[number];
|
||||
onDismiss: Notification['onDismiss'];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user