feat[tooltip]: add `arrow` attribute (#7459)
* docs: updating the `dropdownRender` description and jumps in the FAQ for Select * wip: add popover-arrow * wip: trigger add arrow attr * fix: remove popupContextKey * optimize * perf: optimize * docs: optimize docs * docs: add `arrow` attribute in tooltip en-US docs * fix: fix bug * perf[demo]: `radio-group` replace with `segmented`pull/7514/head
							parent
							
								
									966bc1004c
								
							
						
					
					
						commit
						85c48c0566
					
				|  | @ -209,7 +209,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke | |||
|       // Offset the popover to account for the dropdown arrow
 | ||||
|       // >>>>> Top
 | ||||
|       [connectArrowCls( | ||||
|         [`&-placement-topLeft`, `&-placement-top`, `&-placement-topRight`], | ||||
|         [`&-placement-topLeft`, `&-placement-top`, `&-placement-topRight`].map( | ||||
|           cls => (cls += ':not(&-arrow-hidden)'), | ||||
|         ), | ||||
|         showArrowCls, | ||||
|       )]: { | ||||
|         paddingBottom: dropdownArrowDistance, | ||||
|  | @ -217,7 +219,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke | |||
| 
 | ||||
|       // >>>>> Bottom
 | ||||
|       [connectArrowCls( | ||||
|         [`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`], | ||||
|         [`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`].map( | ||||
|           cls => (cls += ':not(&-arrow-hidden)'), | ||||
|         ), | ||||
|         showArrowCls, | ||||
|       )]: { | ||||
|         paddingTop: dropdownArrowDistance, | ||||
|  | @ -225,7 +229,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke | |||
| 
 | ||||
|       // >>>>> Left
 | ||||
|       [connectArrowCls( | ||||
|         [`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`], | ||||
|         [`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`].map( | ||||
|           cls => (cls += ':not(&-arrow-hidden)'), | ||||
|         ), | ||||
|         showArrowCls, | ||||
|       )]: { | ||||
|         paddingRight: { | ||||
|  | @ -236,7 +242,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke | |||
| 
 | ||||
|       // >>>>> Right
 | ||||
|       [connectArrowCls( | ||||
|         [`&-placement-rightTop`, `&-placement-right`, `&-placement-rightBottom`], | ||||
|         [`&-placement-rightTop`, `&-placement-right`, `&-placement-rightBottom`].map( | ||||
|           cls => (cls += ':not(&-arrow-hidden)'), | ||||
|         ), | ||||
|         showArrowCls, | ||||
|       )]: { | ||||
|         paddingLeft: { | ||||
|  |  | |||
|  | @ -145,11 +145,16 @@ export default defineComponent({ | |||
|     }); | ||||
| 
 | ||||
|     const tooltipPlacements = computed(() => { | ||||
|       const { builtinPlacements, arrowPointAtCenter, autoAdjustOverflow } = props; | ||||
|       const { builtinPlacements, autoAdjustOverflow, arrow, arrowPointAtCenter } = props; | ||||
|       let mergedArrowPointAtCenter = arrowPointAtCenter; | ||||
| 
 | ||||
|       if (typeof arrow === 'object') { | ||||
|         mergedArrowPointAtCenter = arrow.pointAtCenter ?? arrowPointAtCenter; | ||||
|       } | ||||
|       return ( | ||||
|         builtinPlacements || | ||||
|         getPlacements({ | ||||
|           arrowPointAtCenter, | ||||
|           arrowPointAtCenter: mergedArrowPointAtCenter, | ||||
|           autoAdjustOverflow, | ||||
|         }) | ||||
|       ); | ||||
|  | @ -283,6 +288,7 @@ export default defineComponent({ | |||
|         ...attrs, | ||||
|         ...(props as TooltipProps), | ||||
|         prefixCls: prefixCls.value, | ||||
|         arrow: !!props.arrow, | ||||
|         getPopupContainer: getPopupContainer?.value, | ||||
|         builtinPlacements: tooltipPlacements.value, | ||||
|         visible: tempVisible, | ||||
|  |  | |||
|  | @ -35,7 +35,12 @@ export default () => ({ | |||
|   mouseEnterDelay: Number, | ||||
|   mouseLeaveDelay: Number, | ||||
|   getPopupContainer: Function as PropType<(triggerNode: HTMLElement) => HTMLElement>, | ||||
|   /**@deprecated Please use `arrow={{ pointAtCenter: true }}` instead. */ | ||||
|   arrowPointAtCenter: { type: Boolean, default: undefined }, | ||||
|   arrow: { | ||||
|     type: [Boolean, Object] as PropType<boolean | { pointAtCenter?: boolean }>, | ||||
|     default: true as boolean | { pointAtCenter?: boolean }, | ||||
|   }, | ||||
|   autoAdjustOverflow: { | ||||
|     type: [Boolean, Object] as PropType<boolean | AdjustOverflow>, | ||||
|     default: undefined as boolean | AdjustOverflow, | ||||
|  |  | |||
|  | @ -0,0 +1,143 @@ | |||
| <docs> | ||||
| --- | ||||
| order: 6 | ||||
| title: | ||||
|   zh-CN: 箭头展示 | ||||
|   en-US: Arrow show | ||||
| --- | ||||
| 
 | ||||
| ## zh-CN | ||||
| 支持显示、隐藏以及将箭头保持居中定位。 | ||||
| 
 | ||||
| ## en-US | ||||
| 
 | ||||
| Support show, hide or keep arrow in the center. | ||||
| </docs> | ||||
| 
 | ||||
| <template> | ||||
|   <div id="components-a-tooltip-demo-arrow"> | ||||
|     <div style="margin-bottom: 24px"> | ||||
|       <a-segmented v-model:value="arrow" :options="options" /> | ||||
|     </div> | ||||
|     <div :style="{ marginLeft: `${buttonWidth}px`, whiteSpace: 'nowrap' }"> | ||||
|       <a-tooltip placement="topLeft" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>TL</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="top" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>Top</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="topRight" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>TR</a-button> | ||||
|       </a-tooltip> | ||||
|     </div> | ||||
|     <div :style="{ width: `${buttonWidth}px`, float: 'left' }"> | ||||
|       <a-tooltip placement="leftTop" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>LT</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="left" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>Left</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="leftBottom" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>LB</a-button> | ||||
|       </a-tooltip> | ||||
|     </div> | ||||
|     <div :style="{ width: `${buttonWidth}px`, marginLeft: `${buttonWidth * 4 + 24}px` }"> | ||||
|       <a-tooltip placement="rightTop" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>RT</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="right" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>Right</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="rightBottom" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>RB</a-button> | ||||
|       </a-tooltip> | ||||
|     </div> | ||||
|     <div :style="{ marginLeft: `${buttonWidth}px`, clear: 'both', whiteSpace: 'nowrap' }"> | ||||
|       <a-tooltip placement="bottomLeft" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>BL</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="bottom" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>Bottom</a-button> | ||||
|       </a-tooltip> | ||||
|       <a-tooltip placement="bottomRight" :arrow="mergedArrow"> | ||||
|         <template #title> | ||||
|           <span>prompt text</span> | ||||
|         </template> | ||||
|         <a-button>BR</a-button> | ||||
|       </a-tooltip> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { ref, computed } from 'vue'; | ||||
| const buttonWidth = 70; | ||||
| 
 | ||||
| const arrow = ref<string>('show'); | ||||
| 
 | ||||
| const options = [ | ||||
|   { | ||||
|     label: 'Show', | ||||
|     value: 'show', | ||||
|   }, | ||||
|   { | ||||
|     label: 'Hide', | ||||
|     value: 'hide', | ||||
|   }, | ||||
|   { | ||||
|     label: 'Center', | ||||
|     value: 'center', | ||||
|   }, | ||||
| ]; | ||||
| const mergedArrow = computed(() => { | ||||
|   switch (arrow.value) { | ||||
|     case 'show': | ||||
|       return true; | ||||
|     case 'hide': | ||||
|       return false; | ||||
|     case 'center': | ||||
|       return { pointAtCenter: true }; | ||||
|   } | ||||
| }); | ||||
| </script> | ||||
| <style scoped> | ||||
| :deep(#components-a-tooltip-demo-arrow) .ant-btn { | ||||
|   width: 70px; | ||||
|   text-align: center; | ||||
|   padding: 0; | ||||
|   margin-right: 8px; | ||||
|   margin-bottom: 8px; | ||||
| } | ||||
| </style> | ||||
|  | @ -5,6 +5,7 @@ | |||
|     <arrow-point-at-center /> | ||||
|     <auto-adjust-overflow /> | ||||
|     <color /> | ||||
|     <Arrow /> | ||||
|   </demo-sort> | ||||
| </template> | ||||
| <script lang="ts"> | ||||
|  | @ -13,6 +14,7 @@ import Placement from './placement.vue'; | |||
| import arrowPointAtCenter from './arrow-point-at-center.vue'; | ||||
| import AutoAdjustOverflow from './auto-adjust-overflow.vue'; | ||||
| import Color from './color.vue'; | ||||
| import Arrow from './arrow.vue'; | ||||
| import CN from '../index.zh-CN.md'; | ||||
| import US from '../index.en-US.md'; | ||||
| import { defineComponent } from 'vue'; | ||||
|  | @ -25,6 +27,7 @@ export default defineComponent({ | |||
|     arrowPointAtCenter, | ||||
|     AutoAdjustOverflow, | ||||
|     Color, | ||||
|     Arrow, | ||||
|   }, | ||||
|   setup() { | ||||
|     return {}; | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover. | |||
| | --- | --- | --- | --- | --- | | ||||
| | align | this value will be merged into placement's config, please refer to the settings [dom-align](https://github.com/yiminghe/dom-align) | Object | - |  | | ||||
| | arrowPointAtCenter | Whether the arrow is pointed at the center of target | boolean | `false` |  | | ||||
| | arrow | Change arrow's visible state and change whether the arrow is pointed at the center of target. | boolean \| { pointAtCenter: boolean} | `true` |  | | ||||
| | autoAdjustOverflow | Whether to adjust popup placement automatically when popup is off screen | boolean | `true` |  | | ||||
| | color | The background color | string | - |  | | ||||
| | destroyTooltipOnHide | Whether to destroy tooltip on hide | boolean | false |  | | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*gwrhTozoTC4AAA | |||
| | --- | --- | --- | --- | --- | | ||||
| | align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | 无 |  | | ||||
| | arrowPointAtCenter | 箭头是否指向目标元素中心 | boolean | `false` |  | | ||||
| | arrow | 修改箭头的显示状态以及修改箭头是否指向目标元素中心 | boolean \| { pointAtCenter: boolean} | `true` |  | | ||||
| | autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` |  | | ||||
| | color | 背景颜色 | string | 无 |  | | ||||
| | destroyTooltipOnHide | 隐藏后是否销毁 tooltip | boolean | false |  | | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import Content from './Content'; | |||
| import { getPropsSlot } from '../../_util/props-util'; | ||||
| import type { CSSProperties, PropType } from 'vue'; | ||||
| import { defineComponent, shallowRef, watchEffect } from 'vue'; | ||||
| 
 | ||||
| function noop() {} | ||||
| export default defineComponent({ | ||||
|   compatConfig: { MODE: 3 }, | ||||
|  | @ -36,16 +37,20 @@ export default defineComponent({ | |||
|     popupVisible: { type: Boolean, default: undefined }, | ||||
|     onVisibleChange: Function, | ||||
|     onPopupAlign: Function, | ||||
|     arrow: { type: Boolean, default: true }, | ||||
|   }, | ||||
|   setup(props, { slots, attrs, expose }) { | ||||
|     const triggerDOM = shallowRef(); | ||||
| 
 | ||||
|     const getPopupElement = () => { | ||||
|       const { prefixCls, tipId, overlayInnerStyle } = props; | ||||
| 
 | ||||
|       return [ | ||||
|         <div class={`${prefixCls}-arrow`} key="arrow"> | ||||
|           {getPropsSlot(slots, props, 'arrowContent')} | ||||
|         </div>, | ||||
|         !!props.arrow ? ( | ||||
|           <div class={`${prefixCls}-arrow`} key="arrow"> | ||||
|             {getPropsSlot(slots, props, 'arrowContent')} | ||||
|           </div> | ||||
|         ) : null, | ||||
|         <Content | ||||
|           key="content" | ||||
|           prefixCls={prefixCls} | ||||
|  | @ -122,6 +127,7 @@ export default defineComponent({ | |||
|         onPopupVisibleChange: props.onVisibleChange || (noop as any), | ||||
|         onPopupAlign: props.onPopupAlign || noop, | ||||
|         ref: triggerDOM, | ||||
|         arrow: !!props.arrow, | ||||
|         popup: getPopupElement(), | ||||
|       }; | ||||
|       return <Trigger {...triggerProps} v-slots={{ default: slots.default }}></Trigger>; | ||||
|  |  | |||
|  | @ -210,6 +210,7 @@ const Tour = defineComponent({ | |||
|           /> | ||||
|           <Trigger | ||||
|             {...restProps} | ||||
|             arrow={!!restProps.arrow} | ||||
|             builtinPlacements={ | ||||
|               !curStep.value.target | ||||
|                 ? undefined | ||||
|  |  | |||
|  | @ -171,7 +171,13 @@ export default defineComponent({ | |||
|       if (childNode.length > 1) { | ||||
|         childNode = <div class={`${prefixCls}-content`}>{childNode}</div>; | ||||
|       } | ||||
|       const mergedClassName = classNames(prefixCls, attrs.class, alignedClassName.value); | ||||
| 
 | ||||
|       const mergedClassName = classNames( | ||||
|         prefixCls, | ||||
|         attrs.class, | ||||
|         alignedClassName.value, | ||||
|         !props.arrow && `${prefixCls}-arrow-hidden`, | ||||
|       ); | ||||
|       const hasAnimate = visible.value || !props.visible; | ||||
|       const transitionProps = hasAnimate ? getTransitionProps(motion.value.name, motion.value) : {}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ export const innerProps = { | |||
|   destroyPopupOnHide: Boolean, | ||||
|   forceRender: Boolean, | ||||
| 
 | ||||
|   arrow: { type: Boolean, default: true }, | ||||
| 
 | ||||
|   // Legacy Motion
 | ||||
|   animation: [String, Object], | ||||
|   transitionName: String, | ||||
|  |  | |||
|  | @ -414,11 +414,13 @@ export default defineComponent({ | |||
|         stretch, | ||||
|         alignPoint, | ||||
|         mobile, | ||||
|         arrow, | ||||
|         forceRender, | ||||
|       } = this.$props; | ||||
|       const { sPopupVisible, point } = this.$data; | ||||
|       const popupProps = { | ||||
|         prefixCls, | ||||
|         arrow, | ||||
|         destroyPopupOnHide, | ||||
|         visible: sPopupVisible, | ||||
|         point: alignPoint ? point : null, | ||||
|  |  | |||
|  | @ -111,6 +111,7 @@ export const triggerProps = () => ({ | |||
|   onPopupVisibleChange: Function as PropType<(open: boolean) => void>, | ||||
|   afterPopupVisibleChange: PropTypes.func.def(noop), | ||||
|   popup: PropTypes.any, | ||||
|   arrow: PropTypes.bool.def(true), | ||||
|   popupStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties }, | ||||
|   prefixCls: PropTypes.string.def('rc-trigger-popup'), | ||||
|   popupClassName: PropTypes.string.def(''), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Carl Chen
						Carl Chen