mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: image full screen overlay for popoups
This commit is contained in:
@@ -3692,6 +3692,93 @@ div.day-empty {
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.popup-media-fullscreenable {
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
|
||||
}
|
||||
.popup-media-fullscreenable:hover {
|
||||
opacity: 0.95;
|
||||
}
|
||||
.popup-media-fullscreenable:focus {
|
||||
outline: none;
|
||||
}
|
||||
.popup-media-fullscreenable:focus-visible {
|
||||
outline: 2px solid color-mix(in srgb, var(--text-primary) 70%, transparent);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2147483646;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: clamp(20px, 4vw, 48px);
|
||||
box-sizing: border-box;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
backdrop-filter: blur(6px);
|
||||
opacity: 0;
|
||||
transition: opacity 0.28s cubic-bezier(0.22, 0.03, 0.26, 1);
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-backdrop.bsplus-popup-media-overlay-backdrop--visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-backdrop.bsplus-popup-media-overlay--instant {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
max-width: min(96vw, 1320px);
|
||||
max-height: calc(100vh - clamp(40px, 10vw, 96px));
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
background: var(--background-primary);
|
||||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.35);
|
||||
opacity: 0;
|
||||
transform: scale(0.94) translateY(12px);
|
||||
transition:
|
||||
opacity 0.28s cubic-bezier(0.22, 0.03, 0.26, 1),
|
||||
transform 0.28s cubic-bezier(0.22, 0.03, 0.26, 1);
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-backdrop.bsplus-popup-media-overlay-backdrop--visible
|
||||
.bsplus-popup-media-overlay-inner {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-backdrop.bsplus-popup-media-overlay--instant
|
||||
.bsplus-popup-media-overlay-inner {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-slot {
|
||||
width: 100%;
|
||||
max-height: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: clamp(16px, 3vw, 28px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bsplus-popup-media-overlay-media {
|
||||
max-width: 100%;
|
||||
max-height: calc(100vh - clamp(120px, 22vh, 200px));
|
||||
width: auto;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -1000px 0;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import stringToHTML from "../stringToHTML";
|
||||
import { settingsState } from "../listeners/SettingsState";
|
||||
import { openPopup } from "./PopupManager";
|
||||
import { attachPopupMediaFullscreenIfPresent } from "./attachPopupMediaFullscreen";
|
||||
|
||||
/** Same hosting pattern as the privacy statement branding images (avoids page-relative extension URLs on Engage). */
|
||||
const ENGAGE_PROMO_IMG_URL =
|
||||
@@ -49,6 +50,8 @@ export function showEngageParentsAnnouncement(onDismissed?: () => void) {
|
||||
</div>
|
||||
`).firstChild as HTMLElement;
|
||||
|
||||
attachPopupMediaFullscreenIfPresent(text, ".engageParentsPromoImg");
|
||||
|
||||
settingsState.engageParentsAnnouncementShown = true;
|
||||
|
||||
openPopup({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import stringToHTML from "../stringToHTML";
|
||||
import { settingsState } from "../listeners/SettingsState";
|
||||
import { openPopup } from "./PopupManager";
|
||||
import { attachPopupMediaFullscreenIfPresent } from "./attachPopupMediaFullscreen";
|
||||
|
||||
const PRIVACY_STATEMENT_VERSION = "2025-12-19";
|
||||
|
||||
@@ -59,6 +60,8 @@ export function showPrivacyNotification(onDismissed?: () => void) {
|
||||
</div>
|
||||
`).firstChild as HTMLElement;
|
||||
|
||||
attachPopupMediaFullscreenIfPresent(text, "img.aboutImg");
|
||||
|
||||
settingsState.privacyStatementLastUpdated = "2025-12-20";
|
||||
settingsState.privacyStatementShown = true;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import stringToHTML from "../stringToHTML";
|
||||
import browser from "webextension-polyfill";
|
||||
import kofi from "@/resources/kofi.png?base64";
|
||||
import { openPopup } from "./PopupManager";
|
||||
import { attachPopupMediaFullscreen } from "./attachPopupMediaFullscreen";
|
||||
|
||||
export function OpenWhatsNewPopup(onDismissed?: () => void) {
|
||||
const header = stringToHTML(
|
||||
@@ -28,6 +29,7 @@ export function OpenWhatsNewPopup(onDismissed?: () => void) {
|
||||
video.appendChild(source);
|
||||
video.classList.add("whatsnewImg");
|
||||
imageContainer.appendChild(video);
|
||||
attachPopupMediaFullscreen(video);
|
||||
|
||||
const text = stringToHTML(/* html */ `
|
||||
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: auto;">
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Makes popup hero images/videos open a padded overlay (not browser fullscreen) on click.
|
||||
* Escape or backdrop click dismisses it. Clicks use stopPropagation so the
|
||||
* parent SEQTA popup does not close.
|
||||
*/
|
||||
|
||||
import { settingsState } from "../listeners/SettingsState";
|
||||
|
||||
const FULLSCREENABLE_CLASS = "popup-media-fullscreenable";
|
||||
const OVERLAY_VISIBLE_CLASS = "bsplus-popup-media-overlay-backdrop--visible";
|
||||
const OVERLAY_ANIM_MS = 280;
|
||||
|
||||
function isImageOrVideo(el: Element): el is HTMLImageElement | HTMLVideoElement {
|
||||
return el instanceof HTMLImageElement || el instanceof HTMLVideoElement;
|
||||
}
|
||||
|
||||
export function attachPopupMediaFullscreen(el: HTMLImageElement | HTMLVideoElement) {
|
||||
el.classList.add(FULLSCREENABLE_CLASS);
|
||||
el.setAttribute("tabindex", "0");
|
||||
el.setAttribute("role", "button");
|
||||
el.setAttribute("aria-label", "View larger");
|
||||
el.title = "Click to view larger";
|
||||
|
||||
const open = (e: Event) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openMediaOverlayViewer(el);
|
||||
};
|
||||
|
||||
el.addEventListener("click", open);
|
||||
el.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
open(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openMediaOverlayViewer(source: HTMLImageElement | HTMLVideoElement) {
|
||||
const backdrop = document.createElement("div");
|
||||
backdrop.id = "bsplus-popup-media-overlay";
|
||||
backdrop.className = "bsplus-popup-media-overlay-backdrop";
|
||||
|
||||
const inner = document.createElement("div");
|
||||
inner.className = "bsplus-popup-media-overlay-inner";
|
||||
|
||||
const slot = document.createElement("div");
|
||||
slot.className = "bsplus-popup-media-overlay-slot";
|
||||
|
||||
let media: HTMLImageElement | HTMLVideoElement;
|
||||
if (source instanceof HTMLVideoElement) {
|
||||
const v = source;
|
||||
const nv = document.createElement("video");
|
||||
nv.classList.add("bsplus-popup-media-overlay-media");
|
||||
nv.controls = true;
|
||||
nv.playsInline = true;
|
||||
nv.loop = v.loop;
|
||||
nv.muted = v.muted;
|
||||
nv.volume = v.volume;
|
||||
for (const s of v.querySelectorAll("source")) {
|
||||
const ns = document.createElement("source");
|
||||
ns.src = (s as HTMLSourceElement).src;
|
||||
const t = (s as HTMLSourceElement).type;
|
||||
if (t) ns.type = t;
|
||||
nv.appendChild(ns);
|
||||
}
|
||||
nv.addEventListener(
|
||||
"loadeddata",
|
||||
() => {
|
||||
try {
|
||||
nv.currentTime = v.currentTime;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
void nv.play().catch(() => {});
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
v.pause();
|
||||
nv.load();
|
||||
media = nv;
|
||||
} else {
|
||||
const img = document.createElement("img");
|
||||
img.classList.add("bsplus-popup-media-overlay-media");
|
||||
img.src = source.currentSrc || source.src;
|
||||
img.alt = source.alt || "";
|
||||
media = img;
|
||||
}
|
||||
|
||||
media.addEventListener("click", (e) => e.stopPropagation());
|
||||
|
||||
slot.appendChild(media);
|
||||
inner.append(slot);
|
||||
backdrop.appendChild(inner);
|
||||
document.body.append(backdrop);
|
||||
|
||||
if (!settingsState.animations) {
|
||||
backdrop.classList.add("bsplus-popup-media-overlay--instant");
|
||||
backdrop.classList.add(OVERLAY_VISIBLE_CLASS);
|
||||
} else {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
backdrop.classList.add(OVERLAY_VISIBLE_CLASS);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
inner.addEventListener("click", (e) => e.stopPropagation());
|
||||
|
||||
let done = false;
|
||||
const removeOverlay = () => {
|
||||
if (source instanceof HTMLVideoElement && media instanceof HTMLVideoElement) {
|
||||
try {
|
||||
source.currentTime = media.currentTime;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
void source.play().catch(() => {});
|
||||
}
|
||||
backdrop.remove();
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
document.removeEventListener("keydown", onDocKey, true);
|
||||
|
||||
if (!settingsState.animations) {
|
||||
removeOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
backdrop.classList.remove(OVERLAY_VISIBLE_CLASS);
|
||||
window.setTimeout(removeOverlay, OVERLAY_ANIM_MS);
|
||||
};
|
||||
|
||||
const onDocKey = (ev: KeyboardEvent) => {
|
||||
if (ev.key === "Escape") {
|
||||
ev.stopPropagation();
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", onDocKey, true);
|
||||
|
||||
backdrop.addEventListener("click", () => {
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
export function attachPopupMediaFullscreenIfPresent(
|
||||
root: ParentNode,
|
||||
selector: string,
|
||||
) {
|
||||
const el = root.querySelector(selector);
|
||||
if (el && isImageOrVideo(el)) {
|
||||
attachPopupMediaFullscreen(el);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user