2020-03-23 01:14:12 +08:00

140 lines
3.3 KiB
TypeScript

import { EventEmitter } from 'events';
import { TipConfig } from '../../types';
export interface TipOptions extends TipConfig {
target: HTMLElement;
}
function findTip(target: HTMLElement | null): TipOptions | null {
if (!target) {
return null;
}
// optimize deep finding on mouseover
let loopupLimit = 10;
while (target && loopupLimit-- > 0) {
// get tip from target node
if (target.dataset && target.dataset.tip) {
return {
children: target.dataset.tip,
direction: target.dataset.direction || target.dataset.dir,
theme: target.dataset.theme,
target,
};
}
// or get tip from child nodes
let child: HTMLElement | null = target.lastElementChild as HTMLElement;
while (child) {
if (child.dataset && child.dataset.role === 'tip') {
const tipId = child.dataset.tipId;
if (!tipId) {
return null;
}
const tipProps = tipsMap.get(tipId);
if (!tipProps) {
return null;
}
return {
...tipProps,
target,
};
}
child = child.previousElementSibling as HTMLElement;
}
target = target.parentNode as HTMLElement;
}
return null;
}
class TipHandler {
tip: TipOptions | null = null;
private showDelay: number | null = null;
private hideDelay: number | null = null;
private emitter = new EventEmitter();
setTarget(target: HTMLElement) {
const tip = findTip(target);
if (tip) {
if (this.tip) {
// the some target should return
if (this.tip.target === tip.target) {
this.tip = tip;
return;
}
// not show already, reset show delay
if (this.showDelay) {
clearTimeout(this.showDelay);
this.showDelay = null;
this.tip = null;
} else {
if (this.hideDelay) {
clearTimeout(this.hideDelay);
this.hideDelay = null;
}
this.tip = tip;
this.emitter.emit('tipchange');
return;
}
}
this.tip = tip;
if (this.hideDelay) {
clearTimeout(this.hideDelay);
this.hideDelay = null;
this.emitter.emit('tipchange');
} else {
this.showDelay = setTimeout(() => {
this.showDelay = null;
this.emitter.emit('tipchange');
}, 350) as any;
}
} else {
if (this.showDelay) {
clearTimeout(this.showDelay);
this.showDelay = null;
} else {
this.hideDelay = setTimeout(() => {
this.hideDelay = null;
}, 100) as any;
}
this.tip = null;
this.emitter.emit('tipchange');
}
}
hideImmediately() {
if (this.hideDelay) {
clearTimeout(this.hideDelay);
this.hideDelay = null;
}
if (this.showDelay) {
clearTimeout(this.showDelay);
this.showDelay = null;
}
this.tip = null;
this.emitter.emit('tipchange');
}
onChange(func: () => void) {
this.emitter.on('tipchange', func);
return () => {
this.emitter.removeListener('tipchange', func);
};
}
}
const tipsMap = new Map<string, TipConfig>();
export function saveTips(id: string, props: TipConfig | null) {
if (props) {
tipsMap.set(id, props);
} else {
tipsMap.delete(id);
}
}
export default new TipHandler();