slide show and shortcuts working

This commit is contained in:
Jon
2026-04-29 22:07:16 +01:00
parent fd743e1146
commit 6892738382

View File

@@ -185,51 +185,45 @@
#clock-empty svg { opacity: 0.25; }
#clock-empty p { font-size: 17px; text-align: center; line-height: 1.5; }
/* ─── CRYPTO PANEL ─── */
.crypto-body {
flex: 1;
padding: 0;
.icon-btn.slideshow-active { color: var(--accent2); background: rgba(48,209,88,0.15); }
/* ─── WIDGET PANELS (tabs 2-6): compact bar + full-height widget ─── */
.widget-bar {
display: flex;
flex-direction: column;
overflow: hidden;
min-height: 0;
align-items: center;
justify-content: space-between;
padding: 6px 12px 6px 16px;
flex-shrink: 0;
border-bottom: 1px solid var(--divider);
background: rgba(0,0,0,0.9);
height: 38px;
}
gecko-coin-list-widget {
display: block;
width: 100% !important;
height: 100% !important;
flex: 1;
min-height: 0;
.widget-bar-title {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--subtext);
text-transform: uppercase;
}
/* ─── CHART & HEATMAP PANELS ─── */
.chart-body {
.widget-fill {
flex: 1;
display: flex;
flex-direction: column;
padding: 0;
overflow: hidden;
min-height: 0;
overflow: hidden;
}
gecko-coin-list-widget,
gecko-coin-price-chart-widget,
gecko-coin-heatmap-widget {
display: block;
width: 100% !important;
height: 100% !important;
flex: 1;
min-height: 0;
}
/* Force the inner iframe/shadow content to fill */
gecko-coin-price-chart-widget > *,
gecko-coin-heatmap-widget > *,
gecko-coin-list-widget > * {
width: 100% !important;
height: 100% !important;
}
/* ─── PLACEHOLDER PANELS ─── */
.placeholder-body {
flex: 1;
@@ -357,6 +351,11 @@
<header class="panel-header">
<h1>World Clock</h1>
<div class="header-actions">
<button class="icon-btn" id="slideshow-btn" title="Start slideshow (S)">
<svg id="slideshow-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<polygon points="3,2 14,8 3,14"/>
</svg>
</button>
<button class="icon-btn" id="fs-btn" title="Toggle fullscreen">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/>
@@ -382,56 +381,55 @@
<!-- ══════════════ PANEL 2: CRYPTO ══════════════ -->
<div class="panel" id="panel-2">
<header class="panel-header">
<h1>Crypto</h1>
<div class="header-actions">
<button class="icon-btn fs-all" title="Toggle fullscreen">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/>
</svg>
</button>
</div>
</header>
<div class="crypto-body">
<gecko-coin-list-widget
locale="en"
dark-mode="true"
outlined="true"
coin-ids="bitcoin,ethereum,tether,pax-gold,solana,binancecoin,ripple"
initial-currency="usd">
</gecko-coin-list-widget>
<div class="widget-bar">
<span class="widget-bar-title">Crypto</span>
<button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button>
</div>
<div class="widget-fill">
<gecko-coin-list-widget locale="en" dark-mode="true" outlined="true" coin-ids="bitcoin,ethereum,tether,pax-gold,solana,binancecoin,ripple" initial-currency="usd"></gecko-coin-list-widget>
</div>
</div>
<!-- ══════════════ PANELS 310: PLACEHOLDERS ══════════════ -->
<!-- ══════════════ PANEL 3: HEATMAP ══════════════ -->
<div class="panel" id="panel-3">
<header class="panel-header"><h1>Heatmap</h1><div class="header-actions"><button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button></div></header>
<div class="chart-body">
<div class="widget-bar">
<span class="widget-bar-title">Heatmap</span>
<button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button>
</div>
<div class="widget-fill">
<gecko-coin-heatmap-widget locale="en" dark-mode="true" outlined="true" top="10"></gecko-coin-heatmap-widget>
</div>
</div>
<!-- ══════════════ PANEL 4: BTC CHART ══════════════ -->
<div class="panel" id="panel-4">
<header class="panel-header"><h1>Bitcoin</h1><div class="header-actions"><button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button></div></header>
<div class="chart-body">
<div class="widget-bar">
<span class="widget-bar-title">Bitcoin</span>
<button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button>
</div>
<div class="widget-fill">
<gecko-coin-price-chart-widget locale="en" dark-mode="true" outlined="true" initial-currency="usd"></gecko-coin-price-chart-widget>
</div>
</div>
<!-- ══════════════ PANEL 5: ETH CHART ══════════════ -->
<div class="panel" id="panel-5">
<header class="panel-header"><h1>Ethereum</h1><div class="header-actions"><button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button></div></header>
<div class="chart-body">
<div class="widget-bar">
<span class="widget-bar-title">Ethereum</span>
<button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button>
</div>
<div class="widget-fill">
<gecko-coin-price-chart-widget locale="en" dark-mode="true" outlined="true" coin-id="ethereum" initial-currency="usd"></gecko-coin-price-chart-widget>
</div>
</div>
<!-- ══════════════ PANEL 6: PAX GOLD CHART ══════════════ -->
<div class="panel" id="panel-6">
<header class="panel-header"><h1>PAX Gold</h1><div class="header-actions"><button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button></div></header>
<div class="chart-body">
<div class="widget-bar">
<span class="widget-bar-title">PAX Gold</span>
<button class="icon-btn fs-all" title="Toggle fullscreen"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M1 6V1h5M10 1h5v5M15 10v5h-5M6 15H1v-5"/></svg></button>
</div>
<div class="widget-fill">
<gecko-coin-price-chart-widget locale="en" dark-mode="true" outlined="true" coin-id="pax-gold" initial-currency="usd"></gecko-coin-price-chart-widget>
</div>
</div>
@@ -485,22 +483,30 @@
<script>
// ─── TAB SYSTEM ───────────────────────────────────────────
let activeTab = 1;
let slideshowTimer = null;
const TAB_NAMES = {
1:'World Clock', 2:'Crypto', 3:'Heatmap', 4:'Bitcoin', 5:'Ethereum', 6:'PAX Gold',
6:'Tab 6', 7:'Tab 7', 8:'Tab 8', 9:'Tab 9', 10:'Tab 10'
7:'Tab 7', 8:'Tab 8', 9:'Tab 9', 10:'Tab 10'
};
function switchTab(n) {
function switchTab(n, fromSlideshow = false) {
if (n < 1 || n > 10 || n === activeTab) return;
// Manual switch stops slideshow
if (!fromSlideshow && slideshowTimer) slideshowOff();
document.getElementById(`panel-${activeTab}`)?.classList.remove('active');
document.querySelector(`.tab-btn[data-tab="${activeTab}"]`)?.classList.remove('active');
activeTab = n;
document.getElementById(`panel-${n}`)?.classList.add('active');
document.querySelector(`.tab-btn[data-tab="${n}"]`)?.classList.add('active');
showToast(`<strong>${n === 10 ? '0' : n}</strong> &nbsp;·&nbsp; ${TAB_NAMES[n]}`);
// nudge widgets to re-measure after panel becomes visible
requestAnimationFrame(() => window.dispatchEvent(new Event('resize')));
// Force widgets to fill their container by setting explicit pixel height
requestAnimationFrame(() => {
sizeWidgets();
window.dispatchEvent(new Event('resize'));
// second pass after widget may have initialised
setTimeout(() => { sizeWidgets(); window.dispatchEvent(new Event('resize')); }, 300);
});
}
document.querySelectorAll('.tab-btn').forEach(btn =>
@@ -517,6 +523,7 @@ document.addEventListener('keydown', e => {
if (e.key === '0') { switchTab(10); return; }
if (e.key === 'f' || e.key === 'F') toggleFullscreen();
if (e.key === 'r' || e.key === 'R') { showToast('↻ Refreshing…'); setTimeout(() => location.reload(), 400); }
if (e.key === 's' || e.key === 'S') toggleSlideshow();
});
// ─── TOAST ────────────────────────────────────────────────
@@ -785,8 +792,97 @@ document.getElementById('modal-overlay').addEventListener('click', e => {
});
document.getElementById('modal-search').addEventListener('input', e => renderTZList(e.target.value));
// ─── WIDGET SIZING ────────────────────────────────────────
function sizeWidgets() {
const tabBarH = document.getElementById('tab-bar').offsetHeight;
const viewH = window.innerHeight;
['gecko-coin-list-widget', 'gecko-coin-price-chart-widget', 'gecko-coin-heatmap-widget'].forEach(sel => {
document.querySelectorAll(sel).forEach(el => {
const bar = el.closest('.panel')?.querySelector('.widget-bar');
const barH = bar ? bar.offsetHeight : 0;
const h = viewH - tabBarH - barH;
// Apply to the custom element itself
el.style.cssText += `height:${h}px !important; min-height:${h}px !important; max-height:${h}px !important;`;
el.setAttribute('height', h);
// Apply to any child iframe (gecko widgets render one)
const tryFrame = (root) => {
if (!root) return;
root.querySelectorAll('iframe').forEach(f => {
f.style.height = h + 'px';
f.style.minHeight = h + 'px';
f.height = h;
});
};
tryFrame(el);
tryFrame(el.shadowRoot);
});
});
// Also force widget-fill divs to exact remaining height
document.querySelectorAll('.widget-fill').forEach(fill => {
const bar = fill.closest('.panel')?.querySelector('.widget-bar');
const barH = bar ? bar.offsetHeight : 0;
const h = viewH - tabBarH - barH;
fill.style.height = h + 'px';
fill.style.minHeight = h + 'px';
fill.style.maxHeight = h + 'px';
});
}
window.addEventListener('resize', sizeWidgets);
// Re-run sizeWidgets whenever any new iframe appears inside a gecko widget
// (they load asynchronously after the custom element registers)
const iframeObserver = new MutationObserver(() => sizeWidgets());
document.querySelectorAll('gecko-coin-list-widget, gecko-coin-price-chart-widget, gecko-coin-heatmap-widget').forEach(el => {
iframeObserver.observe(el, { childList: true, subtree: true });
});
// ─── SLIDESHOW ────────────────────────────────────────────
const SLIDESHOW_TABS = [1, 2, 3, 4, 5, 6]; // only populated tabs
const SLIDESHOW_INTERVAL = 10000;
const PLAY_ICON = `<polygon points="3,2 14,8 3,14"/>`;
const PAUSE_ICON = `<rect x="3" y="2" width="4" height="12" rx="1"/><rect x="9" y="2" width="4" height="12" rx="1"/>`;
function slideshowOn() {
const btn = document.getElementById('slideshow-btn');
btn.classList.add('slideshow-active');
btn.title = 'Stop slideshow (S)';
btn.querySelector('svg').innerHTML = PAUSE_ICON;
// advance immediately then every 10s
advanceSlideshow();
slideshowTimer = setInterval(advanceSlideshow, SLIDESHOW_INTERVAL);
}
function slideshowOff() {
clearInterval(slideshowTimer);
slideshowTimer = null;
const btn = document.getElementById('slideshow-btn');
btn.classList.remove('slideshow-active');
btn.title = 'Start slideshow (S)';
btn.querySelector('svg').innerHTML = PLAY_ICON;
}
function toggleSlideshow() {
slideshowTimer ? slideshowOff() : slideshowOn();
}
function advanceSlideshow() {
const idx = SLIDESHOW_TABS.indexOf(activeTab);
const next = SLIDESHOW_TABS[(idx + 1) % SLIDESHOW_TABS.length];
switchTab(next, true);
}
document.getElementById('slideshow-btn').addEventListener('click', toggleSlideshow);
// ─── INIT ─────────────────────────────────────────────────
renderClocks();
sizeWidgets();
setTimeout(sizeWidgets, 200);
setTimeout(sizeWidgets, 600);
setTimeout(sizeWidgets, 1500);
setTimeout(sizeWidgets, 3000);
</script>
</body>
</html>