浏览代码

Merge branch main into release

pointhalo 2 年之前
父节点
当前提交
685e01e0c9

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

@@ -708,7 +708,7 @@ WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/
 ## FAQ
 ## FAQ
 
 
 -  #### 为什么使用 LocaleProvider 后, Modal.confirm 确认、取消按钮的文本没有国际化?
 -  #### 为什么使用 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 的文本。   
     你可以通过 `okText` 和 `cancelText` 这两个属性来根据 Locale 重新设置 i18 的文本。   
     在1.2版本之后,你也可以通过 Modal.useModal 方法来返回 modal 实体以及 contextHolder 节点。将 contextHolder 插入到你需要获取 context 位置,即可使 Modal 获取到对应的 Context,如 ConfigProvider 或者 LocaleProvider 的配置。
     在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】
 - 【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 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)
     - 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】
 - 【Fix】
     - 修复 Popover 中的 Transfer 在拖拽时导致 Popover 意外关闭问题  [#1226](https://github.com/DouyinFE/semi-design/issues/1226)
     - 修复 Popover 中的 Transfer 在拖拽时导致 Popover 意外关闭问题  [#1226](https://github.com/DouyinFE/semi-design/issues/1226)
     - 修复 弹出层中的 Transfer/ TagInput 在拖拽时被拖拽项消失问题  [#1149](https://github.com/DouyinFE/semi-design/issues/1149)
     - 修复 弹出层中的 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;
     notifyClose: () => void;
     toggleDisplayNone: (displayNone: boolean, callback?: (displayNone: boolean) => void) => void;
     toggleDisplayNone: (displayNone: boolean, callback?: (displayNone: boolean) => void) => void;
     notifyFullScreen: (isFullScreen: boolean) => void;
     notifyFullScreen: (isFullScreen: boolean) => void;
-    getProps: () => ModalProps;
-    setShouldRender:(shouldRender:boolean)=>void
+    getProps: () => ModalProps
 }
 }
 
 
 export interface ModalProps {
 export interface ModalProps {
@@ -59,8 +58,7 @@ export interface ModalProps {
 
 
 export interface ModalState {
 export interface ModalState {
     displayNone: boolean;
     displayNone: boolean;
-    isFullScreen: boolean;
-    shouldRender:boolean
+    isFullScreen: boolean
 }
 }
 
 
 export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
 export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
@@ -101,9 +99,6 @@ export default class ModalFoundation extends BaseFoundation<ModalAdapter> {
         this._adapter.toggleDisplayNone(displayNone, callback);
         this._adapter.toggleDisplayNone(displayNone, callback);
     };
     };
 
 
-    setShouldRender=(shouldRender)=>{
-        this._adapter.setShouldRender(shouldRender);
-    }
 
 
     // // eslint-disable-next-line max-len
     // // eslint-disable-next-line max-len
     // mergeMotionProp = (motion: Motion, prop: string, cb: () => void) => {
     // 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 {
 export interface SideSheetState {
-    displayNone: boolean;
-    shouldRender: boolean
+    displayNone: boolean
 }
 }
 
 
 export interface SideSheetAdapter extends DefaultAdapter<SideSheetProps, SideSheetState> {
 export interface SideSheetAdapter extends DefaultAdapter<SideSheetProps, SideSheetState> {
@@ -43,8 +42,7 @@ export interface SideSheetAdapter extends DefaultAdapter<SideSheetProps, SideShe
     notifyVisibleChange: (visible: boolean) => void;
     notifyVisibleChange: (visible: boolean) => void;
     setOnKeyDownListener: () => void;
     setOnKeyDownListener: () => void;
     removeKeyDownListener: () => 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) {
     onVisibleChange(visible: boolean) {
         this._adapter.notifyVisibleChange(visible);
         this._adapter.notifyVisibleChange(visible);

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

@@ -20,6 +20,10 @@ $module: #{$prefix}-tagInput;
         &-item {
         &-item {
             display: flex;
             display: flex;
             align-items: center;
             align-items: center;
+
+            &-move {
+                z-index: $z-tagInput_drag_item_move;
+            }
         }
         }
 
 
         &-handler {
         &-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; // 标签输入框描边宽度 - 选中态
 $width-tagInput-border-focus: $border-thickness-control-focus; // 标签输入框描边宽度 - 选中态
 
 
 $radius-tagInput: var(--semi-border-radius-small); // 标签输入框圆角
 $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;
                 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
 $font-transfer_header_all-fontWeight: 600; // 穿梭框字重
 $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: 1070; // Image 组件预览层z-index
 $z-image_preview_header: 1; // Image 组件预览层中 header 部分 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
 $font-family-regular: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
 $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: {
     Table: {
         emptyText: 'Không kết quả',
         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: {
     Select: {
         emptyText: 'Không kết quả',
         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 = {
         this.state = {
             displayNone: !props.visible,
             displayNone: !props.visible,
             isFullScreen: props.fullScreen,
             isFullScreen: props.fullScreen,
-            shouldRender: this.props.visible || (this.props.keepDOM && !this.props.lazyRender)
         };
         };
         this.foundation = new ModalFoundation(this.adapter);
         this.foundation = new ModalFoundation(this.adapter);
         this.modalRef = React.createRef();
         this.modalRef = React.createRef();
@@ -169,11 +168,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                     this.setState({ isFullScreen });
                     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();
             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){
         if (!prevState.displayNone && this.state.displayNone){
             this.foundation.afterHide();
             this.foundation.afterHide();
         }
         }
@@ -278,8 +267,6 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
     updateState = () => {
     updateState = () => {
         const { visible } = this.props;
         const { visible } = this.props;
         this.foundation.toggleDisplayNone(!visible);
         this.foundation.toggleDisplayNone(!visible);
-        const shouldRender = this.props.visible || (this.props.keepDOM && (!this.props.lazyRender || this._haveRendered));
-        this.foundation.setShouldRender(shouldRender);
     };
     };
 
 
     renderFooter = (): ReactNode => {
     renderFooter = (): ReactNode => {
@@ -375,9 +362,12 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
             [`${cssClasses.DIALOG}-displayNone`]: keepDOM && this.state.displayNone,
             [`${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;
             this._haveRendered = true;
         }
         }
+
         return (
         return (
             <Portal style={wrapperStyle} getPopupContainer={getPopupContainer}>
             <Portal style={wrapperStyle} getPopupContainer={getPopupContainer}>
                 <CSSAnimation
                 <CSSAnimation
@@ -398,7 +388,7 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                             >
                             >
                                 {
                                 {
                                     ({ animationClassName: maskAnimationClassName, animationEventsNeedBind: maskAnimationEventsNeedBind })=>{
                                     ({ animationClassName: maskAnimationClassName, animationEventsNeedBind: maskAnimationEventsNeedBind })=>{
-                                        return this.state.shouldRender ? <ModalContent
+                                        return shouldRender ? <ModalContent
                                             {...restProps}
                                             {...restProps}
                                             contentExtraProps={animationEventsNeedBind}
                                             contentExtraProps={animationEventsNeedBind}
                                             maskExtraProps={maskAnimationEventsNeedBind}
                                             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) {
     constructor(props: SideSheetReactProps) {
         super(props);
         super(props);
-        this.state = { displayNone: !this.props.visible, shouldRender: this.props.visible };
+        this.state = { displayNone: !this.props.visible };
         this.foundation = new SideSheetFoundation(this.adapter);
         this.foundation = new SideSheetFoundation(this.adapter);
     }
     }
 
 
@@ -128,11 +128,6 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
                     this.setState({ displayNone: displayNone });
                     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) {
         if (prevState.displayNone !== this.state.displayNone) {
             this.foundation.onVisibleChange(!this.state.displayNone);
             this.foundation.onVisibleChange(!this.state.displayNone);
@@ -192,8 +183,6 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
     };
     };
 
 
     updateState = () => {
     updateState = () => {
-        const shouldRender = (this.props.visible || this.props.keepDOM);
-        this.foundation.setShouldRender(shouldRender);
         this.foundation.toggleDisplayNone(!this.props.visible);
         this.foundation.toggleDisplayNone(!this.props.visible);
     }
     }
 
 
@@ -235,7 +224,7 @@ export default class SideSheet extends BaseComponent<SideSheetReactProps, SideSh
             height: sheetHeight,
             height: sheetHeight,
             onClose: this.handleCancel,
             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.
         // 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={
         return <CSSAnimation motion={this.props.motion} animationState={visible ? 'enter' : 'leave'} startClassName={
             visible ? `${prefixCls}-animation-mask_show` : `${prefixCls}-animation-mask_hide`
             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*/}
                         onAnimationEnd={this.updateState /* for no mask case*/}
                     >
                     >
                         {({ animationClassName, animationStyle, animationEventsNeedBind }) => {
                         {({ animationClassName, animationStyle, animationEventsNeedBind }) => {
-                            return this.state.shouldRender ? <SideSheetContent
+                            return shouldRender ? <SideSheetContent
                                 {...contentProps}
                                 {...contentProps}
                                 maskExtraProps={maskAnimationEventsNeedBind}
                                 maskExtraProps={maskAnimationEventsNeedBind}
                                 wrapperExtraProps={animationEventsNeedBind}
                                 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 = {
 const style = {
   width: 400,
   width: 400,
   marginTop: 10,
   marginTop: 10,
@@ -458,3 +457,73 @@ export const TagInputInForm = () => (
 PrefixSuffix.story = {
 PrefixSuffix.story = {
   name: 'TagInputInForm'
   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) {
         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
             // @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 (
         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 { omit, values } from 'lodash';
 import './transfer.scss';
 import './transfer.scss';
 import { SortableContainer, SortableElement, sortableHandle } from 'react-sortable-hoc';
 import { SortableContainer, SortableElement, sortableHandle } from 'react-sortable-hoc';
@@ -786,147 +780,51 @@ CustomRenderWithDragSort.story = {
   name: 'customRender with drag sort',
   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 (
   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;
 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> {
 class Transfer extends BaseComponent<TransferProps, TransferState> {
     static propTypes = {
     static propTypes = {
         style: PropTypes.object,
         style: PropTypes.object,
@@ -572,22 +588,14 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
     }
     }
 
 
     renderRightSortableList(selectedData: Array<ResolvedDataItem>) {
     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
         // @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;
         return sortList;
     }
     }