diff --git a/index.js b/index.js index 5d771fa..afec942 100644 --- a/index.js +++ b/index.js @@ -979,60 +979,141 @@ function addDiceQuickReply() { } /** - * Opens the settings popup. + * SettingsModal - Manages the settings popup modal + * Handles opening, closing, theming, and animations */ -function openSettingsPopup() { - const theme = extensionSettings.theme || 'default'; - $('#rpg-settings-popup').attr('data-theme', theme); - - // Apply custom theme colors if custom theme is selected - if (theme === 'custom') { - applyCustomThemeToSettingsPopup(); +class SettingsModal { + constructor() { + this.modal = document.getElementById('rpg-settings-popup'); + this.content = this.modal?.querySelector('.rpg-settings-popup-content'); + this.isAnimating = false; } - $('#rpg-settings-popup').fadeIn(200); + /** + * Opens the modal with proper animation + */ + open() { + if (this.isAnimating || !this.modal) return; + + // Apply theme + const theme = extensionSettings.theme || 'default'; + this.modal.setAttribute('data-theme', theme); + + // Apply custom theme if needed + if (theme === 'custom') { + this._applyCustomTheme(); + } + + // Open modal with CSS class + this.modal.classList.add('is-open'); + this.modal.classList.remove('is-closing'); + + // Focus management + this.modal.querySelector('#rpg-close-settings')?.focus(); + } + + /** + * Closes the modal with animation + */ + close() { + if (this.isAnimating || !this.modal) return; + + this.isAnimating = true; + this.modal.classList.add('is-closing'); + this.modal.classList.remove('is-open'); + + // Wait for animation to complete + setTimeout(() => { + this.modal.classList.remove('is-closing'); + this.isAnimating = false; + }, 200); + } + + /** + * Updates the theme in real-time (used when theme selector changes) + */ + updateTheme() { + if (!this.modal) return; + + const theme = extensionSettings.theme || 'default'; + this.modal.setAttribute('data-theme', theme); + + if (theme === 'custom') { + this._applyCustomTheme(); + } else { + // Clear custom CSS variables to let theme CSS take over + this._clearCustomTheme(); + } + } + + /** + * Applies custom theme colors + * @private + */ + _applyCustomTheme() { + if (!this.content || !extensionSettings.customColors) return; + + this.content.style.setProperty('--rpg-bg', extensionSettings.customColors.bg); + this.content.style.setProperty('--rpg-accent', extensionSettings.customColors.accent); + this.content.style.setProperty('--rpg-text', extensionSettings.customColors.text); + this.content.style.setProperty('--rpg-highlight', extensionSettings.customColors.highlight); + } + + /** + * Clears custom theme colors + * @private + */ + _clearCustomTheme() { + if (!this.content) return; + + this.content.style.setProperty('--rpg-bg', ''); + this.content.style.setProperty('--rpg-accent', ''); + this.content.style.setProperty('--rpg-text', ''); + this.content.style.setProperty('--rpg-highlight', ''); + } +} + +// Global instance +let settingsModal = null; + +/** + * Opens the settings popup. + * Backwards compatible wrapper for SettingsModal class. + */ +function openSettingsPopup() { + if (settingsModal) { + settingsModal.open(); + } } /** * Closes the settings popup. + * Backwards compatible wrapper for SettingsModal class. */ function closeSettingsPopup() { - $('#rpg-settings-popup').fadeOut(200); + if (settingsModal) { + settingsModal.close(); + } } /** * Applies custom theme colors to the settings popup. + * Backwards compatible wrapper for SettingsModal class. + * @deprecated Use settingsModal.updateTheme() instead */ function applyCustomThemeToSettingsPopup() { - const popup = $('#rpg-settings-popup .rpg-settings-popup-content'); - popup.css({ - '--rpg-bg': extensionSettings.customColors.bg, - '--rpg-accent': extensionSettings.customColors.accent, - '--rpg-text': extensionSettings.customColors.text, - '--rpg-highlight': extensionSettings.customColors.highlight - }); + if (settingsModal) { + settingsModal._applyCustomTheme(); + } } /** * Updates the settings popup theme in real-time. + * Backwards compatible wrapper for SettingsModal class. */ function updateSettingsPopupTheme() { - const theme = extensionSettings.theme || 'default'; - const popup = $('#rpg-settings-popup .rpg-settings-popup-content'); - - $('#rpg-settings-popup').attr('data-theme', theme); - - // Apply custom theme colors if custom theme is selected - if (theme === 'custom') { - applyCustomThemeToSettingsPopup(); - } else { - // Clear custom CSS variables to let theme CSS take over - popup.css({ - '--rpg-bg': '', - '--rpg-accent': '', - '--rpg-text': '', - '--rpg-highlight': '' - }); + if (settingsModal) { + settingsModal.updateTheme(); } } @@ -1040,16 +1121,26 @@ function updateSettingsPopupTheme() { * Sets up the settings popup functionality. */ function setupSettingsPopup() { + // Initialize SettingsModal instance + settingsModal = new SettingsModal(); + // Open settings popup $('#rpg-open-settings').on('click', function() { openSettingsPopup(); }); - // Close settings popup - $('#rpg-close-settings, .rpg-settings-popup-overlay').on('click', function() { + // Close settings popup - close button + $('#rpg-close-settings').on('click', function() { closeSettingsPopup(); }); + // Close on backdrop click (clicking outside content) + $('#rpg-settings-popup').on('click', function(e) { + if (e.target === this) { + closeSettingsPopup(); + } + }); + // Clear cache button $('#rpg-clear-cache').on('click', function() { // Clear the data diff --git a/style.css b/style.css index 70f3d77..8bd7905 100644 --- a/style.css +++ b/style.css @@ -2583,94 +2583,135 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } /* ============================================ - SETTINGS POPUP + SETTINGS MODAL - MOBILE FIRST ============================================ */ + +/* CSS Custom Properties for Responsive Scaling */ +.rpg-settings-popup { + /* Fluid spacing */ + --modal-padding: clamp(0.5rem, 2vw, 0.75rem); + --modal-gap: clamp(0.375rem, 1.5vw, 0.5rem); + --modal-border-width: 2px; + + /* Fluid typography */ + --modal-font-base: clamp(0.8rem, 3vw, 0.9rem); + --modal-font-small: clamp(0.7rem, 2.5vw, 0.8rem); + --modal-font-heading: clamp(0.9rem, 3.5vw, 1rem); + + /* Content constraints - more height than dice roller */ + --modal-max-width: min(90vw, 500px); + --modal-max-height: 75vh; +} + +/* Modal Container - Hidden by default */ .rpg-settings-popup { position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; + inset: 0; z-index: 10000; - display: flex; + display: none; align-items: center; justify-content: center; + padding: 0.5rem; } -.rpg-settings-popup-overlay { +/* Open state */ +.rpg-settings-popup.is-open { + display: flex; + animation: fadeIn 0.2s ease-out; +} + +/* Closing state */ +.rpg-settings-popup.is-closing { + display: flex; + animation: fadeOut 0.2s ease-in; +} + +/* Backdrop overlay - using ::before pseudo-element */ +.rpg-settings-popup::before { + content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); + inset: 0; + background: rgba(0, 0, 0, 0.85); backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); } +/* Modal Content Box */ .rpg-settings-popup-content { position: relative; - background: var(--rpg-bg); - border: 3px solid var(--rpg-border); - border-radius: 0.938em; - max-width: 31.25rem; - width: 90%; - max-height: 80vh; - overflow: hidden; + width: 100%; + max-width: var(--modal-max-width); + height: auto; + max-height: var(--modal-max-height); + min-height: 0; + background: rgba(30, 30, 30, 0.8); + border: var(--modal-border-width) solid var(--rpg-border); + border-radius: 0.5rem; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.9); + color: var(--rpg-text); display: flex; flex-direction: column; - box-shadow: 0 10px 50px rgba(0, 0, 0, 0.9); - animation: popupSlideIn 0.3s ease-out; - color: var(--rpg-text); + overflow: hidden; + animation: slideInUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); + margin: auto 0; } +/* Header */ .rpg-settings-popup-header { display: flex; align-items: center; justify-content: space-between; - padding: 0.938em 1.25em; + padding: var(--modal-padding); background: var(--rpg-accent); - border-bottom: 1px solid var(--rpg-border); + border-bottom: var(--modal-border-width) solid var(--rpg-border); + flex-shrink: 0; } .rpg-settings-popup-header h3 { margin: 0; - font-size: 1.125rem; + font-size: var(--modal-font-heading); color: var(--rpg-highlight); display: flex; align-items: center; - gap: 0.625em; + gap: var(--modal-gap); } +/* Close button - touch-friendly */ .rpg-popup-close { background: transparent; border: none; color: var(--rpg-text); font-size: 1.5rem; cursor: pointer; - padding: 0; - width: 1.875rem; - height: 1.875rem; + padding: 0.5rem; + min-width: 44px; + min-height: 44px; display: flex; align-items: center; justify-content: center; - border-radius: 0.25em; + border-radius: 0.25rem; transition: all 0.2s ease; } -.rpg-popup-close:hover { +.rpg-popup-close:active { background: rgba(255, 255, 255, 0.1); color: var(--rpg-highlight); } +/* Scrollable Body */ .rpg-settings-popup-body { - padding: 1.25em; + padding: var(--modal-padding); overflow-y: auto; - flex: 1; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + flex: 1 1 auto; + min-height: 0; } +/* Settings Groups */ .rpg-settings-group { - margin-bottom: 1.25em; - padding-bottom: 1.25em; + margin-bottom: var(--modal-padding); + padding-bottom: var(--modal-padding); border-bottom: 1px solid var(--rpg-border); } @@ -2679,15 +2720,16 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } .rpg-settings-group h4 { - margin: 0 0 0.938em 0; - font-size: 0.938rem; + margin: 0 0 var(--modal-gap) 0; + font-size: var(--modal-font-base); color: var(--rpg-highlight); display: flex; align-items: center; - gap: 0.5em; + gap: var(--modal-gap); + font-weight: 600; } -/* Apply theme to settings popup */ +/* Theme Support */ #rpg-settings-popup[data-theme="sci-fi"] .rpg-settings-popup-content { --rpg-bg: #0a0e27; --rpg-accent: #1a1f3a; @@ -2712,6 +2754,24 @@ body:has(.rpg-panel.rpg-position-left) #sheld { --rpg-border: #ff00ff; } +/* Desktop Enhancement (1001px+) */ +@media (min-width: 1001px) { + .rpg-settings-popup { + --modal-padding: 1rem; + --modal-gap: 0.75rem; + --modal-font-base: 0.9rem; + --modal-font-small: 0.8rem; + --modal-font-heading: 1.125rem; + --modal-max-width: 600px; + } + + /* Hover effects on desktop */ + .rpg-popup-close:hover { + background: rgba(255, 255, 255, 0.1); + color: var(--rpg-highlight); + } +} + /* ============================================ CHAT THOUGHT OVERLAYS ============================================ */ diff --git a/template.html b/template.html index 4ba4257..6d30c04 100644 --- a/template.html +++ b/template.html @@ -68,17 +68,19 @@ - -