rosary: add show/hide images toggle, fix PiP timing and breakpoint

- Add "Bilder anzeigen" / "Show Images" toggle persisted to localStorage
- Bump mystery image column/PiP breakpoint from 900px to 1200px so
  prayers keep full width on medium screens
- Fix PiP not appearing on page load by splitting $effect and using
  tick() to wait for DOM before measuring element dimensions
- Fix Toggle checkbox default margin causing misalignment
This commit is contained in:
2026-02-09 09:12:32 +01:00
parent a4738134fe
commit a5e119f976
2 changed files with 33 additions and 8 deletions

View File

@@ -34,6 +34,7 @@
.toggle-wrapper input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
margin: 0;
width: 44px;
height: 24px;
background: var(--nord2);

View File

@@ -1,5 +1,5 @@
<script>
import { onMount } from "svelte";
import { onMount, tick } from "svelte";
import { createLanguageContext } from "$lib/contexts/languageContext.js";
import { createPip } from "$lib/js/pip.svelte";
import "$lib/css/christ.css";
@@ -183,6 +183,9 @@ const mysteryTitlesEnglish = {
// Toggle for including Luminous mysteries (initialized from URL param or default)
let includeLuminous = $state(data.initialLuminous);
// Toggle for showing mystery images
let showImages = $state(true);
// Flag to prevent saving before we've loaded from localStorage
let hasLoadedFromStorage = false;
@@ -208,6 +211,7 @@ const labels = $derived({
glorious: isEnglish ? 'Glorious' : 'Glorreichen',
luminous: isEnglish ? 'Luminous' : 'Lichtreichen',
includeLuminous: isEnglish ? 'Include Luminous Mysteries' : 'Lichtreiche Geheimnisse einbeziehen',
showImages: isEnglish ? 'Show Images' : 'Bilder anzeigen',
beginning: isEnglish ? 'Beginning' : 'Anfang',
signOfCross: isEnglish ? '♱ Sign of the Cross' : '♱ Das Kreuzzeichen',
ourFather: isEnglish ? 'Our Father' : 'Vater unser',
@@ -230,12 +234,17 @@ const labels = $derived({
mysteryLove: isEnglish ? 'Jesus, who may kindle our love' : 'Jesus, der in uns die Liebe entzünde'
});
// Save luminous toggle state to localStorage whenever it changes (but only after initial load)
// Save toggle states to localStorage whenever they change (but only after initial load)
$effect(() => {
if (typeof localStorage !== 'undefined' && hasLoadedFromStorage) {
localStorage.setItem('rosary_includeLuminous', includeLuminous.toString());
}
});
$effect(() => {
if (typeof localStorage !== 'undefined' && hasLoadedFromStorage) {
localStorage.setItem('rosary_showImages', showImages.toString());
}
});
// Function to get the appropriate mystery for a given weekday
function getMysteryForWeekday(date, includeLuminous) {
@@ -315,7 +324,7 @@ let sectionElements = {};
let svgContainer;
// Whether the rosary has mystery images (stable, doesn't change during scroll)
const hasMysteryImages = $derived(selectedMystery === 'schmerzhaften');
const hasMysteryImages = $derived(showImages && selectedMystery === 'schmerzhaften');
// Mystery image scroll target based on active section
function getMysteryScrollTarget(section) {
@@ -359,14 +368,20 @@ let rosaryPipEl = $state(null);
let lastPipSrc = $state(null);
function isMobilePip() {
return !window.matchMedia('(min-width: 900px)').matches;
return !window.matchMedia('(min-width: 1200px)').matches;
}
$effect(() => {
if (mysteryPipSrc) lastPipSrc = mysteryPipSrc;
});
$effect(() => {
if (!rosaryPipEl || !isMobilePip()) return;
if (mysteryPipSrc) {
pip.show(rosaryPipEl);
// Wait for DOM update so the <img> has rendered with dimensions
tick().then(() => {
if (rosaryPipEl) pip.show(rosaryPipEl);
});
} else {
pip.hide();
}
@@ -489,13 +504,17 @@ for (let d = 1; d < 5; d++) {
const pos = sectionPositions;
onMount(() => {
// Load toggle state from localStorage only if not overridden by URL params
// Load toggle states from localStorage only if not overridden by URL params
if (!data.hasUrlLuminous) {
const savedIncludeLuminous = localStorage.getItem('rosary_includeLuminous');
if (savedIncludeLuminous !== null) {
includeLuminous = savedIncludeLuminous === 'true';
}
}
const savedShowImages = localStorage.getItem('rosary_showImages');
if (savedShowImages !== null) {
showImages = savedShowImages === 'true';
}
// If no mystery was specified in URL, recompute based on loaded preferences
if (!data.hasUrlMystery) {
@@ -1387,7 +1406,7 @@ h1 {
display: none;
}
@media (min-width: 900px) {
@media (min-width: 1200px) {
.rosary-layout.has-mystery-image {
grid-template-columns: clamp(250px, 30vw, 400px) 1fr auto;
}
@@ -1445,7 +1464,7 @@ h1 {
.mystery-pip.enlarged img {
height: 37.5vh;
}
@media (min-width: 900px) {
@media (min-width: 1200px) {
.mystery-pip {
display: none;
}
@@ -1530,6 +1549,11 @@ h1 {
href={luminousToggleHref}
/>
<Toggle
bind:checked={showImages}
label={labels.showImages}
/>
<!-- Language Toggle (link for no-JS, enhanced with onclick for JS) -->
<LanguageToggle
initialLatin={data.initialLatin}