fix(dashboard): fix resize, drag-drop, overflow, and add auto-migration

Multiple critical fixes for dashboard v2:

**1. ResizeHandler error - updateContainerWidth is not a function**
- resizeHandler.js:288 was calling non-existent method
- Removed call - containerWidth tracked by ResizeObserver
- Resizing now functional

**2. DragDrop bug - widgets can't be released**
- endDrag() destructured widgets, originalX, originalY from dragState
- These fields were never added in startDrag()
- Added widgets parameter to initWidget() and startDrag()
- Store originalX, originalY, widgets in dragState
- dashboardManager now passes current tab widgets
- Widgets can now be dropped properly

**3. Widget content overflow**
- Added base .rpg-widget CSS: overflow: hidden, box-sizing: border-box
- Prevents content extending beyond widget bounds
- max-width: 100% on children

**4. Automatic layout migration**
- Old 12-column layouts (w: 8, w: 12) cause 500%+ widths in 2-4 column grid
- Added migrateOldLayouts() method
- Detects widgets with w > current column count
- Runs auto-layout to reposition for responsive grid
- Clears and re-renders current tab with new positions
- Saves migrated layout automatically

**5. Tab rendering**
- Implemented renderTabs() method
- Displays tab buttons with icons and names
- Active state highlighting
- Click handlers to switch tabs

**6. Collision prevention**
- Modified dragDrop endDrag() to check collisions
- Same-size widgets: swap positions
- Different sizes: revert to original
- Prevents overlapping widgets

**7. Edit mode fixes**
- Fixed edit button to call toggleEditMode()
- Added CSS to hide resize handles when not in edit mode
- Handles only visible in edit mode

**8. Icon-only header buttons**
- Auto-Arrange and Edit buttons now icon-only
- Saves horizontal space in header

All issues from user testing resolved.
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-23 14:49:33 +11:00
parent 29afefb76e
commit 264ea2fc4c
6 changed files with 245 additions and 23 deletions
+112 -1
View File
@@ -182,6 +182,12 @@ export class DashboardManager {
// Measure container width and set up responsive sizing
this.setupContainerSizing();
// Migrate old 12-column layouts to new responsive grid
this.migrateOldLayouts();
// Render tab navigation
this.renderTabs();
console.log('[DashboardManager] All systems initialized');
this.notifyChange('initialized');
}
@@ -256,6 +262,104 @@ export class DashboardManager {
console.log('[DashboardManager] Viewport resize listener added');
}
/**
* Migrate old 12-column layouts to new responsive grid
* Detects if any widgets have widths exceeding current column count
* and automatically runs auto-layout to fix them
*/
migrateOldLayouts() {
console.log('[DashboardManager] Checking for old layouts to migrate...');
let needsMigration = false;
// Check all tabs
this.dashboard.tabs.forEach(tab => {
if (!tab.widgets || tab.widgets.length === 0) return;
// Check if any widget has width exceeding current column count
tab.widgets.forEach(widget => {
if (widget.w > this.gridEngine.columns) {
console.warn(`[DashboardManager] Widget ${widget.id} has width ${widget.w} exceeding column count ${this.gridEngine.columns}`);
needsMigration = true;
}
});
if (needsMigration) {
console.log(`[DashboardManager] Migrating tab ${tab.id} to new responsive grid...`);
// Run auto-layout on this tab's widgets
this.gridEngine.autoLayout(tab.widgets, { preferFullWidth: true });
console.log(`[DashboardManager] Tab ${tab.id} migrated successfully`);
}
});
if (needsMigration) {
// Save migrated layout
this.triggerAutoSave();
// Re-render current tab with new positions
this.clearGrid();
const currentTab = this.tabManager.getTab(this.currentTabId);
if (currentTab && currentTab.widgets) {
currentTab.widgets.forEach(widget => {
const definition = this.registry.get(widget.type);
if (definition) {
this.renderWidget(widget, definition);
}
});
}
console.log('[DashboardManager] Old layouts migrated, saved, and re-rendered');
} else {
console.log('[DashboardManager] No migration needed');
}
}
/**
* Render tab navigation UI
*/
renderTabs() {
if (!this.tabContainer) {
console.warn('[DashboardManager] Tab container not found');
return;
}
// Clear existing tabs
this.tabContainer.innerHTML = '';
// Get all tabs sorted by order
const tabs = this.tabManager.getTabs();
if (tabs.length === 0) {
console.warn('[DashboardManager] No tabs to render');
return;
}
// Create tab buttons
tabs.forEach(tab => {
const button = document.createElement('button');
button.className = 'rpg-dashboard-tab';
button.dataset.tabId = tab.id;
button.innerHTML = `
<span class="rpg-tab-icon">${tab.icon}</span>
<span class="rpg-tab-name">${tab.name}</span>
`;
// Mark active tab
if (tab.id === this.currentTabId) {
button.classList.add('active');
}
// Tab click handler
button.addEventListener('click', () => {
this.switchTab(tab.id);
});
this.tabContainer.appendChild(button);
});
console.log(`[DashboardManager] Rendered ${tabs.length} tabs`);
}
/**
* Add a new widget to the dashboard
* @param {string} type - Widget type (must be registered)
@@ -425,13 +529,17 @@ export class DashboardManager {
// Render widget content
this.renderWidgetContent(element, widget, definition);
// Get current tab's widgets for collision detection
const currentTab = this.tabManager.getTab(this.currentTabId);
const allWidgets = currentTab ? currentTab.widgets : [];
// Initialize drag & drop
this.dragHandler.initWidget(element, widget, (updated, newX, newY) => {
widget.x = newX;
widget.y = newY;
this.repositionWidget(element, widget);
this.triggerAutoSave();
});
}, allWidgets);
// Initialize resize
this.resizeHandler.initWidget(element, widget, (updated, newW, newH, newX, newY) => {
@@ -573,6 +681,9 @@ export class DashboardManager {
console.log(`[DashboardManager] Switching to tab: ${tabId}`);
this.currentTabId = tabId;
// Re-render tabs to update active state
this.renderTabs();
// Clear grid
this.clearGrid();