|
@@ -10,6 +10,7 @@ import Portal from "@/components/common/portal/Portal";
|
|
|
*/
|
|
*/
|
|
|
export default function CommonModal({
|
|
export default function CommonModal({
|
|
|
isOpen,
|
|
isOpen,
|
|
|
|
|
+ destroyOnClose = false,
|
|
|
contentClassName,
|
|
contentClassName,
|
|
|
clickOutsideClose,
|
|
clickOutsideClose,
|
|
|
onClose,
|
|
onClose,
|
|
@@ -18,6 +19,7 @@ export default function CommonModal({
|
|
|
footer
|
|
footer
|
|
|
}: {
|
|
}: {
|
|
|
isOpen: boolean;
|
|
isOpen: boolean;
|
|
|
|
|
+ destroyOnClose?: boolean;
|
|
|
contentClassName?: string;
|
|
contentClassName?: string;
|
|
|
clickOutsideClose?: boolean;
|
|
clickOutsideClose?: boolean;
|
|
|
onClose: (e: boolean) => void;
|
|
onClose: (e: boolean) => void;
|
|
@@ -29,6 +31,7 @@ export default function CommonModal({
|
|
|
// outing - 隐藏动画中
|
|
// outing - 隐藏动画中
|
|
|
// hidden - 隐藏
|
|
// hidden - 隐藏
|
|
|
const [rootVisible, setRootVisible] = useState<'show' | 'outing' | 'hidden'>('hidden');
|
|
const [rootVisible, setRootVisible] = useState<'show' | 'outing' | 'hidden'>('hidden');
|
|
|
|
|
+ const [mounted, setMounted] = useState(false);
|
|
|
|
|
|
|
|
const cssInit = useRef(false);
|
|
const cssInit = useRef(false);
|
|
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
@@ -38,33 +41,65 @@ export default function CommonModal({
|
|
|
const [rootPddingBottom, setRootPddingBottom] = useState(0);
|
|
const [rootPddingBottom, setRootPddingBottom] = useState(0);
|
|
|
const [rootPddingTop, setRootPddingTop] = useState(0);
|
|
const [rootPddingTop, setRootPddingTop] = useState(0);
|
|
|
|
|
|
|
|
- const baseClassNname = "absolute box-border w-full bg-white transition-transform duration-300 ease-out";
|
|
|
|
|
- let applyClassName = "bottom-0 left-0 h-17/20";
|
|
|
|
|
|
|
+ const baseClassNname = "absolute box-border transition-transform duration-300 ease-out";
|
|
|
|
|
+ let applyClassName = "bottom-0 left-0 h-17/20 w-full bg-white";
|
|
|
if(contentClassName) {
|
|
if(contentClassName) {
|
|
|
applyClassName = contentClassName;
|
|
applyClassName = contentClassName;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
useLayoutEffect(() => {
|
|
|
const original = document.documentElement.style.overflow;
|
|
const original = document.documentElement.style.overflow;
|
|
|
- if(isOpen) {
|
|
|
|
|
|
|
+ if(isOpen){
|
|
|
|
|
+ // destroy模式重新创建DOM
|
|
|
|
|
+ if(destroyOnClose){
|
|
|
|
|
+ // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
+ setMounted(true);
|
|
|
|
|
+ }
|
|
|
document.documentElement.style.overflow = "hidden";
|
|
document.documentElement.style.overflow = "hidden";
|
|
|
- } else {
|
|
|
|
|
|
|
+ requestAnimationFrame(()=>{
|
|
|
|
|
+ setRootVisible("show");
|
|
|
|
|
+ });
|
|
|
|
|
+ }else{
|
|
|
document.documentElement.style.overflow = "";
|
|
document.documentElement.style.overflow = "";
|
|
|
- }
|
|
|
|
|
- // intent: 为了实现关闭时也有动画效果,告诉eslint忽略这个错误
|
|
|
|
|
- if(isOpen) {
|
|
|
|
|
- // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
- setRootVisible('show');
|
|
|
|
|
- } else {
|
|
|
|
|
- if(cssInit.current) {
|
|
|
|
|
- // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
- setRootVisible('outing');
|
|
|
|
|
|
|
+ if(destroyOnClose){
|
|
|
|
|
+ // 先播放关闭动画
|
|
|
|
|
+ if(mounted){
|
|
|
|
|
+ // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
+ setRootVisible("outing");
|
|
|
|
|
+ }
|
|
|
|
|
+ }else{
|
|
|
|
|
+ // 保留DOM
|
|
|
|
|
+ if(cssInit.current){
|
|
|
|
|
+ // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
+ setRootVisible("outing");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
+ return ()=>{ document.documentElement.style.overflow = original; };
|
|
|
|
|
+ },[isOpen, destroyOnClose, mounted]);
|
|
|
|
|
|
|
|
- return () => { document.documentElement.style.overflow = original; };
|
|
|
|
|
- },[isOpen]);
|
|
|
|
|
|
|
+ // useLayoutEffect(() => {
|
|
|
|
|
+ // const original = document.documentElement.style.overflow;
|
|
|
|
|
+ // if(isOpen) {
|
|
|
|
|
+ // document.documentElement.style.overflow = "hidden";
|
|
|
|
|
+ // } else {
|
|
|
|
|
+ // document.documentElement.style.overflow = "";
|
|
|
|
|
+ // }
|
|
|
|
|
+ // // intent: 为了实现关闭时也有动画效果,告诉eslint忽略这个错误
|
|
|
|
|
+ // if(isOpen) {
|
|
|
|
|
+ // // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
+ // setRootVisible('show');
|
|
|
|
|
+ // } else {
|
|
|
|
|
+ // if(cssInit.current) {
|
|
|
|
|
+ // // eslint-disable-next-line react-hooks/set-state-in-effect
|
|
|
|
|
+ // setRootVisible('outing');
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // return () => { document.documentElement.style.overflow = original; };
|
|
|
|
|
+ // },[isOpen]);
|
|
|
// 当弹窗完全显示后计算 footer 高度
|
|
// 当弹窗完全显示后计算 footer 高度
|
|
|
useLayoutEffect(() => {
|
|
useLayoutEffect(() => {
|
|
|
if (rootVisible === 'show' && !cssInit.current && footerRef.current && headerRef.current) {
|
|
if (rootVisible === 'show' && !cssInit.current && footerRef.current && headerRef.current) {
|
|
@@ -85,12 +120,18 @@ export default function CommonModal({
|
|
|
const handlerAnimationEnd = (e: React.AnimationEvent<HTMLDivElement>) => {
|
|
const handlerAnimationEnd = (e: React.AnimationEvent<HTMLDivElement>) => {
|
|
|
if(e.animationName === "fade-out") {
|
|
if(e.animationName === "fade-out") {
|
|
|
setRootVisible('hidden');
|
|
setRootVisible('hidden');
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if(destroyOnClose){
|
|
|
|
|
+ setMounted(false);
|
|
|
|
|
+ cssInit.current=false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
// 初始化时是隐藏状态
|
|
// 初始化时是隐藏状态
|
|
|
// fade-in的时候display block + fade-in
|
|
// fade-in的时候display block + fade-in
|
|
|
// fade-out的时候需要先加fade-out 类名,动画效果结束之后才能display none
|
|
// fade-out的时候需要先加fade-out 类名,动画效果结束之后才能display none
|
|
|
|
|
+ if(destroyOnClose && !mounted){
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
return (
|
|
return (
|
|
|
<Portal>
|
|
<Portal>
|
|
|
<div
|
|
<div
|