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:
@@ -282,46 +282,19 @@ export class DragDropHandler {
|
||||
const collision = this.gridEngine.detectCollision(widget, otherWidgets);
|
||||
|
||||
if (collision) {
|
||||
// Find which widget we collided with
|
||||
const collidedWidget = otherWidgets.find(other => {
|
||||
return !(
|
||||
widget.x + widget.w <= other.x ||
|
||||
widget.x >= other.x + other.w ||
|
||||
widget.y + widget.h <= other.y ||
|
||||
widget.y >= other.y + other.h
|
||||
);
|
||||
});
|
||||
console.log('[DragDrop] Collision detected, pushing widgets aside and reflowing');
|
||||
|
||||
// If same size, swap positions
|
||||
if (collidedWidget && widget.w === collidedWidget.w && widget.h === collidedWidget.h) {
|
||||
console.log('[DragDrop] Swapping positions with:', collidedWidget.id);
|
||||
const tempX = collidedWidget.x;
|
||||
const tempY = collidedWidget.y;
|
||||
collidedWidget.x = widget.x;
|
||||
collidedWidget.y = widget.y;
|
||||
widget.x = tempX;
|
||||
widget.y = tempY;
|
||||
// Instead of reverting, reflow all widgets to push collisions aside
|
||||
// The reflow algorithm will automatically push overlapping widgets down
|
||||
const allWidgets = [widget, ...otherWidgets];
|
||||
this.gridEngine.reflow(allWidgets);
|
||||
|
||||
// Call callback with swapped position
|
||||
if (onDragEnd) {
|
||||
onDragEnd(widget, widget.x, widget.y);
|
||||
}
|
||||
} else {
|
||||
// Different sizes or multiple collisions - revert to original
|
||||
console.warn('[DragDrop] Collision detected, reverting to original position');
|
||||
widget.x = originalX;
|
||||
widget.y = originalY;
|
||||
console.log('[DragDrop] Reflow complete, widget at:', widget.x, widget.y);
|
||||
}
|
||||
|
||||
// Call callback with original position (no change)
|
||||
if (onDragEnd) {
|
||||
onDragEnd(widget, widget.x, widget.y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No collision, commit new position
|
||||
if (onDragEnd) {
|
||||
onDragEnd(widget, widget.x, widget.y);
|
||||
}
|
||||
// Always commit the position (either the dropped position or reflowed position)
|
||||
if (onDragEnd) {
|
||||
onDragEnd(widget, widget.x, widget.y);
|
||||
}
|
||||
|
||||
this.cleanup();
|
||||
|
||||
Reference in New Issue
Block a user