slide show and shortcuts working
This commit is contained in:
210
dashboard.html
210
dashboard.html
@@ -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 3–10: 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> · ${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>
|
||||
|
||||
Reference in New Issue
Block a user