Browse Source

feat: toast js2css

代强 3 years ago
parent
commit
0253cf19d4

+ 11 - 0
packages/semi-foundation/toast/animation.scss

@@ -0,0 +1,11 @@
+$animation_duration-toast-show: 300ms;
+$animation_duration-toast-hide: 300ms;
+$animation_function-toast-show: ease-in-out;
+$animation_function-toast-hide: ease-in-out;
+$animation_delay-toast-show: 0s;
+$animation_delay-toast-hide: 0s;
+
+$animation_opacity-toast-show: 0;
+$animation_opacity-toast-hide: 0;
+$animation_transform_translateY-toast-show: -100%;
+$animation_transform_translateY-toast-hide: -100%;

+ 30 - 1
packages/semi-foundation/toast/toast.scss

@@ -1,4 +1,4 @@
-//@import '../theme/variables.scss';
+@import "./animation.scss";
 @import "./variables.scss";
 
 $module: #{$prefix}-toast;
@@ -87,6 +87,35 @@ $icon: #{$prefix}-toast-icon;
     .#{$icon}-error {
         color: $color-toast_danger-icon;
     }
+
+    &-animation-show{
+        animation:  $animation_duration-toast-show #{$module}-keyframe-toast-show $animation_delay-toast-show ease-in-out;
+    }
+
+    &-animation-hide{
+        animation: $animation_duration-toast-hide #{$module}-keyframe-toast-hide $animation_delay-toast-hide ease-in-out;
+    }
+
+    @keyframes #{$module}-keyframe-toast-show{
+        0%{
+            opacity: $animation_opacity-toast-show;
+            transform: translateY($animation_transform_translateY-toast-show);
+        }
+        100%{
+            opacity: 1;
+        }
+    }
+
+    @keyframes #{$module}-keyframe-toast-hide{
+        0%{
+            opacity: 1;
+        }
+        100%{
+            opacity: $animation_opacity-toast-hide;
+            transform: translateY($animation_transform_translateY-toast-hide);
+        }
+    }
+
 }
 
 @import "./rtl.scss";

+ 1 - 2
packages/semi-foundation/toast/toastFoundation.ts

@@ -1,6 +1,5 @@
 import BaseFoundation, { DefaultAdapter } from '../base/foundation';
 import { isNumber, noop } from 'lodash';
-import { Motion } from '../utils/type';
 
 
 export type ToastType = 'success' | 'warning' | 'error' | 'info' | 'default';
@@ -33,7 +32,7 @@ export interface ToastProps extends ConfigProps {
 
 export interface ToastInstance extends ToastProps{
     id?: string;
-    motion?: Motion;
+    motion?: boolean;
 }
 
 // eslint-disable-next-line @typescript-eslint/no-empty-interface

+ 35 - 28
packages/semi-ui/_cssAnimation/index.tsx

@@ -1,5 +1,5 @@
-import React, { CSSProperties, ReactNode } from 'react';
-import { isEqual, noop } from "lodash";
+import React, {CSSProperties, ReactNode} from 'react';
+import {isEqual, noop} from "lodash";
 
 
 interface AnimationEventsNeedBind {
@@ -15,34 +15,37 @@ interface AnimationProps {
     children: ({}: {
         animationClassName: string,
         animationStyle: CSSProperties,
-        animationEventsNeedBind: AnimationEventsNeedBind
+        animationEventsNeedBind: AnimationEventsNeedBind,
+        isAnimating: boolean
     }) => ReactNode
     animationState: "enter" | "leave"
     onAnimationEnd?: () => void;
     onAnimationStart?: () => void;
-    motion?:boolean;
-    replayKey?:string;
+    motion?: boolean;
+    replayKey?: string;
 }
 
 interface AnimationState {
     currentClassName: string
-    extraStyle: CSSProperties
+    extraStyle: CSSProperties,
+    isAnimating: boolean
 }
 
 
 class CSSAnimation extends React.Component<AnimationProps, AnimationState> {
 
     static defaultProps = {
-        motion:true,
-        replayKey:"",
+        motion: true,
+        replayKey: "",
     }
 
     constructor(props) {
         super(props);
-        
+
         this.state = {
             currentClassName: this.props.startClassName,
-            extraStyle: {}
+            extraStyle: {},
+            isAnimating: true
         };
     }
 
@@ -54,15 +57,16 @@ class CSSAnimation extends React.Component<AnimationProps, AnimationState> {
         if (changedKeys.includes("startClassName") || changedKeys.includes('replayKey') || changedKeys.includes("motion")) {
             this.setState({
                 currentClassName: this.props.startClassName,
-                extraStyle: {}
-            }, ()=>{
+                extraStyle: {},
+                isAnimating: true
+            }, () => {
                 this.props.onAnimationStart?.();
-                if(!this.props.motion){
+                if (!this.props.motion) {
                     this.props.onAnimationEnd?.();
                 }
             });
         }
-    
+
 
     }
 
@@ -74,7 +78,8 @@ class CSSAnimation extends React.Component<AnimationProps, AnimationState> {
     handleAnimationEnd = () => {
         this.setState({
             currentClassName: this.props.endClassName,
-            extraStyle: {}
+            extraStyle: {},
+            isAnimating: false
         }, () => {
             this.props.onAnimationEnd?.();
         });
@@ -82,22 +87,24 @@ class CSSAnimation extends React.Component<AnimationProps, AnimationState> {
 
 
     render() {
-        if(this.props.motion){
-           return this.props.children({
-            animationClassName: this.state.currentClassName ?? "",
-            animationStyle: this.state.extraStyle,
-            animationEventsNeedBind: {
-                onAnimationStart: this.handleAnimationStart,
-                onAnimationEnd: this.handleAnimationEnd
-            }
-        }); 
-        }else{
+        if (this.props.motion) {
+            return this.props.children({
+                animationClassName: this.state.currentClassName ?? "",
+                animationStyle: this.state.extraStyle,
+                animationEventsNeedBind: {
+                    onAnimationStart: this.handleAnimationStart,
+                    onAnimationEnd: this.handleAnimationEnd
+                },
+                isAnimating: this.state.isAnimating
+            });
+        } else {
             return this.props.children({
                 animationClassName: "",
                 animationStyle: {},
-                animationEventsNeedBind: {}
-        })
-      }
+                animationEventsNeedBind: {},
+                isAnimating: this.state.isAnimating
+            })
+        }
     }
 }
 

+ 0 - 43
packages/semi-ui/toast/ToastTransition.tsx

@@ -1,43 +0,0 @@
-// @ts-ignore  Currently there is no types for semi-animation-react
-import { Transition } from '@douyinfe/semi-animation-react';
-import { Motion } from '../_base/base';
-import React, { CSSProperties } from 'react';
-
-
-export interface ToastTransitionProps{
-    motion?: Motion<ToastTransitionProps>;
-    children?: React.ReactNode | ((TransitionProps: any) => any)
-}
-
-
-export default function ToastTransition(props: ToastTransitionProps = {}) {
-    let { motion = {} } = props;
-
-    if (typeof motion === 'function') {
-        motion = motion(props);
-    } else if (!motion || typeof motion !== 'object') {
-        motion = {};
-    }
-
-    return (
-        <Transition
-            // onFrame={style => console.log(style)}
-            from={{ translateY: -100, opacity: 0 }}
-            enter={{ translateY: { val: 0, tension: 560, friction: 32 }, opacity: { val: 1, duration: 300 } } as any}
-            leave={{
-                translateY: { val: -100, easing: 'easeOutCubic', duration: 300 },
-                opacity: { val: 0, duration: 200 },
-            } as any}
-            {...motion}
-        >
-            {typeof props.children === 'function' ?
-                ({ translateY, opacity }: { translateY: string | number; opacity: string | number }) =>
-                    (props.children as
-                        (styles: { transform: CSSProperties['transform']; opacity: string | number }) => any)({
-                        transform: `translateY(${translateY}%)`,
-                        opacity,
-                    }) :
-                props.children}
-        </Transition>
-    );
-}

+ 17 - 20
packages/semi-ui/toast/index.tsx

@@ -11,13 +11,14 @@ import { cssClasses, strings } from '@douyinfe/semi-foundation/toast/constants';
 import BaseComponent from '../_base/baseComponent';
 import Toast from './toast';
 import '@douyinfe/semi-foundation/toast/toast.scss';
-import ToastTransition from './ToastTransition';
 import getUuid from '@douyinfe/semi-foundation/utils/uuid';
 import useToast from './useToast';
 import { ConfigProps, ToastInstance, ToastProps, ToastState } from '@douyinfe/semi-foundation/toast/toastFoundation';
 import { Motion } from '../_base/base';
+import CSSAnimation from '../_cssAnimation';
+import cls from 'classnames';
+
 
-export type { ToastTransitionProps } from './ToastTransition';
 export interface ToastReactProps extends ToastProps{
     id?: string;
     style?: CSSProperties;
@@ -56,7 +57,7 @@ const createBaseToast = () => class ToastList extends BaseComponent<ToastListPro
         this.state = {
             list: [],
             removedItems: [],
-            updatedItems: []
+            updatedItems: [],
         };
         this.foundation = new ToastListFoundation(this.adapter);
     }
@@ -219,23 +220,19 @@ const createBaseToast = () => class ToastList extends BaseComponent<ToastListPro
 
         return (
             <React.Fragment>
-                {list.map((item, index) =>
-                    (item.motion ? (
-                        <ToastTransition key={item.id || index} motion={item.motion}>
-                            {removedItems.find(removedItem => removedItem.id === item.id) ?
-                                null :
-                                transitionStyle => (
-                                    <Toast
-                                        {...item}
-                                        style={{ ...transitionStyle, ...item.style }}
-                                        close={id => this.remove(id)}
-                                        ref={refFn}
-                                    />
-                                )}
-                        </ToastTransition>
-                    ) : (
-                        <Toast {...item} style={{ ...item.style }} close={id => this.remove(id)} ref={refFn} />
-                    ))
+                {list.map((item, index) =>{
+                    const isRemoved = removedItems.includes(item);
+                    return <CSSAnimation key={item.id} motion={item.motion} animationState={isRemoved?"leave":"enter"} startClassName={isRemoved?`${cssClasses.PREFIX}-animation-hide`:`${cssClasses.PREFIX}-animation-show`}>
+                        {
+                            ({animationClassName,animationEventsNeedBind,isAnimating})=>{
+                                return  (isRemoved && !isAnimating) ? null : <Toast {...item} className={cls({
+                                    [item.className]:Boolean(item.className),
+                                    [animationClassName]:true
+                                })} {...animationEventsNeedBind} style={{ ...item.style }} close={id => this.remove(id)} ref={refFn} />
+                            }
+                        }
+                    </CSSAnimation>
+                }
                 )}
             </React.Fragment>
         );

+ 5 - 0
packages/semi-ui/toast/toast.tsx

@@ -17,9 +17,12 @@ export interface ToastReactProps extends ToastProps {
     style?: CSSProperties;
     icon?: React.ReactNode;
     content: React.ReactNode;
+    onAnimationEnd?: (e:React.AnimationEvent) => void;
+    onAnimationStart?: (e:React.AnimationEvent) => void;
 }
 
 class Toast extends BaseComponent<ToastReactProps, ToastState> {
+
     static contextType = ConfigContext;
     static propTypes = {
         onClose: PropTypes.func,
@@ -130,6 +133,8 @@ class Toast extends BaseComponent<ToastReactProps, ToastState> {
                 style={style}
                 onMouseEnter={this.clearCloseTimer}
                 onMouseLeave={this.startCloseTimer}
+                onAnimationStart={this.props.onAnimationStart}
+                onAnimationEnd={this.props.onAnimationEnd}
             >
                 <div className={`${prefixCls}-content`}>
                     {this.renderIcon()}