diff --git a/static/css/style.css b/static/css/style.css index 436f58a..8e3c2fc 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -83,7 +83,8 @@ h1, h2 { } label { - display: block; + display: flex; + align-items: center; margin-bottom: 5px; font-weight: bold; color: var(--text-tertiary); @@ -164,7 +165,6 @@ button.loading:hover { padding: 20px; background: var(--section-bg); border-radius: 10px; - border-left: 5px solid #667eea; transition: background 0.3s ease; } @@ -761,7 +761,7 @@ input[readonly] { top: 20px; right: 20px; background: rgba(255, 255, 255, 0.95); - border: 3px solid #764ba2; + border: 1px solid #764ba2; border-radius: 50%; width: 50px; height: 50px; @@ -779,7 +779,7 @@ input[readonly] { .dark-mode #darkModeToggle { background: rgba(39, 39, 42, 0.95); - border: 3px solid #ffffff; + border: 1px solid #ffffff; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(255, 255, 255, 0.2); } @@ -848,3 +848,204 @@ input[readonly] { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } } + +/* Modal Styles */ +.modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 2000; + justify-content: center; + align-items: center; + backdrop-filter: blur(5px); + animation: fadeIn 0.2s ease; +} + +.modal-overlay.active { + display: flex; +} + +.modal-content { + background: var(--container-bg); + border-radius: 15px; + padding: 30px; + max-width: 500px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + position: relative; + animation: slideIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideIn { + from { transform: translateY(-30px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.modal-header h3 { + margin: 0; + color: var(--text-secondary); +} + +.modal-close { + background: transparent; + border: 2px solid var(--text-tertiary); + font-size: 28px; + cursor: pointer; + color: var(--text-tertiary); + padding: 0; + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease; +} + +.modal-close:hover { + background: rgba(102, 126, 234, 0.1); + color: var(--text-tertiary); + border-color: #667eea; +} + +.modal-body { + color: var(--text-color); +} + +.modal-body p { + margin: 15px 0; +} + +.modal-body ul { + margin: 10px 0; + padding-left: 20px; +} + +.modal-body li { + margin: 8px 0; +} + +.help-icon-btn { + background: transparent; + border: 2px solid var(--border-color); + border-radius: 50%; + width: 44px; + height: 44px; + min-width: 44px; + min-height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: var(--text-secondary); + font-size: 24px; /* Ensures the icon remains visually 24px */ + font-weight: bold; + font-family: Arial, sans-serif; + margin-left: 8px; + padding: 0; + line-height: 1; + transition: all 0.2s ease; + flex-shrink: 0; +} + +.help-icon-btn:hover { + background: #667eea; + border-color: #667eea; + color: white; + transform: scale(1.1); +} + +/* Toggleable Section Styles */ +.toggleable-section { + position: relative; + padding: 0; +} + +.section-header { + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + padding: 15px; + margin: 0; + border-radius: 8px; + transition: all 0.2s ease; + width: 100%; + border: 1px solid var(--border-color); + background: var(--section-bg); + text-align: left; + box-shadow: none; +} + +.section-header:hover { + background: rgba(102, 126, 234, 0.05); + border-color: #667eea; + box-shadow: none; +} + +.dark-mode .section-header:hover { + background: rgba(102, 126, 234, 0.1); + border-color: #667eea; + box-shadow: none; +} + +.section-title { + margin: 0; + flex: 1; + pointer-events: none; + font-size: 1.5em; + font-weight: bold; + color: var(--text-secondary); + text-align: center; +} + +.toggle-btn { + background: transparent; + border: none; + font-size: 16px; + cursor: pointer; + padding: 0; + margin-right: 10px; + color: var(--text-secondary); + transition: color 0.2s ease; + line-height: 1; + flex-shrink: 0; + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; + font-family: monospace; +} + +.section-content { + max-height: none; + overflow: hidden; + transition: max-height 0.5s ease, opacity 0.3s ease, padding 0.3s ease; + opacity: 1; + padding: 20px; +} + +.section-content.collapsed { + max-height: 0; + opacity: 0; + padding: 0; +} \ No newline at end of file diff --git a/static/js/modal.js b/static/js/modal.js new file mode 100644 index 0000000..2673515 --- /dev/null +++ b/static/js/modal.js @@ -0,0 +1,72 @@ +/** + * Modal functionality for help dialogs + */ + +function openModal(modalId) { + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.add('active'); + document.body.style.overflow = 'hidden'; // Prevent background scrolling + } +} + +function closeModal(modalId) { + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.remove('active'); + document.body.style.overflow = ''; // Restore scrolling + } +} + +// Initialize modal listeners when DOM is ready +function initializeModals() { + // Open location help modal when button is clicked + const locationHelpBtn = document.getElementById('locationHelpBtn'); + if (locationHelpBtn) { + locationHelpBtn.addEventListener('click', function() { + openModal('locationHelpModal'); + }); + } + + // Close modal when clicking on overlay (outside modal content) + const overlays = document.querySelectorAll('.modal-overlay'); + overlays.forEach(overlay => { + overlay.addEventListener('click', function(e) { + if (e.target === this) { + closeModal(this.id); + } + }); + }); + + // Close modal when clicking close button + const closeButtons = document.querySelectorAll('.modal-close'); + closeButtons.forEach(button => { + button.addEventListener('click', function() { + const modal = this.closest('.modal-overlay'); + if (modal) { + closeModal(modal.id); + } + }); + }); + + // Close modal on Escape key (remove old listener first to prevent duplicates) + document.removeEventListener('keydown', handleEscapeKey); + document.addEventListener('keydown', handleEscapeKey); +} + +// Named function for Escape key handling to allow proper cleanup +function handleEscapeKey(e) { + if (e.key === 'Escape') { + const activeModal = document.querySelector('.modal-overlay.active'); + if (activeModal) { + closeModal(activeModal.id); + } + } +} + +// Initialize when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeModals); +} else { + initializeModals(); +} diff --git a/static/js/section-toggle.js b/static/js/section-toggle.js new file mode 100644 index 0000000..44fbbf5 --- /dev/null +++ b/static/js/section-toggle.js @@ -0,0 +1,79 @@ +/** + * Toggle functionality for collapsible sections + */ + +/** + * Update the UI state of a section (expanded or collapsed) + * @param {HTMLElement} content - The content element to toggle + * @param {HTMLElement} toggleIcon - The icon element to update + * @param {HTMLElement} header - The header element to update + * @param {boolean} expanded - Whether the section should be expanded + */ +function updateSectionState(content, toggleIcon, header, expanded) { + if (expanded) { + content.classList.remove('collapsed'); + toggleIcon.classList.remove('collapsed'); + toggleIcon.textContent = 'β–Ό'; + header.setAttribute('aria-expanded', 'true'); + } else { + content.classList.add('collapsed'); + toggleIcon.classList.add('collapsed'); + toggleIcon.textContent = 'β–Ά'; + header.setAttribute('aria-expanded', 'false'); + } +} + +function initializeToggleSections() { + const headers = document.querySelectorAll('.section-header'); + + headers.forEach(header => { + header.addEventListener('click', function(e) { + e.preventDefault(); + + const sectionId = this.getAttribute('data-section'); + const content = document.getElementById(sectionId); + const toggleIcon = this.querySelector('.toggle-btn'); + + // Check if required elements exist before proceeding + if (!content || !toggleIcon) { + console.warn(`Missing elements for section: ${sectionId}`); + return; + } + + const isCollapsed = content.classList.contains('collapsed'); + const shouldExpand = isCollapsed; + + // Update UI state + updateSectionState(content, toggleIcon, this, shouldExpand); + + // Save state to localStorage + localStorage.setItem(`section-${sectionId}`, shouldExpand ? 'expanded' : 'collapsed'); + }); + }); + + // Restore saved states from localStorage + restoreSectionStates(); +} + +function restoreSectionStates() { + const headers = document.querySelectorAll('.section-header'); + + headers.forEach(header => { + const sectionId = header.getAttribute('data-section'); + const savedState = localStorage.getItem(`section-${sectionId}`); + const content = document.getElementById(sectionId); + const toggleIcon = header.querySelector('.toggle-btn'); + + if (savedState === 'expanded' && content && toggleIcon) { + updateSectionState(content, toggleIcon, header, true); + } + // Default is already collapsed, no need to do anything + }); +} + +// Initialize when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeToggleSections); +} else { + initializeToggleSections(); +} diff --git a/templates/full_chart.html b/templates/full_chart.html index 4df3309..a1787f7 100644 --- a/templates/full_chart.html +++ b/templates/full_chart.html @@ -5,6 +5,7 @@ {% block extra_scripts %} + {% if streaming %} @@ -148,8 +149,12 @@

⬆️ Ascendant

-
-

πŸͺ All Planets

+
+ +
-
-

🏠 Houses

+
+ + +