mirror of https://github.com/vizality/vizality
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
5.9 KiB
218 lines
5.9 KiB
/**
|
|
*
|
|
*/
|
|
|
|
import { Button, Icon, Flex, LazyImage, ContextMenu, Markdown } from '@vizality/components';
|
|
import { getModule, contextMenu } from '@vizality/webpack';
|
|
import { joinClassNames } from '@vizality/util/dom';
|
|
import React, { memo } from 'react';
|
|
|
|
const { openContextMenu, closeContextMenu } = contextMenu;
|
|
|
|
/**
|
|
*
|
|
* @component
|
|
*/
|
|
const Content = memo(({ markdown, content }) => {
|
|
const { thin } = getModule('thin');
|
|
return (
|
|
<div className={joinClassNames('vz-toast-content', thin)}>
|
|
{markdown
|
|
? <Markdown source={content} />
|
|
: content
|
|
}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
/**
|
|
*
|
|
* @component
|
|
*/
|
|
const CloseButton = memo(({ toastId }) => (
|
|
<Icon
|
|
name='Close'
|
|
size='20'
|
|
className='vz-toast-close-wrapper'
|
|
iconClassName='vz-toast-close'
|
|
onClick={evt => evt.shiftKey ? vizality.api.notifications.closeAllActiveToasts() : vizality.api.notifications.closeToast(toastId)}
|
|
/>
|
|
));
|
|
|
|
/**
|
|
*
|
|
* @component
|
|
*/
|
|
const Header = memo(({ toastId, icon, markdown, header, content, buttons }) => {
|
|
return (
|
|
<div className='vz-toast-meta-wrapper'>
|
|
{(icon?.url || icon?.name) && (
|
|
<div className='vz-toast-decorator'>
|
|
{icon.url && !icon.name && (
|
|
<LazyImage
|
|
src={icon.url}
|
|
width={icon.size || '40'}
|
|
height={icon.size || '40'}
|
|
className='vz-toast-icon'
|
|
/>
|
|
)}
|
|
{icon.name && (
|
|
<Icon
|
|
size={icon.size || '40'}
|
|
className='vz-toast-icon-wrapper'
|
|
iconClassName='vz-toast-icon'
|
|
color={icon.color}
|
|
name={icon.name}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
<div className='vz-toast-meta'>
|
|
<div className='vz-toast-header'>
|
|
{markdown
|
|
? <Markdown source={header} />
|
|
: header
|
|
}
|
|
</div>
|
|
{!content && !buttons?.length && (
|
|
<CloseButton toastId={toastId} />
|
|
)}
|
|
{content && <Content markdown={markdown} content={content} />}
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
/**
|
|
*
|
|
* @component
|
|
*/
|
|
const Footer = memo(({ buttons }) => {
|
|
const { horizontal } = getModule('buttonsWrapper', 'horizontal');
|
|
const { marginTop20 } = getModule('marginTop20');
|
|
return (
|
|
<Flex className={joinClassNames('vz-toast-footer', marginTop20)}>
|
|
<Flex className={joinClassNames('vz-toast-footer-buttons', horizontal)} justify={Flex.Justify.END}>
|
|
{buttons.map((button, i) => {
|
|
const btnProps = {};
|
|
[ 'size', 'look', 'color' ].forEach(prop => {
|
|
if (button[prop]) {
|
|
const value = button[prop].includes('-')
|
|
? button[prop]
|
|
: Button[`${prop.charAt(0).toUpperCase() + prop.slice(1)}s`][button[prop].toUpperCase()];
|
|
if (value) {
|
|
btnProps[prop] = value;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (!btnProps.size) {
|
|
btnProps.size = Button.Sizes.SMALL;
|
|
}
|
|
|
|
return (
|
|
<Button
|
|
key={i}
|
|
{...btnProps}
|
|
onClick={evt => {
|
|
if (button.onClick) {
|
|
button.onClick(evt);
|
|
}
|
|
}}
|
|
>
|
|
{button.text}
|
|
</Button>
|
|
);
|
|
})}
|
|
</Flex>
|
|
</Flex>
|
|
);
|
|
});
|
|
|
|
/**
|
|
*
|
|
* @component
|
|
*/
|
|
export default memo(({ toast }) => {
|
|
const Toast =
|
|
typeof toast.custom === 'function'
|
|
? toast.custom
|
|
: () => toast.custom;
|
|
|
|
/**
|
|
* We only need to insert a close button if it's not a custom toast, has content, and
|
|
* the showCloseButton property is true.
|
|
*/
|
|
if (toast.showCloseButton && !toast.custom && toast.content) {
|
|
const CloseButton = {
|
|
text: 'Dismiss',
|
|
color: 'white',
|
|
look: 'link',
|
|
onClick: evt => evt.shiftKey ? vizality.api.notifications.closeAllActiveToasts() : vizality.api.notifications.closeToast(toast.id)
|
|
};
|
|
/**
|
|
* If there are button(s) already, insert a close button into the beginning
|
|
* of the array.
|
|
*/
|
|
if (toast.buttons?.length) {
|
|
/**
|
|
* Let's give the close button the same size as the last button specified.
|
|
*/
|
|
CloseButton.size = toast.buttons[toast.buttons.length - 1].size;
|
|
toast.buttons.unshift(CloseButton);
|
|
} else {
|
|
CloseButton.text = 'Okay';
|
|
CloseButton.color = 'white';
|
|
CloseButton.look = 'filled';
|
|
toast.buttons = [ CloseButton ];
|
|
}
|
|
}
|
|
|
|
return (
|
|
toast.custom
|
|
? <Toast {...toast.custom.props} />
|
|
: (
|
|
<div
|
|
className='vz-toast-body-inner'
|
|
vz-toast-id={toast.id}
|
|
onContextMenu={evt => openContextMenu(evt, () => (
|
|
<ContextMenu.Menu navId='dashboard-item' onClose={closeContextMenu}>
|
|
<ContextMenu.Item
|
|
id='close-toast'
|
|
label='Close Toast'
|
|
action={() => vizality.api.notifications.closeToast(toast.id)}
|
|
/>
|
|
<ContextMenu.Item
|
|
id='close-all-toasts'
|
|
label='Close All Toasts'
|
|
action={() => vizality.api.notifications.closeAllActiveToasts()}
|
|
/>
|
|
<ContextMenu.Item
|
|
id='view-toast-settings'
|
|
label='View Toast Settings'
|
|
action={() => vizality.api.routes.navigateTo('settings/notifications#toasts')}
|
|
/>
|
|
</ContextMenu.Menu>
|
|
))}
|
|
>
|
|
<Header
|
|
markdown={toast.markdown}
|
|
image={toast.image}
|
|
icon={toast.icon}
|
|
buttons={toast.buttons}
|
|
header={toast.header}
|
|
toastId={toast.id}
|
|
content={toast.content}
|
|
/>
|
|
{toast.buttons?.length && (
|
|
<Footer
|
|
buttons={toast.buttons}
|
|
toastId={toast.id}
|
|
showCloseButton={toast.showCloseButton}
|
|
/>
|
|
)}
|
|
</div>
|
|
)
|
|
);
|
|
});
|