rabbit-design
Version:
A lightweight UI plugin library written in TypeScript and based on JavaScript
243 lines (193 loc) • 7.74 kB
text/typescript
import PREFIX from '../prefix';
import { type, validComps } from '../../utils';
import { $el, createElem, setCss, setHtml, setText, removeAttrs } from '../../dom-utils';
import { _Popper, CssTransition, _arrow } from '../../mixins';
interface TooltipEvents {
onShow?: () => void;
onHide?: () => void;
}
interface Config {
config(
el: string
): {
content: string | number; // 显示的内容
events: (options: TooltipEvents) => void; // Tooltip 事件
};
}
let SHOWTIMER: any, EVENTTIMER: any;
class Tooltip implements Config {
readonly VERSION: string;
readonly COMPONENTS: NodeListOf<Element>;
constructor() {
this.VERSION = 'v1.0';
this.COMPONENTS = $el('r-tooltip', { all: true });
this._create(this.COMPONENTS);
_arrow.scrollUpdate();
}
public config(
el: string
): {
content: string | number;
events: (options: TooltipEvents) => void;
} {
const target = $el(el);
validComps(target, 'tooltip');
const { _getDelay, _getIsAlways, _getIsDisabled } = Tooltip.prototype;
const popper = target.querySelector(`.${PREFIX.tooltip}-popper`);
const popperText = target.querySelector(`.${PREFIX.tooltip}-inner`);
return {
get content() {
return setText(popperText);
},
set content(newVal: string | number) {
if (type.isStr(newVal) || type.isNum(newVal)) setHtml(popperText, `${newVal}`);
},
events({ onShow, onHide }): void {
if (_getIsAlways(target) || _getIsDisabled(target)) return;
const delay = _getDelay(target);
_Popper.handleHoverShowAndHideEvents({
reference: target,
popper: popper,
datasetVal: 'tooltipShow',
showCb: onShow,
hideCb: onHide,
delay: delay,
timer: EVENTTIMER
});
}
};
}
private _create(COMPONENTS: NodeListOf<Element>): void {
COMPONENTS.forEach((node) => {
this._createTooltipNodes(node);
removeAttrs(node, ['content', 'theme', 'delay', 'max-width', 'disabled', 'always']);
});
}
private _createTooltipNodes(reference: Element): void {
const TooltipRef = createElem('div');
const TooltipPopper = createElem('div');
const TooltipContent = createElem('div');
const TooltipArrow = createElem('div');
const TooltipInner = createElem('div');
this._setCls(reference, [
TooltipRef,
TooltipPopper,
TooltipContent,
TooltipArrow,
TooltipInner
]);
this._setTip(reference, TooltipInner);
this._setMaxWidth(reference, TooltipInner);
const disabledShow = this._getIsDisabled(reference);
const alwaysShow = this._setIsAlwaysShow(reference, TooltipPopper);
// 如果 tooltip 没有设置为总是可见或者禁用显示,则正常鼠标移入移出事件
if (!alwaysShow && !disabledShow) {
this._triggerDisplay('enter', reference, TooltipPopper);
this._triggerDisplay('leave', reference, TooltipPopper);
}
const { firstElementChild } = reference;
// 只选取第一个子元素作为宿主元素
if (firstElementChild) TooltipRef.appendChild(firstElementChild);
TooltipPopper.appendChild(TooltipContent);
TooltipPopper.append(TooltipArrow, TooltipInner);
reference.append(TooltipRef, TooltipPopper);
}
private _setCls(reference: Element, nodes: HTMLElement[]): void {
// 获取主题颜色
const theme = this._getTheme(reference);
const nodesCls = [
`${PREFIX.tooltip}-rel`,
`${PREFIX.tooltip}-popper ${PREFIX.tooltip}-${theme}`,
`${PREFIX.tooltip}-content`,
`${PREFIX.tooltip}-arrow`,
`${PREFIX.tooltip}-inner`
];
// 批量添加样式类名
let i = 0;
const { length } = nodes;
for (; i < length; i++) {
const node = nodes[i];
node.className = `${nodesCls[i]}`;
}
}
private _triggerDisplay(
trigger: 'enter' | 'leave',
reference: Element,
popper: HTMLElement
): void {
const ev = () => {
if (trigger === 'enter') this._initSetPopper(reference, popper);
CssTransition(popper, {
inOrOut: trigger === 'enter' ? 'in' : 'out',
rmCls: true,
enterCls: 'zoom-big-fast-enter',
leaveCls: 'zoom-big-fast-leave',
timeout: 85
});
};
const delay = this._getDelay(reference);
if (trigger === 'enter') {
reference.addEventListener('mouseenter', () => {
SHOWTIMER = setTimeout(() => {
ev();
}, delay);
});
_arrow.toggleUpdate(popper, 'hover', reference, delay);
} else {
reference.addEventListener('mouseleave', () => {
clearTimeout(SHOWTIMER);
ev();
});
}
}
private _setTip(reference: Element, popper: Element): string {
return (popper.textContent = this._getTip(reference));
}
private _setMaxWidth(reference: Element, popper: HTMLElement): void {
const maxWidth = this._getMaxWidth(reference);
if (maxWidth <= 0) return;
setCss(popper, 'maxWidth', `${maxWidth}px`);
popper.classList.add(`${PREFIX.tooltip}-inner-with-width`);
}
private _initSetPopper(reference: Element, popper: HTMLElement): any {
const offset = this._getOffset(reference);
const placement = this._getPlacement(reference);
popper.setAttribute('x-placement', placement);
return _Popper._newCreatePopper(reference, popper, placement, offset);
}
private _setIsAlwaysShow(reference: Element, popper: HTMLElement): boolean | void {
const isAlways = this._getIsAlways(reference);
if (isAlways) {
setCss(popper, 'display', '');
this._initSetPopper(reference, popper);
return true;
}
setCss(popper, 'display', 'none');
}
private _getTip(node: Element): string {
return node.getAttribute('content') || '';
}
private _getPlacement(node: Element): any {
return node.getAttribute('placement') || 'bottom';
}
private _getDelay(node: Element): number {
// 默认 100毫秒的延迟,防止鼠标快速经过时也会显示tooltip
return Number(node.getAttribute('delay')) || 100;
}
private _getIsAlways(node: Element): boolean {
return node.getAttribute('always') === 'true';
}
private _getIsDisabled(node: Element): boolean {
return node.getAttribute('disabled') === 'true';
}
private _getTheme(node: Element): string {
return node.getAttribute('theme') || 'dark';
}
private _getMaxWidth(node: Element): number {
return Number(node.getAttribute('max-width')) || 0;
}
private _getOffset(node: Element): number {
return Number(node.getAttribute('offset')) || 0;
}
}
export default Tooltip;