mirror of https://github.com/buster-so/buster.git
add basic toaster
This commit is contained in:
parent
59beb494c4
commit
c301821c4f
|
@ -87,6 +87,7 @@
|
||||||
"react-virtualized-auto-sizer": "^1.0.25",
|
"react-virtualized-auto-sizer": "^1.0.25",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
|
"sonner": "^2.0.1",
|
||||||
"split-pane-react": "^0.1.3",
|
"split-pane-react": "^0.1.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"utility-types": "^3.11.0",
|
"utility-types": "^3.11.0",
|
||||||
|
@ -2412,6 +2413,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
|
"extraneous": true,
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -23496,6 +23498,16 @@
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sonner": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
|
|
|
@ -96,6 +96,7 @@
|
||||||
"react-virtualized-auto-sizer": "^1.0.25",
|
"react-virtualized-auto-sizer": "^1.0.25",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
|
"sonner": "^2.0.1",
|
||||||
"split-pane-react": "^0.1.3",
|
"split-pane-react": "^0.1.3",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
"utility-types": "^3.11.0",
|
"utility-types": "^3.11.0",
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
import { Toaster } from 'sonner';
|
||||||
|
|
||||||
|
type ToasterProps = React.ComponentProps<typeof Toaster>;
|
||||||
|
|
||||||
|
const AppToaster = ({ ...props }: ToasterProps) => {
|
||||||
|
const { theme = 'system' } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toaster
|
||||||
|
position="top-center"
|
||||||
|
theme={theme as ToasterProps['theme']}
|
||||||
|
className="toaster group"
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
toast:
|
||||||
|
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||||
|
description: 'group-[.toast]:text-muted-foreground',
|
||||||
|
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||||
|
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { AppToaster };
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './AppToaster';
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { PropsWithChildren } from 'react';
|
import React, { PropsWithChildren } from 'react';
|
||||||
import { App, ModalFuncProps } from 'antd';
|
// import { App, ModalFuncProps } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { toast, type ExternalToast } from 'sonner';
|
||||||
|
// import { createStyles } from 'antd-style';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import {
|
import {
|
||||||
useContextSelector,
|
useContextSelector,
|
||||||
|
@ -18,39 +19,57 @@ export interface NotificationProps {
|
||||||
duration?: number;
|
duration?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = createStyles(({ token, css }) => ({
|
// const useStyles = createStyles(({ token, css }) => ({
|
||||||
modal: css`
|
// modal: css`
|
||||||
.busterv2-modal-body {
|
// .busterv2-modal-body {
|
||||||
padding: 0px !important;
|
// padding: 0px !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.busterv2-modal-confirm-body {
|
// .busterv2-modal-confirm-body {
|
||||||
padding: 24px 32px 16px 32px !important;
|
// padding: 24px 32px 16px 32px !important;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.busterv2-modal-confirm-btns {
|
// .busterv2-modal-confirm-btns {
|
||||||
margin-top: 0px !important;
|
// margin-top: 0px !important;
|
||||||
padding: 12px 32px !important;
|
// padding: 12px 32px !important;
|
||||||
border-top: 0.5px solid ${token.colorBorder};
|
// border-top: 0.5px solid ${token.colorBorder};
|
||||||
display: flex;
|
// display: flex;
|
||||||
align-items: center;
|
// align-items: center;
|
||||||
justify-content: flex-end;
|
// justify-content: flex-end;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.busterv2-modal-confirm-content {
|
// .busterv2-modal-confirm-content {
|
||||||
color: ${token.colorTextSecondary} !important;
|
// color: ${token.colorTextSecondary} !important;
|
||||||
}
|
// }
|
||||||
`
|
// `
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
export const useBusterNotificationsInternal = () => {
|
export const useBusterNotificationsInternal = () => {
|
||||||
const { message, notification, modal } = App.useApp();
|
// const { message, notification, modal } = App.useApp();
|
||||||
|
// const { cx, styles } = useStyles();
|
||||||
const { cx, styles } = useStyles();
|
|
||||||
|
|
||||||
const openNotification = useMemoizedFn(
|
const openNotification = useMemoizedFn(
|
||||||
(props: { title?: string; message: string; type: NotificationType }) => {
|
(props: { title?: string; message: string; type: NotificationType }) => {
|
||||||
notification?.open?.({ ...props, description: props.message, message: props.title });
|
const { title, message, type } = props;
|
||||||
|
|
||||||
|
const toastOptions: ExternalToast = {
|
||||||
|
description: message,
|
||||||
|
position: 'top-center'
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'success':
|
||||||
|
return toast.success(title, toastOptions);
|
||||||
|
case 'info':
|
||||||
|
return toast.info(title, toastOptions);
|
||||||
|
case 'warning':
|
||||||
|
return toast.warning(title, toastOptions);
|
||||||
|
case 'error':
|
||||||
|
return toast.error(title, toastOptions);
|
||||||
|
default:
|
||||||
|
const _never: never = type;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,24 +78,24 @@ export const useBusterNotificationsInternal = () => {
|
||||||
const type = values.type || 'error';
|
const type = values.type || 'error';
|
||||||
const title = values.title || 'Error';
|
const title = values.title || 'Error';
|
||||||
const message = values.message || 'Something went wrong. Please try again.';
|
const message = values.message || 'Something went wrong. Please try again.';
|
||||||
openNotification({ ...values, message, title, type });
|
return openNotification({ ...values, message, title, type });
|
||||||
});
|
});
|
||||||
|
|
||||||
const openInfoNotification = useMemoizedFn(
|
const openInfoNotification = useMemoizedFn(
|
||||||
({ type = 'info', message = 'Info', title = 'Info', ...props }: NotificationProps) => {
|
({ type = 'info', message = 'Info', title = 'Info', ...props }: NotificationProps) => {
|
||||||
openNotification({ ...props, title, message, type });
|
return openNotification({ ...props, title, message, type });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const openSuccessNotification = useMemoizedFn(
|
const openSuccessNotification = useMemoizedFn(
|
||||||
({ type = 'success', title = 'Success', message = 'success', ...props }: NotificationProps) => {
|
({ type = 'success', title = 'Success', message = 'success', ...props }: NotificationProps) => {
|
||||||
openNotification({ ...props, message, title, type });
|
return openNotification({ ...props, message, title, type });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const openWarningNotification = useMemoizedFn(
|
const openWarningNotification = useMemoizedFn(
|
||||||
({ type = 'warning', title = 'Warning', message = 'Warning', ...props }: NotificationProps) => {
|
({ type = 'warning', title = 'Warning', message = 'Warning', ...props }: NotificationProps) => {
|
||||||
openNotification({ ...props, message, title, type });
|
return openNotification({ ...props, message, title, type });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -86,28 +105,27 @@ export const useBusterNotificationsInternal = () => {
|
||||||
(props: {
|
(props: {
|
||||||
type: NotificationType;
|
type: NotificationType;
|
||||||
message: string;
|
message: string;
|
||||||
loading?: boolean;
|
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
}) => {
|
}) => {
|
||||||
if (props.loading) {
|
return openNotification({
|
||||||
message.loading(props.message, props.duration, props.onClose);
|
...props,
|
||||||
} else {
|
title: props.message,
|
||||||
message?.[props.type]?.(props.message, props.duration, props.onClose);
|
message: ''
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const openErrorMessage = useMemoizedFn((message: string) => {
|
const openErrorMessage = useMemoizedFn((message: string) => {
|
||||||
openMessage({ type: 'error', message });
|
return openMessage({ type: 'error', message });
|
||||||
});
|
});
|
||||||
|
|
||||||
const openInfoMessage = useMemoizedFn((message: string, duration?: number) => {
|
const openInfoMessage = useMemoizedFn((message: string, duration?: number) => {
|
||||||
openMessage({ type: 'info', message, duration });
|
return openMessage({ type: 'info', message, duration });
|
||||||
});
|
});
|
||||||
|
|
||||||
const openSuccessMessage = useMemoizedFn((message: string) => {
|
const openSuccessMessage = useMemoizedFn((message: string) => {
|
||||||
openMessage({ type: 'success', message });
|
return openMessage({ type: 'success', message });
|
||||||
});
|
});
|
||||||
|
|
||||||
const openConfirmModal = useMemoizedFn(
|
const openConfirmModal = useMemoizedFn(
|
||||||
|
@ -117,36 +135,34 @@ export const useBusterNotificationsInternal = () => {
|
||||||
onOk: () => void;
|
onOk: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
okButtonProps?: ModalFuncProps['okButtonProps'];
|
|
||||||
cancelButtonProps?: ModalFuncProps['cancelButtonProps'];
|
|
||||||
width?: string | number;
|
width?: string | number;
|
||||||
useReject?: boolean;
|
useReject?: boolean;
|
||||||
}): Promise<void> => {
|
}): Promise<void> => {
|
||||||
const useReject = props.useReject ?? true;
|
const useReject = props.useReject ?? true;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
modal.confirm({
|
// modal.confirm({
|
||||||
icon: props.icon || <></>,
|
// icon: props.icon || <></>,
|
||||||
...props,
|
// ...props,
|
||||||
className: cx(styles.modal, ''),
|
// className: cx(styles.modal, ''),
|
||||||
cancelButtonProps: {
|
// cancelButtonProps: {
|
||||||
...props.cancelButtonProps,
|
// ...props.cancelButtonProps,
|
||||||
type: 'text'
|
// type: 'text'
|
||||||
},
|
// },
|
||||||
okButtonProps: {
|
// okButtonProps: {
|
||||||
...props.okButtonProps,
|
// ...props.okButtonProps,
|
||||||
type: 'default'
|
// type: 'default'
|
||||||
},
|
// },
|
||||||
onOk: async () => {
|
// onOk: async () => {
|
||||||
await props.onOk();
|
// await props.onOk();
|
||||||
resolve();
|
// resolve();
|
||||||
},
|
// },
|
||||||
onCancel: async () => {
|
// onCancel: async () => {
|
||||||
await props.onCancel?.();
|
// await props.onCancel?.();
|
||||||
if (useReject) reject();
|
// if (useReject) reject();
|
||||||
else resolve();
|
// else resolve();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
createContext,
|
createContext,
|
||||||
ContextSelector
|
ContextSelector
|
||||||
} from '@fluentui/react-context-selector';
|
} from '@fluentui/react-context-selector';
|
||||||
|
import { AppToaster } from '@/components/ui/toaster';
|
||||||
|
|
||||||
export const ENABLE_DARK_MODE = false;
|
export const ENABLE_DARK_MODE = false;
|
||||||
|
|
||||||
|
@ -31,7 +32,10 @@ export const BusterStyleProvider: React.FC<PropsWithChildren<{}>> = ({ children
|
||||||
enableSystem={ENABLE_DARK_MODE}
|
enableSystem={ENABLE_DARK_MODE}
|
||||||
themes={['light', 'dark']}
|
themes={['light', 'dark']}
|
||||||
disableTransitionOnChange>
|
disableTransitionOnChange>
|
||||||
<BaseBusterStyleProvider>{children}</BaseBusterStyleProvider>
|
<BaseBusterStyleProvider>
|
||||||
|
{children}
|
||||||
|
<AppToaster />
|
||||||
|
</BaseBusterStyleProvider>
|
||||||
</NextThemeProvider>
|
</NextThemeProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue