PageSourceSearch

https://app.yuml.me/assets/controllers/tooltip_controller-6d6bf72c.js

js yuml.me collected 2026-10-02 04:33:51 UTC 2,716 bytes, 106 lines download raw bytes

1// Example usage:
2// <div data-controller="tooltip" data-tooltip-content-value="Hello world"></div>
3
4import { Controller } from "@hotwired/stimulus"
5import { autoUpdate, computePosition, flip, offset, arrow, shift } from "@floating-ui/dom"
6
7export default class extends Controller {
8  static values = {
9    content: String,
10    placement: "top",
11    offset: 6,
12    allowHtml: true
13  }
14
15  connect() {
16    this.createTooltipElements()
17    this.cleanup = autoUpdate(this.element, this.tooltip, this.updatePosition.bind(this))
18    this.addEvents()
19  }
20
21  disconnect() {
22    this.tooltip?.remove()
23    this?.cleanup()
24  }
25
26  createTooltipElements() {
27    this.tooltip = document.createElement("div")
28    this.tooltip.dataset.tooltipTarget = "content"
29    this.tooltip.classList.add("tooltip")
30    if (this.allowHtmlValue) {
31      this.tooltip.innerHTML = this.contentValue
32    } else {
33      this.tooltip.textContent = this.contentValue
34    }
35
36    this.arrow = document.createElement("div")
37    this.arrow.classList.add("arrow")
38
39    this.tooltip.appendChild(this.arrow)
40    document.body.appendChild(this.tooltip)
41  }
42
43  updatePosition() {
44    computePosition(this.element, this.tooltip, {
45      placement: this.placementValue,
46      middleware: [
47        flip(),
48        shift({
49          mainAxis: true,
50          crossAxis: true,
51          padding: 2,
52        }),
53        offset(this.offsetValue),
54        arrow({element: this.arrow}),
55      ]
56    }).then(({x, y, placement, middlewareData}) => {
57      Object.assign(this.tooltip.style, {
58        left: `${x}px`,
59        top: `${y}px`
60      });
61
62      const side = placement.split("-")[0];
63
64      const staticSide = {
65        top: "bottom",
66        right: "left",
67        bottom: "top",
68        left: "right"
69      }[side];
70
71      if (middlewareData.arrow) {
72        const { x, y } = middlewareData.arrow;
73        Object.assign(this.arrow.style, {
74          left: x != null ? `${x}px` : "",
75          top: y != null ? `${y}px` : "",
76          // Ensure the static side gets unset when
77          // flipping to other placements' axes.
78          right: "",
79          bottom: "",
80          [staticSide]: `${-8 / 2}px`,
81        });
82      }
83    })
84  }
85
86  showTooltip() {
87    this.tooltip.classList.add("open")
88    this.updatePosition()
89  }
90
91  hideTooltip() {
92    this.tooltip.classList.remove("open")
93  }
94
95  addEvents() {
96    [
97      ['mouseenter', this.showTooltip.bind(this)],
98      ['mouseleave', this.hideTooltip.bind(this)],
99      ['focus', this.showTooltip.bind(this)],
100      ['blur', this.hideTooltip.bind(this)],
101      ['click', this.hideTooltip.bind(this)],
102    ].forEach(([event, listener]) => {
103      this.element.addEventListener(event, listener)
104    })
105  }
106}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.