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
Carl Chen 7 months ago committed by GitHub
parent 966bc1004c
commit 85c48c0566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -209,7 +209,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke
// Offset the popover to account for the dropdown arrow // Offset the popover to account for the dropdown arrow
// >>>>> Top // >>>>> Top
[connectArrowCls( [connectArrowCls(
[`&-placement-topLeft`, `&-placement-top`, `&-placement-topRight`], [`&-placement-topLeft`, `&-placement-top`, `&-placement-topRight`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls, showArrowCls,
)]: { )]: {
paddingBottom: dropdownArrowDistance, paddingBottom: dropdownArrowDistance,
@ -217,7 +219,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke
// >>>>> Bottom // >>>>> Bottom
[connectArrowCls( [connectArrowCls(
[`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`], [`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls, showArrowCls,
)]: { )]: {
paddingTop: dropdownArrowDistance, paddingTop: dropdownArrowDistance,
@ -225,7 +229,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke
// >>>>> Left // >>>>> Left
[connectArrowCls( [connectArrowCls(
[`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`], [`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls, showArrowCls,
)]: { )]: {
paddingRight: { paddingRight: {
@ -236,7 +242,9 @@ export default function getArrowStyle<Token extends TokenWithCommonCls<AliasToke
// >>>>> Right // >>>>> Right
[connectArrowCls( [connectArrowCls(
[`&-placement-rightTop`, `&-placement-right`, `&-placement-rightBottom`], [`&-placement-rightTop`, `&-placement-right`, `&-placement-rightBottom`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls, showArrowCls,
)]: { )]: {
paddingLeft: { paddingLeft: {

@ -145,11 +145,16 @@ export default defineComponent({
}); });
const tooltipPlacements = computed(() => { 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 ( return (
builtinPlacements || builtinPlacements ||
getPlacements({ getPlacements({
arrowPointAtCenter, arrowPointAtCenter: mergedArrowPointAtCenter,
autoAdjustOverflow, autoAdjustOverflow,
}) })
); );
@ -283,6 +288,7 @@ export default defineComponent({
...attrs, ...attrs,
...(props as TooltipProps), ...(props as TooltipProps),
prefixCls: prefixCls.value, prefixCls: prefixCls.value,
arrow: !!props.arrow,
getPopupContainer: getPopupContainer?.value, getPopupContainer: getPopupContainer?.value,
builtinPlacements: tooltipPlacements.value, builtinPlacements: tooltipPlacements.value,
visible: tempVisible, visible: tempVisible,

@ -35,7 +35,12 @@ export default () => ({
mouseEnterDelay: Number, mouseEnterDelay: Number,
mouseLeaveDelay: Number, mouseLeaveDelay: Number,
getPopupContainer: Function as PropType<(triggerNode: HTMLElement) => HTMLElement>, getPopupContainer: Function as PropType<(triggerNode: HTMLElement) => HTMLElement>,
/**@deprecated Please use `arrow={{ pointAtCenter: true }}` instead. */
arrowPointAtCenter: { type: Boolean, default: undefined }, arrowPointAtCenter: { type: Boolean, default: undefined },
arrow: {
type: [Boolean, Object] as PropType<boolean | { pointAtCenter?: boolean }>,
default: true as boolean | { pointAtCenter?: boolean },
},
autoAdjustOverflow: { autoAdjustOverflow: {
type: [Boolean, Object] as PropType<boolean | AdjustOverflow>, type: [Boolean, Object] as PropType<boolean | AdjustOverflow>,
default: undefined as 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 /> <arrow-point-at-center />
<auto-adjust-overflow /> <auto-adjust-overflow />
<color /> <color />
<Arrow />
</demo-sort> </demo-sort>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -13,6 +14,7 @@ import Placement from './placement.vue';
import arrowPointAtCenter from './arrow-point-at-center.vue'; import arrowPointAtCenter from './arrow-point-at-center.vue';
import AutoAdjustOverflow from './auto-adjust-overflow.vue'; import AutoAdjustOverflow from './auto-adjust-overflow.vue';
import Color from './color.vue'; import Color from './color.vue';
import Arrow from './arrow.vue';
import CN from '../index.zh-CN.md'; import CN from '../index.zh-CN.md';
import US from '../index.en-US.md'; import US from '../index.en-US.md';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
@ -25,6 +27,7 @@ export default defineComponent({
arrowPointAtCenter, arrowPointAtCenter,
AutoAdjustOverflow, AutoAdjustOverflow,
Color, Color,
Arrow,
}, },
setup() { setup() {
return {}; 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 | - | | | 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` | | | 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` | | | autoAdjustOverflow | Whether to adjust popup placement automatically when popup is off screen | boolean | `true` | |
| color | The background color | string | - | | | color | The background color | string | - | |
| destroyTooltipOnHide | Whether to destroy tooltip on hide | boolean | false | | | 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 | 无 | | | align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | 无 | |
| arrowPointAtCenter | 箭头是否指向目标元素中心 | boolean | `false` | | | arrowPointAtCenter | 箭头是否指向目标元素中心 | boolean | `false` | |
| arrow | 修改箭头的显示状态以及修改箭头是否指向目标元素中心 | boolean \| { pointAtCenter: boolean} | `true` | |
| autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` | | | autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` | |
| color | 背景颜色 | string | 无 | | | color | 背景颜色 | string | 无 | |
| destroyTooltipOnHide | 隐藏后是否销毁 tooltip | boolean | false | | | destroyTooltipOnHide | 隐藏后是否销毁 tooltip | boolean | false | |

@ -5,6 +5,7 @@ import Content from './Content';
import { getPropsSlot } from '../../_util/props-util'; import { getPropsSlot } from '../../_util/props-util';
import type { CSSProperties, PropType } from 'vue'; import type { CSSProperties, PropType } from 'vue';
import { defineComponent, shallowRef, watchEffect } from 'vue'; import { defineComponent, shallowRef, watchEffect } from 'vue';
function noop() {} function noop() {}
export default defineComponent({ export default defineComponent({
compatConfig: { MODE: 3 }, compatConfig: { MODE: 3 },
@ -36,16 +37,20 @@ export default defineComponent({
popupVisible: { type: Boolean, default: undefined }, popupVisible: { type: Boolean, default: undefined },
onVisibleChange: Function, onVisibleChange: Function,
onPopupAlign: Function, onPopupAlign: Function,
arrow: { type: Boolean, default: true },
}, },
setup(props, { slots, attrs, expose }) { setup(props, { slots, attrs, expose }) {
const triggerDOM = shallowRef(); const triggerDOM = shallowRef();
const getPopupElement = () => { const getPopupElement = () => {
const { prefixCls, tipId, overlayInnerStyle } = props; const { prefixCls, tipId, overlayInnerStyle } = props;
return [ return [
<div class={`${prefixCls}-arrow`} key="arrow"> !!props.arrow ? (
{getPropsSlot(slots, props, 'arrowContent')} <div class={`${prefixCls}-arrow`} key="arrow">
</div>, {getPropsSlot(slots, props, 'arrowContent')}
</div>
) : null,
<Content <Content
key="content" key="content"
prefixCls={prefixCls} prefixCls={prefixCls}
@ -122,6 +127,7 @@ export default defineComponent({
onPopupVisibleChange: props.onVisibleChange || (noop as any), onPopupVisibleChange: props.onVisibleChange || (noop as any),
onPopupAlign: props.onPopupAlign || noop, onPopupAlign: props.onPopupAlign || noop,
ref: triggerDOM, ref: triggerDOM,
arrow: !!props.arrow,
popup: getPopupElement(), popup: getPopupElement(),
}; };
return <Trigger {...triggerProps} v-slots={{ default: slots.default }}></Trigger>; return <Trigger {...triggerProps} v-slots={{ default: slots.default }}></Trigger>;

@ -210,6 +210,7 @@ const Tour = defineComponent({
/> />
<Trigger <Trigger
{...restProps} {...restProps}
arrow={!!restProps.arrow}
builtinPlacements={ builtinPlacements={
!curStep.value.target !curStep.value.target
? undefined ? undefined

@ -171,7 +171,13 @@ export default defineComponent({
if (childNode.length > 1) { if (childNode.length > 1) {
childNode = <div class={`${prefixCls}-content`}>{childNode}</div>; 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 hasAnimate = visible.value || !props.visible;
const transitionProps = hasAnimate ? getTransitionProps(motion.value.name, motion.value) : {}; const transitionProps = hasAnimate ? getTransitionProps(motion.value.name, motion.value) : {};

@ -10,6 +10,8 @@ export const innerProps = {
destroyPopupOnHide: Boolean, destroyPopupOnHide: Boolean,
forceRender: Boolean, forceRender: Boolean,
arrow: { type: Boolean, default: true },
// Legacy Motion // Legacy Motion
animation: [String, Object], animation: [String, Object],
transitionName: String, transitionName: String,

@ -414,11 +414,13 @@ export default defineComponent({
stretch, stretch,
alignPoint, alignPoint,
mobile, mobile,
arrow,
forceRender, forceRender,
} = this.$props; } = this.$props;
const { sPopupVisible, point } = this.$data; const { sPopupVisible, point } = this.$data;
const popupProps = { const popupProps = {
prefixCls, prefixCls,
arrow,
destroyPopupOnHide, destroyPopupOnHide,
visible: sPopupVisible, visible: sPopupVisible,
point: alignPoint ? point : null, point: alignPoint ? point : null,

@ -111,6 +111,7 @@ export const triggerProps = () => ({
onPopupVisibleChange: Function as PropType<(open: boolean) => void>, onPopupVisibleChange: Function as PropType<(open: boolean) => void>,
afterPopupVisibleChange: PropTypes.func.def(noop), afterPopupVisibleChange: PropTypes.func.def(noop),
popup: PropTypes.any, popup: PropTypes.any,
arrow: PropTypes.bool.def(true),
popupStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties }, popupStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
prefixCls: PropTypes.string.def('rc-trigger-popup'), prefixCls: PropTypes.string.def('rc-trigger-popup'),
popupClassName: PropTypes.string.def(''), popupClassName: PropTypes.string.def(''),

Loading…
Cancel
Save