import { Controller } from '@hotwired/stimulus' import { computePosition, flip, shift, offset, autoUpdate } from '@floating-ui/dom'; export default class extends Controller { static targets = ["tooltip"]; static values = { placement: { type: String, default: "top" }, offset: { type: Number, default: 10 }, crossAxis: { type: Number, default: 0 }, alignmentAxis: { type: Number, default: null }, }; connect() { this._cleanup = null; this.boundUpdate = this.update.bind(this); this.startAutoUpdate(); this.addEventListeners(); } disconnect() { this.removeEventListeners(); this.stopAutoUpdate(); } addEventListeners() { this.element.addEventListener("mouseenter", this.show); this.element.addEventListener("mouseleave", this.hide); } removeEventListeners() { this.element.removeEventListener("mouseenter", this.show); this.element.removeEventListener("mouseleave", this.hide); } show = () => { this.tooltipTarget.style.display = 'block'; this.update(); // Ensure immediate update when shown } hide = () => { this.tooltipTarget.style.display = 'none'; } startAutoUpdate() { if (!this._cleanup) { this._cleanup = autoUpdate( this.element, this.tooltipTarget, this.boundUpdate ); } } stopAutoUpdate() { if (this._cleanup) { this._cleanup(); this._cleanup = null; } } update() { // Update position even if not visible, to ensure correct positioning when shown computePosition(this.element, this.tooltipTarget, { placement: this.placementValue, middleware: [ offset({ mainAxis: this.offsetValue, crossAxis: this.crossAxisValue, alignmentAxis: this.alignmentAxisValue }), flip(), shift({ padding: 5 }) ], }).then(({ x, y, placement, middlewareData }) => { Object.assign(this.tooltipTarget.style, { left: `${x}px`, top: `${y}px`, }); }); } }