feat: улучшен drag & drop для multiview панелей
- Реализована перестановка панелей только при отпускании кнопки мыши - Исправлен алгоритм swapPanels для корректной работы с любыми панелями - Устранена ошибка TypeError при обращении к classList - Добавлено подробное логирование для отладки - Улучшена визуальная обратная связь с анимацией перестановки - Панели теперь меняются местами без сдвига остальных элементов Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
@@ -610,6 +610,86 @@ a{color:var(--link)}
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
/* Drag & Drop стили */
|
||||||
|
cursor: move;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для перетаскиваемого элемента */
|
||||||
|
.multi-view-panel.dragging {
|
||||||
|
opacity: 0.7;
|
||||||
|
transform: scale(1.02);
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для области drop - готовность к перестановке */
|
||||||
|
.multi-view-panel.drop-target {
|
||||||
|
border-color: var(--ok);
|
||||||
|
background: var(--tab-active);
|
||||||
|
box-shadow: 0 0 0 2px var(--ok), 0 0 15px rgba(158, 206, 106, 0.2);
|
||||||
|
transform: scale(1.01);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для области drop - перестановка в процессе */
|
||||||
|
.multi-view-panel.drop-target.swapping {
|
||||||
|
box-shadow: 0 0 0 3px var(--ok), 0 0 25px rgba(158, 206, 106, 0.4);
|
||||||
|
transform: scale(1.02);
|
||||||
|
animation: dropTargetSwap 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Анимация перестановки */
|
||||||
|
@keyframes dropTargetSwap {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 3px var(--ok), 0 0 25px rgba(158, 206, 106, 0.4);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 4px var(--ok), 0 0 35px rgba(158, 206, 106, 0.6);
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 3px var(--ok), 0 0 25px rgba(158, 206, 106, 0.4);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для заголовка панели - область для захвата */
|
||||||
|
.multi-view-header {
|
||||||
|
cursor: grab;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multi-view-header:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Индикатор возможности перетаскивания */
|
||||||
|
.multi-view-header::before {
|
||||||
|
content: "⋮⋮";
|
||||||
|
position: absolute;
|
||||||
|
left: 8px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.multi-view-panel:hover .multi-view-header::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Скрываем индикатор при перетаскивании */
|
||||||
|
.multi-view-panel.dragging .multi-view-header::before {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multi-view-content {
|
.multi-view-content {
|
||||||
@@ -2003,6 +2083,8 @@ a{color:var(--link)}
|
|||||||
/* Равная высота строк для нескольких рядов (3+ окон) */
|
/* Равная высота строк для нескольких рядов (3+ окон) */
|
||||||
grid-auto-rows: 1fr;
|
grid-auto-rows: 1fr;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
/* Поддержка drag & drop */
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multi-view-panel {
|
.multi-view-panel {
|
||||||
@@ -2016,7 +2098,7 @@ a{color:var(--link)}
|
|||||||
}
|
}
|
||||||
|
|
||||||
.multi-view-header {
|
.multi-view-header {
|
||||||
padding: 5px 16px;
|
padding: 5px 16px 5px 24px; /* Увеличиваем левый отступ для индикатора перетаскивания */
|
||||||
background: var(--chip);
|
background: var(--chip);
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1867,6 +1867,14 @@ async function setupMultiView() {
|
|||||||
|
|
||||||
console.log(`setupMultiView: Multi-view setup completed for ${state.selectedContainers.length} containers`);
|
console.log(`setupMultiView: Multi-view setup completed for ${state.selectedContainers.length} containers`);
|
||||||
|
|
||||||
|
// Применяем сохраненный порядок панелей
|
||||||
|
setTimeout(() => {
|
||||||
|
// Сначала очищаем дубликаты, если они есть
|
||||||
|
cleanupDuplicatePanels();
|
||||||
|
// Затем применяем порядок
|
||||||
|
applyPanelOrder();
|
||||||
|
}, 100); // Небольшая задержка для завершения создания панелей
|
||||||
|
|
||||||
// Обновляем счетчики для multi view
|
// Обновляем счетчики для multi view
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
recalculateMultiViewCounters();
|
recalculateMultiViewCounters();
|
||||||
@@ -1937,6 +1945,9 @@ function createMultiViewPanel(service) {
|
|||||||
console.error(`Failed to create multi-view log element for ${service.name}`);
|
console.error(`Failed to create multi-view log element for ${service.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляем drag & drop функциональность
|
||||||
|
setupDragAndDrop(panel);
|
||||||
|
|
||||||
// Инициализируем состояние кнопок уровней логирования для этого контейнера
|
// Инициализируем состояние кнопок уровней логирования для этого контейнера
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const levelBtns = panel.querySelectorAll('.level-btn');
|
const levelBtns = panel.querySelectorAll('.level-btn');
|
||||||
@@ -5850,4 +5861,405 @@ function reinitializeElements() {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DRAG & DROP ФУНКЦИОНАЛЬНОСТЬ ДЛЯ MULTI-VIEW ПАНЕЛЕЙ
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Находит целевую панель для drop с расширенной зоной поиска
|
||||||
|
* @param {number} x - X координата курсора
|
||||||
|
* @param {number} y - Y координата курсора
|
||||||
|
* @param {HTMLElement} draggedElement - Перетаскиваемый элемент
|
||||||
|
* @returns {HTMLElement|null} Найденная панель или null
|
||||||
|
*/
|
||||||
|
function findTargetPanel(x, y, draggedElement) {
|
||||||
|
// Сначала пробуем найти панель точно под курсором
|
||||||
|
let elementBelow = document.elementFromPoint(x, y);
|
||||||
|
let targetPanel = elementBelow?.closest('.multi-view-panel');
|
||||||
|
|
||||||
|
if (targetPanel && targetPanel !== draggedElement) {
|
||||||
|
const targetId = targetPanel.getAttribute('data-container-id');
|
||||||
|
console.log(`Found target panel: ${targetId} at (${x}, ${y})`);
|
||||||
|
return targetPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если не нашли, расширяем зону поиска в радиусе 50px
|
||||||
|
const searchRadius = 50;
|
||||||
|
const searchPoints = [
|
||||||
|
{ x: x - searchRadius, y: y },
|
||||||
|
{ x: x + searchRadius, y: y },
|
||||||
|
{ x: x, y: y - searchRadius },
|
||||||
|
{ x: x, y: y + searchRadius },
|
||||||
|
{ x: x - searchRadius/2, y: y - searchRadius/2 },
|
||||||
|
{ x: x + searchRadius/2, y: y - searchRadius/2 },
|
||||||
|
{ x: x - searchRadius/2, y: y + searchRadius/2 },
|
||||||
|
{ x: x + searchRadius/2, y: y + searchRadius/2 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const point of searchPoints) {
|
||||||
|
elementBelow = document.elementFromPoint(point.x, point.y);
|
||||||
|
targetPanel = elementBelow?.closest('.multi-view-panel');
|
||||||
|
|
||||||
|
if (targetPanel && targetPanel !== draggedElement) {
|
||||||
|
return targetPanel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если все еще не нашли, проверяем все панели на пересечение с курсором
|
||||||
|
const allPanels = document.querySelectorAll('.multi-view-panel');
|
||||||
|
for (const panel of allPanels) {
|
||||||
|
if (panel === draggedElement) continue;
|
||||||
|
|
||||||
|
const rect = panel.getBoundingClientRect();
|
||||||
|
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Настраивает drag & drop функциональность для панели multi-view
|
||||||
|
* @param {HTMLElement} panel - Панель для настройки drag & drop
|
||||||
|
*/
|
||||||
|
function setupDragAndDrop(panel) {
|
||||||
|
const header = panel.querySelector('.multi-view-header');
|
||||||
|
if (!header) return;
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let dragStartX = 0;
|
||||||
|
let dragStartY = 0;
|
||||||
|
let draggedElement = null;
|
||||||
|
let draggedIndex = -1;
|
||||||
|
let dropTarget = null;
|
||||||
|
|
||||||
|
// Обработчик начала перетаскивания
|
||||||
|
header.addEventListener('mousedown', (e) => {
|
||||||
|
// Проверяем, что клик по заголовку, а не по кнопкам уровней
|
||||||
|
if (e.target.closest('.level-btn')) return;
|
||||||
|
|
||||||
|
isDragging = true;
|
||||||
|
draggedElement = panel;
|
||||||
|
draggedIndex = Array.from(panel.parentNode.children).indexOf(panel);
|
||||||
|
|
||||||
|
dragStartX = e.clientX;
|
||||||
|
dragStartY = e.clientY;
|
||||||
|
|
||||||
|
// Добавляем класс для визуального эффекта
|
||||||
|
panel.classList.add('dragging');
|
||||||
|
|
||||||
|
// Предотвращаем выделение текста
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
console.log(`Drag started for panel: ${panel.getAttribute('data-container-id')}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработчик движения мыши
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
if (!isDragging || !draggedElement) return;
|
||||||
|
|
||||||
|
const deltaX = e.clientX - dragStartX;
|
||||||
|
const deltaY = e.clientY - dragStartY;
|
||||||
|
|
||||||
|
// Минимальное расстояние для начала перетаскивания
|
||||||
|
if (Math.abs(deltaX) < 5 && Math.abs(deltaY) < 5) return;
|
||||||
|
|
||||||
|
// Обновляем позицию элемента
|
||||||
|
draggedElement.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
|
||||||
|
|
||||||
|
// Находим элемент под курсором с расширенной зоной поиска
|
||||||
|
const targetPanel = findTargetPanel(e.clientX, e.clientY, draggedElement);
|
||||||
|
|
||||||
|
// Убираем подсветку с предыдущей цели
|
||||||
|
if (dropTarget && dropTarget !== targetPanel) {
|
||||||
|
dropTarget.classList.remove('drop-target');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подсвечиваем новую цель (без перестановки во время перетаскивания)
|
||||||
|
if (targetPanel && targetPanel !== draggedElement) {
|
||||||
|
// Убираем подсветку с предыдущей цели
|
||||||
|
if (dropTarget && dropTarget !== targetPanel) {
|
||||||
|
dropTarget.classList.remove('drop-target');
|
||||||
|
}
|
||||||
|
|
||||||
|
dropTarget = targetPanel;
|
||||||
|
targetPanel.classList.add('drop-target');
|
||||||
|
} else {
|
||||||
|
if (dropTarget) {
|
||||||
|
dropTarget.classList.remove('drop-target');
|
||||||
|
}
|
||||||
|
dropTarget = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработчик окончания перетаскивания
|
||||||
|
document.addEventListener('mouseup', (e) => {
|
||||||
|
if (!isDragging || !draggedElement) return;
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
|
||||||
|
// Убираем визуальные эффекты
|
||||||
|
draggedElement.classList.remove('dragging');
|
||||||
|
draggedElement.style.transform = '';
|
||||||
|
|
||||||
|
// Если есть целевая панель, выполняем перестановку
|
||||||
|
if (dropTarget) {
|
||||||
|
const containerId = draggedElement.getAttribute('data-container-id');
|
||||||
|
const targetContainerId = dropTarget.getAttribute('data-container-id');
|
||||||
|
|
||||||
|
console.log(`Dropping panel ${containerId} at position of ${targetContainerId}`);
|
||||||
|
|
||||||
|
// Сохраняем ссылку на элемент для setTimeout
|
||||||
|
const targetElement = dropTarget;
|
||||||
|
|
||||||
|
// Добавляем класс для анимации перестановки
|
||||||
|
targetElement.classList.add('swapping');
|
||||||
|
|
||||||
|
// Выполняем перестановку
|
||||||
|
swapPanels(draggedElement, targetElement);
|
||||||
|
|
||||||
|
// Убираем класс анимации через короткое время
|
||||||
|
setTimeout(() => {
|
||||||
|
if (targetElement && targetElement.classList) {
|
||||||
|
targetElement.classList.remove('swapping');
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
// Убираем подсветку
|
||||||
|
targetElement.classList.remove('drop-target');
|
||||||
|
|
||||||
|
// Сохраняем новый порядок в localStorage
|
||||||
|
savePanelOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сбрасываем переменные
|
||||||
|
draggedElement = null;
|
||||||
|
draggedIndex = -1;
|
||||||
|
dropTarget = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Мгновенно меняет местами две панели в DOM и обновляет массив selectedContainers
|
||||||
|
* @param {HTMLElement} panel1 - Первая панель
|
||||||
|
* @param {HTMLElement} panel2 - Вторая панель
|
||||||
|
*/
|
||||||
|
function swapPanels(panel1, panel2) {
|
||||||
|
if (!panel1 || !panel2 || panel1 === panel2) return;
|
||||||
|
|
||||||
|
const containerId1 = panel1.getAttribute('data-container-id');
|
||||||
|
const containerId2 = panel2.getAttribute('data-container-id');
|
||||||
|
|
||||||
|
if (!containerId1 || !containerId2) {
|
||||||
|
console.error('Missing container IDs:', { containerId1, containerId2 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Before swap - Panel1: ${containerId1}, Panel2: ${containerId2}`);
|
||||||
|
console.log('Before swap - selectedContainers:', [...state.selectedContainers]);
|
||||||
|
|
||||||
|
// Меняем местами панели в DOM
|
||||||
|
const parent = panel1.parentNode;
|
||||||
|
|
||||||
|
// Получаем позиции панелей в DOM
|
||||||
|
const panel1NextSibling = panel1.nextSibling;
|
||||||
|
const panel2NextSibling = panel2.nextSibling;
|
||||||
|
|
||||||
|
// Вставляем panel1 на место panel2
|
||||||
|
parent.insertBefore(panel1, panel2NextSibling);
|
||||||
|
|
||||||
|
// Вставляем panel2 на место panel1
|
||||||
|
parent.insertBefore(panel2, panel1NextSibling);
|
||||||
|
|
||||||
|
// Обновляем массив selectedContainers
|
||||||
|
const index1 = state.selectedContainers.indexOf(containerId1);
|
||||||
|
const index2 = state.selectedContainers.indexOf(containerId2);
|
||||||
|
|
||||||
|
if (index1 !== -1 && index2 !== -1) {
|
||||||
|
// Меняем местами элементы в массиве
|
||||||
|
state.selectedContainers[index1] = containerId2;
|
||||||
|
state.selectedContainers[index2] = containerId1;
|
||||||
|
|
||||||
|
console.log(`After swap - Panel1: ${containerId1}, Panel2: ${containerId2}`);
|
||||||
|
console.log('After swap - selectedContainers:', [...state.selectedContainers]);
|
||||||
|
|
||||||
|
// Проверяем, что атрибуты data-container-id остались правильными
|
||||||
|
const newContainerId1 = panel1.getAttribute('data-container-id');
|
||||||
|
const newContainerId2 = panel2.getAttribute('data-container-id');
|
||||||
|
console.log(`After swap - Panel1 data-container-id: ${newContainerId1}, Panel2 data-container-id: ${newContainerId2}`);
|
||||||
|
} else {
|
||||||
|
console.error('Could not find container IDs in selectedContainers:', { index1, index2, containerId1, containerId2 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переставляет контейнеры в массиве selectedContainers
|
||||||
|
* @param {string} draggedId - ID перетаскиваемого контейнера
|
||||||
|
* @param {string} targetId - ID целевого контейнера
|
||||||
|
*/
|
||||||
|
function reorderContainers(draggedId, targetId) {
|
||||||
|
const draggedIndex = state.selectedContainers.indexOf(draggedId);
|
||||||
|
const targetIndex = state.selectedContainers.indexOf(targetId);
|
||||||
|
|
||||||
|
if (draggedIndex === -1 || targetIndex === -1) return;
|
||||||
|
|
||||||
|
// Удаляем перетаскиваемый элемент
|
||||||
|
state.selectedContainers.splice(draggedIndex, 1);
|
||||||
|
|
||||||
|
// Вставляем его в новую позицию
|
||||||
|
const newTargetIndex = draggedIndex < targetIndex ? targetIndex - 1 : targetIndex;
|
||||||
|
state.selectedContainers.splice(newTargetIndex, 0, draggedId);
|
||||||
|
|
||||||
|
console.log('Reordered containers:', state.selectedContainers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сохраняет порядок панелей в localStorage
|
||||||
|
*/
|
||||||
|
function savePanelOrder() {
|
||||||
|
const panelOrder = Array.from(document.querySelectorAll('.multi-view-panel'))
|
||||||
|
.map(panel => panel.getAttribute('data-container-id'))
|
||||||
|
.filter(id => id);
|
||||||
|
|
||||||
|
// Удаляем дубликаты, сохраняя порядок первого вхождения
|
||||||
|
const uniquePanelOrder = [...new Set(panelOrder)];
|
||||||
|
|
||||||
|
localStorage.setItem('lb_panel_order', JSON.stringify(uniquePanelOrder));
|
||||||
|
console.log('Panel order saved:', uniquePanelOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загружает порядок панелей из localStorage
|
||||||
|
* @returns {Array} Массив ID контейнеров в сохраненном порядке
|
||||||
|
*/
|
||||||
|
function loadPanelOrder() {
|
||||||
|
try {
|
||||||
|
const savedOrder = localStorage.getItem('lb_panel_order');
|
||||||
|
if (savedOrder) {
|
||||||
|
const order = JSON.parse(savedOrder);
|
||||||
|
// Удаляем дубликаты из загруженного порядка
|
||||||
|
const uniqueOrder = [...new Set(order)];
|
||||||
|
console.log('Panel order loaded:', uniqueOrder);
|
||||||
|
return uniqueOrder;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading panel order:', error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Применяет сохраненный порядок панелей
|
||||||
|
*/
|
||||||
|
function applyPanelOrder() {
|
||||||
|
if (!state.multiViewMode) return;
|
||||||
|
|
||||||
|
const savedOrder = loadPanelOrder();
|
||||||
|
if (!savedOrder || savedOrder.length === 0) return;
|
||||||
|
|
||||||
|
const grid = document.getElementById('multiViewGrid');
|
||||||
|
if (!grid) return;
|
||||||
|
|
||||||
|
// Создаем карту панелей по ID контейнера
|
||||||
|
const panels = Array.from(grid.children);
|
||||||
|
const panelMap = {};
|
||||||
|
panels.forEach(panel => {
|
||||||
|
const containerId = panel.getAttribute('data-container-id');
|
||||||
|
if (containerId) {
|
||||||
|
panelMap[containerId] = panel;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Переставляем панели согласно сохраненному порядку
|
||||||
|
savedOrder.forEach(containerId => {
|
||||||
|
const panel = panelMap[containerId];
|
||||||
|
if (panel && panel.parentNode === grid) {
|
||||||
|
grid.appendChild(panel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Добавляем панели для новых контейнеров, которых нет в сохраненном порядке
|
||||||
|
const currentContainers = [...state.selectedContainers];
|
||||||
|
const newContainers = currentContainers.filter(id => !savedOrder.includes(id));
|
||||||
|
|
||||||
|
newContainers.forEach(containerId => {
|
||||||
|
// Проверяем, что панель для этого контейнера еще не существует
|
||||||
|
const existingPanel = grid.querySelector(`[data-container-id="${containerId}"]`);
|
||||||
|
if (!existingPanel) {
|
||||||
|
const service = state.services.find(s => s.id === containerId);
|
||||||
|
if (service) {
|
||||||
|
console.log(`Creating new panel for container: ${service.name} (${containerId})`);
|
||||||
|
const panel = createMultiViewPanel(service);
|
||||||
|
grid.appendChild(panel);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Panel for container ${containerId} already exists, skipping creation`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обновляем массив selectedContainers, сохраняя все текущие контейнеры
|
||||||
|
// но применяя сохраненный порядок для тех, которые есть в сохраненном порядке
|
||||||
|
const orderedContainers = savedOrder.filter(id => currentContainers.includes(id));
|
||||||
|
state.selectedContainers = [...orderedContainers, ...newContainers];
|
||||||
|
|
||||||
|
// Обновляем grid template columns для нового количества панелей
|
||||||
|
const totalPanels = state.selectedContainers.length;
|
||||||
|
let columns = 1;
|
||||||
|
if (totalPanels === 1) columns = 1;
|
||||||
|
else if (totalPanels === 2) columns = 2;
|
||||||
|
else if (totalPanels <= 4) columns = 2;
|
||||||
|
else if (totalPanels <= 6) columns = 3;
|
||||||
|
else columns = 4;
|
||||||
|
|
||||||
|
grid.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
|
||||||
|
console.log(`Updated grid template columns to: repeat(${columns}, 1fr) for ${totalPanels} panels`);
|
||||||
|
|
||||||
|
console.log('Applied panel order:', state.selectedContainers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Очищает дубликаты из localStorage и обновляет порядок панелей
|
||||||
|
*/
|
||||||
|
function cleanupDuplicatePanels() {
|
||||||
|
try {
|
||||||
|
const savedOrder = localStorage.getItem('lb_panel_order');
|
||||||
|
if (savedOrder) {
|
||||||
|
const order = JSON.parse(savedOrder);
|
||||||
|
const uniqueOrder = [...new Set(order)];
|
||||||
|
|
||||||
|
// Если были найдены дубликаты, обновляем localStorage
|
||||||
|
if (order.length !== uniqueOrder.length) {
|
||||||
|
console.log('Found duplicates in panel order, cleaning up:', order, '->', uniqueOrder);
|
||||||
|
localStorage.setItem('lb_panel_order', JSON.stringify(uniqueOrder));
|
||||||
|
|
||||||
|
// Если мы в multiview режиме, пересоздаем панели
|
||||||
|
if (state.multiViewMode) {
|
||||||
|
console.log('Recreating panels to remove duplicates');
|
||||||
|
// Очищаем текущий grid
|
||||||
|
const grid = document.getElementById('multiViewGrid');
|
||||||
|
if (grid) {
|
||||||
|
grid.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересоздаем панели в правильном порядке
|
||||||
|
setTimeout(() => {
|
||||||
|
applyPanelOrder();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error cleaning up duplicate panels:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспортируем функции для использования в других частях кода
|
||||||
|
window.setupDragAndDrop = setupDragAndDrop;
|
||||||
|
window.savePanelOrder = savePanelOrder;
|
||||||
|
window.loadPanelOrder = loadPanelOrder;
|
||||||
|
window.applyPanelOrder = applyPanelOrder;
|
||||||
|
window.cleanupDuplicatePanels = cleanupDuplicatePanels;
|
||||||
|
window.swapPanels = swapPanels;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user