Complete 3.1.1: Grid Implementation Foundation - Replace flexbox with CSS Grid for theater layout
This commit is contained in:
parent
c48042ea4b
commit
1879c44202
6 changed files with 21 additions and 20 deletions
|
|
@ -1,282 +0,0 @@
|
|||
// UI Controls Module
|
||||
// Handles UI toggle functions, keyboard shortcuts, notifications, and DOM utilities
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
AppModules.require('api');
|
||||
AppModules.register('ui-controls');
|
||||
|
||||
// Toast notification system
|
||||
function showToast(message, duration = AppConfig.ui.toastDuration) {
|
||||
const toast = document.getElementById('toast');
|
||||
if (!toast) return;
|
||||
|
||||
toast.textContent = message;
|
||||
toast.classList.add('show');
|
||||
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
||||
function hideToast() {
|
||||
const toast = document.getElementById('toast');
|
||||
if (toast) {
|
||||
toast.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
// Notification badge management
|
||||
function updateNotificationBadge() {
|
||||
const badge = document.getElementById('notificationBadge');
|
||||
|
||||
if (badge) {
|
||||
if (AppState.unreadCount > 0) {
|
||||
badge.textContent = AppState.unreadCount > 99 ? '99+' : AppState.unreadCount;
|
||||
badge.classList.add('show');
|
||||
} else {
|
||||
badge.classList.remove('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chat toggle functionality
|
||||
function toggleChat() {
|
||||
const chatSection = document.getElementById('chatSection');
|
||||
const videoSection = document.getElementById('videoSection');
|
||||
const toggleBtn = document.getElementById('chatToggleText');
|
||||
|
||||
if (!chatSection || !videoSection || !toggleBtn) return;
|
||||
|
||||
AppState.chatCollapsed = !AppState.chatCollapsed;
|
||||
|
||||
if (AppState.chatCollapsed) {
|
||||
chatSection.classList.add('collapsed');
|
||||
videoSection.classList.add('expanded');
|
||||
toggleBtn.textContent = 'Show Chat';
|
||||
} else {
|
||||
chatSection.classList.remove('collapsed');
|
||||
videoSection.classList.remove('expanded');
|
||||
toggleBtn.textContent = 'Hide Chat';
|
||||
// Clear unread count when reopening
|
||||
AppState.unreadCount = 0;
|
||||
updateNotificationBadge();
|
||||
}
|
||||
}
|
||||
|
||||
// Viewer count update
|
||||
function updateViewerCount(count) {
|
||||
const viewerElement = document.getElementById('viewerCount');
|
||||
if (viewerElement) {
|
||||
viewerElement.textContent = count + (count === 1 ? ' viewer' : ' viewers');
|
||||
}
|
||||
}
|
||||
|
||||
// Connection status dot
|
||||
function updateConnectionStatus(online) {
|
||||
const statusDot = document.getElementById('statusDot');
|
||||
const statusText = document.getElementById('statusText');
|
||||
|
||||
if (statusDot && statusText) {
|
||||
if (online) {
|
||||
statusDot.classList.remove('offline');
|
||||
statusText.textContent = 'Connected';
|
||||
} else {
|
||||
statusDot.classList.add('offline');
|
||||
statusText.textContent = 'Reconnecting...';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard shortcuts handler
|
||||
function handleKeyboardShortcuts(event) {
|
||||
// Skip if typing in input field
|
||||
if (event.target.matches('input, textarea')) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'c':
|
||||
case 'C':
|
||||
// Toggle chat
|
||||
event.preventDefault();
|
||||
toggleChat();
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
// Toggle fullscreen
|
||||
event.preventDefault();
|
||||
toggleFullscreen();
|
||||
break;
|
||||
case '/':
|
||||
// Focus chat input
|
||||
event.preventDefault();
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
if (messageInput) {
|
||||
messageInput.focus();
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
// Toggle picture-in-picture
|
||||
event.preventDefault();
|
||||
togglePictureInPicture();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fullscreen functionality
|
||||
function toggleFullscreen() {
|
||||
if (!AppState.player) return;
|
||||
|
||||
if (AppState.player.isFullscreen()) {
|
||||
AppState.player.exitFullscreen();
|
||||
} else {
|
||||
AppState.player.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
// Picture-in-picture functionality
|
||||
function togglePictureInPicture() {
|
||||
const video = document.getElementById('video-player');
|
||||
if (!video || !video.requestPictureInPicture) return;
|
||||
|
||||
if (document.pictureInPictureElement) {
|
||||
document.exitPictureInPicture();
|
||||
} else {
|
||||
video.requestPictureInPicture();
|
||||
}
|
||||
}
|
||||
|
||||
// Sound notification for new messages
|
||||
function playNotificationSound() {
|
||||
if (!AppState.soundEnabled || !document.hidden) return;
|
||||
|
||||
try {
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.value = 800;
|
||||
oscillator.type = 'sine';
|
||||
gainNode.gain.value = 0.1;
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.1);
|
||||
} catch(e) {
|
||||
AppLogger.log('Audio notification failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile responsive behavior
|
||||
function handleWindowResize() {
|
||||
const isMobile = window.innerWidth <= AppConfig.ui.mobileBreakpoint;
|
||||
|
||||
if (isMobile && !AppState.chatCollapsed) {
|
||||
// Auto-collapse chat on mobile for better viewing
|
||||
toggleChat();
|
||||
}
|
||||
}
|
||||
|
||||
// Page visibility API handler
|
||||
function handleVisibilityChange() {
|
||||
if (!document.hidden && !AppState.chatCollapsed) {
|
||||
// Clear unread count when page becomes visible
|
||||
AppState.unreadCount = 0;
|
||||
updateNotificationBadge();
|
||||
}
|
||||
}
|
||||
|
||||
// DOM utility functions
|
||||
const DOMUtils = {
|
||||
// Add event listener with proper cleanup tracking
|
||||
addEvent: function(element, event, handler) {
|
||||
if (!element) return;
|
||||
|
||||
element.addEventListener(event, handler);
|
||||
|
||||
// Store handler for potential cleanup
|
||||
if (!element._handlers) {
|
||||
element._handlers = new Map();
|
||||
}
|
||||
if (!element._handlers.has(event)) {
|
||||
element._handlers.set(event, []);
|
||||
}
|
||||
element._handlers.get(event).push(handler);
|
||||
},
|
||||
|
||||
// Remove event listeners (for cleanup)
|
||||
removeEvent: function(element, event, handler) {
|
||||
if (!element || !element._handlers || !element._handlers.has(event)) return;
|
||||
|
||||
const handlers = element._handlers.get(event);
|
||||
const index = handlers.indexOf(handler);
|
||||
if (index > -1) {
|
||||
handlers.splice(index, 1);
|
||||
element.removeEventListener(event, handler);
|
||||
}
|
||||
},
|
||||
|
||||
// Get element with optional error logging
|
||||
getElement: function(id, required = false) {
|
||||
const element = document.getElementById(id);
|
||||
if (required && !element) {
|
||||
AppLogger.error(`Required element with id '${id}' not found`);
|
||||
}
|
||||
return element;
|
||||
},
|
||||
|
||||
// Add/remove classes safely
|
||||
addClass: function(element, className) {
|
||||
if (element) element.classList.add(className);
|
||||
},
|
||||
|
||||
removeClass: function(element, className) {
|
||||
if (element) element.classList.remove(className);
|
||||
},
|
||||
|
||||
toggleClass: function(element, className) {
|
||||
if (element) element.classList.toggle(className);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize event listeners
|
||||
function initializeEventListeners() {
|
||||
// Keyboard shortcuts
|
||||
DOMUtils.addEvent(document, 'keydown', handleKeyboardShortcuts);
|
||||
|
||||
// Window resize for mobile responsiveness
|
||||
DOMUtils.addEvent(window, 'resize', handleWindowResize);
|
||||
|
||||
// Page visibility for notification clearing
|
||||
DOMUtils.addEvent(document, 'visibilitychange', handleVisibilityChange);
|
||||
|
||||
AppLogger.log('UI controls event listeners initialized');
|
||||
}
|
||||
|
||||
// Public API
|
||||
window.UIControls = {
|
||||
showToast: showToast,
|
||||
hideToast: hideToast,
|
||||
toggleChat: toggleChat,
|
||||
toggleFullscreen: toggleFullscreen,
|
||||
togglePictureInPicture: togglePictureInPicture,
|
||||
updateViewerCount: updateViewerCount,
|
||||
updateConnectionStatus: updateConnectionStatus,
|
||||
updateNotificationBadge: updateNotificationBadge,
|
||||
playNotificationSound: playNotificationSound,
|
||||
DOMUtils: DOMUtils
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', initializeEventListeners);
|
||||
|
||||
AppLogger.log('UI Controls module loaded');
|
||||
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue