Browse Source

feat: add expanded spacing to tooltip (#1833)

* feat: add expanded spacing to tooltip

* feat: change spacing static propTypes in datepicker, dropdown, popover and selelct
YannLynn 2 years ago
parent
commit
eab3aba1c5

+ 1 - 1
content/show/popover/index-en-US.md

@@ -549,7 +549,7 @@ Please refer to [Use with Tooltip/Popconfirm](/en-US/show/tooltip#%E6%90%AD%E9%8
 | returnFocusOnClose | After pressing the Esc key, whether the focus returns to the trigger, it only takes effect when the trigger is set to hover, focus, click, etc                                                                                                | boolean | true | **2.8.0** |
 | visible | Display popup or not                                                                                                                                                                                                                          | boolean |  |
 | position | Directions, optional values: `top`, `topLeft`, `topRight`, `left`, `leftTop`, `leftBottom`, `right`, `rightTop`, `rightBottom`, `bottom`, `bottomLeft`, `bottomRight`                                                                         | string | "bottom" |
-| spacing | The distance between the out layer and the children element, in px                                                                                                                                                                            | number | 4(while showArrow=false) 10(while showArrow=true) |  |
+| spacing | The distance between the out layer and the children element, in px                                                                                                                                                                            | number| <ApiType detail='{ x: number; y: number }'>SpacingObject</ApiType>  | 4(while showArrow=false) 10(while showArrow=true) |  |
 | showArrow | Display little arrow or not                                                                                                                                                                                                                   | boolean |  |
 | trigger | Trigger mode, optional value: `hover`, `focus`, `click`, `custom`                                                                                                                                                                             | string | 'hover' |
 | stopPropagation | Whether to prevent click events on the bomb layer from bubbling                                                                                                                                                                               | boolean | false | **0.34.0** |

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

@@ -539,7 +539,7 @@ import { Button, Input, Popover, Space } from '@douyinfe/semi-ui';
 | rePosKey           | 可以更新该项值手动触发弹出层的重新定位                                                                                                         | string\|number             |            |             |
 | returnFocusOnClose | 按下 Esc 键后,焦点是否回到 trigger 上,设置 trigger 为 hover, focus, click 时生效 | boolean | true  | **2.8.0**  |
 | position           | 方向,可选值:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string                     | "bottom"                                    |            |
-| spacing            | 弹出层与 children 元素的距离,单位 px                                                                                                       | number                     | 4(showArrow=false 时) 10(showArrow=true 时) |            |
+| spacing            | 弹出层与 children 元素的距离,单位 px                                                                                                       | number| <ApiType detail='{ x: number; y: number }'>SpacingObject</ApiType>                       | 4(showArrow=false 时) 10(showArrow=true 时) |            |
 | showArrow          | 是否显示“小三角”                                                                                                                            | boolean                    |                                             |            |
 | stopPropagation    | 是否阻止弹出层上的点击事件冒泡                                                                                                              | boolean                    | false                                       | **0.34.0** |
 | trigger            | 触发方式,可选值:`hover`, `focus`, `click`, `custom`, `contextMenu`(v2.42支持)                                                          | string                     | 'hover'                                     |            |

+ 1 - 1
content/show/tooltip/index-en-US.md

@@ -349,7 +349,7 @@ import { Popconfirm, Tooltip, Button } from '@douyinfe/semi-ui';
 | preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user                               | boolean |  |  |
 | rePosKey | This value can be updated to manually trigger the repositioning of the pop-up layer.                                                                                                                                        | string | number |  |
 | style | Pop-up layer inline style                                                                                                                                                                                                   | object |  |  |
-| spacing | The distance between the pop-up layer and the `children` element                                                                                                                                                            | number |  |  |
+| spacing | The distance between the pop-up layer and the `children` element                                                                                                                                                            | number | <ApiType detail='{ x: number; y: number }'>SpacingObject</ApiType>|  |  |
 | showArrow | Does it show an arrow triangle?                                                                                                                                                                                             | boolean | true |  |
 | stopPropagation | Whether to prevent click events on the bomb layer from bubbling                                                                                                                                                             | boolean | false | **0.34.0** |
 | transformFromCenter | Whether to transform from the horizontal or vertical center of the element of the package, this parameter affects only the `tansform-origin 'of the dynamic effect transformation and generally does not need to be changed | boolean | true |

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

@@ -385,7 +385,7 @@ function Demo() {
 | preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法                                                                                                               | boolean |  |  |
 | rePosKey | 可以更新该项值手动触发弹出层的重新定位                                                                                                                                  | string\|number |  |  |
 | style    | 弹出层的内联样式                                                                                                                                             | object |  |  |
-| spacing | 弹出层与 `children` 元素的距离,单位 px                                                                                                                          | number | 8 |  |
+| spacing | 弹出层与 `children` 元素的距离,单位 px                                                                                                                          | number | <ApiType detail='{ x: number; y: number }'>SpacingObject</ApiType>  | 8 |  |
 | showArrow | 是否显示箭头三角形                                                                                                                                            | boolean | true |  |
 | stopPropagation | 是否阻止弹层上的点击事件冒泡                                                                                                                                       | boolean | false | **0.34.0** |
 | transformFromCenter | 是否从包裹的元素水平或垂直中心处变换,该参数仅影响动效变换的 `transform-origin`,一般无需改动                                                                                             | boolean | true |  |

+ 49 - 30
packages/semi-foundation/tooltip/foundation.ts

@@ -407,10 +407,19 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
         };
         const wrapperRect = (isEmpty(props.wrapperRect) ? props.wrapperRect : this._adapter.getWrapperBounding()) || { ...defaultRect as any };
         const position = props.position != null ? props.position : this.getProp('position');
-        const SPACING = spacing != null ? spacing : this.getProp('spacing');
+        const RAW_SPACING = spacing != null ? spacing : this.getProp('spacing');
         const { arrowPointAtCenter, showArrow, arrowBounding } = this.getProps();
         const pointAtCenter = showArrow && arrowPointAtCenter;
 
+        let SPACING = RAW_SPACING;
+        let ANO_SPACING = 0;
+        if (typeof RAW_SPACING !== 'number') {
+            // extended spacing api with {x: number, y: number}, the axes of the spacing is determined based on the position
+            const isTopOrBottom = position.includes('top') || position.includes('bottom');
+            SPACING = isTopOrBottom ? RAW_SPACING.y : RAW_SPACING.x;
+            ANO_SPACING = isTopOrBottom ? RAW_SPACING.x : RAW_SPACING.y;
+        }
+
         const horizontalArrowWidth = get(arrowBounding, 'width', 24);
         const verticalArrowHeight = get(arrowBounding, 'width', 24);
         const arrowOffsetY = get(arrowBounding, 'offsetY', 0);
@@ -446,7 +455,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             case 'top':
                 // left = middleX;
                 // top = triggerRect.top - SPACING;
-                left = isWidthOverFlow ? (isTriggerNearLeft ? containerRect.left + wrapperRect.width / 2 : containerRect.right - wrapperRect.width / 2 + offsetWidth): middleX;
+                left = isWidthOverFlow ? (isTriggerNearLeft ? containerRect.left + wrapperRect.width / 2 : containerRect.right - wrapperRect.width / 2 + offsetWidth): middleX + ANO_SPACING;
                 top = isHeightOverFlow ? containerRect.bottom + offsetHeight : triggerRect.top - SPACING;
                 translateX = -0.5;
                 translateY = -1;
@@ -454,14 +463,14 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             case 'topLeft':
                 // left = pointAtCenter ? middleX - offsetXWithArrow : triggerRect.left;
                 // top = triggerRect.top - SPACING;
-                left = isWidthOverFlow ? (isWrapperWidthOverflow ? containerRect.left : containerRect.right - wrapperRect.width ) : (pointAtCenter ? middleX - offsetXWithArrow : triggerRect.left);
+                left = isWidthOverFlow ? (isWrapperWidthOverflow ? containerRect.left : containerRect.right - wrapperRect.width ) : (pointAtCenter ? middleX - offsetXWithArrow + ANO_SPACING : triggerRect.left + ANO_SPACING);
                 top = isHeightOverFlow ? containerRect.bottom + offsetHeight : triggerRect.top - SPACING;
                 translateY = -1;
                 break;
             case 'topRight':
                 // left = pointAtCenter ? middleX + offsetXWithArrow : triggerRect.right;
                 // top = triggerRect.top - SPACING;
-                left = isWidthOverFlow ? containerRect.right + offsetWidth : (pointAtCenter ? middleX + offsetXWithArrow : triggerRect.right);
+                left = isWidthOverFlow ? containerRect.right + offsetWidth : (pointAtCenter ? middleX + offsetXWithArrow + ANO_SPACING : triggerRect.right + ANO_SPACING);
                 top = isHeightOverFlow ? containerRect.bottom + offsetHeight : triggerRect.top - SPACING;
                 translateY = -1;
                 translateX = -1;
@@ -471,7 +480,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                 // top = middleY;
                 // left = isWidthOverFlow? containerRect.right - SPACING : triggerRect.left - SPACING;
                 left = isWidthOverFlow ? containerRect.right + offsetWidth - SPACING + offsetXWithArrow : triggerRect.left - SPACING;
-                top = isHeightOverFlow ? (isTriggerNearTop ? containerRect.top + wrapperRect.height / 2 : containerRect.bottom - wrapperRect.height / 2 + offsetHeight): middleY;
+                top = isHeightOverFlow ? (isTriggerNearTop ? containerRect.top + wrapperRect.height / 2 : containerRect.bottom - wrapperRect.height / 2 + offsetHeight): middleY + ANO_SPACING;
                 translateX = -1;
                 translateY = -0.5;
                 break;
@@ -479,34 +488,34 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                 // left = triggerRect.left - SPACING;
                 // top = pointAtCenter ? middleY - offsetYWithArrow : triggerRect.top;
                 left = isWidthOverFlow ? containerRect.right + offsetWidth - SPACING + offsetXWithArrow : triggerRect.left - SPACING;
-                top = isHeightOverFlow ? containerRect.top : (pointAtCenter ? middleY - offsetYWithArrow : triggerRect.top);
+                top = isHeightOverFlow ? containerRect.top : (pointAtCenter ? middleY - offsetYWithArrow + ANO_SPACING : triggerRect.top + ANO_SPACING);
                 translateX = -1;
                 break;
             case 'leftBottom':
                 // left = triggerRect.left - SPACING;
                 // top = pointAtCenter ? middleY + offsetYWithArrow : triggerRect.bottom;
                 left = isWidthOverFlow ? containerRect.right + offsetWidth - SPACING + offsetXWithArrow: triggerRect.left - SPACING;
-                top = isHeightOverFlow ? containerRect.bottom + offsetHeight: (pointAtCenter ? middleY + offsetYWithArrow : triggerRect.bottom);
+                top = isHeightOverFlow ? containerRect.bottom + offsetHeight: (pointAtCenter ? middleY + offsetYWithArrow + ANO_SPACING : triggerRect.bottom + ANO_SPACING);
                 translateX = -1;
                 translateY = -1;
                 break;
             case 'bottom':
                 // left = middleX;
                 // top = triggerRect.top + triggerRect.height + SPACING;
-                left = isWidthOverFlow ? (isTriggerNearLeft ? containerRect.left + wrapperRect.width / 2 : containerRect.right - wrapperRect.width / 2 + offsetWidth): middleX;
+                left = isWidthOverFlow ? (isTriggerNearLeft ? containerRect.left + wrapperRect.width / 2 : containerRect.right - wrapperRect.width / 2 + offsetWidth): middleX + ANO_SPACING;
                 top = isHeightOverFlow ? containerRect.top + offsetYWithArrow - SPACING: triggerRect.top + triggerRect.height + SPACING;
                 translateX = -0.5;
                 break;
             case 'bottomLeft':
                 // left = pointAtCenter ? middleX - offsetXWithArrow : triggerRect.left;
                 // top = triggerRect.bottom + SPACING;
-                left = isWidthOverFlow ? (isWrapperWidthOverflow ? containerRect.left : containerRect.right - wrapperRect.width ) : (pointAtCenter ? middleX - offsetXWithArrow : triggerRect.left);
+                left = isWidthOverFlow ? (isWrapperWidthOverflow ? containerRect.left : containerRect.right - wrapperRect.width ) : (pointAtCenter ? middleX - offsetXWithArrow + ANO_SPACING: triggerRect.left + ANO_SPACING);
                 top = isHeightOverFlow ? containerRect.top + offsetYWithArrow - SPACING : triggerRect.top + triggerRect.height + SPACING;
                 break;
             case 'bottomRight':
                 // left = pointAtCenter ? middleX + offsetXWithArrow : triggerRect.right;
                 // top = triggerRect.bottom + SPACING;
-                left = isWidthOverFlow ? containerRect.right + offsetWidth : (pointAtCenter ? middleX + offsetXWithArrow : triggerRect.right);
+                left = isWidthOverFlow ? containerRect.right + offsetWidth : (pointAtCenter ? middleX + offsetXWithArrow + ANO_SPACING : triggerRect.right + ANO_SPACING);
                 top = isHeightOverFlow ? containerRect.top + offsetYWithArrow - SPACING : triggerRect.top + triggerRect.height + SPACING;
                 translateX = -1;
                 break;
@@ -514,20 +523,20 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
                 // left = triggerRect.right + SPACING;
                 // top = middleY;
                 left = isWidthOverFlow ? containerRect.left - SPACING + offsetXWithArrow : triggerRect.right + SPACING;
-                top = isHeightOverFlow ? (isTriggerNearTop ? containerRect.top + wrapperRect.height / 2 : containerRect.bottom - wrapperRect.height / 2 + offsetHeight) : middleY;
+                top = isHeightOverFlow ? (isTriggerNearTop ? containerRect.top + wrapperRect.height / 2 : containerRect.bottom - wrapperRect.height / 2 + offsetHeight) : middleY + ANO_SPACING;
                 translateY = -0.5;
                 break;
             case 'rightTop':
                 // left = triggerRect.right + SPACING;
                 // top = pointAtCenter ? middleY - offsetYWithArrow : triggerRect.top;
                 left = isWidthOverFlow ? containerRect.left - SPACING + offsetXWithArrow : triggerRect.right + SPACING;
-                top = isHeightOverFlow ? containerRect.top : (pointAtCenter ? middleY - offsetYWithArrow : triggerRect.top);
+                top = isHeightOverFlow ? containerRect.top : (pointAtCenter ? middleY - offsetYWithArrow + ANO_SPACING : triggerRect.top + ANO_SPACING);
                 break;
             case 'rightBottom':
                 // left = triggerRect.right + SPACING;
                 // top = pointAtCenter ? middleY + offsetYWithArrow : triggerRect.bottom;
                 left = isWidthOverFlow ? containerRect.left - SPACING + offsetXWithArrow : triggerRect.right + SPACING;
-                top = isHeightOverFlow ? containerRect.bottom + offsetHeight : (pointAtCenter ? middleY + offsetYWithArrow : triggerRect.bottom);
+                top = isHeightOverFlow ? containerRect.bottom + offsetHeight : (pointAtCenter ? middleY + offsetYWithArrow + ANO_SPACING : triggerRect.bottom + ANO_SPACING);
                 translateY = -1;
                 break;
             case 'leftTopOver':
@@ -723,7 +732,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
     // place the dom correctly
     adjustPosIfNeed(position: Position | string, style: Record<string, any>, triggerRect: DOMRect, wrapperRect: DOMRect, containerRect: PopupContainerDOMRect) {
         const { innerWidth, innerHeight } = window;
-        const { spacing, margin } = this.getProps();
+        const { margin } = this.getProps();
 
         const marginLeft = typeof margin === 'number' ? margin : margin.marginLeft;
         const marginTop = typeof margin === 'number' ? margin : margin.marginTop;
@@ -733,6 +742,16 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
         let isHeightOverFlow = false;
         let isWidthOverFlow = false;
 
+        const raw_spacing = this.getProp('spacing');
+        let spacing = raw_spacing;
+        let ano_spacing = 0;
+        if (typeof raw_spacing !== 'number') {
+            // extended spacing api with {x: number, y: number}, the axes of the spacing is determined based on the position
+            const isTopOrBottom = position.includes('top') || position.includes('bottom');
+            spacing = isTopOrBottom ? raw_spacing.y : raw_spacing.x;
+            ano_spacing = isTopOrBottom ? raw_spacing.x : raw_spacing.y;
+        }
+
         if (wrapperRect.width > 0 && wrapperRect.height > 0) {
             // let clientLeft = left + translateX * wrapperRect.width - containerRect.scrollLeft;
             // let clientTop = top + translateY * wrapperRect.height - containerRect.scrollTop;
@@ -770,10 +789,10 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             const shouldViewReverseTopOver = restClientTop - marginBottom< wrapperRect.height + spacing && clientBottom - marginTop> wrapperRect.height + spacing;
             const shouldViewReverseBottomOver = clientBottom - marginTop < wrapperRect.height + spacing && restClientTop - marginBottom > wrapperRect.height + spacing;
 
-            const shouldViewReverseTopSide = restClientTop < wrapperRect.height && clientBottom > wrapperRect.height;
-            const shouldViewReverseBottomSide = clientBottom < wrapperRect.height && restClientTop > wrapperRect.height;
-            const shouldViewReverseLeftSide = restClientLeft < wrapperRect.width && clientRight > wrapperRect.width;
-            const shouldViewReverseRightSide = clientRight < wrapperRect.width && restClientLeft > wrapperRect.width;
+            const shouldViewReverseTopSide = restClientTop < wrapperRect.height + ano_spacing && clientBottom > wrapperRect.height + ano_spacing;
+            const shouldViewReverseBottomSide = clientBottom < wrapperRect.height + ano_spacing && restClientTop > wrapperRect.height + ano_spacing;
+            const shouldViewReverseLeftSide = restClientLeft < wrapperRect.width + ano_spacing && clientRight > wrapperRect.width + ano_spacing;
+            const shouldViewReverseRightSide = clientRight < wrapperRect.width + ano_spacing && restClientLeft > wrapperRect.width + ano_spacing;
 
             const shouldReverseTopOver = restClientTop < wrapperRect.height + spacing && clientBottom > wrapperRect.height + spacing;
             const shouldReverseBottomOver = clientBottom < wrapperRect.height + spacing && restClientTop > wrapperRect.height + spacing;
@@ -802,10 +821,10 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             const shouldContainerReverseTopOver = this.isReverse(restClientTopInContainer - marginBottom, clientBottomInContainer - marginTop, wrapperRect.height + spacing);
             const shouldContainerReverseBottomOver = this.isReverse(clientBottomInContainer - marginTop, restClientTopInContainer - marginBottom, wrapperRect.height + spacing);
 
-            const shouldContainerReverseTopSide = this.isReverse(restClientTopInContainer, clientBottomInContainer, wrapperRect.height);
-            const shouldContainerReverseBottomSide = this.isReverse(clientBottomInContainer, restClientTopInContainer, wrapperRect.height);
-            const shouldContainerReverseLeftSide = this.isReverse(restClientLeftInContainer, clientRightInContainer, wrapperRect.width);
-            const shouldContainerReverseRightSide = this.isReverse(clientRightInContainer, restClientLeftInContainer, wrapperRect.width);
+            const shouldContainerReverseTopSide = this.isReverse(restClientTopInContainer, clientBottomInContainer, wrapperRect.height + ano_spacing);
+            const shouldContainerReverseBottomSide = this.isReverse(clientBottomInContainer, restClientTopInContainer, wrapperRect.height + ano_spacing);
+            const shouldContainerReverseLeftSide = this.isReverse(restClientLeftInContainer, clientRightInContainer, wrapperRect.width + ano_spacing);
+            const shouldContainerReverseRightSide = this.isReverse(clientRightInContainer, restClientLeftInContainer, wrapperRect.width + ano_spacing);
 
             const halfHeight = triggerRect.height / 2;
             const halfWidth = triggerRect.width / 2;
@@ -816,10 +835,10 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             const isViewXOverFlow = this.isOverFlow(clientLeft - marginLeft, restClientRight - marginRight, wrapperRect.width + spacing);
             const isViewYOverFlowSide = this.isOverFlow(clientBottom - marginTop, restClientTop - marginBottom, wrapperRect.height + spacing);
             const isViewXOverFlowSide = this.isOverFlow(clientRight - marginLeft, restClientLeft - marginRight, wrapperRect.width + spacing);
-            const isViewYOverFlowSideHalf = this.isHalfOverFlow(clientBottom - halfHeight, restClientTop - halfHeight, wrapperRect.height / 2);
-            const isViewXOverFlowSideHalf = this.isHalfOverFlow(clientRight - halfWidth, restClientLeft - halfWidth, wrapperRect.width / 2);
-            const isViewYEnoughSideHalf = this.isHalfAllEnough(clientBottom - halfHeight, restClientTop - halfHeight, wrapperRect.height / 2);
-            const isViewXEnoughSideHalf = this.isHalfAllEnough(clientRight - halfWidth, restClientLeft - halfWidth, wrapperRect.width / 2);
+            const isViewYOverFlowSideHalf = this.isHalfOverFlow(clientBottom - halfHeight, restClientTop - halfHeight, (wrapperRect.height + ano_spacing) / 2);
+            const isViewXOverFlowSideHalf = this.isHalfOverFlow(clientRight - halfWidth, restClientLeft - halfWidth, (wrapperRect.width + ano_spacing) / 2);
+            const isViewYEnoughSideHalf = this.isHalfAllEnough(clientBottom - halfHeight, restClientTop - halfHeight, (wrapperRect.height + ano_spacing) / 2);
+            const isViewXEnoughSideHalf = this.isHalfAllEnough(clientRight - halfWidth, restClientLeft - halfWidth, (wrapperRect.width + ano_spacing) / 2);
 
             // 容器, 原空间与反向空间是否都不足判断
             // container, whether the original space and the reverse space are insufficient to judge
@@ -827,10 +846,10 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             const isContainerXOverFlow = this.isOverFlow(clientLeftInContainer - marginLeft, restClientRightInContainer - marginRight, wrapperRect.width + spacing);
             const isContainerYOverFlowSide = this.isOverFlow(clientBottomInContainer - marginTop, restClientTopInContainer - marginBottom, wrapperRect.height + spacing);
             const isContainerXOverFlowSide = this.isOverFlow(clientRightInContainer - marginLeft, restClientLeftInContainer - marginRight, wrapperRect.width + spacing);
-            const isContainerYOverFlowSideHalf = this.isHalfOverFlow(clientBottomInContainer - halfHeight, restClientTopInContainer - halfHeight, wrapperRect.height / 2);
-            const isContainerXOverFlowSideHalf = this.isHalfOverFlow(clientRightInContainer - halfWidth, restClientLeftInContainer - halfWidth, wrapperRect.width / 2);
-            const isContainerYEnoughSideHalf = this.isHalfAllEnough(clientBottomInContainer - halfHeight, restClientTopInContainer - halfHeight, wrapperRect.height / 2);
-            const isContainerXEnoughSideHalf = this.isHalfAllEnough(clientRightInContainer - halfWidth, restClientLeftInContainer - halfWidth, wrapperRect.width / 2);
+            const isContainerYOverFlowSideHalf = this.isHalfOverFlow(clientBottomInContainer - halfHeight, restClientTopInContainer - halfHeight, (wrapperRect.height + ano_spacing) / 2);
+            const isContainerXOverFlowSideHalf = this.isHalfOverFlow(clientRightInContainer - halfWidth, restClientLeftInContainer - halfWidth, (wrapperRect.width + ano_spacing) / 2);
+            const isContainerYEnoughSideHalf = this.isHalfAllEnough(clientBottomInContainer - halfHeight, restClientTopInContainer - halfHeight, (wrapperRect.height + ano_spacing) / 2);
+            const isContainerXEnoughSideHalf = this.isHalfAllEnough(clientRightInContainer - halfWidth, restClientLeftInContainer - halfWidth, (wrapperRect.width + ano_spacing) / 2);
 
             // 综合 viewport + container 判断微调,即视口 + 容器都放置不行时才能考虑位置调整
             // Comprehensive viewport + container judgment fine-tuning, that is, the position adjustment can only be considered when the viewport + container cannot be placed.

+ 1 - 1
packages/semi-ui/datePicker/datePicker.tsx

@@ -135,7 +135,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
         validateStatus: PropTypes.oneOf(strings.STATUS),
         renderDate: PropTypes.func,
         renderFullDate: PropTypes.func,
-        spacing: PropTypes.number,
+        spacing: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         startDateOffset: PropTypes.func,
         endDateOffset: PropTypes.func,
         autoSwitchDate: PropTypes.bool,

+ 1 - 1
packages/semi-ui/dropdown/index.tsx

@@ -91,7 +91,7 @@ class Dropdown extends BaseComponent<DropdownProps, DropdownState> {
         position: PropTypes.oneOf(positionSet),
         rePosKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
         render: PropTypes.node,
-        spacing: PropTypes.number,
+        spacing: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         showTick: PropTypes.bool,
         style: PropTypes.object,
         trigger: PropTypes.oneOf(triggerSet),

+ 4 - 3
packages/semi-ui/popover/index.tsx

@@ -10,6 +10,7 @@ import { BaseProps } from '../_base/baseComponent';
 import { isFunction, noop } from 'lodash';
 
 import type { ArrowProps } from './Arrow';
+import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
 export type { ArrowProps };
 declare interface ArrowStyle {
     borderColor?: string;
@@ -32,7 +33,7 @@ export interface PopoverProps extends BaseProps {
     onVisibleChange?: (visible: boolean) => void;
     onClickOutSide?: (e: React.MouseEvent) => void;
     showArrow?: boolean;
-    spacing?: number;
+    spacing?: number | { x: number; y: number };
     stopPropagation?: boolean | string;
     arrowStyle?: ArrowStyle;
     arrowBounding?: ArrowBounding;
@@ -75,7 +76,7 @@ class Popover extends React.PureComponent<PopoverProps, PopoverState> {
         onVisibleChange: PropTypes.func,
         onClickOutSide: PropTypes.func,
         style: PropTypes.object,
-        spacing: PropTypes.number,
+        spacing: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         zIndex: PropTypes.number,
         showArrow: PropTypes.bool,
         arrowStyle: PropTypes.shape({
@@ -170,7 +171,7 @@ class Popover extends React.PureComponent<PopoverProps, PopoverState> {
 
         const arrow = showArrow ? <Arrow {...arrowProps} /> : false;
 
-        if (typeof spacing !== 'number') {
+        if (isNullOrUndefined(spacing)) {
             spacing = showArrow ? numbers.SPACING_WITH_ARROW : numbers.SPACING;
         }
 

+ 5 - 5
packages/semi-ui/select/index.tsx

@@ -299,7 +299,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         autoAdjustOverflow: PropTypes.bool,
         mouseEnterDelay: PropTypes.number,
         mouseLeaveDelay: PropTypes.number,
-        spacing: PropTypes.number,
+        spacing: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         onBlur: PropTypes.func,
         onFocus: PropTypes.func,
         onClear: PropTypes.func,
@@ -917,7 +917,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 ref={this.setOptionContainerEl}
                 onKeyDown={e => this.foundation.handleContainerKeyDown(e)}
             >
-                {outerTopSlot ? <div className={`${prefixcls}-option-list-outer-top-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{outerTopSlot}</div> : null }
+                {outerTopSlot ? <div className={`${prefixcls}-option-list-outer-top-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{outerTopSlot}</div> : null}
                 <div
                     style={{ maxHeight: `${maxHeight}px` }}
                     className={optionListCls}
@@ -925,11 +925,11 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                     aria-multiselectable={multiple}
                     onScroll={e => this.foundation.handleListScroll(e)}
                 >
-                    {innerTopSlot ? <div className={`${prefixcls}-option-list-inner-top-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{innerTopSlot}</div> : null }
+                    {innerTopSlot ? <div className={`${prefixcls}-option-list-inner-top-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{innerTopSlot}</div> : null}
                     {loading ? this.renderLoading() : isEmpty ? this.renderEmpty() : listContent}
-                    {innerBottomSlot ? <div className={`${prefixcls}-option-list-inner-bottom-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{innerBottomSlot}</div> : null }
+                    {innerBottomSlot ? <div className={`${prefixcls}-option-list-inner-bottom-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{innerBottomSlot}</div> : null}
                 </div>
-                {outerBottomSlot ? <div className={`${prefixcls}-option-list-outer-bottom-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{outerBottomSlot}</div> : null }
+                {outerBottomSlot ? <div className={`${prefixcls}-option-list-outer-bottom-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{outerBottomSlot}</div> : null}
             </div>
         );
     }

+ 20 - 0
packages/semi-ui/tooltip/_story/tooltip.stories.jsx

@@ -1645,6 +1645,26 @@ export const wordBreak = () => {
   );
 }
 
+
+export const TooltipAllAddSpacing = () => (
+  <div className="demo">
+    <ScrollDemo spacing={{ x: 16, y: 16}} />
+    <div
+      style={{
+        padding: 120,
+      }}
+    >
+      <ScrollDemo
+        showArrow={false}
+        spacing={{ x: 16, y: 16}}
+      />
+    </div>
+  </div>
+);
+
+TooltipAllAddSpacing.story = {
+  name: 'tooltip All Add Spacing',
+};
 export const ViewportPrioritJudgment = () => {
   // If the viewport reverse space is sufficient, the viewport result shall prevail
   return (

+ 6 - 6
packages/semi-ui/tooltip/index.tsx

@@ -61,7 +61,7 @@ export interface TooltipProps extends BaseProps {
     prefixCls?: string;
     onVisibleChange?: (visible: boolean) => void;
     onClickOutSide?: (e: React.MouseEvent) => void;
-    spacing?: number;
+    spacing?: number | { x: number; y: number };
     margin?: number | { marginLeft: number; marginTop: number; marginRight: number; marginBottom: number };
     showArrow?: boolean | React.ReactNode;
     zIndex?: number;
@@ -133,7 +133,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
         prefixCls: PropTypes.string,
         onVisibleChange: PropTypes.func,
         onClickOutSide: PropTypes.func,
-        spacing: PropTypes.number,
+        spacing: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         margin: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
         showArrow: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
         zIndex: PropTypes.number,
@@ -537,8 +537,8 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
         const bgColor = get(style, 'backgroundColor');
 
         const iconComponent = placement.includes('left') || placement.includes('right') ?
-            <TriangleArrowVertical/> :
-            <TriangleArrow/>;
+            <TriangleArrowVertical /> :
+            <TriangleArrow />;
         if (showArrow) {
             if (isValidElement(showArrow)) {
                 icon = showArrow;
@@ -638,7 +638,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
                                 ...(displayNone ? { display: "none" } : {}),
                                 transformOrigin,
                                 ...style,
-                                ...(userOpacity ? { opacity: isPositionUpdated ? opacity : "0" }:{}) 
+                                ...(userOpacity ? { opacity: isPositionUpdated ? opacity : "0" } : {})
                             }}
                             {...portalEventSet}
                             {...animationEventsNeedBind}
@@ -719,7 +719,7 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
         const { isInsert, triggerEventSet, visible, id } = this.state;
         const { wrapWhenSpecial, role, trigger } = this.props;
         let { children } = this.props;
-        const childrenStyle = { ...get(children, 'props.style') as React.CSSProperties } ;
+        const childrenStyle = { ...get(children, 'props.style') as React.CSSProperties };
         const extraStyle: React.CSSProperties = {};
 
         if (wrapWhenSpecial) {