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`
This commit is contained in:
Carl Chen 2024-04-19 13:17:15 +08:00 committed by GitHub
parent 966bc1004c
commit 85c48c0566
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 195 additions and 10 deletions

View File

@ -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: {

View File

@ -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,

View File

@ -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,

View File

@ -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>

View File

@ -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 {};

View File

@ -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 | |

View File

@ -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 | |

View File

@ -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>;

View File

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

View File

@ -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) : {};

View File

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

View File

@ -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,

View File

@ -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(''),