mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-27 06:33:32 +03:00
feat: rewrite message component
This commit is contained in:
parent
d5f6ab0bb5
commit
0cce817813
@ -6,9 +6,7 @@ import { copy } from './copy';
|
||||
const IconBooth: FC<{ name: string; Icon: FC<any> }> = ({ name, Icon }) => {
|
||||
const on_click = () => {
|
||||
copy(`<${name} />`);
|
||||
message.success({
|
||||
content: 'Copied.',
|
||||
});
|
||||
message.success('Copied ~');
|
||||
};
|
||||
return (
|
||||
<IconContainer title={name} onClick={on_click}>
|
||||
|
@ -14,6 +14,7 @@
|
||||
"@mui/x-date-pickers-pro": "^5.0.0-alpha.7",
|
||||
"@types/react-date-range": "^1.4.4",
|
||||
"clsx": "^1.2.0",
|
||||
"notistack": "^2.0.5",
|
||||
"react-date-range": "^1.4.0"
|
||||
}
|
||||
}
|
||||
|
83
libs/components/ui/src/message/Message.tsx
Normal file
83
libs/components/ui/src/message/Message.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import {
|
||||
NotificationInstance,
|
||||
type NotificationController,
|
||||
type NotificationInstanceProps,
|
||||
type NotificationContent,
|
||||
type NotificationOption,
|
||||
type NotificationKey,
|
||||
} from '../notification';
|
||||
import {
|
||||
SuccessMessage,
|
||||
ErrorMessage,
|
||||
InfoMessage,
|
||||
WarningMessage,
|
||||
} from './MessageContent';
|
||||
|
||||
type MessageMethod = (
|
||||
message: NotificationContent,
|
||||
option?: Omit<NotificationOption, 'content'>
|
||||
) => Promise<NotificationKey>;
|
||||
|
||||
export class Message {
|
||||
private _notificationController: NotificationController = null;
|
||||
private _notificationProps: NotificationInstanceProps = {};
|
||||
|
||||
constructor(props: NotificationInstanceProps) {
|
||||
this._notificationProps = props;
|
||||
}
|
||||
|
||||
private _ensureController() {
|
||||
if (!this._notificationController) {
|
||||
NotificationInstance(
|
||||
this._notificationProps,
|
||||
notificationController => {
|
||||
this._notificationController = notificationController;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public success: MessageMethod = async (message, option) => {
|
||||
await this._ensureController();
|
||||
|
||||
return this._notificationController.add(message, {
|
||||
content: (key, message) => (
|
||||
<SuccessMessage id={key} message={message} />
|
||||
),
|
||||
...option,
|
||||
});
|
||||
};
|
||||
|
||||
public error: MessageMethod = async (message, option) => {
|
||||
await this._ensureController();
|
||||
|
||||
return this._notificationController.add(message, {
|
||||
content: (key, message) => (
|
||||
<ErrorMessage id={key} message={message} />
|
||||
),
|
||||
...option,
|
||||
});
|
||||
};
|
||||
|
||||
public warning: MessageMethod = async (message, option) => {
|
||||
await this._ensureController();
|
||||
|
||||
return this._notificationController.add(message, {
|
||||
content: (key, message) => (
|
||||
<WarningMessage id={key} message={message} />
|
||||
),
|
||||
...option,
|
||||
});
|
||||
};
|
||||
|
||||
public info: MessageMethod = async (message, option) => {
|
||||
await this._ensureController();
|
||||
|
||||
return this._notificationController.add(message, {
|
||||
content: (key, message) => (
|
||||
<InfoMessage id={key} message={message} />
|
||||
),
|
||||
...option,
|
||||
});
|
||||
};
|
||||
}
|
69
libs/components/ui/src/message/MessageContent.tsx
Normal file
69
libs/components/ui/src/message/MessageContent.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { NotificationContentProps } from '../notification';
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import Alert from '@mui/material/Alert';
|
||||
|
||||
// TODO: Variants types of message content await designers
|
||||
const commonStyle = { background: '#fff' };
|
||||
export const SuccessMessage = forwardRef<
|
||||
HTMLDivElement,
|
||||
NotificationContentProps
|
||||
>(({ message, id }, ref) => {
|
||||
return (
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="success"
|
||||
ref={ref}
|
||||
style={commonStyle}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
);
|
||||
});
|
||||
|
||||
export const ErrorMessage = forwardRef<
|
||||
HTMLDivElement,
|
||||
NotificationContentProps
|
||||
>(({ message, id }, ref) => {
|
||||
return (
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="error"
|
||||
ref={ref}
|
||||
style={commonStyle}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
);
|
||||
});
|
||||
|
||||
export const WarningMessage = forwardRef<
|
||||
HTMLDivElement,
|
||||
NotificationContentProps
|
||||
>(({ message, id }, ref) => {
|
||||
return (
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="warning"
|
||||
ref={ref}
|
||||
style={commonStyle}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
);
|
||||
});
|
||||
|
||||
export const InfoMessage = forwardRef<HTMLDivElement, NotificationContentProps>(
|
||||
({ message, id }, ref) => {
|
||||
return (
|
||||
<Alert
|
||||
variant="outlined"
|
||||
severity="info"
|
||||
ref={ref}
|
||||
style={commonStyle}
|
||||
>
|
||||
{message}
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
);
|
@ -1,48 +0,0 @@
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { styled } from '../styled';
|
||||
|
||||
import type { ContainerProps } from './types';
|
||||
|
||||
interface ShowProps {
|
||||
Container: FC<ContainerProps>;
|
||||
/**
|
||||
* 自动关闭延时,单位毫秒。设为 0 时,不自动关闭。默认 2000
|
||||
*/
|
||||
duration?: number;
|
||||
content: ReactNode;
|
||||
}
|
||||
|
||||
export const show = ({ Container, duration = 2000, content }: ShowProps) => {
|
||||
const root_element = document.createElement('div');
|
||||
document.body.appendChild(root_element);
|
||||
|
||||
function close() {
|
||||
document.body.removeChild(root_element);
|
||||
}
|
||||
|
||||
const react_root = createRoot(root_element);
|
||||
|
||||
react_root.render(
|
||||
<PortalContainer>
|
||||
<Container content={content} duration={duration} close={close} />
|
||||
</PortalContainer>
|
||||
);
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
close();
|
||||
}, duration);
|
||||
}
|
||||
|
||||
return close;
|
||||
};
|
||||
|
||||
const PortalContainer = styled('div')({
|
||||
position: 'fixed',
|
||||
top: '100px',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
backgroundColor: '#fff',
|
||||
zIndex: 100,
|
||||
});
|
@ -1,18 +1,9 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { Message } from './Message';
|
||||
|
||||
import { show } from './base';
|
||||
import { Success } from './success';
|
||||
|
||||
interface SuccessProps {
|
||||
/**
|
||||
* 自动关闭延时,单位毫秒。默认2000
|
||||
*/
|
||||
duration?: number;
|
||||
content: ReactNode;
|
||||
}
|
||||
|
||||
export const message = {
|
||||
success({ duration, content }: SuccessProps) {
|
||||
return show({ Container: Success, duration, content });
|
||||
export const message = new Message({
|
||||
anchorOrigin: {
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
},
|
||||
};
|
||||
autoHideDuration: 2000,
|
||||
});
|
||||
|
@ -1,14 +0,0 @@
|
||||
import type { FC } from 'react';
|
||||
import { styled } from '../styled';
|
||||
import type { ContainerProps } from './types';
|
||||
|
||||
export const Success: FC<ContainerProps> = ({ content }) => {
|
||||
return <Container>{content}</Container>;
|
||||
};
|
||||
|
||||
const Container = styled('div')({
|
||||
maxWidth: '200px',
|
||||
backgroundColor: 'rgba(64, 223, 155)',
|
||||
borderRadius: '4px',
|
||||
padding: '8px',
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface ContainerProps {
|
||||
content: ReactNode;
|
||||
duration: number;
|
||||
close: () => void;
|
||||
}
|
62
libs/components/ui/src/notification/Notification.tsx
Normal file
62
libs/components/ui/src/notification/Notification.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
|
||||
import {
|
||||
SnackbarProvider,
|
||||
useSnackbar,
|
||||
type SnackbarProviderProps,
|
||||
type OptionsObject,
|
||||
type SnackbarMessage,
|
||||
type SnackbarKey,
|
||||
} from 'notistack';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
export type NotificationController = {
|
||||
add: (message: SnackbarMessage, options?: OptionsObject) => SnackbarKey;
|
||||
close: (key?: SnackbarKey) => void;
|
||||
};
|
||||
export type NotificationInstanceProps = Omit<SnackbarProviderProps, 'children'>;
|
||||
export type NotificationContentProps = {
|
||||
id: SnackbarKey;
|
||||
message: SnackbarMessage;
|
||||
};
|
||||
export type NotificationContent = SnackbarMessage;
|
||||
export type NotificationOption = OptionsObject;
|
||||
export type NotificationKey = SnackbarKey;
|
||||
|
||||
const Notification = forwardRef<NotificationController>((props, ref) => {
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
add: (message, options) => {
|
||||
return enqueueSnackbar(message, options);
|
||||
},
|
||||
close: key => closeSnackbar(key),
|
||||
}));
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
export const NotificationInstance = (
|
||||
snackbarProviderProps: NotificationInstanceProps,
|
||||
callback: (notificationController: NotificationController) => void
|
||||
) => {
|
||||
const rootElement = document.createElement('div');
|
||||
document.body.appendChild(rootElement);
|
||||
const reactRoot = createRoot(rootElement);
|
||||
// ReactDOM.unmountComponentAtNode will call the ref function again after execution, called to prevent repeated calls
|
||||
let called = false;
|
||||
// react mounted dom is async
|
||||
const ref = async (notificationController: NotificationController) => {
|
||||
if (called) {
|
||||
return;
|
||||
}
|
||||
called = true;
|
||||
callback(notificationController);
|
||||
};
|
||||
|
||||
reactRoot.render(
|
||||
<SnackbarProvider {...snackbarProviderProps}>
|
||||
<Notification ref={ref} />
|
||||
</SnackbarProvider>
|
||||
);
|
||||
};
|
1
libs/components/ui/src/notification/index.ts
Normal file
1
libs/components/ui/src/notification/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Notification';
|
Loading…
Reference in New Issue
Block a user