Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { nanoid } from "nanoid";
import type { FontWeight } from "../Common/Style";
import { rgba, type RgbaColorArray } from "../Common/Color";
 
export interface TooltipOptions {
  color?: string | RgbaColorArray;
  backgroundColor?: string | RgbaColorArray;
  fontSize?: number;
  fontWeight?: FontWeight;
  paddingInline?: number;
  paddingBlock?: number;
  borderRadius?: number;
}
 
export class Tooltip {
  id = "tooltip";
  visible = false;
  color = rgba("#fff");
  fontWeight = "500" as FontWeight;
  backgroundColor = rgba("#000");
  fontSize = 16;
  paddingInline = 8;
  paddingBlock = 4;
  borderRadius = 4;
 
  constructor(options?: TooltipOptions) {
    this.id = `tooltip-${nanoid()}`;
    this.color = options?.color ? rgba(options.color) : this.color;
    this.backgroundColor = options?.backgroundColor ? rgba(options.backgroundColor) : this.backgroundColor;
    this.paddingInline = options?.paddingInline ?? this.paddingInline;
    this.paddingBlock = options?.paddingBlock ?? this.paddingBlock;
    this.borderRadius = options?.borderRadius ?? this.borderRadius;
    this.fontSize = options?.fontSize ?? this.fontSize;
    this.fontWeight = options?.fontWeight ?? this.fontWeight;
    this.initialize();
  }
 
  initialize() {
    if (document.getElementById(this.id)) return;
    const span = document.createElement("span");
    const root = document.body;
 
    span.id = this.id;
    span.style.display = "none";
    span.style.position = "absolute";
    this.apply(span);
 
    root?.appendChild(span);
  }
 
  update(options: Partial<TooltipOptions>) {
    const span = document.getElementById(this.id);
 
    this.color = options?.color ? rgba(options.color) : this.color;
    this.backgroundColor = options?.backgroundColor ? rgba(options.backgroundColor) : this.backgroundColor;
    this.paddingInline = options?.paddingInline ?? this.paddingInline;
    this.paddingBlock = options?.paddingBlock ?? this.paddingBlock;
    this.borderRadius = options?.borderRadius ?? this.borderRadius;
    this.fontSize = options?.fontSize ?? this.fontSize;
 
    if (span) {
      this.apply(span);
    }
  }
 
  apply(node: HTMLElement) {
    node.style.color = this.color.toString();
    node.style.backgroundColor = this.backgroundColor.toString();
    node.style.paddingInline = `${this.paddingInline}px`;
    node.style.paddingBlock = `${this.paddingBlock}px`;
    node.style.borderRadius = `${this.borderRadius}px`;
    node.style.fontSize = `${this.fontSize}px`;
    node.style.fontWeight = this.fontWeight;
    node.style.zIndex = "9999";
    node.style.pointerEvents = "none";
  }
 
  show(x: number, y: number, text?: string, center = true) {
    const span = document.getElementById(this.id);
 
    this.visible = true;
 
    if (span && text) {
      span.style.display = "block";
      if (center) {
        span.style.left = `${x - span.clientWidth / 2}px`;
      } else {
        span.style.left = `${x}px`;
      }
      span.style.top = `${y}px`;
      span.innerText = text;
    }
  }
 
  hide() {
    if (!this.visible) {
      return;
    }
    const span = document.getElementById(this.id);
 
    this.visible = false;
 
    if (span) {
      span.style.display = "none";
    }
  }
 
  destroy() {
    document.getElementById(this.id)?.remove();
  }
}