Skip to content

Commit

Permalink
feat: Taost 80%
Browse files Browse the repository at this point in the history
  • Loading branch information
NeverEllipsis committed Sep 10, 2024
1 parent d3ee5e4 commit 649d0c5
Show file tree
Hide file tree
Showing 13 changed files with 626 additions and 133 deletions.
2 changes: 1 addition & 1 deletion packages/bui-core/src/Picker/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ name: Picker 选择器

## 代码演示

### 基础弹窗
### 基础选择器

使用`open`控制选择器的打开/关闭,点击遮罩层等关闭的事件会通过`onClose`回调返回

Expand Down
74 changes: 46 additions & 28 deletions packages/bui-core/src/Toast/FunctionalToast.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRoot } from 'react-dom/client';
import { render, unmount, getRootElement } from '@bifrostui/utils';
import React, { useCallback, useEffect, useState } from 'react';
import ToastView from './Toast';
import {
Expand All @@ -21,18 +21,6 @@ const formatProps = (props) => {
return props;
};

/**
* Toast组件容器
*/
const createContainer = (
getContainer?: HTMLElement | (() => HTMLElement) | undefined,
): HTMLElement => {
const container =
typeof getContainer === 'function' ? getContainer() : getContainer;

return container || document.body;
};

/**
* 销毁全部Toast
*/
Expand All @@ -49,28 +37,49 @@ const functionalToast = (props: ToastProps) => {
duration: 2000,
position: 'center',
allowMultiple: false,
closeOnClickBackdrop: false,
disableClick: false,
...(formatProps(props) || {}),
};

const rootWrapper = document.createElement('div');
const container = createContainer();
container.appendChild(rootWrapper);

const instance: ToastReturnType = {
close: () => null,
};
const rootWrapper = document.createElement('div');
if (options.disableClick) {
const styles = {
position: 'fixed',
top: '0',
bottom: '0',
left: '0',
right: '0',
zIndex: 'var(--bui-z-index-toast)',
};
Object.keys(styles).forEach((property) => {
rootWrapper.style[property] = styles[property];
});
}

const rootElement = getRootElement();
rootElement.appendChild(rootWrapper);

const ToastComponent = () => {
const { duration, allowMultiple, ...others } = options;
const { duration, allowMultiple, onClose, ...others } = options;
const [open, setOpen] = useState(false);
let timer;
const fadeTimeout = {
enter: 350,
exit: 150,
};

const close = useCallback(() => {
setOpen(false);
if (rootWrapper.parentNode) {
rootWrapper.parentNode.removeChild(rootWrapper);
}
setTimeout(() => {
const unmountRes = unmount(rootWrapper);
if (unmountRes && rootWrapper.parentNode) {
rootWrapper.parentNode.removeChild(rootWrapper);
}
}, fadeTimeout.exit);
onClose?.();
}, [rootWrapper]);

useEffect(() => {
Expand All @@ -92,28 +101,37 @@ const functionalToast = (props: ToastProps) => {

return (
<ToastView
open={open}
onClose={() => {
setOpen(false);
}}
{...others}
open={open}
timeout={fadeTimeout}
onClose={close}
/>
);
};

const root = createRoot(rootWrapper);
root.render(<ToastComponent />);
render(<ToastComponent />, rootWrapper);

return instance;
};

/**
* Toast.warning(options: ToastOptions)
* Toast.loading(options: ToastOptions)
* Toast.success(options: ToastOptions)
* Toast.fail(options: ToastOptions)
*/
['warning', 'loading', 'success', 'fail'].forEach((methodName: ToastType) => {
functionalToast[methodName] = (options: ToastOptions) =>
functionalToast({
type: methodName,
...(formatProps(options) || {}),
});
});

/**
* 清除所有Toast
* Toast.clear()
*/
functionalToast.clear = () => {
// 处理toast还未弹出就立刻销毁的情况,将销毁放到下一个时间循环中,避免销毁失败
setTimeout(() => {
Expand Down
85 changes: 45 additions & 40 deletions packages/bui-core/src/Toast/Toast.less
Original file line number Diff line number Diff line change
@@ -1,58 +1,63 @@
@import '~@bifrostui/styles/mixins/index.less';

.bui-toast {
position: fixed;
right: unset;
bottom: unset;
font-family: var(--bui-font-family);

--min-width: 86px;
--max-width: 80%;
--font-size: var(--bui-text-size-1);
--color: var(--bui-color-white);
--padding: var(--bui-spacing-xl);
--word-break: break-all;
--top: 15%;
--bottom: 85%;
--position-top: 15%;
--position-bottom: 85%;
--background-color: rgba(0, 0, 0, 0.8);
--border-radius: var(--bui-shape-radius-default);
--text-align: center;

&-content {
&.bui-toast-allow-click {
position: fixed;
left: 50%;
z-index: var(--bui-z-index-toast);
width: fit-content;
// 无效,最大宽度取决于绝对定位的left值
max-width: 92%;
padding: var(--padding);
font-size: var(--font-size);
color: var(--color);
border-radius: var(--border-radius);
word-break: var(--word-break);
background-color: var(--background-color);

&-center {
top: 50%;
transform: translate(-50%, -50%);
}
right: unset;
bottom: unset;
}

&-top {
top: var(--top);
transform: translate(-50%, calc(-1 * var(--top)));
}
position: fixed;
left: 50%;
z-index: var(--bui-z-index-toast);
width: fit-content;
min-width: var(--min-width);
max-width: var(--max-width);
padding: var(--padding);
font-size: var(--font-size);
color: var(--color);
border-radius: var(--border-radius);
word-break: var(--word-break);
white-space: pre-wrap;
background-color: var(--background-color);
text-align: var(--text-align);
font-family: var(--bui-font-family);

&-bottom {
top: var(--bottom);
transform: translate(-50%, calc(-1 * var(--bottom)));
}
&-center {
top: 50%;
transform: translate(-50%, -50%);
}

&-top {
top: var(--position-top);
transform: translate(-50%, calc(-1 * var(--position-top)));
}

&-bottom {
top: var(--position-bottom);
transform: translate(-50%, calc(-1 * var(--position-bottom)));
}

&-icon {
display: flex;
flex-direction: column;
align-items: center;
&-icon {
display: flex;
flex-direction: column;
align-items: center;

.bui-svg-icon {
margin-bottom: 8px;
font-size: 30px;
}
.bui-svg-icon {
margin-bottom: 8px;
font-size: 30px;
}
}
}
51 changes: 21 additions & 30 deletions packages/bui-core/src/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
SuccessCircleFilledBoldIcon,
} from '@bifrostui/icons';
import { ToastProps } from './Toast.types';
import Modal from '../Modal';
import Fade from '../Fade';
import './Toast.less';

Expand All @@ -18,13 +17,12 @@ const ToastComponent = React.forwardRef<HTMLDivElement, ToastProps>(
const {
className,
style,
open,
type,
icon,
message,
position = 'center',
icon,
FadeProps,
closeOnClickBackdrop = false,
onClose,
disableClick = false,
...others
} = props;

Expand All @@ -37,31 +35,24 @@ const ToastComponent = React.forwardRef<HTMLDivElement, ToastProps>(
const iconDom = iconMap[type] || icon;

return (
<Modal
className={clsx(prefixCls, className)}
ref={ref}
style={style}
onClose={closeOnClickBackdrop && onClose}
hideBackdrop
disableScrollLock
disablePortal
{...others}
>
<Fade appear in timeout={1000} {...FadeProps}>
<div
className={clsx(
`${prefixCls}-content`,
`${prefixCls}-content-${position}`,
{
[`${prefixCls}-content-icon`]: !!iconDom,
},
)}
>
{iconDom}
{message}
</div>
</Fade>
</Modal>
<Fade {...others} in={open} appear={false} unmountOnExit>
<div
className={clsx(
prefixCls,
`${prefixCls}-${position}`,
{
[`${prefixCls}-icon`]: !!iconDom,
[`${prefixCls}-allow-click`]: !disableClick,
},
className,
)}
ref={ref}
style={style}
>
{iconDom}
{message}
</div>
</Fade>
);
},
);
Expand Down
23 changes: 11 additions & 12 deletions packages/bui-core/src/Toast/Toast.types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import React from 'react';
import { ModalProps } from '../Modal/Modal.types';
import { FadeProps } from '../Fade/Fade.types';

/**
* 提示类型
*/
export type ToastType = 'loading' | 'success' | 'fail' | 'warning';

export interface ToastProps extends ModalProps {
export interface ToastProps extends FadeProps {
/**
* 是否展示
*/
open?: boolean;
/**
* 提示类型
*/
type?: ToastType;
/**
* toast内容
* toast内容,支持使用`\n`换行
*/
message?: string;
/**
Expand All @@ -27,7 +30,7 @@ export interface ToastProps extends ModalProps {
*/
position?: 'top' | 'center' | 'bottom';
/**
* 是否允许存在多个Toast
* 是否允许同时存在多个Toast
* @default false
*/
allowMultiple?: boolean;
Expand All @@ -36,24 +39,20 @@ export interface ToastProps extends ModalProps {
*/
icon?: React.ReactNode;
/**
* Fade组件的Props
*/
FadeProps?: Partial<FadeProps>;
/**
* 是否在点击遮罩层后关闭
* 展示Toast时,页面内容是否可以点击
* @default false
*/
closeOnClickBackdrop?: boolean;
disableClick?: boolean;
/**
* 关闭时的回调函数
*/
onClose?: ModalProps['onClose'];
onClose?: () => void;
}

/**
* 函数式调用配置参数
*/
export type ToastOptions = Omit<ToastProps, 'type'> | string;
export type ToastOptions = Omit<ToastProps, 'type' | 'open'> | string;

/**
* 函数式调用返回值类型
Expand Down
Loading

0 comments on commit 649d0c5

Please sign in to comment.