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:
@@ -138,15 +138,12 @@ function getInlineDashboardTemplate() {
|
|||||||
<div class="rpg-dashboard-header-right">
|
<div class="rpg-dashboard-header-right">
|
||||||
<button id="rpg-dashboard-auto-layout" class="rpg-dashboard-btn rpg-auto-layout-btn" title="Auto-Arrange Widgets">
|
<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>
|
<i class="fa-solid fa-table-cells-large"></i>
|
||||||
<span>Auto-Arrange</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button id="rpg-dashboard-edit-mode" class="rpg-dashboard-btn rpg-edit-mode-btn" title="Toggle Edit Mode">
|
<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>
|
<i class="fa-solid fa-pen-to-square"></i>
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button id="rpg-dashboard-add-widget" class="rpg-dashboard-btn rpg-add-widget-btn" style="display: none;" title="Add Widget">
|
<button id="rpg-dashboard-add-widget" class="rpg-dashboard-btn rpg-add-widget-btn" style="display: none;" title="Add Widget">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus"></i>
|
||||||
<span>Add Widget</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button id="rpg-dashboard-export-layout" class="rpg-dashboard-btn rpg-export-btn" style="display: none;" title="Export Layout">
|
<button id="rpg-dashboard-export-layout" class="rpg-dashboard-btn rpg-export-btn" style="display: none;" title="Export Layout">
|
||||||
<i class="fa-solid fa-download"></i>
|
<i class="fa-solid fa-download"></i>
|
||||||
@@ -202,9 +199,9 @@ function setupDashboardEventListeners(dependencies) {
|
|||||||
const editModeBtn = document.querySelector('#rpg-dashboard-edit-mode');
|
const editModeBtn = document.querySelector('#rpg-dashboard-edit-mode');
|
||||||
if (editModeBtn) {
|
if (editModeBtn) {
|
||||||
editModeBtn.addEventListener('click', () => {
|
editModeBtn.addEventListener('click', () => {
|
||||||
if (dashboardManager) {
|
if (dashboardManager && dashboardManager.editManager) {
|
||||||
const isEditMode = dashboardManager.editModeManager.isEditMode();
|
console.log('[RPG Companion] Edit button clicked');
|
||||||
dashboardManager.editModeManager.setEditMode(!isEditMode);
|
dashboardManager.editManager.toggleEditMode();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,12 @@ export class DashboardManager {
|
|||||||
// Measure container width and set up responsive sizing
|
// Measure container width and set up responsive sizing
|
||||||
this.setupContainerSizing();
|
this.setupContainerSizing();
|
||||||
|
|
||||||
|
// Migrate old 12-column layouts to new responsive grid
|
||||||
|
this.migrateOldLayouts();
|
||||||
|
|
||||||
|
// Render tab navigation
|
||||||
|
this.renderTabs();
|
||||||
|
|
||||||
console.log('[DashboardManager] All systems initialized');
|
console.log('[DashboardManager] All systems initialized');
|
||||||
this.notifyChange('initialized');
|
this.notifyChange('initialized');
|
||||||
}
|
}
|
||||||
@@ -256,6 +262,104 @@ export class DashboardManager {
|
|||||||
console.log('[DashboardManager] Viewport resize listener added');
|
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
|
* Add a new widget to the dashboard
|
||||||
* @param {string} type - Widget type (must be registered)
|
* @param {string} type - Widget type (must be registered)
|
||||||
@@ -425,13 +529,17 @@ export class DashboardManager {
|
|||||||
// Render widget content
|
// Render widget content
|
||||||
this.renderWidgetContent(element, widget, definition);
|
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
|
// Initialize drag & drop
|
||||||
this.dragHandler.initWidget(element, widget, (updated, newX, newY) => {
|
this.dragHandler.initWidget(element, widget, (updated, newX, newY) => {
|
||||||
widget.x = newX;
|
widget.x = newX;
|
||||||
widget.y = newY;
|
widget.y = newY;
|
||||||
this.repositionWidget(element, widget);
|
this.repositionWidget(element, widget);
|
||||||
this.triggerAutoSave();
|
this.triggerAutoSave();
|
||||||
});
|
}, allWidgets);
|
||||||
|
|
||||||
// Initialize resize
|
// Initialize resize
|
||||||
this.resizeHandler.initWidget(element, widget, (updated, newW, newH, newX, newY) => {
|
this.resizeHandler.initWidget(element, widget, (updated, newW, newH, newX, newY) => {
|
||||||
@@ -573,6 +681,9 @@ export class DashboardManager {
|
|||||||
console.log(`[DashboardManager] Switching to tab: ${tabId}`);
|
console.log(`[DashboardManager] Switching to tab: ${tabId}`);
|
||||||
this.currentTabId = tabId;
|
this.currentTabId = tabId;
|
||||||
|
|
||||||
|
// Re-render tabs to update active state
|
||||||
|
this.renderTabs();
|
||||||
|
|
||||||
// Clear grid
|
// Clear grid
|
||||||
this.clearGrid();
|
this.clearGrid();
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,11 @@
|
|||||||
<!-- Auto-Layout Button -->
|
<!-- Auto-Layout Button -->
|
||||||
<button id="rpg-dashboard-auto-layout" class="rpg-dashboard-btn rpg-auto-layout-btn" title="Auto-Arrange Widgets">
|
<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>
|
<i class="fa-solid fa-table-cells-large"></i>
|
||||||
<span>Auto-Arrange</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Edit Mode Toggle -->
|
<!-- Edit Mode Toggle -->
|
||||||
<button id="rpg-dashboard-edit-mode" class="rpg-dashboard-btn rpg-edit-mode-btn" title="Toggle Edit Mode">
|
<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>
|
<i class="fa-solid fa-pen-to-square"></i>
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Add Widget Button (shown in edit mode) -->
|
<!-- Add Widget Button (shown in edit mode) -->
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ export class DragDropHandler {
|
|||||||
* @param {HTMLElement} element - Widget DOM element
|
* @param {HTMLElement} element - Widget DOM element
|
||||||
* @param {Object} widget - Widget data object
|
* @param {Object} widget - Widget data object
|
||||||
* @param {Function} onDragEnd - Callback when drag completes (widget, newX, newY)
|
* @param {Function} onDragEnd - Callback when drag completes (widget, newX, newY)
|
||||||
|
* @param {Array<Object>} widgets - All widgets (for collision detection)
|
||||||
*/
|
*/
|
||||||
initWidget(element, widget, onDragEnd) {
|
initWidget(element, widget, onDragEnd, widgets = []) {
|
||||||
// Store handler reference for cleanup
|
// Store handler reference for cleanup
|
||||||
const dragHandle = element.querySelector('.drag-handle') || element;
|
const dragHandle = element.querySelector('.drag-handle') || element;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ export class DragDropHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.startDrag(e, element, widget, onDragEnd);
|
this.startDrag(e, element, widget, onDragEnd, widgets);
|
||||||
};
|
};
|
||||||
|
|
||||||
const touchStartHandler = (e) => {
|
const touchStartHandler = (e) => {
|
||||||
@@ -77,7 +78,7 @@ export class DragDropHandler {
|
|||||||
// Delay touch drag to allow scrolling
|
// Delay touch drag to allow scrolling
|
||||||
this.touchTimer = setTimeout(() => {
|
this.touchTimer = setTimeout(() => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.startDrag(e.touches[0], element, widget, onDragEnd);
|
this.startDrag(e.touches[0], element, widget, onDragEnd, widgets);
|
||||||
}, this.options.touchDelay);
|
}, this.options.touchDelay);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,8 +130,9 @@ export class DragDropHandler {
|
|||||||
* @param {HTMLElement} element - Element being dragged
|
* @param {HTMLElement} element - Element being dragged
|
||||||
* @param {Object} widget - Widget data
|
* @param {Object} widget - Widget data
|
||||||
* @param {Function} onDragEnd - Callback when drag completes
|
* @param {Function} onDragEnd - Callback when drag completes
|
||||||
|
* @param {Array<Object>} widgets - All widgets (for collision detection)
|
||||||
*/
|
*/
|
||||||
startDrag(e, element, widget, onDragEnd) {
|
startDrag(e, element, widget, onDragEnd, widgets = []) {
|
||||||
// Calculate pointer offset from element top-left
|
// Calculate pointer offset from element top-left
|
||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
const offsetX = e.clientX - rect.left;
|
const offsetX = e.clientX - rect.left;
|
||||||
@@ -148,7 +150,10 @@ export class DragDropHandler {
|
|||||||
offsetY,
|
offsetY,
|
||||||
ghost,
|
ghost,
|
||||||
isDragging: true,
|
isDragging: true,
|
||||||
onDragEnd
|
onDragEnd,
|
||||||
|
widgets,
|
||||||
|
originalX: widget.x,
|
||||||
|
originalY: widget.y
|
||||||
};
|
};
|
||||||
|
|
||||||
// Change cursor
|
// Change cursor
|
||||||
@@ -263,7 +268,7 @@ export class DragDropHandler {
|
|||||||
endDrag() {
|
endDrag() {
|
||||||
if (!this.dragState) return;
|
if (!this.dragState) return;
|
||||||
|
|
||||||
const { element, widget, onDragEnd } = this.dragState;
|
const { element, widget, onDragEnd, widgets, originalX, originalY } = this.dragState;
|
||||||
|
|
||||||
// Restore original element
|
// Restore original element
|
||||||
element.style.opacity = '1';
|
element.style.opacity = '1';
|
||||||
@@ -272,9 +277,51 @@ export class DragDropHandler {
|
|||||||
const dragHandle = element.querySelector('.drag-handle') || element;
|
const dragHandle = element.querySelector('.drag-handle') || element;
|
||||||
dragHandle.style.cursor = 'grab';
|
dragHandle.style.cursor = 'grab';
|
||||||
|
|
||||||
// Call callback with new position
|
// Check for collision before committing
|
||||||
if (onDragEnd) {
|
const otherWidgets = widgets.filter(w => w.id !== widget.id);
|
||||||
onDragEnd(widget, widget.x, widget.y);
|
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
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
|
|||||||
@@ -284,8 +284,7 @@ export class ResizeHandler {
|
|||||||
const deltaX = clientX - startX;
|
const deltaX = clientX - startX;
|
||||||
const deltaY = clientY - startY;
|
const deltaY = clientY - startY;
|
||||||
|
|
||||||
// Get column/row size in pixels
|
// Get column/row size in pixels (containerWidth already set by ResizeObserver in DashboardManager)
|
||||||
this.gridEngine.updateContainerWidth();
|
|
||||||
const totalGaps = this.gridEngine.gap * (this.gridEngine.columns + 1);
|
const totalGaps = this.gridEngine.gap * (this.gridEngine.columns + 1);
|
||||||
const colWidth = (this.gridEngine.containerWidth - totalGaps) / this.gridEngine.columns;
|
const colWidth = (this.gridEngine.containerWidth - totalGaps) / this.gridEngine.columns;
|
||||||
const rowHeight = this.gridEngine.rowHeight;
|
const rowHeight = this.gridEngine.rowHeight;
|
||||||
|
|||||||
@@ -1073,12 +1073,56 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rpg-dashboard-btn {
|
.rpg-dashboard-tabs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-dashboard-tab {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
padding: 0.4rem 0.6rem;
|
padding: 0.4rem 0.7rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-dashboard-tab:hover {
|
||||||
|
background: var(--SmartThemeBlurTintColor);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-dashboard-tab.active {
|
||||||
|
background: var(--SmartThemeBlurTintColor);
|
||||||
|
border-color: var(--SmartThemeBorderColor);
|
||||||
|
opacity: 1;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-tab-icon {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-tab-name {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-dashboard-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.5rem;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
border: 1px solid var(--SmartThemeBorderColor);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
background: var(--SmartThemeBlurTintColor);
|
background: var(--SmartThemeBlurTintColor);
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
@@ -1093,7 +1137,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rpg-dashboard-btn i {
|
.rpg-dashboard-btn i {
|
||||||
font-size: 0.85rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rpg-dashboard-grid {
|
.rpg-dashboard-grid {
|
||||||
@@ -1102,6 +1146,19 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hide resize handles by default */
|
||||||
|
.resize-handles {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show resize handles in edit mode */
|
||||||
|
.edit-mode .resize-handles {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
DASHBOARD V2 WIDGET STYLES
|
DASHBOARD V2 WIDGET STYLES
|
||||||
========================================
|
========================================
|
||||||
@@ -1116,6 +1173,19 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
- %: Container-relative sizing
|
- %: Container-relative sizing
|
||||||
======================================== */
|
======================================== */
|
||||||
|
|
||||||
|
/* Base widget container - ensures content stays within bounds */
|
||||||
|
.rpg-widget {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-widget > * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.rpg-widget .rpg-stats-content {
|
.rpg-widget .rpg-stats-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; /* Stack vertically */
|
flex-direction: column; /* Stack vertically */
|
||||||
|
|||||||
Reference in New Issue
Block a user