/** @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); }); } 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; let x = RECT.left + SCROLL_X; let y = RECT.bottom + SCROLL_Y + 8; const WIDTH = 280; if( x + WIDTH > window.innerWidth ){ x = window.innerWidth - WIDTH - 16; } this.x = x; this.y = y; }, /** * Close the hovercard. */ close(){ this.start = false; this.data = null; this.anchorEl = null; this.error = false; } } }