Skip to content

feat[tooltip]: add arrow attribute #7459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 19, 2024
16 changes: 12 additions & 4 deletions components/style/placementArrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,23 +209,29 @@ 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,
},

// >>>>> Bottom
[connectArrowCls(
[`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`],
[`&-placement-bottomLeft`, `&-placement-bottom`, `&-placement-bottomRight`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls,
)]: {
paddingTop: dropdownArrowDistance,
},

// >>>>> Left
[connectArrowCls(
[`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`],
[`&-placement-leftTop`, `&-placement-left`, `&-placement-leftBottom`].map(
cls => (cls += ':not(&-arrow-hidden)'),
),
showArrowCls,
)]: {
paddingRight: {
Expand All @@ -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: {
Expand Down
10 changes: 8 additions & 2 deletions components/tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
);
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions components/tooltip/abstractTooltipProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
143 changes: 143 additions & 0 deletions components/tooltip/demo/arrow.vue
Original file line number Diff line number Diff line change
@@ -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>
3 changes: 3 additions & 0 deletions components/tooltip/demo/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<arrow-point-at-center />
<auto-adjust-overflow />
<color />
<Arrow />
</demo-sort>
</template>
<script lang="ts">
Expand All @@ -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';
Expand All @@ -25,6 +27,7 @@ export default defineComponent({
arrowPointAtCenter,
AutoAdjustOverflow,
Color,
Arrow,
},
setup() {
return {};
Expand Down
1 change: 1 addition & 0 deletions components/tooltip/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down
1 change: 1 addition & 0 deletions components/tooltip/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand Down
12 changes: 9 additions & 3 deletions components/vc-tooltip/src/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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>;
Expand Down
1 change: 1 addition & 0 deletions components/vc-tour/Tour.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ const Tour = defineComponent({
/>
<Trigger
{...restProps}
arrow={!!restProps.arrow}
builtinPlacements={
!curStep.value.target
? undefined
Expand Down
8 changes: 7 additions & 1 deletion components/vc-trigger/Popup/PopupInner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) : {};

Expand Down
2 changes: 2 additions & 0 deletions components/vc-trigger/Popup/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const innerProps = {
destroyPopupOnHide: Boolean,
forceRender: Boolean,

arrow: { type: Boolean, default: true },

// Legacy Motion
animation: [String, Object],
transitionName: String,
Expand Down
2 changes: 2 additions & 0 deletions components/vc-trigger/Trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions components/vc-trigger/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(''),
Expand Down
Loading