djledda.de main
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

131 рядки
4.7 KiB

  1. interface Point {
  2. x: number;
  3. y: number;
  4. }
  5. interface Size {
  6. height: string;
  7. width: string;
  8. }
  9. class SexyTooltip {
  10. private static readonly carrierStyle: Partial<CSSStyleDeclaration> = {
  11. opacity: "0",
  12. display: "block",
  13. pointerEvents: "none",
  14. backgroundColor: "black",
  15. border: "white solid 1px",
  16. color: "white",
  17. padding: "10px",
  18. position: "absolute",
  19. zIndex: "1",
  20. overflow: "hidden",
  21. height: "0",
  22. width: "0",
  23. transition: "opacity 200ms, height 200ms, width 200ms",
  24. };
  25. private static readonly textCarrierStyle: Partial<CSSStyleDeclaration> = {
  26. width: "350px",
  27. display: "block",
  28. overflow: "hidden",
  29. };
  30. private static readonly defaultWidth = 350;
  31. private readonly carrier: HTMLDivElement;
  32. private readonly textCarrier: HTMLSpanElement;
  33. private readonly elementsWithTooltips: Element[] = [];
  34. private active: boolean = false;
  35. constructor(carrier: HTMLDivElement) {
  36. if (carrier.childNodes.length > 0) {
  37. throw new Error("Incorrect setup for tooltip! Remove the child nodes from the tooltip div.");
  38. }
  39. this.carrier = carrier;
  40. this.textCarrier = document.createElement("span");
  41. this.carrier.appendChild(this.textCarrier);
  42. Object.assign(this.carrier.style, SexyTooltip.carrierStyle);
  43. Object.assign(this.textCarrier.style, SexyTooltip.textCarrierStyle);
  44. document.querySelectorAll(".tooltip").forEach((element) => {
  45. if (element.nodeName === "SPAN") {
  46. console.log(element.previousElementSibling);
  47. this.elementsWithTooltips.push(element.previousElementSibling);
  48. }
  49. });
  50. document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY }));
  51. }
  52. rerenderTooltip(mousePos: Point) {
  53. const newText = this.getTooltipTextForHoveredElement(mousePos);
  54. if (newText !== this.textCarrier.innerHTML) {
  55. this.transitionTooltipSize(newText);
  56. const tooltipHasText = newText !== "";
  57. if (tooltipHasText !== this.active) {
  58. this.toggleActive();
  59. }
  60. }
  61. if (this.isStillVisible()) {
  62. this.updatePosition(mousePos);
  63. }
  64. }
  65. isStillVisible() {
  66. return getComputedStyle(this.carrier).opacity !== "0";
  67. }
  68. updatePosition(pos: Point) {
  69. if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) {
  70. this.carrier.style.left = (pos.x + 15) + "px";
  71. } else {
  72. this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px";
  73. }
  74. if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) {
  75. this.carrier.style.top = pos.y + "px";
  76. } else {
  77. this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px";
  78. }
  79. }
  80. toggleActive() {
  81. if (this.active) {
  82. this.active = false;
  83. this.carrier.style.opacity = "0";
  84. this.carrier.style.padding = "0";
  85. } else {
  86. this.active = true;
  87. this.carrier.style.opacity = "1";
  88. this.carrier.style.padding = SexyTooltip.carrierStyle.padding;
  89. }
  90. }
  91. transitionTooltipSize(text: string) {
  92. const testDiv = document.createElement("div");
  93. testDiv.style.height = "auto";
  94. testDiv.style.width = SexyTooltip.defaultWidth + "px";
  95. testDiv.innerHTML = text;
  96. document.body.appendChild(testDiv);
  97. const calculatedHeight = testDiv.scrollHeight;
  98. testDiv.style.display = "inline";
  99. testDiv.style.width = "auto";
  100. document.body.removeChild(testDiv);
  101. const size = { height: calculatedHeight + "px", width: "350px" };
  102. this.carrier.style.height = size.height;
  103. this.carrier.style.width = text === "" ? "0" : size.width;
  104. this.textCarrier.innerHTML = text;
  105. }
  106. getTooltipTextForHoveredElement(mousePos: Point): string {
  107. for (const elem of this.elementsWithTooltips) {
  108. const boundingRect = elem.getBoundingClientRect();
  109. const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 &&
  110. mousePos.y <= boundingRect.bottom + window.pageYOffset + 1;
  111. const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 &&
  112. mousePos.x <= boundingRect.right + window.pageXOffset + 1;
  113. if (inYRange && inXRange) {
  114. return (elem.nextElementSibling as HTMLSpanElement)?.innerText ?? "";
  115. }
  116. }
  117. return "";
  118. }
  119. }
  120. export default SexyTooltip;