refactor: tooltip
parent
337d958c67
commit
1d01df4b85
|
@ -10,7 +10,7 @@ import RightOutlined from '@ant-design/icons-vue/RightOutlined';
|
|||
import useConfigInject from '../config-provider/hooks/useConfigInject';
|
||||
import devWarning from '../vc-util/devWarning';
|
||||
import omit from '../_util/omit';
|
||||
import getPlacements from '../tooltip/placements';
|
||||
import getPlacements from '../_util/placements';
|
||||
|
||||
export type DropdownProps = Partial<ExtractPropTypes<ReturnType<typeof dropdownProps>>>;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import './pagination/style';
|
|||
// import './badge/style';
|
||||
import './tabs/style';
|
||||
import './input/style';
|
||||
import './tooltip/style';
|
||||
// import './tooltip/style';
|
||||
import './popover/style';
|
||||
import './popconfirm/style';
|
||||
// import './menu/style';
|
||||
|
|
|
@ -41,7 +41,7 @@ import type { ComponentToken as NotificationComponentToken } from '../../notific
|
|||
// import type { ComponentToken as TabsComponentToken } from '../../tabs/style';
|
||||
// import type { ComponentToken as TagComponentToken } from '../../tag/style';
|
||||
// import type { ComponentToken as TimelineComponentToken } from '../../timeline/style';
|
||||
// import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style';
|
||||
import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style';
|
||||
// import type { ComponentToken as TransferComponentToken } from '../../transfer/style';
|
||||
// import type { ComponentToken as TypographyComponentToken } from '../../typography/style';
|
||||
// import type { ComponentToken as UploadComponentToken } from '../../upload/style';
|
||||
|
@ -106,7 +106,7 @@ export interface ComponentTokenMap {
|
|||
Modal?: ModalComponentToken;
|
||||
Message?: MessageComponentToken;
|
||||
// Upload?: UploadComponentToken;
|
||||
// Tooltip?: TooltipComponentToken;
|
||||
Tooltip?: TooltipComponentToken;
|
||||
// Table?: TableComponentToken;
|
||||
// Space?: SpaceComponentToken;
|
||||
// Progress?: ProgressComponentToken;
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
import type { ExtractPropTypes } from 'vue';
|
||||
import { computed, watch, defineComponent, onMounted, ref } from 'vue';
|
||||
import type { CSSProperties, ExtractPropTypes } from 'vue';
|
||||
import { computed, watch, defineComponent, ref } from 'vue';
|
||||
import VcTooltip from '../vc-tooltip';
|
||||
import classNames from '../_util/classNames';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import warning from '../_util/warning';
|
||||
import { getStyle, filterEmpty, isValidElement, initDefaultProps } from '../_util/props-util';
|
||||
import {
|
||||
getStyle,
|
||||
filterEmpty,
|
||||
isValidElement,
|
||||
initDefaultProps,
|
||||
isFragment,
|
||||
} from '../_util/props-util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
export type { TriggerType, TooltipPlacement } from './abstractTooltipProps';
|
||||
import abstractTooltipProps from './abstractTooltipProps';
|
||||
import useConfigInject from '../config-provider/hooks/useConfigInject';
|
||||
import getPlacements from './placements';
|
||||
import getPlacements from '../_util/placements';
|
||||
import firstNotUndefined from '../_util/firstNotUndefined';
|
||||
import raf from '../_util/raf';
|
||||
import { parseColor } from './util';
|
||||
export type { AdjustOverflow, PlacementsConfig } from './placements';
|
||||
export type { AdjustOverflow, PlacementsConfig } from '../_util/placements';
|
||||
import useStyle from './style';
|
||||
|
||||
// https://github.com/react-component/tooltip
|
||||
// https://github.com/yiminghe/dom-align
|
||||
|
@ -26,10 +33,12 @@ export interface TooltipAlignConfig {
|
|||
useCssBottom?: boolean;
|
||||
useCssTransform?: boolean;
|
||||
}
|
||||
|
||||
const splitObject = (obj: any, keys: string[]) => {
|
||||
const picked = {};
|
||||
const omitted = { ...obj };
|
||||
const splitObject = <T extends CSSProperties>(
|
||||
obj: T,
|
||||
keys: (keyof T)[],
|
||||
): Record<'picked' | 'omitted', T> => {
|
||||
const picked: T = {} as T;
|
||||
const omitted: T = { ...obj };
|
||||
keys.forEach(key => {
|
||||
if (obj && key in obj) {
|
||||
picked[key] = obj[key];
|
||||
|
@ -74,29 +83,32 @@ export default defineComponent({
|
|||
slots: ['title'],
|
||||
// emits: ['update:visible', 'visibleChange'],
|
||||
setup(props, { slots, emit, attrs, expose }) {
|
||||
const { prefixCls, getPopupContainer, direction } = useConfigInject('tooltip', props);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
[
|
||||
['visible', 'open'],
|
||||
['onVisibleChange', 'onOpenChange'],
|
||||
].forEach(([deprecatedName, newName]) => {
|
||||
warning(
|
||||
props[deprecatedName] === undefined,
|
||||
'Tooltip',
|
||||
`\`${deprecatedName}\` is deprecated, please use \`${newName}\` instead.`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const visible = ref(firstNotUndefined([props.visible, props.defaultVisible]));
|
||||
const { prefixCls, getPopupContainer, direction } = useConfigInject('tooltip', props);
|
||||
const mergedOpen = computed(() => props.open ?? props.visible);
|
||||
const innerOpen = ref(firstNotUndefined([props.open, props.visible]));
|
||||
|
||||
const tooltip = ref();
|
||||
|
||||
onMounted(() => {
|
||||
warning(
|
||||
props.defaultVisible === undefined,
|
||||
'Tooltip',
|
||||
`'defaultVisible' is deprecated, please use 'v-model:visible'`,
|
||||
);
|
||||
});
|
||||
let rafId: any;
|
||||
watch(
|
||||
() => props.visible,
|
||||
val => {
|
||||
raf.cancel(rafId);
|
||||
rafId = raf(() => {
|
||||
visible.value = !!val;
|
||||
});
|
||||
},
|
||||
);
|
||||
watch(mergedOpen, val => {
|
||||
raf.cancel(rafId);
|
||||
rafId = raf(() => {
|
||||
innerOpen.value = !!val;
|
||||
});
|
||||
});
|
||||
const isNoTitle = () => {
|
||||
const title = props.title ?? slots.title;
|
||||
return !title && title !== 0;
|
||||
|
@ -104,12 +116,14 @@ export default defineComponent({
|
|||
|
||||
const handleVisibleChange = (val: boolean) => {
|
||||
const noTitle = isNoTitle();
|
||||
if (props.visible === undefined) {
|
||||
visible.value = noTitle ? false : val;
|
||||
if (mergedOpen.value === undefined) {
|
||||
innerOpen.value = noTitle ? false : val;
|
||||
}
|
||||
if (!noTitle) {
|
||||
emit('update:visible', val);
|
||||
emit('visibleChange', val);
|
||||
emit('update:open', val);
|
||||
emit('openChange', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,7 +131,7 @@ export default defineComponent({
|
|||
return tooltip.value.getPopupDomNode();
|
||||
};
|
||||
|
||||
expose({ getPopupDomNode, visible, forcePopupAlign: () => tooltip.value?.forcePopupAlign() });
|
||||
expose({ getPopupDomNode, open, forcePopupAlign: () => tooltip.value?.forcePopupAlign() });
|
||||
|
||||
const tooltipPlacements = computed(() => {
|
||||
const { builtinPlacements, arrowPointAtCenter, autoAdjustOverflow } = props;
|
||||
|
@ -139,7 +153,8 @@ export default defineComponent({
|
|||
((elementType.__ANT_BUTTON === true || elementType === 'button') &&
|
||||
isTrueProps(ele.props.disabled)) ||
|
||||
(elementType.__ANT_SWITCH === true &&
|
||||
(isTrueProps(ele.props.disabled) || isTrueProps(ele.props.loading)))
|
||||
(isTrueProps(ele.props.disabled) || isTrueProps(ele.props.loading))) ||
|
||||
(elementType.__ANT_RADIO === true && isTrueProps(ele.props.disabled))
|
||||
) {
|
||||
// Pick some layout related style properties up to span
|
||||
// Prevent layout bugs like https://github.com/ant-design/ant-design/issues/5254
|
||||
|
@ -153,14 +168,14 @@ export default defineComponent({
|
|||
'display',
|
||||
'zIndex',
|
||||
]);
|
||||
const spanStyle = {
|
||||
const spanStyle: CSSProperties = {
|
||||
display: 'inline-block', // default inline-block is important
|
||||
...picked,
|
||||
cursor: 'not-allowed',
|
||||
lineHeight: 1, // use the true height of child nodes
|
||||
width: ele.props && ele.props.block ? '100%' : null,
|
||||
width: ele.props && ele.props.block ? '100%' : undefined,
|
||||
};
|
||||
const buttonStyle = {
|
||||
const buttonStyle: CSSProperties = {
|
||||
...omitted,
|
||||
pointerEvents: 'none',
|
||||
};
|
||||
|
@ -190,46 +205,50 @@ export default defineComponent({
|
|||
// 当前返回的位置
|
||||
const placement = Object.keys(placements).find(
|
||||
key =>
|
||||
placements[key].points[0] === align.points[0] &&
|
||||
placements[key].points[1] === align.points[1],
|
||||
placements[key].points[0] === align.points?.[0] &&
|
||||
placements[key].points[1] === align.points?.[1],
|
||||
);
|
||||
if (!placement) {
|
||||
return;
|
||||
if (placement) {
|
||||
// 根据当前坐标设置动画点
|
||||
const rect = domNode.getBoundingClientRect();
|
||||
const transformOrigin = {
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
};
|
||||
if (placement.indexOf('top') >= 0 || placement.indexOf('Bottom') >= 0) {
|
||||
transformOrigin.top = `${rect.height - align.offset[1]}px`;
|
||||
} else if (placement.indexOf('Top') >= 0 || placement.indexOf('bottom') >= 0) {
|
||||
transformOrigin.top = `${-align.offset[1]}px`;
|
||||
}
|
||||
if (placement.indexOf('left') >= 0 || placement.indexOf('Right') >= 0) {
|
||||
transformOrigin.left = `${rect.width - align.offset[0]}px`;
|
||||
} else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) {
|
||||
transformOrigin.left = `${-align.offset[0]}px`;
|
||||
}
|
||||
domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`;
|
||||
}
|
||||
// 根据当前坐标设置动画点
|
||||
const rect = domNode.getBoundingClientRect();
|
||||
const transformOrigin = {
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
};
|
||||
if (placement.indexOf('top') >= 0 || placement.indexOf('Bottom') >= 0) {
|
||||
transformOrigin.top = `${rect.height - align.offset[1]}px`;
|
||||
} else if (placement.indexOf('Top') >= 0 || placement.indexOf('bottom') >= 0) {
|
||||
transformOrigin.top = `${-align.offset[1]}px`;
|
||||
}
|
||||
if (placement.indexOf('left') >= 0 || placement.indexOf('Right') >= 0) {
|
||||
transformOrigin.left = `${rect.width - align.offset[0]}px`;
|
||||
} else if (placement.indexOf('right') >= 0 || placement.indexOf('Left') >= 0) {
|
||||
transformOrigin.left = `${-align.offset[0]}px`;
|
||||
}
|
||||
domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`;
|
||||
};
|
||||
const colorInfo = computed(() => parseColor(prefixCls.value, props.color));
|
||||
const injectFromPopover = computed(() => (attrs as any)['data-popover-inject']);
|
||||
const [wrapSSR, hashId] = useStyle(
|
||||
prefixCls,
|
||||
computed(() => !injectFromPopover.value),
|
||||
);
|
||||
return () => {
|
||||
const { openClassName, overlayClassName } = props;
|
||||
let children = filterEmpty(slots.default?.()) ?? null;
|
||||
children = children.length === 1 ? children[0] : children;
|
||||
|
||||
let tempVisible = visible.value;
|
||||
let tempVisible = innerOpen.value;
|
||||
// Hide tooltip when there is no title
|
||||
if (props.visible === undefined && isNoTitle()) {
|
||||
if (mergedOpen.value === undefined && isNoTitle()) {
|
||||
tempVisible = false;
|
||||
}
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
const child = getDisabledCompatibleChildren(
|
||||
isValidElement(children) ? children : <span>{children}</span>,
|
||||
isValidElement(children) && !isFragment(children) ? children : <span>{children}</span>,
|
||||
);
|
||||
const childCls = classNames({
|
||||
[openClassName || `${prefixCls.value}-open`]: true,
|
||||
|
@ -242,6 +261,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
colorInfo.value.className,
|
||||
hashId.value,
|
||||
);
|
||||
const formattedOverlayInnerStyle = {
|
||||
...props.overlayInnerStyle,
|
||||
|
@ -261,7 +281,7 @@ export default defineComponent({
|
|||
onVisibleChange: handleVisibleChange,
|
||||
onPopupAlign,
|
||||
};
|
||||
return (
|
||||
return wrapSSR(
|
||||
<VcTooltip
|
||||
{...vcTooltipProps}
|
||||
v-slots={{
|
||||
|
@ -271,8 +291,8 @@ export default defineComponent({
|
|||
overlay: getOverlay,
|
||||
}}
|
||||
>
|
||||
{visible.value ? cloneElement(child, { class: childCls }) : child}
|
||||
</VcTooltip>
|
||||
{innerOpen.value ? cloneElement(child, { class: childCls }) : child}
|
||||
</VcTooltip>,
|
||||
);
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import type { CSSProperties, PropType } from 'vue';
|
||||
import type { AlignType, BuildInPlacements } from '../vc-trigger/interface';
|
||||
import type { AdjustOverflow } from './placements';
|
||||
import type { AdjustOverflow } from '../_util/placements';
|
||||
export type TriggerType = 'hover' | 'focus' | 'click' | 'contextmenu';
|
||||
import type { PresetColorType } from '../_util/colors';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import { objectType } from '../_util/type';
|
||||
export type TooltipPlacement =
|
||||
| 'top'
|
||||
| 'left'
|
||||
|
@ -20,16 +21,14 @@ export type TooltipPlacement =
|
|||
|
||||
export default () => ({
|
||||
trigger: [String, Array] as PropType<TriggerType | TriggerType[]>,
|
||||
open: { type: Boolean, default: undefined },
|
||||
/** @deprecated Please use `open` instead. */
|
||||
visible: { type: Boolean, default: undefined },
|
||||
defaultVisible: { type: Boolean, default: undefined },
|
||||
placement: String as PropType<TooltipPlacement>,
|
||||
color: String as PropType<LiteralUnion<PresetColorType>>,
|
||||
transitionName: String,
|
||||
overlayStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||
overlayInnerStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: undefined as CSSProperties,
|
||||
},
|
||||
overlayStyle: objectType<CSSProperties>(),
|
||||
overlayInnerStyle: objectType<CSSProperties>(),
|
||||
overlayClassName: String,
|
||||
openClassName: String,
|
||||
prefixCls: String,
|
||||
|
@ -42,15 +41,13 @@ export default () => ({
|
|||
default: undefined as boolean | AdjustOverflow,
|
||||
},
|
||||
destroyTooltipOnHide: { type: Boolean, default: undefined },
|
||||
align: {
|
||||
type: Object as PropType<AlignType>,
|
||||
default: undefined as AlignType,
|
||||
},
|
||||
builtinPlacements: {
|
||||
type: Object as PropType<BuildInPlacements>,
|
||||
default: undefined as BuildInPlacements,
|
||||
},
|
||||
align: objectType<AlignType>(),
|
||||
builtinPlacements: objectType<BuildInPlacements>(),
|
||||
children: Array,
|
||||
/** @deprecated Please use `onOpenChange` instead. */
|
||||
onVisibleChange: Function as PropType<(vis: boolean) => void>,
|
||||
/** @deprecated Please use `onUpdate:open` instead. */
|
||||
'onUpdate:visible': Function as PropType<(vis: boolean) => void>,
|
||||
onOpenChange: Function as PropType<(vis: boolean) => void>,
|
||||
'onUpdate:open': Function as PropType<(vis: boolean) => void>,
|
||||
});
|
||||
|
|
|
@ -62,7 +62,7 @@ export default defineComponent({
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
#components-a-tooltip-demo-color .ant-btn {
|
||||
:deep(#components-a-tooltip-demo-color) .ant-btn {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ export default defineComponent({
|
|||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
#components-a-tooltip-demo-placement .ant-btn {
|
||||
:deep(#components-a-tooltip-demo-placement) .ant-btn {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
|
|
|
@ -22,28 +22,27 @@ A simple text popup tip.
|
|||
|
||||
The following APIs are shared by Tooltip, Popconfirm, Popover.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| 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` |
|
||||
| autoAdjustOverflow | Whether to adjust popup placement automatically when popup is off screen | boolean | `true` |
|
||||
| color | The background color | string | - |
|
||||
| defaultVisible | Whether the floating tooltip card is visible by default | boolean | `false` |
|
||||
| destroyTooltipOnHide | Whether to destroy tooltip on hide | boolean | false |
|
||||
| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body`. | Function(triggerNode) | () => document.body |
|
||||
| mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 |
|
||||
| mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 |
|
||||
| overlayClassName | Class name of the tooltip card | string | - |
|
||||
| overlayStyle | Style of the tooltip card | object | - |
|
||||
| placement | The position of the tooltip relative to the target, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` |
|
||||
| trigger | Tooltip trigger mode | `hover` \| `focus` \| `click` \| `contextmenu` | `hover` |
|
||||
| visible(v-model) | Whether the floating tooltip card is visible or not | boolean | `false` |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 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` | |
|
||||
| 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 | |
|
||||
| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body`. | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 | |
|
||||
| mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 | |
|
||||
| overlayClassName | Class name of the tooltip card | string | - | |
|
||||
| overlayStyle | Style of the tooltip card | object | - | |
|
||||
| placement | The position of the tooltip relative to the target, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` | |
|
||||
| trigger | Tooltip trigger mode | `hover` \| `focus` \| `click` \| `contextmenu` | `hover` | |
|
||||
| open(v-model) | Whether the floating tooltip card is open or not, Use `visible` under 4.0.0 | boolean | `false` | 4.0.0 |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments | |
|
||||
| Events Name | Description | Arguments | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| visibleChange | Callback executed when visibility of the tooltip card is changed | (visible) => void | - |
|
||||
| openChange | Callback executed when visibility of the tooltip card is changed | (visible) => void | 4.0 |
|
||||
|
||||
## Note
|
||||
|
||||
|
|
|
@ -23,28 +23,27 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
|
|||
|
||||
以下 API 为 Tooltip、Popconfirm、Popover 共享的 API。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | 无 |
|
||||
| arrowPointAtCenter | 箭头是否指向目标元素中心 | boolean | `false` |
|
||||
| autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` |
|
||||
| color | 背景颜色 | string | 无 |
|
||||
| defaultVisible | 默认是否显隐 | boolean | false |
|
||||
| destroyTooltipOnHide | 隐藏后是否销毁 tooltip | boolean | false |
|
||||
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | Function(triggerNode) | () => document.body |
|
||||
| mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip,单位:秒 | number | 0.1 |
|
||||
| mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip,单位:秒 | number | 0.1 |
|
||||
| overlayClassName | 卡片类名 | string | 无 |
|
||||
| overlayStyle | 卡片样式 | object | 无 |
|
||||
| placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | top |
|
||||
| trigger | 触发行为,可选 `hover/focus/click/contextmenu` | string | hover |
|
||||
| visible(v-model) | 用于手动控制浮层显隐 | boolean | false |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| align | 该值将合并到 placement 的配置中,设置参考 [dom-align](https://github.com/yiminghe/dom-align) | Object | 无 | |
|
||||
| arrowPointAtCenter | 箭头是否指向目标元素中心 | boolean | `false` | |
|
||||
| autoAdjustOverflow | 气泡被遮挡时自动调整位置 | boolean | `true` | |
|
||||
| color | 背景颜色 | string | 无 | |
|
||||
| destroyTooltipOnHide | 隐藏后是否销毁 tooltip | boolean | false | |
|
||||
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip,单位:秒 | number | 0.1 | |
|
||||
| mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip,单位:秒 | number | 0.1 | |
|
||||
| overlayClassName | 卡片类名 | string | 无 | |
|
||||
| overlayStyle | 卡片样式 | object | 无 | |
|
||||
| placement | 气泡框位置,可选 `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | top | |
|
||||
| trigger | 触发行为,可选 `hover/focus/click/contextmenu` | string | hover | |
|
||||
| open(v-model) | 用于手动控制浮层显隐, 小于 4.0.0 使用 `visible` | boolean | false | 4.0 |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| ------------- | -------------- | ----------------- |
|
||||
| visibleChange | 显示隐藏的回调 | (visible) => void |
|
||||
| 事件名称 | 说明 | 回调参数 | 版本 |
|
||||
| ---------- | -------------- | ----------------- | ---- |
|
||||
| openChange | 显示隐藏的回调 | (visible) => void | 4.0 |
|
||||
|
||||
## 注意
|
||||
|
||||
|
|
|
@ -1,225 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@tooltip-prefix-cls: ~'@{ant-prefix}-tooltip';
|
||||
|
||||
@tooltip-arrow-shadow-width: 3px;
|
||||
|
||||
@tooltip-arrow-rotate-width: sqrt(@tooltip-arrow-width * @tooltip-arrow-width * 2) +
|
||||
@tooltip-arrow-shadow-width * 2;
|
||||
|
||||
@tooltip-arrow-offset-vertical: 5px; // 8 - 3px
|
||||
@tooltip-arrow-offset-horizontal: 13px; // 16 - 3px
|
||||
|
||||
// Base class
|
||||
.@{tooltip-prefix-cls} {
|
||||
.reset-component();
|
||||
|
||||
position: absolute;
|
||||
z-index: @zindex-tooltip;
|
||||
display: block;
|
||||
width: max-content;
|
||||
width: intrinsic;
|
||||
max-width: @tooltip-max-width;
|
||||
visibility: visible;
|
||||
|
||||
&-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-placement-top,
|
||||
&-placement-topLeft,
|
||||
&-placement-topRight {
|
||||
padding-bottom: @tooltip-distance;
|
||||
}
|
||||
|
||||
&-placement-right,
|
||||
&-placement-rightTop,
|
||||
&-placement-rightBottom {
|
||||
padding-left: @tooltip-distance;
|
||||
}
|
||||
|
||||
&-placement-bottom,
|
||||
&-placement-bottomLeft,
|
||||
&-placement-bottomRight {
|
||||
padding-top: @tooltip-distance;
|
||||
}
|
||||
|
||||
&-placement-left,
|
||||
&-placement-leftTop,
|
||||
&-placement-leftBottom {
|
||||
padding-right: @tooltip-distance;
|
||||
}
|
||||
|
||||
// Wrapper for the tooltip content
|
||||
&-inner {
|
||||
min-width: 30px;
|
||||
min-height: 32px;
|
||||
padding: 6px 8px;
|
||||
color: @tooltip-color;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
word-wrap: break-word;
|
||||
background-color: @tooltip-bg;
|
||||
border-radius: @tooltip-border-radius;
|
||||
box-shadow: @box-shadow-base;
|
||||
}
|
||||
|
||||
// Arrows
|
||||
&-arrow {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
display: block;
|
||||
width: @tooltip-arrow-rotate-width;
|
||||
height: @tooltip-arrow-rotate-width;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
|
||||
&-content {
|
||||
// Use linear gradient to mix box shadow of tooltip inner
|
||||
--antd-arrow-background-color: linear-gradient(
|
||||
to right bottom,
|
||||
fadeout(@tooltip-bg, 10%),
|
||||
@tooltip-bg
|
||||
);
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
width: @tooltip-arrow-width;
|
||||
height: @tooltip-arrow-width;
|
||||
margin: auto;
|
||||
background-color: transparent;
|
||||
content: '';
|
||||
pointer-events: auto;
|
||||
.roundedArrow(@tooltip-arrow-width, 5px);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-top &-arrow,
|
||||
&-placement-topLeft &-arrow,
|
||||
&-placement-topRight &-arrow {
|
||||
bottom: 0;
|
||||
transform: translateY(100%);
|
||||
|
||||
&-content {
|
||||
box-shadow: @tooltip-arrow-shadow-width @tooltip-arrow-shadow-width 7px fade(@black, 7%);
|
||||
transform: translateY((-@tooltip-arrow-rotate-width / 2)) rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-top &-arrow {
|
||||
left: 50%;
|
||||
transform: translateY(100%) translateX(-50%);
|
||||
}
|
||||
|
||||
&-placement-topLeft &-arrow {
|
||||
left: @tooltip-arrow-offset-horizontal;
|
||||
}
|
||||
|
||||
&-placement-topRight &-arrow {
|
||||
right: @tooltip-arrow-offset-horizontal;
|
||||
}
|
||||
|
||||
&-placement-right &-arrow,
|
||||
&-placement-rightTop &-arrow,
|
||||
&-placement-rightBottom &-arrow {
|
||||
left: 0;
|
||||
transform: translateX(-100%);
|
||||
|
||||
&-content {
|
||||
box-shadow: -@tooltip-arrow-shadow-width @tooltip-arrow-shadow-width 7px fade(@black, 7%);
|
||||
transform: translateX((@tooltip-arrow-rotate-width / 2)) rotate(135deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-right &-arrow {
|
||||
top: 50%;
|
||||
transform: translateX(-100%) translateY(-50%);
|
||||
}
|
||||
|
||||
&-placement-rightTop &-arrow {
|
||||
top: @tooltip-arrow-offset-vertical;
|
||||
}
|
||||
|
||||
&-placement-rightBottom &-arrow {
|
||||
bottom: @tooltip-arrow-offset-vertical;
|
||||
}
|
||||
|
||||
&-placement-left &-arrow,
|
||||
&-placement-leftTop &-arrow,
|
||||
&-placement-leftBottom &-arrow {
|
||||
right: 0;
|
||||
transform: translateX(100%);
|
||||
|
||||
&-content {
|
||||
box-shadow: @tooltip-arrow-shadow-width -@tooltip-arrow-shadow-width 7px fade(@black, 7%);
|
||||
transform: translateX((-@tooltip-arrow-rotate-width / 2)) rotate(315deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-left &-arrow {
|
||||
top: 50%;
|
||||
transform: translateX(100%) translateY(-50%);
|
||||
}
|
||||
|
||||
&-placement-leftTop &-arrow {
|
||||
top: @tooltip-arrow-offset-vertical;
|
||||
}
|
||||
|
||||
&-placement-leftBottom &-arrow {
|
||||
bottom: @tooltip-arrow-offset-vertical;
|
||||
}
|
||||
|
||||
&-placement-bottom &-arrow,
|
||||
&-placement-bottomLeft &-arrow,
|
||||
&-placement-bottomRight &-arrow {
|
||||
top: 0;
|
||||
transform: translateY(-100%);
|
||||
|
||||
&-content {
|
||||
box-shadow: -@tooltip-arrow-shadow-width -@tooltip-arrow-shadow-width 7px fade(@black, 7%);
|
||||
transform: translateY((@tooltip-arrow-rotate-width / 2)) rotate(225deg);
|
||||
}
|
||||
}
|
||||
|
||||
&-placement-bottom &-arrow {
|
||||
left: 50%;
|
||||
transform: translateY(-100%) translateX(-50%);
|
||||
}
|
||||
|
||||
&-placement-bottomLeft &-arrow {
|
||||
left: @tooltip-arrow-offset-horizontal;
|
||||
}
|
||||
|
||||
&-placement-bottomRight &-arrow {
|
||||
right: @tooltip-arrow-offset-horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
.generator-tooltip-preset-color(@i: length(@preset-colors)) when (@i > 0) {
|
||||
.generator-tooltip-preset-color(@i - 1);
|
||||
@color: extract(@preset-colors, @i);
|
||||
@lightColor: '@{color}-6';
|
||||
.@{tooltip-prefix-cls}-@{color} {
|
||||
.@{tooltip-prefix-cls}-inner {
|
||||
background-color: @@lightColor;
|
||||
}
|
||||
.@{tooltip-prefix-cls}-arrow {
|
||||
&-content::before {
|
||||
background: @@lightColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.generator-tooltip-preset-color();
|
||||
|
||||
@import './rtl';
|
|
@ -0,0 +1,157 @@
|
|||
import { initZoomMotion } from '../../_style/motion';
|
||||
import type { FullToken, GenerateStyle, UseComponentStyleResult } from '../../theme/internal';
|
||||
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
|
||||
import { genPresetColor, resetComponent } from '../../_style';
|
||||
import getArrowStyle, { MAX_VERTICAL_CONTENT_RADIUS } from '../../_style/placementArrow';
|
||||
import { Ref } from 'vue';
|
||||
|
||||
export interface ComponentToken {
|
||||
zIndexPopup: number;
|
||||
colorBgDefault: string;
|
||||
}
|
||||
|
||||
interface TooltipToken extends FullToken<'Tooltip'> {
|
||||
// default variables
|
||||
tooltipMaxWidth: number;
|
||||
tooltipColor: string;
|
||||
tooltipBg: string;
|
||||
tooltipBorderRadius: number;
|
||||
tooltipRadiusOuter: number;
|
||||
}
|
||||
|
||||
const genTooltipStyle: GenerateStyle<TooltipToken> = token => {
|
||||
const {
|
||||
componentCls, // ant-tooltip
|
||||
tooltipMaxWidth,
|
||||
tooltipColor,
|
||||
tooltipBg,
|
||||
tooltipBorderRadius,
|
||||
zIndexPopup,
|
||||
controlHeight,
|
||||
boxShadowSecondary,
|
||||
paddingSM,
|
||||
paddingXS,
|
||||
tooltipRadiusOuter,
|
||||
} = token;
|
||||
|
||||
return [
|
||||
{
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
position: 'absolute',
|
||||
zIndex: zIndexPopup,
|
||||
display: 'block',
|
||||
'&': [{ width: 'max-content' }, { width: 'intrinsic' }],
|
||||
maxWidth: tooltipMaxWidth,
|
||||
visibility: 'visible',
|
||||
'&-hidden': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
'--antd-arrow-background-color': tooltipBg,
|
||||
|
||||
// Wrapper for the tooltip content
|
||||
[`${componentCls}-inner`]: {
|
||||
minWidth: controlHeight,
|
||||
minHeight: controlHeight,
|
||||
padding: `${paddingSM / 2}px ${paddingXS}px`,
|
||||
color: tooltipColor,
|
||||
textAlign: 'start',
|
||||
textDecoration: 'none',
|
||||
wordWrap: 'break-word',
|
||||
backgroundColor: tooltipBg,
|
||||
borderRadius: tooltipBorderRadius,
|
||||
boxShadow: boxShadowSecondary,
|
||||
},
|
||||
|
||||
// Limit left and right placement radius
|
||||
[[
|
||||
`&-placement-left`,
|
||||
`&-placement-leftTop`,
|
||||
`&-placement-leftBottom`,
|
||||
`&-placement-right`,
|
||||
`&-placement-rightTop`,
|
||||
`&-placement-rightBottom`,
|
||||
].join(',')]: {
|
||||
[`${componentCls}-inner`]: {
|
||||
borderRadius: Math.min(tooltipBorderRadius, MAX_VERTICAL_CONTENT_RADIUS),
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-content`]: {
|
||||
position: 'relative',
|
||||
},
|
||||
|
||||
// generator for preset color
|
||||
...genPresetColor(token, (colorKey, { darkColor }) => ({
|
||||
[`&${componentCls}-${colorKey}`]: {
|
||||
[`${componentCls}-inner`]: {
|
||||
backgroundColor: darkColor,
|
||||
},
|
||||
[`${componentCls}-arrow`]: {
|
||||
'--antd-arrow-background-color': darkColor,
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
// RTL
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Arrow Style
|
||||
getArrowStyle<TooltipToken>(
|
||||
mergeToken<TooltipToken>(token, {
|
||||
borderRadiusOuter: tooltipRadiusOuter,
|
||||
}),
|
||||
{
|
||||
colorBg: 'var(--antd-arrow-background-color)',
|
||||
showArrowCls: '',
|
||||
contentRadius: tooltipBorderRadius,
|
||||
limitVerticalRadius: true,
|
||||
},
|
||||
),
|
||||
|
||||
// Pure Render
|
||||
{
|
||||
[`${componentCls}-pure`]: {
|
||||
position: 'relative',
|
||||
maxWidth: 'none',
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default (prefixCls: Ref<string>, injectStyle: Ref<boolean>): UseComponentStyleResult => {
|
||||
const useOriginHook = genComponentStyleHook(
|
||||
'Tooltip',
|
||||
token => {
|
||||
// Popover use Tooltip as internal component. We do not need to handle this.
|
||||
if (injectStyle.value === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { borderRadius, colorTextLightSolid, colorBgDefault, borderRadiusOuter } = token;
|
||||
|
||||
const TooltipToken = mergeToken<TooltipToken>(token, {
|
||||
// default variables
|
||||
tooltipMaxWidth: 250,
|
||||
tooltipColor: colorTextLightSolid,
|
||||
tooltipBorderRadius: borderRadius,
|
||||
tooltipBg: colorBgDefault,
|
||||
tooltipRadiusOuter: borderRadiusOuter > 4 ? 4 : borderRadiusOuter,
|
||||
});
|
||||
|
||||
return [genTooltipStyle(TooltipToken), initZoomMotion(token, 'zoom-big-fast')];
|
||||
},
|
||||
({ zIndexPopupBase, colorBgSpotlight }) => ({
|
||||
zIndexPopup: zIndexPopupBase + 70,
|
||||
colorBgDefault: colorBgSpotlight,
|
||||
}),
|
||||
);
|
||||
|
||||
return useOriginHook(prefixCls);
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
import '../../style/index.less';
|
||||
import './index.less';
|
|
@ -1,14 +0,0 @@
|
|||
@tooltip-prefix-cls: ~'@{ant-prefix}-tooltip';
|
||||
|
||||
// Base class
|
||||
.@{tooltip-prefix-cls} {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
// Wrapper for the tooltip content
|
||||
&-inner {
|
||||
.@{tooltip-prefix-cls}-rtl & {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue