Files
SignageHTML/public/js/main.js
kyle 60e41c2cc4 Kiosk UI/UX overhaul: dark landscape mode with hero countdowns and full-width layout
Redesign the landscape orientation for kiosk readability at 3-10m distance:

- Add dark kiosk background (#1a1a2e) with high-contrast light text
- Replace 2-column grid with 5-row full-width stacking layout
- Add compact weather bar (temp + sunrise/sunset) replacing full widget
- Enlarge countdown to 2em hero size in landscape
- Replace time ranges with next 2-3 absolute departure times
- Add 3-tier urgency colors: Nu (green), 1-2min (red), 3-5min (orange)
- Make site headers full-width blue gradient bars in landscape
- Tighten card spacing (65px min-height, 8px gap) for 4-stop visibility
- Add scrolling news ticker with /api/ticker fallback messages
- Fix daylight bar from position:fixed to relative in landscape grid
- Hide background overlay in landscape for maximum contrast
- Fix weather-section HTML missing closing div tags

All changes scoped behind body.landscape CSS selectors; other orientations unaffected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 19:12:08 +01:00

107 lines
3.8 KiB
JavaScript

/**
* Main application entry point
* Initializes all components when the DOM is ready
*/
import { Constants } from './utils/constants.js';
import { logger } from './utils/logger.js';
import { ConfigManager } from './components/ConfigManager.js';
import { Clock } from './components/Clock.js';
import { WeatherManager } from './components/WeatherManager.js';
import { DeparturesManager } from './components/DeparturesManager.js';
import { NewsTicker } from './components/NewsTicker.js';
/**
* Function to ensure content wrapper exists for rotated orientations
*/
function ensureContentWrapper() {
if (!document.getElementById('content-wrapper')) {
logger.info('Creating content wrapper');
const wrapper = document.createElement('div');
wrapper.id = 'content-wrapper';
// Move all body children to the wrapper except excluded elements
const excludedElements = ['config-button', 'config-modal', 'background-overlay'];
// Create an array of nodes to move (can't modify while iterating)
const nodesToMove = [];
for (let i = 0; i < document.body.children.length; i++) {
const child = document.body.children[i];
if (!excludedElements.includes(child.id) && child.id !== 'content-wrapper') {
nodesToMove.push(child);
}
}
// Move the nodes to the wrapper
nodesToMove.forEach(node => {
wrapper.appendChild(node);
});
// Add the wrapper back to the body
document.body.appendChild(wrapper);
}
}
// Initialize components when the DOM is loaded
document.addEventListener('DOMContentLoaded', async function() {
logger.info('DOM fully loaded');
try {
// Initialize ConfigManager first
logger.info('Creating ConfigManager...');
window.configManager = new ConfigManager({
defaultOrientation: 'normal',
defaultDarkMode: 'auto'
});
// Initialize Clock
const timezone = Constants.TIMEZONE || 'Europe/Stockholm';
window.clock = new Clock({
elementId: 'clock',
timezone: timezone
});
// Initialize WeatherManager with location from window config or constants
const defaultLat = window.DEFAULT_LOCATION?.latitude ||
Constants.DEFAULT_LOCATION.LATITUDE || 59.3293;
const defaultLon = window.DEFAULT_LOCATION?.longitude ||
Constants.DEFAULT_LOCATION.LONGITUDE || 18.0686;
window.weatherManager = new WeatherManager({
latitude: defaultLat,
longitude: defaultLon
});
// Initialize DeparturesManager
window.departuresManager = new DeparturesManager({
containerId: 'departures',
statusId: 'status',
lastUpdatedId: 'last-updated'
});
// Initialize NewsTicker (visible in landscape mode only via CSS)
window.newsTicker = new NewsTicker();
// Set up event listeners
document.addEventListener('darkModeChanged', event => {
document.body.classList.toggle('dark-mode', event.detail.isDarkMode);
});
document.addEventListener('configChanged', event => {
if (['vertical', 'upsidedown', 'vertical-reverse'].includes(event.detail.config.orientation)) {
ensureContentWrapper();
}
});
// Ensure content wrapper exists initially
ensureContentWrapper();
logger.info('All components initialized successfully');
} catch (error) {
logger.error('Error during initialization:', error);
const errorDiv = document.createElement('div');
errorDiv.className = 'error';
errorDiv.textContent = `Initialization error: ${error.message}`;
document.body.appendChild(errorDiv);
}
});