From 896a99382d460af8cccc83f5027d830fcdcffb33 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Tue, 10 Feb 2026 21:15:50 +0100 Subject: [PATCH] refactor: extract PipImage component from inline PiP markup Deduplicates mobile PiP image code shared between the rosary page and StickyImage. Adds fullscreen support to StickyImage and fixes hidden PiP elements capturing pointer events via pointer-events: none default. --- src/lib/components/PipImage.svelte | 119 ++++++++++++++++++ src/lib/components/StickyImage.svelte | 79 +++--------- src/lib/js/pip.svelte.ts | 7 +- .../[rosary=rosaryLang]/+page.svelte | 111 +--------------- 4 files changed, 141 insertions(+), 175 deletions(-) create mode 100644 src/lib/components/PipImage.svelte diff --git a/src/lib/components/PipImage.svelte b/src/lib/components/PipImage.svelte new file mode 100644 index 00000000..c8e51620 --- /dev/null +++ b/src/lib/components/PipImage.svelte @@ -0,0 +1,119 @@ + + + +
+ {#if src} + + {/if} + {#if pip.showControls} + + {/if} +
+ + diff --git a/src/lib/components/StickyImage.svelte b/src/lib/components/StickyImage.svelte index b6fed7d7..bae06758 100644 --- a/src/lib/components/StickyImage.svelte +++ b/src/lib/components/StickyImage.svelte @@ -1,6 +1,7 @@
- -
+
+
{@render children()}
@@ -107,32 +101,8 @@ .sticky-image-layout.overlay { display: contents; } -.image-wrap { - position: fixed; - top: 0; - left: 0; - z-index: 10000; - width: auto; - opacity: 0; - touch-action: none; - cursor: grab; - user-select: none; - transition: opacity 0.25s ease; -} -.image-wrap:active { - cursor: grabbing; -} -.image-wrap img { - height: 25vh; - width: auto; - object-fit: contain; - border-radius: 6px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); - pointer-events: none; - transition: height 0.25s ease; -} -.image-wrap.enlarged img { - height: 37.5vh; +.image-wrap-desktop { + display: none; } .content-scroll { width: 100%; @@ -148,29 +118,18 @@ gap: 2rem; width: calc(100% + 25vw + 2rem); } - .overlay .image-wrap { + .image-wrap-desktop { + display: block; position: sticky; top: 4rem; - left: auto; - transform: none !important; - width: auto; align-self: start; order: 1; - opacity: 1; - z-index: auto; - cursor: default; - touch-action: auto; - user-select: auto; - transition: none; } - .overlay .image-wrap img { + .overlay .image-wrap-desktop img { height: auto; max-height: calc(100vh - 5rem); width: auto; max-width: 25vw; - border-radius: 0; - box-shadow: none; - pointer-events: auto; } .sticky-image-layout:not(.overlay) { flex-direction: row; @@ -180,37 +139,27 @@ .sticky-image-layout:not(.overlay) .content-scroll { flex: 0 1 700px; } - .sticky-image-layout:not(.overlay) .image-wrap { + .sticky-image-layout:not(.overlay) .image-wrap-desktop { + display: block; position: sticky; top: 4rem; - left: auto; - transform: none !important; - opacity: 1; flex: 1; - background-color: transparent; - padding: 0; order: 1; - cursor: default; - touch-action: auto; - user-select: auto; - transition: none; } - .sticky-image-layout:not(.overlay) .image-wrap img { + .sticky-image-layout:not(.overlay) .image-wrap-desktop img { max-height: calc(100vh - 4rem); height: auto; width: 100%; object-fit: contain; - border-radius: 0; - box-shadow: none; } } @media (prefers-color-scheme: light) { - .sticky-image-layout:not(.overlay) .image-wrap { + .sticky-image-layout:not(.overlay) .image-wrap-desktop { background-color: var(--nord5); } } @media (prefers-color-scheme: light) and (min-width: 1024px) { - .sticky-image-layout:not(.overlay) .image-wrap { + .sticky-image-layout:not(.overlay) .image-wrap-desktop { background-color: transparent; } } diff --git a/src/lib/js/pip.svelte.ts b/src/lib/js/pip.svelte.ts index 1d465777..d859fbc5 100644 --- a/src/lib/js/pip.svelte.ts +++ b/src/lib/js/pip.svelte.ts @@ -155,12 +155,14 @@ export function createPip(opts: PipOptions = {}) { showControls = false; if (fullscreen) { target.style.opacity = '1'; + target.style.pointerEvents = 'auto'; return; } const pos = getCornerPos(corner, target); dragPos = pos; target.style.transform = `translate(${pos.x}px, ${pos.y}px)`; target.style.opacity = '1'; + target.style.pointerEvents = 'auto'; } function hide() { @@ -178,7 +180,10 @@ export function createPip(opts: PipOptions = {}) { el.style.transition = ''; } } - if (el) el.style.opacity = '0'; + if (el) { + el.style.opacity = '0'; + el.style.pointerEvents = 'none'; + } } function reposition() { diff --git a/src/routes/[faithLang=faithLang]/[rosary=rosaryLang]/+page.svelte b/src/routes/[faithLang=faithLang]/[rosary=rosaryLang]/+page.svelte index 0fb5c274..49dd20dc 100644 --- a/src/routes/[faithLang=faithLang]/[rosary=rosaryLang]/+page.svelte +++ b/src/routes/[faithLang=faithLang]/[rosary=rosaryLang]/+page.svelte @@ -2,6 +2,7 @@ import { onMount, tick } from "svelte"; import { createLanguageContext } from "$lib/contexts/languageContext.js"; import { createPip } from "$lib/js/pip.svelte"; +import PipImage from "$lib/components/PipImage.svelte"; import "$lib/css/christ.css"; import "$lib/css/action_button.css"; import Kreuzzeichen from "$lib/components/prayers/Kreuzzeichen.svelte"; @@ -1462,86 +1463,6 @@ h1 { } } -/* Mobile PiP for mystery images */ -.mystery-pip { - position: fixed; - top: 0; - left: 0; - z-index: 10000; - opacity: 0; - touch-action: none; - cursor: grab; - user-select: none; - transition: opacity 0.25s ease; -} -.mystery-pip:active { - cursor: grabbing; -} -.mystery-pip img { - height: 25vh; - width: auto; - object-fit: contain; - border-radius: 6px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); - pointer-events: none; - transition: height 0.25s ease; -} -.mystery-pip.enlarged img { - height: 37.5vh; -} -.mystery-pip.fullscreen { - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 0.95); - display: flex; - align-items: center; - justify-content: center; - cursor: default; -} -.mystery-pip.fullscreen img { - border-radius: 0; - box-shadow: none; -} -.pip-fullscreen-btn { - all: unset; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: transparent; - filter: drop-shadow(0 0 1px black); - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - padding: 0; - z-index: 1; - pointer-events: auto; - outline: none; - transition: transform 0.15s ease; -} -.pip-fullscreen-btn:hover, -.pip-fullscreen-btn:active { - transform: translate(-50%, -50%) scale(1.2); -} -.mystery-pip.fullscreen .pip-fullscreen-btn { - top: auto; - left: auto; - bottom: 10vw; - right: 10vw; - transform: none; -} -.mystery-pip.fullscreen .pip-fullscreen-btn:hover, -.mystery-pip.fullscreen .pip-fullscreen-btn:active { - transform: scale(0.85); -} -@media (min-width: 1200px) { - .mystery-pip { - display: none; - } -} {labels.pageTitle} @@ -1957,35 +1878,7 @@ h1 { {#if hasMysteryImages} - -
- {#if lastPipSrc} - pip.reposition()}> - {/if} - {#if pip.showControls} - - {/if} -
+ pip.reposition()} bind:el={rosaryPipEl} /> {/if}