/** * `materialTooltip`: shows an `` popover for the control it belongs to. * * The trigger is the popover's parent element (the button, or the standalone wrapper). Hover * counts only for a pointer that can hover, after a short delay so a pointer passing over a * toolbar does not flash every label; keyboard focus shows it at once. A press and Escape hide it * at once; leaving and blur let it stand for M3's 1.5s, so the words can still be read while the * pointer moves on. Only one tooltip is up at a time: showing one takes the last one down, which * the browser will not do for us because these are `popover="manual"`. */ const HOVER_DELAY_MS = 500 const LEAVE_DELAY_MS = 1500 let visible = null document.addEventListener('alpine:init', () => { window.Alpine.data('materialTooltip', () => ({ timer: null, listeners: [], init() { const tip = this.$el const trigger = tip.parentElement const open = () => tip.matches(':popover-open') const show = (delay) => { clearTimeout(this.timer) this.timer = setTimeout(() => { if (!tip.isConnected || open()) { return } if (visible && visible !== tip && visible.matches(':popover-open')) { visible.hidePopover() } visible = tip tip.showPopover() }, delay) } const hide = (delay = 0) => { clearTimeout(this.timer) this.timer = setTimeout(() => { if (open()) { tip.hidePopover() } if (visible === tip) { visible = null } }, delay) } this.listen(trigger, 'pointerenter', (event) => event.pointerType === 'mouse' && show(HOVER_DELAY_MS)) this.listen(trigger, 'pointerleave', () => hide(LEAVE_DELAY_MS)) this.listen(trigger, 'pointerdown', () => hide()) this.listen(trigger, 'focusin', () => trigger.matches(':focus-within:has(:focus-visible), :focus-visible') && show(0)) this.listen(trigger, 'focusout', () => hide(LEAVE_DELAY_MS)) this.listen(document, 'keydown', (event) => event.key === 'Escape' && hide()) }, listen(target, type, handler) { target.addEventListener(type, handler) this.listeners.push(() => target.removeEventListener(type, handler)) }, destroy() { clearTimeout(this.timer) if (visible === this.$el) { visible = null } this.listeners.forEach((remove) => remove()) }, })) })