fix: auto-arrange now correctly recalculates widget grid layouts

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
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-11-04 17:04:23 +11:00
parent db1ee0d08b
commit 2c86af5561
4 changed files with 69 additions and 9 deletions
@@ -296,7 +296,6 @@ function setupDashboardEventListeners(dependencies) {
});
if (confirmed) {
console.log('[RPG Companion] Auto-layout button clicked');
dashboardManager.autoLayoutWidgets();
}
}
+53 -5
View File
@@ -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<Object>} 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.)
+2 -1
View File
@@ -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: () => {},
@@ -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)`;