diff --git a/index.js b/index.js index 1b1f914..a0b696d 100644 --- a/index.js +++ b/index.js @@ -100,6 +100,10 @@ import { setupMobileKeyboardHandling, setupContentEditableScrolling } from './src/systems/ui/mobile.js'; +import { + setupDesktopTabs, + removeDesktopTabs +} from './src/systems/ui/desktop.js'; // Feature modules import { setupPlotButtons, sendPlotProgression } from './src/systems/features/plotProgression.js'; @@ -368,6 +372,11 @@ async function initUI() { // Setup mobile toggle button setupMobileToggle(); + // Setup desktop tabs (only on desktop viewport) + if (window.innerWidth > 1000) { + setupDesktopTabs(); + } + // Setup collapse/expand toggle button setupCollapseToggle(); diff --git a/src/systems/ui/desktop.js b/src/systems/ui/desktop.js new file mode 100644 index 0000000..2b8e2a4 --- /dev/null +++ b/src/systems/ui/desktop.js @@ -0,0 +1,139 @@ +/** + * Desktop UI Module + * Handles desktop-specific UI functionality: tab navigation + */ + +/** + * Sets up desktop tab navigation for organizing content. + * Only runs on desktop viewports (>1000px). + * Creates two tabs: Status (Stats/Info/Thoughts) and Inventory. + */ +export function setupDesktopTabs() { + const isDesktop = window.innerWidth > 1000; + if (!isDesktop) return; + + // Check if tabs already exist + if ($('.rpg-tabs-nav').length > 0) return; + + const $contentBox = $('.rpg-content-box'); + + // Get existing sections + const $userStats = $('#rpg-user-stats'); + const $infoBox = $('#rpg-info-box'); + const $thoughts = $('#rpg-thoughts'); + const $inventory = $('#rpg-inventory'); + + // If no sections exist, nothing to organize + if ($userStats.length === 0 && $infoBox.length === 0 && $thoughts.length === 0 && $inventory.length === 0) { + return; + } + + // Create tab navigation + const $tabNav = $(` +
+ + +
+ `); + + // Create tab content containers + const $statusTab = $('
'); + const $inventoryTab = $('
'); + + // Move sections into their respective tabs (detach to preserve event handlers) + if ($userStats.length > 0) { + $statusTab.append($userStats.detach()); + $userStats.show(); + } + if ($infoBox.length > 0) { + $statusTab.append($infoBox.detach()); + $infoBox.show(); + } + if ($thoughts.length > 0) { + $statusTab.append($thoughts.detach()); + $thoughts.show(); + } + if ($inventory.length > 0) { + $inventoryTab.append($inventory.detach()); + $inventory.show(); + } + + // Hide dividers on desktop tabs (tabs separate content naturally) + $('.rpg-divider').hide(); + + // Build desktop tab structure + const $tabsContainer = $('
'); + $tabsContainer.append($tabNav); + $tabsContainer.append($statusTab); + $tabsContainer.append($inventoryTab); + + // Replace content box with tabs container + $contentBox.html('').append($tabsContainer); + + // Handle tab switching + $tabNav.find('.rpg-tab-btn').on('click', function() { + const tabName = $(this).data('tab'); + + // Update active tab button + $tabNav.find('.rpg-tab-btn').removeClass('active'); + $(this).addClass('active'); + + // Update active tab content + $('.rpg-tab-content').removeClass('active'); + $(`.rpg-tab-content[data-tab-content="${tabName}"]`).addClass('active'); + }); + + console.log('[RPG Desktop] Desktop tabs initialized'); +} + +/** + * Removes desktop tab navigation and restores original layout. + * Used when transitioning from desktop to mobile. + */ +export function removeDesktopTabs() { + // Get sections from tabs before removing + const $userStats = $('#rpg-user-stats').detach(); + const $infoBox = $('#rpg-info-box').detach(); + const $thoughts = $('#rpg-thoughts').detach(); + const $inventory = $('#rpg-inventory').detach(); + + // Remove tabs container + $('.rpg-tabs-container').remove(); + + // Get dividers + const $dividerStats = $('#rpg-divider-stats'); + const $dividerInfo = $('#rpg-divider-info'); + const $dividerThoughts = $('#rpg-divider-thoughts'); + + // Restore original sections to content box in correct order + const $contentBox = $('.rpg-content-box'); + + // Re-insert sections in original order: User Stats, Info Box, Thoughts, Inventory + if ($dividerStats.length) { + $dividerStats.before($userStats); + $dividerInfo.before($infoBox); + $dividerThoughts.before($thoughts); + $contentBox.append($inventory); + } else { + // Fallback if dividers don't exist + $contentBox.append($userStats); + $contentBox.append($infoBox); + $contentBox.append($thoughts); + $contentBox.append($inventory); + } + + // Show sections and dividers + $userStats.show(); + $infoBox.show(); + $thoughts.show(); + $inventory.show(); + $('.rpg-divider').show(); + + console.log('[RPG Desktop] Desktop tabs removed'); +} diff --git a/src/systems/ui/mobile.js b/src/systems/ui/mobile.js index d109995..f2648b3 100644 --- a/src/systems/ui/mobile.js +++ b/src/systems/ui/mobile.js @@ -6,6 +6,7 @@ import { extensionSettings } from '../../core/state.js'; import { saveSettings } from '../../core/persistence.js'; import { closeMobilePanelWithAnimation, updateCollapseToggleIcon } from './layout.js'; +import { setupDesktopTabs, removeDesktopTabs } from './desktop.js'; /** * Sets up the mobile toggle button (FAB) with drag functionality. @@ -331,6 +332,9 @@ export function setupMobileToggle() { if (!wasMobile && isMobile) { console.log('[RPG Mobile] Transitioning desktop -> mobile'); + // Remove desktop tabs first + removeDesktopTabs(); + // Remove desktop positioning classes $panel.removeClass('rpg-position-right rpg-position-left rpg-position-top'); @@ -384,6 +388,9 @@ export function setupMobileToggle() { // Remove mobile tabs structure removeMobileTabs(); + // Setup desktop tabs + setupDesktopTabs(); + // Force reflow to apply position instantly $panel[0].offsetHeight; @@ -519,54 +526,59 @@ export function setupMobileTabs() { return; } - // Create tab navigation (only show tabs for sections that exist) + // Create tab navigation (3 tabs for mobile) const tabs = []; - const hasInfoOrCharacters = $infoBox.length > 0 || $thoughts.length > 0; - const hasStatsOrInventory = $userStats.length > 0 || $inventory.length > 0; + const hasStats = $userStats.length > 0; + const hasInfo = $infoBox.length > 0 || $thoughts.length > 0; + const hasInventory = $inventory.length > 0; - if (hasStatsOrInventory) { + // Tab 1: Stats (User Stats only) + if (hasStats) { tabs.push(''); } - // Combine Info and Characters into one tab - if (hasInfoOrCharacters) { - tabs.push(''); + // Tab 2: Info (Info Box + Character Thoughts) + if (hasInfo) { + tabs.push(''); + } + // Tab 3: Inventory + if (hasInventory) { + tabs.push(''); } const $tabNav = $('
' + tabs.join('') + '
'); // Determine which tab should be active let firstTab = ''; - if (hasStatsOrInventory) firstTab = 'stats'; - else if (hasInfoOrCharacters) firstTab = 'info-characters'; + if (hasStats) firstTab = 'stats'; + else if (hasInfo) firstTab = 'info'; + else if (hasInventory) firstTab = 'inventory'; // Create tab content wrappers const $statsTab = $('
'); - const $infoCharactersTab = $('
'); - - // Create combined content wrapper for Info and Characters - const $combinedWrapper = $('
'); + const $infoTab = $('
'); + const $inventoryTab = $('
'); // Move sections into their respective tabs (detach to preserve event handlers) + // Stats tab: User Stats only if ($userStats.length > 0) { $statsTab.append($userStats.detach()); $userStats.show(); } - if ($inventory.length > 0) { - $statsTab.append($inventory.detach()); - $inventory.show(); - } + + // Info tab: Info Box + Character Thoughts if ($infoBox.length > 0) { - $combinedWrapper.append($infoBox.detach()); + $infoTab.append($infoBox.detach()); $infoBox.show(); } if ($thoughts.length > 0) { - $combinedWrapper.append($thoughts.detach()); + $infoTab.append($thoughts.detach()); $thoughts.show(); } - // Add combined wrapper to the info-characters tab - if (hasInfoOrCharacters) { - $infoCharactersTab.append($combinedWrapper); + // Inventory tab: Inventory only + if ($inventory.length > 0) { + $inventoryTab.append($inventory.detach()); + $inventory.show(); } // Hide dividers on mobile @@ -577,8 +589,9 @@ export function setupMobileTabs() { $mobileContainer.append($tabNav); // Only append tab content wrappers that have content - if (hasStatsOrInventory) $mobileContainer.append($statsTab); - if (hasInfoOrCharacters) $mobileContainer.append($infoCharactersTab); + if (hasStats) $mobileContainer.append($statsTab); + if (hasInfo) $mobileContainer.append($infoTab); + if (hasInventory) $mobileContainer.append($inventoryTab); // Insert mobile tab structure at the beginning of content box $contentBox.prepend($mobileContainer); diff --git a/style.css b/style.css index 8ebe3b8..2db615f 100644 --- a/style.css +++ b/style.css @@ -4109,6 +4109,81 @@ body:has(.rpg-panel.rpg-position-left) #sheld { color: white; } +/* ============================================ + DESKTOP TABS SYSTEM + ============================================ */ + +/* Desktop tabs container */ +.rpg-tabs-container { + display: flex; + flex-direction: column; + gap: 0; + height: 100%; + width: 100%; +} + +/* Desktop tab navigation */ +.rpg-tabs-nav { + display: flex; + gap: 0; + background: var(--SmartThemeBlurTintColor); + border-bottom: 2px solid var(--SmartThemeBorderColor); + margin-bottom: 1rem; +} + +/* Desktop tab button */ +.rpg-tab-btn { + flex: 1; + padding: 0.75rem 1rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + font-size: 0.95rem; + font-weight: 500; + color: var(--SmartThemeBodyColor); + background: transparent; + border: none; + border-bottom: 3px solid transparent; + cursor: pointer; + transition: all 0.2s ease; +} + +.rpg-tab-btn:hover { + background: var(--SmartThemeQuoteColor); + color: var(--ac-style-color-matchedText); +} + +.rpg-tab-btn.active { + background: var(--SmartThemeQuoteColor); + border-bottom-color: var(--ac-style-color-matchedText); + color: var(--ac-style-color-matchedText); +} + +.rpg-tab-btn i { + font-size: 1.1rem; +} + +/* Desktop tab content */ +.rpg-tab-content { + display: none; + flex-direction: column; + gap: 1rem; + overflow-y: auto; + overflow-x: hidden; + padding: 0 0.5rem; + flex: 1; +} + +.rpg-tab-content.active { + display: flex; +} + +/* Hide dividers when tabs are active (tabs separate content) */ +.rpg-tabs-container .rpg-divider { + display: none; +} + /* Mobile Responsive Styles */ @media (max-width: 768px) { .rpg-inventory-subtabs {