feat(dashboard): add auto-layout button with smart widget packing

Implements intelligent auto-layout system that efficiently arranges widgets to maximize space usage while respecting panel width constraints.

**Key Features:**
- Smart packing algorithm that sorts by widget area and finds optimal positions
- Respects responsive column count (2-4 columns based on panel width)
- Prefers full-width widgets when possible to eliminate gaps
- Fallback to narrower widths for better vertical packing
- Maintains minimum widget sizes

**Implementation:**
- GridEngine.autoLayout() - Core packing algorithm with collision detection
- DashboardManager.autoLayoutWidgets() - High-level API that re-renders after layout
- Auto-Arrange button in dashboard header (uses fa-table-cells-large icon)
- Event handler wired to call autoLayoutWidgets with preferFullWidth=true

**Algorithm Strategy:**
1. Sort widgets by area (largest first) for efficient packing
2. For each widget, try full-width placement first
3. Find first available position using row-by-row scan
4. If position is too far down, try narrower widths
5. Mark cells as occupied to prevent overlaps

**Testing Notes:**
- Works with current responsive column system (2-4 columns)
- Respects minimum sizes and column constraints
- Re-renders all widgets after repositioning
- Auto-saves layout changes

Part of Epic 2: Dashboard Widget Library
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-23 14:00:00 +11:00
parent e32a008f0b
commit 122bb3194a
13 changed files with 668 additions and 87 deletions
+35 -16
View File
@@ -8,7 +8,7 @@
import { extensionName } from '../../core/config.js';
import { extensionSettings } from '../../core/state.js';
import { saveSettings } from '../../core/persistence.js';
import { renderExtensionTemplateAsync } from '../../../../../extensions.js';
import { renderExtensionTemplateAsync } from '../../../../../../extensions.js';
import { DashboardManager } from './dashboardManager.js';
import { WidgetRegistry } from './widgetRegistry.js';
@@ -136,6 +136,10 @@ function getInlineDashboardTemplate() {
<div id="rpg-dashboard-tabs" class="rpg-dashboard-tabs"></div>
</div>
<div class="rpg-dashboard-header-right">
<button id="rpg-dashboard-auto-layout" class="rpg-dashboard-btn rpg-auto-layout-btn" title="Auto-Arrange Widgets">
<i class="fa-solid fa-table-cells-large"></i>
<span>Auto-Arrange</span>
</button>
<button id="rpg-dashboard-edit-mode" class="rpg-dashboard-btn rpg-edit-mode-btn" title="Toggle Edit Mode">
<i class="fa-solid fa-pen-to-square"></i>
<span>Edit</span>
@@ -183,6 +187,17 @@ function registerAllWidgets(registry, dependencies) {
* Set up dashboard event listeners
*/
function setupDashboardEventListeners(dependencies) {
// Auto-layout button
const autoLayoutBtn = document.querySelector('#rpg-dashboard-auto-layout');
if (autoLayoutBtn) {
autoLayoutBtn.addEventListener('click', () => {
if (dashboardManager) {
console.log('[RPG Companion] Auto-layout button clicked');
dashboardManager.autoLayoutWidgets({ preferFullWidth: true });
}
});
}
// Edit mode toggle
const editModeBtn = document.querySelector('#rpg-dashboard-edit-mode');
if (editModeBtn) {
@@ -310,28 +325,32 @@ export function createDefaultLayout(manager) {
return;
}
console.log('[RPG Companion] Creating default dashboard layout...');
console.log('[RPG Companion] Creating default dashboard layout (2-column optimized)...');
const mainTab = manager.tabManager.getActiveTabId();
// Add widgets with default positions
// Row 1: User Stats (left) + Calendar + Weather + Temperature + Clock (right)
manager.addWidget('userStats', mainTab, { x: 0, y: 0, w: 6, h: 4 });
manager.addWidget('calendar', mainTab, { x: 6, y: 0, w: 2, h: 2 });
manager.addWidget('weather', mainTab, { x: 8, y: 0, w: 3, h: 2 });
manager.addWidget('temperature', mainTab, { x: 11, y: 0, w: 2, h: 2 });
// Add widgets with 2-column layout positions
// Row 1-2: User Stats (full width)
manager.addWidget('userStats', mainTab, { x: 0, y: 0, w: 2, h: 3 });
// Row 2: Location (top right) + Clock (bottom right)
manager.addWidget('location', mainTab, { x: 6, y: 2, w: 6, h: 2 });
manager.addWidget('clock', mainTab, { x: 10, y: 2, w: 2, h: 2 });
// Row 3: Calendar (left) + Weather (right)
manager.addWidget('calendar', mainTab, { x: 0, y: 3, w: 1, h: 2 });
manager.addWidget('weather', mainTab, { x: 1, y: 3, w: 1, h: 2 });
// Row 3: Present Characters
manager.addWidget('presentCharacters', mainTab, { x: 0, y: 4, w: 12, h: 4 });
// Row 4: Temperature (left) + Clock (right)
manager.addWidget('temperature', mainTab, { x: 0, y: 5, w: 1, h: 2 });
manager.addWidget('clock', mainTab, { x: 1, y: 5, w: 1, h: 2 });
// Row 4: Inventory
manager.addWidget('inventory', mainTab, { x: 0, y: 8, w: 12, h: 6 });
// Row 5: Location (full width)
manager.addWidget('location', mainTab, { x: 0, y: 7, w: 2, h: 2 });
console.log('[RPG Companion] Default layout created');
// Row 6-7: Present Characters (full width)
manager.addWidget('presentCharacters', mainTab, { x: 0, y: 9, w: 2, h: 3 });
// Row 8-13: Inventory (full width)
manager.addWidget('inventory', mainTab, { x: 0, y: 12, w: 2, h: 6 });
console.log('[RPG Companion] Default layout created (2-column optimized)');
}
/**