UNPKG

rabbit-design

Version:

A lightweight UI plugin library written in TypeScript and based on JavaScript

243 lines (193 loc) 7.74 kB
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;