vTooltipInEditor.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import Vue from "vue";
  2. import { computePosition, offset, flip, shift, arrow } from "@floating-ui/dom";
  3. let tooltipNode = null;
  4. let intervalId = null;
  5. function removeTooltip() {
  6. try {
  7. intervalId && clearInterval(intervalId);
  8. tooltipNode && document.body.removeChild(tooltipNode);
  9. } catch (e) {
  10. // console.log(
  11. // "尝试从DOM上移除tooltip元素失败,通常是因为已经在其他回调中被移除了,不需处理:",
  12. // e
  13. // );
  14. }
  15. }
  16. Vue.directive("tooltip", {
  17. bind: function (el, binding) {
  18. if (!binding.value) {
  19. return;
  20. }
  21. el.addEventListener(
  22. "mouseenter",
  23. function (e) {
  24. tooltipNode = document.createElement("div");
  25. tooltipNode.style.position = "fixed";
  26. tooltipNode.style.zIndex = 100;
  27. tooltipNode.style.backgroundColor = "#191A1C";
  28. tooltipNode.style.border = "1px solid rgba(151, 151, 151, 0.2)";
  29. tooltipNode.style.borderRadius = "3px";
  30. tooltipNode.style.border = "1px solid rgba(151, 151, 151, 0.2)";
  31. tooltipNode.style.boxShadow = "0px 2px 12px 0px rgba(0, 0, 0, 0.06)";
  32. tooltipNode.style.padding = "8px 8px";
  33. tooltipNode.style.fontSize = "12px";
  34. tooltipNode.style.cursor = "default";
  35. tooltipNode.style.pointerEvents = "none";
  36. tooltipNode.style.wordBreak = "keep-all";
  37. tooltipNode.style.whiteSpace = "pre";
  38. tooltipNode.style.fontSize = "12px";
  39. tooltipNode.style.lineHeight = "17px";
  40. tooltipNode.style.color = "rgba(255, 255, 255, 0.6)";
  41. tooltipNode.innerText = binding.value;
  42. const arrowNode = document.createElement("div");
  43. arrowNode.style.position = "absolute";
  44. arrowNode.style.backgroundColor = "inherit";
  45. arrowNode.style.boxSizing = "border-box";
  46. arrowNode.style.width = "12px";
  47. arrowNode.style.height = "12px";
  48. arrowNode.style.border = "1px solid transparent";
  49. arrowNode.style.borderRight = "inherit";
  50. arrowNode.style.borderBottom = "inherit";
  51. arrowNode.style.transform = "rotate(45deg)";
  52. tooltipNode.appendChild(arrowNode);
  53. document.body.appendChild(tooltipNode);
  54. computePosition(el, tooltipNode, {
  55. placement: "top",
  56. middleware: [
  57. offset(13),
  58. flip(),
  59. shift({ padding: 12 }),
  60. arrow({
  61. element: arrowNode,
  62. padding: 3,
  63. }),
  64. ],
  65. })
  66. .then(({ x, y, placement, middlewareData }) => {
  67. Object.assign(tooltipNode.style, {
  68. left: `${x}px`,
  69. top: `${y}px`,
  70. });
  71. const { x: arrowX, y: arrowY } = middlewareData.arrow;
  72. const staticSide = {
  73. top: "bottom",
  74. right: "left",
  75. bottom: "top",
  76. left: "right",
  77. }[placement.split("-")[0]];
  78. Object.assign(arrowNode.style, {
  79. left: arrowX != null ? `${arrowX}px` : "",
  80. [staticSide]: "-6px",
  81. });
  82. })
  83. .catch((err) => {
  84. console.log(
  85. "计算tooltip位置时出现异常,可能因为目标元素已经被卸载。"
  86. );
  87. });
  88. intervalId = setInterval(() => {
  89. if (!document.contains(el)) {
  90. removeTooltip();
  91. }
  92. }, 300);
  93. },
  94. {
  95. passive: false,
  96. }
  97. );
  98. el.addEventListener("mouseleave", removeTooltip);
  99. el.addEventListener("mousedown", removeTooltip);
  100. el.addEventListener("keydown", removeTooltip);
  101. el.addEventListener("scroll", removeTooltip);
  102. el.addEventListener("dragstart", removeTooltip);
  103. el.addEventListener("dragstart", removeTooltip);
  104. el.addEventListener("dragleave", removeTooltip);
  105. },
  106. });