/** @typedef { import('types/HovercardResponse.js').HovercardResponse} HovercardResponse */ export default function hovercard(){ return { /** * @type {boolean} */ start: false, /** * @type {HovercardResponse} */ data: null, /** * @type {boolean} */ loading: false, /** * @type {any} */ error: false, /** * @type {HTMLElement|null} */ anchorEl: null, /** * @type {number} */ x: 0, /** * @type {number} */ y: 0, /** * * @param {HTMLElement} anchorEl * @param {string} fetchUrl * @return {Promise} */ async open(anchorEl, fetchUrl){ if( this.start && this.anchorEl === anchorEl ){ // this.close(); return; } this.start = true; this.anchorEl = anchorEl; this.data = null; this.loading = true; this.error = false; this.updatePosition(anchorEl); try { const RESPONSE = await fetch(fetchUrl); if( !RESPONSE.ok ) throw new Error(RESPONSE.status); let json = await RESPONSE.json(); if( !json.user ) throw new Error(RESPONSE.status); this.data = json.user; Alpine.nextTick(() => { const card = document.querySelector('.hovercard'); if (card) window.refreshIcons(card); this.updatePosition(this.anchorEl); }); } catch( error ){ this.error = true; } finally { this.loading = false; } }, /** * Update Hovercard position * @param {HTMLElement} anchorEl */ updatePosition(anchorEl){ const RECT = anchorEl.getBoundingClientRect(); const SCROLL_X = window.scrollX; const SCROLL_Y = window.scrollY; const VIEWPORT_WIDTH = window.innerWidth; const VIEWPORT_HEIGHT = window.innerHeight; const CARD = document.querySelector('.hovercard'); const WIDTH = CARD?.offsetWidth || 280; const HEIGHT = CARD?.offsetHeight || 320; let x = RECT.right + SCROLL_X + 8; let y = RECT.bottom + SCROLL_Y + 8; if( x + WIDTH > VIEWPORT_WIDTH - 8 && RECT.left + SCROLL_X - WIDTH - 8 >= 8 ){ x = RECT.left + SCROLL_X - WIDTH - 8; } else { x = Math.max(8, Math.min(x, VIEWPORT_WIDTH - WIDTH - 8)); } if( y + HEIGHT > VIEWPORT_HEIGHT + SCROLL_Y - 8 && RECT.top + SCROLL_Y - HEIGHT - 8 >= 8 ){ y = RECT.top + SCROLL_Y - HEIGHT - 8; } else { y = Math.max(8, Math.min(y, VIEWPORT_HEIGHT + SCROLL_Y - HEIGHT - 8)); } this.x = x; this.y = y; }, /** * Close the hovercard. */ close(){ this.start = false; this.data = null; this.anchorEl = null; this.error = false; } } }