mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 14:00:35 +00:00
140 lines
3.3 KiB
TypeScript
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();
|