Skip to content

Commit

Permalink
Merge pull request #900 from huoyuhao/fix/dialog/mode
Browse files Browse the repository at this point in the history
fix(dialog): 修复mask关闭dialog
  • Loading branch information
honkinglin authored Jun 17, 2022
2 parents 210d878 + 810c030 commit acc4230
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 67 deletions.
6 changes: 3 additions & 3 deletions src/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ const Dialog = forwardRef((props: DialogProps, ref: React.Ref<DialogInstance>) =

const {
visible,
attach = 'body',
attach,
closeBtn,
footer,
footer = true,
onCancel = noop,
onConfirm = noop,
cancelBtn = cancelText,
Expand Down Expand Up @@ -162,7 +162,7 @@ const Dialog = forwardRef((props: DialogProps, ref: React.Ref<DialogInstance>) =
closeBtn={renderCloseIcon()}
classPrefix={classPrefix}
onClose={onClose}
footer={footer === undefined ? defaultFooter() : footer}
footer={footer === true ? defaultFooter() : footer}
ref={dialogDom}
/>
);
Expand Down
58 changes: 32 additions & 26 deletions src/dialog/RenderDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import isNumber from 'lodash/isNumber';
import React, { useRef, CSSProperties, useEffect, forwardRef, useImperativeHandle } from 'react';
import { CSSTransition } from 'react-transition-group';
import classnames from 'classnames';
Expand All @@ -13,6 +12,9 @@ enum KeyCode {
ESC = 27,
}

function GetCSSValue(v: string | number) {
return Number.isNaN(Number(v)) ? v : `${Number(v)}px`;
}
export interface RenderDialogProps extends DialogProps {
prefixCls?: string;
classPrefix: string;
Expand Down Expand Up @@ -50,6 +52,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
closeOnEscKeydown,
closeOnOverlayClick,
destroyOnClose,
showInAttachedElement,
} = props;
const wrap = useRef<HTMLDivElement>();
const dialog = useRef<HTMLDivElement>();
Expand All @@ -58,8 +61,9 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
const bodyOverflow = useRef<string>();
const bodyCssTextRef = useRef<string>();
const isModal = mode === 'modal';
const isNormal = mode === 'normal';
const canDraggable = props.draggable && mode === 'modeless';
const dialogOpenClass = `${prefixCls}__open`;
const dialogOpenClass = `${prefixCls}__${mode}`;
useDialogEsc(visible, wrap);
useImperativeHandle(ref, () => wrap.current);
useLayoutEffect(() => {
Expand All @@ -69,7 +73,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi

useLayoutEffect(() => {
if (visible) {
if (isModal && bodyOverflow.current !== 'hidden' && preventScrollThrough) {
if (isModal && bodyOverflow.current !== 'hidden' && preventScrollThrough && !showInAttachedElement) {
const scrollWidth = window.innerWidth - document.body.offsetWidth;
// 减少回流
if (bodyCssTextRef.current === '') {
Expand All @@ -90,7 +94,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
wrap.current.focus();
}
} else if (isModal) {
const openDialogDom = document.querySelectorAll(`.${dialogOpenClass}`);
const openDialogDom = document.querySelectorAll(`${prefixCls}__mode`);
if (openDialogDom.length < 1) {
document.body.style.cssText = bodyCssTextRef.current;
}
Expand All @@ -99,7 +103,8 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
// 组件销毁后重置 body 样式
return () => {
if (isModal) {
const openDialogDom = document.querySelectorAll(`.${dialogOpenClass}`);
// 此处只能查询mode模式的dialog个数 因为modeless 会点击透传 normal 是正常文档流
const openDialogDom = document.querySelectorAll(`${prefixCls}__mode`);
if (openDialogDom.length < 1) {
document.body.style.cssText = bodyCssTextRef.current;
document.body.style.overflow = bodyOverflow.current;
Expand All @@ -109,7 +114,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
document.body.style.overflow = bodyOverflow.current;
}
};
}, [preventScrollThrough, attach, visible, mode, isModal, dialogOpenClass]);
}, [preventScrollThrough, attach, visible, mode, isModal, showInAttachedElement, prefixCls]);

useEffect(() => {
// 动画渲染初始位置
Expand All @@ -128,7 +133,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
}
if (isModal && preventScrollThrough) {
// 还原body的滚动条
const openDialogDom = document.querySelectorAll(`.${dialogOpenClass}`);
const openDialogDom = document.querySelectorAll(`${prefixCls}__mode`);
if (isModal && openDialogDom.length < 1) {
document.body.style.overflow = bodyOverflow.current;
}
Expand All @@ -144,9 +149,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
};

const onMaskClick = (e: React.MouseEvent<HTMLDivElement>) => {
// 如果不是modal模式 默认没有mask 也就没有相关点击事件
if (mode !== 'modal') return;
if (e.target === e.currentTarget && closeOnOverlayClick) {
if (closeOnOverlayClick) {
onOverlayClick({ e });
onClose({ e, trigger: 'overlay' });
}
Expand All @@ -171,9 +174,12 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
const renderDialog = () => {
const dest: any = {};
if (props.width !== undefined) {
dest.width = props.width;
dest.width = GetCSSValue(props.width);
}
// normal 场景下,需要设置zindex 为auto 避免出现多个dialog,normal出现在最上层
if (props.mode === 'normal') {
dest.zIndex = 'auto';
}

const footer = props.footer ? <div className={`${prefixCls}__footer`}>{props.footer}</div> : null;

const { header } = props;
Expand Down Expand Up @@ -232,24 +238,21 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
document.addEventListener('mouseup', onDialogMoveEnd);
}
};
// 顶部定位实现
const positionStyle: any = {};
if (props.top !== undefined) {
// 判断是否时数字
if (isNumber(props.top) && props.top < 0) {
positionStyle.paddingTop = `${props.top}px`;
} else {
positionStyle.paddingTop = props.top;
}
if (props.top) {
const topValue = GetCSSValue(props.top);
positionStyle.paddingTop = topValue;
}
// 此处获取定位方式 top 优先级较高 存在时 默认使用top定位
const classNames = classnames(
const positionClass = classnames(
`${prefixCls}__position`,
{ [`${prefixCls}--top`]: !!props.top },
`${props.placement && !props.top ? `${prefixCls}--${props.placement}` : ''}`,
);
const dialogElement = (
<div className={`${prefixCls}__wrap`} onClick={onMaskClick}>
<div className={classNames} style={positionStyle}>
<div className={isNormal ? '' : `${prefixCls}__wrap`}>
<div className={isNormal ? '' : positionClass} style={positionStyle}>
<div
ref={dialog}
style={style}
Expand Down Expand Up @@ -295,7 +298,7 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
unmountOnExit
nodeRef={maskRef}
>
<div ref={maskRef} className={`${prefixCls}__mask`} />
<div ref={maskRef} className={`${prefixCls}__mask`} onClick={onMaskClick} />
</CSSTransition>
);
}
Expand All @@ -316,9 +319,12 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
const wrapClass = classnames(
props.className,
`${prefixCls}__ctx`,
`${prefixCls}__ctx--fixed`,
!isNormal ? `${prefixCls}__ctx--fixed` : '',
visible ? dialogOpenClass : '',
isModal && showInAttachedElement ? `${prefixCls}__ctx--absolute` : '',
props.mode === 'modeless' ? `${prefixCls}__ctx--modeless` : '',
);
// 如果不是modal模式 默认没有mask 也就没有相关点击mask事件
const dialog = (
<div ref={wrap} className={wrapClass} style={wrapStyle} onKeyDown={handleKeyDown} tabIndex={0}>
{mode === 'modal' && renderMask()}
Expand All @@ -327,9 +333,9 @@ const RenderDialog = forwardRef((props: RenderDialogProps, ref: React.Ref<HTMLDi
);

let dom = null;

if (visible || wrap.current) {
if (!attach) {
// normal模式attach无效
if (attach === '' || isNormal) {
dom = dialog;
} else {
dom = (
Expand Down
29 changes: 27 additions & 2 deletions src/dialog/__tests__/__snapshots__/modal.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ exports[`async.jsx 1`] = `

exports[`attach.jsx 1`] = `
<DocumentFragment>
<div>
<div
style="position: relative; height: 400px;"
>
<button
class="t-button t-button--theme-primary t-button--variant-base"
style="margin-right: 16px;"
Expand Down Expand Up @@ -53,6 +55,17 @@ exports[`attach.jsx 1`] = `
挂载函数返回节点
</span>
</button>
<button
class="t-button t-button--theme-primary t-button--variant-base"
style="margin-right: 16px;"
type="button"
>
<span
class="t-button__text"
>
展示在挂载元素区域
</span>
</button>
</div>
</DocumentFragment>
`;
Expand Down Expand Up @@ -154,7 +167,18 @@ exports[`modal.jsx 1`] = `
<span
class="t-button__text"
>
非模态框
非模态对话框
</span>
</button>
<button
class="t-button t-button--theme-primary t-button--variant-base"
style="margin-right: 16px;"
type="button"
>
<span
class="t-button__text"
>
非模态对话框2
</span>
</button>
<button
Expand All @@ -168,6 +192,7 @@ exports[`modal.jsx 1`] = `
</span>
</button>
</div>
<div />
</div>
</DocumentFragment>
`;
Expand Down
24 changes: 22 additions & 2 deletions src/dialog/_example/attach.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function AttachModalExample() {
visibleBody: false,
visibleIdAttach: false,
visibleFunctionAttach: false,
visibleShowInAttachedElement: false,
});

const handleOpen = (visibleName) => {
Expand All @@ -27,10 +28,10 @@ export default function AttachModalExample() {

const getAttach = () => elRef.current;

const { visibleBody, visibleIdAttach, visibleFunctionAttach } = state;
const { visibleBody, visibleIdAttach, visibleFunctionAttach, visibleShowInAttachedElement } = state;

return (
<div ref={elRef}>
<div ref={elRef} style={{ position: 'relative', height: '400px' }}>
<Button theme="primary" onClick={() => handleOpen('visibleBody')} style={buttonStyle}>
挂载在body
</Button>
Expand All @@ -40,6 +41,9 @@ export default function AttachModalExample() {
<Button theme="primary" onClick={() => handleOpen('visibleFunctionAttach')} style={buttonStyle}>
挂载函数返回节点
</Button>
<Button theme="primary" onClick={() => handleOpen('visibleShowInAttachedElement')} style={buttonStyle}>
展示在挂载元素区域
</Button>
<Dialog
mattach="body"
header="挂载在body"
Expand Down Expand Up @@ -91,6 +95,22 @@ export default function AttachModalExample() {
<div>我是内容</div>
</div>
</Dialog>

<Dialog
header="展示在挂载元素区域"
attach={getAttach}
showInAttachedElement={true}
visible={visibleShowInAttachedElement}
onClose={() => handleClose('visibleShowInAttachedElement')}
onOpened={() => {
console.log('dialog is open');
}}
>
<div>
<div>父元素(挂载元素)需要有定位属性,如:position: relative</div>
<div>showInAttachedElement API 仅针对模态对话框有效</div>
</div>
</Dialog>
</div>
);
}
Loading

0 comments on commit acc4230

Please sign in to comment.