UNPKG

feeeedback

Version:

Simple, beautiful, open-source feedback widget with Discord webhooks

141 lines (122 loc) 4.29 kB
(function () { const FRAME_WIDTH = 320; const FRAME_HEIGHT = 178; const MARGIN = 4; const ANIMATION_DURATION = 200; const frameUrl = "https://feeeedback.tiag.workers.dev/widget/index.html#" + encodeURIComponent( location.hostname + "/" + (document.currentScript?.getAttribute("pid") || document.querySelector("script[pid]")?.getAttribute("pid")) ); const frame = document.createElement("iframe"); frame.src = frameUrl; frame.style.display = "none"; frame.style.width = FRAME_WIDTH + "px"; frame.style.height = FRAME_HEIGHT + "px"; frame.style.border = "none"; frame.style.boxShadow = "0 18px 50px -10px rgba(0, 0, 0, 0.2)"; frame.style.borderRadius = "16px"; frame.frameborder = "none"; frame.style.position = "fixed"; frame.style.backgroundColor = "white"; frame.style.zIndex = "9999"; frame.style.transition = `opacity ${ANIMATION_DURATION}ms ease, transform ${ANIMATION_DURATION}ms ease`; frame.style.opacity = "0"; frame.style.transform = "scale(0.95)"; document.body.appendChild(frame); function calculatePosition(triggerElement) { const viewport = { width: window.innerWidth, height: window.innerHeight, }; const trigger = { top: triggerElement.getBoundingClientRect().top, left: triggerElement.getBoundingClientRect().left, width: triggerElement.offsetWidth, height: triggerElement.offsetHeight, bottom: triggerElement.getBoundingClientRect().bottom, right: triggerElement.getBoundingClientRect().right, }; const position = { top: 0, left: 0, placement: "bottom", }; if (trigger.bottom + FRAME_HEIGHT + MARGIN <= viewport.height) { position.top = trigger.bottom + MARGIN; position.placement = "bottom"; } else if (trigger.top - FRAME_HEIGHT - MARGIN >= 0) { position.top = trigger.top - FRAME_HEIGHT - MARGIN; position.placement = "top"; } else { position.top = Math.max(MARGIN, (viewport.height - FRAME_HEIGHT) / 2); position.placement = "center"; } if (trigger.left + FRAME_WIDTH + MARGIN <= viewport.width) { position.left = trigger.left; } else if (trigger.right - FRAME_WIDTH >= MARGIN) { position.left = trigger.right - FRAME_WIDTH; } else { position.left = Math.max(MARGIN, (viewport.width - FRAME_WIDTH) / 2); } position.top = Math.max( MARGIN, Math.min(position.top, viewport.height - FRAME_HEIGHT - MARGIN) ); position.left = Math.max( MARGIN, Math.min(position.left, viewport.width - FRAME_WIDTH - MARGIN) ); return position; } function showFrame(position) { frame.style.display = "block"; frame.style.top = position.top + "px"; frame.style.left = position.left + "px"; frame.setAttribute("data-placement", position.placement); requestAnimationFrame(() => { frame.style.opacity = "1"; frame.style.transform = "scale(1)"; }); } function hideFrame() { frame.style.opacity = "0"; frame.style.transform = "scale(0.95)"; setTimeout(() => { frame.style.display = "none"; }, ANIMATION_DURATION); } document.body.addEventListener("click", function (e) { const element = e.target.closest("[data-feeeedback]") || (e.target.getAttribute("data-feeeedback") ? e.target : null); if (!element) { hideFrame(); return; } const position = calculatePosition(element); showFrame(position); frame.contentWindow.postMessage("feeeedback--reset", "*"); }); window.addEventListener("message", function (e) { if (e.data == "feeeedback--close") { hideFrame(); } }); let resizeTimeout; window.addEventListener("resize", function () { if (frame.style.display === "none") return; clearTimeout(resizeTimeout); resizeTimeout = setTimeout(() => { const element = document.querySelector("[data-feeeedback]:hover") || document.querySelector("[data-feeeedback]"); if (element) { const position = calculatePosition(element); frame.style.top = position.top + "px"; frame.style.left = position.left + "px"; frame.setAttribute("data-placement", position.placement); } }, 100); }); })();