feat(dashboard): implement smart widget collision and category-aware layout
Complete dashboard v2 improvements for better UX and visual consistency: **1. Push-Aside Drag/Drop (dragDrop.js)** - Replace swap/revert logic with intelligent reflow algorithm - When widgets collide on drag, automatically push overlapping widgets down - All affected widgets repositioned after reflow completes - Eliminates widget overlap issues **2. Unified Widget Styling (style.css)** - Add consistent .rpg-widget container styling for all widgets - Background: rgba(0,0,0,0.2) for visual separation - Border-left: 3px highlight for category identification - Box-shadow and border-radius for depth and polish - Maintain individual widget decorative styles **3. Logical Default Layout (defaultLayout.js)** - Reorganize widgets into semantic clusters with clear comments: - USER CLUSTER (top): userInfo → userStats → userMood + userAttributes - SCENE CLUSTER (middle): calendar + weather → temp + clock → location - SOCIAL CLUSTER (bottom): presentCharacters - userInfo widget now at top (y=0) as expected - All positions use rem units for responsive scaling **4. Category-Aware Auto-Layout (dashboardManager.js)** - Implement sortWidgetsByCategory() with priority ordering: user → scene → social → inventory - Within user category, specific ordering: userInfo → userStats → userMood → userAttributes - Add preserveOrder option to gridEngine.autoLayout() - Auto-arrange now uses logical grouping instead of random bin-packing **5. Multi-Tab Auto-Distribution (dashboardManager.js)** - Add estimateLayoutHeight() to detect when content exceeds threshold - Implement distributeWidgetsByCategory() for automatic tab creation: - "Status" tab: user + scene widgets - "Social" tab: social widgets (if any) - "Inventory" tab: inventory widgets (if any) - Each tab gets category-aware auto-layout - 80rem height threshold for single-tab limit **6. Widget Category Metadata (widgets/)** - Add category field to all widget definitions: - userInfo, userStats, userMood, userAttributes: 'user' - calendar, weather, temperature, clock, location: 'scene' - presentCharacters: 'social' - inventory: 'inventory' **7. Integration Improvements (dashboardIntegration.js)** - Set default layout on initialization for reset functionality - Add reset layout button to dashboard header - Wire up reset button event handler **8. Core State Management (index.js)** - Add getInfoBoxData() and setInfoBoxData() to state API - Ensure info box data persists across sessions **Technical Details:** - Rem units throughout for 1080p→4K→mobile responsive scaling - Reflow algorithm leverages existing gridEngine collision detection - Category-aware sorting preserves logical relationships - Multi-tab distribution prevents single-page scroll fatigue - All changes maintain backwards compatibility with existing layouts Fixes dashboard issues after rem unit conversion introduced massive positioning bugs. Users reported widgets overlapping on drag, visual inconsistency, and random auto-arrange behavior. Related: Epic 2 (Dashboard v2), Phase 3.2
This commit is contained in:
@@ -11,6 +11,7 @@ import { saveSettings } from '../../core/persistence.js';
|
||||
import { renderExtensionTemplateAsync } from '../../../../../../extensions.js';
|
||||
import { DashboardManager } from './dashboardManager.js';
|
||||
import { WidgetRegistry } from './widgetRegistry.js';
|
||||
import { generateDefaultDashboard } from './defaultLayout.js';
|
||||
|
||||
// Widget imports
|
||||
import { registerUserInfoWidget } from './widgets/userInfoWidget.js';
|
||||
@@ -92,6 +93,11 @@ export async function initializeDashboard(dependencies) {
|
||||
// Initialize the dashboard
|
||||
await dashboardManager.init();
|
||||
|
||||
// Set default layout (required for reset functionality)
|
||||
const defaultLayout = generateDefaultDashboard();
|
||||
dashboardManager.setDefaultLayout(defaultLayout);
|
||||
console.log('[RPG Companion] Default layout set with', defaultLayout.tabs.length, 'tabs');
|
||||
|
||||
// Set up dashboard event listeners
|
||||
setupDashboardEventListeners(dependencies);
|
||||
|
||||
@@ -139,6 +145,9 @@ function getInlineDashboardTemplate() {
|
||||
<div id="rpg-dashboard-tabs" class="rpg-dashboard-tabs"></div>
|
||||
</div>
|
||||
<div class="rpg-dashboard-header-right">
|
||||
<button id="rpg-dashboard-reset-layout" class="rpg-dashboard-btn rpg-reset-layout-btn" title="Reset to Default Layout">
|
||||
<i class="fa-solid fa-rotate-left"></i>
|
||||
</button>
|
||||
<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>
|
||||
</button>
|
||||
@@ -194,13 +203,26 @@ function registerAllWidgets(registry, dependencies) {
|
||||
* Set up dashboard event listeners
|
||||
*/
|
||||
function setupDashboardEventListeners(dependencies) {
|
||||
// Reset layout button
|
||||
const resetLayoutBtn = document.querySelector('#rpg-dashboard-reset-layout');
|
||||
if (resetLayoutBtn) {
|
||||
resetLayoutBtn.addEventListener('click', () => {
|
||||
if (dashboardManager) {
|
||||
if (confirm('Reset dashboard to default layout? This will remove all widgets and reload the defaults.')) {
|
||||
console.log('[RPG Companion] Reset layout button clicked');
|
||||
dashboardManager.resetLayout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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 });
|
||||
dashboardManager.autoLayoutWidgets();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -332,32 +354,36 @@ export function createDefaultLayout(manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[RPG Companion] Creating default dashboard layout (2-column optimized)...');
|
||||
console.log('[RPG Companion] Creating default dashboard layout with modular widgets...');
|
||||
|
||||
const mainTab = manager.tabManager.getActiveTabId();
|
||||
|
||||
// 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 });
|
||||
// Add modular user widgets
|
||||
// Row 0: User Info (avatar, name, level) - full width
|
||||
manager.addWidget('userInfo', mainTab, { x: 0, y: 0, w: 2, h: 1 });
|
||||
|
||||
// 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 1-2: User Stats (health/energy bars) - full width
|
||||
manager.addWidget('userStats', mainTab, { x: 0, y: 1, w: 2, h: 2 });
|
||||
|
||||
// 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 3-4: User Mood (left) + User Attributes (right)
|
||||
manager.addWidget('userMood', mainTab, { x: 0, y: 3, w: 1, h: 1 });
|
||||
manager.addWidget('userAttributes', mainTab, { x: 1, y: 3, w: 1, h: 2 });
|
||||
|
||||
// Row 5: Location (full width)
|
||||
manager.addWidget('location', mainTab, { x: 0, y: 7, w: 2, h: 2 });
|
||||
// Row 5-6: Calendar (left) + Weather (right)
|
||||
manager.addWidget('calendar', mainTab, { x: 0, y: 5, w: 1, h: 2 });
|
||||
manager.addWidget('weather', mainTab, { x: 1, y: 5, w: 1, h: 2 });
|
||||
|
||||
// Row 6-7: Present Characters (full width)
|
||||
manager.addWidget('presentCharacters', mainTab, { x: 0, y: 9, w: 2, h: 3 });
|
||||
// Row 7-8: Temperature (left) + Clock (right)
|
||||
manager.addWidget('temperature', mainTab, { x: 0, y: 7, w: 1, h: 2 });
|
||||
manager.addWidget('clock', mainTab, { x: 1, y: 7, w: 1, h: 2 });
|
||||
|
||||
// Row 8-13: Inventory (full width)
|
||||
manager.addWidget('inventory', mainTab, { x: 0, y: 12, w: 2, h: 6 });
|
||||
// Row 9-10: Location (full width)
|
||||
manager.addWidget('location', mainTab, { x: 0, y: 9, w: 2, h: 2 });
|
||||
|
||||
console.log('[RPG Companion] Default layout created (2-column optimized)');
|
||||
// Row 11-13: Present Characters (full width)
|
||||
manager.addWidget('presentCharacters', mainTab, { x: 0, y: 11, w: 2, h: 3 });
|
||||
|
||||
console.log('[RPG Companion] Default layout created with modular widgets');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user