From 2c86af556122204df9326f41d1ae9235b13b1058 Mon Sep 17 00:00:00 2001 From: Lucas 'Paperboy' Rose-Winters Date: Tue, 4 Nov 2025 17:04:23 +1100 Subject: [PATCH] fix: auto-arrange now correctly recalculates widget grid layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root Cause: After auto-arrange reorganized widgets, code attempted to call onResize() for ALL tabs' widgets. However, switchTab() clears the this.widgets Map and only re-renders the current tab. Result: this.widgets.get(widgetId) returned undefined for non-active tabs, so onResize() was never called. Additionally, User Attributes onResize() was receiving grid units (2) instead of pixel width (~290px), causing calculateOptimalColumns() to think only 2 columns would fit when 3 columns could easily fit. The Fix: 1. Iterate over this.widgets Map (currently rendered widgets only) instead of this.dashboard.tabs (includes non-rendered widgets) 2. Use container.offsetWidth (pixel width) in onResize instead of grid units 3. Enable DEBUG flags temporarily to reveal previously suppressed logs Result: - onResize() now called for all visible widgets after auto-arrange - User Attributes correctly maintains 3×3 grid at 2 grid units wide - No more 2×5 layout with orphaned last attribute Fixes: User Attributes breaking into 2×5 grid after auto-arrange --- src/systems/dashboard/dashboardIntegration.js | 1 - src/systems/dashboard/dashboardManager.js | 58 +++++++++++++++++-- src/systems/dashboard/gridEngine.js | 3 +- .../dashboard/widgets/userAttributesWidget.js | 16 ++++- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/systems/dashboard/dashboardIntegration.js b/src/systems/dashboard/dashboardIntegration.js index 8c9aba3..ba99a57 100644 --- a/src/systems/dashboard/dashboardIntegration.js +++ b/src/systems/dashboard/dashboardIntegration.js @@ -296,7 +296,6 @@ function setupDashboardEventListeners(dependencies) { }); if (confirmed) { - console.log('[RPG Companion] Auto-layout button clicked'); dashboardManager.autoLayoutWidgets(); } } diff --git a/src/systems/dashboard/dashboardManager.js b/src/systems/dashboard/dashboardManager.js index 2127cdc..b4d6ece 100644 --- a/src/systems/dashboard/dashboardManager.js +++ b/src/systems/dashboard/dashboardManager.js @@ -14,7 +14,8 @@ */ // Performance: Disable console logging (console.error still active) -const DEBUG = false; +// Temporarily enabled for debugging auto-arrange onResize issue +const DEBUG = true; const console = DEBUG ? window.console : { log: () => {}, warn: () => {}, @@ -130,10 +131,29 @@ export class DashboardManager { if (currentTab && currentTab.widgets && currentTab.widgets.length > 0) { console.log(`[DashboardManager] Auto-reflowing ${currentTab.widgets.length} widgets for ${newCols} columns`); + // Store current widget dimensions before auto-layout + const dimensionsBefore = new Map(); + currentTab.widgets.forEach(widget => { + dimensionsBefore.set(widget.id, { w: widget.w, h: widget.h }); + }); + // Run auto-layout to reflow and expand widgets for new grid // This prevents overlap and optimizes space usage this.gridEngine.autoLayout(currentTab.widgets, { preserveOrder: true }); + // Call onResize handlers for widgets whose dimensions changed + // This allows widgets to update internal layouts (e.g., User Attributes grid columns) + currentTab.widgets.forEach(widget => { + const before = dimensionsBefore.get(widget.id); + if (before && (before.w !== widget.w || before.h !== widget.h)) { + const widgetData = this.widgets.get(widget.id); + if (widgetData?.definition?.onResize && widgetData.element) { + console.log(`[DashboardManager] Calling onResize for ${widget.type} (${before.w}x${before.h} → ${widget.w}x${widget.h})`); + widgetData.definition.onResize(widgetData.element, widget.w, widget.h); + } + } + }); + // Save changes this.triggerAutoSave(); } @@ -871,6 +891,7 @@ export class DashboardManager { * @param {Array} widgets - All widgets to distribute */ distributeWidgetsByCategory(widgets) { + console.log('[DashboardManager] ===== DISTRIBUTE WIDGETS BY CATEGORY CALLED ====='); console.log('[DashboardManager] Distributing widgets across multiple tabs'); // Group widgets by category @@ -919,7 +940,6 @@ export class DashboardManager { widgets: groups.scene }); - // Auto-layout scene widgets this.gridEngine.autoLayout(groups.scene, { preserveOrder: true }); } @@ -933,7 +953,6 @@ export class DashboardManager { widgets: groups.social }); - // Auto-layout social widgets this.gridEngine.autoLayout(groups.social, { preserveOrder: true }); } @@ -947,7 +966,6 @@ export class DashboardManager { widgets: groups.inventory }); - // Auto-layout inventory widgets this.gridEngine.autoLayout(groups.inventory, { preserveOrder: true }); } @@ -961,7 +979,6 @@ export class DashboardManager { widgets: groups.quests }); - // Auto-layout quest widgets this.gridEngine.autoLayout(groups.quests, { preserveOrder: true }); } @@ -973,6 +990,17 @@ export class DashboardManager { this.switchTab(this.dashboard.tabs[0].id); } + // After rendering, call onResize for all currently rendered widgets to update internal layouts + // This ensures widgets like User Attributes recalculate their grid columns + // Note: Only iterate over this.widgets (currently rendered), not all tabs (includes non-rendered widgets) + console.log(`[DashboardManager] Calling onResize for ${this.widgets.size} rendered widgets after auto-layout`); + this.widgets.forEach(widgetData => { + if (widgetData?.definition?.onResize && widgetData.element) { + console.log(`[DashboardManager] Calling onResize for ${widgetData.widget.type} (${widgetData.widget.w}x${widgetData.widget.h})`); + widgetData.definition.onResize(widgetData.element, widgetData.widget.w, widgetData.widget.h); + } + }); + // Save layout this.triggerAutoSave(); } @@ -1569,11 +1597,30 @@ export class DashboardManager { // Update tab's widgets array with sorted order currentTab.widgets = sortedWidgets; + // Store current widget dimensions before auto-layout + const dimensionsBefore = new Map(); + currentTab.widgets.forEach(widget => { + dimensionsBefore.set(widget.id, { w: widget.w, h: widget.h }); + }); + // Auto-layout widgets on the current tab this.gridEngine.autoLayout(currentTab.widgets, { preserveOrder: options.preserveOrder !== false }); + // Call onResize handlers for widgets whose dimensions changed + // This allows widgets to update internal layouts (e.g., User Attributes grid columns) + currentTab.widgets.forEach(widget => { + const before = dimensionsBefore.get(widget.id); + if (before && (before.w !== widget.w || before.h !== widget.h)) { + const widgetData = this.widgets.get(widget.id); + if (widgetData?.definition?.onResize && widgetData.element) { + console.log(`[DashboardManager] Calling onResize for ${widget.type} (${before.w}x${before.h} → ${widget.w}x${widget.h})`); + widgetData.definition.onResize(widgetData.element, widget.w, widget.h); + } + } + }); + // Re-render all widgets with new positions this.clearGrid(); currentTab.widgets.forEach(widget => { @@ -1601,6 +1648,7 @@ export class DashboardManager { * @param {boolean} [options.resetSizes=true] - Reset widgets to default sizes before layout */ autoLayoutWidgets(options = {}) { + console.log('[DashboardManager] ===== AUTO-LAYOUT WIDGETS CALLED ====='); console.log('[DashboardManager] Auto-layout widgets requested'); // Gather ALL widgets from ALL tabs (don't lose inventory, social, etc.) diff --git a/src/systems/dashboard/gridEngine.js b/src/systems/dashboard/gridEngine.js index b24c472..67c2763 100644 --- a/src/systems/dashboard/gridEngine.js +++ b/src/systems/dashboard/gridEngine.js @@ -9,7 +9,8 @@ */ // Performance: Disable console logging (console.error still active) -const DEBUG = false; +// Temporarily enabled for debugging auto-arrange onResize issue +const DEBUG = true; const console = DEBUG ? window.console : { log: () => {}, warn: () => {}, diff --git a/src/systems/dashboard/widgets/userAttributesWidget.js b/src/systems/dashboard/widgets/userAttributesWidget.js index f8ccdfe..a1babd4 100644 --- a/src/systems/dashboard/widgets/userAttributesWidget.js +++ b/src/systems/dashboard/widgets/userAttributesWidget.js @@ -172,8 +172,20 @@ export function registerUserAttributesWidget(registry, dependencies) { // Count visible attributes from DOM const attrCount = statsGrid.querySelectorAll('.rpg-classic-stat').length; - // Recalculate optimal columns based on new width - const optimalCols = calculateOptimalColumns(attrCount, newW); + // Get actual pixel width of container (not grid units) + // calculateOptimalColumns expects pixel width to determine if 3 columns fit + const containerWidth = container.offsetWidth; + + console.log('[UserAttributes] onResize called:', { + gridUnits: `${newW}x${newH}`, + pixelWidth: containerWidth, + attrCount: attrCount + }); + + // Recalculate optimal columns based on actual pixel width + const optimalCols = calculateOptimalColumns(attrCount, containerWidth); + + console.log('[UserAttributes] Calculated optimal columns:', optimalCols); // Apply new grid layout statsGrid.style.gridTemplateColumns = `repeat(${optimalCols}, 1fr)`;