- Removed unnecessary dependencies (fs, path - built-in modules) - Organized debug files into /debug folder - Reduced excessive console logging for cleaner output - Fixed README formatting issues - Added comprehensive .gitignore - Removed redundant backup files (game.js.new) - Fixed duplicate code patterns in analytics - Created CLEANUP_SUMMARY.md documentation - Regenerated clean package-lock.json No breaking changes - all functionality preserved
678 lines
25 KiB
JavaScript
678 lines
25 KiB
JavaScript
/**
|
||
* Draggable & Resizable Cards System
|
||
* Allows admin cards to be dragged, dropped, and resized within the admin panel
|
||
*/
|
||
|
||
class DraggableCardManager {
|
||
constructor() {
|
||
this.cards = [];
|
||
this.draggedCard = null;
|
||
this.startPos = { x: 0, y: 0 };
|
||
this.startSize = { width: 0, height: 0 };
|
||
this.isResizing = false;
|
||
this.isDragging = false;
|
||
this.gridContainer = null;
|
||
this.savedLayouts = this.loadSavedLayouts();
|
||
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
// Wait for DOM to be ready
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => this.setupDraggableCards());
|
||
} else {
|
||
this.setupDraggableCards();
|
||
}
|
||
|
||
console.log('🎯 Draggable Cards Manager initialized');
|
||
}
|
||
|
||
setupDraggableCards() {
|
||
// Find all potential containers with cards
|
||
const containers = [
|
||
document.querySelector('.admin-grid'),
|
||
document.querySelector('.container'),
|
||
document.body
|
||
];
|
||
|
||
// Use the first container found, or body as fallback
|
||
this.gridContainer = containers.find(container => container && container.querySelector('.card')) || document.body;
|
||
|
||
if (!this.gridContainer) {
|
||
console.log('No container with cards found, retrying in 1 second...');
|
||
setTimeout(() => this.setupDraggableCards(), 1000);
|
||
return;
|
||
}
|
||
|
||
// Make the container suitable for absolute positioning
|
||
if (this.gridContainer.style.position !== 'relative' && this.gridContainer.style.position !== 'absolute') {
|
||
this.gridContainer.style.position = 'relative';
|
||
}
|
||
this.gridContainer.style.minHeight = '400px';
|
||
|
||
// Setup all cards found anywhere on the page
|
||
this.setupAllCards();
|
||
|
||
// Load saved layout
|
||
this.applySavedLayout();
|
||
|
||
// Add control buttons
|
||
this.addControlButtons();
|
||
|
||
// Watch for new cards being added dynamically
|
||
this.setupCardObserver();
|
||
|
||
console.log('📋 Draggable cards setup complete for all pages');
|
||
}
|
||
|
||
setupAllCards() {
|
||
// Find all cards on the entire page, not just in one container
|
||
const allCards = document.querySelectorAll('.card');
|
||
|
||
allCards.forEach((card, index) => {
|
||
// Skip if already setup
|
||
if (card.dataset.draggableSetup === 'true') return;
|
||
|
||
this.makeCardDraggable(card, index);
|
||
this.makeCardResizable(card, index);
|
||
this.addCardHeader(card, index);
|
||
|
||
card.dataset.draggableSetup = 'true';
|
||
});
|
||
|
||
console.log(`🎯 Setup ${allCards.length} draggable cards`);
|
||
}
|
||
|
||
setupCardObserver() {
|
||
// Watch for new cards being added to the page
|
||
const observer = new MutationObserver((mutations) => {
|
||
mutations.forEach((mutation) => {
|
||
mutation.addedNodes.forEach((node) => {
|
||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||
// Check if the added node is a card or contains cards
|
||
const cards = node.classList && node.classList.contains('card')
|
||
? [node]
|
||
: node.querySelectorAll ? node.querySelectorAll('.card') : [];
|
||
|
||
cards.forEach((card, index) => {
|
||
if (card.dataset.draggableSetup !== 'true') {
|
||
const allCards = document.querySelectorAll('.card');
|
||
const cardIndex = Array.from(allCards).indexOf(card);
|
||
this.makeCardDraggable(card, cardIndex);
|
||
this.makeCardResizable(card, cardIndex);
|
||
this.addCardHeader(card, cardIndex);
|
||
card.dataset.draggableSetup = 'true';
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
observer.observe(document.body, {
|
||
childList: true,
|
||
subtree: true
|
||
});
|
||
|
||
this.cardObserver = observer;
|
||
}
|
||
|
||
autoArrangeCards() {
|
||
const cards = this.freeformContainer.querySelectorAll('.card');
|
||
const containerWidth = this.freeformContainer.clientWidth - 40; // Account for padding
|
||
const cardWidth = 350; // Standard width for side-by-side
|
||
const cardHeight = 300; // Standard height
|
||
const gap = 20; // Gap between cards
|
||
|
||
const cardsPerRow = Math.floor(containerWidth / (cardWidth + gap));
|
||
|
||
cards.forEach((card, index) => {
|
||
const row = Math.floor(index / cardsPerRow);
|
||
const col = index % cardsPerRow;
|
||
|
||
const x = 20 + (col * (cardWidth + gap));
|
||
const y = 20 + (row * (cardHeight + gap));
|
||
|
||
card.style.left = x + 'px';
|
||
card.style.top = y + 'px';
|
||
card.style.width = cardWidth + 'px';
|
||
});
|
||
|
||
console.log(`📐 Auto-arranged ${cards.length} cards in ${cardsPerRow} columns`);
|
||
}
|
||
|
||
setupCards() {
|
||
const cards = this.gridContainer.querySelectorAll('.card');
|
||
|
||
cards.forEach((card, index) => {
|
||
this.makeCardDraggable(card, index);
|
||
this.makeCardResizable(card, index);
|
||
this.addCardHeader(card, index);
|
||
});
|
||
}
|
||
|
||
makeCardDraggable(card, index) {
|
||
// Add draggable handle
|
||
const header = card.querySelector('.card-header') || this.createCardHeader(card);
|
||
|
||
header.style.cursor = 'move';
|
||
header.addEventListener('mousedown', (e) => this.startDrag(e, card, index));
|
||
header.addEventListener('touchstart', (e) => this.startDrag(e, card, index));
|
||
|
||
// Prevent text selection while dragging
|
||
header.style.userSelect = 'none';
|
||
header.style.webkitUserSelect = 'none';
|
||
}
|
||
|
||
makeCardResizable(card, index) {
|
||
// Add resize handles
|
||
const resizeHandle = document.createElement('div');
|
||
resizeHandle.className = 'resize-handle';
|
||
resizeHandle.innerHTML = '⟲';
|
||
resizeHandle.style.cssText = `
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
width: 20px;
|
||
height: 20px;
|
||
cursor: se-resize;
|
||
background: var(--primary);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
border-radius: 4px 0 8px 0;
|
||
opacity: 0.7;
|
||
transition: opacity 0.2s ease;
|
||
`;
|
||
|
||
resizeHandle.addEventListener('mousedown', (e) => this.startResize(e, card, index));
|
||
resizeHandle.addEventListener('touchstart', (e) => this.startResize(e, card, index));
|
||
|
||
card.style.position = 'relative';
|
||
card.appendChild(resizeHandle);
|
||
|
||
// Show/hide resize handle on hover
|
||
card.addEventListener('mouseenter', () => resizeHandle.style.opacity = '1');
|
||
card.addEventListener('mouseleave', () => resizeHandle.style.opacity = '0.7');
|
||
}
|
||
|
||
createCardHeader(card) {
|
||
const header = document.createElement('div');
|
||
header.className = 'card-header';
|
||
header.style.cssText = `
|
||
background: var(--primary);
|
||
color: white;
|
||
padding: 0.5rem 1rem;
|
||
margin: -2rem -2rem 1rem -2rem;
|
||
border-radius: 8px 8px 0 0;
|
||
font-weight: 600;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
opacity: 0.3;
|
||
transition: opacity 0.3s ease;
|
||
`;
|
||
|
||
const title = card.querySelector('h4');
|
||
const titleText = title ? title.textContent : `Card ${card.dataset.cardIndex || ''}`;
|
||
header.innerHTML = `
|
||
<span>${titleText}</span>
|
||
<span class="drag-indicator" style="opacity: 0.7;">⋮⋮</span>
|
||
`;
|
||
|
||
card.insertBefore(header, card.firstChild);
|
||
if (title) title.style.display = 'none'; // Hide original title
|
||
|
||
return header;
|
||
}
|
||
|
||
addCardHeader(card, index) {
|
||
card.dataset.cardIndex = index;
|
||
|
||
if (!card.querySelector('.card-header')) {
|
||
this.createCardHeader(card);
|
||
}
|
||
}
|
||
|
||
startDrag(e, card, index) {
|
||
// Only allow dragging if global drag mode is enabled
|
||
if (!document.body.classList.contains('global-drag-mode')) {
|
||
return;
|
||
}
|
||
|
||
e.preventDefault();
|
||
|
||
this.isDragging = true;
|
||
this.draggedCard = card;
|
||
|
||
const clientX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
|
||
const clientY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY;
|
||
|
||
const rect = card.getBoundingClientRect();
|
||
this.startPos = {
|
||
x: clientX - rect.left,
|
||
y: clientY - rect.top
|
||
};
|
||
|
||
// Switch to absolute positioning
|
||
const parentRect = card.offsetParent ? card.offsetParent.getBoundingClientRect() : { left: 0, top: 0 };
|
||
card.style.position = 'absolute';
|
||
card.style.left = rect.left - parentRect.left + 'px';
|
||
card.style.top = rect.top - parentRect.top + 'px';
|
||
card.style.width = rect.width + 'px';
|
||
card.style.zIndex = '1000';
|
||
card.style.transform = 'rotate(2deg)';
|
||
card.style.boxShadow = '0 8px 32px rgba(0,0,0,0.3)';
|
||
|
||
// Add event listeners
|
||
document.addEventListener('mousemove', this.handleDrag);
|
||
document.addEventListener('touchmove', this.handleDrag);
|
||
document.addEventListener('mouseup', this.stopDrag);
|
||
document.addEventListener('touchend', this.stopDrag);
|
||
|
||
card.classList.add('dragging');
|
||
}
|
||
|
||
handleDrag = (e) => {
|
||
if (!this.isDragging || !this.draggedCard) return;
|
||
|
||
e.preventDefault();
|
||
|
||
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
|
||
const clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;
|
||
|
||
const containerRect = this.freeformContainer ? this.freeformContainer.getBoundingClientRect() :
|
||
(this.draggedCard.offsetParent ? this.draggedCard.offsetParent.getBoundingClientRect() : { left: 0, top: 0 });
|
||
const newX = clientX - containerRect.left - this.startPos.x;
|
||
const newY = clientY - containerRect.top - this.startPos.y;
|
||
|
||
// Keep within bounds of the container with some padding
|
||
const container = this.freeformContainer || this.draggedCard.offsetParent || document.body;
|
||
const maxX = container.clientWidth - this.draggedCard.offsetWidth - 10;
|
||
const maxY = container.clientHeight - this.draggedCard.offsetHeight - 10;
|
||
|
||
this.draggedCard.style.left = Math.max(0, Math.min(maxX, newX)) + 'px';
|
||
this.draggedCard.style.top = Math.max(0, Math.min(maxY, newY)) + 'px';
|
||
}
|
||
|
||
stopDrag = (e) => {
|
||
if (!this.isDragging || !this.draggedCard) return;
|
||
|
||
this.isDragging = false;
|
||
|
||
// Reset visual effects
|
||
this.draggedCard.style.transform = '';
|
||
this.draggedCard.style.zIndex = '';
|
||
this.draggedCard.style.boxShadow = '';
|
||
this.draggedCard.classList.remove('dragging');
|
||
|
||
// Save position
|
||
this.saveCardLayout();
|
||
|
||
// Clean up event listeners
|
||
document.removeEventListener('mousemove', this.handleDrag);
|
||
document.removeEventListener('touchmove', this.handleDrag);
|
||
document.removeEventListener('mouseup', this.stopDrag);
|
||
document.removeEventListener('touchend', this.stopDrag);
|
||
|
||
this.draggedCard = null;
|
||
}
|
||
|
||
startResize(e, card, index) {
|
||
// Only allow resizing if global drag mode is enabled
|
||
if (!document.body.classList.contains('global-drag-mode')) {
|
||
return;
|
||
}
|
||
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
this.isResizing = true;
|
||
this.draggedCard = card;
|
||
|
||
const clientX = e.type === 'touchstart' ? e.touches[0].clientX : e.clientX;
|
||
const clientY = e.type === 'touchstart' ? e.touches[0].clientY : e.clientY;
|
||
|
||
this.startPos = { x: clientX, y: clientY };
|
||
this.startSize = {
|
||
width: card.offsetWidth,
|
||
height: card.offsetHeight
|
||
};
|
||
|
||
document.addEventListener('mousemove', this.handleResize);
|
||
document.addEventListener('touchmove', this.handleResize);
|
||
document.addEventListener('mouseup', this.stopResize);
|
||
document.addEventListener('touchend', this.stopResize);
|
||
|
||
card.classList.add('resizing');
|
||
}
|
||
|
||
handleResize = (e) => {
|
||
if (!this.isResizing || !this.draggedCard) return;
|
||
|
||
e.preventDefault();
|
||
|
||
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
|
||
const clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY;
|
||
|
||
const deltaX = clientX - this.startPos.x;
|
||
const deltaY = clientY - this.startPos.y;
|
||
|
||
const newWidth = Math.max(200, this.startSize.width + deltaX);
|
||
const newHeight = Math.max(150, this.startSize.height + deltaY);
|
||
|
||
this.draggedCard.style.width = newWidth + 'px';
|
||
this.draggedCard.style.height = newHeight + 'px';
|
||
}
|
||
|
||
stopResize = (e) => {
|
||
if (!this.isResizing) return;
|
||
|
||
this.isResizing = false;
|
||
this.draggedCard.classList.remove('resizing');
|
||
|
||
// Save size
|
||
this.saveCardLayout();
|
||
|
||
// Clean up event listeners
|
||
document.removeEventListener('mousemove', this.handleResize);
|
||
document.removeEventListener('touchmove', this.handleResize);
|
||
document.removeEventListener('mouseup', this.stopResize);
|
||
document.removeEventListener('touchend', this.stopResize);
|
||
|
||
this.draggedCard = null;
|
||
}
|
||
|
||
saveCardLayout() {
|
||
const layout = {};
|
||
const cards = this.gridContainer.querySelectorAll('.card');
|
||
|
||
cards.forEach((card, index) => {
|
||
layout[index] = {
|
||
left: card.style.left,
|
||
top: card.style.top,
|
||
width: card.style.width,
|
||
height: card.style.height,
|
||
position: card.style.position
|
||
};
|
||
});
|
||
|
||
localStorage.setItem('adminCardLayout', JSON.stringify(layout));
|
||
console.log('💾 Card layout saved');
|
||
}
|
||
|
||
loadSavedLayouts() {
|
||
try {
|
||
const saved = localStorage.getItem('adminCardLayout');
|
||
return saved ? JSON.parse(saved) : {};
|
||
} catch (e) {
|
||
console.warn('Could not load saved layouts:', e);
|
||
return {};
|
||
}
|
||
}
|
||
|
||
applySavedLayout() {
|
||
const cards = this.gridContainer.querySelectorAll('.card');
|
||
|
||
cards.forEach((card, index) => {
|
||
const savedLayout = this.savedLayouts[index];
|
||
if (savedLayout) {
|
||
Object.assign(card.style, savedLayout);
|
||
}
|
||
});
|
||
}
|
||
|
||
addControlButtons() {
|
||
// Check if controls already exist
|
||
if (document.querySelector('.global-card-controls')) return;
|
||
|
||
// Check if hamburger menu exists and adjust position accordingly
|
||
const menuToggle = document.querySelector('.menu-toggle');
|
||
const topPosition = menuToggle ? '80px' : '20px';
|
||
|
||
const controlsDiv = document.createElement('div');
|
||
controlsDiv.className = 'global-card-controls card-controls';
|
||
controlsDiv.style.cssText = `
|
||
position: fixed;
|
||
top: ${topPosition};
|
||
right: 20px;
|
||
z-index: 900;
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
background: rgba(255,255,255,0.95);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 8px;
|
||
padding: 0.75rem;
|
||
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
|
||
border: 1px solid var(--border);
|
||
max-width: 300px;
|
||
flex-wrap: wrap;
|
||
`;
|
||
|
||
const toggleBtn = document.createElement('button');
|
||
toggleBtn.className = 'btn btn-primary';
|
||
toggleBtn.innerHTML = '🎯 Enable Drag Mode';
|
||
toggleBtn.style.fontSize = '0.9rem';
|
||
toggleBtn.onclick = () => this.toggleDragMode(toggleBtn);
|
||
|
||
const resetBtn = document.createElement('button');
|
||
resetBtn.className = 'btn btn-secondary';
|
||
resetBtn.innerHTML = '🔄 Reset';
|
||
resetBtn.style.fontSize = '0.9rem';
|
||
resetBtn.onclick = () => this.resetLayout();
|
||
|
||
const saveBtn = document.createElement('button');
|
||
saveBtn.className = 'btn btn-success';
|
||
saveBtn.innerHTML = '💾 Save';
|
||
saveBtn.style.fontSize = '0.9rem';
|
||
saveBtn.onclick = () => this.saveCardLayout();
|
||
|
||
const arrangeBtn = document.createElement('button');
|
||
arrangeBtn.className = 'btn btn-info';
|
||
arrangeBtn.innerHTML = '📐 Auto-Arrange';
|
||
arrangeBtn.style.fontSize = '0.9rem';
|
||
arrangeBtn.onclick = () => {
|
||
if (document.body.classList.contains('global-drag-mode') && this.freeformContainer) {
|
||
this.autoArrangeCards();
|
||
}
|
||
};
|
||
|
||
const minimizeBtn = document.createElement('button');
|
||
minimizeBtn.className = 'btn btn-secondary';
|
||
minimizeBtn.innerHTML = '−';
|
||
minimizeBtn.style.fontSize = '1.2rem';
|
||
minimizeBtn.style.width = '32px';
|
||
minimizeBtn.style.padding = '0.25rem';
|
||
minimizeBtn.title = 'Minimize controls';
|
||
minimizeBtn.onclick = () => this.toggleControls(controlsDiv, minimizeBtn);
|
||
|
||
controlsDiv.appendChild(toggleBtn);
|
||
controlsDiv.appendChild(resetBtn);
|
||
controlsDiv.appendChild(saveBtn);
|
||
controlsDiv.appendChild(arrangeBtn);
|
||
controlsDiv.appendChild(minimizeBtn);
|
||
|
||
document.body.appendChild(controlsDiv);
|
||
|
||
this.controlsDiv = controlsDiv;
|
||
this.toggleBtn = toggleBtn;
|
||
|
||
console.log('🎛️ Global card controls added');
|
||
}
|
||
|
||
toggleControls(controlsDiv, minimizeBtn) {
|
||
const buttons = controlsDiv.querySelectorAll('.btn:not(:last-child)');
|
||
const isMinimized = controlsDiv.dataset.minimized === 'true';
|
||
|
||
if (isMinimized) {
|
||
// Expand
|
||
buttons.forEach(btn => btn.style.display = 'inline-flex');
|
||
minimizeBtn.innerHTML = '−';
|
||
minimizeBtn.title = 'Minimize controls';
|
||
controlsDiv.dataset.minimized = 'false';
|
||
} else {
|
||
// Minimize
|
||
buttons.forEach(btn => btn.style.display = 'none');
|
||
minimizeBtn.innerHTML = '⚙️';
|
||
minimizeBtn.title = 'Expand controls';
|
||
controlsDiv.dataset.minimized = 'true';
|
||
}
|
||
}
|
||
|
||
toggleDragMode(button) {
|
||
const isCurrentlyEnabled = document.body.classList.contains('global-drag-mode');
|
||
|
||
if (isCurrentlyEnabled) {
|
||
// Disable drag mode
|
||
document.body.classList.remove('global-drag-mode');
|
||
button.innerHTML = '🎯 Enable Drag Mode';
|
||
button.className = 'btn btn-primary';
|
||
|
||
// Return cards to their original containers if possible
|
||
const cards = document.querySelectorAll('.card');
|
||
cards.forEach(card => {
|
||
// Reset positioning
|
||
card.style.position = '';
|
||
card.style.left = '';
|
||
card.style.top = '';
|
||
card.style.zIndex = '';
|
||
card.style.width = '';
|
||
card.style.minWidth = '';
|
||
|
||
// Move back to original container if it was in freeform container
|
||
if (card.parentNode === this.freeformContainer) {
|
||
const originalContainer = document.querySelector('.admin-grid') ||
|
||
document.querySelector('.container') ||
|
||
document.body;
|
||
originalContainer.appendChild(card);
|
||
}
|
||
});
|
||
|
||
// Hide freeform container
|
||
if (this.freeformContainer) {
|
||
this.freeformContainer.style.display = 'none';
|
||
}
|
||
|
||
// Remove drag mode indicator
|
||
const indicator = document.querySelector('.global-drag-indicator');
|
||
if (indicator) indicator.remove();
|
||
|
||
console.log('🔒 Drag mode disabled - cards returned to normal layout');
|
||
} else {
|
||
// Enable drag mode
|
||
document.body.classList.add('global-drag-mode');
|
||
button.innerHTML = '🔒 Disable Drag Mode';
|
||
button.className = 'btn btn-warning';
|
||
|
||
// Add drag mode indicator
|
||
this.addDragModeIndicator();
|
||
|
||
// Create and show freeform container
|
||
this.ensureFreeformContainer();
|
||
this.freeformContainer.style.display = 'block';
|
||
|
||
// Convert all cards to absolute positioning with flexible layout
|
||
const cards = document.querySelectorAll('.card');
|
||
|
||
cards.forEach((card, index) => {
|
||
const rect = card.getBoundingClientRect();
|
||
|
||
// Move card to freeform container if not already there
|
||
if (card.parentNode !== this.freeformContainer) {
|
||
this.freeformContainer.appendChild(card);
|
||
}
|
||
|
||
const containerRect = this.freeformContainer.getBoundingClientRect();
|
||
|
||
card.style.position = 'absolute';
|
||
card.style.left = Math.max(10, (rect.left - containerRect.left)) + 'px';
|
||
card.style.top = Math.max(10, (rect.top - containerRect.top)) + 'px';
|
||
card.style.width = Math.min(400, Math.max(250, rect.width)) + 'px'; // Flexible width
|
||
card.style.zIndex = '10';
|
||
card.style.minWidth = '250px';
|
||
card.style.maxWidth = '500px'; // Allow for side-by-side placement
|
||
});
|
||
|
||
// Auto-arrange cards in a grid-like pattern initially
|
||
this.autoArrangeCards();
|
||
|
||
console.log('🎯 Free-form drag mode enabled - cards can be placed side-by-side');
|
||
}
|
||
}
|
||
|
||
ensureFreeformContainer() {
|
||
// Create or get the freeform positioning container
|
||
if (!this.freeformContainer || !document.body.contains(this.freeformContainer)) {
|
||
this.freeformContainer = document.createElement('div');
|
||
this.freeformContainer.className = 'freeform-card-container';
|
||
this.freeformContainer.style.cssText = `
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
padding: 2rem;
|
||
box-sizing: border-box;
|
||
`;
|
||
|
||
// Find the main container to replace or append to
|
||
const mainContainer = document.querySelector('.container') || document.body;
|
||
if (mainContainer === document.body) {
|
||
document.body.appendChild(this.freeformContainer);
|
||
} else {
|
||
mainContainer.parentNode.insertBefore(this.freeformContainer, mainContainer.nextSibling);
|
||
}
|
||
}
|
||
return this.freeformContainer;
|
||
}
|
||
|
||
addDragModeIndicator() {
|
||
// Remove existing indicator
|
||
const existing = document.querySelector('.global-drag-indicator');
|
||
if (existing) existing.remove();
|
||
|
||
const indicator = document.createElement('div');
|
||
indicator.className = 'global-drag-indicator';
|
||
indicator.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: var(--primary);
|
||
color: white;
|
||
padding: 0.75rem;
|
||
text-align: center;
|
||
font-size: 0.9rem;
|
||
z-index: 899;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||
`;
|
||
indicator.innerHTML = '🎯 Free-Form Layout Active - Drag cards anywhere, place them side-by-side, resize as needed';
|
||
|
||
document.body.appendChild(indicator);
|
||
}
|
||
|
||
resetLayout() {
|
||
const cards = this.gridContainer.querySelectorAll('.card');
|
||
|
||
cards.forEach((card) => {
|
||
card.style.position = '';
|
||
card.style.left = '';
|
||
card.style.top = '';
|
||
card.style.width = '';
|
||
card.style.height = '';
|
||
});
|
||
|
||
localStorage.removeItem('adminCardLayout');
|
||
console.log('🔄 Layout reset');
|
||
}
|
||
}
|
||
|
||
// Initialize when DOM is ready
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
window.draggableCardManager = new DraggableCardManager();
|
||
});
|
||
} else {
|
||
window.draggableCardManager = new DraggableCardManager();
|
||
} |