From 6d3e0b006c5908160eab8642844fc5f3dcb54bc0 Mon Sep 17 00:00:00 2001
From: luckyadam <weitaozsh@gmail.com>
Date: Thu, 17 Dec 2020 16:57:15 +0800
Subject: [PATCH] =?UTF-8?q?fix(SwipeAction):=20=E4=BF=AE=E5=A4=8D=20SwipeA?=
 =?UTF-8?q?ction=20=E6=BB=91=E5=8A=A8=E5=8D=A1=E9=A1=BF=E7=9A=84=E9=97=AE?=
 =?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/taro-ui/config/rollup.config.js      |  11 -
 packages/taro-ui/package.json                 |   1 -
 .../src/components/swipe-action/index.tsx     | 286 ++++++++----------
 .../src/style/components/swipe-action.scss    |   7 +
 packages/taro-ui/types/swipe-action.d.ts      |   1 +
 5 files changed, 136 insertions(+), 170 deletions(-)

diff --git a/packages/taro-ui/config/rollup.config.js b/packages/taro-ui/config/rollup.config.js
index a1ba6b589..237f2940a 100644
--- a/packages/taro-ui/config/rollup.config.js
+++ b/packages/taro-ui/config/rollup.config.js
@@ -29,17 +29,6 @@ export default {
       file: resolveFile(Package.module),
       format: 'es',
       sourcemap: true
-    },
-    {
-      file: resolveFile(Package.browser),
-      format: 'umd',
-      name: 'taro-ui',
-      sourcemap: true,
-      globals: {
-        react: 'React',
-        '@tarojs/components': 'components',
-        '@tarojs/taro': 'Taro'
-      }
     }
   ],
   external: externalPackages,
diff --git a/packages/taro-ui/package.json b/packages/taro-ui/package.json
index f61c20b43..4467fdb9b 100644
--- a/packages/taro-ui/package.json
+++ b/packages/taro-ui/package.json
@@ -2,7 +2,6 @@
   "name": "taro-ui",
   "version": "3.0.0-alpha.3",
   "description": "UI KIT for Taro",
-  "browser": "dist/index.umd.js",
   "module": "dist/index.esm.js",
   "main": "dist/index.js",
   "source": "src/index.ts",
diff --git a/packages/taro-ui/src/components/swipe-action/index.tsx b/packages/taro-ui/src/components/swipe-action/index.tsx
index d68c13a48..873f3c65a 100644
--- a/packages/taro-ui/src/components/swipe-action/index.tsx
+++ b/packages/taro-ui/src/components/swipe-action/index.tsx
@@ -1,20 +1,14 @@
 import classNames from 'classnames'
-import _inRange from 'lodash/inRange'
-import _isEmpty from 'lodash/isEmpty'
 import PropTypes, { InferProps } from 'prop-types'
 import React from 'react'
-import { Text, View } from '@tarojs/components'
-import { CommonEvent, ITouchEvent } from '@tarojs/components/types/common'
+import { Text, View, MovableArea, MovableView } from '@tarojs/components'
+import { CommonEvent } from '@tarojs/components/types/common'
 import {
   AtSwipeActionProps,
   AtSwipeActionState,
   SwipeActionOption
 } from '../../../types/swipe-action'
-import {
-  delayGetClientRect,
-  delayGetScrollOffset,
-  uuid
-} from '../../common/utils'
+import { delayQuerySelector, uuid } from '../../common/utils'
 import AtSwipeActionOptions from './options/index'
 
 export default class AtSwipeAction extends React.Component<
@@ -24,48 +18,22 @@ export default class AtSwipeAction extends React.Component<
   public static defaultProps: AtSwipeActionProps
   public static propTypes: InferProps<AtSwipeActionProps>
 
-  private endValue: number
-  private startX: number
-  private startY: number
   private maxOffsetSize: number
-  private domInfo: any
-  private isMoving: boolean
-  private isTouching: boolean
+  private moveX: number
+  private eleWidth: number
 
   public constructor(props: AtSwipeActionProps) {
     super(props)
     const { isOpened } = props
-    this.endValue = 0
-    this.startX = 0
-    this.startY = 0
     this.maxOffsetSize = 0
-    this.domInfo = {
-      top: 0,
-      bottom: 0,
-      left: 0,
-      right: 0
-    }
-    this.isMoving = false
-    this.isTouching = false
     this.state = {
       componentId: uuid(),
       offsetSize: 0,
-      _isOpened: !!isOpened
+      _isOpened: !!isOpened,
+      needAnimation: false
     }
-  }
-
-  private getDomInfo(): Promise<void> {
-    return Promise.all([
-      delayGetClientRect({
-        delayTime: 0,
-        selectorStr: `#swipeAction-${this.state.componentId}`
-      }),
-      delayGetScrollOffset({ delayTime: 0 })
-    ]).then(([rect, scrollOffset]) => {
-      rect[0].top += scrollOffset[0].scrollTop
-      rect[0].bottom += scrollOffset[0].scrollTop
-      this.domInfo = rect[0]
-    })
+    this.moveX = 0
+    this.eleWidth = 0
   }
 
   public UNSAFE_componentWillReceiveProps(nextProps: AtSwipeActionProps): void {
@@ -77,31 +45,49 @@ export default class AtSwipeAction extends React.Component<
     }
   }
 
-  private _reset(isOpened: boolean): void {
-    this.isMoving = false
-    this.isTouching = false
+  public componentDidMount(): void {
+    if (this.eleWidth === 0) {
+      delayQuerySelector(`#swipeAction-${this.state.componentId}`, 0).then(
+        res => {
+          if (res[0]) {
+            this.eleWidth = res[0].width
+          }
+        }
+      )
+    }
+  }
 
+  public componentDidUpdate(): void {
+    delayQuerySelector(`#swipeAction-${this.state.componentId}`, 0).then(
+      res => {
+        if (res[0]) {
+          this.eleWidth = res[0].width
+        }
+      }
+    )
+  }
+
+  private _reset(isOpened: boolean): void {
     if (isOpened) {
-      this.endValue = -this.maxOffsetSize
       this.setState({
         _isOpened: true,
-        offsetSize: -this.maxOffsetSize
+        offsetSize: 0
       })
     } else {
-      this.endValue = 0
-      this.setState({
-        offsetSize: 0,
-        _isOpened: false
-      })
+      this.setState(
+        {
+          offsetSize: this.moveX
+        },
+        () => {
+          this.setState({
+            offsetSize: this.maxOffsetSize,
+            _isOpened: false
+          })
+        }
+      )
     }
   }
 
-  private computeTransform = (value: number): string | null =>
-    // if (Taro.getEnv() === Taro.ENV_TYPE.ALIPAY) {
-    //   return !_isNil(value) ? `translate3d(${value}px,0,0)` : null
-    // }
-    value ? `translate3d(${value}px,0,0)` : null
-
   private handleOpened = (event: CommonEvent): void => {
     const { onOpened } = this.props
     if (typeof onOpened === 'function' && this.state._isOpened) {
@@ -116,78 +102,16 @@ export default class AtSwipeAction extends React.Component<
     }
   }
 
-  private handleTouchStart = (e: ITouchEvent): void => {
-    const { clientX, clientY } = e.touches[0]
-
-    if (this.props.disabled) return
-
-    this.getDomInfo()
-
-    this.startX = clientX
-    this.startY = clientY
-    this.isTouching = true
-  }
-
-  private handleTouchMove = (e: ITouchEvent): void => {
-    if (_isEmpty(this.domInfo)) {
-      return
-    }
-
-    const { startX, startY } = this
-    const { top, bottom, left, right } = this.domInfo
-    const { clientX, clientY, pageX, pageY } = e.touches[0]
-
-    const x = Math.abs(clientX - startX)
-    const y = Math.abs(clientY - startY)
-
-    const inDom = _inRange(pageX, left, right) && _inRange(pageY, top, bottom)
-
-    if (!this.isMoving && inDom) {
-      this.isMoving =
-        y === 0 ||
-        x / y >= Number.parseFloat(Math.tan((45 * Math.PI) / 180).toFixed(2))
-    }
-
-    if (this.isTouching && this.isMoving) {
-      e.preventDefault()
-
-      const offsetSize = clientX - this.startX
-      const isRight = offsetSize > 0
-
-      if (this.state.offsetSize === 0 && isRight) return
-
-      const value = this.endValue + offsetSize
-      this.setState({
-        offsetSize: value >= 0 ? 0 : value
-      })
-    }
-  }
-
-  private handleTouchEnd = (event: ITouchEvent): void => {
-    this.isTouching = false
-
-    const { offsetSize } = this.state
-
-    this.endValue = offsetSize
-
-    const breakpoint = this.maxOffsetSize / 2
-    const absOffsetSize = Math.abs(offsetSize)
-
-    if (absOffsetSize > breakpoint) {
-      this._reset(true)
-      this.handleOpened(event)
-      return
-    }
-
-    this._reset(false) // TODO: Check behavior
-    this.handleClosed(event)
-  }
-
   private handleDomInfo = ({ width }: { width: number }): void => {
     const { _isOpened } = this.state
 
     this.maxOffsetSize = width
     this._reset(_isOpened)
+    setTimeout(() => {
+      this.setState({
+        needAnimation: true
+      })
+    }, 0)
   }
 
   private handleClick = (
@@ -206,51 +130,97 @@ export default class AtSwipeAction extends React.Component<
     }
   }
 
+  onTouchEnd = e => {
+    if (this.moveX === 0) {
+      this._reset(true)
+      this.handleOpened(e)
+      return
+    }
+    if (this.moveX === this.maxOffsetSize) {
+      this._reset(false)
+      this.handleClosed(e)
+      return
+    }
+    if (this.state._isOpened && this.moveX > 0) {
+      this._reset(false)
+      this.handleClosed(e)
+      return
+    }
+    if (this.maxOffsetSize - this.moveX < this.maxOffsetSize * 0.4) {
+      this._reset(false)
+      this.handleClosed(e)
+    } else {
+      this._reset(true)
+      this.handleOpened(e)
+    }
+  }
+
+  onChange = e => {
+    this.moveX = e.detail.x
+  }
+
   public render(): JSX.Element {
-    const { offsetSize, componentId } = this.state
+    const { componentId, offsetSize, needAnimation } = this.state
     const { options } = this.props
     const rootClass = classNames('at-swipe-action', this.props.className)
-    const transform = this.computeTransform(offsetSize)
-    const transformStyle: React.CSSProperties = transform ? { transform } : {}
 
     return (
       <View
         id={`swipeAction-${componentId}`}
         className={rootClass}
-        onTouchMove={this.handleTouchMove}
-        onTouchEnd={this.handleTouchEnd}
-        onTouchStart={this.handleTouchStart}
+        style={{
+          width: this.eleWidth === 0 ? '100%' : `${this.eleWidth}px`
+        }}
       >
-        <View
-          className={classNames('at-swipe-action__content', {
-            animtion: !this.isTouching
-          })}
-          style={transformStyle}
+        <MovableArea
+          className='at-swipe-action__area'
+          style={{
+            width:
+              this.eleWidth === 0
+                ? '100%'
+                : `${this.eleWidth + this.maxOffsetSize}px`,
+            transform:
+              this.eleWidth === 0
+                ? `translate(0, 0)`
+                : `translate(-${this.maxOffsetSize}px, 0)`
+          }}
         >
-          {this.props.children}
-        </View>
-
-        {Array.isArray(options) && options.length > 0 ? (
-          <AtSwipeActionOptions
-            options={options}
-            componentId={componentId}
-            onQueryedDom={this.handleDomInfo}
+          <MovableView
+            className='at-swipe-action__content'
+            direction='horizontal'
+            damping={50}
+            x={offsetSize}
+            onTouchEnd={this.onTouchEnd}
+            onChange={this.onChange}
+            animation={needAnimation}
+            style={{
+              width: this.eleWidth === 0 ? '100%' : `${this.eleWidth}px`
+            }}
           >
-            {options.map((item, key) => (
-              <View
-                key={`${item.text}-${key}`}
-                style={item.style}
-                onClick={(e): void => this.handleClick(item, key, e)}
-                className={classNames(
-                  'at-swipe-action__option',
-                  item.className
-                )}
-              >
-                <Text className='option__text'>{item.text}</Text>
-              </View>
-            ))}
-          </AtSwipeActionOptions>
-        ) : null}
+            {this.props.children}
+          </MovableView>
+          {Array.isArray(options) && options.length > 0 ? (
+            <AtSwipeActionOptions
+              options={options}
+              componentId={componentId}
+              onQueryedDom={this.handleDomInfo}
+            >
+              {options.map((item, key) => (
+                <View
+                  key={`${item.text}-${key}`}
+                  style={item.style}
+                  onClick={(e): void => this.handleClick(item, key, e)}
+                  className={classNames(
+                    'at-swipe-action__option',
+                    item.className
+                  )}
+                >
+                  <Text className='option__text'>{item.text}</Text>
+                </View>
+              ))}
+            </AtSwipeActionOptions>
+          ) : null}
+        </MovableArea>
       </View>
     )
   }
diff --git a/packages/taro-ui/src/style/components/swipe-action.scss b/packages/taro-ui/src/style/components/swipe-action.scss
index 827ba2485..db83c9a8a 100644
--- a/packages/taro-ui/src/style/components/swipe-action.scss
+++ b/packages/taro-ui/src/style/components/swipe-action.scss
@@ -4,12 +4,19 @@
 .at-swipe-action {
   position: relative;
   overflow: hidden;
+  width: 100%;
+
+  &__area {
+    height: auto;
+  }
 
   /* elements */
   &__content {
     position: relative;
     font-size: $font-size-lg;
     background-color: $at-swipe-action-bg-color;
+    display: flex;
+    height: 100%;
     z-index: 2;
 
     &.animtion {
diff --git a/packages/taro-ui/types/swipe-action.d.ts b/packages/taro-ui/types/swipe-action.d.ts
index d8a3f2150..fc2d85e5c 100644
--- a/packages/taro-ui/types/swipe-action.d.ts
+++ b/packages/taro-ui/types/swipe-action.d.ts
@@ -59,6 +59,7 @@ export interface AtSwipeActionState {
   componentId: string
   offsetSize: number
   _isOpened: boolean
+  needAnimation: boolean
 }
 
 export interface AtSwipeActionOptionsProps extends AtComponent {