djledda.de main
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 

123 rader
4.4 KiB

  1. import { h, qsa } from "@/util.ts";
  2. import { type CSSProperties, defineComponent, onBeforeUnmount, onMounted, ref } from "vue";
  3. const carrierStyle = {
  4. opacity: "0",
  5. display: "block",
  6. pointerEvents: "none",
  7. backgroundColor: "black",
  8. border: "white solid 1px",
  9. color: "white",
  10. padding: "10px",
  11. position: "absolute",
  12. zIndex: "1",
  13. overflow: "hidden",
  14. height: "0",
  15. width: "0",
  16. transition: "opacity 200ms, height 200ms, width 200ms",
  17. } satisfies CSSProperties;
  18. const textCarrierStyle = {
  19. width: "350px",
  20. display: "block",
  21. overflow: "hidden",
  22. } satisfies CSSProperties;
  23. const defaultWidth = 350;
  24. export default defineComponent({
  25. name: "dj-sexy-tooltip",
  26. setup() {
  27. const active = ref(false);
  28. const carrier = ref<HTMLElement | null>(null);
  29. const textCarrier = ref<HTMLElement | null>(null);
  30. onMounted(() => {
  31. document.body.appendChild(h("style", { textContent: `.tooltip { display: none; }` }));
  32. document.body.style.position = "relative";
  33. document.addEventListener("mousemove", (event) => this.rerenderTooltip({ x: event.pageX, y: event.pageY }));
  34. });
  35. function rerenderTooltip(mousePos: { x: number; y: number }) {
  36. const newText = this.getTooltipTextForHoveredElement(mousePos);
  37. if (newText !== this.textCarrier) {
  38. this.transitionTooltipSize(newText);
  39. const tooltipHasText = newText !== "";
  40. if (tooltipHasText !== this.active) {
  41. this.toggleActive();
  42. }
  43. }
  44. if (this.isStillVisible()) {
  45. this.updatePosition(mousePos);
  46. }
  47. }
  48. function isStillVisible() {
  49. return getComputedStyle(carrier.value).opacity !== "0";
  50. }
  51. function updatePosition(pos: { x: number; y: number }) {
  52. if (pos.x + 15 + this.carrier.clientWidth <= document.body.scrollWidth) {
  53. this.carrier.style.left = (pos.x + 15) + "px";
  54. } else {
  55. this.carrier.style.left = (document.body.scrollWidth - this.carrier.clientWidth - 5) + "px";
  56. }
  57. if (pos.y + this.carrier.clientHeight <= document.body.scrollHeight) {
  58. this.carrier.style.top = pos.y + "px";
  59. } else {
  60. this.carrier.style.top = (document.body.scrollHeight - this.carrier.clientHeight - 5) + "px";
  61. }
  62. }
  63. function toggleActive() {
  64. if (this.active) {
  65. this.active = false;
  66. this.carrier.style.opacity = "0";
  67. this.carrier.style.padding = "0";
  68. } else {
  69. this.active = true;
  70. this.carrier.style.opacity = "1";
  71. this.carrier.style.padding = SexyTooltip.carrierStyle.padding;
  72. }
  73. }
  74. function transitionTooltipSize(text: string) {
  75. const testDiv = h("div");
  76. testDiv.style.height = "auto";
  77. testDiv.style.width = SexyTooltip.defaultWidth + "px";
  78. testDiv.innerHTML = text;
  79. document.body.appendChild(testDiv);
  80. const calculatedHeight = testDiv.scrollHeight;
  81. testDiv.style.display = "inline";
  82. testDiv.style.width = "auto";
  83. document.body.removeChild(testDiv);
  84. const size = { height: calculatedHeight + "px", width: "350px" };
  85. this.carrier.style.height = size.height;
  86. this.carrier.style.width = text === "" ? "0" : size.width;
  87. this.textCarrier.innerHTML = text;
  88. }
  89. function getTooltipTextForHoveredElement(mousePos: { x: number; y: number }) {
  90. for (const elem of this.elementsWithTooltips) {
  91. const boundingRect = elem.getBoundingClientRect();
  92. const inYRange = mousePos.y >= boundingRect.top + window.pageYOffset - 1 &&
  93. mousePos.y <= boundingRect.bottom + window.pageYOffset + 1;
  94. const inXRange = mousePos.x >= boundingRect.left + window.pageXOffset - 1 &&
  95. mousePos.x <= boundingRect.right + window.pageXOffset + 1;
  96. if (inYRange && inXRange) {
  97. return elem.nextElementSibling?.innerText ?? "";
  98. }
  99. }
  100. return "";
  101. }
  102. return () => (
  103. <div ref={carrier}>
  104. <span ref={textCarrier} />
  105. </div>
  106. );
  107. },
  108. });