mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Cb 3587 migrate snackbars to css modules (#1842)
* CB-3587 add css modules, remove reshadow styles from snackbars * CB-3587 fix notification cross button style --------- Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
This commit is contained in:
co-authored by
mr-anton-t
parent
664864d478
commit
45c201c465
@@ -0,0 +1,30 @@
|
||||
.notificationBody {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
& .bodyTextBlock {
|
||||
margin-top: 8px;
|
||||
padding-right: 24px;
|
||||
& .message {
|
||||
font-size: 16px;
|
||||
opacity: 0.8;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.textBlockTitle {
|
||||
composes: theme-typography--headline6 from global;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: 1.55rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -5,40 +5,9 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
const SNACKBAR_BODY_STYLES = css`
|
||||
notification-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
& body-text-block {
|
||||
margin-top: 8px;
|
||||
padding-right: 24px;
|
||||
& message {
|
||||
font-size: 16px;
|
||||
opacity: 0.8;
|
||||
overflow: auto;
|
||||
max-height: 200px;
|
||||
margin-bottom: 8px;
|
||||
word-break: break-word;
|
||||
white-space: pre-line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text-block-title {
|
||||
composes: theme-typography--headline6 from global;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: 1.55rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`;
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import style from './SnackbarBody.m.css';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -46,14 +15,17 @@ interface Props {
|
||||
}
|
||||
|
||||
export const SnackbarBody: React.FC<React.PropsWithChildren<Props>> = function SnackbarBody({ title, className, children }) {
|
||||
return styled(SNACKBAR_BODY_STYLES)(
|
||||
<notification-body as="div" className={className}>
|
||||
<body-text-block as="div">
|
||||
<text-block-title title={title} as="h2">
|
||||
const styles = useS(style);
|
||||
return (
|
||||
<div data-testid="notification-body" className={s(styles, { notificationBody: true }, className)}>
|
||||
<div data-testid="body-text-block" className={styles.bodyTextBlock}>
|
||||
<h2 data-testid="text-block-title" title={title} className={styles.textBlockTitle}>
|
||||
{title}
|
||||
</text-block-title>
|
||||
<message as="div">{children}</message>
|
||||
</body-text-block>
|
||||
</notification-body>,
|
||||
</h2>
|
||||
<div data-testid="message" className={styles.message}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
.notificationContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -5,25 +5,19 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
const SNACKBAR_CONTENT_STYLES = css`
|
||||
notification-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import style from './SnackbarContent.m.css';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const SnackbarContent: React.FC<React.PropsWithChildren<Props>> = function SnackbarContent({ children, className }) {
|
||||
return styled(SNACKBAR_CONTENT_STYLES)(
|
||||
<notification-content as="div" className={className}>
|
||||
const styles = useS(style);
|
||||
return (
|
||||
<div data-testid="notification-content" className={s(styles, { notificationContent: true }, className)}>
|
||||
{children}
|
||||
</notification-content>,
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.notificationFooter {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.footerTime {
|
||||
composes: theme-typography--caption from global;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.actions:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.actions > *:not(:first-child) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
@@ -5,25 +5,9 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
const SNACKBAR_FOOTER_STYLES = css`
|
||||
notification-footer {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
}
|
||||
footer-time {
|
||||
composes: theme-typography--caption from global;
|
||||
opacity: 0.7;
|
||||
}
|
||||
actions:empty {
|
||||
display: none;
|
||||
}
|
||||
actions > *:not(:first-child) {
|
||||
margin-left: 16px;
|
||||
}
|
||||
`;
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import style from './SnackbarFooter.m.css';
|
||||
|
||||
interface Props {
|
||||
timestamp: number;
|
||||
@@ -31,12 +15,17 @@ interface Props {
|
||||
}
|
||||
|
||||
export const SnackbarFooter: React.FC<React.PropsWithChildren<Props>> = function SnackbarFooter({ timestamp, className, children }) {
|
||||
const styles = useS(style);
|
||||
const timeStringFromTimestamp = new Date(timestamp).toLocaleTimeString();
|
||||
|
||||
return styled(SNACKBAR_FOOTER_STYLES)(
|
||||
<notification-footer as="div" className={className}>
|
||||
<footer-time as="span">{timeStringFromTimestamp}</footer-time>
|
||||
<actions as="div">{children}</actions>
|
||||
</notification-footer>,
|
||||
return (
|
||||
<div data-testid="notification-footer" className={s(styles, { notificationFooter: true }, className)}>
|
||||
<span data-testid="footer-time" className={styles.footerTime}>
|
||||
{timeStringFromTimestamp}
|
||||
</span>
|
||||
<div data-testid="actions" className={styles.actions}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.notificationMark {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.loaderContainer {
|
||||
padding-right: 12px;
|
||||
& .loader {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
@@ -5,25 +5,13 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { ENotificationType } from '@cloudbeaver/core-events';
|
||||
|
||||
import { Loader } from '../../Loader/Loader';
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import { NotificationMark } from '../NotificationMark';
|
||||
|
||||
const SNACKBAR_STATUS_STYLES = css`
|
||||
NotificationMark {
|
||||
padding-right: 12px;
|
||||
}
|
||||
loader-container {
|
||||
padding-right: 12px;
|
||||
& Loader {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
import style from './SnackbarStatus.m.css';
|
||||
|
||||
interface SnackbarStatusProps {
|
||||
status: ENotificationType;
|
||||
@@ -31,13 +19,12 @@ interface SnackbarStatusProps {
|
||||
}
|
||||
|
||||
export const SnackbarStatus: React.FC<SnackbarStatusProps> = function SnackbarStatus({ status, className }) {
|
||||
return styled(SNACKBAR_STATUS_STYLES)(
|
||||
status === ENotificationType.Loading ? (
|
||||
<loader-container className={className}>
|
||||
<Loader fullSize hideMessage />
|
||||
</loader-container>
|
||||
) : (
|
||||
<NotificationMark className={className} type={status} />
|
||||
),
|
||||
const styles = useS(style);
|
||||
return status === ENotificationType.Loading ? (
|
||||
<div data-testid="loader-container" className={s(styles, { loaderContainer: true }, className)}>
|
||||
<Loader className={styles.loader} fullSize hideMessage />
|
||||
</div>
|
||||
) : (
|
||||
<NotificationMark className={s(styles, { notificationMark: true }, className)} type={status} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
.notification {
|
||||
composes: theme-background-surface theme-text-on-surface theme-elevation-z5 from global;
|
||||
position: relative;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 500px;
|
||||
margin-bottom: 16px;
|
||||
margin-left: 16px;
|
||||
padding: 16px 16px;
|
||||
line-height: 1.5;
|
||||
opacity: 0;
|
||||
border-radius: var(--theme-group-element-radius);
|
||||
transition: opacity 0.3s ease-in-out, transform 0.5s ease-in-out;
|
||||
transform: translateX(-100%);
|
||||
|
||||
&.mounted {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.closing {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.iconButton {
|
||||
position: absolute;
|
||||
|
||||
&.large {
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
@@ -6,46 +6,11 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled, { css, use } from 'reshadow';
|
||||
|
||||
import { IconButton } from '../../IconButton';
|
||||
|
||||
const SNACKBAR_WRAPPER_STYLES = css`
|
||||
notification {
|
||||
composes: theme-background-surface theme-text-on-surface theme-elevation-z5 from global;
|
||||
position: relative;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
width: 500px;
|
||||
margin-bottom: 16px;
|
||||
margin-left: 16px;
|
||||
padding: 16px 16px;
|
||||
line-height: 1.5;
|
||||
opacity: 0;
|
||||
border-radius: var(--theme-group-element-radius);
|
||||
transition: opacity 0.3s ease-in-out, transform 0.5s ease-in-out;
|
||||
transform: translateX(-100%);
|
||||
|
||||
&[use|mounted] {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
&[use|closing] {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
IconButton {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { s } from '../../s';
|
||||
import { useS } from '../../useS';
|
||||
import style from './SnackbarWrapper.m.css';
|
||||
|
||||
interface Props {
|
||||
closing?: boolean;
|
||||
@@ -61,16 +26,19 @@ export const SnackbarWrapper: React.FC<React.PropsWithChildren<Props>> = functio
|
||||
children,
|
||||
className,
|
||||
}) {
|
||||
const styles = useS(style);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return styled(SNACKBAR_WRAPPER_STYLES)(
|
||||
<notification as="div" className={className} {...use({ mounted, closing })}>
|
||||
return (
|
||||
<div data-testid="notification" className={s(styles, { notification: true, mounted, closing }, className)}>
|
||||
{children}
|
||||
{!persistent && onClose && <IconButton name="cross" viewBox="0 0 16 16" onClick={onClose} />}
|
||||
</notification>,
|
||||
{!persistent && onClose && (
|
||||
<IconButton name="cross" viewBox="0 0 16 16" className={s(styles, { iconButton: true, large: true })} onClick={onClose} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user