瀏覽代碼

feat:modal transition css

feat:modal transition css

feat:modal transition css

feat:modal transition css
代强 4 年之前
父節點
當前提交
9e17c2b2cf

+ 1 - 1
packages/semi-foundation/modal/constants.ts

@@ -14,4 +14,4 @@ const strings = {
 export {
     cssClasses,
     strings
-};
+};

+ 96 - 27
packages/semi-foundation/modal/modal.scss

@@ -8,7 +8,7 @@ $module: #{$prefix}-modal;
     position: relative;
     // width: 600px;
     margin: $spacing-modal-marginY $spacing-modal-marginX;
-
+    
     &-mask {
         position: fixed;
         top: $spacing-modal_mask-top;
@@ -19,18 +19,18 @@ $module: #{$prefix}-modal;
         height: 100%;
         // filter: alpha(opacity=50);
         z-index: $z-modal-mask;
-
+        
         &-hidden {
             display: none;
         }
     }
-
+    
     &-icon-wrapper {
         display: inline-flex;
         margin-right: $spacing-modal_icon_wrapper-marginRight;
         width: $width-icon-extra-large;
     }
-
+    
     &-wrap {
         position: fixed;
         overflow: auto;
@@ -42,7 +42,7 @@ $module: #{$prefix}-modal;
         -webkit-overflow-scrolling: touch;
         outline: 0;
     }
-
+    
     &-title {
         display: inline-flex;
         align-items: flex-start;
@@ -50,7 +50,7 @@ $module: #{$prefix}-modal;
         width: $width-modal_title;
         margin: $spacing-modal_title-margin;
     }
-
+    
     &-content {
         position: relative;
         display: flex;
@@ -65,18 +65,19 @@ $module: #{$prefix}-modal;
         background-clip: padding-box;
         overflow: hidden;
         @include shadow-elevated;
+        animation: #{$module}-show 1s ease-in 0s;
     }
-
+    
     &-content-fullScreen {
         border-radius: $radius-modal_content_fullscreen;
         border: none;
     }
-
+    
     // &-close {
     // position: absolute;
     // right: 15px;
     // }
-
+    
     &-header {
         display: flex;
         align-items: flex-start;
@@ -87,24 +88,24 @@ $module: #{$prefix}-modal;
         color: $color-modal_main-text;
         border-bottom: $width-modal_header-border solid $color-modal_header-border;
     }
-
+    
     &-body-wrapper {
         display: flex;
         align-items: flex-start;
         margin: $spacing-modal_body_wrapper-marginY $spacing-modal_body_wrapper-marginX;
     }
-
+    
     &-body {
         flex: 1 1 auto;
         // padding: $spacing-loose;
         margin: $spacing-modal_body-margin;
         padding: $spacing-modal_body-padding;
     }
-
+    
     &-withIcon {
         margin-left: $spacing-modal_content_withicon-marginLeft;
     }
-
+    
     &-footer {
         // padding: $spacing-loose;
         margin: $spacing-modal_footer-marginY $spacing-modal_footer-marginX;
@@ -112,62 +113,62 @@ $module: #{$prefix}-modal;
         text-align: right;
         border-radius: $radius-modal_footer;
         border-top: $width-modal_footer-border solid $color-modal_footer-border;
-
+        
         .#{$prefix}-button {
             margin-left: $spacing-modal_footer_button-marginLeft;
             margin-right: 0;
         }
     }
-
+    
     &-confirm {
         .#{$module}-header {
             margin-bottom: $spacing-modal_confirm_header-marginBottom;
         }
-
+        
         // &-content-withIcon {
         // margin-left: 36px;
         // }
-
+        
         &-icon-wrapper {
             display: inline-flex;
             margin-right: $spacing-modal_confirm_icon_wrapper-marginRight;
             width: $width-icon-extra-large;
         }
-
+        
         &-icon {
             display: inline-flex;
             color: $color-modal_primary-icon;
         }
     }
-
+    
     &-info-icon {
         color: $color-modal_info-icon;
     }
-
+    
     &-success-icon {
         color: $color-modal_success-icon;
     }
-
+    
     &-error-icon {
         color: $color-modal_danger-icon;
     }
-
+    
     &-warning-icon {
         color: $color-modal_warning-icon;
     }
-
+    
     &-small {
         width: $width-modal_small;
     }
-
+    
     &-medium {
         width: $width-modal_medium;
     }
-
+    
     &-large {
         width: $width-modal_large;
     }
-
+    
     &-full-width {
         width: $width-modal_full_width;
     }
@@ -193,8 +194,76 @@ $module: #{$prefix}-modal;
     }
 }
 
-.#{$module}-hidden {
+.#{$module}-displayNone {
     display: none;
 }
 
+.#{$module}-content-animate-hide {
+    animation: 90ms #{$module}-content-keyframe-hide ease forwards;
+}
+
+.#{$module}-content-animate-show {
+    animation: 120ms #{$module}-content-keyframe-show cubic-bezier(0, 0, 0.26, 1.38) forwards;
+}
+
+.#{$module}-mask-animate-hide {
+    animation: 90ms #{$module}-mask-keyframe-hide ease forwards;
+}
+
+.#{$module}-mask-animate-show {
+    animation: 90ms #{$module}-mask-keyframe-show ease forwards;
+}
+
+
+@keyframes #{$module}-content-keyframe-show {
+    
+    0% {
+        opacity: 0;
+        transform: scale(.7);
+    }
+
+    100% {
+        opacity: 1;
+        transform: scale(1);
+    }
+}
+
+@keyframes #{$module}-content-keyframe-hide {
+    
+    0% {
+        opacity: 1;
+        transform: scale(1);
+    }
+    
+    100% {
+        opacity: 0;
+        transform: scale(0.7);
+    }
+}
+
+
+@keyframes #{$module}-mask-keyframe-show {
+    
+    0% {
+        opacity: 0;
+    }
+    
+    100% {
+        opacity: 1;
+    }
+}
+
+@keyframes #{$module}-mask-keyframe-hide {
+    
+    0% {
+        opacity: 1;
+    }
+    
+    100% {
+        opacity: 0;
+    }
+}
+
+
+
 @import "./rtl.scss";

+ 3 - 0
packages/semi-foundation/modal/modalContentFoundation.ts

@@ -6,6 +6,9 @@ export interface ModalContentProps extends ModalProps{
     onClose: (e: any) => void;
     getContainerContext: () => any;
     isFullScreen?: boolean;
+    contentClassName?:string,
+    maskClassName?:string;
+    onAnimationEnd?:()=>void;
 }
 
 export interface ModalContentState{

+ 1 - 1
packages/semi-foundation/modal/rtl.scss

@@ -12,7 +12,7 @@ $module: #{$prefix}-modal;
             margin-right: 0;
             margin-left: $spacing-modal_confirm_icon_wrapper-marginRight;
         }
-        
+
     }
 
     &-withIcon {

+ 34 - 53
packages/semi-ui/modal/Modal.tsx

@@ -8,18 +8,10 @@ import Portal from '../_portal';
 import LocaleConsumer from '../locale/localeConsumer';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
-import { isUndefined } from 'lodash-es';
-import '@douyinfe/semi-foundation/modal/modal.scss';
 import { noop } from 'lodash-es';
-import ContentTransition from './ModalTransition';
+import '@douyinfe/semi-foundation/modal/modal.scss';
 import BaseComponent from '../_base/baseComponent';
-import confirm, {
-    withConfirm,
-    withError,
-    withInfo,
-    withSuccess,
-    withWarning
-} from '../modal/confirm';
+import confirm, { withConfirm, withError, withInfo, withSuccess, withWarning } from '../modal/confirm';
 import { Locale } from '../locale/interface';
 import useModal from '../modal/useModal';
 import { ButtonProps } from '../button/Button';
@@ -314,22 +306,16 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
         );
     };
 
-    getDialog = () => {
-        const {
-            footer,
-            ...restProps
-        } = this.props;
-        const renderFooter = 'footer' in this.props ? footer : this.renderFooter();
-        return <ModalContent {...restProps} footer={renderFooter} onClose={this.handleCancel} />;
-    };
+    // getDialog = () => {
+    //     const {
+    //         footer,
+    //         ...restProps
+    //     } = this.props;
+    //     const renderFooter = 'footer' in this.props ? footer : this.renderFooter();
+    //     return <ModalContent {...restProps} footer={renderFooter} onClose={this.handleCancel}/>;
+    // };
 
-    renderDialogWithTransition = ({
-        opacity,
-        scale,
-    }: {
-        opacity?: CSSProperties['opacity'];
-        scale?: number;
-    } = {}) => {
+    renderDialog = () => {
         let {
             footer,
             className,
@@ -339,10 +325,11 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
             style: styleFromProps,
             zIndex,
             getPopupContainer,
+            visible,
             ...restProps
         } = this.props;
-        let maskStyle = maskStyleFromProps;
         let style = styleFromProps;
+        const maskStyle = maskStyleFromProps;
         const renderFooter = 'footer' in this.props ? footer : this.renderFooter();
         if (this.props.centered) {
             style = {
@@ -350,20 +337,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                 top: '50%', ...style,
             };
         }
-        if (!isUndefined(opacity)) {
-            maskStyle = { opacity, ...maskStyle };
-            style = { opacity, ...style };
-        }
-
-        if (!isUndefined(scale)) {
-            style = { transform: `scale(${scale})`, ...style };
-            if (this.props.centered) {
-                style = {
-                    ...style,
-                    transform: `scale(${scale}) translateY(-${50 / scale}%)`,
-                };
-            }
-        }
         let wrapperStyle: {
             zIndex?: CSSProperties['zIndex'];
             position?: CSSProperties['position'];
@@ -378,18 +351,36 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
         }
 
         const classList = cls(className, {
-            [`${cssClasses.DIALOG}-hidden`]: keepDOM && this.state.hidden,
+            [`${cssClasses.DIALOG}-displayNone`]: keepDOM && this.state.hidden,
         });
+        const contentClassName = cls({
+            [`${cssClasses.DIALOG}-content-animate-hide`]: !visible,
+            [`${cssClasses.DIALOG}-content-animate-show`]: visible
+        });
+        const maskClassName = cls({
+            [`${cssClasses.DIALOG}-mask-animate-hide`]: !visible,
+            [`${cssClasses.DIALOG}-mask-animate-show`]: visible
+        })
+
         return (
             <Portal style={wrapperStyle} getPopupContainer={getPopupContainer}>
                 <ModalContent
                     {...restProps}
                     isFullScreen={this.state.isFullScreen}
+                    contentClassName={contentClassName}
+                    maskClassName={maskClassName}
                     className={classList}
                     getPopupContainer={getPopupContainer}
                     maskStyle={maskStyle}
                     style={style}
                     ref={this.modalRef}
+                    onAnimationEnd={() => {
+                        if (!visible && !this.state.hidden) {
+                            this.foundation.toggleHidden(true)
+                        } else if (visible && this.state.hidden) {
+                            this.foundation.toggleHidden(false)
+                        }
+                    }}
                     footer={renderFooter}
                     onClose={this.handleCancel}
 
@@ -405,19 +396,9 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
             lazyRender,
         } = this.props;
         this._active = this._active || visible;
-        const shouldRender = (visible || keepDOM) && (!lazyRender || this._active);
-
-        const mergedMotion = this.foundation.getMergedMotion();
-        if (mergedMotion) {
-            return (
-                <ContentTransition motion={mergedMotion} controlled={keepDOM} visible={visible}>
-                    {shouldRender ? (...animationState) => this.renderDialogWithTransition(...animationState) : null}
-                </ContentTransition>
-            );
-        }
-
+        const shouldRender = ((visible || keepDOM) && (!lazyRender || this._active)) || !this.state.hidden;
         if (shouldRender) {
-            return this.renderDialogWithTransition();
+            return this.renderDialog();
         }
 
         return null;

+ 15 - 6
packages/semi-ui/modal/ModalContent.tsx

@@ -23,11 +23,16 @@ export default class ModalContent extends BaseComponent<ModalContentProps, Modal
     static propTypes = {
         close: PropTypes.func,
         getContainerContext: PropTypes.func,
+        contentClassName: PropTypes.string,
+        maskClassName: PropTypes.string,
+        onAnimationEnd: PropTypes.func
     };
 
     static defaultProps = {
         close: noop,
         getContainerContext: noop,
+        contentClassName: '',
+        maskClassName: ''
     };
     dialogId: string;
     private timeoutId: NodeJS.Timeout;
@@ -107,12 +112,12 @@ export default class ModalContent extends BaseComponent<ModalContentProps, Modal
 
     getMaskElement = () => {
         const { ...props } = this.props;
-        const { mask } = props;
+        const { mask, maskClassName } = props;
         if (mask) {
             const className = cls(`${cssClasses.DIALOG}-mask`, {
                 // [`${cssClasses.DIALOG}-mask-hidden`]: !props.visible,
             });
-            return <div key="mask" className={className} style={props.maskStyle} />;
+            return <div key="mask" className={cls(className, maskClassName)} style={props.maskStyle}/>;
         }
         return null;
     };
@@ -124,7 +129,7 @@ export default class ModalContent extends BaseComponent<ModalContentProps, Modal
         } = this.props;
         let closer;
         if (closable) {
-            const iconType = closeIcon || <IconClose />;
+            const iconType = closeIcon || <IconClose/>;
             closer = (
                 <Button
                     className={`${cssClasses.DIALOG}-close`}
@@ -216,8 +221,11 @@ export default class ModalContent extends BaseComponent<ModalContentProps, Modal
                 style={{ ...props.style, ...style }}
                 id={this.dialogId}
             >
-                <div className={cls([`${cssClasses.DIALOG}-content`,
-                    { [`${cssClasses.DIALOG}-content-fullScreen`]: props.isFullScreen }])}>
+                <div
+                    onAnimationEnd={props.onAnimationEnd}
+                    className={cls([`${cssClasses.DIALOG}-content`,
+                        props.contentClassName,
+                        { [`${cssClasses.DIALOG}-content-fullScreen`]: props.isFullScreen }])}>
                     {header}
                     {body}
                     {footer}
@@ -262,6 +270,7 @@ export default class ModalContent extends BaseComponent<ModalContentProps, Modal
 
         // @ts-ignore Unreachable branch
         // eslint-disable-next-line max-len
-        return containerContext && containerContext.Provider ? <containerContext.Provider value={containerContext.value}>{elem}</containerContext.Provider> : elem;
+        return containerContext && containerContext.Provider ?
+            <containerContext.Provider value={containerContext.value}>{elem}</containerContext.Provider> : elem;
     }
 }

+ 0 - 43
packages/semi-ui/modal/ModalTransition.tsx

@@ -1,43 +0,0 @@
-// @ts-ignore Temporarily do not proceed  the action package ts
-import { Transition } from '@douyinfe/semi-animation-react';
-import React, { JSXElementConstructor } from 'react';
-import { Motion } from '../_base/base';
-
-interface ContentTransitionProps {
-    // eslint-disable-next-line max-len
-    motion?: Motion<ContentTransitionProps>;
-    children?: React.ReactNode | JSXElementConstructor<any>;
-    controlled?: boolean;
-    visible?: boolean;
-}
-
-export default function ContentTransition(props: ContentTransitionProps = {}) {
-    const { motion: motionFromProps, children, controlled, visible } = props;
-    let motion = motionFromProps;
-    let extra = {};
-    if (typeof motion === 'function') {
-        motion = motion(props);
-    } else if (!motion || typeof motion !== 'object') {
-        motion = {};
-    }
-
-    if (controlled) {
-        extra = {
-            // immediate: true,
-            state: visible ? 'enter' : 'leave',
-        };
-    }
-
-    return (
-        <Transition
-            config={{ tension: 600, friction: 30 } as any}
-            from={{ scale: 0.7, opacity: { val: 0, duration: 180 } }}
-            enter={{ scale: { val: 1 }, opacity: { val: 1, duration: 90 } } as any}
-            leave={{ scale: { val: 0.7 }, opacity: { val: 0, duration: 75 } } as any}
-            {...extra}
-            {...motion}
-        >
-            {children}
-        </Transition>
-    );
-}