feat: update tooltip

pull/2386/head
tanjinzhou 2020-06-09 18:30:18 +08:00
parent 24ef11afb9
commit 69a53b303d
8 changed files with 150 additions and 183 deletions

View File

@ -1,14 +1,14 @@
import { cloneElement } from '../_util/vnode';
import { inject, cloneVNode, isVNode } from 'vue';
import VcTooltip from '../vc-tooltip';
import getPlacements from './placements';
import PropTypes from '../_util/vue-types';
import {
hasProp,
getComponentFromProp,
getComponent,
getClass,
getStyle,
isValidElement,
getListeners,
filterEmpty,
getSlot,
} from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
import abstractTooltipProps from './abstractTooltipProps';
@ -35,8 +35,10 @@ export default {
...props,
title: PropTypes.any,
},
inject: {
configProvider: { default: () => ConfigConsumerProps },
setup() {
return {
configProvider: inject('configProvider', ConfigConsumerProps),
};
},
data() {
return {
@ -107,14 +109,14 @@ export default {
display: 'inline-block', // default inline-block is important
...picked,
cursor: 'not-allowed',
width: ele.componentOptions.propsData.block ? '100%' : null,
width: ele.props && ele.props.block ? '100%' : null,
};
const buttonStyle = {
...omitted,
pointerEvents: 'none',
};
const spanCls = getClass(ele);
const child = cloneElement(ele, {
const child = cloneVNode(ele, {
style: buttonStyle,
class: null,
});
@ -128,12 +130,12 @@ export default {
},
isNoTitle() {
const title = getComponentFromProp(this, 'title');
const title = getComponent(this, 'title');
return !title && title !== 0;
},
getOverlay() {
const title = getComponentFromProp(this, 'title');
const title = getComponent(this, 'title');
if (title === 0) {
return title;
}
@ -173,12 +175,12 @@ export default {
},
render() {
const { $props, $data, $slots } = this;
const { $props, $data, $attrs } = this;
const { prefixCls: customizePrefixCls, openClassName, getPopupContainer } = $props;
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('tooltip', customizePrefixCls);
let children = ($slots.default || []).filter(c => c.tag || c.text.trim() !== '');
let children = filterEmpty(getSlot(this));
children = children.length === 1 ? children[0] : children;
let sVisible = $data.sVisible;
// Hide tooltip when there is no title
@ -189,30 +191,26 @@ export default {
return null;
}
const child = this.getDisabledCompatibleChildren(
isValidElement(children) ? children : <span>{children}</span>,
isVNode(children) ? children : <span>{children}</span>,
);
const childCls = {
[openClassName || `${prefixCls}-open`]: true,
};
const tooltipProps = {
props: {
...$props,
prefixCls,
getTooltipContainer: getPopupContainer || getContextPopupContainer,
builtinPlacements: this.getPlacements(),
overlay: this.getOverlay(),
visible: sVisible,
},
...$attrs,
...$props,
prefixCls,
getTooltipContainer: getPopupContainer || getContextPopupContainer,
builtinPlacements: this.getPlacements(),
overlay: this.getOverlay(),
visible: sVisible,
ref: 'tooltip',
on: {
...getListeners(this),
visibleChange: this.onVisibleChange,
popupAlign: this.onPopupAlign,
},
onVisibleChange: this.onVisibleChange,
onPopupAlign: this.onPopupAlign,
};
return (
<VcTooltip {...tooltipProps}>
{sVisible ? cloneElement(child, { class: childCls }) : child}
{sVisible ? cloneVNode(child, { class: childCls }) : child}
</VcTooltip>
);
},

View File

@ -1,10 +1,8 @@
import ToolTip from './Tooltip';
import Base from '../base';
/* istanbul ignore next */
ToolTip.install = function(Vue) {
Vue.use(Base);
Vue.component(ToolTip.name, ToolTip);
ToolTip.install = function(app) {
app.component(ToolTip.name, ToolTip);
};
export default ToolTip;

View File

@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types';
import Trigger from '../vc-trigger';
import { placements } from './placements';
import Content from './Content';
import { hasProp, getComponentFromProp, getOptionProps, getListeners } from '../_util/props-util';
import { hasProp, getComponent, getOptionProps } from '../_util/props-util';
function noop() {}
export default {
props: {
@ -31,14 +31,14 @@ export default {
const { prefixCls, tipId } = this.$props;
return [
<div class={`${prefixCls}-arrow`} key="arrow">
{getComponentFromProp(this, 'arrowContent')}
{getComponent(this, 'arrowContent')}
</div>,
<Content
key="content"
trigger={this.$refs.trigger}
prefixCls={prefixCls}
id={tipId}
overlay={getComponentFromProp(this, 'overlay')}
overlay={getComponent(this, 'overlay')}
/>,
];
},
@ -69,37 +69,33 @@ export default {
if (hasProp(this, 'visible')) {
extraProps.popupVisible = this.$props.visible;
}
const listeners = getListeners(this);
const { $attrs } = this;
const triggerProps = {
props: {
popupClassName: overlayClassName,
prefixCls,
action: trigger,
builtinPlacements: placements,
popupPlacement: placement,
popupAlign: align,
getPopupContainer: getTooltipContainer,
afterPopupVisibleChange: afterVisibleChange,
popupTransitionName: transitionName,
popupAnimation: animation,
defaultPopupVisible: defaultVisible,
destroyPopupOnHide: destroyTooltipOnHide,
mouseLeaveDelay,
popupStyle: overlayStyle,
mouseEnterDelay,
...extraProps,
},
on: {
...listeners,
popupVisibleChange: listeners.visibleChange || noop,
popupAlign: listeners.popupAlign || noop,
},
popupClassName: overlayClassName,
prefixCls,
action: trigger,
builtinPlacements: placements,
popupPlacement: placement,
popupAlign: align,
getPopupContainer: getTooltipContainer,
afterPopupVisibleChange: afterVisibleChange,
popupTransitionName: transitionName,
popupAnimation: animation,
defaultPopupVisible: defaultVisible,
destroyPopupOnHide: destroyTooltipOnHide,
mouseLeaveDelay,
popupStyle: overlayStyle,
mouseEnterDelay,
...extraProps,
...$attrs,
onPopupVisibleChange: $attrs.onVisibleChange || noop,
onPopupAlign: $attrs.onPopupAlign || noop,
ref: 'trigger',
};
return (
<Trigger {...triggerProps}>
<template slot="popup">{this.getPopupElement(h)}</template>
{this.$slots.default}
<template slot="popup">{this.getPopupElement()}</template>
{this.$slots.default && this.$slots.default()}
</Trigger>
);
},

View File

@ -7,16 +7,14 @@ export default {
},
render() {
const { hiddenClassName, visible } = this.$props;
let children = null;
if (hiddenClassName || !this.$slots.default || this.$slots.default.length > 1) {
const child = this.$slots.default && this.$slots.default();
if (hiddenClassName || (child && child.length > 1)) {
const cls = '';
if (!visible && hiddenClassName) {
// cls += ` ${hiddenClassName}`
}
children = <div class={cls}>{this.$slots.default}</div>;
} else {
children = this.$slots.default[0];
return <div class={cls}>{child}</div>;
}
return children;
return child;
},
};

View File

@ -1,3 +1,4 @@
import { Transition } from 'vue';
import PropTypes from '../_util/vue-types';
import Align from '../vc-align';
import PopupInner from './PopupInner';
@ -9,6 +10,7 @@ import { getListeners } from '../_util/props-util';
export default {
name: 'VCTriggerPopup',
mixins: [BaseMixin],
inheritAttrs: false,
props: {
visible: PropTypes.bool,
getClassNameFromAlign: PropTypes.func,
@ -255,7 +257,7 @@ export default {
}
if (destroyPopupOnHide) {
return (
<transition {...transitionProps}>
<Transition {...transitionProps}>
{visible ? (
<Align
target={this.getAlignTarget()}
@ -268,11 +270,11 @@ export default {
<PopupInner {...popupInnerProps}>{$slots.default}</PopupInner>
</Align>
) : null}
</transition>
</Transition>
);
}
return (
<transition {...transitionProps}>
<Transition {...transitionProps}>
<Align
v-show={visible}
target={this.getAlignTarget()}
@ -285,7 +287,7 @@ export default {
>
<PopupInner {...popupInnerProps}>{$slots.default}</PopupInner>
</Align>
</transition>
</Transition>
);
},
@ -314,9 +316,9 @@ export default {
);
if (maskTransition) {
maskElement = (
<transition appear name={maskTransition}>
<Transition appear name={maskTransition}>
{maskElement}
</transition>
</Transition>
);
}
}

View File

@ -1,6 +1,5 @@
import PropTypes from '../_util/vue-types';
import LazyRenderBox from './LazyRenderBox';
import { getListeners } from '../_util/props-util';
export default {
props: {
@ -10,14 +9,11 @@ export default {
},
render() {
const { prefixCls, visible, hiddenClassName } = this.$props;
const divProps = {
on: getListeners(this),
};
return (
<div {...divProps} class={!visible ? hiddenClassName : ''}>
<div class={!visible ? hiddenClassName : ''}>
<LazyRenderBox class={`${prefixCls}-content`} visible={visible}>
{this.$slots.default}
{this.$slots.default && this.$slots.default()}
</LazyRenderBox>
</div>
);

View File

@ -1,12 +1,13 @@
import Vue from 'vue';
import { cloneVNode, inject, provide } from 'vue';
import ref from 'vue-ref';
import PropTypes from '../_util/vue-types';
import contains from '../vc-util/Dom/contains';
import {
hasProp,
getComponentFromProp,
getDataEvents,
getEvents,
filterEmpty,
getSlot,
getListeners,
} from '../_util/props-util';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
@ -15,10 +16,7 @@ import warning from '../_util/warning';
import Popup from './Popup';
import { getAlignFromPlacement, getAlignPopupClassName, noop } from './utils';
import BaseMixin from '../_util/BaseMixin';
import { cloneElement } from '../_util/vnode';
import ContainerRender from '../_util/ContainerRender';
Vue.use(ref, { name: 'ant-ref' });
import Portal from '../_util/Portal';
function returnEmptyString() {
return '';
@ -40,7 +38,9 @@ const ALL_HANDLERS = [
export default {
name: 'Trigger',
directives: { 'ant-ref': ref },
mixins: [BaseMixin],
inheritAttrs: false,
props: {
action: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def([]),
showAction: PropTypes.any.def([]),
@ -76,16 +76,6 @@ export default {
stretch: PropTypes.string,
alignPoint: PropTypes.bool, // Maybe we can support user pass position in the future
},
provide() {
return {
vcTriggerContext: this,
};
},
inject: {
vcTriggerContext: { default: () => ({}) },
savePopupRef: { default: () => noop },
dialogContext: { default: () => null },
},
data() {
const props = this.$props;
let popupVisible;
@ -113,24 +103,26 @@ export default {
}
},
},
created() {
provide('vcTriggerContext', this);
},
setup() {
return {
vcTriggerContext: inject('configProvider', {}),
savePopupRef: inject('vcTriggerContext', noop),
dialogContext: inject('dialogContext', null),
};
},
deactivated() {
this.setPopupVisible(false);
},
mounted() {
this.$nextTick(() => {
this.renderComponent(null);
this.updatedCal();
});
},
updated() {
const triggerAfterPopupVisibleChange = () => {
if (this.sPopupVisible !== this.prevPopupVisible) {
this.afterPopupVisibleChange(this.sPopupVisible);
}
this.prevPopupVisible = this.sPopupVisible;
};
this.renderComponent(null, triggerAfterPopupVisibleChange);
this.$nextTick(() => {
this.updatedCal();
});
@ -425,14 +417,12 @@ export default {
align: getListeners(this).popupAlign || noop,
...mouseProps,
},
directives: [
{
name: 'ant-ref',
value: this.savePopup,
},
],
};
return <Popup {...popupProps}>{getComponentFromProp(self, 'popup')}</Popup>;
return (
<Popup v-ant-ref={this.savePopup} {...popupProps}>
{getComponentFromProp(self, 'popup')}
</Popup>
);
},
getContainer() {
@ -453,7 +443,7 @@ export default {
},
setPopupVisible(sPopupVisible, event) {
const { alignPoint, sPopupVisible: prevPopupVisible } = this;
const { alignPoint, sPopupVisible: prevPopupVisible, $attrs } = this;
this.clearDelayTimer();
if (prevPopupVisible !== sPopupVisible) {
if (!hasProp(this, 'popupVisible')) {
@ -462,8 +452,7 @@ export default {
prevPopupVisible,
});
}
const listeners = getListeners(this);
listeners.popupVisibleChange && listeners.popupVisibleChange(sPopupVisible);
$attrs.onPopupVisibleChange && $attrs.onPopupVisibleChange(sPopupVisible);
}
// Always record the point position since mouseEnterDelay will delay the show
if (alignPoint && event) {
@ -482,7 +471,11 @@ export default {
},
});
},
handlePortalUpdate() {
if (this.prevPopupVisible !== this.sPopupVisible) {
this.afterPopupVisibleChange(this.sPopupVisible);
}
},
delaySetPopupVisible(visible, delayS, event) {
const delay = delayS * 1000;
this.clearDelayTimer();
@ -587,77 +580,74 @@ export default {
},
},
render() {
const { sPopupVisible } = this;
const children = filterEmpty(this.$slots.default);
const { sPopupVisible, $attrs } = this;
const children = filterEmpty(getSlot(this));
const { forceRender, alignPoint } = this.$props;
if (children.length > 1) {
warning(false, 'Trigger $slots.default.length > 1, just support only one default', true);
}
const child = children[0];
this.childOriginEvents = getDataEvents(child);
this.childOriginEvents = getEvents(this);
const newChildProps = {
props: {},
nativeOn: {},
key: 'trigger',
class: $attrs.class,
};
if (this.isContextmenuToShow()) {
newChildProps.nativeOn.contextmenu = this.onContextmenu;
newChildProps.onContextmenu = this.onContextmenu;
} else {
newChildProps.nativeOn.contextmenu = this.createTwoChains('contextmenu');
newChildProps.onContextmenu = this.createTwoChains('contextmenu');
}
if (this.isClickToHide() || this.isClickToShow()) {
newChildProps.nativeOn.click = this.onClick;
newChildProps.nativeOn.mousedown = this.onMousedown;
newChildProps.nativeOn.touchstart = this.onTouchstart;
newChildProps.onClick = this.onClick;
newChildProps.onMousedown = this.onMousedown;
newChildProps.onTouchstart = this.onTouchstart;
} else {
newChildProps.nativeOn.click = this.createTwoChains('click');
newChildProps.nativeOn.mousedown = this.createTwoChains('mousedown');
newChildProps.nativeOn.touchstart = this.createTwoChains('onTouchstart');
newChildProps.onClick = this.createTwoChains('click');
newChildProps.onMousedown = this.createTwoChains('mousedown');
newChildProps.onTouchstart = this.createTwoChains('onTouchstart');
}
if (this.isMouseEnterToShow()) {
newChildProps.nativeOn.mouseenter = this.onMouseenter;
newChildProps.onMouseenter = this.onMouseenter;
if (alignPoint) {
newChildProps.nativeOn.mousemove = this.onMouseMove;
newChildProps.onMousemove = this.onMouseMove;
}
} else {
newChildProps.nativeOn.mouseenter = this.createTwoChains('mouseenter');
newChildProps.onMouseenter = this.createTwoChains('mouseenter');
}
if (this.isMouseLeaveToHide()) {
newChildProps.nativeOn.mouseleave = this.onMouseleave;
newChildProps.onMouseleave = this.onMouseleave;
} else {
newChildProps.nativeOn.mouseleave = this.createTwoChains('mouseleave');
newChildProps.onMouseleave = this.createTwoChains('mouseleave');
}
if (this.isFocusToShow() || this.isBlurToHide()) {
newChildProps.nativeOn.focus = this.onFocus;
newChildProps.nativeOn.blur = this.onBlur;
newChildProps.onFocus = this.onFocus;
newChildProps.onBlur = this.onBlur;
} else {
newChildProps.nativeOn.focus = this.createTwoChains('focus');
newChildProps.nativeOn.blur = e => {
newChildProps.onFocus = this.createTwoChains('focus');
newChildProps.onBlur = e => {
if (e && (!e.relatedTarget || !contains(e.target, e.relatedTarget))) {
this.createTwoChains('blur')(e);
}
};
}
this.trigger = cloneElement(child, newChildProps);
return (
<ContainerRender
parent={this}
visible={sPopupVisible}
autoMount={false}
forceRender={forceRender}
getComponent={this.getComponent}
getContainer={this.getContainer}
children={({ renderComponent }) => {
this.renderComponent = renderComponent;
return this.trigger;
}}
/>
);
const trigger = cloneVNode(child, newChildProps);
let portal;
// prevent unmounting after it's rendered
if (sPopupVisible || this._component || forceRender) {
portal = (
<Portal
key="portal"
children={this.getComponent()}
getContainer={this.getContainer}
didUpdate={this.handlePortalUpdate}
></Portal>
);
}
return [portal, trigger];
},
};

View File

@ -1,4 +1,4 @@
import Vue from 'vue';
import { cloneVNode, inject, provide } from 'vue';
import ref from 'vue-ref';
import PropTypes from '../_util/vue-types';
import contains from '../vc-util/Dom/contains';
@ -7,6 +7,7 @@ import {
getComponentFromProp,
getEvents,
filterEmpty,
getSlot,
getListeners,
} from '../_util/props-util';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../_util/requestAnimationTimeout';
@ -15,11 +16,8 @@ import warning from '../_util/warning';
import Popup from './Popup';
import { getAlignFromPlacement, getAlignPopupClassName, noop } from './utils';
import BaseMixin from '../_util/BaseMixin';
import { cloneElement } from '../_util/vnode';
import Portal from '../_util/Portal';
Vue.use(ref, { name: 'ant-ref' });
function returnEmptyString() {
return '';
}
@ -40,6 +38,7 @@ const ALL_HANDLERS = [
export default {
name: 'Trigger',
directives: { 'ant-ref': ref },
mixins: [BaseMixin],
props: {
action: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def([]),
@ -76,16 +75,6 @@ export default {
stretch: PropTypes.string,
alignPoint: PropTypes.bool, // Maybe we can support user pass position in the future
},
provide() {
return {
vcTriggerContext: this,
};
},
inject: {
vcTriggerContext: { default: () => ({}) },
savePopupRef: { default: () => noop },
dialogContext: { default: () => null },
},
data() {
const props = this.$props;
let popupVisible;
@ -113,6 +102,16 @@ export default {
}
},
},
created() {
provide('vcTriggerContext', this);
},
setup() {
return {
vcTriggerContext: inject('configProvider', {}),
savePopupRef: inject('vcTriggerContext', noop),
dialogContext: inject('dialogContext', null),
};
},
deactivated() {
this.setPopupVisible(false);
},
@ -417,14 +416,12 @@ export default {
align: getListeners(this).popupAlign || noop,
...mouseProps,
},
directives: [
{
name: 'ant-ref',
value: this.savePopup,
},
],
};
return <Popup {...popupProps}>{getComponentFromProp(self, 'popup')}</Popup>;
return (
<Popup v-ant-ref={this.savePopup} {...popupProps}>
{getComponentFromProp(self, 'popup')}
</Popup>
);
},
getContainer() {
@ -584,7 +581,7 @@ export default {
},
render() {
const { sPopupVisible } = this;
const children = filterEmpty(this.$slots.default);
const children = filterEmpty(getSlot(this));
const { forceRender, alignPoint } = this.$props;
if (children.length > 1) {
@ -639,7 +636,7 @@ export default {
};
}
const trigger = cloneElement(child, newChildProps);
const trigger = cloneVNode(child, newChildProps);
let portal;
// prevent unmounting after it's rendered
if (sPopupVisible || this._component || forceRender) {
@ -652,14 +649,6 @@ export default {
></Portal>
);
}
return portal
? cloneElement(trigger, {
children: [
...((trigger.componentOptions ? trigger.componentOptions.children : trigger.children) ||
[]),
portal,
],
})
: trigger;
return [portal, trigger];
},
};