From b3c3f34e501673b5e07a5b2d4c57006cd8020b2a 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 0000000..c8e5162 --- /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 b6fed7d..bae0675 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 1d46577..d859fbc 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 0fb5c27..49dd20d 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}