Quellcode durchsuchen

Merge branch main into release

pointhalo vor 2 Jahren
Ursprung
Commit
685e01e0c9

+ 1 - 1
content/show/modal/index.md

@@ -708,7 +708,7 @@ WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/
 ## FAQ
 
 -  #### 为什么使用 LocaleProvider 后, Modal.confirm 确认、取消按钮的文本没有国际化?
-    Modal 使用 Portal 将浮层节点插入到 DOM 树中。但这个操作仅能改变节点在 DOM 树中的位置,无法改变节点在 React 节点树中的位置,LocalProvider是基于 Contenxt 机制传递的,必须是从属的 React 子结点才可消费到 Local 相关 Contenxt。因此命令式的 Modal 的内置文本无法自动适配国际化。
+    Modal 使用 Portal 将浮层节点插入到 DOM 树中。但这个操作仅能改变节点在 DOM 树中的位置,无法改变节点在 React 节点树中的位置,LocalProvider是基于 Context 机制传递的,必须是从属的 React 子结点才可消费到 Local 相关 Context。因此命令式的 Modal 的内置文本无法自动适配国际化。
     你可以通过 `okText` 和 `cancelText` 这两个属性来根据 Locale 重新设置 i18 的文本。   
     在1.2版本之后,你也可以通过 Modal.useModal 方法来返回 modal 实体以及 contextHolder 节点。将 contextHolder 插入到你需要获取 context 位置,即可使 Modal 获取到对应的 Context,如 ConfigProvider 或者 LocaleProvider 的配置。
 

+ 1 - 1
content/start/changelog/index-en-US.md

@@ -16,7 +16,7 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 ---
 
-#### 🎉 2.23.0 (2022-11-11)
+#### 🎉 2.23.1 (2022-11-11)
 - 【Fix】
     - Fixed the problem that Transfer in Popover caused Popover to close unexpectedly when dragging [#1226](https://github.com/DouyinFE/semi-design/issues/1226)
     - Fixed the issue that the Transfer/ TagInput in the pop-up layer disappeared when the dragged item was dragged  [#1149](https://github.com/DouyinFE/semi-design/issues/1149)

+ 1 - 1
content/start/changelog/index.md

@@ -17,7 +17,7 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 ---
 
 
-#### 🎉 2.23.0 (2022-11-11)
+#### 🎉 2.23.1 (2022-11-11)
 - 【Fix】
     - 修复 Popover 中的 Transfer 在拖拽时导致 Popover 意外关闭问题  [#1226](https://github.com/DouyinFE/semi-design/issues/1226)
     - 修复 弹出层中的 Transfer/ TagInput 在拖拽时被拖拽项消失问题  [#1149](https://github.com/DouyinFE/semi-design/issues/1149)

+ 2 - 7
packages/semi-foundation/modal/modalFoundation.ts

@@ -11,8 +11,7 @@ export interface ModalAdapter extends DefaultAdapter<ModalProps, ModalState> {
     notifyClose: () => void;
     toggleDisplayNone: (displayNone: boolean, callback?: (displayNone: boolean) => void) => void;
     notifyFullScreen: (isFullScreen: boolean) => void;
-    getProps: () => ModalProps;
-    setShouldRender:(shouldRender:boolean)=>void
+    getProps: () => ModalProps
 }
 
 export interface ModalProps {
@@ -59,8 +58,7 @@ export interface ModalProps {
 
 export interface ModalState {
     displayNone: boolean;
-    isFullScreen: boolean;
-    shouldRender:boolean
+    isFullScreen: boolean
 }
 
 export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
@@ -101,9 +99,6 @@ export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
         this._adapter.toggleDisplayNone(displayNone, callback);
     };
 
-    setShouldRender=(shouldRender)=>{
-        this._adapter.setShouldRender(shouldRender);
-    }
 
     // // eslint-disable-next-line max-len
     // mergeMotionProp = (motion: Motion, prop: string, cb: () => void) => {

+ 2 - 7
packages/semi-foundation/sideSheet/sideSheetFoundation.ts

@@ -32,8 +32,7 @@ export interface SideSheetProps {
 }
 
 export interface SideSheetState {
-    displayNone: boolean;
-    shouldRender: boolean
+    displayNone: boolean
 }
 
 export interface SideSheetAdapter extends DefaultAdapter<SideSheetProps, SideSheetState> {
@@ -43,8 +42,7 @@ export interface SideSheetAdapter extends DefaultAdapter<SideSheetProps, SideShe
     notifyVisibleChange: (visible: boolean) => void;
     setOnKeyDownListener: () => void;
     removeKeyDownListener: () => void;
-    toggleDisplayNone: (displayNone: boolean) => void;
-    setShouldRender: (shouldRender: boolean) => void
+    toggleDisplayNone: (displayNone: boolean) => void
 }
 
 
@@ -91,9 +89,6 @@ export default class SideSheetFoundation extends BaseFoundation<SideSheetAdapter
         }
     }
 
-    setShouldRender(shouldRender: boolean) {
-        this._adapter.setShouldRender(shouldRender);
-    }
 
     onVisibleChange(visible: boolean) {
         this._adapter.notifyVisibleChange(visible);

+ 4 - 0
packages/semi-foundation/tagInput/tagInput.scss

@@ -20,6 +20,10 @@ $module: #{$prefix}-tagInput;
         &-item {
             display: flex;
             align-items: center;
+
+            &-move {
+                z-index: $z-tagInput_drag_item_move;
+            }
         }
 
         &-handler {

+ 2 - 0
packages/semi-foundation/tagInput/variables.scss

@@ -47,3 +47,5 @@ $width-tagInput-border-hover: $width-tagInput-border-default; // 标签输入框
 $width-tagInput-border-focus: $border-thickness-control-focus; // 标签输入框描边宽度 - 选中态
 
 $radius-tagInput: var(--semi-border-radius-small); // 标签输入框圆角
+
+$z-tagInput_drag_item_move: 2000 !default; // 标签输入框中正在拖拽元素的z-index

+ 9 - 3
packages/semi-foundation/transfer/transfer.scss

@@ -165,9 +165,15 @@ $module: #{$prefix}-transfer;
                 word-break: break-all;
             }
 
-            &-drag-handler {
-                margin-right: $spacing-transfer_right_item_drag_handler-marginRight;
-                flex-shrink: 0;
+            &-drag {
+                &-handler {
+                    margin-right: $spacing-transfer_right_item_drag_handler-marginRight;
+                    flex-shrink: 0;
+                }
+
+                &-item-move {
+                    z-index: $z-transfer_right_item_drag_item_move;
+                }            
             }
         }
 

+ 2 - 0
packages/semi-foundation/transfer/variables.scss

@@ -53,3 +53,5 @@ $radius-transfer: var(--semi-border-radius-medium); // 穿梭框圆角
 
 // Font
 $font-transfer_header_all-fontWeight: 600; // 穿梭框字重
+
+$z-transfer_right_item_drag_item_move: 2000 !default; // 穿梭框右侧面板中正在拖拽元素的z-index

+ 3 - 0
packages/semi-theme-default/scss/variables.scss

@@ -45,6 +45,9 @@ $z-tooltip: 1060; // tooltip 组件 z-index
 $z-image_preview: 1070; // Image 组件预览层z-index
 $z-image_preview_header: 1; // Image 组件预览层中 header 部分 z-index
 
+// 正在拖拽中的元素的 z-index,需要高于所有的弹出层组件 z-index
+$z-transfer_right_item_drag_item_move: 2000; // 穿梭框右侧面板中正在拖拽元素的z-index
+$z-tagInput_drag_item_move: 2000; // 标签输入框中正在拖拽元素的z-index
 
 // font
 $font-family-regular: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',

+ 1 - 1
packages/semi-ui/locale/source/vi_VN.ts

@@ -99,7 +99,7 @@ const local: Locale = {
     },
     Table: {
         emptyText: 'Không kết quả',
-        pageText: 'Hiển thị $ {currentStart} đến $ {currentEnd} trong tổng số $ {total}}',
+        pageText: 'Hiển thị ${currentStart} đến ${currentEnd} trong tổng số ${total}}',
     },
     Select: {
         emptyText: 'Không kết quả',

+ 5 - 15
packages/semi-ui/modal/Modal.tsx

@@ -121,7 +121,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
         this.state = {
             displayNone: !props.visible,
             isFullScreen: props.fullScreen,
-            shouldRender: this.props.visible || (this.props.keepDOM && !this.props.lazyRender)
         };
         this.foundation = new ModalFoundation(this.adapter);
         this.modalRef = React.createRef();
@@ -169,11 +168,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                     this.setState({ isFullScreen });
                 }
             },
-            setShouldRender: (shouldRender)=>{
-                if (shouldRender!==this.state.shouldRender){
-                    this.setState({ shouldRender });
-                }
-            }
         };
     }
 
@@ -251,11 +245,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
             this.foundation.beforeShow();
         }
 
-        const shouldRender = this.props.visible || (this.props.keepDOM && (!this.props.lazyRender || this._haveRendered));
-        if (shouldRender === true && this.state.shouldRender === false) {
-            this.foundation.setShouldRender(true);
-        }
-
         if (!prevState.displayNone && this.state.displayNone){
             this.foundation.afterHide();
         }
@@ -278,8 +267,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
     updateState = () => {
         const { visible } = this.props;
         this.foundation.toggleDisplayNone(!visible);
-        const shouldRender = this.props.visible || (this.props.keepDOM && (!this.props.lazyRender || this._haveRendered));
-        this.foundation.setShouldRender(shouldRender);
     };
 
     renderFooter = (): ReactNode => {
@@ -375,9 +362,12 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
             [`${cssClasses.DIALOG}-displayNone`]: keepDOM && this.state.displayNone,
         });
 
-        if (this.state.shouldRender){
+        const shouldRender = this.props.visible || (this.props.keepDOM && (!this.props.lazyRender || this._haveRendered)) || (this.props.motion && !this.state.displayNone /* When there is animation, we use displayNone to judge whether animation is ended and judge whether to unmount content */);
+
+        if (shouldRender){
             this._haveRendered = true;
         }
+
         return (
             <Portal style={wrapperStyle} getPopupContainer={getPopupContainer}>
                 <CSSAnimation
@@ -398,7 +388,7 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                             >
                                 {
                                     ({ animationClassName: maskAnimationClassName, animationEventsNeedBind: maskAnimationEventsNeedBind })=>{
-                                        return this.state.shouldRender ? <ModalContent
+                                        return shouldRender ? <ModalContent
                                             {...restProps}
                                             contentExtraProps={animationEventsNeedBind}
                                             maskExtraProps={maskAnimationEventsNeedBind}

+ 3 - 14
packages/semi-ui/sideSheet/index.tsx

@@ -86,7 +86,7 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
 
     constructor(props: SideSheetReactProps) {
         super(props);
-        this.state = { displayNone: !this.props.visible, shouldRender: this.props.visible };
+        this.state = { displayNone: !this.props.visible };
         this.foundation = new SideSheetFoundation(this.adapter);
     }
 
@@ -128,11 +128,6 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
                     this.setState({ displayNone: displayNone });
                 }
             },
-            setShouldRender: (shouldRender: boolean) => {
-                if (shouldRender !== this.state.shouldRender) {
-                    this.setState({ shouldRender });
-                }
-            }
         };
     }
 
@@ -166,10 +161,6 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
         }
 
 
-        const shouldRender = (this.props.visible || this.props.keepDOM);
-        if (shouldRender === true && this.state.shouldRender === false) {
-            this.foundation.setShouldRender(true);
-        }
 
         if (prevState.displayNone !== this.state.displayNone) {
             this.foundation.onVisibleChange(!this.state.displayNone);
@@ -192,8 +183,6 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
     };
 
     updateState = () => {
-        const shouldRender = (this.props.visible || this.props.keepDOM);
-        this.foundation.setShouldRender(shouldRender);
         this.foundation.toggleDisplayNone(!this.props.visible);
     }
 
@@ -235,7 +224,7 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
             height: sheetHeight,
             onClose: this.handleCancel,
         };
-
+        const shouldRender = (this.props.visible || this.props.keepDOM) || (this.props.motion && !this.state.displayNone /* When there is animation, we use displayNone to judge whether animation is ended and judge whether to unmount content */);
         // Since user could change animate duration , we don't know which animation end first. So we call updateState func twice.
         return <CSSAnimation motion={this.props.motion} animationState={visible ? 'enter' : 'leave'} startClassName={
             visible ? `${prefixCls}-animation-mask_show` : `${prefixCls}-animation-mask_hide`
@@ -252,7 +241,7 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
                         onAnimationEnd={this.updateState /* for no mask case*/}
                     >
                         {({ animationClassName, animationStyle, animationEventsNeedBind }) => {
-                            return this.state.shouldRender ? <SideSheetContent
+                            return shouldRender ? <SideSheetContent
                                 {...contentProps}
                                 maskExtraProps={maskAnimationEventsNeedBind}
                                 wrapperExtraProps={animationEventsNeedBind}

+ 73 - 4
packages/semi-ui/tagInput/_story/tagInput.stories.jsx

@@ -1,7 +1,6 @@
-import React, { useState } from 'react';
-import { Toast, Icon, Button, Avatar, Form, Switch } from '@douyinfe/semi-ui/';
-import TagInput from '../index';
-import { IconGift, IconVigoLogo, IconClose } from '@douyinfe/semi-icons';
+import React, { useState, useCallback } from 'react';
+import { Toast, Icon, Button, Avatar, Form, Popover, SideSheet, Modal, TagInput } from '../../index';
+import { IconGift, IconVigoLogo } from '@douyinfe/semi-icons';
 const style = {
   width: 400,
   marginTop: 10,
@@ -458,3 +457,73 @@ export const TagInputInForm = () => (
 PrefixSuffix.story = {
   name: 'TagInputInForm'
 };
+
+export const TagInputInPopover = () => {
+  // 在弹出层中点击item,可拖拽item被遮挡问题:https://github.com/DouyinFE/semi-design/issues/1149
+  const [sideSheetVisible, setSideSheetVisible] = useState(false);
+  const [modalVisible, setModalVisible] = useState(false);
+  const sideSheetChange = useCallback(() => {
+    setSideSheetVisible(!sideSheetVisible);
+  }, [sideSheetVisible]);
+
+  const showDialog = () => {
+    setModalVisible(true);
+  };
+
+  const handleOk = () => {
+    setModalVisible(false);
+  };
+
+  const handleCancel = () => {
+    setModalVisible(false);
+  };
+
+  const data = Array.from({ length: 30 }, (v, i) => {
+    return {
+      label: `选项名称 ${i}`,
+      value: i,
+      disabled: false,
+      key: i
+    };
+  });
+
+  const tagInputNode = (<TagInput
+    draggable
+    allowDuplicates={false}
+    defaultValue={['抖音', '火山', '西瓜视频']}
+    placeholder='请输入...'
+    onChange={v => console.log(v)}
+  />);
+
+  return (
+    <div className="App">
+      <p>issues 1149: 在弹出层中点击item,可拖拽item被遮挡问题</p>
+      <Popover
+        trigger="click"
+        position='rightTop'
+        content={<div style={{ padding: 100 }}>{tagInputNode}</div>}
+      >
+        <Button>TagInput In Popover</Button>
+      </Popover>
+      <br /><br />
+       {/* 弹出层:sideSheet */}
+       <Button onClick={sideSheetChange}>TagInput In SideSheet</Button>
+        <SideSheet title="滑动侧边栏" visible={sideSheetVisible} onCancel={sideSheetChange} size="medium">
+          {tagInputNode}
+        </SideSheet>
+        <br /><br />
+        {/* 弹出层:Modal */}
+        <Button onClick={showDialog}>TagInput in Modal</Button>
+        <Modal
+          title="基本对话框"
+          visible={modalVisible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+          closeOnEsc={true}
+        >
+          {tagInputNode}
+        </Modal>
+    </div>
+  );
+}
+

+ 2 - 1
packages/semi-ui/tagInput/index.tsx

@@ -485,8 +485,9 @@ class TagInput extends BaseComponent<TagInputProps, TagInputState> {
         }));
 
         if (active && draggable && sortableListItems.length > 0) {
+            // helperClass:add styles to the helper(item being dragged) https://github.com/clauderic/react-sortable-hoc/issues/87
             // @ts-ignore skip SortableItem type check
-            return <SortableList useDragHandle items={sortableListItems} onSortEnd={this.onSortEnd} axis={"xy"} />;
+            return <SortableList useDragHandle helperClass={`${prefixCls}-drag-item-move`} items={sortableListItems} onSortEnd={this.onSortEnd} axis={"xy"} />;
         } 
         return (
             <>

+ 43 - 145
packages/semi-ui/transfer/_story/transfer.stories.jsx

@@ -1,11 +1,5 @@
-import React, { useState, useRef, useMemo } from 'react';
-import { Transfer, Button, Select } from '../../index';
-import Table from '../../table';
-import Avatar from '../../avatar';
-import Checkbox from '../../checkbox';
-import Icon from '../../icons';
-import Tree from '../../tree';
-import Input from '../../input';
+import React, { useState, useRef } from 'react';
+import { Transfer, Button, Popover, SideSheet, Avatar, Checkbox, Tree, Input } from '../../index';
 import { omit, values } from 'lodash';
 import './transfer.scss';
 import { SortableContainer, SortableElement, sortableHandle } from 'react-sortable-hoc';
@@ -786,147 +780,51 @@ CustomRenderWithDragSort.story = {
   name: 'customRender with drag sort',
 };
 
-export const RefMethodSearch = () => {
-  const transferRef1 = useRef(null);
-  const transferRef2 = useRef(null);
-  
-  const data = useMemo(() => (Array.from({ length: 100 }, (v, i) => {
-      return {
-          label: `选项名称 ${i}`,
-          value: i,
-          disabled: false,
-          key: i,
-      };
-  })), []);
-
-  const treeData = [
-      {
-          label: 'Asia',
-          value: 'Asia',
-          key: '0',
-          children: [
-              {
-                  label: 'AreaOne',
-                  value: 'AreaOne',
-                  key: '0-0',
-                  children: [
-                      {
-                          label: 'Beijing',
-                          value: 'Beijing',
-                          key: '0-0-0',
-                      },
-                      {
-                          label: 'Shanghai',
-                          value: 'Shanghai',
-                          key: '0-0-1',
-                      },
-                      {
-                          label: 'Chengdu',
-                          value: 'Chengdu',
-                          key: '0-0-2',
-                      },
-                  ],
-              },
-              {
-                  label: 'Japan',
-                  value: 'Japan',
-                  key: '0-1',
-                  children: [
-                      {
-                          label: 'Osaka',
-                          value: 'Osaka',
-                          key: '0-1-0',
-                      },
-                  ],
-              },
-          ],
-      },
-      {
-          label: 'North America',
-          value: 'North America',
-          key: '1',
-          children: [
-              {
-                  label: 'United States',
-                  value: 'United States',
-                  key: '1-0',
-              },
-              {
-                  label: 'Canada',
-                  value: 'Canada',
-                  key: '1-1',
-              },
-              {
-                  label: 'Mexico',
-                  value: 'Mexico',
-                  disabled: true,
-                  key: '1-2',
-              },
-              {
-                  label: 'Cuba',
-                  value: 'Cuba',
-                  key: '1-3',
-              },
-          ],
-      },
-  ];
-
-  const [v, $v] = useState(['Shanghai']);
+export const TransferInPopover = () => {
+  // 点击可拖拽item,导致弹出层消失问题:https://github.com/DouyinFE/semi-design/issues/1226
+  // 在弹出层中点击item,导致可拖拽item被遮挡问题:https://github.com/DouyinFE/semi-design/issues/1149
+  const [visible, setVisible] = useState(false);
+  const change = () => {
+      setVisible(!visible);
+  };
 
-  const list = [
-      { value: '0', label: '0', otherKey: 0 },
-      { value: '1', label: '1',  otherKey: 1 },
-      { value: '2', label: '2', otherKey: 2 },
-      { value: '3', label: '3', otherKey: 3 },
-  ];
+  const data = Array.from({ length: 30 }, (v, i) => {
+    return {
+      label: `选项名称 ${i}`,
+      value: i,
+      disabled: false,
+      key: i
+    };
+  });
 
-  const list2 = [
-      { value: 'Canada', label: 'Canada', otherKey: 0 },
-      { value: 'Shanghai', label: 'Shanghai',  otherKey: 1 },
-  ]
+  const transferNode = (
+    <Transfer
+      style={{ width: 568, height: 416 }}
+      dataSource={data}
+      defaultValue={[2, 4]}
+      draggable
+      onChange={(values, items) => console.log(values, items)}
+    />
+  );
 
   return (
+    <div className="App">
       <>
-          <p>通过 Transfer 的 search 方法进行筛选, 左侧 list 需要显示筛选结果</p>
-          <Select 
-              placeholder={'选择项目可改变transfer中input值'}
-              style={{ width: 300 }} 
-              optionList={list}
-              onSelect={(value) => {
-                  transferRef1 && transferRef1.current && transferRef1.current.search(value);
-              }}
-              showClear
-          />
-          <Transfer
-              ref={transferRef1}
-              style={{ width: 568, height: 416 }}
-              dataSource={data}
-              onChange={(values, items) => console.log(values, items)}
-              onSearch={(value) => {
-                console.log('search value', value);
-              }}
-          />
-          <p>通过 Transfer 的 search 方法进行筛选, 左侧 tree 应当呈现筛选结果</p>
-          <Select 
-              placeholder={'选择项目可改变transfer中input值'}
-              style={{ width: 300 }} 
-              optionList={list2}
-              onSelect={(value) => {
-                  transferRef2 && transferRef2.current && transferRef2.current.search(value);
-              }}
-              showClear
-          />
-          <Transfer 
-              ref={transferRef2}
-              dataSource={treeData} 
-              style={{ width: 568, height: 416 }}
-              type="treeList" 
-              value={v} 
-              onChange={$v} 
-              onSearch={(value) => {
-                console.log('search value', value);
-              }}
-          />
+        {/* 弹出层:Popover */}
+        <Popover
+          trigger="click"
+          position='rightTop'
+          content={transferNode}
+        >
+          <Button>Transfer In Popover</Button>
+        </Popover>
+        {/* 弹出层:sideSheet */}
+        <br /><br />
+        <Button onClick={change}>Transfer In SideSheet</Button>
+        <SideSheet title="滑动侧边栏" visible={visible} onCancel={change} size="medium">
+          {transferNode}
+        </SideSheet>
       </>
+    </div>
   );
-};
+}

+ 23 - 15
packages/semi-ui/transfer/index.tsx

@@ -152,6 +152,22 @@ export interface TransferProps {
 
 const prefixcls = cssClasses.PREFIX;
 
+// SortableItem & SortableList should not be assigned inside of the render function
+const SortableItem = SortableElement((
+    (props: DraggableResolvedDataItem) => (props.item.node as React.FC<DraggableResolvedDataItem>)
+));
+
+const SortableList = SortableContainer(({ items }: { items: Array<ResolvedDataItem> }) => (
+    <div className={`${prefixcls}-right-list`} role="list" aria-label="Selected list">
+        {items.map((item, index: number) => (
+            // @ts-ignore skip SortableItem type check
+            <SortableItem key={item.label} index={index} item={item} />
+        ))}
+    </div>
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore see reasons: https://github.com/clauderic/react-sortable-hoc/issues/206
+), { distance: 10 });
+
 class Transfer extends BaseComponent<TransferProps, TransferState> {
     static propTypes = {
         style: PropTypes.object,
@@ -572,22 +588,14 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
     }
 
     renderRightSortableList(selectedData: Array<ResolvedDataItem>) {
-        // when choose some items && draggable is true
-        const SortableItem = SortableElement((
-            (props: DraggableResolvedDataItem) => this.renderRightItem(props.item)) as React.FC<DraggableResolvedDataItem>
-        );
-        const SortableList = SortableContainer(({ items }: { items: Array<ResolvedDataItem> }) => (
-            <div className={`${prefixcls}-right-list`} role="list" aria-label="Selected list">
-                {items.map((item, index: number) => (
-                    // @ts-ignore skip SortableItem type check
-                    <SortableItem key={item.label} index={index} item={item} />
-                ))}
-            </div>
-        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-        // @ts-ignore see reasons: https://github.com/clauderic/react-sortable-hoc/issues/206
-        ), { distance: 10 });
+        const sortableListItems = selectedData.map(item => ({
+            ...item,
+            node: this.renderRightItem(item)
+        }));
+
+        // helperClass:add styles to the helper(item being dragged) https://github.com/clauderic/react-sortable-hoc/issues/87
         // @ts-ignore skip SortableItem type check
-        const sortList = <SortableList useDragHandle onSortEnd={this.onSortEnd} items={selectedData} />;
+        const sortList = <SortableList useDragHandle helperClass={`${prefixcls}-right-item-drag-item-move`} onSortEnd={this.onSortEnd} items={sortableListItems} />;
         return sortList;
     }