vxe-pc-ui
Version:
A vue based PC component library
448 lines (447 loc) • 12.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _vue = require("vue");
var _xeUtils = _interopRequireDefault(require("xe-utils"));
var _ui = require("../../ui");
var _utils = require("../../ui/src/utils");
var _dom = require("../../ui/src/dom");
var _vn = require("../../ui/src/vn");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = exports.default = (0, _vue.defineComponent)({
name: 'VxeTooltip',
props: {
modelValue: Boolean,
size: {
type: String,
default: () => (0, _ui.getConfig)().tooltip.size || (0, _ui.getConfig)().size
},
selector: String,
trigger: {
type: String,
default: () => (0, _ui.getConfig)().tooltip.trigger || 'hover'
},
theme: {
type: String,
default: () => (0, _ui.getConfig)().tooltip.theme || 'dark'
},
content: {
type: [String, Number],
default: null
},
useHTML: Boolean,
zIndex: [String, Number],
popupClassName: [String, Function],
isArrow: {
type: Boolean,
default: () => (0, _ui.getConfig)().tooltip.isArrow
},
enterable: {
type: Boolean,
default: () => (0, _ui.getConfig)().tooltip.enterable
},
enterDelay: {
type: Number,
default: () => (0, _ui.getConfig)().tooltip.enterDelay
},
leaveDelay: {
type: Number,
default: () => (0, _ui.getConfig)().tooltip.leaveDelay
}
},
emits: ['update:modelValue'],
setup(props, context) {
const {
slots,
emit
} = context;
const xID = _xeUtils.default.uniqueId();
const {
computeSize
} = (0, _ui.useSize)(props);
const reactData = (0, _vue.reactive)({
target: null,
isUpdate: false,
visible: false,
tipContent: '',
tipActive: false,
tipTarget: null,
tipZindex: 0,
tipStore: {
style: {},
placement: '',
arrowStyle: {}
}
});
const internalData = {};
const refElem = (0, _vue.ref)();
const refMaps = {
refElem
};
const $xeTooltip = {
xID,
props,
context,
reactData,
internalData,
getRefMaps: () => refMaps
};
let tooltipMethods = {};
const updateTipStyle = () => {
const {
tipTarget,
tipStore
} = reactData;
if (tipTarget) {
const {
scrollTop,
scrollLeft,
visibleWidth
} = (0, _dom.getDomNode)();
const {
top,
left
} = (0, _dom.getAbsolutePos)(tipTarget);
const el = refElem.value;
const marginSize = 6;
const offsetHeight = el.offsetHeight;
const offsetWidth = el.offsetWidth;
let tipLeft = left;
let tipTop = top - offsetHeight - marginSize;
tipLeft = Math.max(marginSize, left + Math.floor((tipTarget.offsetWidth - offsetWidth) / 2));
if (tipLeft + offsetWidth + marginSize > scrollLeft + visibleWidth) {
tipLeft = scrollLeft + visibleWidth - offsetWidth - marginSize;
}
if (top - offsetHeight < scrollTop + marginSize) {
tipStore.placement = 'bottom';
tipTop = top + tipTarget.offsetHeight + marginSize;
}
tipStore.style.top = `${tipTop}px`;
tipStore.style.left = `${tipLeft}px`;
tipStore.arrowStyle.left = `${left - tipLeft + tipTarget.offsetWidth / 2}px`;
}
};
const updateValue = value => {
if (value !== reactData.visible) {
reactData.visible = value;
reactData.isUpdate = true;
emit('update:modelValue', value);
}
};
const updateZindex = () => {
if (reactData.tipZindex < (0, _utils.getLastZIndex)()) {
reactData.tipZindex = (0, _utils.nextZIndex)();
}
};
const clickEvent = () => {
if (reactData.visible) {
tooltipMethods.close();
} else {
handleVisible(reactData.target || getSelectorEl(), props.content);
}
};
const targetMouseenterEvent = () => {
handleVisible(reactData.target || getSelectorEl(), props.content);
};
const targetMouseleaveEvent = () => {
const {
trigger,
enterable,
leaveDelay
} = props;
reactData.tipActive = false;
if (enterable && trigger === 'hover') {
setTimeout(() => {
if (!reactData.tipActive) {
tooltipMethods.close();
}
}, leaveDelay);
} else {
tooltipMethods.close();
}
};
const wrapperMouseenterEvent = () => {
reactData.tipActive = true;
};
const wrapperMouseleaveEvent = () => {
const {
trigger,
enterable,
leaveDelay
} = props;
reactData.tipActive = false;
if (enterable && trigger === 'hover') {
setTimeout(() => {
if (!reactData.tipActive) {
tooltipMethods.close();
}
}, leaveDelay);
}
};
const showTip = () => {
const {
tipStore
} = reactData;
const el = refElem.value;
if (el) {
const parentNode = el.parentNode;
if (!parentNode) {
document.body.appendChild(el);
}
}
updateValue(true);
updateZindex();
tipStore.placement = 'top';
tipStore.style = {
width: 'auto',
left: 0,
top: 0,
zIndex: props.zIndex || reactData.tipZindex
};
tipStore.arrowStyle = {
left: '50%'
};
return tooltipMethods.updatePlacement();
};
const handleDelayFn = () => {
internalData.showDelayTip = _xeUtils.default.debounce(() => {
if (reactData.tipActive) {
showTip();
}
}, props.enterDelay, {
leading: false,
trailing: true
});
};
const handleVisible = (target, content) => {
const contentSlot = slots.content;
if (!contentSlot && (content === '' || _xeUtils.default.eqNull(content))) {
return (0, _vue.nextTick)();
}
if (target) {
const {
showDelayTip
} = internalData;
const {
trigger,
enterDelay
} = props;
reactData.tipActive = true;
reactData.tipTarget = target;
reactData.tipContent = content;
if (enterDelay && trigger === 'hover') {
if (showDelayTip) {
showDelayTip();
}
} else {
return showTip();
}
}
return (0, _vue.nextTick)();
};
const getSelectorEl = () => {
const {
selector
} = props;
if (selector) {
if (_xeUtils.default.isElement(selector)) {
return selector;
}
if (_xeUtils.default.isString(selector)) {
return document.querySelector(selector);
}
}
return null;
};
tooltipMethods = {
dispatchEvent(type, params, evnt) {
emit(type, (0, _ui.createEvent)(evnt, {
$tooltip: $xeTooltip
}, params));
},
open(target, content) {
return handleVisible(target || reactData.target || getSelectorEl(), content);
},
close() {
reactData.tipTarget = null;
reactData.tipActive = false;
Object.assign(reactData.tipStore, {
style: {},
placement: '',
arrowStyle: null
});
updateValue(false);
return (0, _vue.nextTick)();
},
toVisible(target, content) {
return handleVisible(target, content);
},
updatePlacement() {
return (0, _vue.nextTick)().then(() => {
const {
tipTarget
} = reactData;
const el = refElem.value;
if (tipTarget && el) {
updateTipStyle();
return (0, _vue.nextTick)().then(() => {
updateTipStyle();
});
}
});
},
isActived() {
return reactData.tipActive;
},
setActived(active) {
reactData.tipActive = !!active;
}
};
Object.assign($xeTooltip, tooltipMethods);
const renderContent = () => {
const {
useHTML
} = props;
const {
tipContent
} = reactData;
const contentSlot = slots.content;
if (contentSlot) {
return (0, _vue.h)('div', {
key: 1,
class: 'vxe-tooltip--content'
}, (0, _vn.getSlotVNs)(contentSlot({})));
}
if (useHTML) {
return (0, _vue.h)('div', {
key: 2,
class: 'vxe-tooltip--content',
innerHTML: tipContent
});
}
return (0, _vue.h)('div', {
key: 3,
class: 'vxe-tooltip--content'
}, `${tipContent}`);
};
const renderVN = () => {
const {
popupClassName,
theme,
isArrow,
enterable
} = props;
const {
tipActive,
visible,
tipStore
} = reactData;
const defaultSlot = slots.default;
const vSize = computeSize.value;
let ons;
if (enterable) {
ons = {
onMouseenter: wrapperMouseenterEvent,
onMouseleave: wrapperMouseleaveEvent
};
}
return (0, _vue.h)('div', Object.assign({
ref: refElem,
class: ['vxe-tooltip--wrapper', `theme--${theme}`, popupClassName ? _xeUtils.default.isFunction(popupClassName) ? popupClassName({
$tooltip: $xeTooltip
}) : popupClassName : '', {
[`size--${vSize}`]: vSize,
[`placement--${tipStore.placement}`]: tipStore.placement,
'is--enterable': enterable,
'is--visible': visible,
'is--arrow': isArrow,
'is--active': tipActive
}],
style: tipStore.style
}, ons), [renderContent(), (0, _vue.h)('div', {
class: 'vxe-tooltip--arrow',
style: tipStore.arrowStyle
}), ...(defaultSlot ? (0, _vn.getSlotVNs)(defaultSlot({})) : [])]);
};
(0, _vue.watch)(() => props.enterDelay, () => {
handleDelayFn();
});
(0, _vue.watch)(() => props.content, val => {
reactData.tipContent = val;
});
(0, _vue.watch)(() => props.modelValue, val => {
if (!reactData.isUpdate) {
if (val) {
handleVisible(reactData.target || getSelectorEl(), props.content);
} else {
tooltipMethods.close();
}
}
reactData.isUpdate = false;
});
(0, _vue.onMounted)(() => {
(0, _vue.nextTick)(() => {
const {
trigger,
content
} = props;
const wrapperElem = refElem.value;
if (wrapperElem) {
const parentNode = wrapperElem.parentNode;
if (parentNode) {
reactData.tipContent = content;
reactData.tipZindex = (0, _utils.nextZIndex)();
_xeUtils.default.arrayEach(wrapperElem.children, (elem, index) => {
if (index > 1) {
parentNode.insertBefore(elem, wrapperElem);
if (!reactData.target) {
reactData.target = elem;
}
}
});
parentNode.removeChild(wrapperElem);
const {
target
} = reactData;
if (target) {
if (trigger === 'hover') {
target.onmouseenter = targetMouseenterEvent;
target.onmouseleave = targetMouseleaveEvent;
} else if (trigger === 'click') {
target.onclick = clickEvent;
}
}
if (props.modelValue) {
handleVisible(target || getSelectorEl(), content);
}
}
}
});
});
(0, _vue.onBeforeUnmount)(() => {
const {
target
} = reactData;
const wrapperElem = refElem.value;
if (target) {
target.onmouseenter = null;
target.onmouseleave = null;
target.onclick = null;
}
if (wrapperElem) {
const parentNode = wrapperElem.parentNode;
if (parentNode) {
parentNode.removeChild(wrapperElem);
}
}
});
handleDelayFn();
$xeTooltip.renderVN = renderVN;
return $xeTooltip;
},
render() {
return this.renderVN();
}
});